blob: df30490da820da8f4c31fce7df386e2e0efb50a8 [file] [log] [blame]
Fabio Estevam339e7732018-05-21 23:32:55 -03001// SPDX-License-Identifier: GPL-2.0+
2//
3// MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
4// Copyright 2008 Juergen Beisert, kernel@pengutronix.de
5//
6// Based on code from Freescale,
7// Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
Shawn Guofba311f2010-12-18 21:39:31 +08008
Thierry Reding641d0342013-01-21 11:09:01 +01009#include <linux/err.h>
Shawn Guofba311f2010-12-18 21:39:31 +080010#include <linux/init.h>
11#include <linux/interrupt.h>
12#include <linux/io.h>
13#include <linux/irq.h>
Shawn Guo0b76c542012-08-20 16:43:32 +080014#include <linux/irqdomain.h>
Shawn Guo4052d452012-05-04 14:29:22 +080015#include <linux/of.h>
16#include <linux/of_address.h>
17#include <linux/of_device.h>
Shawn Guo8d7cf832011-06-06 09:37:58 -060018#include <linux/platform_device.h>
19#include <linux/slab.h>
Linus Walleij0f4630f2015-12-04 14:02:58 +010020#include <linux/gpio/driver.h>
21/* FIXME: for gpio_get_value(), replace this by direct register read */
22#include <linux/gpio.h>
Paul Gortmakerbb207ef2011-07-03 13:38:09 -040023#include <linux/module.h>
Shawn Guofba311f2010-12-18 21:39:31 +080024
Shawn Guo8d7cf832011-06-06 09:37:58 -060025#define MXS_SET 0x4
26#define MXS_CLR 0x8
Shawn Guofba311f2010-12-18 21:39:31 +080027
Shawn Guo164387d2012-05-03 23:32:52 +080028#define PINCTRL_DOUT(p) ((is_imx23_gpio(p) ? 0x0500 : 0x0700) + (p->id) * 0x10)
29#define PINCTRL_DIN(p) ((is_imx23_gpio(p) ? 0x0600 : 0x0900) + (p->id) * 0x10)
30#define PINCTRL_DOE(p) ((is_imx23_gpio(p) ? 0x0700 : 0x0b00) + (p->id) * 0x10)
31#define PINCTRL_PIN2IRQ(p) ((is_imx23_gpio(p) ? 0x0800 : 0x1000) + (p->id) * 0x10)
32#define PINCTRL_IRQEN(p) ((is_imx23_gpio(p) ? 0x0900 : 0x1100) + (p->id) * 0x10)
33#define PINCTRL_IRQLEV(p) ((is_imx23_gpio(p) ? 0x0a00 : 0x1200) + (p->id) * 0x10)
34#define PINCTRL_IRQPOL(p) ((is_imx23_gpio(p) ? 0x0b00 : 0x1300) + (p->id) * 0x10)
35#define PINCTRL_IRQSTAT(p) ((is_imx23_gpio(p) ? 0x0c00 : 0x1400) + (p->id) * 0x10)
Shawn Guofba311f2010-12-18 21:39:31 +080036
37#define GPIO_INT_FALL_EDGE 0x0
38#define GPIO_INT_LOW_LEV 0x1
39#define GPIO_INT_RISE_EDGE 0x2
40#define GPIO_INT_HIGH_LEV 0x3
41#define GPIO_INT_LEV_MASK (1 << 0)
42#define GPIO_INT_POL_MASK (1 << 1)
43
Shawn Guo164387d2012-05-03 23:32:52 +080044enum mxs_gpio_id {
45 IMX23_GPIO,
46 IMX28_GPIO,
47};
48
Grant Likely7b2fa572011-06-06 09:37:58 -060049struct mxs_gpio_port {
50 void __iomem *base;
51 int id;
52 int irq;
Shawn Guo0b76c542012-08-20 16:43:32 +080053 struct irq_domain *domain;
Linus Walleij0f4630f2015-12-04 14:02:58 +010054 struct gpio_chip gc;
Bartosz Golaszewski5751d3d2017-08-09 14:25:07 +020055 struct device *dev;
Shawn Guo164387d2012-05-03 23:32:52 +080056 enum mxs_gpio_id devid;
Gwenhael Goavec-Merou66d79902013-01-29 09:16:33 +010057 u32 both_edges;
Grant Likely7b2fa572011-06-06 09:37:58 -060058};
59
Shawn Guo164387d2012-05-03 23:32:52 +080060static inline int is_imx23_gpio(struct mxs_gpio_port *port)
61{
62 return port->devid == IMX23_GPIO;
63}
64
65static inline int is_imx28_gpio(struct mxs_gpio_port *port)
66{
67 return port->devid == IMX28_GPIO;
68}
69
Shawn Guofba311f2010-12-18 21:39:31 +080070/* Note: This driver assumes 32 GPIOs are handled in one register */
71
Uwe Kleine-Königbf0c11182011-02-18 21:31:41 +010072static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
Shawn Guofba311f2010-12-18 21:39:31 +080073{
Gwenhael Goavec-Merou66d79902013-01-29 09:16:33 +010074 u32 val;
Shawn Guo0b76c542012-08-20 16:43:32 +080075 u32 pin_mask = 1 << d->hwirq;
Shawn Guo498c17c2011-06-07 22:00:54 +080076 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
Sascha Hauerf08ea3c2016-10-21 15:11:38 +020077 struct irq_chip_type *ct = irq_data_get_chip_type(d);
Shawn Guo498c17c2011-06-07 22:00:54 +080078 struct mxs_gpio_port *port = gc->private;
Shawn Guofba311f2010-12-18 21:39:31 +080079 void __iomem *pin_addr;
80 int edge;
81
Sascha Hauerf08ea3c2016-10-21 15:11:38 +020082 if (!(ct->type & type))
83 if (irq_setup_alt_chip(d, type))
84 return -EINVAL;
85
Gwenhael Goavec-Merou66d79902013-01-29 09:16:33 +010086 port->both_edges &= ~pin_mask;
Shawn Guofba311f2010-12-18 21:39:31 +080087 switch (type) {
Gwenhael Goavec-Merou66d79902013-01-29 09:16:33 +010088 case IRQ_TYPE_EDGE_BOTH:
Linus Walleij0f4630f2015-12-04 14:02:58 +010089 val = gpio_get_value(port->gc.base + d->hwirq);
Gwenhael Goavec-Merou66d79902013-01-29 09:16:33 +010090 if (val)
91 edge = GPIO_INT_FALL_EDGE;
92 else
93 edge = GPIO_INT_RISE_EDGE;
94 port->both_edges |= pin_mask;
95 break;
Shawn Guofba311f2010-12-18 21:39:31 +080096 case IRQ_TYPE_EDGE_RISING:
97 edge = GPIO_INT_RISE_EDGE;
98 break;
99 case IRQ_TYPE_EDGE_FALLING:
100 edge = GPIO_INT_FALL_EDGE;
101 break;
102 case IRQ_TYPE_LEVEL_LOW:
103 edge = GPIO_INT_LOW_LEV;
104 break;
105 case IRQ_TYPE_LEVEL_HIGH:
106 edge = GPIO_INT_HIGH_LEV;
107 break;
108 default:
109 return -EINVAL;
110 }
111
112 /* set level or edge */
Shawn Guo164387d2012-05-03 23:32:52 +0800113 pin_addr = port->base + PINCTRL_IRQLEV(port);
Sascha Hauerf08ea3c2016-10-21 15:11:38 +0200114 if (edge & GPIO_INT_LEV_MASK) {
Shawn Guo8d7cf832011-06-06 09:37:58 -0600115 writel(pin_mask, pin_addr + MXS_SET);
Sascha Hauerf08ea3c2016-10-21 15:11:38 +0200116 writel(pin_mask, port->base + PINCTRL_IRQEN(port) + MXS_SET);
117 } else {
Shawn Guo8d7cf832011-06-06 09:37:58 -0600118 writel(pin_mask, pin_addr + MXS_CLR);
Sascha Hauerf08ea3c2016-10-21 15:11:38 +0200119 writel(pin_mask, port->base + PINCTRL_PIN2IRQ(port) + MXS_SET);
120 }
Shawn Guofba311f2010-12-18 21:39:31 +0800121
122 /* set polarity */
Shawn Guo164387d2012-05-03 23:32:52 +0800123 pin_addr = port->base + PINCTRL_IRQPOL(port);
Shawn Guofba311f2010-12-18 21:39:31 +0800124 if (edge & GPIO_INT_POL_MASK)
Shawn Guo8d7cf832011-06-06 09:37:58 -0600125 writel(pin_mask, pin_addr + MXS_SET);
Shawn Guofba311f2010-12-18 21:39:31 +0800126 else
Shawn Guo8d7cf832011-06-06 09:37:58 -0600127 writel(pin_mask, pin_addr + MXS_CLR);
Shawn Guofba311f2010-12-18 21:39:31 +0800128
Fabio Estevam2bee9e02018-07-24 13:29:28 -0300129 writel(pin_mask, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
Shawn Guofba311f2010-12-18 21:39:31 +0800130
131 return 0;
132}
133
Gwenhael Goavec-Merou66d79902013-01-29 09:16:33 +0100134static void mxs_flip_edge(struct mxs_gpio_port *port, u32 gpio)
135{
136 u32 bit, val, edge;
137 void __iomem *pin_addr;
138
139 bit = 1 << gpio;
140
141 pin_addr = port->base + PINCTRL_IRQPOL(port);
142 val = readl(pin_addr);
143 edge = val & bit;
144
145 if (edge)
146 writel(bit, pin_addr + MXS_CLR);
147 else
148 writel(bit, pin_addr + MXS_SET);
149}
150
Shawn Guofba311f2010-12-18 21:39:31 +0800151/* MXS has one interrupt *per* gpio port */
Thomas Gleixnerbd0b9ac2015-09-14 10:42:37 +0200152static void mxs_gpio_irq_handler(struct irq_desc *desc)
Shawn Guofba311f2010-12-18 21:39:31 +0800153{
154 u32 irq_stat;
Jiang Liu476f8b42015-06-04 12:13:15 +0800155 struct mxs_gpio_port *port = irq_desc_get_handler_data(desc);
Shawn Guofba311f2010-12-18 21:39:31 +0800156
Uwe Kleine-König1f6b5dd2011-01-25 16:54:22 +0100157 desc->irq_data.chip->irq_ack(&desc->irq_data);
158
Shawn Guo164387d2012-05-03 23:32:52 +0800159 irq_stat = readl(port->base + PINCTRL_IRQSTAT(port)) &
160 readl(port->base + PINCTRL_IRQEN(port));
Shawn Guofba311f2010-12-18 21:39:31 +0800161
162 while (irq_stat != 0) {
163 int irqoffset = fls(irq_stat) - 1;
Gwenhael Goavec-Merou66d79902013-01-29 09:16:33 +0100164 if (port->both_edges & (1 << irqoffset))
165 mxs_flip_edge(port, irqoffset);
166
Shawn Guo0b76c542012-08-20 16:43:32 +0800167 generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
Shawn Guofba311f2010-12-18 21:39:31 +0800168 irq_stat &= ~(1 << irqoffset);
169 }
170}
171
172/*
173 * Set interrupt number "irq" in the GPIO as a wake-up source.
174 * While system is running, all registered GPIO interrupts need to have
175 * wake-up enabled. When system is suspended, only selected GPIO interrupts
176 * need to have wake-up enabled.
177 * @param irq interrupt source number
178 * @param enable enable as wake-up if equal to non-zero
179 * @return This function returns 0 on success.
180 */
Uwe Kleine-Königbf0c11182011-02-18 21:31:41 +0100181static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable)
Shawn Guofba311f2010-12-18 21:39:31 +0800182{
Shawn Guo498c17c2011-06-07 22:00:54 +0800183 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
184 struct mxs_gpio_port *port = gc->private;
Shawn Guofba311f2010-12-18 21:39:31 +0800185
Shawn Guo61617152011-06-07 22:00:53 +0800186 if (enable)
187 enable_irq_wake(port->irq);
188 else
189 disable_irq_wake(port->irq);
Shawn Guofba311f2010-12-18 21:39:31 +0800190
191 return 0;
192}
193
Arnd Bergmannabc8d582016-12-16 10:08:14 +0100194static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
Shawn Guo498c17c2011-06-07 22:00:54 +0800195{
196 struct irq_chip_generic *gc;
197 struct irq_chip_type *ct;
Bartosz Golaszewski5751d3d2017-08-09 14:25:07 +0200198 int rv;
Shawn Guo498c17c2011-06-07 22:00:54 +0800199
Bartosz Golaszewski5751d3d2017-08-09 14:25:07 +0200200 gc = devm_irq_alloc_generic_chip(port->dev, "gpio-mxs", 2, irq_base,
201 port->base, handle_level_irq);
Peng Fan1bbc5572015-08-23 21:11:53 +0800202 if (!gc)
203 return -ENOMEM;
204
Shawn Guo498c17c2011-06-07 22:00:54 +0800205 gc->private = port;
206
Sascha Hauerf08ea3c2016-10-21 15:11:38 +0200207 ct = &gc->chip_types[0];
208 ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
Shawn Guo591567a2011-07-19 21:16:56 +0800209 ct->chip.irq_ack = irq_gc_ack_set_bit;
Sascha Hauer66a37c32016-10-21 15:11:37 +0200210 ct->chip.irq_mask = irq_gc_mask_disable_reg;
211 ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
Shawn Guo498c17c2011-06-07 22:00:54 +0800212 ct->chip.irq_set_type = mxs_gpio_set_irq_type;
Shawn Guo591567a2011-07-19 21:16:56 +0800213 ct->chip.irq_set_wake = mxs_gpio_set_wake_irq;
Sascha Hauerf08ea3c2016-10-21 15:11:38 +0200214 ct->chip.flags = IRQCHIP_SET_TYPE_MASKED;
Shawn Guo164387d2012-05-03 23:32:52 +0800215 ct->regs.ack = PINCTRL_IRQSTAT(port) + MXS_CLR;
Sascha Hauerf08ea3c2016-10-21 15:11:38 +0200216 ct->regs.enable = PINCTRL_PIN2IRQ(port) + MXS_SET;
217 ct->regs.disable = PINCTRL_PIN2IRQ(port) + MXS_CLR;
218
219 ct = &gc->chip_types[1];
220 ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
221 ct->chip.irq_ack = irq_gc_ack_set_bit;
222 ct->chip.irq_mask = irq_gc_mask_disable_reg;
223 ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
224 ct->chip.irq_set_type = mxs_gpio_set_irq_type;
225 ct->chip.irq_set_wake = mxs_gpio_set_wake_irq;
226 ct->chip.flags = IRQCHIP_SET_TYPE_MASKED;
Shawn Guo164387d2012-05-03 23:32:52 +0800227 ct->regs.ack = PINCTRL_IRQSTAT(port) + MXS_CLR;
Sascha Hauer66a37c32016-10-21 15:11:37 +0200228 ct->regs.enable = PINCTRL_IRQEN(port) + MXS_SET;
229 ct->regs.disable = PINCTRL_IRQEN(port) + MXS_CLR;
Sascha Hauerf08ea3c2016-10-21 15:11:38 +0200230 ct->handler = handle_level_irq;
Shawn Guo498c17c2011-06-07 22:00:54 +0800231
Bartosz Golaszewski5751d3d2017-08-09 14:25:07 +0200232 rv = devm_irq_setup_generic_chip(port->dev, gc, IRQ_MSK(32),
233 IRQ_GC_INIT_NESTED_LOCK,
234 IRQ_NOREQUEST, 0);
Peng Fan1bbc5572015-08-23 21:11:53 +0800235
Bartosz Golaszewski5751d3d2017-08-09 14:25:07 +0200236 return rv;
Shawn Guo498c17c2011-06-07 22:00:54 +0800237}
Shawn Guofba311f2010-12-18 21:39:31 +0800238
Shawn Guo06f88a82011-06-06 22:31:29 +0800239static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
Shawn Guofba311f2010-12-18 21:39:31 +0800240{
Linus Walleij0f4630f2015-12-04 14:02:58 +0100241 struct mxs_gpio_port *port = gpiochip_get_data(gc);
Shawn Guofba311f2010-12-18 21:39:31 +0800242
Shawn Guo0b76c542012-08-20 16:43:32 +0800243 return irq_find_mapping(port->domain, offset);
Shawn Guofba311f2010-12-18 21:39:31 +0800244}
245
Janusz Uzyckic8aaa1b2014-11-19 09:55:22 +0100246static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
247{
Linus Walleij0f4630f2015-12-04 14:02:58 +0100248 struct mxs_gpio_port *port = gpiochip_get_data(gc);
Janusz Uzyckic8aaa1b2014-11-19 09:55:22 +0100249 u32 mask = 1 << offset;
250 u32 dir;
251
252 dir = readl(port->base + PINCTRL_DOE(port));
253 return !(dir & mask);
254}
255
Krzysztof Kozlowskif4f79d42015-05-02 00:56:47 +0900256static const struct platform_device_id mxs_gpio_ids[] = {
Shawn Guo164387d2012-05-03 23:32:52 +0800257 {
258 .name = "imx23-gpio",
259 .driver_data = IMX23_GPIO,
260 }, {
261 .name = "imx28-gpio",
262 .driver_data = IMX28_GPIO,
263 }, {
264 /* sentinel */
265 }
266};
267MODULE_DEVICE_TABLE(platform, mxs_gpio_ids);
268
Shawn Guo4052d452012-05-04 14:29:22 +0800269static const struct of_device_id mxs_gpio_dt_ids[] = {
270 { .compatible = "fsl,imx23-gpio", .data = (void *) IMX23_GPIO, },
271 { .compatible = "fsl,imx28-gpio", .data = (void *) IMX28_GPIO, },
272 { /* sentinel */ }
273};
274MODULE_DEVICE_TABLE(of, mxs_gpio_dt_ids);
275
Bill Pemberton38363092012-11-19 13:22:34 -0500276static int mxs_gpio_probe(struct platform_device *pdev)
Shawn Guofba311f2010-12-18 21:39:31 +0800277{
Shawn Guo4052d452012-05-04 14:29:22 +0800278 struct device_node *np = pdev->dev.of_node;
279 struct device_node *parent;
Shawn Guo8d7cf832011-06-06 09:37:58 -0600280 static void __iomem *base;
281 struct mxs_gpio_port *port;
Shawn Guo0b76c542012-08-20 16:43:32 +0800282 int irq_base;
Shawn Guo498c17c2011-06-07 22:00:54 +0800283 int err;
Shawn Guofba311f2010-12-18 21:39:31 +0800284
Shawn Guo940a4f72012-05-04 10:30:14 +0800285 port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
Shawn Guo8d7cf832011-06-06 09:37:58 -0600286 if (!port)
287 return -ENOMEM;
Shawn Guofba311f2010-12-18 21:39:31 +0800288
Fabio Estevam99357122013-11-05 17:21:22 -0200289 port->id = of_alias_get_id(np, "gpio");
290 if (port->id < 0)
291 return port->id;
Thierry Reding1f2d3572018-04-30 09:38:12 +0200292 port->devid = (enum mxs_gpio_id)of_device_get_match_data(&pdev->dev);
Bartosz Golaszewski5751d3d2017-08-09 14:25:07 +0200293 port->dev = &pdev->dev;
Shawn Guo940a4f72012-05-04 10:30:14 +0800294 port->irq = platform_get_irq(pdev, 0);
295 if (port->irq < 0)
296 return port->irq;
297
Shawn Guo8d7cf832011-06-06 09:37:58 -0600298 /*
299 * map memory region only once, as all the gpio ports
300 * share the same one
301 */
302 if (!base) {
Fabio Estevam99357122013-11-05 17:21:22 -0200303 parent = of_get_parent(np);
304 base = of_iomap(parent, 0);
305 of_node_put(parent);
306 if (!base)
307 return -EADDRNOTAVAIL;
Shawn Guofba311f2010-12-18 21:39:31 +0800308 }
Shawn Guo8d7cf832011-06-06 09:37:58 -0600309 port->base = base;
310
Sascha Hauerf08ea3c2016-10-21 15:11:38 +0200311 /* initially disable the interrupts */
312 writel(0, port->base + PINCTRL_PIN2IRQ(port));
Shawn Guo164387d2012-05-03 23:32:52 +0800313 writel(0, port->base + PINCTRL_IRQEN(port));
Shawn Guo8d7cf832011-06-06 09:37:58 -0600314
315 /* clear address has to be used to clear IRQSTAT bits */
Shawn Guo164387d2012-05-03 23:32:52 +0800316 writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
Shawn Guo8d7cf832011-06-06 09:37:58 -0600317
Bartosz Golaszewski8514b542017-03-04 17:23:39 +0100318 irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, 32, numa_node_id());
Arvind Yadav44df0812016-10-05 15:08:36 +0530319 if (irq_base < 0) {
320 err = irq_base;
321 goto out_iounmap;
322 }
Shawn Guo0b76c542012-08-20 16:43:32 +0800323
324 port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
325 &irq_domain_simple_ops, NULL);
326 if (!port->domain) {
327 err = -ENODEV;
Bartosz Golaszewski8514b542017-03-04 17:23:39 +0100328 goto out_iounmap;
Shawn Guo0b76c542012-08-20 16:43:32 +0800329 }
330
Shawn Guo498c17c2011-06-07 22:00:54 +0800331 /* gpio-mxs can be a generic irq chip */
Peng Fan1bbc5572015-08-23 21:11:53 +0800332 err = mxs_gpio_init_gc(port, irq_base);
333 if (err < 0)
334 goto out_irqdomain_remove;
Shawn Guo8d7cf832011-06-06 09:37:58 -0600335
336 /* setup one handler for each entry */
Russell Kinga44735f2015-06-16 23:06:45 +0100337 irq_set_chained_handler_and_data(port->irq, mxs_gpio_irq_handler,
338 port);
Shawn Guo8d7cf832011-06-06 09:37:58 -0600339
Linus Walleij0f4630f2015-12-04 14:02:58 +0100340 err = bgpio_init(&port->gc, &pdev->dev, 4,
Shawn Guo164387d2012-05-03 23:32:52 +0800341 port->base + PINCTRL_DIN(port),
Maxime Ripard90dae4e2013-04-29 16:07:18 +0200342 port->base + PINCTRL_DOUT(port) + MXS_SET,
343 port->base + PINCTRL_DOUT(port) + MXS_CLR,
Linus Torvalds84a442b2012-05-26 12:57:47 -0700344 port->base + PINCTRL_DOE(port), NULL, 0);
Shawn Guo8d7cf832011-06-06 09:37:58 -0600345 if (err)
Linus Walleij0f4630f2015-12-04 14:02:58 +0100346 goto out_irqdomain_remove;
Shawn Guofba311f2010-12-18 21:39:31 +0800347
Linus Walleij0f4630f2015-12-04 14:02:58 +0100348 port->gc.to_irq = mxs_gpio_to_irq;
349 port->gc.get_direction = mxs_gpio_get_direction;
350 port->gc.base = port->id * 32;
Shawn Guo06f88a82011-06-06 22:31:29 +0800351
Linus Walleij0f4630f2015-12-04 14:02:58 +0100352 err = gpiochip_add_data(&port->gc, port);
Shawn Guo0b76c542012-08-20 16:43:32 +0800353 if (err)
Linus Walleij0f4630f2015-12-04 14:02:58 +0100354 goto out_irqdomain_remove;
Shawn Guo06f88a82011-06-06 22:31:29 +0800355
Shawn Guofba311f2010-12-18 21:39:31 +0800356 return 0;
Shawn Guo0b76c542012-08-20 16:43:32 +0800357
Peng Fan1bbc5572015-08-23 21:11:53 +0800358out_irqdomain_remove:
359 irq_domain_remove(port->domain);
Arvind Yadav44df0812016-10-05 15:08:36 +0530360out_iounmap:
361 iounmap(port->base);
Shawn Guo0b76c542012-08-20 16:43:32 +0800362 return err;
Shawn Guofba311f2010-12-18 21:39:31 +0800363}
364
Shawn Guo8d7cf832011-06-06 09:37:58 -0600365static struct platform_driver mxs_gpio_driver = {
366 .driver = {
367 .name = "gpio-mxs",
Shawn Guo4052d452012-05-04 14:29:22 +0800368 .of_match_table = mxs_gpio_dt_ids,
Bartosz Golaszewski60909ec2017-08-09 14:25:01 +0200369 .suppress_bind_attrs = true,
Shawn Guo8d7cf832011-06-06 09:37:58 -0600370 },
371 .probe = mxs_gpio_probe,
Shawn Guo164387d2012-05-03 23:32:52 +0800372 .id_table = mxs_gpio_ids,
Shawn Guofba311f2010-12-18 21:39:31 +0800373};
Sascha Haueref196602011-01-24 12:57:46 +0100374
Shawn Guo8d7cf832011-06-06 09:37:58 -0600375static int __init mxs_gpio_init(void)
Sascha Haueref196602011-01-24 12:57:46 +0100376{
Shawn Guo8d7cf832011-06-06 09:37:58 -0600377 return platform_driver_register(&mxs_gpio_driver);
Sascha Haueref196602011-01-24 12:57:46 +0100378}
Shawn Guo8d7cf832011-06-06 09:37:58 -0600379postcore_initcall(mxs_gpio_init);
Shawn Guofba311f2010-12-18 21:39:31 +0800380
Shawn Guo8d7cf832011-06-06 09:37:58 -0600381MODULE_AUTHOR("Freescale Semiconductor, "
382 "Daniel Mack <danielncaiaq.de>, "
383 "Juergen Beisert <kernel@pengutronix.de>");
384MODULE_DESCRIPTION("Freescale MXS GPIO");
385MODULE_LICENSE("GPL");