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:
parent
cedded7b50
commit
1376d39ef1
4 changed files with 74 additions and 35 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue