Commit 80daac3f authored by Stefan Roese's avatar Stefan Roese Committed by Josh Boyer

[POWERPC] 4xx: Add endpoint support to 4xx PCIe driver

This patch adds basic endpoint support to the 4xx PCIe driver.

This is done by checking the device_type property of the PCIe
device node ("pci" for root-complex and "pci-endpoint" for endpoint
configuration).

Note: Currently we map a fixed 64MByte window to PLB address 0 (SDRAM).
This should probably be configurable via a dts property.
Signed-off-by: default avatarStefan Roese <sr@denx.de>
Acked-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarJosh Boyer <jwboyer@linux.vnet.ibm.com>
parent a96df496
...@@ -1387,28 +1387,59 @@ static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port, ...@@ -1387,28 +1387,59 @@ static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port,
resource_size_t size = res->end - res->start + 1; resource_size_t size = res->end - res->start + 1;
u64 sa; u64 sa;
/* Calculate window size */ if (port->endpoint) {
sa = (0xffffffffffffffffull << ilog2(size));; resource_size_t ep_addr = 0;
if (res->flags & IORESOURCE_PREFETCH) resource_size_t ep_size = 32 << 20;
sa |= 0x8;
/* Currently we map a fixed 64MByte window to PLB address
* 0 (SDRAM). This should probably be configurable via a dts
* property.
*/
/* Calculate window size */
sa = (0xffffffffffffffffull << ilog2(ep_size));;
/* Setup BAR0 */
out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa) |
PCI_BASE_ADDRESS_MEM_TYPE_64);
out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa)); /* Disable BAR1 & BAR2 */
out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa)); out_le32(mbase + PECFG_BAR1MPA, 0);
out_le32(mbase + PECFG_BAR2HMPA, 0);
out_le32(mbase + PECFG_BAR2LMPA, 0);
/* The setup of the split looks weird to me ... let's see if it works */ out_le32(mbase + PECFG_PIM01SAH, RES_TO_U32_HIGH(sa));
out_le32(mbase + PECFG_PIM0LAL, 0x00000000); out_le32(mbase + PECFG_PIM01SAL, RES_TO_U32_LOW(sa));
out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
out_le32(mbase + PECFG_PIM1LAL, 0x00000000); out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(ep_addr));
out_le32(mbase + PECFG_PIM1LAH, 0x00000000); out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(ep_addr));
out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); } else {
out_le32(mbase + PECFG_PIM01SAL, 0x00000000); /* Calculate window size */
sa = (0xffffffffffffffffull << ilog2(size));;
if (res->flags & IORESOURCE_PREFETCH)
sa |= 0x8;
out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa));
/* The setup of the split looks weird to me ... let's see
* if it works
*/
out_le32(mbase + PECFG_PIM0LAL, 0x00000000);
out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
out_le32(mbase + PECFG_PIM1LAL, 0x00000000);
out_le32(mbase + PECFG_PIM1LAH, 0x00000000);
out_le32(mbase + PECFG_PIM01SAH, 0xffff0000);
out_le32(mbase + PECFG_PIM01SAL, 0x00000000);
out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(res->start));
out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(res->start));
}
/* Enable inbound mapping */ /* Enable inbound mapping */
out_le32(mbase + PECFG_PIMEN, 0x1); out_le32(mbase + PECFG_PIMEN, 0x1);
out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(res->start));
out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(res->start));
/* Enable I/O, Mem, and Busmaster cycles */ /* Enable I/O, Mem, and Busmaster cycles */
out_le16(mbase + PCI_COMMAND, out_le16(mbase + PCI_COMMAND,
in_le16(mbase + PCI_COMMAND) | in_le16(mbase + PCI_COMMAND) |
...@@ -1422,13 +1453,8 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) ...@@ -1422,13 +1453,8 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
const int *bus_range; const int *bus_range;
int primary = 0, busses; int primary = 0, busses;
void __iomem *mbase = NULL, *cfg_data = NULL; void __iomem *mbase = NULL, *cfg_data = NULL;
const u32 *pval;
/* XXX FIXME: Handle endpoint mode properly */ u32 val;
if (port->endpoint) {
printk(KERN_WARNING "PCIE%d: Port in endpoint mode !\n",
port->index);
return;
}
/* Check if primary bridge */ /* Check if primary bridge */
if (of_get_property(port->node, "primary", NULL)) if (of_get_property(port->node, "primary", NULL))
...@@ -1462,21 +1488,30 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) ...@@ -1462,21 +1488,30 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
hose->last_busno = hose->first_busno + busses; hose->last_busno = hose->first_busno + busses;
} }
/* We map the external config space in cfg_data and the host config if (!port->endpoint) {
* space in cfg_addr. External space is 1M per bus, internal space /* Only map the external config space in cfg_data for
* is 4K * PCIe root-complexes. External space is 1M per bus
*/
cfg_data = ioremap(port->cfg_space.start +
(hose->first_busno + 1) * 0x100000,
busses * 0x100000);
if (cfg_data == NULL) {
printk(KERN_ERR "%s: Can't map external config space !",
port->node->full_name);
goto fail;
}
hose->cfg_data = cfg_data;
}
/* Always map the host config space in cfg_addr.
* Internal space is 4K
*/ */
cfg_data = ioremap(port->cfg_space.start +
(hose->first_busno + 1) * 0x100000,
busses * 0x100000);
mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000); mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000);
if (cfg_data == NULL || mbase == NULL) { if (mbase == NULL) {
printk(KERN_ERR "%s: Can't map config space !", printk(KERN_ERR "%s: Can't map internal config space !",
port->node->full_name); port->node->full_name);
goto fail; goto fail;
} }
hose->cfg_data = cfg_data;
hose->cfg_addr = mbase; hose->cfg_addr = mbase;
pr_debug("PCIE %s, bus %d..%d\n", port->node->full_name, pr_debug("PCIE %s, bus %d..%d\n", port->node->full_name,
...@@ -1489,12 +1524,14 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) ...@@ -1489,12 +1524,14 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
port->hose = hose; port->hose = hose;
mbase = (void __iomem *)hose->cfg_addr; mbase = (void __iomem *)hose->cfg_addr;
/* if (!port->endpoint) {
* Set bus numbers on our root port /*
*/ * Set bus numbers on our root port
out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno); */
out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1); out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno);
out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno); out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1);
out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno);
}
/* /*
* OMRs are already reset, also disable PIMs * OMRs are already reset, also disable PIMs
...@@ -1515,17 +1552,49 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) ...@@ -1515,17 +1552,49 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
ppc4xx_configure_pciex_PIMs(port, hose, mbase, &dma_window); ppc4xx_configure_pciex_PIMs(port, hose, mbase, &dma_window);
/* The root complex doesn't show up if we don't set some vendor /* The root complex doesn't show up if we don't set some vendor
* and device IDs into it. Those are the same bogus one that the * and device IDs into it. The defaults below are the same bogus
* initial code in arch/ppc add. We might want to change that. * one that the initial code in arch/ppc had. This can be
* overwritten by setting the "vendor-id/device-id" properties
* in the pciex node.
*/ */
out_le16(mbase + 0x200, 0xaaa0 + port->index);
out_le16(mbase + 0x202, 0xbed0 + port->index);
/* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ /* Get the (optional) vendor-/device-id from the device-tree */
out_le32(mbase + 0x208, 0x06040001); pval = of_get_property(port->node, "vendor-id", NULL);
if (pval) {
val = *pval;
} else {
if (!port->endpoint)
val = 0xaaa0 + port->index;
else
val = 0xeee0 + port->index;
}
out_le16(mbase + 0x200, val);
pval = of_get_property(port->node, "device-id", NULL);
if (pval) {
val = *pval;
} else {
if (!port->endpoint)
val = 0xbed0 + port->index;
else
val = 0xfed0 + port->index;
}
out_le16(mbase + 0x202, val);
if (!port->endpoint) {
/* Set Class Code to PCI-PCI bridge and Revision Id to 1 */
out_le32(mbase + 0x208, 0x06040001);
printk(KERN_INFO "PCIE%d: successfully set as root-complex\n",
port->index);
} else {
/* Set Class Code to Processor/PPC */
out_le32(mbase + 0x208, 0x0b200001);
printk(KERN_INFO "PCIE%d: successfully set as endpoint\n",
port->index);
}
printk(KERN_INFO "PCIE%d: successfully set as root-complex\n",
port->index);
return; return;
fail: fail:
if (hose) if (hose)
...@@ -1542,6 +1611,7 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np) ...@@ -1542,6 +1611,7 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
const u32 *pval; const u32 *pval;
int portno; int portno;
unsigned int dcrs; unsigned int dcrs;
const char *val;
/* First, proceed to core initialization as we assume there's /* First, proceed to core initialization as we assume there's
* only one PCIe core in the system * only one PCIe core in the system
...@@ -1573,8 +1643,20 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np) ...@@ -1573,8 +1643,20 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
} }
port->sdr_base = *pval; port->sdr_base = *pval;
/* XXX Currently, we only support root complex mode */ /* Check if device_type property is set to "pci" or "pci-endpoint".
port->endpoint = 0; * Resulting from this setup this PCIe port will be configured
* as root-complex or as endpoint.
*/
val = of_get_property(port->node, "device_type", NULL);
if (!strcmp(val, "pci-endpoint")) {
port->endpoint = 1;
} else if (!strcmp(val, "pci")) {
port->endpoint = 0;
} else {
printk(KERN_ERR "PCIE: missing or incorrect device_type for %s\n",
np->full_name);
return;
}
/* Fetch config space registers address */ /* Fetch config space registers address */
if (of_address_to_resource(np, 0, &port->cfg_space)) { if (of_address_to_resource(np, 0, &port->cfg_space)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment