diff --git a/sys/dev/thunderbolt/nhi.c b/sys/dev/thunderbolt/nhi.c --- a/sys/dev/thunderbolt/nhi.c +++ b/sys/dev/thunderbolt/nhi.c @@ -261,11 +261,73 @@ return (0); } +static int +nhi_reset_v1(struct nhi_softc *sc) +{ + + /* See section 2.4 of HCM guide v2. */ + nhi_write_reg(sc, ROUTER_HIR, 1); + pause_sbt("nhi", ustosbt(10 * 1000), 0, C_HARDCLOCK); + return (0); +} + +static int +nhi_reset_v2(struct nhi_softc *sc) +{ + uint32_t reg; + + /* + * TODO "The Connection Manager shall disable all Transmit Descriptor + * Rings and wait for at least 1 millisecond prior to setting the Host + * Router Reset bit to 1b. After the Connection Manager sets the Host + * Router Reset bit to 1b, it shall not access the Receive Descriptor + * Rings until the Host Router Reset bit is set to 0b." + */ + /* See section 3.5 of HCM guide v2. */ + nhi_write_reg(sc, ROUTER_HRR, 1); + /* + * "The Host Router is required to complete its reset within 500ms + * after the Host Router Reset bit is set to 1b." + */ + for (size_t i = 0; i < 10 && reg; i++) { + /* + * Wait at least 50 ms after writing before reading this + * register. If this is 1, it means that we are still + * resetting. + */ + pause_sbt("nhi", ustosbt(50 * 1000), 0, C_HARDCLOCK); + reg = nhi_read_reg(sc, ROUTER_HRR); + } + if (reg == 0) { + tb_debug(sc, DBG_INIT|DBG_EXTRA, + "Succeeded in resetting host router\n"); + return (0); + } + tb_printf(sc, "Host router reset timed out\n"); + return (ETIMEDOUT); +} + +static int +nhi_reset(struct nhi_softc *sc) +{ + + tb_debug(sc, DBG_INIT, "Resetting host router\n"); + + switch (sc->ver) { + case NHI_VER_1_0: + return (nhi_reset_v1(sc)); + case NHI_VER_2_0: + return (nhi_reset_v2(sc)); + } + return (ENXIO); +} + int nhi_attach(struct nhi_softc *sc) { - uint32_t val; - int error = 0; + uint32_t val; + struct nhi_host_caps caps; + int error = 0; if ((error = nhi_setup_sysctl(sc)) != 0) return (error); @@ -275,18 +337,34 @@ nhi_configure_caps(sc); /* - * Get the number of TX/RX paths. This sizes some of the register - * arrays during allocation and initialization. USB4 spec says that - * the max is 21. Alpine Ridge appears to default to 12. + * Get the host interface version and number of TX/RX paths. This + * sizes some of the register arrays during allocation and + * initialization. USB4 spec says that the max is 21. Alpine Ridge + * appears to default to 12. */ - val = GET_HOST_CAPS_PATHS(nhi_read_reg(sc, NHI_HOST_CAPS)); - tb_debug(sc, DBG_INIT|DBG_NOISY, "Total Paths= %d\n", val); - if ((val == 0) || (val > 21) || ((NHI_IS_AR(sc) && val != 12))) { + val = nhi_read_reg(sc, NHI_HOST_CAPS); + caps = *(struct nhi_host_caps *)&val; + if (caps.version_major == 0 && caps.version_minor == 0) { + tb_printf(sc, "Host interface is version 1.0\n"); + sc->ver = NHI_VER_1_0; + } else if (caps.version_major == 2 && caps.version_minor == 0) { + tb_printf(sc, "Host interface is version 2.0\n"); + sc->ver = NHI_VER_2_0; + } else { + tb_printf(sc, "WARN: unexpected host interface version %d.%d -" + " assuming 1.0\n", caps.version_major, caps.version_minor); + sc->ver = NHI_VER_1_0; + } + tb_debug(sc, DBG_INIT|DBG_NOISY, "Total Paths= %d\n", caps.total_paths); + if ((caps.total_paths == 0) || (caps.total_paths > 21) || + ((NHI_IS_AR(sc) && caps.total_paths != 12))) { tb_printf(sc, "WARN: unexpected number of paths: %d\n", val); /* return (ENXIO); */ } sc->path_count = val; + nhi_reset(sc); + SLIST_INIT(&sc->ring_list); error = nhi_pci_configure_interrupts(sc); diff --git a/sys/dev/thunderbolt/nhi_reg.h b/sys/dev/thunderbolt/nhi_reg.h --- a/sys/dev/thunderbolt/nhi_reg.h +++ b/sys/dev/thunderbolt/nhi_reg.h @@ -54,6 +54,10 @@ #define DEVICE_PINK_SARDINE_0 0x1668 #define DEVICE_PINK_SARDINE_1 0x1669 +/* Timings */ +#define NHI_SLPR_WAIT_US 50000 /* 50 ms, tSetSR */ +#define NHI_SLPR_WAIT_MAX 100 + /* * * MMIO Registers * * Ring buffer registers * @@ -176,7 +180,15 @@ /* Native Host Interface Control registers */ #define NHI_HOST_CAPS 0x39640 -#define GET_HOST_CAPS_PATHS(val) ((val) & 0x3f) + +/* Host Interface Capabilities, 12.6.3.1.1 */ +struct nhi_host_caps { + uint32_t total_paths:11; + uint8_t reserved1:5; + uint8_t version_major:3; + uint8_t version_minor:5; + uint8_t reserved0:8; +} __packed; /* * This definition comes from the Linux driver. In the USB4 spec, this @@ -256,7 +268,7 @@ #define ICL_VSCAP22_FORCEPWR (1 << 1) /* * Data structures - * Transmit buffer descriptor. Must be aligned on a 4byte boundary + * Transmit buffer descriptor, 12.3.1. Must be aligned on a 4byte boundary */ struct nhi_tx_buffer_desc { uint32_t addr_lo; @@ -275,7 +287,7 @@ } __packed; /* - * Receive buffer descriptor. 4 byte aligned. This goes into + * Receive buffer descriptor, 12.4.1. 4 byte aligned. This goes into * the descriptor ring, but changes into the _post form when the * controller uses it. */ diff --git a/sys/dev/thunderbolt/nhi_var.h b/sys/dev/thunderbolt/nhi_var.h --- a/sys/dev/thunderbolt/nhi_var.h +++ b/sys/dev/thunderbolt/nhi_var.h @@ -201,6 +201,11 @@ uint8_t uuid[16]; uint8_t lc_uuid[16]; + + enum { + NHI_VER_1_0, + NHI_VER_2_0, + } ver; }; struct nhi_dispatch { diff --git a/sys/dev/thunderbolt/router.c b/sys/dev/thunderbolt/router.c --- a/sys/dev/thunderbolt/router.c +++ b/sys/dev/thunderbolt/router.c @@ -66,8 +66,12 @@ static int _tb_router_attach(struct router_softc *); static void router_prepare_read(struct router_softc *, struct router_command *, int); +static void router_prepare_write(struct router_softc *, struct router_command *, + int); static int _tb_config_read(struct router_softc *, u_int, u_int, u_int, u_int, uint32_t *, void *, struct router_command **); +static int _tb_config_write(struct router_softc *, u_int, u_int, u_int, u_int, + uint32_t *, void *, struct router_command **); static int router_schedule(struct router_softc *, struct router_command *); static int router_schedule_locked(struct router_softc *, struct router_command *); @@ -499,8 +503,66 @@ tb_config_write(struct router_softc *sc, u_int space, u_int adapter, u_int offset, u_int dwlen, uint32_t *buf) { + struct router_command *cmd; + int error, retries; - return(0); + if ((error = _tb_config_write(sc, space, adapter, offset, dwlen, buf, + router_get_config_cb, &cmd)) != 0) + return (error); + + retries = cmd->retries; + mtx_lock(&sc->mtx); + while (retries-- >= 0) { + error = router_schedule_locked(sc, cmd); + if (error) + break; + + error = msleep(cmd, &sc->mtx, 0, "tbtcfg", cmd->timeout * hz); + if (error != EWOULDBLOCK) + break; + sc->inflight_cmd = NULL; + tb_debug(sc, DBG_ROUTER, "Config command timed out, " + "retries=%d\n", retries); + } + + if (cmd->ev != 0) + error = EINVAL; + router_free_cmd(sc, cmd); + mtx_unlock(&sc->mtx); + return (error); +} + +static int +_tb_config_write(struct router_softc *sc, u_int space, u_int adapter, + u_int offset, u_int dwlen, uint32_t *buf, void *cb, + struct router_command **rcmd) /* TODO Check that all these args are being used. */ +{ + struct router_command *cmd; + struct tb_cfg_write *msg; + size_t msglen = sizeof(*msg) + dwlen * 4; + int error; + + if ((error = router_alloc_cmd(sc, &cmd)) != 0) + return (error); + + msg = router_get_frame_data(cmd); + bzero(msg, msglen); + msg->route.hi = sc->route.hi; + msg->route.lo = sc->route.lo; + printf("%s: space= %d, adapter= %d, dwlen= %d, offset= %d\n", + __func__, space, adapter, dwlen, offset); + msg->addr_attrs = TB_CONFIG_ADDR(0, space, adapter, dwlen, offset); + for (size_t i = 0; i < dwlen; i++) + msg->data[i] = buf[i]; + cmd->callback = cb; + cmd->callback_arg = buf; + cmd->dwlen = dwlen; + router_prepare_write(sc, cmd, msglen); + + if (rcmd != NULL) + *rcmd = cmd; + + return (0); } static int @@ -585,6 +647,41 @@ return; } +static void +router_prepare_write(struct router_softc *sc, struct router_command *cmd, + int len) +{ + struct nhi_cmd_frame *nhicmd; + uint32_t *msg; + int msglen, i; + + KASSERT(cmd != NULL, ("cmd cannot be NULL\n")); + KASSERT(len != 0, ("Invalid zero-length command\n")); + KASSERT(len % 4 == 0, ("Message must be 32bit padded\n")); + + nhicmd = cmd->nhicmd; + msglen = (len - 4) / 4; + for (i = 0; i < msglen; i++) + nhicmd->data[i] = htobe32(nhicmd->data[i]); + + msg = (uint32_t *)nhicmd->data; + msg[msglen] = htobe32(tb_calc_crc(nhicmd->data, len-4)); + + nhicmd->pdf = PDF_WRITE; + nhicmd->req_len = len; + + nhicmd->timeout = NHI_CMD_TIMEOUT; + nhicmd->retries = 0; + nhicmd->resp_buffer = (uint32_t *)cmd->resp_buffer; + nhicmd->resp_len = (cmd->dwlen + 3) * 4; + nhicmd->context = cmd; + + cmd->retries = CFG_DEFAULT_RETRIES; + cmd->timeout = CFG_DEFAULT_TIMEOUT; + + return; +} + static int router_schedule(struct router_softc *sc, struct router_command *cmd) { diff --git a/sys/dev/thunderbolt/router_var.h b/sys/dev/thunderbolt/router_var.h --- a/sys/dev/thunderbolt/router_var.h +++ b/sys/dev/thunderbolt/router_var.h @@ -87,6 +87,17 @@ uint16_t vsec_len; }; +/* Router config space registers */ +#define ROUTER_HRR 0x39898 +#define ROUTER_HIR 0x39858 +#define ROUTER_CS_5 5 +#define ROUTER_SLP (1<<0) /* Enter sleep (CS5). */ +#define ROUTER_WOP (1<<1) /* Wake on PCIe (CS5). */ +#define ROUTER_WOU (1<<2) /* Wake on USB3 (CS5). */ +#define ROUTER_WOD (1<<3) /* Wake on DP (CS5). */ +#define ROUTER_CS_6 6 +#define ROUTER_SLPR (1<<0) /* Sleep ready (CS6). */ + int tb_router_attach(struct router_softc *, tb_route_t); int tb_router_attach_root(struct nhi_softc *, tb_route_t); int tb_router_detach(struct router_softc *); @@ -121,7 +132,7 @@ } /* - * Read the Router config space for the router refered to in the softc. + * Read the Router config space for the router referred to in the softc. * addr - The dword offset in the config space * dwlen - The number of dwords * buf - must be large enough to hold the number of dwords requested. @@ -141,7 +152,7 @@ } /* - * Write the Router config space for the router refered to in the softc. + * Write the Router config space for the router referred to in the softc. * addr - The dword offset in the config space * dwlen - The number of dwords * buf - must be large enough to hold the number of dwords requested. @@ -154,7 +165,7 @@ } /* - * Read the Adapter config space for the router refered to in the softc. + * Read the Adapter config space for the router referred to in the softc. * adap - Adapter number * addr - The dword offset in the config space * dwlen - The number of dwords @@ -168,7 +179,7 @@ } /* - * Read the Adapter config space for the router refered to in the softc. + * Read the Adapter config space for the router referred to in the softc. * adap - Adapter number * addr - The dword offset in the config space * dwlen - The number of dwords @@ -182,7 +193,7 @@ } /* - * Read the Path config space for the router refered to in the softc. + * Read the Path config space for the router referred to in the softc. * adap - Adapter number * hopid - HopID of the path * len - The number of adjacent paths @@ -197,7 +208,7 @@ } /* - * Write the Path config space for the router refered to in the softc. + * Write the Path config space for the router referred to in the softc. * adap - Adapter number * hopid - HopID of the path * len - The number of adjacent paths @@ -212,7 +223,7 @@ } /* - * Read the Counters config space for the router refered to in the softc. + * Read the Counters config space for the router referred to in the softc. * Counters come in sets of 3 dwords. * adap - Adapter number * set - The counter set index diff --git a/sys/dev/thunderbolt/tbcfg_reg.h b/sys/dev/thunderbolt/tbcfg_reg.h --- a/sys/dev/thunderbolt/tbcfg_reg.h +++ b/sys/dev/thunderbolt/tbcfg_reg.h @@ -64,8 +64,8 @@ struct tb_cfg_write { tb_route_t route; uint32_t addr_attrs; - uint32_t data[0]; /* Up to 60 dwords */ - /* uint32_t crc is at the end */ + uint32_t data[0]; + uint32_t crc; } __packed; /* Config space write response, 6.4.2.6 */