[[linux-yocto] [PATCH 27/29] gpio-pca953x: add "drive" property.] On 05/02/2016 (Fri 06:53) Saul Wold wrote:
> From: Ismo Puustinen <[email protected]> > > 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. > > The original patch was by Josef Ahmad <[email protected]> and > made on top of kernel 3.8. > > Signed-off-by: Ismo Puustinen <[email protected]> > Signed-off-by: Saul Wold <[email protected]> Upstream status of this one is ..... ? P. -- > --- > 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 > [email protected] > https://lists.yoctoproject.org/listinfo/linux-yocto -- _______________________________________________ linux-yocto mailing list [email protected] https://lists.yoctoproject.org/listinfo/linux-yocto
