.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
---|
1 | 2 | /* |
---|
2 | 3 | * Freescale vf610 GPIO support through PORT and GPIO |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2014 Toradex AG. |
---|
5 | 6 | * |
---|
6 | 7 | * Author: Stefan Agner <stefan@agner.ch>. |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public License |
---|
10 | | - * as published by the Free Software Foundation; either version 2 |
---|
11 | | - * of the License, or (at your option) any later version. |
---|
12 | | - * This program is distributed in the hope that it will be useful, |
---|
13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
15 | | - * GNU General Public License for more details. |
---|
16 | 8 | */ |
---|
17 | | - |
---|
18 | 9 | #include <linux/bitops.h> |
---|
| 10 | +#include <linux/clk.h> |
---|
19 | 11 | #include <linux/err.h> |
---|
20 | | -#include <linux/gpio.h> |
---|
| 12 | +#include <linux/gpio/driver.h> |
---|
21 | 13 | #include <linux/init.h> |
---|
22 | 14 | #include <linux/interrupt.h> |
---|
23 | 15 | #include <linux/io.h> |
---|
.. | .. |
---|
42 | 34 | void __iomem *gpio_base; |
---|
43 | 35 | const struct fsl_gpio_soc_data *sdata; |
---|
44 | 36 | u8 irqc[VF610_GPIO_PER_PORT]; |
---|
| 37 | + struct clk *clk_port; |
---|
| 38 | + struct clk *clk_gpio; |
---|
45 | 39 | int irq; |
---|
46 | 40 | }; |
---|
47 | 41 | |
---|
.. | .. |
---|
91 | 85 | { |
---|
92 | 86 | struct vf610_gpio_port *port = gpiochip_get_data(gc); |
---|
93 | 87 | unsigned long mask = BIT(gpio); |
---|
94 | | - void __iomem *addr; |
---|
| 88 | + unsigned long offset = GPIO_PDIR; |
---|
95 | 89 | |
---|
96 | 90 | if (port->sdata && port->sdata->have_paddr) { |
---|
97 | 91 | mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR); |
---|
98 | | - addr = mask ? port->gpio_base + GPIO_PDOR : |
---|
99 | | - port->gpio_base + GPIO_PDIR; |
---|
100 | | - return !!(vf610_gpio_readl(addr) & BIT(gpio)); |
---|
101 | | - } else { |
---|
102 | | - return !!(vf610_gpio_readl(port->gpio_base + GPIO_PDIR) |
---|
103 | | - & BIT(gpio)); |
---|
| 92 | + if (mask) |
---|
| 93 | + offset = GPIO_PDOR; |
---|
104 | 94 | } |
---|
| 95 | + |
---|
| 96 | + return !!(vf610_gpio_readl(port->gpio_base + offset) & BIT(gpio)); |
---|
105 | 97 | } |
---|
106 | 98 | |
---|
107 | 99 | static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) |
---|
108 | 100 | { |
---|
109 | 101 | struct vf610_gpio_port *port = gpiochip_get_data(gc); |
---|
110 | 102 | unsigned long mask = BIT(gpio); |
---|
| 103 | + unsigned long offset = val ? GPIO_PSOR : GPIO_PCOR; |
---|
111 | 104 | |
---|
112 | | - if (val) |
---|
113 | | - vf610_gpio_writel(mask, port->gpio_base + GPIO_PSOR); |
---|
114 | | - else |
---|
115 | | - vf610_gpio_writel(mask, port->gpio_base + GPIO_PCOR); |
---|
| 105 | + vf610_gpio_writel(mask, port->gpio_base + offset); |
---|
116 | 106 | } |
---|
117 | 107 | |
---|
118 | 108 | static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) |
---|
.. | .. |
---|
135 | 125 | { |
---|
136 | 126 | struct vf610_gpio_port *port = gpiochip_get_data(chip); |
---|
137 | 127 | unsigned long mask = BIT(gpio); |
---|
| 128 | + u32 val; |
---|
138 | 129 | |
---|
139 | | - if (port->sdata && port->sdata->have_paddr) |
---|
140 | | - vf610_gpio_writel(mask, port->gpio_base + GPIO_PDDR); |
---|
| 130 | + if (port->sdata && port->sdata->have_paddr) { |
---|
| 131 | + val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR); |
---|
| 132 | + val |= mask; |
---|
| 133 | + vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR); |
---|
| 134 | + } |
---|
141 | 135 | |
---|
142 | 136 | vf610_gpio_set(chip, gpio, value); |
---|
143 | 137 | |
---|
.. | .. |
---|
242 | 236 | return 0; |
---|
243 | 237 | } |
---|
244 | 238 | |
---|
| 239 | +static void vf610_gpio_disable_clk(void *data) |
---|
| 240 | +{ |
---|
| 241 | + clk_disable_unprepare(data); |
---|
| 242 | +} |
---|
| 243 | + |
---|
245 | 244 | static int vf610_gpio_probe(struct platform_device *pdev) |
---|
246 | 245 | { |
---|
247 | 246 | struct device *dev = &pdev->dev; |
---|
248 | 247 | struct device_node *np = dev->of_node; |
---|
249 | 248 | struct vf610_gpio_port *port; |
---|
250 | | - struct resource *iores; |
---|
251 | 249 | struct gpio_chip *gc; |
---|
| 250 | + struct gpio_irq_chip *girq; |
---|
252 | 251 | struct irq_chip *ic; |
---|
253 | 252 | int i; |
---|
254 | 253 | int ret; |
---|
255 | 254 | |
---|
256 | | - port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); |
---|
| 255 | + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); |
---|
257 | 256 | if (!port) |
---|
258 | 257 | return -ENOMEM; |
---|
259 | 258 | |
---|
260 | 259 | port->sdata = of_device_get_match_data(dev); |
---|
261 | | - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
262 | | - port->base = devm_ioremap_resource(dev, iores); |
---|
| 260 | + port->base = devm_platform_ioremap_resource(pdev, 0); |
---|
263 | 261 | if (IS_ERR(port->base)) |
---|
264 | 262 | return PTR_ERR(port->base); |
---|
265 | 263 | |
---|
266 | | - iores = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
---|
267 | | - port->gpio_base = devm_ioremap_resource(dev, iores); |
---|
| 264 | + port->gpio_base = devm_platform_ioremap_resource(pdev, 1); |
---|
268 | 265 | if (IS_ERR(port->gpio_base)) |
---|
269 | 266 | return PTR_ERR(port->gpio_base); |
---|
270 | 267 | |
---|
.. | .. |
---|
272 | 269 | if (port->irq < 0) |
---|
273 | 270 | return port->irq; |
---|
274 | 271 | |
---|
| 272 | + port->clk_port = devm_clk_get(dev, "port"); |
---|
| 273 | + ret = PTR_ERR_OR_ZERO(port->clk_port); |
---|
| 274 | + if (!ret) { |
---|
| 275 | + ret = clk_prepare_enable(port->clk_port); |
---|
| 276 | + if (ret) |
---|
| 277 | + return ret; |
---|
| 278 | + ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk, |
---|
| 279 | + port->clk_port); |
---|
| 280 | + if (ret) |
---|
| 281 | + return ret; |
---|
| 282 | + } else if (ret == -EPROBE_DEFER) { |
---|
| 283 | + /* |
---|
| 284 | + * Percolate deferrals, for anything else, |
---|
| 285 | + * just live without the clocking. |
---|
| 286 | + */ |
---|
| 287 | + return ret; |
---|
| 288 | + } |
---|
| 289 | + |
---|
| 290 | + port->clk_gpio = devm_clk_get(dev, "gpio"); |
---|
| 291 | + ret = PTR_ERR_OR_ZERO(port->clk_gpio); |
---|
| 292 | + if (!ret) { |
---|
| 293 | + ret = clk_prepare_enable(port->clk_gpio); |
---|
| 294 | + if (ret) |
---|
| 295 | + return ret; |
---|
| 296 | + ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk, |
---|
| 297 | + port->clk_gpio); |
---|
| 298 | + if (ret) |
---|
| 299 | + return ret; |
---|
| 300 | + } else if (ret == -EPROBE_DEFER) { |
---|
| 301 | + return ret; |
---|
| 302 | + } |
---|
| 303 | + |
---|
275 | 304 | gc = &port->gc; |
---|
276 | 305 | gc->of_node = np; |
---|
277 | 306 | gc->parent = dev; |
---|
278 | | - gc->label = "vf610-gpio"; |
---|
| 307 | + gc->label = dev_name(dev); |
---|
279 | 308 | gc->ngpio = VF610_GPIO_PER_PORT; |
---|
280 | 309 | gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT; |
---|
281 | 310 | |
---|
.. | .. |
---|
294 | 323 | ic->irq_set_type = vf610_gpio_irq_set_type; |
---|
295 | 324 | ic->irq_set_wake = vf610_gpio_irq_set_wake; |
---|
296 | 325 | |
---|
297 | | - ret = gpiochip_add_data(gc, port); |
---|
298 | | - if (ret < 0) |
---|
299 | | - return ret; |
---|
300 | | - |
---|
301 | 326 | /* Mask all GPIO interrupts */ |
---|
302 | 327 | for (i = 0; i < gc->ngpio; i++) |
---|
303 | 328 | vf610_gpio_writel(0, port->base + PORT_PCR(i)); |
---|
.. | .. |
---|
305 | 330 | /* Clear the interrupt status register for all GPIO's */ |
---|
306 | 331 | vf610_gpio_writel(~0, port->base + PORT_ISFR); |
---|
307 | 332 | |
---|
308 | | - ret = gpiochip_irqchip_add(gc, ic, 0, handle_edge_irq, IRQ_TYPE_NONE); |
---|
309 | | - if (ret) { |
---|
310 | | - dev_err(dev, "failed to add irqchip\n"); |
---|
311 | | - gpiochip_remove(gc); |
---|
312 | | - return ret; |
---|
313 | | - } |
---|
314 | | - gpiochip_set_chained_irqchip(gc, ic, port->irq, |
---|
315 | | - vf610_gpio_irq_handler); |
---|
| 333 | + girq = &gc->irq; |
---|
| 334 | + girq->chip = ic; |
---|
| 335 | + girq->parent_handler = vf610_gpio_irq_handler; |
---|
| 336 | + girq->num_parents = 1; |
---|
| 337 | + girq->parents = devm_kcalloc(&pdev->dev, 1, |
---|
| 338 | + sizeof(*girq->parents), |
---|
| 339 | + GFP_KERNEL); |
---|
| 340 | + if (!girq->parents) |
---|
| 341 | + return -ENOMEM; |
---|
| 342 | + girq->parents[0] = port->irq; |
---|
| 343 | + girq->default_type = IRQ_TYPE_NONE; |
---|
| 344 | + girq->handler = handle_edge_irq; |
---|
316 | 345 | |
---|
317 | | - return 0; |
---|
| 346 | + return devm_gpiochip_add_data(dev, gc, port); |
---|
318 | 347 | } |
---|
319 | 348 | |
---|
320 | 349 | static struct platform_driver vf610_gpio_driver = { |
---|