linux/drivers/hwmon/pwm-fan.c
<<
>>
Prefs
   1/*
   2 * pwm-fan.c - Hwmon driver for fans connected to PWM lines.
   3 *
   4 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
   5 *
   6 * Author: Kamil Debski <k.debski@samsung.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/hwmon.h>
  20#include <linux/hwmon-sysfs.h>
  21#include <linux/module.h>
  22#include <linux/mutex.h>
  23#include <linux/of.h>
  24#include <linux/platform_device.h>
  25#include <linux/pwm.h>
  26#include <linux/sysfs.h>
  27#include <linux/thermal.h>
  28
  29#define MAX_PWM 255
  30
  31struct pwm_fan_ctx {
  32        struct mutex lock;
  33        struct pwm_device *pwm;
  34        unsigned int pwm_value;
  35        unsigned int pwm_fan_state;
  36        unsigned int pwm_fan_max_state;
  37        unsigned int *pwm_fan_cooling_levels;
  38        struct thermal_cooling_device *cdev;
  39};
  40
  41static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
  42{
  43        unsigned long period;
  44        int ret = 0;
  45        struct pwm_state state = { };
  46
  47        mutex_lock(&ctx->lock);
  48        if (ctx->pwm_value == pwm)
  49                goto exit_set_pwm_err;
  50
  51        pwm_init_state(ctx->pwm, &state);
  52        period = ctx->pwm->args.period;
  53        state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
  54        state.enabled = pwm ? true : false;
  55
  56        ret = pwm_apply_state(ctx->pwm, &state);
  57        if (!ret)
  58                ctx->pwm_value = pwm;
  59exit_set_pwm_err:
  60        mutex_unlock(&ctx->lock);
  61        return ret;
  62}
  63
  64static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
  65{
  66        int i;
  67
  68        for (i = 0; i < ctx->pwm_fan_max_state; ++i)
  69                if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
  70                        break;
  71
  72        ctx->pwm_fan_state = i;
  73}
  74
  75static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
  76                       const char *buf, size_t count)
  77{
  78        struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  79        unsigned long pwm;
  80        int ret;
  81
  82        if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
  83                return -EINVAL;
  84
  85        ret = __set_pwm(ctx, pwm);
  86        if (ret)
  87                return ret;
  88
  89        pwm_fan_update_state(ctx, pwm);
  90        return count;
  91}
  92
  93static ssize_t show_pwm(struct device *dev,
  94                        struct device_attribute *attr, char *buf)
  95{
  96        struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  97
  98        return sprintf(buf, "%u\n", ctx->pwm_value);
  99}
 100
 101
 102static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0);
 103
 104static struct attribute *pwm_fan_attrs[] = {
 105        &sensor_dev_attr_pwm1.dev_attr.attr,
 106        NULL,
 107};
 108
 109ATTRIBUTE_GROUPS(pwm_fan);
 110
 111/* thermal cooling device callbacks */
 112static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
 113                                 unsigned long *state)
 114{
 115        struct pwm_fan_ctx *ctx = cdev->devdata;
 116
 117        if (!ctx)
 118                return -EINVAL;
 119
 120        *state = ctx->pwm_fan_max_state;
 121
 122        return 0;
 123}
 124
 125static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
 126                                 unsigned long *state)
 127{
 128        struct pwm_fan_ctx *ctx = cdev->devdata;
 129
 130        if (!ctx)
 131                return -EINVAL;
 132
 133        *state = ctx->pwm_fan_state;
 134
 135        return 0;
 136}
 137
 138static int
 139pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 140{
 141        struct pwm_fan_ctx *ctx = cdev->devdata;
 142        int ret;
 143
 144        if (!ctx || (state > ctx->pwm_fan_max_state))
 145                return -EINVAL;
 146
 147        if (state == ctx->pwm_fan_state)
 148                return 0;
 149
 150        ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
 151        if (ret) {
 152                dev_err(&cdev->device, "Cannot set pwm!\n");
 153                return ret;
 154        }
 155
 156        ctx->pwm_fan_state = state;
 157
 158        return ret;
 159}
 160
 161static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
 162        .get_max_state = pwm_fan_get_max_state,
 163        .get_cur_state = pwm_fan_get_cur_state,
 164        .set_cur_state = pwm_fan_set_cur_state,
 165};
 166
 167static int pwm_fan_of_get_cooling_data(struct device *dev,
 168                                       struct pwm_fan_ctx *ctx)
 169{
 170        struct device_node *np = dev->of_node;
 171        int num, i, ret;
 172
 173        if (!of_find_property(np, "cooling-levels", NULL))
 174                return 0;
 175
 176        ret = of_property_count_u32_elems(np, "cooling-levels");
 177        if (ret <= 0) {
 178                dev_err(dev, "Wrong data!\n");
 179                return ret ? : -EINVAL;
 180        }
 181
 182        num = ret;
 183        ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
 184                                                   GFP_KERNEL);
 185        if (!ctx->pwm_fan_cooling_levels)
 186                return -ENOMEM;
 187
 188        ret = of_property_read_u32_array(np, "cooling-levels",
 189                                         ctx->pwm_fan_cooling_levels, num);
 190        if (ret) {
 191                dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
 192                return ret;
 193        }
 194
 195        for (i = 0; i < num; i++) {
 196                if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
 197                        dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
 198                                ctx->pwm_fan_cooling_levels[i], MAX_PWM);
 199                        return -EINVAL;
 200                }
 201        }
 202
 203        ctx->pwm_fan_max_state = num - 1;
 204
 205        return 0;
 206}
 207
 208static int pwm_fan_probe(struct platform_device *pdev)
 209{
 210        struct thermal_cooling_device *cdev;
 211        struct pwm_fan_ctx *ctx;
 212        struct device *hwmon;
 213        int ret;
 214        struct pwm_state state = { };
 215
 216        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 217        if (!ctx)
 218                return -ENOMEM;
 219
 220        mutex_init(&ctx->lock);
 221
 222        ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL);
 223        if (IS_ERR(ctx->pwm)) {
 224                dev_err(&pdev->dev, "Could not get PWM\n");
 225                return PTR_ERR(ctx->pwm);
 226        }
 227
 228        platform_set_drvdata(pdev, ctx);
 229
 230        ctx->pwm_value = MAX_PWM;
 231
 232        /* Set duty cycle to maximum allowed and enable PWM output */
 233        pwm_init_state(ctx->pwm, &state);
 234        state.duty_cycle = ctx->pwm->args.period - 1;
 235        state.enabled = true;
 236
 237        ret = pwm_apply_state(ctx->pwm, &state);
 238        if (ret) {
 239                dev_err(&pdev->dev, "Failed to configure PWM\n");
 240                return ret;
 241        }
 242
 243        hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan",
 244                                                       ctx, pwm_fan_groups);
 245        if (IS_ERR(hwmon)) {
 246                dev_err(&pdev->dev, "Failed to register hwmon device\n");
 247                ret = PTR_ERR(hwmon);
 248                goto err_pwm_disable;
 249        }
 250
 251        ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx);
 252        if (ret)
 253                return ret;
 254
 255        ctx->pwm_fan_state = ctx->pwm_fan_max_state;
 256        if (IS_ENABLED(CONFIG_THERMAL)) {
 257                cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
 258                                                          "pwm-fan", ctx,
 259                                                          &pwm_fan_cooling_ops);
 260                if (IS_ERR(cdev)) {
 261                        dev_err(&pdev->dev,
 262                                "Failed to register pwm-fan as cooling device");
 263                        ret = PTR_ERR(cdev);
 264                        goto err_pwm_disable;
 265                }
 266                ctx->cdev = cdev;
 267                thermal_cdev_update(cdev);
 268        }
 269
 270        return 0;
 271
 272err_pwm_disable:
 273        state.enabled = false;
 274        pwm_apply_state(ctx->pwm, &state);
 275
 276        return ret;
 277}
 278
 279static int pwm_fan_remove(struct platform_device *pdev)
 280{
 281        struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
 282
 283        thermal_cooling_device_unregister(ctx->cdev);
 284        if (ctx->pwm_value)
 285                pwm_disable(ctx->pwm);
 286        return 0;
 287}
 288
 289#ifdef CONFIG_PM_SLEEP
 290static int pwm_fan_suspend(struct device *dev)
 291{
 292        struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
 293
 294        if (ctx->pwm_value)
 295                pwm_disable(ctx->pwm);
 296        return 0;
 297}
 298
 299static int pwm_fan_resume(struct device *dev)
 300{
 301        struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
 302        struct pwm_args pargs;
 303        unsigned long duty;
 304        int ret;
 305
 306        if (ctx->pwm_value == 0)
 307                return 0;
 308
 309        pwm_get_args(ctx->pwm, &pargs);
 310        duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
 311        ret = pwm_config(ctx->pwm, duty, pargs.period);
 312        if (ret)
 313                return ret;
 314        return pwm_enable(ctx->pwm);
 315}
 316#endif
 317
 318static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume);
 319
 320static const struct of_device_id of_pwm_fan_match[] = {
 321        { .compatible = "pwm-fan", },
 322        {},
 323};
 324MODULE_DEVICE_TABLE(of, of_pwm_fan_match);
 325
 326static struct platform_driver pwm_fan_driver = {
 327        .probe          = pwm_fan_probe,
 328        .remove         = pwm_fan_remove,
 329        .driver = {
 330                .name           = "pwm-fan",
 331                .pm             = &pwm_fan_pm,
 332                .of_match_table = of_pwm_fan_match,
 333        },
 334};
 335
 336module_platform_driver(pwm_fan_driver);
 337
 338MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
 339MODULE_ALIAS("platform:pwm-fan");
 340MODULE_DESCRIPTION("PWM FAN driver");
 341MODULE_LICENSE("GPL");
 342