provide routines to access VPD data at the PCI layer...
remove sk's own implementation, and use the new calls to get the data... Reviewed by: -arch
This commit is contained in:
parent
e230c2c3ca
commit
667dc26e71
@ -41,7 +41,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/kernel.h>
|
#include <sys/kernel.h>
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/endian.h>
|
||||||
|
|
||||||
#include <vm/vm.h>
|
#include <vm/vm.h>
|
||||||
#include <vm/pmap.h>
|
#include <vm/pmap.h>
|
||||||
@ -90,6 +90,13 @@ static int pci_modevent(module_t mod, int what, void *arg);
|
|||||||
static void pci_hdrtypedata(device_t pcib, int b, int s, int f,
|
static void pci_hdrtypedata(device_t pcib, int b, int s, int f,
|
||||||
pcicfgregs *cfg);
|
pcicfgregs *cfg);
|
||||||
static void pci_read_extcap(device_t pcib, pcicfgregs *cfg);
|
static void pci_read_extcap(device_t pcib, pcicfgregs *cfg);
|
||||||
|
static uint32_t pci_read_vpd_reg(device_t pcib, pcicfgregs *cfg,
|
||||||
|
int reg);
|
||||||
|
#if 0
|
||||||
|
static void pci_write_vpd_reg(device_t pcib, pcicfgregs *cfg,
|
||||||
|
int reg, uint32_t data);
|
||||||
|
#endif
|
||||||
|
static void pci_read_vpd(device_t pcib, pcicfgregs *cfg);
|
||||||
|
|
||||||
static device_method_t pci_methods[] = {
|
static device_method_t pci_methods[] = {
|
||||||
/* Device interface */
|
/* Device interface */
|
||||||
@ -127,6 +134,8 @@ static device_method_t pci_methods[] = {
|
|||||||
DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method),
|
DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method),
|
||||||
DEVMETHOD(pci_enable_io, pci_enable_io_method),
|
DEVMETHOD(pci_enable_io, pci_enable_io_method),
|
||||||
DEVMETHOD(pci_disable_io, pci_disable_io_method),
|
DEVMETHOD(pci_disable_io, pci_disable_io_method),
|
||||||
|
DEVMETHOD(pci_get_vpd_ident, pci_get_vpd_ident_method),
|
||||||
|
DEVMETHOD(pci_get_vpd_readonly, pci_get_vpd_readonly_method),
|
||||||
DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method),
|
DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method),
|
||||||
DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method),
|
DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method),
|
||||||
DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
|
DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
|
||||||
@ -344,7 +353,6 @@ pci_hdrtypedata(device_t pcib, int b, int s, int f, pcicfgregs *cfg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* read configuration header into pcicfgregs structure */
|
/* read configuration header into pcicfgregs structure */
|
||||||
|
|
||||||
struct pci_devinfo *
|
struct pci_devinfo *
|
||||||
pci_read_device(device_t pcib, int b, int s, int f, size_t size)
|
pci_read_device(device_t pcib, int b, int s, int f, size_t size)
|
||||||
{
|
{
|
||||||
@ -428,7 +436,7 @@ pci_read_extcap(device_t pcib, pcicfgregs *cfg)
|
|||||||
ptrptr = PCIR_CAP_PTR;
|
ptrptr = PCIR_CAP_PTR;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ptrptr = PCIR_CAP_PTR_2;
|
ptrptr = PCIR_CAP_PTR_2; /* cardbus capabilities ptr */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return; /* no extended capabilities support */
|
return; /* no extended capabilities support */
|
||||||
@ -468,13 +476,304 @@ pci_read_extcap(device_t pcib, pcicfgregs *cfg)
|
|||||||
cfg->msi.msi_data = PCIR_MSI_DATA;
|
cfg->msi.msi_data = PCIR_MSI_DATA;
|
||||||
cfg->msi.msi_msgnum = 1 << ((cfg->msi.msi_ctrl &
|
cfg->msi.msi_msgnum = 1 << ((cfg->msi.msi_ctrl &
|
||||||
PCIM_MSICTRL_MMC_MASK)>>1);
|
PCIM_MSICTRL_MMC_MASK)>>1);
|
||||||
|
break;
|
||||||
|
case PCIY_VPD: /* PCI Vital Product Data */
|
||||||
|
cfg->vpd.vpd_reg = ptr;
|
||||||
|
pci_read_vpd(pcib, cfg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* REG use carry through to next functions */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PCI Vital Product Data
|
||||||
|
*/
|
||||||
|
static uint32_t
|
||||||
|
pci_read_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg)
|
||||||
|
{
|
||||||
|
#define WREG(n, v, w) PCIB_WRITE_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, v, w)
|
||||||
|
|
||||||
|
KASSERT((reg & 3) == 0, ("VPD register must by 4 byte aligned"));
|
||||||
|
|
||||||
|
WREG(cfg->vpd.vpd_reg + 2, reg, 2);
|
||||||
|
while ((REG(cfg->vpd.vpd_reg + 2, 2) & 0x8000) != 0x8000)
|
||||||
|
DELAY(1); /* limit looping */
|
||||||
|
|
||||||
|
return REG(cfg->vpd.vpd_reg + 4, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static void
|
||||||
|
pci_write_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t data)
|
||||||
|
{
|
||||||
|
KASSERT((reg & 3) == 0, ("VPD register must by 4 byte aligned"));
|
||||||
|
|
||||||
|
WREG(cfg->vpd.vpd_reg + 4, data, 4);
|
||||||
|
WREG(cfg->vpd.vpd_reg + 2, reg | 0x8000, 2);
|
||||||
|
while ((REG(cfg->vpd.vpd_reg + 2, 2) & 0x8000) == 0x8000)
|
||||||
|
DELAY(1); /* limit looping */
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#undef WREG
|
||||||
|
|
||||||
|
struct vpd_readstate {
|
||||||
|
device_t pcib;
|
||||||
|
pcicfgregs *cfg;
|
||||||
|
uint32_t val;
|
||||||
|
int bytesinval;
|
||||||
|
int off;
|
||||||
|
uint8_t cksum;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t
|
||||||
|
vpd_nextbyte(struct vpd_readstate *vrs)
|
||||||
|
{
|
||||||
|
uint8_t byte;
|
||||||
|
|
||||||
|
if (vrs->bytesinval == 0) {
|
||||||
|
vrs->val = le32toh(pci_read_vpd_reg(vrs->pcib, vrs->cfg,
|
||||||
|
vrs->off));
|
||||||
|
vrs->off += 4;
|
||||||
|
byte = vrs->val & 0xff;
|
||||||
|
vrs->bytesinval = 3;
|
||||||
|
} else {
|
||||||
|
vrs->val = vrs->val >> 8;
|
||||||
|
byte = vrs->val & 0xff;
|
||||||
|
vrs->bytesinval--;
|
||||||
|
}
|
||||||
|
|
||||||
|
vrs->cksum += byte;
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pci_read_vpd(device_t pcib, pcicfgregs *cfg)
|
||||||
|
{
|
||||||
|
struct vpd_readstate vrs;
|
||||||
|
int state;
|
||||||
|
int name;
|
||||||
|
int remain;
|
||||||
|
int end;
|
||||||
|
int i;
|
||||||
|
uint8_t byte;
|
||||||
|
int alloc, off; /* alloc/off for RO/W arrays */
|
||||||
|
int cksumvalid;
|
||||||
|
int dflen;
|
||||||
|
|
||||||
|
/* init vpd reader */
|
||||||
|
vrs.bytesinval = 0;
|
||||||
|
vrs.off = 0;
|
||||||
|
vrs.pcib = pcib;
|
||||||
|
vrs.cfg = cfg;
|
||||||
|
vrs.cksum = 0;
|
||||||
|
|
||||||
|
state = 0;
|
||||||
|
name = remain = i = 0; /* shut up stupid gcc */
|
||||||
|
alloc = off = 0; /* shut up stupid gcc */
|
||||||
|
dflen = 0; /* shut up stupid gcc */
|
||||||
|
end = 0;
|
||||||
|
cksumvalid = -1;
|
||||||
|
for (; !end;) {
|
||||||
|
byte = vpd_nextbyte(&vrs);
|
||||||
|
#if 0
|
||||||
|
printf("vpd: val: %#x, off: %d, bytesinval: %d, byte: %#hhx, " \
|
||||||
|
"state: %d, remain: %d, name: %#x, i: %d\n", vrs.val,
|
||||||
|
vrs.off, vrs.bytesinval, byte, state, remain, name, i);
|
||||||
|
#endif
|
||||||
|
switch (state) {
|
||||||
|
case 0: /* item name */
|
||||||
|
if (byte & 0x80) {
|
||||||
|
remain = vpd_nextbyte(&vrs);
|
||||||
|
remain |= vpd_nextbyte(&vrs) << 8;
|
||||||
|
if (remain > (0x7f*4 - vrs.off)) {
|
||||||
|
end = 1;
|
||||||
|
printf(
|
||||||
|
"pci%d:%d:%d: invalid vpd data, remain %#x\n",
|
||||||
|
cfg->bus, cfg->slot, cfg->func,
|
||||||
|
remain);
|
||||||
|
}
|
||||||
|
name = byte & 0x7f;
|
||||||
|
} else {
|
||||||
|
remain = byte & 0x7;
|
||||||
|
name = (byte >> 3) & 0xf;
|
||||||
|
}
|
||||||
|
switch (name) {
|
||||||
|
case 0x2: /* String */
|
||||||
|
cfg->vpd.vpd_ident = malloc(remain + 1,
|
||||||
|
M_DEVBUF, M_WAITOK);
|
||||||
|
i = 0;
|
||||||
|
state = 1;
|
||||||
|
break;
|
||||||
|
case 0xf: /* End */
|
||||||
|
end = 1;
|
||||||
|
state = -1;
|
||||||
|
break;
|
||||||
|
case 0x10: /* VPD-R */
|
||||||
|
alloc = 8;
|
||||||
|
off = 0;
|
||||||
|
cfg->vpd.vpd_ros = malloc(alloc *
|
||||||
|
sizeof *cfg->vpd.vpd_ros, M_DEVBUF,
|
||||||
|
M_WAITOK);
|
||||||
|
state = 2;
|
||||||
|
break;
|
||||||
|
case 0x11: /* VPD-W */
|
||||||
|
alloc = 8;
|
||||||
|
off = 0;
|
||||||
|
cfg->vpd.vpd_w = malloc(alloc *
|
||||||
|
sizeof *cfg->vpd.vpd_w, M_DEVBUF,
|
||||||
|
M_WAITOK);
|
||||||
|
state = 5;
|
||||||
|
break;
|
||||||
|
default: /* XXX - unimplemented */
|
||||||
|
state = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /* Identifier String */
|
||||||
|
cfg->vpd.vpd_ident[i++] = byte;
|
||||||
|
remain--;
|
||||||
|
if (remain == 0) {
|
||||||
|
cfg->vpd.vpd_ident[i] = '\0';
|
||||||
|
state = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: /* VPD-R Keyword Header */
|
||||||
|
if (off == alloc) {
|
||||||
|
cfg->vpd.vpd_ros = reallocf(cfg->vpd.vpd_ros,
|
||||||
|
(alloc *= 2) * sizeof *cfg->vpd.vpd_ros,
|
||||||
|
M_DEVBUF, M_WAITOK);
|
||||||
|
}
|
||||||
|
cfg->vpd.vpd_ros[off].keyword[0] = byte;
|
||||||
|
cfg->vpd.vpd_ros[off].keyword[1] = vpd_nextbyte(&vrs);
|
||||||
|
dflen = vpd_nextbyte(&vrs);
|
||||||
|
cfg->vpd.vpd_ros[off].value = malloc((dflen + 1) *
|
||||||
|
sizeof *cfg->vpd.vpd_ros[off].value,
|
||||||
|
M_DEVBUF, M_WAITOK);
|
||||||
|
remain -= 3;
|
||||||
|
i = 0;
|
||||||
|
state = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: /* VPD-R Keyword Value */
|
||||||
|
cfg->vpd.vpd_ros[off].value[i++] = byte;
|
||||||
|
if (strncmp(cfg->vpd.vpd_ros[off].keyword,
|
||||||
|
"RV", 2) == 0 && cksumvalid == -1) {
|
||||||
|
if (vrs.cksum == 0)
|
||||||
|
cksumvalid = 1;
|
||||||
|
else {
|
||||||
|
printf(
|
||||||
|
"pci%d:%d:%d: bad VPD cksum, remain %hhu\n",
|
||||||
|
cfg->bus, cfg->slot, cfg->func,
|
||||||
|
vrs.cksum);
|
||||||
|
cksumvalid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dflen--;
|
||||||
|
remain--;
|
||||||
|
if (dflen == 0)
|
||||||
|
cfg->vpd.vpd_ros[off++].value[i++] = '\0';
|
||||||
|
if (dflen == 0 && remain == 0) {
|
||||||
|
cfg->vpd.vpd_rocnt = off;
|
||||||
|
cfg->vpd.vpd_ros = reallocf(cfg->vpd.vpd_ros,
|
||||||
|
off * sizeof *cfg->vpd.vpd_ros,
|
||||||
|
M_DEVBUF, M_WAITOK);
|
||||||
|
state = 0;
|
||||||
|
} else if (dflen == 0)
|
||||||
|
state = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
remain--;
|
||||||
|
if (remain == 0)
|
||||||
|
state = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5: /* VPD-W Keyword Header */
|
||||||
|
if (off == alloc) {
|
||||||
|
cfg->vpd.vpd_w = reallocf(cfg->vpd.vpd_w,
|
||||||
|
(alloc *= 2) * sizeof *cfg->vpd.vpd_w,
|
||||||
|
M_DEVBUF, M_WAITOK);
|
||||||
|
}
|
||||||
|
cfg->vpd.vpd_w[off].keyword[0] = byte;
|
||||||
|
cfg->vpd.vpd_w[off].keyword[1] = vpd_nextbyte(&vrs);
|
||||||
|
cfg->vpd.vpd_w[off].len = dflen = vpd_nextbyte(&vrs);
|
||||||
|
cfg->vpd.vpd_w[off].start = vrs.off - vrs.bytesinval;
|
||||||
|
cfg->vpd.vpd_w[off].value = malloc((dflen + 1) *
|
||||||
|
sizeof *cfg->vpd.vpd_w[off].value,
|
||||||
|
M_DEVBUF, M_WAITOK);
|
||||||
|
remain -= 3;
|
||||||
|
i = 0;
|
||||||
|
state = 6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6: /* VPD-W Keyword Value */
|
||||||
|
cfg->vpd.vpd_w[off].value[i++] = byte;
|
||||||
|
dflen--;
|
||||||
|
remain--;
|
||||||
|
if (dflen == 0)
|
||||||
|
cfg->vpd.vpd_w[off++].value[i++] = '\0';
|
||||||
|
if (dflen == 0 && remain == 0) {
|
||||||
|
cfg->vpd.vpd_wcnt = off;
|
||||||
|
cfg->vpd.vpd_w = reallocf(cfg->vpd.vpd_w,
|
||||||
|
off * sizeof *cfg->vpd.vpd_w,
|
||||||
|
M_DEVBUF, M_WAITOK);
|
||||||
|
state = 0;
|
||||||
|
} else if (dflen == 0)
|
||||||
|
state = 5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("pci%d:%d:%d: invalid state: %d\n",
|
||||||
|
cfg->bus, cfg->slot, cfg->func, state);
|
||||||
|
end = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
#undef REG
|
#undef REG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pci_get_vpd_ident_method(device_t dev, device_t child, const char **identptr)
|
||||||
|
{
|
||||||
|
struct pci_devinfo *dinfo = device_get_ivars(child);
|
||||||
|
pcicfgregs *cfg = &dinfo->cfg;
|
||||||
|
|
||||||
|
*identptr = cfg->vpd.vpd_ident;
|
||||||
|
|
||||||
|
if (*identptr == NULL)
|
||||||
|
return ENXIO;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pci_get_vpd_readonly_method(device_t dev, device_t child, const char *kw,
|
||||||
|
const char **vptr)
|
||||||
|
{
|
||||||
|
struct pci_devinfo *dinfo = device_get_ivars(child);
|
||||||
|
pcicfgregs *cfg = &dinfo->cfg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cfg->vpd.vpd_rocnt; i++)
|
||||||
|
if (memcmp(kw, cfg->vpd.vpd_ros[i].keyword,
|
||||||
|
sizeof cfg->vpd.vpd_ros[i].keyword) == 0) {
|
||||||
|
*vptr = cfg->vpd.vpd_ros[i].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != cfg->vpd.vpd_rocnt)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*vptr = NULL;
|
||||||
|
return ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the offset in configuration space of the requested extended
|
* Return the offset in configuration space of the requested extended
|
||||||
* capability entry or 0 if the specified capability was not found.
|
* capability entry or 0 if the specified capability was not found.
|
||||||
@ -532,9 +831,19 @@ int
|
|||||||
pci_freecfg(struct pci_devinfo *dinfo)
|
pci_freecfg(struct pci_devinfo *dinfo)
|
||||||
{
|
{
|
||||||
struct devlist *devlist_head;
|
struct devlist *devlist_head;
|
||||||
|
int i;
|
||||||
|
|
||||||
devlist_head = &pci_devq;
|
devlist_head = &pci_devq;
|
||||||
|
|
||||||
|
if (dinfo->cfg.vpd.vpd_reg) {
|
||||||
|
free(dinfo->cfg.vpd.vpd_ident, M_DEVBUF);
|
||||||
|
for (i = 0; i < dinfo->cfg.vpd.vpd_rocnt; i++)
|
||||||
|
free(dinfo->cfg.vpd.vpd_ros[i].value, M_DEVBUF);
|
||||||
|
free(dinfo->cfg.vpd.vpd_ros, M_DEVBUF);
|
||||||
|
for (i = 0; i < dinfo->cfg.vpd.vpd_wcnt; i++)
|
||||||
|
free(dinfo->cfg.vpd.vpd_w[i].value, M_DEVBUF);
|
||||||
|
free(dinfo->cfg.vpd.vpd_w, M_DEVBUF);
|
||||||
|
}
|
||||||
STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links);
|
STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links);
|
||||||
free(dinfo, M_DEVBUF);
|
free(dinfo, M_DEVBUF);
|
||||||
|
|
||||||
@ -766,6 +1075,8 @@ pci_disable_io_method(device_t dev, device_t child, int space)
|
|||||||
void
|
void
|
||||||
pci_print_verbose(struct pci_devinfo *dinfo)
|
pci_print_verbose(struct pci_devinfo *dinfo)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (bootverbose) {
|
if (bootverbose) {
|
||||||
pcicfgregs *cfg = &dinfo->cfg;
|
pcicfgregs *cfg = &dinfo->cfg;
|
||||||
|
|
||||||
@ -794,6 +1105,31 @@ pci_print_verbose(struct pci_devinfo *dinfo)
|
|||||||
cfg->pp.pp_cap & PCIM_PCAP_D2SUPP ? " D2" : "",
|
cfg->pp.pp_cap & PCIM_PCAP_D2SUPP ? " D2" : "",
|
||||||
status & PCIM_PSTAT_DMASK);
|
status & PCIM_PSTAT_DMASK);
|
||||||
}
|
}
|
||||||
|
if (cfg->vpd.vpd_reg) {
|
||||||
|
printf("\tVPD Ident: %s\n", cfg->vpd.vpd_ident);
|
||||||
|
for (i = 0; i < cfg->vpd.vpd_rocnt; i++) {
|
||||||
|
struct vpd_readonly *vrop;
|
||||||
|
vrop = &cfg->vpd.vpd_ros[i];
|
||||||
|
if (strncmp("CP", vrop->keyword, 2) == 0)
|
||||||
|
printf("CP: id %d, BAR%d, off %#x\n",
|
||||||
|
vrop->value[0], vrop->value[1],
|
||||||
|
le16toh(
|
||||||
|
*(uint16_t *)&vrop->value[2]));
|
||||||
|
else if (strncmp("RV", vrop->keyword, 2) == 0)
|
||||||
|
printf("RV: %#hhx\n", vrop->value[0]);
|
||||||
|
else
|
||||||
|
printf("\t%.2s: %s\n", vrop->keyword,
|
||||||
|
vrop->value);
|
||||||
|
}
|
||||||
|
for (i = 0; i < cfg->vpd.vpd_wcnt; i++) {
|
||||||
|
struct vpd_write *vwp;
|
||||||
|
vwp = &cfg->vpd.vpd_w[i];
|
||||||
|
if (strncmp("RW", vwp->keyword, 2) != 0)
|
||||||
|
printf("\t%.2s(%#x-%#x): %s\n",
|
||||||
|
vwp->keyword, vwp->start,
|
||||||
|
vwp->start + vwp->len, vwp->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (cfg->msi.msi_data) {
|
if (cfg->msi.msi_data) {
|
||||||
int ctrl;
|
int ctrl;
|
||||||
|
|
||||||
|
@ -56,6 +56,19 @@ METHOD int set_powerstate {
|
|||||||
int state;
|
int state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
METHOD int get_vpd_ident {
|
||||||
|
device_t dev;
|
||||||
|
device_t child;
|
||||||
|
const char **identptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
METHOD int get_vpd_readonly {
|
||||||
|
device_t dev;
|
||||||
|
device_t child;
|
||||||
|
const char *kw;
|
||||||
|
const char **vptr;
|
||||||
|
};
|
||||||
|
|
||||||
METHOD int enable_busmaster {
|
METHOD int enable_busmaster {
|
||||||
device_t dev;
|
device_t dev;
|
||||||
device_t child;
|
device_t child;
|
||||||
|
@ -49,6 +49,10 @@ int pci_read_ivar(device_t dev, device_t child, int which,
|
|||||||
uintptr_t *result);
|
uintptr_t *result);
|
||||||
int pci_write_ivar(device_t dev, device_t child, int which,
|
int pci_write_ivar(device_t dev, device_t child, int which,
|
||||||
uintptr_t value);
|
uintptr_t value);
|
||||||
|
int pci_get_vpd_ident_method(device_t dev, device_t child,
|
||||||
|
const char **identptr);
|
||||||
|
int pci_get_vpd_readonly_method(device_t dev, device_t child,
|
||||||
|
const char *kw, const char **vptr);
|
||||||
int pci_set_powerstate_method(device_t dev, device_t child,
|
int pci_set_powerstate_method(device_t dev, device_t child,
|
||||||
int state);
|
int state);
|
||||||
int pci_get_powerstate_method(device_t dev, device_t child);
|
int pci_get_powerstate_method(device_t dev, device_t child);
|
||||||
|
@ -59,6 +59,27 @@ struct pcicfg_pp {
|
|||||||
uint8_t pp_data; /* config space address of PCI power data reg */
|
uint8_t pp_data; /* config space address of PCI power data reg */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct vpd_readonly {
|
||||||
|
char keyword[2];
|
||||||
|
char *value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vpd_write {
|
||||||
|
char keyword[2];
|
||||||
|
char *value;
|
||||||
|
int start;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pcicfg_vpd {
|
||||||
|
uint8_t vpd_reg; /* base register, + 2 for addr, + 4 data */
|
||||||
|
char *vpd_ident; /* string identifier */
|
||||||
|
int vpd_rocnt;
|
||||||
|
struct vpd_readonly *vpd_ros;
|
||||||
|
int vpd_wcnt;
|
||||||
|
struct vpd_write *vpd_w;
|
||||||
|
};
|
||||||
|
|
||||||
/* Interesting values for PCI MSI */
|
/* Interesting values for PCI MSI */
|
||||||
struct pcicfg_msi {
|
struct pcicfg_msi {
|
||||||
uint16_t msi_ctrl; /* Message Control */
|
uint16_t msi_ctrl; /* Message Control */
|
||||||
@ -103,6 +124,7 @@ typedef struct pcicfg {
|
|||||||
uint8_t func; /* config space function number */
|
uint8_t func; /* config space function number */
|
||||||
|
|
||||||
struct pcicfg_pp pp; /* pci power management */
|
struct pcicfg_pp pp; /* pci power management */
|
||||||
|
struct pcicfg_vpd vpd; /* pci vital product data */
|
||||||
struct pcicfg_msi msi; /* pci msi */
|
struct pcicfg_msi msi; /* pci msi */
|
||||||
} pcicfgregs;
|
} pcicfgregs;
|
||||||
|
|
||||||
@ -292,6 +314,18 @@ pci_disable_io(device_t dev, int space)
|
|||||||
return(PCI_DISABLE_IO(device_get_parent(dev), dev, space));
|
return(PCI_DISABLE_IO(device_get_parent(dev), dev, space));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __inline int
|
||||||
|
pci_get_vpd_ident(device_t dev, const char **identptr)
|
||||||
|
{
|
||||||
|
return(PCI_GET_VPD_IDENT(device_get_parent(dev), dev, identptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline int
|
||||||
|
pci_get_vpd_readonly(device_t dev, const char *kw, const char **identptr)
|
||||||
|
{
|
||||||
|
return(PCI_GET_VPD_READONLY(device_get_parent(dev), dev, kw, identptr));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the address range falls within the VGA defined address range(s)
|
* Check if the address range falls within the VGA defined address range(s)
|
||||||
*/
|
*/
|
||||||
|
@ -256,9 +256,6 @@ static u_int8_t sk_win_read_1(struct sk_softc *, int);
|
|||||||
static void sk_win_write_4(struct sk_softc *, int, u_int32_t);
|
static void sk_win_write_4(struct sk_softc *, int, u_int32_t);
|
||||||
static void sk_win_write_2(struct sk_softc *, int, u_int32_t);
|
static void sk_win_write_2(struct sk_softc *, int, u_int32_t);
|
||||||
static void sk_win_write_1(struct sk_softc *, int, u_int32_t);
|
static void sk_win_write_1(struct sk_softc *, int, u_int32_t);
|
||||||
static u_int8_t sk_vpd_readbyte(struct sk_softc *, int);
|
|
||||||
static void sk_vpd_read_res(struct sk_softc *, struct vpd_res *, int);
|
|
||||||
static void sk_vpd_read(struct sk_softc *);
|
|
||||||
|
|
||||||
static int sk_miibus_readreg(device_t, int, int);
|
static int sk_miibus_readreg(device_t, int, int);
|
||||||
static int sk_miibus_writereg(device_t, int, int, int);
|
static int sk_miibus_writereg(device_t, int, int, int);
|
||||||
@ -472,113 +469,6 @@ sk_win_write_1(sc, reg, val)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The VPD EEPROM contains Vital Product Data, as suggested in
|
|
||||||
* the PCI 2.1 specification. The VPD data is separared into areas
|
|
||||||
* denoted by resource IDs. The SysKonnect VPD contains an ID string
|
|
||||||
* resource (the name of the adapter), a read-only area resource
|
|
||||||
* containing various key/data fields and a read/write area which
|
|
||||||
* can be used to store asset management information or log messages.
|
|
||||||
* We read the ID string and read-only into buffers attached to
|
|
||||||
* the controller softc structure for later use. At the moment,
|
|
||||||
* we only use the ID string during skc_attach().
|
|
||||||
*/
|
|
||||||
static u_int8_t
|
|
||||||
sk_vpd_readbyte(sc, addr)
|
|
||||||
struct sk_softc *sc;
|
|
||||||
int addr;
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
sk_win_write_2(sc, SK_PCI_REG(SK_PCI_VPD_ADDR), addr);
|
|
||||||
for (i = 0; i < SK_TIMEOUT; i++) {
|
|
||||||
/* ASUS LOM takes a very long time to read VPD. */
|
|
||||||
DELAY(100);
|
|
||||||
if (sk_win_read_2(sc,
|
|
||||||
SK_PCI_REG(SK_PCI_VPD_ADDR)) & SK_VPD_FLAG)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == SK_TIMEOUT)
|
|
||||||
return(0);
|
|
||||||
|
|
||||||
return(sk_win_read_1(sc, SK_PCI_REG(SK_PCI_VPD_DATA)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sk_vpd_read_res(sc, res, addr)
|
|
||||||
struct sk_softc *sc;
|
|
||||||
struct vpd_res *res;
|
|
||||||
int addr;
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
u_int8_t *ptr;
|
|
||||||
|
|
||||||
ptr = (u_int8_t *)res;
|
|
||||||
for (i = 0; i < sizeof(struct vpd_res); i++)
|
|
||||||
ptr[i] = sk_vpd_readbyte(sc, i + addr);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sk_vpd_read(sc)
|
|
||||||
struct sk_softc *sc;
|
|
||||||
{
|
|
||||||
int pos = 0, i;
|
|
||||||
struct vpd_res res;
|
|
||||||
|
|
||||||
/* Check VPD capability */
|
|
||||||
if (sk_win_read_1(sc, SK_PCI_REG(SK_PCI_VPD_CAPID)) != PCIY_VPD)
|
|
||||||
return;
|
|
||||||
if (sc->sk_vpd_prodname != NULL)
|
|
||||||
free(sc->sk_vpd_prodname, M_DEVBUF);
|
|
||||||
if (sc->sk_vpd_readonly != NULL)
|
|
||||||
free(sc->sk_vpd_readonly, M_DEVBUF);
|
|
||||||
sc->sk_vpd_prodname = NULL;
|
|
||||||
sc->sk_vpd_readonly = NULL;
|
|
||||||
sc->sk_vpd_readonly_len = 0;
|
|
||||||
|
|
||||||
sk_vpd_read_res(sc, &res, pos);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bail out quietly if the eeprom appears to be missing or empty.
|
|
||||||
*/
|
|
||||||
if (res.vr_id == 0xff && res.vr_len == 0xff && res.vr_pad == 0xff)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (res.vr_id != VPD_RES_ID) {
|
|
||||||
device_printf(sc->sk_dev, "bad VPD resource id: expected %x "
|
|
||||||
"got %x\n", VPD_RES_ID, res.vr_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += sizeof(res);
|
|
||||||
sc->sk_vpd_prodname = malloc(res.vr_len + 1, M_DEVBUF, M_NOWAIT);
|
|
||||||
if (sc->sk_vpd_prodname != NULL) {
|
|
||||||
for (i = 0; i < res.vr_len; i++)
|
|
||||||
sc->sk_vpd_prodname[i] = sk_vpd_readbyte(sc, i + pos);
|
|
||||||
sc->sk_vpd_prodname[i] = '\0';
|
|
||||||
}
|
|
||||||
pos += res.vr_len;
|
|
||||||
|
|
||||||
sk_vpd_read_res(sc, &res, pos);
|
|
||||||
|
|
||||||
if (res.vr_id != VPD_RES_READ) {
|
|
||||||
device_printf(sc->sk_dev, "bad VPD resource id: expected %x "
|
|
||||||
"got %x\n", VPD_RES_READ, res.vr_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += sizeof(res);
|
|
||||||
sc->sk_vpd_readonly = malloc(res.vr_len, M_DEVBUF, M_NOWAIT);
|
|
||||||
for (i = 0; i < res.vr_len; i++)
|
|
||||||
sc->sk_vpd_readonly[i] = sk_vpd_readbyte(sc, i + pos);
|
|
||||||
sc->sk_vpd_readonly_len = res.vr_len;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sk_miibus_readreg(dev, phy, reg)
|
sk_miibus_readreg(dev, phy, reg)
|
||||||
device_t dev;
|
device_t dev;
|
||||||
@ -1690,7 +1580,8 @@ skc_attach(dev)
|
|||||||
struct sk_softc *sc;
|
struct sk_softc *sc;
|
||||||
int error = 0, *port, sk_macs;
|
int error = 0, *port, sk_macs;
|
||||||
uint8_t skrs;
|
uint8_t skrs;
|
||||||
char *pname, *revstr;
|
const char *pname;
|
||||||
|
char *revstr;
|
||||||
|
|
||||||
sc = device_get_softc(dev);
|
sc = device_get_softc(dev);
|
||||||
sc->sk_dev = dev;
|
sc->sk_dev = dev;
|
||||||
@ -1757,9 +1648,6 @@ skc_attach(dev)
|
|||||||
/* Reset the adapter. */
|
/* Reset the adapter. */
|
||||||
sk_reset(sc);
|
sk_reset(sc);
|
||||||
|
|
||||||
/* Read and save vital product data from EEPROM. */
|
|
||||||
sk_vpd_read(sc);
|
|
||||||
|
|
||||||
skrs = sk_win_read_1(sc, SK_EPROM0);
|
skrs = sk_win_read_1(sc, SK_EPROM0);
|
||||||
if (sc->sk_type == SK_GENESIS) {
|
if (sc->sk_type == SK_GENESIS) {
|
||||||
/* Read and save RAM size and RAMbuffer offset */
|
/* Read and save RAM size and RAMbuffer offset */
|
||||||
@ -1811,7 +1699,8 @@ skc_attach(dev)
|
|||||||
case DEVICEID_DLINK_DGE530T_A1:
|
case DEVICEID_DLINK_DGE530T_A1:
|
||||||
case DEVICEID_DLINK_DGE530T_B1:
|
case DEVICEID_DLINK_DGE530T_B1:
|
||||||
/* Stay with VPD PN. */
|
/* Stay with VPD PN. */
|
||||||
pname = sc->sk_vpd_prodname;
|
if (pci_get_vpd_ident(dev, &pname))
|
||||||
|
goto vpdfailed;
|
||||||
break;
|
break;
|
||||||
case DEVICEID_SK_V2:
|
case DEVICEID_SK_V2:
|
||||||
case DEVICEID_MRVL_4360:
|
case DEVICEID_MRVL_4360:
|
||||||
@ -1821,7 +1710,8 @@ skc_attach(dev)
|
|||||||
switch (sc->sk_type) {
|
switch (sc->sk_type) {
|
||||||
case SK_GENESIS:
|
case SK_GENESIS:
|
||||||
/* Stay with VPD PN. */
|
/* Stay with VPD PN. */
|
||||||
pname = sc->sk_vpd_prodname;
|
if (pci_get_vpd_ident(dev, &pname))
|
||||||
|
goto vpdfailed;
|
||||||
break;
|
break;
|
||||||
case SK_YUKON:
|
case SK_YUKON:
|
||||||
pname = "Marvell Yukon Gigabit Ethernet";
|
pname = "Marvell Yukon Gigabit Ethernet";
|
||||||
@ -1861,6 +1751,7 @@ skc_attach(dev)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
vpdfailed:
|
||||||
device_printf(dev, "unknown device: vendor=%04x, device=%04x, "
|
device_printf(dev, "unknown device: vendor=%04x, device=%04x, "
|
||||||
"chipver=%02x, rev=%x\n",
|
"chipver=%02x, rev=%x\n",
|
||||||
pci_get_vendor(dev), pci_get_device(dev),
|
pci_get_vendor(dev), pci_get_device(dev),
|
||||||
@ -1908,33 +1799,6 @@ skc_attach(dev)
|
|||||||
pname != NULL ? pname : "<unknown>", revstr, sc->sk_rev);
|
pname != NULL ? pname : "<unknown>", revstr, sc->sk_rev);
|
||||||
|
|
||||||
if (bootverbose) {
|
if (bootverbose) {
|
||||||
if (sc->sk_vpd_readonly != NULL &&
|
|
||||||
sc->sk_vpd_readonly_len != 0) {
|
|
||||||
char buf[256];
|
|
||||||
char *dp = sc->sk_vpd_readonly;
|
|
||||||
uint16_t l, len = sc->sk_vpd_readonly_len;
|
|
||||||
|
|
||||||
while (len >= 3) {
|
|
||||||
if ((*dp == 'P' && *(dp+1) == 'N') ||
|
|
||||||
(*dp == 'E' && *(dp+1) == 'C') ||
|
|
||||||
(*dp == 'M' && *(dp+1) == 'N') ||
|
|
||||||
(*dp == 'S' && *(dp+1) == 'N')) {
|
|
||||||
l = 0;
|
|
||||||
while (l < *(dp+2)) {
|
|
||||||
buf[l] = *(dp+3+l);
|
|
||||||
++l;
|
|
||||||
}
|
|
||||||
buf[l] = '\0';
|
|
||||||
device_printf(dev, "%c%c: %s\n",
|
|
||||||
*dp, *(dp+1), buf);
|
|
||||||
len -= (3 + l);
|
|
||||||
dp += (3 + l);
|
|
||||||
} else {
|
|
||||||
len -= (3 + *(dp+2));
|
|
||||||
dp += (3 + *(dp+2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
device_printf(dev, "chip ver = 0x%02x\n", sc->sk_type);
|
device_printf(dev, "chip ver = 0x%02x\n", sc->sk_type);
|
||||||
device_printf(dev, "chip rev = 0x%02x\n", sc->sk_rev);
|
device_printf(dev, "chip rev = 0x%02x\n", sc->sk_rev);
|
||||||
device_printf(dev, "SK_EPROM0 = 0x%02x\n", skrs);
|
device_printf(dev, "SK_EPROM0 = 0x%02x\n", skrs);
|
||||||
@ -2084,11 +1948,6 @@ skc_detach(dev)
|
|||||||
bus_generic_detach(dev);
|
bus_generic_detach(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sc->sk_vpd_prodname != NULL)
|
|
||||||
free(sc->sk_vpd_prodname, M_DEVBUF);
|
|
||||||
if (sc->sk_vpd_readonly != NULL)
|
|
||||||
free(sc->sk_vpd_readonly, M_DEVBUF);
|
|
||||||
|
|
||||||
if (sc->sk_intrhand)
|
if (sc->sk_intrhand)
|
||||||
bus_teardown_intr(dev, sc->sk_res[1], sc->sk_intrhand);
|
bus_teardown_intr(dev, sc->sk_res[1], sc->sk_intrhand);
|
||||||
bus_release_resources(dev, sc->sk_res_spec, sc->sk_res);
|
bus_release_resources(dev, sc->sk_res_spec, sc->sk_res);
|
||||||
|
@ -1290,10 +1290,6 @@
|
|||||||
#define SK_PCI_PWRMGMTCAP 0x004A /* 16 bits */
|
#define SK_PCI_PWRMGMTCAP 0x004A /* 16 bits */
|
||||||
#define SK_PCI_PWRMGMTCTRL 0x004C /* 16 bits */
|
#define SK_PCI_PWRMGMTCTRL 0x004C /* 16 bits */
|
||||||
#define SK_PCI_PME_EVENT 0x004F
|
#define SK_PCI_PME_EVENT 0x004F
|
||||||
#define SK_PCI_VPD_CAPID 0x0050
|
|
||||||
#define SK_PCI_VPD_NEXTPTR 0x0051
|
|
||||||
#define SK_PCI_VPD_ADDR 0x0052
|
|
||||||
#define SK_PCI_VPD_DATA 0x0054
|
|
||||||
|
|
||||||
#define SK_PSTATE_MASK 0x0003
|
#define SK_PSTATE_MASK 0x0003
|
||||||
#define SK_PSTATE_D0 0x0000
|
#define SK_PSTATE_D0 0x0000
|
||||||
@ -1303,30 +1299,6 @@
|
|||||||
#define SK_PME_EN 0x0010
|
#define SK_PME_EN 0x0010
|
||||||
#define SK_PME_STATUS 0x8000
|
#define SK_PME_STATUS 0x8000
|
||||||
|
|
||||||
/*
|
|
||||||
* VPD flag bit. Set to 0 to initiate a read, will become 1 when
|
|
||||||
* read is complete. Set to 1 to initiate a write, will become 0
|
|
||||||
* when write is finished.
|
|
||||||
*/
|
|
||||||
#define SK_VPD_FLAG 0x8000
|
|
||||||
|
|
||||||
/* VPD structures */
|
|
||||||
struct vpd_res {
|
|
||||||
u_int8_t vr_id;
|
|
||||||
u_int8_t vr_len;
|
|
||||||
u_int8_t vr_pad;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct vpd_key {
|
|
||||||
char vk_key[2];
|
|
||||||
u_int8_t vk_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define VPD_RES_ID 0x82 /* ID string */
|
|
||||||
#define VPD_RES_READ 0x90 /* start of read only area */
|
|
||||||
#define VPD_RES_WRITE 0x81 /* start of read/write area */
|
|
||||||
#define VPD_RES_END 0x78 /* end tag */
|
|
||||||
|
|
||||||
#define CSR_WRITE_4(sc, reg, val) \
|
#define CSR_WRITE_4(sc, reg, val) \
|
||||||
bus_write_4((sc)->sk_res[0], (reg), (val))
|
bus_write_4((sc)->sk_res[0], (reg), (val))
|
||||||
#define CSR_WRITE_2(sc, reg, val) \
|
#define CSR_WRITE_2(sc, reg, val) \
|
||||||
@ -1528,9 +1500,6 @@ struct sk_softc {
|
|||||||
u_int8_t sk_type;
|
u_int8_t sk_type;
|
||||||
u_int8_t sk_rev;
|
u_int8_t sk_rev;
|
||||||
u_int8_t spare;
|
u_int8_t spare;
|
||||||
char *sk_vpd_prodname;
|
|
||||||
char *sk_vpd_readonly;
|
|
||||||
uint16_t sk_vpd_readonly_len;
|
|
||||||
u_int32_t sk_rboff; /* RAMbuffer offset */
|
u_int32_t sk_rboff; /* RAMbuffer offset */
|
||||||
u_int32_t sk_ramsize; /* amount of RAM on NIC */
|
u_int32_t sk_ramsize; /* amount of RAM on NIC */
|
||||||
u_int32_t sk_pmd; /* physical media type */
|
u_int32_t sk_pmd; /* physical media type */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user