summaryrefslogtreecommitdiff
path: root/src/cpu/samsung/exynos5-common/i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cpu/samsung/exynos5-common/i2c.c')
-rw-r--r--src/cpu/samsung/exynos5-common/i2c.c354
1 files changed, 78 insertions, 276 deletions
diff --git a/src/cpu/samsung/exynos5-common/i2c.c b/src/cpu/samsung/exynos5-common/i2c.c
index c5315304b8..30fabe1659 100644
--- a/src/cpu/samsung/exynos5-common/i2c.c
+++ b/src/cpu/samsung/exynos5-common/i2c.c
@@ -26,19 +26,14 @@
* The different address mapping is handled by the s3c24xx.h files below.
*/
-#include <common.h>
+#include <delay.h>
#include <arch/io.h>
-#include "clk.h"
+#include <console/console.h>
+#include <device/i2c.h>
#include "cpu/samsung/exynos5-common/clk.h"
-#include "cpu/samsung/exynos5250/cpu.h"
-#include "gpio.h"
-#include "cpu/samsung/exynos5250/gpio.h"
+#include "cpu/samsung/exynos5-common/i2c.h"
#include "cpu/samsung/exynos5250/pinmux.h"
-//#include <fdtdec.h>
-#include "device/i2c-old.h"
-#include "i2c.h"
-
#define I2C_WRITE 0
#define I2C_READ 1
@@ -65,54 +60,49 @@ enum {
I2C_STOP_TIMEOUT_US = 200, /* waiting for stop events */
};
-/* We should not rely on any particular ordering of these IDs */
-#if 0
-#ifndef CONFIG_OF_CONTROL
-static enum periph_id periph_for_dev[EXYNOS_I2C_MAX_CONTROLLERS] = {
- PERIPH_ID_I2C0,
- PERIPH_ID_I2C1,
- PERIPH_ID_I2C2,
- PERIPH_ID_I2C3,
- PERIPH_ID_I2C4,
- PERIPH_ID_I2C5,
- PERIPH_ID_I2C6,
- PERIPH_ID_I2C7,
+static struct s3c24x0_i2c_bus i2c_buses[] = {
+ /* FIXME: exynos5250-specific? */
+ {
+ .bus_num = 0,
+ .regs = (struct s3c24x0_i2c *)0x12c60000,
+ .periph_id = PERIPH_ID_I2C0,
+ },
+ {
+ .bus_num = 1,
+ .regs = (struct s3c24x0_i2c *)0x12c70000,
+ .periph_id = PERIPH_ID_I2C1,
+ },
+ {
+ .bus_num = 2,
+ .regs = (struct s3c24x0_i2c *)0x12c80000,
+ .periph_id = PERIPH_ID_I2C2,
+ },
+ {
+ .bus_num = 3,
+ .regs = (struct s3c24x0_i2c *)0x12c90000,
+ .periph_id = PERIPH_ID_I2C3,
+ },
+ {
+ .bus_num = 4,
+ .regs = (struct s3c24x0_i2c *)0x12ca0000,
+ .periph_id = PERIPH_ID_I2C4,
+ },
+ {
+ .bus_num = 5,
+ .regs = (struct s3c24x0_i2c *)0x12cb0000,
+ .periph_id = PERIPH_ID_I2C5,
+ },
+ {
+ .bus_num = 6,
+ .regs = (struct s3c24x0_i2c *)0x12cc0000,
+ .periph_id = PERIPH_ID_I2C6,
+ },
+ {
+ .bus_num = 7,
+ .regs = (struct s3c24x0_i2c *)0x12cd0000,
+ .periph_id = PERIPH_ID_I2C7,
+ },
};
-#endif
-#endif
-
-static unsigned int g_current_bus __attribute__((section(".data")));
-static struct s3c24x0_i2c *g_early_i2c_config __attribute__((section(".data")));
-
-static struct s3c24x0_i2c_bus i2c_bus[EXYNOS_I2C_MAX_CONTROLLERS]
- __attribute__((section(".data")));
-static int i2c_busses __attribute__((section(".data")));
-
-void i2c_set_early_reg(unsigned int base)
-{
- g_early_i2c_config = (struct s3c24x0_i2c *)base;
-}
-
-static struct s3c24x0_i2c_bus *get_bus(int bus_idx)
-{
- /* If an early i2c config exists we just use that */
- if (g_early_i2c_config) {
- /* FIXME: value not retained from i2c_set_early_reg()? (but then, how
- * did if (!i2c) check pass earlier on? Corrupt value? */
- i2c_bus[0].regs = g_early_i2c_config;
- return &i2c_bus[0];
- }
-
- if (bus_idx < i2c_busses)
- return &i2c_bus[bus_idx];
- debug("Undefined bus: %d\n", bus_idx);
- return NULL;
-}
-
-static inline struct exynos5_gpio_part1 *exynos_get_base_gpio1(void)
-{
- return (struct exynos5_gpio_part1 *)(EXYNOS5_GPIO_PART1_BASE);
-}
static int WaitForXfer(struct s3c24x0_i2c *i2c)
{
@@ -121,7 +111,7 @@ static int WaitForXfer(struct s3c24x0_i2c *i2c)
i = I2C_XFER_TIMEOUT_MS * 20;
while (!(readl(&i2c->iiccon) & I2CCON_IRPND)) {
if (i == 0) {
- debug("%s: i2c xfer timeout\n", __func__);
+ printk(BIOS_ERR, "%s: i2c xfer timeout\n", __func__);
return I2C_NOK_TOUT;
}
udelay(50);
@@ -142,15 +132,14 @@ static void ReadWriteByte(struct s3c24x0_i2c *i2c)
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);
}
-static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd)
+static void i2c_ch_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd)
{
unsigned long freq, pres = 16, div;
+ unsigned long val;
- freq = clock_get_periph_rate(PERIPH_ID_I2C0);
+ freq = clock_get_periph_rate(bus->periph_id);
/* calculate prescaler and divisor values */
if ((freq / pres / (16 + 1)) > speed)
/* set prescaler to 512 */
@@ -162,136 +151,25 @@ static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd)
div++;
/* set prescaler, divisor according to freq, also set ACKGEN, IRQ */
- writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
+ val = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0);
+ writel(val, &bus->regs->iiccon);
- /* init to SLAVE REVEIVE and set slaveaddr */
- writel(0, &i2c->iicstat);
- writel(slaveadd, &i2c->iicadd);
+ /* init to SLAVE RECEIVE mode and clear I2CADDn */
+ writel(0, &bus->regs->iicstat);
+ writel(slaveadd, &bus->regs->iicadd);
/* program Master Transmit (and implicit STOP) */
- writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
-}
-
-/* TODO: determine if this is necessary to init board using FDT-provided info */
-#if 0
-void board_i2c_init(const void *blob)
-{
- /*
- * Turn off the early i2c configuration and init the i2c properly,
- * this is done here to enable the use of i2c configs from FDT.
- */
- i2c_set_early_reg(0);
-
-#ifdef CONFIG_OF_CONTROL
- int node_list[EXYNOS_I2C_MAX_CONTROLLERS];
- int i, count;
-
- count = fdtdec_find_aliases_for_id(blob, "i2c",
- COMPAT_SAMSUNG_S3C2440_I2C, node_list,
- EXYNOS_I2C_MAX_CONTROLLERS);
-
- for (i = 0; i < count; i++) {
- struct s3c24x0_i2c_bus *bus;
- int node = node_list[i];
-
- if (node < 0)
- continue;
- bus = &i2c_bus[i2c_busses];
- bus->regs = (struct s3c24x0_i2c *)
- fdtdec_get_addr(blob, node, "reg");
- bus->id = (enum periph_id)
- fdtdec_get_int(blob, node, "samsung,periph-id", -1);
- bus->node = node;
- bus->bus_num = i2c_busses++;
- }
-#else
- int i;
-
- for (i = 0; i < EXYNOS_I2C_MAX_CONTROLLERS; i++) {
- unsigned intptr_t reg_addr = samsung_get_base_i2c() +
- EXYNOS_I2C_SPACING * i;
-
- i2c_bus[i].regs = (struct s3c24x0_i2c_bus *)reg_addr;
- i2c_bus[i].id = periph_for_dev[i];
- }
- i2c_busses = EXYNOS_I2C_MAX_CONTROLLERS;
-#endif
+ writel(I2C_MODE_MT | I2C_TXRX_ENA, &bus->regs->iicstat);
}
-#endif
/*
* MULTI BUS I2C support
*/
-static void i2c_bus_init(struct s3c24x0_i2c_bus *i2c, unsigned int bus)
-{
- exynos_pinmux_config(i2c->id, 0);
- i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
-}
-
-#ifdef CONFIG_I2C_MULTI_BUS
-int i2c_set_bus_num(unsigned int bus)
+static void i2c_bus_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd)
{
- struct s3c24x0_i2c_bus *i2c;
-
- i2c = get_bus(bus);
- if (!i2c)
- return -1;
- g_current_bus = bus;
- i2c_bus_init(i2c, g_current_bus);
-
- return 0;
+ exynos_pinmux_config(bus->periph_id, 0);
+ i2c_ch_init(bus, speed, slaveadd);
}
-unsigned int i2c_get_bus_num(void)
-{
- return g_current_bus;
-}
-#endif
-
-#ifdef CONFIG_OF_CONTROL
-int i2c_get_bus_num_fdt(const void *blob, int node)
-{
- enum fdt_compat_id compat;
- fdt_addr_t reg;
- int i;
-
- compat = fdtdec_lookup(blob, node);
- if (compat != COMPAT_SAMSUNG_S3C2440_I2C) {
- debug("%s: Not a supported I2C node\n", __func__);
- return -1;
- }
-
- reg = fdtdec_get_addr(blob, node, "reg");
- for (i = 0; i < i2c_busses; i++)
- if (reg == (fdt_addr_t)(unsigned intptr_t)i2c_bus[i].regs)
- return i;
-
- debug("%s: Can't find any matched I2C bus\n", __func__);
- return -1;
-}
-
-int i2c_reset_port_fdt(const void *blob, int node)
-{
- struct s3c24x0_i2c_bus *i2c;
-
- int bus;
-
- bus = i2c_get_bus_num_fdt(blob, node);
- if (bus < 0) {
- printf("could not get bus for node %d\n", node);
- return -1;
- }
- i2c = get_bus(bus);
- if (!i2c) {
- printf("get_bus() failed for node node %d\n", node);
- return -1;
- }
-
- i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
-
- return 0;
-}
-#endif
-
/*
* Verify the whether I2C ACK was received or not
*
@@ -321,21 +199,13 @@ static int i2c_send_verify(struct s3c24x0_i2c *i2c, unsigned char buf[],
return result;
}
-void i2c_init(int speed, int slaveadd)
+void i2c_init(unsigned bus_num, int speed, int slaveadd)
{
struct s3c24x0_i2c_bus *i2c;
- struct exynos5_gpio_part1 *gpio;
int i;
- uint32_t x;
-
- /* By default i2c channel 0 is the current bus */
- g_current_bus = 0;
- i2c = get_bus(g_current_bus);
- if (!i2c)
- return;
-
- i2c_bus_init(i2c, g_current_bus);
+ i2c = &i2c_buses[bus_num];
+ i2c_bus_init(i2c, speed, slaveadd);
/* wait for some time to give previous transfer a chance to finish */
i = I2C_INIT_TIMEOUT_MS * 20;
@@ -344,13 +214,7 @@ void i2c_init(int speed, int slaveadd)
i--;
}
- gpio = exynos_get_base_gpio1();
- /* 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);
+ i2c_ch_init(i2c, speed, slaveadd);
}
/*
@@ -397,7 +261,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
if (data == 0 || data_len == 0) {
/* Don't support data transfer of no length or to address 0 */
- debug("i2c_transfer: bad call\n");
+ printk(BIOS_ERR, "i2c_transfer: bad call\n");
return I2C_NOK;
}
@@ -409,12 +273,10 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
}
if (readl(&i2c->iicstat) & I2CSTAT_BSY) {
- debug("%s: bus busy\n", __func__);
+ printk(BIOS_ERR, "%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);
@@ -465,12 +327,6 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
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);
}
@@ -488,7 +344,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
}
default:
- debug("i2c_transfer: bad call\n");
+ printk(BIOS_ERR, "i2c_transfer: bad call\n");
result = stop_bit_result = I2C_NOK;
break;
}
@@ -501,35 +357,15 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
return (result == I2C_OK) ? stop_bit_result : result;
}
-int i2c_probe(unsigned char chip)
-{
- struct s3c24x0_i2c_bus *i2c;
- unsigned char buf[1];
- int ret;
-
- i2c = get_bus(g_current_bus);
- if (!i2c)
- return -1;
- buf[0] = 0;
-
- /*
- * What is needed is to send the chip address and verify that the
- * address was <ACK>ed (i.e. there was a chip at that address which
- * drove the data line low).
- */
- ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, 0, 0, buf, 1);
-
- return ret != I2C_OK;
-}
-
-int i2c_read(unsigned char chip, unsigned int addr, int alen, unsigned char *buffer, int len)
+int i2c_read(unsigned bus, unsigned chip, unsigned addr,
+ unsigned alen, unsigned char *buf, unsigned len)
{
struct s3c24x0_i2c_bus *i2c;
unsigned char xaddr[4];
int ret;
if (alen > 4) {
- debug("I2C read: addr len %d not supported\n", alen);
+ printk(BIOS_ERR, "I2C read: addr len %d not supported\n", alen);
return 1;
}
@@ -540,42 +376,26 @@ int i2c_read(unsigned char chip, unsigned int addr, int alen, unsigned char *buf
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);
- if (!i2c)
- return -1;
+ i2c = &i2c_buses[bus];
ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, &xaddr[4 - alen],
- alen, buffer, len);
+ alen, buf, len);
if (ret) {
- debug("I2c read: failed %d\n", ret);
+ printk(BIOS_ERR, "I2c read: failed %d\n", ret);
return 1;
}
return 0;
}
-int i2c_write(unsigned char chip, unsigned int addr, int alen, unsigned char *buffer, int len)
+int i2c_write(unsigned bus, unsigned chip, unsigned addr,
+ unsigned alen, unsigned char *buf, unsigned len)
{
struct s3c24x0_i2c_bus *i2c;
unsigned char xaddr[4];
int ret;
if (alen > 4) {
- debug("I2C write: addr len %d not supported\n", alen);
+ printk(BIOS_ERR, "I2C write: addr len %d not supported\n",
+ alen);
return 1;
}
@@ -585,28 +405,10 @@ int i2c_write(unsigned char chip, unsigned int addr, int alen, unsigned char *bu
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);
- if (!i2c)
- return -1;
+ i2c = &i2c_buses[bus];
ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, &xaddr[4 - alen],
- alen, buffer, len);
+ alen, buf, len);
return ret != 0;
}