kernel: add portable pcimsienable()/pcimsidisable(), disable MSI/MSI-X on pcidisable()/pcireset()

This avoids some duplication in the pci support code and
allows pcireset() to diable MSI and MSI-X interrupts
when disabling or reseting a device.
This commit is contained in:
cinap_lenrek 2020-11-21 16:02:21 +01:00
parent cedded7b50
commit 1376d39ef1
4 changed files with 74 additions and 35 deletions

View file

@ -110,13 +110,6 @@ pcicfgrw8(int tbdf, int rno, int data, int read)
return data; return data;
} }
enum {
MSICtrl = 0x02, /* message control register (16 bit) */
MSIAddr = 0x04, /* message address register (64 bit) */
MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
};
typedef struct Pciisr Pciisr; typedef struct Pciisr Pciisr;
struct Pciisr { struct Pciisr {
void (*f)(Ureg*, void*); void (*f)(Ureg*, void*);
@ -130,9 +123,7 @@ static Lock pciisrlk;
void void
pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a) pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a)
{ {
int cap, ok64; ulong dat;
u32int dat;
u64int adr;
Pcidev *p; Pcidev *p;
Pciisr *isr; Pciisr *isr;
@ -140,8 +131,9 @@ pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a)
print("pciintrenable: %T: unknown device\n", tbdf); print("pciintrenable: %T: unknown device\n", tbdf);
return; return;
} }
if((cap = pcicap(p, PciCapMSI)) < 0){
print("pciintrenable: %T: no MSI cap\n", tbdf); if(pcimsidisable(p) < 0){
print("pciintrenable: %T: device doesnt support msi\n", tbdf);
return; return;
} }
@ -170,14 +162,9 @@ pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a)
return; return;
} }
adr = MSI_TARGET_ADDR;
ok64 = (pcicfgr16(p, cap + MSICtrl) & (1<<7)) != 0;
pcicfgw32(p, cap + MSIAddr, adr);
if(ok64) pcicfgw32(p, cap + MSIAddr + 4, adr>>32);
dat = regs[MISC_MSI_DATA_CONFIG]; dat = regs[MISC_MSI_DATA_CONFIG];
dat = ((dat >> 16) & (dat & 0xFFFF)) | (isr-pciisr); dat = ((dat >> 16) & (dat & 0xFFFF)) | (isr-pciisr);
pcicfgw16(p, cap + (ok64 ? MSIData64 : MSIData32), dat); pcimsienable(p, MSI_TARGET_ADDR, dat);
pcicfgw16(p, cap + MSICtrl, 1);
} }
void void

View file

@ -410,13 +410,6 @@ Findbus:
return -1; return -1;
} }
enum {
MSICtrl = 0x02, /* message control register (16 bit) */
MSIAddr = 0x04, /* message address register (64 bit) */
MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
};
enum { enum {
HTMSIMapping = 0xA8, HTMSIMapping = 0xA8,
HTMSIFlags = 0x02, HTMSIFlags = 0x02,
@ -479,7 +472,7 @@ htmsienable(Pcidev *pdev)
static int static int
msiintrenable(Vctl *v) msiintrenable(Vctl *v)
{ {
int tbdf, vno, cap, cpu, ok64; int tbdf, vno, cpu;
Pcidev *pci; Pcidev *pci;
if(getconf("*nomsi") != nil) if(getconf("*nomsi") != nil)
@ -494,16 +487,12 @@ msiintrenable(Vctl *v)
} }
if(htmsienable(pci) < 0) if(htmsienable(pci) < 0)
return -1; return -1;
cap = pcicap(pci, PciCapMSI); if(pcimsidisable(pci) < 0)
if(cap < 0)
return -1; return -1;
vno = allocvector(); vno = allocvector();
cpu = mpintrcpu(); cpu = mpintrcpu();
ok64 = (pcicfgr16(pci, cap + MSICtrl) & (1<<7)) != 0; if(pcimsienable(pci, 0xFEE00000ULL | (cpu << 12), vno | (1<<14)) < 0)
pcicfgw32(pci, cap + MSIAddr, (0xFEE << 20) | (cpu << 12)); return -1;
if(ok64) pcicfgw32(pci, cap + MSIAddr + 4, 0);
pcicfgw16(pci, cap + (ok64 ? MSIData64 : MSIData32), vno | (1<<14));
pcicfgw16(pci, cap + MSICtrl, 1);
v->isr = lapicisr; v->isr = lapicisr;
v->eoi = lapiceoi; v->eoi = lapiceoi;
return vno; return vno;

View file

@ -753,7 +753,7 @@ pcireset(void)
/* don't mess with the bridges */ /* don't mess with the bridges */
if(p->ccrb == 0x06) if(p->ccrb == 0x06)
continue; continue;
pciclrbme(p); pcidisable(p);
} }
} }
@ -870,7 +870,63 @@ pcihtcap(Pcidev *p, int cap)
} }
static int static int
pcigetpmrb(Pcidev* p) pcigetmsi(Pcidev *p)
{
if(p->msi != 0)
return p->msi;
return p->msi = pcicap(p, PciCapMSI);
}
enum {
MSICtrl = 0x02, /* message control register (16 bit) */
MSIAddr = 0x04, /* message address register (64 bit) */
MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
};
int
pcimsienable(Pcidev *p, uvlong addr, ulong data)
{
int off, ok64;
if((off = pcigetmsi(p)) < 0)
return -1;
ok64 = (pcicfgr16(p, off + MSICtrl) & (1<<7)) != 0;
pcicfgw32(p, off + MSIAddr, addr);
if(ok64) pcicfgw32(p, off + MSIAddr+4, addr >> 32);
pcicfgw16(p, off + (ok64 ? MSIData64 : MSIData32), data);
pcicfgw16(p, off + MSICtrl, 1);
return 0;
}
int
pcimsidisable(Pcidev *p)
{
int off;
if((off = pcigetmsi(p)) < 0)
return -1;
pcicfgw16(p, off + MSICtrl, 0);
return 0;
}
enum {
MSIXCtrl = 0x02,
};
static int
pcimsixdisable(Pcidev *p)
{
int off;
if((off = pcicap(p, PciCapMSIX)) < 0)
return -1;
pcicfgw16(p, off + MSIXCtrl, 0);
return 0;
}
static int
pcigetpmrb(Pcidev *p)
{ {
if(p->pmrb != 0) if(p->pmrb != 0)
return p->pmrb; return p->pmrb;
@ -1009,5 +1065,7 @@ pcidisable(Pcidev *p)
{ {
if(p == nil) if(p == nil)
return; return;
pcimsixdisable(p);
pcimsidisable(p);
pciclrbme(p); pciclrbme(p);
} }

View file

@ -199,6 +199,7 @@ struct Pcidev
} prefa; } prefa;
int pmrb; /* power management register block */ int pmrb; /* power management register block */
int msi; /* MSI capability register block */
}; };
enum { enum {
@ -248,6 +249,10 @@ extern void pciclrmwi(Pcidev* p);
extern int pcicap(Pcidev *p, int cap); extern int pcicap(Pcidev *p, int cap);
extern int pcihtcap(Pcidev *p, int cap); extern int pcihtcap(Pcidev *p, int cap);
extern int pcimsienable(Pcidev *p, uvlong addr, ulong data);
extern int pcimsidisable(Pcidev *p);
extern int pcigetpms(Pcidev* p); extern int pcigetpms(Pcidev* p);
extern int pcisetpms(Pcidev* p, int state); extern int pcisetpms(Pcidev* p, int state);