From: Peter Rosin <p...@axentia.se>

All muxes have slave side adapters, many have some arbitrary number of
them. Handle this in the mux core, so that drivers are simplified.

Add i2c_mux_reserve_adapter that can be used when it is known in advance
how many child adapters that is to be added. This avoids reallocating
memory.

Drop i2c_del_mux_adapter and replace it with i2c_del_mux_adapters, since
no mux driver is dynamically deleting individual child adapters anyway.

Signed-off-by: Peter Rosin <p...@axentia.se>
---
 drivers/i2c/i2c-mux.c                      | 71 +++++++++++++++++++++++-------
 drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 10 ++---
 drivers/i2c/muxes/i2c-mux-gpio.c           | 23 ++++------
 drivers/i2c/muxes/i2c-mux-pca9541.c        | 13 +++---
 drivers/i2c/muxes/i2c-mux-pca954x.c        | 29 +++++-------
 drivers/i2c/muxes/i2c-mux-pinctrl.c        | 27 ++++--------
 drivers/i2c/muxes/i2c-mux-reg.c            | 26 ++++-------
 include/linux/i2c-mux.h                    | 15 ++++---
 8 files changed, 110 insertions(+), 104 deletions(-)

diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 00ffbdba2cf8..84169a1c9c1b 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -99,6 +99,29 @@ static unsigned int i2c_mux_parent_classes(struct 
i2c_adapter *parent)
        return class;
 }
 
+int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters)
+{
+       struct i2c_adapter **adapter;
+
+       if (adapters <= muxc->max_adapters)
+               return 0;
+
+       adapter = devm_kmalloc_array(muxc->dev,
+                                    adapters, sizeof(*adapter),
+                                    GFP_KERNEL);
+       if (!adapter)
+               return -ENOMEM;
+
+       memcpy(adapter, muxc->adapter,
+              muxc->max_adapters * sizeof(*adapter));
+
+       devm_kfree(muxc->dev, muxc->adapter);
+       muxc->adapter = adapter;
+       muxc->max_adapters = adapters;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_mux_reserve_adapters);
+
 struct i2c_mux_core *i2c_mux_alloc(struct device *dev, int sizeof_priv)
 {
        struct i2c_mux_core *muxc;
@@ -120,19 +143,29 @@ fail:
 }
 EXPORT_SYMBOL_GPL(i2c_mux_alloc);
 
-struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc,
-                                       struct device *mux_dev,
-                                       u32 force_nr, u32 chan_id,
-                                       unsigned int class)
+int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
+                       struct device *mux_dev,
+                       u32 force_nr, u32 chan_id,
+                       unsigned int class)
 {
        struct i2c_adapter *parent = muxc->parent;
        struct i2c_mux_priv *priv;
        char symlink_name[20];
        int ret;
 
+       if (muxc->adapters >= muxc->max_adapters) {
+               int new_max = 2 * muxc->max_adapters;
+
+               if (!new_max)
+                       new_max = 1;
+               ret = i2c_mux_reserve_adapters(muxc, new_max);
+               if (ret)
+                       return ret;
+       }
+
        priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
        if (!priv)
-               return NULL;
+               return -ENOMEM;
 
        /* Set up private adapter data */
        priv->muxc = muxc;
@@ -204,7 +237,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core 
*muxc,
                        "failed to add mux-adapter (error=%d)\n",
                        ret);
                kfree(priv);
-               return NULL;
+               return ret;
        }
 
        WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, 
"mux_device"),
@@ -216,23 +249,31 @@ struct i2c_adapter *i2c_add_mux_adapter(struct 
i2c_mux_core *muxc,
        dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
                 i2c_adapter_id(&priv->adap));
 
-       return &priv->adap;
+       muxc->adapter[muxc->adapters++] = &priv->adap;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
 
-void i2c_del_mux_adapter(struct i2c_adapter *adap)
+void i2c_del_mux_adapters(struct i2c_mux_core *muxc)
 {
-       struct i2c_mux_priv *priv = adap->algo_data;
        char symlink_name[20];
 
-       snprintf(symlink_name, sizeof(symlink_name), "channel-%u", 
priv->chan_id);
-       sysfs_remove_link(&priv->mux_dev->kobj, symlink_name);
+       while (muxc->adapters) {
+               struct i2c_adapter *adap = muxc->adapter[--muxc->adapters];
+               struct i2c_mux_priv *priv = adap->algo_data;
 
-       sysfs_remove_link(&priv->adap.dev.kobj, "mux_device");
-       i2c_del_adapter(adap);
-       kfree(priv);
+               muxc->adapter[muxc->adapters] = NULL;
+
+               snprintf(symlink_name, sizeof(symlink_name),
+                        "channel-%u", priv->chan_id);
+               sysfs_remove_link(&priv->mux_dev->kobj, symlink_name);
+
+               sysfs_remove_link(&priv->adap.dev.kobj, "mux_device");
+               i2c_del_adapter(adap);
+               kfree(priv);
+       }
 }
-EXPORT_SYMBOL_GPL(i2c_del_mux_adapter);
+EXPORT_SYMBOL_GPL(i2c_del_mux_adapters);
 
 MODULE_AUTHOR("Rodolfo Giometti <giome...@linux.it>");
 MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses");
diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c 
b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
index 1c4741cf290f..49aca5f26ebb 100644
--- a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
+++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
@@ -42,7 +42,6 @@
  */
 
 struct i2c_arbitrator_data {
-       struct i2c_adapter *child;
        int our_gpio;
        int our_gpio_release;
        int their_gpio;
@@ -207,10 +206,9 @@ static int i2c_arbitrator_probe(struct platform_device 
*pdev)
        }
 
        /* Actually add the mux adapter */
-       arb->child = i2c_add_mux_adapter(muxc, dev, 0, 0, 0);
-       if (!arb->child) {
+       ret = i2c_add_mux_adapter(muxc, dev, 0, 0, 0);
+       if (ret) {
                dev_err(dev, "Failed to add adapter\n");
-               ret = -ENODEV;
                i2c_put_adapter(muxc->parent);
        }
 
@@ -220,11 +218,9 @@ static int i2c_arbitrator_probe(struct platform_device 
*pdev)
 static int i2c_arbitrator_remove(struct platform_device *pdev)
 {
        struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
-       struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc);
 
-       i2c_del_mux_adapter(arb->child);
+       i2c_del_mux_adapters(muxc);
        i2c_put_adapter(muxc->parent);
-
        return 0;
 }
 
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index bd000406e160..49b8d83fbc22 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -18,7 +18,6 @@
 #include <linux/of_gpio.h>
 
 struct gpiomux {
-       struct i2c_adapter **adap; /* child busses */
        struct i2c_mux_gpio_platform_data data;
        unsigned gpio_base;
 };
@@ -184,12 +183,9 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
        muxc->select = i2c_mux_gpio_select;
        mux->gpio_base = gpio_base;
 
-       mux->adap = devm_kzalloc(&pdev->dev,
-                                sizeof(*mux->adap) * mux->data.n_values,
-                                GFP_KERNEL);
-       if (!mux->adap) {
-               dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure");
-               ret = -ENOMEM;
+       ret = i2c_mux_reserve_adapters(muxc, mux->data.n_values);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot allocate i2c_adapter structures\n");
                goto alloc_failed;
        }
 
@@ -224,10 +220,9 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
                u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
                unsigned int class = mux->data.classes ? mux->data.classes[i] : 
0;
 
-               mux->adap[i] = i2c_add_mux_adapter(muxc, &pdev->dev, nr,
-                                                  mux->data.values[i], class);
-               if (!mux->adap[i]) {
-                       ret = -ENODEV;
+               ret = i2c_add_mux_adapter(muxc, &pdev->dev, nr,
+                                         mux->data.values[i], class);
+               if (ret) {
                        dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
                        goto add_adapter_failed;
                }
@@ -239,8 +234,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
        return 0;
 
 add_adapter_failed:
-       for (; i > 0; i--)
-               i2c_del_mux_adapter(mux->adap[i - 1]);
+       i2c_del_mux_adapters(muxc);
        i = mux->data.n_gpios;
 err_request_gpio:
        for (; i > 0; i--)
@@ -257,8 +251,7 @@ static int i2c_mux_gpio_remove(struct platform_device *pdev)
        struct gpiomux *mux = i2c_mux_priv(muxc);
        int i;
 
-       for (i = 0; i < mux->data.n_values; i++)
-               i2c_del_mux_adapter(mux->adap[i]);
+       i2c_del_mux_adapters(muxc);
 
        for (i = 0; i < mux->data.n_gpios; i++)
                gpio_free(mux->gpio_base + mux->data.gpios[i]);
diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c 
b/drivers/i2c/muxes/i2c-mux-pca9541.c
index 178c22981636..791efe1d3dbc 100644
--- a/drivers/i2c/muxes/i2c-mux-pca9541.c
+++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
@@ -74,7 +74,6 @@
 
 struct pca9541 {
        struct i2c_client *client;
-       struct i2c_adapter *mux_adap;
        unsigned long select_timeout;
        unsigned long arb_timeout;
 };
@@ -332,6 +331,7 @@ static int pca9541_probe(struct i2c_client *client,
        struct i2c_mux_core *muxc;
        struct pca9541 *data;
        int force;
+       int ret;
 
        if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
@@ -364,11 +364,10 @@ static int pca9541_probe(struct i2c_client *client,
        force = 0;
        if (pdata)
                force = pdata->modes[0].adap_id;
-       data->mux_adap = i2c_add_mux_adapter(muxc, &client->dev, force, 0, 0);
-
-       if (data->mux_adap == NULL) {
+       ret = i2c_add_mux_adapter(muxc, &client->dev, force, 0, 0);
+       if (ret) {
                dev_err(&client->dev, "failed to register master selector\n");
-               return -ENODEV;
+               return ret;
        }
 
        dev_info(&client->dev, "registered master selector for I2C %s\n",
@@ -380,10 +379,8 @@ static int pca9541_probe(struct i2c_client *client,
 static int pca9541_remove(struct i2c_client *client)
 {
        struct i2c_mux_core *muxc = i2c_get_clientdata(client);
-       struct pca9541 *data = i2c_mux_priv(muxc);
-
-       i2c_del_mux_adapter(data->mux_adap);
 
+       i2c_del_mux_adapters(muxc);
        return 0;
 }
 
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c 
b/drivers/i2c/muxes/i2c-mux-pca954x.c
index edc6693ffea9..e3219ba9307c 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -60,7 +60,6 @@ enum pca_type {
 
 struct pca954x {
        enum pca_type type;
-       struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
 
        u8 last_chan;           /* last register value */
        u8 deselect;
@@ -234,6 +233,13 @@ static int pca954x_probe(struct i2c_client *client,
        data->type = id->driver_data;
        data->last_chan = 0;               /* force the first selection */
 
+       ret = i2c_mux_reserve_adapters(muxc, chips[data->type].nchans);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Cannot allocate i2c_adapter structures\n");
+               return ret;
+       }
+
        idle_disconnect_dt = of_node &&
                of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
 
@@ -256,12 +262,10 @@ static int pca954x_probe(struct i2c_client *client,
                                           || idle_disconnect_dt) << num;
                }
 
-               data->virt_adaps[num] =
-                       i2c_add_mux_adapter(muxc, &client->dev,
-                                           force, num, class);
+               ret = i2c_add_mux_adapter(muxc, &client->dev,
+                                         force, num, class);
 
-               if (data->virt_adaps[num] == NULL) {
-                       ret = -ENODEV;
+               if (ret) {
                        dev_err(&client->dev,
                                "failed to register multiplexed adapter"
                                " %d as bus %d\n", num, force);
@@ -277,24 +281,15 @@ static int pca954x_probe(struct i2c_client *client,
        return 0;
 
 virt_reg_failed:
-       for (num--; num >= 0; num--)
-               i2c_del_mux_adapter(data->virt_adaps[num]);
+       i2c_del_mux_adapters(muxc);
        return ret;
 }
 
 static int pca954x_remove(struct i2c_client *client)
 {
        struct i2c_mux_core *muxc = i2c_get_clientdata(client);
-       struct pca954x *data = i2c_mux_priv(muxc);
-       const struct chip_desc *chip = &chips[data->type];
-       int i;
-
-       for (i = 0; i < chip->nchans; ++i)
-               if (data->virt_adaps[i]) {
-                       i2c_del_mux_adapter(data->virt_adaps[i]);
-                       data->virt_adaps[i] = NULL;
-               }
 
+       i2c_del_mux_adapters(muxc);
        return 0;
 }
 
diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c 
b/drivers/i2c/muxes/i2c-mux-pinctrl.c
index 79bd1ea75444..23792a1b2b3c 100644
--- a/drivers/i2c/muxes/i2c-mux-pinctrl.c
+++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c
@@ -31,7 +31,6 @@ struct i2c_mux_pinctrl {
        struct pinctrl *pinctrl;
        struct pinctrl_state **states;
        struct pinctrl_state *state_idle;
-       struct i2c_adapter **busses;
 };
 
 static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan)
@@ -164,12 +163,9 @@ static int i2c_mux_pinctrl_probe(struct platform_device 
*pdev)
                goto err;
        }
 
-       mux->busses = devm_kzalloc(&pdev->dev,
-                                  sizeof(*mux->busses) * mux->pdata->bus_count,
-                                  GFP_KERNEL);
-       if (!mux->busses) {
-               dev_err(&pdev->dev, "Cannot allocate busses\n");
-               ret = -ENOMEM;
+       ret = i2c_mux_reserve_adapters(muxc, mux->pdata->bus_count);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot reserve adapters\n");
                goto err;
        }
 
@@ -219,10 +215,9 @@ static int i2c_mux_pinctrl_probe(struct platform_device 
*pdev)
                u32 bus = mux->pdata->base_bus_num ?
                                (mux->pdata->base_bus_num + i) : 0;
 
-               mux->busses[i] = i2c_add_mux_adapter(muxc, &pdev->dev,
-                                                    bus, i, 0);
-               if (!mux->busses[i]) {
-                       ret = -ENODEV;
+               ret = i2c_add_mux_adapter(muxc, &pdev->dev,
+                                         bus, i, 0);
+               if (ret) {
                        dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
                        goto err_del_adapter;
                }
@@ -231,8 +226,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device 
*pdev)
        return 0;
 
 err_del_adapter:
-       for (; i > 0; i--)
-               i2c_del_mux_adapter(mux->busses[i - 1]);
+       i2c_del_mux_adapters(muxc);
        i2c_put_adapter(muxc->parent);
 err:
        return ret;
@@ -241,14 +235,9 @@ err:
 static int i2c_mux_pinctrl_remove(struct platform_device *pdev)
 {
        struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
-       struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
-       int i;
-
-       for (i = 0; i < mux->pdata->bus_count; i++)
-               i2c_del_mux_adapter(mux->busses[i]);
 
+       i2c_del_mux_adapters(muxc);
        i2c_put_adapter(muxc->parent);
-
        return 0;
 }
 
diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c
index d85879c46d90..73de562b7731 100644
--- a/drivers/i2c/muxes/i2c-mux-reg.c
+++ b/drivers/i2c/muxes/i2c-mux-reg.c
@@ -21,7 +21,6 @@
 #include <linux/slab.h>
 
 struct regmux {
-       struct i2c_adapter **adap; /* child busses */
        struct i2c_mux_reg_platform_data data;
 };
 
@@ -216,11 +215,9 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       mux->adap = devm_kzalloc(&pdev->dev,
-                                sizeof(*mux->adap) * mux->data.n_values,
-                                GFP_KERNEL);
-       if (!mux->adap) {
-               dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure");
+       ret = i2c_mux_reserve_adapters(muxc, mux->data.n_values);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot allocate i2c_adapter structures\n");
                return -ENOMEM;
        }
 
@@ -234,11 +231,9 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
                nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
                class = mux->data.classes ? mux->data.classes[i] : 0;
 
-               mux->adap[i] = i2c_add_mux_adapter(muxc, &pdev->dev,
-                                                  nr, mux->data.values[i],
-                                                  class);
-               if (!mux->adap[i]) {
-                       ret = -ENODEV;
+               ret = i2c_add_mux_adapter(muxc, &pdev->dev, nr,
+                                         mux->data.values[i], class);
+               if (ret) {
                        dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
                        goto add_adapter_failed;
                }
@@ -250,8 +245,7 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
        return 0;
 
 add_adapter_failed:
-       for (; i > 0; i--)
-               i2c_del_mux_adapter(mux->adap[i - 1]);
+       i2c_del_mux_adapters(muxc);
 
        return ret;
 }
@@ -259,12 +253,8 @@ add_adapter_failed:
 static int i2c_mux_reg_remove(struct platform_device *pdev)
 {
        struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
-       struct regmux *mux = i2c_mux_priv(muxc);
-       int i;
-
-       for (i = 0; i < mux->data.n_values; i++)
-               i2c_del_mux_adapter(mux->adap[i]);
 
+       i2c_del_mux_adapters(muxc);
        i2c_put_adapter(muxc->parent);
 
        return 0;
diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h
index 5cd6e1e664e0..bfcdcc46f2a6 100644
--- a/include/linux/i2c-mux.h
+++ b/include/linux/i2c-mux.h
@@ -29,6 +29,9 @@
 
 struct i2c_mux_core {
        struct i2c_adapter *parent;
+       struct i2c_adapter **adapter;
+       int adapters;
+       int max_adapters;
        struct device *dev;
 
        void *priv;
@@ -44,18 +47,20 @@ static inline void *i2c_mux_priv(struct i2c_mux_core *muxc)
        return muxc->priv;
 }
 
+int i2c_mux_reserve_adapters(struct i2c_mux_core *muxc, int adapters);
+
 /*
  * Called to create a i2c bus on a multiplexed bus segment.
  * The mux_dev and chan_id parameters are passed to the select
  * and deselect callback functions to perform hardware-specific
  * mux control.
  */
-struct i2c_adapter *i2c_add_mux_adapter(struct i2c_mux_core *muxc,
-                                       struct device *mux_dev,
-                                       u32 force_nr, u32 chan_id,
-                                       unsigned int class);
+int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
+                       struct device *mux_dev,
+                       u32 force_nr, u32 chan_id,
+                       unsigned int class);
 
-void i2c_del_mux_adapter(struct i2c_adapter *adap);
+void i2c_del_mux_adapters(struct i2c_mux_core *muxc);
 
 #endif /* __KERNEL__ */
 
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to