diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-14 05:02:00 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-14 05:02:00 (GMT) |
commit | 2f466d33f5f60542d3d82c0477de5863b22c94b9 (patch) | |
tree | 839972958941d55852a9af270b04af09e3147116 /drivers/pci | |
parent | f9300eaaac1ca300083ad41937923a90cc3a2394 (diff) | |
parent | eaaeb1cb33310dea6c3fa45d8bfc63e5a1d0a846 (diff) | |
download | linux-2f466d33f5f60542d3d82c0477de5863b22c94b9.tar.xz |
Merge tag 'pci-v3.13-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI changes from Bjorn Helgaas:
"Resource management
- Fix host bridge window coalescing (Alexey Neyman)
- Pass type, width, and prefetchability for window alignment (Wei Yang)
PCI device hotplug
- Convert acpiphp, acpiphp_ibm to dynamic debug (Lan Tianyu)
Power management
- Remove pci_pm_complete() (Liu Chuansheng)
MSI
- Fail initialization if device is not in PCI_D0 (Yijing Wang)
MPS (Max Payload Size)
- Use pcie_get_mps() and pcie_set_mps() to simplify code (Yijing Wang)
- Use pcie_set_readrq() to simplify code (Yijing Wang)
- Use cached pci_dev->pcie_mpss to simplify code (Yijing Wang)
SR-IOV
- Enable upstream bridges even for VFs on virtual buses (Bjorn Helgaas)
- Use pci_is_root_bus() to avoid catching virtual buses (Wei Yang)
Virtualization
- Add x86 MSI masking ops (Konrad Rzeszutek Wilk)
Freescale i.MX6
- Support i.MX6 PCIe controller (Sean Cross)
- Increase link startup timeout (Marek Vasut)
- Probe PCIe in fs_initcall() (Marek Vasut)
- Fix imprecise abort handler (Tim Harvey)
- Remove redundant of_match_ptr (Sachin Kamat)
Renesas R-Car
- Support Gen2 internal PCIe controller (Valentine Barshak)
Samsung Exynos
- Add MSI support (Jingoo Han)
- Turn off power when link fails (Jingoo Han)
- Add Jingoo Han as maintainer (Jingoo Han)
- Add clk_disable_unprepare() on error path (Wei Yongjun)
- Remove redundant of_match_ptr (Sachin Kamat)
Synopsys DesignWare
- Add irq_create_mapping() (Pratyush Anand)
- Add header guards (Seungwon Jeon)
Miscellaneous
- Enable native PCIe services by default on non-ACPI (Andrew Murray)
- Cleanup _OSC usage and messages (Bjorn Helgaas)
- Remove pcibios_last_bus boot option on non-x86 (Bjorn Helgaas)
- Convert bus code to use bus_, drv_, and dev_groups (Greg Kroah-Hartman)
- Remove unused pci_mem_start (Myron Stowe)
- Make sysfs functions static (Sachin Kamat)
- Warn on invalid return from driver probe (Stephen M. Cameron)
- Remove Intel Haswell D3 delays (Todd E Brandt)
- Call pci_set_master() in core if driver doesn't do it (Yinghai Lu)
- Use pci_is_pcie() to simplify code (Yijing Wang)
- Use PCIe capability accessors to simplify code (Yijing Wang)
- Use cached pci_dev->pcie_cap to simplify code (Yijing Wang)
- Removed unused "is_pcie" from struct pci_dev (Yijing Wang)
- Simplify sysfs CPU affinity implementation (Yijing Wang)"
* tag 'pci-v3.13-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (79 commits)
PCI: Enable upstream bridges even for VFs on virtual buses
PCI: Add pci_upstream_bridge()
PCI: Add x86_msi.msi_mask_irq() and msix_mask_irq()
PCI: Warn on driver probe return value greater than zero
PCI: Drop warning about drivers that don't use pci_set_master()
PCI: Workaround missing pci_set_master in pci drivers
powerpc/pci: Use pci_is_pcie() to simplify code [fix]
PCI: Update pcie_ports 'auto' behavior for non-ACPI platforms
PCI: imx6: Probe the PCIe in fs_initcall()
PCI: Add R-Car Gen2 internal PCI support
PCI: imx6: Remove redundant of_match_ptr
PCI: Report pci_pme_active() kmalloc failure
mn10300/PCI: Remove useless pcibios_last_bus
frv/PCI: Remove pcibios_last_bus
PCI: imx6: Increase link startup timeout
PCI: exynos: Remove redundant of_match_ptr
PCI: imx6: Fix imprecise abort handler
PCI: Fail MSI/MSI-X initialization if device is not in PCI_D0
PCI: imx6: Remove redundant dev_err() in imx6_pcie_probe()
x86/PCI: Coalesce multiple overlapping host bridge windows
...
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/host/Kconfig | 14 | ||||
-rw-r--r-- | drivers/pci/host/Makefile | 2 | ||||
-rw-r--r-- | drivers/pci/host/pci-exynos.c | 132 | ||||
-rw-r--r-- | drivers/pci/host/pci-imx6.c | 568 | ||||
-rw-r--r-- | drivers/pci/host/pci-rcar-gen2.c | 333 | ||||
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 4 | ||||
-rw-r--r-- | drivers/pci/host/pcie-designware.c | 257 | ||||
-rw-r--r-- | drivers/pci/host/pcie-designware.h | 26 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpi_pcihp.c | 2 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp.h | 10 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_core.c | 37 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 23 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_ibm.c | 58 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp.h | 2 | ||||
-rw-r--r-- | drivers/pci/msi.c | 28 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 25 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 118 | ||||
-rw-r--r-- | drivers/pci/pci.c | 28 | ||||
-rw-r--r-- | drivers/pci/pci.h | 2 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 15 | ||||
-rw-r--r-- | drivers/pci/probe.c | 4 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 23 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 4 |
23 files changed, 1519 insertions, 196 deletions
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 43186fe..47d46c6 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -15,8 +15,22 @@ 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 +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 c9a997b..13fb333 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -1,4 +1,6 @@ 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 +obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index 94e096b..24beed3 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 @@ -77,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) @@ -202,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); @@ -270,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); @@ -302,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; } @@ -342,9 +411,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 +526,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; @@ -487,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) @@ -535,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, }, }; diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c new file mode 100644 index 0000000..bd70af8 --- /dev/null +++ b/drivers/pci/host/pci-imx6.c @@ -0,0 +1,568 @@ +/* + * PCIe host controller driver for Freescale i.MX6 SoCs + * + * Copyright (C) 2013 Kosagi + * http://www.kosagi.com + * + * Author: Sean Cross <xobs@kosagi.com> + * + * 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 <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/resource.h> +#include <linux/signal.h> +#include <linux/types.h> + +#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) +{ + 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 >= 200) { + 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)) { + 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 = 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); +} +fs_initcall(imx6_pcie_init); + +MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); +MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c new file mode 100644 index 0000000..cbaa5c4 --- /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 <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* 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 <valentine.barshak@cogentembedded.com>"); diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 2e9888a..7c4f38d 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, diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index c10e9ac..1e1fea4 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 <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/msi.h> #include <linux/of_address.h> #include <linux/pci.h> #include <linux/pci_regs.h> @@ -64,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) { @@ -115,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; @@ -128,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; @@ -142,6 +145,205 @@ 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, irq; + + 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) { + irq = irq_find_mapping(pp->irq_domain, + i * 32 + pos); + generic_handle_irq(irq); + 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 = irq_find_mapping(pp->irq_domain, pos0); + if (!irq) + 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; + struct irq_data *data = irq_get_irq_data(irq); + + /* 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 = data->hwirq; + + 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,12 +352,27 @@ 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; struct of_pci_range range; struct of_pci_range_parser parser; u32 val; + int i; if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); @@ -223,6 +440,19 @@ int __init dw_pcie_host_init(struct pcie_port *pp) return -EINVAL; } + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, + MAX_MSI_IRQS, &msi_domain_ops, + &dw_pcie_msi_chip); + if (!pp->irq_domain) { + dev_err(pp->dev, "irq domain init failed\n"); + return -ENXIO; + } + + for (i = 0; i < MAX_MSI_IRQS; i++) + irq_create_mapping(pp->irq_domain, i); + } + if (pp->ops->host_init) pp->ops->host_init(pp); @@ -438,7 +668,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; @@ -461,7 +691,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); @@ -478,17 +708,28 @@ 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); 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 133820f..c15379b 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; @@ -20,6 +23,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 +49,10 @@ struct pcie_port { int irq; u32 lanes; struct pcie_host_ops *ops; + int msi_irq; + struct irq_domain *irq_domain; + unsigned long msi_data; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; struct pcie_host_ops { @@ -51,15 +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 */ diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 5440131..1ce8ee0 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/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index f4e0289..26100f5 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -39,16 +39,6 @@ #include <linux/mutex.h> #include <linux/pci_hotplug.h> -#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 bf2203e..8650d39 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 <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -43,12 +45,9 @@ #include <linux/smp.h> #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 4a0a9ac..5b4e9eb 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 <linux/init.h> #include <linux/module.h> @@ -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) @@ -826,8 +826,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 { @@ -841,7 +842,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 { @@ -862,7 +863,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; } diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 2f5786c..0d64c41 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 <linux/init.h> #include <linux/slab.h> #include <linux/module.h> @@ -43,23 +45,11 @@ #define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>" #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); } diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index e260f20..d876e4b 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 <linux/pci-acpi.h> 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/drivers/pci/msi.c b/drivers/pci/msi.c index d5f90d6..5e63645 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) @@ -831,7 +841,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 +872,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); @@ -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; @@ -955,7 +965,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); @@ -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/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 38f3c01..4548535 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -267,11 +267,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, @@ -602,18 +610,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 */ @@ -1124,9 +1124,8 @@ 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, .resume = pci_pm_resume, .freeze = pci_pm_freeze, @@ -1319,7 +1318,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 d8eb880bd..2aaa83c 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,10 +74,13 @@ 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) -{ +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; @@ -86,30 +90,28 @@ static ssize_t local_cpus_show(struct device *dev, #else mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); #endif - len = cpumask_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 DEVICE_ATTR_RO(local_cpus); static ssize_t local_cpulist_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 = cpulist_scnprintf(buf, PAGE_SIZE-2, mask); - buf[len++] = '\n'; - buf[len] = '\0'; - return len; + return pci_dev_show_local_cpu(dev, 0, attr, buf); } +static DEVICE_ATTR_RO(local_cpulist); /* * PCI Bus Class Devices @@ -170,6 +172,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 +184,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 +212,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 +228,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 +238,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 +246,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 +291,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, @@ -304,7 +313,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, }; @@ -335,8 +344,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) { @@ -366,8 +376,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, @@ -414,6 +425,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 +511,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), +static 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[] = { @@ -554,7 +574,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, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bdd64b1..b127fbda 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1148,18 +1148,16 @@ 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) { - dev_warn(&dev->dev, "driver skip pci_set_master, fix it!\n"); + if (!dev->is_busmaster) pci_set_master(dev); - } return; } @@ -1172,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; @@ -1190,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++) @@ -1644,8 +1645,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); @@ -1666,7 +1669,6 @@ void pci_pme_active(struct pci_dev *dev, bool enable) } } -out: dev_dbg(&dev->dev, "PME# %s\n", enable ? "enabled" : "disabled"); } @@ -2860,7 +2862,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); } @@ -3978,6 +3980,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 @@ -4002,6 +4005,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 diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 607be58..9c91ecc 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[]; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 31063ac..08d131f 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; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7ef0f86..5e14f5a 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; @@ -984,7 +983,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/drivers/pci/quirks.c b/drivers/pci/quirks.c index f6c31fa..9149045 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2955,6 +2955,29 @@ 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 * support this feature. diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index bc26d79..4ce83b2 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; @@ -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) { |