Commit 67b23a322848d828a5e45c0567b72762bfde7abf
Committed by
Wolfgang Denk
1 parent
c24853644d
Exists in
master
and in
54 other branches
I2C: adding new "i2c bus" Command to the I2C Subsystem.
With this Command it is possible to add new I2C Busses, which are behind 1 .. n I2C Muxes. Details see README. Signed-off-by: Heiko Schocher <hs@denx.de>
Showing 7 changed files with 367 additions and 2 deletions Side-by-side Diff
README
... | ... | @@ -1429,6 +1429,53 @@ |
1429 | 1429 | Define this option if you want to use Freescale's I2C driver in |
1430 | 1430 | drivers/i2c/fsl_i2c.c. |
1431 | 1431 | |
1432 | + CONFIG_I2C_MUX | |
1433 | + | |
1434 | + Define this option if you have I2C devices reached over 1 .. n | |
1435 | + I2C Muxes like the pca9544a. This option addes a new I2C | |
1436 | + Command "i2c bus [muxtype:muxaddr:muxchannel]" which adds a | |
1437 | + new I2C Bus to the existing I2C Busses. If you select the | |
1438 | + new Bus with "i2c dev", u-bbot sends first the commandos for | |
1439 | + the muxes to activate this new "bus". | |
1440 | + | |
1441 | + CONFIG_I2C_MULTI_BUS must be also defined, to use this | |
1442 | + feature! | |
1443 | + | |
1444 | + Example: | |
1445 | + Adding a new I2C Bus reached over 2 pca9544a muxes | |
1446 | + The First mux with address 70 and channel 6 | |
1447 | + The Second mux with address 71 and channel 4 | |
1448 | + | |
1449 | + => i2c bus pca9544a:70:6:pca9544a:71:4 | |
1450 | + | |
1451 | + Use the "i2c bus" command without parameter, to get a list | |
1452 | + of I2C Busses with muxes: | |
1453 | + | |
1454 | + => i2c bus | |
1455 | + Busses reached over muxes: | |
1456 | + Bus ID: 2 | |
1457 | + reached over Mux(es): | |
1458 | + pca9544a@70 ch: 4 | |
1459 | + Bus ID: 3 | |
1460 | + reached over Mux(es): | |
1461 | + pca9544a@70 ch: 6 | |
1462 | + pca9544a@71 ch: 4 | |
1463 | + => | |
1464 | + | |
1465 | + If you now switch to the new I2C Bus 3 with "i2c dev 3" | |
1466 | + u-boot sends First the Commando to the mux@70 to enable | |
1467 | + channel 6, and then the Commando to the mux@71 to enable | |
1468 | + the channel 4. | |
1469 | + | |
1470 | + After that, you can use the "normal" i2c commands as | |
1471 | + usual, to communicate with your I2C devices behind | |
1472 | + the 2 muxes. | |
1473 | + | |
1474 | + This option is actually implemented for the bitbanging | |
1475 | + algorithm in common/soft_i2c.c and for the Hardware I2C | |
1476 | + Bus on the MPC8260. But it should be not so difficult | |
1477 | + to add this option to other architectures. | |
1478 | + | |
1432 | 1479 | |
1433 | 1480 | - SPI Support: CONFIG_SPI |
1434 | 1481 |
common/cmd_i2c.c
... | ... | @@ -83,7 +83,9 @@ |
83 | 83 | |
84 | 84 | #include <common.h> |
85 | 85 | #include <command.h> |
86 | +#include <environment.h> | |
86 | 87 | #include <i2c.h> |
88 | +#include <malloc.h> | |
87 | 89 | #include <asm/byteorder.h> |
88 | 90 | |
89 | 91 | /* Display values from last command. |
... | ... | @@ -125,6 +127,14 @@ |
125 | 127 | #define NUM_ELEMENTS_NOPROBE (sizeof(i2c_no_probes)/sizeof(i2c_no_probes[0])) |
126 | 128 | #endif |
127 | 129 | |
130 | +#if defined(CONFIG_I2C_MUX) | |
131 | +static I2C_MUX_DEVICE *i2c_mux_devices = NULL; | |
132 | +static int i2c_mux_busid = CFG_MAX_I2C_BUS; | |
133 | + | |
134 | +DECLARE_GLOBAL_DATA_PTR; | |
135 | + | |
136 | +#endif | |
137 | + | |
128 | 138 | static int |
129 | 139 | mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char *argv[]); |
130 | 140 | |
... | ... | @@ -1188,6 +1198,37 @@ |
1188 | 1198 | return 0; |
1189 | 1199 | } |
1190 | 1200 | |
1201 | +#if defined(CONFIG_I2C_MUX) | |
1202 | +int do_i2c_add_bus(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) | |
1203 | +{ | |
1204 | + int ret=0; | |
1205 | + | |
1206 | + if (argc == 1) { | |
1207 | + /* show all busses */ | |
1208 | + I2C_MUX *mux; | |
1209 | + I2C_MUX_DEVICE *device = i2c_mux_devices; | |
1210 | + | |
1211 | + printf ("Busses reached over muxes:\n"); | |
1212 | + while (device != NULL) { | |
1213 | + printf ("Bus ID: %x\n", device->busid); | |
1214 | + printf (" reached over Mux(es):\n"); | |
1215 | + mux = device->mux; | |
1216 | + while (mux != NULL) { | |
1217 | + printf (" %s@%x ch: %x\n", mux->name, mux->chip, mux->channel); | |
1218 | + mux = mux->next; | |
1219 | + } | |
1220 | + device = device->next; | |
1221 | + } | |
1222 | + } else { | |
1223 | + I2C_MUX_DEVICE *dev; | |
1224 | + | |
1225 | + dev = i2c_mux_ident_muxstring ((uchar *)argv[1]); | |
1226 | + ret = 0; | |
1227 | + } | |
1228 | + return ret; | |
1229 | +} | |
1230 | +#endif /* CONFIG_I2C_MUX */ | |
1231 | + | |
1191 | 1232 | #if defined(CONFIG_I2C_MULTI_BUS) |
1192 | 1233 | int do_i2c_bus_num(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
1193 | 1234 | { |
... | ... | @@ -1226,6 +1267,10 @@ |
1226 | 1267 | |
1227 | 1268 | int do_i2c(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) |
1228 | 1269 | { |
1270 | +#if defined(CONFIG_I2C_MUX) | |
1271 | + if (!strncmp(argv[1], "bu", 2)) | |
1272 | + return do_i2c_add_bus(cmdtp, flag, --argc, ++argv); | |
1273 | +#endif /* CONFIG_I2C_MUX */ | |
1229 | 1274 | if (!strncmp(argv[1], "sp", 2)) |
1230 | 1275 | return do_i2c_bus_speed(cmdtp, flag, --argc, ++argv); |
1231 | 1276 | #if defined(CONFIG_I2C_MULTI_BUS) |
... | ... | @@ -1264,6 +1309,9 @@ |
1264 | 1309 | U_BOOT_CMD( |
1265 | 1310 | i2c, 6, 1, do_i2c, |
1266 | 1311 | "i2c - I2C sub-system\n", |
1312 | +#if defined(CONFIG_I2C_MUX) | |
1313 | + "bus [muxtype:muxaddr:muxchannel] - add a new bus reached over muxes.\n" | |
1314 | +#endif /* CONFIG_I2C_MUX */ | |
1267 | 1315 | "speed [speed] - show or set I2C bus speed\n" |
1268 | 1316 | #if defined(CONFIG_I2C_MULTI_BUS) |
1269 | 1317 | "i2c dev [dev] - show or set current I2C bus\n" |
... | ... | @@ -1335,4 +1383,222 @@ |
1335 | 1383 | " (valid chip values 50..57)\n" |
1336 | 1384 | ); |
1337 | 1385 | #endif |
1386 | + | |
1387 | +#if defined(CONFIG_I2C_MUX) | |
1388 | + | |
1389 | +int i2c_mux_add_device(I2C_MUX_DEVICE *dev) | |
1390 | +{ | |
1391 | + I2C_MUX_DEVICE *devtmp = i2c_mux_devices; | |
1392 | + | |
1393 | + if (i2c_mux_devices == NULL) { | |
1394 | + i2c_mux_devices = dev; | |
1395 | + return 0; | |
1396 | + } | |
1397 | + while (devtmp->next != NULL) | |
1398 | + devtmp = devtmp->next; | |
1399 | + | |
1400 | + devtmp->next = dev; | |
1401 | + return 0; | |
1402 | +} | |
1403 | + | |
1404 | +I2C_MUX_DEVICE *i2c_mux_search_device(int id) | |
1405 | +{ | |
1406 | + I2C_MUX_DEVICE *device = i2c_mux_devices; | |
1407 | + | |
1408 | + while (device != NULL) { | |
1409 | + if (device->busid == id) | |
1410 | + return device; | |
1411 | + device = device->next; | |
1412 | + } | |
1413 | + return NULL; | |
1414 | +} | |
1415 | + | |
1416 | +/* searches in the buf from *pos the next ':'. | |
1417 | + * returns: | |
1418 | + * 0 if found (with *pos = where) | |
1419 | + * < 0 if an error occured | |
1420 | + * > 0 if the end of buf is reached | |
1421 | + */ | |
1422 | +static int i2c_mux_search_next (int *pos, uchar *buf, int len) | |
1423 | +{ | |
1424 | + while ((buf[*pos] != ':') && (*pos < len)) { | |
1425 | + *pos += 1; | |
1426 | + } | |
1427 | + if (*pos >= len) | |
1428 | + return 1; | |
1429 | + if (buf[*pos] != ':') | |
1430 | + return -1; | |
1431 | + return 0; | |
1432 | +} | |
1433 | + | |
1434 | +static int i2c_mux_get_busid (void) | |
1435 | +{ | |
1436 | + int tmp = i2c_mux_busid; | |
1437 | + | |
1438 | + i2c_mux_busid ++; | |
1439 | + return tmp; | |
1440 | +} | |
1441 | + | |
1442 | +/* Analyses a Muxstring and sends immediately the | |
1443 | + Commands to the Muxes. Runs from Flash. | |
1444 | + */ | |
1445 | +int i2c_mux_ident_muxstring_f (uchar *buf) | |
1446 | +{ | |
1447 | + int pos = 0; | |
1448 | + int oldpos; | |
1449 | + int ret = 0; | |
1450 | + int len = strlen((char *)buf); | |
1451 | + int chip; | |
1452 | + uchar channel; | |
1453 | + int was = 0; | |
1454 | + | |
1455 | + while (ret == 0) { | |
1456 | + oldpos = pos; | |
1457 | + /* search name */ | |
1458 | + ret = i2c_mux_search_next(&pos, buf, len); | |
1459 | + if (ret != 0) | |
1460 | + printf ("ERROR\n"); | |
1461 | + /* search address */ | |
1462 | + pos ++; | |
1463 | + oldpos = pos; | |
1464 | + ret = i2c_mux_search_next(&pos, buf, len); | |
1465 | + if (ret != 0) | |
1466 | + printf ("ERROR\n"); | |
1467 | + buf[pos] = 0; | |
1468 | + chip = simple_strtoul((char *)&buf[oldpos], NULL, 16); | |
1469 | + buf[pos] = ':'; | |
1470 | + /* search channel */ | |
1471 | + pos ++; | |
1472 | + oldpos = pos; | |
1473 | + ret = i2c_mux_search_next(&pos, buf, len); | |
1474 | + if (ret < 0) | |
1475 | + printf ("ERROR\n"); | |
1476 | + was = 0; | |
1477 | + if (buf[pos] != 0) { | |
1478 | + buf[pos] = 0; | |
1479 | + was = 1; | |
1480 | + } | |
1481 | + channel = simple_strtoul((char *)&buf[oldpos], NULL, 16); | |
1482 | + if (was) | |
1483 | + buf[pos] = ':'; | |
1484 | + if (i2c_write(chip, 0, 0, &channel, 1) != 0) { | |
1485 | + printf ("Error setting Mux: chip:%x channel: \ | |
1486 | + %x\n", chip, channel); | |
1487 | + return -1; | |
1488 | + } | |
1489 | + pos ++; | |
1490 | + oldpos = pos; | |
1491 | + | |
1492 | + } | |
1493 | + | |
1494 | + return 0; | |
1495 | +} | |
1496 | + | |
1497 | +/* Analyses a Muxstring and if this String is correct | |
1498 | + * adds a new I2C Bus. | |
1499 | + */ | |
1500 | +I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf) | |
1501 | +{ | |
1502 | + I2C_MUX_DEVICE *device; | |
1503 | + I2C_MUX *mux; | |
1504 | + int pos = 0; | |
1505 | + int oldpos; | |
1506 | + int ret = 0; | |
1507 | + int len = strlen((char *)buf); | |
1508 | + int was = 0; | |
1509 | + | |
1510 | + device = (I2C_MUX_DEVICE *)malloc (sizeof(I2C_MUX_DEVICE)); | |
1511 | + device->mux = NULL; | |
1512 | + device->busid = i2c_mux_get_busid (); | |
1513 | + device->next = NULL; | |
1514 | + while (ret == 0) { | |
1515 | + mux = (I2C_MUX *)malloc (sizeof(I2C_MUX)); | |
1516 | + mux->next = NULL; | |
1517 | + /* search name of mux */ | |
1518 | + oldpos = pos; | |
1519 | + ret = i2c_mux_search_next(&pos, buf, len); | |
1520 | + if (ret != 0) | |
1521 | + printf ("%s no name.\n", __FUNCTION__); | |
1522 | + mux->name = (char *)malloc (pos - oldpos + 1); | |
1523 | + memcpy (mux->name, &buf[oldpos], pos - oldpos); | |
1524 | + mux->name[pos - oldpos] = 0; | |
1525 | + /* search address */ | |
1526 | + pos ++; | |
1527 | + oldpos = pos; | |
1528 | + ret = i2c_mux_search_next(&pos, buf, len); | |
1529 | + if (ret != 0) | |
1530 | + printf ("%s no mux address.\n", __FUNCTION__); | |
1531 | + buf[pos] = 0; | |
1532 | + mux->chip = simple_strtoul((char *)&buf[oldpos], NULL, 16); | |
1533 | + buf[pos] = ':'; | |
1534 | + /* search channel */ | |
1535 | + pos ++; | |
1536 | + oldpos = pos; | |
1537 | + ret = i2c_mux_search_next(&pos, buf, len); | |
1538 | + if (ret < 0) | |
1539 | + printf ("%s no mux channel.\n", __FUNCTION__); | |
1540 | + was = 0; | |
1541 | + if (buf[pos] != 0) { | |
1542 | + buf[pos] = 0; | |
1543 | + was = 1; | |
1544 | + } | |
1545 | + mux->channel = simple_strtoul((char *)&buf[oldpos], NULL, 16); | |
1546 | + if (was) | |
1547 | + buf[pos] = ':'; | |
1548 | + if (device->mux == NULL) | |
1549 | + device->mux = mux; | |
1550 | + else { | |
1551 | + I2C_MUX *muxtmp = device->mux; | |
1552 | + while (muxtmp->next != NULL) { | |
1553 | + muxtmp = muxtmp->next; | |
1554 | + } | |
1555 | + muxtmp->next = mux; | |
1556 | + } | |
1557 | + pos ++; | |
1558 | + oldpos = pos; | |
1559 | + } | |
1560 | + if (ret > 0) { | |
1561 | + /* Add Device */ | |
1562 | + i2c_mux_add_device (device); | |
1563 | + return device; | |
1564 | + } | |
1565 | + | |
1566 | + return NULL; | |
1567 | +} | |
1568 | + | |
1569 | +int i2x_mux_select_mux(int bus) | |
1570 | +{ | |
1571 | + I2C_MUX_DEVICE *dev; | |
1572 | + I2C_MUX *mux; | |
1573 | + | |
1574 | + if ((gd->flags & GD_FLG_RELOC) != GD_FLG_RELOC) { | |
1575 | + /* select Default Mux Bus */ | |
1576 | +#if defined(CFG_I2C_IVM_BUS) | |
1577 | + i2c_mux_ident_muxstring_f ((uchar *)CFG_I2C_IVM_BUS); | |
1578 | +#else | |
1579 | + { | |
1580 | + unsigned char *buf; | |
1581 | + buf = (unsigned char *) getenv("EEprom_ivm"); | |
1582 | + if (buf != NULL) | |
1583 | + i2c_mux_ident_muxstring_f (buf); | |
1584 | + } | |
1585 | +#endif | |
1586 | + return 0; | |
1587 | + } | |
1588 | + dev = i2c_mux_search_device(bus); | |
1589 | + if (dev == NULL) | |
1590 | + return -1; | |
1591 | + | |
1592 | + mux = dev->mux; | |
1593 | + while (mux != NULL) { | |
1594 | + if (i2c_write(mux->chip, 0, 0, &mux->channel, 1) != 0) { | |
1595 | + printf ("Error setting Mux: chip:%x channel: \ | |
1596 | + %x\n", mux->chip, mux->channel); | |
1597 | + return -1; | |
1598 | + } | |
1599 | + mux = mux->next; | |
1600 | + } | |
1601 | + return 0; | |
1602 | +} | |
1603 | +#endif /* CONFIG_I2C_MUX */ |
cpu/mpc8260/i2c.c
... | ... | @@ -780,10 +780,23 @@ |
780 | 780 | |
781 | 781 | int i2c_set_bus_num(unsigned int bus) |
782 | 782 | { |
783 | +#if defined(CONFIG_I2C_MUX) | |
784 | + if (bus < CFG_MAX_I2C_BUS) { | |
785 | + i2c_bus_num = bus; | |
786 | + } else { | |
787 | + int ret; | |
788 | + | |
789 | + ret = i2x_mux_select_mux(bus); | |
790 | + if (ret == 0) | |
791 | + i2c_bus_num = bus; | |
792 | + else | |
793 | + return ret; | |
794 | + } | |
795 | +#else | |
783 | 796 | if (bus >= CFG_MAX_I2C_BUS) |
784 | 797 | return -1; |
785 | 798 | i2c_bus_num = bus; |
786 | - | |
799 | +#endif | |
787 | 800 | return 0; |
788 | 801 | } |
789 | 802 | /* TODO: add 100/400k switching */ |
drivers/i2c/soft_i2c.c
... | ... | @@ -223,10 +223,23 @@ |
223 | 223 | |
224 | 224 | int i2c_set_bus_num(unsigned int bus) |
225 | 225 | { |
226 | +#if defined(CONFIG_I2C_MUX) | |
227 | + if (bus < CFG_MAX_I2C_BUS) { | |
228 | + i2c_bus_num = bus; | |
229 | + } else { | |
230 | + int ret; | |
231 | + | |
232 | + ret = i2x_mux_select_mux(bus); | |
233 | + if (ret == 0) | |
234 | + i2c_bus_num = bus; | |
235 | + else | |
236 | + return ret; | |
237 | + } | |
238 | +#else | |
226 | 239 | if (bus >= CFG_MAX_I2C_BUS) |
227 | 240 | return -1; |
228 | 241 | i2c_bus_num = bus; |
229 | - | |
242 | +#endif | |
230 | 243 | return 0; |
231 | 244 | } |
232 | 245 |
include/configs/mgcoge.h
include/configs/mgsuvd.h
include/i2c.h
... | ... | @@ -85,6 +85,29 @@ |
85 | 85 | void i2c_init_board(void); |
86 | 86 | #endif |
87 | 87 | |
88 | +#if defined(CONFIG_I2C_MUX) | |
89 | + | |
90 | +typedef struct _mux { | |
91 | + uchar chip; | |
92 | + uchar channel; | |
93 | + char *name; | |
94 | + struct _mux *next; | |
95 | +} I2C_MUX; | |
96 | + | |
97 | +typedef struct _mux_device { | |
98 | + int busid; | |
99 | + I2C_MUX *mux; /* List of muxes, to reach the device */ | |
100 | + struct _mux_device *next; | |
101 | +} I2C_MUX_DEVICE; | |
102 | + | |
103 | +int i2c_mux_add_device(I2C_MUX_DEVICE *dev); | |
104 | + | |
105 | +I2C_MUX_DEVICE *i2c_mux_search_device(int id); | |
106 | +I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf); | |
107 | +int i2x_mux_select_mux(int bus); | |
108 | +int i2c_mux_ident_muxstring_f (uchar *buf); | |
109 | +#endif | |
110 | + | |
88 | 111 | /* |
89 | 112 | * Probe the given I2C chip address. Returns 0 if a chip responded, |
90 | 113 | * not 0 on failure. |