From a5410a2c37060d9b9396c5edc53e3bf46d14f703 Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Thu, 12 Jan 2023 12:53:02 +0800 Subject: [PATCH] hwmon: sfctemp: Fix the problem that cannot disable sfctemp after enabling PM [runtime_pm off] After Linux starts, it will enable clk and put tempsensor in standby state. When 'cat /sys/class/hwmon/hwmon0/temp1_input' is executed, will switch to the run state, and return the value of the sensor after the interrupt is generated. $ cat /sys/kernel/debug/clk/clk_summary | grep "temp_sensor" enable prepare protect duty hardware clock count count count rate accuracy phase cycle enable ------------------------------------------------------------------------------------------- u0_temp_sensor_clk_temp 1 1 0 1000000 0 0 50000 Y u0_temp_sensor_clk_apb0 1 1 0 49500000 0 0 50000 Y $ cat /sys/class/hwmon/hwmon0/temp1_input 46135 $ cat /sys/class/hwmon/hwmon0/temp1_enable 1 You can 'echo 0 >/sys/class/hwmon/hwmon0/temp1_enable' to turn off the tempsensor and clk. $ echo 0 > /sys/class/hwmon/hwmon0/temp1_enable $ cat /sys/kernel/debug/clk/clk_summary | grep "temp_sensor" enable prepare protect duty hardware clock count count count rate accuracy phase cycle enable ------------------------------------------------------------------------------------------- u0_temp_sensor_clk_temp 0 0 0 1000000 0 0 50000 N u0_temp_sensor_clk_apb0 0 0 0 49500000 0 0 50000 N $ cat /sys/class/hwmon/hwmon0/temp1_enable 0 $ cat /sys/class/hwmon/hwmon0/temp1_input cat: read error: No data available [runtime_pm on] When runtime_pm is turned on, sfctemp will automatically enter the suspend state and close clk after the system is started. $ cat /sys/kernel/debug/clk/clk_summary | grep "temp_sensor" enable prepare protect duty hardware clock count count count rate accuracy phase cycle enable ------------------------------------------------------------------------------------------- u0_temp_sensor_clk_temp 0 0 0 1000000 0 0 50000 N u0_temp_sensor_clk_apb0 0 0 0 49500000 0 0 50000 N when 'cat /sys/class/hwmon/hwmon0/temp1_input', it will call starfive_temp_resume, enable clk and put sfctemp into power on state. $ cat /sys/class/hwmon/hwmon0/temp1_input 46235 After the interrupt is generated and the sensor value is obtained, it will enter the suspend state again. so, hardware-enable-stats is always 'N'. Of course, in this mode (runtime_pm on), it also supports the opening and closing of functions (in fact, clk is not directly operated, and the opening and closing of clk are controlled by runtime_pm) $ cat /sys/class/hwmon/hwmon0/temp1_input 46235 $ cat /sys/class/hwmon/hwmon0/temp1_enable 1 $ echo 0 > /sys/class/hwmon/hwmon0/temp1_enable $ cat /sys/class/hwmon/hwmon0/temp1_enable 0 $ cat /sys/class/hwmon/hwmon0/temp1_input cat: read error: No data available Signed-off-by: Samin Guo --- drivers/hwmon/sfctemp.c | 86 ++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/hwmon/sfctemp.c b/drivers/hwmon/sfctemp.c index a5d0ac1..5ba934c 100644 --- a/drivers/hwmon/sfctemp.c +++ b/drivers/hwmon/sfctemp.c @@ -53,6 +53,7 @@ struct sfctemp { /* serialize access to hardware register and enabled below */ struct mutex lock; struct completion conversion_done; + struct device *dev; void __iomem *regs; struct clk *clk_sense; struct clk *clk_bus; @@ -96,14 +97,10 @@ static void sfctemp_run_single(struct sfctemp *sfctemp) writel(SFCTEMP_RSTN, sfctemp->regs); } -static int sfctemp_enable(struct sfctemp *sfctemp) +static int sfctemp_clk_enable(struct sfctemp *sfctemp) { int ret = 0; - mutex_lock(&sfctemp->lock); - if (sfctemp->enabled) - goto done; - ret = clk_prepare_enable(sfctemp->clk_bus); if (ret) goto err; @@ -118,12 +115,7 @@ static int sfctemp_enable(struct sfctemp *sfctemp) if (ret) goto err_disable_sense; - sfctemp_power_up(sfctemp); - sfctemp->enabled = true; -done: - mutex_unlock(&sfctemp->lock); return ret; - err_disable_sense: clk_disable_unprepare(sfctemp->clk_sense); err_assert_bus: @@ -135,19 +127,41 @@ err: return ret; } +static int sfctemp_clk_disable(struct sfctemp *sfctemp) +{ + reset_control_assert(sfctemp->rst_sense); + clk_disable_unprepare(sfctemp->clk_sense); + reset_control_assert(sfctemp->rst_bus); + clk_disable_unprepare(sfctemp->clk_bus); + return 0; +} + +static int sfctemp_enable(struct sfctemp *sfctemp) +{ + int ret = 0; + + mutex_lock(&sfctemp->lock); + if (sfctemp->enabled || IS_ENABLED(CONFIG_PM)) + goto done; + + ret = sfctemp_clk_enable(sfctemp); + sfctemp_power_up(sfctemp); +done: + sfctemp->enabled = true; + mutex_unlock(&sfctemp->lock); + return ret; +} + static int sfctemp_disable(struct sfctemp *sfctemp) { mutex_lock(&sfctemp->lock); - if (!sfctemp->enabled) + if (!sfctemp->enabled || IS_ENABLED(CONFIG_PM)) goto done; sfctemp_power_down(sfctemp); - reset_control_assert(sfctemp->rst_sense); - clk_disable_unprepare(sfctemp->clk_sense); - reset_control_assert(sfctemp->rst_bus); - clk_disable_unprepare(sfctemp->clk_bus); - sfctemp->enabled = false; + sfctemp_clk_disable(sfctemp); done: + sfctemp->enabled = false; mutex_unlock(&sfctemp->lock); return 0; } @@ -167,6 +181,7 @@ static int sfctemp_convert(struct sfctemp *sfctemp, long *val) goto out; } + pm_runtime_get_sync(sfctemp->dev); sfctemp_run_single(sfctemp); ret = wait_for_completion_interruptible_timeout(&sfctemp->conversion_done, @@ -182,6 +197,7 @@ static int sfctemp_convert(struct sfctemp *sfctemp, long *val) * SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000; ret = 0; + pm_runtime_put(sfctemp->dev); out: mutex_unlock(&sfctemp->lock); return ret; @@ -208,26 +224,18 @@ static int sfctemp_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct sfctemp *sfctemp = dev_get_drvdata(dev); - int ret; - - ret = pm_runtime_get_sync(dev); switch (type) { case hwmon_temp: switch (attr) { case hwmon_temp_enable: *val = sfctemp->enabled; - pm_runtime_put(dev); return 0; case hwmon_temp_input: - ret = sfctemp_convert(sfctemp, val); - pm_runtime_put(dev); - return ret; + return sfctemp_convert(sfctemp, val); } - pm_runtime_put(dev); return -EINVAL; default: - pm_runtime_put(dev); return -EINVAL; } } @@ -281,6 +289,8 @@ static int sfctemp_probe(struct platform_device *pdev) if (!sfctemp) return -ENOMEM; + sfctemp->dev = dev; + sfctemp->enabled = false; dev_set_drvdata(dev, sfctemp); mutex_init(&sfctemp->lock); init_completion(&sfctemp->conversion_done); @@ -309,14 +319,6 @@ static int sfctemp_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(sfctemp->rst_bus), "error getting busreset\n"); - ret = reset_control_assert(sfctemp->rst_sense); - if (ret) - return dev_err_probe(dev, ret, "error asserting sense reset\n"); - - ret = reset_control_assert(sfctemp->rst_bus); - if (ret) - return dev_err_probe(dev, ret, "error asserting bus reset\n"); - ret = platform_get_irq(pdev, 0); if (ret < 0) return ret; @@ -335,33 +337,31 @@ static int sfctemp_probe(struct platform_device *pdev) hwmon_dev = devm_hwmon_device_register_with_info(dev, pdev->name, sfctemp, &sfctemp_chip_info, NULL); - - pm_runtime_enable(hwmon_dev); pm_runtime_enable(dev); - sfctemp_disable(sfctemp); - return PTR_ERR_OR_ZERO(hwmon_dev); } #ifdef CONFIG_PM - static int starfive_temp_suspend(struct device *dev) { struct sfctemp *sfctemp = dev_get_drvdata(dev); - dev_dbg(dev, "starfive temp runtime suspend"); - - return sfctemp_disable(sfctemp); + dev_dbg(dev, "starfive temp runtime suspend\n"); + sfctemp_power_down(sfctemp); + return sfctemp_clk_disable(sfctemp); } static int starfive_temp_resume(struct device *dev) { struct sfctemp *sfctemp = dev_get_drvdata(dev); + int ret; - dev_dbg(dev, "starfive temp runtime resume"); + dev_dbg(dev, "starfive temp runtime resume\n"); - return sfctemp_enable(sfctemp); + ret = sfctemp_clk_enable(sfctemp); + sfctemp_power_up(sfctemp); + return ret; } #endif /* CONFIG_PM */ -- 2.7.4