Merge "msm: tsens: Add Tsens driver snapshot"

This commit is contained in:
qctecmdr 2020-01-07 05:28:23 -08:00 • committed by Gerrit - the friendly Code Review server
commit 2b9c58c7eb
10 changed files with 2991 additions and 0 deletions

View file

@ -396,6 +396,16 @@ config GENERIC_ADC_THERMAL
to this driver. This driver reports the temperature by reading ADC
channel and converts it to temperature based on lookup table.
config THERMAL_TSENS
tristate "Qualcomm Technologies Inc. TSENS Temperature driver"
depends on THERMAL
help
This enables the thermal sysfs driver for the TSENS device. It shows
up in Sysfs as a thermal zone with multiple trip points. Also able
to set threshold temperature for both warm and cool and update
thermal userspace client when a threshold is reached. Warm/Cool
temperature thresholds can be set independently for each sensor.
menu "Qualcomm thermal drivers"
depends on (ARCH_QCOM && OF) || COMPILE_TEST
source "drivers/thermal/qcom/Kconfig"

View file

@ -54,3 +54,5 @@ obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
obj-$(CONFIG_THERMAL_TSENS) += msm-tsens-driver.o
msm-tsens-driver-objs := msm-tsens.o tsens2xxx.o tsens-dbg.o tsens-mtc.o tsens1xxx.o tsens-calib.o

396
drivers/thermal/msm-tsens.c Normal file
View file

@ -0,0 +1,396 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include "tsens.h"
#include "thermal_core.h"
LIST_HEAD(tsens_device_list);
static int tsens_get_temp(void *data, int *temp)
{
struct tsens_sensor *s = data;
struct tsens_device *tmdev = s->tmdev;
return tmdev->ops->get_temp(s, temp);
}
static int tsens_get_min_temp(void *data, int *temp)
{
struct tsens_sensor *s = data;
return tsens_2xxx_get_min_temp(s, temp);
}
static int tsens_set_trip_temp(void *data, int low_temp, int high_temp)
{
struct tsens_sensor *s = data;
struct tsens_device *tmdev = s->tmdev;
if (tmdev->ops->set_trips)
return tmdev->ops->set_trips(s, low_temp, high_temp);
return 0;
}
static int tsens_init(struct tsens_device *tmdev)
{
return tmdev->ops->hw_init(tmdev);
}
static int tsens_calib(struct tsens_device *tmdev)
{
return tmdev->ops->calibrate(tmdev);
}
static int tsens_register_interrupts(struct tsens_device *tmdev)
{
if (tmdev->ops->interrupts_reg)
return tmdev->ops->interrupts_reg(tmdev);
return 0;
}
static const struct of_device_id tsens_table[] = {
{ .compatible = "qcom,msm8996-tsens",
.data = &data_tsens2xxx,
},
{ .compatible = "qcom,msm8953-tsens",
.data = &data_tsens2xxx,
},
{ .compatible = "qcom,msm8998-tsens",
.data = &data_tsens2xxx,
},
{ .compatible = "qcom,msmhamster-tsens",
.data = &data_tsens2xxx,
},
{ .compatible = "qcom,sdm660-tsens",
.data = &data_tsens23xx,
},
{ .compatible = "qcom,sdm630-tsens",
.data = &data_tsens23xx,
},
{ .compatible = "qcom,sm6150-tsens",
.data = &data_tsens23xx,
},
{ .compatible = "qcom,sdm845-tsens",
.data = &data_tsens24xx,
},
{ .compatible = "qcom,tsens24xx",
.data = &data_tsens24xx,
},
{ .compatible = "qcom,tsens26xx",
.data = &data_tsens26xx,
},
{ .compatible = "qcom,msm8937-tsens",
.data = &data_tsens14xx,
},
{ .compatible = "qcom,qcs405-tsens",
.data = &data_tsens14xx_405,
},
{}
};
MODULE_DEVICE_TABLE(of, tsens_table);
static struct thermal_zone_of_device_ops tsens_tm_thermal_zone_ops = {
.get_temp = tsens_get_temp,
.set_trips = tsens_set_trip_temp,
};
static struct thermal_zone_of_device_ops tsens_tm_min_thermal_zone_ops = {
.get_temp = tsens_get_min_temp,
};
static int get_device_tree_data(struct platform_device *pdev,
struct tsens_device *tmdev)
{
struct device_node *of_node = pdev->dev.of_node;
const struct of_device_id *id;
const struct tsens_data *data;
int rc = 0;
struct resource *res_tsens_mem;
u32 min_temp_id;
if (!of_match_node(tsens_table, of_node)) {
pr_err("Need to read SoC specific fuse map\n");
return -ENODEV;
}
id = of_match_node(tsens_table, of_node);
if (id == NULL) {
pr_err("can not find tsens_table of_node\n");
return -ENODEV;
}
data = id->data;
tmdev->ops = data->ops;
tmdev->ctrl_data = data;
tmdev->pdev = pdev;
if (!tmdev->ops || !tmdev->ops->hw_init || !tmdev->ops->get_temp) {
pr_err("Invalid ops\n");
return -EINVAL;
}
/* TSENS register region */
res_tsens_mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "tsens_srot_physical");
if (!res_tsens_mem) {
pr_err("Could not get tsens physical address resource\n");
return -EINVAL;
}
tmdev->tsens_srot_addr = devm_ioremap_resource(&pdev->dev,
res_tsens_mem);
if (IS_ERR(tmdev->tsens_srot_addr)) {
dev_err(&pdev->dev, "Failed to IO map TSENS registers.\n");
return PTR_ERR(tmdev->tsens_srot_addr);
}
/* TSENS TM register region */
res_tsens_mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "tsens_tm_physical");
if (!res_tsens_mem) {
pr_err("Could not get tsens physical address resource\n");
return -EINVAL;
}
tmdev->tsens_tm_addr = devm_ioremap_resource(&pdev->dev,
res_tsens_mem);
if (IS_ERR(tmdev->tsens_tm_addr)) {
dev_err(&pdev->dev, "Failed to IO map TSENS TM registers.\n");
return PTR_ERR(tmdev->tsens_tm_addr);
}
tmdev->phys_addr_tm = res_tsens_mem->start;
/* TSENS eeprom register region */
res_tsens_mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "tsens_eeprom_physical");
if (!res_tsens_mem) {
pr_debug("Could not get tsens physical address resource\n");
} else {
tmdev->tsens_calib_addr = devm_ioremap_resource(&pdev->dev,
res_tsens_mem);
if (IS_ERR(tmdev->tsens_calib_addr)) {
dev_err(&pdev->dev, "Failed to IO map TSENS EEPROM registers.\n");
rc = PTR_ERR(tmdev->tsens_calib_addr);
} else {
rc = tsens_calib(tmdev);
if (rc) {
pr_err("Error initializing TSENS controller\n");
return rc;
}
}
}
if (!of_property_read_u32(of_node, "0C-sensor-num", &min_temp_id))
tmdev->min_temp_sensor_id = (int)min_temp_id;
else
tmdev->min_temp_sensor_id = MIN_TEMP_DEF_OFFSET;
tmdev->tsens_reinit_wa =
of_property_read_bool(of_node, "tsens-reinit-wa");
return rc;
}
static int tsens_thermal_zone_register(struct tsens_device *tmdev)
{
int i = 0, sensor_missing = 0;
for (i = 0; i < TSENS_MAX_SENSORS; i++) {
tmdev->sensor[i].tmdev = tmdev;
tmdev->sensor[i].hw_id = i;
if (tmdev->ops->sensor_en(tmdev, i)) {
tmdev->sensor[i].tzd =
devm_thermal_zone_of_sensor_register(
&tmdev->pdev->dev, i,
&tmdev->sensor[i], &tsens_tm_thermal_zone_ops);
if (IS_ERR(tmdev->sensor[i].tzd)) {
pr_debug("Error registering sensor:%d\n", i);
sensor_missing++;
continue;
}
} else {
pr_debug("Sensor not enabled:%d\n", i);
}
}
if (sensor_missing == TSENS_MAX_SENSORS) {
pr_err("No TSENS sensors to register?\n");
return -ENODEV;
}
if (tmdev->min_temp_sensor_id != MIN_TEMP_DEF_OFFSET) {
tmdev->min_temp.tmdev = tmdev;
tmdev->min_temp.hw_id = tmdev->min_temp_sensor_id;
tmdev->min_temp.tzd =
devm_thermal_zone_of_sensor_register(
&tmdev->pdev->dev, tmdev->min_temp_sensor_id,
&tmdev->min_temp, &tsens_tm_min_thermal_zone_ops);
if (IS_ERR(tmdev->min_temp.tzd))
pr_err("Error registering min temp sensor\n");
}
return 0;
}
static int tsens_tm_remove(struct platform_device *pdev)
{
platform_set_drvdata(pdev, NULL);
return 0;
}
static void tsens_therm_fwk_notify(struct work_struct *work)
{
int i, rc, temp;
struct tsens_device *tmdev =
container_of(work, struct tsens_device, therm_fwk_notify);
TSENS_DBG(tmdev, "Controller %pK\n", &tmdev->phys_addr_tm);
for (i = 0; i < TSENS_MAX_SENSORS; i++) {
if (tmdev->ops->sensor_en(tmdev, i)) {
rc = tsens_get_temp(&tmdev->sensor[i], &temp);
if (rc) {
pr_err("%s: Error:%d reading temp sensor:%d\n",
__func__, rc, i);
continue;
}
TSENS_DBG(tmdev, "Calling trip_temp for sensor %d\n",
i);
of_thermal_handle_trip_temp(tmdev->dev,
tmdev->sensor[i].tzd, temp);
}
}
if (tmdev->min_temp_sensor_id != MIN_TEMP_DEF_OFFSET) {
rc = tsens_get_temp(&tmdev->min_temp, &temp);
if (rc) {
pr_err("%s: Error:%d reading temp sensor:%d\n",
__func__, rc, i);
return;
}
TSENS_DBG(tmdev, "Calling trip_temp for sensor %d\n", i);
of_thermal_handle_trip_temp(tmdev->dev,
tmdev->min_temp.tzd, temp);
}
}
static int tsens_tm_probe(struct platform_device *pdev)
{
struct tsens_device *tmdev = NULL;
int rc;
char tsens_name[40];
if (!(pdev->dev.of_node))
return -ENODEV;
tmdev = devm_kzalloc(&pdev->dev,
sizeof(struct tsens_device) +
TSENS_MAX_SENSORS *
sizeof(struct tsens_sensor),
GFP_KERNEL);
if (tmdev == NULL)
return -ENOMEM;
rc = get_device_tree_data(pdev, tmdev);
if (rc) {
pr_err("Error reading TSENS DT\n");
return rc;
}
rc = tsens_init(tmdev);
if (rc) {
pr_err("Error initializing TSENS controller\n");
return rc;
}
snprintf(tsens_name, sizeof(tsens_name), "tsens_wq_%pa",
&tmdev->phys_addr_tm);
tmdev->tsens_reinit_work = alloc_workqueue(tsens_name,
WQ_HIGHPRI, 0);
if (!tmdev->tsens_reinit_work) {
rc = -ENOMEM;
return rc;
}
INIT_WORK(&tmdev->therm_fwk_notify, tsens_therm_fwk_notify);
rc = tsens_thermal_zone_register(tmdev);
if (rc) {
pr_err("Error registering the thermal zone\n");
return rc;
}
rc = tsens_register_interrupts(tmdev);
if (rc < 0) {
pr_err("TSENS interrupt register failed:%d\n", rc);
return rc;
}
snprintf(tsens_name, sizeof(tsens_name), "tsens_%pa_0",
&tmdev->phys_addr_tm);
tmdev->ipc_log0 = ipc_log_context_create(IPC_LOGPAGES,
tsens_name, 0);
if (!tmdev->ipc_log0)
pr_err("%s : unable to create IPC Logging 0 for tsens %pa\n",
__func__, &tmdev->phys_addr_tm);
snprintf(tsens_name, sizeof(tsens_name), "tsens_%pa_1",
&tmdev->phys_addr_tm);
tmdev->ipc_log1 = ipc_log_context_create(IPC_LOGPAGES,
tsens_name, 0);
if (!tmdev->ipc_log1)
pr_err("%s : unable to create IPC Logging 1 for tsens %pa\n",
__func__, &tmdev->phys_addr_tm);
snprintf(tsens_name, sizeof(tsens_name), "tsens_%pa_2",
&tmdev->phys_addr_tm);
tmdev->ipc_log2 = ipc_log_context_create(IPC_LOGPAGES,
tsens_name, 0);
if (!tmdev->ipc_log2)
pr_err("%s : unable to create IPC Logging 2 for tsens %pa\n",
__func__, &tmdev->phys_addr_tm);
list_add_tail(&tmdev->list, &tsens_device_list);
platform_set_drvdata(pdev, tmdev);
return rc;
}
static struct platform_driver tsens_tm_driver = {
.probe = tsens_tm_probe,
.remove = tsens_tm_remove,
.driver = {
.name = "msm-tsens",
.of_match_table = tsens_table,
},
};
static int __init tsens_tm_init_driver(void)
{
return platform_driver_register(&tsens_tm_driver);
}
subsys_initcall(tsens_tm_init_driver);
static void __exit tsens_tm_deinit(void)
{
platform_driver_unregister(&tsens_tm_driver);
}
module_exit(tsens_tm_deinit);
MODULE_ALIAS("platform:" TSENS_DRIVER_NAME);
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,298 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include "tsens.h"
/* eeprom layout data for 8937 */
#define BASE0_MASK_8937 0x000000ff
#define BASE1_MASK_8937 0xff000000
#define BASE1_SHIFT_8937 24
#define S0_P1_MASK_8937 0x000001f8
#define S1_P1_MASK_8937 0x001f8000
#define S2_P1_MASK_0_4_8937 0xf8000000
#define S2_P1_MASK_5_8937 0x00000001
#define S3_P1_MASK_8937 0x00001f80
#define S4_P1_MASK_8937 0x01f80000
#define S5_P1_MASK_8937 0x00003f00
#define S6_P1_MASK_8937 0x03f00000
#define S7_P1_MASK_8937 0x0000003f
#define S8_P1_MASK_8937 0x0003f000
#define S9_P1_MASK_8937 0x0000003f
#define S10_P1_MASK_8937 0x0003f000
#define S0_P2_MASK_8937 0x00007e00
#define S1_P2_MASK_8937 0x07e00000
#define S2_P2_MASK_8937 0x0000007e
#define S3_P2_MASK_8937 0x0007e000
#define S4_P2_MASK_8937 0x7e000000
#define S5_P2_MASK_8937 0x000fc000
#define S6_P2_MASK_8937 0xfc000000
#define S7_P2_MASK_8937 0x00000fc0
#define S8_P2_MASK_8937 0x00fc0000
#define S9_P2_MASK_8937 0x00000fc0
#define S10_P2_MASK_8937 0x00fc0000
#define S0_P1_SHIFT_8937 3
#define S1_P1_SHIFT_8937 15
#define S2_P1_SHIFT_0_4_8937 27
#define S2_P1_SHIFT_5_8937 5
#define S3_P1_SHIFT_8937 7
#define S4_P1_SHIFT_8937 19
#define S5_P1_SHIFT_8937 8
#define S6_P1_SHIFT_8937 20
#define S8_P1_SHIFT_8937 12
#define S10_P1_SHIFT_8937 12
#define S0_P2_SHIFT_8937 9
#define S1_P2_SHIFT_8937 21
#define S2_P2_SHIFT_8937 1
#define S3_P2_SHIFT_8937 13
#define S4_P2_SHIFT_8937 25
#define S5_P2_SHIFT_8937 14
#define S6_P2_SHIFT_8937 26
#define S7_P2_SHIFT_8937 6
#define S8_P2_SHIFT_8937 18
#define S9_P2_SHIFT_8937 6
#define S10_P2_SHIFT_8937 18
#define CAL_SEL_MASK_8937 0x00000007
/* eeprom layout data for qcs405 */
#define BASE0_MASK_405 0x000007F8
#define BASE1_MASK_405 0x0007F800
#define BASE0_SHIFT_405 0x3
#define BASE1_SHIFT_405 0xB
#define S0_P1_MASK_405 0x0000003F
#define S1_P1_MASK_405 0x0003F000
#define S2_P1_MASK_405 0x3F000000
#define S3_P1_MASK_405 0x000003F0
#define S4_P1_MASK_405 0x003F0000
#define S5_P1_MASK_405 0x0000003F
#define S6_P1_MASK_405 0x0003F000
#define S7_P1_MASK_405 0x3F000000
#define S8_P1_MASK_405 0x000003F0
#define S9_P1_MASK_405 0x003F0000
#define S0_P2_MASK_405 0x00000FC0
#define S1_P2_MASK_405 0x00FC0000
#define S2_P2_MASK_0_1_405 0xC0000000
#define S2_P2_MASK_2_5_405 0x0000000F
#define S3_P2_MASK_405 0x0000FC00
#define S4_P2_MASK_405 0x0FC00000
#define S5_P2_MASK_405 0x00000FC0
#define S6_P2_MASK_405 0x00FC0000
#define S7_P2_MASK_0_1_405 0xC0000000
#define S7_P2_MASK_2_5_405 0x0000000F
#define S8_P2_MASK_405 0x0000FC00
#define S9_P2_MASK_405 0x0FC00000
#define S0_P1_SHIFT_405 0x0
#define S1_P1_SHIFT_405 0xC
#define S2_P1_SHIFT_405 0x18
#define S3_P1_SHIFT_405 0x4
#define S4_P1_SHIFT_405 0x10
#define S5_P1_SHIFT_405 0x0
#define S6_P1_SHIFT_405 0xC
#define S7_P1_SHIFT_405 0x18
#define S8_P1_SHIFT_405 0x4
#define S9_P1_SHIFT_405 0x10
#define S0_P2_SHIFT_405 0x6
#define S1_P2_SHIFT_405 0x12
#define S2_P2_SHIFT_0_1_405 0x1E
#define S2_P2_SHIFT_2_5_405 0x0
#define S3_P2_SHIFT_405 0xA
#define S4_P2_SHIFT_405 0x16
#define S5_P2_SHIFT_405 0x6
#define S6_P2_SHIFT_405 0x12
#define S7_P2_SHIFT_0_1_405 0x1E
#define S7_P2_SHIFT_2_5_405 0x0
#define S8_P2_SHIFT_405 0xA
#define S9_P2_SHIFT_405 0x16
#define CAL_SEL_MASK_405 0x7
#define CAL_DEGC_PT1 30
#define CAL_DEGC_PT2 120
/*
* Use this function on devices where slope and offset calculations
* depend on calibration data read from qfprom. On others the slope
* and offset values are derived from tz->tzp->slope and tz->tzp->offset
* resp.
*/
static void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
u32 *p2, u32 mode)
{
int i;
int num, den;
for (i = 0; i < tmdev->ctrl_data->num_sensors; i++) {
pr_debug(
"sensor%d - data_point1:%#x data_point2:%#x\n",
i, p1[i], p2[i]);
tmdev->sensor[i].slope = SLOPE_DEFAULT;
if (mode == TWO_PT_CALIB) {
/*
* slope (m) = adc_code2 - adc_code1 (y2 - y1)/
* temp_120_degc - temp_30_degc (x2 - x1)
*/
num = p2[i] - p1[i];
num *= SLOPE_FACTOR;
den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
tmdev->sensor[i].slope = num / den;
}
tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
(CAL_DEGC_PT1 *
tmdev->sensor[i].slope);
pr_debug("offset:%d\n", tmdev->sensor[i].offset);
}
}
int calibrate_8937(struct tsens_device *tmdev)
{
int base0 = 0, base1 = 0, i;
u32 p1[TSENS_NUM_SENSORS_8937], p2[TSENS_NUM_SENSORS_8937];
int mode = 0, tmp = 0;
u32 qfprom_cdata[5] = { 0, 0, 0, 0, 0 };
qfprom_cdata[0] = readl_relaxed(tmdev->tsens_calib_addr + 0x1D8);
qfprom_cdata[1] = readl_relaxed(tmdev->tsens_calib_addr + 0x1DC);
qfprom_cdata[2] = readl_relaxed(tmdev->tsens_calib_addr + 0x210);
qfprom_cdata[3] = readl_relaxed(tmdev->tsens_calib_addr + 0x214);
qfprom_cdata[4] = readl_relaxed(tmdev->tsens_calib_addr + 0x230);
mode = (qfprom_cdata[2] & CAL_SEL_MASK_8937);
pr_debug("calibration mode is %d\n", mode);
switch (mode) {
case TWO_PT_CALIB:
base1 = (qfprom_cdata[1] & BASE1_MASK_8937) >> BASE1_SHIFT_8937;
p2[0] = (qfprom_cdata[2] & S0_P2_MASK_8937) >> S0_P2_SHIFT_8937;
p2[1] = (qfprom_cdata[2] & S1_P2_MASK_8937) >> S1_P2_SHIFT_8937;
p2[2] = (qfprom_cdata[3] & S2_P2_MASK_8937) >> S2_P2_SHIFT_8937;
p2[3] = (qfprom_cdata[3] & S3_P2_MASK_8937) >> S3_P2_SHIFT_8937;
p2[4] = (qfprom_cdata[3] & S4_P2_MASK_8937) >> S4_P2_SHIFT_8937;
p2[5] = (qfprom_cdata[0] & S5_P2_MASK_8937) >> S5_P2_SHIFT_8937;
p2[6] = (qfprom_cdata[0] & S6_P2_MASK_8937) >> S6_P2_SHIFT_8937;
p2[7] = (qfprom_cdata[1] & S7_P2_MASK_8937) >> S7_P2_SHIFT_8937;
p2[8] = (qfprom_cdata[1] & S8_P2_MASK_8937) >> S8_P2_SHIFT_8937;
p2[9] = (qfprom_cdata[4] & S9_P2_MASK_8937) >> S9_P2_SHIFT_8937;
p2[10] = ((qfprom_cdata[4] & S10_P2_MASK_8937)
>> S10_P2_SHIFT_8937);
for (i = 0; i < TSENS_NUM_SENSORS_8937; i++)
p2[i] = ((base1 + p2[i]) << 2);
/* Fall through */
case ONE_PT_CALIB2:
base0 = (qfprom_cdata[0] & BASE0_MASK_8937);
p1[0] = (qfprom_cdata[2] & S0_P1_MASK_8937) >> S0_P1_SHIFT_8937;
p1[1] = (qfprom_cdata[2] & S1_P1_MASK_8937) >> S1_P1_SHIFT_8937;
p1[2] = ((qfprom_cdata[2] & S2_P1_MASK_0_4_8937)
>> S2_P1_SHIFT_0_4_8937);
tmp = ((qfprom_cdata[3] & S2_P1_MASK_5_8937)
<< S2_P1_SHIFT_5_8937);
p1[2] |= tmp;
p1[3] = (qfprom_cdata[3] & S3_P1_MASK_8937) >> S3_P1_SHIFT_8937;
p1[4] = (qfprom_cdata[3] & S4_P1_MASK_8937) >> S4_P1_SHIFT_8937;
p1[5] = (qfprom_cdata[0] & S5_P1_MASK_8937) >> S5_P1_SHIFT_8937;
p1[6] = (qfprom_cdata[0] & S6_P1_MASK_8937) >> S6_P1_SHIFT_8937;
p1[7] = (qfprom_cdata[1] & S7_P1_MASK_8937);
p1[8] = (qfprom_cdata[1] & S8_P1_MASK_8937) >> S8_P1_SHIFT_8937;
p1[9] = (qfprom_cdata[4] & S9_P1_MASK_8937);
p1[10] = ((qfprom_cdata[4] & S10_P1_MASK_8937)
>> S10_P1_SHIFT_8937);
for (i = 0; i < TSENS_NUM_SENSORS_8937; i++)
p1[i] = (((base0)+p1[i]) << 2);
break;
default:
for (i = 0; i < TSENS_NUM_SENSORS_8937; i++) {
p1[i] = 500;
p2[i] = 780;
}
break;
}
compute_intercept_slope(tmdev, p1, p2, mode);
return 0;
}
int calibrate_405(struct tsens_device *tmdev)
{
int base0 = 0, base1 = 0, i;
u32 p1[TSENS_NUM_SENSORS_405], p2[TSENS_NUM_SENSORS_405];
int mode = 0, tmp = 0;
u32 qfprom_cdata[5] = { 0, 0, 0, 0, 0 };
qfprom_cdata[0] = readl_relaxed(tmdev->tsens_calib_addr + 0x1F8);
qfprom_cdata[1] = readl_relaxed(tmdev->tsens_calib_addr + 0x1FC);
qfprom_cdata[2] = readl_relaxed(tmdev->tsens_calib_addr + 0x200);
qfprom_cdata[3] = readl_relaxed(tmdev->tsens_calib_addr + 0x204);
qfprom_cdata[4] = readl_relaxed(tmdev->tsens_calib_addr + 0x208);
mode = (qfprom_cdata[4] & CAL_SEL_MASK_405);
pr_debug("calibration mode is %d\n", mode);
switch (mode) {
case TWO_PT_CALIB:
base1 = (qfprom_cdata[4] & BASE1_MASK_405) >> BASE1_SHIFT_405;
p2[0] = (qfprom_cdata[0] & S0_P2_MASK_405) >> S0_P2_SHIFT_405;
p2[1] = (qfprom_cdata[0] & S1_P2_MASK_405) >> S1_P2_SHIFT_405;
tmp = ((qfprom_cdata[0] & S2_P2_MASK_0_1_405)
>> S2_P2_SHIFT_0_1_405);
p2[2] = ((qfprom_cdata[1] & S2_P2_MASK_2_5_405)
>> S2_P2_SHIFT_2_5_405) | tmp;
p2[3] = (qfprom_cdata[1] & S3_P2_MASK_405) >> S3_P2_SHIFT_405;
p2[4] = (qfprom_cdata[1] & S4_P2_MASK_405) >> S4_P2_SHIFT_405;
p2[5] = (qfprom_cdata[2] & S5_P2_MASK_405) >> S5_P2_SHIFT_405;
p2[6] = (qfprom_cdata[2] & S6_P2_MASK_405) >> S6_P2_SHIFT_405;
tmp = ((qfprom_cdata[2] & S7_P2_MASK_0_1_405)
>> S7_P2_SHIFT_0_1_405);
p2[7] = ((qfprom_cdata[3] & S7_P2_MASK_2_5_405)
>> S7_P2_SHIFT_2_5_405) | tmp;
p2[8] = (qfprom_cdata[3] & S8_P2_MASK_405) >> S8_P2_SHIFT_405;
p2[9] = (qfprom_cdata[3] & S9_P2_MASK_405) >> S9_P2_SHIFT_405;
for (i = 0; i < TSENS_NUM_SENSORS_405; i++)
p2[i] = ((base1 + p2[i]) << 2);
/* Fall through */
case ONE_PT_CALIB2:
base0 = (qfprom_cdata[4] & BASE0_MASK_405) >> BASE0_SHIFT_405;
p1[0] = (qfprom_cdata[0] & S0_P1_MASK_405) >> S0_P1_SHIFT_405;
p1[1] = (qfprom_cdata[0] & S1_P1_MASK_405) >> S1_P1_SHIFT_405;
p1[2] = (qfprom_cdata[0] & S2_P1_MASK_405) >> S2_P1_SHIFT_405;
p1[3] = (qfprom_cdata[1] & S3_P1_MASK_405) >> S3_P1_SHIFT_405;
p1[4] = (qfprom_cdata[1] & S4_P1_MASK_405) >> S4_P1_SHIFT_405;
p1[5] = (qfprom_cdata[2] & S5_P1_MASK_405) >> S5_P1_SHIFT_405;
p1[6] = (qfprom_cdata[2] & S6_P1_MASK_405) >> S6_P1_SHIFT_405;
p1[7] = (qfprom_cdata[2] & S7_P1_MASK_405) >> S7_P1_SHIFT_405;
p1[8] = (qfprom_cdata[3] & S8_P1_MASK_405) >> S8_P1_SHIFT_405;
p1[9] = (qfprom_cdata[3] & S9_P1_MASK_405) >> S9_P1_SHIFT_405;
for (i = 0; i < TSENS_NUM_SENSORS_405; i++)
p1[i] = (((base0)+p1[i]) << 2);
break;
default:
for (i = 0; i < TSENS_NUM_SENSORS_405; i++) {
p1[i] = 500;
p2[i] = 780;
}
break;
}
compute_intercept_slope(tmdev, p1, p2, mode);
return 0;
}
MODULE_LICENSE("GPL v2");

419
drivers/thermal/tsens-dbg.c Normal file
View file

@ -0,0 +1,419 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <asm/arch_timer.h>
#include <linux/sched/clock.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include "tsens.h"
#include "tsens-mtc.h"
/* debug defines */
#define TSENS_DBG_BUS_ID_0 0
#define TSENS_DBG_BUS_ID_1 1
#define TSENS_DBG_BUS_ID_2 2
#define TSENS_DBG_BUS_ID_15 15
#define TSENS_DEBUG_LOOP_COUNT_ID_0 2
#define TSENS_DEBUG_LOOP_COUNT 5
#define TSENS_DEBUG_STATUS_REG_START 10
#define TSENS_DEBUG_OFFSET_RANGE 16
#define TSENS_DEBUG_OFFSET_WORD1 0x4
#define TSENS_DEBUG_OFFSET_WORD2 0x8
#define TSENS_DEBUG_OFFSET_WORD3 0xc
#define TSENS_DEBUG_OFFSET_ROW 0x10
#define TSENS_DEBUG_DECIDEGC -950
#define TSENS_DEBUG_CYCLE_MS 64
#define TSENS_DEBUG_POLL_MS 200
#define TSENS_DEBUG_BUS_ID2_MIN_CYCLE 50
#define TSENS_DEBUG_BUS_ID2_MAX_CYCLE 51
#define TSENS_DEBUG_ID_MASK_1_4 0xffffffe1
#define DEBUG_SIZE 10
#define TSENS_DEBUG_CONTROL(n) ((n) + 0x130)
#define TSENS_DEBUG_DATA(n) ((n) + 0x134)
struct tsens_dbg_func {
int (*dbg_func)(struct tsens_device *data, u32 id, u32 dbg_type,
int *temp);
};
static ssize_t
zonemask_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct tsens_device *tmdev = NULL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
return snprintf(buf, PAGE_SIZE,
"Zone =%d th1=%d th2=%d\n", tmdev->mtcsys.zone_mtc,
tmdev->mtcsys.th1, tmdev->mtcsys.th2);
}
static ssize_t
zonemask_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
struct tsens_device *tmdev = NULL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
ret = sscanf(buf, "%d %d %d", &tmdev->mtcsys.zone_mtc,
&tmdev->mtcsys.th1, &tmdev->mtcsys.th2);
if (ret != TSENS_ZONEMASK_PARAMS) {
pr_err("Invalid command line arguments\n");
count = -EINVAL;
} else {
pr_debug("store zone_mtc=%d th1=%d th2=%d\n",
tmdev->mtcsys.zone_mtc,
tmdev->mtcsys.th1, tmdev->mtcsys.th2);
ret = tsens_set_mtc_zone_sw_mask(tmdev->mtcsys.zone_mtc,
tmdev->mtcsys.th1, tmdev->mtcsys.th2);
if (ret < 0) {
pr_err("Invalid command line arguments\n");
count = -EINVAL;
}
}
return count;
}
static ssize_t
zonelog_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret, zlog[TSENS_MTC_ZONE_LOG_SIZE];
struct tsens_device *tmdev = NULL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
ret = tsens_get_mtc_zone_log(tmdev->mtcsys.zone_log, zlog);
if (ret < 0) {
pr_err("Invalid command line arguments\n");
return -EINVAL;
}
return snprintf(buf, PAGE_SIZE,
"Log[0]=%d\nLog[1]=%d\nLog[2]=%d\nLog[3]=%d\nLog[4]=%d\nLog[5]=%d\n",
zlog[0], zlog[1], zlog[2], zlog[3], zlog[4], zlog[5]);
}
static ssize_t
zonelog_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
struct tsens_device *tmdev = NULL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
ret = kstrtou32(buf, 0, &tmdev->mtcsys.zone_log);
if (ret < 0) {
pr_err("Invalid command line arguments\n");
return -EINVAL;
}
return count;
}
static ssize_t
zonehist_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret, zhist[TSENS_MTC_ZONE_HISTORY_SIZE];
struct tsens_device *tmdev = NULL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
ret = tsens_get_mtc_zone_history(tmdev->mtcsys.zone_hist, zhist);
if (ret < 0) {
pr_err("Invalid command line arguments\n");
return -EINVAL;
}
return snprintf(buf, PAGE_SIZE,
"Cool = %d\nYellow = %d\nRed = %d\n",
zhist[0], zhist[1], zhist[2]);
}
static ssize_t
zonehist_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
struct tsens_device *tmdev = NULL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
ret = kstrtou32(buf, 0, &tmdev->mtcsys.zone_hist);
if (ret < 0) {
pr_err("Invalid command line arguments\n");
return -EINVAL;
}
return count;
}
static struct device_attribute tsens_mtc_dev_attr[] = {
__ATTR(zonemask, 0644, zonemask_show, zonemask_store),
__ATTR(zonelog, 0644, zonelog_show, zonelog_store),
__ATTR(zonehist, 0644, zonehist_show, zonehist_store),
};
static struct device_attribute tsens_mtc_dev_attr_V14[] = {
__ATTR(zonemask, 0644, zonemask_show, zonemask_store),
__ATTR(zonelog, 0644, zonelog_show, zonelog_store),
};
static int tsens_dbg_mtc_data(struct tsens_device *data,
u32 id, u32 dbg_type, int *val)
{
int result = 0, i;
struct tsens_device *tmdev = NULL;
struct device_attribute *attr_ptr = NULL;
u32 ver_major, ver_minor, num_elem;
tmdev = data;
ver_major = tmdev->ctrl_data->ver_major;
ver_minor = tmdev->ctrl_data->ver_minor;
if (ver_major == 1 && ver_minor == 4) {
attr_ptr = tsens_mtc_dev_attr_V14;
num_elem = ARRAY_SIZE(tsens_mtc_dev_attr_V14);
} else {
attr_ptr = tsens_mtc_dev_attr;
num_elem = ARRAY_SIZE(tsens_mtc_dev_attr);
}
for (i = 0; i < num_elem; i++) {
result = device_create_file(&tmdev->pdev->dev, &attr_ptr[i]);
if (result < 0)
goto error;
}
return result;
error:
for (i--; i >= 0; i--)
device_remove_file(&tmdev->pdev->dev, &attr_ptr[i]);
return result;
}
static int tsens_dbg_log_temp_reads(struct tsens_device *data, u32 id,
u32 dbg_type, int *temp)
{
struct tsens_sensor *sensor;
struct tsens_device *tmdev = NULL;
u32 idx = 0;
if (!data)
return -EINVAL;
pr_debug("%d %d\n", id, dbg_type);
tmdev = data;
sensor = &tmdev->sensor[id];
idx = tmdev->tsens_dbg.sensor_dbg_info[sensor->hw_id].idx;
tmdev->tsens_dbg.sensor_dbg_info[sensor->hw_id].temp[idx%10] = *temp;
tmdev->tsens_dbg.sensor_dbg_info[sensor->hw_id].time_stmp[idx%10] =
sched_clock();
idx++;
tmdev->tsens_dbg.sensor_dbg_info[sensor->hw_id].idx = idx;
TSENS_DBG(tmdev, "Sensor_id: %d temp: %d\n", id, *temp);
return 0;
}
static int tsens_dbg_log_interrupt_timestamp(struct tsens_device *data,
u32 id, u32 dbg_type, int *val)
{
struct tsens_device *tmdev = NULL;
u32 idx = 0;
if (!data)
return -EINVAL;
pr_debug("%d %d\n", id, dbg_type);
tmdev = data;
/* debug */
idx = tmdev->tsens_dbg.irq_idx;
tmdev->tsens_dbg.irq_time_stmp[idx%10] =
sched_clock();
tmdev->tsens_dbg.irq_idx++;
return 0;
}
static int tsens_dbg_log_bus_id_data(struct tsens_device *data,
u32 id, u32 dbg_type, int *val)
{
struct tsens_device *tmdev = NULL;
u32 loop = 0, i = 0;
uint32_t r1, r2, r3, r4, offset = 0;
unsigned int debug_dump;
unsigned int debug_id = 0, cntrl_id = 0;
void __iomem *srot_addr;
void __iomem *controller_id_addr;
void __iomem *debug_id_addr;
void __iomem *debug_data_addr;
if (!data)
return -EINVAL;
pr_debug("%d %d\n", id, dbg_type);
tmdev = data;
controller_id_addr = TSENS_CONTROLLER_ID(tmdev->tsens_tm_addr);
debug_id_addr = TSENS_DEBUG_CONTROL(tmdev->tsens_tm_addr);
debug_data_addr = TSENS_DEBUG_DATA(tmdev->tsens_tm_addr);
srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr);
cntrl_id = readl_relaxed(controller_id_addr);
TSENS_DUMP(tmdev, "TSENS Controller_id: 0x%x\n", cntrl_id);
loop = 0;
i = 0;
debug_id = readl_relaxed(debug_id_addr);
writel_relaxed((debug_id | (i << 1) | 1),
TSENS_DEBUG_CONTROL(tmdev->tsens_tm_addr));
while (loop < TSENS_DEBUG_LOOP_COUNT_ID_0) {
debug_dump = readl_relaxed(debug_data_addr);
r1 = readl_relaxed(debug_data_addr);
r2 = readl_relaxed(debug_data_addr);
r3 = readl_relaxed(debug_data_addr);
r4 = readl_relaxed(debug_data_addr);
TSENS_DUMP(tmdev,
"ctl:%d, bus-id:%d val:0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
cntrl_id, i, debug_dump, r1, r2, r3, r4);
loop++;
}
for (i = TSENS_DBG_BUS_ID_1; i <= TSENS_DBG_BUS_ID_15; i++) {
loop = 0;
debug_id = readl_relaxed(debug_id_addr);
debug_id = debug_id & TSENS_DEBUG_ID_MASK_1_4;
writel_relaxed((debug_id | (i << 1) | 1),
TSENS_DEBUG_CONTROL(tmdev->tsens_tm_addr));
while (loop < TSENS_DEBUG_LOOP_COUNT) {
debug_dump = readl_relaxed(debug_data_addr);
TSENS_DUMP(tmdev,
"cntrl:%d, bus-id:%d with value: 0x%x\n",
cntrl_id, i, debug_dump);
if (i == TSENS_DBG_BUS_ID_2)
usleep_range(
TSENS_DEBUG_BUS_ID2_MIN_CYCLE,
TSENS_DEBUG_BUS_ID2_MAX_CYCLE);
loop++;
}
}
TSENS_DUMP(tmdev, "Start of TSENS TM dump for ctr 0x%x\n",
cntrl_id);
for (i = 0; i < TSENS_DEBUG_OFFSET_RANGE; i++) {
r1 = readl_relaxed(controller_id_addr + offset);
r2 = readl_relaxed(controller_id_addr + (offset +
TSENS_DEBUG_OFFSET_WORD1));
r3 = readl_relaxed(controller_id_addr + (offset +
TSENS_DEBUG_OFFSET_WORD2));
r4 = readl_relaxed(controller_id_addr + (offset +
TSENS_DEBUG_OFFSET_WORD3));
TSENS_DUMP(tmdev,
"ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
cntrl_id, offset, r1, r2, r3, r4);
offset += TSENS_DEBUG_OFFSET_ROW;
}
offset = 0;
TSENS_DUMP(tmdev, "Start of TSENS SROT dump for ctr 0x%x\n",
cntrl_id);
for (i = 0; i < TSENS_DEBUG_OFFSET_RANGE; i++) {
r1 = readl_relaxed(srot_addr + offset);
r2 = readl_relaxed(srot_addr + (offset +
TSENS_DEBUG_OFFSET_WORD1));
r3 = readl_relaxed(srot_addr + (offset +
TSENS_DEBUG_OFFSET_WORD2));
r4 = readl_relaxed(srot_addr + (offset +
TSENS_DEBUG_OFFSET_WORD3));
TSENS_DUMP(tmdev,
"ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
cntrl_id, offset, r1, r2, r3, r4);
offset += TSENS_DEBUG_OFFSET_ROW;
}
loop = 0;
while (loop < TSENS_DEBUG_LOOP_COUNT) {
offset = TSENS_DEBUG_OFFSET_ROW *
TSENS_DEBUG_STATUS_REG_START;
TSENS_DUMP(tmdev, "Start of TSENS TM dump %d\n",
loop);
/* Limited dump of the registers for the temperature */
for (i = 0; i < TSENS_DEBUG_LOOP_COUNT; i++) {
r1 = readl_relaxed(controller_id_addr + offset);
r2 = readl_relaxed(controller_id_addr +
(offset + TSENS_DEBUG_OFFSET_WORD1));
r3 = readl_relaxed(controller_id_addr +
(offset + TSENS_DEBUG_OFFSET_WORD2));
r4 = readl_relaxed(controller_id_addr +
(offset + TSENS_DEBUG_OFFSET_WORD3));
TSENS_DUMP(tmdev,
"ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
cntrl_id, offset, r1, r2, r3, r4);
offset += TSENS_DEBUG_OFFSET_ROW;
}
loop++;
}
return 0;
}
static struct tsens_dbg_func dbg_arr[] = {
[TSENS_DBG_LOG_TEMP_READS] = {tsens_dbg_log_temp_reads},
[TSENS_DBG_LOG_INTERRUPT_TIMESTAMP] = {
tsens_dbg_log_interrupt_timestamp},
[TSENS_DBG_LOG_BUS_ID_DATA] = {tsens_dbg_log_bus_id_data},
[TSENS_DBG_MTC_DATA] = {tsens_dbg_mtc_data},
};
int tsens2xxx_dbg(struct tsens_device *data, u32 id, u32 dbg_type, int *val)
{
if (dbg_type >= TSENS_DBG_LOG_MAX)
return -EINVAL;
dbg_arr[dbg_type].dbg_func(data, id, dbg_type, val);
return 0;
}
MODULE_LICENSE("GPL v2");

204
drivers/thermal/tsens-mtc.c Normal file
View file

@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include "tsens.h"
#include "tsens-mtc.h"
struct tsens_device *tsens_controller_is_present(void)
{
struct tsens_device *tmdev_chip = NULL;
if (list_empty(&tsens_device_list)) {
pr_err("%s: TSENS controller not available\n", __func__);
return tmdev_chip;
}
list_for_each_entry(tmdev_chip, &tsens_device_list, list)
return tmdev_chip;
return tmdev_chip;
}
EXPORT_SYMBOL(tsens_controller_is_present);
static int tsens_mtc_reset_history_counter(unsigned int zone)
{
unsigned int reg_cntl, is_valid;
void __iomem *sensor_addr;
struct tsens_device *tmdev = NULL;
if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
return -EINVAL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR(tmdev->tsens_tm_addr);
reg_cntl = readl_relaxed((sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
is_valid = (reg_cntl & TSENS_RESET_HISTORY_MASK)
>> TSENS_RESET_HISTORY_SHIFT;
if (!is_valid) {
/*Enable the bit to reset counter*/
writel_relaxed(reg_cntl | (1 << TSENS_RESET_HISTORY_SHIFT),
(sensor_addr + (zone * TSENS_SN_ADDR_OFFSET)));
reg_cntl = readl_relaxed((sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
pr_debug("tsens : zone =%d reg=%x\n", zone, reg_cntl);
}
/*Disable the bit to start counter*/
writel_relaxed(reg_cntl & ~(1 << TSENS_RESET_HISTORY_SHIFT),
(sensor_addr + (zone * TSENS_SN_ADDR_OFFSET)));
reg_cntl = readl_relaxed((sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
pr_debug("tsens : zone =%d reg=%x\n", zone, reg_cntl);
return 0;
}
EXPORT_SYMBOL(tsens_mtc_reset_history_counter);
int tsens_set_mtc_zone_sw_mask(unsigned int zone, unsigned int th1_enable,
unsigned int th2_enable)
{
unsigned int reg_cntl;
void __iomem *sensor_addr;
struct tsens_device *tmdev = NULL;
u32 ver_major, ver_minor;
if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
return -EINVAL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
ver_major = tmdev->ctrl_data->ver_major;
ver_minor = tmdev->ctrl_data->ver_minor;
if (ver_major == 1 && ver_minor == 4) {
sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR_V14
(tmdev->tsens_tm_addr);
} else {
sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR
(tmdev->tsens_tm_addr);
}
if (th1_enable && th2_enable)
writel_relaxed(TSENS_MTC_IN_EFFECT,
(sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
if (!th1_enable && !th2_enable)
writel_relaxed(TSENS_MTC_DISABLE,
(sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
if (th1_enable && !th2_enable)
writel_relaxed(TSENS_TH1_MTC_IN_EFFECT,
(sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
if (!th1_enable && th2_enable)
writel_relaxed(TSENS_TH2_MTC_IN_EFFECT,
(sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
reg_cntl = readl_relaxed((sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
pr_debug("tsens : zone =%d th1=%d th2=%d reg=%x\n",
zone, th1_enable, th2_enable, reg_cntl);
return 0;
}
EXPORT_SYMBOL(tsens_set_mtc_zone_sw_mask);
int tsens_get_mtc_zone_log(unsigned int zone, void *zone_log)
{
unsigned int i, reg_cntl, is_valid, log[TSENS_MTC_ZONE_LOG_SIZE];
int *zlog = (int *)zone_log;
void __iomem *sensor_addr;
struct tsens_device *tmdev = NULL;
u32 ver_major, ver_minor;
if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
return -EINVAL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
ver_major = tmdev->ctrl_data->ver_major;
ver_minor = tmdev->ctrl_data->ver_minor;
if (ver_major == 1 && ver_minor == 4)
sensor_addr = TSENS_TM_MTC_ZONE0_LOG_V14(tmdev->tsens_tm_addr);
else
sensor_addr = TSENS_TM_MTC_ZONE0_LOG(tmdev->tsens_tm_addr);
reg_cntl = readl_relaxed((sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
is_valid = (reg_cntl & TSENS_LOGS_VALID_MASK)
>> TSENS_LOGS_VALID_SHIFT;
if (is_valid) {
log[0] = (reg_cntl & TSENS_LOGS_LATEST_MASK);
log[1] = (reg_cntl & TSENS_LOGS_LOG1_MASK)
>> TSENS_LOGS_LOG1_SHIFT;
log[2] = (reg_cntl & TSENS_LOGS_LOG2_MASK)
>> TSENS_LOGS_LOG2_SHIFT;
log[3] = (reg_cntl & TSENS_LOGS_LOG3_MASK)
>> TSENS_LOGS_LOG3_SHIFT;
log[4] = (reg_cntl & TSENS_LOGS_LOG4_MASK)
>> TSENS_LOGS_LOG4_SHIFT;
log[5] = (reg_cntl & TSENS_LOGS_LOG5_MASK)
>> TSENS_LOGS_LOG5_SHIFT;
for (i = 0; i < (TSENS_MTC_ZONE_LOG_SIZE); i++) {
*(zlog+i) = log[i];
pr_debug("Log[%d]=%d\n", i, log[i]);
}
} else {
pr_debug("tsens: Valid bit disabled\n");
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(tsens_get_mtc_zone_log);
int tsens_get_mtc_zone_history(unsigned int zone, void *zone_hist)
{
unsigned int i, reg_cntl, hist[TSENS_MTC_ZONE_HISTORY_SIZE];
int *zhist = (int *)zone_hist;
void __iomem *sensor_addr;
struct tsens_device *tmdev = NULL;
if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
return -EINVAL;
tmdev = tsens_controller_is_present();
if (!tmdev) {
pr_err("No TSENS controller present\n");
return -EPROBE_DEFER;
}
sensor_addr = TSENS_TM_MTC_ZONE0_HISTORY(tmdev->tsens_tm_addr);
reg_cntl = readl_relaxed((sensor_addr +
(zone * TSENS_SN_ADDR_OFFSET)));
hist[0] = (reg_cntl & TSENS_PS_COOL_CMD_MASK);
hist[1] = (reg_cntl & TSENS_PS_YELLOW_CMD_MASK)
>> TSENS_PS_YELLOW_CMD_SHIFT;
hist[2] = (reg_cntl & TSENS_PS_RED_CMD_MASK)
>> TSENS_PS_RED_CMD_SHIFT;
for (i = 0; i < (TSENS_MTC_ZONE_HISTORY_SIZE); i++) {
*(zhist+i) = hist[i];
pr_debug("tsens : %d\n", hist[i]);
}
return 0;
}
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#define TSENS_NUM_MTC_ZONES_SUPPORT 3
#define TSENS_TM_MTC_ZONE0_SW_MASK_ADDR(n) ((n) + 0x140)
#define TSENS_TM_MTC_ZONE0_LOG(n) ((n) + 0x150)
#define TSENS_TM_MTC_ZONE0_HISTORY(n) ((n) + 0x160)
#define TSENS_TM_MTC_ZONE0_SW_MASK_ADDR_V14(n) ((n) + 0xC0)
#define TSENS_TM_MTC_ZONE0_LOG_V14(n) ((n) + 0xD0)
#define TSENS_SN_ADDR_OFFSET 0x4
#define TSENS_RESET_HISTORY_MASK 0x4
#define TSENS_ZONEMASK_PARAMS 3
#define TSENS_MTC_ZONE_LOG_SIZE 6
#define TSENS_MTC_ZONE_HISTORY_SIZE 3
#define TSENS_TH1_MTC_IN_EFFECT BIT(0)
#define TSENS_TH2_MTC_IN_EFFECT BIT(1)
#define TSENS_MTC_IN_EFFECT 0x3
#define TSENS_MTC_DISABLE 0x0
#define TSENS_LOGS_VALID_MASK 0x40000000
#define TSENS_LOGS_VALID_SHIFT 30
#define TSENS_LOGS_LATEST_MASK 0x0000001f
#define TSENS_LOGS_LOG1_MASK 0x000003e0
#define TSENS_LOGS_LOG2_MASK 0x00007c00
#define TSENS_LOGS_LOG3_MASK 0x000f8000
#define TSENS_LOGS_LOG4_MASK 0x01f00000
#define TSENS_LOGS_LOG5_MASK 0x3e000000
#define TSENS_LOGS_LOG1_SHIFT 5
#define TSENS_LOGS_LOG2_SHIFT 10
#define TSENS_LOGS_LOG3_SHIFT 15
#define TSENS_LOGS_LOG4_SHIFT 20
#define TSENS_LOGS_LOG5_SHIFT 25
#define TSENS_PS_RED_CMD_MASK 0x3ff00000
#define TSENS_PS_YELLOW_CMD_MASK 0x000ffc00
#define TSENS_PS_COOL_CMD_MASK 0x000003ff
#define TSENS_PS_YELLOW_CMD_SHIFT 0xa
#define TSENS_PS_RED_CMD_SHIFT 0x14
#define TSENS_RESET_HISTORY_SHIFT 2
#define TSENS_ZONEMASK_PARAMS 3
#define TSENS_MTC_ZONE_LOG_SIZE 6
#define TSENS_MTC_ZONE_HISTORY_SIZE 3
extern int tsens_get_mtc_zone_history(unsigned int zone, void *zone_hist);
extern struct tsens_device *tsens_controller_is_present(void);
extern int tsens_set_mtc_zone_sw_mask(unsigned int zone,
unsigned int th1_enable, unsigned int th2_enable);
extern int tsens_get_mtc_zone_log(unsigned int zone, void *zone_log);

238
drivers/thermal/tsens.h Normal file
View file

@ -0,0 +1,238 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#ifndef __QCOM_TSENS_H__
#define __QCOM_TSENS_H__
#include <linux/kernel.h>
#include <linux/thermal.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/ipc_logging.h>
#define DEBUG_SIZE 10
#define TSENS_MAX_SENSORS 16
#define TSENS_NUM_SENSORS_8937 11
#define TSENS_NUM_SENSORS_405 10
#define TSENS_CONTROLLER_ID(n) (n)
#define TSENS_CTRL_ADDR(n) (n)
#define TSENS_TM_SN_STATUS(n) ((n) + 0xa0)
#define ONE_PT_CALIB 0x1
#define ONE_PT_CALIB2 0x2
#define TWO_PT_CALIB 0x3
#define SLOPE_FACTOR 1000
#define SLOPE_DEFAULT 3200
#define IPC_LOGPAGES 10
#define MIN_TEMP_DEF_OFFSET 0xFF
#define TSENS_DRIVER_NAME "msm-tsens"
enum tsens_trip_type {
TSENS_TRIP_CONFIGURABLE_HI = 4,
TSENS_TRIP_CONFIGURABLE_LOW
};
enum tsens_dbg_type {
TSENS_DBG_POLL,
TSENS_DBG_LOG_TEMP_READS,
TSENS_DBG_LOG_INTERRUPT_TIMESTAMP,
TSENS_DBG_LOG_BUS_ID_DATA,
TSENS_DBG_MTC_DATA,
TSENS_DBG_LOG_MAX
};
#define tsens_sec_to_msec_value 1000
struct tsens_device;
#ifdef CONFIG_DEBUG_FS
#define TSENS_IPC(idx, dev, msg, args...) do { \
if (dev) { \
if ((idx == 0) && (dev)->ipc_log0) \
ipc_log_string((dev)->ipc_log0, \
"%s: " msg, __func__, args); \
else if ((idx == 1) && (dev)->ipc_log1) \
ipc_log_string((dev)->ipc_log1, \
"%s: " msg, __func__, args); \
else if ((idx == 2) && (dev)->ipc_log2) \
ipc_log_string((dev)->ipc_log2, \
"%s: " msg, __func__, args); \
else \
pr_debug("tsens: invalid logging index\n"); \
} \
} while (0)
#define TSENS_DUMP(dev, msg, args...) do { \
TSENS_IPC(2, dev, msg, args); \
pr_info(msg, ##args); \
} while (0)
#define TSENS_ERR(dev, msg, args...) do { \
pr_err(msg, ##args); \
TSENS_IPC(1, dev, msg, args); \
} while (0)
#define TSENS_INFO(dev, msg, args...) do { \
pr_info(msg, ##args); \
TSENS_IPC(1, dev, msg, args); \
} while (0)
#define TSENS_DBG(dev, msg, args...) do { \
pr_debug(msg, ##args); \
if (dev) { \
TSENS_IPC(0, dev, msg, args); \
} \
} while (0)
#define TSENS_DBG1(dev, msg, args...) do { \
pr_debug(msg, ##args); \
if (dev) { \
TSENS_IPC(1, dev, msg, args); \
} \
} while (0)
#else
#define TSENS_DBG1(x...) pr_debug(x)
#define TSENS_DBG(x...) pr_debug(x)
#define TSENS_INFO(x...) pr_info(x)
#define TSENS_ERR(x...) pr_err(x)
#define TSENS_DUMP(x...) pr_info(x)
#endif
#if IS_ENABLED(CONFIG_THERMAL_TSENS)
int tsens2xxx_dbg(struct tsens_device *data, u32 id, u32 dbg_type, int *temp);
#else
static inline int tsens2xxx_dbg(struct tsens_device *data, u32 id,
u32 dbg_type, int *temp)
{ return -ENXIO; }
#endif
struct tsens_dbg {
u32 idx;
unsigned long long time_stmp[DEBUG_SIZE];
unsigned long temp[DEBUG_SIZE];
};
struct tsens_dbg_context {
struct tsens_device *tmdev;
struct tsens_dbg sensor_dbg_info[TSENS_MAX_SENSORS];
int tsens_critical_wd_cnt;
u32 irq_idx;
unsigned long long irq_time_stmp[DEBUG_SIZE];
struct delayed_work tsens_critical_poll_test;
};
struct tsens_context {
enum thermal_device_mode high_th_state;
enum thermal_device_mode low_th_state;
enum thermal_device_mode crit_th_state;
int high_temp;
int low_temp;
int crit_temp;
int high_adc_code;
int low_adc_code;
};
struct tsens_sensor {
struct tsens_device *tmdev;
struct thermal_zone_device *tzd;
u32 hw_id;
u32 id;
const char *sensor_name;
struct tsens_context thr_state;
int offset;
int slope;
};
/**
* struct tsens_ops - operations as supported by the tsens device
* @init: Function to initialize the tsens device
* @get_temp: Function which returns the temp in millidegC
*/
struct tsens_ops {
int (*hw_init)(struct tsens_device *tmdev);
int (*get_temp)(struct tsens_sensor *tm_sensor, int *temp);
int (*set_trips)(struct tsens_sensor *tm_sensor, int low, int high);
int (*interrupts_reg)(struct tsens_device *tmdev);
int (*dbg)(struct tsens_device *tmdev, u32 id, u32 dbg_type,
int *temp);
int (*sensor_en)(struct tsens_device *tmdev, u32 sensor_id);
int (*calibrate)(struct tsens_device *tmdev);
};
struct tsens_irqs {
const char *name;
irqreturn_t (*handler)(int irq, void *data);
};
/**
* struct tsens_data - tsens instance specific data
* @num_sensors: Max number of sensors supported by platform
* @ops: operations the tsens instance supports
* @hw_ids: Subset of sensors ids supported by platform, if not the first n
*/
struct tsens_data {
const u32 num_sensors;
const struct tsens_ops *ops;
unsigned int *hw_ids;
u32 temp_factor;
bool cycle_monitor;
u32 cycle_compltn_monitor_mask;
bool wd_bark;
u32 wd_bark_mask;
bool mtc;
bool valid_status_check;
u32 ver_major;
u32 ver_minor;
};
struct tsens_mtc_sysfs {
u32 zone_log;
int zone_mtc;
int th1;
int th2;
u32 zone_hist;
};
struct tsens_device {
struct device *dev;
struct platform_device *pdev;
struct list_head list;
struct regmap *map;
struct regmap_field *status_field;
void __iomem *tsens_srot_addr;
void __iomem *tsens_tm_addr;
void __iomem *tsens_calib_addr;
const struct tsens_ops *ops;
void *ipc_log0;
void *ipc_log1;
void *ipc_log2;
phys_addr_t phys_addr_tm;
struct tsens_dbg_context tsens_dbg;
spinlock_t tsens_crit_lock;
spinlock_t tsens_upp_low_lock;
const struct tsens_data *ctrl_data;
struct tsens_mtc_sysfs mtcsys;
int trdy_fail_ctr;
struct tsens_sensor min_temp;
u8 min_temp_sensor_id;
struct workqueue_struct *tsens_reinit_work;
struct work_struct therm_fwk_notify;
bool tsens_reinit_wa;
struct tsens_sensor sensor[0];
};
extern const struct tsens_data data_tsens2xxx, data_tsens23xx, data_tsens24xx,
data_tsens26xx;
extern const struct tsens_data data_tsens14xx, data_tsens14xx_405;
extern struct list_head tsens_device_list;
extern int calibrate_8937(struct tsens_device *tmdev);
extern int calibrate_405(struct tsens_device *tmdev);
extern int tsens_2xxx_get_min_temp(
struct tsens_sensor *sensor, int *temp);
#endif /* __QCOM_TSENS_H__ */

523
drivers/thermal/tsens1xxx.c Normal file
View file

@ -0,0 +1,523 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/vmalloc.h>
#include "tsens.h"
#include "thermal_core.h"
#define TSENS_UPPER_LOWER_INTERRUPT_CTRL(n) (n)
#define TSENS_INTERRUPT_EN BIT(0)
#define TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(n) ((n) + 0x04)
#define TSENS_UPPER_STATUS_CLR BIT(21)
#define TSENS_LOWER_STATUS_CLR BIT(20)
#define TSENS_UPPER_THRESHOLD_MASK 0xffc00
#define TSENS_LOWER_THRESHOLD_MASK 0x3ff
#define TSENS_UPPER_THRESHOLD_SHIFT 10
#define TSENS_S0_STATUS_ADDR(n) ((n) + 0x30)
#define TSENS_SN_ADDR_OFFSET 0x4
#define TSENS_SN_STATUS_TEMP_MASK 0x3ff
#define TSENS_SN_STATUS_LOWER_STATUS BIT(11)
#define TSENS_SN_STATUS_UPPER_STATUS BIT(12)
#define TSENS_STATUS_ADDR_OFFSET 2
#define TSENS_TRDY_MASK BIT(0)
#define TSENS_SN_STATUS_ADDR(n) ((n) + 0x44)
#define TSENS_SN_STATUS_VALID BIT(14)
#define TSENS_SN_STATUS_VALID_MASK 0x4000
#define TSENS_TRDY_ADDR(n) ((n) + 0x84)
#define TSENS_CTRL_ADDR(n) (n)
#define TSENS_EN BIT(0)
#define TSENS_CTRL_SENSOR_EN_MASK(n) ((n >> 3) & 0x7ff)
#define TSENS_TRDY_RDY_MIN_TIME 2000
#define TSENS_TRDY_RDY_MAX_TIME 2100
#define TSENS_THRESHOLD_MAX_CODE 0x3ff
#define TSENS_THRESHOLD_MIN_CODE 0x0
#define TSENS_SCALE_MILLIDEG 1000
static int code_to_degc(u32 adc_code, const struct tsens_sensor *sensor)
{
int degc, num, den;
num = (adc_code * SLOPE_FACTOR) - sensor->offset;
den = sensor->slope;
if (num > 0)
degc = num + (den / 2);
else if (num < 0)
degc = num - (den / 2);
else
degc = num;
degc /= den;
return degc;
}
static int degc_to_code(int degc, const struct tsens_sensor *sensor)
{
int code = ((degc * sensor->slope)
+ sensor->offset)/SLOPE_FACTOR;
if (code > TSENS_THRESHOLD_MAX_CODE)
code = TSENS_THRESHOLD_MAX_CODE;
else if (code < TSENS_THRESHOLD_MIN_CODE)
code = TSENS_THRESHOLD_MIN_CODE;
pr_debug("raw_code:0x%x, degc:%d\n",
code, degc);
return code;
}
static int tsens1xxx_get_temp(struct tsens_sensor *sensor, int *temp)
{
struct tsens_device *tmdev = NULL;
unsigned int code;
void __iomem *sensor_addr;
void __iomem *trdy_addr;
int last_temp = 0, last_temp2 = 0, last_temp3 = 0;
bool last_temp_valid = false, last_temp2_valid = false;
bool last_temp3_valid = false;
if (!sensor)
return -EINVAL;
tmdev = sensor->tmdev;
trdy_addr = TSENS_TRDY_ADDR(tmdev->tsens_tm_addr);
sensor_addr = TSENS_SN_STATUS_ADDR(tmdev->tsens_tm_addr);
code = readl_relaxed(sensor_addr +
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
last_temp = code & TSENS_SN_STATUS_TEMP_MASK;
if (tmdev->ctrl_data->valid_status_check) {
if (code & TSENS_SN_STATUS_VALID)
last_temp_valid = true;
else {
code = readl_relaxed(sensor_addr +
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
last_temp2 = code & TSENS_SN_STATUS_TEMP_MASK;
if (code & TSENS_SN_STATUS_VALID) {
last_temp = last_temp2;
last_temp2_valid = true;
} else {
code = readl_relaxed(sensor_addr +
(sensor->hw_id <<
TSENS_STATUS_ADDR_OFFSET));
last_temp3 = code & TSENS_SN_STATUS_TEMP_MASK;
if (code & TSENS_SN_STATUS_VALID) {
last_temp = last_temp3;
last_temp3_valid = true;
}
}
}
}
if ((tmdev->ctrl_data->valid_status_check) &&
(!last_temp_valid && !last_temp2_valid && !last_temp3_valid)) {
if (last_temp == last_temp2)
last_temp = last_temp2;
else if (last_temp2 == last_temp3)
last_temp = last_temp3;
}
*temp = code_to_degc(last_temp, sensor);
*temp = *temp * TSENS_SCALE_MILLIDEG;
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, (u32)sensor->hw_id,
TSENS_DBG_LOG_TEMP_READS, temp);
return 0;
}
static int tsens_tz_activate_trip_type(struct tsens_sensor *tm_sensor,
int trip, enum thermal_device_mode mode)
{
struct tsens_device *tmdev = NULL;
unsigned int reg_cntl, code, hi_code, lo_code, mask;
/* clear the interrupt and unmask */
if (!tm_sensor || trip < 0)
return -EINVAL;
tmdev = tm_sensor->tmdev;
if (!tmdev)
return -EINVAL;
lo_code = TSENS_THRESHOLD_MIN_CODE;
hi_code = TSENS_THRESHOLD_MAX_CODE;
reg_cntl = readl_relaxed((TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET)));
switch (trip) {
case TSENS_TRIP_CONFIGURABLE_HI:
tmdev->sensor[tm_sensor->hw_id].thr_state.high_th_state = mode;
code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
>> TSENS_UPPER_THRESHOLD_SHIFT;
mask = TSENS_UPPER_STATUS_CLR;
if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
lo_code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
break;
case TSENS_TRIP_CONFIGURABLE_LOW:
tmdev->sensor[tm_sensor->hw_id].thr_state.low_th_state = mode;
code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
mask = TSENS_LOWER_STATUS_CLR;
if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
hi_code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
>> TSENS_UPPER_THRESHOLD_SHIFT;
break;
default:
return -EINVAL;
}
if (mode == THERMAL_DEVICE_DISABLED)
writel_relaxed(reg_cntl | mask,
(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id * TSENS_SN_ADDR_OFFSET)));
else
writel_relaxed(reg_cntl & ~mask,
(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id * TSENS_SN_ADDR_OFFSET)));
/* Enable the thresholds */
mb();
return 0;
}
static int tsens1xxx_set_trip_temp(struct tsens_sensor *tm_sensor,
int low_temp, int high_temp)
{
unsigned int reg_cntl;
unsigned long flags;
struct tsens_device *tmdev = NULL;
int high_code, low_code, rc = 0;
if (!tm_sensor)
return -EINVAL;
tmdev = tm_sensor->tmdev;
if (!tmdev)
return -EINVAL;
spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
if (high_temp != INT_MAX) {
high_temp /= TSENS_SCALE_MILLIDEG;
high_code = degc_to_code(high_temp, tm_sensor);
tmdev->sensor[tm_sensor->hw_id].thr_state.high_adc_code =
high_code;
tmdev->sensor[tm_sensor->hw_id].thr_state.high_temp =
high_temp;
reg_cntl = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET));
high_code <<= TSENS_UPPER_THRESHOLD_SHIFT;
reg_cntl &= ~TSENS_UPPER_THRESHOLD_MASK;
writel_relaxed(reg_cntl | high_code,
(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET)));
}
if (low_temp != INT_MIN) {
low_temp /= TSENS_SCALE_MILLIDEG;
low_code = degc_to_code(low_temp, tm_sensor);
tmdev->sensor[tm_sensor->hw_id].thr_state.low_adc_code =
low_code;
tmdev->sensor[tm_sensor->hw_id].thr_state.low_temp =
low_temp;
reg_cntl = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET));
reg_cntl &= ~TSENS_LOWER_THRESHOLD_MASK;
writel_relaxed(reg_cntl | low_code,
(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET)));
}
/* Set trip temperature thresholds */
mb();
if (high_temp != INT_MAX) {
rc = tsens_tz_activate_trip_type(tm_sensor,
TSENS_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_ENABLED);
if (rc) {
pr_err("trip high enable error :%d\n", rc);
goto fail;
}
} else {
rc = tsens_tz_activate_trip_type(tm_sensor,
TSENS_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_DISABLED);
if (rc) {
pr_err("trip high disable error :%d\n", rc);
goto fail;
}
}
if (low_temp != INT_MIN) {
rc = tsens_tz_activate_trip_type(tm_sensor,
TSENS_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_ENABLED);
if (rc) {
pr_err("trip low enable activation error :%d\n", rc);
goto fail;
}
} else {
rc = tsens_tz_activate_trip_type(tm_sensor,
TSENS_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_DISABLED);
if (rc) {
pr_err("trip low disable error :%d\n", rc);
goto fail;
}
}
fail:
spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
return rc;
}
static irqreturn_t tsens_irq_thread(int irq, void *data)
{
struct tsens_device *tm = data;
unsigned int i, status, threshold, temp, th_temp;
unsigned long flags;
void __iomem *sensor_status_addr;
void __iomem *sensor_status_ctrl_addr;
u32 rc = 0, addr_offset;
sensor_status_addr = TSENS_SN_STATUS_ADDR(tm->tsens_tm_addr);
sensor_status_ctrl_addr =
TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tm->tsens_tm_addr);
for (i = 0; i < tm->ctrl_data->num_sensors; i++) {
bool upper_thr = false, lower_thr = false;
if (IS_ERR(tm->sensor[i].tzd))
continue;
rc = tsens1xxx_get_temp(&tm->sensor[i], &temp);
if (rc) {
pr_debug("Error:%d reading temp sensor:%d\n", rc, i);
continue;
}
spin_lock_irqsave(&tm->tsens_upp_low_lock, flags);
addr_offset = tm->sensor[i].hw_id *
TSENS_SN_ADDR_OFFSET;
status = readl_relaxed(sensor_status_addr + addr_offset);
threshold = readl_relaxed(sensor_status_ctrl_addr +
addr_offset);
if (status & TSENS_SN_STATUS_UPPER_STATUS) {
writel_relaxed(threshold | TSENS_UPPER_STATUS_CLR,
TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(
tm->tsens_tm_addr + addr_offset));
th_temp = code_to_degc((threshold &
TSENS_UPPER_THRESHOLD_MASK) >>
TSENS_UPPER_THRESHOLD_SHIFT,
(tm->sensor + i));
if (th_temp > (temp/TSENS_SCALE_MILLIDEG)) {
pr_debug("Re-arm high threshold\n");
rc = tsens_tz_activate_trip_type(
&tm->sensor[i],
TSENS_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_ENABLED);
if (rc)
pr_err("high rearm failed\n");
} else {
upper_thr = true;
tm->sensor[i].thr_state.high_th_state =
THERMAL_DEVICE_DISABLED;
}
}
if (status & TSENS_SN_STATUS_LOWER_STATUS) {
writel_relaxed(threshold | TSENS_LOWER_STATUS_CLR,
TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(
tm->tsens_tm_addr + addr_offset));
th_temp = code_to_degc((threshold &
TSENS_LOWER_THRESHOLD_MASK),
(tm->sensor + i));
if (th_temp < (temp/TSENS_SCALE_MILLIDEG)) {
pr_debug("Re-arm Low threshold\n");
rc = tsens_tz_activate_trip_type(
&tm->sensor[i],
TSENS_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_ENABLED);
if (rc)
pr_err("low rearm failed\n");
} else {
lower_thr = true;
tm->sensor[i].thr_state.low_th_state =
THERMAL_DEVICE_DISABLED;
}
}
spin_unlock_irqrestore(&tm->tsens_upp_low_lock, flags);
if (upper_thr || lower_thr) {
pr_debug("sensor:%d trigger temp (%d degC)\n",
tm->sensor[i].hw_id,
code_to_degc((status &
TSENS_SN_STATUS_TEMP_MASK),
(tm->sensor + i)));
of_thermal_handle_trip(tm->dev, tm->sensor[i].tzd);
}
}
/* Disable monitoring sensor trip threshold for triggered sensor */
mb();
if (tm->ops->dbg)
tm->ops->dbg(tm, 0, TSENS_DBG_LOG_INTERRUPT_TIMESTAMP, NULL);
return IRQ_HANDLED;
}
static int tsens1xxx_hw_sensor_en(struct tsens_device *tmdev,
u32 sensor_id)
{
void __iomem *srot_addr;
unsigned int srot_val, sensor_en;
srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
srot_val = readl_relaxed(srot_addr);
srot_val = TSENS_CTRL_SENSOR_EN_MASK(srot_val);
sensor_en = ((1 << sensor_id) & srot_val);
return sensor_en;
}
static int tsens1xxx_hw_init(struct tsens_device *tmdev)
{
void __iomem *srot_addr;
unsigned int srot_val;
srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
srot_val = readl_relaxed(srot_addr);
if (!(srot_val & TSENS_EN)) {
pr_err("TSENS device is not enabled\n");
return -ENODEV;
}
writel_relaxed(TSENS_INTERRUPT_EN,
TSENS_UPPER_LOWER_INTERRUPT_CTRL(tmdev->tsens_tm_addr));
spin_lock_init(&tmdev->tsens_upp_low_lock);
if (tmdev->ctrl_data->mtc) {
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, 0, TSENS_DBG_MTC_DATA, NULL);
}
return 0;
}
static const struct tsens_irqs tsens1xxx_irqs[] = {
{ "tsens-upper-lower", tsens_irq_thread},
};
static int tsens1xxx_register_interrupts(struct tsens_device *tmdev)
{
struct platform_device *pdev;
int i, rc;
if (!tmdev)
return -EINVAL;
pdev = tmdev->pdev;
for (i = 0; i < ARRAY_SIZE(tsens1xxx_irqs); i++) {
int irq;
irq = platform_get_irq_byname(pdev, tsens1xxx_irqs[i].name);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq %s\n",
tsens1xxx_irqs[i].name);
return irq;
}
rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
tsens1xxx_irqs[i].handler,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
tsens1xxx_irqs[i].name, tmdev);
if (rc) {
dev_err(&pdev->dev, "failed to get irq %s\n",
tsens1xxx_irqs[i].name);
return rc;
}
enable_irq_wake(irq);
}
return 0;
}
static const struct tsens_ops ops_tsens1xxx = {
.hw_init = tsens1xxx_hw_init,
.get_temp = tsens1xxx_get_temp,
.set_trips = tsens1xxx_set_trip_temp,
.interrupts_reg = tsens1xxx_register_interrupts,
.sensor_en = tsens1xxx_hw_sensor_en,
.calibrate = calibrate_8937,
.dbg = tsens2xxx_dbg,
};
const struct tsens_data data_tsens14xx = {
.num_sensors = TSENS_NUM_SENSORS_8937,
.ops = &ops_tsens1xxx,
.valid_status_check = true,
.mtc = true,
.ver_major = 1,
.ver_minor = 4,
};
static const struct tsens_ops ops_tsens1xxx_405 = {
.hw_init = tsens1xxx_hw_init,
.get_temp = tsens1xxx_get_temp,
.set_trips = tsens1xxx_set_trip_temp,
.interrupts_reg = tsens1xxx_register_interrupts,
.sensor_en = tsens1xxx_hw_sensor_en,
.calibrate = calibrate_405,
.dbg = tsens2xxx_dbg,
};
const struct tsens_data data_tsens14xx_405 = {
.num_sensors = TSENS_NUM_SENSORS_405,
.ops = &ops_tsens1xxx_405,
.valid_status_check = true,
.mtc = true,
.ver_major = 1,
.ver_minor = 4,
};
MODULE_LICENSE("GPL v2");

847
drivers/thermal/tsens2xxx.c Normal file
View file

@ -0,0 +1,847 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/vmalloc.h>
#include "tsens.h"
#include "thermal_core.h"
#include <linux/qcom_scm.h>
#define TSENS_TM_INT_EN(n) ((n) + 0x4)
#define TSENS_TM_CRITICAL_INT_STATUS(n) ((n) + 0x14)
#define TSENS_TM_CRITICAL_INT_CLEAR(n) ((n) + 0x18)
#define TSENS_TM_CRITICAL_INT_MASK(n) ((n) + 0x1c)
#define TSENS_TM_CRITICAL_WD_BARK BIT(31)
#define TSENS_TM_CRITICAL_CYCLE_MONITOR BIT(30)
#define TSENS_TM_CRITICAL_INT_EN BIT(2)
#define TSENS_TM_UPPER_INT_EN BIT(1)
#define TSENS_TM_LOWER_INT_EN BIT(0)
#define TSENS_TM_UPPER_LOWER_INT_DISABLE 0xffffffff
#define TSENS_TM_SN_UPPER_LOWER_THRESHOLD(n) ((n) + 0x20)
#define TSENS_TM_SN_ADDR_OFFSET 0x4
#define TSENS_TM_UPPER_THRESHOLD_SET(n) ((n) << 12)
#define TSENS_TM_UPPER_THRESHOLD_VALUE_SHIFT(n) ((n) >> 12)
#define TSENS_TM_LOWER_THRESHOLD_VALUE(n) ((n) & 0xfff)
#define TSENS_TM_UPPER_THRESHOLD_VALUE(n) (((n) & 0xfff000) >> 12)
#define TSENS_TM_UPPER_THRESHOLD_MASK 0xfff000
#define TSENS_TM_LOWER_THRESHOLD_MASK 0xfff
#define TSENS_TM_UPPER_THRESHOLD_SHIFT 12
#define TSENS_TM_SN_CRITICAL_THRESHOLD(n) ((n) + 0x60)
#define TSENS_STATUS_ADDR_OFFSET 2
#define TSENS_TM_UPPER_INT_MASK(n) (((n) & 0xffff0000) >> 16)
#define TSENS_TM_LOWER_INT_MASK(n) ((n) & 0xffff)
#define TSENS_TM_UPPER_LOWER_INT_STATUS(n) ((n) + 0x8)
#define TSENS_TM_UPPER_LOWER_INT_CLEAR(n) ((n) + 0xc)
#define TSENS_TM_UPPER_LOWER_INT_MASK(n) ((n) + 0x10)
#define TSENS_TM_UPPER_INT_SET(n) (1 << (n + 16))
#define TSENS_TM_SN_CRITICAL_THRESHOLD_MASK 0xfff
#define TSENS_TM_MIN_TEMP_VALID_BIT BIT(16)
#define TSENS_TM_SN_STATUS_VALID_BIT BIT(21)
#define TSENS_TM_SN_STATUS_CRITICAL_STATUS BIT(19)
#define TSENS_TM_SN_STATUS_UPPER_STATUS BIT(18)
#define TSENS_TM_SN_STATUS_LOWER_STATUS BIT(17)
#define TSENS_TM_SN_LAST_TEMP_MASK 0xfff
#define TSENS_TM_CODE_BIT_MASK 0xfff
#define TSENS_TM_0C_THR_MASK 0xfff
#define TSENS_TM_0C_THR_OFFSET 12
#define TSENS_TM_CODE_SIGN_BIT 0x800
#define TSENS_TM_SCALE_DECI_MILLIDEG 100
#define TSENS_DEBUG_WDOG_TRIGGER_COUNT 5
#define TSENS_TM_WATCHDOG_LOG(n) ((n) + 0x13c)
#define TSENS_TM_WATCHDOG_LOG_v23(n) ((n) + 0x170)
#define TSENS_EN BIT(0)
#define TSENS_CTRL_SENSOR_EN_MASK(n) ((n >> 3) & 0xffff)
#define TSENS_TM_TRDY(n) ((n) + 0xe4)
#define TSENS_TM_TRDY_FIRST_ROUND_COMPLETE BIT(3)
#define TSENS_TM_TRDY_FIRST_ROUND_COMPLETE_SHIFT 3
#define TSENS_TM_0C_INT_STATUS(n) ((n) + 0xe0)
#define TSENS_TM_MIN_TEMP(n) ((n) + 0xec)
#define TSENS_TM_0C_THRESHOLDS(n) ((n) + 0x1c)
#define TSENS_MAX_READ_FAIL 50
#define TSENS_INIT_ID 0x5
#define TSENS_RECOVERY_LOOP_COUNT 5
static void msm_tsens_convert_temp(int last_temp, int *temp)
{
int code_mask = ~TSENS_TM_CODE_BIT_MASK;
if (last_temp & TSENS_TM_CODE_SIGN_BIT) {
/* Sign extension for negative value */
last_temp |= code_mask;
}
*temp = last_temp * TSENS_TM_SCALE_DECI_MILLIDEG;
}
static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp)
{
struct tsens_device *tmdev = NULL, *tmdev_itr;
unsigned int code, ret;
void __iomem *sensor_addr, *trdy;
int last_temp = 0, last_temp2 = 0, last_temp3 = 0, count = 0;
int tsens_ret;
if (!sensor)
return -EINVAL;
tmdev = sensor->tmdev;
sensor_addr = TSENS_TM_SN_STATUS(tmdev->tsens_tm_addr);
trdy = TSENS_TM_TRDY(tmdev->tsens_tm_addr);
code = readl_relaxed_no_log(trdy);
if (!((code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) >>
TSENS_TM_TRDY_FIRST_ROUND_COMPLETE_SHIFT)) {
pr_err("%s: tsens device first round not complete0x%x\n",
__func__, code);
/* Wait for 2.5 ms for tsens controller to recover */
do {
udelay(500);
code = readl_relaxed_no_log(trdy);
if (code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) {
TSENS_DUMP(tmdev, "%s",
"tsens controller recovered\n");
goto sensor_read;
}
} while (++count < TSENS_RECOVERY_LOOP_COUNT);
/*
* TSENS controller did not recover,
* proceed with SCM call to re-init it
*/
if (tmdev->tsens_reinit_wa) {
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, 0,
TSENS_DBG_LOG_BUS_ID_DATA, NULL);
/*Make an scm call to re-init TSENS */
TSENS_DBG(tmdev, "%s",
"Calling TZ to re-init TSENS\n");
ret = qcom_scm_tsens_reinit(&tsens_ret);
TSENS_DBG(tmdev, "%s",
"return from scm call\n");
if (ret) {
pr_err("%s: scm call failed %d\n",
__func__, ret);
BUG();
}
if (tsens_ret) {
pr_err("%s: scm call failed to init tsens %d\n",
__func__, tsens_ret);
BUG();
}
/* Notify thermal fwk */
list_for_each_entry(tmdev_itr,
&tsens_device_list, list) {
queue_work(tmdev_itr->tsens_reinit_work,
&tmdev_itr->therm_fwk_notify);
}
} else {
pr_err("%s: tsens controller got reset\n", __func__);
BUG();
}
return -EAGAIN;
}
sensor_read:
tmdev->trdy_fail_ctr = 0;
code = readl_relaxed_no_log(sensor_addr +
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK;
if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
msm_tsens_convert_temp(last_temp, temp);
goto dbg;
}
code = readl_relaxed_no_log(sensor_addr +
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
last_temp2 = code & TSENS_TM_SN_LAST_TEMP_MASK;
if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
last_temp = last_temp2;
msm_tsens_convert_temp(last_temp, temp);
goto dbg;
}
code = readl_relaxed_no_log(sensor_addr +
(sensor->hw_id <<
TSENS_STATUS_ADDR_OFFSET));
last_temp3 = code & TSENS_TM_SN_LAST_TEMP_MASK;
if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
last_temp = last_temp3;
msm_tsens_convert_temp(last_temp, temp);
goto dbg;
}
if (last_temp == last_temp2)
last_temp = last_temp2;
else if (last_temp2 == last_temp3)
last_temp = last_temp3;
msm_tsens_convert_temp(last_temp, temp);
dbg:
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, (u32) sensor->hw_id,
TSENS_DBG_LOG_TEMP_READS, temp);
return 0;
}
int tsens_2xxx_get_min_temp(struct tsens_sensor *sensor, int *temp)
{
struct tsens_device *tmdev = NULL;
unsigned int code;
void __iomem *sensor_addr, *trdy;
int last_temp = 0, last_temp2 = 0, last_temp3 = 0, valid_bit;
if (!sensor)
return -EINVAL;
tmdev = sensor->tmdev;
trdy = TSENS_TM_TRDY(tmdev->tsens_tm_addr);
valid_bit = TSENS_TM_MIN_TEMP_VALID_BIT;
sensor_addr = TSENS_TM_MIN_TEMP(tmdev->tsens_tm_addr);
code = readl_relaxed_no_log(trdy);
if (!((code & TSENS_TM_TRDY_FIRST_ROUND_COMPLETE) >>
TSENS_TM_TRDY_FIRST_ROUND_COMPLETE_SHIFT)) {
pr_err("tsens device first round not complete0x%x, ctr is %d\n",
code, tmdev->trdy_fail_ctr);
tmdev->trdy_fail_ctr++;
if (tmdev->trdy_fail_ctr >= TSENS_MAX_READ_FAIL) {
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, 0,
TSENS_DBG_LOG_BUS_ID_DATA, NULL);
BUG();
}
return -ENODATA;
}
tmdev->trdy_fail_ctr = 0;
code = readl_relaxed_no_log(sensor_addr);
last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK;
if (code & valid_bit) {
msm_tsens_convert_temp(last_temp, temp);
goto dbg;
}
code = readl_relaxed_no_log(sensor_addr);
last_temp2 = code & TSENS_TM_SN_LAST_TEMP_MASK;
if (code & valid_bit) {
last_temp = last_temp2;
msm_tsens_convert_temp(last_temp, temp);
goto dbg;
}
code = readl_relaxed_no_log(sensor_addr);
last_temp3 = code & TSENS_TM_SN_LAST_TEMP_MASK;
if (code & valid_bit) {
last_temp = last_temp3;
msm_tsens_convert_temp(last_temp, temp);
goto dbg;
}
if (last_temp == last_temp2)
last_temp = last_temp2;
else if (last_temp2 == last_temp3)
last_temp = last_temp3;
msm_tsens_convert_temp(last_temp, temp);
dbg:
TSENS_DBG(tmdev, "Min temp: %d\n", *temp);
return 0;
}
static int tsens_tm_activate_trip_type(struct tsens_sensor *tm_sensor,
int trip, enum thermal_device_mode mode)
{
struct tsens_device *tmdev = NULL;
unsigned int reg_cntl, mask;
int rc = 0;
/* clear the interrupt and unmask */
if (!tm_sensor || trip < 0)
return -EINVAL;
tmdev = tm_sensor->tmdev;
if (!tmdev)
return -EINVAL;
mask = (tm_sensor->hw_id);
switch (trip) {
case THERMAL_TRIP_CRITICAL:
tmdev->sensor[tm_sensor->hw_id].thr_state.crit_th_state = mode;
reg_cntl = readl_relaxed(TSENS_TM_CRITICAL_INT_MASK
(tmdev->tsens_tm_addr));
if (mode == THERMAL_DEVICE_DISABLED)
writel_relaxed(reg_cntl | (1 << mask),
(TSENS_TM_CRITICAL_INT_MASK
(tmdev->tsens_tm_addr)));
else
writel_relaxed(reg_cntl & ~(1 << mask),
(TSENS_TM_CRITICAL_INT_MASK
(tmdev->tsens_tm_addr)));
break;
case TSENS_TRIP_CONFIGURABLE_HI:
tmdev->sensor[tm_sensor->hw_id].thr_state.high_th_state = mode;
reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK
(tmdev->tsens_tm_addr));
if (mode == THERMAL_DEVICE_DISABLED)
writel_relaxed(reg_cntl |
(TSENS_TM_UPPER_INT_SET(mask)),
(TSENS_TM_UPPER_LOWER_INT_MASK
(tmdev->tsens_tm_addr)));
else
writel_relaxed(reg_cntl &
~(TSENS_TM_UPPER_INT_SET(mask)),
(TSENS_TM_UPPER_LOWER_INT_MASK
(tmdev->tsens_tm_addr)));
break;
case TSENS_TRIP_CONFIGURABLE_LOW:
tmdev->sensor[tm_sensor->hw_id].thr_state.low_th_state = mode;
reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK
(tmdev->tsens_tm_addr));
if (mode == THERMAL_DEVICE_DISABLED)
writel_relaxed(reg_cntl | (1 << mask),
(TSENS_TM_UPPER_LOWER_INT_MASK
(tmdev->tsens_tm_addr)));
else
writel_relaxed(reg_cntl & ~(1 << mask),
(TSENS_TM_UPPER_LOWER_INT_MASK
(tmdev->tsens_tm_addr)));
break;
default:
rc = -EINVAL;
}
/* Activate and enable the respective trip threshold setting */
mb();
return rc;
}
static int tsens2xxx_set_trip_temp(struct tsens_sensor *tm_sensor,
int low_temp, int high_temp)
{
unsigned int reg_cntl;
unsigned long flags;
struct tsens_device *tmdev = NULL;
int rc = 0;
if (!tm_sensor)
return -EINVAL;
tmdev = tm_sensor->tmdev;
if (!tmdev)
return -EINVAL;
pr_debug("%s: sensor:%d low_temp(mdegC):%d, high_temp(mdegC):%d\n",
__func__, tm_sensor->hw_id, low_temp, high_temp);
spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
if (high_temp != INT_MAX) {
tmdev->sensor[tm_sensor->hw_id].thr_state.high_temp = high_temp;
reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD
(tmdev->tsens_tm_addr)) +
(tm_sensor->hw_id *
TSENS_TM_SN_ADDR_OFFSET));
high_temp /= TSENS_TM_SCALE_DECI_MILLIDEG;
high_temp = TSENS_TM_UPPER_THRESHOLD_SET(high_temp);
high_temp &= TSENS_TM_UPPER_THRESHOLD_MASK;
reg_cntl &= ~TSENS_TM_UPPER_THRESHOLD_MASK;
writel_relaxed(reg_cntl | high_temp,
(TSENS_TM_SN_UPPER_LOWER_THRESHOLD
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET)));
}
if (low_temp != INT_MIN) {
tmdev->sensor[tm_sensor->hw_id].thr_state.low_temp = low_temp;
reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD
(tmdev->tsens_tm_addr)) +
(tm_sensor->hw_id *
TSENS_TM_SN_ADDR_OFFSET));
low_temp /= TSENS_TM_SCALE_DECI_MILLIDEG;
low_temp &= TSENS_TM_LOWER_THRESHOLD_MASK;
reg_cntl &= ~TSENS_TM_LOWER_THRESHOLD_MASK;
writel_relaxed(reg_cntl | low_temp,
(TSENS_TM_SN_UPPER_LOWER_THRESHOLD
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET)));
}
/* Set trip temperature thresholds */
mb();
if (high_temp != INT_MAX) {
rc = tsens_tm_activate_trip_type(tm_sensor,
TSENS_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_ENABLED);
if (rc) {
pr_err("trip high enable error :%d\n", rc);
goto fail;
}
} else {
rc = tsens_tm_activate_trip_type(tm_sensor,
TSENS_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_DISABLED);
if (rc) {
pr_err("trip high disable error :%d\n", rc);
goto fail;
}
}
if (low_temp != INT_MIN) {
rc = tsens_tm_activate_trip_type(tm_sensor,
TSENS_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_ENABLED);
if (rc) {
pr_err("trip low enable activation error :%d\n", rc);
goto fail;
}
} else {
rc = tsens_tm_activate_trip_type(tm_sensor,
TSENS_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_DISABLED);
if (rc) {
pr_err("trip low disable error :%d\n", rc);
goto fail;
}
}
fail:
spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
return rc;
}
static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data)
{
struct tsens_device *tm = data;
unsigned int i, status, wd_log, wd_mask;
unsigned long flags;
void __iomem *sensor_status_addr, *sensor_int_mask_addr;
void __iomem *sensor_critical_addr;
void __iomem *wd_critical_addr, *wd_log_addr;
sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_tm_addr);
sensor_int_mask_addr =
TSENS_TM_CRITICAL_INT_MASK(tm->tsens_tm_addr);
sensor_critical_addr =
TSENS_TM_SN_CRITICAL_THRESHOLD(tm->tsens_tm_addr);
wd_critical_addr =
TSENS_TM_CRITICAL_INT_STATUS(tm->tsens_tm_addr);
if (tm->ctrl_data->ver_major == 2 && tm->ctrl_data->ver_minor == 3)
wd_log_addr = TSENS_TM_WATCHDOG_LOG_v23(tm->tsens_tm_addr);
else
wd_log_addr = TSENS_TM_WATCHDOG_LOG(tm->tsens_tm_addr);
if (tm->ctrl_data->wd_bark) {
wd_mask = readl_relaxed(wd_critical_addr);
if (wd_mask & TSENS_TM_CRITICAL_WD_BARK) {
/*
* Clear watchdog interrupt and
* increment global wd count
*/
writel_relaxed(wd_mask | TSENS_TM_CRITICAL_WD_BARK,
(TSENS_TM_CRITICAL_INT_CLEAR
(tm->tsens_tm_addr)));
writel_relaxed(wd_mask & ~(TSENS_TM_CRITICAL_WD_BARK),
(TSENS_TM_CRITICAL_INT_CLEAR
(tm->tsens_tm_addr)));
wd_log = readl_relaxed(wd_log_addr);
if (wd_log >= TSENS_DEBUG_WDOG_TRIGGER_COUNT) {
pr_err("Watchdog count:%d\n", wd_log);
if (tm->ops->dbg)
tm->ops->dbg(tm, 0,
TSENS_DBG_LOG_BUS_ID_DATA, NULL);
BUG();
}
return IRQ_HANDLED;
}
}
for (i = 0; i < TSENS_MAX_SENSORS; i++) {
int int_mask, int_mask_val;
u32 addr_offset;
if (IS_ERR(tm->sensor[i].tzd))
continue;
spin_lock_irqsave(&tm->tsens_crit_lock, flags);
addr_offset = tm->sensor[i].hw_id *
TSENS_TM_SN_ADDR_OFFSET;
status = readl_relaxed(sensor_status_addr + addr_offset);
int_mask = readl_relaxed(sensor_int_mask_addr);
if ((status & TSENS_TM_SN_STATUS_CRITICAL_STATUS) &&
!(int_mask & (1 << tm->sensor[i].hw_id))) {
int_mask = readl_relaxed(sensor_int_mask_addr);
int_mask_val = (1 << tm->sensor[i].hw_id);
/* Mask the corresponding interrupt for the sensors */
writel_relaxed(int_mask | int_mask_val,
TSENS_TM_CRITICAL_INT_MASK(
tm->tsens_tm_addr));
/* Clear the corresponding sensors interrupt */
writel_relaxed(int_mask_val,
TSENS_TM_CRITICAL_INT_CLEAR
(tm->tsens_tm_addr));
writel_relaxed(0,
TSENS_TM_CRITICAL_INT_CLEAR(
tm->tsens_tm_addr));
tm->sensor[i].thr_state.crit_th_state =
THERMAL_DEVICE_DISABLED;
}
spin_unlock_irqrestore(&tm->tsens_crit_lock, flags);
}
/* Mask critical interrupt */
mb();
return IRQ_HANDLED;
}
static irqreturn_t tsens_tm_irq_thread(int irq, void *data)
{
struct tsens_device *tm = data;
unsigned int i, status, threshold, temp;
unsigned long flags;
void __iomem *sensor_status_addr;
void __iomem *sensor_int_mask_addr;
void __iomem *sensor_upper_lower_addr;
u32 addr_offset = 0;
sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_tm_addr);
sensor_int_mask_addr =
TSENS_TM_UPPER_LOWER_INT_MASK(tm->tsens_tm_addr);
sensor_upper_lower_addr =
TSENS_TM_SN_UPPER_LOWER_THRESHOLD(tm->tsens_tm_addr);
for (i = 0; i < TSENS_MAX_SENSORS; i++) {
bool upper_thr = false, lower_thr = false;
int int_mask, int_mask_val = 0, rc;
if (IS_ERR(tm->sensor[i].tzd))
continue;
rc = tsens2xxx_get_temp(&tm->sensor[i], &temp);
if (rc) {
pr_debug("Error:%d reading temp sensor:%d\n", rc, i);
continue;
}
spin_lock_irqsave(&tm->tsens_upp_low_lock, flags);
addr_offset = tm->sensor[i].hw_id *
TSENS_TM_SN_ADDR_OFFSET;
status = readl_relaxed(sensor_status_addr + addr_offset);
threshold = readl_relaxed(sensor_upper_lower_addr +
addr_offset);
int_mask = readl_relaxed(sensor_int_mask_addr);
if ((status & TSENS_TM_SN_STATUS_UPPER_STATUS) &&
!(int_mask &
(1 << (tm->sensor[i].hw_id + 16)))) {
int_mask = readl_relaxed(sensor_int_mask_addr);
int_mask_val = TSENS_TM_UPPER_INT_SET(
tm->sensor[i].hw_id);
/* Mask the corresponding interrupt for the sensors */
writel_relaxed(int_mask | int_mask_val,
TSENS_TM_UPPER_LOWER_INT_MASK(
tm->tsens_tm_addr));
/* Clear the corresponding sensors interrupt */
writel_relaxed(int_mask_val,
TSENS_TM_UPPER_LOWER_INT_CLEAR(
tm->tsens_tm_addr));
writel_relaxed(0,
TSENS_TM_UPPER_LOWER_INT_CLEAR(
tm->tsens_tm_addr));
if (TSENS_TM_UPPER_THRESHOLD_VALUE(threshold) >
(temp/TSENS_TM_SCALE_DECI_MILLIDEG)) {
pr_debug("Re-arm high threshold\n");
rc = tsens_tm_activate_trip_type(
&tm->sensor[i],
TSENS_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_ENABLED);
if (rc)
pr_err("high rearm failed:%d\n", rc);
} else {
upper_thr = true;
tm->sensor[i].thr_state.high_th_state =
THERMAL_DEVICE_DISABLED;
}
}
if ((status & TSENS_TM_SN_STATUS_LOWER_STATUS) &&
!(int_mask &
(1 << tm->sensor[i].hw_id))) {
int_mask = readl_relaxed(sensor_int_mask_addr);
int_mask_val = (1 << tm->sensor[i].hw_id);
/* Mask the corresponding interrupt for the sensors */
writel_relaxed(int_mask | int_mask_val,
TSENS_TM_UPPER_LOWER_INT_MASK(
tm->tsens_tm_addr));
/* Clear the corresponding sensors interrupt */
writel_relaxed(int_mask_val,
TSENS_TM_UPPER_LOWER_INT_CLEAR(
tm->tsens_tm_addr));
writel_relaxed(0,
TSENS_TM_UPPER_LOWER_INT_CLEAR(
tm->tsens_tm_addr));
if (TSENS_TM_LOWER_THRESHOLD_VALUE(threshold)
< (temp/TSENS_TM_SCALE_DECI_MILLIDEG)) {
pr_debug("Re-arm low threshold\n");
rc = tsens_tm_activate_trip_type(
&tm->sensor[i],
TSENS_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_ENABLED);
if (rc)
pr_err("low rearm failed:%d\n", rc);
} else {
lower_thr = true;
tm->sensor[i].thr_state.low_th_state =
THERMAL_DEVICE_DISABLED;
}
}
spin_unlock_irqrestore(&tm->tsens_upp_low_lock, flags);
if (upper_thr || lower_thr) {
/* Use id for multiple controllers */
pr_debug("sensor:%d trigger temp (%d degC)\n",
tm->sensor[i].hw_id, temp);
of_thermal_handle_trip_temp(tm->dev,
tm->sensor[i].tzd, temp);
}
}
/* Disable monitoring sensor trip threshold for triggered sensor */
mb();
if (tm->ops->dbg)
tm->ops->dbg(tm, 0, TSENS_DBG_LOG_INTERRUPT_TIMESTAMP, NULL);
return IRQ_HANDLED;
}
static irqreturn_t tsens_tm_0C_irq_thread(int irq, void *data)
{
struct tsens_device *tm = data;
int status, thrs, set_thr, reset_thr;
void __iomem *srot_addr, *addr;
addr = TSENS_TM_0C_INT_STATUS(tm->tsens_tm_addr);
status = readl_relaxed(addr);
srot_addr = TSENS_CTRL_ADDR(tm->tsens_srot_addr);
thrs = readl_relaxed(TSENS_TM_0C_THRESHOLDS(srot_addr));
msm_tsens_convert_temp(thrs & TSENS_TM_0C_THR_MASK, &reset_thr);
msm_tsens_convert_temp(
((thrs >> TSENS_TM_0C_THR_OFFSET) &
TSENS_TM_0C_THR_MASK), &set_thr);
if (status)
of_thermal_handle_trip_temp(tm->dev,
tm->min_temp.tzd, set_thr);
else
of_thermal_handle_trip_temp(tm->dev,
tm->min_temp.tzd, reset_thr);
return IRQ_HANDLED;
}
static int tsens2xxx_hw_sensor_en(struct tsens_device *tmdev,
u32 sensor_id)
{
void __iomem *srot_addr;
unsigned int srot_val, sensor_en;
srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
srot_val = readl_relaxed(srot_addr);
srot_val = TSENS_CTRL_SENSOR_EN_MASK(srot_val);
sensor_en = ((1 << sensor_id) & srot_val);
return sensor_en;
}
static int tsens2xxx_hw_init(struct tsens_device *tmdev)
{
void __iomem *srot_addr;
void __iomem *sensor_int_mask_addr;
unsigned int srot_val, crit_mask, crit_val;
void __iomem *int_mask_addr;
srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
srot_val = readl_relaxed(srot_addr);
if (!(srot_val & TSENS_EN)) {
pr_err("TSENS device is not enabled\n");
return -ENODEV;
}
if (tmdev->ctrl_data->cycle_monitor) {
sensor_int_mask_addr =
TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr);
crit_mask = readl_relaxed(sensor_int_mask_addr);
crit_val = TSENS_TM_CRITICAL_CYCLE_MONITOR;
if (tmdev->ctrl_data->cycle_compltn_monitor_mask)
writel_relaxed((crit_mask | crit_val),
(TSENS_TM_CRITICAL_INT_MASK
(tmdev->tsens_tm_addr)));
else
writel_relaxed((crit_mask & ~crit_val),
(TSENS_TM_CRITICAL_INT_MASK
(tmdev->tsens_tm_addr)));
/*Update critical cycle monitoring*/
mb();
}
if (tmdev->ctrl_data->wd_bark) {
sensor_int_mask_addr =
TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr);
crit_mask = readl_relaxed(sensor_int_mask_addr);
crit_val = TSENS_TM_CRITICAL_WD_BARK;
if (tmdev->ctrl_data->wd_bark_mask)
writel_relaxed((crit_mask | crit_val),
(TSENS_TM_CRITICAL_INT_MASK
(tmdev->tsens_tm_addr)));
else
writel_relaxed((crit_mask & ~crit_val),
(TSENS_TM_CRITICAL_INT_MASK
(tmdev->tsens_tm_addr)));
/*Update watchdog monitoring*/
mb();
}
int_mask_addr = TSENS_TM_UPPER_LOWER_INT_MASK(tmdev->tsens_tm_addr);
writel_relaxed(TSENS_TM_UPPER_LOWER_INT_DISABLE, int_mask_addr);
writel_relaxed(TSENS_TM_CRITICAL_INT_EN |
TSENS_TM_UPPER_INT_EN | TSENS_TM_LOWER_INT_EN,
TSENS_TM_INT_EN(tmdev->tsens_tm_addr));
spin_lock_init(&tmdev->tsens_crit_lock);
spin_lock_init(&tmdev->tsens_upp_low_lock);
if (tmdev->ctrl_data->mtc) {
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, 0, TSENS_DBG_MTC_DATA, NULL);
}
return 0;
}
static const struct tsens_irqs tsens2xxx_irqs[] = {
{ "tsens-upper-lower", tsens_tm_irq_thread},
{ "tsens-critical", tsens_tm_critical_irq_thread},
{ "tsens-0C", tsens_tm_0C_irq_thread},
};
static int tsens2xxx_register_interrupts(struct tsens_device *tmdev)
{
struct platform_device *pdev;
int i, rc, irq_no;
unsigned long irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
if (!tmdev)
return -EINVAL;
if (tmdev->min_temp_sensor_id != MIN_TEMP_DEF_OFFSET)
irq_no = ARRAY_SIZE(tsens2xxx_irqs);
else
irq_no = ARRAY_SIZE(tsens2xxx_irqs) - 1;
pdev = tmdev->pdev;
for (i = 0; i < irq_no; i++) {
int irq;
irq = platform_get_irq_byname(pdev, tsens2xxx_irqs[i].name);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq %s\n",
tsens2xxx_irqs[i].name);
return irq;
}
if (i == 2)
irqflags = IRQF_TRIGGER_RISING | IRQF_ONESHOT;
rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
tsens2xxx_irqs[i].handler,
irqflags, tsens2xxx_irqs[i].name, tmdev);
if (rc) {
dev_err(&pdev->dev, "failed to get irq %s\n",
tsens2xxx_irqs[i].name);
return rc;
}
enable_irq_wake(irq);
}
return 0;
}
static const struct tsens_ops ops_tsens2xxx = {
.hw_init = tsens2xxx_hw_init,
.get_temp = tsens2xxx_get_temp,
.set_trips = tsens2xxx_set_trip_temp,
.interrupts_reg = tsens2xxx_register_interrupts,
.dbg = tsens2xxx_dbg,
.sensor_en = tsens2xxx_hw_sensor_en,
};
const struct tsens_data data_tsens2xxx = {
.cycle_monitor = false,
.cycle_compltn_monitor_mask = 1,
.wd_bark = false,
.wd_bark_mask = 1,
.ops = &ops_tsens2xxx,
.mtc = true,
};
const struct tsens_data data_tsens23xx = {
.cycle_monitor = true,
.cycle_compltn_monitor_mask = 1,
.wd_bark = true,
.wd_bark_mask = 1,
.ops = &ops_tsens2xxx,
.mtc = false,
.ver_major = 2,
.ver_minor = 3,
};
const struct tsens_data data_tsens24xx = {
.cycle_monitor = true,
.cycle_compltn_monitor_mask = 1,
.wd_bark = true,
/* Enable Watchdog monitoring by unmasking */
.wd_bark_mask = 0,
.ops = &ops_tsens2xxx,
.mtc = false,
};
const struct tsens_data data_tsens26xx = {
.cycle_monitor = true,
.cycle_compltn_monitor_mask = 1,
.wd_bark = true,
.wd_bark_mask = 0,
.ops = &ops_tsens2xxx,
.mtc = false,
.ver_major = 2,
.ver_minor = 6,
};