diff options
Diffstat (limited to 'drivers/gpio/db8500_gpio.c')
| -rw-r--r-- | drivers/gpio/db8500_gpio.c | 221 | 
1 files changed, 221 insertions, 0 deletions
| diff --git a/drivers/gpio/db8500_gpio.c b/drivers/gpio/db8500_gpio.c new file mode 100644 index 000000000..d5cb383e8 --- /dev/null +++ b/drivers/gpio/db8500_gpio.c @@ -0,0 +1,221 @@ +/* + * Code ported from Nomadik GPIO driver in ST-Ericsson Linux kernel code. + * The purpose is that GPIO config found in kernel should work by simply + * copy-paste it to U-boot. + * + * Original Linux authors: + * Copyright (C) 2008,2009 STMicroelectronics + * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it> + *   Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com> + * + * Ported to U-boot by: + * Copyright (C) 2010 Joakim Axelsson <joakim.axelsson AT stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/io.h> + +#include <asm/arch/db8500_gpio.h> +#include <asm/arch/db8500_pincfg.h> +#include <linux/compiler.h> + +#define IO_ADDR(x) (void *) (x) + +/* + * The GPIO module in the db8500 Systems-on-Chip is an + * AMBA device, managing 32 pins and alternate functions. The logic block + * is currently only used in the db8500. + */ + +#define GPIO_TOTAL_PINS		268 +#define GPIO_PINS_PER_BLOCK	32 +#define GPIO_BLOCKS_COUNT	(GPIO_TOTAL_PINS/GPIO_PINS_PER_BLOCK + 1) +#define GPIO_BLOCK(pin)		(((pin + GPIO_PINS_PER_BLOCK) >> 5) - 1) +#define GPIO_PIN_WITHIN_BLOCK(pin)	((pin)%(GPIO_PINS_PER_BLOCK)) + +/* Register in the logic block */ +#define DB8500_GPIO_DAT		0x00 +#define DB8500_GPIO_DATS	0x04 +#define DB8500_GPIO_DATC	0x08 +#define DB8500_GPIO_PDIS	0x0c +#define DB8500_GPIO_DIR		0x10 +#define DB8500_GPIO_DIRS	0x14 +#define DB8500_GPIO_DIRC	0x18 +#define DB8500_GPIO_SLPC	0x1c +#define DB8500_GPIO_AFSLA	0x20 +#define DB8500_GPIO_AFSLB	0x24 + +#define DB8500_GPIO_RIMSC	0x40 +#define DB8500_GPIO_FIMSC	0x44 +#define DB8500_GPIO_IS		0x48 +#define DB8500_GPIO_IC		0x4c +#define DB8500_GPIO_RWIMSC	0x50 +#define DB8500_GPIO_FWIMSC	0x54 +#define DB8500_GPIO_WKS		0x58 + +static void __iomem *get_gpio_addr(unsigned gpio) +{ +	/* Our list of GPIO chips */ +	static void __iomem *gpio_addrs[GPIO_BLOCKS_COUNT] = { +		IO_ADDR(CFG_GPIO_0_BASE), +		IO_ADDR(CFG_GPIO_1_BASE), +		IO_ADDR(CFG_GPIO_2_BASE), +		IO_ADDR(CFG_GPIO_3_BASE), +		IO_ADDR(CFG_GPIO_4_BASE), +		IO_ADDR(CFG_GPIO_5_BASE), +		IO_ADDR(CFG_GPIO_6_BASE), +		IO_ADDR(CFG_GPIO_7_BASE), +		IO_ADDR(CFG_GPIO_8_BASE) +	}; + +	return gpio_addrs[GPIO_BLOCK(gpio)]; +} + +static unsigned get_gpio_offset(unsigned gpio) +{ +	return GPIO_PIN_WITHIN_BLOCK(gpio); +} + +/* Can only be called from config_pin. Don't configure alt-mode directly */ +static void gpio_set_mode(unsigned gpio, enum db8500_gpio_alt mode) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); +	u32 bit = 1 << offset; +	u32 afunc, bfunc; + +	afunc = readl(addr + DB8500_GPIO_AFSLA) & ~bit; +	bfunc = readl(addr + DB8500_GPIO_AFSLB) & ~bit; +	if (mode & DB8500_GPIO_ALT_A) +		afunc |= bit; +	if (mode & DB8500_GPIO_ALT_B) +		bfunc |= bit; +	writel(afunc, addr + DB8500_GPIO_AFSLA); +	writel(bfunc, addr + DB8500_GPIO_AFSLB); +} + +/** + * db8500_gpio_set_pull() - enable/disable pull up/down on a gpio + * @gpio: pin number + * @pull: one of DB8500_GPIO_PULL_DOWN, DB8500_GPIO_PULL_UP, + *  and DB8500_GPIO_PULL_NONE + * + * Enables/disables pull up/down on a specified pin.  This only takes effect if + * the pin is configured as an input (either explicitly or by the alternate + * function). + * + * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is + * configured as an input.  Otherwise, due to the way the controller registers + * work, this function will change the value output on the pin. + */ +void db8500_gpio_set_pull(unsigned gpio, enum db8500_gpio_pull pull) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); +	u32 bit = 1 << offset; +	u32 pdis; + +	pdis = readl(addr + DB8500_GPIO_PDIS); +	if (pull == DB8500_GPIO_PULL_NONE) +		pdis |= bit; +	else +		pdis &= ~bit; +	writel(pdis, addr + DB8500_GPIO_PDIS); + +	if (pull == DB8500_GPIO_PULL_UP) +		writel(bit, addr + DB8500_GPIO_DATS); +	else if (pull == DB8500_GPIO_PULL_DOWN) +		writel(bit, addr + DB8500_GPIO_DATC); +} + +void db8500_gpio_make_input(unsigned gpio) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); + +	writel(1 << offset, addr + DB8500_GPIO_DIRC); +} + +int db8500_gpio_get_input(unsigned gpio) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); +	u32 bit = 1 << offset; + +	printf("db8500_gpio_get_input gpio=%u addr=%p offset=%u bit=%#x\n", +		gpio, addr, offset, bit); + +	return (readl(addr + DB8500_GPIO_DAT) & bit) != 0; +} + +void db8500_gpio_make_output(unsigned gpio, int val) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); + +	writel(1 << offset, addr + DB8500_GPIO_DIRS); +	db8500_gpio_set_output(gpio, val); +} + +void db8500_gpio_set_output(unsigned gpio, int val) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); + +	if (val) +		writel(1 << offset, addr + DB8500_GPIO_DATS); +	else +		writel(1 << offset, addr + DB8500_GPIO_DATC); +} + +/** + * config_pin - configure a pin's mux attributes + * @cfg: pin confguration + * + * Configures a pin's mode (alternate function or GPIO), its pull up status, + * and its sleep mode based on the specified configuration.  The @cfg is + * usually one of the SoC specific macros defined in mach/<soc>-pins.h.  These + * are constructed using, and can be further enhanced with, the macros in + * plat/pincfg.h. + * + * If a pin's mode is set to GPIO, it is configured as an input to avoid + * side-effects.  The gpio can be manipulated later using standard GPIO API + * calls. + */ +static void config_pin(unsigned long cfg) +{ +	int pin = PIN_NUM(cfg); +	int pull = PIN_PULL(cfg); +	int af = PIN_ALT(cfg); +	int output = PIN_DIR(cfg); +	int val = PIN_VAL(cfg); + +	if (output) +		db8500_gpio_make_output(pin, val); +	else { +		db8500_gpio_make_input(pin); +		db8500_gpio_set_pull(pin, pull); +	} + +	gpio_set_mode(pin, af); +} + +/** + * db8500_config_pins - configure several pins at once + * @cfgs: array of pin configurations + * @num: number of elments in the array + * + * Configures several pins using config_pin(). Refer to that function for + * further information. + */ +void db8500_gpio_config_pins(unsigned long *cfgs, size_t num) +{ +	size_t i; + +	for (i = 0; i < num; i++) +		config_pin(cfgs[i]); +} |