summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2018-09-28 09:35:32 -0400
committerMika Westerberg <mika.westerberg@linux.intel.com>2019-04-18 04:18:53 -0400
commit44242d6c9703208e7e7abd6b4dbb258a930dd01a (patch)
treee6705e5cbff04126399d3ebb8a8a4677b9aa8ce1
parent3b4b3235ca5bf1b69ff15a269d9881b2604dd4fa (diff)
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>
-rw-r--r--drivers/thunderbolt/path.c22
-rw-r--r--drivers/thunderbolt/switch.c22
-rw-r--r--drivers/thunderbolt/tb.h4
-rw-r--r--drivers/thunderbolt/tb_regs.h3
-rw-r--r--drivers/thunderbolt/tunnel.c93
-rw-r--r--drivers/thunderbolt/tunnel.h10
6 files changed, 149 insertions, 5 deletions
diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
index 3fc92881a197..e4c22f53ef4d 100644
--- a/drivers/thunderbolt/path.c
+++ b/drivers/thunderbolt/path.c
@@ -341,7 +341,8 @@ static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop)
341 } 341 }
342} 342}
343 343
344static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index) 344static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
345 bool clear_fc)
345{ 346{
346 struct tb_regs_hop hop; 347 struct tb_regs_hop hop;
347 ktime_t timeout; 348 ktime_t timeout;
@@ -369,8 +370,20 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index)
369 if (ret) 370 if (ret)
370 return ret; 371 return ret;
371 372
372 if (!hop.pending) 373 if (!hop.pending) {
374 if (clear_fc) {
375 /* Clear flow control */
376 hop.ingress_fc = 0;
377 hop.egress_fc = 0;
378 hop.ingress_shared_buffer = 0;
379 hop.egress_shared_buffer = 0;
380
381 return tb_port_write(port, &hop, TB_CFG_HOPS,
382 2 * hop_index, 2);
383 }
384
373 return 0; 385 return 0;
386 }
374 387
375 usleep_range(10, 20); 388 usleep_range(10, 20);
376 } while (ktime_before(ktime_get(), timeout)); 389 } while (ktime_before(ktime_get(), timeout));
@@ -384,7 +397,8 @@ static void __tb_path_deactivate_hops(struct tb_path *path, int first_hop)
384 397
385 for (i = first_hop; i < path->path_length; i++) { 398 for (i = first_hop; i < path->path_length; i++) {
386 res = __tb_path_deactivate_hop(path->hops[i].in_port, 399 res = __tb_path_deactivate_hop(path->hops[i].in_port,
387 path->hops[i].in_hop_index); 400 path->hops[i].in_hop_index,
401 path->clear_fc);
388 if (res && res != -ENODEV) 402 if (res && res != -ENODEV)
389 tb_port_warn(path->hops[i].in_port, 403 tb_port_warn(path->hops[i].in_port,
390 "hop deactivation failed for hop %d, index %d\n", 404 "hop deactivation failed for hop %d, index %d\n",
@@ -459,7 +473,7 @@ int tb_path_activate(struct tb_path *path)
459 473
460 /* If it is left active deactivate it first */ 474 /* If it is left active deactivate it first */
461 __tb_path_deactivate_hop(path->hops[i].in_port, 475 __tb_path_deactivate_hop(path->hops[i].in_port,
462 path->hops[i].in_hop_index); 476 path->hops[i].in_hop_index, path->clear_fc);
463 477
464 /* dword 0 */ 478 /* dword 0 */
465 hop.next_hop = path->hops[i].next_hop_index; 479 hop.next_hop = path->hops[i].next_hop_index;
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 1de1afa24182..ecf53d986a5c 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -556,6 +556,28 @@ int tb_port_add_nfc_credits(struct tb_port *port, int credits)
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 */
565int 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 * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER 581 * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER
560 * 582 *
561 * Return: Returns 0 on success or an error code on failure. 583 * Return: Returns 0 on success or an error code on failure.
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 89b5d18f6035..119a00837992 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -207,6 +207,8 @@ enum tb_path_port {
207 * @weight: Weight of the path inside the priority group 207 * @weight: Weight of the path inside the priority group
208 * @drop_packages: Drop packages from queue tail or head 208 * @drop_packages: Drop packages from queue tail or head
209 * @activated: Is the path active 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 * @hops: Path hops 212 * @hops: Path hops
211 * @path_length: How many hops the path uses 213 * @path_length: How many hops the path uses
212 * 214 *
@@ -227,6 +229,7 @@ struct tb_path {
227 int weight:4; 229 int weight:4;
228 bool drop_packages; 230 bool drop_packages;
229 bool activated; 231 bool activated;
232 bool clear_fc;
230 struct tb_path_hop *hops; 233 struct tb_path_hop *hops;
231 int path_length; 234 int path_length;
232}; 235};
@@ -583,6 +586,7 @@ static inline bool tb_switch_is_fr(const struct tb_switch *sw)
583 586
584int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); 587int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
585int tb_port_add_nfc_credits(struct tb_port *port, int credits); 588int tb_port_add_nfc_credits(struct tb_port *port, int credits);
589int tb_port_set_initial_credits(struct tb_port *port, u32 credits);
586int tb_port_clear_counter(struct tb_port *port, int counter); 590int tb_port_clear_counter(struct tb_port *port, int counter);
587int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid); 591int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
588void tb_port_release_in_hopid(struct tb_port *port, int hopid); 592void tb_port_release_in_hopid(struct tb_port *port, int hopid);
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 3ce705184e2c..deb9d4a977b9 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -215,6 +215,9 @@ struct tb_regs_port_header {
215#define TB_PORT_NFC_CREDITS_MASK GENMASK(19, 0) 215#define TB_PORT_NFC_CREDITS_MASK GENMASK(19, 0)
216#define TB_PORT_MAX_CREDITS_SHIFT 20 216#define TB_PORT_MAX_CREDITS_SHIFT 20
217#define TB_PORT_MAX_CREDITS_MASK GENMASK(26, 20) 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/* Display Port adapter registers */ 222/* Display Port adapter registers */
220 223
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 0bc6639c6e74..9f9b26b12d0a 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -27,7 +27,10 @@
27#define TB_DP_AUX_PATH_OUT 1 27#define TB_DP_AUX_PATH_OUT 1
28#define TB_DP_AUX_PATH_IN 2 28#define TB_DP_AUX_PATH_IN 2
29 29
30static const char * const tb_tunnel_names[] = { "PCI", "DP" }; 30#define TB_DMA_PATH_OUT 0
31#define TB_DMA_PATH_IN 1
32
33static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA" };
31 34
32#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ 35#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
33 do { \ 36 do { \
@@ -471,6 +474,94 @@ err_free:
471 return NULL; 474 return NULL;
472} 475}
473 476
477static u32 tb_dma_credits(struct tb_port *nhi)
478{
479 u32 max_credits;
480
481 max_credits = (nhi->config.nfc_credits & TB_PORT_MAX_CREDITS_MASK) >>
482 TB_PORT_MAX_CREDITS_SHIFT;
483 return min(max_credits, 13U);
484}
485
486static int tb_dma_activate(struct tb_tunnel *tunnel, bool active)
487{
488 struct tb_port *nhi = tunnel->src_port;
489 u32 credits;
490
491 credits = active ? tb_dma_credits(nhi) : 0;
492 return tb_port_set_initial_credits(nhi, credits);
493}
494
495static void tb_dma_init_path(struct tb_path *path, unsigned int isb,
496 unsigned int efc, u32 credits)
497{
498 int i;
499
500 path->egress_fc_enable = efc;
501 path->ingress_fc_enable = TB_PATH_ALL;
502 path->egress_shared_buffer = TB_PATH_NONE;
503 path->ingress_shared_buffer = isb;
504 path->priority = 5;
505 path->weight = 1;
506 path->clear_fc = true;
507
508 for (i = 0; i < path->path_length; i++)
509 path->hops[i].initial_credits = credits;
510}
511
512/**
513 * tb_tunnel_alloc_dma() - allocate a DMA tunnel
514 * @tb: Pointer to the domain structure
515 * @nhi: Host controller port
516 * @dst: Destination null port which the other domain is connected to
517 * @transmit_ring: NHI ring number used to send packets towards the
518 * other domain
519 * @transmit_path: HopID used for transmitting packets
520 * @receive_ring: NHI ring number used to receive packets from the
521 * other domain
522 * @reveive_path: HopID used for receiving packets
523 *
524 * Return: Returns a tb_tunnel on success or NULL on failure.
525 */
526struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
527 struct tb_port *dst, int transmit_ring,
528 int transmit_path, int receive_ring,
529 int receive_path)
530{
531 struct tb_tunnel *tunnel;
532 struct tb_path *path;
533 u32 credits;
534
535 tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_DMA);
536 if (!tunnel)
537 return NULL;
538
539 tunnel->activate = tb_dma_activate;
540 tunnel->src_port = nhi;
541 tunnel->dst_port = dst;
542
543 credits = tb_dma_credits(nhi);
544
545 path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, "DMA RX");
546 if (!path) {
547 tb_tunnel_free(tunnel);
548 return NULL;
549 }
550 tb_dma_init_path(path, TB_PATH_NONE, TB_PATH_SOURCE | TB_PATH_INTERNAL,
551 credits);
552 tunnel->paths[TB_DMA_PATH_IN] = path;
553
554 path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0, "DMA TX");
555 if (!path) {
556 tb_tunnel_free(tunnel);
557 return NULL;
558 }
559 tb_dma_init_path(path, TB_PATH_SOURCE, TB_PATH_ALL, credits);
560 tunnel->paths[TB_DMA_PATH_OUT] = path;
561
562 return tunnel;
563}
564
474/** 565/**
475 * tb_tunnel_free() - free a tunnel 566 * tb_tunnel_free() - free a tunnel
476 * @tunnel: Tunnel to be freed 567 * @tunnel: Tunnel to be freed
diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h
index 0373779f43ba..c68bbcd3a62c 100644
--- a/drivers/thunderbolt/tunnel.h
+++ b/drivers/thunderbolt/tunnel.h
@@ -14,6 +14,7 @@
14enum tb_tunnel_type { 14enum tb_tunnel_type {
15 TB_TUNNEL_PCI, 15 TB_TUNNEL_PCI,
16 TB_TUNNEL_DP, 16 TB_TUNNEL_DP,
17 TB_TUNNEL_DMA,
17}; 18};
18 19
19/** 20/**
@@ -47,6 +48,10 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
47struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in); 48struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in);
48struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, 49struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
49 struct tb_port *out); 50 struct tb_port *out);
51struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
52 struct tb_port *dst, int transmit_ring,
53 int transmit_path, int receive_ring,
54 int receive_path);
50 55
51void tb_tunnel_free(struct tb_tunnel *tunnel); 56void tb_tunnel_free(struct tb_tunnel *tunnel);
52int tb_tunnel_activate(struct tb_tunnel *tunnel); 57int tb_tunnel_activate(struct tb_tunnel *tunnel);
@@ -64,5 +69,10 @@ static inline bool tb_tunnel_is_dp(const struct tb_tunnel *tunnel)
64 return tunnel->type == TB_TUNNEL_DP; 69 return tunnel->type == TB_TUNNEL_DP;
65} 70}
66 71
72static inline bool tb_tunnel_is_dma(const struct tb_tunnel *tunnel)
73{
74 return tunnel->type == TB_TUNNEL_DMA;
75}
76
67#endif 77#endif
68 78