diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2008-02-05 01:28:28 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-05 12:44:13 -0500 |
commit | b98348bdd08dc4ec11828aa98a78edde15c53cfa (patch) | |
tree | a0b4618bf8ec436581f29080c32ddb07e7489bd0 /arch/avr32 | |
parent | b72540c30c9c8c2c3f17cae29962cfb50fbe166a (diff) |
gpiolib: avr32 at32ap platform support
Teach AVR32 to use the "GPIO Library" when exposing its GPIOs, so that signals
on external chips (like GPIO expanders) can easily be used.
This mostly reorganizes some existing logic, with two minor changes in
behavior:
- The PSR registers are used instead of the previous "gpio_mask" values,
matching AT91 behavior and removing some duplication between that role
and that of "pinmux_mask".
- NR_IRQs grew to acommodate a bank of external GPIOs. Eventually this
number should probably become a board-specific config option.
There's a debugfs dump of status for the built-in GPIOs, showing which pins
have deglitching, pullups, or open drain drive enabled, as well as the ID
string used when requesting each IRQ.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Cc: Jean Delvare <khali@linux-fr.org>
Cc: Eric Miao <eric.miao@marvell.com>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Philipp Zabel <philipp.zabel@gmail.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: Ben Gardner <bgardner@wabtec.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/avr32')
-rw-r--r-- | arch/avr32/Kconfig | 1 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pio.c | 172 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/pio.h | 2 |
3 files changed, 94 insertions, 81 deletions
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index c816f29154c9..28e0caf4156c 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig | |||
@@ -82,6 +82,7 @@ config PLATFORM_AT32AP | |||
82 | select SUBARCH_AVR32B | 82 | select SUBARCH_AVR32B |
83 | select MMU | 83 | select MMU |
84 | select PERFORMANCE_COUNTERS | 84 | select PERFORMANCE_COUNTERS |
85 | select HAVE_GPIO_LIB | ||
85 | 86 | ||
86 | # | 87 | # |
87 | # CPU types | 88 | # CPU types |
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index d61a02da898c..38a8fa31c0b5 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c | |||
@@ -24,11 +24,11 @@ | |||
24 | #define MAX_NR_PIO_DEVICES 8 | 24 | #define MAX_NR_PIO_DEVICES 8 |
25 | 25 | ||
26 | struct pio_device { | 26 | struct pio_device { |
27 | struct gpio_chip chip; | ||
27 | void __iomem *regs; | 28 | void __iomem *regs; |
28 | const struct platform_device *pdev; | 29 | const struct platform_device *pdev; |
29 | struct clk *clk; | 30 | struct clk *clk; |
30 | u32 pinmux_mask; | 31 | u32 pinmux_mask; |
31 | u32 gpio_mask; | ||
32 | char name[8]; | 32 | char name[8]; |
33 | }; | 33 | }; |
34 | 34 | ||
@@ -64,7 +64,8 @@ void __init at32_select_periph(unsigned int pin, unsigned int periph, | |||
64 | goto fail; | 64 | goto fail; |
65 | } | 65 | } |
66 | 66 | ||
67 | if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask))) { | 67 | if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask) |
68 | || gpiochip_is_requested(&pio->chip, pin_index))) { | ||
68 | printk("%s: pin %u is busy\n", pio->name, pin_index); | 69 | printk("%s: pin %u is busy\n", pio->name, pin_index); |
69 | goto fail; | 70 | goto fail; |
70 | } | 71 | } |
@@ -79,9 +80,6 @@ void __init at32_select_periph(unsigned int pin, unsigned int periph, | |||
79 | if (!(flags & AT32_GPIOF_PULLUP)) | 80 | if (!(flags & AT32_GPIOF_PULLUP)) |
80 | pio_writel(pio, PUDR, mask); | 81 | pio_writel(pio, PUDR, mask); |
81 | 82 | ||
82 | /* gpio_request NOT allowed */ | ||
83 | set_bit(pin_index, &pio->gpio_mask); | ||
84 | |||
85 | return; | 83 | return; |
86 | 84 | ||
87 | fail: | 85 | fail: |
@@ -130,9 +128,6 @@ void __init at32_select_gpio(unsigned int pin, unsigned long flags) | |||
130 | 128 | ||
131 | pio_writel(pio, PER, mask); | 129 | pio_writel(pio, PER, mask); |
132 | 130 | ||
133 | /* gpio_request now allowed */ | ||
134 | clear_bit(pin_index, &pio->gpio_mask); | ||
135 | |||
136 | return; | 131 | return; |
137 | 132 | ||
138 | fail: | 133 | fail: |
@@ -166,96 +161,50 @@ fail: | |||
166 | 161 | ||
167 | /* GPIO API */ | 162 | /* GPIO API */ |
168 | 163 | ||
169 | int gpio_request(unsigned int gpio, const char *label) | 164 | static int direction_input(struct gpio_chip *chip, unsigned offset) |
170 | { | 165 | { |
171 | struct pio_device *pio; | 166 | struct pio_device *pio = container_of(chip, struct pio_device, chip); |
172 | unsigned int pin; | 167 | u32 mask = 1 << offset; |
173 | |||
174 | pio = gpio_to_pio(gpio); | ||
175 | if (!pio) | ||
176 | return -ENODEV; | ||
177 | 168 | ||
178 | pin = gpio & 0x1f; | 169 | if (!(pio_readl(pio, PSR) & mask)) |
179 | if (test_and_set_bit(pin, &pio->gpio_mask)) | 170 | return -EINVAL; |
180 | return -EBUSY; | ||
181 | 171 | ||
172 | pio_writel(pio, ODR, mask); | ||
182 | return 0; | 173 | return 0; |
183 | } | 174 | } |
184 | EXPORT_SYMBOL(gpio_request); | ||
185 | 175 | ||
186 | void gpio_free(unsigned int gpio) | 176 | static int gpio_get(struct gpio_chip *chip, unsigned offset) |
187 | { | 177 | { |
188 | struct pio_device *pio; | 178 | struct pio_device *pio = container_of(chip, struct pio_device, chip); |
189 | unsigned int pin; | ||
190 | 179 | ||
191 | pio = gpio_to_pio(gpio); | 180 | return (pio_readl(pio, PDSR) >> offset) & 1; |
192 | if (!pio) { | ||
193 | printk(KERN_ERR | ||
194 | "gpio: attempted to free invalid pin %u\n", gpio); | ||
195 | return; | ||
196 | } | ||
197 | |||
198 | pin = gpio & 0x1f; | ||
199 | if (!test_and_clear_bit(pin, &pio->gpio_mask)) | ||
200 | printk(KERN_ERR "gpio: freeing free or non-gpio pin %s-%u\n", | ||
201 | pio->name, pin); | ||
202 | } | 181 | } |
203 | EXPORT_SYMBOL(gpio_free); | ||
204 | 182 | ||
205 | int gpio_direction_input(unsigned int gpio) | 183 | static void gpio_set(struct gpio_chip *chip, unsigned offset, int value); |
206 | { | ||
207 | struct pio_device *pio; | ||
208 | unsigned int pin; | ||
209 | |||
210 | pio = gpio_to_pio(gpio); | ||
211 | if (!pio) | ||
212 | return -ENODEV; | ||
213 | |||
214 | pin = gpio & 0x1f; | ||
215 | pio_writel(pio, ODR, 1 << pin); | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | EXPORT_SYMBOL(gpio_direction_input); | ||
220 | 184 | ||
221 | int gpio_direction_output(unsigned int gpio, int value) | 185 | static int direction_output(struct gpio_chip *chip, unsigned offset, int value) |
222 | { | 186 | { |
223 | struct pio_device *pio; | 187 | struct pio_device *pio = container_of(chip, struct pio_device, chip); |
224 | unsigned int pin; | 188 | u32 mask = 1 << offset; |
225 | |||
226 | pio = gpio_to_pio(gpio); | ||
227 | if (!pio) | ||
228 | return -ENODEV; | ||
229 | 189 | ||
230 | gpio_set_value(gpio, value); | 190 | if (!(pio_readl(pio, PSR) & mask)) |
231 | 191 | return -EINVAL; | |
232 | pin = gpio & 0x1f; | ||
233 | pio_writel(pio, OER, 1 << pin); | ||
234 | 192 | ||
193 | gpio_set(chip, offset, value); | ||
194 | pio_writel(pio, OER, mask); | ||
235 | return 0; | 195 | return 0; |
236 | } | 196 | } |
237 | EXPORT_SYMBOL(gpio_direction_output); | ||
238 | 197 | ||
239 | int gpio_get_value(unsigned int gpio) | 198 | static void gpio_set(struct gpio_chip *chip, unsigned offset, int value) |
240 | { | 199 | { |
241 | struct pio_device *pio = &pio_dev[gpio >> 5]; | 200 | struct pio_device *pio = container_of(chip, struct pio_device, chip); |
201 | u32 mask = 1 << offset; | ||
242 | 202 | ||
243 | return (pio_readl(pio, PDSR) >> (gpio & 0x1f)) & 1; | ||
244 | } | ||
245 | EXPORT_SYMBOL(gpio_get_value); | ||
246 | |||
247 | void gpio_set_value(unsigned int gpio, int value) | ||
248 | { | ||
249 | struct pio_device *pio = &pio_dev[gpio >> 5]; | ||
250 | u32 mask; | ||
251 | |||
252 | mask = 1 << (gpio & 0x1f); | ||
253 | if (value) | 203 | if (value) |
254 | pio_writel(pio, SODR, mask); | 204 | pio_writel(pio, SODR, mask); |
255 | else | 205 | else |
256 | pio_writel(pio, CODR, mask); | 206 | pio_writel(pio, CODR, mask); |
257 | } | 207 | } |
258 | EXPORT_SYMBOL(gpio_set_value); | ||
259 | 208 | ||
260 | /*--------------------------------------------------------------------------*/ | 209 | /*--------------------------------------------------------------------------*/ |
261 | 210 | ||
@@ -339,6 +288,63 @@ gpio_irq_setup(struct pio_device *pio, int irq, int gpio_irq) | |||
339 | 288 | ||
340 | /*--------------------------------------------------------------------------*/ | 289 | /*--------------------------------------------------------------------------*/ |
341 | 290 | ||
291 | #ifdef CONFIG_DEBUG_FS | ||
292 | |||
293 | #include <linux/seq_file.h> | ||
294 | |||
295 | /* | ||
296 | * This shows more info than the generic gpio dump code: | ||
297 | * pullups, deglitching, open drain drive. | ||
298 | */ | ||
299 | static void pio_bank_show(struct seq_file *s, struct gpio_chip *chip) | ||
300 | { | ||
301 | struct pio_device *pio = container_of(chip, struct pio_device, chip); | ||
302 | u32 psr, osr, imr, pdsr, pusr, ifsr, mdsr; | ||
303 | unsigned i; | ||
304 | u32 mask; | ||
305 | char bank; | ||
306 | |||
307 | psr = pio_readl(pio, PSR); | ||
308 | osr = pio_readl(pio, OSR); | ||
309 | imr = pio_readl(pio, IMR); | ||
310 | pdsr = pio_readl(pio, PDSR); | ||
311 | pusr = pio_readl(pio, PUSR); | ||
312 | ifsr = pio_readl(pio, IFSR); | ||
313 | mdsr = pio_readl(pio, MDSR); | ||
314 | |||
315 | bank = 'A' + pio->pdev->id; | ||
316 | |||
317 | for (i = 0, mask = 1; i < 32; i++, mask <<= 1) { | ||
318 | const char *label; | ||
319 | |||
320 | label = gpiochip_is_requested(chip, i); | ||
321 | if (!label) | ||
322 | continue; | ||
323 | |||
324 | seq_printf(s, " gpio-%-3d P%c%-2d (%-12s) %s %s %s", | ||
325 | chip->base + i, bank, i, | ||
326 | label, | ||
327 | (osr & mask) ? "out" : "in ", | ||
328 | (mask & pdsr) ? "hi" : "lo", | ||
329 | (mask & pusr) ? " " : "up"); | ||
330 | if (ifsr & mask) | ||
331 | seq_printf(s, " deglitch"); | ||
332 | if ((osr & mdsr) & mask) | ||
333 | seq_printf(s, " open-drain"); | ||
334 | if (imr & mask) | ||
335 | seq_printf(s, " irq-%d edge-both", | ||
336 | gpio_to_irq(chip->base + i)); | ||
337 | seq_printf(s, "\n"); | ||
338 | } | ||
339 | } | ||
340 | |||
341 | #else | ||
342 | #define pio_bank_show NULL | ||
343 | #endif | ||
344 | |||
345 | |||
346 | /*--------------------------------------------------------------------------*/ | ||
347 | |||
342 | static int __init pio_probe(struct platform_device *pdev) | 348 | static int __init pio_probe(struct platform_device *pdev) |
343 | { | 349 | { |
344 | struct pio_device *pio = NULL; | 350 | struct pio_device *pio = NULL; |
@@ -349,6 +355,18 @@ static int __init pio_probe(struct platform_device *pdev) | |||
349 | pio = &pio_dev[pdev->id]; | 355 | pio = &pio_dev[pdev->id]; |
350 | BUG_ON(!pio->regs); | 356 | BUG_ON(!pio->regs); |
351 | 357 | ||
358 | pio->chip.label = pio->name; | ||
359 | pio->chip.base = pdev->id * 32; | ||
360 | pio->chip.ngpio = 32; | ||
361 | |||
362 | pio->chip.direction_input = direction_input; | ||
363 | pio->chip.get = gpio_get; | ||
364 | pio->chip.direction_output = direction_output; | ||
365 | pio->chip.set = gpio_set; | ||
366 | pio->chip.dbg_show = pio_bank_show; | ||
367 | |||
368 | gpiochip_add(&pio->chip); | ||
369 | |||
352 | gpio_irq_setup(pio, irq, gpio_irq_base); | 370 | gpio_irq_setup(pio, irq, gpio_irq_base); |
353 | 371 | ||
354 | platform_set_drvdata(pdev, pio); | 372 | platform_set_drvdata(pdev, pio); |
@@ -406,12 +424,6 @@ void __init at32_init_pio(struct platform_device *pdev) | |||
406 | pio->pdev = pdev; | 424 | pio->pdev = pdev; |
407 | pio->regs = ioremap(regs->start, regs->end - regs->start + 1); | 425 | pio->regs = ioremap(regs->start, regs->end - regs->start + 1); |
408 | 426 | ||
409 | /* | ||
410 | * request_gpio() is only valid for pins that have been | ||
411 | * explicitly configured as GPIO and not previously requested | ||
412 | */ | ||
413 | pio->gpio_mask = ~0UL; | ||
414 | |||
415 | /* start with irqs disabled and acked */ | 427 | /* start with irqs disabled and acked */ |
416 | pio_writel(pio, IDR, ~0UL); | 428 | pio_writel(pio, IDR, ~0UL); |
417 | (void) pio_readl(pio, ISR); | 429 | (void) pio_readl(pio, ISR); |
diff --git a/arch/avr32/mach-at32ap/pio.h b/arch/avr32/mach-at32ap/pio.h index 50fa3aca32c5..7795116a483a 100644 --- a/arch/avr32/mach-at32ap/pio.h +++ b/arch/avr32/mach-at32ap/pio.h | |||
@@ -19,7 +19,7 @@ | |||
19 | #define PIO_OSR 0x0018 | 19 | #define PIO_OSR 0x0018 |
20 | #define PIO_IFER 0x0020 | 20 | #define PIO_IFER 0x0020 |
21 | #define PIO_IFDR 0x0024 | 21 | #define PIO_IFDR 0x0024 |
22 | #define PIO_ISFR 0x0028 | 22 | #define PIO_IFSR 0x0028 |
23 | #define PIO_SODR 0x0030 | 23 | #define PIO_SODR 0x0030 |
24 | #define PIO_CODR 0x0034 | 24 | #define PIO_CODR 0x0034 |
25 | #define PIO_ODSR 0x0038 | 25 | #define PIO_ODSR 0x0038 |