From: Ismo Puustinen <ismo.puusti...@intel.com> Galileo gen 2 has support for setting GPIO modes. Expose these properties through the GPIO sysfs interface. This approach is bit hacky, since it changes the interface semantics.
Original patch by Josef Ahmad <josef.ah...@linux.intel.com>. Upstream-status: Forward-ported from Intel IOT Develper Kit Quark BSP. Inappropriate for Linux upstream, since it changes the sysfs GPIO userspace API. We need to keep this for backwards compatibility. A better solution would be to implement a pin control driver to handle the drive states of the GPIO pins, but there isn't a userspace interface yet for that. Signed-off-by: Ismo Puustinen <ismo.puusti...@intel.com> Signed-off-by: Saul Wold <s...@linux.intel.com> --- drivers/gpio/gpio-pca953x.c | 57 +++++++++++++++++++++++++++---- drivers/gpio/gpiolib-sysfs.c | 78 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 18 ++++++++++ drivers/gpio/gpiolib.h | 7 +++- include/asm-generic/gpio.h | 5 +++ include/linux/gpio.h | 10 ++++++ include/linux/gpio/consumer.h | 11 ++++++ include/linux/gpio/driver.h | 2 ++ 8 files changed, 180 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 0227cde..8f49bd6 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -39,6 +39,9 @@ #define PCA957X_MSK 6 #define PCA957X_INTS 7 +#define PCA953X_PUPD_EN 35 +#define PCA953X_PUPD_SEL 36 + #define PCA_GPIO_MASK 0x00FF #define PCA_INT 0x0100 #define PCA953X_TYPE 0x1000 @@ -374,6 +377,43 @@ exit: mutex_unlock(&chip->i2c_lock); } +static int pca953x_gpio_set_drive(struct gpio_chip *gc, + unsigned off, unsigned mode) +{ + struct pca953x_chip *chip; + int ret = 0; + int val; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + if (chip->chip_type != PCA953X_TYPE) + return -EINVAL; + + mutex_lock(&chip->i2c_lock); + + switch (mode) { + case GPIOF_DRIVE_PULLUP: + ret = pca953x_write_single(chip, PCA953X_PUPD_EN, 1, off) || + pca953x_write_single(chip, PCA953X_PUPD_SEL, 1, off); + break; + case GPIOF_DRIVE_PULLDOWN: + ret = pca953x_write_single(chip, PCA953X_PUPD_EN, 1, off) || + pca953x_write_single(chip, PCA953X_PUPD_SEL, 0, off); + break; + case GPIOF_DRIVE_STRONG: + case GPIOF_DRIVE_HIZ: + ret = pca953x_read_single(chip, PCA953X_PUPD_EN, &val, off) || + pca953x_write_single(chip, PCA953X_PUPD_EN, 0, off) || + pca953x_write_single(chip, PCA953X_PUPD_SEL, val, off); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&chip->i2c_lock); + return ret; +} + static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) { struct gpio_chip *gc; @@ -392,6 +432,9 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->dev = &chip->client->dev; gc->owner = THIS_MODULE; gc->names = chip->names; + + if (chip->chip_type == PCA953X_TYPE) + gc->set_drive = pca953x_gpio_set_drive; } #ifdef CONFIG_GPIO_PCA953X_IRQ @@ -548,7 +591,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid) } static int pca953x_irq_setup(struct pca953x_chip *chip, - int irq_base) + int irq_base) { struct i2c_client *client = chip->client; int ret, i, offset = 0; @@ -591,10 +634,10 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, } ret = gpiochip_irqchip_add(&chip->gpio_chip, - &pca953x_irq_chip, - irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); + &pca953x_irq_chip, + irq_base, + handle_simple_irq, + IRQ_TYPE_NONE); if (ret) { dev_err(&client->dev, "could not connect irqchip to gpiochip\n"); @@ -607,7 +650,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, #else /* CONFIG_GPIO_PCA953X_IRQ */ static int pca953x_irq_setup(struct pca953x_chip *chip, - int irq_base) + int irq_base) { struct i2c_client *client = chip->client; @@ -628,7 +671,7 @@ static int device_pca953x_init(struct pca953x_chip *chip, u32 invert) goto out; ret = pca953x_read_regs(chip, PCA953X_DIRECTION, - chip->reg_direction); + chip->reg_direction); if (ret) goto out; diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index af3bc7a..4f90f69 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -6,6 +6,7 @@ #include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/kdev_t.h> +#include <linux/gpio.h> #include "gpiolib.h" @@ -356,6 +357,82 @@ static ssize_t gpio_active_low_store(struct device *dev, static DEVICE_ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store); + +static ssize_t gpio_drive_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) { + status = -EIO; + } else { + if (test_bit(FLAG_PULLUP, &desc->flags)) + status = sprintf(buf, "pullup\n"); + else if (test_bit(FLAG_PULLDOWN, &desc->flags)) + status = sprintf(buf, "pulldown\n"); + else if (test_bit(FLAG_STRONG, &desc->flags)) + status = sprintf(buf, "strong\n"); + else if (test_bit(FLAG_HIZ, &desc->flags)) + status = sprintf(buf, "hiz\n"); + else + status = -EINVAL; + } + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_drive_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else { + if (sysfs_streq(buf, "pullup")) { + status = gpiod_set_drive(desc, GPIOF_DRIVE_PULLUP); + if (!status) { + desc->flags &= ~GPIO_DRIVE_MASK; + set_bit(FLAG_PULLUP, &desc->flags); + } + } else if (sysfs_streq(buf, "pulldown")) { + status = gpiod_set_drive(desc, GPIOF_DRIVE_PULLDOWN); + if (!status) { + desc->flags &= ~GPIO_DRIVE_MASK; + set_bit(FLAG_PULLDOWN, &desc->flags); + } + } else if (sysfs_streq(buf, "strong")) { + status = gpiod_set_drive(desc, GPIOF_DRIVE_STRONG); + if (!status) { + desc->flags &= ~GPIO_DRIVE_MASK; + set_bit(FLAG_STRONG, &desc->flags); + } + } else if (sysfs_streq(buf, "hiz")) { + status = gpiod_set_drive(desc, GPIOF_DRIVE_HIZ); + if (!status) { + desc->flags &= ~GPIO_DRIVE_MASK; + set_bit(FLAG_HIZ, &desc->flags); + } + } else { + status = -EINVAL; + } + } + + mutex_unlock(&sysfs_lock); + return status ? : size; +} + +static const DEVICE_ATTR(drive, 0644, + gpio_drive_show, gpio_drive_store); + + static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -382,6 +459,7 @@ static struct attribute *gpio_attrs[] = { &dev_attr_edge.attr, &dev_attr_value.attr, &dev_attr_active_low.attr, + &dev_attr_drive.attr, NULL, }; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6bc612b..038159b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1127,6 +1127,24 @@ int gpiod_is_active_low(const struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_is_active_low); +int gpiod_set_drive(struct gpio_desc *desc, unsigned mode) +{ + unsigned long flags; + struct gpio_chip *chip; + + chip = desc->chip; + if (!chip || !chip->set || !chip->set_drive) + goto fail; + + might_sleep_if(chip->can_sleep); + + return chip->set_drive(chip, gpio_chip_hwgpio(desc), mode); + +fail: + return -EINVAL; +} +EXPORT_SYMBOL_GPL(gpiod_set_drive); + /* I/O calls are only valid after configuration completed; the relevant * "is this a valid GPIO" error checks should already have been done. * diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 594b179..2ffc2e6 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -91,12 +91,17 @@ struct gpio_desc { #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define FLAG_SYSFS_DIR 10 /* show sysfs direction attribute */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ +#define FLAG_PULLUP 12 /* Gpio drive is resistive pullup */ +#define FLAG_PULLDOWN 13 /* Gpio drive is resistive pulldown */ +#define FLAG_STRONG 14 /* Gpio drive is strong (fast output) */ +#define FLAG_HIZ 15 /* Gpio drive is Hi-Z (input) */ #define ID_SHIFT 16 /* add new flags before this one */ #define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1) #define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) - +#define GPIO_DRIVE_MASK (BIT(FLAG_PULLUP) | BIT(FLAG_PULLDOWN) \ + | BIT(FLAG_STRONG) | BIT(FLAG_HIZ)) const char *label; }; diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 9bb0d11..21c9497 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -76,6 +76,11 @@ static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) return gpiod_set_debounce(gpio_to_desc(gpio), debounce); } +static inline int gpio_set_drive(unsigned gpio, unsigned mode) +{ + return gpiod_set_drive(gpio_to_desc(gpio), mode); +} + static inline int gpio_get_value_cansleep(unsigned gpio) { return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio)); diff --git a/include/linux/gpio.h b/include/linux/gpio.h index ab81339..c22f1f6 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -30,6 +30,11 @@ #define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT) #define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE) +#define GPIOF_DRIVE_PULLUP (1 << 7) +#define GPIOF_DRIVE_PULLDOWN (1 << 8) +#define GPIOF_DRIVE_STRONG (1 << 9) +#define GPIOF_DRIVE_HIZ (1 << 10) + /** * struct gpio - a structure describing a GPIO with configuration * @gpio: the GPIO number @@ -148,6 +153,11 @@ static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) return -ENOSYS; } +static inline int gpio_set_drive(unsigned gpio, unsigned mode) +{ + return -ENOSYS; +} + static inline int gpio_get_value(unsigned gpio) { /* GPIO can never have been requested or set as {in,out}put */ diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index da04265..ecdbc74 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -121,6 +121,8 @@ void gpiod_set_raw_array_cansleep(unsigned int array_size, int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); +int gpiod_set_drive(struct gpio_desc *desc, unsigned mode); + int gpiod_is_active_low(const struct gpio_desc *desc); int gpiod_cansleep(const struct gpio_desc *desc); @@ -375,6 +377,15 @@ static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) return -ENOSYS; } + +static inline int gpiod_set_drive(unsigned gpio, unsigned mode) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return -ENOSYS; +} + + static inline int gpiod_is_active_low(const struct gpio_desc *desc) { /* GPIO can never have been requested */ diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index f1b3659..2f19b34 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -97,6 +97,8 @@ struct gpio_chip { int (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce); + int (*set_drive)(struct gpio_chip *chip, + unsigned offset, unsigned mode); int (*to_irq)(struct gpio_chip *chip, unsigned offset); -- 2.5.0 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto