of: i2c: Add DT bindings for idle states to PCA954x mux driver

Introduce two new device tree bindings to specify idle state of PCA954x family
of I2C multiplexors:
  - idle-state: specifies particular child bus to be selected in idle;
  - idle-disconnect: signals that mux should disconnect all child buses in idle;

Signed-off-by: Alexander Sverdlin <[email protected]>
---
 .../devicetree/bindings/i2c/i2c-mux-pca954x.txt    |    3 +
 drivers/i2c/muxes/i2c-mux-pca954x.c                |   51 +++++++++++++++++--
 2 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt 
b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
index 34a3fb6..1fbe287 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
@@ -16,6 +16,9 @@ Required Properties:
 Optional Properties:
 
   - reset-gpios: Reference to the GPIO connected to the reset input.
+  - idle-state: Child bus connected in idle state (specified by its "reg" 
value)
+  - idle-disconnect: Boolean; if defined, forces mux to disconnect all children
+    in idle state
 
 
 Example:
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c 
b/drivers/i2c/muxes/i2c-mux-pca954x.c
index ec11b40..69cf603 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -43,6 +43,7 @@
 #include <linux/module.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #define PCA954X_MAX_NCHANS 8
 
@@ -62,6 +63,8 @@ struct pca954x {
        struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
 
        u8 last_chan;           /* last register value */
+       bool idle_disconnect;
+       s8 idle_chan;           /* valid if not negative */
 };
 
 struct chip_desc {
@@ -172,10 +175,20 @@ static int pca954x_deselect_mux(struct i2c_adapter *adap,
                                void *client, u32 chan)
 {
        struct pca954x *data = i2c_get_clientdata(client);
+       struct pca954x_platform_data *pdata =
+               dev_get_platdata(&((struct i2c_client *)client)->dev);
+
+       if ((pdata && pdata->modes[chan].deselect_on_exit) ||
+           data->idle_disconnect) {
+               /* Deselect active channel */
+               data->last_chan = 0;
+               return pca954x_reg_write(adap, client, data->last_chan);
+       }
 
-       /* Deselect active channel */
-       data->last_chan = 0;
-       return pca954x_reg_write(adap, client, data->last_chan);
+       if (data->idle_chan >= 0)
+               return pca954x_select_chan(adap, client, data->idle_chan);
+
+       return 0;
 }
 
 /*
@@ -186,6 +199,7 @@ static int pca954x_probe(struct i2c_client *client,
 {
        struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
        struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
+       struct device_node *of_node = client->dev.of_node;
        struct gpio_desc *gpio;
        int num, force, class;
        struct pca954x *data;
@@ -216,6 +230,27 @@ static int pca954x_probe(struct i2c_client *client,
 
        data->type = id->driver_data;
        data->last_chan = 0;               /* force the first selection */
+       data->idle_chan = -1;              /* no forced idle state */
+
+       if (of_node) {
+               u32 ch;
+
+               if (of_property_read_bool(of_node, "idle-disconnect"))
+                       data->idle_disconnect = true;
+
+               if (!of_property_read_u32_index(of_node, "idle-state", 0, &ch)) 
{
+                       if (ch < PCA954X_MAX_NCHANS) {
+                               data->idle_chan = ch;
+                               /* Force idle state from the beginning */
+                               ret = pca954x_select_chan(adap, client, ch);
+                               if (ret)
+                                       return ret;
+                       } else {
+                               dev_warn(&client->dev,
+                                        "Invalid idle-state property\n");
+                       }
+               }
+       }
 
        /* Now create an adapter for each channel */
        for (num = 0; num < chips[data->type].nchans; num++) {
@@ -234,8 +269,7 @@ static int pca954x_probe(struct i2c_client *client,
                data->virt_adaps[num] =
                        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);
 
                if (data->virt_adaps[num] == NULL) {
                        ret = -ENODEV;
@@ -281,7 +315,12 @@ static int pca954x_resume(struct device *dev)
        struct pca954x *data = i2c_get_clientdata(client);
 
        data->last_chan = 0;
-       return i2c_smbus_write_byte(client, 0);
+       /* Restore idle state on resume */
+       if (data->idle_chan >= 0)
+               return pca954x_select_chan(to_i2c_adapter(client->dev.parent),
+                                          client, data->idle_chan);
+       else
+               return i2c_smbus_write_byte(client, 0);
 }
 #endif
 
-- 
1.7.1


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

Reply via email to