Logo Search packages:      
Sourcecode: linux version File versions  Download package

gpio.c

/*
 *
 * arch/arm/mach-u300/gpio.c
 *
 *
 * Copyright (C) 2007-2009 ST-Ericsson AB
 * License terms: GNU General Public License (GPL) version 2
 * U300 GPIO module.
 * This can driver either of the two basic GPIO cores
 * available in the U300 platforms:
 * COH 901 335   - Used in DB3150 (U300 1.0) and DB3200 (U330 1.0)
 * COH 901 571/3 - Used in DB3210 (U365 2.0) and DB3350 (U335 1.0)
 * Notice that you also have inline macros in <asm-arch/gpio.h>
 * Author: Linus Walleij <linus.walleij@stericsson.com>
 * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
 *
 */
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>

/* Reference to GPIO block clock */
static struct clk *clk;

/* Memory resource */
static struct resource *memres;
static void __iomem *virtbase;
static struct device *gpiodev;

00036 struct u300_gpio_port {
      const char *name;
      int irq;
      int number;
};


static struct u300_gpio_port gpio_ports[] = {
      {
            .name = "gpio0",
            .number = 0,
      },
      {
            .name = "gpio1",
            .number = 1,
      },
      {
            .name = "gpio2",
            .number = 2,
      },
#ifdef U300_COH901571_3
      {
            .name = "gpio3",
            .number = 3,
      },
      {
            .name = "gpio4",
            .number = 4,
      },
#ifdef CONFIG_MACH_U300_BS335
      {
            .name = "gpio5",
            .number = 5,
      },
      {
            .name = "gpio6",
            .number = 6,
      },
#endif
#endif

};


#ifdef U300_COH901571_3

/* Default input value */
#define DEFAULT_OUTPUT_LOW   0
#define DEFAULT_OUTPUT_HIGH  1

/* GPIO Pull-Up status */
#define DISABLE_PULL_UP  0
#define ENABLE_PULL_UP  1

#define GPIO_NOT_USED 0
#define GPIO_IN       1
#define GPIO_OUT      2

struct u300_gpio_configuration_data {
      unsigned char pin_usage;
      unsigned char default_output_value;
      unsigned char pull_up;
};

/* Initial configuration */
const struct u300_gpio_configuration_data
u300_gpio_config[U300_GPIO_NUM_PORTS][U300_GPIO_PINS_PER_PORT] = {
#ifdef CONFIG_MACH_U300_BS335
      /* Port 0, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_HIGH,  DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP}
      },
      /* Port 1, pins 0-7 */
      {
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_HIGH,  DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP}
      },
      /* Port 2, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP}
      },
      /* Port 3, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP}
      },
      /* Port 4, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP}
      },
      /* Port 5, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP}
      },
      /* Port 6, pind 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP}
      }
#endif

#ifdef CONFIG_MACH_U300_BS365
      /* Port 0, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP}
      },
      /* Port 1, pins 0-7 */
      {
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_HIGH,  DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP}
      },
      /* Port 2, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,   DISABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP}
      },
      /* Port 3, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP}
      },
      /* Port 4, pins 0-7 */
      {
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_IN,  DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            /* These 4 pins doesn't exist on DB3210 */
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP},
            {GPIO_OUT, DEFAULT_OUTPUT_LOW,    ENABLE_PULL_UP}
      }
#endif
};
#endif


/* No users == we can power down GPIO */
static int gpio_users;

00248 struct gpio_struct {
      int (*callback)(void *);
      void *data;
      int users;
};

static struct gpio_struct gpio_pin[U300_GPIO_MAX];

/*
 * Let drivers register callback in order to get notified when there is
 * an interrupt on the gpio pin
 */
int gpio_register_callback(unsigned gpio, int (*func)(void *arg), void *data)
{
      if (gpio_pin[gpio].callback)
            dev_warn(gpiodev, "%s: WARNING: callback already "
                   "registered for gpio pin#%d\n", __func__, gpio);
      gpio_pin[gpio].callback = func;
      gpio_pin[gpio].data = data;

      return 0;
}
EXPORT_SYMBOL(gpio_register_callback);

int gpio_unregister_callback(unsigned gpio)
{
      if (!gpio_pin[gpio].callback)
            dev_warn(gpiodev, "%s: WARNING: callback already "
                   "unregistered for gpio pin#%d\n", __func__, gpio);
      gpio_pin[gpio].callback = NULL;
      gpio_pin[gpio].data = NULL;

      return 0;
}
EXPORT_SYMBOL(gpio_unregister_callback);

/* Non-zero means valid */
int gpio_is_valid(int number)
{
      if (number >= 0 &&
          number < (U300_GPIO_NUM_PORTS * U300_GPIO_PINS_PER_PORT))
            return 1;
      return 0;
}
EXPORT_SYMBOL(gpio_is_valid);

int gpio_request(unsigned gpio, const char *label)
{
      if (gpio_pin[gpio].users)
            return -EINVAL;
      else
            gpio_pin[gpio].users++;

      gpio_users++;

      return 0;
}
EXPORT_SYMBOL(gpio_request);

void gpio_free(unsigned gpio)
{
      gpio_users--;
      gpio_pin[gpio].users--;
      if (unlikely(gpio_pin[gpio].users < 0)) {
            dev_warn(gpiodev, "warning: gpio#%d release mismatch\n",
                   gpio);
            gpio_pin[gpio].users = 0;
      }

      return;
}
EXPORT_SYMBOL(gpio_free);

/* This returns zero or nonzero */
int gpio_get_value(unsigned gpio)
{
      return readl(virtbase + U300_GPIO_PXPDIR +
        PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING) & (1 << (gpio & 0x07));
}
EXPORT_SYMBOL(gpio_get_value);

/*
 * We hope that the compiler will optimize away the unused branch
 * in case "value" is a constant
 */
void gpio_set_value(unsigned gpio, int value)
{
      u32 val;
      unsigned long flags;

      local_irq_save(flags);
      if (value) {
            /* set */
            val = readl(virtbase + U300_GPIO_PXPDOR +
              PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING)
              & (1 << (gpio & 0x07));
            writel(val | (1 << (gpio & 0x07)), virtbase +
              U300_GPIO_PXPDOR +
              PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING);
      } else {
            /* clear */
            val = readl(virtbase + U300_GPIO_PXPDOR +
              PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING)
              & (1 << (gpio & 0x07));
            writel(val & ~(1 << (gpio & 0x07)), virtbase +
              U300_GPIO_PXPDOR +
              PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING);
      }
      local_irq_restore(flags);
}
EXPORT_SYMBOL(gpio_set_value);

int gpio_direction_input(unsigned gpio)
{
      unsigned long flags;
      u32 val;

      if (gpio > U300_GPIO_MAX)
            return -EINVAL;

      local_irq_save(flags);
      val = readl(virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      /* Mask out this pin*/
      val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((gpio & 0x07) << 1));
      /* This is not needed since it sets the bits to zero.*/
      /* val |= (U300_GPIO_PXPCR_PIN_MODE_INPUT << (gpio*2)); */
      writel(val, virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      local_irq_restore(flags);
      return 0;
}
EXPORT_SYMBOL(gpio_direction_input);

int gpio_direction_output(unsigned gpio, int value)
{
      unsigned long flags;
      u32 val;

      if (gpio > U300_GPIO_MAX)
            return -EINVAL;

      local_irq_save(flags);
      val = readl(virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      /* Mask out this pin */
      val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << ((gpio & 0x07) << 1));
      /*
       * FIXME: configure for push/pull, open drain or open source per pin
       * in setup. The current driver will only support push/pull.
       */
      val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL
                  << ((gpio & 0x07) << 1));
      writel(val, virtbase + U300_GPIO_PXPCR + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      gpio_set_value(gpio, value);
      local_irq_restore(flags);
      return 0;
}
EXPORT_SYMBOL(gpio_direction_output);

/*
 * Enable an IRQ, edge is rising edge (!= 0) or falling edge (==0).
 */
void enable_irq_on_gpio_pin(unsigned gpio, int edge)
{
      u32 val;
      unsigned long flags;
      local_irq_save(flags);

      val = readl(virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      val |= (1 << (gpio & 0x07));
      writel(val, virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      val = readl(virtbase + U300_GPIO_PXICR + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      if (edge)
            val |= (1 << (gpio & 0x07));
      else
            val &= ~(1 << (gpio & 0x07));
      writel(val, virtbase + U300_GPIO_PXICR + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      local_irq_restore(flags);
}
EXPORT_SYMBOL(enable_irq_on_gpio_pin);

void disable_irq_on_gpio_pin(unsigned gpio)
{
      u32 val;
      unsigned long flags;

      local_irq_save(flags);
      val = readl(virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      val &= ~(1 << (gpio & 0x07));
      writel(val, virtbase + U300_GPIO_PXIEN + PIN_TO_PORT(gpio) *
                        U300_GPIO_PORTX_SPACING);
      local_irq_restore(flags);
}
EXPORT_SYMBOL(disable_irq_on_gpio_pin);

/* Enable (value == 0) or disable (value == 1) internal pullup */
void gpio_pullup(unsigned gpio, int value)
{
      u32 val;
      unsigned long flags;

      local_irq_save(flags);
      if (value) {
            val = readl(virtbase + U300_GPIO_PXPER + PIN_TO_PORT(gpio) *
                              U300_GPIO_PORTX_SPACING);
            writel(val | (1 << (gpio & 0x07)), virtbase + U300_GPIO_PXPER +
                        PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING);
      } else {
            val = readl(virtbase + U300_GPIO_PXPER + PIN_TO_PORT(gpio) *
                              U300_GPIO_PORTX_SPACING);
            writel(val & ~(1 << (gpio & 0x07)), virtbase + U300_GPIO_PXPER +
                        PIN_TO_PORT(gpio) * U300_GPIO_PORTX_SPACING);
      }
      local_irq_restore(flags);
}
EXPORT_SYMBOL(gpio_pullup);

static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
      struct u300_gpio_port *port = dev_id;
      u32 val;
      int pin;

      /* Read event register */
      val = readl(virtbase + U300_GPIO_PXIEV + port->number *
                        U300_GPIO_PORTX_SPACING);
      /* Mask with enable register */
      val &= readl(virtbase + U300_GPIO_PXIEV + port->number *
                        U300_GPIO_PORTX_SPACING);
      /* Mask relevant bits */
      val &= U300_GPIO_PXIEV_ALL_IRQ_EVENT_MASK;
      /* ACK IRQ (clear event) */
      writel(val, virtbase + U300_GPIO_PXIEV + port->number *
                        U300_GPIO_PORTX_SPACING);
      /* Print message */
      while (val != 0) {
            unsigned gpio;

            pin = __ffs(val);
            /* mask off this pin */
            val &= ~(1 << pin);
            gpio = (port->number << 3) + pin;

            if (gpio_pin[gpio].callback)
                  (void)gpio_pin[gpio].callback(gpio_pin[gpio].data);
            else
                  dev_dbg(gpiodev, "stray GPIO IRQ on line %d\n",
                         gpio);
      }
      return IRQ_HANDLED;
}

static void gpio_set_initial_values(void)
{
#ifdef U300_COH901571_3
      int i, j;
      unsigned long flags;
      u32 val;

      /* Write default values to all pins */
      for (i = 0; i < U300_GPIO_NUM_PORTS; i++) {
            val = 0;
            for (j = 0; j < 8; j++)
                  val |= (u32) (u300_gpio_config[i][j].default_output_value != DEFAULT_OUTPUT_LOW) << j;
            local_irq_save(flags);
            writel(val, virtbase + U300_GPIO_PXPDOR + i * U300_GPIO_PORTX_SPACING);
            local_irq_restore(flags);
      }

      /*
       * Put all pins that are set to either 'GPIO_OUT' or 'GPIO_NOT_USED'
       * to output and 'GPIO_IN' to input for each port. And initalize
       * default value on outputs.
       */
      for (i = 0; i < U300_GPIO_NUM_PORTS; i++) {
            for (j = 0; j < U300_GPIO_PINS_PER_PORT; j++) {
                  local_irq_save(flags);
                  val = readl(virtbase + U300_GPIO_PXPCR +
                               i * U300_GPIO_PORTX_SPACING);
                  /* Mask out this pin */
                  val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK << (j << 1));

                  if (u300_gpio_config[i][j].pin_usage != GPIO_IN)
                        val |= (U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL << (j << 1));
                  writel(val, virtbase + U300_GPIO_PXPCR +
                               i * U300_GPIO_PORTX_SPACING);
                  local_irq_restore(flags);
            }
      }

      /* Enable or disable the internal pull-ups in the GPIO ASIC block */
      for (i = 0; i < U300_GPIO_MAX; i++) {
            val = 0;
            for (j = 0; j < 8; j++)
                  val |= (u32)((u300_gpio_config[i][j].pull_up == DISABLE_PULL_UP)) << j;
            local_irq_save(flags);
            writel(val, virtbase + U300_GPIO_PXPER + i * U300_GPIO_PORTX_SPACING);
            local_irq_restore(flags);
      }
#endif
}

static int __init gpio_probe(struct platform_device *pdev)
{
      u32 val;
      int err = 0;
      int i;
      int num_irqs;

      gpiodev = &pdev->dev;
      memset(gpio_pin, 0, sizeof(gpio_pin));

      /* Get GPIO clock */
      clk = clk_get(&pdev->dev, NULL);
      if (IS_ERR(clk)) {
            err = PTR_ERR(clk);
            dev_err(gpiodev, "could not get GPIO clock\n");
            goto err_no_clk;
      }
      err = clk_enable(clk);
      if (err) {
            dev_err(gpiodev, "could not enable GPIO clock\n");
            goto err_no_clk_enable;
      }

      memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
      if (!memres)
            goto err_no_resource;

      if (request_mem_region(memres->start, memres->end - memres->start, "GPIO Controller")
          == NULL) {
            err = -ENODEV;
            goto err_no_ioregion;
      }

      virtbase = ioremap(memres->start, resource_size(memres));
      if (!virtbase) {
            err = -ENOMEM;
            goto err_no_ioremap;
      }
      dev_info(gpiodev, "remapped 0x%08x to %p\n",
             memres->start, virtbase);

#ifdef U300_COH901335
      dev_info(gpiodev, "initializing GPIO Controller COH 901 335\n");
      /* Turn on the GPIO block */
      writel(U300_GPIO_CR_BLOCK_CLOCK_ENABLE, virtbase + U300_GPIO_CR);
#endif

#ifdef U300_COH901571_3
      dev_info(gpiodev, "initializing GPIO Controller COH 901 571/3\n");
      val = readl(virtbase + U300_GPIO_CR);
      dev_info(gpiodev, "COH901571/3 block version: %d, " \
             "number of cores: %d\n",
             ((val & 0x0000FE00) >> 9),
             ((val & 0x000001FC) >> 2));
      writel(U300_GPIO_CR_BLOCK_CLKRQ_ENABLE, virtbase + U300_GPIO_CR);
#endif

      gpio_set_initial_values();

      for (num_irqs = 0 ; num_irqs < U300_GPIO_NUM_PORTS; num_irqs++) {

            gpio_ports[num_irqs].irq =
                  platform_get_irq_byname(pdev,
                                    gpio_ports[num_irqs].name);

            err = request_irq(gpio_ports[num_irqs].irq,
                          gpio_irq_handler, IRQF_DISABLED,
                          gpio_ports[num_irqs].name,
                          &gpio_ports[num_irqs]);
            if (err) {
                  dev_err(gpiodev, "cannot allocate IRQ for %s!\n",
                        gpio_ports[num_irqs].name);
                  goto err_no_irq;
            }
            /* Turns off PortX_irq_force */
            writel(0x0, virtbase + U300_GPIO_PXIFR +
                         num_irqs * U300_GPIO_PORTX_SPACING);
      }

      return 0;

 err_no_irq:
      for (i = 0; i < num_irqs; i++)
            free_irq(gpio_ports[i].irq, &gpio_ports[i]);
      iounmap(virtbase);
 err_no_ioremap:
      release_mem_region(memres->start, memres->end - memres->start);
 err_no_ioregion:
 err_no_resource:
      clk_disable(clk);
 err_no_clk_enable:
      clk_put(clk);
 err_no_clk:
      dev_info(gpiodev, "module ERROR:%d\n", err);
      return err;
}

static int __exit gpio_remove(struct platform_device *pdev)
{
      int i;

      /* Turn off the GPIO block */
      writel(0x00000000U, virtbase + U300_GPIO_CR);
      for (i = 0 ; i < U300_GPIO_NUM_PORTS; i++)
            free_irq(gpio_ports[i].irq, &gpio_ports[i]);
      iounmap(virtbase);
      release_mem_region(memres->start, memres->end - memres->start);
      clk_disable(clk);
      clk_put(clk);
      return 0;
}

static struct platform_driver gpio_driver = {
      .driver           = {
            .name = "u300-gpio",
      },
      .remove           = __exit_p(gpio_remove),
};


static int __init u300_gpio_init(void)
{
      return platform_driver_probe(&gpio_driver, gpio_probe);
}

static void __exit u300_gpio_exit(void)
{
      platform_driver_unregister(&gpio_driver);
}

arch_initcall(u300_gpio_init);
module_exit(u300_gpio_exit);

MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");

#ifdef U300_COH901571_3
MODULE_DESCRIPTION("ST-Ericsson AB COH 901 571/3 GPIO driver");
#endif

#ifdef U300_COH901335
MODULE_DESCRIPTION("ST-Ericsson AB COH 901 335 GPIO driver");
#endif

MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index