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: 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>
This commit is contained in:
Sanjay R Mehta 2021-08-06 11:59:05 -05:00 committed by Mika Westerberg
parent 1651d9e781
commit e390909ac7
2 changed files with 27 additions and 8 deletions

View 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");

View 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;
};
/**