diff options
author | jmg <jmg@FreeBSD.org> | 2006-10-09 16:15:56 +0000 |
---|---|---|
committer | jmg <jmg@FreeBSD.org> | 2006-10-09 16:15:56 +0000 |
commit | 6a8dfa76182b17f26de62b30dea11f79111400b7 (patch) | |
tree | 1d727b5091a8658d90d11ec0750cf847f4a210f6 /sys/dev/pci | |
parent | 03ea52e1daed73beb0329e7b08c841ce9c831f6d (diff) | |
download | FreeBSD-src-6a8dfa76182b17f26de62b30dea11f79111400b7.zip FreeBSD-src-6a8dfa76182b17f26de62b30dea11f79111400b7.tar.gz |
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
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/pci.c | 342 | ||||
-rw-r--r-- | sys/dev/pci/pci_if.m | 13 | ||||
-rw-r--r-- | sys/dev/pci/pci_private.h | 4 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 34 |
4 files changed, 390 insertions, 3 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 1816a26..ca4aee1 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -41,7 +41,7 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/queue.h> #include <sys/sysctl.h> -#include <sys/types.h> +#include <sys/endian.h> #include <vm/vm.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, 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[] = { /* Device interface */ @@ -127,6 +134,8 @@ static device_method_t pci_methods[] = { DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method), DEVMETHOD(pci_enable_io, pci_enable_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_set_powerstate, pci_set_powerstate_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 */ - struct pci_devinfo * 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; break; case 2: - ptrptr = PCIR_CAP_PTR_2; + ptrptr = PCIR_CAP_PTR_2; /* cardbus capabilities ptr */ break; default: 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_msgnum = 1 << ((cfg->msi.msi_ctrl & 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: + 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 } +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 * capability entry or 0 if the specified capability was not found. @@ -532,9 +831,19 @@ int pci_freecfg(struct pci_devinfo *dinfo) { struct devlist *devlist_head; + int i; 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); free(dinfo, M_DEVBUF); @@ -766,6 +1075,8 @@ pci_disable_io_method(device_t dev, device_t child, int space) void pci_print_verbose(struct pci_devinfo *dinfo) { + int i; + if (bootverbose) { pcicfgregs *cfg = &dinfo->cfg; @@ -794,6 +1105,31 @@ pci_print_verbose(struct pci_devinfo *dinfo) cfg->pp.pp_cap & PCIM_PCAP_D2SUPP ? " D2" : "", 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) { int ctrl; diff --git a/sys/dev/pci/pci_if.m b/sys/dev/pci/pci_if.m index 0de5696..df2456a 100644 --- a/sys/dev/pci/pci_if.m +++ b/sys/dev/pci/pci_if.m @@ -56,6 +56,19 @@ METHOD int set_powerstate { 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 { device_t dev; device_t child; diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h index b6bbaa0..dc1866f 100644 --- a/sys/dev/pci/pci_private.h +++ b/sys/dev/pci/pci_private.h @@ -49,6 +49,10 @@ int pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result); int pci_write_ivar(device_t dev, device_t child, int which, 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 state); int pci_get_powerstate_method(device_t dev, device_t child); diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index ade206a..fc37941 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -59,6 +59,27 @@ struct pcicfg_pp { 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 */ struct pcicfg_msi { uint16_t msi_ctrl; /* Message Control */ @@ -103,6 +124,7 @@ typedef struct pcicfg { uint8_t func; /* config space function number */ struct pcicfg_pp pp; /* pci power management */ + struct pcicfg_vpd vpd; /* pci vital product data */ struct pcicfg_msi msi; /* pci msi */ } pcicfgregs; @@ -292,6 +314,18 @@ pci_disable_io(device_t dev, int 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) */ |