commit: http://blackfin.uclinux.org/git/?p=linux-kernel;a=commitdiff;h=2ca9c057472784375a3d1a21c6ca7bb11a3ab8fc branch: http://blackfin.uclinux.org/git/?p=linux-kernel;a=shortlog;h=refs/heads/trunk
The new ADI gpio controller was introduced since the BF548 and BF60x processors. It differs a lot from former one on BF5xx processors. So, split its driver from the older gpio driver in blackfin arch folder. Signed-off-by: Sonic Zhang <[email protected]> --- drivers/gpio/Makefile | 1 + drivers/gpio/gpio-adi2.c | 593 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 594 insertions(+), 0 deletions(-) diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 22e07bc..859b6ee 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o +obj-$(CONFIG_GPIO_ADI2) += gpio-adi2.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o diff --git a/drivers/gpio/gpio-adi2.c b/drivers/gpio/gpio-adi2.c new file mode 100644 index 0000000..de5f1fb --- /dev/null +++ b/drivers/gpio/gpio-adi2.c @@ -0,0 +1,593 @@ +/* + * ADI GPIO Abstraction Layer + * + * Copyright 2007-2013 Analog Devices Inc. + * + * Licensed under the GPL-2 or later + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/gpio.h> +#include <linux/irq.h> + +static struct gpio_port_t * const gpio_array[] = { + (struct gpio_port_t *)PORTA_FER, + (struct gpio_port_t *)PORTB_FER, + (struct gpio_port_t *)PORTC_FER, + (struct gpio_port_t *)PORTD_FER, + (struct gpio_port_t *)PORTE_FER, + (struct gpio_port_t *)PORTF_FER, + (struct gpio_port_t *)PORTG_FER, +#if defined(CONFIG_BF54x) + (struct gpio_port_t *)PORTH_FER, + (struct gpio_port_t *)PORTI_FER, + (struct gpio_port_t *)PORTJ_FER, +#endif +}; + +#define RESOURCE_LABEL_SIZE 16 + +static struct str_ident { + char name[RESOURCE_LABEL_SIZE]; +} str_ident[MAX_RESOURCES]; + +#if defined(CONFIG_PM) +static struct gpio_port_s gpio_bank_saved[GPIO_BANK_NUM]; +#endif + +static void gpio_error(unsigned gpio) +{ + printk(KERN_ERR "gpio-adi2: GPIO %d wasn't requested!\n", gpio); +} + +static void set_label(unsigned short ident, const char *label) +{ + if (label) { + strncpy(str_ident[ident].name, label, + RESOURCE_LABEL_SIZE); + str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0; + } +} + +static char *get_label(unsigned short ident) +{ + return (*str_ident[ident].name ? str_ident[ident].name : "UNKNOWN"); +} + +static int cmp_label(unsigned short ident, const char *label) +{ + if (label == NULL) { + dump_stack(); + printk(KERN_ERR "Please provide none-null label\n"); + } + + if (label) + return strcmp(str_ident[ident].name, label); + else + return -EINVAL; +} + +#define map_entry(m, i) reserved_##m##_map[gpio_bank(i)] +#define is_reserved(m, i, e) (map_entry(m, i) & gpio_bit(i)) +#define reserve(m, i) (map_entry(m, i) |= gpio_bit(i)) +#define unreserve(m, i) (map_entry(m, i) &= ~gpio_bit(i)) +#define DECLARE_RESERVED_MAP(m, c) static unsigned short reserved_##m##_map[c] + +DECLARE_RESERVED_MAP(gpio, GPIO_BANK_NUM); +DECLARE_RESERVED_MAP(peri, DIV_ROUND_UP(MAX_RESOURCES, GPIO_BANKSIZE)); +DECLARE_RESERVED_MAP(gpio_irq, GPIO_BANK_NUM); + +static inline int check_gpio(unsigned gpio) +{ +#if defined(CONFIG_BF54x) + if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15 + || gpio == GPIO_PH14 || gpio == GPIO_PH15 + || gpio == GPIO_PJ14 || gpio == GPIO_PJ15) + return -EINVAL; +#endif + if (gpio >= MAX_BLACKFIN_GPIOS) + return -EINVAL; + return 0; +} + +static void port_setup(unsigned gpio, unsigned short usage) +{ + if (check_gpio(gpio)) + return; + + if (usage == GPIO_USAGE) + gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio); + SSYNC(); +} + +static inline void portmux_setup(unsigned short per) +{ + u16 ident = P_IDENT(per); + u16 function = P_FUNCT2MUX(per); + u32 pmux; + + pmux = gpio_array[gpio_bank(ident)]->port_mux; + + pmux &= ~(0x3 << (2 * gpio_sub_n(ident))); + pmux |= (function & 0x3) << (2 * gpio_sub_n(ident)); + + gpio_array[gpio_bank(ident)]->port_mux = pmux; +} + +static inline u16 get_portmux(unsigned short per) +{ + u16 ident = P_IDENT(per); + u32 pmux = gpio_array[gpio_bank(ident)]->port_mux; + return (pmux >> (2 * gpio_sub_n(ident)) & 0x3); +} + +#ifdef CONFIG_PM + +int adi_gpio_pm_standby_ctrl(unsigned ctrl) +{ + return 0; +} + +void adi_gpio_pm_hibernate_suspend(void) +{ + int i, bank; + + for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) { + bank = gpio_bank(i); + + gpio_bank_saved[bank].fer = gpio_array[bank]->port_fer; + gpio_bank_saved[bank].mux = gpio_array[bank]->port_mux; + gpio_bank_saved[bank].data = ""> + gpio_bank_saved[bank].inen = gpio_array[bank]->inen; + gpio_bank_saved[bank].dir = gpio_array[bank]->dir_set; + } +} + +void adi_gpio_pm_hibernate_restore(void) +{ + int i, bank; + + for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) { + bank = gpio_bank(i); + + gpio_array[bank]->port_mux = gpio_bank_saved[bank].mux; + gpio_array[bank]->port_fer = gpio_bank_saved[bank].fer; + gpio_array[bank]->inen = gpio_bank_saved[bank].inen; + gpio_array[bank]->data_set = gpio_bank_saved[bank].data + & gpio_bank_saved[bank].dir; + gpio_array[bank]->dir_set = gpio_bank_saved[bank].dir; + } +} +#endif + +static unsigned short get_gpio_dir(unsigned gpio) +{ + return 0x01 & + (gpio_array[gpio_bank(gpio)]->dir_clear >> gpio_sub_n(gpio)); +} + +/*********************************************************** +* +* FUNCTIONS: ADI Processor Peripheral Resource Allocation +* and PortMux Setup +* +* INPUTS/OUTPUTS: +* per Peripheral Identifier +* label String +* +* DESCRIPTION: ADI Processor Peripheral Resource Allocation and Setup API +**************************************************************/ + +int peripheral_request(unsigned short per, const char *label) +{ + unsigned long flags; + unsigned short ident = P_IDENT(per); + + /* + * Don't cares are pins with only one dedicated function + */ + + if (per & P_DONTCARE) + return 0; + + if (!(per & P_DEFINED)) + return -ENODEV; + + BUG_ON(ident >= MAX_RESOURCES); + + flags = hard_local_irq_save(); + + /* If a pin can be muxed as either GPIO or peripheral, make + * sure it is not already a GPIO pin when we request it. + */ + if (unlikely(!check_gpio(ident) && is_reserved(gpio, ident, 1))) { + if (system_state == SYSTEM_BOOTING) + dump_stack(); + printk(KERN_ERR + "%s: Peripheral %d is already reserved as GPIO by %s !\n", + __func__, ident, get_label(ident)); + hard_local_irq_restore(flags); + return -EBUSY; + } + + if (unlikely(is_reserved(peri, ident, 1))) { + + /* + * Pin functions like AMC address strobes my + * be requested and used by several drivers + */ + + if (!((per & P_MAYSHARE) && get_portmux(per) == P_FUNCT2MUX(per))) { + /* + * Allow that the identical pin function can + * be requested from the same driver twice + */ + + if (cmp_label(ident, label) == 0) + goto anyway; + + if (system_state == SYSTEM_BOOTING) + dump_stack(); + printk(KERN_ERR + "%s: Peripheral %d function %d is already reserved by %s !\n", + __func__, ident, P_FUNCT2MUX(per), get_label(ident)); + hard_local_irq_restore(flags); + return -EBUSY; + } + } + + anyway: + reserve(peri, ident); + + portmux_setup(per); + port_setup(ident, PERIPHERAL_USAGE); + + hard_local_irq_restore(flags); + set_label(ident, label); + + return 0; +} +EXPORT_SYMBOL(peripheral_request); + +int peripheral_request_list(const unsigned short per[], const char *label) +{ + u16 cnt; + int ret; + + for (cnt = 0; per[cnt] != 0; cnt++) { + + ret = peripheral_request(per[cnt], label); + + if (ret < 0) { + for ( ; cnt > 0; cnt--) + peripheral_free(per[cnt - 1]); + + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL(peripheral_request_list); + +void peripheral_free(unsigned short per) +{ + unsigned long flags; + unsigned short ident = P_IDENT(per); + + if (per & P_DONTCARE) + return; + + if (!(per & P_DEFINED)) + return; + + flags = hard_local_irq_save(); + + if (unlikely(!is_reserved(peri, ident, 0))) { + hard_local_irq_restore(flags); + return; + } + + if (!(per & P_MAYSHARE)) + port_setup(ident, GPIO_USAGE); + + unreserve(peri, ident); + + set_label(ident, "free"); + + hard_local_irq_restore(flags); +} +EXPORT_SYMBOL(peripheral_free); + +void peripheral_free_list(const unsigned short per[]) +{ + u16 cnt; + for (cnt = 0; per[cnt] != 0; cnt++) + peripheral_free(per[cnt]); +} +EXPORT_SYMBOL(peripheral_free_list); + +/*********************************************************** +* +* FUNCTIONS: ADI Processor GPIO Driver +* +* INPUTS/OUTPUTS: +* gpio PIO Number between 0 and MAX_GPIOS +* label String +* +* DESCRIPTION: ADI Processor GPIO Driver API +**************************************************************/ + +static int adi_gpio_request(struct gpio_chip *chip, unsigned gpio) +{ + unsigned long flags; + + if (check_gpio(gpio) < 0) + return -EINVAL; + + flags = hard_local_irq_save(); + + /* + * Allow that the identical GPIO can + * be requested from the same driver twice + * Do nothing and return - + */ + + if (cmp_label(gpio, chip->label) == 0) { + hard_local_irq_restore(flags); + return 0; + } + + if (unlikely(is_reserved(gpio, gpio, 1))) { + if (system_state == SYSTEM_BOOTING) + dump_stack(); + printk(KERN_ERR "gpio-adi2: GPIO %d is already reserved by %s !\n", + gpio, get_label(gpio)); + hard_local_irq_restore(flags); + return -EBUSY; + } + if (unlikely(is_reserved(peri, gpio, 1))) { + if (system_state == SYSTEM_BOOTING) + dump_stack(); + printk(KERN_ERR + "gpio-adi2: GPIO %d is already reserved as Peripheral by %s !\n", + gpio, get_label(gpio)); + hard_local_irq_restore(flags); + return -EBUSY; + } + if (unlikely(is_reserved(gpio_irq, gpio, 1))) { + printk(KERN_NOTICE "gpio-adi2: GPIO %d is already reserved as gpio-irq!" + " (Documentation/blackfin/bfin-gpio-notes.txt)\n", gpio); + } + + reserve(gpio, gpio); + set_label(gpio, chip->label); + + hard_local_irq_restore(flags); + + port_setup(gpio, GPIO_USAGE); + + return 0; +} + +static void adi_gpio_free(struct gpio_chip *chip, unsigned gpio) +{ + unsigned long flags; + + if (check_gpio(gpio) < 0) + return; + + might_sleep(); + + flags = hard_local_irq_save(); + + if (unlikely(!is_reserved(gpio, gpio, 0))) { + if (system_state == SYSTEM_BOOTING) + dump_stack(); + gpio_error(gpio); + hard_local_irq_restore(flags); + return; + } + + unreserve(gpio, gpio); + + set_label(gpio, "free"); + + hard_local_irq_restore(flags); +} + +int adi_gpio_irq_request(unsigned gpio, const char *label) +{ + unsigned long flags; + + if (check_gpio(gpio) < 0) + return -EINVAL; + + flags = hard_local_irq_save(); + + if (unlikely(is_reserved(peri, gpio, 1))) { + if (system_state == SYSTEM_BOOTING) + dump_stack(); + printk(KERN_ERR + "gpio-adi2: GPIO %d is already reserved as Peripheral by %s !\n", + gpio, get_label(gpio)); + hard_local_irq_restore(flags); + return -EBUSY; + } + if (unlikely(is_reserved(gpio, gpio, 1))) + printk(KERN_NOTICE "gpio-adi2: GPIO %d is already reserved by %s! " + "(Documentation/blackfin/bfin-gpio-notes.txt)\n", + gpio, get_label(gpio)); + + reserve(gpio_irq, gpio); + set_label(gpio, label); + + hard_local_irq_restore(flags); + + port_setup(gpio, GPIO_USAGE); + + return 0; +} + +void adi_gpio_irq_free(unsigned gpio) +{ + unsigned long flags; + + if (check_gpio(gpio) < 0) + return; + + flags = hard_local_irq_save(); + + if (unlikely(!is_reserved(gpio_irq, gpio, 0))) { + if (system_state == SYSTEM_BOOTING) + dump_stack(); + gpio_error(gpio); + hard_local_irq_restore(flags); + return; + } + + unreserve(gpio_irq, gpio); + + set_label(gpio, "free"); + + hard_local_irq_restore(flags); +} + +static inline void __adi_gpio_direction_input(unsigned gpio) +{ + gpio_array[gpio_bank(gpio)]->dir_clear = gpio_bit(gpio); + gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio); +} + +static int adi_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + unsigned long flags; + + if (unlikely(!is_reserved(gpio, gpio, 0))) { + gpio_error(gpio); + return -EINVAL; + } + + flags = hard_local_irq_save(); + __adi_gpio_direction_input(gpio); + hard_local_irq_restore(flags); + + return 0; +} + +void adi_gpio_irq_prepare(unsigned gpio) +{ + unsigned long flags; + + port_setup(gpio, GPIO_USAGE); + + flags = hard_local_irq_save(); + __adi_gpio_direction_input(gpio); + hard_local_irq_restore(flags); +} + +static void adi_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int arg) +{ + if (arg) + gpio_array[gpio_bank(gpio)]->data_set = gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->data_clear = gpio_bit(gpio); +} + +static int adi_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) +{ + unsigned long flags; + + if (unlikely(!is_reserved(gpio, gpio, 0))) { + gpio_error(gpio); + return -EINVAL; + } + + flags = hard_local_irq_save(); + + gpio_array[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); + gpio_set_value(gpio, value); + gpio_array[gpio_bank(gpio)]->dir_set = gpio_bit(gpio); + + hard_local_irq_restore(flags); + + return 0; +} + +static int adi_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + return (1 & (gpio_array[gpio_bank(gpio)]->data >> gpio_sub_n(gpio))); +} + +static int adi_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) +{ + return gpio + GPIO_IRQ_BASE; +} + +#if defined(CONFIG_PROC_FS) +static int gpio_proc_show(struct seq_file *m, void *v) +{ + int c, irq, gpio; + + for (c = 0; c < MAX_RESOURCES; c++) { + irq = is_reserved(gpio_irq, c, 1); + gpio = is_reserved(gpio, c, 1); + if (!check_gpio(c) && (gpio || irq)) + seq_printf(m, "GPIO_%d: \t%s%s \t\tGPIO %s\n", c, + get_label(c), (gpio && irq) ? " *" : "", + get_gpio_dir(c) ? "OUTPUT" : "INPUT"); + else if (is_reserved(peri, c, 1)) + seq_printf(m, "GPIO_%d: \t%s \t\tPeripheral\n", c, get_label(c)); + else + continue; + } + + return 0; +} + +static int gpio_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, gpio_proc_show, NULL); +} + +static const struct file_operations gpio_proc_ops = { + .open = gpio_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static __init int gpio_register_proc(void) +{ + struct proc_dir_entry *proc_gpio; + + proc_gpio = proc_create("gpio", 0, NULL, &gpio_proc_ops); + return proc_gpio == NULL; +} +__initcall(gpio_register_proc); +#endif + +static struct gpio_chip gpio_adi2_chip = { + .label = "gpio-adi2", + .direction_input = adi_gpio_direction_input, + .get = adi_gpio_get_value, + .direction_output = adi_gpio_direction_output, + .set = adi_gpio_set_value, + .request = adi_gpio_request, + .free = adi_gpio_free, + .to_irq = adi_gpio_to_irq, + .base = 0, + .ngpio = MAX_GPIOS, +}; + +static int __init gpiolib_setup(void) +{ + return gpiochip_add(&gpio_adi2_chip); +} +arch_initcall(gpiolib_setup);
_______________________________________________ Linux-kernel-commits mailing list [email protected] https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits
