@@ -28,6 +28,8 @@ Required properties:
Optional properties:
- idle-state: value to set the muxer to when idle. When no value is
given, it defaults to the last value used.
+- i2c-controlled: The muxed I2C bus is also used to control all the gpios
+ used for muxing.
For each i2c child node, an I2C child bus will be created. They will
be numbered based on their order in the device tree.
@@ -23,6 +23,10 @@ Required properties:
- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side
port is connected to.
+Optional properties:
+- i2c-controlled: The muxed I2C bus is also used to control all the pinctrl
+ pins used for muxing.
+
Also required are:
* Standard pinctrl properties that specify the pin mux state for each child
@@ -54,6 +54,25 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap,
return ret;
}
+static int __i2c_mux_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
+ int ret;
+
+ /* Switch to the right mux port and perform the transfer. */
+
+ ret = muxc->select(muxc, priv->chan_id);
+ if (ret >= 0)
+ ret = i2c_transfer(parent, msgs, num);
+ if (muxc->deselect)
+ muxc->deselect(muxc, priv->chan_id);
+
+ return ret;
+}
+
static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
u16 addr, unsigned short flags,
char read_write, u8 command,
@@ -76,6 +95,28 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
return ret;
}
+static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap,
+ u16 addr, unsigned short flags,
+ char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
+ int ret;
+
+ /* Select the right mux port and perform the transfer. */
+
+ ret = muxc->select(muxc, priv->chan_id);
+ if (ret >= 0)
+ ret = i2c_smbus_xfer(parent, addr, flags,
+ read_write, command, size, data);
+ if (muxc->deselect)
+ muxc->deselect(muxc, priv->chan_id);
+
+ return ret;
+}
+
/* Return the parent's functionality */
static u32 i2c_mux_functionality(struct i2c_adapter *adap)
{
@@ -98,6 +139,45 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent)
return class;
}
+static void i2c_mux_lock_bus(struct i2c_adapter *adapter, int flags)
+{
+ struct i2c_mux_priv *priv = adapter->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
+
+ rt_mutex_lock(&muxc->bus_lock);
+ if (!(flags & I2C_LOCK_ADAPTER))
+ return;
+ i2c_lock_bus(parent, flags);
+}
+
+static int i2c_mux_trylock_bus(struct i2c_adapter *adapter, int flags)
+{
+ struct i2c_mux_priv *priv = adapter->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
+
+ if (!rt_mutex_trylock(&muxc->bus_lock))
+ return 0;
+ if (!(flags & I2C_LOCK_ADAPTER))
+ return 1;
+ if (parent->trylock_bus(parent, flags))
+ return 1;
+ rt_mutex_unlock(&muxc->bus_lock);
+ return 0;
+}
+
+static void i2c_mux_unlock_bus(struct i2c_adapter *adapter, int flags)
+{
+ struct i2c_mux_priv *priv = adapter->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
+
+ if (flags & I2C_LOCK_ADAPTER)
+ i2c_unlock_bus(parent, flags);
+ rt_mutex_unlock(&muxc->bus_lock);
+}
+
static void i2c_parent_lock_bus(struct i2c_adapter *adapter, int flags)
{
struct i2c_mux_priv *priv = adapter->algo_data;
@@ -155,6 +235,7 @@ struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv)
if (sizeof_priv)
muxc->priv = muxc + 1;
muxc->dev = dev;
+ rt_mutex_init(&muxc->bus_lock);
return muxc;
}
EXPORT_SYMBOL_GPL(i2c_mux_alloc);
@@ -189,10 +270,18 @@ int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
/* Need to do algo dynamically because we don't know ahead
* of time what sort of physical adapter we'll be dealing with.
*/
- if (parent->algo->master_xfer)
- priv->algo.master_xfer = i2c_mux_master_xfer;
- if (parent->algo->smbus_xfer)
- priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
+ if (parent->algo->master_xfer) {
+ if (muxc->i2c_controlled)
+ priv->algo.master_xfer = __i2c_mux_master_xfer;
+ else
+ priv->algo.master_xfer = i2c_mux_master_xfer;
+ }
+ if (parent->algo->smbus_xfer) {
+ if (muxc->i2c_controlled)
+ priv->algo.smbus_xfer = __i2c_mux_smbus_xfer;
+ else
+ priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
+ }
priv->algo.functionality = i2c_mux_functionality;
/* Now fill out new adapter structure */
@@ -205,9 +294,15 @@ int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
priv->adap.retries = parent->retries;
priv->adap.timeout = parent->timeout;
priv->adap.quirks = parent->quirks;
- priv->adap.lock_bus = i2c_parent_lock_bus;
- priv->adap.trylock_bus = i2c_parent_trylock_bus;
- priv->adap.unlock_bus = i2c_parent_unlock_bus;
+ if (muxc->i2c_controlled) {
+ priv->adap.lock_bus = i2c_mux_lock_bus;
+ priv->adap.trylock_bus = i2c_mux_trylock_bus;
+ priv->adap.unlock_bus = i2c_mux_unlock_bus;
+ } else {
+ priv->adap.lock_bus = i2c_parent_lock_bus;
+ priv->adap.trylock_bus = i2c_parent_trylock_bus;
+ priv->adap.unlock_bus = i2c_parent_unlock_bus;
+ }
/* Sanity check on class */
if (i2c_mux_parent_classes(parent) & class)
@@ -68,6 +68,8 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
if (!np)
return -ENODEV;
+ mux->data.i2c_controlled = of_property_read_bool(np,
+ "i2c-controlled");
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
if (!adapter_np) {
dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
@@ -177,6 +179,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
if (!parent)
return -EPROBE_DEFER;
+ muxc->i2c_controlled = mux->data.i2c_controlled;
muxc->parent = parent;
muxc->select = i2c_mux_gpio_select;
mux->gpio_base = gpio_base;
@@ -96,6 +96,8 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
}
}
+ mux->pdata->i2c_controlled = of_property_read_bool(np,
+ "i2c-controlled");
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
if (!adapter_np) {
dev_err(muxc->dev, "Cannot parse i2c-parent\n");
@@ -193,6 +195,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
}
muxc->select = i2c_mux_pinctrl_select;
+ muxc->i2c_controlled = mux->pdata->i2c_controlled;
muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num);
if (!muxc->parent) {
dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
@@ -27,6 +27,7 @@
* @gpios: Array of GPIO numbers used to control MUX
* @n_gpios: Number of GPIOs used to control MUX
* @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used
+ * @i2c_controlled: Set if the parent i2c bus is used to control the gpio.
*/
struct i2c_mux_gpio_platform_data {
int parent;
@@ -38,6 +39,7 @@ struct i2c_mux_gpio_platform_data {
const unsigned *gpios;
int n_gpios;
unsigned idle;
+ bool i2c_controlled;
};
#endif /* _LINUX_I2C_MUX_GPIO_H */
@@ -29,6 +29,7 @@
* @pinctrl_state_idle: The pinctrl state to select when no child bus is being
* accessed. If NULL, the most recently used pinctrl state will be left
* selected.
+ * @i2c_controlled: Set if the parent i2c bus is used to control the pinctrl.
*/
struct i2c_mux_pinctrl_platform_data {
int parent_bus_num;
@@ -36,6 +37,7 @@ struct i2c_mux_pinctrl_platform_data {
int bus_count;
const char **pinctrl_states;
const char *pinctrl_state_idle;
+ bool i2c_controlled;
};
#endif
@@ -33,6 +33,8 @@ struct i2c_mux_core {
int adapters;
int max_adapters;
struct device *dev;
+ struct rt_mutex bus_lock;
+ bool i2c_controlled;
void *priv;