From 1f94a94f67e1083e19fb7b436dd7ca7a4ba03f2b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Jan 2015 20:34:39 -0600 Subject: PCI: Add generic config accessors Many PCI controllers' configuration space accesses are memory-mapped and vary only in address calculation and access checks. There are 2 main access methods: a decoded address space such as ECAM or a single address and data register similar to x86. This implementation can support both cases as well as be used in cases that need additional pre- or post-access handling. Add a new pci_ops member, map_bus, which can do access checks and any necessary setup. It returns the address to use for the configuration space access. The access types supported are 32-bit only accesses or correct byte, word, or dword sized accesses. Tested-by: Thierry Reding Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas Reviewed-by: Thierry Reding --- drivers/pci/access.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 49dd766852ba..d9b64a175990 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -67,6 +67,93 @@ EXPORT_SYMBOL(pci_bus_write_config_byte); EXPORT_SYMBOL(pci_bus_write_config_word); EXPORT_SYMBOL(pci_bus_write_config_dword); +int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + void __iomem *addr; + + addr = bus->ops->map_bus(bus, devfn, where); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (size == 1) + *val = readb(addr); + else if (size == 2) + *val = readw(addr); + else + *val = readl(addr); + + return PCIBIOS_SUCCESSFUL; +} +EXPORT_SYMBOL_GPL(pci_generic_config_read); + +int pci_generic_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + void __iomem *addr; + + addr = bus->ops->map_bus(bus, devfn, where); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 1) + writeb(val, addr); + else if (size == 2) + writew(val, addr); + else + writel(val, addr); + + return PCIBIOS_SUCCESSFUL; +} +EXPORT_SYMBOL_GPL(pci_generic_config_write); + +int pci_generic_config_read32(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + void __iomem *addr; + + addr = bus->ops->map_bus(bus, devfn, where & ~0x3); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + *val = readl(addr); + + if (size <= 2) + *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); + + return PCIBIOS_SUCCESSFUL; +} +EXPORT_SYMBOL_GPL(pci_generic_config_read32); + +int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + void __iomem *addr; + u32 mask, tmp; + + addr = bus->ops->map_bus(bus, devfn, where & ~0x3); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 4) { + writel(val, addr); + return PCIBIOS_SUCCESSFUL; + } else { + mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); + } + + tmp = readl(addr) & mask; + tmp |= val << ((where & 0x3) * 8); + writel(tmp, addr); + + return PCIBIOS_SUCCESSFUL; +} +EXPORT_SYMBOL_GPL(pci_generic_config_write32); + /** * pci_bus_set_ops - Set raw operations of pci bus * @bus: pci bus struct -- cgit v1.2.1 From 21186728865ec0485fe874a175f99b375c859f2b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Jan 2015 20:34:46 -0600 Subject: PCI: generic: Convert to use generic config accessors Convert the generic host PCI driver to use the generic config access functions. Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas Acked-by: Will Deacon CC: linux-arm-kernel@lists.infradead.org --- drivers/pci/host/pci-host-generic.c | 51 +++---------------------------------- 1 file changed, 3 insertions(+), 48 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index 6eb1aa75bd37..925e29e3d4c8 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -76,55 +76,9 @@ static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = { .map_bus = gen_pci_map_cfg_bus_ecam, }; -static int gen_pci_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - void __iomem *addr; - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; - - addr = pci->cfg.ops->map_bus(bus, devfn, where); - - switch (size) { - case 1: - *val = readb(addr); - break; - case 2: - *val = readw(addr); - break; - default: - *val = readl(addr); - } - - return PCIBIOS_SUCCESSFUL; -} - -static int gen_pci_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - void __iomem *addr; - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; - - addr = pci->cfg.ops->map_bus(bus, devfn, where); - - switch (size) { - case 1: - writeb(val, addr); - break; - case 2: - writew(val, addr); - break; - default: - writel(val, addr); - } - - return PCIBIOS_SUCCESSFUL; -} - static struct pci_ops gen_pci_ops = { - .read = gen_pci_config_read, - .write = gen_pci_config_write, + .read = pci_generic_config_read, + .write = pci_generic_config_write, }; static const struct of_device_id gen_pci_of_match[] = { @@ -287,6 +241,7 @@ static int gen_pci_probe(struct platform_device *pdev) of_id = of_match_node(gen_pci_of_match, np); pci->cfg.ops = of_id->data; + gen_pci_ops.map_bus = pci->cfg.ops->map_bus; pci->host.dev.parent = dev; INIT_LIST_HEAD(&pci->host.windows); INIT_LIST_HEAD(&pci->resources); -- cgit v1.2.1 From b44923b78d11175d80a1693deb82bdcd13070ba9 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Jan 2015 20:34:47 -0600 Subject: PCI: rcar: Convert to use generic config accessors Convert the rcar-gen2 host PCI driver to use the generic config access functions. This changes the I/O accessors from io(read|write)X to readX/writeX variants which are equivalent on ARM. Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas Acked-by: Geert Uytterhoeven CC: Simon Horman CC: linux-sh@vger.kernel.org --- drivers/pci/host/pci-rcar-gen2.c | 51 +++------------------------------------- 1 file changed, 3 insertions(+), 48 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index d9c042febb1a..dd6b84e6206c 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -131,52 +131,6 @@ static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, 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 rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { @@ -325,8 +279,9 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) } static struct pci_ops rcar_pci_ops = { - .read = rcar_pci_read_config, - .write = rcar_pci_write_config, + .map_bus = rcar_pci_cfg_base, + .read = pci_generic_config_read, + .write = pci_generic_config_write, }; static int rcar_pci_probe(struct platform_device *pdev) -- cgit v1.2.1 From 0e7ac8de01d20a9e365dfa975720816eeb35ff1a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Jan 2015 20:34:48 -0600 Subject: PCI: tegra: Convert to use generic config accessors Convert the Tegra host PCI driver to use the generic config access functions. Tested-by: Thierry Reding Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas Acked-by: Thierry Reding CC: Stephen Warren CC: Alexandre Courbot CC: linux-tegra@vger.kernel.org --- drivers/pci/host/pci-tegra.c | 55 +++----------------------------------------- 1 file changed, 3 insertions(+), 52 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index a800ae916394..44fd31b9fa2d 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -480,59 +480,10 @@ static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus, return addr; } -static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *value) -{ - void __iomem *addr; - - addr = tegra_pcie_conf_address(bus, devfn, where); - if (!addr) { - *value = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - *value = readl(addr); - - if (size == 1) - *value = (*value >> (8 * (where & 3))) & 0xff; - else if (size == 2) - *value = (*value >> (8 * (where & 3))) & 0xffff; - - return PCIBIOS_SUCCESSFUL; -} - -static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 value) -{ - void __iomem *addr; - u32 mask, tmp; - - addr = tegra_pcie_conf_address(bus, devfn, where); - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (size == 4) { - writel(value, addr); - return PCIBIOS_SUCCESSFUL; - } - - if (size == 2) - mask = ~(0xffff << ((where & 0x3) * 8)); - else if (size == 1) - mask = ~(0xff << ((where & 0x3) * 8)); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - tmp = readl(addr) & mask; - tmp |= value << ((where & 0x3) * 8); - writel(tmp, addr); - - return PCIBIOS_SUCCESSFUL; -} - static struct pci_ops tegra_pcie_ops = { - .read = tegra_pcie_read_conf, - .write = tegra_pcie_write_conf, + .map_bus = tegra_pcie_conf_address, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, }; static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) -- cgit v1.2.1 From 350f8be5bb402a1d6804adeba0031926ad246bf1 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Jan 2015 20:34:49 -0600 Subject: PCI: xgene: Convert to use generic config accessors Convert the xgene host PCI driver to use the generic config access functions. Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas CC: Tanmay Inamdar CC: linux-arm-kernel@lists.infradead.org --- drivers/pci/host/pci-xgene.c | 150 +++---------------------------------------- 1 file changed, 9 insertions(+), 141 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index b1d0596457c5..ee6a3d3105d8 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -74,92 +74,6 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags) return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags; } -/* PCIe Configuration Out/In */ -static inline void xgene_pcie_cfg_out32(void __iomem *addr, int offset, u32 val) -{ - writel(val, addr + offset); -} - -static inline void xgene_pcie_cfg_out16(void __iomem *addr, int offset, u16 val) -{ - u32 val32 = readl(addr + (offset & ~0x3)); - - switch (offset & 0x3) { - case 2: - val32 &= ~0xFFFF0000; - val32 |= (u32)val << 16; - break; - case 0: - default: - val32 &= ~0xFFFF; - val32 |= val; - break; - } - writel(val32, addr + (offset & ~0x3)); -} - -static inline void xgene_pcie_cfg_out8(void __iomem *addr, int offset, u8 val) -{ - u32 val32 = readl(addr + (offset & ~0x3)); - - switch (offset & 0x3) { - case 0: - val32 &= ~0xFF; - val32 |= val; - break; - case 1: - val32 &= ~0xFF00; - val32 |= (u32)val << 8; - break; - case 2: - val32 &= ~0xFF0000; - val32 |= (u32)val << 16; - break; - case 3: - default: - val32 &= ~0xFF000000; - val32 |= (u32)val << 24; - break; - } - writel(val32, addr + (offset & ~0x3)); -} - -static inline void xgene_pcie_cfg_in32(void __iomem *addr, int offset, u32 *val) -{ - *val = readl(addr + offset); -} - -static inline void xgene_pcie_cfg_in16(void __iomem *addr, int offset, u32 *val) -{ - *val = readl(addr + (offset & ~0x3)); - - switch (offset & 0x3) { - case 2: - *val >>= 16; - break; - } - - *val &= 0xFFFF; -} - -static inline void xgene_pcie_cfg_in8(void __iomem *addr, int offset, u32 *val) -{ - *val = readl(addr + (offset & ~0x3)); - - switch (offset & 0x3) { - case 3: - *val = *val >> 24; - break; - case 2: - *val = *val >> 16; - break; - case 1: - *val = *val >> 8; - break; - } - *val &= 0xFF; -} - /* * When the address bit [17:16] is 2'b01, the Configuration access will be * treated as Type 1 and it will be forwarded to external PCIe device. @@ -213,69 +127,23 @@ static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) return false; } -static int xgene_pcie_read_config(struct pci_bus *bus, unsigned int devfn, - int offset, int len, u32 *val) -{ - struct xgene_pcie_port *port = bus->sysdata; - void __iomem *addr; - - if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (xgene_pcie_hide_rc_bars(bus, offset)) { - *val = 0; - return PCIBIOS_SUCCESSFUL; - } - - xgene_pcie_set_rtdid_reg(bus, devfn); - addr = xgene_pcie_get_cfg_base(bus); - switch (len) { - case 1: - xgene_pcie_cfg_in8(addr, offset, val); - break; - case 2: - xgene_pcie_cfg_in16(addr, offset, val); - break; - default: - xgene_pcie_cfg_in32(addr, offset, val); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - -static int xgene_pcie_write_config(struct pci_bus *bus, unsigned int devfn, - int offset, int len, u32 val) +static int xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int offset) { struct xgene_pcie_port *port = bus->sysdata; - void __iomem *addr; - if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (xgene_pcie_hide_rc_bars(bus, offset)) - return PCIBIOS_SUCCESSFUL; + if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up || + xgene_pcie_hide_rc_bars(bus, offset)) + return NULL; xgene_pcie_set_rtdid_reg(bus, devfn); - addr = xgene_pcie_get_cfg_base(bus); - switch (len) { - case 1: - xgene_pcie_cfg_out8(addr, offset, (u8)val); - break; - case 2: - xgene_pcie_cfg_out16(addr, offset, (u16)val); - break; - default: - xgene_pcie_cfg_out32(addr, offset, val); - break; - } - - return PCIBIOS_SUCCESSFUL; + return xgene_pcie_get_cfg_base(bus); } static struct pci_ops xgene_pcie_ops = { - .read = xgene_pcie_read_config, - .write = xgene_pcie_write_config + .map_bus = xgene_pcie_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, }; static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr, -- cgit v1.2.1 From 029e2151fc4a5760b4ab963d7613f8603084232a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Jan 2015 20:34:50 -0600 Subject: PCI: xilinx: Convert to use generic config accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the Xilinx host PCI driver to use the generic config access functions. Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas CC: Michal Simek CC: "Sören Brinkmann" CC: linux-arm-kernel@lists.infradead.org --- drivers/pci/host/pcie-xilinx.c | 88 +++++------------------------------------- 1 file changed, 9 insertions(+), 79 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index ef3ebaf9a738..f67d0363e3c9 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -189,7 +189,7 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) } /** - * xilinx_pcie_config_base - Get configuration base + * xilinx_pcie_map_bus - Get configuration base * @bus: PCI Bus structure * @devfn: Device/function * @where: Offset from base @@ -197,96 +197,26 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) * Return: Base address of the configuration space needed to be * accessed. */ -static void __iomem *xilinx_pcie_config_base(struct pci_bus *bus, - unsigned int devfn, int where) +static void __iomem *xilinx_pcie_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) { struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata); int relbus; + if (!xilinx_pcie_valid_device(bus, devfn)) + return NULL; + relbus = (bus->number << ECAM_BUS_NUM_SHIFT) | (devfn << ECAM_DEV_NUM_SHIFT); return port->reg_base + relbus + where; } -/** - * xilinx_pcie_read_config - Read configuration space - * @bus: PCI Bus structure - * @devfn: Device/function - * @where: Offset from base - * @size: Byte/word/dword - * @val: Value to be read - * - * Return: PCIBIOS_SUCCESSFUL on success - * PCIBIOS_DEVICE_NOT_FOUND on failure - */ -static int xilinx_pcie_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - void __iomem *addr; - - if (!xilinx_pcie_valid_device(bus, devfn)) { - *val = 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - addr = xilinx_pcie_config_base(bus, devfn, where); - - switch (size) { - case 1: - *val = readb(addr); - break; - case 2: - *val = readw(addr); - break; - default: - *val = readl(addr); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - -/** - * xilinx_pcie_write_config - Write configuration space - * @bus: PCI Bus structure - * @devfn: Device/function - * @where: Offset from base - * @size: Byte/word/dword - * @val: Value to be written to device - * - * Return: PCIBIOS_SUCCESSFUL on success - * PCIBIOS_DEVICE_NOT_FOUND on failure - */ -static int xilinx_pcie_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - void __iomem *addr; - - if (!xilinx_pcie_valid_device(bus, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - addr = xilinx_pcie_config_base(bus, devfn, where); - - switch (size) { - case 1: - writeb(val, addr); - break; - case 2: - writew(val, addr); - break; - default: - writel(val, addr); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - /* PCIe operations */ static struct pci_ops xilinx_pcie_ops = { - .read = xilinx_pcie_read_config, - .write = xilinx_pcie_write_config, + .map_bus = xilinx_pcie_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, }; /* MSI functions */ -- cgit v1.2.1