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;
|
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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue