
On Wed, Jun 6, 2012 at 11:09 PM, Rajeshwari Shinde <rajeshwari.s@samsung.com
wrote:
This patch modifies the S3C I2C driver to suppport EXYNOS5. The cahnges made to driver are as follows: - I2C base address is passed as a parameter to many functions to avoid multiple #ifdef - I2C init for Exynos5 is made as different function. - Channel initialisation is moved to a commom funation as it is required by both the i2c_init. - Separate functions written to get I2C base address, peripheral id for pinmux support. - Hardcoding for I2CCON_ACKGEN removed. - Replaced printf with debug. - Checkpatch issues resolved.
Signed-off-by: Alim Akhtar alim.akhtar@samsung.com Signed-off-by: Doug Anderson dianders@chromium.org Signed-off-by: Rajeshwari Shinde rajeshwari.s@samsung.com Acked-by: Simon Glass sjg@chromium.org
Changes in V2: - Removed #define for I2C cahnnels from hearder file except for I2C0. - Incorporated review comments from Simon Glass.
Acked-by: Simon Glass sjg@chromium.org
(Looking forward to getting the transfer loop simplified one day!)
drivers/i2c/s3c24x0_i2c.c | 254 ++++++++++++++++++++++++++++++++------------- drivers/i2c/s3c24x0_i2c.h | 3 + 2 files changed, 184 insertions(+), 73 deletions(-)
diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index ba6f39b..a71f147 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -27,10 +27,17 @@ */
#include <common.h> +#ifdef CONFIG_EXYNOS5 +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pinmux.h> +#else #include <asm/arch/s3c24x0_cpu.h>
+#endif #include <asm/io.h> #include <i2c.h> +#include "s3c24x0_i2c.h"
#ifdef CONFIG_HARD_I2C
@@ -45,6 +52,7 @@
#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 */ @@ -53,6 +61,44 @@
#define I2C_TIMEOUT 1 /* 1 second */
+#ifdef CONFIG_EXYNOS5 +static unsigned int g_current_bus; /* Stores Current I2C Bus */
+/* We should not rely on any particular ordering of these IDs */ +static enum periph_id periph_for_dev[] = {
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 enum periph_id i2c_get_periph_id(unsigned dev_index) +{
if (dev_index < ARRAY_SIZE(periph_for_dev))
return periph_for_dev[dev_index];
debug("%s: invalid bus %d", __func__, dev_index);
return PERIPH_ID_NONE;
+}
+static struct s3c24x0_i2c *get_base_i2c(int bus_idx) +{
struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c
*)samsung_get_base_i2c();
return &i2c[bus_idx];
+}
+static inline struct exynos5_gpio_part1 *exynos_get_base_gpio1(void) +{
return (struct exynos5_gpio_part1 *)(EXYNOS5_GPIO_PART1_BASE);
+}
+#else static int GetI2CSDA(void) { struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); @@ -77,16 +123,17 @@ static void SetI2CSCL(int x) struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
#ifdef CONFIG_S3C2410
writel((readl(&gpio->gpedat) & ~0x4000) | (x & 1) << 14,
&gpio->gpedat);
writel((readl(&gpio->gpedat) & ~0x4000) |
(x & 1) << 14, &gpio->gpedat);
#endif #ifdef CONFIG_S3C2400 writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat); #endif } +#endif
-static int WaitForXfer(void) +static int WaitForXfer(struct s3c24x0_i2c *i2c) {
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); int i; i = I2C_TIMEOUT * 10000;
@@ -98,25 +145,84 @@ static int WaitForXfer(void) return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT; }
-static int IsACK(void) +static int IsACK(struct s3c24x0_i2c *i2c) {
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
}
-static void ReadWriteByte(void) +static void ReadWriteByte(struct s3c24x0_i2c *i2c) {
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon);
}
+static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) +{
ulong freq, pres = 16, div;
+#ifdef CONFIG_EXYNOS5
freq = get_i2c_clk();
+#else
freq = get_PCLK();
+#endif
/* 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 *i2c, unsigned int bus) +{
int periph_id = i2c_get_periph_id(bus);
exynos_pinmux_config(periph_id, 0);
i2c_ch_init(i2c, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+}
+#ifdef CONFIG_EXYNOS5 +void i2c_init(int speed, int slaveadd) +{
struct s3c24x0_i2c *i2c;
struct exynos5_gpio_part1 *gpio;
int i;
/* By default i2c channel 0 is the current bus */
g_current_bus = I2C0;
i2c = get_base_i2c(g_current_bus);
i2c_bus_init(i2c, g_current_bus);
/* wait for some time to give previous transfer a chance to finish
*/
i = I2C_TIMEOUT * 1000;
while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) {
udelay(1000);
i--;
}
gpio = exynos_get_base_gpio1();
writel((readl(&gpio->b3.con) & ~0x00FF) | 0x0022, &gpio->b3.con);
i2c_ch_init(i2c, speed, slaveadd);
+}
+#else void i2c_init(int speed, int slaveadd) { struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
ulong freq, pres = 16, div; int i; /* wait for some time to give previous transfer a chance to finish
*/ @@ -171,27 +277,9 @@ void i2c_init(int speed, int slaveadd) #endif }
/* calculate prescaler and divisor values */
freq = get_PCLK();
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);
i2c_ch_init(i2c, speed, slaveadd);
} +#endif
/*
- cmd_type is 0 for write, 1 for read.
@@ -200,19 +288,19 @@ void i2c_init(int speed, int slaveadd)
- by the char, we could make it larger if needed. If it is
- 0 we skip the address write cycle.
*/ -static -int i2c_transfer(unsigned char cmd_type,
unsigned char chip,
unsigned char addr[],
unsigned char addr_len,
unsigned char data[], unsigned short data_len)
+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)
{
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); int i, result; if (data == 0 || data_len == 0) { /*Don't support data transfer of no length or to address 0
*/
printf("i2c_transfer: bad call\n");
debug("i2c_transfer: bad call\n"); return I2C_NOK; }
@@ -226,7 +314,7 @@ int i2c_transfer(unsigned char cmd_type, if (readl(&i2c->iicstat) & I2CSTAT_BSY) return I2C_NOK_TOUT;
writel(readl(&i2c->iiccon) | 0x80, &i2c->iiccon);
writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon); result = I2C_OK; switch (cmd_type) {
@@ -238,16 +326,16 @@ int i2c_transfer(unsigned char cmd_type, &i2c->iicstat); i = 0; while ((i < addr_len) && (result == I2C_OK)) {
result = WaitForXfer();
result = WaitForXfer(i2c); writel(addr[i], &i2c->iicds);
ReadWriteByte();
ReadWriteByte(i2c); i++; } i = 0; while ((i < data_len) && (result == I2C_OK)) {
result = WaitForXfer();
result = WaitForXfer(i2c); writel(data[i], &i2c->iicds);
ReadWriteByte();
ReadWriteByte(i2c); i++; } } else {
@@ -257,19 +345,19 @@ int i2c_transfer(unsigned char cmd_type, &i2c->iicstat); i = 0; while ((i < data_len) && (result = I2C_OK)) {
result = WaitForXfer();
result = WaitForXfer(i2c); writel(data[i], &i2c->iicds);
ReadWriteByte();
ReadWriteByte(i2c); i++; } } if (result == I2C_OK)
result = WaitForXfer();
result = WaitForXfer(i2c); /* send STOP */ writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
ReadWriteByte();
ReadWriteByte(i2c); break; case I2C_READ:
@@ -279,13 +367,13 @@ int i2c_transfer(unsigned char cmd_type, /* send START */ writel(readl(&i2c->iicstat) | I2C_START_STOP, &i2c->iicstat);
result = WaitForXfer();
if (IsACK()) {
result = WaitForXfer(i2c);
if (IsACK(i2c)) { i = 0; while ((i < addr_len) && (result ==
I2C_OK)) { writel(addr[i], &i2c->iicds);
ReadWriteByte();
result = WaitForXfer();
ReadWriteByte(i2c);
result = WaitForXfer(i2c); i++; }
@@ -293,16 +381,17 @@ int i2c_transfer(unsigned char cmd_type, /* resend START */ writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, &i2c->iicstat);
ReadWriteByte();
result = WaitForXfer();
ReadWriteByte(i2c);
result = WaitForXfer(i2c); i = 0; while ((i < data_len) && (result ==
I2C_OK)) { /* disable ACK for final READ */ if (i == data_len - 1) writel(readl(&i2c->iiccon)
& ~0x80,
&i2c->iiccon);
ReadWriteByte();
result = WaitForXfer();
& ~I2CCON_ACKGEN,
&i2c->iiccon);
ReadWriteByte(i2c);
result = WaitForXfer(i2c); data[i] = readl(&i2c->iicds); i++; }
@@ -316,17 +405,18 @@ int i2c_transfer(unsigned char cmd_type, /* send START */ writel(readl(&i2c->iicstat) | I2C_START_STOP, &i2c->iicstat);
result = WaitForXfer();
result = WaitForXfer(i2c);
if (IsACK()) {
if (IsACK(i2c)) { i = 0; while ((i < data_len) && (result ==
I2C_OK)) { /* disable ACK for final READ */ if (i == data_len - 1) writel(readl(&i2c->iiccon) &
~0x80,
&i2c->iiccon);
ReadWriteByte();
result = WaitForXfer();
~I2CCON_ACKGEN,
&i2c->iiccon);
ReadWriteByte(i2c);
result = WaitForXfer(i2c); data[i] = readl(&i2c->iicds); i++; }
@@ -337,22 +427,28 @@ int i2c_transfer(unsigned char cmd_type,
/* send STOP */ writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
ReadWriteByte();
ReadWriteByte(i2c); break; default:
printf("i2c_transfer: bad call\n");
debug("i2c_transfer: bad call\n"); result = I2C_NOK; break; }
return (result);
return result;
}
int i2c_probe(uchar chip) {
struct s3c24x0_i2c *i2c; uchar buf[1];
+#ifdef CONFIG_EXYNOS5
i2c = get_base_i2c(g_current_bus);
+#else
i2c = s3c24x0_get_base_i2c();
+#endif buf[0] = 0;
/*
@@ -360,16 +456,17 @@ int i2c_probe(uchar chip) * address was <ACK>ed (i.e. there was a chip at that address which * drove the data line low). */
return i2c_transfer(I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK;
return i2c_transfer(i2c, I2C_READ, chip << 1, 0, 0, buf, 1) !=
I2C_OK; }
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) {
struct s3c24x0_i2c *i2c; uchar xaddr[4]; int ret; if (alen > 4) {
printf("I2C read: addr len %d not supported\n", alen);
debug("I2C read: addr len %d not supported\n", alen); return 1; }
@@ -396,10 +493,15 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif
if ((ret =
i2c_transfer(I2C_READ, chip << 1, &xaddr[4 - alen], alen,
buffer, len)) != 0) {
printf("I2c read: failed %d\n", ret);
+#ifdef CONFIG_EXYNOS5
i2c = get_base_i2c(g_current_bus);
+#else
i2c = s3c24x0_get_base_i2c();
+#endif
ret = i2c_transfer(i2c, I2C_READ, chip << 1, &xaddr[4 - alen],
alen,
buffer, len);
if (ret != 0) {
debug("I2c read: failed %d\n", ret); return 1; } return 0;
@@ -407,10 +509,11 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) {
struct s3c24x0_i2c *i2c; uchar xaddr[4]; if (alen > 4) {
printf("I2C write: addr len %d not supported\n", alen);
debug("I2C write: addr len %d not supported\n", alen); return 1; }
@@ -436,8 +539,13 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif +#ifdef CONFIG_EXYNOS5
i2c = get_base_i2c(g_current_bus);
+#else
i2c = s3c24x0_get_base_i2c();
+#endif return (i2c_transfer
(I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer,
(i2c, I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer, len) != 0);
} #endif /* CONFIG_HARD_I2C */ diff --git a/drivers/i2c/s3c24x0_i2c.h b/drivers/i2c/s3c24x0_i2c.h index d357a0a..57aafb1 100644 --- a/drivers/i2c/s3c24x0_i2c.h +++ b/drivers/i2c/s3c24x0_i2c.h @@ -23,6 +23,9 @@ #ifndef _S3C24X0_I2C_H #define _S3C24X0_I2C_H
+/* I2C channels exynos5 has 8 i2c channel */ +#define I2C0 0
struct s3c24x0_i2c { u32 iiccon; u32 iicstat; -- 1.7.4.4