From: Pramod Kumar Date: Thu, 19 Nov 2015 03:52:19 +0000 (+0530) Subject: pinctrl: Rename gpio driver from cygnus to iproc X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=616043d58a89c0966e479a83f64ba8c0ab3e657e;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git pinctrl: Rename gpio driver from cygnus to iproc Rename gpio driver file name from pinctrl-cygnus-gpio.c to pinctrl-iproc-gpio.c to make it more generic so that all iproc based future SoCs using the same gpio block could use this driver. Signed-off-by: Pramod Kumar Reviewed-by: Ray Jui Reviewed-by: Scott Branden Acked-by: Rob Herring Signed-off-by: Linus Walleij --- diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig index cd11d4d9ad58..59495473ed53 100644 --- a/drivers/pinctrl/bcm/Kconfig +++ b/drivers/pinctrl/bcm/Kconfig @@ -20,27 +20,41 @@ config PINCTRL_BCM2835 select PINMUX select PINCONF -config PINCTRL_CYGNUS_GPIO - bool "Broadcom Cygnus GPIO (with PINCONF) driver" - depends on OF_GPIO && ARCH_BCM_CYGNUS +config PINCTRL_IPROC_GPIO + bool "Broadcom iProc GPIO (with PINCONF) driver" + depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST) select GPIOLIB_IRQCHIP select PINCONF select GENERIC_PINCONF - default ARCH_BCM_CYGNUS + default ARCH_BCM_IPROC help - Say yes here to enable the Broadcom Cygnus GPIO driver. + Say yes here to enable the Broadcom iProc GPIO driver. + + The Broadcom iProc based SoCs- Cygnus, NS2, NSP and Stingray, use + same GPIO Controller IP hence this driver could be used for all. The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and the always-ON GPIO controller (CRMU/AON). All 3 GPIO controllers are supported by this driver. - All 3 Cygnus GPIO controllers support basic PINCONF functions such + The Broadcom NSP has two GPIO controllers including the ChipcommonA + GPIO, the ChipcommonB GPIO. Later controller is supported by this + driver. + + The Broadcom NS2 has two GPIO controller including the CRMU GPIO, + the ChipcommonG GPIO. Both controllers are supported by this driver. + + The Broadcom Stingray GPIO controllers are supported by this driver. + + All above SoCs GPIO controllers support basic PINCONF functions such as bias pull up, pull down, and drive strength configurations, when these pins are muxed to GPIO. - Pins from the ASIU GPIO can be individually muxed to GPIO function, - through interaction with the Cygnus IOMUX controller. + It provides the framework where pins from the individual GPIO can be + individually muxed to GPIO function, through interaction with the + SoCs IOMUX controller. This features could be used only on SoCs which + support individual pin muxing. config PINCTRL_CYGNUS_MUX bool "Broadcom Cygnus IOMUX driver" diff --git a/drivers/pinctrl/bcm/Makefile b/drivers/pinctrl/bcm/Makefile index 2b2f70ee804c..9ac6370f1c92 100644 --- a/drivers/pinctrl/bcm/Makefile +++ b/drivers/pinctrl/bcm/Makefile @@ -2,5 +2,5 @@ obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o -obj-$(CONFIG_PINCTRL_CYGNUS_GPIO) += pinctrl-cygnus-gpio.o +obj-$(CONFIG_PINCTRL_IPROC_GPIO) += pinctrl-iproc-gpio.o obj-$(CONFIG_PINCTRL_CYGNUS_MUX) += pinctrl-cygnus-mux.o diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c b/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c deleted file mode 100644 index fa66c3e05cc3..000000000000 --- a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c +++ /dev/null @@ -1,759 +0,0 @@ -/* - * Copyright (C) 2014-2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file contains the Broadcom Iproc GPIO driver that supports 3 - * GPIO controllers on Iproc including the ASIU GPIO controller, the - * chipCommonG GPIO controller, and the always-on GPIO controller. Basic - * PINCONF such as bias pull up/down, and drive strength are also supported - * in this driver. - * - * It provides the functionality where pins from the GPIO can be - * individually muxed to GPIO function, if individual pad - * configuration is supported, through the interaction with respective - * SoCs IOMUX controller. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pinctrl-utils.h" - -#define IPROC_GPIO_DATA_IN_OFFSET 0x00 -#define IPROC_GPIO_DATA_OUT_OFFSET 0x04 -#define IPROC_GPIO_OUT_EN_OFFSET 0x08 -#define IPROC_GPIO_INT_TYPE_OFFSET 0x0c -#define IPROC_GPIO_INT_DE_OFFSET 0x10 -#define IPROC_GPIO_INT_EDGE_OFFSET 0x14 -#define IPROC_GPIO_INT_MSK_OFFSET 0x18 -#define IPROC_GPIO_INT_STAT_OFFSET 0x1c -#define IPROC_GPIO_INT_MSTAT_OFFSET 0x20 -#define IPROC_GPIO_INT_CLR_OFFSET 0x24 -#define IPROC_GPIO_PAD_RES_OFFSET 0x34 -#define IPROC_GPIO_RES_EN_OFFSET 0x38 - -/* drive strength control for ASIU GPIO */ -#define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 - -/* drive strength control for CCM/CRMU (AON) GPIO */ -#define IPROC_GPIO_DRV0_CTRL_OFFSET 0x00 - -#define GPIO_BANK_SIZE 0x200 -#define NGPIOS_PER_BANK 32 -#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) - -#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) -#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) - -#define GPIO_DRV_STRENGTH_BIT_SHIFT 20 -#define GPIO_DRV_STRENGTH_BITS 3 -#define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) - -/* - * Iproc GPIO core - * - * @dev: pointer to device - * @base: I/O register base for Iproc GPIO controller - * @io_ctrl: I/O register base for certain type of Iproc GPIO controller that - * has the PINCONF support implemented outside of the GPIO block - * @lock: lock to protect access to I/O registers - * @gc: GPIO chip - * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs - * @pinmux_is_supported: flag to indicate this GPIO controller contains pins - * that can be individually muxed to GPIO - * @pctl: pointer to pinctrl_dev - * @pctldesc: pinctrl descriptor - */ -struct iproc_gpio { - struct device *dev; - - void __iomem *base; - void __iomem *io_ctrl; - - spinlock_t lock; - - struct gpio_chip gc; - unsigned num_banks; - - bool pinmux_is_supported; - - struct pinctrl_dev *pctl; - struct pinctrl_desc pctldesc; -}; - -static inline struct iproc_gpio *to_iproc_gpio(struct gpio_chip *gc) -{ - return container_of(gc, struct iproc_gpio, gc); -} - -/* - * Mapping from PINCONF pins to GPIO pins is 1-to-1 - */ -static inline unsigned iproc_pin_to_gpio(unsigned pin) -{ - return pin; -} - -/** - * iproc_set_bit - set or clear one bit (corresponding to the GPIO pin) in a - * Iproc GPIO register - * - * @iproc_gpio: Iproc GPIO device - * @reg: register offset - * @gpio: GPIO pin - * @set: set or clear - */ -static inline void iproc_set_bit(struct iproc_gpio *chip, unsigned int reg, - unsigned gpio, bool set) -{ - unsigned int offset = IPROC_GPIO_REG(gpio, reg); - unsigned int shift = IPROC_GPIO_SHIFT(gpio); - u32 val; - - val = readl(chip->base + offset); - if (set) - val |= BIT(shift); - else - val &= ~BIT(shift); - writel(val, chip->base + offset); -} - -static inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg, - unsigned gpio) -{ - unsigned int offset = IPROC_GPIO_REG(gpio, reg); - unsigned int shift = IPROC_GPIO_SHIFT(gpio); - - return !!(readl(chip->base + offset) & BIT(shift)); -} - -static void iproc_gpio_irq_handler(struct irq_desc *desc) -{ - struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct iproc_gpio *chip = to_iproc_gpio(gc); - struct irq_chip *irq_chip = irq_desc_get_chip(desc); - int i, bit; - - chained_irq_enter(irq_chip, desc); - - /* go through the entire GPIO banks and handle all interrupts */ - for (i = 0; i < chip->num_banks; i++) { - unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) + - IPROC_GPIO_INT_MSTAT_OFFSET); - - for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { - unsigned pin = NGPIOS_PER_BANK * i + bit; - int child_irq = irq_find_mapping(gc->irqdomain, pin); - - /* - * Clear the interrupt before invoking the - * handler, so we do not leave any window - */ - writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + - IPROC_GPIO_INT_CLR_OFFSET); - - generic_handle_irq(child_irq); - } - } - - chained_irq_exit(irq_chip, desc); -} - - -static void iproc_gpio_irq_ack(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned gpio = d->hwirq; - unsigned int offset = IPROC_GPIO_REG(gpio, - IPROC_GPIO_INT_CLR_OFFSET); - unsigned int shift = IPROC_GPIO_SHIFT(gpio); - u32 val = BIT(shift); - - writel(val, chip->base + offset); -} - -/** - * iproc_gpio_irq_set_mask - mask/unmask a GPIO interrupt - * - * @d: IRQ chip data - * @unmask: mask/unmask GPIO interrupt - */ -static void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned gpio = d->hwirq; - - iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask); -} - -static void iproc_gpio_irq_mask(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - iproc_gpio_irq_set_mask(d, false); - spin_unlock_irqrestore(&chip->lock, flags); -} - -static void iproc_gpio_irq_unmask(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - iproc_gpio_irq_set_mask(d, true); - spin_unlock_irqrestore(&chip->lock, flags); -} - -static int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned gpio = d->hwirq; - bool level_triggered = false; - bool dual_edge = false; - bool rising_or_high = false; - unsigned long flags; - - switch (type & IRQ_TYPE_SENSE_MASK) { - case IRQ_TYPE_EDGE_RISING: - rising_or_high = true; - break; - - case IRQ_TYPE_EDGE_FALLING: - break; - - case IRQ_TYPE_EDGE_BOTH: - dual_edge = true; - break; - - case IRQ_TYPE_LEVEL_HIGH: - level_triggered = true; - rising_or_high = true; - break; - - case IRQ_TYPE_LEVEL_LOW: - level_triggered = true; - break; - - default: - dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", - type); - return -EINVAL; - } - - spin_lock_irqsave(&chip->lock, flags); - iproc_set_bit(chip, IPROC_GPIO_INT_TYPE_OFFSET, gpio, - level_triggered); - iproc_set_bit(chip, IPROC_GPIO_INT_DE_OFFSET, gpio, dual_edge); - iproc_set_bit(chip, IPROC_GPIO_INT_EDGE_OFFSET, gpio, - rising_or_high); - spin_unlock_irqrestore(&chip->lock, flags); - - dev_dbg(chip->dev, - "gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d\n", - gpio, level_triggered, dual_edge, rising_or_high); - - return 0; -} - -static struct irq_chip iproc_gpio_irq_chip = { - .name = "bcm-iproc-gpio", - .irq_ack = iproc_gpio_irq_ack, - .irq_mask = iproc_gpio_irq_mask, - .irq_unmask = iproc_gpio_irq_unmask, - .irq_set_type = iproc_gpio_irq_set_type, -}; - -/* - * Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO - */ -static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) -{ - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned gpio = gc->base + offset; - - /* not all Iproc GPIO pins can be muxed individually */ - if (!chip->pinmux_is_supported) - return 0; - - return pinctrl_request_gpio(gpio); -} - -static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) -{ - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned gpio = gc->base + offset; - - if (!chip->pinmux_is_supported) - return; - - pinctrl_free_gpio(gpio); -} - -static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) -{ - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, false); - spin_unlock_irqrestore(&chip->lock, flags); - - dev_dbg(chip->dev, "gpio:%u set input\n", gpio); - - return 0; -} - -static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, - int val) -{ - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, true); - iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); - spin_unlock_irqrestore(&chip->lock, flags); - - dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); - - return 0; -} - -static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) -{ - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); - spin_unlock_irqrestore(&chip->lock, flags); - - dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); -} - -static int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) -{ - struct iproc_gpio *chip = to_iproc_gpio(gc); - unsigned int offset = IPROC_GPIO_REG(gpio, - IPROC_GPIO_DATA_IN_OFFSET); - unsigned int shift = IPROC_GPIO_SHIFT(gpio); - - return !!(readl(chip->base + offset) & BIT(shift)); -} - -static int iproc_get_groups_count(struct pinctrl_dev *pctldev) -{ - return 1; -} - -/* - * Only one group: "gpio_grp", since this local pinctrl device only performs - * GPIO specific PINCONF configurations - */ -static const char *iproc_get_group_name(struct pinctrl_dev *pctldev, - unsigned selector) -{ - return "gpio_grp"; -} - -static const struct pinctrl_ops iproc_pctrl_ops = { - .get_groups_count = iproc_get_groups_count, - .get_group_name = iproc_get_group_name, - .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, - .dt_free_map = pinctrl_utils_dt_free_map, -}; - -static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, - bool disable, bool pull_up) -{ - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - - if (disable) { - iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, false); - } else { - iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, - pull_up); - iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, true); - } - - spin_unlock_irqrestore(&chip->lock, flags); - - dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); - - return 0; -} - -static void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio, - bool *disable, bool *pull_up) -{ - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio); - *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio); - spin_unlock_irqrestore(&chip->lock, flags); -} - -static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, - unsigned strength) -{ - void __iomem *base; - unsigned int i, offset, shift; - u32 val; - unsigned long flags; - - /* make sure drive strength is supported */ - if (strength < 2 || strength > 16 || (strength % 2)) - return -ENOTSUPP; - - if (chip->io_ctrl) { - base = chip->io_ctrl; - offset = IPROC_GPIO_DRV0_CTRL_OFFSET; - } else { - base = chip->base; - offset = IPROC_GPIO_REG(gpio, - IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); - } - - shift = IPROC_GPIO_SHIFT(gpio); - - dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, - strength); - - spin_lock_irqsave(&chip->lock, flags); - strength = (strength / 2) - 1; - for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { - val = readl(base + offset); - val &= ~BIT(shift); - val |= ((strength >> i) & 0x1) << shift; - writel(val, base + offset); - offset += 4; - } - spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, - u16 *strength) -{ - void __iomem *base; - unsigned int i, offset, shift; - u32 val; - unsigned long flags; - - if (chip->io_ctrl) { - base = chip->io_ctrl; - offset = IPROC_GPIO_DRV0_CTRL_OFFSET; - } else { - base = chip->base; - offset = IPROC_GPIO_REG(gpio, - IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); - } - - shift = IPROC_GPIO_SHIFT(gpio); - - spin_lock_irqsave(&chip->lock, flags); - *strength = 0; - for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { - val = readl(base + offset) & BIT(shift); - val >>= shift; - *strength += (val << i); - offset += 4; - } - - /* convert to mA */ - *strength = (*strength + 1) * 2; - spin_unlock_irqrestore(&chip->lock, flags); - - return 0; -} - -static int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long *config) -{ - struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); - enum pin_config_param param = pinconf_to_config_param(*config); - unsigned gpio = iproc_pin_to_gpio(pin); - u16 arg; - bool disable, pull_up; - int ret; - - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: - iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); - if (disable) - return 0; - else - return -EINVAL; - - case PIN_CONFIG_BIAS_PULL_UP: - iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); - if (!disable && pull_up) - return 0; - else - return -EINVAL; - - case PIN_CONFIG_BIAS_PULL_DOWN: - iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); - if (!disable && !pull_up) - return 0; - else - return -EINVAL; - - case PIN_CONFIG_DRIVE_STRENGTH: - ret = iproc_gpio_get_strength(chip, gpio, &arg); - if (ret) - return ret; - else - *config = pinconf_to_config_packed(param, arg); - - return 0; - - default: - return -ENOTSUPP; - } - - return -ENOTSUPP; -} - -static int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long *configs, unsigned num_configs) -{ - struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); - enum pin_config_param param; - u16 arg; - unsigned i, gpio = iproc_pin_to_gpio(pin); - int ret = -ENOTSUPP; - - for (i = 0; i < num_configs; i++) { - param = pinconf_to_config_param(configs[i]); - arg = pinconf_to_config_argument(configs[i]); - - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: - ret = iproc_gpio_set_pull(chip, gpio, true, false); - if (ret < 0) - goto out; - break; - - case PIN_CONFIG_BIAS_PULL_UP: - ret = iproc_gpio_set_pull(chip, gpio, false, true); - if (ret < 0) - goto out; - break; - - case PIN_CONFIG_BIAS_PULL_DOWN: - ret = iproc_gpio_set_pull(chip, gpio, false, false); - if (ret < 0) - goto out; - break; - - case PIN_CONFIG_DRIVE_STRENGTH: - ret = iproc_gpio_set_strength(chip, gpio, arg); - if (ret < 0) - goto out; - break; - - default: - dev_err(chip->dev, "invalid configuration\n"); - return -ENOTSUPP; - } - } /* for each config */ - -out: - return ret; -} - -static const struct pinconf_ops iproc_pconf_ops = { - .is_generic = true, - .pin_config_get = iproc_pin_config_get, - .pin_config_set = iproc_pin_config_set, -}; - -/* - * Iproc GPIO controller supports some PINCONF related configurations such as - * pull up, pull down, and drive strength, when the pin is configured to GPIO - * - * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the - * local GPIO pins - */ -static int iproc_gpio_register_pinconf(struct iproc_gpio *chip) -{ - struct pinctrl_desc *pctldesc = &chip->pctldesc; - struct pinctrl_pin_desc *pins; - struct gpio_chip *gc = &chip->gc; - int i; - - pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); - if (!pins) - return -ENOMEM; - - for (i = 0; i < gc->ngpio; i++) { - pins[i].number = i; - pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, - "gpio-%d", i); - if (!pins[i].name) - return -ENOMEM; - } - - pctldesc->name = dev_name(chip->dev); - pctldesc->pctlops = &iproc_pctrl_ops; - pctldesc->pins = pins; - pctldesc->npins = gc->ngpio; - pctldesc->confops = &iproc_pconf_ops; - - chip->pctl = pinctrl_register(pctldesc, chip->dev, chip); - if (IS_ERR(chip->pctl)) { - dev_err(chip->dev, "unable to register pinctrl device\n"); - return PTR_ERR(chip->pctl); - } - - return 0; -} - -static void iproc_gpio_unregister_pinconf(struct cygnus_gpio *chip) -{ - pinctrl_unregister(chip->pctl); -} - -static const struct of_device_id iproc_gpio_of_match[] = { - { .compatible = "brcm,cygnus-ccm-gpio" }, - { .compatible = "brcm,cygnus-asiu-gpio" }, - { .compatible = "brcm,cygnus-crmu-gpio" }, - { .compatible = "brcm,iproc-gpio" }, - { } -}; - -static int iproc_gpio_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *res; - struct iproc_gpio *chip; - struct gpio_chip *gc; - u32 ngpios; - int irq, ret; - - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->dev = dev; - platform_set_drvdata(pdev, chip); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - chip->base = devm_ioremap_resource(dev, res); - if (IS_ERR(chip->base)) { - dev_err(dev, "unable to map I/O memory\n"); - return PTR_ERR(chip->base); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - chip->io_ctrl = devm_ioremap_resource(dev, res); - if (IS_ERR(chip->io_ctrl)) { - dev_err(dev, "unable to map I/O memory\n"); - return PTR_ERR(chip->io_ctrl); - } - } - - if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { - dev_err(&pdev->dev, "missing ngpios DT property\n"); - return -ENODEV; - } - - spin_lock_init(&chip->lock); - - gc = &chip->gc; - gc->base = -1; - gc->ngpio = ngpios; - chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; - gc->label = dev_name(dev); - gc->dev = dev; - gc->of_node = dev->of_node; - gc->request = iproc_gpio_request; - gc->free = iproc_gpio_free; - gc->direction_input = iproc_gpio_direction_input; - gc->direction_output = iproc_gpio_direction_output; - gc->set = iproc_gpio_set; - gc->get = iproc_gpio_get; - - chip->pinmux_is_supported = of_property_read_bool(dev->of_node, - "gpio-ranges"); - - ret = gpiochip_add(gc); - if (ret < 0) { - dev_err(dev, "unable to add GPIO chip\n"); - return ret; - } - - ret = iproc_gpio_register_pinconf(chip); - if (ret) { - dev_err(dev, "unable to register pinconf\n"); - goto err_rm_gpiochip; - } - - /* optional GPIO interrupt support */ - irq = platform_get_irq(pdev, 0); - if (irq) { - ret = gpiochip_irqchip_add(gc, &iproc_gpio_irq_chip, 0, - handle_simple_irq, IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "no GPIO irqchip\n"); - goto err_unregister_pinconf; - } - - gpiochip_set_chained_irqchip(gc, &iproc_gpio_irq_chip, irq, - iproc_gpio_irq_handler); - } - - return 0; - -err_unregister_pinconf: - iproc_gpio_unregister_pinconf(chip); - -err_rm_gpiochip: - gpiochip_remove(gc); - - return ret; -} - -static struct platform_driver iproc_gpio_driver = { - .driver = { - .name = "iproc-gpio", - .of_match_table = iproc_gpio_of_match, - }, - .probe = iproc_gpio_probe, -}; - -static int __init iproc_gpio_init(void) -{ - return platform_driver_probe(&iproc_gpio_driver, iproc_gpio_probe); -} -arch_initcall_sync(iproc_gpio_init); diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c new file mode 100644 index 000000000000..525ea138d9ca --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -0,0 +1,758 @@ +/* + * Copyright (C) 2014-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This file contains the Broadcom Iproc GPIO driver that supports 3 + * GPIO controllers on Iproc including the ASIU GPIO controller, the + * chipCommonG GPIO controller, and the always-on GPIO controller. Basic + * PINCONF such as bias pull up/down, and drive strength are also supported + * in this driver. + * + * It provides the functionality where pins from the GPIO can be + * individually muxed to GPIO function, if individual pad + * configuration is supported, through the interaction with respective + * SoCs IOMUX controller. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pinctrl-utils.h" + +#define IPROC_GPIO_DATA_IN_OFFSET 0x00 +#define IPROC_GPIO_DATA_OUT_OFFSET 0x04 +#define IPROC_GPIO_OUT_EN_OFFSET 0x08 +#define IPROC_GPIO_INT_TYPE_OFFSET 0x0c +#define IPROC_GPIO_INT_DE_OFFSET 0x10 +#define IPROC_GPIO_INT_EDGE_OFFSET 0x14 +#define IPROC_GPIO_INT_MSK_OFFSET 0x18 +#define IPROC_GPIO_INT_STAT_OFFSET 0x1c +#define IPROC_GPIO_INT_MSTAT_OFFSET 0x20 +#define IPROC_GPIO_INT_CLR_OFFSET 0x24 +#define IPROC_GPIO_PAD_RES_OFFSET 0x34 +#define IPROC_GPIO_RES_EN_OFFSET 0x38 + +/* drive strength control for ASIU GPIO */ +#define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 + +/* drive strength control for CCM/CRMU (AON) GPIO */ +#define IPROC_GPIO_DRV0_CTRL_OFFSET 0x00 + +#define GPIO_BANK_SIZE 0x200 +#define NGPIOS_PER_BANK 32 +#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) + +#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) +#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) + +#define GPIO_DRV_STRENGTH_BIT_SHIFT 20 +#define GPIO_DRV_STRENGTH_BITS 3 +#define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) + +/* + * Iproc GPIO core + * + * @dev: pointer to device + * @base: I/O register base for Iproc GPIO controller + * @io_ctrl: I/O register base for certain type of Iproc GPIO controller that + * has the PINCONF support implemented outside of the GPIO block + * @lock: lock to protect access to I/O registers + * @gc: GPIO chip + * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs + * @pinmux_is_supported: flag to indicate this GPIO controller contains pins + * that can be individually muxed to GPIO + * @pctl: pointer to pinctrl_dev + * @pctldesc: pinctrl descriptor + */ +struct iproc_gpio { + struct device *dev; + + void __iomem *base; + void __iomem *io_ctrl; + + spinlock_t lock; + + struct gpio_chip gc; + unsigned num_banks; + + bool pinmux_is_supported; + + struct pinctrl_dev *pctl; + struct pinctrl_desc pctldesc; +}; + +static inline struct iproc_gpio *to_iproc_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct iproc_gpio, gc); +} + +/* + * Mapping from PINCONF pins to GPIO pins is 1-to-1 + */ +static inline unsigned iproc_pin_to_gpio(unsigned pin) +{ + return pin; +} + +/** + * iproc_set_bit - set or clear one bit (corresponding to the GPIO pin) in a + * Iproc GPIO register + * + * @iproc_gpio: Iproc GPIO device + * @reg: register offset + * @gpio: GPIO pin + * @set: set or clear + */ +static inline void iproc_set_bit(struct iproc_gpio *chip, unsigned int reg, + unsigned gpio, bool set) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, reg); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + u32 val; + + val = readl(chip->base + offset); + if (set) + val |= BIT(shift); + else + val &= ~BIT(shift); + writel(val, chip->base + offset); +} + +static inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg, + unsigned gpio) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, reg); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + + return !!(readl(chip->base + offset) & BIT(shift)); +} + +static void iproc_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct iproc_gpio *chip = to_iproc_gpio(gc); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); + int i, bit; + + chained_irq_enter(irq_chip, desc); + + /* go through the entire GPIO banks and handle all interrupts */ + for (i = 0; i < chip->num_banks; i++) { + unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) + + IPROC_GPIO_INT_MSTAT_OFFSET); + + for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { + unsigned pin = NGPIOS_PER_BANK * i + bit; + int child_irq = irq_find_mapping(gc->irqdomain, pin); + + /* + * Clear the interrupt before invoking the + * handler, so we do not leave any window + */ + writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + + IPROC_GPIO_INT_CLR_OFFSET); + + generic_handle_irq(child_irq); + } + } + + chained_irq_exit(irq_chip, desc); +} + + +static void iproc_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned gpio = d->hwirq; + unsigned int offset = IPROC_GPIO_REG(gpio, + IPROC_GPIO_INT_CLR_OFFSET); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + u32 val = BIT(shift); + + writel(val, chip->base + offset); +} + +/** + * iproc_gpio_irq_set_mask - mask/unmask a GPIO interrupt + * + * @d: IRQ chip data + * @unmask: mask/unmask GPIO interrupt + */ +static void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned gpio = d->hwirq; + + iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask); +} + +static void iproc_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + iproc_gpio_irq_set_mask(d, false); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void iproc_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + iproc_gpio_irq_set_mask(d, true); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned gpio = d->hwirq; + bool level_triggered = false; + bool dual_edge = false; + bool rising_or_high = false; + unsigned long flags; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + rising_or_high = true; + break; + + case IRQ_TYPE_EDGE_FALLING: + break; + + case IRQ_TYPE_EDGE_BOTH: + dual_edge = true; + break; + + case IRQ_TYPE_LEVEL_HIGH: + level_triggered = true; + rising_or_high = true; + break; + + case IRQ_TYPE_LEVEL_LOW: + level_triggered = true; + break; + + default: + dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", + type); + return -EINVAL; + } + + spin_lock_irqsave(&chip->lock, flags); + iproc_set_bit(chip, IPROC_GPIO_INT_TYPE_OFFSET, gpio, + level_triggered); + iproc_set_bit(chip, IPROC_GPIO_INT_DE_OFFSET, gpio, dual_edge); + iproc_set_bit(chip, IPROC_GPIO_INT_EDGE_OFFSET, gpio, + rising_or_high); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, + "gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d\n", + gpio, level_triggered, dual_edge, rising_or_high); + + return 0; +} + +static struct irq_chip iproc_gpio_irq_chip = { + .name = "bcm-iproc-gpio", + .irq_ack = iproc_gpio_irq_ack, + .irq_mask = iproc_gpio_irq_mask, + .irq_unmask = iproc_gpio_irq_unmask, + .irq_set_type = iproc_gpio_irq_set_type, +}; + +/* + * Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO + */ +static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned gpio = gc->base + offset; + + /* not all Iproc GPIO pins can be muxed individually */ + if (!chip->pinmux_is_supported) + return 0; + + return pinctrl_request_gpio(gpio); +} + +static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned gpio = gc->base + offset; + + if (!chip->pinmux_is_supported) + return; + + pinctrl_free_gpio(gpio); +} + +static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) +{ + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, false); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set input\n", gpio); + + return 0; +} + +static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, + int val) +{ + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, true); + iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); + + return 0; +} + +static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) +{ + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); +} + +static int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) +{ + struct iproc_gpio *chip = to_iproc_gpio(gc); + unsigned int offset = IPROC_GPIO_REG(gpio, + IPROC_GPIO_DATA_IN_OFFSET); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + + return !!(readl(chip->base + offset) & BIT(shift)); +} + +static int iproc_get_groups_count(struct pinctrl_dev *pctldev) +{ + return 1; +} + +/* + * Only one group: "gpio_grp", since this local pinctrl device only performs + * GPIO specific PINCONF configurations + */ +static const char *iproc_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return "gpio_grp"; +} + +static const struct pinctrl_ops iproc_pctrl_ops = { + .get_groups_count = iproc_get_groups_count, + .get_group_name = iproc_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_dt_free_map, +}; + +static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, + bool disable, bool pull_up) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (disable) { + iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, false); + } else { + iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, + pull_up); + iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, true); + } + + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); + + return 0; +} + +static void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio, + bool *disable, bool *pull_up) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio); + *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, + unsigned strength) +{ + void __iomem *base; + unsigned int i, offset, shift; + u32 val; + unsigned long flags; + + /* make sure drive strength is supported */ + if (strength < 2 || strength > 16 || (strength % 2)) + return -ENOTSUPP; + + if (chip->io_ctrl) { + base = chip->io_ctrl; + offset = IPROC_GPIO_DRV0_CTRL_OFFSET; + } else { + base = chip->base; + offset = IPROC_GPIO_REG(gpio, + IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); + } + + shift = IPROC_GPIO_SHIFT(gpio); + + dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, + strength); + + spin_lock_irqsave(&chip->lock, flags); + strength = (strength / 2) - 1; + for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { + val = readl(base + offset); + val &= ~BIT(shift); + val |= ((strength >> i) & 0x1) << shift; + writel(val, base + offset); + offset += 4; + } + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, + u16 *strength) +{ + void __iomem *base; + unsigned int i, offset, shift; + u32 val; + unsigned long flags; + + if (chip->io_ctrl) { + base = chip->io_ctrl; + offset = IPROC_GPIO_DRV0_CTRL_OFFSET; + } else { + base = chip->base; + offset = IPROC_GPIO_REG(gpio, + IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); + } + + shift = IPROC_GPIO_SHIFT(gpio); + + spin_lock_irqsave(&chip->lock, flags); + *strength = 0; + for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { + val = readl(base + offset) & BIT(shift); + val >>= shift; + *strength += (val << i); + offset += 4; + } + + /* convert to mA */ + *strength = (*strength + 1) * 2; + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned gpio = iproc_pin_to_gpio(pin); + u16 arg; + bool disable, pull_up; + int ret; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (disable) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_UP: + iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (!disable && pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_DOWN: + iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (!disable && !pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = iproc_gpio_get_strength(chip, gpio, &arg); + if (ret) + return ret; + *config = pinconf_to_config_packed(param, arg); + + return 0; + + default: + return -ENOTSUPP; + } + + return -ENOTSUPP; +} + +static int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *configs, unsigned num_configs) +{ + struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + u16 arg; + unsigned i, gpio = iproc_pin_to_gpio(pin); + int ret = -ENOTSUPP; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + ret = iproc_gpio_set_pull(chip, gpio, true, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + ret = iproc_gpio_set_pull(chip, gpio, false, true); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = iproc_gpio_set_pull(chip, gpio, false, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = iproc_gpio_set_strength(chip, gpio, arg); + if (ret < 0) + goto out; + break; + + default: + dev_err(chip->dev, "invalid configuration\n"); + return -ENOTSUPP; + } + } /* for each config */ + +out: + return ret; +} + +static const struct pinconf_ops iproc_pconf_ops = { + .is_generic = true, + .pin_config_get = iproc_pin_config_get, + .pin_config_set = iproc_pin_config_set, +}; + +/* + * Iproc GPIO controller supports some PINCONF related configurations such as + * pull up, pull down, and drive strength, when the pin is configured to GPIO + * + * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the + * local GPIO pins + */ +static int iproc_gpio_register_pinconf(struct iproc_gpio *chip) +{ + struct pinctrl_desc *pctldesc = &chip->pctldesc; + struct pinctrl_pin_desc *pins; + struct gpio_chip *gc = &chip->gc; + int i; + + pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < gc->ngpio; i++) { + pins[i].number = i; + pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, + "gpio-%d", i); + if (!pins[i].name) + return -ENOMEM; + } + + pctldesc->name = dev_name(chip->dev); + pctldesc->pctlops = &iproc_pctrl_ops; + pctldesc->pins = pins; + pctldesc->npins = gc->ngpio; + pctldesc->confops = &iproc_pconf_ops; + + chip->pctl = pinctrl_register(pctldesc, chip->dev, chip); + if (IS_ERR(chip->pctl)) { + dev_err(chip->dev, "unable to register pinctrl device\n"); + return PTR_ERR(chip->pctl); + } + + return 0; +} + +static void iproc_gpio_unregister_pinconf(struct cygnus_gpio *chip) +{ + pinctrl_unregister(chip->pctl); +} + +static const struct of_device_id iproc_gpio_of_match[] = { + { .compatible = "brcm,cygnus-ccm-gpio" }, + { .compatible = "brcm,cygnus-asiu-gpio" }, + { .compatible = "brcm,cygnus-crmu-gpio" }, + { .compatible = "brcm,iproc-gpio" }, + { } +}; + +static int iproc_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct iproc_gpio *chip; + struct gpio_chip *gc; + u32 ngpios; + int irq, ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + platform_set_drvdata(pdev, chip); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->base)) { + dev_err(dev, "unable to map I/O memory\n"); + return PTR_ERR(chip->base); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + chip->io_ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->io_ctrl)) { + dev_err(dev, "unable to map I/O memory\n"); + return PTR_ERR(chip->io_ctrl); + } + } + + if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { + dev_err(&pdev->dev, "missing ngpios DT property\n"); + return -ENODEV; + } + + spin_lock_init(&chip->lock); + + gc = &chip->gc; + gc->base = -1; + gc->ngpio = ngpios; + chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; + gc->label = dev_name(dev); + gc->dev = dev; + gc->of_node = dev->of_node; + gc->request = iproc_gpio_request; + gc->free = iproc_gpio_free; + gc->direction_input = iproc_gpio_direction_input; + gc->direction_output = iproc_gpio_direction_output; + gc->set = iproc_gpio_set; + gc->get = iproc_gpio_get; + + chip->pinmux_is_supported = of_property_read_bool(dev->of_node, + "gpio-ranges"); + + ret = gpiochip_add(gc); + if (ret < 0) { + dev_err(dev, "unable to add GPIO chip\n"); + return ret; + } + + ret = iproc_gpio_register_pinconf(chip); + if (ret) { + dev_err(dev, "unable to register pinconf\n"); + goto err_rm_gpiochip; + } + + /* optional GPIO interrupt support */ + irq = platform_get_irq(pdev, 0); + if (irq) { + ret = gpiochip_irqchip_add(gc, &iproc_gpio_irq_chip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret) { + dev_err(dev, "no GPIO irqchip\n"); + goto err_unregister_pinconf; + } + + gpiochip_set_chained_irqchip(gc, &iproc_gpio_irq_chip, irq, + iproc_gpio_irq_handler); + } + + return 0; + +err_unregister_pinconf: + iproc_gpio_unregister_pinconf(chip); + +err_rm_gpiochip: + gpiochip_remove(gc); + + return ret; +} + +static struct platform_driver iproc_gpio_driver = { + .driver = { + .name = "iproc-gpio", + .of_match_table = iproc_gpio_of_match, + }, + .probe = iproc_gpio_probe, +}; + +static int __init iproc_gpio_init(void) +{ + return platform_driver_probe(&iproc_gpio_driver, iproc_gpio_probe); +} +arch_initcall_sync(iproc_gpio_init);