]>
Commit | Line | Data |
---|---|---|
9af4d80b FV |
1 | /* |
2 | * GPIO controller in LSI ZEVIO SoCs. | |
3 | * | |
4 | * Author: Fabian Vogt <fabian@ritter-vogt.de> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/spinlock.h> | |
12 | #include <linux/errno.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/bitops.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/of_device.h> | |
17 | #include <linux/of_gpio.h> | |
18 | #include <linux/slab.h> | |
19 | #include <linux/gpio.h> | |
20 | ||
21 | /* | |
22 | * Memory layout: | |
23 | * This chip has four gpio sections, each controls 8 GPIOs. | |
24 | * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10. | |
25 | * Disclaimer: Reverse engineered! | |
26 | * For more information refer to: | |
27 | * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29 | |
28 | * | |
29 | * 0x00-0x3F: Section 0 | |
30 | * +0x00: Masked interrupt status (read-only) | |
31 | * +0x04: R: Interrupt status W: Reset interrupt status | |
32 | * +0x08: R: Interrupt mask W: Mask interrupt | |
33 | * +0x0C: W: Unmask interrupt (write-only) | |
34 | * +0x10: Direction: I/O=1/0 | |
35 | * +0x14: Output | |
36 | * +0x18: Input (read-only) | |
37 | * +0x20: R: Level interrupt W: Set as level interrupt | |
38 | * 0x40-0x7F: Section 1 | |
39 | * 0x80-0xBF: Section 2 | |
40 | * 0xC0-0xFF: Section 3 | |
41 | */ | |
42 | ||
43 | #define ZEVIO_GPIO_SECTION_SIZE 0x40 | |
44 | ||
45 | /* Offsets to various registers */ | |
46 | #define ZEVIO_GPIO_INT_MASKED_STATUS 0x00 | |
47 | #define ZEVIO_GPIO_INT_STATUS 0x04 | |
48 | #define ZEVIO_GPIO_INT_UNMASK 0x08 | |
49 | #define ZEVIO_GPIO_INT_MASK 0x0C | |
50 | #define ZEVIO_GPIO_DIRECTION 0x10 | |
51 | #define ZEVIO_GPIO_OUTPUT 0x14 | |
52 | #define ZEVIO_GPIO_INPUT 0x18 | |
53 | #define ZEVIO_GPIO_INT_STICKY 0x20 | |
54 | ||
55 | #define to_zevio_gpio(chip) container_of(to_of_mm_gpio_chip(chip), \ | |
56 | struct zevio_gpio, chip) | |
57 | ||
58 | /* Bit number of GPIO in its section */ | |
59 | #define ZEVIO_GPIO_BIT(gpio) (gpio&7) | |
60 | ||
61 | struct zevio_gpio { | |
62 | spinlock_t lock; | |
63 | struct of_mm_gpio_chip chip; | |
64 | }; | |
65 | ||
66 | static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin, | |
67 | unsigned port_offset) | |
68 | { | |
69 | unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; | |
70 | return readl(IOMEM(c->chip.regs + section_offset + port_offset)); | |
71 | } | |
72 | ||
73 | static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin, | |
74 | unsigned port_offset, u32 val) | |
75 | { | |
76 | unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE; | |
77 | writel(val, IOMEM(c->chip.regs + section_offset + port_offset)); | |
78 | } | |
79 | ||
80 | /* Functions for struct gpio_chip */ | |
81 | static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin) | |
82 | { | |
83 | struct zevio_gpio *controller = to_zevio_gpio(chip); | |
ef7de262 | 84 | u32 val, dir; |
9af4d80b | 85 | |
ef7de262 AL |
86 | spin_lock(&controller->lock); |
87 | dir = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); | |
88 | if (dir & BIT(ZEVIO_GPIO_BIT(pin))) | |
89 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT); | |
90 | else | |
91 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); | |
92 | spin_unlock(&controller->lock); | |
9af4d80b FV |
93 | |
94 | return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1; | |
95 | } | |
96 | ||
97 | static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) | |
98 | { | |
99 | struct zevio_gpio *controller = to_zevio_gpio(chip); | |
100 | u32 val; | |
101 | ||
102 | spin_lock(&controller->lock); | |
103 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); | |
104 | if (value) | |
105 | val |= BIT(ZEVIO_GPIO_BIT(pin)); | |
106 | else | |
107 | val &= ~BIT(ZEVIO_GPIO_BIT(pin)); | |
108 | ||
109 | zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); | |
110 | spin_unlock(&controller->lock); | |
111 | } | |
112 | ||
113 | static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin) | |
114 | { | |
115 | struct zevio_gpio *controller = to_zevio_gpio(chip); | |
116 | u32 val; | |
117 | ||
118 | spin_lock(&controller->lock); | |
119 | ||
120 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); | |
121 | val |= BIT(ZEVIO_GPIO_BIT(pin)); | |
122 | zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); | |
123 | ||
124 | spin_unlock(&controller->lock); | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | static int zevio_gpio_direction_output(struct gpio_chip *chip, | |
130 | unsigned pin, int value) | |
131 | { | |
132 | struct zevio_gpio *controller = to_zevio_gpio(chip); | |
133 | u32 val; | |
134 | ||
135 | spin_lock(&controller->lock); | |
136 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT); | |
137 | if (value) | |
138 | val |= BIT(ZEVIO_GPIO_BIT(pin)); | |
139 | else | |
140 | val &= ~BIT(ZEVIO_GPIO_BIT(pin)); | |
141 | ||
142 | zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); | |
143 | val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION); | |
144 | val &= ~BIT(ZEVIO_GPIO_BIT(pin)); | |
145 | zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val); | |
146 | ||
147 | spin_unlock(&controller->lock); | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin) | |
153 | { | |
154 | /* | |
155 | * TODO: Implement IRQs. | |
156 | * Not implemented yet due to weird lockups | |
157 | */ | |
158 | ||
159 | return -ENXIO; | |
160 | } | |
161 | ||
162 | static struct gpio_chip zevio_gpio_chip = { | |
163 | .direction_input = zevio_gpio_direction_input, | |
164 | .direction_output = zevio_gpio_direction_output, | |
165 | .set = zevio_gpio_set, | |
166 | .get = zevio_gpio_get, | |
167 | .to_irq = zevio_gpio_to_irq, | |
168 | .base = 0, | |
169 | .owner = THIS_MODULE, | |
170 | .ngpio = 32, | |
171 | .of_gpio_n_cells = 2, | |
172 | }; | |
173 | ||
174 | /* Initialization */ | |
175 | static int zevio_gpio_probe(struct platform_device *pdev) | |
176 | { | |
177 | struct zevio_gpio *controller; | |
178 | int status, i; | |
179 | ||
180 | controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL); | |
50908d61 | 181 | if (!controller) |
9af4d80b | 182 | return -ENOMEM; |
9af4d80b | 183 | |
ff00be69 RR |
184 | platform_set_drvdata(pdev, controller); |
185 | ||
9af4d80b FV |
186 | /* Copy our reference */ |
187 | controller->chip.gc = zevio_gpio_chip; | |
58383c78 | 188 | controller->chip.gc.parent = &pdev->dev; |
9af4d80b FV |
189 | |
190 | status = of_mm_gpiochip_add(pdev->dev.of_node, &(controller->chip)); | |
191 | if (status) { | |
192 | dev_err(&pdev->dev, "failed to add gpiochip: %d\n", status); | |
193 | return status; | |
194 | } | |
195 | ||
196 | spin_lock_init(&controller->lock); | |
197 | ||
198 | /* Disable interrupts, they only cause errors */ | |
199 | for (i = 0; i < controller->chip.gc.ngpio; i += 8) | |
200 | zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF); | |
201 | ||
58383c78 | 202 | dev_dbg(controller->chip.gc.parent, "ZEVIO GPIO controller set up!\n"); |
9af4d80b FV |
203 | |
204 | return 0; | |
205 | } | |
206 | ||
ff00be69 RR |
207 | static int zevio_gpio_remove(struct platform_device *pdev) |
208 | { | |
209 | struct zevio_gpio *controller = platform_get_drvdata(pdev); | |
210 | ||
211 | of_mm_gpiochip_remove(&controller->chip); | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
a49f2e74 | 216 | static const struct of_device_id zevio_gpio_of_match[] = { |
9af4d80b FV |
217 | { .compatible = "lsi,zevio-gpio", }, |
218 | { }, | |
219 | }; | |
220 | ||
221 | MODULE_DEVICE_TABLE(of, zevio_gpio_of_match); | |
222 | ||
223 | static struct platform_driver zevio_gpio_driver = { | |
224 | .driver = { | |
225 | .name = "gpio-zevio", | |
9ea8d810 | 226 | .of_match_table = zevio_gpio_of_match, |
9af4d80b FV |
227 | }, |
228 | .probe = zevio_gpio_probe, | |
ff00be69 | 229 | .remove = zevio_gpio_remove, |
9af4d80b FV |
230 | }; |
231 | module_platform_driver(zevio_gpio_driver); | |
232 | ||
233 | MODULE_LICENSE("GPL"); | |
234 | MODULE_AUTHOR("Fabian Vogt <fabian@ritter-vogt.de>"); | |
235 | MODULE_DESCRIPTION("LSI ZEVIO SoC GPIO driver"); |