snow: use bootblock build class for I2C code
This gets rid of a bunch of duplicate I2C code in the bootblock. Change-Id: I51f625a0f738cca4ed2453fbcb78092e4110bc7e Signed-off-by: David Hendricks <dhendrix@chromium.org> Reviewed-on: http://review.coreboot.org/2289 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
This commit is contained in:
parent
00e480e22d
commit
94e230aa93
|
@ -1,4 +1,5 @@
|
||||||
bootblock-y += pwm.c
|
bootblock-y += pwm.c
|
||||||
|
bootblock-y += s3c24x0_i2c.c
|
||||||
bootblock-y += s5p_gpio.c
|
bootblock-y += s5p_gpio.c
|
||||||
bootblock-y += timer.c
|
bootblock-y += timer.c
|
||||||
|
|
||||||
|
|
|
@ -68,76 +68,7 @@ static void do_serial(void)
|
||||||
uart_init();
|
uart_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define I2C_WRITE 0
|
|
||||||
#define I2C_READ 1
|
|
||||||
|
|
||||||
#define I2C_OK 0
|
|
||||||
#define I2C_NOK 1
|
|
||||||
#define I2C_NACK 2
|
|
||||||
#define I2C_NOK_LA 3 /* Lost arbitration */
|
|
||||||
#define I2C_NOK_TOUT 4 /* time out */
|
|
||||||
|
|
||||||
#define I2CSTAT_BSY 0x20 /* Busy bit */
|
|
||||||
#define I2CSTAT_NACK 0x01 /* Nack bit */
|
|
||||||
#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */
|
|
||||||
#define I2CCON_IRPND 0x10 /* Interrupt pending bit */
|
|
||||||
#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */
|
|
||||||
#define I2C_MODE_MR 0x80 /* Master Receive Mode */
|
|
||||||
#define I2C_START_STOP 0x20 /* START / STOP */
|
|
||||||
#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
|
|
||||||
|
|
||||||
|
|
||||||
/* The timeouts we live by */
|
|
||||||
enum {
|
|
||||||
I2C_XFER_TIMEOUT_MS = 35, /* xfer to complete */
|
|
||||||
I2C_INIT_TIMEOUT_MS = 1000, /* bus free on init */
|
|
||||||
I2C_IDLE_TIMEOUT_MS = 100, /* waiting for bus idle */
|
|
||||||
I2C_STOP_TIMEOUT_US = 200, /* waiting for stop events */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define I2C0_BASE 0x12c60000
|
#define I2C0_BASE 0x12c60000
|
||||||
struct s3c24x0_i2c_bus i2c0 = {
|
|
||||||
.node = 0,
|
|
||||||
.bus_num = 0,
|
|
||||||
.regs = (struct s3c24x0_i2c *)I2C0_BASE,
|
|
||||||
.id = PERIPH_ID_I2C0,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd)
|
|
||||||
{
|
|
||||||
unsigned long freq, pres = 16, div;
|
|
||||||
|
|
||||||
freq = clock_get_periph_rate(PERIPH_ID_I2C0);
|
|
||||||
/* calculate prescaler and divisor values */
|
|
||||||
if ((freq / pres / (16 + 1)) > speed)
|
|
||||||
/* set prescaler to 512 */
|
|
||||||
pres = 512;
|
|
||||||
|
|
||||||
div = 0;
|
|
||||||
|
|
||||||
while ((freq / pres / (div + 1)) > speed)
|
|
||||||
div++;
|
|
||||||
|
|
||||||
/* set prescaler, divisor according to freq, also set ACKGEN, IRQ */
|
|
||||||
writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
|
|
||||||
|
|
||||||
/* init to SLAVE REVEIVE and set slaveaddr */
|
|
||||||
writel(0, &i2c->iicstat);
|
|
||||||
writel(slaveadd, &i2c->iicadd);
|
|
||||||
/* program Master Transmit (and implicit STOP) */
|
|
||||||
writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void i2c_bus_init(struct s3c24x0_i2c_bus *i2c, unsigned int bus)
|
|
||||||
{
|
|
||||||
// exynos_pinmux_config(i2c->id, 0);
|
|
||||||
gpio_cfg_pin(GPIO_B30, EXYNOS_GPIO_FUNC(0x2));
|
|
||||||
gpio_cfg_pin(GPIO_B31, EXYNOS_GPIO_FUNC(0x2));
|
|
||||||
gpio_set_pull(GPIO_B30, EXYNOS_GPIO_PULL_NONE);
|
|
||||||
gpio_set_pull(GPIO_B31, EXYNOS_GPIO_PULL_NONE);
|
|
||||||
|
|
||||||
i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_barriers(void);
|
void do_barriers(void);
|
||||||
void do_barriers(void)
|
void do_barriers(void)
|
||||||
|
@ -170,338 +101,6 @@ void my_udelay(unsigned int n)
|
||||||
"bne 1b":"=r" (n):"0"(n));
|
"bne 1b":"=r" (n):"0"(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
void i2c_init(int speed, int slaveadd)
|
|
||||||
{
|
|
||||||
struct s3c24x0_i2c_bus *i2c = &i2c0;
|
|
||||||
struct exynos5_gpio_part1 *gpio;
|
|
||||||
int i;
|
|
||||||
uint32_t x;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* By default i2c channel 0 is the current bus */
|
|
||||||
g_current_bus = 0;
|
|
||||||
|
|
||||||
i2c = get_bus(g_current_bus);
|
|
||||||
if (!i2c)
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
i2c_bus_init(i2c, 0);
|
|
||||||
|
|
||||||
/* wait for some time to give previous transfer a chance to finish */
|
|
||||||
i = I2C_INIT_TIMEOUT_MS * 20;
|
|
||||||
while ((readl(&i2c->regs->iicstat) & I2CSTAT_BSY) && (i > 0)) {
|
|
||||||
my_udelay(50);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpio = (struct exynos5_gpio_part1 *)(EXYNOS5_GPIO_PART1_BASE);
|
|
||||||
/* FIXME(dhendrix): cannot use nested macro (compilation failure) */
|
|
||||||
// writel((readl(&gpio->b3.con) & ~0x00FF) | 0x0022, &gpio->b3.con);
|
|
||||||
x = readl(&gpio->b3.con);
|
|
||||||
writel((x & ~0x00FF) | 0x0022, &gpio->b3.con);
|
|
||||||
|
|
||||||
i2c_ch_init(i2c->regs, speed, slaveadd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int WaitForXfer(struct s3c24x0_i2c *i2c)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
i = I2C_XFER_TIMEOUT_MS * 20;
|
|
||||||
while (!(readl(&i2c->iiccon) & I2CCON_IRPND)) {
|
|
||||||
if (i == 0) {
|
|
||||||
//debug("%s: i2c xfer timeout\n", __func__);
|
|
||||||
return I2C_NOK_TOUT;
|
|
||||||
}
|
|
||||||
my_udelay(50);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return I2C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int IsACK(struct s3c24x0_i2c *i2c)
|
|
||||||
{
|
|
||||||
return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ReadWriteByte(struct s3c24x0_i2c *i2c)
|
|
||||||
{
|
|
||||||
uint32_t x;
|
|
||||||
|
|
||||||
x = readl(&i2c->iiccon);
|
|
||||||
writel(x & ~I2CCON_IRPND, &i2c->iiccon);
|
|
||||||
/* FIXME(dhendrix): cannot use nested macro (compilation failure) */
|
|
||||||
// writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Verify the whether I2C ACK was received or not
|
|
||||||
*
|
|
||||||
* @param i2c pointer to I2C register base
|
|
||||||
* @param buf array of data
|
|
||||||
* @param len length of data
|
|
||||||
* return I2C_OK when transmission done
|
|
||||||
* I2C_NACK otherwise
|
|
||||||
*/
|
|
||||||
static int i2c_send_verify(struct s3c24x0_i2c *i2c, unsigned char buf[],
|
|
||||||
unsigned char len)
|
|
||||||
{
|
|
||||||
int i, result = I2C_OK;
|
|
||||||
|
|
||||||
if (IsACK(i2c)) {
|
|
||||||
for (i = 0; (i < len) && (result == I2C_OK); i++) {
|
|
||||||
writel(buf[i], &i2c->iicds);
|
|
||||||
ReadWriteByte(i2c);
|
|
||||||
result = WaitForXfer(i2c);
|
|
||||||
if (result == I2C_OK && !IsACK(i2c))
|
|
||||||
result = I2C_NACK;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = I2C_NACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Send a STOP event and wait for it to have completed
|
|
||||||
*
|
|
||||||
* @param mode If it is a master transmitter or receiver
|
|
||||||
* @return I2C_OK if the line became idle before timeout I2C_NOK_TOUT otherwise
|
|
||||||
*/
|
|
||||||
static int i2c_send_stop(struct s3c24x0_i2c *i2c, int mode)
|
|
||||||
{
|
|
||||||
int timeout;
|
|
||||||
|
|
||||||
/* Setting the STOP event to fire */
|
|
||||||
writel(mode | I2C_TXRX_ENA, &i2c->iicstat);
|
|
||||||
ReadWriteByte(i2c);
|
|
||||||
|
|
||||||
/* Wait for the STOP to send and the bus to go idle */
|
|
||||||
for (timeout = I2C_STOP_TIMEOUT_US; timeout > 0; timeout -= 5) {
|
|
||||||
if (!(readl(&i2c->iicstat) & I2CSTAT_BSY))
|
|
||||||
return I2C_OK;
|
|
||||||
my_udelay(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return I2C_NOK_TOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* cmd_type is 0 for write, 1 for read.
|
|
||||||
*
|
|
||||||
* addr_len can take any value from 0-255, it is only limited
|
|
||||||
* by the char, we could make it larger if needed. If it is
|
|
||||||
* 0 we skip the address write cycle.
|
|
||||||
*/
|
|
||||||
static int i2c_transfer(struct s3c24x0_i2c *i2c,
|
|
||||||
unsigned char cmd_type,
|
|
||||||
unsigned char chip,
|
|
||||||
unsigned char addr[],
|
|
||||||
unsigned char addr_len,
|
|
||||||
unsigned char data[],
|
|
||||||
unsigned short data_len)
|
|
||||||
{
|
|
||||||
int i, result, stop_bit_result;
|
|
||||||
uint32_t x;
|
|
||||||
|
|
||||||
if (data == 0 || data_len == 0) {
|
|
||||||
/* Don't support data transfer of no length or to address 0 */
|
|
||||||
//debug("i2c_transfer: bad call\n");
|
|
||||||
return I2C_NOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check I2C bus idle */
|
|
||||||
i = I2C_IDLE_TIMEOUT_MS * 20;
|
|
||||||
while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) {
|
|
||||||
my_udelay(50);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readl(&i2c->iicstat) & I2CSTAT_BSY) {
|
|
||||||
//debug("%s: bus busy\n", __func__);
|
|
||||||
return I2C_NOK_TOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME(dhendrix): cannot use nested macro (compilation failure) */
|
|
||||||
//writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon);
|
|
||||||
x = readl(&i2c->iiccon);
|
|
||||||
writel(x | I2CCON_ACKGEN, &i2c->iiccon);
|
|
||||||
|
|
||||||
if (addr && addr_len) {
|
|
||||||
writel(chip, &i2c->iicds);
|
|
||||||
/* send START */
|
|
||||||
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
|
|
||||||
&i2c->iicstat);
|
|
||||||
if (WaitForXfer(i2c) == I2C_OK)
|
|
||||||
result = i2c_send_verify(i2c, addr, addr_len);
|
|
||||||
else
|
|
||||||
result = I2C_NACK;
|
|
||||||
} else
|
|
||||||
result = I2C_NACK;
|
|
||||||
|
|
||||||
switch (cmd_type) {
|
|
||||||
case I2C_WRITE:
|
|
||||||
if (result == I2C_OK)
|
|
||||||
result = i2c_send_verify(i2c, data, data_len);
|
|
||||||
else {
|
|
||||||
writel(chip, &i2c->iicds);
|
|
||||||
/* send START */
|
|
||||||
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
|
|
||||||
&i2c->iicstat);
|
|
||||||
if (WaitForXfer(i2c) == I2C_OK)
|
|
||||||
result = i2c_send_verify(i2c, data, data_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == I2C_OK)
|
|
||||||
result = WaitForXfer(i2c);
|
|
||||||
|
|
||||||
stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MT);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case I2C_READ:
|
|
||||||
{
|
|
||||||
int was_ok = (result == I2C_OK);
|
|
||||||
|
|
||||||
writel(chip, &i2c->iicds);
|
|
||||||
/* resend START */
|
|
||||||
writel(I2C_MODE_MR | I2C_TXRX_ENA |
|
|
||||||
I2C_START_STOP, &i2c->iicstat);
|
|
||||||
ReadWriteByte(i2c);
|
|
||||||
result = WaitForXfer(i2c);
|
|
||||||
|
|
||||||
if (was_ok || IsACK(i2c)) {
|
|
||||||
i = 0;
|
|
||||||
while ((i < data_len) && (result == I2C_OK)) {
|
|
||||||
/* disable ACK for final READ */
|
|
||||||
if (i == data_len - 1) {
|
|
||||||
/* FIXME(dhendrix): nested macro */
|
|
||||||
#if 0
|
|
||||||
writel(readl(&i2c->iiccon) &
|
|
||||||
~I2CCON_ACKGEN,
|
|
||||||
&i2c->iiccon);
|
|
||||||
#endif
|
|
||||||
x = readl(&i2c->iiccon) & ~I2CCON_ACKGEN;
|
|
||||||
writel(x, &i2c->iiccon);
|
|
||||||
}
|
|
||||||
ReadWriteByte(i2c);
|
|
||||||
result = WaitForXfer(i2c);
|
|
||||||
data[i] = readl(&i2c->iicds);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = I2C_NACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MR);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
//debug("i2c_transfer: bad call\n");
|
|
||||||
result = stop_bit_result = I2C_NOK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the transmission went fine, then only the stop bit was left to
|
|
||||||
* fail. Otherwise, the real failure we're interested in came before
|
|
||||||
* that, during the actual transmission.
|
|
||||||
*/
|
|
||||||
return (result == I2C_OK) ? stop_bit_result : result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
|
||||||
{
|
|
||||||
struct s3c24x0_i2c_bus *i2c = &i2c0;
|
|
||||||
uchar xaddr[4];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (alen > 4) {
|
|
||||||
//debug("I2C read: addr len %d not supported\n", alen);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alen > 0) {
|
|
||||||
xaddr[0] = (addr >> 24) & 0xFF;
|
|
||||||
xaddr[1] = (addr >> 16) & 0xFF;
|
|
||||||
xaddr[2] = (addr >> 8) & 0xFF;
|
|
||||||
xaddr[3] = addr & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
|
||||||
/*
|
|
||||||
* EEPROM chips that implement "address overflow" are ones
|
|
||||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
|
||||||
* address and the extra bits end up in the "chip address"
|
|
||||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
|
||||||
* four 256 byte chips.
|
|
||||||
*
|
|
||||||
* Note that we consider the length of the address field to
|
|
||||||
* still be one byte because the extra address bits are
|
|
||||||
* hidden in the chip address.
|
|
||||||
*/
|
|
||||||
if (alen > 0)
|
|
||||||
chip |= ((addr >> (alen * 8)) &
|
|
||||||
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
|
||||||
#endif
|
|
||||||
if (!i2c)
|
|
||||||
return -1;
|
|
||||||
ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, &xaddr[4 - alen],
|
|
||||||
alen, buffer, len);
|
|
||||||
if (ret) {
|
|
||||||
//debug("I2c read: failed %d\n", ret);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
|
||||||
{
|
|
||||||
struct s3c24x0_i2c_bus *i2c;
|
|
||||||
uchar xaddr[4];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (alen > 4) {
|
|
||||||
//debug("I2C write: addr len %d not supported\n", alen);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alen > 0) {
|
|
||||||
xaddr[0] = (addr >> 24) & 0xFF;
|
|
||||||
xaddr[1] = (addr >> 16) & 0xFF;
|
|
||||||
xaddr[2] = (addr >> 8) & 0xFF;
|
|
||||||
xaddr[3] = addr & 0xFF;
|
|
||||||
}
|
|
||||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
|
||||||
/*
|
|
||||||
* EEPROM chips that implement "address overflow" are ones
|
|
||||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
|
||||||
* address and the extra bits end up in the "chip address"
|
|
||||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
|
||||||
* four 256 byte chips.
|
|
||||||
*
|
|
||||||
* Note that we consider the length of the address field to
|
|
||||||
* still be one byte because the extra address bits are
|
|
||||||
* hidden in the chip address.
|
|
||||||
*/
|
|
||||||
if (alen > 0)
|
|
||||||
chip |= ((addr >> (alen * 8)) &
|
|
||||||
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
|
||||||
#endif
|
|
||||||
//i2c = get_bus(g_current_bus);
|
|
||||||
i2c = &i2c0;
|
|
||||||
if (!i2c)
|
|
||||||
return -1;
|
|
||||||
ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, &xaddr[4 - alen],
|
|
||||||
alen, buffer, len);
|
|
||||||
|
|
||||||
return ret != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Max77686 parameters values
|
* Max77686 parameters values
|
||||||
* see max77686.h for parameters details
|
* see max77686.h for parameters details
|
||||||
|
@ -1428,6 +1027,7 @@ void bootblock_mainboard_init(void)
|
||||||
{
|
{
|
||||||
/* FIXME: we should not need UART in bootblock, this is only
|
/* FIXME: we should not need UART in bootblock, this is only
|
||||||
done for testing purposes */
|
done for testing purposes */
|
||||||
|
i2c_set_early_reg(I2C0_BASE);
|
||||||
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
||||||
power_init();
|
power_init();
|
||||||
clock_init();
|
clock_init();
|
||||||
|
|
Loading…
Reference in New Issue