Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

thunderbolt: Add support for DMA tunnels

In addition to PCIe and Display Port tunnels it is also possible to
create tunnels that forward DMA traffic from the host interface adapter
(NHI) to a NULL port that is connected to another domain through a
Thunderbolt cable. These tunnels can be used to carry software messages
such as networking packets.

To support this we introduce another tunnel type (TB_TUNNEL_DMA) that
supports paths from NHI to NULL port and back.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>

+149 -5
+18 -4
drivers/thunderbolt/path.c
··· 341 341 } 342 342 } 343 343 344 - static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index) 344 + static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index, 345 + bool clear_fc) 345 346 { 346 347 struct tb_regs_hop hop; 347 348 ktime_t timeout; ··· 370 369 if (ret) 371 370 return ret; 372 371 373 - if (!hop.pending) 372 + if (!hop.pending) { 373 + if (clear_fc) { 374 + /* Clear flow control */ 375 + hop.ingress_fc = 0; 376 + hop.egress_fc = 0; 377 + hop.ingress_shared_buffer = 0; 378 + hop.egress_shared_buffer = 0; 379 + 380 + return tb_port_write(port, &hop, TB_CFG_HOPS, 381 + 2 * hop_index, 2); 382 + } 383 + 374 384 return 0; 385 + } 375 386 376 387 usleep_range(10, 20); 377 388 } while (ktime_before(ktime_get(), timeout)); ··· 397 384 398 385 for (i = first_hop; i < path->path_length; i++) { 399 386 res = __tb_path_deactivate_hop(path->hops[i].in_port, 400 - path->hops[i].in_hop_index); 387 + path->hops[i].in_hop_index, 388 + path->clear_fc); 401 389 if (res && res != -ENODEV) 402 390 tb_port_warn(path->hops[i].in_port, 403 391 "hop deactivation failed for hop %d, index %d\n", ··· 473 459 474 460 /* If it is left active deactivate it first */ 475 461 __tb_path_deactivate_hop(path->hops[i].in_port, 476 - path->hops[i].in_hop_index); 462 + path->hops[i].in_hop_index, path->clear_fc); 477 463 478 464 /* dword 0 */ 479 465 hop.next_hop = path->hops[i].next_hop_index;
+22
drivers/thunderbolt/switch.c
··· 556 556 } 557 557 558 558 /** 559 + * tb_port_set_initial_credits() - Set initial port link credits allocated 560 + * @port: Port to set the initial credits 561 + * @credits: Number of credits to to allocate 562 + * 563 + * Set initial credits value to be used for ingress shared buffering. 564 + */ 565 + int tb_port_set_initial_credits(struct tb_port *port, u32 credits) 566 + { 567 + u32 data; 568 + int ret; 569 + 570 + ret = tb_port_read(port, &data, TB_CFG_PORT, 5, 1); 571 + if (ret) 572 + return ret; 573 + 574 + data &= ~TB_PORT_LCA_MASK; 575 + data |= (credits << TB_PORT_LCA_SHIFT) & TB_PORT_LCA_MASK; 576 + 577 + return tb_port_write(port, &data, TB_CFG_PORT, 5, 1); 578 + } 579 + 580 + /** 559 581 * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER 560 582 * 561 583 * Return: Returns 0 on success or an error code on failure.
+4
drivers/thunderbolt/tb.h
··· 207 207 * @weight: Weight of the path inside the priority group 208 208 * @drop_packages: Drop packages from queue tail or head 209 209 * @activated: Is the path active 210 + * @clear_fc: Clear all flow control from the path config space entries 211 + * when deactivating this path 210 212 * @hops: Path hops 211 213 * @path_length: How many hops the path uses 212 214 * ··· 229 227 int weight:4; 230 228 bool drop_packages; 231 229 bool activated; 230 + bool clear_fc; 232 231 struct tb_path_hop *hops; 233 232 int path_length; 234 233 }; ··· 586 583 587 584 int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); 588 585 int tb_port_add_nfc_credits(struct tb_port *port, int credits); 586 + int tb_port_set_initial_credits(struct tb_port *port, u32 credits); 589 587 int tb_port_clear_counter(struct tb_port *port, int counter); 590 588 int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid); 591 589 void tb_port_release_in_hopid(struct tb_port *port, int hopid);
+3
drivers/thunderbolt/tb_regs.h
··· 215 215 #define TB_PORT_NFC_CREDITS_MASK GENMASK(19, 0) 216 216 #define TB_PORT_MAX_CREDITS_SHIFT 20 217 217 #define TB_PORT_MAX_CREDITS_MASK GENMASK(26, 20) 218 + /* DWORD 5 */ 219 + #define TB_PORT_LCA_SHIFT 22 220 + #define TB_PORT_LCA_MASK GENMASK(28, 22) 218 221 219 222 /* Display Port adapter registers */ 220 223
+92 -1
drivers/thunderbolt/tunnel.c
··· 27 27 #define TB_DP_AUX_PATH_OUT 1 28 28 #define TB_DP_AUX_PATH_IN 2 29 29 30 - static const char * const tb_tunnel_names[] = { "PCI", "DP" }; 30 + #define TB_DMA_PATH_OUT 0 31 + #define TB_DMA_PATH_IN 1 32 + 33 + static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA" }; 31 34 32 35 #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ 33 36 do { \ ··· 472 469 err_free: 473 470 tb_tunnel_free(tunnel); 474 471 return NULL; 472 + } 473 + 474 + static u32 tb_dma_credits(struct tb_port *nhi) 475 + { 476 + u32 max_credits; 477 + 478 + max_credits = (nhi->config.nfc_credits & TB_PORT_MAX_CREDITS_MASK) >> 479 + TB_PORT_MAX_CREDITS_SHIFT; 480 + return min(max_credits, 13U); 481 + } 482 + 483 + static int tb_dma_activate(struct tb_tunnel *tunnel, bool active) 484 + { 485 + struct tb_port *nhi = tunnel->src_port; 486 + u32 credits; 487 + 488 + credits = active ? tb_dma_credits(nhi) : 0; 489 + return tb_port_set_initial_credits(nhi, credits); 490 + } 491 + 492 + static void tb_dma_init_path(struct tb_path *path, unsigned int isb, 493 + unsigned int efc, u32 credits) 494 + { 495 + int i; 496 + 497 + path->egress_fc_enable = efc; 498 + path->ingress_fc_enable = TB_PATH_ALL; 499 + path->egress_shared_buffer = TB_PATH_NONE; 500 + path->ingress_shared_buffer = isb; 501 + path->priority = 5; 502 + path->weight = 1; 503 + path->clear_fc = true; 504 + 505 + for (i = 0; i < path->path_length; i++) 506 + path->hops[i].initial_credits = credits; 507 + } 508 + 509 + /** 510 + * tb_tunnel_alloc_dma() - allocate a DMA tunnel 511 + * @tb: Pointer to the domain structure 512 + * @nhi: Host controller port 513 + * @dst: Destination null port which the other domain is connected to 514 + * @transmit_ring: NHI ring number used to send packets towards the 515 + * other domain 516 + * @transmit_path: HopID used for transmitting packets 517 + * @receive_ring: NHI ring number used to receive packets from the 518 + * other domain 519 + * @reveive_path: HopID used for receiving packets 520 + * 521 + * Return: Returns a tb_tunnel on success or NULL on failure. 522 + */ 523 + struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, 524 + struct tb_port *dst, int transmit_ring, 525 + int transmit_path, int receive_ring, 526 + int receive_path) 527 + { 528 + struct tb_tunnel *tunnel; 529 + struct tb_path *path; 530 + u32 credits; 531 + 532 + tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_DMA); 533 + if (!tunnel) 534 + return NULL; 535 + 536 + tunnel->activate = tb_dma_activate; 537 + tunnel->src_port = nhi; 538 + tunnel->dst_port = dst; 539 + 540 + credits = tb_dma_credits(nhi); 541 + 542 + path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, "DMA RX"); 543 + if (!path) { 544 + tb_tunnel_free(tunnel); 545 + return NULL; 546 + } 547 + tb_dma_init_path(path, TB_PATH_NONE, TB_PATH_SOURCE | TB_PATH_INTERNAL, 548 + credits); 549 + tunnel->paths[TB_DMA_PATH_IN] = path; 550 + 551 + path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0, "DMA TX"); 552 + if (!path) { 553 + tb_tunnel_free(tunnel); 554 + return NULL; 555 + } 556 + tb_dma_init_path(path, TB_PATH_SOURCE, TB_PATH_ALL, credits); 557 + tunnel->paths[TB_DMA_PATH_OUT] = path; 558 + 559 + return tunnel; 475 560 } 476 561 477 562 /**
+10
drivers/thunderbolt/tunnel.h
··· 14 14 enum tb_tunnel_type { 15 15 TB_TUNNEL_PCI, 16 16 TB_TUNNEL_DP, 17 + TB_TUNNEL_DMA, 17 18 }; 18 19 19 20 /** ··· 48 47 struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in); 49 48 struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, 50 49 struct tb_port *out); 50 + struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, 51 + struct tb_port *dst, int transmit_ring, 52 + int transmit_path, int receive_ring, 53 + int receive_path); 51 54 52 55 void tb_tunnel_free(struct tb_tunnel *tunnel); 53 56 int tb_tunnel_activate(struct tb_tunnel *tunnel); ··· 67 62 static inline bool tb_tunnel_is_dp(const struct tb_tunnel *tunnel) 68 63 { 69 64 return tunnel->type == TB_TUNNEL_DP; 65 + } 66 + 67 + static inline bool tb_tunnel_is_dma(const struct tb_tunnel *tunnel) 68 + { 69 + return tunnel->type == TB_TUNNEL_DMA; 70 70 } 71 71 72 72 #endif