--- zzzz-none-000/linux-4.4.60/drivers/pci/host/pcie-designware.c 2017-04-08 07:53:53.000000000 +0000 +++ scorpion-7490-727/linux-4.4.60/drivers/pci/host/pcie-designware.c 2021-02-04 17:41:59.000000000 +0000 @@ -22,6 +22,7 @@ #include #include #include +#include #include "pcie-designware.h" @@ -69,6 +70,28 @@ #define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) #define PCIE_ATU_UPPER_TARGET 0x91C +/* PCIe Port Logic registers */ +#define PLR_OFFSET 0x700 +#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) +#define PCIE_PHY_DEBUG_R1_LINK_UP 0x00000010 + +#define PCIE_ATU_CR1_OUTBOUND_6_GEN3 0xC00 +#define PCIE_ATU_CR2_OUTBOUND_6_GEN3 0xC04 +#define PCIE_ATU_LOWER_BASE_OUTBOUND_6_GEN3 0xC08 +#define PCIE_ATU_UPPER_BASE_OUTBOUND_6_GEN3 0xC0C +#define PCIE_ATU_LIMIT_OUTBOUND_6_GEN3 0xC10 +#define PCIE_ATU_LOWER_TARGET_OUTBOUND_6_GEN3 0xC14 +#define PCIE_ATU_UPPER_TARGET_OUTBOUND_6_GEN3 0xC18 + +#define PCIE_ATU_CR1_OUTBOUND_7_GEN3 0xE00 +#define PCIE_ATU_CR2_OUTBOUND_7_GEN3 0xE04 +#define PCIE_ATU_LOWER_BASE_OUTBOUND_7_GEN3 0xE08 +#define PCIE_ATU_UPPER_BASE_OUTBOUND_7_GEN3 0xE0C +#define PCIE_ATU_LIMIT_OUTBOUND_7_GEN3 0xE10 +#define PCIE_ATU_LOWER_TARGET_OUTBOUND_7_GEN3 0xE14 +#define PCIE_ATU_UPPER_TARGET_OUTBOUND_7_GEN3 0xE18 + +# static struct pci_ops dw_pcie_ops; int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val) @@ -128,42 +151,57 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) { - int ret; - if (pp->ops->rd_own_conf) - ret = pp->ops->rd_own_conf(pp, where, size, val); - else - ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); + return pp->ops->rd_own_conf(pp, where, size, val); - return ret; + return dw_pcie_cfg_read(pp->dbi_base + where, size, val); } static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val) { - int ret; - if (pp->ops->wr_own_conf) - ret = pp->ops->wr_own_conf(pp, where, size, val); - else - ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); + return pp->ops->wr_own_conf(pp, where, size, val); - return ret; + return dw_pcie_cfg_write(pp->dbi_base + where, size, val); +} + +static inline void dw_pcie_writel_rc_gen3(struct pcie_port *pp, u32 val, u32 reg) +{ + writel_relaxed(val, pp->dm_iatu + reg); } static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index, int type, u64 cpu_addr, u64 pci_addr, u32 size) { - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1), - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); + if (pp->is_gen3) { + dw_pcie_writel_rc_gen3(pp, 0x4, PCIE_ATU_CR1_OUTBOUND_6_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x90000000, PCIE_ATU_CR2_OUTBOUND_6_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x0, PCIE_ATU_LOWER_BASE_OUTBOUND_6_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x0, PCIE_ATU_UPPER_BASE_OUTBOUND_6_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x00107FFFF, PCIE_ATU_LIMIT_OUTBOUND_6_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x0, PCIE_ATU_LOWER_TARGET_OUTBOUND_6_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x0, PCIE_ATU_UPPER_TARGET_OUTBOUND_6_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x5, PCIE_ATU_CR1_OUTBOUND_7_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x90000000, PCIE_ATU_CR2_OUTBOUND_7_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x200000, PCIE_ATU_LOWER_BASE_OUTBOUND_7_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x0, PCIE_ATU_UPPER_BASE_OUTBOUND_7_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x7FFFFF, PCIE_ATU_LIMIT_OUTBOUND_7_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x0, PCIE_ATU_LOWER_TARGET_OUTBOUND_7_GEN3); + dw_pcie_writel_rc_gen3(pp, 0x0, PCIE_ATU_UPPER_TARGET_OUTBOUND_7_GEN3); + } else { + dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index, + PCIE_ATU_VIEWPORT); + dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1), + PCIE_ATU_LIMIT); + dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET); + dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1); + dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); + + } } static struct irq_chip dw_msi_irq_chip = { @@ -374,18 +412,199 @@ clear_irq_range(pp, irq, 1, data->hwirq); } +static void dw_msi_teardown_irqs(struct msi_desc *msi, struct pcie_port *pp) +{ + struct irq_data *data = irq_get_irq_data(msi->irq); + + clear_irq_range(pp, msi->irq, msi->nvec_used, data->hwirq); +} + +static int pcie_create_qgic_msi_irq(struct pcie_port *dev) +{ + int irq, pos; + +again: + pos = find_first_zero_bit(dev->msi_irq_in_use, MAX_MSI_IRQS); + + if (pos >= MAX_MSI_IRQS) + return -ENOSPC; + + if (test_and_set_bit(pos, dev->msi_irq_in_use)) + goto again; + + if (pos >= MAX_MSI_IRQS) { + printk("PCIe: pos %d is not less than %d\n", + pos, MAX_MSI_IRQS); + return ENOSPC; + } + + irq = dev->msi[pos]; + if (!irq) { + printk("PCIe: failed to create QGIC MSI IRQ.\n"); + return -EINVAL; + } + + return irq; +} + +static int qgic_msi_setup_irq(struct pci_dev *pdev, + struct msi_desc *desc, int nvec) +{ + int irq, index, firstirq = 0; + struct msi_msg msg; + struct pcie_port *dev = pdev->bus->sysdata; + + for (index = 0; index < nvec; index++) { + irq = pcie_create_qgic_msi_irq(dev); + + if (irq < 0) + return irq; + + if (index == 0) + firstirq = irq; + + irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); + } + + /* write msi vector and data */ + irq_set_msi_desc(firstirq, desc); + + msg.address_hi = 0; + msg.address_lo = dev->msi_gicm_addr; + msg.data = dev->msi_gicm_base + (firstirq - dev->msi[0]); + write_msi_msg(firstirq, &msg); + + return 0; +} + +static int msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, + struct msi_desc *desc) +{ + struct pcie_port *pp = pdev->bus->sysdata; + + if (pp->msi_gicm_addr) + return qgic_msi_setup_irq(pdev, desc, 1); + else + return dw_msi_setup_irq(chip, pdev, desc); +} + +int qgic_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + struct msi_desc *entry; + int ret = 0; + + if (type == PCI_CAP_ID_MSIX) + return -EINVAL; + + /* FIXME: + * AVM/AMY: The whole qgic_msi code looks broken. + * Ie. we don't have proper error handling/cleanup and + * if we don't get consecutive IRQ numbers here by coincidence, + * neither registering IRQs in HW nor teardown will work. + * Warn if we ever use this. + * Otherwise wait for mainline patches. + */ + WARN_ONCE(true, "This code is broken. Fix it!\n"); + list_for_each_entry(entry, &dev->dev.msi_list, list) { + entry->nvec_used = nvec; + entry->msi_attrib.multiple = order_base_2(nvec); + ret = qgic_msi_setup_irq(dev, entry, nvec); + } + + return ret; +} + +static int msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev, + int nvec, int type) +{ + struct pcie_port *pp = pdev->bus->sysdata; + + if (pp->msi_gicm_addr) + return qgic_setup_msi_irqs(pdev, nvec, type); + else + return dw_msi_setup_irqs(chip, pdev, nvec, type); +} + +void pcie_destroy_qgic_msi_irq(unsigned int irq, struct pcie_port *dev) +{ + int pos; + + pos = irq - dev->msi[0]; + clear_bit(pos, dev->msi_irq_in_use); +} + +static void msi_teardown_irq(struct msi_controller *chip, unsigned int irq) +{ + struct irq_data *data = irq_get_irq_data(irq); + struct msi_desc *msi; + struct pcie_port *pp; + + if (data) { + msi = irq_data_get_msi_desc(data); + pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); + + if (pp->msi_gicm_addr) + pcie_destroy_qgic_msi_irq(irq, pp); + else + dw_msi_teardown_irq(chip, irq); + } +} + +static void msi_teardown_irqs(struct pci_dev *dev) +{ + struct msi_desc *entry; + struct pcie_port *pp = dev->bus->sysdata; + + list_for_each_entry(entry, &dev->dev.msi_list, list) { + + if (entry->irq == 0) + continue; + + if (pp->msi_gicm_addr) { + int i; + + for (i = 0; i < entry->nvec_used; i++) + pcie_destroy_qgic_msi_irq(entry->irq + i, pp); + } else { + dw_msi_teardown_irqs(entry, pp); + } + } +} + static struct msi_controller dw_pcie_msi_chip = { - .setup_irq = dw_msi_setup_irq, - .setup_irqs = dw_msi_setup_irqs, - .teardown_irq = dw_msi_teardown_irq, + .setup_irq = msi_setup_irq, + .setup_irqs = msi_setup_irqs, + .teardown_irq = msi_teardown_irq, + .teardown_irqs = msi_teardown_irqs, }; +int dw_pcie_wait_for_link(struct pcie_port *pp) +{ + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (dw_pcie_link_up(pp)) { + dev_info(pp->dev, "link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + dev_err(pp->dev, "phy link never came up\n"); + + return -ETIMEDOUT; +} + int dw_pcie_link_up(struct pcie_port *pp) { + u32 val; + if (pp->ops->link_up) return pp->ops->link_up(pp); - else - return 0; + + val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); + return val & PCIE_PHY_DEBUG_R1_LINK_UP; } static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, @@ -407,7 +626,6 @@ struct platform_device *pdev = to_platform_device(pp->dev); struct pci_bus *bus, *child; struct resource *cfg_res; - u32 val; int i, ret; LIST_HEAD(res); struct resource_entry *win; @@ -496,26 +714,85 @@ pp->lanes = 0; if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (!pp->ops->msi_host_init) { - pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, + if (!pp->msi_gicm_addr) { + if (!pp->ops->msi_host_init) { + pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, MAX_MSI_IRQS, &msi_domain_ops, &dw_pcie_msi_chip); - if (!pp->irq_domain) { - dev_err(pp->dev, "irq domain init failed\n"); - return -ENXIO; + if (!pp->irq_domain) { + dev_err(pp->dev, "irq domain init failed\n"); + return -ENXIO; + } + + for (i = 0; i < MAX_MSI_IRQS; i++) + irq_create_mapping(pp->irq_domain, i); + } else { + ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); + if (ret < 0) + return ret; } + } + } - for (i = 0; i < MAX_MSI_IRQS; i++) - irq_create_mapping(pp->irq_domain, i); - } else { - ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); - if (ret < 0) - return ret; + if (pp->ops->host_init) { + ret = pp->ops->host_init(pp); + if (ret) { + dev_err(pp->dev, "hostinit failed\n"); + return ret; } } - if (pp->ops->host_init) - pp->ops->host_init(pp); + pp->root_bus_nr = pp->busn->start; + if (IS_ENABLED(CONFIG_PCI_MSI)) { + bus = pci_scan_root_bus_msi(pp->dev, pp->root_bus_nr, + &dw_pcie_ops, pp, &res, + &dw_pcie_msi_chip); + dw_pcie_msi_chip.dev = pp->dev; + } else + bus = pci_scan_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops, + pp, &res); + if (!bus) + return -ENOMEM; + + pp->pci_bus = bus; + if (pp->ops->scan_bus) + pp->ops->scan_bus(pp); + +#ifdef CONFIG_ARM + /* support old dtbs that incorrectly describe IRQs */ + pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); +#endif + + if (!pci_has_flag(PCI_PROBE_ONLY)) { + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + } + + pci_bus_add_devices(bus); + return 0; +} + +int dw_pcie_host_init_pm(struct pcie_port *pp) +{ + int ret; + LIST_HEAD(res); + u32 val; + struct pci_bus *bus, *child; + + pci_add_resource(&res, pp->busn); + pci_add_resource(&res, pp->io); + pci_add_resource(&res, pp->mem); + + if (pp->ops->host_init) { + ret = pp->ops->host_init(pp); + if (ret) { + dev_err(pp->dev, "pm_hostinit failed\n"); + return ret; + } + } if (!pp->ops->rd_other_conf) dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, @@ -531,18 +808,19 @@ val |= PORT_LOGIC_SPEED_CHANGE; dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); - pp->root_bus_nr = pp->busn->start; if (IS_ENABLED(CONFIG_PCI_MSI)) { bus = pci_scan_root_bus_msi(pp->dev, pp->root_bus_nr, &dw_pcie_ops, pp, &res, &dw_pcie_msi_chip); dw_pcie_msi_chip.dev = pp->dev; - } else + } else { bus = pci_scan_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops, pp, &res); + } if (!bus) return -ENOMEM; + pp->pci_bus = bus; if (pp->ops->scan_bus) pp->ops->scan_bus(pp); @@ -550,7 +828,6 @@ /* support old dtbs that incorrectly describe IRQs */ pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); #endif - if (!pci_has_flag(PCI_PROBE_ONLY)) { pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); @@ -561,6 +838,7 @@ pci_bus_add_devices(bus); return 0; + } static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, @@ -658,46 +936,36 @@ int size, u32 *val) { struct pcie_port *pp = bus->sysdata; - int ret; if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) { *val = 0xffffffff; return PCIBIOS_DEVICE_NOT_FOUND; } - if (bus->number != pp->root_bus_nr) - if (pp->ops->rd_other_conf) - ret = pp->ops->rd_other_conf(pp, bus, devfn, - where, size, val); - else - ret = dw_pcie_rd_other_conf(pp, bus, devfn, - where, size, val); - else - ret = dw_pcie_rd_own_conf(pp, where, size, val); + if (bus->number == pp->root_bus_nr) + return dw_pcie_rd_own_conf(pp, where, size, val); - return ret; + if (pp->ops->rd_other_conf) + return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val); + + return dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); } static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 val) { struct pcie_port *pp = bus->sysdata; - int ret; if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) return PCIBIOS_DEVICE_NOT_FOUND; - if (bus->number != pp->root_bus_nr) - if (pp->ops->wr_other_conf) - ret = pp->ops->wr_other_conf(pp, bus, devfn, - where, size, val); - else - ret = dw_pcie_wr_other_conf(pp, bus, devfn, - where, size, val); - else - ret = dw_pcie_wr_own_conf(pp, where, size, val); + if (bus->number == pp->root_bus_nr) + return dw_pcie_wr_own_conf(pp, where, size, val); - return ret; + if (pp->ops->wr_other_conf) + return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val); + + return dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); } static struct pci_ops dw_pcie_ops = { @@ -708,8 +976,6 @@ void dw_pcie_setup_rc(struct pcie_port *pp) { u32 val; - u32 membase; - u32 memlimit; /* set the number of lanes */ dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val); @@ -768,18 +1034,31 @@ val |= 0x00010100; dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS); - /* setup memory base, memory limit */ - membase = ((u32)pp->mem_base & 0xfff00000) >> 16; - memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000; - val = memlimit | membase; - dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE); - /* setup command register */ dw_pcie_readl_rc(pp, PCI_COMMAND, &val); val &= 0xffff0000; val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR; dw_pcie_writel_rc(pp, val, PCI_COMMAND); + + /* + * If the platform provides ->rd_other_conf, it means the platform + * uses its own address translation component rather than ATU, so + * we should not program the ATU here. + */ + if (!pp->ops->rd_other_conf) + dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_MEM, pp->mem_base, + pp->mem_bus_addr, pp->mem_size); + + dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); + + /* program correct class for RC */ + dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI); + + dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val); + val |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); } MODULE_AUTHOR("Jingoo Han ");