* [PATCH 0/2] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring
@ 2026-05-15 8:53 Jishnu Prakash
2026-05-15 8:53 ` [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver Jishnu Prakash
2026-05-15 8:53 ` [PATCH 2/2] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring Jishnu Prakash
0 siblings, 2 replies; 9+ messages in thread
From: Jishnu Prakash @ 2026-05-15 8:53 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano,
Zhang Rui, Lukasz Luba
Cc: Jishnu Prakash, linux-arm-msm, linux-iio, linux-kernel, linux-pm,
Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong,
Stephan Gerhold
Support for the main PMIC5 Gen3 ADC driver has been merged now.
In order to complete adding support for the Gen3 ADC_TM auxiliary
driver, some more changes are needed in the main driver, mainly to
improve its shared interrupt's handling mechanism.
Patch 1 simplifies the interrupt handling in the main ADC driver
by declaring the interrupt as a shared one and dropping the logic
used to call the ADC_TM notifier for handling ADC_TM interrupts on
the first SDAM whose interrupt is shared.
Patch 2 adds the auxiliary thermal driver which supports the ADC_TM
functionality of ADC5 Gen3.
Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
---
Link to previous ADC5 Gen3 patch series:
V10: https://lore.kernel.org/all/20260130115421.2197892-1-jishnu.prakash@oss.qualcomm.com/
Patches 1-3 of the above series have been merged now. Patch 4 received some
later comments which are addressed in this series.
Patch 2 of this present series is derived from patch 4 of the above series,
with some changes.
Changes since V10:
- Added patch 1 of this series to make the SDAM0 IRQ a shared IRQ and
remove ADC_TM notifier, as suggested by Daniel.
- Made following changes to address Daniel's comments on patch 4 in
previous series:
- Added IRQ request call for SDAM0 IRQ, marking it as a shared IRQ.
- Split interrupt handler into main and threaded IRQ functions.
- Removed workqueue used in IRQ handler and its cleanup, instead use threaded
part of handler for same functionality.
- Removed callback function exposed to main ADC driver for TM IRQ handling on first SDAM.
- Removed workaround to capture temperature causing threshold violation inside interrupt
handler and then return this on next get_temp() call.
- Removed all error prints in interrupt handler and updated return value to IRQ_NONE
in case of errors.
- Removed explicit check to disable ADC_TM channel in case of (INT_MAX/-INT_MAX)
high/low thresholds being set in .set_trips callback.
- Also addressed Jonathan's comments on V10 patch 4 related to header file inclusion and for()
loop iterator initialization.
- Dropped Reviewed-by tag from Jonathan on V10 patch 4 due to significant changes made now.
- Added a status clearing register write in adc_tm5_gen3_disable_channel(), missed earlier.
- Moved cleanup action to disable ADC_TM channels in driver probe to just before IRQ requests,
for proper utilization.
To: Jonathan Cameron <jic23@kernel.org>
To: David Lechner <dlechner@baylibre.com>
To: Nuno Sá <nuno.sa@analog.com>
To: Andy Shevchenko <andy@kernel.org>
To: Amit Kucheria <amitk@kernel.org>
To: Thara Gopinath <thara.gopinath@gmail.com>
To: "Rafael J. Wysocki" <rafael@kernel.org>
To: Daniel Lezcano <daniel.lezcano@kernel.org>
To: Zhang Rui <rui.zhang@intel.com>
To: Lukasz Luba <lukasz.luba@arm.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: linux-iio@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-pm@vger.kernel.org
Cc: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
Cc: David Collins <david.collins@oss.qualcomm.com>
Cc: Anjelique Melendez <anjelique.melendez@oss.qualcomm.com>
Cc: Neil Armstrong <neil.armstrong@linaro.org>
Cc: Stephan Gerhold <stephan.gerhold@linaro.org>
---
Jishnu Prakash (2):
iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver
thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring
drivers/iio/adc/qcom-spmi-adc5-gen3.c | 52 +--
drivers/thermal/qcom/Kconfig | 9 +
drivers/thermal/qcom/Makefile | 1 +
drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c | 437 ++++++++++++++++++++++++++
include/linux/iio/adc/qcom-adc5-gen3-common.h | 2 -
5 files changed, 455 insertions(+), 46 deletions(-)
---
base-commit: 7fd2df204f342fc17d1a0bfcd474b24232fb0f32
change-id: 20260514-gen3_adc_tm-410dcb98be62
Best regards,
--
Jishnu Prakash <jishnu.prakash@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver 2026-05-15 8:53 [PATCH 0/2] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring Jishnu Prakash @ 2026-05-15 8:53 ` Jishnu Prakash 2026-05-15 13:54 ` Jonathan Cameron 2026-05-17 7:08 ` Andy Shevchenko 2026-05-15 8:53 ` [PATCH 2/2] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring Jishnu Prakash 1 sibling, 2 replies; 9+ messages in thread From: Jishnu Prakash @ 2026-05-15 8:53 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba Cc: Jishnu Prakash, linux-arm-msm, linux-iio, linux-kernel, linux-pm, Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong, Stephan Gerhold The SDAM0 IRQ can be triggered for both EOC (end of conversion) events for immediate ADC reads done in this driver and for threshold violation events, based on ADC_TM thresholds configured from the auxiliary ADC_TM driver on TM channels on the first SDAM. At present, this interrupt is handled only in the ISR in the main ADC driver. When the ISR is triggered for an ADC_TM event, this driver notifies the ADC_TM driver by calling a notifier callback exposed from it for this purpose. To simplify the interrupt handling in both drivers, share the interrupt between the drivers. With this, ADC_TM interrupts on SDAM0 will be handled directly in the ADC_TM driver, so remove the notifier callback and all TM interrupt handling in the main ADC ISR. Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> --- drivers/iio/adc/qcom-spmi-adc5-gen3.c | 52 +++++---------------------- include/linux/iio/adc/qcom-adc5-gen3-common.h | 2 -- 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c index f8168a14b907..a819c3e627a0 100644 --- a/drivers/iio/adc/qcom-spmi-adc5-gen3.c +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c @@ -56,9 +56,6 @@ struct adc5_channel_prop { * requests from multiple clients. * @data: software configuration data. * @n_tm_channels: number of ADC channels used for TM measurements. - * @handler: TM callback to be called for threshold violation interrupt - * on first SDAM. - * @tm_aux: pointer to auxiliary TM device. */ struct adc5_chip { struct device *dev; @@ -70,8 +67,6 @@ struct adc5_chip { struct mutex lock; const struct adc5_data *data; unsigned int n_tm_channels; - void (*handler)(struct auxiliary_device *tm_aux); - struct auxiliary_device *tm_aux; }; int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index, @@ -287,23 +282,21 @@ static irqreturn_t adc5_gen3_isr(int irq, void *dev_id) { struct adc5_chip *adc = dev_id; struct device *dev = adc->dev; - struct auxiliary_device *adev; u8 status, eoc_status, val; - u8 tm_status[2]; int ret; ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_STATUS1, &status, sizeof(status)); if (ret) { dev_err(dev, "adc read status1 failed with %d\n", ret); - return IRQ_HANDLED; + return IRQ_NONE; } ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_EOC_STS, &eoc_status, sizeof(eoc_status)); if (ret) { dev_err(dev, "adc read eoc status failed with %d\n", ret); - return IRQ_HANDLED; + return IRQ_NONE; } if (status & ADC5_GEN3_STATUS1_CONV_FAULT) { @@ -316,30 +309,13 @@ static irqreturn_t adc5_gen3_isr(int irq, void *dev_id) return IRQ_HANDLED; } - /* CHAN0 is the preconfigured channel for immediate conversion */ - if (eoc_status & ADC5_GEN3_EOC_CHAN_0) - complete(&adc->complete); - - ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, - ADC5_GEN3_TM_HIGH_STS, tm_status, sizeof(tm_status)); - if (ret) { - dev_err(dev, "adc read TM status failed with %d\n", ret); - return IRQ_HANDLED; - } - - dev_dbg(dev, "Interrupt status:%#x, EOC status:%#x, high:%#x, low:%#x\n", - status, eoc_status, tm_status[0], tm_status[1]); - - if (tm_status[0] || tm_status[1]) { - adev = adc->tm_aux; - if (!adev || !adev->dev.driver) { - dev_err(dev, "adc_tm auxiliary device not initialized\n"); - return IRQ_HANDLED; - } + dev_dbg(dev, "Interrupt status:%#x, EOC status:%#x\n", status, eoc_status); - adc->handler(adev); - } + /* CHAN0 is the preconfigured channel for immediate conversion */ + if (!(eoc_status & ADC5_GEN3_EOC_CHAN_0)) + return IRQ_NONE; + complete(&adc->complete); return IRQ_HANDLED; } @@ -684,8 +660,6 @@ static int adc5_gen3_add_aux_tm_device(struct adc5_chip *adc) if (ret) return ret; - adc->tm_aux = &aux_device->aux_dev; - return 0; } @@ -741,16 +715,6 @@ int adc5_gen3_therm_code_to_temp(struct device *dev, } EXPORT_SYMBOL_NS_GPL(adc5_gen3_therm_code_to_temp, "QCOM_SPMI_ADC5_GEN3"); -void adc5_gen3_register_tm_event_notifier(struct device *dev, - void (*handler)(struct auxiliary_device *)) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); - struct adc5_chip *adc = iio_priv(indio_dev); - - adc->handler = handler; -} -EXPORT_SYMBOL_NS_GPL(adc5_gen3_register_tm_event_notifier, "QCOM_SPMI_ADC5_GEN3"); - static int adc5_gen3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -818,7 +782,7 @@ static int adc5_gen3_probe(struct platform_device *pdev) } ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq, - adc5_gen3_isr, 0, + adc5_gen3_isr, IRQF_ONESHOT | IRQF_SHARED, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name, adc); if (ret) diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h index 6303eaa6640b..39cbfcbdb101 100644 --- a/include/linux/iio/adc/qcom-adc5-gen3-common.h +++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h @@ -205,7 +205,5 @@ int adc5_gen3_get_scaled_reading(struct device *dev, int adc5_gen3_therm_code_to_temp(struct device *dev, struct adc5_channel_common_prop *common_props, u16 code, int *val); -void adc5_gen3_register_tm_event_notifier(struct device *dev, - void (*handler)(struct auxiliary_device *)); #endif /* QCOM_ADC5_GEN3_COMMON_H */ -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver 2026-05-15 8:53 ` [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver Jishnu Prakash @ 2026-05-15 13:54 ` Jonathan Cameron 2026-05-21 10:46 ` Jishnu Prakash 2026-05-17 7:08 ` Andy Shevchenko 1 sibling, 1 reply; 9+ messages in thread From: Jonathan Cameron @ 2026-05-15 13:54 UTC (permalink / raw) To: Jishnu Prakash Cc: David Lechner, Nuno Sá, Andy Shevchenko, Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-arm-msm, linux-iio, linux-kernel, linux-pm, Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong, Stephan Gerhold On Fri, 15 May 2026 14:23:44 +0530 Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> wrote: > The SDAM0 IRQ can be triggered for both EOC (end of conversion) events for > immediate ADC reads done in this driver and for threshold violation events, > based on ADC_TM thresholds configured from the auxiliary ADC_TM driver on > TM channels on the first SDAM. > > At present, this interrupt is handled only in the ISR in the main ADC driver. > When the ISR is triggered for an ADC_TM event, this driver notifies the ADC_TM > driver by calling a notifier callback exposed from it for this purpose. > > To simplify the interrupt handling in both drivers, share the interrupt between > the drivers. With this, ADC_TM interrupts on SDAM0 will be handled directly in > the ADC_TM driver, so remove the notifier callback and all TM interrupt > handling in the main ADC ISR. > > Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> > --- Some stuff from Sashiko on this one: https://sashiko.dev/#/patchset/20260515-gen3_adc_tm-v1-0-39ba29f9b4ab%40oss.qualcomm.com Given I assume you didn't see the warning (I'm fairly sure the bots analysis is correct as we've been busy fixing similar cases all cycle), can I just check, have you tested this on latest upstream? Thanks, Jonathan > drivers/iio/adc/qcom-spmi-adc5-gen3.c | 52 +++++---------------------- > include/linux/iio/adc/qcom-adc5-gen3-common.h | 2 -- > 2 files changed, 8 insertions(+), 46 deletions(-) > > diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c > index f8168a14b907..a819c3e627a0 100644 > --- a/drivers/iio/adc/qcom-spmi-adc5-gen3.c > +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c > static int adc5_gen3_probe(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > @@ -818,7 +782,7 @@ static int adc5_gen3_probe(struct platform_device *pdev) > } > > ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq, > - adc5_gen3_isr, 0, > + adc5_gen3_isr, IRQF_ONESHOT | IRQF_SHARED, Sashikio points out that IRQF_ONESHOT is never correct for a non threaded interrupt. The point of that flag is to ensure we don't handle another interrupt until the thread is done. If there isn't a thread then it doesn't do anything (other than omit a warning!) > adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name, > adc); > if (ret) > diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h > index 6303eaa6640b..39cbfcbdb101 100644 > --- a/include/linux/iio/adc/qcom-adc5-gen3-common.h > +++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h > @@ -205,7 +205,5 @@ int adc5_gen3_get_scaled_reading(struct device *dev, > int adc5_gen3_therm_code_to_temp(struct device *dev, > struct adc5_channel_common_prop *common_props, > u16 code, int *val); > -void adc5_gen3_register_tm_event_notifier(struct device *dev, > - void (*handler)(struct auxiliary_device *)); > > #endif /* QCOM_ADC5_GEN3_COMMON_H */ > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver 2026-05-15 13:54 ` Jonathan Cameron @ 2026-05-21 10:46 ` Jishnu Prakash 2026-05-22 10:47 ` Jonathan Cameron 0 siblings, 1 reply; 9+ messages in thread From: Jishnu Prakash @ 2026-05-21 10:46 UTC (permalink / raw) To: Jonathan Cameron Cc: David Lechner, Nuno Sá, Andy Shevchenko, Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-arm-msm, linux-iio, linux-kernel, linux-pm, Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong, Stephan Gerhold Hi Jonathan, On 5/15/2026 7:24 PM, Jonathan Cameron wrote: > On Fri, 15 May 2026 14:23:44 +0530 > Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> wrote: > >> The SDAM0 IRQ can be triggered for both EOC (end of conversion) events for >> immediate ADC reads done in this driver and for threshold violation events, >> based on ADC_TM thresholds configured from the auxiliary ADC_TM driver on >> TM channels on the first SDAM. >> >> At present, this interrupt is handled only in the ISR in the main ADC driver. >> When the ISR is triggered for an ADC_TM event, this driver notifies the ADC_TM >> driver by calling a notifier callback exposed from it for this purpose. >> >> To simplify the interrupt handling in both drivers, share the interrupt between >> the drivers. With this, ADC_TM interrupts on SDAM0 will be handled directly in >> the ADC_TM driver, so remove the notifier callback and all TM interrupt >> handling in the main ADC ISR. >> >> Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> >> --- > > Some stuff from Sashiko on this one: > https://sashiko.dev/#/patchset/20260515-gen3_adc_tm-v1-0-39ba29f9b4ab%40oss.qualcomm.com > > Given I assume you didn't see the warning (I'm fairly sure the bots analysis is correct > as we've been busy fixing similar cases all cycle), can I just check, have you tested > this on latest upstream? I had tested on a build based on top of Linux 7.1-rc2 and verified the driver's basic functionality, but I think I overlooked the warning from the interrupt management code, sorry about the miss. > > Thanks, > > Jonathan > > >> drivers/iio/adc/qcom-spmi-adc5-gen3.c | 52 +++++---------------------- >> include/linux/iio/adc/qcom-adc5-gen3-common.h | 2 -- >> 2 files changed, 8 insertions(+), 46 deletions(-) >> >> diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c >> index f8168a14b907..a819c3e627a0 100644 >> --- a/drivers/iio/adc/qcom-spmi-adc5-gen3.c >> +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c > >> static int adc5_gen3_probe(struct platform_device *pdev) >> { >> struct device *dev = &pdev->dev; >> @@ -818,7 +782,7 @@ static int adc5_gen3_probe(struct platform_device *pdev) >> } >> >> ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq, >> - adc5_gen3_isr, 0, >> + adc5_gen3_isr, IRQF_ONESHOT | IRQF_SHARED, > > Sashikio points out that IRQF_ONESHOT is never correct for a non threaded > interrupt. The point of that flag is to ensure we don't handle another interrupt > until the thread is done. If there isn't a thread then it doesn't do anything > (other than omit a warning!) I tried at first keeping only the IRQF_SHARED flag here, but it seems that shared interrupts need to agree on the ONESHOT flag configuration, else the second interrupt's IRQ request call fails. And the ADC_TM interrupt needs to be ONESHOT, since we don't want that interrupt to be rearmed before we have notified the thermal framework from the threaded part of the handler. So I had to add the IRQF_ONESHOT here too, though it is not useful here. I think it's best to use a threaded IRQ handler in this driver too. I don't really see any meaningful way to split the actions in the interrupt handler here into a primary handler and a threaded handler, so is it fine if I just make the primary handler NULL and move all the ISR functionality into the threaded handler part ? Thanks, Jishnu > >> adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name, >> adc); >> if (ret) >> diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h >> index 6303eaa6640b..39cbfcbdb101 100644 >> --- a/include/linux/iio/adc/qcom-adc5-gen3-common.h >> +++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h >> @@ -205,7 +205,5 @@ int adc5_gen3_get_scaled_reading(struct device *dev, >> int adc5_gen3_therm_code_to_temp(struct device *dev, >> struct adc5_channel_common_prop *common_props, >> u16 code, int *val); >> -void adc5_gen3_register_tm_event_notifier(struct device *dev, >> - void (*handler)(struct auxiliary_device *)); >> >> #endif /* QCOM_ADC5_GEN3_COMMON_H */ >> > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver 2026-05-21 10:46 ` Jishnu Prakash @ 2026-05-22 10:47 ` Jonathan Cameron 2026-05-26 10:53 ` Jishnu Prakash 0 siblings, 1 reply; 9+ messages in thread From: Jonathan Cameron @ 2026-05-22 10:47 UTC (permalink / raw) To: Jishnu Prakash Cc: David Lechner, Nuno Sá, Andy Shevchenko, Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-arm-msm, linux-iio, linux-kernel, linux-pm, Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong, Stephan Gerhold On Thu, 21 May 2026 16:16:17 +0530 Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> wrote: > Hi Jonathan, > > On 5/15/2026 7:24 PM, Jonathan Cameron wrote: > > On Fri, 15 May 2026 14:23:44 +0530 > > Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> wrote: > > > >> The SDAM0 IRQ can be triggered for both EOC (end of conversion) events for > >> immediate ADC reads done in this driver and for threshold violation events, > >> based on ADC_TM thresholds configured from the auxiliary ADC_TM driver on > >> TM channels on the first SDAM. > >> > >> At present, this interrupt is handled only in the ISR in the main ADC driver. > >> When the ISR is triggered for an ADC_TM event, this driver notifies the ADC_TM > >> driver by calling a notifier callback exposed from it for this purpose. > >> > >> To simplify the interrupt handling in both drivers, share the interrupt between > >> the drivers. With this, ADC_TM interrupts on SDAM0 will be handled directly in > >> the ADC_TM driver, so remove the notifier callback and all TM interrupt > >> handling in the main ADC ISR. > >> > >> Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> > >> --- > > > > Some stuff from Sashiko on this one: > > https://sashiko.dev/#/patchset/20260515-gen3_adc_tm-v1-0-39ba29f9b4ab%40oss.qualcomm.com > > > > Given I assume you didn't see the warning (I'm fairly sure the bots analysis is correct > > as we've been busy fixing similar cases all cycle), can I just check, have you tested > > this on latest upstream? > > I had tested on a build based on top of Linux 7.1-rc2 and verified the driver's basic > functionality, but I think I overlooked the warning from the interrupt management code, > sorry about the miss. > > > > > Thanks, > > > > Jonathan > > > > > >> drivers/iio/adc/qcom-spmi-adc5-gen3.c | 52 +++++---------------------- > >> include/linux/iio/adc/qcom-adc5-gen3-common.h | 2 -- > >> 2 files changed, 8 insertions(+), 46 deletions(-) > >> > >> diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c > >> index f8168a14b907..a819c3e627a0 100644 > >> --- a/drivers/iio/adc/qcom-spmi-adc5-gen3.c > >> +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c > > > >> static int adc5_gen3_probe(struct platform_device *pdev) > >> { > >> struct device *dev = &pdev->dev; > >> @@ -818,7 +782,7 @@ static int adc5_gen3_probe(struct platform_device *pdev) > >> } > >> > >> ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq, > >> - adc5_gen3_isr, 0, > >> + adc5_gen3_isr, IRQF_ONESHOT | IRQF_SHARED, > > > > Sashikio points out that IRQF_ONESHOT is never correct for a non threaded > > interrupt. The point of that flag is to ensure we don't handle another interrupt > > until the thread is done. If there isn't a thread then it doesn't do anything > > (other than omit a warning!) > > I tried at first keeping only the IRQF_SHARED flag here, but it seems that > shared interrupts need to agree on the ONESHOT flag configuration, else the > second interrupt's IRQ request call fails. > > And the ADC_TM interrupt needs to be ONESHOT, since we don't want that interrupt to > be rearmed before we have notified the thermal framework from the threaded > part of the handler. So I had to add the IRQF_ONESHOT here too, though it is > not useful here. That's an interesting corner case. Maybe the warning needs to be more refined? (I don't think it checks for shared?) > > I think it's best to use a threaded IRQ handler in this driver too. I don't really > see any meaningful way to split the actions in the interrupt handler here into a primary > handler and a threaded handler, so is it fine if I just make the primary handler NULL > and move all the ISR functionality into the threaded handler part ? That's fine by me. Just add some comments on why. J > > Thanks, > Jishnu > > > > > >> adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name, > >> adc); > >> if (ret) > >> diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h > >> index 6303eaa6640b..39cbfcbdb101 100644 > >> --- a/include/linux/iio/adc/qcom-adc5-gen3-common.h > >> +++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h > >> @@ -205,7 +205,5 @@ int adc5_gen3_get_scaled_reading(struct device *dev, > >> int adc5_gen3_therm_code_to_temp(struct device *dev, > >> struct adc5_channel_common_prop *common_props, > >> u16 code, int *val); > >> -void adc5_gen3_register_tm_event_notifier(struct device *dev, > >> - void (*handler)(struct auxiliary_device *)); > >> > >> #endif /* QCOM_ADC5_GEN3_COMMON_H */ > >> > > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver 2026-05-22 10:47 ` Jonathan Cameron @ 2026-05-26 10:53 ` Jishnu Prakash 0 siblings, 0 replies; 9+ messages in thread From: Jishnu Prakash @ 2026-05-26 10:53 UTC (permalink / raw) To: Jonathan Cameron Cc: David Lechner, Nuno Sá, Andy Shevchenko, Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-arm-msm, linux-iio, linux-kernel, linux-pm, Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong, Stephan Gerhold Hi Jonathan, On 5/22/2026 4:17 PM, Jonathan Cameron wrote: > On Thu, 21 May 2026 16:16:17 +0530 > Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> wrote: > >> Hi Jonathan, >> >> On 5/15/2026 7:24 PM, Jonathan Cameron wrote: >>> On Fri, 15 May 2026 14:23:44 +0530 >>> Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> wrote: >>> ... >>>> diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c >>>> index f8168a14b907..a819c3e627a0 100644 >>>> --- a/drivers/iio/adc/qcom-spmi-adc5-gen3.c >>>> +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c >>> >>>> static int adc5_gen3_probe(struct platform_device *pdev) >>>> { >>>> struct device *dev = &pdev->dev; >>>> @@ -818,7 +782,7 @@ static int adc5_gen3_probe(struct platform_device *pdev) >>>> } >>>> >>>> ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq, >>>> - adc5_gen3_isr, 0, >>>> + adc5_gen3_isr, IRQF_ONESHOT | IRQF_SHARED, >>> >>> Sashikio points out that IRQF_ONESHOT is never correct for a non threaded >>> interrupt. The point of that flag is to ensure we don't handle another interrupt >>> until the thread is done. If there isn't a thread then it doesn't do anything >>> (other than omit a warning!) >> >> I tried at first keeping only the IRQF_SHARED flag here, but it seems that >> shared interrupts need to agree on the ONESHOT flag configuration, else the >> second interrupt's IRQ request call fails. >> >> And the ADC_TM interrupt needs to be ONESHOT, since we don't want that interrupt to >> be rearmed before we have notified the thermal framework from the threaded >> part of the handler. So I had to add the IRQF_ONESHOT here too, though it is >> not useful here. > That's an interesting corner case. Maybe the warning needs to be more refined? > (I don't think it checks for shared?) Yes, at the point Sashiko indicated, it looks like there is no check for shared IRQ, although it is checked later in the same function (__setup_irq). > >> >> I think it's best to use a threaded IRQ handler in this driver too. I don't really >> see any meaningful way to split the actions in the interrupt handler here into a primary >> handler and a threaded handler, so is it fine if I just make the primary handler NULL >> and move all the ISR functionality into the threaded handler part ? > > That's fine by me. Just add some comments on why. Thanks for confirming, I'll push again with a comment explaining this change. Thanks, Jishnu > > J >> >> Thanks, >> Jishnu >> >> >>> >>>> adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name, >>>> adc); >>>> if (ret) >>>> diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h >>>> index 6303eaa6640b..39cbfcbdb101 100644 >>>> --- a/include/linux/iio/adc/qcom-adc5-gen3-common.h >>>> +++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h >>>> @@ -205,7 +205,5 @@ int adc5_gen3_get_scaled_reading(struct device *dev, >>>> int adc5_gen3_therm_code_to_temp(struct device *dev, >>>> struct adc5_channel_common_prop *common_props, >>>> u16 code, int *val); >>>> -void adc5_gen3_register_tm_event_notifier(struct device *dev, >>>> - void (*handler)(struct auxiliary_device *)); >>>> >>>> #endif /* QCOM_ADC5_GEN3_COMMON_H */ >>>> >>> >> > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver 2026-05-15 8:53 ` [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver Jishnu Prakash 2026-05-15 13:54 ` Jonathan Cameron @ 2026-05-17 7:08 ` Andy Shevchenko 2026-05-20 15:20 ` Daniel Lezcano 1 sibling, 1 reply; 9+ messages in thread From: Andy Shevchenko @ 2026-05-17 7:08 UTC (permalink / raw) To: Jishnu Prakash Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-arm-msm, linux-iio, linux-kernel, linux-pm, Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong, Stephan Gerhold On Fri, May 15, 2026 at 02:23:44PM +0530, Jishnu Prakash wrote: > The SDAM0 IRQ can be triggered for both EOC (end of conversion) events for > immediate ADC reads done in this driver and for threshold violation events, > based on ADC_TM thresholds configured from the auxiliary ADC_TM driver on > TM channels on the first SDAM. > > At present, this interrupt is handled only in the ISR in the main ADC driver. > When the ISR is triggered for an ADC_TM event, this driver notifies the ADC_TM > driver by calling a notifier callback exposed from it for this purpose. > > To simplify the interrupt handling in both drivers, share the interrupt between > the drivers. With this, ADC_TM interrupts on SDAM0 will be handled directly in "will"?! Do you mean this patch deliberately breaks the working setups? > the ADC_TM driver, so remove the notifier callback and all TM interrupt > handling in the main ADC ISR. -- With Best Regards, Andy Shevchenko ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver 2026-05-17 7:08 ` Andy Shevchenko @ 2026-05-20 15:20 ` Daniel Lezcano 0 siblings, 0 replies; 9+ messages in thread From: Daniel Lezcano @ 2026-05-20 15:20 UTC (permalink / raw) To: Andy Shevchenko, Jishnu Prakash Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-arm-msm, linux-iio, linux-kernel, linux-pm, Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong, Stephan Gerhold Hi Andy, On 5/17/26 09:08, Andy Shevchenko wrote: > On Fri, May 15, 2026 at 02:23:44PM +0530, Jishnu Prakash wrote: >> The SDAM0 IRQ can be triggered for both EOC (end of conversion) events for >> immediate ADC reads done in this driver and for threshold violation events, >> based on ADC_TM thresholds configured from the auxiliary ADC_TM driver on >> TM channels on the first SDAM. >> >> At present, this interrupt is handled only in the ISR in the main ADC driver. >> When the ISR is triggered for an ADC_TM event, this driver notifies the ADC_TM >> driver by calling a notifier callback exposed from it for this purpose. >> >> To simplify the interrupt handling in both drivers, share the interrupt between >> the drivers. With this, ADC_TM interrupts on SDAM0 will be handled directly in > > "will"?! Do you mean this patch deliberately breaks the working setups? No, the interrupt is definitively shared between the iio and the thermal sensor. So when submitting the previous series, all IIO patches were picked up but I asked Jishnu to revisit the interrupt handling in order to make it shared. That is what he did here by reworking the ISR in the IIO and resending the thermal sensor I did not pick for this reason before. This patch does not break working setups. ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/2] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring 2026-05-15 8:53 [PATCH 0/2] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring Jishnu Prakash 2026-05-15 8:53 ` [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver Jishnu Prakash @ 2026-05-15 8:53 ` Jishnu Prakash 1 sibling, 0 replies; 9+ messages in thread From: Jishnu Prakash @ 2026-05-15 8:53 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Amit Kucheria, Thara Gopinath, Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba Cc: Jishnu Prakash, linux-arm-msm, linux-iio, linux-kernel, linux-pm, Kamal Wadhwa, David Collins, Anjelique Melendez, Neil Armstrong, Stephan Gerhold Add support for ADC_TM part of PMIC5 Gen3. This is an auxiliary driver under the Gen3 ADC driver, which implements the threshold setting and interrupt generating functionalities of QCOM ADC_TM drivers, used to support thermal trip points. Signed-off-by: Jishnu Prakash <jishnu.prakash@oss.qualcomm.com> --- drivers/thermal/qcom/Kconfig | 9 + drivers/thermal/qcom/Makefile | 1 + drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c | 437 ++++++++++++++++++++++++++ 3 files changed, 447 insertions(+) diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index a6bb01082ec6..1acb11e4ac80 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -21,6 +21,15 @@ config QCOM_SPMI_ADC_TM5 Thermal client sets threshold temperature for both warm and cool and gets updated when a threshold is reached. +config QCOM_SPMI_ADC_TM5_GEN3 + tristate "Qualcomm SPMI PMIC Thermal Monitor ADC5 Gen3" + depends on QCOM_SPMI_ADC5_GEN3 + help + This enables the auxiliary thermal driver for the ADC5 Gen3 thermal + monitoring device. It shows up as a thermal zone with multiple trip points. + Thermal client sets threshold temperature for both warm and cool and + gets updated when a threshold is reached. + config QCOM_SPMI_TEMP_ALARM tristate "Qualcomm SPMI PMIC Temperature Alarm" depends on OF && SPMI && IIO diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index 0fa2512042e7..828d9e7bc797 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \ tsens-8960.o obj-$(CONFIG_QCOM_SPMI_ADC_TM5) += qcom-spmi-adc-tm5.o +obj-$(CONFIG_QCOM_SPMI_ADC_TM5_GEN3) += qcom-spmi-adc-tm5-gen3.o obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o obj-$(CONFIG_QCOM_LMH) += lmh.o diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c new file mode 100644 index 000000000000..633008f173a8 --- /dev/null +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5-gen3.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/container_of.h> +#include <linux/device/devres.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/iio/adc/qcom-adc5-gen3-common.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/thermal.h> +#include <linux/types.h> +#include <linux/unaligned.h> + +#include "../thermal_hwmon.h" + +#define ADC_TM5_GEN3_CONFIG_REGS 12 + +struct device; +struct adc_tm5_gen3_chip; + +/** + * struct adc_tm5_gen3_channel_props - ADC_TM channel structure + * @timer: time period of recurring TM measurement. + * @tm_chan_index: TM channel number used (ranging from 1-7). + * @sdam_index: SDAM on which this TM channel lies. + * @common_props: structure with common ADC channel properties. + * @high_thr_en: TM high threshold crossing detection enabled. + * @low_thr_en: TM low threshold crossing detection enabled. + * @chip: ADC TM device. + * @tzd: pointer to thermal device corresponding to TM channel. + */ +struct adc_tm5_gen3_channel_props { + unsigned int timer; + unsigned int tm_chan_index; + unsigned int sdam_index; + struct adc5_channel_common_prop common_props; + bool high_thr_en; + bool low_thr_en; + struct adc_tm5_gen3_chip *chip; + struct thermal_zone_device *tzd; +}; + +/** + * struct adc_tm5_gen3_chip - ADC Thermal Monitoring device structure + * @dev_data: Top-level ADC device data. + * @chan_props: Array of ADC_TM channel structures. + * @nchannels: number of TM channels allocated + * @dev: SPMI ADC5 Gen3 device. + */ +struct adc_tm5_gen3_chip { + struct adc5_device_data *dev_data; + struct adc_tm5_gen3_channel_props *chan_props; + unsigned int nchannels; + struct device *dev; +}; + +DEFINE_GUARD(adc5_gen3, struct adc_tm5_gen3_chip *, adc5_gen3_mutex_lock(_T->dev), + adc5_gen3_mutex_unlock(_T->dev)) + +static int get_sdam_from_irq(struct adc_tm5_gen3_chip *adc_tm5, int irq) +{ + for (int i = 0; i < adc_tm5->dev_data->num_sdams; i++) { + if (adc_tm5->dev_data->base[i].irq == irq) + return i; + } + return -ENOENT; +} + +static irqreturn_t adctm5_gen3_isr(int irq, void *dev_id) +{ + struct adc_tm5_gen3_chip *adc_tm5 = dev_id; + int ret, sdam_num; + u8 tm_status[2]; + u8 status, val; + + sdam_num = get_sdam_from_irq(adc_tm5, irq); + if (sdam_num < 0) + return IRQ_NONE; + + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_STATUS1, + &status, sizeof(status)); + if (ret) + return IRQ_NONE; + + if (status & ADC5_GEN3_STATUS1_CONV_FAULT) { + val = ADC5_GEN3_CONV_ERR_CLR_REQ; + adc5_gen3_status_clear(adc_tm5->dev_data, sdam_num, + ADC5_GEN3_CONV_ERR_CLR, &val, 1); + return IRQ_HANDLED; + } + + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_num, ADC5_GEN3_TM_HIGH_STS, + tm_status, sizeof(tm_status)); + if (ret) + return IRQ_NONE; + + if (tm_status[0] || tm_status[1]) + return IRQ_WAKE_THREAD; + + return IRQ_NONE; +} + +static int adc5_gen3_tm_status_check(struct adc_tm5_gen3_chip *adc_tm5, + int sdam_index, u8 *tm_status, u8 *buf) +{ + int ret; + + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_index, ADC5_GEN3_TM_HIGH_STS, + tm_status, 2); + if (ret) + return ret; + + ret = adc5_gen3_status_clear(adc_tm5->dev_data, sdam_index, ADC5_GEN3_TM_HIGH_STS_CLR, + tm_status, 2); + if (ret) + return ret; + + ret = adc5_gen3_read(adc_tm5->dev_data, sdam_index, ADC5_GEN3_CH_DATA0(0), + buf, 16); + return ret; +} + +static irqreturn_t adctm5_gen3_isr_thread(int irq, void *dev_id) +{ + struct adc_tm5_gen3_chip *adc_tm5 = dev_id; + int sdam_index = -1; + u8 tm_status[2] = { }; + u8 buf[16] = { }; + + for (int i = 0; i < adc_tm5->nchannels; i++) { + struct adc_tm5_gen3_channel_props *chan_prop = &adc_tm5->chan_props[i]; + int offset = chan_prop->tm_chan_index; + bool upper_set, lower_set; + int ret; + + scoped_guard(adc5_gen3, adc_tm5) { + if (chan_prop->sdam_index != sdam_index) { + sdam_index = chan_prop->sdam_index; + ret = adc5_gen3_tm_status_check(adc_tm5, sdam_index, + tm_status, buf); + if (ret) + return IRQ_NONE; + } + + upper_set = ((tm_status[0] & BIT(offset)) && chan_prop->high_thr_en); + lower_set = ((tm_status[1] & BIT(offset)) && chan_prop->low_thr_en); + } + + if (!(upper_set || lower_set)) + continue; + + thermal_zone_device_update(chan_prop->tzd, THERMAL_TRIP_VIOLATED); + } + + return IRQ_HANDLED; +} + +static int adc_tm5_gen3_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct adc_tm5_gen3_channel_props *prop = thermal_zone_device_priv(tz); + struct adc_tm5_gen3_chip *adc_tm5; + + if (!prop || !prop->chip) + return -EINVAL; + + adc_tm5 = prop->chip; + + return adc5_gen3_get_scaled_reading(adc_tm5->dev, &prop->common_props, + temp); +} + +static int adc_tm5_gen3_disable_channel(struct adc_tm5_gen3_channel_props *prop) +{ + struct adc_tm5_gen3_chip *adc_tm5 = prop->chip; + int ret; + u8 val; + + prop->high_thr_en = false; + prop->low_thr_en = false; + + ret = adc5_gen3_poll_wait_hs(adc_tm5->dev_data, prop->sdam_index); + if (ret) + return ret; + + val = BIT(prop->tm_chan_index); + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, + ADC5_GEN3_TM_HIGH_STS_CLR, &val, sizeof(val)); + if (ret) + return ret; + + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, + ADC5_GEN3_TM_LOW_STS_CLR, &val, sizeof(val)); + if (ret) + return ret; + + val = MEAS_INT_DISABLE; + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, + ADC5_GEN3_TIMER_SEL, &val, sizeof(val)); + if (ret) + return ret; + + /* To indicate there is an actual conversion request */ + val = ADC5_GEN3_CHAN_CONV_REQ | prop->tm_chan_index; + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, + ADC5_GEN3_PERPH_CH, &val, sizeof(val)); + if (ret) + return ret; + + val = ADC5_GEN3_CONV_REQ_REQ; + return adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, + ADC5_GEN3_CONV_REQ, &val, sizeof(val)); +} + +static int adc_tm5_gen3_configure(struct adc_tm5_gen3_channel_props *prop, + int low_temp, int high_temp) +{ + struct adc_tm5_gen3_chip *adc_tm5 = prop->chip; + u8 buf[ADC_TM5_GEN3_CONFIG_REGS]; + u8 conv_req; + u16 adc_code; + int ret; + + ret = adc5_gen3_poll_wait_hs(adc_tm5->dev_data, prop->sdam_index); + if (ret < 0) + return ret; + + ret = adc5_gen3_read(adc_tm5->dev_data, prop->sdam_index, + ADC5_GEN3_SID, buf, sizeof(buf)); + if (ret < 0) + return ret; + + /* Write SID */ + buf[0] = FIELD_PREP(ADC5_GEN3_SID_MASK, prop->common_props.sid); + + /* Select TM channel and indicate there is an actual conversion request */ + buf[1] = ADC5_GEN3_CHAN_CONV_REQ | prop->tm_chan_index; + + buf[2] = prop->timer; + + /* Digital param selection */ + adc5_gen3_update_dig_param(&prop->common_props, &buf[3]); + + /* Update fast average sample value */ + buf[4] &= ~ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK; + buf[4] |= prop->common_props.avg_samples | ADC5_GEN3_FAST_AVG_CTL_EN; + + /* Select ADC channel */ + buf[5] = prop->common_props.channel; + + /* Select HW settle delay for channel */ + buf[6] = FIELD_PREP(ADC5_GEN3_HW_SETTLE_DELAY_MASK, + prop->common_props.hw_settle_time_us); + + /* High temperature corresponds to low voltage threshold */ + prop->low_thr_en = (high_temp != INT_MAX); + if (prop->low_thr_en) { + adc_code = qcom_adc_tm5_gen2_temp_res_scale(high_temp); + put_unaligned_le16(adc_code, &buf[8]); + } + + /* Low temperature corresponds to high voltage threshold */ + prop->high_thr_en = (low_temp != -INT_MAX); + if (prop->high_thr_en) { + adc_code = qcom_adc_tm5_gen2_temp_res_scale(low_temp); + put_unaligned_le16(adc_code, &buf[10]); + } + + buf[7] = 0; + if (prop->high_thr_en) + buf[7] |= ADC5_GEN3_HIGH_THR_INT_EN; + if (prop->low_thr_en) + buf[7] |= ADC5_GEN3_LOW_THR_INT_EN; + + ret = adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, ADC5_GEN3_SID, + buf, sizeof(buf)); + if (ret < 0) + return ret; + + conv_req = ADC5_GEN3_CONV_REQ_REQ; + return adc5_gen3_write(adc_tm5->dev_data, prop->sdam_index, + ADC5_GEN3_CONV_REQ, &conv_req, sizeof(conv_req)); +} + +static int adc_tm5_gen3_set_trip_temp(struct thermal_zone_device *tz, + int low_temp, int high_temp) +{ + struct adc_tm5_gen3_channel_props *prop = thermal_zone_device_priv(tz); + struct adc_tm5_gen3_chip *adc_tm5; + + if (!prop || !prop->chip) + return -EINVAL; + + adc_tm5 = prop->chip; + + dev_dbg(adc_tm5->dev, "channel:%s, low_temp(mdegC):%d, high_temp(mdegC):%d\n", + prop->common_props.label, low_temp, high_temp); + + guard(adc5_gen3)(adc_tm5); + + return adc_tm5_gen3_configure(prop, low_temp, high_temp); +} + +static const struct thermal_zone_device_ops adc_tm_ops = { + .get_temp = adc_tm5_gen3_get_temp, + .set_trips = adc_tm5_gen3_set_trip_temp, +}; + +static int adc_tm5_register_tzd(struct adc_tm5_gen3_chip *adc_tm5) +{ + struct thermal_zone_device *tzd; + unsigned int channel; + int ret; + + for (int i = 0; i < adc_tm5->nchannels; i++) { + channel = ADC5_GEN3_V_CHAN(adc_tm5->chan_props[i].common_props); + tzd = devm_thermal_of_zone_register(adc_tm5->dev, channel, + &adc_tm5->chan_props[i], + &adc_tm_ops); + if (IS_ERR(tzd)) { + if (PTR_ERR(tzd) == -ENODEV) { + dev_info(adc_tm5->dev, + "thermal sensor on channel %d is not used\n", + channel); + continue; + } + return dev_err_probe(adc_tm5->dev, PTR_ERR(tzd), + "Error registering TZ zone:%ld for channel:%d\n", + PTR_ERR(tzd), channel); + } + adc_tm5->chan_props[i].tzd = tzd; + ret = devm_thermal_add_hwmon_sysfs(adc_tm5->dev, tzd); + if (ret) + return ret; + } + return 0; +} + +static void adc5_gen3_disable(void *data) +{ + struct adc_tm5_gen3_chip *adc_tm5 = data; + + guard(adc5_gen3)(adc_tm5); + /* Disable all available TM channels */ + for (int i = 0; i < adc_tm5->nchannels; i++) + adc_tm5_gen3_disable_channel(&adc_tm5->chan_props[i]); +} + +static int adc_tm5_probe(struct auxiliary_device *aux_dev, + const struct auxiliary_device_id *id) +{ + struct adc_tm5_gen3_chip *adc_tm5; + struct tm5_aux_dev_wrapper *aux_dev_wrapper; + struct device *dev = &aux_dev->dev; + int ret; + + adc_tm5 = devm_kzalloc(dev, sizeof(*adc_tm5), GFP_KERNEL); + if (!adc_tm5) + return -ENOMEM; + + aux_dev_wrapper = container_of(aux_dev, struct tm5_aux_dev_wrapper, + aux_dev); + + adc_tm5->dev = dev; + adc_tm5->dev_data = aux_dev_wrapper->dev_data; + adc_tm5->nchannels = aux_dev_wrapper->n_tm_channels; + adc_tm5->chan_props = devm_kcalloc(dev, aux_dev_wrapper->n_tm_channels, + sizeof(*adc_tm5->chan_props), GFP_KERNEL); + if (!adc_tm5->chan_props) + return -ENOMEM; + + for (int i = 0; i < adc_tm5->nchannels; i++) { + adc_tm5->chan_props[i].common_props = aux_dev_wrapper->tm_props[i]; + adc_tm5->chan_props[i].timer = MEAS_INT_1S; + adc_tm5->chan_props[i].sdam_index = (i + 1) / 8; + adc_tm5->chan_props[i].tm_chan_index = (i + 1) % 8; + adc_tm5->chan_props[i].chip = adc_tm5; + } + + /* This is to disable all ADC_TM channels in case of probe failure. */ + ret = devm_add_action(dev, adc5_gen3_disable, adc_tm5); + if (ret) + return ret; + + /* + * First SDAM's interrupt is shared between main ADC driver + * and auxiliary TM driver, so its flags must include + * IRQF_SHARED. This is not needed for other SDAMs as they + * will be used only for TM functionality. + */ + + ret = devm_request_threaded_irq(dev, + adc_tm5->dev_data->base[0].irq, + adctm5_gen3_isr, adctm5_gen3_isr_thread, + IRQF_ONESHOT | IRQF_SHARED, + adc_tm5->dev_data->base[0].irq_name, + adc_tm5); + if (ret < 0) + return ret; + + for (int i = 1; i < adc_tm5->dev_data->num_sdams; i++) { + ret = devm_request_threaded_irq(dev, + adc_tm5->dev_data->base[i].irq, + adctm5_gen3_isr, adctm5_gen3_isr_thread, + IRQF_ONESHOT, adc_tm5->dev_data->base[i].irq_name, + adc_tm5); + if (ret < 0) + return ret; + } + + return adc_tm5_register_tzd(adc_tm5); +} + +static const struct auxiliary_device_id adctm5_auxiliary_id_table[] = { + { .name = "qcom_spmi_adc5_gen3.adc5_tm_gen3", }, + { } +}; + +MODULE_DEVICE_TABLE(auxiliary, adctm5_auxiliary_id_table); + +static struct auxiliary_driver adctm5gen3_auxiliary_driver = { + .id_table = adctm5_auxiliary_id_table, + .probe = adc_tm5_probe, +}; + +module_auxiliary_driver(adctm5gen3_auxiliary_driver); + +MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("QCOM_SPMI_ADC5_GEN3"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-05-26 10:53 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-15 8:53 [PATCH 0/2] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring Jishnu Prakash 2026-05-15 8:53 ` [PATCH 1/2] iio: adc: qcom-spmi-adc5-gen3: Share SDAM0 IRQ with ADC_TM auxiliary driver Jishnu Prakash 2026-05-15 13:54 ` Jonathan Cameron 2026-05-21 10:46 ` Jishnu Prakash 2026-05-22 10:47 ` Jonathan Cameron 2026-05-26 10:53 ` Jishnu Prakash 2026-05-17 7:08 ` Andy Shevchenko 2026-05-20 15:20 ` Daniel Lezcano 2026-05-15 8:53 ` [PATCH 2/2] thermal: qcom: add support for PMIC5 Gen3 ADC thermal monitoring Jishnu Prakash
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox