summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2011-05-23 11:35:33 +0900
committerPaul Mundt <lethal@linux-sh.org>2011-05-23 11:35:33 +0900
commit8ace5c4698ec8da53e69095596718d5a936433de (patch)
tree1f5959b3ac289e3bde420cc14109be0cc518a75a /arch/powerpc/platforms
parenta375b15164dd9264f724ad941825e52c90145151 (diff)
parent71a8638480eb8fb6cfabe2ee9ca3fbc6e3453a14 (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into sh-latest
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r--arch/powerpc/platforms/44x/iss4xx.c6
-rw-r--r--arch/powerpc/platforms/512x/mpc5121_ads_cpld.c10
-rw-r--r--arch/powerpc/platforms/52xx/media5200.c4
-rw-r--r--arch/powerpc/platforms/52xx/mpc52xx_pic.c83
-rw-r--r--arch/powerpc/platforms/82xx/pq2ads-pci-pic.c12
-rw-r--r--arch/powerpc/platforms/83xx/suspend.c7
-rw-r--r--arch/powerpc/platforms/85xx/smp.c12
-rw-r--r--arch/powerpc/platforms/85xx/socrates_fpga_pic.c26
-rw-r--r--arch/powerpc/platforms/86xx/gef_pic.c10
-rw-r--r--arch/powerpc/platforms/86xx/mpc8610_hpcd.c99
-rw-r--r--arch/powerpc/platforms/86xx/mpc86xx_smp.c6
-rw-r--r--arch/powerpc/platforms/8xx/m8xx_setup.c2
-rw-r--r--arch/powerpc/platforms/Kconfig31
-rw-r--r--arch/powerpc/platforms/Kconfig.cputype24
-rw-r--r--arch/powerpc/platforms/Makefile1
-rw-r--r--arch/powerpc/platforms/cell/Kconfig4
-rw-r--r--arch/powerpc/platforms/cell/Makefile9
-rw-r--r--arch/powerpc/platforms/cell/axon_msi.c3
-rw-r--r--arch/powerpc/platforms/cell/beat_interrupt.c27
-rw-r--r--arch/powerpc/platforms/cell/beat_interrupt.h3
-rw-r--r--arch/powerpc/platforms/cell/beat_smp.c124
-rw-r--r--arch/powerpc/platforms/cell/cbe_regs.c11
-rw-r--r--arch/powerpc/platforms/cell/celleb_pci.c25
-rw-r--r--arch/powerpc/platforms/cell/celleb_pci.h3
-rw-r--r--arch/powerpc/platforms/cell/celleb_setup.c4
-rw-r--r--arch/powerpc/platforms/cell/interrupt.c16
-rw-r--r--arch/powerpc/platforms/cell/io-workarounds.c185
-rw-r--r--arch/powerpc/platforms/cell/io-workarounds.h49
-rw-r--r--arch/powerpc/platforms/cell/qpace_setup.c1
-rw-r--r--arch/powerpc/platforms/cell/setup.c4
-rw-r--r--arch/powerpc/platforms/cell/smp.c37
-rw-r--r--arch/powerpc/platforms/cell/spider-pci.c3
-rw-r--r--arch/powerpc/platforms/cell/spider-pic.c21
-rw-r--r--arch/powerpc/platforms/cell/spu_base.c28
-rw-r--r--arch/powerpc/platforms/cell/spufs/sched.c2
-rw-r--r--arch/powerpc/platforms/chrp/smp.c4
-rw-r--r--arch/powerpc/platforms/embedded6xx/flipper-pic.c15
-rw-r--r--arch/powerpc/platforms/embedded6xx/hlwd-pic.c15
-rw-r--r--arch/powerpc/platforms/iseries/Kconfig4
-rw-r--r--arch/powerpc/platforms/iseries/exception.S62
-rw-r--r--arch/powerpc/platforms/iseries/irq.c13
-rw-r--r--arch/powerpc/platforms/iseries/setup.c9
-rw-r--r--arch/powerpc/platforms/iseries/smp.c45
-rw-r--r--arch/powerpc/platforms/iseries/smp.h6
-rw-r--r--arch/powerpc/platforms/powermac/Kconfig11
-rw-r--r--arch/powerpc/platforms/powermac/pic.c67
-rw-r--r--arch/powerpc/platforms/powermac/pic.h11
-rw-r--r--arch/powerpc/platforms/powermac/pmac.h1
-rw-r--r--arch/powerpc/platforms/powermac/smp.c105
-rw-r--r--arch/powerpc/platforms/ps3/interrupt.c8
-rw-r--r--arch/powerpc/platforms/ps3/smp.c22
-rw-r--r--arch/powerpc/platforms/ps3/spu.c4
-rw-r--r--arch/powerpc/platforms/pseries/Kconfig23
-rw-r--r--arch/powerpc/platforms/pseries/Makefile2
-rw-r--r--arch/powerpc/platforms/pseries/dtl.c20
-rw-r--r--arch/powerpc/platforms/pseries/eeh.c82
-rw-r--r--arch/powerpc/platforms/pseries/eeh_driver.c22
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-cpu.c5
-rw-r--r--arch/powerpc/platforms/pseries/io_event_irq.c231
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c117
-rw-r--r--arch/powerpc/platforms/pseries/kexec.c5
-rw-r--r--arch/powerpc/platforms/pseries/lpar.c48
-rw-r--r--arch/powerpc/platforms/pseries/plpar_wrappers.h27
-rw-r--r--arch/powerpc/platforms/pseries/ras.c6
-rw-r--r--arch/powerpc/platforms/pseries/setup.c48
-rw-r--r--arch/powerpc/platforms/pseries/smp.c24
-rw-r--r--arch/powerpc/platforms/pseries/xics.c949
-rw-r--r--arch/powerpc/platforms/pseries/xics.h23
-rw-r--r--arch/powerpc/platforms/wsp/Kconfig28
-rw-r--r--arch/powerpc/platforms/wsp/Makefile6
-rw-r--r--arch/powerpc/platforms/wsp/ics.c712
-rw-r--r--arch/powerpc/platforms/wsp/ics.h20
-rw-r--r--arch/powerpc/platforms/wsp/opb_pic.c332
-rw-r--r--arch/powerpc/platforms/wsp/psr2.c95
-rw-r--r--arch/powerpc/platforms/wsp/scom_smp.c427
-rw-r--r--arch/powerpc/platforms/wsp/scom_wsp.c77
-rw-r--r--arch/powerpc/platforms/wsp/setup.c36
-rw-r--r--arch/powerpc/platforms/wsp/smp.c88
-rw-r--r--arch/powerpc/platforms/wsp/wsp.h17
79 files changed, 2780 insertions, 1984 deletions
diff --git a/arch/powerpc/platforms/44x/iss4xx.c b/arch/powerpc/platforms/44x/iss4xx.c
index aa46e9d1e77..19395f18b1d 100644
--- a/arch/powerpc/platforms/44x/iss4xx.c
+++ b/arch/powerpc/platforms/44x/iss4xx.c
@@ -87,7 +87,7 @@ static void __cpuinit smp_iss4xx_setup_cpu(int cpu)
mpic_setup_this_cpu();
}
-static void __cpuinit smp_iss4xx_kick_cpu(int cpu)
+static int __cpuinit smp_iss4xx_kick_cpu(int cpu)
{
struct device_node *cpunode = of_get_cpu_node(cpu, NULL);
const u64 *spin_table_addr_prop;
@@ -104,7 +104,7 @@ static void __cpuinit smp_iss4xx_kick_cpu(int cpu)
NULL);
if (spin_table_addr_prop == NULL) {
pr_err("CPU%d: Can't start, missing cpu-release-addr !\n", cpu);
- return;
+ return -ENOENT;
}
/* Assume it's mapped as part of the linear mapping. This is a bit
@@ -117,6 +117,8 @@ static void __cpuinit smp_iss4xx_kick_cpu(int cpu)
smp_wmb();
spin_table[1] = __pa(start_secondary_47x);
mb();
+
+ return 0;
}
static struct smp_ops_t iss_smp_ops = {
diff --git a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c
index cfc4b200998..9f09319352c 100644
--- a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c
+++ b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c
@@ -61,7 +61,7 @@ irq_to_pic_bit(unsigned int irq)
static void
cpld_mask_irq(struct irq_data *d)
{
- unsigned int cpld_irq = (unsigned int)irq_map[d->irq].hwirq;
+ unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d);
void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
out_8(pic_mask,
@@ -71,7 +71,7 @@ cpld_mask_irq(struct irq_data *d)
static void
cpld_unmask_irq(struct irq_data *d)
{
- unsigned int cpld_irq = (unsigned int)irq_map[d->irq].hwirq;
+ unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d);
void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
out_8(pic_mask,
@@ -97,7 +97,7 @@ cpld_pic_get_irq(int offset, u8 ignore, u8 __iomem *statusp,
status |= (ignore | mask);
if (status == 0xff)
- return NO_IRQ_IGNORE;
+ return NO_IRQ;
cpld_irq = ffz(status) + offset;
@@ -109,14 +109,14 @@ cpld_pic_cascade(unsigned int irq, struct irq_desc *desc)
{
irq = cpld_pic_get_irq(0, PCI_IGNORE, &cpld_regs->pci_status,
&cpld_regs->pci_mask);
- if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) {
+ if (irq != NO_IRQ) {
generic_handle_irq(irq);
return;
}
irq = cpld_pic_get_irq(8, MISC_IGNORE, &cpld_regs->misc_status,
&cpld_regs->misc_mask);
- if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) {
+ if (irq != NO_IRQ) {
generic_handle_irq(irq);
return;
}
diff --git a/arch/powerpc/platforms/52xx/media5200.c b/arch/powerpc/platforms/52xx/media5200.c
index 57a6a349e93..96f85e5e0cd 100644
--- a/arch/powerpc/platforms/52xx/media5200.c
+++ b/arch/powerpc/platforms/52xx/media5200.c
@@ -56,7 +56,7 @@ static void media5200_irq_unmask(struct irq_data *d)
spin_lock_irqsave(&media5200_irq.lock, flags);
val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE);
- val |= 1 << (MEDIA5200_IRQ_SHIFT + irq_map[d->irq].hwirq);
+ val |= 1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d));
out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val);
spin_unlock_irqrestore(&media5200_irq.lock, flags);
}
@@ -68,7 +68,7 @@ static void media5200_irq_mask(struct irq_data *d)
spin_lock_irqsave(&media5200_irq.lock, flags);
val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE);
- val &= ~(1 << (MEDIA5200_IRQ_SHIFT + irq_map[d->irq].hwirq));
+ val &= ~(1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d)));
out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val);
spin_unlock_irqrestore(&media5200_irq.lock, flags);
}
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c
index 1dd15400f6f..1a9a4957057 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_pic.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c
@@ -157,48 +157,30 @@ static inline void io_be_clrbit(u32 __iomem *addr, int bitno)
*/
static void mpc52xx_extirq_mask(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_clrbit(&intr->ctrl, 11 - l2irq);
}
static void mpc52xx_extirq_unmask(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_setbit(&intr->ctrl, 11 - l2irq);
}
static void mpc52xx_extirq_ack(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_setbit(&intr->ctrl, 27-l2irq);
}
static int mpc52xx_extirq_set_type(struct irq_data *d, unsigned int flow_type)
{
u32 ctrl_reg, type;
- int irq;
- int l2irq;
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
void *handler = handle_level_irq;
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
- pr_debug("%s: irq=%x. l2=%d flow_type=%d\n", __func__, irq, l2irq, flow_type);
+ pr_debug("%s: irq=%x. l2=%d flow_type=%d\n", __func__,
+ (int) irqd_to_hwirq(d), l2irq, flow_type);
switch (flow_type) {
case IRQF_TRIGGER_HIGH: type = 0; break;
@@ -237,23 +219,13 @@ static int mpc52xx_null_set_type(struct irq_data *d, unsigned int flow_type)
static void mpc52xx_main_mask(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_setbit(&intr->main_mask, 16 - l2irq);
}
static void mpc52xx_main_unmask(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_clrbit(&intr->main_mask, 16 - l2irq);
}
@@ -270,23 +242,13 @@ static struct irq_chip mpc52xx_main_irqchip = {
*/
static void mpc52xx_periph_mask(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_setbit(&intr->per_mask, 31 - l2irq);
}
static void mpc52xx_periph_unmask(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_clrbit(&intr->per_mask, 31 - l2irq);
}
@@ -303,34 +265,19 @@ static struct irq_chip mpc52xx_periph_irqchip = {
*/
static void mpc52xx_sdma_mask(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_setbit(&sdma->IntMask, l2irq);
}
static void mpc52xx_sdma_unmask(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
io_be_clrbit(&sdma->IntMask, l2irq);
}
static void mpc52xx_sdma_ack(struct irq_data *d)
{
- int irq;
- int l2irq;
-
- irq = irq_map[d->irq].hwirq;
- l2irq = irq & MPC52xx_IRQ_L2_MASK;
-
+ int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK;
out_be32(&sdma->IntPend, 1 << l2irq);
}
@@ -539,7 +486,7 @@ void __init mpc52xx_init_irq(void)
unsigned int mpc52xx_get_irq(void)
{
u32 status;
- int irq = NO_IRQ_IGNORE;
+ int irq;
status = in_be32(&intr->enc_status);
if (status & 0x00000400) { /* critical */
@@ -562,6 +509,8 @@ unsigned int mpc52xx_get_irq(void)
} else {
irq |= (MPC52xx_IRQ_L1_PERP << MPC52xx_IRQ_L1_OFFSET);
}
+ } else {
+ return NO_IRQ;
}
return irq_linear_revmap(mpc52xx_irqhost, irq);
diff --git a/arch/powerpc/platforms/82xx/pq2ads-pci-pic.c b/arch/powerpc/platforms/82xx/pq2ads-pci-pic.c
index 4a4eb6ffa12..8ccf9ed62fe 100644
--- a/arch/powerpc/platforms/82xx/pq2ads-pci-pic.c
+++ b/arch/powerpc/platforms/82xx/pq2ads-pci-pic.c
@@ -42,7 +42,7 @@ struct pq2ads_pci_pic {
static void pq2ads_pci_mask_irq(struct irq_data *d)
{
struct pq2ads_pci_pic *priv = irq_data_get_irq_chip_data(d);
- int irq = NUM_IRQS - virq_to_hw(d->irq) - 1;
+ int irq = NUM_IRQS - irqd_to_hwirq(d) - 1;
if (irq != -1) {
unsigned long flags;
@@ -58,7 +58,7 @@ static void pq2ads_pci_mask_irq(struct irq_data *d)
static void pq2ads_pci_unmask_irq(struct irq_data *d)
{
struct pq2ads_pci_pic *priv = irq_data_get_irq_chip_data(d);
- int irq = NUM_IRQS - virq_to_hw(d->irq) - 1;
+ int irq = NUM_IRQS - irqd_to_hwirq(d) - 1;
if (irq != -1) {
unsigned long flags;
@@ -112,16 +112,8 @@ static int pci_pic_host_map(struct irq_host *h, unsigned int virq,
return 0;
}
-static void pci_host_unmap(struct irq_host *h, unsigned int virq)
-{
- /* remove chip and handler */
- irq_set_chip_data(virq, NULL);
- irq_set_chip(virq, NULL);
-}
-
static struct irq_host_ops pci_pic_host_ops = {
.map = pci_pic_host_map,
- .unmap = pci_host_unmap,
};
int __init pq2ads_pci_init_irq(void)
diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c
index 188272934cf..104faa8aa23 100644
--- a/arch/powerpc/platforms/83xx/suspend.c
+++ b/arch/powerpc/platforms/83xx/suspend.c
@@ -318,17 +318,20 @@ static const struct platform_suspend_ops mpc83xx_suspend_ops = {
.end = mpc83xx_suspend_end,
};
+static struct of_device_id pmc_match[];
static int pmc_probe(struct platform_device *ofdev)
{
+ const struct of_device_id *match;
struct device_node *np = ofdev->dev.of_node;
struct resource res;
struct pmc_type *type;
int ret = 0;
- if (!ofdev->dev.of_match)
+ match = of_match_device(pmc_match, &ofdev->dev);
+ if (!match)
return -EINVAL;
- type = ofdev->dev.of_match->data;
+ type = match->data;
if (!of_device_is_available(np))
return -ENODEV;
diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c
index 0d00ff9d05a..d6a93a10c0f 100644
--- a/arch/powerpc/platforms/85xx/smp.c
+++ b/arch/powerpc/platforms/85xx/smp.c
@@ -41,7 +41,7 @@ extern void __early_start(void);
#define NUM_BOOT_ENTRY 8
#define SIZE_BOOT_ENTRY (NUM_BOOT_ENTRY * sizeof(u32))
-static void __init
+static int __init
smp_85xx_kick_cpu(int nr)
{
unsigned long flags;
@@ -60,7 +60,7 @@ smp_85xx_kick_cpu(int nr)
if (cpu_rel_addr == NULL) {
printk(KERN_ERR "No cpu-release-addr for cpu %d\n", nr);
- return;
+ return -ENOENT;
}
/*
@@ -107,6 +107,8 @@ smp_85xx_kick_cpu(int nr)
iounmap(bptr_vaddr);
pr_debug("waited %d msecs for CPU #%d.\n", n, nr);
+
+ return 0;
}
static void __init
@@ -233,8 +235,10 @@ void __init mpc85xx_smp_init(void)
smp_85xx_ops.message_pass = smp_mpic_message_pass;
}
- if (cpu_has_feature(CPU_FTR_DBELL))
- smp_85xx_ops.message_pass = doorbell_message_pass;
+ if (cpu_has_feature(CPU_FTR_DBELL)) {
+ smp_85xx_ops.message_pass = smp_muxed_ipi_message_pass;
+ smp_85xx_ops.cause_ipi = doorbell_cause_ipi;
+ }
BUG_ON(!smp_85xx_ops.message_pass);
diff --git a/arch/powerpc/platforms/85xx/socrates_fpga_pic.c b/arch/powerpc/platforms/85xx/socrates_fpga_pic.c
index db864623b4a..12cb9bb2cc6 100644
--- a/arch/powerpc/platforms/85xx/socrates_fpga_pic.c
+++ b/arch/powerpc/platforms/85xx/socrates_fpga_pic.c
@@ -48,8 +48,6 @@ static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {
[8] = {0, IRQ_TYPE_LEVEL_HIGH},
};
-#define socrates_fpga_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq)
-
static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
static void __iomem *socrates_fpga_pic_iobase;
@@ -110,11 +108,9 @@ void socrates_fpga_pic_cascade(unsigned int irq, struct irq_desc *desc)
static void socrates_fpga_pic_ack(struct irq_data *d)
{
unsigned long flags;
- unsigned int hwirq, irq_line;
+ unsigned int irq_line, hwirq = irqd_to_hwirq(d);
uint32_t mask;
- hwirq = socrates_fpga_irq_to_hw(d->irq);
-
irq_line = fpga_irqs[hwirq].irq_line;
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
@@ -127,12 +123,10 @@ static void socrates_fpga_pic_ack(struct irq_data *d)
static void socrates_fpga_pic_mask(struct irq_data *d)
{
unsigned long flags;
- unsigned int hwirq;
+ unsigned int hwirq = irqd_to_hwirq(d);
int irq_line;
u32 mask;
- hwirq = socrates_fpga_irq_to_hw(d->irq);
-
irq_line = fpga_irqs[hwirq].irq_line;
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
@@ -145,12 +139,10 @@ static void socrates_fpga_pic_mask(struct irq_data *d)
static void socrates_fpga_pic_mask_ack(struct irq_data *d)
{
unsigned long flags;
- unsigned int hwirq;
+ unsigned int hwirq = irqd_to_hwirq(d);
int irq_line;
u32 mask;
- hwirq = socrates_fpga_irq_to_hw(d->irq);
-
irq_line = fpga_irqs[hwirq].irq_line;
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
@@ -164,12 +156,10 @@ static void socrates_fpga_pic_mask_ack(struct irq_data *d)
static void socrates_fpga_pic_unmask(struct irq_data *d)
{
unsigned long flags;
- unsigned int hwirq;
+ unsigned int hwirq = irqd_to_hwirq(d);
int irq_line;
u32 mask;
- hwirq = socrates_fpga_irq_to_hw(d->irq);
-
irq_line = fpga_irqs[hwirq].irq_line;
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
@@ -182,12 +172,10 @@ static void socrates_fpga_pic_unmask(struct irq_data *d)
static void socrates_fpga_pic_eoi(struct irq_data *d)
{
unsigned long flags;
- unsigned int hwirq;
+ unsigned int hwirq = irqd_to_hwirq(d);
int irq_line;
u32 mask;
- hwirq = socrates_fpga_irq_to_hw(d->irq);
-
irq_line = fpga_irqs[hwirq].irq_line;
raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
@@ -201,12 +189,10 @@ static int socrates_fpga_pic_set_type(struct irq_data *d,
unsigned int flow_type)
{
unsigned long flags;
- unsigned int hwirq;
+ unsigned int hwirq = irqd_to_hwirq(d);
int polarity;
u32 mask;
- hwirq = socrates_fpga_irq_to_hw(d->irq);
-
if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
return -EINVAL;
diff --git a/arch/powerpc/platforms/86xx/gef_pic.c b/arch/powerpc/platforms/86xx/gef_pic.c
index 0beec7d5566..94594e58594 100644
--- a/arch/powerpc/platforms/86xx/gef_pic.c
+++ b/arch/powerpc/platforms/86xx/gef_pic.c
@@ -46,8 +46,6 @@
#define GEF_PIC_CPU0_MCP_MASK GEF_PIC_MCP_MASK(0)
#define GEF_PIC_CPU1_MCP_MASK GEF_PIC_MCP_MASK(1)
-#define gef_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq)
-
static DEFINE_RAW_SPINLOCK(gef_pic_lock);
@@ -113,11 +111,9 @@ void gef_pic_cascade(unsigned int irq, struct irq_desc *desc)
static void gef_pic_mask(struct irq_data *d)
{
unsigned long flags;
- unsigned int hwirq;
+ unsigned int hwirq = irqd_to_hwirq(d);
u32 mask;
- hwirq = gef_irq_to_hw(d->irq);
-
raw_spin_lock_irqsave(&gef_pic_lock, flags);
mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
mask &= ~(1 << hwirq);
@@ -136,11 +132,9 @@ static void gef_pic_mask_ack(struct irq_data *d)
static void gef_pic_unmask(struct irq_data *d)
{
unsigned long flags;
- unsigned int hwirq;
+ unsigned int hwirq = irqd_to_hwirq(d);
u32 mask;
- hwirq = gef_irq_to_hw(d->irq);
-
raw_spin_lock_irqsave(&gef_pic_lock, flags);
mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
mask |= (1 << hwirq);
diff --git a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c
index 018cc67be42..a896511690c 100644
--- a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c
+++ b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c
@@ -66,7 +66,7 @@ static void __init mpc8610_suspend_init(void)
return;
}
- ret = request_irq(irq, mpc8610_sw9_irq, 0, "sw9/wakeup", NULL);
+ ret = request_irq(irq, mpc8610_sw9_irq, 0, "sw9:wakeup", NULL);
if (ret) {
pr_err("%s: can't request pixis event IRQ: %d\n",
__func__, ret);
@@ -105,45 +105,77 @@ machine_device_initcall(mpc86xx_hpcd, mpc8610_declare_of_platform_devices);
#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
-static u32 get_busfreq(void)
-{
- struct device_node *node;
-
- u32 fs_busfreq = 0;
- node = of_find_node_by_type(NULL, "cpu");
- if (node) {
- unsigned int size;
- const unsigned int *prop =
- of_get_property(node, "bus-frequency", &size);
- if (prop)
- fs_busfreq = *prop;
- of_node_put(node);
- };
- return fs_busfreq;
-}
+/*
+ * DIU Area Descriptor
+ *
+ * The MPC8610 reference manual shows the bits of the AD register in
+ * little-endian order, which causes the BLUE_C field to be split into two
+ * parts. To simplify the definition of the MAKE_AD() macro, we define the
+ * fields in big-endian order and byte-swap the result.
+ *
+ * So even though the registers don't look like they're in the
+ * same bit positions as they are on the P1022, the same value is written to
+ * the AD register on the MPC8610 and on the P1022.
+ */
+#define AD_BYTE_F 0x10000000
+#define AD_ALPHA_C_MASK 0x0E000000
+#define AD_ALPHA_C_SHIFT 25
+#define AD_BLUE_C_MASK 0x01800000
+#define AD_BLUE_C_SHIFT 23
+#define AD_GREEN_C_MASK 0x00600000
+#define AD_GREEN_C_SHIFT 21
+#define AD_RED_C_MASK 0x00180000
+#define AD_RED_C_SHIFT 19
+#define AD_PALETTE 0x00040000
+#define AD_PIXEL_S_MASK 0x00030000
+#define AD_PIXEL_S_SHIFT 16
+#define AD_COMP_3_MASK 0x0000F000
+#define AD_COMP_3_SHIFT 12
+#define AD_COMP_2_MASK 0x00000F00
+#define AD_COMP_2_SHIFT 8
+#define AD_COMP_1_MASK 0x000000F0
+#define AD_COMP_1_SHIFT 4
+#define AD_COMP_0_MASK 0x0000000F
+#define AD_COMP_0_SHIFT 0
+
+#define MAKE_AD(alpha, red, blue, green, size, c0, c1, c2, c3) \
+ cpu_to_le32(AD_BYTE_F | (alpha << AD_ALPHA_C_SHIFT) | \
+ (blue << AD_BLUE_C_SHIFT) | (green << AD_GREEN_C_SHIFT) | \
+ (red << AD_RED_C_SHIFT) | (c3 << AD_COMP_3_SHIFT) | \
+ (c2 << AD_COMP_2_SHIFT) | (c1 << AD_COMP_1_SHIFT) | \
+ (c0 << AD_COMP_0_SHIFT) | (size << AD_PIXEL_S_SHIFT))
unsigned int mpc8610hpcd_get_pixel_format(unsigned int bits_per_pixel,
int monitor_port)
{
static const unsigned long pixelformat[][3] = {
- {0x88882317, 0x88083218, 0x65052119},
- {0x88883316, 0x88082219, 0x65053118},
+ {
+ MAKE_AD(3, 0, 2, 1, 3, 8, 8, 8, 8),
+ MAKE_AD(4, 2, 0, 1, 2, 8, 8, 8, 0),
+ MAKE_AD(4, 0, 2, 1, 1, 5, 6, 5, 0)
+ },
+ {
+ MAKE_AD(3, 2, 0, 1, 3, 8, 8, 8, 8),
+ MAKE_AD(4, 0, 2, 1, 2, 8, 8, 8, 0),
+ MAKE_AD(4, 2, 0, 1, 1, 5, 6, 5, 0)
+ },
};
- unsigned int pix_fmt, arch_monitor;
+ unsigned int arch_monitor;
+ /* The DVI port is mis-wired on revision 1 of this board. */
arch_monitor = ((*pixis_arch == 0x01) && (monitor_port == 0))? 0 : 1;
- /* DVI port for board version 0x01 */
-
- if (bits_per_pixel == 32)
- pix_fmt = pixelformat[arch_monitor][0];
- else if (bits_per_pixel == 24)
- pix_fmt = pixelformat[arch_monitor][1];
- else if (bits_per_pixel == 16)
- pix_fmt = pixelformat[arch_monitor][2];
- else
- pix_fmt = pixelformat[1][0];
-
- return pix_fmt;
+
+ switch (bits_per_pixel) {
+ case 32:
+ return pixelformat[arch_monitor][0];
+ case 24:
+ return pixelformat[arch_monitor][1];
+ case 16:
+ return pixelformat[arch_monitor][2];
+ default:
+ pr_err("fsl-diu: unsupported pixel depth %u\n", bits_per_pixel);
+ return 0;
+ }
}
void mpc8610hpcd_set_gamma_table(int monitor_port, char *gamma_table_base)
@@ -190,8 +222,7 @@ void mpc8610hpcd_set_pixel_clock(unsigned int pixclock)
}
/* Pixel Clock configuration */
- pr_debug("DIU: Bus Frequency = %d\n", get_busfreq());
- speed_ccb = get_busfreq();
+ speed_ccb = fsl_get_sys_freq();
/* Calculate the pixel clock with the smallest error */
/* calculate the following in steps to avoid overflow */
diff --git a/arch/powerpc/platforms/86xx/mpc86xx_smp.c b/arch/powerpc/platforms/86xx/mpc86xx_smp.c
index eacea0e3fcc..af09baee22c 100644
--- a/arch/powerpc/platforms/86xx/mpc86xx_smp.c
+++ b/arch/powerpc/platforms/86xx/mpc86xx_smp.c
@@ -56,7 +56,7 @@ smp_86xx_release_core(int nr)
}
-static void __init
+static int __init
smp_86xx_kick_cpu(int nr)
{
unsigned int save_vector;
@@ -65,7 +65,7 @@ smp_86xx_kick_cpu(int nr)
unsigned int *vector = (unsigned int *)(KERNELBASE + 0x100);
if (nr < 0 || nr >= NR_CPUS)
- return;
+ return -ENOENT;
pr_debug("smp_86xx_kick_cpu: kick CPU #%d\n", nr);
@@ -92,6 +92,8 @@ smp_86xx_kick_cpu(int nr)
local_irq_restore(flags);
pr_debug("wait CPU #%d for %d msecs.\n", nr, n);
+
+ return 0;
}
diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c
index 9ecce995dd4..1e121088826 100644
--- a/arch/powerpc/platforms/8xx/m8xx_setup.c
+++ b/arch/powerpc/platforms/8xx/m8xx_setup.c
@@ -150,7 +150,7 @@ void __init mpc8xx_calibrate_decr(void)
*/
cpu = of_find_node_by_type(NULL, "cpu");
virq= irq_of_parse_and_map(cpu, 0);
- irq = irq_map[virq].hwirq;
+ irq = virq_to_hw(virq);
sys_tmr2 = immr_map(im_sit);
out_be16(&sys_tmr2->sit_tbscr, ((1 << (7 - (irq/2))) << 8) |
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index f7b07720aa3..f970ca2b180 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -20,6 +20,7 @@ source "arch/powerpc/platforms/embedded6xx/Kconfig"
source "arch/powerpc/platforms/44x/Kconfig"
source "arch/powerpc/platforms/40x/Kconfig"
source "arch/powerpc/platforms/amigaone/Kconfig"
+source "arch/powerpc/platforms/wsp/Kconfig"
config KVM_GUEST
bool "KVM Guest support"
@@ -56,16 +57,19 @@ config UDBG_RTAS_CONSOLE
depends on PPC_RTAS
default n
+config PPC_SMP_MUXED_IPI
+ bool
+ help
+ Select this opton if your platform supports SMP and your
+ interrupt controller provides less than 4 interrupts to each
+ cpu. This will enable the generic code to multiplex the 4
+ messages on to one ipi.
+
config PPC_UDBG_BEAT
bool "BEAT based debug console"
depends on PPC_CELLEB
default n
-config XICS
- depends on PPC_PSERIES
- bool
- default y
-
config IPIC
bool
default n
@@ -147,14 +151,27 @@ config PPC_970_NAP
bool
default n
+config PPC_P7_NAP
+ bool
+ default n
+
config PPC_INDIRECT_IO
bool
select GENERIC_IOMAP
- default n
+
+config PPC_INDIRECT_PIO
+ bool
+ select PPC_INDIRECT_IO
+
+config PPC_INDIRECT_MMIO
+ bool
+ select PPC_INDIRECT_IO
+
+config PPC_IO_WORKAROUNDS
+ bool
config GENERIC_IOMAP
bool
- default n
source "drivers/cpufreq/Kconfig"
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index 111138c55f9..2165b65876f 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -73,6 +73,7 @@ config PPC_BOOK3S_64
config PPC_BOOK3E_64
bool "Embedded processors"
select PPC_FPU # Make it a choice ?
+ select PPC_SMP_MUXED_IPI
endchoice
@@ -107,6 +108,10 @@ config POWER4
depends on PPC64 && PPC_BOOK3S
def_bool y
+config PPC_A2
+ bool
+ depends on PPC_BOOK3E_64
+
config TUNE_CELL
bool "Optimize for Cell Broadband Engine"
depends on PPC64 && PPC_BOOK3S
@@ -174,6 +179,7 @@ config FSL_BOOKE
config PPC_FSL_BOOK3E
bool
select FSL_EMB_PERFMON
+ select PPC_SMP_MUXED_IPI
default y if FSL_BOOKE
config PTE_64BIT
@@ -226,6 +232,24 @@ config VSX
If in doubt, say Y here.
+config PPC_ICSWX
+ bool "Support for PowerPC icswx coprocessor instruction"
+ depends on POWER4
+ default n
+ ---help---
+
+ This option enables kernel support for the PowerPC Initiate
+ Coprocessor Store Word (icswx) coprocessor instruction on POWER7
+ or newer processors.
+
+ This option is only useful if you have a processor that supports
+ the icswx coprocessor instruction. It does not have any effect
+ on processors without the icswx coprocessor instruction.
+
+ This option slightly increases kernel memory usage.
+
+ If in doubt, say N here.
+
config SPE
bool "SPE Support"
depends on E200 || (E500 && !PPC_E500MC)
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index fdb9f0b0d7a..73e2116cfee 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_PPC_CELL) += cell/
obj-$(CONFIG_PPC_PS3) += ps3/
obj-$(CONFIG_EMBEDDED6xx) += embedded6xx/
obj-$(CONFIG_AMIGAONE) += amigaone/
+obj-$(CONFIG_PPC_WSP) += wsp/
diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig
index 81239ebed83..67d5009b4e8 100644
--- a/arch/powerpc/platforms/cell/Kconfig
+++ b/arch/powerpc/platforms/cell/Kconfig
@@ -6,7 +6,8 @@ config PPC_CELL_COMMON
bool
select PPC_CELL
select PPC_DCR_MMIO
- select PPC_INDIRECT_IO
+ select PPC_INDIRECT_PIO
+ select PPC_INDIRECT_MMIO
select PPC_NATIVE
select PPC_RTAS
select IRQ_EDGE_EOI_HANDLER
@@ -15,6 +16,7 @@ config PPC_CELL_NATIVE
bool
select PPC_CELL_COMMON
select MPIC
+ select PPC_IO_WORKAROUNDS
select IBM_NEW_EMAC_EMAC4
select IBM_NEW_EMAC_RGMII
select IBM_NEW_EMAC_ZMII #test only
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile
index 83fafe92264..a4a89350bcf 100644
--- a/arch/powerpc/platforms/cell/Makefile
+++ b/arch/powerpc/platforms/cell/Makefile
@@ -1,7 +1,7 @@
obj-$(CONFIG_PPC_CELL_COMMON) += cbe_regs.o interrupt.o pervasive.o
obj-$(CONFIG_PPC_CELL_NATIVE) += iommu.o setup.o spider-pic.o \
- pmu.o io-workarounds.o spider-pci.o
+ pmu.o spider-pci.o
obj-$(CONFIG_CBE_RAS) += ras.o
obj-$(CONFIG_CBE_THERM) += cbe_thermal.o
@@ -39,11 +39,10 @@ obj-y += celleb_setup.o \
celleb_pci.o celleb_scc_epci.o \
celleb_scc_pciex.o \
celleb_scc_uhc.o \
- io-workarounds.o spider-pci.o \
- beat.o beat_htab.o beat_hvCall.o \
- beat_interrupt.o beat_iommu.o
+ spider-pci.o beat.o beat_htab.o \
+ beat_hvCall.o beat_interrupt.o \
+ beat_iommu.o
-obj-$(CONFIG_SMP) += beat_smp.o
obj-$(CONFIG_PPC_UDBG_BEAT) += beat_udbg.o
obj-$(CONFIG_SERIAL_TXX9) += celleb_scc_sio.o
obj-$(CONFIG_SPU_BASE) += beat_spu_priv1.o
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
index bb5ebf8fa80..ac06903e136 100644
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -113,7 +113,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
pr_devel("axon_msi: woff %x roff %x msi %x\n",
write_offset, msic->read_offset, msi);
- if (msi < NR_IRQS && irq_map[msi].host == msic->irq_host) {
+ if (msi < NR_IRQS && irq_get_chip_data(msi) == msic) {
generic_handle_irq(msi);
msic->fifo_virt[idx] = cpu_to_le32(0xffffffff);
} else {
@@ -320,6 +320,7 @@ static struct irq_chip msic_irq_chip = {
static int msic_host_map(struct irq_host *h, unsigned int virq,
irq_hw_number_t hw)
{
+ irq_set_chip_data(virq, h->host_data);
irq_set_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq);
return 0;
diff --git a/arch/powerpc/platforms/cell/beat_interrupt.c b/arch/powerpc/platforms/cell/beat_interrupt.c
index 4cb9e147c30..55015e1f693 100644
--- a/arch/powerpc/platforms/cell/beat_interrupt.c
+++ b/arch/powerpc/platforms/cell/beat_interrupt.c
@@ -148,16 +148,6 @@ static int beatic_pic_host_map(struct irq_host *h, unsigned int virq,
}
/*
- * Update binding hardware IRQ number (hw) and Virtuql
- * IRQ number (virq). This is called only once for a given mapping.
- */
-static void beatic_pic_host_remap(struct irq_host *h, unsigned int virq,
- irq_hw_number_t hw)
-{
- beat_construct_and_connect_irq_plug(virq, hw);
-}
-
-/*
* Translate device-tree interrupt spec to irq_hw_number_t style (ulong),
* to pass away to irq_create_mapping().
*
@@ -184,7 +174,6 @@ static int beatic_pic_host_match(struct irq_host *h, struct device_node *np)
static struct irq_host_ops beatic_pic_host_ops = {
.map = beatic_pic_host_map,
- .remap = beatic_pic_host_remap,
.unmap = beatic_pic_host_unmap,
.xlate = beatic_pic_host_xlate,
.match = beatic_pic_host_match,
@@ -257,22 +246,6 @@ void __init beatic_init_IRQ(void)
irq_set_default_host(beatic_host);
}
-#ifdef CONFIG_SMP
-
-/* Nullified to compile with SMP mode */
-void beatic_setup_cpu(int cpu)
-{
-}
-
-void beatic_cause_IPI(int cpu, int mesg)
-{
-}
-
-void beatic_request_IPIs(void)
-{
-}
-#endif /* CONFIG_SMP */
-
void beatic_deinit_IRQ(void)
{
int i;
diff --git a/arch/powerpc/platforms/cell/beat_interrupt.h b/arch/powerpc/platforms/cell/beat_interrupt.h
index b470fd0051f..a7e52f91a07 100644
--- a/arch/powerpc/platforms/cell/beat_interrupt.h
+++ b/arch/powerpc/platforms/cell/beat_interrupt.h
@@ -24,9 +24,6 @@
extern void beatic_init_IRQ(void);
extern unsigned int beatic_get_irq(void);
-extern void beatic_cause_IPI(int cpu, int mesg);
-extern void beatic_request_IPIs(void);
-extern void beatic_setup_cpu(int);
extern void beatic_deinit_IRQ(void);
#endif
diff --git a/arch/powerpc/platforms/cell/beat_smp.c b/arch/powerpc/platforms/cell/beat_smp.c
deleted file mode 100644
index 26efc204c47..00000000000
--- a/arch/powerpc/platforms/cell/beat_smp.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * SMP support for Celleb platform. (Incomplete)
- *
- * (C) Copyright 2006 TOSHIBA CORPORATION
- *
- * This code is based on arch/powerpc/platforms/cell/smp.c:
- * Dave Engebretsen, Peter Bergner, and
- * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com
- * Plus various changes from other IBM teams...
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#undef DEBUG
-
-#include <linux/kernel.h>
-#include <linux/smp.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/threads.h>
-#include <linux/cpu.h>
-
-#include <asm/irq.h>
-#include <asm/smp.h>
-#include <asm/machdep.h>
-#include <asm/udbg.h>
-
-#include "beat_interrupt.h"
-
-#ifdef DEBUG
-#define DBG(fmt...) udbg_printf(fmt)
-#else
-#define DBG(fmt...)
-#endif
-
-/*
- * The primary thread of each non-boot processor is recorded here before
- * smp init.
- */
-/* static cpumask_t of_spin_map; */
-
-/**
- * smp_startup_cpu() - start the given cpu
- *
- * At boot time, there is nothing to do for primary threads which were
- * started from Open Firmware. For anything else, call RTAS with the
- * appropriate start location.
- *
- * Returns:
- * 0 - failure
- * 1 - success
- */
-static inline int __devinit smp_startup_cpu(unsigned int lcpu)
-{
- return 0;
-}
-
-static void smp_beatic_message_pass(int target, int msg)
-{
- unsigned int i;
-
- if (target < NR_CPUS) {
- beatic_cause_IPI(target, msg);
- } else {
- for_each_online_cpu(i) {
- if (target == MSG_ALL_BUT_SELF
- && i == smp_processor_id())
- continue;
- beatic_cause_IPI(i, msg);
- }
- }
-}
-
-static int __init smp_beatic_probe(void)
-{
- return cpus_weight(cpu_possible_map);
-}
-
-static void __devinit smp_beatic_setup_cpu(int cpu)
-{
- beatic_setup_cpu(cpu);
-}
-
-static void __devinit smp_celleb_kick_cpu(int nr)
-{
- BUG_ON(nr < 0 || nr >= NR_CPUS);
-
- if (!smp_startup_cpu(nr))
- return;
-}
-
-static int smp_celleb_cpu_bootable(unsigned int nr)
-{
- return 1;
-}
-static struct smp_ops_t bpa_beatic_smp_ops = {
- .message_pass = smp_beatic_message_pass,
- .probe = smp_beatic_probe,
- .kick_cpu = smp_celleb_kick_cpu,
- .setup_cpu = smp_beatic_setup_cpu,
- .cpu_bootable = smp_celleb_cpu_bootable,
-};
-
-/* This is called very early */
-void __init smp_init_celleb(void)
-{
- DBG(" -> smp_init_celleb()\n");
-
- smp_ops = &bpa_beatic_smp_ops;
-
- DBG(" <- smp_init_celleb()\n");
-}
diff --git a/arch/powerpc/platforms/cell/cbe_regs.c b/arch/powerpc/platforms/cell/cbe_regs.c
index dbc338f187a..f3917e7a5b4 100644
--- a/arch/powerpc/platforms/cell/cbe_regs.c
+++ b/arch/powerpc/platforms/cell/cbe_regs.c
@@ -45,8 +45,8 @@ static struct cbe_thread_map
unsigned int cbe_id;
} cbe_thread_map[NR_CPUS];
-static cpumask_t cbe_local_mask[MAX_CBE] = { [0 ... MAX_CBE-1] = CPU_MASK_NONE };
-static cpumask_t cbe_first_online_cpu = CPU_MASK_NONE;
+static cpumask_t cbe_local_mask[MAX_CBE] = { [0 ... MAX_CBE-1] = {CPU_BITS_NONE} };
+static cpumask_t cbe_first_online_cpu = { CPU_BITS_NONE };
static struct cbe_regs_map *cbe_find_map(struct device_node *np)
{
@@ -159,7 +159,8 @@ EXPORT_SYMBOL_GPL(cbe_cpu_to_node);
u32 cbe_node_to_cpu(int node)
{
- return find_first_bit( (unsigned long *) &cbe_local_mask[node], sizeof(cpumask_t));
+ return cpumask_first(&cbe_local_mask[node]);
+
}
EXPORT_SYMBOL_GPL(cbe_node_to_cpu);
@@ -268,9 +269,9 @@ void __init cbe_regs_init(void)
thread->regs = map;
thread->cbe_id = cbe_id;
map->be_node = thread->be_node;
- cpu_set(i, cbe_local_mask[cbe_id]);
+ cpumask_set_cpu(i, &cbe_local_mask[cbe_id]);
if(thread->thread_id == 0)
- cpu_set(i, cbe_first_online_cpu);
+ cpumask_set_cpu(i, &cbe_first_online_cpu);
}
}
diff --git a/arch/powerpc/platforms/cell/celleb_pci.c b/arch/powerpc/platforms/cell/celleb_pci.c
index 404d1fc04d5..5822141aa63 100644
--- a/arch/powerpc/platforms/cell/celleb_pci.c
+++ b/arch/powerpc/platforms/cell/celleb_pci.c
@@ -41,7 +41,6 @@
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
-#include "io-workarounds.h"
#include "celleb_pci.h"
#define MAX_PCI_DEVICES 32
@@ -320,7 +319,7 @@ static int __init celleb_setup_fake_pci_device(struct device_node *node,
size = 256;
config = &private->fake_config[devno][fn];
- *config = alloc_maybe_bootmem(size, GFP_KERNEL);
+ *config = zalloc_maybe_bootmem(size, GFP_KERNEL);
if (*config == NULL) {
printk(KERN_ERR "PCI: "
"not enough memory for fake configuration space\n");
@@ -331,7 +330,7 @@ static int __init celleb_setup_fake_pci_device(struct device_node *node,
size = sizeof(struct celleb_pci_resource);
res = &private->res[devno][fn];
- *res = alloc_maybe_bootmem(size, GFP_KERNEL);
+ *res = zalloc_maybe_bootmem(size, GFP_KERNEL);
if (*res == NULL) {
printk(KERN_ERR
"PCI: not enough memory for resource data space\n");
@@ -432,7 +431,7 @@ static int __init phb_set_bus_ranges(struct device_node *dev,
static void __init celleb_alloc_private_mem(struct pci_controller *hose)
{
hose->private_data =
- alloc_maybe_bootmem(sizeof(struct celleb_pci_private),
+ zalloc_maybe_bootmem(sizeof(struct celleb_pci_private),
GFP_KERNEL);
}
@@ -469,18 +468,6 @@ static struct of_device_id celleb_phb_match[] __initdata = {
},
};
-static int __init celleb_io_workaround_init(struct pci_controller *phb,
- struct celleb_phb_spec *phb_spec)
-{
- if (phb_spec->ops) {
- iowa_register_bus(phb, phb_spec->ops, phb_spec->iowa_init,
- phb_spec->iowa_data);
- io_workaround_init();
- }
-
- return 0;
-}
-
int __init celleb_setup_phb(struct pci_controller *phb)
{
struct device_node *dev = phb->dn;
@@ -500,7 +487,11 @@ int __init celleb_setup_phb(struct pci_controller *phb)
if (rc)
return 1;
- return celleb_io_workaround_init(phb, phb_spec);
+ if (phb_spec->ops)
+ iowa_register_bus(phb, phb_spec->ops,
+ phb_spec->iowa_init,
+ phb_spec->iowa_data);
+ return 0;
}
int celleb_pci_probe_mode(struct pci_bus *bus)
diff --git a/arch/powerpc/platforms/cell/celleb_pci.h b/arch/powerpc/platforms/cell/celleb_pci.h
index 4cba1523ec5..a801fcc5f38 100644
--- a/arch/powerpc/platforms/cell/celleb_pci.h
+++ b/arch/powerpc/platforms/cell/celleb_pci.h
@@ -26,8 +26,9 @@
#include <asm/pci-bridge.h>
#include <asm/prom.h>
#include <asm/ppc-pci.h>
+#include <asm/io-workarounds.h>
-#include "io-workarounds.h"
+struct iowa_bus;
struct celleb_phb_spec {
int (*setup)(struct device_node *, struct pci_controller *);
diff --git a/arch/powerpc/platforms/cell/celleb_setup.c b/arch/powerpc/platforms/cell/celleb_setup.c
index e5384557977..d58d9bae4b9 100644
--- a/arch/powerpc/platforms/cell/celleb_setup.c
+++ b/arch/powerpc/platforms/cell/celleb_setup.c
@@ -128,10 +128,6 @@ static void __init celleb_setup_arch_beat(void)
spu_management_ops = &spu_management_of_ops;
#endif
-#ifdef CONFIG_SMP
- smp_init_celleb();
-#endif
-
celleb_setup_arch_common();
}
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 44cfd1bef89..449c08c1586 100644
--- a/arch/powerpc/platforms/cell/interrupt.c
+++ b/arch/powerpc/platforms/cell/interrupt.c
@@ -196,8 +196,20 @@ static irqreturn_t iic_ipi_action(int irq, void *dev_id)
{
int ipi = (int)(long)dev_id;
- smp_message_recv(ipi);
-
+ switch(ipi) {
+ case PPC_MSG_CALL_FUNCTION:
+ generic_smp_call_function_interrupt();
+ break;
+ case PPC_MSG_RESCHEDULE:
+ scheduler_ipi();
+ break;
+ case PPC_MSG_CALL_FUNC_SINGLE:
+ generic_smp_call_function_single_interrupt();
+ break;
+ case PPC_MSG_DEBUGGER_BREAK:
+ debug_ipi_action(0, NULL);
+ break;
+ }
return IRQ_HANDLED;
}
static void iic_request_ipi(int ipi, const char *name)
diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c
deleted file mode 100644
index 5c1118e3194..00000000000
--- a/arch/powerpc/platforms/cell/io-workarounds.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Support PCI IO workaround
- *
- * Copyright (C) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>
- * IBM, Corp.
- * (C) Copyright 2007-2008 TOSHIBA CORPORATION
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#undef DEBUG
-
-#include <linux/kernel.h>
-
-#include <asm/io.h>
-#include <asm/machdep.h>
-#include <asm/pgtable.h>
-#include <asm/ppc-pci.h>
-
-#include "io-workarounds.h"
-
-#define IOWA_MAX_BUS 8
-
-static struct iowa_bus iowa_busses[IOWA_MAX_BUS];
-static unsigned int iowa_bus_count;
-
-static struct iowa_bus *iowa_pci_find(unsigned long vaddr, unsigned long paddr)
-{
- int i, j;
- struct resource *res;
- unsigned long vstart, vend;
-
- for (i = 0; i < iowa_bus_count; i++) {
- struct iowa_bus *bus = &iowa_busses[i];
- struct pci_controller *phb = bus->phb;
-
- if (vaddr) {
- vstart = (unsigned long)phb->io_base_virt;
- vend = vstart + phb->pci_io_size - 1;
- if ((vaddr >= vstart) && (vaddr <= vend))
- return bus;
- }
-
- if (paddr)
- for (j = 0; j < 3; j++) {
- res = &phb->mem_resources[j];
- if (paddr >= res->start && paddr <= res->end)
- return bus;
- }
- }
-
- return NULL;
-}
-
-struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr)
-{
- struct iowa_bus *bus;
- int token;
-
- token = PCI_GET_ADDR_TOKEN(addr);
-
- if (token && token <= iowa_bus_count)
- bus = &iowa_busses[token - 1];
- else {
- unsigned long vaddr, paddr;
- pte_t *ptep;
-
- vaddr = (unsigned long)PCI_FIX_ADDR(addr);
- if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END)
- return NULL;
-
- ptep = find_linux_pte(init_mm.pgd, vaddr);
- if (ptep == NULL)
- paddr = 0;
- else
- paddr = pte_pfn(*ptep) << PAGE_SHIFT;
- bus = iowa_pci_find(vaddr, paddr);
-
- if (bus == NULL)
- return NULL;
- }
-
- return bus;
-}
-
-struct iowa_bus *iowa_pio_find_bus(unsigned long port)
-{
- unsigned long vaddr = (unsigned long)pci_io_base + port;
- return iowa_pci_find(vaddr, 0);
-}
-
-
-#define DEF_PCI_AC_RET(name, ret, at, al, space, aa) \
-static ret iowa_##name at \
-{ \
- struct iowa_bus *bus; \
- bus = iowa_##space##_find_bus(aa); \
- if (bus && bus->ops && bus->ops->name) \
- return bus->ops->name al; \
- return __do_##name al; \
-}
-
-#define DEF_PCI_AC_NORET(name, at, al, space, aa) \
-static void iowa_##name at \
-{ \
- struct iowa_bus *bus; \
- bus = iowa_##space##_find_bus(aa); \
- if (bus && bus->ops && bus->ops->name) { \
- bus->ops->name al; \
- return; \
- } \
- __do_##name al; \
-}
-
-#include <asm/io-defs.h>
-
-#undef DEF_PCI_AC_RET
-#undef DEF_PCI_AC_NORET
-
-static const struct ppc_pci_io __devinitconst iowa_pci_io = {
-
-#define DEF_PCI_AC_RET(name, ret, at, al, space, aa) .name = iowa_##name,
-#define DEF_PCI_AC_NORET(name, at, al, space, aa) .name = iowa_##name,
-
-#include <asm/io-defs.h>
-
-#undef DEF_PCI_AC_RET
-#undef DEF_PCI_AC_NORET
-
-};
-
-static void __iomem *iowa_ioremap(phys_addr_t addr, unsigned long size,
- unsigned long flags, void *caller)
-{
- struct iowa_bus *bus;
- void __iomem *res = __ioremap_caller(addr, size, flags, caller);
- int busno;
-
- bus = iowa_pci_find(0, (unsigned long)addr);
- if (bus != NULL) {
- busno = bus - iowa_busses;
- PCI_SET_ADDR_TOKEN(res, busno + 1);
- }
- return res;
-}
-
-/* Regist new bus to support workaround */
-void __devinit iowa_register_bus(struct pci_controller *phb,
- struct ppc_pci_io *ops,
- int (*initfunc)(struct iowa_bus *, void *), void *data)
-{
- struct iowa_bus *bus;
- struct device_node *np = phb->dn;
-
- if (iowa_bus_count >= IOWA_MAX_BUS) {
- pr_err("IOWA:Too many pci bridges, "
- "workarounds disabled for %s\n", np->full_name);
- return;
- }
-
- bus = &iowa_busses[iowa_bus_count];
- bus->phb = phb;
- bus->ops = ops;
-
- if (initfunc)
- if ((*initfunc)(bus, data))
- return;
-
- iowa_bus_count++;
-
- pr_debug("IOWA:[%d]Add bus, %s.\n", iowa_bus_count-1, np->full_name);
-}
-
-/* enable IO workaround */
-void __devinit io_workaround_init(void)
-{
- static int io_workaround_inited;
-
- if (io_workaround_inited)
- return;
- ppc_pci_io = iowa_pci_io;
- ppc_md.ioremap = iowa_ioremap;
- io_workaround_inited = 1;
-}
diff --git a/arch/powerpc/platforms/cell/io-workarounds.h b/arch/powerpc/platforms/cell/io-workarounds.h
deleted file mode 100644
index 6efc7782ebf..00000000000
--- a/arch/powerpc/platforms/cell/io-workarounds.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Support PCI IO workaround
- *
- * (C) Copyright 2007-2008 TOSHIBA CORPORATION
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef _IO_WORKAROUNDS_H
-#define _IO_WORKAROUNDS_H
-
-#include <linux/io.h>
-#include <asm/pci-bridge.h>
-
-/* Bus info */
-struct iowa_bus {
- struct pci_controller *phb;
- struct ppc_pci_io *ops;
- void *private;
-};
-
-void __devinit io_workaround_init(void);
-void __devinit iowa_register_bus(struct pci_controller *, struct ppc_pci_io *,
- int (*)(struct iowa_bus *, void *), void *);
-struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR);
-struct iowa_bus *iowa_pio_find_bus(unsigned long);
-
-extern struct ppc_pci_io spiderpci_ops;
-extern int spiderpci_iowa_init(struct iowa_bus *, void *);
-
-#define SPIDER_PCI_REG_BASE 0xd000
-#define SPIDER_PCI_REG_SIZE 0x1000
-#define SPIDER_PCI_VCI_CNTL_STAT 0x0110
-#define SPIDER_PCI_DUMMY_READ 0x0810
-#define SPIDER_PCI_DUMMY_READ_BASE 0x0814
-
-#endif /* _IO_WORKAROUNDS_H */
diff --git a/arch/powerpc/platforms/cell/qpace_setup.c b/arch/powerpc/platforms/cell/qpace_setup.c
index d31c594cfdf..51e290126bc 100644
--- a/arch/powerpc/platforms/cell/qpace_setup.c
+++ b/arch/powerpc/platforms/cell/qpace_setup.c
@@ -42,7 +42,6 @@
#include "interrupt.h"
#include "pervasive.h"
#include "ras.h"
-#include "io-workarounds.h"
static void qpace_show_cpuinfo(struct seq_file *m)
{
diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c
index fd57bfe00ed..c73cf4c43fc 100644
--- a/arch/powerpc/platforms/cell/setup.c
+++ b/arch/powerpc/platforms/cell/setup.c
@@ -51,11 +51,11 @@
#include <asm/udbg.h>
#include <asm/mpic.h>
#include <asm/cell-regs.h>
+#include <asm/io-workarounds.h>
#include "interrupt.h"
#include "pervasive.h"
#include "ras.h"
-#include "io-workarounds.h"
#ifdef DEBUG
#define DBG(fmt...) udbg_printf(fmt)
@@ -136,8 +136,6 @@ static int __devinit cell_setup_phb(struct pci_controller *phb)
iowa_register_bus(phb, &spiderpci_ops, &spiderpci_iowa_init,
(void *)SPIDER_PCI_REG_BASE);
- io_workaround_init();
-
return 0;
}
diff --git a/arch/powerpc/platforms/cell/smp.c b/arch/powerpc/platforms/cell/smp.c
index f774530075b..d176e6148e3 100644
--- a/arch/powerpc/platforms/cell/smp.c
+++ b/arch/powerpc/platforms/cell/smp.c
@@ -77,7 +77,7 @@ static inline int __devinit smp_startup_cpu(unsigned int lcpu)
unsigned int pcpu;
int start_cpu;
- if (cpu_isset(lcpu, of_spin_map))
+ if (cpumask_test_cpu(lcpu, &of_spin_map))
/* Already started by OF and sitting in spin loop */
return 1;
@@ -103,27 +103,11 @@ static inline int __devinit smp_startup_cpu(unsigned int lcpu)
return 1;
}
-static void smp_iic_message_pass(int target, int msg)
-{
- unsigned int i;
-
- if (target < NR_CPUS) {
- iic_cause_IPI(target, msg);
- } else {
- for_each_online_cpu(i) {
- if (target == MSG_ALL_BUT_SELF
- && i == smp_processor_id())
- continue;
- iic_cause_IPI(i, msg);
- }
- }
-}
-
static int __init smp_iic_probe(void)
{
iic_request_IPIs();
- return cpus_weight(cpu_possible_map);
+ return cpumask_weight(cpu_possible_mask);
}
static void __devinit smp_cell_setup_cpu(int cpu)
@@ -137,12 +121,12 @@ static void __devinit smp_cell_setup_cpu(int cpu)
mtspr(SPRN_DABRX, DABRX_KERNEL | DABRX_USER);
}
-static void __devinit smp_cell_kick_cpu(int nr)
+static int __devinit smp_cell_kick_cpu(int nr)
{
BUG_ON(nr < 0 || nr >= NR_CPUS);
if (!smp_startup_cpu(nr))
- return;
+ return -ENOENT;
/*
* The processor is currently spinning, waiting for the
@@ -150,6 +134,8 @@ static void __devinit smp_cell_kick_cpu(int nr)
* the processor will continue on to secondary_start
*/
paca[nr].cpu_start = 1;
+
+ return 0;
}
static int smp_cell_cpu_bootable(unsigned int nr)
@@ -166,7 +152,7 @@ static int smp_cell_cpu_bootable(unsigned int nr)
return 1;
}
static struct smp_ops_t bpa_iic_smp_ops = {
- .message_pass = smp_iic_message_pass,
+ .message_pass = iic_cause_IPI,
.probe = smp_iic_probe,
.kick_cpu = smp_cell_kick_cpu,
.setup_cpu = smp_cell_setup_cpu,
@@ -186,13 +172,12 @@ void __init smp_init_cell(void)
if (cpu_has_feature(CPU_FTR_SMT)) {
for_each_present_cpu(i) {
if (cpu_thread_in_core(i) == 0)
- cpu_set(i, of_spin_map);
+ cpumask_set_cpu(i, &of_spin_map);
}
- } else {
- of_spin_map = cpu_present_map;
- }
+ } else
+ cpumask_copy(&of_spin_map, cpu_present_mask);
- cpu_clear(boot_cpuid, of_spin_map);
+ cpumask_clear_cpu(boot_cpuid, &of_spin_map);
/* Non-lpar has additional take/give timebase */
if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
diff --git a/arch/powerpc/platforms/cell/spider-pci.c b/arch/powerpc/platforms/cell/spider-pci.c
index ca7731c0b59..f1f7878893f 100644
--- a/arch/powerpc/platforms/cell/spider-pci.c
+++ b/arch/powerpc/platforms/cell/spider-pci.c
@@ -27,8 +27,7 @@
#include <asm/ppc-pci.h>
#include <asm/pci-bridge.h>
-
-#include "io-workarounds.h"
+#include <asm/io-workarounds.h>
#define SPIDER_PCI_DISABLE_PREFETCH
diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c
index c5cf50e6b45..442c28c00f8 100644
--- a/arch/powerpc/platforms/cell/spider-pic.c
+++ b/arch/powerpc/platforms/cell/spider-pic.c
@@ -68,9 +68,9 @@ struct spider_pic {
};
static struct spider_pic spider_pics[SPIDER_CHIP_COUNT];
-static struct spider_pic *spider_virq_to_pic(unsigned int virq)
+static struct spider_pic *spider_irq_data_to_pic(struct irq_data *d)
{
- return irq_map[virq].host->host_data;
+ return irq_data_get_irq_chip_data(d);
}
static void __iomem *spider_get_irq_config(struct spider_pic *pic,
@@ -81,24 +81,24 @@ static void __iomem *spider_get_irq_config(struct spider_pic *pic,
static void spider_unmask_irq(struct irq_data *d)
{
- struct spider_pic *pic = spider_virq_to_pic(d->irq);
- void __iomem *cfg = spider_get_irq_config(pic, irq_map[d->irq].hwirq);
+ struct spider_pic *pic = spider_irq_data_to_pic(d);
+ void __iomem *cfg = spider_get_irq_config(pic, irqd_to_hwirq(d));
out_be32(cfg, in_be32(cfg) | 0x30000000u);
}
static void spider_mask_irq(struct irq_data *d)
{
- struct spider_pic *pic = spider_virq_to_pic(d->irq);
- void __iomem *cfg = spider_get_irq_config(pic, irq_map[d->irq].hwirq);
+ struct spider_pic *pic = spider_irq_data_to_pic(d);
+ void __iomem *cfg = spider_get_irq_config(pic, irqd_to_hwirq(d));
out_be32(cfg, in_be32(cfg) & ~0x30000000u);
}
static void spider_ack_irq(struct irq_data *d)
{
- struct spider_pic *pic = spider_virq_to_pic(d->irq);
- unsigned int src = irq_map[d->irq].hwirq;
+ struct spider_pic *pic = spider_irq_data_to_pic(d);
+ unsigned int src = irqd_to_hwirq(d);
/* Reset edge detection logic if necessary
*/
@@ -116,8 +116,8 @@ static void spider_ack_irq(struct irq_data *d)
static int spider_set_irq_type(struct irq_data *d, unsigned int type)
{
unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
- struct spider_pic *pic = spider_virq_to_pic(d->irq);
- unsigned int hw = irq_map[d->irq].hwirq;
+ struct spider_pic *pic = spider_irq_data_to_pic(d);
+ unsigned int hw = irqd_to_hwirq(d);
void __iomem *cfg = spider_get_irq_config(pic, hw);
u32 old_mask;
u32 ic;
@@ -171,6 +171,7 @@ static struct irq_chip spider_pic = {
static int spider_host_map(struct irq_host *h, unsigned int virq,
irq_hw_number_t hw)
{
+ irq_set_chip_data(virq, h->host_data);
irq_set_chip_and_handler(virq, &spider_pic, handle_level_irq);
/* Set default irq type */
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index acfaccea5f4..3675da73623 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -32,6 +32,7 @@
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/linux_logo.h>
+#include <linux/syscore_ops.h>
#include <asm/spu.h>
#include <asm/spu_priv1.h>
#include <asm/spu_csa.h>
@@ -521,18 +522,8 @@ void spu_init_channels(struct spu *spu)
}
EXPORT_SYMBOL_GPL(spu_init_channels);
-static int spu_shutdown(struct sys_device *sysdev)
-{
- struct spu *spu = container_of(sysdev, struct spu, sysdev);
-
- spu_free_irqs(spu);
- spu_destroy_spu(spu);
- return 0;
-}
-
static struct sysdev_class spu_sysdev_class = {
.name = "spu",
- .shutdown = spu_shutdown,
};
int spu_add_sysdev_attr(struct sysdev_attribute *attr)
@@ -797,6 +788,22 @@ static inline void crash_register_spus(struct list_head *list)
}
#endif
+static void spu_shutdown(void)
+{
+ struct spu *spu;
+
+ mutex_lock(&spu_full_list_mutex);
+ list_for_each_entry(spu, &spu_full_list, full_list) {
+ spu_free_irqs(spu);
+ spu_destroy_spu(spu);
+ }
+ mutex_unlock(&spu_full_list_mutex);
+}
+
+static struct syscore_ops spu_syscore_ops = {
+ .shutdown = spu_shutdown,
+};
+
static int __init init_spu_base(void)
{
int i, ret = 0;
@@ -830,6 +837,7 @@ static int __init init_spu_base(void)
crash_register_spus(&spu_full_list);
mutex_unlock(&spu_full_list_mutex);
spu_add_sysdev_attr(&attr_stat);
+ register_syscore_ops(&spu_syscore_ops);
spu_init_affinity();
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c
index 65203857b0c..32cb4e66d2c 100644
--- a/arch/powerpc/platforms/cell/spufs/sched.c
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
@@ -141,7 +141,7 @@ void __spu_update_sched_info(struct spu_context *ctx)
* runqueue. The context will be rescheduled on the proper node
* if it is timesliced or preempted.
*/
- ctx->cpus_allowed = current->cpus_allowed;
+ cpumask_copy(&ctx->cpus_allowed, tsk_cpus_allowed(current));
/* Save the current cpu id for spu interrupt routing. */
ctx->last_ran = raw_smp_processor_id();
diff --git a/arch/powerpc/platforms/chrp/smp.c b/arch/powerpc/platforms/chrp/smp.c
index 02cafecc90e..a800122e4dd 100644
--- a/arch/powerpc/platforms/chrp/smp.c
+++ b/arch/powerpc/platforms/chrp/smp.c
@@ -30,10 +30,12 @@
#include <asm/mpic.h>
#include <asm/rtas.h>
-static void __devinit smp_chrp_kick_cpu(int nr)
+static int __devinit smp_chrp_kick_cpu(int nr)
{
*(unsigned long *)KERNELBASE = nr;
asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory");
+
+ return 0;
}
static void __devinit smp_chrp_setup_cpu(int cpu_nr)
diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.c b/arch/powerpc/platforms/embedded6xx/flipper-pic.c
index 12aa62b6f22..f61a2dd96b9 100644
--- a/arch/powerpc/platforms/embedded6xx/flipper-pic.c
+++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.c
@@ -48,7 +48,7 @@
static void flipper_pic_mask_and_ack(struct irq_data *d)
{
- int irq = virq_to_hw(d->irq);
+ int irq = irqd_to_hwirq(d);
void __iomem *io_base = irq_data_get_irq_chip_data(d);
u32 mask = 1 << irq;
@@ -59,7 +59,7 @@ static void flipper_pic_mask_and_ack(struct irq_data *d)
static void flipper_pic_ack(struct irq_data *d)
{
- int irq = virq_to_hw(d->irq);
+ int irq = irqd_to_hwirq(d);
void __iomem *io_base = irq_data_get_irq_chip_data(d);
/* this is at least needed for RSW */
@@ -68,7 +68,7 @@ static void flipper_pic_ack(struct irq_data *d)
static void flipper_pic_mask(struct irq_data *d)
{
- int irq = virq_to_hw(d->irq);
+ int irq = irqd_to_hwirq(d);
void __iomem *io_base = irq_data_get_irq_chip_data(d);
clrbits32(io_base + FLIPPER_IMR, 1 << irq);
@@ -76,7 +76,7 @@ static void flipper_pic_mask(struct irq_data *d)
static void flipper_pic_unmask(struct irq_data *d)
{
- int irq = virq_to_hw(d->irq);
+ int irq = irqd_to_hwirq(d);
void __iomem *io_base = irq_data_get_irq_chip_data(d);
setbits32(io_base + FLIPPER_IMR, 1 << irq);
@@ -107,12 +107,6 @@ static int flipper_pic_map(struct irq_host *h, unsigned int virq,
return 0;
}
-static void flipper_pic_unmap(struct irq_host *h, unsigned int irq)
-{
- irq_set_chip_data(irq, NULL);
- irq_set_chip(irq, NULL);
-}
-
static int flipper_pic_match(struct irq_host *h, struct device_node *np)
{
return 1;
@@ -121,7 +115,6 @@ static int flipper_pic_match(struct irq_host *h, struct device_node *np)
static struct irq_host_ops flipper_irq_host_ops = {
.map = flipper_pic_map,
- .unmap = flipper_pic_unmap,
.match = flipper_pic_match,
};
diff --git a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
index 2bdddfc9d52..e4919170c6b 100644
--- a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
+++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
@@ -43,7 +43,7 @@
static void hlwd_pic_mask_and_ack(struct irq_data *d)
{
- int irq = virq_to_hw(d->irq);
+ int irq = irqd_to_hwirq(d);
void __iomem *io_base = irq_data_get_irq_chip_data(d);
u32 mask = 1 << irq;
@@ -53,7 +53,7 @@ static void hlwd_pic_mask_and_ack(struct irq_data *d)
static void hlwd_pic_ack(struct irq_data *d)
{
- int irq = virq_to_hw(d->irq);
+ int irq = irqd_to_hwirq(d);
void __iomem *io_base = irq_data_get_irq_chip_data(d);
out_be32(io_base + HW_BROADWAY_ICR, 1 << irq);
@@ -61,7 +61,7 @@ static void hlwd_pic_ack(struct irq_data *d)
static void hlwd_pic_mask(struct irq_data *d)
{
- int irq = virq_to_hw(d->irq);
+ int irq = irqd_to_hwirq(d);
void __iomem *io_base = irq_data_get_irq_chip_data(d);
clrbits32(io_base + HW_BROADWAY_IMR, 1 << irq);
@@ -69,7 +69,7 @@ static void hlwd_pic_mask(struct irq_data *d)
static void hlwd_pic_unmask(struct irq_data *d)
{
- int irq = virq_to_hw(d->irq);
+ int irq = irqd_to_hwirq(d);
void __iomem *io_base = irq_data_get_irq_chip_data(d);
setbits32(io_base + HW_BROADWAY_IMR, 1 << irq);
@@ -100,15 +100,8 @@ static int hlwd_pic_map(struct irq_host *h, unsigned int virq,
return 0;
}
-static void hlwd_pic_unmap(struct irq_host *h, unsigned int irq)
-{
- irq_set_chip_data(irq, NULL);
- irq_set_chip(irq, NULL);
-}
-
static struct irq_host_ops hlwd_irq_host_ops = {
.map = hlwd_pic_map,
- .unmap = hlwd_pic_unmap,
};
static unsigned int __hlwd_pic_get_irq(struct irq_host *h)
diff --git a/arch/powerpc/platforms/iseries/Kconfig b/arch/powerpc/platforms/iseries/Kconfig
index e5bc9f75d47..b57cda3a081 100644
--- a/arch/powerpc/platforms/iseries/Kconfig
+++ b/arch/powerpc/platforms/iseries/Kconfig
@@ -1,7 +1,9 @@
config PPC_ISERIES
bool "IBM Legacy iSeries"
depends on PPC64 && PPC_BOOK3S
- select PPC_INDIRECT_IO
+ select PPC_SMP_MUXED_IPI
+ select PPC_INDIRECT_PIO
+ select PPC_INDIRECT_MMIO
select PPC_PCI_CHOICE if EXPERT
menu "iSeries device drivers"
diff --git a/arch/powerpc/platforms/iseries/exception.S b/arch/powerpc/platforms/iseries/exception.S
index 32a56c6dfa7..29c02f36b32 100644
--- a/arch/powerpc/platforms/iseries/exception.S
+++ b/arch/powerpc/platforms/iseries/exception.S
@@ -31,6 +31,7 @@
#include <asm/thread_info.h>
#include <asm/ptrace.h>
#include <asm/cputable.h>
+#include <asm/mmu.h>
#include "exception.h"
@@ -60,29 +61,31 @@ system_reset_iSeries:
/* Spin on __secondary_hold_spinloop until it is updated by the boot cpu. */
/* In the UP case we'll yield() later, and we will not access the paca anyway */
#ifdef CONFIG_SMP
-1:
+iSeries_secondary_wait_paca:
HMT_LOW
LOAD_REG_ADDR(r23, __secondary_hold_spinloop)
ld r23,0(r23)
- sync
- LOAD_REG_ADDR(r3,current_set)
- sldi r28,r24,3 /* get current_set[cpu#] */
- ldx r3,r3,r28
- addi r1,r3,THREAD_SIZE
- subi r1,r1,STACK_FRAME_OVERHEAD
- cmpwi 0,r23,0 /* Keep poking the Hypervisor until */
- bne 2f /* we're released */
- /* Let the Hypervisor know we are alive */
+ cmpdi 0,r23,0
+ bne 2f /* go on when the master is ready */
+
+ /* Keep poking the Hypervisor until we're released */
/* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */
lis r3,0x8002
rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */
li r0,-1 /* r0=-1 indicates a Hypervisor call */
sc /* Invoke the hypervisor via a system call */
- b 1b
-#endif
+ b iSeries_secondary_wait_paca
2:
+ HMT_MEDIUM
+ sync
+
+ LOAD_REG_ADDR(r3, nr_cpu_ids) /* get number of pacas allocated */
+ lwz r3,0(r3) /* nr_cpus= or NR_CPUS can limit */
+ cmpld 0,r24,r3 /* is our cpu number allocated? */
+ bge iSeries_secondary_yield /* no, yield forever */
+
/* Load our paca now that it's been allocated */
LOAD_REG_ADDR(r13, paca)
ld r13,0(r13)
@@ -93,10 +96,24 @@ system_reset_iSeries:
ori r23,r23,MSR_RI
mtmsrd r23 /* RI on */
- HMT_LOW
-#ifdef CONFIG_SMP
+iSeries_secondary_smp_loop:
lbz r23,PACAPROCSTART(r13) /* Test if this processor
* should start */
+ cmpwi 0,r23,0
+ bne 3f /* go on when we are told */
+
+ HMT_LOW
+ /* Let the Hypervisor know we are alive */
+ /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */
+ lis r3,0x8002
+ rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */
+ li r0,-1 /* r0=-1 indicates a Hypervisor call */
+ sc /* Invoke the hypervisor via a system call */
+ mfspr r13,SPRN_SPRG_PACA /* Put r13 back ???? */
+ b iSeries_secondary_smp_loop /* wait for signal to start */
+
+3:
+ HMT_MEDIUM
sync
LOAD_REG_ADDR(r3,current_set)
sldi r28,r24,3 /* get current_set[cpu#] */
@@ -104,27 +121,22 @@ system_reset_iSeries:
addi r1,r3,THREAD_SIZE
subi r1,r1,STACK_FRAME_OVERHEAD
- cmpwi 0,r23,0
- beq iSeries_secondary_smp_loop /* Loop until told to go */
b __secondary_start /* Loop until told to go */
-iSeries_secondary_smp_loop:
- /* Let the Hypervisor know we are alive */
- /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */
- lis r3,0x8002
- rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */
-#else /* CONFIG_SMP */
+#endif /* CONFIG_SMP */
+
+iSeries_secondary_yield:
/* Yield the processor. This is required for non-SMP kernels
which are running on multi-threaded machines. */
+ HMT_LOW
lis r3,0x8000
rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */
addi r3,r3,18 /* r3 = 0x8000000000000012 which is "yield" */
li r4,0 /* "yield timed" */
li r5,-1 /* "yield forever" */
-#endif /* CONFIG_SMP */
li r0,-1 /* r0=-1 indicates a Hypervisor call */
sc /* Invoke the hypervisor via a system call */
mfspr r13,SPRN_SPRG_PACA /* Put r13 back ???? */
- b 2b /* If SMP not configured, secondaries
+ b iSeries_secondary_yield /* If SMP not configured, secondaries
* loop forever */
/*** ISeries-LPAR interrupt handlers ***/
@@ -157,7 +169,7 @@ BEGIN_FTR_SECTION
FTR_SECTION_ELSE
EXCEPTION_PROLOG_1(PACA_EXGEN)
EXCEPTION_PROLOG_ISERIES_1
-ALT_FTR_SECTION_END_IFCLR(CPU_FTR_SLB)
+ALT_MMU_FTR_SECTION_END_IFCLR(MMU_FTR_SLB)
b data_access_common
.do_stab_bolted_iSeries:
diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c
index 52a6889832c..b2103453eb0 100644
--- a/arch/powerpc/platforms/iseries/irq.c
+++ b/arch/powerpc/platforms/iseries/irq.c
@@ -42,7 +42,6 @@
#include "irq.h"
#include "pci.h"
#include "call_pci.h"
-#include "smp.h"
#ifdef CONFIG_PCI
@@ -171,7 +170,7 @@ static void iseries_enable_IRQ(struct irq_data *d)
{
u32 bus, dev_id, function, mask;
const u32 sub_bus = 0;
- unsigned int rirq = (unsigned int)irq_map[d->irq].hwirq;
+ unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
/* The IRQ has already been locked by the caller */
bus = REAL_IRQ_TO_BUS(rirq);
@@ -188,7 +187,7 @@ static unsigned int iseries_startup_IRQ(struct irq_data *d)
{
u32 bus, dev_id, function, mask;
const u32 sub_bus = 0;
- unsigned int rirq = (unsigned int)irq_map[d->irq].hwirq;
+ unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
bus = REAL_IRQ_TO_BUS(rirq);
function = REAL_IRQ_TO_FUNC(rirq);
@@ -234,7 +233,7 @@ static void iseries_shutdown_IRQ(struct irq_data *d)
{
u32 bus, dev_id, function, mask;
const u32 sub_bus = 0;
- unsigned int rirq = (unsigned int)irq_map[d->irq].hwirq;
+ unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
/* irq should be locked by the caller */
bus = REAL_IRQ_TO_BUS(rirq);
@@ -257,7 +256,7 @@ static void iseries_disable_IRQ(struct irq_data *d)
{
u32 bus, dev_id, function, mask;
const u32 sub_bus = 0;
- unsigned int rirq = (unsigned int)irq_map[d->irq].hwirq;
+ unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
/* The IRQ has already been locked by the caller */
bus = REAL_IRQ_TO_BUS(rirq);
@@ -271,7 +270,7 @@ static void iseries_disable_IRQ(struct irq_data *d)
static void iseries_end_IRQ(struct irq_data *d)
{
- unsigned int rirq = (unsigned int)irq_map[d->irq].hwirq;
+ unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
HvCallPci_eoi(REAL_IRQ_TO_BUS(rirq), REAL_IRQ_TO_SUBBUS(rirq),
(REAL_IRQ_TO_IDSEL(rirq) << 4) + REAL_IRQ_TO_FUNC(rirq));
@@ -316,7 +315,7 @@ unsigned int iSeries_get_irq(void)
#ifdef CONFIG_SMP
if (get_lppaca()->int_dword.fields.ipi_cnt) {
get_lppaca()->int_dword.fields.ipi_cnt = 0;
- iSeries_smp_message_recv();
+ smp_ipi_demux();
}
#endif /* CONFIG_SMP */
if (hvlpevent_is_pending())
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c
index 2946ae10fbf..c25a0815c26 100644
--- a/arch/powerpc/platforms/iseries/setup.c
+++ b/arch/powerpc/platforms/iseries/setup.c
@@ -249,7 +249,7 @@ static unsigned long iSeries_process_mainstore_vpd(struct MemoryBlock *mb_array,
unsigned long i;
unsigned long mem_blocks = 0;
- if (cpu_has_feature(CPU_FTR_SLB))
+ if (mmu_has_feature(MMU_FTR_SLB))
mem_blocks = iSeries_process_Regatta_mainstore_vpd(mb_array,
max_entries);
else
@@ -634,7 +634,7 @@ static int __init iseries_probe(void)
hpte_init_iSeries();
/* iSeries does not support 16M pages */
- cur_cpu_spec->cpu_features &= ~CPU_FTR_16M_PAGE;
+ cur_cpu_spec->mmu_features &= ~MMU_FTR_16M_PAGE;
return 1;
}
@@ -685,6 +685,11 @@ void * __init iSeries_early_setup(void)
powerpc_firmware_features |= FW_FEATURE_ISERIES;
powerpc_firmware_features |= FW_FEATURE_LPAR;
+#ifdef CONFIG_SMP
+ /* On iSeries we know we can never have more than 64 cpus */
+ nr_cpu_ids = max(nr_cpu_ids, 64);
+#endif
+
iSeries_fixup_klimit();
/*
diff --git a/arch/powerpc/platforms/iseries/smp.c b/arch/powerpc/platforms/iseries/smp.c
index 6c6029914db..e3265adde5d 100644
--- a/arch/powerpc/platforms/iseries/smp.c
+++ b/arch/powerpc/platforms/iseries/smp.c
@@ -42,57 +42,23 @@
#include <asm/cputable.h>
#include <asm/system.h>
-#include "smp.h"
-
-static unsigned long iSeries_smp_message[NR_CPUS];
-
-void iSeries_smp_message_recv(void)
-{
- int cpu = smp_processor_id();
- int msg;
-
- if (num_online_cpus() < 2)
- return;
-
- for (msg = 0; msg < 4; msg++)
- if (test_and_clear_bit(msg, &iSeries_smp_message[cpu]))
- smp_message_recv(msg);
-}
-
-static inline void smp_iSeries_do_message(int cpu, int msg)
+static void smp_iSeries_cause_ipi(int cpu, unsigned long data)
{
- set_bit(msg, &iSeries_smp_message[cpu]);
HvCall_sendIPI(&(paca[cpu]));
}
-static void smp_iSeries_message_pass(int target, int msg)
-{
- int i;
-
- if (target < NR_CPUS)
- smp_iSeries_do_message(target, msg);
- else {
- for_each_online_cpu(i) {
- if ((target == MSG_ALL_BUT_SELF) &&
- (i == smp_processor_id()))
- continue;
- smp_iSeries_do_message(i, msg);
- }
- }
-}
-
static int smp_iSeries_probe(void)
{
return cpumask_weight(cpu_possible_mask);
}
-static void smp_iSeries_kick_cpu(int nr)
+static int smp_iSeries_kick_cpu(int nr)
{
BUG_ON((nr < 0) || (nr >= NR_CPUS));
/* Verify that our partition has a processor nr */
if (lppaca_of(nr).dyn_proc_status >= 2)
- return;
+ return -ENOENT;
/* The processor is currently spinning, waiting
* for the cpu_start field to become non-zero
@@ -100,6 +66,8 @@ static void smp_iSeries_kick_cpu(int nr)
* continue on to secondary_start in iSeries_head.S
*/
paca[nr].cpu_start = 1;
+
+ return 0;
}
static void __devinit smp_iSeries_setup_cpu(int nr)
@@ -107,7 +75,8 @@ static void __devinit smp_iSeries_setup_cpu(int nr)
}
static struct smp_ops_t iSeries_smp_ops = {
- .message_pass = smp_iSeries_message_pass,
+ .message_pass = smp_muxed_ipi_message_pass,
+ .cause_ipi = smp_iSeries_cause_ipi,
.probe = smp_iSeries_probe,
.kick_cpu = smp_iSeries_kick_cpu,
.setup_cpu = smp_iSeries_setup_cpu,
diff --git a/arch/powerpc/platforms/iseries/smp.h b/arch/powerpc/platforms/iseries/smp.h
deleted file mode 100644
index d501f7de01e..00000000000
--- a/arch/powerpc/platforms/iseries/smp.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _PLATFORMS_ISERIES_SMP_H
-#define _PLATFORMS_ISERIES_SMP_H
-
-extern void iSeries_smp_message_recv(void);
-
-#endif /* _PLATFORMS_ISERIES_SMP_H */
diff --git a/arch/powerpc/platforms/powermac/Kconfig b/arch/powerpc/platforms/powermac/Kconfig
index 1e1a0873e1d..1afd10f6785 100644
--- a/arch/powerpc/platforms/powermac/Kconfig
+++ b/arch/powerpc/platforms/powermac/Kconfig
@@ -18,4 +18,13 @@ config PPC_PMAC64
select PPC_970_NAP
default y
-
+config PPC_PMAC32_PSURGE
+ bool "Support for powersurge upgrade cards" if EXPERT
+ depends on SMP && PPC32 && PPC_PMAC
+ select PPC_SMP_MUXED_IPI
+ default y
+ help
+ The powersurge cpu boards can be used in the generation
+ of powermacs that have a socket for an upgradeable cpu card,
+ including the 7500, 8500, 9500, 9600. Support exists for
+ both dual and quad socket upgrade cards.
diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c
index 023f24086a0..9089b042119 100644
--- a/arch/powerpc/platforms/powermac/pic.c
+++ b/arch/powerpc/platforms/powermac/pic.c
@@ -21,7 +21,7 @@
#include <linux/signal.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
-#include <linux/sysdev.h>
+#include <linux/syscore_ops.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#include <linux/module.h>
@@ -84,7 +84,7 @@ static void __pmac_retrigger(unsigned int irq_nr)
static void pmac_mask_and_ack_irq(struct irq_data *d)
{
- unsigned int src = irq_map[d->irq].hwirq;
+ unsigned int src = irqd_to_hwirq(d);
unsigned long bit = 1UL << (src & 0x1f);
int i = src >> 5;
unsigned long flags;
@@ -106,7 +106,7 @@ static void pmac_mask_and_ack_irq(struct irq_data *d)
static void pmac_ack_irq(struct irq_data *d)
{
- unsigned int src = irq_map[d->irq].hwirq;
+ unsigned int src = irqd_to_hwirq(d);
unsigned long bit = 1UL << (src & 0x1f);
int i = src >> 5;
unsigned long flags;
@@ -152,7 +152,7 @@ static void __pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
static unsigned int pmac_startup_irq(struct irq_data *d)
{
unsigned long flags;
- unsigned int src = irq_map[d->irq].hwirq;
+ unsigned int src = irqd_to_hwirq(d);
unsigned long bit = 1UL << (src & 0x1f);
int i = src >> 5;
@@ -169,7 +169,7 @@ static unsigned int pmac_startup_irq(struct irq_data *d)
static void pmac_mask_irq(struct irq_data *d)
{
unsigned long flags;
- unsigned int src = irq_map[d->irq].hwirq;
+ unsigned int src = irqd_to_hwirq(d);
raw_spin_lock_irqsave(&pmac_pic_lock, flags);
__clear_bit(src, ppc_cached_irq_mask);
@@ -180,7 +180,7 @@ static void pmac_mask_irq(struct irq_data *d)
static void pmac_unmask_irq(struct irq_data *d)
{
unsigned long flags;
- unsigned int src = irq_map[d->irq].hwirq;
+ unsigned int src = irqd_to_hwirq(d);
raw_spin_lock_irqsave(&pmac_pic_lock, flags);
__set_bit(src, ppc_cached_irq_mask);
@@ -193,7 +193,7 @@ static int pmac_retrigger(struct irq_data *d)
unsigned long flags;
raw_spin_lock_irqsave(&pmac_pic_lock, flags);
- __pmac_retrigger(irq_map[d->irq].hwirq);
+ __pmac_retrigger(irqd_to_hwirq(d));
raw_spin_unlock_irqrestore(&pmac_pic_lock, flags);
return 1;
}
@@ -239,15 +239,12 @@ static unsigned int pmac_pic_get_irq(void)
unsigned long bits = 0;
unsigned long flags;
-#ifdef CONFIG_SMP
- void psurge_smp_message_recv(void);
-
- /* IPI's are a hack on the powersurge -- Cort */
- if ( smp_processor_id() != 0 ) {
- psurge_smp_message_recv();
- return NO_IRQ_IGNORE; /* ignore, already handled */
+#ifdef CONFIG_PPC_PMAC32_PSURGE
+ /* IPI's are a hack on the powersurge -- Cort */
+ if (smp_processor_id() != 0) {
+ return psurge_secondary_virq;
}
-#endif /* CONFIG_SMP */
+#endif /* CONFIG_PPC_PMAC32_PSURGE */
raw_spin_lock_irqsave(&pmac_pic_lock, flags);
for (irq = max_real_irqs; (irq -= 32) >= 0; ) {
int i = irq >> 5;
@@ -677,7 +674,7 @@ not_found:
return viaint;
}
-static int pmacpic_suspend(struct sys_device *sysdev, pm_message_t state)
+static int pmacpic_suspend(void)
{
int viaint = pmacpic_find_viaint();
@@ -698,7 +695,7 @@ static int pmacpic_suspend(struct sys_device *sysdev, pm_message_t state)
return 0;
}
-static int pmacpic_resume(struct sys_device *sysdev)
+static void pmacpic_resume(void)
{
int i;
@@ -709,39 +706,19 @@ static int pmacpic_resume(struct sys_device *sysdev)
for (i = 0; i < max_real_irqs; ++i)
if (test_bit(i, sleep_save_mask))
pmac_unmask_irq(irq_get_irq_data(i));
-
- return 0;
}
-#endif /* CONFIG_PM && CONFIG_PPC32 */
-
-static struct sysdev_class pmacpic_sysclass = {
- .name = "pmac_pic",
-};
-
-static struct sys_device device_pmacpic = {
- .id = 0,
- .cls = &pmacpic_sysclass,
-};
-
-static struct sysdev_driver driver_pmacpic = {
-#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
- .suspend = &pmacpic_suspend,
- .resume = &pmacpic_resume,
-#endif /* CONFIG_PM && CONFIG_PPC32 */
+static struct syscore_ops pmacpic_syscore_ops = {
+ .suspend = pmacpic_suspend,
+ .resume = pmacpic_resume,
};
-static int __init init_pmacpic_sysfs(void)
+static int __init init_pmacpic_syscore(void)
{
-#ifdef CONFIG_PPC32
- if (max_irqs == 0)
- return -ENODEV;
-#endif
- printk(KERN_DEBUG "Registering pmac pic with sysfs...\n");
- sysdev_class_register(&pmacpic_sysclass);
- sysdev_register(&device_pmacpic);
- sysdev_driver_register(&pmacpic_sysclass, &driver_pmacpic);
+ register_syscore_ops(&pmacpic_syscore_ops);
return 0;
}
-machine_subsys_initcall(powermac, init_pmacpic_sysfs);
+machine_subsys_initcall(powermac, init_pmacpic_syscore);
+
+#endif /* CONFIG_PM && CONFIG_PPC32 */
diff --git a/arch/powerpc/platforms/powermac/pic.h b/arch/powerpc/platforms/powermac/pic.h
deleted file mode 100644
index d622a8345aa..00000000000
--- a/arch/powerpc/platforms/powermac/pic.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __PPC_PLATFORMS_PMAC_PIC_H
-#define __PPC_PLATFORMS_PMAC_PIC_H
-
-#include <linux/irq.h>
-
-extern struct irq_chip pmac_pic;
-
-extern void pmac_pic_init(void);
-extern int pmac_get_irq(void);
-
-#endif /* __PPC_PLATFORMS_PMAC_PIC_H */
diff --git a/arch/powerpc/platforms/powermac/pmac.h b/arch/powerpc/platforms/powermac/pmac.h
index 20468f49aec..8327cce2bdb 100644
--- a/arch/powerpc/platforms/powermac/pmac.h
+++ b/arch/powerpc/platforms/powermac/pmac.h
@@ -33,6 +33,7 @@ extern void pmac_setup_pci_dma(void);
extern void pmac_check_ht_link(void);
extern void pmac_setup_smp(void);
+extern int psurge_secondary_virq;
extern void low_cpu_die(void) __attribute__((noreturn));
extern int pmac_nvram_init(void);
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c
index a830c5e8065..db092d7c4c5 100644
--- a/arch/powerpc/platforms/powermac/smp.c
+++ b/arch/powerpc/platforms/powermac/smp.c
@@ -70,7 +70,7 @@ static void (*pmac_tb_freeze)(int freeze);
static u64 timebase;
static int tb_req;
-#ifdef CONFIG_PPC32
+#ifdef CONFIG_PPC_PMAC32_PSURGE
/*
* Powersurge (old powermac SMP) support.
@@ -124,6 +124,10 @@ static volatile u32 __iomem *psurge_start;
/* what sort of powersurge board we have */
static int psurge_type = PSURGE_NONE;
+/* irq for secondary cpus to report */
+static struct irq_host *psurge_host;
+int psurge_secondary_virq;
+
/*
* Set and clear IPIs for powersurge.
*/
@@ -156,51 +160,52 @@ static inline void psurge_clr_ipi(int cpu)
/*
* On powersurge (old SMP powermac architecture) we don't have
* separate IPIs for separate messages like openpic does. Instead
- * we have a bitmap for each processor, where a 1 bit means that
- * the corresponding message is pending for that processor.
- * Ideally each cpu's entry would be in a different cache line.
+ * use the generic demux helpers
* -- paulus.
*/
-static unsigned long psurge_smp_message[NR_CPUS];
-
-void psurge_smp_message_recv(void)
+static irqreturn_t psurge_ipi_intr(int irq, void *d)
{
- int cpu = smp_processor_id();
- int msg;
-
- /* clear interrupt */
- psurge_clr_ipi(cpu);
+ psurge_clr_ipi(smp_processor_id());
+ smp_ipi_demux();
- if (num_online_cpus() < 2)
- return;
+ return IRQ_HANDLED;
+}
- /* make sure there is a message there */
- for (msg = 0; msg < 4; msg++)
- if (test_and_clear_bit(msg, &psurge_smp_message[cpu]))
- smp_message_recv(msg);
+static void smp_psurge_cause_ipi(int cpu, unsigned long data)
+{
+ psurge_set_ipi(cpu);
}
-irqreturn_t psurge_primary_intr(int irq, void *d)
+static int psurge_host_map(struct irq_host *h, unsigned int virq,
+ irq_hw_number_t hw)
{
- psurge_smp_message_recv();
- return IRQ_HANDLED;
+ irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_percpu_irq);
+
+ return 0;
}
-static void smp_psurge_message_pass(int target, int msg)
+struct irq_host_ops psurge_host_ops = {
+ .map = psurge_host_map,
+};
+
+static int psurge_secondary_ipi_init(void)
{
- int i;
+ int rc = -ENOMEM;
- if (num_online_cpus() < 2)
- return;
+ psurge_host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0,
+ &psurge_host_ops, 0);
- for_each_online_cpu(i) {
- if (target == MSG_ALL
- || (target == MSG_ALL_BUT_SELF && i != smp_processor_id())
- || target == i) {
- set_bit(msg, &psurge_smp_message[i]);
- psurge_set_ipi(i);
- }
- }
+ if (psurge_host)
+ psurge_secondary_virq = irq_create_direct_mapping(psurge_host);
+
+ if (psurge_secondary_virq)
+ rc = request_irq(psurge_secondary_virq, psurge_ipi_intr,
+ IRQF_DISABLED|IRQF_PERCPU, "IPI", NULL);
+
+ if (rc)
+ pr_err("Failed to setup secondary cpu IPI\n");
+
+ return rc;
}
/*
@@ -311,6 +316,9 @@ static int __init smp_psurge_probe(void)
ncpus = 2;
}
+ if (psurge_secondary_ipi_init())
+ return 1;
+
psurge_start = ioremap(PSURGE_START, 4);
psurge_pri_intr = ioremap(PSURGE_PRI_INTR, 4);
@@ -329,7 +337,7 @@ static int __init smp_psurge_probe(void)
return ncpus;
}
-static void __init smp_psurge_kick_cpu(int nr)
+static int __init smp_psurge_kick_cpu(int nr)
{
unsigned long start = __pa(__secondary_start_pmac_0) + nr * 8;
unsigned long a, flags;
@@ -394,11 +402,13 @@ static void __init smp_psurge_kick_cpu(int nr)
psurge_set_ipi(1);
if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu - done", 0x354);
+
+ return 0;
}
static struct irqaction psurge_irqaction = {
- .handler = psurge_primary_intr,
- .flags = IRQF_DISABLED,
+ .handler = psurge_ipi_intr,
+ .flags = IRQF_DISABLED|IRQF_PERCPU,
.name = "primary IPI",
};
@@ -437,14 +447,15 @@ void __init smp_psurge_give_timebase(void)
/* PowerSurge-style Macs */
struct smp_ops_t psurge_smp_ops = {
- .message_pass = smp_psurge_message_pass,
+ .message_pass = smp_muxed_ipi_message_pass,
+ .cause_ipi = smp_psurge_cause_ipi,
.probe = smp_psurge_probe,
.kick_cpu = smp_psurge_kick_cpu,
.setup_cpu = smp_psurge_setup_cpu,
.give_timebase = smp_psurge_give_timebase,
.take_timebase = smp_psurge_take_timebase,
};
-#endif /* CONFIG_PPC32 - actually powersurge support */
+#endif /* CONFIG_PPC_PMAC32_PSURGE */
/*
* Core 99 and later support
@@ -791,14 +802,14 @@ static int __init smp_core99_probe(void)
return ncpus;
}
-static void __devinit smp_core99_kick_cpu(int nr)
+static int __devinit smp_core99_kick_cpu(int nr)
{
unsigned int save_vector;
unsigned long target, flags;
unsigned int *vector = (unsigned int *)(PAGE_OFFSET+0x100);
if (nr < 0 || nr > 3)
- return;
+ return -ENOENT;
if (ppc_md.progress)
ppc_md.progress("smp_core99_kick_cpu", 0x346);
@@ -830,6 +841,8 @@ static void __devinit smp_core99_kick_cpu(int nr)
local_irq_restore(flags);
if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347);
+
+ return 0;
}
static void __devinit smp_core99_setup_cpu(int cpu_nr)
@@ -842,6 +855,7 @@ static void __devinit smp_core99_setup_cpu(int cpu_nr)
mpic_setup_this_cpu();
}
+#ifdef CONFIG_PPC64
#ifdef CONFIG_HOTPLUG_CPU
static int smp_core99_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
@@ -879,7 +893,6 @@ static struct notifier_block __cpuinitdata smp_core99_cpu_nb = {
static void __init smp_core99_bringup_done(void)
{
-#ifdef CONFIG_PPC64
extern void g5_phy_disable_cpu1(void);
/* Close i2c bus if it was used for tb sync */
@@ -894,14 +907,14 @@ static void __init smp_core99_bringup_done(void)
set_cpu_present(1, false);
g5_phy_disable_cpu1();
}
-#endif /* CONFIG_PPC64 */
-
#ifdef CONFIG_HOTPLUG_CPU
register_cpu_notifier(&smp_core99_cpu_nb);
#endif
+
if (ppc_md.progress)
ppc_md.progress("smp_core99_bringup_done", 0x349);
}
+#endif /* CONFIG_PPC64 */
#ifdef CONFIG_HOTPLUG_CPU
@@ -975,7 +988,9 @@ static void pmac_cpu_die(void)
struct smp_ops_t core99_smp_ops = {
.message_pass = smp_mpic_message_pass,
.probe = smp_core99_probe,
+#ifdef CONFIG_PPC64
.bringup_done = smp_core99_bringup_done,
+#endif
.kick_cpu = smp_core99_kick_cpu,
.setup_cpu = smp_core99_setup_cpu,
.give_timebase = smp_core99_give_timebase,
@@ -1000,7 +1015,7 @@ void __init pmac_setup_smp(void)
of_node_put(np);
smp_ops = &core99_smp_ops;
}
-#ifdef CONFIG_PPC32
+#ifdef CONFIG_PPC_PMAC32_PSURGE
else {
/* We have to set bits in cpu_possible_mask here since the
* secondary CPU(s) aren't in the device tree. Various
@@ -1013,7 +1028,7 @@ void __init pmac_setup_smp(void)
set_cpu_possible(cpu, true);
smp_ops = &psurge_smp_ops;
}
-#endif /* CONFIG_PPC32 */
+#endif /* CONFIG_PPC_PMAC32_PSURGE */
#ifdef CONFIG_HOTPLUG_CPU
ppc_md.cpu_die = pmac_cpu_die;
diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c
index f2f6413b81d..600ed2c0ed5 100644
--- a/arch/powerpc/platforms/ps3/interrupt.c
+++ b/arch/powerpc/platforms/ps3/interrupt.c
@@ -197,7 +197,7 @@ static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
result = irq_set_chip_data(*virq, pd);
if (result) {
- pr_debug("%s:%d: set_irq_chip_data failed\n",
+ pr_debug("%s:%d: irq_set_chip_data failed\n",
__func__, __LINE__);
goto fail_set;
}
@@ -659,11 +659,6 @@ static void __maybe_unused _dump_mask(struct ps3_private *pd,
static void dump_bmp(struct ps3_private* pd) {};
#endif /* defined(DEBUG) */
-static void ps3_host_unmap(struct irq_host *h, unsigned int virq)
-{
- irq_set_chip_data(virq, NULL);
-}
-
static int ps3_host_map(struct irq_host *h, unsigned int virq,
irq_hw_number_t hwirq)
{
@@ -683,7 +678,6 @@ static int ps3_host_match(struct irq_host *h, struct device_node *np)
static struct irq_host_ops ps3_host_ops = {
.map = ps3_host_map,
- .unmap = ps3_host_unmap,
.match = ps3_host_match,
};
diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c
index 51ffde40af2..4c44794faac 100644
--- a/arch/powerpc/platforms/ps3/smp.c
+++ b/arch/powerpc/platforms/ps3/smp.c
@@ -39,7 +39,7 @@
#define MSG_COUNT 4
static DEFINE_PER_CPU(unsigned int [MSG_COUNT], ps3_ipi_virqs);
-static void do_message_pass(int target, int msg)
+static void ps3_smp_message_pass(int cpu, int msg)
{
int result;
unsigned int virq;
@@ -49,28 +49,12 @@ static void do_message_pass(int target, int msg)
return;
}
- virq = per_cpu(ps3_ipi_virqs, target)[msg];
+ virq = per_cpu(ps3_ipi_virqs, cpu)[msg];
result = ps3_send_event_locally(virq);
if (result)
DBG("%s:%d: ps3_send_event_locally(%d, %d) failed"
- " (%d)\n", __func__, __LINE__, target, msg, result);
-}
-
-static void ps3_smp_message_pass(int target, int msg)
-{
- int cpu;
-
- if (target < NR_CPUS)
- do_message_pass(target, msg);
- else if (target == MSG_ALL_BUT_SELF) {
- for_each_online_cpu(cpu)
- if (cpu != smp_processor_id())
- do_message_pass(cpu, msg);
- } else {
- for_each_online_cpu(cpu)
- do_message_pass(cpu, msg);
- }
+ " (%d)\n", __func__, __LINE__, cpu, msg, result);
}
static int ps3_smp_probe(void)
diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c
index 39a472e9e80..375a9f92158 100644
--- a/arch/powerpc/platforms/ps3/spu.c
+++ b/arch/powerpc/platforms/ps3/spu.c
@@ -197,7 +197,7 @@ static void spu_unmap(struct spu *spu)
* The current HV requires the spu shadow regs to be mapped with the
* PTE page protection bits set as read-only (PP=3). This implementation
* uses the low level __ioremap() to bypass the page protection settings
- * inforced by ioremap_flags() to get the needed PTE bits set for the
+ * inforced by ioremap_prot() to get the needed PTE bits set for the
* shadow regs.
*/
@@ -214,7 +214,7 @@ static int __init setup_areas(struct spu *spu)
goto fail_ioremap;
}
- spu->local_store = (__force void *)ioremap_flags(spu->local_store_phys,
+ spu->local_store = (__force void *)ioremap_prot(spu->local_store_phys,
LS_SIZE, _PAGE_NO_CACHE);
if (!spu->local_store) {
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 5b3da4b4ea7..71af4c5d6c0 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -3,7 +3,10 @@ config PPC_PSERIES
bool "IBM pSeries & new (POWER5-based) iSeries"
select MPIC
select PCI_MSI
- select XICS
+ select PPC_XICS
+ select PPC_ICP_NATIVE
+ select PPC_ICP_HV
+ select PPC_ICS_RTAS
select PPC_I8259
select PPC_RTAS
select PPC_RTAS_DAEMON
@@ -47,6 +50,24 @@ config SCANLOG
tristate "Scanlog dump interface"
depends on RTAS_PROC && PPC_PSERIES
+config IO_EVENT_IRQ
+ bool "IO Event Interrupt support"
+ depends on PPC_PSERIES
+ default y
+ help
+ Select this option, if you want to enable support for IO Event
+ interrupts. IO event interrupt is a mechanism provided by RTAS
+ to return information about hardware error and non-error events
+ which may need OS attention. RTAS returns events for multiple
+ event types and scopes. Device drivers can register their handlers
+ to receive events.
+
+ This option will only enable the IO event platform code. You
+ will still need to enable or compile the actual drivers
+ that use this infrastruture to handle IO event interrupts.
+
+ Say Y if you are unsure.
+
config LPARCFG
bool "LPAR Configuration Data"
depends on PPC_PSERIES || PPC_ISERIES
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index fc5237810ec..3556e402cbf 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -5,7 +5,6 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \
setup.o iommu.o event_sources.o ras.o \
firmware.o power.o dlpar.o mobility.o
obj-$(CONFIG_SMP) += smp.o
-obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_SCANLOG) += scanlog.o
obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o
obj-$(CONFIG_KEXEC) += kexec.o
@@ -22,6 +21,7 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o
obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o
obj-$(CONFIG_CMM) += cmm.o
obj-$(CONFIG_DTL) += dtl.o
+obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o
ifeq ($(CONFIG_PPC_PSERIES),y)
obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index c371bc06434..e9190073bb9 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -52,10 +52,10 @@ static u8 dtl_event_mask = 0x7;
/*
- * Size of per-cpu log buffers. Default is just under 16 pages worth.
+ * Size of per-cpu log buffers. Firmware requires that the buffer does
+ * not cross a 4k boundary.
*/
-static int dtl_buf_entries = (16 * 85);
-
+static int dtl_buf_entries = N_DISPATCH_LOG;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
struct dtl_ring {
@@ -151,7 +151,7 @@ static int dtl_start(struct dtl *dtl)
/* Register our dtl buffer with the hypervisor. The HV expects the
* buffer size to be passed in the second word of the buffer */
- ((u32 *)dtl->buf)[1] = dtl->buf_entries * sizeof(struct dtl_entry);
+ ((u32 *)dtl->buf)[1] = DISPATCH_LOG_BYTES;
hwcpu = get_hard_smp_processor_id(dtl->cpu);
addr = __pa(dtl->buf);
@@ -196,13 +196,15 @@ static int dtl_enable(struct dtl *dtl)
long int rc;
struct dtl_entry *buf = NULL;
+ if (!dtl_cache)
+ return -ENOMEM;
+
/* only allow one reader */
if (dtl->buf)
return -EBUSY;
n_entries = dtl_buf_entries;
- buf = kmalloc_node(n_entries * sizeof(struct dtl_entry),
- GFP_KERNEL, cpu_to_node(dtl->cpu));
+ buf = kmem_cache_alloc_node(dtl_cache, GFP_KERNEL, cpu_to_node(dtl->cpu));
if (!buf) {
printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
__func__, dtl->cpu);
@@ -223,7 +225,7 @@ static int dtl_enable(struct dtl *dtl)
spin_unlock(&dtl->lock);
if (rc)
- kfree(buf);
+ kmem_cache_free(dtl_cache, buf);
return rc;
}
@@ -231,7 +233,7 @@ static void dtl_disable(struct dtl *dtl)
{
spin_lock(&dtl->lock);
dtl_stop(dtl);
- kfree(dtl->buf);
+ kmem_cache_free(dtl_cache, dtl->buf);
dtl->buf = NULL;
dtl->buf_entries = 0;
spin_unlock(&dtl->lock);
@@ -365,7 +367,7 @@ static int dtl_init(void)
event_mask_file = debugfs_create_x8("dtl_event_mask", 0600,
dtl_dir, &dtl_event_mask);
- buf_entries_file = debugfs_create_u32("dtl_buf_entries", 0600,
+ buf_entries_file = debugfs_create_u32("dtl_buf_entries", 0400,
dtl_dir, &dtl_buf_entries);
if (!event_mask_file || !buf_entries_file) {
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 89649173d3a..46b55cf563e 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -93,6 +93,7 @@ static int ibm_slot_error_detail;
static int ibm_get_config_addr_info;
static int ibm_get_config_addr_info2;
static int ibm_configure_bridge;
+static int ibm_configure_pe;
int eeh_subsystem_enabled;
EXPORT_SYMBOL(eeh_subsystem_enabled);
@@ -261,6 +262,8 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity)
pci_regs_buf[0] = 0;
rtas_pci_enable(pdn, EEH_THAW_MMIO);
+ rtas_configure_bridge(pdn);
+ eeh_restore_bars(pdn);
loglen = gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN);
rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen);
@@ -448,6 +451,39 @@ void eeh_clear_slot (struct device_node *dn, int mode_flag)
raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
}
+void __eeh_set_pe_freset(struct device_node *parent, unsigned int *freset)
+{
+ struct device_node *dn;
+
+ for_each_child_of_node(parent, dn) {
+ if (PCI_DN(dn)) {
+
+ struct pci_dev *dev = PCI_DN(dn)->pcidev;
+
+ if (dev && dev->driver)
+ *freset |= dev->needs_freset;
+
+ __eeh_set_pe_freset(dn, freset);
+ }
+ }
+}
+
+void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset)
+{
+ struct pci_dev *dev;
+ dn = find_device_pe(dn);
+
+ /* Back up one, since config addrs might be shared */
+ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
+ dn = dn->parent;
+
+ dev = PCI_DN(dn)->pcidev;
+ if (dev)
+ *freset |= dev->needs_freset;
+
+ __eeh_set_pe_freset(dn, freset);
+}
+
/**
* eeh_dn_check_failure - check if all 1's data is due to EEH slot freeze
* @dn device node
@@ -692,15 +728,24 @@ rtas_pci_slot_reset(struct pci_dn *pdn, int state)
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
- rc = rtas_call(ibm_set_slot_reset,4,1, NULL,
+ rc = rtas_call(ibm_set_slot_reset, 4, 1, NULL,
config_addr,
BUID_HI(pdn->phb->buid),
BUID_LO(pdn->phb->buid),
state);
- if (rc)
- printk (KERN_WARNING "EEH: Unable to reset the failed slot,"
- " (%d) #RST=%d dn=%s\n",
- rc, state, pdn->node->full_name);
+
+ /* Fundamental-reset not supported on this PE, try hot-reset */
+ if (rc == -8 && state == 3) {
+ rc = rtas_call(ibm_set_slot_reset, 4, 1, NULL,
+ config_addr,
+ BUID_HI(pdn->phb->buid),
+ BUID_LO(pdn->phb->buid), 1);
+ if (rc)
+ printk(KERN_WARNING
+ "EEH: Unable to reset the failed slot,"
+ " #RST=%d dn=%s\n",
+ rc, pdn->node->full_name);
+ }
}
/**
@@ -736,18 +781,21 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
/**
* rtas_set_slot_reset -- assert the pci #RST line for 1/4 second
* @pdn: pci device node to be reset.
- *
- * Return 0 if success, else a non-zero value.
*/
static void __rtas_set_slot_reset(struct pci_dn *pdn)
{
- struct pci_dev *dev = pdn->pcidev;
+ unsigned int freset = 0;
- /* Determine type of EEH reset required by device,
- * default hot reset or fundamental reset
- */
- if (dev && dev->needs_freset)
+ /* Determine type of EEH reset required for
+ * Partitionable Endpoint, a hot-reset (1)
+ * or a fundamental reset (3).
+ * A fundamental reset required by any device under
+ * Partitionable Endpoint trumps hot-reset.
+ */
+ eeh_set_pe_freset(pdn->node, &freset);
+
+ if (freset)
rtas_pci_slot_reset(pdn, 3);
else
rtas_pci_slot_reset(pdn, 1);
@@ -895,13 +943,20 @@ rtas_configure_bridge(struct pci_dn *pdn)
{
int config_addr;
int rc;
+ int token;
/* Use PE configuration address, if present */
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
- rc = rtas_call(ibm_configure_bridge,3,1, NULL,
+ /* Use new configure-pe function, if supported */
+ if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE)
+ token = ibm_configure_pe;
+ else
+ token = ibm_configure_bridge;
+
+ rc = rtas_call(token, 3, 1, NULL,
config_addr,
BUID_HI(pdn->phb->buid),
BUID_LO(pdn->phb->buid));
@@ -1077,6 +1132,7 @@ void __init eeh_init(void)
ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info");
ibm_get_config_addr_info2 = rtas_token("ibm,get-config-addr-info2");
ibm_configure_bridge = rtas_token ("ibm,configure-bridge");
+ ibm_configure_pe = rtas_token("ibm,configure-pe");
if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE)
return;
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index b8d70f5d9aa..1b6cb10589e 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -328,7 +328,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
struct pci_bus *frozen_bus;
int rc = 0;
enum pci_ers_result result = PCI_ERS_RESULT_NONE;
- const char *location, *pci_str, *drv_str;
+ const char *location, *pci_str, *drv_str, *bus_pci_str, *bus_drv_str;
frozen_dn = find_device_pe(event->dn);
if (!frozen_dn) {
@@ -364,13 +364,8 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
frozen_pdn = PCI_DN(frozen_dn);
frozen_pdn->eeh_freeze_count++;
- if (frozen_pdn->pcidev) {
- pci_str = pci_name (frozen_pdn->pcidev);
- drv_str = pcid_name (frozen_pdn->pcidev);
- } else {
- pci_str = eeh_pci_name(event->dev);
- drv_str = pcid_name (event->dev);
- }
+ pci_str = eeh_pci_name(event->dev);
+ drv_str = pcid_name(event->dev);
if (frozen_pdn->eeh_freeze_count > EEH_MAX_ALLOWED_FREEZES)
goto excess_failures;
@@ -378,8 +373,17 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
printk(KERN_WARNING
"EEH: This PCI device has failed %d times in the last hour:\n",
frozen_pdn->eeh_freeze_count);
+
+ if (frozen_pdn->pcidev) {
+ bus_pci_str = pci_name(frozen_pdn->pcidev);
+ bus_drv_str = pcid_name(frozen_pdn->pcidev);
+ printk(KERN_WARNING
+ "EEH: Bus location=%s driver=%s pci addr=%s\n",
+ location, bus_drv_str, bus_pci_str);
+ }
+
printk(KERN_WARNING
- "EEH: location=%s driver=%s pci addr=%s\n",
+ "EEH: Device location=%s driver=%s pci addr=%s\n",
location, drv_str, pci_str);
/* Walk the various device drivers attached to this slot through
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index ef8c45489e2..46f13a3c5d0 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -19,6 +19,7 @@
*/
#include <linux/kernel.h>
+#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/cpu.h>
#include <asm/system.h>
@@ -28,7 +29,7 @@
#include <asm/machdep.h>
#include <asm/vdso_datapage.h>
#include <asm/pSeries_reconfig.h>
-#include "xics.h"
+#include <asm/xics.h>
#include "plpar_wrappers.h"
#include "offline_states.h"
@@ -280,7 +281,7 @@ static int pseries_add_processor(struct device_node *np)
}
for_each_cpu(cpu, tmp) {
- BUG_ON(cpumask_test_cpu(cpu, cpu_present_mask));
+ BUG_ON(cpu_present(cpu));
set_cpu_present(cpu, true);
set_hard_smp_processor_id(cpu, *intserv++);
}
diff --git a/arch/powerpc/platforms/pseries/io_event_irq.c b/arch/powerpc/platforms/pseries/io_event_irq.c
new file mode 100644
index 00000000000..c829e6067d5
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/io_event_irq.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2010 2011 Mark Nelson and Tseng-Hui (Frank) Lin, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+
+#include <asm/machdep.h>
+#include <asm/rtas.h>
+#include <asm/irq.h>
+#include <asm/io_event_irq.h>
+
+#include "pseries.h"
+
+/*
+ * IO event interrupt is a mechanism provided by RTAS to return
+ * information about hardware error and non-error events. Device
+ * drivers can register their event handlers to receive events.
+ * Device drivers are expected to use atomic_notifier_chain_register()
+ * and atomic_notifier_chain_unregister() to register and unregister
+ * their event handlers. Since multiple IO event types and scopes
+ * share an IO event interrupt, the event handlers are called one
+ * by one until the IO event is claimed by one of the handlers.
+ * The event handlers are expected to return NOTIFY_OK if the
+ * event is handled by the event handler or NOTIFY_DONE if the
+ * event does not belong to the handler.
+ *
+ * Usage:
+ *
+ * Notifier function:
+ * #include <asm/io_event_irq.h>
+ * int event_handler(struct notifier_block *nb, unsigned long val, void *data) {
+ * p = (struct pseries_io_event_sect_data *) data;
+ * if (! is_my_event(p->scope, p->event_type)) return NOTIFY_DONE;
+ * :
+ * :
+ * return NOTIFY_OK;
+ * }
+ * struct notifier_block event_nb = {
+ * .notifier_call = event_handler,
+ * }
+ *
+ * Registration:
+ * atomic_notifier_chain_register(&pseries_ioei_notifier_list, &event_nb);
+ *
+ * Unregistration:
+ * atomic_notifier_chain_unregister(&pseries_ioei_notifier_list, &event_nb);
+ */
+
+ATOMIC_NOTIFIER_HEAD(pseries_ioei_notifier_list);
+EXPORT_SYMBOL_GPL(pseries_ioei_notifier_list);
+
+static int ioei_check_exception_token;
+
+/* pSeries event log format */
+
+/* Two bytes ASCII section IDs */
+#define PSERIES_ELOG_SECT_ID_PRIV_HDR (('P' << 8) | 'H')
+#define PSERIES_ELOG_SECT_ID_USER_HDR (('U' << 8) | 'H')
+#define PSERIES_ELOG_SECT_ID_PRIMARY_SRC (('P' << 8) | 'S')
+#define PSERIES_ELOG_SECT_ID_EXTENDED_UH (('E' << 8) | 'H')
+#define PSERIES_ELOG_SECT_ID_FAILING_MTMS (('M' << 8) | 'T')
+#define PSERIES_ELOG_SECT_ID_SECONDARY_SRC (('S' << 8) | 'S')
+#define PSERIES_ELOG_SECT_ID_DUMP_LOCATOR (('D' << 8) | 'H')
+#define PSERIES_ELOG_SECT_ID_FW_ERROR (('S' << 8) | 'W')
+#define PSERIES_ELOG_SECT_ID_IMPACT_PART_ID (('L' << 8) | 'P')
+#define PSERIES_ELOG_SECT_ID_LOGIC_RESOURCE_ID (('L' << 8) | 'R')
+#define PSERIES_ELOG_SECT_ID_HMC_ID (('H' << 8) | 'M')
+#define PSERIES_ELOG_SECT_ID_EPOW (('E' << 8) | 'P')
+#define PSERIES_ELOG_SECT_ID_IO_EVENT (('I' << 8) | 'E')
+#define PSERIES_ELOG_SECT_ID_MANUFACT_INFO (('M' << 8) | 'I')
+#define PSERIES_ELOG_SECT_ID_CALL_HOME (('C' << 8) | 'H')
+#define PSERIES_ELOG_SECT_ID_USER_DEF (('U' << 8) | 'D')
+
+/* Vendor specific Platform Event Log Format, Version 6, section header */
+struct pseries_elog_section {
+ uint16_t id; /* 0x00 2-byte ASCII section ID */
+ uint16_t length; /* 0x02 Section length in bytes */
+ uint8_t version; /* 0x04 Section version */
+ uint8_t subtype; /* 0x05 Section subtype */
+ uint16_t creator_component; /* 0x06 Creator component ID */
+ uint8_t data[]; /* 0x08 Start of section data */
+};
+
+static char ioei_rtas_buf[RTAS_DATA_BUF_SIZE] __cacheline_aligned;
+
+/**
+ * Find data portion of a specific section in RTAS extended event log.
+ * @elog: RTAS error/event log.
+ * @sect_id: secsion ID.
+ *
+ * Return:
+ * pointer to the section data of the specified section
+ * NULL if not found
+ */
+static struct pseries_elog_section *find_xelog_section(struct rtas_error_log *elog,
+ uint16_t sect_id)
+{
+ struct rtas_ext_event_log_v6 *xelog =
+ (struct rtas_ext_event_log_v6 *) elog->buffer;
+ struct pseries_elog_section *sect;
+ unsigned char *p, *log_end;
+
+ /* Check that we understand the format */
+ if (elog->extended_log_length < sizeof(struct rtas_ext_event_log_v6) ||
+ xelog->log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG ||
+ xelog->company_id != RTAS_V6EXT_COMPANY_ID_IBM)
+ return NULL;
+
+ log_end = elog->buffer + elog->extended_log_length;
+ p = xelog->vendor_log;
+ while (p < log_end) {
+ sect = (struct pseries_elog_section *)p;
+ if (sect->id == sect_id)
+ return sect;
+ p += sect->length;
+ }
+ return NULL;
+}
+
+/**
+ * Find the data portion of an IO Event section from event log.
+ * @elog: RTAS error/event log.
+ *
+ * Return:
+ * pointer to a valid IO event section data. NULL if not found.
+ */
+static struct pseries_io_event * ioei_find_event(struct rtas_error_log *elog)
+{
+ struct pseries_elog_section *sect;
+
+ /* We should only ever get called for io-event interrupts, but if
+ * we do get called for another type then something went wrong so
+ * make some noise about it.
+ * RTAS_TYPE_IO only exists in extended event log version 6 or later.
+ * No need to check event log version.
+ */
+ if (unlikely(elog->type != RTAS_TYPE_IO)) {
+ printk_once(KERN_WARNING "io_event_irq: Unexpected event type %d",
+ elog->type);
+ return NULL;
+ }
+
+ sect = find_xelog_section(elog, PSERIES_ELOG_SECT_ID_IO_EVENT);
+ if (unlikely(!sect)) {
+ printk_once(KERN_WARNING "io_event_irq: RTAS extended event "
+ "log does not contain an IO Event section. "
+ "Could be a bug in system firmware!\n");
+ return NULL;
+ }
+ return (struct pseries_io_event *) &sect->data;
+}
+
+/*
+ * PAPR:
+ * - check-exception returns the first found error or event and clear that
+ * error or event so it is reported once.
+ * - Each interrupt returns one event. If a plateform chooses to report
+ * multiple events through a single interrupt, it must ensure that the
+ * interrupt remains asserted until check-exception has been used to
+ * process all out-standing events for that interrupt.
+ *
+ * Implementation notes:
+ * - Events must be processed in the order they are returned. Hence,
+ * sequential in nature.
+ * - The owner of an event is determined by combinations of scope,
+ * event type, and sub-type. There is no easy way to pre-sort clients
+ * by scope or event type alone. For example, Torrent ISR route change
+ * event is reported with scope 0x00 (Not Applicatable) rather than
+ * 0x3B (Torrent-hub). It is better to let the clients to identify
+ * who owns the the event.
+ */
+
+static irqreturn_t ioei_interrupt(int irq, void *dev_id)
+{
+ struct pseries_io_event *event;
+ int rtas_rc;
+
+ for (;;) {
+ rtas_rc = rtas_call(ioei_check_exception_token, 6, 1, NULL,
+ RTAS_VECTOR_EXTERNAL_INTERRUPT,
+ virq_to_hw(irq),
+ RTAS_IO_EVENTS, 1 /* Time Critical */,
+ __pa(ioei_rtas_buf),
+ RTAS_DATA_BUF_SIZE);
+ if (rtas_rc != 0)
+ break;
+
+ event = ioei_find_event((struct rtas_error_log *)ioei_rtas_buf);
+ if (!event)
+ continue;
+
+ atomic_notifier_call_chain(&pseries_ioei_notifier_list,
+ 0, event);
+ }
+ return IRQ_HANDLED;
+}
+
+static int __init ioei_init(void)
+{
+ struct device_node *np;
+
+ ioei_check_exception_token = rtas_token("check-exception");
+ if (ioei_check_exception_token == RTAS_UNKNOWN_SERVICE) {
+ pr_warning("IO Event IRQ not supported on this system !\n");
+ return -ENODEV;
+ }
+ np = of_find_node_by_path("/event-sources/ibm,io-events");
+ if (np) {
+ request_event_sources_irqs(np, ioei_interrupt, "IO_EVENT");
+ of_node_put(np);
+ } else {
+ pr_err("io_event_irq: No ibm,io-events on system! "
+ "IO Event interrupt disabled.\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+machine_subsys_initcall(pseries, ioei_init);
+
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 6d5412a18b2..01faab9456c 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -659,15 +659,18 @@ static void remove_ddw(struct device_node *np)
{
struct dynamic_dma_window_prop *dwp;
struct property *win64;
- const u32 *ddr_avail;
+ const u32 *ddw_avail;
u64 liobn;
int len, ret;
- ddr_avail = of_get_property(np, "ibm,ddw-applicable", &len);
+ ddw_avail = of_get_property(np, "ibm,ddw-applicable", &len);
win64 = of_find_property(np, DIRECT64_PROPNAME, NULL);
- if (!win64 || !ddr_avail || len < 3 * sizeof(u32))
+ if (!win64)
return;
+ if (!ddw_avail || len < 3 * sizeof(u32) || win64->length < sizeof(*dwp))
+ goto delprop;
+
dwp = win64->value;
liobn = (u64)be32_to_cpu(dwp->liobn);
@@ -681,28 +684,29 @@ static void remove_ddw(struct device_node *np)
pr_debug("%s successfully cleared tces in window.\n",
np->full_name);
- ret = rtas_call(ddr_avail[2], 1, 1, NULL, liobn);
+ ret = rtas_call(ddw_avail[2], 1, 1, NULL, liobn);
if (ret)
pr_warning("%s: failed to remove direct window: rtas returned "
"%d to ibm,remove-pe-dma-window(%x) %llx\n",
- np->full_name, ret, ddr_avail[2], liobn);
+ np->full_name, ret, ddw_avail[2], liobn);
else
pr_debug("%s: successfully removed direct window: rtas returned "
"%d to ibm,remove-pe-dma-window(%x) %llx\n",
- np->full_name, ret, ddr_avail[2], liobn);
-}
+ np->full_name, ret, ddw_avail[2], liobn);
+delprop:
+ ret = prom_remove_property(np, win64);
+ if (ret)
+ pr_warning("%s: failed to remove direct window property: %d\n",
+ np->full_name, ret);
+}
-static int dupe_ddw_if_already_created(struct pci_dev *dev, struct device_node *pdn)
+static u64 find_existing_ddw(struct device_node *pdn)
{
- struct device_node *dn;
- struct pci_dn *pcidn;
struct direct_window *window;
const struct dynamic_dma_window_prop *direct64;
u64 dma_addr = 0;
- dn = pci_device_to_OF_node(dev);
- pcidn = PCI_DN(dn);
spin_lock(&direct_window_list_lock);
/* check if we already created a window and dupe that config if so */
list_for_each_entry(window, &direct_window_list, list) {
@@ -717,36 +721,40 @@ static int dupe_ddw_if_already_created(struct pci_dev *dev, struct device_node *
return dma_addr;
}
-static u64 dupe_ddw_if_kexec(struct pci_dev *dev, struct device_node *pdn)
+static int find_existing_ddw_windows(void)
{
- struct device_node *dn;
- struct pci_dn *pcidn;
int len;
+ struct device_node *pdn;
struct direct_window *window;
const struct dynamic_dma_window_prop *direct64;
- u64 dma_addr = 0;
- dn = pci_device_to_OF_node(dev);
- pcidn = PCI_DN(dn);
- direct64 = of_get_property(pdn, DIRECT64_PROPNAME, &len);
- if (direct64) {
+ if (!firmware_has_feature(FW_FEATURE_LPAR))
+ return 0;
+
+ for_each_node_with_property(pdn, DIRECT64_PROPNAME) {
+ direct64 = of_get_property(pdn, DIRECT64_PROPNAME, &len);
+ if (!direct64)
+ continue;
+
window = kzalloc(sizeof(*window), GFP_KERNEL);
- if (!window) {
+ if (!window || len < sizeof(struct dynamic_dma_window_prop)) {
+ kfree(window);
remove_ddw(pdn);
- } else {
- window->device = pdn;
- window->prop = direct64;
- spin_lock(&direct_window_list_lock);
- list_add(&window->list, &direct_window_list);
- spin_unlock(&direct_window_list_lock);
- dma_addr = direct64->dma_base;
+ continue;
}
+
+ window->device = pdn;
+ window->prop = direct64;
+ spin_lock(&direct_window_list_lock);
+ list_add(&window->list, &direct_window_list);
+ spin_unlock(&direct_window_list_lock);
}
- return dma_addr;
+ return 0;
}
+machine_arch_initcall(pseries, find_existing_ddw_windows);
-static int query_ddw(struct pci_dev *dev, const u32 *ddr_avail,
+static int query_ddw(struct pci_dev *dev, const u32 *ddw_avail,
struct ddw_query_response *query)
{
struct device_node *dn;
@@ -767,15 +775,15 @@ static int query_ddw(struct pci_dev *dev, const u32 *ddr_avail,
if (pcidn->eeh_pe_config_addr)
cfg_addr = pcidn->eeh_pe_config_addr;
buid = pcidn->phb->buid;
- ret = rtas_call(ddr_avail[0], 3, 5, (u32 *)query,
+ ret = rtas_call(ddw_avail[0], 3, 5, (u32 *)query,
cfg_addr, BUID_HI(buid), BUID_LO(buid));
dev_info(&dev->dev, "ibm,query-pe-dma-windows(%x) %x %x %x"
- " returned %d\n", ddr_avail[0], cfg_addr, BUID_HI(buid),
+ " returned %d\n", ddw_avail[0], cfg_addr, BUID_HI(buid),
BUID_LO(buid), ret);
return ret;
}
-static int create_ddw(struct pci_dev *dev, const u32 *ddr_avail,
+static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail,
struct ddw_create_response *create, int page_shift,
int window_shift)
{
@@ -800,12 +808,12 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddr_avail,
do {
/* extra outputs are LIOBN and dma-addr (hi, lo) */
- ret = rtas_call(ddr_avail[1], 5, 4, (u32 *)create, cfg_addr,
+ ret = rtas_call(ddw_avail[1], 5, 4, (u32 *)create, cfg_addr,
BUID_HI(buid), BUID_LO(buid), page_shift, window_shift);
} while (rtas_busy_delay(ret));
dev_info(&dev->dev,
"ibm,create-pe-dma-window(%x) %x %x %x %x %x returned %d "
- "(liobn = 0x%x starting addr = %x %x)\n", ddr_avail[1],
+ "(liobn = 0x%x starting addr = %x %x)\n", ddw_avail[1],
cfg_addr, BUID_HI(buid), BUID_LO(buid), page_shift,
window_shift, ret, create->liobn, create->addr_hi, create->addr_lo);
@@ -831,18 +839,14 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
int page_shift;
u64 dma_addr, max_addr;
struct device_node *dn;
- const u32 *uninitialized_var(ddr_avail);
+ const u32 *uninitialized_var(ddw_avail);
struct direct_window *window;
- struct property *uninitialized_var(win64);
+ struct property *win64;
struct dynamic_dma_window_prop *ddwprop;
mutex_lock(&direct_window_init_mutex);
- dma_addr = dupe_ddw_if_already_created(dev, pdn);
- if (dma_addr != 0)
- goto out_unlock;
-
- dma_addr = dupe_ddw_if_kexec(dev, pdn);
+ dma_addr = find_existing_ddw(pdn);
if (dma_addr != 0)
goto out_unlock;
@@ -854,8 +858,8 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
* for the given node in that order.
* the property is actually in the parent, not the PE
*/
- ddr_avail = of_get_property(pdn, "ibm,ddw-applicable", &len);
- if (!ddr_avail || len < 3 * sizeof(u32))
+ ddw_avail = of_get_property(pdn, "ibm,ddw-applicable", &len);
+ if (!ddw_avail || len < 3 * sizeof(u32))
goto out_unlock;
/*
@@ -865,7 +869,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
* of page sizes: supported and supported for migrate-dma.
*/
dn = pci_device_to_OF_node(dev);
- ret = query_ddw(dev, ddr_avail, &query);
+ ret = query_ddw(dev, ddw_avail, &query);
if (ret != 0)
goto out_unlock;
@@ -907,13 +911,14 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
}
win64->name = kstrdup(DIRECT64_PROPNAME, GFP_KERNEL);
win64->value = ddwprop = kmalloc(sizeof(*ddwprop), GFP_KERNEL);
+ win64->length = sizeof(*ddwprop);
if (!win64->name || !win64->value) {
dev_info(&dev->dev,
"couldn't allocate property name and value\n");
goto out_free_prop;
}
- ret = create_ddw(dev, ddr_avail, &create, page_shift, len);
+ ret = create_ddw(dev, ddw_avail, &create, page_shift, len);
if (ret != 0)
goto out_free_prop;
@@ -1021,13 +1026,16 @@ static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)
const void *dma_window = NULL;
u64 dma_offset;
- if (!dev->dma_mask || !dma_supported(dev, dma_mask))
+ if (!dev->dma_mask)
return -EIO;
+ if (!dev_is_pci(dev))
+ goto check_mask;
+
+ pdev = to_pci_dev(dev);
+
/* only attempt to use a new window if 64-bit DMA is requested */
if (!disable_ddw && dma_mask == DMA_BIT_MASK(64)) {
- pdev = to_pci_dev(dev);
-
dn = pci_device_to_OF_node(pdev);
dev_dbg(dev, "node is %s\n", dn->full_name);
@@ -1054,12 +1062,17 @@ static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)
}
}
- /* fall-through to iommu ops */
- if (!ddw_enabled) {
- dev_info(dev, "Using 32-bit DMA via iommu\n");
+ /* fall back on iommu ops, restore table pointer with ops */
+ if (!ddw_enabled && get_dma_ops(dev) != &dma_iommu_ops) {
+ dev_info(dev, "Restoring 32-bit DMA via iommu\n");
set_dma_ops(dev, &dma_iommu_ops);
+ pci_dma_dev_setup_pSeriesLP(pdev);
}
+check_mask:
+ if (!dma_supported(dev, dma_mask))
+ return -EIO;
+
*dev->dma_mask = dma_mask;
return 0;
}
diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c
index 77d38a5e2ff..54cf3a4aa16 100644
--- a/arch/powerpc/platforms/pseries/kexec.c
+++ b/arch/powerpc/platforms/pseries/kexec.c
@@ -7,15 +7,18 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+
#include <asm/machdep.h>
#include <asm/page.h>
#include <asm/firmware.h>
#include <asm/kexec.h>
#include <asm/mpic.h>
+#include <asm/xics.h>
#include <asm/smp.h>
#include "pseries.h"
-#include "xics.h"
#include "plpar_wrappers.h"
static void pseries_kexec_cpu_down(int crash_shutdown, int secondary)
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index ca5d5898d32..39e6e0a7b2f 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -329,6 +329,8 @@ static long pSeries_lpar_hpte_insert(unsigned long hpte_group,
/* Make pHyp happy */
if ((rflags & _PAGE_NO_CACHE) & !(rflags & _PAGE_WRITETHRU))
hpte_r &= ~_PAGE_COHERENT;
+ if (firmware_has_feature(FW_FEATURE_XCMO) && !(hpte_r & HPTE_R_N))
+ flags |= H_COALESCE_CAND;
lpar_rc = plpar_pte_enter(flags, hpte_group, hpte_v, hpte_r, &slot);
if (unlikely(lpar_rc == H_PTEG_FULL)) {
@@ -573,7 +575,7 @@ static void pSeries_lpar_flush_hash_range(unsigned long number, int local)
unsigned long i, pix, rc;
unsigned long flags = 0;
struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
- int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE);
+ int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
unsigned long param[9];
unsigned long va;
unsigned long hash, index, shift, hidx, slot;
@@ -771,3 +773,47 @@ out:
local_irq_restore(flags);
}
#endif
+
+/**
+ * h_get_mpp
+ * H_GET_MPP hcall returns info in 7 parms
+ */
+int h_get_mpp(struct hvcall_mpp_data *mpp_data)
+{
+ int rc;
+ unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
+
+ rc = plpar_hcall9(H_GET_MPP, retbuf);
+
+ mpp_data->entitled_mem = retbuf[0];
+ mpp_data->mapped_mem = retbuf[1];
+
+ mpp_data->group_num = (retbuf[2] >> 2 * 8) & 0xffff;
+ mpp_data->pool_num = retbuf[2] & 0xffff;
+
+ mpp_data->mem_weight = (retbuf[3] >> 7 * 8) & 0xff;
+ mpp_data->unallocated_mem_weight = (retbuf[3] >> 6 * 8) & 0xff;
+ mpp_data->unallocated_entitlement = retbuf[3] & 0xffffffffffff;
+
+ mpp_data->pool_size = retbuf[4];
+ mpp_data->loan_request = retbuf[5];
+ mpp_data->backing_mem = retbuf[6];
+
+ return rc;
+}
+EXPORT_SYMBOL(h_get_mpp);
+
+int h_get_mpp_x(struct hvcall_mpp_x_data *mpp_x_data)
+{
+ int rc;
+ unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = { 0 };
+
+ rc = plpar_hcall9(H_GET_MPP_X, retbuf);
+
+ mpp_x_data->coalesced_bytes = retbuf[0];
+ mpp_x_data->pool_coalesced_bytes = retbuf[1];
+ mpp_x_data->pool_purr_cycles = retbuf[2];
+ mpp_x_data->pool_spurr_cycles = retbuf[3];
+
+ return rc;
+}
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h
index d9801117124..4bf21207d7d 100644
--- a/arch/powerpc/platforms/pseries/plpar_wrappers.h
+++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h
@@ -270,31 +270,4 @@ static inline long plpar_put_term_char(unsigned long termno, unsigned long len,
lbuf[1]);
}
-static inline long plpar_eoi(unsigned long xirr)
-{
- return plpar_hcall_norets(H_EOI, xirr);
-}
-
-static inline long plpar_cppr(unsigned long cppr)
-{
- return plpar_hcall_norets(H_CPPR, cppr);
-}
-
-static inline long plpar_ipi(unsigned long servernum, unsigned long mfrr)
-{
- return plpar_hcall_norets(H_IPI, servernum, mfrr);
-}
-
-static inline long plpar_xirr(unsigned long *xirr_ret, unsigned char cppr)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_XIRR, retbuf, cppr);
-
- *xirr_ret = retbuf[0];
-
- return rc;
-}
-
#endif /* _PSERIES_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c
index c55d7ad9c64..086d2ae4e06 100644
--- a/arch/powerpc/platforms/pseries/ras.c
+++ b/arch/powerpc/platforms/pseries/ras.c
@@ -122,7 +122,7 @@ static irqreturn_t ras_epow_interrupt(int irq, void *dev_id)
status = rtas_call(ras_check_exception_token, 6, 1, NULL,
RTAS_VECTOR_EXTERNAL_INTERRUPT,
- irq_map[irq].hwirq,
+ virq_to_hw(irq),
RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS,
critical, __pa(&ras_log_buf),
rtas_get_error_log_max());
@@ -157,7 +157,7 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id)
status = rtas_call(ras_check_exception_token, 6, 1, NULL,
RTAS_VECTOR_EXTERNAL_INTERRUPT,
- irq_map[irq].hwirq,
+ virq_to_hw(irq),
RTAS_INTERNAL_ERROR, 1 /*Time Critical */,
__pa(&ras_log_buf),
rtas_get_error_log_max());
@@ -227,7 +227,7 @@ static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
struct rtas_error_log *h, *errhdr = NULL;
if (!VALID_FWNMI_BUFFER(regs->gpr[3])) {
- printk(KERN_ERR "FWNMI: corrupt r3\n");
+ printk(KERN_ERR "FWNMI: corrupt r3 0x%016lx\n", regs->gpr[3]);
return NULL;
}
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 00072414908..593acceeff9 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -53,9 +53,9 @@
#include <asm/irq.h>
#include <asm/time.h>
#include <asm/nvram.h>
-#include "xics.h"
#include <asm/pmc.h>
#include <asm/mpic.h>
+#include <asm/xics.h>
#include <asm/ppc-pci.h>
#include <asm/i8259.h>
#include <asm/udbg.h>
@@ -205,6 +205,9 @@ static void __init pseries_mpic_init_IRQ(void)
mpic_assign_isu(mpic, n, isuaddr);
}
+ /* Setup top-level get_irq */
+ ppc_md.get_irq = mpic_get_irq;
+
/* All ISUs are setup, complete initialization */
mpic_init(mpic);
@@ -214,7 +217,7 @@ static void __init pseries_mpic_init_IRQ(void)
static void __init pseries_xics_init_IRQ(void)
{
- xics_init_IRQ();
+ xics_init();
pseries_setup_i8259_cascade();
}
@@ -238,7 +241,6 @@ static void __init pseries_discover_pic(void)
if (strstr(typep, "open-pic")) {
pSeries_mpic_node = of_node_get(np);
ppc_md.init_IRQ = pseries_mpic_init_IRQ;
- ppc_md.get_irq = mpic_get_irq;
setup_kexec_cpu_down_mpic();
smp_init_pseries_mpic();
return;
@@ -276,6 +278,8 @@ static struct notifier_block pci_dn_reconfig_nb = {
.notifier_call = pci_dn_reconfig_notifier,
};
+struct kmem_cache *dtl_cache;
+
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
/*
* Allocate space for the dispatch trace log for all possible cpus
@@ -291,10 +295,12 @@ static int alloc_dispatch_logs(void)
if (!firmware_has_feature(FW_FEATURE_SPLPAR))
return 0;
+ if (!dtl_cache)
+ return 0;
+
for_each_possible_cpu(cpu) {
pp = &paca[cpu];
- dtl = kmalloc_node(DISPATCH_LOG_BYTES, GFP_KERNEL,
- cpu_to_node(cpu));
+ dtl = kmem_cache_alloc(dtl_cache, GFP_KERNEL);
if (!dtl) {
pr_warn("Failed to allocate dispatch trace log for cpu %d\n",
cpu);
@@ -324,10 +330,27 @@ static int alloc_dispatch_logs(void)
return 0;
}
-
-early_initcall(alloc_dispatch_logs);
+#else /* !CONFIG_VIRT_CPU_ACCOUNTING */
+static inline int alloc_dispatch_logs(void)
+{
+ return 0;
+}
#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
+static int alloc_dispatch_log_kmem_cache(void)
+{
+ dtl_cache = kmem_cache_create("dtl", DISPATCH_LOG_BYTES,
+ DISPATCH_LOG_BYTES, 0, NULL);
+ if (!dtl_cache) {
+ pr_warn("Failed to create dispatch trace log buffer cache\n");
+ pr_warn("Stolen time statistics will be unreliable\n");
+ return 0;
+ }
+
+ return alloc_dispatch_logs();
+}
+early_initcall(alloc_dispatch_log_kmem_cache);
+
static void __init pSeries_setup_arch(void)
{
/* Discover PIC type and setup ppc_md accordingly */
@@ -395,6 +418,16 @@ static int pseries_set_xdabr(unsigned long dabr)
#define CMO_CHARACTERISTICS_TOKEN 44
#define CMO_MAXLENGTH 1026
+void pSeries_coalesce_init(void)
+{
+ struct hvcall_mpp_x_data mpp_x_data;
+
+ if (firmware_has_feature(FW_FEATURE_CMO) && !h_get_mpp_x(&mpp_x_data))
+ powerpc_firmware_features |= FW_FEATURE_XCMO;
+ else
+ powerpc_firmware_features &= ~FW_FEATURE_XCMO;
+}
+
/**
* fw_cmo_feature_init - FW_FEATURE_CMO is not stored in ibm,hypertas-functions,
* handle that here. (Stolen from parse_system_parameter_string)
@@ -464,6 +497,7 @@ void pSeries_cmo_feature_init(void)
pr_debug("CMO enabled, PrPSP=%d, SecPSP=%d\n", CMO_PrPSP,
CMO_SecPSP);
powerpc_firmware_features |= FW_FEATURE_CMO;
+ pSeries_coalesce_init();
} else
pr_debug("CMO not enabled, PrPSP=%d, SecPSP=%d\n", CMO_PrPSP,
CMO_SecPSP);
diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c
index a509c5292a6..fbffd7e47ab 100644
--- a/arch/powerpc/platforms/pseries/smp.c
+++ b/arch/powerpc/platforms/pseries/smp.c
@@ -44,10 +44,11 @@
#include <asm/mpic.h>
#include <asm/vdso_datapage.h>
#include <asm/cputhreads.h>
+#include <asm/mpic.h>
+#include <asm/xics.h>
#include "plpar_wrappers.h"
#include "pseries.h"
-#include "xics.h"
#include "offline_states.h"
@@ -136,7 +137,6 @@ out:
return 1;
}
-#ifdef CONFIG_XICS
static void __devinit smp_xics_setup_cpu(int cpu)
{
if (cpu != boot_cpuid)
@@ -151,14 +151,13 @@ static void __devinit smp_xics_setup_cpu(int cpu)
set_default_offline_state(cpu);
#endif
}
-#endif /* CONFIG_XICS */
-static void __devinit smp_pSeries_kick_cpu(int nr)
+static int __devinit smp_pSeries_kick_cpu(int nr)
{
BUG_ON(nr < 0 || nr >= NR_CPUS);
if (!smp_startup_cpu(nr))
- return;
+ return -ENOENT;
/*
* The processor is currently spinning, waiting for the
@@ -180,6 +179,8 @@ static void __devinit smp_pSeries_kick_cpu(int nr)
"Ret= %ld\n", nr, rc);
}
#endif
+
+ return 0;
}
static int smp_pSeries_cpu_bootable(unsigned int nr)
@@ -197,23 +198,22 @@ static int smp_pSeries_cpu_bootable(unsigned int nr)
return 1;
}
-#ifdef CONFIG_MPIC
+
static struct smp_ops_t pSeries_mpic_smp_ops = {
.message_pass = smp_mpic_message_pass,
.probe = smp_mpic_probe,
.kick_cpu = smp_pSeries_kick_cpu,
.setup_cpu = smp_mpic_setup_cpu,
};
-#endif
-#ifdef CONFIG_XICS
+
static struct smp_ops_t pSeries_xics_smp_ops = {
- .message_pass = smp_xics_message_pass,
- .probe = smp_xics_probe,
+ .message_pass = smp_muxed_ipi_message_pass,
+ .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */
+ .probe = xics_smp_probe,
.kick_cpu = smp_pSeries_kick_cpu,
.setup_cpu = smp_xics_setup_cpu,
.cpu_bootable = smp_pSeries_cpu_bootable,
};
-#endif
/* This is called very early */
static void __init smp_init_pseries(void)
@@ -245,14 +245,12 @@ static void __init smp_init_pseries(void)
pr_debug(" <- smp_init_pSeries()\n");
}
-#ifdef CONFIG_MPIC
void __init smp_init_pseries_mpic(void)
{
smp_ops = &pSeries_mpic_smp_ops;
smp_init_pseries();
}
-#endif
void __init smp_init_pseries_xics(void)
{
diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c
deleted file mode 100644
index d6901334d66..00000000000
--- a/arch/powerpc/platforms/pseries/xics.c
+++ /dev/null
@@ -1,949 +0,0 @@
-/*
- * arch/powerpc/platforms/pseries/xics.c
- *
- * Copyright 2000 IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/types.h>
-#include <linux/threads.h>
-#include <linux/kernel.h>
-#include <linux/irq.h>
-#include <linux/smp.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/radix-tree.h>
-#include <linux/cpu.h>
-#include <linux/msi.h>
-#include <linux/of.h>
-#include <linux/percpu.h>
-
-#include <asm/firmware.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/smp.h>
-#include <asm/rtas.h>
-#include <asm/hvcall.h>
-#include <asm/machdep.h>
-
-#include "xics.h"
-#include "plpar_wrappers.h"
-
-static struct irq_host *xics_host;
-
-#define XICS_IPI 2
-#define XICS_IRQ_SPURIOUS 0
-
-/* Want a priority other than 0. Various HW issues require this. */
-#define DEFAULT_PRIORITY 5
-
-/*
- * Mark IPIs as higher priority so we can take them inside interrupts that
- * arent marked IRQF_DISABLED
- */
-#define IPI_PRIORITY 4
-
-/* The least favored priority */
-#define LOWEST_PRIORITY 0xFF
-
-/* The number of priorities defined above */
-#define MAX_NUM_PRIORITIES 3
-
-static unsigned int default_server = 0xFF;
-static unsigned int default_distrib_server = 0;
-static unsigned int interrupt_server_size = 8;
-
-/* RTAS service tokens */
-static int ibm_get_xive;
-static int ibm_set_xive;
-static int ibm_int_on;
-static int ibm_int_off;
-
-struct xics_cppr {
- unsigned char stack[MAX_NUM_PRIORITIES];
- int index;
-};
-
-static DEFINE_PER_CPU(struct xics_cppr, xics_cppr);
-
-/* Direct hardware low level accessors */
-
-/* The part of the interrupt presentation layer that we care about */
-struct xics_ipl {
- union {
- u32 word;
- u8 bytes[4];
- } xirr_poll;
- union {
- u32 word;
- u8 bytes[4];
- } xirr;
- u32 dummy;
- union {
- u32 word;
- u8 bytes[4];
- } qirr;
-};
-
-static struct xics_ipl __iomem *xics_per_cpu[NR_CPUS];
-
-static inline unsigned int direct_xirr_info_get(void)
-{
- int cpu = smp_processor_id();
-
- return in_be32(&xics_per_cpu[cpu]->xirr.word);
-}
-
-static inline void direct_xirr_info_set(unsigned int value)
-{
- int cpu = smp_processor_id();
-
- out_be32(&xics_per_cpu[cpu]->xirr.word, value);
-}
-
-static inline void direct_cppr_info(u8 value)
-{
- int cpu = smp_processor_id();
-
- out_8(&xics_per_cpu[cpu]->xirr.bytes[0], value);
-}
-
-static inline void direct_qirr_info(int n_cpu, u8 value)
-{
- out_8(&xics_per_cpu[n_cpu]->qirr.bytes[0], value);
-}
-
-
-/* LPAR low level accessors */
-
-static inline unsigned int lpar_xirr_info_get(unsigned char cppr)
-{
- unsigned long lpar_rc;
- unsigned long return_value;
-
- lpar_rc = plpar_xirr(&return_value, cppr);
- if (lpar_rc != H_SUCCESS)
- panic(" bad return code xirr - rc = %lx\n", lpar_rc);
- return (unsigned int)return_value;
-}
-
-static inline void lpar_xirr_info_set(unsigned int value)
-{
- unsigned long lpar_rc;
-
- lpar_rc = plpar_eoi(value);
- if (lpar_rc != H_SUCCESS)
- panic("bad return code EOI - rc = %ld, value=%x\n", lpar_rc,
- value);
-}
-
-static inline void lpar_cppr_info(u8 value)
-{
- unsigned long lpar_rc;
-
- lpar_rc = plpar_cppr(value);
- if (lpar_rc != H_SUCCESS)
- panic("bad return code cppr - rc = %lx\n", lpar_rc);
-}
-
-static inline void lpar_qirr_info(int n_cpu , u8 value)
-{
- unsigned long lpar_rc;
-
- lpar_rc = plpar_ipi(get_hard_smp_processor_id(n_cpu), value);
- if (lpar_rc != H_SUCCESS)
- panic("bad return code qirr - rc = %lx\n", lpar_rc);
-}
-
-
-/* Interface to generic irq subsystem */
-
-#ifdef CONFIG_SMP
-/*
- * For the moment we only implement delivery to all cpus or one cpu.
- *
- * If the requested affinity is cpu_all_mask, we set global affinity.
- * If not we set it to the first cpu in the mask, even if multiple cpus
- * are set. This is so things like irqbalance (which set core and package
- * wide affinities) do the right thing.
- */
-static int get_irq_server(unsigned int virq, const struct cpumask *cpumask,
- unsigned int strict_check)
-{
-
- if (!distribute_irqs)
- return default_server;
-
- if (!cpumask_subset(cpu_possible_mask, cpumask)) {
- int server = cpumask_first_and(cpu_online_mask, cpumask);
-
- if (server < nr_cpu_ids)
- return get_hard_smp_processor_id(server);
-
- if (strict_check)
- return -1;
- }
-
- /*
- * Workaround issue with some versions of JS20 firmware that
- * deliver interrupts to cpus which haven't been started. This
- * happens when using the maxcpus= boot option.
- */
- if (cpumask_equal(cpu_online_mask, cpu_present_mask))
- return default_distrib_server;
-
- return default_server;
-}
-#else
-#define get_irq_server(virq, cpumask, strict_check) (default_server)
-#endif
-
-static void xics_unmask_irq(struct irq_data *d)
-{
- unsigned int hwirq;
- int call_status;
- int server;
-
- pr_devel("xics: unmask virq %d\n", d->irq);
-
- hwirq = (unsigned int)irq_map[d->irq].hwirq;
- pr_devel(" -> map to hwirq 0x%x\n", hwirq);
- if (hwirq == XICS_IPI || hwirq == XICS_IRQ_SPURIOUS)
- return;
-
- server = get_irq_server(d->irq, d->affinity, 0);
-
- call_status = rtas_call(ibm_set_xive, 3, 1, NULL, hwirq, server,
- DEFAULT_PRIORITY);
- if (call_status != 0) {
- printk(KERN_ERR
- "%s: ibm_set_xive irq %u server %x returned %d\n",
- __func__, hwirq, server, call_status);
- return;
- }
-
- /* Now unmask the interrupt (often a no-op) */
- call_status = rtas_call(ibm_int_on, 1, 1, NULL, hwirq);
- if (call_status != 0) {
- printk(KERN_ERR "%s: ibm_int_on irq=%u returned %d\n",
- __func__, hwirq, call_status);
- return;
- }
-}
-
-static unsigned int xics_startup(struct irq_data *d)
-{
- /*
- * The generic MSI code returns with the interrupt disabled on the
- * card, using the MSI mask bits. Firmware doesn't appear to unmask
- * at that level, so we do it here by hand.
- */
- if (d->msi_desc)
- unmask_msi_irq(d);
-
- /* unmask it */
- xics_unmask_irq(d);
- return 0;
-}
-
-static void xics_mask_real_irq(unsigned int hwirq)
-{
- int call_status;
-
- if (hwirq == XICS_IPI)
- return;
-
- call_status = rtas_call(ibm_int_off, 1, 1, NULL, hwirq);
- if (call_status != 0) {
- printk(KERN_ERR "%s: ibm_int_off irq=%u returned %d\n",
- __func__, hwirq, call_status);
- return;
- }
-
- /* Have to set XIVE to 0xff to be able to remove a slot */
- call_status = rtas_call(ibm_set_xive, 3, 1, NULL, hwirq,
- default_server, 0xff);
- if (call_status != 0) {
- printk(KERN_ERR "%s: ibm_set_xive(0xff) irq=%u returned %d\n",
- __func__, hwirq, call_status);
- return;
- }
-}
-
-static void xics_mask_irq(struct irq_data *d)
-{
- unsigned int hwirq;
-
- pr_devel("xics: mask virq %d\n", d->irq);
-
- hwirq = (unsigned int)irq_map[d->irq].hwirq;
- if (hwirq == XICS_IPI || hwirq == XICS_IRQ_SPURIOUS)
- return;
- xics_mask_real_irq(hwirq);
-}
-
-static void xics_mask_unknown_vec(unsigned int vec)
-{
- printk(KERN_ERR "Interrupt %u (real) is invalid, disabling it.\n", vec);
- xics_mask_real_irq(vec);
-}
-
-static inline unsigned int xics_xirr_vector(unsigned int xirr)
-{
- /*
- * The top byte is the old cppr, to be restored on EOI.
- * The remaining 24 bits are the vector.
- */
- return xirr & 0x00ffffff;
-}
-
-static void push_cppr(unsigned int vec)
-{
- struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
-
- if (WARN_ON(os_cppr->index >= MAX_NUM_PRIORITIES - 1))
- return;
-
- if (vec == XICS_IPI)
- os_cppr->stack[++os_cppr->index] = IPI_PRIORITY;
- else
- os_cppr->stack[++os_cppr->index] = DEFAULT_PRIORITY;
-}
-
-static unsigned int xics_get_irq_direct(void)
-{
- unsigned int xirr = direct_xirr_info_get();
- unsigned int vec = xics_xirr_vector(xirr);
- unsigned int irq;
-
- if (vec == XICS_IRQ_SPURIOUS)
- return NO_IRQ;
-
- irq = irq_radix_revmap_lookup(xics_host, vec);
- if (likely(irq != NO_IRQ)) {
- push_cppr(vec);
- return irq;
- }
-
- /* We don't have a linux mapping, so have rtas mask it. */
- xics_mask_unknown_vec(vec);
-
- /* We might learn about it later, so EOI it */
- direct_xirr_info_set(xirr);
- return NO_IRQ;
-}
-
-static unsigned int xics_get_irq_lpar(void)
-{
- struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
- unsigned int xirr = lpar_xirr_info_get(os_cppr->stack[os_cppr->index]);
- unsigned int vec = xics_xirr_vector(xirr);
- unsigned int irq;
-
- if (vec == XICS_IRQ_SPURIOUS)
- return NO_IRQ;
-
- irq = irq_radix_revmap_lookup(xics_host, vec);
- if (likely(irq != NO_IRQ)) {
- push_cppr(vec);
- return irq;
- }
-
- /* We don't have a linux mapping, so have RTAS mask it. */
- xics_mask_unknown_vec(vec);
-
- /* We might learn about it later, so EOI it */
- lpar_xirr_info_set(xirr);
- return NO_IRQ;
-}
-
-static unsigned char pop_cppr(void)
-{
- struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
-
- if (WARN_ON(os_cppr->index < 1))
- return LOWEST_PRIORITY;
-
- return os_cppr->stack[--os_cppr->index];
-}
-
-static void xics_eoi_direct(struct irq_data *d)
-{
- unsigned int hwirq = (unsigned int)irq_map[d->irq].hwirq;
-
- iosync();
- direct_xirr_info_set((pop_cppr() << 24) | hwirq);
-}
-
-static void xics_eoi_lpar(struct irq_data *d)
-{
- unsigned int hwirq = (unsigned int)irq_map[d->irq].hwirq;
-
- iosync();
- lpar_xirr_info_set((pop_cppr() << 24) | hwirq);
-}
-
-static int
-xics_set_affinity(struct irq_data *d, const struct cpumask *cpumask, bool force)
-{
- unsigned int hwirq;
- int status;
- int xics_status[2];
- int irq_server;
-
- hwirq = (unsigned int)irq_map[d->irq].hwirq;
- if (hwirq == XICS_IPI || hwirq == XICS_IRQ_SPURIOUS)
- return -1;
-
- status = rtas_call(ibm_get_xive, 1, 3, xics_status, hwirq);
-
- if (status) {
- printk(KERN_ERR "%s: ibm,get-xive irq=%u returns %d\n",
- __func__, hwirq, status);
- return -1;
- }
-
- irq_server = get_irq_server(d->irq, cpumask, 1);
- if (irq_server == -1) {
- char cpulist[128];
- cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
- printk(KERN_WARNING
- "%s: No online cpus in the mask %s for irq %d\n",
- __func__, cpulist, d->irq);
- return -1;
- }
-
- status = rtas_call(ibm_set_xive, 3, 1, NULL,
- hwirq, irq_server, xics_status[1]);
-
- if (status) {
- printk(KERN_ERR "%s: ibm,set-xive irq=%u returns %d\n",
- __func__, hwirq, status);
- return -1;
- }
-
- return 0;
-}
-
-static struct irq_chip xics_pic_direct = {
- .name = "XICS",
- .irq_startup = xics_startup,
- .irq_mask = xics_mask_irq,
- .irq_unmask = xics_unmask_irq,
- .irq_eoi = xics_eoi_direct,
- .irq_set_affinity = xics_set_affinity
-};
-
-static struct irq_chip xics_pic_lpar = {
- .name = "XICS",
- .irq_startup = xics_startup,
- .irq_mask = xics_mask_irq,
- .irq_unmask = xics_unmask_irq,
- .irq_eoi = xics_eoi_lpar,
- .irq_set_affinity = xics_set_affinity
-};
-
-
-/* Interface to arch irq controller subsystem layer */
-
-/* Points to the irq_chip we're actually using */
-static struct irq_chip *xics_irq_chip;
-
-static int xics_host_match(struct irq_host *h, struct device_node *node)
-{
- /* IBM machines have interrupt parents of various funky types for things
- * like vdevices, events, etc... The trick we use here is to match
- * everything here except the legacy 8259 which is compatible "chrp,iic"
- */
- return !of_device_is_compatible(node, "chrp,iic");
-}
-
-static int xics_host_map(struct irq_host *h, unsigned int virq,
- irq_hw_number_t hw)
-{
- pr_devel("xics: map virq %d, hwirq 0x%lx\n", virq, hw);
-
- /* Insert the interrupt mapping into the radix tree for fast lookup */
- irq_radix_revmap_insert(xics_host, virq, hw);
-
- irq_set_status_flags(virq, IRQ_LEVEL);
- irq_set_chip_and_handler(virq, xics_irq_chip, handle_fasteoi_irq);
- return 0;
-}
-
-static int xics_host_xlate(struct irq_host *h, struct device_node *ct,
- const u32 *intspec, unsigned int intsize,
- irq_hw_number_t *out_hwirq, unsigned int *out_flags)
-
-{
- /* Current xics implementation translates everything
- * to level. It is not technically right for MSIs but this
- * is irrelevant at this point. We might get smarter in the future
- */
- *out_hwirq = intspec[0];
- *out_flags = IRQ_TYPE_LEVEL_LOW;
-
- return 0;
-}
-
-static struct irq_host_ops xics_host_ops = {
- .match = xics_host_match,
- .map = xics_host_map,
- .xlate = xics_host_xlate,
-};
-
-static void __init xics_init_host(void)
-{
- if (firmware_has_feature(FW_FEATURE_LPAR))
- xics_irq_chip = &xics_pic_lpar;
- else
- xics_irq_chip = &xics_pic_direct;
-
- xics_host = irq_alloc_host(NULL, IRQ_HOST_MAP_TREE, 0, &xics_host_ops,
- XICS_IRQ_SPURIOUS);
- BUG_ON(xics_host == NULL);
- irq_set_default_host(xics_host);
-}
-
-
-/* Inter-processor interrupt support */
-
-#ifdef CONFIG_SMP
-/*
- * XICS only has a single IPI, so encode the messages per CPU
- */
-static DEFINE_PER_CPU_SHARED_ALIGNED(unsigned long, xics_ipi_message);
-
-static inline void smp_xics_do_message(int cpu, int msg)
-{
- unsigned long *tgt = &per_cpu(xics_ipi_message, cpu);
-
- set_bit(msg, tgt);
- mb();
- if (firmware_has_feature(FW_FEATURE_LPAR))
- lpar_qirr_info(cpu, IPI_PRIORITY);
- else
- direct_qirr_info(cpu, IPI_PRIORITY);
-}
-
-void smp_xics_message_pass(int target, int msg)
-{
- unsigned int i;
-
- if (target < NR_CPUS) {
- smp_xics_do_message(target, msg);
- } else {
- for_each_online_cpu(i) {
- if (target == MSG_ALL_BUT_SELF
- && i == smp_processor_id())
- continue;
- smp_xics_do_message(i, msg);
- }
- }
-}
-
-static irqreturn_t xics_ipi_dispatch(int cpu)
-{
- unsigned long *tgt = &per_cpu(xics_ipi_message, cpu);
-
- mb(); /* order mmio clearing qirr */
- while (*tgt) {
- if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, tgt)) {
- smp_message_recv(PPC_MSG_CALL_FUNCTION);
- }
- if (test_and_clear_bit(PPC_MSG_RESCHEDULE, tgt)) {
- smp_message_recv(PPC_MSG_RESCHEDULE);
- }
- if (test_and_clear_bit(PPC_MSG_CALL_FUNC_SINGLE, tgt)) {
- smp_message_recv(PPC_MSG_CALL_FUNC_SINGLE);
- }
-#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
- if (test_and_clear_bit(PPC_MSG_DEBUGGER_BREAK, tgt)) {
- smp_message_recv(PPC_MSG_DEBUGGER_BREAK);
- }
-#endif
- }
- return IRQ_HANDLED;
-}
-
-static irqreturn_t xics_ipi_action_direct(int irq, void *dev_id)
-{
- int cpu = smp_processor_id();
-
- direct_qirr_info(cpu, 0xff);
-
- return xics_ipi_dispatch(cpu);
-}
-
-static irqreturn_t xics_ipi_action_lpar(int irq, void *dev_id)
-{
- int cpu = smp_processor_id();
-
- lpar_qirr_info(cpu, 0xff);
-
- return xics_ipi_dispatch(cpu);
-}
-
-static void xics_request_ipi(void)
-{
- unsigned int ipi;
- int rc;
-
- ipi = irq_create_mapping(xics_host, XICS_IPI);
- BUG_ON(ipi == NO_IRQ);
-
- /*
- * IPIs are marked IRQF_DISABLED as they must run with irqs
- * disabled
- */
- irq_set_handler(ipi, handle_percpu_irq);
- if (firmware_has_feature(FW_FEATURE_LPAR))
- rc = request_irq(ipi, xics_ipi_action_lpar,
- IRQF_DISABLED|IRQF_PERCPU, "IPI", NULL);
- else
- rc = request_irq(ipi, xics_ipi_action_direct,
- IRQF_DISABLED|IRQF_PERCPU, "IPI", NULL);
- BUG_ON(rc);
-}
-
-int __init smp_xics_probe(void)
-{
- xics_request_ipi();
-
- return cpumask_weight(cpu_possible_mask);
-}
-
-#endif /* CONFIG_SMP */
-
-
-/* Initialization */
-
-static void xics_update_irq_servers(void)
-{
- int i, j;
- struct device_node *np;
- u32 ilen;
- const u32 *ireg;
- u32 hcpuid;
-
- /* Find the server numbers for the boot cpu. */
- np = of_get_cpu_node(boot_cpuid, NULL);
- BUG_ON(!np);
-
- ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
- if (!ireg) {
- of_node_put(np);
- return;
- }
-
- i = ilen / sizeof(int);
- hcpuid = get_hard_smp_processor_id(boot_cpuid);
-
- /* Global interrupt distribution server is specified in the last
- * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last
- * entry fom this property for current boot cpu id and use it as
- * default distribution server
- */
- for (j = 0; j < i; j += 2) {
- if (ireg[j] == hcpuid) {
- default_server = hcpuid;
- default_distrib_server = ireg[j+1];
- }
- }
-
- of_node_put(np);
-}
-
-static void __init xics_map_one_cpu(int hw_id, unsigned long addr,
- unsigned long size)
-{
- int i;
-
- /* This may look gross but it's good enough for now, we don't quite
- * have a hard -> linux processor id matching.
- */
- for_each_possible_cpu(i) {
- if (!cpu_present(i))
- continue;
- if (hw_id == get_hard_smp_processor_id(i)) {
- xics_per_cpu[i] = ioremap(addr, size);
- return;
- }
- }
-}
-
-static void __init xics_init_one_node(struct device_node *np,
- unsigned int *indx)
-{
- unsigned int ilen;
- const u32 *ireg;
-
- /* This code does the theorically broken assumption that the interrupt
- * server numbers are the same as the hard CPU numbers.
- * This happens to be the case so far but we are playing with fire...
- * should be fixed one of these days. -BenH.
- */
- ireg = of_get_property(np, "ibm,interrupt-server-ranges", NULL);
-
- /* Do that ever happen ? we'll know soon enough... but even good'old
- * f80 does have that property ..
- */
- WARN_ON(ireg == NULL);
- if (ireg) {
- /*
- * set node starting index for this node
- */
- *indx = *ireg;
- }
- ireg = of_get_property(np, "reg", &ilen);
- if (!ireg)
- panic("xics_init_IRQ: can't find interrupt reg property");
-
- while (ilen >= (4 * sizeof(u32))) {
- unsigned long addr, size;
-
- /* XXX Use proper OF parsing code here !!! */
- addr = (unsigned long)*ireg++ << 32;
- ilen -= sizeof(u32);
- addr |= *ireg++;
- ilen -= sizeof(u32);
- size = (unsigned long)*ireg++ << 32;
- ilen -= sizeof(u32);
- size |= *ireg++;
- ilen -= sizeof(u32);
- xics_map_one_cpu(*indx, addr, size);
- (*indx)++;
- }
-}
-
-void __init xics_init_IRQ(void)
-{
- struct device_node *np;
- u32 indx = 0;
- int found = 0;
- const u32 *isize;
-
- ppc64_boot_msg(0x20, "XICS Init");
-
- ibm_get_xive = rtas_token("ibm,get-xive");
- ibm_set_xive = rtas_token("ibm,set-xive");
- ibm_int_on = rtas_token("ibm,int-on");
- ibm_int_off = rtas_token("ibm,int-off");
-
- for_each_node_by_type(np, "PowerPC-External-Interrupt-Presentation") {
- found = 1;
- if (firmware_has_feature(FW_FEATURE_LPAR)) {
- of_node_put(np);
- break;
- }
- xics_init_one_node(np, &indx);
- }
- if (found == 0)
- return;
-
- /* get the bit size of server numbers */
- found = 0;
-
- for_each_compatible_node(np, NULL, "ibm,ppc-xics") {
- isize = of_get_property(np, "ibm,interrupt-server#-size", NULL);
-
- if (!isize)
- continue;
-
- if (!found) {
- interrupt_server_size = *isize;
- found = 1;
- } else if (*isize != interrupt_server_size) {
- printk(KERN_WARNING "XICS: "
- "mismatched ibm,interrupt-server#-size\n");
- interrupt_server_size = max(*isize,
- interrupt_server_size);
- }
- }
-
- xics_update_irq_servers();
- xics_init_host();
-
- if (firmware_has_feature(FW_FEATURE_LPAR))
- ppc_md.get_irq = xics_get_irq_lpar;
- else
- ppc_md.get_irq = xics_get_irq_direct;
-
- xics_setup_cpu();
-
- ppc64_boot_msg(0x21, "XICS Done");
-}
-
-/* Cpu startup, shutdown, and hotplug */
-
-static void xics_set_cpu_priority(unsigned char cppr)
-{
- struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
-
- /*
- * we only really want to set the priority when there's
- * just one cppr value on the stack
- */
- WARN_ON(os_cppr->index != 0);
-
- os_cppr->stack[0] = cppr;
-
- if (firmware_has_feature(FW_FEATURE_LPAR))
- lpar_cppr_info(cppr);
- else
- direct_cppr_info(cppr);
- iosync();
-}
-
-/* Have the calling processor join or leave the specified global queue */
-static void xics_set_cpu_giq(unsigned int gserver, unsigned int join)
-{
- int index;
- int status;
-
- if (!rtas_indicator_present(GLOBAL_INTERRUPT_QUEUE, NULL))
- return;
-
- index = (1UL << interrupt_server_size) - 1 - gserver;
-
- status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE, index, join);
-
- WARN(status < 0, "set-indicator(%d, %d, %u) returned %d\n",
- GLOBAL_INTERRUPT_QUEUE, index, join, status);
-}
-
-void xics_setup_cpu(void)
-{
- xics_set_cpu_priority(LOWEST_PRIORITY);
-
- xics_set_cpu_giq(default_distrib_server, 1);
-}
-
-void xics_teardown_cpu(void)
-{
- struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
- int cpu = smp_processor_id();
-
- /*
- * we have to reset the cppr index to 0 because we're
- * not going to return from the IPI
- */
- os_cppr->index = 0;
- xics_set_cpu_priority(0);
-
- /* Clear any pending IPI request */
- if (firmware_has_feature(FW_FEATURE_LPAR))
- lpar_qirr_info(cpu, 0xff);
- else
- direct_qirr_info(cpu, 0xff);
-}
-
-void xics_kexec_teardown_cpu(int secondary)
-{
- xics_teardown_cpu();
-
- /*
- * we take the ipi irq but and never return so we
- * need to EOI the IPI, but want to leave our priority 0
- *
- * should we check all the other interrupts too?
- * should we be flagging idle loop instead?
- * or creating some task to be scheduled?
- */
-
- if (firmware_has_feature(FW_FEATURE_LPAR))
- lpar_xirr_info_set((0x00 << 24) | XICS_IPI);
- else
- direct_xirr_info_set((0x00 << 24) | XICS_IPI);
-
- /*
- * Some machines need to have at least one cpu in the GIQ,
- * so leave the master cpu in the group.
- */
- if (secondary)
- xics_set_cpu_giq(default_distrib_server, 0);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-
-/* Interrupts are disabled. */
-void xics_migrate_irqs_away(void)
-{
- int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id();
- int virq;
-
- /* If we used to be the default server, move to the new "boot_cpuid" */
- if (hw_cpu == default_server)
- xics_update_irq_servers();
-
- /* Reject any interrupt that was queued to us... */
- xics_set_cpu_priority(0);
-
- /* Remove ourselves from the global interrupt queue */
- xics_set_cpu_giq(default_distrib_server, 0);
-
- /* Allow IPIs again... */
- xics_set_cpu_priority(DEFAULT_PRIORITY);
-
- for_each_irq(virq) {
- struct irq_desc *desc;
- struct irq_chip *chip;
- unsigned int hwirq;
- int xics_status[2];
- int status;
- unsigned long flags;
-
- /* We can't set affinity on ISA interrupts */
- if (virq < NUM_ISA_INTERRUPTS)
- continue;
- if (irq_map[virq].host != xics_host)
- continue;
- hwirq = (unsigned int)irq_map[virq].hwirq;
- /* We need to get IPIs still. */
- if (hwirq == XICS_IPI || hwirq == XICS_IRQ_SPURIOUS)
- continue;
-
- desc = irq_to_desc(virq);
-
- /* We only need to migrate enabled IRQS */
- if (desc == NULL || desc->action == NULL)
- continue;
-
- chip = irq_desc_get_chip(desc);
- if (chip == NULL || chip->irq_set_affinity == NULL)
- continue;
-
- raw_spin_lock_irqsave(&desc->lock, flags);
-
- status = rtas_call(ibm_get_xive, 1, 3, xics_status, hwirq);
- if (status) {
- printk(KERN_ERR "%s: ibm,get-xive irq=%u returns %d\n",
- __func__, hwirq, status);
- goto unlock;
- }
-
- /*
- * We only support delivery to all cpus or to one cpu.
- * The irq has to be migrated only in the single cpu
- * case.
- */
- if (xics_status[0] != hw_cpu)
- goto unlock;
-
- /* This is expected during cpu offline. */
- if (cpu_online(cpu))
- printk(KERN_WARNING "IRQ %u affinity broken off cpu %u\n",
- virq, cpu);
-
- /* Reset affinity to all cpus */
- cpumask_setall(desc->irq_data.affinity);
- chip->irq_set_affinity(&desc->irq_data, cpu_all_mask, true);
-unlock:
- raw_spin_unlock_irqrestore(&desc->lock, flags);
- }
-}
-#endif
diff --git a/arch/powerpc/platforms/pseries/xics.h b/arch/powerpc/platforms/pseries/xics.h
deleted file mode 100644
index d1d5a83039a..00000000000
--- a/arch/powerpc/platforms/pseries/xics.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * arch/powerpc/platforms/pseries/xics.h
- *
- * Copyright 2000 IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#ifndef _POWERPC_KERNEL_XICS_H
-#define _POWERPC_KERNEL_XICS_H
-
-extern void xics_init_IRQ(void);
-extern void xics_setup_cpu(void);
-extern void xics_teardown_cpu(void);
-extern void xics_kexec_teardown_cpu(int secondary);
-extern void xics_migrate_irqs_away(void);
-extern int smp_xics_probe(void);
-extern void smp_xics_message_pass(int target, int msg);
-
-#endif /* _POWERPC_KERNEL_XICS_H */
diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig
new file mode 100644
index 00000000000..c3c48eb62cc
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/Kconfig
@@ -0,0 +1,28 @@
+config PPC_WSP
+ bool
+ default n
+
+menu "WSP platform selection"
+ depends on PPC_BOOK3E_64
+
+config PPC_PSR2
+ bool "PSR-2 platform"
+ select PPC_A2
+ select GENERIC_TBSYNC
+ select PPC_SCOM
+ select EPAPR_BOOT
+ select PPC_WSP
+ select PPC_XICS
+ select PPC_ICP_NATIVE
+ default y
+
+endmenu
+
+config PPC_A2_DD2
+ bool "Support for DD2 based A2/WSP systems"
+ depends on PPC_A2
+
+config WORKAROUND_ERRATUM_463
+ depends on PPC_A2_DD2
+ bool "Workaround erratum 463"
+ default y
diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile
new file mode 100644
index 00000000000..095be73d6cd
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/Makefile
@@ -0,0 +1,6 @@
+ccflags-y += -mno-minimal-toc
+
+obj-y += setup.o ics.o
+obj-$(CONFIG_PPC_PSR2) += psr2.o opb_pic.o
+obj-$(CONFIG_PPC_WSP) += scom_wsp.o
+obj-$(CONFIG_SMP) += smp.o scom_smp.o
diff --git a/arch/powerpc/platforms/wsp/ics.c b/arch/powerpc/platforms/wsp/ics.c
new file mode 100644
index 00000000000..e53bd9e7b12
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/ics.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright 2008-2011 IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/cpu.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/xics.h>
+
+#include "wsp.h"
+#include "ics.h"
+
+
+/* WSP ICS */
+
+struct wsp_ics {
+ struct ics ics;
+ struct device_node *dn;
+ void __iomem *regs;
+ spinlock_t lock;
+ unsigned long *bitmap;
+ u32 chip_id;
+ u32 lsi_base;
+ u32 lsi_count;
+ u64 hwirq_start;
+ u64 count;
+#ifdef CONFIG_SMP
+ int *hwirq_cpu_map;
+#endif
+};
+
+#define to_wsp_ics(ics) container_of(ics, struct wsp_ics, ics)
+
+#define INT_SRC_LAYER_BUID_REG(base) ((base) + 0x00)
+#define IODA_TBL_ADDR_REG(base) ((base) + 0x18)
+#define IODA_TBL_DATA_REG(base) ((base) + 0x20)
+#define XIVE_UPDATE_REG(base) ((base) + 0x28)
+#define ICS_INT_CAPS_REG(base) ((base) + 0x30)
+
+#define TBL_AUTO_INCREMENT ((1UL << 63) | (1UL << 15))
+#define TBL_SELECT_XIST (1UL << 48)
+#define TBL_SELECT_XIVT (1UL << 49)
+
+#define IODA_IRQ(irq) ((irq) & (0x7FFULL)) /* HRM 5.1.3.4 */
+
+#define XIST_REQUIRED 0x8
+#define XIST_REJECTED 0x4
+#define XIST_PRESENTED 0x2
+#define XIST_PENDING 0x1
+
+#define XIVE_SERVER_SHIFT 42
+#define XIVE_SERVER_MASK 0xFFFFULL
+#define XIVE_PRIORITY_MASK 0xFFULL
+#define XIVE_PRIORITY_SHIFT 32
+#define XIVE_WRITE_ENABLE (1ULL << 63)
+
+/*
+ * The docs refer to a 6 bit field called ChipID, which consists of a
+ * 3 bit NodeID and a 3 bit ChipID. On WSP the ChipID is always zero
+ * so we ignore it, and every where we use "chip id" in this code we
+ * mean the NodeID.
+ */
+#define WSP_ICS_CHIP_SHIFT 17
+
+
+static struct wsp_ics *ics_list;
+static int num_ics;
+
+/* ICS Source controller accessors */
+
+static u64 wsp_ics_get_xive(struct wsp_ics *ics, unsigned int irq)
+{
+ unsigned long flags;
+ u64 xive;
+
+ spin_lock_irqsave(&ics->lock, flags);
+ out_be64(IODA_TBL_ADDR_REG(ics->regs), TBL_SELECT_XIVT | IODA_IRQ(irq));
+ xive = in_be64(IODA_TBL_DATA_REG(ics->regs));
+ spin_unlock_irqrestore(&ics->lock, flags);
+
+ return xive;
+}
+
+static void wsp_ics_set_xive(struct wsp_ics *ics, unsigned int irq, u64 xive)
+{
+ xive &= ~XIVE_ADDR_MASK;
+ xive |= (irq & XIVE_ADDR_MASK);
+ xive |= XIVE_WRITE_ENABLE;
+
+ out_be64(XIVE_UPDATE_REG(ics->regs), xive);
+}
+
+static u64 xive_set_server(u64 xive, unsigned int server)
+{
+ u64 mask = ~(XIVE_SERVER_MASK << XIVE_SERVER_SHIFT);
+
+ xive &= mask;
+ xive |= (server & XIVE_SERVER_MASK) << XIVE_SERVER_SHIFT;
+
+ return xive;
+}
+
+static u64 xive_set_priority(u64 xive, unsigned int priority)
+{
+ u64 mask = ~(XIVE_PRIORITY_MASK << XIVE_PRIORITY_SHIFT);
+
+ xive &= mask;
+ xive |= (priority & XIVE_PRIORITY_MASK) << XIVE_PRIORITY_SHIFT;
+
+ return xive;
+}
+
+
+#ifdef CONFIG_SMP
+/* Find logical CPUs within mask on a given chip and store result in ret */
+void cpus_on_chip(int chip_id, cpumask_t *mask, cpumask_t *ret)
+{
+ int cpu, chip;
+ struct device_node *cpu_dn, *dn;
+ const u32 *prop;
+
+ cpumask_clear(ret);
+ for_each_cpu(cpu, mask) {
+ cpu_dn = of_get_cpu_node(cpu, NULL);
+ if (!cpu_dn)
+ continue;
+
+ prop = of_get_property(cpu_dn, "at-node", NULL);
+ if (!prop) {
+ of_node_put(cpu_dn);
+ continue;
+ }
+
+ dn = of_find_node_by_phandle(*prop);
+ of_node_put(cpu_dn);
+
+ chip = wsp_get_chip_id(dn);
+ if (chip == chip_id)
+ cpumask_set_cpu(cpu, ret);
+
+ of_node_put(dn);
+ }
+}
+
+/* Store a suitable CPU to handle a hwirq in the ics->hwirq_cpu_map cache */
+static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq,
+ const cpumask_t *affinity)
+{
+ cpumask_var_t avail, newmask;
+ int ret = -ENOMEM, cpu, cpu_rover = 0, target;
+ int index = hwirq - ics->hwirq_start;
+ unsigned int nodeid;
+
+ BUG_ON(index < 0 || index >= ics->count);
+
+ if (!ics->hwirq_cpu_map)
+ return -ENOMEM;
+
+ if (!distribute_irqs) {
+ ics->hwirq_cpu_map[hwirq - ics->hwirq_start] = xics_default_server;
+ return 0;
+ }
+
+ /* Allocate needed CPU masks */
+ if (!alloc_cpumask_var(&avail, GFP_KERNEL))
+ goto ret;
+ if (!alloc_cpumask_var(&newmask, GFP_KERNEL))
+ goto freeavail;
+
+ /* Find PBus attached to the source of this IRQ */
+ nodeid = (hwirq >> WSP_ICS_CHIP_SHIFT) & 0x3; /* 12:14 */
+
+ /* Find CPUs that could handle this IRQ */
+ if (affinity)
+ cpumask_and(avail, cpu_online_mask, affinity);
+ else
+ cpumask_copy(avail, cpu_online_mask);
+
+ /* Narrow selection down to logical CPUs on the same chip */
+ cpus_on_chip(nodeid, avail, newmask);
+
+ /* Ensure we haven't narrowed it down to 0 */
+ if (unlikely(cpumask_empty(newmask))) {
+ if (unlikely(cpumask_empty(avail))) {
+ ret = -1;
+ goto out;
+ }
+ cpumask_copy(newmask, avail);
+ }
+
+ /* Choose a CPU out of those we narrowed it down to in round robin */
+ target = hwirq % cpumask_weight(newmask);
+ for_each_cpu(cpu, newmask) {
+ if (cpu_rover++ >= target) {
+ ics->hwirq_cpu_map[index] = get_hard_smp_processor_id(cpu);
+ ret = 0;
+ goto out;
+ }
+ }
+
+ /* Shouldn't happen */
+ WARN_ON(1);
+
+out:
+ free_cpumask_var(newmask);
+freeavail:
+ free_cpumask_var(avail);
+ret:
+ if (ret < 0) {
+ ics->hwirq_cpu_map[index] = cpumask_first(cpu_online_mask);
+ pr_warning("Error, falling hwirq 0x%x routing back to CPU %i\n",
+ hwirq, ics->hwirq_cpu_map[index]);
+ }
+ return ret;
+}
+
+static void alloc_irq_map(struct wsp_ics *ics)
+{
+ int i;
+
+ ics->hwirq_cpu_map = kmalloc(sizeof(int) * ics->count, GFP_KERNEL);
+ if (!ics->hwirq_cpu_map) {
+ pr_warning("Allocate hwirq_cpu_map failed, "
+ "IRQ balancing disabled\n");
+ return;
+ }
+
+ for (i=0; i < ics->count; i++)
+ ics->hwirq_cpu_map[i] = xics_default_server;
+}
+
+static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq)
+{
+ int index = hwirq - ics->hwirq_start;
+
+ BUG_ON(index < 0 || index >= ics->count);
+
+ if (!ics->hwirq_cpu_map)
+ return xics_default_server;
+
+ return ics->hwirq_cpu_map[index];
+}
+#else /* !CONFIG_SMP */
+static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq,
+ const cpumask_t *affinity)
+{
+ return 0;
+}
+
+static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq)
+{
+ return xics_default_server;
+}
+
+static void alloc_irq_map(struct wsp_ics *ics) { }
+#endif
+
+static void wsp_chip_unmask_irq(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ struct wsp_ics *ics;
+ int server;
+ u64 xive;
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return;
+
+ ics = d->chip_data;
+ if (WARN_ON(!ics))
+ return;
+
+ server = get_irq_server(ics, hw_irq);
+
+ xive = wsp_ics_get_xive(ics, hw_irq);
+ xive = xive_set_server(xive, server);
+ xive = xive_set_priority(xive, DEFAULT_PRIORITY);
+ wsp_ics_set_xive(ics, hw_irq, xive);
+}
+
+static unsigned int wsp_chip_startup(struct irq_data *d)
+{
+ /* unmask it */
+ wsp_chip_unmask_irq(d);
+ return 0;
+}
+
+static void wsp_mask_real_irq(unsigned int hw_irq, struct wsp_ics *ics)
+{
+ u64 xive;
+
+ if (hw_irq == XICS_IPI)
+ return;
+
+ if (WARN_ON(!ics))
+ return;
+ xive = wsp_ics_get_xive(ics, hw_irq);
+ xive = xive_set_server(xive, xics_default_server);
+ xive = xive_set_priority(xive, LOWEST_PRIORITY);
+ wsp_ics_set_xive(ics, hw_irq, xive);
+}
+
+static void wsp_chip_mask_irq(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ struct wsp_ics *ics = d->chip_data;
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return;
+
+ wsp_mask_real_irq(hw_irq, ics);
+}
+
+static int wsp_chip_set_affinity(struct irq_data *d,
+ const struct cpumask *cpumask, bool force)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ struct wsp_ics *ics;
+ int ret;
+ u64 xive;
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return -1;
+
+ ics = d->chip_data;
+ if (WARN_ON(!ics))
+ return -1;
+ xive = wsp_ics_get_xive(ics, hw_irq);
+
+ /*
+ * For the moment only implement delivery to all cpus or one cpu.
+ * Get current irq_server for the given irq
+ */
+ ret = cache_hwirq_map(ics, d->irq, cpumask);
+ if (ret == -1) {
+ char cpulist[128];
+ cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
+ pr_warning("%s: No online cpus in the mask %s for irq %d\n",
+ __func__, cpulist, d->irq);
+ return -1;
+ } else if (ret == -ENOMEM) {
+ pr_warning("%s: Out of memory\n", __func__);
+ return -1;
+ }
+
+ xive = xive_set_server(xive, get_irq_server(ics, hw_irq));
+ wsp_ics_set_xive(ics, hw_irq, xive);
+
+ return 0;
+}
+
+static struct irq_chip wsp_irq_chip = {
+ .name = "WSP ICS",
+ .irq_startup = wsp_chip_startup,
+ .irq_mask = wsp_chip_mask_irq,
+ .irq_unmask = wsp_chip_unmask_irq,
+ .irq_set_affinity = wsp_chip_set_affinity
+};
+
+static int wsp_ics_host_match(struct ics *ics, struct device_node *dn)
+{
+ /* All ICSs in the system implement a global irq number space,
+ * so match against them all. */
+ return of_device_is_compatible(dn, "ibm,ppc-xics");
+}
+
+static int wsp_ics_match_hwirq(struct wsp_ics *wsp_ics, unsigned int hwirq)
+{
+ if (hwirq >= wsp_ics->hwirq_start &&
+ hwirq < wsp_ics->hwirq_start + wsp_ics->count)
+ return 1;
+
+ return 0;
+}
+
+static int wsp_ics_map(struct ics *ics, unsigned int virq)
+{
+ struct wsp_ics *wsp_ics = to_wsp_ics(ics);
+ unsigned int hw_irq = virq_to_hw(virq);
+ unsigned long flags;
+
+ if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
+ return -ENOENT;
+
+ irq_set_chip_and_handler(virq, &wsp_irq_chip, handle_fasteoi_irq);
+
+ irq_set_chip_data(virq, wsp_ics);
+
+ spin_lock_irqsave(&wsp_ics->lock, flags);
+ bitmap_allocate_region(wsp_ics->bitmap, hw_irq - wsp_ics->hwirq_start, 0);
+ spin_unlock_irqrestore(&wsp_ics->lock, flags);
+
+ return 0;
+}
+
+static void wsp_ics_mask_unknown(struct ics *ics, unsigned long hw_irq)
+{
+ struct wsp_ics *wsp_ics = to_wsp_ics(ics);
+
+ if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
+ return;
+
+ pr_err("%s: IRQ %lu (real) is invalid, disabling it.\n", __func__, hw_irq);
+ wsp_mask_real_irq(hw_irq, wsp_ics);
+}
+
+static long wsp_ics_get_server(struct ics *ics, unsigned long hw_irq)
+{
+ struct wsp_ics *wsp_ics = to_wsp_ics(ics);
+
+ if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
+ return -ENOENT;
+
+ return get_irq_server(wsp_ics, hw_irq);
+}
+
+/* HW Number allocation API */
+
+static struct wsp_ics *wsp_ics_find_dn_ics(struct device_node *dn)
+{
+ struct device_node *iparent;
+ int i;
+
+ iparent = of_irq_find_parent(dn);
+ if (!iparent) {
+ pr_err("wsp_ics: Failed to find interrupt parent!\n");
+ return NULL;
+ }
+
+ for(i = 0; i < num_ics; i++) {
+ if(ics_list[i].dn == iparent)
+ break;
+ }
+
+ if (i >= num_ics) {
+ pr_err("wsp_ics: Unable to find parent bitmap!\n");
+ return NULL;
+ }
+
+ return &ics_list[i];
+}
+
+int wsp_ics_alloc_irq(struct device_node *dn, int num)
+{
+ struct wsp_ics *ics;
+ int order, offset;
+
+ ics = wsp_ics_find_dn_ics(dn);
+ if (!ics)
+ return -ENODEV;
+
+ /* Fast, but overly strict if num isn't a power of two */
+ order = get_count_order(num);
+
+ spin_lock_irq(&ics->lock);
+ offset = bitmap_find_free_region(ics->bitmap, ics->count, order);
+ spin_unlock_irq(&ics->lock);
+
+ if (offset < 0)
+ return offset;
+
+ return offset + ics->hwirq_start;
+}
+
+void wsp_ics_free_irq(struct device_node *dn, unsigned int irq)
+{
+ struct wsp_ics *ics;
+
+ ics = wsp_ics_find_dn_ics(dn);
+ if (WARN_ON(!ics))
+ return;
+
+ spin_lock_irq(&ics->lock);
+ bitmap_release_region(ics->bitmap, irq, 0);
+ spin_unlock_irq(&ics->lock);
+}
+
+/* Initialisation */
+
+static int __init wsp_ics_bitmap_setup(struct wsp_ics *ics,
+ struct device_node *dn)
+{
+ int len, i, j, size;
+ u32 start, count;
+ const u32 *p;
+
+ size = BITS_TO_LONGS(ics->count) * sizeof(long);
+ ics->bitmap = kzalloc(size, GFP_KERNEL);
+ if (!ics->bitmap) {
+ pr_err("wsp_ics: ENOMEM allocating IRQ bitmap!\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&ics->lock);
+
+ p = of_get_property(dn, "available-ranges", &len);
+ if (!p || !len) {
+ /* FIXME this should be a WARN() once mambo is updated */
+ pr_err("wsp_ics: No available-ranges defined for %s\n",
+ dn->full_name);
+ return 0;
+ }
+
+ if (len % (2 * sizeof(u32)) != 0) {
+ /* FIXME this should be a WARN() once mambo is updated */
+ pr_err("wsp_ics: Invalid available-ranges for %s\n",
+ dn->full_name);
+ return 0;
+ }
+
+ bitmap_fill(ics->bitmap, ics->count);
+
+ for (i = 0; i < len / sizeof(u32); i += 2) {
+ start = of_read_number(p + i, 1);
+ count = of_read_number(p + i + 1, 1);
+
+ pr_devel("%s: start: %d count: %d\n", __func__, start, count);
+
+ if ((start + count) > (ics->hwirq_start + ics->count) ||
+ start < ics->hwirq_start) {
+ pr_err("wsp_ics: Invalid range! -> %d to %d\n",
+ start, start + count);
+ break;
+ }
+
+ for (j = 0; j < count; j++)
+ bitmap_release_region(ics->bitmap,
+ (start + j) - ics->hwirq_start, 0);
+ }
+
+ /* Ensure LSIs are not available for allocation */
+ bitmap_allocate_region(ics->bitmap, ics->lsi_base,
+ get_count_order(ics->lsi_count));
+
+ return 0;
+}
+
+static int __init wsp_ics_setup(struct wsp_ics *ics, struct device_node *dn)
+{
+ u32 lsi_buid, msi_buid, msi_base, msi_count;
+ void __iomem *regs;
+ const u32 *p;
+ int rc, len, i;
+ u64 caps, buid;
+
+ p = of_get_property(dn, "interrupt-ranges", &len);
+ if (!p || len < (2 * sizeof(u32))) {
+ pr_err("wsp_ics: No/bad interrupt-ranges found on %s\n",
+ dn->full_name);
+ return -ENOENT;
+ }
+
+ if (len > (2 * sizeof(u32))) {
+ pr_err("wsp_ics: Multiple ics ranges not supported.\n");
+ return -EINVAL;
+ }
+
+ regs = of_iomap(dn, 0);
+ if (!regs) {
+ pr_err("wsp_ics: of_iomap(%s) failed\n", dn->full_name);
+ return -ENXIO;
+ }
+
+ ics->hwirq_start = of_read_number(p, 1);
+ ics->count = of_read_number(p + 1, 1);
+ ics->regs = regs;
+
+ ics->chip_id = wsp_get_chip_id(dn);
+ if (WARN_ON(ics->chip_id < 0))
+ ics->chip_id = 0;
+
+ /* Get some informations about the critter */
+ caps = in_be64(ICS_INT_CAPS_REG(ics->regs));
+ buid = in_be64(INT_SRC_LAYER_BUID_REG(ics->regs));
+ ics->lsi_count = caps >> 56;
+ msi_count = (caps >> 44) & 0x7ff;
+
+ /* Note: LSI BUID is 9 bits, but really only 3 are BUID and the
+ * rest is mixed in the interrupt number. We store the whole
+ * thing though
+ */
+ lsi_buid = (buid >> 48) & 0x1ff;
+ ics->lsi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | lsi_buid << 5;
+ msi_buid = (buid >> 37) & 0x7;
+ msi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | msi_buid << 11;
+
+ pr_info("wsp_ics: Found %s\n", dn->full_name);
+ pr_info("wsp_ics: irq range : 0x%06llx..0x%06llx\n",
+ ics->hwirq_start, ics->hwirq_start + ics->count - 1);
+ pr_info("wsp_ics: %4d LSIs : 0x%06x..0x%06x\n",
+ ics->lsi_count, ics->lsi_base,
+ ics->lsi_base + ics->lsi_count - 1);
+ pr_info("wsp_ics: %4d MSIs : 0x%06x..0x%06x\n",
+ msi_count, msi_base,
+ msi_base + msi_count - 1);
+
+ /* Let's check the HW config is sane */
+ if (ics->lsi_base < ics->hwirq_start ||
+ (ics->lsi_base + ics->lsi_count) > (ics->hwirq_start + ics->count))
+ pr_warning("wsp_ics: WARNING ! LSIs out of interrupt-ranges !\n");
+ if (msi_base < ics->hwirq_start ||
+ (msi_base + msi_count) > (ics->hwirq_start + ics->count))
+ pr_warning("wsp_ics: WARNING ! MSIs out of interrupt-ranges !\n");
+
+ /* We don't check for overlap between LSI and MSI, which will happen
+ * if we use the same BUID, I'm not sure yet how legit that is.
+ */
+
+ rc = wsp_ics_bitmap_setup(ics, dn);
+ if (rc) {
+ iounmap(regs);
+ return rc;
+ }
+
+ ics->dn = of_node_get(dn);
+ alloc_irq_map(ics);
+
+ for(i = 0; i < ics->count; i++)
+ wsp_mask_real_irq(ics->hwirq_start + i, ics);
+
+ ics->ics.map = wsp_ics_map;
+ ics->ics.mask_unknown = wsp_ics_mask_unknown;
+ ics->ics.get_server = wsp_ics_get_server;
+ ics->ics.host_match = wsp_ics_host_match;
+
+ xics_register_ics(&ics->ics);
+
+ return 0;
+}
+
+static void __init wsp_ics_set_default_server(void)
+{
+ struct device_node *np;
+ u32 hwid;
+
+ /* Find the server number for the boot cpu. */
+ np = of_get_cpu_node(boot_cpuid, NULL);
+ BUG_ON(!np);
+
+ hwid = get_hard_smp_processor_id(boot_cpuid);
+
+ pr_info("wsp_ics: default server is %#x, CPU %s\n", hwid, np->full_name);
+ xics_default_server = hwid;
+
+ of_node_put(np);
+}
+
+static int __init wsp_ics_init(void)
+{
+ struct device_node *dn;
+ struct wsp_ics *ics;
+ int rc, found;
+
+ wsp_ics_set_default_server();
+
+ found = 0;
+ for_each_compatible_node(dn, NULL, "ibm,ppc-xics")
+ found++;
+
+ if (found == 0) {
+ pr_err("wsp_ics: No ICS's found!\n");
+ return -ENODEV;
+ }
+
+ ics_list = kmalloc(sizeof(*ics) * found, GFP_KERNEL);
+ if (!ics_list) {
+ pr_err("wsp_ics: No memory for structs.\n");
+ return -ENOMEM;
+ }
+
+ num_ics = 0;
+ ics = ics_list;
+ for_each_compatible_node(dn, NULL, "ibm,wsp-xics") {
+ rc = wsp_ics_setup(ics, dn);
+ if (rc == 0) {
+ ics++;
+ num_ics++;
+ }
+ }
+
+ if (found != num_ics) {
+ pr_err("wsp_ics: Failed setting up %d ICS's\n",
+ found - num_ics);
+ return -1;
+ }
+
+ return 0;
+}
+
+void __init wsp_init_irq(void)
+{
+ wsp_ics_init();
+ xics_init();
+
+ /* We need to patch our irq chip's EOI to point to the right ICP */
+ wsp_irq_chip.irq_eoi = icp_ops->eoi;
+}
diff --git a/arch/powerpc/platforms/wsp/ics.h b/arch/powerpc/platforms/wsp/ics.h
new file mode 100644
index 00000000000..e34d5310264
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/ics.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2009 IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef __ICS_H
+#define __ICS_H
+
+#define XIVE_ADDR_MASK 0x7FFULL
+
+extern void wsp_init_irq(void);
+
+extern int wsp_ics_alloc_irq(struct device_node *dn, int num);
+extern void wsp_ics_free_irq(struct device_node *dn, unsigned int irq);
+
+#endif /* __ICS_H */
diff --git a/arch/powerpc/platforms/wsp/opb_pic.c b/arch/powerpc/platforms/wsp/opb_pic.c
new file mode 100644
index 00000000000..be05631a3c1
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/opb_pic.c
@@ -0,0 +1,332 @@
+/*
+ * IBM Onboard Peripheral Bus Interrupt Controller
+ *
+ * Copyright 2010 Jack Miller, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#include <asm/reg_a2.h>
+#include <asm/irq.h>
+
+#define OPB_NR_IRQS 32
+
+#define OPB_MLSASIER 0x04 /* MLS Accumulated Status IER */
+#define OPB_MLSIR 0x50 /* MLS Interrupt Register */
+#define OPB_MLSIER 0x54 /* MLS Interrupt Enable Register */
+#define OPB_MLSIPR 0x58 /* MLS Interrupt Polarity Register */
+#define OPB_MLSIIR 0x5c /* MLS Interrupt Inputs Register */
+
+static int opb_index = 0;
+
+struct opb_pic {
+ struct irq_host *host;
+ void *regs;
+ int index;
+ spinlock_t lock;
+};
+
+static u32 opb_in(struct opb_pic *opb, int offset)
+{
+ return in_be32(opb->regs + offset);
+}
+
+static void opb_out(struct opb_pic *opb, int offset, u32 val)
+{
+ out_be32(opb->regs + offset, val);
+}
+
+static void opb_unmask_irq(struct irq_data *d)
+{
+ struct opb_pic *opb;
+ unsigned long flags;
+ u32 ier, bitset;
+
+ opb = d->chip_data;
+ bitset = (1 << (31 - irqd_to_hwirq(d)));
+
+ spin_lock_irqsave(&opb->lock, flags);
+
+ ier = opb_in(opb, OPB_MLSIER);
+ opb_out(opb, OPB_MLSIER, ier | bitset);
+ ier = opb_in(opb, OPB_MLSIER);
+
+ spin_unlock_irqrestore(&opb->lock, flags);
+}
+
+static void opb_mask_irq(struct irq_data *d)
+{
+ struct opb_pic *opb;
+ unsigned long flags;
+ u32 ier, mask;
+
+ opb = d->chip_data;
+ mask = ~(1 << (31 - irqd_to_hwirq(d)));
+
+ spin_lock_irqsave(&opb->lock, flags);
+
+ ier = opb_in(opb, OPB_MLSIER);
+ opb_out(opb, OPB_MLSIER, ier & mask);
+ ier = opb_in(opb, OPB_MLSIER); // Flush posted writes
+
+ spin_unlock_irqrestore(&opb->lock, flags);
+}
+
+static void opb_ack_irq(struct irq_data *d)
+{
+ struct opb_pic *opb;
+ unsigned long flags;
+ u32 bitset;
+
+ opb = d->chip_data;
+ bitset = (1 << (31 - irqd_to_hwirq(d)));
+
+ spin_lock_irqsave(&opb->lock, flags);
+
+ opb_out(opb, OPB_MLSIR, bitset);
+ opb_in(opb, OPB_MLSIR); // Flush posted writes
+
+ spin_unlock_irqrestore(&opb->lock, flags);
+}
+
+static void opb_mask_ack_irq(struct irq_data *d)
+{
+ struct opb_pic *opb;
+ unsigned long flags;
+ u32 bitset;
+ u32 ier, ir;
+
+ opb = d->chip_data;
+ bitset = (1 << (31 - irqd_to_hwirq(d)));
+
+ spin_lock_irqsave(&opb->lock, flags);
+
+ ier = opb_in(opb, OPB_MLSIER);
+ opb_out(opb, OPB_MLSIER, ier & ~bitset);
+ ier = opb_in(opb, OPB_MLSIER); // Flush posted writes
+
+ opb_out(opb, OPB_MLSIR, bitset);
+ ir = opb_in(opb, OPB_MLSIR); // Flush posted writes
+
+ spin_unlock_irqrestore(&opb->lock, flags);
+}
+
+static int opb_set_irq_type(struct irq_data *d, unsigned int flow)
+{
+ struct opb_pic *opb;
+ unsigned long flags;
+ int invert, ipr, mask, bit;
+
+ opb = d->chip_data;
+
+ /* The only information we're interested in in the type is whether it's
+ * a high or low trigger. For high triggered interrupts, the polarity
+ * set for it in the MLS Interrupt Polarity Register is 0, for low
+ * interrupts it's 1 so that the proper input in the MLS Interrupt Input
+ * Register is interrupted as asserting the interrupt. */
+
+ switch (flow) {
+ case IRQ_TYPE_NONE:
+ opb_mask_irq(d);
+ return 0;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ invert = 0;
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ invert = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ bit = (1 << (31 - irqd_to_hwirq(d)));
+ mask = ~bit;
+
+ spin_lock_irqsave(&opb->lock, flags);
+
+ ipr = opb_in(opb, OPB_MLSIPR);
+ ipr = (ipr & mask) | (invert ? bit : 0);
+ opb_out(opb, OPB_MLSIPR, ipr);
+ ipr = opb_in(opb, OPB_MLSIPR); // Flush posted writes
+
+ spin_unlock_irqrestore(&opb->lock, flags);
+
+ /* Record the type in the interrupt descriptor */
+ irqd_set_trigger_type(d, flow);
+
+ return 0;
+}
+
+static struct irq_chip opb_irq_chip = {
+ .name = "OPB",
+ .irq_mask = opb_mask_irq,
+ .irq_unmask = opb_unmask_irq,
+ .irq_mask_ack = opb_mask_ack_irq,
+ .irq_ack = opb_ack_irq,
+ .irq_set_type = opb_set_irq_type
+};
+
+static int opb_host_map(struct irq_host *host, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct opb_pic *opb;
+
+ opb = host->host_data;
+
+ /* Most of the important stuff is handled by the generic host code, like
+ * the lookup, so just attach some info to the virtual irq */
+
+ irq_set_chip_data(virq, opb);
+ irq_set_chip_and_handler(virq, &opb_irq_chip, handle_level_irq);
+ irq_set_irq_type(virq, IRQ_TYPE_NONE);
+
+ return 0;
+}
+
+static int opb_host_xlate(struct irq_host *host, struct device_node *dn,
+ const u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq, unsigned int *out_type)
+{
+ /* Interrupt size must == 2 */
+ BUG_ON(intsize != 2);
+ *out_hwirq = intspec[0];
+ *out_type = intspec[1];
+ return 0;
+}
+
+static struct irq_host_ops opb_host_ops = {
+ .map = opb_host_map,
+ .xlate = opb_host_xlate,
+};
+
+irqreturn_t opb_irq_handler(int irq, void *private)
+{
+ struct opb_pic *opb;
+ u32 ir, src, subvirq;
+
+ opb = (struct opb_pic *) private;
+
+ /* Read the OPB MLS Interrupt Register for
+ * asserted interrupts */
+ ir = opb_in(opb, OPB_MLSIR);
+ if (!ir)
+ return IRQ_NONE;
+
+ do {
+ /* Get 1 - 32 source, *NOT* bit */
+ src = 32 - ffs(ir);
+
+ /* Translate from the OPB's conception of interrupt number to
+ * Linux's virtual IRQ */
+
+ subvirq = irq_linear_revmap(opb->host, src);
+
+ generic_handle_irq(subvirq);
+ } while ((ir = opb_in(opb, OPB_MLSIR)));
+
+ return IRQ_HANDLED;
+}
+
+struct opb_pic *opb_pic_init_one(struct device_node *dn)
+{
+ struct opb_pic *opb;
+ struct resource res;
+
+ if (of_address_to_resource(dn, 0, &res)) {
+ printk(KERN_ERR "opb: Couldn't translate resource\n");
+ return NULL;
+ }
+
+ opb = kzalloc(sizeof(struct opb_pic), GFP_KERNEL);
+ if (!opb) {
+ printk(KERN_ERR "opb: Failed to allocate opb struct!\n");
+ return NULL;
+ }
+
+ /* Get access to the OPB MMIO registers */
+ opb->regs = ioremap(res.start + 0x10000, 0x1000);
+ if (!opb->regs) {
+ printk(KERN_ERR "opb: Failed to allocate register space!\n");
+ goto free_opb;
+ }
+
+ /* Allocate an irq host so that Linux knows that despite only
+ * having one interrupt to issue, we're the controller for multiple
+ * hardware IRQs, so later we can lookup their virtual IRQs. */
+
+ opb->host = irq_alloc_host(dn, IRQ_HOST_MAP_LINEAR,
+ OPB_NR_IRQS, &opb_host_ops, -1);
+
+ if (!opb->host) {
+ printk(KERN_ERR "opb: Failed to allocate IRQ host!\n");
+ goto free_regs;
+ }
+
+ opb->index = opb_index++;
+ spin_lock_init(&opb->lock);
+ opb->host->host_data = opb;
+
+ /* Disable all interrupts by default */
+ opb_out(opb, OPB_MLSASIER, 0);
+ opb_out(opb, OPB_MLSIER, 0);
+
+ /* ACK any interrupts left by FW */
+ opb_out(opb, OPB_MLSIR, 0xFFFFFFFF);
+
+ return opb;
+
+free_regs:
+ iounmap(opb->regs);
+free_opb:
+ kfree(opb);
+ return NULL;
+}
+
+void __init opb_pic_init(void)
+{
+ struct device_node *dn;
+ struct opb_pic *opb;
+ int virq;
+ int rc;
+
+ /* Call init_one for each OPB device */
+ for_each_compatible_node(dn, NULL, "ibm,opb") {
+
+ /* Fill in an OPB struct */
+ opb = opb_pic_init_one(dn);
+ if (!opb) {
+ printk(KERN_WARNING "opb: Failed to init node, skipped!\n");
+ continue;
+ }
+
+ /* Map / get opb's hardware virtual irq */
+ virq = irq_of_parse_and_map(dn, 0);
+ if (virq <= 0) {
+ printk("opb: irq_op_parse_and_map failed!\n");
+ continue;
+ }
+
+ /* Attach opb interrupt handler to new virtual IRQ */
+ rc = request_irq(virq, opb_irq_handler, 0, "OPB LS Cascade", opb);
+ if (rc) {
+ printk("opb: request_irq failed: %d\n", rc);
+ continue;
+ }
+
+ printk("OPB%d init with %d IRQs at %p\n", opb->index,
+ OPB_NR_IRQS, opb->regs);
+ }
+}
diff --git a/arch/powerpc/platforms/wsp/psr2.c b/arch/powerpc/platforms/wsp/psr2.c
new file mode 100644
index 00000000000..40f28916ff6
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/psr2.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2008-2011, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/smp.h>
+
+#include <asm/machdep.h>
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/udbg.h>
+
+#include "ics.h"
+#include "wsp.h"
+
+
+static void psr2_spin(void)
+{
+ hard_irq_disable();
+ for (;;) ;
+}
+
+static void psr2_restart(char *cmd)
+{
+ psr2_spin();
+}
+
+static int psr2_probe_devices(void)
+{
+ struct device_node *np;
+
+ /* Our RTC is a ds1500. It seems to be programatically compatible
+ * with the ds1511 for which we have a driver so let's use that
+ */
+ np = of_find_compatible_node(NULL, NULL, "dallas,ds1500");
+ if (np != NULL) {
+ struct resource res;
+ if (of_address_to_resource(np, 0, &res) == 0)
+ platform_device_register_simple("ds1511", 0, &res, 1);
+ }
+ return 0;
+}
+machine_arch_initcall(psr2_md, psr2_probe_devices);
+
+static void __init psr2_setup_arch(void)
+{
+ /* init to some ~sane value until calibrate_delay() runs */
+ loops_per_jiffy = 50000000;
+
+ scom_init_wsp();
+
+ /* Setup SMP callback */
+#ifdef CONFIG_SMP
+ a2_setup_smp();
+#endif
+}
+
+static int __init psr2_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ if (!of_flat_dt_is_compatible(root, "ibm,psr2"))
+ return 0;
+
+ return 1;
+}
+
+static void __init psr2_init_irq(void)
+{
+ wsp_init_irq();
+ opb_pic_init();
+}
+
+define_machine(psr2_md) {
+ .name = "PSR2 A2",
+ .probe = psr2_probe,
+ .setup_arch = psr2_setup_arch,
+ .restart = psr2_restart,
+ .power_off = psr2_spin,
+ .halt = psr2_spin,
+ .calibrate_decr = generic_calibrate_decr,
+ .init_IRQ = psr2_init_irq,
+ .progress = udbg_progress,
+ .power_save = book3e_idle,
+};
diff --git a/arch/powerpc/platforms/wsp/scom_smp.c b/arch/powerpc/platforms/wsp/scom_smp.c
new file mode 100644
index 00000000000..141e7803209
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/scom_smp.c
@@ -0,0 +1,427 @@
+/*
+ * SCOM support for A2 platforms
+ *
+ * Copyright 2007-2011 Benjamin Herrenschmidt, David Gibson,
+ * Michael Ellerman, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/cputhreads.h>
+#include <asm/reg_a2.h>
+#include <asm/scom.h>
+#include <asm/udbg.h>
+
+#include "wsp.h"
+
+#define SCOM_RAMC 0x2a /* Ram Command */
+#define SCOM_RAMC_TGT1_EXT 0x80000000
+#define SCOM_RAMC_SRC1_EXT 0x40000000
+#define SCOM_RAMC_SRC2_EXT 0x20000000
+#define SCOM_RAMC_SRC3_EXT 0x10000000
+#define SCOM_RAMC_ENABLE 0x00080000
+#define SCOM_RAMC_THREADSEL 0x00060000
+#define SCOM_RAMC_EXECUTE 0x00010000
+#define SCOM_RAMC_MSR_OVERRIDE 0x00008000
+#define SCOM_RAMC_MSR_PR 0x00004000
+#define SCOM_RAMC_MSR_GS 0x00002000
+#define SCOM_RAMC_FORCE 0x00001000
+#define SCOM_RAMC_FLUSH 0x00000800
+#define SCOM_RAMC_INTERRUPT 0x00000004
+#define SCOM_RAMC_ERROR 0x00000002
+#define SCOM_RAMC_DONE 0x00000001
+#define SCOM_RAMI 0x29 /* Ram Instruction */
+#define SCOM_RAMIC 0x28 /* Ram Instruction and Command */
+#define SCOM_RAMIC_INSN 0xffffffff00000000
+#define SCOM_RAMD 0x2d /* Ram Data */
+#define SCOM_RAMDH 0x2e /* Ram Data High */
+#define SCOM_RAMDL 0x2f /* Ram Data Low */
+#define SCOM_PCCR0 0x33 /* PC Configuration Register 0 */
+#define SCOM_PCCR0_ENABLE_DEBUG 0x80000000
+#define SCOM_PCCR0_ENABLE_RAM 0x40000000
+#define SCOM_THRCTL 0x30 /* Thread Control and Status */
+#define SCOM_THRCTL_T0_STOP 0x80000000
+#define SCOM_THRCTL_T1_STOP 0x40000000
+#define SCOM_THRCTL_T2_STOP 0x20000000
+#define SCOM_THRCTL_T3_STOP 0x10000000
+#define SCOM_THRCTL_T0_STEP 0x08000000
+#define SCOM_THRCTL_T1_STEP 0x04000000
+#define SCOM_THRCTL_T2_STEP 0x02000000
+#define SCOM_THRCTL_T3_STEP 0x01000000
+#define SCOM_THRCTL_T0_RUN 0x00800000
+#define SCOM_THRCTL_T1_RUN 0x00400000
+#define SCOM_THRCTL_T2_RUN 0x00200000
+#define SCOM_THRCTL_T3_RUN 0x00100000
+#define SCOM_THRCTL_T0_PM 0x00080000
+#define SCOM_THRCTL_T1_PM 0x00040000
+#define SCOM_THRCTL_T2_PM 0x00020000
+#define SCOM_THRCTL_T3_PM 0x00010000
+#define SCOM_THRCTL_T0_UDE 0x00008000
+#define SCOM_THRCTL_T1_UDE 0x00004000
+#define SCOM_THRCTL_T2_UDE 0x00002000
+#define SCOM_THRCTL_T3_UDE 0x00001000
+#define SCOM_THRCTL_ASYNC_DIS 0x00000800
+#define SCOM_THRCTL_TB_DIS 0x00000400
+#define SCOM_THRCTL_DEC_DIS 0x00000200
+#define SCOM_THRCTL_AND 0x31 /* Thread Control and Status */
+#define SCOM_THRCTL_OR 0x32 /* Thread Control and Status */
+
+
+static DEFINE_PER_CPU(scom_map_t, scom_ptrs);
+
+static scom_map_t get_scom(int cpu, struct device_node *np, int *first_thread)
+{
+ scom_map_t scom = per_cpu(scom_ptrs, cpu);
+ int tcpu;
+
+ if (scom_map_ok(scom)) {
+ *first_thread = 0;
+ return scom;
+ }
+
+ *first_thread = 1;
+
+ scom = scom_map_device(np, 0);
+
+ for (tcpu = cpu_first_thread_sibling(cpu);
+ tcpu <= cpu_last_thread_sibling(cpu); tcpu++)
+ per_cpu(scom_ptrs, tcpu) = scom;
+
+ /* Hack: for the boot core, this will actually get called on
+ * the second thread up, not the first so our test above will
+ * set first_thread incorrectly. */
+ if (cpu_first_thread_sibling(cpu) == 0)
+ *first_thread = 0;
+
+ return scom;
+}
+
+static int a2_scom_ram(scom_map_t scom, int thread, u32 insn, int extmask)
+{
+ u64 cmd, mask, val;
+ int n = 0;
+
+ cmd = ((u64)insn << 32) | (((u64)extmask & 0xf) << 28)
+ | ((u64)thread << 17) | SCOM_RAMC_ENABLE | SCOM_RAMC_EXECUTE;
+ mask = SCOM_RAMC_DONE | SCOM_RAMC_INTERRUPT | SCOM_RAMC_ERROR;
+
+ scom_write(scom, SCOM_RAMIC, cmd);
+
+ while (!((val = scom_read(scom, SCOM_RAMC)) & mask)) {
+ pr_devel("Waiting on RAMC = 0x%llx\n", val);
+ if (++n == 3) {
+ pr_err("RAMC timeout on instruction 0x%08x, thread %d\n",
+ insn, thread);
+ return -1;
+ }
+ }
+
+ if (val & SCOM_RAMC_INTERRUPT) {
+ pr_err("RAMC interrupt on instruction 0x%08x, thread %d\n",
+ insn, thread);
+ return -SCOM_RAMC_INTERRUPT;
+ }
+
+ if (val & SCOM_RAMC_ERROR) {
+ pr_err("RAMC error on instruction 0x%08x, thread %d\n",
+ insn, thread);
+ return -SCOM_RAMC_ERROR;
+ }
+
+ return 0;
+}
+
+static int a2_scom_getgpr(scom_map_t scom, int thread, int gpr, int alt,
+ u64 *out_gpr)
+{
+ int rc;
+
+ /* or rN, rN, rN */
+ u32 insn = 0x7c000378 | (gpr << 21) | (gpr << 16) | (gpr << 11);
+ rc = a2_scom_ram(scom, thread, insn, alt ? 0xf : 0x0);
+ if (rc)
+ return rc;
+
+ *out_gpr = scom_read(scom, SCOM_RAMD);
+
+ return 0;
+}
+
+static int a2_scom_getspr(scom_map_t scom, int thread, int spr, u64 *out_spr)
+{
+ int rc, sprhi, sprlo;
+ u32 insn;
+
+ sprhi = spr >> 5;
+ sprlo = spr & 0x1f;
+ insn = 0x7c2002a6 | (sprlo << 16) | (sprhi << 11); /* mfspr r1,spr */
+
+ if (spr == 0x0ff0)
+ insn = 0x7c2000a6; /* mfmsr r1 */
+
+ rc = a2_scom_ram(scom, thread, insn, 0xf);
+ if (rc)
+ return rc;
+ return a2_scom_getgpr(scom, thread, 1, 1, out_spr);
+}
+
+static int a2_scom_setgpr(scom_map_t scom, int thread, int gpr,
+ int alt, u64 val)
+{
+ u32 lis = 0x3c000000 | (gpr << 21);
+ u32 li = 0x38000000 | (gpr << 21);
+ u32 oris = 0x64000000 | (gpr << 21) | (gpr << 16);
+ u32 ori = 0x60000000 | (gpr << 21) | (gpr << 16);
+ u32 rldicr32 = 0x780007c6 | (gpr << 21) | (gpr << 16);
+ u32 highest = val >> 48;
+ u32 higher = (val >> 32) & 0xffff;
+ u32 high = (val >> 16) & 0xffff;
+ u32 low = val & 0xffff;
+ int lext = alt ? 0x8 : 0x0;
+ int oext = alt ? 0xf : 0x0;
+ int rc = 0;
+
+ if (highest)
+ rc |= a2_scom_ram(scom, thread, lis | highest, lext);
+
+ if (higher) {
+ if (highest)
+ rc |= a2_scom_ram(scom, thread, oris | higher, oext);
+ else
+ rc |= a2_scom_ram(scom, thread, li | higher, lext);
+ }
+
+ if (highest || higher)
+ rc |= a2_scom_ram(scom, thread, rldicr32, oext);
+
+ if (high) {
+ if (highest || higher)
+ rc |= a2_scom_ram(scom, thread, oris | high, oext);
+ else
+ rc |= a2_scom_ram(scom, thread, lis | high, lext);
+ }
+
+ if (highest || higher || high)
+ rc |= a2_scom_ram(scom, thread, ori | low, oext);
+ else
+ rc |= a2_scom_ram(scom, thread, li | low, lext);
+
+ return rc;
+}
+
+static int a2_scom_setspr(scom_map_t scom, int thread, int spr, u64 val)
+{
+ int sprhi = spr >> 5;
+ int sprlo = spr & 0x1f;
+ /* mtspr spr, r1 */
+ u32 insn = 0x7c2003a6 | (sprlo << 16) | (sprhi << 11);
+
+ if (spr == 0x0ff0)
+ insn = 0x7c200124; /* mtmsr r1 */
+
+ if (a2_scom_setgpr(scom, thread, 1, 1, val))
+ return -1;
+
+ return a2_scom_ram(scom, thread, insn, 0xf);
+}
+
+static int a2_scom_initial_tlb(scom_map_t scom, int thread)
+{
+ extern u32 a2_tlbinit_code_start[], a2_tlbinit_code_end[];
+ extern u32 a2_tlbinit_after_iprot_flush[];
+ extern u32 a2_tlbinit_after_linear_map[];
+ u32 assoc, entries, i;
+ u64 epn, tlbcfg;
+ u32 *p;
+ int rc;
+
+ /* Invalidate all entries (including iprot) */
+
+ rc = a2_scom_getspr(scom, thread, SPRN_TLB0CFG, &tlbcfg);
+ if (rc)
+ goto scom_fail;
+ entries = tlbcfg & TLBnCFG_N_ENTRY;
+ assoc = (tlbcfg & TLBnCFG_ASSOC) >> 24;
+ epn = 0;
+
+ /* Set MMUCR2 to enable 4K, 64K, 1M, 16M and 1G pages */
+ a2_scom_setspr(scom, thread, SPRN_MMUCR2, 0x000a7531);
+ /* Set MMUCR3 to write all thids bit to the TLB */
+ a2_scom_setspr(scom, thread, SPRN_MMUCR3, 0x0000000f);
+
+ /* Set MAS1 for 1G page size, and MAS2 to our initial EPN */
+ a2_scom_setspr(scom, thread, SPRN_MAS1, MAS1_TSIZE(BOOK3E_PAGESZ_1GB));
+ a2_scom_setspr(scom, thread, SPRN_MAS2, epn);
+ for (i = 0; i < entries; i++) {
+
+ a2_scom_setspr(scom, thread, SPRN_MAS0, MAS0_ESEL(i % assoc));
+
+ /* tlbwe */
+ rc = a2_scom_ram(scom, thread, 0x7c0007a4, 0);
+ if (rc)
+ goto scom_fail;
+
+ /* Next entry is new address? */
+ if((i + 1) % assoc == 0) {
+ epn += (1 << 30);
+ a2_scom_setspr(scom, thread, SPRN_MAS2, epn);
+ }
+ }
+
+ /* Setup args for linear mapping */
+ rc = a2_scom_setgpr(scom, thread, 3, 0, MAS0_TLBSEL(0));
+ if (rc)
+ goto scom_fail;
+
+ /* Linear mapping */
+ for (p = a2_tlbinit_code_start; p < a2_tlbinit_after_linear_map; p++) {
+ rc = a2_scom_ram(scom, thread, *p, 0);
+ if (rc)
+ goto scom_fail;
+ }
+
+ /*
+ * For the boot thread, between the linear mapping and the debug
+ * mappings there is a loop to flush iprot mappings. Ramming doesn't do
+ * branches, but the secondary threads don't need to be nearly as smart
+ * (i.e. we don't need to worry about invalidating the mapping we're
+ * standing on).
+ */
+
+ /* Debug mappings. Expects r11 = MAS0 from linear map (set above) */
+ for (p = a2_tlbinit_after_iprot_flush; p < a2_tlbinit_code_end; p++) {
+ rc = a2_scom_ram(scom, thread, *p, 0);
+ if (rc)
+ goto scom_fail;
+ }
+
+scom_fail:
+ if (rc)
+ pr_err("Setting up initial TLB failed, err %d\n", rc);
+
+ if (rc == -SCOM_RAMC_INTERRUPT) {
+ /* Interrupt, dump some status */
+ int rc[10];
+ u64 iar, srr0, srr1, esr, mas0, mas1, mas2, mas7_3, mas8, ccr2;
+ rc[0] = a2_scom_getspr(scom, thread, SPRN_IAR, &iar);
+ rc[1] = a2_scom_getspr(scom, thread, SPRN_SRR0, &srr0);
+ rc[2] = a2_scom_getspr(scom, thread, SPRN_SRR1, &srr1);
+ rc[3] = a2_scom_getspr(scom, thread, SPRN_ESR, &esr);
+ rc[4] = a2_scom_getspr(scom, thread, SPRN_MAS0, &mas0);
+ rc[5] = a2_scom_getspr(scom, thread, SPRN_MAS1, &mas1);
+ rc[6] = a2_scom_getspr(scom, thread, SPRN_MAS2, &mas2);
+ rc[7] = a2_scom_getspr(scom, thread, SPRN_MAS7_MAS3, &mas7_3);
+ rc[8] = a2_scom_getspr(scom, thread, SPRN_MAS8, &mas8);
+ rc[9] = a2_scom_getspr(scom, thread, SPRN_A2_CCR2, &ccr2);
+ pr_err(" -> retreived IAR =0x%llx (err %d)\n", iar, rc[0]);
+ pr_err(" retreived SRR0=0x%llx (err %d)\n", srr0, rc[1]);
+ pr_err(" retreived SRR1=0x%llx (err %d)\n", srr1, rc[2]);
+ pr_err(" retreived ESR =0x%llx (err %d)\n", esr, rc[3]);
+ pr_err(" retreived MAS0=0x%llx (err %d)\n", mas0, rc[4]);
+ pr_err(" retreived MAS1=0x%llx (err %d)\n", mas1, rc[5]);
+ pr_err(" retreived MAS2=0x%llx (err %d)\n", mas2, rc[6]);
+ pr_err(" retreived MS73=0x%llx (err %d)\n", mas7_3, rc[7]);
+ pr_err(" retreived MAS8=0x%llx (err %d)\n", mas8, rc[8]);
+ pr_err(" retreived CCR2=0x%llx (err %d)\n", ccr2, rc[9]);
+ }
+
+ return rc;
+}
+
+int __devinit a2_scom_startup_cpu(unsigned int lcpu, int thr_idx,
+ struct device_node *np)
+{
+ u64 init_iar, init_msr, init_ccr2;
+ unsigned long start_here;
+ int rc, core_setup;
+ scom_map_t scom;
+ u64 pccr0;
+
+ scom = get_scom(lcpu, np, &core_setup);
+ if (!scom) {
+ printk(KERN_ERR "Couldn't map SCOM for CPU%d\n", lcpu);
+ return -1;
+ }
+
+ pr_devel("Bringing up CPU%d using SCOM...\n", lcpu);
+
+ pccr0 = scom_read(scom, SCOM_PCCR0);
+ scom_write(scom, SCOM_PCCR0, pccr0 | SCOM_PCCR0_ENABLE_DEBUG |
+ SCOM_PCCR0_ENABLE_RAM);
+
+ /* Stop the thead with THRCTL. If we are setting up the TLB we stop all
+ * threads. We also disable asynchronous interrupts while RAMing.
+ */
+ if (core_setup)
+ scom_write(scom, SCOM_THRCTL_OR,
+ SCOM_THRCTL_T0_STOP |
+ SCOM_THRCTL_T1_STOP |
+ SCOM_THRCTL_T2_STOP |
+ SCOM_THRCTL_T3_STOP |
+ SCOM_THRCTL_ASYNC_DIS);
+ else
+ scom_write(scom, SCOM_THRCTL_OR, SCOM_THRCTL_T0_STOP >> thr_idx);
+
+ /* Flush its pipeline just in case */
+ scom_write(scom, SCOM_RAMC, ((u64)thr_idx << 17) |
+ SCOM_RAMC_FLUSH | SCOM_RAMC_ENABLE);
+
+ a2_scom_getspr(scom, thr_idx, SPRN_IAR, &init_iar);
+ a2_scom_getspr(scom, thr_idx, 0x0ff0, &init_msr);
+ a2_scom_getspr(scom, thr_idx, SPRN_A2_CCR2, &init_ccr2);
+
+ /* Set MSR to MSR_CM (0x0ff0 is magic value for MSR_CM) */
+ rc = a2_scom_setspr(scom, thr_idx, 0x0ff0, MSR_CM);
+ if (rc) {
+ pr_err("Failed to set MSR ! err %d\n", rc);
+ return rc;
+ }
+
+ /* RAM in an sync/isync for the sake of it */
+ a2_scom_ram(scom, thr_idx, 0x7c0004ac, 0);
+ a2_scom_ram(scom, thr_idx, 0x4c00012c, 0);
+
+ if (core_setup) {
+ pr_devel("CPU%d is first thread in core, initializing TLB...\n",
+ lcpu);
+ rc = a2_scom_initial_tlb(scom, thr_idx);
+ if (rc)
+ goto fail;
+ }
+
+ start_here = *(unsigned long *)(core_setup ? generic_secondary_smp_init
+ : generic_secondary_thread_init);
+ pr_devel("CPU%d entry point at 0x%lx...\n", lcpu, start_here);
+
+ rc |= a2_scom_setspr(scom, thr_idx, SPRN_IAR, start_here);
+ rc |= a2_scom_setgpr(scom, thr_idx, 3, 0,
+ get_hard_smp_processor_id(lcpu));
+ /*
+ * Tell book3e_secondary_core_init not to set up the TLB, we've
+ * already done that.
+ */
+ rc |= a2_scom_setgpr(scom, thr_idx, 4, 0, 1);
+
+ rc |= a2_scom_setspr(scom, thr_idx, SPRN_TENS, 0x1 << thr_idx);
+
+ scom_write(scom, SCOM_RAMC, 0);
+ scom_write(scom, SCOM_THRCTL_AND, ~(SCOM_THRCTL_T0_STOP >> thr_idx));
+ scom_write(scom, SCOM_PCCR0, pccr0);
+fail:
+ pr_devel(" SCOM initialization %s\n", rc ? "failed" : "succeeded");
+ if (rc) {
+ pr_err("Old IAR=0x%08llx MSR=0x%08llx CCR2=0x%08llx\n",
+ init_iar, init_msr, init_ccr2);
+ }
+
+ return rc;
+}
diff --git a/arch/powerpc/platforms/wsp/scom_wsp.c b/arch/powerpc/platforms/wsp/scom_wsp.c
new file mode 100644
index 00000000000..4052e2259f3
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/scom_wsp.c
@@ -0,0 +1,77 @@
+/*
+ * SCOM backend for WSP
+ *
+ * Copyright 2010 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/cputhreads.h>
+#include <asm/reg_a2.h>
+#include <asm/scom.h>
+#include <asm/udbg.h>
+
+#include "wsp.h"
+
+
+static scom_map_t wsp_scom_map(struct device_node *dev, u64 reg, u64 count)
+{
+ struct resource r;
+ u64 xscom_addr;
+
+ if (!of_get_property(dev, "scom-controller", NULL)) {
+ pr_err("%s: device %s is not a SCOM controller\n",
+ __func__, dev->full_name);
+ return SCOM_MAP_INVALID;
+ }
+
+ if (of_address_to_resource(dev, 0, &r)) {
+ pr_debug("Failed to find SCOM controller address\n");
+ return 0;
+ }
+
+ /* Transform the SCOM address into an XSCOM offset */
+ xscom_addr = ((reg & 0x7f000000) >> 1) | ((reg & 0xfffff) << 3);
+
+ return (scom_map_t)ioremap(r.start + xscom_addr, count << 3);
+}
+
+static void wsp_scom_unmap(scom_map_t map)
+{
+ iounmap((void *)map);
+}
+
+static u64 wsp_scom_read(scom_map_t map, u32 reg)
+{
+ u64 __iomem *addr = (u64 __iomem *)map;
+
+ return in_be64(addr + reg);
+}
+
+static void wsp_scom_write(scom_map_t map, u32 reg, u64 value)
+{
+ u64 __iomem *addr = (u64 __iomem *)map;
+
+ return out_be64(addr + reg, value);
+}
+
+static const struct scom_controller wsp_scom_controller = {
+ .map = wsp_scom_map,
+ .unmap = wsp_scom_unmap,
+ .read = wsp_scom_read,
+ .write = wsp_scom_write
+};
+
+void scom_init_wsp(void)
+{
+ scom_init(&wsp_scom_controller);
+}
diff --git a/arch/powerpc/platforms/wsp/setup.c b/arch/powerpc/platforms/wsp/setup.c
new file mode 100644
index 00000000000..11ac2f05e01
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/setup.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 Michael Ellerman, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+
+#include "wsp.h"
+
+/*
+ * Find chip-id by walking up device tree looking for ibm,wsp-chip-id property.
+ * Won't work for nodes that are not a descendant of a wsp node.
+ */
+int wsp_get_chip_id(struct device_node *dn)
+{
+ const u32 *p;
+ int rc;
+
+ /* Start looking at the specified node, not its parent */
+ dn = of_node_get(dn);
+ while (dn && !(p = of_get_property(dn, "ibm,wsp-chip-id", NULL)))
+ dn = of_get_next_parent(dn);
+
+ if (!dn)
+ return -1;
+
+ rc = *p;
+ of_node_put(dn);
+
+ return rc;
+}
diff --git a/arch/powerpc/platforms/wsp/smp.c b/arch/powerpc/platforms/wsp/smp.c
new file mode 100644
index 00000000000..9d20fa9d371
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/smp.c
@@ -0,0 +1,88 @@
+/*
+ * SMP Support for A2 platforms
+ *
+ * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/smp.h>
+
+#include <asm/dbell.h>
+#include <asm/machdep.h>
+#include <asm/xics.h>
+
+#include "ics.h"
+#include "wsp.h"
+
+static void __devinit smp_a2_setup_cpu(int cpu)
+{
+ doorbell_setup_this_cpu();
+
+ if (cpu != boot_cpuid)
+ xics_setup_cpu();
+}
+
+int __devinit smp_a2_kick_cpu(int nr)
+{
+ const char *enable_method;
+ struct device_node *np;
+ int thr_idx;
+
+ if (nr < 0 || nr >= NR_CPUS)
+ return -ENOENT;
+
+ np = of_get_cpu_node(nr, &thr_idx);
+ if (!np)
+ return -ENODEV;
+
+ enable_method = of_get_property(np, "enable-method", NULL);
+ pr_devel("CPU%d has enable-method: \"%s\"\n", nr, enable_method);
+
+ if (!enable_method) {
+ printk(KERN_ERR "CPU%d has no enable-method\n", nr);
+ return -ENOENT;
+ } else if (strcmp(enable_method, "ibm,a2-scom") == 0) {
+ if (a2_scom_startup_cpu(nr, thr_idx, np))
+ return -1;
+ } else {
+ printk(KERN_ERR "CPU%d: Don't understand enable-method \"%s\"\n",
+ nr, enable_method);
+ return -EINVAL;
+ }
+
+ /*
+ * The processor is currently spinning, waiting for the
+ * cpu_start field to become non-zero After we set cpu_start,
+ * the processor will continue on to secondary_start
+ */
+ paca[nr].cpu_start = 1;
+
+ return 0;
+}
+
+static int __init smp_a2_probe(void)
+{
+ return cpus_weight(cpu_possible_map);
+}
+
+static struct smp_ops_t a2_smp_ops = {
+ .message_pass = smp_muxed_ipi_message_pass,
+ .cause_ipi = doorbell_cause_ipi,
+ .probe = smp_a2_probe,
+ .kick_cpu = smp_a2_kick_cpu,
+ .setup_cpu = smp_a2_setup_cpu,
+};
+
+void __init a2_setup_smp(void)
+{
+ smp_ops = &a2_smp_ops;
+}
diff --git a/arch/powerpc/platforms/wsp/wsp.h b/arch/powerpc/platforms/wsp/wsp.h
new file mode 100644
index 00000000000..7c3e087fd2f
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/wsp.h
@@ -0,0 +1,17 @@
+#ifndef __WSP_H
+#define __WSP_H
+
+#include <asm/wsp.h>
+
+extern void wsp_setup_pci(void);
+extern void scom_init_wsp(void);
+
+extern void a2_setup_smp(void);
+extern int a2_scom_startup_cpu(unsigned int lcpu, int thr_idx,
+ struct device_node *np);
+int smp_a2_cpu_bootable(unsigned int nr);
+int __devinit smp_a2_kick_cpu(int nr);
+
+void opb_pic_init(void);
+
+#endif /* __WSP_H */