/* arch/arm/mach-msm/generic_gpio.c * * Copyright (C) 2007 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program 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. * */ #include #include #include #include #include #include #include "gpio_chip.h" #define GPIO_NUM_TO_CHIP_INDEX(gpio) ((gpio)>>5) struct gpio_state { unsigned long flags; int refcount; }; static DEFINE_SPINLOCK(gpio_chips_lock); static LIST_HEAD(gpio_chip_list); static struct gpio_chip **gpio_chip_array; static unsigned long gpio_chip_array_size; int register_gpio_chip(struct gpio_chip *new_gpio_chip) { int err = 0; struct gpio_chip *gpio_chip; int i; unsigned long irq_flags; unsigned int chip_array_start_index, chip_array_end_index; new_gpio_chip->state = kzalloc((new_gpio_chip->end + 1 - new_gpio_chip->start) * sizeof(new_gpio_chip->state[0]), GFP_KERNEL); if (new_gpio_chip->state == NULL) { printk(KERN_ERR "register_gpio_chip: failed to allocate state\n"); return -ENOMEM; } spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip_array_start_index = GPIO_NUM_TO_CHIP_INDEX(new_gpio_chip->start); chip_array_end_index = GPIO_NUM_TO_CHIP_INDEX(new_gpio_chip->end); if (chip_array_end_index >= gpio_chip_array_size) { struct gpio_chip **new_gpio_chip_array; unsigned long new_gpio_chip_array_size = chip_array_end_index + 1; new_gpio_chip_array = kmalloc(new_gpio_chip_array_size * sizeof(new_gpio_chip_array[0]), GFP_ATOMIC); if (new_gpio_chip_array == NULL) { printk(KERN_ERR "register_gpio_chip: failed to allocate array\n"); err = -ENOMEM; goto failed; } for (i = 0; i < gpio_chip_array_size; i++) new_gpio_chip_array[i] = gpio_chip_array[i]; for (i = gpio_chip_array_size; i < new_gpio_chip_array_size; i++) new_gpio_chip_array[i] = NULL; gpio_chip_array = new_gpio_chip_array; gpio_chip_array_size = new_gpio_chip_array_size; } list_for_each_entry(gpio_chip, &gpio_chip_list, list) { if (gpio_chip->start > new_gpio_chip->end) { list_add_tail(&new_gpio_chip->list, &gpio_chip->list); goto added; } if (gpio_chip->end >= new_gpio_chip->start) { printk(KERN_ERR "register_gpio_source %u-%u overlaps with %u-%u\n", new_gpio_chip->start, new_gpio_chip->end, gpio_chip->start, gpio_chip->end); err = -EBUSY; goto failed; } } list_add_tail(&new_gpio_chip->list, &gpio_chip_list); added: for (i = chip_array_start_index; i <= chip_array_end_index; i++) { if (gpio_chip_array[i] == NULL || gpio_chip_array[i]->start > new_gpio_chip->start) gpio_chip_array[i] = new_gpio_chip; } failed: spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); if (err) kfree(new_gpio_chip->state); return err; } static struct gpio_chip *get_gpio_chip_locked(unsigned int gpio) { unsigned long i; struct gpio_chip *chip; i = GPIO_NUM_TO_CHIP_INDEX(gpio); if (i >= gpio_chip_array_size) return NULL; chip = gpio_chip_array[i]; if (chip == NULL) return NULL; list_for_each_entry_from(chip, &gpio_chip_list, list) { if (gpio < chip->start) return NULL; if (gpio <= chip->end) return chip; } return NULL; } static int request_gpio(unsigned int gpio, unsigned long flags) { int err = 0; struct gpio_chip *chip; unsigned long irq_flags; unsigned long chip_index; spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip = get_gpio_chip_locked(gpio); if (chip == NULL) { err = -EINVAL; goto err; } chip_index = gpio - chip->start; if (chip->state[chip_index].refcount == 0) { chip->configure(chip, gpio, flags); chip->state[chip_index].flags = flags; chip->state[chip_index].refcount++; } else if ((flags & IRQF_SHARED) && (chip->state[chip_index].flags & IRQF_SHARED)) chip->state[chip_index].refcount++; else err = -EBUSY; err: spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); return err; } int gpio_request(unsigned gpio, const char *label) { return request_gpio(gpio, 0); } EXPORT_SYMBOL(gpio_request); void gpio_free(unsigned gpio) { struct gpio_chip *chip; unsigned long irq_flags; unsigned long chip_index; spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip = get_gpio_chip_locked(gpio); if (chip) { chip_index = gpio - chip->start; chip->state[chip_index].refcount--; } spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); } EXPORT_SYMBOL(gpio_free); static int gpio_get_irq_num(unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp) { int ret = -ENOTSUPP; struct gpio_chip *chip; unsigned long irq_flags; spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip = get_gpio_chip_locked(gpio); if (chip && chip->get_irq_num) ret = chip->get_irq_num(chip, gpio, irqp, irqnumflagsp); spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); return ret; } int gpio_to_irq(unsigned gpio) { int ret, irq; ret = gpio_get_irq_num(gpio, &irq, NULL); if (ret) return ret; return irq; } EXPORT_SYMBOL(gpio_to_irq); int gpio_configure(unsigned int gpio, unsigned long flags) { int ret = -ENOTSUPP; struct gpio_chip *chip; unsigned long irq_flags; spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip = get_gpio_chip_locked(gpio); if (chip) ret = chip->configure(chip, gpio, flags); spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); return ret; } EXPORT_SYMBOL(gpio_configure); int gpio_direction_input(unsigned gpio) { return gpio_configure(gpio, GPIOF_INPUT); } EXPORT_SYMBOL(gpio_direction_input); int gpio_direction_output(unsigned gpio, int value) { gpio_set_value(gpio, value); return gpio_configure(gpio, GPIOF_DRIVE_OUTPUT); } EXPORT_SYMBOL(gpio_direction_output); int gpio_get_value(unsigned gpio) { int ret = -ENOTSUPP; struct gpio_chip *chip; unsigned long irq_flags; spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip = get_gpio_chip_locked(gpio); if (chip && chip->read) ret = chip->read(chip, gpio); spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); return ret; } EXPORT_SYMBOL(gpio_get_value); void gpio_set_value(unsigned gpio, int on) { int ret = -ENOTSUPP; struct gpio_chip *chip; unsigned long irq_flags; spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip = get_gpio_chip_locked(gpio); if (chip && chip->write) ret = chip->write(chip, gpio, on); spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); } EXPORT_SYMBOL(gpio_set_value); int gpio_read_detect_status(unsigned int gpio) { int ret = -ENOTSUPP; struct gpio_chip *chip; unsigned long irq_flags; spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip = get_gpio_chip_locked(gpio); if (chip && chip->read_detect_status) ret = chip->read_detect_status(chip, gpio); spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); return ret; } EXPORT_SYMBOL(gpio_read_detect_status); int gpio_clear_detect_status(unsigned int gpio) { int ret = -ENOTSUPP; struct gpio_chip *chip; unsigned long irq_flags; spin_lock_irqsave(&gpio_chips_lock, irq_flags); chip = get_gpio_chip_locked(gpio); if (chip && chip->clear_detect_status) ret = chip->clear_detect_status(chip, gpio); spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); return ret; } EXPORT_SYMBOL(gpio_clear_detect_status);