diff options
Diffstat (limited to 'drivers/i2c/i2c-mux.c')
| -rw-r--r-- | drivers/i2c/i2c-mux.c | 72 |
1 files changed, 56 insertions, 16 deletions
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index d7a4833be41..2d0847b6be6 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> +#include <linux/of.h> /* multiplexer per channel data */ struct i2c_mux_priv { @@ -31,11 +32,11 @@ struct i2c_mux_priv { struct i2c_algorithm algo; struct i2c_adapter *parent; - void *mux_dev; /* the mux chip/device */ + void *mux_priv; /* the mux chip/device */ u32 chan_id; /* the channel id */ - int (*select)(struct i2c_adapter *, void *mux_dev, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_dev, u32 chan_id); + int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); + int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); }; static int i2c_mux_master_xfer(struct i2c_adapter *adap, @@ -47,11 +48,11 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, /* Switch to the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_dev, priv->chan_id); + ret = priv->select(parent, priv->mux_priv, priv->chan_id); if (ret >= 0) ret = parent->algo->master_xfer(parent, msgs, num); if (priv->deselect) - priv->deselect(parent, priv->mux_dev, priv->chan_id); + priv->deselect(parent, priv->mux_priv, priv->chan_id); return ret; } @@ -67,12 +68,12 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, /* Select the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_dev, priv->chan_id); + ret = priv->select(parent, priv->mux_priv, priv->chan_id); if (ret >= 0) ret = parent->algo->smbus_xfer(parent, addr, flags, read_write, command, size, data); if (priv->deselect) - priv->deselect(parent, priv->mux_dev, priv->chan_id); + priv->deselect(parent, priv->mux_priv, priv->chan_id); return ret; } @@ -86,8 +87,23 @@ static u32 i2c_mux_functionality(struct i2c_adapter *adap) return parent->algo->functionality(parent); } +/* Return all parent classes, merged */ +static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) +{ + unsigned int class = 0; + + do { + class |= parent->class; + parent = i2c_parent_is_i2c_adapter(parent); + } while (parent); + + return class; +} + struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - void *mux_dev, u32 force_nr, u32 chan_id, + struct device *mux_dev, + void *mux_priv, u32 force_nr, u32 chan_id, + unsigned int class, int (*select) (struct i2c_adapter *, void *, u32), int (*deselect) (struct i2c_adapter *, @@ -102,7 +118,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, /* Set up private adapter data */ priv->parent = parent; - priv->mux_dev = mux_dev; + priv->mux_priv = mux_priv; priv->chan_id = chan_id; priv->select = select; priv->deselect = deselect; @@ -123,6 +139,35 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, priv->adap.algo = &priv->algo; priv->adap.algo_data = priv; priv->adap.dev.parent = &parent->dev; + priv->adap.retries = parent->retries; + priv->adap.timeout = parent->timeout; + + /* Sanity check on class */ + if (i2c_mux_parent_classes(parent) & class) + dev_err(&parent->dev, + "Segment %d behind mux can't share classes with ancestors\n", + chan_id); + else + priv->adap.class = class; + + /* + * Try to populate the mux adapter's of_node, expands to + * nothing if !CONFIG_OF. + */ + if (mux_dev->of_node) { + struct device_node *child; + u32 reg; + + for_each_child_of_node(mux_dev->of_node, child) { + ret = of_property_read_u32(child, "reg", ®); + if (ret) + continue; + if (chan_id == reg) { + priv->adap.dev.of_node = child; + break; + } + } + } if (force_nr) { priv->adap.nr = force_nr; @@ -145,17 +190,12 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, } EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); -int i2c_del_mux_adapter(struct i2c_adapter *adap) +void i2c_del_mux_adapter(struct i2c_adapter *adap) { struct i2c_mux_priv *priv = adap->algo_data; - int ret; - ret = i2c_del_adapter(adap); - if (ret < 0) - return ret; + i2c_del_adapter(adap); kfree(priv); - - return 0; } EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); |
