From eabd5d8d70ecf7072ac5b7aef2fe239553346dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bie=C3=9Fmann?= Date: Thu, 10 Feb 2011 00:30:04 +0000 Subject: soft_i2c: add necessary includes for AVR32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Bießmann --- drivers/i2c/soft_i2c.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index 1595c0714..ae3c57392 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -30,6 +30,9 @@ #include #include #endif +#if defined(CONFIG_AVR32) +#include +#endif #if defined(CONFIG_AT91FAMILY) #include #include -- cgit v1.2.3 From 7f86bd576f7570b4d19d016f768c869d88b3d0ab Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:26 +0000 Subject: i2c: Staticize local functions in mxc i2c driver Some functions in the MXC i2c driver were not static, fix this by making them so. Signed-off-by: Marek Vasut Cc: Heiko Schocher Cc: Stefano Babic --- drivers/i2c/mxc_i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 18270b9de..a73b10b9c 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -115,7 +115,7 @@ static uint8_t i2c_imx_get_clk(unsigned int rate) /* * Set I2C Bus speed */ -int bus_i2c_set_bus_speed(void *base, int speed) +static int bus_i2c_set_bus_speed(void *base, int speed) { struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; u8 clk_idx = i2c_imx_get_clk(speed); @@ -133,7 +133,7 @@ int bus_i2c_set_bus_speed(void *base, int speed) /* * Get I2C Speed */ -unsigned int bus_i2c_get_bus_speed(void *base) +static unsigned int bus_i2c_get_bus_speed(void *base) { struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; u8 clk_idx = readb(&i2c_regs->ifdr); -- cgit v1.2.3 From f32a470ed3dfff493473bc25c7d0a01920507eed Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:28 +0000 Subject: i2c: mxs: Abstract out the MXS I2C speed setup This patch pulls out the I2C speed setup from the i2c_init() call and implements the bus configuration lookup table with register values that needs to be programmed into the I2C IP to run at particular speed. This patch is a first step towards implementing run-time I2C bus speed configuration for the MXS I2C IP. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam --- drivers/i2c/mxs_i2c.c | 57 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 2a193c220..98f6e8c3d 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -210,34 +210,51 @@ int i2c_probe(uchar chip) return ret; } +static struct mxs_i2c_speed_table { + uint32_t speed; + uint32_t timing0; + uint32_t timing1; +} mxs_i2c_tbl[] = { + { + 100000, + (0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) | + (0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET), + (0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) | + (0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET) + }, + { + 400000, + (0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) | + (0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET), + (0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) | + (0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET), + } +}; + +static struct mxs_i2c_speed_table *mxs_i2c_speed_to_cfg(uint32_t speed) +{ + int i; + for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) + if (mxs_i2c_tbl[i].speed == speed) + return &mxs_i2c_tbl[i]; + return NULL; +} + void i2c_init(int speed, int slaveadd) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); - mxs_i2c_reset(); - - switch (speed) { - case 100000: - writel((0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) | - (0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing0); - writel((0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) | - (0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing1); - break; - case 400000: - writel((0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) | - (0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing0); - writel((0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) | - (0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing1); - break; - default: + if (!spd) { printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); return; } + mxs_i2c_reset(); + + writel(spd->timing0, &i2c_regs->hw_i2c_timing0); + writel(spd->timing1, &i2c_regs->hw_i2c_timing1); + writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), &i2c_regs->hw_i2c_timing2); -- cgit v1.2.3 From a06f590f7fce2f484d3bc0bff48a7aa90b70f13d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:29 +0000 Subject: i2c: mxs: Implement i2c_get/set_bus_speed() This patch implements the setup and retrieval functions for the I2C bus speed on the MXS I2C IP. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam --- drivers/i2c/mxs_i2c.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 98f6e8c3d..4152242d9 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -240,6 +240,51 @@ static struct mxs_i2c_speed_table *mxs_i2c_speed_to_cfg(uint32_t speed) return NULL; } +static uint32_t mxs_i2c_cfg_to_speed(uint32_t timing0, uint32_t timing1) +{ + int i; + for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) { + if (mxs_i2c_tbl[i].timing0 != timing0) + continue; + if (mxs_i2c_tbl[i].timing1 != timing1) + continue; + return mxs_i2c_tbl[i].speed; + } + + return 0; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); + + if (!spd) { + printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); + return -EINVAL; + } + + writel(spd->timing0, &i2c_regs->hw_i2c_timing0); + writel(spd->timing1, &i2c_regs->hw_i2c_timing1); + + writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | + (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing2); + + return 0; +} + +unsigned int i2c_get_bus_speed(void) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t timing0, timing1; + + timing0 = readl(&i2c_regs->hw_i2c_timing0); + timing1 = readl(&i2c_regs->hw_i2c_timing1); + + return mxs_i2c_cfg_to_speed(timing0, timing1); +} + void i2c_init(int speed, int slaveadd) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; -- cgit v1.2.3 From a157e0d5f6aa0b86bd7b6ae09d11b12a4a736d97 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:30 +0000 Subject: i2c: mxs: Use i2c_set_bus_speed() in i2c_init() Use i2c_set_bus_speed() in i2c_init() within the mxs i2c driver to avoid duplication of code. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam --- drivers/i2c/mxs_i2c.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 4152242d9..3771452b9 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -287,22 +287,8 @@ unsigned int i2c_get_bus_speed(void) void i2c_init(int speed, int slaveadd) { - struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; - struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); - - if (!spd) { - printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); - return; - } - mxs_i2c_reset(); - - writel(spd->timing0, &i2c_regs->hw_i2c_timing0); - writel(spd->timing1, &i2c_regs->hw_i2c_timing1); - - writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | - (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing2); + i2c_set_bus_speed(speed); return; } -- cgit v1.2.3 From aff36ea92ec0700cd9241bf01e72956a3ab9600e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:31 +0000 Subject: i2c: mxs: Fix TIMING2 register value According to FSL, the value in the TIMING2 register shall be 0x00300030 instead of what's written in the datasheet. This new value correlates with older STMP36xx datasheet. Issues were detected in Linux when this register was misconfigured, so write this correct value. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam --- drivers/i2c/mxs_i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 3771452b9..006fb919f 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -267,8 +267,8 @@ int i2c_set_bus_speed(unsigned int speed) writel(spd->timing0, &i2c_regs->hw_i2c_timing0); writel(spd->timing1, &i2c_regs->hw_i2c_timing1); - writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | - (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), + writel((0x0030 << I2C_TIMING2_BUS_FREE_OFFSET) | + (0x0030 << I2C_TIMING2_LEADIN_COUNT_OFFSET), &i2c_regs->hw_i2c_timing2); return 0; -- cgit v1.2.3 From 1e2fc0d19bac9bf4f62d259169e902e700a18bad Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 30 Nov 2012 18:17:06 +0000 Subject: mxs: i2c: Restore speed setting after block reset The I2C block reset configures the I2C bus speed to strange value. Read the I2C speed from the block before reseting the block and restore it afterwards, so the I2C operates correctly. This issue can be replicated by doing unsuccessful I2C transfer, after such transfer finishes, the I2C block clock speed is misconfigured. Signed-off-by: Marek Vasut Cc: Heiko Schocher Cc: Fabio Estevam --- drivers/i2c/mxs_i2c.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 006fb919f..73a6659dd 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -40,6 +40,7 @@ void mxs_i2c_reset(void) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; int ret; + int speed = i2c_get_bus_speed(); ret = mxs_reset_block(&i2c_regs->hw_i2c_ctrl0_reg); if (ret) { @@ -53,6 +54,8 @@ void mxs_i2c_reset(void) &i2c_regs->hw_i2c_ctrl1_clr); writel(I2C_QUEUECTRL_PIO_QUEUE_MODE, &i2c_regs->hw_i2c_queuectrl_set); + + i2c_set_bus_speed(speed); } void mxs_i2c_setup_read(uint8_t chip, int len) -- cgit v1.2.3 From fa5e2845a867a3715240ff221aaec2825b5c73df Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 30 Nov 2012 18:17:07 +0000 Subject: mxs: i2c: Implement algorithm to set up arbitrary i2c speed This algorithm computes the values of TIMING{0,1,2} registers for the MX28 I2C block. This algorithm was derived by using a scope, but the result seems correct. The resulting values programmed into the registers do not correlate with the contents in datasheet. When using the values from the datasheet, the I2C clock were completely wrong. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam Cc: Wolfgang Denk --- drivers/i2c/mxs_i2c.c | 87 ++++++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 73a6659dd..b907f7b37 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -213,62 +214,39 @@ int i2c_probe(uchar chip) return ret; } -static struct mxs_i2c_speed_table { - uint32_t speed; - uint32_t timing0; - uint32_t timing1; -} mxs_i2c_tbl[] = { - { - 100000, - (0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) | - (0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET), - (0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) | - (0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET) - }, - { - 400000, - (0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) | - (0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET), - (0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) | - (0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET), - } -}; - -static struct mxs_i2c_speed_table *mxs_i2c_speed_to_cfg(uint32_t speed) -{ - int i; - for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) - if (mxs_i2c_tbl[i].speed == speed) - return &mxs_i2c_tbl[i]; - return NULL; -} - -static uint32_t mxs_i2c_cfg_to_speed(uint32_t timing0, uint32_t timing1) -{ - int i; - for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) { - if (mxs_i2c_tbl[i].timing0 != timing0) - continue; - if (mxs_i2c_tbl[i].timing1 != timing1) - continue; - return mxs_i2c_tbl[i].speed; - } - - return 0; -} - int i2c_set_bus_speed(unsigned int speed) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; - struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); + /* + * The timing derivation algorithm. There is no documentation for this + * algorithm available, it was derived by using the scope and fiddling + * with constants until the result observed on the scope was good enough + * for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be + * possible to assume the algorithm works for other frequencies as well. + * + * Note it was necessary to cap the frequency on both ends as it's not + * possible to configure completely arbitrary frequency for the I2C bus + * clock. + */ + uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); + uint32_t base = ((clk / speed) - 38) / 2; + uint16_t high_count = base + 3; + uint16_t low_count = base - 3; + uint16_t rcv_count = (high_count * 3) / 4; + uint16_t xmit_count = low_count / 4; + + if (speed > 540000) { + printf("MXS I2C: Speed too high (%d Hz)\n", speed); + return -EINVAL; + } - if (!spd) { - printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); + if (speed < 12000) { + printf("MXS I2C: Speed too low (%d Hz)\n", speed); return -EINVAL; } - writel(spd->timing0, &i2c_regs->hw_i2c_timing0); - writel(spd->timing1, &i2c_regs->hw_i2c_timing1); + writel((high_count << 16) | rcv_count, &i2c_regs->hw_i2c_timing0); + writel((low_count << 16) | xmit_count, &i2c_regs->hw_i2c_timing1); writel((0x0030 << I2C_TIMING2_BUS_FREE_OFFSET) | (0x0030 << I2C_TIMING2_LEADIN_COUNT_OFFSET), @@ -280,12 +258,15 @@ int i2c_set_bus_speed(unsigned int speed) unsigned int i2c_get_bus_speed(void) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; - uint32_t timing0, timing1; + uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); + uint32_t timing0; timing0 = readl(&i2c_regs->hw_i2c_timing0); - timing1 = readl(&i2c_regs->hw_i2c_timing1); - - return mxs_i2c_cfg_to_speed(timing0, timing1); + /* + * This is a reverse version of the algorithm presented in + * i2c_set_bus_speed(). Please refer there for details. + */ + return clk / ((((timing0 >> 16) - 3) * 2) + 38); } void i2c_init(int speed, int slaveadd) -- cgit v1.2.3 From 66839773a82cb56ebeb2b99322151f8a57a30efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Mon, 3 Dec 2012 06:07:21 +0000 Subject: omap24xx_i2c: Handle OMAP5 like OMAP2,3,4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OMAP5 has 8b i2c data register field, like OMAP2, 3 and 4. Handle in the same way. This fixes the following error on OMAP5: OMAP5430 EVM # mmc rescan timed out in wait_for_bb: I2C_STAT=1410 twl6035: could not turn on LDO9. Signed-off-by: Vincent Stehlé --- drivers/i2c/omap24xx_i2c.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 094305fdf..af454f901 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -179,7 +179,8 @@ static int i2c_read_byte(u8 devaddr, u16 regoffset, u8 alen, u8 *value) if (status & I2C_STAT_XRDY) { w = tmpbuf[i++]; #if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX)) w |= tmpbuf[i++] << 8; #endif writew(w, &i2c_base->data); @@ -209,7 +210,8 @@ static int i2c_read_byte(u8 devaddr, u16 regoffset, u8 alen, u8 *value) } if (status & I2C_STAT_RRDY) { #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX) *value = readb(&i2c_base->data); #else *value = readw(&i2c_base->data); @@ -239,7 +241,8 @@ static void flush_fifo(void) stat = readw(&i2c_base->stat); if (stat == I2C_STAT_RRDY) { #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX) readb(&i2c_base->data); #else readw(&i2c_base->data); @@ -289,7 +292,8 @@ int i2c_probe(uchar chip) if (status & I2C_STAT_RRDY) { res = 0; #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX) readb(&i2c_base->data); #else readw(&i2c_base->data); @@ -376,7 +380,8 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) if (status & I2C_STAT_XRDY) { w = (i < 0) ? tmpbuf[2+i] : buffer[i]; #if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX)) w |= ((++i < 0) ? tmpbuf[2+i] : buffer[i]) << 8; #endif writew(w, &i2c_base->data); -- cgit v1.2.3 From c86d9ed3820bbd89e264261fb022bdf9448bfde7 Mon Sep 17 00:00:00 2001 From: Piotr Wilczek Date: Tue, 20 Nov 2012 02:19:05 +0000 Subject: drivers:i2c: Modify I2C driver for Exynos4 This patch modifies the S3C i2c driver to support both Exynos4 and Exynos5 Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park CC: Minkyu Kang --- drivers/i2c/s3c24x0_i2c.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index 9bc4c7f1d..90d297a28 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -27,7 +27,7 @@ */ #include -#ifdef CONFIG_EXYNOS5 +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) #include #include #else @@ -62,7 +62,7 @@ static unsigned int g_current_bus; /* Stores Current I2C Bus */ -#ifndef CONFIG_EXYNOS5 +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) static int GetI2CSDA(void) { struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); @@ -121,7 +121,12 @@ static void ReadWriteByte(struct s3c24x0_i2c *i2c) static struct s3c24x0_i2c *get_base_i2c(void) { -#ifdef CONFIG_EXYNOS5 +#ifdef CONFIG_EXYNOS4 + struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c() + + (EXYNOS4_I2C_SPACING + * g_current_bus)); + return i2c; +#elif defined CONFIG_EXYNOS5 struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c() + (EXYNOS5_I2C_SPACING * g_current_bus)); @@ -134,7 +139,7 @@ static struct s3c24x0_i2c *get_base_i2c(void) static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) { ulong freq, pres = 16, div; -#ifdef CONFIG_EXYNOS5 +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) freq = get_i2c_clk(); #else freq = get_PCLK(); @@ -188,7 +193,7 @@ unsigned int i2c_get_bus_num(void) void i2c_init(int speed, int slaveadd) { struct s3c24x0_i2c *i2c; -#ifndef CONFIG_EXYNOS5 +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); #endif int i; @@ -204,7 +209,7 @@ void i2c_init(int speed, int slaveadd) i--; } -#ifndef CONFIG_EXYNOS5 +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) { #ifdef CONFIG_S3C2410 ulong old_gpecon = readl(&gpio->gpecon); @@ -248,7 +253,7 @@ void i2c_init(int speed, int slaveadd) writel(old_gpecon, &gpio->pgcon); #endif } -#endif /* #ifndef CONFIG_EXYNOS5 */ +#endif /* #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) */ i2c_ch_init(i2c, speed, slaveadd); } -- cgit v1.2.3 From ac6e2fe6e4afe54a9c7ef0d93827a86e264814f2 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:15 +0000 Subject: designware_i2c.c: Added the support for MULTI_BUS This patch adds the capability to switch between 10 different I2C busses (from 0 to 9). Signed-off-by: Armando Visconti --- drivers/i2c/designware_i2c.c | 82 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index bf64a2a64..4e4bfd4f5 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -26,7 +26,12 @@ #include #include "designware_i2c.h" -static struct i2c_regs *const i2c_regs_p = +#ifdef CONFIG_I2C_MULTI_BUS +static unsigned int bus_initialized[CONFIG_SYS_I2C_BUS_MAX]; +static unsigned int current_bus = 0; +#endif + +static struct i2c_regs *i2c_regs_p = (struct i2c_regs *)CONFIG_SYS_I2C_BASE; /* @@ -150,6 +155,10 @@ void i2c_init(int speed, int slaveadd) enbl = readl(&i2c_regs_p->ic_enable); enbl |= IC_ENABLE_0B; writel(enbl, &i2c_regs_p->ic_enable); + +#ifdef CONFIG_I2C_MULTI_BUS + bus_initialized[current_bus] = 1; +#endif } /* @@ -344,3 +353,74 @@ int i2c_probe(uchar chip) return ret; } + +#ifdef CONFIG_I2C_MULTI_BUS +int i2c_set_bus_num(unsigned int bus) +{ + switch (bus) { + case 0: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE; + break; +#ifdef CONFIG_SYS_I2C_BASE1 + case 1: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE1; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE2 + case 2: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE2; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE3 + case 3: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE3; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE4 + case 4: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE4; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE5 + case 5: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE5; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE6 + case 6: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE6; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE7 + case 7: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE7; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE8 + case 8: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE8; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE9 + case 9: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE9; + break; +#endif + default: + printf("Bad bus: %d\n", bus); + return -1; + } + + current_bus = bus; + + if (!bus_initialized[current_bus]) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +int i2c_get_bus_num(void) +{ + return current_bus; +} +#endif -- cgit v1.2.3 From 491739bb7456b95bb863421f6cd76af0ff9b797c Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:16 +0000 Subject: designware_i2c: Added s/w generation of stop bit In the newer versions of designware i2c IP there is the possibility of configuring it with IC_EMPTYFIFO_HOLD_MASTER_EN=1, which basically requires the s/w to generate the stop bit condition directly, as the h/w will not automatically generate it when TX_FIFO is empty. To avoid generation of an extra 0x0 byte sent as data, the IC_STOP command must be sent along with the last IC_CMD. This patch always writes bit[9] of ic_data_cmd even in the older versions, assuming that it is a noop there. Signed-off-by: Armando Visconti --- drivers/i2c/designware_i2c.c | 11 ++++++++--- drivers/i2c/designware_i2c.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 4e4bfd4f5..eab313102 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -283,7 +283,10 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) start_time_rx = get_timer(0); while (len) { - writel(IC_CMD, &i2c_regs_p->ic_cmd_data); + if (len == 1) + writel(IC_CMD | IC_STOP, &i2c_regs_p->ic_cmd_data); + else + writel(IC_CMD, &i2c_regs_p->ic_cmd_data); if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) { *buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data); @@ -322,9 +325,11 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) start_time_tx = get_timer(0); while (len) { if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) { - writel(*buffer, &i2c_regs_p->ic_cmd_data); + if (--len == 0) + writel(*buffer | IC_STOP, &i2c_regs_p->ic_cmd_data); + else + writel(*buffer, &i2c_regs_p->ic_cmd_data); buffer++; - len--; start_time_tx = get_timer(0); } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h index 03b520ed4..e004152ac 100644 --- a/drivers/i2c/designware_i2c.h +++ b/drivers/i2c/designware_i2c.h @@ -95,6 +95,7 @@ struct i2c_regs { /* i2c data buffer and command register definitions */ #define IC_CMD 0x0100 +#define IC_STOP 0x0200 /* i2c interrupt status register definitions */ #define IC_GEN_CALL 0x0800 -- cgit v1.2.3 From 5b8439bbdea9ad8c120cda408a97ed2e55e943d3 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:17 +0000 Subject: designware_i2c: Fixed the setting of the i2c bus speed There are three couple (hcnt/lcnt) of registers for each speed (SS/FS/HS). The driver needs to set the proper couple of regs according to what speed we are setting. Signed-off-by: Armando Visconti --- drivers/i2c/designware_i2c.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index eab313102..665387091 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -44,7 +44,6 @@ static void set_speed(int i2c_spd) { unsigned int cntl; unsigned int hcnt, lcnt; - unsigned int high, low; unsigned int enbl; /* to set speed cltr must be disabled */ @@ -52,39 +51,38 @@ static void set_speed(int i2c_spd) enbl &= ~IC_ENABLE_0B; writel(enbl, &i2c_regs_p->ic_enable); - cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK)); switch (i2c_spd) { case IC_SPEED_MODE_MAX: cntl |= IC_CON_SPD_HS; - high = MIN_HS_SCL_HIGHTIME; - low = MIN_HS_SCL_LOWTIME; + hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_hs_scl_hcnt); + lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_hs_scl_lcnt); break; case IC_SPEED_MODE_STANDARD: cntl |= IC_CON_SPD_SS; - high = MIN_SS_SCL_HIGHTIME; - low = MIN_SS_SCL_LOWTIME; + hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_ss_scl_hcnt); + lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_ss_scl_lcnt); break; case IC_SPEED_MODE_FAST: default: cntl |= IC_CON_SPD_FS; - high = MIN_FS_SCL_HIGHTIME; - low = MIN_FS_SCL_LOWTIME; + hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt); + lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt); break; } writel(cntl, &i2c_regs_p->ic_con); - hcnt = (IC_CLK * high) / NANO_TO_MICRO; - writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt); - - lcnt = (IC_CLK * low) / NANO_TO_MICRO; - writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt); - - /* re-enable i2c ctrl back now that speed is set */ + /* Enable back i2c now speed set */ enbl |= IC_ENABLE_0B; writel(enbl, &i2c_regs_p->ic_enable); } -- cgit v1.2.3 From ea31b7a7be738d7f4048f9fbe20fe02356999d44 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:18 +0000 Subject: designware_i2c.h: Fixed the correct values for SCL low/high time Signed-off-by: Armando Visconti --- drivers/i2c/designware_i2c.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h index e004152ac..0dc8884ed 100644 --- a/drivers/i2c/designware_i2c.h +++ b/drivers/i2c/designware_i2c.h @@ -65,9 +65,9 @@ struct i2c_regs { /* High and low times in different speed modes (in ns) */ #define MIN_SS_SCL_HIGHTIME 4000 -#define MIN_SS_SCL_LOWTIME 5000 -#define MIN_FS_SCL_HIGHTIME 800 -#define MIN_FS_SCL_LOWTIME 1700 +#define MIN_SS_SCL_LOWTIME 4700 +#define MIN_FS_SCL_HIGHTIME 600 +#define MIN_FS_SCL_LOWTIME 1300 #define MIN_HS_SCL_HIGHTIME 60 #define MIN_HS_SCL_LOWTIME 160 -- cgit v1.2.3 From d40d914c06e73c2a7823c2a6dd164d1c10529f84 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:19 +0000 Subject: designware_i2c.h: Define IC_CLK only if not already defined in config file Signed-off-by: Armando Visconti --- drivers/i2c/designware_i2c.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h index 0dc8884ed..2faf4a871 100644 --- a/drivers/i2c/designware_i2c.h +++ b/drivers/i2c/designware_i2c.h @@ -60,7 +60,9 @@ struct i2c_regs { u32 ic_tx_abrt_source; }; +#if !defined(IC_CLK) #define IC_CLK 166 +#endif #define NANO_TO_MICRO 1000 /* High and low times in different speed modes (in ns) */ -- cgit v1.2.3 From 32057717e06a4e703fdf3774671cea14554de76b Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:27 -0600 Subject: env: Add a baudrate env handler Remove the hard-coded baudrate handler and use a callback instead Signed-off-by: Joe Hershberger --- drivers/serial/serial.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index f5f43a6dd..1f8955a0f 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -32,6 +33,11 @@ DECLARE_GLOBAL_DATA_PTR; static struct serial_device *serial_devices; static struct serial_device *serial_current; +/* + * Table with supported baudrates (defined in config_xyz.h) + */ +static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE; +#define N_BAUDRATES (sizeof(baudrate_table) / sizeof(baudrate_table[0])) /** * serial_null() - Void registration routine of a serial driver @@ -45,6 +51,70 @@ static void serial_null(void) { } +/** + * on_baudrate() - Update the actual baudrate when the env var changes + * + * This will check for a valid baudrate and only apply it if valid. + */ +static int on_baudrate(const char *name, const char *value, enum env_op op, + int flags) +{ + int i; + int baudrate; + + switch (op) { + case env_op_create: + case env_op_overwrite: + /* + * Switch to new baudrate if new baudrate is supported + */ + baudrate = simple_strtoul(value, NULL, 10); + + /* Not actually changing */ + if (gd->baudrate == baudrate) + return 0; + + for (i = 0; i < N_BAUDRATES; ++i) { + if (baudrate == baudrate_table[i]) + break; + } + if (i == N_BAUDRATES) { + if ((flags & H_FORCE) == 0) + printf("## Baudrate %d bps not supported\n", + baudrate); + return 1; + } + if ((flags & H_INTERACTIVE) != 0) { + printf("## Switch baudrate to %d" + " bps and press ENTER ...\n", baudrate); + udelay(50000); + } + + gd->baudrate = baudrate; +#if defined(CONFIG_PPC) || defined(CONFIG_MCF52x2) + gd->bd->bi_baudrate = baudrate; +#endif + + serial_setbrg(); + + udelay(50000); + + if ((flags & H_INTERACTIVE) != 0) + while (1) { + if (getc() == '\r') + break; + } + + return 0; + case env_op_delete: + printf("## Baudrate may not be deleted\n"); + return 1; + default: + return 0; + } +} +U_BOOT_ENV_CALLBACK(baudrate, on_baudrate); + /** * serial_initfunc() - Forward declare of driver registration routine * @name: Name of the real driver registration routine. -- cgit v1.2.3