Commit 606f7047603422746d112e41937649d44f311af4
Committed by
Albert ARIBAUD
1 parent
5e862b9539
Exists in
v2017.01-smarct4x
and in
37 other branches
lpc32xx: add GPIO support
This driver only supports Driver Model, not legacy model. Signed-off-by: Albert ARIBAUD (3ADEV) <albert.aribaud@3adev.fr>
Showing 5 changed files with 349 additions and 0 deletions Side-by-side Diff
arch/arm/cpu/arm926ejs/lpc32xx/devices.c
... | ... | @@ -9,6 +9,7 @@ |
9 | 9 | #include <asm/arch/clk.h> |
10 | 10 | #include <asm/arch/uart.h> |
11 | 11 | #include <asm/io.h> |
12 | +#include <dm.h> | |
12 | 13 | |
13 | 14 | static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE; |
14 | 15 | static struct uart_ctrl_regs *ctrl = (struct uart_ctrl_regs *)UART_CTRL_BASE; |
... | ... | @@ -61,4 +62,8 @@ |
61 | 62 | ctrl |= CLK_I2C2_ENABLE; |
62 | 63 | writel(ctrl, &clk->i2cclk_ctrl); |
63 | 64 | } |
65 | + | |
66 | +U_BOOT_DEVICE(lpc32xx_gpios) = { | |
67 | + .name = "gpio_lpc32xx" | |
68 | +}; |
arch/arm/include/asm/arch-lpc32xx/gpio.h
1 | +/* | |
2 | + * LPC32xx GPIO interface | |
3 | + * | |
4 | + * (C) Copyright 2014 DENX Software Engineering GmbH | |
5 | + * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr> | |
6 | + * | |
7 | + * SPDX-License-Identifier: GPL-2.0+ | |
8 | + */ | |
9 | + | |
10 | +/** | |
11 | + * GPIO Register map for LPC32xx | |
12 | + */ | |
13 | + | |
14 | +struct gpio_regs { | |
15 | + u32 p3_inp_state; | |
16 | + u32 p3_outp_set; | |
17 | + u32 p3_outp_clr; | |
18 | + u32 p3_outp_state; | |
19 | + /* Watch out! the following are shared between p2 and p3 */ | |
20 | + u32 p2_p3_dir_set; | |
21 | + u32 p2_p3_dir_clr; | |
22 | + u32 p2_p3_dir_state; | |
23 | + /* Now back to 'one register for one port' */ | |
24 | + u32 p2_inp_state; | |
25 | + u32 p2_outp_set; | |
26 | + u32 p2_outp_clr; | |
27 | + u32 reserved1[6]; | |
28 | + u32 p0_inp_state; | |
29 | + u32 p0_outp_set; | |
30 | + u32 p0_outp_clr; | |
31 | + u32 p0_outp_state; | |
32 | + u32 p0_dir_set; | |
33 | + u32 p0_dir_clr; | |
34 | + u32 p0_dir_state; | |
35 | + u32 reserved2; | |
36 | + u32 p1_inp_state; | |
37 | + u32 p1_outp_set; | |
38 | + u32 p1_outp_clr; | |
39 | + u32 p1_outp_state; | |
40 | + u32 p1_dir_set; | |
41 | + u32 p1_dir_clr; | |
42 | + u32 p1_dir_state; | |
43 | +}; |
drivers/gpio/Kconfig
... | ... | @@ -7,4 +7,11 @@ |
7 | 7 | the GPIO uclass. Drivers provide methods to query the |
8 | 8 | particular GPIOs that they provide. The uclass interface |
9 | 9 | is defined in include/asm-generic/gpio.h. |
10 | + | |
11 | +config LPC32XX_GPIO | |
12 | + bool "LPC32XX GPIO driver" | |
13 | + depends on DM | |
14 | + default n | |
15 | + help | |
16 | + Support for the LPC32XX GPIO driver. |
drivers/gpio/Makefile
drivers/gpio/lpc32xx_gpio.c
1 | +/* | |
2 | + * LPC32xxGPIO driver | |
3 | + * | |
4 | + * (C) Copyright 2014 DENX Software Engineering GmbH | |
5 | + * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr> | |
6 | + * | |
7 | + * SPDX-License-Identifier: GPL-2.0+ | |
8 | + */ | |
9 | + | |
10 | +#include <asm/io.h> | |
11 | +#include <asm/arch-lpc32xx/cpu.h> | |
12 | +#include <asm/arch-lpc32xx/gpio.h> | |
13 | +#include <asm-generic/gpio.h> | |
14 | +#include <dm.h> | |
15 | + | |
16 | +/** | |
17 | + * LPC32xx GPIOs work in banks but are non-homogeneous: | |
18 | + * - each bank holds a different number of GPIOs | |
19 | + * - some GPIOs are input/ouput, some input only, some output only; | |
20 | + * - some GPIOs have different meanings as an input and as an output; | |
21 | + * - some GPIOs are controlled on a given port and bit index, but | |
22 | + * read on another one. | |
23 | +* | |
24 | + * In order to keep this code simple, GPIOS are considered here as | |
25 | + * homogeneous and linear, from 0 to 127. | |
26 | + * | |
27 | + * ** WARNING #1 ** | |
28 | + * | |
29 | + * Client code is responsible for properly using valid GPIO numbers, | |
30 | + * including cases where a single physical GPIO has differing numbers | |
31 | + * for setting its direction, reading it and/or writing to it. | |
32 | + * | |
33 | + * ** WARNING #2 ** | |
34 | + * | |
35 | + * Please read NOTE in description of lpc32xx_gpio_get_function(). | |
36 | + */ | |
37 | + | |
38 | +#define LPC32XX_GPIOS 128 | |
39 | + | |
40 | +struct lpc32xx_gpio_platdata { | |
41 | + struct gpio_regs *regs; | |
42 | + /* GPIO FUNCTION: SEE WARNING #2 */ | |
43 | + signed char function[LPC32XX_GPIOS]; | |
44 | +}; | |
45 | + | |
46 | +/** | |
47 | + * We have 4 GPIO ports of 32 bits each | |
48 | + */ | |
49 | + | |
50 | +#define MAX_GPIO 128 | |
51 | + | |
52 | +#define GPIO_TO_PORT(gpio) ((gpio / 32) & 3) | |
53 | +#define GPIO_TO_RANK(gpio) (gpio % 32) | |
54 | +#define GPIO_TO_MASK(gpio) (1 << (gpio % 32)) | |
55 | + | |
56 | +/** | |
57 | + * Configure a GPIO number 'offset' as input | |
58 | + */ | |
59 | + | |
60 | +static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset) | |
61 | +{ | |
62 | + int port, mask; | |
63 | + struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); | |
64 | + struct gpio_regs *regs = gpio_platdata->regs; | |
65 | + | |
66 | + port = GPIO_TO_PORT(offset); | |
67 | + mask = GPIO_TO_MASK(offset); | |
68 | + | |
69 | + switch (port) { | |
70 | + case 0: | |
71 | + writel(mask, ®s->p0_dir_clr); | |
72 | + break; | |
73 | + case 1: | |
74 | + writel(mask, ®s->p1_dir_clr); | |
75 | + break; | |
76 | + case 2: | |
77 | + /* ports 2 and 3 share a common direction */ | |
78 | + case 3: | |
79 | + writel(mask, ®s->p2_p3_dir_clr); | |
80 | + break; | |
81 | + default: | |
82 | + return -1; | |
83 | + } | |
84 | + | |
85 | + /* GPIO FUNCTION: SEE WARNING #2 */ | |
86 | + gpio_platdata->function[offset] = GPIOF_INPUT; | |
87 | + | |
88 | + return 0; | |
89 | +} | |
90 | + | |
91 | +/** | |
92 | + * Get the value of a GPIO | |
93 | + */ | |
94 | + | |
95 | +static int lpc32xx_gpio_get_value(struct udevice *dev, unsigned offset) | |
96 | +{ | |
97 | + int port, rank, mask, value; | |
98 | + struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); | |
99 | + struct gpio_regs *regs = gpio_platdata->regs; | |
100 | + | |
101 | + port = GPIO_TO_PORT(offset); | |
102 | + | |
103 | + switch (port) { | |
104 | + case 0: | |
105 | + value = readl(®s->p0_inp_state); | |
106 | + break; | |
107 | + case 1: | |
108 | + value = readl(®s->p1_inp_state); | |
109 | + break; | |
110 | + case 2: | |
111 | + value = readl(®s->p2_inp_state); | |
112 | + break; | |
113 | + case 3: | |
114 | + value = readl(®s->p3_inp_state); | |
115 | + break; | |
116 | + default: | |
117 | + return -1; | |
118 | + } | |
119 | + | |
120 | + rank = GPIO_TO_RANK(offset); | |
121 | + mask = GPIO_TO_MASK(offset); | |
122 | + | |
123 | + return (value & mask) >> rank; | |
124 | +} | |
125 | + | |
126 | +/** | |
127 | + * Set a GPIO | |
128 | + */ | |
129 | + | |
130 | +static int gpio_set(struct udevice *dev, unsigned gpio) | |
131 | +{ | |
132 | + int port, mask; | |
133 | + struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); | |
134 | + struct gpio_regs *regs = gpio_platdata->regs; | |
135 | + | |
136 | + port = GPIO_TO_PORT(gpio); | |
137 | + mask = GPIO_TO_MASK(gpio); | |
138 | + | |
139 | + switch (port) { | |
140 | + case 0: | |
141 | + writel(mask, ®s->p0_outp_set); | |
142 | + break; | |
143 | + case 1: | |
144 | + writel(mask, ®s->p1_outp_set); | |
145 | + break; | |
146 | + case 2: | |
147 | + writel(mask, ®s->p2_outp_set); | |
148 | + break; | |
149 | + case 3: | |
150 | + writel(mask, ®s->p3_outp_set); | |
151 | + break; | |
152 | + default: | |
153 | + return -1; | |
154 | + } | |
155 | + return 0; | |
156 | +} | |
157 | + | |
158 | +/** | |
159 | + * Clear a GPIO | |
160 | + */ | |
161 | + | |
162 | +static int gpio_clr(struct udevice *dev, unsigned gpio) | |
163 | +{ | |
164 | + int port, mask; | |
165 | + struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); | |
166 | + struct gpio_regs *regs = gpio_platdata->regs; | |
167 | + | |
168 | + port = GPIO_TO_PORT(gpio); | |
169 | + mask = GPIO_TO_MASK(gpio); | |
170 | + | |
171 | + switch (port) { | |
172 | + case 0: | |
173 | + writel(mask, ®s->p0_outp_clr); | |
174 | + break; | |
175 | + case 1: | |
176 | + writel(mask, ®s->p1_outp_clr); | |
177 | + break; | |
178 | + case 2: | |
179 | + writel(mask, ®s->p2_outp_clr); | |
180 | + break; | |
181 | + case 3: | |
182 | + writel(mask, ®s->p3_outp_clr); | |
183 | + break; | |
184 | + default: | |
185 | + return -1; | |
186 | + } | |
187 | + return 0; | |
188 | +} | |
189 | + | |
190 | +/** | |
191 | + * Set the value of a GPIO | |
192 | + */ | |
193 | + | |
194 | +static int lpc32xx_gpio_set_value(struct udevice *dev, unsigned offset, | |
195 | + int value) | |
196 | +{ | |
197 | + if (value) | |
198 | + return gpio_set(dev, offset); | |
199 | + else | |
200 | + return gpio_clr(dev, offset); | |
201 | +} | |
202 | + | |
203 | +/** | |
204 | + * Configure a GPIO number 'offset' as output with given initial value. | |
205 | + */ | |
206 | + | |
207 | +static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset, | |
208 | + int value) | |
209 | +{ | |
210 | + int port, mask; | |
211 | + struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); | |
212 | + struct gpio_regs *regs = gpio_platdata->regs; | |
213 | + | |
214 | + port = GPIO_TO_PORT(offset); | |
215 | + mask = GPIO_TO_MASK(offset); | |
216 | + | |
217 | + switch (port) { | |
218 | + case 0: | |
219 | + writel(mask, ®s->p0_dir_set); | |
220 | + break; | |
221 | + case 1: | |
222 | + writel(mask, ®s->p1_dir_set); | |
223 | + break; | |
224 | + case 2: | |
225 | + /* ports 2 and 3 share a common direction */ | |
226 | + case 3: | |
227 | + writel(mask, ®s->p2_p3_dir_set); | |
228 | + break; | |
229 | + default: | |
230 | + return -1; | |
231 | + } | |
232 | + | |
233 | + /* GPIO FUNCTION: SEE WARNING #2 */ | |
234 | + gpio_platdata->function[offset] = GPIOF_OUTPUT; | |
235 | + | |
236 | + return lpc32xx_gpio_set_value(dev, offset, value); | |
237 | +} | |
238 | + | |
239 | +/** | |
240 | + * GPIO functions are supposed to be computed from their current | |
241 | + * configuration, but that's way too complicated in LPC32XX. A simpler | |
242 | + * approach is used, where the GPIO functions are cached in an array. | |
243 | + * When the GPIO is in use, its function is either "input" or "output" | |
244 | + * depending on its direction, otherwise its function is "unknown". | |
245 | + * | |
246 | + * ** NOTE ** | |
247 | + * | |
248 | + * THIS APPROACH WAS CHOSEN DU TO THE COMPLEX NATURE OF THE LPC32XX | |
249 | + * GPIOS; DO NOT TAKE THIS AS AN EXAMPLE FOR NEW CODE. | |
250 | + */ | |
251 | + | |
252 | +static int lpc32xx_gpio_get_function(struct udevice *dev, unsigned offset) | |
253 | +{ | |
254 | + struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); | |
255 | + return gpio_platdata->function[offset]; | |
256 | +} | |
257 | + | |
258 | +static const struct dm_gpio_ops gpio_lpc32xx_ops = { | |
259 | + .direction_input = lpc32xx_gpio_direction_input, | |
260 | + .direction_output = lpc32xx_gpio_direction_output, | |
261 | + .get_value = lpc32xx_gpio_get_value, | |
262 | + .set_value = lpc32xx_gpio_set_value, | |
263 | + .get_function = lpc32xx_gpio_get_function, | |
264 | +}; | |
265 | + | |
266 | +static int lpc32xx_gpio_probe(struct udevice *dev) | |
267 | +{ | |
268 | + struct lpc32xx_gpio_platdata *gpio_platdata = dev_get_platdata(dev); | |
269 | + struct gpio_dev_priv *uc_priv = dev->uclass_priv; | |
270 | + | |
271 | + if (dev->of_offset == -1) { | |
272 | + /* Tell the uclass how many GPIOs we have */ | |
273 | + uc_priv->gpio_count = LPC32XX_GPIOS; | |
274 | + } | |
275 | + | |
276 | + /* set base address for GPIO registers */ | |
277 | + gpio_platdata->regs = (struct gpio_regs *)GPIO_BASE; | |
278 | + | |
279 | + /* all GPIO functions are unknown until requested */ | |
280 | + /* GPIO FUNCTION: SEE WARNING #2 */ | |
281 | + memset(gpio_platdata->function, GPIOF_UNKNOWN, | |
282 | + sizeof(gpio_platdata->function)); | |
283 | + | |
284 | + return 0; | |
285 | +} | |
286 | + | |
287 | +U_BOOT_DRIVER(gpio_lpc32xx) = { | |
288 | + .name = "gpio_lpc32xx", | |
289 | + .id = UCLASS_GPIO, | |
290 | + .ops = &gpio_lpc32xx_ops, | |
291 | + .probe = lpc32xx_gpio_probe, | |
292 | + .priv_auto_alloc_size = sizeof(struct lpc32xx_gpio_platdata), | |
293 | +}; |