1 /* $OpenBSD: iicmux.c,v 1.2 2022/04/06 18:59:28 naddy Exp $ */ 2 /* 3 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/malloc.h> 21 #include <sys/device.h> 22 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #define _I2C_PRIVATE 27 #include <dev/i2c/i2cvar.h> 28 29 #include <dev/ofw/openfirm.h> 30 #include <dev/ofw/ofw_misc.h> 31 #include <dev/ofw/ofw_pinctrl.h> 32 33 #define IICMUX_MAX_CHANNELS 4 34 35 struct iicmux_bus { 36 struct iicmux_softc *pb_sc; 37 int pb_node; 38 int pb_channel; 39 struct i2c_controller pb_ic; 40 struct i2c_bus pb_ib; 41 struct device *pb_iic; 42 }; 43 44 struct iicmux_softc { 45 struct device sc_dev; 46 i2c_tag_t sc_tag; 47 48 int sc_node; 49 int sc_channel; 50 int sc_nchannel; 51 struct iicmux_bus sc_bus[IICMUX_MAX_CHANNELS]; 52 const char *sc_busnames[IICMUX_MAX_CHANNELS]; 53 struct rwlock sc_lock; 54 55 int sc_enable; 56 }; 57 58 int iicmux_match(struct device *, void *, void *); 59 void iicmux_attach(struct device *, struct device *, void *); 60 61 const struct cfattach iicmux_ca = { 62 sizeof(struct iicmux_softc), iicmux_match, iicmux_attach 63 }; 64 65 struct cfdriver iicmux_cd = { 66 NULL, "iicmux", DV_DULL 67 }; 68 69 int iicmux_acquire_bus(void *, int); 70 void iicmux_release_bus(void *, int); 71 int iicmux_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 72 void *, size_t, int); 73 void iicmux_bus_scan(struct device *, struct i2cbus_attach_args *, void *); 74 75 int 76 iicmux_match(struct device *parent, void *match, void *aux) 77 { 78 struct fdt_attach_args *faa = aux; 79 80 return OF_is_compatible(faa->fa_node, "i2c-mux-pinctrl"); 81 } 82 83 void 84 iicmux_attach(struct device *parent, struct device *self, void *aux) 85 { 86 struct iicmux_softc *sc = (struct iicmux_softc *)self; 87 struct fdt_attach_args *faa = aux; 88 int node = faa->fa_node; 89 uint32_t phandle; 90 char *names; 91 char *name; 92 char *end; 93 int len; 94 95 sc->sc_channel = -1; /* unknown */ 96 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 97 98 phandle = OF_getpropint(node, "i2c-parent", 0); 99 sc->sc_tag = i2c_byphandle(phandle); 100 if (sc->sc_tag == NULL) { 101 printf(": can't find parent bus\n"); 102 return; 103 } 104 105 len = OF_getproplen(node, "pinctrl-names"); 106 if (len <= 0) { 107 printf(": no channels\n"); 108 return; 109 } 110 111 names = malloc(len, M_DEVBUF, M_WAITOK); 112 OF_getprop(node, "pinctrl-names", names, len); 113 end = names + len; 114 name = names; 115 while (name < end) { 116 if (strcmp(name, "idle") == 0) 117 break; 118 119 if (sc->sc_nchannel >= IICMUX_MAX_CHANNELS) { 120 printf(": too many channels\n"); 121 free(names, M_DEVBUF, len); 122 return; 123 } 124 125 sc->sc_busnames[sc->sc_nchannel] = name; 126 name += strlen(name) + 1; 127 sc->sc_nchannel++; 128 } 129 130 printf("\n"); 131 132 sc->sc_node = node; 133 for (node = OF_child(node); node; node = OF_peer(node)) { 134 struct i2cbus_attach_args iba; 135 struct iicmux_bus *pb; 136 uint32_t channel; 137 138 channel = OF_getpropint(node, "reg", -1); 139 if (channel >= sc->sc_nchannel) 140 continue; 141 142 pb = &sc->sc_bus[channel]; 143 pb->pb_sc = sc; 144 pb->pb_node = node; 145 pb->pb_channel = channel; 146 pb->pb_ic.ic_cookie = pb; 147 pb->pb_ic.ic_acquire_bus = iicmux_acquire_bus; 148 pb->pb_ic.ic_release_bus = iicmux_release_bus; 149 pb->pb_ic.ic_exec = iicmux_exec; 150 151 /* Configure the child busses. */ 152 memset(&iba, 0, sizeof(iba)); 153 iba.iba_name = "iic"; 154 iba.iba_tag = &pb->pb_ic; 155 iba.iba_bus_scan = iicmux_bus_scan; 156 iba.iba_bus_scan_arg = &pb->pb_node; 157 158 config_found(&sc->sc_dev, &iba, iicbus_print); 159 160 pb->pb_ib.ib_node = node; 161 pb->pb_ib.ib_ic = &pb->pb_ic; 162 i2c_register(&pb->pb_ib); 163 } 164 } 165 166 int 167 iicmux_set_channel(struct iicmux_softc *sc, int channel, int flags) 168 { 169 int error; 170 171 if (channel < -1 || channel >= sc->sc_nchannel) 172 return ENXIO; 173 174 if (sc->sc_channel == channel) 175 return 0; 176 177 error = pinctrl_byname(sc->sc_node, sc->sc_busnames[channel]); 178 if (error == 0) 179 sc->sc_channel = channel; 180 181 return error ? EIO : 0; 182 } 183 184 int 185 iicmux_acquire_bus(void *cookie, int flags) 186 { 187 struct iicmux_bus *pb = cookie; 188 struct iicmux_softc *sc = pb->pb_sc; 189 int rwflags = RW_WRITE; 190 int error; 191 192 if (flags & I2C_F_POLL) 193 rwflags |= RW_NOSLEEP; 194 195 error = rw_enter(&sc->sc_lock, rwflags); 196 if (error) 197 return error; 198 199 /* Acquire parent bus. */ 200 error = iic_acquire_bus(sc->sc_tag, flags); 201 if (error) { 202 rw_exit_write(&sc->sc_lock); 203 return error; 204 } 205 206 error = iicmux_set_channel(sc, pb->pb_channel, flags); 207 if (error) { 208 iic_release_bus(sc->sc_tag, flags); 209 rw_exit_write(&sc->sc_lock); 210 return error; 211 } 212 213 return 0; 214 } 215 216 void 217 iicmux_release_bus(void *cookie, int flags) 218 { 219 struct iicmux_bus *pb = cookie; 220 struct iicmux_softc *sc = pb->pb_sc; 221 222 if (pinctrl_byname(sc->sc_node, "idle") == 0) 223 sc->sc_channel = -1; 224 225 /* Release parent bus. */ 226 iic_release_bus(sc->sc_tag, flags); 227 rw_exit_write(&sc->sc_lock); 228 } 229 230 int 231 iicmux_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 232 size_t cmdlen, void *buf, size_t buflen, int flags) 233 { 234 struct iicmux_bus *pb = cookie; 235 struct iicmux_softc *sc = pb->pb_sc; 236 237 rw_assert_wrlock(&sc->sc_lock); 238 239 /* Issue the transaction on the parent bus. */ 240 return iic_exec(sc->sc_tag, op, addr, cmd, cmdlen, buf, buflen, flags); 241 } 242 243 void 244 iicmux_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) 245 { 246 int iba_node = *(int *)arg; 247 struct i2c_attach_args ia; 248 char name[32]; 249 uint32_t reg[1]; 250 int node; 251 252 for (node = OF_child(iba_node); node; node = OF_peer(node)) { 253 memset(name, 0, sizeof(name)); 254 memset(reg, 0, sizeof(reg)); 255 256 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 257 continue; 258 if (name[0] == '\0') 259 continue; 260 261 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 262 continue; 263 264 memset(&ia, 0, sizeof(ia)); 265 ia.ia_tag = iba->iba_tag; 266 ia.ia_addr = bemtoh32(®[0]); 267 ia.ia_name = name; 268 ia.ia_cookie = &node; 269 config_found(self, &ia, iic_print); 270 } 271 } 272