Clock fix and MXC I2C rework series
Subsystems (3)
cover 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(-)
Signature · 1 line
1.7.5.4
PATCH 1/4 MX5: Modify the PLL decoding algorithm
The PLL decoding algorithm didn't take into account many configuration bits. Adjust it according to Linux kernel. Also, add PLL4 for MX53.
--- 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(-)
arch/arm/cpu/armv7/mx5/clock.c +67 -10
@@ -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 ));
arch/arm/include/asm/arch-mx5/imx-regs.h +3 -0
@@ -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)
Signature · 1 line
1.7.5.4
PATCH 1/4 MX5: Modify the PLL decoding algorithm
Quoted 36 lines · Marek Vasut
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 <[email protected]>---
?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(-)arch/arm/cpu/armv7/mx5/clock.c +0
-0@@ -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,
?};arch/arm/cpu/armv7/mx5/clock.c +3
-0@@ -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 ?};
arch/arm/cpu/armv7/mx5/clock.c struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { · line 60
Quoted 10 lines
arch/arm/cpu/armv7/mx5/clock.c +10
-0+#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?
arch/arm/cpu/armv7/mx5/clock.c struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { · line 67
Quoted 9 lines
arch/arm/cpu/armv7/mx5/clock.c +1
-0+ ?struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE;?/*
- * Calculate the frequency of this pll.
+ * Calculate the frequency of PLLn.
?*/arch/arm/cpu/armv7/mx5/clock.c +1
-1-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?
Quoted 56 lines
?{
- ? ? ? u32 mfi, mfn, mfd, pd;
+ ? ? ?uint32_tctrl, op, mfd, mfn, mfi, pdf, ret;
+ ? ? ?uint64_trefclk, temp;
+ ? ? ?int32_tmfn_abs;arch/arm/cpu/armv7/mx5/clock.c +42
-7+ + ? ? ? 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;
Quoted 1 line
?}
This decode schema is correct. Thanks,
arch/arm/cpu/armv7/mx5/clock.c int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) · line 340
Quoted 12 lines
?/*
arch/arm/cpu/armv7/mx5/clock.c +4
-0@@ -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.
Quoted 6 lines
arch/arm/include/asm/arch-mx5/imx-regs.h +0
-0
arch/arm/include/asm/arch-mx5/imx-regs.h
Quoted 10 lines
arch/arm/include/asm/arch-mx5/imx-regs.h +3
-1@@ -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
Jason Liu
Quoted 3 lines
1.7.5.4
PATCH 1/4 MX5: Modify the PLL decoding algorithm
Quoted 37 lines · Jason Hui
Quoted 36 lines · Marek Vasut
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 <[email protected]>--- 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(-)arch/arm/cpu/armv7/mx5/clock.c +5
-0@@ -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 };
Quoted 12 lines
arch/arm/cpu/armv7/mx5/clock.c struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { · line 60
Quoted 10 lines
arch/arm/cpu/armv7/mx5/clock.c +10
-0+#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 0x7ffffffCan we put this stuff to
crm_reg.hfile?
Right. Clock related defines are in the crm_reg.h file
Best regards,
Stefano Babic
Signature · 5 lines
===================================================================== 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 at denx.de =====================================================================
PATCH v2 1/4 MX5: Modify the PLL decoding algorithm
In reply to Marek VasutThe PLL decoding algorithm didn't take into account many configuration bits. Adjust it according to Linux kernel. Also, add PLL4 for MX53.
--- 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
arch/arm/cpu/armv7/mx5/clock.c +56 -10
@@ -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 ));
arch/arm/include/asm/arch-mx5/crm_regs.h +11 -0
@@ -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__ */
arch/arm/include/asm/arch-mx5/imx-regs.h +3 -0
@@ -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)
Signature · 1 line
1.7.5.4
PATCH 2/4 MX5: Add AHB clock reporting and fix IPG clock reporting
In reply to Marek Vasut--- arch/arm/cpu/armv7/mx5/clock.c | 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
arch/arm/cpu/armv7/mx5/clock.c +27 -9
@@ -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 ));
Signature · 1 line
1.7.5.4
PATCH 2/4 MX5: Add AHB clock reporting and fix IPG clock reporting
Hi, Marek
Quoted 17 lines · Marek Vasut
Signed-off-by Marek Vasut <[email protected]>---
?arch/arm/cpu/armv7/mx5/clock.c | ? 36 +++++++++++++++++++++++++++--------- ?1 files changed, 27 insertions(+), 9 deletions(-)arch/arm/cpu/armv7/mx5/clock.c +0
-0@@ -152,18 +152,35 @@ static u32 get_periph_clk(void) ?}?/*
+ * Get the rate of ahb clock.
+ */arch/arm/cpu/armv7/mx5/clock.c +2
-0+static u32 get_ahb_clk (void ) +{
arch/arm/cpu/armv7/mx5/clock.c static u32 get_periph_clk(void) · line 168
Quoted 10 lines
arch/arm/cpu/armv7/mx5/clock.c +10
-0+ ? ? ? 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
PATCH 2/4 MX5: Add AHB clock reporting and fix IPG clock reporting
Quoted 34 lines · Jason Hui
Hi, Marek
Quoted 18 lines · Marek Vasut
Signed-off-by Marek Vasut <[email protected]>--- arch/arm/cpu/armv7/mx5/clock.c | 36+++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
(quoted diff) +0
-0
b/arch/arm/cpu/armv7/mx5/clock.cindex 9f37f7f..a9fe81c 100644arch/arm/cpu/armv7/mx5/clock.c +4
-0@@ -152,18 +152,35 @@ static u32 get_periph_clk(void) }/* +* Get the rate of ahb clock. +*/ +static u32 get_ahb_clk (void ) +{arch/arm/cpu/armv7/mx5/clock.c static u32 get_periph_clk(void) · line 168
Quoted 10 lines
arch/arm/cpu/armv7/mx5/clock.c +10
-0+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.
Quoted 2 lines
Currently implementation,we did not take care the
ahb_bypassclock 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.
Quoted 2 lines
Jason Liu
PATCH 2/4 MX5: Add AHB clock reporting and fix IPG clock reporting
Hi, Marek,
Quoted 35 lines · Marek Vasut
Quoted 34 lines · Jason Hui
Hi, Marek
Quoted 18 lines · Marek Vasut
Signed-off-by Marek Vasut <[email protected]>---
?arch/arm/cpu/armv7/mx5/clock.c | ? 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)(quoted diff) +0
-0
b/arch/arm/cpu/armv7/mx5/clock.cindex 9f37f7f..a9fe81c 100644arch/arm/cpu/armv7/mx5/clock.c +0
-0@@ -152,18 +152,35 @@ static u32 get_periph_clk(void) ?}?/*
+ * Get the rate of ahb clock.
+ */arch/arm/cpu/armv7/mx5/clock.c +2
-0+static u32 get_ahb_clk (void ) +{arch/arm/cpu/armv7/mx5/clock.c static u32 get_periph_clk(void) · line 168
Quoted 10 lines
arch/arm/cpu/armv7/mx5/clock.c +10
-0+ ? ? ? 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.
Quoted 4 lines
Quoted 2 lines
Currently implementation,we did not take care the
ahb_bypassclock 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,
Quoted 2 lines
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
Quoted 4 lines
Quoted 3 lines
Jason Liu
PATCH v2 2/4 MX5: Add AHB clock reporting and fix IPG clock reporting
In reply to Marek Vasut--- arch/arm/cpu/armv7/mx5/clock.c | 36 +++++++++++++++++++++++++++--------- 1 files changed, 27 insertions(+), 9 deletions(-)
V2: Use get_periph_clock() as AHB clocksource.
arch/arm/cpu/armv7/mx5/clock.c +27 -9
@@ -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 ));
Signature · 1 line
1.7.5.4
PATCH v2 2/4 MX5: Add AHB clock reporting and fix IPG clock reporting
Hi, Marek,
Quoted 3 lines · Marek Vasut
Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Jason Hui <[email protected]>
Here is: Jason Liu <[email protected]>
Quoted 66 lines
---
?arch/arm/cpu/armv7/mx5/clock.c | ? 36 +++++++++++++++++++++++++++--------- ?1 files changed, 27 insertions(+), 9 deletions(-)V2: Use
get_periph_clock()as AHB clocksource.arch/arm/cpu/armv7/mx5/clock.c +0
-0@@ -152,18 +152,35 @@ static u32 get_periph_clk(void) ?}?/*
+ * Get the rate of ahb clock.
+ */arch/arm/cpu/armv7/mx5/clock.c +14
-0+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 u32get_ipg_clk(void)
?{
- ? ? ? u32ahb_podf,ipg_podf;arch/arm/cpu/armv7/mx5/clock.c +9
-7- - ? ? ? 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;?}
?/*
arch/arm/cpu/armv7/mx5/clock.c +0
-0@@ -290,7 +307,7 @@ unsigned int mxc_get_clock(enum mxc_clock clk)? ? ? ?case
MXC_ARM_CLK:
? ? ? ? ? ? ? ?returnget_mcu_main_clk();
? ? ? ?caseMXC_AHB_CLK:
- ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? returnget_ahb_clk();
? ? ? ?caseMXC_IPG_CLK:
? ? ? ? ? ? ? ?returnget_ipg_clk();
? ? ? ?caseMXC_IPG_PERCLK:arch/arm/cpu/armv7/mx5/clock.c +0
-0@@ -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);
Quoted 5 lines
?#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.
BR
Jason Liu
Quoted 4 lines
--
1.7.5.4
PATCH 3/4 MX5: Clean up the output of "clocks" command
In reply to Marek VasutThe new output looks like this:
Quoted 1 line
clocks
PLL1 800 MHz PLL2 665 MHz PLL3 216 MHz
AHB 133000 kHz
IPG 66500 kHz
IPG PERCLK 665000 kHz
--- arch/arm/cpu/armv7/mx5/clock.c | 16 +++++++++------- 1 files changed, 9 insertions(+), 7 deletions(-)
arch/arm/cpu/armv7/mx5/clock.c +9 -7
@@ -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 ; }
Signature · 1 line
1.7.5.4
PATCH 3/4 MX5: Clean up the output of "clocks" command
Quoted 6 lines · Marek Vasut
The new output looks like this:
Quoted 1 line
clocks
PLL1 800 MHz PLL2 665 MHz PLL3 216 MHz
Quoted 6 lines
AHB 133000 kHz
IPG 66500 kHz
IPG PERCLK 665000 kHzSigned-off-by Marek Vasut <[email protected]>---
Thanks cleaning up !
Best regards,
Stefano Babic
Signature · 5 lines
===================================================================== 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 at denx.de =====================================================================
PATCH 3/4 MX5: Clean up the output of "clocks" command
In reply to Marek VasutHi, Marek,
Quoted 6 lines · Marek Vasut
The new output looks like this:
Quoted 1 line
clocks
PLL1 ? ? ? ? ? ?800 MHz
PLL2 ? ? ? ? ? ?665 MHz
PLL3 ? ? ? ? ? ?216 MHz
Quoted 9 lines
AHB ? ? ? ? ?133000 kHz
IPG ? ? ? ? ? 66500 kHz
IPG PERCLK ? 665000 kHzSigned-off-by Marek Vasut <[email protected]>---
?arch/arm/cpu/armv7/mx5/clock.c | ? 16 +++++++++------- ?1 files changed, 9 insertions(+), 7 deletions(-)
Acked-by Jason Liu <[email protected]>
Jason
PATCH 4/4 I2C: mxc_i2c rework
In reply to Marek VasutRewrite 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 ;-)
--- drivers/i2c/mxc_i2c.c | 423 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 290 insertions(+), 133 deletions(-)
drivers/i2c/mxc_i2c.c +290 -133
@@ -1,7 +1,15 @@/* -* i2c driver for Freescale mx31 +* i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer <[email protected]> +* (c) 2011 Marek Vasut <[email protected]> +* +* Based on i2c-imx.c from linux kernel: +* Copyright (C) 2005 Torsten Koschorrek <[email protected]> +* Copyright (C) 2005 Matthias Blaschke <[email protected]> +* Copyright (C) 2007 RightHand Technologies, Inc. +* Copyright (C) 2008 Darius Augulis <[email protected]> +* * * 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 */
Signature · 1 line
1.7.5.4
PATCH v2 4/4 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 ;-)
--- drivers/i2c/mxc_i2c.c | 423 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 290 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
drivers/i2c/mxc_i2c.c +290 -133
@@ -1,7 +1,15 @@/* -* i2c driver for Freescale mx31 +* i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer <[email protected]> +* (c) 2011 Marek Vasut <[email protected]> +* +* Based on i2c-imx.c from linux kernel: +* Copyright (C) 2005 Torsten Koschorrek <[email protected]> +* Copyright (C) 2005 Matthias Blaschke <[email protected]> +* Copyright (C) 2007 RightHand Technologies, Inc. +* Copyright (C) 2008 Darius Augulis <[email protected]> +* * * 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 */
Signature · 1 line
1.7.5.4
PATCH v2 4/4 I2C: mxc_i2c rework
Hello Marek,
Quoted 6 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)
Quoted 6 lines
Signed-off-by Marek Vasut <[email protected]>--- drivers/i2c/mxc_i2c.c | 423 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 290 insertions(+), 133 deletions(-)V2: Use PERCLK as a source.
bye,
Heiko
Signature · 2 lines
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
PATCH v3 4/4 I2C: mxc_i2c rework
In reply to Marek VasutRewrite 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 ;-)
--- drivers/i2c/mxc_i2c.c | 422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)
V2: Use PERCLK as a source.
V3: Remove forgotten unused variables.
drivers/i2c/mxc_i2c.c +289 -133
@@ -1,7 +1,15 @@/* -* i2c driver for Freescale mx31 +* i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer <[email protected]> +* (c) 2011 Marek Vasut <[email protected]> +* +* Based on i2c-imx.c from linux kernel: +* Copyright (C) 2005 Torsten Koschorrek <[email protected]> +* Copyright (C) 2005 Matthias Blaschke <[email protected]> +* Copyright (C) 2007 RightHand Technologies, Inc. +* Copyright (C) 2008 Darius Augulis <[email protected]> +* * * 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 */
Signature · 1 line
1.7.5.4
PATCH v4 4/4 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 ;-)
--- 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
drivers/i2c/mxc_i2c.c +289 -133
@@ -1,7 +1,15 @@/* -* i2c driver for Freescale mx31 +* i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer <[email protected]> +* (c) 2011 Marek Vasut <[email protected]> +* +* Based on i2c-imx.c from linux kernel: +* Copyright (C) 2005 Torsten Koschorrek <[email protected]> +* Copyright (C) 2005 Matthias Blaschke <[email protected]> +* Copyright (C) 2007 RightHand Technologies, Inc. +* Copyright (C) 2008 Darius Augulis <[email protected]> +* * * 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 */
Signature · 1 line
1.7.5.4
PATCH v4 4/4 I2C: mxc_i2c rework
Hi, Marek,
Quoted 14 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
?* This version is much closer to Linux implementation. ?* FixesIPG_PERCLKbeing incorrectly used as clock source ?* Fixes behaviour of the driver on iMX51
?* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>---
?drivers/i2c/mxc_i2c.c | ?422 +++++++++++++++++++++++++++++++++---------------- ?1 files changed, 289 insertions(+), 133 deletions(-)
Quoted 6 lines
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 <[email protected]> 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 ;-)
commit 6478021f12db3e248b8b495dde02c51d05a38054
Author: Holger Brunck <[email protected]> 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.
Did I missed something?
Jason
PATCH v4 4/4 I2C: mxc_i2c rework
Quoted 27 lines · Jason Hui
Hi, Marek,
Quoted 12 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>--- drivers/i2c/mxc_i2c.c | 422Quoted 8 lines
+++++++++++++++++++++++++++++++++---------------- 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:
Quoted 7 lines
MX53EVK U-Boot > pmic dump 10
PMIC ID:0x0000ffff[Rev: unknown]
0x00:0001ffff00ffffff0039ffff0000ffff00ffffff0000ffff0000ffff0000ffff0x08:0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0040ffff
It's strange, reading the registers with i2c md seems to work.
PATCH v4 4/4 I2C: mxc_i2c rework
Hi, Marek,
Quoted 31 lines · Marek Vasut
Quoted 30 lines · Jason Hui
Hi, Marek,
Quoted 15 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
?* This version is much closer to Linux implementation. ?* FixesIPG_PERCLKbeing incorrectly used as clock source ?* Fixes behaviour of the driver on iMX51
?* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>---
?drivers/i2c/mxc_i2c.c | ?422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)Quoted 5 lines
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]
Quoted 6 lines
Quoted 4 lines
0x00:0001ffff00ffffff0039ffff0000ffff00ffffff0000ffff0000ffff0000ffff0x08:0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0040ffffIt'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
Quoted 1 line
PATCH v4 4/4 I2C: mxc_i2c rework
Quoted 7 lines · Jason Hui
Hi, Marek,
Quoted 4 lines · Marek Vasut
Quoted 3 lines · Jason Hui
Hi, Marek,
On Tue, Sep 20, 2011 at 10:35 AM, Marek Vasut <[email protected]>
Quoted 31 lines · wrote:
Quoted 31 lines
Quoted 30 lines
Quoted 12 lines
Rewrite the
mxc_i2cdriver.
* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>--- drivers/i2c/mxc_i2c.c | 422Quoted 8 lines
+++++++++++++++++++++++++++++++++---------------- 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:0001ffff00ffffff0039ffff0000ffff00ffffff0000ffff0000ffff0000ffff0x08:0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0040ffff
Quoted 7 lines
Quoted 1 line
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
Quoted 2 lines
Jason Liu
PATCH v4 4/4 I2C: mxc_i2c rework
Hi, Marek,
Quoted 42 lines · Marek Vasut
Quoted 7 lines · Jason Hui
Hi, Marek,
Quoted 4 lines · Marek Vasut
Quoted 3 lines · Jason Hui
Hi, Marek,
On Tue, Sep 20, 2011 at 10:35 AM, Marek Vasut <[email protected]>
Quoted 33 lines · wrote:
Quoted 32 lines
Quoted 24 lines
Quoted 15 lines
Rewrite the
mxc_i2cdriver.
?* This version is much closer to Linux implementation. ?* FixesIPG_PERCLKbeing incorrectly used as clock source ?* Fixes behaviour of the driver on iMX51
?* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>---
?drivers/i2c/mxc_i2c.c | ?422 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 289 insertions(+), 133 deletions(-)Quoted 5 lines
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:
Quoted 6 lines
MX53EVK U-Boot > pmic dump 10
PMIC ID:0x0000ffff[Rev: unknown]
0x00:0001ffff00ffffff0039ffff0000ffff00ffffff0000ffff0000ffff0000ffff0x08:0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0040ffffIt's strange, reading the registers with i2c md seems to work.
Quoted 7 lines
Quoted 5 lines
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.
Quoted 4 lines
Quoted 3 lines
Jason Liu
PATCH v5 4/4 I2C: mxc_i2c rework
In reply to Marek VasutRewrite 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 ;-)
--- 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.
drivers/i2c/mxc_i2c.c +289 -133
@@ -1,7 +1,15 @@/* -* i2c driver for Freescale mx31 +* i2c driver for Freescale i.MX series * * (c) 2007 Pengutronix, Sascha Hauer <[email protected]> +* (c) 2011 Marek Vasut <[email protected]> +* +* Based on i2c-imx.c from linux kernel: +* Copyright (C) 2005 Torsten Koschorrek <[email protected]> +* Copyright (C) 2005 Matthias Blaschke <[email protected]> +* Copyright (C) 2007 RightHand Technologies, Inc. +* Copyright (C) 2008 Darius Augulis <[email protected]> +* * * 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 */
Signature · 1 line
1.7.5.4
PATCH v5 4/4 I2C: mxc_i2c rework
Hi, Marek,
Quoted 10 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
?* This version is much closer to Linux implementation. ?* FixesIPG_PERCLKbeing incorrectly used as clock source ?* Fixes behaviour of the driver on iMX51
?* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>
Quoted 9 lines
---
?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: CorrectTX_NO_AKbit handling.
Test on i.MX53evk, it woks now.
BR,
Jason Liu
Quoted 1 line
Fabio Estevam
reviewerPATCH v5 4/4 I2C: mxc_i2c rework
Quoted 4 lines · Jason Hui
Test on i.MX53evk, it woks now.
Acked-by Jason Liu <[email protected]>Tested-by Jason Liu <[email protected]>
I guess you meant linaro.org ;-)
PATCH v5 4/4 I2C: mxc_i2c rework
In reply to Jason HuiQuoted 23 lines · Jason Hui
Hi, Marek,
Quoted 12 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>--- drivers/i2c/mxc_i2c.c | 422Quoted 7 lines
+++++++++++++++++++++++++++++++++---------------- 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: CorrectTX_NO_AKbit handling.
Quoted 4 lines
Test on i.MX53evk, it woks now.
Acked-by Jason Liu <[email protected]>Tested-by Jason Liu <[email protected]>
Thanks.
When can I expect you commiting the DIALOG PMIC of doom code ?
Cheers
Quoted 4 lines
BR,
Jason Liu
PATCH v5 4/4 I2C: mxc_i2c rework
In reply to Jason HuiQuoted 23 lines · Jason Hui
Hi, Marek,
Quoted 10 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>Quoted 9 lines
--- 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: CorrectTX_NO_AKbit handling.
Quoted 4 lines
Test on i.MX53evk, it woks now.
Acked-by Jason Liu <[email protected]>Tested-by Jason Liu <[email protected]>
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
Signature · 5 lines
===================================================================== 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 at denx.de =====================================================================
PATCH v5 4/4 I2C: mxc_i2c rework
Hello Stefano,
Quoted 23 lines · Stefano Babic
Quoted 22 lines · Jason Hui
Hi, Marek,
Quoted 19 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>--- 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: CorrectTX_NO_AKbit handling.
Quoted 6 lines
Quoted 4 lines
Test on i.MX53evk, it woks now.
Acked-by Jason Liu <[email protected]>Tested-by Jason Liu <[email protected]>Thanks everybody for the good work !
Yep! Thanks to Marek and Jason.
Quoted 2 lines
I will merge now this series into u-boot-imx. I do not see any open issus (please correct me if I am wrong !).
bye,
Heiko
Signature · 2 lines
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
PATCH v5 4/4 I2C: mxc_i2c rework
In reply to Marek VasutHi Marek,
Quoted 6 lines · On Thu, 22 Sep 2011 21:22:12 +0200 Marek Vasut <[email protected]> wrote:
Rewrite the
mxc_i2cdriver.
* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)
Quoted 7 lines
Signed-off-by Marek Vasut <[email protected]>Cc Stefano Babic <[email protected]>Cc Heiko Schocher <[email protected]>Cc Jason Hui <[email protected]>--- 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:
Quoted 8 lines
(quoted diff) +4
-3+/* + * 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())
Review
Quoted 10 lines
(quoted diff) +9
-0+ 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
drivers/i2c/mxc_i2c.c +3 -4
@@ -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)
PATCH 4/4 I2C: mxc_i2c rework
In reply to Marek VasutQuoted 8 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.
* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>---
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
Signature · 5 lines
===================================================================== 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 at denx.de =====================================================================
PATCH 4/4 I2C: mxc_i2c rework
Quoted 8 lines · Stefano Babic
Quoted 7 lines · Marek Vasut
Rewrite the
mxc_i2cdriver.* This version is much closer to Linux implementation.
* FixesIPG_PERCLKbeing incorrectly used as clock source
* Fixes behaviour of the driver on iMX51
* Clean up coding style a bit ;-)
Quoted 7 lines
Quoted 2 lines
Signed-off-by Marek Vasut <[email protected]>---
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 ;-)
Quoted 5 lines
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
Quoted 3 lines
Best regards,
Stefano Babic
PATCH 4/4 I2C: mxc_i2c rework
In reply to Stefano BabicHi, Stefano,
2011/9/19 Stefano Babic <[email protected]>:
Quoted 12 lines · Marek Vasut
Quoted 8 lines
Rewrite the
mxc_i2cdriver.
?* This version is much closer to Linux implementation. ?* FixesIPG_PERCLKbeing incorrectly used as clock source ?* Fixes behaviour of the driver on iMX51
?* Clean up coding style a bit ;-)Signed-off-by Marek Vasut <[email protected]>---
Hi Marek,
Quoted 7 lines
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
Quoted 15 lines
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 at denx.de
=====================================================================
_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot