| @@ -1,228 +1,228 @@ | | | @@ -1,228 +1,228 @@ |
1 | /* $NetBSD: i2cmux.c,v 1.1 2020/12/28 20:29:57 thorpej Exp $ */ | | 1 | /* $NetBSD: i2cmux.c,v 1.2 2021/01/24 19:35:21 jmcneill Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2020 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2020 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe. | | 8 | * by Jason R. Thorpe. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. | | 29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #include <sys/cdefs.h> | | 32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: i2cmux.c,v 1.1 2020/12/28 20:29:57 thorpej Exp $"); | | 33 | __KERNEL_RCSID(0, "$NetBSD: i2cmux.c,v 1.2 2021/01/24 19:35:21 jmcneill Exp $"); |
34 | | | 34 | |
35 | #include <sys/types.h> | | 35 | #include <sys/types.h> |
36 | #include <sys/device.h> | | 36 | #include <sys/device.h> |
37 | #include <sys/kernel.h> | | 37 | #include <sys/kernel.h> |
38 | #include <sys/kmem.h> | | 38 | #include <sys/kmem.h> |
39 | | | 39 | |
40 | #include <dev/fdt/fdtvar.h> | | 40 | #include <dev/fdt/fdtvar.h> |
41 | #include <dev/i2c/i2cvar.h> | | 41 | #include <dev/i2c/i2cvar.h> |
42 | #include <dev/i2c/i2cmuxvar.h> | | 42 | #include <dev/i2c/i2cmuxvar.h> |
43 | | | 43 | |
44 | /* | | 44 | /* |
45 | * i2c mux | | 45 | * i2c mux |
46 | * | | 46 | * |
47 | * This works by interposing a set of virtual controllers behind the real | | 47 | * This works by interposing a set of virtual controllers behind the real |
48 | * i2c controller. We provide our own acquire and release functions that | | 48 | * i2c controller. We provide our own acquire and release functions that |
49 | * perform the following tasks: | | 49 | * perform the following tasks: |
50 | * | | 50 | * |
51 | * acquire -> acquire parent controller, program mux | | 51 | * acquire -> acquire parent controller, program mux |
52 | * | | 52 | * |
53 | * release -> idle mux, release parent controller | | 53 | * release -> idle mux, release parent controller |
54 | * | | 54 | * |
55 | * All of the actual I/O operations are transparently passed through. | | 55 | * All of the actual I/O operations are transparently passed through. |
56 | * | | 56 | * |
57 | * N.B. the locking order; the generic I2C layer has already acquired | | 57 | * N.B. the locking order; the generic I2C layer has already acquired |
58 | * our virtual controller's mutex before calling our acquire function, | | 58 | * our virtual controller's mutex before calling our acquire function, |
59 | * and we will then acquire the real controller's mutex when we acquire | | 59 | * and we will then acquire the real controller's mutex when we acquire |
60 | * the bus, so the order is: | | 60 | * the bus, so the order is: |
61 | * | | 61 | * |
62 | * mux virtual controller -> parent controller | | 62 | * mux virtual controller -> parent controller |
63 | * | | 63 | * |
64 | * These are common routines used by various i2c mux controller | | 64 | * These are common routines used by various i2c mux controller |
65 | * implementations (gpio, pin mux, i2c device, etc.). | | 65 | * implementations (gpio, pin mux, i2c device, etc.). |
66 | */ | | 66 | */ |
67 | | | 67 | |
68 | /*****************************************************************************/ | | 68 | /*****************************************************************************/ |
69 | | | 69 | |
70 | static int | | 70 | static int |
71 | iicmux_acquire_bus(void * const v, int const flags) | | 71 | iicmux_acquire_bus(void * const v, int const flags) |
72 | { | | 72 | { |
73 | struct iicmux_bus * const bus = v; | | 73 | struct iicmux_bus * const bus = v; |
74 | struct iicmux_softc * const sc = bus->mux; | | 74 | struct iicmux_softc * const sc = bus->mux; |
75 | int error; | | 75 | int error; |
76 | | | 76 | |
77 | error = iic_acquire_bus(sc->sc_i2c_parent, flags); | | 77 | error = iic_acquire_bus(sc->sc_i2c_parent, flags); |
78 | if (error) { | | 78 | if (error) { |
79 | return error; | | 79 | return error; |
80 | } | | 80 | } |
81 | | | 81 | |
82 | error = sc->sc_config->acquire_bus(bus, flags); | | 82 | error = sc->sc_config->acquire_bus(bus, flags); |
83 | if (error) { | | 83 | if (error) { |
84 | iic_release_bus(sc->sc_i2c_parent, flags); | | 84 | iic_release_bus(sc->sc_i2c_parent, flags); |
85 | } | | 85 | } |
86 | | | 86 | |
87 | return error; | | 87 | return error; |
88 | } | | 88 | } |
89 | | | 89 | |
90 | static void | | 90 | static void |
91 | iicmux_release_bus(void * const v, int const flags) | | 91 | iicmux_release_bus(void * const v, int const flags) |
92 | { | | 92 | { |
93 | struct iicmux_bus * const bus = v; | | 93 | struct iicmux_bus * const bus = v; |
94 | struct iicmux_softc * const sc = bus->mux; | | 94 | struct iicmux_softc * const sc = bus->mux; |
95 | | | 95 | |
96 | sc->sc_config->release_bus(bus, flags); | | 96 | sc->sc_config->release_bus(bus, flags); |
97 | iic_release_bus(sc->sc_i2c_parent, flags); | | 97 | iic_release_bus(sc->sc_i2c_parent, flags); |
98 | } | | 98 | } |
99 | | | 99 | |
100 | static int | | 100 | static int |
101 | iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr, | | 101 | iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr, |
102 | const void * const cmdbuf, size_t const cmdlen, void * const databuf, | | 102 | const void * const cmdbuf, size_t const cmdlen, void * const databuf, |
103 | size_t const datalen, int const flags) | | 103 | size_t const datalen, int const flags) |
104 | { | | 104 | { |
105 | struct iicmux_bus * const bus = v; | | 105 | struct iicmux_bus * const bus = v; |
106 | struct iicmux_softc * const sc = bus->mux; | | 106 | struct iicmux_softc * const sc = bus->mux; |
107 | | | 107 | |
108 | return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen, | | 108 | return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen, |
109 | databuf, datalen, flags); | | 109 | databuf, datalen, flags); |
110 | } | | 110 | } |
111 | | | 111 | |
112 | /*****************************************************************************/ | | 112 | /*****************************************************************************/ |
113 | | | 113 | |
114 | static int | | 114 | static int |
115 | iicmux_count_children(struct iicmux_softc * const sc) | | 115 | iicmux_count_children(struct iicmux_softc * const sc) |
116 | { | | 116 | { |
117 | char name[32]; | | 117 | char name[32]; |
118 | int child, count; | | 118 | int child, count; |
119 | | | 119 | |
120 | restart: | | 120 | restart: |
121 | for (child = OF_child(sc->sc_i2c_mux_phandle), count = 0; child; | | 121 | for (child = OF_child(sc->sc_i2c_mux_phandle), count = 0; child; |
122 | child = OF_peer(child)) { | | 122 | child = OF_peer(child)) { |
123 | if (OF_getprop(child, "name", name, sizeof(name)) <= 0) { | | 123 | if (OF_getprop(child, "name", name, sizeof(name)) <= 0) { |
124 | continue; | | 124 | continue; |
125 | } | | 125 | } |
126 | if (strcmp(name, "i2c-mux") == 0) { | | 126 | if (strcmp(name, "i2c-mux") == 0) { |
127 | /* | | 127 | /* |
128 | * The node we encountered is the acutal parent | | 128 | * The node we encountered is the acutal parent |
129 | * of the i2c bus children. Stash its phandle | | 129 | * of the i2c bus children. Stash its phandle |
130 | * and restart the enumeration. | | 130 | * and restart the enumeration. |
131 | */ | | 131 | */ |
132 | sc->sc_i2c_mux_phandle = child; | | 132 | sc->sc_i2c_mux_phandle = child; |
133 | goto restart; | | 133 | goto restart; |
134 | } | | 134 | } |
135 | count++; | | 135 | count++; |
136 | } | | 136 | } |
137 | | | 137 | |
138 | return count; | | 138 | return count; |
139 | } | | 139 | } |
140 | | | 140 | |
141 | /* XXX iicbus_print() should be able to do this. */ | | 141 | /* XXX iicbus_print() should be able to do this. */ |
142 | static int | | 142 | static int |
143 | iicmux_print(void * const aux, const char * const pnp) | | 143 | iicmux_print(void * const aux, const char * const pnp) |
144 | { | | 144 | { |
145 | i2c_tag_t const tag = aux; | | 145 | i2c_tag_t const tag = aux; |
146 | struct iicmux_bus * const bus = tag->ic_cookie; | | 146 | struct iicmux_bus * const bus = tag->ic_cookie; |
147 | int rv; | | 147 | int rv; |
148 | | | 148 | |
149 | rv = iicbus_print(aux, pnp); | | 149 | rv = iicbus_print(aux, pnp); |
150 | aprint_normal(" bus %d", bus->busidx); | | 150 | aprint_normal(" bus %d", bus->busidx); |
151 | | | 151 | |
152 | return rv; | | 152 | return rv; |
153 | } | | 153 | } |
154 | | | 154 | |
155 | static void | | 155 | static void |
156 | iicmux_attach_bus(struct iicmux_softc * const sc, | | 156 | iicmux_attach_bus(struct iicmux_softc * const sc, |
157 | int const phandle, int const busidx) | | 157 | int const phandle, int const busidx) |
158 | { | | 158 | { |
159 | struct iicmux_bus * const bus = &sc->sc_busses[busidx]; | | 159 | struct iicmux_bus * const bus = &sc->sc_busses[busidx]; |
160 | | | 160 | |
161 | bus->mux = sc; | | 161 | bus->mux = sc; |
162 | bus->busidx = busidx; | | 162 | bus->busidx = busidx; |
163 | bus->phandle = phandle; | | 163 | bus->phandle = phandle; |
164 | | | 164 | |
165 | bus->bus_data = sc->sc_config->get_bus_info(bus); | | 165 | bus->bus_data = sc->sc_config->get_bus_info(bus); |
166 | if (bus->bus_data == NULL) { | | 166 | if (bus->bus_data == NULL) { |
167 | aprint_error_dev(sc->sc_dev, | | 167 | aprint_error_dev(sc->sc_dev, |
168 | "unable to get info for bus %d\n", busidx); | | 168 | "unable to get info for bus %d\n", busidx); |
169 | return; | | 169 | return; |
170 | } | | 170 | } |
171 | | | 171 | |
172 | iic_tag_init(&bus->controller); | | 172 | iic_tag_init(&bus->controller); |
173 | bus->controller.ic_cookie = bus; | | 173 | bus->controller.ic_cookie = bus; |
174 | bus->controller.ic_acquire_bus = iicmux_acquire_bus; | | 174 | bus->controller.ic_acquire_bus = iicmux_acquire_bus; |
175 | bus->controller.ic_release_bus = iicmux_release_bus; | | 175 | bus->controller.ic_release_bus = iicmux_release_bus; |
176 | bus->controller.ic_exec = iicmux_exec; | | 176 | bus->controller.ic_exec = iicmux_exec; |
177 | | | 177 | |
178 | fdtbus_register_i2c_controller(&bus->controller, bus->phandle); | | 178 | fdtbus_register_i2c_controller(&bus->controller, bus->phandle); |
179 | | | 179 | |
180 | fdtbus_attach_i2cbus(sc->sc_dev, bus->phandle, &bus->controller, | | 180 | fdtbus_attach_i2cbus(sc->sc_dev, bus->phandle, &bus->controller, |
181 | iicmux_print); | | 181 | iicmux_print); |
182 | } | | 182 | } |
183 | | | 183 | |
184 | void | | 184 | void |
185 | iicmux_attach(struct iicmux_softc * const sc) | | 185 | iicmux_attach(struct iicmux_softc * const sc) |
186 | { | | 186 | { |
187 | | | 187 | |
188 | /* | | 188 | /* |
189 | * We expect sc->sc_phandle, sc->sc_config, and sc->sc_i2c_parent | | 189 | * We expect sc->sc_phandle, sc->sc_config, and sc->sc_i2c_parent |
190 | * to be initialized by the front-end. | | 190 | * to be initialized by the front-end. |
191 | */ | | 191 | */ |
192 | KASSERT(sc->sc_phandle > 0); | | 192 | KASSERT(sc->sc_phandle > 0); |
193 | KASSERT(sc->sc_config != NULL); | | 193 | KASSERT(sc->sc_config != NULL); |
194 | KASSERT(sc->sc_i2c_parent != NULL); | | 194 | KASSERT(sc->sc_i2c_parent != NULL); |
195 | | | 195 | |
196 | /* | | 196 | /* |
197 | * We start out assuming that the i2c bus nodes are children of | | 197 | * We start out assuming that the i2c bus nodes are children of |
198 | * our own node. We'll adjust later if we encounter an "i2c-mux" | | 198 | * our own node. We'll adjust later if we encounter an "i2c-mux" |
199 | * node when counting our children. If we encounter such a node, | | 199 | * node when counting our children. If we encounter such a node, |
200 | * then it's that node that is the parent of the i2c bus children. | | 200 | * then it's that node that is the parent of the i2c bus children. |
201 | */ | | 201 | */ |
202 | sc->sc_i2c_mux_phandle = sc->sc_phandle; | | 202 | sc->sc_i2c_mux_phandle = sc->sc_phandle; |
203 | | | 203 | |
204 | /* | | 204 | /* |
205 | * Gather up all of the various bits of information needed | | 205 | * Gather up all of the various bits of information needed |
206 | * for this particular type of i2c mux. | | 206 | * for this particular type of i2c mux. |
207 | */ | | 207 | */ |
208 | sc->sc_mux_data = sc->sc_config->get_mux_info(sc); | | 208 | sc->sc_mux_data = sc->sc_config->get_mux_info(sc); |
209 | if (sc->sc_mux_data == NULL) { | | 209 | if (sc->sc_mux_data == NULL) { |
210 | aprint_error_dev(sc->sc_dev, "unable to get info for mux\n"); | | 210 | aprint_error_dev(sc->sc_dev, "unable to get info for mux\n"); |
211 | return; | | 211 | return; |
212 | } | | 212 | } |
213 | | | 213 | |
214 | sc->sc_nbusses = iicmux_count_children(sc); | | 214 | sc->sc_nbusses = iicmux_count_children(sc); |
215 | if (sc->sc_nbusses == 0) { | | 215 | if (sc->sc_nbusses == 0) { |
216 | return; | | 216 | return; |
217 | } | | 217 | } |
218 | | | 218 | |
219 | sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses, | | 219 | sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses, |
220 | KM_SLEEP); | | 220 | KM_SLEEP); |
221 | | | 221 | |
222 | int child, idx; | | 222 | int child, idx; |
223 | for (child = OF_child(sc->sc_i2c_mux_phandle), idx = 0; child; | | 223 | for (child = OF_child(sc->sc_i2c_mux_phandle), idx = 0; child; |
224 | child = OF_peer(child), idx++) { | | 224 | child = OF_peer(child), idx++) { |
225 | KASSERT(idx < sc->sc_nbusses); | | 225 | KASSERT(idx < sc->sc_nbusses); |
226 | iicmux_attach_bus(sc, child, idx); | | 226 | iicmux_attach_bus(sc, child, idx); |
227 | } | | 227 | } |
228 | } | | 228 | } |