From fdfe151127a75de037c7e32cef110ae9c7c5e3c8 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 5 Sep 2013 15:55:29 +0800 Subject: [PATCH 01/79] PCI: Use pci_is_pcie() to simplify code Use pci_is_pcie() instead of pci_find_capability() to simplify code. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7ef0f868b3e0..c90d0f8021e7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -641,8 +641,7 @@ static void pci_set_bus_speed(struct pci_bus *bus) return; } - pos = pci_find_capability(bridge, PCI_CAP_ID_EXP); - if (pos) { + if (pci_is_pcie(bridge)) { u32 linkcap; u16 linksta; From f8a26fe637fc740ebb927ce25dcf036363a998df Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 5 Sep 2013 15:55:28 +0800 Subject: [PATCH 02/79] x86/pci: Use cached pci_dev->pcie_cap to simplify code The PCI core caches the PCIe Capability offset in pci_dev->pcie_cap, so use that instead of pci_find_capability(). Use pci_bus_set_ops() when replacing the device pci_ops. And use #defines instead of numeric constants. [bhelgaas: changelog, also use PCI_EXP_LNKCTL_ASPMC] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- arch/x86/pci/fixup.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index f5809fa2753e..b046e070e088 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -231,7 +231,7 @@ static int quirk_pcie_aspm_write(struct pci_bus *bus, unsigned int devfn, int wh offset = quirk_aspm_offset[GET_INDEX(bus->self->device, devfn)]; if ((offset) && (where == offset)) - value = value & 0xfffffffc; + value = value & ~PCI_EXP_LNKCTL_ASPMC; return raw_pci_write(pci_domain_nr(bus), bus->number, devfn, where, size, value); @@ -252,7 +252,7 @@ static struct pci_ops quirk_pcie_aspm_ops = { */ static void pcie_rootport_aspm_quirk(struct pci_dev *pdev) { - int cap_base, i; + int i; struct pci_bus *pbus; struct pci_dev *dev; @@ -278,7 +278,7 @@ static void pcie_rootport_aspm_quirk(struct pci_dev *pdev) for (i = GET_INDEX(pdev->device, 0); i <= GET_INDEX(pdev->device, 7); ++i) quirk_aspm_offset[i] = 0; - pbus->ops = pbus->parent->ops; + pci_bus_set_ops(pbus, pbus->parent->ops); } else { /* * If devices are attached to the root port at power-up or @@ -286,13 +286,15 @@ static void pcie_rootport_aspm_quirk(struct pci_dev *pdev) * each root port to save the register offsets and replace the * bus ops. */ - list_for_each_entry(dev, &pbus->devices, bus_list) { + list_for_each_entry(dev, &pbus->devices, bus_list) /* There are 0 to 8 devices attached to this bus */ - cap_base = pci_find_capability(dev, PCI_CAP_ID_EXP); - quirk_aspm_offset[GET_INDEX(pdev->device, dev->devfn)] = cap_base + 0x10; - } - pbus->ops = &quirk_pcie_aspm_ops; + quirk_aspm_offset[GET_INDEX(pdev->device, dev->devfn)] = + dev->pcie_cap + PCI_EXP_LNKCTL; + + pci_bus_set_ops(pbus, &quirk_pcie_aspm_ops); + dev_info(&pbus->dev, "writes to ASPM control bits will be ignored\n"); } + } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MCH_PA, pcie_rootport_aspm_quirk); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MCH_PA1, pcie_rootport_aspm_quirk); From c0102c00d8c9f4109509d9d68631a1ad47703b5f Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 5 Sep 2013 15:55:25 +0800 Subject: [PATCH 03/79] [SCSI] bfa: Use pcie_set()/get_readrq() to simplify code Use pcie_get_readrq()/pcie_set_readrq() to simplify code. Jon Mason proposed a similar patch a couple years ago (see below). [bhelgaas: validate pcie_max_read_reqsz, add pointer to Jon's patch] Reference: http://lkml.kernel.org/r/1309191190-14670-1-git-send-email-jdmason@kudzu.us Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Cc: Jiang Liu Cc: Anil Gurumurthy Cc: Vijaya Mohan Guvva Cc: "James E.J. Bottomley" --- drivers/scsi/bfa/bfad.c | 53 ++++++++++------------------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c index f8ca7becacca..7591fa4e28bb 100644 --- a/drivers/scsi/bfa/bfad.c +++ b/drivers/scsi/bfa/bfad.c @@ -766,49 +766,20 @@ bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad) bfad->pcidev = pdev; /* Adjust PCIe Maximum Read Request Size */ - if (pcie_max_read_reqsz > 0) { - int pcie_cap_reg; - u16 pcie_dev_ctl; - u16 mask = 0xffff; - - switch (pcie_max_read_reqsz) { - case 128: - mask = 0x0; - break; - case 256: - mask = 0x1000; - break; - case 512: - mask = 0x2000; - break; - case 1024: - mask = 0x3000; - break; - case 2048: - mask = 0x4000; - break; - case 4096: - mask = 0x5000; - break; - default: - break; - } - - pcie_cap_reg = pci_find_capability(pdev, PCI_CAP_ID_EXP); - if (mask != 0xffff && pcie_cap_reg) { - pcie_cap_reg += 0x08; - pci_read_config_word(pdev, pcie_cap_reg, &pcie_dev_ctl); - if ((pcie_dev_ctl & 0x7000) != mask) { - printk(KERN_WARNING "BFA[%s]: " + if (pci_is_pcie(pdev) && pcie_max_read_reqsz) { + if (pcie_max_read_reqsz >= 128 && + pcie_max_read_reqsz <= 4096 && + is_power_of_2(pcie_max_read_reqsz)) { + int max_rq = pcie_get_readrq(pdev); + printk(KERN_WARNING "BFA[%s]: " "pcie_max_read_request_size is %d, " - "reset to %d\n", bfad->pci_name, - (1 << ((pcie_dev_ctl & 0x7000) >> 12)) << 7, + "reset to %d\n", bfad->pci_name, max_rq, pcie_max_read_reqsz); - - pcie_dev_ctl &= ~0x7000; - pci_write_config_word(pdev, pcie_cap_reg, - pcie_dev_ctl | mask); - } + pcie_set_readrq(pdev, pcie_max_read_reqsz); + } else { + printk(KERN_WARNING "BFA[%s]: invalid " + "pcie_max_read_request_size %d ignored\n", + bfad->pci_name, pcie_max_read_reqsz); } } From ad4d35f865408a494f0a4c02b1c7ebd3f80f5dbf Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 5 Sep 2013 15:55:26 +0800 Subject: [PATCH 04/79] [SCSI] csiostor: Use pcie_capability_clear_and_set_word() to simplify code pci_is_pcie() and pcie_capability_clear_and_set_word() make it trivial to set the PCIe Completion Timeout, so just fold the csio_set_pcie_completion_timeout() function into its caller. [bhelgaas: changelog, fold csio_set_pcie_completion_timeout() into caller] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Cc: Jiang Liu Cc: "James E.J. Bottomley" Cc: Naresh Kumar Inna Cc: "David S. Miller" Cc: Jesper Juhl --- drivers/scsi/csiostor/csio_hw.c | 22 ++++------------------ include/uapi/linux/pci_regs.h | 3 ++- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 0eb35b9b3784..0eaec4748957 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -852,22 +852,6 @@ csio_hw_get_flash_params(struct csio_hw *hw) return 0; } -static void -csio_set_pcie_completion_timeout(struct csio_hw *hw, u8 range) -{ - uint16_t val; - int pcie_cap; - - if (!csio_pci_capability(hw->pdev, PCI_CAP_ID_EXP, &pcie_cap)) { - pci_read_config_word(hw->pdev, - pcie_cap + PCI_EXP_DEVCTL2, &val); - val &= 0xfff0; - val |= range ; - pci_write_config_word(hw->pdev, - pcie_cap + PCI_EXP_DEVCTL2, val); - } -} - /*****************************************************************************/ /* HW State machine assists */ /*****************************************************************************/ @@ -2069,8 +2053,10 @@ csio_hw_configure(struct csio_hw *hw) goto out; } - /* Set pci completion timeout value to 4 seconds. */ - csio_set_pcie_completion_timeout(hw, 0xd); + /* Set PCIe completion timeout to 4 seconds */ + if (pci_is_pcie(hw->pdev)) + pcie_capability_clear_and_set_word(hw->pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0xd); hw->chip_ops->chip_set_mem_win(hw, MEMWIN_CSIOSTOR); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index baa7852468ef..1a38377a0032 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -558,7 +558,8 @@ #define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ #define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ -#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */ +#define PCI_EXP_DEVCTL2_ARI 0x0020 /* Alternative Routing-ID */ #define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x0100 /* Allow IDO for requests */ #define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x0200 /* Allow IDO for completions */ #define PCI_EXP_DEVCTL2_LTR_EN 0x0400 /* Enable LTR mechanism */ From ce9f7ed92adfc4c56c68d757e66fd1497dcf1130 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 5 Sep 2013 15:55:30 +0800 Subject: [PATCH 05/79] [SCSI] qla2xxx: Use pcie_is_pcie() to simplify code Use pci_is_pcie() instead of pci_find_capability() to simplify code. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Chad Dupuis Cc: Andrew Vasquez Cc: linux-driver@qlogic.com Cc: "James E.J. Bottomley" --- drivers/scsi/qla2xxx/qla_mr.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c index 62ee7131b204..30d20e74e48a 100644 --- a/drivers/scsi/qla2xxx/qla_mr.c +++ b/drivers/scsi/qla2xxx/qla_mr.c @@ -507,7 +507,7 @@ qlafx00_pci_config(scsi_qla_host_t *vha) pci_write_config_word(ha->pdev, PCI_COMMAND, w); /* PCIe -- adjust Maximum Read Request Size (2048). */ - if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) + if (pci_is_pcie(ha->pdev)) pcie_set_readrq(ha->pdev, 2048); ha->chip_revision = ha->pdev->revision; @@ -660,10 +660,8 @@ char * qlafx00_pci_info_str(struct scsi_qla_host *vha, char *str) { struct qla_hw_data *ha = vha->hw; - int pcie_reg; - pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP); - if (pcie_reg) { + if (pci_is_pcie(ha->pdev)) { strcpy(str, "PCIe iSA"); return str; } From f0308261b1dd5acba5c7e797159e60f94706df7d Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 5 Sep 2013 15:55:27 +0800 Subject: [PATCH 06/79] powerpc/pci: Use pci_is_pcie() to simplify code Use pci_is_pcie() to simplify code. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Kumar Gala Reviewed-by: Gavin Shan Cc: Gavin Shan Cc: Benjamin Herrenschmidt Cc: Paul Mackerras --- arch/powerpc/kernel/eeh.c | 3 +-- arch/powerpc/sysdev/fsl_pci.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 55593ee2d5aa..6ebbe545b7a5 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -189,8 +189,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) } /* If PCI-E capable, dump PCI-E cap 10, and the AER */ - cap = pci_find_capability(dev, PCI_CAP_ID_EXP); - if (cap) { + if (pci_is_pcie(dev)) { n += scnprintf(buf+n, len-n, "pci-e cap10:\n"); printk(KERN_WARNING "EEH: PCI-E capabilities and status follow:\n"); diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index ccfb50ddfe38..92e7258478d8 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -45,7 +45,7 @@ static void quirk_fsl_pcie_header(struct pci_dev *dev) u8 hdr_type; /* if we aren't a PCIe don't bother */ - if (!pci_find_capability(dev, PCI_CAP_ID_EXP)) + if (!pci_is_pcie(dev)) return; /* if we aren't in host mode don't bother */ From dedf1e4dfd5477b4315ad451b4be0ff8d9f7e85f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:05:53 -0600 Subject: [PATCH 07/79] ACPI: Write _OSC bit field definitions in hex Update _OSC definition comments to correspond to the 1-based spec wording (DWORD 1, etc.) Write _OSC field #defines as hex to make clear that they are bits in a 32-bit DWORD, not arbitrary values. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- include/linux/acpi.h | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index a5db4aeefa36..164ba10ddcb3 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -304,39 +304,39 @@ struct acpi_osc_context { #define OSC_SUPPORT_TYPE 1 #define OSC_CONTROL_TYPE 2 -/* _OSC DW0 Definition */ -#define OSC_QUERY_ENABLE 1 -#define OSC_REQUEST_ERROR 2 -#define OSC_INVALID_UUID_ERROR 4 -#define OSC_INVALID_REVISION_ERROR 8 -#define OSC_CAPABILITIES_MASK_ERROR 16 +/* _OSC Capabilities DWORD 1: Query/Control and Error Returns (generic) */ +#define OSC_QUERY_ENABLE 0x00000001 /* input */ +#define OSC_REQUEST_ERROR 0x00000002 /* return */ +#define OSC_INVALID_UUID_ERROR 0x00000004 /* return */ +#define OSC_INVALID_REVISION_ERROR 0x00000008 /* return */ +#define OSC_CAPABILITIES_MASK_ERROR 0x00000010 /* return */ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); -/* platform-wide _OSC bits */ -#define OSC_SB_PAD_SUPPORT 1 -#define OSC_SB_PPC_OST_SUPPORT 2 -#define OSC_SB_PR3_SUPPORT 4 -#define OSC_SB_HOTPLUG_OST_SUPPORT 8 -#define OSC_SB_APEI_SUPPORT 16 +/* Platform-Wide Capabilities _OSC: Capabilities DWORD 2: Support Field */ +#define OSC_SB_PAD_SUPPORT 0x00000001 +#define OSC_SB_PPC_OST_SUPPORT 0x00000002 +#define OSC_SB_PR3_SUPPORT 0x00000004 +#define OSC_SB_HOTPLUG_OST_SUPPORT 0x00000008 +#define OSC_SB_APEI_SUPPORT 0x00000010 +#define OSC_SB_CPC_SUPPORT 0x00000020 extern bool osc_sb_apei_support_acked; -/* PCI defined _OSC bits */ -/* _OSC DW1 Definition (OS Support Fields) */ -#define OSC_EXT_PCI_CONFIG_SUPPORT 1 -#define OSC_ACTIVE_STATE_PWR_SUPPORT 2 -#define OSC_CLOCK_PWR_CAPABILITY_SUPPORT 4 -#define OSC_PCI_SEGMENT_GROUPS_SUPPORT 8 -#define OSC_MSI_SUPPORT 16 -#define OSC_PCI_SUPPORT_MASKS 0x1f +/* PCI Host Bridge _OSC: Capabilities DWORD 2: Support Field */ +#define OSC_EXT_PCI_CONFIG_SUPPORT 0x00000001 +#define OSC_ACTIVE_STATE_PWR_SUPPORT 0x00000002 +#define OSC_CLOCK_PWR_CAPABILITY_SUPPORT 0x00000004 +#define OSC_PCI_SEGMENT_GROUPS_SUPPORT 0x00000008 +#define OSC_MSI_SUPPORT 0x00000010 +#define OSC_PCI_SUPPORT_MASKS 0x0000001f -/* _OSC DW1 Definition (OS Control Fields) */ -#define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 1 -#define OSC_SHPC_NATIVE_HP_CONTROL 2 -#define OSC_PCI_EXPRESS_PME_CONTROL 4 -#define OSC_PCI_EXPRESS_AER_CONTROL 8 -#define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL 16 +/* PCI Host Bridge _OSC: Capabilities DWORD 3: Control Field */ +#define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 0x00000001 +#define OSC_SHPC_NATIVE_HP_CONTROL 0x00000002 +#define OSC_PCI_EXPRESS_PME_CONTROL 0x00000004 +#define OSC_PCI_EXPRESS_AER_CONTROL 0x00000008 +#define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL 0x00000010 #define OSC_PCI_CONTROL_MASKS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \ OSC_SHPC_NATIVE_HP_CONTROL | \ From b938a229c85a567de7dba2d806d9f63a7c90483e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:05:54 -0600 Subject: [PATCH 08/79] ACPI: Rename OSC_QUERY_TYPE to OSC_QUERY_DWORD OSC_QUERY_TYPE isn't a "type"; it's an index into the _OSC Capabilities Buffer of DWORDs. Rename OSC_QUERY_TYPE, OSC_SUPPORT_TYPE, and OSC_CONTROL_TYPE to OSC_QUERY_DWORD, etc., to make this clear. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/apei/apei-base.c | 6 +++--- drivers/acpi/bus.c | 18 +++++++++--------- drivers/acpi/pci_root.c | 14 +++++++------- include/linux/acpi.h | 7 ++++--- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 46f80e2c92f7..6d2c49b86b7f 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -758,9 +758,9 @@ int apei_osc_setup(void) .cap.pointer = capbuf, }; - capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; - capbuf[OSC_SUPPORT_TYPE] = 1; - capbuf[OSC_CONTROL_TYPE] = 0; + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; + capbuf[OSC_SUPPORT_DWORD] = 1; + capbuf[OSC_CONTROL_DWORD] = 0; if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)) || ACPI_FAILURE(acpi_run_osc(handle, &context))) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index b587ec8257b2..fbcfaa682c15 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -255,7 +255,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) acpi_print_osc_error(handle, context, "_OSC invalid revision"); if (errors & OSC_CAPABILITIES_MASK_ERROR) { - if (((u32 *)context->cap.pointer)[OSC_QUERY_TYPE] + if (((u32 *)context->cap.pointer)[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE) goto out_success; status = AE_SUPPORT; @@ -295,30 +295,30 @@ static void acpi_bus_osc_support(void) }; acpi_handle handle; - capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; - capbuf[OSC_SUPPORT_TYPE] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */ + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; + capbuf[OSC_SUPPORT_DWORD] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */ #if defined(CONFIG_ACPI_PROCESSOR_AGGREGATOR) ||\ defined(CONFIG_ACPI_PROCESSOR_AGGREGATOR_MODULE) - capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_PAD_SUPPORT; + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT; #endif #if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE) - capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_PPC_OST_SUPPORT; + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; #endif #ifdef ACPI_HOTPLUG_OST - capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_HOTPLUG_OST_SUPPORT; + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; #endif if (!ghes_disable) - capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_APEI_SUPPORT; + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT; if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return; if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) { u32 *capbuf_ret = context.ret.pointer; - if (context.ret.length > OSC_SUPPORT_TYPE) + if (context.ret.length > OSC_SUPPORT_DWORD) osc_sb_apei_support_acked = - capbuf_ret[OSC_SUPPORT_TYPE] & OSC_SB_APEI_SUPPORT; + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; kfree(context.ret.pointer); } /* do we need to check other returned cap? Sounds no */ diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index d3874f425653..323afd7ccfbf 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -158,14 +158,14 @@ static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, support &= OSC_PCI_SUPPORT_MASKS; support |= root->osc_support_set; - capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; - capbuf[OSC_SUPPORT_TYPE] = support; + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; + capbuf[OSC_SUPPORT_DWORD] = support; if (control) { *control &= OSC_PCI_CONTROL_MASKS; - capbuf[OSC_CONTROL_TYPE] = *control | root->osc_control_set; + capbuf[OSC_CONTROL_DWORD] = *control | root->osc_control_set; } else { /* Run _OSC query only with existing controls. */ - capbuf[OSC_CONTROL_TYPE] = root->osc_control_set; + capbuf[OSC_CONTROL_DWORD] = root->osc_control_set; } status = acpi_pci_run_osc(root->device->handle, capbuf, &result); @@ -357,9 +357,9 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req) goto out; } - capbuf[OSC_QUERY_TYPE] = 0; - capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set; - capbuf[OSC_CONTROL_TYPE] = ctrl; + capbuf[OSC_QUERY_DWORD] = 0; + capbuf[OSC_SUPPORT_DWORD] = root->osc_support_set; + capbuf[OSC_CONTROL_DWORD] = ctrl; status = acpi_pci_run_osc(handle, capbuf, mask); if (ACPI_SUCCESS(status)) root->osc_control_set = *mask; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 164ba10ddcb3..d220d1465d9b 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -300,9 +300,10 @@ struct acpi_osc_context { struct acpi_buffer ret; /* free by caller if success */ }; -#define OSC_QUERY_TYPE 0 -#define OSC_SUPPORT_TYPE 1 -#define OSC_CONTROL_TYPE 2 +/* Indexes into _OSC Capabilities Buffer (DWORDs 2 & 3 are device-specific) */ +#define OSC_QUERY_DWORD 0 /* DWORD 1 */ +#define OSC_SUPPORT_DWORD 1 /* DWORD 2 */ +#define OSC_CONTROL_DWORD 2 /* DWORD 3 */ /* _OSC Capabilities DWORD 1: Query/Control and Error Returns (generic) */ #define OSC_QUERY_ENABLE 0x00000001 /* input */ From c8678473609b0271ffa0963af82e575312e882cb Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:05:55 -0600 Subject: [PATCH 09/79] ACPI: Tidy acpi_run_osc() declarations Move the acpi_run_osc() prototype next to the related structure and update comments. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- include/linux/acpi.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d220d1465d9b..a2f501c0a4f4 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -294,12 +294,14 @@ void __init acpi_nvs_nosave_s3(void); #endif /* CONFIG_PM_SLEEP */ struct acpi_osc_context { - char *uuid_str; /* uuid string */ + char *uuid_str; /* UUID string */ int rev; - struct acpi_buffer cap; /* arg2/arg3 */ - struct acpi_buffer ret; /* free by caller if success */ + struct acpi_buffer cap; /* list of DWORD capabilities */ + struct acpi_buffer ret; /* free by caller if success */ }; +acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); + /* Indexes into _OSC Capabilities Buffer (DWORDs 2 & 3 are device-specific) */ #define OSC_QUERY_DWORD 0 /* DWORD 1 */ #define OSC_SUPPORT_DWORD 1 /* DWORD 2 */ @@ -312,8 +314,6 @@ struct acpi_osc_context { #define OSC_INVALID_REVISION_ERROR 0x00000008 /* return */ #define OSC_CAPABILITIES_MASK_ERROR 0x00000010 /* return */ -acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); - /* Platform-Wide Capabilities _OSC: Capabilities DWORD 2: Support Field */ #define OSC_SB_PAD_SUPPORT 0x00000001 #define OSC_SB_PPC_OST_SUPPORT 0x00000002 From b4481934167430d82ea9c857cf95276460e4b607 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:22:11 -0600 Subject: [PATCH 10/79] ACPI: Remove unused OSC_PCI_NATIVE_HOTPLUG OSC_PCI_NATIVE_HOTPLUG is completely unused, so remove it. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- include/linux/acpi.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index a2f501c0a4f4..ca072e3927a2 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -345,9 +345,6 @@ extern bool osc_sb_apei_support_acked; OSC_PCI_EXPRESS_AER_CONTROL | \ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL) -#define OSC_PCI_NATIVE_HOTPLUG (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \ - OSC_SHPC_NATIVE_HP_CONTROL) - extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req); From 335b15097d571007b125eb9fe4ef1f84e61bd31d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:24:24 -0600 Subject: [PATCH 11/79] ACPI: Write OSC_PCI_CONTROL_MASKS like OSC_PCI_SUPPORT_MASKS We write OSC_PCI_SUPPORT_MASKS as a simple 0x1f, so do the same for OSC_PCI_CONTROL_MASKS. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- include/linux/acpi.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index ca072e3927a2..bb4e7701b26b 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -338,12 +338,7 @@ extern bool osc_sb_apei_support_acked; #define OSC_PCI_EXPRESS_PME_CONTROL 0x00000004 #define OSC_PCI_EXPRESS_AER_CONTROL 0x00000008 #define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL 0x00000010 - -#define OSC_PCI_CONTROL_MASKS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \ - OSC_SHPC_NATIVE_HP_CONTROL | \ - OSC_PCI_EXPRESS_PME_CONTROL | \ - OSC_PCI_EXPRESS_AER_CONTROL | \ - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL) +#define OSC_PCI_CONTROL_MASKS 0x0000001f extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req); From 7dab9ef4f0823072a3c9afdb3b373c9f2f38848b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:07:39 -0600 Subject: [PATCH 12/79] PCI/ACPI: Name _OSC #defines more consistently Make PCI Host Bridge _OSC #defines more consistent. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 19 +++++++++---------- drivers/pci/hotplug/acpi_pcihp.c | 2 +- drivers/pci/hotplug/shpchp.h | 2 +- include/linux/acpi.h | 12 ++++++------ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 323afd7ccfbf..28dd55509789 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -49,10 +49,10 @@ static int acpi_pci_root_add(struct acpi_device *device, const struct acpi_device_id *not_used); static void acpi_pci_root_remove(struct acpi_device *device); -#define ACPI_PCIE_REQ_SUPPORT (OSC_EXT_PCI_CONFIG_SUPPORT \ - | OSC_ACTIVE_STATE_PWR_SUPPORT \ - | OSC_CLOCK_PWR_CAPABILITY_SUPPORT \ - | OSC_MSI_SUPPORT) +#define ACPI_PCIE_REQ_SUPPORT (OSC_PCI_EXT_CONFIG_SUPPORT \ + | OSC_PCI_ASPM_SUPPORT \ + | OSC_PCI_CLOCK_PM_SUPPORT \ + | OSC_PCI_MSI_SUPPORT) static const struct acpi_device_id root_device_ids[] = { {"PNP0A03", 0}, @@ -439,13 +439,12 @@ static int acpi_pci_root_add(struct acpi_device *device, acpi_pci_osc_support(root, flags); if (pci_ext_cfg_avail()) - flags |= OSC_EXT_PCI_CONFIG_SUPPORT; + flags |= OSC_PCI_EXT_CONFIG_SUPPORT; if (pcie_aspm_support_enabled()) { - flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | - OSC_CLOCK_PWR_CAPABILITY_SUPPORT; + flags |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT; } if (pci_msi_enabled()) - flags |= OSC_MSI_SUPPORT; + flags |= OSC_PCI_MSI_SUPPORT; if (flags != base_flags) { status = acpi_pci_osc_support(root, flags); if (ACPI_FAILURE(status)) { @@ -458,7 +457,7 @@ static int acpi_pci_root_add(struct acpi_device *device, if (!pcie_ports_disabled && (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { - flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL + flags = OSC_PCI_EXPRESS_CAPABILITY_CONTROL | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | OSC_PCI_EXPRESS_PME_CONTROL; @@ -474,7 +473,7 @@ static int acpi_pci_root_add(struct acpi_device *device, "Requesting ACPI _OSC control (0x%02x)\n", flags); status = acpi_pci_osc_control_set(handle, &flags, - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); + OSC_PCI_EXPRESS_CAPABILITY_CONTROL); if (ACPI_SUCCESS(status)) { dev_info(&device->dev, "ACPI _OSC control (0x%02x) granted\n", flags); diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 2a47e82821da..f8140164ec0b 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -338,7 +338,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags) acpi_handle chandle, handle; struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; - flags &= OSC_SHPC_NATIVE_HP_CONTROL; + flags &= OSC_PCI_SHPC_NATIVE_HP_CONTROL; if (!flags) { err("Invalid flags %u specified!\n", flags); return -EINVAL; diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index e260f207a90e..d876e4b3c6a9 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -191,7 +191,7 @@ static inline const char *slot_name(struct slot *slot) #include static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) { - u32 flags = OSC_SHPC_NATIVE_HP_CONTROL; + u32 flags = OSC_PCI_SHPC_NATIVE_HP_CONTROL; return acpi_get_hp_hw_control_from_firmware(dev, flags); } #else diff --git a/include/linux/acpi.h b/include/linux/acpi.h index bb4e7701b26b..e2e52cf53224 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -325,19 +325,19 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); extern bool osc_sb_apei_support_acked; /* PCI Host Bridge _OSC: Capabilities DWORD 2: Support Field */ -#define OSC_EXT_PCI_CONFIG_SUPPORT 0x00000001 -#define OSC_ACTIVE_STATE_PWR_SUPPORT 0x00000002 -#define OSC_CLOCK_PWR_CAPABILITY_SUPPORT 0x00000004 +#define OSC_PCI_EXT_CONFIG_SUPPORT 0x00000001 +#define OSC_PCI_ASPM_SUPPORT 0x00000002 +#define OSC_PCI_CLOCK_PM_SUPPORT 0x00000004 #define OSC_PCI_SEGMENT_GROUPS_SUPPORT 0x00000008 -#define OSC_MSI_SUPPORT 0x00000010 +#define OSC_PCI_MSI_SUPPORT 0x00000010 #define OSC_PCI_SUPPORT_MASKS 0x0000001f /* PCI Host Bridge _OSC: Capabilities DWORD 3: Control Field */ #define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 0x00000001 -#define OSC_SHPC_NATIVE_HP_CONTROL 0x00000002 +#define OSC_PCI_SHPC_NATIVE_HP_CONTROL 0x00000002 #define OSC_PCI_EXPRESS_PME_CONTROL 0x00000004 #define OSC_PCI_EXPRESS_AER_CONTROL 0x00000008 -#define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL 0x00000010 +#define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010 #define OSC_PCI_CONTROL_MASKS 0x0000001f extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, From 4ffe6e54b0ce259d254e7740a7a4a99dad14a484 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:07:41 -0600 Subject: [PATCH 13/79] PCI/ACPI: Drop unnecessary _OSC existence tests There's no need to check whether _OSC exists here; we eventually call acpi_evaluate_object(..., "_OSC", ...), and that will fail gracefully if _OSC doesn't exist. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 28dd55509789..cc87cc4fea0e 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -180,11 +180,7 @@ static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags) { acpi_status status; - acpi_handle tmp; - status = acpi_get_handle(root->device->handle, "_OSC", &tmp); - if (ACPI_FAILURE(status)) - return status; mutex_lock(&osc_lock); status = acpi_pci_query_osc(root, flags, NULL); mutex_unlock(&osc_lock); @@ -316,9 +312,8 @@ EXPORT_SYMBOL_GPL(acpi_get_pci_dev); acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req) { struct acpi_pci_root *root; - acpi_status status; + acpi_status status = AE_OK; u32 ctrl, capbuf[3]; - acpi_handle tmp; if (!mask) return AE_BAD_PARAMETER; @@ -331,10 +326,6 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req) if (!root) return AE_NOT_EXIST; - status = acpi_get_handle(handle, "_OSC", &tmp); - if (ACPI_FAILURE(status)) - return status; - mutex_lock(&osc_lock); *mask = ctrl | root->osc_control_set; From 3e43abb012d45dc284ef9a0fb0cea0fb004b5607 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:07:41 -0600 Subject: [PATCH 14/79] PCI/ACPI: Move _OSC stuff from acpi_pci_root_add() to negotiate_os_control() This doesn't change any of the _OSC code; it just moves it out into a new function so it doesn't clutter acpi_pci_root_add() so much. This also enables future simplifications. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 162 +++++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index cc87cc4fea0e..3e57f104e09a 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -360,6 +360,90 @@ out: } EXPORT_SYMBOL(acpi_pci_osc_control_set); +static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, + int *clear_aspm) +{ + u32 flags, base_flags; + acpi_status status; + struct acpi_device *device = root->device; + acpi_handle handle = device->handle; + + /* + * All supported architectures that use ACPI have support for + * PCI domains, so we indicate this in _OSC support capabilities. + */ + flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + acpi_pci_osc_support(root, flags); + + if (pci_ext_cfg_avail()) + flags |= OSC_PCI_EXT_CONFIG_SUPPORT; + if (pcie_aspm_support_enabled()) { + flags |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT; + } + if (pci_msi_enabled()) + flags |= OSC_PCI_MSI_SUPPORT; + if (flags != base_flags) { + status = acpi_pci_osc_support(root, flags); + if (ACPI_FAILURE(status)) { + dev_info(&device->dev, "ACPI _OSC support " + "notification failed, disabling PCIe ASPM\n"); + *no_aspm = 1; + flags = base_flags; + } + } + + if (!pcie_ports_disabled + && (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { + flags = OSC_PCI_EXPRESS_CAPABILITY_CONTROL + | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL + | OSC_PCI_EXPRESS_PME_CONTROL; + + if (pci_aer_available()) { + if (aer_acpi_firmware_first()) + dev_dbg(&device->dev, + "PCIe errors handled by BIOS.\n"); + else + flags |= OSC_PCI_EXPRESS_AER_CONTROL; + } + + dev_info(&device->dev, + "Requesting ACPI _OSC control (0x%02x)\n", flags); + + status = acpi_pci_osc_control_set(handle, &flags, + OSC_PCI_EXPRESS_CAPABILITY_CONTROL); + if (ACPI_SUCCESS(status)) { + dev_info(&device->dev, + "ACPI _OSC control (0x%02x) granted\n", flags); + if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { + /* + * We have ASPM control, but the FADT indicates + * that it's unsupported. Clear it. + */ + *clear_aspm = 1; + } + } else { + dev_info(&device->dev, + "ACPI _OSC request failed (%s), " + "returned control mask: 0x%02x\n", + acpi_format_exception(status), flags); + dev_info(&device->dev, + "ACPI _OSC control for PCIe not granted, disabling ASPM\n"); + /* + * We want to disable ASPM here, but aspm_disabled + * needs to remain in its state from boot so that we + * properly handle PCIe 1.1 devices. So we set this + * flag here, to defer the action until after the ACPI + * root scan. + */ + *no_aspm = 1; + } + } else { + dev_info(&device->dev, + "Unable to request _OSC control " + "(_OSC support mask: 0x%02x)\n", flags); + } +} + static int acpi_pci_root_add(struct acpi_device *device, const struct acpi_device_id *not_used) { @@ -367,9 +451,8 @@ static int acpi_pci_root_add(struct acpi_device *device, acpi_status status; int result; struct acpi_pci_root *root; - u32 flags, base_flags; acpi_handle handle = device->handle; - bool no_aspm = false, clear_aspm = false; + int no_aspm = 0, clear_aspm = 0; root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); if (!root) @@ -422,80 +505,7 @@ static int acpi_pci_root_add(struct acpi_device *device, root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle); - /* - * All supported architectures that use ACPI have support for - * PCI domains, so we indicate this in _OSC support capabilities. - */ - flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; - acpi_pci_osc_support(root, flags); - - if (pci_ext_cfg_avail()) - flags |= OSC_PCI_EXT_CONFIG_SUPPORT; - if (pcie_aspm_support_enabled()) { - flags |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT; - } - if (pci_msi_enabled()) - flags |= OSC_PCI_MSI_SUPPORT; - if (flags != base_flags) { - status = acpi_pci_osc_support(root, flags); - if (ACPI_FAILURE(status)) { - dev_info(&device->dev, "ACPI _OSC support " - "notification failed, disabling PCIe ASPM\n"); - no_aspm = true; - flags = base_flags; - } - } - - if (!pcie_ports_disabled - && (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { - flags = OSC_PCI_EXPRESS_CAPABILITY_CONTROL - | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL - | OSC_PCI_EXPRESS_PME_CONTROL; - - if (pci_aer_available()) { - if (aer_acpi_firmware_first()) - dev_dbg(&device->dev, - "PCIe errors handled by BIOS.\n"); - else - flags |= OSC_PCI_EXPRESS_AER_CONTROL; - } - - dev_info(&device->dev, - "Requesting ACPI _OSC control (0x%02x)\n", flags); - - status = acpi_pci_osc_control_set(handle, &flags, - OSC_PCI_EXPRESS_CAPABILITY_CONTROL); - if (ACPI_SUCCESS(status)) { - dev_info(&device->dev, - "ACPI _OSC control (0x%02x) granted\n", flags); - if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { - /* - * We have ASPM control, but the FADT indicates - * that it's unsupported. Clear it. - */ - clear_aspm = true; - } - } else { - dev_info(&device->dev, - "ACPI _OSC request failed (%s), " - "returned control mask: 0x%02x\n", - acpi_format_exception(status), flags); - dev_info(&device->dev, - "ACPI _OSC control for PCIe not granted, disabling ASPM\n"); - /* - * We want to disable ASPM here, but aspm_disabled - * needs to remain in its state from boot so that we - * properly handle PCIe 1.1 devices. So we set this - * flag here, to defer the action until after the ACPI - * root scan. - */ - no_aspm = true; - } - } else { - dev_info(&device->dev, - "Unable to request _OSC control " - "(_OSC support mask: 0x%02x)\n", flags); - } + negotiate_os_control(root, &no_aspm, &clear_aspm); /* * TBD: Need PCI interface for enumeration/configuration of roots. From b8eb67fcc4548b9f0d00bbfd1c0c8f72d8018900 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:07:42 -0600 Subject: [PATCH 15/79] PCI/ACPI: Split _OSC "support" and "control" flags into separate variables Previously we used "flags" for both: - the bitmask of features we support (segments, ASPM, MSI, etc.), and - the bitmask of features we want to control (native hotplug, AER, etc.) To reduce confusion, this patch splits this into two variables: "support" is the bitmask of features we support, and "control" is the bitmask of features we want to control. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 3e57f104e09a..3e06d4e179ec 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -363,7 +363,7 @@ EXPORT_SYMBOL(acpi_pci_osc_control_set); static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, int *clear_aspm) { - u32 flags, base_flags; + u32 support, base_support, control; acpi_status status; struct acpi_device *device = root->device; acpi_handle handle = device->handle; @@ -372,29 +372,29 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. */ - flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; - acpi_pci_osc_support(root, flags); + support = base_support = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + acpi_pci_osc_support(root, support); if (pci_ext_cfg_avail()) - flags |= OSC_PCI_EXT_CONFIG_SUPPORT; + support |= OSC_PCI_EXT_CONFIG_SUPPORT; if (pcie_aspm_support_enabled()) { - flags |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT; + support |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT; } if (pci_msi_enabled()) - flags |= OSC_PCI_MSI_SUPPORT; - if (flags != base_flags) { - status = acpi_pci_osc_support(root, flags); + support |= OSC_PCI_MSI_SUPPORT; + if (support != base_support) { + status = acpi_pci_osc_support(root, support); if (ACPI_FAILURE(status)) { dev_info(&device->dev, "ACPI _OSC support " "notification failed, disabling PCIe ASPM\n"); *no_aspm = 1; - flags = base_flags; + support = base_support; } } if (!pcie_ports_disabled - && (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { - flags = OSC_PCI_EXPRESS_CAPABILITY_CONTROL + && (support & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { + control = OSC_PCI_EXPRESS_CAPABILITY_CONTROL | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | OSC_PCI_EXPRESS_PME_CONTROL; @@ -403,17 +403,18 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, dev_dbg(&device->dev, "PCIe errors handled by BIOS.\n"); else - flags |= OSC_PCI_EXPRESS_AER_CONTROL; + control |= OSC_PCI_EXPRESS_AER_CONTROL; } dev_info(&device->dev, - "Requesting ACPI _OSC control (0x%02x)\n", flags); + "Requesting ACPI _OSC control (0x%02x)\n", control); - status = acpi_pci_osc_control_set(handle, &flags, + status = acpi_pci_osc_control_set(handle, &control, OSC_PCI_EXPRESS_CAPABILITY_CONTROL); if (ACPI_SUCCESS(status)) { dev_info(&device->dev, - "ACPI _OSC control (0x%02x) granted\n", flags); + "ACPI _OSC control (0x%02x) granted\n", + control); if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { /* * We have ASPM control, but the FADT indicates @@ -425,7 +426,7 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, dev_info(&device->dev, "ACPI _OSC request failed (%s), " "returned control mask: 0x%02x\n", - acpi_format_exception(status), flags); + acpi_format_exception(status), control); dev_info(&device->dev, "ACPI _OSC control for PCIe not granted, disabling ASPM\n"); /* @@ -440,7 +441,7 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, } else { dev_info(&device->dev, "Unable to request _OSC control " - "(_OSC support mask: 0x%02x)\n", flags); + "(_OSC support mask: 0x%02x)\n", support); } } From 1b2a7be60eaaf17bc88368d748471dd70e40befd Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:07:42 -0600 Subject: [PATCH 16/79] PCI/ACPI: Run _OSC only once for OSPM feature support Previously, we ran _OSC once to tell the platform that we support PCI Segment Groups, then we ran it again if we supported any additional features (ASPM, MSI, or extended config space). I don't think it's necessary to run it twice, since we can easily build the complete mask of features we support before running _OSC the first time. We run _OSC again later when requesting control of PCIe features; that's unaffected by this change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 3e06d4e179ec..0e2004100c44 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -373,23 +373,18 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, * PCI domains, so we indicate this in _OSC support capabilities. */ support = base_support = OSC_PCI_SEGMENT_GROUPS_SUPPORT; - acpi_pci_osc_support(root, support); - if (pci_ext_cfg_avail()) support |= OSC_PCI_EXT_CONFIG_SUPPORT; - if (pcie_aspm_support_enabled()) { + if (pcie_aspm_support_enabled()) support |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT; - } if (pci_msi_enabled()) support |= OSC_PCI_MSI_SUPPORT; - if (support != base_support) { - status = acpi_pci_osc_support(root, support); - if (ACPI_FAILURE(status)) { - dev_info(&device->dev, "ACPI _OSC support " - "notification failed, disabling PCIe ASPM\n"); - *no_aspm = 1; - support = base_support; - } + status = acpi_pci_osc_support(root, support); + if (ACPI_FAILURE(status)) { + dev_info(&device->dev, "ACPI _OSC support " + "notification failed, disabling PCIe ASPM\n"); + *no_aspm = 1; + support = base_support; } if (!pcie_ports_disabled From 65afe91622c456560e20d57a779b807c20822e81 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:07:43 -0600 Subject: [PATCH 17/79] PCI/ACPI: Skip _OSC control tests if _OSC support call failed If the _OSC support notification fails, we will never request control (because "support == OSC_PCI_SEGMENT_GROUPS_SUPPORT", which doesn't include all the features in ACPI_PCIE_REQ_SUPPORT), so we can return early to simplify the code. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 0e2004100c44..67cc43a134ad 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -363,7 +363,7 @@ EXPORT_SYMBOL(acpi_pci_osc_control_set); static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, int *clear_aspm) { - u32 support, base_support, control; + u32 support, control; acpi_status status; struct acpi_device *device = root->device; acpi_handle handle = device->handle; @@ -372,7 +372,7 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. */ - support = base_support = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + support = OSC_PCI_SEGMENT_GROUPS_SUPPORT; if (pci_ext_cfg_avail()) support |= OSC_PCI_EXT_CONFIG_SUPPORT; if (pcie_aspm_support_enabled()) @@ -381,10 +381,10 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, support |= OSC_PCI_MSI_SUPPORT; status = acpi_pci_osc_support(root, support); if (ACPI_FAILURE(status)) { - dev_info(&device->dev, "ACPI _OSC support " - "notification failed, disabling PCIe ASPM\n"); + dev_info(&device->dev, "_OSC failed (%s); disabling ASPM\n", + acpi_format_exception(status)); *no_aspm = 1; - support = base_support; + return; } if (!pcie_ports_disabled From 43613a1fac0eec78de14fecc9de184e1052abac7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:07:43 -0600 Subject: [PATCH 18/79] PCI/ACPI: Separate out _OSC "PCIe port services disabled" path Test "pcie_ports_disabled" separately so we can give a better message. Previously we said "Unable to request _OSC control..."; now we'll say "PCIe port services disabled; not requesting _OSC control". "pcie_ports_disabled" is true when CONFIG_PCIEPORTBUS=n or we boot with "pcie_ports=compat". Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 67cc43a134ad..68e5a187e784 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -387,8 +387,12 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, return; } - if (!pcie_ports_disabled - && (support & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { + if (pcie_ports_disabled) { + dev_info(&device->dev, "PCIe port services disabled; not requesting _OSC control\n"); + return; + } + + if ((support & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { control = OSC_PCI_EXPRESS_CAPABILITY_CONTROL | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | OSC_PCI_EXPRESS_PME_CONTROL; From de18966228ed4b42393ecbe83ba20ff3db78bfdc Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:50:52 -0600 Subject: [PATCH 19/79] PCI/ACPI: Separate out _OSC "we don't support enough services" path Test the services we support (extended config space, ASPM, MSI) separately so we can give a better message. Previously we said "Unable to request _OSC control..."; now we'll say "we support %#02x but %#02x are required". Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 82 ++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 68e5a187e784..65aefcf78552 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -392,55 +392,55 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, return; } - if ((support & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) { - control = OSC_PCI_EXPRESS_CAPABILITY_CONTROL - | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL - | OSC_PCI_EXPRESS_PME_CONTROL; + if ((support & ACPI_PCIE_REQ_SUPPORT) != ACPI_PCIE_REQ_SUPPORT) { + dev_info(&device->dev, "Not requesting _OSC control (we support %#02x but %#02x are required)\n", + support, ACPI_PCIE_REQ_SUPPORT); + return; + } - if (pci_aer_available()) { - if (aer_acpi_firmware_first()) - dev_dbg(&device->dev, - "PCIe errors handled by BIOS.\n"); - else - control |= OSC_PCI_EXPRESS_AER_CONTROL; - } + control = OSC_PCI_EXPRESS_CAPABILITY_CONTROL + | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL + | OSC_PCI_EXPRESS_PME_CONTROL; + if (pci_aer_available()) { + if (aer_acpi_firmware_first()) + dev_dbg(&device->dev, + "PCIe errors handled by BIOS.\n"); + else + control |= OSC_PCI_EXPRESS_AER_CONTROL; + } + + dev_info(&device->dev, + "Requesting ACPI _OSC control (0x%02x)\n", control); + + status = acpi_pci_osc_control_set(handle, &control, + OSC_PCI_EXPRESS_CAPABILITY_CONTROL); + if (ACPI_SUCCESS(status)) { dev_info(&device->dev, - "Requesting ACPI _OSC control (0x%02x)\n", control); - - status = acpi_pci_osc_control_set(handle, &control, - OSC_PCI_EXPRESS_CAPABILITY_CONTROL); - if (ACPI_SUCCESS(status)) { - dev_info(&device->dev, - "ACPI _OSC control (0x%02x) granted\n", - control); - if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { - /* - * We have ASPM control, but the FADT indicates - * that it's unsupported. Clear it. - */ - *clear_aspm = 1; - } - } else { - dev_info(&device->dev, - "ACPI _OSC request failed (%s), " - "returned control mask: 0x%02x\n", - acpi_format_exception(status), control); - dev_info(&device->dev, - "ACPI _OSC control for PCIe not granted, disabling ASPM\n"); + "ACPI _OSC control (0x%02x) granted\n", + control); + if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { /* - * We want to disable ASPM here, but aspm_disabled - * needs to remain in its state from boot so that we - * properly handle PCIe 1.1 devices. So we set this - * flag here, to defer the action until after the ACPI - * root scan. + * We have ASPM control, but the FADT indicates + * that it's unsupported. Clear it. */ - *no_aspm = 1; + *clear_aspm = 1; } } else { dev_info(&device->dev, - "Unable to request _OSC control " - "(_OSC support mask: 0x%02x)\n", support); + "ACPI _OSC request failed (%s), " + "returned control mask: 0x%02x\n", + acpi_format_exception(status), control); + dev_info(&device->dev, + "ACPI _OSC control for PCIe not granted, disabling ASPM\n"); + /* + * We want to disable ASPM here, but aspm_disabled + * needs to remain in its state from boot so that we + * properly handle PCIe 1.1 devices. So we set this + * flag here, to defer the action until after the ACPI + * root scan. + */ + *no_aspm = 1; } } From 955f14b4ed0648da12f0a7011f94150b8982a5c2 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Sep 2013 15:07:45 -0600 Subject: [PATCH 20/79] PCI/ACPI: Decode _OSC bitmasks symbolically This updates _OSC-related messages to be more human-readable. We now always show the features we declare support for (this was previously invisible) as well as the features we are granted control of. Typical changes: -acpi PNP0A08:00: Requesting ACPI _OSC control (0x1d) -acpi PNP0A08:00: ACPI _OSC control (0x1d) granted +acpi PNP0A08:00: _OSC: OS supports [ExtendedConfig ASPM ClockPM Segments MSI] +acpi PNP0A08:00: _OSC: OS now controls [PCIeHotplug PME AER PCIeCapability] Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 84 ++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 65aefcf78552..924ad92852c1 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -127,6 +127,55 @@ static acpi_status try_get_root_bridge_busnr(acpi_handle handle, return AE_OK; } +struct pci_osc_bit_struct { + u32 bit; + char *desc; +}; + +static struct pci_osc_bit_struct pci_osc_support_bit[] = { + { OSC_PCI_EXT_CONFIG_SUPPORT, "ExtendedConfig" }, + { OSC_PCI_ASPM_SUPPORT, "ASPM" }, + { OSC_PCI_CLOCK_PM_SUPPORT, "ClockPM" }, + { OSC_PCI_SEGMENT_GROUPS_SUPPORT, "Segments" }, + { OSC_PCI_MSI_SUPPORT, "MSI" }, +}; + +static struct pci_osc_bit_struct pci_osc_control_bit[] = { + { OSC_PCI_EXPRESS_NATIVE_HP_CONTROL, "PCIeHotplug" }, + { OSC_PCI_SHPC_NATIVE_HP_CONTROL, "SHPCHotplug" }, + { OSC_PCI_EXPRESS_PME_CONTROL, "PME" }, + { OSC_PCI_EXPRESS_AER_CONTROL, "AER" }, + { OSC_PCI_EXPRESS_CAPABILITY_CONTROL, "PCIeCapability" }, +}; + +static void decode_osc_bits(struct acpi_pci_root *root, char *msg, u32 word, + struct pci_osc_bit_struct *table, int size) +{ + char buf[80]; + int i, len = 0; + struct pci_osc_bit_struct *entry; + + buf[0] = '\0'; + for (i = 0, entry = table; i < size; i++, entry++) + if (word & entry->bit) + len += snprintf(buf + len, sizeof(buf) - len, "%s%s", + len ? " " : "", entry->desc); + + dev_info(&root->device->dev, "_OSC: %s [%s]\n", msg, buf); +} + +static void decode_osc_support(struct acpi_pci_root *root, char *msg, u32 word) +{ + decode_osc_bits(root, msg, word, pci_osc_support_bit, + ARRAY_SIZE(pci_osc_support_bit)); +} + +static void decode_osc_control(struct acpi_pci_root *root, char *msg, u32 word) +{ + decode_osc_bits(root, msg, word, pci_osc_control_bit, + ARRAY_SIZE(pci_osc_control_bit)); +} + static u8 pci_osc_uuid_str[] = "33DB4D5B-1FF7-401C-9657-7441C03DD766"; static acpi_status acpi_pci_run_osc(acpi_handle handle, @@ -340,10 +389,14 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req) goto out; if (ctrl == *mask) break; + decode_osc_control(root, "platform does not support", + ctrl & ~(*mask)); ctrl = *mask; } if ((ctrl & req) != req) { + decode_osc_control(root, "not requesting control; platform does not support", + req & ~(ctrl)); status = AE_SUPPORT; goto out; } @@ -363,7 +416,7 @@ EXPORT_SYMBOL(acpi_pci_osc_control_set); static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, int *clear_aspm) { - u32 support, control; + u32 support, control, requested; acpi_status status; struct acpi_device *device = root->device; acpi_handle handle = device->handle; @@ -379,6 +432,8 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, support |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT; if (pci_msi_enabled()) support |= OSC_PCI_MSI_SUPPORT; + + decode_osc_support(root, "OS supports", support); status = acpi_pci_osc_support(root, support); if (ACPI_FAILURE(status)) { dev_info(&device->dev, "_OSC failed (%s); disabling ASPM\n", @@ -393,8 +448,8 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, } if ((support & ACPI_PCIE_REQ_SUPPORT) != ACPI_PCIE_REQ_SUPPORT) { - dev_info(&device->dev, "Not requesting _OSC control (we support %#02x but %#02x are required)\n", - support, ACPI_PCIE_REQ_SUPPORT); + decode_osc_support(root, "not requesting OS control; OS requires", + ACPI_PCIE_REQ_SUPPORT); return; } @@ -404,21 +459,17 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, if (pci_aer_available()) { if (aer_acpi_firmware_first()) - dev_dbg(&device->dev, - "PCIe errors handled by BIOS.\n"); + dev_info(&device->dev, + "PCIe AER handled by firmware\n"); else control |= OSC_PCI_EXPRESS_AER_CONTROL; } - dev_info(&device->dev, - "Requesting ACPI _OSC control (0x%02x)\n", control); - + requested = control; status = acpi_pci_osc_control_set(handle, &control, OSC_PCI_EXPRESS_CAPABILITY_CONTROL); if (ACPI_SUCCESS(status)) { - dev_info(&device->dev, - "ACPI _OSC control (0x%02x) granted\n", - control); + decode_osc_control(root, "OS now controls", control); if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { /* * We have ASPM control, but the FADT indicates @@ -427,12 +478,11 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, *clear_aspm = 1; } } else { - dev_info(&device->dev, - "ACPI _OSC request failed (%s), " - "returned control mask: 0x%02x\n", - acpi_format_exception(status), control); - dev_info(&device->dev, - "ACPI _OSC control for PCIe not granted, disabling ASPM\n"); + decode_osc_control(root, "OS requested", requested); + decode_osc_control(root, "platform willing to grant", control); + dev_info(&device->dev, "_OSC failed (%s); disabling ASPM\n", + acpi_format_exception(status)); + /* * We want to disable ASPM here, but aspm_disabled * needs to remain in its state from boot so that we From f1c66c4678ad223bda0dcd261e4048f009234f85 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 24 Sep 2013 12:08:06 -0600 Subject: [PATCH 21/79] PCI: Export pcie_set_mps() and pcie_get_mps() Export pcie_get_mps() and pcie_set_mps() functions so drivers can use them to simplify code. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e8ccf6c0f08a..8c812464f259 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3972,6 +3972,7 @@ int pcie_get_mps(struct pci_dev *dev) return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); } +EXPORT_SYMBOL(pcie_get_mps); /** * pcie_set_mps - set PCI Express maximum payload size @@ -3996,6 +3997,7 @@ int pcie_set_mps(struct pci_dev *dev, int mps) return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_PAYLOAD, v); } +EXPORT_SYMBOL(pcie_set_mps); /** * pcie_get_minimum_link - determine minimum link settings of a PCI device From 503275bf371fbab01a14f2e9fd1ac7aa20c81645 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 9 Sep 2013 21:13:04 +0800 Subject: [PATCH 22/79] tile/PCI: use cached pci_dev->pcie_mpss to simplify code The PCI core caches the "PCIe Max Payload Size Supported" in pci_dev->pcie_mpss, so use that instead of pcie_capability_read_dword(). Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- arch/tile/kernel/pci.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c index b7180e6e900d..c45593db7718 100644 --- a/arch/tile/kernel/pci.c +++ b/arch/tile/kernel/pci.c @@ -251,15 +251,12 @@ static void fixup_read_and_payload_sizes(void) /* Scan for the smallest maximum payload size. */ for_each_pci_dev(dev) { u32 devcap; - int max_payload; if (!pci_is_pcie(dev)) continue; - pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &devcap); - max_payload = devcap & PCI_EXP_DEVCAP_PAYLOAD; - if (max_payload < smallest_max_payload) - smallest_max_payload = max_payload; + if (dev->pcie_mpss < smallest_max_payload) + smallest_max_payload = dev->pcie_mpss; } /* Now, set the max_payload_size for all devices to that value. */ From dcaa73dc3411c4d771d1d6a950c6b8a9c71da089 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 9 Sep 2013 21:13:05 +0800 Subject: [PATCH 23/79] IB/qib: Use pci_is_root_bus() to check whether it is a root bus Use pci_is_root_bus() instead of "if (bus->parent)" statement for better readability. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Mike Marciniszyn --- drivers/infiniband/hw/qib/qib_pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c index 3f14009fb662..45e55ff33dbf 100644 --- a/drivers/infiniband/hw/qib/qib_pcie.c +++ b/drivers/infiniband/hw/qib/qib_pcie.c @@ -590,7 +590,7 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd) /* Find out supported and configured values for parent (root) */ parent = dd->pcidev->bus->self; - if (parent->bus->parent) { + if (!pci_is_root_bus(parent->bus)) { qib_devinfo(dd->pcidev, "Parent not root\n"); goto bail; } From 0ce0e62f1f7893f983a8f61bc8f5306e80d520b1 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 9 Sep 2013 21:13:06 +0800 Subject: [PATCH 24/79] IB/qib: Use pcie_set_mps() and pcie_get_mps() to simplify code Refactor qib_tune_pcie_caps(). Use pcie_get_mps(), pcie_set_mps(), pcie_get_readrq(), and pcie_set_readrq() to simplify the code. The PCI core caches the "PCIe Max Payload Size Supported" in pci_dev->pcie_mpss, so use that instead of pcie_capability_read_word(). Remove the unused val2fld() and fld2val(). Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Mike Marciniszyn --- drivers/infiniband/hw/qib/qib_pcie.c | 94 +++++++++------------------- 1 file changed, 29 insertions(+), 65 deletions(-) diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c index 45e55ff33dbf..24973c8b8443 100644 --- a/drivers/infiniband/hw/qib/qib_pcie.c +++ b/drivers/infiniband/hw/qib/qib_pcie.c @@ -476,30 +476,6 @@ void qib_pcie_reenable(struct qib_devdata *dd, u16 cmd, u8 iline, u8 cline) "pci_enable_device failed after reset: %d\n", r); } -/* code to adjust PCIe capabilities. */ - -static int fld2val(int wd, int mask) -{ - int lsbmask; - - if (!mask) - return 0; - wd &= mask; - lsbmask = mask ^ (mask & (mask - 1)); - wd /= lsbmask; - return wd; -} - -static int val2fld(int wd, int mask) -{ - int lsbmask; - - if (!mask) - return 0; - lsbmask = mask ^ (mask & (mask - 1)); - wd *= lsbmask; - return wd; -} static int qib_pcie_coalesce; module_param_named(pcie_coalesce, qib_pcie_coalesce, int, S_IRUGO); @@ -584,9 +560,8 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd) { int ret = 1; /* Assume the worst */ struct pci_dev *parent; - u16 pcaps, pctl, ecaps, ectl; - int rc_sup, ep_sup; - int rc_cur, ep_cur; + u16 rc_mpss, rc_mps, ep_mpss, ep_mps; + u16 rc_mrrs, ep_mrrs, max_mrrs; /* Find out supported and configured values for parent (root) */ parent = dd->pcidev->bus->self; @@ -597,38 +572,29 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd) if (!pci_is_pcie(parent) || !pci_is_pcie(dd->pcidev)) goto bail; - pcie_capability_read_word(parent, PCI_EXP_DEVCAP, &pcaps); - pcie_capability_read_word(parent, PCI_EXP_DEVCTL, &pctl); + rc_mpss = parent->pcie_mpss; + rc_mps = ffs(pcie_get_mps(parent)) - 8; /* Find out supported and configured values for endpoint (us) */ - pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCAP, &ecaps); - pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCTL, &ectl); + ep_mpss = dd->pcidev->pcie_mpss; + ep_mps = ffs(pcie_get_mps(dd->pcidev)) - 8; ret = 0; /* Find max payload supported by root, endpoint */ - rc_sup = fld2val(pcaps, PCI_EXP_DEVCAP_PAYLOAD); - ep_sup = fld2val(ecaps, PCI_EXP_DEVCAP_PAYLOAD); - if (rc_sup > ep_sup) - rc_sup = ep_sup; - - rc_cur = fld2val(pctl, PCI_EXP_DEVCTL_PAYLOAD); - ep_cur = fld2val(ectl, PCI_EXP_DEVCTL_PAYLOAD); + if (rc_mpss > ep_mpss) + rc_mpss = ep_mpss; /* If Supported greater than limit in module param, limit it */ - if (rc_sup > (qib_pcie_caps & 7)) - rc_sup = qib_pcie_caps & 7; + if (rc_mpss > (qib_pcie_caps & 7)) + rc_mpss = qib_pcie_caps & 7; /* If less than (allowed, supported), bump root payload */ - if (rc_sup > rc_cur) { - rc_cur = rc_sup; - pctl = (pctl & ~PCI_EXP_DEVCTL_PAYLOAD) | - val2fld(rc_cur, PCI_EXP_DEVCTL_PAYLOAD); - pcie_capability_write_word(parent, PCI_EXP_DEVCTL, pctl); + if (rc_mpss > rc_mps) { + rc_mps = rc_mpss; + pcie_set_mps(parent, 128 << rc_mps); } /* If less than (allowed, supported), bump endpoint payload */ - if (rc_sup > ep_cur) { - ep_cur = rc_sup; - ectl = (ectl & ~PCI_EXP_DEVCTL_PAYLOAD) | - val2fld(ep_cur, PCI_EXP_DEVCTL_PAYLOAD); - pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL, ectl); + if (rc_mpss > ep_mps) { + ep_mps = rc_mpss; + pcie_set_mps(dd->pcidev, 128 << ep_mps); } /* @@ -636,23 +602,21 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd) * No field for max supported, but PCIe spec limits it to 4096, * which is code '5' (log2(4096) - 7) */ - rc_sup = 5; - if (rc_sup > ((qib_pcie_caps >> 4) & 7)) - rc_sup = (qib_pcie_caps >> 4) & 7; - rc_cur = fld2val(pctl, PCI_EXP_DEVCTL_READRQ); - ep_cur = fld2val(ectl, PCI_EXP_DEVCTL_READRQ); + max_mrrs = 5; + if (max_mrrs > ((qib_pcie_caps >> 4) & 7)) + max_mrrs = (qib_pcie_caps >> 4) & 7; - if (rc_sup > rc_cur) { - rc_cur = rc_sup; - pctl = (pctl & ~PCI_EXP_DEVCTL_READRQ) | - val2fld(rc_cur, PCI_EXP_DEVCTL_READRQ); - pcie_capability_write_word(parent, PCI_EXP_DEVCTL, pctl); + max_mrrs = 128 << max_mrrs; + rc_mrrs = pcie_get_readrq(parent); + ep_mrrs = pcie_get_readrq(dd->pcidev); + + if (max_mrrs > rc_mrrs) { + rc_mrrs = max_mrrs; + pcie_set_readrq(parent, rc_mrrs); } - if (rc_sup > ep_cur) { - ep_cur = rc_sup; - ectl = (ectl & ~PCI_EXP_DEVCTL_READRQ) | - val2fld(ep_cur, PCI_EXP_DEVCTL_READRQ); - pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL, ectl); + if (max_mrrs > ep_mrrs) { + ep_mrrs = max_mrrs; + pcie_set_readrq(dd->pcidev, ep_mrrs); } bail: return ret; From a006482b67a96c16dfefc558e36863c51e1829bf Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 23 Sep 2013 15:25:26 -0600 Subject: [PATCH 25/79] PCI: Drop "setting latency timer" messages This message isn't useful any more, so drop it. Reference: https://bugzilla.kernel.org/show_bug.cgi?id=60636 Reported-by: Oleksil Shevchuk Reference: http://lkml.kernel.org/r/CALCETrWkr53ZjqdN3t7rTTfr=+ZKZXJoYsuBcwPf0kN_33GfAw@mail.gmail.com Reported-by: Andy Lutomirski Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e8ccf6c0f08a..ad7fc72a40a0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2854,7 +2854,7 @@ void __weak pcibios_set_master(struct pci_dev *dev) lat = pcibios_max_latency; else return; - dev_printk(KERN_DEBUG, &dev->dev, "setting latency timer to %d\n", lat); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); } From a895c28a1f5d761679cb229b19fb08ca2067211e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 3 Sep 2013 13:31:05 -0700 Subject: [PATCH 26/79] PCI: Fix comment typo, remove unnecessary !! in pci_is_pcie() Use normal kernel-doc "Returns:" instead of "retrun" Assignment to bool is always 1 or 0 so the !! isn't necessary. Signed-off-by: Joe Perches Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/pci.h b/include/linux/pci.h index da172f956ad6..ee0f3b60af22 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1749,11 +1749,11 @@ static inline int pci_pcie_cap(struct pci_dev *dev) * pci_is_pcie - check if the PCI device is PCI Express capable * @dev: PCI device * - * Retrun true if the PCI device is PCI Express capable, false otherwise. + * Returns: true if the PCI device is PCI Express capable, false otherwise. */ static inline bool pci_is_pcie(struct pci_dev *dev) { - return !!pci_pcie_cap(dev); + return pci_pcie_cap(dev); } /** From 62a276f81fca41778abd8cb6d720a36302a89473 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 6 Sep 2013 11:26:24 -0600 Subject: [PATCH 27/79] [SCSI] qla2xxx: Use standard PCIe Capability Link register field names Use the standard #defines for PCIe Link Capability register fields rather than bare numbers. This also uses the new PCI Express Capability accessor rather than reading the capability directly. Signed-off-by: Bjorn Helgaas Acked-by: Saurav Kashyap Acked-by: Giridhar Malavali --- drivers/scsi/qla2xxx/qla_os.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 9f01bbbf3a26..bcd57f699ebb 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -494,18 +494,14 @@ qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str) static char *pci_bus_modes[] = { "33", "66", "100", "133", }; struct qla_hw_data *ha = vha->hw; uint32_t pci_bus; - int pcie_reg; - pcie_reg = pci_pcie_cap(ha->pdev); - if (pcie_reg) { + if (pci_is_pcie(ha->pdev)) { char lwstr[6]; - uint16_t pcie_lstat, lspeed, lwidth; + uint32_t lstat, lspeed, lwidth; - pcie_reg += PCI_EXP_LNKCAP; - pci_read_config_word(ha->pdev, pcie_reg, &pcie_lstat); - lspeed = pcie_lstat & (BIT_0 | BIT_1 | BIT_2 | BIT_3); - lwidth = (pcie_lstat & - (BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_8 | BIT_9)) >> 4; + pcie_capability_read_dword(ha->pdev, PCI_EXP_LNKCAP, &lstat); + lspeed = lstat & PCI_EXP_LNKCAP_SLS; + lwidth = (lstat & PCI_EXP_LNKCAP_MLW) >> 4; strcpy(str, "PCIe ("); switch (lspeed) { From a37bee7956ab34785815aafbe9f920c2cfd02b97 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 2 Sep 2013 14:34:40 +0800 Subject: [PATCH 28/79] PCI: Update pci_find_slot() description in pci.txt pci_find_slot() has been removed from the kernel. Document its closest replacement, pci_get_domain_bus_and_slot(), instead. [bhelgaas: fix indentation, keep mention in "obsolete" section] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- Documentation/PCI/pci.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Documentation/PCI/pci.txt b/Documentation/PCI/pci.txt index bccf602a87f5..6f458564d625 100644 --- a/Documentation/PCI/pci.txt +++ b/Documentation/PCI/pci.txt @@ -525,8 +525,9 @@ corresponding register block for you. 6. Other interesting functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -pci_find_slot() Find pci_dev corresponding to given bus and - slot numbers. +pci_get_domain_bus_and_slot() Find pci_dev corresponding to given domain, + bus and slot and number. If the device is + found, its reference count is increased. pci_set_power_state() Set PCI Power Management state (0=D0 ... 3=D3) pci_find_capability() Find specified capability in device's capability list. @@ -582,7 +583,8 @@ having sane locking. pci_find_device() Superseded by pci_get_device() pci_find_subsys() Superseded by pci_get_subsys() -pci_find_slot() Superseded by pci_get_slot() +pci_find_slot() Superseded by pci_get_domain_bus_and_slot() +pci_get_slot() Superseded by pci_get_domain_bus_and_slot() The alternative is the traditional PCI device driver that walks PCI From 115e3bc5e23e7ec3c85a2014bfa96c0ddd036083 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 3 Sep 2013 10:02:09 +0800 Subject: [PATCH 29/79] PCI: Remove unused "is_pcie" from pci_dev structure No one uses "is_pcie" now; remove this obsolete member. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 1 - include/linux/pci.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7ef0f868b3e0..2372babe2fc5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -984,7 +984,6 @@ void set_pcie_port_type(struct pci_dev *pdev) pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); if (!pos) return; - pdev->is_pcie = 1; pdev->pcie_cap = pos; pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); pdev->pcie_flags_reg = reg16; diff --git a/include/linux/pci.h b/include/linux/pci.h index ee0f3b60af22..197e2d21f2cb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -330,8 +330,6 @@ struct pci_dev { unsigned int msix_enabled:1; unsigned int ari_enabled:1; /* ARI forwarding */ unsigned int is_managed:1; - unsigned int is_pcie:1; /* Obsolete. Will be removed. - Use pci_is_pcie() instead */ unsigned int needs_freset:1; /* Dev requires fundamental reset */ unsigned int state_saved:1; unsigned int is_physfn:1; From 2ba29e270e977b213a7d58ae1152c23a1c3074a3 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 6 Sep 2013 09:45:56 +0800 Subject: [PATCH 30/79] PCI: Use pci_is_root_bus() to check for root bus In __pci_bus_size_bridges() we check whether a bus is a root bus by testing bus->self. As indicated by commit 79af72d7 ("PCI: pci_is_root_bus helper"), bus->self == NULL is not a proper way to check for a root bus. One issue is that "virtual" buses added for SR-IOV (via virtfn_add_bus()) have bus->self == NULL but are not root buses. This patch changes it to pci_is_root_bus() to check whether it is a root bus. [bhelgaas: changelog] Signed-off-by: Wei Yang Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index bc26d7990cc3..8df686c48bd1 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1136,7 +1136,7 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, } /* The root bus? */ - if (!bus->self) + if (pci_is_root_bus(bus)) return; switch (bus->self->class >> 8) { From 77a0dfcd8a4d9a93a21c8be56eaeb65cd69b351d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 24 Sep 2013 15:14:57 -0600 Subject: [PATCH 31/79] PCI: Document reason for using pci_is_root_bus() Some code assumes "bus->self == NULL" means the bus is a root bus. This adds a comment explaining why this is incorrect ("virtual" buses added for SR-IOV have "bus->self == NULL" but are not root buses). No functional change. Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/pci.h b/include/linux/pci.h index 197e2d21f2cb..d3a888ae4b2e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -470,6 +470,10 @@ struct pci_bus { /* * Returns true if the pci bus is root (behind host-pci bridge), * false otherwise + * + * Some code assumes that "bus->self == NULL" means that bus is a root bus. + * This is incorrect because "virtual" buses added for SR-IOV (via + * virtfn_add_bus()) have "bus->self == NULL" but are not root buses. */ static inline bool pci_is_root_bus(struct pci_bus *pbus) { From 3ad94b0d795c50836160781e63ee6bb752766aaf Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 6 Sep 2013 09:45:58 +0800 Subject: [PATCH 32/79] PCI: Pass type, width, and prefetchability for window alignment When calculating window_alignment(), type information like IORESOURCE_MEM and IORESOURCE_PREFETCH may not be enough. For example, on powernv, we need to know whether the window is 64-bit or not. This patch passes the full resource type (res->flags) for window alignment. [bhelgaas: changelog] Signed-off-by: Wei Yang Signed-off-by: Bjorn Helgaas Acked-by: Gavin Shan --- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8df686c48bd1..4ce83b26ae9e 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -982,7 +982,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, } min_align = calculate_mem_align(aligns, max_order); - min_align = max(min_align, window_alignment(bus, b_res->flags & mask)); + min_align = max(min_align, window_alignment(bus, b_res->flags)); size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align); if (children_add_size > add_size) add_size = children_add_size; From b8cac70afe983f0c48a0296bbb7a6177e99fbbb9 Mon Sep 17 00:00:00 2001 From: Todd E Brandt Date: Tue, 10 Sep 2013 16:10:43 -0700 Subject: [PATCH 33/79] PCI: Remove Intel Haswell D3 delays The latest Intel Haswell chipsets have a hardware optimization which allows on-chip PCI devices to ignore the 10ms delay before entering or exiting D3 suspend. This patch implements the optimization as a PCI quirk, since we want tight control over which devices use it. This way we can test each device individually to be sure there are no issues before we enable the quirk. The first set of devices are from the Haswell platform, which includes every PCI device that is on the northbridge and southbridge. This patch reduces the Haswell suspend time from 93 ms to 47 ms and resume time from 160 ms to 64 ms. Signed-off-by: Todd Brandt Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/quirks.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index f6c31fabf3af..91490453c229 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2954,6 +2954,29 @@ static void disable_igfx_irq(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); +/* + * PCI devices which are on Intel chips can skip the 10ms delay + * before entering D3 mode. + */ +static void quirk_remove_d3_delay(struct pci_dev *dev) +{ + dev->d3_delay = 0; +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c00, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0412, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c0c, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c31, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3a, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3d, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c2d, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c20, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c18, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c1c, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c26, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c4e, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c02, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c22, quirk_remove_d3_delay); + /* * Some devices may pass our check in pci_intx_mask_supported if * PCI_COMMAND_INTX_DISABLE works though they actually do not properly From 4af8225567065f1825baed259822b0d26c343fad Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 12 Sep 2013 20:29:24 +0900 Subject: [PATCH 34/79] MAINTAINERS: Add Jingoo Han as Samsung Exynos PCIe driver maintainer Add myself as maintainer for Samsung Exynos PCIe driver. Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e61c2e83fc2b..bc6d82162ebe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6346,6 +6346,12 @@ S: Supported F: Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt F: drivers/pci/host/pci-tegra.c +PCI DRIVER FOR SAMSUNG EXYNOS +M: Jingoo Han +L: linux-pci@vger.kernel.org +S: Maintained +F: drivers/pci/host/pci-exynos.c + PCMCIA SUBSYSTEM P: Linux PCMCIA Team L: linux-pcmcia@lists.infradead.org From f342d940ee0e3a2b5197fd4fbade1cb6bbc960b7 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 6 Sep 2013 15:54:59 +0900 Subject: [PATCH 35/79] PCI: exynos: Add support for MSI This patch adds support for Message Signaled Interrupt in the Exynos PCIe driver using Synopsys designware PCIe core IP. Signed-off-by: Siva Reddy Kallam Signed-off-by: Srikanth T Shivanand Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas Cc: Pratyush Anand Cc: Mohit KUMAR --- drivers/pci/host/pci-exynos.c | 44 ++++++ drivers/pci/host/pcie-designware.c | 240 +++++++++++++++++++++++++++++ drivers/pci/host/pcie-designware.h | 14 ++ 3 files changed, 298 insertions(+) diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index 94e096bb2d0a..f062acaf052a 100644 --- a/drivers/pci/host/pci-exynos.c +++ b/drivers/pci/host/pci-exynos.c @@ -48,6 +48,7 @@ struct exynos_pcie { #define PCIE_IRQ_SPECIAL 0x008 #define PCIE_IRQ_EN_PULSE 0x00c #define PCIE_IRQ_EN_LEVEL 0x010 +#define IRQ_MSI_ENABLE (0x1 << 2) #define PCIE_IRQ_EN_SPECIAL 0x014 #define PCIE_PWR_RESET 0x018 #define PCIE_CORE_RESET 0x01c @@ -342,9 +343,36 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) return IRQ_HANDLED; } +static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) +{ + struct pcie_port *pp = arg; + + dw_handle_msi_irq(pp); + + return IRQ_HANDLED; +} + +static void exynos_pcie_msi_init(struct pcie_port *pp) +{ + u32 val; + struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); + + dw_pcie_msi_init(pp); + + /* enable MSI interrupt */ + val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_EN_LEVEL); + val |= IRQ_MSI_ENABLE; + exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL); + return; +} + static void exynos_pcie_enable_interrupts(struct pcie_port *pp) { exynos_pcie_enable_irq_pulse(pp); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + exynos_pcie_msi_init(pp); + return; } @@ -430,6 +458,22 @@ static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) return ret; } + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (!pp->msi_irq) { + dev_err(&pdev->dev, "failed to get msi irq\n"); + return -ENODEV; + } + + ret = devm_request_irq(&pdev->dev, pp->msi_irq, + exynos_pcie_msi_irq_handler, + IRQF_SHARED, "exynos-pcie", pp); + if (ret) { + dev_err(&pdev->dev, "failed to request msi irq\n"); + return ret; + } + } + pp->root_bus_nr = -1; pp->ops = &exynos_pcie_host_ops; diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index c10e9ac9bbbc..896301788e9d 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -11,8 +11,11 @@ * published by the Free Software Foundation. */ +#include +#include #include #include +#include #include #include #include @@ -142,6 +145,204 @@ int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, return ret; } +static struct irq_chip dw_msi_irq_chip = { + .name = "PCI-MSI", + .irq_enable = unmask_msi_irq, + .irq_disable = mask_msi_irq, + .irq_mask = mask_msi_irq, + .irq_unmask = unmask_msi_irq, +}; + +/* MSI int handler */ +void dw_handle_msi_irq(struct pcie_port *pp) +{ + unsigned long val; + int i, pos; + + for (i = 0; i < MAX_MSI_CTRLS; i++) { + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, + (u32 *)&val); + if (val) { + pos = 0; + while ((pos = find_next_bit(&val, 32, pos)) != 32) { + generic_handle_irq(pp->msi_irq_start + + (i * 32) + pos); + pos++; + } + } + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val); + } +} + +void dw_pcie_msi_init(struct pcie_port *pp) +{ + pp->msi_data = __get_free_pages(GFP_KERNEL, 0); + + /* program the msi_data */ + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, + virt_to_phys((void *)pp->msi_data)); + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); +} + +static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) +{ + int flag = 1; + + do { + pos = find_next_zero_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos); + /*if you have reached to the end then get out from here.*/ + if (pos == MAX_MSI_IRQS) + return -ENOSPC; + /* + * Check if this position is at correct offset.nvec is always a + * power of two. pos0 must be nvec bit alligned. + */ + if (pos % msgvec) + pos += msgvec - (pos % msgvec); + else + flag = 0; + } while (flag); + + *pos0 = pos; + return 0; +} + +static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) +{ + int res, bit, irq, pos0, pos1, i; + u32 val; + struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); + + if (!pp) { + BUG(); + return -EINVAL; + } + + pos0 = find_first_zero_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS); + if (pos0 % no_irqs) { + if (find_valid_pos0(pp, no_irqs, pos0, &pos0)) + goto no_valid_irq; + } + if (no_irqs > 1) { + pos1 = find_next_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos0); + /* there must be nvec number of consecutive free bits */ + while ((pos1 - pos0) < no_irqs) { + if (find_valid_pos0(pp, no_irqs, pos1, &pos0)) + goto no_valid_irq; + pos1 = find_next_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos0); + } + } + + irq = (pp->msi_irq_start + pos0); + + if ((irq + no_irqs) > (pp->msi_irq_start + MAX_MSI_IRQS-1)) + goto no_valid_irq; + + i = 0; + while (i < no_irqs) { + set_bit(pos0 + i, pp->msi_irq_in_use); + irq_alloc_descs((irq + i), (irq + i), 1, 0); + irq_set_msi_desc(irq + i, desc); + /*Enable corresponding interrupt in MSI interrupt controller */ + res = ((pos0 + i) / 32) * 12; + bit = (pos0 + i) % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + i++; + } + + *pos = pos0; + return irq; + +no_valid_irq: + *pos = pos0; + return -ENOSPC; +} + +static void clear_irq(unsigned int irq) +{ + int res, bit, val, pos; + struct irq_desc *desc; + struct msi_desc *msi; + struct pcie_port *pp; + + /* get the port structure */ + desc = irq_to_desc(irq); + msi = irq_desc_get_msi_desc(desc); + pp = sys_to_pcie(msi->dev->bus->sysdata); + if (!pp) { + BUG(); + return; + } + + pos = irq - pp->msi_irq_start; + + irq_free_desc(irq); + + clear_bit(pos, pp->msi_irq_in_use); + + /* Disable corresponding interrupt on MSI interrupt controller */ + res = (pos / 32) * 12; + bit = pos % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + +static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, + struct msi_desc *desc) +{ + int irq, pos, msgvec; + u16 msg_ctr; + struct msi_msg msg; + struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); + + if (!pp) { + BUG(); + return -EINVAL; + } + + pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS, + &msg_ctr); + msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4; + if (msgvec == 0) + msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1; + if (msgvec > 5) + msgvec = 0; + + irq = assign_irq((1 << msgvec), desc, &pos); + if (irq < 0) + return irq; + + msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; + msg_ctr |= msgvec << 4; + pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, + msg_ctr); + desc->msi_attrib.multiple = msgvec; + + msg.address_lo = virt_to_phys((void *)pp->msi_data); + msg.address_hi = 0x0; + msg.data = pos; + write_msi_msg(irq, &msg); + + return 0; +} + +static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) +{ + clear_irq(irq); +} + +static struct msi_chip dw_pcie_msi_chip = { + .setup_irq = dw_msi_setup_irq, + .teardown_irq = dw_msi_teardown_irq, +}; + int dw_pcie_link_up(struct pcie_port *pp) { if (pp->ops->link_up) @@ -150,6 +351,20 @@ int dw_pcie_link_up(struct pcie_port *pp) return 0; } +static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +static const struct irq_domain_ops msi_domain_ops = { + .map = dw_pcie_msi_map, +}; + int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; @@ -157,6 +372,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct of_pci_range_parser parser; u32 val; + struct irq_domain *irq_domain; + if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); return -EINVAL; @@ -223,6 +440,18 @@ int __init dw_pcie_host_init(struct pcie_port *pp) return -EINVAL; } + if (IS_ENABLED(CONFIG_PCI_MSI)) { + irq_domain = irq_domain_add_linear(pp->dev->of_node, + MAX_MSI_IRQS, &msi_domain_ops, + &dw_pcie_msi_chip); + if (!irq_domain) { + dev_err(pp->dev, "irq domain init failed\n"); + return -ENXIO; + } + + pp->msi_irq_start = irq_find_mapping(irq_domain, 0); + } + if (pp->ops->host_init) pp->ops->host_init(pp); @@ -485,10 +714,21 @@ int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return pp->irq; } +static void dw_pcie_add_bus(struct pci_bus *bus) +{ + if (IS_ENABLED(CONFIG_PCI_MSI)) { + struct pcie_port *pp = sys_to_pcie(bus->sysdata); + + dw_pcie_msi_chip.dev = pp->dev; + bus->msi = &dw_pcie_msi_chip; + } +} + static struct hw_pci dw_pci = { .setup = dw_pcie_setup, .scan = dw_pcie_scan_bus, .map_irq = dw_pcie_map_irq, + .add_bus = dw_pcie_add_bus, }; void dw_pcie_setup_rc(struct pcie_port *pp) diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 133820f1da97..faccbbf31907 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -20,6 +20,14 @@ struct pcie_port_info { phys_addr_t mem_bus_addr; }; +/* + * Maximum number of MSI IRQs can be 256 per controller. But keep + * it 32 as of now. Probably we will never need more than 32. If needed, + * then increment it in multiple of 32. + */ +#define MAX_MSI_IRQS 32 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) + struct pcie_port { struct device *dev; u8 root_bus_nr; @@ -38,6 +46,10 @@ struct pcie_port { int irq; u32 lanes; struct pcie_host_ops *ops; + int msi_irq; + int msi_irq_start; + unsigned long msi_data; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; struct pcie_host_ops { @@ -57,6 +69,8 @@ int cfg_read(void __iomem *addr, int where, int size, u32 *val); int cfg_write(void __iomem *addr, int where, int size, u32 val); int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val); int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val); +void dw_handle_msi_irq(struct pcie_port *pp); +void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); From f62b878b4dfc71de24ea4bc085d042862cba88e4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 6 Sep 2013 17:21:45 +0900 Subject: [PATCH 36/79] PCI: exynos: Turn off power of phy block when link failed When link failed, there is no need to turn on phy block. Also, turning on phy block is added, in order to turn on phy block regardless of the default value of phy registers. Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pci-exynos.c | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index f062acaf052a..ee692c2c3d73 100644 --- a/drivers/pci/host/pci-exynos.c +++ b/drivers/pci/host/pci-exynos.c @@ -78,18 +78,28 @@ struct exynos_pcie { #define PCIE_PHY_PLL_BIAS 0x00c #define PCIE_PHY_DCC_FEEDBACK 0x014 #define PCIE_PHY_PLL_DIV_1 0x05c +#define PCIE_PHY_COMMON_POWER 0x064 +#define PCIE_PHY_COMMON_PD_CMN (0x1 << 3) #define PCIE_PHY_TRSV0_EMP_LVL 0x084 #define PCIE_PHY_TRSV0_DRV_LVL 0x088 #define PCIE_PHY_TRSV0_RXCDR 0x0ac +#define PCIE_PHY_TRSV0_POWER 0x0c4 +#define PCIE_PHY_TRSV0_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV0_LVCC 0x0dc #define PCIE_PHY_TRSV1_EMP_LVL 0x144 #define PCIE_PHY_TRSV1_RXCDR 0x16c +#define PCIE_PHY_TRSV1_POWER 0x184 +#define PCIE_PHY_TRSV1_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV1_LVCC 0x19c #define PCIE_PHY_TRSV2_EMP_LVL 0x204 #define PCIE_PHY_TRSV2_RXCDR 0x22c +#define PCIE_PHY_TRSV2_POWER 0x244 +#define PCIE_PHY_TRSV2_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV2_LVCC 0x25c #define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 #define PCIE_PHY_TRSV3_RXCDR 0x2ec +#define PCIE_PHY_TRSV3_POWER 0x304 +#define PCIE_PHY_TRSV3_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV3_LVCC 0x31c static inline void exynos_elb_writel(struct exynos_pcie *pcie, u32 val, u32 reg) @@ -203,6 +213,58 @@ static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp) exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET); } +static void exynos_pcie_power_on_phy(struct pcie_port *pp) +{ + u32 val; + struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); + val &= ~PCIE_PHY_COMMON_PD_CMN; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER); + val &= ~PCIE_PHY_TRSV0_PD_TSV; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER); + val &= ~PCIE_PHY_TRSV1_PD_TSV; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER); + val &= ~PCIE_PHY_TRSV2_PD_TSV; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER); + val &= ~PCIE_PHY_TRSV3_PD_TSV; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); +} + +static void exynos_pcie_power_off_phy(struct pcie_port *pp) +{ + u32 val; + struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); + val |= PCIE_PHY_COMMON_PD_CMN; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER); + val |= PCIE_PHY_TRSV0_PD_TSV; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER); + val |= PCIE_PHY_TRSV1_PD_TSV; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER); + val |= PCIE_PHY_TRSV2_PD_TSV; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER); + + val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER); + val |= PCIE_PHY_TRSV3_PD_TSV; + exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); +} + static void exynos_pcie_init_phy(struct pcie_port *pp) { struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); @@ -271,6 +333,9 @@ static int exynos_pcie_establish_link(struct pcie_port *pp) /* de-assert phy reset */ exynos_pcie_deassert_phy_reset(pp); + /* power on phy */ + exynos_pcie_power_on_phy(pp); + /* initialize phy */ exynos_pcie_init_phy(pp); @@ -303,6 +368,9 @@ static int exynos_pcie_establish_link(struct pcie_port *pp) PCIE_PHY_PLL_LOCKED); dev_info(pp->dev, "PLL Locked: 0x%x\n", val); } + /* power off phy */ + exynos_pcie_power_off_phy(pp); + dev_err(pp->dev, "PCIe Link Fail\n"); return -EINVAL; } From 1e65249d4a0aa31a7862b9ed04e267d43a4fd267 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 25 Sep 2013 16:40:54 -0600 Subject: [PATCH 37/79] PCI: tegra: Add missing __iomem annotation Added missing __iomem annotation in order to fix the following sparse warnings: drivers/pci/host/pci-tegra.c:411:41: warning: incorrect type in return expression (different address spaces) drivers/pci/host/pci-tegra.c:411:41: expected void [noderef] * drivers/pci/host/pci-tegra.c:411:41: got void *addr drivers/pci/host/pci-tegra.c:419:25: warning: incorrect type in return expression (different address spaces) drivers/pci/host/pci-tegra.c:419:25: expected void [noderef] * drivers/pci/host/pci-tegra.c:419:25: got void *addr Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas Acked-by: Thierry Reding --- drivers/pci/host/pci-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 2e9888a0635a..7c4f38dd42ba 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -408,7 +408,7 @@ static void __iomem *tegra_pcie_bus_map(struct tegra_pcie *pcie, list_for_each_entry(bus, &pcie->busses, list) if (bus->nr == busnr) - return bus->area->addr; + return (void __iomem *)bus->area->addr; bus = tegra_pcie_bus_alloc(pcie, busnr); if (IS_ERR(bus)) @@ -416,7 +416,7 @@ static void __iomem *tegra_pcie_bus_map(struct tegra_pcie *pcie, list_add_tail(&bus->list, &pcie->busses); - return bus->area->addr; + return (void __iomem *)bus->area->addr; } static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus, From bd950799d9510cac994fad2ea020767fe878b84b Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Tue, 24 Sep 2013 18:11:48 -0600 Subject: [PATCH 38/79] PCI: acpiphp: Convert to dynamic debug This patch is to use pr_debug/info/warn/err to replace acpiphp debug functions and remove module's debug param. User interface change: before this patch, boot with the "acpiphp.debug" kernel parameter to turn on debug. After this patch, set CONFIG_DYNAMIC_DEBUG=y and boot with "acpiphp.dyndebug=+p" instead. See Documentation/dynamic-debug-howto.txt. [bhelgaas: changelog] Signed-off-by: Lan Tianyu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpiphp.h | 10 -------- drivers/pci/hotplug/acpiphp_core.c | 37 +++++++++++++++--------------- drivers/pci/hotplug/acpiphp_glue.c | 23 ++++++++++--------- 3 files changed, 30 insertions(+), 40 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index f4e028924667..26100f510b10 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -39,16 +39,6 @@ #include #include -#define dbg(format, arg...) \ - do { \ - if (acpiphp_debug) \ - printk(KERN_DEBUG "%s: " format, \ - MY_NAME , ## arg); \ - } while (0) -#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) - struct acpiphp_context; struct acpiphp_bridge; struct acpiphp_slot; diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index bf2203ef1308..8650d39db392 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -31,6 +31,8 @@ * */ +#define pr_fmt(fmt) "acpiphp: " fmt + #include #include #include @@ -43,12 +45,9 @@ #include #include "acpiphp.h" -#define MY_NAME "acpiphp" - /* name size which is used for entries in pcihpfs */ #define SLOT_NAME_SIZE 21 /* {_SUN} */ -bool acpiphp_debug; bool acpiphp_disabled; /* local variables */ @@ -61,9 +60,7 @@ static struct acpiphp_attention_info *attention_info; MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); MODULE_PARM_DESC(disable, "disable acpiphp driver"); -module_param_named(debug, acpiphp_debug, bool, 0644); module_param_named(disable, acpiphp_disabled, bool, 0444); /* export the attention callback registration methods */ @@ -139,7 +136,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) { struct slot *slot = hotplug_slot->private; - dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); + pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); /* enable the specified slot */ return acpiphp_enable_slot(slot->acpi_slot); @@ -156,7 +153,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) { struct slot *slot = hotplug_slot->private; - dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); + pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); /* disable the specified slot */ return acpiphp_disable_and_eject_slot(slot->acpi_slot); @@ -176,8 +173,9 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) { int retval = -ENODEV; - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot_name(hotplug_slot)); - + pr_debug("%s - physical_slot = %s\n", __func__, + hotplug_slot_name(hotplug_slot)); + if (attention_info && try_module_get(attention_info->owner)) { retval = attention_info->set_attn(hotplug_slot, status); module_put(attention_info->owner); @@ -199,7 +197,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) { struct slot *slot = hotplug_slot->private; - dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); + pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); *value = acpiphp_get_power_status(slot->acpi_slot); @@ -221,7 +219,8 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) { int retval = -EINVAL; - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot_name(hotplug_slot)); + pr_debug("%s - physical_slot = %s\n", __func__, + hotplug_slot_name(hotplug_slot)); if (attention_info && try_module_get(attention_info->owner)) { retval = attention_info->get_attn(hotplug_slot, value); @@ -244,7 +243,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) { struct slot *slot = hotplug_slot->private; - dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); + pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); *value = acpiphp_get_latch_status(slot->acpi_slot); @@ -264,7 +263,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { struct slot *slot = hotplug_slot->private; - dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); + pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); *value = acpiphp_get_adapter_status(slot->acpi_slot); @@ -279,7 +278,7 @@ static void release_slot(struct hotplug_slot *hotplug_slot) { struct slot *slot = hotplug_slot->private; - dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); + pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); kfree(slot->hotplug_slot); kfree(slot); @@ -322,11 +321,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, if (retval == -EBUSY) goto error_hpslot; if (retval) { - err("pci_hp_register failed with error %d\n", retval); + pr_err("pci_hp_register failed with error %d\n", retval); goto error_hpslot; } - info("Slot [%s] registered\n", slot_name(slot)); + pr_info("Slot [%s] registered\n", slot_name(slot)); return 0; error_hpslot: @@ -343,17 +342,17 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) struct slot *slot = acpiphp_slot->slot; int retval = 0; - info("Slot [%s] unregistered\n", slot_name(slot)); + pr_info("Slot [%s] unregistered\n", slot_name(slot)); retval = pci_hp_deregister(slot->hotplug_slot); if (retval) - err("pci_hp_deregister failed with error %d\n", retval); + pr_err("pci_hp_deregister failed with error %d\n", retval); } void __init acpiphp_init(void) { - info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n", + pr_info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n", acpiphp_disabled ? ", disabled by user; please report a bug" : ""); } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 0b7d23b4ad95..6557702a0592 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -39,6 +39,8 @@ * bus. It loses the refcount when the the driver unloads. */ +#define pr_fmt(fmt) "acpiphp_glue: " fmt + #include #include @@ -58,8 +60,6 @@ static LIST_HEAD(bridge_list); static DEFINE_MUTEX(bridge_mutex); static DEFINE_MUTEX(acpiphp_context_lock); -#define MY_NAME "acpiphp_glue" - static void handle_hotplug_event(acpi_handle handle, u32 type, void *data); static void acpiphp_sanitize_bus(struct pci_bus *bus); static void acpiphp_set_hpp_values(struct pci_bus *bus); @@ -335,7 +335,7 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, if (ACPI_FAILURE(status)) sun = bridge->nr_slots; - dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", + pr_debug("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", sun, pci_domain_nr(pbus), pbus->number, device); retval = acpiphp_register_hotplug_slot(slot, sun); @@ -343,10 +343,10 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, slot->slot = NULL; bridge->nr_slots--; if (retval == -EBUSY) - warn("Slot %llu already registered by another " + pr_warn("Slot %llu already registered by another " "hotplug driver\n", sun); else - warn("acpiphp_register_hotplug_slot failed " + pr_warn("acpiphp_register_hotplug_slot failed " "(err code = 0x%x)\n", retval); } /* Even if the slot registration fails, we can still use it. */ @@ -369,7 +369,7 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, if (register_hotplug_dock_device(handle, &acpiphp_dock_ops, context, acpiphp_dock_init, acpiphp_dock_release)) - dbg("failed to register dock device\n"); + pr_debug("failed to register dock device\n"); } /* install notify handler */ @@ -427,7 +427,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) ACPI_SYSTEM_NOTIFY, handle_hotplug_event); if (ACPI_FAILURE(status)) - err("failed to remove notify handler\n"); + pr_err("failed to remove notify handler\n"); } } if (slot->slot) @@ -830,8 +830,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) switch (type) { case ACPI_NOTIFY_BUS_CHECK: /* bus re-enumerate */ - dbg("%s: Bus check notify on %s\n", __func__, objname); - dbg("%s: re-enumerating slots under %s\n", __func__, objname); + pr_debug("%s: Bus check notify on %s\n", __func__, objname); + pr_debug("%s: re-enumerating slots under %s\n", + __func__, objname); if (bridge) { acpiphp_check_bridge(bridge); } else { @@ -845,7 +846,7 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) case ACPI_NOTIFY_DEVICE_CHECK: /* device check */ - dbg("%s: Device check notify on %s\n", __func__, objname); + pr_debug("%s: Device check notify on %s\n", __func__, objname); if (bridge) { acpiphp_check_bridge(bridge); } else { @@ -866,7 +867,7 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) case ACPI_NOTIFY_EJECT_REQUEST: /* request device eject */ - dbg("%s: Device eject notify on %s\n", __func__, objname); + pr_debug("%s: Device eject notify on %s\n", __func__, objname); acpiphp_disable_and_eject_slot(func->slot); break; } From 43e7aa47a7fc3948304d386780ed3100cb67ddac Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Tue, 24 Sep 2013 18:07:28 -0600 Subject: [PATCH 39/79] PCI: acpiphp_ibm: Convert to dynamic debug This patch is to use pr_debug/info/warn/err to replace acpiphp_ibm debug functions and remove module's debug param. User interface change: before this patch, boot with the "acpiphp_ibm.debug" kernel parameter to turn on debug. After this patch, set CONFIG_DYNAMIC_DEBUG=y and boot with "acpiphp_ibm.dyndebug=+p" instead. See Documentation/dynamic-debug-howto.txt. [bhelgaas: changelog] Signed-off-by: Lan Tianyu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpiphp_ibm.c | 58 +++++++++++++------------------ 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 2f5786c8522c..0d64c414bf78 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -25,6 +25,8 @@ * */ +#define pr_fmt(fmt) "acpiphp_ibm: " fmt + #include #include #include @@ -43,23 +45,11 @@ #define DRIVER_AUTHOR "Irene Zubarev , Vernon Mauery " #define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension" -static bool debug; MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(DRIVER_VERSION); -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, " Debugging mode enabled or not"); -#define MY_NAME "acpiphp_ibm" - -#undef dbg -#define dbg(format, arg...) \ -do { \ - if (debug) \ - printk(KERN_DEBUG "%s: " format, \ - MY_NAME , ## arg); \ -} while (0) #define FOUND_APCI 0x61504349 /* these are the names for the IBM ACPI pseudo-device */ @@ -189,7 +179,7 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status) ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot)); - dbg("%s: set slot %d (%d) attention status to %d\n", __func__, + pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__, ibm_slot->slot.slot_num, ibm_slot->slot.slot_id, (status ? 1 : 0)); @@ -202,10 +192,10 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status) stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", ¶ms, &rc); if (ACPI_FAILURE(stat)) { - err("APLS evaluation failed: 0x%08x\n", stat); + pr_err("APLS evaluation failed: 0x%08x\n", stat); return -ENODEV; } else if (!rc) { - err("APLS method failed: 0x%08llx\n", rc); + pr_err("APLS method failed: 0x%08llx\n", rc); return -ERANGE; } return 0; @@ -234,7 +224,7 @@ static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status) else *status = 0; - dbg("%s: get slot %d (%d) attention status is %d\n", __func__, + pr_debug("%s: get slot %d (%d) attention status is %d\n", __func__, ibm_slot->slot.slot_num, ibm_slot->slot.slot_id, *status); @@ -266,10 +256,10 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context) u8 subevent = event & 0xf0; struct notification *note = context; - dbg("%s: Received notification %02x\n", __func__, event); + pr_debug("%s: Received notification %02x\n", __func__, event); if (subevent == 0x80) { - dbg("%s: generationg bus event\n", __func__); + pr_debug("%s: generationg bus event\n", __func__); acpi_bus_generate_netlink_event(note->device->pnp.device_class, dev_name(¬e->device->dev), note->event, detail); @@ -301,7 +291,7 @@ static int ibm_get_table_from_acpi(char **bufp) status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer); if (ACPI_FAILURE(status)) { - err("%s: APCI evaluation failed\n", __func__); + pr_err("%s: APCI evaluation failed\n", __func__); return -ENODEV; } @@ -309,13 +299,13 @@ static int ibm_get_table_from_acpi(char **bufp) if (!(package) || (package->type != ACPI_TYPE_PACKAGE) || !(package->package.elements)) { - err("%s: Invalid APCI object\n", __func__); + pr_err("%s: Invalid APCI object\n", __func__); goto read_table_done; } for(size = 0, i = 0; i < package->package.count; i++) { if (package->package.elements[i].type != ACPI_TYPE_BUFFER) { - err("%s: Invalid APCI element %d\n", __func__, i); + pr_err("%s: Invalid APCI element %d\n", __func__, i); goto read_table_done; } size += package->package.elements[i].buffer.length; @@ -325,7 +315,7 @@ static int ibm_get_table_from_acpi(char **bufp) goto read_table_done; lbuf = kzalloc(size, GFP_KERNEL); - dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n", + pr_debug("%s: element count: %i, ASL table size: %i, &table = 0x%p\n", __func__, package->package.count, size, lbuf); if (lbuf) { @@ -370,8 +360,8 @@ static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj, { int bytes_read = -EINVAL; char *table = NULL; - - dbg("%s: pos = %d, size = %zd\n", __func__, (int)pos, size); + + pr_debug("%s: pos = %d, size = %zd\n", __func__, (int)pos, size); if (pos == 0) { bytes_read = ibm_get_table_from_acpi(&table); @@ -403,7 +393,7 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle, status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) { - err("%s: Failed to get device information status=0x%x\n", + pr_err("%s: Failed to get device information status=0x%x\n", __func__, status); return retval; } @@ -411,7 +401,7 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle, if (info->current_status && (info->valid & ACPI_VALID_HID) && (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) || !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) { - dbg("found hardware: %s, handle: %p\n", + pr_debug("found hardware: %s, handle: %p\n", info->hardware_id.string, handle); *phandle = handle; /* returning non-zero causes the search to stop @@ -432,18 +422,18 @@ static int __init ibm_acpiphp_init(void) struct acpi_device *device; struct kobject *sysdir = &pci_slots_kset->kobj; - dbg("%s\n", __func__); + pr_debug("%s\n", __func__); if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, ibm_find_acpi_device, NULL, &ibm_acpi_handle, NULL) != FOUND_APCI) { - err("%s: acpi_walk_namespace failed\n", __func__); + pr_err("%s: acpi_walk_namespace failed\n", __func__); retval = -ENODEV; goto init_return; } - dbg("%s: found IBM aPCI device\n", __func__); + pr_debug("%s: found IBM aPCI device\n", __func__); if (acpi_bus_get_device(ibm_acpi_handle, &device)) { - err("%s: acpi_bus_get_device failed\n", __func__); + pr_err("%s: acpi_bus_get_device failed\n", __func__); retval = -ENODEV; goto init_return; } @@ -457,7 +447,7 @@ static int __init ibm_acpiphp_init(void) ACPI_DEVICE_NOTIFY, ibm_handle_events, &ibm_note); if (ACPI_FAILURE(status)) { - err("%s: Failed to register notification handler\n", + pr_err("%s: Failed to register notification handler\n", __func__); retval = -EBUSY; goto init_cleanup; @@ -479,17 +469,17 @@ static void __exit ibm_acpiphp_exit(void) acpi_status status; struct kobject *sysdir = &pci_slots_kset->kobj; - dbg("%s\n", __func__); + pr_debug("%s\n", __func__); if (acpiphp_unregister_attention(&ibm_attention_info)) - err("%s: attention info deregistration failed", __func__); + pr_err("%s: attention info deregistration failed", __func__); status = acpi_remove_notify_handler( ibm_acpi_handle, ACPI_DEVICE_NOTIFY, ibm_handle_events); if (ACPI_FAILURE(status)) - err("%s: Notification handler removal failed\n", __func__); + pr_err("%s: Notification handler removal failed\n", __func__); /* remove the /sys entries */ sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr); } From 8d6a35fb13406f87d926fffeee0d70360ce3077d Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Thu, 26 Sep 2013 11:24:46 +0800 Subject: [PATCH 40/79] ARM: imx6q: Add PCIe bits to GPR syscon definition PCIe requires additional bits be defined for GPR8 and GPR12. Signed-off-by: Sean Cross Signed-off-by: Shawn Guo Signed-off-by: Bjorn Helgaas --- include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h index b6bdcd66c07d..e00e9f362fd5 100644 --- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h +++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h @@ -241,6 +241,12 @@ #define IMX6Q_GPR5_L2_CLK_STOP BIT(8) +#define IMX6Q_GPR8_TX_SWING_LOW (0x7f << 25) +#define IMX6Q_GPR8_TX_SWING_FULL (0x7f << 18) +#define IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB (0x3f << 12) +#define IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB (0x3f << 6) +#define IMX6Q_GPR8_TX_DEEMPH_GEN1 (0x3f << 0) + #define IMX6Q_GPR9_TZASC2_BYP BIT(1) #define IMX6Q_GPR9_TZASC1_BYP BIT(0) @@ -273,7 +279,9 @@ #define IMX6Q_GPR12_ARMP_AHB_CLK_EN BIT(26) #define IMX6Q_GPR12_ARMP_ATB_CLK_EN BIT(25) #define IMX6Q_GPR12_ARMP_APB_CLK_EN BIT(24) +#define IMX6Q_GPR12_DEVICE_TYPE (0xf << 12) #define IMX6Q_GPR12_PCIE_CTL_2 BIT(10) +#define IMX6Q_GPR12_LOS_LEVEL (0x1f << 4) #define IMX6Q_GPR13_SDMA_STOP_REQ BIT(30) #define IMX6Q_GPR13_CAN2_STOP_REQ BIT(29) From bb38919ec56e0758c3ae56dfc091dcde1391353e Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Thu, 26 Sep 2013 11:24:47 +0800 Subject: [PATCH 41/79] PCI: imx6: Add support for i.MX6 PCIe controller Add support for the PCIe port present on the i.MX6 family of controllers. These use the Synopsis Designware core tied to their own PHY. Signed-off-by: Sean Cross Signed-off-by: Shawn Guo Signed-off-by: Bjorn Helgaas Acked-by: Sascha Hauer --- .../bindings/pci/designware-pcie.txt | 7 +- drivers/pci/host/Kconfig | 6 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-imx6.c | 575 ++++++++++++++++++ 4 files changed, 588 insertions(+), 1 deletion(-) create mode 100644 drivers/pci/host/pci-imx6.c diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt index eabcb4b5db6e..dd8d920bcbd6 100644 --- a/Documentation/devicetree/bindings/pci/designware-pcie.txt +++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt @@ -3,7 +3,7 @@ Required properties: - compatible: should contain "snps,dw-pcie" to identify the core, plus an identifier for the specific instance, such - as "samsung,exynos5440-pcie". + as "samsung,exynos5440-pcie" or "fsl,imx6q-pcie". - reg: base addresses and lengths of the pcie controller, the phy controller, additional register for the phy controller. - interrupts: interrupt values for level interrupt, @@ -21,6 +21,11 @@ Required properties: - num-lanes: number of lanes to use - reset-gpio: gpio pin number of power good signal +Optional properties for fsl,imx6q-pcie +- power-on-gpio: gpio pin number of power-enable signal +- wake-up-gpio: gpio pin number of incoming wakeup signal +- disable-gpio: gpio pin number of outgoing rfkill/endpoint disable signal + Example: SoC specific DT Entry: diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 3d9504811126..efa24d9a3361 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -15,6 +15,12 @@ config PCI_EXYNOS select PCIEPORTBUS select PCIE_DW +config PCI_IMX6 + bool "Freescale i.MX6 PCIe controller" + depends on SOC_IMX6Q + select PCIEPORTBUS + select PCIE_DW + config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index c9a997b2690d..287d6a053dda 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o +obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c new file mode 100644 index 000000000000..5afa9226a080 --- /dev/null +++ b/drivers/pci/host/pci-imx6.c @@ -0,0 +1,575 @@ +/* + * PCIe host controller driver for Freescale i.MX6 SoCs + * + * Copyright (C) 2013 Kosagi + * http://www.kosagi.com + * + * Author: Sean Cross + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp) + +struct imx6_pcie { + int reset_gpio; + int power_on_gpio; + int wake_up_gpio; + int disable_gpio; + struct clk *lvds_gate; + struct clk *sata_ref_100m; + struct clk *pcie_ref_125m; + struct clk *pcie_axi; + struct pcie_port pp; + struct regmap *iomuxc_gpr; + void __iomem *mem_base; +}; + +/* PCIe Port Logic registers (memory-mapped) */ +#define PL_OFFSET 0x700 +#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) +#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) + +#define PCIE_PHY_CTRL (PL_OFFSET + 0x114) +#define PCIE_PHY_CTRL_DATA_LOC 0 +#define PCIE_PHY_CTRL_CAP_ADR_LOC 16 +#define PCIE_PHY_CTRL_CAP_DAT_LOC 17 +#define PCIE_PHY_CTRL_WR_LOC 18 +#define PCIE_PHY_CTRL_RD_LOC 19 + +#define PCIE_PHY_STAT (PL_OFFSET + 0x110) +#define PCIE_PHY_STAT_ACK_LOC 16 + +/* PHY registers (not memory-mapped) */ +#define PCIE_PHY_RX_ASIC_OUT 0x100D + +#define PHY_RX_OVRD_IN_LO 0x1005 +#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) +#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) + +static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val) +{ + u32 val; + u32 max_iterations = 10; + u32 wait_counter = 0; + + do { + val = readl(dbi_base + PCIE_PHY_STAT); + val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; + wait_counter++; + + if (val == exp_val) + return 0; + + udelay(1); + } while (wait_counter < max_iterations); + + return -ETIMEDOUT; +} + +static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr) +{ + u32 val; + int ret; + + val = addr << PCIE_PHY_CTRL_DATA_LOC; + writel(val, dbi_base + PCIE_PHY_CTRL); + + val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); + writel(val, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 1); + if (ret) + return ret; + + val = addr << PCIE_PHY_CTRL_DATA_LOC; + writel(val, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 0); + if (ret) + return ret; + + return 0; +} + +/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ +static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data) +{ + u32 val, phy_ctl; + int ret; + + ret = pcie_phy_wait_ack(dbi_base, addr); + if (ret) + return ret; + + /* assert Read signal */ + phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; + writel(phy_ctl, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 1); + if (ret) + return ret; + + val = readl(dbi_base + PCIE_PHY_STAT); + *data = val & 0xffff; + + /* deassert Read signal */ + writel(0x00, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 0); + if (ret) + return ret; + + return 0; +} + +static int pcie_phy_write(void __iomem *dbi_base, int addr, int data) +{ + u32 var; + int ret; + + /* write addr */ + /* cap addr */ + ret = pcie_phy_wait_ack(dbi_base, addr); + if (ret) + return ret; + + var = data << PCIE_PHY_CTRL_DATA_LOC; + writel(var, dbi_base + PCIE_PHY_CTRL); + + /* capture data */ + var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); + writel(var, dbi_base + PCIE_PHY_CTRL); + + ret = pcie_phy_poll_ack(dbi_base, 1); + if (ret) + return ret; + + /* deassert cap data */ + var = data << PCIE_PHY_CTRL_DATA_LOC; + writel(var, dbi_base + PCIE_PHY_CTRL); + + /* wait for ack de-assertion */ + ret = pcie_phy_poll_ack(dbi_base, 0); + if (ret) + return ret; + + /* assert wr signal */ + var = 0x1 << PCIE_PHY_CTRL_WR_LOC; + writel(var, dbi_base + PCIE_PHY_CTRL); + + /* wait for ack */ + ret = pcie_phy_poll_ack(dbi_base, 1); + if (ret) + return ret; + + /* deassert wr signal */ + var = data << PCIE_PHY_CTRL_DATA_LOC; + writel(var, dbi_base + PCIE_PHY_CTRL); + + /* wait for ack de-assertion */ + ret = pcie_phy_poll_ack(dbi_base, 0); + if (ret) + return ret; + + writel(0x0, dbi_base + PCIE_PHY_CTRL); + + return 0; +} + +/* Added for PCI abort handling */ +static int imx6q_pcie_abort_handler(unsigned long addr, + unsigned int fsr, struct pt_regs *regs) +{ + /* + * If it was an imprecise abort, then we need to correct the + * return address to be _after_ the instruction. + */ + if (fsr & (1 << 10)) + regs->ARM_pc += 4; + return 0; +} + +static int imx6_pcie_assert_core_reset(struct pcie_port *pp) +{ + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); + + gpio_set_value(imx6_pcie->reset_gpio, 0); + msleep(100); + gpio_set_value(imx6_pcie->reset_gpio, 1); + + return 0; +} + +static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) +{ + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + int ret; + + if (gpio_is_valid(imx6_pcie->power_on_gpio)) + gpio_set_value(imx6_pcie->power_on_gpio, 1); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); + + ret = clk_prepare_enable(imx6_pcie->sata_ref_100m); + if (ret) { + dev_err(pp->dev, "unable to enable sata_ref_100m\n"); + goto err_sata_ref; + } + + ret = clk_prepare_enable(imx6_pcie->pcie_ref_125m); + if (ret) { + dev_err(pp->dev, "unable to enable pcie_ref_125m\n"); + goto err_pcie_ref; + } + + ret = clk_prepare_enable(imx6_pcie->lvds_gate); + if (ret) { + dev_err(pp->dev, "unable to enable lvds_gate\n"); + goto err_lvds_gate; + } + + ret = clk_prepare_enable(imx6_pcie->pcie_axi); + if (ret) { + dev_err(pp->dev, "unable to enable pcie_axi\n"); + goto err_pcie_axi; + } + + /* allow the clocks to stabilize */ + usleep_range(200, 500); + + return 0; + +err_pcie_axi: + clk_disable_unprepare(imx6_pcie->lvds_gate); +err_lvds_gate: + clk_disable_unprepare(imx6_pcie->pcie_ref_125m); +err_pcie_ref: + clk_disable_unprepare(imx6_pcie->sata_ref_100m); +err_sata_ref: + return ret; + +} + +static void imx6_pcie_init_phy(struct pcie_port *pp) +{ + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); + + /* configure constant input signal to the pcie ctrl and phy */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_LOS_LEVEL, 9 << 4); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, 0 << 6); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, 20 << 12); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_SWING_FULL, 127 << 18); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_SWING_LOW, 127 << 25); +} + +static void imx6_pcie_host_init(struct pcie_port *pp) +{ + int count = 0; + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + + imx6_pcie_assert_core_reset(pp); + + imx6_pcie_init_phy(pp); + + imx6_pcie_deassert_core_reset(pp); + + dw_pcie_setup_rc(pp); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); + + while (!dw_pcie_link_up(pp)) { + usleep_range(100, 1000); + count++; + if (count >= 10) { + dev_err(pp->dev, "phy link never came up\n"); + dev_dbg(pp->dev, + "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", + readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), + readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); + break; + } + } + + return; +} + +static int imx6_pcie_link_up(struct pcie_port *pp) +{ + u32 rc, ltssm, rx_valid, temp; + + /* link is debug bit 36, debug register 1 starts at bit 32 */ + rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << (36 - 32)); + if (rc) + return -EAGAIN; + + /* + * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. + * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). + * If (MAC/LTSSM.state == Recovery.RcvrLock) + * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition + * to gen2 is stuck + */ + pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid); + ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F; + + if (rx_valid & 0x01) + return 0; + + if (ltssm != 0x0d) + return 0; + + dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); + + pcie_phy_read(pp->dbi_base, + PHY_RX_OVRD_IN_LO, &temp); + temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN + | PHY_RX_OVRD_IN_LO_RX_PLL_EN); + pcie_phy_write(pp->dbi_base, + PHY_RX_OVRD_IN_LO, temp); + + usleep_range(2000, 3000); + + pcie_phy_read(pp->dbi_base, + PHY_RX_OVRD_IN_LO, &temp); + temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN + | PHY_RX_OVRD_IN_LO_RX_PLL_EN); + pcie_phy_write(pp->dbi_base, + PHY_RX_OVRD_IN_LO, temp); + + return 0; +} + +static struct pcie_host_ops imx6_pcie_host_ops = { + .link_up = imx6_pcie_link_up, + .host_init = imx6_pcie_host_init, +}; + +static int imx6_add_pcie_port(struct pcie_port *pp, + struct platform_device *pdev) +{ + int ret; + + pp->irq = platform_get_irq(pdev, 0); + if (!pp->irq) { + dev_err(&pdev->dev, "failed to get irq\n"); + return -ENODEV; + } + + pp->root_bus_nr = -1; + pp->ops = &imx6_pcie_host_ops; + + spin_lock_init(&pp->conf_lock); + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(&pdev->dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int __init imx6_pcie_probe(struct platform_device *pdev) +{ + struct imx6_pcie *imx6_pcie; + struct pcie_port *pp; + struct device_node *np = pdev->dev.of_node; + struct resource *dbi_base; + int ret; + + imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL); + if (!imx6_pcie) + return -ENOMEM; + + pp = &imx6_pcie->pp; + pp->dev = &pdev->dev; + + /* Added for PCI abort handling */ + hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0, + "imprecise external abort"); + + dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!dbi_base) { + dev_err(&pdev->dev, "dbi_base memory resource not found\n"); + return -ENODEV; + } + + pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base); + if (IS_ERR(pp->dbi_base)) { + dev_err(&pdev->dev, "unable to remap dbi_base\n"); + ret = PTR_ERR(pp->dbi_base); + goto err; + } + + /* Fetch GPIOs */ + imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (!gpio_is_valid(imx6_pcie->reset_gpio)) { + dev_err(&pdev->dev, "no reset-gpio defined\n"); + ret = -ENODEV; + } + ret = devm_gpio_request_one(&pdev->dev, + imx6_pcie->reset_gpio, + GPIOF_OUT_INIT_LOW, + "PCIe reset"); + if (ret) { + dev_err(&pdev->dev, "unable to get reset gpio\n"); + goto err; + } + + imx6_pcie->power_on_gpio = of_get_named_gpio(np, "power-on-gpio", 0); + if (gpio_is_valid(imx6_pcie->power_on_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, + imx6_pcie->power_on_gpio, + GPIOF_OUT_INIT_LOW, + "PCIe power enable"); + if (ret) { + dev_err(&pdev->dev, "unable to get power-on gpio\n"); + goto err; + } + } + + imx6_pcie->wake_up_gpio = of_get_named_gpio(np, "wake-up-gpio", 0); + if (gpio_is_valid(imx6_pcie->wake_up_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, + imx6_pcie->wake_up_gpio, + GPIOF_IN, + "PCIe wake up"); + if (ret) { + dev_err(&pdev->dev, "unable to get wake-up gpio\n"); + goto err; + } + } + + imx6_pcie->disable_gpio = of_get_named_gpio(np, "disable-gpio", 0); + if (gpio_is_valid(imx6_pcie->disable_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, + imx6_pcie->disable_gpio, + GPIOF_OUT_INIT_HIGH, + "PCIe disable endpoint"); + if (ret) { + dev_err(&pdev->dev, "unable to get disable-ep gpio\n"); + goto err; + } + } + + /* Fetch clocks */ + imx6_pcie->lvds_gate = devm_clk_get(&pdev->dev, "lvds_gate"); + if (IS_ERR(imx6_pcie->lvds_gate)) { + dev_err(&pdev->dev, + "lvds_gate clock select missing or invalid\n"); + ret = PTR_ERR(imx6_pcie->lvds_gate); + goto err; + } + + imx6_pcie->sata_ref_100m = devm_clk_get(&pdev->dev, "sata_ref_100m"); + if (IS_ERR(imx6_pcie->sata_ref_100m)) { + dev_err(&pdev->dev, + "sata_ref_100m clock source missing or invalid\n"); + ret = PTR_ERR(imx6_pcie->sata_ref_100m); + goto err; + } + + imx6_pcie->pcie_ref_125m = devm_clk_get(&pdev->dev, "pcie_ref_125m"); + if (IS_ERR(imx6_pcie->pcie_ref_125m)) { + dev_err(&pdev->dev, + "pcie_ref_125m clock source missing or invalid\n"); + ret = PTR_ERR(imx6_pcie->pcie_ref_125m); + goto err; + } + + imx6_pcie->pcie_axi = devm_clk_get(&pdev->dev, "pcie_axi"); + if (IS_ERR(imx6_pcie->pcie_axi)) { + dev_err(&pdev->dev, + "pcie_axi clock source missing or invalid\n"); + ret = PTR_ERR(imx6_pcie->pcie_axi); + goto err; + } + + /* Grab GPR config register range */ + imx6_pcie->iomuxc_gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imx6_pcie->iomuxc_gpr)) { + dev_err(&pdev->dev, "unable to find iomuxc registers\n"); + ret = PTR_ERR(imx6_pcie->iomuxc_gpr); + goto err; + } + + ret = imx6_add_pcie_port(pp, pdev); + if (ret < 0) + goto err; + + platform_set_drvdata(pdev, imx6_pcie); + return 0; + +err: + return ret; +} + +static const struct of_device_id imx6_pcie_of_match[] = { + { .compatible = "fsl,imx6q-pcie", }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); + +static struct platform_driver imx6_pcie_driver = { + .driver = { + .name = "imx6q-pcie", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(imx6_pcie_of_match), + }, +}; + +/* Freescale PCIe driver does not allow module unload */ + +static int __init imx6_pcie_init(void) +{ + return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe); +} +module_init(imx6_pcie_init); + +MODULE_AUTHOR("Sean Cross "); +MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); From 09a2c73ddfc7f173237fc7209a65b34dd5bcb5ed Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 12 Sep 2013 16:20:00 +0800 Subject: [PATCH 42/79] PCI: Remove unused PCI_MSIX_FLAGS_BIRMASK definition PCI_MSIX_FLAGS_BIRMASK has been replaced by PCI_MSIX_TABLE_BIR for better readability. Now no one uses it, remove it. No functional change. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- include/uapi/linux/pci_regs.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index baa7852468ef..f0509148111d 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -319,7 +319,6 @@ #define PCI_MSIX_PBA 8 /* Pending Bit Array offset */ #define PCI_MSIX_PBA_BIR 0x00000007 /* BAR index */ #define PCI_MSIX_PBA_OFFSET 0xfffffff8 /* Offset into specified BAR */ -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) /* deprecated */ #define PCI_CAP_MSIX_SIZEOF 12 /* size of MSIX registers */ /* MSI-X entry's format */ From 03078633a6eb86fdb6ea2f40e6352de4b1181bbf Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 24 Sep 2013 14:24:49 -0600 Subject: [PATCH 43/79] IB/qib: Drop qib_tune_pcie_caps() and qib_tune_pcie_coalesce() return values The callers of qib_tune_pcie_caps() and qib_tune_pcie_coalesce() don't check the return values, so this patch drops the return values altogether. Signed-off-by: Bjorn Helgaas Acked-by: Mike Marciniszyn --- drivers/infiniband/hw/qib/qib_pcie.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c index 24973c8b8443..c8d9c4ab142b 100644 --- a/drivers/infiniband/hw/qib/qib_pcie.c +++ b/drivers/infiniband/hw/qib/qib_pcie.c @@ -51,8 +51,8 @@ * file calls, even though this violates some * expectations of harmlessness. */ -static int qib_tune_pcie_caps(struct qib_devdata *); -static int qib_tune_pcie_coalesce(struct qib_devdata *); +static void qib_tune_pcie_caps(struct qib_devdata *); +static void qib_tune_pcie_coalesce(struct qib_devdata *); /* * Do all the common PCIe setup and initialization. @@ -487,7 +487,7 @@ MODULE_PARM_DESC(pcie_coalesce, "tune PCIe colescing on some Intel chipsets"); * of these chipsets, with some BIOS settings, and enabling it on those * systems may result in the system crashing, and/or data corruption. */ -static int qib_tune_pcie_coalesce(struct qib_devdata *dd) +static void qib_tune_pcie_coalesce(struct qib_devdata *dd) { int r; struct pci_dev *parent; @@ -495,18 +495,18 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd) u32 mask, bits, val; if (!qib_pcie_coalesce) - return 0; + return; /* Find out supported and configured values for parent (root) */ parent = dd->pcidev->bus->self; if (parent->bus->parent) { qib_devinfo(dd->pcidev, "Parent not root\n"); - return 1; + return; } if (!pci_is_pcie(parent)) - return 1; + return; if (parent->vendor != 0x8086) - return 1; + return; /* * - bit 12: Max_rdcmp_Imt_EN: need to set to 1 @@ -539,13 +539,12 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd) mask = (3U << 24) | (7U << 10); } else { /* not one of the chipsets that we know about */ - return 1; + return; } pci_read_config_dword(parent, 0x48, &val); val &= ~mask; val |= bits; r = pci_write_config_dword(parent, 0x48, val); - return 0; } /* @@ -556,9 +555,8 @@ static int qib_pcie_caps; module_param_named(pcie_caps, qib_pcie_caps, int, S_IRUGO); MODULE_PARM_DESC(pcie_caps, "Max PCIe tuning: Payload (0..3), ReadReq (4..7)"); -static int qib_tune_pcie_caps(struct qib_devdata *dd) +static void qib_tune_pcie_caps(struct qib_devdata *dd) { - int ret = 1; /* Assume the worst */ struct pci_dev *parent; u16 rc_mpss, rc_mps, ep_mpss, ep_mps; u16 rc_mrrs, ep_mrrs, max_mrrs; @@ -567,18 +565,18 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd) parent = dd->pcidev->bus->self; if (!pci_is_root_bus(parent->bus)) { qib_devinfo(dd->pcidev, "Parent not root\n"); - goto bail; + return; } if (!pci_is_pcie(parent) || !pci_is_pcie(dd->pcidev)) - goto bail; + return; + rc_mpss = parent->pcie_mpss; rc_mps = ffs(pcie_get_mps(parent)) - 8; /* Find out supported and configured values for endpoint (us) */ ep_mpss = dd->pcidev->pcie_mpss; ep_mps = ffs(pcie_get_mps(dd->pcidev)) - 8; - ret = 0; /* Find max payload supported by root, endpoint */ if (rc_mpss > ep_mpss) rc_mpss = ep_mpss; @@ -618,8 +616,6 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd) ep_mrrs = max_mrrs; pcie_set_readrq(dd->pcidev, ep_mrrs); } -bail: - return ret; } /* End of PCIe capability tuning */ From 9db008d0d963f358c13381e86f5a042579216f06 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 9 Sep 2013 21:13:07 +0800 Subject: [PATCH 44/79] staging: et131x: Use pci_dev->pcie_mpss and pcie_set_readrq() to simplify code The PCI core caches the "PCIe Max Payload Size Supported" in pci_dev->pcie_mpss, so use that instead of pcie_capability_read_dword(). Also use pcie_set_readrq() instead of pcie_capability_clear_and_set_word() to simplify code. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman Acked-by: Mark Einon --- drivers/staging/et131x/et131x.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/staging/et131x/et131x.c b/drivers/staging/et131x/et131x.c index f73e58f5ef8d..876881d1826e 100644 --- a/drivers/staging/et131x/et131x.c +++ b/drivers/staging/et131x/et131x.c @@ -3605,17 +3605,10 @@ static int et131x_pci_init(struct et131x_adapter *adapter, goto err_out; } - /* Let's set up the PORT LOGIC Register. First we need to know what - * the max_payload_size is - */ - if (pcie_capability_read_word(pdev, PCI_EXP_DEVCAP, &max_payload)) { - dev_err(&pdev->dev, - "Could not read PCI config space for Max Payload Size\n"); - goto err_out; - } + /* Let's set up the PORT LOGIC Register. */ /* Program the Ack/Nak latency and replay timers */ - max_payload &= 0x07; + max_payload = pdev->pcie_mpss; if (max_payload < 2) { static const u16 acknak[2] = { 0x76, 0xD0 }; @@ -3645,8 +3638,7 @@ static int et131x_pci_init(struct et131x_adapter *adapter, } /* Change the max read size to 2k */ - if (pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL, - PCI_EXP_DEVCTL_READRQ, 0x4 << 12)) { + if (pcie_set_readrq(pdev, 2048)) { dev_err(&pdev->dev, "Couldn't change PCI config space for Max read size\n"); goto err_out; From c11592fea04901920236859898f5bc82b6c1a2f1 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 9 Sep 2013 21:13:08 +0800 Subject: [PATCH 45/79] drm/radeon: use pcie_get_readrq() and pcie_set_readrq() to simplify code Use pcie_get_readrq() and pcie_set_readrq() functions to simplify code. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Deucher --- drivers/gpu/drm/radeon/evergreen.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 555164e270a7..871066a8c630 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1174,23 +1174,16 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev) { - u16 ctl, v; - int err; - - err = pcie_capability_read_word(rdev->pdev, PCI_EXP_DEVCTL, &ctl); - if (err) - return; - - v = (ctl & PCI_EXP_DEVCTL_READRQ) >> 12; + int readrq; + u16 v; + readrq = pcie_get_readrq(rdev->pdev); + v = ffs(readrq) - 8; /* if bios or OS sets MAX_READ_REQUEST_SIZE to an invalid value, fix it * to avoid hangs or perfomance issues */ - if ((v == 0) || (v == 6) || (v == 7)) { - ctl &= ~PCI_EXP_DEVCTL_READRQ; - ctl |= (2 << 12); - pcie_capability_write_word(rdev->pdev, PCI_EXP_DEVCTL, ctl); - } + if ((v == 0) || (v == 6) || (v == 7)) + pcie_set_readrq(rdev->pdev, 512); } static bool dce4_is_in_vblank(struct radeon_device *rdev, int crtc) From f8db3c9086cd33ee3efc913ade291694f54a57f2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 29 Sep 2013 10:29:11 +0800 Subject: [PATCH 46/79] PCI: exynos: Add missing clk_disable_unprepare() on error path Add the missing clk_disable_unprepare() before return from exynos_pcie_probe() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han --- drivers/pci/host/pci-exynos.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index ee692c2c3d73..89804728bd5b 100644 --- a/drivers/pci/host/pci-exynos.c +++ b/drivers/pci/host/pci-exynos.c @@ -599,18 +599,24 @@ static int __init exynos_pcie_probe(struct platform_device *pdev) elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); exynos_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base); - if (IS_ERR(exynos_pcie->elbi_base)) - return PTR_ERR(exynos_pcie->elbi_base); + if (IS_ERR(exynos_pcie->elbi_base)) { + ret = PTR_ERR(exynos_pcie->elbi_base); + goto fail_bus_clk; + } phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1); exynos_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base); - if (IS_ERR(exynos_pcie->phy_base)) - return PTR_ERR(exynos_pcie->phy_base); + if (IS_ERR(exynos_pcie->phy_base)) { + ret = PTR_ERR(exynos_pcie->phy_base); + goto fail_bus_clk; + } block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2); exynos_pcie->block_base = devm_ioremap_resource(&pdev->dev, block_base); - if (IS_ERR(exynos_pcie->block_base)) - return PTR_ERR(exynos_pcie->block_base); + if (IS_ERR(exynos_pcie->block_base)) { + ret = PTR_ERR(exynos_pcie->block_base); + goto fail_bus_clk; + } ret = add_pcie_port(pp, pdev); if (ret < 0) From 0f49ba5599048aae2fb3479a1814736e969cf38c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 7 Oct 2013 14:51:02 -0600 Subject: [PATCH 47/79] PCI: convert bus code to use bus_groups The bus_attrs field of struct bus_type is going away soon, dev_groups should be used instead. This converts the PCI bus code to use the correct field. Cc: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-driver.c | 2 +- drivers/pci/pci-sysfs.c | 16 +++++++++++++--- drivers/pci/pci.h | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 98f7b9b89507..e00f74143115 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1317,7 +1317,7 @@ struct bus_type pci_bus_type = { .remove = pci_device_remove, .shutdown = pci_device_shutdown, .dev_attrs = pci_dev_attrs, - .bus_attrs = pci_bus_attrs, + .bus_groups = pci_bus_groups, .drv_attrs = pci_drv_attrs, .pm = PCI_PM_OPS_PTR, }; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7128cfdd64aa..d8eb880bd1fc 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -302,10 +302,20 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, } return count; } +static BUS_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store); -struct bus_attribute pci_bus_attrs[] = { - __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store), - __ATTR_NULL +struct attribute *pci_bus_attrs[] = { + &bus_attr_rescan.attr, + NULL, +}; + +static const struct attribute_group pci_bus_group = { + .attrs = pci_bus_attrs, +}; + +const struct attribute_group *pci_bus_groups[] = { + &pci_bus_group, + NULL, }; static ssize_t diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8a00c063d7bc..607be58dd728 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -156,7 +156,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev) extern struct device_attribute pci_dev_attrs[]; extern const struct attribute_group *pcibus_groups[]; extern struct device_type pci_dev_type; -extern struct bus_attribute pci_bus_attrs[]; +extern const struct attribute_group *pci_bus_groups[]; /** From 2229c1fbc5bd3bd7eb376285de159d086e79d58a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 7 Oct 2013 14:51:20 -0600 Subject: [PATCH 48/79] PCI: convert bus code to use drv_groups The drv_attrs field of struct bus_type is going away soon, drv_groups should be used instead. This converts the PCI bus code to use the correct field. Cc: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-driver.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e00f74143115..38f3c0140dfb 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -135,6 +135,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) return retval; return count; } +static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); /** * store_remove_id - remove a PCI device ID from this driver @@ -180,12 +181,14 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) return retval; return count; } +static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); -static struct driver_attribute pci_drv_attrs[] = { - __ATTR(new_id, S_IWUSR, NULL, store_new_id), - __ATTR(remove_id, S_IWUSR, NULL, store_remove_id), - __ATTR_NULL, +static struct attribute *pci_drv_attrs[] = { + &driver_attr_new_id.attr, + &driver_attr_remove_id.attr, + NULL, }; +ATTRIBUTE_GROUPS(pci_drv); /** * pci_match_id - See if a pci device matches a given pci_id table @@ -1318,7 +1321,7 @@ struct bus_type pci_bus_type = { .shutdown = pci_device_shutdown, .dev_attrs = pci_dev_attrs, .bus_groups = pci_bus_groups, - .drv_attrs = pci_drv_attrs, + .drv_groups = pci_drv_groups, .pm = PCI_PM_OPS_PTR, }; From 5136b2da770d53f026ab091f0423729ebf37a6b5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 6 Oct 2013 23:55:40 -0700 Subject: [PATCH 49/79] PCI: convert bus code to use dev_groups The dev_attrs field of struct bus_type is going away soon, dev_groups should be used instead. This converts the PCI bus code to use the correct field. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 2 +- drivers/pci/pci-sysfs.c | 73 +++++++++++++++++++++++++--------------- drivers/pci/pci.h | 2 +- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 38f3c0140dfb..9f85960a62ed 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1319,7 +1319,7 @@ struct bus_type pci_bus_type = { .probe = pci_device_probe, .remove = pci_device_remove, .shutdown = pci_device_shutdown, - .dev_attrs = pci_dev_attrs, + .dev_groups = pci_dev_groups, .bus_groups = pci_bus_groups, .drv_groups = pci_drv_groups, .pm = PCI_PM_OPS_PTR, diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d8eb880bd1fc..618c06094295 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -42,7 +42,8 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ \ pdev = to_pci_dev (dev); \ return sprintf (buf, format_string, pdev->field); \ -} +} \ +static DEVICE_ATTR_RO(field) pci_config_attr(vendor, "0x%04x\n"); pci_config_attr(device, "0x%04x\n"); @@ -73,6 +74,7 @@ static ssize_t broken_parity_status_store(struct device *dev, return count; } +static DEVICE_ATTR_RW(broken_parity_status); static ssize_t local_cpus_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -91,7 +93,7 @@ static ssize_t local_cpus_show(struct device *dev, buf[len] = '\0'; return len; } - +static DEVICE_ATTR_RO(local_cpus); static ssize_t local_cpulist_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -110,6 +112,7 @@ static ssize_t local_cpulist_show(struct device *dev, buf[len] = '\0'; return len; } +static DEVICE_ATTR_RO(local_cpulist); /* * PCI Bus Class Devices @@ -170,6 +173,7 @@ resource_show(struct device * dev, struct device_attribute *attr, char * buf) } return (str - buf); } +static DEVICE_ATTR_RO(resource); static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -181,10 +185,11 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), (u8)(pci_dev->class)); } +static DEVICE_ATTR_RO(modalias); -static ssize_t is_enabled_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) +static ssize_t enabled_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) { struct pci_dev *pdev = to_pci_dev(dev); unsigned long val; @@ -208,14 +213,15 @@ static ssize_t is_enabled_store(struct device *dev, return result < 0 ? result : count; } -static ssize_t is_enabled_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct pci_dev *pdev; pdev = to_pci_dev (dev); return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt)); } +static DEVICE_ATTR_RW(enabled); #ifdef CONFIG_NUMA static ssize_t @@ -223,6 +229,7 @@ numa_node_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf (buf, "%d\n", dev->numa_node); } +static DEVICE_ATTR_RO(numa_node); #endif static ssize_t @@ -232,6 +239,7 @@ dma_mask_bits_show(struct device *dev, struct device_attribute *attr, char *buf) return sprintf (buf, "%d\n", fls64(pdev->dma_mask)); } +static DEVICE_ATTR_RO(dma_mask_bits); static ssize_t consistent_dma_mask_bits_show(struct device *dev, struct device_attribute *attr, @@ -239,6 +247,7 @@ consistent_dma_mask_bits_show(struct device *dev, struct device_attribute *attr, { return sprintf (buf, "%d\n", fls64(dev->coherent_dma_mask)); } +static DEVICE_ATTR_RO(consistent_dma_mask_bits); static ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -283,6 +292,7 @@ msi_bus_store(struct device *dev, struct device_attribute *attr, return count; } +static DEVICE_ATTR_RW(msi_bus); static DEFINE_MUTEX(pci_remove_rescan_mutex); static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, @@ -414,6 +424,7 @@ static ssize_t d3cold_allowed_show(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); return sprintf (buf, "%u\n", pdev->d3cold_allowed); } +static DEVICE_ATTR_RW(d3cold_allowed); #endif #ifdef CONFIG_PCI_IOV @@ -499,30 +510,38 @@ static struct device_attribute sriov_numvfs_attr = sriov_numvfs_show, sriov_numvfs_store); #endif /* CONFIG_PCI_IOV */ -struct device_attribute pci_dev_attrs[] = { - __ATTR_RO(resource), - __ATTR_RO(vendor), - __ATTR_RO(device), - __ATTR_RO(subsystem_vendor), - __ATTR_RO(subsystem_device), - __ATTR_RO(class), - __ATTR_RO(irq), - __ATTR_RO(local_cpus), - __ATTR_RO(local_cpulist), - __ATTR_RO(modalias), +struct attribute *pci_dev_attrs[] = { + &dev_attr_resource.attr, + &dev_attr_vendor.attr, + &dev_attr_device.attr, + &dev_attr_subsystem_vendor.attr, + &dev_attr_subsystem_device.attr, + &dev_attr_class.attr, + &dev_attr_irq.attr, + &dev_attr_local_cpus.attr, + &dev_attr_local_cpulist.attr, + &dev_attr_modalias.attr, #ifdef CONFIG_NUMA - __ATTR_RO(numa_node), + &dev_attr_numa_node.attr, #endif - __ATTR_RO(dma_mask_bits), - __ATTR_RO(consistent_dma_mask_bits), - __ATTR(enable, 0600, is_enabled_show, is_enabled_store), - __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), - broken_parity_status_show,broken_parity_status_store), - __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), + &dev_attr_dma_mask_bits.attr, + &dev_attr_consistent_dma_mask_bits.attr, + &dev_attr_enabled.attr, + &dev_attr_broken_parity_status.attr, + &dev_attr_msi_bus.attr, #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) - __ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store), + &dev_attr_d3cold_allowed.attr, #endif - __ATTR_NULL, + NULL, +}; + +static const struct attribute_group pci_dev_group = { + .attrs = pci_dev_attrs, +}; + +const struct attribute_group *pci_dev_groups[] = { + &pci_dev_group, + NULL, }; static struct attribute *pcibus_attrs[] = { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 607be58dd728..9c91ecc1301b 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -153,7 +153,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev) return (dev->no_d1d2 || parent_dstates); } -extern struct device_attribute pci_dev_attrs[]; +extern const struct attribute_group *pci_dev_groups[]; extern const struct attribute_group *pcibus_groups[]; extern struct device_type pci_dev_type; extern const struct attribute_group *pci_bus_groups[]; From bf22c90fb190e837173d61368e3c80f5b78751a8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 28 Sep 2013 15:42:00 +0530 Subject: [PATCH 50/79] PCI: Make pci_bus_attrs, pci_dev_attrs, dev_rescan_attr, dev_remove_attr, vga_attr static Local variables used only in this file are made static. [bhelgaas: also make pci_dev_attrs[] static (from Fengguang)] Signed-off-by: Sachin Kamat Signed-off-by: Fengguang Wu Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 618c06094295..82cc45867eaa 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -314,7 +314,7 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, } static BUS_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store); -struct attribute *pci_bus_attrs[] = { +static struct attribute *pci_bus_attrs[] = { &bus_attr_rescan.attr, NULL, }; @@ -345,8 +345,9 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr, } return count; } -struct device_attribute dev_rescan_attr = __ATTR(rescan, (S_IWUSR|S_IWGRP), - NULL, dev_rescan_store); +static struct device_attribute dev_rescan_attr = __ATTR(rescan, + (S_IWUSR|S_IWGRP), + NULL, dev_rescan_store); static void remove_callback(struct device *dev) { @@ -376,8 +377,9 @@ remove_store(struct device *dev, struct device_attribute *dummy, count = ret; return count; } -struct device_attribute dev_remove_attr = __ATTR(remove, (S_IWUSR|S_IWGRP), - NULL, remove_store); +static struct device_attribute dev_remove_attr = __ATTR(remove, + (S_IWUSR|S_IWGRP), + NULL, remove_store); static ssize_t dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, @@ -510,7 +512,7 @@ static struct device_attribute sriov_numvfs_attr = sriov_numvfs_show, sriov_numvfs_store); #endif /* CONFIG_PCI_IOV */ -struct attribute *pci_dev_attrs[] = { +static struct attribute *pci_dev_attrs[] = { &dev_attr_resource.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, @@ -573,7 +575,7 @@ boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf) !!(pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)); } -struct device_attribute vga_attr = __ATTR_RO(boot_vga); +static struct device_attribute vga_attr = __ATTR_RO(boot_vga); static ssize_t pci_read_config(struct file *filp, struct kobject *kobj, From f91da04d0aebf8edbcd2fde5f9d7df85a0b6f6cd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 4 Oct 2013 12:04:44 -0600 Subject: [PATCH 51/79] PCI: Make pci_dev_pm_ops static pci_dev_pm_ops is local to pci-driver.c. Make it static. Signed-off-by: Sachin Kamat Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 98f7b9b89507..32b6bc5a23ac 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1121,7 +1121,7 @@ static int pci_pm_runtime_idle(struct device *dev) #ifdef CONFIG_PM -const struct dev_pm_ops pci_dev_pm_ops = { +static const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, .complete = pci_pm_complete, .suspend = pci_pm_suspend, From fa9e9621900497f0e74f1719ee5e9b34feffd829 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Sat, 28 Sep 2013 11:55:49 -0600 Subject: [PATCH 52/79] cris/PCI: Remove unused pci_mem_start Remove unused 'pci_mem_start' variable. Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas CC: Mikael Starvik CC: Jesper Nilsson --- arch/cris/include/asm/pci.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/cris/include/asm/pci.h b/arch/cris/include/asm/pci.h index 146da904cdd8..f666734926d5 100644 --- a/arch/cris/include/asm/pci.h +++ b/arch/cris/include/asm/pci.h @@ -11,7 +11,6 @@ #define pcibios_assign_all_busses(void) 1 -extern unsigned long pci_mem_start; #define PCIBIOS_MIN_IO 0x1000 #define PCIBIOS_MIN_MEM 0x10000000 From 135530d584bafa88bcb924552fb13bcab07ca1a5 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Sat, 28 Sep 2013 11:59:56 -0600 Subject: [PATCH 53/79] mn10300/PCI: Remove unused pci_mem_start Remove unused 'pci_mem_start' variable. Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas Acked-by: David Howells CC: Koichi Yasutake --- arch/mn10300/include/asm/pci.h | 1 - arch/mn10300/kernel/setup.c | 3 --- 2 files changed, 4 deletions(-) diff --git a/arch/mn10300/include/asm/pci.h b/arch/mn10300/include/asm/pci.h index 6f31cc0f1a87..166323824683 100644 --- a/arch/mn10300/include/asm/pci.h +++ b/arch/mn10300/include/asm/pci.h @@ -44,7 +44,6 @@ extern void unit_pci_init(void); #define pcibios_assign_all_busses() 0 #endif -extern unsigned long pci_mem_start; #define PCIBIOS_MIN_IO 0xBE000004 #define PCIBIOS_MIN_MEM 0xB8000000 diff --git a/arch/mn10300/kernel/setup.c b/arch/mn10300/kernel/setup.c index ebac9c11f796..2ad7f32fa122 100644 --- a/arch/mn10300/kernel/setup.c +++ b/arch/mn10300/kernel/setup.c @@ -35,9 +35,6 @@ struct mn10300_cpuinfo boot_cpu_data; -/* For PCI or other memory-mapped resources */ -unsigned long pci_mem_start = 0x18000000; - static char __initdata cmd_line[COMMAND_LINE_SIZE]; char redboot_command_line[COMMAND_LINE_SIZE] = "console=ttyS0,115200 root=/dev/mtdblock3 rw"; From c489f5fbb1f5a770f98e492af5c47befb32890cd Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 30 Sep 2013 15:02:38 +0800 Subject: [PATCH 54/79] PCI: Add pci_dev_show_local_cpu() to simplify code local_cpus_show() and local_cpulist_show() are almost the same. This adds a new helper function, pci_dev_show_local_cpu(), to simplify code. The same strategy is already used by cpuaffinity_show() and cpulistaffinity_show(). Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7128cfdd64aa..d9252dd89ea9 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -74,27 +74,10 @@ static ssize_t broken_parity_status_store(struct device *dev, return count; } -static ssize_t local_cpus_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - const struct cpumask *mask; - int len; - -#ifdef CONFIG_NUMA - mask = (dev_to_node(dev) == -1) ? cpu_online_mask : - cpumask_of_node(dev_to_node(dev)); -#else - mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); -#endif - len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask); - buf[len++] = '\n'; - buf[len] = '\0'; - return len; -} - - -static ssize_t local_cpulist_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t pci_dev_show_local_cpu(struct device *dev, + int type, + struct device_attribute *attr, + char *buf) { const struct cpumask *mask; int len; @@ -105,12 +88,27 @@ static ssize_t local_cpulist_show(struct device *dev, #else mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); #endif - len = cpulist_scnprintf(buf, PAGE_SIZE-2, mask); + len = type ? + cpumask_scnprintf(buf, PAGE_SIZE-2, mask) : + cpulist_scnprintf(buf, PAGE_SIZE-2, mask); + buf[len++] = '\n'; buf[len] = '\0'; return len; } +static ssize_t local_cpus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return pci_dev_show_local_cpu(dev, 1, attr, buf); +} + +static ssize_t local_cpulist_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return pci_dev_show_local_cpu(dev, 0, attr, buf); +} + /* * PCI Bus Class Devices */ From 84822b158fd3fc7d9f9c67f1b26a1e007880e13c Mon Sep 17 00:00:00 2001 From: Liu Chuansheng Date: Mon, 7 Oct 2013 15:29:27 -0600 Subject: [PATCH 55/79] PCI/PM: Remove pci_pm_complete() 88d26136 ("PM: Prevent runtime suspend during system resume") removed the pm_runtime_put_sync() from pci_pm_complete() to PM core code device_complete(). Here the pci_pm_complete() is doing the same work which can be done in device_complete(), so we can remove it directly. Signed-off-by: Liu Chuansheng Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/pci-driver.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 32b6bc5a23ac..b60fe6737f78 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -599,18 +599,10 @@ static int pci_pm_prepare(struct device *dev) return error; } -static void pci_pm_complete(struct device *dev) -{ - struct device_driver *drv = dev->driver; - - if (drv && drv->pm && drv->pm->complete) - drv->pm->complete(dev); -} #else /* !CONFIG_PM_SLEEP */ #define pci_pm_prepare NULL -#define pci_pm_complete NULL #endif /* !CONFIG_PM_SLEEP */ @@ -1123,7 +1115,6 @@ static int pci_pm_runtime_idle(struct device *dev) static const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, - .complete = pci_pm_complete, .suspend = pci_pm_suspend, .resume = pci_pm_resume, .freeze = pci_pm_freeze, From 18edf4512cfa3e3662bdbdfc5f11c2eb20721734 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 9 Oct 2013 09:12:21 -0600 Subject: [PATCH 56/79] PCI: designware: Add header guards Add header guards to prevent redundant inclusion. Signed-off-by: Seungwon Jeon Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-designware.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index faccbbf31907..d87fbaeffe70 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -11,6 +11,9 @@ * published by the Free Software Foundation. */ +#ifndef _PCIE_DESIGNWARE_H +#define _PCIE_DESIGNWARE_H + struct pcie_port_info { u32 cfg0_size; u32 cfg1_size; @@ -77,3 +80,5 @@ int dw_pcie_host_init(struct pcie_port *pp); int dw_pcie_setup(int nr, struct pci_sys_data *sys); struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys); int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); + +#endif /* _PCIE_DESIGNWARE_H */ From 73e408508bf6c76d8dc06f044f0e4703a1e27f14 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 9 Oct 2013 09:12:37 -0600 Subject: [PATCH 57/79] PCI: designware: Make dw_pcie_rd_own_conf(), etc., static The following variables and functions are used only in pcie-designware.c, so make them static: global_io_offset dw_pcie_rd_own_conf() dw_pcie_wr_own_conf() dw_pcie_setup() dw_pcie_scan_bus() dw_pcie_map_irq() Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han --- drivers/pci/host/pcie-designware.c | 16 ++++++++-------- drivers/pci/host/pcie-designware.h | 7 ------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 896301788e9d..a2e69afabe6f 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -67,7 +67,7 @@ static struct hw_pci dw_pci; -unsigned long global_io_offset; +static unsigned long global_io_offset; static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) { @@ -118,8 +118,8 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg) writel(val, pp->dbi_base + reg); } -int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) +static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) { int ret; @@ -131,8 +131,8 @@ int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, return ret; } -int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) +static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) { int ret; @@ -667,7 +667,7 @@ static struct pci_ops dw_pcie_ops = { .write = dw_pcie_wr_conf, }; -int dw_pcie_setup(int nr, struct pci_sys_data *sys) +static int dw_pcie_setup(int nr, struct pci_sys_data *sys) { struct pcie_port *pp; @@ -690,7 +690,7 @@ int dw_pcie_setup(int nr, struct pci_sys_data *sys) return 1; } -struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) +static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) { struct pci_bus *bus; struct pcie_port *pp = sys_to_pcie(sys); @@ -707,7 +707,7 @@ struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) return bus; } -int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index d87fbaeffe70..890c2ced1abd 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -66,19 +66,12 @@ struct pcie_host_ops { void (*host_init)(struct pcie_port *pp); }; -extern unsigned long global_io_offset; - int cfg_read(void __iomem *addr, int where, int size, u32 *val); int cfg_write(void __iomem *addr, int where, int size, u32 val); -int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val); -int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val); void dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); -int dw_pcie_setup(int nr, struct pci_sys_data *sys); -struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys); -int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); #endif /* _PCIE_DESIGNWARE_H */ From 904d0e7889933fb48d921c998fd1cabb3a9d6635 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Wed, 9 Oct 2013 21:32:12 +0900 Subject: [PATCH 58/79] PCI: designware: Add irq_create_mapping() Without irq_create_mapping(), the correct IRQ number cannot be provided. In this case, it makes problems such as NULL dereference. Thus, irq_create_mapping() should be added for MSI. Suggested-by: Kishon Vijay Abraham I Signed-off-by: Pratyush Anand Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-designware.c | 25 +++++++++++++------------ drivers/pci/host/pcie-designware.h | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index a2e69afabe6f..1e1fea4d959b 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -157,7 +157,7 @@ static struct irq_chip dw_msi_irq_chip = { void dw_handle_msi_irq(struct pcie_port *pp) { unsigned long val; - int i, pos; + int i, pos, irq; for (i = 0; i < MAX_MSI_CTRLS; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, @@ -165,8 +165,9 @@ void dw_handle_msi_irq(struct pcie_port *pp) if (val) { pos = 0; while ((pos = find_next_bit(&val, 32, pos)) != 32) { - generic_handle_irq(pp->msi_irq_start - + (i * 32) + pos); + irq = irq_find_mapping(pp->irq_domain, + i * 32 + pos); + generic_handle_irq(irq); pos++; } } @@ -237,9 +238,8 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) } } - irq = (pp->msi_irq_start + pos0); - - if ((irq + no_irqs) > (pp->msi_irq_start + MAX_MSI_IRQS-1)) + irq = irq_find_mapping(pp->irq_domain, pos0); + if (!irq) goto no_valid_irq; i = 0; @@ -270,6 +270,7 @@ static void clear_irq(unsigned int irq) struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; + struct irq_data *data = irq_get_irq_data(irq); /* get the port structure */ desc = irq_to_desc(irq); @@ -280,7 +281,7 @@ static void clear_irq(unsigned int irq) return; } - pos = irq - pp->msi_irq_start; + pos = data->hwirq; irq_free_desc(irq); @@ -371,8 +372,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct of_pci_range range; struct of_pci_range_parser parser; u32 val; - - struct irq_domain *irq_domain; + int i; if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); @@ -441,15 +441,16 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } if (IS_ENABLED(CONFIG_PCI_MSI)) { - irq_domain = irq_domain_add_linear(pp->dev->of_node, + pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, MAX_MSI_IRQS, &msi_domain_ops, &dw_pcie_msi_chip); - if (!irq_domain) { + if (!pp->irq_domain) { dev_err(pp->dev, "irq domain init failed\n"); return -ENXIO; } - pp->msi_irq_start = irq_find_mapping(irq_domain, 0); + for (i = 0; i < MAX_MSI_IRQS; i++) + irq_create_mapping(pp->irq_domain, i); } if (pp->ops->host_init) diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 890c2ced1abd..c15379be2372 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -50,7 +50,7 @@ struct pcie_port { u32 lanes; struct pcie_host_ops *ops; int msi_irq; - int msi_irq_start; + struct irq_domain *irq_domain; unsigned long msi_data; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; From 6b49ee49af11890f83fa660a99cd95ac6dacd2fa Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 9 Oct 2013 14:39:08 -0600 Subject: [PATCH 59/79] MAINTAINERS: Add arch/x86/pci to PCI file patterns I handle many arch/x86/pci changes, so help those patches get to the PCI patchwork. Signed-off-by: Bjorn Helgaas Acked-by: H. Peter Anvin Acked-by: Ingo Molnar --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e61c2e83fc2b..18d6c2a7bd36 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6338,6 +6338,7 @@ S: Supported F: Documentation/PCI/ F: drivers/pci/ F: include/linux/pci* +F: arch/x86/pci/ PCI DRIVER FOR NVIDIA TEGRA M: Thierry Reding From 3ad674d6c6c5724c83f10f422d826420c83536cd Mon Sep 17 00:00:00 2001 From: Alexey Neyman Date: Wed, 9 Oct 2013 16:16:38 -0600 Subject: [PATCH 60/79] x86/PCI: Coalesce multiple overlapping host bridge windows Previously we coalesced windows by expanding the first overlapping one and making the second invalid. But we never look at the expanded first window again, so we fail to notice other windows that overlap it. For example, we coalesced these: [io 0x0000-0x03af] // #0 [io 0x03e0-0x0cf7] // #1 [io 0x0000-0xdfff] // #2 into these, which still overlap: [io 0x0000-0xdfff] // #0 [io 0x03e0-0x0cf7] // #1 The fix is to expand the *second* overlapping resource and ignore the first, so we get this instead with no overlaps: [io 0x0000-0xdfff] // #2 [bhelgaas: changelog] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=62511 Signed-off-by: Alexey Neyman Signed-off-by: Bjorn Helgaas --- arch/x86/pci/acpi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index b30e937689d6..7fb24e53d4c8 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -354,12 +354,12 @@ static void coalesce_windows(struct pci_root_info *info, unsigned long type) * the kernel resource tree doesn't allow overlaps. */ if (resource_overlaps(res1, res2)) { - res1->start = min(res1->start, res2->start); - res1->end = max(res1->end, res2->end); + res2->start = min(res1->start, res2->start); + res2->end = max(res1->end, res2->end); dev_info(&info->bridge->dev, "host bridge window expanded to %pR; %pR ignored\n", - res1, res2); - res2->flags = 0; + res2, res1); + res1->flags = 0; } } } From 9b5cd0948b67e1750498b5ff85267e87d3b4c5b3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 12 Oct 2013 14:11:02 +0800 Subject: [PATCH 61/79] PCI: imx6: Remove redundant dev_err() in imx6_pcie_probe() There is an error message within devm_ioremap_resource() already, so remove the dev_err() call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Bjorn Helgaas Reviewed-by: Jingoo Han Acked-by: Shawn Guo --- drivers/pci/host/pci-imx6.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 5afa9226a080..51338ac5775d 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -439,7 +439,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base); if (IS_ERR(pp->dbi_base)) { - dev_err(&pdev->dev, "unable to remap dbi_base\n"); ret = PTR_ERR(pp->dbi_base); goto err; } From 869a16157d1ac92a61770be0bc1cf83fbe99d724 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 10 Oct 2013 20:58:11 +0800 Subject: [PATCH 62/79] PCI: Fail MSI/MSI-X initialization if device is not in PCI_D0 Currently, pci_enable_msi() and pci_enable_msix() return success even if the device power state is not D0. However, we don't write the MSI message to the device registers, and the registers will never be updated later. This patch makes pci_enable_msi() and pci_enable_msix() return an error instead. [bhelgaas: changelog] Signed-off-by: Yijing Wang Acked-by: Ben Hutchings Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index d5f90d6383bc..604265c40853 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -831,7 +831,7 @@ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) int status, maxvec; u16 msgctl; - if (!dev->msi_cap) + if (!dev->msi_cap || dev->current_state != PCI_D0) return -EINVAL; pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); @@ -862,7 +862,7 @@ int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) int ret, nvec; u16 msgctl; - if (!dev->msi_cap) + if (!dev->msi_cap || dev->current_state != PCI_D0) return -EINVAL; pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); @@ -955,7 +955,7 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) int status, nr_entries; int i, j; - if (!entries || !dev->msix_cap) + if (!entries || !dev->msix_cap || dev->current_state != PCI_D0) return -EINVAL; status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX); From 4ec3ed7f5e91e9325c810dcb995ef5a55e4a79a6 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 17 Oct 2013 17:27:22 -0700 Subject: [PATCH 63/79] PCI: imx6: Fix imprecise abort handler An imprecise abort is triggered when a port behind a switch is accessed and no device is present. At enumeration, imprecise aborts are not enabled thus this ends up getting deferred until the kernel has completed init. At that point we must not adjust PC - the handler must do nothing, but a handler must exist. This fixes random crashes that occur right after freeing init. Tested-by: Marek Vasut Signed-off-by: Tim Harvey Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo Acked-by: Marek Vasut --- drivers/pci/host/pci-imx6.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 51338ac5775d..7426ad961b81 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -200,12 +200,6 @@ static int pcie_phy_write(void __iomem *dbi_base, int addr, int data) static int imx6q_pcie_abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { - /* - * If it was an imprecise abort, then we need to correct the - * return address to be _after_ the instruction. - */ - if (fsr & (1 << 10)) - regs->ARM_pc += 4; return 0; } From eb36309a9db9e9e8e8e0813ebbe3a14a58969351 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 21 Oct 2013 14:36:43 +0530 Subject: [PATCH 64/79] PCI: exynos: Remove redundant of_match_ptr This driver is DT only. Hence of_match_ptr is not required. Signed-off-by: Sachin Kamat Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han --- drivers/pci/host/pci-exynos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index 89804728bd5b..24beed38ddc7 100644 --- a/drivers/pci/host/pci-exynos.c +++ b/drivers/pci/host/pci-exynos.c @@ -653,7 +653,7 @@ static struct platform_driver exynos_pcie_driver = { .driver = { .name = "exynos-pcie", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(exynos_pcie_of_match), + .of_match_table = exynos_pcie_of_match, }, }; From 017f10e1c78e14d48be7a28f2c33a32dae15fee5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 22 Oct 2013 22:12:18 -0700 Subject: [PATCH 65/79] PCI: imx6: Increase link startup timeout A longer link startup timeout is required when certain PCI switches are attached to the root complex. This was tested with a Pericom switch and a PLX switch. Signed-off-by: Marek Vasut Signed-off-by: Bjorn Helgaas Acked-by: Tim Harvey Acked-by: Shawn Guo --- drivers/pci/host/pci-imx6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 7426ad961b81..baafb397a698 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -318,7 +318,7 @@ static void imx6_pcie_host_init(struct pcie_port *pp) while (!dw_pcie_link_up(pp)) { usleep_range(100, 1000); count++; - if (count >= 10) { + if (count >= 200) { dev_err(pp->dev, "phy link never came up\n"); dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", From 9ba205c384fc27cc7ff761744b012f1956a7e3e4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 22 Oct 2013 20:55:46 -0600 Subject: [PATCH 66/79] frv/PCI: Remove pcibios_last_bus pcibios_last_bus was apparently copied from x86. On FR-V, it is statically initialized to -1 and never changed unless the user boots with "pci=lastbus=". I doubt that option is used on FR-V, so this patch removes all the code related to pcibios_last_bus. Signed-off-by: Bjorn Helgaas --- arch/frv/mb93090-mb00/pci-frv.h | 1 - arch/frv/mb93090-mb00/pci-vdk.c | 36 --------------------------------- 2 files changed, 37 deletions(-) diff --git a/arch/frv/mb93090-mb00/pci-frv.h b/arch/frv/mb93090-mb00/pci-frv.h index 76c4e73d643d..a7e487fe76ed 100644 --- a/arch/frv/mb93090-mb00/pci-frv.h +++ b/arch/frv/mb93090-mb00/pci-frv.h @@ -30,7 +30,6 @@ void pcibios_resource_survey(void); /* pci-vdk.c */ -extern int __nongpreldata pcibios_last_bus; extern struct pci_ops *__nongpreldata pci_root_ops; /* pci-irq.c */ diff --git a/arch/frv/mb93090-mb00/pci-vdk.c b/arch/frv/mb93090-mb00/pci-vdk.c index deb67843693c..efa5d65b0007 100644 --- a/arch/frv/mb93090-mb00/pci-vdk.c +++ b/arch/frv/mb93090-mb00/pci-vdk.c @@ -25,7 +25,6 @@ unsigned int __nongpreldata pci_probe = 1; -int __nongpreldata pcibios_last_bus = -1; struct pci_ops *__nongpreldata pci_root_ops; /* @@ -219,37 +218,6 @@ static struct pci_ops * __init pci_check_direct(void) return NULL; } -/* - * Discover remaining PCI buses in case there are peer host bridges. - * We use the number of last PCI bus provided by the PCI BIOS. - */ -static void __init pcibios_fixup_peer_bridges(void) -{ - struct pci_bus bus; - struct pci_dev dev; - int n; - u16 l; - - if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff) - return; - printk("PCI: Peer bridge fixup\n"); - for (n=0; n <= pcibios_last_bus; n++) { - if (pci_find_bus(0, n)) - continue; - bus.number = n; - bus.ops = pci_root_ops; - dev.bus = &bus; - for(dev.devfn=0; dev.devfn<256; dev.devfn += 8) - if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) && - l != 0x0000 && l != 0xffff) { - printk("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l); - printk("PCI: Discovered peer bus %02x\n", n); - pci_scan_bus(n, pci_root_ops, NULL); - break; - } - } -} - /* * Exceptions for specific devices. Usually work-arounds for fatal design flaws. */ @@ -418,7 +386,6 @@ int __init pcibios_init(void) pci_scan_root_bus(NULL, 0, pci_root_ops, NULL, &resources); pcibios_irq_init(); - pcibios_fixup_peer_bridges(); pcibios_fixup_irqs(); pcibios_resource_survey(); @@ -432,9 +399,6 @@ char * __init pcibios_setup(char *str) if (!strcmp(str, "off")) { pci_probe = 0; return NULL; - } else if (!strncmp(str, "lastbus=", 8)) { - pcibios_last_bus = simple_strtol(str+8, NULL, 0); - return NULL; } return str; } From 004bd069f876816f5f9023e8b1498753bca103fa Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 22 Oct 2013 21:06:20 -0600 Subject: [PATCH 67/79] mn10300/PCI: Remove useless pcibios_last_bus pcibios_last_bus was apparently copied from x86. On mn10300, it is statically initialized to -1 and may be set with the "pci=lastbus=" boot option, but it is never tested. This patch removes everything related to pcibios_last_bus. Signed-off-by: Bjorn Helgaas --- arch/mn10300/unit-asb2305/pci-asb2305.h | 1 - arch/mn10300/unit-asb2305/pci.c | 5 ----- 2 files changed, 6 deletions(-) diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.h b/arch/mn10300/unit-asb2305/pci-asb2305.h index 7fa66a0e4624..9e17aca5a2a1 100644 --- a/arch/mn10300/unit-asb2305/pci-asb2305.h +++ b/arch/mn10300/unit-asb2305/pci-asb2305.h @@ -35,7 +35,6 @@ extern void pcibios_resource_survey(void); /* pci.c */ -extern int pcibios_last_bus; extern struct pci_ops *pci_root_ops; extern struct irq_routing_table *pcibios_get_irq_routing_table(void); diff --git a/arch/mn10300/unit-asb2305/pci.c b/arch/mn10300/unit-asb2305/pci.c index e37fac0461f3..6b4339f8c9c2 100644 --- a/arch/mn10300/unit-asb2305/pci.c +++ b/arch/mn10300/unit-asb2305/pci.c @@ -24,7 +24,6 @@ unsigned int pci_probe = 1; -int pcibios_last_bus = -1; struct pci_ops *pci_root_ops; /* @@ -392,10 +391,6 @@ char *__init pcibios_setup(char *str) if (!strcmp(str, "off")) { pci_probe = 0; return NULL; - - } else if (!strncmp(str, "lastbus=", 8)) { - pcibios_last_bus = simple_strtol(str+8, NULL, 0); - return NULL; } return str; From 0394cb192db4397753046775a8caa736397737b5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 16 Oct 2013 12:32:53 -0600 Subject: [PATCH 68/79] PCI: Report pci_pme_active() kmalloc failure Previously, if kmalloc() failed, we claimed "PME# enabled" in dmesg, even though we didn't add the device to the pci_pme_list. This prints a more correct warning. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/pci.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ad7fc72a40a0..36cc8d5ae8b2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1638,8 +1638,10 @@ void pci_pme_active(struct pci_dev *dev, bool enable) if (enable) { pme_dev = kmalloc(sizeof(struct pci_pme_device), GFP_KERNEL); - if (!pme_dev) - goto out; + if (!pme_dev) { + dev_warn(&dev->dev, "can't enable PME#\n"); + return; + } pme_dev->dev = dev; mutex_lock(&pci_pme_list_mutex); list_add(&pme_dev->list, &pci_pme_list); @@ -1660,7 +1662,6 @@ void pci_pme_active(struct pci_dev *dev, bool enable) } } -out: dev_dbg(&dev->dev, "PME# %s\n", enable ? "enabled" : "disabled"); } From 8bcadbe17207aee0df4a1f5cb41371d71bf3e4b0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 21 Oct 2013 14:36:41 +0530 Subject: [PATCH 69/79] PCI: imx6: Remove redundant of_match_ptr imx6_pcie_of_match is always compiled in because PCI_IMX6 depends on SOC_IMX6Q, which only supports OF build. Hence of_match_ptr is not required. [bhelgaas: add changelog details from Shawn] Signed-off-by: Sachin Kamat Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo Cc: Sean Cross --- drivers/pci/host/pci-imx6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index baafb397a698..0a435b8f54c2 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -551,7 +551,7 @@ static struct platform_driver imx6_pcie_driver = { .driver = { .name = "imx6q-pcie", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(imx6_pcie_of_match), + .of_match_table = imx6_pcie_of_match, }, }; From ba3eb9fce31571257f90c5e610ad74d6dd59b1f2 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Tue, 29 Oct 2013 20:12:51 +0400 Subject: [PATCH 70/79] PCI: Add R-Car Gen2 internal PCI support This adds internal PCI controller driver for R-Car Gen2 SoC. There are three PCI controllers available with only a single EHCI/OHCI device built-in on each PCI bus. This gives us three USB channels. Channel 0 is shared with the USBHS device, while channel 2 is shared with the USBSS. The PCI controllers do not support I/O port space mapping, and it is not needed here. Signed-off-by: Valentine Barshak Signed-off-by: Bjorn Helgaas --- drivers/pci/host/Kconfig | 8 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-rcar-gen2.c | 333 +++++++++++++++++++++++++++++++ 3 files changed, 342 insertions(+) create mode 100644 drivers/pci/host/pci-rcar-gen2.c diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 3d9504811126..2e33c2ba825f 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -19,4 +19,12 @@ config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA +config PCI_RCAR_GEN2 + bool "Renesas R-Car Gen2 Internal PCI controller" + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) + help + Say Y here if you want internal PCI support on R-Car Gen2 SoC. + There are 3 internal PCI controllers available with a single + built-in EHCI/OHCI host controller present on each one. + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index c9a997b2690d..63d972775289 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o +obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c new file mode 100644 index 000000000000..cbaa5c4397e3 --- /dev/null +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -0,0 +1,333 @@ +/* + * pci-rcar-gen2: internal PCI bus support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AHB-PCI Bridge PCI communication registers */ +#define RCAR_AHBPCI_PCICOM_OFFSET 0x800 + +#define RCAR_PCIAHB_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x00) +#define RCAR_PCIAHB_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x04) +#define RCAR_PCIAHB_PREFETCH0 0x0 +#define RCAR_PCIAHB_PREFETCH4 0x1 +#define RCAR_PCIAHB_PREFETCH8 0x2 +#define RCAR_PCIAHB_PREFETCH16 0x3 + +#define RCAR_AHBPCI_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x10) +#define RCAR_AHBPCI_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x14) +#define RCAR_AHBPCI_WIN_CTR_MEM (3 << 1) +#define RCAR_AHBPCI_WIN_CTR_CFG (5 << 1) +#define RCAR_AHBPCI_WIN1_HOST (1 << 30) +#define RCAR_AHBPCI_WIN1_DEVICE (1 << 31) + +#define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20) +#define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24) +#define RCAR_PCI_INT_A (1 << 16) +#define RCAR_PCI_INT_B (1 << 17) +#define RCAR_PCI_INT_PME (1 << 19) + +#define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30) +#define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0) +#define RCAR_AHB_BUS_MMODE_BYTE_BURST (1 << 1) +#define RCAR_AHB_BUS_MMODE_WR_INCR (1 << 2) +#define RCAR_AHB_BUS_MMODE_HBUS_REQ (1 << 7) +#define RCAR_AHB_BUS_SMODE_READYCTR (1 << 17) +#define RCAR_AHB_BUS_MODE (RCAR_AHB_BUS_MMODE_HTRANS | \ + RCAR_AHB_BUS_MMODE_BYTE_BURST | \ + RCAR_AHB_BUS_MMODE_WR_INCR | \ + RCAR_AHB_BUS_MMODE_HBUS_REQ | \ + RCAR_AHB_BUS_SMODE_READYCTR) + +#define RCAR_USBCTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x34) +#define RCAR_USBCTR_USBH_RST (1 << 0) +#define RCAR_USBCTR_PCICLK_MASK (1 << 1) +#define RCAR_USBCTR_PLL_RST (1 << 2) +#define RCAR_USBCTR_DIRPD (1 << 8) +#define RCAR_USBCTR_PCIAHB_WIN2_EN (1 << 9) +#define RCAR_USBCTR_PCIAHB_WIN1_256M (0 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_512M (1 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_1G (2 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_2G (3 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_MASK (3 << 10) + +#define RCAR_PCI_ARBITER_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x40) +#define RCAR_PCI_ARBITER_PCIREQ0 (1 << 0) +#define RCAR_PCI_ARBITER_PCIREQ1 (1 << 1) +#define RCAR_PCI_ARBITER_PCIBP_MODE (1 << 12) + +#define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48) + +/* Number of internal PCI controllers */ +#define RCAR_PCI_NR_CONTROLLERS 3 + +struct rcar_pci_priv { + void __iomem *reg; + struct resource io_res; + struct resource mem_res; + struct resource *cfg_res; + int irq; +}; + +/* PCI configuration space operations */ +static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct pci_sys_data *sys = bus->sysdata; + struct rcar_pci_priv *priv = sys->private_data; + int slot, val; + + if (sys->busnr != bus->number || PCI_FUNC(devfn)) + return NULL; + + /* Only one EHCI/OHCI device built-in */ + slot = PCI_SLOT(devfn); + if (slot > 2) + return NULL; + + val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG : + RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG; + + iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG); + return priv->reg + (slot >> 1) * 0x100 + where; +} + +static int rcar_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + void __iomem *reg = rcar_pci_cfg_base(bus, devfn, where); + + if (!reg) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *val = ioread8(reg); + break; + case 2: + *val = ioread16(reg); + break; + default: + *val = ioread32(reg); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int rcar_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + void __iomem *reg = rcar_pci_cfg_base(bus, devfn, where); + + if (!reg) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + iowrite8(val, reg); + break; + case 2: + iowrite16(val, reg); + break; + default: + iowrite32(val, reg); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +/* PCI interrupt mapping */ +static int __init rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct pci_sys_data *sys = dev->bus->sysdata; + struct rcar_pci_priv *priv = sys->private_data; + + return priv->irq; +} + +/* PCI host controller setup */ +static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) +{ + struct rcar_pci_priv *priv = sys->private_data; + void __iomem *reg = priv->reg; + u32 val; + + val = ioread32(reg + RCAR_PCI_UNIT_REV_REG); + pr_info("PCI: bus%u revision %x\n", sys->busnr, val); + + /* Disable Direct Power Down State and assert reset */ + val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD; + val |= RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST; + iowrite32(val, reg + RCAR_USBCTR_REG); + udelay(4); + + /* De-assert reset and set PCIAHB window1 size to 1GB */ + val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK | + RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST); + iowrite32(val | RCAR_USBCTR_PCIAHB_WIN1_1G, reg + RCAR_USBCTR_REG); + + /* Configure AHB master and slave modes */ + iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG); + + /* Configure PCI arbiter */ + val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG); + val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 | + RCAR_PCI_ARBITER_PCIBP_MODE; + iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); + + /* PCI-AHB mapping: 0x40000000-0x80000000 */ + iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16, + reg + RCAR_PCIAHB_WIN1_CTR_REG); + + /* AHB-PCI mapping: OHCI/EHCI registers */ + val = priv->mem_res.start | RCAR_AHBPCI_WIN_CTR_MEM; + iowrite32(val, reg + RCAR_AHBPCI_WIN2_CTR_REG); + + /* Enable AHB-PCI bridge PCI configuration access */ + iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG, + reg + RCAR_AHBPCI_WIN1_CTR_REG); + /* Set PCI-AHB Window1 address */ + iowrite32(0x40000000 | PCI_BASE_ADDRESS_MEM_PREFETCH, + reg + PCI_BASE_ADDRESS_1); + /* Set AHB-PCI bridge PCI communication area address */ + val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET; + iowrite32(val, reg + PCI_BASE_ADDRESS_0); + + val = ioread32(reg + PCI_COMMAND); + val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + iowrite32(val, reg + PCI_COMMAND); + + /* Enable PCI interrupts */ + iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME, + reg + RCAR_PCI_INT_ENABLE_REG); + + /* Add PCI resources */ + pci_add_resource(&sys->resources, &priv->io_res); + pci_add_resource(&sys->resources, &priv->mem_res); + + return 1; +} + +static struct pci_ops rcar_pci_ops = { + .read = rcar_pci_read_config, + .write = rcar_pci_write_config, +}; + +static struct hw_pci rcar_hw_pci __initdata = { + .map_irq = rcar_pci_map_irq, + .ops = &rcar_pci_ops, + .setup = rcar_pci_setup, +}; + +static int rcar_pci_count __initdata; + +static int __init rcar_pci_add_controller(struct rcar_pci_priv *priv) +{ + void **private_data; + int count; + + if (rcar_hw_pci.nr_controllers < rcar_pci_count) + goto add_priv; + + /* (Re)allocate private data pointer array if needed */ + count = rcar_pci_count + RCAR_PCI_NR_CONTROLLERS; + private_data = kzalloc(count * sizeof(void *), GFP_KERNEL); + if (!private_data) + return -ENOMEM; + + rcar_pci_count = count; + if (rcar_hw_pci.private_data) { + memcpy(private_data, rcar_hw_pci.private_data, + rcar_hw_pci.nr_controllers * sizeof(void *)); + kfree(rcar_hw_pci.private_data); + } + + rcar_hw_pci.private_data = private_data; + +add_priv: + /* Add private data pointer to the array */ + rcar_hw_pci.private_data[rcar_hw_pci.nr_controllers++] = priv; + return 0; +} + +static int __init rcar_pci_probe(struct platform_device *pdev) +{ + struct resource *cfg_res, *mem_res; + struct rcar_pci_priv *priv; + void __iomem *reg; + + cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(&pdev->dev, cfg_res); + if (!reg) + return -ENODEV; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!mem_res || !mem_res->start) + return -ENODEV; + + priv = devm_kzalloc(&pdev->dev, + sizeof(struct rcar_pci_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mem_res = *mem_res; + /* + * The controller does not support/use port I/O, + * so setup a dummy port I/O region here. + */ + priv->io_res.start = priv->mem_res.start; + priv->io_res.end = priv->mem_res.end; + priv->io_res.flags = IORESOURCE_IO; + + priv->cfg_res = cfg_res; + + priv->irq = platform_get_irq(pdev, 0); + priv->reg = reg; + + return rcar_pci_add_controller(priv); +} + +static struct platform_driver rcar_pci_driver = { + .driver = { + .name = "pci-rcar-gen2", + }, +}; + +static int __init rcar_pci_init(void) +{ + int retval; + + retval = platform_driver_probe(&rcar_pci_driver, rcar_pci_probe); + if (!retval) + pci_common_init(&rcar_hw_pci); + + /* Private data pointer array is not needed any more */ + kfree(rcar_hw_pci.private_data); + rcar_hw_pci.private_data = NULL; + + return retval; +} + +subsys_initcall(rcar_pci_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car Gen2 internal PCI"); +MODULE_AUTHOR("Valentine Barshak "); From f216f57ffe6eede3c8a763add65d331e688f8c56 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 15 Oct 2013 18:06:38 +0200 Subject: [PATCH 71/79] PCI: imx6: Probe the PCIe in fs_initcall() Probe the PCIe driver in fs_initcall() instead of module_init() to assure that pci_assign_unassigned_resources() will be called early. This function is called in dw_pcie_host_init(), which is in turn called from imx6_add_pcie_port(), which is called from imx6_pcie_probe(). If this is not called early, we will hit resource collisions since pcieport driver is then probed way too late. Signed-off-by: Marek Vasut Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo Cc: Frank Li Cc: Jingoo Han Cc: Mohit KUMAR Cc: Pratyush Anand Cc: Richard Zhu Cc: Sascha Hauer Cc: Sean Cross Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Tim Harvey Cc: Troy Kisky Cc: Yinghai Lu --- drivers/pci/host/pci-imx6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 0a435b8f54c2..bd70af8f31ac 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -561,7 +561,7 @@ static int __init imx6_pcie_init(void) { return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe); } -module_init(imx6_pcie_init); +fs_initcall(imx6_pcie_init); MODULE_AUTHOR("Sean Cross "); MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver"); From 6b87e700cd65120b70aaa097a8f4e7f22f1945ee Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 26 Oct 2013 18:23:25 +0100 Subject: [PATCH 72/79] PCI: Update pcie_ports 'auto' behavior for non-ACPI platforms The pcie_ports parameter, which defaults to 'auto', allows a user to specify if PCIe port services are disabled ('compat'), always enabled ('native'), or only used when allowed by the BIOS ('auto'). Where CONFIG_ACPI isn't enabled, as is often the case for non x86/ia64 platforms, the 'auto' behavior results in that of 'compat'. Thus in order to use port services on these platforms 'pcie_ports=native' must be added to the kernel command line. This patch results in the 'native' behavior being followed where 'auto' is selected and ACPI is not enabled. Signed-off-by: Andrew Murray Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_core.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 31063ac30992..08d131f7815b 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -260,13 +260,14 @@ static int get_port_device_capability(struct pci_dev *dev) if (pcie_ports_disabled) return 0; - err = pcie_port_platform_notify(dev, &cap_mask); - if (!pcie_ports_auto) { - cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP - | PCIE_PORT_SERVICE_VC; - if (pci_aer_available()) - cap_mask |= PCIE_PORT_SERVICE_AER; - } else if (err) { + cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP + | PCIE_PORT_SERVICE_VC; + if (pci_aer_available()) + cap_mask |= PCIE_PORT_SERVICE_AER; + + if (pcie_ports_auto) { + err = pcie_port_platform_notify(dev, &cap_mask); + if (err) return 0; } From 9437eca18170008a085a61bf96c99f2d923b0393 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 4 Nov 2013 16:51:31 -0700 Subject: [PATCH 73/79] powerpc/pci: Use pci_is_pcie() to simplify code [fix] Fix f0308261b1 ("powerpc/pci: Use pci_is_pcie() to simplify code"). I accidentally merged v2 instead of v3, so this adds the difference. Without this, "cap" is the left-over PCI-X capability offset, and we're using it as the PCIe capability offset. [bhelgaas: extracted v2->v3 diff] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- arch/powerpc/kernel/eeh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 6ebbe545b7a5..db86f97a0e92 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -195,7 +195,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) "EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { - eeh_ops->read_config(dn, cap+4*i, 4, &cfg); + eeh_ops->read_config(dn, dev->pcie_cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg); } From cf3e1feba7f906f233790f1806592730a88f584d Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 5 Nov 2013 13:34:38 -0700 Subject: [PATCH 74/79] PCI: Workaround missing pci_set_master in pci drivers Ben Herrenschmidt found that commit 928bea964827 ("PCI: Delay enabling bridges until they're needed") breaks PCI in some powerpc environments. The reason is that the PCIe port driver will call pci_enable_device() on the bridge, so the device is enabled, but skips pci_set_master because pcie_port_auto and no acpi on powerpc. Because of that, pci_enable_bridge() later on (called as a result of the child device driver doing pci_enable_device) will see the bridge as already enabled and will not call pci_set_master() on it. Fixed by add checking in pci_enable_bridge, and call pci_set_master if driver skip that. That will make the code more robot and wade off problem for missing pci_set_master in drivers. Reported-by: Benjamin Herrenschmidt Signed-off-by: Yinghai Lu Signed-off-by: Linus Torvalds --- drivers/pci/pci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 36cc8d5ae8b2..7a92d81c28e0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1155,8 +1155,14 @@ static void pci_enable_bridge(struct pci_dev *dev) pci_enable_bridge(dev->bus->self); - if (pci_is_enabled(dev)) + if (pci_is_enabled(dev)) { + if (!dev->is_busmaster) { + dev_warn(&dev->dev, "driver skip pci_set_master, fix it!\n"); + pci_set_master(dev); + } return; + } + retval = pci_enable_device(dev); if (retval) dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n", From fbeeb822f6f45cadf154d7b7cff1c13537cd799d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 5 Nov 2013 13:34:51 -0700 Subject: [PATCH 75/79] PCI: Drop warning about drivers that don't use pci_set_master() f41f064cf4 ("PCI: Workaround missing pci_set_master in pci drivers") made pci_enable_bridge() turn on bus mastering if the driver hadn't done so already. It also added a warning in this case. But there's no reason to warn about it unless it's actually a problem to enable bus mastering here. This patch drops the warning because I'm not aware of any such problem. Signed-off-by: Bjorn Helgaas CC: Paul Bolle --- drivers/pci/pci.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7a92d81c28e0..ac40f90cfd5f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1156,10 +1156,8 @@ static void pci_enable_bridge(struct pci_dev *dev) pci_enable_bridge(dev->bus->self); if (pci_is_enabled(dev)) { - if (!dev->is_busmaster) { - dev_warn(&dev->dev, "driver skip pci_set_master, fix it!\n"); + if (!dev->is_busmaster) pci_set_master(dev); - } return; } From f92d74c1f5afaff7cd1ea14ade8f1ba6b519e422 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Fri, 1 Nov 2013 14:34:55 -0500 Subject: [PATCH 76/79] PCI: Warn on driver probe return value greater than zero Ages ago, drivers could return values greater than zero from their probe function and this would be regarded as success. But after f3ec4f87d607 ("PCI: change device runtime PM settings for probe and remove") and 967577b06241 ("PCI/PM: Keep runtime PM enabled for unbound PCI devices"), we set dev->driver to NULL if the driver's probe function returns a value greater than zero. __pci_device_probe() treats this as success, and drivers can still mostly work even with dev->driver == NULL, but PCI power management doesn't work, and we don't call the driver's remove function on rmmod. To help catch these driver problems, issue a warning in this case. [bhelgaas: changelog] Signed-off-by: Stephen M. Cameron Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index b60fe6737f78..0929ae3e7830 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -264,11 +264,19 @@ static long local_pci_probe(void *_ddi) pm_runtime_get_sync(dev); pci_dev->driver = pci_drv; rc = pci_drv->probe(pci_dev, ddi->id); - if (rc) { + if (!rc) + return rc; + if (rc < 0) { pci_dev->driver = NULL; pm_runtime_put_sync(dev); + return rc; } - return rc; + /* + * Probe function should return < 0 for failure, 0 for success + * Treat values > 0 as success, but warn. + */ + dev_warn(dev, "Driver probe function unexpectedly returned %d\n", rc); + return 0; } static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, From 0e4ccb1505a9e29c50170742ce26ac4655baab2d Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 6 Nov 2013 16:16:56 -0500 Subject: [PATCH 77/79] PCI: Add x86_msi.msi_mask_irq() and msix_mask_irq() Certain platforms do not allow writes in the MSI-X BARs to setup or tear down vector values. To combat against the generic code trying to write to that and either silently being ignored or crashing due to the pagetables being marked R/O this patch introduces a platform override. Note that we keep two separate, non-weak, functions default_mask_msi_irqs() and default_mask_msix_irqs() for the behavior of the arch_mask_msi_irqs() and arch_mask_msix_irqs(), as the default behavior is needed by x86 PCI code. For Xen, which does not allow the guest to write to MSI-X tables - as the hypervisor is solely responsible for setting the vector values - we implement two nops. This fixes a Xen guest crash when passing a PCI device with MSI-X to the guest. See the bugzilla for more details. [bhelgaas: add bugzilla info] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=64581 Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Bjorn Helgaas CC: Sucheta Chakraborty CC: Zhenzhong Duan --- arch/x86/include/asm/x86_init.h | 3 +++ arch/x86/kernel/x86_init.c | 10 ++++++++++ arch/x86/pci/xen.c | 13 ++++++++++++- drivers/pci/msi.c | 22 ++++++++++++++++------ include/linux/msi.h | 2 ++ 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index 828a1565ba57..0f1be11e43d2 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -172,6 +172,7 @@ struct x86_platform_ops { struct pci_dev; struct msi_msg; +struct msi_desc; struct x86_msi_ops { int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type); @@ -182,6 +183,8 @@ struct x86_msi_ops { void (*teardown_msi_irqs)(struct pci_dev *dev); void (*restore_msi_irqs)(struct pci_dev *dev, int irq); int (*setup_hpet_msi)(unsigned int irq, unsigned int id); + u32 (*msi_mask_irq)(struct msi_desc *desc, u32 mask, u32 flag); + u32 (*msix_mask_irq)(struct msi_desc *desc, u32 flag); }; struct IO_APIC_route_entry; diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index 8ce0072cd700..021783b1f46a 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -116,6 +116,8 @@ struct x86_msi_ops x86_msi = { .teardown_msi_irqs = default_teardown_msi_irqs, .restore_msi_irqs = default_restore_msi_irqs, .setup_hpet_msi = default_setup_hpet_msi, + .msi_mask_irq = default_msi_mask_irq, + .msix_mask_irq = default_msix_mask_irq, }; /* MSI arch specific hooks */ @@ -138,6 +140,14 @@ void arch_restore_msi_irqs(struct pci_dev *dev, int irq) { x86_msi.restore_msi_irqs(dev, irq); } +u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) +{ + return x86_msi.msi_mask_irq(desc, mask, flag); +} +u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag) +{ + return x86_msi.msix_mask_irq(desc, flag); +} #endif struct x86_io_apic_ops x86_io_apic_ops = { diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 48e8461057ba..5eee4959785d 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -382,7 +382,14 @@ static void xen_teardown_msi_irq(unsigned int irq) { xen_destroy_irq(irq); } - +static u32 xen_nop_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) +{ + return 0; +} +static u32 xen_nop_msix_mask_irq(struct msi_desc *desc, u32 flag) +{ + return 0; +} #endif int __init pci_xen_init(void) @@ -406,6 +413,8 @@ int __init pci_xen_init(void) x86_msi.setup_msi_irqs = xen_setup_msi_irqs; x86_msi.teardown_msi_irq = xen_teardown_msi_irq; x86_msi.teardown_msi_irqs = xen_teardown_msi_irqs; + x86_msi.msi_mask_irq = xen_nop_msi_mask_irq; + x86_msi.msix_mask_irq = xen_nop_msix_mask_irq; #endif return 0; } @@ -485,6 +494,8 @@ int __init pci_xen_initial_domain(void) x86_msi.setup_msi_irqs = xen_initdom_setup_msi_irqs; x86_msi.teardown_msi_irq = xen_teardown_msi_irq; x86_msi.restore_msi_irqs = xen_initdom_restore_msi_irqs; + x86_msi.msi_mask_irq = xen_nop_msi_mask_irq; + x86_msi.msix_mask_irq = xen_nop_msix_mask_irq; #endif xen_setup_acpi_sci(); __acpi_register_gsi = acpi_register_gsi_xen; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 604265c40853..5e63645a7abe 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -185,7 +185,7 @@ static inline __attribute_const__ u32 msi_enabled_mask(u16 control) * reliably as devices without an INTx disable bit will then generate a * level IRQ which will never be cleared. */ -static u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) +u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { u32 mask_bits = desc->masked; @@ -199,9 +199,14 @@ static u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) return mask_bits; } +__weak u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) +{ + return default_msi_mask_irq(desc, mask, flag); +} + static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { - desc->masked = __msi_mask_irq(desc, mask, flag); + desc->masked = arch_msi_mask_irq(desc, mask, flag); } /* @@ -211,7 +216,7 @@ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) * file. This saves a few milliseconds when initialising devices with lots * of MSI-X interrupts. */ -static u32 __msix_mask_irq(struct msi_desc *desc, u32 flag) +u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag) { u32 mask_bits = desc->masked; unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + @@ -224,9 +229,14 @@ static u32 __msix_mask_irq(struct msi_desc *desc, u32 flag) return mask_bits; } +__weak u32 arch_msix_mask_irq(struct msi_desc *desc, u32 flag) +{ + return default_msix_mask_irq(desc, flag); +} + static void msix_mask_irq(struct msi_desc *desc, u32 flag) { - desc->masked = __msix_mask_irq(desc, flag); + desc->masked = arch_msix_mask_irq(desc, flag); } static void msi_set_mask_bit(struct irq_data *data, u32 flag) @@ -902,7 +912,7 @@ void pci_msi_shutdown(struct pci_dev *dev) pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &ctrl); mask = msi_capable_mask(ctrl); /* Keep cached state to be restored */ - __msi_mask_irq(desc, mask, ~mask); + arch_msi_mask_irq(desc, mask, ~mask); /* Restore dev->irq to its default pin-assertion irq */ dev->irq = desc->msi_attrib.default_irq; @@ -998,7 +1008,7 @@ void pci_msix_shutdown(struct pci_dev *dev) /* Return the device with MSI-X masked as initial states */ list_for_each_entry(entry, &dev->msi_list, list) { /* Keep cached states to be restored */ - __msix_mask_irq(entry, 1); + arch_msix_mask_irq(entry, 1); } msix_set_enable(dev, 0); diff --git a/include/linux/msi.h b/include/linux/msi.h index b17ead818aec..87cce50bd121 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -64,6 +64,8 @@ void arch_restore_msi_irqs(struct pci_dev *dev, int irq); void default_teardown_msi_irqs(struct pci_dev *dev); void default_restore_msi_irqs(struct pci_dev *dev, int irq); +u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); +u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag); struct msi_chip { struct module *owner; From c6bde215acfd637708142ae671843b6f0eadbc6d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 6 Nov 2013 10:11:48 -0700 Subject: [PATCH 78/79] PCI: Add pci_upstream_bridge() This adds a pci_upstream_bridge() interface to find the PCI-to-PCI bridge upstream from a device. This is typically just "dev->bus->self", but in the case of a VF on a virtual bus, we have to start from the corresponding PF. Returns NULL if there is no upstream PCI bridge, i.e., if the device is on a root bus. Signed-off-by: Bjorn Helgaas Acked-by: Yinghai Lu --- include/linux/pci.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/pci.h b/include/linux/pci.h index d3a888ae4b2e..835ec7bf6c05 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -480,6 +480,15 @@ static inline bool pci_is_root_bus(struct pci_bus *pbus) return !(pbus->parent); } +static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev) +{ + dev = pci_physfn(dev); + if (pci_is_root_bus(dev->bus)) + return NULL; + + return dev->bus->self; +} + #ifdef CONFIG_PCI_MSI static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) { From 79272138bd5c35edb9be5ae7b473a2741751a706 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 6 Nov 2013 10:00:51 -0700 Subject: [PATCH 79/79] PCI: Enable upstream bridges even for VFs on virtual buses Previously we enabled the upstream PCI-to-PCI bridge only when "dev->bus->self != NULL". In the case of a VF on a virtual bus, where "bus->self == NULL", we didn't enable the upstream bridge. This fixes that by enabling the upstream bridge of the PF corresponding to the VF. Signed-off-by: Bjorn Helgaas Acked-by: Yinghai Lu --- drivers/pci/pci.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ac40f90cfd5f..d3ed931c0770 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1148,12 +1148,12 @@ int pci_reenable_device(struct pci_dev *dev) static void pci_enable_bridge(struct pci_dev *dev) { + struct pci_dev *bridge; int retval; - if (!dev) - return; - - pci_enable_bridge(dev->bus->self); + bridge = pci_upstream_bridge(dev); + if (bridge) + pci_enable_bridge(bridge); if (pci_is_enabled(dev)) { if (!dev->is_busmaster) @@ -1170,6 +1170,7 @@ static void pci_enable_bridge(struct pci_dev *dev) static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) { + struct pci_dev *bridge; int err; int i, bars = 0; @@ -1188,7 +1189,9 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) if (atomic_inc_return(&dev->enable_cnt) > 1) return 0; /* already enabled */ - pci_enable_bridge(dev->bus->self); + bridge = pci_upstream_bridge(dev); + if (bridge) + pci_enable_bridge(bridge); /* only skip sriov related */ for (i = 0; i <= PCI_ROM_RESOURCE; i++)