diff options
Diffstat (limited to 'drivers/thermal')
23 files changed, 2236 insertions, 669 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 3c3dc4a3d52c..4166c10ba314 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -260,16 +260,6 @@ config ARMADA_THERMAL Enable this option if you want to have support for thermal management controller present in Armada 370 and Armada XP SoC. -config TEGRA_SOCTHERM - tristate "Tegra SOCTHERM thermal management" - depends on ARCH_TEGRA - help - Enable this option for integrated thermal management support on NVIDIA - Tegra124 systems-on-chip. The driver supports four thermal zones - (CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal - zones to manage temperatures. This option is also required for the - emergency thermal reset (thermtrip) feature to function. - config DB8500_CPUFREQ_COOLING tristate "DB8500 cpufreq cooling" depends on ARCH_U8500 || COMPILE_TEST @@ -399,6 +389,17 @@ depends on ARCH_STI && OF source "drivers/thermal/st/Kconfig" endmenu +config TANGO_THERMAL + tristate "Tango thermal management" + depends on ARCH_TANGO || COMPILE_TEST + help + Enable the Tango thermal driver, which supports the primitive + temperature sensor embedded in Tango chips since the SMP8758. + This sensor only generates a 1-bit signal to indicate whether + the die temperature exceeds a programmable threshold. + +source "drivers/thermal/tegra/Kconfig" + config QCOM_SPMI_TEMP_ALARM tristate "Qualcomm SPMI PMIC Temperature Alarm" depends on OF && SPMI && IIO @@ -410,4 +411,14 @@ config QCOM_SPMI_TEMP_ALARM real time die temperature if an ADC is present or an estimate of the temperature based upon the over temperature stage value. +config GENERIC_ADC_THERMAL + tristate "Generic ADC based thermal sensor" + depends on IIO + help + This enabled a thermal sysfs driver for the temperature sensor + which is connected to the General Purpose ADC. The ADC channel + is read via IIO framework and the channel information is provided + to this driver. This driver reports the temperature by reading ADC + channel and converts it to temperature based on lookup table. + endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 8e9cbc3b5679..10b07c14f8a9 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -35,6 +35,7 @@ obj-y += samsung/ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o +obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o @@ -46,6 +47,7 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o obj-$(CONFIG_ST_THERMAL) += st/ -obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o +obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/ obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o +obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index 5e820b541506..97fad8f51e1c 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -160,7 +160,7 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp) struct hisi_thermal_sensor *sensor = _sensor; struct hisi_thermal_data *data = sensor->thermal; - int sensor_id = 0, i; + int sensor_id = -1, i; long max_temp = 0; *temp = hisi_thermal_get_sensor_temp(data, sensor); @@ -168,12 +168,19 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp) sensor->sensor_temp = *temp; for (i = 0; i < HISI_MAX_SENSORS; i++) { + if (!data->sensors[i].tzd) + continue; + if (data->sensors[i].sensor_temp >= max_temp) { max_temp = data->sensors[i].sensor_temp; sensor_id = i; } } + /* If no sensor has been enabled, then skip to enable irq */ + if (sensor_id == -1) + return 0; + mutex_lock(&data->thermal_lock); data->irq_bind_sensor = sensor_id; mutex_unlock(&data->thermal_lock); @@ -226,8 +233,12 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) sensor->thres_temp / 1000); mutex_unlock(&data->thermal_lock); - for (i = 0; i < HISI_MAX_SENSORS; i++) + for (i = 0; i < HISI_MAX_SENSORS; i++) { + if (!data->sensors[i].tzd) + continue; + thermal_zone_device_update(data->sensors[i].tzd); + } return IRQ_HANDLED; } @@ -243,10 +254,11 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev, sensor->id = index; sensor->thermal = data; - sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, sensor->id, - sensor, &hisi_of_thermal_ops); + sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, + sensor->id, sensor, &hisi_of_thermal_ops); if (IS_ERR(sensor->tzd)) { ret = PTR_ERR(sensor->tzd); + sensor->tzd = NULL; dev_err(&pdev->dev, "failed to register sensor id %d: %d\n", sensor->id, ret); return ret; @@ -331,28 +343,21 @@ static int hisi_thermal_probe(struct platform_device *pdev) return ret; } + hisi_thermal_enable_bind_irq_sensor(data); + irq_get_irqchip_state(data->irq, IRQCHIP_STATE_MASKED, + &data->irq_enabled); + for (i = 0; i < HISI_MAX_SENSORS; ++i) { ret = hisi_thermal_register_sensor(pdev, data, &data->sensors[i], i); - if (ret) { + if (ret) dev_err(&pdev->dev, "failed to register thermal sensor: %d\n", ret); - goto err_get_sensor_data; - } + else + hisi_thermal_toggle_sensor(&data->sensors[i], true); } - hisi_thermal_enable_bind_irq_sensor(data); - data->irq_enabled = true; - - for (i = 0; i < HISI_MAX_SENSORS; i++) - hisi_thermal_toggle_sensor(&data->sensors[i], true); - return 0; - -err_get_sensor_data: - clk_disable_unprepare(data->clk); - - return ret; } static int hisi_thermal_remove(struct platform_device *pdev) @@ -363,8 +368,10 @@ static int hisi_thermal_remove(struct platform_device *pdev) for (i = 0; i < HISI_MAX_SENSORS; i++) { struct hisi_thermal_sensor *sensor = &data->sensors[i]; + if (!sensor->tzd) + continue; + hisi_thermal_toggle_sensor(sensor, false); - thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd); } hisi_thermal_disable_sensor(data); diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index 36fa724a36c8..42c1ac057bad 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -198,49 +198,33 @@ static struct thermal_zone_device_ops proc_thermal_local_ops = { .get_temp = proc_thermal_get_zone_temp, }; -static int proc_thermal_add(struct device *dev, - struct proc_thermal_device **priv) +static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv) { - struct proc_thermal_device *proc_priv; - struct acpi_device *adev; + int i; acpi_status status; struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *elements, *ppcc; union acpi_object *p; - unsigned long long tmp; - struct thermal_zone_device_ops *ops = NULL; - int i; - int ret; - - adev = ACPI_COMPANION(dev); - if (!adev) - return -ENODEV; + int ret = 0; - status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf); + status = acpi_evaluate_object(proc_priv->adev->handle, "PPCC", + NULL, &buf); if (ACPI_FAILURE(status)) return -ENODEV; p = buf.pointer; if (!p || (p->type != ACPI_TYPE_PACKAGE)) { - dev_err(dev, "Invalid PPCC data\n"); + dev_err(proc_priv->dev, "Invalid PPCC data\n"); ret = -EFAULT; goto free_buffer; } + if (!p->package.count) { - dev_err(dev, "Invalid PPCC package size\n"); + dev_err(proc_priv->dev, "Invalid PPCC package size\n"); ret = -EFAULT; goto free_buffer; } - proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL); - if (!proc_priv) { - ret = -ENOMEM; - goto free_buffer; - } - - proc_priv->dev = dev; - proc_priv->adev = adev; - for (i = 0; i < min((int)p->package.count - 1, 2); ++i) { elements = &(p->package.elements[i+1]); if (elements->type != ACPI_TYPE_PACKAGE || @@ -257,12 +241,62 @@ static int proc_thermal_add(struct device *dev, proc_priv->power_limits[i].step_uw = ppcc[5].integer.value; } +free_buffer: + kfree(buf.pointer); + + return ret; +} + +#define PROC_POWER_CAPABILITY_CHANGED 0x83 +static void proc_thermal_notify(acpi_handle handle, u32 event, void *data) +{ + struct proc_thermal_device *proc_priv = data; + + if (!proc_priv) + return; + + switch (event) { + case PROC_POWER_CAPABILITY_CHANGED: + proc_thermal_read_ppcc(proc_priv); + int340x_thermal_zone_device_update(proc_priv->int340x_zone); + break; + default: + dev_err(proc_priv->dev, "Unsupported event [0x%x]\n", event); + break; + } +} + + +static int proc_thermal_add(struct device *dev, + struct proc_thermal_device **priv) +{ + struct proc_thermal_device *proc_priv; + struct acpi_device *adev; + acpi_status status; + unsigned long long tmp; + struct thermal_zone_device_ops *ops = NULL; + int ret; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL); + if (!proc_priv) + return -ENOMEM; + + proc_priv->dev = dev; + proc_priv->adev = adev; *priv = proc_priv; - ret = sysfs_create_group(&dev->kobj, - &power_limit_attribute_group); + ret = proc_thermal_read_ppcc(proc_priv); + if (!ret) { + ret = sysfs_create_group(&dev->kobj, + &power_limit_attribute_group); + + } if (ret) - goto free_buffer; + return ret; status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp); if (ACPI_FAILURE(status)) { @@ -274,20 +308,32 @@ static int proc_thermal_add(struct device *dev, proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops); if (IS_ERR(proc_priv->int340x_zone)) { - sysfs_remove_group(&proc_priv->dev->kobj, - &power_limit_attribute_group); ret = PTR_ERR(proc_priv->int340x_zone); + goto remove_group; } else ret = 0; -free_buffer: - kfree(buf.pointer); + ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, + proc_thermal_notify, + (void *)proc_priv); + if (ret) + goto remove_zone; + + return 0; + +remove_zone: + int340x_thermal_zone_remove(proc_priv->int340x_zone); +remove_group: + sysfs_remove_group(&proc_priv->dev->kobj, + &power_limit_attribute_group); return ret; } static void proc_thermal_remove(struct proc_thermal_device *proc_priv) { + acpi_remove_notify_handler(proc_priv->adev->handle, + ACPI_DEVICE_NOTIFY, proc_thermal_notify); int340x_thermal_zone_remove(proc_priv->int340x_zone); sysfs_remove_group(&proc_priv->dev->kobj, &power_limit_attribute_group); diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index 6c79588251d5..015ce2eb6eb7 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -510,12 +510,6 @@ static int start_power_clamp(void) unsigned long cpu; struct task_struct *thread; - /* check if pkg cstate counter is completely 0, abort in this case */ - if (!has_pkg_state_counter()) { - pr_err("pkg cstate counter not functional, abort\n"); - return -EINVAL; - } - set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1); /* prevent cpu hotplug */ get_online_cpus(); @@ -672,35 +666,11 @@ static struct thermal_cooling_device_ops powerclamp_cooling_ops = { .set_cur_state = powerclamp_set_cur_state, }; -/* runs on Nehalem and later */ static const struct x86_cpu_id intel_powerclamp_ids[] __initconst = { - { X86_VENDOR_INTEL, 6, 0x1a}, - { X86_VENDOR_INTEL, 6, 0x1c}, - { X86_VENDOR_INTEL, 6, 0x1e}, - { X86_VENDOR_INTEL, 6, 0x1f}, - { X86_VENDOR_INTEL, 6, 0x25}, - { X86_VENDOR_INTEL, 6, 0x26}, - { X86_VENDOR_INTEL, 6, 0x2a}, - { X86_VENDOR_INTEL, 6, 0x2c}, - { X86_VENDOR_INTEL, 6, 0x2d}, - { X86_VENDOR_INTEL, 6, 0x2e}, - { X86_VENDOR_INTEL, 6, 0x2f}, - { X86_VENDOR_INTEL, 6, 0x37}, - { X86_VENDOR_INTEL, 6, 0x3a}, - { X86_VENDOR_INTEL, 6, 0x3c}, - { X86_VENDOR_INTEL, 6, 0x3d}, - { X86_VENDOR_INTEL, 6, 0x3e}, - { X86_VENDOR_INTEL, 6, 0x3f}, - { X86_VENDOR_INTEL, 6, 0x45}, - { X86_VENDOR_INTEL, 6, 0x46}, - { X86_VENDOR_INTEL, 6, 0x47}, - { X86_VENDOR_INTEL, 6, 0x4c}, - { X86_VENDOR_INTEL, 6, 0x4d}, - { X86_VENDOR_INTEL, 6, 0x4e}, - { X86_VENDOR_INTEL, 6, 0x4f}, - { X86_VENDOR_INTEL, 6, 0x56}, - { X86_VENDOR_INTEL, 6, 0x57}, - { X86_VENDOR_INTEL, 6, 0x5e}, + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_MWAIT }, + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ARAT }, + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_NONSTOP_TSC }, + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_CONSTANT_TSC}, {} }; MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids); @@ -712,11 +682,12 @@ static int __init powerclamp_probe(void) boot_cpu_data.x86, boot_cpu_data.x86_model); return -ENODEV; } - if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC) || - !boot_cpu_has(X86_FEATURE_CONSTANT_TSC) || - !boot_cpu_has(X86_FEATURE_MWAIT) || - !boot_cpu_has(X86_FEATURE_ARAT)) + + /* The goal for idle time alignment is to achieve package cstate. */ + if (!has_pkg_state_counter()) { + pr_info("No package C-state available"); return -ENODEV; + } /* find the deepest mwait value */ find_target_mwait(); diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 507632b9648e..262ab0a2266f 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -144,7 +144,6 @@ struct mtk_thermal { s32 o_slope; s32 vts[MT8173_NUM_SENSORS]; - struct thermal_zone_device *tzd; }; struct mtk_thermal_bank_cfg { @@ -572,16 +571,11 @@ static int mtk_thermal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mt); - mt->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, mt, - &mtk_thermal_ops); - if (IS_ERR(mt->tzd)) - goto err_register; + devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt, + &mtk_thermal_ops); return 0; -err_register: - clk_disable_unprepare(mt->clk_peri_therm); - err_disable_clk_auxadc: clk_disable_unprepare(mt->clk_auxadc); @@ -592,8 +586,6 @@ static int mtk_thermal_remove(struct platform_device *pdev) { struct mtk_thermal *mt = platform_get_drvdata(pdev); - thermal_zone_of_sensor_unregister(&pdev->dev, mt->tzd); - clk_disable_unprepare(mt->clk_peri_therm); clk_disable_unprepare(mt->clk_auxadc); diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index d8ec44b194d6..b8e509c60848 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -331,6 +331,14 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip, if (trip >= data->ntrips || trip < 0) return -EDOM; + if (data->ops->set_trip_temp) { + int ret; + + ret = data->ops->set_trip_temp(data->sensor_data, trip, temp); + if (ret) + return ret; + } + /* thermal framework should take care of data->mask & (1 << trip) */ data->trips[trip].temperature = temp; @@ -906,7 +914,7 @@ finish: return tz; free_tbps: - for (i = 0; i < tz->num_tbps; i++) + for (i = i - 1; i >= 0; i--) of_node_put(tz->tbps[i].cooling_device); kfree(tz->tbps); free_trips: diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c index b677aada5b52..f8a3c60bef94 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom-spmi-temp-alarm.c @@ -260,7 +260,7 @@ static int qpnp_tm_probe(struct platform_device *pdev) if (ret < 0) goto fail; - chip->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, chip, + chip->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, chip, &qpnp_tm_sensor_ops); if (IS_ERR(chip->tz_dev)) { dev_err(&pdev->dev, "failed to register sensor\n"); @@ -281,7 +281,6 @@ static int qpnp_tm_remove(struct platform_device *pdev) { struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev); - thermal_zone_of_sensor_unregister(&pdev->dev, chip->tz_dev); if (!IS_ERR(chip->adc)) iio_channel_release(chip->adc); diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 82daba09e150..71a339271fa5 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -492,7 +492,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) goto error_unregister; if (of_data == USE_OF_THERMAL) - priv->zone = thermal_zone_of_sensor_register( + priv->zone = devm_thermal_zone_of_sensor_register( dev, i, priv, &rcar_thermal_zone_of_ops); else diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 233a564442a0..5d491f16a866 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -1,7 +1,5 @@ /* - * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd - * - * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd + * Copyright (c) 2014-2016, Fuzhou Rockchip Electronics Co., Ltd * Caesar Wang <wxt@rock-chips.com> * * This program is free software; you can redistribute it and/or modify it @@ -23,8 +21,10 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/reset.h> #include <linux/thermal.h> +#include <linux/mfd/syscon.h> #include <linux/pinctrl/consumer.h> /** @@ -73,7 +73,7 @@ enum adc_sort_mode { #define SOC_MAX_SENSORS 2 /** - * struct chip_tsadc_table: hold information about chip-specific differences + * struct chip_tsadc_table - hold information about chip-specific differences * @id: conversion table * @length: size of conversion table * @data_mask: mask to apply on data inputs @@ -86,6 +86,20 @@ struct chip_tsadc_table { enum adc_sort_mode mode; }; +/** + * struct rockchip_tsadc_chip - hold the private data of tsadc chip + * @chn_id[SOC_MAX_SENSORS]: the sensor id of chip correspond to the channel + * @chn_num: the channel number of tsadc chip + * @tshut_temp: the hardware-controlled shutdown temperature value + * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) + * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) + * @initialize: SoC special initialize tsadc controller method + * @irq_ack: clear the interrupt + * @get_temp: get the temperature + * @set_tshut_temp: set the hardware-controlled shutdown temperature + * @set_tshut_mode: set the hardware-controlled shutdown mode + * @table: the chip-specific conversion table + */ struct rockchip_tsadc_chip { /* The sensor id of chip correspond to the ADC channel */ int chn_id[SOC_MAX_SENSORS]; @@ -97,7 +111,8 @@ struct rockchip_tsadc_chip { enum tshut_polarity tshut_polarity; /* Chip-wide methods */ - void (*initialize)(void __iomem *reg, enum tshut_polarity p); + void (*initialize)(struct regmap *grf, + void __iomem *reg, enum tshut_polarity p); void (*irq_ack)(void __iomem *reg); void (*control)(void __iomem *reg, bool on); @@ -112,12 +127,32 @@ struct rockchip_tsadc_chip { struct chip_tsadc_table table; }; +/** + * struct rockchip_thermal_sensor - hold the information of thermal sensor + * @thermal: pointer to the platform/configuration data + * @tzd: pointer to a thermal zone + * @id: identifier of the thermal sensor + */ struct rockchip_thermal_sensor { struct rockchip_thermal_data *thermal; struct thermal_zone_device *tzd; int id; }; +/** + * struct rockchip_thermal_data - hold the private data of thermal driver + * @chip: pointer to the platform/configuration data + * @pdev: platform device of thermal + * @reset: the reset controller of tsadc + * @sensors[SOC_MAX_SENSORS]: the thermal sensor + * @clk: the controller clock is divided by the exteral 24MHz + * @pclk: the advanced peripherals bus clock + * @grf: the general register file will be used to do static set by software + * @regs: the base address of tsadc controller + * @tshut_temp: the hardware-controlled shutdown temperature value + * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) + * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) + */ struct rockchip_thermal_data { const struct rockchip_tsadc_chip *chip; struct platform_device *pdev; @@ -128,6 +163,7 @@ struct rockchip_thermal_data { struct clk *clk; struct clk *pclk; + struct regmap *grf; void __iomem *regs; int tshut_temp; @@ -142,6 +178,7 @@ struct rockchip_thermal_data { * TSADCV3_* are used for newer SoCs than RK3288. (e.g: RK3228, RK3399) * */ +#define TSADCV2_USER_CON 0x00 #define TSADCV2_AUTO_CON 0x04 #define TSADCV2_INT_EN 0x08 #define TSADCV2_INT_PD 0x0c @@ -155,12 +192,7 @@ struct rockchip_thermal_data { #define TSADCV2_AUTO_EN BIT(0) #define TSADCV2_AUTO_SRC_EN(chn) BIT(4 + (chn)) #define TSADCV2_AUTO_TSHUT_POLARITY_HIGH BIT(8) -/** - * TSADCV1_AUTO_Q_SEL_EN: - * whether select (1024 - tsadc_q) as output - * 1'b0:use tsadc_q as output(temperature-code is rising sequence) - * 1'b1:use(1024 - tsadc_q) as output (temperature-code is falling sequence) - */ + #define TSADCV3_AUTO_Q_SEL_EN BIT(1) #define TSADCV2_INT_SRC_EN(chn) BIT(chn) @@ -177,19 +209,32 @@ struct rockchip_thermal_data { #define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT 4 #define TSADCV2_AUTO_PERIOD_TIME 250 /* msec */ #define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* msec */ +#define TSADCV2_USER_INTER_PD_SOC 0x340 /* 13 clocks */ -struct tsadc_table { - u32 code; - int temp; -}; +#define GRF_SARADC_TESTBIT 0x0e644 +#define GRF_TSADC_TESTBIT_L 0x0e648 +#define GRF_TSADC_TESTBIT_H 0x0e64c + +#define GRF_TSADC_TSEN_PD_ON (0x30003 << 0) +#define GRF_TSADC_TSEN_PD_OFF (0x30000 << 0) +#define GRF_SARADC_TESTBIT_ON (0x10001 << 2) +#define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2) /** + * struct tsadc_table - code to temperature conversion table + * @code: the value of adc channel + * @temp: the temperature * Note: - * Code to Temperature mapping of the Temperature sensor is a piece wise linear + * code to temperature mapping of the temperature sensor is a piece wise linear * curve.Any temperature, code faling between to 2 give temperatures can be * linearly interpolated. - * Code to Temperature mapping should be updated based on sillcon results. + * Code to Temperature mapping should be updated based on manufacturer results. */ +struct tsadc_table { + u32 code; + int temp; +}; + static const struct tsadc_table rk3228_code_table[] = { {0, -40000}, {588, -40000}, @@ -308,40 +353,40 @@ static const struct tsadc_table rk3368_code_table[] = { static const struct tsadc_table rk3399_code_table[] = { {0, -40000}, - {593, -40000}, - {598, -35000}, - {603, -30000}, - {609, -25000}, - {614, -20000}, - {619, -15000}, - {625, -10000}, - {630, -5000}, - {635, 0}, - {641, 5000}, - {646, 10000}, - {651, 15000}, - {657, 20000}, - {662, 25000}, - {667, 30000}, - {673, 35000}, - {678, 40000}, - {684, 45000}, - {689, 50000}, - {694, 55000}, - {700, 60000}, - {705, 65000}, - {711, 70000}, - {716, 75000}, - {722, 80000}, - {727, 85000}, - {733, 90000}, - {738, 95000}, - {743, 100000}, - {749, 105000}, - {754, 110000}, - {760, 115000}, - {765, 120000}, - {771, 125000}, + {402, -40000}, + {410, -35000}, + {419, -30000}, + {427, -25000}, + {436, -20000}, + {444, -15000}, + {453, -10000}, + {461, -5000}, + {470, 0}, + {478, 5000}, + {487, 10000}, + {496, 15000}, + {504, 20000}, + {513, 25000}, + {521, 30000}, + {530, 35000}, + {538, 40000}, + {547, 45000}, + {555, 50000}, + {564, 55000}, + {573, 60000}, + {581, 65000}, + {590, 70000}, + {599, 75000}, + {607, 80000}, + {616, 85000}, + {624, 90000}, + {633, 95000}, + {642, 100000}, + {650, 105000}, + {659, 110000}, + {668, 115000}, + {677, 120000}, + {685, 125000}, {TSADCV3_DATA_MASK, 125000}, }; @@ -405,8 +450,8 @@ static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code, return -EAGAIN; /* Incorrect reading */ while (low <= high) { - if (code >= table.id[mid - 1].code && - code < table.id[mid].code) + if (code <= table.id[mid].code && + code > table.id[mid - 1].code) break; else if (code > table.id[mid].code) low = mid + 1; @@ -449,7 +494,7 @@ static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code, * If the temperature is higher than COMP_INT or COMP_SHUT for * "debounce" times, TSADC controller will generate interrupt or TSHUT. */ -static void rk_tsadcv2_initialize(void __iomem *regs, +static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs, enum tshut_polarity tshut_polarity) { if (tshut_polarity == TSHUT_HIGH_ACTIVE) @@ -466,6 +511,62 @@ static void rk_tsadcv2_initialize(void __iomem *regs, regs + TSADCV2_AUTO_PERIOD_HT); writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE); + + if (IS_ERR(grf)) { + pr_warn("%s: Missing rockchip,grf property\n", __func__); + return; + } +} + +/** + * rk_tsadcv3_initialize - initialize TASDC Controller. + * + * (1) The tsadc control power sequence. + * + * (2) Set TSADC_V2_AUTO_PERIOD: + * Configure the interleave between every two accessing of + * TSADC in normal operation. + * + * (2) Set TSADCV2_AUTO_PERIOD_HT: + * Configure the interleave between every two accessing of + * TSADC after the temperature is higher than COM_SHUT or COM_INT. + * + * (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE: + * If the temperature is higher than COMP_INT or COMP_SHUT for + * "debounce" times, TSADC controller will generate interrupt or TSHUT. + */ +static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs, + enum tshut_polarity tshut_polarity) +{ + /* The tsadc control power sequence */ + if (IS_ERR(grf)) { + /* Set interleave value to workround ic time sync issue */ + writel_relaxed(TSADCV2_USER_INTER_PD_SOC, regs + + TSADCV2_USER_CON); + } else { + regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_ON); + mdelay(10); + regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_OFF); + usleep_range(15, 100); /* The spec note says at least 15 us */ + regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON); + regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON); + usleep_range(90, 200); /* The spec note says at least 90 us */ + } + + if (tshut_polarity == TSHUT_HIGH_ACTIVE) + writel_relaxed(0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH, + regs + TSADCV2_AUTO_CON); + else + writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH, + regs + TSADCV2_AUTO_CON); + + writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD); + writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT, + regs + TSADCV2_HIGHT_INT_DEBOUNCE); + writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME, + regs + TSADCV2_AUTO_PERIOD_HT); + writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, + regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE); } static void rk_tsadcv2_irq_ack(void __iomem *regs) @@ -498,10 +599,11 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable) } /** - * @rk_tsadcv3_control: - * TSADC controller works at auto mode, and some SoCs need set the tsadc_q_sel - * bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output adc value if - * setting this bit to enable. + * rk_tsadcv3_control - the tsadc controller is enabled or disabled. + * + * NOTE: TSADC controller works at auto mode, and some SoCs need set the + * tsadc_q_sel bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output + * adc value if setting this bit to enable. */ static void rk_tsadcv3_control(void __iomem *regs, bool enable) { @@ -603,6 +705,30 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = { }, }; +static const struct rockchip_tsadc_chip rk3366_tsadc_data = { + .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ + .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */ + .chn_num = 2, /* two channels for tsadc */ + |