diff mbox

[03/10] i2c-mux: move the slave side adapter management to i2c_mux_core

Message ID 1451920215-29167-4-git-send-email-peda@lysator.liu.se
State Superseded
Headers show

Commit Message

Peter Rosin Jan. 4, 2016, 3:10 p.m. UTC
From: Peter Rosin <peda@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 <peda@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(-)

Comments

kernel test robot Jan. 4, 2016, 4:02 p.m. UTC | #1
Hi Peter,

[auto build test ERROR on wsa/i2c/for-next]
[also build test ERROR on v4.4-rc8 next-20160104]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Peter-Rosin/i2c-mux-cleanup-and-locking-update/20160104-231355
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux i2c/for-next
config: x86_64-randconfig-i0-201601 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   drivers/media/dvb-frontends/m88ds3103.c: In function 'm88ds3103_probe':
   drivers/media/dvb-frontends/m88ds3103.c:1470:41: warning: passing argument 1 of 'i2c_add_mux_adapter' from incompatible pointer type [-Wincompatible-pointer-types]
     dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
                                            ^
   In file included from drivers/media/dvb-frontends/m88ds3103_priv.h:24:0,
                    from drivers/media/dvb-frontends/m88ds3103.c:17:
   include/linux/i2c-mux.h:58:5: note: expected 'struct i2c_mux_core *' but argument is of type 'struct i2c_adapter *'
    int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
        ^
   drivers/media/dvb-frontends/m88ds3103.c:1471:13: warning: passing argument 3 of 'i2c_add_mux_adapter' makes integer from pointer without a cast [-Wint-conversion]
                dev, 0, 0, 0, m88ds3103_select,
                ^
   In file included from drivers/media/dvb-frontends/m88ds3103_priv.h:24:0,
                    from drivers/media/dvb-frontends/m88ds3103.c:17:
   include/linux/i2c-mux.h:58:5: note: expected 'u32 {aka unsigned int}' but argument is of type 'struct m88ds3103_dev *'
    int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
        ^
   drivers/media/dvb-frontends/m88ds3103.c:1470:21: error: too many arguments to function 'i2c_add_mux_adapter'
     dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
                        ^
   In file included from drivers/media/dvb-frontends/m88ds3103_priv.h:24:0,
                    from drivers/media/dvb-frontends/m88ds3103.c:17:
   include/linux/i2c-mux.h:58:5: note: declared here
    int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
        ^
>> drivers/media/dvb-frontends/m88ds3103.c:1470:19: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
     dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
                      ^
   drivers/media/dvb-frontends/m88ds3103.c: In function 'm88ds3103_remove':
>> drivers/media/dvb-frontends/m88ds3103.c:1505:2: error: implicit declaration of function 'i2c_del_mux_adapter' [-Werror=implicit-function-declaration]
     i2c_del_mux_adapter(dev->i2c_adapter);
     ^
   cc1: some warnings being treated as errors
--
   drivers/media/dvb-frontends/rtl2830.c: In function 'rtl2830_probe':
   drivers/media/dvb-frontends/rtl2830.c:868:37: warning: passing argument 1 of 'i2c_add_mux_adapter' from incompatible pointer type [-Wincompatible-pointer-types]
     dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
                                        ^
   In file included from drivers/media/dvb-frontends/rtl2830_priv.h:24:0,
                    from drivers/media/dvb-frontends/rtl2830.c:18:
   include/linux/i2c-mux.h:58:5: note: expected 'struct i2c_mux_core *' but argument is of type 'struct i2c_adapter *'
    int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
        ^
   drivers/media/dvb-frontends/rtl2830.c:869:4: warning: passing argument 3 of 'i2c_add_mux_adapter' makes integer from pointer without a cast [-Wint-conversion]
       client, 0, 0, 0, rtl2830_select, NULL);
       ^
   In file included from drivers/media/dvb-frontends/rtl2830_priv.h:24:0,
                    from drivers/media/dvb-frontends/rtl2830.c:18:
   include/linux/i2c-mux.h:58:5: note: expected 'u32 {aka unsigned int}' but argument is of type 'struct i2c_client *'
    int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
        ^
   drivers/media/dvb-frontends/rtl2830.c:868:17: error: too many arguments to function 'i2c_add_mux_adapter'
     dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
                    ^
   In file included from drivers/media/dvb-frontends/rtl2830_priv.h:24:0,
                    from drivers/media/dvb-frontends/rtl2830.c:18:
   include/linux/i2c-mux.h:58:5: note: declared here
    int i2c_add_mux_adapter(struct i2c_mux_core *muxc,
        ^
>> drivers/media/dvb-frontends/rtl2830.c:868:15: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
     dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
                  ^
   drivers/media/dvb-frontends/rtl2830.c: In function 'rtl2830_remove':
>> drivers/media/dvb-frontends/rtl2830.c:903:2: error: implicit declaration of function 'i2c_del_mux_adapter' [-Werror=implicit-function-declaration]
     i2c_del_mux_adapter(dev->adapter);
     ^
   cc1: some warnings being treated as errors

vim +/i2c_del_mux_adapter +1505 drivers/media/dvb-frontends/m88ds3103.c

f01919e8f Antti Palosaari 2015-04-16  1464  		goto err_kfree;
56ea37da3 Antti Palosaari 2015-10-03  1465  	ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10);
395d00d1c Antti Palosaari 2013-02-25  1466  	if (ret)
f01919e8f Antti Palosaari 2015-04-16  1467  		goto err_kfree;
395d00d1c Antti Palosaari 2013-02-25  1468  
44b9055b4 Antti Palosaari 2013-11-19  1469  	/* create mux i2c adapter for tuner */
f01919e8f Antti Palosaari 2015-04-16 @1470  	dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
f01919e8f Antti Palosaari 2015-04-16  1471  					       dev, 0, 0, 0, m88ds3103_select,
478932b16 Antti Palosaari 2015-04-16  1472  					       NULL);
4347df6a7 Dan Carpenter   2015-06-02  1473  	if (dev->i2c_adapter == NULL) {
4347df6a7 Dan Carpenter   2015-06-02  1474  		ret = -ENOMEM;
f01919e8f Antti Palosaari 2015-04-16  1475  		goto err_kfree;
4347df6a7 Dan Carpenter   2015-06-02  1476  	}
44b9055b4 Antti Palosaari 2013-11-19  1477  
395d00d1c Antti Palosaari 2013-02-25  1478  	/* create dvb_frontend */
f01919e8f Antti Palosaari 2015-04-16  1479  	memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
f01919e8f Antti Palosaari 2015-04-16  1480  	if (dev->chip_id == M88RS6000_CHIP_ID)
7978b8a1b Antti Palosaari 2015-04-16  1481  		strncpy(dev->fe.ops.info.name, "Montage Technology M88RS6000",
7978b8a1b Antti Palosaari 2015-04-16  1482  			sizeof(dev->fe.ops.info.name));
f01919e8f Antti Palosaari 2015-04-16  1483  	if (!pdata->attach_in_use)
f01919e8f Antti Palosaari 2015-04-16  1484  		dev->fe.ops.release = NULL;
f01919e8f Antti Palosaari 2015-04-16  1485  	dev->fe.demodulator_priv = dev;
f01919e8f Antti Palosaari 2015-04-16  1486  	i2c_set_clientdata(client, dev);
f01919e8f Antti Palosaari 2015-04-16  1487  
f01919e8f Antti Palosaari 2015-04-16  1488  	/* setup callbacks */
f01919e8f Antti Palosaari 2015-04-16  1489  	pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend;
f01919e8f Antti Palosaari 2015-04-16  1490  	pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter;
f01919e8f Antti Palosaari 2015-04-16  1491  	return 0;
f01919e8f Antti Palosaari 2015-04-16  1492  err_kfree:
f01919e8f Antti Palosaari 2015-04-16  1493  	kfree(dev);
395d00d1c Antti Palosaari 2013-02-25  1494  err:
f01919e8f Antti Palosaari 2015-04-16  1495  	dev_dbg(&client->dev, "failed=%d\n", ret);
f01919e8f Antti Palosaari 2015-04-16  1496  	return ret;
395d00d1c Antti Palosaari 2013-02-25  1497  }
395d00d1c Antti Palosaari 2013-02-25  1498  
f01919e8f Antti Palosaari 2015-04-16  1499  static int m88ds3103_remove(struct i2c_client *client)
f01919e8f Antti Palosaari 2015-04-16  1500  {
7978b8a1b Antti Palosaari 2015-04-16  1501  	struct m88ds3103_dev *dev = i2c_get_clientdata(client);
395d00d1c Antti Palosaari 2013-02-25  1502  
f01919e8f Antti Palosaari 2015-04-16  1503  	dev_dbg(&client->dev, "\n");
395d00d1c Antti Palosaari 2013-02-25  1504  
f01919e8f Antti Palosaari 2015-04-16 @1505  	i2c_del_mux_adapter(dev->i2c_adapter);
395d00d1c Antti Palosaari 2013-02-25  1506  
f01919e8f Antti Palosaari 2015-04-16  1507  	kfree(dev);
f01919e8f Antti Palosaari 2015-04-16  1508  	return 0;

:::::: The code at line 1505 was first introduced by commit
:::::: f01919e8f54f645fb00fdb823fe266e21eebe3b1 [media] m88ds3103: add I2C client binding

:::::: TO: Antti Palosaari <crope@iki.fi>
:::::: CC: Mauro Carvalho Chehab <mchehab@osg.samsung.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

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 <giometti@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__ */