Commit e390909a authored by Sanjay R Mehta's avatar Sanjay R Mehta Committed by Mika Westerberg
Browse files

thunderbolt: Add vendor specific NHI quirk for auto-clearing interrupt status



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: default avatarBasavaraj Natikar <Basavaraj.Natikar@amd.com>
Signed-off-by: default avatarSanjay R Mehta <sanju.mehta@amd.com>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent 1651d9e7
Loading
Loading
Loading
Loading
+25 −8
Original line number Diff line number Diff line
@@ -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,15 +68,18 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
		else
			index = ring->hop + ring->nhi->hop_count;

		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.
			 * 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;
		step = index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
@@ -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");
+2 −0
Original line number Diff line number Diff line
@@ -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;
};

/**