[U-Boot] [PATCH 0/4] Clock fix and MXC I2C rework series

First three patches in this series fix problems with clock decoding on MX5, the remaining patch reworks the MXC I2C driver.
This is tested on: * MX51 EfikaSB * MX51 EfikaMX * MX53 QSB
Marek Vasut (4): MX5: Modify the PLL decoding algorithm MX5: Add AHB clock reporting and fix IPG clock reporting MX5: Clean up the output of "clocks" command I2C: mxc_i2c rework
arch/arm/cpu/armv7/mx5/clock.c | 125 +++++++-- arch/arm/include/asm/arch-mx5/imx-regs.h | 3 + drivers/i2c/mxc_i2c.c | 423 ++++++++++++++++++++---------- 3 files changed, 394 insertions(+), 157 deletions(-)

The PLL decoding algorithm didn't take into account many configuration bits. Adjust it according to Linux kernel. Also, add PLL4 for MX53.
Signed-off-by: Marek Vasut marek.vasut@gmail.com --- arch/arm/cpu/armv7/mx5/clock.c | 77 ++++++++++++++++++++++++++---- arch/arm/include/asm/arch-mx5/imx-regs.h | 3 + 2 files changed, 70 insertions(+), 10 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 00610a0..9f37f7f 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -29,11 +29,13 @@ #include <asm/arch/imx-regs.h> #include <asm/arch/crm_regs.h> #include <asm/arch/clock.h> +#include <div64.h>
enum pll_clocks { PLL1_CLOCK = 0, PLL2_CLOCK, PLL3_CLOCK, + PLL4_CLOCK, PLL_CLOCKS, };
@@ -41,25 +43,76 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { [PLL1_CLOCK] = (struct mxc_pll_reg *)PLL1_BASE_ADDR, [PLL2_CLOCK] = (struct mxc_pll_reg *)PLL2_BASE_ADDR, [PLL3_CLOCK] = (struct mxc_pll_reg *)PLL3_BASE_ADDR, +#ifdef CONFIG_MX53 + [PLL4_CLOCK] = (struct mxc_pll_reg *)PLL4_BASE_ADDR, +#endif };
+#define MXC_DPLLC_CTL_HFSM (1 << 7) +#define MXC_DPLLC_CTL_DPDCK0_2_EN (1 << 12) + +#define MXC_DPLLC_OP_PDF_MASK 0xf +#define MXC_DPLLC_OP_MFI_MASK (0xf << 4) +#define MXC_DPLLC_OP_MFI_OFFSET 4 + +#define MXC_DPLLC_MFD_MFD_MASK 0x7ffffff + +#define MXC_DPLLC_MFN_MFN_MASK 0x7ffffff + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE;
/* - * Calculate the frequency of this pll. + * Calculate the frequency of PLLn. */ -static u32 decode_pll(struct mxc_pll_reg *pll, u32 infreq) +static uint32_t decode_pll(struct mxc_pll_reg *pll, uint32_t infreq) { - u32 mfi, mfn, mfd, pd; + uint32_t ctrl, op, mfd, mfn, mfi, pdf, ret; + uint64_t refclk, temp; + int32_t mfn_abs; + + ctrl = readl(&pll->ctrl); + + if (ctrl & MXC_DPLLC_CTL_HFSM) { + mfn = __raw_readl(&pll->hfs_mfn); + mfd = __raw_readl(&pll->hfs_mfd); + op = __raw_readl(&pll->hfs_op); + } else { + mfn = __raw_readl(&pll->mfn); + mfd = __raw_readl(&pll->mfd); + op = __raw_readl(&pll->op); + }
- mfn = __raw_readl(&pll->mfn); - mfd = __raw_readl(&pll->mfd) + 1; - mfi = __raw_readl(&pll->op); - pd = (mfi & 0xF) + 1; - mfi = (mfi >> 4) & 0xF; - mfi = (mfi >= 5) ? mfi : 5; + mfd &= MXC_DPLLC_MFD_MFD_MASK; + mfn &= MXC_DPLLC_MFN_MFN_MASK; + pdf = op & MXC_DPLLC_OP_PDF_MASK; + mfi = (op & MXC_DPLLC_OP_MFI_MASK) >> MXC_DPLLC_OP_MFI_OFFSET; + + /* 21.2.3 */ + if (mfi < 5) + mfi = 5; + + /* Sign extend */ + if (mfn >= 0x04000000) { + mfn |= 0xfc000000; + mfn_abs = -mfn; + } else + mfn_abs = mfn; + + refclk = infreq * 2; + if (ctrl & MXC_DPLLC_CTL_DPDCK0_2_EN) + refclk *= 2; + + refclk /= pdf + 1; + temp = refclk * mfn_abs; + do_div(temp, mfd + 1); + ret = refclk * mfi; + + if ((int)mfn < 0) + ret -= temp; + else + ret += temp;
- return ((4 * (infreq / 1000) * (mfi * mfd + mfn)) / (mfd * pd)) * 1000; + return ret; }
/* @@ -279,6 +332,10 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("pll2: %dMHz\n", freq / 1000000); freq = decode_pll(mxc_plls[PLL3_CLOCK], CONFIG_SYS_MX5_HCLK); printf("pll3: %dMHz\n", freq / 1000000); +#ifdef CONFIG_MX53 + freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK); + printf("pll4: %dMHz\n", freq / 1000000); +#endif printf("ipg clock : %dHz\n", mxc_get_clock(MXC_IPG_CLK)); printf("ipg per clock : %dHz\n", mxc_get_clock(MXC_IPG_PERCLK));
diff --git a/arch/arm/include/asm/arch-mx5/imx-regs.h b/arch/arm/include/asm/arch-mx5/imx-regs.h index a4e680b..8a0f9e6 100644 --- a/arch/arm/include/asm/arch-mx5/imx-regs.h +++ b/arch/arm/include/asm/arch-mx5/imx-regs.h @@ -100,6 +100,9 @@ #define PLL1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00080000) #define PLL2_BASE_ADDR (AIPS2_BASE_ADDR + 0x00084000) #define PLL3_BASE_ADDR (AIPS2_BASE_ADDR + 0x00088000) +#ifdef CONFIG_MX53 +#define PLL4_BASE_ADDR (AIPS2_BASE_ADDR + 0x0008c000) +#endif #define AHBMAX_BASE_ADDR (AIPS2_BASE_ADDR + 0x00094000) #define IIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x00098000) #define CSU_BASE_ADDR (AIPS2_BASE_ADDR + 0x0009C000)

On Thu, Sep 15, 2011 at 8:09 AM, Marek Vasut marek.vasut@gmail.com wrote:
The PLL decoding algorithm didn't take into account many configuration bits. Adjust it according to Linux kernel. Also, add PLL4 for MX53.
Signed-off-by: Marek Vasut marek.vasut@gmail.com
arch/arm/cpu/armv7/mx5/clock.c | 77 ++++++++++++++++++++++++++---- arch/arm/include/asm/arch-mx5/imx-regs.h | 3 + 2 files changed, 70 insertions(+), 10 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 00610a0..9f37f7f 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -29,11 +29,13 @@ #include <asm/arch/imx-regs.h> #include <asm/arch/crm_regs.h> #include <asm/arch/clock.h> +#include <div64.h>
enum pll_clocks { PLL1_CLOCK = 0, PLL2_CLOCK, PLL3_CLOCK,
- PLL4_CLOCK,
PLL_CLOCKS, };
@@ -41,25 +43,76 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { [PLL1_CLOCK] = (struct mxc_pll_reg *)PLL1_BASE_ADDR, [PLL2_CLOCK] = (struct mxc_pll_reg *)PLL2_BASE_ADDR, [PLL3_CLOCK] = (struct mxc_pll_reg *)PLL3_BASE_ADDR, +#ifdef CONFIG_MX53
- [PLL4_CLOCK] = (struct mxc_pll_reg *)PLL4_BASE_ADDR,
+#endif };
+#define MXC_DPLLC_CTL_HFSM (1 << 7) +#define MXC_DPLLC_CTL_DPDCK0_2_EN (1 << 12)
+#define MXC_DPLLC_OP_PDF_MASK 0xf +#define MXC_DPLLC_OP_MFI_MASK (0xf << 4) +#define MXC_DPLLC_OP_MFI_OFFSET 4
+#define MXC_DPLLC_MFD_MFD_MASK 0x7ffffff
+#define MXC_DPLLC_MFN_MFN_MASK 0x7ffffff
Can we put this stuff to crm_reg.h file?
struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE;
/*
- Calculate the frequency of this pll.
- Calculate the frequency of PLLn.
*/ -static u32 decode_pll(struct mxc_pll_reg *pll, u32 infreq) +static uint32_t decode_pll(struct mxc_pll_reg *pll, uint32_t infreq)
I found that you prefer to using the uint32_t than u32? what's the rule for u-boot? Wolfgang?
{
- u32 mfi, mfn, mfd, pd;
- uint32_t ctrl, op, mfd, mfn, mfi, pdf, ret;
- uint64_t refclk, temp;
- int32_t mfn_abs;
- ctrl = readl(&pll->ctrl);
- if (ctrl & MXC_DPLLC_CTL_HFSM) {
- mfn = __raw_readl(&pll->hfs_mfn);
- mfd = __raw_readl(&pll->hfs_mfd);
- op = __raw_readl(&pll->hfs_op);
- } else {
- mfn = __raw_readl(&pll->mfn);
- mfd = __raw_readl(&pll->mfd);
- op = __raw_readl(&pll->op);
- }
- mfn = __raw_readl(&pll->mfn);
- mfd = __raw_readl(&pll->mfd) + 1;
- mfi = __raw_readl(&pll->op);
- pd = (mfi & 0xF) + 1;
- mfi = (mfi >> 4) & 0xF;
- mfi = (mfi >= 5) ? mfi : 5;
- mfd &= MXC_DPLLC_MFD_MFD_MASK;
- mfn &= MXC_DPLLC_MFN_MFN_MASK;
- pdf = op & MXC_DPLLC_OP_PDF_MASK;
- mfi = (op & MXC_DPLLC_OP_MFI_MASK) >> MXC_DPLLC_OP_MFI_OFFSET;
- /* 21.2.3 */
- if (mfi < 5)
- mfi = 5;
- /* Sign extend */
- if (mfn >= 0x04000000) {
- mfn |= 0xfc000000;
- mfn_abs = -mfn;
- } else
- mfn_abs = mfn;
- refclk = infreq * 2;
- if (ctrl & MXC_DPLLC_CTL_DPDCK0_2_EN)
- refclk *= 2;
- refclk /= pdf + 1;
- temp = refclk * mfn_abs;
- do_div(temp, mfd + 1);
- ret = refclk * mfi;
- if ((int)mfn < 0)
- ret -= temp;
- else
- ret += temp;
- return ((4 * (infreq / 1000) * (mfi * mfd + mfn)) / (mfd * pd)) * 1000;
- return ret;
}
This decode schema is correct. Thanks,
/* @@ -279,6 +332,10 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("pll2: %dMHz\n", freq / 1000000); freq = decode_pll(mxc_plls[PLL3_CLOCK], CONFIG_SYS_MX5_HCLK); printf("pll3: %dMHz\n", freq / 1000000); +#ifdef CONFIG_MX53
- freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK);
- printf("pll4: %dMHz\n", freq / 1000000);
+#endif printf("ipg clock : %dHz\n", mxc_get_clock(MXC_IPG_CLK)); printf("ipg per clock : %dHz\n", mxc_get_clock(MXC_IPG_PERCLK));
In fact, no one use PLL4 in u-boot. But I'm not against to put it in.
diff --git a/arch/arm/include/asm/arch-mx5/imx-regs.h b/arch/arm/include/asm/arch-mx5/imx-regs.h index a4e680b..8a0f9e6 100644 --- a/arch/arm/include/asm/arch-mx5/imx-regs.h +++ b/arch/arm/include/asm/arch-mx5/imx-regs.h @@ -100,6 +100,9 @@ #define PLL1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00080000) #define PLL2_BASE_ADDR (AIPS2_BASE_ADDR + 0x00084000) #define PLL3_BASE_ADDR (AIPS2_BASE_ADDR + 0x00088000) +#ifdef CONFIG_MX53 +#define PLL4_BASE_ADDR (AIPS2_BASE_ADDR + 0x0008c000) +#endif #define AHBMAX_BASE_ADDR (AIPS2_BASE_ADDR + 0x00094000) #define IIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x00098000) #define CSU_BASE_ADDR (AIPS2_BASE_ADDR + 0x0009C000) --
Tested on i.mx53evk, it outputs:
MX53EVK U-Boot > clocks PLL1 800 MHz PLL2 400 MHz PLL3 216 MHz PLL4 594 MHz
AHB 133333 kHz IPG 66666 kHz IPG PERCLK 20000 kHz
Test-by: Jason Liu Jason.hui@linaro.org
Jason Liu
1.7.5.4

On 09/22/2011 05:20 AM, Jason Hui wrote:
On Thu, Sep 15, 2011 at 8:09 AM, Marek Vasut marek.vasut@gmail.com wrote:
The PLL decoding algorithm didn't take into account many configuration bits. Adjust it according to Linux kernel. Also, add PLL4 for MX53.
Signed-off-by: Marek Vasut marek.vasut@gmail.com
arch/arm/cpu/armv7/mx5/clock.c | 77 ++++++++++++++++++++++++++---- arch/arm/include/asm/arch-mx5/imx-regs.h | 3 + 2 files changed, 70 insertions(+), 10 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 00610a0..9f37f7f 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -29,11 +29,13 @@ #include <asm/arch/imx-regs.h> #include <asm/arch/crm_regs.h> #include <asm/arch/clock.h> +#include <div64.h>
enum pll_clocks { PLL1_CLOCK = 0, PLL2_CLOCK, PLL3_CLOCK,
PLL4_CLOCK, PLL_CLOCKS,
};
@@ -41,25 +43,76 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { [PLL1_CLOCK] = (struct mxc_pll_reg *)PLL1_BASE_ADDR, [PLL2_CLOCK] = (struct mxc_pll_reg *)PLL2_BASE_ADDR, [PLL3_CLOCK] = (struct mxc_pll_reg *)PLL3_BASE_ADDR, +#ifdef CONFIG_MX53
[PLL4_CLOCK] = (struct mxc_pll_reg *)PLL4_BASE_ADDR,
+#endif };
+#define MXC_DPLLC_CTL_HFSM (1 << 7) +#define MXC_DPLLC_CTL_DPDCK0_2_EN (1 << 12)
+#define MXC_DPLLC_OP_PDF_MASK 0xf +#define MXC_DPLLC_OP_MFI_MASK (0xf << 4) +#define MXC_DPLLC_OP_MFI_OFFSET 4
+#define MXC_DPLLC_MFD_MFD_MASK 0x7ffffff
+#define MXC_DPLLC_MFN_MFN_MASK 0x7ffffff
Can we put this stuff to crm_reg.h file?
Right. Clock related defines are in the crm_reg.h file
Best regards, Stefano Babic

The PLL decoding algorithm didn't take into account many configuration bits. Adjust it according to Linux kernel. Also, add PLL4 for MX53.
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Jason Hui jason.hui@linaro.org --- arch/arm/cpu/armv7/mx5/clock.c | 66 +++++++++++++++++++++++++----- arch/arm/include/asm/arch-mx5/crm_regs.h | 11 +++++ arch/arm/include/asm/arch-mx5/imx-regs.h | 3 + 3 files changed, 70 insertions(+), 10 deletions(-)
V2: Move register bitfields to crm_regs.h
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 00610a0..c28c1c3 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -29,11 +29,13 @@ #include <asm/arch/imx-regs.h> #include <asm/arch/crm_regs.h> #include <asm/arch/clock.h> +#include <div64.h>
enum pll_clocks { PLL1_CLOCK = 0, PLL2_CLOCK, PLL3_CLOCK, + PLL4_CLOCK, PLL_CLOCKS, };
@@ -41,25 +43,65 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { [PLL1_CLOCK] = (struct mxc_pll_reg *)PLL1_BASE_ADDR, [PLL2_CLOCK] = (struct mxc_pll_reg *)PLL2_BASE_ADDR, [PLL3_CLOCK] = (struct mxc_pll_reg *)PLL3_BASE_ADDR, +#ifdef CONFIG_MX53 + [PLL4_CLOCK] = (struct mxc_pll_reg *)PLL4_BASE_ADDR, +#endif };
struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE;
/* - * Calculate the frequency of this pll. + * Calculate the frequency of PLLn. */ -static u32 decode_pll(struct mxc_pll_reg *pll, u32 infreq) +static uint32_t decode_pll(struct mxc_pll_reg *pll, uint32_t infreq) { - u32 mfi, mfn, mfd, pd; + uint32_t ctrl, op, mfd, mfn, mfi, pdf, ret; + uint64_t refclk, temp; + int32_t mfn_abs; + + ctrl = readl(&pll->ctrl); + + if (ctrl & MXC_DPLLC_CTL_HFSM) { + mfn = __raw_readl(&pll->hfs_mfn); + mfd = __raw_readl(&pll->hfs_mfd); + op = __raw_readl(&pll->hfs_op); + } else { + mfn = __raw_readl(&pll->mfn); + mfd = __raw_readl(&pll->mfd); + op = __raw_readl(&pll->op); + }
- mfn = __raw_readl(&pll->mfn); - mfd = __raw_readl(&pll->mfd) + 1; - mfi = __raw_readl(&pll->op); - pd = (mfi & 0xF) + 1; - mfi = (mfi >> 4) & 0xF; - mfi = (mfi >= 5) ? mfi : 5; + mfd &= MXC_DPLLC_MFD_MFD_MASK; + mfn &= MXC_DPLLC_MFN_MFN_MASK; + pdf = op & MXC_DPLLC_OP_PDF_MASK; + mfi = (op & MXC_DPLLC_OP_MFI_MASK) >> MXC_DPLLC_OP_MFI_OFFSET; + + /* 21.2.3 */ + if (mfi < 5) + mfi = 5; + + /* Sign extend */ + if (mfn >= 0x04000000) { + mfn |= 0xfc000000; + mfn_abs = -mfn; + } else + mfn_abs = mfn; + + refclk = infreq * 2; + if (ctrl & MXC_DPLLC_CTL_DPDCK0_2_EN) + refclk *= 2; + + refclk /= pdf + 1; + temp = refclk * mfn_abs; + do_div(temp, mfd + 1); + ret = refclk * mfi; + + if ((int)mfn < 0) + ret -= temp; + else + ret += temp;
- return ((4 * (infreq / 1000) * (mfi * mfd + mfn)) / (mfd * pd)) * 1000; + return ret; }
/* @@ -279,6 +321,10 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("pll2: %dMHz\n", freq / 1000000); freq = decode_pll(mxc_plls[PLL3_CLOCK], CONFIG_SYS_MX5_HCLK); printf("pll3: %dMHz\n", freq / 1000000); +#ifdef CONFIG_MX53 + freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK); + printf("pll4: %dMHz\n", freq / 1000000); +#endif printf("ipg clock : %dHz\n", mxc_get_clock(MXC_IPG_CLK)); printf("ipg per clock : %dHz\n", mxc_get_clock(MXC_IPG_PERCLK));
diff --git a/arch/arm/include/asm/arch-mx5/crm_regs.h b/arch/arm/include/asm/arch-mx5/crm_regs.h index 4ed8eb3..fcc0e36 100644 --- a/arch/arm/include/asm/arch-mx5/crm_regs.h +++ b/arch/arm/include/asm/arch-mx5/crm_regs.h @@ -200,4 +200,15 @@ struct mxc_ccm_reg { /* Define the bits in register CLPCR */ #define MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS (0x1 << 18)
+#define MXC_DPLLC_CTL_HFSM (1 << 7) +#define MXC_DPLLC_CTL_DPDCK0_2_EN (1 << 12) + +#define MXC_DPLLC_OP_PDF_MASK 0xf +#define MXC_DPLLC_OP_MFI_MASK (0xf << 4) +#define MXC_DPLLC_OP_MFI_OFFSET 4 + +#define MXC_DPLLC_MFD_MFD_MASK 0x7ffffff + +#define MXC_DPLLC_MFN_MFN_MASK 0x7ffffff + #endif /* __ARCH_ARM_MACH_MX51_CRM_REGS_H__ */ diff --git a/arch/arm/include/asm/arch-mx5/imx-regs.h b/arch/arm/include/asm/arch-mx5/imx-regs.h index 098c300..d069209 100644 --- a/arch/arm/include/asm/arch-mx5/imx-regs.h +++ b/arch/arm/include/asm/arch-mx5/imx-regs.h @@ -100,6 +100,9 @@ #define PLL1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00080000) #define PLL2_BASE_ADDR (AIPS2_BASE_ADDR + 0x00084000) #define PLL3_BASE_ADDR (AIPS2_BASE_ADDR + 0x00088000) +#ifdef CONFIG_MX53 +#define PLL4_BASE_ADDR (AIPS2_BASE_ADDR + 0x0008c000) +#endif #define AHBMAX_BASE_ADDR (AIPS2_BASE_ADDR + 0x00094000) #define IIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x00098000) #define CSU_BASE_ADDR (AIPS2_BASE_ADDR + 0x0009C000)

Signed-off-by: Marek Vasut marek.vasut@gmail.com --- arch/arm/cpu/armv7/mx5/clock.c | 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 9f37f7f..a9fe81c 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -152,18 +152,35 @@ static u32 get_periph_clk(void) }
/* + * Get the rate of ahb clock. + */ +static u32 get_ahb_clk(void) +{ + uint32_t freq, div, reg; + + freq = decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK); + + reg = __raw_readl(&mxc_ccm->cbcdr); + div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >> + MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1; + + return freq / div; +} + +/* * Get the rate of ipg clock. */ static u32 get_ipg_clk(void) { - u32 ahb_podf, ipg_podf; - - ahb_podf = __raw_readl(&mxc_ccm->cbcdr); - ipg_podf = (ahb_podf & MXC_CCM_CBCDR_IPG_PODF_MASK) >> - MXC_CCM_CBCDR_IPG_PODF_OFFSET; - ahb_podf = (ahb_podf & MXC_CCM_CBCDR_AHB_PODF_MASK) >> - MXC_CCM_CBCDR_AHB_PODF_OFFSET; - return get_periph_clk() / ((ahb_podf + 1) * (ipg_podf + 1)); + uint32_t freq, reg, div; + + freq = get_ahb_clk(); + + reg = __raw_readl(&mxc_ccm->cbcdr); + div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >> + MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1; + + return freq / div; }
/* @@ -290,7 +307,7 @@ unsigned int mxc_get_clock(enum mxc_clock clk) case MXC_ARM_CLK: return get_mcu_main_clk(); case MXC_AHB_CLK: - break; + return get_ahb_clk(); case MXC_IPG_CLK: return get_ipg_clk(); case MXC_IPG_PERCLK: @@ -336,6 +353,7 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK); printf("pll4: %dMHz\n", freq / 1000000); #endif + printf("ahb clock : %dHz\n", mxc_get_clock(MXC_AHB_CLK)); printf("ipg clock : %dHz\n", mxc_get_clock(MXC_IPG_CLK)); printf("ipg per clock : %dHz\n", mxc_get_clock(MXC_IPG_PERCLK));

Hi, Marek
On Thu, Sep 15, 2011 at 8:09 AM, Marek Vasut marek.vasut@gmail.com wrote:
Signed-off-by: Marek Vasut marek.vasut@gmail.com
arch/arm/cpu/armv7/mx5/clock.c | 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 9f37f7f..a9fe81c 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -152,18 +152,35 @@ static u32 get_periph_clk(void) }
/*
- Get the rate of ahb clock.
- */
+static u32 get_ahb_clk(void) +{
- uint32_t freq, div, reg;
- freq = decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK);
- reg = __raw_readl(&mxc_ccm->cbcdr);
- div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >>
- MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1;
- return freq / div;
+}
This is not correct, ahb clock is not fixed-source from PLL2.
Currently implementation,we did not take care the ahb_bypass clock setting and don't consider the lp-apm mode for main-bus clock input.
Jason Liu

On Thursday, September 22, 2011 05:05:43 AM Jason Hui wrote:
Hi, Marek
On Thu, Sep 15, 2011 at 8:09 AM, Marek Vasut marek.vasut@gmail.com wrote:
Signed-off-by: Marek Vasut marek.vasut@gmail.com
arch/arm/cpu/armv7/mx5/clock.c | 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 9f37f7f..a9fe81c 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -152,18 +152,35 @@ static u32 get_periph_clk(void) }
/*
- Get the rate of ahb clock.
- */
+static u32 get_ahb_clk(void) +{
uint32_t freq, div, reg;
freq = decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK);
reg = __raw_readl(&mxc_ccm->cbcdr);
div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >>
MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1;
return freq / div;
+}
This is not correct, ahb clock is not fixed-source from PLL2.
Currently implementation,we did not take care the ahb_bypass clock setting and don't consider the lp-apm mode for main-bus clock input.
There is no "current implementation". Besides, that's how the BootROM sets it up so until someone needs it, this is ok.
Jason Liu

Hi, Marek,
On Thu, Sep 22, 2011 at 11:41 AM, Marek Vasut marek.vasut@gmail.com wrote:
On Thursday, September 22, 2011 05:05:43 AM Jason Hui wrote:
Hi, Marek
On Thu, Sep 15, 2011 at 8:09 AM, Marek Vasut marek.vasut@gmail.com wrote:
Signed-off-by: Marek Vasut marek.vasut@gmail.com
arch/arm/cpu/armv7/mx5/clock.c | 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 9f37f7f..a9fe81c 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -152,18 +152,35 @@ static u32 get_periph_clk(void) }
/*
- Get the rate of ahb clock.
- */
+static u32 get_ahb_clk(void) +{
- uint32_t freq, div, reg;
- freq = decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK);
- reg = __raw_readl(&mxc_ccm->cbcdr);
- div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >>
- MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1;
- return freq / div;
+}
This is not correct, ahb clock is not fixed-source from PLL2.
Currently implementation,we did not take care the ahb_bypass clock setting and don't consider the lp-apm mode for main-bus clock input.
There is no "current implementation".
The current implementation is for the get_periph_clk(..), anyway, the abh clock should be:
ahb_clk = get_periph_clk()/ahb_pdof, ipg_clk = ahb_clk/ipg_podf,
Besides, that's how the BootROM sets it up so until someone needs it, this is ok.
u-boot still can change the BootROM setting.
Jason Liu
Jason Liu

Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Jason Hui jason.hui@linaro.org --- arch/arm/cpu/armv7/mx5/clock.c | 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
V2: Use get_periph_clock() as AHB clocksource.
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 9f37f7f..b87b29e 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -152,18 +152,35 @@ static u32 get_periph_clk(void) }
/* + * Get the rate of ahb clock. + */ +static u32 get_ahb_clk(void) +{ + uint32_t freq, div, reg; + + freq = get_periph_clk(); + + reg = __raw_readl(&mxc_ccm->cbcdr); + div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >> + MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1; + + return freq / div; +} + +/* * Get the rate of ipg clock. */ static u32 get_ipg_clk(void) { - u32 ahb_podf, ipg_podf; - - ahb_podf = __raw_readl(&mxc_ccm->cbcdr); - ipg_podf = (ahb_podf & MXC_CCM_CBCDR_IPG_PODF_MASK) >> - MXC_CCM_CBCDR_IPG_PODF_OFFSET; - ahb_podf = (ahb_podf & MXC_CCM_CBCDR_AHB_PODF_MASK) >> - MXC_CCM_CBCDR_AHB_PODF_OFFSET; - return get_periph_clk() / ((ahb_podf + 1) * (ipg_podf + 1)); + uint32_t freq, reg, div; + + freq = get_ahb_clk(); + + reg = __raw_readl(&mxc_ccm->cbcdr); + div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >> + MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1; + + return freq / div; }
/* @@ -290,7 +307,7 @@ unsigned int mxc_get_clock(enum mxc_clock clk) case MXC_ARM_CLK: return get_mcu_main_clk(); case MXC_AHB_CLK: - break; + return get_ahb_clk(); case MXC_IPG_CLK: return get_ipg_clk(); case MXC_IPG_PERCLK: @@ -336,6 +353,7 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK); printf("pll4: %dMHz\n", freq / 1000000); #endif + printf("ahb clock : %dHz\n", mxc_get_clock(MXC_AHB_CLK)); printf("ipg clock : %dHz\n", mxc_get_clock(MXC_IPG_CLK)); printf("ipg per clock : %dHz\n", mxc_get_clock(MXC_IPG_PERCLK));

Hi, Marek,
On Fri, Sep 23, 2011 at 3:20 AM, Marek Vasut marek.vasut@gmail.com wrote:
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Jason Hui jason.hui@linaro.org
Here is: Jason Liu jason.hui@linaro.org
arch/arm/cpu/armv7/mx5/clock.c | 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
V2: Use get_periph_clock() as AHB clocksource.
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 9f37f7f..b87b29e 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -152,18 +152,35 @@ static u32 get_periph_clk(void) }
/*
- Get the rate of ahb clock.
- */
+static u32 get_ahb_clk(void) +{
- uint32_t freq, div, reg;
- freq = get_periph_clk();
- reg = __raw_readl(&mxc_ccm->cbcdr);
- div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >>
- MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1;
- return freq / div;
+}
+/* * Get the rate of ipg clock. */ static u32 get_ipg_clk(void) {
- u32 ahb_podf, ipg_podf;
- ahb_podf = __raw_readl(&mxc_ccm->cbcdr);
- ipg_podf = (ahb_podf & MXC_CCM_CBCDR_IPG_PODF_MASK) >>
- MXC_CCM_CBCDR_IPG_PODF_OFFSET;
- ahb_podf = (ahb_podf & MXC_CCM_CBCDR_AHB_PODF_MASK) >>
- MXC_CCM_CBCDR_AHB_PODF_OFFSET;
- return get_periph_clk() / ((ahb_podf + 1) * (ipg_podf + 1));
- uint32_t freq, reg, div;
- freq = get_ahb_clk();
- reg = __raw_readl(&mxc_ccm->cbcdr);
- div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >>
- MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1;
- return freq / div;
}
/* @@ -290,7 +307,7 @@ unsigned int mxc_get_clock(enum mxc_clock clk) case MXC_ARM_CLK: return get_mcu_main_clk(); case MXC_AHB_CLK:
- break;
- return get_ahb_clk();
case MXC_IPG_CLK: return get_ipg_clk(); case MXC_IPG_PERCLK: @@ -336,6 +353,7 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK); printf("pll4: %dMHz\n", freq / 1000000); #endif
- printf("ahb clock : %dHz\n", mxc_get_clock(MXC_AHB_CLK));
printf("ipg clock : %dHz\n", mxc_get_clock(MXC_IPG_CLK)); printf("ipg per clock : %dHz\n", mxc_get_clock(MXC_IPG_PERCLK));
Looks good to me.
Acked-by: Jason Liu jason.hui@linaro.org
BR Jason Liu
-- 1.7.5.4

The new output looks like this:
clocks
PLL1 800 MHz PLL2 665 MHz PLL3 216 MHz
AHB 133000 kHz IPG 66500 kHz IPG PERCLK 665000 kHz
Signed-off-by: Marek Vasut marek.vasut@gmail.com --- arch/arm/cpu/armv7/mx5/clock.c | 16 +++++++++------- 1 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index a9fe81c..dd3615d 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -344,18 +344,20 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) u32 freq;
freq = decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK); - printf("pll1: %dMHz\n", freq / 1000000); + printf("PLL1 %8d MHz\n", freq / 1000000); freq = decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK); - printf("pll2: %dMHz\n", freq / 1000000); + printf("PLL2 %8d MHz\n", freq / 1000000); freq = decode_pll(mxc_plls[PLL3_CLOCK], CONFIG_SYS_MX5_HCLK); - printf("pll3: %dMHz\n", freq / 1000000); + printf("PLL3 %8d MHz\n", freq / 1000000); #ifdef CONFIG_MX53 freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK); - printf("pll4: %dMHz\n", freq / 1000000); + printf("PLL4 %8d MHz\n", freq / 1000000); #endif - printf("ahb clock : %dHz\n", mxc_get_clock(MXC_AHB_CLK)); - printf("ipg clock : %dHz\n", mxc_get_clock(MXC_IPG_CLK)); - printf("ipg per clock : %dHz\n", mxc_get_clock(MXC_IPG_PERCLK)); + + printf("\n"); + printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); + printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); + printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000);
return 0; }

On 09/15/2011 02:09 AM, Marek Vasut wrote:
The new output looks like this:
clocks
PLL1 800 MHz PLL2 665 MHz PLL3 216 MHz
AHB 133000 kHz IPG 66500 kHz IPG PERCLK 665000 kHz
Signed-off-by: Marek Vasut marek.vasut@gmail.com
Thanks cleaning up !
Acked-by: Stefano Babic sbabic@denx.de
Best regards, Stefano Babic

Hi, Marek,
On Thu, Sep 15, 2011 at 8:09 AM, Marek Vasut marek.vasut@gmail.com wrote:
The new output looks like this:
clocks
PLL1 800 MHz PLL2 665 MHz PLL3 216 MHz
AHB 133000 kHz IPG 66500 kHz IPG PERCLK 665000 kHz
Signed-off-by: Marek Vasut marek.vasut@gmail.com
arch/arm/cpu/armv7/mx5/clock.c | 16 +++++++++------- 1 files changed, 9 insertions(+), 7 deletions(-)
Acked-by Jason Liu Jason.hui@linaro.org
Jason

Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com --- drivers/i2c/mxc_i2c.c | 423 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 290 insertions(+), 133 deletions(-)
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index ebde3c5..bc0300d 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -1,7 +1,15 @@ /* - * i2c driver for Freescale mx31 + * i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer s.hauer@pengutronix.de + * (c) 2011 Marek Vasut marek.vasut@gmail.com + * + * Based on i2c-imx.c from linux kernel: + * Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de> + * Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de> + * Copyright (C) 2007 RightHand Technologies, Inc. + * Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt> + * * * See file CREDITS for list of people who contributed to this * project. @@ -30,11 +38,13 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h>
-#define IADR 0x00 -#define IFDR 0x04 -#define I2CR 0x08 -#define I2SR 0x0c -#define I2DR 0x10 +struct mxc_i2c_regs { + uint32_t iadr; + uint32_t ifdr; + uint32_t i2cr; + uint32_t i2sr; + uint32_t i2dr; +};
#define I2CR_IEN (1 << 7) #define I2CR_IIEN (1 << 6) @@ -68,215 +78,362 @@ #endif
#define I2C_MAX_TIMEOUT 10000 -#define I2C_MAX_RETRIES 3
-static u16 div[] = { 30, 32, 36, 42, 48, 52, 60, 72, 80, 88, 104, 128, 144, - 160, 192, 240, 288, 320, 384, 480, 576, 640, 768, 960, - 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840}; +static u16 i2c_clk_div[50][2] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; + +static u8 clk_idx;
-static inline void i2c_reset(void) -{ - writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(0, I2C_BASE + I2SR); - writew(I2CR_IEN, I2C_BASE + I2CR); -} - -void i2c_init(int speed, int unused) +/* + * Calculate and set proper clock divider + */ +static void i2c_imx_set_clk(unsigned int rate) { - int freq; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int i2c_clk_rate; + unsigned int div; int i;
#if defined(CONFIG_MX31) struct clock_control_regs *sc_regs = (struct clock_control_regs *)CCM_BASE; + /* start the required I2C clock */ writel(readl(&sc_regs->cgr0) | (3 << I2C_CLK_OFFSET), &sc_regs->cgr0); #endif - freq = mxc_get_clock(MXC_IPG_PERCLK);
- for (i = 0; i < 0x1f; i++) - if (freq / div[i] <= speed) - break; + /* Divider value calculation */ + i2c_clk_rate = mxc_get_clock(MXC_IPG_CLK); + div = (i2c_clk_rate + rate - 1) / rate; + if (div < i2c_clk_div[0][0]) + i = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + i = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (i = 0; i2c_clk_div[i][0] < div; i++) + ; + + /* Store divider value */ + clk_idx = i2c_clk_div[i][1]; + writeb(clk_idx, &i2c_regs->ifdr); +}
- debug("%s: speed: %d\n", __func__, speed); +/* + * Reset I2C Controller + */ +void i2c_reset(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + + writeb(0, &i2c_regs->i2cr); /* Reset module */ + writeb(0, &i2c_regs->i2sr); +}
- writew(i, I2C_BASE + IFDR); +/* + * Init I2C Bus + */ +void i2c_init(int speed, int unused) +{ + i2c_imx_set_clk(speed); i2c_reset(); }
-static int wait_idle(void) +/* + * Wait for bus to be busy (or free if for_busy = 0) + * + * for_busy = 1: Wait for IBB to be asserted + * for_busy = 0: Wait for IBB to be de-asserted + */ +int i2c_imx_bus_busy(int for_busy) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp; + int timeout = I2C_MAX_TIMEOUT;
- while ((readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) { - writew(0, I2C_BASE + I2SR); + while (timeout--) { + temp = readb(&i2c_regs->i2sr); + + if (for_busy && (temp & I2SR_IBB)) + return 0; + if (!for_busy && !(temp & I2SR_IBB)) + return 0; + udelay(1); } - return timeout ? timeout : (!(readw(I2C_BASE + I2SR) & I2SR_IBB)); + + return 1; }
-static int wait_busy(void) +/* + * Wait for transaction to complete + */ +int i2c_imx_trx_complete(void) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int timeout = I2C_MAX_TIMEOUT;
- while (!(readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) + while (timeout--) { + if (readb(&i2c_regs->i2sr) & I2SR_IIF) { + writeb(0, &i2c_regs->i2sr); + return 0; + } + udelay(1); - writew(0, I2C_BASE + I2SR); /* clear interrupt */ + }
- return timeout; + return 1; }
-static int wait_complete(void) +/* + * Check if the transaction was ACKed + */ +int i2c_imx_acked(void) { - int timeout = I2C_MAX_TIMEOUT; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
- while ((!(readw(I2C_BASE + I2SR) & I2SR_ICF)) && (--timeout)) { - writew(0, I2C_BASE + I2SR); - udelay(1); - } - udelay(200); + return readb(&i2c_regs->i2sr) & I2SR_RX_NO_AK; +}
- writew(0, I2C_BASE + I2SR); /* clear interrupt */ +/* + * Start the controller + */ +int i2c_imx_start(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + int result;
- return timeout; -} + writeb(clk_idx, &i2c_regs->ifdr);
+ /* Enable I2C controller */ + writeb(0, &i2c_regs->i2sr); + writeb(I2CR_IEN, &i2c_regs->i2cr);
-static int tx_byte(u8 byte) -{ - writew(byte, I2C_BASE + I2DR); + /* Wait controller to be stable */ + udelay(50); + + /* Start I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_MSTA; + writeb(temp, &i2c_regs->i2cr); + + result = i2c_imx_bus_busy(1); + if (result) + return result; + + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr);
- if (!wait_complete() || readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK) - return -1; return 0; }
-static int rx_byte(int last) +/* + * Stop the controller + */ +void i2c_imx_stop() { - if (!wait_complete()) - return -1; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + + /* Stop I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr);
- if (last) - writew(I2CR_IEN, I2C_BASE + I2CR); + i2c_imx_bus_busy(0);
- return readw(I2C_BASE + I2DR); + /* Disable I2C controller */ + writeb(0, &i2c_regs->i2cr); }
-int i2c_probe(uchar chip) +/* + * Set chip address and access mode + * + * read = 1: READ access + * read = 0: WRITE access + */ +int i2c_imx_set_chip_addr(uchar chip, int read) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int ret;
- writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(I2CR_IEN, I2C_BASE + I2CR); + writeb((chip << 1) | read, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR); - ret = tx_byte(chip << 1); - writew(I2CR_IEN | I2CR_MTX, I2C_BASE + I2CR); + ret = i2c_imx_acked(); + if (ret) + return ret;
return ret; }
-static int i2c_addr(uchar chip, uint addr, int alen) +/* + * Write register address + */ +int i2c_imx_set_reg_addr(uint addr, int alen) { - int i, retry = 0; - for (retry = 0; retry < 3; retry++) { - if (wait_idle()) + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + int i; + + for (i = 0; i < (8 * alen); i += 8) { + writeb((addr >> i) & 0xff, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) break; - i2c_reset(); - for (i = 0; i < I2C_MAX_TIMEOUT; i++) - udelay(1); - } - if (retry >= I2C_MAX_RETRIES) { - debug("%s:bus is busy(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR);
- if (!wait_busy()) { - debug("%s:trigger start fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; + ret = i2c_imx_acked(); + if (ret) + break; }
- if (tx_byte(chip << 1) || (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:chip address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - while (alen--) - if (tx_byte((addr >> (alen * 8)) & 0xff) || - (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:device address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - return 0; + return ret; }
-int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +/* + * Try if a chip add given address responds (probe the chip) + */ +int i2c_probe(uchar chip) { - int timeout = I2C_MAX_TIMEOUT; int ret;
- debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + ret = i2c_imx_start(); + if (ret) + return ret;
- if (i2c_addr(chip, addr, alen)) { - printf("i2c_addr failed\n"); - return -1; - } + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA, I2C_BASE + I2CR); + i2c_imx_stop();
- if (tx_byte(chip << 1 | 1)) - return -1; + return ret; +}
- writew(I2CR_IEN | I2CR_MSTA | - ((len == 1) ? I2CR_TX_NO_AK : 0), - I2C_BASE + I2CR); +/* + * Read data from I2C device + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + unsigned int temp; + int i;
- ret = readw(I2C_BASE + I2DR); + ret = i2c_imx_start(); + if (ret) + return ret; + + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret; + + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret; + + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_RSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = i2c_imx_set_chip_addr(chip, 1); + if (ret) + return ret; + + /* setup bus to read data */ + temp = readb(&i2c_regs->i2cr); + temp &= ~I2CR_MTX; + if (len == 1) + temp &= ~I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + readb(&i2c_regs->i2dr); + + /* read data */ + for (i = 0; i < len; i++) { + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + if (i == (len - 1)) { + temp = readb(&i2c_regs->i2cr); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr); + i2c_imx_bus_busy(0); + } else if (i == (len - 2)) { + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + }
- while (len--) { - ret = rx_byte(len == 0); - if (ret < 0) - return -1; - *buf++ = ret; - if (len <= 1) - writew(I2CR_IEN | I2CR_MSTA | - I2CR_TX_NO_AK, - I2C_BASE + I2CR); + buf[i] = readb(&i2c_regs->i2dr); }
- writew(I2CR_IEN, I2C_BASE + I2CR); - - while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + i2c_imx_stop();
- return 0; + return ret; }
+/* + * Write data to I2C device + */ int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) { - int timeout = I2C_MAX_TIMEOUT; - debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + unsigned int temp; + int i;
- if (i2c_addr(chip, addr, alen)) - return -1; + ret = i2c_imx_start(); + if (ret) + return ret;
- while (len--) - if (tx_byte(*buf++)) - return -1; + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN, I2C_BASE + I2CR); + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret;
- while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + for (i = 0; i < len; i++) { + writeb(buf[i], &i2c_regs->i2dr);
- return 0; -} + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + ret = i2c_imx_acked(); + if (ret) + return ret; + }
+ i2c_imx_stop(); + + return ret; +} #endif /* CONFIG_HARD_I2C */

Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com --- drivers/i2c/mxc_i2c.c | 423 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 290 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index ebde3c5..c817f9f 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -1,7 +1,15 @@ /* - * i2c driver for Freescale mx31 + * i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer s.hauer@pengutronix.de + * (c) 2011 Marek Vasut marek.vasut@gmail.com + * + * Based on i2c-imx.c from linux kernel: + * Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de> + * Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de> + * Copyright (C) 2007 RightHand Technologies, Inc. + * Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt> + * * * See file CREDITS for list of people who contributed to this * project. @@ -30,11 +38,13 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h>
-#define IADR 0x00 -#define IFDR 0x04 -#define I2CR 0x08 -#define I2SR 0x0c -#define I2DR 0x10 +struct mxc_i2c_regs { + uint32_t iadr; + uint32_t ifdr; + uint32_t i2cr; + uint32_t i2sr; + uint32_t i2dr; +};
#define I2CR_IEN (1 << 7) #define I2CR_IIEN (1 << 6) @@ -68,215 +78,362 @@ #endif
#define I2C_MAX_TIMEOUT 10000 -#define I2C_MAX_RETRIES 3
-static u16 div[] = { 30, 32, 36, 42, 48, 52, 60, 72, 80, 88, 104, 128, 144, - 160, 192, 240, 288, 320, 384, 480, 576, 640, 768, 960, - 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840}; +static u16 i2c_clk_div[50][2] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; + +static u8 clk_idx;
-static inline void i2c_reset(void) -{ - writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(0, I2C_BASE + I2SR); - writew(I2CR_IEN, I2C_BASE + I2CR); -} - -void i2c_init(int speed, int unused) +/* + * Calculate and set proper clock divider + */ +static void i2c_imx_set_clk(unsigned int rate) { - int freq; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int i2c_clk_rate; + unsigned int div; int i;
#if defined(CONFIG_MX31) struct clock_control_regs *sc_regs = (struct clock_control_regs *)CCM_BASE; + /* start the required I2C clock */ writel(readl(&sc_regs->cgr0) | (3 << I2C_CLK_OFFSET), &sc_regs->cgr0); #endif - freq = mxc_get_clock(MXC_IPG_PERCLK);
- for (i = 0; i < 0x1f; i++) - if (freq / div[i] <= speed) - break; + /* Divider value calculation */ + i2c_clk_rate = mxc_get_clock(MXC_IPG_PERCLK); + div = (i2c_clk_rate + rate - 1) / rate; + if (div < i2c_clk_div[0][0]) + i = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + i = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (i = 0; i2c_clk_div[i][0] < div; i++) + ; + + /* Store divider value */ + clk_idx = i2c_clk_div[i][1]; + writeb(clk_idx, &i2c_regs->ifdr); +}
- debug("%s: speed: %d\n", __func__, speed); +/* + * Reset I2C Controller + */ +void i2c_reset(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + + writeb(0, &i2c_regs->i2cr); /* Reset module */ + writeb(0, &i2c_regs->i2sr); +}
- writew(i, I2C_BASE + IFDR); +/* + * Init I2C Bus + */ +void i2c_init(int speed, int unused) +{ + i2c_imx_set_clk(speed); i2c_reset(); }
-static int wait_idle(void) +/* + * Wait for bus to be busy (or free if for_busy = 0) + * + * for_busy = 1: Wait for IBB to be asserted + * for_busy = 0: Wait for IBB to be de-asserted + */ +int i2c_imx_bus_busy(int for_busy) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp; + int timeout = I2C_MAX_TIMEOUT;
- while ((readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) { - writew(0, I2C_BASE + I2SR); + while (timeout--) { + temp = readb(&i2c_regs->i2sr); + + if (for_busy && (temp & I2SR_IBB)) + return 0; + if (!for_busy && !(temp & I2SR_IBB)) + return 0; + udelay(1); } - return timeout ? timeout : (!(readw(I2C_BASE + I2SR) & I2SR_IBB)); + + return 1; }
-static int wait_busy(void) +/* + * Wait for transaction to complete + */ +int i2c_imx_trx_complete(void) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int timeout = I2C_MAX_TIMEOUT;
- while (!(readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) + while (timeout--) { + if (readb(&i2c_regs->i2sr) & I2SR_IIF) { + writeb(0, &i2c_regs->i2sr); + return 0; + } + udelay(1); - writew(0, I2C_BASE + I2SR); /* clear interrupt */ + }
- return timeout; + return 1; }
-static int wait_complete(void) +/* + * Check if the transaction was ACKed + */ +int i2c_imx_acked(void) { - int timeout = I2C_MAX_TIMEOUT; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
- while ((!(readw(I2C_BASE + I2SR) & I2SR_ICF)) && (--timeout)) { - writew(0, I2C_BASE + I2SR); - udelay(1); - } - udelay(200); + return readb(&i2c_regs->i2sr) & I2SR_RX_NO_AK; +}
- writew(0, I2C_BASE + I2SR); /* clear interrupt */ +/* + * Start the controller + */ +int i2c_imx_start(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + int result;
- return timeout; -} + writeb(clk_idx, &i2c_regs->ifdr);
+ /* Enable I2C controller */ + writeb(0, &i2c_regs->i2sr); + writeb(I2CR_IEN, &i2c_regs->i2cr);
-static int tx_byte(u8 byte) -{ - writew(byte, I2C_BASE + I2DR); + /* Wait controller to be stable */ + udelay(50); + + /* Start I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_MSTA; + writeb(temp, &i2c_regs->i2cr); + + result = i2c_imx_bus_busy(1); + if (result) + return result; + + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr);
- if (!wait_complete() || readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK) - return -1; return 0; }
-static int rx_byte(int last) +/* + * Stop the controller + */ +void i2c_imx_stop() { - if (!wait_complete()) - return -1; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + + /* Stop I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr);
- if (last) - writew(I2CR_IEN, I2C_BASE + I2CR); + i2c_imx_bus_busy(0);
- return readw(I2C_BASE + I2DR); + /* Disable I2C controller */ + writeb(0, &i2c_regs->i2cr); }
-int i2c_probe(uchar chip) +/* + * Set chip address and access mode + * + * read = 1: READ access + * read = 0: WRITE access + */ +int i2c_imx_set_chip_addr(uchar chip, int read) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int ret;
- writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(I2CR_IEN, I2C_BASE + I2CR); + writeb((chip << 1) | read, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR); - ret = tx_byte(chip << 1); - writew(I2CR_IEN | I2CR_MTX, I2C_BASE + I2CR); + ret = i2c_imx_acked(); + if (ret) + return ret;
return ret; }
-static int i2c_addr(uchar chip, uint addr, int alen) +/* + * Write register address + */ +int i2c_imx_set_reg_addr(uint addr, int alen) { - int i, retry = 0; - for (retry = 0; retry < 3; retry++) { - if (wait_idle()) + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + int i; + + for (i = 0; i < (8 * alen); i += 8) { + writeb((addr >> i) & 0xff, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) break; - i2c_reset(); - for (i = 0; i < I2C_MAX_TIMEOUT; i++) - udelay(1); - } - if (retry >= I2C_MAX_RETRIES) { - debug("%s:bus is busy(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR);
- if (!wait_busy()) { - debug("%s:trigger start fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; + ret = i2c_imx_acked(); + if (ret) + break; }
- if (tx_byte(chip << 1) || (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:chip address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - while (alen--) - if (tx_byte((addr >> (alen * 8)) & 0xff) || - (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:device address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - return 0; + return ret; }
-int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +/* + * Try if a chip add given address responds (probe the chip) + */ +int i2c_probe(uchar chip) { - int timeout = I2C_MAX_TIMEOUT; int ret;
- debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + ret = i2c_imx_start(); + if (ret) + return ret;
- if (i2c_addr(chip, addr, alen)) { - printf("i2c_addr failed\n"); - return -1; - } + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA, I2C_BASE + I2CR); + i2c_imx_stop();
- if (tx_byte(chip << 1 | 1)) - return -1; + return ret; +}
- writew(I2CR_IEN | I2CR_MSTA | - ((len == 1) ? I2CR_TX_NO_AK : 0), - I2C_BASE + I2CR); +/* + * Read data from I2C device + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + unsigned int temp; + int i;
- ret = readw(I2C_BASE + I2DR); + ret = i2c_imx_start(); + if (ret) + return ret; + + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret; + + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret; + + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_RSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = i2c_imx_set_chip_addr(chip, 1); + if (ret) + return ret; + + /* setup bus to read data */ + temp = readb(&i2c_regs->i2cr); + temp &= ~I2CR_MTX; + if (len == 1) + temp &= ~I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + readb(&i2c_regs->i2dr); + + /* read data */ + for (i = 0; i < len; i++) { + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + if (i == (len - 1)) { + temp = readb(&i2c_regs->i2cr); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr); + i2c_imx_bus_busy(0); + } else if (i == (len - 2)) { + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + }
- while (len--) { - ret = rx_byte(len == 0); - if (ret < 0) - return -1; - *buf++ = ret; - if (len <= 1) - writew(I2CR_IEN | I2CR_MSTA | - I2CR_TX_NO_AK, - I2C_BASE + I2CR); + buf[i] = readb(&i2c_regs->i2dr); }
- writew(I2CR_IEN, I2C_BASE + I2CR); - - while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + i2c_imx_stop();
- return 0; + return ret; }
+/* + * Write data to I2C device + */ int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) { - int timeout = I2C_MAX_TIMEOUT; - debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + unsigned int temp; + int i;
- if (i2c_addr(chip, addr, alen)) - return -1; + ret = i2c_imx_start(); + if (ret) + return ret;
- while (len--) - if (tx_byte(*buf++)) - return -1; + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN, I2C_BASE + I2CR); + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret;
- while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + for (i = 0; i < len; i++) { + writeb(buf[i], &i2c_regs->i2dr);
- return 0; -} + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + ret = i2c_imx_acked(); + if (ret) + return ret; + }
+ i2c_imx_stop(); + + return ret; +} #endif /* CONFIG_HARD_I2C */

Hello Marek,
Marek Vasut wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com
drivers/i2c/mxc_i2c.c | 423 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 290 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
Acked-by: Heiko Schocher hs@denx.de
bye, Heiko

Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com --- drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
V3: Remove forgotten unused variables.
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index ebde3c5..75052e5 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -1,7 +1,15 @@ /* - * i2c driver for Freescale mx31 + * i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer s.hauer@pengutronix.de + * (c) 2011 Marek Vasut marek.vasut@gmail.com + * + * Based on i2c-imx.c from linux kernel: + * Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de> + * Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de> + * Copyright (C) 2007 RightHand Technologies, Inc. + * Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt> + * * * See file CREDITS for list of people who contributed to this * project. @@ -30,11 +38,13 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h>
-#define IADR 0x00 -#define IFDR 0x04 -#define I2CR 0x08 -#define I2SR 0x0c -#define I2DR 0x10 +struct mxc_i2c_regs { + uint32_t iadr; + uint32_t ifdr; + uint32_t i2cr; + uint32_t i2sr; + uint32_t i2dr; +};
#define I2CR_IEN (1 << 7) #define I2CR_IIEN (1 << 6) @@ -68,215 +78,361 @@ #endif
#define I2C_MAX_TIMEOUT 10000 -#define I2C_MAX_RETRIES 3
-static u16 div[] = { 30, 32, 36, 42, 48, 52, 60, 72, 80, 88, 104, 128, 144, - 160, 192, 240, 288, 320, 384, 480, 576, 640, 768, 960, - 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840}; +static u16 i2c_clk_div[50][2] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; + +static u8 clk_idx;
-static inline void i2c_reset(void) -{ - writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(0, I2C_BASE + I2SR); - writew(I2CR_IEN, I2C_BASE + I2CR); -} - -void i2c_init(int speed, int unused) +/* + * Calculate and set proper clock divider + */ +static void i2c_imx_set_clk(unsigned int rate) { - int freq; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int i2c_clk_rate; + unsigned int div; int i;
#if defined(CONFIG_MX31) struct clock_control_regs *sc_regs = (struct clock_control_regs *)CCM_BASE; + /* start the required I2C clock */ writel(readl(&sc_regs->cgr0) | (3 << I2C_CLK_OFFSET), &sc_regs->cgr0); #endif - freq = mxc_get_clock(MXC_IPG_PERCLK);
- for (i = 0; i < 0x1f; i++) - if (freq / div[i] <= speed) - break; + /* Divider value calculation */ + i2c_clk_rate = mxc_get_clock(MXC_IPG_PERCLK); + div = (i2c_clk_rate + rate - 1) / rate; + if (div < i2c_clk_div[0][0]) + i = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + i = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (i = 0; i2c_clk_div[i][0] < div; i++) + ; + + /* Store divider value */ + clk_idx = i2c_clk_div[i][1]; + writeb(clk_idx, &i2c_regs->ifdr); +}
- debug("%s: speed: %d\n", __func__, speed); +/* + * Reset I2C Controller + */ +void i2c_reset(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + + writeb(0, &i2c_regs->i2cr); /* Reset module */ + writeb(0, &i2c_regs->i2sr); +}
- writew(i, I2C_BASE + IFDR); +/* + * Init I2C Bus + */ +void i2c_init(int speed, int unused) +{ + i2c_imx_set_clk(speed); i2c_reset(); }
-static int wait_idle(void) +/* + * Wait for bus to be busy (or free if for_busy = 0) + * + * for_busy = 1: Wait for IBB to be asserted + * for_busy = 0: Wait for IBB to be de-asserted + */ +int i2c_imx_bus_busy(int for_busy) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp; + int timeout = I2C_MAX_TIMEOUT;
- while ((readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) { - writew(0, I2C_BASE + I2SR); + while (timeout--) { + temp = readb(&i2c_regs->i2sr); + + if (for_busy && (temp & I2SR_IBB)) + return 0; + if (!for_busy && !(temp & I2SR_IBB)) + return 0; + udelay(1); } - return timeout ? timeout : (!(readw(I2C_BASE + I2SR) & I2SR_IBB)); + + return 1; }
-static int wait_busy(void) +/* + * Wait for transaction to complete + */ +int i2c_imx_trx_complete(void) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int timeout = I2C_MAX_TIMEOUT;
- while (!(readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) + while (timeout--) { + if (readb(&i2c_regs->i2sr) & I2SR_IIF) { + writeb(0, &i2c_regs->i2sr); + return 0; + } + udelay(1); - writew(0, I2C_BASE + I2SR); /* clear interrupt */ + }
- return timeout; + return 1; }
-static int wait_complete(void) +/* + * Check if the transaction was ACKed + */ +int i2c_imx_acked(void) { - int timeout = I2C_MAX_TIMEOUT; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
- while ((!(readw(I2C_BASE + I2SR) & I2SR_ICF)) && (--timeout)) { - writew(0, I2C_BASE + I2SR); - udelay(1); - } - udelay(200); + return readb(&i2c_regs->i2sr) & I2SR_RX_NO_AK; +}
- writew(0, I2C_BASE + I2SR); /* clear interrupt */ +/* + * Start the controller + */ +int i2c_imx_start(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + int result;
- return timeout; -} + writeb(clk_idx, &i2c_regs->ifdr);
+ /* Enable I2C controller */ + writeb(0, &i2c_regs->i2sr); + writeb(I2CR_IEN, &i2c_regs->i2cr);
-static int tx_byte(u8 byte) -{ - writew(byte, I2C_BASE + I2DR); + /* Wait controller to be stable */ + udelay(50); + + /* Start I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_MSTA; + writeb(temp, &i2c_regs->i2cr); + + result = i2c_imx_bus_busy(1); + if (result) + return result; + + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr);
- if (!wait_complete() || readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK) - return -1; return 0; }
-static int rx_byte(int last) +/* + * Stop the controller + */ +void i2c_imx_stop(void) { - if (!wait_complete()) - return -1; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + + /* Stop I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr);
- if (last) - writew(I2CR_IEN, I2C_BASE + I2CR); + i2c_imx_bus_busy(0);
- return readw(I2C_BASE + I2DR); + /* Disable I2C controller */ + writeb(0, &i2c_regs->i2cr); }
-int i2c_probe(uchar chip) +/* + * Set chip address and access mode + * + * read = 1: READ access + * read = 0: WRITE access + */ +int i2c_imx_set_chip_addr(uchar chip, int read) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int ret;
- writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(I2CR_IEN, I2C_BASE + I2CR); + writeb((chip << 1) | read, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR); - ret = tx_byte(chip << 1); - writew(I2CR_IEN | I2CR_MTX, I2C_BASE + I2CR); + ret = i2c_imx_acked(); + if (ret) + return ret;
return ret; }
-static int i2c_addr(uchar chip, uint addr, int alen) +/* + * Write register address + */ +int i2c_imx_set_reg_addr(uint addr, int alen) { - int i, retry = 0; - for (retry = 0; retry < 3; retry++) { - if (wait_idle()) + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + int i; + + for (i = 0; i < (8 * alen); i += 8) { + writeb((addr >> i) & 0xff, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) break; - i2c_reset(); - for (i = 0; i < I2C_MAX_TIMEOUT; i++) - udelay(1); - } - if (retry >= I2C_MAX_RETRIES) { - debug("%s:bus is busy(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR);
- if (!wait_busy()) { - debug("%s:trigger start fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; + ret = i2c_imx_acked(); + if (ret) + break; }
- if (tx_byte(chip << 1) || (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:chip address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - while (alen--) - if (tx_byte((addr >> (alen * 8)) & 0xff) || - (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:device address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - return 0; + return ret; }
-int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +/* + * Try if a chip add given address responds (probe the chip) + */ +int i2c_probe(uchar chip) { - int timeout = I2C_MAX_TIMEOUT; int ret;
- debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + ret = i2c_imx_start(); + if (ret) + return ret;
- if (i2c_addr(chip, addr, alen)) { - printf("i2c_addr failed\n"); - return -1; - } + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA, I2C_BASE + I2CR); + i2c_imx_stop();
- if (tx_byte(chip << 1 | 1)) - return -1; + return ret; +}
- writew(I2CR_IEN | I2CR_MSTA | - ((len == 1) ? I2CR_TX_NO_AK : 0), - I2C_BASE + I2CR); +/* + * Read data from I2C device + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + unsigned int temp; + int i;
- ret = readw(I2C_BASE + I2DR); + ret = i2c_imx_start(); + if (ret) + return ret; + + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret; + + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret; + + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_RSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = i2c_imx_set_chip_addr(chip, 1); + if (ret) + return ret; + + /* setup bus to read data */ + temp = readb(&i2c_regs->i2cr); + temp &= ~I2CR_MTX; + if (len == 1) + temp &= ~I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + readb(&i2c_regs->i2dr); + + /* read data */ + for (i = 0; i < len; i++) { + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + if (i == (len - 1)) { + temp = readb(&i2c_regs->i2cr); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr); + i2c_imx_bus_busy(0); + } else if (i == (len - 2)) { + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + }
- while (len--) { - ret = rx_byte(len == 0); - if (ret < 0) - return -1; - *buf++ = ret; - if (len <= 1) - writew(I2CR_IEN | I2CR_MSTA | - I2CR_TX_NO_AK, - I2C_BASE + I2CR); + buf[i] = readb(&i2c_regs->i2dr); }
- writew(I2CR_IEN, I2C_BASE + I2CR); - - while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + i2c_imx_stop();
- return 0; + return ret; }
+/* + * Write data to I2C device + */ int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) { - int timeout = I2C_MAX_TIMEOUT; - debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + int i;
- if (i2c_addr(chip, addr, alen)) - return -1; + ret = i2c_imx_start(); + if (ret) + return ret;
- while (len--) - if (tx_byte(*buf++)) - return -1; + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN, I2C_BASE + I2CR); + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret;
- while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + for (i = 0; i < len; i++) { + writeb(buf[i], &i2c_regs->i2dr);
- return 0; -} + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + ret = i2c_imx_acked(); + if (ret) + return ret; + }
+ i2c_imx_stop(); + + return ret; +} #endif /* CONFIG_HARD_I2C */

Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org --- drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
V3: Remove forgotten unused variables.
V4: Add missing Cc field to commit message
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index ebde3c5..75052e5 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -1,7 +1,15 @@ /* - * i2c driver for Freescale mx31 + * i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer s.hauer@pengutronix.de + * (c) 2011 Marek Vasut marek.vasut@gmail.com + * + * Based on i2c-imx.c from linux kernel: + * Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de> + * Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de> + * Copyright (C) 2007 RightHand Technologies, Inc. + * Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt> + * * * See file CREDITS for list of people who contributed to this * project. @@ -30,11 +38,13 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h>
-#define IADR 0x00 -#define IFDR 0x04 -#define I2CR 0x08 -#define I2SR 0x0c -#define I2DR 0x10 +struct mxc_i2c_regs { + uint32_t iadr; + uint32_t ifdr; + uint32_t i2cr; + uint32_t i2sr; + uint32_t i2dr; +};
#define I2CR_IEN (1 << 7) #define I2CR_IIEN (1 << 6) @@ -68,215 +78,361 @@ #endif
#define I2C_MAX_TIMEOUT 10000 -#define I2C_MAX_RETRIES 3
-static u16 div[] = { 30, 32, 36, 42, 48, 52, 60, 72, 80, 88, 104, 128, 144, - 160, 192, 240, 288, 320, 384, 480, 576, 640, 768, 960, - 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840}; +static u16 i2c_clk_div[50][2] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; + +static u8 clk_idx;
-static inline void i2c_reset(void) -{ - writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(0, I2C_BASE + I2SR); - writew(I2CR_IEN, I2C_BASE + I2CR); -} - -void i2c_init(int speed, int unused) +/* + * Calculate and set proper clock divider + */ +static void i2c_imx_set_clk(unsigned int rate) { - int freq; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int i2c_clk_rate; + unsigned int div; int i;
#if defined(CONFIG_MX31) struct clock_control_regs *sc_regs = (struct clock_control_regs *)CCM_BASE; + /* start the required I2C clock */ writel(readl(&sc_regs->cgr0) | (3 << I2C_CLK_OFFSET), &sc_regs->cgr0); #endif - freq = mxc_get_clock(MXC_IPG_PERCLK);
- for (i = 0; i < 0x1f; i++) - if (freq / div[i] <= speed) - break; + /* Divider value calculation */ + i2c_clk_rate = mxc_get_clock(MXC_IPG_PERCLK); + div = (i2c_clk_rate + rate - 1) / rate; + if (div < i2c_clk_div[0][0]) + i = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + i = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (i = 0; i2c_clk_div[i][0] < div; i++) + ; + + /* Store divider value */ + clk_idx = i2c_clk_div[i][1]; + writeb(clk_idx, &i2c_regs->ifdr); +}
- debug("%s: speed: %d\n", __func__, speed); +/* + * Reset I2C Controller + */ +void i2c_reset(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + + writeb(0, &i2c_regs->i2cr); /* Reset module */ + writeb(0, &i2c_regs->i2sr); +}
- writew(i, I2C_BASE + IFDR); +/* + * Init I2C Bus + */ +void i2c_init(int speed, int unused) +{ + i2c_imx_set_clk(speed); i2c_reset(); }
-static int wait_idle(void) +/* + * Wait for bus to be busy (or free if for_busy = 0) + * + * for_busy = 1: Wait for IBB to be asserted + * for_busy = 0: Wait for IBB to be de-asserted + */ +int i2c_imx_bus_busy(int for_busy) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp; + int timeout = I2C_MAX_TIMEOUT;
- while ((readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) { - writew(0, I2C_BASE + I2SR); + while (timeout--) { + temp = readb(&i2c_regs->i2sr); + + if (for_busy && (temp & I2SR_IBB)) + return 0; + if (!for_busy && !(temp & I2SR_IBB)) + return 0; + udelay(1); } - return timeout ? timeout : (!(readw(I2C_BASE + I2SR) & I2SR_IBB)); + + return 1; }
-static int wait_busy(void) +/* + * Wait for transaction to complete + */ +int i2c_imx_trx_complete(void) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int timeout = I2C_MAX_TIMEOUT;
- while (!(readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) + while (timeout--) { + if (readb(&i2c_regs->i2sr) & I2SR_IIF) { + writeb(0, &i2c_regs->i2sr); + return 0; + } + udelay(1); - writew(0, I2C_BASE + I2SR); /* clear interrupt */ + }
- return timeout; + return 1; }
-static int wait_complete(void) +/* + * Check if the transaction was ACKed + */ +int i2c_imx_acked(void) { - int timeout = I2C_MAX_TIMEOUT; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
- while ((!(readw(I2C_BASE + I2SR) & I2SR_ICF)) && (--timeout)) { - writew(0, I2C_BASE + I2SR); - udelay(1); - } - udelay(200); + return readb(&i2c_regs->i2sr) & I2SR_RX_NO_AK; +}
- writew(0, I2C_BASE + I2SR); /* clear interrupt */ +/* + * Start the controller + */ +int i2c_imx_start(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + int result;
- return timeout; -} + writeb(clk_idx, &i2c_regs->ifdr);
+ /* Enable I2C controller */ + writeb(0, &i2c_regs->i2sr); + writeb(I2CR_IEN, &i2c_regs->i2cr);
-static int tx_byte(u8 byte) -{ - writew(byte, I2C_BASE + I2DR); + /* Wait controller to be stable */ + udelay(50); + + /* Start I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_MSTA; + writeb(temp, &i2c_regs->i2cr); + + result = i2c_imx_bus_busy(1); + if (result) + return result; + + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr);
- if (!wait_complete() || readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK) - return -1; return 0; }
-static int rx_byte(int last) +/* + * Stop the controller + */ +void i2c_imx_stop(void) { - if (!wait_complete()) - return -1; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + + /* Stop I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr);
- if (last) - writew(I2CR_IEN, I2C_BASE + I2CR); + i2c_imx_bus_busy(0);
- return readw(I2C_BASE + I2DR); + /* Disable I2C controller */ + writeb(0, &i2c_regs->i2cr); }
-int i2c_probe(uchar chip) +/* + * Set chip address and access mode + * + * read = 1: READ access + * read = 0: WRITE access + */ +int i2c_imx_set_chip_addr(uchar chip, int read) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int ret;
- writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(I2CR_IEN, I2C_BASE + I2CR); + writeb((chip << 1) | read, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR); - ret = tx_byte(chip << 1); - writew(I2CR_IEN | I2CR_MTX, I2C_BASE + I2CR); + ret = i2c_imx_acked(); + if (ret) + return ret;
return ret; }
-static int i2c_addr(uchar chip, uint addr, int alen) +/* + * Write register address + */ +int i2c_imx_set_reg_addr(uint addr, int alen) { - int i, retry = 0; - for (retry = 0; retry < 3; retry++) { - if (wait_idle()) + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + int i; + + for (i = 0; i < (8 * alen); i += 8) { + writeb((addr >> i) & 0xff, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) break; - i2c_reset(); - for (i = 0; i < I2C_MAX_TIMEOUT; i++) - udelay(1); - } - if (retry >= I2C_MAX_RETRIES) { - debug("%s:bus is busy(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR);
- if (!wait_busy()) { - debug("%s:trigger start fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; + ret = i2c_imx_acked(); + if (ret) + break; }
- if (tx_byte(chip << 1) || (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:chip address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - while (alen--) - if (tx_byte((addr >> (alen * 8)) & 0xff) || - (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:device address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - return 0; + return ret; }
-int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +/* + * Try if a chip add given address responds (probe the chip) + */ +int i2c_probe(uchar chip) { - int timeout = I2C_MAX_TIMEOUT; int ret;
- debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + ret = i2c_imx_start(); + if (ret) + return ret;
- if (i2c_addr(chip, addr, alen)) { - printf("i2c_addr failed\n"); - return -1; - } + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA, I2C_BASE + I2CR); + i2c_imx_stop();
- if (tx_byte(chip << 1 | 1)) - return -1; + return ret; +}
- writew(I2CR_IEN | I2CR_MSTA | - ((len == 1) ? I2CR_TX_NO_AK : 0), - I2C_BASE + I2CR); +/* + * Read data from I2C device + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + unsigned int temp; + int i;
- ret = readw(I2C_BASE + I2DR); + ret = i2c_imx_start(); + if (ret) + return ret; + + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret; + + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret; + + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_RSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = i2c_imx_set_chip_addr(chip, 1); + if (ret) + return ret; + + /* setup bus to read data */ + temp = readb(&i2c_regs->i2cr); + temp &= ~I2CR_MTX; + if (len == 1) + temp &= ~I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + readb(&i2c_regs->i2dr); + + /* read data */ + for (i = 0; i < len; i++) { + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + if (i == (len - 1)) { + temp = readb(&i2c_regs->i2cr); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr); + i2c_imx_bus_busy(0); + } else if (i == (len - 2)) { + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + }
- while (len--) { - ret = rx_byte(len == 0); - if (ret < 0) - return -1; - *buf++ = ret; - if (len <= 1) - writew(I2CR_IEN | I2CR_MSTA | - I2CR_TX_NO_AK, - I2C_BASE + I2CR); + buf[i] = readb(&i2c_regs->i2dr); }
- writew(I2CR_IEN, I2C_BASE + I2CR); - - while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + i2c_imx_stop();
- return 0; + return ret; }
+/* + * Write data to I2C device + */ int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) { - int timeout = I2C_MAX_TIMEOUT; - debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + int i;
- if (i2c_addr(chip, addr, alen)) - return -1; + ret = i2c_imx_start(); + if (ret) + return ret;
- while (len--) - if (tx_byte(*buf++)) - return -1; + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN, I2C_BASE + I2CR); + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret;
- while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + for (i = 0; i < len; i++) { + writeb(buf[i], &i2c_regs->i2dr);
- return 0; -} + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + ret = i2c_imx_acked(); + if (ret) + return ret; + }
+ i2c_imx_stop(); + + return ret; +} #endif /* CONFIG_HARD_I2C */

Hi, Marek,
On Tue, Sep 20, 2011 at 10:35 AM, Marek Vasut marek.vasut@gmail.com wrote:
Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
V3: Remove forgotten unused variables.
V4: Add missing Cc field to commit message
Test result on i.mx53evk, it shows that this patch does not work well, here is the log:
MX53EVK U-Boot > pmic dump 10 PMIC ID: 0x0000ffff [Rev: unknown]
0x00: 0001ffff 00ffffff 0039ffff 0000ffff 00ffffff 0000ffff 0000ffff 0000ffff 0x08: 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0040ffff
Without the patch, i2c work well, here is the log:
MX53EVK U-Boot > pmic dump 10 PMIC ID: 0x000045d0 [Rev: 2.0]
0x00: 00015088 00ffffff 00395208 00004081 00fff7ff 0000401c 00000418 000045d0 0x08: 00000000 00000000 00000001 00000000 00000000 00000040 00000000 00400000
The source code is based on top of the latest master and then apply your v4 patch on top of it.
commit 6dcee21ee3a809d4d99ab168593c0d15ea8d6df3 Author: Marek Vasut marek.vasut@gmail.com Date: Tue Sep 20 04:35:38 2011 +0200
I2C: mxc_i2c rework
Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
commit 6478021f12db3e248b8b495dde02c51d05a38054 Author: Holger Brunck holger.brunck@keymile.com Date: Tue Sep 20 05:05:55 2011 +0000
km/common: fix bug in IVM mac address access
The MAC address stored in the inventory eeprom begins at offset 1.
Signed-off-by: Holger Brunck holger.brunck@keymile.com Signed-off-by: Valentin Longchamp valentin.longchamp@keymile.com cc: Wolfgang Denk wd@denx.de
Did I missed something?
Jason

On Thursday, September 22, 2011 04:45:20 AM Jason Hui wrote:
Hi, Marek,
On Tue, Sep 20, 2011 at 10:35 AM, Marek Vasut marek.vasut@gmail.com wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
V3: Remove forgotten unused variables.
V4: Add missing Cc field to commit message
Test result on i.mx53evk, it shows that this patch does not work well, here is the log:
MX53EVK U-Boot > pmic dump 10 PMIC ID: 0x0000ffff [Rev: unknown]
0x00: 0001ffff 00ffffff 0039ffff 0000ffff 00ffffff 0000ffff 0000ffff 0000ffff 0x08: 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0040ffff
It's strange, reading the registers with i2c md seems to work.

Hi, Marek,
On Thu, Sep 22, 2011 at 11:43 AM, Marek Vasut marek.vasut@gmail.com wrote:
On Thursday, September 22, 2011 04:45:20 AM Jason Hui wrote:
Hi, Marek,
On Tue, Sep 20, 2011 at 10:35 AM, Marek Vasut marek.vasut@gmail.com wrote:
Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
V3: Remove forgotten unused variables.
V4: Add missing Cc field to commit message
Test result on i.mx53evk, it shows that this patch does not work well, here is the log:
MX53EVK U-Boot > pmic dump 10 PMIC ID: 0x0000ffff [Rev: unknown]
0x00: 0001ffff 00ffffff 0039ffff 0000ffff 00ffffff 0000ffff 0000ffff 0000ffff 0x08: 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0040ffff
It's strange, reading the registers with i2c md seems to work.
Here is i2c md output: MX53EVK U-Boot > i2c md 8 0 0000: 01 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
seems not ok.
Jason Liu

On Thursday, September 22, 2011 06:54:22 AM Jason Hui wrote:
Hi, Marek,
On Thu, Sep 22, 2011 at 11:43 AM, Marek Vasut marek.vasut@gmail.com wrote:
On Thursday, September 22, 2011 04:45:20 AM Jason Hui wrote:
Hi, Marek,
On Tue, Sep 20, 2011 at 10:35 AM, Marek Vasut marek.vasut@gmail.com
wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
V3: Remove forgotten unused variables.
V4: Add missing Cc field to commit message
Test result on i.mx53evk, it shows that this patch does not work well, here is the log:
MX53EVK U-Boot > pmic dump 10 PMIC ID: 0x0000ffff [Rev: unknown]
0x00: 0001ffff 00ffffff 0039ffff 0000ffff 00ffffff 0000ffff 0000ffff 0000ffff 0x08: 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0040ffff
It's strange, reading the registers with i2c md seems to work.
Here is i2c md output: MX53EVK U-Boot > i2c md 8 0 0000: 01 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
seems not ok.
8 seems certainly not ok since the pmic is at 0x48
Jason Liu

Hi, Marek,
On Thu, Sep 22, 2011 at 1:47 PM, Marek Vasut marek.vasut@gmail.com wrote:
On Thursday, September 22, 2011 06:54:22 AM Jason Hui wrote:
Hi, Marek,
On Thu, Sep 22, 2011 at 11:43 AM, Marek Vasut marek.vasut@gmail.com wrote:
On Thursday, September 22, 2011 04:45:20 AM Jason Hui wrote:
Hi, Marek,
On Tue, Sep 20, 2011 at 10:35 AM, Marek Vasut marek.vasut@gmail.com
wrote:
Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
V3: Remove forgotten unused variables.
V4: Add missing Cc field to commit message
Test result on i.mx53evk, it shows that this patch does not work well, here is the log:
MX53EVK U-Boot > pmic dump 10 PMIC ID: 0x0000ffff [Rev: unknown]
0x00: 0001ffff 00ffffff 0039ffff 0000ffff 00ffffff 0000ffff 0000ffff 0000ffff 0x08: 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0000ffff 0040ffff
It's strange, reading the registers with i2c md seems to work.
Here is i2c md output: MX53EVK U-Boot > i2c md 8 0 0000: 01 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
seems not ok.
8 seems certainly not ok since the pmic is at 0x48
On mx53evk board, there is one PMIC(FSL, Atlas), but on mx53loco board, there is one PMIC(DLG9053).
Both are I2C interface, I just test the patch on mx53evk board.
Jason Liu

Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org --- drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source. V3: Remove forgotten unused variables. V4: Add missing Cc field to commit message. V5: Correct TX_NO_AK bit handling.
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index ebde3c5..a805bf6 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -1,7 +1,15 @@ /* - * i2c driver for Freescale mx31 + * i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer s.hauer@pengutronix.de + * (c) 2011 Marek Vasut marek.vasut@gmail.com + * + * Based on i2c-imx.c from linux kernel: + * Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de> + * Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de> + * Copyright (C) 2007 RightHand Technologies, Inc. + * Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt> + * * * See file CREDITS for list of people who contributed to this * project. @@ -30,11 +38,13 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h>
-#define IADR 0x00 -#define IFDR 0x04 -#define I2CR 0x08 -#define I2SR 0x0c -#define I2DR 0x10 +struct mxc_i2c_regs { + uint32_t iadr; + uint32_t ifdr; + uint32_t i2cr; + uint32_t i2sr; + uint32_t i2dr; +};
#define I2CR_IEN (1 << 7) #define I2CR_IIEN (1 << 6) @@ -68,215 +78,361 @@ #endif
#define I2C_MAX_TIMEOUT 10000 -#define I2C_MAX_RETRIES 3
-static u16 div[] = { 30, 32, 36, 42, 48, 52, 60, 72, 80, 88, 104, 128, 144, - 160, 192, 240, 288, 320, 384, 480, 576, 640, 768, 960, - 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840}; +static u16 i2c_clk_div[50][2] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; + +static u8 clk_idx;
-static inline void i2c_reset(void) -{ - writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(0, I2C_BASE + I2SR); - writew(I2CR_IEN, I2C_BASE + I2CR); -} - -void i2c_init(int speed, int unused) +/* + * Calculate and set proper clock divider + */ +static void i2c_imx_set_clk(unsigned int rate) { - int freq; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int i2c_clk_rate; + unsigned int div; int i;
#if defined(CONFIG_MX31) struct clock_control_regs *sc_regs = (struct clock_control_regs *)CCM_BASE; + /* start the required I2C clock */ writel(readl(&sc_regs->cgr0) | (3 << I2C_CLK_OFFSET), &sc_regs->cgr0); #endif - freq = mxc_get_clock(MXC_IPG_PERCLK);
- for (i = 0; i < 0x1f; i++) - if (freq / div[i] <= speed) - break; + /* Divider value calculation */ + i2c_clk_rate = mxc_get_clock(MXC_IPG_PERCLK); + div = (i2c_clk_rate + rate - 1) / rate; + if (div < i2c_clk_div[0][0]) + i = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + i = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (i = 0; i2c_clk_div[i][0] < div; i++) + ; + + /* Store divider value */ + clk_idx = i2c_clk_div[i][1]; + writeb(clk_idx, &i2c_regs->ifdr); +}
- debug("%s: speed: %d\n", __func__, speed); +/* + * Reset I2C Controller + */ +void i2c_reset(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + + writeb(0, &i2c_regs->i2cr); /* Reset module */ + writeb(0, &i2c_regs->i2sr); +}
- writew(i, I2C_BASE + IFDR); +/* + * Init I2C Bus + */ +void i2c_init(int speed, int unused) +{ + i2c_imx_set_clk(speed); i2c_reset(); }
-static int wait_idle(void) +/* + * Wait for bus to be busy (or free if for_busy = 0) + * + * for_busy = 1: Wait for IBB to be asserted + * for_busy = 0: Wait for IBB to be de-asserted + */ +int i2c_imx_bus_busy(int for_busy) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp; + int timeout = I2C_MAX_TIMEOUT;
- while ((readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) { - writew(0, I2C_BASE + I2SR); + while (timeout--) { + temp = readb(&i2c_regs->i2sr); + + if (for_busy && (temp & I2SR_IBB)) + return 0; + if (!for_busy && !(temp & I2SR_IBB)) + return 0; + udelay(1); } - return timeout ? timeout : (!(readw(I2C_BASE + I2SR) & I2SR_IBB)); + + return 1; }
-static int wait_busy(void) +/* + * Wait for transaction to complete + */ +int i2c_imx_trx_complete(void) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int timeout = I2C_MAX_TIMEOUT;
- while (!(readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) + while (timeout--) { + if (readb(&i2c_regs->i2sr) & I2SR_IIF) { + writeb(0, &i2c_regs->i2sr); + return 0; + } + udelay(1); - writew(0, I2C_BASE + I2SR); /* clear interrupt */ + }
- return timeout; + return 1; }
-static int wait_complete(void) +/* + * Check if the transaction was ACKed + */ +int i2c_imx_acked(void) { - int timeout = I2C_MAX_TIMEOUT; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
- while ((!(readw(I2C_BASE + I2SR) & I2SR_ICF)) && (--timeout)) { - writew(0, I2C_BASE + I2SR); - udelay(1); - } - udelay(200); + return readb(&i2c_regs->i2sr) & I2SR_RX_NO_AK; +}
- writew(0, I2C_BASE + I2SR); /* clear interrupt */ +/* + * Start the controller + */ +int i2c_imx_start(void) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + int result;
- return timeout; -} + writeb(clk_idx, &i2c_regs->ifdr);
+ /* Enable I2C controller */ + writeb(0, &i2c_regs->i2sr); + writeb(I2CR_IEN, &i2c_regs->i2cr);
-static int tx_byte(u8 byte) -{ - writew(byte, I2C_BASE + I2DR); + /* Wait controller to be stable */ + udelay(50); + + /* Start I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_MSTA; + writeb(temp, &i2c_regs->i2cr); + + result = i2c_imx_bus_busy(1); + if (result) + return result; + + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr);
- if (!wait_complete() || readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK) - return -1; return 0; }
-static int rx_byte(int last) +/* + * Stop the controller + */ +void i2c_imx_stop(void) { - if (!wait_complete()) - return -1; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + unsigned int temp = 0; + + /* Stop I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr);
- if (last) - writew(I2CR_IEN, I2C_BASE + I2CR); + i2c_imx_bus_busy(0);
- return readw(I2C_BASE + I2DR); + /* Disable I2C controller */ + writeb(0, &i2c_regs->i2cr); }
-int i2c_probe(uchar chip) +/* + * Set chip address and access mode + * + * read = 1: READ access + * read = 0: WRITE access + */ +int i2c_imx_set_chip_addr(uchar chip, int read) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; int ret;
- writew(0, I2C_BASE + I2CR); /* Reset module */ - writew(I2CR_IEN, I2C_BASE + I2CR); + writeb((chip << 1) | read, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR); - ret = tx_byte(chip << 1); - writew(I2CR_IEN | I2CR_MTX, I2C_BASE + I2CR); + ret = i2c_imx_acked(); + if (ret) + return ret;
return ret; }
-static int i2c_addr(uchar chip, uint addr, int alen) +/* + * Write register address + */ +int i2c_imx_set_reg_addr(uint addr, int alen) { - int i, retry = 0; - for (retry = 0; retry < 3; retry++) { - if (wait_idle()) + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + int i; + + for (i = 0; i < (8 * alen); i += 8) { + writeb((addr >> i) & 0xff, &i2c_regs->i2dr); + + ret = i2c_imx_trx_complete(); + if (ret) break; - i2c_reset(); - for (i = 0; i < I2C_MAX_TIMEOUT; i++) - udelay(1); - } - if (retry >= I2C_MAX_RETRIES) { - debug("%s:bus is busy(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR);
- if (!wait_busy()) { - debug("%s:trigger start fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; + ret = i2c_imx_acked(); + if (ret) + break; }
- if (tx_byte(chip << 1) || (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:chip address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - while (alen--) - if (tx_byte((addr >> (alen * 8)) & 0xff) || - (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { - debug("%s:device address cycle fail(%x)\n", - __func__, readw(I2C_BASE + I2SR)); - return -1; - } - return 0; + return ret; }
-int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +/* + * Try if a chip add given address responds (probe the chip) + */ +int i2c_probe(uchar chip) { - int timeout = I2C_MAX_TIMEOUT; int ret;
- debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + ret = i2c_imx_start(); + if (ret) + return ret;
- if (i2c_addr(chip, addr, alen)) { - printf("i2c_addr failed\n"); - return -1; - } + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA, I2C_BASE + I2CR); + i2c_imx_stop();
- if (tx_byte(chip << 1 | 1)) - return -1; + return ret; +}
- writew(I2CR_IEN | I2CR_MSTA | - ((len == 1) ? I2CR_TX_NO_AK : 0), - I2C_BASE + I2CR); +/* + * Read data from I2C device + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + unsigned int temp; + int i;
- ret = readw(I2C_BASE + I2DR); + ret = i2c_imx_start(); + if (ret) + return ret; + + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret; + + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret; + + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_RSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = i2c_imx_set_chip_addr(chip, 1); + if (ret) + return ret; + + /* setup bus to read data */ + temp = readb(&i2c_regs->i2cr); + temp &= ~(I2CR_MTX | I2CR_TX_NO_AK); + if (len == 1) + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + readb(&i2c_regs->i2dr); + + /* read data */ + for (i = 0; i < len; i++) { + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + if (i == (len - 1)) { + temp = readb(&i2c_regs->i2cr); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr); + i2c_imx_bus_busy(0); + } else if (i == (len - 2)) { + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + }
- while (len--) { - ret = rx_byte(len == 0); - if (ret < 0) - return -1; - *buf++ = ret; - if (len <= 1) - writew(I2CR_IEN | I2CR_MSTA | - I2CR_TX_NO_AK, - I2C_BASE + I2CR); + buf[i] = readb(&i2c_regs->i2dr); }
- writew(I2CR_IEN, I2C_BASE + I2CR); - - while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + i2c_imx_stop();
- return 0; + return ret; }
+/* + * Write data to I2C device + */ int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) { - int timeout = I2C_MAX_TIMEOUT; - debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", - __func__, chip, addr, alen, len); + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + int ret; + int i;
- if (i2c_addr(chip, addr, alen)) - return -1; + ret = i2c_imx_start(); + if (ret) + return ret;
- while (len--) - if (tx_byte(*buf++)) - return -1; + /* write slave address */ + ret = i2c_imx_set_chip_addr(chip, 0); + if (ret) + return ret;
- writew(I2CR_IEN, I2C_BASE + I2CR); + ret = i2c_imx_set_reg_addr(addr, alen); + if (ret) + return ret;
- while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + for (i = 0; i < len; i++) { + writeb(buf[i], &i2c_regs->i2dr);
- return 0; -} + ret = i2c_imx_trx_complete(); + if (ret) + return ret; + + ret = i2c_imx_acked(); + if (ret) + return ret; + }
+ i2c_imx_stop(); + + return ret; +} #endif /* CONFIG_HARD_I2C */

Hi, Marek,
On Fri, Sep 23, 2011 at 3:22 AM, Marek Vasut marek.vasut@gmail.com wrote:
Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source. V3: Remove forgotten unused variables. V4: Add missing Cc field to commit message. V5: Correct TX_NO_AK bit handling.
Test on i.MX53evk, it woks now.
Acked-by: Jason Liu jason.hui@linro.org Tested-by: Jason Liu jason.hui@linro.org
BR, Jason Liu

On Fri, Sep 23, 2011 at 12:32 AM, Jason Hui jason.hui@linaro.org wrote:
Test on i.MX53evk, it woks now.
Acked-by: Jason Liu jason.hui@linro.org Tested-by: Jason Liu jason.hui@linro.org
I guess you meant linaro.org ;-)

On Friday, September 23, 2011 05:32:13 AM Jason Hui wrote:
Hi, Marek,
On Fri, Sep 23, 2011 at 3:22 AM, Marek Vasut marek.vasut@gmail.com wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source. V3: Remove forgotten unused variables. V4: Add missing Cc field to commit message. V5: Correct TX_NO_AK bit handling.
Test on i.MX53evk, it woks now.
Acked-by: Jason Liu jason.hui@linro.org Tested-by: Jason Liu jason.hui@linro.org
Thanks.
When can I expect you commiting the DIALOG PMIC of doom code ?
Cheers
BR, Jason Liu

On 09/23/2011 05:32 AM, Jason Hui wrote:
Hi, Marek,
On Fri, Sep 23, 2011 at 3:22 AM, Marek Vasut marek.vasut@gmail.com wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source. V3: Remove forgotten unused variables. V4: Add missing Cc field to commit message. V5: Correct TX_NO_AK bit handling.
Test on i.MX53evk, it woks now.
Acked-by: Jason Liu jason.hui@linro.org Tested-by: Jason Liu jason.hui@linro.org
Thanks everybody for the good work !
I will merge now this series into u-boot-imx. I do not see any open issus (please correct me if I am wrong !).
Best regards, Stefano Babic

Hello Stefano,
Stefano Babic wrote:
On 09/23/2011 05:32 AM, Jason Hui wrote:
Hi, Marek,
On Fri, Sep 23, 2011 at 3:22 AM, Marek Vasut marek.vasut@gmail.com wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source. V3: Remove forgotten unused variables. V4: Add missing Cc field to commit message. V5: Correct TX_NO_AK bit handling.
Test on i.MX53evk, it woks now.
Acked-by: Jason Liu jason.hui@linro.org Tested-by: Jason Liu jason.hui@linro.org
Thanks everybody for the good work !
Yep! Thanks to Marek and Jason.
I will merge now this series into u-boot-imx. I do not see any open issus (please correct me if I am wrong !).
Acked-by: Heiko Schocher hs@denx.de
bye, Heiko

Hi Marek,
On Thu, 22 Sep 2011 21:22:12 +0200 Marek Vasut marek.vasut@gmail.com wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com Cc: Stefano Babic sbabic@denx.de Cc: Heiko Schocher hs@denx.de Cc: Jason Hui jason.hui@linaro.org
drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
Unfortunately this patch breaks accessing the I2C EEPROM on imx31_phycore board. On this board the U-Boot environment is stored in the I2C EEPROM. With this patch applied reading the environment doesn't work correctly, I always get "bad CRC" warning and fall back to default environment. Some EEPROM data dumps using i2c commands reveal that the EEPROM data addressing is broken, this is due to the wrong swapping of address bytes in the code below:
+/*
- Write register address
- */
+int i2c_imx_set_reg_addr(uint addr, int alen) {
- int i, retry = 0;
- for (retry = 0; retry < 3; retry++) {
if (wait_idle())
- struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
- int ret;
- int i;
- for (i = 0; i < (8 * alen); i += 8) {
writeb((addr >> i) & 0xff, &i2c_regs->i2dr);
ret = i2c_imx_trx_complete();
if (ret) break;
Applying the patch below fixes the EEPROM addressing issue, i2c commands seem to work correctly: dumping the EEPROM data to memory by
uboot> i2c read 0x52 0.2 1000 80000000 uboot> md 80000000 1 80000000: cf6bcdbd ..k.
and calculating the checksum by
uboot> crc 80000004 ffc CRC32 for 80000004 ... 80000fff ==> cf6bcdbd
shows that it works, but when the board boots, the reading of the environment still doesn't work. Bad CRC is always reported, even with the below patch applied. Reverting this driver rework commit fixes the issue.
Any idea where the problem could be?
Thanks, Anatolij
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index a805bf6..9984c2a 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -291,11 +291,10 @@ int i2c_imx_set_chip_addr(uchar chip, int read) int i2c_imx_set_reg_addr(uint addr, int alen) { struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; - int ret; - int i; + int ret = 0;
- for (i = 0; i < (8 * alen); i += 8) { - writeb((addr >> i) & 0xff, &i2c_regs->i2dr); + while (alen--) { + writeb((addr >> (alen * 8)) & 0xff, &i2c_regs->i2dr);
ret = i2c_imx_trx_complete(); if (ret)

On 09/15/2011 02:09 AM, Marek Vasut wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com
Hi Marek,
you miss to set the version number of your patchset. This can confuse us, as confuses patchwork.
Jason, do you have any issue with the last version of this patch (again, without version number, sent by Marek last 15/9) ? I saw Heiko's ACK in a previous version, but I won't push if some boards/SOC will be broken by this patchset. Any comments ?
Best regards, Stefano Babic

On Monday, September 19, 2011 12:11:55 PM Stefano Babic wrote:
On 09/15/2011 02:09 AM, Marek Vasut wrote:
Rewrite the mxc_i2c driver.
- This version is much closer to Linux implementation.
- Fixes IPG_PERCLK being incorrectly used as clock source
- Fixes behaviour of the driver on iMX51
- Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com
Hi Marek,
you miss to set the version number of your patchset. This can confuse us, as confuses patchwork.
Oh damn ... sorry. I'll do better next time ;-)
Jason, do you have any issue with the last version of this patch (again, without version number, sent by Marek last 15/9) ? I saw Heiko's ACK in a previous version, but I won't push if some boards/SOC will be broken by this patchset. Any comments ?
I think this one was acked by Heiko today too.
Cheers
Best regards, Stefano Babic

Hi, Stefano,
2011/9/19 Stefano Babic sbabic@denx.de:
On 09/15/2011 02:09 AM, Marek Vasut wrote:
Rewrite the mxc_i2c driver. * This version is much closer to Linux implementation. * Fixes IPG_PERCLK being incorrectly used as clock source * Fixes behaviour of the driver on iMX51 * Clean up coding style a bit ;-)
Signed-off-by: Marek Vasut marek.vasut@gmail.com
Hi Marek,
you miss to set the version number of your patchset. This can confuse us, as confuses patchwork.
Jason, do you have any issue with the last version of this patch (again, without version number, sent by Marek last 15/9) ? I saw Heiko's ACK in a previous version, but I won't push if some boards/SOC will be broken by this patchset. Any comments ?
I will give one test on the latest patch set (v4) tomorrow and send out the test result. Sorry for the delay.
Jason
Best regards, Stefano Babic
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de ===================================================================== _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
participants (7)
-
Anatolij Gustschin
-
Fabio Estevam
-
Heiko Schocher
-
Jason Hui
-
Jason Liu
-
Marek Vasut
-
Stefano Babic