aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubi32-gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ubicom32/files/arch/ubicom32/mach-common/ubi32-gpio.c')
-rw-r--r--target/linux/ubicom32/files/arch/ubicom32/mach-common/ubi32-gpio.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubi32-gpio.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubi32-gpio.c
new file mode 100644
index 000000000..5fa22f52b
--- /dev/null
+++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubi32-gpio.c
@@ -0,0 +1,411 @@
+/*
+ * arch/ubicom32/mach-common/ubi32-gpio.c
+ * Ubicom gpio driver
+ *
+ * (C) Copyright 2009, Ubicom, Inc.
+ *
+ * This file is part of the Ubicom32 Linux Kernel Port.
+ *
+ * The Ubicom32 Linux Kernel Port is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Ubicom32 Linux Kernel Port is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the Ubicom32 Linux Kernel Port. If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Ubicom32 implementation derived from (with many thanks):
+ * arch/m68knommu
+ * arch/blackfin
+ * arch/parisc
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/version.h>
+
+#if defined(CONFIG_PROC_FS)
+#include <linux/proc_fs.h>
+#endif
+
+#include <linux/io.h>
+#include <asm/ip5000.h>
+#include <linux/gpio.h>
+
+#define UBI_GPIO_CHECK_RANGE 0 /* !0 enables range checking */
+
+
+/*
+ * Each I/O port can be configured to operate in one of several
+ * functional modes. One of these modes is GPIO, which causes the
+ * entire port to function as a GPIO port. Since the various port
+ * registers serve the system with other important functions, such as
+ * ethernet, serial, USB, etc., it isn't advantageous to set any of
+ * the ports to be entirely dedicated for GPIO use. The processor
+ * alternatively allows individual bits of a port to be assigned to be
+ * used as GPIO independently from the overall port function. This
+ * bit-by-bit assignment is selected by setting the corresponding bit
+ * in the port's gpio_mask register. When set, the selected bit is
+ * then enabled as a GPIO. If the corresponding bit is set in the
+ * gpio_ctl register of the port, the bit is configured as a GPIO
+ * output. Otherwise, it is an input.
+ *
+ * NOTE: This driver uses the bit-by-bit GPIO function assignment
+ * exclusively and *never* sets the port function registers to the
+ * GPIO function.
+ *
+ * GPIO is not the main function of any of the I/O ports. The port
+ * bit widths are variable from one port to the next, determined by
+ * the more common I/O functions of the ports. For simplicity, this
+ * driver assumes all the ports are 32 bits wide regardless of the
+ * real bit width of the port. GPIO bits are numbered from zero to
+ * MAX_UBICOM_GPIOS. Within a port, the least significant bit is
+ * numbered bit zero, the most significant is bit 31. Since the ports
+ * are considered logically contiguous, GPIO #32 is the zeroth bit in
+ * port #1, and so on. Due to the hardware definition, there are
+ * large gaps in the GPIO numbers representing real pins.
+ *
+ * NOTE: It is up to the programmer to refer to the processor data
+ * sheet to determine which bits in which ports can be accessed and
+ * used for GPIO.
+ *
+ */
+
+
+/* There are 9 ports, A through I. Not all 32 bits in each
+ * port can be a GPIO, but we pretend they are. Its up to the
+ * programmer to refer to the processor data sheet.
+ */
+#define MAX_UBICOM_GPIOS (9 * 32) /* ARCH_NR_GPIOS */
+#define NUM_GPIO_PORTS (gpio_bank(MAX_UBICOM_GPIOS))
+
+
+/* GPIO reservation bit map array */
+static int reserved_gpio_map[NUM_GPIO_PORTS];
+
+
+/* Array of hardware io_port addresses */
+static struct ubicom32_io_port *gpio_bank_addr[NUM_GPIO_PORTS] =
+{
+ UBICOM32_IO_PORT(RA),
+ UBICOM32_IO_PORT(RB),
+ UBICOM32_IO_PORT(RC),
+ UBICOM32_IO_PORT(RD),
+ UBICOM32_IO_PORT(RE),
+ UBICOM32_IO_PORT(RF),
+ UBICOM32_IO_PORT(RG),
+ UBICOM32_IO_PORT(RH),
+ UBICOM32_IO_PORT(RI)
+};
+
+
+struct ubi_gpio_chip {
+ /*
+ * Right now, nothing else lives here.
+ */
+ struct gpio_chip gpio_chip;
+};
+
+
+#if UBI_GPIO_CHECK_RANGE
+inline int check_gpio(unsigned gpio)
+{
+ if (gpio >= MAX_UBICOM_GPIOS)
+ return -EINVAL;
+ return 0;
+}
+#else
+#define check_gpio(n) (0)
+#endif
+
+/*
+ * ubi_gpio_get_port
+ * Get the IO port associated with a certain gpio
+ */
+struct ubicom32_io_port *ubi_gpio_get_port(unsigned gpio)
+{
+ if (gpio_bank(gpio) > NUM_GPIO_PORTS) {
+ return NULL;
+ }
+ return gpio_bank_addr[gpio_bank(gpio)];
+}
+
+/*
+ * ubi_gpio_error()
+ */
+static void ubi_gpio_error(unsigned gpio)
+{
+ printk(KERN_ERR "ubicom-gpio: GPIO %d wasn't requested!\n", gpio);
+}
+
+/*
+ * ubi_port_setup()
+ */
+static void ubi_port_setup(unsigned gpio, unsigned short usage)
+{
+ if (!check_gpio(gpio)) {
+ if (usage) {
+ UBICOM32_GPIO_ENABLE(gpio);
+ } else {
+ UBICOM32_GPIO_DISABLE(gpio);
+ }
+ }
+}
+
+/*
+ * ubi_gpio_request()
+ */
+static int ubi_gpio_request(struct gpio_chip *chip, unsigned gpio)
+{
+ unsigned long flags;
+
+ if (check_gpio(gpio) < 0)
+ return -EINVAL;
+
+ local_irq_save(flags);
+
+ if (unlikely(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
+ printk(KERN_ERR "ubi-gpio: GPIO %d is already reserved!\n",
+ gpio);
+ local_irq_restore(flags);
+ return -EBUSY;
+ }
+
+ reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio);
+
+ ubi_port_setup(gpio, 1);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+/*
+ * ubi_gpio_free()
+ */
+static void ubi_gpio_free(struct gpio_chip *chip, unsigned gpio)
+{
+ unsigned long flags;
+
+ if (check_gpio(gpio) < 0)
+ return;
+
+ local_irq_save(flags);
+
+ if (unlikely(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)))) {
+ ubi_gpio_error(gpio);
+ local_irq_restore(flags);
+ return;
+ }
+
+ /* Assert the pin is no longer claimed */
+ reserved_gpio_map[gpio_bank(gpio)] &= ~gpio_bit(gpio);
+
+ /* Revert port bit to use specified by port->function */
+ ubi_port_setup(gpio, 0);
+
+ local_irq_restore(flags);
+}
+
+/*
+ * ubi_gpio_direction_input()
+ */
+static int ubi_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ unsigned long flags;
+
+ if (!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
+ ubi_gpio_error(gpio);
+ return -EINVAL;
+ }
+
+ local_irq_save(flags);
+
+ /* Configure pin as gpio */
+ ubi_port_setup(gpio, 1);
+
+ /* Assert pin is an input */
+ UBICOM32_GPIO_SET_PIN_INPUT(gpio);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+
+/*
+ * ubi_gpio_direction_output()
+ */
+static int ubi_gpio_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ unsigned long flags;
+
+ if (!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
+ ubi_gpio_error(gpio);
+ return -EINVAL;
+ }
+
+ local_irq_save(flags);
+
+ /* Configure pin as gpio and set initial value in gpio_out register
+ * so that when we enable it as an output, it will have the correct
+ * initial value.
+ */
+ ubi_port_setup(gpio, 1);
+ if (value) {
+ UBICOM32_GPIO_SET_PIN_HIGH(gpio);
+ } else {
+ UBICOM32_GPIO_SET_PIN_LOW(gpio);
+ }
+
+ /* Enable the pin as an output */
+ UBICOM32_GPIO_SET_PIN_OUTPUT(gpio);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+
+/*
+ * ubi_gpio_get_value()
+ */
+static int ubi_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ return 0 != (gpio_bank_addr[gpio_bank(gpio)]->gpio_in & gpio_bit(gpio));
+}
+
+
+/*
+ * ubi_gpio_set_value()
+ */
+static void ubi_gpio_set_value(struct gpio_chip *chip, unsigned gpio,
+ int arg)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+
+ if (arg) {
+ UBICOM32_GPIO_SET_PIN_HIGH(gpio);
+ } else {
+ UBICOM32_GPIO_SET_PIN_LOW(gpio);
+ }
+
+ local_irq_restore(flags);
+}
+
+
+/*
+ * ubi_gpio_to_irq()
+ */
+static int ubi_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ return gpio_to_irq(gpio);
+}
+
+
+/*
+ * ubi_gpio_init()
+ */
+int __init ubi_gpio_init(void)
+{
+ int k;
+ int status;
+ struct ubi_gpio_chip *chip;
+ struct gpio_chip *gc;
+
+ printk(KERN_INFO "Ubicom GPIO Controller\n");
+
+ chip = kzalloc(sizeof(struct ubi_gpio_chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ gc = &chip->gpio_chip;
+ gc->request = ubi_gpio_request;
+ gc->free = ubi_gpio_free;
+ gc->to_irq = ubi_gpio_to_irq;
+ gc->direction_input = ubi_gpio_direction_input;
+ gc->direction_output = ubi_gpio_direction_output;
+ gc->get = ubi_gpio_get_value;
+ gc->set = ubi_gpio_set_value;
+ gc->can_sleep = 0;
+ gc->base = 0;
+ gc->ngpio = MAX_UBICOM_GPIOS; /* ARCH_NR_GPIOS - 1 */
+ gc->label = "ubi_gpio";
+
+ status = gpiochip_add(gc);
+ if (status != 0) {
+ kfree(chip);
+ return status;
+ }
+
+ /* Assert all pins are free */
+ for (k = 0; k < NUM_GPIO_PORTS; k++) {
+ reserved_gpio_map[k] = 0;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_PROC_FS)
+/*
+ * ubi_get_gpio_dir()
+ */
+static int ubi_get_gpio_dir(unsigned gpio)
+{
+ if (gpio_bank_addr[gpio_bank(gpio)]->gpio_ctl & gpio_bit(gpio))
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * gpio_proc_read()
+ */
+static int ubi_gpio_proc_read(char *buf, char **start, off_t offset,
+ int len, int *unused_i, void *unused_v)
+{
+ int c, outlen = 0;
+
+ for (c = 0; c < MAX_UBICOM_GPIOS; c++) {
+ if (!check_gpio(c) &&
+ (reserved_gpio_map[gpio_bank(c)] & gpio_bit(c))) {
+ len = sprintf(buf, "GPIO_%d:\t\tGPIO %s\n", c,
+ ubi_get_gpio_dir(c) ? "OUTPUT" : "INPUT");
+ } else {
+ continue;
+ }
+
+ buf += len;
+ outlen += len;
+ }
+ return outlen;
+}
+
+/*
+ * ubi_gpio_register_proc()
+ */
+static __init int ubi_gpio_register_proc(void)
+{
+ struct proc_dir_entry *proc_gpio;
+
+ proc_gpio = create_proc_entry("gpio", S_IRUGO, NULL);
+ if (proc_gpio)
+ proc_gpio->read_proc = ubi_gpio_proc_read;
+
+ return proc_gpio != NULL;
+}
+device_initcall(ubi_gpio_register_proc);
+#endif