Skip to content

Commit

Permalink
[ARM] 4988/1: Add GPIO lib support to the EP93xx
Browse files Browse the repository at this point in the history
Adds support for the generic GPIO lib to the EP93xx family. The gpio
handling code has been moved from core.c to a new file called gpio.c.
The GPIO based IRQ code has not been changed.

Signed-off-by: Ryan Mallon <ryan@bluewatersys.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Ryan Mallon authored and Russell King committed Apr 19, 2008
1 parent 05dda97 commit b685004
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 115 deletions.
1 change: 1 addition & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ config ARCH_EP93XX
select ARM_AMBA
select ARM_VIC
select GENERIC_GPIO
select HAVE_GPIO_LIB
help
This enables support for the Cirrus EP93xx series of CPUs.

Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-ep93xx/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#
# Makefile for the linux kernel.
#
obj-y := core.o clock.o
obj-y := core.o clock.o gpio.o
obj-m :=
obj-n :=
obj- :=
Expand Down
109 changes: 12 additions & 97 deletions arch/arm/mach-ep93xx/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ static const u8 int_type2_register_offset[3] = { 0x94, 0xb0, 0x50 };
static const u8 eoi_register_offset[3] = { 0x98, 0xb4, 0x54 };
static const u8 int_en_register_offset[3] = { 0x9c, 0xb8, 0x5c };

static void update_gpio_int_params(unsigned port)
void ep93xx_gpio_update_int_params(unsigned port)
{
BUG_ON(port > 2);

Expand All @@ -175,98 +175,10 @@ static void update_gpio_int_params(unsigned port)
EP93XX_GPIO_REG(int_en_register_offset[port]));
}

/* Port ordering is: A B F D E C G H */
static const u8 data_register_offset[8] = {
0x00, 0x04, 0x30, 0x0c, 0x20, 0x08, 0x38, 0x40,
};

static const u8 data_direction_register_offset[8] = {
0x10, 0x14, 0x34, 0x1c, 0x24, 0x18, 0x3c, 0x44,
};

#define GPIO_IN 0
#define GPIO_OUT 1

static void ep93xx_gpio_set_direction(unsigned line, int direction)
{
unsigned int data_direction_register;
unsigned long flags;
unsigned char v;

data_direction_register =
EP93XX_GPIO_REG(data_direction_register_offset[line >> 3]);

local_irq_save(flags);
if (direction == GPIO_OUT) {
if (line >= 0 && line <= EP93XX_GPIO_LINE_MAX_IRQ) {
/* Port A/B/F */
gpio_int_unmasked[line >> 3] &= ~(1 << (line & 7));
update_gpio_int_params(line >> 3);
}

v = __raw_readb(data_direction_register);
v |= 1 << (line & 7);
__raw_writeb(v, data_direction_register);
} else if (direction == GPIO_IN) {
v = __raw_readb(data_direction_register);
v &= ~(1 << (line & 7));
__raw_writeb(v, data_direction_register);
}
local_irq_restore(flags);
}

int gpio_direction_input(unsigned gpio)
{
if (gpio > EP93XX_GPIO_LINE_MAX)
return -EINVAL;

ep93xx_gpio_set_direction(gpio, GPIO_IN);

return 0;
}
EXPORT_SYMBOL(gpio_direction_input);

int gpio_direction_output(unsigned gpio, int value)
{
if (gpio > EP93XX_GPIO_LINE_MAX)
return -EINVAL;

gpio_set_value(gpio, value);
ep93xx_gpio_set_direction(gpio, GPIO_OUT);

return 0;
}
EXPORT_SYMBOL(gpio_direction_output);

int gpio_get_value(unsigned gpio)
{
unsigned int data_register;

data_register = EP93XX_GPIO_REG(data_register_offset[gpio >> 3]);

return !!(__raw_readb(data_register) & (1 << (gpio & 7)));
}
EXPORT_SYMBOL(gpio_get_value);

void gpio_set_value(unsigned gpio, int value)
void ep93xx_gpio_int_mask(unsigned line)
{
unsigned int data_register;
unsigned long flags;
unsigned char v;

data_register = EP93XX_GPIO_REG(data_register_offset[gpio >> 3]);

local_irq_save(flags);
v = __raw_readb(data_register);
if (value)
v |= 1 << (gpio & 7);
else
v &= ~(1 << (gpio & 7));
__raw_writeb(v, data_register);
local_irq_restore(flags);
gpio_int_unmasked[line >> 3] &= ~(1 << (line & 7));
}
EXPORT_SYMBOL(gpio_set_value);


/*************************************************************************
* EP93xx IRQ handling
Expand Down Expand Up @@ -316,7 +228,7 @@ static void ep93xx_gpio_irq_ack(unsigned int irq)

if ((irq_desc[irq].status & IRQ_TYPE_SENSE_MASK) == IRQT_BOTHEDGE) {
gpio_int_type2[port] ^= port_mask; /* switch edge direction */
update_gpio_int_params(port);
ep93xx_gpio_update_int_params(port);
}

__raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
Expand All @@ -332,7 +244,7 @@ static void ep93xx_gpio_irq_mask_ack(unsigned int irq)
gpio_int_type2[port] ^= port_mask; /* switch edge direction */

gpio_int_unmasked[port] &= ~port_mask;
update_gpio_int_params(port);
ep93xx_gpio_update_int_params(port);

__raw_writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
}
Expand All @@ -343,7 +255,7 @@ static void ep93xx_gpio_irq_mask(unsigned int irq)
int port = line >> 3;

gpio_int_unmasked[port] &= ~(1 << (line & 7));
update_gpio_int_params(port);
ep93xx_gpio_update_int_params(port);
}

static void ep93xx_gpio_irq_unmask(unsigned int irq)
Expand All @@ -352,7 +264,7 @@ static void ep93xx_gpio_irq_unmask(unsigned int irq)
int port = line >> 3;

gpio_int_unmasked[port] |= 1 << (line & 7);
update_gpio_int_params(port);
ep93xx_gpio_update_int_params(port);
}


Expand All @@ -368,7 +280,7 @@ static int ep93xx_gpio_irq_type(unsigned int irq, unsigned int type)
const int port = gpio >> 3;
const int port_mask = 1 << (gpio & 7);

ep93xx_gpio_set_direction(gpio, GPIO_IN);
gpio_direction_output(gpio, gpio_get_value(gpio));

switch (type) {
case IRQT_RISING:
Expand Down Expand Up @@ -411,7 +323,7 @@ static int ep93xx_gpio_irq_type(unsigned int irq, unsigned int type)
desc->status &= ~IRQ_TYPE_SENSE_MASK;
desc->status |= type & IRQ_TYPE_SENSE_MASK;

update_gpio_int_params(port);
ep93xx_gpio_update_int_params(port);

return 0;
}
Expand Down Expand Up @@ -549,6 +461,7 @@ static struct platform_device ep93xx_ohci_device = {
.resource = ep93xx_ohci_resources,
};

extern void ep93xx_gpio_init(void);

void __init ep93xx_init_devices(void)
{
Expand All @@ -562,6 +475,8 @@ void __init ep93xx_init_devices(void)
__raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
__raw_writel(v, EP93XX_SYSCON_DEVICE_CONFIG);

ep93xx_gpio_init();

amba_device_register(&uart1_device, &iomem_resource);
amba_device_register(&uart2_device, &iomem_resource);
amba_device_register(&uart3_device, &iomem_resource);
Expand Down
158 changes: 158 additions & 0 deletions arch/arm/mach-ep93xx/gpio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* linux/arch/arm/mach-ep93xx/gpio.c
*
* Generic EP93xx GPIO handling
*
* Copyright (c) 2008 Ryan Mallon <ryan@bluewatersys.com>
*
* Based on code originally from:
* linux/arch/arm/mach-ep93xx/core.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>

#include <asm/arch/ep93xx-regs.h>
#include <asm/io.h>
#include <asm/gpio.h>

struct ep93xx_gpio_chip {
struct gpio_chip chip;

unsigned int data_reg;
unsigned int data_dir_reg;
};

#define to_ep93xx_gpio_chip(c) container_of(c, struct ep93xx_gpio_chip, chip)

/* From core.c */
extern void ep93xx_gpio_int_mask(unsigned line);
extern void ep93xx_gpio_update_int_params(unsigned port);

static int ep93xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip);
unsigned long flags;
u8 v;

local_irq_save(flags);
v = __raw_readb(ep93xx_chip->data_dir_reg);
v &= ~(1 << offset);
__raw_writeb(v, ep93xx_chip->data_dir_reg);
local_irq_restore(flags);

return 0;
}

static int ep93xx_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int val)
{
struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip);
unsigned long flags;
int line;
u8 v;

local_irq_save(flags);

/* Set the value */
v = __raw_readb(ep93xx_chip->data_reg);
if (val)
v |= (1 << offset);
else
v &= ~(1 << offset);
__raw_writeb(v, ep93xx_chip->data_reg);

/* Drive as an output */
line = chip->base + offset;
if (line <= EP93XX_GPIO_LINE_MAX_IRQ) {
/* Ports A/B/F */
ep93xx_gpio_int_mask(line);
ep93xx_gpio_update_int_params(line >> 3);
}

v = __raw_readb(ep93xx_chip->data_dir_reg);
v |= (1 << offset);
__raw_writeb(v, ep93xx_chip->data_dir_reg);

local_irq_restore(flags);

return 0;
}

static int ep93xx_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip);

return !!(__raw_readb(ep93xx_chip->data_reg) & (1 << offset));
}

static void ep93xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
{
struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip);
unsigned long flags;
u8 v;

local_irq_save(flags);
v = __raw_readb(ep93xx_chip->data_reg);
if (val)
v |= (1 << offset);
else
v &= ~(1 << offset);
__raw_writeb(v, ep93xx_chip->data_reg);
local_irq_restore(flags);
}

static void ep93xx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip);
u8 data_reg, data_dir_reg;
int i;

data_reg = __raw_readb(ep93xx_chip->data_reg);
data_dir_reg = __raw_readb(ep93xx_chip->data_dir_reg);

for (i = 0; i < chip->ngpio; i++)
seq_printf(s, "GPIO %s%d: %s %s\n", chip->label, i,
(data_reg & (1 << i)) ? "set" : "clear",
(data_dir_reg & (1 << i)) ? "out" : "in");
}

#define EP93XX_GPIO_BANK(name, dr, ddr, base_gpio) \
{ \
.chip = { \
.label = name, \
.direction_input = ep93xx_gpio_direction_input, \
.direction_output = ep93xx_gpio_direction_output, \
.get = ep93xx_gpio_get, \
.set = ep93xx_gpio_set, \
.dbg_show = ep93xx_gpio_dbg_show, \
.base = base_gpio, \
.ngpio = 8, \
}, \
.data_reg = EP93XX_GPIO_REG(dr), \
.data_dir_reg = EP93XX_GPIO_REG(ddr), \
}

static struct ep93xx_gpio_chip ep93xx_gpio_banks[] = {
EP93XX_GPIO_BANK("A", 0x00, 0x10, 0),
EP93XX_GPIO_BANK("B", 0x04, 0x14, 8),
EP93XX_GPIO_BANK("C", 0x30, 0x34, 40),
EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24),
EP93XX_GPIO_BANK("E", 0x20, 0x24, 32),
EP93XX_GPIO_BANK("F", 0x08, 0x18, 16),
EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48),
EP93XX_GPIO_BANK("H", 0x40, 0x44, 56),
};

void __init ep93xx_gpio_init(void)
{
int i;

for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++)
gpiochip_add(&ep93xx_gpio_banks[i].chip);
}
21 changes: 4 additions & 17 deletions include/asm-arm/arch-ep93xx/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,30 +101,17 @@

/* new generic GPIO API - see Documentation/gpio.txt */

static inline int gpio_request(unsigned gpio, const char *label)
{
if (gpio > EP93XX_GPIO_LINE_MAX)
return -EINVAL;
return 0;
}
#include <asm-generic/gpio.h>

static inline void gpio_free(unsigned gpio)
{
}

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

#include <asm-generic/gpio.h> /* cansleep wrappers */
#define gpio_get_value __gpio_get_value
#define gpio_set_value __gpio_set_value
#define gpio_cansleep __gpio_cansleep

/*
* Map GPIO A0..A7 (0..7) to irq 64..71,
* B0..B7 (7..15) to irq 72..79, and
* F0..F7 (16..24) to irq 80..87.
*/

static inline int gpio_to_irq(unsigned gpio)
{
if (gpio <= EP93XX_GPIO_LINE_MAX_IRQ)
Expand Down

0 comments on commit b685004

Please sign in to comment.