aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWolfram Sang <wsa+renesas@sang-engineering.com>2016-01-13 09:29:27 -0500
committerWolfram Sang <wsa@the-dreams.de>2016-02-12 13:16:04 -0500
commit50a5ba876908147b36441c754e835588143c6b54 (patch)
treec280d50a5f6d1a4fde1537a13e87cd423cb775bd
parent388f7b1d6e8ca06762e2454d28d6c3c55ad0fe95 (diff)
i2c: mux: demux-pinctrl: add driver
This driver allows an I2C bus to switch between multiple masters. This is not hot-switching because connected I2C slaves will be re-instantiated. It is meant to select the best I2C core at runtime once the task is known. Example: Prefer i2c-gpio over another I2C core because of HW errata affecting your use case. Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--Documentation/ABI/testing/sysfs-platform-i2c-demux-pinctrl23
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.txt135
-rw-r--r--drivers/i2c/muxes/Kconfig9
-rw-r--r--drivers/i2c/muxes/Makefile2
-rw-r--r--drivers/i2c/muxes/i2c-demux-pinctrl.c272
5 files changed, 441 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-i2c-demux-pinctrl b/Documentation/ABI/testing/sysfs-platform-i2c-demux-pinctrl
new file mode 100644
index 000000000000..7ac7d7262bb7
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-i2c-demux-pinctrl
@@ -0,0 +1,23 @@
1What: /sys/devices/platform/<i2c-demux-name>/cur_master
2Date: January 2016
3KernelVersion: 4.6
4Contact: Wolfram Sang <wsa@the-dreams.de>
5Description:
6
7This file selects the active I2C master for a demultiplexed bus.
8
9Write 0 there for the first master, 1 for the second etc. Reading the file will
10give you a list with the active master marked. Example from a Renesas Lager
11board:
12
13root@Lager:~# cat /sys/devices/platform/i2c@8/cur_master
14* 0 - /i2c@9
15 1 - /i2c@e6520000
16 2 - /i2c@e6530000
17
18root@Lager:~# echo 2 > /sys/devices/platform/i2c@8/cur_master
19
20root@Lager:~# cat /sys/devices/platform/i2c@8/cur_master
21 0 - /i2c@9
22 1 - /i2c@e6520000
23* 2 - /i2c@e6530000
diff --git a/Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.txt b/Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.txt
new file mode 100644
index 000000000000..6078aefe7ed4
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.txt
@@ -0,0 +1,135 @@
1Pinctrl-based I2C Bus DeMux
2
3This binding describes an I2C bus demultiplexer that uses pin multiplexing to
4route the I2C signals, and represents the pin multiplexing configuration using
5the pinctrl device tree bindings. This may be used to select one I2C IP core at
6runtime which may have a better feature set for a given task than another I2C
7IP core on the SoC. The most simple example is to fall back to GPIO bitbanging
8if your current runtime configuration hits an errata of the internal IP core.
9
10 +-------------------------------+
11 | SoC |
12 | | +-----+ +-----+
13 | +------------+ | | dev | | dev |
14 | |I2C IP Core1|--\ | +-----+ +-----+
15 | +------------+ \-------+ | | |
16 | |Pinctrl|--|------+--------+
17 | +------------+ +-------+ |
18 | |I2C IP Core2|--/ |
19 | +------------+ |
20 | |
21 +-------------------------------+
22
23Required properties:
24- compatible: "i2c-demux-pinctrl"
25- i2c-parent: List of phandles of I2C masters available for selection. The first
26 one will be used as default.
27- i2c-bus-name: The name of this bus. Also needed as pinctrl-name for the I2C
28 parents.
29
30Furthermore, I2C mux properties and child nodes. See mux.txt in this directory.
31
32Example:
33
34Here is a snipplet for a bus to be demuxed. It contains various i2c clients for
35HDMI, so the bus is named "i2c-hdmi":
36
37 i2chdmi: i2c@8 {
38
39 compatible = "i2c-demux-pinctrl";
40 i2c-parent = <&gpioi2c>, <&iic2>, <&i2c2>;
41 i2c-bus-name = "i2c-hdmi";
42 #address-cells = <1>;
43 #size-cells = <0>;
44
45 ak4643: sound-codec@12 {
46 compatible = "asahi-kasei,ak4643";
47
48 #sound-dai-cells = <0>;
49 reg = <0x12>;
50 };
51
52 composite-in@20 {
53 compatible = "adi,adv7180";
54 reg = <0x20>;
55 remote = <&vin1>;
56
57 port {
58 adv7180: endpoint {
59 bus-width = <8>;
60 remote-endpoint = <&vin1ep0>;
61 };
62 };
63 };
64
65 hdmi@39 {
66 compatible = "adi,adv7511w";
67 reg = <0x39>;
68 interrupt-parent = <&gpio1>;
69 interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
70
71 adi,input-depth = <8>;
72 adi,input-colorspace = "rgb";
73 adi,input-clock = "1x";
74 adi,input-style = <1>;
75 adi,input-justification = "evenly";
76
77 ports {
78 #address-cells = <1>;
79 #size-cells = <0>;
80
81 port@0 {
82 reg = <0>;
83 adv7511_in: endpoint {
84 remote-endpoint = <&du_out_lvds0>;
85 };
86 };
87
88 port@1 {
89 reg = <1>;
90 adv7511_out: endpoint {
91 remote-endpoint = <&hdmi_con>;
92 };
93 };
94 };
95 };
96 };
97
98And for clarification, here are the snipplets for the i2c-parents:
99
100 gpioi2c: i2c@9 {
101 #address-cells = <1>;
102 #size-cells = <0>;
103 compatible = "i2c-gpio";
104 status = "disabled";
105 gpios = <&gpio5 6 GPIO_ACTIVE_HIGH /* sda */
106 &gpio5 5 GPIO_ACTIVE_HIGH /* scl */
107 >;
108 i2c-gpio,delay-us = <5>;
109 };
110
111...
112
113&i2c2 {
114 pinctrl-0 = <&i2c2_pins>;
115 pinctrl-names = "i2c-hdmi";
116
117 clock-frequency = <100000>;
118};
119
120...
121
122&iic2 {
123 pinctrl-0 = <&iic2_pins>;
124 pinctrl-names = "i2c-hdmi";
125
126 clock-frequency = <100000>;
127};
128
129Please note:
130
131- pinctrl properties for the parent I2C controllers need a pinctrl state
132 with the same name as i2c-bus-name, not "default"!
133
134- the i2c masters must have their status "disabled". This driver will
135 enable them at runtime when needed.
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index f06b0e24673b..e280c8ecc0b5 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -72,4 +72,13 @@ config I2C_MUX_REG
72 This driver can also be built as a module. If so, the module 72 This driver can also be built as a module. If so, the module
73 will be called i2c-mux-reg. 73 will be called i2c-mux-reg.
74 74
75config I2C_DEMUX_PINCTRL
76 tristate "pinctrl-based I2C demultiplexer"
77 depends on PINCTRL && OF
78 select OF_DYNAMIC
79 help
80 If you say yes to this option, support will be included for an I2C
81 demultiplexer that uses the pinctrl subsystem. This is useful if you
82 want to change the I2C master at run-time depending on features.
83
75endmenu 84endmenu
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index e89799b76a92..7c267c29b191 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -3,6 +3,8 @@
3 3
4obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o 4obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
5 5
6obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o
7
6obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o 8obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
7obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o 9obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
8obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o 10obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c
new file mode 100644
index 000000000000..7748a0a5ddb9
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c
@@ -0,0 +1,272 @@
1/*
2 * Pinctrl based I2C DeMultiplexer
3 *
4 * Copyright (C) 2015-16 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
5 * Copyright (C) 2015-16 by Renesas Electronics Corporation
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; version 2 of the License.
10 *
11 * See the bindings doc for DTS setup and the sysfs doc for usage information.
12 * (look for filenames containing 'i2c-demux-pinctrl' in Documentation/)
13 */
14
15#include <linux/i2c.h>
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/pinctrl/consumer.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22#include <linux/sysfs.h>
23
24struct i2c_demux_pinctrl_chan {
25 struct device_node *parent_np;
26 struct i2c_adapter *parent_adap;
27 struct of_changeset chgset;
28};
29
30struct i2c_demux_pinctrl_priv {
31 int cur_chan;
32 int num_chan;
33 struct device *dev;
34 const char *bus_name;
35 struct i2c_adapter cur_adap;
36 struct i2c_algorithm algo;
37 struct i2c_demux_pinctrl_chan chan[];
38};
39
40static struct property status_okay = { .name = "status", .length = 3, .value = "ok" };
41
42static int i2c_demux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
43{
44 struct i2c_demux_pinctrl_priv *priv = adap->algo_data;
45 struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap;
46
47 return __i2c_transfer(parent, msgs, num);
48}
49
50static u32 i2c_demux_functionality(struct i2c_adapter *adap)
51{
52 struct i2c_demux_pinctrl_priv *priv = adap->algo_data;
53 struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap;
54
55 return parent->algo->functionality(parent);
56}
57
58static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan)
59{
60 struct i2c_adapter *adap;
61 struct pinctrl *p;
62 int ret;
63
64 ret = of_changeset_apply(&priv->chan[new_chan].chgset);
65 if (ret)
66 goto err;
67
68 adap = of_find_i2c_adapter_by_node(priv->chan[new_chan].parent_np);
69 if (!adap) {
70 ret = -ENODEV;
71 goto err;
72 }
73
74 p = devm_pinctrl_get_select(adap->dev.parent, priv->bus_name);
75 if (IS_ERR(p)) {
76 ret = PTR_ERR(p);
77 goto err_with_put;
78 }
79
80 priv->chan[new_chan].parent_adap = adap;
81 priv->cur_chan = new_chan;
82
83 /* Now fill out current adapter structure. cur_chan must be up to date */
84 priv->algo.master_xfer = i2c_demux_master_xfer;
85 priv->algo.functionality = i2c_demux_functionality;
86
87 snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name),
88 "i2c-demux (master i2c-%d)", i2c_adapter_id(adap));
89 priv->cur_adap.owner = THIS_MODULE;
90 priv->cur_adap.algo = &priv->algo;
91 priv->cur_adap.algo_data = priv;
92 priv->cur_adap.dev.parent = priv->dev;
93 priv->cur_adap.class = adap->class;
94 priv->cur_adap.retries = adap->retries;
95 priv->cur_adap.timeout = adap->timeout;
96 priv->cur_adap.quirks = adap->quirks;
97 priv->cur_adap.dev.of_node = priv->dev->of_node;
98 ret = i2c_add_adapter(&priv->cur_adap);
99 if (ret < 0)
100 goto err_with_put;
101
102 return 0;
103
104 err_with_put:
105 i2c_put_adapter(adap);
106 err:
107 dev_err(priv->dev, "failed to setup demux-adapter %d (%d)\n", new_chan, ret);
108 return ret;
109}
110
111static int i2c_demux_deactivate_master(struct i2c_demux_pinctrl_priv *priv)
112{
113 int ret, cur = priv->cur_chan;
114
115 if (cur < 0)
116 return 0;
117
118 i2c_del_adapter(&priv->cur_adap);
119 i2c_put_adapter(priv->chan[cur].parent_adap);
120
121 ret = of_changeset_revert(&priv->chan[cur].chgset);
122
123 priv->chan[cur].parent_adap = NULL;
124 priv->cur_chan = -EINVAL;
125
126 return ret;
127}
128
129static int i2c_demux_change_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan)
130{
131 int ret;
132
133 if (new_chan == priv->cur_chan)
134 return 0;
135
136 ret = i2c_demux_deactivate_master(priv);
137 if (ret)
138 return ret;
139
140 return i2c_demux_activate_master(priv, new_chan);
141}
142
143static ssize_t cur_master_show(struct device *dev, struct device_attribute *attr,
144 char *buf)
145{
146 struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev);
147 int count = 0, i;
148
149 for (i = 0; i < priv->num_chan && count < PAGE_SIZE; i++)
150 count += scnprintf(buf + count, PAGE_SIZE - count, "%c %d - %s\n",
151 i == priv->cur_chan ? '*' : ' ', i,
152 priv->chan[i].parent_np->full_name);
153
154 return count;
155}
156
157static ssize_t cur_master_store(struct device *dev, struct device_attribute *attr,
158 const char *buf, size_t count)
159{
160 struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev);
161 unsigned int val;
162 int ret;
163
164 ret = kstrtouint(buf, 0, &val);
165 if (ret < 0)
166 return ret;
167
168 if (val >= priv->num_chan)
169 return -EINVAL;
170
171 ret = i2c_demux_change_master(priv, val);
172
173 return ret < 0 ? ret : count;
174}
175static DEVICE_ATTR_RW(cur_master);
176
177static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
178{
179 struct device_node *np = pdev->dev.of_node;
180 struct i2c_demux_pinctrl_priv *priv;
181 int num_chan, i, j, err;
182
183 num_chan = of_count_phandle_with_args(np, "i2c-parent", NULL);
184 if (num_chan < 2) {
185 dev_err(&pdev->dev, "Need at least two I2C masters to switch\n");
186 return -EINVAL;
187 }
188
189 priv = devm_kzalloc(&pdev->dev, sizeof(*priv)
190 + num_chan * sizeof(struct i2c_demux_pinctrl_chan), GFP_KERNEL);
191 if (!priv)
192 return -ENOMEM;
193
194 err = of_property_read_string(np, "i2c-bus-name", &priv->bus_name);
195 if (err)
196 return err;
197
198 for (i = 0; i < num_chan; i++) {
199 struct device_node *adap_np;
200
201 adap_np = of_parse_phandle(np, "i2c-parent", i);
202 if (!adap_np) {
203 dev_err(&pdev->dev, "can't get phandle for parent %d\n", i);
204 err = -ENOENT;
205 goto err_rollback;
206 }
207 priv->chan[i].parent_np = adap_np;
208
209 of_changeset_init(&priv->chan[i].chgset);
210 of_changeset_update_property(&priv->chan[i].chgset, adap_np, &status_okay);
211 }
212
213 priv->num_chan = num_chan;
214 priv->dev = &pdev->dev;
215
216 platform_set_drvdata(pdev, priv);
217
218 /* switch to first parent as active master */
219 i2c_demux_activate_master(priv, 0);
220
221 err = device_create_file(&pdev->dev, &dev_attr_cur_master);
222 if (err)
223 goto err_rollback;
224
225 return 0;
226
227err_rollback:
228 for (j = 0; j < i; j++) {
229 of_node_put(priv->chan[j].parent_np);
230 of_changeset_destroy(&priv->chan[j].chgset);
231 }
232
233 return err;
234}
235
236static int i2c_demux_pinctrl_remove(struct platform_device *pdev)
237{
238 struct i2c_demux_pinctrl_priv *priv = platform_get_drvdata(pdev);
239 int i;
240
241 device_remove_file(&pdev->dev, &dev_attr_cur_master);
242
243 i2c_demux_deactivate_master(priv);
244
245 for (i = 0; i < priv->num_chan; i++) {
246 of_node_put(priv->chan[i].parent_np);
247 of_changeset_destroy(&priv->chan[i].chgset);
248 }
249
250 return 0;
251}
252
253static const struct of_device_id i2c_demux_pinctrl_of_match[] = {
254 { .compatible = "i2c-demux-pinctrl", },
255 {},
256};
257MODULE_DEVICE_TABLE(of, i2c_demux_pinctrl_of_match);
258
259static struct platform_driver i2c_demux_pinctrl_driver = {
260 .driver = {
261 .name = "i2c-demux-pinctrl",
262 .of_match_table = i2c_demux_pinctrl_of_match,
263 },
264 .probe = i2c_demux_pinctrl_probe,
265 .remove = i2c_demux_pinctrl_remove,
266};
267module_platform_driver(i2c_demux_pinctrl_driver);
268
269MODULE_DESCRIPTION("pinctrl-based I2C demux driver");
270MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
271MODULE_LICENSE("GPL v2");
272MODULE_ALIAS("platform:i2c-demux-pinctrl");