123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * A simple sysfs interface for the generic PWM framework
- *
- * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com>
- *
- * Based on previous work by Lars Poeschel <poeschel@lemonage.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/device.h>
- #include <linux/mutex.h>
- #include <linux/err.h>
- #include <linux/slab.h>
- #include <linux/kdev_t.h>
- #include <linux/pwm.h>
- struct pwm_export {
- struct device child;
- struct pwm_device *pwm;
- struct mutex lock;
- };
- static struct pwm_export *child_to_pwm_export(struct device *child)
- {
- return container_of(child, struct pwm_export, child);
- }
- static struct pwm_device *child_to_pwm_device(struct device *child)
- {
- struct pwm_export *export = child_to_pwm_export(child);
- return export->pwm;
- }
- static ssize_t period_show(struct device *child,
- struct device_attribute *attr,
- char *buf)
- {
- const struct pwm_device *pwm = child_to_pwm_device(child);
- struct pwm_state state;
- pwm_get_state(pwm, &state);
- return sprintf(buf, "%u\n", state.period);
- }
- static ssize_t period_store(struct device *child,
- struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct pwm_export *export = child_to_pwm_export(child);
- struct pwm_device *pwm = export->pwm;
- struct pwm_state state;
- unsigned int val;
- int ret;
- ret = kstrtouint(buf, 0, &val);
- if (ret)
- return ret;
- mutex_lock(&export->lock);
- pwm_get_state(pwm, &state);
- state.period = val;
- ret = pwm_apply_state(pwm, &state);
- mutex_unlock(&export->lock);
- return ret ? : size;
- }
- static ssize_t duty_cycle_show(struct device *child,
- struct device_attribute *attr,
- char *buf)
- {
- const struct pwm_device *pwm = child_to_pwm_device(child);
- struct pwm_state state;
- pwm_get_state(pwm, &state);
- return sprintf(buf, "%u\n", state.duty_cycle);
- }
- static ssize_t duty_cycle_store(struct device *child,
- struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct pwm_export *export = child_to_pwm_export(child);
- struct pwm_device *pwm = export->pwm;
- struct pwm_state state;
- unsigned int val;
- int ret;
- ret = kstrtouint(buf, 0, &val);
- if (ret)
- return ret;
- mutex_lock(&export->lock);
- pwm_get_state(pwm, &state);
- state.duty_cycle = val;
- ret = pwm_apply_state(pwm, &state);
- mutex_unlock(&export->lock);
- return ret ? : size;
- }
- static ssize_t enable_show(struct device *child,
- struct device_attribute *attr,
- char *buf)
- {
- const struct pwm_device *pwm = child_to_pwm_device(child);
- struct pwm_state state;
- pwm_get_state(pwm, &state);
- return sprintf(buf, "%d\n", state.enabled);
- }
- static ssize_t enable_store(struct device *child,
- struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct pwm_export *export = child_to_pwm_export(child);
- struct pwm_device *pwm = export->pwm;
- struct pwm_state state;
- int val, ret;
- ret = kstrtoint(buf, 0, &val);
- if (ret)
- return ret;
- mutex_lock(&export->lock);
- pwm_get_state(pwm, &state);
- switch (val) {
- case 0:
- state.enabled = false;
- break;
- case 1:
- state.enabled = true;
- break;
- default:
- ret = -EINVAL;
- goto unlock;
- }
- ret = pwm_apply_state(pwm, &state);
- unlock:
- mutex_unlock(&export->lock);
- return ret ? : size;
- }
- static ssize_t polarity_show(struct device *child,
- struct device_attribute *attr,
- char *buf)
- {
- const struct pwm_device *pwm = child_to_pwm_device(child);
- const char *polarity = "unknown";
- struct pwm_state state;
- pwm_get_state(pwm, &state);
- switch (state.polarity) {
- case PWM_POLARITY_NORMAL:
- polarity = "normal";
- break;
- case PWM_POLARITY_INVERSED:
- polarity = "inversed";
- break;
- }
- return sprintf(buf, "%s\n", polarity);
- }
- static ssize_t polarity_store(struct device *child,
- struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct pwm_export *export = child_to_pwm_export(child);
- struct pwm_device *pwm = export->pwm;
- enum pwm_polarity polarity;
- struct pwm_state state;
- int ret;
- if (sysfs_streq(buf, "normal"))
- polarity = PWM_POLARITY_NORMAL;
- else if (sysfs_streq(buf, "inversed"))
- polarity = PWM_POLARITY_INVERSED;
- else
- return -EINVAL;
- mutex_lock(&export->lock);
- pwm_get_state(pwm, &state);
- state.polarity = polarity;
- ret = pwm_apply_state(pwm, &state);
- mutex_unlock(&export->lock);
- return ret ? : size;
- }
- static ssize_t capture_show(struct device *child,
- struct device_attribute *attr,
- char *buf)
- {
- struct pwm_device *pwm = child_to_pwm_device(child);
- struct pwm_capture result;
- int ret;
- ret = pwm_capture(pwm, &result, jiffies_to_msecs(HZ));
- if (ret)
- return ret;
- return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
- }
- static DEVICE_ATTR_RW(period);
- static DEVICE_ATTR_RW(duty_cycle);
- static DEVICE_ATTR_RW(enable);
- static DEVICE_ATTR_RW(polarity);
- static DEVICE_ATTR_RO(capture);
- static struct attribute *pwm_attrs[] = {
- &dev_attr_period.attr,
- &dev_attr_duty_cycle.attr,
- &dev_attr_enable.attr,
- &dev_attr_polarity.attr,
- &dev_attr_capture.attr,
- NULL
- };
- ATTRIBUTE_GROUPS(pwm);
- static void pwm_export_release(struct device *child)
- {
- struct pwm_export *export = child_to_pwm_export(child);
- kfree(export);
- }
- static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
- {
- struct pwm_export *export;
- int ret;
- if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags))
- return -EBUSY;
- export = kzalloc(sizeof(*export), GFP_KERNEL);
- if (!export) {
- clear_bit(PWMF_EXPORTED, &pwm->flags);
- return -ENOMEM;
- }
- export->pwm = pwm;
- mutex_init(&export->lock);
- export->child.release = pwm_export_release;
- export->child.parent = parent;
- export->child.devt = MKDEV(0, 0);
- export->child.groups = pwm_groups;
- dev_set_name(&export->child, "pwm%u", pwm->hwpwm);
- ret = device_register(&export->child);
- if (ret) {
- clear_bit(PWMF_EXPORTED, &pwm->flags);
- kfree(export);
- return ret;
- }
- return 0;
- }
- static int pwm_unexport_match(struct device *child, void *data)
- {
- return child_to_pwm_device(child) == data;
- }
- static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm)
- {
- struct device *child;
- if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
- return -ENODEV;
- child = device_find_child(parent, pwm, pwm_unexport_match);
- if (!child)
- return -ENODEV;
- /* for device_find_child() */
- put_device(child);
- device_unregister(child);
- pwm_put(pwm);
- return 0;
- }
- static ssize_t export_store(struct device *parent,
- struct device_attribute *attr,
- const char *buf, size_t len)
- {
- struct pwm_chip *chip = dev_get_drvdata(parent);
- struct pwm_device *pwm;
- unsigned int hwpwm;
- int ret;
- ret = kstrtouint(buf, 0, &hwpwm);
- if (ret < 0)
- return ret;
- if (hwpwm >= chip->npwm)
- return -ENODEV;
- pwm = pwm_request_from_chip(chip, hwpwm, "sysfs");
- if (IS_ERR(pwm))
- return PTR_ERR(pwm);
- ret = pwm_export_child(parent, pwm);
- if (ret < 0)
- pwm_put(pwm);
- return ret ? : len;
- }
- static DEVICE_ATTR_WO(export);
- static ssize_t unexport_store(struct device *parent,
- struct device_attribute *attr,
- const char *buf, size_t len)
- {
- struct pwm_chip *chip = dev_get_drvdata(parent);
- unsigned int hwpwm;
- int ret;
- ret = kstrtouint(buf, 0, &hwpwm);
- if (ret < 0)
- return ret;
- if (hwpwm >= chip->npwm)
- return -ENODEV;
- ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]);
- return ret ? : len;
- }
- static DEVICE_ATTR_WO(unexport);
- static ssize_t npwm_show(struct device *parent, struct device_attribute *attr,
- char *buf)
- {
- const struct pwm_chip *chip = dev_get_drvdata(parent);
- return sprintf(buf, "%u\n", chip->npwm);
- }
- static DEVICE_ATTR_RO(npwm);
- static struct attribute *pwm_chip_attrs[] = {
- &dev_attr_export.attr,
- &dev_attr_unexport.attr,
- &dev_attr_npwm.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(pwm_chip);
- static struct class pwm_class = {
- .name = "pwm",
- .owner = THIS_MODULE,
- .dev_groups = pwm_chip_groups,
- };
- static int pwmchip_sysfs_match(struct device *parent, const void *data)
- {
- return dev_get_drvdata(parent) == data;
- }
- void pwmchip_sysfs_export(struct pwm_chip *chip)
- {
- struct device *parent;
- /*
- * If device_create() fails the pwm_chip is still usable by
- * the kernel its just not exported.
- */
- parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
- "pwmchip%d", chip->base);
- if (IS_ERR(parent)) {
- dev_warn(chip->dev,
- "device_create failed for pwm_chip sysfs export\n");
- }
- }
- void pwmchip_sysfs_unexport(struct pwm_chip *chip)
- {
- struct device *parent;
- unsigned int i;
- parent = class_find_device(&pwm_class, NULL, chip,
- pwmchip_sysfs_match);
- if (!parent)
- return;
- for (i = 0; i < chip->npwm; i++) {
- struct pwm_device *pwm = &chip->pwms[i];
- if (test_bit(PWMF_EXPORTED, &pwm->flags))
- pwm_unexport_child(parent, pwm);
- }
- put_device(parent);
- device_unregister(parent);
- }
- static int __init pwm_sysfs_init(void)
- {
- return class_register(&pwm_class);
- }
- subsys_initcall(pwm_sysfs_init);
|