[U-Boot-Users] [PATCH] 83xx: Add support for CFG_I2C_SPEED in fsl_i2c.c

Timur Tabi timur at freescale.com
Wed Jan 9 17:18:41 CET 2008


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 at 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
-- 
1.5.2.4





More information about the U-Boot mailing list