
On 12 March 2013 22:11, Simon Glass sjg@chromium.org wrote:
Hi Naveen,
On Tue, Mar 12, 2013 at 3:58 AM, Naveen Krishna Chatradhi < ch.naveen@samsung.com> wrote:
Add support for hsi2c controller available on exynos5420.
Note: driver currently supports only fast speed mode 100kbps
Signed-off-by: Naveen Krishna Chatradhi ch.naveen@samsung.com Signed-off-by: R. Chandrasekar rc.sekar@samsung.com
I just commented on the Linux driver, so some comments may apply here also.
drivers/i2c/s3c24x0_i2c.c | 389 ++++++++++++++++++++++++++++++++++++++++++--- drivers/i2c/s3c24x0_i2c.h | 33 ++++ 2 files changed, 397 insertions(+), 25 deletions(-)
diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index 769a2ba..3117ab7 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -32,6 +32,7 @@ #include <asm/arch/clk.h> #include <asm/arch/cpu.h> #include <asm/arch/pinmux.h> +#include <asm/arch/clock.h> #else #include <asm/arch/s3c24x0_cpu.h> #endif @@ -50,6 +51,60 @@ #define I2C_NOK_LA 3 /* Lost arbitration */ #define I2C_NOK_TOUT 4 /* time out */
+/* HSI2C specific register description */
+/* I2C_CTL Register bits */ +#define HSI2C_FUNC_MODE_I2C (1u << 0) +#define HSI2C_MASTER (1u << 3) +#define HSI2C_RXCHON (1u << 6) /* Write/Send */ +#define HSI2C_TXCHON (1u << 7) /* Read/Receive */ +#define HSI2C_SW_RST (1u << 31)
+/* I2C_FIFO_CTL Register bits */ +#define HSI2C_RXFIFO_EN (1u << 0) +#define HSI2C_TXFIFO_EN (1u << 1) +#define HSI2C_TXFIFO_TRIGGER_LEVEL (0x20 << 16) +#define HSI2C_RXFIFO_TRIGGER_LEVEL (0x20 << 4)
+/* I2C_TRAILING_CTL Register bits */ +#define HSI2C_TRAILING_COUNT (0xff)
+/* I2C_INT_EN Register bits */ +#define HSI2C_INT_TX_ALMOSTEMPTY_EN (1u << 0) +#define HSI2C_INT_RX_ALMOSTFULL_EN (1u << 1) +#define HSI2C_INT_TRAILING_EN (1u << 6) +#define HSI2C_INT_I2C_EN (1u << 9)
+/* I2C_CONF Register bits */ +#define HSI2C_AUTO_MODE (1u << 31) +#define HSI2C_10BIT_ADDR_MODE (1u << 30) +#define HSI2C_HS_MODE (1u << 29)
+/* I2C_AUTO_CONF Register bits */ +#define HSI2C_READ_WRITE (1u << 16) +#define HSI2C_STOP_AFTER_TRANS (1u << 17) +#define HSI2C_MASTER_RUN (1u << 31)
+/* I2C_TIMEOUT Register bits */ +#define HSI2C_TIMEOUT_EN (1u << 31)
+/* I2C_TRANS_STATUS register bits */ +#define HSI2C_MASTER_BUSY (1u << 17) +#define HSI2C_SLAVE_BUSY (1u << 16) +#define HSI2C_NO_DEV (1u << 3) +#define HSI2C_NO_DEV_ACK (1u << 2) +#define HSI2C_TRANS_ABORT (1u << 1) +#define HSI2C_TRANS_DONE (1u << 0) +#define HSI2C_TIMEOUT_AUTO (0u << 0)
+#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10)
+/* Controller operating frequency, timing values for operation
- are calculated against this frequency
- */
+#define HSI2C_FS_TX_CLOCK 1000000
+/* S3C I2C Controller bits */ #define I2CSTAT_BSY 0x20 /* Busy bit */ #define I2CSTAT_NACK 0x01 /* Nack bit */ #define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ @@ -61,6 +116,7 @@
#define I2C_TIMEOUT 1 /* 1 second */
+#define HSI2C_TIMEOUT 100
/*
- For SPL boot some boards need i2c before SDRAM is initialised so force
@@ -120,9 +176,23 @@ static int WaitForXfer(struct s3c24x0_i2c *i2c) return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT; }
-static int IsACK(struct s3c24x0_i2c *i2c) +static int hsi2c_wait_for_irq(struct exynos5_hsi2c *i2c) {
return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
int i = HSI2C_TIMEOUT * 10;
int ret = I2C_NOK_TOUT;
while (i > 0) {
/* wait for a while and retry */
udelay(50);
if (readl(&i2c->usi_int_stat) &
(HSI2C_INT_I2C_EN | HSI2C_INT_TX_ALMOSTEMPTY_EN)) {
ret = I2C_OK;
break;
}
i--;
Please can we use the get_timer() method for timeouts instead of udelay()?
Sure thing
}
return ret;
}
static void ReadWriteByte(struct s3c24x0_i2c *i2c) @@ -130,6 +200,22 @@ static void ReadWriteByte(struct s3c24x0_i2c *i2c) writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); }
+static void hsi2c_clear_irqpd(struct exynos5_hsi2c *i2c) +{
writel(readl(&i2c->usi_int_stat), &i2c->usi_int_stat);
+}
+static int hsi2c_isack(struct exynos5_hsi2c *i2c) +{
return readl(&i2c->usi_trans_status) &
(HSI2C_NO_DEV | HSI2C_NO_DEV_ACK);
+}
+static int IsACK(struct s3c24x0_i2c *i2c)
There are two functions - hsi2c_isack() and IsAck(). What is the difference?
There are 3 similar functions re-written for I2C and HSI2C. May be i can use one function once the DT is completely implemented in this driver
+{
return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
+}
static struct s3c24x0_i2c *get_base_i2c(void) { #ifdef CONFIG_EXYNOS4 @@ -147,6 +233,18 @@ static struct s3c24x0_i2c *get_base_i2c(void) #endif }
+static struct exynos5_hsi2c *get_base_hsi2c(void) +{
struct exynos5_hsi2c *i2c = NULL;
int bus = g_current_bus;
if (proid_is_exynos5420())
i2c = (struct exynos5_hsi2c *)(EXYNOS5420_I2C_BASE +
((bus) * EXYNOS5_I2C_SPACING));
return i2c;
+}
This should come from the device tree information that you are decoding (the ->reg property).
I've not used the DT yet. I just followed the legacy code in the driver. Will implement it and send v2 soon.
static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) { ulong freq, pres = 16, div; @@ -174,6 +272,74 @@ static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); }
+static void hsi2c_ch_init(struct exynos5_hsi2c *i2c, int speed) +{
u32 i2c_timeout;
ulong clkin = get_i2c_clk();
u32 i2c_timing_s1;
u32 i2c_timing_s2;
u32 i2c_timing_s3;
u32 i2c_timing_sla;
unsigned int op_clk = HSI2C_FS_TX_CLOCK;
unsigned int n_clkdiv;
unsigned int t_start_su, t_start_hd;
unsigned int t_stop_su;
unsigned int t_data_su, t_data_hd;
unsigned int t_scl_l, t_scl_h;
unsigned int t_sr_release;
unsigned int t_ftl_cycle;
unsigned int i = 0, utemp0 = 0, utemp1 = 0, utemp2 = 0;
/* FPCLK / FI2C =
* (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
* uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2)
* uTemp1 = (TSCLK_L + TSCLK_H + 2)
* uTemp2 = TSCLK_L + TSCLK_H
*/
t_ftl_cycle = (readl(&i2c->usi_conf) >> 16) & 0x7;
utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle;
/* CLK_DIV max is 256 */
for (i = 0; i < 256; i++) {
utemp1 = utemp0 / (i + 1);
/* SCLK_L/H max is 256 / 2 */
if (utemp1 < 128) {
utemp2 = utemp1 - 2;
break;
}
}
n_clkdiv = i;
t_scl_l = utemp2 / 2;
t_scl_h = utemp2 / 2;
t_start_su = t_scl_l;
t_start_hd = t_scl_l;
t_stop_su = t_scl_l;
t_data_su = t_scl_l / 2;
t_data_hd = t_scl_l / 2;
t_sr_release = utemp2;
i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su <<
8;
i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0;
i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0;
i2c_timing_sla = t_data_hd << 0;
writel(HSI2C_TRAILING_COUNT, &i2c->usi_trailing_ctl);
/* Clear to enable Timeout */
i2c_timeout = readl(&i2c->usi_timeout);
i2c_timeout &= ~HSI2C_TIMEOUT_EN;
writel(i2c_timeout, &i2c->usi_timeout);
writel(readl(&i2c->usi_conf) | HSI2C_AUTO_MODE, &i2c->usi_conf);
/* Currently operating in Fast speed mode. */
writel(i2c_timing_s1, &i2c->usi_timing_fs1);
writel(i2c_timing_s2, &i2c->usi_timing_fs2);
writel(i2c_timing_s3, &i2c->usi_timing_fs3);
writel(i2c_timing_sla, &i2c->usi_timing_sla);
+}
/*
- MULTI BUS I2C support
*/ @@ -181,16 +347,18 @@ static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) #ifdef CONFIG_I2C_MULTI_BUS int i2c_set_bus_num(unsigned int bus) {
struct s3c24x0_i2c *i2c;
if ((bus < 0) || (bus >= CONFIG_MAX_I2C_NUM)) { debug("Bad bus: %d\n", bus); return -1; } g_current_bus = bus;
i2c = get_base_i2c();
i2c_ch_init(i2c, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
if (proid_is_exynos5420() && (bus > 3)) {
hsi2c_ch_init(get_base_hsi2c(),
CONFIG_SYS_I2C_SPEED);
} else
i2c_ch_init(get_base_i2c(), CONFIG_SYS_I2C_SPEED,
CONFIG_SYS_I2C_SLAVE); return 0;
} @@ -269,24 +437,182 @@ void i2c_init(int speed, int slaveadd) }
/*
- 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 hsi2c_send_stop(struct exynos5_hsi2c *i2c, int result) +{
int timeout;
int ret = I2C_NOK_TOUT;
/* Wait for the STOP to send and the bus to go idle */
for (timeout = HSI2C_TIMEOUT; timeout > 0; timeout -= 5) {
if (!(readl(&i2c->usi_trans_status) & HSI2C_MASTER_BUSY)) {
ret = I2C_OK;
goto out;
}
udelay(5);
}
- out:
/* Setting the STOP event to fire */
writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl);
writel(0x0, &i2c->usi_int_en);
return (result == I2C_OK) ? ret : result;
+}
+static int hsi2c_write(unsigned char chip,
unsigned char addr[],
unsigned char alen,
unsigned char data[],
unsigned short len)
+{
struct exynos5_hsi2c *i2c = get_base_hsi2c();
int i = 0, result = I2C_OK;
u32 i2c_auto_conf;
u32 stat;
/* Check I2C bus idle */
i = HSI2C_TIMEOUT * 20;
while ((readl(&i2c->usi_trans_status) & HSI2C_MASTER_BUSY)
&& (i > 0)) {
udelay(50);
i--;
}
It seems like perhaps you should have a function to wait until not busy and call it from the above function, and here.
Sure.
stat = readl(&i2c->usi_trans_status);
if (stat & HSI2C_MASTER_BUSY) {
debug("%s: bus busy\n", __func__);
return I2C_NOK_TOUT;
}
/* Disable TXFIFO and RXFIFO */
writel(0, &i2c->usi_fifo_ctl);
/* chip address */
writel(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr);
/* Enable interrupts */
writel((HSI2C_INT_I2C_EN | HSI2C_INT_TX_ALMOSTEMPTY_EN),
&i2c->usi_int_en);
/* usi_ctl enable i2c func, master write configure */
writel((HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER),
&i2c->usi_ctl);
/* i2c_conf configure */
writel(readl(&i2c->usi_conf) | HSI2C_AUTO_MODE, &i2c->usi_conf);
/* auto_conf for write length and stop configure */
i2c_auto_conf = ((len + alen) | HSI2C_STOP_AFTER_TRANS);
i2c_auto_conf &= ~HSI2C_READ_WRITE;
writel(i2c_auto_conf, &i2c->usi_auto_conf);
/* Master run, start xfer */
writel(readl(&i2c->usi_auto_conf) | HSI2C_MASTER_RUN,
&i2c->usi_auto_conf);
result = hsi2c_wait_for_irq(i2c);
if ((result == I2C_OK) && hsi2c_isack(i2c)) {
result = I2C_NACK;
goto err;
}
for (i = 0; i < alen && (result == I2C_OK); i++) {
writel(addr[i], &i2c->usi_txdata);
result = hsi2c_wait_for_irq(i2c);
}
for (i = 0; i < len && (result == I2C_OK); i++) {
writel(data[i], &i2c->usi_txdata);
result = hsi2c_wait_for_irq(i2c);
Do we have to wait for an irq after every tx byte?
Actually, FIFOs were not implemented at all in the u-boot driver. FIFOs are 64 depth and of variable sizes. Will test with them enabled
}
- err:
hsi2c_clear_irqpd(i2c);
return hsi2c_send_stop(i2c, result);
+}
+static int hsi2c_read(unsigned char chip,
unsigned char addr[],
unsigned char alen,
unsigned char data[],
unsigned short len,
int check)
This seems to have a lot of common code with the above. Should they be joined up?
Common initialization code can be split out.
+{
struct exynos5_hsi2c *i2c = get_base_hsi2c();
int i, result;
u32 i2c_auto_conf;
if (!check) {
result = hsi2c_write(chip, addr, alen, data, 0);
if (result != I2C_OK) {
debug("write failed Result = %d\n", result);
return result;
}
}
/* start read */
/* Disable TXFIFO and RXFIFO */
writel(0, &i2c->usi_fifo_ctl);
/* chip address */
writel(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr);
/* Enable interrupts */
writel(HSI2C_INT_I2C_EN, &i2c->usi_int_en);
/* i2c_conf configure */
outdent
writel(readl(&i2c->usi_conf) | HSI2C_AUTO_MODE, &i2c->usi_conf);
/* auto_conf, length and stop configure */
i2c_auto_conf = (len | HSI2C_STOP_AFTER_TRANS | HSI2C_READ_WRITE);
writel(i2c_auto_conf, &i2c->usi_auto_conf);
/* usi_ctl enable i2c func, master WRITE configure */
writel((HSI2C_RXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER),
&i2c->usi_ctl);
/* Master run, start xfer */
writel(readl(&i2c->usi_auto_conf) | HSI2C_MASTER_RUN,
&i2c->usi_auto_conf);
result = hsi2c_wait_for_irq(i2c);
if ((result == I2C_OK) && hsi2c_isack(i2c)) {
result = I2C_NACK;
goto err;
}
for (i = 0; i < len && (result == I2C_OK); i++) {
result = hsi2c_wait_for_irq(i2c);
data[i] = readl(&i2c->usi_rxdata);
}
I suppose the FIFOs are not used here. How larger are the FIFOs?
- err:
/* Stop and quit */
hsi2c_clear_irqpd(i2c);
return hsi2c_send_stop(i2c, result);
+}
+/*
- 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,
+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) {
struct s3c24x0_i2c *i2c = get_base_i2c(); int i, result; 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; } @@ -428,10 +754,8 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
int i2c_probe(uchar chip) {
struct s3c24x0_i2c *i2c; uchar buf[1];
i2c = get_base_i2c(); buf[0] = 0; /*
@@ -439,12 +763,15 @@ 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, I2C_READ, chip << 1, 0, 0, buf, 1) !=
I2C_OK;
if (proid_is_exynos5420() && (g_current_bus > 3))
return (hsi2c_read(chip, 0, 1, buf, 1, 1) != I2C_OK);
I think the type of i2c port should be set by the FDT, not hard-coded according to the chip type.
Will implement it, that would simplify a lot of checks for future aswell.
else
return i2c_transfer(
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;
@@ -476,9 +803,13 @@ int i2c_read(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();
ret = i2c_transfer(i2c, I2C_READ, chip << 1, &xaddr[4 - alen],
alen,
buffer, len);
if (proid_is_exynos5420() && (g_current_bus > 3))
ret = hsi2c_read(chip, &xaddr[4 - alen],
alen, buffer, len, 0);
else
ret = i2c_transfer(I2C_READ, chip << 1,
&xaddr[4 - alen], alen, buffer, len);
if (ret != 0) { debug("I2c read: failed %d\n", ret); return 1;
@@ -488,7 +819,6 @@ 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) {
@@ -518,31 +848,36 @@ 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, I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer,
len) != 0);
if (proid_is_exynos5420() && (g_current_bus > 3))
return (hsi2c_write(chip, &xaddr[4 - alen],
alen, buffer, len) != 0);
else
return (i2c_transfer(I2C_WRITE, chip << 1,
&xaddr[4 - alen], alen, buffer, len) != 0);
}
#ifdef CONFIG_OF_CONTROL void board_i2c_init(const void *blob) {
struct s3c24x0_i2c_bus *bus; int node_list[CONFIG_MAX_I2C_NUM]; int count, i; count = fdtdec_find_aliases_for_id(blob, "i2c", COMPAT_SAMSUNG_S3C2440_I2C, node_list, CONFIG_MAX_I2C_NUM);
for (i = 0; i < count; i++) {
struct s3c24x0_i2c_bus *bus; int node = node_list[i]; if (node <= 0) continue; bus = &i2c_bus[i];
bus->regs = (struct s3c24x0_i2c *)
fdtdec_get_addr(blob, node, "reg");
fdtdec_get_addr(blob, node, "reg");
Here you have bus->regs so you should use it in the driver.
bus->hsregs = (struct exynos5_hsi2c *)
fdtdec_get_addr(blob, node, "reg");
All of the ports here will be COMPAT_SAMSUNG_S3C2440_I2C. I think you need to find ports of a different type for the high speed i2c. Perhaps look at how tegra i2c handles having two different node types in the same driver.
This would help Simon, Thanks.
The type of a port needs to be stored in the bus structure, and I think this should be done even in the non-FDT case, to simplify the code.
Right, Will go through the references and submit a v2 with the DT changes fixed.
bus->id = pinmux_decode_periph_id(blob, node); bus->node = node; bus->bus_num = i2c_busses++;
@@ -589,7 +924,11 @@ int i2c_reset_port_fdt(const void *blob, int node) return -1; }
i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
if (proid_is_exynos5420() && (bus > 3))
hsi2c_ch_init(i2c->hsregs, CONFIG_SYS_I2C_SPEED);
else
i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED,
CONFIG_SYS_I2C_SLAVE); return 0;
} diff --git a/drivers/i2c/s3c24x0_i2c.h b/drivers/i2c/s3c24x0_i2c.h index a56d749..81ed19c 100644 --- a/drivers/i2c/s3c24x0_i2c.h +++ b/drivers/i2c/s3c24x0_i2c.h @@ -31,10 +31,43 @@ struct s3c24x0_i2c { u32 iiclc; };
+struct exynos5_hsi2c {
u32 usi_ctl;
u32 usi_fifo_ctl;
u32 usi_trailing_ctl;
u32 usi_clk_ctl;
u32 usi_clk_slot;
u32 spi_ctl;
u32 uart_ctl;
u32 res1;
u32 usi_int_en;
u32 usi_int_stat;
u32 usi_modem_stat;
u32 usi_error_stat;
u32 usi_fifo_stat;
u32 usi_txdata;
u32 usi_rxdata;
u32 res2;
u32 usi_conf;
u32 usi_auto_conf;
u32 usi_timeout;
u32 usi_manual_cmd;
u32 usi_trans_status;
u32 usi_timing_hs1;
u32 usi_timing_hs2;
u32 usi_timing_hs3;
u32 usi_timing_fs1;
u32 usi_timing_fs2;
u32 usi_timing_fs3;
u32 usi_timing_sla;
u32 i2c_addr;
+};
struct s3c24x0_i2c_bus { int node; /* device tree node */ int bus_num; /* i2c bus number */ struct s3c24x0_i2c *regs;
struct exynos5_hsi2c *hsregs; int id;
};
#endif /* _S3C24X0_I2C_H */
1.7.9.5
Regards,
Simon
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot