diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-18 01:31:27 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-18 01:31:27 +0300 |
commit | bec04432cb9036dedf89140c102b5ac03e4b3626 (patch) | |
tree | 390d76ddf5f302577efc704a78251f099bfecbf5 /drivers/thermal | |
parent | e29116758c4e06be9ba6358350f9d9f466414efb (diff) | |
parent | 1e032393d9680c1f3b5238ec0f3f4eb006ee83d2 (diff) | |
download | linux-bec04432cb9036dedf89140c102b5ac03e4b3626.tar.xz |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
Pull thermal management updates from Zhang Rui:
- introduce brcmstb AVS TMON thermal driver (Brian Norris)
- add Rockchip RV1108 support in rockchip thermal driver (Rocky Hao)
- major rework on HISI driver plus additional support of hisi3660
(Daniel Lezcano)
- add nvmem-cells binding on imx6sx (Leonard Crestez)
- fix a NULL pointer dereference on ti thermal driver unloading (Tony
Lindgren)
- improve tmon tool to make it easier to cross-compile tmon (Markus
Mayer)
- add Coffee Lake and Cannon Lake support for intel processor and pch
thermal drivers (Srinivas Pandruvada)
- other small fixes and cleanups (Arvind Yadav, Colin Ian King, Allen
Wild, Nicolin Chen, Baruch SiachNiklas Söderlund, Arnd Bergmann)
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (44 commits)
thermal: pch: Add Cannon Lake support
thermal: int340x: processor_thermal: Add Coffee Lake support
thermal: int340x: processor_thermal: Add Cannon Lake support
thermal: bxt: remove redundant variable trip
thermal: cpu_cooling: pr_err() strings should end with newlines
thermal: add brcmstb AVS TMON driver
Documentation: devicetree: add binding for Broadcom STB AVS TMON
thermal/drivers/hisi: Add support for hi3660 SoC
thermal/drivers/hisi: Prepare to add support for other hisi platforms
thermal/drivers/hisi: Add platform prefix to function name
thermal/drivers/hisi: Put platform code together
thermal/drivers/qcom-spmi: Use devm_iio_channel_get
thermal/drivers/generic-iio-adc: Switch tz request to devm version
thermal/drivers/step_wise: Fix temperature regulation misbehavior
thermal/drivers/hisi: Use round up step value
thermal/drivers/hisi: Move the clk setup in the corresponding functions
thermal/drivers/hisi: Remove mutex_lock in the code
thermal/drivers/hisi: Remove thermal data back pointer
thermal/drivers/hisi: Convert long to int
thermal/drivers/hisi: Rename and remove unused field
...
Diffstat (limited to 'drivers/thermal')
-rw-r--r-- | drivers/thermal/Kconfig | 3 | ||||
-rw-r--r-- | drivers/thermal/armada_thermal.c | 2 | ||||
-rw-r--r-- | drivers/thermal/broadcom/Kconfig | 7 | ||||
-rw-r--r-- | drivers/thermal/broadcom/Makefile | 1 | ||||
-rw-r--r-- | drivers/thermal/broadcom/brcmstb_thermal.c | 387 | ||||
-rw-r--r-- | drivers/thermal/cpu_cooling.c | 2 | ||||
-rw-r--r-- | drivers/thermal/hisi_thermal.c | 612 | ||||
-rw-r--r-- | drivers/thermal/imx_thermal.c | 104 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/processor_thermal_device.c | 6 | ||||
-rw-r--r-- | drivers/thermal/intel_bxt_pmic_thermal.c | 3 | ||||
-rw-r--r-- | drivers/thermal/intel_pch_thermal.c | 11 | ||||
-rw-r--r-- | drivers/thermal/intel_powerclamp.c | 4 | ||||
-rw-r--r-- | drivers/thermal/qcom-spmi-temp-alarm.c | 43 | ||||
-rw-r--r-- | drivers/thermal/rcar_gen3_thermal.c | 34 | ||||
-rw-r--r-- | drivers/thermal/rockchip_thermal.c | 67 | ||||
-rw-r--r-- | drivers/thermal/step_wise.c | 11 | ||||
-rw-r--r-- | drivers/thermal/tegra/soctherm.c | 2 | ||||
-rw-r--r-- | drivers/thermal/thermal-generic-adc.c | 24 | ||||
-rw-r--r-- | drivers/thermal/ti-soc-thermal/ti-thermal-common.c | 3 |
19 files changed, 1006 insertions, 320 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 07002df4f83a..315ae2926e20 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -206,6 +206,7 @@ config HISI_THERMAL config IMX_THERMAL tristate "Temperature sensor driver for Freescale i.MX SoCs" depends on (ARCH_MXC && CPU_THERMAL) || COMPILE_TEST + depends on NVMEM || !NVMEM depends on MFD_SYSCON depends on OF help @@ -408,7 +409,7 @@ config MTK_THERMAL controller present in Mediatek SoCs menu "Broadcom thermal drivers" -depends on ARCH_BCM || COMPILE_TEST +depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST source "drivers/thermal/broadcom/Kconfig" endmenu diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index ae75328945f7..706d74798cbe 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -58,7 +58,7 @@ struct armada_thermal_data { /* Test for a valid sensor value (optional) */ bool (*is_valid)(struct armada_thermal_priv *); - /* Formula coeficients: temp = (b + m * reg) / div */ + /* Formula coeficients: temp = (b - m * reg) / div */ unsigned long coef_b; unsigned long coef_m; unsigned long coef_div; diff --git a/drivers/thermal/broadcom/Kconfig b/drivers/thermal/broadcom/Kconfig index 42c098e86f84..c106a15bf7f9 100644 --- a/drivers/thermal/broadcom/Kconfig +++ b/drivers/thermal/broadcom/Kconfig @@ -6,6 +6,13 @@ config BCM2835_THERMAL help Support for thermal sensors on Broadcom bcm2835 SoCs. +config BRCMSTB_THERMAL + tristate "Broadcom STB AVS TMON thermal driver" + depends on ARCH_BRCMSTB || COMPILE_TEST + help + Enable this driver if you have a Broadcom STB SoC and would like + thermal framework support. + config BCM_NS_THERMAL tristate "Northstar thermal driver" depends on ARCH_BCM_IPROC || COMPILE_TEST diff --git a/drivers/thermal/broadcom/Makefile b/drivers/thermal/broadcom/Makefile index c6f62e4fd0ee..fae10ecafaef 100644 --- a/drivers/thermal/broadcom/Makefile +++ b/drivers/thermal/broadcom/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o +obj-$(CONFIG_BRCMSTB_THERMAL) += brcmstb_thermal.o obj-$(CONFIG_BCM_NS_THERMAL) += ns-thermal.o diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c new file mode 100644 index 000000000000..1919f91fa756 --- /dev/null +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -0,0 +1,387 @@ +/* + * Broadcom STB AVS TMON thermal sensor driver + * + * Copyright (c) 2015-2017 Broadcom + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define DRV_NAME "brcmstb_thermal" + +#define pr_fmt(fmt) DRV_NAME ": " fmt + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/irqreturn.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/thermal.h> + +#define AVS_TMON_STATUS 0x00 + #define AVS_TMON_STATUS_valid_msk BIT(11) + #define AVS_TMON_STATUS_data_msk GENMASK(10, 1) + #define AVS_TMON_STATUS_data_shift 1 + +#define AVS_TMON_EN_OVERTEMP_RESET 0x04 + #define AVS_TMON_EN_OVERTEMP_RESET_msk BIT(0) + +#define AVS_TMON_RESET_THRESH 0x08 + #define AVS_TMON_RESET_THRESH_msk GENMASK(10, 1) + #define AVS_TMON_RESET_THRESH_shift 1 + +#define AVS_TMON_INT_IDLE_TIME 0x10 + +#define AVS_TMON_EN_TEMP_INT_SRCS 0x14 + #define AVS_TMON_EN_TEMP_INT_SRCS_high BIT(1) + #define AVS_TMON_EN_TEMP_INT_SRCS_low BIT(0) + +#define AVS_TMON_INT_THRESH 0x18 + #define AVS_TMON_INT_THRESH_high_msk GENMASK(26, 17) + #define AVS_TMON_INT_THRESH_high_shift 17 + #define AVS_TMON_INT_THRESH_low_msk GENMASK(10, 1) + #define AVS_TMON_INT_THRESH_low_shift 1 + +#define AVS_TMON_TEMP_INT_CODE 0x1c +#define AVS_TMON_TP_TEST_ENABLE 0x20 + +/* Default coefficients */ +#define AVS_TMON_TEMP_SLOPE -487 +#define AVS_TMON_TEMP_OFFSET 410040 + +/* HW related temperature constants */ +#define AVS_TMON_TEMP_MAX 0x3ff +#define AVS_TMON_TEMP_MIN -88161 +#define AVS_TMON_TEMP_MASK AVS_TMON_TEMP_MAX + +enum avs_tmon_trip_type { + TMON_TRIP_TYPE_LOW = 0, + TMON_TRIP_TYPE_HIGH, + TMON_TRIP_TYPE_RESET, + TMON_TRIP_TYPE_MAX, +}; + +struct avs_tmon_trip { + /* HW bit to enable the trip */ + u32 enable_offs; + u32 enable_mask; + + /* HW field to read the trip temperature */ + u32 reg_offs; + u32 reg_msk; + int reg_shift; +}; + +static struct avs_tmon_trip avs_tmon_trips[] = { + /* Trips when temperature is below threshold */ + [TMON_TRIP_TYPE_LOW] = { + .enable_offs = AVS_TMON_EN_TEMP_INT_SRCS, + .enable_mask = AVS_TMON_EN_TEMP_INT_SRCS_low, + .reg_offs = AVS_TMON_INT_THRESH, + .reg_msk = AVS_TMON_INT_THRESH_low_msk, + .reg_shift = AVS_TMON_INT_THRESH_low_shift, + }, + /* Trips when temperature is above threshold */ + [TMON_TRIP_TYPE_HIGH] = { + .enable_offs = AVS_TMON_EN_TEMP_INT_SRCS, + .enable_mask = AVS_TMON_EN_TEMP_INT_SRCS_high, + .reg_offs = AVS_TMON_INT_THRESH, + .reg_msk = AVS_TMON_INT_THRESH_high_msk, + .reg_shift = AVS_TMON_INT_THRESH_high_shift, + }, + /* Automatically resets chip when above threshold */ + [TMON_TRIP_TYPE_RESET] = { + .enable_offs = AVS_TMON_EN_OVERTEMP_RESET, + .enable_mask = AVS_TMON_EN_OVERTEMP_RESET_msk, + .reg_offs = AVS_TMON_RESET_THRESH, + .reg_msk = AVS_TMON_RESET_THRESH_msk, + .reg_shift = AVS_TMON_RESET_THRESH_shift, + }, +}; + +struct brcmstb_thermal_priv { + void __iomem *tmon_base; + struct device *dev; + struct thermal_zone_device *thermal; +}; + +static void avs_tmon_get_coeffs(struct thermal_zone_device *tz, int *slope, + int *offset) +{ + *slope = thermal_zone_get_slope(tz); + *offset = thermal_zone_get_offset(tz); +} + +/* Convert a HW code to a temperature reading (millidegree celsius) */ +static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, + u32 code) +{ + const int val = code & AVS_TMON_TEMP_MASK; + int slope, offset; + + avs_tmon_get_coeffs(tz, &slope, &offset); + + return slope * val + offset; +} + +/* + * Convert a temperature value (millidegree celsius) to a HW code + * + * @temp: temperature to convert + * @low: if true, round toward the low side + */ +static inline u32 avs_tmon_temp_to_code(struct thermal_zone_device *tz, + int temp, bool low) +{ + int slope, offset; + + if (temp < AVS_TMON_TEMP_MIN) + return AVS_TMON_TEMP_MAX; /* Maximum code value */ + + avs_tmon_get_coeffs(tz, &slope, &offset); + + if (temp >= offset) + return 0; /* Minimum code value */ + + if (low) + return (u32)(DIV_ROUND_UP(offset - temp, abs(slope))); + else + return (u32)((offset - temp) / abs(slope)); +} + +static int brcmstb_get_temp(void *data, int *temp) +{ + struct brcmstb_thermal_priv *priv = data; + u32 val; + long t; + + val = __raw_readl(priv->tmon_base + AVS_TMON_STATUS); + + if (!(val & AVS_TMON_STATUS_valid_msk)) { + dev_err(priv->dev, "reading not valid\n"); + return -EIO; + } + + val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift; + + t = avs_tmon_code_to_temp(priv->thermal, val); + if (t < 0) + *temp = 0; + else + *temp = t; + + return 0; +} + +static void avs_tmon_trip_enable(struct brcmstb_thermal_priv *priv, + enum avs_tmon_trip_type type, int en) +{ + struct avs_tmon_trip *trip = &avs_tmon_trips[type]; + u32 val = __raw_readl(priv->tmon_base + trip->enable_offs); + + dev_dbg(priv->dev, "%sable trip, type %d\n", en ? "en" : "dis", type); + + if (en) + val |= trip->enable_mask; + else + val &= ~trip->enable_mask; + + __raw_writel(val, priv->tmon_base + trip->enable_offs); +} + +static int avs_tmon_get_trip_temp(struct brcmstb_thermal_priv *priv, + enum avs_tmon_trip_type type) +{ + struct avs_tmon_trip *trip = &avs_tmon_trips[type]; + u32 val = __raw_readl(priv->tmon_base + trip->reg_offs); + + val &= trip->reg_msk; + val >>= trip->reg_shift; + + return avs_tmon_code_to_temp(priv->thermal, val); +} + +static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv, + enum avs_tmon_trip_type type, + int temp) +{ + struct avs_tmon_trip *trip = &avs_tmon_trips[type]; + u32 val, orig; + + dev_dbg(priv->dev, "set temp %d to %d\n", type, temp); + + /* round toward low temp for the low interrupt */ + val = avs_tmon_temp_to_code(priv->thermal, temp, + type == TMON_TRIP_TYPE_LOW); + + val <<= trip->reg_shift; + val &= trip->reg_msk; + + orig = __raw_readl(priv->tmon_base + trip->reg_offs); + orig &= ~trip->reg_msk; + orig |= val; + __raw_writel(orig, priv->tmon_base + trip->reg_offs); +} + +static int avs_tmon_get_intr_temp(struct brcmstb_thermal_priv *priv) +{ + u32 val; + + val = __raw_readl(priv->tmon_base + AVS_TMON_TEMP_INT_CODE); + return avs_tmon_code_to_temp(priv->thermal, val); +} + +static irqreturn_t brcmstb_tmon_irq_thread(int irq, void *data) +{ + struct brcmstb_thermal_priv *priv = data; + int low, high, intr; + + low = avs_tmon_get_trip_temp(priv, TMON_TRIP_TYPE_LOW); + high = avs_tmon_get_trip_temp(priv, TMON_TRIP_TYPE_HIGH); + intr = avs_tmon_get_intr_temp(priv); + + dev_dbg(priv->dev, "low/intr/high: %d/%d/%d\n", + low, intr, high); + + /* Disable high-temp until next threshold shift */ + if (intr >= high) + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0); + /* Disable low-temp until next threshold shift */ + if (intr <= low) + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0); + + /* + * Notify using the interrupt temperature, in case the temperature + * changes before it can next be read out + */ + thermal_zone_device_update(priv->thermal, intr); + + return IRQ_HANDLED; +} + +static int brcmstb_set_trips(void *data, int low, int high) +{ + struct brcmstb_thermal_priv *priv = data; + + dev_dbg(priv->dev, "set trips %d <--> %d\n", low, high); + + /* + * Disable low-temp if "low" is too small. As per thermal framework + * API, we use -INT_MAX rather than INT_MIN. + */ + if (low <= -INT_MAX) { + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0); + } else { + avs_tmon_set_trip_temp(priv, TMON_TRIP_TYPE_LOW, low); + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 1); + } + + /* Disable high-temp if "high" is too big. */ + if (high == INT_MAX) { + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0); + } else { + avs_tmon_set_trip_temp(priv, TMON_TRIP_TYPE_HIGH, high); + avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 1); + } + + return 0; +} + +static struct thermal_zone_of_device_ops of_ops = { + .get_temp = brcmstb_get_temp, + .set_trips = brcmstb_set_trips, +}; + +static const struct of_device_id brcmstb_thermal_id_table[] = { + { .compatible = "brcm,avs-tmon" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table); + +static int brcmstb_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal; + struct brcmstb_thermal_priv *priv; + struct resource *res; + int irq, ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->tmon_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->tmon_base)) + return PTR_ERR(priv->tmon_base); + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + thermal = thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &of_ops); + if (IS_ERR(thermal)) { + ret = PTR_ERR(thermal); + dev_err(&pdev->dev, "could not register sensor: %d\n", ret); + return ret; + } + + priv->thermal = thermal; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get IRQ\n"); + ret = irq; + goto err; + } + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + brcmstb_tmon_irq_thread, IRQF_ONESHOT, + DRV_NAME, priv); + if (ret < 0) { + dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); + goto err; + } + + dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n"); + + return 0; + +err: + thermal_zone_of_sensor_unregister(&pdev->dev, thermal); + return ret; +} + +static int brcmstb_thermal_exit(struct platform_device *pdev) +{ + struct brcmstb_thermal_priv *priv = platform_get_drvdata(pdev); + struct thermal_zone_device *thermal = priv->thermal; + + if (thermal) + thermal_zone_of_sensor_unregister(&pdev->dev, priv->thermal); + + return 0; +} + +static struct platform_driver brcmstb_thermal_driver = { + .probe = brcmstb_thermal_probe, + .remove = brcmstb_thermal_exit, + .driver = { + .name = DRV_NAME, + .of_match_table = brcmstb_thermal_id_table, + }, +}; +module_platform_driver(brcmstb_thermal_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("Broadcom STB AVS TMON thermal driver"); diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 908a8014cf76..dc63aba092e4 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -696,7 +696,7 @@ __cpufreq_cooling_register(struct device_node *np, bool first; if (IS_ERR_OR_NULL(policy)) { - pr_err("%s: cpufreq policy isn't valid: %p", __func__, policy); + pr_err("%s: cpufreq policy isn't valid: %p\n", __func__, policy); return ERR_PTR(-EINVAL); } diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index bd3572c41585..2d855a96cdd9 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -23,222 +23,450 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/of_device.h> #include "thermal_core.h" -#define TEMP0_TH (0x4) -#define TEMP0_RST_TH (0x8) -#define TEMP0_CFG (0xC) -#define TEMP0_EN (0x10) -#define TEMP0_INT_EN (0x14) -#define TEMP0_INT_CLR (0x18) -#define TEMP0_RST_MSK (0x1C) -#define TEMP0_VALUE (0x28) - -#define HISI_TEMP_BASE (-60) -#define HISI_TEMP_RESET (100000) - -#define HISI_MAX_SENSORS 4 +#define HI6220_TEMP0_LAG (0x0) +#define HI6220_TEMP0_TH (0x4) +#define HI6220_TEMP0_RST_TH (0x8) +#define HI6220_TEMP0_CFG (0xC) +#define HI6220_TEMP0_CFG_SS_MSK (0xF000) +#define HI6220_TEMP0_CFG_HDAK_MSK (0x30) +#define HI6220_TEMP0_EN (0x10) +#define HI6220_TEMP0_INT_EN (0x14) +#define HI6220_TEMP0_INT_CLR (0x18) +#define HI6220_TEMP0_RST_MSK (0x1C) +#define HI6220_TEMP0_VALUE (0x28) + +#define HI3660_OFFSET(chan) ((chan) * 0x40) +#define HI3660_TEMP(chan) (HI3660_OFFSET(chan) + 0x1C) +#define HI3660_TH(chan) (HI3660_OFFSET(chan) + 0x20) +#define HI3660_LAG(chan) (HI3660_OFFSET(chan) + 0x28) +#define HI3660_INT_EN(chan) (HI3660_OFFSET(chan) + 0x2C) +#define HI3660_INT_CLR(chan) (HI3660_OFFSET(chan) + 0x30) + +#define HI6220_TEMP_BASE (-60000) +#define HI6220_TEMP_RESET (100000) +#define HI6220_TEMP_STEP (785) +#define HI6220_TEMP_LAG (3500) + +#define HI3660_TEMP_BASE (-63780) +#define HI3660_TEMP_STEP (205) +#define HI3660_TEMP_LAG (4000) + +#define HI6220_DEFAULT_SENSOR 2 +#define HI3660_DEFAULT_SENSOR 1 struct hisi_thermal_sensor { - struct hisi_thermal_data *thermal; struct thermal_zone_device *tzd; - - long sensor_temp; uint32_t id; uint32_t thres_temp; }; struct hisi_thermal_data { - struct mutex thermal_lock; /* protects register data */ + int (*get_temp)(struct hisi_thermal_data *data); + int (*enable_sensor)(struct hisi_thermal_data *data); + int (*disable_sensor)(struct hisi_thermal_data *data); + int (*irq_handler)(struct hisi_thermal_data *data); struct platform_device *pdev; struct clk *clk; - struct hisi_thermal_sensor sensors[HISI_MAX_SENSORS]; - - int irq, irq_bind_sensor; - bool irq_enabled; - + struct hisi_thermal_sensor sensor; void __iomem *regs; + int irq; }; -/* in millicelsius */ -static inline int _step_to_temp(int step) +/* + * The temperature computation on the tsensor is as follow: + * Unit: millidegree Celsius + * Step: 200/255 (0.7843) + * Temperature base: -60°C + * + * The register is programmed in temperature steps, every step is 785 + * millidegree and begins at -60 000 m°C + * + * The temperature from the steps: + * + * Temp = TempBase + (steps x 785) + * + * and the steps from the temperature: + * + * steps = (Temp - TempBase) / 785 + * + */ +static inline int hi6220_thermal_step_to_temp(int step) { - /* - * Every step equals (1 * 200) / 255 celsius, and finally - * need convert to millicelsius. - */ - return (HISI_TEMP_BASE * 1000 + (step * 200000 / 255)); + return HI6220_TEMP_BASE + (step * HI6220_TEMP_STEP); } -static inline long _temp_to_step(long temp) +static inline int hi6220_thermal_temp_to_step(int temp) { - return ((temp - HISI_TEMP_BASE * 1000) * 255) / 200000; + return DIV_ROUND_UP(temp - HI6220_TEMP_BASE, HI6220_TEMP_STEP); } -static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data, - struct hisi_thermal_sensor *sensor) +/* + * for Hi3660, + * Step: 189/922 (0.205) + * Temperature base: -63.780°C + * + * The register is programmed in temperature steps, every step is 205 + * millidegree and begins at -63 780 m°C + */ +static inline int hi3660_thermal_step_to_temp(int step) { - long val; + return HI3660_TEMP_BASE + step * HI3660_TEMP_STEP; +} - mutex_lock(&data->thermal_lock); +static inline int hi3660_thermal_temp_to_step(int temp) +{ + return DIV_ROUND_UP(temp - HI3660_TEMP_BASE, HI3660_TEMP_STEP); +} - /* disable interrupt */ - writel(0x0, data->regs + TEMP0_INT_EN); - writel(0x1, data->regs + TEMP0_INT_CLR); +/* + * The lag register contains 5 bits encoding the temperature in steps. + * + * Each time the temperature crosses the threshold boundary, an + * interrupt is raised. It could be when the temperature is going + * above the threshold or below. However, if the temperature is + * fluctuating around this value due to the load, we can receive + * several interrupts which may not desired. + * + * We can setup a temperature representing the delta between the + * threshold and the current temperature when the temperature is + * decreasing. + * + * For instance: the lag register is 5°C, the threshold is 65°C, when + * the temperature reaches 65°C an interrupt is raised and when the + * temperature decrease to 65°C - 5°C another interrupt is raised. + * + * A very short lag can lead to an interrupt storm, a long lag + * increase the latency to react to the temperature changes. In our + * case, that is not really a problem as we are polling the + * temperature. + * + * [0:4] : lag register + * + * The temperature is coded in steps, cf. HI6220_TEMP_STEP. + * + * Min : 0x00 : 0.0 °C + * Max : 0x1F : 24.3 °C + * + * The 'value' parameter is in milliCelsius. + */ +static inline void hi6220_thermal_set_lag(void __iomem *addr, int value) +{ + writel(DIV_ROUND_UP(value, HI6220_TEMP_STEP) & 0x1F, + addr + HI6220_TEMP0_LAG); +} - /* disable module firstly */ - writel(0x0, data->regs + TEMP0_EN); +static inline void hi6220_thermal_alarm_clear(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_INT_CLR); +} - /* select sensor id */ - writel((sensor->id << 12), data->regs + TEMP0_CFG); +static inline void hi6220_thermal_alarm_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_INT_EN); +} - /* enable module */ - writel(0x1, data->regs + TEMP0_EN); +static inline void hi6220_thermal_alarm_set(void __iomem *addr, int temp) +{ + writel(hi6220_thermal_temp_to_step(temp) | 0x0FFFFFF00, + addr + HI6220_TEMP0_TH); +} - usleep_range(3000, 5000); +static inline void hi6220_thermal_reset_set(void __iomem *addr, int temp) +{ + writel(hi6220_thermal_temp_to_step(temp), addr + HI6220_TEMP0_RST_TH); +} - val = readl(data->regs + TEMP0_VALUE); - val = _step_to_temp(val); +static inline void hi6220_thermal_reset_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_RST_MSK); +} - mutex_unlock(&data->thermal_lock); +static inline void hi6220_thermal_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_EN); +} - return val; +static inline int hi6220_thermal_get_temperature(void __iomem *addr) +{ + return hi6220_thermal_step_to_temp(readl(addr + HI6220_TEMP0_VALUE)); } -static void hisi_thermal_enable_bind_irq_sensor - (struct hisi_thermal_data *data) +/* + * [0:6] lag register + * + * The temperature is coded in steps, cf. HI3660_TEMP_STEP. + * + * Min : 0x00 : 0.0 °C + * Max : 0x7F : 26.0 °C + * + */ +static inline void hi3660_thermal_set_lag(void __iomem *addr, + int id, int value) { - struct hisi_thermal_sensor *sensor; + writel(DIV_ROUND_UP(value, HI3660_TEMP_STEP) & 0x7F, + addr + HI3660_LAG(id)); +} - mutex_lock(&data->thermal_lock); +static inline void hi3660_thermal_alarm_clear(void __iomem *addr, + int id, int value) +{ + writel(value, addr + HI3660_INT_CLR(id)); +} - sensor = &data->sensors[data->irq_bind_sensor]; +static inline void hi3660_thermal_alarm_enable(void __iomem *addr, + int id, int value) +{ + writel(value, addr + HI3660_INT_EN(id)); +} - /* setting the hdak time */ - writel(0x0, data->regs + TEMP0_CFG); +static inline void hi3660_thermal_alarm_set(void __iomem *addr, + int id, int value) +{ + writel(value, addr + HI3660_TH(id)); +} + +static inline int hi3660_thermal_get_temperature(void __iomem *addr, int id) +{ + return hi3660_thermal_step_to_temp(readl(addr + HI3660_TEMP(id))); +} + +/* + * Temperature configuration register - Sensor selection + * + * Bits [19:12] + * + * 0x0: local sensor (default) + * 0x1: remote sensor 1 (ACPU cluster 1) + * 0x2: remote sensor 2 (ACPU cluster 0) + * 0x3: remote sensor 3 (G3D) + */ +static inline void hi6220_thermal_sensor_select(void __iomem *addr, int sensor) +{ + writel((readl(addr + HI6220_TEMP0_CFG) & ~HI6220_TEMP0_CFG_SS_MSK) | + (sensor << 12), addr + HI6220_TEMP0_CFG); +} + +/* + * Temperature configuration register - Hdak conversion polling interval + * + * Bits [5:4] + * + * 0x0 : 0.768 ms + * 0x1 : 6.144 ms + * 0x2 : 49.152 ms + * 0x3 : 393.216 ms + */ +static inline void hi6220_thermal_hdak_set(void __iomem *addr, int value) +{ + writel((readl(addr + HI6220_TEMP0_CFG) & ~HI6220_TEMP0_CFG_HDAK_MSK) | + (value << 4), addr + HI6220_TEMP0_CFG); +} + +static int hi6220_thermal_irq_handler(struct hisi_thermal_data *data) +{ + hi6220_thermal_alarm_clear(data->regs, 1); + return 0; +} + +static int hi3660_thermal_irq_handler(struct hisi_thermal_data *data) +{ + hi3660_thermal_alarm_clear(data->regs, data->sensor.id, 1); + return 0; +} + +static int hi6220_thermal_get_temp(struct hisi_thermal_data *data) +{ + return hi6220_thermal_get_temperature(data->regs); +} + +static int hi3660_thermal_get_temp(struct hisi_thermal_data *data) +{ + return hi3660_thermal_get_temperature(data->regs, data->sensor.id); +} + +static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data) +{ + /* disable sensor module */ + hi6220_thermal_enable(data->regs, 0); + hi6220_thermal_alarm_enable(data->regs, 0); + hi6220_thermal_reset_enable(data->regs, 0); + + clk_disable_unprepare(data->clk); + + return 0; +} + +static int hi3660_thermal_disable_sensor(struct hisi_thermal_data *data) +{ + /* disable sensor module */ + hi3660_thermal_alarm_enable(data->regs, data->sensor.id, 0); + return 0; +} + +static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data) +{ + struct hisi_thermal_sensor *sensor = &data->sensor; + int ret; + + /* enable clock for tsensor */ + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; /* disable module firstly */ - writel(0x0, data->regs + TEMP0_RST_MSK); - writel(0x0, data->regs + TEMP0_EN); + hi6220_thermal_reset_enable(data->regs, 0); + hi6220_thermal_enable(data->regs, 0); /* select sensor id */ - writel((sensor->id << 12), data->regs + TEMP0_CFG); + hi6220_thermal_sensor_select(data->regs, sensor->id); + + /* setting the hdak time */ + hi6220_thermal_hdak_set(data->regs, 0); + + /* setting lag value between current temp and the threshold */ + hi6220_thermal_set_lag(data->regs, HI6220_TEMP_LAG); /* enable for interrupt */ - writel(_temp_to_step(sensor->thres_temp) | 0x0FFFFFF00, - data->regs + TEMP0_TH); + hi6220_thermal_alarm_set(data->regs, sensor->thres_temp); - writel(_temp_to_step(HISI_TEMP_RESET), data->regs + TEMP0_RST_TH); + hi6220_thermal_reset_set(data->regs, HI6220_TEMP_RESET); /* enable module */ - writel(0x1, data->regs + TEMP0_RST_MSK); - writel(0x1, data->regs + TEMP0_EN); - - writel(0x0, data->regs + TEMP0_INT_CLR); - writel(0x1, data->regs + TEMP0_INT_EN); + hi6220_thermal_reset_enable(data->regs, 1); + hi6220_thermal_enable(data->regs, 1); - usleep_range(3000, 5000); + hi6220_thermal_alarm_clear(data->regs, 0); + hi6220_thermal_alarm_enable(data->regs, 1); - mutex_unlock(&data->thermal_lock); + return 0; } -static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data) +static int hi3660_thermal_enable_sensor(struct hisi_thermal_data *data) { - mutex_lock(&data->thermal_lock); + unsigned int value; + struct hisi_thermal_sensor *sensor = &data->sensor; - /* disable sensor module */ - writel(0x0, data->regs + TEMP0_INT_EN); - writel(0x0, data->regs + TEMP0_RST_MSK); - writel(0x0, data->regs + TEMP0_EN); + /* disable interrupt */ + hi3660_thermal_alarm_enable(data->regs, sensor->id, 0); - mutex_unlock(&data->thermal_lock); -} + /* setting lag value between current temp and the threshold */ + hi3660_thermal_set_lag(data->regs, sensor->id, HI3660_TEMP_LAG); -static int hisi_thermal_get_temp(void *_sensor, int *temp) -{ - struct hisi_thermal_sensor *sensor = _sensor; - struct hisi_thermal_data *data = sensor->thermal; + /* set interrupt threshold */ + value = hi3660_thermal_temp_to_step(sensor->thres_temp); + hi3660_thermal_alarm_set(data->regs, sensor->id, value); - int sensor_id = -1, i; - long max_temp = 0; + /* enable interrupt */ + hi3660_thermal_alarm_clear(data->regs, sensor->id, 1); + hi3660_thermal_alarm_enable(data->regs, sensor->id, 1); - *temp = hisi_thermal_get_sensor_temp(data, sensor); + return 0; +} - sensor->sensor_temp = *temp; +static int hi6220_thermal_probe(struct hisi_thermal_data *data) +{ + struct platform_device *pdev = data->pdev; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; - for (i = 0; i < HISI_MAX_SENSORS; i++) { - if (!data->sensors[i].tzd) - continue; + data->get_temp = hi6220_thermal_get_temp; + data->enable_sensor = hi6220_thermal_enable_sensor; + data->disable_sensor = hi6220_thermal_disable_sensor; + data->irq_handler = hi6220_thermal_irq_handler; - if (data->sensors[i].sensor_temp >= max_temp) { - max_temp = data->sensors[i].sensor_temp; - sensor_id = i; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(data->regs)) { + dev_err(dev, "failed to get io address\n"); + return PTR_ERR(data->regs); } - /* 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); - - dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n", - sensor->id, data->irq_enabled, *temp, sensor->thres_temp); - /* - * Bind irq to sensor for two cases: - * Reenable alarm IRQ if temperature below threshold; - * if irq has been enabled, always set it; - */ - if (data->irq_enabled) { - hisi_thermal_enable_bind_irq_sensor(data); - return 0; + data->clk = devm_clk_get(dev, "thermal_clk"); + if (IS_ERR(data->clk)) { + ret = PTR_ERR(data->clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get thermal clk: %d\n", ret); + return ret; } - if (max_temp < sensor->thres_temp) { - data->irq_enabled = true; - hisi_thermal_enable_bind_irq_sensor(data); - enable_irq(data->irq); - } + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) + return data->irq; + + data->sensor.id = HI6220_DEFAULT_SENSOR; return 0; } -static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = { - .get_temp = hisi_thermal_get_temp, -}; +static int hi3660_thermal_probe(struct hisi_thermal_data *data) +{ + struct platform_device *pdev = data->pdev; + struct device *dev = &pdev->dev; + struct resource *res; + + data->get_temp = hi3660_thermal_get_temp; + data->enable_sensor = hi3660_thermal_enable_sensor; + data->disable_sensor = hi3660_thermal_disable_sensor; + data->irq_handler = hi3660_thermal_irq_handler; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(data->regs)) { + dev_err(dev, "failed to get io address\n"); + return PTR_ERR(data->regs); + } -static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev) + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) + return data->irq; + + data->sensor.id = HI3660_DEFAULT_SENSOR; + + return 0; +} + +static int hisi_thermal_get_temp(void *__data, int *temp) { - struct hisi_thermal_data *data = dev; + struct hisi_thermal_data *data = __data; + struct hisi_thermal_sensor *sensor = &data->sensor; - disable_irq_nosync(irq); - data->irq_enabled = false; + *temp = data->get_temp(data); - return IRQ_WAKE_THREAD; + dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n", + sensor->id, *temp, sensor->thres_temp); + + return 0; } +static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = { + .get_temp = hisi_thermal_get_temp, +}; + static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) { struct hisi_thermal_data *data = dev; - struct hisi_thermal_sensor *sensor; - int i; + struct hisi_thermal_sensor *sensor = &data->sensor; + int temp = 0; - mutex_lock(&data->thermal_lock); - sensor = &data->sensors[data->irq_bind_sensor]; + data->irq_handler(data); - dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n", - sensor->thres_temp / 1000); - mutex_unlock(&data->thermal_lock); + hisi_thermal_get_temp(data, &temp); - for (i = 0; i < HISI_MAX_SENSORS; i++) { - if (!data->sensors[i].tzd) - continue; + if (temp >= sensor->thres_temp) { + dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n", + temp, sensor->thres_temp); - thermal_zone_device_update(data->sensors[i].tzd, + thermal_zone_device_update(data->sensor.tzd, THERMAL_EVENT_UNSPECIFIED); + + } else { + dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n", + temp, sensor->thres_temp); } return IRQ_HANDLED; @@ -246,17 +474,14 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) static int hisi_thermal_register_sensor(struct platform_device *pdev, struct hisi_thermal_data *data, - struct hisi_thermal_sensor *sensor, - int index) + struct hisi_thermal_sensor *sensor) { int ret, i; const struct thermal_trip *trip; - sensor->id = index; - sensor->thermal = data; - sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, - sensor->id, sensor, &hisi_of_thermal_ops); + sensor->id, data, + &hisi_of_thermal_ops); if (IS_ERR(sensor->tzd)) { ret = PTR_ERR(sensor->tzd); sensor->tzd = NULL; @@ -278,7 +503,14 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev, } static const struct of_device_id of_hisi_thermal_match[] = { - { .compatible = "hisilicon,tsensor" }, + { + .compatible = "hisilicon,tsensor", + .data = hi6220_thermal_probe + }, + { + .compatible = "hisilicon,hi3660-tsensor", + .data = hi3660_thermal_probe + }, { /* end */ } }; MODULE_DEVICE_TABLE(of, of_hisi_thermal_match); @@ -295,88 +527,63 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor, static int hisi_thermal_probe(struct platform_device *pdev) { struct hisi_thermal_data *data; - struct resource *res; - int i; + int const (*platform_probe)(struct hisi_thermal_data *); + struct device *dev = &pdev->dev; int ret; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - mutex_init(&data->thermal_lock); data->pdev = pdev; + platform_set_drvdata(pdev, data); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(data->regs)) { - dev_err(&pdev->dev, "failed to get io address\n"); - return PTR_ERR(data->regs); + platform_probe = of_device_get_match_data(dev); + if (!platform_probe) { + dev_err(dev, "failed to get probe func\n"); + return -EINVAL; } - data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) - return data->irq; - - ret = devm_request_threaded_irq(&pdev->dev, data->irq, - hisi_thermal_alarm_irq, - hisi_thermal_alarm_irq_thread, - 0, "hisi_thermal", data); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); + ret = platform_probe(data); + if (ret) return ret; - } - platform_set_drvdata(pdev, data); - - data->clk = devm_clk_get(&pdev->dev, "thermal_clk"); - if (IS_ERR(data->clk)) { - ret = PTR_ERR(data->clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to get thermal clk: %d\n", ret); + ret = hisi_thermal_register_sensor(pdev, data, + &data->sensor); + if (ret) { + dev_err(dev, "failed to register thermal sensor: %d\n", ret); return ret; } - /* enable clock for thermal */ - ret = clk_prepare_enable(data->clk); + ret = data->enable_sensor(data); if (ret) { - dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); + dev_err(dev, "Failed to setup the sensor: %d\n", ret); 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) - dev_err(&pdev->dev, - "failed to register thermal sensor: %d\n", ret); - else - hisi_thermal_toggle_sensor(&data->sensors[i], true); + if (data->irq) { + ret = devm_request_threaded_irq(dev, data->irq, NULL, + hisi_thermal_alarm_irq_thread, + IRQF_ONESHOT, "hisi_thermal", data); + if (ret < 0) { + dev_err(dev, "failed to request alarm irq: %d\n", ret); + return ret; + } } + hisi_thermal_toggle_sensor(&data->sensor, true); + return 0; } static int hisi_thermal_remove(struct platform_device *pdev) { struct hisi_thermal_data *data = platform_get_drvdata(pdev); - int i; + struct hisi_thermal_sensor *sensor = &data->sensor; - for (i = 0; i < HISI_MAX_SENSORS; i++) { - struct hisi_thermal_sensor *sensor = &data->sensors[i]; + hisi_thermal_toggle_sensor(sensor, false); - if (!sensor->tzd) - continue; - - hisi_thermal_toggle_sensor(sensor, false); - } - - hisi_thermal_disable_sensor(data); - clk_disable_unprepare(data->clk); + data->disable_sensor(data); return 0; } @@ -386,10 +593,7 @@ static int hisi_thermal_suspend(struct device *dev) { struct hisi_thermal_data *data = dev_get_drvdata(dev); - hisi_thermal_disable_sensor(data); - data->irq_enabled = false; - - clk_disable_unprepare(data->clk); + data->disable_sensor(data); return 0; } @@ -397,16 +601,8 @@ static int hisi_thermal_suspend(struct device *dev) static int hisi_thermal_resume(struct device *dev) { struct hisi_thermal_data *data = dev_get_drvdata(dev); - int ret; - ret = clk_prepare_enable(data->clk); - if (ret) - return ret; - - data->irq_enabled = true; - hisi_thermal_enable_bind_irq_sensor(data); - - return 0; + return data->enable_sensor(data); } #endif diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 4798b4b1fd77..e7d4ffc3de7f 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -25,6 +25,7 @@ #include <linux/slab.h> #include <linux/thermal.h> #include <linux/types.h> +#include <linux/nvmem-consumer.h> #define REG_SET 0x4 #define REG_CLR 0x8 @@ -94,7 +95,7 @@ struct imx_thermal_data { struct thermal_cooling_device *cdev; enum thermal_device_mode mode; struct regmap *tempmon; - u32 c1, c2; /* See formula in imx_get_sensor_data() */ + u32 c1, c2; /* See formula in imx_init_calib() */ int temp_passive; int temp_critical; int temp_max; @@ -177,7 +178,7 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp) n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT; - /* See imx_get_sensor_data() for formula derivation */ + /* See imx_init_calib() for formula derivation */ *temp = data->c2 - n_meas * data->c1; /* Update alarm value to next higher trip point for TEMPMON_IMX6Q */ @@ -346,29 +347,12 @@ static struct thermal_zone_device_ops imx_tz_ops = { .set_trip_temp = imx_set_trip_temp, }; -static int imx_get_sensor_data(struct platform_device *pdev) +static int imx_init_calib(struct platform_device *pdev, u32 val) { struct imx_thermal_data *data = platform_get_drvdata(pdev); - struct regmap *map; int t1, n1; - int ret; - u32 val; u64 temp64; - map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "fsl,tempmon-data"); - if (IS_ERR(map)) { - ret = PTR_ERR(map); - dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret); - return ret; - } - - ret = regmap_read(map, OCOTP_ANA1, &val); - if (ret) { - dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret); - return ret; - } - if (val == 0 || val == ~0) { dev_err(&pdev->dev, "invalid sensor calibration data\n"); return -EINVAL; @@ -405,12 +389,12 @@ static int imx_get_sensor_data(struct platform_device *pdev) data->c1 = temp64; data->c2 = n1 * data->c1 + 1000 * t1; - /* use OTP for thermal grade */ - ret = regmap_read(map, OCOTP_MEM0, &val); - if (ret) { - dev_err(&pdev->dev, "failed to read temp grade: %d\n", ret); - return ret; - } + return 0; +} + +static void imx_init_temp_grade(struct platform_device *pdev, u32 val) +{ + struct imx_thermal_data *data = platform_get_drvdata(pdev); /* The maximum die temp is specified by the Temperature Grade */ switch ((val >> 6) & 0x3) { @@ -438,6 +422,55 @@ static int imx_get_sensor_data(struct platform_device *pdev) */ data->temp_critical = data->temp_max - (1000 * 5); data->temp_passive = data->temp_max - (1000 * 10); +} + +static int imx_init_from_tempmon_data(struct platform_device *pdev) +{ + struct regmap *map; + int ret; + u32 val; + + map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "fsl,tempmon-data"); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret); + return ret; + } + + ret = regmap_read(map, OCOTP_ANA1, &val); + if (ret) { + dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret); + return ret; + } + ret = imx_init_calib(pdev, val); + if (ret) + return ret; + + ret = regmap_read(map, OCOTP_MEM0, &val); + if (ret) { + dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret); + return ret; + } + imx_init_temp_grade(pdev, val); + + return 0; +} + +static int imx_init_from_nvmem_cells(struct platform_device *pdev) +{ + int ret; + u32 val; + + ret = nvmem_cell_read_u32(&pdev->dev, "calib", &val); + if (ret) + return ret; + imx_init_calib(pdev, val); + + ret = nvmem_cell_read_u32(&pdev->dev, "temp_grade", &val); + if (ret) + return ret; + imx_init_temp_grade(pdev, val); return 0; } @@ -514,10 +547,21 @@ static int imx_thermal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - ret = imx_get_sensor_data(pdev); - if (ret) { - dev_err(&pdev->dev, "failed to get sensor data\n"); - return ret; + if (of_find_property(pdev->dev.of_node, "nvmem-cells", NULL)) { + ret = imx_init_from_nvmem_cells(pdev); + if (ret == -EPROBE_DEFER) + return ret; + if (ret) { + dev_err(&pdev->dev, "failed to init from nvmem: %d\n", + ret); + return ret; + } + } else { + ret = imx_init_from_tempmon_data(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to init from from fsl,tempmon-data\n"); + return ret; + } } /* Make sure sensor is in known good state for measurements */ diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index f02341f7134d..80bbf9ce2fb6 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -30,6 +30,10 @@ /* Skylake thermal reporting device */ #define PCI_DEVICE_ID_PROC_SKL_THERMAL 0x1903 +/* CannonLake thermal reporting device */ +#define PCI_DEVICE_ID_PROC_CNL_THERMAL 0x5a03 +#define PCI_DEVICE_ID_PROC_CFL_THERMAL 0x3E83 + /* Braswell thermal reporting device */ #define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC @@ -461,6 +465,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT1_THERMAL)}, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTX_THERMAL)}, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTP_THERMAL)}, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CNL_THERMAL)}, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CFL_THERMAL)}, { 0, }, }; diff --git a/drivers/thermal/intel_bxt_pmic_thermal.c b/drivers/thermal/intel_bxt_pmic_thermal.c index ef6b32242ccb..94cfd0064c43 100644 --- a/drivers/thermal/intel_bxt_pmic_thermal.c +++ b/drivers/thermal/intel_bxt_pmic_thermal.c @@ -166,7 +166,7 @@ static irqreturn_t pmic_thermal_irq_handler(int irq, void *data) struct pmic_thermal_data *td; struct intel_soc_pmic *pmic; struct regmap *regmap; - u8 reg_val, mask, irq_stat, trip; + u8 reg_val, mask, irq_stat; u16 reg, evt_stat_reg; int i, j, ret; @@ -201,7 +201,6 @@ static irqreturn_t pmic_thermal_irq_handler(int irq, void *data) if (regmap_read(regmap, evt_stat_reg, &ret)) return IRQ_HANDLED; - trip = td->maps[i].trip_config[j].trip_num; tzd = thermal_zone_get_zone_by_name(td->maps[i].handle); if (!IS_ERR(tzd)) thermal_zone_device_update(tzd, diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel_pch_thermal.c index c60b1cfcc64e..8a7f69b4b022 100644 --- a/drivers/thermal/intel_pch_thermal.c +++ b/drivers/thermal/intel_pch_thermal.c @@ -30,6 +30,8 @@ #define PCH_THERMAL_DID_WPT 0x9CA4 /* Wildcat Point */ #define PCH_THERMAL_DID_SKL 0x9D31 /* Skylake PCH */ #define PCH_THERMAL_DID_SKL_H 0xA131 /* Skylake PCH 100 series */ +#define PCH_THERMAL_DID_CNL 0x9Df9 /* CNL PCH */ +#define PCH_THERMAL_DID_CNL_H 0xA379 /* CNL-H PCH */ /* Wildcat Point-LP PCH Thermal registers */ #define WPT_TEMP 0x0000 /* Temperature */ @@ -278,6 +280,7 @@ enum board_ids { board_hsw, board_wpt, board_skl, + board_cnl, }; static const struct board_info { @@ -296,6 +299,10 @@ static const struct board_info { .name = "pch_skylake", .ops = &pch_dev_ops_wpt, }, + [board_cnl] = { + .name = "pch_cannonlake", + .ops = &pch_dev_ops_wpt, + }, }; static int intel_pch_thermal_probe(struct pci_dev *pdev, @@ -398,6 +405,10 @@ static const struct pci_device_id intel_pch_thermal_id[] = { .driver_data = board_skl, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H), .driver_data = board_skl, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL), + .driver_data = board_cnl, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H), + .driver_data = board_cnl, }, { 0, }, }; MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id); diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index d718cd179ddb..4540e892b61d 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -675,13 +675,13 @@ static int __init powerclamp_probe(void) { if (!x86_match_cpu(intel_powerclamp_ids)) { - pr_err("CPU does not support MWAIT"); + pr_err("CPU does not support MWAIT\n"); return -ENODEV; } /* The goal for idle time alignment is to achieve package cstate. */ if (!has_pkg_state_counter()) { - pr_info("No package C-state available"); + pr_info("No package C-state available\n"); return -ENODEV; } diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c index f50241962ad2..95f987d5aa71 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom-spmi-temp-alarm.c @@ -125,7 +125,7 @@ static int qpnp_tm_get_temp(void *data, int *temp) if (!temp) return -EINVAL; - if (IS_ERR(chip->adc)) { + if (!chip->adc) { ret = qpnp_tm_update_temp_no_adc(chip); if (ret < 0) return ret; @@ -224,67 +224,53 @@ static int qpnp_tm_probe(struct platform_device *pdev) return irq; /* ADC based measurements are optional */ - chip->adc = iio_channel_get(&pdev->dev, "thermal"); - if (PTR_ERR(chip->adc) == -EPROBE_DEFER) - return PTR_ERR(chip->adc); + chip->adc = devm_iio_channel_get(&pdev->dev, "thermal"); + if (IS_ERR(chip->adc)) { + ret = PTR_ERR(chip->adc); + chip->adc = NULL; + if (ret == -EPROBE_DEFER) + return ret; + } chip->base = res; ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type); if (ret < 0) { dev_err(&pdev->dev, "could not read type\n"); - goto fail; + return ret; } ret = qpnp_tm_read(chip, QPNP_TM_REG_SUBTYPE, &subtype); if (ret < 0) { dev_err(&pdev->dev, "could not read subtype\n"); - goto fail; + return ret; } if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) { dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n", type, subtype); - ret = -ENODEV; - goto fail; + return -ENODEV; } ret = qpnp_tm_init(chip); if (ret < 0) { dev_err(&pdev->dev, "init failed\n"); - goto fail; + return ret; } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qpnp_tm_isr, IRQF_ONESHOT, node->name, chip); if (ret < 0) - goto fail; + return ret; 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"); - ret = PTR_ERR(chip->tz_dev); - goto fail; + return PTR_ERR(chip->tz_dev); } return 0; - -fail: - if (!IS_ERR(chip->adc)) - iio_channel_release(chip->adc); - - return ret; -} - -static int qpnp_tm_remove(struct platform_device *pdev) -{ - struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev); - - if (!IS_ERR(chip->adc)) - iio_channel_release(chip->adc); - - return 0; } static const struct of_device_id qpnp_tm_match_table[] = { @@ -299,7 +285,6 @@ static struct platform_driver qpnp_tm_driver = { .of_match_table = qpnp_tm_match_table, }, .probe = qpnp_tm_probe, - .remove = qpnp_tm_remove, }; module_platform_driver(qpnp_tm_driver); diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 203aca44a2bb..561a0a332208 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -24,6 +24,7 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/spinlock.h> +#include <linux/sys_soc.h> #include <linux/thermal.h> #include "thermal_core.h" @@ -90,10 +91,6 @@ struct rcar_gen3_thermal_priv { struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; unsigned int num_tscs; spinlock_t lock; /* Protect interrupts on and off */ - const struct rcar_gen3_thermal_data *data; -}; - -struct rcar_gen3_thermal_data { void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc); }; @@ -278,7 +275,12 @@ static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data) return IRQ_HANDLED; } -static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) +static const struct soc_device_attribute r8a7795es1[] = { + { .soc_id = "r8a7795", .revision = "ES1.*" }, + { /* sentinel */ } +}; + +static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc) { rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR); rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 0x0); @@ -303,7 +305,7 @@ static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) usleep_range(1000, 2000); } -static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc) +static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc) { u32 reg_val; @@ -324,17 +326,9 @@ static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc) usleep_range(1000, 2000); } -static const struct rcar_gen3_thermal_data r8a7795_data = { - .thermal_init = r8a7795_thermal_init, -}; - -static const struct rcar_gen3_thermal_data r8a7796_data = { - .thermal_init = r8a7796_thermal_init, -}; - static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { - { .compatible = "renesas,r8a7795-thermal", .data = &r8a7795_data}, - { .compatible = "renesas,r8a7796-thermal", .data = &r8a7796_data}, + { .compatible = "renesas,r8a7795-thermal", }, + { .compatible = "renesas,r8a7796-thermal", }, {}, }; MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); @@ -371,7 +365,9 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->data = of_device_get_match_data(dev); + priv->thermal_init = rcar_gen3_thermal_init; + if (soc_device_match(r8a7795es1)) + priv->thermal_init = rcar_gen3_thermal_init_r8a7795es1; spin_lock_init(&priv->lock); @@ -423,7 +419,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) priv->tscs[i] = tsc; - priv->data->thermal_init(tsc); + priv->thermal_init(tsc); rcar_gen3_thermal_calc_coefs(&tsc->coef, ptat, thcode[i]); zone = devm_thermal_zone_of_sensor_register(dev, i, tsc, @@ -476,7 +472,7 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev) for (i = 0; i < priv->num_tscs; i++) { struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; - priv->data->thermal_init(tsc); + priv->thermal_init(tsc); rcar_gen3_thermal_set_trips(tsc, tsc->low, tsc->high); } diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 206035139110..f36375d5a16c 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -242,6 +242,45 @@ struct tsadc_table { int temp; }; +static const struct tsadc_table rv1108_table[] = { + {0, -40000}, + {374, -40000}, + {382, -35000}, + {389, -30000}, + {397, -25000}, + {405, -20000}, + {413, -15000}, + {421, -10000}, + {429, -5000}, + {436, 0}, + {444, 5000}, + {452, 10000}, + {460, 15000}, + {468, 20000}, + {476, 25000}, + {483, 30000}, + {491, 35000}, + {499, 40000}, + {507, 45000}, + {515, 50000}, + {523, 55000}, + {531, 60000}, + {539, 65000}, + {547, 70000}, + {555, 75000}, + {562, 80000}, + {570, 85000}, + {578, 90000}, + {586, 95000}, + {594, 100000}, + {602, 105000}, + {610, 110000}, + {618, 115000}, + {626, 120000}, + {634, 125000}, + {TSADCV2_DATA_MASK, 125000}, +}; + static const struct tsadc_table rk3228_code_table[] = { {0, -40000}, {588, -40000}, @@ -779,6 +818,30 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, writel_relaxed(val, regs + TSADCV2_INT_EN); } +static const struct rockchip_tsadc_chip rv1108_tsadc_data = { + .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ + .chn_num = 1, /* one channel for tsadc */ + + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv2_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = { + .id = rv1108_table, + .length = ARRAY_SIZE(rv1108_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + static const struct rockchip_tsadc_chip rk3228_tsadc_data = { .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ .chn_num = 1, /* one channel for tsadc */ @@ -928,6 +991,10 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = { static const struct of_device_id of_rockchip_thermal_match[] = { { + .compatible = "rockchip,rv1108-tsadc", + .data = (void *)&rv1108_tsadc_data, + }, + { .compatible = "rockchip,rk3228-tsadc", .data = (void *)&rk3228_tsadc_data, }, diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index be95826631b7..ee047ca43084 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -31,8 +31,7 @@ * If the temperature is higher than a trip point, * a. if the trend is THERMAL_TREND_RAISING, use higher cooling * state for this trip point - * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling - * state for this trip point + * b. if the trend is THERMAL_TREND_DROPPING, do nothing * c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit * for this trip point * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit @@ -94,9 +93,11 @@ static unsigned long get_target_state(struct thermal_instance *instance, if (!throttle) next_target = THERMAL_NO_TARGET; } else { - next_target = cur_state - 1; - if (next_target > instance->upper) - next_target = instance->upper; + if (!throttle) { + next_target = cur_state - 1; + if (next_target > instance->upper) + next_target = instance->upper; + } } break; case THERMAL_TREND_DROP_FULL: diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 7d2db23d71a3..075db1de5e53 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -483,7 +483,7 @@ static int throttrip_program(struct device *dev, unsigned int throt; u32 r, reg_off; - if (!dev || !sg || !stc || !stc->init) + if (!sg || !stc || !stc->init) return -EINVAL; temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 73f55d6a1721..46d3005335c7 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -126,38 +126,23 @@ static int gadc_thermal_probe(struct platform_device *pdev) gti->dev = &pdev->dev; platform_set_drvdata(pdev, gti); - gti->channel = iio_channel_get(&pdev->dev, "sensor-channel"); + gti->channel = devm_iio_channel_get(&pdev->dev, "sensor-channel"); if (IS_ERR(gti->channel)) { ret = PTR_ERR(gti->channel); dev_err(&pdev->dev, "IIO channel not found: %d\n", ret); return ret; } - gti->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, - gti, &gadc_thermal_ops); + gti->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, gti, + &gadc_thermal_ops); if (IS_ERR(gti->tz_dev)) { ret = PTR_ERR(gti->tz_dev); dev_err(&pdev->dev, "Thermal zone sensor register failed: %d\n", ret); - goto sensor_fail; + return ret; } return 0; - -sensor_fail: - iio_channel_release(gti->channel); - - return ret; -} - -static int gadc_thermal_remove(struct platform_device *pdev) -{ - struct gadc_thermal_info *gti = platform_get_drvdata(pdev); - - thermal_zone_of_sensor_unregister(&pdev->dev, gti->tz_dev); - iio_channel_release(gti->channel); - - return 0; } static const struct of_device_id of_adc_thermal_match[] = { @@ -172,7 +157,6 @@ static struct platform_driver gadc_thermal_driver = { .of_match_table = of_adc_thermal_match, }, .probe = gadc_thermal_probe, - .remove = gadc_thermal_remove, }; module_platform_driver(gadc_thermal_driver); diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index c211a8e4a210..b4f981daeaf2 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -278,7 +278,8 @@ int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id) if (data) { cpufreq_cooling_unregister(data->cool_dev); - cpufreq_cpu_put(data->policy); + if (data->policy) + cpufreq_cpu_put(data->policy); } return 0; |