- phys: phandle of the PCIe PHY device
- phy-names: must be "pcie-phy"
+- brcm,pcie-ob: Some iProc SoCs do not have the outbound address mapping done
+by the ASIC after power on reset. In this case, SW needs to configure it
+
+If the brcm,pcie-ob property is present, the following properties become
+effective:
+
+Required:
+- brcm,pcie-ob-axi-offset: The offset from the AXI address to the internal
+address used by the iProc PCIe core (not the PCIe address)
+- brcm,pcie-ob-window-size: The outbound address mapping window size (in MB)
+
+Optional:
+- brcm,pcie-ob-oarr-size: Some iProc SoCs need the OARR size bit to be set to
+increase the outbound window size
+
Example:
pcie0: pcie@18012000 {
compatible = "brcm,iproc-pcie";
phys = <&phy 0 5>;
phy-names = "pcie-phy";
+
+ brcm,pcie-ob;
+ brcm,pcie-ob-oarr-size;
+ brcm,pcie-ob-axi-offset = <0x00000000>;
+ brcm,pcie-ob-window-size = <256>;
};
pcie1: pcie@18013000 {
- interrupt-map-mask and interrupt-map: standard PCI properties
to define the mapping of the PCIe interface to interrupt
numbers.
-- num-lanes: number of lanes to use
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- "pcie_bus"
Optional properties:
+- num-lanes: number of lanes to use (this property should be specified unless
+ the link is brought already up in BIOS)
- reset-gpio: gpio pin number of power good signal
- bus-range: PCI bus numbers covered (it is recommended for new devicetrees to
specify this property, to keep backwards compatibility a range of 0x00-0xff
- #size-cells : Must be 2.
- reg : The Configuration Space base address and size, as accessed
- from the parent bus.
-
+ from the parent bus. The base address corresponds to
+ the first bus in the "bus-range" property. If no
+ "bus-range" is specified, this will be bus 0 (the default).
Properties of the /chosen node:
chosen {
stdout-path = &serial0;
- linux,pci-probe-only;
};
};
#include <linux/seq_file.h>
#include <linux/root_dev.h>
#include <linux/of.h>
+#include <linux/of_pci.h>
#include <linux/kexec.h>
#include <asm/mmu.h>
* PCI_PROBE_ONLY and PCI_REASSIGN_ALL_BUS can be set via properties
* in chosen.
*/
- if (of_chosen) {
- const int *prop;
-
- prop = of_get_property(of_chosen,
- "linux,pci-probe-only", NULL);
- if (prop) {
- if (*prop)
- pci_add_flags(PCI_PROBE_ONLY);
- else
- pci_clear_flags(PCI_PROBE_ONLY);
- }
- }
+ of_pci_check_probe_only();
}
static void __init pSeries_setup_arch(void)
#include <linux/of_device.h>
#include <linux/of_pci.h>
#include <linux/slab.h>
+#include <asm-generic/pci-bridge.h>
static inline int __of_pci_pci_compare(struct device_node *node,
unsigned int data)
}
EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
+/**
+ * of_pci_check_probe_only - Setup probe only mode if linux,pci-probe-only
+ * is present and valid
+ */
+void of_pci_check_probe_only(void)
+{
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32(of_chosen, "linux,pci-probe-only", &val);
+ if (ret) {
+ if (ret == -ENODATA || ret == -EOVERFLOW)
+ pr_warn("linux,pci-probe-only without valid value, ignoring\n");
+ return;
+ }
+
+ if (val)
+ pci_add_flags(PCI_PROBE_ONLY);
+ else
+ pci_clear_flags(PCI_PROBE_ONLY);
+
+ pr_info("PCI: PROBE_ONLY %sabled\n", val ? "en" : "dis");
+}
+EXPORT_SYMBOL_GPL(of_pci_check_probe_only);
+
/**
* of_pci_dma_configure - Setup DMA configuration
* @dev: ptr to pci_dev struct of the PCI device
int ret;
exynos_pcie_sideband_dbi_r_mode(pp, true);
- ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
+ ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
exynos_pcie_sideband_dbi_r_mode(pp, false);
return ret;
}
int ret;
exynos_pcie_sideband_dbi_w_mode(pp, true);
- ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3),
- where, size, val);
+ ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
exynos_pcie_sideband_dbi_w_mode(pp, false);
return ret;
}
struct gen_pci_cfg_bus_ops {
u32 bus_shift;
- void __iomem *(*map_bus)(struct pci_bus *, unsigned int, int);
+ struct pci_ops ops;
};
struct gen_pci_cfg_windows {
struct resource *bus_range;
void __iomem **win;
- const struct gen_pci_cfg_bus_ops *ops;
+ struct gen_pci_cfg_bus_ops *ops;
};
/*
static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = {
.bus_shift = 16,
- .map_bus = gen_pci_map_cfg_bus_cam,
+ .ops = {
+ .map_bus = gen_pci_map_cfg_bus_cam,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
+ }
};
static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
.bus_shift = 20,
- .map_bus = gen_pci_map_cfg_bus_ecam,
-};
-
-static struct pci_ops gen_pci_ops = {
- .read = pci_generic_config_read,
- .write = pci_generic_config_write,
+ .ops = {
+ .map_bus = gen_pci_map_cfg_bus_ecam,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
+ }
};
static const struct of_device_id gen_pci_of_match[] = {
struct resource *bus_range;
struct device *dev = pci->host.dev.parent;
struct device_node *np = dev->of_node;
+ u32 sz = 1 << pci->cfg.ops->bus_shift;
err = of_address_to_resource(np, 0, &pci->cfg.res);
if (err) {
bus_range = pci->cfg.bus_range;
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
u32 idx = busn - bus_range->start;
- u32 sz = 1 << pci->cfg.ops->bus_shift;
pci->cfg.win[idx] = devm_ioremap(dev,
- pci->cfg.res.start + busn * sz,
+ pci->cfg.res.start + idx * sz,
sz);
if (!pci->cfg.win[idx])
return -ENOMEM;
int err;
const char *type;
const struct of_device_id *of_id;
- const int *prop;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
return -EINVAL;
}
- prop = of_get_property(of_chosen, "linux,pci-probe-only", NULL);
- if (prop) {
- if (*prop)
- pci_add_flags(PCI_PROBE_ONLY);
- else
- pci_clear_flags(PCI_PROBE_ONLY);
- }
+ of_pci_check_probe_only();
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->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
pci->host.dev.parent = dev;
INIT_LIST_HEAD(&pci->host.windows);
INIT_LIST_HEAD(&pci->resources);
if (!pci_has_flag(PCI_PROBE_ONLY))
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
- bus = pci_scan_root_bus(dev, 0, &gen_pci_ops, pci, &pci->resources);
+
+ bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
+ &pci->cfg.ops->ops, pci, &pci->resources);
if (!bus) {
dev_err(dev, "Scanning rootbus failed");
return -ENODEV;
/* PHY registers (not memory-mapped) */
#define PCIE_PHY_RX_ASIC_OUT 0x100D
+#define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0)
#define PHY_RX_OVRD_IN_LO 0x1005
#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0);
- if (rx_valid & 0x01)
+ if (rx_valid & PCIE_PHY_RX_ASIC_OUT_VALID)
return 0;
if ((debug_r0 & 0x3f) != 0x0d)
IRQF_SHARED, "mx6-pcie-msi", pp);
if (ret) {
dev_err(&pdev->dev, "failed to request MSI irq\n");
- return -ENODEV;
+ return ret;
}
}
*bit_pos = offset >> 3;
}
-u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
+phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
{
struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
- return dw_pcie_cfg_read(addr + (where & ~0x3), where, size, val);
+ return dw_pcie_cfg_read(addr + where, size, val);
}
int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
- return dw_pcie_cfg_write(addr + (where & ~0x3), where, size, val);
+ return dw_pcie_cfg_write(addr + where, size, val);
}
/**
/* Keystone DW specific MSI controller APIs/definitions */
void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset);
-u32 ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
+phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
/* Keystone specific PCI controller APIs */
void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
#define PCIE_DEV_REV_OFF 0x0008
#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
+#define PCIE_CAP_PCIEXP 0x0060
#define PCIE_HEADER_LOG_4_OFF 0x0128
#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
#define PCIE_STAT_BUS 0xff00
#define PCIE_STAT_DEV 0x1f0000
#define PCIE_STAT_LINK_DOWN BIT(0)
+#define PCIE_RC_RTSTA 0x1a14
#define PCIE_DEBUG_CTRL 0x1a60
#define PCIE_DEBUG_SOFT_RESET BIT(20)
+enum {
+ PCISWCAP = PCI_BRIDGE_CONTROL + 2,
+ PCISWCAP_EXP_LIST_ID = PCISWCAP + PCI_CAP_LIST_ID,
+ PCISWCAP_EXP_DEVCAP = PCISWCAP + PCI_EXP_DEVCAP,
+ PCISWCAP_EXP_DEVCTL = PCISWCAP + PCI_EXP_DEVCTL,
+ PCISWCAP_EXP_LNKCAP = PCISWCAP + PCI_EXP_LNKCAP,
+ PCISWCAP_EXP_LNKCTL = PCISWCAP + PCI_EXP_LNKCTL,
+ PCISWCAP_EXP_SLTCAP = PCISWCAP + PCI_EXP_SLTCAP,
+ PCISWCAP_EXP_SLTCTL = PCISWCAP + PCI_EXP_SLTCTL,
+ PCISWCAP_EXP_RTCTL = PCISWCAP + PCI_EXP_RTCTL,
+ PCISWCAP_EXP_RTSTA = PCISWCAP + PCI_EXP_RTSTA,
+ PCISWCAP_EXP_DEVCAP2 = PCISWCAP + PCI_EXP_DEVCAP2,
+ PCISWCAP_EXP_DEVCTL2 = PCISWCAP + PCI_EXP_DEVCTL2,
+ PCISWCAP_EXP_LNKCAP2 = PCISWCAP + PCI_EXP_LNKCAP2,
+ PCISWCAP_EXP_LNKCTL2 = PCISWCAP + PCI_EXP_LNKCTL2,
+ PCISWCAP_EXP_SLTCAP2 = PCISWCAP + PCI_EXP_SLTCAP2,
+ PCISWCAP_EXP_SLTCTL2 = PCISWCAP + PCI_EXP_SLTCTL2,
+};
+
/* PCI configuration space of a PCI-to-PCI bridge */
struct mvebu_sw_pci_bridge {
u16 vendor;
u16 device;
u16 command;
+ u16 status;
u16 class;
u8 interface;
u8 revision;
u16 memlimit;
u16 iobaseupper;
u16 iolimitupper;
- u8 cappointer;
- u8 reserved1;
- u16 reserved2;
u32 romaddr;
u8 intline;
u8 intpin;
u16 bridgectrl;
+
+ /* PCI express capability */
+ u32 pcie_sltcap;
+ u16 pcie_devctl;
+ u16 pcie_rtctl;
};
struct mvebu_pcie_port;
unsigned int io_target;
unsigned int io_attr;
struct clk *clk;
- int reset_gpio;
- int reset_active_low;
+ struct gpio_desc *reset_gpio;
char *reset_name;
struct mvebu_sw_pci_bridge bridge;
struct device_node *dn;
struct pci_bus *bus,
u32 devfn, int where, int size, u32 *val)
{
+ void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
+
mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
PCIE_CONF_ADDR_OFF);
- *val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
-
- if (size == 1)
- *val = (*val >> (8 * (where & 3))) & 0xff;
- else if (size == 2)
- *val = (*val >> (8 * (where & 3))) & 0xffff;
+ switch (size) {
+ case 1:
+ *val = readb_relaxed(conf_data + (where & 3));
+ break;
+ case 2:
+ *val = readw_relaxed(conf_data + (where & 2));
+ break;
+ case 4:
+ *val = readl_relaxed(conf_data);
+ break;
+ }
return PCIBIOS_SUCCESSFUL;
}
struct pci_bus *bus,
u32 devfn, int where, int size, u32 val)
{
- u32 _val, shift = 8 * (where & 3);
+ void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
PCIE_CONF_ADDR_OFF);
- _val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
- if (size == 4)
- _val = val;
- else if (size == 2)
- _val = (_val & ~(0xffff << shift)) | ((val & 0xffff) << shift);
- else if (size == 1)
- _val = (_val & ~(0xff << shift)) | ((val & 0xff) << shift);
- else
+ switch (size) {
+ case 1:
+ writeb(val, conf_data + (where & 3));
+ break;
+ case 2:
+ writew(val, conf_data + (where & 2));
+ break;
+ case 4:
+ writel(val, conf_data);
+ break;
+ default:
return PCIBIOS_BAD_REGISTER_NUMBER;
-
- mvebu_writel(port, _val, PCIE_CONF_DATA_OFF);
+ }
return PCIBIOS_SUCCESSFUL;
}
/* We support 32 bits I/O addressing */
bridge->iobase = PCI_IO_RANGE_TYPE_32;
bridge->iolimit = PCI_IO_RANGE_TYPE_32;
+
+ /* Add capabilities */
+ bridge->status = PCI_STATUS_CAP_LIST;
}
/*
break;
case PCI_COMMAND:
- *value = bridge->command;
+ *value = bridge->command | bridge->status << 16;
break;
case PCI_CLASS_REVISION:
*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
break;
+ case PCI_CAPABILITY_LIST:
+ *value = PCISWCAP;
+ break;
+
case PCI_ROM_ADDRESS1:
*value = 0;
break;
*value = 0;
break;
+ case PCISWCAP_EXP_LIST_ID:
+ /* Set PCIe v2, root port, slot support */
+ *value = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
+ PCI_EXP_FLAGS_SLOT) << 16 | PCI_CAP_ID_EXP;
+ break;
+
+ case PCISWCAP_EXP_DEVCAP:
+ *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
+ break;
+
+ case PCISWCAP_EXP_DEVCTL:
+ *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) &
+ ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
+ *value |= bridge->pcie_devctl;
+ break;
+
+ case PCISWCAP_EXP_LNKCAP:
+ /*
+ * PCIe requires the clock power management capability to be
+ * hard-wired to zero for downstream ports
+ */
+ *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
+ ~PCI_EXP_LNKCAP_CLKPM;
+ break;
+
+ case PCISWCAP_EXP_LNKCTL:
+ *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
+ break;
+
+ case PCISWCAP_EXP_SLTCAP:
+ *value = bridge->pcie_sltcap;
+ break;
+
+ case PCISWCAP_EXP_SLTCTL:
+ *value = PCI_EXP_SLTSTA_PDS << 16;
+ break;
+
+ case PCISWCAP_EXP_RTCTL:
+ *value = bridge->pcie_rtctl;
+ break;
+
+ case PCISWCAP_EXP_RTSTA:
+ *value = mvebu_readl(port, PCIE_RC_RTSTA);
+ break;
+
+ /* PCIe requires the v2 fields to be hard-wired to zero */
+ case PCISWCAP_EXP_DEVCAP2:
+ case PCISWCAP_EXP_DEVCTL2:
+ case PCISWCAP_EXP_LNKCAP2:
+ case PCISWCAP_EXP_LNKCTL2:
+ case PCISWCAP_EXP_SLTCAP2:
+ case PCISWCAP_EXP_SLTCTL2:
default:
- *value = 0xffffffff;
- return PCIBIOS_BAD_REGISTER_NUMBER;
+ /*
+ * PCI defines configuration read accesses to reserved or
+ * unimplemented registers to read as zero and complete
+ * normally.
+ */
+ *value = 0;
+ return PCIBIOS_SUCCESSFUL;
}
if (size == 2)
mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus);
break;
+ case PCISWCAP_EXP_DEVCTL:
+ /*
+ * Armada370 data says these bits must always
+ * be zero when in root complex mode.
+ */
+ value &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
+
+ /*
+ * If the mask is 0xffff0000, then we only want to write
+ * the device control register, rather than clearing the
+ * RW1C bits in the device status register. Mask out the
+ * status register bits.
+ */
+ if (mask == 0xffff0000)
+ value &= 0xffff;
+
+ mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL);
+ break;
+
+ case PCISWCAP_EXP_LNKCTL:
+ /*
+ * If we don't support CLKREQ, we must ensure that the
+ * CLKREQ enable bit always reads zero. Since we haven't
+ * had this capability, and it's dependent on board wiring,
+ * disable it for the time being.
+ */
+ value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
+
+ /*
+ * If the mask is 0xffff0000, then we only want to write
+ * the link control register, rather than clearing the
+ * RW1C bits in the link status register. Mask out the
+ * status register bits.
+ */
+ if (mask == 0xffff0000)
+ value &= 0xffff;
+
+ mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
+ break;
+
+ case PCISWCAP_EXP_RTSTA:
+ mvebu_writel(port, value, PCIE_RC_RTSTA);
+ break;
+
default:
break;
}
if (!mvebu_pcie_link_up(port))
return PCIBIOS_DEVICE_NOT_FOUND;
- /*
- * On the secondary bus, we don't want to expose any other
- * device than the device physically connected in the PCIe
- * slot, visible in slot 0. In slot 1, there's a special
- * Marvell device that only makes sense when the Armada is
- * used as a PCIe endpoint.
- */
- if (bus->number == port->bridge.secondary_bus &&
- PCI_SLOT(devfn) != 0)
- return PCIBIOS_DEVICE_NOT_FOUND;
-
/* Access the real PCIe interface */
ret = mvebu_pcie_hw_wr_conf(port, bus, devfn,
where, size, val);
return PCIBIOS_DEVICE_NOT_FOUND;
}
- /*
- * On the secondary bus, we don't want to expose any other
- * device than the device physically connected in the PCIe
- * slot, visible in slot 0. In slot 1, there's a special
- * Marvell device that only makes sense when the Armada is
- * used as a PCIe endpoint.
- */
- if (bus->number == port->bridge.secondary_bus &&
- PCI_SLOT(devfn) != 0) {
- *val = 0xffffffff;
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
-
/* Access the real PCIe interface */
ret = mvebu_pcie_hw_rd_conf(port, bus, devfn,
where, size, val);
return 0;
}
+static void mvebu_pcie_port_clk_put(void *data)
+{
+ struct mvebu_pcie_port *port = data;
+
+ clk_put(port->clk);
+}
+
+static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ struct mvebu_pcie_port *port, struct device_node *child)
+{
+ struct device *dev = &pcie->pdev->dev;
+ enum of_gpio_flags flags;
+ int reset_gpio, ret;
+
+ port->pcie = pcie;
+
+ if (of_property_read_u32(child, "marvell,pcie-port", &port->port)) {
+ dev_warn(dev, "ignoring %s, missing pcie-port property\n",
+ of_node_full_name(child));
+ goto skip;
+ }
+
+ if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane))
+ port->lane = 0;
+
+ port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port,
+ port->lane);
+ if (!port->name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ port->devfn = of_pci_get_devfn(child);
+ if (port->devfn < 0)
+ goto skip;
+
+ ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM,
+ &port->mem_target, &port->mem_attr);
+ if (ret < 0) {
+ dev_err(dev, "%s: cannot get tgt/attr for mem window\n",
+ port->name);
+ goto skip;
+ }
+
+ if (resource_size(&pcie->io) != 0) {
+ mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_IO,
+ &port->io_target, &port->io_attr);
+ } else {
+ port->io_target = -1;
+ port->io_attr = -1;
+ }
+
+ reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags);
+ if (reset_gpio == -EPROBE_DEFER) {
+ ret = reset_gpio;
+ goto err;
+ }
+
+ if (gpio_is_valid(reset_gpio)) {
+ unsigned long gpio_flags;
+
+ port->reset_name = devm_kasprintf(dev, GFP_KERNEL, "%s-reset",
+ port->name);
+ if (!port->reset_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (flags & OF_GPIO_ACTIVE_LOW) {
+ dev_info(dev, "%s: reset gpio is active low\n",
+ of_node_full_name(child));
+ gpio_flags = GPIOF_ACTIVE_LOW |
+ GPIOF_OUT_INIT_LOW;
+ } else {
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+ }
+
+ ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags,
+ port->reset_name);
+ if (ret) {
+ if (ret == -EPROBE_DEFER)
+ goto err;
+ goto skip;
+ }
+
+ port->reset_gpio = gpio_to_desc(reset_gpio);
+ }
+
+ port->clk = of_clk_get_by_name(child, NULL);
+ if (IS_ERR(port->clk)) {
+ dev_err(dev, "%s: cannot get clock\n", port->name);
+ goto skip;
+ }
+
+ ret = devm_add_action(dev, mvebu_pcie_port_clk_put, port);
+ if (ret < 0) {
+ clk_put(port->clk);
+ goto err;
+ }
+
+ return 1;
+
+skip:
+ ret = 0;
+
+ /* In the case of skipping, we need to free these */
+ devm_kfree(dev, port->reset_name);
+ port->reset_name = NULL;
+ devm_kfree(dev, port->name);
+ port->name = NULL;
+
+err:
+ return ret;
+}
+
+/*
+ * Power up a PCIe port. PCIe requires the refclk to be stable for 100µs
+ * prior to releasing PERST. See table 2-4 in section 2.6.2 AC Specifications
+ * of the PCI Express Card Electromechanical Specification, 1.1.
+ */
+static int mvebu_pcie_powerup(struct mvebu_pcie_port *port)
+{
+ int ret;
+
+ ret = clk_prepare_enable(port->clk);
+ if (ret < 0)
+ return ret;
+
+ if (port->reset_gpio) {
+ u32 reset_udelay = 20000;
+
+ of_property_read_u32(port->dn, "reset-delay-us",
+ &reset_udelay);
+
+ udelay(100);
+
+ gpiod_set_value_cansleep(port->reset_gpio, 0);
+ msleep(reset_udelay / 1000);
+ }
+
+ return 0;
+}
+
+/*
+ * Power down a PCIe port. Strictly, PCIe requires us to place the card
+ * in D3hot state before asserting PERST#.
+ */
+static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port)
+{
+ if (port->reset_gpio)
+ gpiod_set_value_cansleep(port->reset_gpio, 1);
+
+ clk_disable_unprepare(port->clk);
+}
+
static int mvebu_pcie_probe(struct platform_device *pdev)
{
struct mvebu_pcie *pcie;
struct device_node *np = pdev->dev.of_node;
struct device_node *child;
- int i, ret;
+ int num, i, ret;
pcie = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pcie),
GFP_KERNEL);
return ret;
}
- i = 0;
- for_each_child_of_node(pdev->dev.of_node, child) {
- if (!of_device_is_available(child))
- continue;
- i++;
- }
+ num = of_get_available_child_count(pdev->dev.of_node);
- pcie->ports = devm_kzalloc(&pdev->dev, i *
- sizeof(struct mvebu_pcie_port),
+ pcie->ports = devm_kcalloc(&pdev->dev, num, sizeof(*pcie->ports),
GFP_KERNEL);
if (!pcie->ports)
return -ENOMEM;
i = 0;
- for_each_child_of_node(pdev->dev.of_node, child) {
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
struct mvebu_pcie_port *port = &pcie->ports[i];
- enum of_gpio_flags flags;
-
- if (!of_device_is_available(child))
- continue;
- port->pcie = pcie;
-
- if (of_property_read_u32(child, "marvell,pcie-port",
- &port->port)) {
- dev_warn(&pdev->dev,
- "ignoring PCIe DT node, missing pcie-port property\n");
- continue;
- }
-
- if (of_property_read_u32(child, "marvell,pcie-lane",
- &port->lane))
- port->lane = 0;
-
- port->name = kasprintf(GFP_KERNEL, "pcie%d.%d",
- port->port, port->lane);
-
- port->devfn = of_pci_get_devfn(child);
- if (port->devfn < 0)
- continue;
-
- ret = mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_MEM,
- &port->mem_target, &port->mem_attr);
+ ret = mvebu_pcie_parse_port(pcie, port, child);
if (ret < 0) {
- dev_err(&pdev->dev, "PCIe%d.%d: cannot get tgt/attr for mem window\n",
- port->port, port->lane);
+ of_node_put(child);
+ return ret;
+ } else if (ret == 0) {
continue;
}
- if (resource_size(&pcie->io) != 0)
- mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO,
- &port->io_target, &port->io_attr);
- else {
- port->io_target = -1;
- port->io_attr = -1;
- }
+ port->dn = child;
+ i++;
+ }
+ pcie->nports = i;
- port->reset_gpio = of_get_named_gpio_flags(child,
- "reset-gpios", 0, &flags);
- if (gpio_is_valid(port->reset_gpio)) {
- u32 reset_udelay = 20000;
-
- port->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
- port->reset_name = kasprintf(GFP_KERNEL,
- "pcie%d.%d-reset", port->port, port->lane);
- of_property_read_u32(child, "reset-delay-us",
- &reset_udelay);
-
- ret = devm_gpio_request_one(&pdev->dev,
- port->reset_gpio, GPIOF_DIR_OUT, port->reset_name);
- if (ret) {
- if (ret == -EPROBE_DEFER)
- return ret;
- continue;
- }
-
- gpio_set_value(port->reset_gpio,
- (port->reset_active_low) ? 1 : 0);
- msleep(reset_udelay/1000);
- }
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
- port->clk = of_clk_get_by_name(child, NULL);
- if (IS_ERR(port->clk)) {
- dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
- port->port, port->lane);
+ child = port->dn;
+ if (!child)
continue;
- }
- ret = clk_prepare_enable(port->clk);
- if (ret)
+ ret = mvebu_pcie_powerup(port);
+ if (ret < 0)
continue;
port->base = mvebu_pcie_map_registers(pdev, child, port);
if (IS_ERR(port->base)) {
- dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
- port->port, port->lane);
+ dev_err(&pdev->dev, "%s: cannot map registers\n",
+ port->name);
port->base = NULL;
- clk_disable_unprepare(port->clk);
+ mvebu_pcie_powerdown(port);
continue;
}
mvebu_pcie_set_local_dev_nr(port, 1);
-
- port->dn = child;
mvebu_sw_pci_bridge_init(port);
- i++;
}
pcie->nports = i;
static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
unsigned int busnr)
{
- pgprot_t prot = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_XN |
- L_PTE_MT_DEV_SHARED | L_PTE_SHARED;
+ pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
+ L_PTE_XN | L_PTE_MT_DEV_SHARED | L_PTE_SHARED);
phys_addr_t cs = pcie->cs->start;
struct tegra_pcie_bus *bus;
unsigned int i;
return 0;
}
-static int xgene_pcie_msi_enable(struct pci_bus *bus)
-{
- struct device_node *msi_node;
-
- msi_node = of_parse_phandle(bus->dev.of_node,
- "msi-parent", 0);
- if (!msi_node)
- return -ENODEV;
-
- bus->msi = of_pci_find_msi_chip_by_node(msi_node);
- if (!bus->msi)
- return -ENODEV;
-
- of_node_put(msi_node);
- bus->msi->dev = &bus->dev;
- return 0;
-}
-
static int xgene_pcie_probe_bridge(struct platform_device *pdev)
{
struct device_node *dn = pdev->dev.of_node;
if (!bus)
return -ENOMEM;
- if (IS_ENABLED(CONFIG_PCI_MSI))
- if (xgene_pcie_msi_enable(bus))
- dev_info(port->dev, "failed to enable MSI\n");
-
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
pci_bus_add_devices(bus);
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
-#define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8)
+#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8)
#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
return sys->private_data;
}
-int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val)
+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
{
- *val = readl(addr);
+ if ((uintptr_t)addr & (size - 1)) {
+ *val = 0;
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
- if (size == 1)
- *val = (*val >> (8 * (where & 3))) & 0xff;
+ if (size == 4)
+ *val = readl(addr);
else if (size == 2)
- *val = (*val >> (8 * (where & 3))) & 0xffff;
- else if (size != 4)
+ *val = readw(addr);
+ else if (size == 1)
+ *val = readb(addr);
+ else {
+ *val = 0;
return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
return PCIBIOS_SUCCESSFUL;
}
-int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val)
+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
{
+ if ((uintptr_t)addr & (size - 1))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
if (size == 4)
writel(val, addr);
else if (size == 2)
- writew(val, addr + (where & 2));
+ writew(val, addr);
else if (size == 1)
- writeb(val, addr + (where & 3));
+ writeb(val, addr);
else
return PCIBIOS_BAD_REGISTER_NUMBER;
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 & ~0x3), where,
- size, val);
+ ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
return 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 & ~0x3), where,
- size, val);
+ ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
return ret;
}
void dw_pcie_msi_init(struct pcie_port *pp)
{
+ u64 msi_target;
+
pp->msi_data = __get_free_pages(GFP_KERNEL, 0);
+ msi_target = virt_to_phys((void *)pp->msi_data);
/* program the msi_data */
dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
- virt_to_phys((void *)pp->msi_data));
- dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
+ (u32)(msi_target & 0xffffffff));
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
+ (u32)(msi_target >> 32 & 0xffffffff));
}
static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
}
*pos = pos0;
+ desc->nvec_used = no_irqs;
+ desc->msi_attrib.multiple = order_base_2(no_irqs);
+
return irq;
no_valid_irq:
return -ENOSPC;
}
+static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
+{
+ struct msi_msg msg;
+ u64 msi_target;
+
+ if (pp->ops->get_msi_addr)
+ msi_target = pp->ops->get_msi_addr(pp);
+ else
+ msi_target = virt_to_phys((void *)pp->msi_data);
+
+ msg.address_lo = (u32)(msi_target & 0xffffffff);
+ msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff);
+
+ if (pp->ops->get_msi_data)
+ msg.data = pp->ops->get_msi_data(pp, pos);
+ else
+ msg.data = pos;
+
+ pci_write_msi_msg(irq, &msg);
+}
+
static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
struct msi_desc *desc)
{
int irq, pos;
- struct msi_msg msg;
struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
if (desc->msi_attrib.is_msix)
if (irq < 0)
return irq;
- if (pp->ops->get_msi_addr)
- msg.address_lo = pp->ops->get_msi_addr(pp);
- else
- msg.address_lo = virt_to_phys((void *)pp->msi_data);
- msg.address_hi = 0x0;
+ dw_msi_setup_msg(pp, irq, pos);
- if (pp->ops->get_msi_data)
- msg.data = pp->ops->get_msi_data(pp, pos);
- else
- msg.data = pos;
+ return 0;
+}
- pci_write_msi_msg(irq, &msg);
+static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev,
+ int nvec, int type)
+{
+#ifdef CONFIG_PCI_MSI
+ int irq, pos;
+ struct msi_desc *desc;
+ struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata);
+
+ /* MSI-X interrupts are not supported */
+ if (type == PCI_CAP_ID_MSIX)
+ return -EINVAL;
+
+ WARN_ON(!list_is_singular(&pdev->dev.msi_list));
+ desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list);
+
+ irq = assign_irq(nvec, desc, &pos);
+ if (irq < 0)
+ return irq;
+
+ dw_msi_setup_msg(pp, irq, pos);
return 0;
+#else
+ return -EINVAL;
+#endif
}
static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
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,
};
}
}
- if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
- dev_err(pp->dev, "Failed to parse the number of lanes\n");
- return -EINVAL;
- }
+ ret = of_property_read_u32(np, "num-lanes", &pp->lanes);
+ if (ret)
+ pp->lanes = 0;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
if (!pp->ops->msi_host_init) {
u32 devfn, int where, int size, u32 *val)
{
int ret, type;
- u32 address, busdev, cfg_size;
+ u32 busdev, cfg_size;
u64 cpu_addr;
void __iomem *va_cfg_base;
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
- address = where & ~0x3;
if (bus->parent->number == pp->root_bus_nr) {
type = PCIE_ATU_TYPE_CFG0;
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
type, cpu_addr,
busdev, cfg_size);
- ret = dw_pcie_cfg_read(va_cfg_base + address, where, size, val);
+ ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
PCIE_ATU_TYPE_IO, pp->io_mod_base,
pp->io_bus_addr, pp->io_size);
u32 devfn, int where, int size, u32 val)
{
int ret, type;
- u32 address, busdev, cfg_size;
+ u32 busdev, cfg_size;
u64 cpu_addr;
void __iomem *va_cfg_base;
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
- address = where & ~0x3;
if (bus->parent->number == pp->root_bus_nr) {
type = PCIE_ATU_TYPE_CFG0;
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
type, cpu_addr,
busdev, cfg_size);
- ret = dw_pcie_cfg_write(va_cfg_base + address, where, size, val);
+ ret = dw_pcie_cfg_write(va_cfg_base + where, size, val);
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
PCIE_ATU_TYPE_IO, pp->io_mod_base,
pp->io_bus_addr, pp->io_size);
case 8:
val |= PORT_LINK_MODE_8_LANES;
break;
+ default:
+ dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes);
+ return;
}
dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
void (*host_init)(struct pcie_port *pp);
void (*msi_set_irq)(struct pcie_port *pp, int irq);
void (*msi_clear_irq)(struct pcie_port *pp, int irq);
- u32 (*get_msi_addr)(struct pcie_port *pp);
+ phys_addr_t (*get_msi_addr)(struct pcie_port *pp);
u32 (*get_msi_data)(struct pcie_port *pp, int pos);
void (*scan_bus)(struct pcie_port *pp);
int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip);
};
-int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val);
-int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val);
+int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
+int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
void dw_pcie_msi_init(struct pcie_port *pp);
int dw_pcie_link_up(struct pcie_port *pp);
return -ENOMEM;
}
+ if (of_property_read_bool(np, "brcm,pcie-ob")) {
+ u32 val;
+
+ ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
+ &val);
+ if (ret) {
+ dev_err(pcie->dev,
+ "missing brcm,pcie-ob-axi-offset property\n");
+ return ret;
+ }
+ pcie->ob.axi_offset = val;
+
+ ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
+ &val);
+ if (ret) {
+ dev_err(pcie->dev,
+ "missing brcm,pcie-ob-window-size property\n");
+ return ret;
+ }
+ pcie->ob.window_size = (resource_size_t)val * SZ_1M;
+
+ if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size"))
+ pcie->ob.set_oarr_size = true;
+
+ pcie->need_ob_cfg = true;
+ }
+
/* PHY use is optional */
pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
if (IS_ERR(pcie->phy)) {
/*
* Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de>
- * Copyright (C) 2015 Broadcom Corporatcommon ion
+ * Copyright (C) 2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
#include "pcie-iproc.h"
#define CLK_CONTROL_OFFSET 0x000
+#define EP_PERST_SOURCE_SELECT_SHIFT 2
+#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT)
#define EP_MODE_SURVIVE_PERST_SHIFT 1
#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT)
#define RC_PCIE_RST_OUTPUT_SHIFT 0
#define SYS_RC_INTX_EN 0x330
#define SYS_RC_INTX_MASK 0xf
+#define PCIE_LINK_STATUS_OFFSET 0xf0c
+#define PCIE_PHYLINKUP_SHIFT 3
+#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT)
+#define PCIE_DL_ACTIVE_SHIFT 2
+#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)
+
+#define OARR_VALID_SHIFT 0
+#define OARR_VALID BIT(OARR_VALID_SHIFT)
+#define OARR_SIZE_CFG_SHIFT 1
+#define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT)
+
+#define OARR_LO(window) (0xd20 + (window) * 8)
+#define OARR_HI(window) (0xd24 + (window) * 8)
+#define OMAP_LO(window) (0xd40 + (window) * 8)
+#define OMAP_HI(window) (0xd44 + (window) * 8)
+
+#define MAX_NUM_OB_WINDOWS 2
+
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
{
struct iproc_pcie *pcie;
u32 val;
/*
- * Configure the PCIe controller as root complex and send a downstream
- * reset
+ * Select perst_b signal as reset source. Put the device into reset,
+ * and then bring it out of reset
*/
- val = EP_MODE_SURVIVE_PERST | RC_PCIE_RST_OUTPUT;
+ val = readl(pcie->base + CLK_CONTROL_OFFSET);
+ val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
+ ~RC_PCIE_RST_OUTPUT;
writel(val, pcie->base + CLK_CONTROL_OFFSET);
udelay(250);
- val &= ~EP_MODE_SURVIVE_PERST;
+
+ val |= RC_PCIE_RST_OUTPUT;
writel(val, pcie->base + CLK_CONTROL_OFFSET);
- msleep(250);
+ msleep(100);
}
static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
{
u8 hdr_type;
- u32 link_ctrl;
+ u32 link_ctrl, class, val;
u16 pos, link_status;
- int link_is_active = 0;
+ bool link_is_active = false;
+
+ val = readl(pcie->base + PCIE_LINK_STATUS_OFFSET);
+ if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
+ dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
+ return -ENODEV;
+ }
/* make sure we are not in EP mode */
pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type);
}
/* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */
- pci_bus_write_config_word(bus, 0, PCI_CLASS_DEVICE,
- PCI_CLASS_BRIDGE_PCI);
+#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c
+#define PCI_CLASS_BRIDGE_MASK 0xffff00
+#define PCI_CLASS_BRIDGE_SHIFT 8
+ pci_bus_read_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, &class);
+ class &= ~PCI_CLASS_BRIDGE_MASK;
+ class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT);
+ pci_bus_write_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, class);
/* check link status to see if link is active */
pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status);
if (link_status & PCI_EXP_LNKSTA_NLW)
- link_is_active = 1;
+ link_is_active = true;
if (!link_is_active) {
/* try GEN 1 link speed */
pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA,
&link_status);
if (link_status & PCI_EXP_LNKSTA_NLW)
- link_is_active = 1;
+ link_is_active = true;
}
}
writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
}
+/**
+ * Some iProc SoCs require the SW to configure the outbound address mapping
+ *
+ * Outbound address translation:
+ *
+ * iproc_pcie_address = axi_address - axi_offset
+ * OARR = iproc_pcie_address
+ * OMAP = pci_addr
+ *
+ * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
+ */
+static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
+ u64 pci_addr, resource_size_t size)
+{
+ struct iproc_pcie_ob *ob = &pcie->ob;
+ unsigned i;
+ u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
+ u64 remainder;
+
+ if (size > max_size) {
+ dev_err(pcie->dev,
+ "res size 0x%pap exceeds max supported size 0x%llx\n",
+ &size, max_size);
+ return -EINVAL;
+ }
+
+ div64_u64_rem(size, ob->window_size, &remainder);
+ if (remainder) {
+ dev_err(pcie->dev,
+ "res size %pap needs to be multiple of window size %pap\n",
+ &size, &ob->window_size);
+ return -EINVAL;
+ }
+
+ if (axi_addr < ob->axi_offset) {
+ dev_err(pcie->dev,
+ "axi address %pap less than offset %pap\n",
+ &axi_addr, &ob->axi_offset);
+ return -EINVAL;
+ }
+
+ /*
+ * Translate the AXI address to the internal address used by the iProc
+ * PCIe core before programming the OARR
+ */
+ axi_addr -= ob->axi_offset;
+
+ for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
+ writel(lower_32_bits(axi_addr) | OARR_VALID |
+ (ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
+ writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
+ writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
+ writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
+
+ size -= ob->window_size;
+ if (size == 0)
+ break;
+
+ axi_addr += ob->window_size;
+ pci_addr += ob->window_size;
+ }
+
+ return 0;
+}
+
+static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
+ struct list_head *resources)
+{
+ struct resource_entry *window;
+ int ret;
+
+ resource_list_for_each_entry(window, resources) {
+ struct resource *res = window->res;
+ u64 res_type = resource_type(res);
+
+ switch (res_type) {
+ case IORESOURCE_IO:
+ case IORESOURCE_BUS:
+ break;
+ case IORESOURCE_MEM:
+ ret = iproc_pcie_setup_ob(pcie, res->start,
+ res->start - window->offset,
+ resource_size(res));
+ if (ret)
+ return ret;
+ break;
+ default:
+ dev_err(pcie->dev, "invalid resource %pR\n", res);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
{
int ret;
iproc_pcie_reset(pcie);
+ if (pcie->need_ob_cfg) {
+ ret = iproc_pcie_map_ranges(pcie, res);
+ if (ret) {
+ dev_err(pcie->dev, "map failed\n");
+ goto err_power_off_phy;
+ }
+ }
+
#ifdef CONFIG_ARM
pcie->sysdata.private_data = pcie;
sysdata = &pcie->sysdata;
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
-#ifdef CONFIG_ARM
pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
-#endif
pci_bus_add_devices(bus);
return 0;
#ifndef _PCIE_IPROC_H
#define _PCIE_IPROC_H
-#define IPROC_PCIE_MAX_NUM_IRQS 6
+/**
+ * iProc PCIe outbound mapping
+ * @set_oarr_size: indicates the OARR size bit needs to be set
+ * @axi_offset: offset from the AXI address to the internal address used by
+ * the iProc PCIe core
+ * @window_size: outbound window size
+ */
+struct iproc_pcie_ob {
+ bool set_oarr_size;
+ resource_size_t axi_offset;
+ resource_size_t window_size;
+};
/**
* iProc PCIe device
* @dev: pointer to device data structure
* @base: PCIe host controller I/O register base
- * @resources: linked list of all PCI resources
* @sysdata: Per PCI controller data (ARM-specific)
* @root_bus: pointer to root bus
* @phy: optional PHY device that controls the Serdes
* @irqs: interrupt IDs
+ * @map_irq: function callback to map interrupts
+ * @need_ob_cfg: indidates SW needs to configure the outbound mapping window
+ * @ob: outbound mapping parameters
*/
struct iproc_pcie {
struct device *dev;
#endif
struct pci_bus *root_bus;
struct phy *phy;
- int irqs[IPROC_PCIE_MAX_NUM_IRQS];
int (*map_irq)(const struct pci_dev *, u8, u8);
+ bool need_ob_cfg;
+ struct iproc_pcie_ob ob;
};
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
* default value in capability register is 512 bytes. So force
* it to 128 here.
*/
- dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_DEVCTL, 4, &val);
+ dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val);
val &= ~PCI_EXP_DEVCTL_READRQ;
- dw_pcie_cfg_write(pp->dbi_base, exp_cap_off + PCI_EXP_DEVCTL, 4, val);
+ dw_pcie_cfg_write(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val);
- dw_pcie_cfg_write(pp->dbi_base, PCI_VENDOR_ID, 2, 0x104A);
- dw_pcie_cfg_write(pp->dbi_base, PCI_DEVICE_ID, 2, 0xCD80);
+ dw_pcie_cfg_write(pp->dbi_base + PCI_VENDOR_ID, 2, 0x104A);
+ dw_pcie_cfg_write(pp->dbi_base + PCI_DEVICE_ID, 2, 0xCD80);
/*
* if is_gen1 is set then handle it, so that some buggy card
* also works
*/
if (spear13xx_pcie->is_gen1) {
- dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_LNKCAP, 4,
- &val);
+ dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
+ 4, &val);
if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
val &= ~((u32)PCI_EXP_LNKCAP_SLS);
val |= PCI_EXP_LNKCAP_SLS_2_5GB;
- dw_pcie_cfg_write(pp->dbi_base, exp_cap_off +
- PCI_EXP_LNKCAP, 4, val);
+ dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
+ PCI_EXP_LNKCAP, 4, val);
}
- dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_LNKCTL2, 4,
- &val);
+ dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
+ 2, &val);
if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
val &= ~((u32)PCI_EXP_LNKCAP_SLS);
val |= PCI_EXP_LNKCAP_SLS_2_5GB;
- dw_pcie_cfg_write(pp->dbi_base, exp_cap_off +
- PCI_EXP_LNKCTL2, 4, val);
+ dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
+ PCI_EXP_LNKCTL2, 2, val);
}
}
int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
+ struct msi_controller *chip = dev->bus->msi;
struct msi_desc *entry;
int ret;
+ if (chip && chip->setup_irqs)
+ return chip->setup_irqs(chip, dev, nvec, type);
/*
* If an architecture wants to support multiple MSI, it needs to
* override arch_setup_msi_irqs()
int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev,
struct msi_desc *desc);
+ int (*setup_irqs)(struct msi_controller *chip, struct pci_dev *dev,
+ int nvec, int type);
void (*teardown_irq)(struct msi_controller *chip, unsigned int irq);
};
int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
int of_get_pci_domain_nr(struct device_node *node);
void of_pci_dma_configure(struct pci_dev *pci_dev);
+void of_pci_check_probe_only(void);
#else
static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
{
}
static inline void of_pci_dma_configure(struct pci_dev *pci_dev) { }
+
+static inline void of_pci_check_probe_only(void) { }
#endif
#if defined(CONFIG_OF_ADDRESS)