1
2 /*
3 * Copyright 2022 Advanced Micro Devices, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Authors: AMD
24 *
25 */
26 /*********************************************************************/
27 // USB4 DPIA BANDWIDTH ALLOCATION LOGIC
28 /*********************************************************************/
29 #include "link_dp_dpia_bw.h"
30 #include "link_dpcd.h"
31 #include "dc_dmub_srv.h"
32
33 #define DC_LOGGER \
34 link->ctx->logger
35
36 #define Kbps_TO_Gbps (1000 * 1000)
37
38 #define MST_TIME_SLOT_COUNT 64
39
40 // ------------------------------------------------------------------
41 // PRIVATE FUNCTIONS
42 // ------------------------------------------------------------------
43 /*
44 * Always Check the following:
45 * - Is it USB4 link?
46 * - Is HPD HIGH?
47 * - Is BW Allocation Support Mode enabled on DP-Tx?
48 */
link_dp_is_bw_alloc_available(struct dc_link * link)49 static bool link_dp_is_bw_alloc_available(struct dc_link *link)
50 {
51 return (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling
52 && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dpia_bw_alloc
53 && link->dpcd_caps.usb4_dp_tun_info.driver_bw_cap.bits.driver_bw_alloc_support);
54 }
55
reset_bw_alloc_struct(struct dc_link * link)56 static void reset_bw_alloc_struct(struct dc_link *link)
57 {
58 link->dpia_bw_alloc_config.bw_alloc_enabled = false;
59 link->dpia_bw_alloc_config.link_verified_bw = 0;
60 link->dpia_bw_alloc_config.link_max_bw = 0;
61 link->dpia_bw_alloc_config.allocated_bw = 0;
62 link->dpia_bw_alloc_config.estimated_bw = 0;
63 link->dpia_bw_alloc_config.bw_granularity = 0;
64 link->dpia_bw_alloc_config.dp_overhead = 0;
65 link->dpia_bw_alloc_config.nrd_max_lane_count = 0;
66 link->dpia_bw_alloc_config.nrd_max_link_rate = 0;
67 for (int i = 0; i < MAX_SINKS_PER_LINK; i++)
68 link->dpia_bw_alloc_config.remote_sink_req_bw[i] = 0;
69 DC_LOG_DEBUG("reset usb4 bw alloc of link(%d)\n", link->link_index);
70 }
71
72 #define BW_GRANULARITY_0 4 // 0.25 Gbps
73 #define BW_GRANULARITY_1 2 // 0.5 Gbps
74 #define BW_GRANULARITY_2 1 // 1 Gbps
75
get_bw_granularity(struct dc_link * link)76 static uint8_t get_bw_granularity(struct dc_link *link)
77 {
78 uint8_t bw_granularity = 0;
79
80 core_link_read_dpcd(
81 link,
82 DP_BW_GRANULALITY,
83 &bw_granularity,
84 sizeof(uint8_t));
85
86 switch (bw_granularity & 0x3) {
87 case 0:
88 bw_granularity = BW_GRANULARITY_0;
89 break;
90 case 1:
91 bw_granularity = BW_GRANULARITY_1;
92 break;
93 case 2:
94 default:
95 bw_granularity = BW_GRANULARITY_2;
96 break;
97 }
98
99 return bw_granularity;
100 }
101
get_estimated_bw(struct dc_link * link)102 static int get_estimated_bw(struct dc_link *link)
103 {
104 uint8_t bw_estimated_bw = 0;
105
106 if (link->dpia_bw_alloc_config.bw_granularity == 0) {
107 DC_LOG_ERROR("%s: BW granularity is zero!\n", __func__);
108 return 0;
109 }
110
111 core_link_read_dpcd(
112 link,
113 ESTIMATED_BW,
114 &bw_estimated_bw,
115 sizeof(uint8_t));
116
117 return bw_estimated_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
118 }
119
get_non_reduced_max_link_rate(struct dc_link * link)120 static uint8_t get_non_reduced_max_link_rate(struct dc_link *link)
121 {
122 uint8_t nrd_max_link_rate = 0;
123
124 core_link_read_dpcd(
125 link,
126 DP_TUNNELING_MAX_LINK_RATE,
127 &nrd_max_link_rate,
128 sizeof(uint8_t));
129
130 return nrd_max_link_rate;
131 }
132
get_non_reduced_max_lane_count(struct dc_link * link)133 static uint8_t get_non_reduced_max_lane_count(struct dc_link *link)
134 {
135 uint8_t nrd_max_lane_count = 0;
136
137 core_link_read_dpcd(
138 link,
139 DP_TUNNELING_MAX_LANE_COUNT,
140 &nrd_max_lane_count,
141 sizeof(uint8_t));
142
143 return nrd_max_lane_count;
144 }
145
146 /*
147 * Read all New BW alloc configuration ex: estimated_bw, allocated_bw,
148 * granuality, Driver_ID, CM_Group, & populate the BW allocation structs
149 * for host router and dpia
150 */
retrieve_usb4_dp_bw_allocation_info(struct dc_link * link)151 static void retrieve_usb4_dp_bw_allocation_info(struct dc_link *link)
152 {
153 reset_bw_alloc_struct(link);
154
155 /* init the known values */
156 link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link);
157 link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
158 link->dpia_bw_alloc_config.nrd_max_link_rate = get_non_reduced_max_link_rate(link);
159 link->dpia_bw_alloc_config.nrd_max_lane_count = get_non_reduced_max_lane_count(link);
160
161 DC_LOG_DEBUG("%s: bw_granularity(%d), estimated_bw(%d)\n",
162 __func__, link->dpia_bw_alloc_config.bw_granularity,
163 link->dpia_bw_alloc_config.estimated_bw);
164 DC_LOG_DEBUG("%s: nrd_max_link_rate(%d), nrd_max_lane_count(%d)\n",
165 __func__, link->dpia_bw_alloc_config.nrd_max_link_rate,
166 link->dpia_bw_alloc_config.nrd_max_lane_count);
167 }
168
169 /*
170 * Cleanup function for when the dpia is unplugged to reset struct
171 * and perform any required clean up
172 *
173 * @link: pointer to the dc_link struct instance
174 *
175 * return: none
176 */
dpia_bw_alloc_unplug(struct dc_link * link)177 static void dpia_bw_alloc_unplug(struct dc_link *link)
178 {
179 if (link) {
180 DC_LOG_DEBUG("%s: resetting BW alloc config for link(%d)\n",
181 __func__, link->link_index);
182 reset_bw_alloc_struct(link);
183 }
184 }
185
link_dpia_send_bw_alloc_request(struct dc_link * link,int req_bw)186 static void link_dpia_send_bw_alloc_request(struct dc_link *link, int req_bw)
187 {
188 uint32_t request_reg_val;
189 uint32_t temp, request_bw;
190
191 if (link->dpia_bw_alloc_config.bw_granularity == 0) {
192 DC_LOG_ERROR("%s: Link[%d]: bw_granularity is zero!", __func__, link->link_index);
193 return;
194 }
195
196 temp = req_bw * link->dpia_bw_alloc_config.bw_granularity;
197 request_reg_val = temp / Kbps_TO_Gbps;
198
199 /* Always make sure to add more to account for floating points */
200 if (temp % Kbps_TO_Gbps)
201 ++request_reg_val;
202
203 request_bw = request_reg_val * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity);
204
205 if (request_bw > (uint32_t)link->dpia_bw_alloc_config.estimated_bw) {
206 DC_LOG_ERROR("%s: Link[%d]: Request BW (%d --> %d) > Estimated BW (%d)... Set to Estimated BW!",
207 __func__, link->link_index,
208 req_bw, request_bw, link->dpia_bw_alloc_config.estimated_bw);
209 req_bw = link->dpia_bw_alloc_config.estimated_bw;
210
211 temp = req_bw * link->dpia_bw_alloc_config.bw_granularity;
212 request_reg_val = temp / Kbps_TO_Gbps;
213 if (temp % Kbps_TO_Gbps)
214 ++request_reg_val;
215 }
216
217 link->dpia_bw_alloc_config.allocated_bw = request_bw;
218 DC_LOG_DC("%s: Link[%d]: Request BW: %d", __func__, link->link_index, request_bw);
219
220 uint8_t requested_bw_dpcd = (uint8_t)request_reg_val;
221 core_link_write_dpcd(link, REQUESTED_BW, &requested_bw_dpcd,
222 sizeof(uint8_t));
223 }
224
225 // ------------------------------------------------------------------
226 // PUBLIC FUNCTIONS
227 // ------------------------------------------------------------------
link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link * link)228 bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link)
229 {
230 bool ret = false;
231 uint8_t val;
232
233 val = DPTX_BW_ALLOC_MODE_ENABLE | DPTX_BW_ALLOC_UNMASK_IRQ;
234
235 if (core_link_write_dpcd(link, DPTX_BW_ALLOCATION_MODE_CONTROL, &val, sizeof(uint8_t)) == DC_OK) {
236 DC_LOG_DEBUG("%s: link[%d] DPTX BW allocation mode enabled", __func__, link->link_index);
237
238 retrieve_usb4_dp_bw_allocation_info(link);
239
240 if (
241 link->dpia_bw_alloc_config.nrd_max_link_rate
242 && link->dpia_bw_alloc_config.nrd_max_lane_count) {
243 link->reported_link_cap.link_rate = link->dpia_bw_alloc_config.nrd_max_link_rate;
244 link->reported_link_cap.lane_count = link->dpia_bw_alloc_config.nrd_max_lane_count;
245 }
246
247 link->dpia_bw_alloc_config.bw_alloc_enabled = true;
248 ret = true;
249
250 if (link->dc->debug.dpia_debug.bits.enable_usb4_bw_zero_alloc_patch) {
251 /*
252 * During DP tunnel creation, the CM preallocates BW
253 * and reduces the estimated BW of other DPIAs.
254 * The CM releases the preallocation only when the allocation is complete.
255 * Perform a zero allocation to make the CM release the preallocation
256 * and correctly update the estimated BW for all DPIAs per host router.
257 */
258 link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, 0);
259 }
260 } else
261 DC_LOG_DEBUG("%s: link[%d] failed to enable DPTX BW allocation mode", __func__, link->link_index);
262
263 return ret;
264 }
265
266 /*
267 * Handle DP BW allocation status register
268 *
269 * @link: pointer to the dc_link struct instance
270 * @status: content of DP tunneling status DPCD register
271 *
272 * return: none
273 */
link_dp_dpia_handle_bw_alloc_status(struct dc_link * link,uint8_t status)274 void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status)
275 {
276 if (status & DP_TUNNELING_BW_REQUEST_SUCCEEDED) {
277 DC_LOG_DEBUG("%s: BW Allocation request succeeded on link(%d)",
278 __func__, link->link_index);
279 }
280
281 if (status & DP_TUNNELING_BW_REQUEST_FAILED) {
282 DC_LOG_DEBUG("%s: BW Allocation request failed on link(%d) allocated/estimated BW=%d",
283 __func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
284
285 link_dpia_send_bw_alloc_request(link, link->dpia_bw_alloc_config.estimated_bw);
286 }
287
288 if (status & DP_TUNNELING_BW_ALLOC_CAP_CHANGED) {
289 link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link);
290
291 DC_LOG_DEBUG("%s: Granularity changed on link(%d) new granularity=%d",
292 __func__, link->link_index, link->dpia_bw_alloc_config.bw_granularity);
293 }
294
295 if (status & DP_TUNNELING_ESTIMATED_BW_CHANGED) {
296 link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
297
298 DC_LOG_DEBUG("%s: Estimated BW changed on link(%d) new estimated BW=%d",
299 __func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
300 }
301
302 core_link_write_dpcd(
303 link, DP_TUNNELING_STATUS,
304 &status, sizeof(status));
305 }
306
307 /*
308 * Handle the DP Bandwidth allocation for DPIA
309 *
310 */
dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link * link,int peak_bw)311 void dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int peak_bw)
312 {
313 if (link && link->dpcd_caps.usb4_dp_tun_info.dp_tun_cap.bits.dp_tunneling
314 && link->dpia_bw_alloc_config.bw_alloc_enabled) {
315 if (peak_bw > 0) {
316 // If DP over USB4 then we need to check BW allocation
317 link->dpia_bw_alloc_config.link_max_bw = peak_bw;
318
319 link_dpia_send_bw_alloc_request(link, peak_bw);
320 } else
321 dpia_bw_alloc_unplug(link);
322 }
323 }
324
link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link * link,int req_bw)325 void link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw)
326 {
327 link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
328
329 DC_LOG_DEBUG("%s: ENTER: link[%d] hpd(%d) Allocated_BW: %d Estimated_BW: %d Req_BW: %d",
330 __func__, link->link_index, link->hpd_status,
331 link->dpia_bw_alloc_config.allocated_bw,
332 link->dpia_bw_alloc_config.estimated_bw,
333 req_bw);
334
335 if (link_dp_is_bw_alloc_available(link))
336 link_dpia_send_bw_alloc_request(link, req_bw);
337 else
338 DC_LOG_DEBUG("%s: BW Allocation mode not available", __func__);
339 }
340
link_dpia_get_dp_overhead(const struct dc_link * link)341 uint32_t link_dpia_get_dp_overhead(const struct dc_link *link)
342 {
343 uint32_t link_dp_overhead = 0;
344
345 if ((link->type == dc_connection_mst_branch) &&
346 !link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) {
347 /* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH
348 * MST overhead is 1/64 of link bandwidth (excluding any overhead)
349 */
350 const struct dc_link_settings *link_cap = dc_link_get_link_cap(link);
351
352 if (link_cap) {
353 uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate *
354 (uint32_t)link_cap->lane_count *
355 LINK_RATE_REF_FREQ_IN_KHZ * 8;
356 link_dp_overhead = (link_bw_in_kbps / MST_TIME_SLOT_COUNT)
357 + ((link_bw_in_kbps % MST_TIME_SLOT_COUNT) ? 1 : 0);
358 }
359 }
360
361 return link_dp_overhead;
362 }
363
364 /*
365 * Aggregates the DPIA bandwidth usage for the respective USB4 Router.
366 * And then validate if the required bandwidth is within the router's capacity.
367 *
368 * @dc_validation_dpia_set: pointer to the dc_validation_dpia_set
369 * @count: number of DPIA validation sets
370 *
371 * return: true if validation is succeeded
372 */
link_dpia_validate_dp_tunnel_bandwidth(const struct dc_validation_dpia_set * dpia_link_sets,uint8_t count)373 bool link_dpia_validate_dp_tunnel_bandwidth(const struct dc_validation_dpia_set *dpia_link_sets, uint8_t count)
374 {
375 uint32_t granularity_Gbps;
376 const struct dc_link *link;
377 uint32_t link_bw_granularity;
378 uint32_t link_required_bw;
379 struct usb4_router_validation_set router_sets[MAX_HOST_ROUTERS_NUM] = { 0 };
380 uint8_t i;
381 bool is_success = true;
382 uint8_t router_count = 0;
383
384 if ((dpia_link_sets == NULL) || (count == 0))
385 return is_success;
386
387 // Iterate through each DP tunneling link (DPIA).
388 // Aggregate its bandwidth requirements onto the respective USB4 router.
389 for (i = 0; i < count; i++) {
390 link = dpia_link_sets[i].link;
391 link_required_bw = dpia_link_sets[i].required_bw;
392 const struct dc_tunnel_settings *dp_tunnel_settings = dpia_link_sets[i].tunnel_settings;
393
394 if ((link == NULL) || (dp_tunnel_settings == NULL) || dp_tunnel_settings->bw_granularity == 0)
395 break;
396
397 if (link->type == dc_connection_mst_branch)
398 link_required_bw += link_dpia_get_dp_overhead(link);
399
400 granularity_Gbps = (Kbps_TO_Gbps / dp_tunnel_settings->bw_granularity);
401 link_bw_granularity = (link_required_bw / granularity_Gbps) * granularity_Gbps +
402 ((link_required_bw % granularity_Gbps) ? granularity_Gbps : 0);
403
404 // Find or add the USB4 router associated with the current DPIA link
405 for (uint8_t j = 0; j < MAX_HOST_ROUTERS_NUM; j++) {
406 if (router_sets[j].is_valid == false) {
407 router_sets[j].is_valid = true;
408 router_sets[j].cm_id = dp_tunnel_settings->cm_id;
409 router_count++;
410 }
411
412 if (router_sets[j].cm_id == dp_tunnel_settings->cm_id) {
413 uint32_t remaining_bw =
414 dp_tunnel_settings->estimated_bw - dp_tunnel_settings->allocated_bw;
415
416 router_sets[j].allocated_bw += dp_tunnel_settings->allocated_bw;
417
418 if (remaining_bw > router_sets[j].remaining_bw)
419 router_sets[j].remaining_bw = remaining_bw;
420
421 // Get the max estimated BW within the same CM_ID
422 if (dp_tunnel_settings->estimated_bw > router_sets[j].estimated_bw)
423 router_sets[j].estimated_bw = dp_tunnel_settings->estimated_bw;
424
425 router_sets[j].required_bw += link_bw_granularity;
426 router_sets[j].dpia_count++;
427 break;
428 }
429 }
430 }
431
432 // Validate bandwidth for each unique router found.
433 for (i = 0; i < router_count; i++) {
434 uint32_t total_bw = 0;
435
436 if (router_sets[i].is_valid == false)
437 break;
438
439 // Determine the total available bandwidth for the current router based on aggregated data
440 if ((router_sets[i].dpia_count == 1) || (router_sets[i].allocated_bw == 0))
441 total_bw = router_sets[i].estimated_bw;
442 else
443 total_bw = router_sets[i].allocated_bw + router_sets[i].remaining_bw;
444
445 if (router_sets[i].required_bw > total_bw) {
446 is_success = false;
447 break;
448 }
449 }
450
451 return is_success;
452 }
453
454