diff options
author | David S. Miller <davem@davemloft.net> | 2016-01-13 00:21:27 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-01-13 00:21:27 -0500 |
commit | ddb5388ffd0ad75d07e7b78181a0b579824ba6f0 (patch) | |
tree | be1e2bd103c69d7bbace3fffd97bc3d714bbc3d7 /drivers/pinctrl/bcm | |
parent | ccdf6ce6a8dba374668ae9b4d763e19903611c38 (diff) | |
parent | 67990608c8b95d2b8ccc29932376ae73d5818727 (diff) | |
download | linux-ddb5388ffd0ad75d07e7b78181a0b579824ba6f0.tar.gz linux-ddb5388ffd0ad75d07e7b78181a0b579824ba6f0.tar.bz2 linux-ddb5388ffd0ad75d07e7b78181a0b579824ba6f0.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
Diffstat (limited to 'drivers/pinctrl/bcm')
-rw-r--r-- | drivers/pinctrl/bcm/Kconfig | 48 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/Makefile | 3 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-bcm2835.c | 2 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-iproc-gpio.c (renamed from drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c) | 358 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-nsp-gpio.c | 749 |
5 files changed, 958 insertions, 202 deletions
diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig index cd11d4d9ad58..2cc74384cafa 100644 --- a/drivers/pinctrl/bcm/Kconfig +++ b/drivers/pinctrl/bcm/Kconfig @@ -9,6 +9,7 @@ config PINCTRL_BCM281XX select PINCONF select GENERIC_PINCONF select REGMAP_MMIO + default ARCH_BCM_MOBILE help Say Y here to support Broadcom BCM281xx pinctrl driver, which is used for the BCM281xx SoC family, including BCM11130, BCM11140, BCM11351, @@ -20,27 +21,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" @@ -54,3 +69,20 @@ config PINCTRL_CYGNUS_MUX The Broadcom Cygnus IOMUX driver supports group based IOMUX configuration, with the exception that certain individual pins can be overrided to GPIO function + +config PINCTRL_NSP_GPIO + bool "Broadcom NSP GPIO (with PINCONF) driver" + depends on OF_GPIO && (ARCH_BCM_NSP || COMPILE_TEST) + select GPIOLIB_IRQCHIP + select PINCONF + select GENERIC_PINCONF + default ARCH_BCM_NSP + help + Say yes here to enable the Broadcom NSP GPIO driver. + + The Broadcom Northstar Plus SoC ChipcommonA GPIO controller is + supported by this driver. + + The ChipcommonA GPIO controller support basic PINCONF functions such + as bias pull up, pull down, and drive strength configurations, when + these pins are muxed to GPIO. diff --git a/drivers/pinctrl/bcm/Makefile b/drivers/pinctrl/bcm/Makefile index 2b2f70ee804c..6148367d5e8c 100644 --- a/drivers/pinctrl/bcm/Makefile +++ b/drivers/pinctrl/bcm/Makefile @@ -2,5 +2,6 @@ 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 +obj-$(CONFIG_PINCTRL_NSP_GPIO) += pinctrl-nsp-gpio.o diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index 2e6ca69635aa..75b0d8c8f058 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -795,7 +795,7 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, return 0; out: - kfree(maps); + bcm2835_pctl_dt_free_map(pctldev, maps, num_pins * maps_per_pin); return err; } diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index 12a48f498b75..314591a4609b 100644 --- a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -10,14 +10,16 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * This file contains the Broadcom Cygnus GPIO driver that supports 3 - * GPIO controllers on Cygnus including the ASIU GPIO controller, the + * 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. * - * Pins from the ASIU GPIO can be individually muxed to GPIO function, - * through the interaction with the Cygnus IOMUX controller + * 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 <linux/kernel.h> @@ -34,42 +36,42 @@ #include "../pinctrl-utils.h" -#define CYGNUS_GPIO_DATA_IN_OFFSET 0x00 -#define CYGNUS_GPIO_DATA_OUT_OFFSET 0x04 -#define CYGNUS_GPIO_OUT_EN_OFFSET 0x08 -#define CYGNUS_GPIO_INT_TYPE_OFFSET 0x0c -#define CYGNUS_GPIO_INT_DE_OFFSET 0x10 -#define CYGNUS_GPIO_INT_EDGE_OFFSET 0x14 -#define CYGNUS_GPIO_INT_MSK_OFFSET 0x18 -#define CYGNUS_GPIO_INT_STAT_OFFSET 0x1c -#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20 -#define CYGNUS_GPIO_INT_CLR_OFFSET 0x24 -#define CYGNUS_GPIO_PAD_RES_OFFSET 0x34 -#define CYGNUS_GPIO_RES_EN_OFFSET 0x38 +#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 CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 +#define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 /* drive strength control for CCM/CRMU (AON) GPIO */ -#define CYGNUS_GPIO_DRV0_CTRL_OFFSET 0x00 +#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 CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) -#define CYGNUS_GPIO_SHIFT(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) /* - * Cygnus GPIO core + * Iproc GPIO core * * @dev: pointer to device - * @base: I/O register base for Cygnus GPIO controller - * @io_ctrl: I/O register base for certain type of Cygnus GPIO controller that + * @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 @@ -79,7 +81,7 @@ * @pctl: pointer to pinctrl_dev * @pctldesc: pinctrl descriptor */ -struct cygnus_gpio { +struct iproc_gpio { struct device *dev; void __iomem *base; @@ -96,33 +98,33 @@ struct cygnus_gpio { struct pinctrl_desc pctldesc; }; -static inline struct cygnus_gpio *to_cygnus_gpio(struct gpio_chip *gc) +static inline struct iproc_gpio *to_iproc_gpio(struct gpio_chip *gc) { - return container_of(gc, struct cygnus_gpio, gc); + return container_of(gc, struct iproc_gpio, gc); } /* * Mapping from PINCONF pins to GPIO pins is 1-to-1 */ -static inline unsigned cygnus_pin_to_gpio(unsigned pin) +static inline unsigned iproc_pin_to_gpio(unsigned pin) { return pin; } /** - * cygnus_set_bit - set or clear one bit (corresponding to the GPIO pin) in a - * Cygnus GPIO register + * iproc_set_bit - set or clear one bit (corresponding to the GPIO pin) in a + * Iproc GPIO register * - * @cygnus_gpio: Cygnus GPIO device + * @iproc_gpio: Iproc GPIO device * @reg: register offset * @gpio: GPIO pin * @set: set or clear */ -static inline void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg, +static inline void iproc_set_bit(struct iproc_gpio *chip, unsigned int reg, unsigned gpio, bool set) { - unsigned int offset = CYGNUS_GPIO_REG(gpio, reg); - unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); + unsigned int offset = IPROC_GPIO_REG(gpio, reg); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); u32 val; val = readl(chip->base + offset); @@ -133,19 +135,19 @@ static inline void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg, writel(val, chip->base + offset); } -static inline bool cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg, +static inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg, unsigned gpio) { - unsigned int offset = CYGNUS_GPIO_REG(gpio, reg); - unsigned int shift = CYGNUS_GPIO_SHIFT(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 cygnus_gpio_irq_handler(struct irq_desc *desc) +static void iproc_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); struct irq_chip *irq_chip = irq_desc_get_chip(desc); int i, bit; @@ -154,7 +156,7 @@ static void cygnus_gpio_irq_handler(struct irq_desc *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) + - CYGNUS_GPIO_INT_MSTAT_OFFSET); + IPROC_GPIO_INT_MSTAT_OFFSET); for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { unsigned pin = NGPIOS_PER_BANK * i + bit; @@ -165,7 +167,7 @@ static void cygnus_gpio_irq_handler(struct irq_desc *desc) * handler, so we do not leave any window */ writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + - CYGNUS_GPIO_INT_CLR_OFFSET); + IPROC_GPIO_INT_CLR_OFFSET); generic_handle_irq(child_irq); } @@ -175,60 +177,60 @@ static void cygnus_gpio_irq_handler(struct irq_desc *desc) } -static void cygnus_gpio_irq_ack(struct irq_data *d) +static void iproc_gpio_irq_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned gpio = d->hwirq; - unsigned int offset = CYGNUS_GPIO_REG(gpio, - CYGNUS_GPIO_INT_CLR_OFFSET); - unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); + 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); } /** - * cygnus_gpio_irq_set_mask - mask/unmask a GPIO interrupt + * iproc_gpio_irq_set_mask - mask/unmask a GPIO interrupt * * @d: IRQ chip data * @unmask: mask/unmask GPIO interrupt */ -static void cygnus_gpio_irq_set_mask(struct irq_data *d, bool unmask) +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 cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned gpio = d->hwirq; - cygnus_set_bit(chip, CYGNUS_GPIO_INT_MSK_OFFSET, gpio, unmask); + iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask); } -static void cygnus_gpio_irq_mask(struct irq_data *d) +static void iproc_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); - cygnus_gpio_irq_set_mask(d, false); + iproc_gpio_irq_set_mask(d, false); spin_unlock_irqrestore(&chip->lock, flags); } -static void cygnus_gpio_irq_unmask(struct irq_data *d) +static void iproc_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); - cygnus_gpio_irq_set_mask(d, true); + iproc_gpio_irq_set_mask(d, true); spin_unlock_irqrestore(&chip->lock, flags); } -static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type) +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 cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned gpio = d->hwirq; bool level_triggered = false; bool dual_edge = false; @@ -263,10 +265,10 @@ static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type) } spin_lock_irqsave(&chip->lock, flags); - cygnus_set_bit(chip, CYGNUS_GPIO_INT_TYPE_OFFSET, gpio, + iproc_set_bit(chip, IPROC_GPIO_INT_TYPE_OFFSET, gpio, level_triggered); - cygnus_set_bit(chip, CYGNUS_GPIO_INT_DE_OFFSET, gpio, dual_edge); - cygnus_set_bit(chip, CYGNUS_GPIO_INT_EDGE_OFFSET, gpio, + 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); @@ -277,32 +279,32 @@ static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type) return 0; } -static struct irq_chip cygnus_gpio_irq_chip = { - .name = "bcm-cygnus-gpio", - .irq_ack = cygnus_gpio_irq_ack, - .irq_mask = cygnus_gpio_irq_mask, - .irq_unmask = cygnus_gpio_irq_unmask, - .irq_set_type = cygnus_gpio_irq_set_type, +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 Cygnus IOMUX pinmux controller to mux individual pins to GPIO + * Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO */ -static int cygnus_gpio_request(struct gpio_chip *gc, unsigned offset) +static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) { - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned gpio = gc->base + offset; - /* not all Cygnus GPIO pins can be muxed individually */ + /* not all Iproc GPIO pins can be muxed individually */ if (!chip->pinmux_is_supported) return 0; return pinctrl_request_gpio(gpio); } -static void cygnus_gpio_free(struct gpio_chip *gc, unsigned offset) +static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) { - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned gpio = gc->base + offset; if (!chip->pinmux_is_supported) @@ -311,13 +313,13 @@ static void cygnus_gpio_free(struct gpio_chip *gc, unsigned offset) pinctrl_free_gpio(gpio); } -static int cygnus_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) +static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) { - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); - cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, false); + 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); @@ -325,15 +327,15 @@ static int cygnus_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) return 0; } -static int cygnus_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, +static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, int val) { - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); - cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, true); - cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); + 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); @@ -341,29 +343,29 @@ static int cygnus_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, return 0; } -static void cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) +static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) { - struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct iproc_gpio *chip = to_iproc_gpio(gc); unsigned long flags; spin_lock_irqsave(&chip->lock, flags); - cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); + 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 cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio) +static int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) { - struct cygnus_gpio *chip = to_cygnus_gpio(gc); - unsigned int offset = CYGNUS_GPIO_REG(gpio, - CYGNUS_GPIO_DATA_IN_OFFSET); - unsigned int shift = CYGNUS_GPIO_SHIFT(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 cygnus_get_groups_count(struct pinctrl_dev *pctldev) +static int iproc_get_groups_count(struct pinctrl_dev *pctldev) { return 1; } @@ -372,20 +374,20 @@ static int cygnus_get_groups_count(struct pinctrl_dev *pctldev) * Only one group: "gpio_grp", since this local pinctrl device only performs * GPIO specific PINCONF configurations */ -static const char *cygnus_get_group_name(struct pinctrl_dev *pctldev, +static const char *iproc_get_group_name(struct pinctrl_dev *pctldev, unsigned selector) { return "gpio_grp"; } -static const struct pinctrl_ops cygnus_pctrl_ops = { - .get_groups_count = cygnus_get_groups_count, - .get_group_name = cygnus_get_group_name, +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 cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio, +static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, bool disable, bool pull_up) { unsigned long flags; @@ -393,11 +395,11 @@ static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio, spin_lock_irqsave(&chip->lock, flags); if (disable) { - cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, false); + iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, false); } else { - cygnus_set_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio, + iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, pull_up); - cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, true); + iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, true); } spin_unlock_irqrestore(&chip->lock, flags); @@ -407,18 +409,18 @@ static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio, return 0; } -static void cygnus_gpio_get_pull(struct cygnus_gpio *chip, unsigned gpio, +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 = !cygnus_get_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio); - *pull_up = cygnus_get_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio); + *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 cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio, +static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, unsigned strength) { void __iomem *base; @@ -432,14 +434,14 @@ static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio, if (chip->io_ctrl) { base = chip->io_ctrl; - offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET; + offset = IPROC_GPIO_DRV0_CTRL_OFFSET; } else { base = chip->base; - offset = CYGNUS_GPIO_REG(gpio, - CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET); + offset = IPROC_GPIO_REG(gpio, + IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); } - shift = CYGNUS_GPIO_SHIFT(gpio); + shift = IPROC_GPIO_SHIFT(gpio); dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, strength); @@ -458,7 +460,7 @@ static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio, return 0; } -static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio, +static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, u16 *strength) { void __iomem *base; @@ -468,14 +470,14 @@ static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio, if (chip->io_ctrl) { base = chip->io_ctrl; - offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET; + offset = IPROC_GPIO_DRV0_CTRL_OFFSET; } else { base = chip->base; - offset = CYGNUS_GPIO_REG(gpio, - CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET); + offset = IPROC_GPIO_REG(gpio, + IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); } - shift = CYGNUS_GPIO_SHIFT(gpio); + shift = IPROC_GPIO_SHIFT(gpio); spin_lock_irqsave(&chip->lock, flags); *strength = 0; @@ -493,44 +495,43 @@ static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio, return 0; } -static int cygnus_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, +static int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { - struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); enum pin_config_param param = pinconf_to_config_param(*config); - unsigned gpio = cygnus_pin_to_gpio(pin); + unsigned gpio = iproc_pin_to_gpio(pin); u16 arg; bool disable, pull_up; int ret; switch (param) { case PIN_CONFIG_BIAS_DISABLE: - cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up); + iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); if (disable) return 0; else return -EINVAL; case PIN_CONFIG_BIAS_PULL_UP: - cygnus_gpio_get_pull(chip, gpio, &disable, &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: - cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up); + iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); if (!disable && !pull_up) return 0; else return -EINVAL; case PIN_CONFIG_DRIVE_STRENGTH: - ret = cygnus_gpio_get_strength(chip, gpio, &arg); + ret = iproc_gpio_get_strength(chip, gpio, &arg); if (ret) return ret; - else - *config = pinconf_to_config_packed(param, arg); + *config = pinconf_to_config_packed(param, arg); return 0; @@ -541,13 +542,13 @@ static int cygnus_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, return -ENOTSUPP; } -static int cygnus_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, +static int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs) { - struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); enum pin_config_param param; u16 arg; - unsigned i, gpio = cygnus_pin_to_gpio(pin); + unsigned i, gpio = iproc_pin_to_gpio(pin); int ret = -ENOTSUPP; for (i = 0; i < num_configs; i++) { @@ -556,25 +557,25 @@ static int cygnus_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, switch (param) { case PIN_CONFIG_BIAS_DISABLE: - ret = cygnus_gpio_set_pull(chip, gpio, true, false); + ret = iproc_gpio_set_pull(chip, gpio, true, false); if (ret < 0) goto out; break; case PIN_CONFIG_BIAS_PULL_UP: - ret = cygnus_gpio_set_pull(chip, gpio, false, true); + ret = iproc_gpio_set_pull(chip, gpio, false, true); if (ret < 0) goto out; break; case PIN_CONFIG_BIAS_PULL_DOWN: - ret = cygnus_gpio_set_pull(chip, gpio, false, false); + ret = iproc_gpio_set_pull(chip, gpio, false, false); if (ret < 0) goto out; break; case PIN_CONFIG_DRIVE_STRENGTH: - ret = cygnus_gpio_set_strength(chip, gpio, arg); + ret = iproc_gpio_set_strength(chip, gpio, arg); if (ret < 0) goto out; break; @@ -589,20 +590,20 @@ out: return ret; } -static const struct pinconf_ops cygnus_pconf_ops = { +static const struct pinconf_ops iproc_pconf_ops = { .is_generic = true, - .pin_config_get = cygnus_pin_config_get, - .pin_config_set = cygnus_pin_config_set, + .pin_config_get = iproc_pin_config_get, + .pin_config_set = iproc_pin_config_set, }; /* - * Cygnus GPIO controller supports some PINCONF related configurations such as + * 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 cygnus_gpio_register_pinconf(struct cygnus_gpio *chip) +static int iproc_gpio_register_pinconf(struct iproc_gpio *chip) { struct pinctrl_desc *pctldesc = &chip->pctldesc; struct pinctrl_pin_desc *pins; @@ -622,10 +623,10 @@ static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip) } pctldesc->name = dev_name(chip->dev); - pctldesc->pctlops = &cygnus_pctrl_ops; + pctldesc->pctlops = &iproc_pctrl_ops; pctldesc->pins = pins; pctldesc->npins = gc->ngpio; - pctldesc->confops = &cygnus_pconf_ops; + pctldesc->confops = &iproc_pconf_ops; chip->pctl = pinctrl_register(pctldesc, chip->dev, chip); if (IS_ERR(chip->pctl)) { @@ -636,59 +637,27 @@ static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip) return 0; } -static void cygnus_gpio_unregister_pinconf(struct cygnus_gpio *chip) +static void iproc_gpio_unregister_pinconf(struct iproc_gpio *chip) { - if (chip->pctl) - pinctrl_unregister(chip->pctl); + pinctrl_unregister(chip->pctl); } -struct cygnus_gpio_data { - unsigned num_gpios; -}; - -static const struct cygnus_gpio_data cygnus_cmm_gpio_data = { - .num_gpios = 24, -}; - -static const struct cygnus_gpio_data cygnus_asiu_gpio_data = { - .num_gpios = 146, +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 const struct cygnus_gpio_data cygnus_crmu_gpio_data = { - .num_gpios = 6, -}; - -static const struct of_device_id cygnus_gpio_of_match[] = { - { - .compatible = "brcm,cygnus-ccm-gpio", - .data = &cygnus_cmm_gpio_data, - }, - { - .compatible = "brcm,cygnus-asiu-gpio", - .data = &cygnus_asiu_gpio_data, - }, - { - .compatible = "brcm,cygnus-crmu-gpio", - .data = &cygnus_crmu_gpio_data, - } -}; - -static int cygnus_gpio_probe(struct platform_device *pdev) +static int iproc_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; - struct cygnus_gpio *chip; + struct iproc_gpio *chip; struct gpio_chip *gc; u32 ngpios; int irq, ret; - const struct of_device_id *match; - const struct cygnus_gpio_data *gpio_data; - - match = of_match_device(cygnus_gpio_of_match, dev); - if (!match) - return -ENODEV; - gpio_data = match->data; - ngpios = gpio_data->num_gpios; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -713,6 +682,11 @@ static int cygnus_gpio_probe(struct platform_device *pdev) } } + 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; @@ -722,12 +696,12 @@ static int cygnus_gpio_probe(struct platform_device *pdev) gc->label = dev_name(dev); gc->dev = dev; gc->of_node = dev->of_node; - gc->request = cygnus_gpio_request; - gc->free = cygnus_gpio_free; - gc->direction_input = cygnus_gpio_direction_input; - gc->direction_output = cygnus_gpio_direction_output; - gc->set = cygnus_gpio_set; - gc->get = cygnus_gpio_get; + 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"); @@ -738,7 +712,7 @@ static int cygnus_gpio_probe(struct platform_device *pdev) return ret; } - ret = cygnus_gpio_register_pinconf(chip); + ret = iproc_gpio_register_pinconf(chip); if (ret) { dev_err(dev, "unable to register pinconf\n"); goto err_rm_gpiochip; @@ -747,21 +721,21 @@ static int cygnus_gpio_probe(struct platform_device *pdev) /* optional GPIO interrupt support */ irq = platform_get_irq(pdev, 0); if (irq) { - ret = gpiochip_irqchip_add(gc, &cygnus_gpio_irq_chip, 0, + 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, &cygnus_gpio_irq_chip, irq, - cygnus_gpio_irq_handler); + gpiochip_set_chained_irqchip(gc, &iproc_gpio_irq_chip, irq, + iproc_gpio_irq_handler); } return 0; err_unregister_pinconf: - cygnus_gpio_unregister_pinconf(chip); + iproc_gpio_unregister_pinconf(chip); err_rm_gpiochip: gpiochip_remove(gc); @@ -769,16 +743,16 @@ err_rm_gpiochip: return ret; } -static struct platform_driver cygnus_gpio_driver = { +static struct platform_driver iproc_gpio_driver = { .driver = { - .name = "cygnus-gpio", - .of_match_table = cygnus_gpio_of_match, + .name = "iproc-gpio", + .of_match_table = iproc_gpio_of_match, }, - .probe = cygnus_gpio_probe, + .probe = iproc_gpio_probe, }; -static int __init cygnus_gpio_init(void) +static int __init iproc_gpio_init(void) { - return platform_driver_probe(&cygnus_gpio_driver, cygnus_gpio_probe); + return platform_driver_probe(&iproc_gpio_driver, iproc_gpio_probe); } -arch_initcall_sync(cygnus_gpio_init); +arch_initcall_sync(iproc_gpio_init); diff --git a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c new file mode 100644 index 000000000000..725c36f917f9 --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c @@ -0,0 +1,749 @@ +/* + * Copyright (C) 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 Northstar Plus (NSP) GPIO driver that + * supports the chipCommonA GPIO controller. Basic PINCONF such as bias, + * pull up/down, slew and drive strength are also supported in this driver. + * + * Pins from the chipCommonA GPIO can be individually muxed to GPIO function, + * through the interaction with the NSP IOMUX controller. + */ + +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/slab.h> + +#include "../pinctrl-utils.h" + +#define NSP_CHIP_A_INT_STATUS 0x00 +#define NSP_CHIP_A_INT_MASK 0x04 +#define NSP_GPIO_DATA_IN 0x40 +#define NSP_GPIO_DATA_OUT 0x44 +#define NSP_GPIO_OUT_EN 0x48 +#define NSP_GPIO_INT_POLARITY 0x50 +#define NSP_GPIO_INT_MASK 0x54 +#define NSP_GPIO_EVENT 0x58 +#define NSP_GPIO_EVENT_INT_MASK 0x5c +#define NSP_GPIO_EVENT_INT_POLARITY 0x64 +#define NSP_CHIP_A_GPIO_INT_BIT 0x01 + +/* I/O parameters offset for chipcommon A GPIO */ +#define NSP_GPIO_DRV_CTRL 0x00 +#define NSP_GPIO_HYSTERESIS_EN 0x10 +#define NSP_GPIO_SLEW_RATE_EN 0x14 +#define NSP_PULL_UP_EN 0x18 +#define NSP_PULL_DOWN_EN 0x1c +#define GPIO_DRV_STRENGTH_BITS 0x03 + +/* + * nsp GPIO core + * + * @dev: pointer to device + * @base: I/O register base for nsp GPIO controller + * @io_ctrl: I/O register base for PINCONF support outside the GPIO block + * @gc: GPIO chip + * @pctl: pointer to pinctrl_dev + * @pctldesc: pinctrl descriptor + * @irq_domain: pointer to irq domain + * @lock: lock to protect access to I/O registers + */ +struct nsp_gpio { + struct device *dev; + void __iomem *base; + void __iomem *io_ctrl; + struct gpio_chip gc; + struct pinctrl_dev *pctl; + struct pinctrl_desc pctldesc; + struct irq_domain *irq_domain; + spinlock_t lock; +}; + +enum base_type { + REG, + IO_CTRL +}; + +static inline struct nsp_gpio *to_nsp_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct nsp_gpio, gc); +} + +/* + * Mapping from PINCONF pins to GPIO pins is 1-to-1 + */ +static inline unsigned nsp_pin_to_gpio(unsigned pin) +{ + return pin; +} + +/* + * nsp_set_bit - set or clear one bit (corresponding to the GPIO pin) in a + * nsp GPIO register + * + * @nsp_gpio: nsp GPIO device + * @base_type: reg base to modify + * @reg: register offset + * @gpio: GPIO pin + * @set: set or clear + */ +static inline void nsp_set_bit(struct nsp_gpio *chip, enum base_type address, + unsigned int reg, unsigned gpio, bool set) +{ + u32 val; + void __iomem *base_address; + + if (address == IO_CTRL) + base_address = chip->io_ctrl; + else + base_address = chip->base; + + val = readl(base_address + reg); + if (set) + val |= BIT(gpio); + else + val &= ~BIT(gpio); + + writel(val, base_address + reg); +} + +/* + * nsp_get_bit - get one bit (corresponding to the GPIO pin) in a + * nsp GPIO register + */ +static inline bool nsp_get_bit(struct nsp_gpio *chip, enum base_type address, + unsigned int reg, unsigned gpio) +{ + if (address == IO_CTRL) + return !!(readl(chip->io_ctrl + reg) & BIT(gpio)); + else + return !!(readl(chip->base + reg) & BIT(gpio)); +} + +static irqreturn_t nsp_gpio_irq_handler(int irq, void *data) +{ + struct nsp_gpio *chip = (struct nsp_gpio *)data; + struct gpio_chip gc = chip->gc; + int bit; + unsigned long int_bits = 0; + u32 int_status; + + /* go through the entire GPIOs and handle all interrupts */ + int_status = readl(chip->base + NSP_CHIP_A_INT_STATUS); + if (int_status & NSP_CHIP_A_GPIO_INT_BIT) { + unsigned int event, level; + + /* Get level and edge interrupts */ + event = readl(chip->base + NSP_GPIO_EVENT_INT_MASK) & + readl(chip->base + NSP_GPIO_EVENT); + level = readl(chip->base + NSP_GPIO_DATA_IN) ^ + readl(chip->base + NSP_GPIO_INT_POLARITY); + level &= readl(chip->base + NSP_GPIO_INT_MASK); + int_bits = level | event; + + for_each_set_bit(bit, &int_bits, gc.ngpio) { + /* + * Clear the interrupt before invoking the + * handler, so we do not leave any window + */ + writel(BIT(bit), chip->base + NSP_GPIO_EVENT); + generic_handle_irq( + irq_linear_revmap(chip->irq_domain, bit)); + } + } + + return int_bits ? IRQ_HANDLED : IRQ_NONE; +} + +static void nsp_gpio_irq_ack(struct irq_data *d) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned gpio = d->hwirq; + u32 val = BIT(gpio); + u32 trigger_type; + + trigger_type = irq_get_trigger_type(d->irq); + if (trigger_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + nsp_set_bit(chip, REG, NSP_GPIO_EVENT, gpio, val); +} + +/* + * nsp_gpio_irq_set_mask - mask/unmask a GPIO interrupt + * + * @d: IRQ chip data + * @unmask: mask/unmask GPIO interrupt + */ +static void nsp_gpio_irq_set_mask(struct irq_data *d, bool unmask) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned gpio = d->hwirq; + u32 trigger_type; + + trigger_type = irq_get_trigger_type(d->irq); + if (trigger_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + nsp_set_bit(chip, REG, NSP_GPIO_EVENT_INT_MASK, gpio, unmask); + else + nsp_set_bit(chip, REG, NSP_GPIO_INT_MASK, gpio, unmask); +} + +static void nsp_gpio_irq_mask(struct irq_data *d) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + nsp_gpio_irq_set_mask(d, false); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void nsp_gpio_irq_unmask(struct irq_data *d) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + nsp_gpio_irq_set_mask(d, true); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int nsp_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned gpio = d->hwirq; + bool level_low; + bool falling; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + falling = nsp_get_bit(chip, REG, NSP_GPIO_EVENT_INT_POLARITY, gpio); + level_low = nsp_get_bit(chip, REG, NSP_GPIO_INT_POLARITY, gpio); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + falling = false; + break; + + case IRQ_TYPE_EDGE_FALLING: + falling = true; + break; + + case IRQ_TYPE_LEVEL_HIGH: + level_low = false; + break; + + case IRQ_TYPE_LEVEL_LOW: + level_low = true; + break; + + default: + dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", + type); + spin_unlock_irqrestore(&chip->lock, flags); + return -EINVAL; + } + + nsp_set_bit(chip, REG, NSP_GPIO_EVENT_INT_POLARITY, gpio, falling); + nsp_set_bit(chip, REG, NSP_GPIO_INT_POLARITY, gpio, level_low); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u level_low:%s falling:%s\n", gpio, + level_low ? "true" : "false", falling ? "true" : "false"); + return 0; +} + +static struct irq_chip nsp_gpio_irq_chip = { + .name = "gpio-a", + .irq_enable = nsp_gpio_irq_unmask, + .irq_disable = nsp_gpio_irq_mask, + .irq_ack = nsp_gpio_irq_ack, + .irq_mask = nsp_gpio_irq_mask, + .irq_unmask = nsp_gpio_irq_unmask, + .irq_set_type = nsp_gpio_irq_set_type, +}; + +/* + * Request the nsp IOMUX pinmux controller to mux individual pins to GPIO + */ +static int nsp_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + unsigned gpio = gc->base + offset; + + return pinctrl_request_gpio(gpio); +} + +static void nsp_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + unsigned gpio = gc->base + offset; + + pinctrl_free_gpio(gpio); +} + +static int nsp_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) +{ + struct nsp_gpio *chip = to_nsp_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + nsp_set_bit(chip, REG, NSP_GPIO_OUT_EN, gpio, false); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set input\n", gpio); + return 0; +} + +static int nsp_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, + int val) +{ + struct nsp_gpio *chip = to_nsp_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + nsp_set_bit(chip, REG, NSP_GPIO_OUT_EN, gpio, true); + nsp_set_bit(chip, REG, NSP_GPIO_DATA_OUT, 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 nsp_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) +{ + struct nsp_gpio *chip = to_nsp_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + nsp_set_bit(chip, REG, NSP_GPIO_DATA_OUT, gpio, !!(val)); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); +} + +static int nsp_gpio_get(struct gpio_chip *gc, unsigned gpio) +{ + struct nsp_gpio *chip = to_nsp_gpio(gc); + + return !!(readl(chip->base + NSP_GPIO_DATA_IN) & BIT(gpio)); +} + +static int nsp_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct nsp_gpio *chip = to_nsp_gpio(gc); + + return irq_linear_revmap(chip->irq_domain, offset); +} + +static int nsp_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 *nsp_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return "gpio_grp"; +} + +static const struct pinctrl_ops nsp_pctrl_ops = { + .get_groups_count = nsp_get_groups_count, + .get_group_name = nsp_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_dt_free_map, +}; + +static int nsp_gpio_set_slew(struct nsp_gpio *chip, unsigned gpio, u16 slew) +{ + if (slew) + nsp_set_bit(chip, IO_CTRL, NSP_GPIO_SLEW_RATE_EN, gpio, true); + else + nsp_set_bit(chip, IO_CTRL, NSP_GPIO_SLEW_RATE_EN, gpio, false); + + return 0; +} + +static int nsp_gpio_set_pull(struct nsp_gpio *chip, unsigned gpio, + bool pull_up, bool pull_down) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + nsp_set_bit(chip, IO_CTRL, NSP_PULL_DOWN_EN, gpio, pull_down); + nsp_set_bit(chip, IO_CTRL, NSP_PULL_UP_EN, gpio, pull_up); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set pullup:%d pulldown: %d\n", + gpio, pull_up, pull_down); + return 0; +} + +static void nsp_gpio_get_pull(struct nsp_gpio *chip, unsigned gpio, + bool *pull_up, bool *pull_down) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + *pull_up = nsp_get_bit(chip, IO_CTRL, NSP_PULL_UP_EN, gpio); + *pull_down = nsp_get_bit(chip, IO_CTRL, NSP_PULL_DOWN_EN, gpio); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int nsp_gpio_set_strength(struct nsp_gpio *chip, unsigned gpio, + u16 strength) +{ + u32 offset, shift, i; + u32 val; + unsigned long flags; + + /* make sure drive strength is supported */ + if (strength < 2 || strength > 16 || (strength % 2)) + return -ENOTSUPP; + + shift = gpio; + offset = NSP_GPIO_DRV_CTRL; + 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 = GPIO_DRV_STRENGTH_BITS; i > 0; i--) { + val = readl(chip->io_ctrl + offset); + val &= ~BIT(shift); + val |= ((strength >> (i-1)) & 0x1) << shift; + writel(val, chip->io_ctrl + offset); + offset += 4; + } + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int nsp_gpio_get_strength(struct nsp_gpio *chip, unsigned gpio, + u16 *strength) +{ + unsigned int offset, shift; + u32 val; + unsigned long flags; + int i; + + offset = NSP_GPIO_DRV_CTRL; + shift = gpio; + + spin_lock_irqsave(&chip->lock, flags); + *strength = 0; + for (i = (GPIO_DRV_STRENGTH_BITS - 1); i >= 0; i--) { + val = readl(chip->io_ctrl + 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; +} + +int nsp_pin_config_group_get(struct pinctrl_dev *pctldev, unsigned selector, + unsigned long *config) +{ + return 0; +} + +int nsp_pin_config_group_set(struct pinctrl_dev *pctldev, unsigned selector, + unsigned long *configs, unsigned num_configs) +{ + return 0; +} + +static int nsp_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct nsp_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned int gpio; + u16 arg = 0; + bool pull_up, pull_down; + int ret; + + gpio = nsp_pin_to_gpio(pin); + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down); + if ((pull_up == false) && (pull_down == false)) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_UP: + nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down); + if (pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_DOWN: + nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down); + if (pull_down) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = nsp_gpio_get_strength(chip, gpio, &arg); + if (ret) + return ret; + *config = pinconf_to_config_packed(param, arg); + return 0; + + default: + return -ENOTSUPP; + } +} + +static int nsp_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *configs, unsigned num_configs) +{ + struct nsp_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + u16 arg; + unsigned int i, gpio; + int ret = -ENOTSUPP; + + gpio = nsp_pin_to_gpio(pin); + 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 = nsp_gpio_set_pull(chip, gpio, false, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + ret = nsp_gpio_set_pull(chip, gpio, true, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = nsp_gpio_set_pull(chip, gpio, false, true); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = nsp_gpio_set_strength(chip, gpio, arg); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_SLEW_RATE: + ret = nsp_gpio_set_slew(chip, gpio, arg); + if (ret < 0) + goto out; + break; + + default: + dev_err(chip->dev, "invalid configuration\n"); + return -ENOTSUPP; + } + } + +out: + return ret; +} + +static const struct pinconf_ops nsp_pconf_ops = { + .is_generic = true, + .pin_config_get = nsp_pin_config_get, + .pin_config_set = nsp_pin_config_set, + .pin_config_group_get = nsp_pin_config_group_get, + .pin_config_group_set = nsp_pin_config_group_set, +}; + +/* + * NSP GPIO controller supports some PINCONF related configurations such as + * pull up, pull down, slew 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 nsp_gpio_register_pinconf(struct nsp_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 = &nsp_pctrl_ops; + pctldesc->pins = pins; + pctldesc->npins = gc->ngpio; + pctldesc->confops = &nsp_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 const struct of_device_id nsp_gpio_of_match[] = { + {.compatible = "brcm,nsp-gpio-a",}, + {} +}; + +static int nsp_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct nsp_gpio *chip; + struct gpio_chip *gc; + u32 val, count; + int irq, ret; + + if (of_property_read_u32(pdev->dev.of_node, "ngpios", &val)) { + dev_err(&pdev->dev, "Missing ngpios OF property\n"); + return -ENODEV; + } + + 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); + 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); + } + + spin_lock_init(&chip->lock); + gc = &chip->gc; + gc->base = -1; + gc->can_sleep = false; + gc->ngpio = val; + gc->label = dev_name(dev); + gc->dev = dev; + gc->of_node = dev->of_node; + gc->request = nsp_gpio_request; + gc->free = nsp_gpio_free; + gc->direction_input = nsp_gpio_direction_input; + gc->direction_output = nsp_gpio_direction_output; + gc->set = nsp_gpio_set; + gc->get = nsp_gpio_get; + gc->to_irq = nsp_gpio_to_irq; + + /* optional GPIO interrupt support */ + irq = platform_get_irq(pdev, 0); + if (irq > 0) { + /* Create irq domain so that each pin can be assigned an IRQ.*/ + chip->irq_domain = irq_domain_add_linear(gc->of_node, gc->ngpio, + &irq_domain_simple_ops, + chip); + if (!chip->irq_domain) { + dev_err(&pdev->dev, "Couldn't allocate IRQ domain\n"); + return -ENXIO; + } + + /* Map each gpio to an IRQ and set the handler for gpiolib. */ + for (count = 0; count < gc->ngpio; count++) { + int irq = irq_create_mapping(chip->irq_domain, count); + + irq_set_chip_and_handler(irq, &nsp_gpio_irq_chip, + handle_simple_irq); + irq_set_chip_data(irq, chip); + } + + /* Install ISR for this GPIO controller. */ + ret = devm_request_irq(&pdev->dev, irq, nsp_gpio_irq_handler, + IRQF_SHARED, "gpio-a", chip); + if (ret) { + dev_err(&pdev->dev, "Unable to request IRQ%d: %d\n", + irq, ret); + goto err_rm_gpiochip; + } + + val = readl(chip->base + NSP_CHIP_A_INT_MASK); + val = val | NSP_CHIP_A_GPIO_INT_BIT; + writel(val, (chip->base + NSP_CHIP_A_INT_MASK)); + } + + ret = gpiochip_add(gc); + if (ret < 0) { + dev_err(dev, "unable to add GPIO chip\n"); + return ret; + } + + ret = nsp_gpio_register_pinconf(chip); + if (ret) { + dev_err(dev, "unable to register pinconf\n"); + goto err_rm_gpiochip; + } + + return 0; + +err_rm_gpiochip: + gpiochip_remove(gc); + + return ret; +} + +static struct platform_driver nsp_gpio_driver = { + .driver = { + .name = "nsp-gpio-a", + .of_match_table = nsp_gpio_of_match, + }, + .probe = nsp_gpio_probe, +}; + +static int __init nsp_gpio_init(void) +{ + return platform_driver_probe(&nsp_gpio_driver, nsp_gpio_probe); +} +arch_initcall_sync(nsp_gpio_init); |