1 /* $OpenBSD: tipd.c,v 1.3 2023/07/23 11:42:44 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 55 struct 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 66 int tipd_match(struct device *, void *, void *); 67 void tipd_attach(struct device *, struct device *, void *); 68 int tipd_activate(struct device *, int); 69 70 const struct cfattach tipd_ca = { 71 sizeof(struct tipd_softc), tipd_match, tipd_attach, NULL, 72 tipd_activate 73 }; 74 75 struct cfdriver tipd_cd = { 76 NULL, "tipd", DV_DULL 77 }; 78 79 int tipd_intr(void *); 80 81 int tipd_read_4(struct tipd_softc *, uint8_t, uint32_t *); 82 int tipd_read_8(struct tipd_softc *, uint8_t, uint64_t *); 83 int tipd_write_4(struct tipd_softc *, uint8_t, uint32_t); 84 int tipd_write_8(struct tipd_softc *, uint8_t, uint64_t); 85 int tipd_exec(struct tipd_softc *, const char *, 86 const void *, size_t, void *, size_t); 87 88 int 89 tipd_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 96 void 97 tipd_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 125 int 126 tipd_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 157 void 158 tipd_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 174 void 175 tipd_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 191 int 192 tipd_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 225 fail: 226 tipd_write_8(sc, TPS_INT_CLEAR_1, event); 227 return 1; 228 } 229 230 int 231 tipd_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, ®, 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 247 int 248 tipd_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, ®, 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 264 int 265 tipd_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, ®, sizeof(reg), buf, sizeof(buf), 0); 276 iic_release_bus(sc->sc_tag, 0); 277 278 return error; 279 } 280 281 int 282 tipd_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, ®, sizeof(reg), buf, sizeof(buf), 0); 293 iic_release_bus(sc->sc_tag, 0); 294 295 return error; 296 } 297 298 int 299 tipd_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 307 if (wlen >= sizeof(buf) - 1) 308 return EINVAL; 309 310 error = tipd_read_4(sc, TPS_CMD1, &val); 311 if (error) 312 return error; 313 if (val == TPS_CMD("!CMD")) 314 return EBUSY; 315 316 if (wlen > 0) { 317 buf[0] = wlen; 318 memcpy(&buf[1], wbuf, wlen); 319 iic_acquire_bus(sc->sc_tag, 0); 320 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 321 sc->sc_addr, ®, sizeof(reg), buf, sizeof(buf), 0); 322 iic_release_bus(sc->sc_tag, 0); 323 if (error) 324 return error; 325 } 326 327 error = tipd_write_4(sc, TPS_CMD1, TPS_CMD(cmd)); 328 if (error) 329 return error; 330 331 for (timo = 1000; timo > 0; timo--) { 332 error = tipd_read_4(sc, TPS_CMD1, &val); 333 if (error) 334 return error; 335 if (val == TPS_CMD("!CMD")) 336 return EBUSY; 337 if (val == 0) 338 break; 339 delay(10); 340 } 341 342 if (timo == 0) 343 return ETIMEDOUT; 344 345 if (rlen > 0) { 346 memset(buf, 0, sizeof(buf)); 347 iic_acquire_bus(sc->sc_tag, 0); 348 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 349 sc->sc_addr, ®, sizeof(reg), buf, sizeof(buf), 0); 350 iic_release_bus(sc->sc_tag, 0); 351 if (error) 352 return error; 353 if (buf[0] < rlen) 354 return EIO; 355 memcpy(rbuf, &buf[1], rlen); 356 } 357 358 return 0; 359 } 360