summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2017-06-06 08:24:57 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-06-09 05:42:41 -0400
commit046bee1f9ab83b4549c185804ae9cbfbb8f9641f (patch)
tree7bc81ed524fdcca2d146e8aa0a14d189b3469f33 /drivers/thunderbolt
parentb2466355c0007cbd853c3babce0cdb6ef1ff23bc (diff)
thunderbolt: Add MSI-X support
Intel Thunderbolt controllers support up to 16 MSI-X vectors. Using MSI-X is preferred over MSI or legacy interrupt and may bring additional performance because there is no need to check the status registers which interrupt was triggered. While there we convert comments in structs tb_ring and tb_nhi to follow kernel-doc format more closely. This code is based on the work done by Amir Levy and Michael Jamet. Signed-off-by: Michael Jamet <michael.jamet@intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Yehezkel Bernat <yehezkel.bernat@intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Andreas Noever <andreas.noever@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/ctl.c4
-rw-r--r--drivers/thunderbolt/nhi.c165
-rw-r--r--drivers/thunderbolt/nhi.h56
-rw-r--r--drivers/thunderbolt/nhi_regs.h9
4 files changed, 198 insertions, 36 deletions
diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c
index 1031d97407a8..889a32dd21e7 100644
--- a/drivers/thunderbolt/ctl.c
+++ b/drivers/thunderbolt/ctl.c
@@ -488,11 +488,11 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data)
488 if (!ctl->frame_pool) 488 if (!ctl->frame_pool)
489 goto err; 489 goto err;
490 490
491 ctl->tx = ring_alloc_tx(nhi, 0, 10); 491 ctl->tx = ring_alloc_tx(nhi, 0, 10, RING_FLAG_NO_SUSPEND);
492 if (!ctl->tx) 492 if (!ctl->tx)
493 goto err; 493 goto err;
494 494
495 ctl->rx = ring_alloc_rx(nhi, 0, 10); 495 ctl->rx = ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND);
496 if (!ctl->rx) 496 if (!ctl->rx)
497 goto err; 497 goto err;
498 498
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index a8c20413dbda..ed75c49748f5 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -21,6 +21,12 @@
21 21
22#define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring") 22#define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring")
23 23
24/*
25 * Minimal number of vectors when we use MSI-X. Two for control channel
26 * Rx/Tx and the rest four are for cross domain DMA paths.
27 */
28#define MSIX_MIN_VECS 6
29#define MSIX_MAX_VECS 16
24 30
25static int ring_interrupt_index(struct tb_ring *ring) 31static int ring_interrupt_index(struct tb_ring *ring)
26{ 32{
@@ -42,6 +48,37 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
42 int bit = ring_interrupt_index(ring) & 31; 48 int bit = ring_interrupt_index(ring) & 31;
43 int mask = 1 << bit; 49 int mask = 1 << bit;
44 u32 old, new; 50 u32 old, new;
51
52 if (ring->irq > 0) {
53 u32 step, shift, ivr, misc;
54 void __iomem *ivr_base;
55 int index;
56
57 if (ring->is_tx)
58 index = ring->hop;
59 else
60 index = ring->hop + ring->nhi->hop_count;
61
62 /*
63 * Ask the hardware to clear interrupt status bits automatically
64 * since we already know which interrupt was triggered.
65 */
66 misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
67 if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
68 misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
69 iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
70 }
71
72 ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
73 step = index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
74 shift = index % REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
75 ivr = ioread32(ivr_base + step);
76 ivr &= ~(REG_INT_VEC_ALLOC_MASK << shift);
77 if (active)
78 ivr |= ring->vector << shift;
79 iowrite32(ivr, ivr_base + step);
80 }
81
45 old = ioread32(ring->nhi->iobase + reg); 82 old = ioread32(ring->nhi->iobase + reg);
46 if (active) 83 if (active)
47 new = old | mask; 84 new = old | mask;
@@ -239,8 +276,50 @@ int __ring_enqueue(struct tb_ring *ring, struct ring_frame *frame)
239 return ret; 276 return ret;
240} 277}
241 278
279static irqreturn_t ring_msix(int irq, void *data)
280{
281 struct tb_ring *ring = data;
282
283 schedule_work(&ring->work);
284 return IRQ_HANDLED;
285}
286
287static int ring_request_msix(struct tb_ring *ring, bool no_suspend)
288{
289 struct tb_nhi *nhi = ring->nhi;
290 unsigned long irqflags;
291 int ret;
292
293 if (!nhi->pdev->msix_enabled)
294 return 0;
295
296 ret = ida_simple_get(&nhi->msix_ida, 0, MSIX_MAX_VECS, GFP_KERNEL);
297 if (ret < 0)
298 return ret;
299
300 ring->vector = ret;
301
302 ring->irq = pci_irq_vector(ring->nhi->pdev, ring->vector);
303 if (ring->irq < 0)
304 return ring->irq;
305
306 irqflags = no_suspend ? IRQF_NO_SUSPEND : 0;
307 return request_irq(ring->irq, ring_msix, irqflags, "thunderbolt", ring);
308}
309
310static void ring_release_msix(struct tb_ring *ring)
311{
312 if (ring->irq <= 0)
313 return;
314
315 free_irq(ring->irq, ring);
316 ida_simple_remove(&ring->nhi->msix_ida, ring->vector);
317 ring->vector = 0;
318 ring->irq = 0;
319}
320
242static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size, 321static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
243 bool transmit) 322 bool transmit, unsigned int flags)
244{ 323{
245 struct tb_ring *ring = NULL; 324 struct tb_ring *ring = NULL;
246 dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n", 325 dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n",
@@ -271,9 +350,14 @@ static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
271 ring->hop = hop; 350 ring->hop = hop;
272 ring->is_tx = transmit; 351 ring->is_tx = transmit;
273 ring->size = size; 352 ring->size = size;
353 ring->flags = flags;
274 ring->head = 0; 354 ring->head = 0;
275 ring->tail = 0; 355 ring->tail = 0;
276 ring->running = false; 356 ring->running = false;
357
358 if (ring_request_msix(ring, flags & RING_FLAG_NO_SUSPEND))
359 goto err;
360
277 ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev, 361 ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev,
278 size * sizeof(*ring->descriptors), 362 size * sizeof(*ring->descriptors),
279 &ring->descriptors_dma, GFP_KERNEL | __GFP_ZERO); 363 &ring->descriptors_dma, GFP_KERNEL | __GFP_ZERO);
@@ -295,14 +379,16 @@ err:
295 return NULL; 379 return NULL;
296} 380}
297 381
298struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size) 382struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
383 unsigned int flags)
299{ 384{
300 return ring_alloc(nhi, hop, size, true); 385 return ring_alloc(nhi, hop, size, true, flags);
301} 386}
302 387
303struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size) 388struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
389 unsigned int flags)
304{ 390{
305 return ring_alloc(nhi, hop, size, false); 391 return ring_alloc(nhi, hop, size, false, flags);
306} 392}
307 393
308/** 394/**
@@ -413,6 +499,8 @@ void ring_free(struct tb_ring *ring)
413 RING_TYPE(ring), ring->hop); 499 RING_TYPE(ring), ring->hop);
414 } 500 }
415 501
502 ring_release_msix(ring);
503
416 dma_free_coherent(&ring->nhi->pdev->dev, 504 dma_free_coherent(&ring->nhi->pdev->dev,
417 ring->size * sizeof(*ring->descriptors), 505 ring->size * sizeof(*ring->descriptors),
418 ring->descriptors, ring->descriptors_dma); 506 ring->descriptors, ring->descriptors_dma);
@@ -428,9 +516,9 @@ void ring_free(struct tb_ring *ring)
428 516
429 mutex_unlock(&ring->nhi->lock); 517 mutex_unlock(&ring->nhi->lock);
430 /** 518 /**
431 * ring->work can no longer be scheduled (it is scheduled only by 519 * ring->work can no longer be scheduled (it is scheduled only
432 * nhi_interrupt_work and ring_stop). Wait for it to finish before 520 * by nhi_interrupt_work, ring_stop and ring_msix). Wait for it
433 * freeing the ring. 521 * to finish before freeing the ring.
434 */ 522 */
435 flush_work(&ring->work); 523 flush_work(&ring->work);
436 mutex_destroy(&ring->lock); 524 mutex_destroy(&ring->lock);
@@ -528,9 +616,52 @@ static void nhi_shutdown(struct tb_nhi *nhi)
528 * We have to release the irq before calling flush_work. Otherwise an 616 * We have to release the irq before calling flush_work. Otherwise an
529 * already executing IRQ handler could call schedule_work again. 617 * already executing IRQ handler could call schedule_work again.
530 */ 618 */
531 devm_free_irq(&nhi->pdev->dev, nhi->pdev->irq, nhi); 619 if (!nhi->pdev->msix_enabled) {
532 flush_work(&nhi->interrupt_work); 620 devm_free_irq(&nhi->pdev->dev, nhi->pdev->irq, nhi);
621 flush_work(&nhi->interrupt_work);
622 }
533 mutex_destroy(&nhi->lock); 623 mutex_destroy(&nhi->lock);
624 ida_destroy(&nhi->msix_ida);
625}
626
627static int nhi_init_msi(struct tb_nhi *nhi)
628{
629 struct pci_dev *pdev = nhi->pdev;
630 int res, irq, nvec;
631
632 /* In case someone left them on. */
633 nhi_disable_interrupts(nhi);
634
635 ida_init(&nhi->msix_ida);
636
637 /*
638 * The NHI has 16 MSI-X vectors or a single MSI. We first try to
639 * get all MSI-X vectors and if we succeed, each ring will have
640 * one MSI-X. If for some reason that does not work out, we
641 * fallback to a single MSI.
642 */
643 nvec = pci_alloc_irq_vectors(pdev, MSIX_MIN_VECS, MSIX_MAX_VECS,
644 PCI_IRQ_MSIX);
645 if (nvec < 0) {
646 nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
647 if (nvec < 0)
648 return nvec;
649
650 INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work);
651
652 irq = pci_irq_vector(nhi->pdev, 0);
653 if (irq < 0)
654 return irq;
655
656 res = devm_request_irq(&pdev->dev, irq, nhi_msi,
657 IRQF_NO_SUSPEND, "thunderbolt", nhi);
658 if (res) {
659 dev_err(&pdev->dev, "request_irq failed, aborting\n");
660 return res;
661 }
662 }
663
664 return 0;
534} 665}
535 666
536static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) 667static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -545,12 +676,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
545 return res; 676 return res;
546 } 677 }
547 678
548 res = pci_enable_msi(pdev);
549 if (res) {
550 dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
551 return res;
552 }
553
554 res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt"); 679 res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt");
555 if (res) { 680 if (res) {
556 dev_err(&pdev->dev, "cannot obtain PCI resources, aborting\n"); 681 dev_err(&pdev->dev, "cannot obtain PCI resources, aborting\n");
@@ -568,7 +693,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
568 if (nhi->hop_count != 12 && nhi->hop_count != 32) 693 if (nhi->hop_count != 12 && nhi->hop_count != 32)
569 dev_warn(&pdev->dev, "unexpected hop count: %d\n", 694 dev_warn(&pdev->dev, "unexpected hop count: %d\n",
570 nhi->hop_count); 695 nhi->hop_count);
571 INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work);
572 696
573 nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count, 697 nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count,
574 sizeof(*nhi->tx_rings), GFP_KERNEL); 698 sizeof(*nhi->tx_rings), GFP_KERNEL);
@@ -577,12 +701,9 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
577 if (!nhi->tx_rings || !nhi->rx_rings) 701 if (!nhi->tx_rings || !nhi->rx_rings)
578 return -ENOMEM; 702 return -ENOMEM;
579 703
580 nhi_disable_interrupts(nhi); /* In case someone left them on. */ 704 res = nhi_init_msi(nhi);
581 res = devm_request_irq(&pdev->dev, pdev->irq, nhi_msi,
582 IRQF_NO_SUSPEND, /* must work during _noirq */
583 "thunderbolt", nhi);
584 if (res) { 705 if (res) {
585 dev_err(&pdev->dev, "request_irq failed, aborting\n"); 706 dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
586 return res; 707 return res;
587 } 708 }
588 709
diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h
index 317242939b31..630f44140530 100644
--- a/drivers/thunderbolt/nhi.h
+++ b/drivers/thunderbolt/nhi.h
@@ -7,45 +7,75 @@
7#ifndef DSL3510_H_ 7#ifndef DSL3510_H_
8#define DSL3510_H_ 8#define DSL3510_H_
9 9
10#include <linux/idr.h>
10#include <linux/mutex.h> 11#include <linux/mutex.h>
11#include <linux/workqueue.h> 12#include <linux/workqueue.h>
12 13
13/** 14/**
14 * struct tb_nhi - thunderbolt native host interface 15 * struct tb_nhi - thunderbolt native host interface
16 * @lock: Must be held during ring creation/destruction. Is acquired by
17 * interrupt_work when dispatching interrupts to individual rings.
18 * @pdev: Pointer to the PCI device
19 * @iobase: MMIO space of the NHI
20 * @tx_rings: All Tx rings available on this host controller
21 * @rx_rings: All Rx rings available on this host controller
22 * @msix_ida: Used to allocate MSI-X vectors for rings
23 * @interrupt_work: Work scheduled to handle ring interrupt when no
24 * MSI-X is used.
25 * @hop_count: Number of rings (end point hops) supported by NHI.
15 */ 26 */
16struct tb_nhi { 27struct tb_nhi {
17 struct mutex lock; /* 28 struct mutex lock;
18 * Must be held during ring creation/destruction.
19 * Is acquired by interrupt_work when dispatching
20 * interrupts to individual rings.
21 **/
22 struct pci_dev *pdev; 29 struct pci_dev *pdev;
23 void __iomem *iobase; 30 void __iomem *iobase;
24 struct tb_ring **tx_rings; 31 struct tb_ring **tx_rings;
25 struct tb_ring **rx_rings; 32 struct tb_ring **rx_rings;
33 struct ida msix_ida;
26 struct work_struct interrupt_work; 34 struct work_struct interrupt_work;
27 u32 hop_count; /* Number of rings (end point hops) supported by NHI. */ 35 u32 hop_count;
28}; 36};
29 37
30/** 38/**
31 * struct tb_ring - thunderbolt TX or RX ring associated with a NHI 39 * struct tb_ring - thunderbolt TX or RX ring associated with a NHI
40 * @lock: Lock serializing actions to this ring. Must be acquired after
41 * nhi->lock.
42 * @nhi: Pointer to the native host controller interface
43 * @size: Size of the ring
44 * @hop: Hop (DMA channel) associated with this ring
45 * @head: Head of the ring (write next descriptor here)
46 * @tail: Tail of the ring (complete next descriptor here)
47 * @descriptors: Allocated descriptors for this ring
48 * @queue: Queue holding frames to be transferred over this ring
49 * @in_flight: Queue holding frames that are currently in flight
50 * @work: Interrupt work structure
51 * @is_tx: Is the ring Tx or Rx
52 * @running: Is the ring running
53 * @irq: MSI-X irq number if the ring uses MSI-X. %0 otherwise.
54 * @vector: MSI-X vector number the ring uses (only set if @irq is > 0)
55 * @flags: Ring specific flags
32 */ 56 */
33struct tb_ring { 57struct tb_ring {
34 struct mutex lock; /* must be acquired after nhi->lock */ 58 struct mutex lock;
35 struct tb_nhi *nhi; 59 struct tb_nhi *nhi;
36 int size; 60 int size;
37 int hop; 61 int hop;
38 int head; /* write next descriptor here */ 62 int head;
39 int tail; /* complete next descriptor here */ 63 int tail;
40 struct ring_desc *descriptors; 64 struct ring_desc *descriptors;
41 dma_addr_t descriptors_dma; 65 dma_addr_t descriptors_dma;
42 struct list_head queue; 66 struct list_head queue;
43 struct list_head in_flight; 67 struct list_head in_flight;
44 struct work_struct work; 68 struct work_struct work;
45 bool is_tx:1; /* rx otherwise */ 69 bool is_tx:1;
46 bool running:1; 70 bool running:1;
71 int irq;
72 u8 vector;
73 unsigned int flags;
47}; 74};
48 75
76/* Leave ring interrupt enabled on suspend */
77#define RING_FLAG_NO_SUSPEND BIT(0)
78
49struct ring_frame; 79struct ring_frame;
50typedef void (*ring_cb)(struct tb_ring*, struct ring_frame*, bool canceled); 80typedef void (*ring_cb)(struct tb_ring*, struct ring_frame*, bool canceled);
51 81
@@ -64,8 +94,10 @@ struct ring_frame {
64 94
65#define TB_FRAME_SIZE 0x100 /* minimum size for ring_rx */ 95#define TB_FRAME_SIZE 0x100 /* minimum size for ring_rx */
66 96
67struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size); 97struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
68struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size); 98 unsigned int flags);
99struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
100 unsigned int flags);
69void ring_start(struct tb_ring *ring); 101void ring_start(struct tb_ring *ring);
70void ring_stop(struct tb_ring *ring); 102void ring_stop(struct tb_ring *ring);
71void ring_free(struct tb_ring *ring); 103void ring_free(struct tb_ring *ring);
diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h
index 75cf0691e6c5..48b98d3c7e6a 100644
--- a/drivers/thunderbolt/nhi_regs.h
+++ b/drivers/thunderbolt/nhi_regs.h
@@ -95,7 +95,16 @@ struct ring_desc {
95#define REG_RING_INTERRUPT_BASE 0x38200 95#define REG_RING_INTERRUPT_BASE 0x38200
96#define RING_INTERRUPT_REG_COUNT(nhi) ((31 + 2 * nhi->hop_count) / 32) 96#define RING_INTERRUPT_REG_COUNT(nhi) ((31 + 2 * nhi->hop_count) / 32)
97 97
98/* Interrupt Vector Allocation */
99#define REG_INT_VEC_ALLOC_BASE 0x38c40
100#define REG_INT_VEC_ALLOC_BITS 4
101#define REG_INT_VEC_ALLOC_MASK GENMASK(3, 0)
102#define REG_INT_VEC_ALLOC_REGS (32 / REG_INT_VEC_ALLOC_BITS)
103
98/* The last 11 bits contain the number of hops supported by the NHI port. */ 104/* The last 11 bits contain the number of hops supported by the NHI port. */
99#define REG_HOP_COUNT 0x39640 105#define REG_HOP_COUNT 0x39640
100 106
107#define REG_DMA_MISC 0x39864
108#define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2)
109
101#endif 110#endif