diff options
Diffstat (limited to 'cpu/mpc83xx/i2c.c')
-rw-r--r-- | cpu/mpc83xx/i2c.c | 214 |
1 files changed, 130 insertions, 84 deletions
diff --git a/cpu/mpc83xx/i2c.c b/cpu/mpc83xx/i2c.c index fc89fb1..ce78491 100644 --- a/cpu/mpc83xx/i2c.c +++ b/cpu/mpc83xx/i2c.c @@ -47,75 +47,121 @@ DECLARE_GLOBAL_DATA_PTR; -/* Three I2C bus speeds are supported here (50kHz, 100kHz - * and 400kHz). It should be easy to add more. Note that - * the maximum bus speed for I2C bus 1 is CSB/3, while I2C - * bus 2 can go as high as CSB. - * Typical values for CSB are 266MHz and 200MHz. */ - - /* 50kH 100kHz 400kHz */ -static const uchar speed_map_266[][3] = - {{0x2e, 0x2a, 0x20}, /* base 88MHz */ - {0x34, 0x30, 0x28}}; /* base 266 MHz */ - -static const uchar speed_map_200[][3] = - {{0x2c, 0x28, 0x20}, /* base 66 MHz */ - {0x33, 0x2f, 0x26}}; /* base 200 MHz */ - /* Initialize the bus pointer to whatever one the SPD EEPROM is on. * Default is bus 1. This is necessary because the DDR initialization * runs from ROM, and we can't switch buses because we can't modify * the i2c_dev variable. Everything gets straightened out once i2c_init * is called from RAM. */ -#if defined CFG_SPD_BUS_NUM -static i2c_t *i2c_dev = CFG_SPD_BUS_NUM; +#ifndef CFG_SPD_BUS_NUM +#define CFG_SPD_BUS_NUM 1 +#endif + +static unsigned int i2c_bus_num = CFG_SPD_BUS_NUM; + +#if CFG_SPD_BUS_NUM == 1 +static volatile i2c_t *i2c_dev = I2C_1; #else -static i2c_t *i2c_dev = I2C_1; +static volatile i2c_t *i2c_dev = I2C_2; #endif -static uchar busNum = I2C_BUS_1 ; -static int bus_speed[2] = {0, 0}; +static int i2c_bus_speed[2] = {0, 0}; -static int set_speed(int speed) +/* + * Map the frequency divider to the FDR. This data is taken from table 17-5 + * of the MPC8349EA reference manual, with duplicates removed. + */ +static struct { + unsigned int divider; + u8 fdr; +} i2c_speed_map[] = { - uchar value; - const uchar *spdPtr; - - /* Global data contains maximum I2C bus 1 speed, which is CSB/3 */ - if(gd->i2c_clk == 88000000) - { - spdPtr = speed_map_266[busNum]; - } - else if(gd->i2c_clk == 66000000) - { - spdPtr = speed_map_200[busNum]; - } - else - { - printf("Max I2C bus speed %d not supported\n", gd->i2c_clk); - return -1; - } - - switch(speed) - { - case 50000: - value = *(spdPtr + 0); - break; - case 100000: - value = *(spdPtr + 1); - break; - case 400000: - value = *(spdPtr + 2); - break; - default: - printf("I2C bus speed %d not supported\n", speed); - return -2; - } - /* set clock */ - writeb(value, &i2c_dev->fdr); - bus_speed[busNum] = speed; - return 0; + {0, 0x20}, + {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} +}; + +#define NUM_I2C_SPEEDS (sizeof(i2c_speed_map) / sizeof(i2c_speed_map[0])) + +static int set_speed(unsigned int speed) +{ + unsigned long i2c_clk; + unsigned int divider, i; + u8 fdr = 0x3F; + + i2c_clk = (i2c_bus_num == 2) ? gd->i2c2_clk : gd->i2c1_clk; + + divider = i2c_clk / speed; + + /* Scan i2c_speed_map[] for the closest matching divider.*/ + + for (i = 0; i < NUM_I2C_SPEEDS-1; i++) { + /* Locate our divider in between two entries in i2c_speed_map[] */ + if ((divider >= i2c_speed_map[i].divider) && + (divider <= i2c_speed_map[i+1].divider)) { + /* Which one is closer? */ + if ((divider - i2c_speed_map[i].divider) < (i2c_speed_map[i+1].divider - divider)) { + fdr = i2c_speed_map[i].fdr; + } else { + fdr = i2c_speed_map[i+1].fdr; + } + break; + } + } + + writeb(fdr, &i2c_dev->fdr); + i2c_bus_speed[i2c_bus_num - 1] = speed; + + return 0; } @@ -125,16 +171,16 @@ static void _i2c_init(int speed, int slaveadd) writeb(0x00 , &i2c_dev->cr); /* set clock */ - writeb(speed, &i2c_dev->fdr); + set_speed(speed); /* set default filter */ - writeb(0x10,&i2c_dev->dfsrr); + writeb(IC2_FDR,&i2c_dev->dfsrr); /* write slave address */ writeb(slaveadd, &i2c_dev->adr); /* clear status register */ - writeb(0x00, &i2c_dev->sr); + writeb(I2C_CR_MTX, &i2c_dev->sr); /* start I2C controller */ writeb(I2C_CR_MEN, &i2c_dev->cr); @@ -142,19 +188,19 @@ static void _i2c_init(int speed, int slaveadd) void i2c_init(int speed, int slaveadd) { - /* Set both interfaces to the same speed and slave address */ - /* Note: This function gets called twice - before and after - * relocation to RAM. The first time it's called, we are unable - * to change buses, so whichever one 'i2c_dev' was initialized to - * gets set twice. When run from RAM both buses get set properly */ - - i2c_set_bus_num(I2C_BUS_1); - _i2c_init(speed, slaveadd); -#ifdef CFG_I2C2_OFFSET - i2c_set_bus_num(I2C_BUS_2); - _i2c_init(speed, slaveadd); - i2c_set_bus_num(I2C_BUS_1); -#endif /* CFG_I2C2_OFFSET */ + /* Set both interfaces to the same speed and slave address */ + /* Note: This function gets called twice - before and after + * relocation to RAM. The first time it's called, we are unable + * to change buses, so whichever one 'i2c_dev' was initialized to + * gets set twice. When run from RAM both buses get set properly */ + + i2c_set_bus_num(1); + _i2c_init(speed, slaveadd); +#ifdef CFG_I2C2_OFFSET + i2c_set_bus_num(2); + _i2c_init(speed, slaveadd); + i2c_set_bus_num(1); +#endif /* CFG_I2C2_OFFSET */ } static __inline__ int @@ -340,38 +386,38 @@ void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val) i2c_write (i2c_addr, reg, 1, &val, 1); } -int i2c_set_bus_num(uchar bus) +int i2c_set_bus_num(unsigned int bus) { - if(bus == I2C_BUS_1) + if(bus == 1) { i2c_dev = I2C_1; } #ifdef CFG_I2C2_OFFSET - else if(bus == I2C_BUS_2) + else if(bus == 2) { i2c_dev = I2C_2; } -#endif /* CFG_I2C2_OFFSET */ +#endif else { return -1; } - busNum = bus; + i2c_bus_num = bus; return 0; } -int i2c_set_bus_speed(int speed) +int i2c_set_bus_speed(unsigned int speed) { return set_speed(speed); } -uchar i2c_get_bus_num(void) +unsigned int i2c_get_bus_num(void) { - return busNum; + return i2c_bus_num; } -int i2c_get_bus_speed(void) +unsigned int i2c_get_bus_speed(void) { - return bus_speed[busNum]; + return i2c_bus_speed[i2c_bus_num - 1]; } #endif /* CONFIG_HARD_I2C */ |