Commit | Line | Data |
---|---|---|
361b7911 JS |
1 | /* |
2 | * Copyright 2015 IBM Corp. | |
3 | * | |
4 | * Joel Stanley <joel@jms.id.au> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the License, or (at your option) any later version. | |
10 | */ | |
11 | ||
12 | #include <linux/module.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/spinlock.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/gpio/driver.h> | |
19 | #include <linux/pinctrl/consumer.h> | |
20 | ||
21 | struct aspeed_gpio { | |
22 | struct gpio_chip chip; | |
23 | spinlock_t lock; | |
24 | void __iomem *base; | |
25 | int irq; | |
26 | }; | |
27 | ||
28 | struct aspeed_gpio_bank { | |
29 | uint16_t val_regs; | |
30 | uint16_t irq_regs; | |
31 | const char names[4]; | |
32 | }; | |
33 | ||
34 | static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { | |
35 | { | |
36 | .val_regs = 0x0000, | |
37 | .irq_regs = 0x0008, | |
38 | .names = { 'A', 'B', 'C', 'D' }, | |
39 | }, | |
40 | { | |
41 | .val_regs = 0x0020, | |
42 | .irq_regs = 0x0028, | |
43 | .names = { 'E', 'F', 'G', 'H' }, | |
44 | }, | |
45 | { | |
46 | .val_regs = 0x0070, | |
47 | .irq_regs = 0x0098, | |
48 | .names = { 'I', 'J', 'K', 'L' }, | |
49 | }, | |
50 | { | |
51 | .val_regs = 0x0078, | |
52 | .irq_regs = 0x00e8, | |
53 | .names = { 'M', 'N', 'O', 'P' }, | |
54 | }, | |
55 | { | |
56 | .val_regs = 0x0080, | |
57 | .irq_regs = 0x0118, | |
58 | .names = { 'Q', 'R', 'S', 'T' }, | |
59 | }, | |
60 | { | |
61 | .val_regs = 0x0088, | |
62 | .irq_regs = 0x0148, | |
63 | .names = { 'U', 'V', 'W', 'X' }, | |
64 | }, | |
65 | /* | |
66 | * A bank exists for { 'Y', 'Z', "AA", "AB" }, but is not implemented. | |
67 | * Only half of GPIOs Y support interrupt configuration, and none of Z, | |
68 | * AA or AB do as they are output only. | |
69 | */ | |
70 | }; | |
71 | ||
72 | #define GPIO_BANK(x) ((x) >> 5) | |
73 | #define GPIO_OFFSET(x) ((x) & 0x1f) | |
74 | #define GPIO_BIT(x) BIT(GPIO_OFFSET(x)) | |
75 | ||
76 | #define GPIO_DATA 0x00 | |
77 | #define GPIO_DIR 0x04 | |
78 | ||
79 | #define GPIO_IRQ_ENABLE 0x00 | |
80 | #define GPIO_IRQ_TYPE0 0x04 | |
81 | #define GPIO_IRQ_TYPE1 0x08 | |
82 | #define GPIO_IRQ_TYPE2 0x0c | |
83 | #define GPIO_IRQ_STATUS 0x10 | |
84 | ||
85 | static const struct aspeed_gpio_bank *to_bank(unsigned int offset) | |
86 | { | |
87 | unsigned int bank = GPIO_BANK(offset); | |
88 | ||
89 | WARN_ON(bank > ARRAY_SIZE(aspeed_gpio_banks)); | |
90 | return &aspeed_gpio_banks[bank]; | |
91 | } | |
92 | ||
93 | static void __iomem *bank_val_reg(struct aspeed_gpio *gpio, | |
94 | const struct aspeed_gpio_bank *bank, | |
95 | unsigned int reg) | |
96 | { | |
97 | return gpio->base + bank->val_regs + reg; | |
98 | } | |
99 | ||
100 | static void __iomem *bank_irq_reg(struct aspeed_gpio *gpio, | |
101 | const struct aspeed_gpio_bank *bank, | |
102 | unsigned int reg) | |
103 | { | |
104 | return gpio->base + bank->irq_regs + reg; | |
105 | } | |
106 | ||
107 | static int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset) | |
108 | { | |
109 | struct aspeed_gpio *gpio = gpiochip_get_data(gc); | |
110 | const struct aspeed_gpio_bank *bank = to_bank(offset); | |
111 | ||
112 | return !!(ioread32(bank_val_reg(gpio, bank, GPIO_DATA)) | |
113 | & GPIO_BIT(offset)); | |
114 | } | |
115 | ||
116 | static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, | |
117 | int val) | |
118 | { | |
119 | struct aspeed_gpio *gpio = gpiochip_get_data(gc); | |
120 | const struct aspeed_gpio_bank *bank = to_bank(offset); | |
121 | void __iomem *addr; | |
122 | u32 reg; | |
123 | ||
124 | addr = bank_val_reg(gpio, bank, GPIO_DATA); | |
125 | reg = ioread32(addr); | |
126 | ||
127 | if (val) | |
128 | reg |= GPIO_BIT(offset); | |
129 | else | |
130 | reg &= ~GPIO_BIT(offset); | |
131 | ||
132 | iowrite32(reg, addr); | |
133 | } | |
134 | ||
135 | static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, | |
136 | int val) | |
137 | { | |
138 | struct aspeed_gpio *gpio = gpiochip_get_data(gc); | |
139 | unsigned long flags; | |
140 | ||
141 | spin_lock_irqsave(&gpio->lock, flags); | |
142 | ||
143 | __aspeed_gpio_set(gc, offset, val); | |
144 | ||
145 | spin_unlock_irqrestore(&gpio->lock, flags); | |
146 | } | |
147 | ||
148 | static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) | |
149 | { | |
150 | struct aspeed_gpio *gpio = gpiochip_get_data(gc); | |
151 | const struct aspeed_gpio_bank *bank = to_bank(offset); | |
152 | unsigned long flags; | |
153 | u32 reg; | |
154 | ||
155 | spin_lock_irqsave(&gpio->lock, flags); | |
156 | ||
157 | reg = ioread32(bank_val_reg(gpio, bank, GPIO_DIR)); | |
158 | iowrite32(reg & ~GPIO_BIT(offset), bank_val_reg(gpio, bank, GPIO_DIR)); | |
159 | ||
160 | spin_unlock_irqrestore(&gpio->lock, flags); | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static int aspeed_gpio_dir_out(struct gpio_chip *gc, | |
166 | unsigned int offset, int val) | |
167 | { | |
168 | struct aspeed_gpio *gpio = gpiochip_get_data(gc); | |
169 | const struct aspeed_gpio_bank *bank = to_bank(offset); | |
170 | unsigned long flags; | |
171 | u32 reg; | |
172 | ||
173 | spin_lock_irqsave(&gpio->lock, flags); | |
174 | ||
175 | reg = ioread32(bank_val_reg(gpio, bank, GPIO_DIR)); | |
176 | iowrite32(reg | GPIO_BIT(offset), bank_val_reg(gpio, bank, GPIO_DIR)); | |
177 | ||
178 | __aspeed_gpio_set(gc, offset, val); | |
179 | ||
180 | spin_unlock_irqrestore(&gpio->lock, flags); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) | |
186 | { | |
187 | struct aspeed_gpio *gpio = gpiochip_get_data(gc); | |
188 | const struct aspeed_gpio_bank *bank = to_bank(offset); | |
189 | unsigned long flags; | |
190 | u32 val; | |
191 | ||
192 | spin_lock_irqsave(&gpio->lock, flags); | |
193 | ||
194 | val = ioread32(bank_val_reg(gpio, bank, GPIO_DIR)) & GPIO_BIT(offset); | |
195 | ||
196 | spin_unlock_irqrestore(&gpio->lock, flags); | |
197 | ||
198 | return !val; | |
199 | ||
200 | } | |
201 | ||
202 | static inline int irqd_to_aspeed_gpio_data(struct irq_data *d, | |
203 | struct aspeed_gpio **gpio, | |
204 | const struct aspeed_gpio_bank **bank, | |
205 | u32 *bit) | |
206 | { | |
207 | int offset; | |
208 | ||
209 | offset = irqd_to_hwirq(d); | |
210 | ||
211 | *gpio = irq_data_get_irq_chip_data(d); | |
212 | *bank = to_bank(offset); | |
213 | *bit = GPIO_BIT(offset); | |
214 | ||
215 | return 0; | |
216 | } | |
217 | ||
218 | static void aspeed_gpio_irq_ack(struct irq_data *d) | |
219 | { | |
220 | const struct aspeed_gpio_bank *bank; | |
221 | struct aspeed_gpio *gpio; | |
222 | unsigned long flags; | |
223 | void __iomem *status_addr; | |
224 | u32 bit; | |
225 | int rc; | |
226 | ||
227 | rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit); | |
228 | if (rc) | |
229 | return; | |
230 | ||
231 | status_addr = bank_irq_reg(gpio, bank, GPIO_IRQ_STATUS); | |
232 | ||
233 | spin_lock_irqsave(&gpio->lock, flags); | |
234 | iowrite32(bit, status_addr); | |
235 | spin_unlock_irqrestore(&gpio->lock, flags); | |
236 | } | |
237 | ||
238 | static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) | |
239 | { | |
240 | const struct aspeed_gpio_bank *bank; | |
241 | struct aspeed_gpio *gpio; | |
242 | unsigned long flags; | |
243 | u32 reg, bit; | |
244 | void __iomem *addr; | |
245 | int rc; | |
246 | ||
247 | rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit); | |
248 | if (rc) | |
249 | return; | |
250 | ||
251 | addr = bank_irq_reg(gpio, bank, GPIO_IRQ_ENABLE); | |
252 | ||
253 | spin_lock_irqsave(&gpio->lock, flags); | |
254 | ||
255 | reg = ioread32(addr); | |
256 | if (set) | |
257 | reg |= bit; | |
258 | else | |
259 | reg &= bit; | |
260 | iowrite32(reg, addr); | |
261 | ||
262 | spin_unlock_irqrestore(&gpio->lock, flags); | |
263 | } | |
264 | ||
265 | static void aspeed_gpio_irq_mask(struct irq_data *d) | |
266 | { | |
267 | aspeed_gpio_irq_set_mask(d, false); | |
268 | } | |
269 | ||
270 | static void aspeed_gpio_irq_unmask(struct irq_data *d) | |
271 | { | |
272 | aspeed_gpio_irq_set_mask(d, true); | |
273 | } | |
274 | ||
275 | static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) | |
276 | { | |
277 | u32 type0 = 0; | |
278 | u32 type1 = 0; | |
279 | u32 type2 = 0; | |
280 | u32 bit, reg; | |
281 | const struct aspeed_gpio_bank *bank; | |
282 | irq_flow_handler_t handler; | |
283 | struct aspeed_gpio *gpio; | |
284 | unsigned long flags; | |
285 | void __iomem *addr; | |
286 | int rc; | |
287 | ||
288 | rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit); | |
289 | if (rc) | |
290 | return -EINVAL; | |
291 | ||
292 | switch (type & IRQ_TYPE_SENSE_MASK) { | |
293 | case IRQ_TYPE_EDGE_BOTH: | |
294 | type2 |= bit; | |
295 | case IRQ_TYPE_EDGE_RISING: | |
296 | type0 |= bit; | |
297 | case IRQ_TYPE_EDGE_FALLING: | |
298 | handler = handle_edge_irq; | |
299 | break; | |
300 | case IRQ_TYPE_LEVEL_HIGH: | |
301 | type0 |= bit; | |
302 | case IRQ_TYPE_LEVEL_LOW: | |
303 | type1 |= bit; | |
304 | handler = handle_level_irq; | |
305 | break; | |
306 | default: | |
307 | return -EINVAL; | |
308 | } | |
309 | ||
310 | spin_lock_irqsave(&gpio->lock, flags); | |
311 | ||
312 | addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE0); | |
313 | reg = ioread32(addr); | |
314 | reg = (reg & ~bit) | type0; | |
315 | iowrite32(reg, addr); | |
316 | ||
317 | addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE1); | |
318 | reg = ioread32(addr); | |
319 | reg = (reg & ~bit) | type1; | |
320 | iowrite32(reg, addr); | |
321 | ||
322 | addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE2); | |
323 | reg = ioread32(addr); | |
324 | reg = (reg & ~bit) | type2; | |
325 | iowrite32(reg, addr); | |
326 | ||
327 | spin_unlock_irqrestore(&gpio->lock, flags); | |
328 | ||
329 | irq_set_handler_locked(d, handler); | |
330 | ||
331 | return 0; | |
332 | } | |
333 | ||
334 | static void aspeed_gpio_irq_handler(struct irq_desc *desc) | |
335 | { | |
336 | struct gpio_chip *gc = irq_desc_get_handler_data(desc); | |
337 | struct irq_chip *ic = irq_desc_get_chip(desc); | |
338 | struct aspeed_gpio *data = gpiochip_get_data(gc); | |
339 | unsigned int i, p, girq; | |
340 | unsigned long reg; | |
341 | ||
342 | chained_irq_enter(ic, desc); | |
343 | ||
344 | for (i = 0; i < ARRAY_SIZE(aspeed_gpio_banks); i++) { | |
345 | const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i]; | |
346 | ||
347 | reg = ioread32(bank_irq_reg(data, bank, GPIO_IRQ_STATUS)); | |
348 | ||
349 | for_each_set_bit(p, ®, 32) { | |
350 | girq = irq_find_mapping(gc->irqdomain, i * 32 + p); | |
351 | generic_handle_irq(girq); | |
352 | } | |
353 | ||
354 | } | |
355 | ||
356 | chained_irq_exit(ic, desc); | |
357 | } | |
358 | ||
359 | static struct irq_chip aspeed_gpio_irqchip = { | |
360 | .name = "aspeed-gpio", | |
361 | .irq_ack = aspeed_gpio_irq_ack, | |
362 | .irq_mask = aspeed_gpio_irq_mask, | |
363 | .irq_unmask = aspeed_gpio_irq_unmask, | |
364 | .irq_set_type = aspeed_gpio_set_type, | |
365 | }; | |
366 | ||
367 | static int aspeed_gpio_setup_irqs(struct aspeed_gpio *gpio, | |
368 | struct platform_device *pdev) | |
369 | { | |
370 | int rc; | |
371 | ||
372 | rc = platform_get_irq(pdev, 0); | |
373 | if (rc < 0) | |
374 | return rc; | |
375 | ||
376 | gpio->irq = rc; | |
377 | ||
378 | rc = gpiochip_irqchip_add(&gpio->chip, &aspeed_gpio_irqchip, | |
379 | 0, handle_bad_irq, IRQ_TYPE_NONE); | |
380 | if (rc) { | |
381 | dev_info(&pdev->dev, "Could not add irqchip\n"); | |
382 | return rc; | |
383 | } | |
384 | ||
385 | gpiochip_set_chained_irqchip(&gpio->chip, &aspeed_gpio_irqchip, | |
386 | gpio->irq, aspeed_gpio_irq_handler); | |
387 | ||
388 | return 0; | |
389 | } | |
390 | ||
391 | static int aspeed_gpio_request(struct gpio_chip *chip, unsigned int offset) | |
392 | { | |
393 | return pinctrl_request_gpio(chip->base + offset); | |
394 | } | |
395 | ||
396 | static void aspeed_gpio_free(struct gpio_chip *chip, unsigned int offset) | |
397 | { | |
398 | pinctrl_free_gpio(chip->base + offset); | |
399 | } | |
400 | ||
401 | static int __init aspeed_gpio_probe(struct platform_device *pdev) | |
402 | { | |
403 | struct aspeed_gpio *gpio; | |
404 | struct resource *res; | |
405 | int rc; | |
406 | ||
407 | gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); | |
408 | if (!gpio) | |
409 | return -ENOMEM; | |
410 | ||
411 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
361b7911 | 412 | gpio->base = devm_ioremap_resource(&pdev->dev, res); |
7f8b9657 WY |
413 | if (IS_ERR(gpio->base)) |
414 | return PTR_ERR(gpio->base); | |
361b7911 JS |
415 | |
416 | spin_lock_init(&gpio->lock); | |
417 | ||
418 | gpio->chip.ngpio = ARRAY_SIZE(aspeed_gpio_banks) * 32; | |
419 | ||
420 | gpio->chip.parent = &pdev->dev; | |
421 | gpio->chip.direction_input = aspeed_gpio_dir_in; | |
422 | gpio->chip.direction_output = aspeed_gpio_dir_out; | |
423 | gpio->chip.get_direction = aspeed_gpio_get_direction; | |
424 | gpio->chip.request = aspeed_gpio_request; | |
425 | gpio->chip.free = aspeed_gpio_free; | |
426 | gpio->chip.get = aspeed_gpio_get; | |
427 | gpio->chip.set = aspeed_gpio_set; | |
428 | gpio->chip.label = dev_name(&pdev->dev); | |
429 | gpio->chip.base = -1; | |
430 | ||
431 | rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); | |
432 | if (rc < 0) | |
433 | return rc; | |
434 | ||
435 | return aspeed_gpio_setup_irqs(gpio, pdev); | |
436 | } | |
437 | ||
438 | static const struct of_device_id aspeed_gpio_of_table[] = { | |
439 | { .compatible = "aspeed,ast2400-gpio" }, | |
440 | { .compatible = "aspeed,ast2500-gpio" }, | |
441 | {} | |
442 | }; | |
443 | MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table); | |
444 | ||
445 | static struct platform_driver aspeed_gpio_driver = { | |
446 | .driver = { | |
447 | .name = KBUILD_MODNAME, | |
448 | .of_match_table = aspeed_gpio_of_table, | |
449 | }, | |
450 | }; | |
451 | ||
452 | module_platform_driver_probe(aspeed_gpio_driver, aspeed_gpio_probe); | |
453 | ||
454 | MODULE_DESCRIPTION("Aspeed GPIO Driver"); | |
e50237c7 | 455 | MODULE_LICENSE("GPL"); |