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;
}
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;
struct Pciisr {
void (*f)(Ureg*, void*);
@ -130,9 +123,7 @@ static Lock pciisrlk;
void
pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a)
{
int cap, ok64;
u32int dat;
u64int adr;
ulong dat;
Pcidev *p;
Pciisr *isr;
@ -140,8 +131,9 @@ pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a)
print("pciintrenable: %T: unknown device\n", tbdf);
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;
}
@ -170,14 +162,9 @@ pciintrenable(int tbdf, void (*f)(Ureg*, void*), void *a)
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 = ((dat >> 16) & (dat & 0xFFFF)) | (isr-pciisr);
pcicfgw16(p, cap + (ok64 ? MSIData64 : MSIData32), dat);
pcicfgw16(p, cap + MSICtrl, 1);
pcimsienable(p, MSI_TARGET_ADDR, dat);
}
void

View file

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

View file

@ -753,7 +753,7 @@ pcireset(void)
/* don't mess with the bridges */
if(p->ccrb == 0x06)
continue;
pciclrbme(p);
pcidisable(p);
}
}
@ -869,6 +869,62 @@ pcihtcap(Pcidev *p, int cap)
return enumcaps(p, matchhtcap, cap);
}
static int
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)
{
@ -1009,5 +1065,7 @@ pcidisable(Pcidev *p)
{
if(p == nil)
return;
pcimsixdisable(p);
pcimsidisable(p);
pciclrbme(p);
}

View file

@ -199,6 +199,7 @@ struct Pcidev
} prefa;
int pmrb; /* power management register block */
int msi; /* MSI capability register block */
};
enum {
@ -248,6 +249,10 @@ extern void pciclrmwi(Pcidev* p);
extern int pcicap(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 pcisetpms(Pcidev* p, int state);