Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 30 Sep 2025 23:17:20 +0800
From:      Zhenlei Huang <zlei@FreeBSD.org>
To:        Aymeric Wibo <obiwac@freebsd.org>
Cc:        "src-committers@freebsd.org" <src-committers@FreeBSD.org>, "dev-commits-src-all@freebsd.org" <dev-commits-src-all@FreeBSD.org>, "dev-commits-src-main@freebsd.org" <dev-commits-src-main@FreeBSD.org>
Subject:   Re: git: 2ed9833791f2 - main - thunderbolt: Import USB4 code
Message-ID:  <76A05154-90E6-4237-851F-EFA35D29D3CC@FreeBSD.org>
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
Nice work !

Now my thunderbolt ethernet is correctly recognized and works great on =
my old MBP.

Cheers !

Best regards,
Zhenlei

> On Sep 28, 2025, at 1:13 AM, Aymeric Wibo <obiwac@freebsd.org> wrote:
>=20
> The branch main has been updated by obiwac:
>=20
> URL: =
https://cgit.FreeBSD.org/src/commit/?id=3D2ed9833791f28e14843ac813f90cb030=
e45948dc
>=20
> 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
>=20
>    thunderbolt: Import USB4 code
>=20
>    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.
>=20
>    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.
>=20
>    Mostly completed work:
>=20
>    - Debug/trace framework.
>    - NHI controller driver.
>    - PCIe bridge driver.
>    - Router and config space layer handling (just reading in this =
commit).
>=20
>    Link to the email where Scott shared details about the initial USB4
>    work:
>=20
>    =
https://lists.freebsd.org/archives/freebsd-hackers/2024-July/003411.html
>=20
>    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(+)
>=20
> 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 =3D malloc(sizeof(struct hcm_softc), M_THUNDERBOLT, =
M_NOWAIT|M_ZERO);
> +	if (hcm =3D=3D NULL) {
> +		tb_debug(nsc, DBG_HCM, "Cannot allocate hcm object\n");
> +		return (ENOMEM);
> +	}
> +
> +	hcm->dev =3D nsc->dev;
> +	hcm->nsc =3D nsc;
> +	nsc->hcm =3D hcm;
> +
> +	hcm->taskqueue =3D taskqueue_create("hcm_event", M_NOWAIT,
> +	    taskqueue_thread_enqueue, &hcm->taskqueue);
> +	if (hcm->taskqueue =3D=3D 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 =3D 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 =3D (struct hcm_softc *)arg;
> +
> +	tb_debug(hcm, DBG_HCM|DBG_EXTRA, "hcm_cfg_task called\n");
> +
> +	buf =3D malloc(8 * 4, M_THUNDERBOLT, M_NOWAIT|M_ZERO);
> +	if (buf =3D=3D NULL) {
> +		tb_debug(hcm, DBG_HCM, "Cannot alloc memory for =
discovery\n");
> +		return;
> +	}
> +
> +	rsc =3D hcm->nsc->root_rsc;
> +	error =3D tb_config_router_read(rsc, 0, 5, buf);
> +	if (error !=3D 0) {
> +		free(buf, M_NHI);
> +		return;
> +	}
> +
> +	cfg =3D (struct tb_cfg_router *)buf;
> +
> +	cap.space =3D TB_CFG_CS_ROUTER;
> +	cap.adap =3D 0;
> +	cap.next_cap =3D GET_ROUTER_CS_NEXT_CAP(cfg);
> +	while (cap.next_cap !=3D 0) {
> +		error =3D tb_config_next_cap(rsc, &cap);
> +		if (error !=3D 0)
> +			break;
> +
> +		if ((cap.cap_id =3D=3D TB_CFG_CAP_VSEC) && (cap.vsc_len =
=3D=3D 0)) {
> +			tb_debug(hcm, DBG_HCM, "Router Cap=3D %d, vsec=3D =
%d, "
> +			    "len=3D %d, next_cap=3D %d\n", cap.cap_id,
> +			    cap.vsc_id, cap.vsec_len, cap.next_cap);
> +		} else if (cap.cap_id =3D=3D TB_CFG_CAP_VSC) {
> +			tb_debug(hcm, DBG_HCM, "Router cap=3D %d, vsc=3D =
%d, "
> +			    "len=3D %d, next_cap=3D %d\n", cap.cap_id,
> +			    cap.vsc_id, cap.vsc_len, cap.next_cap);
> +		} else
> +			tb_debug(hcm, DBG_HCM, "Router cap=3D %d, "
> +			    "next_cap=3D %d\n", cap.cap_id, =
cap.next_cap);
> +		if (cap.next_cap > TB_CFG_CAP_OFFSET_MAX)
> +			cap.next_cap =3D 0;
> +	}
> +
> +	u =3D (uint8_t *)buf;
> +	error =3D tb_config_get_lc_uuid(rsc, u);
> +	if (error =3D=3D 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 =3D 1; i <=3D rsc->max_adap; i++) {
> +		error =3D tb_config_adapter_read(rsc, i, 0, 8, buf);
> +		if (error !=3D 0) {
> +			tb_debug(hcm, DBG_HCM, "Adapter %d: no =
adapter\n", i);
> +			continue;
> +		}
> +		adp =3D (struct tb_cfg_adapter *)buf;
> +		tb_debug(hcm, DBG_HCM, "Adapter %d: %s, max_counters=3D =
0x%08x,"
> +		    " adapter_num=3D %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) !=3D ADP_CS2_LANE)
> +			continue;
> +
> +		error =3D tb_config_find_adapter_cap(rsc, i, =
TB_CFG_CAP_LANE,
> +		    &offset);
> +		if (error)
> +			continue;
> +
> +		error =3D tb_config_adapter_read(rsc, i, offset, 3, =
buf);
> +		if (error)
> +			continue;
> +
> +		lane =3D (struct tb_cfg_cap_lane *)buf;
> +		tb_debug(hcm, DBG_HCM, "Lane Adapter State=3D %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) =3D=3D
> +		    CAP_LANE_STATE_CL0) {
> +			tb_route_t newr;
> +
> +			newr.hi =3D rsc->route.hi;
> +			newr.lo =3D rsc->route.lo | (i << rsc->depth * =
8);
> +
> +			tb_printf(hcm, "want to add router at =
0x%08x%08x\n",
> +			    newr.hi, newr.lo);
> +			error =3D 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 =3D default, 1 =3D force-on, 2 =3D 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 =3D NHI_DEBUG_LEVEL;
> +	sc->max_ring_count =3D NHI_DEFAULT_NUM_RINGS;
> +	sc->force_hcm =3D NHI_FORCE_HCM;
> +
> +	/* Inherit setting from the upstream thunderbolt switch node */
> +	val =3D TB_GET_DEBUG(sc->dev, &sc->debug);
> +	if (val !=3D 0) {
> +		dc =3D devclass_find("tbolt");
> +		if (dc !=3D NULL) {
> +			ufp =3D devclass_get_device(dc, =
device_get_unit(sc->dev));
> +			if (ufp !=3D NULL)
> +				TB_GET_DEBUG(ufp, &sc->debug);
> +		} else {
> +			if (TUNABLE_STR_FETCH("hw.tbolt.debug_level", =
oid,
> +			    80) !=3D 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) !=3D 0)
> +		tb_parse_debug(&sc->debug, oid);
> +	if (TUNABLE_INT_FETCH("hw.nhi.max_rings", &val) !=3D 0) {
> +		val =3D min(val, NHI_MAX_NUM_RINGS);
> +		sc->max_ring_count =3D max(val, 1);
> +	}
> +	if (TUNABLE_INT_FETCH("hw.nhi.force_hcm", &val) !=3D 0)
> +		sc->force_hcm =3D 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) !=3D 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) !=3D 0) {
> +		val =3D min(val, NHI_MAX_NUM_RINGS);
> +		sc->max_ring_count =3D max(val, 1);
> +	}
> +	snprintf(tmpstr, sizeof(tmpstr), "dev, nhi.%d.force_hcm",
> +	    device_get_unit(sc->dev));
> +	if (TUNABLE_INT_FETCH(tmpstr, &val) !=3D 0)
> +		sc->force_hcm =3D val;
> +
> +	return;
> +}
> +
> +static void
> +nhi_configure_caps(struct nhi_softc *sc)
> +{
> +
> +	if (NHI_IS_USB4(sc) || (sc->force_hcm =3D=3D NHI_FORCE_HCM_ON))
> +		sc->caps |=3D NHI_CAP_HCM;
> +	if (sc->force_hcm =3D=3D NHI_FORCE_HCM_OFF)
> +		sc->caps &=3D ~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 =3D 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 |=3D NHI_MBOX_BUSY;
> +
> +	val =3D nhi_read_reg(sc, TBT_INMAILCMD);
> +	tb_debug(sc, DBG_MBOX|DBG_FULL, "Reading INMAILCMD=3D 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 =3D NHI_MAILBOX_TIMEOUT;
> +	while (timeout--) {
> +		DELAY(1000000);
> +		val =3D nhi_read_reg(sc, TBT_INMAILCMD);
> +		tb_debug(sc, DBG_MBOX|DBG_EXTRA,
> +		    "Polling INMAILCMD=3D 0x%08x\n", val);
> +		if ((val & INMAILCMD_OPREQ) =3D=3D 0)
> +			break;
> +	}
> +	sc->hwflags &=3D ~NHI_MBOX_BUSY;
> +	mtx_unlock(&sc->nhi_mtx);
> +
> +	error =3D 0;
> +	if (val & INMAILCMD_OPREQ) {
> +		tb_printf(sc, "Timeout waiting for mailbox\n");
> +		error =3D ETIMEDOUT;
> +	}
> +	if (val & INMAILCMD_ERROR) {
> +		tb_printf(sc, "Firmware reports error in mailbox\n");
> +		error =3D 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 =3D=3D NULL)
> +		return (EINVAL);
> +	*val =3D nhi_read_reg(sc, TBT_OUTMAILCMD);
> +	return (0);
> +}
> +
> +int
> +nhi_attach(struct nhi_softc *sc)
> +{
> +	uint32_t val;
> +	int error =3D 0;
> +
> +	if ((error =3D nhi_setup_sysctl(sc)) !=3D 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 =3D GET_HOST_CAPS_PATHS(nhi_read_reg(sc, NHI_HOST_CAPS));
> +	tb_debug(sc, DBG_INIT|DBG_NOISY, "Total Paths=3D %d\n", val);
> +	if ((val =3D=3D 0) || (val > 21) || ((NHI_IS_AR(sc) && val !=3D =
12))) {
> +		tb_printf(sc, "WARN: unexpected number of paths: %d\n", =
val);
> +		/* return (ENXIO); */
> +	}
> +	sc->path_count =3D val;
> +
> +	SLIST_INIT(&sc->ring_list);
> +
> +	error =3D nhi_pci_configure_interrupts(sc);
> +	if (error =3D=3D 0)
> +		error =3D nhi_alloc_ring0(sc);
> +	if (error =3D=3D 0) {
> +		nhi_configure_ring(sc, sc->ring0);
> +		nhi_activate_ring(sc->ring0);
> +		nhi_fill_rx_ring(sc, sc->ring0);
> +	}
> +
> +	if (error =3D=3D 0)
> +		error =3D tbdev_add_interface(sc);
> +
> +	if ((error =3D=3D 0) && (NHI_USE_ICM(sc)))
> +		tb_printf(sc, "WARN: device uses an internal connection =
manager\n");
> +	if ((error =3D=3D 0) && (NHI_USE_HCM(sc)))
> +		;
> +	error =3D hcm_attach(sc);
> +
> +	if (error =3D=3D 0)
> +		error =3D nhi_init(sc);
> +
> +	return (error);
> +}
> +
> +int
> +nhi_detach(struct nhi_softc *sc)
> +{
> +
> +	if (NHI_USE_HCM(sc))
> +		hcm_detach(sc);
> +
> +	if (sc->root_rsc !=3D 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 =3D arg;
> +	if (error =3D=3D 0 && nsegs =3D=3D 1) {
> +		*addr =3D segs[0].ds_addr;
> +	} else
> +		*addr =3D 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 >=3D 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 =3D rx_depth * sizeof(void *);
> +	txring_len =3D tx_depth * sizeof(void *);
> +	r =3D malloc(sizeof(struct nhi_ring_pair) + rxring_len + =
txring_len,
> +	    M_NHI, M_NOWAIT|M_ZERO);
> +	if (r =3D=3D NULL) {
> +		tb_printf(sc, "ERROR: Cannot allocate ring memory\n");
> +		return (ENOMEM);
> +	}
> +
> +	r->sc =3D sc;
> +	TAILQ_INIT(&r->tx_head);
> +	TAILQ_INIT(&r->rx_head);
> +	r->ring_num =3D ringnum;
> +	r->tx_ring_depth =3D tx_depth;
> +	r->tx_ring_mask =3D tx_depth - 1;
> +	r->rx_ring_depth =3D rx_depth;
> +	r->rx_ring_mask =3D rx_depth - 1;
> +	r->rx_pici_reg =3D NHI_RX_RING_PICI + ringnum * 16;
> +	r->tx_pici_reg =3D NHI_TX_RING_PICI + ringnum * 16;
> +	r->rx_cmd_ring =3D (struct nhi_cmd_frame **)((uint8_t *)r + =
sizeof (*r));
> +	r->tx_cmd_ring =3D (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 =3D sizeof(struct nhi_tx_buffer_desc) * =
r->tx_ring_depth;
> +	ring_size +=3D sizeof(struct nhi_rx_buffer_desc) * =
r->rx_ring_depth;
> +	tb_debug(sc, DBG_INIT | DBG_FULL, "Ring %d ring_size=3D %d\n",
> +	    ringnum, ring_size);
> +
> +	bus_dma_template_init(&t, sc->parent_dmat);
> +	t.alignment =3D 4;
> +	t.maxsize =3D t.maxsegsize =3D ring_size;
> +	t.nsegments =3D 1;
> +	if ((error =3D bus_dma_template_tag(&t, &r->ring_dmat)) !=3D 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 =3D ring;
> +
> +	r->tx_ring =3D (union nhi_ring_desc *)(ring);
> +	r->tx_ring_busaddr =3D ring_busaddr;
> +	ring +=3D sizeof(struct nhi_tx_buffer_desc) * r->tx_ring_depth;
> +	ring_busaddr +=3D sizeof(struct nhi_tx_buffer_desc) * =
r->tx_ring_depth;
> +
> +	r->rx_ring =3D (union nhi_ring_desc *)(ring);
> +	r->rx_ring_busaddr =3D 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 =3D 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 !=3D 0) {
> +		bus_dmamap_unload(r->ring_dmat, r->ring_map);
> +		r->tx_ring_busaddr =3D 0;
> +	}
> +	if (r->ring !=3D NULL) {
> +		bus_dmamem_free(r->ring_dmat, r->ring, r->ring_map);
> +		r->ring =3D NULL;
> +	}
> +	if (r->ring_dmat !=3D NULL) {
> +		bus_dma_tag_destroy(r->ring_dmat);
> +		r->ring_dmat =3D NULL;
> +	}
> +	mtx_destroy(&r->mtx);
> +}
> +
> +static void
> +nhi_free_rings(struct nhi_softc *sc)
> +{
> +	struct nhi_ring_pair *r;
> +
> +	while ((r =3D SLIST_FIRST(&sc->ring_list)) !=3D 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 =3D ring->ring_num * 16;
> +
> +	/* Program the TX ring address and size */
> +	busaddr =3D 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=3D 0x%x\n",
> +	    ring->ring_num, ring->tx_ring_depth);
> +
> +	/* Program the RX ring address and size */
> +	busaddr =3D ring->rx_ring_busaddr;
> +	val =3D (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=3D 0x%x\n",
> +	    ring->ring_num, val);
> +
> +	return (0);
> +}
> +
> +static int
> +nhi_activate_ring(struct nhi_ring_pair *ring)
> +{
> +	struct nhi_softc *sc =3D ring->sc;
> +	int idx;
> +
> +	nhi_pci_enable_interrupt(ring);
> +
> +	idx =3D 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 =3D r->sc;
> +	int idx;
> +
> +	idx =3D 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 =3D 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 =3D nhi_alloc_ring(sc, 0, NHI_RING0_TX_DEPTH,
> +	    NHI_RING0_RX_DEPTH, &r)) !=3D 0) {
> +		tb_printf(sc, "Error allocating control ring\n");
> +		return (error);
> +	}
> +
> +	r->rx_buffer_size =3D NHI_RING0_FRAME_SIZE;/* Control packets =
are small */
> +
> +	/* Allocate the RX and TX buffers that are used for Ring0 comms =
*/
> +	size =3D r->tx_ring_depth * NHI_RING0_FRAME_SIZE;
> +	size +=3D r->rx_ring_depth * NHI_RING0_FRAME_SIZE;
> +
> +	bus_dma_template_init(&t, sc->parent_dmat);
> +	t.maxsize =3D t.maxsegsize =3D size;
> +	t.nsegments =3D 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) !=3D 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 =3D frames_busaddr;
> +	sc->ring0_frames =3D frames;
> +
> +	/* Allocate the driver command trackers */
> +	sc->ring0_cmds =3D malloc(sizeof(struct nhi_cmd_frame) *
> +	    (r->tx_ring_depth + r->rx_ring_depth), M_NHI, M_NOWAIT | =
M_ZERO);
> +	if (sc->ring0_cmds =3D=3D NULL)
> +		return (ENOMEM);
> +
> +	/* Initialize the RX frames so they can be used */
> +	mtx_lock(&r->mtx);
> +	for (i =3D 0; i < r->rx_ring_depth; i++) {
> +		cmd =3D &sc->ring0_cmds[i];
> +		cmd->data =3D (uint32_t *)(frames + NHI_RING0_FRAME_SIZE =
* i);
> +		cmd->data_busaddr =3D frames_busaddr + =
NHI_RING0_FRAME_SIZE * i;
> +		cmd->flags =3D CMD_MAPPED;
> +		cmd->idx =3D 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 =3D &sc->ring0_cmds[i];
> +		cmd->data =3D (uint32_t *)(frames + NHI_RING0_FRAME_SIZE =
* i);
> +		cmd->data_busaddr =3D frames_busaddr + =
NHI_RING0_FRAME_SIZE * i;
> +		cmd->flags =3D CMD_MAPPED;
> +		cmd->idx =3D 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 =3D &sc->intr_trackers[0];
> +	trkr->ring =3D r;
> +	r->tracker =3D trkr;
> +
> +	/* XXX Should be an array */
> +	sc->ring0 =3D 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 !=3D NULL) {
> +		free(sc->ring0_cmds, M_NHI);
> +		sc->ring0_cmds =3D NULL;
> +	}
> +
> +	if (sc->ring0_frames_busaddr !=3D 0) {
> +		bus_dmamap_unload(sc->ring0_dmat, sc->ring0_map);
> +		sc->ring0_frames_busaddr =3D 0;
> *** 5529 LINES SKIPPED ***






Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?76A05154-90E6-4237-851F-EFA35D29D3CC>