/* * 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 * Rewritten based on work by Prafulla WADASKAR * * Ported to U-Boot by: * Copyright (C) 2010 Joakim Axelsson * * 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 #include #include #include #include #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/-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]); }