diff --git a/share/man/man9/pci.9 b/share/man/man9/pci.9 index 9071debe26e2..e55ea02547b6 100644 --- a/share/man/man9/pci.9 +++ b/share/man/man9/pci.9 @@ -42,6 +42,9 @@ .Nm pci_find_device , .Nm pci_find_extcap , .Nm pci_find_htcap , +.Nm pci_find_next_cap , +.Nm pci_find_next_extcap , +.Nm pci_find_next_htcap , .Nm pci_find_pcie_root_port , .Nm pci_get_id , .Nm pci_get_max_payload , @@ -100,6 +103,12 @@ .Fn pci_find_extcap "device_t dev" "int capability" "int *capreg" .Ft int .Fn pci_find_htcap "device_t dev" "int capability" "int *capreg" +.Ft int +.Fn pci_find_next_cap "device_t dev" "int capability" "int start" "int *capreg" +.Ft int +.Fn pci_find_next_extcap "device_t dev" "int capability" "int start" "int *capreg" +.Ft int +.Fn pci_find_next_htcap "device_t dev" "int capability" "int start" "int *capreg" .Ft device_t .Fn pci_find_pcie_root_port "device_t dev" .Ft int @@ -330,6 +339,22 @@ returns zero. If the capability is not found or the device does not support capabilities, .Fn pci_find_cap returns an error. +The +.Fn pci_find_next_cap +function is used to locate the next instance of a PCI capability +register set for the device +.Fa dev . +The +.Fa start +should be the +.Fa *capreg +returned by a prior +.Fn pci_find_cap +or +.Fn pci_find_next_cap . +When no more instances are located +.Fn pci_find_next_cap +returns an error. .Pp The .Fn pci_find_extcap @@ -352,6 +377,22 @@ If the extended capability is not found or the device is not a PCI-express device, .Fn pci_find_extcap returns an error. +The +.Fn pci_find_next_extcap +function is used to locate the next instance of a PCI-express +extended capability register set for the device +.Fa dev . +The +.Fa start +should be the +.Fa *capreg +returned by a prior +.Fn pci_find_extcap +or +.Fn pci_find_next_extcap . +When no more instances are located +.Fn pci_find_next_extcap +returns an error. .Pp The .Fn pci_find_htcap @@ -373,6 +414,22 @@ returns zero. If the capability is not found or the device is not a HyperTransport device, .Fn pci_find_htcap returns an error. +The +.Fn pci_find_next_htcap +function is used to locate the next instance of a HyperTransport capability +register set for the device +.Fa dev . +The +.Fa start +should be the +.Fa *capreg +returned by a prior +.Fn pci_find_htcap +or +.Fn pci_find_next_htcap . +When no more instances are located +.Fn pci_find_next_htcap +returns an error. .Pp The .Fn pci_find_pcie_root_port diff --git a/sys/dev/pci/hostb_pci.c b/sys/dev/pci/hostb_pci.c index fade49dcc324..63dcf5685c3f 100644 --- a/sys/dev/pci/hostb_pci.c +++ b/sys/dev/pci/hostb_pci.c @@ -206,6 +206,14 @@ pci_hostb_find_cap(device_t dev, device_t child, int capability, return (pci_find_cap(dev, capability, capreg)); } +static int +pci_hostb_find_next_cap(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + + return (pci_find_next_cap(dev, capability, start, capreg)); +} + static int pci_hostb_find_extcap(device_t dev, device_t child, int capability, int *capreg) @@ -214,6 +222,14 @@ pci_hostb_find_extcap(device_t dev, device_t child, int capability, return (pci_find_extcap(dev, capability, capreg)); } +static int +pci_hostb_find_next_extcap(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + + return (pci_find_next_extcap(dev, capability, start, capreg)); +} + static int pci_hostb_find_htcap(device_t dev, device_t child, int capability, int *capreg) @@ -222,6 +238,14 @@ pci_hostb_find_htcap(device_t dev, device_t child, int capability, return (pci_find_htcap(dev, capability, capreg)); } +static int +pci_hostb_find_next_htcap(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + + return (pci_find_next_htcap(dev, capability, start, capreg)); +} + static device_method_t pci_hostb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pci_hostb_probe), @@ -252,8 +276,11 @@ static device_method_t pci_hostb_methods[] = { DEVMETHOD(pci_set_powerstate, pci_hostb_set_powerstate), DEVMETHOD(pci_assign_interrupt, pci_hostb_assign_interrupt), DEVMETHOD(pci_find_cap, pci_hostb_find_cap), + DEVMETHOD(pci_find_next_cap, pci_hostb_find_next_cap), DEVMETHOD(pci_find_extcap, pci_hostb_find_extcap), + DEVMETHOD(pci_find_next_extcap, pci_hostb_find_next_extcap), DEVMETHOD(pci_find_htcap, pci_hostb_find_htcap), + DEVMETHOD(pci_find_next_htcap, pci_hostb_find_next_htcap), { 0, 0 } }; diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index fb2989aa4d19..ec340024443b 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -183,8 +183,11 @@ static device_method_t pci_methods[] = { DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method), DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method), DEVMETHOD(pci_find_cap, pci_find_cap_method), + DEVMETHOD(pci_find_next_cap, pci_find_next_cap_method), DEVMETHOD(pci_find_extcap, pci_find_extcap_method), + DEVMETHOD(pci_find_next_extcap, pci_find_next_extcap_method), DEVMETHOD(pci_find_htcap, pci_find_htcap_method), + DEVMETHOD(pci_find_next_htcap, pci_find_next_htcap_method), DEVMETHOD(pci_alloc_msi, pci_alloc_msi_method), DEVMETHOD(pci_alloc_msix, pci_alloc_msix_method), DEVMETHOD(pci_enable_msi, pci_enable_msi_method), @@ -1377,7 +1380,7 @@ pci_find_htcap_method(device_t dev, device_t child, int capability, int *capreg) * Traverse the capabilities list checking each HT capability * to see if it matches the requested HT capability. */ - while (ptr != 0) { + for (;;) { val = pci_read_config(child, ptr + PCIR_HT_COMMAND, 2); if (capability == PCIM_HTCAP_SLAVE || capability == PCIM_HTCAP_HOST) @@ -1391,13 +1394,51 @@ pci_find_htcap_method(device_t dev, device_t child, int capability, int *capreg) } /* Skip to the next HT capability. */ - while (ptr != 0) { - ptr = pci_read_config(child, ptr + PCICAP_NEXTPTR, 1); - if (pci_read_config(child, ptr + PCICAP_ID, 1) == - PCIY_HT) - break; + if (pci_find_next_cap(child, PCIY_HT, ptr, &ptr) != 0) + break; + } + + return (ENOENT); +} + +/* + * Find the next requested HyperTransport capability after start and return + * the offset in configuration space via the pointer provided. The function + * returns 0 on success and an error code otherwise. + */ +int +pci_find_next_htcap_method(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + int ptr; + uint16_t val; + + KASSERT(pci_read_config(child, start + PCICAP_ID, 1) == PCIY_HT, + ("start capability is not HyperTransport capability")); + ptr = start; + + /* + * Traverse the capabilities list checking each HT capability + * to see if it matches the requested HT capability. + */ + for (;;) { + /* Skip to the next HT capability. */ + if (pci_find_next_cap(child, PCIY_HT, ptr, &ptr) != 0) + break; + + val = pci_read_config(child, ptr + PCIR_HT_COMMAND, 2); + if (capability == PCIM_HTCAP_SLAVE || + capability == PCIM_HTCAP_HOST) + val &= 0xe000; + else + val &= PCIM_HTCMD_CAP_MASK; + if (val == capability) { + if (capreg != NULL) + *capreg = ptr; + return (0); } } + return (ENOENT); } @@ -1412,8 +1453,8 @@ pci_find_cap_method(device_t dev, device_t child, int capability, { struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; - u_int32_t status; - u_int8_t ptr; + uint32_t status; + uint8_t ptr; /* * Check the CAP_LIST bit of the PCI status register first. @@ -1454,6 +1495,33 @@ pci_find_cap_method(device_t dev, device_t child, int capability, return (ENOENT); } +/* + * Find the next requested capability after start and return the offset in + * configuration space via the pointer provided. The function returns + * 0 on success and an error code otherwise. + */ +int +pci_find_next_cap_method(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + uint8_t ptr; + + KASSERT(pci_read_config(child, start + PCICAP_ID, 1) == capability, + ("start capability is not expected capability")); + + ptr = pci_read_config(child, start + PCICAP_NEXTPTR, 1); + while (ptr != 0) { + if (pci_read_config(child, ptr + PCICAP_ID, 1) == capability) { + if (capreg != NULL) + *capreg = ptr; + return (0); + } + ptr = pci_read_config(child, ptr + PCICAP_NEXTPTR, 1); + } + + return (ENOENT); +} + /* * Find the requested extended capability and return the offset in * configuration space via the pointer provided. The function returns @@ -1491,6 +1559,41 @@ pci_find_extcap_method(device_t dev, device_t child, int capability, return (ENOENT); } +/* + * Find the next requested extended capability after start and return the + * offset in configuration space via the pointer provided. The function + * returns 0 on success and an error code otherwise. + */ +int +pci_find_next_extcap_method(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + uint32_t ecap; + uint16_t ptr; + + /* Only supported for PCI-express devices. */ + if (cfg->pcie.pcie_location == 0) + return (ENXIO); + + ecap = pci_read_config(child, start, 4); + KASSERT(PCI_EXTCAP_ID(ecap) == capability, + ("start extended capability is not expected capability")); + ptr = PCI_EXTCAP_NEXTPTR(ecap); + while (ptr != 0) { + ecap = pci_read_config(child, ptr, 4); + if (PCI_EXTCAP_ID(ecap) == capability) { + if (capreg != NULL) + *capreg = ptr; + return (0); + } + ptr = PCI_EXTCAP_NEXTPTR(ecap); + } + + return (ENOENT); +} + /* * Support for MSI-X message interrupts. */ diff --git a/sys/dev/pci/pci_if.m b/sys/dev/pci/pci_if.m index 07fabbd000ee..a7a2af7bb4a9 100644 --- a/sys/dev/pci/pci_if.m +++ b/sys/dev/pci/pci_if.m @@ -141,6 +141,14 @@ METHOD int find_cap { int *capreg; }; +METHOD int find_next_cap { + device_t dev; + device_t child; + int capability; + int start; + int *capreg; +}; + METHOD int find_extcap { device_t dev; device_t child; @@ -148,6 +156,14 @@ METHOD int find_extcap { int *capreg; }; +METHOD int find_next_extcap { + device_t dev; + device_t child; + int capability; + int start; + int *capreg; +}; + METHOD int find_htcap { device_t dev; device_t child; @@ -155,6 +171,14 @@ METHOD int find_htcap { int *capreg; }; +METHOD int find_next_htcap { + device_t dev; + device_t child; + int capability; + int start; + int *capreg; +}; + METHOD int alloc_msi { device_t dev; device_t child; diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h index dd254e66079f..f468152b0a9a 100644 --- a/sys/dev/pci/pci_private.h +++ b/sys/dev/pci/pci_private.h @@ -90,10 +90,16 @@ int pci_enable_io_method(device_t dev, device_t child, int space); int pci_disable_io_method(device_t dev, device_t child, int space); int pci_find_cap_method(device_t dev, device_t child, int capability, int *capreg); +int pci_find_next_cap_method(device_t dev, device_t child, + int capability, int start, int *capreg); int pci_find_extcap_method(device_t dev, device_t child, int capability, int *capreg); +int pci_find_next_extcap_method(device_t dev, device_t child, + int capability, int start, int *capreg); int pci_find_htcap_method(device_t dev, device_t child, int capability, int *capreg); +int pci_find_next_htcap_method(device_t dev, device_t child, + int capability, int start, int *capreg); int pci_alloc_msi_method(device_t dev, device_t child, int *count); int pci_alloc_msix_method(device_t dev, device_t child, int *count); void pci_enable_msi_method(device_t dev, device_t child, diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index 8497490a2c05..991868438fef 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -467,18 +467,39 @@ pci_find_cap(device_t dev, int capability, int *capreg) return (PCI_FIND_CAP(device_get_parent(dev), dev, capability, capreg)); } +static __inline int +pci_find_next_cap(device_t dev, int capability, int start, int *capreg) +{ + return (PCI_FIND_NEXT_CAP(device_get_parent(dev), dev, capability, start, + capreg)); +} + static __inline int pci_find_extcap(device_t dev, int capability, int *capreg) { return (PCI_FIND_EXTCAP(device_get_parent(dev), dev, capability, capreg)); } +static __inline int +pci_find_next_extcap(device_t dev, int capability, int start, int *capreg) +{ + return (PCI_FIND_NEXT_EXTCAP(device_get_parent(dev), dev, capability, + start, capreg)); +} + static __inline int pci_find_htcap(device_t dev, int capability, int *capreg) { return (PCI_FIND_HTCAP(device_get_parent(dev), dev, capability, capreg)); } +static __inline int +pci_find_next_htcap(device_t dev, int capability, int start, int *capreg) +{ + return (PCI_FIND_NEXT_HTCAP(device_get_parent(dev), dev, capability, + start, capreg)); +} + static __inline int pci_alloc_msi(device_t dev, int *count) { diff --git a/sys/dev/pci/vga_pci.c b/sys/dev/pci/vga_pci.c index f68f206fa26d..3e19912a92c1 100644 --- a/sys/dev/pci/vga_pci.c +++ b/sys/dev/pci/vga_pci.c @@ -496,6 +496,14 @@ vga_pci_find_cap(device_t dev, device_t child, int capability, return (pci_find_cap(dev, capability, capreg)); } +static int +vga_pci_find_next_cap(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + + return (pci_find_next_cap(dev, capability, start, capreg)); +} + static int vga_pci_find_extcap(device_t dev, device_t child, int capability, int *capreg) @@ -504,6 +512,14 @@ vga_pci_find_extcap(device_t dev, device_t child, int capability, return (pci_find_extcap(dev, capability, capreg)); } +static int +vga_pci_find_next_extcap(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + + return (pci_find_next_extcap(dev, capability, start, capreg)); +} + static int vga_pci_find_htcap(device_t dev, device_t child, int capability, int *capreg) @@ -512,6 +528,14 @@ vga_pci_find_htcap(device_t dev, device_t child, int capability, return (pci_find_htcap(dev, capability, capreg)); } +static int +vga_pci_find_next_htcap(device_t dev, device_t child, int capability, + int start, int *capreg) +{ + + return (pci_find_next_htcap(dev, capability, start, capreg)); +} + static int vga_pci_alloc_msi(device_t dev, device_t child, int *count) { @@ -622,8 +646,11 @@ static device_method_t vga_pci_methods[] = { DEVMETHOD(pci_set_powerstate, vga_pci_set_powerstate), DEVMETHOD(pci_assign_interrupt, vga_pci_assign_interrupt), DEVMETHOD(pci_find_cap, vga_pci_find_cap), + DEVMETHOD(pci_find_next_cap, vga_pci_find_next_cap), DEVMETHOD(pci_find_extcap, vga_pci_find_extcap), + DEVMETHOD(pci_find_next_extcap, vga_pci_find_next_extcap), DEVMETHOD(pci_find_htcap, vga_pci_find_htcap), + DEVMETHOD(pci_find_next_htcap, vga_pci_find_next_htcap), DEVMETHOD(pci_alloc_msi, vga_pci_alloc_msi), DEVMETHOD(pci_alloc_msix, vga_pci_alloc_msix), DEVMETHOD(pci_remap_msix, vga_pci_remap_msix),