Message ID | 20250103163805.1775705-5-claudiu.beznea.uj@bp.renesas.com (mailing list archive) |
---|---|
State | Under Review |
Delegated to: | Geert Uytterhoeven |
Headers | show |
Series | thermal: renesas: Add support for RZ/G3S | expand |
Hi Claudiu, CC iio On Fri, Jan 3, 2025 at 5:38 PM Claudiu <claudiu.beznea@tuxon.dev> wrote: > From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > > The Renesas RZ/G3S SoC features a Thermal Sensor Unit (TSU) that reports > the junction temperature. The temperature is reported through a dedicated > ADC channel. Add a driver for the Renesas RZ/G3S TSU. > > Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> Thanks for your patch! > --- /dev/null > +++ b/drivers/thermal/renesas/rzg3s_thermal.c > +static int rzg3s_thermal_probe(struct platform_device *pdev) > +{ > + struct rzg3s_thermal_priv *priv; > + struct device *dev = &pdev->dev; > + int 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); > + > + priv->channel = devm_iio_channel_get(dev, "tsu"); Given there's only a single IIO channel, you could pass NULL instead of the name, and drop "io-channel-names" from the DT bindings. I don't know what's the IIO policy w.r.t. unnamed channels, though. > + if (IS_ERR(priv->channel)) > + return dev_err_probe(dev, PTR_ERR(priv->channel), "Failed to get IIO channel!\n"); > + > + priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); > + if (IS_ERR(priv->rstc)) > + return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset!\n"); > + > + priv->dev = dev; > + priv->mode = THERMAL_DEVICE_DISABLED; > + platform_set_drvdata(pdev, priv); > + > + pm_runtime_set_autosuspend_delay(dev, 300); > + pm_runtime_use_autosuspend(dev); > + pm_runtime_enable(dev); > + > + ret = rzg3s_thermal_read_calib(priv); > + if (ret) { > + dev_err_probe(dev, ret, "Failed to read calibration data!\n"); > + goto rpm_disable; > + } > + > + priv->tz = thermal_of_zone_register(dev->of_node, 0, priv, &rzg3s_tz_of_ops); > + if (IS_ERR(priv->tz)) { > + dev_err_probe(dev, PTR_ERR(priv->tz), "Failed to register thermal zone!\n"); > + goto rpm_disable; > + } > + > + ret = thermal_add_hwmon_sysfs(priv->tz); > + if (ret) { > + dev_err_probe(dev, ret, "Failed to add hwmon sysfs!\n"); > + goto tz_unregister; > + } > + > + return 0; > + > +tz_unregister: > + thermal_of_zone_unregister(priv->tz); > +rpm_disable: > + pm_runtime_disable(dev); > + pm_runtime_dont_use_autosuspend(dev); > + return ret; > +} Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
On Wed, 22 Jan 2025 11:29:19 +0100 Geert Uytterhoeven <geert@linux-m68k.org> wrote: > Hi Claudiu, > > CC iio > > On Fri, Jan 3, 2025 at 5:38 PM Claudiu <claudiu.beznea@tuxon.dev> wrote: > > From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > > > > The Renesas RZ/G3S SoC features a Thermal Sensor Unit (TSU) that reports > > the junction temperature. The temperature is reported through a dedicated > > ADC channel. Add a driver for the Renesas RZ/G3S TSU. > > > > Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > > Thanks for your patch! > > > --- /dev/null > > +++ b/drivers/thermal/renesas/rzg3s_thermal.c > > > +static int rzg3s_thermal_probe(struct platform_device *pdev) > > +{ > > + struct rzg3s_thermal_priv *priv; > > + struct device *dev = &pdev->dev; > > + int 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); > > + > > + priv->channel = devm_iio_channel_get(dev, "tsu"); > > Given there's only a single IIO channel, you could pass NULL instead > of the name, and drop "io-channel-names" from the DT bindings. > I don't know what's the IIO policy w.r.t. unnamed channels, though. It's supported, so fine as long as no future additional names show up. Will just fallback to index 0 I think. Jonathan > > > + if (IS_ERR(priv->channel)) > > + return dev_err_probe(dev, PTR_ERR(priv->channel), "Failed to get IIO channel!\n"); > > + > > + priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); > > + if (IS_ERR(priv->rstc)) > > + return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset!\n"); > > + > > + priv->dev = dev; > > + priv->mode = THERMAL_DEVICE_DISABLED; > > + platform_set_drvdata(pdev, priv); > > + > > + pm_runtime_set_autosuspend_delay(dev, 300); > > + pm_runtime_use_autosuspend(dev); > > + pm_runtime_enable(dev); > > + > > + ret = rzg3s_thermal_read_calib(priv); > > + if (ret) { > > + dev_err_probe(dev, ret, "Failed to read calibration data!\n"); > > + goto rpm_disable; > > + } > > + > > + priv->tz = thermal_of_zone_register(dev->of_node, 0, priv, &rzg3s_tz_of_ops); > > + if (IS_ERR(priv->tz)) { > > + dev_err_probe(dev, PTR_ERR(priv->tz), "Failed to register thermal zone!\n"); > > + goto rpm_disable; > > + } > > + > > + ret = thermal_add_hwmon_sysfs(priv->tz); > > + if (ret) { > > + dev_err_probe(dev, ret, "Failed to add hwmon sysfs!\n"); > > + goto tz_unregister; > > + } > > + > > + return 0; > > + > > +tz_unregister: > > + thermal_of_zone_unregister(priv->tz); > > +rpm_disable: > > + pm_runtime_disable(dev); > > + pm_runtime_dont_use_autosuspend(dev); > > + return ret; > > +} > > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org > > In personal conversations with technical people, I call myself a hacker. But > when I'm talking to journalists I just say "programmer" or something like that. > -- Linus Torvalds >
On 25.01.2025 14:18, Jonathan Cameron wrote: > On Wed, 22 Jan 2025 11:29:19 +0100 > Geert Uytterhoeven <geert@linux-m68k.org> wrote: > >> Hi Claudiu, >> >> CC iio >> >> On Fri, Jan 3, 2025 at 5:38 PM Claudiu <claudiu.beznea@tuxon.dev> wrote: >>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> >>> >>> The Renesas RZ/G3S SoC features a Thermal Sensor Unit (TSU) that reports >>> the junction temperature. The temperature is reported through a dedicated >>> ADC channel. Add a driver for the Renesas RZ/G3S TSU. >>> >>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> >> >> Thanks for your patch! >> >>> --- /dev/null >>> +++ b/drivers/thermal/renesas/rzg3s_thermal.c >> >>> +static int rzg3s_thermal_probe(struct platform_device *pdev) >>> +{ >>> + struct rzg3s_thermal_priv *priv; >>> + struct device *dev = &pdev->dev; >>> + int 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); >>> + >>> + priv->channel = devm_iio_channel_get(dev, "tsu"); >> >> Given there's only a single IIO channel, you could pass NULL instead >> of the name, and drop "io-channel-names" from the DT bindings. >> I don't know what's the IIO policy w.r.t. unnamed channels, though. > > It's supported, so fine as long as no future additional names show up. > Will just fallback to index 0 I think. If everyone agrees, I would keep the name, too, to avoid complications in case this IP variant will be extended on future SoCs. Thank you, Claudiu > > Jonathan > >> >>> + if (IS_ERR(priv->channel)) >>> + return dev_err_probe(dev, PTR_ERR(priv->channel), "Failed to get IIO channel!\n"); >>> + >>> + priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); >>> + if (IS_ERR(priv->rstc)) >>> + return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset!\n"); >>> + >>> + priv->dev = dev; >>> + priv->mode = THERMAL_DEVICE_DISABLED; >>> + platform_set_drvdata(pdev, priv); >>> + >>> + pm_runtime_set_autosuspend_delay(dev, 300); >>> + pm_runtime_use_autosuspend(dev); >>> + pm_runtime_enable(dev); >>> + >>> + ret = rzg3s_thermal_read_calib(priv); >>> + if (ret) { >>> + dev_err_probe(dev, ret, "Failed to read calibration data!\n"); >>> + goto rpm_disable; >>> + } >>> + >>> + priv->tz = thermal_of_zone_register(dev->of_node, 0, priv, &rzg3s_tz_of_ops); >>> + if (IS_ERR(priv->tz)) { >>> + dev_err_probe(dev, PTR_ERR(priv->tz), "Failed to register thermal zone!\n"); >>> + goto rpm_disable; >>> + } >>> + >>> + ret = thermal_add_hwmon_sysfs(priv->tz); >>> + if (ret) { >>> + dev_err_probe(dev, ret, "Failed to add hwmon sysfs!\n"); >>> + goto tz_unregister; >>> + } >>> + >>> + return 0; >>> + >>> +tz_unregister: >>> + thermal_of_zone_unregister(priv->tz); >>> +rpm_disable: >>> + pm_runtime_disable(dev); >>> + pm_runtime_dont_use_autosuspend(dev); >>> + return ret; >>> +} >> >> Gr{oetje,eeting}s, >> >> Geert >> >> -- >> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org >> >> In personal conversations with technical people, I call myself a hacker. But >> when I'm talking to journalists I just say "programmer" or something like that. >> -- Linus Torvalds >> >
Hi Claudiu, On Mon, 27 Jan 2025 at 09:33, Claudiu Beznea <claudiu.beznea@tuxon.dev> wrote: > On 25.01.2025 14:18, Jonathan Cameron wrote: > > On Wed, 22 Jan 2025 11:29:19 +0100 > > Geert Uytterhoeven <geert@linux-m68k.org> wrote: > >> On Fri, Jan 3, 2025 at 5:38 PM Claudiu <claudiu.beznea@tuxon.dev> wrote: > >>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > >>> > >>> The Renesas RZ/G3S SoC features a Thermal Sensor Unit (TSU) that reports > >>> the junction temperature. The temperature is reported through a dedicated > >>> ADC channel. Add a driver for the Renesas RZ/G3S TSU. > >>> > >>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > >> > >> Thanks for your patch! > >> > >>> --- /dev/null > >>> +++ b/drivers/thermal/renesas/rzg3s_thermal.c > >> > >>> +static int rzg3s_thermal_probe(struct platform_device *pdev) > >>> +{ > >>> + struct rzg3s_thermal_priv *priv; > >>> + struct device *dev = &pdev->dev; > >>> + int 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); > >>> + > >>> + priv->channel = devm_iio_channel_get(dev, "tsu"); > >> > >> Given there's only a single IIO channel, you could pass NULL instead > >> of the name, and drop "io-channel-names" from the DT bindings. > >> I don't know what's the IIO policy w.r.t. unnamed channels, though. > > > > It's supported, so fine as long as no future additional names show up. > > Will just fallback to index 0 I think. > > If everyone agrees, I would keep the name, too, to avoid complications in > case this IP variant will be extended on future SoCs. Fine for me. Gr{oetje,eeting}s, Geert
Hi Claudiu, > -----Original Message----- > From: Geert Uytterhoeven <geert@linux-m68k.org> > Sent: 27 January 2025 08:55 > Subject: Re: [PATCH 4/6] thermal: renesas: rzg3s: Add thermal driver for the Renesas RZ/G3S SoC > > Hi Claudiu, > > On Mon, 27 Jan 2025 at 09:33, Claudiu Beznea <claudiu.beznea@tuxon.dev> wrote: > > On 25.01.2025 14:18, Jonathan Cameron wrote: > > > On Wed, 22 Jan 2025 11:29:19 +0100 > > > Geert Uytterhoeven <geert@linux-m68k.org> wrote: > > >> On Fri, Jan 3, 2025 at 5:38 PM Claudiu <claudiu.beznea@tuxon.dev> wrote: > > >>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > > >>> > > >>> The Renesas RZ/G3S SoC features a Thermal Sensor Unit (TSU) that > > >>> reports the junction temperature. The temperature is reported > > >>> through a dedicated ADC channel. Add a driver for the Renesas RZ/G3S TSU. > > >>> > > >>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > > >> > > >> Thanks for your patch! > > >> > > >>> --- /dev/null > > >>> +++ b/drivers/thermal/renesas/rzg3s_thermal.c > > >> > > >>> +static int rzg3s_thermal_probe(struct platform_device *pdev) { > > >>> + struct rzg3s_thermal_priv *priv; > > >>> + struct device *dev = &pdev->dev; > > >>> + int 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); > > >>> + > > >>> + priv->channel = devm_iio_channel_get(dev, "tsu"); > > >> > > >> Given there's only a single IIO channel, you could pass NULL > > >> instead of the name, and drop "io-channel-names" from the DT bindings. > > >> I don't know what's the IIO policy w.r.t. unnamed channels, though. > > > > > > It's supported, so fine as long as no future additional names show up. > > > Will just fallback to index 0 I think. > > > > If everyone agrees, I would keep the name, too, to avoid complications > > in case this IP variant will be extended on future SoCs. If you are planning to extend this driver to other SoCs then may be update KConfig with dependency on ARCH_RENESAS? see [1] [1] https://lore.kernel.org/linux-renesas-soc/20250118-trout-of-luxurious-inquire-aae9aa@krzk-bin/ Cheers, Biju
Hi, Biju, On 27.01.2025 11:11, Biju Das wrote: > Hi Claudiu, > >> -----Original Message----- >> From: Geert Uytterhoeven <geert@linux-m68k.org> >> Sent: 27 January 2025 08:55 >> Subject: Re: [PATCH 4/6] thermal: renesas: rzg3s: Add thermal driver for the Renesas RZ/G3S SoC >> >> Hi Claudiu, >> >> On Mon, 27 Jan 2025 at 09:33, Claudiu Beznea <claudiu.beznea@tuxon.dev> wrote: >>> On 25.01.2025 14:18, Jonathan Cameron wrote: >>>> On Wed, 22 Jan 2025 11:29:19 +0100 >>>> Geert Uytterhoeven <geert@linux-m68k.org> wrote: >>>>> On Fri, Jan 3, 2025 at 5:38 PM Claudiu <claudiu.beznea@tuxon.dev> wrote: >>>>>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> >>>>>> >>>>>> The Renesas RZ/G3S SoC features a Thermal Sensor Unit (TSU) that >>>>>> reports the junction temperature. The temperature is reported >>>>>> through a dedicated ADC channel. Add a driver for the Renesas RZ/G3S TSU. >>>>>> >>>>>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> >>>>> >>>>> Thanks for your patch! >>>>> >>>>>> --- /dev/null >>>>>> +++ b/drivers/thermal/renesas/rzg3s_thermal.c >>>>> >>>>>> +static int rzg3s_thermal_probe(struct platform_device *pdev) { >>>>>> + struct rzg3s_thermal_priv *priv; >>>>>> + struct device *dev = &pdev->dev; >>>>>> + int 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); >>>>>> + >>>>>> + priv->channel = devm_iio_channel_get(dev, "tsu"); >>>>> >>>>> Given there's only a single IIO channel, you could pass NULL >>>>> instead of the name, and drop "io-channel-names" from the DT bindings. >>>>> I don't know what's the IIO policy w.r.t. unnamed channels, though. >>>> >>>> It's supported, so fine as long as no future additional names show up. >>>> Will just fallback to index 0 I think. >>> >>> If everyone agrees, I would keep the name, too, to avoid complications >>> in case this IP variant will be extended on future SoCs. > > If you are planning to extend this driver to other SoCs then may be update I don't plan to extend it. My point here was to keep the driver as is for any possible future extensions that might arise. Thank you, Claudiu > KConfig with dependency on ARCH_RENESAS? see [1] > > [1] https://lore.kernel.org/linux-renesas-soc/20250118-trout-of-luxurious-inquire-aae9aa@krzk-bin/ > > Cheers, > Biju
Hi Claudiu, > -----Original Message----- > From: Claudiu Beznea <claudiu.beznea@tuxon.dev> > Sent: 27 January 2025 09:16 > Subject: Re: [PATCH 4/6] thermal: renesas: rzg3s: Add thermal driver for the Renesas RZ/G3S SoC > > Hi, Biju, > > On 27.01.2025 11:11, Biju Das wrote: > > Hi Claudiu, > > > >> -----Original Message----- > >> From: Geert Uytterhoeven <geert@linux-m68k.org> > >> Sent: 27 January 2025 08:55 > >> Subject: Re: [PATCH 4/6] thermal: renesas: rzg3s: Add thermal driver > >> for the Renesas RZ/G3S SoC > >> > >> Hi Claudiu, > >> > >> On Mon, 27 Jan 2025 at 09:33, Claudiu Beznea <claudiu.beznea@tuxon.dev> wrote: > >>> On 25.01.2025 14:18, Jonathan Cameron wrote: > >>>> On Wed, 22 Jan 2025 11:29:19 +0100 > >>>> Geert Uytterhoeven <geert@linux-m68k.org> wrote: > >>>>> On Fri, Jan 3, 2025 at 5:38 PM Claudiu <claudiu.beznea@tuxon.dev> wrote: > >>>>>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > >>>>>> > >>>>>> The Renesas RZ/G3S SoC features a Thermal Sensor Unit (TSU) that > >>>>>> reports the junction temperature. The temperature is reported > >>>>>> through a dedicated ADC channel. Add a driver for the Renesas RZ/G3S TSU. > >>>>>> > >>>>>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > >>>>> > >>>>> Thanks for your patch! > >>>>> > >>>>>> --- /dev/null > >>>>>> +++ b/drivers/thermal/renesas/rzg3s_thermal.c > >>>>> > >>>>>> +static int rzg3s_thermal_probe(struct platform_device *pdev) { > >>>>>> + struct rzg3s_thermal_priv *priv; > >>>>>> + struct device *dev = &pdev->dev; > >>>>>> + int 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); > >>>>>> + > >>>>>> + priv->channel = devm_iio_channel_get(dev, "tsu"); > >>>>> > >>>>> Given there's only a single IIO channel, you could pass NULL > >>>>> instead of the name, and drop "io-channel-names" from the DT bindings. > >>>>> I don't know what's the IIO policy w.r.t. unnamed channels, though. > >>>> > >>>> It's supported, so fine as long as no future additional names show up. > >>>> Will just fallback to index 0 I think. > >>> > >>> If everyone agrees, I would keep the name, too, to avoid > >>> complications in case this IP variant will be extended on future SoCs. > > > > If you are planning to extend this driver to other SoCs then may be > > update > > I don't plan to extend it. My point here was to keep the driver as is for any possible future > extensions that might arise. OK, then it is fine as the driver is only for RZ/G3S SoC. Cheers, Biju
diff --git a/MAINTAINERS b/MAINTAINERS index d2ab799a0659..0b5854dc2d5d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20131,6 +20131,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/potentiometer/renesas,x9250.yaml F: drivers/iio/potentiometer/x9250.c +RENESAS RZ/G3S THERMAL SENSOR UNIT DRIVER +M: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/thermal/renesas,r9a08g045-tsu.yaml +F: drivers/thermal/renesas/rzg3s_thermal.c + RESET CONTROLLER FRAMEWORK M: Philipp Zabel <p.zabel@pengutronix.de> S: Maintained diff --git a/drivers/thermal/renesas/Kconfig b/drivers/thermal/renesas/Kconfig index dcf5fc5ae08e..566478797095 100644 --- a/drivers/thermal/renesas/Kconfig +++ b/drivers/thermal/renesas/Kconfig @@ -26,3 +26,11 @@ config RZG2L_THERMAL help Enable this to plug the RZ/G2L thermal sensor driver into the Linux thermal framework. + +config RZG3S_THERMAL + tristate "Renesas RZ/G3S thermal driver" + depends on ARCH_R9A08G045 || COMPILE_TEST + depends on OF && IIO && RZG2L_ADC + help + Enable this to plug the RZ/G3S thermal sensor driver into the Linux + thermal framework. diff --git a/drivers/thermal/renesas/Makefile b/drivers/thermal/renesas/Makefile index bf9cb3cb94d6..1feb5ab78827 100644 --- a/drivers/thermal/renesas/Makefile +++ b/drivers/thermal/renesas/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_RZG2L_THERMAL) += rzg2l_thermal.o +obj-$(CONFIG_RZG3S_THERMAL) += rzg3s_thermal.o diff --git a/drivers/thermal/renesas/rzg3s_thermal.c b/drivers/thermal/renesas/rzg3s_thermal.c new file mode 100644 index 000000000000..6719f9ca05eb --- /dev/null +++ b/drivers/thermal/renesas/rzg3s_thermal.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G3S TSU Thermal Sensor Driver + * + * Copyright (C) 2024 Renesas Electronics Corporation + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/iio/consumer.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/thermal.h> +#include <linux/units.h> + +#include "../thermal_hwmon.h" + +#define TSU_SM 0x0 +#define TSU_SM_EN BIT(0) +#define TSU_SM_OE BIT(1) +#define OTPTSUTRIM_REG(n) (0x18 + (n) * 0x4) +#define OTPTSUTRIM_EN_MASK BIT(31) +#define OTPTSUTRIM_MASK GENMASK(11, 0) + +#define TSU_READ_STEPS 8 + +/* Default calibration values, if FUSE values are missing. */ +#define SW_CALIB0_VAL 1297 +#define SW_CALIB1_VAL 751 + +#define MCELSIUS(temp) ((temp) * MILLIDEGREE_PER_DEGREE) + +/** + * struct rzg3s_thermal_priv - RZ/G3S thermal private data structure + * @base: TSU base address + * @dev: device pointer + * @tz: thermal zone pointer + * @rstc: reset control + * @channel: IIO channel to read the TSU + * @mode: current device mode + * @calib0: calibration value + * @calib1: calibration value + */ +struct rzg3s_thermal_priv { + void __iomem *base; + struct device *dev; + struct thermal_zone_device *tz; + struct reset_control *rstc; + struct iio_channel *channel; + enum thermal_device_mode mode; + u16 calib0; + u16 calib1; +}; + +static int rzg3s_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz); + struct device *dev = priv->dev; + u32 ts_code_ave = 0; + int ret, val; + + if (priv->mode != THERMAL_DEVICE_ENABLED) + return -EAGAIN; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + for (u8 i = 0; i < TSU_READ_STEPS; i++) { + ret = iio_read_channel_raw(priv->channel, &val); + if (ret < 0) + goto rpm_put; + + ts_code_ave += val; + /* + * According to the HW manual (section 40.4.4 Procedure for Measuring the + * Temperature) we need to wait here at leat 3us. + */ + usleep_range(5, 10); + } + + ret = 0; + ts_code_ave = DIV_ROUND_CLOSEST(ts_code_ave, TSU_READ_STEPS); + + /* + * According to the HW manual (section 40.4.4 Procedure for Measuring the Temperature) + * the computation formula is as follows: + * + * Tj = (ts_code_ave - priv->calib1) * 165 / (priv->calib0 - priv->calib1) - 40 + */ + *temp = DIV_ROUND_CLOSEST((ts_code_ave - priv->calib1) * 165, + (priv->calib0 - priv->calib1)) - 40; + + /* Report it in mili degrees Celsius and round it up to 0.5 degrees Celsius. */ + *temp = roundup(MCELSIUS(*temp), 500); + +rpm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static void rzg3s_thermal_set_mode(struct rzg3s_thermal_priv *priv, + enum thermal_device_mode mode) +{ + struct device *dev = priv->dev; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return; + + if (mode == THERMAL_DEVICE_DISABLED) { + writel(0, priv->base + TSU_SM); + } else { + writel(TSU_SM_EN, priv->base + TSU_SM); + /* + * According to the HW manual (section 40.4.1 Procedure for + * Starting the TSU) we need to wait here 30us or more. + */ + usleep_range(30, 40); + + writel(TSU_SM_OE | TSU_SM_EN, priv->base + TSU_SM); + /* + * According to the HW manual (section 40.4.1 Procedure for + * Starting the TSU) we need to wait here 50us or more. + */ + usleep_range(50, 60); + } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static int rzg3s_thermal_change_mode(struct thermal_zone_device *tz, + enum thermal_device_mode mode) +{ + struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz); + + if (priv->mode == mode) + return 0; + + rzg3s_thermal_set_mode(priv, mode); + priv->mode = mode; + + return 0; +} + +static const struct thermal_zone_device_ops rzg3s_tz_of_ops = { + .get_temp = rzg3s_thermal_get_temp, + .change_mode = rzg3s_thermal_change_mode, +}; + +static int rzg3s_thermal_read_calib(struct rzg3s_thermal_priv *priv) +{ + struct device *dev = priv->dev; + u32 val; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + val = readl(priv->base + OTPTSUTRIM_REG(0)); + if (val & OTPTSUTRIM_EN_MASK) + priv->calib0 = FIELD_GET(OTPTSUTRIM_MASK, val); + else + priv->calib0 = SW_CALIB0_VAL; + + val = readl(priv->base + OTPTSUTRIM_REG(1)); + if (val & OTPTSUTRIM_EN_MASK) + priv->calib1 = FIELD_GET(OTPTSUTRIM_MASK, val); + else + priv->calib1 = SW_CALIB1_VAL; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int rzg3s_thermal_probe(struct platform_device *pdev) +{ + struct rzg3s_thermal_priv *priv; + struct device *dev = &pdev->dev; + int 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); + + priv->channel = devm_iio_channel_get(dev, "tsu"); + if (IS_ERR(priv->channel)) + return dev_err_probe(dev, PTR_ERR(priv->channel), "Failed to get IIO channel!\n"); + + priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(priv->rstc)) + return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset!\n"); + + priv->dev = dev; + priv->mode = THERMAL_DEVICE_DISABLED; + platform_set_drvdata(pdev, priv); + + pm_runtime_set_autosuspend_delay(dev, 300); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + ret = rzg3s_thermal_read_calib(priv); + if (ret) { + dev_err_probe(dev, ret, "Failed to read calibration data!\n"); + goto rpm_disable; + } + + priv->tz = thermal_of_zone_register(dev->of_node, 0, priv, &rzg3s_tz_of_ops); + if (IS_ERR(priv->tz)) { + dev_err_probe(dev, PTR_ERR(priv->tz), "Failed to register thermal zone!\n"); + goto rpm_disable; + } + + ret = thermal_add_hwmon_sysfs(priv->tz); + if (ret) { + dev_err_probe(dev, ret, "Failed to add hwmon sysfs!\n"); + goto tz_unregister; + } + + return 0; + +tz_unregister: + thermal_of_zone_unregister(priv->tz); +rpm_disable: + pm_runtime_disable(dev); + pm_runtime_dont_use_autosuspend(dev); + return ret; +} + +static void rzg3s_thermal_remove(struct platform_device *pdev) +{ + struct rzg3s_thermal_priv *priv = dev_get_drvdata(&pdev->dev); + + thermal_remove_hwmon_sysfs(priv->tz); + thermal_of_zone_unregister(priv->tz); + pm_runtime_disable(priv->dev); + pm_runtime_dont_use_autosuspend(priv->dev); +} + +static int rzg3s_thermal_suspend(struct device *dev) +{ + struct rzg3s_thermal_priv *priv = dev_get_drvdata(dev); + + rzg3s_thermal_set_mode(priv, THERMAL_DEVICE_DISABLED); + + return reset_control_assert(priv->rstc); +} + +static int rzg3s_thermal_resume(struct device *dev) +{ + struct rzg3s_thermal_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(priv->rstc); + if (ret) + return ret; + + if (priv->mode != THERMAL_DEVICE_DISABLED) + rzg3s_thermal_set_mode(priv, priv->mode); + + return 0; +} + +static const struct dev_pm_ops rzg3s_thermal_pm_ops = { + SYSTEM_SLEEP_PM_OPS(rzg3s_thermal_suspend, rzg3s_thermal_resume) +}; + +static const struct of_device_id rzg3s_thermal_dt_ids[] = { + { .compatible = "renesas,r9a08g045-tsu" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzg3s_thermal_dt_ids); + +static struct platform_driver rzg3s_thermal_driver = { + .driver = { + .name = "rzg3s_thermal", + .of_match_table = rzg3s_thermal_dt_ids, + .pm = pm_ptr(&rzg3s_thermal_pm_ops), + }, + .probe = rzg3s_thermal_probe, + .remove = rzg3s_thermal_remove, +}; +module_platform_driver(rzg3s_thermal_driver); + +MODULE_DESCRIPTION("Renesas RZ/G3S Thermal Sensor Unit Driver"); +MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>"); +MODULE_LICENSE("GPL");