mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 04:42:04 -04:00
Merge "drivers: thermal: Add a snapshot of bcl soc driver"
This commit is contained in:
commit
1966f732fa
7 changed files with 1948 additions and 0 deletions
|
|
@ -21,6 +21,35 @@ config QCOM_SPMI_TEMP_ALARM
|
|||
real time die temperature if an ADC is present or an estimate of the
|
||||
temperature based upon the over temperature stage value.
|
||||
|
||||
config QTI_QMI_SENSOR
|
||||
tristate "QTI QMI sensor driver"
|
||||
depends on QCOM_QMI_HELPERS && THERMAL_OF && QTI_THERMAL
|
||||
help
|
||||
This enables to list the QTI remote subsystem temperature sensors.
|
||||
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_BCL_PMIC5
|
||||
tristate "BCL driver for BCL peripherals in PMIC5"
|
||||
depends on SPMI && THERMAL_OF && QTI_THERMAL
|
||||
help
|
||||
This driver provides routines to configure and monitor the BCL
|
||||
PMIC peripheral. This driver registers the battery current and
|
||||
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_OF && POWER_SUPPLY && QTI_THERMAL
|
||||
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_CPU_ISOLATE_COOLING_DEVICE
|
||||
tristate "QTI CPU Isolate cooling devices"
|
||||
depends on THERMAL_OF && QTI_THERMAL
|
||||
|
|
|
|||
|
|
@ -4,4 +4,8 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
|
|||
qcom_tsens-y += tsens.o tsens-common.o tsens-v0_1.o \
|
||||
tsens-8960.o tsens-v2.o tsens-v1.o
|
||||
obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
|
||||
obj-$(CONFIG_QTI_QMI_SENSOR) += qti_qmi_sensor.o
|
||||
qti_qmi_sensor-y += thermal_sensor_service_v01.o qmi_sensors.o
|
||||
obj-$(CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE) += cpu_isolate.o
|
||||
obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o
|
||||
obj-$(CONFIG_QTI_BCL_SOC_DRIVER) += bcl_soc.o
|
||||
|
|
|
|||
667
drivers/thermal/qcom/bcl_pmic5.c
Normal file
667
drivers/thermal/qcom/bcl_pmic5.c
Normal file
|
|
@ -0,0 +1,667 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spmi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "../thermal_core.h"
|
||||
|
||||
#define BCL_DRIVER_NAME "bcl_pmic5"
|
||||
#define BCL_MONITOR_EN 0x46
|
||||
#define BCL_IRQ_STATUS 0x08
|
||||
|
||||
#define BCL_IBAT_HIGH 0x4B
|
||||
#define BCL_IBAT_TOO_HIGH 0x4C
|
||||
#define BCL_IBAT_READ 0x86
|
||||
#define BCL_IBAT_SCALING_UA 78127
|
||||
|
||||
#define BCL_VBAT_READ 0x76
|
||||
#define BCL_VBAT_ADC_LOW 0x48
|
||||
#define BCL_VBAT_COMP_LOW 0x49
|
||||
#define BCL_VBAT_COMP_TLOW 0x4A
|
||||
|
||||
#define BCL_IRQ_L0 0x1
|
||||
#define BCL_IRQ_L1 0x2
|
||||
#define BCL_IRQ_L2 0x4
|
||||
|
||||
#define BCL_VBAT_SCALING_UV 49827
|
||||
#define BCL_VBAT_NO_READING 127
|
||||
#define BCL_VBAT_BASE_MV 2000
|
||||
#define BCL_VBAT_INC_MV 25
|
||||
#define BCL_VBAT_MAX_MV 3600
|
||||
#define BCL_VBAT_THRESH_BASE 2250
|
||||
|
||||
#define MAX_PERPH_COUNT 2
|
||||
|
||||
enum bcl_dev_type {
|
||||
BCL_IBAT_LVL0,
|
||||
BCL_IBAT_LVL1,
|
||||
BCL_VBAT_LVL0,
|
||||
BCL_VBAT_LVL1,
|
||||
BCL_VBAT_LVL2,
|
||||
BCL_LVL0,
|
||||
BCL_LVL1,
|
||||
BCL_LVL2,
|
||||
BCL_TYPE_MAX,
|
||||
};
|
||||
|
||||
static char bcl_int_names[BCL_TYPE_MAX][25] = {
|
||||
"bcl-ibat-lvl0",
|
||||
"bcl-ibat-lvl1",
|
||||
"bcl-vbat-lvl0",
|
||||
"bcl-vbat-lvl1",
|
||||
"bcl-vbat-lvl2",
|
||||
"bcl-lvl0",
|
||||
"bcl-lvl1",
|
||||
"bcl-lvl2",
|
||||
};
|
||||
|
||||
struct bcl_device;
|
||||
|
||||
struct bcl_peripheral_data {
|
||||
int irq_num;
|
||||
int status_bit_idx;
|
||||
long trip_thresh;
|
||||
int last_val;
|
||||
struct mutex state_trans_lock;
|
||||
bool irq_enabled;
|
||||
enum bcl_dev_type type;
|
||||
struct thermal_zone_of_device_ops ops;
|
||||
struct thermal_zone_device *tz_dev;
|
||||
struct bcl_device *dev;
|
||||
};
|
||||
|
||||
struct bcl_device {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
uint16_t fg_bcl_addr;
|
||||
struct bcl_peripheral_data param[BCL_TYPE_MAX];
|
||||
};
|
||||
|
||||
static struct bcl_device *bcl_devices[MAX_PERPH_COUNT];
|
||||
static int bcl_device_ct;
|
||||
static bool ibat_use_qg_adc;
|
||||
|
||||
static int bcl_read_register(struct bcl_device *bcl_perph, int16_t reg_offset,
|
||||
unsigned int *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!bcl_perph) {
|
||||
pr_err("BCL device not initialized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = regmap_read(bcl_perph->regmap,
|
||||
(bcl_perph->fg_bcl_addr + reg_offset),
|
||||
data);
|
||||
if (ret < 0)
|
||||
pr_err("Error reading register 0x%04x err:%d\n",
|
||||
bcl_perph->fg_bcl_addr + reg_offset, ret);
|
||||
else
|
||||
pr_debug("Read register:0x%04x value:0x%02x\n",
|
||||
bcl_perph->fg_bcl_addr + reg_offset,
|
||||
*data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcl_write_register(struct bcl_device *bcl_perph,
|
||||
int16_t reg_offset, uint8_t data)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t *write_buf = &data;
|
||||
uint16_t base;
|
||||
|
||||
if (!bcl_perph) {
|
||||
pr_err("BCL device not initialized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
base = bcl_perph->fg_bcl_addr;
|
||||
ret = regmap_write(bcl_perph->regmap, (base + reg_offset), *write_buf);
|
||||
if (ret < 0) {
|
||||
pr_err("Error reading register:0x%04x val:0x%02x err:%d\n",
|
||||
base + reg_offset, data, ret);
|
||||
return ret;
|
||||
}
|
||||
pr_debug("wrote 0x%02x to 0x%04x\n", data, base + reg_offset);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void convert_adc_to_vbat_thresh_val(int *val)
|
||||
{
|
||||
/*
|
||||
* Threshold register is bit shifted from ADC MSB.
|
||||
* So the scaling factor is half.
|
||||
*/
|
||||
*val = (*val * BCL_VBAT_SCALING_UV) / 2000;
|
||||
}
|
||||
|
||||
static void convert_adc_to_vbat_val(int *val)
|
||||
{
|
||||
*val = (*val * BCL_VBAT_SCALING_UV) / 1000;
|
||||
}
|
||||
|
||||
static void convert_ibat_to_adc_val(int *val)
|
||||
{
|
||||
/*
|
||||
* Threshold register is bit shifted from ADC MSB.
|
||||
* So the scaling factor is half.
|
||||
*/
|
||||
if (ibat_use_qg_adc)
|
||||
*val = (int)div_s64(*val * 2000 * 2, BCL_IBAT_SCALING_UA);
|
||||
else
|
||||
*val = (int)div_s64(*val * 2000, BCL_IBAT_SCALING_UA);
|
||||
|
||||
}
|
||||
|
||||
static void convert_adc_to_ibat_val(int *val)
|
||||
{
|
||||
/* Scaling factor will be half if ibat_use_qg_adc is true */
|
||||
if (ibat_use_qg_adc)
|
||||
*val = (int)div_s64(*val * BCL_IBAT_SCALING_UA, 2 * 1000);
|
||||
else
|
||||
*val = (int)div_s64(*val * BCL_IBAT_SCALING_UA, 1000);
|
||||
}
|
||||
|
||||
static int bcl_set_ibat(void *data, int low, int high)
|
||||
{
|
||||
int ret = 0, ibat_ua, thresh_value;
|
||||
int8_t val = 0;
|
||||
int16_t addr;
|
||||
struct bcl_peripheral_data *bat_data =
|
||||
(struct bcl_peripheral_data *)data;
|
||||
|
||||
mutex_lock(&bat_data->state_trans_lock);
|
||||
thresh_value = high;
|
||||
if (bat_data->trip_thresh == thresh_value)
|
||||
goto set_trip_exit;
|
||||
|
||||
if (bat_data->irq_num && bat_data->irq_enabled) {
|
||||
disable_irq_nosync(bat_data->irq_num);
|
||||
bat_data->irq_enabled = false;
|
||||
}
|
||||
if (thresh_value == INT_MAX) {
|
||||
bat_data->trip_thresh = thresh_value;
|
||||
goto set_trip_exit;
|
||||
}
|
||||
|
||||
ibat_ua = thresh_value;
|
||||
convert_ibat_to_adc_val(&thresh_value);
|
||||
val = (int8_t)thresh_value;
|
||||
switch (bat_data->type) {
|
||||
case BCL_IBAT_LVL0:
|
||||
addr = BCL_IBAT_HIGH;
|
||||
pr_debug("ibat high threshold:%d mA ADC:0x%02x\n",
|
||||
ibat_ua, val);
|
||||
break;
|
||||
case BCL_IBAT_LVL1:
|
||||
addr = BCL_IBAT_TOO_HIGH;
|
||||
pr_debug("ibat too high threshold:%d mA ADC:0x%02x\n",
|
||||
ibat_ua, val);
|
||||
break;
|
||||
default:
|
||||
goto set_trip_exit;
|
||||
}
|
||||
ret = bcl_write_register(bat_data->dev, addr, val);
|
||||
if (ret)
|
||||
goto set_trip_exit;
|
||||
bat_data->trip_thresh = ibat_ua;
|
||||
|
||||
if (bat_data->irq_num && !bat_data->irq_enabled) {
|
||||
enable_irq(bat_data->irq_num);
|
||||
bat_data->irq_enabled = true;
|
||||
}
|
||||
|
||||
set_trip_exit:
|
||||
mutex_unlock(&bat_data->state_trans_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcl_read_ibat(void *data, int *adc_value)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int val = 0;
|
||||
struct bcl_peripheral_data *bat_data =
|
||||
(struct bcl_peripheral_data *)data;
|
||||
|
||||
*adc_value = val;
|
||||
ret = bcl_read_register(bat_data->dev, BCL_IBAT_READ, &val);
|
||||
if (ret)
|
||||
goto bcl_read_exit;
|
||||
/* IBat ADC reading is in 2's compliment form */
|
||||
*adc_value = sign_extend32(val, 7);
|
||||
if (val == 0) {
|
||||
/*
|
||||
* The sensor sometime can read a value 0 if there is
|
||||
* consequtive reads
|
||||
*/
|
||||
*adc_value = bat_data->last_val;
|
||||
} else {
|
||||
convert_adc_to_ibat_val(adc_value);
|
||||
bat_data->last_val = *adc_value;
|
||||
}
|
||||
pr_debug("ibat:%d mA ADC:0x%02x\n", bat_data->last_val, val);
|
||||
|
||||
bcl_read_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcl_get_vbat_trip(struct thermal_zone_device *tzd,
|
||||
int type, int *trip)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int val = 0;
|
||||
struct bcl_peripheral_data *bat_data =
|
||||
(struct bcl_peripheral_data *)tzd->devdata;
|
||||
int16_t addr;
|
||||
|
||||
*trip = 0;
|
||||
switch (type + BCL_VBAT_LVL0) {
|
||||
case BCL_VBAT_LVL0:
|
||||
addr = BCL_VBAT_ADC_LOW;
|
||||
break;
|
||||
case BCL_VBAT_LVL1:
|
||||
addr = BCL_VBAT_COMP_LOW;
|
||||
break;
|
||||
case BCL_VBAT_LVL2:
|
||||
addr = BCL_VBAT_COMP_TLOW;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = bcl_read_register(bat_data->dev, addr, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (addr == BCL_VBAT_ADC_LOW) {
|
||||
*trip = val;
|
||||
convert_adc_to_vbat_thresh_val(trip);
|
||||
pr_debug("vbat trip: %d mV ADC:0x%02x\n", *trip, val);
|
||||
} else {
|
||||
*trip = BCL_VBAT_THRESH_BASE + val * 25;
|
||||
if (*trip > BCL_VBAT_MAX_MV)
|
||||
*trip = BCL_VBAT_MAX_MV;
|
||||
pr_debug("vbat-%s-low trip: %d mV ADC:0x%02x\n",
|
||||
(addr == BCL_VBAT_COMP_LOW) ?
|
||||
"too" : "critical",
|
||||
*trip, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcl_read_vbat_tz(struct thermal_zone_device *tzd, int *adc_value)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int val = 0;
|
||||
struct bcl_peripheral_data *bat_data =
|
||||
(struct bcl_peripheral_data *)tzd->devdata;
|
||||
|
||||
*adc_value = val;
|
||||
ret = bcl_read_register(bat_data->dev, BCL_VBAT_READ, &val);
|
||||
if (ret)
|
||||
goto bcl_read_exit;
|
||||
*adc_value = val;
|
||||
if (*adc_value == BCL_VBAT_NO_READING) {
|
||||
*adc_value = bat_data->last_val;
|
||||
} else {
|
||||
convert_adc_to_vbat_val(adc_value);
|
||||
bat_data->last_val = *adc_value;
|
||||
}
|
||||
pr_debug("vbat:%d mv\n", bat_data->last_val);
|
||||
|
||||
bcl_read_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcl_read_vbat_type(struct thermal_zone_device *tzd, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops vbat_tzd_ops = {
|
||||
.get_temp = bcl_read_vbat_tz,
|
||||
.get_trip_temp = bcl_get_vbat_trip,
|
||||
.get_trip_type = bcl_read_vbat_type,
|
||||
};
|
||||
|
||||
static struct thermal_zone_params vbat_tzp = {
|
||||
.governor_name = "step_wise",
|
||||
.no_hwmon = true,
|
||||
.num_tbps = 0,
|
||||
.tbp = NULL,
|
||||
.sustainable_power = 0,
|
||||
.k_po = 0,
|
||||
.k_pu = 0,
|
||||
.k_i = 0,
|
||||
.k_d = 0,
|
||||
.integral_cutoff = 0,
|
||||
.slope = 1,
|
||||
.offset = 0
|
||||
};
|
||||
|
||||
static int bcl_set_lbat(void *data, int low, int high)
|
||||
{
|
||||
struct bcl_peripheral_data *bat_data =
|
||||
(struct bcl_peripheral_data *)data;
|
||||
|
||||
mutex_lock(&bat_data->state_trans_lock);
|
||||
|
||||
if (high == INT_MAX &&
|
||||
bat_data->irq_num && bat_data->irq_enabled) {
|
||||
disable_irq_nosync(bat_data->irq_num);
|
||||
bat_data->irq_enabled = false;
|
||||
pr_debug("lbat[%d]: disable irq:%d\n",
|
||||
bat_data->type,
|
||||
bat_data->irq_num);
|
||||
} else if (high != INT_MAX &&
|
||||
bat_data->irq_num && !bat_data->irq_enabled) {
|
||||
enable_irq(bat_data->irq_num);
|
||||
bat_data->irq_enabled = true;
|
||||
pr_debug("lbat[%d]: enable irq:%d\n",
|
||||
bat_data->type,
|
||||
bat_data->irq_num);
|
||||
}
|
||||
|
||||
mutex_unlock(&bat_data->state_trans_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcl_read_lbat(void *data, int *adc_value)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int val = 0;
|
||||
struct bcl_peripheral_data *bat_data =
|
||||
(struct bcl_peripheral_data *)data;
|
||||
struct bcl_device *bcl_perph = bat_data->dev;
|
||||
|
||||
*adc_value = val;
|
||||
ret = bcl_read_register(bcl_perph, BCL_IRQ_STATUS, &val);
|
||||
if (ret)
|
||||
goto bcl_read_exit;
|
||||
switch (bat_data->type) {
|
||||
case BCL_LVL0:
|
||||
*adc_value = val & BCL_IRQ_L0;
|
||||
break;
|
||||
case BCL_LVL1:
|
||||
*adc_value = val & BCL_IRQ_L1;
|
||||
break;
|
||||
case BCL_LVL2:
|
||||
*adc_value = val & BCL_IRQ_L2;
|
||||
break;
|
||||
default:
|
||||
pr_err("Invalid sensor type:%d\n", bat_data->type);
|
||||
ret = -ENODEV;
|
||||
goto bcl_read_exit;
|
||||
}
|
||||
bat_data->last_val = *adc_value;
|
||||
pr_debug("lbat:%d val:%d\n", bat_data->type,
|
||||
bat_data->last_val);
|
||||
|
||||
bcl_read_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t bcl_handle_irq(int irq, void *data)
|
||||
{
|
||||
struct bcl_peripheral_data *perph_data =
|
||||
(struct bcl_peripheral_data *)data;
|
||||
unsigned int irq_status = 0;
|
||||
struct bcl_device *bcl_perph;
|
||||
|
||||
bcl_perph = perph_data->dev;
|
||||
bcl_read_register(bcl_perph, BCL_IRQ_STATUS, &irq_status);
|
||||
|
||||
if (irq_status & perph_data->status_bit_idx) {
|
||||
pr_debug("Irq:%d triggered for bcl type:%s. status:%u\n",
|
||||
irq, bcl_int_names[perph_data->type],
|
||||
irq_status);
|
||||
of_thermal_handle_trip_temp(perph_data->dev->dev,
|
||||
perph_data->tz_dev,
|
||||
perph_data->status_bit_idx);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bcl_get_devicetree_data(struct platform_device *pdev,
|
||||
struct bcl_device *bcl_perph)
|
||||
{
|
||||
int ret = 0;
|
||||
const __be32 *prop = NULL;
|
||||
struct device_node *dev_node = pdev->dev.of_node;
|
||||
|
||||
prop = of_get_address(dev_node, 0, NULL, NULL);
|
||||
if (prop) {
|
||||
bcl_perph->fg_bcl_addr = be32_to_cpu(*prop);
|
||||
pr_debug("fg_bcl@%04x\n", bcl_perph->fg_bcl_addr);
|
||||
} else {
|
||||
dev_err(&pdev->dev, "No fg_bcl registers found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ibat_use_qg_adc = of_property_read_bool(dev_node,
|
||||
"qcom,ibat-use-qg-adc-5a");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bcl_fetch_trip(struct platform_device *pdev, enum bcl_dev_type type,
|
||||
struct bcl_peripheral_data *data,
|
||||
irqreturn_t (*handle)(int, void *))
|
||||
{
|
||||
int ret = 0, irq_num = 0;
|
||||
char *int_name = bcl_int_names[type];
|
||||
|
||||
mutex_lock(&data->state_trans_lock);
|
||||
data->irq_num = 0;
|
||||
data->irq_enabled = false;
|
||||
irq_num = platform_get_irq_byname(pdev, int_name);
|
||||
if (irq_num > 0 && handle) {
|
||||
ret = devm_request_threaded_irq(&pdev->dev,
|
||||
irq_num, NULL, handle,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
int_name, data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Error requesting trip irq. err:%d\n",
|
||||
ret);
|
||||
mutex_unlock(&data->state_trans_lock);
|
||||
return;
|
||||
}
|
||||
disable_irq_nosync(irq_num);
|
||||
data->irq_num = irq_num;
|
||||
} else if (irq_num > 0 && !handle) {
|
||||
disable_irq_nosync(irq_num);
|
||||
data->irq_num = irq_num;
|
||||
}
|
||||
mutex_unlock(&data->state_trans_lock);
|
||||
}
|
||||
|
||||
static void bcl_vbat_init(struct platform_device *pdev,
|
||||
enum bcl_dev_type type, struct bcl_device *bcl_perph)
|
||||
{
|
||||
struct bcl_peripheral_data *vbat = &bcl_perph->param[type];
|
||||
|
||||
mutex_init(&vbat->state_trans_lock);
|
||||
vbat->dev = bcl_perph;
|
||||
vbat->irq_num = 0;
|
||||
vbat->irq_enabled = false;
|
||||
vbat->tz_dev = thermal_zone_device_register("vbat", 3, 0, vbat,
|
||||
&vbat_tzd_ops, &vbat_tzp, 0, 0);
|
||||
if (IS_ERR(vbat->tz_dev)) {
|
||||
pr_debug("vbat[%s] register failed. err:%ld\n",
|
||||
bcl_int_names[type],
|
||||
PTR_ERR(vbat->tz_dev));
|
||||
vbat->tz_dev = NULL;
|
||||
return;
|
||||
}
|
||||
thermal_zone_device_update(vbat->tz_dev, THERMAL_DEVICE_UP);
|
||||
}
|
||||
|
||||
static void bcl_probe_vbat(struct platform_device *pdev,
|
||||
struct bcl_device *bcl_perph)
|
||||
{
|
||||
bcl_vbat_init(pdev, BCL_VBAT_LVL0, bcl_perph);
|
||||
}
|
||||
|
||||
static void bcl_ibat_init(struct platform_device *pdev,
|
||||
enum bcl_dev_type type, struct bcl_device *bcl_perph)
|
||||
{
|
||||
struct bcl_peripheral_data *ibat = &bcl_perph->param[type];
|
||||
|
||||
mutex_init(&ibat->state_trans_lock);
|
||||
ibat->type = type;
|
||||
ibat->dev = bcl_perph;
|
||||
ibat->irq_num = 0;
|
||||
ibat->irq_enabled = false;
|
||||
ibat->ops.get_temp = bcl_read_ibat;
|
||||
ibat->ops.set_trips = bcl_set_ibat;
|
||||
ibat->tz_dev = thermal_zone_of_sensor_register(&pdev->dev,
|
||||
type, ibat, &ibat->ops);
|
||||
if (IS_ERR(ibat->tz_dev)) {
|
||||
pr_debug("ibat:[%s] register failed. err:%ld\n",
|
||||
bcl_int_names[type],
|
||||
PTR_ERR(ibat->tz_dev));
|
||||
ibat->tz_dev = NULL;
|
||||
return;
|
||||
}
|
||||
thermal_zone_device_update(ibat->tz_dev, THERMAL_DEVICE_UP);
|
||||
}
|
||||
|
||||
static void bcl_probe_ibat(struct platform_device *pdev,
|
||||
struct bcl_device *bcl_perph)
|
||||
{
|
||||
bcl_ibat_init(pdev, BCL_IBAT_LVL0, bcl_perph);
|
||||
bcl_ibat_init(pdev, BCL_IBAT_LVL1, bcl_perph);
|
||||
}
|
||||
|
||||
static void bcl_lvl_init(struct platform_device *pdev,
|
||||
enum bcl_dev_type type, int sts_bit_idx, struct bcl_device *bcl_perph)
|
||||
{
|
||||
struct bcl_peripheral_data *lbat = &bcl_perph->param[type];
|
||||
|
||||
mutex_init(&lbat->state_trans_lock);
|
||||
lbat->type = type;
|
||||
lbat->dev = bcl_perph;
|
||||
lbat->status_bit_idx = sts_bit_idx;
|
||||
bcl_fetch_trip(pdev, type, lbat, bcl_handle_irq);
|
||||
if (lbat->irq_num <= 0)
|
||||
return;
|
||||
|
||||
lbat->ops.get_temp = bcl_read_lbat;
|
||||
lbat->ops.set_trips = bcl_set_lbat;
|
||||
|
||||
lbat->tz_dev = thermal_zone_of_sensor_register(&pdev->dev,
|
||||
type, lbat, &lbat->ops);
|
||||
if (IS_ERR(lbat->tz_dev)) {
|
||||
pr_debug("lbat:[%s] register failed. err:%ld\n",
|
||||
bcl_int_names[type],
|
||||
PTR_ERR(lbat->tz_dev));
|
||||
lbat->tz_dev = NULL;
|
||||
return;
|
||||
}
|
||||
thermal_zone_device_update(lbat->tz_dev, THERMAL_DEVICE_UP);
|
||||
}
|
||||
|
||||
static void bcl_probe_lvls(struct platform_device *pdev,
|
||||
struct bcl_device *bcl_perph)
|
||||
{
|
||||
bcl_lvl_init(pdev, BCL_LVL0, BCL_IRQ_L0, bcl_perph);
|
||||
bcl_lvl_init(pdev, BCL_LVL1, BCL_IRQ_L1, bcl_perph);
|
||||
bcl_lvl_init(pdev, BCL_LVL2, BCL_IRQ_L2, bcl_perph);
|
||||
}
|
||||
|
||||
static void bcl_configure_bcl_peripheral(struct bcl_device *bcl_perph)
|
||||
{
|
||||
bcl_write_register(bcl_perph, BCL_MONITOR_EN, BIT(7));
|
||||
}
|
||||
|
||||
static int bcl_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i = 0;
|
||||
struct bcl_device *bcl_perph =
|
||||
(struct bcl_device *)dev_get_drvdata(&pdev->dev);
|
||||
|
||||
for (; i < BCL_TYPE_MAX; i++) {
|
||||
if (!bcl_perph->param[i].tz_dev)
|
||||
continue;
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev,
|
||||
bcl_perph->param[i].tz_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcl_device *bcl_perph = NULL;
|
||||
|
||||
if (bcl_device_ct >= MAX_PERPH_COUNT) {
|
||||
dev_err(&pdev->dev, "Max bcl peripheral supported already.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
bcl_devices[bcl_device_ct] = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*bcl_devices[0]), GFP_KERNEL);
|
||||
if (!bcl_devices[bcl_device_ct])
|
||||
return -ENOMEM;
|
||||
bcl_perph = bcl_devices[bcl_device_ct];
|
||||
bcl_perph->dev = &pdev->dev;
|
||||
|
||||
bcl_perph->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!bcl_perph->regmap) {
|
||||
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bcl_device_ct++;
|
||||
bcl_get_devicetree_data(pdev, bcl_perph);
|
||||
bcl_probe_vbat(pdev, bcl_perph);
|
||||
bcl_probe_ibat(pdev, bcl_perph);
|
||||
bcl_probe_lvls(pdev, bcl_perph);
|
||||
bcl_configure_bcl_peripheral(bcl_perph);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, bcl_perph);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcl_match[] = {
|
||||
{
|
||||
.compatible = "qcom,bcl-v5",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver bcl_driver = {
|
||||
.probe = bcl_probe,
|
||||
.remove = bcl_remove,
|
||||
.driver = {
|
||||
.name = BCL_DRIVER_NAME,
|
||||
.of_match_table = bcl_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(bcl_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
189
drivers/thermal/qcom/bcl_soc.c
Normal file
189
drivers/thermal/qcom/bcl_soc.c
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018-2019, The Linux Foundation. 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_core.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_set_soc(void *data, int low, int high)
|
||||
{
|
||||
if (low == bcl_perph->trip_temp)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&bcl_perph->state_trans_lock);
|
||||
pr_debug("low soc threshold:%d\n", low);
|
||||
bcl_perph->trip_temp = low;
|
||||
if (low == INT_MIN) {
|
||||
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 = 100;
|
||||
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 = ret.intval;
|
||||
}
|
||||
pr_debug("soc:%d\n", *val);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void bcl_evaluate_soc(struct work_struct *work)
|
||||
{
|
||||
int battery_percentage;
|
||||
|
||||
if (!bcl_perph->tz_dev)
|
||||
return;
|
||||
|
||||
if (bcl_read_soc(NULL, &battery_percentage))
|
||||
return;
|
||||
|
||||
mutex_lock(&bcl_perph->state_trans_lock);
|
||||
if (!bcl_perph->irq_enabled)
|
||||
goto eval_exit;
|
||||
if (battery_percentage > bcl_perph->trip_temp)
|
||||
goto eval_exit;
|
||||
|
||||
bcl_perph->trip_val = battery_percentage;
|
||||
mutex_unlock(&bcl_perph->state_trans_lock);
|
||||
of_thermal_handle_trip_temp(bcl_perph->dev,
|
||||
bcl_perph->tz_dev, bcl_perph->trip_val);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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 v2");
|
||||
724
drivers/thermal/qcom/qmi_sensors.c
Normal file
724
drivers/thermal/qcom/qmi_sensors.c
Normal file
|
|
@ -0,0 +1,724 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018-2019, The Linux Foundation. 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 <linux/kernel.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include "thermal_sensor_service_v01.h"
|
||||
#include "../thermal_core.h"
|
||||
|
||||
#define QMI_SENS_DRIVER "qmi-therm-sensors"
|
||||
#define QMI_TS_RESP_TOUT msecs_to_jiffies(100)
|
||||
#define QMI_CLIENT_NAME_LENGTH 40
|
||||
#define QMI_FL_SIGN 0x80000000
|
||||
#define QMI_FL_EXP 0x7f800000
|
||||
#define QMI_FL_MANTISSA 0x007fffff
|
||||
#define QMI_FL_NORM 0x00800000
|
||||
#define QMI_FL_SIGN_BIT 31
|
||||
#define QMI_MANTISSA_MSB 23
|
||||
|
||||
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_MAX_NR
|
||||
};
|
||||
|
||||
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 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"},
|
||||
};
|
||||
|
||||
static int32_t encode_qmi(int32_t val)
|
||||
{
|
||||
uint32_t shift = 0, local_val = 0;
|
||||
int32_t temp_val = 0;
|
||||
|
||||
if (val == INT_MAX || val == INT_MIN)
|
||||
return 0;
|
||||
|
||||
temp_val = val = val / 1000;
|
||||
if (val < 0) {
|
||||
temp_val *= -1;
|
||||
local_val |= 1 << QMI_FL_SIGN_BIT;
|
||||
}
|
||||
shift = find_last_bit((const unsigned long *)&temp_val,
|
||||
sizeof(temp_val) * 8);
|
||||
local_val |= ((shift + 127) << QMI_MANTISSA_MSB);
|
||||
temp_val &= ~(1 << shift);
|
||||
|
||||
local_val |= temp_val << (QMI_MANTISSA_MSB - shift);
|
||||
pr_debug("inp:%d shift:%d out:%x temp_val:%x\n",
|
||||
val, shift, local_val, temp_val);
|
||||
|
||||
return local_val;
|
||||
}
|
||||
|
||||
static int32_t decode_qmi(int32_t val)
|
||||
{
|
||||
int32_t sign = 0, shift = 0, local_val;
|
||||
|
||||
sign = (val & QMI_FL_SIGN) ? -1 : 1;
|
||||
shift = (val & QMI_FL_EXP) >> QMI_MANTISSA_MSB;
|
||||
shift = QMI_MANTISSA_MSB - (shift - 127);
|
||||
local_val = (val & QMI_FL_MANTISSA) | QMI_FL_NORM;
|
||||
pr_debug("val:0x%x sign:%d shift:%d mantissa:%x temp:%d\n",
|
||||
val, sign, shift, local_val,
|
||||
sign * (local_val >> shift));
|
||||
|
||||
return sign * (local_val >> shift);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
of_thermal_handle_trip_temp(qmi_sens->dev, qmi_sens->tz_dev,
|
||||
qmi_sens->last_reading);
|
||||
};
|
||||
|
||||
static void qmi_ts_update_temperature(struct qmi_ts_instance *ts,
|
||||
const struct ts_temp_report_ind_msg_v01 *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_V01)))
|
||||
continue;
|
||||
|
||||
qmi_sens->last_reading =
|
||||
decode_qmi(ind_msg->temp) * 1000;
|
||||
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->high_thresh != INT_MAX &&
|
||||
qmi_sens->last_reading >= qmi_sens->high_thresh) ||
|
||||
(qmi_sens->low_thresh != INT_MIN &&
|
||||
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(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
|
||||
struct qmi_txn *txn, const void *decoded)
|
||||
{
|
||||
const struct ts_temp_report_ind_msg_v01 *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_V01) ||
|
||||
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_request(struct qmi_sensor *qmi_sens,
|
||||
bool send_current_temp_report)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ts_register_notification_temp_resp_msg_v01 resp;
|
||||
struct ts_register_notification_temp_req_msg_v01 req;
|
||||
struct qmi_ts_instance *ts = qmi_sens->ts;
|
||||
struct qmi_txn txn;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
|
||||
strlcpy(req.sensor_id.sensor_id, qmi_sens->qmi_name,
|
||||
QMI_TS_SENSOR_ID_LENGTH_MAX_V01);
|
||||
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 =
|
||||
encode_qmi(qmi_sens->high_thresh);
|
||||
req.temp_threshold_low_valid =
|
||||
qmi_sens->low_thresh != INT_MIN;
|
||||
req.temp_threshold_low =
|
||||
encode_qmi(qmi_sens->low_thresh);
|
||||
}
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
ret = qmi_txn_init(&ts->handle, &txn,
|
||||
ts_register_notification_temp_resp_msg_v01_ei, &resp);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi txn init failed for %s ret:%d\n",
|
||||
qmi_sens->qmi_name, ret);
|
||||
goto qmi_send_exit;
|
||||
}
|
||||
|
||||
ret = qmi_send_request(&ts->handle, NULL, &txn,
|
||||
QMI_TS_REGISTER_NOTIFICATION_TEMP_REQ_V01,
|
||||
TS_REGISTER_NOTIFICATION_TEMP_REQ_MSG_V01_MAX_MSG_LEN,
|
||||
ts_register_notification_temp_req_msg_v01_ei, &req);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi txn send failed for %s ret:%d\n",
|
||||
qmi_sens->qmi_name, ret);
|
||||
qmi_txn_cancel(&txn);
|
||||
goto qmi_send_exit;
|
||||
}
|
||||
|
||||
ret = qmi_txn_wait(&txn, QMI_TS_RESP_TOUT);
|
||||
if (ret < 0) {
|
||||
pr_err("qmi txn wait failed for %s ret:%d\n",
|
||||
qmi_sens->qmi_name, ret);
|
||||
goto qmi_send_exit;
|
||||
}
|
||||
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
|
||||
ret = resp.resp.result;
|
||||
pr_err("qmi NOT success for %s ret:%d\n",
|
||||
qmi_sens->qmi_name, ret);
|
||||
goto qmi_send_exit;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
qmi_send_exit:
|
||||
mutex_unlock(&ts->mutex);
|
||||
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)
|
||||
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_V01,
|
||||
.ei = ts_temp_report_ind_msg_v01_ei,
|
||||
.decoded_size = sizeof(struct ts_temp_report_ind_msg_v01),
|
||||
.fn = qmi_ts_ind_cb
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
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_v01 req;
|
||||
struct ts_get_sensor_list_resp_msg_v01 *ts_resp;
|
||||
int ret = 0, i;
|
||||
struct qmi_txn txn;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
/* size of ts_resp is very high, use heap memory rather than stack */
|
||||
ts_resp = kzalloc(sizeof(*ts_resp), GFP_KERNEL);
|
||||
if (!ts_resp)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
ret = qmi_txn_init(&ts->handle, &txn,
|
||||
ts_get_sensor_list_resp_msg_v01_ei, ts_resp);
|
||||
if (ret < 0) {
|
||||
pr_err("Transaction Init error for inst_id:0x%x ret:%d\n",
|
||||
ts->inst_id, ret);
|
||||
goto reg_exit;
|
||||
}
|
||||
|
||||
ret = qmi_send_request(&ts->handle, NULL, &txn,
|
||||
QMI_TS_GET_SENSOR_LIST_REQ_V01,
|
||||
TS_GET_SENSOR_LIST_REQ_MSG_V01_MAX_MSG_LEN,
|
||||
ts_get_sensor_list_req_msg_v01_ei,
|
||||
&req);
|
||||
if (ret < 0) {
|
||||
qmi_txn_cancel(&txn);
|
||||
goto reg_exit;
|
||||
}
|
||||
|
||||
ret = qmi_txn_wait(&txn, QMI_TS_RESP_TOUT);
|
||||
if (ret < 0) {
|
||||
pr_err("Transaction wait error for inst_id:0x%x ret:%d\n",
|
||||
ts->inst_id, ret);
|
||||
goto reg_exit;
|
||||
}
|
||||
if (ts_resp->resp.result != QMI_RESULT_SUCCESS_V01) {
|
||||
ret = ts_resp->resp.result;
|
||||
pr_err("Get sensor list NOT success for inst_id:0x%x ret:%d\n",
|
||||
ts->inst_id, ret);
|
||||
goto reg_exit;
|
||||
}
|
||||
mutex_unlock(&ts->mutex);
|
||||
|
||||
for (i = 0; i < ts_resp->sensor_list_len; i++) {
|
||||
struct qmi_sensor *qmi_sens = NULL;
|
||||
|
||||
list_for_each_entry(qmi_sens, &ts->ts_sensor_list,
|
||||
ts_node) {
|
||||
if ((strncasecmp(qmi_sens->qmi_name,
|
||||
ts_resp->sensor_list[i].sensor_id,
|
||||
QMI_TS_SENSOR_ID_LENGTH_MAX_V01)))
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(ts_resp);
|
||||
return ret;
|
||||
|
||||
reg_exit:
|
||||
mutex_unlock(&ts->mutex);
|
||||
kfree(ts_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);
|
||||
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);
|
||||
|
||||
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);
|
||||
strlcpy(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_MIN;
|
||||
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_V01_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);
|
||||
goto probe_err;
|
||||
}
|
||||
ret = qmi_add_lookup(&ts->handle, TS_SERVICE_ID_V01,
|
||||
TS_SERVICE_VERS_V01, ts->inst_id);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "QMI register failed for 0x%x, ret:%d\n",
|
||||
ts->inst_id, ret);
|
||||
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 v2");
|
||||
253
drivers/thermal/qcom/thermal_sensor_service_v01.c
Normal file
253
drivers/thermal/qcom/thermal_sensor_service_v01.c
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/soc/qcom/qmi.h>
|
||||
#include "thermal_sensor_service_v01.h"
|
||||
|
||||
static struct qmi_elem_info ts_sensor_type_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRING,
|
||||
.elem_len = QMI_TS_SENSOR_ID_LENGTH_MAX_V01 + 1,
|
||||
.elem_size = sizeof(char),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0,
|
||||
.offset = offsetof(struct ts_sensor_type_v01,
|
||||
sensor_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.array_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_get_sensor_list_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.array_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_get_sensor_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 ts_get_sensor_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 ts_get_sensor_list_resp_msg_v01,
|
||||
sensor_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 ts_get_sensor_list_resp_msg_v01,
|
||||
sensor_list_len),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = QMI_TS_SENSOR_LIST_MAX_V01,
|
||||
.elem_size = sizeof(struct ts_sensor_type_v01),
|
||||
.array_type = VAR_LEN_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset =
|
||||
offsetof(struct ts_get_sensor_list_resp_msg_v01,
|
||||
sensor_list),
|
||||
.ei_array = ts_sensor_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.array_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_register_notification_temp_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct ts_sensor_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(
|
||||
struct ts_register_notification_temp_req_msg_v01,
|
||||
sensor_id),
|
||||
.ei_array = ts_sensor_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 ts_register_notification_temp_req_msg_v01,
|
||||
send_current_temp_report),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(
|
||||
struct ts_register_notification_temp_req_msg_v01,
|
||||
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_v01,
|
||||
temp_threshold_high),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(
|
||||
struct ts_register_notification_temp_req_msg_v01,
|
||||
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_v01,
|
||||
temp_threshold_low),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x12,
|
||||
.offset = offsetof(
|
||||
struct ts_register_notification_temp_req_msg_v01,
|
||||
seq_num_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x12,
|
||||
.offset = offsetof(
|
||||
struct ts_register_notification_temp_req_msg_v01,
|
||||
seq_num),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.array_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_register_notification_temp_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 ts_register_notification_temp_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = qmi_response_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.array_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct qmi_elem_info ts_temp_report_ind_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct ts_sensor_type_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v01,
|
||||
sensor_id),
|
||||
.ei_array = ts_sensor_type_v01_ei,
|
||||
},
|
||||
{
|
||||
.data_type = QMI_SIGNED_4_BYTE_ENUM,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(enum ts_temp_report_type_enum_v01),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v01,
|
||||
report_type),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v01,
|
||||
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_v01,
|
||||
temp),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v01,
|
||||
seq_num_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_4_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint32_t),
|
||||
.array_type = NO_ARRAY,
|
||||
.tlv_type = 0x11,
|
||||
.offset = offsetof(struct ts_temp_report_ind_msg_v01,
|
||||
seq_num),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.array_type = NO_ARRAY,
|
||||
.array_type = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
82
drivers/thermal/qcom/thermal_sensor_service_v01.h
Normal file
82
drivers/thermal/qcom/thermal_sensor_service_v01.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_SENSOR_SERVICE_V01_H
|
||||
#define THERMAL_SENSOR_SERVICE_V01_H
|
||||
|
||||
#define TS_SERVICE_ID_V01 0x17
|
||||
#define TS_SERVICE_VERS_V01 0x01
|
||||
|
||||
#define QMI_TS_GET_SENSOR_LIST_RESP_V01 0x0020
|
||||
#define QMI_TS_GET_SUPPORTED_MSGS_REQ_V01 0x001E
|
||||
#define QMI_TS_GET_SUPPORTED_MSGS_RESP_V01 0x001E
|
||||
#define QMI_TS_REGISTER_NOTIFICATION_TEMP_REQ_V01 0x0021
|
||||
#define QMI_TS_REGISTER_NOTIFICATION_TEMP_RESP_V01 0x0021
|
||||
#define QMI_TS_GET_SUPPORTED_FIELDS_RESP_V01 0x001F
|
||||
#define QMI_TS_GET_SENSOR_LIST_REQ_V01 0x0020
|
||||
#define QMI_TS_TEMP_REPORT_IND_V01 0x0022
|
||||
#define QMI_TS_GET_SUPPORTED_FIELDS_REQ_V01 0x001F
|
||||
|
||||
#define QMI_TS_SENSOR_ID_LENGTH_MAX_V01 32
|
||||
#define QMI_TS_SENSOR_LIST_MAX_V01 32
|
||||
|
||||
struct ts_sensor_type_v01 {
|
||||
char sensor_id[QMI_TS_SENSOR_ID_LENGTH_MAX_V01 + 1];
|
||||
};
|
||||
|
||||
struct ts_get_sensor_list_req_msg_v01 {
|
||||
char placeholder;
|
||||
};
|
||||
#define TS_GET_SENSOR_LIST_REQ_MSG_V01_MAX_MSG_LEN 0
|
||||
extern struct qmi_elem_info ts_get_sensor_list_req_msg_v01_ei[];
|
||||
|
||||
struct ts_get_sensor_list_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
uint8_t sensor_list_valid;
|
||||
uint32_t sensor_list_len;
|
||||
struct ts_sensor_type_v01 sensor_list[QMI_TS_SENSOR_LIST_MAX_V01];
|
||||
};
|
||||
#define TS_GET_SENSOR_LIST_RESP_MSG_V01_MAX_MSG_LEN 1067
|
||||
extern struct qmi_elem_info ts_get_sensor_list_resp_msg_v01_ei[];
|
||||
|
||||
struct ts_register_notification_temp_req_msg_v01 {
|
||||
struct ts_sensor_type_v01 sensor_id;
|
||||
uint8_t send_current_temp_report;
|
||||
uint8_t temp_threshold_high_valid;
|
||||
int temp_threshold_high;
|
||||
uint8_t temp_threshold_low_valid;
|
||||
int temp_threshold_low;
|
||||
uint8_t seq_num_valid;
|
||||
uint32_t seq_num;
|
||||
};
|
||||
#define TS_REGISTER_NOTIFICATION_TEMP_REQ_MSG_V01_MAX_MSG_LEN 61
|
||||
extern struct qmi_elem_info ts_register_notification_temp_req_msg_v01_ei[];
|
||||
|
||||
struct ts_register_notification_temp_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define TS_REGISTER_NOTIFICATION_TEMP_RESP_MSG_V01_MAX_MSG_LEN 7
|
||||
extern struct qmi_elem_info ts_register_notification_temp_resp_msg_v01_ei[];
|
||||
|
||||
enum ts_temp_report_type_enum_v01 {
|
||||
TS_TEMP_REPORT_TYPE_ENUM_MIN_VAL_V01 = INT_MIN,
|
||||
QMI_TS_TEMP_REPORT_CURRENT_TEMP_V01 = 0,
|
||||
QMI_TS_TEMP_REPORT_THRESHOLD_HIGH_V01 = 1,
|
||||
QMI_TS_TEMP_REPORT_THRESHOLD_LOW_V01 = 2,
|
||||
TS_TEMP_REPORT_TYPE_ENUM_MAX_VAL_V01 = INT_MAX,
|
||||
};
|
||||
|
||||
struct ts_temp_report_ind_msg_v01 {
|
||||
struct ts_sensor_type_v01 sensor_id;
|
||||
enum ts_temp_report_type_enum_v01 report_type;
|
||||
uint8_t temp_valid;
|
||||
long temp;
|
||||
uint8_t seq_num_valid;
|
||||
uint32_t seq_num;
|
||||
};
|
||||
#define TS_TEMP_REPORT_IND_MSG_V01_MAX_MSG_LEN 57
|
||||
extern struct qmi_elem_info ts_temp_report_ind_msg_v01_ei[];
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue