blob: 2ffcd9fdd1f2f7d3df917aa0324a07e3f23b17c8 [file] [log] [blame]
Rabin Vincentd3425712015-06-06 22:30:40 +02001#include <linux/kernel.h>
2#include <linux/init.h>
3#include <linux/gpio.h>
Rabin Vincent29b53572015-07-31 14:48:57 +02004#include <linux/gpio/driver.h>
Rabin Vincentd3425712015-06-06 22:30:40 +02005#include <linux/of_gpio.h>
6#include <linux/io.h>
Rabin Vincent29b53572015-07-31 14:48:57 +02007#include <linux/interrupt.h>
Rabin Vincentd3425712015-06-06 22:30:40 +02008#include <linux/platform_device.h>
9#include <linux/basic_mmio_gpio.h>
10
11#define ETRAX_FS_rw_pa_dout 0
12#define ETRAX_FS_r_pa_din 4
13#define ETRAX_FS_rw_pa_oe 8
14#define ETRAX_FS_rw_intr_cfg 12
15#define ETRAX_FS_rw_intr_mask 16
16#define ETRAX_FS_rw_ack_intr 20
17#define ETRAX_FS_r_intr 24
Rabin Vincent29b53572015-07-31 14:48:57 +020018#define ETRAX_FS_r_masked_intr 28
Rabin Vincentd3425712015-06-06 22:30:40 +020019#define ETRAX_FS_rw_pb_dout 32
20#define ETRAX_FS_r_pb_din 36
21#define ETRAX_FS_rw_pb_oe 40
22#define ETRAX_FS_rw_pc_dout 48
23#define ETRAX_FS_r_pc_din 52
24#define ETRAX_FS_rw_pc_oe 56
25#define ETRAX_FS_rw_pd_dout 64
26#define ETRAX_FS_r_pd_din 68
27#define ETRAX_FS_rw_pd_oe 72
28#define ETRAX_FS_rw_pe_dout 80
29#define ETRAX_FS_r_pe_din 84
30#define ETRAX_FS_rw_pe_oe 88
31
Rabin Vincentd7050732015-07-22 15:05:19 +020032#define ARTPEC3_r_pa_din 0
33#define ARTPEC3_rw_pa_dout 4
34#define ARTPEC3_rw_pa_oe 8
35#define ARTPEC3_r_pb_din 44
36#define ARTPEC3_rw_pb_dout 48
37#define ARTPEC3_rw_pb_oe 52
38#define ARTPEC3_r_pc_din 88
39#define ARTPEC3_rw_pc_dout 92
40#define ARTPEC3_rw_pc_oe 96
41#define ARTPEC3_r_pd_din 116
Rabin Vincent29b53572015-07-31 14:48:57 +020042#define ARTPEC3_rw_intr_cfg 120
43#define ARTPEC3_rw_intr_pins 124
44#define ARTPEC3_rw_intr_mask 128
45#define ARTPEC3_rw_ack_intr 132
46#define ARTPEC3_r_masked_intr 140
47
48#define GIO_CFG_OFF 0
49#define GIO_CFG_HI 1
50#define GIO_CFG_LO 2
51#define GIO_CFG_SET 3
52#define GIO_CFG_POSEDGE 5
53#define GIO_CFG_NEGEDGE 6
54#define GIO_CFG_ANYEDGE 7
55
56struct etraxfs_gpio_info;
57
58struct etraxfs_gpio_block {
59 spinlock_t lock;
60 u32 mask;
61 u32 cfg;
62 u32 pins;
63 unsigned int group[8];
64
65 void __iomem *regs;
66 const struct etraxfs_gpio_info *info;
67};
68
69struct etraxfs_gpio_chip {
70 struct bgpio_chip bgc;
71 struct etraxfs_gpio_block *block;
72};
Rabin Vincentd7050732015-07-22 15:05:19 +020073
Rabin Vincentd3425712015-06-06 22:30:40 +020074struct etraxfs_gpio_port {
75 const char *label;
76 unsigned int oe;
77 unsigned int dout;
78 unsigned int din;
79 unsigned int ngpio;
80};
81
82struct etraxfs_gpio_info {
83 unsigned int num_ports;
84 const struct etraxfs_gpio_port *ports;
Rabin Vincent29b53572015-07-31 14:48:57 +020085
86 unsigned int rw_ack_intr;
87 unsigned int rw_intr_mask;
88 unsigned int rw_intr_cfg;
89 unsigned int rw_intr_pins;
90 unsigned int r_masked_intr;
Rabin Vincentd3425712015-06-06 22:30:40 +020091};
92
93static const struct etraxfs_gpio_port etraxfs_gpio_etraxfs_ports[] = {
94 {
95 .label = "A",
96 .ngpio = 8,
97 .oe = ETRAX_FS_rw_pa_oe,
98 .dout = ETRAX_FS_rw_pa_dout,
99 .din = ETRAX_FS_r_pa_din,
100 },
101 {
102 .label = "B",
103 .ngpio = 18,
104 .oe = ETRAX_FS_rw_pb_oe,
105 .dout = ETRAX_FS_rw_pb_dout,
106 .din = ETRAX_FS_r_pb_din,
107 },
108 {
109 .label = "C",
110 .ngpio = 18,
111 .oe = ETRAX_FS_rw_pc_oe,
112 .dout = ETRAX_FS_rw_pc_dout,
113 .din = ETRAX_FS_r_pc_din,
114 },
115 {
116 .label = "D",
117 .ngpio = 18,
118 .oe = ETRAX_FS_rw_pd_oe,
119 .dout = ETRAX_FS_rw_pd_dout,
120 .din = ETRAX_FS_r_pd_din,
121 },
122 {
123 .label = "E",
124 .ngpio = 18,
125 .oe = ETRAX_FS_rw_pe_oe,
126 .dout = ETRAX_FS_rw_pe_dout,
127 .din = ETRAX_FS_r_pe_din,
128 },
129};
130
131static const struct etraxfs_gpio_info etraxfs_gpio_etraxfs = {
132 .num_ports = ARRAY_SIZE(etraxfs_gpio_etraxfs_ports),
133 .ports = etraxfs_gpio_etraxfs_ports,
Rabin Vincent29b53572015-07-31 14:48:57 +0200134 .rw_ack_intr = ETRAX_FS_rw_ack_intr,
135 .rw_intr_mask = ETRAX_FS_rw_intr_mask,
136 .rw_intr_cfg = ETRAX_FS_rw_intr_cfg,
137 .r_masked_intr = ETRAX_FS_r_masked_intr,
Rabin Vincentd3425712015-06-06 22:30:40 +0200138};
139
Rabin Vincentd7050732015-07-22 15:05:19 +0200140static const struct etraxfs_gpio_port etraxfs_gpio_artpec3_ports[] = {
141 {
142 .label = "A",
143 .ngpio = 32,
144 .oe = ARTPEC3_rw_pa_oe,
145 .dout = ARTPEC3_rw_pa_dout,
146 .din = ARTPEC3_r_pa_din,
147 },
148 {
149 .label = "B",
150 .ngpio = 32,
151 .oe = ARTPEC3_rw_pb_oe,
152 .dout = ARTPEC3_rw_pb_dout,
153 .din = ARTPEC3_r_pb_din,
154 },
155 {
156 .label = "C",
157 .ngpio = 16,
158 .oe = ARTPEC3_rw_pc_oe,
159 .dout = ARTPEC3_rw_pc_dout,
160 .din = ARTPEC3_r_pc_din,
161 },
162 {
163 .label = "D",
164 .ngpio = 32,
165 .din = ARTPEC3_r_pd_din,
166 },
167};
168
169static const struct etraxfs_gpio_info etraxfs_gpio_artpec3 = {
170 .num_ports = ARRAY_SIZE(etraxfs_gpio_artpec3_ports),
171 .ports = etraxfs_gpio_artpec3_ports,
Rabin Vincent29b53572015-07-31 14:48:57 +0200172 .rw_ack_intr = ARTPEC3_rw_ack_intr,
173 .rw_intr_mask = ARTPEC3_rw_intr_mask,
174 .rw_intr_cfg = ARTPEC3_rw_intr_cfg,
175 .r_masked_intr = ARTPEC3_r_masked_intr,
176 .rw_intr_pins = ARTPEC3_rw_intr_pins,
Rabin Vincentd7050732015-07-22 15:05:19 +0200177};
178
Rabin Vincent29b53572015-07-31 14:48:57 +0200179static unsigned int etraxfs_gpio_chip_to_port(struct gpio_chip *gc)
180{
181 return gc->label[0] - 'A';
182}
183
Rabin Vincentd3425712015-06-06 22:30:40 +0200184static int etraxfs_gpio_of_xlate(struct gpio_chip *gc,
185 const struct of_phandle_args *gpiospec,
186 u32 *flags)
187{
188 /*
189 * Port numbers are A to E, and the properties are integers, so we
190 * specify them as 0xA - 0xE.
191 */
Rabin Vincent29b53572015-07-31 14:48:57 +0200192 if (etraxfs_gpio_chip_to_port(gc) + 0xA != gpiospec->args[2])
Rabin Vincentd3425712015-06-06 22:30:40 +0200193 return -EINVAL;
194
195 return of_gpio_simple_xlate(gc, gpiospec, flags);
196}
197
198static const struct of_device_id etraxfs_gpio_of_table[] = {
199 {
200 .compatible = "axis,etraxfs-gio",
201 .data = &etraxfs_gpio_etraxfs,
202 },
Rabin Vincentd7050732015-07-22 15:05:19 +0200203 {
204 .compatible = "axis,artpec3-gio",
205 .data = &etraxfs_gpio_artpec3,
206 },
Rabin Vincentd3425712015-06-06 22:30:40 +0200207 {},
208};
209
Rabin Vincent29b53572015-07-31 14:48:57 +0200210static unsigned int etraxfs_gpio_to_group_irq(unsigned int gpio)
211{
212 return gpio % 8;
213}
214
215static unsigned int etraxfs_gpio_to_group_pin(struct etraxfs_gpio_chip *chip,
216 unsigned int gpio)
217{
218 return 4 * etraxfs_gpio_chip_to_port(&chip->bgc.gc) + gpio / 8;
219}
220
221static void etraxfs_gpio_irq_ack(struct irq_data *d)
222{
223 struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
224 struct etraxfs_gpio_block *block = chip->block;
225 unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
226
227 writel(BIT(grpirq), block->regs + block->info->rw_ack_intr);
228}
229
230static void etraxfs_gpio_irq_mask(struct irq_data *d)
231{
232 struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
233 struct etraxfs_gpio_block *block = chip->block;
234 unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
235
236 spin_lock(&block->lock);
237 block->mask &= ~BIT(grpirq);
238 writel(block->mask, block->regs + block->info->rw_intr_mask);
239 spin_unlock(&block->lock);
240}
241
242static void etraxfs_gpio_irq_unmask(struct irq_data *d)
243{
244 struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
245 struct etraxfs_gpio_block *block = chip->block;
246 unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
247
248 spin_lock(&block->lock);
249 block->mask |= BIT(grpirq);
250 writel(block->mask, block->regs + block->info->rw_intr_mask);
251 spin_unlock(&block->lock);
252}
253
254static int etraxfs_gpio_irq_set_type(struct irq_data *d, u32 type)
255{
256 struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
257 struct etraxfs_gpio_block *block = chip->block;
258 unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
259 u32 cfg;
260
261 switch (type) {
262 case IRQ_TYPE_EDGE_RISING:
263 cfg = GIO_CFG_POSEDGE;
264 break;
265 case IRQ_TYPE_EDGE_FALLING:
266 cfg = GIO_CFG_NEGEDGE;
267 break;
268 case IRQ_TYPE_EDGE_BOTH:
269 cfg = GIO_CFG_ANYEDGE;
270 break;
271 case IRQ_TYPE_LEVEL_LOW:
272 cfg = GIO_CFG_LO;
273 break;
274 case IRQ_TYPE_LEVEL_HIGH:
275 cfg = GIO_CFG_HI;
276 break;
277 default:
278 return -EINVAL;
279 }
280
281 spin_lock(&block->lock);
282 block->cfg &= ~(0x7 << (grpirq * 3));
283 block->cfg |= (cfg << (grpirq * 3));
284 writel(block->cfg, block->regs + block->info->rw_intr_cfg);
285 spin_unlock(&block->lock);
286
287 return 0;
288}
289
290static int etraxfs_gpio_irq_request_resources(struct irq_data *d)
291{
292 struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
293 struct etraxfs_gpio_block *block = chip->block;
294 unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
Linus Walleij01e2dae2015-08-31 08:56:04 +0200295 int ret = -EBUSY;
Rabin Vincent29b53572015-07-31 14:48:57 +0200296
297 spin_lock(&block->lock);
298 if (block->group[grpirq])
299 goto out;
300
301 ret = gpiochip_lock_as_irq(&chip->bgc.gc, d->hwirq);
302 if (ret)
303 goto out;
304
305 block->group[grpirq] = d->irq;
306 if (block->info->rw_intr_pins) {
307 unsigned int pin = etraxfs_gpio_to_group_pin(chip, d->hwirq);
308
309 block->pins &= ~(0xf << (grpirq * 4));
310 block->pins |= (pin << (grpirq * 4));
311
312 writel(block->pins, block->regs + block->info->rw_intr_pins);
313 }
314
315out:
316 spin_unlock(&block->lock);
Linus Walleij01e2dae2015-08-31 08:56:04 +0200317 return ret;
Rabin Vincent29b53572015-07-31 14:48:57 +0200318}
319
320static void etraxfs_gpio_irq_release_resources(struct irq_data *d)
321{
322 struct etraxfs_gpio_chip *chip = irq_data_get_irq_chip_data(d);
323 struct etraxfs_gpio_block *block = chip->block;
324 unsigned int grpirq = etraxfs_gpio_to_group_irq(d->hwirq);
325
326 spin_lock(&block->lock);
327 block->group[grpirq] = 0;
328 gpiochip_unlock_as_irq(&chip->bgc.gc, d->hwirq);
329 spin_unlock(&block->lock);
330}
331
332static struct irq_chip etraxfs_gpio_irq_chip = {
333 .name = "gpio-etraxfs",
334 .irq_ack = etraxfs_gpio_irq_ack,
335 .irq_mask = etraxfs_gpio_irq_mask,
336 .irq_unmask = etraxfs_gpio_irq_unmask,
337 .irq_set_type = etraxfs_gpio_irq_set_type,
338 .irq_request_resources = etraxfs_gpio_irq_request_resources,
339 .irq_release_resources = etraxfs_gpio_irq_release_resources,
340};
341
342static irqreturn_t etraxfs_gpio_interrupt(int irq, void *dev_id)
343{
344 struct etraxfs_gpio_block *block = dev_id;
345 unsigned long intr = readl(block->regs + block->info->r_masked_intr);
346 int bit;
347
348 for_each_set_bit(bit, &intr, 8)
349 generic_handle_irq(block->group[bit]);
350
351 return IRQ_RETVAL(intr & 0xff);
352}
353
Rabin Vincentd3425712015-06-06 22:30:40 +0200354static int etraxfs_gpio_probe(struct platform_device *pdev)
355{
356 struct device *dev = &pdev->dev;
357 const struct etraxfs_gpio_info *info;
358 const struct of_device_id *match;
Rabin Vincent29b53572015-07-31 14:48:57 +0200359 struct etraxfs_gpio_block *block;
360 struct etraxfs_gpio_chip *chips;
361 struct resource *res, *irq;
362 bool allportsirq = false;
Rabin Vincentd3425712015-06-06 22:30:40 +0200363 void __iomem *regs;
364 int ret;
365 int i;
366
367 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
368 regs = devm_ioremap_resource(dev, res);
Krzysztof Kozlowski01540312015-07-09 22:19:53 +0900369 if (IS_ERR(regs))
370 return PTR_ERR(regs);
Rabin Vincentd3425712015-06-06 22:30:40 +0200371
372 match = of_match_node(etraxfs_gpio_of_table, dev->of_node);
373 if (!match)
374 return -EINVAL;
375
376 info = match->data;
377
378 chips = devm_kzalloc(dev, sizeof(*chips) * info->num_ports, GFP_KERNEL);
379 if (!chips)
380 return -ENOMEM;
381
Rabin Vincent29b53572015-07-31 14:48:57 +0200382 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
383 if (!irq)
384 return -EINVAL;
385
386 block = devm_kzalloc(dev, sizeof(*block), GFP_KERNEL);
387 if (!block)
388 return -ENOMEM;
389
390 spin_lock_init(&block->lock);
391
392 block->regs = regs;
393 block->info = info;
394
395 writel(0, block->regs + info->rw_intr_mask);
396 writel(0, block->regs + info->rw_intr_cfg);
397 if (info->rw_intr_pins) {
398 allportsirq = true;
399 writel(0, block->regs + info->rw_intr_pins);
400 }
401
402 ret = devm_request_irq(dev, irq->start, etraxfs_gpio_interrupt,
403 IRQF_SHARED, dev_name(dev), block);
404 if (ret) {
405 dev_err(dev, "Unable to request irq %d\n", ret);
406 return ret;
407 }
408
Rabin Vincentd3425712015-06-06 22:30:40 +0200409 for (i = 0; i < info->num_ports; i++) {
Rabin Vincent29b53572015-07-31 14:48:57 +0200410 struct etraxfs_gpio_chip *chip = &chips[i];
411 struct bgpio_chip *bgc = &chip->bgc;
Rabin Vincentd3425712015-06-06 22:30:40 +0200412 const struct etraxfs_gpio_port *port = &info->ports[i];
Rabin Vincentd7050732015-07-22 15:05:19 +0200413 unsigned long flags = BGPIOF_READ_OUTPUT_REG_SET;
414 void __iomem *dat = regs + port->din;
415 void __iomem *set = regs + port->dout;
416 void __iomem *dirout = regs + port->oe;
417
Rabin Vincent29b53572015-07-31 14:48:57 +0200418 chip->block = block;
419
Rabin Vincentd7050732015-07-22 15:05:19 +0200420 if (dirout == set) {
421 dirout = set = NULL;
422 flags = BGPIOF_NO_OUTPUT;
423 }
Rabin Vincentd3425712015-06-06 22:30:40 +0200424
425 ret = bgpio_init(bgc, dev, 4,
Rabin Vincentd7050732015-07-22 15:05:19 +0200426 dat, set, NULL, dirout, NULL,
427 flags);
Rabin Vincent29b53572015-07-31 14:48:57 +0200428 if (ret) {
429 dev_err(dev, "Unable to init port %s\n",
430 port->label);
431 continue;
432 }
Rabin Vincentd3425712015-06-06 22:30:40 +0200433
434 bgc->gc.ngpio = port->ngpio;
435 bgc->gc.label = port->label;
436
437 bgc->gc.of_node = dev->of_node;
438 bgc->gc.of_gpio_n_cells = 3;
439 bgc->gc.of_xlate = etraxfs_gpio_of_xlate;
440
441 ret = gpiochip_add(&bgc->gc);
Rabin Vincent29b53572015-07-31 14:48:57 +0200442 if (ret) {
Rabin Vincentd3425712015-06-06 22:30:40 +0200443 dev_err(dev, "Unable to register port %s\n",
444 bgc->gc.label);
Rabin Vincent29b53572015-07-31 14:48:57 +0200445 continue;
446 }
447
448 if (i > 0 && !allportsirq)
449 continue;
450
451 ret = gpiochip_irqchip_add(&bgc->gc, &etraxfs_gpio_irq_chip, 0,
452 handle_level_irq, IRQ_TYPE_NONE);
453 if (ret) {
454 dev_err(dev, "Unable to add irqchip to port %s\n",
455 bgc->gc.label);
456 }
Rabin Vincentd3425712015-06-06 22:30:40 +0200457 }
458
459 return 0;
460}
461
462static struct platform_driver etraxfs_gpio_driver = {
463 .driver = {
464 .name = "etraxfs-gpio",
465 .of_match_table = of_match_ptr(etraxfs_gpio_of_table),
466 },
467 .probe = etraxfs_gpio_probe,
468};
469
470static int __init etraxfs_gpio_init(void)
471{
472 return platform_driver_register(&etraxfs_gpio_driver);
473}
474
475device_initcall(etraxfs_gpio_init);