| .. | .. |
|---|
| 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 | |
|---|
| 271 | 268 | port->irq = platform_get_irq(pdev, 0); |
|---|
| 272 | 269 | if (port->irq < 0) |
|---|
| 273 | 270 | return port->irq; |
|---|
| 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 | + } |
|---|
| 274 | 303 | |
|---|
| 275 | 304 | gc = &port->gc; |
|---|
| 276 | 305 | gc->of_node = np; |
|---|
| .. | .. |
|---|
| 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 = { |
|---|