
Add support to fsl_i2c.c for setting and querying the I2C bus speed. Current 83xx boards define the CFG_I2C_SPEED, but fsl_i2c.c ignores the value and uses a conservative value of 0x3F when programming the I2C bus speed.
Signed-off-by: Timur Tabi timur@freescale.com ---
Unfortunately, only 83xx calculates and stores the I2C clock frequencies, so this code is compiled on 83xx only.
cpu/mpc83xx/cpu.c | 24 +++++++++++ drivers/i2c/fsl_i2c.c | 90 ++++++++++++++++++++++++++++++++++++++++- include/asm-ppc/fsl_i2c.h | 23 ++++++++++ include/configs/MPC832XEMDS.h | 1 + 4 files changed, 137 insertions(+), 1 deletions(-)
diff --git a/cpu/mpc83xx/cpu.c b/cpu/mpc83xx/cpu.c index f1ea17d..51cb1b7 100644 --- a/cpu/mpc83xx/cpu.c +++ b/cpu/mpc83xx/cpu.c @@ -716,3 +716,27 @@ int dma_xfer(void *dest, u32 count, void *src) return ((int)dma_check()); } #endif /*CONFIG_DDR_ECC*/ + +#ifdef CFG_I2C_SPEED +/* + * Map the frequency divider to the FDR. This data is taken from table 17-5 + * of the MPC8349EA reference manual, with duplicates removed. It also applies + * to the 8360. + */ +struct fsl_i2c_speed_map fsl_i2c_speed_map[] = { + {256, 0x20}, {288, 0x21}, {320, 0x22}, {352, 0x23}, + {384, 0x24}, {416, 0x01}, {448, 0x25}, {480, 0x02}, + {512, 0x26}, {576, 0x27}, {640, 0x28}, {704, 0x05}, + {768, 0x29}, {832, 0x06}, {896, 0x2A}, {1024, 0x2B}, + {1152, 0x08}, {1280, 0x2C}, {1536, 0x2D}, {1792, 0x2E}, + {1920, 0x0B}, {2048, 0x2F}, {2304, 0x0C}, {2560, 0x30}, + {3072, 0x31}, {3584, 0x32}, {3840, 0x0F}, {4096, 0x33}, + {4608, 0x10}, {5120, 0x34}, {6144, 0x35}, {7168, 0x36}, + {7680, 0x13}, {8192, 0x37}, {9216, 0x14}, {10240, 0x38}, + {12288, 0x39}, {14336, 0x3A}, {15360, 0x17}, {16384, 0x3B}, + {18432, 0x18}, {20480, 0x3C}, {24576, 0x3D}, {28672, 0x3E}, + {30720, 0x1B}, {32768, 0x3F}, {36864, 0x1C}, {40960, 0x1D}, + {49152, 0x1E}, {61440, 0x1F}, + {-1, 0x1F} +}; +#endif diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index 22485ea..bca2c75 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -32,6 +32,8 @@ #define I2C_READ_BIT 1 #define I2C_WRITE_BIT 0
+DECLARE_GLOBAL_DATA_PTR; + /* Initialize the bus pointer to whatever one the SPD EEPROM is on. * Default is bus 0. This is necessary because the DDR initialization * runs from ROM, and we can't switch buses because we can't modify @@ -43,6 +45,8 @@ static unsigned int i2c_bus_num __attribute__ ((section ("data"))) = CFG_SPD_BUS static unsigned int i2c_bus_num __attribute__ ((section ("data"))) = 0; #endif
+static unsigned int i2c_bus_speed[2] = {CFG_I2C_SPEED, CFG_I2C_SPEED}; + static volatile struct fsl_i2c *i2c_dev[2] = { (struct fsl_i2c *) (CFG_IMMR + CFG_I2C_OFFSET), #ifdef CFG_I2C2_OFFSET @@ -50,6 +54,53 @@ static volatile struct fsl_i2c *i2c_dev[2] = { #endif };
+#ifdef CONFIG_MPC83XX +/* Currently, only 83xx determines and stores the I2C clock frequency */ + +#ifdef CFG_I2C_SPEED +/* Define a default I2C speed map if a real map isn't defined elsewhere */ +#pragma weak fsl_i2c_speed_map = default_fsl_i2c_speed_map + +struct fsl_i2c_speed_map default_fsl_i2c_speed_map[] = { + {0, 0x3F}, {-1, 0x3F} +}; +#endif + +/* + * Get the I2C FDR value for a given I2C clock and a given I2C bus speed + */ +static u8 get_fdr(unsigned int i2c_clk, unsigned int speed) +{ +#ifdef CFG_I2C_SPEED + extern struct fsl_i2c_speed_map fsl_i2c_speed_map[]; + unsigned int d; /* The I2C clock divider */ + unsigned int i = 0; + unsigned int low, high; + + d = i2c_clk / speed; + + /* Scan fsl_i2c_speed_map[] for the closest matching divider.*/ + + while (fsl_i2c_speed_map[i].divider != (unsigned int) -1) { + low = fsl_i2c_speed_map[i].divider; + high = fsl_i2c_speed_map[i+1].divider; + + if ((d >= low) && (d <= high)) { + /* Which one is closer? */ + if ((d - low) < (high - d)) + return fsl_i2c_speed_map[i].fdr; + else + return fsl_i2c_speed_map[i+1].fdr; + break; + } + i++; + } +#endif + + return 0x3F; /* Use a conservative value as the default */ +} +#endif /* CONFIG_MPC83XX */ + void i2c_init(int speed, int slaveadd) { @@ -59,23 +110,37 @@ i2c_init(int speed, int slaveadd)
writeb(0, &dev->cr); /* stop I2C controller */ udelay(5); /* let it shutdown in peace */ +#ifdef CONFIG_MPC83XX + /* Currently, only 83xx determines and stores the I2C clock frequency */ + writeb(get_fdr(gd->i2c1_clk, speed), &dev->fdr); /* set bus speed */ +#else writeb(0x3F, &dev->fdr); /* set bus speed */ +#endif writeb(0x3F, &dev->dfsrr); /* set default filter */ writeb(slaveadd << 1, &dev->adr); /* write slave address */ writeb(0x0, &dev->sr); /* clear status register */ writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */
+ i2c_bus_speed[0] = speed; + #ifdef CFG_I2C2_OFFSET dev = (struct fsl_i2c *) (CFG_IMMR + CFG_I2C2_OFFSET);
writeb(0, &dev->cr); /* stop I2C controller */ udelay(5); /* let it shutdown in peace */ +#ifdef CONFIG_MPC83XX + /* Currently, only 83xx determines and stores the I2C clock frequency */ + writeb(get_fdr(gd->i2c2_clk, speed), &dev->fdr); /* set bus speed */ +#else writeb(0x3F, &dev->fdr); /* set bus speed */ +#endif writeb(0x3F, &dev->dfsrr); /* set default filter */ writeb(slaveadd << 1, &dev->adr); /* write slave address */ writeb(0x0, &dev->sr); /* clear status register */ writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ -#endif /* CFG_I2C2_OFFSET */ + + i2c_bus_speed[1] = speed; +#endif }
static __inline__ int @@ -279,7 +344,23 @@ int i2c_set_bus_num(unsigned int bus)
int i2c_set_bus_speed(unsigned int speed) { +#ifdef CONFIG_MPC83XX + /* Currently, only 83xx determines and stores the I2C clock frequency */ + + unsigned int i2c_clk = (i2c_bus_num == 1) ? gd->i2c2_clk : gd->i2c1_clk; + u8 fdr = get_fdr(i2c_clk, speed); + + printf("Writing %x to FDR\n", fdr); + writeb(0, &i2c_dev[i2c_bus_num]->cr); /* stop controller */ + writeb(fdr, &i2c_dev[i2c_bus_num]->fdr); /* set bus speed */ + writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); /* start controller */ + + i2c_bus_speed[i2c_bus_num] = speed; + + return 0; +#else return -1; +#endif }
unsigned int i2c_get_bus_num(void) @@ -289,7 +370,14 @@ unsigned int i2c_get_bus_num(void)
unsigned int i2c_get_bus_speed(void) { +#ifdef CONFIG_MPC83XX + /* Currently, only 83xx determines and stores the I2C clock frequency */ + + return i2c_bus_speed[i2c_bus_num]; +#else return 0; +#endif } + #endif /* CONFIG_HARD_I2C */ #endif /* CONFIG_FSL_I2C */ diff --git a/include/asm-ppc/fsl_i2c.h b/include/asm-ppc/fsl_i2c.h index 4f71341..f84a803 100644 --- a/include/asm-ppc/fsl_i2c.h +++ b/include/asm-ppc/fsl_i2c.h @@ -83,4 +83,27 @@ typedef struct fsl_i2c { u8 res6[0xE8]; } fsl_i2c_t;
+/* + * Map I2C frequency dividers to FDR values + * + * This structure is used to define the elements of a table that maps I2C + * frequency divider (I2C clock rate divided by I2C bus speed) to a value to be + * programmed into the Frequency Divider Ratio (FDR) register. + * + * The actual table should be defined in the board file, and it must be called + * fsl_i2c_speed_map[]. + * + * The first entry must be {0, X}, where X == fsl_i2c_speed_map[1].fdr. + * + * The last entry of the table must have a value of {-1,x}, where x is same + * FDR value as the second-to-last entry. + * + * The values of the divider must be in increasing numerical order, i.e. + * fsl_i2c_speed_map[x+1].divider > fsl_i2c_speed_map[x].divider. + */ +typedef struct fsl_i2c_speed_map { + unsigned int divider; + u8 fdr; +} fsl_i2c_speed_map_t; + #endif /* _ASM_I2C_H_ */ diff --git a/include/configs/MPC832XEMDS.h b/include/configs/MPC832XEMDS.h index c9c6d88..d5e588a 100644 --- a/include/configs/MPC832XEMDS.h +++ b/include/configs/MPC832XEMDS.h @@ -335,6 +335,7 @@ #define CFG_I2C_SLAVE 0x7F #define CFG_I2C_NOPROBES {0x51} /* Don't probe these addrs */ #define CFG_I2C_OFFSET 0x3000 +#define CONFIG_I2C_CMD_TREE
/* * Config on-board RTC