diff options
author | Wolfram Sang <wsa+renesas@sang-engineering.com> | 2016-01-13 09:29:27 -0500 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2016-02-12 13:16:04 -0500 |
commit | 50a5ba876908147b36441c754e835588143c6b54 (patch) | |
tree | c280d50a5f6d1a4fde1537a13e87cd423cb775bd | |
parent | 388f7b1d6e8ca06762e2454d28d6c3c55ad0fe95 (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-pinctrl | 23 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.txt | 135 | ||||
-rw-r--r-- | drivers/i2c/muxes/Kconfig | 9 | ||||
-rw-r--r-- | drivers/i2c/muxes/Makefile | 2 | ||||
-rw-r--r-- | drivers/i2c/muxes/i2c-demux-pinctrl.c | 272 |
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 @@ | |||
1 | What: /sys/devices/platform/<i2c-demux-name>/cur_master | ||
2 | Date: January 2016 | ||
3 | KernelVersion: 4.6 | ||
4 | Contact: Wolfram Sang <wsa@the-dreams.de> | ||
5 | Description: | ||
6 | |||
7 | This file selects the active I2C master for a demultiplexed bus. | ||
8 | |||
9 | Write 0 there for the first master, 1 for the second etc. Reading the file will | ||
10 | give you a list with the active master marked. Example from a Renesas Lager | ||
11 | board: | ||
12 | |||
13 | root@Lager:~# cat /sys/devices/platform/i2c@8/cur_master | ||
14 | * 0 - /i2c@9 | ||
15 | 1 - /i2c@e6520000 | ||
16 | 2 - /i2c@e6530000 | ||
17 | |||
18 | root@Lager:~# echo 2 > /sys/devices/platform/i2c@8/cur_master | ||
19 | |||
20 | root@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 @@ | |||
1 | Pinctrl-based I2C Bus DeMux | ||
2 | |||
3 | This binding describes an I2C bus demultiplexer that uses pin multiplexing to | ||
4 | route the I2C signals, and represents the pin multiplexing configuration using | ||
5 | the pinctrl device tree bindings. This may be used to select one I2C IP core at | ||
6 | runtime which may have a better feature set for a given task than another I2C | ||
7 | IP core on the SoC. The most simple example is to fall back to GPIO bitbanging | ||
8 | if 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 | |||
23 | Required 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 | |||
30 | Furthermore, I2C mux properties and child nodes. See mux.txt in this directory. | ||
31 | |||
32 | Example: | ||
33 | |||
34 | Here is a snipplet for a bus to be demuxed. It contains various i2c clients for | ||
35 | HDMI, 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 | |||
98 | And 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 | |||
129 | Please 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 | ||
75 | config 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 | |||
75 | endmenu | 84 | endmenu |
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 | ||
4 | obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o | 4 | obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o |
5 | 5 | ||
6 | obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o | ||
7 | |||
6 | obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o | 8 | obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o |
7 | obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o | 9 | obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o |
8 | obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o | 10 | obj-$(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 | |||
24 | struct i2c_demux_pinctrl_chan { | ||
25 | struct device_node *parent_np; | ||
26 | struct i2c_adapter *parent_adap; | ||
27 | struct of_changeset chgset; | ||
28 | }; | ||
29 | |||
30 | struct 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 | |||
40 | static struct property status_okay = { .name = "status", .length = 3, .value = "ok" }; | ||
41 | |||
42 | static 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 | |||
50 | static 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 | |||
58 | static 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 | |||
111 | static 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 | |||
129 | static 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 | |||
143 | static 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 | |||
157 | static 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 | } | ||
175 | static DEVICE_ATTR_RW(cur_master); | ||
176 | |||
177 | static 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 | |||
227 | err_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 | |||
236 | static 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 | |||
253 | static const struct of_device_id i2c_demux_pinctrl_of_match[] = { | ||
254 | { .compatible = "i2c-demux-pinctrl", }, | ||
255 | {}, | ||
256 | }; | ||
257 | MODULE_DEVICE_TABLE(of, i2c_demux_pinctrl_of_match); | ||
258 | |||
259 | static 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 | }; | ||
267 | module_platform_driver(i2c_demux_pinctrl_driver); | ||
268 | |||
269 | MODULE_DESCRIPTION("pinctrl-based I2C demux driver"); | ||
270 | MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>"); | ||
271 | MODULE_LICENSE("GPL v2"); | ||
272 | MODULE_ALIAS("platform:i2c-demux-pinctrl"); | ||