blob: d5cb383e85e1b08aa3d4f8ca65a9daa85c4119d7 [file] [log] [blame] [edit]
/*
* 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]);
}