From: kettenis Date: Mon, 17 Jul 2023 17:50:22 +0000 (+0000) Subject: Put the USB Type-C power delivery controller into the "S5" state during X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=8634d8ffadb96272f12886a0eb6512f400db68c9;p=openbsd Put the USB Type-C power delivery controller into the "S5" state during suspend. This removes VBUS, avoiding USB devices that are plugged in from consuming power during suspend. ok mlarkin@, tobhe@, deraadt@ --- diff --git a/sys/dev/fdt/tipd.c b/sys/dev/fdt/tipd.c index 6f7a3cb97f5..a1c7147aaaa 100644 --- a/sys/dev/fdt/tipd.c +++ b/sys/dev/fdt/tipd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tipd.c,v 1.1 2022/12/12 19:18:25 kettenis Exp $ */ +/* $OpenBSD: tipd.c,v 1.2 2023/07/17 17:50:22 kettenis Exp $ */ /* * Copyright (c) 2022 Mark Kettenis * @@ -29,6 +29,8 @@ #include +#define TPS_CMD1 0x08 +#define TPS_DATA1 0x09 #define TPS_INT_EVENT_1 0x14 #define TPS_INT_EVENT_2 0x15 #define TPS_INT_MASK_1 0x16 @@ -37,6 +39,11 @@ #define TPS_INT_CLEAR_2 0x19 #define TPS_STATUS 0x1a #define TPS_STATUS_PLUG_PRESENT (1 << 0) +#define TPS_SYSTEM_POWER_STATE 0x20 +#define TPS_SYSTEM_POWER_STATE_S0 0 +#define TPS_SYSTEM_POWER_STATE_S5 5 + +#define TPS_CMD(s) ((s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0]) /* * Interrupt bits on the CD321x controllers used by Apple differ from @@ -56,9 +63,11 @@ struct tipd_softc { int tipd_match(struct device *, void *, void *); void tipd_attach(struct device *, struct device *, void *); +int tipd_activate(struct device *, int); const struct cfattach tipd_ca = { - sizeof(struct tipd_softc), tipd_match, tipd_attach + sizeof(struct tipd_softc), tipd_match, tipd_attach, NULL, + tipd_activate }; struct cfdriver tipd_cd = { @@ -69,7 +78,10 @@ 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_4(struct tipd_softc *, uint8_t, uint32_t); int tipd_write_8(struct tipd_softc *, uint8_t, uint64_t); +int tipd_exec(struct tipd_softc *, const char *, + const void *, size_t, void *, size_t); int tipd_match(struct device *parent, void *match, void *aux) @@ -107,6 +119,31 @@ tipd_attach(struct device *parent, struct device *self, void *aux) } } +int +tipd_activate(struct device *self, int act) +{ + struct tipd_softc *sc = (struct tipd_softc *)self; + uint8_t state; + int error; + + switch (act) { + case DVACT_SUSPEND: + state = TPS_SYSTEM_POWER_STATE_S5; + error = tipd_exec(sc, "SSPS", &state, sizeof(state), NULL, 0); + if (error) + printf("%s: powerdown failed\n", sc->sc_dev.dv_xname); + break; + case DVACT_RESUME: + state = TPS_SYSTEM_POWER_STATE_S0; + error = tipd_exec(sc, "SSPS", &state, sizeof(state), NULL, 0); + if (error) + printf("%s: powerup failed\n", sc->sc_dev.dv_xname); + break; + } + + return 0; +} + void tipd_connect(struct tipd_softc *sc) { @@ -206,6 +243,23 @@ tipd_read_8(struct tipd_softc *sc, uint8_t reg, uint64_t *val) return error; } +int +tipd_write_4(struct tipd_softc *sc, uint8_t reg, uint32_t val) +{ + uint8_t buf[5]; + int error; + + buf[0] = 4; + htolem32(&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; +} + int tipd_write_8(struct tipd_softc *sc, uint8_t reg, uint64_t val) { @@ -222,3 +276,66 @@ tipd_write_8(struct tipd_softc *sc, uint8_t reg, uint64_t val) return error; } + +int +tipd_exec(struct tipd_softc *sc, const char *cmd, const void *wbuf, + size_t wlen, void *rbuf, size_t rlen) +{ + char buf[65]; + uint32_t val; + int timo, error; + uint8_t reg = TPS_DATA1; + + if (wlen >= sizeof(buf) - 1) + return EINVAL; + + error = tipd_read_4(sc, TPS_CMD1, &val); + if (error) + return error; + if (val == TPS_CMD("!CMD")) + return EBUSY; + + if (wlen > 0) { + buf[0] = wlen; + memcpy(&buf[1], wbuf, wlen); + 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); + if (error) + return error; + } + + error = tipd_write_4(sc, TPS_CMD1, TPS_CMD(cmd)); + if (error) + return error; + + for (timo = 1000; timo > 0; timo--) { + error = tipd_read_4(sc, TPS_CMD1, &val); + if (error) + return error; + if (val == TPS_CMD("!CMD")) + return EBUSY; + if (val == 0) + break; + delay(10); + } + + if (timo == 0) + return ETIMEDOUT; + + if (rlen > 0) { + memset(buf, 0, sizeof(buf)); + 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); + if (error) + return error; + if (buf[0] < rlen) + return EIO; + memcpy(rbuf, &buf[1], rlen); + } + + return 0; +}