1/* $OpenBSD: pca9548.c,v 1.7 2024/05/13 01:15:50 jsg 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 45struct 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 55struct 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 72struct pcamux_crs { 73 uint16_t i2c_addr; 74 struct aml_node *devnode; 75}; 76#endif 77 78int pcamux_match(struct device *, void *, void *); 79void pcamux_attach(struct device *, struct device *, void *); 80 81const struct cfattach pcamux_ca = { 82 sizeof(struct pcamux_softc), pcamux_match, pcamux_attach 83}; 84 85struct cfdriver pcamux_cd = { 86 NULL, "pcamux", DV_DULL 87}; 88 89void pcamux_attach_fdt(struct pcamux_softc *, struct i2c_attach_args *); 90void pcamux_attach_acpi(struct pcamux_softc *, struct i2c_attach_args *); 91 92#if NACPI > 0 93int pcamux_attach_acpi_mux(struct aml_node *, void *); 94void pcamux_acpi_bus_scan(struct device *, 95 struct i2cbus_attach_args *, void *); 96int pcamux_acpi_found_hid(struct aml_node *, void *); 97int pcamux_acpi_parse_crs(int, union acpi_resource *, void *); 98#endif 99 100int pcamux_acquire_bus(void *, int); 101void pcamux_release_bus(void *, int); 102int pcamux_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 103 void *, size_t, int); 104void pcamux_bus_scan(struct device *, struct i2cbus_attach_args *, void *); 105 106int 107pcamux_match(struct device *parent, void *match, void *aux) 108{ 109 struct i2c_attach_args *ia = aux; 110 111 if (strcmp(ia->ia_name, "nxp,pca9546") == 0 || 112 strcmp(ia->ia_name, "nxp,pca9547") == 0 || 113 strcmp(ia->ia_name, "nxp,pca9548") == 0 || 114 strcmp(ia->ia_name, "NXP0002") == 0) 115 return (1); 116 return (0); 117} 118 119void 120pcamux_attach(struct device *parent, struct device *self, void *aux) 121{ 122 struct pcamux_softc *sc = (struct pcamux_softc *)self; 123 struct i2c_attach_args *ia = aux; 124 125 sc->sc_tag = ia->ia_tag; 126 sc->sc_addr = ia->ia_addr; 127 128 sc->sc_channel = -1; /* unknown */ 129 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 130 131 if (strcmp(ia->ia_name, "nxp,pca9546") == 0) { 132 sc->sc_switch = 1; 133 sc->sc_nchannel = 4; 134 } else if (strcmp(ia->ia_name, "nxp,pca9547") == 0 || 135 strcmp(ia->ia_name, "NXP0002") == 0) { 136 sc->sc_enable = 1 << 3; 137 sc->sc_nchannel = 8; 138 } else if (strcmp(ia->ia_name, "nxp,pca9548") == 0) { 139 sc->sc_switch = 1; 140 sc->sc_nchannel = 8; 141 } 142 143 printf("\n"); 144 145 if (strcmp(ia->ia_name, "NXP0002") == 0) 146 pcamux_attach_acpi(sc, ia); 147 else 148 pcamux_attach_fdt(sc, ia); 149} 150 151void 152pcamux_attach_fdt(struct pcamux_softc *sc, struct i2c_attach_args *ia) 153{ 154 int node = *(int *)ia->ia_cookie; 155 156 sc->sc_node = node; 157 for (node = OF_child(node); node; node = OF_peer(node)) { 158 struct i2cbus_attach_args iba; 159 struct pcamux_bus *pb; 160 uint32_t channel; 161 162 channel = OF_getpropint(node, "reg", -1); 163 if (channel >= sc->sc_nchannel) 164 continue; 165 166 pb = &sc->sc_bus[channel]; 167 pb->pb_sc = sc; 168 pb->pb_node = node; 169 pb->pb_channel = channel; 170 pb->pb_ic.ic_cookie = pb; 171 pb->pb_ic.ic_acquire_bus = pcamux_acquire_bus; 172 pb->pb_ic.ic_release_bus = pcamux_release_bus; 173 pb->pb_ic.ic_exec = pcamux_exec; 174 175 /* Configure the child busses. */ 176 memset(&iba, 0, sizeof(iba)); 177 iba.iba_name = "iic"; 178 iba.iba_tag = &pb->pb_ic; 179 iba.iba_bus_scan = pcamux_bus_scan; 180 iba.iba_bus_scan_arg = &pb->pb_node; 181 182 config_found(&sc->sc_dev, &iba, iicbus_print); 183 184 pb->pb_ib.ib_node = node; 185 pb->pb_ib.ib_ic = &pb->pb_ic; 186 i2c_register(&pb->pb_ib); 187 } 188} 189 190void 191pcamux_attach_acpi(struct pcamux_softc *sc, struct i2c_attach_args *ia) 192{ 193#if NACPI > 0 194 struct aml_node *node = ia->ia_cookie; 195 196 sc->sc_devnode = node; 197 aml_walknodes(node, AML_WALK_PRE, pcamux_attach_acpi_mux, sc); 198#endif 199} 200 201#if NACPI > 0 202int 203pcamux_attach_acpi_mux(struct aml_node *node, void *arg) 204{ 205 struct pcamux_softc *sc = arg; 206 struct i2cbus_attach_args iba; 207 struct pcamux_bus *pb; 208 uint64_t channel; 209 210 /* Only the node's direct children */ 211 if (node->parent != sc->sc_devnode) 212 return 0; 213 214 /* Must have channel as address */ 215 if (aml_evalinteger(acpi_softc, node, "_ADR", 0, NULL, &channel) || 216 channel >= sc->sc_nchannel) 217 return 0; 218 219 pb = &sc->sc_bus[channel]; 220 pb->pb_sc = sc; 221 pb->pb_devnode = node; 222 pb->pb_channel = channel; 223 pb->pb_ic.ic_cookie = pb; 224 pb->pb_ic.ic_acquire_bus = pcamux_acquire_bus; 225 pb->pb_ic.ic_release_bus = pcamux_release_bus; 226 pb->pb_ic.ic_exec = pcamux_exec; 227 228 /* Configure the child busses. */ 229 memset(&iba, 0, sizeof(iba)); 230 iba.iba_name = "iic"; 231 iba.iba_tag = &pb->pb_ic; 232 iba.iba_bus_scan = pcamux_acpi_bus_scan; 233 iba.iba_bus_scan_arg = pb; 234 235 config_found(&sc->sc_dev, &iba, iicbus_print); 236 237#ifndef SMALL_KERNEL 238 node->i2c = &pb->pb_ic; 239 acpi_register_gsb(acpi_softc, node); 240#endif 241 return 0; 242} 243 244void 245pcamux_acpi_bus_scan(struct device *iic, struct i2cbus_attach_args *iba, 246 void *aux) 247{ 248 struct pcamux_bus *pb = aux; 249 250 pb->pb_iic = iic; 251 aml_find_node(pb->pb_devnode, "_HID", pcamux_acpi_found_hid, aux); 252} 253 254int 255pcamux_acpi_found_hid(struct aml_node *node, void *arg) 256{ 257 struct pcamux_bus *pb = arg; 258 struct pcamux_softc *sc = pb->pb_sc; 259 struct pcamux_crs crs; 260 struct aml_value res; 261 int64_t sta; 262 char cdev[16], dev[16]; 263 struct i2c_attach_args ia; 264 265 /* Skip our own _HID. */ 266 if (node->parent == pb->pb_devnode) 267 return 0; 268 269 /* Only direct descendants, because of possible muxes. */ 270 if (node->parent && node->parent->parent != pb->pb_devnode) 271 return 0; 272 273 if (acpi_parsehid(node, arg, cdev, dev, 16) != 0) 274 return 0; 275 276 sta = acpi_getsta(acpi_softc, node->parent); 277 if ((sta & STA_PRESENT) == 0) 278 return 0; 279 280 if (aml_evalname(acpi_softc, node->parent, "_CRS", 0, NULL, &res)) 281 return 0; 282 283 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { 284 printf("%s: invalid _CRS object (type %d len %d)\n", 285 sc->sc_dev.dv_xname, res.type, res.length); 286 aml_freevalue(&res); 287 return (0); 288 } 289 memset(&crs, 0, sizeof(crs)); 290 crs.devnode = sc->sc_devnode; 291 aml_parse_resource(&res, pcamux_acpi_parse_crs, &crs); 292 aml_freevalue(&res); 293 294 acpi_attach_deps(acpi_softc, node->parent); 295 296 memset(&ia, 0, sizeof(ia)); 297 ia.ia_tag = &pb->pb_ic; 298 ia.ia_name = dev; 299 ia.ia_addr = crs.i2c_addr; 300 ia.ia_cookie = node->parent; 301 302 config_found(pb->pb_iic, &ia, iic_print); 303 node->parent->attached = 1; 304 305 return 0; 306} 307 308int 309pcamux_acpi_parse_crs(int crsidx, union acpi_resource *crs, void *arg) 310{ 311 struct pcamux_crs *sc_crs = arg; 312 313 switch (AML_CRSTYPE(crs)) { 314 case LR_SERBUS: 315 if (crs->lr_serbus.type == LR_SERBUS_I2C) 316 sc_crs->i2c_addr = crs->lr_i2cbus._adr; 317 break; 318 319 default: 320 printf("%s: unknown resource type %d\n", __func__, 321 AML_CRSTYPE(crs)); 322 } 323 324 return 0; 325} 326#endif 327 328int 329pcamux_set_channel(struct pcamux_softc *sc, int channel, int flags) 330{ 331 uint8_t data; 332 int error; 333 334 if (channel < -1 || channel >= sc->sc_nchannel) 335 return ENXIO; 336 337 if (sc->sc_channel == channel) 338 return 0; 339 340 data = 0; 341 if (channel != -1) { 342 if (sc->sc_switch) 343 data = 1 << channel; 344 else 345 data = sc->sc_enable | channel; 346 } 347 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 348 sc->sc_addr, NULL, 0, &data, sizeof data, flags); 349 350 return error; 351} 352 353int 354pcamux_acquire_bus(void *cookie, int flags) 355{ 356 struct pcamux_bus *pb = cookie; 357 struct pcamux_softc *sc = pb->pb_sc; 358 int rwflags = RW_WRITE; 359 int error; 360 361 if (flags & I2C_F_POLL) 362 rwflags |= RW_NOSLEEP; 363 364 error = rw_enter(&sc->sc_lock, rwflags); 365 if (error) 366 return error; 367 368 /* Acquire parent bus. */ 369 error = iic_acquire_bus(sc->sc_tag, flags); 370 if (error) { 371 rw_exit_write(&sc->sc_lock); 372 return error; 373 } 374 375 error = pcamux_set_channel(sc, pb->pb_channel, flags); 376 if (error) { 377 iic_release_bus(sc->sc_tag, flags); 378 rw_exit_write(&sc->sc_lock); 379 return error; 380 } 381 382 return 0; 383} 384 385void 386pcamux_release_bus(void *cookie, int flags) 387{ 388 struct pcamux_bus *pb = cookie; 389 struct pcamux_softc *sc = pb->pb_sc; 390 391 /* Release parent bus. */ 392 iic_release_bus(sc->sc_tag, flags); 393 rw_exit_write(&sc->sc_lock); 394} 395 396int 397pcamux_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 398 size_t cmdlen, void *buf, size_t buflen, int flags) 399{ 400 struct pcamux_bus *pb = cookie; 401 struct pcamux_softc *sc = pb->pb_sc; 402 403 rw_assert_wrlock(&sc->sc_lock); 404 405 /* Issue the transaction on the parent bus. */ 406 return iic_exec(sc->sc_tag, op, addr, cmd, cmdlen, buf, buflen, flags); 407} 408 409void 410pcamux_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) 411{ 412 int iba_node = *(int *)arg; 413 struct i2c_attach_args ia; 414 char name[32]; 415 uint32_t reg[1]; 416 int node; 417 418 for (node = OF_child(iba_node); node; node = OF_peer(node)) { 419 memset(name, 0, sizeof(name)); 420 memset(reg, 0, sizeof(reg)); 421 422 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 423 continue; 424 if (name[0] == '\0') 425 continue; 426 427 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 428 continue; 429 430 memset(&ia, 0, sizeof(ia)); 431 ia.ia_tag = iba->iba_tag; 432 ia.ia_addr = bemtoh32(®[0]); 433 ia.ia_name = name; 434 ia.ia_cookie = &node; 435 config_found(self, &ia, iic_print); 436 } 437} 438