summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/powermac
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/powermac')
-rw-r--r--arch/powerpc/platforms/powermac/feature.c65
-rw-r--r--arch/powerpc/platforms/powermac/pci.c210
-rw-r--r--arch/powerpc/platforms/powermac/pic.c72
-rw-r--r--arch/powerpc/platforms/powermac/setup.c13
-rw-r--r--arch/powerpc/platforms/powermac/smp.c319
5 files changed, 447 insertions, 232 deletions
diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c
index b1f896952b1..d2915d64d45 100644
--- a/arch/powerpc/platforms/powermac/feature.c
+++ b/arch/powerpc/platforms/powermac/feature.c
@@ -101,7 +101,8 @@ static const char *macio_names[] =
"Keylargo",
"Pangea",
"Intrepid",
- "K2"
+ "K2",
+ "Shasta",
};
@@ -119,7 +120,7 @@ static const char *macio_names[] =
static struct device_node *uninorth_node;
static u32 __iomem *uninorth_base;
static u32 uninorth_rev;
-static int uninorth_u3;
+static int uninorth_maj;
static void __iomem *u3_ht;
/*
@@ -1399,8 +1400,15 @@ static long g5_fw_enable(struct device_node *node, long param, long value)
static long g5_mpic_enable(struct device_node *node, long param, long value)
{
unsigned long flags;
+ struct device_node *parent = of_get_parent(node);
+ int is_u3;
- if (node->parent == NULL || strcmp(node->parent->name, "u3"))
+ if (parent == NULL)
+ return 0;
+ is_u3 = strcmp(parent->name, "u3") == 0 ||
+ strcmp(parent->name, "u4") == 0;
+ of_node_put(parent);
+ if (!is_u3)
return 0;
LOCK(flags);
@@ -1464,7 +1472,7 @@ static long g5_i2s_enable(struct device_node *node, long param, long value)
},
};
- if (macio->type != macio_keylargo2 /* && macio->type != macio_shasta*/)
+ if (macio->type != macio_keylargo2 && macio->type != macio_shasta)
return -ENODEV;
if (strncmp(node->name, "i2s-", 4))
return -ENODEV;
@@ -1473,11 +1481,9 @@ static long g5_i2s_enable(struct device_node *node, long param, long value)
case 0:
case 1:
break;
-#if 0
case 2:
if (macio->type == macio_shasta)
break;
-#endif
default:
return -ENODEV;
}
@@ -1508,7 +1514,7 @@ static long g5_reset_cpu(struct device_node *node, long param, long value)
struct device_node *np;
macio = &macio_chips[0];
- if (macio->type != macio_keylargo2)
+ if (macio->type != macio_keylargo2 && macio->type != macio_shasta)
return -ENODEV;
np = find_path_device("/cpus");
@@ -1547,7 +1553,8 @@ static long g5_reset_cpu(struct device_node *node, long param, long value)
*/
void g5_phy_disable_cpu1(void)
{
- UN_OUT(U3_API_PHY_CONFIG_1, 0);
+ if (uninorth_maj == 3)
+ UN_OUT(U3_API_PHY_CONFIG_1, 0);
}
#endif /* CONFIG_POWER4 */
@@ -2462,6 +2469,14 @@ static struct pmac_mb_def pmac_mb_defs[] = {
PMAC_TYPE_POWERMAC_G5_U3L, g5_features,
0,
},
+ { "PowerMac11,2", "PowerMac G5 Dual Core",
+ PMAC_TYPE_POWERMAC_G5_U3L, g5_features,
+ 0,
+ },
+ { "PowerMac12,1", "iMac G5 (iSight)",
+ PMAC_TYPE_POWERMAC_G5_U3L, g5_features,
+ 0,
+ },
{ "RackMac3,1", "XServe G5",
PMAC_TYPE_XSERVE_G5, g5_features,
0,
@@ -2574,6 +2589,11 @@ static int __init probe_motherboard(void)
pmac_mb.model_name = "Unknown K2-based";
pmac_mb.features = g5_features;
break;
+ case macio_shasta:
+ pmac_mb.model_id = PMAC_TYPE_UNKNOWN_SHASTA;
+ pmac_mb.model_name = "Unknown Shasta-based";
+ pmac_mb.features = g5_features;
+ break;
#endif /* CONFIG_POWER4 */
default:
return -ENODEV;
@@ -2651,7 +2671,12 @@ static void __init probe_uninorth(void)
/* Locate G5 u3 */
if (uninorth_node == NULL) {
uninorth_node = of_find_node_by_name(NULL, "u3");
- uninorth_u3 = 1;
+ uninorth_maj = 3;
+ }
+ /* Locate G5 u4 */
+ if (uninorth_node == NULL) {
+ uninorth_node = of_find_node_by_name(NULL, "u4");
+ uninorth_maj = 4;
}
if (uninorth_node == NULL)
return;
@@ -2664,12 +2689,13 @@ static void __init probe_uninorth(void)
return;
uninorth_base = ioremap(address, 0x40000);
uninorth_rev = in_be32(UN_REG(UNI_N_VERSION));
- if (uninorth_u3)
+ if (uninorth_maj == 3 || uninorth_maj == 4)
u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000);
- printk(KERN_INFO "Found %s memory controller & host bridge,"
- " revision: %d\n", uninorth_u3 ? "U3" : "UniNorth",
- uninorth_rev);
+ printk(KERN_INFO "Found %s memory controller & host bridge"
+ " @ 0x%08x revision: 0x%02x\n", uninorth_maj == 3 ? "U3" :
+ uninorth_maj == 4 ? "U4" : "UniNorth",
+ (unsigned int)address, uninorth_rev);
printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base);
/* Set the arbitrer QAck delay according to what Apple does
@@ -2677,7 +2703,8 @@ static void __init probe_uninorth(void)
if (uninorth_rev < 0x11) {
actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK;
actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 :
- UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
+ UNI_N_ARB_CTRL_QACK_DELAY) <<
+ UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
UN_OUT(UNI_N_ARB_CTRL, actrl);
}
@@ -2685,7 +2712,8 @@ static void __init probe_uninorth(void)
* revs 1.5 to 2.O and Pangea. Seem to toggle the UniN Maxbus/PCI
* memory timeout
*/
- if ((uninorth_rev >= 0x11 && uninorth_rev <= 0x24) || uninorth_rev == 0xc0)
+ if ((uninorth_rev >= 0x11 && uninorth_rev <= 0x24) ||
+ uninorth_rev == 0xc0)
UN_OUT(0x2160, UN_IN(0x2160) & 0x00ffffff);
}
@@ -2736,12 +2764,14 @@ static void __init probe_one_macio(const char *name, const char *compat, int typ
node->full_name);
return;
}
- if (type == macio_keylargo) {
+ if (type == macio_keylargo || type == macio_keylargo2) {
u32 *did = (u32 *)get_property(node, "device-id", NULL);
if (*did == 0x00000025)
type = macio_pangea;
if (*did == 0x0000003e)
type = macio_intrepid;
+ if (*did == 0x0000004f)
+ type = macio_shasta;
}
macio_chips[i].of_node = node;
macio_chips[i].type = type;
@@ -2840,7 +2870,8 @@ set_initial_features(void)
}
#ifdef CONFIG_POWER4
- if (macio_chips[0].type == macio_keylargo2) {
+ if (macio_chips[0].type == macio_keylargo2 ||
+ macio_chips[0].type == macio_shasta) {
#ifndef CONFIG_SMP
/* On SMP machines running UP, we have the second CPU eating
* bus cycles. We need to take it off the bus. This is done
diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c
index 5aab261075d..f671ed25390 100644
--- a/arch/powerpc/platforms/powermac/pci.c
+++ b/arch/powerpc/platforms/powermac/pci.c
@@ -1,7 +1,7 @@
/*
* Support for PCI bridges found on Power Macintoshes.
*
- * Copyright (C) 2003 Benjamin Herrenschmuidt (benh@kernel.crashing.org)
+ * Copyright (C) 2003-2005 Benjamin Herrenschmuidt (benh@kernel.crashing.org)
* Copyright (C) 1997 Paul Mackerras (paulus@samba.org)
*
* This program is free software; you can redistribute it and/or
@@ -25,7 +25,7 @@
#include <asm/pmac_feature.h>
#include <asm/grackle.h>
#ifdef CONFIG_PPC64
-#include <asm/iommu.h>
+//#include <asm/iommu.h>
#include <asm/ppc-pci.h>
#endif
@@ -44,6 +44,7 @@ static int add_bridge(struct device_node *dev);
static int has_uninorth;
#ifdef CONFIG_PPC64
static struct pci_controller *u3_agp;
+static struct pci_controller *u4_pcie;
static struct pci_controller *u3_ht;
#endif /* CONFIG_PPC64 */
@@ -97,11 +98,8 @@ static void __init fixup_bus_range(struct device_node *bridge)
/* Lookup the "bus-range" property for the hose */
bus_range = (int *) get_property(bridge, "bus-range", &len);
- if (bus_range == NULL || len < 2 * sizeof(int)) {
- printk(KERN_WARNING "Can't get bus-range for %s\n",
- bridge->full_name);
+ if (bus_range == NULL || len < 2 * sizeof(int))
return;
- }
bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]);
}
@@ -128,14 +126,14 @@ static void __init fixup_bus_range(struct device_node *bridge)
*/
#define MACRISC_CFA0(devfn, off) \
- ((1 << (unsigned long)PCI_SLOT(dev_fn)) \
- | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \
- | (((unsigned long)(off)) & 0xFCUL))
+ ((1 << (unsigned int)PCI_SLOT(dev_fn)) \
+ | (((unsigned int)PCI_FUNC(dev_fn)) << 8) \
+ | (((unsigned int)(off)) & 0xFCUL))
#define MACRISC_CFA1(bus, devfn, off) \
- ((((unsigned long)(bus)) << 16) \
- |(((unsigned long)(devfn)) << 8) \
- |(((unsigned long)(off)) & 0xFCUL) \
+ ((((unsigned int)(bus)) << 16) \
+ |(((unsigned int)(devfn)) << 8) \
+ |(((unsigned int)(off)) & 0xFCUL) \
|1UL)
static unsigned long macrisc_cfg_access(struct pci_controller* hose,
@@ -168,7 +166,8 @@ static int macrisc_read_config(struct pci_bus *bus, unsigned int devfn,
hose = pci_bus_to_host(bus);
if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
-
+ if (offset >= 0x100)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
addr = macrisc_cfg_access(hose, bus->number, devfn, offset);
if (!addr)
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -199,7 +198,8 @@ static int macrisc_write_config(struct pci_bus *bus, unsigned int devfn,
hose = pci_bus_to_host(bus);
if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
-
+ if (offset >= 0x100)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
addr = macrisc_cfg_access(hose, bus->number, devfn, offset);
if (!addr)
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -234,12 +234,13 @@ static struct pci_ops macrisc_pci_ops =
/*
* Verify that a specific (bus, dev_fn) exists on chaos
*/
-static int
-chaos_validate_dev(struct pci_bus *bus, int devfn, int offset)
+static int chaos_validate_dev(struct pci_bus *bus, int devfn, int offset)
{
struct device_node *np;
u32 *vendor, *device;
+ if (offset >= 0x100)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
np = pci_busdev_to_OF_node(bus, devfn);
if (np == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -341,10 +342,10 @@ static int u3_ht_skip_device(struct pci_controller *hose,
}
#define U3_HT_CFA0(devfn, off) \
- ((((unsigned long)devfn) << 8) | offset)
+ ((((unsigned int)devfn) << 8) | offset)
#define U3_HT_CFA1(bus, devfn, off) \
(U3_HT_CFA0(devfn, off) \
- + (((unsigned long)bus) << 16) \
+ + (((unsigned int)bus) << 16) \
+ 0x01000000UL)
static unsigned long u3_ht_cfg_access(struct pci_controller* hose,
@@ -370,7 +371,8 @@ static int u3_ht_read_config(struct pci_bus *bus, unsigned int devfn,
hose = pci_bus_to_host(bus);
if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
-
+ if (offset >= 0x100)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
addr = u3_ht_cfg_access(hose, bus->number, devfn, offset);
if (!addr)
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -419,7 +421,8 @@ static int u3_ht_write_config(struct pci_bus *bus, unsigned int devfn,
hose = pci_bus_to_host(bus);
if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
-
+ if (offset >= 0x100)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
addr = u3_ht_cfg_access(hose, bus->number, devfn, offset);
if (!addr)
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -459,6 +462,112 @@ static struct pci_ops u3_ht_pci_ops =
u3_ht_read_config,
u3_ht_write_config
};
+
+#define U4_PCIE_CFA0(devfn, off) \
+ ((1 << ((unsigned int)PCI_SLOT(dev_fn))) \
+ | (((unsigned int)PCI_FUNC(dev_fn)) << 8) \
+ | ((((unsigned int)(off)) >> 8) << 28) \
+ | (((unsigned int)(off)) & 0xfcU))
+
+#define U4_PCIE_CFA1(bus, devfn, off) \
+ ((((unsigned int)(bus)) << 16) \
+ |(((unsigned int)(devfn)) << 8) \
+ | ((((unsigned int)(off)) >> 8) << 28) \
+ |(((unsigned int)(off)) & 0xfcU) \
+ |1UL)
+
+static unsigned long u4_pcie_cfg_access(struct pci_controller* hose,
+ u8 bus, u8 dev_fn, int offset)
+{
+ unsigned int caddr;
+
+ if (bus == hose->first_busno) {
+ caddr = U4_PCIE_CFA0(dev_fn, offset);
+ } else
+ caddr = U4_PCIE_CFA1(bus, dev_fn, offset);
+
+ /* Uninorth will return garbage if we don't read back the value ! */
+ do {
+ out_le32(hose->cfg_addr, caddr);
+ } while (in_le32(hose->cfg_addr) != caddr);
+
+ offset &= 0x03;
+ return ((unsigned long)hose->cfg_data) + offset;
+}
+
+static int u4_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 *val)
+{
+ struct pci_controller *hose;
+ unsigned long addr;
+
+ hose = pci_bus_to_host(bus);
+ if (hose == NULL)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ if (offset >= 0x1000)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ addr = u4_pcie_cfg_access(hose, bus->number, devfn, offset);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ /*
+ * Note: the caller has already checked that offset is
+ * suitably aligned and that len is 1, 2 or 4.
+ */
+ switch (len) {
+ case 1:
+ *val = in_8((u8 *)addr);
+ break;
+ case 2:
+ *val = in_le16((u16 *)addr);
+ break;
+ default:
+ *val = in_le32((u32 *)addr);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int u4_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 val)
+{
+ struct pci_controller *hose;
+ unsigned long addr;
+
+ hose = pci_bus_to_host(bus);
+ if (hose == NULL)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ if (offset >= 0x1000)
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ addr = u4_pcie_cfg_access(hose, bus->number, devfn, offset);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ /*
+ * Note: the caller has already checked that offset is
+ * suitably aligned and that len is 1, 2 or 4.
+ */
+ switch (len) {
+ case 1:
+ out_8((u8 *)addr, val);
+ (void) in_8((u8 *)addr);
+ break;
+ case 2:
+ out_le16((u16 *)addr, val);
+ (void) in_le16((u16 *)addr);
+ break;
+ default:
+ out_le32((u32 *)addr, val);
+ (void) in_le32((u32 *)addr);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops u4_pcie_pci_ops =
+{
+ u4_pcie_read_config,
+ u4_pcie_write_config
+};
+
#endif /* CONFIG_PPC64 */
#ifdef CONFIG_PPC32
@@ -628,15 +737,36 @@ static void __init setup_u3_agp(struct pci_controller* hose)
hose->ops = &macrisc_pci_ops;
hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000);
hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000);
-
u3_agp = hose;
}
+static void __init setup_u4_pcie(struct pci_controller* hose)
+{
+ /* We currently only implement the "non-atomic" config space, to
+ * be optimised later.
+ */
+ hose->ops = &u4_pcie_pci_ops;
+ hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000);
+ hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000);
+
+ /* The bus contains a bridge from root -> device, we need to
+ * make it visible on bus 0 so that we pick the right type
+ * of config cycles. If we didn't, we would have to force all
+ * config cycles to be type 1. So we override the "bus-range"
+ * property here
+ */
+ hose->first_busno = 0x00;
+ hose->last_busno = 0xff;
+ u4_pcie = hose;
+}
+
static void __init setup_u3_ht(struct pci_controller* hose)
{
struct device_node *np = (struct device_node *)hose->arch_data;
+ struct pci_controller *other = NULL;
int i, cur;
+
hose->ops = &u3_ht_pci_ops;
/* We hard code the address because of the different size of
@@ -670,11 +800,20 @@ static void __init setup_u3_ht(struct pci_controller* hose)
u3_ht = hose;
- if (u3_agp == NULL) {
- DBG("U3 has no AGP, using full resource range\n");
+ if (u3_agp != NULL)
+ other = u3_agp;
+ else if (u4_pcie != NULL)
+ other = u4_pcie;
+
+ if (other == NULL) {
+ DBG("U3/4 has no AGP/PCIE, using full resource range\n");
return;
}
+ /* Fixup bus range vs. PCIE */
+ if (u4_pcie)
+ hose->last_busno = u4_pcie->first_busno - 1;
+
/* We "remove" the AGP resources from the resources allocated to HT,
* that is we create "holes". However, that code does assumptions
* that so far happen to be true (cross fingers...), typically that
@@ -682,7 +821,7 @@ static void __init setup_u3_ht(struct pci_controller* hose)
*/
cur = 0;
for (i=0; i<3; i++) {
- struct resource *res = &u3_agp->mem_resources[i];
+ struct resource *res = &other->mem_resources[i];
if (res->flags != IORESOURCE_MEM)
continue;
/* We don't care about "fine" resources */
@@ -777,9 +916,13 @@ static int __init add_bridge(struct device_node *dev)
setup_u3_ht(hose);
disp_name = "U3-HT";
primary = 1;
+ } else if (device_is_compatible(dev, "u4-pcie")) {
+ setup_u4_pcie(hose);
+ disp_name = "U4-PCIE";
+ primary = 0;
}
- printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number: %d->%d\n",
- disp_name, hose->first_busno, hose->last_busno);
+ printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number:"
+ " %d->%d\n", disp_name, hose->first_busno, hose->last_busno);
#endif /* CONFIG_PPC64 */
/* 32 bits only bridges */
@@ -900,6 +1043,8 @@ void __init pmac_pci_init(void)
pci_setup_phb_io(u3_ht, 1);
if (u3_agp)
pci_setup_phb_io(u3_agp, 0);
+ if (u4_pcie)
+ pci_setup_phb_io(u4_pcie, 0);
/*
* On ppc64, fixup the IO resources on our host bridges as
@@ -912,7 +1057,8 @@ void __init pmac_pci_init(void)
/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We
* assume there is no P2P bridge on the AGP bus, which should be a
- * safe assumptions hopefully.
+ * safe assumptions for now. We should do something better in the
+ * future though
*/
if (u3_agp) {
struct device_node *np = u3_agp->arch_data;
@@ -920,7 +1066,6 @@ void __init pmac_pci_init(void)
for (np = np->child; np; np = np->sibling)
PCI_DN(np)->busno = 0xf0;
}
-
/* pmac_check_ht_link(); */
/* Tell pci.c to not use the common resource allocation mechanism */
@@ -1127,7 +1272,8 @@ void pmac_pci_fixup_pciata(struct pci_dev* dev)
good:
pci_read_config_byte(dev, PCI_CLASS_PROG, &progif);
if ((progif & 5) != 5) {
- printk(KERN_INFO "Forcing PCI IDE into native mode: %s\n", pci_name(dev));
+ printk(KERN_INFO "Forcing PCI IDE into native mode: %s\n",
+ pci_name(dev));
(void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5);
if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) ||
(progif & 5) != 5)
@@ -1153,7 +1299,8 @@ static void fixup_k2_sata(struct pci_dev* dev)
for (i = 0; i < 6; i++) {
dev->resource[i].start = dev->resource[i].end = 0;
dev->resource[i].flags = 0;
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i,
+ 0);
}
} else {
pci_read_config_word(dev, PCI_COMMAND, &cmd);
@@ -1162,7 +1309,8 @@ static void fixup_k2_sata(struct pci_dev* dev)
for (i = 0; i < 5; i++) {
dev->resource[i].start = dev->resource[i].end = 0;
dev->resource[i].flags = 0;
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i,
+ 0);
}
}
}
diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c
index dbb524a851a..18bf3011d1e 100644
--- a/arch/powerpc/platforms/powermac/pic.c
+++ b/arch/powerpc/platforms/powermac/pic.c
@@ -524,18 +524,56 @@ static void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic)
#endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */
}
+static struct mpic * __init pmac_setup_one_mpic(struct device_node *np,
+ int master)
+{
+ unsigned char senses[128];
+ int offset = master ? 0 : 128;
+ int count = master ? 128 : 124;
+ const char *name = master ? " MPIC 1 " : " MPIC 2 ";
+ struct resource r;
+ struct mpic *mpic;
+ unsigned int flags = master ? MPIC_PRIMARY : 0;
+ int rc;
+
+ rc = of_address_to_resource(np, 0, &r);
+ if (rc)
+ return NULL;
+
+ pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0);
+
+ prom_get_irq_senses(senses, offset, offset + count);
+
+ flags |= MPIC_WANTS_RESET;
+ if (get_property(np, "big-endian", NULL))
+ flags |= MPIC_BIG_ENDIAN;
+
+ /* Primary Big Endian means HT interrupts. This is quite dodgy
+ * but works until I find a better way
+ */
+ if (master && (flags & MPIC_BIG_ENDIAN))
+ flags |= MPIC_BROKEN_U3;
+
+ mpic = mpic_alloc(r.start, flags, 0, offset, count, master ? 252 : 0,
+ senses, count, name);
+ if (mpic == NULL)
+ return NULL;
+
+ mpic_init(mpic);
+
+ return mpic;
+ }
+
static int __init pmac_pic_probe_mpic(void)
{
struct mpic *mpic1, *mpic2;
struct device_node *np, *master = NULL, *slave = NULL;
- unsigned char senses[128];
- struct resource r;
/* We can have up to 2 MPICs cascaded */
for (np = NULL; (np = of_find_node_by_type(np, "open-pic"))
!= NULL;) {
if (master == NULL &&
- get_property(np, "interrupt-parent", NULL) != NULL)
+ get_property(np, "interrupts", NULL) == NULL)
master = of_node_get(np);
else if (slave == NULL)
slave = of_node_get(np);
@@ -557,13 +595,8 @@ static int __init pmac_pic_probe_mpic(void)
ppc_md.get_irq = mpic_get_irq;
/* Setup master */
- BUG_ON(of_address_to_resource(master, 0, &r));
- pmac_call_feature(PMAC_FTR_ENABLE_MPIC, master, 0, 0);
- prom_get_irq_senses(senses, 0, 128);
- mpic1 = mpic_alloc(r.start, MPIC_PRIMARY | MPIC_WANTS_RESET,
- 0, 0, 128, 252, senses, 128, " OpenPIC ");
+ mpic1 = pmac_setup_one_mpic(master, 1);
BUG_ON(mpic1 == NULL);
- mpic_init(mpic1);
/* Install NMI if any */
pmac_pic_setup_mpic_nmi(mpic1);
@@ -574,27 +607,12 @@ static int __init pmac_pic_probe_mpic(void)
if (slave == NULL || slave->n_intrs < 1)
return 0;
- /* Setup slave, failures are non-fatal */
- if (of_address_to_resource(slave, 0, &r)) {
- printk(KERN_ERR "Can't get address of MPIC %s\n",
- slave->full_name);
- return 0;
- }
- pmac_call_feature(PMAC_FTR_ENABLE_MPIC, slave, 0, 0);
- prom_get_irq_senses(senses, 128, 128 + 124);
-
- /* We don't need to set MPIC_BROKEN_U3 here since we don't have
- * hypertransport interrupts routed to it, at least not on currently
- * supported machines, that may change.
- */
- mpic2 = mpic_alloc(r.start, MPIC_BIG_ENDIAN | MPIC_WANTS_RESET,
- 0, 128, 124, 0, senses, 124, " U3-MPIC ");
+ mpic2 = pmac_setup_one_mpic(slave, 0);
if (mpic2 == NULL) {
- printk(KERN_ERR "Can't create slave MPIC %s\n",
- slave->full_name);
+ printk(KERN_ERR "Failed to setup slave MPIC\n");
+ of_node_put(slave);
return 0;
}
- mpic_init(mpic2);
mpic_setup_cascade(slave->intrs[0].line, pmac_u3_cascade, mpic2);
of_node_put(slave);
diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c
index 18c5620f87f..1daa5a06e9e 100644
--- a/arch/powerpc/platforms/powermac/setup.c
+++ b/arch/powerpc/platforms/powermac/setup.c
@@ -345,7 +345,7 @@ void __init pmac_setup_arch(void)
#ifdef CONFIG_SMP
/* Check for Core99 */
- if (find_devices("uni-n") || find_devices("u3"))
+ if (find_devices("uni-n") || find_devices("u3") || find_devices("u4"))
smp_ops = &core99_smp_ops;
#ifdef CONFIG_PPC32
else
@@ -635,7 +635,7 @@ static void __init pmac_init_early(void)
/* Setup interrupt mapping options */
ppc64_interrupt_controller = IC_OPEN_PIC;
- iommu_init_early_u3();
+ iommu_init_early_dart();
#endif
}
@@ -711,7 +711,7 @@ static int __init pmac_probe(int platform)
* occupies having to be broken up so the DART itself is not
* part of the cacheable linar mapping
*/
- alloc_u3_dart_table();
+ alloc_dart_table();
#endif
#ifdef CONFIG_PMAC_SMU
@@ -733,10 +733,11 @@ static int pmac_pci_probe_mode(struct pci_bus *bus)
struct device_node *node = bus->sysdata;
/* We need to use normal PCI probing for the AGP bus,
- since the device for the AGP bridge isn't in the tree. */
- if (bus->self == NULL && device_is_compatible(node, "u3-agp"))
+ * since the device for the AGP bridge isn't in the tree.
+ */
+ if (bus->self == NULL && (device_is_compatible(node, "u3-agp") ||
+ device_is_compatible(node, "u4-pcie")))
return PCI_PROBE_NORMAL;
-
return PCI_PROBE_DEVTREE;
}
#endif
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c
index 862f1e985c1..df01bb8feb1 100644
--- a/arch/powerpc/platforms/powermac/smp.c
+++ b/arch/powerpc/platforms/powermac/smp.c
@@ -361,7 +361,6 @@ static void __init psurge_dual_sync_tb(int cpu_nr)
set_dec(tb_ticks_per_jiffy);
/* XXX fixme */
set_tb(0, 0);
- last_jiffy_stamp(cpu_nr) = 0;
if (cpu_nr > 0) {
mb();
@@ -429,15 +428,62 @@ struct smp_ops_t psurge_smp_ops = {
};
#endif /* CONFIG_PPC32 - actually powersurge support */
+/*
+ * Core 99 and later support
+ */
+
+static void (*pmac_tb_freeze)(int freeze);
+static unsigned long timebase;
+static int tb_req;
+
+static void smp_core99_give_timebase(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ while(!tb_req)
+ barrier();
+ tb_req = 0;
+ (*pmac_tb_freeze)(1);
+ mb();
+ timebase = get_tb();
+ mb();
+ while (timebase)
+ barrier();
+ mb();
+ (*pmac_tb_freeze)(0);
+ mb();
+
+ local_irq_restore(flags);
+}
+
+
+static void __devinit smp_core99_take_timebase(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ tb_req = 1;
+ mb();
+ while (!timebase)
+ barrier();
+ mb();
+ set_tb(timebase >> 32, timebase & 0xffffffff);
+ timebase = 0;
+ mb();
+ set_dec(tb_ticks_per_jiffy/2);
+
+ local_irq_restore(flags);
+}
+
#ifdef CONFIG_PPC64
/*
* G5s enable/disable the timebase via an i2c-connected clock chip.
*/
static struct device_node *pmac_tb_clock_chip_host;
static u8 pmac_tb_pulsar_addr;
-static void (*pmac_tb_freeze)(int freeze);
-static DEFINE_SPINLOCK(timebase_lock);
-static unsigned long timebase;
static void smp_core99_cypress_tb_freeze(int freeze)
{
@@ -447,7 +493,8 @@ static void smp_core99_cypress_tb_freeze(int freeze)
/* Strangely, the device-tree says address is 0xd2, but darwin
* accesses 0xd0 ...
*/
- pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_combined);
+ pmac_low_i2c_setmode(pmac_tb_clock_chip_host,
+ pmac_low_i2c_mode_combined);
rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host,
0xd0 | pmac_low_i2c_read,
0x81, &data, 1);
@@ -475,7 +522,8 @@ static void smp_core99_pulsar_tb_freeze(int freeze)
u8 data;
int rc;
- pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_combined);
+ pmac_low_i2c_setmode(pmac_tb_clock_chip_host,
+ pmac_low_i2c_mode_combined);
rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host,
pmac_tb_pulsar_addr | pmac_low_i2c_read,
0x2e, &data, 1);
@@ -496,54 +544,14 @@ static void smp_core99_pulsar_tb_freeze(int freeze)
}
}
-
-static void smp_core99_give_timebase(void)
-{
- /* Open i2c bus for synchronous access */
- if (pmac_low_i2c_open(pmac_tb_clock_chip_host, 0))
- panic("Can't open i2c for TB sync !\n");
-
- spin_lock(&timebase_lock);
- (*pmac_tb_freeze)(1);
- mb();
- timebase = get_tb();
- spin_unlock(&timebase_lock);
-
- while (timebase)
- barrier();
-
- spin_lock(&timebase_lock);
- (*pmac_tb_freeze)(0);
- spin_unlock(&timebase_lock);
-
- /* Close i2c bus */
- pmac_low_i2c_close(pmac_tb_clock_chip_host);
-}
-
-
-static void __devinit smp_core99_take_timebase(void)
-{
- while (!timebase)
- barrier();
- spin_lock(&timebase_lock);
- set_tb(timebase >> 32, timebase & 0xffffffff);
- timebase = 0;
- spin_unlock(&timebase_lock);
-}
-
-static void __init smp_core99_setup(int ncpus)
+static void __init smp_core99_setup_i2c_hwsync(int ncpus)
{
struct device_node *cc = NULL;
struct device_node *p;
+ const char *name = NULL;
u32 *reg;
int ok;
- /* HW sync only on these platforms */
- if (!machine_is_compatible("PowerMac7,2") &&
- !machine_is_compatible("PowerMac7,3") &&
- !machine_is_compatible("RackMac3,1"))
- return;
-
/* Look for the clock chip */
while ((cc = of_find_node_by_name(cc, "i2c-hwclock")) != NULL) {
p = of_get_parent(cc);
@@ -561,114 +569,64 @@ static void __init smp_core99_setup(int ncpus)
if (device_is_compatible(cc, "pulsar-legacy-slewing")) {
pmac_tb_freeze = smp_core99_pulsar_tb_freeze;
pmac_tb_pulsar_addr = 0xd2;
- printk(KERN_INFO "Timebase clock is Pulsar chip\n");
+ name = "Pulsar";
} else if (device_is_compatible(cc, "cy28508")) {
pmac_tb_freeze = smp_core99_cypress_tb_freeze;
- printk(KERN_INFO "Timebase clock is Cypress chip\n");
+ name = "Cypress";
}
break;
case 0xd4:
pmac_tb_freeze = smp_core99_pulsar_tb_freeze;
pmac_tb_pulsar_addr = 0xd4;
- printk(KERN_INFO "Timebase clock is Pulsar chip\n");
+ name = "Pulsar";
break;
}
- if (pmac_tb_freeze != NULL) {
- pmac_tb_clock_chip_host = of_get_parent(cc);
- of_node_put(cc);
+ if (pmac_tb_freeze != NULL)
break;
- }
}
- if (pmac_tb_freeze == NULL) {
- smp_ops->give_timebase = smp_generic_give_timebase;
- smp_ops->take_timebase = smp_generic_take_timebase;
+ if (pmac_tb_freeze != NULL) {
+ struct device_node *p = of_get_parent(cc);
+ of_node_put(cc);
+ while(p && strcmp(p->type, "i2c")) {
+ cc = of_get_parent(p);
+ of_node_put(p);
+ p = cc;
+ }
+ if (p == NULL)
+ goto no_i2c_sync;
+ /* Open i2c bus for synchronous access */
+ if (pmac_low_i2c_open(p, 0)) {
+ printk(KERN_ERR "Failed top open i2c bus %s for clock"
+ " sync, fallback to software sync !\n",
+ p->full_name);
+ of_node_put(p);
+ goto no_i2c_sync;
+ }
+ pmac_tb_clock_chip_host = p;
+ printk(KERN_INFO "Processor timebase sync using %s i2c clock\n",
+ name);
+ return;
}
+ no_i2c_sync:
+ pmac_tb_freeze = NULL;
}
-/* nothing to do here, caches are already set up by service processor */
-static inline void __devinit core99_init_caches(int cpu)
-{
-}
+#endif /* CONFIG_PPC64 */
-#else /* CONFIG_PPC64 */
/*
- * SMP G4 powermacs use a GPIO to enable/disable the timebase.
+ * SMP G4 and newer G5 use a GPIO to enable/disable the timebase.
*/
static unsigned int core99_tb_gpio; /* Timebase freeze GPIO */
-static unsigned int pri_tb_hi, pri_tb_lo;
-static unsigned int pri_tb_stamp;
-
-/* not __init, called in sleep/wakeup code */
-void smp_core99_give_timebase(void)
+static void smp_core99_gpio_tb_freeze(int freeze)
{
- unsigned long flags;
- unsigned int t;
-
- /* wait for the secondary to be in take_timebase */
- for (t = 100000; t > 0 && !sec_tb_reset; --t)
- udelay(10);
- if (!sec_tb_reset) {
- printk(KERN_WARNING "Timeout waiting sync on second CPU\n");
- return;
- }
-
- /* freeze the timebase and read it */
- /* disable interrupts so the timebase is disabled for the
- shortest possible time */
- local_irq_save(flags);
- pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 4);
+ if (freeze)
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 4);
+ else
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 0);
pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0);
- mb();
- pri_tb_hi = get_tbu();
- pri_tb_lo = get_tbl();
- pri_tb_stamp = last_jiffy_stamp(smp_processor_id());
- mb();
-
- /* tell the secondary we're ready */
- sec_tb_reset = 2;
- mb();
-
- /* wait for the secondary to have taken it */
- /* note: can't use udelay here, since it needs the timebase running */
- for (t = 10000000; t > 0 && sec_tb_reset; --t)
- barrier();
- if (sec_tb_reset)
- /* XXX BUG_ON here? */
- printk(KERN_WARNING "Timeout waiting sync(2) on second CPU\n");
-
- /* Now, restart the timebase by leaving the GPIO to an open collector */
- pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 0);
- pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0);
- local_irq_restore(flags);
-}
-
-/* not __init, called in sleep/wakeup code */
-void smp_core99_take_timebase(void)
-{
- unsigned long flags;
-
- /* tell the primary we're here */
- sec_tb_reset = 1;
- mb();
-
- /* wait for the primary to set pri_tb_hi/lo */
- while (sec_tb_reset < 2)
- mb();
-
- /* set our stuff the same as the primary */
- local_irq_save(flags);
- set_dec(1);
- set_tb(pri_tb_hi, pri_tb_lo);
- last_jiffy_stamp(smp_processor_id()) = pri_tb_stamp;
- mb();
-
- /* tell the primary we're done */
- sec_tb_reset = 0;
- mb();
- local_irq_restore(flags);
}
/* L2 and L3 cache settings to pass from CPU0 to CPU1 on G4 cpus */
@@ -677,6 +635,7 @@ volatile static long int core99_l3_cache;
static void __devinit core99_init_caches(int cpu)
{
+#ifndef CONFIG_PPC64
if (!cpu_has_feature(CPU_FTR_L2CR))
return;
@@ -702,30 +661,80 @@ static void __devinit core99_init_caches(int cpu)
_set_L3CR(core99_l3_cache);
printk("CPU%d: L3CR set to %lx\n", cpu, core99_l3_cache);
}
+#endif /* !CONFIG_PPC64 */
}
static void __init smp_core99_setup(int ncpus)
{
- struct device_node *cpu;
- u32 *tbprop = NULL;
- int i;
+#ifdef CONFIG_PPC64
- core99_tb_gpio = KL_GPIO_TB_ENABLE; /* default value */
- cpu = of_find_node_by_type(NULL, "cpu");
- if (cpu != NULL) {
- tbprop = (u32 *)get_property(cpu, "timebase-enable", NULL);
- if (tbprop)
- core99_tb_gpio = *tbprop;
- of_node_put(cpu);
+ /* i2c based HW sync on some G5s */
+ if (machine_is_compatible("PowerMac7,2") ||
+ machine_is_compatible("PowerMac7,3") ||
+ machine_is_compatible("RackMac3,1"))
+ smp_core99_setup_i2c_hwsync(ncpus);
+
+ /* GPIO based HW sync on recent G5s */
+ if (pmac_tb_freeze == NULL) {
+ struct device_node *np =
+ of_find_node_by_name(NULL, "timebase-enable");
+ u32 *reg = (u32 *)get_property(np, "reg", NULL);
+
+ if (np && reg && !strcmp(np->type, "gpio")) {
+ core99_tb_gpio = *reg;
+ if (core99_tb_gpio < 0x50)
+ core99_tb_gpio += 0x50;
+ pmac_tb_freeze = smp_core99_gpio_tb_freeze;
+ printk(KERN_INFO "Processor timebase sync using"
+ " GPIO 0x%02x\n", core99_tb_gpio);
+ }
}
- /* XXX should get this from reg properties */
- for (i = 1; i < ncpus; ++i)
- smp_hw_index[i] = i;
- powersave_nap = 0;
-}
+#else /* CONFIG_PPC64 */
+
+ /* GPIO based HW sync on ppc32 Core99 */
+ if (pmac_tb_freeze == NULL && !machine_is_compatible("MacRISC4")) {
+ struct device_node *cpu;
+ u32 *tbprop = NULL;
+
+ core99_tb_gpio = KL_GPIO_TB_ENABLE; /* default value */
+ cpu = of_find_node_by_type(NULL, "cpu");
+ if (cpu != NULL) {
+ tbprop = (u32 *)get_property(cpu, "timebase-enable",
+ NULL);
+ if (tbprop)
+ core99_tb_gpio = *tbprop;
+ of_node_put(cpu);
+ }
+ pmac_tb_freeze = smp_core99_gpio_tb_freeze;
+ printk(KERN_INFO "Processor timebase sync using"
+ " GPIO 0x%02x\n", core99_tb_gpio);
+ }
+
+#endif /* CONFIG_PPC64 */
+
+ /* No timebase sync, fallback to software */
+ if (pmac_tb_freeze == NULL) {
+ smp_ops->give_timebase = smp_generic_give_timebase;
+ smp_ops->take_timebase = smp_generic_take_timebase;
+ printk(KERN_INFO "Processor timebase sync using software\n");
+ }
+
+#ifndef CONFIG_PPC64
+ {
+ int i;
+
+ /* XXX should get this from reg properties */
+ for (i = 1; i < ncpus; ++i)
+ smp_hw_index[i] = i;
+ }
#endif
+ /* 32 bits SMP can't NAP */
+ if (!machine_is_compatible("MacRISC4"))
+ powersave_nap = 0;
+}
+
static int __init smp_core99_probe(void)
{
struct device_node *cpus;
@@ -803,17 +812,25 @@ static void __devinit smp_core99_setup_cpu(int cpu_nr)
mpic_setup_this_cpu();
if (cpu_nr == 0) {
-#ifdef CONFIG_POWER4
+#ifdef CONFIG_PPC64
extern void g5_phy_disable_cpu1(void);
+ /* Close i2c bus if it was used for tb sync */
+ if (pmac_tb_clock_chip_host) {
+ pmac_low_i2c_close(pmac_tb_clock_chip_host);
+ pmac_tb_clock_chip_host = NULL;
+ }
+
/* If we didn't start the second CPU, we must take
* it off the bus
*/
if (machine_is_compatible("MacRISC4") &&
num_online_cpus() < 2)
g5_phy_disable_cpu1();
-#endif /* CONFIG_POWER4 */
- if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349);
+#endif /* CONFIG_PPC64 */
+
+ if (ppc_md.progress)
+ ppc_md.progress("core99_setup_cpu 0 done", 0x349);
}
}