diff options
| -rw-r--r-- | README | 47 | ||||
| -rw-r--r-- | common/cmd_i2c.c | 267 | ||||
| -rw-r--r-- | cpu/mpc8260/i2c.c | 15 | ||||
| -rw-r--r-- | drivers/i2c/soft_i2c.c | 15 | ||||
| -rw-r--r-- | include/configs/mgcoge.h | 1 | ||||
| -rw-r--r-- | include/configs/mgsuvd.h | 1 | ||||
| -rw-r--r-- | include/i2c.h | 23 | 
7 files changed, 367 insertions, 2 deletions
| @@ -1429,6 +1429,53 @@ The following options need to be configured:  		Define this option if you want to use Freescale's I2C driver in  		drivers/i2c/fsl_i2c.c. +		CONFIG_I2C_MUX + +		Define this option if you have I2C devices reached over 1 .. n +		I2C Muxes like the pca9544a. This option addes a new I2C +		Command "i2c bus [muxtype:muxaddr:muxchannel]" which adds a +		new I2C Bus to the existing I2C Busses. If you select the +		new Bus with "i2c dev", u-bbot sends first the commandos for +		the muxes to activate this new "bus". + +		CONFIG_I2C_MULTI_BUS must be also defined, to use this +		feature! + +		Example: +		Adding a new I2C Bus reached over 2 pca9544a muxes +			The First mux with address 70 and channel 6 +			The Second mux with address 71 and channel 4 + +		=> i2c bus pca9544a:70:6:pca9544a:71:4 + +		Use the "i2c bus" command without parameter, to get a list +		of I2C Busses with muxes: + +		=> i2c bus +		Busses reached over muxes: +		Bus ID: 2 +		  reached over Mux(es): +		    pca9544a@70 ch: 4 +		Bus ID: 3 +		  reached over Mux(es): +		    pca9544a@70 ch: 6 +		    pca9544a@71 ch: 4 +		=> + +		If you now switch to the new I2C Bus 3 with "i2c dev 3" +		u-boot sends First the Commando to the mux@70 to enable +		channel 6, and then the Commando to the mux@71 to enable +		the channel 4. + +		After that, you can use the "normal" i2c commands as +		usual, to communicate with your I2C devices behind +		the 2 muxes. + +		This option is actually implemented for the bitbanging +		algorithm in common/soft_i2c.c and for the Hardware I2C +		Bus on the MPC8260. But it should be not so difficult +		to add this option to other architectures. +  - SPI Support:	CONFIG_SPI diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index ef9123ebf..8d287fe5f 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -83,7 +83,9 @@  #include <common.h>  #include <command.h> +#include <environment.h>  #include <i2c.h> +#include <malloc.h>  #include <asm/byteorder.h>  /* Display values from last command. @@ -125,6 +127,14 @@ static uchar i2c_no_probes[] = CFG_I2C_NOPROBES;  #define NUM_ELEMENTS_NOPROBE (sizeof(i2c_no_probes)/sizeof(i2c_no_probes[0]))  #endif +#if defined(CONFIG_I2C_MUX) +static I2C_MUX_DEVICE	*i2c_mux_devices = NULL; +static	int	i2c_mux_busid = CFG_MAX_I2C_BUS; + +DECLARE_GLOBAL_DATA_PTR; + +#endif +  static int  mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char *argv[]); @@ -1188,6 +1198,37 @@ int do_i2c_reset(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])  	return 0;  } +#if defined(CONFIG_I2C_MUX) +int do_i2c_add_bus(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +	int ret=0; + +	if (argc == 1) { +		/* show all busses */ +		I2C_MUX		*mux; +		I2C_MUX_DEVICE	*device = i2c_mux_devices; + +		printf ("Busses reached over muxes:\n"); +		while (device != NULL) { +			printf ("Bus ID: %x\n", device->busid); +			printf ("  reached over Mux(es):\n"); +			mux = device->mux; +			while (mux != NULL) { +				printf ("    %s@%x ch: %x\n", mux->name, mux->chip, mux->channel); +				mux = mux->next; +			} +			device = device->next; +		} +	} else { +		I2C_MUX_DEVICE *dev; + +		dev = i2c_mux_ident_muxstring ((uchar *)argv[1]); +		ret = 0; +	} +	return ret; +} +#endif  /* CONFIG_I2C_MUX */ +  #if defined(CONFIG_I2C_MULTI_BUS)  int do_i2c_bus_num(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])  { @@ -1226,6 +1267,10 @@ int do_i2c_bus_speed(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])  int do_i2c(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])  { +#if defined(CONFIG_I2C_MUX) +	if (!strncmp(argv[1], "bu", 2)) +		return do_i2c_add_bus(cmdtp, flag, --argc, ++argv); +#endif  /* CONFIG_I2C_MUX */  	if (!strncmp(argv[1], "sp", 2))  		return do_i2c_bus_speed(cmdtp, flag, --argc, ++argv);  #if defined(CONFIG_I2C_MULTI_BUS) @@ -1264,6 +1309,9 @@ int do_i2c(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])  U_BOOT_CMD(  	i2c, 6, 1, do_i2c,  	"i2c     - I2C sub-system\n", +#if defined(CONFIG_I2C_MUX) +	"bus [muxtype:muxaddr:muxchannel] - add a new bus reached over muxes.\n" +#endif  /* CONFIG_I2C_MUX */  	"speed [speed] - show or set I2C bus speed\n"  #if defined(CONFIG_I2C_MULTI_BUS)  	"i2c dev [dev] - show or set current I2C bus\n" @@ -1335,3 +1383,222 @@ U_BOOT_CMD(  	"      (valid chip values 50..57)\n"  );  #endif + +#if defined(CONFIG_I2C_MUX) + +int i2c_mux_add_device(I2C_MUX_DEVICE *dev) +{ +	I2C_MUX_DEVICE	*devtmp = i2c_mux_devices; + +	if (i2c_mux_devices == NULL) { +		i2c_mux_devices = dev; +		return 0; +	} +	while (devtmp->next != NULL) +		devtmp = devtmp->next; + +	devtmp->next = dev; +	return 0; +} + +I2C_MUX_DEVICE	*i2c_mux_search_device(int id) +{ +	I2C_MUX_DEVICE	*device = i2c_mux_devices; + +	while (device != NULL) { +		if (device->busid == id) +			return device; +		device = device->next; +	} +	return NULL; +} + +/* searches in the buf from *pos the next ':'. + * returns: + *     0 if found (with *pos = where) + *   < 0 if an error occured + *   > 0 if the end of buf is reached + */ +static int i2c_mux_search_next (int *pos, uchar	*buf, int len) +{ +	while ((buf[*pos] != ':') && (*pos < len)) { +		*pos += 1; +	} +	if (*pos >= len) +		return 1; +	if (buf[*pos] != ':') +		return -1; +	return 0; +} + +static int i2c_mux_get_busid (void) +{ +	int	tmp = i2c_mux_busid; + +	i2c_mux_busid ++; +	return tmp; +} + +/* Analyses a Muxstring and sends immediately the +   Commands to the Muxes. Runs from Flash. + */ +int i2c_mux_ident_muxstring_f (uchar *buf) +{ +	int	pos = 0; +	int	oldpos; +	int	ret = 0; +	int	len = strlen((char *)buf); +	int	chip; +	uchar	channel; +	int	was = 0; + +	while (ret == 0) { +		oldpos = pos; +		/* search name */ +		ret = i2c_mux_search_next(&pos, buf, len); +		if (ret != 0) +			printf ("ERROR\n"); +		/* search address */ +		pos ++; +		oldpos = pos; +		ret = i2c_mux_search_next(&pos, buf, len); +		if (ret != 0) +			printf ("ERROR\n"); +		buf[pos] = 0; +		chip = simple_strtoul((char *)&buf[oldpos], NULL, 16); +		buf[pos] = ':'; +		/* search channel */ +		pos ++; +		oldpos = pos; +		ret = i2c_mux_search_next(&pos, buf, len); +		if (ret < 0) +			printf ("ERROR\n"); +		was = 0; +		if (buf[pos] != 0) { +			buf[pos] = 0; +			was = 1; +		} +		channel = simple_strtoul((char *)&buf[oldpos], NULL, 16); +		if (was) +			buf[pos] = ':'; +		if (i2c_write(chip, 0, 0, &channel, 1) != 0) { +			printf ("Error setting Mux: chip:%x channel: \ +				%x\n", chip, channel); +			return -1; +		} +		pos ++; +		oldpos = pos; + +	} + +	return 0; +} + +/* Analyses a Muxstring and if this String is correct + * adds a new I2C Bus. + */ +I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf) +{ +	I2C_MUX_DEVICE	*device; +	I2C_MUX		*mux; +	int	pos = 0; +	int	oldpos; +	int	ret = 0; +	int	len = strlen((char *)buf); +	int	was = 0; + +	device = (I2C_MUX_DEVICE *)malloc (sizeof(I2C_MUX_DEVICE)); +	device->mux = NULL; +	device->busid = i2c_mux_get_busid (); +	device->next = NULL; +	while (ret == 0) { +		mux = (I2C_MUX *)malloc (sizeof(I2C_MUX)); +		mux->next = NULL; +		/* search name of mux */ +		oldpos = pos; +		ret = i2c_mux_search_next(&pos, buf, len); +		if (ret != 0) +			printf ("%s no name.\n", __FUNCTION__); +		mux->name = (char *)malloc (pos - oldpos + 1); +		memcpy (mux->name, &buf[oldpos], pos - oldpos); +		mux->name[pos - oldpos] = 0; +		/* search address */ +		pos ++; +		oldpos = pos; +		ret = i2c_mux_search_next(&pos, buf, len); +		if (ret != 0) +			printf ("%s no mux address.\n", __FUNCTION__); +		buf[pos] = 0; +		mux->chip = simple_strtoul((char *)&buf[oldpos], NULL, 16); +		buf[pos] = ':'; +		/* search channel */ +		pos ++; +		oldpos = pos; +		ret = i2c_mux_search_next(&pos, buf, len); +		if (ret < 0) +			printf ("%s no mux channel.\n", __FUNCTION__); +		was = 0; +		if (buf[pos] != 0) { +			buf[pos] = 0; +			was = 1; +		} +		mux->channel = simple_strtoul((char *)&buf[oldpos], NULL, 16); +		if (was) +			buf[pos] = ':'; +		if (device->mux == NULL) +			device->mux = mux; +		else { +			I2C_MUX		*muxtmp = device->mux; +			while (muxtmp->next != NULL) { +				muxtmp = muxtmp->next; +			} +			muxtmp->next = mux; +		} +		pos ++; +		oldpos = pos; +	} +	if (ret > 0) { +		/* Add Device */ +		i2c_mux_add_device (device); +		return device; +	} + +	return NULL; +} + +int i2x_mux_select_mux(int bus) +{ +	I2C_MUX_DEVICE  *dev; +	I2C_MUX		*mux; + +	if ((gd->flags & GD_FLG_RELOC) != GD_FLG_RELOC) { +		/* select Default Mux Bus */ +#if defined(CFG_I2C_IVM_BUS) +		i2c_mux_ident_muxstring_f ((uchar *)CFG_I2C_IVM_BUS); +#else +		{ +		unsigned char *buf; +		buf = (unsigned char *) getenv("EEprom_ivm"); +		if (buf != NULL) +			i2c_mux_ident_muxstring_f (buf); +		} +#endif +		return 0; +	} +	dev = i2c_mux_search_device(bus); +	if (dev == NULL) +		return -1; + +	mux = dev->mux; +	while (mux != NULL) { +		if (i2c_write(mux->chip, 0, 0, &mux->channel, 1) != 0) { +			printf ("Error setting Mux: chip:%x channel: \ +				%x\n", mux->chip, mux->channel); +			return -1; +		} +		mux = mux->next; +	} +	return 0; +} +#endif /* CONFIG_I2C_MUX */ + diff --git a/cpu/mpc8260/i2c.c b/cpu/mpc8260/i2c.c index 335177fad..a96fbf841 100644 --- a/cpu/mpc8260/i2c.c +++ b/cpu/mpc8260/i2c.c @@ -780,10 +780,23 @@ unsigned int i2c_get_bus_num(void)  int i2c_set_bus_num(unsigned int bus)  { +#if defined(CONFIG_I2C_MUX) +	if (bus < CFG_MAX_I2C_BUS) { +		i2c_bus_num = bus; +	} else { +		int	ret; + +		ret = i2x_mux_select_mux(bus); +		if (ret == 0) +			i2c_bus_num = bus; +		else +			return ret; +	} +#else  	if (bus >= CFG_MAX_I2C_BUS)  		return -1;  	i2c_bus_num = bus; - +#endif  	return 0;  }  /* TODO: add 100/400k switching */ diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index 63e6a7b0d..0a9feb67c 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -223,10 +223,23 @@ unsigned int i2c_get_bus_num(void)  int i2c_set_bus_num(unsigned int bus)  { +#if defined(CONFIG_I2C_MUX) +	if (bus < CFG_MAX_I2C_BUS) { +		i2c_bus_num = bus; +	} else { +		int	ret; + +		ret = i2x_mux_select_mux(bus); +		if (ret == 0) +			i2c_bus_num = bus; +		else +			return ret; +	} +#else  	if (bus >= CFG_MAX_I2C_BUS)  		return -1;  	i2c_bus_num = bus; - +#endif  	return 0;  } diff --git a/include/configs/mgcoge.h b/include/configs/mgcoge.h index 398e09268..6564c15d0 100644 --- a/include/configs/mgcoge.h +++ b/include/configs/mgcoge.h @@ -203,6 +203,7 @@  #define CONFIG_I2C_CMD_TREE	1  #define CFG_MAX_I2C_BUS		2  #define CFG_I2C_INIT_BOARD	1 +#define CONFIG_I2C_MUX		1  /* EEprom support */  #define CFG_I2C_EEPROM_ADDR_LEN	1 diff --git a/include/configs/mgsuvd.h b/include/configs/mgsuvd.h index 20485750e..e2a7c07cc 100644 --- a/include/configs/mgsuvd.h +++ b/include/configs/mgsuvd.h @@ -374,6 +374,7 @@  #define CONFIG_I2C_CMD_TREE	1  #define CFG_MAX_I2C_BUS		2  #define CFG_I2C_INIT_BOARD	1 +#define CONFIG_I2C_MUX		1  /* EEprom support */  #define CFG_I2C_EEPROM_ADDR_LEN	1 diff --git a/include/i2c.h b/include/i2c.h index a6e797a38..9f771dda1 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -85,6 +85,29 @@ void i2c_init(int speed, int slaveaddr);  void i2c_init_board(void);  #endif +#if defined(CONFIG_I2C_MUX) + +typedef struct _mux { +	uchar	chip; +	uchar	channel; +	char	*name; +	struct _mux	*next; +} I2C_MUX; + +typedef struct _mux_device { +	int	busid; +	I2C_MUX	*mux;	/* List of muxes, to reach the device */ +	struct _mux_device	*next; +} I2C_MUX_DEVICE; + +int	i2c_mux_add_device(I2C_MUX_DEVICE *dev); + +I2C_MUX_DEVICE	*i2c_mux_search_device(int id); +I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf); +int i2x_mux_select_mux(int bus); +int i2c_mux_ident_muxstring_f (uchar *buf); +#endif +  /*   * Probe the given I2C chip address.  Returns 0 if a chip responded,   * not 0 on failure. |