diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 0900421aa2f6..d7dda2efa257 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -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" diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 5b5f60a06c76..7243037382bf 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -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 diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c new file mode 100644 index 000000000000..932c18fd4464 --- /dev/null +++ b/drivers/thermal/msm-tsens.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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"); diff --git a/drivers/thermal/tsens-calib.c b/drivers/thermal/tsens-calib.c new file mode 100644 index 000000000000..d95f2d06ea02 --- /dev/null +++ b/drivers/thermal/tsens-calib.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#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"); diff --git a/drivers/thermal/tsens-dbg.c b/drivers/thermal/tsens-dbg.c new file mode 100644 index 000000000000..26922b43f16b --- /dev/null +++ b/drivers/thermal/tsens-dbg.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#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"); diff --git a/drivers/thermal/tsens-mtc.c b/drivers/thermal/tsens-mtc.c new file mode 100644 index 000000000000..9e200cc1adef --- /dev/null +++ b/drivers/thermal/tsens-mtc.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + */ + +#include +#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"); diff --git a/drivers/thermal/tsens-mtc.h b/drivers/thermal/tsens-mtc.h new file mode 100644 index 000000000000..b5cfee91941b --- /dev/null +++ b/drivers/thermal/tsens-mtc.h @@ -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); diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h new file mode 100644 index 000000000000..39b1fe984109 --- /dev/null +++ b/drivers/thermal/tsens.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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__ */ diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c new file mode 100644 index 000000000000..ec9abc05915c --- /dev/null +++ b/drivers/thermal/tsens1xxx.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#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"); diff --git a/drivers/thermal/tsens2xxx.c b/drivers/thermal/tsens2xxx.c new file mode 100644 index 000000000000..576db65a9fd3 --- /dev/null +++ b/drivers/thermal/tsens2xxx.c @@ -0,0 +1,847 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include "tsens.h" +#include "thermal_core.h" +#include + +#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, +};