linux/drivers/gpio/gpio-vf610.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Freescale vf610 GPIO support through PORT and GPIO
   4 *
   5 * Copyright (c) 2014 Toradex AG.
   6 *
   7 * Author: Stefan Agner <stefan@agner.ch>.
   8 */
   9#include <linux/bitops.h>
  10#include <linux/clk.h>
  11#include <linux/err.h>
  12#include <linux/gpio/driver.h>
  13#include <linux/init.h>
  14#include <linux/interrupt.h>
  15#include <linux/io.h>
  16#include <linux/ioport.h>
  17#include <linux/irq.h>
  18#include <linux/platform_device.h>
  19#include <linux/of.h>
  20#include <linux/of_device.h>
  21#include <linux/of_irq.h>
  22#include <linux/pinctrl/consumer.h>
  23
  24#define VF610_GPIO_PER_PORT             32
  25
  26struct fsl_gpio_soc_data {
  27        /* SoCs has a Port Data Direction Register (PDDR) */
  28        bool have_paddr;
  29};
  30
  31struct vf610_gpio_port {
  32        struct gpio_chip gc;
  33        struct irq_chip ic;
  34        void __iomem *base;
  35        void __iomem *gpio_base;
  36        const struct fsl_gpio_soc_data *sdata;
  37        u8 irqc[VF610_GPIO_PER_PORT];
  38        struct clk *clk_port;
  39        struct clk *clk_gpio;
  40        int irq;
  41};
  42
  43#define GPIO_PDOR               0x00
  44#define GPIO_PSOR               0x04
  45#define GPIO_PCOR               0x08
  46#define GPIO_PTOR               0x0c
  47#define GPIO_PDIR               0x10
  48#define GPIO_PDDR               0x14
  49
  50#define PORT_PCR(n)             ((n) * 0x4)
  51#define PORT_PCR_IRQC_OFFSET    16
  52
  53#define PORT_ISFR               0xa0
  54#define PORT_DFER               0xc0
  55#define PORT_DFCR               0xc4
  56#define PORT_DFWR               0xc8
  57
  58#define PORT_INT_OFF            0x0
  59#define PORT_INT_LOGIC_ZERO     0x8
  60#define PORT_INT_RISING_EDGE    0x9
  61#define PORT_INT_FALLING_EDGE   0xa
  62#define PORT_INT_EITHER_EDGE    0xb
  63#define PORT_INT_LOGIC_ONE      0xc
  64
  65static const struct fsl_gpio_soc_data imx_data = {
  66        .have_paddr = true,
  67};
  68
  69static const struct of_device_id vf610_gpio_dt_ids[] = {
  70        { .compatible = "fsl,vf610-gpio",       .data = NULL, },
  71        { .compatible = "fsl,imx7ulp-gpio",     .data = &imx_data, },
  72        { /* sentinel */ }
  73};
  74
  75static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
  76{
  77        writel_relaxed(val, reg);
  78}
  79
  80static inline u32 vf610_gpio_readl(void __iomem *reg)
  81{
  82        return readl_relaxed(reg);
  83}
  84
  85static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)
  86{
  87        struct vf610_gpio_port *port = gpiochip_get_data(gc);
  88        unsigned long mask = BIT(gpio);
  89        unsigned long offset = GPIO_PDIR;
  90
  91        if (port->sdata && port->sdata->have_paddr) {
  92                mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
  93                if (mask)
  94                        offset = GPIO_PDOR;
  95        }
  96
  97        return !!(vf610_gpio_readl(port->gpio_base + offset) & BIT(gpio));
  98}
  99
 100static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 101{
 102        struct vf610_gpio_port *port = gpiochip_get_data(gc);
 103        unsigned long mask = BIT(gpio);
 104        unsigned long offset = val ? GPIO_PSOR : GPIO_PCOR;
 105
 106        vf610_gpio_writel(mask, port->gpio_base + offset);
 107}
 108
 109static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
 110{
 111        struct vf610_gpio_port *port = gpiochip_get_data(chip);
 112        unsigned long mask = BIT(gpio);
 113        u32 val;
 114
 115        if (port->sdata && port->sdata->have_paddr) {
 116                val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
 117                val &= ~mask;
 118                vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
 119        }
 120
 121        return pinctrl_gpio_direction_input(chip->base + gpio);
 122}
 123
 124static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
 125                                       int value)
 126{
 127        struct vf610_gpio_port *port = gpiochip_get_data(chip);
 128        unsigned long mask = BIT(gpio);
 129        u32 val;
 130
 131        if (port->sdata && port->sdata->have_paddr) {
 132                val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
 133                val |= mask;
 134                vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
 135        }
 136
 137        vf610_gpio_set(chip, gpio, value);
 138
 139        return pinctrl_gpio_direction_output(chip->base + gpio);
 140}
 141
 142static void vf610_gpio_irq_handler(struct irq_desc *desc)
 143{
 144        struct vf610_gpio_port *port =
 145                gpiochip_get_data(irq_desc_get_handler_data(desc));
 146        struct irq_chip *chip = irq_desc_get_chip(desc);
 147        int pin;
 148        unsigned long irq_isfr;
 149
 150        chained_irq_enter(chip, desc);
 151
 152        irq_isfr = vf610_gpio_readl(port->base + PORT_ISFR);
 153
 154        for_each_set_bit(pin, &irq_isfr, VF610_GPIO_PER_PORT) {
 155                vf610_gpio_writel(BIT(pin), port->base + PORT_ISFR);
 156
 157                generic_handle_domain_irq(port->gc.irq.domain, pin);
 158        }
 159
 160        chained_irq_exit(chip, desc);
 161}
 162
 163static void vf610_gpio_irq_ack(struct irq_data *d)
 164{
 165        struct vf610_gpio_port *port =
 166                gpiochip_get_data(irq_data_get_irq_chip_data(d));
 167        int gpio = d->hwirq;
 168
 169        vf610_gpio_writel(BIT(gpio), port->base + PORT_ISFR);
 170}
 171
 172static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)
 173{
 174        struct vf610_gpio_port *port =
 175                gpiochip_get_data(irq_data_get_irq_chip_data(d));
 176        u8 irqc;
 177
 178        switch (type) {
 179        case IRQ_TYPE_EDGE_RISING:
 180                irqc = PORT_INT_RISING_EDGE;
 181                break;
 182        case IRQ_TYPE_EDGE_FALLING:
 183                irqc = PORT_INT_FALLING_EDGE;
 184                break;
 185        case IRQ_TYPE_EDGE_BOTH:
 186                irqc = PORT_INT_EITHER_EDGE;
 187                break;
 188        case IRQ_TYPE_LEVEL_LOW:
 189                irqc = PORT_INT_LOGIC_ZERO;
 190                break;
 191        case IRQ_TYPE_LEVEL_HIGH:
 192                irqc = PORT_INT_LOGIC_ONE;
 193                break;
 194        default:
 195                return -EINVAL;
 196        }
 197
 198        port->irqc[d->hwirq] = irqc;
 199
 200        if (type & IRQ_TYPE_LEVEL_MASK)
 201                irq_set_handler_locked(d, handle_level_irq);
 202        else
 203                irq_set_handler_locked(d, handle_edge_irq);
 204
 205        return 0;
 206}
 207
 208static void vf610_gpio_irq_mask(struct irq_data *d)
 209{
 210        struct vf610_gpio_port *port =
 211                gpiochip_get_data(irq_data_get_irq_chip_data(d));
 212        void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
 213
 214        vf610_gpio_writel(0, pcr_base);
 215}
 216
 217static void vf610_gpio_irq_unmask(struct irq_data *d)
 218{
 219        struct vf610_gpio_port *port =
 220                gpiochip_get_data(irq_data_get_irq_chip_data(d));
 221        void __iomem *pcr_base = port->base + PORT_PCR(d->hwirq);
 222
 223        vf610_gpio_writel(port->irqc[d->hwirq] << PORT_PCR_IRQC_OFFSET,
 224                          pcr_base);
 225}
 226
 227static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)
 228{
 229        struct vf610_gpio_port *port =
 230                gpiochip_get_data(irq_data_get_irq_chip_data(d));
 231
 232        if (enable)
 233                enable_irq_wake(port->irq);
 234        else
 235                disable_irq_wake(port->irq);
 236
 237        return 0;
 238}
 239
 240static void vf610_gpio_disable_clk(void *data)
 241{
 242        clk_disable_unprepare(data);
 243}
 244
 245static int vf610_gpio_probe(struct platform_device *pdev)
 246{
 247        struct device *dev = &pdev->dev;
 248        struct device_node *np = dev->of_node;
 249        struct vf610_gpio_port *port;
 250        struct gpio_chip *gc;
 251        struct gpio_irq_chip *girq;
 252        struct irq_chip *ic;
 253        int i;
 254        int ret;
 255
 256        port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
 257        if (!port)
 258                return -ENOMEM;
 259
 260        port->sdata = of_device_get_match_data(dev);
 261        port->base = devm_platform_ioremap_resource(pdev, 0);
 262        if (IS_ERR(port->base))
 263                return PTR_ERR(port->base);
 264
 265        port->gpio_base = devm_platform_ioremap_resource(pdev, 1);
 266        if (IS_ERR(port->gpio_base))
 267                return PTR_ERR(port->gpio_base);
 268
 269        port->irq = platform_get_irq(pdev, 0);
 270        if (port->irq < 0)
 271                return port->irq;
 272
 273        port->clk_port = devm_clk_get(dev, "port");
 274        ret = PTR_ERR_OR_ZERO(port->clk_port);
 275        if (!ret) {
 276                ret = clk_prepare_enable(port->clk_port);
 277                if (ret)
 278                        return ret;
 279                ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk,
 280                                               port->clk_port);
 281                if (ret)
 282                        return ret;
 283        } else if (ret == -EPROBE_DEFER) {
 284                /*
 285                 * Percolate deferrals, for anything else,
 286                 * just live without the clocking.
 287                 */
 288                return ret;
 289        }
 290
 291        port->clk_gpio = devm_clk_get(dev, "gpio");
 292        ret = PTR_ERR_OR_ZERO(port->clk_gpio);
 293        if (!ret) {
 294                ret = clk_prepare_enable(port->clk_gpio);
 295                if (ret)
 296                        return ret;
 297                ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk,
 298                                               port->clk_gpio);
 299                if (ret)
 300                        return ret;
 301        } else if (ret == -EPROBE_DEFER) {
 302                return ret;
 303        }
 304
 305        gc = &port->gc;
 306        gc->parent = dev;
 307        gc->label = "vf610-gpio";
 308        gc->ngpio = VF610_GPIO_PER_PORT;
 309        gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;
 310
 311        gc->request = gpiochip_generic_request;
 312        gc->free = gpiochip_generic_free;
 313        gc->direction_input = vf610_gpio_direction_input;
 314        gc->get = vf610_gpio_get;
 315        gc->direction_output = vf610_gpio_direction_output;
 316        gc->set = vf610_gpio_set;
 317
 318        ic = &port->ic;
 319        ic->name = "gpio-vf610";
 320        ic->irq_ack = vf610_gpio_irq_ack;
 321        ic->irq_mask = vf610_gpio_irq_mask;
 322        ic->irq_unmask = vf610_gpio_irq_unmask;
 323        ic->irq_set_type = vf610_gpio_irq_set_type;
 324        ic->irq_set_wake = vf610_gpio_irq_set_wake;
 325
 326        /* Mask all GPIO interrupts */
 327        for (i = 0; i < gc->ngpio; i++)
 328                vf610_gpio_writel(0, port->base + PORT_PCR(i));
 329
 330        /* Clear the interrupt status register for all GPIO's */
 331        vf610_gpio_writel(~0, port->base + PORT_ISFR);
 332
 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;
 345
 346        return devm_gpiochip_add_data(dev, gc, port);
 347}
 348
 349static struct platform_driver vf610_gpio_driver = {
 350        .driver         = {
 351                .name   = "gpio-vf610",
 352                .of_match_table = vf610_gpio_dt_ids,
 353        },
 354        .probe          = vf610_gpio_probe,
 355};
 356
 357builtin_platform_driver(vf610_gpio_driver);
 358