add pcie capability and read request size linux compat, some pci root support implement support for: - pcie_capability_read_dword() - pcie_capability_read_word() - pcie_capability_write_dword() - pcie_capability_write_word() - pcie_get_readrq() - pcie_set_readrq() implement the "struct pci_dev" bus->self member by creating a minimal fake "struct pci_dev" for the pci bus itself. this is kind of gross. it checks that the current device's parent is a netbsd "pci" device, and that it has a (grand) parent "ppb" device, and then fills in the fake device based upon the pci and ppb devices. add some PCIE_LCSR2_TGT_LSPEED encodings, and map them to linux names. map several other PCIE_LCSR and PCIE_LCAP names. uncomment several pcie code segments in radeon and amdgpu. (not sure that we can test the amdgpu_si.c change, as we use the radeon version and the amdgpu version hangs on the one machine i have.) Index: dev/pci/pcireg.h =================================================================== RCS file: /cvsroot/src/sys/dev/pci/pcireg.h,v retrieving revision 1.166 diff -p -u -r1.166 pcireg.h --- dev/pci/pcireg.h 20 Sep 2022 23:01:42 -0000 1.166 +++ dev/pci/pcireg.h 22 Sep 2022 02:48:09 -0000 @@ -1186,6 +1186,9 @@ typedef u_int8_t pci_revision_t; #define PCIE_LCAP2_DRS __BIT(31) /* DRS Supported */ #define PCIE_LCSR2 0x30 /* Link Control & Status 2 Register */ #define PCIE_LCSR2_TGT_LSPEED __BITS(3, 0) /* Target Link Speed */ +#define PCIE_LCSR2_TGT_LSPEED_2_5G 0x1 /* 2.5GT/s supported */ +#define PCIE_LCSR2_TGT_LSPEED_5G 0x2 /* 5.0GT/s supported */ +#define PCIE_LCSR2_TGT_LSPEED_8G 0x3 /* 8.0GT/s supported */ #define PCIE_LCSR2_ENT_COMPL __BIT(4) /* Enter Compliance */ #define PCIE_LCSR2_HW_AS_DIS __BIT(5) /* HW Autonomous Speed Disabl */ #define PCIE_LCSR2_SEL_DEEMP __BIT(6) /* Selectable De-emphasis */ Index: external/bsd/drm2/dist/drm/amd/amdgpu/amdgpu_cik.c =================================================================== RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/amd/amdgpu/amdgpu_cik.c,v retrieving revision 1.5 diff -p -u -r1.5 amdgpu_cik.c --- external/bsd/drm2/dist/drm/amd/amdgpu/amdgpu_cik.c 19 Dec 2021 10:59:01 -0000 1.5 +++ external/bsd/drm2/dist/drm/amd/amdgpu/amdgpu_cik.c 22 Sep 2022 02:48:09 -0000 @@ -1461,7 +1461,6 @@ static int cik_set_vce_clocks(struct amd static void cik_pcie_gen3_enable(struct amdgpu_device *adev) { -#ifndef __NetBSD__ /* XXX amdgpu pcie */ struct pci_dev *root = adev->pdev->bus->self; u32 speed_cntl, current_data_rate; int i; @@ -1644,7 +1643,6 @@ static void cik_pcie_gen3_enable(struct break; udelay(1); } -#endif } static void cik_program_aspm(struct amdgpu_device *adev) Index: external/bsd/drm2/dist/drm/amd/amdgpu/amdgpu_si.c =================================================================== RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/amd/amdgpu/amdgpu_si.c,v retrieving revision 1.3 diff -p -u -r1.3 amdgpu_si.c --- external/bsd/drm2/dist/drm/amd/amdgpu/amdgpu_si.c 19 Dec 2021 12:21:29 -0000 1.3 +++ external/bsd/drm2/dist/drm/amd/amdgpu/amdgpu_si.c 22 Sep 2022 02:48:09 -0000 @@ -1656,7 +1656,6 @@ static void si_init_golden_registers(str static void si_pcie_gen3_enable(struct amdgpu_device *adev) { -#ifndef __NetBSD__ /* XXX amdgpu pcie */ struct pci_dev *root = adev->pdev->bus->self; u32 speed_cntl, current_data_rate; int i; @@ -1827,7 +1826,6 @@ static void si_pcie_gen3_enable(struct a break; udelay(1); } -#endif /* __NetBSD__ */ } static inline u32 si_pif_phy0_rreg(struct amdgpu_device *adev, u32 reg) @@ -2002,9 +2000,6 @@ static void si_program_aspm(struct amdgp if (!disable_clkreq && !pci_is_root_bus(adev->pdev->bus)) { -#ifdef __NetBSD__ /* XXX amdgpu pcie */ - clk_req_support = false; -#else struct pci_dev *root = adev->pdev->bus->self; u32 lnkcap; @@ -2012,7 +2007,6 @@ static void si_program_aspm(struct amdgp pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap); if (lnkcap & PCI_EXP_LNKCAP_CLKPM) clk_req_support = true; -#endif } else { clk_req_support = false; } Index: external/bsd/drm2/dist/drm/radeon/radeon_cik.c =================================================================== RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/radeon/radeon_cik.c,v retrieving revision 1.6 diff -p -u -r1.6 radeon_cik.c --- external/bsd/drm2/dist/drm/radeon/radeon_cik.c 19 Dec 2021 09:54:20 -0000 1.6 +++ external/bsd/drm2/dist/drm/radeon/radeon_cik.c 22 Sep 2022 02:48:09 -0000 @@ -9564,7 +9564,6 @@ int cik_set_vce_clocks(struct radeon_dev static void cik_pcie_gen3_enable(struct radeon_device *rdev) { -#ifndef __NetBSD__ /* XXX radeon pcie */ struct pci_dev *root = rdev->pdev->bus->self; enum pci_bus_speed speed_cap; u32 speed_cntl, current_data_rate; @@ -9747,7 +9746,6 @@ static void cik_pcie_gen3_enable(struct break; udelay(1); } -#endif } static void cik_program_aspm(struct radeon_device *rdev) Index: external/bsd/drm2/dist/drm/radeon/radeon_si.c =================================================================== RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/radeon/radeon_si.c,v retrieving revision 1.4 diff -p -u -r1.4 radeon_si.c --- external/bsd/drm2/dist/drm/radeon/radeon_si.c 19 Dec 2021 09:56:27 -0000 1.4 +++ external/bsd/drm2/dist/drm/radeon/radeon_si.c 22 Sep 2022 02:48:09 -0000 @@ -7099,7 +7099,6 @@ int si_set_uvd_clocks(struct radeon_devi static void si_pcie_gen3_enable(struct radeon_device *rdev) { -#ifndef __NetBSD__ /* XXX radeon pcie */ struct pci_dev *root = rdev->pdev->bus->self; enum pci_bus_speed speed_cap; u32 speed_cntl, current_data_rate; @@ -7283,7 +7282,6 @@ static void si_pcie_gen3_enable(struct r break; udelay(1); } -#endif } static void si_program_aspm(struct radeon_device *rdev) Index: external/bsd/drm2/include/linux/pci.h =================================================================== RCS file: /cvsroot/src/sys/external/bsd/drm2/include/linux/pci.h,v retrieving revision 1.54 diff -p -u -r1.54 pci.h --- external/bsd/drm2/include/linux/pci.h 20 Sep 2022 23:01:42 -0000 1.54 +++ external/bsd/drm2/include/linux/pci.h 22 Sep 2022 02:48:09 -0000 @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,7 @@ struct acpi_devnode; struct pci_driver; +struct pci_dev; struct pci_bus { /* NetBSD private members */ @@ -68,6 +70,8 @@ struct pci_bus { /* Linux API */ u_int number; + + struct pci_dev *self; }; struct pci_device_id { @@ -133,6 +137,21 @@ CTASSERT(PCI_CLASS_BRIDGE_ISA == 0x0601) #define PCI_CAP_ID_AGP PCI_CAP_AGP +#define PCI_EXP_LNKCTL PCIE_LCSR +#define PCI_EXP_LNKCTL_HAWD PCIE_LCSR_HAWD +#define PCI_EXP_DEVSTA (PCIE_DCSR + 2) +#define PCI_EXP_DEVSTA_TRPND (PCIE_DCSR_TRANSACTION_PND >> 16) +#define PCI_EXP_LNKCTL2 PCIE_LCAP2 +#define PCI_EXP_LNKCTL2_ENTER_COMP PCIE_LCSR2_ENT_COMPL +#define PCI_EXP_LNKCTL2_TX_MARGIN PCIE_LCSR2_TX_MARGIN +#define PCI_EXP_LNKCTL2_TLS PCIE_LCSR2_TGT_LSPEED +#define PCI_EXP_LNKCTL2_TLS_2_5GT PCIE_LCSR2_TGT_LSPEED_2_5G +#define PCI_EXP_LNKCTL2_TLS_5_0GT PCIE_LCSR2_TGT_LSPEED_5G +#define PCI_EXP_LNKCTL2_TLS_8_0GT PCIE_LCSR2_TGT_LSPEED_8G +#define PCI_EXP_LNKCAP PCIE_LCAP +#define PCI_EXP_LNKCAP_CLKPM PCIE_LCAP_CLOCK_PM + + typedef int pci_power_t; #define PCI_D0 0 @@ -262,6 +281,10 @@ enum pcie_link_width { #define pcibios_align_resource linux_pcibios_align_resource #define pcie_get_speed_cap linux_pcie_get_speed_cap #define pcie_bandwidth_available linux_pcie_bandwidth_available +#define pcie_read_config_dword linux_pcie_capability_read_dword +#define pcie_read_config_word linux_pcie_capability_read_word +#define pcie_write_config_dword linux_pcie_capability_write_dword +#define pcie_write_config_word linux_pcie_capability_write_word /* NetBSD local additions. */ void linux_pci_dev_init(struct pci_dev *, device_t, device_t, @@ -292,6 +315,11 @@ int pci_write_config_dword(struct pci_d int pci_write_config_word(struct pci_dev *, int, uint16_t); int pci_write_config_byte(struct pci_dev *, int, uint8_t); +int pcie_capability_read_dword(struct pci_dev *, int, uint32_t *); +int pcie_capability_read_word(struct pci_dev *, int, uint16_t *); +int pcie_capability_write_dword(struct pci_dev *, int, uint32_t); +int pcie_capability_write_word(struct pci_dev *, int, uint16_t); + int pci_bus_read_config_dword(struct pci_bus *, unsigned, int, uint32_t *); int pci_bus_read_config_word(struct pci_bus *, unsigned, int, @@ -310,6 +338,9 @@ void pci_disable_msi(struct pci_dev *); void pci_set_master(struct pci_dev *); void pci_clear_master(struct pci_dev *); +int pcie_get_readrq(struct pci_dev *); +int pcie_set_readrq(struct pci_dev *, int); + bus_addr_t pcibios_align_resource(void *, const struct resource *, bus_addr_t, bus_size_t); int pci_bus_alloc_resource(struct pci_bus *, struct resource *, Index: external/bsd/drm2/linux/linux_pci.c =================================================================== RCS file: /cvsroot/src/sys/external/bsd/drm2/linux/linux_pci.c,v retrieving revision 1.24 diff -p -u -r1.24 linux_pci.c --- external/bsd/drm2/linux/linux_pci.c 20 Sep 2022 23:01:42 -0000 1.24 +++ external/bsd/drm2/linux/linux_pci.c 22 Sep 2022 02:48:09 -0000 @@ -73,6 +73,74 @@ pci_name(struct pci_dev *pdev) return device_xname(pci_dev_dev(pdev)); } +/* + * Setup enough of a parent that we can access config space. + * This is gross and grovels pci(4) and ppb(4) internals. + */ +static struct pci_dev * +alloc_fake_parent_device(device_t parent, const struct pci_attach_args *pa) +{ + + if (parent == NULL || !device_is_a(parent, "pci")) + return NULL; + + device_t pparent = device_parent(parent); + if (pparent == NULL || !device_is_a(pparent, "ppb")) + return NULL; + + struct pci_softc *pcisc = device_private(parent); + struct ppb_softc *ppbsc = device_private(pparent); + + struct pci_dev *parentdev = kmem_zalloc(sizeof(*parentdev), KM_SLEEP); + + /* Copy this device's pci_attach_args{} as a base-line. */ + struct pci_attach_args *npa = &parentdev->pd_pa; + *npa = *pa; + + /* Now update with stuff found in parent. */ + npa->pa_iot = pcisc->sc_iot; + npa->pa_memt = pcisc->sc_memt; + npa->pa_dmat = pcisc->sc_dmat; + npa->pa_dmat64 = pcisc->sc_dmat64; + npa->pa_pc = pcisc->sc_pc; + npa->pa_flags = 0; /* XXX? */ + + /* Copy the parent tag, and read some info about it. */ + npa->pa_tag = ppbsc->sc_tag; + pcireg_t id = pci_conf_read(npa->pa_pc, npa->pa_tag, PCI_ID_REG); + pcireg_t subid = pci_conf_read(npa->pa_pc, npa->pa_tag, + PCI_SUBSYS_ID_REG); + pcireg_t class = pci_conf_read(npa->pa_pc, npa->pa_tag, PCI_CLASS_REG); + + /* + * Fill in as much of pci_attach_args and pci_dev as reasonably possible. + * Most of this is not used currently. + */ + int bus, device, function; + pci_decompose_tag(npa->pa_pc, npa->pa_tag, &bus, &device, &function); + npa->pa_device = device; + npa->pa_function = function; + npa->pa_bus = bus; + npa->pa_id = id; + npa->pa_class = class; + npa->pa_intrswiz = pcisc->sc_intrswiz; + npa->pa_intrtag = pcisc->sc_intrtag; + npa->pa_intrpin = PCI_INTERRUPT_PIN_NONE; + + parentdev->pd_dev = parent; + + parentdev->bus = NULL; + parentdev->devfn = device << 3 | function; + parentdev->vendor = PCI_VENDOR(id); + parentdev->device = PCI_PRODUCT(id); + parentdev->subsystem_vendor = PCI_SUBSYS_VENDOR(subid); + parentdev->subsystem_device = PCI_SUBSYS_ID(subid); + parentdev->revision = PCI_REVISION(class); + parentdev->class = __SHIFTOUT(class, 0xffffff00UL); /* ? */ + + return parentdev; +} + void linux_pci_dev_init(struct pci_dev *pdev, device_t dev, device_t parent, const struct pci_attach_args *pa, int kludges) @@ -101,6 +169,7 @@ linux_pci_dev_init(struct pci_dev *pdev, pdev->bus->pb_pc = pa->pa_pc; pdev->bus->pb_dev = parent; pdev->bus->number = pa->pa_bus; + pdev->bus->self = alloc_fake_parent_device(parent, pa); pdev->devfn = PCI_DEVFN(pa->pa_device, pa->pa_function); pdev->vendor = PCI_VENDOR(pa->pa_id); pdev->device = PCI_PRODUCT(pa->pa_id); @@ -326,6 +395,131 @@ pci_clear_master(struct pci_dev *pdev) PCI_COMMAND_STATUS_REG, csr); } +int +pcie_capability_read_dword(struct pci_dev *pdev, int reg, uint32_t *valuep) +{ + pci_chipset_tag_t pc = pdev->pd_pa.pa_pc; + pcitag_t tag = pdev->pd_pa.pa_tag; + int off; + + *valuep = 0; + + /* Must have capabilities. */ + if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) == 0) + return 1; + + *valuep = pci_conf_read(pc, tag, off + reg); + + return 0; +} + +int +pcie_capability_read_word(struct pci_dev *pdev, int reg, uint16_t *valuep) +{ + pci_chipset_tag_t pc = pdev->pd_pa.pa_pc; + pcitag_t tag = pdev->pd_pa.pa_tag; + int off; + + *valuep = 0; + + /* Must have capabilities. */ + if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) == 0) + return 1; + + *valuep = pci_conf_read(pc, tag, off + (reg &~ 2)) >> (8 * (reg & 2)); + + return 0; +} + +int +pcie_capability_write_dword(struct pci_dev *pdev, int reg, uint32_t value) +{ + pci_chipset_tag_t pc = pdev->pd_pa.pa_pc; + pcitag_t tag = pdev->pd_pa.pa_tag; + int off; + + /* Must have capabilities. */ + if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) == 0) + return 1; + + pci_conf_write(pc, tag, off + reg, value); + + return 0; +} + +int +pcie_capability_write_word(struct pci_dev *pdev, int reg, uint16_t value) +{ + pci_chipset_tag_t pc = pdev->pd_pa.pa_pc; + pcitag_t tag = pdev->pd_pa.pa_tag; + int off; + + /* Must have capabilities. */ + if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) == 0) + return 1; + + pci_rmw_config(pc, tag, off + reg, 2, value); + + return 0; +} + +int +pcie_get_readrq(struct pci_dev *pdev) +{ + pci_chipset_tag_t pc = pdev->pd_pa.pa_pc; + pcitag_t tag = pdev->pd_pa.pa_tag; + pcireg_t reg; + int off; + + if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) == 0) + return -EINVAL; /* XXX NetBSD->Linux */ + + reg = pci_conf_read(pc, tag, off + PCIE_DCSR); + + return 128 << __SHIFTOUT(reg, PCIE_DCSR_MAX_READ_REQ); +} + +int +pcie_set_readrq(struct pci_dev *pdev, int val) +{ + pci_chipset_tag_t pc = pdev->pd_pa.pa_pc; + pcitag_t tag = pdev->pd_pa.pa_tag; + pcireg_t reg, newval = 0; + int off; + + switch (val) { + case 128: + newval = 0; + break; + case 256: + newval = 1; + break; + case 512: + newval = 2; + break; + case 1024: + newval = 3; + break; + case 2048: + newval = 4; + break; + case 4096: + newval = 5; + break; + default: + return -EINVAL; + } + + if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) == 0) + return -EINVAL; /* XXX NetBSD->Linux */ + + reg = pci_conf_read(pc, tag, off + PCIE_DCSR); + reg &= ~PCIE_DCSR_MAX_READ_REQ | (newval << 12); + pci_conf_write(pc, tag, off + PCIE_DCSR, reg); + + return 0; +} + bus_addr_t pcibios_align_resource(void *p, const struct resource *resource, bus_addr_t addr, bus_size_t size) @@ -780,6 +974,9 @@ linux_pci_dev_destroy(struct pci_dev *pd { unsigned i; + if (pdev->bus->self != NULL) { + kmem_free(pdev->bus->self, sizeof(*pdev->bus->self)); + } if (pdev->bus != NULL) { kmem_free(pdev->bus, sizeof(*pdev->bus)); pdev->bus = NULL;