Commit 84d727a1 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Josh Boyer

powerpc/4xx: Add support for ISA holes on 4xx PCI/X/E

This adds support for ISA memory holes on the PCI, PCI-X and
PCI-E busses of the 4xx platforms. The patch includes changes
to the Bamboo and Canyonlands device-trees to add such a hole,
others can be updated separately.

The ISA memory hole is an additional outbound window configured
in the bridge to generate PCI cycles in the low memory addresses,
thus allowing to access things such as the hard-decoded VGA
aperture at 0xa0000..0xbffff or other similar things. It's made
accessible to userspace via the new legacy_mem file in sysfs for
which support was added by a previous patch.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarJosh Boyer <jwboyer@linux.vnet.ibm.com>
parent 9d2421e8
......@@ -269,7 +269,8 @@ PCI0: pci@ec000000 {
* later cannot be changed. Chip supports a second
* IO range but we don't use it for now
*/
ranges = <0x02000000 0x00000000 0xa0000000 0x00000000 0xa0000000 0x00000000 0x20000000
ranges = <0x02000000 0x00000000 0xa0000000 0x00000000 0xa0000000 0x00000000 0x40000000
0x02000000 0x00000000 0x00000000 0x00000000 0xe0000000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x00000000 0xe8000000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
......
......@@ -343,6 +343,7 @@ PCIX0: pci@c0ec00000 {
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000d 0x80000000 0x00000000 0x80000000
0x02000000 0x00000000 0x00000000 0x0000000c 0x0ee00000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000c 0x08000000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
......@@ -373,6 +374,7 @@ PCIE0: pciex@d00000000 {
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000
0x02000000 0x00000000 0x00000000 0x0000000f 0x00000000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
......@@ -414,6 +416,7 @@ PCIE1: pciex@d20000000 {
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x80000000 0x00000000 0x80000000
0x02000000 0x00000000 0x00000000 0x0000000f 0x00100000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000f 0x80010000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
......
......@@ -194,11 +194,41 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
* 4xx PCI 2.x part
*/
static int __init ppc4xx_setup_one_pci_PMM(struct pci_controller *hose,
void __iomem *reg,
u64 plb_addr,
u64 pci_addr,
u64 size,
unsigned int flags,
int index)
{
u32 ma, pcila, pciha;
if ((plb_addr + size) > 0xffffffffull || !is_power_of_2(size) ||
size < 0x1000 || (plb_addr & (size - 1)) != 0) {
printk(KERN_WARNING "%s: Resource out of range\n",
hose->dn->full_name);
return -1;
}
ma = (0xffffffffu << ilog2(size)) | 1;
if (flags & IORESOURCE_PREFETCH)
ma |= 2;
pciha = RES_TO_U32_HIGH(pci_addr);
pcila = RES_TO_U32_LOW(pci_addr);
writel(plb_addr, reg + PCIL0_PMM0LA + (0x10 * index));
writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * index));
writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * index));
writel(ma, reg + PCIL0_PMM0MA + (0x10 * index));
return 0;
}
static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
void __iomem *reg)
{
u32 la, ma, pcila, pciha;
int i, j;
int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
......@@ -213,28 +243,29 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
break;
}
/* Calculate register values */
la = res->start;
pciha = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
pcila = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
/* Configure the resource */
if (ppc4xx_setup_one_pci_PMM(hose, reg,
res->start,
res->start - hose->pci_mem_offset,
res->end + 1 - res->start,
res->flags,
j) == 0) {
j++;
ma = res->end + 1 - res->start;
if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) {
printk(KERN_WARNING "%s: Resource out of range\n",
hose->dn->full_name);
continue;
/* If the resource PCI address is 0 then we have our
* ISA memory hole
*/
if (res->start == hose->pci_mem_offset)
found_isa_hole = 1;
}
ma = (0xffffffffu << ilog2(ma)) | 0x1;
if (res->flags & IORESOURCE_PREFETCH)
ma |= 0x2;
/* Program register values */
writel(la, reg + PCIL0_PMM0LA + (0x10 * j));
writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * j));
writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * j));
writel(ma, reg + PCIL0_PMM0MA + (0x10 * j));
j++;
}
/* Handle ISA memory hole if not already covered */
if (j <= 2 && !found_isa_hole && hose->isa_mem_size)
if (ppc4xx_setup_one_pci_PMM(hose, reg, hose->isa_mem_phys, 0,
hose->isa_mem_size, 0, j) == 0)
printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
hose->dn->full_name);
}
static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose,
......@@ -352,41 +383,32 @@ static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
* 4xx PCI-X part
*/
static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
void __iomem *reg)
static int __init ppc4xx_setup_one_pcix_POM(struct pci_controller *hose,
void __iomem *reg,
u64 plb_addr,
u64 pci_addr,
u64 size,
unsigned int flags,
int index)
{
u32 lah, lal, pciah, pcial, sa;
int i, j;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
continue;
if (j > 1) {
printk(KERN_WARNING "%s: Too many ranges\n",
if (!is_power_of_2(size) || size < 0x1000 ||
(plb_addr & (size - 1)) != 0) {
printk(KERN_WARNING "%s: Resource out of range\n",
hose->dn->full_name);
break;
return -1;
}
/* Calculate register values */
lah = RES_TO_U32_HIGH(res->start);
lal = RES_TO_U32_LOW(res->start);
pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
sa = res->end + 1 - res->start;
if (!is_power_of_2(sa) || sa < 0x100000 ||
sa > 0xffffffffu) {
printk(KERN_WARNING "%s: Resource out of range\n",
hose->dn->full_name);
continue;
}
sa = (0xffffffffu << ilog2(sa)) | 0x1;
lah = RES_TO_U32_HIGH(plb_addr);
lal = RES_TO_U32_LOW(plb_addr);
pciah = RES_TO_U32_HIGH(pci_addr);
pcial = RES_TO_U32_LOW(pci_addr);
sa = (0xffffffffu << ilog2(size)) | 0x1;
/* Program register values */
if (j == 0) {
if (index == 0) {
writel(lah, reg + PCIX0_POM0LAH);
writel(lal, reg + PCIX0_POM0LAL);
writel(pciah, reg + PCIX0_POM0PCIAH);
......@@ -399,8 +421,51 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
writel(pcial, reg + PCIX0_POM1PCIAL);
writel(sa, reg + PCIX0_POM1SA);
}
return 0;
}
static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
void __iomem *reg)
{
int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
continue;
if (j > 1) {
printk(KERN_WARNING "%s: Too many ranges\n",
hose->dn->full_name);
break;
}
/* Configure the resource */
if (ppc4xx_setup_one_pcix_POM(hose, reg,
res->start,
res->start - hose->pci_mem_offset,
res->end + 1 - res->start,
res->flags,
j) == 0) {
j++;
/* If the resource PCI address is 0 then we have our
* ISA memory hole
*/
if (res->start == hose->pci_mem_offset)
found_isa_hole = 1;
}
}
/* Handle ISA memory hole if not already covered */
if (j <= 1 && !found_isa_hole && hose->isa_mem_size)
if (ppc4xx_setup_one_pcix_POM(hose, reg, hose->isa_mem_phys, 0,
hose->isa_mem_size, 0, j) == 0)
printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
hose->dn->full_name);
}
static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose,
......@@ -1317,48 +1382,42 @@ static struct pci_ops ppc4xx_pciex_pci_ops =
.write = ppc4xx_pciex_write_config,
};
static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port,
struct pci_controller *hose,
void __iomem *mbase)
void __iomem *mbase,
u64 plb_addr,
u64 pci_addr,
u64 size,
unsigned int flags,
int index)
{
u32 lah, lal, pciah, pcial, sa;
int i, j;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
continue;
if (j > 1) {
printk(KERN_WARNING "%s: Too many ranges\n",
port->node->full_name);
break;
if (!is_power_of_2(size) ||
(index < 2 && size < 0x100000) ||
(index == 2 && size < 0x100) ||
(plb_addr & (size - 1)) != 0) {
printk(KERN_WARNING "%s: Resource out of range\n",
hose->dn->full_name);
return -1;
}
/* Calculate register values */
lah = RES_TO_U32_HIGH(res->start);
lal = RES_TO_U32_LOW(res->start);
pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
sa = res->end + 1 - res->start;
if (!is_power_of_2(sa) || sa < 0x100000 ||
sa > 0xffffffffu) {
printk(KERN_WARNING "%s: Resource out of range\n",
port->node->full_name);
continue;
}
sa = (0xffffffffu << ilog2(sa)) | 0x1;
lah = RES_TO_U32_HIGH(plb_addr);
lal = RES_TO_U32_LOW(plb_addr);
pciah = RES_TO_U32_HIGH(pci_addr);
pcial = RES_TO_U32_LOW(pci_addr);
sa = (0xffffffffu << ilog2(size)) | 0x1;
/* Program register values */
switch (j) {
switch (index) {
case 0:
out_le32(mbase + PECFG_POM0LAH, pciah);
out_le32(mbase + PECFG_POM0LAL, pcial);
dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
/* Note that 3 here means enabled | single region */
dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3);
break;
case 1:
......@@ -1367,23 +1426,74 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
/* Note that 3 here means enabled | single region */
dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3);
break;
}
j++;
}
/* Configure IO, always 64K starting at 0 */
if (hose->io_resource.flags & IORESOURCE_IO) {
lah = RES_TO_U32_HIGH(hose->io_base_phys);
lal = RES_TO_U32_LOW(hose->io_base_phys);
out_le32(mbase + PECFG_POM2LAH, 0);
out_le32(mbase + PECFG_POM2LAL, 0);
case 2:
out_le32(mbase + PECFG_POM2LAH, pciah);
out_le32(mbase + PECFG_POM2LAL, pcial);
dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0xffff0000 | 3);
/* Note that 3 here means enabled | IO space !!! */
dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, sa | 3);
break;
}
return 0;
}
static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
struct pci_controller *hose,
void __iomem *mbase)
{
int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
struct resource *res = &hose->mem_resources[i];
/* we only care about memory windows */
if (!(res->flags & IORESOURCE_MEM))
continue;
if (j > 1) {
printk(KERN_WARNING "%s: Too many ranges\n",
port->node->full_name);
break;
}
/* Configure the resource */
if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
res->start,
res->start - hose->pci_mem_offset,
res->end + 1 - res->start,
res->flags,
j) == 0) {
j++;
/* If the resource PCI address is 0 then we have our
* ISA memory hole
*/
if (res->start == hose->pci_mem_offset)
found_isa_hole = 1;
}
}
/* Handle ISA memory hole if not already covered */
if (j <= 1 && !found_isa_hole && hose->isa_mem_size)
if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
hose->isa_mem_phys, 0,
hose->isa_mem_size, 0, j) == 0)
printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
hose->dn->full_name);
/* Configure IO, always 64K starting at 0. We hard wire it to 64K !
* Note also that it -has- to be region index 2 on this HW
*/
if (hose->io_resource.flags & IORESOURCE_IO)
ppc4xx_setup_one_pciex_POM(port, hose, mbase,
hose->io_base_phys, 0,
0x10000, IORESOURCE_IO, 2);
}
static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port,
......
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