diff options
Diffstat (limited to 'drivers/pci/host/pci-mvebu.c')
-rw-r--r-- | drivers/pci/host/pci-mvebu.c | 235 |
1 files changed, 149 insertions, 86 deletions
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 729d5a101d62..80b2250ea19a 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -9,13 +9,17 @@ #include <linux/kernel.h> #include <linux/pci.h> #include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> #include <linux/module.h> #include <linux/mbus.h> +#include <linux/msi.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/of_address.h> -#include <linux/of_pci.h> #include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/of_pci.h> #include <linux/of_platform.h> /* @@ -103,6 +107,7 @@ struct mvebu_pcie_port; struct mvebu_pcie { struct platform_device *pdev; struct mvebu_pcie_port *ports; + struct msi_chip *msi; struct resource io; struct resource realio; struct resource mem; @@ -115,7 +120,6 @@ struct mvebu_pcie_port { char *name; void __iomem *base; spinlock_t conf_lock; - int haslink; u32 port; u32 lane; int devfn; @@ -124,6 +128,9 @@ struct mvebu_pcie_port { unsigned int io_target; unsigned int io_attr; struct clk *clk; + int reset_gpio; + int reset_active_low; + char *reset_name; struct mvebu_sw_pci_bridge bridge; struct device_node *dn; struct mvebu_pcie *pcie; @@ -133,29 +140,39 @@ struct mvebu_pcie_port { size_t iowin_size; }; +static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) +{ + writel(val, port->base + reg); +} + +static inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg) +{ + return readl(port->base + reg); +} + static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) { - return !(readl(port->base + PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); + return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); } static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr) { u32 stat; - stat = readl(port->base + PCIE_STAT_OFF); + stat = mvebu_readl(port, PCIE_STAT_OFF); stat &= ~PCIE_STAT_BUS; stat |= nr << 8; - writel(stat, port->base + PCIE_STAT_OFF); + mvebu_writel(port, stat, PCIE_STAT_OFF); } static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr) { u32 stat; - stat = readl(port->base + PCIE_STAT_OFF); + stat = mvebu_readl(port, PCIE_STAT_OFF); stat &= ~PCIE_STAT_DEV; stat |= nr << 16; - writel(stat, port->base + PCIE_STAT_OFF); + mvebu_writel(port, stat, PCIE_STAT_OFF); } /* @@ -163,7 +180,7 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr) * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks * WIN[0-3] -> DRAM bank[0-3] */ -static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) +static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) { const struct mbus_dram_target_info *dram; u32 size; @@ -173,33 +190,34 @@ static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) /* First, disable and clear BARs and windows. */ for (i = 1; i < 3; i++) { - writel(0, port->base + PCIE_BAR_CTRL_OFF(i)); - writel(0, port->base + PCIE_BAR_LO_OFF(i)); - writel(0, port->base + PCIE_BAR_HI_OFF(i)); + mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i)); + mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i)); + mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i)); } for (i = 0; i < 5; i++) { - writel(0, port->base + PCIE_WIN04_CTRL_OFF(i)); - writel(0, port->base + PCIE_WIN04_BASE_OFF(i)); - writel(0, port->base + PCIE_WIN04_REMAP_OFF(i)); + mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i)); + mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i)); + mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i)); } - writel(0, port->base + PCIE_WIN5_CTRL_OFF); - writel(0, port->base + PCIE_WIN5_BASE_OFF); - writel(0, port->base + PCIE_WIN5_REMAP_OFF); + mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF); + mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF); + mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF); /* Setup windows for DDR banks. Count total DDR size on the fly. */ size = 0; for (i = 0; i < dram->num_cs; i++) { const struct mbus_dram_window *cs = dram->cs + i; - writel(cs->base & 0xffff0000, - port->base + PCIE_WIN04_BASE_OFF(i)); - writel(0, port->base + PCIE_WIN04_REMAP_OFF(i)); - writel(((cs->size - 1) & 0xffff0000) | - (cs->mbus_attr << 8) | - (dram->mbus_dram_target_id << 4) | 1, - port->base + PCIE_WIN04_CTRL_OFF(i)); + mvebu_writel(port, cs->base & 0xffff0000, + PCIE_WIN04_BASE_OFF(i)); + mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i)); + mvebu_writel(port, + ((cs->size - 1) & 0xffff0000) | + (cs->mbus_attr << 8) | + (dram->mbus_dram_target_id << 4) | 1, + PCIE_WIN04_CTRL_OFF(i)); size += cs->size; } @@ -209,41 +227,40 @@ static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) size = 1 << fls(size); /* Setup BAR[1] to all DRAM banks. */ - writel(dram->cs[0].base, port->base + PCIE_BAR_LO_OFF(1)); - writel(0, port->base + PCIE_BAR_HI_OFF(1)); - writel(((size - 1) & 0xffff0000) | 1, - port->base + PCIE_BAR_CTRL_OFF(1)); + mvebu_writel(port, dram->cs[0].base, PCIE_BAR_LO_OFF(1)); + mvebu_writel(port, 0, PCIE_BAR_HI_OFF(1)); + mvebu_writel(port, ((size - 1) & 0xffff0000) | 1, + PCIE_BAR_CTRL_OFF(1)); } -static void __init mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) +static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) { - u16 cmd; - u32 mask; + u32 cmd, mask; /* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins(port); /* Master + slave enable. */ - cmd = readw(port->base + PCIE_CMD_OFF); + cmd = mvebu_readl(port, PCIE_CMD_OFF); cmd |= PCI_COMMAND_IO; cmd |= PCI_COMMAND_MEMORY; cmd |= PCI_COMMAND_MASTER; - writew(cmd, port->base + PCIE_CMD_OFF); + mvebu_writel(port, cmd, PCIE_CMD_OFF); /* Enable interrupt lines A-D. */ - mask = readl(port->base + PCIE_MASK_OFF); + mask = mvebu_readl(port, PCIE_MASK_OFF); mask |= PCIE_MASK_ENABLE_INTS; - writel(mask, port->base + PCIE_MASK_OFF); + mvebu_writel(port, mask, PCIE_MASK_OFF); } static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, struct pci_bus *bus, u32 devfn, int where, int size, u32 *val) { - writel(PCIE_CONF_ADDR(bus->number, devfn, where), - port->base + PCIE_CONF_ADDR_OFF); + mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), + PCIE_CONF_ADDR_OFF); - *val = readl(port->base + PCIE_CONF_DATA_OFF); + *val = mvebu_readl(port, PCIE_CONF_DATA_OFF); if (size == 1) *val = (*val >> (8 * (where & 3))) & 0xff; @@ -257,21 +274,24 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, struct pci_bus *bus, u32 devfn, int where, int size, u32 val) { - int ret = PCIBIOS_SUCCESSFUL; + u32 _val, shift = 8 * (where & 3); - writel(PCIE_CONF_ADDR(bus->number, devfn, where), - port->base + PCIE_CONF_ADDR_OFF); + mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), + PCIE_CONF_ADDR_OFF); + _val = mvebu_readl(port, PCIE_CONF_DATA_OFF); if (size == 4) - writel(val, port->base + PCIE_CONF_DATA_OFF); + _val = val; else if (size == 2) - writew(val, port->base + PCIE_CONF_DATA_OFF + (where & 3)); + _val = (_val & ~(0xffff << shift)) | ((val & 0xffff) << shift); else if (size == 1) - writeb(val, port->base + PCIE_CONF_DATA_OFF + (where & 3)); + _val = (_val & ~(0xff << shift)) | ((val & 0xff) << shift); else - ret = PCIBIOS_BAD_REGISTER_NUMBER; + return PCIBIOS_BAD_REGISTER_NUMBER; - return ret; + mvebu_writel(port, _val, PCIE_CONF_DATA_OFF); + + return PCIBIOS_SUCCESSFUL; } static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) @@ -552,7 +572,7 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, if (bus->number == 0) return mvebu_sw_pci_bridge_write(port, where, size, val); - if (!port->haslink) + if (!mvebu_pcie_link_up(port)) return PCIBIOS_DEVICE_NOT_FOUND; /* @@ -594,7 +614,7 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, if (bus->number == 0) return mvebu_sw_pci_bridge_read(port, where, size, val); - if (!port->haslink) { + if (!mvebu_pcie_link_up(port)) { *val = 0xffffffff; return PCIBIOS_DEVICE_NOT_FOUND; } @@ -626,7 +646,7 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; -static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys) +static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) { struct mvebu_pcie *pcie = sys_to_pcie(sys); int i; @@ -645,7 +665,7 @@ static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys) return 1; } -static int __init mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct of_irq oirq; int ret; @@ -673,11 +693,17 @@ static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys) return bus; } -resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, - const struct resource *res, - resource_size_t start, - resource_size_t size, - resource_size_t align) +static void mvebu_pcie_add_bus(struct pci_bus *bus) +{ + struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata); + bus->msi = pcie->msi; +} + +static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, + const struct resource *res, + resource_size_t start, + resource_size_t size, + resource_size_t align) { if (dev->bus->number != 0) return start; @@ -696,7 +722,7 @@ resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, return start; } -static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie) +static void mvebu_pcie_enable(struct mvebu_pcie *pcie) { struct hw_pci hw; @@ -709,6 +735,7 @@ static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie) hw.map_irq = mvebu_pcie_map_irq; hw.ops = &mvebu_pcie_ops; hw.align_resource = mvebu_pcie_align_resource; + hw.add_bus = mvebu_pcie_add_bus; pci_common_init(&hw); } @@ -718,10 +745,8 @@ static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie) * <...> property for one that matches the given port/lane. Once * found, maps it. */ -static void __iomem * __init -mvebu_pcie_map_registers(struct platform_device *pdev, - struct device_node *np, - struct mvebu_pcie_port *port) +static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev, + struct device_node *np, struct mvebu_pcie_port *port) { struct resource regs; int ret = 0; @@ -777,7 +802,22 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn, return -ENOENT; } -static int __init mvebu_pcie_probe(struct platform_device *pdev) +static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie) +{ + struct device_node *msi_node; + + msi_node = of_parse_phandle(pcie->pdev->dev.of_node, + "msi-parent", 0); + if (!msi_node) + return; + + pcie->msi = of_pci_find_msi_chip_by_node(msi_node); + + if (pcie->msi) + pcie->msi->dev = &pcie->pdev->dev; +} + +static int mvebu_pcie_probe(struct platform_device *pdev) { struct mvebu_pcie *pcie; struct device_node *np = pdev->dev.of_node; @@ -790,6 +830,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev) return -ENOMEM; pcie->pdev = pdev; + platform_set_drvdata(pdev, pcie); /* Get the PCIe memory and I/O aperture */ mvebu_mbus_get_pcie_mem_aperture(&pcie->mem); @@ -818,13 +859,14 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev) return ret; } + i = 0; for_each_child_of_node(pdev->dev.of_node, child) { if (!of_device_is_available(child)) continue; - pcie->nports++; + i++; } - pcie->ports = devm_kzalloc(&pdev->dev, pcie->nports * + pcie->ports = devm_kzalloc(&pdev->dev, i * sizeof(struct mvebu_pcie_port), GFP_KERNEL); if (!pcie->ports) @@ -833,6 +875,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev) i = 0; for_each_child_of_node(pdev->dev.of_node, child) { struct mvebu_pcie_port *port = &pcie->ports[i]; + enum of_gpio_flags flags; if (!of_device_is_available(child)) continue; @@ -873,45 +916,68 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev) continue; } + port->reset_gpio = of_get_named_gpio_flags(child, + "reset-gpios", 0, &flags); + if (gpio_is_valid(port->reset_gpio)) { + u32 reset_udelay = 20000; + + port->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; + port->reset_name = kasprintf(GFP_KERNEL, + "pcie%d.%d-reset", port->port, port->lane); + of_property_read_u32(child, "reset-delay-us", + &reset_udelay); + + ret = devm_gpio_request_one(&pdev->dev, + port->reset_gpio, GPIOF_DIR_OUT, port->reset_name); + if (ret) { + if (ret == -EPROBE_DEFER) + return ret; + continue; + } + + gpio_set_value(port->reset_gpio, + (port->reset_active_low) ? 1 : 0); + msleep(reset_udelay/1000); + } + + port->clk = of_clk_get_by_name(child, NULL); + if (IS_ERR(port->clk)) { + dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n", + port->port, port->lane); + continue; + } + + ret = clk_prepare_enable(port->clk); + if (ret) + continue; + port->base = mvebu_pcie_map_registers(pdev, child, port); if (IS_ERR(port->base)) { dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n", port->port, port->lane); port->base = NULL; + clk_disable_unprepare(port->clk); continue; } mvebu_pcie_set_local_dev_nr(port, 1); - if (mvebu_pcie_link_up(port)) { - port->haslink = 1; - dev_info(&pdev->dev, "PCIe%d.%d: link up\n", - port->port, port->lane); - } else { - port->haslink = 0; - dev_info(&pdev->dev, "PCIe%d.%d: link down\n", - port->port, port->lane); - } - port->clk = of_clk_get_by_name(child, NULL); if (IS_ERR(port->clk)) { dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n", port->port, port->lane); iounmap(port->base); - port->haslink = 0; continue; } port->dn = child; - - clk_prepare_enable(port->clk); spin_lock_init(&port->conf_lock); - mvebu_sw_pci_bridge_init(port); - i++; } + pcie->nports = i; + mvebu_pcie_msi_enable(pcie); mvebu_pcie_enable(pcie); return 0; @@ -920,6 +986,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev) static const struct of_device_id mvebu_pcie_of_match_table[] = { { .compatible = "marvell,armada-xp-pcie", }, { .compatible = "marvell,armada-370-pcie", }, + { .compatible = "marvell,dove-pcie", }, { .compatible = "marvell,kirkwood-pcie", }, {}, }; @@ -931,16 +998,12 @@ static struct platform_driver mvebu_pcie_driver = { .name = "mvebu-pcie", .of_match_table = of_match_ptr(mvebu_pcie_of_match_table), + /* driver unloading/unbinding currently not supported */ + .suppress_bind_attrs = true, }, + .probe = mvebu_pcie_probe, }; - -static int __init mvebu_pcie_init(void) -{ - return platform_driver_probe(&mvebu_pcie_driver, - mvebu_pcie_probe); -} - -subsys_initcall(mvebu_pcie_init); +module_platform_driver(mvebu_pcie_driver); MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); MODULE_DESCRIPTION("Marvell EBU PCIe driver"); |