162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/bitops.h> 462306a36Sopenharmony_ci#include <linux/device.h> 562306a36Sopenharmony_ci#include <linux/idr.h> 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/interrupt.h> 862306a36Sopenharmony_ci#include <linux/kdev_t.h> 962306a36Sopenharmony_ci#include <linux/kstrtox.h> 1062306a36Sopenharmony_ci#include <linux/list.h> 1162306a36Sopenharmony_ci#include <linux/mutex.h> 1262306a36Sopenharmony_ci#include <linux/printk.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/sysfs.h> 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2062306a36Sopenharmony_ci#include <linux/gpio/driver.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "gpiolib.h" 2362306a36Sopenharmony_ci#include "gpiolib-sysfs.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct kernfs_node; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define GPIO_IRQF_TRIGGER_NONE 0 2862306a36Sopenharmony_ci#define GPIO_IRQF_TRIGGER_FALLING BIT(0) 2962306a36Sopenharmony_ci#define GPIO_IRQF_TRIGGER_RISING BIT(1) 3062306a36Sopenharmony_ci#define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \ 3162306a36Sopenharmony_ci GPIO_IRQF_TRIGGER_RISING) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct gpiod_data { 3462306a36Sopenharmony_ci struct gpio_desc *desc; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci struct mutex mutex; 3762306a36Sopenharmony_ci struct kernfs_node *value_kn; 3862306a36Sopenharmony_ci int irq; 3962306a36Sopenharmony_ci unsigned char irq_flags; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci bool direction_can_change; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * Lock to serialise gpiod export and unexport, and prevent re-export of 4662306a36Sopenharmony_ci * gpiod whose chip is being unregistered. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic DEFINE_MUTEX(sysfs_lock); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * /sys/class/gpio/gpioN... only for GPIOs that are exported 5262306a36Sopenharmony_ci * /direction 5362306a36Sopenharmony_ci * * MAY BE OMITTED if kernel won't allow direction changes 5462306a36Sopenharmony_ci * * is read/write as "in" or "out" 5562306a36Sopenharmony_ci * * may also be written as "high" or "low", initializing 5662306a36Sopenharmony_ci * output value as specified ("out" implies "low") 5762306a36Sopenharmony_ci * /value 5862306a36Sopenharmony_ci * * always readable, subject to hardware behavior 5962306a36Sopenharmony_ci * * may be writable, as zero/nonzero 6062306a36Sopenharmony_ci * /edge 6162306a36Sopenharmony_ci * * configures behavior of poll(2) on /value 6262306a36Sopenharmony_ci * * available only if pin can generate IRQs on input 6362306a36Sopenharmony_ci * * is read/write as "none", "falling", "rising", or "both" 6462306a36Sopenharmony_ci * /active_low 6562306a36Sopenharmony_ci * * configures polarity of /value 6662306a36Sopenharmony_ci * * is read/write as zero/nonzero 6762306a36Sopenharmony_ci * * also affects existing and subsequent "falling" and "rising" 6862306a36Sopenharmony_ci * /edge configuration 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic ssize_t direction_show(struct device *dev, 7262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 7562306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 7662306a36Sopenharmony_ci int value; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci mutex_lock(&data->mutex); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci gpiod_get_direction(desc); 8162306a36Sopenharmony_ci value = !!test_bit(FLAG_IS_OUT, &desc->flags); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci mutex_unlock(&data->mutex); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", value ? "out" : "in"); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic ssize_t direction_store(struct device *dev, 8962306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 9262306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 9362306a36Sopenharmony_ci ssize_t status; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci mutex_lock(&data->mutex); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (sysfs_streq(buf, "high")) 9862306a36Sopenharmony_ci status = gpiod_direction_output_raw(desc, 1); 9962306a36Sopenharmony_ci else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) 10062306a36Sopenharmony_ci status = gpiod_direction_output_raw(desc, 0); 10162306a36Sopenharmony_ci else if (sysfs_streq(buf, "in")) 10262306a36Sopenharmony_ci status = gpiod_direction_input(desc); 10362306a36Sopenharmony_ci else 10462306a36Sopenharmony_ci status = -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci mutex_unlock(&data->mutex); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return status ? : size; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(direction); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic ssize_t value_show(struct device *dev, 11362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 11662306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 11762306a36Sopenharmony_ci ssize_t status; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci mutex_lock(&data->mutex); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci status = gpiod_get_value_cansleep(desc); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci mutex_unlock(&data->mutex); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (status < 0) 12662306a36Sopenharmony_ci return status; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return sysfs_emit(buf, "%zd\n", status); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic ssize_t value_store(struct device *dev, 13262306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 13562306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 13662306a36Sopenharmony_ci ssize_t status; 13762306a36Sopenharmony_ci long value; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci status = kstrtol(buf, 0, &value); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci mutex_lock(&data->mutex); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!test_bit(FLAG_IS_OUT, &desc->flags)) { 14462306a36Sopenharmony_ci status = -EPERM; 14562306a36Sopenharmony_ci } else if (status == 0) { 14662306a36Sopenharmony_ci gpiod_set_value_cansleep(desc, value); 14762306a36Sopenharmony_ci status = size; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci mutex_unlock(&data->mutex); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return status; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_cistatic DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic irqreturn_t gpio_sysfs_irq(int irq, void *priv) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct gpiod_data *data = priv; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci sysfs_notify_dirent(data->value_kn); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return IRQ_HANDLED; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* Caller holds gpiod-data mutex. */ 16662306a36Sopenharmony_cistatic int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 16962306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 17062306a36Sopenharmony_ci unsigned long irq_flags; 17162306a36Sopenharmony_ci int ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci data->irq = gpiod_to_irq(desc); 17462306a36Sopenharmony_ci if (data->irq < 0) 17562306a36Sopenharmony_ci return -EIO; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); 17862306a36Sopenharmony_ci if (!data->value_kn) 17962306a36Sopenharmony_ci return -ENODEV; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci irq_flags = IRQF_SHARED; 18262306a36Sopenharmony_ci if (flags & GPIO_IRQF_TRIGGER_FALLING) 18362306a36Sopenharmony_ci irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? 18462306a36Sopenharmony_ci IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; 18562306a36Sopenharmony_ci if (flags & GPIO_IRQF_TRIGGER_RISING) 18662306a36Sopenharmony_ci irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? 18762306a36Sopenharmony_ci IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * FIXME: This should be done in the irq_request_resources callback 19162306a36Sopenharmony_ci * when the irq is requested, but a few drivers currently fail 19262306a36Sopenharmony_ci * to do so. 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Remove this redundant call (along with the corresponding 19562306a36Sopenharmony_ci * unlock) when those drivers have been fixed. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); 19862306a36Sopenharmony_ci if (ret < 0) 19962306a36Sopenharmony_ci goto err_put_kn; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags, 20262306a36Sopenharmony_ci "gpiolib", data); 20362306a36Sopenharmony_ci if (ret < 0) 20462306a36Sopenharmony_ci goto err_unlock; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci data->irq_flags = flags; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cierr_unlock: 21162306a36Sopenharmony_ci gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); 21262306a36Sopenharmony_cierr_put_kn: 21362306a36Sopenharmony_ci sysfs_put(data->value_kn); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * Caller holds gpiod-data mutex (unless called after class-device 22062306a36Sopenharmony_ci * deregistration). 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic void gpio_sysfs_free_irq(struct device *dev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 22562306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci data->irq_flags = 0; 22862306a36Sopenharmony_ci free_irq(data->irq, data); 22962306a36Sopenharmony_ci gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); 23062306a36Sopenharmony_ci sysfs_put(data->value_kn); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic const char * const trigger_names[] = { 23462306a36Sopenharmony_ci [GPIO_IRQF_TRIGGER_NONE] = "none", 23562306a36Sopenharmony_ci [GPIO_IRQF_TRIGGER_FALLING] = "falling", 23662306a36Sopenharmony_ci [GPIO_IRQF_TRIGGER_RISING] = "rising", 23762306a36Sopenharmony_ci [GPIO_IRQF_TRIGGER_BOTH] = "both", 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic ssize_t edge_show(struct device *dev, 24162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 24462306a36Sopenharmony_ci int flags; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci mutex_lock(&data->mutex); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci flags = data->irq_flags; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci mutex_unlock(&data->mutex); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (flags >= ARRAY_SIZE(trigger_names)) 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", trigger_names[flags]); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic ssize_t edge_store(struct device *dev, 25962306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 26262306a36Sopenharmony_ci ssize_t status = size; 26362306a36Sopenharmony_ci int flags; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci flags = sysfs_match_string(trigger_names, buf); 26662306a36Sopenharmony_ci if (flags < 0) 26762306a36Sopenharmony_ci return flags; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci mutex_lock(&data->mutex); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (flags == data->irq_flags) { 27262306a36Sopenharmony_ci status = size; 27362306a36Sopenharmony_ci goto out_unlock; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (data->irq_flags) 27762306a36Sopenharmony_ci gpio_sysfs_free_irq(dev); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (flags) { 28062306a36Sopenharmony_ci status = gpio_sysfs_request_irq(dev, flags); 28162306a36Sopenharmony_ci if (!status) 28262306a36Sopenharmony_ci status = size; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ciout_unlock: 28662306a36Sopenharmony_ci mutex_unlock(&data->mutex); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return status; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(edge); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* Caller holds gpiod-data mutex. */ 29362306a36Sopenharmony_cistatic int gpio_sysfs_set_active_low(struct device *dev, int value) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 29662306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 29762306a36Sopenharmony_ci int status = 0; 29862306a36Sopenharmony_ci unsigned int flags = data->irq_flags; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci assign_bit(FLAG_ACTIVE_LOW, &desc->flags, value); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* reconfigure poll(2) support if enabled on one edge only */ 30662306a36Sopenharmony_ci if (flags == GPIO_IRQF_TRIGGER_FALLING || 30762306a36Sopenharmony_ci flags == GPIO_IRQF_TRIGGER_RISING) { 30862306a36Sopenharmony_ci gpio_sysfs_free_irq(dev); 30962306a36Sopenharmony_ci status = gpio_sysfs_request_irq(dev, flags); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return status; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic ssize_t active_low_show(struct device *dev, 31662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 31962306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 32062306a36Sopenharmony_ci int value; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci mutex_lock(&data->mutex); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci mutex_unlock(&data->mutex); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", value); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic ssize_t active_low_store(struct device *dev, 33262306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 33562306a36Sopenharmony_ci ssize_t status; 33662306a36Sopenharmony_ci long value; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci status = kstrtol(buf, 0, &value); 33962306a36Sopenharmony_ci if (status) 34062306a36Sopenharmony_ci return status; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci mutex_lock(&data->mutex); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci status = gpio_sysfs_set_active_low(dev, value); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci mutex_unlock(&data->mutex); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return status ? : size; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(active_low); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, 35362306a36Sopenharmony_ci int n) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 35662306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 35762306a36Sopenharmony_ci struct gpio_desc *desc = data->desc; 35862306a36Sopenharmony_ci umode_t mode = attr->mode; 35962306a36Sopenharmony_ci bool show_direction = data->direction_can_change; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (attr == &dev_attr_direction.attr) { 36262306a36Sopenharmony_ci if (!show_direction) 36362306a36Sopenharmony_ci mode = 0; 36462306a36Sopenharmony_ci } else if (attr == &dev_attr_edge.attr) { 36562306a36Sopenharmony_ci if (gpiod_to_irq(desc) < 0) 36662306a36Sopenharmony_ci mode = 0; 36762306a36Sopenharmony_ci if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) 36862306a36Sopenharmony_ci mode = 0; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return mode; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic struct attribute *gpio_attrs[] = { 37562306a36Sopenharmony_ci &dev_attr_direction.attr, 37662306a36Sopenharmony_ci &dev_attr_edge.attr, 37762306a36Sopenharmony_ci &dev_attr_value.attr, 37862306a36Sopenharmony_ci &dev_attr_active_low.attr, 37962306a36Sopenharmony_ci NULL, 38062306a36Sopenharmony_ci}; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic const struct attribute_group gpio_group = { 38362306a36Sopenharmony_ci .attrs = gpio_attrs, 38462306a36Sopenharmony_ci .is_visible = gpio_is_visible, 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic const struct attribute_group *gpio_groups[] = { 38862306a36Sopenharmony_ci &gpio_group, 38962306a36Sopenharmony_ci NULL 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* 39362306a36Sopenharmony_ci * /sys/class/gpio/gpiochipN/ 39462306a36Sopenharmony_ci * /base ... matching gpio_chip.base (N) 39562306a36Sopenharmony_ci * /label ... matching gpio_chip.label 39662306a36Sopenharmony_ci * /ngpio ... matching gpio_chip.ngpio 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic ssize_t base_show(struct device *dev, 40062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci const struct gpio_chip *chip = dev_get_drvdata(dev); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", chip->base); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(base); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic ssize_t label_show(struct device *dev, 40962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci const struct gpio_chip *chip = dev_get_drvdata(dev); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", chip->label ?: ""); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(label); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic ssize_t ngpio_show(struct device *dev, 41862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci const struct gpio_chip *chip = dev_get_drvdata(dev); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", chip->ngpio); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ngpio); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic struct attribute *gpiochip_attrs[] = { 42762306a36Sopenharmony_ci &dev_attr_base.attr, 42862306a36Sopenharmony_ci &dev_attr_label.attr, 42962306a36Sopenharmony_ci &dev_attr_ngpio.attr, 43062306a36Sopenharmony_ci NULL, 43162306a36Sopenharmony_ci}; 43262306a36Sopenharmony_ciATTRIBUTE_GROUPS(gpiochip); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * /sys/class/gpio/export ... write-only 43662306a36Sopenharmony_ci * integer N ... number of GPIO to export (full access) 43762306a36Sopenharmony_ci * /sys/class/gpio/unexport ... write-only 43862306a36Sopenharmony_ci * integer N ... number of GPIO to unexport 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_cistatic ssize_t export_store(const struct class *class, 44162306a36Sopenharmony_ci const struct class_attribute *attr, 44262306a36Sopenharmony_ci const char *buf, size_t len) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci long gpio; 44562306a36Sopenharmony_ci struct gpio_desc *desc; 44662306a36Sopenharmony_ci int status; 44762306a36Sopenharmony_ci struct gpio_chip *gc; 44862306a36Sopenharmony_ci int offset; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci status = kstrtol(buf, 0, &gpio); 45162306a36Sopenharmony_ci if (status < 0) 45262306a36Sopenharmony_ci goto done; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci desc = gpio_to_desc(gpio); 45562306a36Sopenharmony_ci /* reject invalid GPIOs */ 45662306a36Sopenharmony_ci if (!desc) { 45762306a36Sopenharmony_ci pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); 45862306a36Sopenharmony_ci return -EINVAL; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci gc = desc->gdev->chip; 46162306a36Sopenharmony_ci offset = gpio_chip_hwgpio(desc); 46262306a36Sopenharmony_ci if (!gpiochip_line_is_valid(gc, offset)) { 46362306a36Sopenharmony_ci pr_warn("%s: GPIO %ld masked\n", __func__, gpio); 46462306a36Sopenharmony_ci return -EINVAL; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* No extra locking here; FLAG_SYSFS just signifies that the 46862306a36Sopenharmony_ci * request and export were done by on behalf of userspace, so 46962306a36Sopenharmony_ci * they may be undone on its behalf too. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci status = gpiod_request_user(desc, "sysfs"); 47362306a36Sopenharmony_ci if (status) 47462306a36Sopenharmony_ci goto done; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci status = gpiod_set_transitory(desc, false); 47762306a36Sopenharmony_ci if (status) { 47862306a36Sopenharmony_ci gpiod_free(desc); 47962306a36Sopenharmony_ci goto done; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci status = gpiod_export(desc, true); 48362306a36Sopenharmony_ci if (status < 0) 48462306a36Sopenharmony_ci gpiod_free(desc); 48562306a36Sopenharmony_ci else 48662306a36Sopenharmony_ci set_bit(FLAG_SYSFS, &desc->flags); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cidone: 48962306a36Sopenharmony_ci if (status) 49062306a36Sopenharmony_ci pr_debug("%s: status %d\n", __func__, status); 49162306a36Sopenharmony_ci return status ? : len; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_cistatic CLASS_ATTR_WO(export); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic ssize_t unexport_store(const struct class *class, 49662306a36Sopenharmony_ci const struct class_attribute *attr, 49762306a36Sopenharmony_ci const char *buf, size_t len) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci long gpio; 50062306a36Sopenharmony_ci struct gpio_desc *desc; 50162306a36Sopenharmony_ci int status; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci status = kstrtol(buf, 0, &gpio); 50462306a36Sopenharmony_ci if (status < 0) 50562306a36Sopenharmony_ci goto done; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci desc = gpio_to_desc(gpio); 50862306a36Sopenharmony_ci /* reject bogus commands (gpiod_unexport() ignores them) */ 50962306a36Sopenharmony_ci if (!desc) { 51062306a36Sopenharmony_ci pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); 51162306a36Sopenharmony_ci return -EINVAL; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci status = -EINVAL; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* No extra locking here; FLAG_SYSFS just signifies that the 51762306a36Sopenharmony_ci * request and export were done by on behalf of userspace, so 51862306a36Sopenharmony_ci * they may be undone on its behalf too. 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_ci if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) { 52162306a36Sopenharmony_ci gpiod_unexport(desc); 52262306a36Sopenharmony_ci gpiod_free(desc); 52362306a36Sopenharmony_ci status = 0; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_cidone: 52662306a36Sopenharmony_ci if (status) 52762306a36Sopenharmony_ci pr_debug("%s: status %d\n", __func__, status); 52862306a36Sopenharmony_ci return status ? : len; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_cistatic CLASS_ATTR_WO(unexport); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic struct attribute *gpio_class_attrs[] = { 53362306a36Sopenharmony_ci &class_attr_export.attr, 53462306a36Sopenharmony_ci &class_attr_unexport.attr, 53562306a36Sopenharmony_ci NULL, 53662306a36Sopenharmony_ci}; 53762306a36Sopenharmony_ciATTRIBUTE_GROUPS(gpio_class); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic struct class gpio_class = { 54062306a36Sopenharmony_ci .name = "gpio", 54162306a36Sopenharmony_ci .class_groups = gpio_class_groups, 54262306a36Sopenharmony_ci}; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/** 54662306a36Sopenharmony_ci * gpiod_export - export a GPIO through sysfs 54762306a36Sopenharmony_ci * @desc: GPIO to make available, already requested 54862306a36Sopenharmony_ci * @direction_may_change: true if userspace may change GPIO direction 54962306a36Sopenharmony_ci * Context: arch_initcall or later 55062306a36Sopenharmony_ci * 55162306a36Sopenharmony_ci * When drivers want to make a GPIO accessible to userspace after they 55262306a36Sopenharmony_ci * have requested it -- perhaps while debugging, or as part of their 55362306a36Sopenharmony_ci * public interface -- they may use this routine. If the GPIO can 55462306a36Sopenharmony_ci * change direction (some can't) and the caller allows it, userspace 55562306a36Sopenharmony_ci * will see "direction" sysfs attribute which may be used to change 55662306a36Sopenharmony_ci * the gpio's direction. A "value" attribute will always be provided. 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * Returns zero on success, else an error. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ciint gpiod_export(struct gpio_desc *desc, bool direction_may_change) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct gpio_chip *chip; 56362306a36Sopenharmony_ci struct gpio_device *gdev; 56462306a36Sopenharmony_ci struct gpiod_data *data; 56562306a36Sopenharmony_ci unsigned long flags; 56662306a36Sopenharmony_ci int status; 56762306a36Sopenharmony_ci const char *ioname = NULL; 56862306a36Sopenharmony_ci struct device *dev; 56962306a36Sopenharmony_ci int offset; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* can't export until sysfs is available ... */ 57262306a36Sopenharmony_ci if (!class_is_registered(&gpio_class)) { 57362306a36Sopenharmony_ci pr_debug("%s: called too early!\n", __func__); 57462306a36Sopenharmony_ci return -ENOENT; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!desc) { 57862306a36Sopenharmony_ci pr_debug("%s: invalid gpio descriptor\n", __func__); 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci gdev = desc->gdev; 58362306a36Sopenharmony_ci chip = gdev->chip; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci mutex_lock(&sysfs_lock); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* check if chip is being removed */ 58862306a36Sopenharmony_ci if (!chip || !gdev->mockdev) { 58962306a36Sopenharmony_ci status = -ENODEV; 59062306a36Sopenharmony_ci goto err_unlock; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci spin_lock_irqsave(&gpio_lock, flags); 59462306a36Sopenharmony_ci if (!test_bit(FLAG_REQUESTED, &desc->flags) || 59562306a36Sopenharmony_ci test_bit(FLAG_EXPORT, &desc->flags)) { 59662306a36Sopenharmony_ci spin_unlock_irqrestore(&gpio_lock, flags); 59762306a36Sopenharmony_ci gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n", 59862306a36Sopenharmony_ci __func__, 59962306a36Sopenharmony_ci test_bit(FLAG_REQUESTED, &desc->flags), 60062306a36Sopenharmony_ci test_bit(FLAG_EXPORT, &desc->flags)); 60162306a36Sopenharmony_ci status = -EPERM; 60262306a36Sopenharmony_ci goto err_unlock; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci spin_unlock_irqrestore(&gpio_lock, flags); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 60762306a36Sopenharmony_ci if (!data) { 60862306a36Sopenharmony_ci status = -ENOMEM; 60962306a36Sopenharmony_ci goto err_unlock; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci data->desc = desc; 61362306a36Sopenharmony_ci mutex_init(&data->mutex); 61462306a36Sopenharmony_ci if (chip->direction_input && chip->direction_output) 61562306a36Sopenharmony_ci data->direction_can_change = direction_may_change; 61662306a36Sopenharmony_ci else 61762306a36Sopenharmony_ci data->direction_can_change = false; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci offset = gpio_chip_hwgpio(desc); 62062306a36Sopenharmony_ci if (chip->names && chip->names[offset]) 62162306a36Sopenharmony_ci ioname = chip->names[offset]; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci dev = device_create_with_groups(&gpio_class, &gdev->dev, 62462306a36Sopenharmony_ci MKDEV(0, 0), data, gpio_groups, 62562306a36Sopenharmony_ci ioname ? ioname : "gpio%u", 62662306a36Sopenharmony_ci desc_to_gpio(desc)); 62762306a36Sopenharmony_ci if (IS_ERR(dev)) { 62862306a36Sopenharmony_ci status = PTR_ERR(dev); 62962306a36Sopenharmony_ci goto err_free_data; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci set_bit(FLAG_EXPORT, &desc->flags); 63362306a36Sopenharmony_ci mutex_unlock(&sysfs_lock); 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cierr_free_data: 63762306a36Sopenharmony_ci kfree(data); 63862306a36Sopenharmony_cierr_unlock: 63962306a36Sopenharmony_ci mutex_unlock(&sysfs_lock); 64062306a36Sopenharmony_ci gpiod_dbg(desc, "%s: status %d\n", __func__, status); 64162306a36Sopenharmony_ci return status; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gpiod_export); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int match_export(struct device *dev, const void *desc) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct gpiod_data *data = dev_get_drvdata(dev); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return data->desc == desc; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/** 65362306a36Sopenharmony_ci * gpiod_export_link - create a sysfs link to an exported GPIO node 65462306a36Sopenharmony_ci * @dev: device under which to create symlink 65562306a36Sopenharmony_ci * @name: name of the symlink 65662306a36Sopenharmony_ci * @desc: GPIO to create symlink to, already exported 65762306a36Sopenharmony_ci * 65862306a36Sopenharmony_ci * Set up a symlink from /sys/.../dev/name to /sys/class/gpio/gpioN 65962306a36Sopenharmony_ci * node. Caller is responsible for unlinking. 66062306a36Sopenharmony_ci * 66162306a36Sopenharmony_ci * Returns zero on success, else an error. 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ciint gpiod_export_link(struct device *dev, const char *name, 66462306a36Sopenharmony_ci struct gpio_desc *desc) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct device *cdev; 66762306a36Sopenharmony_ci int ret; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!desc) { 67062306a36Sopenharmony_ci pr_warn("%s: invalid GPIO\n", __func__); 67162306a36Sopenharmony_ci return -EINVAL; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci cdev = class_find_device(&gpio_class, NULL, desc, match_export); 67562306a36Sopenharmony_ci if (!cdev) 67662306a36Sopenharmony_ci return -ENODEV; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = sysfs_create_link(&dev->kobj, &cdev->kobj, name); 67962306a36Sopenharmony_ci put_device(cdev); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return ret; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gpiod_export_link); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/** 68662306a36Sopenharmony_ci * gpiod_unexport - reverse effect of gpiod_export() 68762306a36Sopenharmony_ci * @desc: GPIO to make unavailable 68862306a36Sopenharmony_ci * 68962306a36Sopenharmony_ci * This is implicit on gpiod_free(). 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_civoid gpiod_unexport(struct gpio_desc *desc) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct gpiod_data *data; 69462306a36Sopenharmony_ci struct device *dev; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (!desc) { 69762306a36Sopenharmony_ci pr_warn("%s: invalid GPIO\n", __func__); 69862306a36Sopenharmony_ci return; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci mutex_lock(&sysfs_lock); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (!test_bit(FLAG_EXPORT, &desc->flags)) 70462306a36Sopenharmony_ci goto err_unlock; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci dev = class_find_device(&gpio_class, NULL, desc, match_export); 70762306a36Sopenharmony_ci if (!dev) 70862306a36Sopenharmony_ci goto err_unlock; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci data = dev_get_drvdata(dev); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci clear_bit(FLAG_EXPORT, &desc->flags); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci device_unregister(dev); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* 71762306a36Sopenharmony_ci * Release irq after deregistration to prevent race with edge_store. 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_ci if (data->irq_flags) 72062306a36Sopenharmony_ci gpio_sysfs_free_irq(dev); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci mutex_unlock(&sysfs_lock); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci put_device(dev); 72562306a36Sopenharmony_ci kfree(data); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cierr_unlock: 73062306a36Sopenharmony_ci mutex_unlock(&sysfs_lock); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gpiod_unexport); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ciint gpiochip_sysfs_register(struct gpio_device *gdev) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct device *dev; 73762306a36Sopenharmony_ci struct device *parent; 73862306a36Sopenharmony_ci struct gpio_chip *chip = gdev->chip; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* 74162306a36Sopenharmony_ci * Many systems add gpio chips for SOC support very early, 74262306a36Sopenharmony_ci * before driver model support is available. In those cases we 74362306a36Sopenharmony_ci * register later, in gpiolib_sysfs_init() ... here we just 74462306a36Sopenharmony_ci * verify that _some_ field of gpio_class got initialized. 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_ci if (!class_is_registered(&gpio_class)) 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* 75062306a36Sopenharmony_ci * For sysfs backward compatibility we need to preserve this 75162306a36Sopenharmony_ci * preferred parenting to the gpio_chip parent field, if set. 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci if (chip->parent) 75462306a36Sopenharmony_ci parent = chip->parent; 75562306a36Sopenharmony_ci else 75662306a36Sopenharmony_ci parent = &gdev->dev; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* use chip->base for the ID; it's already known to be unique */ 75962306a36Sopenharmony_ci dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip, 76062306a36Sopenharmony_ci gpiochip_groups, GPIOCHIP_NAME "%d", 76162306a36Sopenharmony_ci chip->base); 76262306a36Sopenharmony_ci if (IS_ERR(dev)) 76362306a36Sopenharmony_ci return PTR_ERR(dev); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci mutex_lock(&sysfs_lock); 76662306a36Sopenharmony_ci gdev->mockdev = dev; 76762306a36Sopenharmony_ci mutex_unlock(&sysfs_lock); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_civoid gpiochip_sysfs_unregister(struct gpio_device *gdev) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct gpio_desc *desc; 77562306a36Sopenharmony_ci struct gpio_chip *chip = gdev->chip; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!gdev->mockdev) 77862306a36Sopenharmony_ci return; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci device_unregister(gdev->mockdev); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* prevent further gpiod exports */ 78362306a36Sopenharmony_ci mutex_lock(&sysfs_lock); 78462306a36Sopenharmony_ci gdev->mockdev = NULL; 78562306a36Sopenharmony_ci mutex_unlock(&sysfs_lock); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* unregister gpiod class devices owned by sysfs */ 78862306a36Sopenharmony_ci for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) { 78962306a36Sopenharmony_ci gpiod_unexport(desc); 79062306a36Sopenharmony_ci gpiod_free(desc); 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic int __init gpiolib_sysfs_init(void) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci int status; 79762306a36Sopenharmony_ci unsigned long flags; 79862306a36Sopenharmony_ci struct gpio_device *gdev; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci status = class_register(&gpio_class); 80162306a36Sopenharmony_ci if (status < 0) 80262306a36Sopenharmony_ci return status; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Scan and register the gpio_chips which registered very 80562306a36Sopenharmony_ci * early (e.g. before the class_register above was called). 80662306a36Sopenharmony_ci * 80762306a36Sopenharmony_ci * We run before arch_initcall() so chip->dev nodes can have 80862306a36Sopenharmony_ci * registered, and so arch_initcall() can always gpiod_export(). 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_ci spin_lock_irqsave(&gpio_lock, flags); 81162306a36Sopenharmony_ci list_for_each_entry(gdev, &gpio_devices, list) { 81262306a36Sopenharmony_ci if (gdev->mockdev) 81362306a36Sopenharmony_ci continue; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* 81662306a36Sopenharmony_ci * TODO we yield gpio_lock here because 81762306a36Sopenharmony_ci * gpiochip_sysfs_register() acquires a mutex. This is unsafe 81862306a36Sopenharmony_ci * and needs to be fixed. 81962306a36Sopenharmony_ci * 82062306a36Sopenharmony_ci * Also it would be nice to use gpio_device_find() here so we 82162306a36Sopenharmony_ci * can keep gpio_chips local to gpiolib.c, but the yield of 82262306a36Sopenharmony_ci * gpio_lock prevents us from doing this. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_ci spin_unlock_irqrestore(&gpio_lock, flags); 82562306a36Sopenharmony_ci status = gpiochip_sysfs_register(gdev); 82662306a36Sopenharmony_ci spin_lock_irqsave(&gpio_lock, flags); 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci spin_unlock_irqrestore(&gpio_lock, flags); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return status; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_cipostcore_initcall(gpiolib_sysfs_init); 833