2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2015-08-03 14:19:21 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2015 Google, Inc
|
|
|
|
* Written by Simon Glass <sjg@chromium.org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <dm.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <i2c.h>
|
2020-05-10 17:40:05 +00:00
|
|
|
#include <log.h>
|
2020-02-03 14:36:16 +00:00
|
|
|
#include <malloc.h>
|
2015-08-03 14:19:21 +00:00
|
|
|
#include <dm/lists.h>
|
|
|
|
#include <dm/root.h>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct i2c_mux: Information the uclass stores about an I2C mux
|
|
|
|
*
|
|
|
|
* @selected: Currently selected mux, or -1 for none
|
|
|
|
* @i2c_bus: I2C bus to use for communcation
|
|
|
|
*/
|
|
|
|
struct i2c_mux {
|
|
|
|
int selected;
|
|
|
|
struct udevice *i2c_bus;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct i2c_mux_bus: Information about each bus the mux controls
|
|
|
|
*
|
|
|
|
* @channel: Channel number used to select this bus
|
|
|
|
*/
|
|
|
|
struct i2c_mux_bus {
|
|
|
|
uint channel;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Find out the mux channel number */
|
|
|
|
static int i2c_mux_child_post_bind(struct udevice *dev)
|
|
|
|
{
|
2020-12-03 23:55:18 +00:00
|
|
|
struct i2c_mux_bus *plat = dev_get_parent_plat(dev);
|
2015-08-03 14:19:21 +00:00
|
|
|
int channel;
|
|
|
|
|
2019-01-09 10:58:24 +00:00
|
|
|
channel = dev_read_u32_default(dev, "reg", -1);
|
2015-08-03 14:19:21 +00:00
|
|
|
if (channel < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
plat->channel = channel;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the I2C buses selected by this mux */
|
|
|
|
static int i2c_mux_post_bind(struct udevice *mux)
|
|
|
|
{
|
2017-05-19 02:09:07 +00:00
|
|
|
ofnode node;
|
2015-08-03 14:19:21 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
debug("%s: %s\n", __func__, mux->name);
|
|
|
|
/*
|
|
|
|
* There is no compatible string in the sub-nodes, so we must manually
|
|
|
|
* bind these
|
|
|
|
*/
|
2017-05-19 02:09:07 +00:00
|
|
|
dev_for_each_subnode(node, mux) {
|
2015-08-03 14:19:21 +00:00
|
|
|
struct udevice *dev;
|
|
|
|
const char *name;
|
i2c: mux: Generate longer i2c mux name
For !DM case busses are listed as
ZynqMP> i2c bus
Bus 0: zynq_0
Bus 1: zynq_0->PCA9544A@0x75:0
Bus 2: zynq_0->PCA9544A@0x75:1
Bus 3: zynq_0->PCA9544A@0x75:2
Bus 4: zynq_1
Bus 5: zynq_1->PCA9548@0x74:0
Bus 6: zynq_1->PCA9548@0x74:1
Bus 7: zynq_1->PCA9548@0x74:2
Bus 8: zynq_1->PCA9548@0x74:3
Bus 9: zynq_1->PCA9548@0x74:4
Bus 10: zynq_1->PCA9548@0x75:0
Bus 11: zynq_1->PCA9548@0x75:1
Bus 12: zynq_1->PCA9548@0x75:2
Bus 13: zynq_1->PCA9548@0x75:3
Bus 14: zynq_1->PCA9548@0x75:4
Bus 15: zynq_1->PCA9548@0x75:5
Bus 16: zynq_1->PCA9548@0x75:6
Bus 17: zynq_1->PCA9548@0x75:7
where is exactly describing i2c bus topology.
By moving to DM case i2c mux buses are using names from DT and because
i2c-muxes describing sub busses with the same names like i2c@0, etc it
is hard to identify which bus is where.
Linux is adding topology information to i2c-mux busses to identify them
better.
This patch is doing the same and composing bus name with topology
information.
When patch is applied with topology information on zcu102-revA.
ZynqMP> i2c bus
Bus 0: i2c@ff020000
20: gpio@20, offset len 1, flags 0
21: gpio@21, offset len 1, flags 0
75: i2c-mux@75, offset len 1, flags 0
Bus 2: i2c@ff020000->i2c-mux@75->i2c@0
Bus 3: i2c@ff020000->i2c-mux@75->i2c@1
Bus 4: i2c@ff020000->i2c-mux@75->i2c@2
Bus 1: i2c@ff030000 (active 1)
74: i2c-mux@74, offset len 1, flags 0
75: i2c-mux@75, offset len 1, flags 0
Bus 5: i2c@ff030000->i2c-mux@74->i2c@0 (active 5)
54: eeprom@54, offset len 1, flags 0
Bus 6: i2c@ff030000->i2c-mux@74->i2c@1
Bus 7: i2c@ff030000->i2c-mux@74->i2c@2
Bus 8: i2c@ff030000->i2c-mux@74->i2c@3
Bus 9: i2c@ff030000->i2c-mux@74->i2c@4
Bus 10: i2c@ff030000->i2c-mux@75->i2c@0
Bus 11: i2c@ff030000->i2c-mux@75->i2c@1
Bus 12: i2c@ff030000->i2c-mux@75->i2c@2
Bus 13: i2c@ff030000->i2c-mux@75->i2c@3
Bus 14: i2c@ff030000->i2c-mux@75->i2c@4
Bus 15: i2c@ff030000->i2c-mux@75->i2c@5
Bus 16: i2c@ff030000->i2c-mux@75->i2c@6
Bus 17: i2c@ff030000->i2c-mux@75->i2c@7
Behavior before the patch is applied.
ZynqMP> i2c bus
Bus 0: i2c@ff020000
20: gpio@20, offset len 1, flags 0
21: gpio@21, offset len 1, flags 0
75: i2c-mux@75, offset len 1, flags 0
Bus 2: i2c@0
Bus 3: i2c@1
Bus 4: i2c@2
Bus 1: i2c@ff030000 (active 1)
74: i2c-mux@74, offset len 1, flags 0
75: i2c-mux@75, offset len 1, flags 0
Bus 5: i2c@0 (active 5)
54: eeprom@54, offset len 1, flags 0
Bus 6: i2c@1
Bus 7: i2c@2
Bus 8: i2c@3
Bus 9: i2c@4
Bus 10: i2c@0
Bus 11: i2c@1
Bus 12: i2c@2
Bus 13: i2c@3
Bus 14: i2c@4
Bus 15: i2c@5
Bus 16: i2c@6
Bus 17: i2c@7
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Heiko Schocher <hs@denx.de>
2019-01-31 15:31:03 +00:00
|
|
|
const char *arrow = "->";
|
|
|
|
char *full_name;
|
|
|
|
int parent_name_len, arrow_len, mux_name_len, name_len;
|
2015-08-03 14:19:21 +00:00
|
|
|
|
2017-05-19 02:09:07 +00:00
|
|
|
name = ofnode_get_name(node);
|
i2c: mux: Generate longer i2c mux name
For !DM case busses are listed as
ZynqMP> i2c bus
Bus 0: zynq_0
Bus 1: zynq_0->PCA9544A@0x75:0
Bus 2: zynq_0->PCA9544A@0x75:1
Bus 3: zynq_0->PCA9544A@0x75:2
Bus 4: zynq_1
Bus 5: zynq_1->PCA9548@0x74:0
Bus 6: zynq_1->PCA9548@0x74:1
Bus 7: zynq_1->PCA9548@0x74:2
Bus 8: zynq_1->PCA9548@0x74:3
Bus 9: zynq_1->PCA9548@0x74:4
Bus 10: zynq_1->PCA9548@0x75:0
Bus 11: zynq_1->PCA9548@0x75:1
Bus 12: zynq_1->PCA9548@0x75:2
Bus 13: zynq_1->PCA9548@0x75:3
Bus 14: zynq_1->PCA9548@0x75:4
Bus 15: zynq_1->PCA9548@0x75:5
Bus 16: zynq_1->PCA9548@0x75:6
Bus 17: zynq_1->PCA9548@0x75:7
where is exactly describing i2c bus topology.
By moving to DM case i2c mux buses are using names from DT and because
i2c-muxes describing sub busses with the same names like i2c@0, etc it
is hard to identify which bus is where.
Linux is adding topology information to i2c-mux busses to identify them
better.
This patch is doing the same and composing bus name with topology
information.
When patch is applied with topology information on zcu102-revA.
ZynqMP> i2c bus
Bus 0: i2c@ff020000
20: gpio@20, offset len 1, flags 0
21: gpio@21, offset len 1, flags 0
75: i2c-mux@75, offset len 1, flags 0
Bus 2: i2c@ff020000->i2c-mux@75->i2c@0
Bus 3: i2c@ff020000->i2c-mux@75->i2c@1
Bus 4: i2c@ff020000->i2c-mux@75->i2c@2
Bus 1: i2c@ff030000 (active 1)
74: i2c-mux@74, offset len 1, flags 0
75: i2c-mux@75, offset len 1, flags 0
Bus 5: i2c@ff030000->i2c-mux@74->i2c@0 (active 5)
54: eeprom@54, offset len 1, flags 0
Bus 6: i2c@ff030000->i2c-mux@74->i2c@1
Bus 7: i2c@ff030000->i2c-mux@74->i2c@2
Bus 8: i2c@ff030000->i2c-mux@74->i2c@3
Bus 9: i2c@ff030000->i2c-mux@74->i2c@4
Bus 10: i2c@ff030000->i2c-mux@75->i2c@0
Bus 11: i2c@ff030000->i2c-mux@75->i2c@1
Bus 12: i2c@ff030000->i2c-mux@75->i2c@2
Bus 13: i2c@ff030000->i2c-mux@75->i2c@3
Bus 14: i2c@ff030000->i2c-mux@75->i2c@4
Bus 15: i2c@ff030000->i2c-mux@75->i2c@5
Bus 16: i2c@ff030000->i2c-mux@75->i2c@6
Bus 17: i2c@ff030000->i2c-mux@75->i2c@7
Behavior before the patch is applied.
ZynqMP> i2c bus
Bus 0: i2c@ff020000
20: gpio@20, offset len 1, flags 0
21: gpio@21, offset len 1, flags 0
75: i2c-mux@75, offset len 1, flags 0
Bus 2: i2c@0
Bus 3: i2c@1
Bus 4: i2c@2
Bus 1: i2c@ff030000 (active 1)
74: i2c-mux@74, offset len 1, flags 0
75: i2c-mux@75, offset len 1, flags 0
Bus 5: i2c@0 (active 5)
54: eeprom@54, offset len 1, flags 0
Bus 6: i2c@1
Bus 7: i2c@2
Bus 8: i2c@3
Bus 9: i2c@4
Bus 10: i2c@0
Bus 11: i2c@1
Bus 12: i2c@2
Bus 13: i2c@3
Bus 14: i2c@4
Bus 15: i2c@5
Bus 16: i2c@6
Bus 17: i2c@7
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Heiko Schocher <hs@denx.de>
2019-01-31 15:31:03 +00:00
|
|
|
|
|
|
|
/* Calculate lenghts of strings */
|
|
|
|
parent_name_len = strlen(mux->parent->name);
|
|
|
|
arrow_len = strlen(arrow);
|
|
|
|
mux_name_len = strlen(mux->name);
|
|
|
|
name_len = strlen(name);
|
|
|
|
|
|
|
|
full_name = calloc(1, parent_name_len + arrow_len +
|
|
|
|
mux_name_len + arrow_len + name_len + 1);
|
|
|
|
if (!full_name)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* Compose bus name */
|
|
|
|
strcat(full_name, mux->parent->name);
|
|
|
|
strcat(full_name, arrow);
|
|
|
|
strcat(full_name, mux->name);
|
|
|
|
strcat(full_name, arrow);
|
|
|
|
strcat(full_name, name);
|
|
|
|
|
|
|
|
ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv",
|
|
|
|
full_name, node, &dev);
|
2020-12-17 04:20:15 +00:00
|
|
|
debug(" - bind ret=%d, %s, seq %d\n", ret,
|
|
|
|
dev ? dev->name : NULL, dev_seq(dev));
|
2015-08-03 14:19:21 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up the mux ready for use */
|
|
|
|
static int i2c_mux_post_probe(struct udevice *mux)
|
|
|
|
{
|
|
|
|
struct i2c_mux *priv = dev_get_uclass_priv(mux);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
debug("%s: %s\n", __func__, mux->name);
|
|
|
|
priv->selected = -1;
|
|
|
|
|
2017-01-16 17:46:06 +00:00
|
|
|
/* if parent is of i2c uclass already, we'll take that, otherwise
|
|
|
|
* look if we find an i2c-parent phandle
|
|
|
|
*/
|
|
|
|
if (UCLASS_I2C == device_get_uclass_id(mux->parent)) {
|
|
|
|
priv->i2c_bus = dev_get_parent(mux);
|
|
|
|
debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus,
|
|
|
|
priv->i2c_bus->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-03 14:19:21 +00:00
|
|
|
ret = uclass_get_device_by_phandle(UCLASS_I2C, mux, "i2c-parent",
|
|
|
|
&priv->i2c_bus);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, priv->i2c_bus->name);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int i2c_mux_select(struct udevice *dev)
|
|
|
|
{
|
2020-12-03 23:55:18 +00:00
|
|
|
struct i2c_mux_bus *plat = dev_get_parent_plat(dev);
|
2015-08-03 14:19:21 +00:00
|
|
|
struct udevice *mux = dev->parent;
|
|
|
|
struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
|
|
|
|
|
|
|
|
if (!ops->select)
|
|
|
|
return -ENOSYS;
|
|
|
|
|
|
|
|
return ops->select(mux, dev, plat->channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
int i2c_mux_deselect(struct udevice *dev)
|
|
|
|
{
|
2020-12-03 23:55:18 +00:00
|
|
|
struct i2c_mux_bus *plat = dev_get_parent_plat(dev);
|
2015-08-03 14:19:21 +00:00
|
|
|
struct udevice *mux = dev->parent;
|
|
|
|
struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
|
|
|
|
|
|
|
|
if (!ops->deselect)
|
|
|
|
return -ENOSYS;
|
|
|
|
|
|
|
|
return ops->deselect(mux, dev, plat->channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_mux_bus_set_bus_speed(struct udevice *dev, unsigned int speed)
|
|
|
|
{
|
|
|
|
struct udevice *mux = dev->parent;
|
|
|
|
struct i2c_mux *priv = dev_get_uclass_priv(mux);
|
|
|
|
int ret, ret2;
|
|
|
|
|
|
|
|
ret = i2c_mux_select(dev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = dm_i2c_set_bus_speed(priv->i2c_bus, speed);
|
|
|
|
ret2 = i2c_mux_deselect(dev);
|
|
|
|
|
|
|
|
return ret ? ret : ret2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr,
|
|
|
|
uint chip_flags)
|
|
|
|
{
|
|
|
|
struct udevice *mux = dev->parent;
|
|
|
|
struct i2c_mux *priv = dev_get_uclass_priv(mux);
|
|
|
|
struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
|
|
|
|
int ret, ret2;
|
|
|
|
|
|
|
|
debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
|
|
|
|
if (!ops->probe_chip)
|
|
|
|
return -ENOSYS;
|
|
|
|
ret = i2c_mux_select(dev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags);
|
|
|
|
ret2 = i2c_mux_deselect(dev);
|
|
|
|
|
|
|
|
return ret ? ret : ret2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg,
|
|
|
|
int nmsgs)
|
|
|
|
{
|
|
|
|
struct udevice *mux = dev->parent;
|
|
|
|
struct i2c_mux *priv = dev_get_uclass_priv(mux);
|
|
|
|
struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
|
|
|
|
int ret, ret2;
|
|
|
|
|
|
|
|
debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
|
|
|
|
if (!ops->xfer)
|
|
|
|
return -ENOSYS;
|
|
|
|
ret = i2c_mux_select(dev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ops->xfer(priv->i2c_bus, msg, nmsgs);
|
|
|
|
ret2 = i2c_mux_deselect(dev);
|
|
|
|
|
|
|
|
return ret ? ret : ret2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dm_i2c_ops i2c_mux_bus_ops = {
|
|
|
|
.xfer = i2c_mux_bus_xfer,
|
|
|
|
.probe_chip = i2c_mux_bus_probe,
|
|
|
|
.set_bus_speed = i2c_mux_bus_set_bus_speed,
|
|
|
|
};
|
|
|
|
|
|
|
|
U_BOOT_DRIVER(i2c_mux_bus) = {
|
|
|
|
.name = "i2c_mux_bus_drv",
|
|
|
|
.id = UCLASS_I2C,
|
|
|
|
.ops = &i2c_mux_bus_ops,
|
|
|
|
};
|
|
|
|
|
|
|
|
UCLASS_DRIVER(i2c_mux) = {
|
|
|
|
.id = UCLASS_I2C_MUX,
|
|
|
|
.name = "i2c_mux",
|
|
|
|
.post_bind = i2c_mux_post_bind,
|
|
|
|
.post_probe = i2c_mux_post_probe,
|
2020-12-03 23:55:17 +00:00
|
|
|
.per_device_auto = sizeof(struct i2c_mux),
|
2020-12-03 23:55:18 +00:00
|
|
|
.per_child_plat_auto = sizeof(struct i2c_mux_bus),
|
2015-08-03 14:19:21 +00:00
|
|
|
.child_post_bind = i2c_mux_child_post_bind,
|
|
|
|
};
|