mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 11:37:06 +02:00
Merge "driver: thermal: userspace_cdev: Add a snapshot of userspace cooling driver"
This commit is contained in:
commit
4c27283738
|
|
@ -62,3 +62,81 @@ config QTI_BCL_PMIC5
|
|||
voltage sensors with the thermal core framework and can take
|
||||
threshold input and notify the thermal core when the threshold is
|
||||
reached.
|
||||
|
||||
config QTI_BCL_SOC_DRIVER
|
||||
tristate "QTI Battery state of charge sensor driver"
|
||||
depends on THERMAL && POWER_SUPPLY
|
||||
help
|
||||
This driver registers battery state of charge as a sensor with
|
||||
thermal framework. This sensor can monitor for state of charge
|
||||
thresholds and notify the thermal framework when the thresholds
|
||||
are reached and cleared. This will help to monitor and apply any
|
||||
mitigation when state of charge goes below a certain threshold.
|
||||
|
||||
config QTI_POLICY_ENGINE_SENSOR
|
||||
tristate "QTI Policy Engine Sensor device"
|
||||
depends on THERMAL
|
||||
help
|
||||
This enables the QTI Policy Engine sensor device. This driver will
|
||||
register the policy engine recommendation with thermal framework as
|
||||
a sensor. This will enable to provide configuration to mitigate
|
||||
cooling devices when a recommendation is sent from hardware.
|
||||
|
||||
config QTI_QMI_COOLING_DEVICE
|
||||
tristate "QTI QMI cooling devices"
|
||||
depends on QCOM_QMI_HELPERS && THERMAL
|
||||
help
|
||||
This enables the QTI remote subsystem cooling devices. These cooling
|
||||
devices will be used by QTI chipset to place various remote
|
||||
subsystem mitigations like remote processor passive mitigation,
|
||||
remote subsystem voltage restriction at low temperatures etc.
|
||||
The QMI cooling device will interface with remote subsystem
|
||||
using QTI QMI interface.
|
||||
|
||||
config QTI_QMI_SENSOR_V2
|
||||
tristate "QTI QMI TS V2 sensor driver"
|
||||
depends on THERMAL_OF
|
||||
select QCOM_QMI_HELPERS
|
||||
help
|
||||
This enables to list the QTI remote subsystem temperature sensors
|
||||
with QMI TS V2 interface. This driver can read the temperature of
|
||||
the remote sensor. These sensors can take thresholds and notify the
|
||||
thermal framework when the threshold is reached.
|
||||
|
||||
config QTI_CPU_VOLTAGE_COOLING_DEVICE
|
||||
tristate "QTI CPU VOLTAGE cooling devices"
|
||||
depends on CPU_FREQ && THERMAL
|
||||
help
|
||||
This enables the QTI CPU Voltage cooling devices. This cooling
|
||||
device will allow the CPUs with different frequency plan in a
|
||||
cluster to be mitigated together based on the voltages. This will
|
||||
decrease or increase the voltages in a cluster based on thermal
|
||||
conditions.
|
||||
|
||||
config QTI_CPU_HOTPLUG_COOLING_DEVICE
|
||||
tristate "QTI CPU Hotplug cooling devices"
|
||||
depends on THERMAL && HOTPLUG_CPU
|
||||
help
|
||||
This enables the QTI CPU Hotplug cooling devices. These cooling
|
||||
devices will be used by QTI chipset to hotplug a CPU to achieve
|
||||
thermal cooling. CPU Hotplug will be done after core isolation,
|
||||
to prevent any process from waking the mitigated CPU.
|
||||
|
||||
config QTI_DDR_COOLING_DEVICE
|
||||
tristate "QTI DDR cooling devices"
|
||||
depends on THERMAL && INTERCONNECT
|
||||
help
|
||||
This enables the QTI DDR cooling devices. These cooling
|
||||
devices will be used by QTI chipset to place a DDR state request
|
||||
to meet the performance requirement under thermally constrained
|
||||
conditions.
|
||||
|
||||
config QTI_USERSPACE_CDEV
|
||||
tristate "QTI Userspace cooling device"
|
||||
depends on THERMAL_OF
|
||||
help
|
||||
This enables the QTI userspace cooling device, which will help
|
||||
send the mitigation notification via thermal framework netlink
|
||||
socket, so that userspace cooling device can perform a mitigation
|
||||
action.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,23 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
|
||||
|
||||
qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \
|
||||
tsens-8960.o
|
||||
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC_TM5) += qcom-spmi-adc-tm5.o
|
||||
obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
|
||||
obj-$(CONFIG_QCOM_LMH) += lmh.o
|
||||
obj-$(CONFIG_QTI_CPU_PAUSE_COOLING_DEVICE) += thermal_pause.o
|
||||
obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o
|
||||
obj-$(CONFIG_QTI_BCL_SOC_DRIVER) += bcl_soc.o
|
||||
obj-$(CONFIG_QTI_POLICY_ENGINE_SENSOR) += policy_engine.o
|
||||
|
||||
obj-$(CONFIG_QTI_QMI_COOLING_DEVICE) += qti_qmi_cdev.o
|
||||
qti_qmi_cdev-y += thermal_mitigation_device_service_v01.o qmi_cooling.o
|
||||
|
||||
obj-$(CONFIG_QTI_QMI_SENSOR_V2) += qti_qmi_sensor_v2.o
|
||||
qti_qmi_sensor_v2-y += thermal_sensor_service_v02.o qmi_sensors_v2.o
|
||||
|
||||
obj-$(CONFIG_QTI_CPU_VOLTAGE_COOLING_DEVICE) += cpu_voltage_cooling.o
|
||||
obj-$(CONFIG_QTI_CPU_HOTPLUG_COOLING_DEVICE) += cpu_hotplug.o
|
||||
obj-$(CONFIG_QTI_DDR_COOLING_DEVICE) += ddr_cdev.o
|
||||
obj-$(CONFIG_QTI_USERSPACE_CDEV) += qti_userspace_cdev.o
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
#include "thermal_zone_internal.h"
|
||||
|
||||
#define BCL_DRIVER_NAME "bcl_pmic5"
|
||||
#define BCL_MONITOR_EN 0x46
|
||||
|
|
@ -799,6 +800,7 @@ static void bcl_lvl_init(struct platform_device *pdev,
|
|||
return;
|
||||
}
|
||||
thermal_zone_device_update(lbat->tz_dev, THERMAL_DEVICE_UP);
|
||||
qti_update_tz_ops(lbat->tz_dev, true);
|
||||
}
|
||||
|
||||
static void bcl_probe_lvls(struct platform_device *pdev,
|
||||
|
|
@ -856,6 +858,7 @@ static int bcl_remove(struct platform_device *pdev)
|
|||
continue;
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev,
|
||||
bcl_perph->param[i].tz_dev);
|
||||
qti_update_tz_ops(bcl_perph->param[i].tz_dev, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
226
drivers/thermal/qcom/bcl_soc.c
Normal file
226
drivers/thermal/qcom/bcl_soc.c
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/thermal.h>
|
||||
#include "thermal_zone_internal.h"
|
||||
|
||||
#define BCL_DRIVER_NAME "bcl_soc_peripheral"
|
||||
|
||||
struct bcl_device {
|
||||
struct device *dev;
|
||||
struct notifier_block psy_nb;
|
||||
struct work_struct soc_eval_work;
|
||||
long trip_temp;
|
||||
int trip_val;
|
||||
struct mutex state_trans_lock;
|
||||
bool irq_enabled;
|
||||
struct thermal_zone_device *tz_dev;
|
||||
struct thermal_zone_of_device_ops ops;
|
||||
};
|
||||
|
||||
static struct bcl_device *bcl_perph;
|
||||
|
||||
static int bcl_soc_get_trend(void *data, int trip,
|
||||
enum thermal_trend *trend)
|
||||
{
|
||||
struct bcl_device *bcl_perph = data;
|
||||
struct thermal_zone_device *tz = bcl_perph->tz_dev;
|
||||
int trip_temp = 0, trip_hyst = 0, temp, ret;
|
||||
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tz->ops->get_trip_hyst) {
|
||||
ret = tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
temp = READ_ONCE(tz->temperature);
|
||||
|
||||
if (temp >= trip_temp)
|
||||
*trend = THERMAL_TREND_RAISING;
|
||||
else if (!trip_hyst && temp < trip_temp)
|
||||
*trend = THERMAL_TREND_DROPPING;
|
||||
else if (temp <= (trip_temp - trip_hyst))
|
||||
*trend = THERMAL_TREND_DROPPING;
|
||||
else
|
||||
*trend = THERMAL_TREND_STABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcl_set_soc(void *data, int low, int high)
|
||||
{
|
||||
if (high == bcl_perph->trip_temp)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&bcl_perph->state_trans_lock);
|
||||
pr_debug("socd threshold:%d\n", high);
|
||||
bcl_perph->trip_temp = high;
|
||||
if (high == INT_MAX) {
|
||||
bcl_perph->irq_enabled = false;
|
||||
goto unlock_and_exit;
|
||||
}
|
||||
bcl_perph->irq_enabled = true;
|
||||
schedule_work(&bcl_perph->soc_eval_work);
|
||||
|
||||
unlock_and_exit:
|
||||
mutex_unlock(&bcl_perph->state_trans_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcl_read_soc(void *data, int *val)
|
||||
{
|
||||
static struct power_supply *batt_psy;
|
||||
union power_supply_propval ret = {0,};
|
||||
int err = 0;
|
||||
|
||||
*val = 0;
|
||||
if (!batt_psy)
|
||||
batt_psy = power_supply_get_by_name("battery");
|
||||
if (batt_psy) {
|
||||
err = power_supply_get_property(batt_psy,
|
||||
POWER_SUPPLY_PROP_CAPACITY, &ret);
|
||||
if (err) {
|
||||
pr_err("battery percentage read error:%d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
*val = 100 - ret.intval;
|
||||
}
|
||||
pr_debug("soc:%d\n", *val);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void bcl_evaluate_soc(struct work_struct *work)
|
||||
{
|
||||
int battery_depletion;
|
||||
|
||||
if (!bcl_perph->tz_dev)
|
||||
return;
|
||||
|
||||
if (bcl_read_soc(NULL, &battery_depletion))
|
||||
return;
|
||||
|
||||
mutex_lock(&bcl_perph->state_trans_lock);
|
||||
if (!bcl_perph->irq_enabled)
|
||||
goto eval_exit;
|
||||
if (battery_depletion < bcl_perph->trip_temp)
|
||||
goto eval_exit;
|
||||
|
||||
bcl_perph->trip_val = battery_depletion;
|
||||
mutex_unlock(&bcl_perph->state_trans_lock);
|
||||
thermal_zone_device_update(bcl_perph->tz_dev,
|
||||
THERMAL_TRIP_VIOLATED);
|
||||
|
||||
return;
|
||||
eval_exit:
|
||||
mutex_unlock(&bcl_perph->state_trans_lock);
|
||||
}
|
||||
|
||||
static int battery_supply_callback(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct power_supply *psy = data;
|
||||
|
||||
if (strcmp(psy->desc->name, "battery"))
|
||||
return NOTIFY_OK;
|
||||
schedule_work(&bcl_perph->soc_eval_work);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int bcl_soc_remove(struct platform_device *pdev)
|
||||
{
|
||||
power_supply_unreg_notifier(&bcl_perph->psy_nb);
|
||||
flush_work(&bcl_perph->soc_eval_work);
|
||||
if (bcl_perph->tz_dev) {
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev,
|
||||
bcl_perph->tz_dev);
|
||||
qti_update_tz_ops(bcl_perph->tz_dev, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcl_soc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
bcl_perph = devm_kzalloc(&pdev->dev, sizeof(*bcl_perph), GFP_KERNEL);
|
||||
if (!bcl_perph)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&bcl_perph->state_trans_lock);
|
||||
bcl_perph->dev = &pdev->dev;
|
||||
bcl_perph->ops.get_temp = bcl_read_soc;
|
||||
bcl_perph->ops.set_trips = bcl_set_soc;
|
||||
bcl_perph->ops.get_trend = bcl_soc_get_trend;
|
||||
INIT_WORK(&bcl_perph->soc_eval_work, bcl_evaluate_soc);
|
||||
bcl_perph->psy_nb.notifier_call = battery_supply_callback;
|
||||
ret = power_supply_reg_notifier(&bcl_perph->psy_nb);
|
||||
if (ret < 0) {
|
||||
pr_err("soc notifier registration error. defer. err:%d\n",
|
||||
ret);
|
||||
ret = -EPROBE_DEFER;
|
||||
goto bcl_soc_probe_exit;
|
||||
}
|
||||
bcl_perph->tz_dev = thermal_zone_of_sensor_register(&pdev->dev,
|
||||
0, bcl_perph, &bcl_perph->ops);
|
||||
if (IS_ERR(bcl_perph->tz_dev)) {
|
||||
pr_err("soc TZ register failed. err:%ld\n",
|
||||
PTR_ERR(bcl_perph->tz_dev));
|
||||
ret = PTR_ERR(bcl_perph->tz_dev);
|
||||
bcl_perph->tz_dev = NULL;
|
||||
goto bcl_soc_probe_exit;
|
||||
}
|
||||
qti_update_tz_ops(bcl_perph->tz_dev, true);
|
||||
thermal_zone_device_update(bcl_perph->tz_dev, THERMAL_DEVICE_UP);
|
||||
schedule_work(&bcl_perph->soc_eval_work);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, bcl_perph);
|
||||
|
||||
return 0;
|
||||
|
||||
bcl_soc_probe_exit:
|
||||
bcl_soc_remove(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcl_match[] = {
|
||||
{
|
||||
.compatible = "qcom,msm-bcl-soc",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver bcl_driver = {
|
||||
.probe = bcl_soc_probe,
|
||||
.remove = bcl_soc_remove,
|
||||
.driver = {
|
||||
.name = BCL_DRIVER_NAME,
|
||||
.of_match_table = bcl_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(bcl_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
292
drivers/thermal/qcom/cpu_hotplug.c
Normal file
292
drivers/thermal/qcom/cpu_hotplug.c
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#define CPU_HOTPLUG_LEVEL 1
|
||||
|
||||
struct cpu_hot_cdev {
|
||||
struct list_head node;
|
||||
int cpu_id;
|
||||
bool cpu_hot_state;
|
||||
bool cpu_cur_state;
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct device_node *np;
|
||||
struct work_struct reg_work;
|
||||
struct work_struct exec_work;
|
||||
};
|
||||
static enum cpuhp_state cpu_hp_online;
|
||||
static DEFINE_MUTEX(cpu_hot_lock);
|
||||
static LIST_HEAD(cpu_hot_cdev_list);
|
||||
|
||||
static int cpu_hot_hp_online(unsigned int online_cpu)
|
||||
{
|
||||
struct cpu_hot_cdev *cpu_hot_cdev;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&cpu_hot_lock);
|
||||
list_for_each_entry(cpu_hot_cdev, &cpu_hot_cdev_list, node) {
|
||||
if (online_cpu != cpu_hot_cdev->cpu_id)
|
||||
continue;
|
||||
|
||||
if (cpu_hot_cdev->cdev) {
|
||||
if (cpu_hot_cdev->cpu_hot_state) {
|
||||
pr_debug("Offline CPU:%d\n",
|
||||
online_cpu);
|
||||
ret = NOTIFY_BAD;
|
||||
}
|
||||
} else
|
||||
queue_work(system_highpri_wq, &cpu_hot_cdev->reg_work);
|
||||
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&cpu_hot_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpu_hot_set_cur_state - callback function to set the current cooling
|
||||
* state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: set this variable to the current cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to change the cpu hotplug
|
||||
* current cooling state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int cpu_hot_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct cpu_hot_cdev *cpu_hot_cdev = cdev->devdata;
|
||||
|
||||
if (cpu_hot_cdev->cpu_id == -1)
|
||||
return -ENODEV;
|
||||
|
||||
/* Request state should be less than max_level */
|
||||
if (state > CPU_HOTPLUG_LEVEL)
|
||||
return -EINVAL;
|
||||
|
||||
state = !!state;
|
||||
/* Check if the old cooling action is same as new cooling action */
|
||||
if (cpu_hot_cdev->cpu_hot_state == state)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&cpu_hot_lock);
|
||||
cpu_hot_cdev->cpu_hot_state = state;
|
||||
mutex_unlock(&cpu_hot_lock);
|
||||
queue_work(system_highpri_wq, &cpu_hot_cdev->exec_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpu_hot_get_cur_state - callback function to get the current cooling
|
||||
* state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: fill this variable with the current cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to return the cpu hotplug
|
||||
* current cooling state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int cpu_hot_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct cpu_hot_cdev *cpu_hot_cdev = cdev->devdata;
|
||||
|
||||
mutex_lock(&cpu_hot_lock);
|
||||
*state = (cpu_hot_cdev->cpu_hot_state) ?
|
||||
CPU_HOTPLUG_LEVEL : 0;
|
||||
mutex_unlock(&cpu_hot_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpu_hot_get_max_state - callback function to get the max cooling state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: fill this variable with the max cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to return the cpu
|
||||
* hotplug max cooling state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int cpu_hot_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
*state = CPU_HOTPLUG_LEVEL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_cooling_device_ops cpu_hot_cooling_ops = {
|
||||
.get_max_state = cpu_hot_get_max_state,
|
||||
.get_cur_state = cpu_hot_get_cur_state,
|
||||
.set_cur_state = cpu_hot_set_cur_state,
|
||||
};
|
||||
|
||||
static void cpu_hot_execute_cdev(struct work_struct *work)
|
||||
{
|
||||
struct cpu_hot_cdev *cpu_hot_cdev =
|
||||
container_of(work, struct cpu_hot_cdev, exec_work);
|
||||
int ret = 0, cpu = 0;
|
||||
|
||||
mutex_lock(&cpu_hot_lock);
|
||||
cpu = cpu_hot_cdev->cpu_id;
|
||||
if (cpu_hot_cdev->cpu_hot_state == CPU_HOTPLUG_LEVEL) {
|
||||
if (!cpu_hot_cdev->cpu_cur_state)
|
||||
goto unlock_exit;
|
||||
mutex_unlock(&cpu_hot_lock);
|
||||
ret = remove_cpu(cpu);
|
||||
mutex_lock(&cpu_hot_lock);
|
||||
if (ret < 0)
|
||||
pr_err("CPU:%d offline error:%d\n", cpu, ret);
|
||||
else
|
||||
cpu_hot_cdev->cpu_cur_state = false;
|
||||
} else {
|
||||
if (cpu_hot_cdev->cpu_cur_state)
|
||||
goto unlock_exit;
|
||||
mutex_unlock(&cpu_hot_lock);
|
||||
ret = add_cpu(cpu);
|
||||
mutex_lock(&cpu_hot_lock);
|
||||
if (ret)
|
||||
pr_err("CPU:%d online error:%d\n", cpu, ret);
|
||||
else
|
||||
cpu_hot_cdev->cpu_cur_state = true;
|
||||
}
|
||||
unlock_exit:
|
||||
mutex_unlock(&cpu_hot_lock);
|
||||
}
|
||||
|
||||
static void cpu_hot_register_cdev(struct work_struct *work)
|
||||
{
|
||||
struct cpu_hot_cdev *cpu_hot_cdev =
|
||||
container_of(work, struct cpu_hot_cdev, reg_work);
|
||||
char cdev_name[THERMAL_NAME_LENGTH] = "";
|
||||
int ret = 0;
|
||||
|
||||
snprintf(cdev_name, THERMAL_NAME_LENGTH, "cpu-hotplug%d",
|
||||
cpu_hot_cdev->cpu_id);
|
||||
|
||||
cpu_hot_cdev->cdev = thermal_of_cooling_device_register(
|
||||
cpu_hot_cdev->np,
|
||||
cdev_name,
|
||||
cpu_hot_cdev,
|
||||
&cpu_hot_cooling_ops);
|
||||
if (IS_ERR(cpu_hot_cdev->cdev)) {
|
||||
ret = PTR_ERR(cpu_hot_cdev->cdev);
|
||||
pr_err("Cooling register failed for %s, ret:%d\n",
|
||||
cdev_name, ret);
|
||||
cpu_hot_cdev->cdev = NULL;
|
||||
return;
|
||||
}
|
||||
pr_debug("Cooling device [%s] registered.\n", cdev_name);
|
||||
}
|
||||
|
||||
static int cpu_hot_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0, cpu = 0;
|
||||
struct device_node *dev_phandle, *subsys_np = NULL;
|
||||
struct device *cpu_dev;
|
||||
struct cpu_hot_cdev *cpu_hot_cdev = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
INIT_LIST_HEAD(&cpu_hot_cdev_list);
|
||||
for_each_available_child_of_node(np, subsys_np) {
|
||||
cpu_hot_cdev = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*cpu_hot_cdev), GFP_KERNEL);
|
||||
if (!cpu_hot_cdev) {
|
||||
of_node_put(subsys_np);
|
||||
return -ENOMEM;
|
||||
}
|
||||
cpu_hot_cdev->cpu_id = -1;
|
||||
cpu_hot_cdev->cpu_hot_state = false;
|
||||
cpu_hot_cdev->cpu_cur_state = true;
|
||||
cpu_hot_cdev->cdev = NULL;
|
||||
cpu_hot_cdev->np = subsys_np;
|
||||
|
||||
dev_phandle = of_parse_phandle(subsys_np, "qcom,cpu", 0);
|
||||
for_each_possible_cpu(cpu) {
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
if (cpu_dev && cpu_dev->of_node == dev_phandle) {
|
||||
cpu_hot_cdev->cpu_id = cpu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cpu_hot_cdev->cpu_id == -1) {
|
||||
dev_err(&pdev->dev, "Invalid CPU phandle\n");
|
||||
continue;
|
||||
}
|
||||
INIT_WORK(&cpu_hot_cdev->reg_work,
|
||||
cpu_hot_register_cdev);
|
||||
INIT_WORK(&cpu_hot_cdev->exec_work,
|
||||
cpu_hot_execute_cdev);
|
||||
list_add(&cpu_hot_cdev->node, &cpu_hot_cdev_list);
|
||||
}
|
||||
|
||||
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpu-hotplug/cdev:online",
|
||||
cpu_hot_hp_online, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
cpu_hp_online = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpu_hot_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cpu_hot_cdev *cpu_hot_cdev = NULL, *next = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (cpu_hp_online) {
|
||||
cpuhp_remove_state_nocalls(cpu_hp_online);
|
||||
cpu_hp_online = 0;
|
||||
}
|
||||
|
||||
mutex_lock(&cpu_hot_lock);
|
||||
list_for_each_entry_safe(cpu_hot_cdev, next, &cpu_hot_cdev_list, node) {
|
||||
if (!cpu_hot_cdev->cdev)
|
||||
goto loop_skip;
|
||||
thermal_cooling_device_unregister(cpu_hot_cdev->cdev);
|
||||
if (cpu_hot_cdev->cpu_hot_state) {
|
||||
cpu_hot_cdev->cpu_hot_state = false;
|
||||
ret = add_cpu(cpu_hot_cdev->cpu_id);
|
||||
if (ret)
|
||||
pr_err("CPU:%d online error:%d\n",
|
||||
cpu_hot_cdev->cpu_id, ret);
|
||||
}
|
||||
loop_skip:
|
||||
list_del(&cpu_hot_cdev->node);
|
||||
}
|
||||
mutex_unlock(&cpu_hot_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cpu_hot_match[] = {
|
||||
{ .compatible = "qcom,cpu-hotplug", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver cpu_hot_driver = {
|
||||
.probe = cpu_hot_probe,
|
||||
.remove = cpu_hot_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = cpu_hot_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(cpu_hot_driver);
|
||||
MODULE_DESCRIPTION("CPU Hotplug cooling device driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
367
drivers/thermal/qcom/cpu_voltage_cooling.c
Normal file
367
drivers/thermal/qcom/cpu_voltage_cooling.c
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#define CPU_MAP_CT 2
|
||||
#define CC_CDEV_DRIVER "CPU-voltage-cdev"
|
||||
|
||||
struct limits_freq_table {
|
||||
unsigned long frequency;
|
||||
unsigned long volt;
|
||||
};
|
||||
|
||||
struct limits_freq_map {
|
||||
unsigned long frequency[CPU_MAP_CT];
|
||||
};
|
||||
|
||||
struct cc_limits_data {
|
||||
struct list_head node;
|
||||
int map_freq_ct;
|
||||
int thermal_state;
|
||||
int cpu_map[CPU_MAP_CT];
|
||||
struct limits_freq_map *map_freq;
|
||||
struct freq_qos_request cc_qos_req[CPU_MAP_CT];
|
||||
char cdev_name[THERMAL_NAME_LENGTH];
|
||||
struct thermal_cooling_device *cdev;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(cc_list_lock);
|
||||
static LIST_HEAD(cc_cdev_list);
|
||||
|
||||
static int cc_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct cc_limits_data *cc_cdev = cdev->devdata;
|
||||
int idx = 0, ret = 0;
|
||||
|
||||
if (state > cc_cdev->map_freq_ct)
|
||||
return -EINVAL;
|
||||
|
||||
if (state == cc_cdev->thermal_state)
|
||||
return 0;
|
||||
|
||||
cc_cdev->thermal_state = state;
|
||||
|
||||
for (idx = 0; idx < CPU_MAP_CT; idx++) {
|
||||
pr_debug("Mitigate CPU:%d to freq:%lu\n", cc_cdev->cpu_map[idx],
|
||||
cc_cdev->map_freq[state].frequency[idx]);
|
||||
ret = freq_qos_update_request(&cc_cdev->cc_qos_req[idx],
|
||||
cc_cdev->map_freq[state].frequency[idx]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct cc_limits_data *cc_cdev = cdev->devdata;
|
||||
|
||||
*state = cc_cdev->thermal_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct cc_limits_data *cc_cdev = cdev->devdata;
|
||||
|
||||
*state = cc_cdev->map_freq_ct;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_cooling_device_ops cc_cooling_ops = {
|
||||
.get_max_state = cc_get_max_state,
|
||||
.get_cur_state = cc_get_cur_state,
|
||||
.set_cur_state = cc_set_cur_state,
|
||||
};
|
||||
|
||||
static int fetch_opp_table(struct device *dev,
|
||||
struct limits_freq_table **freq_table_inp)
|
||||
{
|
||||
int idx = 0, max_opp_ct;
|
||||
struct limits_freq_table *freq_table = NULL;
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long freq = 0;
|
||||
|
||||
max_opp_ct = dev_pm_opp_get_opp_count(dev);
|
||||
if (max_opp_ct <= 0)
|
||||
return max_opp_ct;
|
||||
|
||||
freq_table = kcalloc(max_opp_ct, sizeof(*freq_table), GFP_KERNEL);
|
||||
if (!freq_table)
|
||||
return -ENOMEM;
|
||||
|
||||
for (; idx < max_opp_ct; idx++, freq++) {
|
||||
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
|
||||
if (IS_ERR(opp)) {
|
||||
pr_err("Error fetching freq\n");
|
||||
goto fetch_err_exit;
|
||||
}
|
||||
freq_table[idx].frequency = freq / 1000; //MHz
|
||||
freq_table[idx].volt = dev_pm_opp_get_voltage(opp) / 1000; //mV
|
||||
pr_debug("%d: freq:%lu Mhz volt:%lu mv\n", idx,
|
||||
freq_table[idx].frequency,
|
||||
freq_table[idx].volt);
|
||||
dev_pm_opp_put(opp);
|
||||
}
|
||||
*freq_table_inp = freq_table;
|
||||
|
||||
return max_opp_ct;
|
||||
fetch_err_exit:
|
||||
kfree(freq_table);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int build_unified_table(struct cc_limits_data *cc_cdev,
|
||||
struct limits_freq_table **table, int *table_ct,
|
||||
int *cpu, int cpu_ct)
|
||||
{
|
||||
struct limits_freq_map *freq_map = NULL;
|
||||
int idx = 0, idy = 0, idz = 0, min_idx = 0, max_v = 0, max_idx = 0;
|
||||
|
||||
for (idx = 0; idx < cpu_ct; idx++) {
|
||||
int table_v = table[idx][table_ct[idx] - 1].volt;
|
||||
|
||||
if ((table_v > max_v) || (table_v == max_v &&
|
||||
table_ct[idx] > table_ct[max_idx])) {
|
||||
max_v = table_v;
|
||||
max_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
cc_cdev->thermal_state = 0;
|
||||
cc_cdev->map_freq_ct = table_ct[max_idx] - 1;
|
||||
min_idx = !max_idx;
|
||||
cc_cdev->cpu_map[0] = cpu[max_idx];
|
||||
cc_cdev->cpu_map[1] = cpu[min_idx];
|
||||
freq_map = kcalloc(table_ct[max_idx], sizeof(*freq_map), GFP_KERNEL);
|
||||
if (!freq_map)
|
||||
return -ENOMEM;
|
||||
pr_info("CPU1:%d CPU2:%d\n", cc_cdev->cpu_map[0], cc_cdev->cpu_map[1]);
|
||||
for (idx = table_ct[max_idx] - 1, idy = table_ct[min_idx] - 1, idz = 0;
|
||||
idx >= 0 && idz < table_ct[max_idx]; idx--, idz++) {
|
||||
int volt = table[max_idx][idx].volt;
|
||||
|
||||
freq_map[idz].frequency[0] = table[max_idx][idx].frequency;
|
||||
for (; idy >= 0 ; idy--) {
|
||||
if (table[min_idx][idy].volt <= volt)
|
||||
break;
|
||||
}
|
||||
if (idy < 0)
|
||||
idy = 0;
|
||||
freq_map[idz].frequency[1] = table[min_idx][idy].frequency;
|
||||
pr_info("freq1:%u freq2:%u\n", freq_map[idz].frequency[0],
|
||||
freq_map[idz].frequency[1]);
|
||||
}
|
||||
|
||||
cc_cdev->map_freq = freq_map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cc_limits_data *opp_init(int *cpus)
|
||||
{
|
||||
int cpu1, cpu2;
|
||||
struct device *cpu1_dev, *cpu2_dev;
|
||||
struct limits_freq_table *cpu1_freq_table, *cpu2_freq_table;
|
||||
struct limits_freq_table *cpu_freq_table[CPU_MAP_CT];
|
||||
int table_ct[CPU_MAP_CT], ret = 0;
|
||||
struct cc_limits_data *cc_cdev = NULL;
|
||||
|
||||
cpu1 = cpus[0];
|
||||
cpu2 = cpus[1];
|
||||
cpu1_dev = get_cpu_device(cpu1);
|
||||
if (!cpu1_dev) {
|
||||
pr_err("couldn't find cpu:%d\n", cpu1);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
cpu2_dev = get_cpu_device(cpu2);
|
||||
if (!cpu2_dev) {
|
||||
pr_err("couldn't find cpu:%d\n", cpu2);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
table_ct[0] = fetch_opp_table(cpu1_dev, &cpu1_freq_table);
|
||||
if (table_ct[0] <= 0)
|
||||
goto opp_err_exit;
|
||||
|
||||
table_ct[1] = fetch_opp_table(cpu2_dev, &cpu2_freq_table);
|
||||
if (table_ct[1] <= 0)
|
||||
goto opp_err_exit;
|
||||
|
||||
cc_cdev = kzalloc(sizeof(*cc_cdev), GFP_KERNEL);
|
||||
if (!cc_cdev)
|
||||
goto opp_err_exit;
|
||||
cpu_freq_table[0] = cpu1_freq_table;
|
||||
cpu_freq_table[1] = cpu2_freq_table;
|
||||
ret = build_unified_table(cc_cdev, cpu_freq_table, table_ct, cpus,
|
||||
CPU_MAP_CT);
|
||||
if (ret < 0)
|
||||
goto opp_err_exit;
|
||||
|
||||
kfree(cpu1_freq_table);
|
||||
kfree(cpu2_freq_table);
|
||||
return cc_cdev;
|
||||
opp_err_exit:
|
||||
kfree(cpu1_freq_table);
|
||||
kfree(cpu2_freq_table);
|
||||
if (cc_cdev) {
|
||||
kfree(cc_cdev->map_freq);
|
||||
kfree(cc_cdev);
|
||||
}
|
||||
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
static int cc_init(struct device_node *np, int *cpus)
|
||||
{
|
||||
struct cc_limits_data *cc_cdev;
|
||||
int idx = 0, ret = 0;
|
||||
struct cpufreq_policy *policy;
|
||||
|
||||
mutex_lock(&cc_list_lock);
|
||||
list_for_each_entry(cc_cdev, &cc_cdev_list, node) {
|
||||
if ((cpus[0] == cc_cdev->cpu_map[0] &&
|
||||
cpus[1] == cc_cdev->cpu_map[1]) ||
|
||||
(cpus[0] == cc_cdev->cpu_map[1] &&
|
||||
cpus[1] == cc_cdev->cpu_map[0])) {
|
||||
mutex_unlock(&cc_list_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
policy = cpufreq_cpu_get(cpus[0]);
|
||||
if (!policy) {
|
||||
pr_err("No policy for CPU:%d. Defer.\n", cpus[0]);
|
||||
mutex_unlock(&cc_list_lock);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (cpumask_test_cpu(cpus[1], policy->related_cpus)) {
|
||||
pr_err("CPUs:%d %d are related.\n", cpus[0], cpus[1]);
|
||||
cpufreq_cpu_put(policy);
|
||||
mutex_unlock(&cc_list_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
cpufreq_cpu_put(policy);
|
||||
|
||||
cc_cdev = opp_init(cpus);
|
||||
if (IS_ERR(cc_cdev)) {
|
||||
ret = PTR_ERR(cc_cdev);
|
||||
mutex_unlock(&cc_list_lock);
|
||||
return ret;
|
||||
}
|
||||
for (idx = 0; idx < CPU_MAP_CT; idx++) {
|
||||
policy = cpufreq_cpu_get(cc_cdev->cpu_map[idx]);
|
||||
if (!policy) {
|
||||
pr_err("No policy for CPU:%d\n", cc_cdev->cpu_map[idx]);
|
||||
ret = -ENODEV;
|
||||
goto cc_err_exit;
|
||||
}
|
||||
ret = freq_qos_add_request(&policy->constraints,
|
||||
&cc_cdev->cc_qos_req[idx], FREQ_QOS_MAX,
|
||||
cc_cdev->map_freq[0].frequency[idx]);
|
||||
cpufreq_cpu_put(policy);
|
||||
if (ret < 0) {
|
||||
pr_err("CPU%d Failed to add freq constraint (%d)\n",
|
||||
cc_cdev->cpu_map[idx], ret);
|
||||
goto cc_err_exit;
|
||||
}
|
||||
}
|
||||
snprintf(cc_cdev->cdev_name, THERMAL_NAME_LENGTH,
|
||||
"thermal-cluster-%d-%d", cpus[0], cpus[1]);
|
||||
cc_cdev->cdev = thermal_of_cooling_device_register(
|
||||
np, cc_cdev->cdev_name, cc_cdev,
|
||||
&cc_cooling_ops);
|
||||
list_add(&cc_cdev->node, &cc_cdev_list);
|
||||
mutex_unlock(&cc_list_lock);
|
||||
|
||||
return 0;
|
||||
cc_err_exit:
|
||||
mutex_unlock(&cc_list_lock);
|
||||
for (idx = 0; idx < CPU_MAP_CT; idx++)
|
||||
freq_qos_remove_request(&cc_cdev->cc_qos_req[idx]);
|
||||
kfree(cc_cdev->map_freq);
|
||||
kfree(cc_cdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc_cooling_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *dev_phandle, *subsys_np = NULL;
|
||||
struct device *cpu_dev;
|
||||
int ret = 0, idx = 0, cpu;
|
||||
u32 cpu_map[CPU_MAP_CT];
|
||||
|
||||
for_each_available_child_of_node(np, subsys_np) {
|
||||
for (idx = 0; idx < CPU_MAP_CT; idx++) {
|
||||
dev_phandle = of_parse_phandle(subsys_np, "qcom,cpus",
|
||||
idx);
|
||||
for_each_possible_cpu(cpu) {
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
if (cpu_dev && cpu_dev->of_node ==
|
||||
dev_phandle) {
|
||||
cpu_map[idx] = cpu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = cc_init(subsys_np, cpu_map);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc_cooling_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cc_limits_data *cc_cdev, *cc_next;
|
||||
int idx = 0;
|
||||
|
||||
mutex_lock(&cc_list_lock);
|
||||
list_for_each_entry_safe(cc_cdev, cc_next, &cc_cdev_list, node) {
|
||||
if (cc_cdev->cdev)
|
||||
thermal_cooling_device_unregister(cc_cdev->cdev);
|
||||
|
||||
list_del(&cc_cdev->node);
|
||||
for (idx = 0; idx < CPU_MAP_CT; idx++)
|
||||
freq_qos_remove_request(&cc_cdev->cc_qos_req[idx]);
|
||||
kfree(cc_cdev->map_freq);
|
||||
kfree(cc_cdev);
|
||||
}
|
||||
mutex_unlock(&cc_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cc_cooling_device_match[] = {
|
||||
{.compatible = "qcom,cc-cooling-devices"},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver cc_cooling_driver = {
|
||||
.probe = cc_cooling_probe,
|
||||
.remove = cc_cooling_remove,
|
||||
.driver = {
|
||||
.name = CC_CDEV_DRIVER,
|
||||
.of_match_table = cc_cooling_device_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(cc_cooling_driver);
|
||||
MODULE_DESCRIPTION("CPU Voltage cooling device driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
229
drivers/thermal/qcom/ddr_cdev.c
Normal file
229
drivers/thermal/qcom/ddr_cdev.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/interconnect.h>
|
||||
|
||||
#define DDR_CDEV_NAME "ddr-cdev"
|
||||
|
||||
struct ddr_cdev {
|
||||
uint32_t cur_state;
|
||||
uint32_t max_state;
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct icc_path *icc_path;
|
||||
struct device *dev;
|
||||
uint32_t *freq_table;
|
||||
uint32_t freq_table_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* ddr_set_cur_state - callback function to set the ddr state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: set this variable to the current cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to change the
|
||||
* DDR state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int ddr_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct ddr_cdev *ddr_cdev = cdev->devdata;
|
||||
int ret = 0;
|
||||
|
||||
/* Request state should be less than max_level */
|
||||
if (state > ddr_cdev->max_state)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if the old cooling action is same as new cooling action */
|
||||
if (ddr_cdev->cur_state == state)
|
||||
return 0;
|
||||
|
||||
ret = icc_set_bw(ddr_cdev->icc_path, 0, ddr_cdev->freq_table[state]);
|
||||
if (ret < 0) {
|
||||
dev_err(ddr_cdev->dev, "Error placing DDR freq%u. err:%d\n",
|
||||
ddr_cdev->freq_table[state], ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ddr_cdev->cur_state = state;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ddr_get_cur_state - callback function to get the current cooling
|
||||
* state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: fill this variable with the current cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to return the
|
||||
* current DDR state request.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int ddr_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct ddr_cdev *ddr_cdev = cdev->devdata;
|
||||
*state = ddr_cdev->cur_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ddr_get_max_state - callback function to get the max cooling state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: fill this variable with the max cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to return the DDR
|
||||
* max cooling state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int ddr_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct ddr_cdev *ddr_cdev = cdev->devdata;
|
||||
*state = ddr_cdev->max_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_cooling_device_ops ddr_cdev_ops = {
|
||||
.get_max_state = ddr_get_max_state,
|
||||
.get_cur_state = ddr_get_cur_state,
|
||||
.set_cur_state = ddr_set_cur_state,
|
||||
};
|
||||
|
||||
static int ddr_cdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0, opp_ct = 0, bus_width = 1, idx = 0;
|
||||
struct ddr_cdev *ddr_cdev = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
uint32_t *freq_table = NULL;
|
||||
char cdev_name[THERMAL_NAME_LENGTH] = DDR_CDEV_NAME;
|
||||
|
||||
ddr_cdev = devm_kzalloc(dev, sizeof(*ddr_cdev), GFP_KERNEL);
|
||||
if (!ddr_cdev)
|
||||
return -ENOMEM;
|
||||
ddr_cdev->icc_path = of_icc_get(dev, NULL);
|
||||
if (IS_ERR(ddr_cdev->icc_path)) {
|
||||
ret = PTR_ERR(ddr_cdev->icc_path);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Unable to register icc path: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!of_find_property(np, "qcom,freq-table", &opp_ct)) {
|
||||
dev_err(dev, "No DDR frequency entries\n");
|
||||
ret = -ENODEV;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
if (opp_ct <= 0) {
|
||||
dev_err(dev, "No DDR frequency\n");
|
||||
ret = -ENODEV;
|
||||
goto err_exit;
|
||||
}
|
||||
opp_ct = opp_ct / sizeof(*freq_table);
|
||||
|
||||
/* Add one more entry for 0 or no DDR BW vote */
|
||||
opp_ct++;
|
||||
|
||||
freq_table = devm_kcalloc(dev, opp_ct, sizeof(*freq_table), GFP_KERNEL);
|
||||
if (!freq_table) {
|
||||
ret = -ENOMEM;
|
||||
goto err_exit;
|
||||
}
|
||||
freq_table[0] = 0;
|
||||
|
||||
ret = of_property_read_u32_array(np, "qcom,freq-table",
|
||||
&freq_table[1], opp_ct - 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "DDR frequency read error:%d\n", ret);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
ddr_cdev->freq_table = freq_table;
|
||||
ddr_cdev->freq_table_size = opp_ct;
|
||||
ddr_cdev->cur_state = 0;
|
||||
ddr_cdev->max_state = opp_ct - 1;
|
||||
ddr_cdev->dev = dev;
|
||||
|
||||
ret = of_property_read_u32(np, "qcom,bus-width", &bus_width);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "DDR bus width read error:%d\n", ret);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < opp_ct; idx++)
|
||||
ddr_cdev->freq_table[idx] *= bus_width;
|
||||
|
||||
ret = icc_set_bw(ddr_cdev->icc_path, 0, freq_table[0]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error placing DDR freq request. err:%d\n",
|
||||
ret);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
ddr_cdev->cdev = thermal_of_cooling_device_register(np, cdev_name,
|
||||
ddr_cdev, &ddr_cdev_ops);
|
||||
if (IS_ERR(ddr_cdev->cdev)) {
|
||||
ret = PTR_ERR(ddr_cdev->cdev);
|
||||
dev_err(dev, "Cdev register failed for %s, ret:%d\n",
|
||||
cdev_name, ret);
|
||||
goto err_exit;
|
||||
}
|
||||
dev_dbg(dev, "Cooling device [%s] registered.\n", cdev_name);
|
||||
dev_set_drvdata(dev, ddr_cdev);
|
||||
|
||||
return 0;
|
||||
err_exit:
|
||||
icc_put(ddr_cdev->icc_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ddr_cdev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ddr_cdev *ddr_cdev =
|
||||
(struct ddr_cdev *)dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (ddr_cdev->cdev) {
|
||||
thermal_cooling_device_unregister(ddr_cdev->cdev);
|
||||
ddr_cdev->cdev = NULL;
|
||||
}
|
||||
if (ddr_cdev->icc_path) {
|
||||
icc_set_bw(ddr_cdev->icc_path, 0, ddr_cdev->freq_table[0]);
|
||||
icc_put(ddr_cdev->icc_path);
|
||||
ddr_cdev->icc_path = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ddr_cdev_match[] = {
|
||||
{ .compatible = "qcom,ddr-cooling-device", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver ddr_cdev_driver = {
|
||||
.probe = ddr_cdev_probe,
|
||||
.remove = ddr_cdev_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = ddr_cdev_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ddr_cdev_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
220
drivers/thermal/qcom/policy_engine.c
Normal file
220
drivers/thermal/qcom/policy_engine.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include "thermal_zone_internal.h"
|
||||
|
||||
#define PE_SENS_DRIVER "policy-engine-sensor"
|
||||
#define PE_INT_ENABLE_OFFSET 0x530
|
||||
#define PE_STATUS_OFFSET 0x590
|
||||
#define PE_INT_STATUS_OFFSET 0x620
|
||||
#define PE_INT_STATUS1_OFFSET 0x630
|
||||
#define PE_INTR_CFG 0x11000
|
||||
#define PE_INTR_CLEAR 0x11111
|
||||
#define PE_STS_CLEAR 0xFFFF
|
||||
#define PE_READ_MITIGATION_IDX(val) ((val >> 16) & 0x1F)
|
||||
|
||||
struct pe_sensor_data {
|
||||
struct device *dev;
|
||||
struct thermal_zone_device *tz_dev;
|
||||
int32_t high_thresh;
|
||||
int32_t low_thresh;
|
||||
int32_t irq_num;
|
||||
void __iomem *regmap;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
static int pe_sensor_get_trend(void *data, int trip, enum thermal_trend *trend)
|
||||
{
|
||||
struct pe_sensor_data *pe_sens = (struct pe_sensor_data *)data;
|
||||
struct thermal_zone_device *tz = pe_sens->tz_dev;
|
||||
int value, last_value;
|
||||
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
value = READ_ONCE(tz->temperature);
|
||||
last_value = READ_ONCE(tz->last_temperature);
|
||||
|
||||
if (!value)
|
||||
*trend = THERMAL_TREND_DROPPING;
|
||||
else if (value > last_value)
|
||||
*trend = THERMAL_TREND_RAISING;
|
||||
else if (value < last_value)
|
||||
*trend = THERMAL_TREND_DROPPING;
|
||||
else
|
||||
*trend = THERMAL_TREND_STABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fetch_mitigation_table_idx(struct pe_sensor_data *pe_sens, int *temp)
|
||||
{
|
||||
u32 data = 0;
|
||||
|
||||
data = readl_relaxed(pe_sens->regmap + PE_STATUS_OFFSET);
|
||||
*temp = PE_READ_MITIGATION_IDX(data);
|
||||
dev_dbg(pe_sens->dev, "PE data:%d index:%d\n", data, *temp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pe_sensor_read(void *data, int *temp)
|
||||
{
|
||||
struct pe_sensor_data *pe_sens = (struct pe_sensor_data *)data;
|
||||
|
||||
return fetch_mitigation_table_idx(pe_sens, temp);
|
||||
}
|
||||
|
||||
static int pe_sensor_set_trips(void *data, int low, int high)
|
||||
{
|
||||
struct pe_sensor_data *pe_sens = (struct pe_sensor_data *)data;
|
||||
|
||||
mutex_lock(&pe_sens->mutex);
|
||||
if (pe_sens->high_thresh == high &&
|
||||
pe_sens->low_thresh == low)
|
||||
goto unlock_exit;
|
||||
pe_sens->high_thresh = high;
|
||||
pe_sens->low_thresh = low;
|
||||
dev_dbg(pe_sens->dev, "PE rail set trip. high:%d low:%d\n",
|
||||
high, low);
|
||||
|
||||
unlock_exit:
|
||||
mutex_unlock(&pe_sens->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_of_device_ops pe_sensor_ops = {
|
||||
.get_temp = pe_sensor_read,
|
||||
.set_trips = pe_sensor_set_trips,
|
||||
.get_trend = pe_sensor_get_trend,
|
||||
};
|
||||
|
||||
static irqreturn_t pe_handle_irq(int irq, void *data)
|
||||
{
|
||||
struct pe_sensor_data *pe_sens = (struct pe_sensor_data *)data;
|
||||
int val = 0, ret = 0;
|
||||
|
||||
writel_relaxed(PE_INTR_CLEAR, pe_sens->regmap + PE_INT_STATUS_OFFSET);
|
||||
writel_relaxed(PE_STS_CLEAR, pe_sens->regmap + PE_INT_STATUS1_OFFSET);
|
||||
|
||||
ret = fetch_mitigation_table_idx(pe_sens, &val);
|
||||
if (ret)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
mutex_lock(&pe_sens->mutex);
|
||||
dev_dbg(pe_sens->dev, "Policy Engine interrupt fired value:%d\n", val);
|
||||
if (pe_sens->tz_dev && (val >= pe_sens->high_thresh ||
|
||||
val <= pe_sens->low_thresh)) {
|
||||
mutex_unlock(&pe_sens->mutex);
|
||||
thermal_zone_device_update(pe_sens->tz_dev,
|
||||
THERMAL_TRIP_VIOLATED);
|
||||
} else
|
||||
mutex_unlock(&pe_sens->mutex);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pe_sens_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret = 0;
|
||||
struct pe_sensor_data *pe_sens;
|
||||
struct resource *res;
|
||||
|
||||
pe_sens = devm_kzalloc(dev, sizeof(*pe_sens), GFP_KERNEL);
|
||||
if (!pe_sens)
|
||||
return -ENOMEM;
|
||||
pe_sens->dev = dev;
|
||||
pe_sens->high_thresh = INT_MAX;
|
||||
pe_sens->low_thresh = INT_MIN;
|
||||
mutex_init(&pe_sens->mutex);
|
||||
|
||||
dev_set_drvdata(dev, pe_sens);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "Couldn't get MEM resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(dev, "pe@0x%x size:%d\n", res->start,
|
||||
resource_size(res));
|
||||
|
||||
pe_sens->regmap = devm_ioremap_resource(dev, res);
|
||||
if (!pe_sens->regmap) {
|
||||
dev_err(dev, "Couldn't get regmap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pe_sens->irq_num = platform_get_irq(pdev, 0);
|
||||
if (pe_sens->irq_num < 0) {
|
||||
dev_err(dev, "Couldn't get irq number\n");
|
||||
return pe_sens->irq_num;
|
||||
}
|
||||
pe_sens->tz_dev = devm_thermal_zone_of_sensor_register(
|
||||
dev, 0, pe_sens, &pe_sensor_ops);
|
||||
if (IS_ERR_OR_NULL(pe_sens->tz_dev)) {
|
||||
ret = PTR_ERR(pe_sens->tz_dev);
|
||||
if (ret != -ENODEV)
|
||||
dev_err(dev, "sensor register failed. ret:%d\n", ret);
|
||||
pe_sens->tz_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
qti_update_tz_ops(pe_sens->tz_dev, true);
|
||||
|
||||
writel_relaxed(PE_INTR_CFG, pe_sens->regmap + PE_INT_ENABLE_OFFSET);
|
||||
writel_relaxed(PE_INTR_CLEAR, pe_sens->regmap + PE_INT_STATUS_OFFSET);
|
||||
writel_relaxed(PE_STS_CLEAR, pe_sens->regmap + PE_INT_STATUS1_OFFSET);
|
||||
ret = devm_request_threaded_irq(dev, pe_sens->irq_num, NULL,
|
||||
pe_handle_irq,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
dev_name(dev), pe_sens);
|
||||
if (ret) {
|
||||
dev_err(dev, "Couldn't get irq registered\n");
|
||||
thermal_zone_of_sensor_unregister(pe_sens->dev,
|
||||
pe_sens->tz_dev);
|
||||
return ret;
|
||||
}
|
||||
enable_irq_wake(pe_sens->irq_num);
|
||||
|
||||
dev_dbg(dev, "PE sensor register success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pe_sens_device_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pe_sensor_data *pe_sens =
|
||||
(struct pe_sensor_data *)dev_get_drvdata(&pdev->dev);
|
||||
|
||||
thermal_zone_of_sensor_unregister(pe_sens->dev, pe_sens->tz_dev);
|
||||
qti_update_tz_ops(pe_sens->tz_dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pe_sens_device_match[] = {
|
||||
{.compatible = "qcom,policy-engine"},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver pe_sens_device_driver = {
|
||||
.probe = pe_sens_device_probe,
|
||||
.remove = pe_sens_device_remove,
|
||||
.driver = {
|
||||
.name = PE_SENS_DRIVER,
|
||||
.of_match_table = pe_sens_device_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(pe_sens_device_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
622
drivers/thermal/qcom/qmi_cooling.c
Normal file
622
drivers/thermal/qcom/qmi_cooling.c
Normal file
|
|
@ -0,0 +1,622 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/soc/qcom/qmi.h>
|
||||
#include <linux/net.h>
|
||||
|
||||
#include "thermal_mitigation_device_service_v01.h"
|
||||
|
||||
#define QMI_CDEV_DRIVER "qmi-cooling-device"
|
||||
#define QMI_TMD_RESP_TOUT msecs_to_jiffies(100)
|
||||
|
||||
struct qmi_cooling_device {
|
||||
struct device_node *np;
|
||||
char cdev_name[QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01];
|
||||
char qmi_name[QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01];
|
||||
bool connection_active;
|
||||
struct list_head qmi_node;
|
||||
struct thermal_cooling_device *cdev;
|
||||
unsigned int mtgn_state;
|
||||
unsigned int max_level;
|
||||
struct qmi_tmd_instance *tmd;
|
||||
};
|
||||
|
||||
struct qmi_tmd_instance {
|
||||
struct device *dev;
|
||||
struct qmi_handle handle;
|
||||
struct mutex mutex;
|
||||
uint32_t inst_id;
|
||||
struct list_head tmd_cdev_list;
|
||||
struct work_struct svc_arrive_work;
|
||||
};
|
||||
|
||||
static struct qmi_tmd_instance *tmd_instances;
|
||||
static int tmd_inst_cnt;
|
||||
|
||||
static char device_clients[][QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01] = {
|
||||
{"pa"},
|
||||
{"pa_fr1"},
|
||||
{"cx_vdd_limit"},
|
||||
{"modem"},
|
||||
{"modem_current"},
|
||||
{"modem_skin"},
|
||||
{"modem_bw"},
|
||||
{"modem_bw_backoff"},
|
||||
{"vbatt_low"},
|
||||
{"charge_state"},
|
||||
{"mmw0"},
|
||||
{"mmw1"},
|
||||
{"mmw2"},
|
||||
{"mmw3"},
|
||||
{"mmw_skin0"},
|
||||
{"mmw_skin1"},
|
||||
{"mmw_skin2"},
|
||||
{"mmw_skin3"},
|
||||
{"wlan"},
|
||||
{"wlan_bw"},
|
||||
{"mmw_skin0_dsc"},
|
||||
{"mmw_skin1_dsc"},
|
||||
{"mmw_skin2_dsc"},
|
||||
{"mmw_skin3_dsc"},
|
||||
{"modem_skin_lte_dsc"},
|
||||
{"modem_skin_nr_dsc"},
|
||||
{"pa_dsc"},
|
||||
{"pa_fr1_dsc"},
|
||||
{"cdsp_sw"},
|
||||
{"cdsp_hw"},
|
||||
{"cpuv_restriction_cold"},
|
||||
{"cpr_cold"},
|
||||
{"modem_lte_dsc"},
|
||||
{"modem_nr_dsc"},
|
||||
{"modem_nr_scg_dsc"},
|
||||
{"sdr0_lte_dsc"},
|
||||
{"sdr1_lte_dsc"},
|
||||
{"sdr0_nr_dsc"},
|
||||
{"sdr1_nr_dsc"},
|
||||
{"sdr0_nr_scg_dsc"},
|
||||
{"sdr1_nr_scg_dsc"},
|
||||
{"pa_lte_sdr0_dsc"},
|
||||
{"pa_lte_sdr1_dsc"},
|
||||
{"pa_nr_sdr0_dsc"},
|
||||
{"pa_nr_sdr1_dsc"},
|
||||
{"pa_nr_sdr0_scg_dsc"},
|
||||
{"pa_nr_sdr1_scg_dsc"},
|
||||
{"mmw0_dsc"},
|
||||
{"mmw1_dsc"},
|
||||
{"mmw2_dsc"},
|
||||
{"mmw3_dsc"},
|
||||
{"mmw_ific_dsc"},
|
||||
{"modem_lte_sub1_dsc"},
|
||||
{"modem_nr_sub1_dsc"},
|
||||
{"modem_nr_scg_sub1_dsc"},
|
||||
{"sdr0_lte_sub1_dsc"},
|
||||
{"sdr1_lte_sub1_dsc"},
|
||||
{"sdr0_nr_sub1_dsc"},
|
||||
{"sdr1_nr_sub1_dsc"},
|
||||
{"pa_lte_sdr0_sub1_dsc"},
|
||||
{"pa_lte_sdr1_sub1_dsc"},
|
||||
{"pa_nr_sdr0_sub1_dsc"},
|
||||
{"pa_nr_sdr1_sub1_dsc"},
|
||||
{"pa_nr_sdr0_scg_sub1_dsc"},
|
||||
{"pa_nr_sdr1_scg_sub1_dsc"},
|
||||
{"mmw0_sub1_dsc"},
|
||||
{"mmw1_sub1_dsc"},
|
||||
{"mmw2_sub1_dsc"},
|
||||
{"mmw3_sub1_dsc"},
|
||||
{"mmw_ific_sub1_dsc"},
|
||||
};
|
||||
|
||||
static int qmi_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct qmi_cooling_device *qmi_cdev = cdev->devdata;
|
||||
|
||||
if (!qmi_cdev)
|
||||
return -EINVAL;
|
||||
|
||||
*state = qmi_cdev->max_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmi_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct qmi_cooling_device *qmi_cdev = cdev->devdata;
|
||||
|
||||
if (!qmi_cdev)
|
||||
return -EINVAL;
|
||||
|
||||
*state = qmi_cdev->mtgn_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmi_tmd_send_state_request(struct qmi_cooling_device *qmi_cdev,
|
||||
uint8_t state)
|
||||
{
|
||||
int ret = 0;
|
||||
struct tmd_set_mitigation_level_req_msg_v01 req;
|
||||
struct tmd_set_mitigation_level_resp_msg_v01 tmd_resp;
|
||||
struct qmi_tmd_instance *tmd = qmi_cdev->tmd;
|
||||
struct qmi_txn txn;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
memset(&tmd_resp, 0, sizeof(tmd_resp));
|
||||
|
||||
strscpy(req.mitigation_dev_id.mitigation_dev_id, qmi_cdev->qmi_name,
|
||||
QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01);
|
||||
req.mitigation_level = state;
|
||||
|
||||
mutex_lock(&tmd->mutex);
|
||||
|
||||
ret = qmi_txn_init(&tmd->handle, &txn,
|
||||
tmd_set_mitigation_level_resp_msg_v01_ei, &tmd_resp);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi set state:%d txn init failed for %s ret:%d\n",
|
||||
state, qmi_cdev->cdev_name, ret);
|
||||
goto qmi_send_exit;
|
||||
}
|
||||
|
||||
ret = qmi_send_request(&tmd->handle, NULL, &txn,
|
||||
QMI_TMD_SET_MITIGATION_LEVEL_REQ_V01,
|
||||
TMD_SET_MITIGATION_LEVEL_REQ_MSG_V01_MAX_MSG_LEN,
|
||||
tmd_set_mitigation_level_req_msg_v01_ei, &req);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi set state:%d txn send failed for %s ret:%d\n",
|
||||
state, qmi_cdev->cdev_name, ret);
|
||||
qmi_txn_cancel(&txn);
|
||||
goto qmi_send_exit;
|
||||
}
|
||||
|
||||
ret = qmi_txn_wait(&txn, QMI_TMD_RESP_TOUT);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi set state:%d txn wait failed for %s ret:%d\n",
|
||||
state, qmi_cdev->cdev_name, ret);
|
||||
goto qmi_send_exit;
|
||||
}
|
||||
if (tmd_resp.resp.result != QMI_RESULT_SUCCESS_V01) {
|
||||
ret = tmd_resp.resp.result;
|
||||
pr_err("qmi set state:%d NOT success for %s ret:%d\n",
|
||||
state, qmi_cdev->cdev_name, ret);
|
||||
goto qmi_send_exit;
|
||||
}
|
||||
ret = 0;
|
||||
pr_debug("Requested qmi state:%d for %s\n", state, qmi_cdev->cdev_name);
|
||||
|
||||
qmi_send_exit:
|
||||
mutex_unlock(&tmd->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct qmi_cooling_device *qmi_cdev = cdev->devdata;
|
||||
int ret = 0;
|
||||
|
||||
if (!qmi_cdev)
|
||||
return -EINVAL;
|
||||
|
||||
if (state > qmi_cdev->max_level)
|
||||
return -EINVAL;
|
||||
|
||||
if (qmi_cdev->mtgn_state == state)
|
||||
return 0;
|
||||
|
||||
/* save it and return if server exit */
|
||||
if (!qmi_cdev->connection_active) {
|
||||
qmi_cdev->mtgn_state = state;
|
||||
pr_debug("Pending request:%ld for %s\n", state,
|
||||
qmi_cdev->cdev_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* It is best effort to save state even if QMI fail */
|
||||
ret = qmi_tmd_send_state_request(qmi_cdev, (uint8_t)state);
|
||||
|
||||
qmi_cdev->mtgn_state = state;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_cooling_device_ops qmi_device_ops = {
|
||||
.get_max_state = qmi_get_max_state,
|
||||
.get_cur_state = qmi_get_cur_state,
|
||||
.set_cur_state = qmi_set_cur_state,
|
||||
};
|
||||
|
||||
static int qmi_register_cooling_device(struct qmi_cooling_device *qmi_cdev)
|
||||
{
|
||||
qmi_cdev->cdev = thermal_of_cooling_device_register(
|
||||
qmi_cdev->np,
|
||||
qmi_cdev->cdev_name,
|
||||
qmi_cdev,
|
||||
&qmi_device_ops);
|
||||
if (IS_ERR(qmi_cdev->cdev)) {
|
||||
pr_err("Cooling register failed for %s, ret:%ld\n",
|
||||
qmi_cdev->cdev_name, PTR_ERR(qmi_cdev->cdev));
|
||||
return PTR_ERR(qmi_cdev->cdev);
|
||||
}
|
||||
pr_debug("Cooling register success for %s\n", qmi_cdev->cdev_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_devices_and_register(struct qmi_tmd_instance *tmd)
|
||||
{
|
||||
struct tmd_get_mitigation_device_list_req_msg_v01 req;
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01 *tmd_resp;
|
||||
int ret = 0, i;
|
||||
struct qmi_txn txn;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
/* size of tmd_resp is very high, use heap memory rather than stack */
|
||||
tmd_resp = kzalloc(sizeof(*tmd_resp), GFP_KERNEL);
|
||||
if (!tmd_resp)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&tmd->mutex);
|
||||
ret = qmi_txn_init(&tmd->handle, &txn,
|
||||
tmd_get_mitigation_device_list_resp_msg_v01_ei, tmd_resp);
|
||||
if (ret < 0) {
|
||||
pr_err("Transaction Init error for inst_id:0x%x ret:%d\n",
|
||||
tmd->inst_id, ret);
|
||||
goto reg_exit;
|
||||
}
|
||||
|
||||
ret = qmi_send_request(&tmd->handle, NULL, &txn,
|
||||
QMI_TMD_GET_MITIGATION_DEVICE_LIST_REQ_V01,
|
||||
TMD_GET_MITIGATION_DEVICE_LIST_REQ_MSG_V01_MAX_MSG_LEN,
|
||||
tmd_get_mitigation_device_list_req_msg_v01_ei,
|
||||
&req);
|
||||
if (ret < 0) {
|
||||
qmi_txn_cancel(&txn);
|
||||
goto reg_exit;
|
||||
}
|
||||
|
||||
ret = qmi_txn_wait(&txn, QMI_TMD_RESP_TOUT);
|
||||
if (ret < 0) {
|
||||
pr_err("Transaction wait error for inst_id:0x%x ret:%d\n",
|
||||
tmd->inst_id, ret);
|
||||
goto reg_exit;
|
||||
}
|
||||
if (tmd_resp->resp.result != QMI_RESULT_SUCCESS_V01) {
|
||||
ret = tmd_resp->resp.result;
|
||||
pr_err("Get device list NOT success for inst_id:0x%x ret:%d\n",
|
||||
tmd->inst_id, ret);
|
||||
goto reg_exit;
|
||||
}
|
||||
mutex_unlock(&tmd->mutex);
|
||||
|
||||
for (i = 0; i < tmd_resp->mitigation_device_list_len; i++) {
|
||||
struct qmi_cooling_device *qmi_cdev = NULL;
|
||||
|
||||
list_for_each_entry(qmi_cdev, &tmd->tmd_cdev_list,
|
||||
qmi_node) {
|
||||
struct tmd_mitigation_dev_list_type_v01 *device =
|
||||
&tmd_resp->mitigation_device_list[i];
|
||||
|
||||
if ((strncasecmp(qmi_cdev->qmi_name,
|
||||
device->mitigation_dev_id.mitigation_dev_id,
|
||||
QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01)))
|
||||
continue;
|
||||
|
||||
qmi_cdev->connection_active = true;
|
||||
qmi_cdev->max_level = device->max_mitigation_level;
|
||||
/*
|
||||
* It is better to set current state
|
||||
* initially or during restart
|
||||
*/
|
||||
qmi_tmd_send_state_request(qmi_cdev,
|
||||
qmi_cdev->mtgn_state);
|
||||
if (!qmi_cdev->cdev)
|
||||
ret = qmi_register_cooling_device(qmi_cdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; tmd_resp->mitigation_device_list_ext01_valid &&
|
||||
i < tmd_resp->mitigation_device_list_ext01_len; i++) {
|
||||
struct qmi_cooling_device *qmi_cdev = NULL;
|
||||
|
||||
list_for_each_entry(qmi_cdev, &tmd->tmd_cdev_list,
|
||||
qmi_node) {
|
||||
struct tmd_mitigation_dev_list_type_v01 *device =
|
||||
&tmd_resp->mitigation_device_list_ext01[i];
|
||||
|
||||
if ((strncasecmp(qmi_cdev->qmi_name,
|
||||
device->mitigation_dev_id.mitigation_dev_id,
|
||||
QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01)))
|
||||
continue;
|
||||
|
||||
qmi_cdev->connection_active = true;
|
||||
qmi_cdev->max_level = device->max_mitigation_level;
|
||||
/*
|
||||
* It is better to set current state
|
||||
* initially or during restart
|
||||
*/
|
||||
qmi_tmd_send_state_request(qmi_cdev,
|
||||
qmi_cdev->mtgn_state);
|
||||
if (!qmi_cdev->cdev)
|
||||
ret = qmi_register_cooling_device(qmi_cdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(tmd_resp);
|
||||
return ret;
|
||||
|
||||
reg_exit:
|
||||
mutex_unlock(&tmd->mutex);
|
||||
kfree(tmd_resp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qmi_tmd_svc_arrive(struct work_struct *work)
|
||||
{
|
||||
struct qmi_tmd_instance *tmd = container_of(work,
|
||||
struct qmi_tmd_instance,
|
||||
svc_arrive_work);
|
||||
|
||||
verify_devices_and_register(tmd);
|
||||
}
|
||||
|
||||
static void thermal_qmi_net_reset(struct qmi_handle *qmi)
|
||||
{
|
||||
struct qmi_tmd_instance *tmd = container_of(qmi,
|
||||
struct qmi_tmd_instance,
|
||||
handle);
|
||||
struct qmi_cooling_device *qmi_cdev = NULL;
|
||||
|
||||
list_for_each_entry(qmi_cdev, &tmd->tmd_cdev_list,
|
||||
qmi_node) {
|
||||
if (qmi_cdev->connection_active)
|
||||
qmi_tmd_send_state_request(qmi_cdev,
|
||||
qmi_cdev->mtgn_state);
|
||||
}
|
||||
}
|
||||
|
||||
static void thermal_qmi_del_server(struct qmi_handle *qmi,
|
||||
struct qmi_service *service)
|
||||
{
|
||||
struct qmi_tmd_instance *tmd = container_of(qmi,
|
||||
struct qmi_tmd_instance,
|
||||
handle);
|
||||
struct qmi_cooling_device *qmi_cdev = NULL;
|
||||
|
||||
list_for_each_entry(qmi_cdev, &tmd->tmd_cdev_list, qmi_node)
|
||||
qmi_cdev->connection_active = false;
|
||||
}
|
||||
|
||||
static int thermal_qmi_new_server(struct qmi_handle *qmi,
|
||||
struct qmi_service *service)
|
||||
{
|
||||
struct qmi_tmd_instance *tmd = container_of(qmi,
|
||||
struct qmi_tmd_instance,
|
||||
handle);
|
||||
struct sockaddr_qrtr sq = {AF_QIPCRTR, service->node, service->port};
|
||||
|
||||
mutex_lock(&tmd->mutex);
|
||||
kernel_connect(qmi->sock, (struct sockaddr *)&sq, sizeof(sq), 0);
|
||||
mutex_unlock(&tmd->mutex);
|
||||
queue_work(system_highpri_wq, &tmd->svc_arrive_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct qmi_ops thermal_qmi_event_ops = {
|
||||
.new_server = thermal_qmi_new_server,
|
||||
.del_server = thermal_qmi_del_server,
|
||||
.net_reset = thermal_qmi_net_reset,
|
||||
};
|
||||
|
||||
static void qmi_tmd_cleanup(void)
|
||||
{
|
||||
int idx = 0;
|
||||
struct qmi_tmd_instance *tmd = tmd_instances;
|
||||
struct qmi_cooling_device *qmi_cdev, *c_next;
|
||||
|
||||
for (; idx < tmd_inst_cnt; idx++) {
|
||||
mutex_lock(&tmd[idx].mutex);
|
||||
list_for_each_entry_safe(qmi_cdev, c_next,
|
||||
&tmd[idx].tmd_cdev_list, qmi_node) {
|
||||
qmi_cdev->connection_active = false;
|
||||
if (qmi_cdev->cdev)
|
||||
thermal_cooling_device_unregister(
|
||||
qmi_cdev->cdev);
|
||||
|
||||
list_del(&qmi_cdev->qmi_node);
|
||||
}
|
||||
qmi_handle_release(&tmd[idx].handle);
|
||||
|
||||
mutex_unlock(&tmd[idx].mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static int of_get_qmi_tmd_platform_data(struct device *dev)
|
||||
{
|
||||
int ret = 0, idx = 0, i = 0, subsys_cnt = 0;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *subsys_np = NULL, *cdev_np = NULL;
|
||||
struct qmi_tmd_instance *tmd;
|
||||
struct qmi_cooling_device *qmi_cdev;
|
||||
|
||||
subsys_cnt = of_get_available_child_count(np);
|
||||
if (!subsys_cnt) {
|
||||
dev_err(dev, "No child node to process\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
tmd = devm_kcalloc(dev, subsys_cnt, sizeof(*tmd), GFP_KERNEL);
|
||||
if (!tmd)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_available_child_of_node(np, subsys_np) {
|
||||
if (idx >= subsys_cnt) {
|
||||
of_node_put(subsys_np);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(subsys_np, "qcom,instance-id",
|
||||
&tmd[idx].inst_id);
|
||||
if (ret) {
|
||||
dev_err(dev, "error reading qcom,insance-id. ret:%d\n",
|
||||
ret);
|
||||
goto data_subsys_error;
|
||||
}
|
||||
|
||||
tmd[idx].dev = dev;
|
||||
mutex_init(&tmd[idx].mutex);
|
||||
INIT_LIST_HEAD(&tmd[idx].tmd_cdev_list);
|
||||
INIT_WORK(&tmd[idx].svc_arrive_work, qmi_tmd_svc_arrive);
|
||||
|
||||
for_each_available_child_of_node(subsys_np, cdev_np) {
|
||||
const char *qmi_name;
|
||||
|
||||
qmi_cdev = devm_kzalloc(dev, sizeof(*qmi_cdev),
|
||||
GFP_KERNEL);
|
||||
if (!qmi_cdev) {
|
||||
ret = -ENOMEM;
|
||||
goto data_error;
|
||||
}
|
||||
|
||||
strscpy(qmi_cdev->cdev_name, cdev_np->name,
|
||||
QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01);
|
||||
|
||||
if (!of_property_read_string(cdev_np,
|
||||
"qcom,qmi-dev-name",
|
||||
&qmi_name)) {
|
||||
strscpy(qmi_cdev->qmi_name, qmi_name,
|
||||
QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01);
|
||||
} else {
|
||||
dev_err(dev, "Fail to parse dev name for %s\n",
|
||||
cdev_np->name);
|
||||
break;
|
||||
}
|
||||
/* Check for supported qmi dev*/
|
||||
for (i = 0; i < ARRAY_SIZE(device_clients); i++) {
|
||||
if (strcmp(device_clients[i],
|
||||
qmi_cdev->qmi_name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(device_clients)) {
|
||||
dev_err(dev, "Not supported dev name for %s\n",
|
||||
cdev_np->name);
|
||||
break;
|
||||
}
|
||||
qmi_cdev->tmd = &tmd[idx];
|
||||
qmi_cdev->np = cdev_np;
|
||||
qmi_cdev->mtgn_state = 0;
|
||||
list_add(&qmi_cdev->qmi_node, &tmd[idx].tmd_cdev_list);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
of_node_put(np);
|
||||
tmd_instances = tmd;
|
||||
tmd_inst_cnt = subsys_cnt;
|
||||
|
||||
return 0;
|
||||
data_error:
|
||||
of_node_put(cdev_np);
|
||||
data_subsys_error:
|
||||
of_node_put(subsys_np);
|
||||
of_node_put(np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret = 0, idx = 0;
|
||||
|
||||
ret = of_get_qmi_tmd_platform_data(dev);
|
||||
if (ret)
|
||||
goto probe_err;
|
||||
|
||||
if (!tmd_instances || !tmd_inst_cnt) {
|
||||
dev_err(dev, "Empty tmd instances\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (; idx < tmd_inst_cnt; idx++) {
|
||||
struct qmi_tmd_instance *tmd = &tmd_instances[idx];
|
||||
|
||||
if (list_empty(&tmd->tmd_cdev_list))
|
||||
continue;
|
||||
|
||||
ret = qmi_handle_init(&tmd->handle,
|
||||
TMD_GET_MITIGATION_DEVICE_LIST_RESP_MSG_V01_MAX_MSG_LEN,
|
||||
&thermal_qmi_event_ops, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "QMI[0x%x] handle init failed. err:%d\n",
|
||||
tmd->inst_id, ret);
|
||||
tmd_inst_cnt = idx;
|
||||
goto probe_err;
|
||||
}
|
||||
ret = qmi_add_lookup(&tmd->handle, TMD_SERVICE_ID_V01,
|
||||
TMD_SERVICE_VERS_V01,
|
||||
tmd->inst_id);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "QMI register failed for 0x%x, ret:%d\n",
|
||||
tmd->inst_id, ret);
|
||||
goto probe_err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
probe_err:
|
||||
qmi_tmd_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_device_remove(struct platform_device *pdev)
|
||||
{
|
||||
qmi_tmd_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qmi_device_match[] = {
|
||||
{.compatible = "qcom,qmi-cooling-devices"},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver qmi_device_driver = {
|
||||
.probe = qmi_device_probe,
|
||||
.remove = qmi_device_remove,
|
||||
.driver = {
|
||||
.name = QMI_CDEV_DRIVER,
|
||||
.of_match_table = qmi_device_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init qmi_device_init(void)
|
||||
{
|
||||
return platform_driver_register(&qmi_device_driver);
|
||||
}
|
||||
module_init(qmi_device_init);
|
||||
|
||||
static void __exit qmi_device_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&qmi_device_driver);
|
||||
}
|
||||
module_exit(qmi_device_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("QTI QMI cooling device driver");
|
||||
167
drivers/thermal/qcom/qmi_sensors.h
Normal file
167
drivers/thermal/qcom/qmi_sensors.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QMI_SENSORS_H__
|
||||
#define __QMI_SENSORS_H__
|
||||
|
||||
#define QMI_CLIENT_NAME_LENGTH 40
|
||||
|
||||
enum qmi_ts_sensor {
|
||||
QMI_TS_PA,
|
||||
QMI_TS_PA_1,
|
||||
QMI_TS_PA_2,
|
||||
QMI_TS_QFE_PA_0,
|
||||
QMI_TS_QFE_WTR_0,
|
||||
QMI_TS_MODEM_MODEM,
|
||||
QMI_TS_MMW_0,
|
||||
QMI_TS_MMW_1,
|
||||
QMI_TS_MMW_2,
|
||||
QMI_TS_MMW_3,
|
||||
QMI_TS_MODEM_SKIN,
|
||||
QMI_TS_QFE_PA_MDM,
|
||||
QMI_TS_QFE_PA_WTR,
|
||||
QMI_TS_STREAMER_0,
|
||||
QMI_TS_MOD_MMW_0,
|
||||
QMI_TS_MOD_MMW_1,
|
||||
QMI_TS_MOD_MMW_2,
|
||||
QMI_TS_MOD_MMW_3,
|
||||
QMI_TS_RET_PA_0,
|
||||
QMI_TS_WTR_PA_0,
|
||||
QMI_TS_WTR_PA_1,
|
||||
QMI_TS_WTR_PA_2,
|
||||
QMI_TS_WTR_PA_3,
|
||||
QMI_SYS_THERM1,
|
||||
QMI_SYS_THERM2,
|
||||
QMI_TS_TSENS_1,
|
||||
QMI_TS_MMW_PA1,
|
||||
QMI_TS_MMW_PA2,
|
||||
QMI_TS_MMW_PA3,
|
||||
QMI_TS_SDR_MMW,
|
||||
QMI_TS_QTM_THERM,
|
||||
QMI_TS_BCL_WARN,
|
||||
QMI_TS_SDR0_PA0,
|
||||
QMI_TS_SDR0_PA1,
|
||||
QMI_TS_SDR0_PA2,
|
||||
QMI_TS_SDR0_PA3,
|
||||
QMI_TS_SDR0_PA4,
|
||||
QMI_TS_SDR0_PA5,
|
||||
QMI_TS_SDR0,
|
||||
QMI_TS_SDR1_PA0,
|
||||
QMI_TS_SDR1_PA1,
|
||||
QMI_TS_SDR1_PA2,
|
||||
QMI_TS_SDR1_PA3,
|
||||
QMI_TS_SDR1_PA4,
|
||||
QMI_TS_SDR1_PA5,
|
||||
QMI_TS_SDR1,
|
||||
QMI_TS_MMW0,
|
||||
QMI_TS_MMW1,
|
||||
QMI_TS_MMW2,
|
||||
QMI_TS_MMW3,
|
||||
QMI_TS_MMW_IFIC0,
|
||||
QMI_TS_SUB1_MODEM_CFG,
|
||||
QMI_TS_SUB1_LTE_CC,
|
||||
QMI_TS_SUB1_MCG_FR1_CC,
|
||||
QMI_TS_SUB1_MCG_FR2_CC,
|
||||
QMI_TS_SUB1_SCG_FR1_CC,
|
||||
QMI_TS_SUB1_SCG_FR2_CC,
|
||||
QMI_TS_SUB2_MODEM_CFG,
|
||||
QMI_TS_SUB2_LTE_CC,
|
||||
QMI_TS_SUB2_MCG_FR1_CC,
|
||||
QMI_TS_SUB2_MCG_FR2_CC,
|
||||
QMI_TS_SUB2_SCG_FR1_CC,
|
||||
QMI_TS_SUB2_SCG_FR2_CC,
|
||||
QMI_TS_NSP_ISENSE_TRIM,
|
||||
QMI_TS_EPM0,
|
||||
QMI_TS_EPM1,
|
||||
QMI_TS_EPM2,
|
||||
QMI_TS_EPM3,
|
||||
QMI_TS_EPM4,
|
||||
QMI_TS_EPM5,
|
||||
QMI_TS_EPM6,
|
||||
QMI_TS_EPM7,
|
||||
QMI_TS_SDR0_PA,
|
||||
QMI_TS_SDR1_PA,
|
||||
QMI_TS_MAX_NR
|
||||
};
|
||||
|
||||
static char sensor_clients[QMI_TS_MAX_NR][QMI_CLIENT_NAME_LENGTH] = {
|
||||
{"pa"},
|
||||
{"pa_1"},
|
||||
{"pa_2"},
|
||||
{"qfe_pa0"},
|
||||
{"qfe_wtr0"},
|
||||
{"modem_tsens"},
|
||||
{"qfe_mmw0"},
|
||||
{"qfe_mmw1"},
|
||||
{"qfe_mmw2"},
|
||||
{"qfe_mmw3"},
|
||||
{"xo_therm"},
|
||||
{"qfe_pa_mdm"},
|
||||
{"qfe_pa_wtr"},
|
||||
{"qfe_mmw_streamer0"},
|
||||
{"qfe_mmw0_mod"},
|
||||
{"qfe_mmw1_mod"},
|
||||
{"qfe_mmw2_mod"},
|
||||
{"qfe_mmw3_mod"},
|
||||
{"qfe_ret_pa0"},
|
||||
{"qfe_wtr_pa0"},
|
||||
{"qfe_wtr_pa1"},
|
||||
{"qfe_wtr_pa2"},
|
||||
{"qfe_wtr_pa3"},
|
||||
{"sys_therm1"},
|
||||
{"sys_therm2"},
|
||||
{"modem_tsens1"},
|
||||
{"mmw_pa1"},
|
||||
{"mmw_pa2"},
|
||||
{"mmw_pa3"},
|
||||
{"sdr_mmw_therm"},
|
||||
{"qtm_therm"},
|
||||
{"modem_bcl_warn"},
|
||||
{"sdr0_pa0"},
|
||||
{"sdr0_pa1"},
|
||||
{"sdr0_pa2"},
|
||||
{"sdr0_pa3"},
|
||||
{"sdr0_pa4"},
|
||||
{"sdr0_pa5"},
|
||||
{"sdr0"},
|
||||
{"sdr1_pa0"},
|
||||
{"sdr1_pa1"},
|
||||
{"sdr1_pa2"},
|
||||
{"sdr1_pa3"},
|
||||
{"sdr1_pa4"},
|
||||
{"sdr1_pa5"},
|
||||
{"sdr1"},
|
||||
{"mmw0"},
|
||||
{"mmw1"},
|
||||
{"mmw2"},
|
||||
{"mmw3"},
|
||||
{"mmw_ific0"},
|
||||
{"sub1_modem_cfg"},
|
||||
{"sub1_lte_cc"},
|
||||
{"sub1_mcg_fr1_cc"},
|
||||
{"sub1_mcg_fr2_cc"},
|
||||
{"sub1_scg_fr1_cc"},
|
||||
{"sub1_scg_fr2_cc"},
|
||||
{"sub2_modem_cfg"},
|
||||
{"sub2_lte_cc"},
|
||||
{"sub2_mcg_fr1_cc"},
|
||||
{"sub2_mcg_fr2_cc"},
|
||||
{"sub2_scg_fr1_cc"},
|
||||
{"sub2_scg_fr2_cc"},
|
||||
{"isense_trim"},
|
||||
{"epm0"},
|
||||
{"epm1"},
|
||||
{"epm2"},
|
||||
{"epm3"},
|
||||
{"epm4"},
|
||||
{"epm5"},
|
||||
{"epm6"},
|
||||
{"epm7"},
|
||||
{"sdr0_pa"},
|
||||
{"sdr1_pa"},
|
||||
};
|
||||
|
||||
#endif /* __QMI_SENSORS_H__ */
|
||||
657
drivers/thermal/qcom/qmi_sensors_v2.c
Normal file
657
drivers/thermal/qcom/qmi_sensors_v2.c
Normal file
|
|
@ -0,0 +1,657 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/qcom/qmi.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_sensor_service_v02.h"
|
||||
#include "qmi_sensors.h"
|
||||
#include "thermal_zone_internal.h"
|
||||
|
||||
#define QMI_SENS_DRIVER "qmi-therm-sensors-v2"
|
||||
#define QMI_TS_RESP_TOUT msecs_to_jiffies(100)
|
||||
|
||||
struct qmi_sensor {
|
||||
struct device *dev;
|
||||
char qmi_name[QMI_CLIENT_NAME_LENGTH];
|
||||
bool connection_active;
|
||||
struct list_head ts_node;
|
||||
struct thermal_zone_device *tz_dev;
|
||||
int32_t last_reading;
|
||||
int32_t high_thresh;
|
||||
int32_t low_thresh;
|
||||
struct qmi_ts_instance *ts;
|
||||
enum qmi_ts_sensor sens_type;
|
||||
struct work_struct therm_notify_work;
|
||||
};
|
||||
|
||||
struct qmi_ts_instance {
|
||||
struct device *dev;
|
||||
struct qmi_handle handle;
|
||||
struct mutex mutex;
|
||||
uint32_t inst_id;
|
||||
struct list_head ts_sensor_list;
|
||||
struct work_struct svc_arrive_work;
|
||||
};
|
||||
|
||||
static struct qmi_ts_instance *ts_instances;
|
||||
static int ts_inst_cnt;
|
||||
static atomic_t in_suspend;
|
||||
|
||||
static int qmi_sensor_pm_notify(struct notifier_block *nb,
|
||||
unsigned long mode, void *_unused)
|
||||
{
|
||||
switch (mode) {
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
case PM_RESTORE_PREPARE:
|
||||
case PM_SUSPEND_PREPARE:
|
||||
atomic_set(&in_suspend, 1);
|
||||
break;
|
||||
case PM_POST_HIBERNATION:
|
||||
case PM_POST_RESTORE:
|
||||
case PM_POST_SUSPEND:
|
||||
atomic_set(&in_suspend, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block qmi_sensor_pm_nb = {
|
||||
.notifier_call = qmi_sensor_pm_notify,
|
||||
};
|
||||
|
||||
static void qmi_ts_thresh_notify(struct work_struct *work)
|
||||
{
|
||||
struct qmi_sensor *qmi_sens = container_of(work,
|
||||
struct qmi_sensor,
|
||||
therm_notify_work);
|
||||
|
||||
thermal_zone_device_update(qmi_sens->tz_dev, THERMAL_TRIP_VIOLATED);
|
||||
};
|
||||
|
||||
static void qmi_ts_update_temperature(struct qmi_ts_instance *ts,
|
||||
const struct ts_temp_report_ind_msg_v02 *ind_msg,
|
||||
uint8_t notify)
|
||||
{
|
||||
struct qmi_sensor *qmi_sens;
|
||||
|
||||
list_for_each_entry(qmi_sens, &ts->ts_sensor_list,
|
||||
ts_node) {
|
||||
if ((strncasecmp(qmi_sens->qmi_name,
|
||||
ind_msg->sensor_id.sensor_id,
|
||||
QMI_TS_SENSOR_ID_LENGTH_MAX_V02)))
|
||||
continue;
|
||||
|
||||
qmi_sens->last_reading = ind_msg->temp;
|
||||
|
||||
pr_debug("sensor:%s temperature:%d\n",
|
||||
qmi_sens->qmi_name, qmi_sens->last_reading);
|
||||
if (!qmi_sens->tz_dev)
|
||||
return;
|
||||
if (notify &&
|
||||
(qmi_sens->last_reading >= qmi_sens->high_thresh ||
|
||||
qmi_sens->last_reading <= qmi_sens->low_thresh)) {
|
||||
pr_debug("Sensor:%s Notify. temp:%d\n",
|
||||
ind_msg->sensor_id.sensor_id,
|
||||
qmi_sens->last_reading);
|
||||
queue_work(system_highpri_wq,
|
||||
&qmi_sens->therm_notify_work);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void qmi_ts_ind_cb_v02(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
|
||||
struct qmi_txn *txn, const void *decoded)
|
||||
{
|
||||
const struct ts_temp_report_ind_msg_v02 *ind_msg = decoded;
|
||||
uint8_t notify = 0;
|
||||
struct qmi_ts_instance *ts = container_of(qmi, struct qmi_ts_instance,
|
||||
handle);
|
||||
|
||||
if (!txn) {
|
||||
pr_err("Invalid transaction\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ind_msg->report_type != QMI_TS_TEMP_REPORT_CURRENT_TEMP_V02) ||
|
||||
ind_msg->seq_num_valid)
|
||||
notify = 1;
|
||||
|
||||
if (ind_msg->temp_valid)
|
||||
qmi_ts_update_temperature(ts, ind_msg, notify);
|
||||
else
|
||||
pr_err("Error invalid temperature field.\n");
|
||||
}
|
||||
|
||||
static int qmi_ts_send_request(struct qmi_ts_instance *ts, int msg_id,
|
||||
size_t len, struct qmi_elem_info *req_ei,
|
||||
struct qmi_elem_info *resp_ei,
|
||||
void *req, void *resp,
|
||||
struct qmi_response_type_v01 *resp_data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct qmi_txn txn;
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
ret = qmi_txn_init(&ts->handle, &txn, resp_ei, resp);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi txn init failed for msg id:0x%x ret:%d\n",
|
||||
msg_id, ret);
|
||||
goto qmi_ts_send_exit;
|
||||
}
|
||||
|
||||
ret = qmi_send_request(&ts->handle, NULL, &txn, msg_id, len,
|
||||
req_ei, req);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi txn send failed for msg id:0x%x ret:%d\n",
|
||||
msg_id, ret);
|
||||
qmi_txn_cancel(&txn);
|
||||
goto qmi_ts_send_exit;
|
||||
}
|
||||
|
||||
ret = qmi_txn_wait(&txn, QMI_TS_RESP_TOUT);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi txn wait failed for msg id:0x%x ret:%d\n",
|
||||
msg_id, ret);
|
||||
goto qmi_ts_send_exit;
|
||||
}
|
||||
if (resp_data->result != QMI_RESULT_SUCCESS_V01) {
|
||||
ret = resp_data->result;
|
||||
pr_err("qmi NOT success for msg id:0x%x ret:%d\n",
|
||||
msg_id, ret);
|
||||
goto qmi_ts_send_exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
qmi_ts_send_exit:
|
||||
mutex_unlock(&ts->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_ts_request(struct qmi_sensor *qmi_sens,
|
||||
bool send_current_temp_report)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ts_register_notification_temp_resp_msg_v02 resp;
|
||||
struct ts_register_notification_temp_req_msg_v02 req;
|
||||
struct qmi_ts_instance *ts = qmi_sens->ts;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
|
||||
strscpy(req.sensor_id.sensor_id, qmi_sens->qmi_name,
|
||||
QMI_TS_SENSOR_ID_LENGTH_MAX_V02);
|
||||
req.seq_num = 0;
|
||||
if (send_current_temp_report) {
|
||||
req.send_current_temp_report = 1;
|
||||
req.seq_num_valid = true;
|
||||
} else {
|
||||
req.seq_num_valid = false;
|
||||
req.temp_threshold_high_valid =
|
||||
qmi_sens->high_thresh != INT_MAX;
|
||||
req.temp_threshold_high = qmi_sens->high_thresh;
|
||||
req.temp_threshold_low_valid =
|
||||
qmi_sens->low_thresh != (-INT_MAX);
|
||||
req.temp_threshold_low = qmi_sens->low_thresh;
|
||||
pr_debug("sensor:%s high_valid:%d high:%d low_valid:%d low:%d\n",
|
||||
req.sensor_id.sensor_id, req.temp_threshold_high_valid,
|
||||
req.temp_threshold_high, req.temp_threshold_low_valid,
|
||||
req.temp_threshold_low);
|
||||
}
|
||||
|
||||
ret = qmi_ts_send_request(ts, QMI_TS_REGISTER_NOTIFICATION_TEMP_REQ_V02,
|
||||
TS_REGISTER_NOTIFICATION_TEMP_REQ_MSG_V02_MAX_MSG_LEN,
|
||||
ts_register_notification_temp_req_msg_v02_ei,
|
||||
ts_register_notification_temp_resp_msg_v02_ei,
|
||||
&req, &resp, &resp.resp);
|
||||
if (ret < 0)
|
||||
pr_err("qmi ts txn failed for %s ret:%d\n",
|
||||
qmi_sens->qmi_name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_sensor_read(void *data, int *temp)
|
||||
{
|
||||
struct qmi_sensor *qmi_sens = (struct qmi_sensor *)data;
|
||||
|
||||
if (qmi_sens->connection_active && !atomic_read(&in_suspend))
|
||||
qmi_ts_request(qmi_sens, true);
|
||||
*temp = qmi_sens->last_reading;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmi_sensor_set_trips(void *data, int low, int high)
|
||||
{
|
||||
struct qmi_sensor *qmi_sens = (struct qmi_sensor *)data;
|
||||
int ret = 0;
|
||||
|
||||
if (qmi_sens->high_thresh == high &&
|
||||
qmi_sens->low_thresh == low)
|
||||
return ret;
|
||||
qmi_sens->high_thresh = high;
|
||||
qmi_sens->low_thresh = low;
|
||||
if (!qmi_sens->connection_active)
|
||||
return ret;
|
||||
ret = qmi_ts_request(qmi_sens, false);
|
||||
if (ret < 0)
|
||||
pr_err("Sensor:%s set high trip:%d low trip:%d error%d\n",
|
||||
qmi_sens->qmi_name,
|
||||
qmi_sens->high_thresh,
|
||||
qmi_sens->low_thresh,
|
||||
ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_zone_of_device_ops qmi_sensor_ops = {
|
||||
.get_temp = qmi_sensor_read,
|
||||
.set_trips = qmi_sensor_set_trips,
|
||||
};
|
||||
|
||||
static struct qmi_msg_handler handlers[] = {
|
||||
{
|
||||
.type = QMI_INDICATION,
|
||||
.msg_id = QMI_TS_TEMP_REPORT_IND_V02,
|
||||
.ei = ts_temp_report_ind_msg_v02_ei,
|
||||
.decoded_size = sizeof(struct ts_temp_report_ind_msg_v02),
|
||||
.fn = qmi_ts_ind_cb_v02
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int qmi_register_sensor_device(struct qmi_sensor *qmi_sens)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
qmi_sens->tz_dev = thermal_zone_of_sensor_register(
|
||||
qmi_sens->dev,
|
||||
qmi_sens->sens_type + qmi_sens->ts->inst_id,
|
||||
qmi_sens, &qmi_sensor_ops);
|
||||
if (IS_ERR(qmi_sens->tz_dev)) {
|
||||
ret = PTR_ERR(qmi_sens->tz_dev);
|
||||
if (ret != -ENODEV)
|
||||
pr_err("sensor register failed for %s, ret:%d\n",
|
||||
qmi_sens->qmi_name, ret);
|
||||
qmi_sens->tz_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
qti_update_tz_ops(qmi_sens->tz_dev, true);
|
||||
|
||||
pr_debug("Sensor register success for %s\n", qmi_sens->qmi_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_sensor_and_register(struct qmi_ts_instance *ts)
|
||||
{
|
||||
struct ts_get_sensor_list_req_msg_v02 list_req;
|
||||
struct ts_get_sensor_list_resp_msg_v02 *list_resp;
|
||||
struct ts_get_total_num_of_sensors_req_msg_v02 tot_req;
|
||||
struct ts_get_total_num_of_sensors_resp_msg_v02 tot_resp;
|
||||
int ret = 0, i, j, sensor_list_itr;
|
||||
|
||||
memset(&tot_resp, 0, sizeof(tot_resp));
|
||||
memset(&tot_req, 0, sizeof(tot_req));
|
||||
/* size of list_resp is very high, use heap memory rather than stack */
|
||||
list_resp = kzalloc(sizeof(*list_resp), GFP_KERNEL);
|
||||
if (!list_resp)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = qmi_ts_send_request(ts, QMI_TS_GET_TOTAL_NUM_OF_SENSORS_REQ_V02,
|
||||
TS_GET_TOTAL_NUM_OF_SENSORS_REQ_MSG_V02_MAX_MSG_LEN,
|
||||
ts_get_total_num_of_sensors_req_msg_v02_ei,
|
||||
ts_get_total_num_of_sensors_resp_msg_v02_ei,
|
||||
&tot_req, &tot_resp, &tot_resp.resp);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi ts txn failed, inst_id:%d ret:%d\n",
|
||||
ts->inst_id, ret);
|
||||
goto reg_exit;
|
||||
}
|
||||
|
||||
sensor_list_itr = (tot_resp.total_num_sensors /
|
||||
QMI_TS_SENSOR_LIST_MAX_V02 +
|
||||
(tot_resp.total_num_sensors %
|
||||
QMI_TS_SENSOR_LIST_MAX_V02 ? 1 : 0));
|
||||
|
||||
pr_debug("Total QMI sensors cnt:%d list iteration cnt:%d inst_id:%d\n",
|
||||
tot_resp.total_num_sensors, sensor_list_itr, ts->inst_id);
|
||||
|
||||
for (i = 0; i < sensor_list_itr; i++) {
|
||||
memset(&list_req, 0, sizeof(list_req));
|
||||
memset(list_resp, 0, sizeof(*list_resp));
|
||||
|
||||
list_req.list_index = i;
|
||||
|
||||
ret = qmi_ts_send_request(ts, QMI_TS_GET_SENSOR_LIST_REQ_V02,
|
||||
TS_GET_SENSOR_LIST_REQ_MSG_V02_MAX_MSG_LEN,
|
||||
ts_get_sensor_list_req_msg_v02_ei,
|
||||
ts_get_sensor_list_resp_msg_v02_ei,
|
||||
&list_req, list_resp, &list_resp->resp);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi ts txn failed, inst_id:%d itr:%d ret:%d\n",
|
||||
ts->inst_id, i, ret);
|
||||
goto reg_exit;
|
||||
}
|
||||
|
||||
for (j = 0; j < list_resp->sensor_list_len; j++) {
|
||||
struct qmi_sensor *qmi_sens = NULL;
|
||||
|
||||
pr_debug("QMI TS sensor[%d][%d]: %s\n", i, j,
|
||||
list_resp->sensor_list[j].sensor_id);
|
||||
|
||||
list_for_each_entry(qmi_sens, &ts->ts_sensor_list,
|
||||
ts_node) {
|
||||
if ((strncasecmp(qmi_sens->qmi_name,
|
||||
list_resp->sensor_list[j].sensor_id,
|
||||
QMI_TS_SENSOR_ID_LENGTH_MAX_V02)))
|
||||
continue;
|
||||
|
||||
qmi_sens->connection_active = true;
|
||||
/*
|
||||
* Send a temperature request notification.
|
||||
*/
|
||||
qmi_ts_request(qmi_sens, true);
|
||||
if (!qmi_sens->tz_dev) {
|
||||
ret = qmi_register_sensor_device(qmi_sens);
|
||||
} else {
|
||||
ret = qmi_ts_request(qmi_sens, false);
|
||||
if (ret)
|
||||
pr_err(
|
||||
"sensor:%s trips:%d %d err%d\n",
|
||||
qmi_sens->tz_dev->type,
|
||||
qmi_sens->high_thresh,
|
||||
qmi_sens->low_thresh,
|
||||
ret);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reg_exit:
|
||||
kfree(list_resp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qmi_ts_svc_arrive(struct work_struct *work)
|
||||
{
|
||||
struct qmi_ts_instance *ts = container_of(work,
|
||||
struct qmi_ts_instance,
|
||||
svc_arrive_work);
|
||||
|
||||
verify_sensor_and_register(ts);
|
||||
}
|
||||
|
||||
static void thermal_qmi_net_reset(struct qmi_handle *qmi)
|
||||
{
|
||||
struct qmi_ts_instance *ts = container_of(qmi,
|
||||
struct qmi_ts_instance,
|
||||
handle);
|
||||
struct qmi_sensor *qmi_sens = NULL;
|
||||
int ret;
|
||||
|
||||
pr_debug("reset QMI server\n");
|
||||
list_for_each_entry(qmi_sens, &ts->ts_sensor_list,
|
||||
ts_node) {
|
||||
if (!qmi_sens->connection_active)
|
||||
continue;
|
||||
qmi_ts_request(qmi_sens, true);
|
||||
ret = qmi_ts_request(qmi_sens, false);
|
||||
if (ret)
|
||||
pr_err("Sensor:%s set high trip:%d low trip:%d err%d\n",
|
||||
qmi_sens->tz_dev->type,
|
||||
qmi_sens->high_thresh,
|
||||
qmi_sens->low_thresh,
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void thermal_qmi_del_server(struct qmi_handle *qmi,
|
||||
struct qmi_service *service)
|
||||
{
|
||||
struct qmi_ts_instance *ts = container_of(qmi,
|
||||
struct qmi_ts_instance,
|
||||
handle);
|
||||
struct qmi_sensor *qmi_sens = NULL;
|
||||
|
||||
pr_debug("QMI server deleted\n");
|
||||
list_for_each_entry(qmi_sens, &ts->ts_sensor_list, ts_node)
|
||||
qmi_sens->connection_active = false;
|
||||
}
|
||||
|
||||
static int thermal_qmi_new_server(struct qmi_handle *qmi,
|
||||
struct qmi_service *service)
|
||||
{
|
||||
struct qmi_ts_instance *ts = container_of(qmi,
|
||||
struct qmi_ts_instance,
|
||||
handle);
|
||||
struct sockaddr_qrtr sq = {AF_QIPCRTR, service->node, service->port};
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
pr_debug("new QMI server is up, connecting..\n");
|
||||
kernel_connect(qmi->sock, (struct sockaddr *)&sq, sizeof(sq), 0);
|
||||
mutex_unlock(&ts->mutex);
|
||||
queue_work(system_highpri_wq, &ts->svc_arrive_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct qmi_ops thermal_qmi_event_ops = {
|
||||
.new_server = thermal_qmi_new_server,
|
||||
.del_server = thermal_qmi_del_server,
|
||||
.net_reset = thermal_qmi_net_reset,
|
||||
};
|
||||
|
||||
static void qmi_ts_cleanup(void)
|
||||
{
|
||||
struct qmi_ts_instance *ts;
|
||||
struct qmi_sensor *qmi_sens, *c_next;
|
||||
int idx = 0;
|
||||
|
||||
for (; idx < ts_inst_cnt; idx++) {
|
||||
ts = &ts_instances[idx];
|
||||
mutex_lock(&ts->mutex);
|
||||
list_for_each_entry_safe(qmi_sens, c_next,
|
||||
&ts->ts_sensor_list, ts_node) {
|
||||
qmi_sens->connection_active = false;
|
||||
if (qmi_sens->tz_dev) {
|
||||
thermal_zone_of_sensor_unregister(
|
||||
qmi_sens->dev, qmi_sens->tz_dev);
|
||||
qti_update_tz_ops(qmi_sens->tz_dev, false);
|
||||
qmi_sens->tz_dev = NULL;
|
||||
}
|
||||
|
||||
list_del(&qmi_sens->ts_node);
|
||||
}
|
||||
qmi_handle_release(&ts->handle);
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
}
|
||||
ts_inst_cnt = 0;
|
||||
}
|
||||
|
||||
static int of_get_qmi_ts_platform_data(struct device *dev)
|
||||
{
|
||||
int ret = 0, i = 0, idx = 0;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *subsys_np = NULL;
|
||||
struct qmi_ts_instance *ts;
|
||||
struct qmi_sensor *qmi_sens;
|
||||
int sens_name_max = 0, sens_idx = 0, subsys_cnt = 0;
|
||||
|
||||
subsys_cnt = of_get_available_child_count(np);
|
||||
if (!subsys_cnt) {
|
||||
dev_err(dev, "No child node to process\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ts = devm_kcalloc(dev, subsys_cnt, sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_available_child_of_node(np, subsys_np) {
|
||||
if (idx >= subsys_cnt)
|
||||
break;
|
||||
|
||||
ret = of_property_read_u32(subsys_np, "qcom,instance-id",
|
||||
&ts[idx].inst_id);
|
||||
if (ret) {
|
||||
dev_err(dev, "error reading qcom,insance-id. ret:%d\n",
|
||||
ret);
|
||||
goto data_fetch_err;
|
||||
}
|
||||
|
||||
ts[idx].dev = dev;
|
||||
mutex_init(&ts[idx].mutex);
|
||||
INIT_LIST_HEAD(&ts[idx].ts_sensor_list);
|
||||
INIT_WORK(&ts[idx].svc_arrive_work, qmi_ts_svc_arrive);
|
||||
|
||||
sens_name_max = of_property_count_strings(subsys_np,
|
||||
"qcom,qmi-sensor-names");
|
||||
if (sens_name_max <= 0) {
|
||||
dev_err(dev, "Invalid or no sensor. err:%d\n",
|
||||
sens_name_max);
|
||||
ret = -EINVAL;
|
||||
goto data_fetch_err;
|
||||
}
|
||||
|
||||
for (sens_idx = 0; sens_idx < sens_name_max; sens_idx++) {
|
||||
const char *qmi_name;
|
||||
|
||||
qmi_sens = devm_kzalloc(dev, sizeof(*qmi_sens),
|
||||
GFP_KERNEL);
|
||||
if (!qmi_sens) {
|
||||
ret = -ENOMEM;
|
||||
goto data_fetch_err;
|
||||
}
|
||||
|
||||
of_property_read_string_index(subsys_np,
|
||||
"qcom,qmi-sensor-names", sens_idx,
|
||||
&qmi_name);
|
||||
strscpy(qmi_sens->qmi_name, qmi_name,
|
||||
QMI_CLIENT_NAME_LENGTH);
|
||||
/* Check for supported qmi sensors */
|
||||
for (i = 0; i < QMI_TS_MAX_NR; i++) {
|
||||
if (!strcmp(sensor_clients[i],
|
||||
qmi_sens->qmi_name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= QMI_TS_MAX_NR) {
|
||||
dev_err(dev, "Unknown sensor:%s\n",
|
||||
qmi_sens->qmi_name);
|
||||
ret = -EINVAL;
|
||||
goto data_fetch_err;
|
||||
}
|
||||
dev_dbg(dev, "QMI sensor:%s available\n", qmi_name);
|
||||
qmi_sens->sens_type = i;
|
||||
qmi_sens->ts = &ts[idx];
|
||||
qmi_sens->dev = dev;
|
||||
qmi_sens->last_reading = 0;
|
||||
qmi_sens->high_thresh = INT_MAX;
|
||||
qmi_sens->low_thresh = -INT_MAX;
|
||||
INIT_WORK(&qmi_sens->therm_notify_work,
|
||||
qmi_ts_thresh_notify);
|
||||
list_add(&qmi_sens->ts_node, &ts[idx].ts_sensor_list);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
ts_instances = ts;
|
||||
ts_inst_cnt = subsys_cnt;
|
||||
|
||||
return 0;
|
||||
data_fetch_err:
|
||||
of_node_put(subsys_np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_sens_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret = 0, idx = 0;
|
||||
struct qmi_ts_instance *ts;
|
||||
|
||||
ret = of_get_qmi_ts_platform_data(dev);
|
||||
if (ret)
|
||||
goto probe_err;
|
||||
|
||||
if (!ts_instances || !ts_inst_cnt) {
|
||||
dev_err(dev, "Empty ts instances\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (; idx < ts_inst_cnt; idx++) {
|
||||
ts = &ts_instances[idx];
|
||||
if (list_empty(&ts->ts_sensor_list)) {
|
||||
ret = -ENODEV;
|
||||
goto probe_err;
|
||||
}
|
||||
ret = qmi_handle_init(&ts->handle,
|
||||
TS_GET_SENSOR_LIST_RESP_MSG_V02_MAX_MSG_LEN,
|
||||
&thermal_qmi_event_ops, handlers);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "QMI[0x%x] handle init failed. err:%d\n",
|
||||
ts->inst_id, ret);
|
||||
ts_inst_cnt = idx;
|
||||
ret = -EPROBE_DEFER;
|
||||
goto probe_err;
|
||||
}
|
||||
ret = qmi_add_lookup(&ts->handle, TS_SERVICE_ID_V02,
|
||||
TS_SERVICE_VERS_V02, ts->inst_id);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "QMI register failed for 0x%x, ret:%d\n",
|
||||
ts->inst_id, ret);
|
||||
ret = -EPROBE_DEFER;
|
||||
goto probe_err;
|
||||
}
|
||||
}
|
||||
atomic_set(&in_suspend, 0);
|
||||
register_pm_notifier(&qmi_sensor_pm_nb);
|
||||
return 0;
|
||||
|
||||
probe_err:
|
||||
qmi_ts_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_sens_device_remove(struct platform_device *pdev)
|
||||
{
|
||||
qmi_ts_cleanup();
|
||||
unregister_pm_notifier(&qmi_sensor_pm_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qmi_sens_device_match[] = {
|
||||
{.compatible = "qcom,qmi-sensors"},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver qmi_sens_device_driver = {
|
||||
.probe = qmi_sens_device_probe,
|
||||
.remove = qmi_sens_device_remove,
|
||||
.driver = {
|
||||
.name = QMI_SENS_DRIVER,
|
||||
.of_match_table = qmi_sens_device_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(qmi_sens_device_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
188
drivers/thermal/qcom/qti_userspace_cdev.c
Normal file
188
drivers/thermal/qcom/qti_userspace_cdev.c
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define USERSPACE_CDEV_DRIVER "userspace-cdev"
|
||||
|
||||
struct userspace_cdev {
|
||||
struct device_node *np;
|
||||
char cdev_name[THERMAL_NAME_LENGTH];
|
||||
struct thermal_cooling_device *cdev;
|
||||
unsigned int cur_level;
|
||||
unsigned int max_level;
|
||||
};
|
||||
|
||||
static struct userspace_cdev *cdev_instances;
|
||||
static int inst_cnt;
|
||||
|
||||
static int userspace_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct userspace_cdev *usr_cdev = cdev->devdata;
|
||||
|
||||
if (!usr_cdev)
|
||||
return -EINVAL;
|
||||
*state = usr_cdev->max_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int userspace_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct userspace_cdev *usr_cdev = cdev->devdata;
|
||||
|
||||
if (!usr_cdev)
|
||||
return -EINVAL;
|
||||
*state = usr_cdev->cur_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int userspace_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct userspace_cdev *usr_cdev = cdev->devdata;
|
||||
|
||||
if (!usr_cdev)
|
||||
return -EINVAL;
|
||||
|
||||
if (state > usr_cdev->max_level)
|
||||
return -EINVAL;
|
||||
|
||||
if (usr_cdev->cur_level == state)
|
||||
return 0;
|
||||
usr_cdev->cur_level = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_cooling_device_ops userspace_cdev_ops = {
|
||||
.get_max_state = userspace_get_max_state,
|
||||
.get_cur_state = userspace_get_cur_state,
|
||||
.set_cur_state = userspace_set_cur_state,
|
||||
};
|
||||
|
||||
static void userspace_cdev_cleanup(void)
|
||||
{
|
||||
int idx = 0;
|
||||
struct userspace_cdev *usr_cdev = cdev_instances;
|
||||
|
||||
for (; idx < inst_cnt; idx++) {
|
||||
if (usr_cdev[idx].cdev)
|
||||
thermal_cooling_device_unregister(
|
||||
usr_cdev[idx].cdev);
|
||||
usr_cdev[idx].cdev = NULL;
|
||||
}
|
||||
inst_cnt = 0;
|
||||
}
|
||||
|
||||
static int userspace_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret = 0, idx = 0, subsys_cnt = 0;
|
||||
struct device_node *np = dev->of_node, *subsys_np = NULL;
|
||||
|
||||
subsys_cnt = of_get_available_child_count(np);
|
||||
if (!subsys_cnt) {
|
||||
dev_err(dev, "No child node to process\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
cdev_instances = devm_kcalloc(dev, subsys_cnt, sizeof(*cdev_instances),
|
||||
GFP_KERNEL);
|
||||
if (!cdev_instances)
|
||||
return -ENOMEM;
|
||||
|
||||
inst_cnt = subsys_cnt;
|
||||
for_each_available_child_of_node(np, subsys_np) {
|
||||
if (idx >= subsys_cnt) {
|
||||
of_node_put(subsys_np);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(subsys_np, "qcom,max-level",
|
||||
&cdev_instances[idx].max_level);
|
||||
if (ret) {
|
||||
dev_err(dev, "error reading qcom,max-level. ret:%d\n",
|
||||
ret);
|
||||
goto probe_error;
|
||||
}
|
||||
|
||||
cdev_instances[idx].np = subsys_np;
|
||||
strscpy(cdev_instances[idx].cdev_name, subsys_np->name,
|
||||
THERMAL_NAME_LENGTH);
|
||||
|
||||
cdev_instances[idx].cdev = thermal_of_cooling_device_register(
|
||||
subsys_np,
|
||||
cdev_instances[idx].cdev_name,
|
||||
&cdev_instances[idx],
|
||||
&userspace_cdev_ops);
|
||||
if (IS_ERR(cdev_instances[idx].cdev)) {
|
||||
dev_err(dev, "Error registering cdev:%s err:%d\n",
|
||||
cdev_instances[idx].cdev_name,
|
||||
PTR_ERR(cdev_instances[idx].cdev));
|
||||
cdev_instances[idx].cdev = NULL;
|
||||
goto probe_error;
|
||||
}
|
||||
dev_info(dev, "cdev:%s lvl:%d registered\n",
|
||||
cdev_instances[idx].cdev_name,
|
||||
cdev_instances[idx].max_level);
|
||||
idx++;
|
||||
}
|
||||
of_node_put(np);
|
||||
|
||||
return 0;
|
||||
probe_error:
|
||||
of_node_put(subsys_np);
|
||||
of_node_put(np);
|
||||
inst_cnt = idx;
|
||||
userspace_cdev_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int userspace_device_remove(struct platform_device *pdev)
|
||||
{
|
||||
userspace_cdev_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id userspace_device_match[] = {
|
||||
{.compatible = "qcom,userspace-cooling-devices"},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver userspace_cdev_driver = {
|
||||
.probe = userspace_device_probe,
|
||||
.remove = userspace_device_remove,
|
||||
.driver = {
|
||||
.name = USERSPACE_CDEV_DRIVER,
|
||||
.of_match_table = userspace_device_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init userspace_cdev_init(void)
|
||||
{
|
||||
return platform_driver_register(&userspace_cdev_driver);
|
||||
}
|
||||
module_init(userspace_cdev_init);
|
||||
|
||||
static void __exit userspace_cdev_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&userspace_cdev_driver);
|
||||
}
|
||||
module_exit(userspace_cdev_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Userspace cooling device driver");
|
||||
386
drivers/thermal/qcom/thermal_mitigation_device_service_v01.c
Normal file
386
drivers/thermal/qcom/thermal_mitigation_device_service_v01.c
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/soc/qcom/qmi.h>
|
||||
|
||||
#include "thermal_mitigation_device_service_v01.h"
|
||||
|
||||
static struct qmi_elem_info tmd_mitigation_dev_id_type_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRING,
|
||||
.elem_len = QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01 + 1,
|
||||
.elem_size = sizeof(char),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0,
|
||||
.offset = offsetof(
|
||||
struct tmd_mitigation_dev_id_type_v01,
|
||||
mitigation_dev_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct qmi_elem_info tmd_mitigation_dev_list_type_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct tmd_mitigation_dev_id_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0,
|
||||
.offset = offsetof(
|
||||
struct tmd_mitigation_dev_list_type_v01,
|
||||
mitigation_dev_id),
|
||||
.ei_array = tmd_mitigation_dev_id_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_1_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0,
|
||||
.offset = offsetof(
|
||||
struct tmd_mitigation_dev_list_type_v01,
|
||||
max_mitigation_level),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info tmd_get_mitigation_device_list_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info tmd_get_mitigation_device_list_resp_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01,
|
||||
mitigation_device_list_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_DATA_LEN,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01,
|
||||
mitigation_device_list_len),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = QMI_TMD_MITIGATION_DEV_LIST_MAX_V01,
|
||||
.elem_size = sizeof(
|
||||
struct tmd_mitigation_dev_list_type_v01),
|
||||
.array_type = VAR_LEN_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01,
|
||||
mitigation_device_list),
|
||||
.ei_array = tmd_mitigation_dev_list_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01,
|
||||
mitigation_device_list_ext01_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_DATA_LEN,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01,
|
||||
mitigation_device_list_ext01_len),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = QMI_TMD_MITIGATION_DEV_LIST_EXT01_MAX_V01,
|
||||
.elem_size = sizeof(
|
||||
struct tmd_mitigation_dev_list_type_v01),
|
||||
.array_type = VAR_LEN_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01,
|
||||
mitigation_device_list_ext01),
|
||||
.ei_array = tmd_mitigation_dev_list_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info tmd_set_mitigation_level_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct tmd_mitigation_dev_id_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(
|
||||
struct tmd_set_mitigation_level_req_msg_v01,
|
||||
mitigation_dev_id),
|
||||
.ei_array = tmd_mitigation_dev_id_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_1_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(
|
||||
struct tmd_set_mitigation_level_req_msg_v01,
|
||||
mitigation_level),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info tmd_set_mitigation_level_resp_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(
|
||||
struct tmd_set_mitigation_level_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info tmd_get_mitigation_level_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct tmd_mitigation_dev_id_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_level_req_msg_v01,
|
||||
mitigation_device),
|
||||
.ei_array = tmd_mitigation_dev_id_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info tmd_get_mitigation_level_resp_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_level_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_level_resp_msg_v01,
|
||||
current_mitigation_level_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_1_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_level_resp_msg_v01,
|
||||
current_mitigation_level),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_level_resp_msg_v01,
|
||||
requested_mitigation_level_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_1_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(
|
||||
struct tmd_get_mitigation_level_resp_msg_v01,
|
||||
requested_mitigation_level),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info
|
||||
tmd_register_notification_mitigation_level_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct tmd_mitigation_dev_id_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(
|
||||
struct tmd_register_notification_mitigation_level_req_msg_v01,
|
||||
mitigation_device),
|
||||
.ei_array = tmd_mitigation_dev_id_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info
|
||||
tmd_register_notification_mitigation_level_resp_msg_v01_ei[]
|
||||
= {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(
|
||||
struct tmd_register_notification_mitigation_level_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info
|
||||
tmd_deregister_notification_mitigation_level_req_msg_v01_ei[]
|
||||
= {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct tmd_mitigation_dev_id_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct
|
||||
tmd_deregister_notification_mitigation_level_req_msg_v01,
|
||||
mitigation_device),
|
||||
.ei_array = tmd_mitigation_dev_id_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info
|
||||
tmd_deregister_notification_mitigation_level_resp_msg_v01_ei[]
|
||||
= {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
tmd_deregister_notification_mitigation_level_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info tmd_mitigation_level_report_ind_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct tmd_mitigation_dev_id_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(
|
||||
struct tmd_mitigation_level_report_ind_msg_v01,
|
||||
mitigation_device),
|
||||
.ei_array = tmd_mitigation_dev_id_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_1_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(
|
||||
struct tmd_mitigation_level_report_ind_msg_v01,
|
||||
current_mitigation_level),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
126
drivers/thermal/qcom/thermal_mitigation_device_service_v01.h
Normal file
126
drivers/thermal/qcom/thermal_mitigation_device_service_v01.h
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_MITIGATION_DEVICE_SERVICE_V01_H
|
||||
#define THERMAL_MITIGATION_DEVICE_SERVICE_V01_H
|
||||
|
||||
#define TMD_SERVICE_ID_V01 0x18
|
||||
#define TMD_SERVICE_VERS_V01 0x01
|
||||
|
||||
#define QMI_TMD_GET_MITIGATION_DEVICE_LIST_RESP_V01 0x0020
|
||||
#define QMI_TMD_GET_MITIGATION_LEVEL_REQ_V01 0x0022
|
||||
#define QMI_TMD_GET_SUPPORTED_MSGS_REQ_V01 0x001E
|
||||
#define QMI_TMD_SET_MITIGATION_LEVEL_REQ_V01 0x0021
|
||||
#define QMI_TMD_REGISTER_NOTIFICATION_MITIGATION_LEVEL_RESP_V01 0x0023
|
||||
#define QMI_TMD_GET_SUPPORTED_MSGS_RESP_V01 0x001E
|
||||
#define QMI_TMD_SET_MITIGATION_LEVEL_RESP_V01 0x0021
|
||||
#define QMI_TMD_DEREGISTER_NOTIFICATION_MITIGATION_LEVEL_RESP_V01 0x0024
|
||||
#define QMI_TMD_MITIGATION_LEVEL_REPORT_IND_V01 0x0025
|
||||
#define QMI_TMD_GET_MITIGATION_LEVEL_RESP_V01 0x0022
|
||||
#define QMI_TMD_GET_SUPPORTED_FIELDS_REQ_V01 0x001F
|
||||
#define QMI_TMD_GET_MITIGATION_DEVICE_LIST_REQ_V01 0x0020
|
||||
#define QMI_TMD_REGISTER_NOTIFICATION_MITIGATION_LEVEL_REQ_V01 0x0023
|
||||
#define QMI_TMD_DEREGISTER_NOTIFICATION_MITIGATION_LEVEL_REQ_V01 0x0024
|
||||
#define QMI_TMD_GET_SUPPORTED_FIELDS_RESP_V01 0x001F
|
||||
|
||||
#define QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01 32
|
||||
#define QMI_TMD_MITIGATION_DEV_LIST_MAX_V01 32
|
||||
#define QMI_TMD_MITIGATION_DEV_LIST_EXT01_MAX_V01 64
|
||||
|
||||
struct tmd_mitigation_dev_id_type_v01 {
|
||||
char mitigation_dev_id[QMI_TMD_MITIGATION_DEV_ID_LENGTH_MAX_V01 + 1];
|
||||
};
|
||||
|
||||
struct tmd_mitigation_dev_list_type_v01 {
|
||||
struct tmd_mitigation_dev_id_type_v01 mitigation_dev_id;
|
||||
uint8_t max_mitigation_level;
|
||||
};
|
||||
|
||||
struct tmd_get_mitigation_device_list_req_msg_v01 {
|
||||
char placeholder;
|
||||
};
|
||||
#define TMD_GET_MITIGATION_DEVICE_LIST_REQ_MSG_V01_MAX_MSG_LEN 0
|
||||
extern struct qmi_elem_info tmd_get_mitigation_device_list_req_msg_v01_ei[];
|
||||
|
||||
struct tmd_get_mitigation_device_list_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
uint8_t mitigation_device_list_valid;
|
||||
uint32_t mitigation_device_list_len;
|
||||
struct tmd_mitigation_dev_list_type_v01
|
||||
mitigation_device_list[QMI_TMD_MITIGATION_DEV_LIST_MAX_V01];
|
||||
uint8_t mitigation_device_list_ext01_valid;
|
||||
uint32_t mitigation_device_list_ext01_len;
|
||||
struct tmd_mitigation_dev_list_type_v01
|
||||
mitigation_device_list_ext01[QMI_TMD_MITIGATION_DEV_LIST_EXT01_MAX_V01];
|
||||
};
|
||||
#define TMD_GET_MITIGATION_DEVICE_LIST_RESP_MSG_V01_MAX_MSG_LEN 3279
|
||||
extern struct qmi_elem_info tmd_get_mitigation_device_list_resp_msg_v01_ei[];
|
||||
|
||||
struct tmd_set_mitigation_level_req_msg_v01 {
|
||||
struct tmd_mitigation_dev_id_type_v01 mitigation_dev_id;
|
||||
uint8_t mitigation_level;
|
||||
};
|
||||
#define TMD_SET_MITIGATION_LEVEL_REQ_MSG_V01_MAX_MSG_LEN 40
|
||||
extern struct qmi_elem_info tmd_set_mitigation_level_req_msg_v01_ei[];
|
||||
|
||||
struct tmd_set_mitigation_level_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define TMD_SET_MITIGATION_LEVEL_RESP_MSG_V01_MAX_MSG_LEN 7
|
||||
extern struct qmi_elem_info tmd_set_mitigation_level_resp_msg_v01_ei[];
|
||||
|
||||
struct tmd_get_mitigation_level_req_msg_v01 {
|
||||
struct tmd_mitigation_dev_id_type_v01 mitigation_device;
|
||||
};
|
||||
#define TMD_GET_MITIGATION_LEVEL_REQ_MSG_V01_MAX_MSG_LEN 36
|
||||
extern struct qmi_elem_info tmd_get_mitigation_level_req_msg_v01_ei[];
|
||||
|
||||
struct tmd_get_mitigation_level_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
uint8_t current_mitigation_level_valid;
|
||||
uint8_t current_mitigation_level;
|
||||
uint8_t requested_mitigation_level_valid;
|
||||
uint8_t requested_mitigation_level;
|
||||
};
|
||||
#define TMD_GET_MITIGATION_LEVEL_RESP_MSG_V01_MAX_MSG_LEN 15
|
||||
extern struct qmi_elem_info tmd_get_mitigation_level_resp_msg_v01_ei[];
|
||||
|
||||
struct tmd_register_notification_mitigation_level_req_msg_v01 {
|
||||
struct tmd_mitigation_dev_id_type_v01 mitigation_device;
|
||||
};
|
||||
#define TMD_REGISTER_NOTIFICATION_MITIGATION_LEVEL_REQ_MSG_V01_MAX_MSG_LEN 36
|
||||
extern struct qmi_elem_info
|
||||
tmd_register_notification_mitigation_level_req_msg_v01_ei[];
|
||||
|
||||
struct tmd_register_notification_mitigation_level_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define TMD_REGISTER_NOTIFICATION_MITIGATION_LEVEL_RESP_MSG_V01_MAX_MSG_LEN 7
|
||||
extern struct qmi_elem_info
|
||||
tmd_register_notification_mitigation_level_resp_msg_v01_ei[];
|
||||
|
||||
struct tmd_deregister_notification_mitigation_level_req_msg_v01 {
|
||||
struct tmd_mitigation_dev_id_type_v01 mitigation_device;
|
||||
};
|
||||
#define TMD_DEREGISTER_NOTIFICATION_MITIGATION_LEVEL_REQ_MSG_V01_MAX_MSG_LEN 36
|
||||
extern struct qmi_elem_info
|
||||
tmd_deregister_notification_mitigation_level_req_msg_v01_ei[];
|
||||
|
||||
struct tmd_deregister_notification_mitigation_level_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define TMD_DEREGISTER_NOTIFICATION_MITIGATION_LEVEL_RESP_MSG_V01_MAX_MSG_LEN 7
|
||||
extern struct qmi_elem_info
|
||||
tmd_deregister_notification_mitigation_level_resp_msg_v01_ei[];
|
||||
|
||||
struct tmd_mitigation_level_report_ind_msg_v01 {
|
||||
struct tmd_mitigation_dev_id_type_v01 mitigation_device;
|
||||
uint8_t current_mitigation_level;
|
||||
};
|
||||
#define TMD_MITIGATION_LEVEL_REPORT_IND_MSG_V01_MAX_MSG_LEN 40
|
||||
extern struct qmi_elem_info tmd_mitigation_level_report_ind_msg_v01_ei[];
|
||||
|
||||
#endif
|
||||
286
drivers/thermal/qcom/thermal_sensor_service_v02.c
Normal file
286
drivers/thermal/qcom/thermal_sensor_service_v02.c
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/soc/qcom/qmi.h>
|
||||
#include "thermal_sensor_service_v02.h"
|
||||
|
||||
static struct qmi_elem_info ts_sensor_type_v02_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRING,
|
||||
.elem_len = QMI_TS_SENSOR_ID_LENGTH_MAX_V02 + 1,
|
||||
.elem_size = sizeof(char),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0,
|
||||
.offset = offsetof(struct ts_sensor_type_v02,
|
||||
sensor_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_get_total_num_of_sensors_req_msg_v02_ei[] = {
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_get_total_num_of_sensors_resp_msg_v02_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct ts_get_total_num_of_sensors_resp_msg_v02,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u32),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x03,
|
||||
.offset = offsetof(struct ts_get_total_num_of_sensors_resp_msg_v02,
|
||||
total_num_sensors),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_get_sensor_list_req_msg_v02_ei[] = {
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u32),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct ts_get_sensor_list_req_msg_v02,
|
||||
list_index),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_get_sensor_list_resp_msg_v02_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct ts_get_sensor_list_resp_msg_v02,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct ts_get_sensor_list_resp_msg_v02,
|
||||
sensor_list_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_DATA_LEN,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct ts_get_sensor_list_resp_msg_v02,
|
||||
sensor_list_len),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = QMI_TS_SENSOR_LIST_MAX_V02,
|
||||
.elem_size = sizeof(struct ts_sensor_type_v02),
|
||||
.array_type = VAR_LEN_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct ts_get_sensor_list_resp_msg_v02,
|
||||
sensor_list),
|
||||
.ei_array = ts_sensor_type_v02_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_register_notification_temp_req_msg_v02_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct ts_sensor_type_v02),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct ts_register_notification_temp_req_msg_v02,
|
||||
sensor_id),
|
||||
.ei_array = ts_sensor_type_v02_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_1_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct ts_register_notification_temp_req_msg_v02,
|
||||
send_current_temp_report),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct ts_register_notification_temp_req_msg_v02,
|
||||
temp_threshold_high_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(int),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct ts_register_notification_temp_req_msg_v02,
|
||||
temp_threshold_high),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct ts_register_notification_temp_req_msg_v02,
|
||||
temp_threshold_low_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(int),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct ts_register_notification_temp_req_msg_v02,
|
||||
temp_threshold_low),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x12,
|
||||
.offset = offsetof(struct ts_register_notification_temp_req_msg_v02,
|
||||
seq_num_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u32),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x12,
|
||||
.offset = offsetof(struct ts_register_notification_temp_req_msg_v02,
|
||||
seq_num),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_register_notification_temp_resp_msg_v02_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct ts_register_notification_temp_resp_msg_v02,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_temp_report_ind_msg_v02_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct ts_sensor_type_v02),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v02,
|
||||
sensor_id),
|
||||
.ei_array = ts_sensor_type_v02_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_SIGNED_4_BYTE_ENUM,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(enum ts_temp_report_type_enum_v02),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v02,
|
||||
report_type),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v02,
|
||||
temp_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(int),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v02,
|
||||
temp),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u8),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v02,
|
||||
seq_num_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(u32),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v02,
|
||||
seq_num),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
98
drivers/thermal/qcom/thermal_sensor_service_v02.h
Normal file
98
drivers/thermal/qcom/thermal_sensor_service_v02.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_SENSOR_SERVICE_V02_H
|
||||
#define THERMAL_SENSOR_SERVICE_V02_H
|
||||
|
||||
#define TS_SERVICE_ID_V02 0x17
|
||||
#define TS_SERVICE_VERS_V02 0x02
|
||||
|
||||
#define QMI_TS_GET_SUPPORTED_MSGS_RESP_V02 0x001E
|
||||
#define QMI_TS_GET_SENSOR_LIST_RESP_V02 0x0031
|
||||
#define QMI_TS_GET_SUPPORTED_MSGS_REQ_V02 0x001E
|
||||
#define QMI_TS_GET_TOTAL_NUM_OF_SENSORS_RESP_V02 0x0030
|
||||
#define QMI_TS_REGISTER_NOTIFICATION_TEMP_REQ_V02 0x0032
|
||||
#define QMI_TS_REGISTER_NOTIFICATION_TEMP_RESP_V02 0x0032
|
||||
#define QMI_TS_GET_TOTAL_NUM_OF_SENSORS_REQ_V02 0x0030
|
||||
#define QMI_TS_GET_SUPPORTED_FIELDS_RESP_V02 0x001F
|
||||
#define QMI_TS_GET_SENSOR_LIST_REQ_V02 0x0031
|
||||
#define QMI_TS_TEMP_REPORT_IND_V02 0x0033
|
||||
#define QMI_TS_GET_SUPPORTED_FIELDS_REQ_V02 0x001F
|
||||
|
||||
#define QMI_TS_SENSOR_ID_LENGTH_MAX_V02 32
|
||||
#define QMI_TS_SENSOR_LIST_MAX_V02 96
|
||||
|
||||
struct ts_sensor_type_v02 {
|
||||
char sensor_id[QMI_TS_SENSOR_ID_LENGTH_MAX_V02 + 1];
|
||||
};
|
||||
|
||||
struct ts_get_total_num_of_sensors_req_msg_v02 {
|
||||
char placeholder;
|
||||
};
|
||||
#define TS_GET_TOTAL_NUM_OF_SENSORS_REQ_MSG_V02_MAX_MSG_LEN 0
|
||||
extern struct qmi_elem_info ts_get_total_num_of_sensors_req_msg_v02_ei[];
|
||||
|
||||
struct ts_get_total_num_of_sensors_resp_msg_v02 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
u32 total_num_sensors;
|
||||
};
|
||||
#define TS_GET_TOTAL_NUM_OF_SENSORS_RESP_MSG_V02_MAX_MSG_LEN 14
|
||||
extern struct qmi_elem_info ts_get_total_num_of_sensors_resp_msg_v02_ei[];
|
||||
|
||||
struct ts_get_sensor_list_req_msg_v02 {
|
||||
u32 list_index;
|
||||
};
|
||||
#define TS_GET_SENSOR_LIST_REQ_MSG_V02_MAX_MSG_LEN 7
|
||||
extern struct qmi_elem_info ts_get_sensor_list_req_msg_v02_ei[];
|
||||
|
||||
struct ts_get_sensor_list_resp_msg_v02 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
u8 sensor_list_valid;
|
||||
u32 sensor_list_len;
|
||||
struct ts_sensor_type_v02 sensor_list[QMI_TS_SENSOR_LIST_MAX_V02];
|
||||
};
|
||||
#define TS_GET_SENSOR_LIST_RESP_MSG_V02_MAX_MSG_LEN 3179
|
||||
extern struct qmi_elem_info ts_get_sensor_list_resp_msg_v02_ei[];
|
||||
|
||||
struct ts_register_notification_temp_req_msg_v02 {
|
||||
struct ts_sensor_type_v02 sensor_id;
|
||||
u8 send_current_temp_report;
|
||||
u8 temp_threshold_high_valid;
|
||||
int temp_threshold_high;
|
||||
u8 temp_threshold_low_valid;
|
||||
int temp_threshold_low;
|
||||
u8 seq_num_valid;
|
||||
u32 seq_num;
|
||||
};
|
||||
#define TS_REGISTER_NOTIFICATION_TEMP_REQ_MSG_V02_MAX_MSG_LEN 61
|
||||
extern struct qmi_elem_info ts_register_notification_temp_req_msg_v02_ei[];
|
||||
|
||||
struct ts_register_notification_temp_resp_msg_v02 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define TS_REGISTER_NOTIFICATION_TEMP_RESP_MSG_V02_MAX_MSG_LEN 7
|
||||
extern struct qmi_elem_info ts_register_notification_temp_resp_msg_v02_ei[];
|
||||
|
||||
enum ts_temp_report_type_enum_v02 {
|
||||
TS_TEMP_REPORT_TYPE_ENUM_MIN_VAL_V02 = INT_MIN,
|
||||
QMI_TS_TEMP_REPORT_CURRENT_TEMP_V02 = 0,
|
||||
QMI_TS_TEMP_REPORT_THRESHOLD_HIGH_V02 = 1,
|
||||
QMI_TS_TEMP_REPORT_THRESHOLD_LOW_V02 = 2,
|
||||
TS_TEMP_REPORT_TYPE_ENUM_MAX_VAL_V02 = INT_MAX,
|
||||
};
|
||||
|
||||
struct ts_temp_report_ind_msg_v02 {
|
||||
struct ts_sensor_type_v02 sensor_id;
|
||||
enum ts_temp_report_type_enum_v02 report_type;
|
||||
u8 temp_valid;
|
||||
int temp;
|
||||
u8 seq_num_valid;
|
||||
u32 seq_num;
|
||||
};
|
||||
#define TS_TEMP_REPORT_IND_MSG_V02_MAX_MSG_LEN 57
|
||||
extern struct qmi_elem_info ts_temp_report_ind_msg_v02_ei[];
|
||||
|
||||
#endif
|
||||
50
drivers/thermal/qcom/thermal_zone_internal.h
Normal file
50
drivers/thermal/qcom/thermal_zone_internal.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QTI_THERMAL_ZONE_INTERNAL_H
|
||||
#define __QTI_THERMAL_ZONE_INTERNAL_H
|
||||
|
||||
#include <linux/thermal.h>
|
||||
#include "../thermal_core.h"
|
||||
|
||||
/* Generic helpers for thermal zone -> change_mode ops */
|
||||
static inline int qti_tz_change_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct thermal_instance *instance;
|
||||
|
||||
tz->passive = 0;
|
||||
tz->temperature = THERMAL_TEMP_INVALID;
|
||||
tz->prev_low_trip = -INT_MAX;
|
||||
tz->prev_high_trip = INT_MAX;
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
instance->initialized = false;
|
||||
if (mode == THERMAL_DEVICE_DISABLED) {
|
||||
instance->target = THERMAL_NO_TARGET;
|
||||
instance->cdev->updated = false;
|
||||
thermal_cdev_update(instance->cdev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int qti_update_tz_ops(struct thermal_zone_device *tz, bool enable)
|
||||
{
|
||||
if (!tz || !tz->ops)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
if (enable) {
|
||||
if (!tz->ops->change_mode)
|
||||
tz->ops->change_mode = qti_tz_change_mode;
|
||||
} else {
|
||||
tz->ops->change_mode = NULL;
|
||||
}
|
||||
mutex_unlock(&tz->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // __QTI_THERMAL_ZONE_INTERNAL_H
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2019, 2020, Linaro Ltd.
|
||||
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
#include <linux/thermal.h>
|
||||
#include "../thermal_hwmon.h"
|
||||
#include "tsens.h"
|
||||
#include "thermal_zone_internal.h"
|
||||
|
||||
/**
|
||||
* struct tsens_irq_data - IRQ status and temperature violations
|
||||
|
|
@ -264,7 +266,7 @@ static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
|
|||
static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
|
||||
enum tsens_irq_type irq_type, bool enable)
|
||||
{
|
||||
dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
|
||||
TSENS_DBG_1(priv, "[%u] %s: %s -> %s\n", hw_id, __func__,
|
||||
irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
|
||||
enable ? "en" : "dis");
|
||||
if (tsens_version(priv) > VER_1_X)
|
||||
|
|
@ -348,7 +350,7 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
|
|||
d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
|
||||
d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
|
||||
|
||||
dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
|
||||
TSENS_DBG_1(priv, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
|
||||
hw_id, __func__,
|
||||
(d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
|
||||
d->low_viol, d->up_viol, d->crit_viol,
|
||||
|
|
@ -407,7 +409,7 @@ static irqreturn_t tsens_critical_irq_thread(int irq, void *data)
|
|||
if (ret)
|
||||
return ret;
|
||||
if (wdog_count)
|
||||
dev_dbg(priv->dev, "%s: watchdog count: %d\n",
|
||||
TSENS_DBG_1(priv, "%s: watchdog count: %d\n",
|
||||
__func__, wdog_count);
|
||||
|
||||
/* Fall through to handle critical interrupts if any */
|
||||
|
|
@ -465,7 +467,7 @@ static irqreturn_t tsens_irq_thread(int irq, void *data)
|
|||
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
bool trigger = false;
|
||||
const struct tsens_sensor *s = &priv->sensor[i];
|
||||
struct tsens_sensor *s = &priv->sensor[i];
|
||||
u32 hw_id = s->hw_id;
|
||||
|
||||
if (!s->tzd)
|
||||
|
|
@ -510,6 +512,7 @@ static irqreturn_t tsens_irq_thread(int irq, void *data)
|
|||
spin_unlock_irqrestore(&priv->ul_lock, flags);
|
||||
|
||||
if (trigger) {
|
||||
s->cached_temp = temp;
|
||||
dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
|
||||
hw_id, __func__, temp);
|
||||
thermal_zone_device_update(s->tzd,
|
||||
|
|
@ -527,7 +530,9 @@ static irqreturn_t tsens_irq_thread(int irq, void *data)
|
|||
*/
|
||||
break;
|
||||
}
|
||||
s->cached_temp = INT_MIN;
|
||||
}
|
||||
TSENS_DBG_1(priv, "%s: irq[%d] exit", __func__, irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -570,7 +575,7 @@ static int tsens_set_trips(void *_sensor, int low, int high)
|
|||
|
||||
spin_unlock_irqrestore(&priv->ul_lock, flags);
|
||||
|
||||
dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
|
||||
TSENS_DBG_1(priv, "[%u] %s: (%d:%d)->(%d:%d)\n",
|
||||
hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
|
||||
|
||||
return 0;
|
||||
|
|
@ -603,6 +608,11 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
|
|||
u32 valid;
|
||||
int ret;
|
||||
|
||||
if (s->cached_temp != INT_MIN) {
|
||||
*temp = s->cached_temp;
|
||||
goto dump_and_exit;
|
||||
}
|
||||
|
||||
/* VER_0 doesn't have VALID bit */
|
||||
if (tsens_version(priv) == VER_0)
|
||||
goto get_temp;
|
||||
|
|
@ -622,6 +632,9 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
|
|||
/* Valid bit is set, OK to read the temperature */
|
||||
*temp = tsens_hw_to_mC(s, temp_idx);
|
||||
|
||||
dump_and_exit:
|
||||
TSENS_DBG(priv, "Sensor_id: %d temp: %d", hw_id, *temp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -648,6 +661,8 @@ int get_temp_common(const struct tsens_sensor *s, int *temp)
|
|||
|
||||
*temp = code_to_degc(last_temp, s) * 1000;
|
||||
|
||||
TSENS_DBG(priv, "Sensor_id: %d temp: %d", hw_id, *temp);
|
||||
|
||||
return 0;
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
|
|
@ -705,6 +720,7 @@ static void tsens_debug_init(struct platform_device *pdev)
|
|||
{
|
||||
struct tsens_priv *priv = platform_get_drvdata(pdev);
|
||||
struct dentry *root, *file;
|
||||
char tsens_name[32];
|
||||
|
||||
root = debugfs_lookup("tsens", NULL);
|
||||
if (!root)
|
||||
|
|
@ -720,6 +736,20 @@ static void tsens_debug_init(struct platform_device *pdev)
|
|||
/* A directory for each instance of the TSENS IP */
|
||||
priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root);
|
||||
debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops);
|
||||
|
||||
/* Enable TSENS IPC logging context */
|
||||
snprintf(tsens_name, sizeof(tsens_name), "%s_0", dev_name(&pdev->dev));
|
||||
priv->ipc_log = ipc_log_context_create(IPC_LOGPAGES, tsens_name, 0);
|
||||
if (!priv->ipc_log)
|
||||
dev_err(&pdev->dev, "%s: unable to create IPC Logging for %s\n",
|
||||
__func__, tsens_name);
|
||||
|
||||
snprintf(tsens_name, sizeof(tsens_name), "%s_1", dev_name(&pdev->dev));
|
||||
priv->ipc_log1 = ipc_log_context_create(IPC_LOGPAGES, tsens_name, 0);
|
||||
if (!priv->ipc_log1)
|
||||
dev_err(&pdev->dev, "%s: unable to create IPC Logging for %s\n",
|
||||
__func__, tsens_name);
|
||||
|
||||
}
|
||||
#else
|
||||
static inline void tsens_debug_init(struct platform_device *pdev) {}
|
||||
|
|
@ -1056,6 +1086,7 @@ static int tsens_register(struct tsens_priv *priv)
|
|||
if (devm_thermal_add_hwmon_sysfs(tzd))
|
||||
dev_warn(priv->dev,
|
||||
"Failed to add hwmon sysfs attributes\n");
|
||||
qti_update_tz_ops(tzd, true);
|
||||
}
|
||||
|
||||
/* VER_0 require to set MIN and MAX THRESH
|
||||
|
|
@ -1129,6 +1160,7 @@ static int tsens_probe(struct platform_device *pdev)
|
|||
priv->sensor[i].hw_id = data->hw_ids[i];
|
||||
else
|
||||
priv->sensor[i].hw_id = i;
|
||||
priv->sensor[i].cached_temp = INT_MIN;
|
||||
}
|
||||
priv->feat = data->feat;
|
||||
priv->fields = data->fields;
|
||||
|
|
@ -1159,12 +1191,16 @@ static int tsens_probe(struct platform_device *pdev)
|
|||
static int tsens_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tsens_priv *priv = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
debugfs_remove_recursive(priv->debug_root);
|
||||
tsens_disable_irq(priv);
|
||||
if (priv->ops->disable)
|
||||
priv->ops->disable(priv);
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
if (priv->sensor[i].tzd)
|
||||
qti_update_tz_ops(priv->sensor[i].tzd, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QCOM_TSENS_H__
|
||||
|
|
@ -21,6 +22,7 @@
|
|||
#include <linux/thermal.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
|
||||
struct tsens_priv;
|
||||
|
||||
|
|
@ -54,6 +56,7 @@ struct tsens_sensor {
|
|||
unsigned int hw_id;
|
||||
int slope;
|
||||
u32 status;
|
||||
int cached_temp;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -145,6 +148,25 @@ struct tsens_ops {
|
|||
[_name##_##14] = REG_FIELD(_offset, 30, 30), \
|
||||
[_name##_##15] = REG_FIELD(_offset, 31, 31)
|
||||
|
||||
#define IPC_LOGPAGES 10
|
||||
#define TSENS_DBG(dev, msg, args...) do { \
|
||||
pr_debug("%s:" msg, __func__, args); \
|
||||
if ((dev) && (dev)->ipc_log) { \
|
||||
ipc_log_string((dev)->ipc_log, \
|
||||
"%s: " msg " [%s]\n", \
|
||||
__func__, args, current->comm); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TSENS_DBG_1(priv, msg, args...) do { \
|
||||
dev_dbg((priv)->dev, "%s:" msg, __func__, args); \
|
||||
if ((priv) && (priv)->ipc_log1) { \
|
||||
ipc_log_string((priv)->ipc_log1, \
|
||||
"%s: " msg " [%s]\n", \
|
||||
__func__, args, current->comm); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* reg_field IDs to use as an index into an array
|
||||
* If you change the order of the entries, check the devm_regmap_field_alloc()
|
||||
|
|
@ -551,6 +573,8 @@ struct tsens_context {
|
|||
* @ops: pointer to list of callbacks supported by this device
|
||||
* @debug_root: pointer to debugfs dentry for all tsens
|
||||
* @debug: pointer to debugfs dentry for tsens controller
|
||||
* @ipc_log: pointer for first ipc log context id
|
||||
* @ipc_log1: pointer for second ipc log context id
|
||||
* @sensor: list of sensors attached to this device
|
||||
*/
|
||||
struct tsens_priv {
|
||||
|
|
@ -571,6 +595,8 @@ struct tsens_priv {
|
|||
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug;
|
||||
void *ipc_log;
|
||||
void *ipc_log1;
|
||||
|
||||
struct tsens_sensor sensor[];
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user