@@ -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;
@@ -122,6 +202,31 @@ static void i2c_parent_unlock_bus(struct i2c_adapter *adapter, int flags)
parent->unlock_bus(parent, flags);
}
+struct i2c_adapter *i2c_root_adapter(struct device *dev)
+{
+ struct device *i2c;
+ struct i2c_adapter *i2c_root;
+
+ /*
+ * Walk up the device tree to find an i2c adapter, indicating
+ * that this is an i2c client device. Check all ancestors to
+ * handle mfd devices etc.
+ */
+ for (i2c = dev; i2c; i2c = i2c->parent) {
+ if (i2c->type == &i2c_adapter_type)
+ break;
+ }
+ if (!i2c)
+ return NULL;
+
+ /* Continue up the tree to find the root i2c adapter */
+ i2c_root = to_i2c_adapter(i2c);
+ while (i2c_parent_is_i2c_adapter(i2c_root))
+ i2c_root = i2c_parent_is_i2c_adapter(i2c_root);
+
+ return i2c_root;
+}
+
int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters)
{
struct i2c_adapter **adapter;
@@ -163,9 +268,12 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
muxc->parent = parent;
muxc->dev = dev;
+ if (flags & I2C_CONTROLLED_MUX)
+ muxc->i2c_controlled = 1;
muxc->select = select;
muxc->deselect = deselect;
+ rt_mutex_init(&muxc->bus_lock);
return muxc;
}
EXPORT_SYMBOL_GPL(i2c_mux_alloc);
@@ -200,10 +308,18 @@ int i2c_mux_add_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 */
@@ -216,9 +332,15 @@ int i2c_mux_add_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)
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/gpio.h>
+#include "../../gpio/gpiolib.h"
#include <linux/of_gpio.h>
struct gpiomux {
@@ -137,6 +138,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
struct i2c_mux_core *muxc;
struct gpiomux *mux;
struct i2c_adapter *parent;
+ struct i2c_adapter *root;
unsigned initial_state, gpio_base;
int i, ret;
@@ -178,6 +180,9 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
if (!parent)
return -EPROBE_DEFER;
+ root = i2c_root_adapter(&parent->dev);
+
+ muxc->i2c_controlled = true;
muxc->parent = parent;
mux->gpio_base = gpio_base;
@@ -193,6 +198,9 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
}
for (i = 0; i < mux->data.n_gpios; i++) {
+ struct device *gpio_dev;
+ struct gpio_desc *gpio_desc;
+
ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio");
if (ret) {
dev_err(&pdev->dev, "Failed to request GPIO %d\n",
@@ -209,8 +217,19 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
i++; /* gpio_request above succeeded, so must free */
goto err_request_gpio;
}
+
+ if (!muxc->i2c_controlled)
+ continue;
+
+ gpio_desc = gpio_to_desc(gpio_base + mux->data.gpios[i]);
+ gpio_dev = gpio_desc->chip->parent;
+ muxc->i2c_controlled = i2c_root_adapter(gpio_dev) == root;
}
+ if (muxc->i2c_controlled)
+ dev_info(&pdev->dev,
+ "lock select-transfer-deselect individually\n");
+
for (i = 0; i < mux->data.n_values; i++) {
u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
unsigned int class = mux->data.classes ? mux->data.classes[i] : 0;
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include "../../pinctrl/core.h"
struct i2c_mux_pinctrl {
struct i2c_mux_pinctrl_platform_data *pdata;
@@ -120,10 +121,31 @@ static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
}
#endif
+static struct i2c_adapter *i2c_mux_pinctrl_root_adapter(
+ struct pinctrl_state *state)
+{
+ struct i2c_adapter *root = NULL;
+ struct pinctrl_setting *setting;
+ struct i2c_adapter *pin_root;
+
+ list_for_each_entry(setting, &state->settings, node) {
+ pin_root = i2c_root_adapter(setting->pctldev->dev);
+ if (!pin_root)
+ return NULL;
+ if (!root)
+ root = pin_root;
+ else if (root != pin_root)
+ return NULL;
+ }
+
+ return root;
+}
+
static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
{
struct i2c_mux_core *muxc;
struct i2c_mux_pinctrl *mux;
+ struct i2c_adapter *root;
int i, ret;
muxc = i2c_mux_alloc(NULL, &pdev->dev, sizeof(*mux), 0,
@@ -199,6 +221,23 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
goto err;
}
+ root = i2c_root_adapter(&muxc->parent->dev);
+
+ muxc->i2c_controlled = true;
+ for (i = 0; i < mux->pdata->bus_count; i++) {
+ if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) {
+ muxc->i2c_controlled = false;
+ break;
+ }
+ }
+ if (muxc->i2c_controlled && mux->pdata->pinctrl_state_idle
+ && root != i2c_mux_pinctrl_root_adapter(mux->state_idle))
+ muxc->i2c_controlled = false;
+
+ if (muxc->i2c_controlled)
+ dev_info(&pdev->dev,
+ "lock select-transfer-deselect individually\n");
+
for (i = 0; i < mux->pdata->bus_count; i++) {
u32 bus = mux->pdata->base_bus_num ?
(mux->pdata->base_bus_num + i) : 0;
@@ -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;
@@ -46,11 +48,16 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
int (*select)(struct i2c_mux_core *, u32),
int (*deselect)(struct i2c_mux_core *, u32));
+/* flags for i2c_mux_alloc */
+#define I2C_CONTROLLED_MUX (1<<0)
+
static inline void *i2c_mux_priv(struct i2c_mux_core *muxc)
{
return muxc->priv;
}
+struct i2c_adapter *i2c_root_adapter(struct device *dev);
+
int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters);
/*