jcs's openbsd hax
openbsd
at jcs 366 lines 8.5 kB view raw
1/* $OpenBSD: tipd.c,v 1.4 2025/07/10 22:46:17 kettenis Exp $ */ 2/* 3 * Copyright (c) 2022 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/device.h> 21#include <sys/malloc.h> 22 23#include <machine/intr.h> 24#include <machine/fdt.h> 25 26#include <dev/ofw/openfirm.h> 27#include <dev/ofw/ofw_misc.h> 28#include <dev/ofw/fdt.h> 29 30#include <dev/i2c/i2cvar.h> 31 32#define TPS_CMD1 0x08 33#define TPS_DATA1 0x09 34#define TPS_INT_EVENT_1 0x14 35#define TPS_INT_EVENT_2 0x15 36#define TPS_INT_MASK_1 0x16 37#define TPS_INT_MASK_2 0x17 38#define TPS_INT_CLEAR_1 0x18 39#define TPS_INT_CLEAR_2 0x19 40#define TPS_STATUS 0x1a 41#define TPS_STATUS_PLUG_PRESENT (1 << 0) 42#define TPS_SYSTEM_POWER_STATE 0x20 43#define TPS_SYSTEM_POWER_STATE_S0 0 44#define TPS_SYSTEM_POWER_STATE_S5 5 45#define TPS_POWER_STATUS 0x3f 46 47#define TPS_CMD(s) ((s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0]) 48 49/* 50 * Interrupt bits on the CD321x controllers used by Apple differ from 51 * those used by the standard TPS6598x controllers. 52 */ 53#define CD_INT_PLUG_EVENT (1 << 1) 54 55struct tipd_softc { 56 struct device sc_dev; 57 i2c_tag_t sc_tag; 58 i2c_addr_t sc_addr; 59 60 void *sc_ih; 61 62 struct device_ports sc_ports; 63 uint32_t sc_status; 64}; 65 66int tipd_match(struct device *, void *, void *); 67void tipd_attach(struct device *, struct device *, void *); 68int tipd_activate(struct device *, int); 69 70const struct cfattach tipd_ca = { 71 sizeof(struct tipd_softc), tipd_match, tipd_attach, NULL, 72 tipd_activate 73}; 74 75struct cfdriver tipd_cd = { 76 NULL, "tipd", DV_DULL 77}; 78 79int tipd_intr(void *); 80 81int tipd_read_4(struct tipd_softc *, uint8_t, uint32_t *); 82int tipd_read_8(struct tipd_softc *, uint8_t, uint64_t *); 83int tipd_write_4(struct tipd_softc *, uint8_t, uint32_t); 84int tipd_write_8(struct tipd_softc *, uint8_t, uint64_t); 85int tipd_exec(struct tipd_softc *, const char *, 86 const void *, size_t, void *, size_t); 87 88int 89tipd_match(struct device *parent, void *match, void *aux) 90{ 91 struct i2c_attach_args *ia = aux; 92 93 return iic_is_compatible(ia, "apple,cd321x"); 94} 95 96void 97tipd_attach(struct device *parent, struct device *self, void *aux) 98{ 99 struct tipd_softc *sc = (struct tipd_softc *)self; 100 struct i2c_attach_args *ia = aux; 101 int node = *(int *)ia->ia_cookie; 102 103 sc->sc_tag = ia->ia_tag; 104 sc->sc_addr = ia->ia_addr; 105 106 sc->sc_ih = fdt_intr_establish(node, IPL_BIO, tipd_intr, 107 sc, sc->sc_dev.dv_xname); 108 if (sc->sc_ih == NULL) { 109 printf(": can't establish interrupt\n"); 110 return; 111 } 112 113 printf("\n"); 114 115 tipd_read_4(sc, TPS_STATUS, &sc->sc_status); 116 tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT); 117 118 node = OF_getnodebyname(node, "connector"); 119 if (node) { 120 sc->sc_ports.dp_node = node; 121 device_ports_register(&sc->sc_ports, -1); 122 } 123} 124 125int 126tipd_activate(struct device *self, int act) 127{ 128 struct tipd_softc *sc = (struct tipd_softc *)self; 129 uint8_t state; 130 int error; 131 132 switch (act) { 133 case DVACT_QUIESCE: 134 tipd_write_8(sc, TPS_INT_MASK_1, 0); 135 break; 136 case DVACT_SUSPEND: 137 state = TPS_SYSTEM_POWER_STATE_S5; 138 error = tipd_exec(sc, "SSPS", &state, sizeof(state), NULL, 0); 139 if (error) 140 printf("%s: powerdown failed\n", sc->sc_dev.dv_xname); 141 break; 142 case DVACT_RESUME: 143 state = TPS_SYSTEM_POWER_STATE_S0; 144 error = tipd_exec(sc, "SSPS", &state, sizeof(state), NULL, 0); 145 if (error) 146 printf("%s: powerup failed\n", sc->sc_dev.dv_xname); 147 break; 148 case DVACT_WAKEUP: 149 tipd_read_4(sc, TPS_STATUS, &sc->sc_status); 150 tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT); 151 break; 152 } 153 154 return 0; 155} 156 157void 158tipd_connect(struct tipd_softc *sc) 159{ 160 struct endpoint *ep, *rep; 161 struct usb_controller_port *port; 162 163 ep = endpoint_byreg(&sc->sc_ports, 0, -1); 164 if (ep == NULL) 165 return; 166 rep = endpoint_remote(ep); 167 if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT) 168 return; 169 port = endpoint_get_cookie(rep); 170 if (port && port->up_connect) 171 port->up_connect(port->up_cookie); 172} 173 174void 175tipd_disconnect(struct tipd_softc *sc) 176{ 177 struct endpoint *ep, *rep; 178 struct usb_controller_port *port; 179 180 ep = endpoint_byreg(&sc->sc_ports, 0, -1); 181 if (ep == NULL) 182 return; 183 rep = endpoint_remote(ep); 184 if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT) 185 return; 186 port = endpoint_get_cookie(rep); 187 if (port && port->up_disconnect) 188 port->up_disconnect(port->up_cookie); 189} 190 191int 192tipd_intr(void *arg) 193{ 194 struct tipd_softc *sc = arg; 195 uint64_t event; 196 uint32_t status; 197 int error; 198 199 error = tipd_read_8(sc, TPS_INT_EVENT_1, &event); 200 if (error) 201 return 0; 202 203 if (event == 0) 204 return 0; 205 206 if (event & CD_INT_PLUG_EVENT) { 207 error = tipd_read_4(sc, TPS_STATUS, &status); 208 if (error) 209 goto fail; 210 211 /* 212 * We may get a spurious plug event upon resume. Make 213 * sure we only signal a new connection when the plug 214 * present state really changed. 215 */ 216 if ((status ^ sc->sc_status) & TPS_STATUS_PLUG_PRESENT) { 217 if (status & TPS_STATUS_PLUG_PRESENT) 218 tipd_connect(sc); 219 else 220 tipd_disconnect(sc); 221 sc->sc_status = status; 222 } 223 } 224 225fail: 226 tipd_write_8(sc, TPS_INT_CLEAR_1, event); 227 return 1; 228} 229 230int 231tipd_read_4(struct tipd_softc *sc, uint8_t reg, uint32_t *val) 232{ 233 uint8_t buf[5]; 234 int error; 235 236 iic_acquire_bus(sc->sc_tag, 0); 237 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 238 sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0); 239 iic_release_bus(sc->sc_tag, 0); 240 241 if (error == 0) 242 *val = lemtoh32(&buf[1]); 243 244 return error; 245} 246 247int 248tipd_read_8(struct tipd_softc *sc, uint8_t reg, uint64_t *val) 249{ 250 uint8_t buf[9]; 251 int error; 252 253 iic_acquire_bus(sc->sc_tag, 0); 254 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 255 sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0); 256 iic_release_bus(sc->sc_tag, 0); 257 258 if (error == 0) 259 *val = lemtoh64(&buf[1]); 260 261 return error; 262} 263 264int 265tipd_write_4(struct tipd_softc *sc, uint8_t reg, uint32_t val) 266{ 267 uint8_t buf[5]; 268 int error; 269 270 buf[0] = 4; 271 htolem32(&buf[1], val); 272 273 iic_acquire_bus(sc->sc_tag, 0); 274 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 275 sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0); 276 iic_release_bus(sc->sc_tag, 0); 277 278 return error; 279} 280 281int 282tipd_write_8(struct tipd_softc *sc, uint8_t reg, uint64_t val) 283{ 284 uint8_t buf[9]; 285 int error; 286 287 buf[0] = 8; 288 htolem64(&buf[1], val); 289 290 iic_acquire_bus(sc->sc_tag, 0); 291 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 292 sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0); 293 iic_release_bus(sc->sc_tag, 0); 294 295 return error; 296} 297 298int 299tipd_exec(struct tipd_softc *sc, const char *cmd, const void *wbuf, 300 size_t wlen, void *rbuf, size_t rlen) 301{ 302 char buf[65]; 303 uint32_t val; 304 int timo, error; 305 uint8_t reg = TPS_DATA1; 306 int s; 307 308 if (wlen >= sizeof(buf) - 1) 309 return EINVAL; 310 311 s = splbio(); 312 313 error = tipd_read_4(sc, TPS_CMD1, &val); 314 if (error == 0 && val == TPS_CMD("!CMD")) 315 error = EBUSY; 316 if (error) 317 goto fail; 318 319 if (wlen > 0) { 320 buf[0] = wlen; 321 memcpy(&buf[1], wbuf, wlen); 322 iic_acquire_bus(sc->sc_tag, 0); 323 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 324 sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0); 325 iic_release_bus(sc->sc_tag, 0); 326 if (error) 327 goto fail; 328 } 329 330 error = tipd_write_4(sc, TPS_CMD1, TPS_CMD(cmd)); 331 if (error) 332 goto fail; 333 334 for (timo = 1000; timo > 0; timo--) { 335 error = tipd_read_4(sc, TPS_CMD1, &val); 336 if (error == 0 && val == TPS_CMD("!CMD")) 337 error = EBUSY; 338 if (error) 339 goto fail; 340 if (val == 0) 341 break; 342 delay(10); 343 } 344 345 if (timo == 0) { 346 error = ETIMEDOUT; 347 goto fail; 348 } 349 350 if (rlen > 0) { 351 memset(buf, 0, sizeof(buf)); 352 iic_acquire_bus(sc->sc_tag, 0); 353 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 354 sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0); 355 iic_release_bus(sc->sc_tag, 0); 356 if (error == 0 && buf[0] < rlen) 357 error = EIO; 358 if (error) 359 goto fail; 360 memcpy(rbuf, &buf[1], rlen); 361 } 362 363fail: 364 splx(s); 365 return error; 366}