thunderbolt: Add vendor specific NHI quirk for auto-clearing interrupt status
authorSanjay R Mehta <sanju.mehta@amd.com>
Fri, 6 Aug 2021 16:59:05 +0000 (11:59 -0500)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 9 Aug 2021 11:58:29 +0000 (14:58 +0300)
Introduce nhi_check_quirks() routine to handle any vendor specific quirks
to manage a hardware specific implementation.

On Intel hardware the USB4 controller supports clearing the interrupt
status register automatically right after it is being issued. For this
reason add a new quirk that does that on all Intel hardware.

Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
Signed-off-by: Sanjay R Mehta <sanju.mehta@amd.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/nhi.c
include/linux/thunderbolt.h

index fa44332..c7a2841 100644 (file)
@@ -35,6 +35,8 @@
 
 #define NHI_MAILBOX_TIMEOUT    500 /* ms */
 
+#define QUIRK_AUTO_CLEAR_INT   BIT(0)
+
 static int ring_interrupt_index(struct tb_ring *ring)
 {
        int bit = ring->hop;
@@ -66,14 +68,17 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
                else
                        index = ring->hop + ring->nhi->hop_count;
 
-               /*
-                * Ask the hardware to clear interrupt status bits automatically
-                * since we already know which interrupt was triggered.
-                */
-               misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
-               if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
-                       misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
-                       iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
+               if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
+                       /*
+                        * Ask the hardware to clear interrupt status
+                        * bits automatically since we already know
+                        * which interrupt was triggered.
+                        */
+                       misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
+                       if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
+                               misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
+                               iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
+                       }
                }
 
                ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
@@ -1074,6 +1079,16 @@ static void nhi_shutdown(struct tb_nhi *nhi)
                nhi->ops->shutdown(nhi);
 }
 
+static void nhi_check_quirks(struct tb_nhi *nhi)
+{
+       /*
+        * Intel hardware supports auto clear of the interrupt status
+        * reqister right after interrupt is being issued.
+        */
+       if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL)
+               nhi->quirks |= QUIRK_AUTO_CLEAR_INT;
+}
+
 static int nhi_init_msi(struct tb_nhi *nhi)
 {
        struct pci_dev *pdev = nhi->pdev;
@@ -1190,6 +1205,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (!nhi->tx_rings || !nhi->rx_rings)
                return -ENOMEM;
 
+       nhi_check_quirks(nhi);
+
        res = nhi_init_msi(nhi);
        if (res) {
                dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
index e7c96c3..124e13c 100644 (file)
@@ -468,6 +468,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc)
  * @interrupt_work: Work scheduled to handle ring interrupt when no
  *                 MSI-X is used.
  * @hop_count: Number of rings (end point hops) supported by NHI.
+ * @quirks: NHI specific quirks if any
  */
 struct tb_nhi {
        spinlock_t lock;
@@ -480,6 +481,7 @@ struct tb_nhi {
        bool going_away;
        struct work_struct interrupt_work;
        u32 hop_count;
+       unsigned long quirks;
 };
 
 /**