[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: Stefan Roese <sr@denx.de> Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
This commit is contained in:
@@ -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;
|
|
||||||
|
|
||||||
out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
|
/* Currently we map a fixed 64MByte window to PLB address
|
||||||
out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa));
|
* 0 (SDRAM). This should probably be configurable via a dts
|
||||||
|
* property.
|
||||||
|
*/
|
||||||
|
|
||||||
/* The setup of the split looks weird to me ... let's see if it works */
|
/* Calculate window size */
|
||||||
out_le32(mbase + PECFG_PIM0LAL, 0x00000000);
|
sa = (0xffffffffffffffffull << ilog2(ep_size));;
|
||||||
out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
|
|
||||||
out_le32(mbase + PECFG_PIM1LAL, 0x00000000);
|
/* Setup BAR0 */
|
||||||
out_le32(mbase + PECFG_PIM1LAH, 0x00000000);
|
out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
|
||||||
out_le32(mbase + PECFG_PIM01SAH, 0xffff0000);
|
out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa) |
|
||||||
out_le32(mbase + PECFG_PIM01SAL, 0x00000000);
|
PCI_BASE_ADDRESS_MEM_TYPE_64);
|
||||||
|
|
||||||
|
/* Disable BAR1 & BAR2 */
|
||||||
|
out_le32(mbase + PECFG_BAR1MPA, 0);
|
||||||
|
out_le32(mbase + PECFG_BAR2HMPA, 0);
|
||||||
|
out_le32(mbase + PECFG_BAR2LMPA, 0);
|
||||||
|
|
||||||
|
out_le32(mbase + PECFG_PIM01SAH, RES_TO_U32_HIGH(sa));
|
||||||
|
out_le32(mbase + PECFG_PIM01SAL, RES_TO_U32_LOW(sa));
|
||||||
|
|
||||||
|
out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(ep_addr));
|
||||||
|
out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(ep_addr));
|
||||||
|
} else {
|
||||||
|
/* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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)) {
|
||||||
|
Reference in New Issue
Block a user