Commit | Line | Data |
---|---|---|
119f5e44 MD |
1 | /* |
2 | * Renesas R-Car GPIO Support | |
3 | * | |
1fd2b49d | 4 | * Copyright (C) 2014 Renesas Electronics Corporation |
119f5e44 MD |
5 | * Copyright (C) 2013 Magnus Damm |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/err.h> | |
18 | #include <linux/gpio.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/io.h> | |
22 | #include <linux/ioport.h> | |
23 | #include <linux/irq.h> | |
24 | #include <linux/irqdomain.h> | |
25 | #include <linux/module.h> | |
bd0bf468 | 26 | #include <linux/of.h> |
dc3465a9 | 27 | #include <linux/pinctrl/consumer.h> |
119f5e44 MD |
28 | #include <linux/platform_data/gpio-rcar.h> |
29 | #include <linux/platform_device.h> | |
df0c6c80 | 30 | #include <linux/pm_runtime.h> |
119f5e44 MD |
31 | #include <linux/spinlock.h> |
32 | #include <linux/slab.h> | |
33 | ||
34 | struct gpio_rcar_priv { | |
35 | void __iomem *base; | |
36 | spinlock_t lock; | |
37 | struct gpio_rcar_config config; | |
38 | struct platform_device *pdev; | |
39 | struct gpio_chip gpio_chip; | |
40 | struct irq_chip irq_chip; | |
41 | struct irq_domain *irq_domain; | |
42 | }; | |
43 | ||
44 | #define IOINTSEL 0x00 | |
45 | #define INOUTSEL 0x04 | |
46 | #define OUTDT 0x08 | |
47 | #define INDT 0x0c | |
48 | #define INTDT 0x10 | |
49 | #define INTCLR 0x14 | |
50 | #define INTMSK 0x18 | |
51 | #define MSKCLR 0x1c | |
52 | #define POSNEG 0x20 | |
53 | #define EDGLEVEL 0x24 | |
54 | #define FILONOFF 0x28 | |
7e1092b5 | 55 | #define BOTHEDGE 0x4c |
119f5e44 | 56 | |
159f8a02 LP |
57 | #define RCAR_MAX_GPIO_PER_BANK 32 |
58 | ||
119f5e44 MD |
59 | static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs) |
60 | { | |
61 | return ioread32(p->base + offs); | |
62 | } | |
63 | ||
64 | static inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs, | |
65 | u32 value) | |
66 | { | |
67 | iowrite32(value, p->base + offs); | |
68 | } | |
69 | ||
70 | static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs, | |
71 | int bit, bool value) | |
72 | { | |
73 | u32 tmp = gpio_rcar_read(p, offs); | |
74 | ||
75 | if (value) | |
76 | tmp |= BIT(bit); | |
77 | else | |
78 | tmp &= ~BIT(bit); | |
79 | ||
80 | gpio_rcar_write(p, offs, tmp); | |
81 | } | |
82 | ||
83 | static void gpio_rcar_irq_disable(struct irq_data *d) | |
84 | { | |
85 | struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d); | |
86 | ||
87 | gpio_rcar_write(p, INTMSK, ~BIT(irqd_to_hwirq(d))); | |
88 | } | |
89 | ||
90 | static void gpio_rcar_irq_enable(struct irq_data *d) | |
91 | { | |
92 | struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d); | |
93 | ||
94 | gpio_rcar_write(p, MSKCLR, BIT(irqd_to_hwirq(d))); | |
95 | } | |
96 | ||
97 | static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, | |
98 | unsigned int hwirq, | |
99 | bool active_high_rising_edge, | |
7e1092b5 SH |
100 | bool level_trigger, |
101 | bool both) | |
119f5e44 MD |
102 | { |
103 | unsigned long flags; | |
104 | ||
105 | /* follow steps in the GPIO documentation for | |
106 | * "Setting Edge-Sensitive Interrupt Input Mode" and | |
107 | * "Setting Level-Sensitive Interrupt Input Mode" | |
108 | */ | |
109 | ||
110 | spin_lock_irqsave(&p->lock, flags); | |
111 | ||
112 | /* Configure postive or negative logic in POSNEG */ | |
113 | gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge); | |
114 | ||
115 | /* Configure edge or level trigger in EDGLEVEL */ | |
116 | gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger); | |
117 | ||
7e1092b5 SH |
118 | /* Select one edge or both edges in BOTHEDGE */ |
119 | if (p->config.has_both_edge_trigger) | |
120 | gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both); | |
121 | ||
119f5e44 MD |
122 | /* Select "Interrupt Input Mode" in IOINTSEL */ |
123 | gpio_rcar_modify_bit(p, IOINTSEL, hwirq, true); | |
124 | ||
125 | /* Write INTCLR in case of edge trigger */ | |
126 | if (!level_trigger) | |
127 | gpio_rcar_write(p, INTCLR, BIT(hwirq)); | |
128 | ||
129 | spin_unlock_irqrestore(&p->lock, flags); | |
130 | } | |
131 | ||
132 | static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) | |
133 | { | |
134 | struct gpio_rcar_priv *p = irq_data_get_irq_chip_data(d); | |
135 | unsigned int hwirq = irqd_to_hwirq(d); | |
136 | ||
137 | dev_dbg(&p->pdev->dev, "sense irq = %d, type = %d\n", hwirq, type); | |
138 | ||
139 | switch (type & IRQ_TYPE_SENSE_MASK) { | |
140 | case IRQ_TYPE_LEVEL_HIGH: | |
7e1092b5 SH |
141 | gpio_rcar_config_interrupt_input_mode(p, hwirq, true, true, |
142 | false); | |
119f5e44 MD |
143 | break; |
144 | case IRQ_TYPE_LEVEL_LOW: | |
7e1092b5 SH |
145 | gpio_rcar_config_interrupt_input_mode(p, hwirq, false, true, |
146 | false); | |
119f5e44 MD |
147 | break; |
148 | case IRQ_TYPE_EDGE_RISING: | |
7e1092b5 SH |
149 | gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, |
150 | false); | |
119f5e44 MD |
151 | break; |
152 | case IRQ_TYPE_EDGE_FALLING: | |
7e1092b5 SH |
153 | gpio_rcar_config_interrupt_input_mode(p, hwirq, false, false, |
154 | false); | |
155 | break; | |
156 | case IRQ_TYPE_EDGE_BOTH: | |
157 | if (!p->config.has_both_edge_trigger) | |
158 | return -EINVAL; | |
159 | gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, | |
160 | true); | |
119f5e44 MD |
161 | break; |
162 | default: | |
163 | return -EINVAL; | |
164 | } | |
165 | return 0; | |
166 | } | |
167 | ||
168 | static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id) | |
169 | { | |
170 | struct gpio_rcar_priv *p = dev_id; | |
171 | u32 pending; | |
172 | unsigned int offset, irqs_handled = 0; | |
173 | ||
8808b64d VB |
174 | while ((pending = gpio_rcar_read(p, INTDT) & |
175 | gpio_rcar_read(p, INTMSK))) { | |
119f5e44 MD |
176 | offset = __ffs(pending); |
177 | gpio_rcar_write(p, INTCLR, BIT(offset)); | |
178 | generic_handle_irq(irq_find_mapping(p->irq_domain, offset)); | |
179 | irqs_handled++; | |
180 | } | |
181 | ||
182 | return irqs_handled ? IRQ_HANDLED : IRQ_NONE; | |
183 | } | |
184 | ||
185 | static inline struct gpio_rcar_priv *gpio_to_priv(struct gpio_chip *chip) | |
186 | { | |
187 | return container_of(chip, struct gpio_rcar_priv, gpio_chip); | |
188 | } | |
189 | ||
190 | static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, | |
191 | unsigned int gpio, | |
192 | bool output) | |
193 | { | |
194 | struct gpio_rcar_priv *p = gpio_to_priv(chip); | |
195 | unsigned long flags; | |
196 | ||
197 | /* follow steps in the GPIO documentation for | |
198 | * "Setting General Output Mode" and | |
199 | * "Setting General Input Mode" | |
200 | */ | |
201 | ||
202 | spin_lock_irqsave(&p->lock, flags); | |
203 | ||
204 | /* Configure postive logic in POSNEG */ | |
205 | gpio_rcar_modify_bit(p, POSNEG, gpio, false); | |
206 | ||
207 | /* Select "General Input/Output Mode" in IOINTSEL */ | |
208 | gpio_rcar_modify_bit(p, IOINTSEL, gpio, false); | |
209 | ||
210 | /* Select Input Mode or Output Mode in INOUTSEL */ | |
211 | gpio_rcar_modify_bit(p, INOUTSEL, gpio, output); | |
212 | ||
213 | spin_unlock_irqrestore(&p->lock, flags); | |
214 | } | |
215 | ||
dc3465a9 LP |
216 | static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset) |
217 | { | |
218 | return pinctrl_request_gpio(chip->base + offset); | |
219 | } | |
220 | ||
221 | static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset) | |
222 | { | |
223 | pinctrl_free_gpio(chip->base + offset); | |
224 | ||
225 | /* Set the GPIO as an input to ensure that the next GPIO request won't | |
226 | * drive the GPIO pin as an output. | |
227 | */ | |
228 | gpio_rcar_config_general_input_output_mode(chip, offset, false); | |
229 | } | |
230 | ||
119f5e44 MD |
231 | static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) |
232 | { | |
233 | gpio_rcar_config_general_input_output_mode(chip, offset, false); | |
234 | return 0; | |
235 | } | |
236 | ||
237 | static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) | |
238 | { | |
ae9550f6 MD |
239 | u32 bit = BIT(offset); |
240 | ||
241 | /* testing on r8a7790 shows that INDT does not show correct pin state | |
242 | * when configured as output, so use OUTDT in case of output pins */ | |
243 | if (gpio_rcar_read(gpio_to_priv(chip), INOUTSEL) & bit) | |
7cb5409b | 244 | return !!(gpio_rcar_read(gpio_to_priv(chip), OUTDT) & bit); |
ae9550f6 | 245 | else |
7cb5409b | 246 | return !!(gpio_rcar_read(gpio_to_priv(chip), INDT) & bit); |
119f5e44 MD |
247 | } |
248 | ||
249 | static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) | |
250 | { | |
251 | struct gpio_rcar_priv *p = gpio_to_priv(chip); | |
252 | unsigned long flags; | |
253 | ||
254 | spin_lock_irqsave(&p->lock, flags); | |
255 | gpio_rcar_modify_bit(p, OUTDT, offset, value); | |
256 | spin_unlock_irqrestore(&p->lock, flags); | |
257 | } | |
258 | ||
259 | static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, | |
260 | int value) | |
261 | { | |
262 | /* write GPIO value to output before selecting output mode of pin */ | |
263 | gpio_rcar_set(chip, offset, value); | |
264 | gpio_rcar_config_general_input_output_mode(chip, offset, true); | |
265 | return 0; | |
266 | } | |
267 | ||
268 | static int gpio_rcar_to_irq(struct gpio_chip *chip, unsigned offset) | |
269 | { | |
270 | return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset); | |
271 | } | |
272 | ||
c0d6c1ad LW |
273 | static int gpio_rcar_irq_domain_map(struct irq_domain *h, unsigned int irq, |
274 | irq_hw_number_t hwirq) | |
119f5e44 MD |
275 | { |
276 | struct gpio_rcar_priv *p = h->host_data; | |
277 | ||
c0d6c1ad | 278 | dev_dbg(&p->pdev->dev, "map hw irq = %d, irq = %d\n", (int)hwirq, irq); |
119f5e44 | 279 | |
c0d6c1ad LW |
280 | irq_set_chip_data(irq, h->host_data); |
281 | irq_set_chip_and_handler(irq, &p->irq_chip, handle_level_irq); | |
282 | set_irq_flags(irq, IRQF_VALID); /* kill me now */ | |
119f5e44 MD |
283 | return 0; |
284 | } | |
285 | ||
286 | static struct irq_domain_ops gpio_rcar_irq_domain_ops = { | |
287 | .map = gpio_rcar_irq_domain_map, | |
fe132649 | 288 | .xlate = irq_domain_xlate_twocell, |
119f5e44 MD |
289 | }; |
290 | ||
850dfe17 LP |
291 | struct gpio_rcar_info { |
292 | bool has_both_edge_trigger; | |
293 | }; | |
294 | ||
1fd2b49d HN |
295 | static const struct gpio_rcar_info gpio_rcar_info_gen1 = { |
296 | .has_both_edge_trigger = false, | |
297 | }; | |
298 | ||
299 | static const struct gpio_rcar_info gpio_rcar_info_gen2 = { | |
300 | .has_both_edge_trigger = true, | |
301 | }; | |
302 | ||
850dfe17 LP |
303 | static const struct of_device_id gpio_rcar_of_table[] = { |
304 | { | |
305 | .compatible = "renesas,gpio-r8a7790", | |
1fd2b49d | 306 | .data = &gpio_rcar_info_gen2, |
850dfe17 LP |
307 | }, { |
308 | .compatible = "renesas,gpio-r8a7791", | |
1fd2b49d HN |
309 | .data = &gpio_rcar_info_gen2, |
310 | }, { | |
311 | .compatible = "renesas,gpio-r8a7793", | |
312 | .data = &gpio_rcar_info_gen2, | |
313 | }, { | |
314 | .compatible = "renesas,gpio-r8a7794", | |
315 | .data = &gpio_rcar_info_gen2, | |
850dfe17 LP |
316 | }, { |
317 | .compatible = "renesas,gpio-rcar", | |
1fd2b49d | 318 | .data = &gpio_rcar_info_gen1, |
850dfe17 LP |
319 | }, { |
320 | /* Terminator */ | |
321 | }, | |
322 | }; | |
323 | ||
324 | MODULE_DEVICE_TABLE(of, gpio_rcar_of_table); | |
325 | ||
326 | static int gpio_rcar_parse_pdata(struct gpio_rcar_priv *p) | |
159f8a02 | 327 | { |
e56aee18 | 328 | struct gpio_rcar_config *pdata = dev_get_platdata(&p->pdev->dev); |
159f8a02 LP |
329 | struct device_node *np = p->pdev->dev.of_node; |
330 | struct of_phandle_args args; | |
331 | int ret; | |
159f8a02 | 332 | |
e305062e | 333 | if (pdata) { |
159f8a02 | 334 | p->config = *pdata; |
e305062e | 335 | } else if (IS_ENABLED(CONFIG_OF) && np) { |
850dfe17 LP |
336 | const struct of_device_id *match; |
337 | const struct gpio_rcar_info *info; | |
338 | ||
339 | match = of_match_node(gpio_rcar_of_table, np); | |
340 | if (!match) | |
341 | return -EINVAL; | |
342 | ||
343 | info = match->data; | |
344 | ||
01eb2d18 LP |
345 | ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, |
346 | &args); | |
347 | p->config.number_of_pins = ret == 0 ? args.args[2] | |
159f8a02 LP |
348 | : RCAR_MAX_GPIO_PER_BANK; |
349 | p->config.gpio_base = -1; | |
850dfe17 | 350 | p->config.has_both_edge_trigger = info->has_both_edge_trigger; |
159f8a02 | 351 | } |
159f8a02 LP |
352 | |
353 | if (p->config.number_of_pins == 0 || | |
354 | p->config.number_of_pins > RCAR_MAX_GPIO_PER_BANK) { | |
355 | dev_warn(&p->pdev->dev, | |
356 | "Invalid number of gpio lines %u, using %u\n", | |
357 | p->config.number_of_pins, RCAR_MAX_GPIO_PER_BANK); | |
358 | p->config.number_of_pins = RCAR_MAX_GPIO_PER_BANK; | |
359 | } | |
850dfe17 LP |
360 | |
361 | return 0; | |
159f8a02 LP |
362 | } |
363 | ||
119f5e44 MD |
364 | static int gpio_rcar_probe(struct platform_device *pdev) |
365 | { | |
119f5e44 MD |
366 | struct gpio_rcar_priv *p; |
367 | struct resource *io, *irq; | |
368 | struct gpio_chip *gpio_chip; | |
369 | struct irq_chip *irq_chip; | |
b22978fc GU |
370 | struct device *dev = &pdev->dev; |
371 | const char *name = dev_name(dev); | |
119f5e44 MD |
372 | int ret; |
373 | ||
b22978fc | 374 | p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); |
119f5e44 | 375 | if (!p) { |
119f5e44 MD |
376 | ret = -ENOMEM; |
377 | goto err0; | |
378 | } | |
379 | ||
119f5e44 | 380 | p->pdev = pdev; |
119f5e44 MD |
381 | spin_lock_init(&p->lock); |
382 | ||
159f8a02 | 383 | /* Get device configuration from DT node or platform data. */ |
850dfe17 LP |
384 | ret = gpio_rcar_parse_pdata(p); |
385 | if (ret < 0) | |
386 | return ret; | |
159f8a02 LP |
387 | |
388 | platform_set_drvdata(pdev, p); | |
389 | ||
df0c6c80 GU |
390 | pm_runtime_enable(dev); |
391 | pm_runtime_get_sync(dev); | |
392 | ||
119f5e44 MD |
393 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
394 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
395 | ||
396 | if (!io || !irq) { | |
b22978fc | 397 | dev_err(dev, "missing IRQ or IOMEM\n"); |
119f5e44 MD |
398 | ret = -EINVAL; |
399 | goto err0; | |
400 | } | |
401 | ||
b22978fc | 402 | p->base = devm_ioremap_nocache(dev, io->start, resource_size(io)); |
119f5e44 | 403 | if (!p->base) { |
b22978fc | 404 | dev_err(dev, "failed to remap I/O memory\n"); |
119f5e44 MD |
405 | ret = -ENXIO; |
406 | goto err0; | |
407 | } | |
408 | ||
409 | gpio_chip = &p->gpio_chip; | |
dc3465a9 LP |
410 | gpio_chip->request = gpio_rcar_request; |
411 | gpio_chip->free = gpio_rcar_free; | |
119f5e44 MD |
412 | gpio_chip->direction_input = gpio_rcar_direction_input; |
413 | gpio_chip->get = gpio_rcar_get; | |
414 | gpio_chip->direction_output = gpio_rcar_direction_output; | |
415 | gpio_chip->set = gpio_rcar_set; | |
416 | gpio_chip->to_irq = gpio_rcar_to_irq; | |
417 | gpio_chip->label = name; | |
b22978fc | 418 | gpio_chip->dev = dev; |
119f5e44 MD |
419 | gpio_chip->owner = THIS_MODULE; |
420 | gpio_chip->base = p->config.gpio_base; | |
421 | gpio_chip->ngpio = p->config.number_of_pins; | |
422 | ||
423 | irq_chip = &p->irq_chip; | |
424 | irq_chip->name = name; | |
425 | irq_chip->irq_mask = gpio_rcar_irq_disable; | |
426 | irq_chip->irq_unmask = gpio_rcar_irq_enable; | |
119f5e44 | 427 | irq_chip->irq_set_type = gpio_rcar_irq_set_type; |
40396112 MD |
428 | irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_SET_TYPE_MASKED |
429 | | IRQCHIP_MASK_ON_SUSPEND; | |
119f5e44 MD |
430 | |
431 | p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, | |
432 | p->config.number_of_pins, | |
433 | p->config.irq_base, | |
434 | &gpio_rcar_irq_domain_ops, p); | |
435 | if (!p->irq_domain) { | |
436 | ret = -ENXIO; | |
b22978fc | 437 | dev_err(dev, "cannot initialize irq domain\n"); |
0c8aab8e | 438 | goto err0; |
119f5e44 MD |
439 | } |
440 | ||
b22978fc GU |
441 | if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler, |
442 | IRQF_SHARED, name, p)) { | |
443 | dev_err(dev, "failed to request IRQ\n"); | |
119f5e44 MD |
444 | ret = -ENOENT; |
445 | goto err1; | |
446 | } | |
447 | ||
448 | ret = gpiochip_add(gpio_chip); | |
449 | if (ret) { | |
b22978fc | 450 | dev_err(dev, "failed to add GPIO controller\n"); |
119f5e44 MD |
451 | goto err1; |
452 | } | |
453 | ||
b22978fc | 454 | dev_info(dev, "driving %d GPIOs\n", p->config.number_of_pins); |
119f5e44 MD |
455 | |
456 | /* warn in case of mismatch if irq base is specified */ | |
457 | if (p->config.irq_base) { | |
458 | ret = irq_find_mapping(p->irq_domain, 0); | |
459 | if (p->config.irq_base != ret) | |
b22978fc | 460 | dev_warn(dev, "irq base mismatch (%u/%u)\n", |
119f5e44 MD |
461 | p->config.irq_base, ret); |
462 | } | |
463 | ||
159f8a02 LP |
464 | if (p->config.pctl_name) { |
465 | ret = gpiochip_add_pin_range(gpio_chip, p->config.pctl_name, 0, | |
466 | gpio_chip->base, gpio_chip->ngpio); | |
467 | if (ret < 0) | |
b22978fc | 468 | dev_warn(dev, "failed to add pin range\n"); |
159f8a02 | 469 | } |
dc3465a9 | 470 | |
119f5e44 MD |
471 | return 0; |
472 | ||
473 | err1: | |
474 | irq_domain_remove(p->irq_domain); | |
475 | err0: | |
df0c6c80 GU |
476 | pm_runtime_put(dev); |
477 | pm_runtime_disable(dev); | |
119f5e44 MD |
478 | return ret; |
479 | } | |
480 | ||
481 | static int gpio_rcar_remove(struct platform_device *pdev) | |
482 | { | |
483 | struct gpio_rcar_priv *p = platform_get_drvdata(pdev); | |
119f5e44 | 484 | |
9f5132ae | 485 | gpiochip_remove(&p->gpio_chip); |
119f5e44 MD |
486 | |
487 | irq_domain_remove(p->irq_domain); | |
df0c6c80 GU |
488 | pm_runtime_put(&pdev->dev); |
489 | pm_runtime_disable(&pdev->dev); | |
119f5e44 MD |
490 | return 0; |
491 | } | |
492 | ||
493 | static struct platform_driver gpio_rcar_device_driver = { | |
494 | .probe = gpio_rcar_probe, | |
495 | .remove = gpio_rcar_remove, | |
496 | .driver = { | |
497 | .name = "gpio_rcar", | |
159f8a02 | 498 | .of_match_table = of_match_ptr(gpio_rcar_of_table), |
119f5e44 MD |
499 | } |
500 | }; | |
501 | ||
502 | module_platform_driver(gpio_rcar_device_driver); | |
503 | ||
504 | MODULE_AUTHOR("Magnus Damm"); | |
505 | MODULE_DESCRIPTION("Renesas R-Car GPIO Driver"); | |
506 | MODULE_LICENSE("GPL v2"); |