linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] i2c-mux-pca954x: Disable mux after 200ms timeout
@ 2013-11-26  6:32 Mike Looijmans
       [not found] ` <1385447520-3306-1-git-send-email-mike.looijmans-Oq418RWZeHk@public.gmane.org>
  0 siblings, 1 reply; 11+ messages in thread
From: Mike Looijmans @ 2013-11-26  6:32 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mike Looijmans

Leaving the mux enabled causes needless I2C traffic on the downstream
bus. De-selecting after every request causes excess I2C traffic and
switching.

This patch implements a hybrid solution: After 200ms of inactivity,
the mux is disabled.

Signed-off-by: Mike Looijmans <mike.looijmans-Oq418RWZeHk@public.gmane.org>
---
 drivers/i2c/muxes/i2c-mux-pca954x.c |   45 ++++++++++++++++++++++++++++++++---
 1 file changed, 42 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index a531d80..1241c97 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -41,6 +41,7 @@
 #include <linux/device.h>
 #include <linux/i2c.h>
 #include <linux/i2c-mux.h>
+#include <linux/workqueue.h>
 
 #include <linux/i2c/pca954x.h>
 
@@ -60,7 +61,8 @@ enum pca_type {
 struct pca954x {
 	enum pca_type type;
 	struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
-
+	struct i2c_client *client;
+	struct delayed_work deselect_work;
 	u8 last_chan;		/* last register value */
 };
 
@@ -168,11 +170,43 @@ static int pca954x_select_chan(struct i2c_adapter *adap,
 	return ret;
 }
 
-static int pca954x_deselect_mux(struct i2c_adapter *adap,
+static void pca954x_deselect_work(struct work_struct *work)
+{
+	struct pca954x *data = container_of(
+				work, struct pca954x, deselect_work.work);
+	/* Use the adapter lock as a mutex because any method that changes
+	 * the mux state will also hold this mutex. If the bus is in use,
+	 * the lock will assure that at least that transaction will
+	 * complete. */
+	i2c_lock_adapter(data->client->adapter);
+	if (data->last_chan != 0) {
+		int res;
+		/* Disable mux */
+		dev_dbg(&client->dev, "deselecting mux\n");
+		data->last_chan = 0;
+		res = pca954x_reg_write(data->client->adapter,
+				data->client, data->last_chan);
+		if (res < 0)
+			dev_err(&client->dev,
+				"%s: pca954x_reg_write failed: %d\n",
+				__func__, res);
+	}
+	i2c_unlock_adapter(data->client->adapter);
+}
+
+static int pca954x_deselect_mux_delayed(struct i2c_adapter *adap,
 				void *client, u32 chan)
 {
 	struct pca954x *data = i2c_get_clientdata(client);
+	/* Setup timer to disable at a later interval */
+	schedule_delayed_work(&data->deselect_work, msecs_to_jiffies(200));
+	return 0;
+}
 
+static int pca954x_deselect_mux_immediate(struct i2c_adapter *adap,
+				void *client, u32 chan)
+{
+	struct pca954x *data = i2c_get_clientdata(client);
 	/* Deselect active channel */
 	data->last_chan = 0;
 	return pca954x_reg_write(adap, client, data->last_chan);
@@ -212,6 +246,8 @@ static int pca954x_probe(struct i2c_client *client,
 
 	data->type = id->driver_data;
 	data->last_chan = 0;		   /* force the first selection */
+	data->client = client;
+	INIT_DELAYED_WORK(&data->deselect_work, pca954x_deselect_work);
 
 	/* Now create an adapter for each channel */
 	for (num = 0; num < chips[data->type].nchans; num++) {
@@ -231,7 +267,8 @@ static int pca954x_probe(struct i2c_client *client,
 			i2c_add_mux_adapter(adap, &client->dev, client,
 				force, num, class, pca954x_select_chan,
 				(pdata && pdata->modes[num].deselect_on_exit)
-					? pca954x_deselect_mux : NULL);
+					? pca954x_deselect_mux_immediate
+					: pca954x_deselect_mux_delayed);
 
 		if (data->virt_adaps[num] == NULL) {
 			ret = -ENODEV;
@@ -264,6 +301,8 @@ static int pca954x_remove(struct i2c_client *client)
 	const struct chip_desc *chip = &chips[data->type];
 	int i;
 
+	cancel_delayed_work_sync(&data->deselect_work);
+
 	for (i = 0; i < chip->nchans; ++i)
 		if (data->virt_adaps[i]) {
 			i2c_del_mux_adapter(data->virt_adaps[i]);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 11+ messages in thread
* [PATCH] i2c-mux-pca954x: Disable mux after 200ms timeout
@ 2013-11-25 15:02 Mike Looijmans
  0 siblings, 0 replies; 11+ messages in thread
From: Mike Looijmans @ 2013-11-25 15:02 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mike Looijmans

Leaving the mux enabled causes needless I2C traffic on the downstream
bus. De-selecting after every request causes excess I2C traffic and
switching.

This patch implements a hybrid solution: After 200ms of inactivity,
the mux is disabled.

Signed-off-by: Mike Looijmans <mike.looijmans-Oq418RWZeHk@public.gmane.org>
---
 drivers/i2c/muxes/i2c-mux-pca954x.c |   45 ++++++++++++++++++++++++++++++++---
 1 file changed, 42 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index a531d80..1241c97 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -41,6 +41,7 @@
 #include <linux/device.h>
 #include <linux/i2c.h>
 #include <linux/i2c-mux.h>
+#include <linux/workqueue.h>
 
 #include <linux/i2c/pca954x.h>
 
@@ -60,7 +61,8 @@ enum pca_type {
 struct pca954x {
 	enum pca_type type;
 	struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
-
+	struct i2c_client *client;
+	struct delayed_work deselect_work;
 	u8 last_chan;		/* last register value */
 };
 
@@ -168,11 +170,43 @@ static int pca954x_select_chan(struct i2c_adapter *adap,
 	return ret;
 }
 
-static int pca954x_deselect_mux(struct i2c_adapter *adap,
+static void pca954x_deselect_work(struct work_struct *work)
+{
+	struct pca954x *data = container_of(
+				work, struct pca954x, deselect_work.work);
+	/* Use the adapter lock as a mutex because any method that changes
+	 * the mux state will also hold this mutex. If the bus is in use,
+	 * the lock will assure that at least that transaction will
+	 * complete. */
+	i2c_lock_adapter(data->client->adapter);
+	if (data->last_chan != 0) {
+		int res;
+		/* Disable mux */
+		dev_dbg(&client->dev, "deselecting mux\n");
+		data->last_chan = 0;
+		res = pca954x_reg_write(data->client->adapter,
+				data->client, data->last_chan);
+		if (res < 0)
+			dev_err(&client->dev,
+				"%s: pca954x_reg_write failed: %d\n",
+				__func__, res);
+	}
+	i2c_unlock_adapter(data->client->adapter);
+}
+
+static int pca954x_deselect_mux_delayed(struct i2c_adapter *adap,
 				void *client, u32 chan)
 {
 	struct pca954x *data = i2c_get_clientdata(client);
+	/* Setup timer to disable at a later interval */
+	schedule_delayed_work(&data->deselect_work, msecs_to_jiffies(200));
+	return 0;
+}
 
+static int pca954x_deselect_mux_immediate(struct i2c_adapter *adap,
+				void *client, u32 chan)
+{
+	struct pca954x *data = i2c_get_clientdata(client);
 	/* Deselect active channel */
 	data->last_chan = 0;
 	return pca954x_reg_write(adap, client, data->last_chan);
@@ -212,6 +246,8 @@ static int pca954x_probe(struct i2c_client *client,
 
 	data->type = id->driver_data;
 	data->last_chan = 0;		   /* force the first selection */
+	data->client = client;
+	INIT_DELAYED_WORK(&data->deselect_work, pca954x_deselect_work);
 
 	/* Now create an adapter for each channel */
 	for (num = 0; num < chips[data->type].nchans; num++) {
@@ -231,7 +267,8 @@ static int pca954x_probe(struct i2c_client *client,
 			i2c_add_mux_adapter(adap, &client->dev, client,
 				force, num, class, pca954x_select_chan,
 				(pdata && pdata->modes[num].deselect_on_exit)
-					? pca954x_deselect_mux : NULL);
+					? pca954x_deselect_mux_immediate
+					: pca954x_deselect_mux_delayed);
 
 		if (data->virt_adaps[num] == NULL) {
 			ret = -ENODEV;
@@ -264,6 +301,8 @@ static int pca954x_remove(struct i2c_client *client)
 	const struct chip_desc *chip = &chips[data->type];
 	int i;
 
+	cancel_delayed_work_sync(&data->deselect_work);
+
 	for (i = 0; i < chip->nchans; ++i)
 		if (data->virt_adaps[i]) {
 			i2c_del_mux_adapter(data->virt_adaps[i]);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 11+ messages in thread
* [PATCH] i2c-mux-pca954x: Disable mux after 200ms timeout
@ 2013-11-25 13:43 Mike Looijmans
  2013-11-25 14:54 ` Michael Lawnick
  2013-11-25 14:57 ` Wolfram Sang
  0 siblings, 2 replies; 11+ messages in thread
From: Mike Looijmans @ 2013-11-25 13:43 UTC (permalink / raw)
  To: Wolfram Sang; +Cc: linux-i2c, linux-kernel, Mike Looijmans

Leaving the mux enabled causes needless I2C traffic on the downstream
bus. De-selecting after every request causes excess I2C traffic and
switching.

This patch implements a hybrid solution: After 200ms of inactivity,
the mux is disabled.

Signed-off-by: Mike Looijmans <mike.looijmans@topic.nl>
---
 drivers/i2c/muxes/i2c-mux-pca954x.c |   45 ++++++++++++++++++++++++++++++++---
 1 file changed, 42 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index a531d80..1241c97 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -41,6 +41,7 @@
 #include <linux/device.h>
 #include <linux/i2c.h>
 #include <linux/i2c-mux.h>
+#include <linux/workqueue.h>
 
 #include <linux/i2c/pca954x.h>
 
@@ -60,7 +61,8 @@ enum pca_type {
 struct pca954x {
 	enum pca_type type;
 	struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
-
+	struct i2c_client *client;
+	struct delayed_work deselect_work;
 	u8 last_chan;		/* last register value */
 };
 
@@ -168,11 +170,43 @@ static int pca954x_select_chan(struct i2c_adapter *adap,
 	return ret;
 }
 
-static int pca954x_deselect_mux(struct i2c_adapter *adap,
+static void pca954x_deselect_work(struct work_struct *work)
+{
+	struct pca954x *data = container_of(
+				work, struct pca954x, deselect_work.work);
+	/* Use the adapter lock as a mutex because any method that changes
+	 * the mux state will also hold this mutex. If the bus is in use,
+	 * the lock will assure that at least that transaction will
+	 * complete. */
+	i2c_lock_adapter(data->client->adapter);
+	if (data->last_chan != 0) {
+		int res;
+		/* Disable mux */
+		dev_dbg(&client->dev, "deselecting mux\n");
+		data->last_chan = 0;
+		res = pca954x_reg_write(data->client->adapter,
+				data->client, data->last_chan);
+		if (res < 0)
+			dev_err(&client->dev,
+				"%s: pca954x_reg_write failed: %d\n",
+				__func__, res);
+	}
+	i2c_unlock_adapter(data->client->adapter);
+}
+
+static int pca954x_deselect_mux_delayed(struct i2c_adapter *adap,
 				void *client, u32 chan)
 {
 	struct pca954x *data = i2c_get_clientdata(client);
+	/* Setup timer to disable at a later interval */
+	schedule_delayed_work(&data->deselect_work, msecs_to_jiffies(200));
+	return 0;
+}
 
+static int pca954x_deselect_mux_immediate(struct i2c_adapter *adap,
+				void *client, u32 chan)
+{
+	struct pca954x *data = i2c_get_clientdata(client);
 	/* Deselect active channel */
 	data->last_chan = 0;
 	return pca954x_reg_write(adap, client, data->last_chan);
@@ -212,6 +246,8 @@ static int pca954x_probe(struct i2c_client *client,
 
 	data->type = id->driver_data;
 	data->last_chan = 0;		   /* force the first selection */
+	data->client = client;
+	INIT_DELAYED_WORK(&data->deselect_work, pca954x_deselect_work);
 
 	/* Now create an adapter for each channel */
 	for (num = 0; num < chips[data->type].nchans; num++) {
@@ -231,7 +267,8 @@ static int pca954x_probe(struct i2c_client *client,
 			i2c_add_mux_adapter(adap, &client->dev, client,
 				force, num, class, pca954x_select_chan,
 				(pdata && pdata->modes[num].deselect_on_exit)
-					? pca954x_deselect_mux : NULL);
+					? pca954x_deselect_mux_immediate
+					: pca954x_deselect_mux_delayed);
 
 		if (data->virt_adaps[num] == NULL) {
 			ret = -ENODEV;
@@ -264,6 +301,8 @@ static int pca954x_remove(struct i2c_client *client)
 	const struct chip_desc *chip = &chips[data->type];
 	int i;
 
+	cancel_delayed_work_sync(&data->deselect_work);
+
 	for (i = 0; i < chip->nchans; ++i)
 		if (data->virt_adaps[i]) {
 			i2c_del_mux_adapter(data->virt_adaps[i]);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2013-11-26 17:00 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-26  6:32 [PATCH] i2c-mux-pca954x: Disable mux after 200ms timeout Mike Looijmans
     [not found] ` <1385447520-3306-1-git-send-email-mike.looijmans-Oq418RWZeHk@public.gmane.org>
2013-11-26  9:06   ` Wolfram Sang
2013-11-26  9:36     ` Mike Looijmans
2013-11-26 12:28       ` Wolfram Sang
2013-11-26 13:39         ` Ulf Hansson
2013-11-26 14:06         ` Mike Looijmans
     [not found]           ` <5294AADC.5070707-Oq418RWZeHk@public.gmane.org>
2013-11-26 17:00             ` Wolfram Sang
  -- strict thread matches above, loose matches on Subject: below --
2013-11-25 15:02 Mike Looijmans
2013-11-25 13:43 Mike Looijmans
2013-11-25 14:54 ` Michael Lawnick
2013-11-25 14:57 ` Wolfram Sang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).