176abbddeSH Hartley Sweeten /* 276abbddeSH Hartley Sweeten * A simple sysfs interface for the generic PWM framework 376abbddeSH Hartley Sweeten * 476abbddeSH Hartley Sweeten * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com> 576abbddeSH Hartley Sweeten * 676abbddeSH Hartley Sweeten * Based on previous work by Lars Poeschel <poeschel@lemonage.de> 776abbddeSH Hartley Sweeten * 876abbddeSH Hartley Sweeten * This program is free software; you can redistribute it and/or modify 976abbddeSH Hartley Sweeten * it under the terms of the GNU General Public License as published by 1076abbddeSH Hartley Sweeten * the Free Software Foundation; either version 2, or (at your option) 1176abbddeSH Hartley Sweeten * any later version. 1276abbddeSH Hartley Sweeten * 1376abbddeSH Hartley Sweeten * This program is distributed in the hope that it will be useful, 1476abbddeSH Hartley Sweeten * but WITHOUT ANY WARRANTY; without even the implied warranty of 1576abbddeSH Hartley Sweeten * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1676abbddeSH Hartley Sweeten * GNU General Public License for more details. 1776abbddeSH Hartley Sweeten */ 1876abbddeSH Hartley Sweeten 1976abbddeSH Hartley Sweeten #include <linux/device.h> 2076abbddeSH Hartley Sweeten #include <linux/mutex.h> 2176abbddeSH Hartley Sweeten #include <linux/err.h> 2276abbddeSH Hartley Sweeten #include <linux/slab.h> 2376abbddeSH Hartley Sweeten #include <linux/kdev_t.h> 2476abbddeSH Hartley Sweeten #include <linux/pwm.h> 2576abbddeSH Hartley Sweeten 2676abbddeSH Hartley Sweeten struct pwm_export { 2776abbddeSH Hartley Sweeten struct device child; 2876abbddeSH Hartley Sweeten struct pwm_device *pwm; 2976abbddeSH Hartley Sweeten }; 3076abbddeSH Hartley Sweeten 3176abbddeSH Hartley Sweeten static struct pwm_export *child_to_pwm_export(struct device *child) 3276abbddeSH Hartley Sweeten { 3376abbddeSH Hartley Sweeten return container_of(child, struct pwm_export, child); 3476abbddeSH Hartley Sweeten } 3576abbddeSH Hartley Sweeten 3676abbddeSH Hartley Sweeten static struct pwm_device *child_to_pwm_device(struct device *child) 3776abbddeSH Hartley Sweeten { 3876abbddeSH Hartley Sweeten struct pwm_export *export = child_to_pwm_export(child); 3976abbddeSH Hartley Sweeten 4076abbddeSH Hartley Sweeten return export->pwm; 4176abbddeSH Hartley Sweeten } 4276abbddeSH Hartley Sweeten 4376abbddeSH Hartley Sweeten static ssize_t pwm_period_show(struct device *child, 4476abbddeSH Hartley Sweeten struct device_attribute *attr, 4576abbddeSH Hartley Sweeten char *buf) 4676abbddeSH Hartley Sweeten { 4776abbddeSH Hartley Sweeten const struct pwm_device *pwm = child_to_pwm_device(child); 4876abbddeSH Hartley Sweeten 4915da7b50SBoris Brezillon return sprintf(buf, "%u\n", pwm_get_period(pwm)); 5076abbddeSH Hartley Sweeten } 5176abbddeSH Hartley Sweeten 5276abbddeSH Hartley Sweeten static ssize_t pwm_period_store(struct device *child, 5376abbddeSH Hartley Sweeten struct device_attribute *attr, 5476abbddeSH Hartley Sweeten const char *buf, size_t size) 5576abbddeSH Hartley Sweeten { 5676abbddeSH Hartley Sweeten struct pwm_device *pwm = child_to_pwm_device(child); 5776abbddeSH Hartley Sweeten unsigned int val; 5876abbddeSH Hartley Sweeten int ret; 5976abbddeSH Hartley Sweeten 6076abbddeSH Hartley Sweeten ret = kstrtouint(buf, 0, &val); 6176abbddeSH Hartley Sweeten if (ret) 6276abbddeSH Hartley Sweeten return ret; 6376abbddeSH Hartley Sweeten 6415da7b50SBoris Brezillon ret = pwm_config(pwm, pwm_get_duty_cycle(pwm), val); 6576abbddeSH Hartley Sweeten 6676abbddeSH Hartley Sweeten return ret ? : size; 6776abbddeSH Hartley Sweeten } 6876abbddeSH Hartley Sweeten 6976abbddeSH Hartley Sweeten static ssize_t pwm_duty_cycle_show(struct device *child, 7076abbddeSH Hartley Sweeten struct device_attribute *attr, 7176abbddeSH Hartley Sweeten char *buf) 7276abbddeSH Hartley Sweeten { 7376abbddeSH Hartley Sweeten const struct pwm_device *pwm = child_to_pwm_device(child); 7476abbddeSH Hartley Sweeten 7515da7b50SBoris Brezillon return sprintf(buf, "%u\n", pwm_get_duty_cycle(pwm)); 7676abbddeSH Hartley Sweeten } 7776abbddeSH Hartley Sweeten 7876abbddeSH Hartley Sweeten static ssize_t pwm_duty_cycle_store(struct device *child, 7976abbddeSH Hartley Sweeten struct device_attribute *attr, 8076abbddeSH Hartley Sweeten const char *buf, size_t size) 8176abbddeSH Hartley Sweeten { 8276abbddeSH Hartley Sweeten struct pwm_device *pwm = child_to_pwm_device(child); 8376abbddeSH Hartley Sweeten unsigned int val; 8476abbddeSH Hartley Sweeten int ret; 8576abbddeSH Hartley Sweeten 8676abbddeSH Hartley Sweeten ret = kstrtouint(buf, 0, &val); 8776abbddeSH Hartley Sweeten if (ret) 8876abbddeSH Hartley Sweeten return ret; 8976abbddeSH Hartley Sweeten 9015da7b50SBoris Brezillon ret = pwm_config(pwm, val, pwm_get_period(pwm)); 9176abbddeSH Hartley Sweeten 9276abbddeSH Hartley Sweeten return ret ? : size; 9376abbddeSH Hartley Sweeten } 9476abbddeSH Hartley Sweeten 9576abbddeSH Hartley Sweeten static ssize_t pwm_enable_show(struct device *child, 9676abbddeSH Hartley Sweeten struct device_attribute *attr, 9776abbddeSH Hartley Sweeten char *buf) 9876abbddeSH Hartley Sweeten { 9976abbddeSH Hartley Sweeten const struct pwm_device *pwm = child_to_pwm_device(child); 1005c31252cSBoris Brezillon int enabled = pwm_is_enabled(pwm); 10176abbddeSH Hartley Sweeten 10276abbddeSH Hartley Sweeten return sprintf(buf, "%d\n", enabled); 10376abbddeSH Hartley Sweeten } 10476abbddeSH Hartley Sweeten 10576abbddeSH Hartley Sweeten static ssize_t pwm_enable_store(struct device *child, 10676abbddeSH Hartley Sweeten struct device_attribute *attr, 10776abbddeSH Hartley Sweeten const char *buf, size_t size) 10876abbddeSH Hartley Sweeten { 10976abbddeSH Hartley Sweeten struct pwm_device *pwm = child_to_pwm_device(child); 11076abbddeSH Hartley Sweeten int val, ret; 11176abbddeSH Hartley Sweeten 11276abbddeSH Hartley Sweeten ret = kstrtoint(buf, 0, &val); 11376abbddeSH Hartley Sweeten if (ret) 11476abbddeSH Hartley Sweeten return ret; 11576abbddeSH Hartley Sweeten 11676abbddeSH Hartley Sweeten switch (val) { 11776abbddeSH Hartley Sweeten case 0: 11876abbddeSH Hartley Sweeten pwm_disable(pwm); 11976abbddeSH Hartley Sweeten break; 12076abbddeSH Hartley Sweeten case 1: 12176abbddeSH Hartley Sweeten ret = pwm_enable(pwm); 12276abbddeSH Hartley Sweeten break; 12376abbddeSH Hartley Sweeten default: 12476abbddeSH Hartley Sweeten ret = -EINVAL; 12576abbddeSH Hartley Sweeten break; 12676abbddeSH Hartley Sweeten } 12776abbddeSH Hartley Sweeten 12876abbddeSH Hartley Sweeten return ret ? : size; 12976abbddeSH Hartley Sweeten } 13076abbddeSH Hartley Sweeten 13176abbddeSH Hartley Sweeten static ssize_t pwm_polarity_show(struct device *child, 13276abbddeSH Hartley Sweeten struct device_attribute *attr, 13376abbddeSH Hartley Sweeten char *buf) 13476abbddeSH Hartley Sweeten { 13576abbddeSH Hartley Sweeten const struct pwm_device *pwm = child_to_pwm_device(child); 1365a063d87SThierry Reding const char *polarity = "unknown"; 13776abbddeSH Hartley Sweeten 1385a063d87SThierry Reding switch (pwm_get_polarity(pwm)) { 1395a063d87SThierry Reding case PWM_POLARITY_NORMAL: 1405a063d87SThierry Reding polarity = "normal"; 1415a063d87SThierry Reding break; 1425a063d87SThierry Reding 1435a063d87SThierry Reding case PWM_POLARITY_INVERSED: 1445a063d87SThierry Reding polarity = "inversed"; 1455a063d87SThierry Reding break; 1465a063d87SThierry Reding } 1475a063d87SThierry Reding 1485a063d87SThierry Reding return sprintf(buf, "%s\n", polarity); 14976abbddeSH Hartley Sweeten } 15076abbddeSH Hartley Sweeten 15176abbddeSH Hartley Sweeten static ssize_t pwm_polarity_store(struct device *child, 15276abbddeSH Hartley Sweeten struct device_attribute *attr, 15376abbddeSH Hartley Sweeten const char *buf, size_t size) 15476abbddeSH Hartley Sweeten { 15576abbddeSH Hartley Sweeten struct pwm_device *pwm = child_to_pwm_device(child); 15676abbddeSH Hartley Sweeten enum pwm_polarity polarity; 15776abbddeSH Hartley Sweeten int ret; 15876abbddeSH Hartley Sweeten 15976abbddeSH Hartley Sweeten if (sysfs_streq(buf, "normal")) 16076abbddeSH Hartley Sweeten polarity = PWM_POLARITY_NORMAL; 16176abbddeSH Hartley Sweeten else if (sysfs_streq(buf, "inversed")) 16276abbddeSH Hartley Sweeten polarity = PWM_POLARITY_INVERSED; 16376abbddeSH Hartley Sweeten else 16476abbddeSH Hartley Sweeten return -EINVAL; 16576abbddeSH Hartley Sweeten 16676abbddeSH Hartley Sweeten ret = pwm_set_polarity(pwm, polarity); 16776abbddeSH Hartley Sweeten 16876abbddeSH Hartley Sweeten return ret ? : size; 16976abbddeSH Hartley Sweeten } 17076abbddeSH Hartley Sweeten 17176abbddeSH Hartley Sweeten static DEVICE_ATTR(period, 0644, pwm_period_show, pwm_period_store); 17276abbddeSH Hartley Sweeten static DEVICE_ATTR(duty_cycle, 0644, pwm_duty_cycle_show, pwm_duty_cycle_store); 17376abbddeSH Hartley Sweeten static DEVICE_ATTR(enable, 0644, pwm_enable_show, pwm_enable_store); 17476abbddeSH Hartley Sweeten static DEVICE_ATTR(polarity, 0644, pwm_polarity_show, pwm_polarity_store); 17576abbddeSH Hartley Sweeten 17676abbddeSH Hartley Sweeten static struct attribute *pwm_attrs[] = { 17776abbddeSH Hartley Sweeten &dev_attr_period.attr, 17876abbddeSH Hartley Sweeten &dev_attr_duty_cycle.attr, 17976abbddeSH Hartley Sweeten &dev_attr_enable.attr, 18076abbddeSH Hartley Sweeten &dev_attr_polarity.attr, 18176abbddeSH Hartley Sweeten NULL 18276abbddeSH Hartley Sweeten }; 1836ca142adSAxel Lin ATTRIBUTE_GROUPS(pwm); 18476abbddeSH Hartley Sweeten 18576abbddeSH Hartley Sweeten static void pwm_export_release(struct device *child) 18676abbddeSH Hartley Sweeten { 18776abbddeSH Hartley Sweeten struct pwm_export *export = child_to_pwm_export(child); 18876abbddeSH Hartley Sweeten 18976abbddeSH Hartley Sweeten kfree(export); 19076abbddeSH Hartley Sweeten } 19176abbddeSH Hartley Sweeten 19276abbddeSH Hartley Sweeten static int pwm_export_child(struct device *parent, struct pwm_device *pwm) 19376abbddeSH Hartley Sweeten { 19476abbddeSH Hartley Sweeten struct pwm_export *export; 19576abbddeSH Hartley Sweeten int ret; 19676abbddeSH Hartley Sweeten 19776abbddeSH Hartley Sweeten if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags)) 19876abbddeSH Hartley Sweeten return -EBUSY; 19976abbddeSH Hartley Sweeten 20076abbddeSH Hartley Sweeten export = kzalloc(sizeof(*export), GFP_KERNEL); 20176abbddeSH Hartley Sweeten if (!export) { 20276abbddeSH Hartley Sweeten clear_bit(PWMF_EXPORTED, &pwm->flags); 20376abbddeSH Hartley Sweeten return -ENOMEM; 20476abbddeSH Hartley Sweeten } 20576abbddeSH Hartley Sweeten 20676abbddeSH Hartley Sweeten export->pwm = pwm; 20776abbddeSH Hartley Sweeten 20876abbddeSH Hartley Sweeten export->child.release = pwm_export_release; 20976abbddeSH Hartley Sweeten export->child.parent = parent; 21076abbddeSH Hartley Sweeten export->child.devt = MKDEV(0, 0); 2116ca142adSAxel Lin export->child.groups = pwm_groups; 21276abbddeSH Hartley Sweeten dev_set_name(&export->child, "pwm%u", pwm->hwpwm); 21376abbddeSH Hartley Sweeten 21476abbddeSH Hartley Sweeten ret = device_register(&export->child); 21576abbddeSH Hartley Sweeten if (ret) { 21676abbddeSH Hartley Sweeten clear_bit(PWMF_EXPORTED, &pwm->flags); 21776abbddeSH Hartley Sweeten kfree(export); 21876abbddeSH Hartley Sweeten return ret; 21976abbddeSH Hartley Sweeten } 22076abbddeSH Hartley Sweeten 22176abbddeSH Hartley Sweeten return 0; 22276abbddeSH Hartley Sweeten } 22376abbddeSH Hartley Sweeten 22476abbddeSH Hartley Sweeten static int pwm_unexport_match(struct device *child, void *data) 22576abbddeSH Hartley Sweeten { 22676abbddeSH Hartley Sweeten return child_to_pwm_device(child) == data; 22776abbddeSH Hartley Sweeten } 22876abbddeSH Hartley Sweeten 22976abbddeSH Hartley Sweeten static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm) 23076abbddeSH Hartley Sweeten { 23176abbddeSH Hartley Sweeten struct device *child; 23276abbddeSH Hartley Sweeten 23376abbddeSH Hartley Sweeten if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags)) 23476abbddeSH Hartley Sweeten return -ENODEV; 23576abbddeSH Hartley Sweeten 23676abbddeSH Hartley Sweeten child = device_find_child(parent, pwm, pwm_unexport_match); 23776abbddeSH Hartley Sweeten if (!child) 23876abbddeSH Hartley Sweeten return -ENODEV; 23976abbddeSH Hartley Sweeten 24076abbddeSH Hartley Sweeten /* for device_find_child() */ 24176abbddeSH Hartley Sweeten put_device(child); 24276abbddeSH Hartley Sweeten device_unregister(child); 24376abbddeSH Hartley Sweeten pwm_put(pwm); 24476abbddeSH Hartley Sweeten 24576abbddeSH Hartley Sweeten return 0; 24676abbddeSH Hartley Sweeten } 24776abbddeSH Hartley Sweeten 24876abbddeSH Hartley Sweeten static ssize_t pwm_export_store(struct device *parent, 24976abbddeSH Hartley Sweeten struct device_attribute *attr, 25076abbddeSH Hartley Sweeten const char *buf, size_t len) 25176abbddeSH Hartley Sweeten { 25276abbddeSH Hartley Sweeten struct pwm_chip *chip = dev_get_drvdata(parent); 25376abbddeSH Hartley Sweeten struct pwm_device *pwm; 25476abbddeSH Hartley Sweeten unsigned int hwpwm; 25576abbddeSH Hartley Sweeten int ret; 25676abbddeSH Hartley Sweeten 25776abbddeSH Hartley Sweeten ret = kstrtouint(buf, 0, &hwpwm); 25876abbddeSH Hartley Sweeten if (ret < 0) 25976abbddeSH Hartley Sweeten return ret; 26076abbddeSH Hartley Sweeten 26176abbddeSH Hartley Sweeten if (hwpwm >= chip->npwm) 26276abbddeSH Hartley Sweeten return -ENODEV; 26376abbddeSH Hartley Sweeten 26476abbddeSH Hartley Sweeten pwm = pwm_request_from_chip(chip, hwpwm, "sysfs"); 26576abbddeSH Hartley Sweeten if (IS_ERR(pwm)) 26676abbddeSH Hartley Sweeten return PTR_ERR(pwm); 26776abbddeSH Hartley Sweeten 26876abbddeSH Hartley Sweeten ret = pwm_export_child(parent, pwm); 26976abbddeSH Hartley Sweeten if (ret < 0) 27076abbddeSH Hartley Sweeten pwm_put(pwm); 27176abbddeSH Hartley Sweeten 27276abbddeSH Hartley Sweeten return ret ? : len; 27376abbddeSH Hartley Sweeten } 2749da01759SGreg Kroah-Hartman static DEVICE_ATTR(export, 0200, NULL, pwm_export_store); 27576abbddeSH Hartley Sweeten 27676abbddeSH Hartley Sweeten static ssize_t pwm_unexport_store(struct device *parent, 27776abbddeSH Hartley Sweeten struct device_attribute *attr, 27876abbddeSH Hartley Sweeten const char *buf, size_t len) 27976abbddeSH Hartley Sweeten { 28076abbddeSH Hartley Sweeten struct pwm_chip *chip = dev_get_drvdata(parent); 28176abbddeSH Hartley Sweeten unsigned int hwpwm; 28276abbddeSH Hartley Sweeten int ret; 28376abbddeSH Hartley Sweeten 28476abbddeSH Hartley Sweeten ret = kstrtouint(buf, 0, &hwpwm); 28576abbddeSH Hartley Sweeten if (ret < 0) 28676abbddeSH Hartley Sweeten return ret; 28776abbddeSH Hartley Sweeten 28876abbddeSH Hartley Sweeten if (hwpwm >= chip->npwm) 28976abbddeSH Hartley Sweeten return -ENODEV; 29076abbddeSH Hartley Sweeten 29176abbddeSH Hartley Sweeten ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]); 29276abbddeSH Hartley Sweeten 29376abbddeSH Hartley Sweeten return ret ? : len; 29476abbddeSH Hartley Sweeten } 2959da01759SGreg Kroah-Hartman static DEVICE_ATTR(unexport, 0200, NULL, pwm_unexport_store); 29676abbddeSH Hartley Sweeten 2979da01759SGreg Kroah-Hartman static ssize_t npwm_show(struct device *parent, struct device_attribute *attr, 29876abbddeSH Hartley Sweeten char *buf) 29976abbddeSH Hartley Sweeten { 30076abbddeSH Hartley Sweeten const struct pwm_chip *chip = dev_get_drvdata(parent); 30176abbddeSH Hartley Sweeten 30276abbddeSH Hartley Sweeten return sprintf(buf, "%u\n", chip->npwm); 30376abbddeSH Hartley Sweeten } 3049da01759SGreg Kroah-Hartman static DEVICE_ATTR_RO(npwm); 30576abbddeSH Hartley Sweeten 3069da01759SGreg Kroah-Hartman static struct attribute *pwm_chip_attrs[] = { 3079da01759SGreg Kroah-Hartman &dev_attr_export.attr, 3089da01759SGreg Kroah-Hartman &dev_attr_unexport.attr, 3099da01759SGreg Kroah-Hartman &dev_attr_npwm.attr, 3109da01759SGreg Kroah-Hartman NULL, 31176abbddeSH Hartley Sweeten }; 3129da01759SGreg Kroah-Hartman ATTRIBUTE_GROUPS(pwm_chip); 31376abbddeSH Hartley Sweeten 31476abbddeSH Hartley Sweeten static struct class pwm_class = { 31576abbddeSH Hartley Sweeten .name = "pwm", 31676abbddeSH Hartley Sweeten .owner = THIS_MODULE, 3179da01759SGreg Kroah-Hartman .dev_groups = pwm_chip_groups, 31876abbddeSH Hartley Sweeten }; 31976abbddeSH Hartley Sweeten 32076abbddeSH Hartley Sweeten static int pwmchip_sysfs_match(struct device *parent, const void *data) 32176abbddeSH Hartley Sweeten { 32276abbddeSH Hartley Sweeten return dev_get_drvdata(parent) == data; 32376abbddeSH Hartley Sweeten } 32476abbddeSH Hartley Sweeten 32576abbddeSH Hartley Sweeten void pwmchip_sysfs_export(struct pwm_chip *chip) 32676abbddeSH Hartley Sweeten { 32776abbddeSH Hartley Sweeten struct device *parent; 32876abbddeSH Hartley Sweeten 32976abbddeSH Hartley Sweeten /* 33076abbddeSH Hartley Sweeten * If device_create() fails the pwm_chip is still usable by 33176abbddeSH Hartley Sweeten * the kernel its just not exported. 33276abbddeSH Hartley Sweeten */ 33376abbddeSH Hartley Sweeten parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip, 33476abbddeSH Hartley Sweeten "pwmchip%d", chip->base); 33576abbddeSH Hartley Sweeten if (IS_ERR(parent)) { 33676abbddeSH Hartley Sweeten dev_warn(chip->dev, 33776abbddeSH Hartley Sweeten "device_create failed for pwm_chip sysfs export\n"); 33876abbddeSH Hartley Sweeten } 33976abbddeSH Hartley Sweeten } 34076abbddeSH Hartley Sweeten 34176abbddeSH Hartley Sweeten void pwmchip_sysfs_unexport(struct pwm_chip *chip) 34276abbddeSH Hartley Sweeten { 34376abbddeSH Hartley Sweeten struct device *parent; 34476abbddeSH Hartley Sweeten 34576abbddeSH Hartley Sweeten parent = class_find_device(&pwm_class, NULL, chip, 34676abbddeSH Hartley Sweeten pwmchip_sysfs_match); 34776abbddeSH Hartley Sweeten if (parent) { 34876abbddeSH Hartley Sweeten /* for class_find_device() */ 34976abbddeSH Hartley Sweeten put_device(parent); 35076abbddeSH Hartley Sweeten device_unregister(parent); 35176abbddeSH Hartley Sweeten } 35276abbddeSH Hartley Sweeten } 35376abbddeSH Hartley Sweeten 35476abbddeSH Hartley Sweeten static int __init pwm_sysfs_init(void) 35576abbddeSH Hartley Sweeten { 35676abbddeSH Hartley Sweeten return class_register(&pwm_class); 35776abbddeSH Hartley Sweeten } 35876abbddeSH Hartley Sweeten subsys_initcall(pwm_sysfs_init); 359