diff options
author | Paul Mundt <lethal@linux-sh.org> | 2010-10-04 02:59:29 +0900 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-10-04 02:59:29 +0900 |
commit | 550591143eead286f2cd9d124a55d4a99ed024de (patch) | |
tree | 5f1edc1b8c8e0829f04a5d24b9083bd5a054c1fb /arch/sh/boards/mach-x3proto | |
parent | d39d0ed196aa1685bb24771e92f78633c66ac9cb (diff) |
sh: mach-x3proto: Support for baseboard GPIOs.
This adds trivial support for the GPIOs implemented through the baseboard
CPLD, used for driving the button matrix.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/boards/mach-x3proto')
-rw-r--r-- | arch/sh/boards/mach-x3proto/Makefile | 2 | ||||
-rw-r--r-- | arch/sh/boards/mach-x3proto/gpio.c | 135 |
2 files changed, 137 insertions, 0 deletions
diff --git a/arch/sh/boards/mach-x3proto/Makefile b/arch/sh/boards/mach-x3proto/Makefile index 983e4551fec..708c21c919f 100644 --- a/arch/sh/boards/mach-x3proto/Makefile +++ b/arch/sh/boards/mach-x3proto/Makefile @@ -1 +1,3 @@ obj-y += setup.o ilsel.o + +obj-$(CONFIG_GENERIC_GPIO) += gpio.o diff --git a/arch/sh/boards/mach-x3proto/gpio.c b/arch/sh/boards/mach-x3proto/gpio.c new file mode 100644 index 00000000000..9fcd7ce6aa4 --- /dev/null +++ b/arch/sh/boards/mach-x3proto/gpio.c @@ -0,0 +1,135 @@ +/* + * arch/sh/boards/mach-x3proto/gpio.c + * + * Renesas SH-X3 Prototype Baseboard GPIO Support. + * + * Copyright (C) 2010 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <mach/ilsel.h> +#include <mach/hardware.h> + +#define KEYCTLR 0xb81c0000 +#define KEYOUTR 0xb81c0002 +#define KEYDETR 0xb81c0004 + +static DEFINE_SPINLOCK(x3proto_gpio_lock); +static unsigned int x3proto_gpio_irq_map[NR_BASEBOARD_GPIOS] = { 0, }; + +static int x3proto_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + unsigned long flags; + unsigned int data; + + spin_lock_irqsave(&x3proto_gpio_lock, flags); + data = __raw_readw(KEYCTLR); + data |= (1 << gpio); + __raw_writew(data, KEYCTLR); + spin_unlock_irqrestore(&x3proto_gpio_lock, flags); + + return 0; +} + +static int x3proto_gpio_get(struct gpio_chip *chip, unsigned gpio) +{ + return !!(__raw_readw(KEYDETR) & (1 << gpio)); +} + +static int x3proto_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) +{ + return x3proto_gpio_irq_map[gpio]; +} + +static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = get_irq_desc_chip(desc); + unsigned long mask; + int pin; + + chip->mask_ack(irq); + + mask = __raw_readw(KEYDETR); + + for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) + generic_handle_irq(x3proto_gpio_to_irq(NULL, pin)); + + chip->unmask(irq); +} + +struct gpio_chip x3proto_gpio_chip = { + .label = "x3proto-gpio", + .direction_input = x3proto_gpio_direction_input, + .get = x3proto_gpio_get, + .to_irq = x3proto_gpio_to_irq, + .base = -1, + .ngpio = NR_BASEBOARD_GPIOS, +}; + +int __init x3proto_gpio_setup(void) +{ + unsigned int ilsel; + int ret, i; + + ilsel = ilsel_enable(ILSEL_KEY); + if (unlikely(ilsel < 0)) + return ilsel; + + ret = gpiochip_add(&x3proto_gpio_chip); + if (unlikely(ret)) + goto err_gpio; + + for (i = 0; i < NR_BASEBOARD_GPIOS; i++) { + unsigned long flags; + unsigned int irq = create_irq(); + + if (unlikely(irq < 0)) { + ret = -EINVAL; + goto err_irq; + } + + spin_lock_irqsave(&x3proto_gpio_lock, flags); + x3proto_gpio_irq_map[i] = irq; + set_irq_chip_and_handler_name(irq, &dummy_irq_chip, + handle_simple_irq, "gpio"); + spin_unlock_irqrestore(&x3proto_gpio_lock, flags); + } + + pr_info("registering '%s' support, handling GPIOs %u -> %u, " + "bound to IRQ %u\n", + x3proto_gpio_chip.label, x3proto_gpio_chip.base, + x3proto_gpio_chip.base + x3proto_gpio_chip.ngpio, + ilsel); + + set_irq_chained_handler(ilsel, x3proto_gpio_irq_handler); + set_irq_wake(ilsel, 1); + + return 0; + +err_irq: + for (; i >= 0; --i) + if (x3proto_gpio_irq_map[i]) + destroy_irq(x3proto_gpio_irq_map[i]); + + ret = gpiochip_remove(&x3proto_gpio_chip); + if (unlikely(ret)) + pr_err("Failed deregistering GPIO\n"); + +err_gpio: + synchronize_irq(ilsel); + + ilsel_disable(ILSEL_KEY); + + return ret; +} |