mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-20 08:58:52 +09:00
222 lines
6.1 KiB
C
222 lines
6.1 KiB
C
/*
|
|
* 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 configuration
|
|
*
|
|
* 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]);
|
|
}
|