hwmon: sfctemp: Fix the problem that cannot disable sfctemp after enabling PM
authorSamin Guo <samin.guo@starfivetech.com>
Thu, 12 Jan 2023 04:53:02 +0000 (12:53 +0800)
committerHal Feng <hal.feng@starfivetech.com>
Wed, 18 Jan 2023 01:37:13 +0000 (09:37 +0800)
[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 <samin.guo@starfivetech.com>
drivers/hwmon/sfctemp.c

index a5d0ac1..5ba934c 100644 (file)
@@ -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 */