mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-14 16:46:15 -04:00
Merge "msm: tsens: Add Tsens driver snapshot"
This commit is contained in:
commit
2b9c58c7eb
10 changed files with 2991 additions and 0 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
396
drivers/thermal/msm-tsens.c
Normal 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");
|
||||
298
drivers/thermal/tsens-calib.c
Normal file
298
drivers/thermal/tsens-calib.c
Normal 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
419
drivers/thermal/tsens-dbg.c
Normal 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
204
drivers/thermal/tsens-mtc.c
Normal 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");
|
||||
54
drivers/thermal/tsens-mtc.h
Normal file
54
drivers/thermal/tsens-mtc.h
Normal 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
238
drivers/thermal/tsens.h
Normal 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
523
drivers/thermal/tsens1xxx.c
Normal 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
847
drivers/thermal/tsens2xxx.c
Normal 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,
|
||||
};
|
||||
Loading…
Reference in a new issue