[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:
Родитель
a96df496ed
Коммит
80daac3f86
|
@ -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;
|
||||
u64 sa;
|
||||
|
||||
/* Calculate window size */
|
||||
sa = (0xffffffffffffffffull << ilog2(size));;
|
||||
if (res->flags & IORESOURCE_PREFETCH)
|
||||
sa |= 0x8;
|
||||
if (port->endpoint) {
|
||||
resource_size_t ep_addr = 0;
|
||||
resource_size_t ep_size = 32 << 20;
|
||||
|
||||
out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
|
||||
out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa));
|
||||
/* Currently we map a fixed 64MByte window to PLB address
|
||||
* 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 */
|
||||
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);
|
||||
/* 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);
|
||||
|
||||
/* 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 */
|
||||
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 */
|
||||
out_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;
|
||||
int primary = 0, busses;
|
||||
void __iomem *mbase = NULL, *cfg_data = NULL;
|
||||
|
||||
/* XXX FIXME: Handle endpoint mode properly */
|
||||
if (port->endpoint) {
|
||||
printk(KERN_WARNING "PCIE%d: Port in endpoint mode !\n",
|
||||
port->index);
|
||||
return;
|
||||
}
|
||||
const u32 *pval;
|
||||
u32 val;
|
||||
|
||||
/* Check if primary bridge */
|
||||
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;
|
||||
}
|
||||
|
||||
/* We map the external config space in cfg_data and the host config
|
||||
* space in cfg_addr. External space is 1M per bus, internal space
|
||||
* is 4K
|
||||
if (!port->endpoint) {
|
||||
/* Only map the external config space in cfg_data for
|
||||
* 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);
|
||||
if (cfg_data == NULL || mbase == NULL) {
|
||||
printk(KERN_ERR "%s: Can't map config space !",
|
||||
if (mbase == NULL) {
|
||||
printk(KERN_ERR "%s: Can't map internal config space !",
|
||||
port->node->full_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hose->cfg_data = cfg_data;
|
||||
hose->cfg_addr = mbase;
|
||||
|
||||
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;
|
||||
mbase = (void __iomem *)hose->cfg_addr;
|
||||
|
||||
/*
|
||||
* 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_SUBORDINATE_BUS, hose->last_busno);
|
||||
if (!port->endpoint) {
|
||||
/*
|
||||
* 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_SUBORDINATE_BUS, hose->last_busno);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/* 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
|
||||
* initial code in arch/ppc add. We might want to change that.
|
||||
* and device IDs into it. The defaults below are the same bogus
|
||||
* 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 */
|
||||
out_le32(mbase + 0x208, 0x06040001);
|
||||
/* Get the (optional) vendor-/device-id from the device-tree */
|
||||
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;
|
||||
fail:
|
||||
if (hose)
|
||||
|
@ -1542,6 +1611,7 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
|
|||
const u32 *pval;
|
||||
int portno;
|
||||
unsigned int dcrs;
|
||||
const char *val;
|
||||
|
||||
/* First, proceed to core initialization as we assume there's
|
||||
* 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;
|
||||
|
||||
/* XXX Currently, we only support root complex mode */
|
||||
port->endpoint = 0;
|
||||
/* Check if device_type property is set to "pci" or "pci-endpoint".
|
||||
* 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 */
|
||||
if (of_address_to_resource(np, 0, &port->cfg_space)) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче