linux/drivers/pwm/sysfs.c
<<
>>
Prefs
   1/*
   2 * A simple sysfs interface for the generic PWM framework
   3 *
   4 * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com>
   5 *
   6 * Based on previous work by Lars Poeschel <poeschel@lemonage.de>
   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, or (at your option)
  11 * 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/device.h>
  20#include <linux/mutex.h>
  21#include <linux/err.h>
  22#include <linux/slab.h>
  23#include <linux/kdev_t.h>
  24#include <linux/pwm.h>
  25
  26struct pwm_export {
  27        struct device child;
  28        struct pwm_device *pwm;
  29};
  30
  31static struct pwm_export *child_to_pwm_export(struct device *child)
  32{
  33        return container_of(child, struct pwm_export, child);
  34}
  35
  36static struct pwm_device *child_to_pwm_device(struct device *child)
  37{
  38        struct pwm_export *export = child_to_pwm_export(child);
  39
  40        return export->pwm;
  41}
  42
  43static ssize_t pwm_period_show(struct device *child,
  44                               struct device_attribute *attr,
  45                               char *buf)
  46{
  47        const struct pwm_device *pwm = child_to_pwm_device(child);
  48
  49        return sprintf(buf, "%u\n", pwm->period);
  50}
  51
  52static ssize_t pwm_period_store(struct device *child,
  53                                struct device_attribute *attr,
  54                                const char *buf, size_t size)
  55{
  56        struct pwm_device *pwm = child_to_pwm_device(child);
  57        unsigned int val;
  58        int ret;
  59
  60        ret = kstrtouint(buf, 0, &val);
  61        if (ret)
  62                return ret;
  63
  64        ret = pwm_config(pwm, pwm->duty_cycle, val);
  65
  66        return ret ? : size;
  67}
  68
  69static ssize_t pwm_duty_cycle_show(struct device *child,
  70                                   struct device_attribute *attr,
  71                                   char *buf)
  72{
  73        const struct pwm_device *pwm = child_to_pwm_device(child);
  74
  75        return sprintf(buf, "%u\n", pwm->duty_cycle);
  76}
  77
  78static ssize_t pwm_duty_cycle_store(struct device *child,
  79                                    struct device_attribute *attr,
  80                                    const char *buf, size_t size)
  81{
  82        struct pwm_device *pwm = child_to_pwm_device(child);
  83        unsigned int val;
  84        int ret;
  85
  86        ret = kstrtouint(buf, 0, &val);
  87        if (ret)
  88                return ret;
  89
  90        ret = pwm_config(pwm, val, pwm->period);
  91
  92        return ret ? : size;
  93}
  94
  95static ssize_t pwm_enable_show(struct device *child,
  96                               struct device_attribute *attr,
  97                               char *buf)
  98{
  99        const struct pwm_device *pwm = child_to_pwm_device(child);
 100        int enabled = test_bit(PWMF_ENABLED, &pwm->flags);
 101
 102        return sprintf(buf, "%d\n", enabled);
 103}
 104
 105static ssize_t pwm_enable_store(struct device *child,
 106                                struct device_attribute *attr,
 107                                const char *buf, size_t size)
 108{
 109        struct pwm_device *pwm = child_to_pwm_device(child);
 110        int val, ret;
 111
 112        ret = kstrtoint(buf, 0, &val);
 113        if (ret)
 114                return ret;
 115
 116        switch (val) {
 117        case 0:
 118                pwm_disable(pwm);
 119                break;
 120        case 1:
 121                ret = pwm_enable(pwm);
 122                break;
 123        default:
 124                ret = -EINVAL;
 125                break;
 126        }
 127
 128        return ret ? : size;
 129}
 130
 131static ssize_t pwm_polarity_show(struct device *child,
 132                                 struct device_attribute *attr,
 133                                 char *buf)
 134{
 135        const struct pwm_device *pwm = child_to_pwm_device(child);
 136
 137        return sprintf(buf, "%s\n", pwm->polarity ? "inversed" : "normal");
 138}
 139
 140static ssize_t pwm_polarity_store(struct device *child,
 141                                  struct device_attribute *attr,
 142                                  const char *buf, size_t size)
 143{
 144        struct pwm_device *pwm = child_to_pwm_device(child);
 145        enum pwm_polarity polarity;
 146        int ret;
 147
 148        if (sysfs_streq(buf, "normal"))
 149                polarity = PWM_POLARITY_NORMAL;
 150        else if (sysfs_streq(buf, "inversed"))
 151                polarity = PWM_POLARITY_INVERSED;
 152        else
 153                return -EINVAL;
 154
 155        ret = pwm_set_polarity(pwm, polarity);
 156
 157        return ret ? : size;
 158}
 159
 160static DEVICE_ATTR(period, 0644, pwm_period_show, pwm_period_store);
 161static DEVICE_ATTR(duty_cycle, 0644, pwm_duty_cycle_show, pwm_duty_cycle_store);
 162static DEVICE_ATTR(enable, 0644, pwm_enable_show, pwm_enable_store);
 163static DEVICE_ATTR(polarity, 0644, pwm_polarity_show, pwm_polarity_store);
 164
 165static struct attribute *pwm_attrs[] = {
 166        &dev_attr_period.attr,
 167        &dev_attr_duty_cycle.attr,
 168        &dev_attr_enable.attr,
 169        &dev_attr_polarity.attr,
 170        NULL
 171};
 172
 173static const struct attribute_group pwm_attr_group = {
 174        .attrs          = pwm_attrs,
 175};
 176
 177static const struct attribute_group *pwm_attr_groups[] = {
 178        &pwm_attr_group,
 179        NULL,
 180};
 181
 182static void pwm_export_release(struct device *child)
 183{
 184        struct pwm_export *export = child_to_pwm_export(child);
 185
 186        kfree(export);
 187}
 188
 189static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
 190{
 191        struct pwm_export *export;
 192        int ret;
 193
 194        if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags))
 195                return -EBUSY;
 196
 197        export = kzalloc(sizeof(*export), GFP_KERNEL);
 198        if (!export) {
 199                clear_bit(PWMF_EXPORTED, &pwm->flags);
 200                return -ENOMEM;
 201        }
 202
 203        export->pwm = pwm;
 204
 205        export->child.release = pwm_export_release;
 206        export->child.parent = parent;
 207        export->child.devt = MKDEV(0, 0);
 208        export->child.groups = pwm_attr_groups;
 209        dev_set_name(&export->child, "pwm%u", pwm->hwpwm);
 210
 211        ret = device_register(&export->child);
 212        if (ret) {
 213                clear_bit(PWMF_EXPORTED, &pwm->flags);
 214                kfree(export);
 215                return ret;
 216        }
 217
 218        return 0;
 219}
 220
 221static int pwm_unexport_match(struct device *child, void *data)
 222{
 223        return child_to_pwm_device(child) == data;
 224}
 225
 226static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm)
 227{
 228        struct device *child;
 229
 230        if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
 231                return -ENODEV;
 232
 233        child = device_find_child(parent, pwm, pwm_unexport_match);
 234        if (!child)
 235                return -ENODEV;
 236
 237        /* for device_find_child() */
 238        put_device(child);
 239        device_unregister(child);
 240        pwm_put(pwm);
 241
 242        return 0;
 243}
 244
 245static ssize_t pwm_export_store(struct device *parent,
 246                                struct device_attribute *attr,
 247                                const char *buf, size_t len)
 248{
 249        struct pwm_chip *chip = dev_get_drvdata(parent);
 250        struct pwm_device *pwm;
 251        unsigned int hwpwm;
 252        int ret;
 253
 254        ret = kstrtouint(buf, 0, &hwpwm);
 255        if (ret < 0)
 256                return ret;
 257
 258        if (hwpwm >= chip->npwm)
 259                return -ENODEV;
 260
 261        pwm = pwm_request_from_chip(chip, hwpwm, "sysfs");
 262        if (IS_ERR(pwm))
 263                return PTR_ERR(pwm);
 264
 265        ret = pwm_export_child(parent, pwm);
 266        if (ret < 0)
 267                pwm_put(pwm);
 268
 269        return ret ? : len;
 270}
 271
 272static ssize_t pwm_unexport_store(struct device *parent,
 273                                  struct device_attribute *attr,
 274                                  const char *buf, size_t len)
 275{
 276        struct pwm_chip *chip = dev_get_drvdata(parent);
 277        unsigned int hwpwm;
 278        int ret;
 279
 280        ret = kstrtouint(buf, 0, &hwpwm);
 281        if (ret < 0)
 282                return ret;
 283
 284        if (hwpwm >= chip->npwm)
 285                return -ENODEV;
 286
 287        ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]);
 288
 289        return ret ? : len;
 290}
 291
 292static ssize_t pwm_npwm_show(struct device *parent,
 293                             struct device_attribute *attr,
 294                             char *buf)
 295{
 296        const struct pwm_chip *chip = dev_get_drvdata(parent);
 297
 298        return sprintf(buf, "%u\n", chip->npwm);
 299}
 300
 301static struct device_attribute pwm_chip_attrs[] = {
 302        __ATTR(export, 0200, NULL, pwm_export_store),
 303        __ATTR(unexport, 0200, NULL, pwm_unexport_store),
 304        __ATTR(npwm, 0444, pwm_npwm_show, NULL),
 305        __ATTR_NULL,
 306};
 307
 308static struct class pwm_class = {
 309        .name           = "pwm",
 310        .owner          = THIS_MODULE,
 311        .dev_attrs      = pwm_chip_attrs,
 312};
 313
 314static int pwmchip_sysfs_match(struct device *parent, const void *data)
 315{
 316        return dev_get_drvdata(parent) == data;
 317}
 318
 319void pwmchip_sysfs_export(struct pwm_chip *chip)
 320{
 321        struct device *parent;
 322
 323        /*
 324         * If device_create() fails the pwm_chip is still usable by
 325         * the kernel its just not exported.
 326         */
 327        parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
 328                               "pwmchip%d", chip->base);
 329        if (IS_ERR(parent)) {
 330                dev_warn(chip->dev,
 331                         "device_create failed for pwm_chip sysfs export\n");
 332        }
 333}
 334
 335void pwmchip_sysfs_unexport(struct pwm_chip *chip)
 336{
 337        struct device *parent;
 338
 339        parent = class_find_device(&pwm_class, NULL, chip,
 340                                   pwmchip_sysfs_match);
 341        if (parent) {
 342                /* for class_find_device() */
 343                put_device(parent);
 344                device_unregister(parent);
 345        }
 346}
 347
 348static int __init pwm_sysfs_init(void)
 349{
 350        return class_register(&pwm_class);
 351}
 352subsys_initcall(pwm_sysfs_init);
 353
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.