From 1903ba82823b244e92764a9d899ebaac5ebcd5ec Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 12 Nov 2018 11:16:10 -0600 Subject: PCI/MSI: Remove unused __write_msi_msg() and write_msi_msg() Remove unused __write_msi_msg() and write_msi_msg(). These were added by 83a18912b0e8 ("PCI/MSI: Rename write_msi_msg() to pci_write_msi_msg()"), they served their purpose, and they're no longer needed. Signed-off-by: Bjorn Helgaas CC: Jiang Liu # 83a18912b0e8 author --- include/linux/msi.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/msi.h b/include/linux/msi.h index 7e9b81c3b50d..f934982a0e68 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -148,15 +148,6 @@ u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); void pci_msi_mask_irq(struct irq_data *data); void pci_msi_unmask_irq(struct irq_data *data); -/* Conversion helpers. Should be removed after merging */ -static inline void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) -{ - __pci_write_msi_msg(entry, msg); -} -static inline void write_msi_msg(int irq, struct msi_msg *msg) -{ - pci_write_msi_msg(irq, msg); -} static inline void mask_msi_irq(struct irq_data *data) { pci_msi_mask_irq(data); -- cgit v1.2.3 From fc2786545395e0ca69f245e5617fee0639ff0d25 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 21 Mar 2019 14:39:24 -0500 Subject: PCI/MSI: Remove unused mask_msi_irq() and unmask_msi_irq() Change pcie-xilinx-nwl.c to use pci_msi_mask_irq() and pci_msi_unmask_irq() like all other PCI host controller drivers. Remove the now-unused mask_msi_irq() and unmask_msi_irq(). Signed-off-by: Bjorn Helgaas CC: Michal Simek CC: linux-arm-kernel@lists.infradead.org --- drivers/pci/controller/pcie-xilinx-nwl.c | 9 ++++----- include/linux/msi.h | 9 --------- 2 files changed, 4 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 81538d77f790..3b031f00a94a 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -438,11 +438,10 @@ static const struct irq_domain_ops legacy_domain_ops = { #ifdef CONFIG_PCI_MSI static struct irq_chip nwl_msi_irq_chip = { .name = "nwl_pcie:msi", - .irq_enable = unmask_msi_irq, - .irq_disable = mask_msi_irq, - .irq_mask = mask_msi_irq, - .irq_unmask = unmask_msi_irq, - + .irq_enable = pci_msi_unmask_irq, + .irq_disable = pci_msi_mask_irq, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, }; static struct msi_domain_info nwl_msi_domain_info = { diff --git a/include/linux/msi.h b/include/linux/msi.h index f934982a0e68..052f04fcf953 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -148,15 +148,6 @@ u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); void pci_msi_mask_irq(struct irq_data *data); void pci_msi_unmask_irq(struct irq_data *data); -static inline void mask_msi_irq(struct irq_data *data) -{ - pci_msi_mask_irq(data); -} -static inline void unmask_msi_irq(struct irq_data *data) -{ - pci_msi_unmask_irq(data); -} - /* * The arch hooks to setup up msi irqs. Those functions are * implemented as weak symbols so that they /can/ be overriden by -- cgit v1.2.3 From 4ec73791a64bab25cabf16a6067ee478692e506d Mon Sep 17 00:00:00 2001 From: Stefan Mätje Date: Fri, 29 Mar 2019 18:07:35 +0100 Subject: PCI: Work around Pericom PCIe-to-PCI bridge Retrain Link erratum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to an erratum in some Pericom PCIe-to-PCI bridges in reverse mode (conventional PCI on primary side, PCIe on downstream side), the Retrain Link bit needs to be cleared manually to allow the link training to complete successfully. If it is not cleared manually, the link training is continuously restarted and no devices below the PCI-to-PCIe bridge can be accessed. That means drivers for devices below the bridge will be loaded but won't work and may even crash because the driver is only reading 0xffff. See the Pericom Errata Sheet PI7C9X111SLB_errata_rev1.2_102711.pdf for details. Devices known as affected so far are: PI7C9X110, PI7C9X111SL, PI7C9X130. Add a new flag, clear_retrain_link, in struct pci_dev. Quirks for affected devices set this bit. Note that pcie_retrain_link() lives in aspm.c because that's currently the only place we use it, but this erratum is not specific to ASPM, and we may retrain links for other reasons in the future. Signed-off-by: Stefan Mätje [bhelgaas: apply regardless of CONFIG_PCIEASPM] Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko CC: stable@vger.kernel.org --- drivers/pci/pcie/aspm.c | 9 +++++++++ drivers/pci/quirks.c | 17 +++++++++++++++++ include/linux/pci.h | 2 ++ 3 files changed, 28 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 4fa37622ad66..38e7017478b5 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -205,6 +205,15 @@ static bool pcie_retrain_link(struct pcie_link_state *link) pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); reg16 |= PCI_EXP_LNKCTL_RL; pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); + if (parent->clear_retrain_link) { + /* + * Due to an erratum in some devices the Retrain Link bit + * needs to be cleared again manually to allow the link + * training to succeed. + */ + reg16 &= ~PCI_EXP_LNKCTL_RL; + pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); + } /* Wait for link training end. Break out after waiting for timeout */ start_jiffies = jiffies; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index a59ad09ce911..6f34e17c98f4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2245,6 +2245,23 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10f1, quirk_disable_aspm_l0s); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s); +/* + * Some Pericom PCIe-to-PCI bridges in reverse mode need the PCIe Retrain + * Link bit cleared after starting the link retrain process to allow this + * process to finish. + * + * Affected devices: PI7C9X110, PI7C9X111SL, PI7C9X130. See also the + * Pericom Errata Sheet PI7C9X111SLB_errata_rev1.2_102711.pdf. + */ +static void quirk_enable_clear_retrain_link(struct pci_dev *dev) +{ + dev->clear_retrain_link = 1; + pci_info(dev, "Enable PCIe Retrain Link quirk\n"); +} +DECLARE_PCI_FIXUP_HEADER(0x12d8, 0xe110, quirk_enable_clear_retrain_link); +DECLARE_PCI_FIXUP_HEADER(0x12d8, 0xe111, quirk_enable_clear_retrain_link); +DECLARE_PCI_FIXUP_HEADER(0x12d8, 0xe130, quirk_enable_clear_retrain_link); + static void fixup_rev1_53c810(struct pci_dev *dev) { u32 class = dev->class; diff --git a/include/linux/pci.h b/include/linux/pci.h index 77448215ef5b..2c056a7a728a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -348,6 +348,8 @@ struct pci_dev { unsigned int hotplug_user_indicators:1; /* SlotCtl indicators controlled exclusively by user sysfs */ + unsigned int clear_retrain_link:1; /* Need to clear Retrain Link + bit manually */ unsigned int d3_delay; /* D3->D0 transition time in ms */ unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ -- cgit v1.2.3 From 12fa4f47924c9e0638869c2315dda29d50a94446 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 1 Sep 2017 18:14:04 -0500 Subject: CPER: Add UEFI spec references Add UEFI spec references for CPER UUIDs and structures, fix a few typos, and remove some useless comments. No functional change intended. Link: http://www.uefi.org/specifications Signed-off-by: Bjorn Helgaas --- include/linux/cper.h | 54 +++++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cper.h b/include/linux/cper.h index 9c703a0abe6e..2f361a96dc76 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -44,7 +44,7 @@ */ #define CPER_REC_LEN 256 /* - * Severity difinition for error_severity in struct cper_record_header + * Severity definition for error_severity in struct cper_record_header * and section_severity in struct cper_section_descriptor */ enum { @@ -55,24 +55,21 @@ enum { }; /* - * Validation bits difinition for validation_bits in struct + * Validation bits definition for validation_bits in struct * cper_record_header. If set, corresponding fields in struct * cper_record_header contain valid information. - * - * corresponds platform_id */ #define CPER_VALID_PLATFORM_ID 0x0001 -/* corresponds timestamp */ #define CPER_VALID_TIMESTAMP 0x0002 -/* corresponds partition_id */ #define CPER_VALID_PARTITION_ID 0x0004 /* * Notification type used to generate error record, used in - * notification_type in struct cper_record_header - * - * Corrected Machine Check + * notification_type in struct cper_record_header. These UUIDs are defined + * in the UEFI spec v2.7, sec N.2.1. */ + +/* Corrected Machine Check */ #define CPER_NOTIFY_CMC \ GUID_INIT(0x2DCE8BB1, 0xBDD7, 0x450e, 0xB9, 0xAD, 0x9C, 0xF4, \ 0xEB, 0xD4, 0xF8, 0x90) @@ -122,14 +119,11 @@ enum { #define CPER_SEC_REV 0x0100 /* - * Validation bits difinition for validation_bits in struct + * Validation bits definition for validation_bits in struct * cper_section_descriptor. If set, corresponding fields in struct * cper_section_descriptor contain valid information. - * - * corresponds fru_id */ #define CPER_SEC_VALID_FRU_ID 0x1 -/* corresponds fru_text */ #define CPER_SEC_VALID_FRU_TEXT 0x2 /* @@ -165,10 +159,11 @@ enum { /* * Section type definitions, used in section_type field in struct - * cper_section_descriptor - * - * Processor Generic + * cper_section_descriptor. These UUIDs are defined in the UEFI spec + * v2.7, sec N.2.2. */ + +/* Processor Generic */ #define CPER_SEC_PROC_GENERIC \ GUID_INIT(0x9876CCAD, 0x47B4, 0x4bdb, 0xB6, 0x5E, 0x16, 0xF1, \ 0x93, 0xC4, 0xF3, 0xDB) @@ -325,6 +320,7 @@ enum { */ #pragma pack(1) +/* Record Header, UEFI v2.7 sec N.2.1 */ struct cper_record_header { char signature[CPER_SIG_SIZE]; /* must be CPER_SIG_RECORD */ __u16 revision; /* must be CPER_RECORD_REV */ @@ -344,6 +340,7 @@ struct cper_record_header { __u8 reserved[12]; /* must be zero */ }; +/* Section Descriptor, UEFI v2.7 sec N.2.2 */ struct cper_section_descriptor { __u32 section_offset; /* Offset in bytes of the * section body from the base @@ -359,7 +356,7 @@ struct cper_section_descriptor { __u8 fru_text[20]; }; -/* Generic Processor Error Section */ +/* Generic Processor Error Section, UEFI v2.7 sec N.2.4.1 */ struct cper_sec_proc_generic { __u64 validation_bits; __u8 proc_type; @@ -378,14 +375,14 @@ struct cper_sec_proc_generic { __u64 ip; }; -/* IA32/X64 Processor Error Section */ +/* IA32/X64 Processor Error Section, UEFI v2.7 sec N.2.4.2 */ struct cper_sec_proc_ia { __u64 validation_bits; __u64 lapic_id; __u8 cpuid[48]; }; -/* IA32/X64 Processor Error Information Structure */ +/* IA32/X64 Processor Error Information Structure, UEFI v2.7 sec N.2.4.2.1 */ struct cper_ia_err_info { guid_t err_type; __u64 validation_bits; @@ -396,7 +393,7 @@ struct cper_ia_err_info { __u64 ip; }; -/* IA32/X64 Processor Context Information Structure */ +/* IA32/X64 Processor Context Information Structure, UEFI v2.7 sec N.2.4.2.2 */ struct cper_ia_proc_ctx { __u16 reg_ctx_type; __u16 reg_arr_size; @@ -404,7 +401,7 @@ struct cper_ia_proc_ctx { __u64 mm_reg_addr; }; -/* ARM Processor Error Section */ +/* ARM Processor Error Section, UEFI v2.7 sec N.2.4.4 */ struct cper_sec_proc_arm { __u32 validation_bits; __u16 err_info_num; /* Number of Processor Error Info */ @@ -418,7 +415,7 @@ struct cper_sec_proc_arm { __u32 psci_state; }; -/* ARM Processor Error Information Structure */ +/* ARM Processor Error Information Structure, UEFI v2.7 sec N.2.4.4.1 */ struct cper_arm_err_info { __u8 version; __u8 length; @@ -431,14 +428,14 @@ struct cper_arm_err_info { __u64 physical_fault_addr; }; -/* ARM Processor Context Information Structure */ +/* ARM Processor Context Information Structure, UEFI v2.7 sec N.2.4.4.2 */ struct cper_arm_ctx_info { __u16 version; __u16 type; __u32 size; }; -/* Old Memory Error Section UEFI 2.1, 2.2 */ +/* Old Memory Error Section, UEFI v2.1, v2.2 */ struct cper_sec_mem_err_old { __u64 validation_bits; __u64 error_status; @@ -458,7 +455,7 @@ struct cper_sec_mem_err_old { __u8 error_type; }; -/* Memory Error Section UEFI >= 2.3 */ +/* Memory Error Section (UEFI >= v2.3), UEFI v2.7 sec N.2.5 */ struct cper_sec_mem_err { __u64 validation_bits; __u64 error_status; @@ -478,8 +475,8 @@ struct cper_sec_mem_err { __u8 error_type; __u8 reserved; __u16 rank; - __u16 mem_array_handle; /* card handle in UEFI 2.4 */ - __u16 mem_dev_handle; /* module handle in UEFI 2.4 */ + __u16 mem_array_handle; /* "card handle" in UEFI 2.4 */ + __u16 mem_dev_handle; /* "module handle" in UEFI 2.4 */ }; struct cper_mem_err_compact { @@ -500,6 +497,7 @@ struct cper_mem_err_compact { __u16 mem_dev_handle; }; +/* PCI Express Error Section, UEFI v2.7 sec N.2.7 */ struct cper_sec_pcie { __u64 validation_bits; __u32 port_type; @@ -538,7 +536,7 @@ struct cper_sec_pcie { /* Reset to default packing */ #pragma pack() -extern const char * const cper_proc_error_type_strs[4]; +extern const char *const cper_proc_error_type_strs[4]; u64 cper_next_record_id(void); const char *cper_severity_str(unsigned int); -- cgit v1.2.3 From 113fb03ed1d4c1877cc8817e500867616b222380 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 22 Mar 2019 13:06:40 -0500 Subject: CPER: Remove unnecessary use of user-space types "__u32" and similar types are intended for things exported to user-space, including structs used in ioctls; see include/uapi/asm-generic/int-l64.h. They are not needed for the CPER struct definitions, which not exported to user-space and not used in ioctls. Replace them with the typical "u32" and similar types. No functional change intended. The reason for changing this is to remove the question of "why do we use __u32 here instead of u32?" We should use __u32 when there's a reason for it; otherwise, we should prefer u32 for consistency. Reference: Documentation/process/coding-style.rst Signed-off-by: Bjorn Helgaas CC: Masahiro Yamada CC: Greg Kroah-Hartman CC: Andrew Morton --- include/linux/cper.h | 286 +++++++++++++++++++++++++-------------------------- 1 file changed, 143 insertions(+), 143 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cper.h b/include/linux/cper.h index 2f361a96dc76..cc4980bb0f65 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -323,214 +323,214 @@ enum { /* Record Header, UEFI v2.7 sec N.2.1 */ struct cper_record_header { char signature[CPER_SIG_SIZE]; /* must be CPER_SIG_RECORD */ - __u16 revision; /* must be CPER_RECORD_REV */ - __u32 signature_end; /* must be CPER_SIG_END */ - __u16 section_count; - __u32 error_severity; - __u32 validation_bits; - __u32 record_length; - __u64 timestamp; + u16 revision; /* must be CPER_RECORD_REV */ + u32 signature_end; /* must be CPER_SIG_END */ + u16 section_count; + u32 error_severity; + u32 validation_bits; + u32 record_length; + u64 timestamp; guid_t platform_id; guid_t partition_id; guid_t creator_id; guid_t notification_type; - __u64 record_id; - __u32 flags; - __u64 persistence_information; - __u8 reserved[12]; /* must be zero */ + u64 record_id; + u32 flags; + u64 persistence_information; + u8 reserved[12]; /* must be zero */ }; /* Section Descriptor, UEFI v2.7 sec N.2.2 */ struct cper_section_descriptor { - __u32 section_offset; /* Offset in bytes of the + u32 section_offset; /* Offset in bytes of the * section body from the base * of the record header */ - __u32 section_length; - __u16 revision; /* must be CPER_RECORD_REV */ - __u8 validation_bits; - __u8 reserved; /* must be zero */ - __u32 flags; + u32 section_length; + u16 revision; /* must be CPER_RECORD_REV */ + u8 validation_bits; + u8 reserved; /* must be zero */ + u32 flags; guid_t section_type; guid_t fru_id; - __u32 section_severity; - __u8 fru_text[20]; + u32 section_severity; + u8 fru_text[20]; }; /* Generic Processor Error Section, UEFI v2.7 sec N.2.4.1 */ struct cper_sec_proc_generic { - __u64 validation_bits; - __u8 proc_type; - __u8 proc_isa; - __u8 proc_error_type; - __u8 operation; - __u8 flags; - __u8 level; - __u16 reserved; - __u64 cpu_version; + u64 validation_bits; + u8 proc_type; + u8 proc_isa; + u8 proc_error_type; + u8 operation; + u8 flags; + u8 level; + u16 reserved; + u64 cpu_version; char cpu_brand[128]; - __u64 proc_id; - __u64 target_addr; - __u64 requestor_id; - __u64 responder_id; - __u64 ip; + u64 proc_id; + u64 target_addr; + u64 requestor_id; + u64 responder_id; + u64 ip; }; /* IA32/X64 Processor Error Section, UEFI v2.7 sec N.2.4.2 */ struct cper_sec_proc_ia { - __u64 validation_bits; - __u64 lapic_id; - __u8 cpuid[48]; + u64 validation_bits; + u64 lapic_id; + u8 cpuid[48]; }; /* IA32/X64 Processor Error Information Structure, UEFI v2.7 sec N.2.4.2.1 */ struct cper_ia_err_info { guid_t err_type; - __u64 validation_bits; - __u64 check_info; - __u64 target_id; - __u64 requestor_id; - __u64 responder_id; - __u64 ip; + u64 validation_bits; + u64 check_info; + u64 target_id; + u64 requestor_id; + u64 responder_id; + u64 ip; }; /* IA32/X64 Processor Context Information Structure, UEFI v2.7 sec N.2.4.2.2 */ struct cper_ia_proc_ctx { - __u16 reg_ctx_type; - __u16 reg_arr_size; - __u32 msr_addr; - __u64 mm_reg_addr; + u16 reg_ctx_type; + u16 reg_arr_size; + u32 msr_addr; + u64 mm_reg_addr; }; /* ARM Processor Error Section, UEFI v2.7 sec N.2.4.4 */ struct cper_sec_proc_arm { - __u32 validation_bits; - __u16 err_info_num; /* Number of Processor Error Info */ - __u16 context_info_num; /* Number of Processor Context Info Records*/ - __u32 section_length; - __u8 affinity_level; - __u8 reserved[3]; /* must be zero */ - __u64 mpidr; - __u64 midr; - __u32 running_state; /* Bit 0 set - Processor running. PSCI = 0 */ - __u32 psci_state; + u32 validation_bits; + u16 err_info_num; /* Number of Processor Error Info */ + u16 context_info_num; /* Number of Processor Context Info Records*/ + u32 section_length; + u8 affinity_level; + u8 reserved[3]; /* must be zero */ + u64 mpidr; + u64 midr; + u32 running_state; /* Bit 0 set - Processor running. PSCI = 0 */ + u32 psci_state; }; /* ARM Processor Error Information Structure, UEFI v2.7 sec N.2.4.4.1 */ struct cper_arm_err_info { - __u8 version; - __u8 length; - __u16 validation_bits; - __u8 type; - __u16 multiple_error; - __u8 flags; - __u64 error_info; - __u64 virt_fault_addr; - __u64 physical_fault_addr; + u8 version; + u8 length; + u16 validation_bits; + u8 type; + u16 multiple_error; + u8 flags; + u64 error_info; + u64 virt_fault_addr; + u64 physical_fault_addr; }; /* ARM Processor Context Information Structure, UEFI v2.7 sec N.2.4.4.2 */ struct cper_arm_ctx_info { - __u16 version; - __u16 type; - __u32 size; + u16 version; + u16 type; + u32 size; }; /* Old Memory Error Section, UEFI v2.1, v2.2 */ struct cper_sec_mem_err_old { - __u64 validation_bits; - __u64 error_status; - __u64 physical_addr; - __u64 physical_addr_mask; - __u16 node; - __u16 card; - __u16 module; - __u16 bank; - __u16 device; - __u16 row; - __u16 column; - __u16 bit_pos; - __u64 requestor_id; - __u64 responder_id; - __u64 target_id; - __u8 error_type; + u64 validation_bits; + u64 error_status; + u64 physical_addr; + u64 physical_addr_mask; + u16 node; + u16 card; + u16 module; + u16 bank; + u16 device; + u16 row; + u16 column; + u16 bit_pos; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u8 error_type; }; /* Memory Error Section (UEFI >= v2.3), UEFI v2.7 sec N.2.5 */ struct cper_sec_mem_err { - __u64 validation_bits; - __u64 error_status; - __u64 physical_addr; - __u64 physical_addr_mask; - __u16 node; - __u16 card; - __u16 module; - __u16 bank; - __u16 device; - __u16 row; - __u16 column; - __u16 bit_pos; - __u64 requestor_id; - __u64 responder_id; - __u64 target_id; - __u8 error_type; - __u8 reserved; - __u16 rank; - __u16 mem_array_handle; /* "card handle" in UEFI 2.4 */ - __u16 mem_dev_handle; /* "module handle" in UEFI 2.4 */ + u64 validation_bits; + u64 error_status; + u64 physical_addr; + u64 physical_addr_mask; + u16 node; + u16 card; + u16 module; + u16 bank; + u16 device; + u16 row; + u16 column; + u16 bit_pos; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u8 error_type; + u8 reserved; + u16 rank; + u16 mem_array_handle; /* "card handle" in UEFI 2.4 */ + u16 mem_dev_handle; /* "module handle" in UEFI 2.4 */ }; struct cper_mem_err_compact { - __u64 validation_bits; - __u16 node; - __u16 card; - __u16 module; - __u16 bank; - __u16 device; - __u16 row; - __u16 column; - __u16 bit_pos; - __u64 requestor_id; - __u64 responder_id; - __u64 target_id; - __u16 rank; - __u16 mem_array_handle; - __u16 mem_dev_handle; + u64 validation_bits; + u16 node; + u16 card; + u16 module; + u16 bank; + u16 device; + u16 row; + u16 column; + u16 bit_pos; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u16 rank; + u16 mem_array_handle; + u16 mem_dev_handle; }; /* PCI Express Error Section, UEFI v2.7 sec N.2.7 */ struct cper_sec_pcie { - __u64 validation_bits; - __u32 port_type; + u64 validation_bits; + u32 port_type; struct { - __u8 minor; - __u8 major; - __u8 reserved[2]; + u8 minor; + u8 major; + u8 reserved[2]; } version; - __u16 command; - __u16 status; - __u32 reserved; + u16 command; + u16 status; + u32 reserved; struct { - __u16 vendor_id; - __u16 device_id; - __u8 class_code[3]; - __u8 function; - __u8 device; - __u16 segment; - __u8 bus; - __u8 secondary_bus; - __u16 slot; - __u8 reserved; + u16 vendor_id; + u16 device_id; + u8 class_code[3]; + u8 function; + u8 device; + u16 segment; + u8 bus; + u8 secondary_bus; + u16 slot; + u8 reserved; } device_id; struct { - __u32 lower; - __u32 upper; + u32 lower; + u32 upper; } serial_number; struct { - __u16 secondary_status; - __u16 control; + u16 secondary_status; + u16 control; } bridge; - __u8 capability[60]; - __u8 aer_info[96]; + u8 capability[60]; + u8 aer_info[96]; }; /* Reset to default packing */ -- cgit v1.2.3 From 2a9a801620efac92885fc9cd53594c0b9aba87a4 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 25 Mar 2019 15:09:39 +0530 Subject: PCI: endpoint: Add support to specify alignment for buffers allocated to BARs The address that is allocated using pci_epf_alloc_space() is directly written to the target address of the Inbound Address Translation unit (ie the HW component implementing inbound address decoding) on endpoint controllers. Designware IP [1] has a configuration parameter (CX_ATU_MIN_REGION_SIZE [2]) which has 64KB as default value and the lower 16 bits of the Base, Limit and Target registers of the Inbound ATU are fixed to zero. If the programmed memory address is not aligned to 64 KB boundary this causes memory corruption. Modify pci_epf_alloc_space() API to take alignment size as argument in order to allocate buffers to be mapped to BARs with an alignment that suits the platform where they are used. Add an 'align' parameter to epc_features which can be used by platform drivers to specify the BAR allocation alignment requirements and use this while invoking pci_epf_alloc_space(). [1] "I/O and MEM Match Modes" section in DesignWare Cores PCI Express Controller Databook version 4.90a [2] http://www.ti.com/lit/ug/spruid7c/spruid7c.pdf Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/functions/pci-epf-test.c | 5 +++-- drivers/pci/endpoint/pci-epf-core.c | 10 ++++++++-- include/linux/pci-epc.h | 2 ++ include/linux/pci-epf.h | 3 ++- 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index d0b91da49bf4..c0786ca74312 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -438,7 +438,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) epc_features = epf_test->epc_features; base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg), - test_reg_bar); + test_reg_bar, epc_features->align); if (!base) { dev_err(dev, "Failed to allocated register space\n"); return -ENOMEM; @@ -453,7 +453,8 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) if (!!(epc_features->reserved_bar & (1 << bar))) continue; - base = pci_epf_alloc_space(epf, bar_size[bar], bar); + base = pci_epf_alloc_space(epf, bar_size[bar], bar, + epc_features->align); if (!base) dev_err(dev, "Failed to allocate space for BAR%d\n", bar); diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 8bfdcd291196..fb1306de8f40 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -109,10 +109,12 @@ EXPORT_SYMBOL_GPL(pci_epf_free_space); * pci_epf_alloc_space() - allocate memory for the PCI EPF register space * @size: the size of the memory that has to be allocated * @bar: the BAR number corresponding to the allocated register space + * @align: alignment size for the allocation region * * Invoke to allocate memory for the PCI EPF register space. */ -void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar) +void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, + size_t align) { void *space; struct device *dev = epf->epc->dev.parent; @@ -120,7 +122,11 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar) if (size < 128) size = 128; - size = roundup_pow_of_two(size); + + if (align) + size = ALIGN(size, align); + else + size = roundup_pow_of_two(size); space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL); if (!space) { diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index c3ffa3917f88..f641badc2c61 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -109,6 +109,7 @@ struct pci_epc { * @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver * @bar_fixed_64bit: bitmap to indicate fixed 64bit BARs * @bar_fixed_size: Array specifying the size supported by each BAR + * @align: alignment size required for BAR buffer allocation */ struct pci_epc_features { unsigned int linkup_notifier : 1; @@ -117,6 +118,7 @@ struct pci_epc_features { u8 reserved_bar; u8 bar_fixed_64bit; u64 bar_fixed_size[BAR_5 + 1]; + size_t align; }; #define to_pci_epc(device) container_of((device), struct pci_epc, dev) diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index ec02f58758c8..2d6f07556682 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -149,7 +149,8 @@ void pci_epf_destroy(struct pci_epf *epf); int __pci_epf_register_driver(struct pci_epf_driver *driver, struct module *owner); void pci_epf_unregister_driver(struct pci_epf_driver *driver); -void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar); +void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, + size_t align); void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar); int pci_epf_bind(struct pci_epf *epf); void pci_epf_unbind(struct pci_epf *epf); -- cgit v1.2.3 From 804ee5be633f051f85270ae3f4770a5fc6cd92d5 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Wed, 17 Apr 2019 10:23:56 +0200 Subject: PCI: Remove unused pci_request_region_exclusive() pci_request_region_exclusive() was introduced with commit e8de1481fd71 ("resource: allow MMIO exclusivity for device drivers") in 2.6.29 which was released 2008. It never had an in tree user since then, so after 11 years later let's remove it. Signed-off-by: Johannes Thumshirn Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 25 ------------------------- include/linux/pci.h | 1 - 2 files changed, 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7c1b362f599a..d185b49e105a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3706,31 +3706,6 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) } EXPORT_SYMBOL(pci_request_region); -/** - * pci_request_region_exclusive - Reserved PCI I/O and memory resource - * @pdev: PCI device whose resources are to be reserved - * @bar: BAR to be reserved - * @res_name: Name to be associated with resource. - * - * Mark the PCI region associated with PCI device @pdev BR @bar as - * being reserved by owner @res_name. Do not access any - * address inside the PCI regions unless this call returns - * successfully. - * - * Returns 0 on success, or %EBUSY on error. A warning - * message is also printed on failure. - * - * The key difference that _exclusive makes it that userspace is - * explicitly not allowed to map the resource via /dev/mem or - * sysfs. - */ -int pci_request_region_exclusive(struct pci_dev *pdev, int bar, - const char *res_name) -{ - return __pci_request_region(pdev, bar, res_name, IORESOURCE_EXCLUSIVE); -} -EXPORT_SYMBOL(pci_request_region_exclusive); - /** * pci_release_selected_regions - Release selected PCI I/O and memory resources * @pdev: PCI device whose resources were previously reserved diff --git a/include/linux/pci.h b/include/linux/pci.h index 77448215ef5b..5f32275358cf 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1233,7 +1233,6 @@ int __must_check pci_request_regions(struct pci_dev *, const char *); int __must_check pci_request_regions_exclusive(struct pci_dev *, const char *); void pci_release_regions(struct pci_dev *); int __must_check pci_request_region(struct pci_dev *, int, const char *); -int __must_check pci_request_region_exclusive(struct pci_dev *, int, const char *); void pci_release_region(struct pci_dev *, int); int pci_request_selected_regions(struct pci_dev *, int, const char *); int pci_request_selected_regions_exclusive(struct pci_dev *, int, const char *); -- cgit v1.2.3 From ba8a39820d4614b2b04ac74a48d1f8de677de750 Mon Sep 17 00:00:00 2001 From: Wesley Sheng Date: Mon, 15 Apr 2019 22:41:41 +0800 Subject: switchtec: Increase PFF limit from 48 to 255 The Switchtec devices supports two PCIe Function Frameworks (PFFs) per upstream port (one for the port itself and one for the management endoint), and each PFF may have up to 255 ports. Previously the driver only supported 48 of those ports, and the SWITCHTEC_IOCTL_EVENT_SUMMARY ioctl only returned information about those 48. Increase SWITCHTEC_MAX_PFF_CSR from 48 to 255 so the driver supports all 255 possible ports. Rename SWITCHTEC_IOCTL_EVENT_SUMMARY and associated struct switchtec_ioctl_event_summary to SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY and switchtec_ioctl_event_summary_legacy with so existing applications work unchanged, supporting up to 48 ports. Add replacement SWITCHTEC_IOCTL_EVENT_SUMMARY and struct switchtec_ioctl_event_summary that new and recompiled applications support up to 255 ports. Signed-off-by: Wesley Sheng [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Reviewed-by: Logan Gunthorpe --- drivers/pci/switch/switchtec.c | 39 +++++++++++++++++++++++++----------- include/linux/switchtec.h | 2 +- include/uapi/linux/switchtec_ioctl.h | 13 +++++++++++- 3 files changed, 40 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index e22766c79fe9..7df9a69f7d93 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -658,19 +658,25 @@ static int ioctl_flash_part_info(struct switchtec_dev *stdev, static int ioctl_event_summary(struct switchtec_dev *stdev, struct switchtec_user *stuser, - struct switchtec_ioctl_event_summary __user *usum) + struct switchtec_ioctl_event_summary __user *usum, + size_t size) { - struct switchtec_ioctl_event_summary s = {0}; + struct switchtec_ioctl_event_summary *s; int i; u32 reg; + int ret = 0; - s.global = ioread32(&stdev->mmio_sw_event->global_summary); - s.part_bitmap = ioread32(&stdev->mmio_sw_event->part_event_bitmap); - s.local_part = ioread32(&stdev->mmio_part_cfg->part_event_summary); + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->global = ioread32(&stdev->mmio_sw_event->global_summary); + s->part_bitmap = ioread32(&stdev->mmio_sw_event->part_event_bitmap); + s->local_part = ioread32(&stdev->mmio_part_cfg->part_event_summary); for (i = 0; i < stdev->partition_count; i++) { reg = ioread32(&stdev->mmio_part_cfg_all[i].part_event_summary); - s.part[i] = reg; + s->part[i] = reg; } for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) { @@ -679,15 +685,19 @@ static int ioctl_event_summary(struct switchtec_dev *stdev, break; reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary); - s.pff[i] = reg; + s->pff[i] = reg; } - if (copy_to_user(usum, &s, sizeof(s))) - return -EFAULT; + if (copy_to_user(usum, s, size)) { + ret = -EFAULT; + goto error_case; + } stuser->event_cnt = atomic_read(&stdev->event_cnt); - return 0; +error_case: + kfree(s); + return ret; } static u32 __iomem *global_ev_reg(struct switchtec_dev *stdev, @@ -977,8 +987,9 @@ static long switchtec_dev_ioctl(struct file *filp, unsigned int cmd, case SWITCHTEC_IOCTL_FLASH_PART_INFO: rc = ioctl_flash_part_info(stdev, argp); break; - case SWITCHTEC_IOCTL_EVENT_SUMMARY: - rc = ioctl_event_summary(stdev, stuser, argp); + case SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY: + rc = ioctl_event_summary(stdev, stuser, argp, + sizeof(struct switchtec_ioctl_event_summary_legacy)); break; case SWITCHTEC_IOCTL_EVENT_CTL: rc = ioctl_event_ctl(stdev, argp); @@ -989,6 +1000,10 @@ static long switchtec_dev_ioctl(struct file *filp, unsigned int cmd, case SWITCHTEC_IOCTL_PORT_TO_PFF: rc = ioctl_port_to_pff(stdev, argp); break; + case SWITCHTEC_IOCTL_EVENT_SUMMARY: + rc = ioctl_event_summary(stdev, stuser, argp, + sizeof(struct switchtec_ioctl_event_summary)); + break; default: rc = -ENOTTY; break; diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index 52a079b3a9a6..0cfc34ac37fb 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -20,7 +20,7 @@ #include #define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024 -#define SWITCHTEC_MAX_PFF_CSR 48 +#define SWITCHTEC_MAX_PFF_CSR 255 #define SWITCHTEC_EVENT_OCCURRED BIT(0) #define SWITCHTEC_EVENT_CLEAR BIT(0) diff --git a/include/uapi/linux/switchtec_ioctl.h b/include/uapi/linux/switchtec_ioctl.h index 4f4daf8db954..c912b5a678e4 100644 --- a/include/uapi/linux/switchtec_ioctl.h +++ b/include/uapi/linux/switchtec_ioctl.h @@ -50,7 +50,7 @@ struct switchtec_ioctl_flash_part_info { __u32 active; }; -struct switchtec_ioctl_event_summary { +struct switchtec_ioctl_event_summary_legacy { __u64 global; __u64 part_bitmap; __u32 local_part; @@ -59,6 +59,15 @@ struct switchtec_ioctl_event_summary { __u32 pff[48]; }; +struct switchtec_ioctl_event_summary { + __u64 global; + __u64 part_bitmap; + __u32 local_part; + __u32 padding; + __u32 part[48]; + __u32 pff[255]; +}; + #define SWITCHTEC_IOCTL_EVENT_STACK_ERROR 0 #define SWITCHTEC_IOCTL_EVENT_PPU_ERROR 1 #define SWITCHTEC_IOCTL_EVENT_ISP_ERROR 2 @@ -127,6 +136,8 @@ struct switchtec_ioctl_pff_port { _IOWR('W', 0x41, struct switchtec_ioctl_flash_part_info) #define SWITCHTEC_IOCTL_EVENT_SUMMARY \ _IOR('W', 0x42, struct switchtec_ioctl_event_summary) +#define SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY \ + _IOR('W', 0x42, struct switchtec_ioctl_event_summary_legacy) #define SWITCHTEC_IOCTL_EVENT_CTL \ _IOWR('W', 0x43, struct switchtec_ioctl_event_ctl) #define SWITCHTEC_IOCTL_PFF_TO_PORT \ -- cgit v1.2.3 From 87fcf12e846a5028c14d21a94a0712fd1ad5bad0 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Apr 2019 14:27:36 -0500 Subject: PCI/ACPI: Remove the need for 'struct hotplug_params' We used to first parse all the _HPP and _HPX tables before using the information to program registers of PCIe devices. Up through HPX Type 2, there was only one structure of each type, so we could cheat and store it on the stack. With HPX Type 3 we get an arbitrary number of entries, so the above model doesn't scale that well. Instead of parsing all tables at once, parse and program each entry separately. For _HPP and _HPX Types 0 through 2, this is functionally equivalent. The change enables the upcoming _HPX Type 3 to integrate more easily. Link: https://lore.kernel.org/lkml/20190208162414.3996-3-mr.nuke.me@gmail.com Signed-off-by: Alexandru Gagniuc [bhelgaas: fix build errors] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 108 ++++++++++++++++++++++++-------------------- drivers/pci/probe.c | 16 +++---- include/linux/pci_hotplug.h | 18 ++++---- 3 files changed, 72 insertions(+), 70 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index b25e5fa9d1c9..95f4f86d2f34 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -119,7 +119,7 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) } static acpi_status decode_type0_hpx_record(union acpi_object *record, - struct hotplug_params *hpx) + struct hpp_type0 *hpx0) { int i; union acpi_object *fields = record->package.elements; @@ -132,12 +132,11 @@ static acpi_status decode_type0_hpx_record(union acpi_object *record, for (i = 2; i < 6; i++) if (fields[i].type != ACPI_TYPE_INTEGER) return AE_ERROR; - hpx->t0 = &hpx->type0_data; - hpx->t0->revision = revision; - hpx->t0->cache_line_size = fields[2].integer.value; - hpx->t0->latency_timer = fields[3].integer.value; - hpx->t0->enable_serr = fields[4].integer.value; - hpx->t0->enable_perr = fields[5].integer.value; + hpx0->revision = revision; + hpx0->cache_line_size = fields[2].integer.value; + hpx0->latency_timer = fields[3].integer.value; + hpx0->enable_serr = fields[4].integer.value; + hpx0->enable_perr = fields[5].integer.value; break; default: printk(KERN_WARNING @@ -149,7 +148,7 @@ static acpi_status decode_type0_hpx_record(union acpi_object *record, } static acpi_status decode_type1_hpx_record(union acpi_object *record, - struct hotplug_params *hpx) + struct hpp_type1 *hpx1) { int i; union acpi_object *fields = record->package.elements; @@ -162,11 +161,10 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record, for (i = 2; i < 5; i++) if (fields[i].type != ACPI_TYPE_INTEGER) return AE_ERROR; - hpx->t1 = &hpx->type1_data; - hpx->t1->revision = revision; - hpx->t1->max_mem_read = fields[2].integer.value; - hpx->t1->avg_max_split = fields[3].integer.value; - hpx->t1->tot_max_split = fields[4].integer.value; + hpx1->revision = revision; + hpx1->max_mem_read = fields[2].integer.value; + hpx1->avg_max_split = fields[3].integer.value; + hpx1->tot_max_split = fields[4].integer.value; break; default: printk(KERN_WARNING @@ -178,7 +176,7 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record, } static acpi_status decode_type2_hpx_record(union acpi_object *record, - struct hotplug_params *hpx) + struct hpp_type2 *hpx2) { int i; union acpi_object *fields = record->package.elements; @@ -191,24 +189,23 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record, for (i = 2; i < 18; i++) if (fields[i].type != ACPI_TYPE_INTEGER) return AE_ERROR; - hpx->t2 = &hpx->type2_data; - hpx->t2->revision = revision; - hpx->t2->unc_err_mask_and = fields[2].integer.value; - hpx->t2->unc_err_mask_or = fields[3].integer.value; - hpx->t2->unc_err_sever_and = fields[4].integer.value; - hpx->t2->unc_err_sever_or = fields[5].integer.value; - hpx->t2->cor_err_mask_and = fields[6].integer.value; - hpx->t2->cor_err_mask_or = fields[7].integer.value; - hpx->t2->adv_err_cap_and = fields[8].integer.value; - hpx->t2->adv_err_cap_or = fields[9].integer.value; - hpx->t2->pci_exp_devctl_and = fields[10].integer.value; - hpx->t2->pci_exp_devctl_or = fields[11].integer.value; - hpx->t2->pci_exp_lnkctl_and = fields[12].integer.value; - hpx->t2->pci_exp_lnkctl_or = fields[13].integer.value; - hpx->t2->sec_unc_err_sever_and = fields[14].integer.value; - hpx->t2->sec_unc_err_sever_or = fields[15].integer.value; - hpx->t2->sec_unc_err_mask_and = fields[16].integer.value; - hpx->t2->sec_unc_err_mask_or = fields[17].integer.value; + hpx2->revision = revision; + hpx2->unc_err_mask_and = fields[2].integer.value; + hpx2->unc_err_mask_or = fields[3].integer.value; + hpx2->unc_err_sever_and = fields[4].integer.value; + hpx2->unc_err_sever_or = fields[5].integer.value; + hpx2->cor_err_mask_and = fields[6].integer.value; + hpx2->cor_err_mask_or = fields[7].integer.value; + hpx2->adv_err_cap_and = fields[8].integer.value; + hpx2->adv_err_cap_or = fields[9].integer.value; + hpx2->pci_exp_devctl_and = fields[10].integer.value; + hpx2->pci_exp_devctl_or = fields[11].integer.value; + hpx2->pci_exp_lnkctl_and = fields[12].integer.value; + hpx2->pci_exp_lnkctl_or = fields[13].integer.value; + hpx2->sec_unc_err_sever_and = fields[14].integer.value; + hpx2->sec_unc_err_sever_or = fields[15].integer.value; + hpx2->sec_unc_err_mask_and = fields[16].integer.value; + hpx2->sec_unc_err_mask_or = fields[17].integer.value; break; default: printk(KERN_WARNING @@ -219,17 +216,18 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record, return AE_OK; } -static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) +static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle, + const struct hotplug_program_ops *hp_ops) { acpi_status status; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *package, *record, *fields; + struct hpp_type0 hpx0; + struct hpp_type1 hpx1; + struct hpp_type2 hpx2; u32 type; int i; - /* Clear the return buffer with zeros */ - memset(hpx, 0, sizeof(struct hotplug_params)); - status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer); if (ACPI_FAILURE(status)) return status; @@ -257,19 +255,25 @@ static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) type = fields[0].integer.value; switch (type) { case 0: - status = decode_type0_hpx_record(record, hpx); + memset(&hpx0, 0, sizeof(hpx0)); + status = decode_type0_hpx_record(record, &hpx0); if (ACPI_FAILURE(status)) goto exit; + hp_ops->program_type0(dev, &hpx0); break; case 1: - status = decode_type1_hpx_record(record, hpx); + memset(&hpx1, 0, sizeof(hpx1)); + status = decode_type1_hpx_record(record, &hpx1); if (ACPI_FAILURE(status)) goto exit; + hp_ops->program_type1(dev, &hpx1); break; case 2: - status = decode_type2_hpx_record(record, hpx); + memset(&hpx2, 0, sizeof(hpx2)); + status = decode_type2_hpx_record(record, &hpx2); if (ACPI_FAILURE(status)) goto exit; + hp_ops->program_type2(dev, &hpx2); break; default: printk(KERN_ERR "%s: Type %d record not supported\n", @@ -283,14 +287,16 @@ static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) return status; } -static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) +static acpi_status acpi_run_hpp(struct pci_dev *dev, acpi_handle handle, + const struct hotplug_program_ops *hp_ops) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *package, *fields; + struct hpp_type0 hpp0; int i; - memset(hpp, 0, sizeof(struct hotplug_params)); + memset(&hpp0, 0, sizeof(hpp0)); status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer); if (ACPI_FAILURE(status)) @@ -311,12 +317,13 @@ static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) } } - hpp->t0 = &hpp->type0_data; - hpp->t0->revision = 1; - hpp->t0->cache_line_size = fields[0].integer.value; - hpp->t0->latency_timer = fields[1].integer.value; - hpp->t0->enable_serr = fields[2].integer.value; - hpp->t0->enable_perr = fields[3].integer.value; + hpp0.revision = 1; + hpp0.cache_line_size = fields[0].integer.value; + hpp0.latency_timer = fields[1].integer.value; + hpp0.enable_serr = fields[2].integer.value; + hpp0.enable_perr = fields[3].integer.value; + + hp_ops->program_type0(dev, &hpp0); exit: kfree(buffer.pointer); @@ -328,7 +335,8 @@ exit: * @dev - the pci_dev for which we want parameters * @hpp - allocated by the caller */ -int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) +int pci_acpi_program_hp_params(struct pci_dev *dev, + const struct hotplug_program_ops *hp_ops) { acpi_status status; acpi_handle handle, phandle; @@ -351,10 +359,10 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) * this pci dev. */ while (handle) { - status = acpi_run_hpx(handle, hpp); + status = acpi_run_hpx(dev, handle, hp_ops); if (ACPI_SUCCESS(status)) return 0; - status = acpi_run_hpp(handle, hpp); + status = acpi_run_hpp(dev, handle, hp_ops); if (ACPI_SUCCESS(status)) return 0; if (acpi_is_root_bridge(handle)) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2ec0df04e0dc..dce5ae39d0d8 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2206,8 +2206,11 @@ static void pci_configure_serr(struct pci_dev *dev) static void pci_configure_device(struct pci_dev *dev) { - struct hotplug_params hpp; - int ret; + static const struct hotplug_program_ops hp_ops = { + .program_type0 = program_hpp_type0, + .program_type1 = program_hpp_type1, + .program_type2 = program_hpp_type2, + }; pci_configure_mps(dev); pci_configure_extended_tags(dev, NULL); @@ -2216,14 +2219,7 @@ static void pci_configure_device(struct pci_dev *dev) pci_configure_eetlp_prefix(dev); pci_configure_serr(dev); - memset(&hpp, 0, sizeof(hpp)); - ret = pci_get_hp_params(dev, &hpp); - if (ret) - return; - - program_hpp_type2(dev, hpp.t2); - program_hpp_type1(dev, hpp.t1); - program_hpp_type0(dev, hpp.t0); + pci_acpi_program_hp_params(dev, &hp_ops); } static void pci_release_capabilities(struct pci_dev *dev) diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 7acc9f91e72b..2c1e12b7ac00 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -124,26 +124,24 @@ struct hpp_type2 { u32 sec_unc_err_mask_or; }; -struct hotplug_params { - struct hpp_type0 *t0; /* Type0: NULL if not available */ - struct hpp_type1 *t1; /* Type1: NULL if not available */ - struct hpp_type2 *t2; /* Type2: NULL if not available */ - struct hpp_type0 type0_data; - struct hpp_type1 type1_data; - struct hpp_type2 type2_data; +struct hotplug_program_ops { + void (*program_type0)(struct pci_dev *dev, struct hpp_type0 *hpp); + void (*program_type1)(struct pci_dev *dev, struct hpp_type1 *hpp); + void (*program_type2)(struct pci_dev *dev, struct hpp_type2 *hpp); }; #ifdef CONFIG_ACPI #include -int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp); +int pci_acpi_program_hp_params(struct pci_dev *dev, + const struct hotplug_program_ops *hp_ops); bool pciehp_is_native(struct pci_dev *bridge); int acpi_get_hp_hw_control_from_firmware(struct pci_dev *bridge); bool shpchp_is_native(struct pci_dev *bridge); int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle); int acpi_pci_detect_ejectable(acpi_handle handle); #else -static inline int pci_get_hp_params(struct pci_dev *dev, - struct hotplug_params *hpp) +static inline int pci_acpi_program_hp_params(struct pci_dev *dev, + const struct hotplug_program_ops *hp_ops) { return -ENODEV; } -- cgit v1.2.3 From f873c51a155aaa6dafdc00fa7fda3754f2f9f794 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 8 Feb 2019 10:24:13 -0600 Subject: PCI/ACPI: Implement _HPX Type 3 Setting Record The _HPX Type 3 Setting Record is intended to be more generic and allow configuration of settings not possible with Type 2 records. For example, firmware could ensure that the completion timeout value is set accordingly throughout the PCI tree. Implement support for _HPX Type 3 Setting Records, which were added in the ACPI 6.3 spec. Link: https://lore.kernel.org/lkml/20190208162414.3996-4-mr.nuke.me@gmail.com Signed-off-by: Alexandru Gagniuc Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 63 ++++++++++++++++++++++++ drivers/pci/probe.c | 114 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_hotplug.h | 48 +++++++++++++++++++ 3 files changed, 225 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 95f4f86d2f34..03e02dd6c1d9 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -216,6 +216,64 @@ static acpi_status decode_type2_hpx_record(union acpi_object *record, return AE_OK; } +static void parse_hpx3_register(struct hpx_type3 *hpx3_reg, + union acpi_object *reg_fields) +{ + hpx3_reg->device_type = reg_fields[0].integer.value; + hpx3_reg->function_type = reg_fields[1].integer.value; + hpx3_reg->config_space_location = reg_fields[2].integer.value; + hpx3_reg->pci_exp_cap_id = reg_fields[3].integer.value; + hpx3_reg->pci_exp_cap_ver = reg_fields[4].integer.value; + hpx3_reg->pci_exp_vendor_id = reg_fields[5].integer.value; + hpx3_reg->dvsec_id = reg_fields[6].integer.value; + hpx3_reg->dvsec_rev = reg_fields[7].integer.value; + hpx3_reg->match_offset = reg_fields[8].integer.value; + hpx3_reg->match_mask_and = reg_fields[9].integer.value; + hpx3_reg->match_value = reg_fields[10].integer.value; + hpx3_reg->reg_offset = reg_fields[11].integer.value; + hpx3_reg->reg_mask_and = reg_fields[12].integer.value; + hpx3_reg->reg_mask_or = reg_fields[13].integer.value; +} + +static acpi_status program_type3_hpx_record(struct pci_dev *dev, + union acpi_object *record, + const struct hotplug_program_ops *hp_ops) +{ + union acpi_object *fields = record->package.elements; + u32 desc_count, expected_length, revision; + union acpi_object *reg_fields; + struct hpx_type3 hpx3; + int i; + + revision = fields[1].integer.value; + switch (revision) { + case 1: + desc_count = fields[2].integer.value; + expected_length = 3 + desc_count * 14; + + if (record->package.count != expected_length) + return AE_ERROR; + + for (i = 2; i < expected_length; i++) + if (fields[i].type != ACPI_TYPE_INTEGER) + return AE_ERROR; + + for (i = 0; i < desc_count; i++) { + reg_fields = fields + 3 + i * 14; + parse_hpx3_register(&hpx3, reg_fields); + hp_ops->program_type3(dev, &hpx3); + } + + break; + default: + printk(KERN_WARNING + "%s: Type 3 Revision %d record not supported\n", + __func__, revision); + return AE_ERROR; + } + return AE_OK; +} + static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle, const struct hotplug_program_ops *hp_ops) { @@ -275,6 +333,11 @@ static acpi_status acpi_run_hpx(struct pci_dev *dev, acpi_handle handle, goto exit; hp_ops->program_type2(dev, &hpx2); break; + case 3: + status = program_type3_hpx_record(dev, record, hp_ops); + if (ACPI_FAILURE(status)) + goto exit; + break; default: printk(KERN_ERR "%s: Type %d record not supported\n", __func__, type); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dce5ae39d0d8..ea6c1762d5c8 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2026,6 +2026,119 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) */ } +static u16 hpx3_device_type(struct pci_dev *dev) +{ + u16 pcie_type = pci_pcie_type(dev); + const int pcie_to_hpx3_type[] = { + [PCI_EXP_TYPE_ENDPOINT] = HPX_TYPE_ENDPOINT, + [PCI_EXP_TYPE_LEG_END] = HPX_TYPE_LEG_END, + [PCI_EXP_TYPE_RC_END] = HPX_TYPE_RC_END, + [PCI_EXP_TYPE_RC_EC] = HPX_TYPE_RC_EC, + [PCI_EXP_TYPE_ROOT_PORT] = HPX_TYPE_ROOT_PORT, + [PCI_EXP_TYPE_UPSTREAM] = HPX_TYPE_UPSTREAM, + [PCI_EXP_TYPE_DOWNSTREAM] = HPX_TYPE_DOWNSTREAM, + [PCI_EXP_TYPE_PCI_BRIDGE] = HPX_TYPE_PCI_BRIDGE, + [PCI_EXP_TYPE_PCIE_BRIDGE] = HPX_TYPE_PCIE_BRIDGE, + }; + + if (pcie_type >= ARRAY_SIZE(pcie_to_hpx3_type)) + return 0; + + return pcie_to_hpx3_type[pcie_type]; +} + +static u8 hpx3_function_type(struct pci_dev *dev) +{ + if (dev->is_virtfn) + return HPX_FN_SRIOV_VIRT; + else if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV) > 0) + return HPX_FN_SRIOV_PHYS; + else + return HPX_FN_NORMAL; +} + +static bool hpx3_cap_ver_matches(u8 pcie_cap_id, u8 hpx3_cap_id) +{ + u8 cap_ver = hpx3_cap_id & 0xf; + + if ((hpx3_cap_id & BIT(4)) && cap_ver >= pcie_cap_id) + return true; + else if (cap_ver == pcie_cap_id) + return true; + + return false; +} + +static void program_hpx_type3_register(struct pci_dev *dev, + const struct hpx_type3 *reg) +{ + u32 match_reg, write_reg, header, orig_value; + u16 pos; + + if (!(hpx3_device_type(dev) & reg->device_type)) + return; + + if (!(hpx3_function_type(dev) & reg->function_type)) + return; + + switch (reg->config_space_location) { + case HPX_CFG_PCICFG: + pos = 0; + break; + case HPX_CFG_PCIE_CAP: + pos = pci_find_capability(dev, reg->pci_exp_cap_id); + if (pos == 0) + return; + + break; + case HPX_CFG_PCIE_CAP_EXT: + pos = pci_find_ext_capability(dev, reg->pci_exp_cap_id); + if (pos == 0) + return; + + pci_read_config_dword(dev, pos, &header); + if (!hpx3_cap_ver_matches(PCI_EXT_CAP_VER(header), + reg->pci_exp_cap_ver)) + return; + + break; + case HPX_CFG_VEND_CAP: /* Fall through */ + case HPX_CFG_DVSEC: /* Fall through */ + default: + pci_warn(dev, "Encountered _HPX type 3 with unsupported config space location"); + return; + } + + pci_read_config_dword(dev, pos + reg->match_offset, &match_reg); + + if ((match_reg & reg->match_mask_and) != reg->match_value) + return; + + pci_read_config_dword(dev, pos + reg->reg_offset, &write_reg); + orig_value = write_reg; + write_reg &= reg->reg_mask_and; + write_reg |= reg->reg_mask_or; + + if (orig_value == write_reg) + return; + + pci_write_config_dword(dev, pos + reg->reg_offset, write_reg); + + pci_dbg(dev, "Applied _HPX3 at [0x%x]: 0x%08x -> 0x%08x", + pos, orig_value, write_reg); +} + +static void program_hpx_type3(struct pci_dev *dev, struct hpx_type3 *hpx3) +{ + if (!hpx3) + return; + + if (!pci_is_pcie(dev)) + return; + + program_hpx_type3_register(dev, hpx3); +} + int pci_configure_extended_tags(struct pci_dev *dev, void *ign) { struct pci_host_bridge *host; @@ -2210,6 +2323,7 @@ static void pci_configure_device(struct pci_dev *dev) .program_type0 = program_hpp_type0, .program_type1 = program_hpp_type1, .program_type2 = program_hpp_type2, + .program_type3 = program_hpx_type3, }; pci_configure_mps(dev); diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 2c1e12b7ac00..f694eb2ca978 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -124,10 +124,58 @@ struct hpp_type2 { u32 sec_unc_err_mask_or; }; +/* + * _HPX PCI Express Setting Record (Type 3) + */ +struct hpx_type3 { + u16 device_type; + u16 function_type; + u16 config_space_location; + u16 pci_exp_cap_id; + u16 pci_exp_cap_ver; + u16 pci_exp_vendor_id; + u16 dvsec_id; + u16 dvsec_rev; + u16 match_offset; + u32 match_mask_and; + u32 match_value; + u16 reg_offset; + u32 reg_mask_and; + u32 reg_mask_or; +}; + struct hotplug_program_ops { void (*program_type0)(struct pci_dev *dev, struct hpp_type0 *hpp); void (*program_type1)(struct pci_dev *dev, struct hpp_type1 *hpp); void (*program_type2)(struct pci_dev *dev, struct hpp_type2 *hpp); + void (*program_type3)(struct pci_dev *dev, struct hpx_type3 *hpp); +}; + +enum hpx_type3_dev_type { + HPX_TYPE_ENDPOINT = BIT(0), + HPX_TYPE_LEG_END = BIT(1), + HPX_TYPE_RC_END = BIT(2), + HPX_TYPE_RC_EC = BIT(3), + HPX_TYPE_ROOT_PORT = BIT(4), + HPX_TYPE_UPSTREAM = BIT(5), + HPX_TYPE_DOWNSTREAM = BIT(6), + HPX_TYPE_PCI_BRIDGE = BIT(7), + HPX_TYPE_PCIE_BRIDGE = BIT(8), +}; + +enum hpx_type3_fn_type { + HPX_FN_NORMAL = BIT(0), + HPX_FN_SRIOV_PHYS = BIT(1), + HPX_FN_SRIOV_VIRT = BIT(2), +}; + +enum hpx_type3_cfg_loc { + HPX_CFG_PCICFG = 0, + HPX_CFG_PCIE_CAP = 1, + HPX_CFG_PCIE_CAP_EXT = 2, + HPX_CFG_VEND_CAP = 3, + HPX_CFG_DVSEC = 4, + HPX_CFG_MAX, }; #ifdef CONFIG_ACPI -- cgit v1.2.3 From ba11edc65065ffe38508aa76fdf7a7db15ad8161 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 15 Mar 2019 14:29:40 -0500 Subject: PCI/ACPI: Advertise _HPX Type 3 support via _OSC _OSC now has a way to inform firmware that OS has the capability to interpret _HPX Type 3 setting records. This was added by the following PCI Firmware Specification ECN: ECN: _HPX and PCIe Completion Timeout related _OSC Enhancements Date: September 12, 2018 Affected Document: PCI Firmware Specification, Rev. 3.2 Signed-off-by: Alexandru Gagniuc Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 2 ++ include/linux/acpi.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 707aafc7c2aa..c36781a9b493 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -145,6 +145,7 @@ static struct pci_osc_bit_struct pci_osc_support_bit[] = { { OSC_PCI_CLOCK_PM_SUPPORT, "ClockPM" }, { OSC_PCI_SEGMENT_GROUPS_SUPPORT, "Segments" }, { OSC_PCI_MSI_SUPPORT, "MSI" }, + { OSC_PCI_HPX_TYPE_3_SUPPORT, "HPX-Type3" }, }; static struct pci_osc_bit_struct pci_osc_control_bit[] = { @@ -446,6 +447,7 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, * PCI domains, so we indicate this in _OSC support capabilities. */ support = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + support |= OSC_PCI_HPX_TYPE_3_SUPPORT; if (pci_ext_cfg_avail()) support |= OSC_PCI_EXT_CONFIG_SUPPORT; if (pcie_aspm_support_enabled()) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d5dcebd7aad3..b84bcd705fe1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -513,7 +513,8 @@ extern bool osc_pc_lpi_support_confirmed; #define OSC_PCI_CLOCK_PM_SUPPORT 0x00000004 #define OSC_PCI_SEGMENT_GROUPS_SUPPORT 0x00000008 #define OSC_PCI_MSI_SUPPORT 0x00000010 -#define OSC_PCI_SUPPORT_MASKS 0x0000001f +#define OSC_PCI_HPX_TYPE_3_SUPPORT 0x00000100 +#define OSC_PCI_SUPPORT_MASKS 0x0000011f /* PCI Host Bridge _OSC: Capabilities DWORD 3: Control Field */ #define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 0x00000001 -- cgit v1.2.3 From 4166bfe53093b687a0b1b22e5d943e143b8089b2 Mon Sep 17 00:00:00 2001 From: Jonathan Chocron Date: Thu, 28 Mar 2019 13:57:56 +0200 Subject: PCI: al: Add Amazon Annapurna Labs PCIe host controller driver Add driver for Amazon's Annapurna Labs PCIe host controller. The controller is based on DesignWare's IP. The controller doesn't support accessing the Root Port's config space via ECAM, so we obtain its base address via an AMZN0001 device. Furthermore, the DesignWare PCIe controller doesn't filter out config transactions sent to devices 1 and up on its bus, so they are filtered by the driver. All subordinate buses do support ECAM access. Implementing specific PCI config access functions involves: - Adding an init function to obtain the Root Port's base address from an AMZN0001 device. - Adding a new entry in the MCFG quirk array. [bhelgaas: Note that there is no Kconfig option for this driver because it is only intended for use with the generic ACPI host bridge driver. This driver is only needed because the DesignWare IP doesn't completely support ECAM access to the root bus.] Link: https://lore.kernel.org/lkml/1553774276-24675-1-git-send-email-jonnyc@amazon.com Co-developed-by: Vladimir Aerov Signed-off-by: Jonathan Chocron Signed-off-by: Vladimir Aerov Signed-off-by: Bjorn Helgaas Reviewed-by: David Woodhouse Reviewed-by: Benjamin Herrenschmidt Acked-by: Lorenzo Pieralisi --- MAINTAINERS | 6 +++ drivers/acpi/pci_mcfg.c | 12 +++++ drivers/pci/controller/dwc/Makefile | 1 + drivers/pci/controller/dwc/pcie-al.c | 93 ++++++++++++++++++++++++++++++++++++ include/linux/pci-ecam.h | 1 + 5 files changed, 113 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-al.c (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index e17ebf70b548..8b18d02a50cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12014,6 +12014,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/ S: Supported F: drivers/pci/controller/ +PCIE DRIVER FOR ANNAPURNA LABS +M: Jonathan Chocron +L: linux-pci@vger.kernel.org +S: Maintained +F: drivers/pci/controller/dwc/pcie-al.c + PCIE DRIVER FOR AMLOGIC MESON M: Yue Wang L: linux-pci@vger.kernel.org diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c index a4e8432fc2fb..b42be067fb83 100644 --- a/drivers/acpi/pci_mcfg.c +++ b/drivers/acpi/pci_mcfg.c @@ -52,6 +52,18 @@ struct mcfg_fixup { static struct mcfg_fixup mcfg_quirks[] = { /* { OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */ +#define AL_ECAM(table_id, rev, seg, ops) \ + { "AMAZON", table_id, rev, seg, MCFG_BUS_ANY, ops } + + AL_ECAM("GRAVITON", 0, 0, &al_pcie_ops), + AL_ECAM("GRAVITON", 0, 1, &al_pcie_ops), + AL_ECAM("GRAVITON", 0, 2, &al_pcie_ops), + AL_ECAM("GRAVITON", 0, 3, &al_pcie_ops), + AL_ECAM("GRAVITON", 0, 4, &al_pcie_ops), + AL_ECAM("GRAVITON", 0, 5, &al_pcie_ops), + AL_ECAM("GRAVITON", 0, 6, &al_pcie_ops), + AL_ECAM("GRAVITON", 0, 7, &al_pcie_ops), + #define QCOM_ECAM32(seg) \ { "QCOM ", "QDF2432 ", 1, seg, MCFG_BUS_ANY, &pci_32b_ops } diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index b5f3b83cc2b3..b085dfd4fab7 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -28,5 +28,6 @@ obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o # depending on whether ACPI, the DT driver, or both are enabled. ifdef CONFIG_PCI +obj-$(CONFIG_ARM64) += pcie-al.o obj-$(CONFIG_ARM64) += pcie-hisi.o endif diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c new file mode 100644 index 000000000000..3ab58f0584a8 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Amazon's Annapurna Labs IP (used in chips + * such as Graviton and Alpine) + * + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Author: Jonathan Chocron + */ + +#include +#include +#include +#include "../../pci.h" + +#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) + +struct al_pcie_acpi { + void __iomem *dbi_base; +}; + +static void __iomem *al_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct pci_config_window *cfg = bus->sysdata; + struct al_pcie_acpi *pcie = cfg->priv; + void __iomem *dbi_base = pcie->dbi_base; + + if (bus->number == cfg->busr.start) { + /* + * The DW PCIe core doesn't filter out transactions to other + * devices/functions on the root bus num, so we do this here. + */ + if (PCI_SLOT(devfn) > 0) + return NULL; + else + return dbi_base + where; + } + + return pci_ecam_map_bus(bus, devfn, where); +} + +static int al_pcie_init(struct pci_config_window *cfg) +{ + struct device *dev = cfg->parent; + struct acpi_device *adev = to_acpi_device(dev); + struct acpi_pci_root *root = acpi_driver_data(adev); + struct al_pcie_acpi *al_pcie; + struct resource *res; + int ret; + + al_pcie = devm_kzalloc(dev, sizeof(*al_pcie), GFP_KERNEL); + if (!al_pcie) + return -ENOMEM; + + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + + ret = acpi_get_rc_resources(dev, "AMZN0001", root->segment, res); + if (ret) { + dev_err(dev, "can't get rc dbi base address for SEG %d\n", + root->segment); + return ret; + } + + dev_dbg(dev, "Root port dbi res: %pR\n", res); + + al_pcie->dbi_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(al_pcie->dbi_base)) { + long err = PTR_ERR(al_pcie->dbi_base); + + dev_err(dev, "couldn't remap dbi base %pR (err:%ld)\n", + res, err); + return err; + } + + cfg->priv = al_pcie; + + return 0; +} + +struct pci_ecam_ops al_pcie_ops = { + .bus_shift = 20, + .init = al_pcie_init, + .pci_ops = { + .map_bus = al_pcie_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, + } +}; + +#endif /* defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) */ diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h index 29efa09d686b..a73164c85e78 100644 --- a/include/linux/pci-ecam.h +++ b/include/linux/pci-ecam.h @@ -56,6 +56,7 @@ extern struct pci_ecam_ops thunder_pem_ecam_ops; /* Cavium ThunderX 1.x & 2.x */ extern struct pci_ecam_ops pci_thunder_ecam_ops; /* Cavium ThunderX 1.x */ extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops; /* APM X-Gene PCIe v1 */ extern struct pci_ecam_ops xgene_v2_pcie_ecam_ops; /* APM X-Gene PCIe v2.x */ +extern struct pci_ecam_ops al_pcie_ops; /* Amazon Annapurna Labs PCIe */ #endif #ifdef CONFIG_PCI_HOST_COMMON -- cgit v1.2.3 From 4e544bac8267f65a0bf06aed1bde9964da4812ed Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 24 Apr 2019 21:11:58 +0200 Subject: PCI: Add pci_dev_id() helper In several places in the kernel we find PCI_DEVID used like this: PCI_DEVID(dev->bus->number, dev->devfn) Add a "pci_dev_id(struct pci_dev *dev)" helper to simplify callers. Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 6 +++--- drivers/pci/search.c | 10 +++------- include/linux/pci.h | 5 +++++ 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 73986825d221..e039b740fe74 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1338,7 +1338,7 @@ irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, struct msi_desc *desc) { return (irq_hw_number_t)desc->msi_attrib.entry_nr | - PCI_DEVID(dev->bus->number, dev->devfn) << 11 | + pci_dev_id(dev) << 11 | (pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27; } @@ -1508,7 +1508,7 @@ static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data) u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) { struct device_node *of_node; - u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); + u32 rid = pci_dev_id(pdev); pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); @@ -1531,7 +1531,7 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev) { struct irq_domain *dom; - u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); + u32 rid = pci_dev_id(pdev); pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); dom = of_msi_map_get_device_domain(&pdev->dev, rid); diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 2b5f720862d3..5c7922612733 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -33,7 +33,7 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, struct pci_bus *bus; int ret; - ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data); + ret = fn(pdev, pci_dev_id(pdev), data); if (ret) return ret; @@ -88,9 +88,7 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, return ret; continue; case PCI_EXP_TYPE_PCIE_BRIDGE: - ret = fn(tmp, - PCI_DEVID(tmp->bus->number, - tmp->devfn), data); + ret = fn(tmp, pci_dev_id(tmp), data); if (ret) return ret; continue; @@ -101,9 +99,7 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, PCI_DEVID(tmp->subordinate->number, PCI_DEVFN(0, 0)), data); else - ret = fn(tmp, - PCI_DEVID(tmp->bus->number, - tmp->devfn), data); + ret = fn(tmp, pci_dev_id(tmp), data); if (ret) return ret; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 5f32275358cf..26fa6d143dfe 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -596,6 +596,11 @@ struct pci_bus { #define to_pci_bus(n) container_of(n, struct pci_bus, dev) +static inline u16 pci_dev_id(struct pci_dev *dev) +{ + return PCI_DEVID(dev->bus->number, dev->devfn); +} + /* * Returns true if the PCI bus is root (behind host-PCI bridge), * false otherwise -- cgit v1.2.3 From e80a91ad302b3b66822756afc61f9f9c1658b9c9 Mon Sep 17 00:00:00 2001 From: Srinath Mannam Date: Fri, 3 May 2019 19:35:32 +0530 Subject: PCI: Add dma_ranges window list Add a dma_ranges field in PCI host bridge structure to hold resource entries list of memory regions in sorted order representing memory ranges that can be accessed through DMA transactions. Based-on-a-patch-by: Oza Pawandeep Signed-off-by: Srinath Mannam [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Oza Pawandeep Acked-by: Bjorn Helgaas --- drivers/pci/probe.c | 3 +++ include/linux/pci.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2ec0df04e0dc..2a651089a606 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -595,6 +595,7 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) return NULL; INIT_LIST_HEAD(&bridge->windows); + INIT_LIST_HEAD(&bridge->dma_ranges); bridge->dev.release = pci_release_host_bridge_dev; /* @@ -623,6 +624,7 @@ struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev, return NULL; INIT_LIST_HEAD(&bridge->windows); + INIT_LIST_HEAD(&bridge->dma_ranges); bridge->dev.release = devm_pci_release_host_bridge_dev; return bridge; @@ -632,6 +634,7 @@ EXPORT_SYMBOL(devm_pci_alloc_host_bridge); void pci_free_host_bridge(struct pci_host_bridge *bridge) { pci_free_resource_list(&bridge->windows); + pci_free_resource_list(&bridge->dma_ranges); kfree(bridge); } diff --git a/include/linux/pci.h b/include/linux/pci.h index 77448215ef5b..bba0a29a5066 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -490,6 +490,7 @@ struct pci_host_bridge { void *sysdata; int busnr; struct list_head windows; /* resource_entry */ + struct list_head dma_ranges; /* dma ranges resource list */ u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* Platform IRQ swizzler */ int (*map_irq)(const struct pci_dev *, u8, u8); void (*release_fn)(struct pci_host_bridge *); -- cgit v1.2.3