[U-Boot] [PATCH v4] i2c: s3c24xx: add hsi2c controller support

Naveen Krishna Chatradhi naveenkrishna.ch at gmail.com
Sat Apr 6 03:37:57 CEST 2013


Add support for hsi2c controller available on exynos5420.

Note: driver currently supports only fast speed mode 100kbps

Change-Id: I02555b1dc8f4ac21c50aa5158179768563c92f43
Signed-off-by: Naveen Krishna Chatradhi <ch.naveen at samsung.com>
Signed-off-by: R. Chandrasekar <rc.sekar at samsung.com>
Reviewed-by: Vadim Bendebury <vbendeb at google.com>
Reviewed-by: Simon Glass <sjg at google.com>
---
Changes since v3:

1. Implemented get_timer instead of while and udelay for master busy function
2. Use reg base address from device tree
3. Split the timing function to check for the errors
4. Implemented reset function for to recover from failure cases
5. Implemented a comat string for hsi2c to distingush the channels
6. Minor cosmotic changes

Note: FIFOs will be implemented in subsequent patches

 drivers/i2c/s3c24x0_i2c.c |  494 +++++++++++++++++++++++++++++++++++++++++----
 drivers/i2c/s3c24x0_i2c.h |   36 ++++
 2 files changed, 486 insertions(+), 44 deletions(-)

diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c
index 46d2506..32be91b 100644
--- a/drivers/i2c/s3c24x0_i2c.c
+++ b/drivers/i2c/s3c24x0_i2c.c
@@ -50,6 +50,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 +115,7 @@
 
 #define I2C_TIMEOUT 1		/* 1 second */
 
+#define	HSI2C_TIMEOUT	100
 
 /*
  * For SPL boot some boards need i2c before SDRAM is initialised so force
@@ -73,6 +128,15 @@ static struct s3c24x0_i2c_bus i2c_bus[CONFIG_MAX_I2C_NUM]
 			__attribute__((section(".data")));
 #endif
 
+static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx)
+{
+	if (bus_idx < i2c_busses)
+		return &i2c_bus[bus_idx];
+
+	debug("Undefined bus: %d\n", bus_idx);
+	return NULL;
+}
+
 #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
 static int GetI2CSDA(void)
 {
@@ -113,9 +177,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(100);
+		if (readl(&i2c->usi_int_stat) &
+			(HSI2C_INT_I2C_EN | HSI2C_INT_TX_ALMOSTEMPTY_EN)) {
+			ret = I2C_OK;
+			break;
+		}
+		i--;
+	}
+
+	return ret;
 }
 
 static void ReadWriteByte(struct s3c24x0_i2c *i2c)
@@ -123,6 +201,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)
+{
+	return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
+}
+
 static struct s3c24x0_i2c *get_base_i2c(void)
 {
 #ifdef CONFIG_EXYNOS4
@@ -167,6 +261,97 @@ static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd)
 	writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
 }
 
+static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus)
+{
+	struct exynos5_hsi2c *hsregs = i2c_bus->hsregs;
+	ulong clkin = get_i2c_clk();
+	unsigned int op_clk = HSI2C_FS_TX_CLOCK;
+	unsigned int i = 0, utemp0 = 0, utemp1 = 0;
+	unsigned int t_ftl_cycle;
+
+	/* 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(&hsregs->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);
+		if ((utemp1 < 512) && (utemp1 > 4)) {
+			i2c_bus->clk_cycle = utemp1 - 2;
+			i2c_bus->clk_div = i;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static void hsi2c_ch_init(struct s3c24x0_i2c_bus *i2c_bus)
+{
+	struct exynos5_hsi2c *hsregs = i2c_bus->hsregs;
+	unsigned int t_sr_release;
+	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;
+	u32 i2c_timing_s1;
+	u32 i2c_timing_s2;
+	u32 i2c_timing_s3;
+	u32 i2c_timing_sla;
+
+	n_clkdiv = i2c_bus->clk_div;
+	t_scl_l = i2c_bus->clk_cycle / 2;
+	t_scl_h = i2c_bus->clk_cycle / 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 = i2c_bus->clk_cycle;
+
+	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, &hsregs->usi_trailing_ctl);
+
+	/* Clear to enable Timeout */
+	clrsetbits_le32(&hsregs->usi_timeout, HSI2C_TIMEOUT_EN, 0);
+
+	writel(readl(&hsregs->usi_conf) | HSI2C_AUTO_MODE, &hsregs->usi_conf);
+
+	/* Currently operating in Fast speed mode. */
+	writel(i2c_timing_s1, &hsregs->usi_timing_fs1);
+	writel(i2c_timing_s2, &hsregs->usi_timing_fs2);
+	writel(i2c_timing_s3, &hsregs->usi_timing_fs3);
+	writel(i2c_timing_sla, &hsregs->usi_timing_sla);
+}
+
+/* SW reset for the high speed bus */
+static void exynos5_i2c_reset(struct s3c24x0_i2c_bus *i2c_bus)
+{
+	struct exynos5_hsi2c *i2c = i2c_bus->hsregs;
+	u32 i2c_ctl;
+
+	/* Set and clear the bit for reset */
+	i2c_ctl = readl(&i2c->usi_ctl);
+	i2c_ctl |= HSI2C_SW_RST;
+	writel(i2c_ctl, &i2c->usi_ctl);
+
+	i2c_ctl = readl(&i2c->usi_ctl);
+	i2c_ctl &= ~HSI2C_SW_RST;
+	writel(i2c_ctl, &i2c->usi_ctl);
+
+	/* Initialize the configure registers */
+	hsi2c_ch_init(i2c_bus);
+}
+
 /*
  * MULTI BUS I2C support
  */
@@ -174,7 +359,7 @@ 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;
+	struct s3c24x0_i2c_bus *i2c_bus;
 
 	if ((bus < 0) || (bus >= CONFIG_MAX_I2C_NUM)) {
 		debug("Bad bus: %d\n", bus);
@@ -182,8 +367,16 @@ int i2c_set_bus_num(unsigned int bus)
 	}
 
 	g_current_bus = bus;
-	i2c = get_base_i2c();
-	i2c_ch_init(i2c, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+	i2c_bus = get_bus(i2c_get_bus_num());
+
+	if (i2c_bus->is_highspeed) {
+		if (hsi2c_get_clk_details(i2c_bus))
+			return -1;
+		hsi2c_ch_init(i2c_bus);
+	} else {
+		i2c_ch_init(i2c_bus->regs, CONFIG_SYS_I2C_SPEED,
+			    CONFIG_SYS_I2C_SLAVE);
+	}
 
 	return 0;
 }
@@ -261,6 +454,165 @@ void i2c_init(int speed, int slaveadd)
 	i2c_ch_init(i2c, speed, slaveadd);
 }
 
+static int hsi2c_master_busy(struct exynos5_hsi2c *i2c)
+{
+	ulong start = get_timer(0);
+
+	/* Check I2C bus idle */
+	while ((readl(&i2c->usi_trans_status) & HSI2C_MASTER_BUSY)) {
+		if (get_timer(start) > (HSI2C_TIMEOUT))
+			return I2C_NOK_TOUT;
+		udelay(10);
+	}
+
+	return I2C_OK;
+}
+
+/*
+ * 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 ret = hsi2c_master_busy(i2c);
+
+	/* 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(struct exynos5_hsi2c *i2c,
+			unsigned char chip,
+			unsigned char addr[],
+			unsigned char alen,
+			unsigned char data[],
+			unsigned short len)
+{
+	int i = 0, result = I2C_OK;
+	u32 i2c_auto_conf;
+	int ret = hsi2c_master_busy(i2c);
+
+	if (ret) {
+		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);
+	}
+
+ err:
+	hsi2c_clear_irqpd(i2c);
+	return hsi2c_send_stop(i2c, result);
+}
+
+static int hsi2c_read(struct exynos5_hsi2c *i2c,
+			unsigned char chip,
+			unsigned char addr[],
+			unsigned char alen,
+			unsigned char data[],
+			unsigned short len,
+			int check)
+{
+	int i, result;
+	u32 i2c_auto_conf;
+
+	if (!check) {
+		result =  hsi2c_write(i2c, 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 */
+	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);
+		udelay(100);
+	}
+ err:
+	/* Stop and quit */
+	hsi2c_clear_irqpd(i2c);
+	return hsi2c_send_stop(i2c, result);
+}
+
 /*
  * cmd_type is 0 for write, 1 for read.
  *
@@ -279,7 +631,6 @@ static int i2c_transfer(struct s3c24x0_i2c *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;
 	}
@@ -419,23 +770,29 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
 
 int i2c_probe(uchar chip)
 {
-	struct s3c24x0_i2c *i2c;
+	struct s3c24x0_i2c_bus *i2c;
 	uchar buf[1];
+	int ret;
 
-	i2c = get_base_i2c();
+	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).
-	 */
-	return i2c_transfer(i2c, I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK;
+	if (i2c->is_highspeed) {
+		ret = hsi2c_read(i2c->hsregs,
+				chip, 0, 1, buf, 1, 1);
+	} else {
+		ret = i2c_transfer(i2c->regs,
+				I2C_READ, chip << 1, 0, 0, buf, 1);
+	}
+
+	return ret != I2C_OK;
 }
 
 int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
 {
-	struct s3c24x0_i2c *i2c;
+	struct s3c24x0_i2c_bus *i2c_bus;
 	uchar xaddr[4];
 	int ret;
 
@@ -467,10 +824,19 @@ 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 (ret != 0) {
+	i2c_bus = get_bus(g_current_bus);
+	if (!i2c_bus)
+		return -1;
+
+	if (i2c_bus->is_highspeed)
+		ret = hsi2c_read(i2c_bus->hsregs, chip, &xaddr[4 - alen],
+						alen, buffer, len, 0);
+	else
+		ret = i2c_transfer(i2c_bus->regs, I2C_READ, chip << 1,
+				&xaddr[4 - alen], alen, buffer, len);
+
+	if (ret) {
+		exynos5_i2c_reset(i2c_bus);
 		debug("I2c read: failed %d\n", ret);
 		return 1;
 	}
@@ -479,8 +845,9 @@ 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;
+	struct s3c24x0_i2c_bus *i2c_bus;
 	uchar xaddr[4];
+	int ret;
 
 	if (alen > 4) {
 		debug("I2C write: addr len %d not supported\n", alen);
@@ -509,45 +876,77 @@ 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);
+	i2c_bus = get_bus(g_current_bus);
+	if (!i2c_bus)
+		return -1;
+
+	if (i2c_bus->is_highspeed)
+		ret = hsi2c_write(i2c_bus->hsregs, chip, &xaddr[4 - alen],
+					alen, buffer, len);
+	else
+		ret = i2c_transfer(i2c_bus->regs, I2C_WRITE, chip << 1,
+				&xaddr[4 - alen], alen, buffer, len);
+
+
+	if (ret != 0) {
+		exynos5_i2c_reset(i2c_bus);
+		return 1;
+	}
+
+	return 0;
 }
 
-#ifdef CONFIG_OF_CONTROL
-void board_i2c_init(const void *blob)
+static void process_nodes(const void *blob, int node_list[], int count,
+			 int is_highspeed)
 {
-	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);
+	struct s3c24x0_i2c_bus *bus;
+	int i;
 
 	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");
+		bus->is_highspeed = is_highspeed;
+
+		if (is_highspeed)
+			bus->hsregs = (struct exynos5_hsi2c *)
+					fdtdec_get_addr(blob, node, "reg");
+		else
+			bus->regs = (struct s3c24x0_i2c *)
+					fdtdec_get_addr(blob, node, "reg");
+
 		bus->id = pinmux_decode_periph_id(blob, node);
 		bus->node = node;
 		bus->bus_num = i2c_busses++;
 		exynos_pinmux_config(bus->id, 0);
+
+		/* Mark position as used */
+		node_list[i] = -1;
 	}
 }
 
-static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx)
+#ifdef CONFIG_OF_CONTROL
+void board_i2c_init(const void *blob)
 {
-	if (bus_idx < i2c_busses)
-		return &i2c_bus[bus_idx];
+	int node_list[CONFIG_MAX_I2C_NUM];
+	int count;
 
-	debug("Undefined bus: %d\n", bus_idx);
-	return NULL;
+	/* First get the normal i2c ports */
+	count = fdtdec_find_aliases_for_id(blob, "i2c",
+		COMPAT_SAMSUNG_S3C2440_I2C, node_list,
+		CONFIG_MAX_I2C_NUM);
+	process_nodes(blob, node_list, count, 0);
+
+	/* Now look for high speed i2c ports */
+	count = fdtdec_find_aliases_for_id(blob, "i2c",
+		COMPAT_SAMSUNG_EXYNOS5_I2C, node_list,
+		CONFIG_MAX_I2C_NUM);
+	process_nodes(blob, node_list, count, 1);
+
+	return;
 }
 
 int i2c_get_bus_num_fdt(int node)
@@ -565,7 +964,7 @@ int i2c_get_bus_num_fdt(int node)
 
 int i2c_reset_port_fdt(const void *blob, int node)
 {
-	struct s3c24x0_i2c_bus *i2c;
+	struct s3c24x0_i2c_bus *i2c_bus;
 	int bus;
 
 	bus = i2c_get_bus_num_fdt(node);
@@ -574,13 +973,20 @@ int i2c_reset_port_fdt(const void *blob, int node)
 		return -1;
 	}
 
-	i2c = get_bus(bus);
-	if (!i2c) {
+	i2c_bus = get_bus(bus);
+	if (!i2c_bus) {
 		debug("get_bus() failed for node node %d\n", node);
 		return -1;
 	}
 
-	i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+	if (i2c_bus->is_highspeed) {
+		if (hsi2c_get_clk_details(i2c_bus))
+			return -1;
+		hsi2c_ch_init(i2c_bus);
+	} else {
+		i2c_ch_init(i2c_bus->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..55931e8 100644
--- a/drivers/i2c/s3c24x0_i2c.h
+++ b/drivers/i2c/s3c24x0_i2c.h
@@ -31,10 +31,46 @@ 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 is_highspeed;	/* High speed type, rather than I2C */
 	int id;
+	unsigned clk_cycle;
+	unsigned clk_div;
 };
 #endif /* _S3C24X0_I2C_H */
-- 
1.7.9.5



More information about the U-Boot mailing list