From c56dcfa3d4d0f49f0c37cd24886aa86db7aa7f30 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Nov 2019 08:46:50 +0100 Subject: thermal: db8500: Depromote debug print We are not interested in getting this debug print on our console all the time. Cc: Daniel Lezcano Cc: Stephan Gerhold Fixes: 6c375eccded4 ("thermal: db8500: Rewrite to be a pure OF sensor") Signed-off-by: Linus Walleij Reviewed-by: Stephan Gerhold Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191119074650.2664-1-linus.walleij@linaro.org --- drivers/thermal/db8500_thermal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index 372dbbaaafb8..21d4d6e6409a 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -152,8 +152,8 @@ static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data) db8500_thermal_update_config(th, idx, THERMAL_TREND_RAISING, next_low, next_high); - dev_info(&th->tz->device, - "PRCMU set max %ld, min %ld\n", next_high, next_low); + dev_dbg(&th->tz->device, + "PRCMU set max %ld, min %ld\n", next_high, next_low); } else if (idx == num_points - 1) /* So we roof out 1 degree over the max point */ th->interpolated_temp = db8500_thermal_points[idx] + 1; -- cgit v1.2.3-59-g8ed1b From faae0ed7a5a2d212143d615ec27f9e46aade01a9 Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:10 +0530 Subject: thermal: of-thermal: Appease the kernel-doc deity Replace a comment starting with /** by simply /* to avoid having it interpreted as a kernel-doc comment. Fixes the following warning when compile with make W=1: linux.git/drivers/thermal/of-thermal.c:761: warning: cannot understand function prototype: 'const char *trip_types[] = ' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/dc742789bf4b3c8207b01c7946f2b401350536a7.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/of-thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index dc5093be553e..5235aed2fadd 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -754,7 +754,7 @@ end: return ret; } -/** +/* * It maps 'enum thermal_trip_type' found in include/linux/thermal.h * into the device tree binding of 'trip', property type. */ -- cgit v1.2.3-59-g8ed1b From 7b4e7f07a2c6dab26344e9802822206ba7337bed Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:11 +0530 Subject: thermal: cpu_cooling: Appease the kernel-doc deity Describe the function parameter to fix the following warning with make W=1: linux.git/drivers/thermal/cpu_cooling.c:92: warning: Function parameter or member 'qos_req' not described in 'cpufreq_cooling_device' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/ddb09e9728533c274edae7ff3da515b3cf7ef231.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/cpu_cooling.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 52569b27b426..53dd08f238d5 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -63,6 +63,7 @@ struct time_in_idle { * @policy: cpufreq policy. * @node: list_head to link all cpufreq_cooling_device together. * @idle_time: idle time stats + * @qos_req: PM QoS contraint to apply * * This structure is required for keeping information of each registered * cpufreq_cooling_device. -- cgit v1.2.3-59-g8ed1b From 53d256e7966d13d0f48556680e7fe682ee18084b Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:12 +0530 Subject: thermal: step_wise: Appease the kernel-doc deity Replace - with : to appease the kernel-doc gods and fix warnings such as the following when compiled with make W=1: linux-amit.git/drivers/thermal/step_wise.c:187: warning: Function parameter or member 'tz' not described in 'step_wise_throttle' linux-amit.git/drivers/thermal/step_wise.c:187: warning: Function parameter or member 'trip' not described in 'step_wise_throttle' linux.git/drivers/thermal/fair_share.c:79: warning: Function parameter or member 'tz' not described in 'fair_share_throttle' linux.git/drivers/thermal/fair_share.c:79: warning: Function parameter or member 'trip' not described in 'fair_share_throttle' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/5d48ac6f85667a53902092ad5bbfef8cc89a7162.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/fair_share.c | 4 ++-- drivers/thermal/gov_bang_bang.c | 4 ++-- drivers/thermal/step_wise.c | 4 ++-- drivers/thermal/user_space.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c index afd99f668c65..aaa07180ab48 100644 --- a/drivers/thermal/fair_share.c +++ b/drivers/thermal/fair_share.c @@ -58,8 +58,8 @@ static long get_target_state(struct thermal_zone_device *tz, /** * fair_share_throttle - throttles devices associated with the given zone - * @tz - thermal_zone_device - * @trip - trip point index + * @tz: thermal_zone_device + * @trip: trip point index * * Throttling Logic: This uses three parameters to calculate the new * throttle state of the cooling devices associated with the given zone. diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index b831fc77cf64..991a1c54296d 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -71,8 +71,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) /** * bang_bang_control - controls devices associated with the given zone - * @tz - thermal_zone_device - * @trip - the trip point + * @tz: thermal_zone_device + * @trip: the trip point * * Regulation Logic: a two point regulation, deliver cooling state depending * on the previous state shown in this diagram: diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index 6e051cbd824f..2ae7198d3067 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -174,8 +174,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) /** * step_wise_throttle - throttles devices associated with the given zone - * @tz - thermal_zone_device - * @trip - trip point index + * @tz: thermal_zone_device + * @trip: trip point index * * Throttling Logic: This uses the trend of the thermal zone to throttle. * If the thermal zone is 'heating up' this throttles all the cooling diff --git a/drivers/thermal/user_space.c b/drivers/thermal/user_space.c index 962873fd9242..293cffd9c8ad 100644 --- a/drivers/thermal/user_space.c +++ b/drivers/thermal/user_space.c @@ -17,8 +17,8 @@ /** * notify_user_space - Notifies user space about thermal events - * @tz - thermal_zone_device - * @trip - trip point index + * @tz: thermal_zone_device + * @trip: trip point index * * This function notifies the user space through UEvents. */ -- cgit v1.2.3-59-g8ed1b From 1b5cb9570670a6277cc0bba70409402359f7715d Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:13 +0530 Subject: thermal: devfreq_cooling: Appease the kernel-doc deity Fix up the following warnings with make W=1: linux.git/drivers/thermal/devfreq_cooling.c:68: warning: Function parameter or member 'capped_state' not described in 'devfreq_cooling_device' linux.git/drivers/thermal/devfreq_cooling.c:593: warning: Function parameter or member 'cdev' not described in 'devfreq_cooling_unregister' linux.git/drivers/thermal/devfreq_cooling.c:593: warning: Excess function parameter 'dfc' description in 'devfreq_cooling_unregister' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/7059d82472fe12139fc7a3379c5b9716a23cce5c.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/devfreq_cooling.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index ef59256887ff..a87d4fa031c8 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -53,6 +53,7 @@ static DEFINE_IDA(devfreq_ida); * 'utilization' (which is 'busy_time / 'total_time'). * The 'res_util' range is from 100 to (power_table[state] * 100) * for the corresponding 'state'. + * @capped_state: index to cooling state with in dynamic power budget */ struct devfreq_cooling_device { int id; @@ -587,7 +588,7 @@ EXPORT_SYMBOL_GPL(devfreq_cooling_register); /** * devfreq_cooling_unregister() - Unregister devfreq cooling device. - * @dfc: Pointer to devfreq cooling device to unregister. + * @cdev: Pointer to devfreq cooling device to unregister. */ void devfreq_cooling_unregister(struct thermal_cooling_device *cdev) { -- cgit v1.2.3-59-g8ed1b From 0f43e646dddd60e51e66f8cf53de0a13d8fdd06b Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:14 +0530 Subject: thermal: max77620: Appease the kernel-doc deity Fix up the following warning when compiled with make W=1: linux.git/drivers/thermal/max77620_thermal.c:48: warning: Function parameter or member 'temp' not described in 'max77620_thermal_read_temp' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/c943d51e7913a4b73cda447547b8ee77c857f7ba.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/max77620_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/max77620_thermal.c b/drivers/thermal/max77620_thermal.c index 88fd0fbe0cfa..82d06c7411eb 100644 --- a/drivers/thermal/max77620_thermal.c +++ b/drivers/thermal/max77620_thermal.c @@ -33,7 +33,7 @@ struct max77620_therm_info { /** * max77620_thermal_read_temp: Read PMIC die temperatue. * @data: Device specific data. - * temp: Temperature in millidegrees Celsius + * @temp: Temperature in millidegrees Celsius * * The actual temperature of PMIC die is not available from PMIC. * PMIC only tells the status if it has crossed or not the threshold level -- cgit v1.2.3-59-g8ed1b From 3772bb422072d499683f6dc8a9e3a6c8791bbd86 Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:15 +0530 Subject: thermal: mediatek: Appease the kernel-doc deity Replace a comment starting with /** by simply /* to avoid having it interpreted as a kernel-doc comment. Describe missing function parameters where needed. Fixes up the following warnings when compiled with make W=1: linux.git/drivers/thermal/mtk_thermal.c:374: warning: cannot understand function prototype: 'const struct mtk_thermal_data mt8173_thermal_data = ' linux.git/drivers/thermal/mtk_thermal.c:413: warning: cannot understand function prototype: 'const struct mtk_thermal_data mt2701_thermal_data = ' linux.git/drivers/thermal/mtk_thermal.c:443: warning: cannot understand function prototype: 'const struct mtk_thermal_data mt2712_thermal_data = ' linux.git/drivers/thermal/mtk_thermal.c:499: warning: cannot understand function prototype: 'const struct mtk_thermal_data mt8183_thermal_data = ' linux.git/drivers/thermal/mtk_thermal.c:529: warning: Function parameter or member 'sensno' not described in 'raw_to_mcelsius' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/ba10b886705879fd1b7d529fec50503d6696df20.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/mtk_thermal.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index acf4854cbb8b..76e30603d4d5 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -358,7 +358,7 @@ static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, }; static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 }; static const int mt7622_tc_offset[MT7622_NUM_CONTROLLER] = { 0x0, }; -/** +/* * The MT8173 thermal controller has four banks. Each bank can read up to * four temperature sensors simultaneously. The MT8173 has a total of 5 * temperature sensors. We use each bank to measure a certain area of the @@ -400,7 +400,7 @@ static const struct mtk_thermal_data mt8173_thermal_data = { .sensor_mux_values = mt8173_mux_values, }; -/** +/* * The MT2701 thermal controller has one bank, which can read up to * three temperature sensors simultaneously. The MT2701 has a total of 3 * temperature sensors. @@ -430,7 +430,7 @@ static const struct mtk_thermal_data mt2701_thermal_data = { .sensor_mux_values = mt2701_mux_values, }; -/** +/* * The MT2712 thermal controller has one bank, which can read up to * four temperature sensors simultaneously. The MT2712 has a total of 4 * temperature sensors. @@ -484,7 +484,7 @@ static const struct mtk_thermal_data mt7622_thermal_data = { .sensor_mux_values = mt7622_mux_values, }; -/** +/* * The MT8183 thermal controller has one bank for the current SW framework. * The MT8183 has a total of 6 temperature sensors. * There are two thermal controller to control the six sensor. @@ -495,7 +495,6 @@ static const struct mtk_thermal_data mt7622_thermal_data = { * data, and this indeed needs the temperatures of the individual banks * for making better decisions. */ - static const struct mtk_thermal_data mt8183_thermal_data = { .auxadc_channel = MT8183_TEMP_AUXADC_CHANNEL, .num_banks = MT8183_NUM_SENSORS_PER_ZONE, @@ -519,7 +518,8 @@ static const struct mtk_thermal_data mt8183_thermal_data = { /** * raw_to_mcelsius - convert a raw ADC value to mcelsius - * @mt: The thermal controller + * @mt: The thermal controller + * @sensno: sensor number * @raw: raw ADC value * * This converts the raw ADC value to mcelsius using the SoC specific -- cgit v1.2.3-59-g8ed1b From 66ec4bfcb55e5da0a7258a954f7be1495c053167 Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:16 +0530 Subject: thermal: rockchip: Appease the kernel-doc deity Replace a comment starting with /** by simply /* to avoid having it interpreted as a kernel-doc comment. Describe missing function parameters where needed. Fixes up the following warnings when compiled with make W=1: linux.git/drivers/thermal/rockchip_thermal.c:27: warning: cannot understand function prototype: 'enum tshut_mode ' linux.git/drivers/thermal/rockchip_thermal.c:37: warning: cannot understand function prototype: 'enum tshut_polarity ' linux.git/drivers/thermal/rockchip_thermal.c:46: warning: cannot understand function prototype: 'enum sensor_id ' linux.git/drivers/thermal/rockchip_thermal.c:56: warning: cannot understand function prototype: 'enum adc_sort_mode ' linux.git/drivers/thermal/rockchip_thermal.c:123: warning: Function parameter or member 'chn_id' not described in 'rockchip_tsadc_chip' linux.git/drivers/thermal/rockchip_thermal.c:123: warning: Function parameter or member 'control' not described in 'rockchip_tsadc_chip' linux.git/drivers/thermal/rockchip_thermal.c:167: warning: Function parameter or member 'sensors' not described in 'rockchip_thermal_data' linux.git/drivers/thermal/rockchip_thermal.c:608: warning: Function parameter or member 'grf' not described in 'rk_tsadcv2_initialize' linux.git/drivers/thermal/rockchip_thermal.c:608: warning: Function parameter or member 'regs' not described in 'rk_tsadcv2_initialize' linux.git/drivers/thermal/rockchip_thermal.c:608: warning: Function parameter or member 'tshut_polarity' not described in 'rk_tsadcv2_initialize' linux.git/drivers/thermal/rockchip_thermal.c:644: warning: Function parameter or member 'grf' not described in 'rk_tsadcv3_initialize' linux.git/drivers/thermal/rockchip_thermal.c:644: warning: Function parameter or member 'regs' not described in 'rk_tsadcv3_initialize' linux.git/drivers/thermal/rockchip_thermal.c:644: warning: Function parameter or member 'tshut_polarity' not described in 'rk_tsadcv3_initialize' linux.git/drivers/thermal/rockchip_thermal.c:732: warning: Function parameter or member 'regs' not described in 'rk_tsadcv3_control' linux.git/drivers/thermal/rockchip_thermal.c:732: warning: Function parameter or member 'enable' not described in 'rk_tsadcv3_control' linux.git/drivers/thermal/rockchip_thermal.c:1211: warning: Function parameter or member 'reset' not described in 'rockchip_thermal_reset_controller' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/c3cbdb0619fec602668ba7ae703ba49d67e30b33.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/rockchip_thermal.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 343c2f5c5a25..9ed8085bb792 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -19,7 +19,7 @@ #include #include -/** +/* * If the temperature over a period of time High, * the resulting TSHUT gave CRU module,let it reset the entire chip, * or via GPIO give PMIC. @@ -29,7 +29,7 @@ enum tshut_mode { TSHUT_MODE_GPIO, }; -/** +/* * The system Temperature Sensors tshut(tshut) polarity * the bit 8 is tshut polarity. * 0: low active, 1: high active @@ -39,7 +39,7 @@ enum tshut_polarity { TSHUT_HIGH_ACTIVE, }; -/** +/* * The system has two Temperature Sensors. * sensor0 is for CPU, and sensor1 is for GPU. */ @@ -48,7 +48,7 @@ enum sensor_id { SENSOR_GPU, }; -/** +/* * The conversion table has the adc value and temperature. * ADC_DECREMENT: the adc value is of diminishing.(e.g. rk3288_code_table) * ADC_INCREMENT: the adc value is incremental.(e.g. rk3368_code_table) @@ -80,13 +80,14 @@ struct chip_tsadc_table { /** * 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_id: array of sensor ids of chip corresponding 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 + * @control: enable/disable method for the tsadc controller * @get_temp: get the temperature * @set_alarm_temp: set the high temperature interrupt * @set_tshut_temp: set the hardware-controlled shutdown temperature @@ -139,7 +140,7 @@ struct rockchip_thermal_sensor { * @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 + * @sensors: array of thermal sensors * @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 @@ -590,6 +591,9 @@ static int rk_tsadcv2_code_to_temp(const struct chip_tsadc_table *table, /** * rk_tsadcv2_initialize - initialize TASDC Controller. + * @grf: the general register file will be used to do static set by software + * @regs: the base address of tsadc controller + * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) * * (1) Set TSADC_V2_AUTO_PERIOD: * Configure the interleave between every two accessing of @@ -624,6 +628,9 @@ static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs, /** * rk_tsadcv3_initialize - initialize TASDC Controller. + * @grf: the general register file will be used to do static set by software + * @regs: the base address of tsadc controller + * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) * * (1) The tsadc control power sequence. * @@ -723,6 +730,8 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable) /** * rk_tsadcv3_control - the tsadc controller is enabled or disabled. + * @regs: the base address of tsadc controller + * @enable: boolean flag to enable the controller * * 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 @@ -1206,6 +1215,7 @@ rockchip_thermal_register_sensor(struct platform_device *pdev, /** * Reset TSADC Controller, reset all tsadc registers. + * @reset: the reset controller of tsadc */ static void rockchip_thermal_reset_controller(struct reset_control *reset) { -- cgit v1.2.3-59-g8ed1b From 9625e9e694e7470beaa4bf61244f3568d1457081 Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:17 +0530 Subject: thermal: samsung: Appease the kernel-doc deity Fix up the following warning when compiled with make W=1: linux.git/drivers/thermal/samsung/exynos_tmu.c:141: warning: bad line: driver linux.git/drivers/thermal/samsung/exynos_tmu.c:203: warning: Function parameter or member 'tzd' not described in 'exynos_tmu_data' linux.git/drivers/thermal/samsung/exynos_tmu.c:203: warning: Function parameter or member 'tmu_set_trip_temp' not described in 'exynos_tmu_data' linux.git/drivers/thermal/samsung/exynos_tmu.c:203: warning: Function parameter or member 'tmu_set_trip_hyst' not described in 'exynos_tmu_data' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1ded1697c6e5eff11b034b3302b9c79e88fa9c42.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/samsung/exynos_tmu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index fb2c55123a99..8193b66a3f83 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -138,7 +138,7 @@ enum soc_type { /** * struct exynos_tmu_data : A structure to hold the private data of the TMU - driver + * driver * @id: identifier of the one instance of the TMU controller. * @base: base address of the single instance of the TMU controller. * @base_second: base address of the common registers of the TMU controller. @@ -162,8 +162,11 @@ enum soc_type { * 0 < reference_voltage <= 31 * @regulator: pointer to the TMU regulator structure. * @reg_conf: pointer to structure to register with core thermal. + * @tzd: pointer to thermal_zone_device structure * @ntrip: number of supported trip points. * @enabled: current status of TMU device + * @tmu_set_trip_temp: SoC specific method to set trip (rising threshold) + * @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold) * @tmu_initialize: SoC specific TMU initialization method * @tmu_control: SoC specific TMU control method * @tmu_read: SoC specific TMU temperature read method -- cgit v1.2.3-59-g8ed1b From 6a6d634cd8df10471775911a0d21e0deebbf7ff5 Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:18 +0530 Subject: thermal: tegra: Appease the kernel-doc deity Fix up the following warning when compiled with make W=1: linux.git/drivers/thermal/tegra/soctherm.c:369: warning: Function parameter or member 'value' not described in 'ccroc_writel' linux.git/drivers/thermal/tegra/soctherm.c:369: warning: Excess function parameter 'v' description in 'ccroc_writel' linux.git/drivers/thermal/tegra/soctherm.c:447: warning: Function parameter or member 'dev' not described in 'enforce_temp_range' linux.git/drivers/thermal/tegra/soctherm.c:772: warning: Function parameter or member 'sg' not described in 'tegra_soctherm_set_hwtrips' linux.git/drivers/thermal/tegra/soctherm.c:772: warning: Function parameter or member 'tz' not described in 'tegra_soctherm_set_hwtrips' linux.git/drivers/thermal/tegra/soctherm.c:944: warning: Function parameter or member 'ts' not described in 'soctherm_oc_intr_enable' linux.git/drivers/thermal/tegra/soctherm.c:1167: warning: Function parameter or member 'data' not described in 'soctherm_oc_irq_disable' linux.git/drivers/thermal/tegra/soctherm.c:1167: warning: Excess function parameter 'irq_data' description in 'soctherm_oc_irq_disable' linux.git/drivers/thermal/tegra/soctherm.c:1224: warning: Function parameter or member 'ctrlr' not described in 'soctherm_irq_domain_xlate_twocell' linux.git/drivers/thermal/tegra/soctherm.c:1686: warning: Function parameter or member 'pdev' not described in 'soctherm_init_hw_throt_cdev' linux.git/drivers/thermal/tegra/soctherm.c:1764: warning: Function parameter or member 'ts' not described in 'throttlectl_cpu_level_cfg' linux.git/drivers/thermal/tegra/soctherm.c:1812: warning: Function parameter or member 'ts' not described in 'throttlectl_cpu_level_select' linux.git/drivers/thermal/tegra/soctherm.c:1855: warning: Function parameter or member 'ts' not described in 'throttlectl_cpu_mn' linux.git/drivers/thermal/tegra/soctherm.c:1886: warning: Function parameter or member 'ts' not described in 'throttlectl_gpu_level_select' linux.git/drivers/thermal/tegra/soctherm.c:1928: warning: Function parameter or member 'ts' not described in 'soctherm_throttle_program' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/db764f71253bb2ad569b0aeab4c91207a39317ce.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/tegra/soctherm.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 5acaad3a594f..66e0639da4bf 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -360,7 +360,7 @@ static struct soctherm_oc_irq_chip_data soc_irq_cdata; /** * ccroc_writel() - writes a value to a CCROC register * @ts: pointer to a struct tegra_soctherm - * @v: the value to write + * @value: the value to write * @reg: the register offset * * Writes @v to @reg. No return value. @@ -435,6 +435,7 @@ static int tegra_thermctl_get_temp(void *data, int *out_temp) /** * enforce_temp_range() - check and enforce temperature range [min, max] + * @dev: struct device * of the SOC_THERM instance * @trip_temp: the trip temperature to check * * Checks and enforces the permitted temperature range that SOC_THERM @@ -747,6 +748,8 @@ static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp) /** * tegra_soctherm_set_hwtrips() - set HW trip point from DT data * @dev: struct device * of the SOC_THERM instance + * @sg: pointer to the sensor group to set the thermtrip temperature for + * @tz: struct thermal_zone_device * * * Configure the SOC_THERM HW trip points, setting "THERMTRIP" * "THROTTLE" trip points , using "thermtrips", "critical" or "hot" @@ -931,6 +934,7 @@ static irqreturn_t soctherm_thermal_isr_thread(int irq, void *dev_id) /** * soctherm_oc_intr_enable() - Enables the soctherm over-current interrupt + * @ts: pointer to a struct tegra_soctherm * @alarm: The soctherm throttle id * @enable: Flag indicating enable the soctherm over-current * interrupt or disable it @@ -1156,7 +1160,7 @@ static void soctherm_oc_irq_enable(struct irq_data *data) /** * soctherm_oc_irq_disable() - Disables overcurrent interrupt requests - * @irq_data: The interrupt request information + * @data: The interrupt request information * * Clears the interrupt request enable bit of the overcurrent * interrupt request chip data. @@ -1206,6 +1210,7 @@ static int soctherm_oc_irq_map(struct irq_domain *h, unsigned int virq, /** * soctherm_irq_domain_xlate_twocell() - xlate for soctherm interrupts * @d: Interrupt request domain + * @ctrlr: Controller device tree node * @intspec: Array of u32s from DTs "interrupt" property * @intsize: Number of values inside the intspec array * @out_hwirq: HW IRQ value associated with this interrupt @@ -1681,6 +1686,7 @@ err: /** * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations * and register them as cooling devices. + * @pdev: Pointer to platform_device struct */ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) { @@ -1751,6 +1757,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) /** * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config + * @ts: pointer to a struct tegra_soctherm * @level: describing the level LOW/MED/HIGH of throttling * * It's necessary to set up the CPU-local CCROC NV_THERM instance with @@ -1798,6 +1805,7 @@ static void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level) /** * throttlectl_cpu_level_select() - program CPU pulse skipper config + * @ts: pointer to a struct tegra_soctherm * @throt: the LIGHT/HEAVY of throttle event id * * Pulse skippers are used to throttle clock frequencies. This @@ -1841,6 +1849,7 @@ static void throttlectl_cpu_level_select(struct tegra_soctherm *ts, /** * throttlectl_cpu_mn() - program CPU pulse skipper configuration + * @ts: pointer to a struct tegra_soctherm * @throt: the LIGHT/HEAVY of throttle event id * * Pulse skippers are used to throttle clock frequencies. This @@ -1874,6 +1883,7 @@ static void throttlectl_cpu_mn(struct tegra_soctherm *ts, /** * throttlectl_gpu_level_select() - selects throttling level for GPU + * @ts: pointer to a struct tegra_soctherm * @throt: the LIGHT/HEAVY of throttle event id * * This function programs soctherm's interface to GK20a NV_THERM to select @@ -1918,6 +1928,7 @@ static int soctherm_oc_cfg_program(struct tegra_soctherm *ts, /** * soctherm_throttle_program() - programs pulse skippers' configuration + * @ts: pointer to a struct tegra_soctherm * @throt: the LIGHT/HEAVY of the throttle event id. * * Pulse skippers are used to throttle clock frequencies. -- cgit v1.2.3-59-g8ed1b From be7b848be582ef0c39963d8bd8915b82b99469d6 Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:19 +0530 Subject: thermal: amlogic: Appease the kernel-doc deity Fix up the following warning when compiled with make W=1: linux.git/drivers/thermal/amlogic_thermal.c:78: warning: Function parameter or member 'A' not described in 'amlogic_thermal_soc_calib_data' linux.git/drivers/thermal/amlogic_thermal.c:78: warning: Function parameter or member 'B' not described in 'amlogic_thermal_soc_calib_data' linux.git/drivers/thermal/amlogic_thermal.c:78: warning: Function parameter or member 'm' not described in 'amlogic_thermal_soc_calib_data' linux.git/drivers/thermal/amlogic_thermal.c:78: warning: Function parameter or member 'n' not described in 'amlogic_thermal_soc_calib_data' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/139c9191f1a18d528b5f94376facf40291d28244.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/amlogic_thermal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c index 8a9e9bc421c6..ccb1fe18e993 100644 --- a/drivers/thermal/amlogic_thermal.c +++ b/drivers/thermal/amlogic_thermal.c @@ -67,7 +67,11 @@ /** * struct amlogic_thermal_soc_calib_data - * @A, B, m, n: calibration parameters + * @A: calibration parameters + * @B: calibration parameters + * @m: calibration parameters + * @n: calibration parameters + * * This structure is required for configuration of amlogic thermal driver. */ struct amlogic_thermal_soc_calib_data { -- cgit v1.2.3-59-g8ed1b From 5ca73af203951c60b328c7f394825b3fa0f6b0b8 Mon Sep 17 00:00:00 2001 From: Amit Kucheria Date: Wed, 20 Nov 2019 21:15:20 +0530 Subject: thermal: zx2967: Appease the kernel-doc deity Fix up the following warning when compiled with make W=1: linux.git/drivers/thermal/zx2967_thermal.c:57: warning: Function parameter or member 'dev' not described in 'zx2967_thermal_priv' Signed-off-by: Amit Kucheria Reviewed-by: Viresh Kumar Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1b4f6fb91e2e713ad5135f0d40dcded65dee9d0e.1574242756.git.amit.kucheria@linaro.org --- drivers/thermal/zx2967_thermal.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/zx2967_thermal.c b/drivers/thermal/zx2967_thermal.c index 7c8a82c8e1e9..8e3a2d3c2f9a 100644 --- a/drivers/thermal/zx2967_thermal.c +++ b/drivers/thermal/zx2967_thermal.c @@ -45,6 +45,7 @@ * @clk_topcrm: topcrm clk structure * @clk_apb: apb clk structure * @regs: pointer to base address of the thermal sensor + * @dev: struct device pointer */ struct zx2967_thermal_priv { -- cgit v1.2.3-59-g8ed1b From e167dc43295fb76ff711dd55ce4e39d3656c5481 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:42 -0800 Subject: thermal: qoriq: Add local struct device pointer Use a local "struct device *dev" for brevity. No functional change intended. Signed-off-by: Andrey Smirnov Acked-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-2-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 45e9fcb172cc..dd47b31aeecc 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -262,8 +262,9 @@ static int qoriq_tmu_probe(struct platform_device *pdev) u32 ver; struct qoriq_tmu_data *data; struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; - data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data), + data = devm_kzalloc(dev, sizeof(struct qoriq_tmu_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -274,17 +275,17 @@ static int qoriq_tmu_probe(struct platform_device *pdev) data->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->regs)) { - dev_err(&pdev->dev, "Failed to get memory region\n"); + dev_err(dev, "Failed to get memory region\n"); return PTR_ERR(data->regs); } - data->clk = devm_clk_get_optional(&pdev->dev, NULL); + data->clk = devm_clk_get_optional(dev, NULL); if (IS_ERR(data->clk)) return PTR_ERR(data->clk); ret = clk_prepare_enable(data->clk); if (ret) { - dev_err(&pdev->dev, "Failed to enable clock\n"); + dev_err(dev, "Failed to enable clock\n"); return ret; } @@ -302,7 +303,7 @@ static int qoriq_tmu_probe(struct platform_device *pdev) ret = qoriq_tmu_register_tmu_zone(pdev); if (ret < 0) { - dev_err(&pdev->dev, "Failed to register sensors\n"); + dev_err(dev, "Failed to register sensors\n"); ret = -ENODEV; goto err; } -- cgit v1.2.3-59-g8ed1b From 11ef00f799133b141eb50cab68bca96480c72d80 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:43 -0800 Subject: thermal: qoriq: Don't store struct thermal_zone_device reference Struct thermal_zone_device reference stored as sensor's private data isn't really used anywhere in the code. Drop it. Signed-off-by: Andrey Smirnov Acked-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-3-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index dd47b31aeecc..2f2f5ffa8f26 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -110,7 +110,6 @@ struct qoriq_tmu_data; * Thermal zone data */ struct qoriq_sensor { - struct thermal_zone_device *tzd; struct qoriq_tmu_data *qdata; int id; }; @@ -162,6 +161,9 @@ static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) int id, sites = 0; for (id = 0; id < SITES_MAX; id++) { + struct thermal_zone_device *tzd; + int ret; + qdata->sensor[id] = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_sensor), GFP_KERNEL); if (!qdata->sensor[id]) @@ -169,13 +171,16 @@ static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) qdata->sensor[id]->id = id; qdata->sensor[id]->qdata = qdata; - qdata->sensor[id]->tzd = devm_thermal_zone_of_sensor_register( - &pdev->dev, id, qdata->sensor[id], &tmu_tz_ops); - if (IS_ERR(qdata->sensor[id]->tzd)) { - if (PTR_ERR(qdata->sensor[id]->tzd) == -ENODEV) + + tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, id, + qdata->sensor[id], + &tmu_tz_ops); + ret = PTR_ERR_OR_ZERO(tzd); + if (ret) { + if (ret == -ENODEV) continue; else - return PTR_ERR(qdata->sensor[id]->tzd); + return ret; } if (qdata->ver == TMU_VER1) -- cgit v1.2.3-59-g8ed1b From d6fb05647b10bfd440616de12ca92df6b80652cd Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:44 -0800 Subject: thermal: qoriq: Add local struct qoriq_sensor pointer Add local struct qoriq_sensor pointer in qoriq_tmu_register_tmu_zone() for brevity. Signed-off-by: Andrey Smirnov Cc: Chris Healy Cc: Lucas Stach Cc: Zhang Rui Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-4-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 2f2f5ffa8f26..61733d820e1b 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -162,18 +162,22 @@ static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) for (id = 0; id < SITES_MAX; id++) { struct thermal_zone_device *tzd; + struct qoriq_sensor *sensor; int ret; - qdata->sensor[id] = devm_kzalloc(&pdev->dev, - sizeof(struct qoriq_sensor), GFP_KERNEL); + sensor = devm_kzalloc(&pdev->dev, + sizeof(struct qoriq_sensor), + GFP_KERNEL); if (!qdata->sensor[id]) return -ENOMEM; - qdata->sensor[id]->id = id; - qdata->sensor[id]->qdata = qdata; + qdata->sensor[id] = sensor; + + sensor->id = id; + sensor->qdata = qdata; tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, id, - qdata->sensor[id], + sensor, &tmu_tz_ops); ret = PTR_ERR_OR_ZERO(tzd); if (ret) { -- cgit v1.2.3-59-g8ed1b From b319da1b00d28fb7717784a81e5d9ecac0e98bec Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:45 -0800 Subject: thermal: qoriq: Embed per-sensor data into struct qoriq_tmu_data Embed per-sensor data into struct qoriq_tmu_data so we can drop the code allocating it. This also allows us to get rid of per-sensor back reference to struct qoriq_tmu_data since now its address can be calculated using container_of(). Signed-off-by: Andrey Smirnov Cc: Chris Healy Cc: Lucas Stach Cc: Zhang Rui Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-5-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 61733d820e1b..9886daf8ac97 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -104,13 +104,10 @@ struct qoriq_tmu_regs_v2 { u32 ttrcr[4]; /* Temperature Range Control Register */ }; -struct qoriq_tmu_data; - /* * Thermal zone data */ struct qoriq_sensor { - struct qoriq_tmu_data *qdata; int id; }; @@ -120,9 +117,14 @@ struct qoriq_tmu_data { struct qoriq_tmu_regs_v2 __iomem *regs_v2; struct clk *clk; bool little_endian; - struct qoriq_sensor *sensor[SITES_MAX]; + struct qoriq_sensor sensor[SITES_MAX]; }; +static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s) +{ + return container_of(s, struct qoriq_tmu_data, sensor[s->id]); +} + static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr) { if (p->little_endian) @@ -142,7 +144,7 @@ static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) static int tmu_get_temp(void *p, int *temp) { struct qoriq_sensor *qsensor = p; - struct qoriq_tmu_data *qdata = qsensor->qdata; + struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor); u32 val; val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr); @@ -162,19 +164,10 @@ static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) for (id = 0; id < SITES_MAX; id++) { struct thermal_zone_device *tzd; - struct qoriq_sensor *sensor; + struct qoriq_sensor *sensor = &qdata->sensor[id]; int ret; - sensor = devm_kzalloc(&pdev->dev, - sizeof(struct qoriq_sensor), - GFP_KERNEL); - if (!qdata->sensor[id]) - return -ENOMEM; - - qdata->sensor[id] = sensor; - sensor->id = id; - sensor->qdata = qdata; tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, id, sensor, -- cgit v1.2.3-59-g8ed1b From 03036625d35b6b9c152bb81e406aaf98a7cd3c85 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:46 -0800 Subject: thermal: qoriq: Pass data to qoriq_tmu_register_tmu_zone() directly Pass all necessary data to qoriq_tmu_register_tmu_zone() directly instead of passing a platform device and then deriving it. This is done as a first step to simplify resource deallocation code. Signed-off-by: Andrey Smirnov Acked-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-6-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 9886daf8ac97..f024ac68e2ed 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -157,9 +157,9 @@ static const struct thermal_zone_of_device_ops tmu_tz_ops = { .get_temp = tmu_get_temp, }; -static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) +static int qoriq_tmu_register_tmu_zone(struct device *dev, + struct qoriq_tmu_data *qdata) { - struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev); int id, sites = 0; for (id = 0; id < SITES_MAX; id++) { @@ -169,7 +169,7 @@ static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) sensor->id = id; - tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, id, + tzd = devm_thermal_zone_of_sensor_register(dev, id, sensor, &tmu_tz_ops); ret = PTR_ERR_OR_ZERO(tzd); @@ -303,7 +303,7 @@ static int qoriq_tmu_probe(struct platform_device *pdev) if (ret < 0) goto err; - ret = qoriq_tmu_register_tmu_zone(pdev); + ret = qoriq_tmu_register_tmu_zone(dev, data); if (ret < 0) { dev_err(dev, "Failed to register sensors\n"); ret = -ENODEV; -- cgit v1.2.3-59-g8ed1b From 8e1cda35c3be8ed35cafa50c2b5f719032dd7224 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:47 -0800 Subject: thermal: qoriq: Pass data to qoriq_tmu_calibration() directly We can simplify error cleanup code if instead of passing a "struct platform_device *" to qoriq_tmu_calibration() and deriving a bunch of pointers from it, we pass those pointers directly. This way we won't be force to call platform_set_drvdata() as early in qoriq_tmu_probe() and need to have "platform_set_drvdata(pdev, NULL);" in error path. Signed-off-by: Andrey Smirnov Reviewed-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-7-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index f024ac68e2ed..de8c9cd115aa 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -201,23 +201,23 @@ static int qoriq_tmu_register_tmu_zone(struct device *dev, return 0; } -static int qoriq_tmu_calibration(struct platform_device *pdev) +static int qoriq_tmu_calibration(struct device *dev, + struct qoriq_tmu_data *data) { int i, val, len; u32 range[4]; const u32 *calibration; - struct device_node *np = pdev->dev.of_node; - struct qoriq_tmu_data *data = platform_get_drvdata(pdev); + struct device_node *np = dev->of_node; len = of_property_count_u32_elems(np, "fsl,tmu-range"); if (len < 0 || len > 4) { - dev_err(&pdev->dev, "invalid range data.\n"); + dev_err(dev, "invalid range data.\n"); return len; } val = of_property_read_u32_array(np, "fsl,tmu-range", range, len); if (val != 0) { - dev_err(&pdev->dev, "failed to read range data.\n"); + dev_err(dev, "failed to read range data.\n"); return val; } @@ -227,7 +227,7 @@ static int qoriq_tmu_calibration(struct platform_device *pdev) calibration = of_get_property(np, "fsl,tmu-calibration", &len); if (calibration == NULL || len % 8) { - dev_err(&pdev->dev, "invalid calibration data.\n"); + dev_err(dev, "invalid calibration data.\n"); return -ENODEV; } @@ -271,8 +271,6 @@ static int qoriq_tmu_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - platform_set_drvdata(pdev, data); - data->little_endian = of_property_read_bool(np, "little-endian"); data->regs = devm_platform_ioremap_resource(pdev, 0); @@ -299,7 +297,7 @@ static int qoriq_tmu_probe(struct platform_device *pdev) qoriq_tmu_init_device(data); /* TMU initialization */ - ret = qoriq_tmu_calibration(pdev); /* TMU calibration */ + ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */ if (ret < 0) goto err; @@ -310,11 +308,12 @@ static int qoriq_tmu_probe(struct platform_device *pdev) goto err; } + platform_set_drvdata(pdev, data); + return 0; err: clk_disable_unprepare(data->clk); - platform_set_drvdata(pdev, NULL); return ret; } -- cgit v1.2.3-59-g8ed1b From 01dc58420a2a506848d26cb80f062e6ae84db458 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:48 -0800 Subject: thermal: qoriq: Drop unnecessary drvdata cleanup Driver data of underlying struct device will be set to NULL by Linux's driver infrastructure. Clearing it here is unnecessary. Signed-off-by: Andrey Smirnov Reviewed-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-8-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index de8c9cd115aa..11749c673b3b 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -327,8 +327,6 @@ static int qoriq_tmu_remove(struct platform_device *pdev) clk_disable_unprepare(data->clk); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3-59-g8ed1b From 4316237bd62728a33e7c069ffb3350399ecdab37 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:49 -0800 Subject: thermal: qoriq: Convert driver to use regmap API Convert driver to use regmap API, drop custom LE/BE IO helpers and simplify bit manipulation using regmap_update_bits(). This also allows us to convert some register initialization to use loops and adds convenient debug access to TMU registers via debugfs. Signed-off-by: Andrey Smirnov Reviewed-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-9-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 229 +++++++++++++++++----------------------- 1 file changed, 99 insertions(+), 130 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 11749c673b3b..cacf2230201d 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include "thermal_core.h" @@ -24,85 +26,35 @@ #define TMU_VER1 0x1 #define TMU_VER2 0x2 -/* - * QorIQ TMU Registers - */ -struct qoriq_tmu_site_regs { - u32 tritsr; /* Immediate Temperature Site Register */ - u32 tratsr; /* Average Temperature Site Register */ - u8 res0[0x8]; -}; +#define REGS_TMR 0x000 /* Mode Register */ +#define TMR_DISABLE 0x0 +#define TMR_ME 0x80000000 +#define TMR_ALPF 0x0c000000 -struct qoriq_tmu_regs_v1 { - u32 tmr; /* Mode Register */ - u32 tsr; /* Status Register */ - u32 tmtmir; /* Temperature measurement interval Register */ - u8 res0[0x14]; - u32 tier; /* Interrupt Enable Register */ - u32 tidr; /* Interrupt Detect Register */ - u32 tiscr; /* Interrupt Site Capture Register */ - u32 ticscr; /* Interrupt Critical Site Capture Register */ - u8 res1[0x10]; - u32 tmhtcrh; /* High Temperature Capture Register */ - u32 tmhtcrl; /* Low Temperature Capture Register */ - u8 res2[0x8]; - u32 tmhtitr; /* High Temperature Immediate Threshold */ - u32 tmhtatr; /* High Temperature Average Threshold */ - u32 tmhtactr; /* High Temperature Average Crit Threshold */ - u8 res3[0x24]; - u32 ttcfgr; /* Temperature Configuration Register */ - u32 tscfgr; /* Sensor Configuration Register */ - u8 res4[0x78]; - struct qoriq_tmu_site_regs site[SITES_MAX]; - u8 res5[0x9f8]; - u32 ipbrr0; /* IP Block Revision Register 0 */ - u32 ipbrr1; /* IP Block Revision Register 1 */ - u8 res6[0x310]; - u32 ttrcr[4]; /* Temperature Range Control Register */ -}; +#define REGS_TMTMIR 0x008 /* Temperature measurement interval Register */ +#define TMTMIR_DEFAULT 0x0000000f -struct qoriq_tmu_regs_v2 { - u32 tmr; /* Mode Register */ - u32 tsr; /* Status Register */ - u32 tmsr; /* monitor site register */ - u32 tmtmir; /* Temperature measurement interval Register */ - u8 res0[0x10]; - u32 tier; /* Interrupt Enable Register */ - u32 tidr; /* Interrupt Detect Register */ - u8 res1[0x8]; - u32 tiiscr; /* interrupt immediate site capture register */ - u32 tiascr; /* interrupt average site capture register */ - u32 ticscr; /* Interrupt Critical Site Capture Register */ - u32 res2; - u32 tmhtcr; /* monitor high temperature capture register */ - u32 tmltcr; /* monitor low temperature capture register */ - u32 tmrtrcr; /* monitor rising temperature rate capture register */ - u32 tmftrcr; /* monitor falling temperature rate capture register */ - u32 tmhtitr; /* High Temperature Immediate Threshold */ - u32 tmhtatr; /* High Temperature Average Threshold */ - u32 tmhtactr; /* High Temperature Average Crit Threshold */ - u32 res3; - u32 tmltitr; /* monitor low temperature immediate threshold */ - u32 tmltatr; /* monitor low temperature average threshold register */ - u32 tmltactr; /* monitor low temperature average critical threshold */ - u32 res4; - u32 tmrtrctr; /* monitor rising temperature rate critical threshold */ - u32 tmftrctr; /* monitor falling temperature rate critical threshold*/ - u8 res5[0x8]; - u32 ttcfgr; /* Temperature Configuration Register */ - u32 tscfgr; /* Sensor Configuration Register */ - u8 res6[0x78]; - struct qoriq_tmu_site_regs site[SITES_MAX]; - u8 res7[0x9f8]; - u32 ipbrr0; /* IP Block Revision Register 0 */ - u32 ipbrr1; /* IP Block Revision Register 1 */ - u8 res8[0x300]; - u32 teumr0; - u32 teumr1; - u32 teumr2; - u32 res9; - u32 ttrcr[4]; /* Temperature Range Control Register */ -}; +#define REGS_V2_TMSR 0x008 /* monitor site register */ + +#define REGS_V2_TMTMIR 0x00c /* Temperature measurement interval Register */ + +#define REGS_TIER 0x020 /* Interrupt Enable Register */ +#define TIER_DISABLE 0x0 + + +#define REGS_TTCFGR 0x080 /* Temperature Configuration Register */ +#define REGS_TSCFGR 0x084 /* Sensor Configuration Register */ + +#define REGS_TRITSR(n) (0x100 + 16 * (n)) /* Immediate Temperature + * Site Register + */ +#define REGS_TTRnCR(n) (0xf10 + 4 * (n)) /* Temperature Range n + * Control Register + */ +#define REGS_IPBRR(n) (0xbf8 + 4 * (n)) /* IP Block Revision + * Register n + */ +#define REGS_V2_TEUMR(n) (0xf00 + 4 * (n)) /* * Thermal zone data @@ -113,10 +65,8 @@ struct qoriq_sensor { struct qoriq_tmu_data { int ver; - struct qoriq_tmu_regs_v1 __iomem *regs; - struct qoriq_tmu_regs_v2 __iomem *regs_v2; + struct regmap *regmap; struct clk *clk; - bool little_endian; struct qoriq_sensor sensor[SITES_MAX]; }; @@ -125,29 +75,13 @@ static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s) return container_of(s, struct qoriq_tmu_data, sensor[s->id]); } -static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr) -{ - if (p->little_endian) - iowrite32(val, addr); - else - iowrite32be(val, addr); -} - -static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) -{ - if (p->little_endian) - return ioread32(addr); - else - return ioread32be(addr); -} - static int tmu_get_temp(void *p, int *temp) { struct qoriq_sensor *qsensor = p; struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor); u32 val; - val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr); + regmap_read(qdata->regmap, REGS_TRITSR(qsensor->id), &val); *temp = (val & 0xff) * 1000; return 0; @@ -189,12 +123,12 @@ static int qoriq_tmu_register_tmu_zone(struct device *dev, /* Enable monitoring */ if (sites != 0) { if (qdata->ver == TMU_VER1) { - tmu_write(qdata, sites | TMR_ME | TMR_ALPF, - &qdata->regs->tmr); + regmap_write(qdata->regmap, REGS_TMR, + sites | TMR_ME | TMR_ALPF); } else { - tmu_write(qdata, sites, &qdata->regs_v2->tmsr); - tmu_write(qdata, TMR_ME | TMR_ALPF_V2, - &qdata->regs_v2->tmr); + regmap_write(qdata->regmap, REGS_V2_TMSR, sites); + regmap_write(qdata->regmap, REGS_TMR, + TMR_ME | TMR_ALPF_V2); } } @@ -223,7 +157,7 @@ static int qoriq_tmu_calibration(struct device *dev, /* Init temperature range registers */ for (i = 0; i < len; i++) - tmu_write(data, range[i], &data->regs->ttrcr[i]); + regmap_write(data->regmap, REGS_TTRnCR(i), range[i]); calibration = of_get_property(np, "fsl,tmu-calibration", &len); if (calibration == NULL || len % 8) { @@ -233,9 +167,9 @@ static int qoriq_tmu_calibration(struct device *dev, for (i = 0; i < len; i += 8, calibration += 2) { val = of_read_number(calibration, 1); - tmu_write(data, val, &data->regs->ttcfgr); + regmap_write(data->regmap, REGS_TTCFGR, val); val = of_read_number(calibration + 1, 1); - tmu_write(data, val, &data->regs->tscfgr); + regmap_write(data->regmap, REGS_TSCFGR, val); } return 0; @@ -244,20 +178,40 @@ static int qoriq_tmu_calibration(struct device *dev, static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) { /* Disable interrupt, using polling instead */ - tmu_write(data, TIER_DISABLE, &data->regs->tier); + regmap_write(data->regmap, REGS_TIER, TIER_DISABLE); /* Set update_interval */ + if (data->ver == TMU_VER1) { - tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir); + regmap_write(data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT); } else { - tmu_write(data, TMTMIR_DEFAULT, &data->regs_v2->tmtmir); - tmu_write(data, TEUMR0_V2, &data->regs_v2->teumr0); + regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT); + regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2); } /* Disable monitoring */ - tmu_write(data, TMR_DISABLE, &data->regs->tmr); + regmap_write(data->regmap, REGS_TMR, TMR_DISABLE); } +static const struct regmap_range qoriq_yes_ranges[] = { + regmap_reg_range(REGS_TMR, REGS_TSCFGR), + regmap_reg_range(REGS_TTRnCR(0), REGS_TTRnCR(3)), + regmap_reg_range(REGS_V2_TEUMR(0), REGS_V2_TEUMR(2)), + regmap_reg_range(REGS_IPBRR(0), REGS_IPBRR(1)), + /* Read only registers below */ + regmap_reg_range(REGS_TRITSR(0), REGS_TRITSR(15)), +}; + +static const struct regmap_access_table qoriq_wr_table = { + .yes_ranges = qoriq_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges) - 1, +}; + +static const struct regmap_access_table qoriq_rd_table = { + .yes_ranges = qoriq_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges), +}; + static int qoriq_tmu_probe(struct platform_device *pdev) { int ret; @@ -265,18 +219,37 @@ static int qoriq_tmu_probe(struct platform_device *pdev) struct qoriq_tmu_data *data; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; + const bool little_endian = of_property_read_bool(np, "little-endian"); + const enum regmap_endian format_endian = + little_endian ? REGMAP_ENDIAN_LITTLE : REGMAP_ENDIAN_BIG; + const struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .rd_table = &qoriq_rd_table, + .wr_table = &qoriq_wr_table, + .val_format_endian = format_endian, + .max_register = SZ_4K, + }; + void __iomem *base; data = devm_kzalloc(dev, sizeof(struct qoriq_tmu_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->little_endian = of_property_read_bool(np, "little-endian"); - - data->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->regs)) { + base = devm_platform_ioremap_resource(pdev, 0); + ret = PTR_ERR_OR_ZERO(base); + if (ret) { dev_err(dev, "Failed to get memory region\n"); - return PTR_ERR(data->regs); + return ret; + } + + data->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + ret = PTR_ERR_OR_ZERO(data->regmap); + if (ret) { + dev_err(dev, "Failed to init regmap (%d)\n", ret); + return ret; } data->clk = devm_clk_get_optional(dev, NULL); @@ -290,10 +263,12 @@ static int qoriq_tmu_probe(struct platform_device *pdev) } /* version register offset at: 0xbf8 on both v1 and v2 */ - ver = tmu_read(data, &data->regs->ipbrr0); + ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver); + if (ret) { + dev_err(&pdev->dev, "Failed to read IP block version\n"); + return ret; + } data->ver = (ver >> 8) & 0xff; - if (data->ver == TMU_VER2) - data->regs_v2 = (void __iomem *)data->regs; qoriq_tmu_init_device(data); /* TMU initialization */ @@ -323,7 +298,7 @@ static int qoriq_tmu_remove(struct platform_device *pdev) struct qoriq_tmu_data *data = platform_get_drvdata(pdev); /* Disable monitoring */ - tmu_write(data, TMR_DISABLE, &data->regs->tmr); + regmap_write(data->regmap, REGS_TMR, TMR_DISABLE); clk_disable_unprepare(data->clk); @@ -332,13 +307,12 @@ static int qoriq_tmu_remove(struct platform_device *pdev) static int __maybe_unused qoriq_tmu_suspend(struct device *dev) { - u32 tmr; struct qoriq_tmu_data *data = dev_get_drvdata(dev); + int ret; - /* Disable monitoring */ - tmr = tmu_read(data, &data->regs->tmr); - tmr &= ~TMR_ME; - tmu_write(data, tmr, &data->regs->tmr); + ret = regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, 0); + if (ret) + return ret; clk_disable_unprepare(data->clk); @@ -347,7 +321,6 @@ static int __maybe_unused qoriq_tmu_suspend(struct device *dev) static int __maybe_unused qoriq_tmu_resume(struct device *dev) { - u32 tmr; int ret; struct qoriq_tmu_data *data = dev_get_drvdata(dev); @@ -356,11 +329,7 @@ static int __maybe_unused qoriq_tmu_resume(struct device *dev) return ret; /* Enable monitoring */ - tmr = tmu_read(data, &data->regs->tmr); - tmr |= TMR_ME; - tmu_write(data, tmr, &data->regs->tmr); - - return 0; + return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME); } static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, -- cgit v1.2.3-59-g8ed1b From 45038e03d633b0d49e4211e6dc2b5774ca1672f4 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:50 -0800 Subject: thermal: qoriq: Enable all sensors before registering them Tmu_get_temp will get called as a part of sensor registration via devm_thermal_zone_of_sensor_register(). To prevent it from retruning bogus data we need to enable sensor monitoring before that. Looking at the datasheet (i.MX8MQ RM) there doesn't seem to be any harm in enabling them all, so, for the sake of simplicity, change the code to do just that. Signed-off-by: Andrey Smirnov Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-10-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index cacf2230201d..9a455948ddf0 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -30,6 +30,7 @@ #define TMR_DISABLE 0x0 #define TMR_ME 0x80000000 #define TMR_ALPF 0x0c000000 +#define TMR_MSITE_ALL GENMASK(15, 0) #define REGS_TMTMIR 0x008 /* Temperature measurement interval Register */ #define TMTMIR_DEFAULT 0x0000000f @@ -94,7 +95,15 @@ static const struct thermal_zone_of_device_ops tmu_tz_ops = { static int qoriq_tmu_register_tmu_zone(struct device *dev, struct qoriq_tmu_data *qdata) { - int id, sites = 0; + int id; + + if (qdata->ver == TMU_VER1) { + regmap_write(qdata->regmap, REGS_TMR, + TMR_MSITE_ALL | TMR_ME | TMR_ALPF); + } else { + regmap_write(qdata->regmap, REGS_V2_TMSR, TMR_MSITE_ALL); + regmap_write(qdata->regmap, REGS_TMR, TMR_ME | TMR_ALPF_V2); + } for (id = 0; id < SITES_MAX; id++) { struct thermal_zone_device *tzd; @@ -110,25 +119,9 @@ static int qoriq_tmu_register_tmu_zone(struct device *dev, if (ret) { if (ret == -ENODEV) continue; - else - return ret; - } - if (qdata->ver == TMU_VER1) - sites |= 0x1 << (15 - id); - else - sites |= 0x1 << id; - } - - /* Enable monitoring */ - if (sites != 0) { - if (qdata->ver == TMU_VER1) { - regmap_write(qdata->regmap, REGS_TMR, - sites | TMR_ME | TMR_ALPF); - } else { - regmap_write(qdata->regmap, REGS_V2_TMSR, sites); - regmap_write(qdata->regmap, REGS_TMR, - TMR_ME | TMR_ALPF_V2); + regmap_write(qdata->regmap, REGS_TMR, TMR_DISABLE); + return ret; } } -- cgit v1.2.3-59-g8ed1b From 36564d7e53f9efd14ab5692cd9475a75e8000c98 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:51 -0800 Subject: thermal: qoriq: Do not report invalid temperature reading Before returning measured temperature data to upper layer we need to make sure that the reading was marked as "valid" to avoid reporting bogus data. Signed-off-by: Andrey Smirnov Reviewed-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-11-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 9a455948ddf0..1730dbe5eeb6 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -49,6 +49,7 @@ #define REGS_TRITSR(n) (0x100 + 16 * (n)) /* Immediate Temperature * Site Register */ +#define TRITSR_V BIT(31) #define REGS_TTRnCR(n) (0xf10 + 4 * (n)) /* Temperature Range n * Control Register */ @@ -81,8 +82,24 @@ static int tmu_get_temp(void *p, int *temp) struct qoriq_sensor *qsensor = p; struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor); u32 val; + /* + * REGS_TRITSR(id) has the following layout: + * + * 31 ... 7 6 5 4 3 2 1 0 + * V TEMP + * + * Where V bit signifies if the measurement is ready and is + * within sensor range. TEMP is an 8 bit value representing + * temperature in C. + */ + if (regmap_read_poll_timeout(qdata->regmap, + REGS_TRITSR(qsensor->id), + val, + val & TRITSR_V, + USEC_PER_MSEC, + 10 * USEC_PER_MSEC)) + return -ENODATA; - regmap_read(qdata->regmap, REGS_TRITSR(qsensor->id), &val); *temp = (val & 0xff) * 1000; return 0; -- cgit v1.2.3-59-g8ed1b From c7fc403e40b0ea18976a59e968c23439a80809e8 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:52 -0800 Subject: thermal_hwmon: Add devres wrapper for thermal_add_hwmon_sysfs() Add devres wrapper for thermal_add_hwmon_sysfs() to simplify driver code. Signed-off-by: Andrey Smirnov Reviewed-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-12-andrew.smirnov@gmail.com --- drivers/thermal/thermal_hwmon.c | 28 ++++++++++++++++++++++++++++ drivers/thermal/thermal_hwmon.h | 7 +++++++ 2 files changed, 35 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index dd5d8ee37928..c8d2620f2e42 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -248,3 +248,31 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) kfree(hwmon); } EXPORT_SYMBOL_GPL(thermal_remove_hwmon_sysfs); + +static void devm_thermal_hwmon_release(struct device *dev, void *res) +{ + thermal_remove_hwmon_sysfs(*(struct thermal_zone_device **)res); +} + +int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) +{ + struct thermal_zone_device **ptr; + int ret; + + ptr = devres_alloc(devm_thermal_hwmon_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = thermal_add_hwmon_sysfs(tz); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = tz; + devres_add(&tz->device, ptr); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs); diff --git a/drivers/thermal/thermal_hwmon.h b/drivers/thermal/thermal_hwmon.h index a160b9d62dd0..1a9d65f6a6a8 100644 --- a/drivers/thermal/thermal_hwmon.h +++ b/drivers/thermal/thermal_hwmon.h @@ -17,6 +17,7 @@ #ifdef CONFIG_THERMAL_HWMON int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz); +int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz); void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz); #else static inline int @@ -25,6 +26,12 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) return 0; } +static inline int +devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) +{ + return 0; +} + static inline void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { -- cgit v1.2.3-59-g8ed1b From fd8433099c5b78c2a1915e1b9911ecfdfc041103 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 10 Dec 2019 08:41:53 -0800 Subject: thermal: qoriq: Add hwmon support Expose thermal readings as a HWMON device, so that it could be accessed using lm-sensors. Signed-off-by: Andrey Smirnov Reviewed-by: Daniel Lezcano Tested-by: Lucas Stach Cc: Chris Healy Cc: Lucas Stach Cc: Eduardo Valentin Cc: Daniel Lezcano Cc: Angus Ainslie (Purism) Cc: linux-imx@nxp.com Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191210164153.10463-13-andrew.smirnov@gmail.com --- drivers/thermal/qoriq_thermal.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 1730dbe5eeb6..874bc46e6c73 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -14,6 +14,7 @@ #include #include "thermal_core.h" +#include "thermal_hwmon.h" #define SITES_MAX 16 #define TMR_DISABLE 0x0 @@ -140,6 +141,11 @@ static int qoriq_tmu_register_tmu_zone(struct device *dev, regmap_write(qdata->regmap, REGS_TMR, TMR_DISABLE); return ret; } + + if (devm_thermal_add_hwmon_sysfs(tzd)) + dev_warn(dev, + "Failed to add hwmon sysfs attributes\n"); + } return 0; -- cgit v1.2.3-59-g8ed1b From 2b586feab44f41db605924db15c5b039535b1f9b Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 4 Dec 2019 16:39:27 +0100 Subject: thermal/drivers/Kconfig: Convert the CPU cooling device to a choice The next changes will add a new way to cool down a CPU by injecting idle cycles. With the current configuration, a CPU cooling device is the cpufreq cooling device. As we want to add a new CPU cooling device, let's convert the CPU cooling to a choice giving a list of CPU cooling devices. At this point, there is obviously only one CPU cooling device. There is no functional changes. Signed-off-by: Daniel Lezcano Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20191204153930.9128-1-daniel.lezcano@linaro.org --- drivers/thermal/Kconfig | 14 ++++++++++++-- drivers/thermal/Makefile | 2 +- include/linux/cpu_cooling.h | 6 +++--- 3 files changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 79b27865c6f4..62c1c83f5d31 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -151,8 +151,18 @@ config THERMAL_GOV_POWER_ALLOCATOR config CPU_THERMAL bool "Generic cpu cooling support" - depends on CPU_FREQ depends on THERMAL_OF + help + Enable the CPU cooling features. If the system has no active + cooling device available, this option allows to use the CPU + as a cooling device. + +if CPU_THERMAL + +config CPU_FREQ_THERMAL + bool "CPU frequency cooling device" + depends on CPU_FREQ + default y help This implements the generic cpu cooling mechanism through frequency reduction. An ACPI version of this already exists @@ -160,7 +170,7 @@ config CPU_THERMAL This will be useful for platforms using the generic thermal interface and not the ACPI interface. - If you want this support, you should say Y here. +endif config CLOCK_THERMAL bool "Generic clock cooling support" diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index baeb70bf0568..3f3533c01aa3 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -19,7 +19,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o # cpufreq cooling -thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o +thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpu_cooling.o # clock cooling thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h index b74732535e4b..3cdd85f987d7 100644 --- a/include/linux/cpu_cooling.h +++ b/include/linux/cpu_cooling.h @@ -19,7 +19,7 @@ struct cpufreq_policy; -#ifdef CONFIG_CPU_THERMAL +#ifdef CONFIG_CPU_FREQ_THERMAL /** * cpufreq_cooling_register - function to create cpufreq cooling device. * @policy: cpufreq policy. @@ -40,7 +40,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev); struct thermal_cooling_device * of_cpufreq_cooling_register(struct cpufreq_policy *policy); -#else /* !CONFIG_CPU_THERMAL */ +#else /* !CONFIG_CPU_FREQ_THERMAL */ static inline struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy) { @@ -58,6 +58,6 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) { return NULL; } -#endif /* CONFIG_CPU_THERMAL */ +#endif /* CONFIG_CPU_FREQ_THERMAL */ #endif /* __CPU_COOLING_H__ */ -- cgit v1.2.3-59-g8ed1b From 4abb629bea046758b712f4e26b83d7478d3400b2 Mon Sep 17 00:00:00 2001 From: Zak Hays Date: Mon, 9 Dec 2019 18:55:51 +0000 Subject: thermal: armada: Fix register offsets for AXP As shown in its device tree, Armada XP has the control1 register at 0x184d0, not 0x182d0. Signed-off-by: Zachary Hays Reviewed-by: Miquel Raynal Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/BN8PR10MB337990B7688320D736760BB68C580@BN8PR10MB3379.namprd10.prod.outlook.com --- drivers/thermal/armada_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 709a22f455e9..88363812033c 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -578,7 +578,7 @@ static const struct armada_thermal_data armadaxp_data = { .coef_m = 10000000ULL, .coef_div = 13825, .syscon_status_off = 0xb0, - .syscon_control1_off = 0xd0, + .syscon_control1_off = 0x2d0, }; static const struct armada_thermal_data armada370_data = { -- cgit v1.2.3-59-g8ed1b From ff6628951c214ff9f88dd0b3e292b2b54ca28276 Mon Sep 17 00:00:00 2001 From: Zak Hays Date: Mon, 9 Dec 2019 18:57:23 +0000 Subject: thermal: armada: Clear reset in armadaxp_init The reset bit needs to be cleared in the init sequence otherwise it holds the block in reset. Signed-off-by: Zachary Hays Reviewed-by: Miquel Raynal Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/BN8PR10MB33797EECAC557B5018A0A6628C580@BN8PR10MB3379.namprd10.prod.outlook.com --- drivers/thermal/armada_thermal.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 88363812033c..8c4d1244ee7a 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -155,6 +155,9 @@ static void armadaxp_init(struct platform_device *pdev, regmap_write(priv->syscon, data->syscon_control1_off, reg); + reg &= ~PMU_TDC0_SW_RST_MASK; + regmap_write(priv->syscon, data->syscon_control1_off, reg); + /* Enable the sensor */ regmap_read(priv->syscon, data->syscon_status_off, ®); reg &= ~PMU_TM_DISABLE_MASK; -- cgit v1.2.3-59-g8ed1b From d27970b82a0f552f70e76fab154855b3192aac23 Mon Sep 17 00:00:00 2001 From: Stefan Schaeckeler Date: Wed, 11 Dec 2019 22:17:02 -0800 Subject: thermal: rockchip: Enable hwmon By default, of-based thermal drivers do not enable hwmon. Explicitly enable hwmon for both, the soc and gpu temperature sensor. Signed-off-by: Stefan Schaeckeler Tested-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191212061702.BFE2D6E85603@corona.crabdance.com --- drivers/thermal/rockchip_thermal.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 9ed8085bb792..7c1a8bccdcba 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -58,6 +58,8 @@ enum adc_sort_mode { ADC_INCREMENT, }; +#include "thermal_hwmon.h" + /** * The max sensors is two in rockchip SoCs. * Two sensors: CPU and GPU sensor. @@ -1331,8 +1333,15 @@ static int rockchip_thermal_probe(struct platform_device *pdev) thermal->chip->control(thermal->regs, true); - for (i = 0; i < thermal->chip->chn_num; i++) + for (i = 0; i < thermal->chip->chn_num; i++) { rockchip_thermal_toggle_sensor(&thermal->sensors[i], true); + thermal->sensors[i].tzd->tzp->no_hwmon = false; + error = thermal_add_hwmon_sysfs(thermal->sensors[i].tzd); + if (error) + dev_warn(&pdev->dev, + "failed to register sensor %d with hwmon: %d\n", + i, error); + } platform_set_drvdata(pdev, thermal); @@ -1354,6 +1363,7 @@ static int rockchip_thermal_remove(struct platform_device *pdev) for (i = 0; i < thermal->chip->chn_num; i++) { struct rockchip_thermal_sensor *sensor = &thermal->sensors[i]; + thermal_remove_hwmon_sysfs(sensor->tzd); rockchip_thermal_toggle_sensor(sensor, false); } -- cgit v1.2.3-59-g8ed1b From a4c428e523490bf53e9c4ba2d809130c58c06ac7 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 19 Dec 2019 23:53:16 +0100 Subject: thermal/drivers/cpu_cooling: Introduce the cpu idle cooling driver The cpu idle cooling device offers a new method to cool down a CPU by injecting idle cycles at runtime. It has some similarities with the intel power clamp driver but it is actually designed to be more generic and relying on the idle injection powercap framework. The idle injection duration is fixed while the running duration is variable. That allows to have control on the device reactivity for the user experience. An idle state powering down the CPU or the cluster will allow to drop the static leakage, thus restoring the heat capacity of the SoC. It can be set with a trip point between the hot and the critical points, giving the opportunity to prevent a hard reset of the system when the cpufreq cooling fails to cool down the CPU. With more sophisticated boards having a per core sensor, the idle cooling device allows to cool down a single core without throttling the compute capacity of several cpus belonging to the same clock line, so it could be used in collaboration with the cpufreq cooling device. Signed-off-by: Daniel Lezcano Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20191219225317.17158-2-daniel.lezcano@linaro.org --- MAINTAINERS | 3 + drivers/thermal/Kconfig | 7 ++ drivers/thermal/Makefile | 1 + drivers/thermal/cpuidle_cooling.c | 232 ++++++++++++++++++++++++++++++++++++++ include/linux/cpu_cooling.h | 18 +++ 5 files changed, 261 insertions(+) create mode 100644 drivers/thermal/cpuidle_cooling.c (limited to 'drivers/thermal') diff --git a/MAINTAINERS b/MAINTAINERS index 56765f542244..fd45bd0ec68e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16342,12 +16342,15 @@ F: Documentation/devicetree/bindings/thermal/ THERMAL/CPU_COOLING M: Amit Daniel Kachhap +M: Daniel Lezcano M: Viresh Kumar M: Javi Merino L: linux-pm@vger.kernel.org S: Supported F: Documentation/driver-api/thermal/cpu-cooling-api.rst +F: Documentation/driver-api/thermal/cpu-idle-cooling.rst F: drivers/thermal/cpu_cooling.c +F: drivers/thermal/cpuidle_cooling.c F: include/linux/cpu_cooling.h THERMAL DRIVER FOR AMLOGIC SOCS diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 62c1c83f5d31..dc36941aef6e 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -170,6 +170,13 @@ config CPU_FREQ_THERMAL This will be useful for platforms using the generic thermal interface and not the ACPI interface. +config CPU_IDLE_THERMAL + bool "CPU idle cooling device" + depends on IDLE_INJECT + help + This implements the CPU cooling mechanism through + idle injection. This will throttle the CPU by injecting + idle cycle. endif config CLOCK_THERMAL diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 3f3533c01aa3..6929e6fad1ac 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -20,6 +20,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o # cpufreq cooling thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpu_cooling.o +thermal_sys-$(CONFIG_CPU_IDLE_THERMAL) += cpuidle_cooling.o # clock cooling thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o diff --git a/drivers/thermal/cpuidle_cooling.c b/drivers/thermal/cpuidle_cooling.c new file mode 100644 index 000000000000..0bb843246f59 --- /dev/null +++ b/drivers/thermal/cpuidle_cooling.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Limited. + * + * Author: Daniel Lezcano + * + */ +#include +#include +#include +#include +#include +#include +#include + +/** + * struct cpuidle_cooling_device - data for the idle cooling device + * @ii_dev: an atomic to keep track of the last task exiting the idle cycle + * @state: a normalized integer giving the state of the cooling device + */ +struct cpuidle_cooling_device { + struct idle_inject_device *ii_dev; + unsigned long state; +}; + +static DEFINE_IDA(cpuidle_ida); + +/** + * cpuidle_cooling_runtime - Running time computation + * @idle_duration_us: the idle cooling device + * @state: a percentile based number + * + * The running duration is computed from the idle injection duration + * which is fixed. If we reach 100% of idle injection ratio, that + * means the running duration is zero. If we have a 50% ratio + * injection, that means we have equal duration for idle and for + * running duration. + * + * The formula is deduced as follows: + * + * running = idle x ((100 / ratio) - 1) + * + * For precision purpose for integer math, we use the following: + * + * running = (idle x 100) / ratio - idle + * + * For example, if we have an injected duration of 50%, then we end up + * with 10ms of idle injection and 10ms of running duration. + * + * Return: An unsigned int for a usec based runtime duration. + */ +static unsigned int cpuidle_cooling_runtime(unsigned int idle_duration_us, + unsigned long state) +{ + if (!state) + return 0; + + return ((idle_duration_us * 100) / state) - idle_duration_us; +} + +/** + * cpuidle_cooling_get_max_state - Get the maximum state + * @cdev : the thermal cooling device + * @state : a pointer to the state variable to be filled + * + * The function always returns 100 as the injection ratio. It is + * percentile based for consistency accross different platforms. + * + * Return: The function can not fail, it is always zero + */ +static int cpuidle_cooling_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + /* + * Depending on the configuration or the hardware, the running + * cycle and the idle cycle could be different. We want to + * unify that to an 0..100 interval, so the set state + * interface will be the same whatever the platform is. + * + * The state 100% will make the cluster 100% ... idle. A 0% + * injection ratio means no idle injection at all and 50% + * means for 10ms of idle injection, we have 10ms of running + * time. + */ + *state = 100; + + return 0; +} + +/** + * cpuidle_cooling_get_cur_state - Get the current cooling state + * @cdev: the thermal cooling device + * @state: a pointer to the state + * + * The function just copies the state value from the private thermal + * cooling device structure, the mapping is 1 <-> 1. + * + * Return: The function can not fail, it is always zero + */ +static int cpuidle_cooling_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct cpuidle_cooling_device *idle_cdev = cdev->devdata; + + *state = idle_cdev->state; + + return 0; +} + +/** + * cpuidle_cooling_set_cur_state - Set the current cooling state + * @cdev: the thermal cooling device + * @state: the target state + * + * The function checks first if we are initiating the mitigation which + * in turn wakes up all the idle injection tasks belonging to the idle + * cooling device. In any case, it updates the internal state for the + * cooling device. + * + * Return: The function can not fail, it is always zero + */ +static int cpuidle_cooling_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct cpuidle_cooling_device *idle_cdev = cdev->devdata; + struct idle_inject_device *ii_dev = idle_cdev->ii_dev; + unsigned long current_state = idle_cdev->state; + unsigned int runtime_us, idle_duration_us; + + idle_cdev->state = state; + + idle_inject_get_duration(ii_dev, &runtime_us, &idle_duration_us); + + runtime_us = cpuidle_cooling_runtime(idle_duration_us, state); + + idle_inject_set_duration(ii_dev, runtime_us, idle_duration_us); + + if (current_state == 0 && state > 0) { + idle_inject_start(ii_dev); + } else if (current_state > 0 && !state) { + idle_inject_stop(ii_dev); + } + + return 0; +} + +/** + * cpuidle_cooling_ops - thermal cooling device ops + */ +static struct thermal_cooling_device_ops cpuidle_cooling_ops = { + .get_max_state = cpuidle_cooling_get_max_state, + .get_cur_state = cpuidle_cooling_get_cur_state, + .set_cur_state = cpuidle_cooling_set_cur_state, +}; + +/** + * cpuidle_of_cooling_register - Idle cooling device initialization function + * @drv: a cpuidle driver structure pointer + * @np: a node pointer to a device tree cooling device node + * + * This function is in charge of creating a cooling device per cpuidle + * driver and register it to thermal framework. + * + * Return: zero on success, or negative value corresponding to the + * error detected in the underlying subsystems. + */ +int cpuidle_of_cooling_register(struct device_node *np, + struct cpuidle_driver *drv) +{ + struct idle_inject_device *ii_dev; + struct cpuidle_cooling_device *idle_cdev; + struct thermal_cooling_device *cdev; + char dev_name[THERMAL_NAME_LENGTH]; + int id, ret; + + idle_cdev = kzalloc(sizeof(*idle_cdev), GFP_KERNEL); + if (!idle_cdev) { + ret = -ENOMEM; + goto out; + } + + id = ida_simple_get(&cpuidle_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto out_kfree; + } + + ii_dev = idle_inject_register(drv->cpumask); + if (!ii_dev) { + ret = -EINVAL; + goto out_id; + } + + idle_inject_set_duration(ii_dev, TICK_USEC, TICK_USEC); + + idle_cdev->ii_dev = ii_dev; + + snprintf(dev_name, sizeof(dev_name), "thermal-idle-%d", id); + + cdev = thermal_of_cooling_device_register(np, dev_name, idle_cdev, + &cpuidle_cooling_ops); + if (IS_ERR(cdev)) { + ret = PTR_ERR(cdev); + goto out_unregister; + } + + return 0; + +out_unregister: + idle_inject_unregister(ii_dev); +out_id: + ida_simple_remove(&cpuidle_ida, id); +out_kfree: + kfree(idle_cdev); +out: + return ret; +} + +/** + * cpuidle_cooling_register - Idle cooling device initialization function + * @drv: a cpuidle driver structure pointer + * + * This function is in charge of creating a cooling device per cpuidle + * driver and register it to thermal framework. + * + * Return: zero on success, or negative value corresponding to the + * error detected in the underlying subsystems. + */ +int cpuidle_cooling_register(struct cpuidle_driver *drv) +{ + return cpuidle_of_cooling_register(NULL, drv); +} diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h index 3cdd85f987d7..65501d8f9778 100644 --- a/include/linux/cpu_cooling.h +++ b/include/linux/cpu_cooling.h @@ -60,4 +60,22 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) } #endif /* CONFIG_CPU_FREQ_THERMAL */ +struct cpuidle_driver; + +#ifdef CONFIG_CPU_IDLE_THERMAL +int cpuidle_cooling_register(struct cpuidle_driver *drv); +int cpuidle_of_cooling_register(struct device_node *np, + struct cpuidle_driver *drv); +#else /* CONFIG_CPU_IDLE_THERMAL */ +static inline int cpuidle_cooling_register(struct cpuidle_driver *drv) +{ + return 0; +} +static inline int cpuidle_of_cooling_register(struct device_node *np, + struct cpuidle_driver *drv) +{ + return 0; +} +#endif /* CONFIG_CPU_IDLE_THERMAL */ + #endif /* __CPU_COOLING_H__ */ -- cgit v1.2.3-59-g8ed1b From 23affa2e29c5faa8cb59778f71e3bce2c8b3aa5c Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 19 Dec 2019 23:53:17 +0100 Subject: thermal/drivers/cpu_cooling: Rename to cpufreq_cooling As we introduced the idle injection cooling device called cpuidle_cooling, let's be consistent and rename the cpu_cooling to cpufreq_cooling as this one mitigates with OPPs changes. Signed-off-by: Daniel Lezcano Acked-by: Viresh Kumar Reviewed-by: Amit Kucheria Link: https://lore.kernel.org/r/20191219225317.17158-3-daniel.lezcano@linaro.org --- .../driver-api/thermal/exynos_thermal.rst | 2 +- MAINTAINERS | 2 +- drivers/thermal/Makefile | 2 +- drivers/thermal/clock_cooling.c | 2 +- drivers/thermal/cpu_cooling.c | 670 --------------------- drivers/thermal/cpufreq_cooling.c | 670 +++++++++++++++++++++ include/linux/clock_cooling.h | 2 +- 7 files changed, 675 insertions(+), 675 deletions(-) delete mode 100644 drivers/thermal/cpu_cooling.c create mode 100644 drivers/thermal/cpufreq_cooling.c (limited to 'drivers/thermal') diff --git a/Documentation/driver-api/thermal/exynos_thermal.rst b/Documentation/driver-api/thermal/exynos_thermal.rst index 5bd556566c70..d4e4a5b75805 100644 --- a/Documentation/driver-api/thermal/exynos_thermal.rst +++ b/Documentation/driver-api/thermal/exynos_thermal.rst @@ -67,7 +67,7 @@ TMU driver description: The exynos thermal driver is structured as:: Kernel Core thermal framework - (thermal_core.c, step_wise.c, cpu_cooling.c) + (thermal_core.c, step_wise.c, cpufreq_cooling.c) ^ | | diff --git a/MAINTAINERS b/MAINTAINERS index fd45bd0ec68e..08ebdc7071d7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16349,7 +16349,7 @@ L: linux-pm@vger.kernel.org S: Supported F: Documentation/driver-api/thermal/cpu-cooling-api.rst F: Documentation/driver-api/thermal/cpu-idle-cooling.rst -F: drivers/thermal/cpu_cooling.c +F: drivers/thermal/cpufreq_cooling.c F: drivers/thermal/cpuidle_cooling.c F: include/linux/cpu_cooling.h diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 6929e6fad1ac..d502a597a717 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -19,7 +19,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o # cpufreq cooling -thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpu_cooling.o +thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpufreq_cooling.o thermal_sys-$(CONFIG_CPU_IDLE_THERMAL) += cpuidle_cooling.o # clock cooling diff --git a/drivers/thermal/clock_cooling.c b/drivers/thermal/clock_cooling.c index 3ad3256c48fd..7cb3ae4b44ee 100644 --- a/drivers/thermal/clock_cooling.c +++ b/drivers/thermal/clock_cooling.c @@ -7,7 +7,7 @@ * Copyright (C) 2013 Texas Instruments Inc. * Contact: Eduardo Valentin * - * Highly based on cpu_cooling.c. + * Highly based on cpufreq_cooling.c. * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) * Copyright (C) 2012 Amit Daniel */ diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c deleted file mode 100644 index 53dd08f238d5..000000000000 --- a/drivers/thermal/cpu_cooling.c +++ /dev/null @@ -1,670 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * linux/drivers/thermal/cpu_cooling.c - * - * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) - * - * Copyright (C) 2012-2018 Linaro Limited. - * - * Authors: Amit Daniel - * Viresh Kumar - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * Cooling state <-> CPUFreq frequency - * - * Cooling states are translated to frequencies throughout this driver and this - * is the relation between them. - * - * Highest cooling state corresponds to lowest possible frequency. - * - * i.e. - * level 0 --> 1st Max Freq - * level 1 --> 2nd Max Freq - * ... - */ - -/** - * struct time_in_idle - Idle time stats - * @time: previous reading of the absolute time that this cpu was idle - * @timestamp: wall time of the last invocation of get_cpu_idle_time_us() - */ -struct time_in_idle { - u64 time; - u64 timestamp; -}; - -/** - * struct cpufreq_cooling_device - data for cooling device with cpufreq - * @id: unique integer value corresponding to each cpufreq_cooling_device - * registered. - * @last_load: load measured by the latest call to cpufreq_get_requested_power() - * @cpufreq_state: integer value representing the current state of cpufreq - * cooling devices. - * @max_level: maximum cooling level. One less than total number of valid - * cpufreq frequencies. - * @em: Reference on the Energy Model of the device - * @cdev: thermal_cooling_device pointer to keep track of the - * registered cooling device. - * @policy: cpufreq policy. - * @node: list_head to link all cpufreq_cooling_device together. - * @idle_time: idle time stats - * @qos_req: PM QoS contraint to apply - * - * This structure is required for keeping information of each registered - * cpufreq_cooling_device. - */ -struct cpufreq_cooling_device { - int id; - u32 last_load; - unsigned int cpufreq_state; - unsigned int max_level; - struct em_perf_domain *em; - struct cpufreq_policy *policy; - struct list_head node; - struct time_in_idle *idle_time; - struct freq_qos_request qos_req; -}; - -static DEFINE_IDA(cpufreq_ida); -static DEFINE_MUTEX(cooling_list_lock); -static LIST_HEAD(cpufreq_cdev_list); - -#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR -/** - * get_level: Find the level for a particular frequency - * @cpufreq_cdev: cpufreq_cdev for which the property is required - * @freq: Frequency - * - * Return: level corresponding to the frequency. - */ -static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev, - unsigned int freq) -{ - int i; - - for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { - if (freq > cpufreq_cdev->em->table[i].frequency) - break; - } - - return cpufreq_cdev->max_level - i - 1; -} - -static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev, - u32 freq) -{ - int i; - - for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { - if (freq > cpufreq_cdev->em->table[i].frequency) - break; - } - - return cpufreq_cdev->em->table[i + 1].power; -} - -static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev, - u32 power) -{ - int i; - - for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { - if (power > cpufreq_cdev->em->table[i].power) - break; - } - - return cpufreq_cdev->em->table[i + 1].frequency; -} - -/** - * get_load() - get load for a cpu since last updated - * @cpufreq_cdev: &struct cpufreq_cooling_device for this cpu - * @cpu: cpu number - * @cpu_idx: index of the cpu in time_in_idle* - * - * Return: The average load of cpu @cpu in percentage since this - * function was last called. - */ -static u32 get_load(struct cpufreq_cooling_device *cpufreq_cdev, int cpu, - int cpu_idx) -{ - u32 load; - u64 now, now_idle, delta_time, delta_idle; - struct time_in_idle *idle_time = &cpufreq_cdev->idle_time[cpu_idx]; - - now_idle = get_cpu_idle_time(cpu, &now, 0); - delta_idle = now_idle - idle_time->time; - delta_time = now - idle_time->timestamp; - - if (delta_time <= delta_idle) - load = 0; - else - load = div64_u64(100 * (delta_time - delta_idle), delta_time); - - idle_time->time = now_idle; - idle_time->timestamp = now; - - return load; -} - -/** - * get_dynamic_power() - calculate the dynamic power - * @cpufreq_cdev: &cpufreq_cooling_device for this cdev - * @freq: current frequency - * - * Return: the dynamic power consumed by the cpus described by - * @cpufreq_cdev. - */ -static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_cdev, - unsigned long freq) -{ - u32 raw_cpu_power; - - raw_cpu_power = cpu_freq_to_power(cpufreq_cdev, freq); - return (raw_cpu_power * cpufreq_cdev->last_load) / 100; -} - -/** - * cpufreq_get_requested_power() - get the current power - * @cdev: &thermal_cooling_device pointer - * @tz: a valid thermal zone device pointer - * @power: pointer in which to store the resulting power - * - * Calculate the current power consumption of the cpus in milliwatts - * and store it in @power. This function should actually calculate - * the requested power, but it's hard to get the frequency that - * cpufreq would have assigned if there were no thermal limits. - * Instead, we calculate the current power on the assumption that the - * immediate future will look like the immediate past. - * - * We use the current frequency and the average load since this - * function was last called. In reality, there could have been - * multiple opps since this function was last called and that affects - * the load calculation. While it's not perfectly accurate, this - * simplification is good enough and works. REVISIT this, as more - * complex code may be needed if experiments show that it's not - * accurate enough. - * - * Return: 0 on success, -E* if getting the static power failed. - */ -static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, - struct thermal_zone_device *tz, - u32 *power) -{ - unsigned long freq; - int i = 0, cpu; - u32 total_load = 0; - struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; - struct cpufreq_policy *policy = cpufreq_cdev->policy; - u32 *load_cpu = NULL; - - freq = cpufreq_quick_get(policy->cpu); - - if (trace_thermal_power_cpu_get_power_enabled()) { - u32 ncpus = cpumask_weight(policy->related_cpus); - - load_cpu = kcalloc(ncpus, sizeof(*load_cpu), GFP_KERNEL); - } - - for_each_cpu(cpu, policy->related_cpus) { - u32 load; - - if (cpu_online(cpu)) - load = get_load(cpufreq_cdev, cpu, i); - else - load = 0; - - total_load += load; - if (load_cpu) - load_cpu[i] = load; - - i++; - } - - cpufreq_cdev->last_load = total_load; - - *power = get_dynamic_power(cpufreq_cdev, freq); - - if (load_cpu) { - trace_thermal_power_cpu_get_power(policy->related_cpus, freq, - load_cpu, i, *power); - - kfree(load_cpu); - } - - return 0; -} - -/** - * cpufreq_state2power() - convert a cpu cdev state to power consumed - * @cdev: &thermal_cooling_device pointer - * @tz: a valid thermal zone device pointer - * @state: cooling device state to be converted - * @power: pointer in which to store the resulting power - * - * Convert cooling device state @state into power consumption in - * milliwatts assuming 100% load. Store the calculated power in - * @power. - * - * Return: 0 on success, -EINVAL if the cooling device state could not - * be converted into a frequency or other -E* if there was an error - * when calculating the static power. - */ -static int cpufreq_state2power(struct thermal_cooling_device *cdev, - struct thermal_zone_device *tz, - unsigned long state, u32 *power) -{ - unsigned int freq, num_cpus, idx; - struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; - - /* Request state should be less than max_level */ - if (WARN_ON(state > cpufreq_cdev->max_level)) - return -EINVAL; - - num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus); - - idx = cpufreq_cdev->max_level - state; - freq = cpufreq_cdev->em->table[idx].frequency; - *power = cpu_freq_to_power(cpufreq_cdev, freq) * num_cpus; - - return 0; -} - -/** - * cpufreq_power2state() - convert power to a cooling device state - * @cdev: &thermal_cooling_device pointer - * @tz: a valid thermal zone device pointer - * @power: power in milliwatts to be converted - * @state: pointer in which to store the resulting state - * - * Calculate a cooling device state for the cpus described by @cdev - * that would allow them to consume at most @power mW and store it in - * @state. Note that this calculation depends on external factors - * such as the cpu load or the current static power. Calling this - * function with the same power as input can yield different cooling - * device states depending on those external factors. - * - * Return: 0 on success, -ENODEV if no cpus are online or -EINVAL if - * the calculated frequency could not be converted to a valid state. - * The latter should not happen unless the frequencies available to - * cpufreq have changed since the initialization of the cpu cooling - * device. - */ -static int cpufreq_power2state(struct thermal_cooling_device *cdev, - struct thermal_zone_device *tz, u32 power, - unsigned long *state) -{ - unsigned int target_freq; - u32 last_load, normalised_power; - struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; - struct cpufreq_policy *policy = cpufreq_cdev->policy; - - last_load = cpufreq_cdev->last_load ?: 1; - normalised_power = (power * 100) / last_load; - target_freq = cpu_power_to_freq(cpufreq_cdev, normalised_power); - - *state = get_level(cpufreq_cdev, target_freq); - trace_thermal_power_cpu_limit(policy->related_cpus, target_freq, *state, - power); - return 0; -} - -static inline bool em_is_sane(struct cpufreq_cooling_device *cpufreq_cdev, - struct em_perf_domain *em) { - struct cpufreq_policy *policy; - unsigned int nr_levels; - - if (!em) - return false; - - policy = cpufreq_cdev->policy; - if (!cpumask_equal(policy->related_cpus, to_cpumask(em->cpus))) { - pr_err("The span of pd %*pbl is misaligned with cpufreq policy %*pbl\n", - cpumask_pr_args(to_cpumask(em->cpus)), - cpumask_pr_args(policy->related_cpus)); - return false; - } - - nr_levels = cpufreq_cdev->max_level + 1; - if (em->nr_cap_states != nr_levels) { - pr_err("The number of cap states in pd %*pbl (%u) doesn't match the number of cooling levels (%u)\n", - cpumask_pr_args(to_cpumask(em->cpus)), - em->nr_cap_states, nr_levels); - return false; - } - - return true; -} -#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */ - -static unsigned int get_state_freq(struct cpufreq_cooling_device *cpufreq_cdev, - unsigned long state) -{ - struct cpufreq_policy *policy; - unsigned long idx; - -#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR - /* Use the Energy Model table if available */ - if (cpufreq_cdev->em) { - idx = cpufreq_cdev->max_level - state; - return cpufreq_cdev->em->table[idx].frequency; - } -#endif - - /* Otherwise, fallback on the CPUFreq table */ - policy = cpufreq_cdev->policy; - if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) - idx = cpufreq_cdev->max_level - state; - else - idx = state; - - return policy->freq_table[idx].frequency; -} - -/* cpufreq cooling device callback functions are defined below */ - -/** - * cpufreq_get_max_state - callback function to get the max cooling state. - * @cdev: thermal cooling device pointer. - * @state: fill this variable with the max cooling state. - * - * Callback for the thermal cooling device to return the cpufreq - * max cooling state. - * - * Return: 0 on success, an error code otherwise. - */ -static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, - unsigned long *state) -{ - struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; - - *state = cpufreq_cdev->max_level; - return 0; -} - -/** - * cpufreq_get_cur_state - callback function to get the current cooling state. - * @cdev: thermal cooling device pointer. - * @state: fill this variable with the current cooling state. - * - * Callback for the thermal cooling device to return the cpufreq - * current cooling state. - * - * Return: 0 on success, an error code otherwise. - */ -static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev, - unsigned long *state) -{ - struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; - - *state = cpufreq_cdev->cpufreq_state; - - return 0; -} - -/** - * cpufreq_set_cur_state - callback function to set the current cooling state. - * @cdev: thermal cooling device pointer. - * @state: set this variable to the current cooling state. - * - * Callback for the thermal cooling device to change the cpufreq - * current cooling state. - * - * Return: 0 on success, an error code otherwise. - */ -static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, - unsigned long state) -{ - struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; - - /* Request state should be less than max_level */ - if (WARN_ON(state > cpufreq_cdev->max_level)) - return -EINVAL; - - /* Check if the old cooling action is same as new cooling action */ - if (cpufreq_cdev->cpufreq_state == state) - return 0; - - cpufreq_cdev->cpufreq_state = state; - - return freq_qos_update_request(&cpufreq_cdev->qos_req, - get_state_freq(cpufreq_cdev, state)); -} - -/* Bind cpufreq callbacks to thermal cooling device ops */ - -static struct thermal_cooling_device_ops cpufreq_cooling_ops = { - .get_max_state = cpufreq_get_max_state, - .get_cur_state = cpufreq_get_cur_state, - .set_cur_state = cpufreq_set_cur_state, -}; - -/** - * __cpufreq_cooling_register - helper function to create cpufreq cooling device - * @np: a valid struct device_node to the cooling device device tree node - * @policy: cpufreq policy - * Normally this should be same as cpufreq policy->related_cpus. - * @em: Energy Model of the cpufreq policy - * - * This interface function registers the cpufreq cooling device with the name - * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq - * cooling devices. It also gives the opportunity to link the cooling device - * with a device tree node, in order to bind it via the thermal DT code. - * - * Return: a valid struct thermal_cooling_device pointer on success, - * on failure, it returns a corresponding ERR_PTR(). - */ -static struct thermal_cooling_device * -__cpufreq_cooling_register(struct device_node *np, - struct cpufreq_policy *policy, - struct em_perf_domain *em) -{ - struct thermal_cooling_device *cdev; - struct cpufreq_cooling_device *cpufreq_cdev; - char dev_name[THERMAL_NAME_LENGTH]; - unsigned int i, num_cpus; - struct device *dev; - int ret; - struct thermal_cooling_device_ops *cooling_ops; - - dev = get_cpu_device(policy->cpu); - if (unlikely(!dev)) { - pr_warn("No cpu device for cpu %d\n", policy->cpu); - return ERR_PTR(-ENODEV); - } - - - if (IS_ERR_OR_NULL(policy)) { - pr_err("%s: cpufreq policy isn't valid: %p\n", __func__, policy); - return ERR_PTR(-EINVAL); - } - - i = cpufreq_table_count_valid_entries(policy); - if (!i) { - pr_debug("%s: CPUFreq table not found or has no valid entries\n", - __func__); - return ERR_PTR(-ENODEV); - } - - cpufreq_cdev = kzalloc(sizeof(*cpufreq_cdev), GFP_KERNEL); - if (!cpufreq_cdev) - return ERR_PTR(-ENOMEM); - - cpufreq_cdev->policy = policy; - num_cpus = cpumask_weight(policy->related_cpus); - cpufreq_cdev->idle_time = kcalloc(num_cpus, - sizeof(*cpufreq_cdev->idle_time), - GFP_KERNEL); - if (!cpufreq_cdev->idle_time) { - cdev = ERR_PTR(-ENOMEM); - goto free_cdev; - } - - /* max_level is an index, not a counter */ - cpufreq_cdev->max_level = i - 1; - - ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); - if (ret < 0) { - cdev = ERR_PTR(ret); - goto free_idle_time; - } - cpufreq_cdev->id = ret; - - snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", - cpufreq_cdev->id); - - cooling_ops = &cpufreq_cooling_ops; - -#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR - if (em_is_sane(cpufreq_cdev, em)) { - cpufreq_cdev->em = em; - cooling_ops->get_requested_power = cpufreq_get_requested_power; - cooling_ops->state2power = cpufreq_state2power; - cooling_ops->power2state = cpufreq_power2state; - } else -#endif - if (policy->freq_table_sorted == CPUFREQ_TABLE_UNSORTED) { - pr_err("%s: unsorted frequency tables are not supported\n", - __func__); - cdev = ERR_PTR(-EINVAL); - goto remove_ida; - } - - ret = freq_qos_add_request(&policy->constraints, - &cpufreq_cdev->qos_req, FREQ_QOS_MAX, - get_state_freq(cpufreq_cdev, 0)); - if (ret < 0) { - pr_err("%s: Failed to add freq constraint (%d)\n", __func__, - ret); - cdev = ERR_PTR(ret); - goto remove_ida; - } - - cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, - cooling_ops); - if (IS_ERR(cdev)) - goto remove_qos_req; - - mutex_lock(&cooling_list_lock); - list_add(&cpufreq_cdev->node, &cpufreq_cdev_list); - mutex_unlock(&cooling_list_lock); - - return cdev; - -remove_qos_req: - freq_qos_remove_request(&cpufreq_cdev->qos_req); -remove_ida: - ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); -free_idle_time: - kfree(cpufreq_cdev->idle_time); -free_cdev: - kfree(cpufreq_cdev); - return cdev; -} - -/** - * cpufreq_cooling_register - function to create cpufreq cooling device. - * @policy: cpufreq policy - * - * This interface function registers the cpufreq cooling device with the name - * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq - * cooling devices. - * - * Return: a valid struct thermal_cooling_device pointer on success, - * on failure, it returns a corresponding ERR_PTR(). - */ -struct thermal_cooling_device * -cpufreq_cooling_register(struct cpufreq_policy *policy) -{ - return __cpufreq_cooling_register(NULL, policy, NULL); -} -EXPORT_SYMBOL_GPL(cpufreq_cooling_register); - -/** - * of_cpufreq_cooling_register - function to create cpufreq cooling device. - * @policy: cpufreq policy - * - * This interface function registers the cpufreq cooling device with the name - * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq - * cooling devices. Using this API, the cpufreq cooling device will be - * linked to the device tree node provided. - * - * Using this function, the cooling device will implement the power - * extensions by using a simple cpu power model. The cpus must have - * registered their OPPs using the OPP library. - * - * It also takes into account, if property present in policy CPU node, the - * static power consumed by the cpu. - * - * Return: a valid struct thermal_cooling_device pointer on success, - * and NULL on failure. - */ -struct thermal_cooling_device * -of_cpufreq_cooling_register(struct cpufreq_policy *policy) -{ - struct device_node *np = of_get_cpu_node(policy->cpu, NULL); - struct thermal_cooling_device *cdev = NULL; - - if (!np) { - pr_err("cpu_cooling: OF node not available for cpu%d\n", - policy->cpu); - return NULL; - } - - if (of_find_property(np, "#cooling-cells", NULL)) { - struct em_perf_domain *em = em_cpu_get(policy->cpu); - - cdev = __cpufreq_cooling_register(np, policy, em); - if (IS_ERR(cdev)) { - pr_err("cpu_cooling: cpu%d failed to register as cooling device: %ld\n", - policy->cpu, PTR_ERR(cdev)); - cdev = NULL; - } - } - - of_node_put(np); - return cdev; -} -EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register); - -/** - * cpufreq_cooling_unregister - function to remove cpufreq cooling device. - * @cdev: thermal cooling device pointer. - * - * This interface function unregisters the "thermal-cpufreq-%x" cooling device. - */ -void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) -{ - struct cpufreq_cooling_device *cpufreq_cdev; - - if (!cdev) - return; - - cpufreq_cdev = cdev->devdata; - - mutex_lock(&cooling_list_lock); - list_del(&cpufreq_cdev->node); - mutex_unlock(&cooling_list_lock); - - thermal_cooling_device_unregister(cdev); - freq_qos_remove_request(&cpufreq_cdev->qos_req); - ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); - kfree(cpufreq_cdev->idle_time); - kfree(cpufreq_cdev); -} -EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c new file mode 100644 index 000000000000..fe83d7a210d4 --- /dev/null +++ b/drivers/thermal/cpufreq_cooling.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/drivers/thermal/cpufreq_cooling.c + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) + * + * Copyright (C) 2012-2018 Linaro Limited. + * + * Authors: Amit Daniel + * Viresh Kumar + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Cooling state <-> CPUFreq frequency + * + * Cooling states are translated to frequencies throughout this driver and this + * is the relation between them. + * + * Highest cooling state corresponds to lowest possible frequency. + * + * i.e. + * level 0 --> 1st Max Freq + * level 1 --> 2nd Max Freq + * ... + */ + +/** + * struct time_in_idle - Idle time stats + * @time: previous reading of the absolute time that this cpu was idle + * @timestamp: wall time of the last invocation of get_cpu_idle_time_us() + */ +struct time_in_idle { + u64 time; + u64 timestamp; +}; + +/** + * struct cpufreq_cooling_device - data for cooling device with cpufreq + * @id: unique integer value corresponding to each cpufreq_cooling_device + * registered. + * @last_load: load measured by the latest call to cpufreq_get_requested_power() + * @cpufreq_state: integer value representing the current state of cpufreq + * cooling devices. + * @max_level: maximum cooling level. One less than total number of valid + * cpufreq frequencies. + * @em: Reference on the Energy Model of the device + * @cdev: thermal_cooling_device pointer to keep track of the + * registered cooling device. + * @policy: cpufreq policy. + * @node: list_head to link all cpufreq_cooling_device together. + * @idle_time: idle time stats + * @qos_req: PM QoS contraint to apply + * + * This structure is required for keeping information of each registered + * cpufreq_cooling_device. + */ +struct cpufreq_cooling_device { + int id; + u32 last_load; + unsigned int cpufreq_state; + unsigned int max_level; + struct em_perf_domain *em; + struct cpufreq_policy *policy; + struct list_head node; + struct time_in_idle *idle_time; + struct freq_qos_request qos_req; +}; + +static DEFINE_IDA(cpufreq_ida); +static DEFINE_MUTEX(cooling_list_lock); +static LIST_HEAD(cpufreq_cdev_list); + +#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR +/** + * get_level: Find the level for a particular frequency + * @cpufreq_cdev: cpufreq_cdev for which the property is required + * @freq: Frequency + * + * Return: level corresponding to the frequency. + */ +static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev, + unsigned int freq) +{ + int i; + + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (freq > cpufreq_cdev->em->table[i].frequency) + break; + } + + return cpufreq_cdev->max_level - i - 1; +} + +static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev, + u32 freq) +{ + int i; + + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (freq > cpufreq_cdev->em->table[i].frequency) + break; + } + + return cpufreq_cdev->em->table[i + 1].power; +} + +static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev, + u32 power) +{ + int i; + + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (power > cpufreq_cdev->em->table[i].power) + break; + } + + return cpufreq_cdev->em->table[i + 1].frequency; +} + +/** + * get_load() - get load for a cpu since last updated + * @cpufreq_cdev: &struct cpufreq_cooling_device for this cpu + * @cpu: cpu number + * @cpu_idx: index of the cpu in time_in_idle* + * + * Return: The average load of cpu @cpu in percentage since this + * function was last called. + */ +static u32 get_load(struct cpufreq_cooling_device *cpufreq_cdev, int cpu, + int cpu_idx) +{ + u32 load; + u64 now, now_idle, delta_time, delta_idle; + struct time_in_idle *idle_time = &cpufreq_cdev->idle_time[cpu_idx]; + + now_idle = get_cpu_idle_time(cpu, &now, 0); + delta_idle = now_idle - idle_time->time; + delta_time = now - idle_time->timestamp; + + if (delta_time <= delta_idle) + load = 0; + else + load = div64_u64(100 * (delta_time - delta_idle), delta_time); + + idle_time->time = now_idle; + idle_time->timestamp = now; + + return load; +} + +/** + * get_dynamic_power() - calculate the dynamic power + * @cpufreq_cdev: &cpufreq_cooling_device for this cdev + * @freq: current frequency + * + * Return: the dynamic power consumed by the cpus described by + * @cpufreq_cdev. + */ +static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_cdev, + unsigned long freq) +{ + u32 raw_cpu_power; + + raw_cpu_power = cpu_freq_to_power(cpufreq_cdev, freq); + return (raw_cpu_power * cpufreq_cdev->last_load) / 100; +} + +/** + * cpufreq_get_requested_power() - get the current power + * @cdev: &thermal_cooling_device pointer + * @tz: a valid thermal zone device pointer + * @power: pointer in which to store the resulting power + * + * Calculate the current power consumption of the cpus in milliwatts + * and store it in @power. This function should actually calculate + * the requested power, but it's hard to get the frequency that + * cpufreq would have assigned if there were no thermal limits. + * Instead, we calculate the current power on the assumption that the + * immediate future will look like the immediate past. + * + * We use the current frequency and the average load since this + * function was last called. In reality, there could have been + * multiple opps since this function was last called and that affects + * the load calculation. While it's not perfectly accurate, this + * simplification is good enough and works. REVISIT this, as more + * complex code may be needed if experiments show that it's not + * accurate enough. + * + * Return: 0 on success, -E* if getting the static power failed. + */ +static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + u32 *power) +{ + unsigned long freq; + int i = 0, cpu; + u32 total_load = 0; + struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; + struct cpufreq_policy *policy = cpufreq_cdev->policy; + u32 *load_cpu = NULL; + + freq = cpufreq_quick_get(policy->cpu); + + if (trace_thermal_power_cpu_get_power_enabled()) { + u32 ncpus = cpumask_weight(policy->related_cpus); + + load_cpu = kcalloc(ncpus, sizeof(*load_cpu), GFP_KERNEL); + } + + for_each_cpu(cpu, policy->related_cpus) { + u32 load; + + if (cpu_online(cpu)) + load = get_load(cpufreq_cdev, cpu, i); + else + load = 0; + + total_load += load; + if (load_cpu) + load_cpu[i] = load; + + i++; + } + + cpufreq_cdev->last_load = total_load; + + *power = get_dynamic_power(cpufreq_cdev, freq); + + if (load_cpu) { + trace_thermal_power_cpu_get_power(policy->related_cpus, freq, + load_cpu, i, *power); + + kfree(load_cpu); + } + + return 0; +} + +/** + * cpufreq_state2power() - convert a cpu cdev state to power consumed + * @cdev: &thermal_cooling_device pointer + * @tz: a valid thermal zone device pointer + * @state: cooling device state to be converted + * @power: pointer in which to store the resulting power + * + * Convert cooling device state @state into power consumption in + * milliwatts assuming 100% load. Store the calculated power in + * @power. + * + * Return: 0 on success, -EINVAL if the cooling device state could not + * be converted into a frequency or other -E* if there was an error + * when calculating the static power. + */ +static int cpufreq_state2power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + unsigned long state, u32 *power) +{ + unsigned int freq, num_cpus, idx; + struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; + + /* Request state should be less than max_level */ + if (WARN_ON(state > cpufreq_cdev->max_level)) + return -EINVAL; + + num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus); + + idx = cpufreq_cdev->max_level - state; + freq = cpufreq_cdev->em->table[idx].frequency; + *power = cpu_freq_to_power(cpufreq_cdev, freq) * num_cpus; + + return 0; +} + +/** + * cpufreq_power2state() - convert power to a cooling device state + * @cdev: &thermal_cooling_device pointer + * @tz: a valid thermal zone device pointer + * @power: power in milliwatts to be converted + * @state: pointer in which to store the resulting state + * + * Calculate a cooling device state for the cpus described by @cdev + * that would allow them to consume at most @power mW and store it in + * @state. Note that this calculation depends on external factors + * such as the cpu load or the current static power. Calling this + * function with the same power as input can yield different cooling + * device states depending on those external factors. + * + * Return: 0 on success, -ENODEV if no cpus are online or -EINVAL if + * the calculated frequency could not be converted to a valid state. + * The latter should not happen unless the frequencies available to + * cpufreq have changed since the initialization of the cpu cooling + * device. + */ +static int cpufreq_power2state(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, u32 power, + unsigned long *state) +{ + unsigned int target_freq; + u32 last_load, normalised_power; + struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; + struct cpufreq_policy *policy = cpufreq_cdev->policy; + + last_load = cpufreq_cdev->last_load ?: 1; + normalised_power = (power * 100) / last_load; + target_freq = cpu_power_to_freq(cpufreq_cdev, normalised_power); + + *state = get_level(cpufreq_cdev, target_freq); + trace_thermal_power_cpu_limit(policy->related_cpus, target_freq, *state, + power); + return 0; +} + +static inline bool em_is_sane(struct cpufreq_cooling_device *cpufreq_cdev, + struct em_perf_domain *em) { + struct cpufreq_policy *policy; + unsigned int nr_levels; + + if (!em) + return false; + + policy = cpufreq_cdev->policy; + if (!cpumask_equal(policy->related_cpus, to_cpumask(em->cpus))) { + pr_err("The span of pd %*pbl is misaligned with cpufreq policy %*pbl\n", + cpumask_pr_args(to_cpumask(em->cpus)), + cpumask_pr_args(policy->related_cpus)); + return false; + } + + nr_levels = cpufreq_cdev->max_level + 1; + if (em->nr_cap_states != nr_levels) { + pr_err("The number of cap states in pd %*pbl (%u) doesn't match the number of cooling levels (%u)\n", + cpumask_pr_args(to_cpumask(em->cpus)), + em->nr_cap_states, nr_levels); + return false; + } + + return true; +} +#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */ + +static unsigned int get_state_freq(struct cpufreq_cooling_device *cpufreq_cdev, + unsigned long state) +{ + struct cpufreq_policy *policy; + unsigned long idx; + +#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR + /* Use the Energy Model table if available */ + if (cpufreq_cdev->em) { + idx = cpufreq_cdev->max_level - state; + return cpufreq_cdev->em->table[idx].frequency; + } +#endif + + /* Otherwise, fallback on the CPUFreq table */ + policy = cpufreq_cdev->policy; + if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) + idx = cpufreq_cdev->max_level - state; + else + idx = state; + + return policy->freq_table[idx].frequency; +} + +/* cpufreq cooling device callback functions are defined below */ + +/** + * cpufreq_get_max_state - callback function to get the max cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the max cooling state. + * + * Callback for the thermal cooling device to return the cpufreq + * max cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; + + *state = cpufreq_cdev->max_level; + return 0; +} + +/** + * cpufreq_get_cur_state - callback function to get the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the current cooling state. + * + * Callback for the thermal cooling device to return the cpufreq + * current cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; + + *state = cpufreq_cdev->cpufreq_state; + + return 0; +} + +/** + * cpufreq_set_cur_state - callback function to set the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: set this variable to the current cooling state. + * + * Callback for the thermal cooling device to change the cpufreq + * current cooling state. + * + * Return: 0 on success, an error code otherwise. + */ +static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; + + /* Request state should be less than max_level */ + if (WARN_ON(state > cpufreq_cdev->max_level)) + return -EINVAL; + + /* Check if the old cooling action is same as new cooling action */ + if (cpufreq_cdev->cpufreq_state == state) + return 0; + + cpufreq_cdev->cpufreq_state = state; + + return freq_qos_update_request(&cpufreq_cdev->qos_req, + get_state_freq(cpufreq_cdev, state)); +} + +/* Bind cpufreq callbacks to thermal cooling device ops */ + +static struct thermal_cooling_device_ops cpufreq_cooling_ops = { + .get_max_state = cpufreq_get_max_state, + .get_cur_state = cpufreq_get_cur_state, + .set_cur_state = cpufreq_set_cur_state, +}; + +/** + * __cpufreq_cooling_register - helper function to create cpufreq cooling device + * @np: a valid struct device_node to the cooling device device tree node + * @policy: cpufreq policy + * Normally this should be same as cpufreq policy->related_cpus. + * @em: Energy Model of the cpufreq policy + * + * This interface function registers the cpufreq cooling device with the name + * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq + * cooling devices. It also gives the opportunity to link the cooling device + * with a device tree node, in order to bind it via the thermal DT code. + * + * Return: a valid struct thermal_cooling_device pointer on success, + * on failure, it returns a corresponding ERR_PTR(). + */ +static struct thermal_cooling_device * +__cpufreq_cooling_register(struct device_node *np, + struct cpufreq_policy *policy, + struct em_perf_domain *em) +{ + struct thermal_cooling_device *cdev; + struct cpufreq_cooling_device *cpufreq_cdev; + char dev_name[THERMAL_NAME_LENGTH]; + unsigned int i, num_cpus; + struct device *dev; + int ret; + struct thermal_cooling_device_ops *cooling_ops; + + dev = get_cpu_device(policy->cpu); + if (unlikely(!dev)) { + pr_warn("No cpu device for cpu %d\n", policy->cpu); + return ERR_PTR(-ENODEV); + } + + + if (IS_ERR_OR_NULL(policy)) { + pr_err("%s: cpufreq policy isn't valid: %p\n", __func__, policy); + return ERR_PTR(-EINVAL); + } + + i = cpufreq_table_count_valid_entries(policy); + if (!i) { + pr_debug("%s: CPUFreq table not found or has no valid entries\n", + __func__); + return ERR_PTR(-ENODEV); + } + + cpufreq_cdev = kzalloc(sizeof(*cpufreq_cdev), GFP_KERNEL); + if (!cpufreq_cdev) + return ERR_PTR(-ENOMEM); + + cpufreq_cdev->policy = policy; + num_cpus = cpumask_weight(policy->related_cpus); + cpufreq_cdev->idle_time = kcalloc(num_cpus, + sizeof(*cpufreq_cdev->idle_time), + GFP_KERNEL); + if (!cpufreq_cdev->idle_time) { + cdev = ERR_PTR(-ENOMEM); + goto free_cdev; + } + + /* max_level is an index, not a counter */ + cpufreq_cdev->max_level = i - 1; + + ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); + if (ret < 0) { + cdev = ERR_PTR(ret); + goto free_idle_time; + } + cpufreq_cdev->id = ret; + + snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", + cpufreq_cdev->id); + + cooling_ops = &cpufreq_cooling_ops; + +#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR + if (em_is_sane(cpufreq_cdev, em)) { + cpufreq_cdev->em = em; + cooling_ops->get_requested_power = cpufreq_get_requested_power; + cooling_ops->state2power = cpufreq_state2power; + cooling_ops->power2state = cpufreq_power2state; + } else +#endif + if (policy->freq_table_sorted == CPUFREQ_TABLE_UNSORTED) { + pr_err("%s: unsorted frequency tables are not supported\n", + __func__); + cdev = ERR_PTR(-EINVAL); + goto remove_ida; + } + + ret = freq_qos_add_request(&policy->constraints, + &cpufreq_cdev->qos_req, FREQ_QOS_MAX, + get_state_freq(cpufreq_cdev, 0)); + if (ret < 0) { + pr_err("%s: Failed to add freq constraint (%d)\n", __func__, + ret); + cdev = ERR_PTR(ret); + goto remove_ida; + } + + cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, + cooling_ops); + if (IS_ERR(cdev)) + goto remove_qos_req; + + mutex_lock(&cooling_list_lock); + list_add(&cpufreq_cdev->node, &cpufreq_cdev_list); + mutex_unlock(&cooling_list_lock); + + return cdev; + +remove_qos_req: + freq_qos_remove_request(&cpufreq_cdev->qos_req); +remove_ida: + ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); +free_idle_time: + kfree(cpufreq_cdev->idle_time); +free_cdev: + kfree(cpufreq_cdev); + return cdev; +} + +/** + * cpufreq_cooling_register - function to create cpufreq cooling device. + * @policy: cpufreq policy + * + * This interface function registers the cpufreq cooling device with the name + * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq + * cooling devices. + * + * Return: a valid struct thermal_cooling_device pointer on success, + * on failure, it returns a corresponding ERR_PTR(). + */ +struct thermal_cooling_device * +cpufreq_cooling_register(struct cpufreq_policy *policy) +{ + return __cpufreq_cooling_register(NULL, policy, NULL); +} +EXPORT_SYMBOL_GPL(cpufreq_cooling_register); + +/** + * of_cpufreq_cooling_register - function to create cpufreq cooling device. + * @policy: cpufreq policy + * + * This interface function registers the cpufreq cooling device with the name + * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq + * cooling devices. Using this API, the cpufreq cooling device will be + * linked to the device tree node provided. + * + * Using this function, the cooling device will implement the power + * extensions by using a simple cpu power model. The cpus must have + * registered their OPPs using the OPP library. + * + * It also takes into account, if property present in policy CPU node, the + * static power consumed by the cpu. + * + * Return: a valid struct thermal_cooling_device pointer on success, + * and NULL on failure. + */ +struct thermal_cooling_device * +of_cpufreq_cooling_register(struct cpufreq_policy *policy) +{ + struct device_node *np = of_get_cpu_node(policy->cpu, NULL); + struct thermal_cooling_device *cdev = NULL; + + if (!np) { + pr_err("cpufreq_cooling: OF node not available for cpu%d\n", + policy->cpu); + return NULL; + } + + if (of_find_property(np, "#cooling-cells", NULL)) { + struct em_perf_domain *em = em_cpu_get(policy->cpu); + + cdev = __cpufreq_cooling_register(np, policy, em); + if (IS_ERR(cdev)) { + pr_err("cpufreq_cooling: cpu%d failed to register as cooling device: %ld\n", + policy->cpu, PTR_ERR(cdev)); + cdev = NULL; + } + } + + of_node_put(np); + return cdev; +} +EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register); + +/** + * cpufreq_cooling_unregister - function to remove cpufreq cooling device. + * @cdev: thermal cooling device pointer. + * + * This interface function unregisters the "thermal-cpufreq-%x" cooling device. + */ +void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) +{ + struct cpufreq_cooling_device *cpufreq_cdev; + + if (!cdev) + return; + + cpufreq_cdev = cdev->devdata; + + mutex_lock(&cooling_list_lock); + list_del(&cpufreq_cdev->node); + mutex_unlock(&cooling_list_lock); + + thermal_cooling_device_unregister(cdev); + freq_qos_remove_request(&cpufreq_cdev->qos_req); + ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); + kfree(cpufreq_cdev->idle_time); + kfree(cpufreq_cdev); +} +EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); diff --git a/include/linux/clock_cooling.h b/include/linux/clock_cooling.h index b5cebf766e02..4b0a69863656 100644 --- a/include/linux/clock_cooling.h +++ b/include/linux/clock_cooling.h @@ -7,7 +7,7 @@ * Copyright (C) 2013 Texas Instruments Inc. * Contact: Eduardo Valentin * - * Highly based on cpu_cooling.c. + * Highly based on cpufreq_cooling.c. * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) * Copyright (C) 2012 Amit Daniel */ -- cgit v1.2.3-59-g8ed1b From 8c24b85d2dd479558dc7a53be3f598007ec1a489 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 19 Dec 2019 23:21:52 +0100 Subject: thermal/drivers/of-thermal: Make of_thermal_destroy_zones static The function of_thermal_destroy_zones() is only used internally by the of_parse_thermal_zones() for rollbacking in case of error. Make it static and tag it as an __init function. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191219222154.16100-1-daniel.lezcano@linaro.org --- drivers/thermal/of-thermal.c | 64 +++++++++++++++++++++--------------------- drivers/thermal/thermal_core.h | 2 -- 2 files changed, 32 insertions(+), 34 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 5235aed2fadd..bb6c03685193 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -998,6 +998,38 @@ static inline void of_thermal_free_zone(struct __thermal_zone *tz) kfree(tz); } +/** + * of_thermal_destroy_zones - remove all zones parsed and allocated resources + * + * Finds all zones parsed and added to the thermal framework and remove them + * from the system, together with their resources. + * + */ +static __init void of_thermal_destroy_zones(void) +{ + struct device_node *np, *child; + + np = of_find_node_by_name(NULL, "thermal-zones"); + if (!np) { + pr_debug("unable to find thermal zones\n"); + return; + } + + for_each_available_child_of_node(np, child) { + struct thermal_zone_device *zone; + + zone = thermal_zone_get_zone_by_name(child->name); + if (IS_ERR(zone)) + continue; + + thermal_zone_device_unregister(zone); + kfree(zone->tzp); + kfree(zone->ops); + of_thermal_free_zone(zone->devdata); + } + of_node_put(np); +} + /** * of_parse_thermal_zones - parse device tree thermal data * @@ -1087,35 +1119,3 @@ exit_free: return -ENOMEM; } - -/** - * of_thermal_destroy_zones - remove all zones parsed and allocated resources - * - * Finds all zones parsed and added to the thermal framework and remove them - * from the system, together with their resources. - * - */ -void of_thermal_destroy_zones(void) -{ - struct device_node *np, *child; - - np = of_find_node_by_name(NULL, "thermal-zones"); - if (!np) { - pr_debug("unable to find thermal zones\n"); - return; - } - - for_each_available_child_of_node(np, child) { - struct thermal_zone_device *zone; - - zone = thermal_zone_get_zone_by_name(child->name); - if (IS_ERR(zone)) - continue; - - thermal_zone_device_unregister(zone); - kfree(zone->tzp); - kfree(zone->ops); - of_thermal_free_zone(zone->devdata); - } - of_node_put(np); -} diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 207b0cda70da..a9bf00e91d64 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -92,14 +92,12 @@ thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, /* device tree support */ #ifdef CONFIG_THERMAL_OF int of_parse_thermal_zones(void); -void of_thermal_destroy_zones(void); int of_thermal_get_ntrips(struct thermal_zone_device *); bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *); #else static inline int of_parse_thermal_zones(void) { return 0; } -static inline void of_thermal_destroy_zones(void) { } static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz) { return 0; -- cgit v1.2.3-59-g8ed1b From 93802b031b3664a72c54d731f5e68f4f5a4c56a0 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 19 Dec 2019 23:21:53 +0100 Subject: thermal/drivers/of-thermal: Move the of_thermal_free_zone() to the init section The function of_thermal_free_zone() is only used the initialization function which all belonging to the init section. Move it also to the __init section. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191219222154.16100-2-daniel.lezcano@linaro.org --- drivers/thermal/of-thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index bb6c03685193..3f66e35d6ecf 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -977,7 +977,7 @@ free_tz: return ERR_PTR(ret); } -static inline void of_thermal_free_zone(struct __thermal_zone *tz) +static __init void of_thermal_free_zone(struct __thermal_zone *tz) { struct __thermal_bind_params *tbp; int i, j; -- cgit v1.2.3-59-g8ed1b From dccc5c3b6f30f27ed0f1bea82221e18face20bef Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Thu, 19 Dec 2019 09:28:17 -0800 Subject: thermal/drivers/sun8i: Add thermal driver for H6/H5/H3/A64/A83T/R40 This patch adds the support for allwinner thermal sensor, within allwinner SoC. It will register sensors for thermal framework and use device tree to bind cooling device. Signed-off-by: Yangtao Li Signed-off-by: Ondrej Jirman Signed-off-by: Vasily Khoruzhick Acked-by: Maxime Ripard Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191219172823.1652600-2-anarsoul@gmail.com --- MAINTAINERS | 8 + drivers/thermal/Kconfig | 14 + drivers/thermal/Makefile | 1 + drivers/thermal/sun8i_thermal.c | 639 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 662 insertions(+) create mode 100644 drivers/thermal/sun8i_thermal.c (limited to 'drivers/thermal') diff --git a/MAINTAINERS b/MAINTAINERS index 08ebdc7071d7..405023416681 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -694,6 +694,14 @@ L: linux-crypto@vger.kernel.org S: Maintained F: drivers/crypto/allwinner/ +ALLWINNER THERMAL DRIVER +M: Vasily Khoruzhick +M: Yangtao Li +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml +F: drivers/thermal/sun8i_thermal.c + ALLWINNER VPU DRIVER M: Maxime Ripard M: Paul Kocialkowski diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index dc36941aef6e..5a05db5438d6 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -280,6 +280,20 @@ config SPEAR_THERMAL Enable this to plug the SPEAr thermal sensor driver into the Linux thermal framework. +config SUN8I_THERMAL + tristate "Allwinner sun8i thermal driver" + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM + depends on NVMEM + depends on OF + depends on RESET_CONTROLLER + help + Support for the sun8i thermal sensor driver into the Linux thermal + framework. + + To compile this driver as a module, choose M here: the + module will be called sun8i-thermal. + config ROCKCHIP_THERMAL tristate "Rockchip thermal driver" depends on ARCH_ROCKCHIP || COMPILE_TEST diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index d502a597a717..9fb88e26fb10 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -32,6 +32,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o obj-y += broadcom/ obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o +obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c new file mode 100644 index 000000000000..23a5f4aa4be4 --- /dev/null +++ b/drivers/thermal/sun8i_thermal.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thermal sensor driver for Allwinner SOC + * Copyright (C) 2019 Yangtao Li + * + * Based on the work of Icenowy Zheng + * Based on the work of Ondrej Jirman + * Based on the work of Josef Gajdusek + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SENSOR_NUM 4 + +#define FT_TEMP_MASK GENMASK(11, 0) +#define TEMP_CALIB_MASK GENMASK(11, 0) +#define CALIBRATE_DEFAULT 0x800 + +#define SUN8I_THS_CTRL0 0x00 +#define SUN8I_THS_CTRL2 0x40 +#define SUN8I_THS_IC 0x44 +#define SUN8I_THS_IS 0x48 +#define SUN8I_THS_MFC 0x70 +#define SUN8I_THS_TEMP_CALIB 0x74 +#define SUN8I_THS_TEMP_DATA 0x80 + +#define SUN50I_THS_CTRL0 0x00 +#define SUN50I_H6_THS_ENABLE 0x04 +#define SUN50I_H6_THS_PC 0x08 +#define SUN50I_H6_THS_DIC 0x10 +#define SUN50I_H6_THS_DIS 0x20 +#define SUN50I_H6_THS_MFC 0x30 +#define SUN50I_H6_THS_TEMP_CALIB 0xa0 +#define SUN50I_H6_THS_TEMP_DATA 0xc0 + +#define SUN8I_THS_CTRL0_T_ACQ0(x) (GENMASK(15, 0) & (x)) +#define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16) +#define SUN8I_THS_DATA_IRQ_STS(x) BIT(x + 8) + +#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16) +#define SUN50I_THS_FILTER_EN BIT(2) +#define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x)) +#define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12) +#define SUN50I_H6_THS_DATA_IRQ_STS(x) BIT(x) + +/* millidegree celsius */ +#define THS_EFUSE_CP_FT_MASK 0x3000 +#define THS_EFUSE_CP_FT_BIT 12 +#define THS_CALIBRATION_IN_FT 1 + +struct tsensor { + struct ths_device *tmdev; + struct thermal_zone_device *tzd; + int id; +}; + +struct ths_thermal_chip { + bool has_mod_clk; + bool has_bus_clk_reset; + int sensor_num; + int offset; + int scale; + int ft_deviation; + int temp_data_base; + int (*calibrate)(struct ths_device *tmdev, + u16 *caldata, int callen); + int (*init)(struct ths_device *tmdev); + int (*irq_ack)(struct ths_device *tmdev); + int (*calc_temp)(struct ths_device *tmdev, + int id, int reg); +}; + +struct ths_device { + const struct ths_thermal_chip *chip; + struct device *dev; + struct regmap *regmap; + struct reset_control *reset; + struct clk *bus_clk; + struct clk *mod_clk; + struct tsensor sensor[MAX_SENSOR_NUM]; + u32 cp_ft_flag; +}; + +/* Temp Unit: millidegree Celsius */ +static int sun8i_ths_calc_temp(struct ths_device *tmdev, + int id, int reg) +{ + return tmdev->chip->offset - (reg * tmdev->chip->scale / 10); +} + +static int sun50i_h5_calc_temp(struct ths_device *tmdev, + int id, int reg) +{ + if (reg >= 0x500) + return -1191 * reg / 10 + 223000; + else if (!id) + return -1452 * reg / 10 + 259000; + else + return -1590 * reg / 10 + 276000; +} + +static int sun8i_ths_get_temp(void *data, int *temp) +{ + struct tsensor *s = data; + struct ths_device *tmdev = s->tmdev; + int val = 0; + + regmap_read(tmdev->regmap, tmdev->chip->temp_data_base + + 0x4 * s->id, &val); + + /* ths have no data yet */ + if (!val) + return -EAGAIN; + + *temp = tmdev->chip->calc_temp(tmdev, s->id, val); + /* + * According to the original sdk, there are some platforms(rarely) + * that add a fixed offset value after calculating the temperature + * value. We can't simply put it on the formula for calculating the + * temperature above, because the formula for calculating the + * temperature above is also used when the sensor is calibrated. If + * do this, the correct calibration formula is hard to know. + */ + *temp += tmdev->chip->ft_deviation; + + return 0; +} + +static const struct thermal_zone_of_device_ops ths_ops = { + .get_temp = sun8i_ths_get_temp, +}; + +static const struct regmap_config config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, + .max_register = 0xfc, +}; + +static int sun8i_h3_irq_ack(struct ths_device *tmdev) +{ + int i, state, ret = 0; + + regmap_read(tmdev->regmap, SUN8I_THS_IS, &state); + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + if (state & SUN8I_THS_DATA_IRQ_STS(i)) { + regmap_write(tmdev->regmap, SUN8I_THS_IS, + SUN8I_THS_DATA_IRQ_STS(i)); + ret |= BIT(i); + } + } + + return ret; +} + +static int sun50i_h6_irq_ack(struct ths_device *tmdev) +{ + int i, state, ret = 0; + + regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state); + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) { + regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS, + SUN50I_H6_THS_DATA_IRQ_STS(i)); + ret |= BIT(i); + } + } + + return ret; +} + +static irqreturn_t sun8i_irq_thread(int irq, void *data) +{ + struct ths_device *tmdev = data; + int i, state; + + state = tmdev->chip->irq_ack(tmdev); + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + if (state & BIT(i)) + thermal_zone_device_update(tmdev->sensor[i].tzd, + THERMAL_EVENT_UNSPECIFIED); + } + + return IRQ_HANDLED; +} + +static int sun8i_h3_ths_calibrate(struct ths_device *tmdev, + u16 *caldata, int callen) +{ + int i; + + if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num) + return -EINVAL; + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + int offset = (i % 2) << 4; + + regmap_update_bits(tmdev->regmap, + SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)), + 0xfff << offset, + caldata[i] << offset); + } + + return 0; +} + +static int sun50i_h6_ths_calibrate(struct ths_device *tmdev, + u16 *caldata, int callen) +{ + struct device *dev = tmdev->dev; + int i, ft_temp; + + if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num) + return -EINVAL; + + /* + * efuse layout: + * + * 0 11 16 32 + * +-------+-------+-------+ + * |temp| |sensor0|sensor1| + * +-------+-------+-------+ + * + * The calibration data on the H6 is the ambient temperature and + * sensor values that are filled during the factory test stage. + * + * The unit of stored FT temperature is 0.1 degreee celusis. + * + * We need to calculate a delta between measured and caluclated + * register values and this will become a calibration offset. + */ + ft_temp = (caldata[0] & FT_TEMP_MASK) * 100; + tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK) + >> THS_EFUSE_CP_FT_BIT; + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + int sensor_reg = caldata[i + 1]; + int cdata, offset; + int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg); + + /* + * Calibration data is CALIBRATE_DEFAULT - (calculated + * temperature from sensor reading at factory temperature + * minus actual factory temperature) * 14.88 (scale from + * temperature to register values) + */ + cdata = CALIBRATE_DEFAULT - + ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale); + if (cdata & ~TEMP_CALIB_MASK) { + /* + * Calibration value more than 12-bit, but calibration + * register is 12-bit. In this case, ths hardware can + * still work without calibration, although the data + * won't be so accurate. + */ + dev_warn(dev, "sensor%d is not calibrated.\n", i); + continue; + } + + offset = (i % 2) * 16; + regmap_update_bits(tmdev->regmap, + SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4), + 0xfff << offset, + cdata << offset); + } + + return 0; +} + +static int sun8i_ths_calibrate(struct ths_device *tmdev) +{ + struct nvmem_cell *calcell; + struct device *dev = tmdev->dev; + u16 *caldata; + size_t callen; + int ret = 0; + + calcell = devm_nvmem_cell_get(dev, "calibration"); + if (IS_ERR(calcell)) { + if (PTR_ERR(calcell) == -EPROBE_DEFER) + return -EPROBE_DEFER; + /* + * Even if the external calibration data stored in sid is + * not accessible, the THS hardware can still work, although + * the data won't be so accurate. + * + * The default value of calibration register is 0x800 for + * every sensor, and the calibration value is usually 0x7xx + * or 0x8xx, so they won't be away from the default value + * for a lot. + * + * So here we do not return error if the calibartion data is + * not available, except the probe needs deferring. + */ + goto out; + } + + caldata = nvmem_cell_read(calcell, &callen); + if (IS_ERR(caldata)) { + ret = PTR_ERR(caldata); + goto out; + } + + tmdev->chip->calibrate(tmdev, caldata, callen); + + kfree(caldata); +out: + return ret; +} + +static int sun8i_ths_resource_init(struct ths_device *tmdev) +{ + struct device *dev = tmdev->dev; + struct platform_device *pdev = to_platform_device(dev); + void __iomem *base; + int ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + tmdev->regmap = devm_regmap_init_mmio(dev, base, &config); + if (IS_ERR(tmdev->regmap)) + return PTR_ERR(tmdev->regmap); + + if (tmdev->chip->has_bus_clk_reset) { + tmdev->reset = devm_reset_control_get(dev, 0); + if (IS_ERR(tmdev->reset)) + return PTR_ERR(tmdev->reset); + + tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(tmdev->bus_clk)) + return PTR_ERR(tmdev->bus_clk); + } + + if (tmdev->chip->has_mod_clk) { + tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod"); + if (IS_ERR(tmdev->mod_clk)) + return PTR_ERR(tmdev->mod_clk); + } + + ret = reset_control_deassert(tmdev->reset); + if (ret) + return ret; + + ret = clk_prepare_enable(tmdev->bus_clk); + if (ret) + goto assert_reset; + + ret = clk_set_rate(tmdev->mod_clk, 24000000); + if (ret) + goto bus_disable; + + ret = clk_prepare_enable(tmdev->mod_clk); + if (ret) + goto bus_disable; + + ret = sun8i_ths_calibrate(tmdev); + if (ret) + goto mod_disable; + + return 0; + +mod_disable: + clk_disable_unprepare(tmdev->mod_clk); +bus_disable: + clk_disable_unprepare(tmdev->bus_clk); +assert_reset: + reset_control_assert(tmdev->reset); + + return ret; +} + +static int sun8i_h3_thermal_init(struct ths_device *tmdev) +{ + int val; + + /* average over 4 samples */ + regmap_write(tmdev->regmap, SUN8I_THS_MFC, + SUN50I_THS_FILTER_EN | + SUN50I_THS_FILTER_TYPE(1)); + /* + * clkin = 24MHz + * filter_samples = 4 + * period = 0.25s + * + * x = period * clkin / 4096 / filter_samples - 1 + * = 365 + */ + val = GENMASK(7 + tmdev->chip->sensor_num, 8); + regmap_write(tmdev->regmap, SUN8I_THS_IC, + SUN50I_H6_THS_PC_TEMP_PERIOD(365) | val); + /* + * T_acq = 20us + * clkin = 24MHz + * + * x = T_acq * clkin - 1 + * = 479 + */ + regmap_write(tmdev->regmap, SUN8I_THS_CTRL0, + SUN8I_THS_CTRL0_T_ACQ0(479)); + val = GENMASK(tmdev->chip->sensor_num - 1, 0); + regmap_write(tmdev->regmap, SUN8I_THS_CTRL2, + SUN8I_THS_CTRL2_T_ACQ1(479) | val); + + return 0; +} + +/* + * Without this undocummented value, the returned temperatures would + * be higher than real ones by about 20C. + */ +#define SUN50I_H6_CTRL0_UNK 0x0000002f + +static int sun50i_h6_thermal_init(struct ths_device *tmdev) +{ + int val; + + /* + * T_acq = 20us + * clkin = 24MHz + * + * x = T_acq * clkin - 1 + * = 479 + */ + regmap_write(tmdev->regmap, SUN50I_THS_CTRL0, + SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479)); + /* average over 4 samples */ + regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC, + SUN50I_THS_FILTER_EN | + SUN50I_THS_FILTER_TYPE(1)); + /* + * clkin = 24MHz + * filter_samples = 4 + * period = 0.25s + * + * x = period * clkin / 4096 / filter_samples - 1 + * = 365 + */ + regmap_write(tmdev->regmap, SUN50I_H6_THS_PC, + SUN50I_H6_THS_PC_TEMP_PERIOD(365)); + /* enable sensor */ + val = GENMASK(tmdev->chip->sensor_num - 1, 0); + regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val); + /* thermal data interrupt enable */ + val = GENMASK(tmdev->chip->sensor_num - 1, 0); + regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val); + + return 0; +} + +static int sun8i_ths_register(struct ths_device *tmdev) +{ + int i; + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + tmdev->sensor[i].tmdev = tmdev; + tmdev->sensor[i].id = i; + tmdev->sensor[i].tzd = + devm_thermal_zone_of_sensor_register(tmdev->dev, + i, + &tmdev->sensor[i], + &ths_ops); + if (IS_ERR(tmdev->sensor[i].tzd)) + return PTR_ERR(tmdev->sensor[i].tzd); + } + + return 0; +} + +static int sun8i_ths_probe(struct platform_device *pdev) +{ + struct ths_device *tmdev; + struct device *dev = &pdev->dev; + int ret, irq; + + tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL); + if (!tmdev) + return -ENOMEM; + + tmdev->dev = dev; + tmdev->chip = of_device_get_match_data(&pdev->dev); + if (!tmdev->chip) + return -EINVAL; + + platform_set_drvdata(pdev, tmdev); + + ret = sun8i_ths_resource_init(tmdev); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = tmdev->chip->init(tmdev); + if (ret) + return ret; + + ret = sun8i_ths_register(tmdev); + if (ret) + return ret; + + /* + * Avoid entering the interrupt handler, the thermal device is not + * registered yet, we deffer the registration of the interrupt to + * the end. + */ + ret = devm_request_threaded_irq(dev, irq, NULL, + sun8i_irq_thread, + IRQF_ONESHOT, "ths", tmdev); + if (ret) + return ret; + + return 0; +} + +static int sun8i_ths_remove(struct platform_device *pdev) +{ + struct ths_device *tmdev = platform_get_drvdata(pdev); + + clk_disable_unprepare(tmdev->mod_clk); + clk_disable_unprepare(tmdev->bus_clk); + reset_control_assert(tmdev->reset); + + return 0; +} + +static const struct ths_thermal_chip sun8i_a83t_ths = { + .sensor_num = 3, + .scale = 705, + .offset = 191668, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct ths_thermal_chip sun8i_h3_ths = { + .sensor_num = 1, + .scale = 1211, + .offset = 217000, + .has_mod_clk = true, + .has_bus_clk_reset = true, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct ths_thermal_chip sun8i_r40_ths = { + .sensor_num = 3, + .offset = 251086, + .scale = 1130, + .has_mod_clk = true, + .has_bus_clk_reset = true, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct ths_thermal_chip sun50i_a64_ths = { + .sensor_num = 3, + .offset = 260890, + .scale = 1170, + .has_mod_clk = true, + .has_bus_clk_reset = true, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct ths_thermal_chip sun50i_h5_ths = { + .sensor_num = 2, + .has_mod_clk = true, + .has_bus_clk_reset = true, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun50i_h5_calc_temp, +}; + +static const struct ths_thermal_chip sun50i_h6_ths = { + .sensor_num = 2, + .has_bus_clk_reset = true, + .ft_deviation = 7000, + .offset = 187744, + .scale = 672, + .temp_data_base = SUN50I_H6_THS_TEMP_DATA, + .calibrate = sun50i_h6_ths_calibrate, + .init = sun50i_h6_thermal_init, + .irq_ack = sun50i_h6_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct of_device_id of_ths_match[] = { + { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths }, + { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths }, + { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths }, + { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths }, + { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths }, + { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, of_ths_match); + +static struct platform_driver ths_driver = { + .probe = sun8i_ths_probe, + .remove = sun8i_ths_remove, + .driver = { + .name = "sun8i-thermal", + .of_match_table = of_ths_match, + }, +}; +module_platform_driver(ths_driver); + +MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From c1fde6e19f23f78146a4aeb4a70bbf273224bb0c Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Wed, 8 Jan 2020 00:20:43 +0100 Subject: thermal: generic-adc: silence "no lookup table" on deferred probe A "generic-adc-thermal" without "temperature-lookup-table" is perfectly valid since commit d36e2fa0253875 ("thermal: generic-adc: make lookup table optional"). On deferred probe the message "no lookup table, assuming DAC channel returns milliCelcius" is still logged. Prevent this message on deferred probe of the IIO channel by first looking up the IIO channel. Signed-off-by: Martin Blumenstingl Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200107232044.889075-2-martin.blumenstingl@googlemail.com --- drivers/thermal/thermal-generic-adc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index ae5743c9a894..226e3c2b7469 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -124,13 +124,6 @@ static int gadc_thermal_probe(struct platform_device *pdev) if (!gti) return -ENOMEM; - ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti); - if (ret < 0) - return ret; - - gti->dev = &pdev->dev; - platform_set_drvdata(pdev, gti); - gti->channel = devm_iio_channel_get(&pdev->dev, "sensor-channel"); if (IS_ERR(gti->channel)) { ret = PTR_ERR(gti->channel); @@ -139,6 +132,13 @@ static int gadc_thermal_probe(struct platform_device *pdev) return ret; } + ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti); + if (ret < 0) + return ret; + + gti->dev = &pdev->dev; + platform_set_drvdata(pdev, gti); + gti->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, gti, &gadc_thermal_ops); if (IS_ERR(gti->tz_dev)) { -- cgit v1.2.3-59-g8ed1b From 07d243a6249712c4a32caf18057a4008d3610d48 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Wed, 8 Jan 2020 00:20:44 +0100 Subject: thermal: generic-adc: silence info message for IIO_TEMP channels Since commit d36e2fa0253875 ("thermal: generic-adc: make lookup table optional") "generic-adc-thermal" can be used with an IIO_TEMP channel. In this case the following message is logged at probe time: no lookup table, assuming DAC channel returns milliCelcius Silence this info message if the channel type is known to be in milli celsius. Keep this message when the channel type is unknown or not of type temperature. Signed-off-by: Martin Blumenstingl Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200107232044.889075-3-martin.blumenstingl@googlemail.com --- drivers/thermal/thermal-generic-adc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 226e3c2b7469..73665c3ccfe0 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -76,13 +76,17 @@ static int gadc_thermal_read_linear_lookup_table(struct device *dev, struct gadc_thermal_info *gti) { struct device_node *np = dev->of_node; + enum iio_chan_type chan_type; int ntable; int ret; ntable = of_property_count_elems_of_size(np, "temperature-lookup-table", sizeof(u32)); if (ntable <= 0) { - dev_notice(dev, "no lookup table, assuming DAC channel returns milliCelcius\n"); + ret = iio_get_channel_type(gti->channel, &chan_type); + if (ret || chan_type != IIO_TEMP) + dev_notice(dev, + "no lookup table, assuming DAC channel returns milliCelcius\n"); return 0; } -- cgit v1.2.3-59-g8ed1b From ca07ee4e3de468582e9a370ccfad3e4cf83b5268 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 4 Jan 2020 16:20:53 +0100 Subject: thermal: exynos: Rename Samsung and Exynos to lowercase Fix up inconsistent usage of upper and lowercase letters in "Samsung" and "Exynos" names. "SAMSUNG" and "EXYNOS" are not abbreviations but regular trademarked names. Therefore they should be written with lowercase letters starting with capital letter. The lowercase "Exynos" name is promoted by its manufacturer Samsung Electronics Co., Ltd., in advertisement materials and on website. Although advertisement materials usually use uppercase "SAMSUNG", the lowercase version is used in all legal aspects (e.g. on Wikipedia and in privacy/legal statements on https://www.samsung.com/semiconductor/privacy-global/). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200104152107.11407-7-krzk@kernel.org --- Documentation/driver-api/thermal/exynos_thermal.rst | 6 +++--- drivers/thermal/samsung/Kconfig | 2 +- drivers/thermal/samsung/exynos_tmu.c | 4 ++-- include/dt-bindings/thermal/thermal_exynos.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/thermal') diff --git a/Documentation/driver-api/thermal/exynos_thermal.rst b/Documentation/driver-api/thermal/exynos_thermal.rst index d4e4a5b75805..764df4ab584d 100644 --- a/Documentation/driver-api/thermal/exynos_thermal.rst +++ b/Documentation/driver-api/thermal/exynos_thermal.rst @@ -4,7 +4,7 @@ Kernel driver exynos_tmu Supported chips: -* ARM SAMSUNG EXYNOS4, EXYNOS5 series of SoC +* ARM Samsung Exynos4, Exynos5 series of SoC Datasheet: Not publicly available @@ -14,7 +14,7 @@ Authors: Amit Daniel TMU controller Description: --------------------------- -This driver allows to read temperature inside SAMSUNG EXYNOS4/5 series of SoC. +This driver allows to read temperature inside Samsung Exynos4/5 series of SoC. The chip only exposes the measured 8-bit temperature code value through a register. @@ -43,7 +43,7 @@ The three equations are: Trimming info for 85 degree Celsius (stored at TRIMINFO register) Temperature code measured at 85 degree Celsius which is unchanged -TMU(Thermal Management Unit) in EXYNOS4/5 generates interrupt +TMU(Thermal Management Unit) in Exynos4/5 generates interrupt when temperature exceeds pre-defined levels. The maximum number of configurable threshold is five. The threshold levels are defined as follows:: diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig index fe0d2ba51392..f4eff5a41a84 100644 --- a/drivers/thermal/samsung/Kconfig +++ b/drivers/thermal/samsung/Kconfig @@ -5,7 +5,7 @@ config EXYNOS_THERMAL depends on HAS_IOMEM help If you say yes here you get support for the TMU (Thermal Management - Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises + Unit) driver for Samsung Exynos series of SoCs. This driver initialises the TMU, reports temperature and handles cooling action if defined. This driver uses the Exynos core thermal APIs and TMU configuration data from the supported SoCs. diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 8193b66a3f83..fd4a17812f33 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit) + * exynos_tmu.c - Samsung Exynos TMU (Thermal Management Unit) * * Copyright (C) 2014 Samsung Electronics * Bartlomiej Zolnierkiewicz @@ -1186,7 +1186,7 @@ static struct platform_driver exynos_tmu_driver = { module_platform_driver(exynos_tmu_driver); -MODULE_DESCRIPTION("EXYNOS TMU Driver"); +MODULE_DESCRIPTION("Exynos TMU Driver"); MODULE_AUTHOR("Donggeun Kim "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:exynos-tmu"); diff --git a/include/dt-bindings/thermal/thermal_exynos.h b/include/dt-bindings/thermal/thermal_exynos.h index 642e4e7f4084..52fcb51dda3c 100644 --- a/include/dt-bindings/thermal/thermal_exynos.h +++ b/include/dt-bindings/thermal/thermal_exynos.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * thermal_exynos.h - Samsung EXYNOS TMU device tree definitions + * thermal_exynos.h - Samsung Exynos TMU device tree definitions * * Copyright (C) 2014 Samsung Electronics * Lukasz Majewski -- cgit v1.2.3-59-g8ed1b From d8186285f1fdbc847af75dfeaa4fcc574753c97c Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Mon, 6 Jan 2020 17:46:38 +0000 Subject: thermal: sun8i: Fix r40 ths number According to the spec, r40 has 2 thermal sensors. Sensor0 located in the CPU, another in the GPU. Fixes: dccc5c3b6f30f ("thermal/drivers/sun8i: Add thermal driver for H6/H5/H3/A64/A83T/R40") Signed-off-by: Yangtao Li Tested-by: Corentin Labbe Tested-on: sun8i-r40-bananapi-m2-ultra Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200106174639.20862-1-tiny.windzz@gmail.com --- drivers/thermal/sun8i_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index 23a5f4aa4be4..c5661d7c3e20 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -565,7 +565,7 @@ static const struct ths_thermal_chip sun8i_h3_ths = { }; static const struct ths_thermal_chip sun8i_r40_ths = { - .sensor_num = 3, + .sensor_num = 2, .offset = 251086, .scale = 1130, .has_mod_clk = true, -- cgit v1.2.3-59-g8ed1b From 69d5f3a9c0c9044202db8793f34f0f0eb1e0302b Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 12 Jan 2020 17:13:18 +0000 Subject: thermal: sun8i: Fix using plain integer as NULL pointer in sun8i_ths_resource_init sparse returns a warning: "drivers/thermal/sun8i_thermal.c:341:60: sparse: sparse: Using plain integer as NULL pointer". Fix it by replacing the zero integer by a NULL pointer. Reported-by: kbuild test robot Signed-off-by: Yangtao Li Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200112171318.23025-1-tiny.windzz@gmail.com --- drivers/thermal/sun8i_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index c5661d7c3e20..4bcde9491edb 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -338,7 +338,7 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev) return PTR_ERR(tmdev->regmap); if (tmdev->chip->has_bus_clk_reset) { - tmdev->reset = devm_reset_control_get(dev, 0); + tmdev->reset = devm_reset_control_get(dev, NULL); if (IS_ERR(tmdev->reset)) return PTR_ERR(tmdev->reset); -- cgit v1.2.3-59-g8ed1b From 291292cddccaca99f48b4701121bcb1870e4f7ce Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 12 Jan 2020 18:09:25 +0000 Subject: thermal: sun8i: Remove unused variable and unneeded macros The cp_ft_flag variable is not used after initialization, so delete it. After that, THS_EFUSE_CP_FT_MASK, THS_EFUSE_CP_FT_BIT and THS_CALIBRATION_IN_FT are not needed, so delete them. Signed-off-by: Yangtao Li Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200112180925.23705-1-tiny.windzz@gmail.com --- drivers/thermal/sun8i_thermal.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index 4bcde9491edb..bd7549f9ecba 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -54,9 +54,6 @@ #define SUN50I_H6_THS_DATA_IRQ_STS(x) BIT(x) /* millidegree celsius */ -#define THS_EFUSE_CP_FT_MASK 0x3000 -#define THS_EFUSE_CP_FT_BIT 12 -#define THS_CALIBRATION_IN_FT 1 struct tsensor { struct ths_device *tmdev; @@ -88,7 +85,6 @@ struct ths_device { struct clk *bus_clk; struct clk *mod_clk; struct tsensor sensor[MAX_SENSOR_NUM]; - u32 cp_ft_flag; }; /* Temp Unit: millidegree Celsius */ @@ -244,8 +240,6 @@ static int sun50i_h6_ths_calibrate(struct ths_device *tmdev, * register values and this will become a calibration offset. */ ft_temp = (caldata[0] & FT_TEMP_MASK) * 100; - tmdev->cp_ft_flag = (caldata[0] & THS_EFUSE_CP_FT_MASK) - >> THS_EFUSE_CP_FT_BIT; for (i = 0; i < tmdev->chip->sensor_num; i++) { int sensor_reg = caldata[i + 1]; -- cgit v1.2.3-59-g8ed1b From 85f0ad221317c18e6032b6735f6b36c8a6a96ea9 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 28 Dec 2019 17:19:04 +0000 Subject: thermal: sun8i: Add hwmon support Expose sun8i thermal as a HWMON device. Signed-off-by: Yangtao Li Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191228171904.24618-1-tiny.windzz@gmail.com --- drivers/thermal/sun8i_thermal.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index bd7549f9ecba..74d73be16496 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -20,6 +20,8 @@ #include #include +#include "thermal_hwmon.h" + #define MAX_SENSOR_NUM 4 #define FT_TEMP_MASK GENMASK(11, 0) @@ -471,6 +473,10 @@ static int sun8i_ths_register(struct ths_device *tmdev) &ths_ops); if (IS_ERR(tmdev->sensor[i].tzd)) return PTR_ERR(tmdev->sensor[i].tzd); + + if (devm_thermal_add_hwmon_sysfs(tmdev->sensor[i].tzd)) + dev_warn(tmdev->dev, + "Failed to add hwmon sysfs attributes\n"); } return 0; -- cgit v1.2.3-59-g8ed1b From 59b781352dc4cb9ae27a8ddae0cda979d29d8af7 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 13 Jan 2020 19:56:16 +0100 Subject: thermal: Add BCM2711 thermal driver This adds the thermal sensor driver for the Broadcom BCM2711 SoC, which is placed on the Raspberry Pi 4. The driver only provides SoC temperature reading so far. Signed-off-by: Stefan Wahren Reviewed-by: Florian Fainelli Reviewed-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1578941778-23321-3-git-send-email-stefan.wahren@i2se.com --- drivers/thermal/broadcom/Kconfig | 7 ++ drivers/thermal/broadcom/Makefile | 1 + drivers/thermal/broadcom/bcm2711_thermal.c | 123 +++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 drivers/thermal/broadcom/bcm2711_thermal.c (limited to 'drivers/thermal') diff --git a/drivers/thermal/broadcom/Kconfig b/drivers/thermal/broadcom/Kconfig index cf43e1520de9..061f1db6edc9 100644 --- a/drivers/thermal/broadcom/Kconfig +++ b/drivers/thermal/broadcom/Kconfig @@ -1,4 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only +config BCM2711_THERMAL + tristate "Broadcom AVS RO thermal sensor driver" + depends on ARCH_BCM2835 || COMPILE_TEST + depends on THERMAL_OF && MFD_SYSCON + help + Support for thermal sensors on Broadcom BCM2711 SoCs. + config BCM2835_THERMAL tristate "Thermal sensors on bcm2835 SoC" depends on ARCH_BCM2835 || COMPILE_TEST diff --git a/drivers/thermal/broadcom/Makefile b/drivers/thermal/broadcom/Makefile index 490ab1f7a86d..c917b243d5f0 100644 --- a/drivers/thermal/broadcom/Makefile +++ b/drivers/thermal/broadcom/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_BCM2711_THERMAL) += bcm2711_thermal.o 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/bcm2711_thermal.c b/drivers/thermal/broadcom/bcm2711_thermal.c new file mode 100644 index 000000000000..67c2a737bc9d --- /dev/null +++ b/drivers/thermal/broadcom/bcm2711_thermal.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Broadcom AVS RO thermal sensor driver + * + * based on brcmstb_thermal + * + * Copyright (C) 2020 Stefan Wahren + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../thermal_hwmon.h" + +#define AVS_RO_TEMP_STATUS 0x200 +#define AVS_RO_TEMP_STATUS_VALID_MSK (BIT(16) | BIT(10)) +#define AVS_RO_TEMP_STATUS_DATA_MSK GENMASK(9, 0) + +struct bcm2711_thermal_priv { + struct regmap *regmap; + struct thermal_zone_device *thermal; +}; + +static int bcm2711_get_temp(void *data, int *temp) +{ + struct bcm2711_thermal_priv *priv = data; + int slope = thermal_zone_get_slope(priv->thermal); + int offset = thermal_zone_get_offset(priv->thermal); + u32 val; + int ret; + long t; + + ret = regmap_read(priv->regmap, AVS_RO_TEMP_STATUS, &val); + if (ret) + return ret; + + if (!(val & AVS_RO_TEMP_STATUS_VALID_MSK)) + return -EIO; + + val &= AVS_RO_TEMP_STATUS_DATA_MSK; + + /* Convert a HW code to a temperature reading (millidegree celsius) */ + t = slope * val + offset; + + *temp = t < 0 ? 0 : t; + + return 0; +} + +static const struct thermal_zone_of_device_ops bcm2711_thermal_of_ops = { + .get_temp = bcm2711_get_temp, +}; + +static const struct of_device_id bcm2711_thermal_id_table[] = { + { .compatible = "brcm,bcm2711-thermal" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2711_thermal_id_table); + +static int bcm2711_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal; + struct bcm2711_thermal_priv *priv; + struct device *dev = &pdev->dev; + struct device_node *parent; + struct regmap *regmap; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* get regmap from syscon node */ + parent = of_get_parent(dev->of_node); /* parent should be syscon node */ + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "failed to get regmap: %d\n", ret); + return ret; + } + priv->regmap = regmap; + + thermal = devm_thermal_zone_of_sensor_register(dev, 0, priv, + &bcm2711_thermal_of_ops); + if (IS_ERR(thermal)) { + ret = PTR_ERR(thermal); + dev_err(dev, "could not register sensor: %d\n", ret); + return ret; + } + + priv->thermal = thermal; + + thermal->tzp->no_hwmon = false; + ret = thermal_add_hwmon_sysfs(thermal); + if (ret) + return ret; + + return 0; +} + +static struct platform_driver bcm2711_thermal_driver = { + .probe = bcm2711_thermal_probe, + .driver = { + .name = "bcm2711_thermal", + .of_match_table = bcm2711_thermal_id_table, + }, +}; +module_platform_driver(bcm2711_thermal_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Stefan Wahren"); +MODULE_DESCRIPTION("Broadcom AVS RO thermal sensor driver"); -- cgit v1.2.3-59-g8ed1b From 263c8c4c76d9da63f1f8805ac5fc62a270448301 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 15 Jan 2020 13:54:17 +0100 Subject: thermal: rcar_thermal: Use usleep_range() instead of udelay() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rcar_thermal_update_temp() takes a mutex, so it is always called in a context that can sleep. Hence replace the 300 µs busy loop by a call to usleep_range(), to allow other threads to run. Signed-off-by: Geert Uytterhoeven Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200115125417.5263-1-geert+renesas@glider.be --- drivers/thermal/rcar_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index d0873de718da..a8dd96d2d24c 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -219,7 +219,7 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) * to get stable temperature. * see "Usage Notes" on datasheet */ - udelay(300); + usleep_range(300, 400); new = rcar_thermal_read(priv, THSSR) & CTEMP; if (new == old) { -- cgit v1.2.3-59-g8ed1b From e1ff6fc22f19e2af8adbad618526b80067911d40 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 14 Jan 2020 11:06:02 -0800 Subject: thermal: brcmstb_thermal: Do not use DT coefficients At the time the brcmstb_thermal driver and its binding were merged, the DT binding did not make the coefficients properties a mandatory one, therefore all users of the brcmstb_thermal driver out there have a non functional implementation with zero coefficients. Even if these properties were provided, the formula used for computation is incorrect. The coefficients are entirely process specific (right now, only 28nm is supported) and not board or SoC specific, it is therefore appropriate to hard code them in the driver given the compatibility string we are probed with which has to be updated whenever a new process is introduced. We remove the existing coefficients definition since subsequent patches are going to add support for a new process and will introduce new coefficients as well. Fixes: 9e03cf1b2dd5 ("thermal: add brcmstb AVS TMON driver") Signed-off-by: Florian Fainelli Reviewed-by: Amit Kucheria Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200114190607.29339-2-f.fainelli@gmail.com --- drivers/thermal/broadcom/brcmstb_thermal.c | 31 +++++++++--------------------- 1 file changed, 9 insertions(+), 22 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 5825ac581f56..680f1a070606 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -49,7 +49,7 @@ #define AVS_TMON_TP_TEST_ENABLE 0x20 /* Default coefficients */ -#define AVS_TMON_TEMP_SLOPE -487 +#define AVS_TMON_TEMP_SLOPE 487 #define AVS_TMON_TEMP_OFFSET 410040 /* HW related temperature constants */ @@ -108,23 +108,12 @@ struct brcmstb_thermal_priv { 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; + return (AVS_TMON_TEMP_OFFSET - + (int)((code & AVS_TMON_TEMP_MAX) * AVS_TMON_TEMP_SLOPE)); } /* @@ -136,20 +125,18 @@ static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, 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); + return AVS_TMON_TEMP_MAX; /* Maximum code value */ - if (temp >= offset) + if (temp >= AVS_TMON_TEMP_OFFSET) return 0; /* Minimum code value */ if (low) - return (u32)(DIV_ROUND_UP(offset - temp, abs(slope))); + return (u32)(DIV_ROUND_UP(AVS_TMON_TEMP_OFFSET - temp, + AVS_TMON_TEMP_SLOPE)); else - return (u32)((offset - temp) / abs(slope)); + return (u32)((AVS_TMON_TEMP_OFFSET - temp) / + AVS_TMON_TEMP_SLOPE); } static int brcmstb_get_temp(void *data, int *temp) -- cgit v1.2.3-59-g8ed1b From 8bcda3257acf86b652fb98b7d1ef6bf6c911bd28 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 14 Jan 2020 11:06:03 -0800 Subject: thermal: brcmstb_thermal: Prepare to support a different process The driver is currently assuming that it is operating with a 28nm process chip, which has a specific formula to convert temperature to a code and vice versa. Update the code to support providing two key values: offset and multiplier to derive the correct formulas. Signed-off-by: Florian Fainelli Reviewed-by: Amit Kucheria Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200114190607.29339-3-f.fainelli@gmail.com --- drivers/thermal/broadcom/brcmstb_thermal.c | 47 +++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 14 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 680f1a070606..2d555e7b884a 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -102,18 +102,27 @@ static struct avs_tmon_trip avs_tmon_trips[] = { }, }; +struct brcmstb_thermal_params { + unsigned int offset; + unsigned int mult; +}; + struct brcmstb_thermal_priv { void __iomem *tmon_base; struct device *dev; struct thermal_zone_device *thermal; + /* Process specific thermal parameters used for calculations */ + const struct brcmstb_thermal_params *temp_params; }; /* Convert a HW code to a temperature reading (millidegree celsius) */ -static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, +static inline int avs_tmon_code_to_temp(struct brcmstb_thermal_priv *priv, u32 code) { - return (AVS_TMON_TEMP_OFFSET - - (int)((code & AVS_TMON_TEMP_MAX) * AVS_TMON_TEMP_SLOPE)); + int offset = priv->temp_params->offset; + int mult = priv->temp_params->mult; + + return (offset - (int)((code & AVS_TMON_TEMP_MASK) * mult)); } /* @@ -122,21 +131,22 @@ static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, * @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, +static inline u32 avs_tmon_temp_to_code(struct brcmstb_thermal_priv *priv, int temp, bool low) { + int offset = priv->temp_params->offset; + int mult = priv->temp_params->mult; + if (temp < AVS_TMON_TEMP_MIN) return AVS_TMON_TEMP_MAX; /* Maximum code value */ - if (temp >= AVS_TMON_TEMP_OFFSET) + if (temp >= offset) return 0; /* Minimum code value */ if (low) - return (u32)(DIV_ROUND_UP(AVS_TMON_TEMP_OFFSET - temp, - AVS_TMON_TEMP_SLOPE)); + return (u32)(DIV_ROUND_UP(offset - temp, mult)); else - return (u32)((AVS_TMON_TEMP_OFFSET - temp) / - AVS_TMON_TEMP_SLOPE); + return (u32)((offset - temp) / mult); } static int brcmstb_get_temp(void *data, int *temp) @@ -154,7 +164,7 @@ static int brcmstb_get_temp(void *data, int *temp) val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift; - t = avs_tmon_code_to_temp(priv->thermal, val); + t = avs_tmon_code_to_temp(priv, val); if (t < 0) *temp = 0; else @@ -188,7 +198,7 @@ static int avs_tmon_get_trip_temp(struct brcmstb_thermal_priv *priv, val &= trip->reg_msk; val >>= trip->reg_shift; - return avs_tmon_code_to_temp(priv->thermal, val); + return avs_tmon_code_to_temp(priv, val); } static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv, @@ -201,7 +211,7 @@ static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv, 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, + val = avs_tmon_temp_to_code(priv, temp, type == TMON_TRIP_TYPE_LOW); val <<= trip->reg_shift; @@ -218,7 +228,7 @@ 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); + return avs_tmon_code_to_temp(priv, val); } static irqreturn_t brcmstb_tmon_irq_thread(int irq, void *data) @@ -282,8 +292,13 @@ static const struct thermal_zone_of_device_ops of_ops = { .set_trips = brcmstb_set_trips, }; +static const struct brcmstb_thermal_params brcmstb_28nm_params = { + .offset = 410040, + .mult = 487, +}; + static const struct of_device_id brcmstb_thermal_id_table[] = { - { .compatible = "brcm,avs-tmon" }, + { .compatible = "brcm,avs-tmon", .data = &brcmstb_28nm_params }, {}, }; MODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table); @@ -299,6 +314,10 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->temp_params = of_device_get_match_data(&pdev->dev); + if (!priv->temp_params) + return -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->tmon_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->tmon_base)) -- cgit v1.2.3-59-g8ed1b From c9a506139b7a772be286c34493792ce1b428e992 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 14 Jan 2020 11:06:05 -0800 Subject: thermal: brcmstb_thermal: Add 16nm process thermal parameters Match the 7216 compatible string in order to derive the correct 16nm process thermal parameters to obtain correct readings. Reviewed-by: Amit Kucheria Signed-off-by: Florian Fainelli Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200114190607.29339-5-f.fainelli@gmail.com --- drivers/thermal/broadcom/brcmstb_thermal.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 2d555e7b884a..4b328586959f 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -292,12 +292,18 @@ static const struct thermal_zone_of_device_ops of_ops = { .set_trips = brcmstb_set_trips, }; +static const struct brcmstb_thermal_params brcmstb_16nm_params = { + .offset = 457829, + .mult = 557, +}; + static const struct brcmstb_thermal_params brcmstb_28nm_params = { .offset = 410040, .mult = 487, }; static const struct of_device_id brcmstb_thermal_id_table[] = { + { .compatible = "brcm,avs-tmon-bcm7216", .data = &brcmstb_16nm_params }, { .compatible = "brcm,avs-tmon", .data = &brcmstb_28nm_params }, {}, }; -- cgit v1.2.3-59-g8ed1b From eaf7a88d4f4c48efff77e0c66462d152d3334bcc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 14 Jan 2020 11:06:06 -0800 Subject: thermal: brcmstb_thermal: Restructure interrupt registration If we are successful grabbing the interrupt resource, then register an interrupt handler, this makes it easier to support the interrupt as being optional, which is it for 7216. Reviewed-by: Amit Kucheria Signed-off-by: Florian Fainelli Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200114190607.29339-6-f.fainelli@gmail.com --- drivers/thermal/broadcom/brcmstb_thermal.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 4b328586959f..6ded4b9c53be 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -343,16 +343,15 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) priv->thermal = thermal; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "could not get IRQ\n"); - return irq; - } - 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); - return ret; + if (irq >= 0) { + 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); + return ret; + } } dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n"); -- cgit v1.2.3-59-g8ed1b From 5fdd4e310b450939aac5486cd20e222a127cf114 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 14 Jan 2020 11:06:07 -0800 Subject: thermal: brcmstb_thermal: Register different ops per process Since we do not have interrupts on BCM7216, we cannot have trip point crossing, the thermal subsystem expects us to provide a NULL set_trips operation in that case, so make it possible to provide per-process thermal_zone_of_device_ops Reviewed-by: Amit Kucheria Signed-off-by: Florian Fainelli Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200114190607.29339-7-f.fainelli@gmail.com --- drivers/thermal/broadcom/brcmstb_thermal.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 6ded4b9c53be..8df5edef1ded 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -105,6 +105,7 @@ static struct avs_tmon_trip avs_tmon_trips[] = { struct brcmstb_thermal_params { unsigned int offset; unsigned int mult; + const struct thermal_zone_of_device_ops *of_ops; }; struct brcmstb_thermal_priv { @@ -287,19 +288,25 @@ static int brcmstb_set_trips(void *data, int low, int high) return 0; } -static const struct thermal_zone_of_device_ops of_ops = { +static const struct thermal_zone_of_device_ops brcmstb_16nm_of_ops = { .get_temp = brcmstb_get_temp, - .set_trips = brcmstb_set_trips, }; static const struct brcmstb_thermal_params brcmstb_16nm_params = { .offset = 457829, .mult = 557, + .of_ops = &brcmstb_16nm_of_ops, +}; + +static const struct thermal_zone_of_device_ops brcmstb_28nm_of_ops = { + .get_temp = brcmstb_get_temp, + .set_trips = brcmstb_set_trips, }; static const struct brcmstb_thermal_params brcmstb_28nm_params = { .offset = 410040, .mult = 487, + .of_ops = &brcmstb_28nm_of_ops, }; static const struct of_device_id brcmstb_thermal_id_table[] = { @@ -311,6 +318,7 @@ MODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table); static int brcmstb_thermal_probe(struct platform_device *pdev) { + const struct thermal_zone_of_device_ops *of_ops; struct thermal_zone_device *thermal; struct brcmstb_thermal_priv *priv; struct resource *res; @@ -331,9 +339,10 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) priv->dev = &pdev->dev; platform_set_drvdata(pdev, priv); + of_ops = priv->temp_params->of_ops; thermal = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, - &of_ops); + of_ops); if (IS_ERR(thermal)) { ret = PTR_ERR(thermal); dev_err(&pdev->dev, "could not register sensor: %d\n", ret); -- cgit v1.2.3-59-g8ed1b From f64a6583d3f527b297b88441e1c20e6ed45f8f56 Mon Sep 17 00:00:00 2001 From: "Swaminathan, Nivedita" Date: Thu, 12 Dec 2019 12:30:25 -0800 Subject: thermal: int340x: processor_thermal: Add Jasper Lake support Added new PCI id for Jasper Lake processor thermal device. Signed-off-by: Swaminathan, Nivedita Signed-off-by: Srinivas Pandruvada Acked-by: Zhang Rui Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191212203025.36310-1-srinivas.pandruvada@linux.intel.com --- drivers/thermal/intel/int340x_thermal/processor_thermal_device.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c index 89a015387283..b1fd34516e28 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c @@ -42,6 +42,9 @@ /* IceLake thermal reporting device */ #define PCI_DEVICE_ID_PROC_ICL_THERMAL 0x8a03 +/* JasperLake thermal reporting device */ +#define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4503 + #define DRV_NAME "proc_thermal" struct power_config { @@ -724,6 +727,7 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_GLK_THERMAL)}, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_ICL_THERMAL), .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_JSL_THERMAL)}, { 0, }, }; -- cgit v1.2.3-59-g8ed1b From 66dd8b802c555a9becd81679dc0ee57ede67b142 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Fri, 6 Dec 2019 15:55:31 +0800 Subject: thermal: intel: Fix unmatched pci_release_region The driver calls pci_request_regions() in probe and uses pci_release_regions() in probe failure. However, it calls pci_release_region() in remove, which does match the other two calls. Use pci_release_regions() instead to unify them. Signed-off-by: Chuhong Yuan Acked-by: Zhang Rui Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191206075531.18637-1-hslester96@gmail.com --- drivers/thermal/intel/intel_pch_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/intel/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c index 4f0bb8f502e1..5f7798b8d35f 100644 --- a/drivers/thermal/intel/intel_pch_thermal.c +++ b/drivers/thermal/intel/intel_pch_thermal.c @@ -365,7 +365,7 @@ static void intel_pch_thermal_remove(struct pci_dev *pdev) thermal_zone_device_unregister(ptd->tzd); iounmap(ptd->hw_base); pci_set_drvdata(pdev, NULL); - pci_release_region(pdev, 0); + pci_release_regions(pdev); pci_disable_device(pdev); } -- cgit v1.2.3-59-g8ed1b From 35709c4ee772afc3818cd6d42d123d608feeaa33 Mon Sep 17 00:00:00 2001 From: Gayatri Kammela Date: Wed, 11 Dec 2019 12:00:43 -0800 Subject: thermal: intel: intel_pch_thermal: Add Comet Lake (CML) platform support Add Comet Lake to the list of the platforms to support intel_pch_thermal driver. Cc: Zhang rui Cc: Srinivas Pandruvada Signed-off-by: Gayatri Kammela Acked-by: Zhang Rui Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191211200043.4985-1-gayatri.kammela@intel.com --- drivers/thermal/intel/intel_pch_thermal.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/intel/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c index 5f7798b8d35f..ed75a0c603e7 100644 --- a/drivers/thermal/intel/intel_pch_thermal.c +++ b/drivers/thermal/intel/intel_pch_thermal.c @@ -23,6 +23,7 @@ #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 */ +#define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */ /* Wildcat Point-LP PCH Thermal registers */ #define WPT_TEMP 0x0000 /* Temperature */ @@ -272,6 +273,7 @@ enum board_ids { board_wpt, board_skl, board_cnl, + board_cml, }; static const struct board_info { @@ -294,6 +296,10 @@ static const struct board_info { .name = "pch_cannonlake", .ops = &pch_dev_ops_wpt, }, + [board_cml] = { + .name = "pch_cometlake", + .ops = &pch_dev_ops_wpt, + } }; static int intel_pch_thermal_probe(struct pci_dev *pdev, @@ -398,6 +404,8 @@ static const struct pci_device_id intel_pch_thermal_id[] = { .driver_data = board_cnl, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H), .driver_data = board_cnl, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H), + .driver_data = board_cml, }, { 0, }, }; MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id); -- cgit v1.2.3-59-g8ed1b From dff6d4f80509d1aaf07da9534f3f4b6876876caf Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 17 Jan 2020 17:05:53 +0100 Subject: thermal: rcar_thermal: Remove temperature bound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware manual states that the operation of the sensor is not guaranteed outside the range of -45°C to 125°C, not that the readings are invalid. Remove the bound check and try to deliver temperature readings even if we are outside the guaranteed operation range. Signed-off-by: Niklas Söderlund Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200117160554.3812787-2-niklas.soderlund+renesas@ragnatech.se --- drivers/thermal/rcar_thermal.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index a8dd96d2d24c..8f1aafa2044e 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -275,12 +275,7 @@ static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, tmp = MCELSIUS((priv->ctemp * 5) - 60); mutex_unlock(&priv->lock); - if ((tmp < MCELSIUS(-45)) || (tmp > MCELSIUS(125))) { - struct device *dev = rcar_priv_to_dev(priv); - - dev_err(dev, "it couldn't measure temperature correctly\n"); - return -EIO; - } + /* Guaranteed operating range is -45C to 125C. */ *temp = tmp; -- cgit v1.2.3-59-g8ed1b From 0f510a2457cbbba18a98492bab1bf540be57ebd1 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 17 Jan 2020 17:05:54 +0100 Subject: thermal: rcar_gen3_thermal: Remove temperature bound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware manual states that the operation of the sensor is not guaranteed with temperatures above 125°C, not that the readings are invalid. Remove the bound check and try to deliver temperature readings even if we are outside the guaranteed operation range. Signed-off-by: Niklas Söderlund Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200117160554.3812787-3-niklas.soderlund+renesas@ragnatech.se --- drivers/thermal/rcar_gen3_thermal.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 1460cf9d9f1c..72877bdc072d 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -182,9 +182,7 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) tsc->coef.a2); mcelsius = FIXPT_TO_MCELSIUS(val); - /* Make sure we are inside specifications */ - if ((mcelsius < MCELSIUS(-40)) || (mcelsius > MCELSIUS(125))) - return -EIO; + /* Guaranteed operating range is -40C to 125C. */ /* Round value to device granularity setting */ *temp = rcar_gen3_thermal_round(mcelsius); -- cgit v1.2.3-59-g8ed1b From 370f995d587852dc8da4ce5687acae8be61d7529 Mon Sep 17 00:00:00 2001 From: Peter Mamonov Date: Tue, 27 Aug 2019 17:39:52 +0300 Subject: thermal: of: Make thermal_zone_of_sensor_register return -ENODEV if a sensor OF node is missing When devm_thermal_zone_of_sensor_register() is called from hwmon_thermal_add_sensor() it is possible that the relevant sensor is missing an OF node. In this case thermal_zone_of_sensor_register() returns -EINVAL which causes hwmon_thermal_add_sensor() to fail as well. This patch changes relevant return code of thermal_zone_of_sensor_register() to -ENODEV, which is tolerated by hwmon_thermal_add_sensor(). Here is a particular case of such behaviour: the Marvell ethernet PHYs driver registers hwmon device for the built-in temperature sensor (see drivers/net/phy/marvell.c). Since the sensor doesn't have associated OF node devm_hwmon_device_register() returns error which ultimately causes failure of the PHY driver's probe function. Signed-off-by: Peter Mamonov Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20190827143952.19591-1-pmamonov@gmail.com --- drivers/thermal/of-thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 3f66e35d6ecf..ef0baa954ff0 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -493,7 +493,7 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, if (!dev || !dev->of_node) { of_node_put(np); - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENODEV); } sensor_np = of_node_get(dev->of_node); -- cgit v1.2.3-59-g8ed1b From d401652c1c92f164bc5590548aa3a90f4636af89 Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Fri, 10 Jan 2020 11:16:00 +0100 Subject: thermal: stm32: Fix icifr register name Fix a mistake with the ICIFR register name. Signed-off-by: Pascal Paillet Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200110101605.24984-2-p.paillet@st.com --- drivers/thermal/st/stm_thermal.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index cf9ddc52f30e..7835e109692e 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -30,7 +30,7 @@ #define DTS_DR_OFFSET 0x1C #define DTS_SR_OFFSET 0x20 #define DTS_ITENR_OFFSET 0x24 -#define DTS_CIFR_OFFSET 0x28 +#define DTS_ICIFR_OFFSET 0x28 /* DTS_CFGR1 register mask definitions */ #define HSREF_CLK_DIV_MASK GENMASK(30, 24) @@ -122,10 +122,10 @@ static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata) value = readl_relaxed(sensor->base + DTS_SR_OFFSET); if ((value & LOW_THRESHOLD) == LOW_THRESHOLD) - writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); + writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_ICIFR_OFFSET); if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD) - writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); + writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_ICIFR_OFFSET); thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); @@ -347,7 +347,7 @@ static int stm_enable_irq(struct stm_thermal_sensor *sensor) */ /* Make sure LOW_THRESHOLD IT is clear before enabling */ - writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); + writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_ICIFR_OFFSET); /* Enable IT generation for low threshold */ value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); @@ -356,7 +356,7 @@ static int stm_enable_irq(struct stm_thermal_sensor *sensor) /* Enable the low temperature threshold if needed */ if (sensor->low_temp_enabled) { /* Make sure HIGH_THRESHOLD IT is clear before enabling */ - writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); + writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_ICIFR_OFFSET); /* Enable IT generation for high threshold */ value |= HIGH_THRESHOLD; -- cgit v1.2.3-59-g8ed1b From d4a7e0538fe957fc78187468fa404060da7fa6f4 Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Fri, 10 Jan 2020 11:16:01 +0100 Subject: thermal: stm32: Rework sensor mode management Be sure get_temp returns an error while disabling or enabling the device. Set THERMAL_DEVICE_ENABLED state at the end of power on function. Set THERMAL_DEVICE_DISABLED state at the beginning of power off function. Signed-off-by: Pascal Paillet Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200110101605.24984-3-p.paillet@st.com --- drivers/thermal/st/stm_thermal.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index 7835e109692e..a21fa7e0c72b 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -160,6 +160,8 @@ static int stm_sensor_power_on(struct stm_thermal_sensor *sensor) writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); + sensor->mode = THERMAL_DEVICE_ENABLED; + return 0; } @@ -167,6 +169,8 @@ static int stm_sensor_power_off(struct stm_thermal_sensor *sensor) { u32 value; + sensor->mode = THERMAL_DEVICE_DISABLED; + /* Stop measuring */ value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); value &= ~TS1_START; @@ -374,7 +378,6 @@ static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor) { int ret; - sensor->mode = THERMAL_DEVICE_DISABLED; ret = stm_sensor_power_off(sensor); if (ret) @@ -576,8 +579,6 @@ static int stm_thermal_suspend(struct device *dev) if (ret) return ret; - sensor->mode = THERMAL_DEVICE_DISABLED; - return 0; } @@ -590,7 +591,6 @@ static int stm_thermal_resume(struct device *dev) if (ret) return ret; - sensor->mode = THERMAL_DEVICE_ENABLED; return 0; } @@ -718,8 +718,6 @@ static int stm_thermal_probe(struct platform_device *pdev) if (ret) goto err_tz; - sensor->mode = THERMAL_DEVICE_ENABLED; - dev_info(&pdev->dev, "%s: Driver initialized successfully\n", __func__); -- cgit v1.2.3-59-g8ed1b From 1f64fa365148f003e39b28a86ce3ada020ea3eec Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Fri, 10 Jan 2020 11:16:02 +0100 Subject: thermal: stm32: Disable interrupts at probe In case of CPU reset, the interrupts could be enabled at boot time. Disable interrupts and clear flags. Signed-off-by: Pascal Paillet Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200110101605.24984-4-p.paillet@st.com --- drivers/thermal/st/stm_thermal.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index a21fa7e0c72b..679d38867206 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -51,6 +51,12 @@ /* DTS_DR register mask definitions */ #define TS1_MFREQ_MASK GENMASK(15, 0) +/* DTS_ITENR register mask definitions */ +#define ITENR_MASK (GENMASK(2, 0) | GENMASK(6, 4)) + +/* DTS_ICIFR register mask definitions */ +#define ICIFR_MASK (GENMASK(2, 0) | GENMASK(6, 4)) + /* Less significant bit position definitions */ #define TS1_T0_POS 16 #define TS1_SMP_TIME_POS 16 @@ -330,12 +336,10 @@ static int stm_disable_irq(struct stm_thermal_sensor *sensor) { u32 value; - /* Disable IT generation for low and high thresholds */ + /* Disable IT generation */ value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); - writel_relaxed(value & ~(LOW_THRESHOLD | HIGH_THRESHOLD), - sensor->base + DTS_ITENR_OFFSET); - - dev_dbg(sensor->dev, "%s: IT disabled on sensor side", __func__); + value &= ~ITENR_MASK; + writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET); return 0; } @@ -645,6 +649,11 @@ static int stm_thermal_probe(struct platform_device *pdev) return PTR_ERR(sensor->clk); } + stm_disable_irq(sensor); + + /* Clear irq flags */ + writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET); + /* Register IRQ into GIC */ ret = stm_register_irq(sensor); if (ret) -- cgit v1.2.3-59-g8ed1b From dd4c3919a83074683aab3c1950c69e1131f3c46e Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Fri, 10 Jan 2020 11:16:03 +0100 Subject: thermal: stm32: Handle multiple trip points Let the thermal framework handle the trip points instead of custom code inside the driver. This is backward compatible, simplifies the driver and offers the possibility to the user to set any trip point he needs. stm_thermal_set_trips callback that is registered to set_trips ops to handle the low and high thresholds and replaces stm_thermal_set_threshold and stm_thermal_update_threshold functions. modify irq enable to handle the thresholds. Signed-off-by: Pascal Paillet Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200110101605.24984-5-p.paillet@st.com --- drivers/thermal/st/stm_thermal.c | 298 +++++++++++---------------------------- 1 file changed, 86 insertions(+), 212 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index 679d38867206..29e7ee89adf6 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -61,6 +61,7 @@ #define TS1_T0_POS 16 #define TS1_SMP_TIME_POS 16 #define TS1_HITTHD_POS 16 +#define TS1_LITTHD_POS 0 #define HSREF_CLK_DIV_POS 24 /* DTS_CFGR1 bit definitions */ @@ -97,43 +98,49 @@ struct stm_thermal_sensor { struct thermal_zone_device *th_dev; enum thermal_device_mode mode; struct clk *clk; - int high_temp; - int low_temp; - int temp_critical; - int temp_passive; unsigned int low_temp_enabled; - int num_trips; + unsigned int high_temp_enabled; int irq; - unsigned int irq_enabled; void __iomem *base; int t0, fmt0, ramp_coeff; }; -static irqreturn_t stm_thermal_alarm_irq(int irq, void *sdata) +static int stm_enable_irq(struct stm_thermal_sensor *sensor) { - struct stm_thermal_sensor *sensor = sdata; + u32 value; - disable_irq_nosync(irq); - sensor->irq_enabled = false; + dev_dbg(sensor->dev, "low:%d high:%d\n", sensor->low_temp_enabled, + sensor->high_temp_enabled); + + /* Disable IT generation for low and high thresholds */ + value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); + value &= ~(LOW_THRESHOLD | HIGH_THRESHOLD); - return IRQ_WAKE_THREAD; + if (sensor->low_temp_enabled) + value |= HIGH_THRESHOLD; + + if (sensor->high_temp_enabled) + value |= LOW_THRESHOLD; + + /* Enable interrupts */ + writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET); + + return 0; } -static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata) +static irqreturn_t stm_thermal_irq_handler(int irq, void *sdata) { - u32 value; struct stm_thermal_sensor *sensor = sdata; - /* read IT reason in SR and clear flags */ - value = readl_relaxed(sensor->base + DTS_SR_OFFSET); + dev_dbg(sensor->dev, "sr:%d\n", + readl_relaxed(sensor->base + DTS_SR_OFFSET)); - if ((value & LOW_THRESHOLD) == LOW_THRESHOLD) - writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_ICIFR_OFFSET); + thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); - if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD) - writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_ICIFR_OFFSET); + stm_enable_irq(sensor); - thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); + /* Acknoledge all DTS irqs */ + writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET); return IRQ_HANDLED; } @@ -298,39 +305,6 @@ static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor, return 0; } -static int stm_thermal_set_threshold(struct stm_thermal_sensor *sensor) -{ - u32 value, th; - int ret; - - value = readl_relaxed(sensor->base + DTS_ITR1_OFFSET); - - /* Erase threshold content */ - value &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK); - - /* Retrieve the sample threshold number th for a given temperature */ - ret = stm_thermal_calculate_threshold(sensor, sensor->high_temp, &th); - if (ret) - return ret; - - value |= th & TS1_LITTHD_MASK; - - if (sensor->low_temp_enabled) { - /* Retrieve the sample threshold */ - ret = stm_thermal_calculate_threshold(sensor, sensor->low_temp, - &th); - if (ret) - return ret; - - value |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS)); - } - - /* Write value on the Low interrupt threshold */ - writel_relaxed(value, sensor->base + DTS_ITR1_OFFSET); - - return 0; -} - /* Disable temperature interrupt */ static int stm_disable_irq(struct stm_thermal_sensor *sensor) { @@ -344,66 +318,48 @@ static int stm_disable_irq(struct stm_thermal_sensor *sensor) return 0; } -/* Enable temperature interrupt */ -static int stm_enable_irq(struct stm_thermal_sensor *sensor) +static int stm_thermal_set_trips(void *data, int low, int high) { - u32 value; + struct stm_thermal_sensor *sensor = data; + u32 itr1, th; + int ret; - /* - * Code below enables High temperature threshold using a low threshold - * sampling value - */ + dev_dbg(sensor->dev, "set trips %d <--> %d\n", low, high); - /* Make sure LOW_THRESHOLD IT is clear before enabling */ - writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_ICIFR_OFFSET); + /* Erase threshold content */ + itr1 = readl_relaxed(sensor->base + DTS_ITR1_OFFSET); + itr1 &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK); - /* Enable IT generation for low threshold */ - value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); - value |= LOW_THRESHOLD; + /* + * Disable low-temp if "low" is too small. As per thermal framework + * API, we use -INT_MAX rather than INT_MIN. + */ - /* Enable the low temperature threshold if needed */ - if (sensor->low_temp_enabled) { - /* Make sure HIGH_THRESHOLD IT is clear before enabling */ - writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_ICIFR_OFFSET); + if (low > -INT_MAX) { + sensor->low_temp_enabled = 1; + ret = stm_thermal_calculate_threshold(sensor, low, &th); + if (ret) + return ret; - /* Enable IT generation for high threshold */ - value |= HIGH_THRESHOLD; + itr1 |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS)); + } else { + sensor->low_temp_enabled = 0; } - /* Enable thresholds */ - writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET); - - dev_dbg(sensor->dev, "%s: IT enabled on sensor side", __func__); - - return 0; -} - -static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor) -{ - int ret; - - - ret = stm_sensor_power_off(sensor); - if (ret) - return ret; - - ret = stm_disable_irq(sensor); - if (ret) - return ret; - - ret = stm_thermal_set_threshold(sensor); - if (ret) - return ret; - - ret = stm_enable_irq(sensor); - if (ret) - return ret; + /* Disable high-temp if "high" is too big. */ + if (high < INT_MAX) { + sensor->high_temp_enabled = 1; + ret = stm_thermal_calculate_threshold(sensor, high, &th); + if (ret) + return ret; - ret = stm_sensor_power_on(sensor); - if (ret) - return ret; + itr1 |= (TS1_LITTHD_MASK & (th << TS1_LITTHD_POS)); + } else { + sensor->high_temp_enabled = 0; + } - sensor->mode = THERMAL_DEVICE_ENABLED; + /* Write new threshod values*/ + writel_relaxed(itr1, sensor->base + DTS_ITR1_OFFSET); return 0; } @@ -447,42 +403,6 @@ static int stm_thermal_get_temp(void *data, int *temp) *temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) / sensor->ramp_coeff)); - dev_dbg(sensor->dev, "%s: temperature = %d millicelsius", - __func__, *temp); - - /* Update thresholds */ - if (sensor->num_trips > 1) { - /* Update alarm threshold value to next higher trip point */ - if (sensor->high_temp == sensor->temp_passive && - celsius(*temp) >= sensor->temp_passive) { - sensor->high_temp = sensor->temp_critical; - sensor->low_temp = sensor->temp_passive; - sensor->low_temp_enabled = true; - ret = stm_thermal_update_threshold(sensor); - if (ret) - return ret; - } - - if (sensor->high_temp == sensor->temp_critical && - celsius(*temp) < sensor->temp_passive) { - sensor->high_temp = sensor->temp_passive; - sensor->low_temp_enabled = false; - ret = stm_thermal_update_threshold(sensor); - if (ret) - return ret; - } - - /* - * Re-enable alarm IRQ if temperature below critical - * temperature - */ - if (!sensor->irq_enabled && - (celsius(*temp) < sensor->temp_critical)) { - sensor->irq_enabled = true; - enable_irq(sensor->irq); - } - } - return 0; } @@ -500,8 +420,8 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor) } ret = devm_request_threaded_irq(dev, sensor->irq, - stm_thermal_alarm_irq, - stm_thermal_alarm_irq_thread, + NULL, + stm_thermal_irq_handler, IRQF_ONESHOT, dev->driver->name, sensor); if (ret) { @@ -510,8 +430,6 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor) return ret; } - sensor->irq_enabled = true; - dev_dbg(dev, "%s: thermal IRQ registered", __func__); return 0; @@ -521,6 +439,8 @@ static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor) { int ret; + stm_disable_irq(sensor); + ret = stm_sensor_power_off(sensor); if (ret) return ret; @@ -533,7 +453,6 @@ static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor) static int stm_thermal_prepare(struct stm_thermal_sensor *sensor) { int ret; - struct device *dev = sensor->dev; ret = clk_prepare_enable(sensor->clk); if (ret) @@ -547,26 +466,8 @@ static int stm_thermal_prepare(struct stm_thermal_sensor *sensor) if (ret) goto thermal_unprepare; - /* Set threshold(s) for IRQ */ - ret = stm_thermal_set_threshold(sensor); - if (ret) - goto thermal_unprepare; - - ret = stm_enable_irq(sensor); - if (ret) - goto thermal_unprepare; - - ret = stm_sensor_power_on(sensor); - if (ret) { - dev_err(dev, "%s: failed to power on sensor\n", __func__); - goto irq_disable; - } - return 0; -irq_disable: - stm_disable_irq(sensor); - thermal_unprepare: clk_disable_unprepare(sensor->clk); @@ -595,6 +496,12 @@ static int stm_thermal_resume(struct device *dev) if (ret) return ret; + ret = stm_sensor_power_on(sensor); + if (ret) + return ret; + + thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); + stm_enable_irq(sensor); return 0; } @@ -604,6 +511,7 @@ SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume); static const struct thermal_zone_of_device_ops stm_tz_ops = { .get_temp = stm_thermal_get_temp, + .set_trips = stm_thermal_set_trips, }; static const struct of_device_id stm_thermal_of_match[] = { @@ -616,9 +524,8 @@ static int stm_thermal_probe(struct platform_device *pdev) { struct stm_thermal_sensor *sensor; struct resource *res; - const struct thermal_trip *trip; void __iomem *base; - int ret, i; + int ret; if (!pdev->dev.of_node) { dev_err(&pdev->dev, "%s: device tree node not found\n", @@ -654,10 +561,18 @@ static int stm_thermal_probe(struct platform_device *pdev) /* Clear irq flags */ writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET); - /* Register IRQ into GIC */ - ret = stm_register_irq(sensor); - if (ret) + /* Configure and enable HW sensor */ + ret = stm_thermal_prepare(sensor); + if (ret) { + dev_err(&pdev->dev, "Error preprare sensor: %d\n", ret); return ret; + } + + ret = stm_sensor_power_on(sensor); + if (ret) { + dev_err(&pdev->dev, "Error power on sensor: %d\n", ret); + return ret; + } sensor->th_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, sensor, @@ -670,53 +585,12 @@ static int stm_thermal_probe(struct platform_device *pdev) return ret; } - if (!sensor->th_dev->ops->get_crit_temp) { - /* Critical point must be provided */ - ret = -EINVAL; - goto err_tz; - } - - ret = sensor->th_dev->ops->get_crit_temp(sensor->th_dev, - &sensor->temp_critical); - if (ret) { - dev_err(&pdev->dev, - "Not able to read critical_temp: %d\n", ret); + /* Register IRQ into GIC */ + ret = stm_register_irq(sensor); + if (ret) goto err_tz; - } - - sensor->temp_critical = celsius(sensor->temp_critical); - - /* Set thresholds for IRQ */ - sensor->high_temp = sensor->temp_critical; - - trip = of_thermal_get_trip_points(sensor->th_dev); - sensor->num_trips = of_thermal_get_ntrips(sensor->th_dev); - - /* Find out passive temperature if it exists */ - for (i = (sensor->num_trips - 1); i >= 0; i--) { - if (trip[i].type == THERMAL_TRIP_PASSIVE) { - sensor->temp_passive = celsius(trip[i].temperature); - /* Update high temperature threshold */ - sensor->high_temp = sensor->temp_passive; - } - } - /* - * Ensure low_temp_enabled flag is disabled. - * By disabling low_temp_enabled, low threshold IT will not be - * configured neither enabled because it is not needed as high - * threshold is set on the lowest temperature trip point after - * probe. - */ - sensor->low_temp_enabled = false; - - /* Configure and enable HW sensor */ - ret = stm_thermal_prepare(sensor); - if (ret) { - dev_err(&pdev->dev, - "Not able to enable sensor: %d\n", ret); - goto err_tz; - } + stm_enable_irq(sensor); /* * Thermal_zone doesn't enable hwmon as default, -- cgit v1.2.3-59-g8ed1b From 9d8593f22ea33a88f89075b14fc58a95fc15b8ae Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Fri, 10 Jan 2020 11:16:04 +0100 Subject: thermal: stm32: Improve temperature computing Change the way of computing to avoid rounds by 1 or 2 degrees. Also simplify the sampling time management that is hard-coded to maximum value during probe. Signed-off-by: Pascal Paillet Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200110101605.24984-6-p.paillet@st.com --- drivers/thermal/st/stm_thermal.c | 58 ++++++++++------------------------------ 1 file changed, 14 insertions(+), 44 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index 29e7ee89adf6..aaf25ca2f118 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -59,7 +59,6 @@ /* Less significant bit position definitions */ #define TS1_T0_POS 16 -#define TS1_SMP_TIME_POS 16 #define TS1_HITTHD_POS 16 #define TS1_LITTHD_POS 0 #define HSREF_CLK_DIV_POS 24 @@ -83,15 +82,10 @@ #define ONE_MHZ 1000000 #define POLL_TIMEOUT 5000 #define STARTUP_TIME 40 -#define TS1_T0_VAL0 30 -#define TS1_T0_VAL1 130 +#define TS1_T0_VAL0 30000 /* 30 celsius */ +#define TS1_T0_VAL1 130000 /* 130 celsius */ #define NO_HW_TRIG 0 - -/* The Thermal Framework expects millidegrees */ -#define mcelsius(temp) ((temp) * 1000) - -/* The Sensor expects oC degrees */ -#define celsius(temp) ((temp) / 1000) +#define SAMPLING_TIME 15 struct stm_thermal_sensor { struct device *dev; @@ -280,27 +274,17 @@ static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor, int temp, u32 *th) { int freqM; - u32 sampling_time; - - /* Retrieve the number of periods to sample */ - sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) & - TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS; /* Figure out the CLK_PTAT frequency for a given temperature */ - freqM = ((temp - sensor->t0) * sensor->ramp_coeff) - + sensor->fmt0; - - dev_dbg(sensor->dev, "%s: freqM for threshold = %d Hz", - __func__, freqM); + freqM = ((temp - sensor->t0) * sensor->ramp_coeff) / 1000 + + sensor->fmt0; /* Figure out the threshold sample number */ - *th = clk_get_rate(sensor->clk); + *th = clk_get_rate(sensor->clk) * SAMPLING_TIME / freqM; if (!*th) return -EINVAL; - *th = *th / freqM; - - *th *= sampling_time; + dev_dbg(sensor->dev, "freqM=%d Hz, threshold=0x%x", freqM, *th); return 0; } @@ -368,40 +352,26 @@ static int stm_thermal_set_trips(void *data, int low, int high) static int stm_thermal_get_temp(void *data, int *temp) { struct stm_thermal_sensor *sensor = data; - u32 sampling_time; + u32 periods; int freqM, ret; if (sensor->mode != THERMAL_DEVICE_ENABLED) return -EAGAIN; - /* Retrieve the number of samples */ - ret = readl_poll_timeout(sensor->base + DTS_DR_OFFSET, freqM, - (freqM & TS1_MFREQ_MASK), STARTUP_TIME, - POLL_TIMEOUT); - + /* Retrieve the number of periods sampled */ + ret = readl_relaxed_poll_timeout(sensor->base + DTS_DR_OFFSET, periods, + (periods & TS1_MFREQ_MASK), + STARTUP_TIME, POLL_TIMEOUT); if (ret) return ret; - if (!freqM) - return -ENODATA; - - /* Retrieve the number of periods sampled */ - sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) & - TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS; - - /* Figure out the number of samples per period */ - freqM /= sampling_time; - /* Figure out the CLK_PTAT frequency */ - freqM = clk_get_rate(sensor->clk) / freqM; + freqM = (clk_get_rate(sensor->clk) * SAMPLING_TIME) / periods; if (!freqM) return -EINVAL; - dev_dbg(sensor->dev, "%s: freqM=%d\n", __func__, freqM); - /* Figure out the temperature in mili celsius */ - *temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) / - sensor->ramp_coeff)); + *temp = (freqM - sensor->fmt0) * 1000 / sensor->ramp_coeff + sensor->t0; return 0; } -- cgit v1.2.3-59-g8ed1b From 2f23e319b133c7392493e77f515a237c8e1e6dd6 Mon Sep 17 00:00:00 2001 From: Pascal Paillet Date: Fri, 10 Jan 2020 11:16:05 +0100 Subject: thermal: stm32: Fix low threshold interrupt flood With the STM32 thermal peripheral, it is not possible to dump the temperature that has caused the interrupt. When the temperature reaches the low threshold, we generally read a temperature that is a little bit higher than the low threshold. This maybe due to sampling precision, and also because the CPU becomes hotter when it quits WFI mode. In that case, the framework does not change the trip points. This leads to a lot of low threshold interrupts. The fix is to set the low threshold value 0.5 degrees Celsius below the actual request. The problem is not so frequent with the high threshold and it would no be a good idea to set the threshold value higher than the request. Signed-off-by: Pascal Paillet Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200110101605.24984-7-p.paillet@st.com --- drivers/thermal/st/stm_thermal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index aaf25ca2f118..1cc5e6c5709e 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -321,7 +321,8 @@ static int stm_thermal_set_trips(void *data, int low, int high) if (low > -INT_MAX) { sensor->low_temp_enabled = 1; - ret = stm_thermal_calculate_threshold(sensor, low, &th); + /* add 0.5 of hysteresis due to measurement error */ + ret = stm_thermal_calculate_threshold(sensor, low - 500, &th); if (ret) return ret; -- cgit v1.2.3-59-g8ed1b