From xxxx Mon Sep 17 00:00:00 2001 From: Josef Ahmad Date: Tue, 11 Feb 2014 16:28:26 +0000 Subject: [PATCH 15/21] Quark GPIO 1/2 --- drivers/gpio/Kconfig | 7 ++- drivers/gpio/gpiolib.c | 130 ++++++++++++++++++++++++++++++++++++++++++++ include/asm-generic/gpio.h | 4 ++ include/linux/gpio.h | 10 ++++ 4 files changed, 149 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 682de75..afab416 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -243,13 +243,14 @@ config GPIO_VR41XX Say yes here to support the NEC VR4100 series General-purpose I/O Uint config GPIO_SCH - tristate "Intel SCH/TunnelCreek/Centerton GPIO" + tristate "Intel SCH/TunnelCreek/Centerton/Quark GPIO" depends on PCI && X86 select MFD_CORE select LPC_SCH help Say yes here to support GPIO interface on Intel Poulsbo SCH, - Intel Tunnel Creek processor or Intel Centerton processor. + Intel Tunnel Creek processor, Intel Centerton processor or Intel + Quark. The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are powered by the core power rail and are turned off during sleep modes (S3 and higher). The remaining four GPIOs are powered by @@ -261,6 +262,8 @@ config GPIO_SCH The Intel Centerton processor has a total of 30 GPIO pins. Twenty-one are powered by the core power rail and 9 from the suspend power supply. + The Intel Quark has 2 GPIOs powered by the core power well and 6 + form the suspend power well. config GPIO_ICH tristate "Intel ICH GPIO" diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5359ca7..5e897ff 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -60,11 +60,17 @@ struct gpio_desc { #define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */ #define FLAG_OPEN_DRAIN 8 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 9 /* Gpio is open source type */ +#define FLAG_PULLUP 10 /* Gpio drive is resistive pullup */ +#define FLAG_PULLDOWN 11 /* Gpio drive is resistive pulldown */ +#define FLAG_STRONG 12 /* Gpio drive is strong (fast output) */ +#define FLAG_HIZ 13 /* 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)) #ifdef CONFIG_DEBUG_FS const char *label; @@ -243,6 +249,10 @@ static DEFINE_MUTEX(sysfs_lock); * * is read/write as zero/nonzero * * also affects existing and subsequent "falling" and "rising" * /edge configuration + * /drive + * * sets signal drive mode + * * is read/write as "pullup", "pulldown", "strong" or "hiz" + * */ static ssize_t gpio_direction_show(struct device *dev, @@ -573,9 +583,85 @@ static ssize_t gpio_active_low_store(struct device *dev, static const 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); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else { + if (sysfs_streq(buf, "pullup")) { + status = gpio_set_drive(gpio, GPIOF_DRIVE_PULLUP); + if (!status) { + desc->flags &= ~GPIO_DRIVE_MASK; + set_bit(FLAG_PULLUP, &desc->flags); + } + } else if (sysfs_streq(buf, "pulldown")) { + status = gpio_set_drive(gpio, GPIOF_DRIVE_PULLDOWN); + if (!status) { + desc->flags &= ~GPIO_DRIVE_MASK; + set_bit(FLAG_PULLDOWN, &desc->flags); + } + } else if (sysfs_streq(buf, "strong")) { + status = gpio_set_drive(gpio, GPIOF_DRIVE_STRONG); + if (!status) { + desc->flags &= ~GPIO_DRIVE_MASK; + set_bit(FLAG_STRONG, &desc->flags); + } + } else if (sysfs_streq(buf, "hiz")) { + status = gpio_set_drive(gpio, 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 const struct attribute *gpio_attrs[] = { &dev_attr_value.attr, &dev_attr_active_low.attr, + &dev_attr_drive.attr, NULL, }; @@ -1677,6 +1763,50 @@ fail: } EXPORT_SYMBOL_GPL(gpio_set_debounce); +/** + * gpio_set_drive - sets drive @mode for a @gpio + * @gpio: the gpio to set the drive mode + * @mode: the drive mode + */ +int gpio_set_drive(unsigned gpio, unsigned mode) +{ + unsigned long flags; + struct gpio_chip *chip; + struct gpio_desc *desc = &gpio_desc[gpio]; + int status = -EINVAL; + + spin_lock_irqsave(&gpio_lock, flags); + + if (!gpio_is_valid(gpio)) + goto fail; + chip = desc->chip; + if (!chip || !chip->set || !chip->set_drive) + goto fail; + gpio -= chip->base; + if (gpio >= chip->ngpio) + goto fail; + status = gpio_ensure_requested(desc, gpio); + if (status < 0) + goto fail; + + /* now we know the gpio is valid and chip won't vanish */ + + spin_unlock_irqrestore(&gpio_lock, flags); + + might_sleep_if(chip->can_sleep); + + return chip->set_drive(chip, gpio, mode); + +fail: + spin_unlock_irqrestore(&gpio_lock, flags); + if (status) + pr_debug("%s: gpio-%d status %d\n", + __func__, gpio, status); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_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/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 20ca766..8265ccc 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -65,6 +65,7 @@ struct device_node; * @direction_output: configures signal "offset" as output, or returns error * @set_debounce: optional hook for setting debounce time for specified gpio in * interrupt triggered gpio chips + * @set_drive: optional hook for setting the drive signal for "offset" * @set: assigns output value for signal "offset" * @to_irq: optional hook supporting non-static gpio_to_irq() mappings; * implementation may not sleep @@ -113,6 +114,8 @@ struct gpio_chip { unsigned offset, int value); int (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce); + int (*set_drive)(struct gpio_chip *chip, + unsigned offset, unsigned mode); void (*set)(struct gpio_chip *chip, unsigned offset, int value); @@ -172,6 +175,7 @@ extern int gpio_direction_input(unsigned gpio); extern int gpio_direction_output(unsigned gpio, int value); extern int gpio_set_debounce(unsigned gpio, unsigned debounce); +extern int gpio_set_drive(unsigned gpio, unsigned mode); extern int gpio_get_value_cansleep(unsigned gpio); extern void gpio_set_value_cansleep(unsigned gpio, int value); diff --git a/include/linux/gpio.h b/include/linux/gpio.h index bfe6656..cadd9d2 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -27,6 +27,11 @@ #define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT) #define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE) +#define GPIOF_DRIVE_PULLUP (1 << 6) +#define GPIOF_DRIVE_PULLDOWN (1 << 7) +#define GPIOF_DRIVE_STRONG (1 << 8) +#define GPIOF_DRIVE_HIZ (1 << 9) + /** * struct gpio - a structure describing a GPIO with configuration * @gpio: the GPIO number @@ -156,6 +161,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 */ -- 1.7.4.1