
On Thu, Jul 19, 2012 at 12:39 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 - Channel initialisation is moved to a commom funation as it is required by i2c_init. - Hardcoding for I2CCON_ACKGEN removed. - Replaced printf with debug. - Checkpatch issues resolved. - Pinmux setting will be done in board/samsung/smdk5250/smdk5250.c to avoid repeated setting of gpio lines, as it have multi bus support.
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
I think this is reasonable thank you.
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. Changes in V3: - Incorporated review comments from Joonyoung Shim. - Reduced the number of #ifdef by modifying get_i2c_base function. - Removed duplicate code. Changes in V4: - Resolved build error for s3c2410. Changes in V5: - Pinmux setting will be done in board/samsung/smdk5250/smdk5250.c to avoid repeated setting of gpio lines, as it have multi bus support. - I2C bus offset calulation done based on EXYNOS_I2C_SPACING - Peripharal related code removed. Changes in V6: - g_current_bus made common to all platforms. drivers/i2c/s3c24x0_i2c.c | 193 +++++++++++++++++++++++++++----------------- 1 files changed, 118 insertions(+), 75 deletions(-)
diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index ba6f39b..421c7dd 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -27,10 +27,15 @@ */
#include <common.h> +#ifdef CONFIG_EXYNOS5 +#include <asm/arch/clk.h> +#include <asm/arch/cpu.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 +50,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 +59,10 @@
#define I2C_TIMEOUT 1 /* 1 second */
+static unsigned int g_current_bus; /* Stores Current I2C Bus */
+#ifndef CONFIG_EXYNOS5 static int GetI2CSDA(void) { struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); @@ -77,16 +87,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,35 +109,77 @@ 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 struct s3c24x0_i2c *get_base_i2c(void) +{ +#ifdef CONFIG_EXYNOS5
struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c()
+ (EXYNOS5_I2C_SPACING
* g_current_bus));
return i2c;
+#else
return s3c24x0_get_base_i2c();
+#endif +}
+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);
+}
void i2c_init(int speed, int slaveadd) {
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
struct s3c24x0_i2c *i2c = get_base_i2c();
+#ifndef CONFIG_EXYNOS5 struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
ulong freq, pres = 16, div;
+#endif int i;
/* wait for some time to give previous transfer a chance to finish */
/* By default i2c channel 0 is the current bus */
g_current_bus = 0;
+#ifdef CONFIG_EXYNOS5
i2c_ch_init(i2c, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+#endif
/* 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--; }
+#ifndef CONFIG_EXYNOS5 if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) { #ifdef CONFIG_S3C2410 ulong old_gpecon = readl(&gpio->gpecon); @@ -171,26 +224,8 @@ 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 /* #ifndef CONFIG_EXYNOS5 */ }
/* @@ -200,19 +235,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 +261,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 +273,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 +292,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 +314,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 +328,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 +352,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 +374,24 @@ 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];
i2c = get_base_i2c(); buf[0] = 0; /*
@@ -360,16 +399,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 +436,11 @@ 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);
i2c = get_base_i2c();
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 +448,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 +478,9 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif
i2c = get_base_i2c(); 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 */
1.7.4.4