417 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Freescale vf610 GPIO support through PORT and GPIO4 *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_irq.h>21#include <linux/pinctrl/consumer.h>22 23#define VF610_GPIO_PER_PORT 3224 25struct fsl_gpio_soc_data {26 /* SoCs has a Port Data Direction Register (PDDR) */27 bool have_paddr;28 bool have_dual_base;29};30 31struct vf610_gpio_port {32 struct gpio_chip gc;33 void __iomem *base;34 void __iomem *gpio_base;35 const struct fsl_gpio_soc_data *sdata;36 u8 irqc[VF610_GPIO_PER_PORT];37 struct clk *clk_port;38 struct clk *clk_gpio;39 int irq;40};41 42#define GPIO_PDOR 0x0043#define GPIO_PSOR 0x0444#define GPIO_PCOR 0x0845#define GPIO_PTOR 0x0c46#define GPIO_PDIR 0x1047#define GPIO_PDDR 0x1448 49#define PORT_PCR(n) ((n) * 0x4)50#define PORT_PCR_IRQC_OFFSET 1651 52#define PORT_ISFR 0xa053#define PORT_DFER 0xc054#define PORT_DFCR 0xc455#define PORT_DFWR 0xc856 57#define PORT_INT_OFF 0x058#define PORT_INT_LOGIC_ZERO 0x859#define PORT_INT_RISING_EDGE 0x960#define PORT_INT_FALLING_EDGE 0xa61#define PORT_INT_EITHER_EDGE 0xb62#define PORT_INT_LOGIC_ONE 0xc63 64#define IMX8ULP_GPIO_BASE_OFF 0x4065#define IMX8ULP_BASE_OFF 0x8066 67static const struct fsl_gpio_soc_data vf610_data = {68 .have_dual_base = true,69};70 71static const struct fsl_gpio_soc_data imx_data = {72 .have_paddr = true,73 .have_dual_base = true,74};75 76static const struct fsl_gpio_soc_data imx8ulp_data = {77 .have_paddr = true,78};79 80static const struct of_device_id vf610_gpio_dt_ids[] = {81 { .compatible = "fsl,vf610-gpio", .data = &vf610_data },82 { .compatible = "fsl,imx7ulp-gpio", .data = &imx_data, },83 { .compatible = "fsl,imx8ulp-gpio", .data = &imx8ulp_data, },84 { /* sentinel */ }85};86 87static inline void vf610_gpio_writel(u32 val, void __iomem *reg)88{89 writel_relaxed(val, reg);90}91 92static inline u32 vf610_gpio_readl(void __iomem *reg)93{94 return readl_relaxed(reg);95}96 97static int vf610_gpio_get(struct gpio_chip *gc, unsigned int gpio)98{99 struct vf610_gpio_port *port = gpiochip_get_data(gc);100 u32 mask = BIT(gpio);101 unsigned long offset = GPIO_PDIR;102 103 if (port->sdata->have_paddr) {104 mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR);105 if (mask)106 offset = GPIO_PDOR;107 }108 109 return !!(vf610_gpio_readl(port->gpio_base + offset) & BIT(gpio));110}111 112static void vf610_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)113{114 struct vf610_gpio_port *port = gpiochip_get_data(gc);115 u32 mask = BIT(gpio);116 unsigned long offset = val ? GPIO_PSOR : GPIO_PCOR;117 118 vf610_gpio_writel(mask, port->gpio_base + offset);119}120 121static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned int gpio)122{123 struct vf610_gpio_port *port = gpiochip_get_data(chip);124 u32 mask = BIT(gpio);125 u32 val;126 127 if (port->sdata->have_paddr) {128 val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);129 val &= ~mask;130 vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);131 }132 133 return pinctrl_gpio_direction_input(chip, gpio);134}135 136static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio,137 int value)138{139 struct vf610_gpio_port *port = gpiochip_get_data(chip);140 u32 mask = BIT(gpio);141 u32 val;142 143 vf610_gpio_set(chip, gpio, value);144 145 if (port->sdata->have_paddr) {146 val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);147 val |= mask;148 vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);149 }150 151 return pinctrl_gpio_direction_output(chip, gpio);152}153 154static int vf610_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)155{156 struct vf610_gpio_port *port = gpiochip_get_data(gc);157 u32 mask = BIT(gpio);158 159 mask &= vf610_gpio_readl(port->gpio_base + GPIO_PDDR);160 161 if (mask)162 return GPIO_LINE_DIRECTION_OUT;163 164 return GPIO_LINE_DIRECTION_IN;165}166 167static void vf610_gpio_irq_handler(struct irq_desc *desc)168{169 struct vf610_gpio_port *port =170 gpiochip_get_data(irq_desc_get_handler_data(desc));171 struct irq_chip *chip = irq_desc_get_chip(desc);172 int pin;173 unsigned long irq_isfr;174 175 chained_irq_enter(chip, desc);176 177 irq_isfr = vf610_gpio_readl(port->base + PORT_ISFR);178 179 for_each_set_bit(pin, &irq_isfr, VF610_GPIO_PER_PORT) {180 vf610_gpio_writel(BIT(pin), port->base + PORT_ISFR);181 182 generic_handle_domain_irq(port->gc.irq.domain, pin);183 }184 185 chained_irq_exit(chip, desc);186}187 188static void vf610_gpio_irq_ack(struct irq_data *d)189{190 struct vf610_gpio_port *port =191 gpiochip_get_data(irq_data_get_irq_chip_data(d));192 int gpio = d->hwirq;193 194 vf610_gpio_writel(BIT(gpio), port->base + PORT_ISFR);195}196 197static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type)198{199 struct vf610_gpio_port *port =200 gpiochip_get_data(irq_data_get_irq_chip_data(d));201 u8 irqc;202 203 switch (type) {204 case IRQ_TYPE_EDGE_RISING:205 irqc = PORT_INT_RISING_EDGE;206 break;207 case IRQ_TYPE_EDGE_FALLING:208 irqc = PORT_INT_FALLING_EDGE;209 break;210 case IRQ_TYPE_EDGE_BOTH:211 irqc = PORT_INT_EITHER_EDGE;212 break;213 case IRQ_TYPE_LEVEL_LOW:214 irqc = PORT_INT_LOGIC_ZERO;215 break;216 case IRQ_TYPE_LEVEL_HIGH:217 irqc = PORT_INT_LOGIC_ONE;218 break;219 default:220 return -EINVAL;221 }222 223 port->irqc[d->hwirq] = irqc;224 225 if (type & IRQ_TYPE_LEVEL_MASK)226 irq_set_handler_locked(d, handle_level_irq);227 else228 irq_set_handler_locked(d, handle_edge_irq);229 230 return 0;231}232 233static void vf610_gpio_irq_mask(struct irq_data *d)234{235 struct gpio_chip *gc = irq_data_get_irq_chip_data(d);236 struct vf610_gpio_port *port = gpiochip_get_data(gc);237 irq_hw_number_t gpio_num = irqd_to_hwirq(d);238 void __iomem *pcr_base = port->base + PORT_PCR(gpio_num);239 240 vf610_gpio_writel(0, pcr_base);241 gpiochip_disable_irq(gc, gpio_num);242}243 244static void vf610_gpio_irq_unmask(struct irq_data *d)245{246 struct gpio_chip *gc = irq_data_get_irq_chip_data(d);247 struct vf610_gpio_port *port = gpiochip_get_data(gc);248 irq_hw_number_t gpio_num = irqd_to_hwirq(d);249 void __iomem *pcr_base = port->base + PORT_PCR(gpio_num);250 251 gpiochip_enable_irq(gc, gpio_num);252 vf610_gpio_writel(port->irqc[gpio_num] << PORT_PCR_IRQC_OFFSET,253 pcr_base);254}255 256static int vf610_gpio_irq_set_wake(struct irq_data *d, u32 enable)257{258 struct vf610_gpio_port *port =259 gpiochip_get_data(irq_data_get_irq_chip_data(d));260 261 if (enable)262 enable_irq_wake(port->irq);263 else264 disable_irq_wake(port->irq);265 266 return 0;267}268 269static const struct irq_chip vf610_irqchip = {270 .name = "gpio-vf610",271 .irq_ack = vf610_gpio_irq_ack,272 .irq_mask = vf610_gpio_irq_mask,273 .irq_unmask = vf610_gpio_irq_unmask,274 .irq_set_type = vf610_gpio_irq_set_type,275 .irq_set_wake = vf610_gpio_irq_set_wake,276 .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND277 | IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND,278 GPIOCHIP_IRQ_RESOURCE_HELPERS,279};280 281static void vf610_gpio_disable_clk(void *data)282{283 clk_disable_unprepare(data);284}285 286static int vf610_gpio_probe(struct platform_device *pdev)287{288 struct device *dev = &pdev->dev;289 struct vf610_gpio_port *port;290 struct gpio_chip *gc;291 struct gpio_irq_chip *girq;292 int i;293 int ret;294 bool dual_base;295 296 port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);297 if (!port)298 return -ENOMEM;299 300 port->sdata = of_device_get_match_data(dev);301 302 dual_base = port->sdata->have_dual_base;303 304 /*305 * Handle legacy compatible combinations which used two reg values306 * for the i.MX8ULP and i.MX93.307 */308 if (device_is_compatible(dev, "fsl,imx7ulp-gpio") &&309 (device_is_compatible(dev, "fsl,imx93-gpio") ||310 (device_is_compatible(dev, "fsl,imx8ulp-gpio"))))311 dual_base = true;312 313 if (dual_base) {314 port->base = devm_platform_ioremap_resource(pdev, 0);315 if (IS_ERR(port->base))316 return PTR_ERR(port->base);317 318 port->gpio_base = devm_platform_ioremap_resource(pdev, 1);319 if (IS_ERR(port->gpio_base))320 return PTR_ERR(port->gpio_base);321 } else {322 port->base = devm_platform_ioremap_resource(pdev, 0);323 if (IS_ERR(port->base))324 return PTR_ERR(port->base);325 326 port->gpio_base = port->base + IMX8ULP_GPIO_BASE_OFF;327 port->base = port->base + IMX8ULP_BASE_OFF;328 }329 330 port->irq = platform_get_irq(pdev, 0);331 if (port->irq < 0)332 return port->irq;333 334 port->clk_port = devm_clk_get(dev, "port");335 ret = PTR_ERR_OR_ZERO(port->clk_port);336 if (!ret) {337 ret = clk_prepare_enable(port->clk_port);338 if (ret)339 return ret;340 ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk,341 port->clk_port);342 if (ret)343 return ret;344 } else if (ret == -EPROBE_DEFER) {345 /*346 * Percolate deferrals, for anything else,347 * just live without the clocking.348 */349 return ret;350 }351 352 port->clk_gpio = devm_clk_get(dev, "gpio");353 ret = PTR_ERR_OR_ZERO(port->clk_gpio);354 if (!ret) {355 ret = clk_prepare_enable(port->clk_gpio);356 if (ret)357 return ret;358 ret = devm_add_action_or_reset(dev, vf610_gpio_disable_clk,359 port->clk_gpio);360 if (ret)361 return ret;362 } else if (ret == -EPROBE_DEFER) {363 return ret;364 }365 366 gc = &port->gc;367 gc->parent = dev;368 gc->label = dev_name(dev);369 gc->ngpio = VF610_GPIO_PER_PORT;370 gc->base = -1;371 372 gc->request = gpiochip_generic_request;373 gc->free = gpiochip_generic_free;374 gc->direction_input = vf610_gpio_direction_input;375 gc->get = vf610_gpio_get;376 gc->direction_output = vf610_gpio_direction_output;377 gc->set = vf610_gpio_set;378 /*379 * only IP has Port Data Direction Register(PDDR) can380 * support get direction381 */382 if (port->sdata->have_paddr)383 gc->get_direction = vf610_gpio_get_direction;384 385 /* Mask all GPIO interrupts */386 for (i = 0; i < gc->ngpio; i++)387 vf610_gpio_writel(0, port->base + PORT_PCR(i));388 389 /* Clear the interrupt status register for all GPIO's */390 vf610_gpio_writel(~0, port->base + PORT_ISFR);391 392 girq = &gc->irq;393 gpio_irq_chip_set_chip(girq, &vf610_irqchip);394 girq->parent_handler = vf610_gpio_irq_handler;395 girq->num_parents = 1;396 girq->parents = devm_kcalloc(&pdev->dev, 1,397 sizeof(*girq->parents),398 GFP_KERNEL);399 if (!girq->parents)400 return -ENOMEM;401 girq->parents[0] = port->irq;402 girq->default_type = IRQ_TYPE_NONE;403 girq->handler = handle_edge_irq;404 405 return devm_gpiochip_add_data(dev, gc, port);406}407 408static struct platform_driver vf610_gpio_driver = {409 .driver = {410 .name = "gpio-vf610",411 .of_match_table = vf610_gpio_dt_ids,412 },413 .probe = vf610_gpio_probe,414};415 416builtin_platform_driver(vf610_gpio_driver);417