diff options
author | David Hendricks <dhendrix@chromium.org> | 2013-04-05 16:11:12 -0700 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2013-04-10 00:01:02 +0200 |
commit | b959fbb87adb274b442bc6ab812e5a2ce92ca220 (patch) | |
tree | 9d055eb9126f50e5c72ab8bdcbe6c8efa4bceb51 /src/cpu/samsung/exynos5-common/i2c.c | |
parent | cfb73607be05e57237592c5c94a98589aba04833 (diff) |
exynos5: Re-factor I2C code
This re-factors the Exynos5 I2C code to be simpler and use the
new API, and updates users accordingly.
- i2c_read() and i2c_write() functions updated to take bus number
as an argument.
- Get rid of the EEPROM_ADDR_OVERFLOW stuff in i2c_read() and
i2c_write(). If a chip needs special handling we should take care
of it elsewhere, not in every low-level i2c driver.
- All the confusing bus config functions eliminated. No more
i2c_set_early_config() or i2c_set_bus() or i2c_get_bus(). All this
is handled automatically when the caller does a transaction and
specifies the desired bus number.
- i2c_probe() eliminated. We're not a command-line utility.
- Let the compiler place static variables automatically. We don't need
any of this fancy manual data placement.
- Remove dead code while we're at it. This stuff was ported early on
and much of it was left commented out in case we needed it. Some
also includes nested macros which caused gcc to complain.
- Clean up #includes (no more common.h, woohoo!), replace debug() with
printk().
Change-Id: I8e1f974ea4c6c7db9f33b77bbc4fb16008ed0d2a
Signed-off-by: David Hendricks <dhendrix@chromium.org>
Reviewed-on: http://review.coreboot.org/3044
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Diffstat (limited to 'src/cpu/samsung/exynos5-common/i2c.c')
-rw-r--r-- | src/cpu/samsung/exynos5-common/i2c.c | 354 |
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; } |