summaryrefslogtreecommitdiff
path: root/cpu/mpc83xx/i2c.c
diff options
context:
space:
mode:
authorTimur Tabi <timur@freescale.com>2006-10-31 21:23:16 -0600
committerKim Phillips <kim.phillips@freescale.com>2006-11-03 19:42:22 -0600
commit9ca880a250870a7d55754291b5591d2b5fe89b54 (patch)
treebfef7c3013d3423222e2af4522b3341f42398f8e /cpu/mpc83xx/i2c.c
parentac4b5622ce050b5ee1e154b98df630d778661632 (diff)
downloadu-boot-imx-9ca880a250870a7d55754291b5591d2b5fe89b54.zip
u-boot-imx-9ca880a250870a7d55754291b5591d2b5fe89b54.tar.gz
u-boot-imx-9ca880a250870a7d55754291b5591d2b5fe89b54.tar.bz2
mpc83xx: Fix dual I2C support for the MPC8349ITX, MPC8349EMDS, TQM834x, and MPC8360EMDS
This patch also adds an improved I2C set_speed(), which handles all clock frequencies. Signed-off-by: Timur Tabi <timur@freescale.com>
Diffstat (limited to 'cpu/mpc83xx/i2c.c')
-rw-r--r--cpu/mpc83xx/i2c.c214
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 */