Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2014-2015 Broadcom Corporation |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU General Public License as |
| 6 | * published by the Free Software Foundation version 2. |
| 7 | * |
| 8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any |
| 9 | * kind, whether express or implied; without even the implied warranty |
| 10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
| 12 | * |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 13 | * This file contains the Broadcom Iproc GPIO driver that supports 3 |
| 14 | * GPIO controllers on Iproc including the ASIU GPIO controller, the |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 15 | * chipCommonG GPIO controller, and the always-on GPIO controller. Basic |
| 16 | * PINCONF such as bias pull up/down, and drive strength are also supported |
| 17 | * in this driver. |
| 18 | * |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 19 | * It provides the functionality where pins from the GPIO can be |
| 20 | * individually muxed to GPIO function, if individual pad |
| 21 | * configuration is supported, through the interaction with respective |
| 22 | * SoCs IOMUX controller. |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 23 | */ |
| 24 | |
| 25 | #include <linux/kernel.h> |
| 26 | #include <linux/slab.h> |
| 27 | #include <linux/interrupt.h> |
| 28 | #include <linux/io.h> |
| 29 | #include <linux/gpio.h> |
| 30 | #include <linux/ioport.h> |
| 31 | #include <linux/of_device.h> |
| 32 | #include <linux/of_irq.h> |
| 33 | #include <linux/pinctrl/pinctrl.h> |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 34 | #include <linux/pinctrl/pinconf.h> |
| 35 | #include <linux/pinctrl/pinconf-generic.h> |
| 36 | |
| 37 | #include "../pinctrl-utils.h" |
| 38 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 39 | #define IPROC_GPIO_DATA_IN_OFFSET 0x00 |
| 40 | #define IPROC_GPIO_DATA_OUT_OFFSET 0x04 |
| 41 | #define IPROC_GPIO_OUT_EN_OFFSET 0x08 |
| 42 | #define IPROC_GPIO_INT_TYPE_OFFSET 0x0c |
| 43 | #define IPROC_GPIO_INT_DE_OFFSET 0x10 |
| 44 | #define IPROC_GPIO_INT_EDGE_OFFSET 0x14 |
| 45 | #define IPROC_GPIO_INT_MSK_OFFSET 0x18 |
| 46 | #define IPROC_GPIO_INT_STAT_OFFSET 0x1c |
| 47 | #define IPROC_GPIO_INT_MSTAT_OFFSET 0x20 |
| 48 | #define IPROC_GPIO_INT_CLR_OFFSET 0x24 |
| 49 | #define IPROC_GPIO_PAD_RES_OFFSET 0x34 |
| 50 | #define IPROC_GPIO_RES_EN_OFFSET 0x38 |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 51 | |
| 52 | /* drive strength control for ASIU GPIO */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 53 | #define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 54 | |
| 55 | /* drive strength control for CCM/CRMU (AON) GPIO */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 56 | #define IPROC_GPIO_DRV0_CTRL_OFFSET 0x00 |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 57 | |
| 58 | #define GPIO_BANK_SIZE 0x200 |
| 59 | #define NGPIOS_PER_BANK 32 |
| 60 | #define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) |
| 61 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 62 | #define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) |
| 63 | #define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 64 | |
| 65 | #define GPIO_DRV_STRENGTH_BIT_SHIFT 20 |
| 66 | #define GPIO_DRV_STRENGTH_BITS 3 |
| 67 | #define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) |
| 68 | |
| 69 | /* |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 70 | * Iproc GPIO core |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 71 | * |
| 72 | * @dev: pointer to device |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 73 | * @base: I/O register base for Iproc GPIO controller |
| 74 | * @io_ctrl: I/O register base for certain type of Iproc GPIO controller that |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 75 | * has the PINCONF support implemented outside of the GPIO block |
| 76 | * @lock: lock to protect access to I/O registers |
| 77 | * @gc: GPIO chip |
| 78 | * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs |
| 79 | * @pinmux_is_supported: flag to indicate this GPIO controller contains pins |
| 80 | * that can be individually muxed to GPIO |
| 81 | * @pctl: pointer to pinctrl_dev |
| 82 | * @pctldesc: pinctrl descriptor |
| 83 | */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 84 | struct iproc_gpio { |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 85 | struct device *dev; |
| 86 | |
| 87 | void __iomem *base; |
| 88 | void __iomem *io_ctrl; |
| 89 | |
| 90 | spinlock_t lock; |
| 91 | |
| 92 | struct gpio_chip gc; |
| 93 | unsigned num_banks; |
| 94 | |
| 95 | bool pinmux_is_supported; |
| 96 | |
| 97 | struct pinctrl_dev *pctl; |
| 98 | struct pinctrl_desc pctldesc; |
| 99 | }; |
| 100 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 101 | static inline struct iproc_gpio *to_iproc_gpio(struct gpio_chip *gc) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 102 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 103 | return container_of(gc, struct iproc_gpio, gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | /* |
| 107 | * Mapping from PINCONF pins to GPIO pins is 1-to-1 |
| 108 | */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 109 | static inline unsigned iproc_pin_to_gpio(unsigned pin) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 110 | { |
| 111 | return pin; |
| 112 | } |
| 113 | |
| 114 | /** |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 115 | * iproc_set_bit - set or clear one bit (corresponding to the GPIO pin) in a |
| 116 | * Iproc GPIO register |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 117 | * |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 118 | * @iproc_gpio: Iproc GPIO device |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 119 | * @reg: register offset |
| 120 | * @gpio: GPIO pin |
| 121 | * @set: set or clear |
| 122 | */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 123 | static inline void iproc_set_bit(struct iproc_gpio *chip, unsigned int reg, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 124 | unsigned gpio, bool set) |
| 125 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 126 | unsigned int offset = IPROC_GPIO_REG(gpio, reg); |
| 127 | unsigned int shift = IPROC_GPIO_SHIFT(gpio); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 128 | u32 val; |
| 129 | |
| 130 | val = readl(chip->base + offset); |
| 131 | if (set) |
| 132 | val |= BIT(shift); |
| 133 | else |
| 134 | val &= ~BIT(shift); |
| 135 | writel(val, chip->base + offset); |
| 136 | } |
| 137 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 138 | static inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 139 | unsigned gpio) |
| 140 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 141 | unsigned int offset = IPROC_GPIO_REG(gpio, reg); |
| 142 | unsigned int shift = IPROC_GPIO_SHIFT(gpio); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 143 | |
| 144 | return !!(readl(chip->base + offset) & BIT(shift)); |
| 145 | } |
| 146 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 147 | static void iproc_gpio_irq_handler(struct irq_desc *desc) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 148 | { |
| 149 | struct gpio_chip *gc = irq_desc_get_handler_data(desc); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 150 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 151 | struct irq_chip *irq_chip = irq_desc_get_chip(desc); |
| 152 | int i, bit; |
| 153 | |
| 154 | chained_irq_enter(irq_chip, desc); |
| 155 | |
| 156 | /* go through the entire GPIO banks and handle all interrupts */ |
| 157 | for (i = 0; i < chip->num_banks; i++) { |
| 158 | unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) + |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 159 | IPROC_GPIO_INT_MSTAT_OFFSET); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 160 | |
| 161 | for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { |
| 162 | unsigned pin = NGPIOS_PER_BANK * i + bit; |
| 163 | int child_irq = irq_find_mapping(gc->irqdomain, pin); |
| 164 | |
| 165 | /* |
| 166 | * Clear the interrupt before invoking the |
| 167 | * handler, so we do not leave any window |
| 168 | */ |
| 169 | writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 170 | IPROC_GPIO_INT_CLR_OFFSET); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 171 | |
| 172 | generic_handle_irq(child_irq); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | chained_irq_exit(irq_chip, desc); |
| 177 | } |
| 178 | |
| 179 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 180 | static void iproc_gpio_irq_ack(struct irq_data *d) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 181 | { |
| 182 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 183 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 184 | unsigned gpio = d->hwirq; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 185 | unsigned int offset = IPROC_GPIO_REG(gpio, |
| 186 | IPROC_GPIO_INT_CLR_OFFSET); |
| 187 | unsigned int shift = IPROC_GPIO_SHIFT(gpio); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 188 | u32 val = BIT(shift); |
| 189 | |
| 190 | writel(val, chip->base + offset); |
| 191 | } |
| 192 | |
| 193 | /** |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 194 | * iproc_gpio_irq_set_mask - mask/unmask a GPIO interrupt |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 195 | * |
| 196 | * @d: IRQ chip data |
| 197 | * @unmask: mask/unmask GPIO interrupt |
| 198 | */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 199 | static void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 200 | { |
| 201 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 202 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 203 | unsigned gpio = d->hwirq; |
| 204 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 205 | iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 206 | } |
| 207 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 208 | static void iproc_gpio_irq_mask(struct irq_data *d) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 209 | { |
| 210 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 211 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 212 | unsigned long flags; |
| 213 | |
| 214 | spin_lock_irqsave(&chip->lock, flags); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 215 | iproc_gpio_irq_set_mask(d, false); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 216 | spin_unlock_irqrestore(&chip->lock, flags); |
| 217 | } |
| 218 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 219 | static void iproc_gpio_irq_unmask(struct irq_data *d) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 220 | { |
| 221 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 222 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 223 | unsigned long flags; |
| 224 | |
| 225 | spin_lock_irqsave(&chip->lock, flags); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 226 | iproc_gpio_irq_set_mask(d, true); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 227 | spin_unlock_irqrestore(&chip->lock, flags); |
| 228 | } |
| 229 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 230 | static int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 231 | { |
| 232 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 233 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 234 | unsigned gpio = d->hwirq; |
| 235 | bool level_triggered = false; |
| 236 | bool dual_edge = false; |
| 237 | bool rising_or_high = false; |
| 238 | unsigned long flags; |
| 239 | |
| 240 | switch (type & IRQ_TYPE_SENSE_MASK) { |
| 241 | case IRQ_TYPE_EDGE_RISING: |
| 242 | rising_or_high = true; |
| 243 | break; |
| 244 | |
| 245 | case IRQ_TYPE_EDGE_FALLING: |
| 246 | break; |
| 247 | |
| 248 | case IRQ_TYPE_EDGE_BOTH: |
| 249 | dual_edge = true; |
| 250 | break; |
| 251 | |
| 252 | case IRQ_TYPE_LEVEL_HIGH: |
| 253 | level_triggered = true; |
| 254 | rising_or_high = true; |
| 255 | break; |
| 256 | |
| 257 | case IRQ_TYPE_LEVEL_LOW: |
| 258 | level_triggered = true; |
| 259 | break; |
| 260 | |
| 261 | default: |
| 262 | dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", |
| 263 | type); |
| 264 | return -EINVAL; |
| 265 | } |
| 266 | |
| 267 | spin_lock_irqsave(&chip->lock, flags); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 268 | iproc_set_bit(chip, IPROC_GPIO_INT_TYPE_OFFSET, gpio, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 269 | level_triggered); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 270 | iproc_set_bit(chip, IPROC_GPIO_INT_DE_OFFSET, gpio, dual_edge); |
| 271 | iproc_set_bit(chip, IPROC_GPIO_INT_EDGE_OFFSET, gpio, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 272 | rising_or_high); |
| 273 | spin_unlock_irqrestore(&chip->lock, flags); |
| 274 | |
| 275 | dev_dbg(chip->dev, |
| 276 | "gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d\n", |
| 277 | gpio, level_triggered, dual_edge, rising_or_high); |
| 278 | |
| 279 | return 0; |
| 280 | } |
| 281 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 282 | static struct irq_chip iproc_gpio_irq_chip = { |
| 283 | .name = "bcm-iproc-gpio", |
| 284 | .irq_ack = iproc_gpio_irq_ack, |
| 285 | .irq_mask = iproc_gpio_irq_mask, |
| 286 | .irq_unmask = iproc_gpio_irq_unmask, |
| 287 | .irq_set_type = iproc_gpio_irq_set_type, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 288 | }; |
| 289 | |
| 290 | /* |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 291 | * Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 292 | */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 293 | static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 294 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 295 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 296 | unsigned gpio = gc->base + offset; |
| 297 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 298 | /* not all Iproc GPIO pins can be muxed individually */ |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 299 | if (!chip->pinmux_is_supported) |
| 300 | return 0; |
| 301 | |
| 302 | return pinctrl_request_gpio(gpio); |
| 303 | } |
| 304 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 305 | static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 306 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 307 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 308 | unsigned gpio = gc->base + offset; |
| 309 | |
| 310 | if (!chip->pinmux_is_supported) |
| 311 | return; |
| 312 | |
| 313 | pinctrl_free_gpio(gpio); |
| 314 | } |
| 315 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 316 | static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 317 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 318 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 319 | unsigned long flags; |
| 320 | |
| 321 | spin_lock_irqsave(&chip->lock, flags); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 322 | iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, false); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 323 | spin_unlock_irqrestore(&chip->lock, flags); |
| 324 | |
| 325 | dev_dbg(chip->dev, "gpio:%u set input\n", gpio); |
| 326 | |
| 327 | return 0; |
| 328 | } |
| 329 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 330 | static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 331 | int val) |
| 332 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 333 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 334 | unsigned long flags; |
| 335 | |
| 336 | spin_lock_irqsave(&chip->lock, flags); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 337 | iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, true); |
| 338 | iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 339 | spin_unlock_irqrestore(&chip->lock, flags); |
| 340 | |
| 341 | dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); |
| 342 | |
| 343 | return 0; |
| 344 | } |
| 345 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 346 | static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 347 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 348 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 349 | unsigned long flags; |
| 350 | |
| 351 | spin_lock_irqsave(&chip->lock, flags); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 352 | iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 353 | spin_unlock_irqrestore(&chip->lock, flags); |
| 354 | |
| 355 | dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); |
| 356 | } |
| 357 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 358 | static int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 359 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 360 | struct iproc_gpio *chip = to_iproc_gpio(gc); |
| 361 | unsigned int offset = IPROC_GPIO_REG(gpio, |
| 362 | IPROC_GPIO_DATA_IN_OFFSET); |
| 363 | unsigned int shift = IPROC_GPIO_SHIFT(gpio); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 364 | |
| 365 | return !!(readl(chip->base + offset) & BIT(shift)); |
| 366 | } |
| 367 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 368 | static int iproc_get_groups_count(struct pinctrl_dev *pctldev) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 369 | { |
| 370 | return 1; |
| 371 | } |
| 372 | |
| 373 | /* |
| 374 | * Only one group: "gpio_grp", since this local pinctrl device only performs |
| 375 | * GPIO specific PINCONF configurations |
| 376 | */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 377 | static const char *iproc_get_group_name(struct pinctrl_dev *pctldev, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 378 | unsigned selector) |
| 379 | { |
| 380 | return "gpio_grp"; |
| 381 | } |
| 382 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 383 | static const struct pinctrl_ops iproc_pctrl_ops = { |
| 384 | .get_groups_count = iproc_get_groups_count, |
| 385 | .get_group_name = iproc_get_group_name, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 386 | .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, |
| 387 | .dt_free_map = pinctrl_utils_dt_free_map, |
| 388 | }; |
| 389 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 390 | static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 391 | bool disable, bool pull_up) |
| 392 | { |
| 393 | unsigned long flags; |
| 394 | |
| 395 | spin_lock_irqsave(&chip->lock, flags); |
| 396 | |
| 397 | if (disable) { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 398 | iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, false); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 399 | } else { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 400 | iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 401 | pull_up); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 402 | iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, true); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 403 | } |
| 404 | |
| 405 | spin_unlock_irqrestore(&chip->lock, flags); |
| 406 | |
| 407 | dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); |
| 408 | |
| 409 | return 0; |
| 410 | } |
| 411 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 412 | static void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 413 | bool *disable, bool *pull_up) |
| 414 | { |
| 415 | unsigned long flags; |
| 416 | |
| 417 | spin_lock_irqsave(&chip->lock, flags); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 418 | *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio); |
| 419 | *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 420 | spin_unlock_irqrestore(&chip->lock, flags); |
| 421 | } |
| 422 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 423 | static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 424 | unsigned strength) |
| 425 | { |
| 426 | void __iomem *base; |
| 427 | unsigned int i, offset, shift; |
| 428 | u32 val; |
| 429 | unsigned long flags; |
| 430 | |
| 431 | /* make sure drive strength is supported */ |
| 432 | if (strength < 2 || strength > 16 || (strength % 2)) |
| 433 | return -ENOTSUPP; |
| 434 | |
| 435 | if (chip->io_ctrl) { |
| 436 | base = chip->io_ctrl; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 437 | offset = IPROC_GPIO_DRV0_CTRL_OFFSET; |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 438 | } else { |
| 439 | base = chip->base; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 440 | offset = IPROC_GPIO_REG(gpio, |
| 441 | IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 442 | } |
| 443 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 444 | shift = IPROC_GPIO_SHIFT(gpio); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 445 | |
| 446 | dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, |
| 447 | strength); |
| 448 | |
| 449 | spin_lock_irqsave(&chip->lock, flags); |
| 450 | strength = (strength / 2) - 1; |
| 451 | for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { |
| 452 | val = readl(base + offset); |
| 453 | val &= ~BIT(shift); |
| 454 | val |= ((strength >> i) & 0x1) << shift; |
| 455 | writel(val, base + offset); |
| 456 | offset += 4; |
| 457 | } |
| 458 | spin_unlock_irqrestore(&chip->lock, flags); |
| 459 | |
| 460 | return 0; |
| 461 | } |
| 462 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 463 | static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 464 | u16 *strength) |
| 465 | { |
| 466 | void __iomem *base; |
| 467 | unsigned int i, offset, shift; |
| 468 | u32 val; |
| 469 | unsigned long flags; |
| 470 | |
| 471 | if (chip->io_ctrl) { |
| 472 | base = chip->io_ctrl; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 473 | offset = IPROC_GPIO_DRV0_CTRL_OFFSET; |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 474 | } else { |
| 475 | base = chip->base; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 476 | offset = IPROC_GPIO_REG(gpio, |
| 477 | IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 478 | } |
| 479 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 480 | shift = IPROC_GPIO_SHIFT(gpio); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 481 | |
| 482 | spin_lock_irqsave(&chip->lock, flags); |
| 483 | *strength = 0; |
| 484 | for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { |
| 485 | val = readl(base + offset) & BIT(shift); |
| 486 | val >>= shift; |
| 487 | *strength += (val << i); |
| 488 | offset += 4; |
| 489 | } |
| 490 | |
| 491 | /* convert to mA */ |
| 492 | *strength = (*strength + 1) * 2; |
| 493 | spin_unlock_irqrestore(&chip->lock, flags); |
| 494 | |
| 495 | return 0; |
| 496 | } |
| 497 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 498 | static int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 499 | unsigned long *config) |
| 500 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 501 | struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 502 | enum pin_config_param param = pinconf_to_config_param(*config); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 503 | unsigned gpio = iproc_pin_to_gpio(pin); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 504 | u16 arg; |
| 505 | bool disable, pull_up; |
| 506 | int ret; |
| 507 | |
| 508 | switch (param) { |
| 509 | case PIN_CONFIG_BIAS_DISABLE: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 510 | iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 511 | if (disable) |
| 512 | return 0; |
| 513 | else |
| 514 | return -EINVAL; |
| 515 | |
| 516 | case PIN_CONFIG_BIAS_PULL_UP: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 517 | iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 518 | if (!disable && pull_up) |
| 519 | return 0; |
| 520 | else |
| 521 | return -EINVAL; |
| 522 | |
| 523 | case PIN_CONFIG_BIAS_PULL_DOWN: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 524 | iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 525 | if (!disable && !pull_up) |
| 526 | return 0; |
| 527 | else |
| 528 | return -EINVAL; |
| 529 | |
| 530 | case PIN_CONFIG_DRIVE_STRENGTH: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 531 | ret = iproc_gpio_get_strength(chip, gpio, &arg); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 532 | if (ret) |
| 533 | return ret; |
Pramod Kumar | 616043d | 2015-11-19 09:22:19 +0530 | [diff] [blame] | 534 | *config = pinconf_to_config_packed(param, arg); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 535 | |
| 536 | return 0; |
| 537 | |
| 538 | default: |
| 539 | return -ENOTSUPP; |
| 540 | } |
| 541 | |
| 542 | return -ENOTSUPP; |
| 543 | } |
| 544 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 545 | static int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 546 | unsigned long *configs, unsigned num_configs) |
| 547 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 548 | struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 549 | enum pin_config_param param; |
| 550 | u16 arg; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 551 | unsigned i, gpio = iproc_pin_to_gpio(pin); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 552 | int ret = -ENOTSUPP; |
| 553 | |
| 554 | for (i = 0; i < num_configs; i++) { |
| 555 | param = pinconf_to_config_param(configs[i]); |
| 556 | arg = pinconf_to_config_argument(configs[i]); |
| 557 | |
| 558 | switch (param) { |
| 559 | case PIN_CONFIG_BIAS_DISABLE: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 560 | ret = iproc_gpio_set_pull(chip, gpio, true, false); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 561 | if (ret < 0) |
| 562 | goto out; |
| 563 | break; |
| 564 | |
| 565 | case PIN_CONFIG_BIAS_PULL_UP: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 566 | ret = iproc_gpio_set_pull(chip, gpio, false, true); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 567 | if (ret < 0) |
| 568 | goto out; |
| 569 | break; |
| 570 | |
| 571 | case PIN_CONFIG_BIAS_PULL_DOWN: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 572 | ret = iproc_gpio_set_pull(chip, gpio, false, false); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 573 | if (ret < 0) |
| 574 | goto out; |
| 575 | break; |
| 576 | |
| 577 | case PIN_CONFIG_DRIVE_STRENGTH: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 578 | ret = iproc_gpio_set_strength(chip, gpio, arg); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 579 | if (ret < 0) |
| 580 | goto out; |
| 581 | break; |
| 582 | |
| 583 | default: |
| 584 | dev_err(chip->dev, "invalid configuration\n"); |
| 585 | return -ENOTSUPP; |
| 586 | } |
| 587 | } /* for each config */ |
| 588 | |
| 589 | out: |
| 590 | return ret; |
| 591 | } |
| 592 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 593 | static const struct pinconf_ops iproc_pconf_ops = { |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 594 | .is_generic = true, |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 595 | .pin_config_get = iproc_pin_config_get, |
| 596 | .pin_config_set = iproc_pin_config_set, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 597 | }; |
| 598 | |
| 599 | /* |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 600 | * Iproc GPIO controller supports some PINCONF related configurations such as |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 601 | * pull up, pull down, and drive strength, when the pin is configured to GPIO |
| 602 | * |
| 603 | * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the |
| 604 | * local GPIO pins |
| 605 | */ |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 606 | static int iproc_gpio_register_pinconf(struct iproc_gpio *chip) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 607 | { |
| 608 | struct pinctrl_desc *pctldesc = &chip->pctldesc; |
| 609 | struct pinctrl_pin_desc *pins; |
| 610 | struct gpio_chip *gc = &chip->gc; |
| 611 | int i; |
| 612 | |
| 613 | pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); |
| 614 | if (!pins) |
| 615 | return -ENOMEM; |
| 616 | |
| 617 | for (i = 0; i < gc->ngpio; i++) { |
| 618 | pins[i].number = i; |
| 619 | pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, |
| 620 | "gpio-%d", i); |
| 621 | if (!pins[i].name) |
| 622 | return -ENOMEM; |
| 623 | } |
| 624 | |
| 625 | pctldesc->name = dev_name(chip->dev); |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 626 | pctldesc->pctlops = &iproc_pctrl_ops; |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 627 | pctldesc->pins = pins; |
| 628 | pctldesc->npins = gc->ngpio; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 629 | pctldesc->confops = &iproc_pconf_ops; |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 630 | |
| 631 | chip->pctl = pinctrl_register(pctldesc, chip->dev, chip); |
Masahiro Yamada | 323de9e | 2015-06-09 13:01:16 +0900 | [diff] [blame] | 632 | if (IS_ERR(chip->pctl)) { |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 633 | dev_err(chip->dev, "unable to register pinctrl device\n"); |
Masahiro Yamada | 323de9e | 2015-06-09 13:01:16 +0900 | [diff] [blame] | 634 | return PTR_ERR(chip->pctl); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 635 | } |
| 636 | |
| 637 | return 0; |
| 638 | } |
| 639 | |
Linus Walleij | 0202a11 | 2015-12-21 09:40:39 +0100 | [diff] [blame] | 640 | static void iproc_gpio_unregister_pinconf(struct iproc_gpio *chip) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 641 | { |
Markus Elfring | f10a258 | 2015-11-05 17:10:22 +0100 | [diff] [blame] | 642 | pinctrl_unregister(chip->pctl); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 643 | } |
| 644 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 645 | static const struct of_device_id iproc_gpio_of_match[] = { |
Pramod Kumar | e1aaaf3 | 2015-11-19 09:22:15 +0530 | [diff] [blame] | 646 | { .compatible = "brcm,cygnus-ccm-gpio" }, |
| 647 | { .compatible = "brcm,cygnus-asiu-gpio" }, |
| 648 | { .compatible = "brcm,cygnus-crmu-gpio" }, |
Pramod Kumar | 462de62 | 2015-11-19 09:22:16 +0530 | [diff] [blame] | 649 | { .compatible = "brcm,iproc-gpio" }, |
Pramod Kumar | e1aaaf3 | 2015-11-19 09:22:15 +0530 | [diff] [blame] | 650 | { } |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 651 | }; |
| 652 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 653 | static int iproc_gpio_probe(struct platform_device *pdev) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 654 | { |
| 655 | struct device *dev = &pdev->dev; |
| 656 | struct resource *res; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 657 | struct iproc_gpio *chip; |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 658 | struct gpio_chip *gc; |
| 659 | u32 ngpios; |
| 660 | int irq, ret; |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 661 | |
| 662 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); |
| 663 | if (!chip) |
| 664 | return -ENOMEM; |
| 665 | |
| 666 | chip->dev = dev; |
| 667 | platform_set_drvdata(pdev, chip); |
| 668 | |
| 669 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 670 | chip->base = devm_ioremap_resource(dev, res); |
| 671 | if (IS_ERR(chip->base)) { |
| 672 | dev_err(dev, "unable to map I/O memory\n"); |
| 673 | return PTR_ERR(chip->base); |
| 674 | } |
| 675 | |
| 676 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| 677 | if (res) { |
| 678 | chip->io_ctrl = devm_ioremap_resource(dev, res); |
| 679 | if (IS_ERR(chip->io_ctrl)) { |
| 680 | dev_err(dev, "unable to map I/O memory\n"); |
| 681 | return PTR_ERR(chip->io_ctrl); |
| 682 | } |
| 683 | } |
| 684 | |
Pramod Kumar | e1aaaf3 | 2015-11-19 09:22:15 +0530 | [diff] [blame] | 685 | if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { |
| 686 | dev_err(&pdev->dev, "missing ngpios DT property\n"); |
| 687 | return -ENODEV; |
| 688 | } |
| 689 | |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 690 | spin_lock_init(&chip->lock); |
| 691 | |
| 692 | gc = &chip->gc; |
| 693 | gc->base = -1; |
| 694 | gc->ngpio = ngpios; |
| 695 | chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; |
| 696 | gc->label = dev_name(dev); |
Linus Torvalds | 58cf279a | 2016-01-17 12:32:01 -0800 | [diff] [blame] | 697 | gc->parent = dev; |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 698 | gc->of_node = dev->of_node; |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 699 | gc->request = iproc_gpio_request; |
| 700 | gc->free = iproc_gpio_free; |
| 701 | gc->direction_input = iproc_gpio_direction_input; |
| 702 | gc->direction_output = iproc_gpio_direction_output; |
| 703 | gc->set = iproc_gpio_set; |
| 704 | gc->get = iproc_gpio_get; |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 705 | |
Pramod Kumar | ea92211 | 2015-10-19 11:13:09 +0530 | [diff] [blame] | 706 | chip->pinmux_is_supported = of_property_read_bool(dev->of_node, |
| 707 | "gpio-ranges"); |
| 708 | |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 709 | ret = gpiochip_add(gc); |
| 710 | if (ret < 0) { |
| 711 | dev_err(dev, "unable to add GPIO chip\n"); |
| 712 | return ret; |
| 713 | } |
| 714 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 715 | ret = iproc_gpio_register_pinconf(chip); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 716 | if (ret) { |
| 717 | dev_err(dev, "unable to register pinconf\n"); |
| 718 | goto err_rm_gpiochip; |
| 719 | } |
| 720 | |
| 721 | /* optional GPIO interrupt support */ |
| 722 | irq = platform_get_irq(pdev, 0); |
| 723 | if (irq) { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 724 | ret = gpiochip_irqchip_add(gc, &iproc_gpio_irq_chip, 0, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 725 | handle_simple_irq, IRQ_TYPE_NONE); |
| 726 | if (ret) { |
| 727 | dev_err(dev, "no GPIO irqchip\n"); |
| 728 | goto err_unregister_pinconf; |
| 729 | } |
| 730 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 731 | gpiochip_set_chained_irqchip(gc, &iproc_gpio_irq_chip, irq, |
| 732 | iproc_gpio_irq_handler); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 733 | } |
| 734 | |
| 735 | return 0; |
| 736 | |
| 737 | err_unregister_pinconf: |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 738 | iproc_gpio_unregister_pinconf(chip); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 739 | |
| 740 | err_rm_gpiochip: |
| 741 | gpiochip_remove(gc); |
| 742 | |
| 743 | return ret; |
| 744 | } |
| 745 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 746 | static struct platform_driver iproc_gpio_driver = { |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 747 | .driver = { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 748 | .name = "iproc-gpio", |
| 749 | .of_match_table = iproc_gpio_of_match, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 750 | }, |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 751 | .probe = iproc_gpio_probe, |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 752 | }; |
| 753 | |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 754 | static int __init iproc_gpio_init(void) |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 755 | { |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 756 | return platform_driver_probe(&iproc_gpio_driver, iproc_gpio_probe); |
Ray Jui | b64333c | 2015-03-09 13:45:00 -0700 | [diff] [blame] | 757 | } |
Pramod Kumar | afc8c78 | 2015-11-19 09:22:17 +0530 | [diff] [blame] | 758 | arch_initcall_sync(iproc_gpio_init); |