mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-25 04:23:46 +00:00
c4bd12a7da
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>
224 lines
5.3 KiB
C
224 lines
5.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2015 Google, Inc
|
|
* Written by Simon Glass <sjg@chromium.org>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <errno.h>
|
|
#include <i2c.h>
|
|
#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)
|
|
{
|
|
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
|
|
int channel;
|
|
|
|
channel = dev_read_u32_default(dev, "reg", -1);
|
|
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)
|
|
{
|
|
ofnode node;
|
|
int ret;
|
|
|
|
debug("%s: %s\n", __func__, mux->name);
|
|
/*
|
|
* There is no compatible string in the sub-nodes, so we must manually
|
|
* bind these
|
|
*/
|
|
dev_for_each_subnode(node, mux) {
|
|
struct udevice *dev;
|
|
const char *name;
|
|
const char *arrow = "->";
|
|
char *full_name;
|
|
int parent_name_len, arrow_len, mux_name_len, name_len;
|
|
|
|
name = ofnode_get_name(node);
|
|
|
|
/* 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);
|
|
debug(" - bind ret=%d, %s, req_seq %d\n", ret,
|
|
dev ? dev->name : NULL, dev->req_seq);
|
|
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;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
|
|
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)
|
|
{
|
|
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
|
|
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,
|
|
.per_device_auto_alloc_size = sizeof(struct i2c_mux),
|
|
.per_child_platdata_auto_alloc_size = sizeof(struct i2c_mux_bus),
|
|
.child_post_bind = i2c_mux_child_post_bind,
|
|
};
|