diff options
Diffstat (limited to 'sys/dev/fdt')
-rw-r--r-- | sys/dev/fdt/files.fdt | 7 | ||||
-rw-r--r-- | sys/dev/fdt/tipd.c | 224 | ||||
-rw-r--r-- | sys/dev/fdt/xhci_fdt.c | 44 |
3 files changed, 273 insertions, 2 deletions
diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index 93c8f7de922..222b059391d 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -1,4 +1,4 @@ -# $OpenBSD: files.fdt,v 1.172 2022/11/23 23:43:08 kettenis Exp $ +# $OpenBSD: files.fdt,v 1.173 2022/12/12 19:18:25 kettenis Exp $ # # Config file and device description for machine-independent FDT code. # Included by ports that need it. @@ -677,3 +677,8 @@ file dev/fdt/qcpwm.c qcpwm device qcrtc attach qcrtc at spmi file dev/fdt/qcrtc.c qcrtc + +# TI TPS6598x Type-C controller +device tipd +attach tipd at i2c +file dev/fdt/tipd.c tipd diff --git a/sys/dev/fdt/tipd.c b/sys/dev/fdt/tipd.c new file mode 100644 index 00000000000..6f7a3cb97f5 --- /dev/null +++ b/sys/dev/fdt/tipd.c @@ -0,0 +1,224 @@ +/* $OpenBSD: tipd.c,v 1.1 2022/12/12 19:18:25 kettenis Exp $ */ +/* + * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <machine/intr.h> +#include <machine/fdt.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_misc.h> +#include <dev/ofw/fdt.h> + +#include <dev/i2c/i2cvar.h> + +#define TPS_INT_EVENT_1 0x14 +#define TPS_INT_EVENT_2 0x15 +#define TPS_INT_MASK_1 0x16 +#define TPS_INT_MASK_2 0x17 +#define TPS_INT_CLEAR_1 0x18 +#define TPS_INT_CLEAR_2 0x19 +#define TPS_STATUS 0x1a +#define TPS_STATUS_PLUG_PRESENT (1 << 0) + +/* + * Interrupt bits on the CD321x controllers used by Apple differ from + * those used by the standard TPS6598x controllers. + */ +#define CD_INT_PLUG_EVENT (1 << 1) + +struct tipd_softc { + struct device sc_dev; + i2c_tag_t sc_tag; + i2c_addr_t sc_addr; + + void *sc_ih; + + struct device_ports sc_ports; +}; + +int tipd_match(struct device *, void *, void *); +void tipd_attach(struct device *, struct device *, void *); + +const struct cfattach tipd_ca = { + sizeof(struct tipd_softc), tipd_match, tipd_attach +}; + +struct cfdriver tipd_cd = { + NULL, "tipd", DV_DULL +}; + +int tipd_intr(void *); + +int tipd_read_4(struct tipd_softc *, uint8_t, uint32_t *); +int tipd_read_8(struct tipd_softc *, uint8_t, uint64_t *); +int tipd_write_8(struct tipd_softc *, uint8_t, uint64_t); + +int +tipd_match(struct device *parent, void *match, void *aux) +{ + struct i2c_attach_args *ia = aux; + + return iic_is_compatible(ia, "apple,cd321x"); +} + +void +tipd_attach(struct device *parent, struct device *self, void *aux) +{ + struct tipd_softc *sc = (struct tipd_softc *)self; + struct i2c_attach_args *ia = aux; + int node = *(int *)ia->ia_cookie; + + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + + sc->sc_ih = fdt_intr_establish(node, IPL_BIO, tipd_intr, + sc, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) { + printf(": can't establish interrupt\n"); + return; + } + + printf("\n"); + + tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT); + + node = OF_getnodebyname(node, "connector"); + if (node) { + sc->sc_ports.dp_node = node; + device_ports_register(&sc->sc_ports, -1); + } +} + +void +tipd_connect(struct tipd_softc *sc) +{ + struct endpoint *ep, *rep; + struct usb_controller_port *port; + + ep = endpoint_byreg(&sc->sc_ports, 0, -1); + if (ep == NULL) + return; + rep = endpoint_remote(ep); + if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT) + return; + port = endpoint_get_cookie(rep); + if (port && port->up_connect) + port->up_connect(port->up_cookie); +} + +void +tipd_disconnect(struct tipd_softc *sc) +{ + struct endpoint *ep, *rep; + struct usb_controller_port *port; + + ep = endpoint_byreg(&sc->sc_ports, 0, -1); + if (ep == NULL) + return; + rep = endpoint_remote(ep); + if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT) + return; + port = endpoint_get_cookie(rep); + if (port && port->up_disconnect) + port->up_disconnect(port->up_cookie); +} + +int +tipd_intr(void *arg) +{ + struct tipd_softc *sc = arg; + uint64_t event; + uint32_t status; + int error; + + error = tipd_read_8(sc, TPS_INT_EVENT_1, &event); + if (error) + return 0; + + if (event == 0) + return 0; + + if (event & CD_INT_PLUG_EVENT) { + error = tipd_read_4(sc, TPS_STATUS, &status); + if (error) + goto fail; + + if (status & TPS_STATUS_PLUG_PRESENT) + tipd_connect(sc); + else + tipd_disconnect(sc); + } + +fail: + tipd_write_8(sc, TPS_INT_CLEAR_1, event); + return 1; +} + +int +tipd_read_4(struct tipd_softc *sc, uint8_t reg, uint32_t *val) +{ + uint8_t buf[5]; + int error; + + iic_acquire_bus(sc->sc_tag, 0); + error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_addr, ®, sizeof(reg), buf, sizeof(buf), 0); + iic_release_bus(sc->sc_tag, 0); + + if (error == 0) + *val = lemtoh32(&buf[1]); + + return error; +} + +int +tipd_read_8(struct tipd_softc *sc, uint8_t reg, uint64_t *val) +{ + uint8_t buf[9]; + int error; + + iic_acquire_bus(sc->sc_tag, 0); + error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_addr, ®, sizeof(reg), buf, sizeof(buf), 0); + iic_release_bus(sc->sc_tag, 0); + + if (error == 0) + *val = lemtoh64(&buf[1]); + + return error; +} + +int +tipd_write_8(struct tipd_softc *sc, uint8_t reg, uint64_t val) +{ + uint8_t buf[9]; + int error; + + buf[0] = 8; + htolem64(&buf[1], val); + + iic_acquire_bus(sc->sc_tag, 0); + error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, + sc->sc_addr, ®, sizeof(reg), buf, sizeof(buf), 0); + iic_release_bus(sc->sc_tag, 0); + + return error; +} diff --git a/sys/dev/fdt/xhci_fdt.c b/sys/dev/fdt/xhci_fdt.c index a0c224e10ca..9cdb5f1eeae 100644 --- a/sys/dev/fdt/xhci_fdt.c +++ b/sys/dev/fdt/xhci_fdt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xhci_fdt.c,v 1.19 2022/06/06 09:46:07 kettenis Exp $ */ +/* $OpenBSD: xhci_fdt.c,v 1.20 2022/12/12 19:18:25 kettenis Exp $ */ /* * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> * @@ -19,6 +19,7 @@ #include <sys/systm.h> #include <sys/device.h> #include <sys/malloc.h> +#include <sys/task.h> #include <machine/bus.h> #include <machine/fdt.h> @@ -47,6 +48,10 @@ struct xhci_fdt_softc { bus_addr_t sc_otg_base; bus_size_t sc_otg_size; bus_space_handle_t sc_otg_ioh; + + struct device_ports sc_ports; + struct usb_controller_port sc_usb_controller_port; + struct task sc_snps_connect_task; }; int xhci_fdt_match(struct device *, void *, void *); @@ -222,6 +227,28 @@ xhci_cdns_init(struct xhci_fdt_softc *sc) #define USB3_GUSB2PHYCFG0_SUSPENDUSB20 (1 << 6) #define USB3_GUSB2PHYCFG0_PHYIF (1 << 3) +void +xhci_snps_do_connect(void *arg) +{ + struct xhci_fdt_softc *sc = arg; + + xhci_reinit(&sc->sc); +} + +void +xhci_snps_connect(void *cookie) +{ + struct xhci_fdt_softc *sc = cookie; + + task_add(systq, &sc->sc_snps_connect_task); +} + +void * +xhci_snps_ep_get_cookie(void *cookie, struct endpoint *ep) +{ + return cookie; +} + int xhci_snps_init(struct xhci_fdt_softc *sc) { @@ -229,6 +256,21 @@ xhci_snps_init(struct xhci_fdt_softc *sc) int node = sc->sc_node; uint32_t reg; + /* + * On Apple hardware we need to reset the controller when we + * see a new connection. + */ + if (OF_is_compatible(node, "apple,dwc3")) { + sc->sc_usb_controller_port.up_cookie = sc; + sc->sc_usb_controller_port.up_connect = xhci_snps_connect; + task_set(&sc->sc_snps_connect_task, xhci_snps_do_connect, sc); + + sc->sc_ports.dp_node = node; + sc->sc_ports.dp_cookie = &sc->sc_usb_controller_port; + sc->sc_ports.dp_ep_get_cookie = xhci_snps_ep_get_cookie; + device_ports_register(&sc->sc_ports, EP_USB_CONTROLLER_PORT); + } + /* We don't support device mode, so always force host mode. */ reg = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USB3_GCTL); reg &= ~USB3_GCTL_PRTCAPDIR_MASK; |