Commit 3d1957f0ea0133ec06f9c6fd85dc1acdf66ad29c

Authored by Simon Glass
1 parent df358c6bec

dm: i2c: Add support for multiplexed I2C buses

Add a new I2C_MUX uclass. Devices in this class can multiplex between
several I2C buses, selecting them one at a time for use by the system.
The multiplexing mechanism is left to the driver to decide - it may be
controlled by GPIOs, for example.

The uclass supports only two methods: select() and deselect().

The current mux state is expected to be stored in the mux itself since
it is the only thing that knows how to make things work. The mux can
record the current state and then avoid switching unless it is necessary.
So select() can be skipped if the mux is already in the correct state.
Also deselect() can be made a nop if required.

Signed-off-by: Simon Glass <sjg@chromium.org>

Showing 8 changed files with 316 additions and 0 deletions Side-by-side Diff

doc/device-tree-bindings/i2c/i2c-mux.txt
  1 +Common i2c bus multiplexer/switch properties.
  2 +
  3 +An i2c bus multiplexer/switch will have several child busses that are
  4 +numbered uniquely in a device dependent manner. The nodes for an i2c bus
  5 +multiplexer/switch will have one child node for each child
  6 +bus.
  7 +
  8 +Required properties:
  9 +- #address-cells = <1>;
  10 +- #size-cells = <0>;
  11 +
  12 +Required properties for child nodes:
  13 +- #address-cells = <1>;
  14 +- #size-cells = <0>;
  15 +- reg : The sub-bus number.
  16 +
  17 +Optional properties for child nodes:
  18 +- Other properties specific to the multiplexer/switch hardware.
  19 +- Child nodes conforming to i2c bus binding
  20 +
  21 +
  22 +Example :
  23 +
  24 + /*
  25 + An NXP pca9548 8 channel I2C multiplexer at address 0x70
  26 + with two NXP pca8574 GPIO expanders attached, one each to
  27 + ports 3 and 4.
  28 + */
  29 +
  30 + mux@70 {
  31 + compatible = "nxp,pca9548";
  32 + reg = <0x70>;
  33 + #address-cells = <1>;
  34 + #size-cells = <0>;
  35 +
  36 + i2c@3 {
  37 + #address-cells = <1>;
  38 + #size-cells = <0>;
  39 + reg = <3>;
  40 +
  41 + gpio1: gpio@38 {
  42 + compatible = "nxp,pca8574";
  43 + reg = <0x38>;
  44 + #gpio-cells = <2>;
  45 + gpio-controller;
  46 + };
  47 + };
  48 + i2c@4 {
  49 + #address-cells = <1>;
  50 + #size-cells = <0>;
  51 + reg = <4>;
  52 +
  53 + gpio2: gpio@38 {
  54 + compatible = "nxp,pca8574";
  55 + reg = <0x38>;
  56 + #gpio-cells = <2>;
  57 + gpio-controller;
  58 + };
  59 + };
  60 + };
... ... @@ -73,4 +73,6 @@
73 73 help
74 74 Support for UniPhier FIFO-builtin I2C controller driver.
75 75 This I2C controller is used on PH1-Pro4 or newer UniPhier SoCs.
  76 +
  77 +source "drivers/i2c/muxes/Kconfig"
drivers/i2c/Makefile
... ... @@ -37,4 +37,6 @@
37 37 obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o
38 38 obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o
39 39 obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o
  40 +
  41 +obj-y += muxes/
drivers/i2c/muxes/Kconfig
  1 +config I2C_MUX
  2 + bool "Suport I2C multiplexers"
  3 + depends on DM_I2C
  4 + help
  5 + This enables I2C buses to be multiplexed, so that you can select
  6 + one of several buses using some sort of control mechanism. The
  7 + bus select is handled automatically when that bus is accessed,
  8 + using a suitable I2C MUX driver.
drivers/i2c/muxes/Makefile
  1 +#
  2 +# Copyright (c) 2015 Google, Inc
  3 +#
  4 +# SPDX-License-Identifier: GPL-2.0+
  5 +#
  6 +obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o
drivers/i2c/muxes/i2c-mux-uclass.c
  1 +/*
  2 + * Copyright (c) 2015 Google, Inc
  3 + * Written by Simon Glass <sjg@chromium.org>
  4 + *
  5 + * SPDX-License-Identifier: GPL-2.0+
  6 + */
  7 +
  8 +#include <common.h>
  9 +#include <dm.h>
  10 +#include <errno.h>
  11 +#include <i2c.h>
  12 +#include <dm/lists.h>
  13 +#include <dm/root.h>
  14 +
  15 +DECLARE_GLOBAL_DATA_PTR;
  16 +
  17 +/**
  18 + * struct i2c_mux: Information the uclass stores about an I2C mux
  19 + *
  20 + * @selected: Currently selected mux, or -1 for none
  21 + * @i2c_bus: I2C bus to use for communcation
  22 + */
  23 +struct i2c_mux {
  24 + int selected;
  25 + struct udevice *i2c_bus;
  26 +};
  27 +
  28 +/**
  29 + * struct i2c_mux_bus: Information about each bus the mux controls
  30 + *
  31 + * @channel: Channel number used to select this bus
  32 + */
  33 +struct i2c_mux_bus {
  34 + uint channel;
  35 +};
  36 +
  37 +/* Find out the mux channel number */
  38 +static int i2c_mux_child_post_bind(struct udevice *dev)
  39 +{
  40 + struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
  41 + int channel;
  42 +
  43 + channel = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
  44 + if (channel < 0)
  45 + return -EINVAL;
  46 + plat->channel = channel;
  47 +
  48 + return 0;
  49 +}
  50 +
  51 +/* Find the I2C buses selected by this mux */
  52 +static int i2c_mux_post_bind(struct udevice *mux)
  53 +{
  54 + const void *blob = gd->fdt_blob;
  55 + int ret;
  56 + int offset;
  57 +
  58 + debug("%s: %s\n", __func__, mux->name);
  59 + /*
  60 + * There is no compatible string in the sub-nodes, so we must manually
  61 + * bind these
  62 + */
  63 + for (offset = fdt_first_subnode(blob, mux->of_offset);
  64 + offset > 0;
  65 + offset = fdt_next_subnode(blob, offset)) {
  66 + struct udevice *dev;
  67 + const char *name;
  68 +
  69 + name = fdt_get_name(blob, offset, NULL);
  70 + ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv", name,
  71 + offset, &dev);
  72 + debug(" - bind ret=%d, %s\n", ret, dev ? dev->name : NULL);
  73 + if (ret)
  74 + return ret;
  75 + }
  76 +
  77 + return 0;
  78 +}
  79 +
  80 +/* Set up the mux ready for use */
  81 +static int i2c_mux_post_probe(struct udevice *mux)
  82 +{
  83 + struct i2c_mux *priv = dev_get_uclass_priv(mux);
  84 + int ret;
  85 +
  86 + debug("%s: %s\n", __func__, mux->name);
  87 + priv->selected = -1;
  88 +
  89 + ret = uclass_get_device_by_phandle(UCLASS_I2C, mux, "i2c-parent",
  90 + &priv->i2c_bus);
  91 + if (ret)
  92 + return ret;
  93 + debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, priv->i2c_bus->name);
  94 +
  95 + return 0;
  96 +}
  97 +
  98 +int i2c_mux_select(struct udevice *dev)
  99 +{
  100 + struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
  101 + struct udevice *mux = dev->parent;
  102 + struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
  103 +
  104 + if (!ops->select)
  105 + return -ENOSYS;
  106 +
  107 + return ops->select(mux, dev, plat->channel);
  108 +}
  109 +
  110 +int i2c_mux_deselect(struct udevice *dev)
  111 +{
  112 + struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
  113 + struct udevice *mux = dev->parent;
  114 + struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
  115 +
  116 + if (!ops->deselect)
  117 + return -ENOSYS;
  118 +
  119 + return ops->deselect(mux, dev, plat->channel);
  120 +}
  121 +
  122 +static int i2c_mux_bus_set_bus_speed(struct udevice *dev, unsigned int speed)
  123 +{
  124 + struct udevice *mux = dev->parent;
  125 + struct i2c_mux *priv = dev_get_uclass_priv(mux);
  126 + int ret, ret2;
  127 +
  128 + ret = i2c_mux_select(dev);
  129 + if (ret)
  130 + return ret;
  131 + ret = dm_i2c_set_bus_speed(priv->i2c_bus, speed);
  132 + ret2 = i2c_mux_deselect(dev);
  133 +
  134 + return ret ? ret : ret2;
  135 +}
  136 +
  137 +static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr,
  138 + uint chip_flags)
  139 +{
  140 + struct udevice *mux = dev->parent;
  141 + struct i2c_mux *priv = dev_get_uclass_priv(mux);
  142 + struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
  143 + int ret, ret2;
  144 +
  145 + debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
  146 + if (!ops->probe_chip)
  147 + return -ENOSYS;
  148 + ret = i2c_mux_select(dev);
  149 + if (ret)
  150 + return ret;
  151 + ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags);
  152 + ret2 = i2c_mux_deselect(dev);
  153 +
  154 + return ret ? ret : ret2;
  155 +}
  156 +
  157 +static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg,
  158 + int nmsgs)
  159 +{
  160 + struct udevice *mux = dev->parent;
  161 + struct i2c_mux *priv = dev_get_uclass_priv(mux);
  162 + struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
  163 + int ret, ret2;
  164 +
  165 + debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
  166 + if (!ops->xfer)
  167 + return -ENOSYS;
  168 + ret = i2c_mux_select(dev);
  169 + if (ret)
  170 + return ret;
  171 + ret = ops->xfer(priv->i2c_bus, msg, nmsgs);
  172 + ret2 = i2c_mux_deselect(dev);
  173 +
  174 + return ret ? ret : ret2;
  175 +}
  176 +
  177 +static const struct dm_i2c_ops i2c_mux_bus_ops = {
  178 + .xfer = i2c_mux_bus_xfer,
  179 + .probe_chip = i2c_mux_bus_probe,
  180 + .set_bus_speed = i2c_mux_bus_set_bus_speed,
  181 +};
  182 +
  183 +U_BOOT_DRIVER(i2c_mux_bus) = {
  184 + .name = "i2c_mux_bus_drv",
  185 + .id = UCLASS_I2C,
  186 + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
  187 + .ops = &i2c_mux_bus_ops,
  188 +};
  189 +
  190 +UCLASS_DRIVER(i2c_mux) = {
  191 + .id = UCLASS_I2C_MUX,
  192 + .name = "i2c_mux",
  193 + .post_bind = i2c_mux_post_bind,
  194 + .post_probe = i2c_mux_post_probe,
  195 + .per_device_auto_alloc_size = sizeof(struct i2c_mux),
  196 + .per_child_platdata_auto_alloc_size = sizeof(struct i2c_mux_bus),
  197 + .child_post_bind = i2c_mux_child_post_bind,
  198 +};
include/dm/uclass-id.h
... ... @@ -35,6 +35,7 @@
35 35 UCLASS_I2C, /* I2C bus */
36 36 UCLASS_I2C_EEPROM, /* I2C EEPROM device */
37 37 UCLASS_I2C_GENERIC, /* Generic I2C device */
  38 + UCLASS_I2C_MUX, /* I2C multiplexer */
38 39 UCLASS_LED, /* Light-emitting diode (LED) */
39 40 UCLASS_LPC, /* x86 'low pin count' interface */
40 41 UCLASS_MASS_STORAGE, /* Mass storage device */
... ... @@ -445,6 +445,45 @@
445 445 #define i2c_get_ops(dev) ((struct dm_i2c_ops *)(dev)->driver->ops)
446 446  
447 447 /**
  448 + * struct i2c_mux_ops - operations for an I2C mux
  449 + *
  450 + * The current mux state is expected to be stored in the mux itself since
  451 + * it is the only thing that knows how to make things work. The mux can
  452 + * record the current state and then avoid switching unless it is necessary.
  453 + * So select() can be skipped if the mux is already in the correct state.
  454 + * Also deselect() can be made a nop if required.
  455 + */
  456 +struct i2c_mux_ops {
  457 + /**
  458 + * select() - select one of of I2C buses attached to a mux
  459 + *
  460 + * This will be called when there is no bus currently selected by the
  461 + * mux. This method does not need to deselect the old bus since
  462 + * deselect() will be already have been called if necessary.
  463 + *
  464 + * @mux: Mux device
  465 + * @bus: I2C bus to select
  466 + * @channel: Channel number correponding to the bus to select
  467 + * @return 0 if OK, -ve on error
  468 + */
  469 + int (*select)(struct udevice *mux, struct udevice *bus, uint channel);
  470 +
  471 + /**
  472 + * deselect() - select one of of I2C buses attached to a mux
  473 + *
  474 + * This is used to deselect the currently selected I2C bus.
  475 + *
  476 + * @mux: Mux device
  477 + * @bus: I2C bus to deselect
  478 + * @channel: Channel number correponding to the bus to deselect
  479 + * @return 0 if OK, -ve on error
  480 + */
  481 + int (*deselect)(struct udevice *mux, struct udevice *bus, uint channel);
  482 +};
  483 +
  484 +#define i2c_mux_get_ops(dev) ((struct i2c_mux_ops *)(dev)->driver->ops)
  485 +
  486 +/**
448 487 * i2c_get_chip() - get a device to use to access a chip on a bus
449 488 *
450 489 * This returns the device for the given chip address. The device can then