1 /* $OpenBSD: pca9548.c,v 1.5 2020/11/14 21:50:51 patrick Exp $ */ 2 3 /* 4 * Copyright (c) 2020 Mark Kettenis 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 23 #include <machine/bus.h> 24 25 #define _I2C_PRIVATE 26 #include <dev/i2c/i2cvar.h> 27 28 #ifdef __HAVE_ACPI 29 #include "acpi.h" 30 #endif 31 32 #if NACPI > 0 33 #include <dev/acpi/acpireg.h> 34 #include <dev/acpi/acpivar.h> 35 #include <dev/acpi/acpidev.h> 36 #include <dev/acpi/amltypes.h> 37 #include <dev/acpi/dsdt.h> 38 #endif 39 40 #include <dev/ofw/openfirm.h> 41 #include <dev/ofw/ofw_misc.h> 42 43 #define PCAMUX_MAX_CHANNELS 8 44 45 struct pcamux_bus { 46 struct pcamux_softc *pb_sc; 47 int pb_node; 48 void *pb_devnode; 49 int pb_channel; 50 struct i2c_controller pb_ic; 51 struct i2c_bus pb_ib; 52 struct device *pb_iic; 53 }; 54 55 struct pcamux_softc { 56 struct device sc_dev; 57 i2c_tag_t sc_tag; 58 i2c_addr_t sc_addr; 59 60 int sc_node; 61 void *sc_devnode; 62 int sc_channel; 63 int sc_nchannel; 64 struct pcamux_bus sc_bus[PCAMUX_MAX_CHANNELS]; 65 struct rwlock sc_lock; 66 67 int sc_switch; 68 int sc_enable; 69 }; 70 71 #if NACPI > 0 72 struct pcamux_crs { 73 uint16_t i2c_addr; 74 struct aml_node *devnode; 75 }; 76 #endif 77 78 int pcamux_match(struct device *, void *, void *); 79 void pcamux_attach(struct device *, struct device *, void *); 80 81 struct cfattach pcamux_ca = { 82 sizeof(struct pcamux_softc), pcamux_match, pcamux_attach 83 }; 84 85 struct cfdriver pcamux_cd = { 86 NULL, "pcamux", DV_DULL 87 }; 88 89 void pcamux_attach_fdt(struct pcamux_softc *, struct i2c_attach_args *); 90 void pcamux_attach_acpi(struct pcamux_softc *, struct i2c_attach_args *); 91 92 #if NACPI > 0 93 int pcamux_attach_acpi_mux(struct aml_node *, void *); 94 void pcamux_acpi_bus_scan(struct device *, 95 struct i2cbus_attach_args *, void *); 96 int pcamux_acpi_found_channel(struct aml_node *, void *); 97 int pcamux_acpi_found_hid(struct aml_node *, void *); 98 int pcamux_acpi_parse_crs(int, union acpi_resource *, void *); 99 #endif 100 101 int pcamux_acquire_bus(void *, int); 102 void pcamux_release_bus(void *, int); 103 int pcamux_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 104 void *, size_t, int); 105 void pcamux_bus_scan(struct device *, struct i2cbus_attach_args *, void *); 106 107 int 108 pcamux_match(struct device *parent, void *match, void *aux) 109 { 110 struct i2c_attach_args *ia = aux; 111 112 if (strcmp(ia->ia_name, "nxp,pca9546") == 0 || 113 strcmp(ia->ia_name, "nxp,pca9547") == 0 || 114 strcmp(ia->ia_name, "nxp,pca9548") == 0 || 115 strcmp(ia->ia_name, "NXP0002") == 0) 116 return (1); 117 return (0); 118 } 119 120 void 121 pcamux_attach(struct device *parent, struct device *self, void *aux) 122 { 123 struct pcamux_softc *sc = (struct pcamux_softc *)self; 124 struct i2c_attach_args *ia = aux; 125 126 sc->sc_tag = ia->ia_tag; 127 sc->sc_addr = ia->ia_addr; 128 129 sc->sc_channel = -1; /* unknown */ 130 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 131 132 if (strcmp(ia->ia_name, "nxp,pca9546") == 0) { 133 sc->sc_switch = 1; 134 sc->sc_nchannel = 4; 135 } else if (strcmp(ia->ia_name, "nxp,pca9547") == 0 || 136 strcmp(ia->ia_name, "NXP0002") == 0) { 137 sc->sc_enable = 1 << 3; 138 sc->sc_nchannel = 8; 139 } else if (strcmp(ia->ia_name, "nxp,pca9548") == 0) { 140 sc->sc_switch = 1; 141 sc->sc_nchannel = 8; 142 } 143 144 printf("\n"); 145 146 if (strcmp(ia->ia_name, "NXP0002") == 0) 147 pcamux_attach_acpi(sc, ia); 148 else 149 pcamux_attach_fdt(sc, ia); 150 } 151 152 void 153 pcamux_attach_fdt(struct pcamux_softc *sc, struct i2c_attach_args *ia) 154 { 155 int node = *(int *)ia->ia_cookie; 156 157 sc->sc_node = node; 158 for (node = OF_child(node); node; node = OF_peer(node)) { 159 struct i2cbus_attach_args iba; 160 struct pcamux_bus *pb; 161 uint32_t channel; 162 163 channel = OF_getpropint(node, "reg", -1); 164 if (channel >= sc->sc_nchannel) 165 continue; 166 167 pb = &sc->sc_bus[channel]; 168 pb->pb_sc = sc; 169 pb->pb_node = node; 170 pb->pb_channel = channel; 171 pb->pb_ic.ic_cookie = pb; 172 pb->pb_ic.ic_acquire_bus = pcamux_acquire_bus; 173 pb->pb_ic.ic_release_bus = pcamux_release_bus; 174 pb->pb_ic.ic_exec = pcamux_exec; 175 176 /* Configure the child busses. */ 177 memset(&iba, 0, sizeof(iba)); 178 iba.iba_name = "iic"; 179 iba.iba_tag = &pb->pb_ic; 180 iba.iba_bus_scan = pcamux_bus_scan; 181 iba.iba_bus_scan_arg = &pb->pb_node; 182 183 config_found(&sc->sc_dev, &iba, iicbus_print); 184 185 pb->pb_ib.ib_node = node; 186 pb->pb_ib.ib_ic = &pb->pb_ic; 187 i2c_register(&pb->pb_ib); 188 } 189 } 190 191 void 192 pcamux_attach_acpi(struct pcamux_softc *sc, struct i2c_attach_args *ia) 193 { 194 #if NACPI > 0 195 struct aml_node *node = ia->ia_cookie; 196 197 sc->sc_devnode = node; 198 aml_walknodes(node, AML_WALK_PRE, pcamux_attach_acpi_mux, sc); 199 #endif 200 } 201 202 #if NACPI > 0 203 int 204 pcamux_attach_acpi_mux(struct aml_node *node, void *arg) 205 { 206 struct pcamux_softc *sc = arg; 207 struct i2cbus_attach_args iba; 208 struct pcamux_bus *pb; 209 uint64_t channel; 210 211 /* Only the node's direct children */ 212 if (node->parent != sc->sc_devnode) 213 return 0; 214 215 /* Must have channel as address */ 216 if (aml_evalinteger(acpi_softc, node, "_ADR", 0, NULL, &channel) || 217 channel >= sc->sc_nchannel) 218 return 0; 219 220 pb = &sc->sc_bus[channel]; 221 pb->pb_sc = sc; 222 pb->pb_devnode = node; 223 pb->pb_channel = channel; 224 pb->pb_ic.ic_cookie = pb; 225 pb->pb_ic.ic_acquire_bus = pcamux_acquire_bus; 226 pb->pb_ic.ic_release_bus = pcamux_release_bus; 227 pb->pb_ic.ic_exec = pcamux_exec; 228 229 /* Configure the child busses. */ 230 memset(&iba, 0, sizeof(iba)); 231 iba.iba_name = "iic"; 232 iba.iba_tag = &pb->pb_ic; 233 iba.iba_bus_scan = pcamux_acpi_bus_scan; 234 iba.iba_bus_scan_arg = pb; 235 236 config_found(&sc->sc_dev, &iba, iicbus_print); 237 238 #ifndef SMALL_KERNEL 239 node->i2c = &pb->pb_ic; 240 acpi_register_gsb(acpi_softc, node); 241 #endif 242 return 0; 243 } 244 245 void 246 pcamux_acpi_bus_scan(struct device *iic, struct i2cbus_attach_args *iba, 247 void *aux) 248 { 249 struct pcamux_bus *pb = aux; 250 251 pb->pb_iic = iic; 252 aml_find_node(pb->pb_devnode, "_HID", pcamux_acpi_found_hid, aux); 253 } 254 255 int 256 pcamux_acpi_found_hid(struct aml_node *node, void *arg) 257 { 258 struct pcamux_bus *pb = arg; 259 struct pcamux_softc *sc = pb->pb_sc; 260 struct pcamux_crs crs; 261 struct aml_value res; 262 int64_t sta; 263 char cdev[16], dev[16]; 264 struct i2c_attach_args ia; 265 266 /* Skip our own _HID. */ 267 if (node->parent == pb->pb_devnode) 268 return 0; 269 270 /* Only direct descendants, because of possible muxes. */ 271 if (node->parent && node->parent->parent != pb->pb_devnode) 272 return 0; 273 274 if (acpi_parsehid(node, arg, cdev, dev, 16) != 0) 275 return 0; 276 277 sta = acpi_getsta(acpi_softc, node->parent); 278 if ((sta & STA_PRESENT) == 0) 279 return 0; 280 281 if (aml_evalname(acpi_softc, node->parent, "_CRS", 0, NULL, &res)) 282 return 0; 283 284 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { 285 printf("%s: invalid _CRS object (type %d len %d)\n", 286 sc->sc_dev.dv_xname, res.type, res.length); 287 aml_freevalue(&res); 288 return (0); 289 } 290 memset(&crs, 0, sizeof(crs)); 291 crs.devnode = sc->sc_devnode; 292 aml_parse_resource(&res, pcamux_acpi_parse_crs, &crs); 293 aml_freevalue(&res); 294 295 acpi_attach_deps(acpi_softc, node->parent); 296 297 memset(&ia, 0, sizeof(ia)); 298 ia.ia_tag = &pb->pb_ic; 299 ia.ia_name = dev; 300 ia.ia_addr = crs.i2c_addr; 301 ia.ia_cookie = node->parent; 302 303 config_found(pb->pb_iic, &ia, iic_print); 304 node->parent->attached = 1; 305 306 return 0; 307 } 308 309 int 310 pcamux_acpi_parse_crs(int crsidx, union acpi_resource *crs, void *arg) 311 { 312 struct pcamux_crs *sc_crs = arg; 313 314 switch (AML_CRSTYPE(crs)) { 315 case LR_SERBUS: 316 if (crs->lr_serbus.type == LR_SERBUS_I2C) 317 sc_crs->i2c_addr = crs->lr_i2cbus._adr; 318 break; 319 320 default: 321 printf("%s: unknown resource type %d\n", __func__, 322 AML_CRSTYPE(crs)); 323 } 324 325 return 0; 326 } 327 #endif 328 329 int 330 pcamux_set_channel(struct pcamux_softc *sc, int channel, int flags) 331 { 332 uint8_t data; 333 int error; 334 335 if (channel < -1 || channel >= sc->sc_nchannel) 336 return ENXIO; 337 338 if (sc->sc_channel == channel) 339 return 0; 340 341 data = 0; 342 if (channel != -1) { 343 if (sc->sc_switch) 344 data = 1 << channel; 345 else 346 data = sc->sc_enable | channel; 347 } 348 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 349 sc->sc_addr, NULL, 0, &data, sizeof data, flags); 350 351 return error; 352 } 353 354 int 355 pcamux_acquire_bus(void *cookie, int flags) 356 { 357 struct pcamux_bus *pb = cookie; 358 struct pcamux_softc *sc = pb->pb_sc; 359 int rwflags = RW_WRITE; 360 int error; 361 362 if (flags & I2C_F_POLL) 363 rwflags |= RW_NOSLEEP; 364 365 error = rw_enter(&sc->sc_lock, rwflags); 366 if (error) 367 return error; 368 369 /* Acquire parent bus. */ 370 error = iic_acquire_bus(sc->sc_tag, flags); 371 if (error) { 372 rw_exit_write(&sc->sc_lock); 373 return error; 374 } 375 376 error = pcamux_set_channel(sc, pb->pb_channel, flags); 377 if (error) { 378 iic_release_bus(sc->sc_tag, flags); 379 rw_exit_write(&sc->sc_lock); 380 return error; 381 } 382 383 return 0; 384 } 385 386 void 387 pcamux_release_bus(void *cookie, int flags) 388 { 389 struct pcamux_bus *pb = cookie; 390 struct pcamux_softc *sc = pb->pb_sc; 391 392 /* Release parent bus. */ 393 iic_release_bus(sc->sc_tag, flags); 394 rw_exit_write(&sc->sc_lock); 395 } 396 397 int 398 pcamux_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 399 size_t cmdlen, void *buf, size_t buflen, int flags) 400 { 401 struct pcamux_bus *pb = cookie; 402 struct pcamux_softc *sc = pb->pb_sc; 403 404 rw_assert_wrlock(&sc->sc_lock); 405 406 /* Issue the transaction on the parent bus. */ 407 return iic_exec(sc->sc_tag, op, addr, cmd, cmdlen, buf, buflen, flags); 408 } 409 410 void 411 pcamux_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) 412 { 413 int iba_node = *(int *)arg; 414 struct i2c_attach_args ia; 415 char name[32]; 416 uint32_t reg[1]; 417 int node; 418 419 for (node = OF_child(iba_node); node; node = OF_peer(node)) { 420 memset(name, 0, sizeof(name)); 421 memset(reg, 0, sizeof(reg)); 422 423 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 424 continue; 425 if (name[0] == '\0') 426 continue; 427 428 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 429 continue; 430 431 memset(&ia, 0, sizeof(ia)); 432 ia.ia_tag = iba->iba_tag; 433 ia.ia_addr = bemtoh32(®[0]); 434 ia.ia_name = name; 435 ia.ia_cookie = &node; 436 config_found(self, &ia, iic_print); 437 } 438 } 439