18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCIe driver for Marvell Armada 370 and Armada XP SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/mbus.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 198c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 208c2ecf20Sopenharmony_ci#include <linux/of_pci.h> 218c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "../pci.h" 248c2ecf20Sopenharmony_ci#include "../pci-bridge-emul.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * PCIe unit register offsets. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci#define PCIE_DEV_ID_OFF 0x0000 308c2ecf20Sopenharmony_ci#define PCIE_CMD_OFF 0x0004 318c2ecf20Sopenharmony_ci#define PCIE_DEV_REV_OFF 0x0008 328c2ecf20Sopenharmony_ci#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) 338c2ecf20Sopenharmony_ci#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) 348c2ecf20Sopenharmony_ci#define PCIE_CAP_PCIEXP 0x0060 358c2ecf20Sopenharmony_ci#define PCIE_HEADER_LOG_4_OFF 0x0128 368c2ecf20Sopenharmony_ci#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) 378c2ecf20Sopenharmony_ci#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) 388c2ecf20Sopenharmony_ci#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) 398c2ecf20Sopenharmony_ci#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4)) 408c2ecf20Sopenharmony_ci#define PCIE_WIN5_CTRL_OFF 0x1880 418c2ecf20Sopenharmony_ci#define PCIE_WIN5_BASE_OFF 0x1884 428c2ecf20Sopenharmony_ci#define PCIE_WIN5_REMAP_OFF 0x188c 438c2ecf20Sopenharmony_ci#define PCIE_CONF_ADDR_OFF 0x18f8 448c2ecf20Sopenharmony_ci#define PCIE_CONF_ADDR_EN 0x80000000 458c2ecf20Sopenharmony_ci#define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc)) 468c2ecf20Sopenharmony_ci#define PCIE_CONF_BUS(b) (((b) & 0xff) << 16) 478c2ecf20Sopenharmony_ci#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11) 488c2ecf20Sopenharmony_ci#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 8) 498c2ecf20Sopenharmony_ci#define PCIE_CONF_ADDR(bus, devfn, where) \ 508c2ecf20Sopenharmony_ci (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ 518c2ecf20Sopenharmony_ci PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where) | \ 528c2ecf20Sopenharmony_ci PCIE_CONF_ADDR_EN) 538c2ecf20Sopenharmony_ci#define PCIE_CONF_DATA_OFF 0x18fc 548c2ecf20Sopenharmony_ci#define PCIE_MASK_OFF 0x1910 558c2ecf20Sopenharmony_ci#define PCIE_MASK_ENABLE_INTS 0x0f000000 568c2ecf20Sopenharmony_ci#define PCIE_CTRL_OFF 0x1a00 578c2ecf20Sopenharmony_ci#define PCIE_CTRL_X1_MODE 0x0001 588c2ecf20Sopenharmony_ci#define PCIE_STAT_OFF 0x1a04 598c2ecf20Sopenharmony_ci#define PCIE_STAT_BUS 0xff00 608c2ecf20Sopenharmony_ci#define PCIE_STAT_DEV 0x1f0000 618c2ecf20Sopenharmony_ci#define PCIE_STAT_LINK_DOWN BIT(0) 628c2ecf20Sopenharmony_ci#define PCIE_RC_RTSTA 0x1a14 638c2ecf20Sopenharmony_ci#define PCIE_DEBUG_CTRL 0x1a60 648c2ecf20Sopenharmony_ci#define PCIE_DEBUG_SOFT_RESET BIT(20) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct mvebu_pcie_port; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Structure representing all PCIe interfaces */ 698c2ecf20Sopenharmony_cistruct mvebu_pcie { 708c2ecf20Sopenharmony_ci struct platform_device *pdev; 718c2ecf20Sopenharmony_ci struct mvebu_pcie_port *ports; 728c2ecf20Sopenharmony_ci struct resource io; 738c2ecf20Sopenharmony_ci struct resource realio; 748c2ecf20Sopenharmony_ci struct resource mem; 758c2ecf20Sopenharmony_ci struct resource busn; 768c2ecf20Sopenharmony_ci int nports; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct mvebu_pcie_window { 808c2ecf20Sopenharmony_ci phys_addr_t base; 818c2ecf20Sopenharmony_ci phys_addr_t remap; 828c2ecf20Sopenharmony_ci size_t size; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* Structure representing one PCIe interface */ 868c2ecf20Sopenharmony_cistruct mvebu_pcie_port { 878c2ecf20Sopenharmony_ci char *name; 888c2ecf20Sopenharmony_ci void __iomem *base; 898c2ecf20Sopenharmony_ci u32 port; 908c2ecf20Sopenharmony_ci u32 lane; 918c2ecf20Sopenharmony_ci int devfn; 928c2ecf20Sopenharmony_ci unsigned int mem_target; 938c2ecf20Sopenharmony_ci unsigned int mem_attr; 948c2ecf20Sopenharmony_ci unsigned int io_target; 958c2ecf20Sopenharmony_ci unsigned int io_attr; 968c2ecf20Sopenharmony_ci struct clk *clk; 978c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 988c2ecf20Sopenharmony_ci char *reset_name; 998c2ecf20Sopenharmony_ci struct pci_bridge_emul bridge; 1008c2ecf20Sopenharmony_ci struct device_node *dn; 1018c2ecf20Sopenharmony_ci struct mvebu_pcie *pcie; 1028c2ecf20Sopenharmony_ci struct mvebu_pcie_window memwin; 1038c2ecf20Sopenharmony_ci struct mvebu_pcie_window iowin; 1048c2ecf20Sopenharmony_ci u32 saved_pcie_stat; 1058c2ecf20Sopenharmony_ci struct resource regs; 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci writel(val, port->base + reg); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci return readl(port->base + reg); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci return port->io_target != -1 && port->io_attr != -1; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci u32 stat; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci stat = mvebu_readl(port, PCIE_STAT_OFF); 1338c2ecf20Sopenharmony_ci stat &= ~PCIE_STAT_BUS; 1348c2ecf20Sopenharmony_ci stat |= nr << 8; 1358c2ecf20Sopenharmony_ci mvebu_writel(port, stat, PCIE_STAT_OFF); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u32 stat; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci stat = mvebu_readl(port, PCIE_STAT_OFF); 1438c2ecf20Sopenharmony_ci stat &= ~PCIE_STAT_DEV; 1448c2ecf20Sopenharmony_ci stat |= nr << 16; 1458c2ecf20Sopenharmony_ci mvebu_writel(port, stat, PCIE_STAT_OFF); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* 1498c2ecf20Sopenharmony_ci * Setup PCIE BARs and Address Decode Wins: 1508c2ecf20Sopenharmony_ci * BAR[0] -> internal registers (needed for MSI) 1518c2ecf20Sopenharmony_ci * BAR[1] -> covers all DRAM banks 1528c2ecf20Sopenharmony_ci * BAR[2] -> Disabled 1538c2ecf20Sopenharmony_ci * WIN[0-3] -> DRAM bank[0-3] 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_cistatic void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci const struct mbus_dram_target_info *dram; 1588c2ecf20Sopenharmony_ci u32 size; 1598c2ecf20Sopenharmony_ci int i; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci dram = mv_mbus_dram_info(); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* First, disable and clear BARs and windows. */ 1648c2ecf20Sopenharmony_ci for (i = 1; i < 3; i++) { 1658c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i)); 1668c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i)); 1678c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i)); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 1718c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i)); 1728c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i)); 1738c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i)); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF); 1778c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF); 1788c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Setup windows for DDR banks. Count total DDR size on the fly. */ 1818c2ecf20Sopenharmony_ci size = 0; 1828c2ecf20Sopenharmony_ci for (i = 0; i < dram->num_cs; i++) { 1838c2ecf20Sopenharmony_ci const struct mbus_dram_window *cs = dram->cs + i; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci mvebu_writel(port, cs->base & 0xffff0000, 1868c2ecf20Sopenharmony_ci PCIE_WIN04_BASE_OFF(i)); 1878c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i)); 1888c2ecf20Sopenharmony_ci mvebu_writel(port, 1898c2ecf20Sopenharmony_ci ((cs->size - 1) & 0xffff0000) | 1908c2ecf20Sopenharmony_ci (cs->mbus_attr << 8) | 1918c2ecf20Sopenharmony_ci (dram->mbus_dram_target_id << 4) | 1, 1928c2ecf20Sopenharmony_ci PCIE_WIN04_CTRL_OFF(i)); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci size += cs->size; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Round up 'size' to the nearest power of two. */ 1988c2ecf20Sopenharmony_ci if ((size & (size - 1)) != 0) 1998c2ecf20Sopenharmony_ci size = 1 << fls(size); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Setup BAR[1] to all DRAM banks. */ 2028c2ecf20Sopenharmony_ci mvebu_writel(port, dram->cs[0].base, PCIE_BAR_LO_OFF(1)); 2038c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_BAR_HI_OFF(1)); 2048c2ecf20Sopenharmony_ci mvebu_writel(port, ((size - 1) & 0xffff0000) | 1, 2058c2ecf20Sopenharmony_ci PCIE_BAR_CTRL_OFF(1)); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* 2088c2ecf20Sopenharmony_ci * Point BAR[0] to the device's internal registers. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci mvebu_writel(port, round_down(port->regs.start, SZ_1M), PCIE_BAR_LO_OFF(0)); 2118c2ecf20Sopenharmony_ci mvebu_writel(port, 0, PCIE_BAR_HI_OFF(0)); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci u32 cmd, mask; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* Point PCIe unit MBUS decode windows to DRAM space. */ 2198c2ecf20Sopenharmony_ci mvebu_pcie_setup_wins(port); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Master + slave enable. */ 2228c2ecf20Sopenharmony_ci cmd = mvebu_readl(port, PCIE_CMD_OFF); 2238c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_IO; 2248c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_MEMORY; 2258c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_MASTER; 2268c2ecf20Sopenharmony_ci mvebu_writel(port, cmd, PCIE_CMD_OFF); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Enable interrupt lines A-D. */ 2298c2ecf20Sopenharmony_ci mask = mvebu_readl(port, PCIE_MASK_OFF); 2308c2ecf20Sopenharmony_ci mask |= PCIE_MASK_ENABLE_INTS; 2318c2ecf20Sopenharmony_ci mvebu_writel(port, mask, PCIE_MASK_OFF); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, 2358c2ecf20Sopenharmony_ci struct pci_bus *bus, 2368c2ecf20Sopenharmony_ci u32 devfn, int where, int size, u32 *val) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), 2418c2ecf20Sopenharmony_ci PCIE_CONF_ADDR_OFF); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci switch (size) { 2448c2ecf20Sopenharmony_ci case 1: 2458c2ecf20Sopenharmony_ci *val = readb_relaxed(conf_data + (where & 3)); 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci case 2: 2488c2ecf20Sopenharmony_ci *val = readw_relaxed(conf_data + (where & 2)); 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case 4: 2518c2ecf20Sopenharmony_ci *val = readl_relaxed(conf_data); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, 2598c2ecf20Sopenharmony_ci struct pci_bus *bus, 2608c2ecf20Sopenharmony_ci u32 devfn, int where, int size, u32 val) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), 2658c2ecf20Sopenharmony_ci PCIE_CONF_ADDR_OFF); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci switch (size) { 2688c2ecf20Sopenharmony_ci case 1: 2698c2ecf20Sopenharmony_ci writeb(val, conf_data + (where & 3)); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case 2: 2728c2ecf20Sopenharmony_ci writew(val, conf_data + (where & 2)); 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci case 4: 2758c2ecf20Sopenharmony_ci writel(val, conf_data); 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci default: 2788c2ecf20Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * Remove windows, starting from the largest ones to the smallest 2868c2ecf20Sopenharmony_ci * ones. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_cistatic void mvebu_pcie_del_windows(struct mvebu_pcie_port *port, 2898c2ecf20Sopenharmony_ci phys_addr_t base, size_t size) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci while (size) { 2928c2ecf20Sopenharmony_ci size_t sz = 1 << (fls(size) - 1); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci mvebu_mbus_del_window(base, sz); 2958c2ecf20Sopenharmony_ci base += sz; 2968c2ecf20Sopenharmony_ci size -= sz; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* 3018c2ecf20Sopenharmony_ci * MBus windows can only have a power of two size, but PCI BARs do not 3028c2ecf20Sopenharmony_ci * have this constraint. Therefore, we have to split the PCI BAR into 3038c2ecf20Sopenharmony_ci * areas each having a power of two size. We start from the largest 3048c2ecf20Sopenharmony_ci * one (i.e highest order bit set in the size). 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_cistatic void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, 3078c2ecf20Sopenharmony_ci unsigned int target, unsigned int attribute, 3088c2ecf20Sopenharmony_ci phys_addr_t base, size_t size, 3098c2ecf20Sopenharmony_ci phys_addr_t remap) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci size_t size_mapped = 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci while (size) { 3148c2ecf20Sopenharmony_ci size_t sz = 1 << (fls(size) - 1); 3158c2ecf20Sopenharmony_ci int ret; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base, 3188c2ecf20Sopenharmony_ci sz, remap); 3198c2ecf20Sopenharmony_ci if (ret) { 3208c2ecf20Sopenharmony_ci phys_addr_t end = base + sz - 1; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci dev_err(&port->pcie->pdev->dev, 3238c2ecf20Sopenharmony_ci "Could not create MBus window at [mem %pa-%pa]: %d\n", 3248c2ecf20Sopenharmony_ci &base, &end, ret); 3258c2ecf20Sopenharmony_ci mvebu_pcie_del_windows(port, base - size_mapped, 3268c2ecf20Sopenharmony_ci size_mapped); 3278c2ecf20Sopenharmony_ci return; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci size -= sz; 3318c2ecf20Sopenharmony_ci size_mapped += sz; 3328c2ecf20Sopenharmony_ci base += sz; 3338c2ecf20Sopenharmony_ci if (remap != MVEBU_MBUS_NO_REMAP) 3348c2ecf20Sopenharmony_ci remap += sz; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void mvebu_pcie_set_window(struct mvebu_pcie_port *port, 3398c2ecf20Sopenharmony_ci unsigned int target, unsigned int attribute, 3408c2ecf20Sopenharmony_ci const struct mvebu_pcie_window *desired, 3418c2ecf20Sopenharmony_ci struct mvebu_pcie_window *cur) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci if (desired->base == cur->base && desired->remap == cur->remap && 3448c2ecf20Sopenharmony_ci desired->size == cur->size) 3458c2ecf20Sopenharmony_ci return; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (cur->size != 0) { 3488c2ecf20Sopenharmony_ci mvebu_pcie_del_windows(port, cur->base, cur->size); 3498c2ecf20Sopenharmony_ci cur->size = 0; 3508c2ecf20Sopenharmony_ci cur->base = 0; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * If something tries to change the window while it is enabled 3548c2ecf20Sopenharmony_ci * the change will not be done atomically. That would be 3558c2ecf20Sopenharmony_ci * difficult to do in the general case. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (desired->size == 0) 3608c2ecf20Sopenharmony_ci return; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci mvebu_pcie_add_windows(port, target, attribute, desired->base, 3638c2ecf20Sopenharmony_ci desired->size, desired->remap); 3648c2ecf20Sopenharmony_ci *cur = *desired; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct mvebu_pcie_window desired = {}; 3708c2ecf20Sopenharmony_ci struct pci_bridge_emul_conf *conf = &port->bridge.conf; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Are the new iobase/iolimit values invalid? */ 3738c2ecf20Sopenharmony_ci if (conf->iolimit < conf->iobase || 3748c2ecf20Sopenharmony_ci conf->iolimitupper < conf->iobaseupper || 3758c2ecf20Sopenharmony_ci !(conf->command & PCI_COMMAND_IO)) { 3768c2ecf20Sopenharmony_ci mvebu_pcie_set_window(port, port->io_target, port->io_attr, 3778c2ecf20Sopenharmony_ci &desired, &port->iowin); 3788c2ecf20Sopenharmony_ci return; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!mvebu_has_ioport(port)) { 3828c2ecf20Sopenharmony_ci dev_WARN(&port->pcie->pdev->dev, 3838c2ecf20Sopenharmony_ci "Attempt to set IO when IO is disabled\n"); 3848c2ecf20Sopenharmony_ci return; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * We read the PCI-to-PCI bridge emulated registers, and 3898c2ecf20Sopenharmony_ci * calculate the base address and size of the address decoding 3908c2ecf20Sopenharmony_ci * window to setup, according to the PCI-to-PCI bridge 3918c2ecf20Sopenharmony_ci * specifications. iobase is the bus address, port->iowin_base 3928c2ecf20Sopenharmony_ci * is the CPU address. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci desired.remap = ((conf->iobase & 0xF0) << 8) | 3958c2ecf20Sopenharmony_ci (conf->iobaseupper << 16); 3968c2ecf20Sopenharmony_ci desired.base = port->pcie->io.start + desired.remap; 3978c2ecf20Sopenharmony_ci desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) | 3988c2ecf20Sopenharmony_ci (conf->iolimitupper << 16)) - 3998c2ecf20Sopenharmony_ci desired.remap) + 4008c2ecf20Sopenharmony_ci 1; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, 4038c2ecf20Sopenharmony_ci &port->iowin); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP}; 4098c2ecf20Sopenharmony_ci struct pci_bridge_emul_conf *conf = &port->bridge.conf; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Are the new membase/memlimit values invalid? */ 4128c2ecf20Sopenharmony_ci if (conf->memlimit < conf->membase || 4138c2ecf20Sopenharmony_ci !(conf->command & PCI_COMMAND_MEMORY)) { 4148c2ecf20Sopenharmony_ci mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, 4158c2ecf20Sopenharmony_ci &desired, &port->memwin); 4168c2ecf20Sopenharmony_ci return; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* 4208c2ecf20Sopenharmony_ci * We read the PCI-to-PCI bridge emulated registers, and 4218c2ecf20Sopenharmony_ci * calculate the base address and size of the address decoding 4228c2ecf20Sopenharmony_ci * window to setup, according to the PCI-to-PCI bridge 4238c2ecf20Sopenharmony_ci * specifications. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci desired.base = ((conf->membase & 0xFFF0) << 16); 4268c2ecf20Sopenharmony_ci desired.size = (((conf->memlimit & 0xFFF0) << 16) | 0xFFFFF) - 4278c2ecf20Sopenharmony_ci desired.base + 1; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, 4308c2ecf20Sopenharmony_ci &port->memwin); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic pci_bridge_emul_read_status_t 4348c2ecf20Sopenharmony_cimvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, 4358c2ecf20Sopenharmony_ci int reg, u32 *value) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = bridge->data; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci switch (reg) { 4408c2ecf20Sopenharmony_ci case PCI_EXP_DEVCAP: 4418c2ecf20Sopenharmony_ci *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP); 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci case PCI_EXP_DEVCTL: 4458c2ecf20Sopenharmony_ci *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) & 4468c2ecf20Sopenharmony_ci ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | 4478c2ecf20Sopenharmony_ci PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci case PCI_EXP_LNKCAP: 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * PCIe requires the clock power management capability to be 4538c2ecf20Sopenharmony_ci * hard-wired to zero for downstream ports 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & 4568c2ecf20Sopenharmony_ci ~PCI_EXP_LNKCAP_CLKPM; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci case PCI_EXP_LNKCTL: 4608c2ecf20Sopenharmony_ci *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci case PCI_EXP_SLTCTL: 4648c2ecf20Sopenharmony_ci *value = PCI_EXP_SLTSTA_PDS << 16; 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci case PCI_EXP_RTSTA: 4688c2ecf20Sopenharmony_ci *value = mvebu_readl(port, PCIE_RC_RTSTA); 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci default: 4728c2ecf20Sopenharmony_ci return PCI_BRIDGE_EMUL_NOT_HANDLED; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return PCI_BRIDGE_EMUL_HANDLED; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic void 4798c2ecf20Sopenharmony_cimvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, 4808c2ecf20Sopenharmony_ci int reg, u32 old, u32 new, u32 mask) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = bridge->data; 4838c2ecf20Sopenharmony_ci struct pci_bridge_emul_conf *conf = &bridge->conf; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci switch (reg) { 4868c2ecf20Sopenharmony_ci case PCI_COMMAND: 4878c2ecf20Sopenharmony_ci { 4888c2ecf20Sopenharmony_ci if (!mvebu_has_ioport(port)) 4898c2ecf20Sopenharmony_ci conf->command &= ~PCI_COMMAND_IO; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if ((old ^ new) & PCI_COMMAND_IO) 4928c2ecf20Sopenharmony_ci mvebu_pcie_handle_iobase_change(port); 4938c2ecf20Sopenharmony_ci if ((old ^ new) & PCI_COMMAND_MEMORY) 4948c2ecf20Sopenharmony_ci mvebu_pcie_handle_membase_change(port); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci case PCI_IO_BASE: 5008c2ecf20Sopenharmony_ci /* 5018c2ecf20Sopenharmony_ci * We keep bit 1 set, it is a read-only bit that 5028c2ecf20Sopenharmony_ci * indicates we support 32 bits addressing for the 5038c2ecf20Sopenharmony_ci * I/O 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci conf->iobase |= PCI_IO_RANGE_TYPE_32; 5068c2ecf20Sopenharmony_ci conf->iolimit |= PCI_IO_RANGE_TYPE_32; 5078c2ecf20Sopenharmony_ci mvebu_pcie_handle_iobase_change(port); 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci case PCI_MEMORY_BASE: 5118c2ecf20Sopenharmony_ci mvebu_pcie_handle_membase_change(port); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci case PCI_IO_BASE_UPPER16: 5158c2ecf20Sopenharmony_ci mvebu_pcie_handle_iobase_change(port); 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci case PCI_PRIMARY_BUS: 5198c2ecf20Sopenharmony_ci mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus); 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci default: 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void 5288c2ecf20Sopenharmony_cimvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, 5298c2ecf20Sopenharmony_ci int reg, u32 old, u32 new, u32 mask) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = bridge->data; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci switch (reg) { 5348c2ecf20Sopenharmony_ci case PCI_EXP_DEVCTL: 5358c2ecf20Sopenharmony_ci /* 5368c2ecf20Sopenharmony_ci * Armada370 data says these bits must always 5378c2ecf20Sopenharmony_ci * be zero when in root complex mode. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci new &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | 5408c2ecf20Sopenharmony_ci PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci case PCI_EXP_LNKCTL: 5468c2ecf20Sopenharmony_ci /* 5478c2ecf20Sopenharmony_ci * If we don't support CLKREQ, we must ensure that the 5488c2ecf20Sopenharmony_ci * CLKREQ enable bit always reads zero. Since we haven't 5498c2ecf20Sopenharmony_ci * had this capability, and it's dependent on board wiring, 5508c2ecf20Sopenharmony_ci * disable it for the time being. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_ci new &= ~PCI_EXP_LNKCTL_CLKREQ_EN; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci case PCI_EXP_RTSTA: 5588c2ecf20Sopenharmony_ci mvebu_writel(port, new, PCIE_RC_RTSTA); 5598c2ecf20Sopenharmony_ci break; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { 5648c2ecf20Sopenharmony_ci .write_base = mvebu_pci_bridge_emul_base_conf_write, 5658c2ecf20Sopenharmony_ci .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read, 5668c2ecf20Sopenharmony_ci .write_pcie = mvebu_pci_bridge_emul_pcie_conf_write, 5678c2ecf20Sopenharmony_ci}; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci/* 5708c2ecf20Sopenharmony_ci * Initialize the configuration space of the PCI-to-PCI bridge 5718c2ecf20Sopenharmony_ci * associated with the given PCIe interface. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_cistatic void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct pci_bridge_emul *bridge = &port->bridge; 5768c2ecf20Sopenharmony_ci u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); 5778c2ecf20Sopenharmony_ci u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci bridge->conf.vendor = PCI_VENDOR_ID_MARVELL; 5808c2ecf20Sopenharmony_ci bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16; 5818c2ecf20Sopenharmony_ci bridge->conf.class_revision = 5828c2ecf20Sopenharmony_ci mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (mvebu_has_ioport(port)) { 5858c2ecf20Sopenharmony_ci /* We support 32 bits I/O addressing */ 5868c2ecf20Sopenharmony_ci bridge->conf.iobase = PCI_IO_RANGE_TYPE_32; 5878c2ecf20Sopenharmony_ci bridge->conf.iolimit = PCI_IO_RANGE_TYPE_32; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* 5918c2ecf20Sopenharmony_ci * Older mvebu hardware provides PCIe Capability structure only in 5928c2ecf20Sopenharmony_ci * version 1. New hardware provides it in version 2. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_ci bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci bridge->has_pcie = true; 5978c2ecf20Sopenharmony_ci bridge->data = port; 5988c2ecf20Sopenharmony_ci bridge->ops = &mvebu_pci_bridge_emul_ops; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci return sys->private_data; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, 6098c2ecf20Sopenharmony_ci struct pci_bus *bus, 6108c2ecf20Sopenharmony_ci int devfn) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci int i; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci for (i = 0; i < pcie->nports; i++) { 6158c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = &pcie->ports[i]; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (bus->number == 0 && port->devfn == devfn) 6188c2ecf20Sopenharmony_ci return port; 6198c2ecf20Sopenharmony_ci if (bus->number != 0 && 6208c2ecf20Sopenharmony_ci bus->number >= port->bridge.conf.secondary_bus && 6218c2ecf20Sopenharmony_ci bus->number <= port->bridge.conf.subordinate_bus) 6228c2ecf20Sopenharmony_ci return port; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return NULL; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/* PCI configuration space write function */ 6298c2ecf20Sopenharmony_cistatic int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, 6308c2ecf20Sopenharmony_ci int where, int size, u32 val) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct mvebu_pcie *pcie = bus->sysdata; 6338c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port; 6348c2ecf20Sopenharmony_ci int ret; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci port = mvebu_pcie_find_port(pcie, bus, devfn); 6378c2ecf20Sopenharmony_ci if (!port) 6388c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* Access the emulated PCI-to-PCI bridge */ 6418c2ecf20Sopenharmony_ci if (bus->number == 0) 6428c2ecf20Sopenharmony_ci return pci_bridge_emul_conf_write(&port->bridge, where, 6438c2ecf20Sopenharmony_ci size, val); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (!mvebu_pcie_link_up(port)) 6468c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* Access the real PCIe interface */ 6498c2ecf20Sopenharmony_ci ret = mvebu_pcie_hw_wr_conf(port, bus, devfn, 6508c2ecf20Sopenharmony_ci where, size, val); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci return ret; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci/* PCI configuration space read function */ 6568c2ecf20Sopenharmony_cistatic int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 6578c2ecf20Sopenharmony_ci int size, u32 *val) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct mvebu_pcie *pcie = bus->sysdata; 6608c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port; 6618c2ecf20Sopenharmony_ci int ret; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci port = mvebu_pcie_find_port(pcie, bus, devfn); 6648c2ecf20Sopenharmony_ci if (!port) { 6658c2ecf20Sopenharmony_ci *val = 0xffffffff; 6668c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* Access the emulated PCI-to-PCI bridge */ 6708c2ecf20Sopenharmony_ci if (bus->number == 0) 6718c2ecf20Sopenharmony_ci return pci_bridge_emul_conf_read(&port->bridge, where, 6728c2ecf20Sopenharmony_ci size, val); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (!mvebu_pcie_link_up(port)) { 6758c2ecf20Sopenharmony_ci *val = 0xffffffff; 6768c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* Access the real PCIe interface */ 6808c2ecf20Sopenharmony_ci ret = mvebu_pcie_hw_rd_conf(port, bus, devfn, 6818c2ecf20Sopenharmony_ci where, size, val); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return ret; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic struct pci_ops mvebu_pcie_ops = { 6878c2ecf20Sopenharmony_ci .read = mvebu_pcie_rd_conf, 6888c2ecf20Sopenharmony_ci .write = mvebu_pcie_wr_conf, 6898c2ecf20Sopenharmony_ci}; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, 6928c2ecf20Sopenharmony_ci const struct resource *res, 6938c2ecf20Sopenharmony_ci resource_size_t start, 6948c2ecf20Sopenharmony_ci resource_size_t size, 6958c2ecf20Sopenharmony_ci resource_size_t align) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci if (dev->bus->number != 0) 6988c2ecf20Sopenharmony_ci return start; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * On the PCI-to-PCI bridge side, the I/O windows must have at 7028c2ecf20Sopenharmony_ci * least a 64 KB size and the memory windows must have at 7038c2ecf20Sopenharmony_ci * least a 1 MB size. Moreover, MBus windows need to have a 7048c2ecf20Sopenharmony_ci * base address aligned on their size, and their size must be 7058c2ecf20Sopenharmony_ci * a power of two. This means that if the BAR doesn't have a 7068c2ecf20Sopenharmony_ci * power of two size, several MBus windows will actually be 7078c2ecf20Sopenharmony_ci * created. We need to ensure that the biggest MBus window 7088c2ecf20Sopenharmony_ci * (which will be the first one) is aligned on its size, which 7098c2ecf20Sopenharmony_ci * explains the rounddown_pow_of_two() being done here. 7108c2ecf20Sopenharmony_ci */ 7118c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) 7128c2ecf20Sopenharmony_ci return round_up(start, max_t(resource_size_t, SZ_64K, 7138c2ecf20Sopenharmony_ci rounddown_pow_of_two(size))); 7148c2ecf20Sopenharmony_ci else if (res->flags & IORESOURCE_MEM) 7158c2ecf20Sopenharmony_ci return round_up(start, max_t(resource_size_t, SZ_1M, 7168c2ecf20Sopenharmony_ci rounddown_pow_of_two(size))); 7178c2ecf20Sopenharmony_ci else 7188c2ecf20Sopenharmony_ci return start; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev, 7228c2ecf20Sopenharmony_ci struct device_node *np, 7238c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci int ret = 0; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 0, &port->regs); 7288c2ecf20Sopenharmony_ci if (ret) 7298c2ecf20Sopenharmony_ci return (void __iomem *)ERR_PTR(ret); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return devm_ioremap_resource(&pdev->dev, &port->regs); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci#define DT_FLAGS_TO_TYPE(flags) (((flags) >> 24) & 0x03) 7358c2ecf20Sopenharmony_ci#define DT_TYPE_IO 0x1 7368c2ecf20Sopenharmony_ci#define DT_TYPE_MEM32 0x2 7378c2ecf20Sopenharmony_ci#define DT_CPUADDR_TO_TARGET(cpuaddr) (((cpuaddr) >> 56) & 0xFF) 7388c2ecf20Sopenharmony_ci#define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF) 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic int mvebu_get_tgt_attr(struct device_node *np, int devfn, 7418c2ecf20Sopenharmony_ci unsigned long type, 7428c2ecf20Sopenharmony_ci unsigned int *tgt, 7438c2ecf20Sopenharmony_ci unsigned int *attr) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci const int na = 3, ns = 2; 7468c2ecf20Sopenharmony_ci const __be32 *range; 7478c2ecf20Sopenharmony_ci int rlen, nranges, rangesz, pna, i; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci *tgt = -1; 7508c2ecf20Sopenharmony_ci *attr = -1; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci range = of_get_property(np, "ranges", &rlen); 7538c2ecf20Sopenharmony_ci if (!range) 7548c2ecf20Sopenharmony_ci return -EINVAL; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci pna = of_n_addr_cells(np); 7578c2ecf20Sopenharmony_ci rangesz = pna + na + ns; 7588c2ecf20Sopenharmony_ci nranges = rlen / sizeof(__be32) / rangesz; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci for (i = 0; i < nranges; i++, range += rangesz) { 7618c2ecf20Sopenharmony_ci u32 flags = of_read_number(range, 1); 7628c2ecf20Sopenharmony_ci u32 slot = of_read_number(range + 1, 1); 7638c2ecf20Sopenharmony_ci u64 cpuaddr = of_read_number(range + na, pna); 7648c2ecf20Sopenharmony_ci unsigned long rtype; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_IO) 7678c2ecf20Sopenharmony_ci rtype = IORESOURCE_IO; 7688c2ecf20Sopenharmony_ci else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32) 7698c2ecf20Sopenharmony_ci rtype = IORESOURCE_MEM; 7708c2ecf20Sopenharmony_ci else 7718c2ecf20Sopenharmony_ci continue; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (slot == PCI_SLOT(devfn) && type == rtype) { 7748c2ecf20Sopenharmony_ci *tgt = DT_CPUADDR_TO_TARGET(cpuaddr); 7758c2ecf20Sopenharmony_ci *attr = DT_CPUADDR_TO_ATTR(cpuaddr); 7768c2ecf20Sopenharmony_ci return 0; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci return -ENOENT; 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 7848c2ecf20Sopenharmony_cistatic int mvebu_pcie_suspend(struct device *dev) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct mvebu_pcie *pcie; 7878c2ecf20Sopenharmony_ci int i; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci pcie = dev_get_drvdata(dev); 7908c2ecf20Sopenharmony_ci for (i = 0; i < pcie->nports; i++) { 7918c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = pcie->ports + i; 7928c2ecf20Sopenharmony_ci port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci return 0; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic int mvebu_pcie_resume(struct device *dev) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci struct mvebu_pcie *pcie; 8018c2ecf20Sopenharmony_ci int i; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci pcie = dev_get_drvdata(dev); 8048c2ecf20Sopenharmony_ci for (i = 0; i < pcie->nports; i++) { 8058c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = pcie->ports + i; 8068c2ecf20Sopenharmony_ci mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF); 8078c2ecf20Sopenharmony_ci mvebu_pcie_setup_hw(port); 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci#endif 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic void mvebu_pcie_port_clk_put(void *data) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = data; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci clk_put(port->clk); 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, 8228c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port, struct device_node *child) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct device *dev = &pcie->pdev->dev; 8258c2ecf20Sopenharmony_ci enum of_gpio_flags flags; 8268c2ecf20Sopenharmony_ci int reset_gpio, ret; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci port->pcie = pcie; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "marvell,pcie-port", &port->port)) { 8318c2ecf20Sopenharmony_ci dev_warn(dev, "ignoring %pOF, missing pcie-port property\n", 8328c2ecf20Sopenharmony_ci child); 8338c2ecf20Sopenharmony_ci goto skip; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane)) 8378c2ecf20Sopenharmony_ci port->lane = 0; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port, 8408c2ecf20Sopenharmony_ci port->lane); 8418c2ecf20Sopenharmony_ci if (!port->name) { 8428c2ecf20Sopenharmony_ci ret = -ENOMEM; 8438c2ecf20Sopenharmony_ci goto err; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci port->devfn = of_pci_get_devfn(child); 8478c2ecf20Sopenharmony_ci if (port->devfn < 0) 8488c2ecf20Sopenharmony_ci goto skip; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM, 8518c2ecf20Sopenharmony_ci &port->mem_target, &port->mem_attr); 8528c2ecf20Sopenharmony_ci if (ret < 0) { 8538c2ecf20Sopenharmony_ci dev_err(dev, "%s: cannot get tgt/attr for mem window\n", 8548c2ecf20Sopenharmony_ci port->name); 8558c2ecf20Sopenharmony_ci goto skip; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (resource_size(&pcie->io) != 0) { 8598c2ecf20Sopenharmony_ci mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_IO, 8608c2ecf20Sopenharmony_ci &port->io_target, &port->io_attr); 8618c2ecf20Sopenharmony_ci } else { 8628c2ecf20Sopenharmony_ci port->io_target = -1; 8638c2ecf20Sopenharmony_ci port->io_attr = -1; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags); 8678c2ecf20Sopenharmony_ci if (reset_gpio == -EPROBE_DEFER) { 8688c2ecf20Sopenharmony_ci ret = reset_gpio; 8698c2ecf20Sopenharmony_ci goto err; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (gpio_is_valid(reset_gpio)) { 8738c2ecf20Sopenharmony_ci unsigned long gpio_flags; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci port->reset_name = devm_kasprintf(dev, GFP_KERNEL, "%s-reset", 8768c2ecf20Sopenharmony_ci port->name); 8778c2ecf20Sopenharmony_ci if (!port->reset_name) { 8788c2ecf20Sopenharmony_ci ret = -ENOMEM; 8798c2ecf20Sopenharmony_ci goto err; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (flags & OF_GPIO_ACTIVE_LOW) { 8838c2ecf20Sopenharmony_ci dev_info(dev, "%pOF: reset gpio is active low\n", 8848c2ecf20Sopenharmony_ci child); 8858c2ecf20Sopenharmony_ci gpio_flags = GPIOF_ACTIVE_LOW | 8868c2ecf20Sopenharmony_ci GPIOF_OUT_INIT_LOW; 8878c2ecf20Sopenharmony_ci } else { 8888c2ecf20Sopenharmony_ci gpio_flags = GPIOF_OUT_INIT_HIGH; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags, 8928c2ecf20Sopenharmony_ci port->reset_name); 8938c2ecf20Sopenharmony_ci if (ret) { 8948c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 8958c2ecf20Sopenharmony_ci goto err; 8968c2ecf20Sopenharmony_ci goto skip; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci port->reset_gpio = gpio_to_desc(reset_gpio); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci port->clk = of_clk_get_by_name(child, NULL); 9038c2ecf20Sopenharmony_ci if (IS_ERR(port->clk)) { 9048c2ecf20Sopenharmony_ci dev_err(dev, "%s: cannot get clock\n", port->name); 9058c2ecf20Sopenharmony_ci goto skip; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci ret = devm_add_action(dev, mvebu_pcie_port_clk_put, port); 9098c2ecf20Sopenharmony_ci if (ret < 0) { 9108c2ecf20Sopenharmony_ci clk_put(port->clk); 9118c2ecf20Sopenharmony_ci goto err; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci return 1; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ciskip: 9178c2ecf20Sopenharmony_ci ret = 0; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* In the case of skipping, we need to free these */ 9208c2ecf20Sopenharmony_ci devm_kfree(dev, port->reset_name); 9218c2ecf20Sopenharmony_ci port->reset_name = NULL; 9228c2ecf20Sopenharmony_ci devm_kfree(dev, port->name); 9238c2ecf20Sopenharmony_ci port->name = NULL; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cierr: 9268c2ecf20Sopenharmony_ci return ret; 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci/* 9308c2ecf20Sopenharmony_ci * Power up a PCIe port. PCIe requires the refclk to be stable for 100µs 9318c2ecf20Sopenharmony_ci * prior to releasing PERST. See table 2-4 in section 2.6.2 AC Specifications 9328c2ecf20Sopenharmony_ci * of the PCI Express Card Electromechanical Specification, 1.1. 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_cistatic int mvebu_pcie_powerup(struct mvebu_pcie_port *port) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci int ret; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci ret = clk_prepare_enable(port->clk); 9398c2ecf20Sopenharmony_ci if (ret < 0) 9408c2ecf20Sopenharmony_ci return ret; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (port->reset_gpio) { 9438c2ecf20Sopenharmony_ci u32 reset_udelay = PCI_PM_D3COLD_WAIT * 1000; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci of_property_read_u32(port->dn, "reset-delay-us", 9468c2ecf20Sopenharmony_ci &reset_udelay); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci udelay(100); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(port->reset_gpio, 0); 9518c2ecf20Sopenharmony_ci msleep(reset_udelay / 1000); 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci return 0; 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci/* 9588c2ecf20Sopenharmony_ci * Power down a PCIe port. Strictly, PCIe requires us to place the card 9598c2ecf20Sopenharmony_ci * in D3hot state before asserting PERST#. 9608c2ecf20Sopenharmony_ci */ 9618c2ecf20Sopenharmony_cistatic void mvebu_pcie_powerdown(struct mvebu_pcie_port *port) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(port->reset_gpio, 1); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci clk_disable_unprepare(port->clk); 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci/* 9698c2ecf20Sopenharmony_ci * devm_of_pci_get_host_bridge_resources() only sets up translateable resources, 9708c2ecf20Sopenharmony_ci * so we need extra resource setup parsing our special DT properties encoding 9718c2ecf20Sopenharmony_ci * the MEM and IO apertures. 9728c2ecf20Sopenharmony_ci */ 9738c2ecf20Sopenharmony_cistatic int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci struct device *dev = &pcie->pdev->dev; 9768c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); 9778c2ecf20Sopenharmony_ci int ret; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci /* Get the PCIe memory aperture */ 9808c2ecf20Sopenharmony_ci mvebu_mbus_get_pcie_mem_aperture(&pcie->mem); 9818c2ecf20Sopenharmony_ci if (resource_size(&pcie->mem) == 0) { 9828c2ecf20Sopenharmony_ci dev_err(dev, "invalid memory aperture size\n"); 9838c2ecf20Sopenharmony_ci return -EINVAL; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci pcie->mem.name = "PCI MEM"; 9878c2ecf20Sopenharmony_ci pci_add_resource(&bridge->windows, &pcie->mem); 9888c2ecf20Sopenharmony_ci ret = devm_request_resource(dev, &iomem_resource, &pcie->mem); 9898c2ecf20Sopenharmony_ci if (ret) 9908c2ecf20Sopenharmony_ci return ret; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Get the PCIe IO aperture */ 9938c2ecf20Sopenharmony_ci mvebu_mbus_get_pcie_io_aperture(&pcie->io); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (resource_size(&pcie->io) != 0) { 9968c2ecf20Sopenharmony_ci pcie->realio.flags = pcie->io.flags; 9978c2ecf20Sopenharmony_ci pcie->realio.start = PCIBIOS_MIN_IO; 9988c2ecf20Sopenharmony_ci pcie->realio.end = min_t(resource_size_t, 9998c2ecf20Sopenharmony_ci IO_SPACE_LIMIT - SZ_64K, 10008c2ecf20Sopenharmony_ci resource_size(&pcie->io) - 1); 10018c2ecf20Sopenharmony_ci pcie->realio.name = "PCI I/O"; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci pci_add_resource(&bridge->windows, &pcie->realio); 10048c2ecf20Sopenharmony_ci ret = devm_request_resource(dev, &ioport_resource, &pcie->realio); 10058c2ecf20Sopenharmony_ci if (ret) 10068c2ecf20Sopenharmony_ci return ret; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci return 0; 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci/* 10138c2ecf20Sopenharmony_ci * This is a copy of pci_host_probe(), except that it does the I/O 10148c2ecf20Sopenharmony_ci * remap as the last step, once we are sure we won't fail. 10158c2ecf20Sopenharmony_ci * 10168c2ecf20Sopenharmony_ci * It should be removed once the I/O remap error handling issue has 10178c2ecf20Sopenharmony_ci * been sorted out. 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_cistatic int mvebu_pci_host_probe(struct pci_host_bridge *bridge) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct mvebu_pcie *pcie; 10228c2ecf20Sopenharmony_ci struct pci_bus *bus, *child; 10238c2ecf20Sopenharmony_ci int ret; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci ret = pci_scan_root_bus_bridge(bridge); 10268c2ecf20Sopenharmony_ci if (ret < 0) { 10278c2ecf20Sopenharmony_ci dev_err(bridge->dev.parent, "Scanning root bridge failed"); 10288c2ecf20Sopenharmony_ci return ret; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci pcie = pci_host_bridge_priv(bridge); 10328c2ecf20Sopenharmony_ci if (resource_size(&pcie->io) != 0) { 10338c2ecf20Sopenharmony_ci unsigned int i; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci for (i = 0; i < resource_size(&pcie->realio); i += SZ_64K) 10368c2ecf20Sopenharmony_ci pci_ioremap_io(i, pcie->io.start + i); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci bus = bridge->bus; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci /* 10428c2ecf20Sopenharmony_ci * We insert PCI resources into the iomem_resource and 10438c2ecf20Sopenharmony_ci * ioport_resource trees in either pci_bus_claim_resources() 10448c2ecf20Sopenharmony_ci * or pci_bus_assign_resources(). 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_ci if (pci_has_flag(PCI_PROBE_ONLY)) { 10478c2ecf20Sopenharmony_ci pci_bus_claim_resources(bus); 10488c2ecf20Sopenharmony_ci } else { 10498c2ecf20Sopenharmony_ci pci_bus_size_bridges(bus); 10508c2ecf20Sopenharmony_ci pci_bus_assign_resources(bus); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci list_for_each_entry(child, &bus->children, node) 10538c2ecf20Sopenharmony_ci pcie_bus_configure_settings(child); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci pci_bus_add_devices(bus); 10578c2ecf20Sopenharmony_ci return 0; 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic int mvebu_pcie_probe(struct platform_device *pdev) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 10638c2ecf20Sopenharmony_ci struct mvebu_pcie *pcie; 10648c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge; 10658c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 10668c2ecf20Sopenharmony_ci struct device_node *child; 10678c2ecf20Sopenharmony_ci int num, i, ret; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct mvebu_pcie)); 10708c2ecf20Sopenharmony_ci if (!bridge) 10718c2ecf20Sopenharmony_ci return -ENOMEM; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci pcie = pci_host_bridge_priv(bridge); 10748c2ecf20Sopenharmony_ci pcie->pdev = pdev; 10758c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pcie); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci ret = mvebu_pcie_parse_request_resources(pcie); 10788c2ecf20Sopenharmony_ci if (ret) 10798c2ecf20Sopenharmony_ci return ret; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci num = of_get_available_child_count(np); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci pcie->ports = devm_kcalloc(dev, num, sizeof(*pcie->ports), GFP_KERNEL); 10848c2ecf20Sopenharmony_ci if (!pcie->ports) 10858c2ecf20Sopenharmony_ci return -ENOMEM; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci i = 0; 10888c2ecf20Sopenharmony_ci for_each_available_child_of_node(np, child) { 10898c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = &pcie->ports[i]; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci ret = mvebu_pcie_parse_port(pcie, port, child); 10928c2ecf20Sopenharmony_ci if (ret < 0) { 10938c2ecf20Sopenharmony_ci of_node_put(child); 10948c2ecf20Sopenharmony_ci return ret; 10958c2ecf20Sopenharmony_ci } else if (ret == 0) { 10968c2ecf20Sopenharmony_ci continue; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci port->dn = child; 11008c2ecf20Sopenharmony_ci i++; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci pcie->nports = i; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci for (i = 0; i < pcie->nports; i++) { 11058c2ecf20Sopenharmony_ci struct mvebu_pcie_port *port = &pcie->ports[i]; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci child = port->dn; 11088c2ecf20Sopenharmony_ci if (!child) 11098c2ecf20Sopenharmony_ci continue; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci ret = mvebu_pcie_powerup(port); 11128c2ecf20Sopenharmony_ci if (ret < 0) 11138c2ecf20Sopenharmony_ci continue; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci port->base = mvebu_pcie_map_registers(pdev, child, port); 11168c2ecf20Sopenharmony_ci if (IS_ERR(port->base)) { 11178c2ecf20Sopenharmony_ci dev_err(dev, "%s: cannot map registers\n", port->name); 11188c2ecf20Sopenharmony_ci port->base = NULL; 11198c2ecf20Sopenharmony_ci mvebu_pcie_powerdown(port); 11208c2ecf20Sopenharmony_ci continue; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci mvebu_pcie_setup_hw(port); 11248c2ecf20Sopenharmony_ci mvebu_pcie_set_local_dev_nr(port, 1); 11258c2ecf20Sopenharmony_ci mvebu_pci_bridge_emul_init(port); 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci pcie->nports = i; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci bridge->sysdata = pcie; 11318c2ecf20Sopenharmony_ci bridge->ops = &mvebu_pcie_ops; 11328c2ecf20Sopenharmony_ci bridge->align_resource = mvebu_pcie_align_resource; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci return mvebu_pci_host_probe(bridge); 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic const struct of_device_id mvebu_pcie_of_match_table[] = { 11388c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-xp-pcie", }, 11398c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-370-pcie", }, 11408c2ecf20Sopenharmony_ci { .compatible = "marvell,dove-pcie", }, 11418c2ecf20Sopenharmony_ci { .compatible = "marvell,kirkwood-pcie", }, 11428c2ecf20Sopenharmony_ci {}, 11438c2ecf20Sopenharmony_ci}; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mvebu_pcie_pm_ops = { 11468c2ecf20Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mvebu_pcie_suspend, mvebu_pcie_resume) 11478c2ecf20Sopenharmony_ci}; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cistatic struct platform_driver mvebu_pcie_driver = { 11508c2ecf20Sopenharmony_ci .driver = { 11518c2ecf20Sopenharmony_ci .name = "mvebu-pcie", 11528c2ecf20Sopenharmony_ci .of_match_table = mvebu_pcie_of_match_table, 11538c2ecf20Sopenharmony_ci /* driver unloading/unbinding currently not supported */ 11548c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 11558c2ecf20Sopenharmony_ci .pm = &mvebu_pcie_pm_ops, 11568c2ecf20Sopenharmony_ci }, 11578c2ecf20Sopenharmony_ci .probe = mvebu_pcie_probe, 11588c2ecf20Sopenharmony_ci}; 11598c2ecf20Sopenharmony_cibuiltin_platform_driver(mvebu_pcie_driver); 1160