Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 30 Sep 2025 08:44:03 +0200
From:      Wolfram Schneider <wosch@freebsd.org>
To:        Aymeric Wibo <obiwac@freebsd.org>
Cc:        src-committers@freebsd.org, dev-commits-src-all@freebsd.org,  dev-commits-src-main@freebsd.org
Subject:   Re: git: 2ed9833791f2 - main - thunderbolt: Import USB4 code
Message-ID:  <CAMWY7CAy4Gv-7tTHdgZMwq=kGUV2NPtC4BvBBiS=93J0OhZnCA@mail.gmail.com>
In-Reply-To: <202509271713.58RHDTTL008060@gitrepo.freebsd.org>
References:  <202509271713.58RHDTTL008060@gitrepo.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
I'm now getting a stale symlink:

./tools/build/stale-symlink-buildworld.sh
stale symlink detected: lrwxrwxr-x  1 wosch wheel 91 Sep 29 22:14
/var/tmp/freebsd-obj-wosch/home/projects/freebsd-src/amd64.amd64/sys/GENERIC/modules/home/projects/freebsd-src/sys/modules/thunderbolt/opt_acpi_wmi.h
-> /var/tmp/freebsd-obj-wosch/home/projects/freebsd-src/amd64.amd64/sys/GENERIC/opt_acpi_wmi.h

the only reference to the file "opt_acpi_wmi.h" is in the Makefile

sys/modules/thunderbolt/Makefile:SRCS+= opt_acpi.h opt_acpi_wmi.h
acpi_if.h acpi_wmi_if.h

What is this file supposed to do, where does it come from?

-Wolfram

On Sat, 27 Sept 2025 at 19:13, Aymeric Wibo <obiwac@freebsd.org> wrote:
>
> The branch main has been updated by obiwac:
>
> URL: https://cgit.FreeBSD.org/src/commit/?id=2ed9833791f28e14843ac813f90cb030e45948dc
>
> commit 2ed9833791f28e14843ac813f90cb030e45948dc
> Author:     Aymeric Wibo <obiwac@FreeBSD.org>
> AuthorDate: 2025-09-27 11:50:43 +0000
> Commit:     Aymeric Wibo <obiwac@FreeBSD.org>
> CommitDate: 2025-09-27 17:13:13 +0000
>
>     thunderbolt: Import USB4 code
>
>     Add initial USB4 code written by Scott Long and originally passed on to
>     HPS (source: https://github.com/hselasky/usb4), minus the ICM code and
>     with some small fixes.
>
>     For context, older TB chips implemented the connection manager in
>     firmware (ICM) instead of in the OS (HCM), but maintaining the ICM code
>     would be a huge burden for not many chips.
>
>     Mostly completed work:
>
>     - Debug/trace framework.
>     - NHI controller driver.
>     - PCIe bridge driver.
>     - Router and config space layer handling (just reading in this commit).
>
>     Link to the email where Scott shared details about the initial USB4
>     work:
>
>     https://lists.freebsd.org/archives/freebsd-hackers/2024-July/003411.html
>
>     Glanced at by:  emaste, imp
>     Sponsored by:   The FreeBSD Foundation
>     Differential Revision:  https://reviews.freebsd.org/D49450
>     Event:          EuroBSDcon 2025
> ---
>  sys/dev/thunderbolt/hcm.c          |  223 +++++++
>  sys/dev/thunderbolt/hcm_var.h      |   47 ++
>  sys/dev/thunderbolt/nhi.c          | 1170 ++++++++++++++++++++++++++++++++++++
>  sys/dev/thunderbolt/nhi_pci.c      |  529 ++++++++++++++++
>  sys/dev/thunderbolt/nhi_reg.h      |  332 ++++++++++
>  sys/dev/thunderbolt/nhi_var.h      |  277 +++++++++
>  sys/dev/thunderbolt/nhi_wmi.c      |  198 ++++++
>  sys/dev/thunderbolt/router.c       |  939 +++++++++++++++++++++++++++++
>  sys/dev/thunderbolt/router_var.h   |  242 ++++++++
>  sys/dev/thunderbolt/tb_acpi_pcib.c |  181 ++++++
>  sys/dev/thunderbolt/tb_debug.c     |  334 ++++++++++
>  sys/dev/thunderbolt/tb_debug.h     |   93 +++
>  sys/dev/thunderbolt/tb_dev.c       |  331 ++++++++++
>  sys/dev/thunderbolt/tb_dev.h       |   41 ++
>  sys/dev/thunderbolt/tb_if.m        |  121 ++++
>  sys/dev/thunderbolt/tb_ioctl.h     |   52 ++
>  sys/dev/thunderbolt/tb_pcib.c      |  614 +++++++++++++++++++
>  sys/dev/thunderbolt/tb_pcib.h      |   93 +++
>  sys/dev/thunderbolt/tb_reg.h       |   52 ++
>  sys/dev/thunderbolt/tb_var.h       |   54 ++
>  sys/dev/thunderbolt/tbcfg_reg.h    |  363 +++++++++++
>  sys/modules/Makefile               |    5 +
>  sys/modules/thunderbolt/Makefile   |   13 +
>  23 files changed, 6304 insertions(+)
>
> diff --git a/sys/dev/thunderbolt/hcm.c b/sys/dev/thunderbolt/hcm.c
> new file mode 100644
> index 000000000000..b8f703fc3b52
> --- /dev/null
> +++ b/sys/dev/thunderbolt/hcm.c
> @@ -0,0 +1,223 @@
> +/*-
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2022 Scott Long
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
> + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
> + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> + * SUCH DAMAGE.
> + */
> +
> +#include "opt_thunderbolt.h"
> +
> +/* Host Configuration Manager (HCM) for USB4 and later TB3 */
> +#include <sys/types.h>
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/kernel.h>
> +#include <sys/module.h>
> +#include <sys/bus.h>
> +#include <sys/conf.h>
> +#include <sys/malloc.h>
> +#include <sys/queue.h>
> +#include <sys/sysctl.h>
> +#include <sys/lock.h>
> +#include <sys/mutex.h>
> +#include <sys/taskqueue.h>
> +#include <sys/gsb_crc32.h>
> +#include <sys/endian.h>
> +#include <vm/vm.h>
> +#include <vm/pmap.h>
> +
> +#include <machine/bus.h>
> +#include <machine/stdarg.h>
> +
> +#include <dev/thunderbolt/nhi_reg.h>
> +#include <dev/thunderbolt/nhi_var.h>
> +#include <dev/thunderbolt/tb_reg.h>
> +#include <dev/thunderbolt/tb_var.h>
> +#include <dev/thunderbolt/tb_debug.h>
> +#include <dev/thunderbolt/tbcfg_reg.h>
> +#include <dev/thunderbolt/router_var.h>
> +#include <dev/thunderbolt/hcm_var.h>
> +
> +static void hcm_cfg_task(void *, int);
> +
> +int
> +hcm_attach(struct nhi_softc *nsc)
> +{
> +       struct hcm_softc *hcm;
> +
> +       tb_debug(nsc, DBG_HCM|DBG_EXTRA, "hcm_attach called\n");
> +
> +       hcm = malloc(sizeof(struct hcm_softc), M_THUNDERBOLT, M_NOWAIT|M_ZERO);
> +       if (hcm == NULL) {
> +               tb_debug(nsc, DBG_HCM, "Cannot allocate hcm object\n");
> +               return (ENOMEM);
> +       }
> +
> +       hcm->dev = nsc->dev;
> +       hcm->nsc = nsc;
> +       nsc->hcm = hcm;
> +
> +       hcm->taskqueue = taskqueue_create("hcm_event", M_NOWAIT,
> +           taskqueue_thread_enqueue, &hcm->taskqueue);
> +       if (hcm->taskqueue == NULL)
> +               return (ENOMEM);
> +       taskqueue_start_threads(&hcm->taskqueue, 1, PI_DISK, "tbhcm%d_tq",
> +           device_get_unit(nsc->dev));
> +       TASK_INIT(&hcm->cfg_task, 0, hcm_cfg_task, hcm);
> +
> +       return (0);
> +}
> +
> +int
> +hcm_detach(struct nhi_softc *nsc)
> +{
> +       struct hcm_softc *hcm;
> +
> +       hcm = nsc->hcm;
> +       if (hcm->taskqueue)
> +               taskqueue_free(hcm->taskqueue);
> +
> +       return (0);
> +}
> +
> +int
> +hcm_router_discover(struct hcm_softc *hcm)
> +{
> +
> +       taskqueue_enqueue(hcm->taskqueue, &hcm->cfg_task);
> +
> +       return (0);
> +}
> +
> +static void
> +hcm_cfg_task(void *arg, int pending)
> +{
> +       struct hcm_softc *hcm;
> +       struct router_softc *rsc;
> +       struct router_cfg_cap cap;
> +       struct tb_cfg_router *cfg;
> +       struct tb_cfg_adapter *adp;
> +       struct tb_cfg_cap_lane *lane;
> +       uint32_t *buf;
> +       uint8_t *u;
> +       u_int error, i, offset;
> +
> +       hcm = (struct hcm_softc *)arg;
> +
> +       tb_debug(hcm, DBG_HCM|DBG_EXTRA, "hcm_cfg_task called\n");
> +
> +       buf = malloc(8 * 4, M_THUNDERBOLT, M_NOWAIT|M_ZERO);
> +       if (buf == NULL) {
> +               tb_debug(hcm, DBG_HCM, "Cannot alloc memory for discovery\n");
> +               return;
> +       }
> +
> +       rsc = hcm->nsc->root_rsc;
> +       error = tb_config_router_read(rsc, 0, 5, buf);
> +       if (error != 0) {
> +               free(buf, M_NHI);
> +               return;
> +       }
> +
> +       cfg = (struct tb_cfg_router *)buf;
> +
> +       cap.space = TB_CFG_CS_ROUTER;
> +       cap.adap = 0;
> +       cap.next_cap = GET_ROUTER_CS_NEXT_CAP(cfg);
> +       while (cap.next_cap != 0) {
> +               error = tb_config_next_cap(rsc, &cap);
> +               if (error != 0)
> +                       break;
> +
> +               if ((cap.cap_id == TB_CFG_CAP_VSEC) && (cap.vsc_len == 0)) {
> +                       tb_debug(hcm, DBG_HCM, "Router Cap= %d, vsec= %d, "
> +                           "len= %d, next_cap= %d\n", cap.cap_id,
> +                           cap.vsc_id, cap.vsec_len, cap.next_cap);
> +               } else if (cap.cap_id == TB_CFG_CAP_VSC) {
> +                       tb_debug(hcm, DBG_HCM, "Router cap= %d, vsc= %d, "
> +                           "len= %d, next_cap= %d\n", cap.cap_id,
> +                           cap.vsc_id, cap.vsc_len, cap.next_cap);
> +               } else
> +                       tb_debug(hcm, DBG_HCM, "Router cap= %d, "
> +                           "next_cap= %d\n", cap.cap_id, cap.next_cap);
> +               if (cap.next_cap > TB_CFG_CAP_OFFSET_MAX)
> +                       cap.next_cap = 0;
> +       }
> +
> +       u = (uint8_t *)buf;
> +       error = tb_config_get_lc_uuid(rsc, u);
> +       if (error == 0) {
> +               tb_debug(hcm, DBG_HCM, "Router LC UUID: %02x%02x%02x%02x-"
> +                   "%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
> +                   u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
> +                   u[9], u[10], u[11], u[12], u[13], u[14], u[15]);
> +       } else
> +               tb_printf(hcm, "Error finding LC registers: %d\n", error);
> +
> +       for (i = 1; i <= rsc->max_adap; i++) {
> +               error = tb_config_adapter_read(rsc, i, 0, 8, buf);
> +               if (error != 0) {
> +                       tb_debug(hcm, DBG_HCM, "Adapter %d: no adapter\n", i);
> +                       continue;
> +               }
> +               adp = (struct tb_cfg_adapter *)buf;
> +               tb_debug(hcm, DBG_HCM, "Adapter %d: %s, max_counters= 0x%08x,"
> +                   " adapter_num= %d\n", i,
> +                   tb_get_string(GET_ADP_CS_TYPE(adp), tb_adapter_type),
> +                   GET_ADP_CS_MAX_COUNTERS(adp), GET_ADP_CS_ADP_NUM(adp));
> +
> +               if (GET_ADP_CS_TYPE(adp) != ADP_CS2_LANE)
> +                       continue;
> +
> +               error = tb_config_find_adapter_cap(rsc, i, TB_CFG_CAP_LANE,
> +                   &offset);
> +               if (error)
> +                       continue;
> +
> +               error = tb_config_adapter_read(rsc, i, offset, 3, buf);
> +               if (error)
> +                       continue;
> +
> +               lane = (struct tb_cfg_cap_lane *)buf;
> +               tb_debug(hcm, DBG_HCM, "Lane Adapter State= %s %s\n",
> +                   tb_get_string((lane->current_lws & CAP_LANE_STATE_MASK),
> +                   tb_adapter_state), (lane->targ_lwp & CAP_LANE_DISABLE) ?
> +                   "disabled" : "enabled");
> +
> +               if ((lane->current_lws & CAP_LANE_STATE_MASK) ==
> +                   CAP_LANE_STATE_CL0) {
> +                       tb_route_t newr;
> +
> +                       newr.hi = rsc->route.hi;
> +                       newr.lo = rsc->route.lo | (i << rsc->depth * 8);
> +
> +                       tb_printf(hcm, "want to add router at 0x%08x%08x\n",
> +                           newr.hi, newr.lo);
> +                       error = tb_router_attach(rsc, newr);
> +                       tb_printf(rsc, "tb_router_attach returned %d\n", error);
> +               }
> +       }
> +
> +       free(buf, M_THUNDERBOLT);
> +}
> diff --git a/sys/dev/thunderbolt/hcm_var.h b/sys/dev/thunderbolt/hcm_var.h
> new file mode 100644
> index 000000000000..a11c8e9b6a92
> --- /dev/null
> +++ b/sys/dev/thunderbolt/hcm_var.h
> @@ -0,0 +1,47 @@
> +/*-
> + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
> + *
> + * Copyright (c) 2022 Scott Long
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
> + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
> + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> + * SUCH DAMAGE.
> + *
> + * $FreeBSD$
> + */
> +
> +#ifndef _HCM_VAR_H
> +#define _HCM_VAR_H
> +
> +struct hcm_softc {
> +       u_int                   debug;
> +       device_t                dev;
> +       struct nhi_softc        *nsc;
> +
> +       struct task             cfg_task;
> +       struct taskqueue        *taskqueue;
> +};
> +
> +int hcm_attach(struct nhi_softc *);
> +int hcm_detach(struct nhi_softc *);
> +int hcm_router_discover(struct hcm_softc *);
> +
> +#endif /* _HCM_VAR_H */
> diff --git a/sys/dev/thunderbolt/nhi.c b/sys/dev/thunderbolt/nhi.c
> new file mode 100644
> index 000000000000..205e69c16253
> --- /dev/null
> +++ b/sys/dev/thunderbolt/nhi.c
> @@ -0,0 +1,1170 @@
> +/*-
> + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
> + *
> + * Copyright (c) 2022 Scott Long
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
> + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
> + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> + * SUCH DAMAGE.
> + */
> +
> +#include "opt_thunderbolt.h"
> +
> +/* PCIe interface for Thunderbolt Native Host Interface (nhi) */
> +#include <sys/types.h>
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/kernel.h>
> +#include <sys/module.h>
> +#include <sys/bus.h>
> +#include <sys/conf.h>
> +#include <sys/malloc.h>
> +#include <sys/queue.h>
> +#include <sys/sysctl.h>
> +#include <sys/lock.h>
> +#include <sys/mutex.h>
> +#include <sys/taskqueue.h>
> +#include <sys/gsb_crc32.h>
> +#include <sys/endian.h>
> +#include <vm/vm.h>
> +#include <vm/pmap.h>
> +
> +#include <machine/bus.h>
> +#include <machine/stdarg.h>
> +
> +#include <dev/thunderbolt/nhi_reg.h>
> +#include <dev/thunderbolt/nhi_var.h>
> +#include <dev/thunderbolt/tb_reg.h>
> +#include <dev/thunderbolt/tb_var.h>
> +#include <dev/thunderbolt/tb_debug.h>
> +#include <dev/thunderbolt/hcm_var.h>
> +#include <dev/thunderbolt/tbcfg_reg.h>
> +#include <dev/thunderbolt/router_var.h>
> +#include <dev/thunderbolt/tb_dev.h>
> +#include "tb_if.h"
> +
> +static int nhi_alloc_ring(struct nhi_softc *, int, int, int,
> +    struct nhi_ring_pair **);
> +static void nhi_free_ring(struct nhi_ring_pair *);
> +static void nhi_free_rings(struct nhi_softc *);
> +static int nhi_configure_ring(struct nhi_softc *, struct nhi_ring_pair *);
> +static int nhi_activate_ring(struct nhi_ring_pair *);
> +static int nhi_deactivate_ring(struct nhi_ring_pair *);
> +static int nhi_alloc_ring0(struct nhi_softc *);
> +static void nhi_free_ring0(struct nhi_softc *);
> +static void nhi_fill_rx_ring(struct nhi_softc *, struct nhi_ring_pair *);
> +static int nhi_init(struct nhi_softc *);
> +static void nhi_post_init(void *);
> +static int nhi_tx_enqueue(struct nhi_ring_pair *, struct nhi_cmd_frame *);
> +static int nhi_setup_sysctl(struct nhi_softc *);
> +
> +SYSCTL_NODE(_hw, OID_AUTO, nhi, CTLFLAG_RD, 0, "NHI Driver Parameters");
> +
> +MALLOC_DEFINE(M_NHI, "nhi", "nhi driver memory");
> +
> +#ifndef NHI_DEBUG_LEVEL
> +#define NHI_DEBUG_LEVEL 0
> +#endif
> +
> +/* 0 = default, 1 = force-on, 2 = force-off */
> +#ifndef NHI_FORCE_HCM
> +#define NHI_FORCE_HCM 0
> +#endif
> +
> +void
> +nhi_get_tunables(struct nhi_softc *sc)
> +{
> +       devclass_t dc;
> +       device_t ufp;
> +       char    tmpstr[80], oid[80];
> +       u_int   val;
> +
> +       /* Set local defaults */
> +       sc->debug = NHI_DEBUG_LEVEL;
> +       sc->max_ring_count = NHI_DEFAULT_NUM_RINGS;
> +       sc->force_hcm = NHI_FORCE_HCM;
> +
> +       /* Inherit setting from the upstream thunderbolt switch node */
> +       val = TB_GET_DEBUG(sc->dev, &sc->debug);
> +       if (val != 0) {
> +               dc = devclass_find("tbolt");
> +               if (dc != NULL) {
> +                       ufp = devclass_get_device(dc, device_get_unit(sc->dev));
> +                       if (ufp != NULL)
> +                               TB_GET_DEBUG(ufp, &sc->debug);
> +               } else {
> +                       if (TUNABLE_STR_FETCH("hw.tbolt.debug_level", oid,
> +                           80) != 0)
> +                               tb_parse_debug(&sc->debug, oid);
> +               }
> +       }
> +
> +       /*
> +        * Grab global variables.  Allow nhi debug flags to override
> +        * thunderbolt debug flags, if present.
> +        */
> +       bzero(oid, 80);
> +       if (TUNABLE_STR_FETCH("hw.nhi.debug_level", oid, 80) != 0)
> +               tb_parse_debug(&sc->debug, oid);
> +       if (TUNABLE_INT_FETCH("hw.nhi.max_rings", &val) != 0) {
> +               val = min(val, NHI_MAX_NUM_RINGS);
> +               sc->max_ring_count = max(val, 1);
> +       }
> +       if (TUNABLE_INT_FETCH("hw.nhi.force_hcm", &val) != 0)
> +               sc->force_hcm = val;
> +
> +       /* Grab instance variables */
> +       bzero(oid, 80);
> +       snprintf(tmpstr, sizeof(tmpstr), "dev.nhi.%d.debug_level",
> +           device_get_unit(sc->dev));
> +       if (TUNABLE_STR_FETCH(tmpstr, oid, 80) != 0)
> +               tb_parse_debug(&sc->debug, oid);
> +       snprintf(tmpstr, sizeof(tmpstr), "dev.nhi.%d.max_rings",
> +           device_get_unit(sc->dev));
> +       if (TUNABLE_INT_FETCH(tmpstr, &val) != 0) {
> +               val = min(val, NHI_MAX_NUM_RINGS);
> +               sc->max_ring_count = max(val, 1);
> +       }
> +       snprintf(tmpstr, sizeof(tmpstr), "dev, nhi.%d.force_hcm",
> +           device_get_unit(sc->dev));
> +       if (TUNABLE_INT_FETCH(tmpstr, &val) != 0)
> +               sc->force_hcm = val;
> +
> +       return;
> +}
> +
> +static void
> +nhi_configure_caps(struct nhi_softc *sc)
> +{
> +
> +       if (NHI_IS_USB4(sc) || (sc->force_hcm == NHI_FORCE_HCM_ON))
> +               sc->caps |= NHI_CAP_HCM;
> +       if (sc->force_hcm == NHI_FORCE_HCM_OFF)
> +               sc->caps &= ~NHI_CAP_HCM;
> +}
> +
> +struct nhi_cmd_frame *
> +nhi_alloc_tx_frame(struct nhi_ring_pair *r)
> +{
> +       struct nhi_cmd_frame *cmd;
> +
> +       mtx_lock(&r->mtx);
> +       cmd = nhi_alloc_tx_frame_locked(r);
> +       mtx_unlock(&r->mtx);
> +
> +       return (cmd);
> +}
> +
> +void
> +nhi_free_tx_frame(struct nhi_ring_pair *r, struct nhi_cmd_frame *cmd)
> +{
> +       mtx_lock(&r->mtx);
> +       nhi_free_tx_frame_locked(r, cmd);
> +       mtx_unlock(&r->mtx);
> +}
> +
> +/*
> + * Push a command and data dword through the mailbox to the firmware.
> + * Response is either good, error, or timeout.  Commands that return data
> + * do so by reading OUTMAILDATA.
> + */
> +int
> +nhi_inmail_cmd(struct nhi_softc *sc, uint32_t cmd, uint32_t data)
> +{
> +       uint32_t val;
> +       u_int error, timeout;
> +
> +       mtx_lock(&sc->nhi_mtx);
> +       /*
> +        * XXX Should a defer/reschedule happen here, or is it not worth
> +        * worrying about?
> +        */
> +       if (sc->hwflags & NHI_MBOX_BUSY) {
> +               mtx_unlock(&sc->nhi_mtx);
> +               tb_debug(sc, DBG_MBOX, "Driver busy with mailbox\n");
> +               return (EBUSY);
> +       }
> +       sc->hwflags |= NHI_MBOX_BUSY;
> +
> +       val = nhi_read_reg(sc, TBT_INMAILCMD);
> +       tb_debug(sc, DBG_MBOX|DBG_FULL, "Reading INMAILCMD= 0x%08x\n", val);
> +       if (val & INMAILCMD_ERROR)
> +               tb_debug(sc, DBG_MBOX, "Error already set in INMAILCMD\n");
> +       if (val & INMAILCMD_OPREQ) {
> +               mtx_unlock(&sc->nhi_mtx);
> +               tb_debug(sc, DBG_MBOX,
> +                   "INMAILCMD request already in progress\n");
> +               return (EBUSY);
> +       }
> +
> +       nhi_write_reg(sc, TBT_INMAILDATA, data);
> +       nhi_write_reg(sc, TBT_INMAILCMD, cmd | INMAILCMD_OPREQ);
> +
> +       /* Poll at 1s intervals */
> +       timeout = NHI_MAILBOX_TIMEOUT;
> +       while (timeout--) {
> +               DELAY(1000000);
> +               val = nhi_read_reg(sc, TBT_INMAILCMD);
> +               tb_debug(sc, DBG_MBOX|DBG_EXTRA,
> +                   "Polling INMAILCMD= 0x%08x\n", val);
> +               if ((val & INMAILCMD_OPREQ) == 0)
> +                       break;
> +       }
> +       sc->hwflags &= ~NHI_MBOX_BUSY;
> +       mtx_unlock(&sc->nhi_mtx);
> +
> +       error = 0;
> +       if (val & INMAILCMD_OPREQ) {
> +               tb_printf(sc, "Timeout waiting for mailbox\n");
> +               error = ETIMEDOUT;
> +       }
> +       if (val & INMAILCMD_ERROR) {
> +               tb_printf(sc, "Firmware reports error in mailbox\n");
> +               error = EINVAL;
> +       }
> +
> +       return (error);
> +}
> +
> +/*
> + * Pull command status and data from the firmware mailbox.
> + */
> +int
> +nhi_outmail_cmd(struct nhi_softc *sc, uint32_t *val)
> +{
> +
> +       if (val == NULL)
> +               return (EINVAL);
> +       *val = nhi_read_reg(sc, TBT_OUTMAILCMD);
> +       return (0);
> +}
> +
> +int
> +nhi_attach(struct nhi_softc *sc)
> +{
> +       uint32_t val;
> +       int error = 0;
> +
> +       if ((error = nhi_setup_sysctl(sc)) != 0)
> +               return (error);
> +
> +       mtx_init(&sc->nhi_mtx, "nhimtx", "NHI Control Mutex", MTX_DEF);
> +
> +       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.
> +        */
> +       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))) {
> +               tb_printf(sc, "WARN: unexpected number of paths: %d\n", val);
> +               /* return (ENXIO); */
> +       }
> +       sc->path_count = val;
> +
> +       SLIST_INIT(&sc->ring_list);
> +
> +       error = nhi_pci_configure_interrupts(sc);
> +       if (error == 0)
> +               error = nhi_alloc_ring0(sc);
> +       if (error == 0) {
> +               nhi_configure_ring(sc, sc->ring0);
> +               nhi_activate_ring(sc->ring0);
> +               nhi_fill_rx_ring(sc, sc->ring0);
> +       }
> +
> +       if (error == 0)
> +               error = tbdev_add_interface(sc);
> +
> +       if ((error == 0) && (NHI_USE_ICM(sc)))
> +               tb_printf(sc, "WARN: device uses an internal connection manager\n");
> +       if ((error == 0) && (NHI_USE_HCM(sc)))
> +               ;
> +       error = hcm_attach(sc);
> +
> +       if (error == 0)
> +               error = nhi_init(sc);
> +
> +       return (error);
> +}
> +
> +int
> +nhi_detach(struct nhi_softc *sc)
> +{
> +
> +       if (NHI_USE_HCM(sc))
> +               hcm_detach(sc);
> +
> +       if (sc->root_rsc != NULL)
> +               tb_router_detach(sc->root_rsc);
> +
> +       tbdev_remove_interface(sc);
> +
> +       nhi_pci_disable_interrupts(sc);
> +
> +       nhi_free_ring0(sc);
> +
> +       /* XXX Should the rings be marked as !VALID in the descriptors? */
> +       nhi_free_rings(sc);
> +
> +       mtx_destroy(&sc->nhi_mtx);
> +
> +       return (0);
> +}
> +
> +static void
> +nhi_memaddr_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
> +{
> +       bus_addr_t *addr;
> +
> +       addr = arg;
> +       if (error == 0 && nsegs == 1) {
> +               *addr = segs[0].ds_addr;
> +       } else
> +               *addr = 0;
> +}
> +
> +static int
> +nhi_alloc_ring(struct nhi_softc *sc, int ringnum, int tx_depth, int rx_depth,
> +    struct nhi_ring_pair **rp)
> +{
> +       bus_dma_template_t t;
> +       bus_addr_t ring_busaddr;
> +       struct nhi_ring_pair *r;
> +       int ring_size, error;
> +       u_int rxring_len, txring_len;
> +       char *ring;
> +
> +       if (ringnum >= sc->max_ring_count) {
> +               tb_debug(sc, DBG_INIT, "Tried to allocate ring number %d\n",
> +                   ringnum);
> +               return (EINVAL);
> +       }
> +
> +       /* Allocate the ring structure and the RX ring tacker together. */
> +       rxring_len = rx_depth * sizeof(void *);
> +       txring_len = tx_depth * sizeof(void *);
> +       r = malloc(sizeof(struct nhi_ring_pair) + rxring_len + txring_len,
> +           M_NHI, M_NOWAIT|M_ZERO);
> +       if (r == NULL) {
> +               tb_printf(sc, "ERROR: Cannot allocate ring memory\n");
> +               return (ENOMEM);
> +       }
> +
> +       r->sc = sc;
> +       TAILQ_INIT(&r->tx_head);
> +       TAILQ_INIT(&r->rx_head);
> +       r->ring_num = ringnum;
> +       r->tx_ring_depth = tx_depth;
> +       r->tx_ring_mask = tx_depth - 1;
> +       r->rx_ring_depth = rx_depth;
> +       r->rx_ring_mask = rx_depth - 1;
> +       r->rx_pici_reg = NHI_RX_RING_PICI + ringnum * 16;
> +       r->tx_pici_reg = NHI_TX_RING_PICI + ringnum * 16;
> +       r->rx_cmd_ring = (struct nhi_cmd_frame **)((uint8_t *)r + sizeof (*r));
> +       r->tx_cmd_ring = (struct nhi_cmd_frame **)((uint8_t *)r->rx_cmd_ring +
> +           rxring_len);
> +
> +       snprintf(r->name, NHI_RING_NAMELEN, "nhiring%d\n", ringnum);
> +       mtx_init(&r->mtx, r->name, "NHI Ring Lock", MTX_DEF);
> +       tb_debug(sc, DBG_INIT | DBG_FULL, "Allocated ring context at %p, "
> +           "mutex %p\n", r, &r->mtx);
> +
> +       /* Allocate the RX and TX buffer descriptor rings */
> +       ring_size = sizeof(struct nhi_tx_buffer_desc) * r->tx_ring_depth;
> +       ring_size += sizeof(struct nhi_rx_buffer_desc) * r->rx_ring_depth;
> +       tb_debug(sc, DBG_INIT | DBG_FULL, "Ring %d ring_size= %d\n",
> +           ringnum, ring_size);
> +
> +       bus_dma_template_init(&t, sc->parent_dmat);
> +       t.alignment = 4;
> +       t.maxsize = t.maxsegsize = ring_size;
> +       t.nsegments = 1;
> +       if ((error = bus_dma_template_tag(&t, &r->ring_dmat)) != 0) {
> +               tb_printf(sc, "Cannot allocate ring %d DMA tag: %d\n",
> +                   ringnum, error);
> +               return (ENOMEM);
> +       }
> +       if (bus_dmamem_alloc(r->ring_dmat, (void **)&ring, BUS_DMA_NOWAIT,
> +           &r->ring_map)) {
> +               tb_printf(sc, "Cannot allocate ring memory\n");
> +               return (ENOMEM);
> +       }
> +       bzero(ring, ring_size);
> +       bus_dmamap_load(r->ring_dmat, r->ring_map, ring, ring_size,
> +           nhi_memaddr_cb, &ring_busaddr, 0);
> +
> +       r->ring = ring;
> +
> +       r->tx_ring = (union nhi_ring_desc *)(ring);
> +       r->tx_ring_busaddr = ring_busaddr;
> +       ring += sizeof(struct nhi_tx_buffer_desc) * r->tx_ring_depth;
> +       ring_busaddr += sizeof(struct nhi_tx_buffer_desc) * r->tx_ring_depth;
> +
> +       r->rx_ring = (union nhi_ring_desc *)(ring);
> +       r->rx_ring_busaddr = ring_busaddr;
> +
> +       tb_debug(sc, DBG_INIT | DBG_EXTRA, "Ring %d: RX %p [0x%jx] "
> +           "TX %p [0x%jx]\n", ringnum, r->tx_ring, r->tx_ring_busaddr,
> +           r->rx_ring, r->rx_ring_busaddr);
> +
> +       *rp = r;
> +       return (0);
> +}
> +
> +static void
> +nhi_free_ring(struct nhi_ring_pair *r)
> +{
> +
> +       tb_debug(r->sc, DBG_INIT, "Freeing ring %d resources\n", r->ring_num);
> +       nhi_deactivate_ring(r);
> +
> +       if (r->tx_ring_busaddr != 0) {
> +               bus_dmamap_unload(r->ring_dmat, r->ring_map);
> +               r->tx_ring_busaddr = 0;
> +       }
> +       if (r->ring != NULL) {
> +               bus_dmamem_free(r->ring_dmat, r->ring, r->ring_map);
> +               r->ring = NULL;
> +       }
> +       if (r->ring_dmat != NULL) {
> +               bus_dma_tag_destroy(r->ring_dmat);
> +               r->ring_dmat = NULL;
> +       }
> +       mtx_destroy(&r->mtx);
> +}
> +
> +static void
> +nhi_free_rings(struct nhi_softc *sc)
> +{
> +       struct nhi_ring_pair *r;
> +
> +       while ((r = SLIST_FIRST(&sc->ring_list)) != NULL) {
> +               nhi_free_ring(r);
> +               mtx_lock(&sc->nhi_mtx);
> +               SLIST_REMOVE_HEAD(&sc->ring_list, ring_link);
> +               mtx_unlock(&sc->nhi_mtx);
> +               free(r, M_NHI);
> +       }
> +
> +       return;
> +}
> +
> +static int
> +nhi_configure_ring(struct nhi_softc *sc, struct nhi_ring_pair *ring)
> +{
> +       bus_addr_t busaddr;
> +       uint32_t val;
> +       int idx;
> +
> +       idx = ring->ring_num * 16;
> +
> +       /* Program the TX ring address and size */
> +       busaddr = ring->tx_ring_busaddr;
> +       nhi_write_reg(sc, NHI_TX_RING_ADDR_LO + idx, busaddr & 0xffffffff);
> +       nhi_write_reg(sc, NHI_TX_RING_ADDR_HI + idx, busaddr >> 32);
> +       nhi_write_reg(sc, NHI_TX_RING_SIZE + idx, ring->tx_ring_depth);
> +       nhi_write_reg(sc, NHI_TX_RING_TABLE_TIMESTAMP + idx, 0x0);
> +       tb_debug(sc, DBG_INIT, "TX Ring %d TX_RING_SIZE= 0x%x\n",
> +           ring->ring_num, ring->tx_ring_depth);
> +
> +       /* Program the RX ring address and size */
> +       busaddr = ring->rx_ring_busaddr;
> +       val = (ring->rx_buffer_size << 16) | ring->rx_ring_depth;
> +       nhi_write_reg(sc, NHI_RX_RING_ADDR_LO + idx, busaddr & 0xffffffff);
> +       nhi_write_reg(sc, NHI_RX_RING_ADDR_HI + idx, busaddr >> 32);
> +       nhi_write_reg(sc, NHI_RX_RING_SIZE + idx, val);
> +       nhi_write_reg(sc, NHI_RX_RING_TABLE_BASE1 + idx, 0xffffffff);
> +       tb_debug(sc, DBG_INIT, "RX Ring %d RX_RING_SIZE= 0x%x\n",
> +           ring->ring_num, val);
> +
> +       return (0);
> +}
> +
> +static int
> +nhi_activate_ring(struct nhi_ring_pair *ring)
> +{
> +       struct nhi_softc *sc = ring->sc;
> +       int idx;
> +
> +       nhi_pci_enable_interrupt(ring);
> +
> +       idx = ring->ring_num * 32;
> +       tb_debug(sc, DBG_INIT, "Activating ring %d at idx %d\n",
> +           ring->ring_num, idx);
> +       nhi_write_reg(sc, NHI_TX_RING_TABLE_BASE0 + idx,
> +           TX_TABLE_RAW | TX_TABLE_VALID);
> +       nhi_write_reg(sc, NHI_RX_RING_TABLE_BASE0 + idx,
> +           RX_TABLE_RAW | RX_TABLE_VALID);
> +
> +       return (0);
> +}
> +
> +static int
> +nhi_deactivate_ring(struct nhi_ring_pair *r)
> +{
> +       struct nhi_softc *sc = r->sc;
> +       int idx;
> +
> +       idx = r->ring_num * 32;
> +       tb_debug(sc, DBG_INIT, "Deactiving ring %d at idx %d\n",
> +           r->ring_num, idx);
> +       nhi_write_reg(sc, NHI_TX_RING_TABLE_BASE0 + idx, 0);
> +       nhi_write_reg(sc, NHI_RX_RING_TABLE_BASE0 + idx, 0);
> +
> +       idx = r->ring_num * 16;
> +       tb_debug(sc, DBG_INIT, "Setting ring %d sizes to 0\n", r->ring_num);
> +       nhi_write_reg(sc, NHI_TX_RING_SIZE + idx, 0);
> +       nhi_write_reg(sc, NHI_RX_RING_SIZE + idx, 0);
> +
> +       return (0);
> +}
> +
> +static int
> +nhi_alloc_ring0(struct nhi_softc *sc)
> +{
> +       bus_addr_t frames_busaddr;
> +       bus_dma_template_t t;
> +       struct nhi_intr_tracker *trkr;
> +       struct nhi_ring_pair *r;
> +       struct nhi_cmd_frame *cmd;
> +       char *frames;
> +       int error, size, i;
> +
> +       if ((error = nhi_alloc_ring(sc, 0, NHI_RING0_TX_DEPTH,
> +           NHI_RING0_RX_DEPTH, &r)) != 0) {
> +               tb_printf(sc, "Error allocating control ring\n");
> +               return (error);
> +       }
> +
> +       r->rx_buffer_size = NHI_RING0_FRAME_SIZE;/* Control packets are small */
> +
> +       /* Allocate the RX and TX buffers that are used for Ring0 comms */
> +       size = r->tx_ring_depth * NHI_RING0_FRAME_SIZE;
> +       size += r->rx_ring_depth * NHI_RING0_FRAME_SIZE;
> +
> +       bus_dma_template_init(&t, sc->parent_dmat);
> +       t.maxsize = t.maxsegsize = size;
> +       t.nsegments = 1;
> +       if (bus_dma_template_tag(&t, &sc->ring0_dmat)) {
> +               tb_printf(sc, "Error allocating control ring buffer tag\n");
> +               return (ENOMEM);
> +       }
> +
> +       if (bus_dmamem_alloc(sc->ring0_dmat, (void **)&frames, BUS_DMA_NOWAIT,
> +           &sc->ring0_map) != 0) {
> +               tb_printf(sc, "Error allocating control ring memory\n");
> +               return (ENOMEM);
> +       }
> +       bzero(frames, size);
> +       bus_dmamap_load(sc->ring0_dmat, sc->ring0_map, frames, size,
> +           nhi_memaddr_cb, &frames_busaddr, 0);
> +       sc->ring0_frames_busaddr = frames_busaddr;
> +       sc->ring0_frames = frames;
> +
> +       /* Allocate the driver command trackers */
> +       sc->ring0_cmds = malloc(sizeof(struct nhi_cmd_frame) *
> +           (r->tx_ring_depth + r->rx_ring_depth), M_NHI, M_NOWAIT | M_ZERO);
> +       if (sc->ring0_cmds == NULL)
> +               return (ENOMEM);
> +
> +       /* Initialize the RX frames so they can be used */
> +       mtx_lock(&r->mtx);
> +       for (i = 0; i < r->rx_ring_depth; i++) {
> +               cmd = &sc->ring0_cmds[i];
> +               cmd->data = (uint32_t *)(frames + NHI_RING0_FRAME_SIZE * i);
> +               cmd->data_busaddr = frames_busaddr + NHI_RING0_FRAME_SIZE * i;
> +               cmd->flags = CMD_MAPPED;
> +               cmd->idx = i;
> +               TAILQ_INSERT_TAIL(&r->rx_head, cmd, cm_link);
> +       }
> +
> +       /* Inititalize the TX frames */
> +       for ( ; i < r->tx_ring_depth + r->rx_ring_depth - 1; i++) {
> +               cmd = &sc->ring0_cmds[i];
> +               cmd->data = (uint32_t *)(frames + NHI_RING0_FRAME_SIZE * i);
> +               cmd->data_busaddr = frames_busaddr + NHI_RING0_FRAME_SIZE * i;
> +               cmd->flags = CMD_MAPPED;
> +               cmd->idx = i;
> +               nhi_free_tx_frame_locked(r, cmd);
> +       }
> +       mtx_unlock(&r->mtx);
> +
> +       /* Do a 1:1 mapping of rings to interrupt vectors. */
> +       /* XXX Should be abstracted */
> +       trkr = &sc->intr_trackers[0];
> +       trkr->ring = r;
> +       r->tracker = trkr;
> +
> +       /* XXX Should be an array */
> +       sc->ring0 = r;
> +       SLIST_INSERT_HEAD(&sc->ring_list, r, ring_link);
> +
> +       return (0);
> +}
> +
> +static void
> +nhi_free_ring0(struct nhi_softc *sc)
> +{
> +       if (sc->ring0_cmds != NULL) {
> +               free(sc->ring0_cmds, M_NHI);
> +               sc->ring0_cmds = NULL;
> +       }
> +
> +       if (sc->ring0_frames_busaddr != 0) {
> +               bus_dmamap_unload(sc->ring0_dmat, sc->ring0_map);
> +               sc->ring0_frames_busaddr = 0;
> *** 5529 LINES SKIPPED ***



-- 
Wolfram Schneider <wosch@FreeBSD.org> https://wolfram.schneider.org



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAMWY7CAy4Gv-7tTHdgZMwq=kGUV2NPtC4BvBBiS=93J0OhZnCA>