diff options
Diffstat (limited to 'drivers/gpio/adi_gpio2.c')
| -rw-r--r-- | drivers/gpio/adi_gpio2.c | 440 | 
1 files changed, 440 insertions, 0 deletions
| diff --git a/drivers/gpio/adi_gpio2.c b/drivers/gpio/adi_gpio2.c new file mode 100644 index 000000000..7a034eba1 --- /dev/null +++ b/drivers/gpio/adi_gpio2.c @@ -0,0 +1,440 @@ +/* + * ADI GPIO2 Abstraction Layer + * Support BF54x, BF60x and future processors. + * + * Copyright 2008-2013 Analog Devices Inc. + * + * Licensed under the GPL-2 or later + */ + +#include <common.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/portmux.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]; + +static void gpio_error(unsigned gpio) +{ +	printf("adi_gpio2: 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) +		printf("adi_gpio2: 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) unsigned short reserved_##m##_map[c] + +static DECLARE_RESERVED_MAP(gpio, GPIO_BANK_NUM); +static DECLARE_RESERVED_MAP(peri, gpio_bank(MAX_RESOURCES)); + +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_GPIOS) +		return -EINVAL; +	return 0; +} + +static void port_setup(unsigned gpio, unsigned short usage) +{ +#if defined(CONFIG_BF54x) +	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); +#else +	if (usage == GPIO_USAGE) +		gpio_array[gpio_bank(gpio)]->port_fer_clear = gpio_bit(gpio); +	else +		gpio_array[gpio_bank(gpio)]->port_fer_set = gpio_bit(gpio); +#endif +	SSYNC(); +} + +inline void portmux_setup(unsigned short per) +{ +	u32 pmux; +	u16 ident = P_IDENT(per); +	u16 function = P_FUNCT2MUX(per); + +	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; +} + +inline u16 get_portmux(unsigned short per) +{ +	u32 pmux; +	u16 ident = P_IDENT(per); + +	pmux = gpio_array[gpio_bank(ident)]->port_mux; + +	return pmux >> (2 * gpio_sub_n(ident)) & 0x3; +} + +unsigned short get_gpio_dir(unsigned gpio) +{ +	return 0x01 & +		(gpio_array[gpio_bank(gpio)]->dir_clear >> gpio_sub_n(gpio)); +} + +/*********************************************************** +* +* FUNCTIONS:	Peripheral Resource Allocation +*		and PortMux Setup +* +* INPUTS/OUTPUTS: +* per	Peripheral Identifier +* label	String +* +* DESCRIPTION: Peripheral Resource Allocation and Setup API +**************************************************************/ + +int peripheral_request(unsigned short per, const char *label) +{ +	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); + +	/* 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))) { +		printf("%s: Peripheral %d is already reserved as GPIO by %s!\n", +		       __func__, ident, get_label(ident)); +		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; + +			printf("%s: Peripheral %d function %d is already " +				"reserved by %s!\n", __func__, ident, +				P_FUNCT2MUX(per), get_label(ident)); +			return -EBUSY; +		} +	} + + anyway: +	reserve(peri, ident); + +	portmux_setup(per); +	port_setup(ident, PERIPHERAL_USAGE); + +	set_label(ident, label); + +	return 0; +} + +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; +} + +void peripheral_free(unsigned short per) +{ +	unsigned short ident = P_IDENT(per); + +	if (per & P_DONTCARE) +		return; + +	if (!(per & P_DEFINED)) +		return; + +	if (unlikely(!is_reserved(peri, ident, 0))) +		return; + +	if (!(per & P_MAYSHARE)) +		port_setup(ident, GPIO_USAGE); + +	unreserve(peri, ident); + +	set_label(ident, "free"); +} + +void peripheral_free_list(const unsigned short per[]) +{ +	u16 cnt; +	for (cnt = 0; per[cnt] != 0; cnt++) +		peripheral_free(per[cnt]); +} + +/*********************************************************** +* +* FUNCTIONS: GPIO Driver +* +* INPUTS/OUTPUTS: +* gpio	PIO Number between 0 and MAX_GPIOS +* label	String +* +* DESCRIPTION: GPIO Driver API +**************************************************************/ + +int gpio_request(unsigned gpio, const char *label) +{ +	if (check_gpio(gpio) < 0) +		return -EINVAL; + +	/* +	 * Allow that the identical GPIO can +	 * be requested from the same driver twice +	 * Do nothing and return - +	 */ + +	if (cmp_label(gpio, label) == 0) +		return 0; + +	if (unlikely(is_reserved(gpio, gpio, 1))) { +		printf("adi_gpio2: GPIO %d is already reserved by %s!\n", +			gpio, get_label(gpio)); +		return -EBUSY; +	} +	if (unlikely(is_reserved(peri, gpio, 1))) { +		printf("adi_gpio2: GPIO %d is already reserved as Peripheral " +			"by %s!\n", gpio, get_label(gpio)); +		return -EBUSY; +	} + +	reserve(gpio, gpio); +	set_label(gpio, label); + +	port_setup(gpio, GPIO_USAGE); + +	return 0; +} + +int gpio_free(unsigned gpio) +{ +	if (check_gpio(gpio) < 0) +		return -1; + +	if (unlikely(!is_reserved(gpio, gpio, 0))) { +		gpio_error(gpio); +		return -1; +	} + +	unreserve(gpio, gpio); + +	set_label(gpio, "free"); + +	return 0; +} + +#ifdef ADI_SPECIAL_GPIO_BANKS +static DECLARE_RESERVED_MAP(special_gpio, gpio_bank(MAX_RESOURCES)); + +int special_gpio_request(unsigned gpio, const char *label) +{ +	/* +	 * Allow that the identical GPIO can +	 * be requested from the same driver twice +	 * Do nothing and return - +	 */ + +	if (cmp_label(gpio, label) == 0) +		return 0; + +	if (unlikely(is_reserved(special_gpio, gpio, 1))) { +		printf("adi_gpio2: GPIO %d is already reserved by %s!\n", +			gpio, get_label(gpio)); +		return -EBUSY; +	} +	if (unlikely(is_reserved(peri, gpio, 1))) { +		printf("adi_gpio2: GPIO %d is already reserved as Peripheral " +			"by %s!\n", gpio, get_label(gpio)); + +		return -EBUSY; +	} + +	reserve(special_gpio, gpio); +	reserve(peri, gpio); + +	set_label(gpio, label); +	port_setup(gpio, GPIO_USAGE); + +	return 0; +} + +void special_gpio_free(unsigned gpio) +{ +	if (unlikely(!is_reserved(special_gpio, gpio, 0))) { +		gpio_error(gpio); +		return; +	} + +	reserve(special_gpio, gpio); +	reserve(peri, gpio); +	set_label(gpio, "free"); +} +#endif + +static inline void __gpio_direction_input(unsigned gpio) +{ +	gpio_array[gpio_bank(gpio)]->dir_clear = gpio_bit(gpio); +#if defined(CONFIG_BF54x) +	gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio); +#else +	gpio_array[gpio_bank(gpio)]->inen_set = gpio_bit(gpio); +#endif +} + +int gpio_direction_input(unsigned gpio) +{ +	unsigned long flags; + +	if (!is_reserved(gpio, gpio, 0)) { +		gpio_error(gpio); +		return -EINVAL; +	} + +	local_irq_save(flags); +	__gpio_direction_input(gpio); +	local_irq_restore(flags); + +	return 0; +} + +int gpio_set_value(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); + +	return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ +	unsigned long flags; + +	if (!is_reserved(gpio, gpio, 0)) { +		gpio_error(gpio); +		return -EINVAL; +	} + +	local_irq_save(flags); + +#if defined(CONFIG_BF54x) +	gpio_array[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); +#else +	gpio_array[gpio_bank(gpio)]->inen_clear = gpio_bit(gpio); +#endif +	gpio_set_value(gpio, value); +	gpio_array[gpio_bank(gpio)]->dir_set = gpio_bit(gpio); + +	local_irq_restore(flags); + +	return 0; +} + +int gpio_get_value(unsigned gpio) +{ +	return 1 & (gpio_array[gpio_bank(gpio)]->data >> gpio_sub_n(gpio)); +} + +void gpio_labels(void) +{ +	int c, gpio; + +	for (c = 0; c < MAX_RESOURCES; c++) { +		gpio = is_reserved(gpio, c, 1); +		if (!check_gpio(c) && gpio) +			printf("GPIO_%d:\t%s\tGPIO %s\n", c, get_label(c), +				get_gpio_dir(c) ? "OUTPUT" : "INPUT"); +		else if (is_reserved(peri, c, 1)) +			printf("GPIO_%d:\t%s\tPeripheral\n", c, get_label(c)); +		else +			continue; +	} +} |