xref: /freebsd/sys/dev/thunderbolt/hcm.c (revision 2ed9833791f28e14843ac813f90cb030e45948dc)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Scott Long
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "opt_thunderbolt.h"
30 
31 /* Host Configuration Manager (HCM) for USB4 and later TB3 */
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 #include <sys/malloc.h>
40 #include <sys/queue.h>
41 #include <sys/sysctl.h>
42 #include <sys/lock.h>
43 #include <sys/mutex.h>
44 #include <sys/taskqueue.h>
45 #include <sys/gsb_crc32.h>
46 #include <sys/endian.h>
47 #include <vm/vm.h>
48 #include <vm/pmap.h>
49 
50 #include <machine/bus.h>
51 #include <machine/stdarg.h>
52 
53 #include <dev/thunderbolt/nhi_reg.h>
54 #include <dev/thunderbolt/nhi_var.h>
55 #include <dev/thunderbolt/tb_reg.h>
56 #include <dev/thunderbolt/tb_var.h>
57 #include <dev/thunderbolt/tb_debug.h>
58 #include <dev/thunderbolt/tbcfg_reg.h>
59 #include <dev/thunderbolt/router_var.h>
60 #include <dev/thunderbolt/hcm_var.h>
61 
62 static void hcm_cfg_task(void *, int);
63 
64 int
hcm_attach(struct nhi_softc * nsc)65 hcm_attach(struct nhi_softc *nsc)
66 {
67 	struct hcm_softc *hcm;
68 
69 	tb_debug(nsc, DBG_HCM|DBG_EXTRA, "hcm_attach called\n");
70 
71 	hcm = malloc(sizeof(struct hcm_softc), M_THUNDERBOLT, M_NOWAIT|M_ZERO);
72 	if (hcm == NULL) {
73 		tb_debug(nsc, DBG_HCM, "Cannot allocate hcm object\n");
74 		return (ENOMEM);
75 	}
76 
77 	hcm->dev = nsc->dev;
78 	hcm->nsc = nsc;
79 	nsc->hcm = hcm;
80 
81 	hcm->taskqueue = taskqueue_create("hcm_event", M_NOWAIT,
82 	    taskqueue_thread_enqueue, &hcm->taskqueue);
83 	if (hcm->taskqueue == NULL)
84 		return (ENOMEM);
85 	taskqueue_start_threads(&hcm->taskqueue, 1, PI_DISK, "tbhcm%d_tq",
86 	    device_get_unit(nsc->dev));
87 	TASK_INIT(&hcm->cfg_task, 0, hcm_cfg_task, hcm);
88 
89 	return (0);
90 }
91 
92 int
hcm_detach(struct nhi_softc * nsc)93 hcm_detach(struct nhi_softc *nsc)
94 {
95 	struct hcm_softc *hcm;
96 
97 	hcm = nsc->hcm;
98 	if (hcm->taskqueue)
99 		taskqueue_free(hcm->taskqueue);
100 
101 	return (0);
102 }
103 
104 int
hcm_router_discover(struct hcm_softc * hcm)105 hcm_router_discover(struct hcm_softc *hcm)
106 {
107 
108 	taskqueue_enqueue(hcm->taskqueue, &hcm->cfg_task);
109 
110 	return (0);
111 }
112 
113 static void
hcm_cfg_task(void * arg,int pending)114 hcm_cfg_task(void *arg, int pending)
115 {
116 	struct hcm_softc *hcm;
117 	struct router_softc *rsc;
118 	struct router_cfg_cap cap;
119 	struct tb_cfg_router *cfg;
120 	struct tb_cfg_adapter *adp;
121 	struct tb_cfg_cap_lane *lane;
122 	uint32_t *buf;
123 	uint8_t *u;
124 	u_int error, i, offset;
125 
126 	hcm = (struct hcm_softc *)arg;
127 
128 	tb_debug(hcm, DBG_HCM|DBG_EXTRA, "hcm_cfg_task called\n");
129 
130 	buf = malloc(8 * 4, M_THUNDERBOLT, M_NOWAIT|M_ZERO);
131 	if (buf == NULL) {
132 		tb_debug(hcm, DBG_HCM, "Cannot alloc memory for discovery\n");
133 		return;
134 	}
135 
136 	rsc = hcm->nsc->root_rsc;
137 	error = tb_config_router_read(rsc, 0, 5, buf);
138 	if (error != 0) {
139 		free(buf, M_NHI);
140 		return;
141 	}
142 
143 	cfg = (struct tb_cfg_router *)buf;
144 
145 	cap.space = TB_CFG_CS_ROUTER;
146 	cap.adap = 0;
147 	cap.next_cap = GET_ROUTER_CS_NEXT_CAP(cfg);
148 	while (cap.next_cap != 0) {
149 		error = tb_config_next_cap(rsc, &cap);
150 		if (error != 0)
151 			break;
152 
153 		if ((cap.cap_id == TB_CFG_CAP_VSEC) && (cap.vsc_len == 0)) {
154 			tb_debug(hcm, DBG_HCM, "Router Cap= %d, vsec= %d, "
155 			    "len= %d, next_cap= %d\n", cap.cap_id,
156 			    cap.vsc_id, cap.vsec_len, cap.next_cap);
157 		} else if (cap.cap_id == TB_CFG_CAP_VSC) {
158 			tb_debug(hcm, DBG_HCM, "Router cap= %d, vsc= %d, "
159 			    "len= %d, next_cap= %d\n", cap.cap_id,
160 			    cap.vsc_id, cap.vsc_len, cap.next_cap);
161 		} else
162 			tb_debug(hcm, DBG_HCM, "Router cap= %d, "
163 			    "next_cap= %d\n", cap.cap_id, cap.next_cap);
164 		if (cap.next_cap > TB_CFG_CAP_OFFSET_MAX)
165 			cap.next_cap = 0;
166 	}
167 
168 	u = (uint8_t *)buf;
169 	error = tb_config_get_lc_uuid(rsc, u);
170 	if (error == 0) {
171 		tb_debug(hcm, DBG_HCM, "Router LC UUID: %02x%02x%02x%02x-"
172 		    "%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
173 		    u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
174 		    u[9], u[10], u[11], u[12], u[13], u[14], u[15]);
175 	} else
176 		tb_printf(hcm, "Error finding LC registers: %d\n", error);
177 
178 	for (i = 1; i <= rsc->max_adap; i++) {
179 		error = tb_config_adapter_read(rsc, i, 0, 8, buf);
180 		if (error != 0) {
181 			tb_debug(hcm, DBG_HCM, "Adapter %d: no adapter\n", i);
182 			continue;
183 		}
184 		adp = (struct tb_cfg_adapter *)buf;
185 		tb_debug(hcm, DBG_HCM, "Adapter %d: %s, max_counters= 0x%08x,"
186 		    " adapter_num= %d\n", i,
187 		    tb_get_string(GET_ADP_CS_TYPE(adp), tb_adapter_type),
188 		    GET_ADP_CS_MAX_COUNTERS(adp), GET_ADP_CS_ADP_NUM(adp));
189 
190 		if (GET_ADP_CS_TYPE(adp) != ADP_CS2_LANE)
191 			continue;
192 
193 		error = tb_config_find_adapter_cap(rsc, i, TB_CFG_CAP_LANE,
194 		    &offset);
195 		if (error)
196 			continue;
197 
198 		error = tb_config_adapter_read(rsc, i, offset, 3, buf);
199 		if (error)
200 			continue;
201 
202 		lane = (struct tb_cfg_cap_lane *)buf;
203 		tb_debug(hcm, DBG_HCM, "Lane Adapter State= %s %s\n",
204 		    tb_get_string((lane->current_lws & CAP_LANE_STATE_MASK),
205 		    tb_adapter_state), (lane->targ_lwp & CAP_LANE_DISABLE) ?
206 		    "disabled" : "enabled");
207 
208 		if ((lane->current_lws & CAP_LANE_STATE_MASK) ==
209 		    CAP_LANE_STATE_CL0) {
210 			tb_route_t newr;
211 
212 			newr.hi = rsc->route.hi;
213 			newr.lo = rsc->route.lo | (i << rsc->depth * 8);
214 
215 			tb_printf(hcm, "want to add router at 0x%08x%08x\n",
216 			    newr.hi, newr.lo);
217 			error = tb_router_attach(rsc, newr);
218 			tb_printf(rsc, "tb_router_attach returned %d\n", error);
219 		}
220 	}
221 
222 	free(buf, M_THUNDERBOLT);
223 }
224