]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0+ | |
2 | /* | |
3 | * Atmel PIO4 device driver | |
4 | * | |
5 | * Copyright (C) 2015 Atmel Corporation | |
6 | * Wenyou.Yang <[email protected]> | |
7 | */ | |
8 | #include <common.h> | |
9 | #include <clk.h> | |
10 | #include <dm.h> | |
11 | #include <fdtdec.h> | |
12 | #include <malloc.h> | |
13 | #include <asm/arch/hardware.h> | |
14 | #include <asm/gpio.h> | |
15 | #include <mach/gpio.h> | |
16 | #include <mach/atmel_pio4.h> | |
17 | ||
18 | DECLARE_GLOBAL_DATA_PTR; | |
19 | ||
20 | static struct atmel_pio4_port *atmel_pio4_port_base(u32 port) | |
21 | { | |
22 | struct atmel_pio4_port *base = NULL; | |
23 | ||
24 | switch (port) { | |
25 | case AT91_PIO_PORTA: | |
26 | base = (struct atmel_pio4_port *)ATMEL_BASE_PIOA; | |
27 | break; | |
28 | case AT91_PIO_PORTB: | |
29 | base = (struct atmel_pio4_port *)ATMEL_BASE_PIOB; | |
30 | break; | |
31 | case AT91_PIO_PORTC: | |
32 | base = (struct atmel_pio4_port *)ATMEL_BASE_PIOC; | |
33 | break; | |
34 | case AT91_PIO_PORTD: | |
35 | base = (struct atmel_pio4_port *)ATMEL_BASE_PIOD; | |
36 | break; | |
37 | default: | |
38 | printf("Error: Atmel PIO4: Failed to get PIO base of port#%d!\n", | |
39 | port); | |
40 | break; | |
41 | } | |
42 | ||
43 | return base; | |
44 | } | |
45 | ||
46 | static int atmel_pio4_config_io_func(u32 port, u32 pin, | |
47 | u32 func, u32 config) | |
48 | { | |
49 | struct atmel_pio4_port *port_base; | |
50 | u32 reg, mask; | |
51 | ||
52 | if (pin >= ATMEL_PIO_NPINS_PER_BANK) | |
53 | return -EINVAL; | |
54 | ||
55 | port_base = atmel_pio4_port_base(port); | |
56 | if (!port_base) | |
57 | return -EINVAL; | |
58 | ||
59 | mask = 1 << pin; | |
60 | reg = func; | |
61 | reg |= config; | |
62 | ||
63 | writel(mask, &port_base->mskr); | |
64 | writel(reg, &port_base->cfgr); | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
69 | int atmel_pio4_set_gpio(u32 port, u32 pin, u32 config) | |
70 | { | |
71 | return atmel_pio4_config_io_func(port, pin, | |
72 | ATMEL_PIO_CFGR_FUNC_GPIO, | |
73 | config); | |
74 | } | |
75 | ||
76 | int atmel_pio4_set_a_periph(u32 port, u32 pin, u32 config) | |
77 | { | |
78 | return atmel_pio4_config_io_func(port, pin, | |
79 | ATMEL_PIO_CFGR_FUNC_PERIPH_A, | |
80 | config); | |
81 | } | |
82 | ||
83 | int atmel_pio4_set_b_periph(u32 port, u32 pin, u32 config) | |
84 | { | |
85 | return atmel_pio4_config_io_func(port, pin, | |
86 | ATMEL_PIO_CFGR_FUNC_PERIPH_B, | |
87 | config); | |
88 | } | |
89 | ||
90 | int atmel_pio4_set_c_periph(u32 port, u32 pin, u32 config) | |
91 | { | |
92 | return atmel_pio4_config_io_func(port, pin, | |
93 | ATMEL_PIO_CFGR_FUNC_PERIPH_C, | |
94 | config); | |
95 | } | |
96 | ||
97 | int atmel_pio4_set_d_periph(u32 port, u32 pin, u32 config) | |
98 | { | |
99 | return atmel_pio4_config_io_func(port, pin, | |
100 | ATMEL_PIO_CFGR_FUNC_PERIPH_D, | |
101 | config); | |
102 | } | |
103 | ||
104 | int atmel_pio4_set_e_periph(u32 port, u32 pin, u32 config) | |
105 | { | |
106 | return atmel_pio4_config_io_func(port, pin, | |
107 | ATMEL_PIO_CFGR_FUNC_PERIPH_E, | |
108 | config); | |
109 | } | |
110 | ||
111 | int atmel_pio4_set_f_periph(u32 port, u32 pin, u32 config) | |
112 | { | |
113 | return atmel_pio4_config_io_func(port, pin, | |
114 | ATMEL_PIO_CFGR_FUNC_PERIPH_F, | |
115 | config); | |
116 | } | |
117 | ||
118 | int atmel_pio4_set_g_periph(u32 port, u32 pin, u32 config) | |
119 | { | |
120 | return atmel_pio4_config_io_func(port, pin, | |
121 | ATMEL_PIO_CFGR_FUNC_PERIPH_G, | |
122 | config); | |
123 | } | |
124 | ||
125 | int atmel_pio4_set_pio_output(u32 port, u32 pin, u32 value) | |
126 | { | |
127 | struct atmel_pio4_port *port_base; | |
128 | u32 reg, mask; | |
129 | ||
130 | if (pin >= ATMEL_PIO_NPINS_PER_BANK) | |
131 | return -EINVAL; | |
132 | ||
133 | port_base = atmel_pio4_port_base(port); | |
134 | if (!port_base) | |
135 | return -EINVAL; | |
136 | ||
137 | mask = 0x01 << pin; | |
138 | reg = ATMEL_PIO_CFGR_FUNC_GPIO | ATMEL_PIO_DIR_MASK; | |
139 | ||
140 | writel(mask, &port_base->mskr); | |
141 | writel(reg, &port_base->cfgr); | |
142 | ||
143 | if (value) | |
144 | writel(mask, &port_base->sodr); | |
145 | else | |
146 | writel(mask, &port_base->codr); | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | int atmel_pio4_get_pio_input(u32 port, u32 pin) | |
152 | { | |
153 | struct atmel_pio4_port *port_base; | |
154 | u32 reg, mask; | |
155 | ||
156 | if (pin >= ATMEL_PIO_NPINS_PER_BANK) | |
157 | return -EINVAL; | |
158 | ||
159 | port_base = atmel_pio4_port_base(port); | |
160 | if (!port_base) | |
161 | return -EINVAL; | |
162 | ||
163 | mask = 0x01 << pin; | |
164 | reg = ATMEL_PIO_CFGR_FUNC_GPIO; | |
165 | ||
166 | writel(mask, &port_base->mskr); | |
167 | writel(reg, &port_base->cfgr); | |
168 | ||
169 | return (readl(&port_base->pdsr) & mask) ? 1 : 0; | |
170 | } | |
171 | ||
172 | #if CONFIG_IS_ENABLED(DM_GPIO) | |
173 | ||
174 | struct atmel_pioctrl_data { | |
175 | u32 nbanks; | |
176 | }; | |
177 | ||
178 | struct atmel_pio4_platdata { | |
179 | struct atmel_pio4_port *reg_base; | |
180 | }; | |
181 | ||
182 | static struct atmel_pio4_port *atmel_pio4_bank_base(struct udevice *dev, | |
183 | u32 bank) | |
184 | { | |
185 | struct atmel_pio4_platdata *plat = dev_get_platdata(dev); | |
186 | struct atmel_pio4_port *port_base = | |
187 | (struct atmel_pio4_port *)((u32)plat->reg_base + | |
188 | ATMEL_PIO_BANK_OFFSET * bank); | |
189 | ||
190 | return port_base; | |
191 | } | |
192 | ||
193 | static int atmel_pio4_direction_input(struct udevice *dev, unsigned offset) | |
194 | { | |
195 | u32 bank = ATMEL_PIO_BANK(offset); | |
196 | u32 line = ATMEL_PIO_LINE(offset); | |
197 | struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank); | |
198 | u32 mask = BIT(line); | |
199 | ||
200 | writel(mask, &port_base->mskr); | |
201 | ||
202 | clrbits_le32(&port_base->cfgr, | |
203 | ATMEL_PIO_CFGR_FUNC_MASK | ATMEL_PIO_DIR_MASK); | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | static int atmel_pio4_direction_output(struct udevice *dev, | |
209 | unsigned offset, int value) | |
210 | { | |
211 | u32 bank = ATMEL_PIO_BANK(offset); | |
212 | u32 line = ATMEL_PIO_LINE(offset); | |
213 | struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank); | |
214 | u32 mask = BIT(line); | |
215 | ||
216 | writel(mask, &port_base->mskr); | |
217 | ||
218 | clrsetbits_le32(&port_base->cfgr, | |
219 | ATMEL_PIO_CFGR_FUNC_MASK, ATMEL_PIO_DIR_MASK); | |
220 | ||
221 | if (value) | |
222 | writel(mask, &port_base->sodr); | |
223 | else | |
224 | writel(mask, &port_base->codr); | |
225 | ||
226 | return 0; | |
227 | } | |
228 | ||
229 | static int atmel_pio4_get_value(struct udevice *dev, unsigned offset) | |
230 | { | |
231 | u32 bank = ATMEL_PIO_BANK(offset); | |
232 | u32 line = ATMEL_PIO_LINE(offset); | |
233 | struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank); | |
234 | u32 mask = BIT(line); | |
235 | ||
236 | return (readl(&port_base->pdsr) & mask) ? 1 : 0; | |
237 | } | |
238 | ||
239 | static int atmel_pio4_set_value(struct udevice *dev, | |
240 | unsigned offset, int value) | |
241 | { | |
242 | u32 bank = ATMEL_PIO_BANK(offset); | |
243 | u32 line = ATMEL_PIO_LINE(offset); | |
244 | struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank); | |
245 | u32 mask = BIT(line); | |
246 | ||
247 | if (value) | |
248 | writel(mask, &port_base->sodr); | |
249 | else | |
250 | writel(mask, &port_base->codr); | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | static int atmel_pio4_get_function(struct udevice *dev, unsigned offset) | |
256 | { | |
257 | u32 bank = ATMEL_PIO_BANK(offset); | |
258 | u32 line = ATMEL_PIO_LINE(offset); | |
259 | struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank); | |
260 | u32 mask = BIT(line); | |
261 | ||
262 | writel(mask, &port_base->mskr); | |
263 | ||
264 | return (readl(&port_base->cfgr) & | |
265 | ATMEL_PIO_DIR_MASK) ? GPIOF_OUTPUT : GPIOF_INPUT; | |
266 | } | |
267 | ||
268 | static const struct dm_gpio_ops atmel_pio4_ops = { | |
269 | .direction_input = atmel_pio4_direction_input, | |
270 | .direction_output = atmel_pio4_direction_output, | |
271 | .get_value = atmel_pio4_get_value, | |
272 | .set_value = atmel_pio4_set_value, | |
273 | .get_function = atmel_pio4_get_function, | |
274 | }; | |
275 | ||
276 | static int atmel_pio4_bind(struct udevice *dev) | |
277 | { | |
278 | return dm_scan_fdt_dev(dev); | |
279 | } | |
280 | ||
281 | static int atmel_pio4_probe(struct udevice *dev) | |
282 | { | |
283 | struct atmel_pio4_platdata *plat = dev_get_platdata(dev); | |
284 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); | |
285 | struct atmel_pioctrl_data *pioctrl_data; | |
286 | struct clk clk; | |
287 | fdt_addr_t addr_base; | |
288 | u32 nbanks; | |
289 | int ret; | |
290 | ||
291 | ret = clk_get_by_index(dev, 0, &clk); | |
292 | if (ret) | |
293 | return ret; | |
294 | ||
295 | ret = clk_enable(&clk); | |
296 | if (ret) | |
297 | return ret; | |
298 | ||
299 | clk_free(&clk); | |
300 | ||
301 | addr_base = devfdt_get_addr(dev); | |
302 | if (addr_base == FDT_ADDR_T_NONE) | |
303 | return -EINVAL; | |
304 | ||
305 | plat->reg_base = (struct atmel_pio4_port *)addr_base; | |
306 | ||
307 | pioctrl_data = (struct atmel_pioctrl_data *)dev_get_driver_data(dev); | |
308 | nbanks = pioctrl_data->nbanks; | |
309 | ||
310 | uc_priv->bank_name = fdt_get_name(gd->fdt_blob, dev_of_offset(dev), | |
311 | NULL); | |
312 | uc_priv->gpio_count = nbanks * ATMEL_PIO_NPINS_PER_BANK; | |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
317 | /* | |
318 | * The number of banks can be different from a SoC to another one. | |
319 | * We can have up to 16 banks. | |
320 | */ | |
321 | static const struct atmel_pioctrl_data atmel_sama5d2_pioctrl_data = { | |
322 | .nbanks = 4, | |
323 | }; | |
324 | ||
325 | static const struct udevice_id atmel_pio4_ids[] = { | |
326 | { | |
327 | .compatible = "atmel,sama5d2-gpio", | |
328 | .data = (ulong)&atmel_sama5d2_pioctrl_data, | |
329 | }, | |
330 | {} | |
331 | }; | |
332 | ||
333 | U_BOOT_DRIVER(gpio_atmel_pio4) = { | |
334 | .name = "gpio_atmel_pio4", | |
335 | .id = UCLASS_GPIO, | |
336 | .ops = &atmel_pio4_ops, | |
337 | .probe = atmel_pio4_probe, | |
338 | .bind = atmel_pio4_bind, | |
339 | .of_match = atmel_pio4_ids, | |
340 | .platdata_auto_alloc_size = sizeof(struct atmel_pio4_platdata), | |
341 | }; | |
342 | ||
343 | #endif |