]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
hwmon: (gpio-fan) Add regulator support
authorAlexander Stein <alexander.stein@ew.tq-group.com>
Wed, 9 Apr 2025 06:54:26 +0000 (08:54 +0200)
committerGuenter Roeck <linux@roeck-us.net>
Wed, 23 Apr 2025 14:18:27 +0000 (07:18 -0700)
FANs might be supplied by a regulator which needs to be enabled as well.
This is implemented using runtime PM. Every time speed_index changes from
0 to non-zero and vise versa RPM is resumed or suspended.
Intitial RPM state is determined by initial value of speed_index.

Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
Link: https://lore.kernel.org/r/20250409065430.1413439-1-alexander.stein@ew.tq-group.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
drivers/hwmon/gpio-fan.c

index b779240328d59f37c3a4c8a7e3fe4c0e631d4f3a..516c34bb61c9cfa2927d31ee6459c8306be2fb5b 100644 (file)
@@ -20,6 +20,9 @@
 #include <linux/gpio/consumer.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
 #include <linux/thermal.h>
 
 struct gpio_fan_speed {
@@ -42,6 +45,7 @@ struct gpio_fan_data {
        bool                    pwm_enable;
        struct gpio_desc        *alarm_gpio;
        struct work_struct      alarm_work;
+       struct regulator        *supply;
 };
 
 /*
@@ -125,13 +129,32 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
 }
 
 /* Must be called with fan_data->lock held, except during initialization. */
-static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
+static int set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
 {
        if (fan_data->speed_index == speed_index)
-               return;
+               return 0;
+
+       if (fan_data->speed_index == 0 && speed_index > 0) {
+               int ret;
+
+               ret = pm_runtime_resume_and_get(fan_data->dev);
+               if (ret < 0)
+                       return ret;
+       }
 
        __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
+
+       if (fan_data->speed_index > 0 && speed_index == 0) {
+               int ret;
+
+               ret = pm_runtime_put_sync(fan_data->dev);
+               if (ret < 0)
+                       return ret;
+       }
+
        fan_data->speed_index = speed_index;
+
+       return 0;
 }
 
 static int get_fan_speed_index(struct gpio_fan_data *fan_data)
@@ -176,7 +199,7 @@ static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
        struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
        unsigned long pwm;
        int speed_index;
-       int ret = count;
+       int ret;
 
        if (kstrtoul(buf, 10, &pwm) || pwm > 255)
                return -EINVAL;
@@ -189,12 +212,12 @@ static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
        }
 
        speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
-       set_fan_speed(fan_data, speed_index);
+       ret = set_fan_speed(fan_data, speed_index);
 
 exit_unlock:
        mutex_unlock(&fan_data->lock);
 
-       return ret;
+       return ret ? ret : count;
 }
 
 static ssize_t pwm1_enable_show(struct device *dev,
@@ -211,6 +234,7 @@ static ssize_t pwm1_enable_store(struct device *dev,
 {
        struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
        unsigned long val;
+       int ret = 0;
 
        if (kstrtoul(buf, 10, &val) || val > 1)
                return -EINVAL;
@@ -224,11 +248,11 @@ static ssize_t pwm1_enable_store(struct device *dev,
 
        /* Disable manual control mode: set fan at full speed. */
        if (val == 0)
-               set_fan_speed(fan_data, fan_data->num_speed - 1);
+               ret = set_fan_speed(fan_data, fan_data->num_speed - 1);
 
        mutex_unlock(&fan_data->lock);
 
-       return count;
+       return ret ? ret : count;
 }
 
 static ssize_t pwm1_mode_show(struct device *dev,
@@ -279,7 +303,7 @@ static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
                goto exit_unlock;
        }
 
-       set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
+       ret = set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
 
 exit_unlock:
        mutex_unlock(&fan_data->lock);
@@ -386,6 +410,7 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
                                  unsigned long state)
 {
        struct gpio_fan_data *fan_data = cdev->devdata;
+       int ret;
 
        if (!fan_data)
                return -EINVAL;
@@ -395,11 +420,11 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
 
        mutex_lock(&fan_data->lock);
 
-       set_fan_speed(fan_data, state);
+       ret = set_fan_speed(fan_data, state);
 
        mutex_unlock(&fan_data->lock);
 
-       return 0;
+       return ret;
 }
 
 static const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
@@ -499,6 +524,8 @@ static void gpio_fan_stop(void *data)
        mutex_lock(&fan_data->lock);
        set_fan_speed(data, 0);
        mutex_unlock(&fan_data->lock);
+
+       pm_runtime_disable(fan_data->dev);
 }
 
 static int gpio_fan_probe(struct platform_device *pdev)
@@ -521,6 +548,11 @@ static int gpio_fan_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, fan_data);
        mutex_init(&fan_data->lock);
 
+       fan_data->supply = devm_regulator_get(dev, "fan");
+       if (IS_ERR(fan_data->supply))
+               return dev_err_probe(dev, PTR_ERR(fan_data->supply),
+                                    "Failed to get fan-supply");
+
        /* Configure control GPIOs if available. */
        if (fan_data->gpios && fan_data->num_gpios > 0) {
                if (!fan_data->speed || fan_data->num_speed <= 1)
@@ -548,6 +580,17 @@ static int gpio_fan_probe(struct platform_device *pdev)
                        return err;
        }
 
+       pm_runtime_set_suspended(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+       /* If current GPIO state is active, mark RPM as active as well */
+       if (fan_data->speed_index > 0) {
+               int ret;
+
+               ret = pm_runtime_resume_and_get(&pdev->dev);
+               if (ret)
+                       return ret;
+       }
+
        /* Optional cooling device register for Device tree platforms */
        fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np,
                                "gpio-fan", fan_data, &gpio_fan_cool_ops);
@@ -565,41 +608,69 @@ static void gpio_fan_shutdown(struct platform_device *pdev)
                set_fan_speed(fan_data, 0);
 }
 
+static int gpio_fan_runtime_suspend(struct device *dev)
+{
+       struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (fan_data->supply)
+               ret = regulator_disable(fan_data->supply);
+
+       return ret;
+}
+
+static int gpio_fan_runtime_resume(struct device *dev)
+{
+       struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (fan_data->supply)
+               ret = regulator_enable(fan_data->supply);
+
+       return ret;
+}
+
 static int gpio_fan_suspend(struct device *dev)
 {
        struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+       int ret = 0;
 
        if (fan_data->gpios) {
                fan_data->resume_speed = fan_data->speed_index;
                mutex_lock(&fan_data->lock);
-               set_fan_speed(fan_data, 0);
+               ret = set_fan_speed(fan_data, 0);
                mutex_unlock(&fan_data->lock);
        }
 
-       return 0;
+       return ret;
 }
 
 static int gpio_fan_resume(struct device *dev)
 {
        struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+       int ret = 0;
 
        if (fan_data->gpios) {
                mutex_lock(&fan_data->lock);
-               set_fan_speed(fan_data, fan_data->resume_speed);
+               ret = set_fan_speed(fan_data, fan_data->resume_speed);
                mutex_unlock(&fan_data->lock);
        }
 
-       return 0;
+       return ret;
 }
 
-static DEFINE_SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
+static const struct dev_pm_ops gpio_fan_pm = {
+       RUNTIME_PM_OPS(gpio_fan_runtime_suspend,
+                      gpio_fan_runtime_resume, NULL)
+       SYSTEM_SLEEP_PM_OPS(gpio_fan_suspend, gpio_fan_resume)
+};
 
 static struct platform_driver gpio_fan_driver = {
        .probe          = gpio_fan_probe,
        .shutdown       = gpio_fan_shutdown,
        .driver = {
                .name   = "gpio-fan",
-               .pm     = pm_sleep_ptr(&gpio_fan_pm),
+               .pm     = pm_ptr(&gpio_fan_pm),
                .of_match_table = of_gpio_fan_match,
        },
 };