[U-Boot] [PATCH v2] i2c: muxes: Add support for TI PCA954X mux

Add support for common TI i2c mux which is available on ZynqMP zcu102 board. DM i2c mux core code is selecting/deselecting bus before/after every command is performed that's why only one channel is active at a time. That's also the reason why deselect is just disable all available channels.
Signed-off-by: Michal Simek michal.simek@xilinx.com Reviewed-by: Heiko Schocher hs@denx.de Reviewed-by: Simon Glass sjg@chromium.org ---
Changes in v2: - use fdtdec_get_int() for getting i2c address instead of dev_get_addr() which is designed for MMIO accesses and requires non zero size cells. - Change returned error code - Simon - Use 954x instead of 954X and 95xx - Michal - Extend Kconfig description - Simon - Add comments to private structure - Simon
Simon, Heiko: Feel free to comment this version and reject your tag.
--- drivers/i2c/muxes/Kconfig | 10 ++++++ drivers/i2c/muxes/Makefile | 1 + drivers/i2c/muxes/pca954x.c | 79 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 drivers/i2c/muxes/pca954x.c
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index f959d9de9e8b..48900ed2afc5 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -24,3 +24,13 @@ config I2C_ARB_GPIO_CHALLENGE I2C multimaster arbitration scheme using GPIOs and a challenge & response mechanism where masters have to claim the bus by asserting a GPIO. + +config I2C_MUX_PCA954x + tristate "TI PCA954x I2C Mux/switches" + depends on I2C_MUX + help + If you say yes here you get support for the TI PCA954x + I2C mux/switch devices. It is x width I2C multiplexer which enables to + paritioning I2C bus and connect multiple devices with the same address + to the same I2C controller where driver handles proper routing to + target i2c device. PCA9544 and PCA9548 are supported. diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 47c1240d7e9e..0811add4216e 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -5,3 +5,4 @@ # obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o obj-$(CONFIG_$(SPL_)I2C_MUX) += i2c-mux-uclass.o +obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c new file mode 100644 index 000000000000..7e0d2da4d605 --- /dev/null +++ b/drivers/i2c/muxes/pca954x.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 - 2016 Xilinx, Inc. + * Written by Michal Simek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <asm/gpio.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct pca954x_priv { + u32 addr; /* I2C mux address */ + u32 width; /* I2C mux width - number of busses */ +}; + +static int pca954x_deselect(struct udevice *mux, struct udevice *bus, + uint channel) +{ + struct pca954x_priv *priv = dev_get_priv(mux); + uchar byte = 0; + + return dm_i2c_write(mux, priv->addr, &byte, 1); +} + +static int pca954x_select(struct udevice *mux, struct udevice *bus, + uint channel) +{ + struct pca954x_priv *priv = dev_get_priv(mux); + uchar byte = 1 << channel; + + return dm_i2c_write(mux, priv->addr, &byte, 1); +} + +static const struct i2c_mux_ops pca954x_ops = { + .select = pca954x_select, + .deselect = pca954x_deselect, +}; + +static const struct udevice_id pca954x_ids[] = { + { .compatible = "nxp,pca9548", .data = (ulong)8 }, + { .compatible = "nxp,pca9544", .data = (ulong)4 }, + { } +}; + +static int pca954x_ofdata_to_platdata(struct udevice *dev) +{ + struct pca954x_priv *priv = dev_get_priv(dev); + + priv->addr = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", 0); + if (!priv->addr) { + debug("MUX not found\n"); + return -ENODEV; + } + priv->width = dev_get_driver_data(dev); + + if (!priv->width) { + debug("No I2C MUX width specified\n"); + return -EINVAL; + } + + debug("Device %s at 0x%x with width %d\n", + dev->name, priv->addr, priv->width); + + return 0; +} + +U_BOOT_DRIVER(pca954x) = { + .name = "pca954x", + .id = UCLASS_I2C_MUX, + .of_match = pca954x_ids, + .ops = &pca954x_ops, + .ofdata_to_platdata = pca954x_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct pca954x_priv), +};

Looking at the TI PCA9544 datasheet, this looks like a well behaved part that should avoid some of the problems I've seen with the NXP954x series. If a design incorporates GPIO control of the Vcc line into these parts, then it should be able to deal with stuck downstream busses that cannot be recovered through the usual inband methods (9-clocks, SMBUS holding clock low for x ms, etc.)
There could be a stub for toggling Vcc to get POR, or for clearing a downstream bus, but that gets into board specific implementation details.
On Mon, Apr 25, 2016 at 1:50 AM Michal Simek michal.simek@xilinx.com wrote:
Add support for common TI i2c mux which is available on ZynqMP zcu102 board. DM i2c mux core code is selecting/deselecting bus before/after every command is performed that's why only one channel is active at a time. That's also the reason why deselect is just disable all available channels.
Signed-off-by: Michal Simek michal.simek@xilinx.com Reviewed-by: Heiko Schocher hs@denx.de Reviewed-by: Simon Glass sjg@chromium.org
Changes in v2:
- use fdtdec_get_int() for getting i2c address instead of dev_get_addr() which is designed for MMIO accesses and requires non zero size cells.
- Change returned error code - Simon
- Use 954x instead of 954X and 95xx - Michal
- Extend Kconfig description - Simon
- Add comments to private structure - Simon
Simon, Heiko: Feel free to comment this version and reject your tag.
drivers/i2c/muxes/Kconfig | 10 ++++++ drivers/i2c/muxes/Makefile | 1 + drivers/i2c/muxes/pca954x.c | 79 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 drivers/i2c/muxes/pca954x.c
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index f959d9de9e8b..48900ed2afc5 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -24,3 +24,13 @@ config I2C_ARB_GPIO_CHALLENGE I2C multimaster arbitration scheme using GPIOs and a challenge & response mechanism where masters have to claim the bus by asserting a GPIO.
+config I2C_MUX_PCA954x
tristate "TI PCA954x I2C Mux/switches"
depends on I2C_MUX
help
If you say yes here you get support for the TI PCA954x
I2C mux/switch devices. It is x width I2C multiplexer which
enables to
paritioning I2C bus and connect multiple devices with the same
address
to the same I2C controller where driver handles proper routing to
target i2c device. PCA9544 and PCA9548 are supported.
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 47c1240d7e9e..0811add4216e 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -5,3 +5,4 @@ # obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o obj-$(CONFIG_$(SPL_)I2C_MUX) += i2c-mux-uclass.o +obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c new file mode 100644 index 000000000000..7e0d2da4d605 --- /dev/null +++ b/drivers/i2c/muxes/pca954x.c @@ -0,0 +1,79 @@ +/*
- Copyright (C) 2015 - 2016 Xilinx, Inc.
- Written by Michal Simek
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <asm/gpio.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct pca954x_priv {
u32 addr; /* I2C mux address */
u32 width; /* I2C mux width - number of busses */
+};
+static int pca954x_deselect(struct udevice *mux, struct udevice *bus,
uint channel)
+{
struct pca954x_priv *priv = dev_get_priv(mux);
uchar byte = 0;
return dm_i2c_write(mux, priv->addr, &byte, 1);
+}
+static int pca954x_select(struct udevice *mux, struct udevice *bus,
uint channel)
+{
struct pca954x_priv *priv = dev_get_priv(mux);
uchar byte = 1 << channel;
return dm_i2c_write(mux, priv->addr, &byte, 1);
+}
+static const struct i2c_mux_ops pca954x_ops = {
.select = pca954x_select,
.deselect = pca954x_deselect,
+};
+static const struct udevice_id pca954x_ids[] = {
{ .compatible = "nxp,pca9548", .data = (ulong)8 },
{ .compatible = "nxp,pca9544", .data = (ulong)4 },
{ }
+};
+static int pca954x_ofdata_to_platdata(struct udevice *dev) +{
struct pca954x_priv *priv = dev_get_priv(dev);
priv->addr = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg",
0);
if (!priv->addr) {
debug("MUX not found\n");
return -ENODEV;
}
priv->width = dev_get_driver_data(dev);
if (!priv->width) {
debug("No I2C MUX width specified\n");
return -EINVAL;
}
debug("Device %s at 0x%x with width %d\n",
dev->name, priv->addr, priv->width);
return 0;
+}
+U_BOOT_DRIVER(pca954x) = {
.name = "pca954x",
.id = UCLASS_I2C_MUX,
.of_match = pca954x_ids,
.ops = &pca954x_ops,
.ofdata_to_platdata = pca954x_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct pca954x_priv),
+};
1.9.1
participants (2)
-
Ben Stoltz
-
Michal Simek