diff options
Diffstat (limited to 'drivers/pci/host')
-rw-r--r-- | drivers/pci/host/Kconfig | 2 | ||||
-rw-r--r-- | drivers/pci/host/pci-dra7xx.c | 119 | ||||
-rw-r--r-- | drivers/pci/host/pcie-iproc.c | 60 | ||||
-rw-r--r-- | drivers/pci/host/pcie-iproc.h | 4 |
4 files changed, 153 insertions, 32 deletions
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index c132bddc03f3..e339a8c42f76 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -135,7 +135,7 @@ config PCIE_IPROC_PLATFORM through the generic platform bus interface config PCIE_IPROC_BCMA - bool "Broadcom iProc PCIe BCMA bus driver" + tristate "Broadcom iProc PCIe BCMA bus driver" depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST) select PCIE_IPROC select BCMA diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c index 1a0d124b199a..52e2da7b3cd7 100644 --- a/drivers/pci/host/pci-dra7xx.c +++ b/drivers/pci/host/pci-dra7xx.c @@ -17,6 +17,7 @@ #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_gpio.h> #include <linux/pci.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> @@ -83,6 +84,17 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, writel(value, pcie->base + offset); } +static inline u32 dra7xx_pcie_readl_rc(struct pcie_port *pp, u32 offset) +{ + return readl(pp->dbi_base + offset); +} + +static inline void dra7xx_pcie_writel_rc(struct pcie_port *pp, u32 offset, + u32 value) +{ + writel(value, pp->dbi_base + offset); +} + static int dra7xx_pcie_link_up(struct pcie_port *pp) { struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); @@ -324,6 +336,9 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; char name[10]; + int gpio_sel; + enum of_gpio_flags flags; + unsigned long gpio_flags; dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); if (!dra7xx) @@ -383,7 +398,23 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(dev); if (IS_ERR_VALUE(ret)) { dev_err(dev, "pm_runtime_get_sync failed\n"); - goto err_phy; + goto err_get_sync; + } + + gpio_sel = of_get_gpio_flags(dev->of_node, 0, &flags); + if (gpio_is_valid(gpio_sel)) { + gpio_flags = (flags & OF_GPIO_ACTIVE_LOW) ? + GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; + ret = devm_gpio_request_one(dev, gpio_sel, gpio_flags, + "pcie_reset"); + if (ret) { + dev_err(&pdev->dev, "gpio%d request failed, ret %d\n", + gpio_sel, ret); + goto err_gpio; + } + } else if (gpio_sel == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_gpio; } reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); @@ -394,12 +425,14 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) ret = dra7xx_add_pcie_port(dra7xx, pdev); if (ret < 0) - goto err_add_port; + goto err_gpio; return 0; -err_add_port: +err_gpio: pm_runtime_put(dev); + +err_get_sync: pm_runtime_disable(dev); err_phy: @@ -430,6 +463,85 @@ static int __exit dra7xx_pcie_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int dra7xx_pcie_suspend(struct device *dev) +{ + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + struct pcie_port *pp = &dra7xx->pp; + u32 val; + + /* clear MSE */ + val = dra7xx_pcie_readl_rc(pp, PCI_COMMAND); + val &= ~PCI_COMMAND_MEMORY; + dra7xx_pcie_writel_rc(pp, PCI_COMMAND, val); + + return 0; +} + +static int dra7xx_pcie_resume(struct device *dev) +{ + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + struct pcie_port *pp = &dra7xx->pp; + u32 val; + + /* set MSE */ + val = dra7xx_pcie_readl_rc(pp, PCI_COMMAND); + val |= PCI_COMMAND_MEMORY; + dra7xx_pcie_writel_rc(pp, PCI_COMMAND, val); + + return 0; +} + +static int dra7xx_pcie_suspend_noirq(struct device *dev) +{ + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + int count = dra7xx->phy_count; + + while (count--) { + phy_power_off(dra7xx->phy[count]); + phy_exit(dra7xx->phy[count]); + } + + return 0; +} + +static int dra7xx_pcie_resume_noirq(struct device *dev) +{ + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + int phy_count = dra7xx->phy_count; + int ret; + int i; + + for (i = 0; i < phy_count; i++) { + ret = phy_init(dra7xx->phy[i]); + if (ret < 0) + goto err_phy; + + ret = phy_power_on(dra7xx->phy[i]); + if (ret < 0) { + phy_exit(dra7xx->phy[i]); + goto err_phy; + } + } + + return 0; + +err_phy: + while (--i >= 0) { + phy_power_off(dra7xx->phy[i]); + phy_exit(dra7xx->phy[i]); + } + + return ret; +} +#endif + +static const struct dev_pm_ops dra7xx_pcie_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend, dra7xx_pcie_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend_noirq, + dra7xx_pcie_resume_noirq) +}; + static const struct of_device_id of_dra7xx_pcie_match[] = { { .compatible = "ti,dra7-pcie", }, {}, @@ -441,6 +553,7 @@ static struct platform_driver dra7xx_pcie_driver = { .driver = { .name = "dra7-pcie", .of_match_table = of_dra7xx_pcie_match, + .pm = &dra7xx_pcie_pm_ops, }, }; diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index d77481ea553e..fe2efb141a9b 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -58,9 +58,17 @@ #define SYS_RC_INTX_EN 0x330 #define SYS_RC_INTX_MASK 0xf -static inline struct iproc_pcie *sys_to_pcie(struct pci_sys_data *sys) +static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) { - return sys->private_data; + struct iproc_pcie *pcie; +#ifdef CONFIG_ARM + struct pci_sys_data *sys = bus->sysdata; + + pcie = sys->private_data; +#else + pcie = bus->sysdata; +#endif + return pcie; } /** @@ -71,8 +79,7 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus, unsigned int devfn, int where) { - struct pci_sys_data *sys = bus->sysdata; - struct iproc_pcie *pcie = sys_to_pcie(sys); + struct iproc_pcie *pcie = iproc_data(bus); unsigned slot = PCI_SLOT(devfn); unsigned fn = PCI_FUNC(devfn); unsigned busno = bus->number; @@ -186,32 +193,34 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie) int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) { int ret; + void *sysdata; struct pci_bus *bus; if (!pcie || !pcie->dev || !pcie->base) return -EINVAL; - if (pcie->phy) { - ret = phy_init(pcie->phy); - if (ret) { - dev_err(pcie->dev, "unable to initialize PCIe PHY\n"); - return ret; - } - - ret = phy_power_on(pcie->phy); - if (ret) { - dev_err(pcie->dev, "unable to power on PCIe PHY\n"); - goto err_exit_phy; - } + ret = phy_init(pcie->phy); + if (ret) { + dev_err(pcie->dev, "unable to initialize PCIe PHY\n"); + return ret; + } + ret = phy_power_on(pcie->phy); + if (ret) { + dev_err(pcie->dev, "unable to power on PCIe PHY\n"); + goto err_exit_phy; } iproc_pcie_reset(pcie); +#ifdef CONFIG_ARM pcie->sysdata.private_data = pcie; + sysdata = &pcie->sysdata; +#else + sysdata = pcie; +#endif - bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops, - &pcie->sysdata, res); + bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops, sysdata, res); if (!bus) { dev_err(pcie->dev, "unable to create PCI root bus\n"); ret = -ENOMEM; @@ -229,7 +238,9 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) pci_scan_child_bus(bus); pci_assign_unassigned_bus_resources(bus); +#ifdef CONFIG_ARM pci_fixup_irqs(pci_common_swizzle, pcie->map_irq); +#endif pci_bus_add_devices(bus); return 0; @@ -239,12 +250,9 @@ err_rm_root_bus: pci_remove_root_bus(bus); err_power_off_phy: - if (pcie->phy) - phy_power_off(pcie->phy); + phy_power_off(pcie->phy); err_exit_phy: - if (pcie->phy) - phy_exit(pcie->phy); - + phy_exit(pcie->phy); return ret; } EXPORT_SYMBOL(iproc_pcie_setup); @@ -254,10 +262,8 @@ int iproc_pcie_remove(struct iproc_pcie *pcie) pci_stop_root_bus(pcie->root_bus); pci_remove_root_bus(pcie->root_bus); - if (pcie->phy) { - phy_power_off(pcie->phy); - phy_exit(pcie->phy); - } + phy_power_off(pcie->phy); + phy_exit(pcie->phy); return 0; } diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h index ba0a108309cc..c9e4c10a462e 100644 --- a/drivers/pci/host/pcie-iproc.h +++ b/drivers/pci/host/pcie-iproc.h @@ -21,7 +21,7 @@ * @dev: pointer to device data structure * @base: PCIe host controller I/O register base * @resources: linked list of all PCI resources - * @sysdata: Per PCI controller data + * @sysdata: Per PCI controller data (ARM-specific) * @root_bus: pointer to root bus * @phy: optional PHY device that controls the Serdes * @irqs: interrupt IDs @@ -29,7 +29,9 @@ struct iproc_pcie { struct device *dev; void __iomem *base; +#ifdef CONFIG_ARM struct pci_sys_data sysdata; +#endif struct pci_bus *root_bus; struct phy *phy; int irqs[IPROC_PCIE_MAX_NUM_IRQS]; |