dt-bindings: thermal: Add support for Airoha EN7581 thermal sensor
Subsystems (2)
PATCH v5 1/2 dt-bindings: thermal: Add support for Airoha EN7581 thermal sensor
Add support for Airoha EN7581 thermal sensor and monitor. This is a simple sensor for the CPU or SoC Package that provide thermal sensor and trip point for hot low and critical condition to fire interrupt and react on the abnormal state.
---
Changes v2:
- Add Reviewed-by tag
.../thermal/airoha,en7581-thermal.yaml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/airoha,en7581-thermal.yaml
Documentation/devicetree/bindings/thermal/airoha,en7581-thermal.yaml new file +48 -0
@@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/airoha,en7581-thermal.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha EN7581 Thermal Sensor and Monitor + +maintainers: + - Christian Marangi <[email protected]> + +properties: + compatible: + const: airoha,en7581-thermal + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + airoha,chip-scu: + description: phandle to the chip SCU syscon + $ref: /schemas/types.yaml#/definitions/phandle + + '#thermal-sensor-cells': + const: 0 + +required: + - compatible + - reg + - interrupts + - airoha,chip-scu + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + thermal-sensor@1efbd800 { + compatible = "airoha,en7581-thermal"; + reg = <0x1efbd000 0xd5c>; + interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>; + airoha,chip-scu = <&chip_scu>; + + #thermal-sensor-cells = <0>; + };
Signature · 1 line
2.47.1
PATCH v5 2/2 thermal: Add support for Airoha EN7581 thermal sensor
Add support for Airoha EN7581 thermal sensor. This provide support for reading the CPU or SoC Package sensor and to setup trip points for hot and critical condition. An interrupt is fired to react on this and doesn't require passive poll to read the temperature.
The thermal regs provide a way to read the ADC value from an external register placed in the Chip SCU regs. Monitor will read this value and fire an interrupt if the trip condition configured is reached.
The Thermal Trip and Interrupt logic is conceptually similar to Mediatek LVTS Thermal but differ in register mapping and actual function/bug workaround. The implementation only share some register names but from functionality observation it's very different and used only for the basic function of periodically poll the temp and trip the interrupt.
---
Changes v5:
- Add additional info on difference from Mediatek LVTS driver Changes v4:
- Handle offset and slope in priv driver
Changes v3:
- Handle thermal_zone_device moved in different header
- Enable interrupt after thermal register
- Use new way to provide slope and offset
Changes v2:
- Add missing Makefile and Kconfig entry (somehow not included in v1)
- Sort include header
- Add missing bitfield.h
drivers/thermal/Kconfig | 9 + drivers/thermal/Makefile | 1 + drivers/thermal/airoha_thermal.c | 480 +++++++++++++++++++++++++++++++ 3 files changed, 490 insertions(+) create mode 100644 drivers/thermal/airoha_thermal.c
drivers/thermal/Kconfig +9 -0
@@ -327,6 +327,15 @@ config QORIQ_THERMAL cpufreq is used as the cooling device to throttle CPUs when the passive trip is crossed. +config AIROHA_THERMAL + tristate "Airoha thermal sensor driver" + depends on ARCH_AIROHA || COMPILE_TEST + depends on MFD_SYSCON + depends on OF + help + Enable this to plug the Airoha thermal sensor driver into the Linux + thermal framework. + config SPEAR_THERMAL tristate "SPEAr thermal sensor driver" depends on PLAT_SPEAR || COMPILE_TEST
drivers/thermal/Makefile +1 -0
@@ -38,6 +38,7 @@ obj-$(CONFIG_K3_THERMAL) += k3_bandgap.o k3_j72xx_bandgap.o # platform thermal drivers obj-y += broadcom/ obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o +obj-$(CONFIG_AIROHA_THERMAL) += airoha_thermal.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
drivers/thermal/airoha_thermal.c new file +480 -0
480 lines not on this page · raw message
Signature · 1 line
2.47.1
Re: [PATCH v5 2/2] thermal: Add support for Airoha EN7581 thermal sensor
Hi Angelo,
AFAIR, the LVTS driver had issues with the interrupts.
This driver proposed by Ansuel Smith looks very similar to the LVTS and there are some comments regarding errors with the documentation below which may appy to the LVTS driver too.
Would you mind to check ?
See below.
Quoted 17 lines · Christian Marangi
Add support for Airoha EN7581 thermal sensor. This provide support for reading the CPU or SoC Package sensor and to setup trip points for hot and critical condition. An interrupt is fired to react on this and doesn't require passive poll to read the temperature.
The thermal regs provide a way to read the ADC value from an external register placed in the Chip SCU regs. Monitor will read this value and fire an interrupt if the trip condition configured is reached.
The Thermal Trip and Interrupt logic is conceptually similar to Mediatek LVTS Thermal but differ in register mapping and actual function/bug workaround. The implementation only share some register names but from functionality observation it's very different and used only for the basic function of periodically poll the temp and trip the interrupt.
Signed-off-by Christian Marangi <[email protected]>---
[ ... ]
Quoted 18 lines
drivers/thermal/airoha_thermal.c new file +18
-0+#define EN7581_HINTEN1 BIT(6) +#define EN7581_CINTEN1 BIT(5) +#define EN7581_NOHOTINTEN0 BIT(4) +/* Similar to COLD and HOT also these seems to be swapped in documentation */ +#define EN7581_LOFSINTEN0 BIT(3)/* In documentation: BIT(2) */ +#define EN7581_HOFSINTEN0 BIT(2)/* In documentation: BIT(3) */ +/* It seems documentation have these swapped as the HW +* - Fire BIT(1) when lower than EN7581_COLD_THRE +* - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or +* EN7581_HOT_THRE +*/ +#define EN7581_CINTEN0 BIT(1)/* In documentation: BIT(0) */ +#define EN7581_HINTEN0 BIT(0)/* In documentation: BIT(1) */ +#define EN7581_TEMPMONINTSTS 0x810 +#define EN7581_STAGE3_INT_STAT BIT(31) +#define EN7581_STAGE2_INT_STAT BIT(30) +#define EN7581_STAGE1_INT_STAT BIT(29) +#define EN7581_FILTER_INT_STAT_3 BIT(28)
[ ... ]
Quoted 33 lines
drivers/thermal/airoha_thermal.c new file +33
-0+#define EN7581_NOHOTINTSTS0 BIT(4) +/* Similar to COLD and HOT also these seems to be swapped in documentation */ +#define EN7581_LOFSINTSTS0 BIT(3)/* In documentation: BIT(2) */ +#define EN7581_HOFSINTSTS0 BIT(2)/* In documentation: BIT(3) */ +/* It seems documentation have these swapped as the HW +* - Fire BIT(1) when lower than EN7581_COLD_THRE +* - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or +* EN7581_HOT_THRE +* +* To clear things, we swap the define but we keep them documented here. +*/ +#define EN7581_CINTSTS0 BIT(1)/* In documentation: BIT(0) */ +#define EN7581_HINTSTS0 BIT(0)/* In documentation: BIT(1)*/ +/* Monitor will take the bigger threshold between HOT2NORMAL and HOT +* and will fire both HOT2NORMAL and HOT interrupt when higher than the 2 +* +* It has also been observed that not setting HOT2NORMAL makes the monitor +* treat COLD threshold as HOT2NORMAL. +*/ +#define EN7581_TEMPH2NTHRE 0x824 +/* It seems HOT2NORMAL is actually NORMAL2HOT */ +#define EN7581_HOT2NORMAL_THRE GENMASK(11, 0) +#define EN7581_TEMPHTHRE 0x828 +#define EN7581_HOT_THRE GENMASK(11, 0) +/* Monitor will use this as HOT2NORMAL (fire interrupt when lower than...)*/ +#define EN7581_TEMPCTHRE 0x82c +#define EN7581_COLD_THRE GENMASK(11, 0) +/* Also LOW and HIGH offset register are swapped */ +#define EN7581_TEMPOFFSETL 0x830/* In documentation: 0x834 */ +#define EN7581_LOW_OFFSET GENMASK(11, 0) +#define EN7581_TEMPOFFSETH 0x834/* In documentation: 0x830 */ +#define EN7581_HIGH_OFFSET GENMASK(11, 0) +#define EN7581_TEMPMSRCTL0 0x838
[ ... ]
Signature · 5 lines
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog
Re: [PATCH v5 2/2] thermal: Add support for Airoha EN7581 thermal sensor
In reply to Christian MarangiQuoted 19 lines · Christian Marangi
Add support for Airoha EN7581 thermal sensor. This provide support for reading the CPU or SoC Package sensor and to setup trip points for hot and critical condition. An interrupt is fired to react on this and doesn't require passive poll to read the temperature.
The thermal regs provide a way to read the ADC value from an external register placed in the Chip SCU regs. Monitor will read this value and fire an interrupt if the trip condition configured is reached.
The Thermal Trip and Interrupt logic is conceptually similar to Mediatek LVTS Thermal but differ in register mapping and actual function/bug workaround. The implementation only share some register names but from functionality observation it's very different and used only for the basic function of periodically poll the temp and trip the interrupt.
Signed-off-by Christian Marangi <[email protected]>---
Changes v5:
- Add additional info on difference from Mediatek LVTS driver
[ ... ]
Quoted 33 lines
drivers/thermal/airoha_thermal.c new file +33
-0+static void airoha_init_thermal_ADC_mode (struct airoha_thermal_priv *priv) +{ +u32 adc_mux, pllrg; + +/* Save PLLRG current value */ +regmap_read (priv->chip_scu,EN7581_PLLRG_PROTECT , &pllrg); + +/* Give access to thermal regs */ +regmap_write (priv->chip_scu,EN7581_PLLRG_PROTECT ,EN7581_SCU_THERMAL_PROTECT_KEY ); + adc_mux =FIELD_PREP (EN7581_MUX_TADC ,EN7581_SCU_THERMAL_MUX_DIODE1 ); +regmap_write (priv->chip_scu,EN7581_PWD_TADC , adc_mux); + +/* Restore PLLRG value on exit */ +regmap_write (priv->chip_scu,EN7581_PLLRG_PROTECT , pllrg); +} + +static int airoha_thermal_get_temp (struct thermal_zone_device *tz,int *temp) +{ +struct airoha_thermal_priv *priv =thermal_zone_device_priv (tz); +int min, max, avg_temp, temp_adc; +int i; + +/* Get the starting temp */ + temp_adc =airoha_get_thermal_ADC (priv); + min = temp_adc; + max = temp_adc; + avg_temp = temp_adc; + +/* Make 5 more measurement and average the temp ADC difference */ +for (i =0 ; i <5 ; i++) { + temp_adc =airoha_get_thermal_ADC (priv); + avg_temp += temp_adc; +if (temp_adc > max)
drivers/thermal/airoha_thermal.c line 255
Quoted 10 lines
drivers/thermal/airoha_thermal.c new file +10
-0+ max = temp_adc; +if (temp_adc < min) + min = temp_adc; + } + avg_temp = avg_temp - max - min; + avg_temp /=4 ; + + *temp =RAW_TO_TEMP (priv, avg_temp); +return 0 ; +}
Does this chip support the averaging with the filtered mode which prevent to do this expensive calls when getting the temperature ?
drivers/thermal/airoha_thermal.c line 265
Quoted 9 lines
drivers/thermal/airoha_thermal.c new file +9
-0+static int airoha_thermal_set_trips (struct thermal_zone_device *tz,int low, +int high) +{ +struct airoha_thermal_priv *priv =thermal_zone_device_priv (tz); + +if (high !=INT_MAX ) { +/* Validate high and clamp them a sane value */ +if (high >RAW_TO_TEMP (priv,FIELD_MAX (EN7581_DOUT_TADC_MASK ))) + high =110000 ;
There are helpers in minmax.h
drivers/thermal/airoha_thermal.c line 275
Quoted 9 lines
drivers/thermal/airoha_thermal.c new file +9
-0+/* We offset the high temp of 1°C to trigger correct event */ +writel (TEMP_TO_RAW (priv, high) >>4 , + priv->base +EN7581_TEMPOFFSETH ); + } + +if (low != -INT_MAX ) { +/* Validate low and clamp them to a sane value */ +if (low <RAW_TO_TEMP (priv,0 )) + low =-33000 ;
Same.
drivers/thermal/airoha_thermal.c line 283
Quoted 7 lines
drivers/thermal/airoha_thermal.c new file +7
-0+/* We offset the low temp of 1°C to trigger correct event */ +writel (TEMP_TO_RAW (priv, low) >>4 , + priv->base +EN7581_TEMPOFFSETL ); + } + +/* Enable sensor 0 monitor */ +writel (EN7581_SENSE0_EN , priv->base +EN7581_TEMPMONCTL0 );
The sensor is enabled in this routine but disabled anywhere else. Should it be enabled in the init routine or in the set_mode callbacks ?
Quoted 14 lines
drivers/thermal/airoha_thermal.c new file +14
-0+return 0 ; +} + +static const struct thermal_zone_device_ops thdev_ops = { + .get_temp = airoha_thermal_get_temp, + .set_trips = airoha_thermal_set_trips, +}; + +static irqreturn_t airoha_thermal_irq (int irq,void *data) +{ +struct airoha_thermal_priv *priv = data; +enum thermal_notify_event event; +u32 status; +
drivers/thermal/airoha_thermal.c line 308
Quoted 10 lines
drivers/thermal/airoha_thermal.c new file +10
-0+ status =readl (priv->base +EN7581_TEMPMONINTSTS ); +switch (status & (EN7581_HOFSINTSTS0 |EN7581_LOFSINTSTS0 )) { +case EN7581_HOFSINTSTS0 : + event =THERMAL_TRIP_VIOLATED ; +break ; +case EN7581_LOFSINTSTS0 : + event =THERMAL_EVENT_UNSPECIFIED ; +break ; +default : + goto exit;
Is the default case possible ?
drivers/thermal/airoha_thermal.c line 315
Quoted 6 lines
drivers/thermal/airoha_thermal.c new file +6
-0+ } +thermal_zone_device_update (priv->tz, event); + +exit: +/* reset interrupt */ +writel (status, priv->base +EN7581_TEMPMONINTSTS );
You may want to the ack the interrupt before calling thermal_zone_device_update() as the underlying code can interact with the driver like calling set_trips. I'm not sure if it has on impact or not but IMO it is a good practice.
Quoted 3 lines
drivers/thermal/airoha_thermal.c new file +3
-0+return IRQ_HANDLED ; +} +
[ ... ]
Quoted 44 lines
drivers/thermal/airoha_thermal.c new file +44
-0+static void airoha_thermal_setup_monitor (struct airoha_thermal_priv *priv) +{ +/* Set measure mode */ +writel (FIELD_PREP (EN7581_MSRCTL0 ,EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4 ), + priv->base +EN7581_TEMPMSRCTL0 ); + +/* +* Configure ADC valid reading addr +* The AHB temp monitor system doesn't have direct access to the +* thermal sensor. It does instead work by providing all kind of +* address to configure how to access and setup an ADC for the +* sensor. EN7581 supports only one sensor hence the +* implementation is greatly simplified but the AHB supports +* up to 4 different sensor from the same ADC that can be +* switched by tuning the ADC mux or wiriting address. +* +* We set valid instead of volt as we don't enable valid/volt +* split reading and AHB read valid addr in such case. +*/ +writel (priv->scu_adc_res.start +EN7581_DOUT_TADC , + priv->base +EN7581_TEMPADCVALIDADDR ); + +/* +* Configure valid bit on a fake value of bit 16. The ADC outputs +* max of 2 bytes for voltage. +*/ +writel (FIELD_PREP (EN7581_ADV_RD_VALID_POS ,16 ), + priv->base +EN7581_TEMPADCVALIDMASK ); + +/* +* AHB supports max 12 bytes for ADC voltage. Shift the read +* value 4 bit to the right. Precision lost by this is minimal +* in the order of half a °C and is acceptable in the context +* of triggering interrupt in critical condition. +*/ +writel (FIELD_PREP (EN7581_ADC_VOLTAGE_SHIFT ,4 ), + priv->base +EN7581_TEMPADCVOLTAGESHIFT ); + +/* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */ +writel (FIELD_PREP (EN7581_PERIOD_UNIT ,3 ), + priv->base +EN7581_TEMPMONCTL1 ); + +/* +* filt interval is 1 * 52.715us = 52.715us,
drivers/thermal/airoha_thermal.c line 403
Quoted 10 lines
drivers/thermal/airoha_thermal.c new file +10
-0+* sen interval is 379 * 52.715us = 19.97ms +*/ +writel (FIELD_PREP (EN7581_FILT_INTERVAL ,1 ) | +FIELD_PREP (EN7581_FILT_INTERVAL ,379 ), + priv->base +EN7581_TEMPMONCTL2 ); + +/* AHB poll is set to 146 * 68.64 = 10.02us */ +writel (FIELD_PREP (EN7581_ADC_POLL_INTVL ,146 ), + priv->base +EN7581_TEMPAHBPOLL ); +}
Thanks for documenting the different steps
Quoted 23 lines
drivers/thermal/airoha_thermal.c new file +23
-0+static int airoha_thermal_probe (struct platform_device *pdev) +{ +struct airoha_thermal_priv *priv; +struct device_node *chip_scu_np; +struct device *dev = &pdev->dev; +int irq, ret; + + priv =devm_kzalloc (dev,sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; + + priv->base =devm_platform_ioremap_resource (pdev,0 ); +if (IS_ERR (priv->base)) +return PTR_ERR (priv->base); + + chip_scu_np =of_parse_phandle (dev->of_node,"airoha,chip-scu" ,0 ); +if (!chip_scu_np) +return -EINVAL ; + + priv->chip_scu =syscon_node_to_regmap (chip_scu_np); +if (IS_ERR (priv->chip_scu)) +return PTR_ERR (priv->chip_scu); +
drivers/thermal/airoha_thermal.c line 437
Quoted 10 lines
drivers/thermal/airoha_thermal.c new file +10
-0+of_address_to_resource (chip_scu_np,0 , &priv->scu_adc_res); +of_node_put (chip_scu_np); + + irq =platform_get_irq (pdev,0 ); +if (irq <0 ) +return irq; + + ret =devm_request_threaded_irq (&pdev->dev, irq,NULL , + airoha_thermal_irq,IRQF_ONESHOT , + pdev->name, (void *)priv);
(void *) cast is not needed
Quoted 43 lines
drivers/thermal/airoha_thermal.c new file +43
-0+if (ret) { +dev_err (dev,"Can't get interrupt working.\n" ); +return ret; + } + +airoha_thermal_setup_monitor (priv); +airoha_thermal_setup_adc_val (dev, priv); + +/* register of thermal sensor and get info from DT */ + priv->tz =devm_thermal_of_zone_register (dev,0 , priv, &thdev_ops); +if (IS_ERR (priv->tz)) { +dev_err (dev,"register thermal zone sensor failed\n" ); +return PTR_ERR (priv->tz); + } + +platform_set_drvdata (pdev, priv); + +/* Enable LOW and HIGH interrupt */ +writel (EN7581_HOFSINTEN0 |EN7581_LOFSINTEN0 , + priv->base +EN7581_TEMPMONINT ); + +return 0 ; +} + +static const struct of_device_id airoha_thermal_match[] = { + { .compatible ="airoha,en7581-thermal" }, + {}, +}; +MODULE_DEVICE_TABLE (of, airoha_thermal_match); + +static struct platform_driver airoha_thermal_driver = { + .driver = { + .name ="airoha-thermal" , + .of_match_table = airoha_thermal_match, + }, + .probe = airoha_thermal_probe, +}; + +module_platform_driver (airoha_thermal_driver); + +MODULE_AUTHOR ("Christian Marangi <[email protected]>" ); +MODULE_DESCRIPTION ("Airoha thermal driver" ); +MODULE_LICENSE ("GPL" );
Signature · 5 lines
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog
Re: [PATCH v5 2/2] thermal: Add support for Airoha EN7581 thermal sensor
Quoted 61 lines · Daniel Lezcano
Quoted 19 lines · Christian Marangi
Add support for Airoha EN7581 thermal sensor. This provide support for reading the CPU or SoC Package sensor and to setup trip points for hot and critical condition. An interrupt is fired to react on this and doesn't require passive poll to read the temperature.
The thermal regs provide a way to read the ADC value from an external register placed in the Chip SCU regs. Monitor will read this value and fire an interrupt if the trip condition configured is reached.
The Thermal Trip and Interrupt logic is conceptually similar to Mediatek LVTS Thermal but differ in register mapping and actual function/bug workaround. The implementation only share some register names but from functionality observation it's very different and used only for the basic function of periodically poll the temp and trip the interrupt.
Signed-off-by Christian Marangi <[email protected]>---
Changes v5:
- Add additional info on difference from Mediatek LVTS driver[ ... ]
Quoted 38 lines
drivers/thermal/airoha_thermal.c new file +38
-0+static void airoha_init_thermal_ADC_mode (struct airoha_thermal_priv *priv) +{ +u32 adc_mux, pllrg; + +/* Save PLLRG current value */ +regmap_read (priv->chip_scu,EN7581_PLLRG_PROTECT , &pllrg); + +/* Give access to thermal regs */ +regmap_write (priv->chip_scu,EN7581_PLLRG_PROTECT ,EN7581_SCU_THERMAL_PROTECT_KEY ); + adc_mux =FIELD_PREP (EN7581_MUX_TADC ,EN7581_SCU_THERMAL_MUX_DIODE1 ); +regmap_write (priv->chip_scu,EN7581_PWD_TADC , adc_mux); + +/* Restore PLLRG value on exit */ +regmap_write (priv->chip_scu,EN7581_PLLRG_PROTECT , pllrg); +} + +static int airoha_thermal_get_temp (struct thermal_zone_device *tz,int *temp) +{ +struct airoha_thermal_priv *priv =thermal_zone_device_priv (tz); +int min, max, avg_temp, temp_adc; +int i; + +/* Get the starting temp */ + temp_adc =airoha_get_thermal_ADC (priv); + min = temp_adc; + max = temp_adc; + avg_temp = temp_adc; + +/* Make 5 more measurement and average the temp ADC difference */ +for (i =0 ; i <5 ; i++) { + temp_adc =airoha_get_thermal_ADC (priv); + avg_temp += temp_adc; +if (temp_adc > max) + max = temp_adc; +if (temp_adc < min) + min = temp_adc; + } + avg_temp = avg_temp - max - min;
Quoted 9 lines
drivers/thermal/airoha_thermal.c line 255
Quoted 5 lines
drivers/thermal/airoha_thermal.c new file +5
-0+ avg_temp /=4 ; + + *temp =RAW_TO_TEMP (priv, avg_temp); +return 0 ; +}Does this chip support the averaging with the filtered mode which prevent to do this expensive calls when getting the temperature ?
If you notice in this function we read directly from the ADC sensor to have a more precise and realtime temperature.
Yes the thermal module support averaging (and this is what is set for the critical temperature monitor) but since we base everything on interrupt the expensive call is called only when the user poll the temperature with sysfs.
Do you prefer to use the thermal module entirely?
Quoted 12 lines
drivers/thermal/airoha_thermal.c line 265
Quoted 9 lines
drivers/thermal/airoha_thermal.c new file +9
-0+static int airoha_thermal_set_trips (struct thermal_zone_device *tz,int low, +int high) +{ +struct airoha_thermal_priv *priv =thermal_zone_device_priv (tz); + +if (high !=INT_MAX ) { +/* Validate high and clamp them a sane value */ +if (high >RAW_TO_TEMP (priv,FIELD_MAX (EN7581_DOUT_TADC_MASK ))) + high =110000 ;There are helpers in
minmax.h
Ok will change to use the helper.
Quoted 12 lines
drivers/thermal/airoha_thermal.c line 275
Quoted 9 lines
drivers/thermal/airoha_thermal.c new file +9
-0+/* We offset the high temp of 1°C to trigger correct event */ +writel (TEMP_TO_RAW (priv, high) >>4 , + priv->base +EN7581_TEMPOFFSETH ); + } + +if (low != -INT_MAX ) { +/* Validate low and clamp them to a sane value */ +if (low <RAW_TO_TEMP (priv,0 )) + low =-33000 ;Same.
Quoted 11 lines
drivers/thermal/airoha_thermal.c line 283
Quoted 7 lines
drivers/thermal/airoha_thermal.c new file +7
-0+/* We offset the low temp of 1°C to trigger correct event */ +writel (TEMP_TO_RAW (priv, low) >>4 , + priv->base +EN7581_TEMPOFFSETL ); + } + +/* Enable sensor 0 monitor */ +writel (EN7581_SENSE0_EN , priv->base +EN7581_TEMPMONCTL0 );The sensor is enabled in this routine but disabled anywhere else. Should it be enabled in the init routine or in the
set_modecallbacks ?
It's expected, we enable sensor only when trip are actually set. I observed that enabling before makes the thing fire interrupt with wrong trip point or makes the module stuck in some strange way (due to interrupt trip interval conflicts)
Quoted 18 lines
Quoted 18 lines
drivers/thermal/airoha_thermal.c new file +18
-0+return 0 ; +} + +static const struct thermal_zone_device_ops thdev_ops = { + .get_temp = airoha_thermal_get_temp, + .set_trips = airoha_thermal_set_trips, +}; + +static irqreturn_t airoha_thermal_irq (int irq,void *data) +{ +struct airoha_thermal_priv *priv = data; +enum thermal_notify_event event; +u32 status; + + status =readl (priv->base +EN7581_TEMPMONINTSTS ); +switch (status & (EN7581_HOFSINTSTS0 |EN7581_LOFSINTSTS0 )) { +case EN7581_HOFSINTSTS0 : + event =THERMAL_TRIP_VIOLATED ;
Quoted 9 lines
drivers/thermal/airoha_thermal.c line 308
Quoted 6 lines
drivers/thermal/airoha_thermal.c new file +6
-0+break ; +case EN7581_LOFSINTSTS0 : + event =THERMAL_EVENT_UNSPECIFIED ; +break ; +default : + goto exit;Is the default case possible ?
No but it's really to make compile happy.
Quoted 12 lines
drivers/thermal/airoha_thermal.c line 315
Quoted 6 lines
drivers/thermal/airoha_thermal.c new file +6
-0+ } +thermal_zone_device_update (priv->tz, event); + +exit: +/* reset interrupt */ +writel (status, priv->base +EN7581_TEMPMONINTSTS );You may want to the ack the interrupt before calling
thermal_zone_device_update()as the underlying code can interact with the driver like callingset_trips. I'm not sure if it has on impact or not but IMO it is a good practice.
Ok will move the interrupt reset before thermal_zone_device_update.
Quoted 150 lines
Quoted 3 lines
drivers/thermal/airoha_thermal.c new file +3
-0+return IRQ_HANDLED ; +} +[ ... ]
Quoted 44 lines
drivers/thermal/airoha_thermal.c new file +44
-0+static void airoha_thermal_setup_monitor (struct airoha_thermal_priv *priv) +{ +/* Set measure mode */ +writel (FIELD_PREP (EN7581_MSRCTL0 ,EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4 ), + priv->base +EN7581_TEMPMSRCTL0 ); + +/* +* Configure ADC valid reading addr +* The AHB temp monitor system doesn't have direct access to the +* thermal sensor. It does instead work by providing all kind of +* address to configure how to access and setup an ADC for the +* sensor. EN7581 supports only one sensor hence the +* implementation is greatly simplified but the AHB supports +* up to 4 different sensor from the same ADC that can be +* switched by tuning the ADC mux or wiriting address. +* +* We set valid instead of volt as we don't enable valid/volt +* split reading and AHB read valid addr in such case. +*/ +writel (priv->scu_adc_res.start +EN7581_DOUT_TADC , + priv->base +EN7581_TEMPADCVALIDADDR ); + +/* +* Configure valid bit on a fake value of bit 16. The ADC outputs +* max of 2 bytes for voltage. +*/ +writel (FIELD_PREP (EN7581_ADV_RD_VALID_POS ,16 ), + priv->base +EN7581_TEMPADCVALIDMASK ); + +/* +* AHB supports max 12 bytes for ADC voltage. Shift the read +* value 4 bit to the right. Precision lost by this is minimal +* in the order of half a °C and is acceptable in the context +* of triggering interrupt in critical condition. +*/ +writel (FIELD_PREP (EN7581_ADC_VOLTAGE_SHIFT ,4 ), + priv->base +EN7581_TEMPADCVOLTAGESHIFT ); + +/* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */ +writel (FIELD_PREP (EN7581_PERIOD_UNIT ,3 ), + priv->base +EN7581_TEMPMONCTL1 ); + +/* +* filt interval is 1 * 52.715us = 52.715us, drivers/thermal/airoha_thermal.c line 403
Quoted 10 lines
drivers/thermal/airoha_thermal.c new file +10
-0+* sen interval is 379 * 52.715us = 19.97ms +*/ +writel (FIELD_PREP (EN7581_FILT_INTERVAL ,1 ) | +FIELD_PREP (EN7581_FILT_INTERVAL ,379 ), + priv->base +EN7581_TEMPMONCTL2 ); + +/* AHB poll is set to 146 * 68.64 = 10.02us */ +writel (FIELD_PREP (EN7581_ADC_POLL_INTVL ,146 ), + priv->base +EN7581_TEMPAHBPOLL ); +}Thanks for documenting the different steps
Quoted 23 lines
drivers/thermal/airoha_thermal.c new file +23
-0+static int airoha_thermal_probe (struct platform_device *pdev) +{ +struct airoha_thermal_priv *priv; +struct device_node *chip_scu_np; +struct device *dev = &pdev->dev; +int irq, ret; + + priv =devm_kzalloc (dev,sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; + + priv->base =devm_platform_ioremap_resource (pdev,0 ); +if (IS_ERR (priv->base)) +return PTR_ERR (priv->base); + + chip_scu_np =of_parse_phandle (dev->of_node,"airoha,chip-scu" ,0 ); +if (!chip_scu_np) +return -EINVAL ; + + priv->chip_scu =syscon_node_to_regmap (chip_scu_np); +if (IS_ERR (priv->chip_scu)) +return PTR_ERR (priv->chip_scu); +drivers/thermal/airoha_thermal.c line 437
Quoted 10 lines
drivers/thermal/airoha_thermal.c new file +10
-0+of_address_to_resource (chip_scu_np,0 , &priv->scu_adc_res); +of_node_put (chip_scu_np); + + irq =platform_get_irq (pdev,0 ); +if (irq <0 ) +return irq; + + ret =devm_request_threaded_irq (&pdev->dev, irq,NULL , + airoha_thermal_irq,IRQF_ONESHOT , + pdev->name, (void *)priv);(void *) cast is not needed
Quoted 43 lines
drivers/thermal/airoha_thermal.c new file +43
-0+if (ret) { +dev_err (dev,"Can't get interrupt working.\n" ); +return ret; + } + +airoha_thermal_setup_monitor (priv); +airoha_thermal_setup_adc_val (dev, priv); + +/* register of thermal sensor and get info from DT */ + priv->tz =devm_thermal_of_zone_register (dev,0 , priv, &thdev_ops); +if (IS_ERR (priv->tz)) { +dev_err (dev,"register thermal zone sensor failed\n" ); +return PTR_ERR (priv->tz); + } + +platform_set_drvdata (pdev, priv); + +/* Enable LOW and HIGH interrupt */ +writel (EN7581_HOFSINTEN0 |EN7581_LOFSINTEN0 , + priv->base +EN7581_TEMPMONINT ); + +return 0 ; +} + +static const struct of_device_id airoha_thermal_match[] = { + { .compatible ="airoha,en7581-thermal" }, + {}, +}; +MODULE_DEVICE_TABLE (of, airoha_thermal_match); + +static struct platform_driver airoha_thermal_driver = { + .driver = { + .name ="airoha-thermal" , + .of_match_table = airoha_thermal_match, + }, + .probe = airoha_thermal_probe, +}; + +module_platform_driver (airoha_thermal_driver); + +MODULE_AUTHOR ("Christian Marangi <[email protected]>" ); +MODULE_DESCRIPTION ("Airoha thermal driver" ); +MODULE_LICENSE ("GPL" );Signature · 5 lines
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog
Signature · 1 line
Ansuel
Re: [PATCH v5 2/2] thermal: Add support for Airoha EN7581 thermal sensor
Quoted 1 line · Christian Marangi
On Fri, May 02, 2025 at 03:30:56PM +0200, Daniel Lezcano wrote:
[ ... ]
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
drivers/thermal/airoha_thermal.c new file +15
-0+static int airoha_thermal_get_temp (struct thermal_zone_device *tz,int *temp) +{ +struct airoha_thermal_priv *priv =thermal_zone_device_priv (tz); +int min, max, avg_temp, temp_adc; +int i; + +/* Get the starting temp */ + temp_adc =airoha_get_thermal_ADC (priv); + min = temp_adc; + max = temp_adc; + avg_temp = temp_adc; + +/* Make 5 more measurement and average the temp ADC difference */ +for (i =0 ; i <5 ; i++) { + temp_adc =airoha_get_thermal_ADC (priv);
Quoted 8 lines
Quoted 8 lines
drivers/thermal/airoha_thermal.c line 251
Quoted 8 lines
drivers/thermal/airoha_thermal.c new file +8
-0+ avg_temp += temp_adc; +if (temp_adc > max) + max = temp_adc; +if (temp_adc < min) + min = temp_adc; + } + avg_temp = avg_temp - max - min; + avg_temp /=4 ;
Why 4 ? There is 5 samples no ? one before the loop and 4 with the loop
Quoted 1 line
Quoted 1 line
drivers/thermal/airoha_thermal.c line 253
Quoted 1 line
drivers/thermal/airoha_thermal.c new file +1
-0+ *temp =RAW_TO_TEMP (priv, avg_temp);
Does RAW_TO_TEMP() on a average raw adc value report the same value as an average of the temperature after their conversion ?
Quoted 2 lines
Quoted 2 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ return 0; +}
The routine is a bit confusing because of the initial adc read.
avg_value = 0;min_value = INT_MAX;max_value = INT_MIN;
for (i = 0; i < AIROHA_MAX_SAMPLES; i++) {
value = airoha_get_thermal_ADC(priv);
min_value = min(value, min_value);
max_value = max(value, max_value);
avg_value += value;
}avg_value -= (min_value + max_value);avg_value /= AIROHA_MAX_SAMPLES;
Quoted 11 lines
Quoted 3 lines
Does this chip support the averaging with the filtered mode which prevent to do this expensive calls when getting the temperature ?
If you notice in this function we read directly from the ADC sensor to have a more precise and realtime temperature.
Yes the thermal module support averaging (and this is what is set for the critical temperature monitor) but since we base everything on interrupt the expensive call is called only when the user poll the temperature with sysfs.
When the temperature reaches the threshold, the interrupt is fired which in turn calls thermal_zone_device_update(). This lead to a polling from the kernel at a higher rate with the governor taking cooling decision.
Quoted 1 line
Do you prefer to use the thermal module entirely?
It is up to you. I would say if the sensor has a hardware assistance to compute the averaging while the CPU does something else it is better. But if the filtered mode has the same issues than the LVTS, then it is probably not worth it.
[ ... ]
Signature · 5 lines
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog