Commit | Line | Data |
---|---|---|
89d043c3 BD |
1 | /* arch/arm/plat-s3c64xx/gpiolib.c |
2 | * | |
3 | * Copyright 2008 Openmoko, Inc. | |
4 | * Copyright 2008 Simtec Electronics | |
5 | * Ben Dooks <ben@simtec.co.uk> | |
6 | * http://armlinux.simtec.co.uk/ | |
7 | * | |
8 | * S3C64XX - GPIOlib support | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | ||
15 | #include <linux/kernel.h> | |
16 | #include <linux/irq.h> | |
17 | #include <linux/io.h> | |
18 | ||
19 | #include <mach/map.h> | |
20 | #include <mach/gpio.h> | |
21b23664 | 21 | #include <mach/gpio-core.h> |
89d043c3 | 22 | |
21b23664 BD |
23 | #include <plat/gpio-cfg.h> |
24 | #include <plat/gpio-cfg-helpers.h> | |
89d043c3 BD |
25 | #include <plat/regs-gpio.h> |
26 | ||
27 | /* GPIO bank summary: | |
28 | * | |
29 | * Bank GPIOs Style SlpCon ExtInt Group | |
30 | * A 8 4Bit Yes 1 | |
31 | * B 7 4Bit Yes 1 | |
32 | * C 8 4Bit Yes 2 | |
33 | * D 5 4Bit Yes 3 | |
34 | * E 5 4Bit Yes None | |
35 | * F 16 2Bit Yes 4 [1] | |
36 | * G 7 4Bit Yes 5 | |
37 | * H 10 4Bit[2] Yes 6 | |
38 | * I 16 2Bit Yes None | |
39 | * J 12 2Bit Yes None | |
40 | * K 16 4Bit[2] No None | |
41 | * L 15 4Bit[2] No None | |
42 | * M 6 4Bit No IRQ_EINT | |
43 | * N 16 2Bit No IRQ_EINT | |
44 | * O 16 2Bit Yes 7 | |
45 | * P 15 2Bit Yes 8 | |
46 | * Q 9 2Bit Yes 9 | |
47 | * | |
48 | * [1] BANKF pins 14,15 do not form part of the external interrupt sources | |
49 | * [2] BANK has two control registers, GPxCON0 and GPxCON1 | |
50 | */ | |
51 | ||
52 | #define OFF_GPCON (0x00) | |
53 | #define OFF_GPDAT (0x04) | |
54 | ||
55 | #define con_4bit_shift(__off) ((__off) * 4) | |
56 | ||
21b23664 BD |
57 | #if 1 |
58 | #define gpio_dbg(x...) do { } while(0) | |
59 | #else | |
beb9f4ed | 60 | #define gpio_dbg(x...) printk(KERN_DEBUG x) |
21b23664 BD |
61 | #endif |
62 | ||
89d043c3 BD |
63 | /* The s3c64xx_gpiolib_4bit routines are to control the gpio banks where |
64 | * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the | |
65 | * following example: | |
66 | * | |
67 | * base + 0x00: Control register, 4 bits per gpio | |
68 | * gpio n: 4 bits starting at (4*n) | |
69 | * 0000 = input, 0001 = output, others mean special-function | |
70 | * base + 0x04: Data register, 1 bit per gpio | |
71 | * bit n: data bit n | |
72 | * | |
73 | * Note, since the data register is one bit per gpio and is at base + 0x4 | |
74 | * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of | |
75 | * the output. | |
76 | */ | |
77 | ||
78 | static int s3c64xx_gpiolib_4bit_input(struct gpio_chip *chip, unsigned offset) | |
79 | { | |
80 | struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); | |
81 | void __iomem *base = ourchip->base; | |
82 | unsigned long con; | |
83 | ||
84 | con = __raw_readl(base + OFF_GPCON); | |
85 | con &= ~(0xf << con_4bit_shift(offset)); | |
86 | __raw_writel(con, base + OFF_GPCON); | |
87 | ||
21b23664 BD |
88 | gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); |
89 | ||
89d043c3 BD |
90 | return 0; |
91 | } | |
92 | ||
93 | static int s3c64xx_gpiolib_4bit_output(struct gpio_chip *chip, | |
94 | unsigned offset, int value) | |
95 | { | |
96 | struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); | |
97 | void __iomem *base = ourchip->base; | |
98 | unsigned long con; | |
99 | unsigned long dat; | |
100 | ||
101 | con = __raw_readl(base + OFF_GPCON); | |
102 | con &= ~(0xf << con_4bit_shift(offset)); | |
103 | con |= 0x1 << con_4bit_shift(offset); | |
104 | ||
105 | dat = __raw_readl(base + OFF_GPDAT); | |
106 | if (value) | |
107 | dat |= 1 << offset; | |
108 | else | |
109 | dat &= ~(1 << offset); | |
110 | ||
111 | __raw_writel(dat, base + OFF_GPDAT); | |
112 | __raw_writel(con, base + OFF_GPCON); | |
113 | __raw_writel(dat, base + OFF_GPDAT); | |
114 | ||
21b23664 BD |
115 | gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); |
116 | ||
89d043c3 BD |
117 | return 0; |
118 | } | |
119 | ||
120 | /* The next set of routines are for the case where the GPIO configuration | |
121 | * registers are 4 bits per GPIO but there is more than one register (the | |
122 | * bank has more than 8 GPIOs. | |
123 | * | |
124 | * This case is the similar to the 4 bit case, but the registers are as | |
125 | * follows: | |
126 | * | |
127 | * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) | |
128 | * gpio n: 4 bits starting at (4*n) | |
129 | * 0000 = input, 0001 = output, others mean special-function | |
130 | * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) | |
131 | * gpio n: 4 bits starting at (4*n) | |
132 | * 0000 = input, 0001 = output, others mean special-function | |
133 | * base + 0x08: Data register, 1 bit per gpio | |
134 | * bit n: data bit n | |
135 | * | |
136 | * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we | |
137 | * store the 'base + 0x4' address so that these routines see the data | |
138 | * register at ourchip->base + 0x04. | |
139 | */ | |
140 | ||
141 | static int s3c64xx_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned offset) | |
142 | { | |
143 | struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); | |
144 | void __iomem *base = ourchip->base; | |
145 | void __iomem *regcon = base; | |
146 | unsigned long con; | |
147 | ||
148 | if (offset > 7) | |
149 | offset -= 8; | |
150 | else | |
151 | regcon -= 4; | |
152 | ||
153 | con = __raw_readl(regcon); | |
154 | con &= ~(0xf << con_4bit_shift(offset)); | |
155 | __raw_writel(con, regcon); | |
156 | ||
21b23664 BD |
157 | gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); |
158 | ||
89d043c3 BD |
159 | return 0; |
160 | ||
161 | } | |
162 | ||
163 | static int s3c64xx_gpiolib_4bit2_output(struct gpio_chip *chip, | |
164 | unsigned offset, int value) | |
165 | { | |
166 | struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); | |
167 | void __iomem *base = ourchip->base; | |
168 | void __iomem *regcon = base; | |
169 | unsigned long con; | |
170 | unsigned long dat; | |
171 | ||
172 | if (offset > 7) | |
173 | offset -= 8; | |
174 | else | |
175 | regcon -= 4; | |
176 | ||
177 | con = __raw_readl(regcon); | |
178 | con &= ~(0xf << con_4bit_shift(offset)); | |
179 | con |= 0x1 << con_4bit_shift(offset); | |
180 | ||
181 | dat = __raw_readl(base + OFF_GPDAT); | |
182 | if (value) | |
183 | dat |= 1 << offset; | |
184 | else | |
185 | dat &= ~(1 << offset); | |
186 | ||
187 | __raw_writel(dat, base + OFF_GPDAT); | |
188 | __raw_writel(con, regcon); | |
189 | __raw_writel(dat, base + OFF_GPDAT); | |
190 | ||
21b23664 BD |
191 | gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); |
192 | ||
89d043c3 BD |
193 | return 0; |
194 | } | |
195 | ||
21b23664 BD |
196 | static struct s3c_gpio_cfg gpio_4bit_cfg_noint = { |
197 | .set_config = s3c_gpio_setcfg_s3c64xx_4bit, | |
198 | .set_pull = s3c_gpio_setpull_updown, | |
199 | .get_pull = s3c_gpio_getpull_updown, | |
200 | }; | |
201 | ||
202 | static struct s3c_gpio_cfg gpio_4bit_cfg_eint0111 = { | |
203 | .cfg_eint = 7, | |
204 | .set_config = s3c_gpio_setcfg_s3c64xx_4bit, | |
205 | .set_pull = s3c_gpio_setpull_updown, | |
206 | .get_pull = s3c_gpio_getpull_updown, | |
207 | }; | |
208 | ||
209 | static struct s3c_gpio_cfg gpio_4bit_cfg_eint0011 = { | |
210 | .cfg_eint = 3, | |
211 | .set_config = s3c_gpio_setcfg_s3c64xx_4bit, | |
212 | .set_pull = s3c_gpio_setpull_updown, | |
213 | .get_pull = s3c_gpio_getpull_updown, | |
214 | }; | |
215 | ||
6a88e983 MC |
216 | int s3c64xx_gpio2int_gpm(struct gpio_chip *chip, unsigned pin) |
217 | { | |
218 | return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO; | |
219 | } | |
220 | ||
89d043c3 BD |
221 | static struct s3c_gpio_chip gpio_4bit[] = { |
222 | { | |
223 | .base = S3C64XX_GPA_BASE, | |
21b23664 | 224 | .config = &gpio_4bit_cfg_eint0111, |
89d043c3 BD |
225 | .chip = { |
226 | .base = S3C64XX_GPA(0), | |
227 | .ngpio = S3C64XX_GPIO_A_NR, | |
228 | .label = "GPA", | |
229 | }, | |
230 | }, { | |
231 | .base = S3C64XX_GPB_BASE, | |
21b23664 | 232 | .config = &gpio_4bit_cfg_eint0111, |
89d043c3 BD |
233 | .chip = { |
234 | .base = S3C64XX_GPB(0), | |
235 | .ngpio = S3C64XX_GPIO_B_NR, | |
236 | .label = "GPB", | |
237 | }, | |
238 | }, { | |
239 | .base = S3C64XX_GPC_BASE, | |
21b23664 | 240 | .config = &gpio_4bit_cfg_eint0111, |
89d043c3 BD |
241 | .chip = { |
242 | .base = S3C64XX_GPC(0), | |
243 | .ngpio = S3C64XX_GPIO_C_NR, | |
244 | .label = "GPC", | |
245 | }, | |
246 | }, { | |
247 | .base = S3C64XX_GPD_BASE, | |
21b23664 | 248 | .config = &gpio_4bit_cfg_eint0111, |
89d043c3 BD |
249 | .chip = { |
250 | .base = S3C64XX_GPD(0), | |
251 | .ngpio = S3C64XX_GPIO_D_NR, | |
252 | .label = "GPD", | |
253 | }, | |
254 | }, { | |
255 | .base = S3C64XX_GPE_BASE, | |
21b23664 | 256 | .config = &gpio_4bit_cfg_noint, |
89d043c3 BD |
257 | .chip = { |
258 | .base = S3C64XX_GPE(0), | |
259 | .ngpio = S3C64XX_GPIO_E_NR, | |
260 | .label = "GPE", | |
261 | }, | |
262 | }, { | |
263 | .base = S3C64XX_GPG_BASE, | |
21b23664 | 264 | .config = &gpio_4bit_cfg_eint0111, |
89d043c3 BD |
265 | .chip = { |
266 | .base = S3C64XX_GPG(0), | |
267 | .ngpio = S3C64XX_GPIO_G_NR, | |
268 | .label = "GPG", | |
269 | }, | |
270 | }, { | |
271 | .base = S3C64XX_GPM_BASE, | |
21b23664 | 272 | .config = &gpio_4bit_cfg_eint0011, |
89d043c3 BD |
273 | .chip = { |
274 | .base = S3C64XX_GPM(0), | |
275 | .ngpio = S3C64XX_GPIO_M_NR, | |
276 | .label = "GPM", | |
6a88e983 | 277 | .to_irq = s3c64xx_gpio2int_gpm, |
89d043c3 BD |
278 | }, |
279 | }, | |
280 | }; | |
281 | ||
6a88e983 MC |
282 | int s3c64xx_gpio2int_gpl(struct gpio_chip *chip, unsigned pin) |
283 | { | |
284 | return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO; | |
285 | } | |
286 | ||
89d043c3 BD |
287 | static struct s3c_gpio_chip gpio_4bit2[] = { |
288 | { | |
289 | .base = S3C64XX_GPH_BASE + 0x4, | |
21b23664 | 290 | .config = &gpio_4bit_cfg_eint0111, |
89d043c3 BD |
291 | .chip = { |
292 | .base = S3C64XX_GPH(0), | |
293 | .ngpio = S3C64XX_GPIO_H_NR, | |
294 | .label = "GPH", | |
295 | }, | |
296 | }, { | |
297 | .base = S3C64XX_GPK_BASE + 0x4, | |
21b23664 | 298 | .config = &gpio_4bit_cfg_noint, |
89d043c3 BD |
299 | .chip = { |
300 | .base = S3C64XX_GPK(0), | |
301 | .ngpio = S3C64XX_GPIO_K_NR, | |
302 | .label = "GPK", | |
303 | }, | |
304 | }, { | |
305 | .base = S3C64XX_GPL_BASE + 0x4, | |
21b23664 | 306 | .config = &gpio_4bit_cfg_eint0011, |
89d043c3 BD |
307 | .chip = { |
308 | .base = S3C64XX_GPL(0), | |
309 | .ngpio = S3C64XX_GPIO_L_NR, | |
310 | .label = "GPL", | |
6a88e983 | 311 | .to_irq = s3c64xx_gpio2int_gpl, |
89d043c3 BD |
312 | }, |
313 | }, | |
314 | }; | |
315 | ||
21b23664 BD |
316 | static struct s3c_gpio_cfg gpio_2bit_cfg_noint = { |
317 | .set_config = s3c_gpio_setcfg_s3c24xx, | |
318 | .set_pull = s3c_gpio_setpull_updown, | |
319 | .get_pull = s3c_gpio_getpull_updown, | |
320 | }; | |
321 | ||
322 | static struct s3c_gpio_cfg gpio_2bit_cfg_eint10 = { | |
323 | .cfg_eint = 2, | |
324 | .set_config = s3c_gpio_setcfg_s3c24xx, | |
325 | .set_pull = s3c_gpio_setpull_updown, | |
326 | .get_pull = s3c_gpio_getpull_updown, | |
327 | }; | |
328 | ||
329 | static struct s3c_gpio_cfg gpio_2bit_cfg_eint11 = { | |
330 | .cfg_eint = 3, | |
331 | .set_config = s3c_gpio_setcfg_s3c24xx, | |
332 | .set_pull = s3c_gpio_setpull_updown, | |
333 | .get_pull = s3c_gpio_getpull_updown, | |
334 | }; | |
335 | ||
0b495737 MS |
336 | int s3c64xx_gpio2int_gpn(struct gpio_chip *chip, unsigned pin) |
337 | { | |
338 | return IRQ_EINT(0) + pin; | |
339 | } | |
340 | ||
89d043c3 BD |
341 | static struct s3c_gpio_chip gpio_2bit[] = { |
342 | { | |
343 | .base = S3C64XX_GPF_BASE, | |
21b23664 | 344 | .config = &gpio_2bit_cfg_eint11, |
89d043c3 BD |
345 | .chip = { |
346 | .base = S3C64XX_GPF(0), | |
347 | .ngpio = S3C64XX_GPIO_F_NR, | |
348 | .label = "GPF", | |
349 | }, | |
350 | }, { | |
351 | .base = S3C64XX_GPI_BASE, | |
21b23664 | 352 | .config = &gpio_2bit_cfg_noint, |
89d043c3 BD |
353 | .chip = { |
354 | .base = S3C64XX_GPI(0), | |
355 | .ngpio = S3C64XX_GPIO_I_NR, | |
356 | .label = "GPI", | |
357 | }, | |
358 | }, { | |
359 | .base = S3C64XX_GPJ_BASE, | |
21b23664 | 360 | .config = &gpio_2bit_cfg_noint, |
89d043c3 BD |
361 | .chip = { |
362 | .base = S3C64XX_GPJ(0), | |
363 | .ngpio = S3C64XX_GPIO_J_NR, | |
364 | .label = "GPJ", | |
365 | }, | |
366 | }, { | |
367 | .base = S3C64XX_GPN_BASE, | |
21b23664 | 368 | .config = &gpio_2bit_cfg_eint10, |
89d043c3 BD |
369 | .chip = { |
370 | .base = S3C64XX_GPN(0), | |
371 | .ngpio = S3C64XX_GPIO_N_NR, | |
372 | .label = "GPN", | |
0b495737 | 373 | .to_irq = s3c64xx_gpio2int_gpn, |
89d043c3 BD |
374 | }, |
375 | }, { | |
376 | .base = S3C64XX_GPO_BASE, | |
21b23664 | 377 | .config = &gpio_2bit_cfg_eint11, |
89d043c3 BD |
378 | .chip = { |
379 | .base = S3C64XX_GPO(0), | |
380 | .ngpio = S3C64XX_GPIO_O_NR, | |
381 | .label = "GPO", | |
382 | }, | |
383 | }, { | |
384 | .base = S3C64XX_GPP_BASE, | |
21b23664 | 385 | .config = &gpio_2bit_cfg_eint11, |
89d043c3 BD |
386 | .chip = { |
387 | .base = S3C64XX_GPP(0), | |
388 | .ngpio = S3C64XX_GPIO_P_NR, | |
389 | .label = "GPP", | |
390 | }, | |
391 | }, { | |
392 | .base = S3C64XX_GPQ_BASE, | |
21b23664 | 393 | .config = &gpio_2bit_cfg_eint11, |
89d043c3 BD |
394 | .chip = { |
395 | .base = S3C64XX_GPQ(0), | |
396 | .ngpio = S3C64XX_GPIO_Q_NR, | |
397 | .label = "GPQ", | |
398 | }, | |
399 | }, | |
400 | }; | |
401 | ||
402 | static __init void s3c64xx_gpiolib_add_4bit(struct s3c_gpio_chip *chip) | |
403 | { | |
404 | chip->chip.direction_input = s3c64xx_gpiolib_4bit_input; | |
405 | chip->chip.direction_output = s3c64xx_gpiolib_4bit_output; | |
d87964c4 | 406 | chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); |
89d043c3 BD |
407 | } |
408 | ||
409 | static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) | |
410 | { | |
411 | chip->chip.direction_input = s3c64xx_gpiolib_4bit2_input; | |
412 | chip->chip.direction_output = s3c64xx_gpiolib_4bit2_output; | |
d87964c4 BD |
413 | chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); |
414 | } | |
415 | ||
416 | static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip) | |
417 | { | |
418 | chip->pm = __gpio_pm(&s3c_gpio_pm_2bit); | |
89d043c3 BD |
419 | } |
420 | ||
421 | static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, | |
422 | int nr_chips, | |
423 | void (*fn)(struct s3c_gpio_chip *)) | |
424 | { | |
425 | for (; nr_chips > 0; nr_chips--, chips++) { | |
426 | if (fn) | |
427 | (fn)(chips); | |
428 | s3c_gpiolib_add(chips); | |
429 | } | |
430 | } | |
431 | ||
432 | static __init int s3c64xx_gpiolib_init(void) | |
433 | { | |
434 | s3c64xx_gpiolib_add(gpio_4bit, ARRAY_SIZE(gpio_4bit), | |
435 | s3c64xx_gpiolib_add_4bit); | |
436 | ||
437 | s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2), | |
438 | s3c64xx_gpiolib_add_4bit2); | |
439 | ||
d87964c4 BD |
440 | s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), |
441 | s3c64xx_gpiolib_add_2bit); | |
89d043c3 BD |
442 | |
443 | return 0; | |
444 | } | |
445 | ||
24d40767 | 446 | core_initcall(s3c64xx_gpiolib_init); |