diff options
Diffstat (limited to 'arch/powerpc/platforms')
55 files changed, 1556 insertions, 237 deletions
diff --git a/arch/powerpc/platforms/44x/44x.h b/arch/powerpc/platforms/44x/44x.h new file mode 100644 index 00000000000..42eabf87fea --- /dev/null +++ b/arch/powerpc/platforms/44x/44x.h @@ -0,0 +1,8 @@ +#ifndef __POWERPC_PLATFORMS_44X_44X_H +#define __POWERPC_PLATFORMS_44X_44X_H + +extern u8 as1_readb(volatile u8 __iomem *addr); +extern void as1_writeb(u8 data, volatile u8 __iomem *addr); +extern void ppc44x_reset_system(char *cmd); + +#endif /* __POWERPC_PLATFORMS_44X_44X_H */ diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig new file mode 100644 index 00000000000..8e66949e7c6 --- /dev/null +++ b/arch/powerpc/platforms/44x/Kconfig @@ -0,0 +1,56 @@ +#config BAMBOO +# bool "Bamboo" +# depends on 44x +# default n +# select 440EP +# help +# This option enables support for the IBM PPC440EP evaluation board. + +config EBONY + bool "Ebony" + depends on 44x + default y + select 440GP + help + This option enables support for the IBM PPC440GP evaluation board. + +#config LUAN +# bool "Luan" +# depends on 44x +# default n +# select 440SP +# help +# This option enables support for the IBM PPC440SP evaluation board. + +#config OCOTEA +# bool "Ocotea" +# depends on 44x +# default n +# select 440GX +# help +# This option enables support for the IBM PPC440GX evaluation board. + +# 44x specific CPU modules, selected based on the board above. +config 440EP + bool + select PPC_FPU + select IBM440EP_ERR42 + +config 440GP + bool + select IBM_NEW_EMAC_ZMII + +config 440GX + bool + +config 440SP + bool + +config 440A + bool + depends on 440GX + default y + +# 44x errata/workaround config symbols, selected by the CPU models above +config IBM440EP_ERR42 + bool diff --git a/arch/powerpc/platforms/44x/Makefile b/arch/powerpc/platforms/44x/Makefile new file mode 100644 index 00000000000..41d0a18a0e4 --- /dev/null +++ b/arch/powerpc/platforms/44x/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_44x) := misc_44x.o +obj-$(CONFIG_EBONY) += ebony.o diff --git a/arch/powerpc/platforms/44x/ebony.c b/arch/powerpc/platforms/44x/ebony.c new file mode 100644 index 00000000000..ad526eafc90 --- /dev/null +++ b/arch/powerpc/platforms/44x/ebony.c @@ -0,0 +1,73 @@ +/* + * Ebony board specific routines + * + * Matt Porter <mporter@kernel.crashing.org> + * Copyright 2002-2005 MontaVista Software Inc. + * + * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> + * Copyright (c) 2003-2005 Zultys Technologies + * + * Rewritten and ported to the merged powerpc tree: + * Copyright 2007 David Gibson <dwg@au1.ibm.com>, 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/init.h> +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/of_platform.h> + +#include "44x.h" + +static struct of_device_id ebony_of_bus[] = { + { .type = "ibm,plb", }, + { .type = "ibm,opb", }, + { .type = "ibm,ebc", }, + {}, +}; + +static int __init ebony_device_probe(void) +{ + if (!machine_is(ebony)) + return 0; + + of_platform_bus_probe(NULL, ebony_of_bus, NULL); + + return 0; +} +device_initcall(ebony_device_probe); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init ebony_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ibm,ebony")) + return 0; + + return 1; +} + +static void __init ebony_setup_arch(void) +{ +} + +define_machine(ebony) { + .name = "Ebony", + .probe = ebony_probe, + .setup_arch = ebony_setup_arch, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc44x_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/44x/misc_44x.S b/arch/powerpc/platforms/44x/misc_44x.S new file mode 100644 index 00000000000..3bce71d5d75 --- /dev/null +++ b/arch/powerpc/platforms/44x/misc_44x.S @@ -0,0 +1,57 @@ +/* + * This file contains miscellaneous low-level functions for PPC 44x. + * Copyright 2007 David Gibson <dwg@au1.ibm.com>, 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 <asm/reg.h> +#include <asm/ppc_asm.h> + + .text + +/* + * Do an IO access in AS1 + */ +_GLOBAL(as1_readb) + mfmsr r7 + ori r0,r7,MSR_DS + sync + mtmsr r0 + sync + isync + lbz r3,0(r3) + sync + mtmsr r7 + sync + isync + blr + +_GLOBAL(as1_writeb) + mfmsr r7 + ori r0,r7,MSR_DS + sync + mtmsr r0 + sync + isync + stb r3,0(r4) + sync + mtmsr r7 + sync + isync + blr + +/* + * void ppc44x_reset_system(char *cmd) + * + * At present, this routine just applies a system reset. + */ +_GLOBAL(ppc44x_reset_system) + mfspr r13,SPRN_DBCR0 + oris r13,r13,DBCR0_RST_SYSTEM@h + mtspr SPRN_DBCR0,r13 + b . /* Just in case the reset doesn't work */ diff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig index bc4aa4a80a1..3ffaa066c2c 100644 --- a/arch/powerpc/platforms/52xx/Kconfig +++ b/arch/powerpc/platforms/52xx/Kconfig @@ -1,5 +1,6 @@ config PPC_MPC52xx bool + select FSL_SOC default n config PPC_MPC5200 diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile index 07cdbcacf15..b91e39c84d4 100644 --- a/arch/powerpc/platforms/52xx/Makefile +++ b/arch/powerpc/platforms/52xx/Makefile @@ -8,3 +8,5 @@ endif obj-$(CONFIG_PPC_EFIKA) += efika.o obj-$(CONFIG_PPC_LITE5200) += lite5200.o + +obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c index a6bba97314e..f591a9fc19b 100644 --- a/arch/powerpc/platforms/52xx/efika.c +++ b/arch/powerpc/platforms/52xx/efika.c @@ -184,6 +184,16 @@ static void efika_show_cpuinfo(struct seq_file *m) of_node_put(root); } +#ifdef CONFIG_PM +static void efika_suspend_prepare(void __iomem *mbar) +{ + u8 pin = 4; /* GPIO_WKUP_4 (GPIO_PSC6_0 - IRDA_RX) */ + u8 level = 1; /* wakeup on high level */ + /* IOW. to wake it up, short pins 1 and 3 on IRDA connector */ + mpc52xx_set_wakeup_gpio(pin, level); +} +#endif + static void __init efika_setup_arch(void) { rtas_initialize(); @@ -199,6 +209,11 @@ static void __init efika_setup_arch(void) efika_pcisetup(); +#ifdef CONFIG_PM + mpc52xx_suspend.board_suspend_prepare = efika_suspend_prepare; + mpc52xx_pm_init(); +#endif + if (ppc_md.progress) ppc_md.progress("Linux/PPC " UTS_RELEASE " running on Efika ;-)\n", 0x0); } diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index 8e2646ac417..1cfc00dfb99 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -85,6 +85,28 @@ error: iounmap(gpio); } +#ifdef CONFIG_PM +static u32 descr_a; +static void lite5200_suspend_prepare(void __iomem *mbar) +{ + u8 pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */ + u8 level = 0; /* wakeup on low level */ + mpc52xx_set_wakeup_gpio(pin, level); + + /* + * power down usb port + * this needs to be called before of-ohci suspend code + */ + descr_a = in_be32(mbar + 0x1048); + out_be32(mbar + 0x1048, (descr_a & ~0x200) | 0x100); +} + +static void lite5200_resume_finish(void __iomem *mbar) +{ + out_be32(mbar + 0x1048, descr_a); +} +#endif + static void __init lite5200_setup_arch(void) { struct device_node *np; @@ -107,6 +129,12 @@ static void __init lite5200_setup_arch(void) mpc52xx_setup_cpu(); /* Generic */ lite5200_setup_cpu(); /* Platorm specific */ +#ifdef CONFIG_PM + mpc52xx_suspend.board_suspend_prepare = lite5200_suspend_prepare; + mpc52xx_suspend.board_resume_finish = lite5200_resume_finish; + mpc52xx_pm_init(); +#endif + #ifdef CONFIG_PCI np = of_find_node_by_type(NULL, "pci"); if (np) { diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pm.c b/arch/powerpc/platforms/52xx/mpc52xx_pm.c new file mode 100644 index 00000000000..fd40044d16c --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_pm.c @@ -0,0 +1,191 @@ +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/io.h> +#include <asm/time.h> +#include <asm/cacheflush.h> +#include <asm/mpc52xx.h> + +#include "mpc52xx_pic.h" + + +/* these are defined in mpc52xx_sleep.S, and only used here */ +extern void mpc52xx_deep_sleep(void *sram, void *sdram_regs, + struct mpc52xx_cdm *, struct mpc52xx_intr *); +extern void mpc52xx_ds_sram(void); +extern const long mpc52xx_ds_sram_size; +extern void mpc52xx_ds_cached(void); +extern const long mpc52xx_ds_cached_size; + +static void __iomem *mbar; +static void __iomem *sdram; +static struct mpc52xx_cdm __iomem *cdm; +static struct mpc52xx_intr __iomem *intr; +static struct mpc52xx_gpio_wkup __iomem *gpiow; +static void *sram; +static int sram_size; + +struct mpc52xx_suspend mpc52xx_suspend; + +static int mpc52xx_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + return 1; + default: + return 0; + } +} + +int mpc52xx_set_wakeup_gpio(u8 pin, u8 level) +{ + u16 tmp; + + /* enable gpio */ + out_8(&gpiow->wkup_gpioe, in_8(&gpiow->wkup_gpioe) | (1 << pin)); + /* set as input */ + out_8(&gpiow->wkup_ddr, in_8(&gpiow->wkup_ddr) & ~(1 << pin)); + /* enable deep sleep interrupt */ + out_8(&gpiow->wkup_inten, in_8(&gpiow->wkup_inten) | (1 << pin)); + /* low/high level creates wakeup interrupt */ + tmp = in_be16(&gpiow->wkup_itype); + tmp &= ~(0x3 << (pin * 2)); + tmp |= (!level + 1) << (pin * 2); + out_be16(&gpiow->wkup_itype, tmp); + /* master enable */ + out_8(&gpiow->wkup_maste, 1); + + return 0; +} + +int mpc52xx_pm_prepare(suspend_state_t state) +{ + if (state != PM_SUSPEND_STANDBY) + return -EINVAL; + + /* map the whole register space */ + mbar = mpc52xx_find_and_map("mpc5200"); + if (!mbar) { + printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); + return -ENOSYS; + } + /* these offsets are from mpc5200 users manual */ + sdram = mbar + 0x100; + cdm = mbar + 0x200; + intr = mbar + 0x500; + gpiow = mbar + 0xc00; + sram = mbar + 0x8000; /* Those will be handled by the */ + sram_size = 0x4000; /* bestcomm driver soon */ + + /* call board suspend code, if applicable */ + if (mpc52xx_suspend.board_suspend_prepare) + mpc52xx_suspend.board_suspend_prepare(mbar); + else { + printk(KERN_ALERT "%s: %i don't know how to wake up the board\n", + __func__, __LINE__); + goto out_unmap; + } + + return 0; + + out_unmap: + iounmap(mbar); + return -ENOSYS; +} + + +char saved_sram[0x4000]; + +int mpc52xx_pm_enter(suspend_state_t state) +{ + u32 clk_enables; + u32 msr, hid0; + u32 intr_main_mask; + void __iomem * irq_0x500 = (void *)CONFIG_KERNEL_START + 0x500; + unsigned long irq_0x500_stop = (unsigned long)irq_0x500 + mpc52xx_ds_cached_size; + char saved_0x500[mpc52xx_ds_cached_size]; + + /* disable all interrupts in PIC */ + intr_main_mask = in_be32(&intr->main_mask); + out_be32(&intr->main_mask, intr_main_mask | 0x1ffff); + + /* don't let DEC expire any time soon */ + mtspr(SPRN_DEC, 0x7fffffff); + + /* save SRAM */ + memcpy(saved_sram, sram, sram_size); + + /* copy low level suspend code to sram */ + memcpy(sram, mpc52xx_ds_sram, mpc52xx_ds_sram_size); + + out_8(&cdm->ccs_sleep_enable, 1); + out_8(&cdm->osc_sleep_enable, 1); + out_8(&cdm->ccs_qreq_test, 1); + + /* disable all but SDRAM and bestcomm (SRAM) clocks */ + clk_enables = in_be32(&cdm->clk_enables); + out_be32(&cdm->clk_enables, clk_enables & 0x00088000); + + /* disable power management */ + msr = mfmsr(); + mtmsr(msr & ~MSR_POW); + + /* enable sleep mode, disable others */ + hid0 = mfspr(SPRN_HID0); + mtspr(SPRN_HID0, (hid0 & ~(HID0_DOZE | HID0_NAP | HID0_DPM)) | HID0_SLEEP); + + /* save original, copy our irq handler, flush from dcache and invalidate icache */ + memcpy(saved_0x500, irq_0x500, mpc52xx_ds_cached_size); + memcpy(irq_0x500, mpc52xx_ds_cached, mpc52xx_ds_cached_size); + flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop); + + /* call low-level sleep code */ + mpc52xx_deep_sleep(sram, sdram, cdm, intr); + + /* restore original irq handler */ + memcpy(irq_0x500, saved_0x500, mpc52xx_ds_cached_size); + flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop); + + /* restore old power mode */ + mtmsr(msr & ~MSR_POW); + mtspr(SPRN_HID0, hid0); + mtmsr(msr); + + out_be32(&cdm->clk_enables, clk_enables); + out_8(&cdm->ccs_sleep_enable, 0); + out_8(&cdm->osc_sleep_enable, 0); + + /* restore SRAM */ + memcpy(sram, saved_sram, sram_size); + + /* restart jiffies */ + wakeup_decrementer(); + + /* reenable interrupts in PIC */ + out_be32(&intr->main_mask, intr_main_mask); + + return 0; +} + +int mpc52xx_pm_finish(suspend_state_t state) +{ + /* call board resume code */ + if (mpc52xx_suspend.board_resume_finish) + mpc52xx_suspend.board_resume_finish(mbar); + + iounmap(mbar); + + return 0; +} + +static struct pm_ops mpc52xx_pm_ops = { + .valid = mpc52xx_pm_valid, + .prepare = mpc52xx_pm_prepare, + .enter = mpc52xx_pm_enter, + .finish = mpc52xx_pm_finish, +}; + +int __init mpc52xx_pm_init(void) +{ + pm_set_ops(&mpc52xx_pm_ops); + return 0; +} diff --git a/arch/powerpc/platforms/52xx/mpc52xx_sleep.S b/arch/powerpc/platforms/52xx/mpc52xx_sleep.S new file mode 100644 index 00000000000..4dc170b0ae1 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_sleep.S @@ -0,0 +1,154 @@ +#include <asm/reg.h> +#include <asm/ppc_asm.h> +#include <asm/processor.h> + + +.text + +_GLOBAL(mpc52xx_deep_sleep) +mpc52xx_deep_sleep: /* args r3-r6: SRAM, SDRAM regs, CDM regs, INTR regs */ + + /* enable interrupts */ + mfmsr r7 + ori r7, r7, 0x8000 /* EE */ + mtmsr r7 + sync; isync; + + li r10, 0 /* flag that irq handler sets */ + + /* enable tmr7 (or any other) interrupt */ + lwz r8, 0x14(r6) /* intr->main_mask */ + ori r8, r8, 0x1 + xori r8, r8, 0x1 + stw r8, 0x14(r6) + sync + + /* emulate tmr7 interrupt */ + li r8, 0x1 + stw r8, 0x40(r6) /* intr->main_emulate */ + sync + + /* wait for it to happen */ +1: + cmpi cr0, r10, 1 + bne cr0, 1b + + /* lock icache */ + mfspr r10, SPRN_HID0 + ori r10, r10, 0x2000 + sync; isync; + mtspr SPRN_HID0, r10 + sync; isync; + + + mflr r9 /* save LR */ + + /* jump to sram */ + mtlr r3 + blrl + + mtlr r9 /* restore LR */ + + /* unlock icache */ + mfspr r10, SPRN_HID0 + ori r10, r10, 0x2000 + xori r10, r10, 0x2000 + sync; isync; + mtspr SPRN_HID0, r10 + sync; isync; + + + /* return to C code */ + blr + + +_GLOBAL(mpc52xx_ds_sram) +mpc52xx_ds_sram: + /* put SDRAM into self-refresh */ + lwz r8, 0x4(r4) /* sdram->ctrl */ + + oris r8, r8, 0x8000 /* mode_en */ + stw r8, 0x4(r4) + sync + + ori r8, r8, 0x0002 /* soft_pre */ + stw r8, 0x4(r4) + sync + xori r8, r8, 0x0002 + + xoris r8, r8, 0x8000 /* !mode_en */ + stw r8, 0x4(r4) + sync + + oris r8, r8, 0x5000 + xoris r8, r8, 0x4000 /* ref_en !cke */ + stw r8, 0x4(r4) + sync + + /* disable SDRAM clock */ + lwz r8, 0x14(r5) /* cdm->clkenable */ + ori r8, r8, 0x0008 + xori r8, r8, 0x0008 + stw r8, 0x14(r5) + sync + + + /* put mpc5200 to sleep */ + mfmsr r10 + oris r10, r10, 0x0004 /* POW = 1 */ + sync; isync; + mtmsr r10 + sync; isync; + + + /* enable clock */ + lwz r8, 0x14(r5) + ori r8, r8, 0x0008 + stw r8, 0x14(r5) + sync + + /* get ram out of self-refresh */ + lwz r8, 0x4(r4) + oris r8, r8, 0x5000 /* cke ref_en */ + stw r8, 0x4(r4) + sync + + blr +_GLOBAL(mpc52xx_ds_sram_size) +mpc52xx_ds_sram_size: + .long $-mpc52xx_ds_sram + + +/* ### interrupt handler for wakeup from deep-sleep ### */ +_GLOBAL(mpc52xx_ds_cached) +mpc52xx_ds_cached: + mtspr SPRN_SPRG0, r7 + mtspr SPRN_SPRG1, r8 + + /* disable emulated interrupt */ + mfspr r7, 311 /* MBAR */ + addi r7, r7, 0x540 /* intr->main_emul */ + li r8, 0 + stw r8, 0(r7) + sync + dcbf 0, r7 + + /* acknowledge wakeup, so CCS releases power pown */ + mfspr r7, 311 /* MBAR */ + addi r7, r7, 0x524 /* intr->enc_status */ + lwz r8, 0(r7) + ori r8, r8, 0x0400 + stw r8, 0(r7) + sync + dcbf 0, r7 + + /* flag - we handled the interrupt */ + li r10, 1 + + mfspr r8, SPRN_SPRG1 + mfspr r7, SPRN_SPRG0 + + rfi +_GLOBAL(mpc52xx_ds_cached_size) +mpc52xx_ds_cached_size: + .long $-mpc52xx_ds_cached diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index fff09f5d6ed..94843ed52a9 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -111,6 +111,7 @@ static struct of_device_id mpc832x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, { .type = "qe", }, + { .type = "mdio", }, {}, }; diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index 6b71e9ffb11..b0b22bb29de 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -73,6 +73,7 @@ static struct of_device_id mpc832x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, { .type = "qe", }, + { .type = "mdio", }, {}, }; diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index 526ed090a44..bceeff8bbfd 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -118,6 +118,7 @@ static struct of_device_id mpc836x_ids[] = { { .type = "soc", }, { .compatible = "soc", }, { .type = "qe", }, + { .type = "mdio", }, {}, }; diff --git a/arch/powerpc/platforms/85xx/mpc8544_ds.c b/arch/powerpc/platforms/85xx/mpc8544_ds.c index 2867f85e632..bec84ffe708 100644 --- a/arch/powerpc/platforms/85xx/mpc8544_ds.c +++ b/arch/powerpc/platforms/85xx/mpc8544_ds.c @@ -84,7 +84,7 @@ void __init mpc8544_ds_pic_init(void) #ifdef CONFIG_PPC_I8259 /* Initialize the i8259 controller */ for_each_node_by_type(np, "interrupt-controller") - if (device_is_compatible(np, "chrp,iic")) { + if (of_device_is_compatible(np, "chrp,iic")) { cascade_node = np; break; } diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c index 7e71636f909..1490eb3ce0d 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -197,7 +197,7 @@ static void __init mpc85xx_cds_pic_init(void) #ifdef CONFIG_PPC_I8259 /* Initialize the i8259 controller */ for_each_node_by_type(np, "interrupt-controller") - if (device_is_compatible(np, "chrp,iic")) { + if (of_device_is_compatible(np, "chrp,iic")) { cascade_node = np; break; } diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index 54db4168995..e3dddbfe66f 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -147,6 +147,7 @@ static struct of_device_id mpc85xx_ids[] = { { .type = "soc", }, { .compatible = "soc", }, { .type = "qe", }, + { .type = "mdio", }, {}, }; diff --git a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c index 3d3d98f5bd4..90877565caa 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c +++ b/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c @@ -102,7 +102,7 @@ mpc86xx_hpcn_init_irq(void) #ifdef CONFIG_PCI /* Initialize i8259 controller */ for_each_node_by_type(np, "interrupt-controller") - if (device_is_compatible(np, "chrp,iic")) { + if (of_device_is_compatible(np, "chrp,iic")) { cascade_node = np; break; } diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 51e33347c14..361acfa2894 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -42,6 +42,7 @@ source "arch/powerpc/platforms/83xx/Kconfig" source "arch/powerpc/platforms/85xx/Kconfig" source "arch/powerpc/platforms/86xx/Kconfig" source "arch/powerpc/platforms/embedded6xx/Kconfig" +source "arch/powerpc/platforms/44x/Kconfig" #source "arch/powerpc/platforms/4xx/Kconfig config PPC_NATIVE diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 452004283f1..d6e041a46d2 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile @@ -6,7 +6,8 @@ obj-$(CONFIG_PPC_PMAC) += powermac/ endif endif obj-$(CONFIG_PPC_CHRP) += chrp/ -obj-$(CONFIG_4xx) += 4xx/ +#obj-$(CONFIG_4xx) += 4xx/ +obj-$(CONFIG_44x) += 44x/ obj-$(CONFIG_PPC_MPC52xx) += 52xx/ obj-$(CONFIG_PPC_8xx) += 8xx/ obj-$(CONFIG_PPC_82xx) += 82xx/ diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c index 4fc4e92775d..47264e72202 100644 --- a/arch/powerpc/platforms/cell/interrupt.c +++ b/arch/powerpc/platforms/cell/interrupt.c @@ -227,7 +227,7 @@ void iic_request_IPIs(void) static int iic_host_match(struct irq_host *h, struct device_node *node) { - return device_is_compatible(node, + return of_device_is_compatible(node, "IBM,CBEA-Internal-Interrupt-Controller"); } @@ -256,7 +256,7 @@ static int iic_host_xlate(struct irq_host *h, struct device_node *ct, unsigned int node, ext, unit, class; const u32 *val; - if (!device_is_compatible(ct, + if (!of_device_is_compatible(ct, "IBM,CBEA-Internal-Interrupt-Controller")) return -ENODEV; if (intsize != 1) @@ -324,7 +324,7 @@ static int __init setup_iic(void) for (dn = NULL; (dn = of_find_node_by_name(dn,"interrupt-controller")) != NULL;) { - if (!device_is_compatible(dn, + if (!of_device_is_compatible(dn, "IBM,CBEA-Internal-Interrupt-Controller")) continue; np = of_get_property(dn, "ibm,interrupt-server-ranges", NULL); diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index 54b96183cb6..db6654272e1 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -112,7 +112,7 @@ static void __init mpic_init_IRQ(void) for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { - if (!device_is_compatible(dn, "CBEA,platform-open-pic")) + if (!of_device_is_compatible(dn, "CBEA,platform-open-pic")) continue; /* The MPIC driver will get everything it needs from the diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c index fb1f15797bb..05f4b3d3d75 100644 --- a/arch/powerpc/platforms/cell/spider-pic.c +++ b/arch/powerpc/platforms/cell/spider-pic.c @@ -358,12 +358,12 @@ void __init spider_init_IRQ(void) */ for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { - if (device_is_compatible(dn, "CBEA,platform-spider-pic")) { + if (of_device_is_compatible(dn, "CBEA,platform-spider-pic")) { if (of_address_to_resource(dn, 0, &r)) { printk(KERN_WARNING "spider-pic: Failed\n"); continue; } - } else if (device_is_compatible(dn, "sti,platform-spider-pic") + } else if (of_device_is_compatible(dn, "sti,platform-spider-pic") && (chip < 2)) { static long hard_coded_pics[] = { 0x24000008000ul, 0x34000008000ul}; diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c index 1469d6478f6..d32fedc991d 100644 --- a/arch/powerpc/platforms/chrp/pci.c +++ b/arch/powerpc/platforms/chrp/pci.c @@ -267,7 +267,7 @@ chrp_find_bridges(void) model = of_get_property(dev, "model", NULL); if (model == NULL) model = "<none>"; - if (device_is_compatible(dev, "IBM,python")) { + if (of_device_is_compatible(dev, "IBM,python")) { setup_python(hose, dev); } else if (is_mot || strncmp(model, "Motorola, Grackle", 17) == 0) { diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index 1870038a8e0..373de4c063d 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -448,7 +448,7 @@ static void __init chrp_find_8259(void) /* Look for cascade */ for_each_node_by_type(np, "interrupt-controller") - if (device_is_compatible(np, "chrp,iic")) { + if (of_device_is_compatible(np, "chrp,iic")) { pic = np; break; } diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig index 9557908ef54..8f3c2a73e16 100644 --- a/arch/powerpc/platforms/embedded6xx/Kconfig +++ b/arch/powerpc/platforms/embedded6xx/Kconfig @@ -20,16 +20,24 @@ config MPC7448HPC2 select TSI108_BRIDGE select DEFAULT_UIMAGE select PPC_UDBG_16550 - select MPIC - select MPIC_WEIRD help Select MPC7448HPC2 if configuring for Freescale MPC7448HPC2 (Taiga) platform + +config PPC_HOLLY + bool "PPC750GX/CL with TSI10x bridge (Hickory/Holly)" + select TSI108_BRIDGE + select PPC_UDBG_16550 + help + Select PPC_HOLLY if configuring for an IBM 750GX/CL Eval + Board with TSI108/9 bridge (Hickory/Holly) endchoice config TSI108_BRIDGE bool - depends on MPC7448HPC2 + depends on MPC7448HPC2 || PPC_HOLLY + select MPIC + select MPIC_WEIRD default y config MPC10X_BRIDGE diff --git a/arch/powerpc/platforms/embedded6xx/Makefile b/arch/powerpc/platforms/embedded6xx/Makefile index d3d11a3cd65..b39fe4f470d 100644 --- a/arch/powerpc/platforms/embedded6xx/Makefile +++ b/arch/powerpc/platforms/embedded6xx/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_MPC7448HPC2) += mpc7448_hpc2.o obj-$(CONFIG_LINKSTATION) += linkstation.o ls_uart.o +obj-$(CONFIG_PPC_HOLLY) += holly.o diff --git a/arch/powerpc/platforms/embedded6xx/holly.c b/arch/powerpc/platforms/embedded6xx/holly.c new file mode 100644 index 00000000000..3a0b4a01401 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/holly.c @@ -0,0 +1,317 @@ +/* + * Board setup routines for the IBM 750GX/CL platform w/ TSI10x bridge + * + * Copyright 2007 IBM Corporation + * + * Stephen Winiecki <stevewin@us.ibm.com> + * Josh Boyer <jwboyer@linux.vnet.ibm.com> + * + * Based on code from mpc7448_hpc2.c + * + * 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. + */ + +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/ide.h> +#include <linux/seq_file.h> +#include <linux/root_dev.h> +#include <linux/serial.h> +#include <linux/tty.h> +#include <linux/serial_core.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/tsi108.h> +#include <asm/pci-bridge.h> +#include <asm/reg.h> +#include <mm/mmu_decl.h> +#include <asm/tsi108_irq.h> +#include <asm/tsi108_pci.h> +#include <asm/mpic.h> +#include <asm/of_platform.h> + +#undef DEBUG + +#define HOLLY_PCI_CFG_PHYS 0x7c000000 + +int holly_exclude_device(u_char bus, u_char devfn) +{ + if (bus == 0 && PCI_SLOT(devfn) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} + +static void holly_remap_bridge(void) +{ + u32 lut_val, lut_addr; + int i; + + printk(KERN_INFO "Remapping PCI bridge\n"); + + /* Re-init the PCI bridge and LUT registers to have mappings that don't + * rely on PIBS + */ + lut_addr = 0x900; + for (i = 0; i < 31; i++) { + tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x00000201); + lut_addr += 4; + tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x0); + lut_addr += 4; + } + + /* Reserve the last LUT entry for PCI I/O space */ + tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x00000241); + lut_addr += 4; + tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x0); + + /* Map PCI I/O space */ + tsi108_write_reg(TSI108_PCI_PFAB_IO_UPPER, 0x0); + tsi108_write_reg(TSI108_PCI_PFAB_IO, 0x1); + + /* Map PCI CFG space */ + tsi108_write_reg(TSI108_PCI_PFAB_BAR0_UPPER, 0x0); + tsi108_write_reg(TSI108_PCI_PFAB_BAR0, 0x7c000000 | 0x01); + + /* We don't need MEM32 and PRM remapping so disable them */ + tsi108_write_reg(TSI108_PCI_PFAB_MEM32, 0x0); + tsi108_write_reg(TSI108_PCI_PFAB_PFM3, 0x0); + tsi108_write_reg(TSI108_PCI_PFAB_PFM4, 0x0); + + /* Set P2O_BAR0 */ + tsi108_write_reg(TSI108_PCI_P2O_BAR0_UPPER, 0x0); + tsi108_write_reg(TSI108_PCI_P2O_BAR0, 0xc0000000); + + /* Init the PCI LUTs to do no remapping */ + lut_addr = 0x500; + lut_val = 0x00000002; + + for (i = 0; i < 32; i++) { + tsi108_write_reg(TSI108_PCI_OFFSET + lut_addr, lut_val); + lut_addr += 4; + tsi108_write_reg(TSI108_PCI_OFFSET + lut_addr, 0x40000000); + lut_addr += 4; + lut_val += 0x02000000; + } + tsi108_write_reg(TSI108_PCI_P2O_PAGE_SIZES, 0x00007900); + + /* Set 64-bit PCI bus address for system memory */ + tsi108_write_reg(TSI108_PCI_P2O_BAR2_UPPER, 0x0); + tsi108_write_reg(TSI108_PCI_P2O_BAR2, 0x0); +} + +static void __init holly_setup_arch(void) +{ + struct device_node *cpu; + struct device_node *np; + + if (ppc_md.progress) + ppc_md.progress("holly_setup_arch():set_bridge", 0); + + cpu = of_find_node_by_type(NULL, "cpu"); + if (cpu) { + const unsigned int *fp; + + fp = of_get_property(cpu, "clock-frequency", NULL); + if (fp) + loops_per_jiffy = *fp / HZ; + else + loops_per_jiffy = 50000000 / HZ; + of_node_put(cpu); + } + tsi108_csr_vir_base = get_vir_csrbase(); + + /* setup PCI host bridge */ + holly_remap_bridge(); + + np = of_find_node_by_type(NULL, "pci"); + if (np) + tsi108_setup_pci(np, HOLLY_PCI_CFG_PHYS, 1); + + ppc_md.pci_exclude_device = holly_exclude_device; + if (ppc_md.progress) + ppc_md.progress("tsi108: resources set", 0x100); + + printk(KERN_INFO "PPC750GX/CL Platform\n"); +} + +/* + * Interrupt setup and service. Interrrupts on the holly come + * from the four external INT pins, PCI interrupts are routed via + * PCI interrupt control registers, it generates internal IRQ23 + * + * Interrupt routing on the Holly Board: + * TSI108:PB_INT[0] -> CPU0:INT# + * TSI108:PB_INT[1] -> CPU0:MCP# + * TSI108:PB_INT[2] -> N/C + * TSI108:PB_INT[3] -> N/C + */ +static void __init holly_init_IRQ(void) +{ + struct mpic *mpic; + phys_addr_t mpic_paddr = 0; + struct device_node *tsi_pic; +#ifdef CONFIG_PCI + unsigned int cascade_pci_irq; + struct device_node *tsi_pci; + struct device_node *cascade_node = NULL; +#endif + + tsi_pic = of_find_node_by_type(NULL, "open-pic"); + if (tsi_pic) { + unsigned int size; + const void *prop = of_get_property(tsi_pic, "reg", &size); + mpic_paddr = of_translate_address(tsi_pic, prop); + } + + if (mpic_paddr == 0) { + printk(KERN_ERR "%s: No tsi108 PIC found !\n", __func__); + return; + } + + pr_debug("%s: tsi108 pic phys_addr = 0x%x\n", __func__, (u32) mpic_paddr); + + mpic = mpic_alloc(tsi_pic, mpic_paddr, + MPIC_PRIMARY | MPIC_BIG_ENDIAN | MPIC_WANTS_RESET | + MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108, + 24, + NR_IRQS-4, /* num_sources used */ + "Tsi108_PIC"); + + BUG_ON(mpic == NULL); + + mpic_assign_isu(mpic, 0, mpic_paddr + 0x100); + + mpic_init(mpic); + +#ifdef CONFIG_PCI + tsi_pci = of_find_node_by_type(NULL, "pci"); + if (tsi_pci == NULL) { + printk(KERN_ERR "%s: No tsi108 pci node found !\n", __func__); + return; + } + + cascade_node = of_find_node_by_type(NULL, "pic-router"); + if (cascade_node == NULL) { + printk(KERN_ERR "%s: No tsi108 pci cascade node found !\n", __func__); + return; + } + + cascade_pci_irq = irq_of_parse_and_map(tsi_pci, 0); + pr_debug("%s: tsi108 cascade_pci_irq = 0x%x\n", __func__, (u32) cascade_pci_irq); + tsi108_pci_int_init(cascade_node); + set_irq_data(cascade_pci_irq, mpic); + set_irq_chained_handler(cascade_pci_irq, tsi108_irq_cascade); +#endif + /* Configure MPIC outputs to CPU0 */ + tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0); + of_node_put(tsi_pic); +} + +void holly_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: IBM\n"); + seq_printf(m, "machine\t\t: PPC750 GX/CL\n"); +} + +void holly_restart(char *cmd) +{ + __be32 __iomem *ocn_bar1 = NULL; + unsigned long bar; + struct device_node *bridge = NULL; + const void *prop; + int size; + phys_addr_t addr = 0xc0000000; + + local_irq_disable(); + + bridge = of_find_node_by_type(NULL, "tsi-bridge"); + if (bridge) { + prop = of_get_property(bridge, "reg", &size); + addr = of_translate_address(bridge, prop); + } + addr += (TSI108_PB_OFFSET + 0x414); + + ocn_bar1 = ioremap(addr, 0x4); + + /* Turn on the BOOT bit so the addresses are correctly + * routed to the HLP interface */ + bar = ioread32be(ocn_bar1); + bar |= 2; + iowrite32be(bar, ocn_bar1); + iosync(); + + /* Set SRR0 to the reset vector and turn on MSR_IP */ + mtspr(SPRN_SRR0, 0xfff00100); + mtspr(SPRN_SRR1, MSR_IP); + + /* Do an rfi to jump back to firmware. Somewhat evil, + * but it works + */ + __asm__ __volatile__("rfi" : : : "memory"); + + /* Spin until reset happens. Shouldn't really get here */ + for (;;) ; +} + +void holly_power_off(void) +{ + local_irq_disable(); + /* No way to shut power off with software */ + for (;;) ; +} + +void holly_halt(void) +{ + holly_power_off(); +} + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init holly_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ibm,holly")) + return 0; + return 1; +} + +static int ppc750_machine_check_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *entry; + + /* Are we prepared to handle this fault */ + if ((entry = search_exception_tables(regs->nip)) != NULL) { + tsi108_clear_pci_cfg_error(); + regs->msr |= MSR_RI; + regs->nip = entry->fixup; + return 1; + } + return 0; +} + +define_machine(holly){ + .name = "PPC750 GX/CL TSI", + .probe = holly_probe, + .setup_arch = holly_setup_arch, + .init_IRQ = holly_init_IRQ, + .show_cpuinfo = holly_show_cpuinfo, + .get_irq = mpic_get_irq, + .restart = holly_restart, + .calibrate_decr = generic_calibrate_decr, + .machine_check_exception = ppc750_machine_check_exception, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index c3f64ddb0be..4542e0c837c 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -41,6 +41,7 @@ #include <asm/reg.h> #include <mm/mmu_decl.h> #include "mpc7448_hpc2.h" +#include <asm/tsi108_pci.h> #include <asm/tsi108_irq.h> #include <asm/mpic.h> @@ -51,16 +52,15 @@ #define DBG(fmt...) do { } while(0) #endif +#define MPC7448HPC2_PCI_CFG_PHYS 0xfb000000 + #ifndef CONFIG_PCI isa_io_base = MPC7448_HPC2_ISA_IO_BASE; isa_mem_base = MPC7448_HPC2_ISA_MEM_BASE; pci_dram_offset = MPC7448_HPC2_PCI_MEM_OFFSET; #endif -extern int tsi108_setup_pci(struct device_node *dev); extern void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val); -extern void tsi108_pci_int_init(struct device_node *node); -extern void tsi108_irq_cascade(unsigned int irq, struct irq_desc *desc); int mpc7448_hpc2_exclude_device(u_char bus, u_char devfn) { @@ -72,28 +72,16 @@ int mpc7448_hpc2_exclude_device(u_char bus, u_char devfn) static void __init mpc7448_hpc2_setup_arch(void) { - struct device_node *cpu; struct device_node *np; if (ppc_md.progress) ppc_md.progress("mpc7448_hpc2_setup_arch():set_bridge", 0); - cpu = of_find_node_by_type(NULL, "cpu"); - if (cpu != 0) { - const unsigned int *fp; - - fp = of_get_property(cpu, "clock-frequency", NULL); - if (fp != 0) - loops_per_jiffy = *fp / HZ; - else - loops_per_jiffy = 50000000 / HZ; - of_node_put(cpu); - } tsi108_csr_vir_base = get_vir_csrbase(); /* setup PCI host bridge */ #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - tsi108_setup_pci(np); + tsi108_setup_pci(np, MPC7448HPC2_PCI_CFG_PHYS, 0); ppc_md.pci_exclude_device = mpc7448_hpc2_exclude_device; if (ppc_md.progress) @@ -222,7 +210,6 @@ static int __init mpc7448_hpc2_probe(void) static int mpc7448_machine_check_exception(struct pt_regs *regs) { - extern void tsi108_clear_pci_cfg_error(void); const struct exception_table_entry *entry; /* Are we prepared to handle this fault */ diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c index b1d3b99c3f9..7aaa5bbc936 100644 --- a/arch/powerpc/platforms/maple/pci.c +++ b/arch/powerpc/platforms/maple/pci.c @@ -467,15 +467,15 @@ static int __init add_bridge(struct device_node *dev) hose->last_busno = bus_range ? bus_range[1] : 0xff; disp_name = NULL; - if (device_is_compatible(dev, "u3-agp")) { + if (of_device_is_compatible(dev, "u3-agp")) { setup_u3_agp(hose); disp_name = "U3-AGP"; primary = 0; - } else if (device_is_compatible(dev, "u3-ht")) { + } else if (of_device_is_compatible(dev, "u3-ht")) { setup_u3_ht(hose); disp_name = "U3-HT"; primary = 1; - } else if (device_is_compatible(dev, "u4-pcie")) { + } else if (of_device_is_compatible(dev, "u4-pcie")) { setup_u4_pcie(hose); disp_name = "U4-PCIE"; primary = 0; @@ -556,12 +556,12 @@ void __init maple_pci_init(void) continue; if (strcmp(np->type, "pci") && strcmp(np->type, "ht")) continue; - if ((device_is_compatible(np, "u4-pcie") || - device_is_compatible(np, "u3-agp")) && + if ((of_device_is_compatible(np, "u4-pcie") || + of_device_is_compatible(np, "u3-agp")) && add_bridge(np) == 0) of_node_get(np); - if (device_is_compatible(np, "u3-ht")) { + if (of_device_is_compatible(np, "u3-ht")) { of_node_get(np); ht = np; } diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 2a30c5b2532..354c0586162 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -231,7 +231,7 @@ static void __init maple_init_IRQ(void) */ for_each_node_by_type(np, "interrupt-controller") - if (device_is_compatible(np, "open-pic")) { + if (of_device_is_compatible(np, "open-pic")) { mpic_node = np; break; } diff --git a/arch/powerpc/platforms/pasemi/cpufreq.c b/arch/powerpc/platforms/pasemi/cpufreq.c index 2a57d602368..3ae083851b0 100644 --- a/arch/powerpc/platforms/pasemi/cpufreq.c +++ b/arch/powerpc/platforms/pasemi/cpufreq.c @@ -31,6 +31,7 @@ #include <asm/hw_irq.h> #include <asm/io.h> #include <asm/prom.h> +#include <asm/time.h> #define SDCASR_REG 0x0100 #define SDCASR_REG_STRIDE 0x1000 @@ -204,6 +205,8 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cur = pas_freqs[cur_astate].frequency; policy->cpus = cpu_online_map; + ppc_proc_freq = policy->cur * 1000ul; + cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu); /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max @@ -270,6 +273,7 @@ static int pas_cpufreq_target(struct cpufreq_policy *policy, cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); mutex_unlock(&pas_switch_mutex); + ppc_proc_freq = freqs.new * 1000ul; return 0; } diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index f88f0ec4c8c..38f107b3c52 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c @@ -114,7 +114,7 @@ static __init void pas_init_IRQ(void) mpic_node = NULL; for_each_node_by_type(np, "interrupt-controller") - if (device_is_compatible(np, "open-pic")) { + if (of_device_is_compatible(np, "open-pic")) { mpic_node = np; break; } @@ -211,7 +211,10 @@ static struct of_device_id pasemi_bus_ids[] = { static int __init pasemi_publish_devices(void) { - /* Publish OF platform devices for southbridge IOs */ + if (!machine_is(pasemi)) + return 0; + + /* Publish OF platform devices for SDC and other non-PCI devices */ of_platform_bus_probe(NULL, pasemi_bus_ids, NULL); return 0; diff --git a/arch/powerpc/platforms/powermac/cpufreq_64.c b/arch/powerpc/platforms/powermac/cpufreq_64.c index 567d5523b69..00f50298c34 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_64.c +++ b/arch/powerpc/platforms/powermac/cpufreq_64.c @@ -357,13 +357,13 @@ static unsigned int g5_cpufreq_get_speed(unsigned int cpu) static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy) { - if (policy->cpu != 0) - return -ENODEV; - policy->governor = CPUFREQ_DEFAULT_GOVERNOR; policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->cur = g5_cpu_freqs[g5_query_freq()].frequency; - policy->cpus = cpu_possible_map; + /* secondary CPUs are tied to the primary one by the + * cpufreq core if in the secondary policy we tell it that + * it actually must be one policy together with all others. */ + policy->cpus = cpu_online_map; cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu); return cpufreq_frequency_table_cpuinfo(policy, diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index 52cfdd86c92..f29705f8047 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -1418,7 +1418,7 @@ static long g5_eth_phy_reset(struct device_node *node, long param, long value) phy = of_get_next_child(node, NULL); if (!phy) return -ENODEV; - need_reset = device_is_compatible(phy, "B5221"); + need_reset = of_device_is_compatible(phy, "B5221"); of_node_put(phy); if (!need_reset) return 0; @@ -2624,7 +2624,7 @@ static void __init probe_one_macio(const char *name, const char *compat, int typ for (node = NULL; (node = of_find_node_by_name(node, name)) != NULL;) { if (!compat) break; - if (device_is_compatible(node, compat)) + if (of_device_is_compatible(node, compat)) break; } if (!node) @@ -2728,7 +2728,7 @@ initial_serial_shutdown(struct device_node *np) conn = of_get_property(np, "AAPL,connector", &len); if (conn && (strcmp(conn, "infrared") == 0)) port_type = PMAC_SCC_IRDA; - else if (device_is_compatible(np, "cobalt")) + else if (of_device_is_compatible(np, "cobalt")) modem = 1; else if (slots && slots->count > 0) { if (strcmp(slots->name, "IrDA") == 0) @@ -2787,7 +2787,7 @@ set_initial_features(void) */ np = of_find_node_by_name(NULL, "ethernet"); while(np) { - if (device_is_compatible(np, "K2-GMAC")) + if (of_device_is_compatible(np, "K2-GMAC")) g5_gmac_enable(np, 0, 1); np = of_find_node_by_name(np, "ethernet"); } @@ -2799,7 +2799,7 @@ set_initial_features(void) */ np = of_find_node_by_name(NULL, "firewire"); while(np) { - if (device_is_compatible(np, "pci106b,5811")) { + if (of_device_is_compatible(np, "pci106b,5811")) { macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; g5_fw_enable(np, 0, 1); } @@ -2817,8 +2817,8 @@ set_initial_features(void) np = of_find_node_by_name(NULL, "ethernet"); while(np) { if (np->parent - && device_is_compatible(np->parent, "uni-north") - && device_is_compatible(np, "gmac")) + && of_device_is_compatible(np->parent, "uni-north") + && of_device_is_compatible(np, "gmac")) core99_gmac_enable(np, 0, 1); np = of_find_node_by_name(np, "ethernet"); } @@ -2831,10 +2831,10 @@ set_initial_features(void) np = of_find_node_by_name(NULL, "firewire"); while(np) { if (np->parent - && device_is_compatible(np->parent, "uni-north") - && (device_is_compatible(np, "pci106b,18") || - device_is_compatible(np, "pci106b,30") || - device_is_compatible(np, "pci11c1,5811"))) { + && of_device_is_compatible(np->parent, "uni-north") + && (of_device_is_compatible(np, "pci106b,18") || + of_device_is_compatible(np, "pci106b,30") || + of_device_is_compatible(np, "pci11c1,5811"))) { macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; core99_firewire_enable(np, 0, 1); } @@ -2845,8 +2845,8 @@ set_initial_features(void) np = of_find_node_by_name(NULL, "ata-6"); while(np) { if (np->parent - && device_is_compatible(np->parent, "uni-north") - && device_is_compatible(np, "kauai-ata")) { + && of_device_is_compatible(np->parent, "uni-north") + && of_device_is_compatible(np, "kauai-ata")) { core99_ata100_enable(np, 1); } np = of_find_node_by_name(np, "ata-6"); diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 5430e146b3e..3f507ab9c5e 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -1207,7 +1207,7 @@ static void pmac_i2c_devscan(void (*callback)(struct device_node *dev, if (strcmp(np->name, p->name)) continue; if (p->compatible && - !device_is_compatible(np, p->compatible)) + !of_device_is_compatible(np, p->compatible)) continue; if (p->quirks & pmac_i2c_quirk_skip) break; diff --git a/arch/powerpc/platforms/powermac/nvram.c b/arch/powerpc/platforms/powermac/nvram.c index 692945c1491..c6f0f9e738e 100644 --- a/arch/powerpc/platforms/powermac/nvram.c +++ b/arch/powerpc/platforms/powermac/nvram.c @@ -553,7 +553,7 @@ static int __init core99_nvram_setup(struct device_node *dp, unsigned long addr) * identify the chip using flash id commands and base ourselves on * a list of known chips IDs */ - if (device_is_compatible(dp, "amd-0137")) { + if (of_device_is_compatible(dp, "amd-0137")) { core99_erase_bank = amd_erase_bank; core99_write_bank = amd_write_bank; } else { @@ -588,7 +588,7 @@ int __init pmac_nvram_init(void) } } - is_core_99 = device_is_compatible(dp, "nvram,flash"); + is_core_99 = of_device_is_compatible(dp, "nvram,flash"); if (is_core_99) { err = core99_nvram_setup(dp, r1.start); goto bail; diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index 22c4ae4c693..c4af9e21ac9 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -934,15 +934,15 @@ static int __init add_bridge(struct device_node *dev) /* 64 bits only bridges */ #ifdef CONFIG_PPC64 - if (device_is_compatible(dev, "u3-agp")) { + if (of_device_is_compatible(dev, "u3-agp")) { setup_u3_agp(hose); disp_name = "U3-AGP"; primary = 0; - } else if (device_is_compatible(dev, "u3-ht")) { + } else if (of_device_is_compatible(dev, "u3-ht")) { setup_u3_ht(hose); disp_name = "U3-HT"; primary = 1; - } else if (device_is_compatible(dev, "u4-pcie")) { + } else if (of_device_is_compatible(dev, "u4-pcie")) { setup_u4_pcie(hose); disp_name = "U4-PCIE"; primary = 0; @@ -953,7 +953,7 @@ static int __init add_bridge(struct device_node *dev) /* 32 bits only bridges */ #ifdef CONFIG_PPC32 - if (device_is_compatible(dev, "uni-north")) { + if (of_device_is_compatible(dev, "uni-north")) { primary = setup_uninorth(hose, &rsrc); disp_name = "UniNorth"; } else if (strcmp(dev->name, "pci") == 0) { @@ -1129,21 +1129,21 @@ pmac_pci_enable_device_hook(struct pci_dev *dev, int initial) return 0; uninorth_child = node->parent && - device_is_compatible(node->parent, "uni-north"); + of_device_is_compatible(node->parent, "uni-north"); /* Firewire & GMAC were disabled after PCI probe, the driver is * claiming them, we must re-enable them now. */ if (uninorth_child && !strcmp(node->name, "firewire") && - (device_is_compatible(node, "pci106b,18") || - device_is_compatible(node, "pci106b,30") || - device_is_compatible(node, "pci11c1,5811"))) { + (of_device_is_compatible(node, "pci106b,18") || + of_device_is_compatible(node, "pci106b,30") || + of_device_is_compatible(node, "pci11c1,5811"))) { pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, node, 0, 1); pmac_call_feature(PMAC_FTR_1394_ENABLE, node, 0, 1); updatecfg = 1; } if (uninorth_child && !strcmp(node->name, "ethernet") && - device_is_compatible(node, "gmac")) { + of_device_is_compatible(node, "gmac")) { pmac_call_feature(PMAC_FTR_GMAC_ENABLE, node, 0, 1); updatecfg = 1; } @@ -1203,18 +1203,18 @@ void __init pmac_pcibios_after_init(void) #endif /* CONFIG_BLK_DEV_IDE */ for_each_node_by_name(nd, "firewire") { - if (nd->parent && (device_is_compatible(nd, "pci106b,18") || - device_is_compatible(nd, "pci106b,30") || - device_is_compatible(nd, "pci11c1,5811")) - && device_is_compatible(nd->parent, "uni-north")) { + if (nd->parent && (of_device_is_compatible(nd, "pci106b,18") || + of_device_is_compatible(nd, "pci106b,30") || + of_device_is_compatible(nd, "pci11c1,5811")) + && of_device_is_compatible(nd->parent, "uni-north")) { pmac_call_feature(PMAC_FTR_1394_ENABLE, nd, 0, 0); pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, nd, 0, 0); } } of_node_put(nd); for_each_node_by_name(nd, "ethernet") { - if (nd->parent && device_is_compatible(nd, "gmac") - && device_is_compatible(nd->parent, "uni-north")) + if (nd->parent && of_device_is_compatible(nd, "gmac") + && of_device_is_compatible(nd->parent, "uni-north")) pmac_call_feature(PMAC_FTR_GMAC_ENABLE, nd, 0, 0); } of_node_put(nd); diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index ae5097ac037..87cd6805171 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -364,7 +364,7 @@ static void __init pmac_pic_probe_oldstyle(void) slave = of_find_node_by_name(master, "mac-io"); /* Check ordering of master & slave */ - if (device_is_compatible(master, "gatwick")) { + if (of_device_is_compatible(master, "gatwick")) { struct device_node *tmp; BUG_ON(slave == NULL); tmp = master; diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index b820cabac69..a410bc76a8a 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -439,76 +439,14 @@ static void __init find_boot_device(void) #endif } -/* TODO: Merge the suspend-to-ram with the common code !!! - * currently, this is a stub implementation for suspend-to-disk - * only - */ - -#ifdef CONFIG_SOFTWARE_SUSPEND - -static int pmac_pm_prepare(suspend_state_t state) -{ - printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); - - return 0; -} - -static int pmac_pm_enter(suspend_state_t state) -{ - printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); - - /* Giveup the lazy FPU & vec so we don't have to back them - * up from the low level code - */ - enable_kernel_fp(); - -#ifdef CONFIG_ALTIVEC - if (cur_cpu_spec->cpu_features & CPU_FTR_ALTIVEC) - enable_kernel_altivec(); -#endif /* CONFIG_ALTIVEC */ - - return 0; -} - -static int pmac_pm_finish(suspend_state_t state) -{ - printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); - - /* Restore userland MMU context */ - set_context(current->active_mm->context.id, current->active_mm->pgd); - - return 0; -} - -static int pmac_pm_valid(suspend_state_t state) -{ - switch (state) { - case PM_SUSPEND_DISK: - return 1; - /* can't do any other states via generic mechanism yet */ - default: - return 0; - } -} - -static struct pm_ops pmac_pm_ops = { - .pm_disk_mode = PM_DISK_SHUTDOWN, - .prepare = pmac_pm_prepare, - .enter = pmac_pm_enter, - .finish = pmac_pm_finish, - .valid = pmac_pm_valid, -}; - -#endif /* CONFIG_SOFTWARE_SUSPEND */ - static int initializing = 1; static int pmac_late_init(void) { initializing = 0; -#ifdef CONFIG_SOFTWARE_SUSPEND - pm_set_ops(&pmac_pm_ops); -#endif /* CONFIG_SOFTWARE_SUSPEND */ + /* this is udbg (which is __init) and we can later use it during + * cpu hotplug (in smp_core99_kick_cpu) */ + ppc_md.progress = NULL; return 0; } @@ -721,12 +659,57 @@ static int pmac_pci_probe_mode(struct pci_bus *bus) /* We need to use normal PCI probing for the AGP bus, * since the device for the AGP bridge isn't in the tree. */ - if (bus->self == NULL && (device_is_compatible(node, "u3-agp") || - device_is_compatible(node, "u4-pcie"))) + if (bus->self == NULL && (of_device_is_compatible(node, "u3-agp") || + of_device_is_compatible(node, "u4-pcie"))) return PCI_PROBE_NORMAL; return PCI_PROBE_DEVTREE; } -#endif + +#ifdef CONFIG_HOTPLUG_CPU +/* access per cpu vars from generic smp.c */ +DECLARE_PER_CPU(int, cpu_state); + +static void pmac_cpu_die(void) +{ + /* + * turn off as much as possible, we'll be + * kicked out as this will only be invoked + * on core99 platforms for now ... + */ + + printk(KERN_INFO "CPU#%d offline\n", smp_processor_id()); + __get_cpu_var(cpu_state) = CPU_DEAD; + smp_wmb(); + + /* + * during the path that leads here preemption is disabled, + * reenable it now so that when coming up preempt count is + * zero correctly + */ + preempt_enable(); + + /* + * hard-disable interrupts for the non-NAP case, the NAP code + * needs to re-enable interrupts (but soft-disables them) + */ + hard_irq_disable(); + + while (1) { + /* let's not take timer interrupts too often ... */ + set_dec(0x7fffffff); + + /* should always be true at this point */ + if (cpu_has_feature(CPU_FTR_CAN_NAP)) + power4_cpu_offline_powersave(); + else { + HMT_low(); + HMT_very_low(); + } + } +} +#endif /* CONFIG_HOTPLUG_CPU */ + +#endif /* CONFIG_PPC64 */ define_machine(powermac) { .name = "PowerMac", @@ -763,6 +746,6 @@ define_machine(powermac) { .phys_mem_access_prot = pci_phys_mem_access_prot, #endif #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC64) - .cpu_die = generic_mach_cpu_die, + .cpu_die = pmac_cpu_die, #endif }; diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index d2cf6136b32..686ed82bde7 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -561,7 +561,7 @@ static void __init smp_core99_setup_i2c_hwsync(int ncpus) /* Look for the clock chip */ while ((cc = of_find_node_by_name(cc, "i2c-hwclock")) != NULL) { p = of_get_parent(cc); - ok = p && device_is_compatible(p, "uni-n-i2c"); + ok = p && of_device_is_compatible(p, "uni-n-i2c"); of_node_put(p); if (!ok) continue; @@ -574,11 +574,11 @@ static void __init smp_core99_setup_i2c_hwsync(int ncpus) continue; switch (*reg) { case 0xd2: - if (device_is_compatible(cc,"pulsar-legacy-slewing")) { + if (of_device_is_compatible(cc,"pulsar-legacy-slewing")) { pmac_tb_freeze = smp_core99_pulsar_tb_freeze; pmac_tb_pulsar_addr = 0xd2; name = "Pulsar"; - } else if (device_is_compatible(cc, "cy28508")) { + } else if (of_device_is_compatible(cc, "cy28508")) { pmac_tb_freeze = smp_core99_cypress_tb_freeze; name = "Cypress"; } @@ -899,7 +899,7 @@ void smp_core99_cpu_die(unsigned int cpu) cpu_dead[cpu] = 0; } -#endif +#endif /* CONFIG_HOTPLUG_CPU && CONFIG_PP32 */ /* Core99 Macs (dual G4s and G5s) */ struct smp_ops_t core99_smp_ops = { @@ -909,8 +909,16 @@ struct smp_ops_t core99_smp_ops = { .setup_cpu = smp_core99_setup_cpu, .give_timebase = smp_core99_give_timebase, .take_timebase = smp_core99_take_timebase, -#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC32) +#if defined(CONFIG_HOTPLUG_CPU) +# if defined(CONFIG_PPC32) .cpu_disable = smp_core99_cpu_disable, .cpu_die = smp_core99_cpu_die, +# endif +# if defined(CONFIG_PPC64) + .cpu_disable = generic_cpu_disable, + .cpu_die = generic_cpu_die, + /* intentionally do *NOT* assign cpu_enable, + * the generic code will use kick_cpu then! */ +# endif #endif }; diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index ea60c451cf8..a1409e450c7 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -273,7 +273,8 @@ void __init ps3_map_htab(void) result = lv1_map_htab(0, &htab_addr); - htab = (hpte_t *)__ioremap(htab_addr, htab_size, PAGE_READONLY_X); + htab = (hpte_t *)__ioremap(htab_addr, htab_size, + pgprot_val(PAGE_READONLY_X)); DBG("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__, htab_addr, (unsigned long)htab); diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index 631c3009561..9da82c266ba 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -89,7 +89,18 @@ struct ps3_private { static DEFINE_PER_CPU(struct ps3_private, ps3_private); -int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, +/** + * ps3_virq_setup - virq related setup. + * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be + * serviced on. + * @outlet: The HV outlet from the various create outlet routines. + * @virq: The assigned Linux virq. + * + * Calls irq_create_mapping() to get a virq and sets the chip data to + * ps3_private data. + */ + +int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, unsigned int *virq) { int result; @@ -111,17 +122,6 @@ int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, goto fail_create; } - /* Binds outlet to cpu + virq. */ - - result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); - - if (result) { - pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", - __func__, __LINE__, ps3_result(result)); - result = -EPERM; - goto fail_connect; - } - pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, outlet, cpu, *virq); @@ -136,94 +136,118 @@ int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, return result; fail_set: - lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq); -fail_connect: irq_dispose_mapping(*virq); fail_create: return result; } -EXPORT_SYMBOL_GPL(ps3_alloc_irq); -int ps3_free_irq(unsigned int virq) +/** + * ps3_virq_destroy - virq related teardown. + * @virq: The assigned Linux virq. + * + * Clears chip data and calls irq_dispose_mapping() for the virq. + */ + +int ps3_virq_destroy(unsigned int virq) { - int result; const struct ps3_private *pd = get_irq_chip_data(virq); pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, pd->node, pd->cpu, virq); - result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); - - if (result) - pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", - __func__, __LINE__, ps3_result(result)); - set_irq_chip_data(virq, NULL); irq_dispose_mapping(virq); - return result; + + pr_debug("%s:%d <-\n", __func__, __LINE__); + return 0; } -EXPORT_SYMBOL_GPL(ps3_free_irq); /** - * ps3_alloc_io_irq - Assign a virq to a system bus device. + * ps3_irq_plug_setup - Generic outlet and virq related setup. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. - * @interrupt_id: The device interrupt id read from the system repository. + * @outlet: The HV outlet from the various create outlet routines. * @virq: The assigned Linux virq. * - * An io irq represents a non-virtualized device interrupt. interrupt_id - * coresponds to the interrupt number of the interrupt controller. + * Sets up virq and connects the irq plug. */ -int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id, +int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, unsigned int *virq) { int result; - unsigned long outlet; + struct ps3_private *pd; - result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); + result = ps3_virq_setup(cpu, outlet, virq); if (result) { - pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", - __func__, __LINE__, ps3_result(result)); - return result; + pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); + goto fail_setup; } - result = ps3_alloc_irq(cpu, outlet, virq); - BUG_ON(result); + pd = get_irq_chip_data(*virq); + + /* Binds outlet to cpu + virq. */ + + result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); + if (result) { + pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", + __func__, __LINE__, ps3_result(result)); + result = -EPERM; + goto fail_connect; + } + + return result; + +fail_connect: + ps3_virq_destroy(*virq); +fail_setup: return result; } -EXPORT_SYMBOL_GPL(ps3_alloc_io_irq); +EXPORT_SYMBOL_GPL(ps3_irq_plug_setup); + +/** + * ps3_irq_plug_destroy - Generic outlet and virq related teardown. + * @virq: The assigned Linux virq. + * + * Disconnects the irq plug and tears down virq. + * Do not call for system bus event interrupts setup with + * ps3_sb_event_receive_port_setup(). + */ -int ps3_free_io_irq(unsigned int virq) +int ps3_irq_plug_destroy(unsigned int virq) { int result; + const struct ps3_private *pd = get_irq_chip_data(virq); - result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); + pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, + pd->node, pd->cpu, virq); + + result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); if (result) - pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", - __func__, __LINE__, ps3_result(result)); + pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", + __func__, __LINE__, ps3_result(result)); - ps3_free_irq(virq); + ps3_virq_destroy(virq); return result; } -EXPORT_SYMBOL_GPL(ps3_free_io_irq); +EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy); /** - * ps3_alloc_event_irq - Allocate a virq for use with a system event. + * ps3_event_receive_port_setup - Setup an event receive port. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. * @virq: The assigned Linux virq. * * The virq can be used with lv1_connect_interrupt_event_receive_port() to - * arrange to receive events, or with ps3_send_event_locally() to signal - * events. + * arrange to receive interrupts from system-bus devices, or with + * ps3_send_event_locally() to signal events. */ -int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq) +int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq) { int result; unsigned long outlet; @@ -237,17 +261,27 @@ int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq) return result; } - result = ps3_alloc_irq(cpu, outlet, virq); + result = ps3_irq_plug_setup(cpu, outlet, virq); BUG_ON(result); return result; } +EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup); + +/** + * ps3_event_receive_port_destroy - Destroy an event receive port. + * @virq: The assigned Linux virq. + * + * Since ps3_event_receive_port_destroy destroys the receive port outlet, + * SB devices need to call disconnect_interrupt_event_receive_port() before + * this. + */ -int ps3_free_event_irq(unsigned int virq) +int ps3_event_receive_port_destroy(unsigned int virq) { int result; - pr_debug(" -> %s:%d\n", __func__, __LINE__); + pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq); result = lv1_destruct_event_receive_port(virq_to_hw(virq)); @@ -255,11 +289,17 @@ int ps3_free_event_irq(unsigned int virq) pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", __func__, __LINE__, ps3_result(result)); - ps3_free_irq(virq); + /* lv1_destruct_event_receive_port() destroys the IRQ plug, + * so don't call ps3_irq_plug_destroy() here. + */ + + result = ps3_virq_destroy(virq); + BUG_ON(result); pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } +EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy); int ps3_send_event_locally(unsigned int virq) { @@ -267,7 +307,7 @@ int ps3_send_event_locally(unsigned int virq) } /** - * ps3_connect_event_irq - Assign a virq to a system bus device. + * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. * @did: The HV device identifier read from the system repository. @@ -278,13 +318,15 @@ int ps3_send_event_locally(unsigned int virq) * coresponds to the software interrupt number. */ -int ps3_connect_event_irq(enum ps3_cpu_binding cpu, +int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, const struct ps3_device_id *did, unsigned int interrupt_id, unsigned int *virq) { + /* this should go in system-bus.c */ + int result; - result = ps3_alloc_event_irq(cpu, virq); + result = ps3_event_receive_port_setup(cpu, virq); if (result) return result; @@ -296,7 +338,7 @@ int ps3_connect_event_irq(enum ps3_cpu_binding cpu, pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" " failed: %s\n", __func__, __LINE__, ps3_result(result)); - ps3_free_event_irq(*virq); + ps3_event_receive_port_destroy(*virq); *virq = NO_IRQ; return result; } @@ -306,10 +348,13 @@ int ps3_connect_event_irq(enum ps3_cpu_binding cpu, return 0; } +EXPORT_SYMBOL(ps3_sb_event_receive_port_setup); -int ps3_disconnect_event_irq(const struct ps3_device_id *did, +int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, unsigned int interrupt_id, unsigned int virq) { + /* this should go in system-bus.c */ + int result; pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, @@ -323,14 +368,65 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did, " failed: %s\n", __func__, __LINE__, ps3_result(result)); - ps3_free_event_irq(virq); + result = ps3_event_receive_port_destroy(virq); + BUG_ON(result); pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } +EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy); /** - * ps3_alloc_vuart_irq - Configure the system virtual uart virq. + * ps3_io_irq_setup - Setup a system bus io irq. + * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be + * serviced on. + * @interrupt_id: The device interrupt id read from the system repository. + * @virq: The assigned Linux virq. + * + * An io irq represents a non-virtualized device interrupt. interrupt_id + * coresponds to the interrupt number of the interrupt controller. + */ + +int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, + unsigned int *virq) +{ + int result; + unsigned long outlet; + + result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); + + if (result) { + pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + result = ps3_irq_plug_setup(cpu, outlet, virq); + BUG_ON(result); + + return result; +} +EXPORT_SYMBOL_GPL(ps3_io_irq_setup); + +int ps3_io_irq_destroy(unsigned int virq) +{ + int result; + + result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); + + if (result) + pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + result = ps3_irq_plug_destroy(virq); + BUG_ON(result); + + return result; +} +EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); + +/** + * ps3_vuart_irq_setup - Setup the system virtual uart virq. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. @@ -340,7 +436,7 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did, * freeing the interrupt will return a wrong state error. */ -int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, +int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, unsigned int *virq) { int result; @@ -359,13 +455,13 @@ int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, return result; } - result = ps3_alloc_irq(cpu, outlet, virq); + result = ps3_irq_plug_setup(cpu, outlet, virq); BUG_ON(result); return result; } -int ps3_free_vuart_irq(unsigned int virq) +int ps3_vuart_irq_destroy(unsigned int virq) { int result; @@ -377,13 +473,14 @@ int ps3_free_vuart_irq(unsigned int virq) return result; } - ps3_free_irq(virq); + result = ps3_irq_plug_destroy(virq); + BUG_ON(result); return result; } /** - * ps3_alloc_spe_irq - Configure an spe virq. + * ps3_spe_irq_setup - Setup an spe virq. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. * @spe_id: The spe_id returned from lv1_construct_logical_spe(). @@ -392,7 +489,7 @@ int ps3_free_vuart_irq(unsigned int virq) * */ -int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, +int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, unsigned int class, unsigned int *virq) { int result; @@ -408,15 +505,16 @@ int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, return result; } - result = ps3_alloc_irq(cpu, outlet, virq); + result = ps3_irq_plug_setup(cpu, outlet, virq); BUG_ON(result); return result; } -int ps3_free_spe_irq(unsigned int virq) +int ps3_spe_irq_destroy(unsigned int virq) { - ps3_free_irq(virq); + int result = ps3_irq_plug_destroy(virq); + BUG_ON(result); return 0; } diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index 2014d2b4444..f8a3e206c58 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -826,5 +826,4 @@ void __init ps3_mm_init(void) void ps3_mm_shutdown(void) { ps3_mm_region_destroy(&map.r1); - map.total = map.rm.size; } diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index ac5df9688dc..c9894933084 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -137,6 +137,12 @@ early_param("ps3fb", early_parse_ps3fb); #define prealloc_ps3fb_videomemory() do { } while (0) #endif +static int ps3_set_dabr(u64 dabr) +{ + enum {DABR_USER = 1, DABR_KERNEL = 2,}; + + return lv1_set_dabr(dabr, DABR_KERNEL | DABR_USER) ? -1 : 0; +} static void __init ps3_setup_arch(void) { @@ -234,6 +240,7 @@ define_machine(ps3) { .get_boot_time = ps3_get_boot_time, .set_rtc_time = ps3_set_rtc_time, .get_rtc_time = ps3_get_rtc_time, + .set_dabr = ps3_set_dabr, .calibrate_decr = ps3_calibrate_decr, .progress = ps3_progress, .restart = ps3_restart, diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c index 6fb887961a6..8729348c060 100644 --- a/arch/powerpc/platforms/ps3/smp.c +++ b/arch/powerpc/platforms/ps3/smp.c @@ -110,7 +110,7 @@ static void __init ps3_smp_setup_cpu(int cpu) BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3); for (i = 0; i < MSG_COUNT; i++) { - result = ps3_alloc_event_irq(cpu, &virqs[i]); + result = ps3_event_receive_port_setup(cpu, &virqs[i]); if (result) continue; @@ -134,11 +134,13 @@ void ps3_smp_cleanup_cpu(int cpu) int i; DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); + for (i = 0; i < MSG_COUNT; i++) { - ps3_free_event_irq(virqs[i]); free_irq(virqs[i], (void*)(long)i); + ps3_event_receive_port_destroy(virqs[i]); virqs[i] = NO_IRQ; } + DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu); } diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index a397e4e17c1..651437cb2c1 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -184,7 +184,7 @@ static int __init setup_areas(struct spu *spu) spu_pdata(spu)->shadow = __ioremap( spu_pdata(spu)->shadow_addr, sizeof(struct spe_shadow), - PAGE_READONLY | _PAGE_NO_CACHE | _PAGE_GUARDED); + pgprot_val(PAGE_READONLY) | _PAGE_NO_CACHE | _PAGE_GUARDED); if (!spu_pdata(spu)->shadow) { pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__); goto fail_ioremap; @@ -230,19 +230,19 @@ static int __init setup_interrupts(struct spu *spu) { int result; - result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, + result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, 0, &spu->irqs[0]); if (result) goto fail_alloc_0; - result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, + result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, 1, &spu->irqs[1]); if (result) goto fail_alloc_1; - result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, + result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, 2, &spu->irqs[2]); if (result) @@ -251,9 +251,9 @@ static int __init setup_interrupts(struct spu *spu) return result; fail_alloc_2: - ps3_free_spe_irq(spu->irqs[1]); + ps3_spe_irq_destroy(spu->irqs[1]); fail_alloc_1: - ps3_free_spe_irq(spu->irqs[0]); + ps3_spe_irq_destroy(spu->irqs[0]); fail_alloc_0: spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ; return result; @@ -301,9 +301,9 @@ static int ps3_destroy_spu(struct spu *spu) result = lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0); BUG_ON(result); - ps3_free_spe_irq(spu->irqs[2]); - ps3_free_spe_irq(spu->irqs[1]); - ps3_free_spe_irq(spu->irqs[0]); + ps3_spe_irq_destroy(spu->irqs[2]); + ps3_spe_irq_destroy(spu->irqs[1]); + ps3_spe_irq_destroy(spu->irqs[0]); spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ; diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 90235d59875..ae1fc92dc1c 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_PCI) += pci.o pci_dlpar.o +obj-$(CONFIG_PCI_MSI) += msi.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 48fbd442e9d..63e23062e98 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -580,6 +580,36 @@ rtas_pci_slot_reset(struct pci_dn *pdn, int state) } /** + * pcibios_set_pcie_slot_reset - Set PCI-E reset state + * @dev: pci device struct + * @state: reset state to enter + * + * Return value: + * 0 if success + **/ +int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) +{ + struct device_node *dn = pci_device_to_OF_node(dev); + struct pci_dn *pdn = PCI_DN(dn); + + switch (state) { + case pcie_deassert_reset: + rtas_pci_slot_reset(pdn, 0); + break; + case pcie_hot_reset: + rtas_pci_slot_reset(pdn, 1); + break; + case pcie_warm_reset: + rtas_pci_slot_reset(pdn, 3); + break; + default: + return -EINVAL; + }; + + return 0; +} + +/** * rtas_set_slot_reset -- assert the pci #RST line for 1/4 second * @pdn: pci device node to be reset. * diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 66665c82415..eec684a8e44 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -504,6 +504,12 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) break; } + if (!pdn || !PCI_DN(pdn)) { + printk(KERN_WARNING "pci_dma_dev_setup_pSeriesLP: " + "no DMA window found for pci dev=%s dn=%s\n", + pci_name(dev), dn? dn->full_name : "<null>"); + return; + } DBG(" parent is %s\n", pdn->full_name); /* Check for parent == NULL so we don't try to setup the empty EADS diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 3a70e8ad7bc..362dfbc260a 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -231,13 +231,13 @@ void __init find_udbg_vterm(void) goto out; vtermno = termno[0]; - if (device_is_compatible(stdout_node, "hvterm1")) { + if (of_device_is_compatible(stdout_node, "hvterm1")) { udbg_putc = udbg_putcLP; udbg_getc = udbg_getcLP; udbg_getc_poll = udbg_getc_pollLP; if (add_console) add_preferred_console("hvc", termno[0] & 0xff, NULL); - } else if (device_is_compatible(stdout_node, "hvterm-protocol")) { + } else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { vtermno = termno[0]; udbg_putc = udbg_hvsi_putc; udbg_getc = udbg_hvsi_getc; diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c new file mode 100644 index 00000000000..6063ea2f67a --- /dev/null +++ b/arch/powerpc/platforms/pseries/msi.c @@ -0,0 +1,270 @@ +/* + * Copyright 2006 Jake Moilanen <moilanen@austin.ibm.com>, IBM Corp. + * Copyright 2006-2007 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; version 2 of the + * License. + * + */ + +#include <linux/device.h> +#include <linux/irq.h> +#include <linux/msi.h> + +#include <asm/rtas.h> +#include <asm/hw_irq.h> +#include <asm/ppc-pci.h> + +static int query_token, change_token; + +#define RTAS_QUERY_FN 0 +#define RTAS_CHANGE_FN 1 +#define RTAS_RESET_FN 2 +#define RTAS_CHANGE_MSI_FN 3 +#define RTAS_CHANGE_MSIX_FN 4 + +static struct pci_dn *get_pdn(struct pci_dev *pdev) +{ + struct device_node *dn; + struct pci_dn *pdn; + + dn = pci_device_to_OF_node(pdev); + if (!dn) { + dev_dbg(&pdev->dev, "rtas_msi: No OF device node\n"); + return NULL; + } + + pdn = PCI_DN(dn); + if (!pdn) { + dev_dbg(&pdev->dev, "rtas_msi: No PCI DN\n"); + return NULL; + } + + return pdn; +} + +/* RTAS Helpers */ + +static int rtas_change_msi(struct pci_dn *pdn, u32 func, u32 num_irqs) +{ + u32 addr, seq_num, rtas_ret[3]; + unsigned long buid; + int rc; + + addr = rtas_config_addr(pdn->busno, pdn->devfn, 0); + buid = pdn->phb->buid; + + seq_num = 1; + do { + if (func == RTAS_CHANGE_MSI_FN || func == RTAS_CHANGE_MSIX_FN) + rc = rtas_call(change_token, 6, 4, rtas_ret, addr, + BUID_HI(buid), BUID_LO(buid), + func, num_irqs, seq_num); + else + rc = rtas_call(change_token, 6, 3, rtas_ret, addr, + BUID_HI(buid), BUID_LO(buid), + func, num_irqs, seq_num); + + seq_num = rtas_ret[1]; + } while (rtas_busy_delay(rc)); + + if (rc == 0) /* Success */ + rc = rtas_ret[0]; + + pr_debug("rtas_msi: ibm,change_msi(func=%d,num=%d) = (%d)\n", + func, num_irqs, rc); + + return rc; +} + +static void rtas_disable_msi(struct pci_dev *pdev) +{ + struct pci_dn *pdn; + + pdn = get_pdn(pdev); + if (!pdn) + return; + + if (rtas_change_msi(pdn, RTAS_CHANGE_FN, 0) != 0) + pr_debug("rtas_msi: Setting MSIs to 0 failed!\n"); +} + +static int rtas_query_irq_number(struct pci_dn *pdn, int offset) +{ + u32 addr, rtas_ret[2]; + unsigned long buid; + int rc; + + addr = rtas_config_addr(pdn->busno, pdn->devfn, 0); + buid = pdn->phb->buid; + + do { + rc = rtas_call(query_token, 4, 3, rtas_ret, addr, + BUID_HI(buid), BUID_LO(buid), offset); + } while (rtas_busy_delay(rc)); + + if (rc) { + pr_debug("rtas_msi: error (%d) querying source number\n", rc); + return rc; + } + + return rtas_ret[0]; +} + +static void rtas_teardown_msi_irqs(struct pci_dev *pdev) +{ + struct msi_desc *entry; + + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + + set_irq_msi(entry->irq, NULL); + irq_dispose_mapping(entry->irq); + } + + rtas_disable_msi(pdev); +} + +static int check_req_msi(struct pci_dev *pdev, int nvec) +{ + struct device_node *dn; + struct pci_dn *pdn; + const u32 *req_msi; + + pdn = get_pdn(pdev); + if (!pdn) + return -ENODEV; + + dn = pdn->node; + + req_msi = of_get_property(dn, "ibm,req#msi", NULL); + if (!req_msi) { + pr_debug("rtas_msi: No ibm,req#msi on %s\n", dn->full_name); + return -ENOENT; + } + + if (*req_msi < nvec) { + pr_debug("rtas_msi: ibm,req#msi requests < %d MSIs\n", nvec); + return -ENOSPC; + } + + return 0; +} + +static int rtas_msi_check_device(struct pci_dev *pdev, int nvec, int type) +{ + if (type == PCI_CAP_ID_MSIX) + pr_debug("rtas_msi: MSI-X untested, trying anyway.\n"); + + return check_req_msi(pdev, nvec); +} + +static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) +{ + struct pci_dn *pdn; + int hwirq, virq, i, rc; + struct msi_desc *entry; + + pdn = get_pdn(pdev); + if (!pdn) + return -ENODEV; + + /* + * Try the new more explicit firmware interface, if that fails fall + * back to the old interface. The old interface is known to never + * return MSI-Xs. + */ + if (type == PCI_CAP_ID_MSI) { + rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec); + + if (rc != nvec) { + pr_debug("rtas_msi: trying the old firmware call.\n"); + rc = rtas_change_msi(pdn, RTAS_CHANGE_FN, nvec); + } + } else + rc = rtas_change_msi(pdn, RTAS_CHANGE_MSIX_FN, nvec); + + if (rc != nvec) { + pr_debug("rtas_msi: rtas_change_msi() failed\n"); + + /* + * In case of an error it's not clear whether the device is + * left with MSI enabled or not, so we explicitly disable. + */ + goto out_free; + } + + i = 0; + list_for_each_entry(entry, &pdev->msi_list, list) { + hwirq = rtas_query_irq_number(pdn, i); + if (hwirq < 0) { + rc = hwirq; + pr_debug("rtas_msi: error (%d) getting hwirq\n", rc); + goto out_free; + } + + virq = irq_create_mapping(NULL, hwirq); + + if (virq == NO_IRQ) { + pr_debug("rtas_msi: Failed mapping hwirq %d\n", hwirq); + rc = -ENOSPC; + goto out_free; + } + + dev_dbg(&pdev->dev, "rtas_msi: allocated virq %d\n", virq); + set_irq_msi(virq, entry); + unmask_msi_irq(virq); + } + + return 0; + + out_free: + rtas_teardown_msi_irqs(pdev); + return rc; +} + +static void rtas_msi_pci_irq_fixup(struct pci_dev *pdev) +{ + /* No LSI -> leave MSIs (if any) configured */ + if (pdev->irq == NO_IRQ) { + dev_dbg(&pdev->dev, "rtas_msi: no LSI, nothing to do.\n"); + return; + } + + /* No MSI -> MSIs can't have been assigned by fw, leave LSI */ + if (check_req_msi(pdev, 1)) { + dev_dbg(&pdev->dev, "rtas_msi: no req#msi, nothing to do.\n"); + return; + } + + dev_dbg(&pdev->dev, "rtas_msi: disabling existing MSI.\n"); + rtas_disable_msi(pdev); +} + +static int rtas_msi_init(void) +{ + query_token = rtas_token("ibm,query-interrupt-source-number"); + change_token = rtas_token("ibm,change-msi"); + + if ((query_token == RTAS_UNKNOWN_SERVICE) || + (change_token == RTAS_UNKNOWN_SERVICE)) { + pr_debug("rtas_msi: no RTAS tokens, no MSI support.\n"); + return -1; + } + + pr_debug("rtas_msi: Registering RTAS MSI callbacks.\n"); + + WARN_ON(ppc_md.setup_msi_irqs); + ppc_md.setup_msi_irqs = rtas_setup_msi_irqs; + ppc_md.teardown_msi_irqs = rtas_teardown_msi_irqs; + ppc_md.msi_check_device = rtas_msi_check_device; + + WARN_ON(ppc_md.pci_irq_fixup); + ppc_md.pci_irq_fixup = rtas_msi_pci_irq_fixup; + + return 0; +} +arch_initcall(rtas_msi_init); diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index fdc1a369f76..ffaf6c5c517 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -79,6 +79,7 @@ pcibios_remove_pci_devices(struct pci_bus *bus) pci_remove_bus_device(dev); } } +EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); /* Must be called before pci_bus_add_devices */ void diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 33eec2822c6..470db6efaeb 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -168,7 +168,7 @@ static void __init pseries_mpic_init_IRQ(void) /* Look for cascade */ for_each_node_by_type(np, "interrupt-controller") - if (device_is_compatible(np, "chrp,iic")) { + if (of_device_is_compatible(np, "chrp,iic")) { cascade = np; break; } diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 896cbf340c4..b854e7f1001 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -477,7 +477,7 @@ static int xics_host_match(struct irq_host *h, struct device_node *node) * like vdevices, events, etc... The trick we use here is to match * everything here except the legacy 8259 which is compatible "chrp,iic" */ - return !device_is_compatible(node, "chrp,iic"); + return !of_device_is_compatible(node, "chrp,iic"); } static int xics_host_map_direct(struct irq_host *h, unsigned int virq, @@ -618,7 +618,7 @@ static void __init xics_setup_8259_cascade(void) unsigned long intack = 0; for_each_node_by_type(np, "interrupt-controller") - if (device_is_compatible(np, "chrp,iic")) { + if (of_device_is_compatible(np, "chrp,iic")) { found = np; break; } |