[PATCH] bcmring: add gpio support
Leo Chen
leochen at broadcom.com
Fri Oct 9 13:21:31 EDT 2009
add gpio.c and gpio_irq.c
Signed-off-by: Leo Hao Chen <leochen at broadcom.com>
---
arch/arm/mach-bcmring/gpio.c | 291 +++++++++++++++++++++++++++++
arch/arm/mach-bcmring/gpio_irq.c | 381 ++++++++++++++++++++++++++++++++++++++
2 files changed, 672 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-bcmring/gpio.c
create mode 100644 arch/arm/mach-bcmring/gpio_irq.c
diff --git a/arch/arm/mach-bcmring/gpio.c b/arch/arm/mach-bcmring/gpio.c
new file mode 100644
index 0000000..161e519
--- /dev/null
+++ b/arch/arm/mach-bcmring/gpio.c
@@ -0,0 +1,291 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************
+*
+* gpio.c
+*
+* PURPOSE:
+*
+* This file implements the GPIO chips required to support the onchip
+* gpio pins, as per gpiolib
+*
+*****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+
+#include <mach/gpio_defs.h>
+#include <mach/csp/gpiomux.h>
+#include <mach/csp/chipcHw_inline.h>
+
+/* ---- Public Variables ------------------------------------------------- */
+
+/* ---- Private Constants and Types -------------------------------------- */
+
+typedef struct {
+ struct gpio_chip chip;
+ volatile GPIOHW_REG_t *reg;
+
+} bcmring_gpio_chip;
+
+/* ---- Private Function Prototypes -------------------------------------- */
+
+/* ---- Private Variables ------------------------------------------------ */
+
+/* ---- Functions -------------------------------------------------------- */
+
+/****************************************************************************
+*
+* Called whenever gpio_request is called. This will force the pin to be
+* a GPIO pin. If you want it to be something else, then gpiomux_request
+* should be called instead.
+*
+*****************************************************************************/
+
+static int gpio_bcmring_request(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned gpio;
+ bcmring_gpio_chip *bcmring_chip =
+ container_of(chip, bcmring_gpio_chip, chip);
+
+ gpio = bcmring_chip->chip.base + offset;
+
+ /*
+ * Ensure that the pin is actually configured as a GPIO. If it isn't then
+ * that means that somebody called chipcHW_setGpioPinFunction directly
+ * which is a no-no.
+ */
+
+ if (chipcHw_getGpioPinFunction(gpio) != chipcHw_GPIO_FUNCTION_GPIO) {
+ printk(KERN_ERR
+ "%s: Error - gpio %d not configured as a GPIO pin\n",
+ __FUNCTION__, gpio);
+ return -EBUSY;
+ }
+
+ return 0;
+
+} /* gpio_bcmring_request */
+
+/****************************************************************************
+*
+* Configure a GPIO pin as an input pin
+*
+*****************************************************************************/
+
+static int gpio_bcmring_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ bcmring_gpio_chip *bcmring_chip =
+ container_of(chip, bcmring_gpio_chip, chip);
+
+ GpioHwReg_SetDirInput(bcmring_chip->reg, offset);
+
+ return 0;
+
+} /* gpio_bcmring_direction_input */
+
+/****************************************************************************
+*
+* Configure a GPIO pin as an output pin and sets its initial value.
+*
+*****************************************************************************/
+
+static int gpio_bcmring_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ bcmring_gpio_chip *bcmring_chip =
+ container_of(chip, bcmring_gpio_chip, chip);
+
+ GpioHwReg_SetValDirOutput(bcmring_chip->reg, offset, value);
+
+ return 0;
+
+} /* gpio_bcmring_direction_output */
+
+/****************************************************************************
+*
+* Retrieve the value of a GPIO pin. Note that this returns zero or the raw
+* value.
+*
+*****************************************************************************/
+
+static int gpio_bcmring_get(struct gpio_chip *chip, unsigned offset)
+{
+ bcmring_gpio_chip *bcmring_chip =
+ container_of(chip, bcmring_gpio_chip, chip);
+ int val;
+
+ val = GpioHwReg_GetVal(bcmring_chip->reg, offset);
+
+ return val;
+
+} /* gpio_bcmring_get */
+
+/****************************************************************************
+*
+* Set the value of a GPIO pin
+*
+*****************************************************************************/
+
+static void gpio_bcmring_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ bcmring_gpio_chip *bcmring_chip =
+ container_of(chip, bcmring_gpio_chip, chip);
+
+ GpioHwReg_SetVal(bcmring_chip->reg, offset, value);
+
+} /* gpio_bcmring_set */
+
+/****************************************************************************
+*
+* gpiolib chip description
+*
+*****************************************************************************/
+
+/*
+ * Note: If you need to add a field, like a register base, then create a new
+ * structure which includes the gpio_chip as the first element and has
+ * the custom fields after. See asm-arm/arch-pxa/gpio.c for an example.
+ */
+
+static bcmring_gpio_chip gpio_bcmring_chip[] = {
+ [0] = {
+ .reg = gpioRegP(0),
+ .chip = {
+ .label = "bcmring-low",
+ .request = gpio_bcmring_request,
+ .direction_input = gpio_bcmring_direction_input,
+ .direction_output = gpio_bcmring_direction_output,
+ .get = gpio_bcmring_get,
+ .set = gpio_bcmring_set,
+ .base = 0,
+ .ngpio = GPIOHW_NUM_PINS_LOWER_BLOCK,
+ },
+ },
+ [1] = {
+ .reg = gpioRegP(1),
+ .chip = {
+ .label = "bcmring-high",
+ .request = gpio_bcmring_request,
+ .direction_input = gpio_bcmring_direction_input,
+ .direction_output = gpio_bcmring_direction_output,
+ .get = gpio_bcmring_get,
+ .set = gpio_bcmring_set,
+ .base = GPIOHW_NUM_PINS_LOWER_BLOCK,
+ .ngpio = GPIOHW_NUM_PINS_HIGHER_BLOCK,
+ },
+ },
+};
+
+/****************************************************************************/
+/**
+* @brief mux_isRequested
+*
+* Find out if a gpio pin has already been requested in the gpiolib sense.
+*
+* @return
+* none
+*/
+/****************************************************************************/
+static const char *mux_isRequested(gpio_defs_e pin)
+{
+ int i;
+
+ /*
+ * For now, convert pin number to chip and offset. Later, convert this
+ * into a gpiomux register/chip array structure. Since this is typically
+ * init time code, it may not be worth worrying about for this one case.
+ */
+ for (i = 0; i < ARRAY_LEN(gpio_bcmring_chip); i++) {
+ struct gpio_chip *chipp = &gpio_bcmring_chip[i].chip;
+ int base = chipp->base;
+ if ((pin >= base) && (pin < base + chipp->ngpio)) {
+ return gpiochip_is_requested(chipp, pin - base);
+ }
+ }
+ return NULL;
+
+} /* mux_isRequested */
+
+/****************************************************************************/
+/**
+* @brief mux_request
+*
+* Request a pin from gpiolib.
+*
+*/
+/****************************************************************************/
+static int mux_request(gpio_defs_e pin, const char *label)
+{
+ return gpio_request(pin, label);
+
+} /* mux_request */
+
+/****************************************************************************/
+/**
+* @brief mux_free
+*
+* Free a pin from gpiolib.
+*
+*/
+/****************************************************************************/
+static void mux_free(gpio_defs_e pin)
+{
+ return gpio_free(pin);
+
+} /* mux_free */
+
+/****************************************************************************
+*
+* brcm_init_gpio
+*
+* Sets up gpiolib so that it's aware of how to manipulate our GPIOs
+*
+*****************************************************************************/
+
+void __init brcm_init_gpio(void)
+{
+ gpiomux_init_t initstruct;
+
+ gpiochip_add(&gpio_bcmring_chip[0].chip);
+ gpiochip_add(&gpio_bcmring_chip[1].chip);
+
+ /* Initialize callbacks for gpiomux function calls into gpiolib */
+ initstruct.request_gpio = mux_request;
+ initstruct.free_gpio = mux_free;
+ initstruct.is_requested = mux_isRequested;
+ gpiomux_Init(&initstruct);
+
+ /*
+ * gpiomux_Init skips pins 14 and 15 (which makes sense when gpiomux_Init
+ * is called from within the bootloaders.
+ *
+ * However, from this point forward in the code, gpiolib "owns" the pins. So
+ * if you want these pins to be UART1, then you should call gpiomux_request
+ * after this so that the pins gets reserved inside gpiolib.
+ */
+
+ chipcHw_setGpioPinFunction(GPIO14_UART1_TXD,
+ chipcHw_GPIO_FUNCTION_GPIO);
+ chipcHw_setGpioPinFunction(GPIO15_UART1_RXD,
+ chipcHw_GPIO_FUNCTION_GPIO);
+
+} /* brcm_init_gpio */
diff --git a/arch/arm/mach-bcmring/gpio_irq.c b/arch/arm/mach-bcmring/gpio_irq.c
new file mode 100644
index 0000000..a9b8892
--- /dev/null
+++ b/arch/arm/mach-bcmring/gpio_irq.c
@@ -0,0 +1,381 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/*
+*
+*****************************************************************************
+*
+* gpio_irq.c
+*
+* PURPOSE:
+*
+* This implements the gpio driver interrupt rising/falling edge driver.
+*
+* NOTES:
+*
+*****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include <mach/gpio_irq.h>
+#include <asm/gpio.h>
+#include <asm/mach/irq.h>
+
+#include <mach/gpio.h>
+#include <mach/gpio_defs.h>
+#include <mach/csp/chipcHw_inline.h>
+#include <csp/gpioHw.h>
+#include <mach/csp/gpioHw_inline.h>
+
+/* ---- Public Variables ------------------------------------------------- */
+
+/* ---- Private Constants and Types -------------------------------------- */
+
+/* Debug logging */
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#define DEBUG 1
+
+#define DBG_ERROR 0x01
+#define DBG_INFO 0x02
+#define DBG_TRACE 0x04
+#define DBG_TRACE2 0x08
+#define DBG_DATA 0x10
+#define DBG_DATA2 0x20
+
+#define DBG_DEFAULT_LEVEL (DBG_ERROR | DBG_INFO)
+
+#if DEBUG
+# define GPIO_DEBUG(level, x) {if (level & gLevel) printk x; }
+#else
+# define GPIO_DEBUG(level, x)
+#endif
+
+static char banner[] __initdata =
+ KERN_INFO "GPIO Control Driver: 1.00 (built on " __DATE__ " " __TIME__
+ ")\n";
+static int gLevel = DBG_DEFAULT_LEVEL;
+
+/* ---- Private Variables ------------------------------------------------ */
+
+/* ---- Functions -------------------------------------------------------- */
+
+/****************************************************************************
+*
+* gpio_irq_ack
+*
+* Called by the interrupt handler to acknowledge (i.e. clear)
+* the interrupt.
+*
+***************************************************************************/
+
+static void gpio_irq_ack(unsigned irq)
+{
+ /*
+ * Since this function is ONLY called with interrupts disabled, we don't
+ * need to disable irqs around the following
+ */
+
+ GpioHw_IrqClear(irq_to_gpio(irq));
+}
+
+/****************************************************************************
+*
+* gpio_irq_mask
+*
+* Called to mask (i.e. disable) an interrupt.
+*
+***************************************************************************/
+
+static void gpio_irq_mask(unsigned irq)
+{
+ /*
+ * Since this function is ONLY called with interrupts disabled, we don't
+ * need to disable irqs around the following
+ */
+
+ GpioHw_IrqDisable(irq_to_gpio(irq));
+}
+
+/****************************************************************************
+*
+* gpio_irq_unmask
+*
+* Called to unmask (i.e. enable) an interrupt.
+*
+***************************************************************************/
+
+static void gpio_irq_unmask(unsigned irq)
+{
+ /*
+ * Since this function is ONLY called with interrupts disabled, we don't
+ * need to disable irqs around the following
+ */
+
+ GpioHw_IrqEnable(irq_to_gpio(irq));
+}
+
+/****************************************************************************
+*
+* gpio_irq_type
+*
+* Sets the type of the GPIO irq.
+*
+***************************************************************************/
+
+static int gpio_irq_set_type(unsigned irq, unsigned type)
+{
+ int gpio;
+
+ gpio = irq_to_gpio(irq);
+
+ /*
+ * Since this function is ONLY called with interrupts disabled, we don't
+ * need to disable irqs around the following
+ */
+
+ if (type == IRQ_TYPE_PROBE) {
+ /* Don't mess GPIOs which already have interrupt handlers registered. */
+
+ if (GpioHw_IsIrqEnabled(gpio) == GPIOHW_IRQ_UNMASKED_INTERRUPT) {
+ return 0;
+ }
+
+ type = IRQ_TYPE_EDGE_BOTH;
+ }
+
+ printk(KERN_INFO "IRQ%d (gpio%d): ", irq, gpio);
+
+ GpioHw_IrqDisable(gpio);
+ GpioHw_SetDirInput(gpio);
+ GpioHw_IrqClear(gpio);
+ GpioHw_IrqEnable(gpio);
+
+ if (type & IRQ_TYPE_EDGE_RISING) {
+ if (type & IRQ_TYPE_EDGE_FALLING) {
+ printk(KERN_INFO "both edges\n");
+ GpioHw_SetIrqType(gpio,
+ GPIOHW_INTERRUPT_TYPE_EDGE_BOTH);
+ } else {
+ printk(KERN_INFO "rising edges\n");
+ GpioHw_SetIrqType(gpio,
+ GPIOHW_INTERRUPT_TYPE_EDGE_RISING);
+ }
+ } else if (type & IRQ_TYPE_EDGE_FALLING) {
+ printk(KERN_INFO "falling edges\n");
+ GpioHw_SetIrqType(gpio, GPIOHW_INTERRUPT_TYPE_EDGE_FALLING);
+ } else if (type & IRQ_TYPE_LEVEL_HIGH) {
+ printk(KERN_INFO "high level\n");
+ GpioHw_SetIrqType(gpio, GPIOHW_INTERRUPT_TYPE_LEVEL_HIGH);
+ } else if (type & IRQ_TYPE_LEVEL_LOW) {
+ printk(KERN_INFO "low level\n");
+ GpioHw_SetIrqType(gpio, GPIOHW_INTERRUPT_TYPE_LEVEL_LOW);
+ } else {
+ printk(KERN_ERR "no edges\n");
+ printk(KERN_ERR "%s: Failed to set type for IRQ%d (gpio%d): ",
+ __func__, irq, gpio);
+ GpioHw_IrqDisable(gpio);
+ return -EINVAL;
+ }
+
+ return 0;
+
+} /* gpio_irq_set_type */
+
+/****************************************************************************
+*
+* gpio_isr_handler0
+*
+* Figures out which GPIO caused the interrupt and calls the register
+* handler to deal with it.
+*
+* The handler function will in all likelyhood be do_edge_IRQ.
+*
+***************************************************************************/
+
+static void gpio_isr_handler0(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned mask;
+ int loop;
+
+ do {
+ loop = 0;
+
+ mask = GpioHw_GetMaskIrqRegStatus(GPIOHW_BLOCK_LOW);
+ if (mask) {
+ /* Clear the interrupts */
+
+ GpioHw_IrqClearReg(GPIOHW_BLOCK_LOW, mask);
+
+ irq = gpio_to_irq(0);
+ desc = irq_desc + irq;
+ do {
+ if (mask & 1) {
+ desc->handle_irq(irq, desc);
+ }
+ irq++;
+ desc++;
+ mask >>= 1;
+
+ } while (mask);
+
+ loop = 1;
+ }
+
+ } while (loop);
+
+} /* gpio_isr_handler0 */
+
+/****************************************************************************
+*
+* gpio_isr_handler1
+*
+* Figures out which GPIO caused the interrupt and calls the register
+* handler to deal with it.
+*
+* The handler function will in all likelyhood be do_edge_IRQ.
+*
+***************************************************************************/
+
+static void gpio_isr_handler1(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned mask;
+ int loop;
+
+ do {
+ loop = 0;
+
+ mask = GpioHw_GetMaskIrqRegStatus(GPIOHW_BLOCK_HIGH);
+ if (mask) {
+ /* Clear the interrupts */
+
+ GpioHw_IrqClearReg(GPIOHW_BLOCK_HIGH, mask);
+
+ irq = gpio_to_irq(32);
+ desc = irq_desc + irq;
+ do {
+ if (mask & 1) {
+ desc->handle_irq(irq, desc);
+ }
+ irq++;
+ desc++;
+ mask >>= 1;
+
+ } while (mask);
+
+ loop = 1;
+ }
+
+ } while (loop);
+
+} /* gpio_isr_handler1 */
+
+/****************************************************************************
+*
+* gpio_chip data structure.
+*
+***************************************************************************/
+
+static struct irq_chip gpio_chip = {
+ .typename = "GPIO-IRQ",
+ .ack = gpio_irq_ack,
+ .mask = gpio_irq_mask,
+ .unmask = gpio_irq_unmask,
+ .disable = gpio_irq_mask,
+ .enable = gpio_irq_unmask,
+ .set_type = gpio_irq_set_type,
+};
+
+/****************************************************************************
+*
+* gpio_cleanup
+*
+* Called to perform module cleanup when the module is unloaded.
+*
+***************************************************************************/
+static void gpio_cleanup(void)
+{
+ GPIO_DEBUG(DBG_TRACE, ("gpio_cleanup()\n"));
+
+}
+
+/****************************************************************************
+*
+* gpio_init
+*
+* Called to perform module initialization when the module is loaded
+*
+***************************************************************************/
+
+static int __init gpio_init(void)
+{
+ /* Initialize debug level */
+ gLevel = DBG_DEFAULT_LEVEL;
+
+ GPIO_DEBUG(DBG_INFO, ("gpio_init called\n"));
+
+ printk(banner);
+ {
+ int irq;
+
+ for (irq = gpio_to_irq(0);
+ irq <= gpio_to_irq(NUM_GPIO_IRQS - 1); irq++) {
+ set_irq_chip(irq, &gpio_chip);
+ set_irq_handler(irq, handle_edge_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+ set_irq_chained_handler(IRQ_GPIO0, gpio_isr_handler0);
+ set_irq_chained_handler(IRQ_GPIO1, gpio_isr_handler1);
+ }
+
+ return 0;
+} /* gpio_init */
+
+/****************************************************************************
+*
+* gpio_exit
+*
+* Called to perform module cleanup when the module is unloaded.
+*
+***************************************************************************/
+
+static void __exit gpio_exit(void)
+{
+ GPIO_DEBUG(DBG_INFO, ("gpio_exit called\n"));
+
+ gpio_cleanup();
+} /* gpio_exit */
+
+/* Changed from module_init to fs_initcall so that GPIO driver
+ * is loaded before the any of the PMU drivers were loaded on
+ * other products - leave it this way for future.
+ */
+
+fs_initcall(gpio_init);
+module_exit(gpio_exit);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("GPIO Control Driver");
--
1.6.0.6
Leo Chen
More information about the linux-arm-kernel
mailing list