[PATCH] thermal: spacemit: k1: disable hardware on driver remove

Shuwei Wu shuwei.wu at mailbox.org
Wed Jul 15 21:39:41 PDT 2026


Hi Pei,

On Wed Jul 15, 2026 at 11:11 AM CST, Pei Xiao wrote:
>
>
> 在 2026/7/14 18:36, Shuwei Wu 写道:
>> Hi Pei,
>>
>> On Mon Jul 13, 2026 at 4:32 PM CST, Pei Xiao wrote:
>>> The driver uses devm_request_threaded_irq() but never disables the
>>> hardware interrupt enable, sensor enable, or sensor power bits when
>>> the driver is unbound.
>>>
>>> Add a devm action that clears K1_TSENSOR_INT_EN_REG,
>>> K1_TSENSOR_EN_ALL, and K1_TSENSOR_PCTRL_ENABLE on remove, mirroring
>>> what k1_tsensor_init() sets up.
>>>
>>> Signed-off-by: Pei Xiao <xiaopei01 at kylinos.cn>
>>> ---
>>>  drivers/thermal/spacemit/k1_tsensor.c | 27 +++++++++++++++++++++++++++
>>>  1 file changed, 27 insertions(+)
>>>
>>> diff --git a/drivers/thermal/spacemit/k1_tsensor.c b/drivers/thermal/spacemit/k1_tsensor.c
>>> index 79222d233129..cf79dc7d403f 100644
>>> --- a/drivers/thermal/spacemit/k1_tsensor.c
>>> +++ b/drivers/thermal/spacemit/k1_tsensor.c
>>> @@ -199,6 +199,29 @@ static irqreturn_t k1_tsensor_irq_thread(int irq, void *data)
>>>  	return IRQ_HANDLED;
>>>  }
>>>  
>>> +static void k1_tsensor_shutdown(struct k1_tsensor *ts)
>>> +{
>>> +	u32 val;
>>> +
>>> +	/* Disable all interrupts */
>>> +	writel(0xffffffff, ts->base + K1_TSENSOR_INT_EN_REG);
>>> +
>>> +	/* Disable all sensor */
>>> +	val = readl(ts->base + K1_TSENSOR_EN_REG);
>>> +	val &= ~K1_TSENSOR_EN_ALL;
>>> +	writel(val, ts->base + K1_TSENSOR_EN_REG);
>>> +
>>> +	/* Power down the sensor module */
>>> +	val = readl(ts->base + K1_TSENSOR_PCTRL_REG);
>>> +	val &= ~K1_TSENSOR_PCTRL_ENABLE;
>>> +	writel(val, ts->base + K1_TSENSOR_PCTRL_REG);
>>> +}
>>> +
>>> +static void devm_k1_tsensor_shutdown(void *data)
>>> +{
>>> +	k1_tsensor_shutdown(data);
>>> +}
>>> +
>>>  static int k1_tsensor_probe(struct platform_device *pdev)
>>>  {
>>>  	struct device *dev = &pdev->dev;
>>> @@ -229,6 +252,10 @@ static int k1_tsensor_probe(struct platform_device *pdev)
>>>  
>>>  	k1_tsensor_init(ts);
>>>  
>>> +	ret = devm_add_action_or_reset(dev, devm_k1_tsensor_shutdown, ts);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>>  	irq = platform_get_irq(pdev, 0);
>>>  	if (irq < 0)
>>>  		return irq;
>> Sorry, I missed the hardware cleanup in the original driver.
>> Thanks for fixing it.
>>
>> I also found that registering the shutdown action immediately after
>> k1_tsensor_init() would cause the thermal zones to be released before
>> the IRQ during devres cleanup. The IRQ thread may then access an
>> unregistered thermal zone.
>>
>> I changed the order so that the thermal zones are registered first,
>> followed by the IRQ and the shutdown action.
>> On removal, the hardware interrupt is disabled first, then the IRQ
>> is released, and finally the thermal zones are released.
> I notice that the changes you've made are significantly more extensive
> than my original patch.
>  Could you please let me know whether you plan to submit this patch
> yourself, 
> or whether you would like me to send it? If you would prefer me to
> submit it, 
> I would be delighted to contribute to the community.

Thank you for your reply. Since the original cleanup issue was
identified by you, I would be happy for you to submit the update patch.

>
> Thanks!
> Pei.
>
>> -> % git --no-pager diff drivers/thermal/spacemit/k1_tsensor.c
>> diff --git a/drivers/thermal/spacemit/k1_tsensor.c b/drivers/thermal/spacemit/k1_tsensor.c
>> index 79222d233129..57dbc652a405 100644
>> --- a/drivers/thermal/spacemit/k1_tsensor.c
>> +++ b/drivers/thermal/spacemit/k1_tsensor.c
>> @@ -199,6 +199,32 @@ static irqreturn_t k1_tsensor_irq_thread(int irq, void *data)
>>  	return IRQ_HANDLED;
>>  }
>>
>> +static void k1_tsensor_shutdown(struct k1_tsensor *ts)
>> +{
>> +	u32 val;
>> +
>> +	/* Disable all interrupts */
>> +	writel(0xffffffff, ts->base + K1_TSENSOR_INT_EN_REG);
>> +
>> +	/* Disable all sensors */
>> +	val = readl(ts->base + K1_TSENSOR_EN_REG);
>> +	val &= ~K1_TSENSOR_EN_ALL;
>> +	writel(val, ts->base + K1_TSENSOR_EN_REG);
>> +
>> +	/* Clear the sampling configuration set by k1_tsensor_init(). */
>> +	val = readl(ts->base + K1_TSENSOR_TIME_REG);
>> +	val &= ~(K1_TSENSOR_TIME_FILTER_PERIOD |
>> +		 K1_TSENSOR_TIME_ADC_CNT_RST |
>> +		 K1_TSENSOR_TIME_WAIT_REF_CNT);
>> +	writel(val, ts->base + K1_TSENSOR_TIME_REG);
>> +
>> +	/* Clear the control bits configured by k1_tsensor_init(). */
>> +	val = readl(ts->base + K1_TSENSOR_PCTRL_REG);
>> +	val &= ~(K1_TSENSOR_PCTRL_RAW_SEL |
>> +		 K1_TSENSOR_PCTRL_TEMP_MODE |
>> +		 K1_TSENSOR_PCTRL_HW_AUTO_MODE |
>> +		 K1_TSENSOR_PCTRL_ENABLE);
>> +	writel(val, ts->base + K1_TSENSOR_PCTRL_REG);
>> +}
>> +
>> +static void devm_k1_tsensor_shutdown(void *data)
>> +{
>> +	k1_tsensor_shutdown(data);
>> +}
>> +
>>  static int k1_tsensor_probe(struct platform_device *pdev)
>>  {
>>  	struct device *dev = &pdev->dev;
>> @@ -229,34 +255,48 @@ static int k1_tsensor_probe(struct platform_device *pdev)
>>
>>  	k1_tsensor_init(ts);
>>
>> -	irq = platform_get_irq(pdev, 0);
>> -	if (irq < 0)
>> -		return irq;
>> -
>> -	ret = devm_request_threaded_irq(dev, irq, NULL,
>> -					k1_tsensor_irq_thread,
>> -					IRQF_ONESHOT, "k1_tsensor", ts);
>> -	if (ret < 0)
>> -		return ret;
>> -
>>  	for (i = 0; i < MAX_SENSOR_NUMBER; ++i) {
>>  		ts->ch[i].id = i;
>>  		ts->ch[i].ts = ts;
>>  		ts->ch[i].tzd = devm_thermal_of_zone_register(dev, i, ts->ch + i, &k1_tsensor_ops);
>> -		if (IS_ERR(ts->ch[i].tzd))
>> -			return PTR_ERR(ts->ch[i].tzd);
>> +		if (IS_ERR(ts->ch[i].tzd)) {
>> +			ret = PTR_ERR(ts->ch[i].tzd);
>> +			goto err_shutdown;
>> +		}
>>
>>  		/* Attach sysfs hwmon attributes for userspace monitoring */
>>  		ret = devm_thermal_add_hwmon_sysfs(dev, ts->ch[i].tzd);
>>  		if (ret)
>>  			dev_warn(dev, "Failed to add hwmon sysfs attributes\n");
>> +	}
>>
>> -		k1_tsensor_enable_irq(ts->ch + i);
>> +	ret = platform_get_irq(pdev, 0);
>> +	if (ret < 0) {
>> +		goto err_shutdown;
>>  	}
> irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = irq; goto
> err_shutdown; }    

agreed.

>
>>
>> +	ret = devm_request_threaded_irq(dev, irq, NULL,
>> +					k1_tsensor_irq_thread,
>> +					IRQF_ONESHOT, "k1_tsensor", ts);
>> +	if (ret < 0)
>> +		goto err_shutdown;
>> +
>> +	ret = devm_add_action_or_reset(dev, devm_k1_tsensor_shutdown, ts);
> *Do we need to rename |devm_k1_tsensor_shutdown| to a function name 
> without the |devm| prefix? I feel the |devm| prefix is a bit redundant.

yes, k1_tsensor_shutdown_action() would be a clearer name.

>
> *
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Enable interrupts only after all zones and the handler are ready. */
>> +	for (i = 0; i < MAX_SENSOR_NUMBER; ++i)
>> +		k1_tsensor_enable_irq(ts->ch + i);
>> +
>>  	platform_set_drvdata(pdev, ts);
>>
>>  	return 0;
>> +
>> +err_shutdown:
>> +	k1_tsensor_shutdown(ts);
>> +	return ret;
>>  }
>>
>>  static const struct of_device_id k1_tsensor_dt_ids[] = {
>>

-- 
Best regards,
Shuwei Wu



More information about the linux-riscv mailing list