Skip to content

Commit

Permalink
gpiolib: avr32 at32ap platform support
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Haavard Skinnemoen <[email protected]>
Cc: Jean Delvare <[email protected]>
Cc: Eric Miao <[email protected]>
Cc: Sam Ravnborg <[email protected]>
Cc: Philipp Zabel <[email protected]>
Cc: Russell King <[email protected]>
Cc: Ben Gardner <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
David Brownell authored and Linus Torvalds committed Feb 5, 2008
1 parent b72540c commit b98348b
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 94 deletions.
1 change: 1 addition & 0 deletions arch/avr32/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ config PLATFORM_AT32AP
select SUBARCH_AVR32B
select MMU
select PERFORMANCE_COUNTERS
select HAVE_GPIO_LIB

#
# CPU types
Expand Down
172 changes: 92 additions & 80 deletions arch/avr32/mach-at32ap/pio.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
#define MAX_NR_PIO_DEVICES 8

struct pio_device {
struct gpio_chip chip;
void __iomem *regs;
const struct platform_device *pdev;
struct clk *clk;
u32 pinmux_mask;
u32 gpio_mask;
char name[8];
};

Expand Down Expand Up @@ -64,7 +64,8 @@ void __init at32_select_periph(unsigned int pin, unsigned int periph,
goto fail;
}

if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask))) {
if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask)
|| gpiochip_is_requested(&pio->chip, pin_index))) {
printk("%s: pin %u is busy\n", pio->name, pin_index);
goto fail;
}
Expand All @@ -79,9 +80,6 @@ void __init at32_select_periph(unsigned int pin, unsigned int periph,
if (!(flags & AT32_GPIOF_PULLUP))
pio_writel(pio, PUDR, mask);

/* gpio_request NOT allowed */
set_bit(pin_index, &pio->gpio_mask);

return;

fail:
Expand Down Expand Up @@ -130,9 +128,6 @@ void __init at32_select_gpio(unsigned int pin, unsigned long flags)

pio_writel(pio, PER, mask);

/* gpio_request now allowed */
clear_bit(pin_index, &pio->gpio_mask);

return;

fail:
Expand Down Expand Up @@ -166,96 +161,50 @@ void __init at32_reserve_pin(unsigned int pin)

/* GPIO API */

int gpio_request(unsigned int gpio, const char *label)
static int direction_input(struct gpio_chip *chip, unsigned offset)
{
struct pio_device *pio;
unsigned int pin;

pio = gpio_to_pio(gpio);
if (!pio)
return -ENODEV;
struct pio_device *pio = container_of(chip, struct pio_device, chip);
u32 mask = 1 << offset;

pin = gpio & 0x1f;
if (test_and_set_bit(pin, &pio->gpio_mask))
return -EBUSY;
if (!(pio_readl(pio, PSR) & mask))
return -EINVAL;

pio_writel(pio, ODR, mask);
return 0;
}
EXPORT_SYMBOL(gpio_request);

void gpio_free(unsigned int gpio)
static int gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct pio_device *pio;
unsigned int pin;
struct pio_device *pio = container_of(chip, struct pio_device, chip);

pio = gpio_to_pio(gpio);
if (!pio) {
printk(KERN_ERR
"gpio: attempted to free invalid pin %u\n", gpio);
return;
}

pin = gpio & 0x1f;
if (!test_and_clear_bit(pin, &pio->gpio_mask))
printk(KERN_ERR "gpio: freeing free or non-gpio pin %s-%u\n",
pio->name, pin);
return (pio_readl(pio, PDSR) >> offset) & 1;
}
EXPORT_SYMBOL(gpio_free);

int gpio_direction_input(unsigned int gpio)
{
struct pio_device *pio;
unsigned int pin;

pio = gpio_to_pio(gpio);
if (!pio)
return -ENODEV;

pin = gpio & 0x1f;
pio_writel(pio, ODR, 1 << pin);

return 0;
}
EXPORT_SYMBOL(gpio_direction_input);
static void gpio_set(struct gpio_chip *chip, unsigned offset, int value);

int gpio_direction_output(unsigned int gpio, int value)
static int direction_output(struct gpio_chip *chip, unsigned offset, int value)
{
struct pio_device *pio;
unsigned int pin;

pio = gpio_to_pio(gpio);
if (!pio)
return -ENODEV;
struct pio_device *pio = container_of(chip, struct pio_device, chip);
u32 mask = 1 << offset;

gpio_set_value(gpio, value);

pin = gpio & 0x1f;
pio_writel(pio, OER, 1 << pin);
if (!(pio_readl(pio, PSR) & mask))
return -EINVAL;

gpio_set(chip, offset, value);
pio_writel(pio, OER, mask);
return 0;
}
EXPORT_SYMBOL(gpio_direction_output);

int gpio_get_value(unsigned int gpio)
static void gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct pio_device *pio = &pio_dev[gpio >> 5];
struct pio_device *pio = container_of(chip, struct pio_device, chip);
u32 mask = 1 << offset;

return (pio_readl(pio, PDSR) >> (gpio & 0x1f)) & 1;
}
EXPORT_SYMBOL(gpio_get_value);

void gpio_set_value(unsigned int gpio, int value)
{
struct pio_device *pio = &pio_dev[gpio >> 5];
u32 mask;

mask = 1 << (gpio & 0x1f);
if (value)
pio_writel(pio, SODR, mask);
else
pio_writel(pio, CODR, mask);
}
EXPORT_SYMBOL(gpio_set_value);

/*--------------------------------------------------------------------------*/

Expand Down Expand Up @@ -337,6 +286,63 @@ gpio_irq_setup(struct pio_device *pio, int irq, int gpio_irq)
set_irq_chained_handler(irq, gpio_irq_handler);
}

/*--------------------------------------------------------------------------*/

#ifdef CONFIG_DEBUG_FS

#include <linux/seq_file.h>

/*
* This shows more info than the generic gpio dump code:
* pullups, deglitching, open drain drive.
*/
static void pio_bank_show(struct seq_file *s, struct gpio_chip *chip)
{
struct pio_device *pio = container_of(chip, struct pio_device, chip);
u32 psr, osr, imr, pdsr, pusr, ifsr, mdsr;
unsigned i;
u32 mask;
char bank;

psr = pio_readl(pio, PSR);
osr = pio_readl(pio, OSR);
imr = pio_readl(pio, IMR);
pdsr = pio_readl(pio, PDSR);
pusr = pio_readl(pio, PUSR);
ifsr = pio_readl(pio, IFSR);
mdsr = pio_readl(pio, MDSR);

bank = 'A' + pio->pdev->id;

for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
const char *label;

label = gpiochip_is_requested(chip, i);
if (!label)
continue;

seq_printf(s, " gpio-%-3d P%c%-2d (%-12s) %s %s %s",
chip->base + i, bank, i,
label,
(osr & mask) ? "out" : "in ",
(mask & pdsr) ? "hi" : "lo",
(mask & pusr) ? " " : "up");
if (ifsr & mask)
seq_printf(s, " deglitch");
if ((osr & mdsr) & mask)
seq_printf(s, " open-drain");
if (imr & mask)
seq_printf(s, " irq-%d edge-both",
gpio_to_irq(chip->base + i));
seq_printf(s, "\n");
}
}

#else
#define pio_bank_show NULL
#endif


/*--------------------------------------------------------------------------*/

static int __init pio_probe(struct platform_device *pdev)
Expand All @@ -349,6 +355,18 @@ static int __init pio_probe(struct platform_device *pdev)
pio = &pio_dev[pdev->id];
BUG_ON(!pio->regs);

pio->chip.label = pio->name;
pio->chip.base = pdev->id * 32;
pio->chip.ngpio = 32;

pio->chip.direction_input = direction_input;
pio->chip.get = gpio_get;
pio->chip.direction_output = direction_output;
pio->chip.set = gpio_set;
pio->chip.dbg_show = pio_bank_show;

gpiochip_add(&pio->chip);

gpio_irq_setup(pio, irq, gpio_irq_base);

platform_set_drvdata(pdev, pio);
Expand Down Expand Up @@ -406,12 +424,6 @@ void __init at32_init_pio(struct platform_device *pdev)
pio->pdev = pdev;
pio->regs = ioremap(regs->start, regs->end - regs->start + 1);

/*
* request_gpio() is only valid for pins that have been
* explicitly configured as GPIO and not previously requested
*/
pio->gpio_mask = ~0UL;

/* start with irqs disabled and acked */
pio_writel(pio, IDR, ~0UL);
(void) pio_readl(pio, ISR);
Expand Down
2 changes: 1 addition & 1 deletion arch/avr32/mach-at32ap/pio.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#define PIO_OSR 0x0018
#define PIO_IFER 0x0020
#define PIO_IFDR 0x0024
#define PIO_ISFR 0x0028
#define PIO_IFSR 0x0028
#define PIO_SODR 0x0030
#define PIO_CODR 0x0034
#define PIO_ODSR 0x0038
Expand Down
2 changes: 0 additions & 2 deletions include/asm-avr32/arch-at32ap/at32ap700x.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
#define GPIO_PERIPH_A 0
#define GPIO_PERIPH_B 1

#define NR_GPIO_CONTROLLERS 4

/*
* Pin numbers identifying specific GPIO pins on the chip. They can
* also be converted to IRQ numbers by passing them through
Expand Down
34 changes: 25 additions & 9 deletions include/asm-avr32/arch-at32ap/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,36 @@
#include <asm/irq.h>


/* Arch-neutral GPIO API */
int __must_check gpio_request(unsigned int gpio, const char *label);
void gpio_free(unsigned int gpio);
/* Some GPIO chips can manage IRQs; some can't. The exact numbers can
* be changed if needed, but for the moment they're not configurable.
*/
#define ARCH_NR_GPIOS (NR_GPIO_IRQS + 2 * 32)

int gpio_direction_input(unsigned int gpio);
int gpio_direction_output(unsigned int gpio, int value);
int gpio_get_value(unsigned int gpio);
void gpio_set_value(unsigned int gpio, int value);

#include <asm-generic/gpio.h> /* cansleep wrappers */
/* Arch-neutral GPIO API, supporting both "native" and external GPIOs. */
#include <asm-generic/gpio.h>

static inline int gpio_get_value(unsigned int gpio)
{
return __gpio_get_value(gpio);
}

static inline void gpio_set_value(unsigned int gpio, int value)
{
__gpio_set_value(gpio, value);
}

static inline int gpio_cansleep(unsigned int gpio)
{
return __gpio_cansleep(gpio);
}


static inline int gpio_to_irq(unsigned int gpio)
{
return gpio + GPIO_IRQ_BASE;
if (gpio < NR_GPIO_IRQS)
return gpio + GPIO_IRQ_BASE;
return -EINVAL;
}

static inline int irq_to_gpio(unsigned int irq)
Expand Down
4 changes: 2 additions & 2 deletions include/asm-avr32/arch-at32ap/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

#define EIM_IRQ_BASE NR_INTERNAL_IRQS
#define NR_EIM_IRQS 32

#define AT32_EXTINT(n) (EIM_IRQ_BASE + (n))

#define GPIO_IRQ_BASE (EIM_IRQ_BASE + NR_EIM_IRQS)
#define NR_GPIO_IRQS (5 * 32)
#define NR_GPIO_CTLR (5 /*internal*/ + 1 /*external*/)
#define NR_GPIO_IRQS (NR_GPIO_CTLR * 32)

#define NR_IRQS (GPIO_IRQ_BASE + NR_GPIO_IRQS)

Expand Down

0 comments on commit b98348b

Please sign in to comment.