1 /* $OpenBSD: tipd.c,v 1.1 2022/12/12 19:18:25 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_INT_EVENT_1 0x14 33 #define TPS_INT_EVENT_2 0x15 34 #define TPS_INT_MASK_1 0x16 35 #define TPS_INT_MASK_2 0x17 36 #define TPS_INT_CLEAR_1 0x18 37 #define TPS_INT_CLEAR_2 0x19 38 #define TPS_STATUS 0x1a 39 #define TPS_STATUS_PLUG_PRESENT (1 << 0) 40 41 /* 42 * Interrupt bits on the CD321x controllers used by Apple differ from 43 * those used by the standard TPS6598x controllers. 44 */ 45 #define CD_INT_PLUG_EVENT (1 << 1) 46 47 struct tipd_softc { 48 struct device sc_dev; 49 i2c_tag_t sc_tag; 50 i2c_addr_t sc_addr; 51 52 void *sc_ih; 53 54 struct device_ports sc_ports; 55 }; 56 57 int tipd_match(struct device *, void *, void *); 58 void tipd_attach(struct device *, struct device *, void *); 59 60 const struct cfattach tipd_ca = { 61 sizeof(struct tipd_softc), tipd_match, tipd_attach 62 }; 63 64 struct cfdriver tipd_cd = { 65 NULL, "tipd", DV_DULL 66 }; 67 68 int tipd_intr(void *); 69 70 int tipd_read_4(struct tipd_softc *, uint8_t, uint32_t *); 71 int tipd_read_8(struct tipd_softc *, uint8_t, uint64_t *); 72 int tipd_write_8(struct tipd_softc *, uint8_t, uint64_t); 73 74 int 75 tipd_match(struct device *parent, void *match, void *aux) 76 { 77 struct i2c_attach_args *ia = aux; 78 79 return iic_is_compatible(ia, "apple,cd321x"); 80 } 81 82 void 83 tipd_attach(struct device *parent, struct device *self, void *aux) 84 { 85 struct tipd_softc *sc = (struct tipd_softc *)self; 86 struct i2c_attach_args *ia = aux; 87 int node = *(int *)ia->ia_cookie; 88 89 sc->sc_tag = ia->ia_tag; 90 sc->sc_addr = ia->ia_addr; 91 92 sc->sc_ih = fdt_intr_establish(node, IPL_BIO, tipd_intr, 93 sc, sc->sc_dev.dv_xname); 94 if (sc->sc_ih == NULL) { 95 printf(": can't establish interrupt\n"); 96 return; 97 } 98 99 printf("\n"); 100 101 tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT); 102 103 node = OF_getnodebyname(node, "connector"); 104 if (node) { 105 sc->sc_ports.dp_node = node; 106 device_ports_register(&sc->sc_ports, -1); 107 } 108 } 109 110 void 111 tipd_connect(struct tipd_softc *sc) 112 { 113 struct endpoint *ep, *rep; 114 struct usb_controller_port *port; 115 116 ep = endpoint_byreg(&sc->sc_ports, 0, -1); 117 if (ep == NULL) 118 return; 119 rep = endpoint_remote(ep); 120 if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT) 121 return; 122 port = endpoint_get_cookie(rep); 123 if (port && port->up_connect) 124 port->up_connect(port->up_cookie); 125 } 126 127 void 128 tipd_disconnect(struct tipd_softc *sc) 129 { 130 struct endpoint *ep, *rep; 131 struct usb_controller_port *port; 132 133 ep = endpoint_byreg(&sc->sc_ports, 0, -1); 134 if (ep == NULL) 135 return; 136 rep = endpoint_remote(ep); 137 if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT) 138 return; 139 port = endpoint_get_cookie(rep); 140 if (port && port->up_disconnect) 141 port->up_disconnect(port->up_cookie); 142 } 143 144 int 145 tipd_intr(void *arg) 146 { 147 struct tipd_softc *sc = arg; 148 uint64_t event; 149 uint32_t status; 150 int error; 151 152 error = tipd_read_8(sc, TPS_INT_EVENT_1, &event); 153 if (error) 154 return 0; 155 156 if (event == 0) 157 return 0; 158 159 if (event & CD_INT_PLUG_EVENT) { 160 error = tipd_read_4(sc, TPS_STATUS, &status); 161 if (error) 162 goto fail; 163 164 if (status & TPS_STATUS_PLUG_PRESENT) 165 tipd_connect(sc); 166 else 167 tipd_disconnect(sc); 168 } 169 170 fail: 171 tipd_write_8(sc, TPS_INT_CLEAR_1, event); 172 return 1; 173 } 174 175 int 176 tipd_read_4(struct tipd_softc *sc, uint8_t reg, uint32_t *val) 177 { 178 uint8_t buf[5]; 179 int error; 180 181 iic_acquire_bus(sc->sc_tag, 0); 182 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 183 sc->sc_addr, ®, sizeof(reg), buf, sizeof(buf), 0); 184 iic_release_bus(sc->sc_tag, 0); 185 186 if (error == 0) 187 *val = lemtoh32(&buf[1]); 188 189 return error; 190 } 191 192 int 193 tipd_read_8(struct tipd_softc *sc, uint8_t reg, uint64_t *val) 194 { 195 uint8_t buf[9]; 196 int error; 197 198 iic_acquire_bus(sc->sc_tag, 0); 199 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 200 sc->sc_addr, ®, sizeof(reg), buf, sizeof(buf), 0); 201 iic_release_bus(sc->sc_tag, 0); 202 203 if (error == 0) 204 *val = lemtoh64(&buf[1]); 205 206 return error; 207 } 208 209 int 210 tipd_write_8(struct tipd_softc *sc, uint8_t reg, uint64_t val) 211 { 212 uint8_t buf[9]; 213 int error; 214 215 buf[0] = 8; 216 htolem64(&buf[1], val); 217 218 iic_acquire_bus(sc->sc_tag, 0); 219 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 220 sc->sc_addr, ®, sizeof(reg), buf, sizeof(buf), 0); 221 iic_release_bus(sc->sc_tag, 0); 222 223 return error; 224 } 225