aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYork Sun <yorksun@freescale.com>2015-08-17 14:53:48 -0400
committerWolfram Sang <wsa@the-dreams.de>2015-08-24 08:05:18 -0400
commitb3fdd32799d834e2626fae087906e886037350c6 (patch)
tree38d3df37fe04e8631ba955a3dfd6ed2a80dd7ff8
parent7a59b00a0906945f7fe25a10332ac0820491a0c3 (diff)
i2c: mux: Add register-based mux i2c-mux-reg
Based on i2c-mux-gpio driver, similarly the register-based mux switch from one bus to another by setting a single register. The register can be on PCIe bus, local bus, or any memory-mapped address. The endianness of such register can be specified in device tree if used, or in platform data. Signed-off-by: York Sun <yorksun@freescale.com> Acked-by: Alexander Sverdlin <alexander.sverdlin@nokia.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt74
-rw-r--r--drivers/i2c/muxes/Kconfig11
-rw-r--r--drivers/i2c/muxes/Makefile1
-rw-r--r--drivers/i2c/muxes/i2c-mux-reg.c294
-rw-r--r--include/linux/platform_data/i2c-mux-reg.h44
5 files changed, 424 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt
new file mode 100644
index 000000000000..688783fbe696
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt
@@ -0,0 +1,74 @@
1Register-based I2C Bus Mux
2
3This binding describes an I2C bus multiplexer that uses a single register
4to route the I2C signals.
5
6Required properties:
7- compatible: i2c-mux-reg
8- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side
9 port is connected to.
10* Standard I2C mux properties. See mux.txt in this directory.
11* I2C child bus nodes. See mux.txt in this directory.
12
13Optional properties:
14- reg: this pair of <offset size> specifies the register to control the mux.
15 The <offset size> depends on its parent node. It can be any memory-mapped
16 address. The size must be either 1, 2, or 4 bytes. If reg is omitted, the
17 resource of this device will be used.
18- little-endian: The existence indicates the register is in little endian.
19- big-endian: The existence indicates the register is in big endian.
20 If both little-endian and big-endian are omitted, the endianness of the
21 CPU will be used.
22- write-only: The existence indicates the register is write-only.
23- idle-state: value to set the muxer to when idle. When no value is
24 given, it defaults to the last value used.
25
26Whenever an access is made to a device on a child bus, the value set
27in the revelant node's reg property will be output to the register.
28
29If an idle state is defined, using the idle-state (optional) property,
30whenever an access is not being made to a device on a child bus, the
31register will be set according to the idle value.
32
33If an idle state is not defined, the most recently used value will be
34left programmed into the register.
35
36Example of a mux on PCIe card, the host is a powerpc SoC (big endian):
37
38 i2c-mux {
39 /* the <offset size> depends on the address translation
40 * of the parent device. If omitted, device resource
41 * will be used instead. The size is to determine
42 * whether iowrite32, iowrite16, or iowrite8 will be used.
43 */
44 reg = <0x6028 0x4>;
45 little-endian; /* little endian register on PCIe */
46 compatible = "i2c-mux-reg";
47 #address-cells = <1>;
48 #size-cells = <0>;
49 i2c-parent = <&i2c1>;
50 i2c@0 {
51 reg = <0>;
52 #address-cells = <1>;
53 #size-cells = <0>;
54
55 si5338: clock-generator@70 {
56 compatible = "silabs,si5338";
57 reg = <0x70>;
58 /* other stuff */
59 };
60 };
61
62 i2c@1 {
63 /* data is written using iowrite32 */
64 reg = <1>;
65 #address-cells = <1>;
66 #size-cells = <0>;
67
68 si5338: clock-generator@70 {
69 compatible = "silabs,si5338";
70 reg = <0x70>;
71 /* other stuff */
72 };
73 };
74 };
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index fdd0769c84a3..f06b0e24673b 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -61,4 +61,15 @@ config I2C_MUX_PINCTRL
61 This driver can also be built as a module. If so, the module will be 61 This driver can also be built as a module. If so, the module will be
62 called pinctrl-i2cmux. 62 called pinctrl-i2cmux.
63 63
64config I2C_MUX_REG
65 tristate "Register-based I2C multiplexer"
66 help
67 If you say yes to this option, support will be included for a
68 register based I2C multiplexer. This driver provides access to
69 I2C busses connected through a MUX, which is controlled
70 by a single register.
71
72 This driver can also be built as a module. If so, the module
73 will be called i2c-mux-reg.
74
64endmenu 75endmenu
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 465778b5d5dc..e89799b76a92 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
7obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o 7obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
8obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o 8obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
9obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o 9obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
10obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o
10 11
11ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG 12ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c
new file mode 100644
index 000000000000..86d41d36a783
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-mux-reg.c
@@ -0,0 +1,294 @@
1/*
2 * I2C multiplexer using a single register
3 *
4 * Copyright 2015 Freescale Semiconductor
5 * York Sun <yorksun@freescale.com>
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; either version 2 of the License, or (at your
10 * option) any later version.
11 */
12
13#include <linux/i2c.h>
14#include <linux/i2c-mux.h>
15#include <linux/init.h>
16#include <linux/io.h>
17#include <linux/module.h>
18#include <linux/of_address.h>
19#include <linux/platform_data/i2c-mux-reg.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22
23struct regmux {
24 struct i2c_adapter *parent;
25 struct i2c_adapter **adap; /* child busses */
26 struct i2c_mux_reg_platform_data data;
27};
28
29static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id)
30{
31 if (!mux->data.reg)
32 return -EINVAL;
33
34 switch (mux->data.reg_size) {
35 case 4:
36 if (mux->data.little_endian) {
37 iowrite32(chan_id, mux->data.reg);
38 if (!mux->data.write_only)
39 ioread32(mux->data.reg);
40 } else {
41 iowrite32be(chan_id, mux->data.reg);
42 if (!mux->data.write_only)
43 ioread32(mux->data.reg);
44 }
45 break;
46 case 2:
47 if (mux->data.little_endian) {
48 iowrite16(chan_id, mux->data.reg);
49 if (!mux->data.write_only)
50 ioread16(mux->data.reg);
51 } else {
52 iowrite16be(chan_id, mux->data.reg);
53 if (!mux->data.write_only)
54 ioread16be(mux->data.reg);
55 }
56 break;
57 case 1:
58 iowrite8(chan_id, mux->data.reg);
59 if (!mux->data.write_only)
60 ioread8(mux->data.reg);
61 break;
62 default:
63 pr_err("Invalid register size\n");
64 return -EINVAL;
65 }
66
67 return 0;
68}
69
70static int i2c_mux_reg_select(struct i2c_adapter *adap, void *data,
71 unsigned int chan)
72{
73 struct regmux *mux = data;
74
75 return i2c_mux_reg_set(mux, chan);
76}
77
78static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data,
79 unsigned int chan)
80{
81 struct regmux *mux = data;
82
83 if (mux->data.idle_in_use)
84 return i2c_mux_reg_set(mux, mux->data.idle);
85
86 return 0;
87}
88
89#ifdef CONFIG_OF
90static int i2c_mux_reg_probe_dt(struct regmux *mux,
91 struct platform_device *pdev)
92{
93 struct device_node *np = pdev->dev.of_node;
94 struct device_node *adapter_np, *child;
95 struct i2c_adapter *adapter;
96 struct resource res;
97 unsigned *values;
98 int i = 0;
99
100 if (!np)
101 return -ENODEV;
102
103 adapter_np = of_parse_phandle(np, "i2c-parent", 0);
104 if (!adapter_np) {
105 dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
106 return -ENODEV;
107 }
108 adapter = of_find_i2c_adapter_by_node(adapter_np);
109 if (!adapter)
110 return -EPROBE_DEFER;
111
112 mux->parent = adapter;
113 mux->data.parent = i2c_adapter_id(adapter);
114 put_device(&adapter->dev);
115
116 mux->data.n_values = of_get_child_count(np);
117 if (of_find_property(np, "little-endian", NULL)) {
118 mux->data.little_endian = true;
119 } else if (of_find_property(np, "big-endian", NULL)) {
120 mux->data.little_endian = false;
121 } else {
122#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : \
123 defined(__LITTLE_ENDIAN)
124 mux->data.little_endian = true;
125#elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : \
126 defined(__BIG_ENDIAN)
127 mux->data.little_endian = false;
128#else
129#error Endianness not defined?
130#endif
131 }
132 if (of_find_property(np, "write-only", NULL))
133 mux->data.write_only = true;
134 else
135 mux->data.write_only = false;
136
137 values = devm_kzalloc(&pdev->dev,
138 sizeof(*mux->data.values) * mux->data.n_values,
139 GFP_KERNEL);
140 if (!values) {
141 dev_err(&pdev->dev, "Cannot allocate values array");
142 return -ENOMEM;
143 }
144
145 for_each_child_of_node(np, child) {
146 of_property_read_u32(child, "reg", values + i);
147 i++;
148 }
149 mux->data.values = values;
150
151 if (!of_property_read_u32(np, "idle-state", &mux->data.idle))
152 mux->data.idle_in_use = true;
153
154 /* map address from "reg" if exists */
155 if (of_address_to_resource(np, 0, &res)) {
156 mux->data.reg_size = resource_size(&res);
157 if (mux->data.reg_size > 4) {
158 dev_err(&pdev->dev, "Invalid address size\n");
159 return -EINVAL;
160 }
161 mux->data.reg = devm_ioremap_resource(&pdev->dev, &res);
162 if (IS_ERR(mux->data.reg))
163 return PTR_ERR(mux->data.reg);
164 }
165
166 return 0;
167}
168#else
169static int i2c_mux_reg_probe_dt(struct gpiomux *mux,
170 struct platform_device *pdev)
171{
172 return 0;
173}
174#endif
175
176static int i2c_mux_reg_probe(struct platform_device *pdev)
177{
178 struct regmux *mux;
179 struct i2c_adapter *parent;
180 struct resource *res;
181 int (*deselect)(struct i2c_adapter *, void *, u32);
182 unsigned int class;
183 int i, ret, nr;
184
185 mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
186 if (!mux)
187 return -ENOMEM;
188
189 platform_set_drvdata(pdev, mux);
190
191 if (dev_get_platdata(&pdev->dev)) {
192 memcpy(&mux->data, dev_get_platdata(&pdev->dev),
193 sizeof(mux->data));
194
195 parent = i2c_get_adapter(mux->data.parent);
196 if (!parent)
197 return -EPROBE_DEFER;
198
199 mux->parent = parent;
200 } else {
201 ret = i2c_mux_reg_probe_dt(mux, pdev);
202 if (ret < 0) {
203 dev_err(&pdev->dev, "Error parsing device tree");
204 return ret;
205 }
206 }
207
208 if (!mux->data.reg) {
209 dev_info(&pdev->dev,
210 "Register not set, using platform resource\n");
211 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
212 mux->data.reg_size = resource_size(res);
213 if (mux->data.reg_size > 4) {
214 dev_err(&pdev->dev, "Invalid resource size\n");
215 return -EINVAL;
216 }
217 mux->data.reg = devm_ioremap_resource(&pdev->dev, res);
218 if (IS_ERR(mux->data.reg))
219 return PTR_ERR(mux->data.reg);
220 }
221
222 mux->adap = devm_kzalloc(&pdev->dev,
223 sizeof(*mux->adap) * mux->data.n_values,
224 GFP_KERNEL);
225 if (!mux->adap) {
226 dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure");
227 return -ENOMEM;
228 }
229
230 if (mux->data.idle_in_use)
231 deselect = i2c_mux_reg_deselect;
232 else
233 deselect = NULL;
234
235 for (i = 0; i < mux->data.n_values; i++) {
236 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
237 class = mux->data.classes ? mux->data.classes[i] : 0;
238
239 mux->adap[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, mux,
240 nr, mux->data.values[i],
241 class, i2c_mux_reg_select,
242 deselect);
243 if (!mux->adap[i]) {
244 ret = -ENODEV;
245 dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
246 goto add_adapter_failed;
247 }
248 }
249
250 dev_dbg(&pdev->dev, "%d port mux on %s adapter\n",
251 mux->data.n_values, mux->parent->name);
252
253 return 0;
254
255add_adapter_failed:
256 for (; i > 0; i--)
257 i2c_del_mux_adapter(mux->adap[i - 1]);
258
259 return ret;
260}
261
262static int i2c_mux_reg_remove(struct platform_device *pdev)
263{
264 struct regmux *mux = platform_get_drvdata(pdev);
265 int i;
266
267 for (i = 0; i < mux->data.n_values; i++)
268 i2c_del_mux_adapter(mux->adap[i]);
269
270 i2c_put_adapter(mux->parent);
271
272 return 0;
273}
274
275static const struct of_device_id i2c_mux_reg_of_match[] = {
276 { .compatible = "i2c-mux-reg", },
277 {},
278};
279MODULE_DEVICE_TABLE(of, i2c_mux_reg_of_match);
280
281static struct platform_driver i2c_mux_reg_driver = {
282 .probe = i2c_mux_reg_probe,
283 .remove = i2c_mux_reg_remove,
284 .driver = {
285 .name = "i2c-mux-reg",
286 },
287};
288
289module_platform_driver(i2c_mux_reg_driver);
290
291MODULE_DESCRIPTION("Register-based I2C multiplexer driver");
292MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
293MODULE_LICENSE("GPL");
294MODULE_ALIAS("platform:i2c-mux-reg");
diff --git a/include/linux/platform_data/i2c-mux-reg.h b/include/linux/platform_data/i2c-mux-reg.h
new file mode 100644
index 000000000000..c68712aadf43
--- /dev/null
+++ b/include/linux/platform_data/i2c-mux-reg.h
@@ -0,0 +1,44 @@
1/*
2 * I2C multiplexer using a single register
3 *
4 * Copyright 2015 Freescale Semiconductor
5 * York Sun <yorksun@freescale.com>
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; either version 2 of the License, or (at your
10 * option) any later version.
11 */
12
13#ifndef __LINUX_PLATFORM_DATA_I2C_MUX_REG_H
14#define __LINUX_PLATFORM_DATA_I2C_MUX_REG_H
15
16/**
17 * struct i2c_mux_reg_platform_data - Platform-dependent data for i2c-mux-reg
18 * @parent: Parent I2C bus adapter number
19 * @base_nr: Base I2C bus number to number adapters from or zero for dynamic
20 * @values: Array of value for each channel
21 * @n_values: Number of multiplexer channels
22 * @little_endian: Indicating if the register is in little endian
23 * @write_only: Reading the register is not allowed by hardware
24 * @classes: Optional I2C auto-detection classes
25 * @idle: Value to write to mux when idle
26 * @idle_in_use: indicate if idle value is in use
27 * @reg: Virtual address of the register to switch channel
28 * @reg_size: register size in bytes
29 */
30struct i2c_mux_reg_platform_data {
31 int parent;
32 int base_nr;
33 const unsigned int *values;
34 int n_values;
35 bool little_endian;
36 bool write_only;
37 const unsigned int *classes;
38 u32 idle;
39 bool idle_in_use;
40 void __iomem *reg;
41 resource_size_t reg_size;
42};
43
44#endif /* __LINUX_PLATFORM_DATA_I2C_MUX_REG_H */