397 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			397 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: BSD-3-Clause-Clear
 | 
						|
/*
 | 
						|
 * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
 | 
						|
 * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 | 
						|
 */
 | 
						|
 | 
						|
#include "core.h"
 | 
						|
#include "acpi.h"
 | 
						|
#include "debug.h"
 | 
						|
 | 
						|
static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func)
 | 
						|
{
 | 
						|
	union acpi_object *obj;
 | 
						|
	acpi_handle root_handle;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	root_handle = ACPI_HANDLE(ab->dev);
 | 
						|
	if (!root_handle) {
 | 
						|
		ath12k_dbg(ab, ATH12K_DBG_BOOT, "invalid acpi handler\n");
 | 
						|
		return -EOPNOTSUPP;
 | 
						|
	}
 | 
						|
 | 
						|
	obj = acpi_evaluate_dsm(root_handle, ab->hw_params->acpi_guid, 0, func,
 | 
						|
				NULL);
 | 
						|
 | 
						|
	if (!obj) {
 | 
						|
		ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi_evaluate_dsm() failed\n");
 | 
						|
		return -ENOENT;
 | 
						|
	}
 | 
						|
 | 
						|
	if (obj->type == ACPI_TYPE_INTEGER) {
 | 
						|
		ab->acpi.func_bit = obj->integer.value;
 | 
						|
	} else if (obj->type == ACPI_TYPE_BUFFER) {
 | 
						|
		switch (func) {
 | 
						|
		case ATH12K_ACPI_DSM_FUNC_TAS_CFG:
 | 
						|
			if (obj->buffer.length != ATH12K_ACPI_DSM_TAS_CFG_SIZE) {
 | 
						|
				ath12k_warn(ab, "invalid ACPI DSM TAS config size: %d\n",
 | 
						|
					    obj->buffer.length);
 | 
						|
				ret = -EINVAL;
 | 
						|
				goto out;
 | 
						|
			}
 | 
						|
 | 
						|
			memcpy(&ab->acpi.tas_cfg, obj->buffer.pointer,
 | 
						|
			       obj->buffer.length);
 | 
						|
 | 
						|
			break;
 | 
						|
		case ATH12K_ACPI_DSM_FUNC_TAS_DATA:
 | 
						|
			if (obj->buffer.length != ATH12K_ACPI_DSM_TAS_DATA_SIZE) {
 | 
						|
				ath12k_warn(ab, "invalid ACPI DSM TAS data size: %d\n",
 | 
						|
					    obj->buffer.length);
 | 
						|
				ret = -EINVAL;
 | 
						|
				goto out;
 | 
						|
			}
 | 
						|
 | 
						|
			memcpy(&ab->acpi.tas_sar_power_table, obj->buffer.pointer,
 | 
						|
			       obj->buffer.length);
 | 
						|
 | 
						|
			break;
 | 
						|
		case ATH12K_ACPI_DSM_FUNC_BIOS_SAR:
 | 
						|
			if (obj->buffer.length != ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE) {
 | 
						|
				ath12k_warn(ab, "invalid ACPI BIOS SAR data size: %d\n",
 | 
						|
					    obj->buffer.length);
 | 
						|
				ret = -EINVAL;
 | 
						|
				goto out;
 | 
						|
			}
 | 
						|
 | 
						|
			memcpy(&ab->acpi.bios_sar_data, obj->buffer.pointer,
 | 
						|
			       obj->buffer.length);
 | 
						|
 | 
						|
			break;
 | 
						|
		case ATH12K_ACPI_DSM_FUNC_GEO_OFFSET:
 | 
						|
			if (obj->buffer.length != ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE) {
 | 
						|
				ath12k_warn(ab, "invalid ACPI GEO OFFSET data size: %d\n",
 | 
						|
					    obj->buffer.length);
 | 
						|
				ret = -EINVAL;
 | 
						|
				goto out;
 | 
						|
			}
 | 
						|
 | 
						|
			memcpy(&ab->acpi.geo_offset_data, obj->buffer.pointer,
 | 
						|
			       obj->buffer.length);
 | 
						|
 | 
						|
			break;
 | 
						|
		case ATH12K_ACPI_DSM_FUNC_INDEX_CCA:
 | 
						|
			if (obj->buffer.length != ATH12K_ACPI_DSM_CCA_DATA_SIZE) {
 | 
						|
				ath12k_warn(ab, "invalid ACPI DSM CCA data size: %d\n",
 | 
						|
					    obj->buffer.length);
 | 
						|
				ret = -EINVAL;
 | 
						|
				goto out;
 | 
						|
			}
 | 
						|
 | 
						|
			memcpy(&ab->acpi.cca_data, obj->buffer.pointer,
 | 
						|
			       obj->buffer.length);
 | 
						|
 | 
						|
			break;
 | 
						|
		case ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE:
 | 
						|
			if (obj->buffer.length != ATH12K_ACPI_DSM_BAND_EDGE_DATA_SIZE) {
 | 
						|
				ath12k_warn(ab, "invalid ACPI DSM band edge data size: %d\n",
 | 
						|
					    obj->buffer.length);
 | 
						|
				ret = -EINVAL;
 | 
						|
				goto out;
 | 
						|
			}
 | 
						|
 | 
						|
			memcpy(&ab->acpi.band_edge_power, obj->buffer.pointer,
 | 
						|
			       obj->buffer.length);
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		ath12k_warn(ab, "ACPI DSM method returned an unsupported object type: %d\n",
 | 
						|
			    obj->type);
 | 
						|
		ret = -EINVAL;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = 0;
 | 
						|
 | 
						|
out:
 | 
						|
	ACPI_FREE(obj);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int ath12k_acpi_set_power_limit(struct ath12k_base *ab)
 | 
						|
{
 | 
						|
	const u8 *tas_sar_power_table = ab->acpi.tas_sar_power_table;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (tas_sar_power_table[0] != ATH12K_ACPI_TAS_DATA_VERSION ||
 | 
						|
	    tas_sar_power_table[1] != ATH12K_ACPI_TAS_DATA_ENABLE) {
 | 
						|
		ath12k_warn(ab, "latest ACPI TAS data is invalid\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = ath12k_wmi_set_bios_cmd(ab, WMI_BIOS_PARAM_TAS_DATA_TYPE,
 | 
						|
				      tas_sar_power_table,
 | 
						|
				      ATH12K_ACPI_DSM_TAS_DATA_SIZE);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to send ACPI TAS data table: %d\n", ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int ath12k_acpi_set_bios_sar_power(struct ath12k_base *ab)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (ab->acpi.bios_sar_data[0] != ATH12K_ACPI_POWER_LIMIT_VERSION ||
 | 
						|
	    ab->acpi.bios_sar_data[1] != ATH12K_ACPI_POWER_LIMIT_ENABLE_FLAG) {
 | 
						|
		ath12k_warn(ab, "invalid latest ACPI BIOS SAR data\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = ath12k_wmi_set_bios_sar_cmd(ab, ab->acpi.bios_sar_data);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to set ACPI BIOS SAR table: %d\n", ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void ath12k_acpi_dsm_notify(acpi_handle handle, u32 event, void *data)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	struct ath12k_base *ab = data;
 | 
						|
 | 
						|
	if (event == ATH12K_ACPI_NOTIFY_EVENT) {
 | 
						|
		ath12k_warn(ab, "unknown acpi notify %u\n", event);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!ab->acpi.acpi_tas_enable) {
 | 
						|
		ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi_tas_enable is false\n");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_DATA);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to update ACPI TAS data table: %d\n", ret);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = ath12k_acpi_set_power_limit(ab);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to set ACPI TAS power limit data: %d", ret);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!ab->acpi.acpi_bios_sar_enable)
 | 
						|
		return;
 | 
						|
 | 
						|
	ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_BIOS_SAR);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to update BIOS SAR: %d\n", ret);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = ath12k_acpi_set_bios_sar_power(ab);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to set BIOS SAR power limit: %d\n", ret);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int ath12k_acpi_set_bios_sar_params(struct ath12k_base *ab)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = ath12k_wmi_set_bios_sar_cmd(ab, ab->acpi.bios_sar_data);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to set ACPI BIOS SAR table: %d\n", ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = ath12k_wmi_set_bios_geo_cmd(ab, ab->acpi.geo_offset_data);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to set ACPI BIOS GEO table: %d\n", ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int ath12k_acpi_set_tas_params(struct ath12k_base *ab)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = ath12k_wmi_set_bios_cmd(ab, WMI_BIOS_PARAM_TAS_CONFIG_TYPE,
 | 
						|
				      ab->acpi.tas_cfg,
 | 
						|
				      ATH12K_ACPI_DSM_TAS_CFG_SIZE);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to send ACPI TAS config table parameter: %d\n",
 | 
						|
			    ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = ath12k_wmi_set_bios_cmd(ab, WMI_BIOS_PARAM_TAS_DATA_TYPE,
 | 
						|
				      ab->acpi.tas_sar_power_table,
 | 
						|
				      ATH12K_ACPI_DSM_TAS_DATA_SIZE);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_warn(ab, "failed to send ACPI TAS data table parameter: %d\n",
 | 
						|
			    ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int ath12k_acpi_start(struct ath12k_base *ab)
 | 
						|
{
 | 
						|
	acpi_status status;
 | 
						|
	u8 *buf;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (!ab->hw_params->acpi_guid)
 | 
						|
		/* not supported with this hardware */
 | 
						|
		return 0;
 | 
						|
 | 
						|
	ab->acpi.acpi_tas_enable = false;
 | 
						|
 | 
						|
	ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS);
 | 
						|
	if (ret) {
 | 
						|
		ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to get ACPI DSM data: %d\n", ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_CFG)) {
 | 
						|
		ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_CFG);
 | 
						|
		if (ret) {
 | 
						|
			ath12k_warn(ab, "failed to get ACPI TAS config table: %d\n", ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_DATA)) {
 | 
						|
		ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_DATA);
 | 
						|
		if (ret) {
 | 
						|
			ath12k_warn(ab, "failed to get ACPI TAS data table: %d\n", ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
 | 
						|
		if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_CFG) &&
 | 
						|
		    ab->acpi.tas_sar_power_table[0] == ATH12K_ACPI_TAS_DATA_VERSION &&
 | 
						|
		    ab->acpi.tas_sar_power_table[1] == ATH12K_ACPI_TAS_DATA_ENABLE)
 | 
						|
			ab->acpi.acpi_tas_enable = true;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_BIOS_SAR)) {
 | 
						|
		ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_BIOS_SAR);
 | 
						|
		if (ret) {
 | 
						|
			ath12k_warn(ab, "failed to get ACPI bios sar data: %d\n", ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_GEO_OFFSET)) {
 | 
						|
		ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_GEO_OFFSET);
 | 
						|
		if (ret) {
 | 
						|
			ath12k_warn(ab, "failed to get ACPI geo offset data: %d\n", ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
 | 
						|
		if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_BIOS_SAR) &&
 | 
						|
		    ab->acpi.bios_sar_data[0] == ATH12K_ACPI_POWER_LIMIT_VERSION &&
 | 
						|
		    ab->acpi.bios_sar_data[1] == ATH12K_ACPI_POWER_LIMIT_ENABLE_FLAG &&
 | 
						|
		    !ab->acpi.acpi_tas_enable)
 | 
						|
			ab->acpi.acpi_bios_sar_enable = true;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ab->acpi.acpi_tas_enable) {
 | 
						|
		ret = ath12k_acpi_set_tas_params(ab);
 | 
						|
		if (ret) {
 | 
						|
			ath12k_warn(ab, "failed to send ACPI parameters: %d\n", ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (ab->acpi.acpi_bios_sar_enable) {
 | 
						|
		ret = ath12k_acpi_set_bios_sar_params(ab);
 | 
						|
		if (ret)
 | 
						|
			return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_CCA)) {
 | 
						|
		ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_INDEX_CCA);
 | 
						|
		if (ret) {
 | 
						|
			ath12k_warn(ab, "failed to get ACPI DSM CCA threshold configuration: %d\n",
 | 
						|
				    ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
 | 
						|
		if (ab->acpi.cca_data[0] == ATH12K_ACPI_CCA_THR_VERSION &&
 | 
						|
		    ab->acpi.cca_data[ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET] ==
 | 
						|
		    ATH12K_ACPI_CCA_THR_ENABLE_FLAG) {
 | 
						|
			buf = ab->acpi.cca_data + ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET;
 | 
						|
			ret = ath12k_wmi_set_bios_cmd(ab,
 | 
						|
						      WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE,
 | 
						|
						      buf,
 | 
						|
						      ATH12K_ACPI_CCA_THR_OFFSET_LEN);
 | 
						|
			if (ret) {
 | 
						|
				ath12k_warn(ab, "failed to set ACPI DSM CCA threshold: %d\n",
 | 
						|
					    ret);
 | 
						|
				return ret;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi,
 | 
						|
				       ATH12K_ACPI_FUNC_BIT_BAND_EDGE_CHAN_POWER)) {
 | 
						|
		ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE);
 | 
						|
		if (ret) {
 | 
						|
			ath12k_warn(ab, "failed to get ACPI DSM band edge channel power: %d\n",
 | 
						|
				    ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
 | 
						|
		if (ab->acpi.band_edge_power[0] == ATH12K_ACPI_BAND_EDGE_VERSION &&
 | 
						|
		    ab->acpi.band_edge_power[1] == ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG) {
 | 
						|
			ret = ath12k_wmi_set_bios_cmd(ab,
 | 
						|
						      WMI_BIOS_PARAM_TYPE_BANDEDGE,
 | 
						|
						      ab->acpi.band_edge_power,
 | 
						|
						      sizeof(ab->acpi.band_edge_power));
 | 
						|
			if (ret) {
 | 
						|
				ath12k_warn(ab,
 | 
						|
					    "failed to set ACPI DSM band edge channel power: %d\n",
 | 
						|
					    ret);
 | 
						|
				return ret;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	status = acpi_install_notify_handler(ACPI_HANDLE(ab->dev),
 | 
						|
					     ACPI_DEVICE_NOTIFY,
 | 
						|
					     ath12k_acpi_dsm_notify, ab);
 | 
						|
	if (ACPI_FAILURE(status)) {
 | 
						|
		ath12k_warn(ab, "failed to install DSM notify callback: %d\n", status);
 | 
						|
		return -EIO;
 | 
						|
	}
 | 
						|
 | 
						|
	ab->acpi.started = true;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void ath12k_acpi_stop(struct ath12k_base *ab)
 | 
						|
{
 | 
						|
	if (!ab->acpi.started)
 | 
						|
		return;
 | 
						|
 | 
						|
	acpi_remove_notify_handler(ACPI_HANDLE(ab->dev),
 | 
						|
				   ACPI_DEVICE_NOTIFY,
 | 
						|
				   ath12k_acpi_dsm_notify);
 | 
						|
 | 
						|
	memset(&ab->acpi, 0, sizeof(ab->acpi));
 | 
						|
}
 |