From 674bfa485554156aa90ce17288712fcb568a42c3 Mon Sep 17 00:00:00 2001 From: Suzuki Poulose Date: Mon, 18 Jul 2011 03:29:20 +0000 Subject: powerpc/44x: Kexec support for PPC440X chipsets This patch adds kexec support for PPC440 based chipsets. This work is based on the KEXEC patches for FSL BookE. The FSL BookE patch and the code flow could be found at the link below: http://patchwork.ozlabs.org/patch/49359/ Steps: 1) Invalidate all the TLB entries except the one this code is run from 2) Create a tmp mapping for our code in the other address space and jump to it 3) Invalidate the entry we used 4) Create a 1:1 mapping for 0-2GiB in blocks of 256M 5) Jump to the new 1:1 mapping and invalidate the tmp mapping I have tested this patches on Ebony, Sequoia boards and Virtex on QEMU. You need kexec-tools commit e8b7939b1e or newer for ppc440x support, available at: git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git Signed-off-by: Suzuki Poulose Cc: Sebastian Andrzej Siewior Signed-off-by: Josh Boyer --- arch/powerpc/Kconfig | 2 +- arch/powerpc/include/asm/kexec.h | 2 +- arch/powerpc/kernel/misc_32.S | 171 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 6926b61acfe..0a3d5560c9b 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -345,7 +345,7 @@ config ARCH_ENABLE_MEMORY_HOTREMOVE config KEXEC bool "kexec system call (EXPERIMENTAL)" - depends on (PPC_BOOK3S || FSL_BOOKE) && EXPERIMENTAL + depends on (PPC_BOOK3S || FSL_BOOKE || (44x && !SMP && !47x)) && EXPERIMENTAL help kexec is a system call that implements the ability to shutdown your current kernel, and to start another kernel. It is like a reboot diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 8a33698c61b..f921eb121d3 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -2,7 +2,7 @@ #define _ASM_POWERPC_KEXEC_H #ifdef __KERNEL__ -#ifdef CONFIG_FSL_BOOKE +#if defined(CONFIG_FSL_BOOKE) || defined(CONFIG_44x) /* * On FSL-BookE we setup a 1:1 mapping which covers the first 2GiB of memory diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 998a1002860..f7d760ab5ca 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -8,6 +8,8 @@ * kexec bits: * Copyright (C) 2002-2003 Eric Biederman * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * PPC44x port. Copyright (C) 2011, IBM Corporation + * Author: Suzuki Poulose * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -735,6 +737,175 @@ relocate_new_kernel: mr r4, r30 mr r5, r31 + li r0, 0 +#elif defined(CONFIG_44x) && !defined(CONFIG_47x) + +/* + * Code for setting up 1:1 mapping for PPC440x for KEXEC + * + * We cannot switch off the MMU on PPC44x. + * So we: + * 1) Invalidate all the mappings except the one we are running from. + * 2) Create a tmp mapping for our code in the other address space(TS) and + * jump to it. Invalidate the entry we started in. + * 3) Create a 1:1 mapping for 0-2GiB in chunks of 256M in original TS. + * 4) Jump to the 1:1 mapping in original TS. + * 5) Invalidate the tmp mapping. + * + * - Based on the kexec support code for FSL BookE + * - Doesn't support 47x yet. + * + */ + /* Save our parameters */ + mr r29, r3 + mr r30, r4 + mr r31, r5 + + /* Load our MSR_IS and TID to MMUCR for TLB search */ + mfspr r3,SPRN_PID + mfmsr r4 + andi. r4,r4,MSR_IS@l + beq wmmucr + oris r3,r3,PPC44x_MMUCR_STS@h +wmmucr: + mtspr SPRN_MMUCR,r3 + sync + + /* + * Invalidate all the TLB entries except the current entry + * where we are running from + */ + bl 0f /* Find our address */ +0: mflr r5 /* Make it accessible */ + tlbsx r23,0,r5 /* Find entry we are in */ + li r4,0 /* Start at TLB entry 0 */ + li r3,0 /* Set PAGEID inval value */ +1: cmpw r23,r4 /* Is this our entry? */ + beq skip /* If so, skip the inval */ + tlbwe r3,r4,PPC44x_TLB_PAGEID /* If not, inval the entry */ +skip: + addi r4,r4,1 /* Increment */ + cmpwi r4,64 /* Are we done? */ + bne 1b /* If not, repeat */ + isync + + /* Create a temp mapping and jump to it */ + andi. r6, r23, 1 /* Find the index to use */ + addi r24, r6, 1 /* r24 will contain 1 or 2 */ + + mfmsr r9 /* get the MSR */ + rlwinm r5, r9, 27, 31, 31 /* Extract the MSR[IS] */ + xori r7, r5, 1 /* Use the other address space */ + + /* Read the current mapping entries */ + tlbre r3, r23, PPC44x_TLB_PAGEID + tlbre r4, r23, PPC44x_TLB_XLAT + tlbre r5, r23, PPC44x_TLB_ATTRIB + + /* Save our current XLAT entry */ + mr r25, r4 + + /* Extract the TLB PageSize */ + li r10, 1 /* r10 will hold PageSize */ + rlwinm r11, r3, 0, 24, 27 /* bits 24-27 */ + + /* XXX: As of now we use 256M, 4K pages */ + cmpwi r11, PPC44x_TLB_256M + bne tlb_4k + rotlwi r10, r10, 28 /* r10 = 256M */ + b write_out +tlb_4k: + cmpwi r11, PPC44x_TLB_4K + bne default + rotlwi r10, r10, 12 /* r10 = 4K */ + b write_out +default: + rotlwi r10, r10, 10 /* r10 = 1K */ + +write_out: + /* + * Write out the tmp 1:1 mapping for this code in other address space + * Fixup EPN = RPN , TS=other address space + */ + insrwi r3, r7, 1, 23 /* Bit 23 is TS for PAGEID field */ + + /* Write out the tmp mapping entries */ + tlbwe r3, r24, PPC44x_TLB_PAGEID + tlbwe r4, r24, PPC44x_TLB_XLAT + tlbwe r5, r24, PPC44x_TLB_ATTRIB + + subi r11, r10, 1 /* PageOffset Mask = PageSize - 1 */ + not r10, r11 /* Mask for PageNum */ + + /* Switch to other address space in MSR */ + insrwi r9, r7, 1, 26 /* Set MSR[IS] = r7 */ + + bl 1f +1: mflr r8 + addi r8, r8, (2f-1b) /* Find the target offset */ + + /* Jump to the tmp mapping */ + mtspr SPRN_SRR0, r8 + mtspr SPRN_SRR1, r9 + rfi + +2: + /* Invalidate the entry we were executing from */ + li r3, 0 + tlbwe r3, r23, PPC44x_TLB_PAGEID + + /* attribute fields. rwx for SUPERVISOR mode */ + li r5, 0 + ori r5, r5, (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G) + + /* Create 1:1 mapping in 256M pages */ + xori r7, r7, 1 /* Revert back to Original TS */ + + li r8, 0 /* PageNumber */ + li r6, 3 /* TLB Index, start at 3 */ + +next_tlb: + rotlwi r3, r8, 28 /* Create EPN (bits 0-3) */ + mr r4, r3 /* RPN = EPN */ + ori r3, r3, (PPC44x_TLB_VALID | PPC44x_TLB_256M) /* SIZE = 256M, Valid */ + insrwi r3, r7, 1, 23 /* Set TS from r7 */ + + tlbwe r3, r6, PPC44x_TLB_PAGEID /* PageID field : EPN, V, SIZE */ + tlbwe r4, r6, PPC44x_TLB_XLAT /* Address translation : RPN */ + tlbwe r5, r6, PPC44x_TLB_ATTRIB /* Attributes */ + + addi r8, r8, 1 /* Increment PN */ + addi r6, r6, 1 /* Increment TLB Index */ + cmpwi r8, 8 /* Are we done ? */ + bne next_tlb + isync + + /* Jump to the new mapping 1:1 */ + li r9,0 + insrwi r9, r7, 1, 26 /* Set MSR[IS] = r7 */ + + bl 1f +1: mflr r8 + and r8, r8, r11 /* Get our offset within page */ + addi r8, r8, (2f-1b) + + and r5, r25, r10 /* Get our target PageNum */ + or r8, r8, r5 /* Target jump address */ + + mtspr SPRN_SRR0, r8 + mtspr SPRN_SRR1, r9 + rfi +2: + /* Invalidate the tmp entry we used */ + li r3, 0 + tlbwe r3, r24, PPC44x_TLB_PAGEID + sync + + /* Restore the parameters */ + mr r3, r29 + mr r4, r30 + mr r5, r31 + li r0, 0 #else li r0, 0 -- cgit v1.2.3-18-g5258 From 2f6bab96ef60dd87e0a17272f9f8a6ee12118d32 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 18 Jul 2011 03:49:14 +0000 Subject: powerpc/44x: Add NOR flash device to Yosemite dts Signed-off-by: Stefan Roese Signed-off-by: Josh Boyer --- arch/powerpc/boot/dts/yosemite.dts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/arch/powerpc/boot/dts/yosemite.dts b/arch/powerpc/boot/dts/yosemite.dts index 64923245f0e..30bb4753577 100644 --- a/arch/powerpc/boot/dts/yosemite.dts +++ b/arch/powerpc/boot/dts/yosemite.dts @@ -138,6 +138,42 @@ clock-frequency = <0>; /* Filled in by zImage */ interrupts = <0x5 0x1>; interrupt-parent = <&UIC1>; + + nor_flash@0,0 { + compatible = "amd,s29gl256n", "cfi-flash"; + bank-width = <2>; + reg = <0x00000000 0x00000000 0x04000000>; + #address-cells = <1>; + #size-cells = <1>; + partition@0 { + label = "kernel"; + reg = <0x00000000 0x001e0000>; + }; + partition@1e0000 { + label = "dtb"; + reg = <0x001e0000 0x00020000>; + }; + partition@200000 { + label = "ramdisk"; + reg = <0x00200000 0x01400000>; + }; + partition@1600000 { + label = "jffs2"; + reg = <0x01600000 0x00400000>; + }; + partition@1a00000 { + label = "user"; + reg = <0x01a00000 0x02540000>; + }; + partition@3f40000 { + label = "env"; + reg = <0x03f40000 0x00040000>; + }; + partition@3f80000 { + label = "u-boot"; + reg = <0x03f80000 0x00080000>; + }; + }; }; UART0: serial@ef600300 { -- cgit v1.2.3-18-g5258 From e7fa1d131472b1134d33aba56a83f8f7689f54fd Mon Sep 17 00:00:00 2001 From: Ayman El-Khashab Date: Wed, 20 Jul 2011 03:02:29 +0000 Subject: powerpc/4xx: enable and fix pcie gen1/gen2 on the 460sx Adds a register to the config space for the 460sx. Changes the vc0 detect to a pll detect. maps configuration space to test the link status. changes the setup to enable gen2 devices to operate at gen2 speeds. fixes mapping that was not correct for the 460sx. added bit definitions for the OMRxMSKL registers. Removed reserved bit that was set incorrectly in the OMR2MSKL register. tested on the 460sx eiger and custom board Signed-off-by: Ayman El-Khashab Signed-off-by: Josh Boyer --- arch/powerpc/sysdev/ppc4xx_pci.c | 89 ++++++++++++++++++++++++++++++++-------- arch/powerpc/sysdev/ppc4xx_pci.h | 12 ++++++ 2 files changed, 84 insertions(+), 17 deletions(-) diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index a59ba96d2c2..edd51c85835 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -1091,6 +1091,10 @@ static int __init ppc460sx_pciex_core_init(struct device_node *np) mtdcri(SDR0, PESDR1_460SX_HSSSLEW, 0xFFFF0000); mtdcri(SDR0, PESDR2_460SX_HSSSLEW, 0xFFFF0000); + /* Set HSS PRBS enabled */ + mtdcri(SDR0, PESDR0_460SX_HSSCTLSET, 0x00001130); + mtdcri(SDR0, PESDR2_460SX_HSSCTLSET, 0x00001130); + udelay(100); /* De-assert PLLRESET */ @@ -1131,9 +1135,6 @@ static int ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2, 0, 0x01000000); - /*Gen-1*/ - mtdcri(SDR0, port->sdr_base + PESDRn_460SX_RCEI, 0x08000000); - dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL), PESDRx_RCSSET_RSTPYN); @@ -1147,14 +1148,42 @@ static int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port) { /* Max 128 Bytes */ out_be32 (port->utl_base + PEUTL_PBBSZ, 0x00000000); + /* Assert VRB and TXE - per datasheet turn off addr validation */ + out_be32(port->utl_base + PEUTL_PCTL, 0x80800000); return 0; } +static void __init ppc460sx_pciex_check_link(struct ppc4xx_pciex_port *port) +{ + void __iomem *mbase; + int attempt = 50; + + port->link = 0; + + mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000); + if (mbase == NULL) { + printk(KERN_ERR "%s: Can't map internal config space !", + port->node->full_name); + goto done; + } + + while (attempt && (0 == (in_le32(mbase + PECFG_460SX_DLLSTA) + & PECFG_460SX_DLLSTA_LINKUP))) { + attempt--; + mdelay(10); + } + if (attempt) + port->link = 1; +done: + iounmap(mbase); + +} + static struct ppc4xx_pciex_hwops ppc460sx_pcie_hwops __initdata = { .core_init = ppc460sx_pciex_core_init, .port_init_hw = ppc460sx_pciex_init_port_hw, .setup_utl = ppc460sx_pciex_init_utl, - .check_link = ppc4xx_pciex_check_link_sdr, + .check_link = ppc460sx_pciex_check_link, }; #endif /* CONFIG_44x */ @@ -1337,15 +1366,15 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) if (rc != 0) return rc; - if (ppc4xx_pciex_hwops->check_link) - ppc4xx_pciex_hwops->check_link(port); - /* * Initialize mapping: disable all regions and configure * CFG and REG regions based on resources in the device tree */ ppc4xx_pciex_port_init_mapping(port); + if (ppc4xx_pciex_hwops->check_link) + ppc4xx_pciex_hwops->check_link(port); + /* * Map UTL */ @@ -1359,13 +1388,23 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) ppc4xx_pciex_hwops->setup_utl(port); /* - * Check for VC0 active and assert RDY. + * Check for VC0 active or PLL Locked and assert RDY. */ if (port->sdr_base) { - if (port->link && - ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, - 1 << 16, 1 << 16, 5000)) { - printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index); + if (of_device_is_compatible(port->node, + "ibm,plb-pciex-460sx")){ + if (port->link && ppc4xx_pciex_wait_on_sdr(port, + PESDRn_RCSSTS, + 1 << 12, 1 << 12, 5000)) { + printk(KERN_INFO "PCIE%d: PLL not locked\n", + port->index); + port->link = 0; + } + } else if (port->link && + ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, + 1 << 16, 1 << 16, 5000)) { + printk(KERN_INFO "PCIE%d: VC0 not active\n", + port->index); port->link = 0; } @@ -1572,8 +1611,15 @@ static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah); dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal); dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff); - /* Note that 3 here means enabled | single region */ - dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3); + /*Enabled and single region */ + if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx")) + dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, + sa | DCRO_PEGPL_460SX_OMR1MSKL_UOT + | DCRO_PEGPL_OMRxMSKL_VAL); + else + dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, + sa | DCRO_PEGPL_OMR1MSKL_UOT + | DCRO_PEGPL_OMRxMSKL_VAL); break; case 1: out_le32(mbase + PECFG_POM1LAH, pciah); @@ -1581,8 +1627,8 @@ static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah); dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal); dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff); - /* Note that 3 here means enabled | single region */ - dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3); + dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, + sa | DCRO_PEGPL_OMRxMSKL_VAL); break; case 2: out_le32(mbase + PECFG_POM2LAH, pciah); @@ -1591,7 +1637,9 @@ static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal); dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff); /* Note that 3 here means enabled | IO space !!! */ - dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, sa | 3); + dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, + sa | DCRO_PEGPL_OMR3MSKL_IO + | DCRO_PEGPL_OMRxMSKL_VAL); break; } @@ -1692,6 +1740,9 @@ static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port, if (res->flags & IORESOURCE_PREFETCH) sa |= 0x8; + if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx")) + sa |= PCI_BASE_ADDRESS_MEM_TYPE_64; + out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa)); out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa)); @@ -1853,6 +1904,10 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) } out_le16(mbase + 0x202, val); + /* Enable Bus master, memory, and io space */ + if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx")) + out_le16(mbase + 0x204, 0x7); + if (!port->endpoint) { /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ out_le32(mbase + 0x208, 0x06040001); diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h index c39a134e868..32ce763a375 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.h +++ b/arch/powerpc/sysdev/ppc4xx_pci.h @@ -464,6 +464,18 @@ #define PECFG_POM2LAL 0x390 #define PECFG_POM2LAH 0x394 +/* 460sx only */ +#define PECFG_460SX_DLLSTA 0x3f8 + +/* 460sx Bit Mappings */ +#define PECFG_460SX_DLLSTA_LINKUP 0x00000010 +#define DCRO_PEGPL_460SX_OMR1MSKL_UOT 0x00000004 + +/* PEGPL Bit Mappings */ +#define DCRO_PEGPL_OMRxMSKL_VAL 0x00000001 +#define DCRO_PEGPL_OMR1MSKL_UOT 0x00000002 +#define DCRO_PEGPL_OMR3MSKL_IO 0x00000002 + /* SDR Bit Mappings */ #define PESDRx_RCSSET_HLDPLB 0x10000000 #define PESDRx_RCSSET_RSTGU 0x01000000 -- cgit v1.2.3-18-g5258 From e57b708beaf1bc14031cb9749b574c9aa1f6188f Mon Sep 17 00:00:00 2001 From: Mike Williams Date: Thu, 7 Jul 2011 06:09:58 +0000 Subject: powerpc/4xx: edac: Add comma to fix build error Commit 4018294b53d1dae026880e45f174c1cc63b5d435 broke the ppc4xx_edac driver at line 210 where the struct member is missing a comma. Signed-off-by: Mike Williams Signed-off-by: Josh Boyer --- drivers/edac/ppc4xx_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index 0de7d877089..38400963e24 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -205,7 +205,7 @@ static struct platform_driver ppc4xx_edac_driver = { .remove = ppc4xx_edac_remove, .driver = { .owner = THIS_MODULE, - .name = PPC4XX_EDAC_MODULE_NAME + .name = PPC4XX_EDAC_MODULE_NAME, .of_match_table = ppc4xx_edac_match, }, }; -- cgit v1.2.3-18-g5258 From 9fcd768d0cc88b41ea459e25d2db12d3e25fa9dd Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Thu, 11 Aug 2011 13:53:44 -0400 Subject: powerpc/40x: Remove obsolete HCU4 board The HCU4 board is unmaintained. Remove it. Signed-off-by: Niklaus Giger Signed-off-by: Josh Boyer --- arch/powerpc/boot/dts/hcu4.dts | 168 -------------------------------- arch/powerpc/configs/40x/hcu4_defconfig | 80 --------------- arch/powerpc/configs/ppc40x_defconfig | 1 - arch/powerpc/platforms/40x/Kconfig | 8 -- arch/powerpc/platforms/40x/Makefile | 1 - arch/powerpc/platforms/40x/hcu4.c | 61 ------------ 6 files changed, 319 deletions(-) delete mode 100644 arch/powerpc/boot/dts/hcu4.dts delete mode 100644 arch/powerpc/configs/40x/hcu4_defconfig delete mode 100644 arch/powerpc/platforms/40x/hcu4.c diff --git a/arch/powerpc/boot/dts/hcu4.dts b/arch/powerpc/boot/dts/hcu4.dts deleted file mode 100644 index 7988598da4c..00000000000 --- a/arch/powerpc/boot/dts/hcu4.dts +++ /dev/null @@ -1,168 +0,0 @@ -/* -* Device Tree Source for Netstal Maschinen HCU4 -* based on the IBM Walnut -* -* Copyright 2008 -* Niklaus Giger -* -* Copyright 2007 IBM Corp. -* Josh Boyer -* -* This file is licensed under the terms of the GNU General Public -* License version 2. This program is licensed "as is" without -* any warranty of any kind, whether express or implied. -*/ - -/dts-v1/; - -/ { - #address-cells = <0x1>; - #size-cells = <0x1>; - model = "netstal,hcu4"; - compatible = "netstal,hcu4"; - dcr-parent = <0x1>; - - aliases { - ethernet0 = "/plb/opb/ethernet@ef600800"; - serial0 = "/plb/opb/serial@ef600300"; - }; - - cpus { - #address-cells = <0x1>; - #size-cells = <0x0>; - - cpu@0 { - device_type = "cpu"; - model = "PowerPC,405GPr"; - reg = <0x0>; - clock-frequency = <0>; /* Filled in by U-Boot */ - timebase-frequency = <0x0>; /* Filled in by U-Boot */ - i-cache-line-size = <0x20>; - d-cache-line-size = <0x20>; - i-cache-size = <0x4000>; - d-cache-size = <0x4000>; - dcr-controller; - dcr-access-method = "native"; - linux,phandle = <0x1>; - }; - }; - - memory { - device_type = "memory"; - reg = <0x0 0x0>; /* Filled in by U-Boot */ - }; - - UIC0: interrupt-controller { - compatible = "ibm,uic"; - interrupt-controller; - cell-index = <0x0>; - dcr-reg = <0xc0 0x9>; - #address-cells = <0x0>; - #size-cells = <0x0>; - #interrupt-cells = <0x2>; - linux,phandle = <0x2>; - }; - - plb { - compatible = "ibm,plb3"; - #address-cells = <0x1>; - #size-cells = <0x1>; - ranges; - clock-frequency = <0x0>; /* Filled in by U-Boot */ - - SDRAM0: memory-controller { - compatible = "ibm,sdram-405gp"; - dcr-reg = <0x10 0x2>; - }; - - MAL: mcmal { - compatible = "ibm,mcmal-405gp", "ibm,mcmal"; - dcr-reg = <0x180 0x62>; - num-tx-chans = <0x1>; - num-rx-chans = <0x1>; - interrupt-parent = <0x2>; - interrupts = <0xb 0x4 0xc 0x4 0xa 0x4 0xd 0x4 0xe 0x4>; - linux,phandle = <0x3>; - }; - - POB0: opb { - compatible = "ibm,opb-405gp", "ibm,opb"; - #address-cells = <0x1>; - #size-cells = <0x1>; - ranges = <0xef600000 0xef600000 0xa00000>; - dcr-reg = <0xa0 0x5>; - clock-frequency = <0x0>; /* Filled in by U-Boot */ - - UART0: serial@ef600300 { - device_type = "serial"; - compatible = "ns16550"; - reg = <0xef600300 0x8>; - virtual-reg = <0xef600300>; - clock-frequency = <0x0>;/* Filled in by U-Boot */ - current-speed = <0>; /* Filled in by U-Boot */ - interrupt-parent = <0x2>; - interrupts = <0x0 0x4>; - }; - - IIC: i2c@ef600500 { - compatible = "ibm,iic-405gp", "ibm,iic"; - reg = <0xef600500 0x11>; - interrupt-parent = <0x2>; - interrupts = <0x2 0x4>; - }; - - GPIO: gpio@ef600700 { - compatible = "ibm,gpio-405gp"; - reg = <0xef600700 0x20>; - }; - - EMAC: ethernet@ef600800 { - device_type = "network"; - compatible = "ibm,emac-405gp", "ibm,emac"; - interrupt-parent = <0x2>; - interrupts = <0xf 0x4 0x9 0x4>; - local-mac-address = [00 00 00 00 00 00]; - reg = <0xef600800 0x70>; - mal-device = <0x3>; - mal-tx-channel = <0x0>; - mal-rx-channel = <0x0>; - cell-index = <0x0>; - max-frame-size = <0x5dc>; - rx-fifo-size = <0x1000>; - tx-fifo-size = <0x800>; - phy-mode = "rmii"; - phy-map = <0x1>; - }; - }; - - EBC0: ebc { - compatible = "ibm,ebc-405gp", "ibm,ebc"; - dcr-reg = <0x12 0x2>; - #address-cells = <0x2>; - #size-cells = <0x1>; - clock-frequency = <0x0>; /* Filled in by U-Boot */ - - sram@0,0 { - reg = <0x0 0x0 0x80000>; - }; - - flash@0,80000 { - compatible = "jedec-flash"; - bank-width = <0x1>; - reg = <0x0 0x80000 0x80000>; - #address-cells = <0x1>; - #size-cells = <0x1>; - - partition@0 { - label = "OpenBIOS"; - reg = <0x0 0x80000>; - read-only; - }; - }; - }; - }; - - chosen { - linux,stdout-path = "/plb/opb/serial@ef600300"; - }; -}; diff --git a/arch/powerpc/configs/40x/hcu4_defconfig b/arch/powerpc/configs/40x/hcu4_defconfig deleted file mode 100644 index ebeb4accad6..00000000000 --- a/arch/powerpc/configs/40x/hcu4_defconfig +++ /dev/null @@ -1,80 +0,0 @@ -CONFIG_40x=y -CONFIG_EXPERIMENTAL=y -CONFIG_SYSVIPC=y -CONFIG_POSIX_MQUEUE=y -CONFIG_LOG_BUF_SHIFT=14 -CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EXPERT=y -CONFIG_KALLSYMS_ALL=y -CONFIG_KALLSYMS_EXTRA_PASS=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -# CONFIG_BLK_DEV_BSG is not set -CONFIG_HCU4=y -# CONFIG_WALNUT is not set -CONFIG_SPARSE_IRQ=y -CONFIG_PCI=y -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set -# CONFIG_INET_LRO is not set -# CONFIG_IPV6 is not set -CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -CONFIG_CONNECTOR=y -CONFIG_MTD=y -CONFIG_MTD_PARTITIONS=y -CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_OF_PARTS=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=m -CONFIG_MTD_CFI=y -CONFIG_MTD_JEDECPROBE=y -CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_PHYSMAP_OF=y -CONFIG_PROC_DEVICETREE=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=35000 -CONFIG_NETDEVICES=y -CONFIG_NET_ETHERNET=y -CONFIG_IBM_NEW_EMAC=y -# CONFIG_INPUT is not set -# CONFIG_SERIO is not set -# CONFIG_VT is not set -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_EXTENDED=y -CONFIG_SERIAL_8250_SHARE_IRQ=y -CONFIG_SERIAL_OF_PLATFORM=y -# CONFIG_HW_RANDOM is not set -# CONFIG_HWMON is not set -CONFIG_VIDEO_OUTPUT_CONTROL=m -# CONFIG_USB_SUPPORT is not set -CONFIG_EXT2_FS=y -CONFIG_INOTIFY=y -CONFIG_PROC_KCORE=y -CONFIG_TMPFS=y -CONFIG_CRAMFS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_ROOT_NFS=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y -CONFIG_DETECT_HUNG_TASK=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set -CONFIG_SYSCTL_SYSCALL_CHECK=y -CONFIG_CRYPTO=y -CONFIG_CRYPTO_CBC=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_DES=y -# CONFIG_CRYPTO_ANSI_CPRNG is not set diff --git a/arch/powerpc/configs/ppc40x_defconfig b/arch/powerpc/configs/ppc40x_defconfig index bfd634b5ada..e9d920c5a87 100644 --- a/arch/powerpc/configs/ppc40x_defconfig +++ b/arch/powerpc/configs/ppc40x_defconfig @@ -14,7 +14,6 @@ CONFIG_MODULE_UNLOAD=y CONFIG_PPC4xx_GPIO=y CONFIG_ACADIA=y CONFIG_EP405=y -CONFIG_HCU4=y CONFIG_HOTFOOT=y CONFIG_KILAUEA=y CONFIG_MAKALU=y diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig index d733d7ca939..ae5e0bfc023 100644 --- a/arch/powerpc/platforms/40x/Kconfig +++ b/arch/powerpc/platforms/40x/Kconfig @@ -32,14 +32,6 @@ config EP405 help This option enables support for the EP405/EP405PC boards. -config HCU4 - bool "Hcu4" - depends on 40x - default n - select 405GPR - help - This option enables support for the Nestal Maschinen HCU4 board. - config HOTFOOT bool "Hotfoot" depends on 40x diff --git a/arch/powerpc/platforms/40x/Makefile b/arch/powerpc/platforms/40x/Makefile index 56e89004c46..88c22de0c85 100644 --- a/arch/powerpc/platforms/40x/Makefile +++ b/arch/powerpc/platforms/40x/Makefile @@ -1,4 +1,3 @@ -obj-$(CONFIG_HCU4) += hcu4.o obj-$(CONFIG_WALNUT) += walnut.o obj-$(CONFIG_XILINX_VIRTEX_GENERIC_BOARD) += virtex.o obj-$(CONFIG_EP405) += ep405.o diff --git a/arch/powerpc/platforms/40x/hcu4.c b/arch/powerpc/platforms/40x/hcu4.c deleted file mode 100644 index 60b2afecab7..00000000000 --- a/arch/powerpc/platforms/40x/hcu4.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Architecture- / platform-specific boot-time initialization code for - * IBM PowerPC 4xx based boards. Adapted from original - * code by Gary Thomas, Cort Dougan , and Dan Malek - * . - * - * Copyright(c) 1999-2000 Grant Erickson - * - * Rewritten and ported to the merged powerpc tree: - * Copyright 2007 IBM Corporation - * Josh Boyer - * - * 2002 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -static __initdata struct of_device_id hcu4_of_bus[] = { - { .compatible = "ibm,plb3", }, - { .compatible = "ibm,opb", }, - { .compatible = "ibm,ebc", }, - {}, -}; - -static int __init hcu4_device_probe(void) -{ - of_platform_bus_probe(NULL, hcu4_of_bus, NULL); - return 0; -} -machine_device_initcall(hcu4, hcu4_device_probe); - -static int __init hcu4_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); - - if (!of_flat_dt_is_compatible(root, "netstal,hcu4")) - return 0; - - return 1; -} - -define_machine(hcu4) { - .name = "HCU4", - .probe = hcu4_probe, - .progress = udbg_progress, - .init_IRQ = uic_init_tree, - .get_irq = uic_get_irq, - .restart = ppc4xx_reset_system, - .calibrate_decr = generic_calibrate_decr, -}; -- cgit v1.2.3-18-g5258 From ce395088832bfd56bd28824b31a6a3685f3fd339 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Fri, 17 Jun 2011 02:51:47 +0000 Subject: cpc925_edac: Support single-processor configurations If second CPU is not enabled, CPC925 EDAC driver will spill out warnings about errors on second Processor Interface. Support masking that out, by detecting at runtime which CPUs are present in device tree. Signed-off-by: Dmitry Eremin-Solenikov Cc: Harry Ciao Cc: Doug Thompson Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Benjamin Herrenschmidt --- drivers/edac/cpc925_edac.c | 67 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index a687a0d1696..a774c0ddaf5 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -90,6 +90,7 @@ enum apimask_bits { ECC_MASK_ENABLE = (APIMASK_ECC_UE_H | APIMASK_ECC_CE_H | APIMASK_ECC_UE_L | APIMASK_ECC_CE_L), }; +#define APIMASK_ADI(n) CPC925_BIT(((n)+1)) /************************************************************ * Processor Interface Exception Register (APIEXCP) @@ -581,16 +582,73 @@ static void cpc925_mc_check(struct mem_ctl_info *mci) } /******************** CPU err device********************************/ +static u32 cpc925_cpu_mask_disabled(void) +{ + struct device_node *cpus; + struct device_node *cpunode = NULL; + static u32 mask = 0; + + /* use cached value if available */ + if (mask != 0) + return mask; + + mask = APIMASK_ADI0 | APIMASK_ADI1; + + cpus = of_find_node_by_path("/cpus"); + if (cpus == NULL) { + cpc925_printk(KERN_DEBUG, "No /cpus node !\n"); + return 0; + } + + while ((cpunode = of_get_next_child(cpus, cpunode)) != NULL) { + const u32 *reg = of_get_property(cpunode, "reg", NULL); + + if (strcmp(cpunode->type, "cpu")) { + cpc925_printk(KERN_ERR, "Not a cpu node in /cpus: %s\n", cpunode->name); + continue; + } + + if (reg == NULL || *reg > 2) { + cpc925_printk(KERN_ERR, "Bad reg value at %s\n", cpunode->full_name); + continue; + } + + mask &= ~APIMASK_ADI(*reg); + } + + if (mask != (APIMASK_ADI0 | APIMASK_ADI1)) { + /* We assume that each CPU sits on it's own PI and that + * for present CPUs the reg property equals to the PI + * interface id */ + cpc925_printk(KERN_WARNING, + "Assuming PI id is equal to CPU MPIC id!\n"); + } + + of_node_put(cpunode); + of_node_put(cpus); + + return mask; +} + /* Enable CPU Errors detection */ static void cpc925_cpu_init(struct cpc925_dev_info *dev_info) { u32 apimask; + u32 cpumask; apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET); - if ((apimask & CPU_MASK_ENABLE) == 0) { - apimask |= CPU_MASK_ENABLE; - __raw_writel(apimask, dev_info->vbase + REG_APIMASK_OFFSET); + + cpumask = cpc925_cpu_mask_disabled(); + if (apimask & cpumask) { + cpc925_printk(KERN_WARNING, "CPU(s) not present, " + "but enabled in APIMASK, disabling\n"); + apimask &= ~cpumask; } + + if ((apimask & CPU_MASK_ENABLE) == 0) + apimask |= CPU_MASK_ENABLE; + + __raw_writel(apimask, dev_info->vbase + REG_APIMASK_OFFSET); } /* Disable CPU Errors detection */ @@ -622,6 +680,9 @@ static void cpc925_cpu_check(struct edac_device_ctl_info *edac_dev) if ((apiexcp & CPU_EXCP_DETECTED) == 0) return; + if ((apiexcp & ~cpc925_cpu_mask_disabled()) == 0) + return; + apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET); cpc925_printk(KERN_INFO, "Processor Interface Fault\n" "Processor Interface register dump:\n"); -- cgit v1.2.3-18-g5258 From 6a5c7be5e484bda5b2639fedf7dbe3f25c15c962 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 24 Jun 2011 09:05:22 +0000 Subject: powerpc: Override dma_get_required_mask by platform hook and ops The hook dma_get_required_mask is supposed to return the mask required by the platform to operate efficently. The generic version of dma_get_required_mask in driver/base/platform.c returns a mask based only on max_pfn. However, this is likely too big for iommu systems and could be too small for platforms that require a dma offset or have a secondary window at a high offset. Override the default, provide a hook in ppc_md used by pseries lpar and cell, and provide the default answer based on memblock_end_of_DRAM(), with hooks for get_dma_offset, and provide an implementation for iommu that looks at the defined table size. Coverting from the end address to the required bit mask is based on the generic implementation. The need for this was discovered when the qla2xxx driver switched to 64 bit dma then reverted to 32 bit when dma_get_required_mask said 32 bits was sufficient. Signed-off-by: Milton Miller Signed-off-by: Nishanth Aravamudan Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org Cc: benh@kernel.crashing.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/dma-mapping.h | 3 +++ arch/powerpc/include/asm/machdep.h | 3 ++- arch/powerpc/kernel/dma-iommu.c | 13 ++++++++++++ arch/powerpc/kernel/dma.c | 39 ++++++++++++++++++++++++++++++++++ arch/powerpc/platforms/cell/iommu.c | 12 +++++++++++ arch/powerpc/platforms/pseries/iommu.c | 27 +++++++++++++++++++++++ 6 files changed, 96 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h index dd70fac57ec..8135e66a4bb 100644 --- a/arch/powerpc/include/asm/dma-mapping.h +++ b/arch/powerpc/include/asm/dma-mapping.h @@ -20,6 +20,8 @@ #define DMA_ERROR_CODE (~(dma_addr_t)0x0) +#define ARCH_HAS_DMA_GET_REQUIRED_MASK + /* Some dma direct funcs must be visible for use in other dma_ops */ extern void *dma_direct_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag); @@ -69,6 +71,7 @@ static inline unsigned long device_to_mask(struct device *dev) */ #ifdef CONFIG_PPC64 extern struct dma_map_ops dma_iommu_ops; +extern u64 dma_iommu_get_required_mask(struct device *dev); #endif extern struct dma_map_ops dma_direct_ops; diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 47cacddb14c..58fc2162301 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -85,8 +85,9 @@ struct machdep_calls { void (*pci_dma_dev_setup)(struct pci_dev *dev); void (*pci_dma_bus_setup)(struct pci_bus *bus); - /* Platform set_dma_mask override */ + /* Platform set_dma_mask and dma_get_required_mask overrides */ int (*dma_set_mask)(struct device *dev, u64 dma_mask); + u64 (*dma_get_required_mask)(struct device *dev); int (*probe)(void); void (*setup_arch)(void); /* Optional, may be NULL */ diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index e7554154a6d..1f2a711a261 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -90,6 +90,19 @@ static int dma_iommu_dma_supported(struct device *dev, u64 mask) return 1; } +u64 dma_iommu_get_required_mask(struct device *dev) +{ + struct iommu_table *tbl = get_iommu_table_base(dev); + u64 mask; + if (!tbl) + return 0; + + mask = 1ULL < (fls_long(tbl->it_offset + tbl->it_size) - 1); + mask += mask - 1; + + return mask; +} + struct dma_map_ops dma_iommu_ops = { .alloc_coherent = dma_iommu_alloc_coherent, .free_coherent = dma_iommu_free_coherent, diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index 4f0959fbfbe..503093efa20 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -170,6 +170,45 @@ int dma_set_mask(struct device *dev, u64 dma_mask) } EXPORT_SYMBOL(dma_set_mask); +u64 dma_get_required_mask(struct device *dev) +{ + struct dma_map_ops *dma_ops = get_dma_ops(dev); + u64 mask, end = 0; + + if (ppc_md.dma_get_required_mask) + return ppc_md.dma_get_required_mask(dev); + + if (unlikely(dma_ops == NULL)) + return 0; + +#ifdef CONFIG_PPC64 + else if (dma_ops == &dma_iommu_ops) + return dma_iommu_get_required_mask(dev); +#endif +#ifdef CONFIG_SWIOTLB + else if (dma_ops == &swiotlb_dma_ops) { + u64 max_direct_dma_addr = dev->archdata.max_direct_dma_addr; + + end = memblock_end_of_DRAM(); + if (max_direct_dma_addr && end > max_direct_dma_addr) + end = max_direct_dma_addr; + end += get_dma_offset(dev); + } +#endif + else if (dma_ops == &dma_direct_ops) + end = memblock_end_of_DRAM() + get_dma_offset(dev); + else { + WARN_ONCE(1, "%s: unknown ops %p\n", __func__, dma_ops); + end = memblock_end_of_DRAM(); + } + + mask = 1ULL << (fls64(end) - 1); + mask += mask - 1; + + return mask; +} +EXPORT_SYMBOL_GPL(dma_get_required_mask); + static int __init dma_init(void) { dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 26a067122a5..5ef55f3b098 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -1159,6 +1159,17 @@ static int __init setup_iommu_fixed(char *str) } __setup("iommu_fixed=", setup_iommu_fixed); +static u64 cell_dma_get_required_mask(struct device *dev) +{ + if (!dev->dma_mask) + return 0; + + if (iommu_fixed_disabled && get_dma_ops(dev) == &dma_iommu_ops) + return dma_iommu_get_required_mask(dev); + + return DMA_BIT_MASK(64); +} + static int __init cell_iommu_init(void) { struct device_node *np; @@ -1175,6 +1186,7 @@ static int __init cell_iommu_init(void) /* Setup various ppc_md. callbacks */ ppc_md.pci_dma_dev_setup = cell_pci_dma_dev_setup; + ppc_md.dma_get_required_mask = cell_dma_get_required_mask; ppc_md.tce_build = tce_build_cell; ppc_md.tce_free = tce_free_cell; diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 01faab9456c..fe5ededf0d6 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1077,12 +1077,38 @@ check_mask: return 0; } +static u64 dma_get_required_mask_pSeriesLP(struct device *dev) +{ + if (!dev->dma_mask) + return 0; + + if (!disable_ddw && dev_is_pci(dev)) { + struct pci_dev *pdev = to_pci_dev(dev); + struct device_node *dn; + + dn = pci_device_to_OF_node(pdev); + + /* search upwards for ibm,dma-window */ + for (; dn && PCI_DN(dn) && !PCI_DN(dn)->iommu_table; + dn = dn->parent) + if (of_get_property(dn, "ibm,dma-window", NULL)) + break; + /* if there is a ibm,ddw-applicable property require 64 bits */ + if (dn && PCI_DN(dn) && + of_get_property(dn, "ibm,ddw-applicable", NULL)) + return DMA_BIT_MASK(64); + } + + return dma_iommu_get_required_mask(dev); +} + #else /* CONFIG_PCI */ #define pci_dma_bus_setup_pSeries NULL #define pci_dma_dev_setup_pSeries NULL #define pci_dma_bus_setup_pSeriesLP NULL #define pci_dma_dev_setup_pSeriesLP NULL #define dma_set_mask_pSeriesLP NULL +#define dma_get_required_mask_pSeriesLP NULL #endif /* !CONFIG_PCI */ static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action, @@ -1186,6 +1212,7 @@ void iommu_init_early_pSeries(void) ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pSeriesLP; ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pSeriesLP; ppc_md.dma_set_mask = dma_set_mask_pSeriesLP; + ppc_md.dma_get_required_mask = dma_get_required_mask_pSeriesLP; } else { ppc_md.tce_build = tce_build_pSeries; ppc_md.tce_free = tce_free_pSeries; -- cgit v1.2.3-18-g5258 From 3a8f7558e475b68254d8bc3a2211f3f89bf67a71 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 24 Jun 2011 09:05:23 +0000 Subject: dma-mapping: Add get_required_mask if arch overrides default If an architecture sets ARCH_HAS_DMA_GET_REQUIRED_MASK and has settable dma_map_ops, the required mask may change by the ops implementation. For example, a system that always has an mmu inline may only require 32 bits while a swiotlb would desire bits to cover all of memory. Therefore add the field if the architecture does not use the generic definition of dma_get_required_mask. The first use will by by powerpc. Note that this does add some dependency on the order in which files are visible here. Signed-off-by: Milton Miller Signed-off-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt Acked-by: FUJITA Tomonori --- include/linux/dma-mapping.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 347fdc32177..aa32fecd1d3 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -41,6 +41,9 @@ struct dma_map_ops { int (*mapping_error)(struct device *dev, dma_addr_t dma_addr); int (*dma_supported)(struct device *dev, u64 mask); int (*set_dma_mask)(struct device *dev, u64 mask); +#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK + u64 (*get_required_mask)(struct device *dev); +#endif int is_phys; }; -- cgit v1.2.3-18-g5258 From d24f9c6999eacd3a7bc2b289e49fcb2bf2fafef2 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 24 Jun 2011 09:05:24 +0000 Subject: powerpc: Use the newly added get_required_mask dma_map_ops hook Now that the generic code has dma_map_ops set, instead of having a messy ifdef & if block in the base dma_get_required_mask hook push the computation into the dma ops. If the ops fails to set the get_required_mask hook default to the width of dma_addr_t. This also corrects ibmbus ibmebus_dma_supported to require a 64 bit mask. I doubt anything is checking or setting the dma mask on that bus. Signed-off-by: Milton Miller Signed-off-by: Nishanth Aravamudan Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org Cc: benh@kernel.crashing.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/device.h | 2 ++ arch/powerpc/include/asm/dma-mapping.h | 3 --- arch/powerpc/kernel/dma-iommu.c | 3 ++- arch/powerpc/kernel/dma-swiotlb.c | 16 +++++++++++++ arch/powerpc/kernel/dma.c | 41 +++++++++++++-------------------- arch/powerpc/kernel/ibmebus.c | 8 ++++++- arch/powerpc/kernel/vio.c | 7 +++++- arch/powerpc/platforms/cell/iommu.c | 13 +++++++++-- arch/powerpc/platforms/ps3/system-bus.c | 7 ++++++ arch/powerpc/platforms/pseries/iommu.c | 2 +- 10 files changed, 68 insertions(+), 34 deletions(-) diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index 16d25c0974b..d57c08acedf 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -37,4 +37,6 @@ struct pdev_archdata { u64 dma_mask; }; +#define ARCH_HAS_DMA_GET_REQUIRED_MASK + #endif /* _ASM_POWERPC_DEVICE_H */ diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h index 8135e66a4bb..dd70fac57ec 100644 --- a/arch/powerpc/include/asm/dma-mapping.h +++ b/arch/powerpc/include/asm/dma-mapping.h @@ -20,8 +20,6 @@ #define DMA_ERROR_CODE (~(dma_addr_t)0x0) -#define ARCH_HAS_DMA_GET_REQUIRED_MASK - /* Some dma direct funcs must be visible for use in other dma_ops */ extern void *dma_direct_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag); @@ -71,7 +69,6 @@ static inline unsigned long device_to_mask(struct device *dev) */ #ifdef CONFIG_PPC64 extern struct dma_map_ops dma_iommu_ops; -extern u64 dma_iommu_get_required_mask(struct device *dev); #endif extern struct dma_map_ops dma_direct_ops; diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index 1f2a711a261..c1ad9db934f 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -90,7 +90,7 @@ static int dma_iommu_dma_supported(struct device *dev, u64 mask) return 1; } -u64 dma_iommu_get_required_mask(struct device *dev) +static u64 dma_iommu_get_required_mask(struct device *dev) { struct iommu_table *tbl = get_iommu_table_base(dev); u64 mask; @@ -111,5 +111,6 @@ struct dma_map_ops dma_iommu_ops = { .dma_supported = dma_iommu_dma_supported, .map_page = dma_iommu_map_page, .unmap_page = dma_iommu_unmap_page, + .get_required_mask = dma_iommu_get_required_mask, }; EXPORT_SYMBOL(dma_iommu_ops); diff --git a/arch/powerpc/kernel/dma-swiotlb.c b/arch/powerpc/kernel/dma-swiotlb.c index 4295e0b94b2..1ebc9189aad 100644 --- a/arch/powerpc/kernel/dma-swiotlb.c +++ b/arch/powerpc/kernel/dma-swiotlb.c @@ -24,6 +24,21 @@ unsigned int ppc_swiotlb_enable; +static u64 swiotlb_powerpc_get_required(struct device *dev) +{ + u64 end, mask, max_direct_dma_addr = dev->archdata.max_direct_dma_addr; + + end = memblock_end_of_DRAM(); + if (max_direct_dma_addr && end > max_direct_dma_addr) + end = max_direct_dma_addr; + end += get_dma_offset(dev); + + mask = 1ULL << (fls64(end) - 1); + mask += mask - 1; + + return mask; +} + /* * At the moment, all platforms that use this code only require * swiotlb to be used if we're operating on HIGHMEM. Since @@ -44,6 +59,7 @@ struct dma_map_ops swiotlb_dma_ops = { .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, .sync_sg_for_device = swiotlb_sync_sg_for_device, .mapping_error = swiotlb_dma_mapping_error, + .get_required_mask = swiotlb_powerpc_get_required, }; void pci_dma_dev_setup_swiotlb(struct pci_dev *pdev) diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index 503093efa20..10b136afbf5 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -96,6 +96,18 @@ static int dma_direct_dma_supported(struct device *dev, u64 mask) #endif } +static u64 dma_direct_get_required_mask(struct device *dev) +{ + u64 end, mask; + + end = memblock_end_of_DRAM() + get_dma_offset(dev); + + mask = 1ULL << (fls64(end) - 1); + mask += mask - 1; + + return mask; +} + static inline dma_addr_t dma_direct_map_page(struct device *dev, struct page *page, unsigned long offset, @@ -144,6 +156,7 @@ struct dma_map_ops dma_direct_ops = { .dma_supported = dma_direct_dma_supported, .map_page = dma_direct_map_page, .unmap_page = dma_direct_unmap_page, + .get_required_mask = dma_direct_get_required_mask, #ifdef CONFIG_NOT_COHERENT_CACHE .sync_single_for_cpu = dma_direct_sync_single, .sync_single_for_device = dma_direct_sync_single, @@ -173,7 +186,6 @@ EXPORT_SYMBOL(dma_set_mask); u64 dma_get_required_mask(struct device *dev) { struct dma_map_ops *dma_ops = get_dma_ops(dev); - u64 mask, end = 0; if (ppc_md.dma_get_required_mask) return ppc_md.dma_get_required_mask(dev); @@ -181,31 +193,10 @@ u64 dma_get_required_mask(struct device *dev) if (unlikely(dma_ops == NULL)) return 0; -#ifdef CONFIG_PPC64 - else if (dma_ops == &dma_iommu_ops) - return dma_iommu_get_required_mask(dev); -#endif -#ifdef CONFIG_SWIOTLB - else if (dma_ops == &swiotlb_dma_ops) { - u64 max_direct_dma_addr = dev->archdata.max_direct_dma_addr; - - end = memblock_end_of_DRAM(); - if (max_direct_dma_addr && end > max_direct_dma_addr) - end = max_direct_dma_addr; - end += get_dma_offset(dev); - } -#endif - else if (dma_ops == &dma_direct_ops) - end = memblock_end_of_DRAM() + get_dma_offset(dev); - else { - WARN_ONCE(1, "%s: unknown ops %p\n", __func__, dma_ops); - end = memblock_end_of_DRAM(); - } + if (dma_ops->get_required_mask) + return dma_ops->get_required_mask(dev); - mask = 1ULL << (fls64(end) - 1); - mask += mask - 1; - - return mask; + return DMA_BIT_MASK(8 * sizeof(dma_addr_t)); } EXPORT_SYMBOL_GPL(dma_get_required_mask); diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 28581f1ad2c..90ef2a44613 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -125,7 +125,12 @@ static void ibmebus_unmap_sg(struct device *dev, static int ibmebus_dma_supported(struct device *dev, u64 mask) { - return 1; + return mask == DMA_BIT_MASK(64); +} + +static u64 ibmebus_dma_get_required_mask(struct device *dev) +{ + return DMA_BIT_MASK(64); } static struct dma_map_ops ibmebus_dma_ops = { @@ -134,6 +139,7 @@ static struct dma_map_ops ibmebus_dma_ops = { .map_sg = ibmebus_map_sg, .unmap_sg = ibmebus_unmap_sg, .dma_supported = ibmebus_dma_supported, + .get_required_mask = ibmebus_dma_get_required_mask, .map_page = ibmebus_map_page, .unmap_page = ibmebus_unmap_page, }; diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index 1b695fdc362..c0493259d13 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -605,6 +605,11 @@ static int vio_dma_iommu_dma_supported(struct device *dev, u64 mask) return dma_iommu_ops.dma_supported(dev, mask); } +static u64 vio_dma_get_required_mask(struct device *dev) +{ + return dma_iommu_ops.get_required_mask(dev); +} + struct dma_map_ops vio_dma_mapping_ops = { .alloc_coherent = vio_dma_iommu_alloc_coherent, .free_coherent = vio_dma_iommu_free_coherent, @@ -613,7 +618,7 @@ struct dma_map_ops vio_dma_mapping_ops = { .map_page = vio_dma_iommu_map_page, .unmap_page = vio_dma_iommu_unmap_page, .dma_supported = vio_dma_iommu_dma_supported, - + .get_required_mask = vio_dma_get_required_mask, }; /** diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 5ef55f3b098..fc46fcac392 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -1161,11 +1161,20 @@ __setup("iommu_fixed=", setup_iommu_fixed); static u64 cell_dma_get_required_mask(struct device *dev) { + struct dma_map_ops *dma_ops; + if (!dev->dma_mask) return 0; - if (iommu_fixed_disabled && get_dma_ops(dev) == &dma_iommu_ops) - return dma_iommu_get_required_mask(dev); + if (!iommu_fixed_disabled && + cell_iommu_get_fixed_address(dev) != OF_BAD_ADDR) + return DMA_BIT_MASK(64); + + dma_ops = get_dma_ops(dev); + if (dma_ops->get_required_mask) + return dma_ops->get_required_mask(dev); + + WARN_ONCE(1, "no get_required_mask in %p ops", dma_ops); return DMA_BIT_MASK(64); } diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c index 23083c39752..688141c76e0 100644 --- a/arch/powerpc/platforms/ps3/system-bus.c +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -695,12 +695,18 @@ static int ps3_dma_supported(struct device *_dev, u64 mask) return mask >= DMA_BIT_MASK(32); } +static u64 ps3_dma_get_required_mask(struct device *_dev) +{ + return DMA_BIT_MASK(32); +} + static struct dma_map_ops ps3_sb_dma_ops = { .alloc_coherent = ps3_alloc_coherent, .free_coherent = ps3_free_coherent, .map_sg = ps3_sb_map_sg, .unmap_sg = ps3_sb_unmap_sg, .dma_supported = ps3_dma_supported, + .get_required_mask = ps3_dma_get_required_mask, .map_page = ps3_sb_map_page, .unmap_page = ps3_unmap_page, }; @@ -711,6 +717,7 @@ static struct dma_map_ops ps3_ioc0_dma_ops = { .map_sg = ps3_ioc0_map_sg, .unmap_sg = ps3_ioc0_unmap_sg, .dma_supported = ps3_dma_supported, + .get_required_mask = ps3_dma_get_required_mask, .map_page = ps3_ioc0_map_page, .unmap_page = ps3_unmap_page, }; diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index fe5ededf0d6..9f121a37eb5 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1099,7 +1099,7 @@ static u64 dma_get_required_mask_pSeriesLP(struct device *dev) return DMA_BIT_MASK(64); } - return dma_iommu_get_required_mask(dev); + return dma_iommu_ops.get_required_mask(dev); } #else /* CONFIG_PCI */ -- cgit v1.2.3-18-g5258 From 2eccacd0974dca73e2151d3fd4c2dacf1a5c7cc2 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 24 Jun 2011 09:05:25 +0000 Subject: powerpc: Tidy up dma_map_ops after adding new hook The new get_required_mask hook name is longer than many of but not all of the prior ops. Tidy the struct initializers to align the equal signs using the local whitespace. Signed-off-by: Milton Miller Signed-off-by: Nishanth Aravamudan Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org Cc: benh@kernel.crashing.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/dma-iommu.c | 14 +++++++------- arch/powerpc/kernel/dma.c | 16 ++++++++-------- arch/powerpc/kernel/ibmebus.c | 14 +++++++------- arch/powerpc/kernel/vio.c | 14 +++++++------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index c1ad9db934f..6f04b9c383a 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -104,13 +104,13 @@ static u64 dma_iommu_get_required_mask(struct device *dev) } struct dma_map_ops dma_iommu_ops = { - .alloc_coherent = dma_iommu_alloc_coherent, - .free_coherent = dma_iommu_free_coherent, - .map_sg = dma_iommu_map_sg, - .unmap_sg = dma_iommu_unmap_sg, - .dma_supported = dma_iommu_dma_supported, - .map_page = dma_iommu_map_page, - .unmap_page = dma_iommu_unmap_page, + .alloc_coherent = dma_iommu_alloc_coherent, + .free_coherent = dma_iommu_free_coherent, + .map_sg = dma_iommu_map_sg, + .unmap_sg = dma_iommu_unmap_sg, + .dma_supported = dma_iommu_dma_supported, + .map_page = dma_iommu_map_page, + .unmap_page = dma_iommu_unmap_page, .get_required_mask = dma_iommu_get_required_mask, }; EXPORT_SYMBOL(dma_iommu_ops); diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index 10b136afbf5..8593f53c4f6 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -149,14 +149,14 @@ static inline void dma_direct_sync_single(struct device *dev, #endif struct dma_map_ops dma_direct_ops = { - .alloc_coherent = dma_direct_alloc_coherent, - .free_coherent = dma_direct_free_coherent, - .map_sg = dma_direct_map_sg, - .unmap_sg = dma_direct_unmap_sg, - .dma_supported = dma_direct_dma_supported, - .map_page = dma_direct_map_page, - .unmap_page = dma_direct_unmap_page, - .get_required_mask = dma_direct_get_required_mask, + .alloc_coherent = dma_direct_alloc_coherent, + .free_coherent = dma_direct_free_coherent, + .map_sg = dma_direct_map_sg, + .unmap_sg = dma_direct_unmap_sg, + .dma_supported = dma_direct_dma_supported, + .map_page = dma_direct_map_page, + .unmap_page = dma_direct_unmap_page, + .get_required_mask = dma_direct_get_required_mask, #ifdef CONFIG_NOT_COHERENT_CACHE .sync_single_for_cpu = dma_direct_sync_single, .sync_single_for_device = dma_direct_sync_single, diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 90ef2a44613..73110fb6bb6 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -134,14 +134,14 @@ static u64 ibmebus_dma_get_required_mask(struct device *dev) } static struct dma_map_ops ibmebus_dma_ops = { - .alloc_coherent = ibmebus_alloc_coherent, - .free_coherent = ibmebus_free_coherent, - .map_sg = ibmebus_map_sg, - .unmap_sg = ibmebus_unmap_sg, - .dma_supported = ibmebus_dma_supported, + .alloc_coherent = ibmebus_alloc_coherent, + .free_coherent = ibmebus_free_coherent, + .map_sg = ibmebus_map_sg, + .unmap_sg = ibmebus_unmap_sg, + .dma_supported = ibmebus_dma_supported, .get_required_mask = ibmebus_dma_get_required_mask, - .map_page = ibmebus_map_page, - .unmap_page = ibmebus_unmap_page, + .map_page = ibmebus_map_page, + .unmap_page = ibmebus_unmap_page, }; static int ibmebus_match_path(struct device *dev, void *data) diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index c0493259d13..34d291d83ec 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -611,13 +611,13 @@ static u64 vio_dma_get_required_mask(struct device *dev) } struct dma_map_ops vio_dma_mapping_ops = { - .alloc_coherent = vio_dma_iommu_alloc_coherent, - .free_coherent = vio_dma_iommu_free_coherent, - .map_sg = vio_dma_iommu_map_sg, - .unmap_sg = vio_dma_iommu_unmap_sg, - .map_page = vio_dma_iommu_map_page, - .unmap_page = vio_dma_iommu_unmap_page, - .dma_supported = vio_dma_iommu_dma_supported, + .alloc_coherent = vio_dma_iommu_alloc_coherent, + .free_coherent = vio_dma_iommu_free_coherent, + .map_sg = vio_dma_iommu_map_sg, + .unmap_sg = vio_dma_iommu_unmap_sg, + .map_page = vio_dma_iommu_map_page, + .unmap_page = vio_dma_iommu_unmap_page, + .dma_supported = vio_dma_iommu_dma_supported, .get_required_mask = vio_dma_get_required_mask, }; -- cgit v1.2.3-18-g5258 From 7df5659eefad9b6d457ccdee016bd78bd064cfc0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 27 Jun 2011 11:45:16 +0000 Subject: serial/8250: Move UPIO_TSI to powerpc This iotype is only used by the legacy_serial code in powerpc, so the code should live there, rather than be compiled in for every 8250 driver. Signed-off-by: Arnd Bergmann Cc: Benjamin Herrenschmidt Cc: linuxppc-dev@lists.ozlabs.org Cc: Greg Kroah-Hartman Cc: linux-serial@vger.kernel.org Acked-by: David Daney Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/legacy_serial.c | 25 +++++++++++++++++++++++++ drivers/tty/serial/8250.c | 23 ----------------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 2b97b80d6d7..c7b5afeecaf 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,24 @@ static struct __initdata of_device_id legacy_serial_parents[] = { static unsigned int legacy_serial_count; static int legacy_serial_console = -1; +static unsigned int tsi_serial_in(struct uart_port *p, int offset) +{ + unsigned int tmp; + offset = offset << p->regshift; + if (offset == UART_IIR) { + tmp = readl(p->membase + (UART_IIR & ~3)); + return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ + } else + return readb(p->membase + offset); +} + +static void tsi_serial_out(struct uart_port *p, int offset, int value) +{ + offset = offset << p->regshift; + if (!((offset == UART_IER) && (value & UART_IER_UUE))) + writeb(value, p->membase + offset); +} + static int __init add_legacy_port(struct device_node *np, int want_index, int iotype, phys_addr_t base, phys_addr_t taddr, unsigned long irq, @@ -102,6 +121,7 @@ static int __init add_legacy_port(struct device_node *np, int want_index, legacy_serial_ports[index].iobase = base; else legacy_serial_ports[index].mapbase = base; + legacy_serial_ports[index].iotype = iotype; legacy_serial_ports[index].uartclk = clock; legacy_serial_ports[index].irq = irq; @@ -112,6 +132,11 @@ static int __init add_legacy_port(struct device_node *np, int want_index, legacy_serial_infos[index].speed = spd ? be32_to_cpup(spd) : 0; legacy_serial_infos[index].irq_check_parent = irq_check_parent; + if (iotype == UPIO_TSI) { + legacy_serial_ports[index].serial_in = tsi_serial_in; + legacy_serial_ports[index].serial_out = tsi_serial_out; + } + printk(KERN_DEBUG "Found legacy serial port %d for %s\n", index, np->full_name); printk(KERN_DEBUG " %s=%llx, taddr=%llx, irq=%lx, clk=%d, speed=%d\n", diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 7f50999eebc..610b8e63710 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -443,24 +443,6 @@ static void au_serial_out(struct uart_port *p, int offset, int value) __raw_writel(value, p->membase + offset); } -static unsigned int tsi_serial_in(struct uart_port *p, int offset) -{ - unsigned int tmp; - offset = map_8250_in_reg(p, offset) << p->regshift; - if (offset == UART_IIR) { - tmp = readl(p->membase + (UART_IIR & ~3)); - return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ - } else - return readb(p->membase + offset); -} - -static void tsi_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - if (!((offset == UART_IER) && (value & UART_IER_UUE))) - writeb(value, p->membase + offset); -} - /* Save the LCR value so it can be re-written when a Busy Detect IRQ occurs. */ static inline void dwapb_save_out_value(struct uart_port *p, int offset, int value) @@ -535,11 +517,6 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = au_serial_out; break; - case UPIO_TSI: - p->serial_in = tsi_serial_in; - p->serial_out = tsi_serial_out; - break; - case UPIO_DWAPB: p->serial_in = mem_serial_in; p->serial_out = dwapb_serial_out; -- cgit v1.2.3-18-g5258 From 41151e77a4d96ea138cede6d84c955aa4769ce74 Mon Sep 17 00:00:00 2001 From: Becky Bruce Date: Tue, 28 Jun 2011 09:54:48 +0000 Subject: powerpc: Hugetlb for BookE Enable hugepages on Freescale BookE processors. This allows the kernel to use huge TLB entries to map pages, which can greatly reduce the number of TLB misses and the amount of TLB thrashing experienced by applications with large memory footprints. Care should be taken when using this on FSL processors, as the number of large TLB entries supported by the core is low (16-64) on current processors. The supported set of hugepage sizes include 4m, 16m, 64m, 256m, and 1g. Page sizes larger than the max zone size are called "gigantic" pages and must be allocated on the command line (and cannot be deallocated). This is currently only fully implemented for Freescale 32-bit BookE processors, but there is some infrastructure in the code for 64-bit BooKE. Signed-off-by: Becky Bruce Signed-off-by: David Gibson Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 3 +- arch/powerpc/include/asm/hugetlb.h | 63 +++++- arch/powerpc/include/asm/mmu-book3e.h | 7 + arch/powerpc/include/asm/mmu-hash64.h | 3 +- arch/powerpc/include/asm/mmu.h | 18 +- arch/powerpc/include/asm/page.h | 31 ++- arch/powerpc/include/asm/page_64.h | 11 - arch/powerpc/include/asm/pte-book3e.h | 3 + arch/powerpc/kernel/head_fsl_booke.S | 133 ++++++++++-- arch/powerpc/mm/Makefile | 1 + arch/powerpc/mm/hash_utils_64.c | 3 - arch/powerpc/mm/hugetlbpage-book3e.c | 121 +++++++++++ arch/powerpc/mm/hugetlbpage.c | 379 +++++++++++++++++++++++++++++---- arch/powerpc/mm/init_32.c | 9 + arch/powerpc/mm/mem.c | 5 + arch/powerpc/mm/mmu_context_nohash.c | 5 + arch/powerpc/mm/pgtable.c | 3 +- arch/powerpc/mm/tlb_low_64e.S | 24 +-- arch/powerpc/mm/tlb_nohash.c | 46 +++- arch/powerpc/platforms/Kconfig.cputype | 4 +- 20 files changed, 766 insertions(+), 106 deletions(-) create mode 100644 arch/powerpc/mm/hugetlbpage-book3e.c diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 0a3d5560c9b..62711421cd6 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -429,8 +429,7 @@ config ARCH_POPULATES_NODE_MAP def_bool y config SYS_SUPPORTS_HUGETLBFS - def_bool y - depends on PPC_BOOK3S_64 + bool source "mm/Kconfig" diff --git a/arch/powerpc/include/asm/hugetlb.h b/arch/powerpc/include/asm/hugetlb.h index 5856a66ab40..86004930a78 100644 --- a/arch/powerpc/include/asm/hugetlb.h +++ b/arch/powerpc/include/asm/hugetlb.h @@ -1,15 +1,60 @@ #ifndef _ASM_POWERPC_HUGETLB_H #define _ASM_POWERPC_HUGETLB_H +#ifdef CONFIG_HUGETLB_PAGE #include +extern struct kmem_cache *hugepte_cache; +extern void __init reserve_hugetlb_gpages(void); + +static inline pte_t *hugepd_page(hugepd_t hpd) +{ + BUG_ON(!hugepd_ok(hpd)); + return (pte_t *)((hpd.pd & ~HUGEPD_SHIFT_MASK) | PD_HUGE); +} + +static inline unsigned int hugepd_shift(hugepd_t hpd) +{ + return hpd.pd & HUGEPD_SHIFT_MASK; +} + +static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr, + unsigned pdshift) +{ + /* + * On 32-bit, we have multiple higher-level table entries that point to + * the same hugepte. Just use the first one since they're all + * identical. So for that case, idx=0. + */ + unsigned long idx = 0; + + pte_t *dir = hugepd_page(*hpdp); +#ifdef CONFIG_PPC64 + idx = (addr & ((1UL << pdshift) - 1)) >> hugepd_shift(*hpdp); +#endif + + return dir + idx; +} + pte_t *huge_pte_offset_and_shift(struct mm_struct *mm, unsigned long addr, unsigned *shift); void flush_dcache_icache_hugepage(struct page *page); +#if defined(CONFIG_PPC_MM_SLICES) || defined(CONFIG_PPC_SUBPAGE_PROT) int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, unsigned long len); +#else +static inline int is_hugepage_only_range(struct mm_struct *mm, + unsigned long addr, + unsigned long len) +{ + return 0; +} +#endif + +void book3e_hugetlb_preload(struct mm_struct *mm, unsigned long ea, pte_t pte); +void flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr); void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, @@ -50,8 +95,11 @@ static inline void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - unsigned long old = pte_update(mm, addr, ptep, ~0UL, 1); - return __pte(old); +#ifdef CONFIG_PPC64 + return __pte(pte_update(mm, addr, ptep, ~0UL, 1)); +#else + return __pte(pte_update(ptep, ~0UL, 0)); +#endif } static inline void huge_ptep_clear_flush(struct vm_area_struct *vma, @@ -93,4 +141,15 @@ static inline void arch_release_hugepage(struct page *page) { } +#else /* ! CONFIG_HUGETLB_PAGE */ +static inline void reserve_hugetlb_gpages(void) +{ + pr_err("Cannot reserve gpages without hugetlb enabled\n"); +} +static inline void flush_hugetlb_page(struct vm_area_struct *vma, + unsigned long vmaddr) +{ +} +#endif + #endif /* _ASM_POWERPC_HUGETLB_H */ diff --git a/arch/powerpc/include/asm/mmu-book3e.h b/arch/powerpc/include/asm/mmu-book3e.h index 3ea0f9a259d..0260ea5ec3c 100644 --- a/arch/powerpc/include/asm/mmu-book3e.h +++ b/arch/powerpc/include/asm/mmu-book3e.h @@ -66,6 +66,7 @@ #define MAS2_M 0x00000004 #define MAS2_G 0x00000002 #define MAS2_E 0x00000001 +#define MAS2_WIMGE_MASK 0x0000001f #define MAS2_EPN_MASK(size) (~0 << (size + 10)) #define MAS2_VAL(addr, size, flags) ((addr) & MAS2_EPN_MASK(size) | (flags)) @@ -80,6 +81,7 @@ #define MAS3_SW 0x00000004 #define MAS3_UR 0x00000002 #define MAS3_SR 0x00000001 +#define MAS3_BAP_MASK 0x0000003f #define MAS3_SPSIZE 0x0000003e #define MAS3_SPSIZE_SHIFT 1 @@ -212,6 +214,11 @@ typedef struct { unsigned int id; unsigned int active; unsigned long vdso_base; +#ifdef CONFIG_PPC_MM_SLICES + u64 low_slices_psize; /* SLB page size encodings */ + u64 high_slices_psize; /* 4 bits per slice for now */ + u16 user_psize; /* page size index */ +#endif } mm_context_t; /* Page size definitions, common between 32 and 64-bit diff --git a/arch/powerpc/include/asm/mmu-hash64.h b/arch/powerpc/include/asm/mmu-hash64.h index b445e0af4c2..db645ec842b 100644 --- a/arch/powerpc/include/asm/mmu-hash64.h +++ b/arch/powerpc/include/asm/mmu-hash64.h @@ -262,8 +262,7 @@ extern void hash_failure_debug(unsigned long ea, unsigned long access, extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend, unsigned long pstart, unsigned long prot, int psize, int ssize); -extern void add_gpage(unsigned long addr, unsigned long page_size, - unsigned long number_of_pages); +extern void add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages); extern void demote_segment_4k(struct mm_struct *mm, unsigned long addr); extern void hpte_init_native(void); diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h index 698b3063868..f0145522cfb 100644 --- a/arch/powerpc/include/asm/mmu.h +++ b/arch/powerpc/include/asm/mmu.h @@ -175,14 +175,16 @@ extern u64 ppc64_rma_size; #define MMU_PAGE_64K_AP 3 /* "Admixed pages" (hash64 only) */ #define MMU_PAGE_256K 4 #define MMU_PAGE_1M 5 -#define MMU_PAGE_8M 6 -#define MMU_PAGE_16M 7 -#define MMU_PAGE_256M 8 -#define MMU_PAGE_1G 9 -#define MMU_PAGE_16G 10 -#define MMU_PAGE_64G 11 -#define MMU_PAGE_COUNT 12 - +#define MMU_PAGE_4M 6 +#define MMU_PAGE_8M 7 +#define MMU_PAGE_16M 8 +#define MMU_PAGE_64M 9 +#define MMU_PAGE_256M 10 +#define MMU_PAGE_1G 11 +#define MMU_PAGE_16G 12 +#define MMU_PAGE_64G 13 + +#define MMU_PAGE_COUNT 14 #if defined(CONFIG_PPC_STD_MMU_64) /* 64-bit classic hash table MMU */ diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index 2cd664ef0a5..dd9c4fd038e 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -36,6 +36,18 @@ #define PAGE_SIZE (ASM_CONST(1) << PAGE_SHIFT) +#ifndef __ASSEMBLY__ +#ifdef CONFIG_HUGETLB_PAGE +extern unsigned int HPAGE_SHIFT; +#else +#define HPAGE_SHIFT PAGE_SHIFT +#endif +#define HPAGE_SIZE ((1UL) << HPAGE_SHIFT) +#define HPAGE_MASK (~(HPAGE_SIZE - 1)) +#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) +#define HUGE_MAX_HSTATE (MMU_PAGE_COUNT-1) +#endif + /* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */ #define __HAVE_ARCH_GATE_AREA 1 @@ -158,6 +170,24 @@ extern phys_addr_t kernstart_addr; #define is_kernel_addr(x) ((x) >= PAGE_OFFSET) #endif +/* + * Use the top bit of the higher-level page table entries to indicate whether + * the entries we point to contain hugepages. This works because we know that + * the page tables live in kernel space. If we ever decide to support having + * page tables at arbitrary addresses, this breaks and will have to change. + */ +#ifdef CONFIG_PPC64 +#define PD_HUGE 0x8000000000000000 +#else +#define PD_HUGE 0x80000000 +#endif + +/* + * Some number of bits at the level of the page table that points to + * a hugepte are used to encode the size. This masks those bits. + */ +#define HUGEPD_SHIFT_MASK 0x3f + #ifndef __ASSEMBLY__ #undef STRICT_MM_TYPECHECKS @@ -243,7 +273,6 @@ typedef unsigned long pgprot_t; #endif typedef struct { signed long pd; } hugepd_t; -#define HUGEPD_SHIFT_MASK 0x3f #ifdef CONFIG_HUGETLB_PAGE static inline int hugepd_ok(hugepd_t hpd) diff --git a/arch/powerpc/include/asm/page_64.h b/arch/powerpc/include/asm/page_64.h index 9356262fd3c..fb40ede6bc0 100644 --- a/arch/powerpc/include/asm/page_64.h +++ b/arch/powerpc/include/asm/page_64.h @@ -64,17 +64,6 @@ extern void copy_page(void *to, void *from); /* Log 2 of page table size */ extern u64 ppc64_pft_size; -/* Large pages size */ -#ifdef CONFIG_HUGETLB_PAGE -extern unsigned int HPAGE_SHIFT; -#else -#define HPAGE_SHIFT PAGE_SHIFT -#endif -#define HPAGE_SIZE ((1UL) << HPAGE_SHIFT) -#define HPAGE_MASK (~(HPAGE_SIZE - 1)) -#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) -#define HUGE_MAX_HSTATE (MMU_PAGE_COUNT-1) - #endif /* __ASSEMBLY__ */ #ifdef CONFIG_PPC_MM_SLICES diff --git a/arch/powerpc/include/asm/pte-book3e.h b/arch/powerpc/include/asm/pte-book3e.h index 082d515930a..0156702ba24 100644 --- a/arch/powerpc/include/asm/pte-book3e.h +++ b/arch/powerpc/include/asm/pte-book3e.h @@ -72,6 +72,9 @@ #define PTE_RPN_SHIFT (24) #endif +#define PTE_WIMGE_SHIFT (19) +#define PTE_BAP_SHIFT (2) + /* On 32-bit, we never clear the top part of the PTE */ #ifdef CONFIG_PPC32 #define _PTE_NONE_MASK 0xffffffff00000000ULL diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 50845924b7d..4ea9bfbf67e 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -236,8 +236,24 @@ _ENTRY(__early_start) * if we find the pte (fall through): * r11 is low pte word * r12 is pointer to the pte + * r10 is the pshift from the PGD, if we're a hugepage */ #ifdef CONFIG_PTE_64BIT +#ifdef CONFIG_HUGETLB_PAGE +#define FIND_PTE \ + rlwinm r12, r10, 13, 19, 29; /* Compute pgdir/pmd offset */ \ + lwzx r11, r12, r11; /* Get pgd/pmd entry */ \ + rlwinm. r12, r11, 0, 0, 20; /* Extract pt base address */ \ + blt 1000f; /* Normal non-huge page */ \ + beq 2f; /* Bail if no table */ \ + oris r11, r11, PD_HUGE@h; /* Put back address bit */ \ + andi. r10, r11, HUGEPD_SHIFT_MASK@l; /* extract size field */ \ + xor r12, r10, r11; /* drop size bits from pointer */ \ + b 1001f; \ +1000: rlwimi r12, r10, 23, 20, 28; /* Compute pte address */ \ + li r10, 0; /* clear r10 */ \ +1001: lwz r11, 4(r12); /* Get pte entry */ +#else #define FIND_PTE \ rlwinm r12, r10, 13, 19, 29; /* Compute pgdir/pmd offset */ \ lwzx r11, r12, r11; /* Get pgd/pmd entry */ \ @@ -245,7 +261,8 @@ _ENTRY(__early_start) beq 2f; /* Bail if no table */ \ rlwimi r12, r10, 23, 20, 28; /* Compute pte address */ \ lwz r11, 4(r12); /* Get pte entry */ -#else +#endif /* HUGEPAGE */ +#else /* !PTE_64BIT */ #define FIND_PTE \ rlwimi r11, r10, 12, 20, 29; /* Create L1 (pgdir/pmd) address */ \ lwz r11, 0(r11); /* Get L1 entry */ \ @@ -402,8 +419,8 @@ interrupt_base: #ifdef CONFIG_PTE_64BIT #ifdef CONFIG_SMP - subf r10,r11,r12 /* create false data dep */ - lwzx r13,r11,r10 /* Get upper pte bits */ + subf r13,r11,r12 /* create false data dep */ + lwzx r13,r11,r13 /* Get upper pte bits */ #else lwz r13,0(r12) /* Get upper pte bits */ #endif @@ -483,8 +500,8 @@ interrupt_base: #ifdef CONFIG_PTE_64BIT #ifdef CONFIG_SMP - subf r10,r11,r12 /* create false data dep */ - lwzx r13,r11,r10 /* Get upper pte bits */ + subf r13,r11,r12 /* create false data dep */ + lwzx r13,r11,r13 /* Get upper pte bits */ #else lwz r13,0(r12) /* Get upper pte bits */ #endif @@ -548,7 +565,7 @@ interrupt_base: /* * Both the instruction and data TLB miss get to this * point to load the TLB. - * r10 - available to use + * r10 - tsize encoding (if HUGETLB_PAGE) or available to use * r11 - TLB (info from Linux PTE) * r12 - available to use * r13 - upper bits of PTE (if PTE_64BIT) or available to use @@ -558,21 +575,73 @@ interrupt_base: * Upon exit, we reload everything and RFI. */ finish_tlb_load: +#ifdef CONFIG_HUGETLB_PAGE + cmpwi 6, r10, 0 /* check for huge page */ + beq 6, finish_tlb_load_cont /* !huge */ + + /* Alas, we need more scratch registers for hugepages */ + mfspr r12, SPRN_SPRG_THREAD + stw r14, THREAD_NORMSAVE(4)(r12) + stw r15, THREAD_NORMSAVE(5)(r12) + stw r16, THREAD_NORMSAVE(6)(r12) + stw r17, THREAD_NORMSAVE(7)(r12) + + /* Get the next_tlbcam_idx percpu var */ +#ifdef CONFIG_SMP + lwz r12, THREAD_INFO-THREAD(r12) + lwz r15, TI_CPU(r12) + lis r14, __per_cpu_offset@h + ori r14, r14, __per_cpu_offset@l + rlwinm r15, r15, 2, 0, 29 + lwzx r16, r14, r15 +#else + li r16, 0 +#endif + lis r17, next_tlbcam_idx@h + ori r17, r17, next_tlbcam_idx@l + add r17, r17, r16 /* r17 = *next_tlbcam_idx */ + lwz r15, 0(r17) /* r15 = next_tlbcam_idx */ + + lis r14, MAS0_TLBSEL(1)@h /* select TLB1 (TLBCAM) */ + rlwimi r14, r15, 16, 4, 15 /* next_tlbcam_idx entry */ + mtspr SPRN_MAS0, r14 + + /* Extract TLB1CFG(NENTRY) */ + mfspr r16, SPRN_TLB1CFG + andi. r16, r16, 0xfff + + /* Update next_tlbcam_idx, wrapping when necessary */ + addi r15, r15, 1 + cmpw r15, r16 + blt 100f + lis r14, tlbcam_index@h + ori r14, r14, tlbcam_index@l + lwz r15, 0(r14) +100: stw r15, 0(r17) + + /* + * Calc MAS1_TSIZE from r10 (which has pshift encoded) + * tlb_enc = (pshift - 10). + */ + subi r15, r10, 10 + mfspr r16, SPRN_MAS1 + rlwimi r16, r15, 7, 20, 24 + mtspr SPRN_MAS1, r16 + + /* copy the pshift for use later */ + mr r14, r10 + + /* fall through */ + +#endif /* CONFIG_HUGETLB_PAGE */ + /* * We set execute, because we don't have the granularity to * properly set this at the page level (Linux problem). * Many of these bits are software only. Bits we don't set * here we (properly should) assume have the appropriate value. */ - - mfspr r12, SPRN_MAS2 -#ifdef CONFIG_PTE_64BIT - rlwimi r12, r11, 32-19, 27, 31 /* extract WIMGE from pte */ -#else - rlwimi r12, r11, 26, 27, 31 /* extract WIMGE from pte */ -#endif - mtspr SPRN_MAS2, r12 - +finish_tlb_load_cont: #ifdef CONFIG_PTE_64BIT rlwinm r12, r11, 32-2, 26, 31 /* Move in perm bits */ andi. r10, r11, _PAGE_DIRTY @@ -581,22 +650,40 @@ finish_tlb_load: andc r12, r12, r10 1: rlwimi r12, r13, 20, 0, 11 /* grab RPN[32:43] */ rlwimi r12, r11, 20, 12, 19 /* grab RPN[44:51] */ - mtspr SPRN_MAS3, r12 +2: mtspr SPRN_MAS3, r12 BEGIN_MMU_FTR_SECTION srwi r10, r13, 12 /* grab RPN[12:31] */ mtspr SPRN_MAS7, r10 END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS) #else li r10, (_PAGE_EXEC | _PAGE_PRESENT) + mr r13, r11 rlwimi r10, r11, 31, 29, 29 /* extract _PAGE_DIRTY into SW */ and r12, r11, r10 andi. r10, r11, _PAGE_USER /* Test for _PAGE_USER */ slwi r10, r12, 1 or r10, r10, r12 iseleq r12, r12, r10 - rlwimi r11, r12, 0, 20, 31 /* Extract RPN from PTE and merge with perms */ - mtspr SPRN_MAS3, r11 + rlwimi r13, r12, 0, 20, 31 /* Get RPN from PTE, merge w/ perms */ + mtspr SPRN_MAS3, r13 #endif + + mfspr r12, SPRN_MAS2 +#ifdef CONFIG_PTE_64BIT + rlwimi r12, r11, 32-19, 27, 31 /* extract WIMGE from pte */ +#else + rlwimi r12, r11, 26, 27, 31 /* extract WIMGE from pte */ +#endif +#ifdef CONFIG_HUGETLB_PAGE + beq 6, 3f /* don't mask if page isn't huge */ + li r13, 1 + slw r13, r13, r14 + subi r13, r13, 1 + rlwinm r13, r13, 0, 0, 19 /* bottom bits used for WIMGE/etc */ + andc r12, r12, r13 /* mask off ea bits within the page */ +#endif +3: mtspr SPRN_MAS2, r12 + #ifdef CONFIG_E200 /* Round robin TLB1 entries assignment */ mfspr r12, SPRN_MAS0 @@ -622,11 +709,19 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS) mtspr SPRN_MAS0,r12 #endif /* CONFIG_E200 */ +tlb_write_entry: tlbwe /* Done...restore registers and get out of here. */ mfspr r10, SPRN_SPRG_THREAD - lwz r11, THREAD_NORMSAVE(3)(r10) +#ifdef CONFIG_HUGETLB_PAGE + beq 6, 8f /* skip restore for 4k page faults */ + lwz r14, THREAD_NORMSAVE(4)(r10) + lwz r15, THREAD_NORMSAVE(5)(r10) + lwz r16, THREAD_NORMSAVE(6)(r10) + lwz r17, THREAD_NORMSAVE(7)(r10) +#endif +8: lwz r11, THREAD_NORMSAVE(3)(r10) mtcr r11 lwz r13, THREAD_NORMSAVE(2)(r10) lwz r12, THREAD_NORMSAVE(1)(r10) diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile index bdca46e0838..991ee813d2a 100644 --- a/arch/powerpc/mm/Makefile +++ b/arch/powerpc/mm/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_PPC_MM_SLICES) += slice.o ifeq ($(CONFIG_HUGETLB_PAGE),y) obj-y += hugetlbpage.o obj-$(CONFIG_PPC_STD_MMU_64) += hugetlbpage-hash64.o +obj-$(CONFIG_PPC_BOOK3E_MMU) += hugetlbpage-book3e.o endif obj-$(CONFIG_PPC_SUBPAGE_PROT) += subpage-prot.o obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 26b2872b3d0..1f8b2a05e3d 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -105,9 +105,6 @@ int mmu_kernel_ssize = MMU_SEGSIZE_256M; int mmu_highuser_ssize = MMU_SEGSIZE_256M; u16 mmu_slb_size = 64; EXPORT_SYMBOL_GPL(mmu_slb_size); -#ifdef CONFIG_HUGETLB_PAGE -unsigned int HPAGE_SHIFT; -#endif #ifdef CONFIG_PPC_64K_PAGES int mmu_ci_restrictions; #endif diff --git a/arch/powerpc/mm/hugetlbpage-book3e.c b/arch/powerpc/mm/hugetlbpage-book3e.c new file mode 100644 index 00000000000..1295b7c1cda --- /dev/null +++ b/arch/powerpc/mm/hugetlbpage-book3e.c @@ -0,0 +1,121 @@ +/* + * PPC Huge TLB Page Support for Book3E MMU + * + * Copyright (C) 2009 David Gibson, IBM Corporation. + * Copyright (C) 2011 Becky Bruce, Freescale Semiconductor + * + */ +#include +#include + +static inline int mmu_get_tsize(int psize) +{ + return mmu_psize_defs[psize].enc; +} + +static inline int book3e_tlb_exists(unsigned long ea, unsigned long pid) +{ + int found = 0; + + mtspr(SPRN_MAS6, pid << 16); + if (mmu_has_feature(MMU_FTR_USE_TLBRSRV)) { + asm volatile( + "li %0,0\n" + "tlbsx. 0,%1\n" + "bne 1f\n" + "li %0,1\n" + "1:\n" + : "=&r"(found) : "r"(ea)); + } else { + asm volatile( + "tlbsx 0,%1\n" + "mfspr %0,0x271\n" + "srwi %0,%0,31\n" + : "=&r"(found) : "r"(ea)); + } + + return found; +} + +void book3e_hugetlb_preload(struct mm_struct *mm, unsigned long ea, pte_t pte) +{ + unsigned long mas1, mas2; + u64 mas7_3; + unsigned long psize, tsize, shift; + unsigned long flags; + +#ifdef CONFIG_PPC_FSL_BOOK3E + int index, lz, ncams; + struct vm_area_struct *vma; +#endif + + if (unlikely(is_kernel_addr(ea))) + return; + +#ifdef CONFIG_MM_SLICES + psize = mmu_get_tsize(get_slice_psize(mm, ea)); + tsize = mmu_get_psize(psize); + shift = mmu_psize_defs[psize].shift; +#else + vma = find_vma(mm, ea); + psize = vma_mmu_pagesize(vma); /* returns actual size in bytes */ + asm (PPC_CNTLZL "%0,%1" : "=r" (lz) : "r" (psize)); + shift = 31 - lz; + tsize = 21 - lz; +#endif + + /* + * We can't be interrupted while we're setting up the MAS + * regusters or after we've confirmed that no tlb exists. + */ + local_irq_save(flags); + + if (unlikely(book3e_tlb_exists(ea, mm->context.id))) { + local_irq_restore(flags); + return; + } + +#ifdef CONFIG_PPC_FSL_BOOK3E + ncams = mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY; + + /* We have to use the CAM(TLB1) on FSL parts for hugepages */ + index = __get_cpu_var(next_tlbcam_idx); + mtspr(SPRN_MAS0, MAS0_ESEL(index) | MAS0_TLBSEL(1)); + + /* Just round-robin the entries and wrap when we hit the end */ + if (unlikely(index == ncams - 1)) + __get_cpu_var(next_tlbcam_idx) = tlbcam_index; + else + __get_cpu_var(next_tlbcam_idx)++; +#endif + mas1 = MAS1_VALID | MAS1_TID(mm->context.id) | MAS1_TSIZE(tsize); + mas2 = ea & ~((1UL << shift) - 1); + mas2 |= (pte_val(pte) >> PTE_WIMGE_SHIFT) & MAS2_WIMGE_MASK; + mas7_3 = (u64)pte_pfn(pte) << PAGE_SHIFT; + mas7_3 |= (pte_val(pte) >> PTE_BAP_SHIFT) & MAS3_BAP_MASK; + if (!pte_dirty(pte)) + mas7_3 &= ~(MAS3_SW|MAS3_UW); + + mtspr(SPRN_MAS1, mas1); + mtspr(SPRN_MAS2, mas2); + + if (mmu_has_feature(MMU_FTR_USE_PAIRED_MAS)) { + mtspr(SPRN_MAS7_MAS3, mas7_3); + } else { + mtspr(SPRN_MAS7, upper_32_bits(mas7_3)); + mtspr(SPRN_MAS3, lower_32_bits(mas7_3)); + } + + asm volatile ("tlbwe"); + + local_irq_restore(flags); +} + +void flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ + struct hstate *hstate = hstate_file(vma->vm_file); + unsigned long tsize = huge_page_shift(hstate) - 10; + + __flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr, tsize, 0); + +} diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 0b9a5c1901b..3a5f59dcbb3 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -1,7 +1,8 @@ /* - * PPC64 (POWER4) Huge TLB Page Support for Kernel. + * PPC Huge TLB Page Support for Kernel. * * Copyright (C) 2003 David Gibson, IBM Corporation. + * Copyright (C) 2011 Becky Bruce, Freescale Semiconductor * * Based on the IA-32 version: * Copyright (C) 2002, Rohit Seth @@ -11,24 +12,39 @@ #include #include #include +#include +#include +#include #include #include #include +#include #define PAGE_SHIFT_64K 16 #define PAGE_SHIFT_16M 24 #define PAGE_SHIFT_16G 34 -#define MAX_NUMBER_GPAGES 1024 +unsigned int HPAGE_SHIFT; -/* Tracks the 16G pages after the device tree is scanned and before the - * huge_boot_pages list is ready. */ -static unsigned long gpage_freearray[MAX_NUMBER_GPAGES]; +/* + * Tracks gpages after the device tree is scanned and before the + * huge_boot_pages list is ready. On 64-bit implementations, this is + * just used to track 16G pages and so is a single array. 32-bit + * implementations may have more than one gpage size due to limitations + * of the memory allocators, so we need multiple arrays + */ +#ifdef CONFIG_PPC64 +#define MAX_NUMBER_GPAGES 1024 +static u64 gpage_freearray[MAX_NUMBER_GPAGES]; static unsigned nr_gpages; - -/* Flag to mark huge PD pointers. This means pmd_bad() and pud_bad() - * will choke on pointers to hugepte tables, which is handy for - * catching screwups early. */ +#else +#define MAX_NUMBER_GPAGES 128 +struct psize_gpages { + u64 gpage_list[MAX_NUMBER_GPAGES]; + unsigned int nr_gpages; +}; +static struct psize_gpages gpage_freearray[MMU_PAGE_COUNT]; +#endif static inline int shift_to_mmu_psize(unsigned int shift) { @@ -49,25 +65,6 @@ static inline unsigned int mmu_psize_to_shift(unsigned int mmu_psize) #define hugepd_none(hpd) ((hpd).pd == 0) -static inline pte_t *hugepd_page(hugepd_t hpd) -{ - BUG_ON(!hugepd_ok(hpd)); - return (pte_t *)((hpd.pd & ~HUGEPD_SHIFT_MASK) | 0xc000000000000000); -} - -static inline unsigned int hugepd_shift(hugepd_t hpd) -{ - return hpd.pd & HUGEPD_SHIFT_MASK; -} - -static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr, unsigned pdshift) -{ - unsigned long idx = (addr & ((1UL << pdshift) - 1)) >> hugepd_shift(*hpdp); - pte_t *dir = hugepd_page(*hpdp); - - return dir + idx; -} - pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift) { pgd_t *pg; @@ -93,7 +90,7 @@ pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift if (is_hugepd(pm)) hpdp = (hugepd_t *)pm; else if (!pmd_none(*pm)) { - return pte_offset_map(pm, ea); + return pte_offset_kernel(pm, ea); } } } @@ -114,8 +111,18 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, unsigned long address, unsigned pdshift, unsigned pshift) { - pte_t *new = kmem_cache_zalloc(PGT_CACHE(pdshift - pshift), - GFP_KERNEL|__GFP_REPEAT); + struct kmem_cache *cachep; + pte_t *new; + +#ifdef CONFIG_PPC64 + cachep = PGT_CACHE(pdshift - pshift); +#else + int i; + int num_hugepd = 1 << (pshift - pdshift); + cachep = hugepte_cache; +#endif + + new = kmem_cache_zalloc(cachep, GFP_KERNEL|__GFP_REPEAT); BUG_ON(pshift > HUGEPD_SHIFT_MASK); BUG_ON((unsigned long)new & HUGEPD_SHIFT_MASK); @@ -124,10 +131,31 @@ static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, return -ENOMEM; spin_lock(&mm->page_table_lock); +#ifdef CONFIG_PPC64 if (!hugepd_none(*hpdp)) - kmem_cache_free(PGT_CACHE(pdshift - pshift), new); + kmem_cache_free(cachep, new); else - hpdp->pd = ((unsigned long)new & ~0x8000000000000000) | pshift; + hpdp->pd = ((unsigned long)new & ~PD_HUGE) | pshift; +#else + /* + * We have multiple higher-level entries that point to the same + * actual pte location. Fill in each as we go and backtrack on error. + * We need all of these so the DTLB pgtable walk code can find the + * right higher-level entry without knowing if it's a hugepage or not. + */ + for (i = 0; i < num_hugepd; i++, hpdp++) { + if (unlikely(!hugepd_none(*hpdp))) + break; + else + hpdp->pd = ((unsigned long)new & ~PD_HUGE) | pshift; + } + /* If we bailed from the for loop early, an error occurred, clean up */ + if (i < num_hugepd) { + for (i = i - 1 ; i >= 0; i--, hpdp--) + hpdp->pd = 0; + kmem_cache_free(cachep, new); + } +#endif spin_unlock(&mm->page_table_lock); return 0; } @@ -169,11 +197,132 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr, unsigned long sz return hugepte_offset(hpdp, addr, pdshift); } +#ifdef CONFIG_PPC32 /* Build list of addresses of gigantic pages. This function is used in early * boot before the buddy or bootmem allocator is setup. */ -void add_gpage(unsigned long addr, unsigned long page_size, - unsigned long number_of_pages) +void add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages) +{ + unsigned int idx = shift_to_mmu_psize(__ffs(page_size)); + int i; + + if (addr == 0) + return; + + gpage_freearray[idx].nr_gpages = number_of_pages; + + for (i = 0; i < number_of_pages; i++) { + gpage_freearray[idx].gpage_list[i] = addr; + addr += page_size; + } +} + +/* + * Moves the gigantic page addresses from the temporary list to the + * huge_boot_pages list. + */ +int alloc_bootmem_huge_page(struct hstate *hstate) +{ + struct huge_bootmem_page *m; + int idx = shift_to_mmu_psize(hstate->order + PAGE_SHIFT); + int nr_gpages = gpage_freearray[idx].nr_gpages; + + if (nr_gpages == 0) + return 0; + +#ifdef CONFIG_HIGHMEM + /* + * If gpages can be in highmem we can't use the trick of storing the + * data structure in the page; allocate space for this + */ + m = alloc_bootmem(sizeof(struct huge_bootmem_page)); + m->phys = gpage_freearray[idx].gpage_list[--nr_gpages]; +#else + m = phys_to_virt(gpage_freearray[idx].gpage_list[--nr_gpages]); +#endif + + list_add(&m->list, &huge_boot_pages); + gpage_freearray[idx].nr_gpages = nr_gpages; + gpage_freearray[idx].gpage_list[nr_gpages] = 0; + m->hstate = hstate; + + return 1; +} +/* + * Scan the command line hugepagesz= options for gigantic pages; store those in + * a list that we use to allocate the memory once all options are parsed. + */ + +unsigned long gpage_npages[MMU_PAGE_COUNT]; + +static int __init do_gpage_early_setup(char *param, char *val) +{ + static phys_addr_t size; + unsigned long npages; + + /* + * The hugepagesz and hugepages cmdline options are interleaved. We + * use the size variable to keep track of whether or not this was done + * properly and skip over instances where it is incorrect. Other + * command-line parsing code will issue warnings, so we don't need to. + * + */ + if ((strcmp(param, "default_hugepagesz") == 0) || + (strcmp(param, "hugepagesz") == 0)) { + size = memparse(val, NULL); + } else if (strcmp(param, "hugepages") == 0) { + if (size != 0) { + if (sscanf(val, "%lu", &npages) <= 0) + npages = 0; + gpage_npages[shift_to_mmu_psize(__ffs(size))] = npages; + size = 0; + } + } + return 0; +} + + +/* + * This function allocates physical space for pages that are larger than the + * buddy allocator can handle. We want to allocate these in highmem because + * the amount of lowmem is limited. This means that this function MUST be + * called before lowmem_end_addr is set up in MMU_init() in order for the lmb + * allocate to grab highmem. + */ +void __init reserve_hugetlb_gpages(void) +{ + static __initdata char cmdline[COMMAND_LINE_SIZE]; + phys_addr_t size, base; + int i; + + strlcpy(cmdline, boot_command_line, COMMAND_LINE_SIZE); + parse_args("hugetlb gpages", cmdline, NULL, 0, &do_gpage_early_setup); + + /* + * Walk gpage list in reverse, allocating larger page sizes first. + * Skip over unsupported sizes, or sizes that have 0 gpages allocated. + * When we reach the point in the list where pages are no longer + * considered gpages, we're done. + */ + for (i = MMU_PAGE_COUNT-1; i >= 0; i--) { + if (mmu_psize_defs[i].shift == 0 || gpage_npages[i] == 0) + continue; + else if (mmu_psize_to_shift(i) < (MAX_ORDER + PAGE_SHIFT)) + break; + + size = (phys_addr_t)(1ULL << mmu_psize_to_shift(i)); + base = memblock_alloc_base(size * gpage_npages[i], size, + MEMBLOCK_ALLOC_ANYWHERE); + add_gpage(base, size, gpage_npages[i]); + } +} + +#else /* PPC64 */ + +/* Build list of addresses of gigantic pages. This function is used in early + * boot before the buddy or bootmem allocator is setup. + */ +void add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages) { if (!addr) return; @@ -199,19 +348,79 @@ int alloc_bootmem_huge_page(struct hstate *hstate) m->hstate = hstate; return 1; } +#endif int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) { return 0; } +#ifdef CONFIG_PPC32 +#define HUGEPD_FREELIST_SIZE \ + ((PAGE_SIZE - sizeof(struct hugepd_freelist)) / sizeof(pte_t)) + +struct hugepd_freelist { + struct rcu_head rcu; + unsigned int index; + void *ptes[0]; +}; + +static DEFINE_PER_CPU(struct hugepd_freelist *, hugepd_freelist_cur); + +static void hugepd_free_rcu_callback(struct rcu_head *head) +{ + struct hugepd_freelist *batch = + container_of(head, struct hugepd_freelist, rcu); + unsigned int i; + + for (i = 0; i < batch->index; i++) + kmem_cache_free(hugepte_cache, batch->ptes[i]); + + free_page((unsigned long)batch); +} + +static void hugepd_free(struct mmu_gather *tlb, void *hugepte) +{ + struct hugepd_freelist **batchp; + + batchp = &__get_cpu_var(hugepd_freelist_cur); + + if (atomic_read(&tlb->mm->mm_users) < 2 || + cpumask_equal(mm_cpumask(tlb->mm), + cpumask_of(smp_processor_id()))) { + kmem_cache_free(hugepte_cache, hugepte); + return; + } + + if (*batchp == NULL) { + *batchp = (struct hugepd_freelist *)__get_free_page(GFP_ATOMIC); + (*batchp)->index = 0; + } + + (*batchp)->ptes[(*batchp)->index++] = hugepte; + if ((*batchp)->index == HUGEPD_FREELIST_SIZE) { + call_rcu_sched(&(*batchp)->rcu, hugepd_free_rcu_callback); + *batchp = NULL; + } +} +#endif + static void free_hugepd_range(struct mmu_gather *tlb, hugepd_t *hpdp, int pdshift, unsigned long start, unsigned long end, unsigned long floor, unsigned long ceiling) { pte_t *hugepte = hugepd_page(*hpdp); - unsigned shift = hugepd_shift(*hpdp); + int i; + unsigned long pdmask = ~((1UL << pdshift) - 1); + unsigned int num_hugepd = 1; + +#ifdef CONFIG_PPC64 + unsigned int shift = hugepd_shift(*hpdp); +#else + /* Note: On 32-bit the hpdp may be the first of several */ + num_hugepd = (1 << (hugepd_shift(*hpdp) - pdshift)); +#endif start &= pdmask; if (start < floor) @@ -224,9 +433,15 @@ static void free_hugepd_range(struct mmu_gather *tlb, hugepd_t *hpdp, int pdshif if (end - 1 > ceiling - 1) return; - hpdp->pd = 0; + for (i = 0; i < num_hugepd; i++, hpdp++) + hpdp->pd = 0; + tlb->need_flush = 1; +#ifdef CONFIG_PPC64 pgtable_free_tlb(tlb, hugepte, pdshift - shift); +#else + hugepd_free(tlb, hugepte); +#endif } static void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud, @@ -331,18 +546,27 @@ void hugetlb_free_pgd_range(struct mmu_gather *tlb, * too. */ - pgd = pgd_offset(tlb->mm, addr); do { next = pgd_addr_end(addr, end); + pgd = pgd_offset(tlb->mm, addr); if (!is_hugepd(pgd)) { if (pgd_none_or_clear_bad(pgd)) continue; hugetlb_free_pud_range(tlb, pgd, addr, next, floor, ceiling); } else { +#ifdef CONFIG_PPC32 + /* + * Increment next by the size of the huge mapping since + * on 32-bit there may be more than one entry at the pgd + * level for a single hugepage, but all of them point to + * the same kmem cache that holds the hugepte. + */ + next = addr + (1 << hugepd_shift(*(hugepd_t *)pgd)); +#endif free_hugepd_range(tlb, (hugepd_t *)pgd, PGDIR_SHIFT, addr, next, floor, ceiling); } - } while (pgd++, addr = next, addr != end); + } while (addr = next, addr != end); } struct page * @@ -466,17 +690,35 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { +#ifdef CONFIG_MM_SLICES struct hstate *hstate = hstate_file(file); int mmu_psize = shift_to_mmu_psize(huge_page_shift(hstate)); return slice_get_unmapped_area(addr, len, flags, mmu_psize, 1, 0); +#else + return get_unmapped_area(file, addr, len, pgoff, flags); +#endif } unsigned long vma_mmu_pagesize(struct vm_area_struct *vma) { +#ifdef CONFIG_MM_SLICES unsigned int psize = get_slice_psize(vma->vm_mm, vma->vm_start); return 1UL << mmu_psize_to_shift(psize); +#else + if (!is_vm_hugetlb_page(vma)) + return PAGE_SIZE; + + return huge_page_size(hstate_vma(vma)); +#endif +} + +static inline bool is_power_of_4(unsigned long x) +{ + if (is_power_of_2(x)) + return (__ilog2(x) % 2) ? false : true; + return false; } static int __init add_huge_page_size(unsigned long long size) @@ -486,9 +728,14 @@ static int __init add_huge_page_size(unsigned long long size) /* Check that it is a page size supported by the hardware and * that it fits within pagetable and slice limits. */ +#ifdef CONFIG_PPC_FSL_BOOK3E + if ((size < PAGE_SIZE) || !is_power_of_4(size)) + return -EINVAL; +#else if (!is_power_of_2(size) || (shift > SLICE_HIGH_SHIFT) || (shift <= PAGE_SHIFT)) return -EINVAL; +#endif if ((mmu_psize = shift_to_mmu_psize(shift)) < 0) return -EINVAL; @@ -525,6 +772,46 @@ static int __init hugepage_setup_sz(char *str) } __setup("hugepagesz=", hugepage_setup_sz); +#ifdef CONFIG_FSL_BOOKE +struct kmem_cache *hugepte_cache; +static int __init hugetlbpage_init(void) +{ + int psize; + + for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) { + unsigned shift; + + if (!mmu_psize_defs[psize].shift) + continue; + + shift = mmu_psize_to_shift(psize); + + /* Don't treat normal page sizes as huge... */ + if (shift != PAGE_SHIFT) + if (add_huge_page_size(1ULL << shift) < 0) + continue; + } + + /* + * Create a kmem cache for hugeptes. The bottom bits in the pte have + * size information encoded in them, so align them to allow this + */ + hugepte_cache = kmem_cache_create("hugepte-cache", sizeof(pte_t), + HUGEPD_SHIFT_MASK + 1, 0, NULL); + if (hugepte_cache == NULL) + panic("%s: Unable to create kmem cache for hugeptes\n", + __func__); + + /* Default hpage size = 4M */ + if (mmu_psize_defs[MMU_PAGE_4M].shift) + HPAGE_SHIFT = mmu_psize_defs[MMU_PAGE_4M].shift; + else + panic("%s: Unable to set default huge page size\n", __func__); + + + return 0; +} +#else static int __init hugetlbpage_init(void) { int psize; @@ -567,15 +854,23 @@ static int __init hugetlbpage_init(void) return 0; } - +#endif module_init(hugetlbpage_init); void flush_dcache_icache_hugepage(struct page *page) { int i; + void *start; BUG_ON(!PageCompound(page)); - for (i = 0; i < (1UL << compound_order(page)); i++) - __flush_dcache_icache(page_address(page+i)); + for (i = 0; i < (1UL << compound_order(page)); i++) { + if (!PageHighMem(page)) { + __flush_dcache_icache(page_address(page+i)); + } else { + start = kmap_atomic(page+i, KM_PPC_SYNC_ICACHE); + __flush_dcache_icache(start); + kunmap_atomic(start, KM_PPC_SYNC_ICACHE); + } + } } diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index c77fef56dad..161cefde5c1 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -44,6 +46,7 @@ #include #include #include +#include #include "mmu_decl.h" @@ -123,6 +126,12 @@ void __init MMU_init(void) /* parse args from command line */ MMU_setup(); + /* + * Reserve gigantic pages for hugetlb. This MUST occur before + * lowmem_end_addr is initialized below. + */ + reserve_hugetlb_gpages(); + if (memblock.memory.cnt > 1) { #ifndef CONFIG_WII memblock.memory.cnt = 1; diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index c781bbcf733..ad9cf49dfb8 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -548,4 +548,9 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, return; hash_preload(vma->vm_mm, address, access, trap); #endif /* CONFIG_PPC_STD_MMU */ +#if (defined(CONFIG_PPC_BOOK3E_64) || defined(CONFIG_PPC_FSL_BOOK3E)) \ + && defined(CONFIG_HUGETLB_PAGE) + if (is_vm_hugetlb_page(vma)) + book3e_hugetlb_preload(vma->vm_mm, address, *ptep); +#endif } diff --git a/arch/powerpc/mm/mmu_context_nohash.c b/arch/powerpc/mm/mmu_context_nohash.c index 336807de550..5b63bd3da4a 100644 --- a/arch/powerpc/mm/mmu_context_nohash.c +++ b/arch/powerpc/mm/mmu_context_nohash.c @@ -292,6 +292,11 @@ int init_new_context(struct task_struct *t, struct mm_struct *mm) mm->context.id = MMU_NO_CONTEXT; mm->context.active = 0; +#ifdef CONFIG_PPC_MM_SLICES + if (slice_mm_new_context(mm)) + slice_set_user_psize(mm, mmu_virtual_psize); +#endif + return 0; } diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c index af40c8768a7..214130a4edc 100644 --- a/arch/powerpc/mm/pgtable.c +++ b/arch/powerpc/mm/pgtable.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -212,7 +213,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, entry = set_access_flags_filter(entry, vma, dirty); changed = !pte_same(*(ptep), entry); if (changed) { - if (!(vma->vm_flags & VM_HUGETLB)) + if (!is_vm_hugetlb_page(vma)) assert_pte_locked(vma->vm_mm, address); __ptep_set_access_flags(ptep, entry); flush_tlb_page_nohash(vma, address); diff --git a/arch/powerpc/mm/tlb_low_64e.S b/arch/powerpc/mm/tlb_low_64e.S index 4ebb34bc01d..dc4a5f385e4 100644 --- a/arch/powerpc/mm/tlb_low_64e.S +++ b/arch/powerpc/mm/tlb_low_64e.S @@ -553,24 +553,24 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_TLBRSRV) rldicl r11,r16,64-VPTE_PGD_SHIFT,64-PGD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq virt_page_table_tlb_miss_fault + cmpdi cr0,r15,0 + bge virt_page_table_tlb_miss_fault #ifndef CONFIG_PPC_64K_PAGES /* Get to PUD entry */ rldicl r11,r16,64-VPTE_PUD_SHIFT,64-PUD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq virt_page_table_tlb_miss_fault + cmpdi cr0,r15,0 + bge virt_page_table_tlb_miss_fault #endif /* CONFIG_PPC_64K_PAGES */ /* Get to PMD entry */ rldicl r11,r16,64-VPTE_PMD_SHIFT,64-PMD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq virt_page_table_tlb_miss_fault + cmpdi cr0,r15,0 + bge virt_page_table_tlb_miss_fault /* Ok, we're all right, we can now create a kernel translation for * a 4K or 64K page from r16 -> r15. @@ -802,24 +802,24 @@ htw_tlb_miss: rldicl r11,r16,64-(PGDIR_SHIFT-3),64-PGD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq htw_tlb_miss_fault + cmpdi cr0,r15,0 + bge htw_tlb_miss_fault #ifndef CONFIG_PPC_64K_PAGES /* Get to PUD entry */ rldicl r11,r16,64-(PUD_SHIFT-3),64-PUD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq htw_tlb_miss_fault + cmpdi cr0,r15,0 + bge htw_tlb_miss_fault #endif /* CONFIG_PPC_64K_PAGES */ /* Get to PMD entry */ rldicl r11,r16,64-(PMD_SHIFT-3),64-PMD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq htw_tlb_miss_fault + cmpdi cr0,r15,0 + bge htw_tlb_miss_fault /* Ok, we're all right, we can now create an indirect entry for * a 1M or 256M page. diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c index d32ec643c23..afc95c7304a 100644 --- a/arch/powerpc/mm/tlb_nohash.c +++ b/arch/powerpc/mm/tlb_nohash.c @@ -36,14 +36,49 @@ #include #include #include +#include #include #include #include +#include #include "mmu_decl.h" -#ifdef CONFIG_PPC_BOOK3E +/* + * This struct lists the sw-supported page sizes. The hardawre MMU may support + * other sizes not listed here. The .ind field is only used on MMUs that have + * indirect page table entries. + */ +#ifdef CONFIG_PPC_BOOK3E_MMU +#ifdef CONFIG_FSL_BOOKE +struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = { + [MMU_PAGE_4K] = { + .shift = 12, + .enc = BOOK3E_PAGESZ_4K, + }, + [MMU_PAGE_4M] = { + .shift = 22, + .enc = BOOK3E_PAGESZ_4M, + }, + [MMU_PAGE_16M] = { + .shift = 24, + .enc = BOOK3E_PAGESZ_16M, + }, + [MMU_PAGE_64M] = { + .shift = 26, + .enc = BOOK3E_PAGESZ_64M, + }, + [MMU_PAGE_256M] = { + .shift = 28, + .enc = BOOK3E_PAGESZ_256M, + }, + [MMU_PAGE_1G] = { + .shift = 30, + .enc = BOOK3E_PAGESZ_1GB, + }, +}; +#else struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = { [MMU_PAGE_4K] = { .shift = 12, @@ -77,6 +112,8 @@ struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = { .enc = BOOK3E_PAGESZ_1GB, }, }; +#endif /* CONFIG_FSL_BOOKE */ + static inline int mmu_get_tsize(int psize) { return mmu_psize_defs[psize].enc; @@ -87,7 +124,7 @@ static inline int mmu_get_tsize(int psize) /* This isn't used on !Book3E for now */ return 0; } -#endif +#endif /* CONFIG_PPC_BOOK3E_MMU */ /* The variables below are currently only used on 64-bit Book3E * though this will probably be made common with other nohash @@ -266,6 +303,11 @@ void __flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr, void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) { +#ifdef CONFIG_HUGETLB_PAGE + if (is_vm_hugetlb_page(vma)) + flush_hugetlb_page(vma, vmaddr); +#endif + __flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr, mmu_get_tsize(mmu_virtual_psize), 0); } diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index e06e39589a0..a85990c886e 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -69,6 +69,7 @@ config PPC_BOOK3S_64 bool "Server processors" select PPC_FPU select PPC_HAVE_PMU_SUPPORT + select SYS_SUPPORTS_HUGETLBFS config PPC_BOOK3E_64 bool "Embedded processors" @@ -173,6 +174,7 @@ config BOOKE config FSL_BOOKE bool depends on (E200 || E500) && PPC32 + select SYS_SUPPORTS_HUGETLBFS if PHYS_64BIT default y # this is for common code between PPC32 & PPC64 FSL BOOKE @@ -296,7 +298,7 @@ config PPC_BOOK3E_MMU config PPC_MM_SLICES bool - default y if HUGETLB_PAGE || (PPC_STD_MMU_64 && PPC_64K_PAGES) + default y if (PPC64 && HUGETLB_PAGE) || (PPC_STD_MMU_64 && PPC_64K_PAGES) default n config VIRT_CPU_ACCOUNTING -- cgit v1.2.3-18-g5258 From 14b9247019432fc25e606b78262eb16a4a33b8ed Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 8 Jul 2011 11:12:42 +0000 Subject: powerpc/mpic: Add support for discontiguous cores There is one place in the MPIC driver that assumes that the cores are numbered from 0 to n-1. However, this is not true if the CPUs are not numbered sequentially. This can happen on a eight-core SOC where cores two and three are removed in the device tree. So instead of blindly looping, we iterate over the discovered CPUs and use the SMP ID as the index. This means that we no longer ask the MPIC how many CPUs there are, so we also delete mpic->num_cpus. We also catch if the number of CPUs in the SOC exceeds the number that the MPIC supports. This should never happen, of course, but it's good to be sure. Signed-off-by: Timur Tabi Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/mpic.h | 2 -- arch/powerpc/sysdev/mpic.c | 24 +++++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index df18989e78d..e6fae49e0b7 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -273,8 +273,6 @@ struct mpic unsigned int irq_count; /* Number of sources */ unsigned int num_sources; - /* Number of CPUs */ - unsigned int num_cpus; /* default senses array */ unsigned char *senses; unsigned int senses_count; diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index d5d3ff3d757..9678081dc4e 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1285,13 +1285,11 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) | MPIC_GREG_GCONF_MCK); - /* Read feature register, calculate num CPUs and, for non-ISU - * MPICs, num sources as well. On ISU MPICs, sources are counted - * as ISUs are added + /* + * Read feature register. For non-ISU MPICs, num sources as well. On + * ISU MPICs, sources are counted as ISUs are added */ greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); - mpic->num_cpus = ((greg_feature & MPIC_GREG_FEATURE_LAST_CPU_MASK) - >> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1; if (isu_size == 0) { if (flags & MPIC_BROKEN_FRR_NIRQS) mpic->num_sources = mpic->irq_count; @@ -1301,10 +1299,18 @@ struct mpic * __init mpic_alloc(struct device_node *node, >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1; } + /* + * The MPIC driver will crash if there are more cores than we + * can initialize, so we may as well catch that problem here. + */ + BUG_ON(num_possible_cpus() > MPIC_MAX_CPUS); + /* Map the per-CPU registers */ - for (i = 0; i < mpic->num_cpus; i++) { - mpic_map(mpic, node, paddr, &mpic->cpuregs[i], - MPIC_INFO(CPU_BASE) + i * MPIC_INFO(CPU_STRIDE), + for_each_possible_cpu(i) { + unsigned int cpu = get_hard_smp_processor_id(i); + + mpic_map(mpic, node, paddr, &mpic->cpuregs[cpu], + MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE), 0x1000); } @@ -1343,7 +1349,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, } printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx," " max %d CPUs\n", - name, vers, (unsigned long long)paddr, mpic->num_cpus); + name, vers, (unsigned long long)paddr, num_possible_cpus()); printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", mpic->isu_size, mpic->isu_shift, mpic->isu_mask); -- cgit v1.2.3-18-g5258 From 7392769365f32c82340f184f93408b12dc3da4dc Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Tue, 19 Jul 2011 06:22:44 +0000 Subject: powerpc: Fix build dependencies for epapr.c which needs libfdt.h Currently, the build can (very rarely) fail to build because libfdt.h has not been created or is in the process of being copied. Signed-off-by: Matthew McClintock Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/boot/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index c26200b40a4..ac6705e7571 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -58,7 +58,7 @@ $(addprefix $(obj)/,$(zlib) cuboot-c2k.o gunzip_util.o main.o prpmc2800.o): \ libfdt := fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c libfdtheader := fdt.h libfdt.h libfdt_internal.h -$(addprefix $(obj)/,$(libfdt) libfdt-wrapper.o simpleboot.o): \ +$(addprefix $(obj)/,$(libfdt) libfdt-wrapper.o simpleboot.o epapr.o): \ $(addprefix $(obj)/,$(libfdtheader)) src-wlib := string.S crt0.S crtsavres.S stdio.c main.c \ -- cgit v1.2.3-18-g5258 From 6c493685f1b209dd4ae41eb52c818cf12da20def Mon Sep 17 00:00:00 2001 From: Jim Keniston Date: Mon, 25 Jul 2011 07:54:50 +0000 Subject: powerpc/nvram: Add compression to fit more oops output into NVRAM Capture more than twice as much text from the printk buffer, and compress it to fit it in the lnx,oops-log NVRAM partition. You can view the compressed text using the new (as of July 20) --unzip option of the nvram command in the powerpc-utils package. [BenH: Added select of ZLIB_DEFLATE] Signed-off-by: Jim Keniston Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/rtas.h | 6 +- arch/powerpc/platforms/pseries/Kconfig | 1 + arch/powerpc/platforms/pseries/nvram.c | 171 +++++++++++++++++++++++++++++++-- 3 files changed, 169 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index 58625d1e780..41f69ae79d4 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -249,10 +249,12 @@ extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal); #define ERR_FLAG_ALREADY_LOGGED 0x0 #define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */ #define ERR_TYPE_RTAS_LOG 0x2 /* from rtas event-scan */ -#define ERR_TYPE_KERNEL_PANIC 0x4 /* from panic() */ +#define ERR_TYPE_KERNEL_PANIC 0x4 /* from die()/panic() */ +#define ERR_TYPE_KERNEL_PANIC_GZ 0x8 /* ditto, compressed */ /* All the types and not flags */ -#define ERR_TYPE_MASK (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC) +#define ERR_TYPE_MASK \ + (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC | ERR_TYPE_KERNEL_PANIC_GZ) #define RTAS_DEBUG KERN_DEBUG "RTAS: " diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 05cf4769b88..c81f6bb9c10 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -15,6 +15,7 @@ config PPC_PSERIES select PPC_UDBG_16550 select PPC_NATIVE select PPC_PCI_CHOICE if EXPERT + select ZLIB_DEFLATE default y config PPC_SPLPAR diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 00cc3a09488..a76b22844d1 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -78,8 +80,41 @@ static struct kmsg_dumper nvram_kmsg_dumper = { #define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */ static unsigned long last_unread_rtas_event; /* timestamp */ -/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */ -static char *oops_buf; +/* + * For capturing and compressing an oops or panic report... + + * big_oops_buf[] holds the uncompressed text we're capturing. + * + * oops_buf[] holds the compressed text, preceded by a prefix. + * The prefix is just a u16 holding the length of the compressed* text. + * (*Or uncompressed, if compression fails.) oops_buf[] gets written + * to NVRAM. + * + * oops_len points to the prefix. oops_data points to the compressed text. + * + * +- oops_buf + * | +- oops_data + * v v + * +------------+-----------------------------------------------+ + * | length | text | + * | (2 bytes) | (oops_data_sz bytes) | + * +------------+-----------------------------------------------+ + * ^ + * +- oops_len + * + * We preallocate these buffers during init to avoid kmalloc during oops/panic. + */ +static size_t big_oops_buf_sz; +static char *big_oops_buf, *oops_buf; +static u16 *oops_len; +static char *oops_data; +static size_t oops_data_sz; + +/* Compression parameters */ +#define COMPR_LEVEL 6 +#define WINDOW_BITS 12 +#define MEM_LEVEL 4 +static struct z_stream_s stream; static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) { @@ -387,11 +422,44 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) sizeof(rtas_log_partition)); } oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); + if (!oops_buf) { + pr_err("nvram: No memory for %s partition\n", + oops_log_partition.name); + return; + } + oops_len = (u16*) oops_buf; + oops_data = oops_buf + sizeof(u16); + oops_data_sz = oops_log_partition.size - sizeof(u16); + + /* + * Figure compression (preceded by elimination of each line's + * severity prefix) will reduce the oops/panic report to at most + * 45% of its original size. + */ + big_oops_buf_sz = (oops_data_sz * 100) / 45; + big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); + if (big_oops_buf) { + stream.workspace = kmalloc(zlib_deflate_workspacesize( + WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); + if (!stream.workspace) { + pr_err("nvram: No memory for compression workspace; " + "skipping compression of %s partition data\n", + oops_log_partition.name); + kfree(big_oops_buf); + big_oops_buf = NULL; + } + } else { + pr_err("No memory for uncompressed %s data; " + "skipping compression\n", oops_log_partition.name); + stream.workspace = NULL; + } + rc = kmsg_dump_register(&nvram_kmsg_dumper); if (rc != 0) { pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); kfree(oops_buf); - return; + kfree(big_oops_buf); + kfree(stream.workspace); } } @@ -473,7 +541,83 @@ static int clobbering_unread_rtas_event(void) NVRAM_RTAS_READ_TIMEOUT); } -/* our kmsg_dump callback */ +/* Squeeze out each line's severity prefix. */ +static size_t elide_severities(char *buf, size_t len) +{ + char *in, *out, *buf_end = buf + len; + /* Assume a at the very beginning marks the start of a line. */ + int newline = 1; + + in = out = buf; + while (in < buf_end) { + if (newline && in+3 <= buf_end && + *in == '<' && isdigit(in[1]) && in[2] == '>') { + in += 3; + newline = 0; + } else { + newline = (*in == '\n'); + *out++ = *in++; + } + } + return out - buf; +} + +/* Derived from logfs_compress() */ +static int nvram_compress(const void *in, void *out, size_t inlen, + size_t outlen) +{ + int err, ret; + + ret = -EIO; + err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, + MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + goto error; + + stream.next_in = in; + stream.avail_in = inlen; + stream.total_in = 0; + stream.next_out = out; + stream.avail_out = outlen; + stream.total_out = 0; + + err = zlib_deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + goto error; + + err = zlib_deflateEnd(&stream); + if (err != Z_OK) + goto error; + + if (stream.total_out >= stream.total_in) + goto error; + + ret = stream.total_out; +error: + return ret; +} + +/* Compress the text from big_oops_buf into oops_buf. */ +static int zip_oops(size_t text_len) +{ + int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, + oops_data_sz); + if (zipped_len < 0) { + pr_err("nvram: compression failed; returned %d\n", zipped_len); + pr_err("nvram: logging uncompressed oops/panic report\n"); + return -1; + } + *oops_len = (u16) zipped_len; + return 0; +} + +/* + * This is our kmsg_dump callback, called after an oops or panic report + * has been written to the printk buffer. We want to capture as much + * of the printk buffer as possible. First, capture as much as we can + * that we think will compress sufficiently to fit in the lnx,oops-log + * partition. If that's too much, go back and capture uncompressed text. + */ static void oops_to_nvram(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, const char *old_msgs, unsigned long old_len, @@ -482,6 +626,8 @@ static void oops_to_nvram(struct kmsg_dumper *dumper, static unsigned int oops_count = 0; static bool panicking = false; size_t text_len; + unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ; + int rc = -1; switch (reason) { case KMSG_DUMP_RESTART: @@ -509,8 +655,19 @@ static void oops_to_nvram(struct kmsg_dumper *dumper, if (clobbering_unread_rtas_event()) return; - text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len, - oops_buf, oops_log_partition.size); + if (big_oops_buf) { + text_len = capture_last_msgs(old_msgs, old_len, + new_msgs, new_len, big_oops_buf, big_oops_buf_sz); + text_len = elide_severities(big_oops_buf, text_len); + rc = zip_oops(text_len); + } + if (rc != 0) { + text_len = capture_last_msgs(old_msgs, old_len, + new_msgs, new_len, oops_data, oops_data_sz); + err_type = ERR_TYPE_KERNEL_PANIC; + *oops_len = (u16) text_len; + } + (void) nvram_write_os_partition(&oops_log_partition, oops_buf, - (int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count); + (int) (sizeof(*oops_len) + *oops_len), err_type, ++oops_count); } -- cgit v1.2.3-18-g5258 From 6dece0eb69b2a28e18d104bc5d707f1cb673f5e0 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 25 Jul 2011 11:29:33 +0000 Subject: powerpc/32: Pass device tree address as u64 to machine_init u64 is used rather than phys_addr_t to keep things simple, as this is called from assembly code. Update callers to pass a 64-bit address in r3/r4. Other unused register assignments that were once parameters to machine_init are dropped. For FSL BookE, look up the physical address of the device tree from the effective address passed in r3 by the loader. This is required for situations where memory does not start at zero (due to AMP or IOMMU-less virtualization), and thus the IMA doesn't start at zero, and thus the device tree effective address does not equal the physical address. Signed-off-by: Scott Wood Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/head_32.S | 7 +++--- arch/powerpc/kernel/head_40x.S | 15 +++---------- arch/powerpc/kernel/head_44x.S | 16 +++----------- arch/powerpc/kernel/head_8xx.S | 13 +++-------- arch/powerpc/kernel/head_fsl_booke.S | 42 ++++++++++++++++++++++-------------- arch/powerpc/kernel/setup_32.c | 2 +- 6 files changed, 39 insertions(+), 56 deletions(-) diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index ba250d505e0..0654dba2c1f 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -139,8 +139,7 @@ __start: trap #endif /* CONFIG_PPC_PMAC */ -1: mr r31,r3 /* save parameters */ - mr r30,r4 +1: mr r31,r3 /* save device tree ptr */ li r24,0 /* cpu # */ /* @@ -964,8 +963,8 @@ start_here: * Do early platform-specific initialization, * and set up the MMU. */ - mr r3,r31 - mr r4,r30 + li r3,0 + mr r4,r31 bl machine_init bl __save_cpu_setup bl MMU_init diff --git a/arch/powerpc/kernel/head_40x.S b/arch/powerpc/kernel/head_40x.S index a91626d87fc..872a6af83ba 100644 --- a/arch/powerpc/kernel/head_40x.S +++ b/arch/powerpc/kernel/head_40x.S @@ -58,13 +58,7 @@ _ENTRY(_stext); _ENTRY(_start); - /* Save parameters we are passed. - */ - mr r31,r3 - mr r30,r4 - mr r29,r5 - mr r28,r6 - mr r27,r7 + mr r31,r3 /* save device tree ptr */ /* We have to turn on the MMU right away so we get cache modes * set correctly. @@ -849,11 +843,8 @@ start_here: /* * Decide what sort of machine this is and initialize the MMU. */ - mr r3,r31 - mr r4,r30 - mr r5,r29 - mr r6,r28 - mr r7,r27 + li r3,0 + mr r4,r31 bl machine_init bl MMU_init diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S index f8e971ba94f..b725dab0f88 100644 --- a/arch/powerpc/kernel/head_44x.S +++ b/arch/powerpc/kernel/head_44x.S @@ -61,14 +61,7 @@ _ENTRY(_start); * of abatron_pteptrs */ nop -/* - * Save parameters we are passed - */ - mr r31,r3 - mr r30,r4 - mr r29,r5 - mr r28,r6 - mr r27,r7 + mr r31,r3 /* save device tree ptr */ li r24,0 /* CPU number */ bl init_cpu_state @@ -120,11 +113,8 @@ _ENTRY(_start); /* * Decide what sort of machine this is and initialize the MMU. */ - mr r3,r31 - mr r4,r30 - mr r5,r29 - mr r6,r28 - mr r7,r27 + li r3,0 + mr r4,r31 bl machine_init bl MMU_init diff --git a/arch/powerpc/kernel/head_8xx.S b/arch/powerpc/kernel/head_8xx.S index 1cbf64e6b41..b68cb173ba2 100644 --- a/arch/powerpc/kernel/head_8xx.S +++ b/arch/powerpc/kernel/head_8xx.S @@ -76,11 +76,7 @@ _ENTRY(_start); */ .globl __start __start: - mr r31,r3 /* save parameters */ - mr r30,r4 - mr r29,r5 - mr r28,r6 - mr r27,r7 + mr r31,r3 /* save device tree ptr */ /* We have to turn on the MMU right away so we get cache modes * set correctly. @@ -723,11 +719,8 @@ start_here: /* * Decide what sort of machine this is and initialize the MMU. */ - mr r3,r31 - mr r4,r30 - mr r5,r29 - mr r6,r28 - mr r7,r27 + li r3,0 + mr r4,r31 bl machine_init bl MMU_init diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 4ea9bfbf67e..e1c699f3b7a 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -63,17 +63,30 @@ _ENTRY(_start); * of abatron_pteptrs */ nop -/* - * Save parameters we are passed - */ - mr r31,r3 - mr r30,r4 - mr r29,r5 - mr r28,r6 - mr r27,r7 - li r25,0 /* phys kernel start (low) */ - li r24,0 /* CPU number */ - li r23,0 /* phys kernel start (high) */ + + /* Translate device tree address to physical, save in r30/r31 */ + mfmsr r16 + mfspr r17,SPRN_PID + rlwinm r17,r17,16,0x3fff0000 /* turn PID into MAS6[SPID] */ + rlwimi r17,r16,28,0x00000001 /* turn MSR[DS] into MAS6[SAS] */ + mtspr SPRN_MAS6,r17 + + tlbsx 0,r3 /* must succeed */ + + mfspr r16,SPRN_MAS1 + mfspr r20,SPRN_MAS3 + rlwinm r17,r16,25,0x1f /* r17 = log2(page size) */ + li r18,1024 + slw r18,r18,r17 /* r18 = page size */ + addi r18,r18,-1 + and r19,r3,r18 /* r19 = page offset */ + andc r31,r20,r18 /* r3 = page base */ + or r31,r31,r19 /* r3 = devtree phys addr */ + mfspr r30,SPRN_MAS7 + + li r25,0 /* phys kernel start (low) */ + li r24,0 /* CPU number */ + li r23,0 /* phys kernel start (high) */ /* We try to not make any assumptions about how the boot loader * setup or used the TLBs. We invalidate all mappings from the @@ -198,11 +211,8 @@ _ENTRY(__early_start) /* * Decide what sort of machine this is and initialize the MMU. */ - mr r3,r31 - mr r4,r30 - mr r5,r29 - mr r6,r28 - mr r7,r27 + mr r3,r30 + mr r4,r31 bl machine_init bl MMU_init diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index 209135af0a4..c1ce86357ec 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -117,7 +117,7 @@ notrace unsigned long __init early_init(unsigned long dt_ptr) * This is called very early on the boot process, after a minimal * MMU environment has been set up but before MMU_init is called. */ -notrace void __init machine_init(unsigned long dt_ptr) +notrace void __init machine_init(u64 dt_ptr) { lockdep_init(); -- cgit v1.2.3-18-g5258 From 7a19081fc2658157a1b84e6f3288450c33d98569 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 8 Aug 2011 01:18:00 +0000 Subject: pseries/iommu: Add missing kfree At this point, window has not been stored anywhere, so it has to be freed before leaving the function. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @exists@ local idexpression x; statement S,S1; expression E; identifier fl; expression *ptr != NULL; @@ x = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S <... when != x when != if (...) { <+...kfree(x)...+> } when any when != true x == NULL x->fl ...> ( if (x == NULL) S1 | if (...) { ... when != x when forall ( return \(0\|<+...x...+>\|ptr\); | * return ...; ) } ) // Signed-off-by: Julia Lawall Acked-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 9f121a37eb5..5905a3b9f7e 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -939,14 +939,14 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) if (ret) { dev_info(&dev->dev, "failed to map direct window for %s: %d\n", dn->full_name, ret); - goto out_clear_window; + goto out_free_window; } ret = prom_add_property(pdn, win64); if (ret) { dev_err(&dev->dev, "unable to add dma window property for %s: %d", pdn->full_name, ret); - goto out_clear_window; + goto out_free_window; } window->device = pdn; @@ -958,6 +958,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) dma_addr = of_read_number(&create.addr_hi, 2); goto out_unlock; +out_free_window: + kfree(window); + out_clear_window: remove_ddw(pdn); -- cgit v1.2.3-18-g5258 From 2fa3d9e5c699ff49e96199eeb9bea4508e7f5dac Mon Sep 17 00:00:00 2001 From: Jimi Xenidis Date: Mon, 8 Aug 2011 11:33:16 +0000 Subject: powerpc/wsp: Fix Wire Speed Processor platform configs Some config selections were applied to the platform (reference board) when they actuall apply to the chip. Signed-off-by: Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/wsp/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig index c3c48eb62cc..d051581e9eb 100644 --- a/arch/powerpc/platforms/wsp/Kconfig +++ b/arch/powerpc/platforms/wsp/Kconfig @@ -1,5 +1,9 @@ config PPC_WSP bool + select PPC_A2 + select PPC_SCOM + select PPC_XICS + select PPC_ICP_NATIVE default n menu "WSP platform selection" @@ -7,13 +11,9 @@ menu "WSP platform selection" 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 -- cgit v1.2.3-18-g5258 From f352c7251255effe6c2326190f1378adbd142aa3 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 8 Aug 2011 12:30:54 +0000 Subject: powerpc/wsp: Add PCIe Root support to PowerEN/WSP Based on a patch by Benjamin Herrenschmidt Modernized and slightly modified to not record erros into the nvram log since we do not have that device driver just yet. Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/wsp/Kconfig | 3 + arch/powerpc/platforms/wsp/Makefile | 1 + arch/powerpc/platforms/wsp/psr2.c | 4 + arch/powerpc/platforms/wsp/wsp.h | 3 + arch/powerpc/platforms/wsp/wsp_pci.c | 1133 ++++++++++++++++++++++++++++++++++ arch/powerpc/platforms/wsp/wsp_pci.h | 268 ++++++++ 6 files changed, 1412 insertions(+) create mode 100644 arch/powerpc/platforms/wsp/wsp_pci.c create mode 100644 arch/powerpc/platforms/wsp/wsp_pci.h diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig index d051581e9eb..f4fb837873f 100644 --- a/arch/powerpc/platforms/wsp/Kconfig +++ b/arch/powerpc/platforms/wsp/Kconfig @@ -4,6 +4,9 @@ config PPC_WSP select PPC_SCOM select PPC_XICS select PPC_ICP_NATIVE + select PCI + select PPC_IO_WORKAROUNDS if PCI + select PPC_INDIRECT_PIO if PCI default n menu "WSP platform selection" diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile index 095be73d6cd..84519f8d894 100644 --- a/arch/powerpc/platforms/wsp/Makefile +++ b/arch/powerpc/platforms/wsp/Makefile @@ -4,3 +4,4 @@ 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 +obj-$(CONFIG_PCI) += wsp_pci.o diff --git a/arch/powerpc/platforms/wsp/psr2.c b/arch/powerpc/platforms/wsp/psr2.c index 40f28916ff6..166f2e4b4be 100644 --- a/arch/powerpc/platforms/wsp/psr2.c +++ b/arch/powerpc/platforms/wsp/psr2.c @@ -63,6 +63,10 @@ static void __init psr2_setup_arch(void) #ifdef CONFIG_SMP a2_setup_smp(); #endif +#ifdef CONFIG_PCI + wsp_setup_pci(); +#endif + } static int __init psr2_probe(void) diff --git a/arch/powerpc/platforms/wsp/wsp.h b/arch/powerpc/platforms/wsp/wsp.h index 7c3e087fd2f..33479818f62 100644 --- a/arch/powerpc/platforms/wsp/wsp.h +++ b/arch/powerpc/platforms/wsp/wsp.h @@ -3,6 +3,9 @@ #include +/* Devtree compatible strings for major devices */ +#define PCIE_COMPATIBLE "ibm,wsp-pciex" + extern void wsp_setup_pci(void); extern void scom_init_wsp(void); diff --git a/arch/powerpc/platforms/wsp/wsp_pci.c b/arch/powerpc/platforms/wsp/wsp_pci.c new file mode 100644 index 00000000000..e0262cd0e2d --- /dev/null +++ b/arch/powerpc/platforms/wsp/wsp_pci.c @@ -0,0 +1,1133 @@ +/* + * Copyright 2010 Ben Herrenschmidt, 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. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wsp.h" +#include "wsp_pci.h" +#include "msi.h" + + +/* Max number of TVTs for one table. Only 32-bit tables can use + * multiple TVTs and so the max currently supported is thus 8 + * since only 2G of DMA space is supported + */ +#define MAX_TABLE_TVT_COUNT 8 + +struct wsp_dma_table { + struct list_head link; + struct iommu_table table; + struct wsp_phb *phb; + struct page *tces[MAX_TABLE_TVT_COUNT]; +}; + +/* We support DMA regions from 0...2G in 32bit space (no support for + * 64-bit DMA just yet). Each device gets a separate TCE table (TVT + * entry) with validation enabled (though not supported by SimiCS + * just yet). + * + * To simplify things, we divide this 2G space into N regions based + * on the constant below which could be turned into a tunable eventually + * + * We then assign dynamically those regions to devices as they show up. + * + * We use a bitmap as an allocator for these. + * + * Tables are allocated/created dynamically as devices are discovered, + * multiple TVT entries are used if needed + * + * When 64-bit DMA support is added we should simply use a separate set + * of larger regions (the HW supports 64 TVT entries). We can + * additionally create a bypass region in 64-bit space for performances + * though that would have a cost in term of security. + * + * If you set NUM_DMA32_REGIONS to 1, then a single table is shared + * for all devices and bus/dev/fn validation is disabled + * + * Note that a DMA32 region cannot be smaller than 256M so the max + * supported here for now is 8. We don't yet support sharing regions + * between multiple devices so the max number of devices supported + * is MAX_TABLE_TVT_COUNT. + */ +#define NUM_DMA32_REGIONS 1 + +struct wsp_phb { + struct pci_controller *hose; + + /* Lock controlling access to the list of dma tables. + * It does -not- protect against dma_* operations on + * those tables, those should be stopped before an entry + * is removed from the list. + * + * The lock is also used for error handling operations + */ + spinlock_t lock; + struct list_head dma_tables; + unsigned long dma32_map; + unsigned long dma32_base; + unsigned int dma32_num_regions; + unsigned long dma32_region_size; + + /* Debugfs stuff */ + struct dentry *ddir; + + struct list_head all; +}; +static LIST_HEAD(wsp_phbs); + +//#define cfg_debug(fmt...) pr_debug(fmt) +#define cfg_debug(fmt...) + + +static int wsp_pcie_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct pci_controller *hose; + int suboff; + u64 addr; + + hose = pci_bus_to_host(bus); + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + if (offset >= 0x1000) + return PCIBIOS_BAD_REGISTER_NUMBER; + addr = PCIE_REG_CA_ENABLE | + ((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT | + ((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT | + ((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT; + suboff = offset & 3; + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + + switch (len) { + case 1: + addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + *val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA) + >> (suboff << 3)) & 0xff; + cfg_debug("read 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, *val); + break; + case 2: + addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + *val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA) + >> (suboff << 3)) & 0xffff; + cfg_debug("read 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, *val); + break; + default: + addr |= 0xful << PCIE_REG_CA_BE_SHIFT; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + *val = in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA); + cfg_debug("read 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, *val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int wsp_pcie_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose; + int suboff; + u64 addr; + + hose = pci_bus_to_host(bus); + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + if (offset >= 0x1000) + return PCIBIOS_BAD_REGISTER_NUMBER; + addr = PCIE_REG_CA_ENABLE | + ((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT | + ((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT | + ((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT; + suboff = offset & 3; + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT; + val <<= suboff << 3; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); + cfg_debug("write 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, val); + break; + case 2: + addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT; + val <<= suboff << 3; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); + cfg_debug("write 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, val); + break; + default: + addr |= 0xful << PCIE_REG_CA_BE_SHIFT; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); + cfg_debug("write 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops wsp_pcie_pci_ops = +{ + .read = wsp_pcie_read_config, + .write = wsp_pcie_write_config, +}; + +#define TCE_SHIFT 12 +#define TCE_PAGE_SIZE (1 << TCE_SHIFT) +#define TCE_PCI_WRITE 0x2 /* write from PCI allowed */ +#define TCE_PCI_READ 0x1 /* read from PCI allowed */ +#define TCE_RPN_MASK 0x3fffffffffful /* 42-bit RPN (4K pages) */ +#define TCE_RPN_SHIFT 12 + +//#define dma_debug(fmt...) pr_debug(fmt) +#define dma_debug(fmt...) + +static int tce_build_wsp(struct iommu_table *tbl, long index, long npages, + unsigned long uaddr, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + struct wsp_dma_table *ptbl = container_of(tbl, + struct wsp_dma_table, + table); + u64 proto_tce; + u64 *tcep; + u64 rpn; + + proto_tce = TCE_PCI_READ; +#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS + proto_tce |= TCE_PCI_WRITE; +#else + if (direction != DMA_TO_DEVICE) + proto_tce |= TCE_PCI_WRITE; +#endif + + /* XXX Make this faster by factoring out the page address for + * within a TCE table + */ + while (npages--) { + /* We don't use it->base as the table can be scattered */ + tcep = (u64 *)page_address(ptbl->tces[index >> 16]); + tcep += (index & 0xffff); + + /* can't move this out since we might cross LMB boundary */ + rpn = __pa(uaddr) >> TCE_SHIFT; + *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; + + dma_debug("[DMA] TCE %p set to 0x%016llx (dma addr: 0x%lx)\n", + tcep, *tcep, (tbl->it_offset + index) << IOMMU_PAGE_SHIFT); + + uaddr += TCE_PAGE_SIZE; + index++; + } + return 0; +} + +static void tce_free_wsp(struct iommu_table *tbl, long index, long npages) +{ + struct wsp_dma_table *ptbl = container_of(tbl, + struct wsp_dma_table, + table); +#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS + struct pci_controller *hose = ptbl->phb->hose; +#endif + u64 *tcep; + + /* XXX Make this faster by factoring out the page address for + * within a TCE table. Also use line-kill option to kill multiple + * TCEs at once + */ + while (npages--) { + /* We don't use it->base as the table can be scattered */ + tcep = (u64 *)page_address(ptbl->tces[index >> 16]); + tcep += (index & 0xffff); + dma_debug("[DMA] TCE %p cleared\n", tcep); + *tcep = 0; +#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS + /* Don't write there since it would pollute other MMIO accesses */ + out_be64(hose->cfg_data + PCIE_REG_TCE_KILL, + PCIE_REG_TCEKILL_SINGLE | PCIE_REG_TCEKILL_PS_4K | + (__pa(tcep) & PCIE_REG_TCEKILL_ADDR_MASK)); +#endif + index++; + } +} + +static struct wsp_dma_table *wsp_pci_create_dma32_table(struct wsp_phb *phb, + unsigned int region, + struct pci_dev *validate) +{ + struct pci_controller *hose = phb->hose; + unsigned long size = phb->dma32_region_size; + unsigned long addr = phb->dma32_region_size * region + phb->dma32_base; + struct wsp_dma_table *tbl; + int tvts_per_table, i, tvt, nid; + unsigned long flags; + + nid = of_node_to_nid(phb->hose->dn); + + /* Calculate how many TVTs are needed */ + tvts_per_table = size / 0x10000000; + if (tvts_per_table == 0) + tvts_per_table = 1; + + /* Calculate the base TVT index. We know all tables have the same + * size so we just do a simple multiply here + */ + tvt = region * tvts_per_table; + + pr_debug(" Region : %d\n", region); + pr_debug(" DMA range : 0x%08lx..0x%08lx\n", addr, addr + size - 1); + pr_debug(" Number of TVTs : %d\n", tvts_per_table); + pr_debug(" Base TVT : %d\n", tvt); + pr_debug(" Node : %d\n", nid); + + tbl = kzalloc_node(sizeof(struct wsp_dma_table), GFP_KERNEL, nid); + if (!tbl) + return ERR_PTR(-ENOMEM); + tbl->phb = phb; + + /* Create as many TVTs as needed, each represents 256M at most */ + for (i = 0; i < tvts_per_table; i++) { + u64 tvt_data1, tvt_data0; + + /* Allocate table. We use a 4K TCE size for now always so + * one table is always 8 * (258M / 4K) == 512K + */ + tbl->tces[i] = alloc_pages_node(nid, GFP_KERNEL, get_order(0x80000)); + if (tbl->tces[i] == NULL) + goto fail; + memset(page_address(tbl->tces[i]), 0, 0x80000); + + pr_debug(" TCE table %d at : %p\n", i, page_address(tbl->tces[i])); + + /* Table size. We currently set it to be the whole 256M region */ + tvt_data0 = 2ull << IODA_TVT0_TCE_TABLE_SIZE_SHIFT; + /* IO page size set to 4K */ + tvt_data1 = 1ull << IODA_TVT1_IO_PAGE_SIZE_SHIFT; + /* Shift in the address */ + tvt_data0 |= __pa(page_address(tbl->tces[i])) << IODA_TVT0_TTA_SHIFT; + + /* Validation stuff. We only validate fully bus/dev/fn for now + * one day maybe we can group devices but that isn't the case + * at the moment + */ + if (validate) { + tvt_data0 |= IODA_TVT0_BUSNUM_VALID_MASK; + tvt_data0 |= validate->bus->number; + tvt_data1 |= IODA_TVT1_DEVNUM_VALID; + tvt_data1 |= ((u64)PCI_SLOT(validate->devfn)) + << IODA_TVT1_DEVNUM_VALUE_SHIFT; + tvt_data1 |= IODA_TVT1_FUNCNUM_VALID; + tvt_data1 |= ((u64)PCI_FUNC(validate->devfn)) + << IODA_TVT1_FUNCNUM_VALUE_SHIFT; + } + + /* XX PE number is always 0 for now */ + + /* Program the values using the PHB lock */ + spin_lock_irqsave(&phb->lock, flags); + out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR, + (tvt + i) | PCIE_REG_IODA_AD_TBL_TVT); + out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, tvt_data1); + out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, tvt_data0); + spin_unlock_irqrestore(&phb->lock, flags); + } + + /* Init bits and pieces */ + tbl->table.it_blocksize = 16; + tbl->table.it_offset = addr >> IOMMU_PAGE_SHIFT; + tbl->table.it_size = size >> IOMMU_PAGE_SHIFT; + + /* + * It's already blank but we clear it anyway. + * Consider an aditiona interface that makes cleaing optional + */ + iommu_init_table(&tbl->table, nid); + + list_add(&tbl->link, &phb->dma_tables); + return tbl; + + fail: + pr_debug(" Failed to allocate a 256M TCE table !\n"); + for (i = 0; i < tvts_per_table; i++) + if (tbl->tces[i]) + __free_pages(tbl->tces[i], get_order(0x80000)); + kfree(tbl); + return ERR_PTR(-ENOMEM); +} + +static void __devinit wsp_pci_dma_dev_setup(struct pci_dev *pdev) +{ + struct dev_archdata *archdata = &pdev->dev.archdata; + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct wsp_phb *phb = hose->private_data; + struct wsp_dma_table *table = NULL; + unsigned long flags; + int i; + + /* Don't assign an iommu table to a bridge */ + if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + return; + + pr_debug("%s: Setting up DMA...\n", pci_name(pdev)); + + spin_lock_irqsave(&phb->lock, flags); + + /* If only one region, check if it already exist */ + if (phb->dma32_num_regions == 1) { + spin_unlock_irqrestore(&phb->lock, flags); + if (list_empty(&phb->dma_tables)) + table = wsp_pci_create_dma32_table(phb, 0, NULL); + else + table = list_first_entry(&phb->dma_tables, + struct wsp_dma_table, + link); + } else { + /* else find a free region */ + for (i = 0; i < phb->dma32_num_regions && !table; i++) { + if (__test_and_set_bit(i, &phb->dma32_map)) + continue; + spin_unlock_irqrestore(&phb->lock, flags); + table = wsp_pci_create_dma32_table(phb, i, pdev); + } + } + + /* Check if we got an error */ + if (IS_ERR(table)) { + pr_err("%s: Failed to create DMA table, err %ld !\n", + pci_name(pdev), PTR_ERR(table)); + return; + } + + /* Or a valid table */ + if (table) { + pr_info("%s: Setup iommu: 32-bit DMA region 0x%08lx..0x%08lx\n", + pci_name(pdev), + table->table.it_offset << IOMMU_PAGE_SHIFT, + (table->table.it_offset << IOMMU_PAGE_SHIFT) + + phb->dma32_region_size - 1); + archdata->dma_data.iommu_table_base = &table->table; + return; + } + + /* Or no room */ + spin_unlock_irqrestore(&phb->lock, flags); + pr_err("%s: Out of DMA space !\n", pci_name(pdev)); +} + +static void __init wsp_pcie_configure_hw(struct pci_controller *hose) +{ + u64 val; + int i; + +#define DUMP_REG(x) \ + pr_debug("%-30s : 0x%016llx\n", #x, in_be64(hose->cfg_data + x)) + +#ifdef CONFIG_WSP_DD1_WORKAROUND_BAD_PCIE_CLASS + /* WSP DD1 has a bogus class code by default in the PCI-E + * root complex's built-in P2P bridge */ + val = in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1); + pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", val); + out_be64(hose->cfg_data + PCIE_REG_SYS_CFG1, + (val & ~PCIE_REG_SYS_CFG1_CLASS_CODE) | (PCI_CLASS_BRIDGE_PCI << 8)); + pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1)); +#endif /* CONFIG_WSP_DD1_WORKAROUND_BAD_PCIE_CLASS */ + +#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS + /* XXX Disable TCE caching, it doesn't work on DD1 */ + out_be64(hose->cfg_data + 0xe50, + in_be64(hose->cfg_data + 0xe50) | (3ull << 62)); + printk("PCI-E DEBUG CONTROL 5 = 0x%llx\n", in_be64(hose->cfg_data + 0xe50)); +#endif + + /* Configure M32A and IO. IO is hard wired to be 1M for now */ + out_be64(hose->cfg_data + PCIE_REG_IO_BASE_ADDR, hose->io_base_phys); + out_be64(hose->cfg_data + PCIE_REG_IO_BASE_MASK, + (~(hose->io_resource.end - hose->io_resource.start)) & + 0x3fffffff000ul); + out_be64(hose->cfg_data + PCIE_REG_IO_START_ADDR, 0 | 1); + + out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_ADDR, + hose->mem_resources[0].start); + printk("Want to write to M32A_BASE_MASK : 0x%llx\n", + (~(hose->mem_resources[0].end - + hose->mem_resources[0].start)) & 0x3ffffff0000ul); + out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_MASK, + (~(hose->mem_resources[0].end - + hose->mem_resources[0].start)) & 0x3ffffff0000ul); + out_be64(hose->cfg_data + PCIE_REG_M32A_START_ADDR, + (hose->mem_resources[0].start - hose->pci_mem_offset) | 1); + + /* Clear all TVT entries + * + * XX Might get TVT count from device-tree + */ + for (i = 0; i < IODA_TVT_COUNT; i++) { + out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR, + PCIE_REG_IODA_AD_TBL_TVT | i); + out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, 0); + out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, 0); + } + + /* Kill the TCE cache */ + out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG, + in_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG) | + PCIE_REG_PHBC_64B_TCE_EN); + + /* Enable 32 & 64-bit MSIs, IO space and M32A */ + val = PCIE_REG_PHBC_32BIT_MSI_EN | + PCIE_REG_PHBC_IO_EN | + PCIE_REG_PHBC_64BIT_MSI_EN | + PCIE_REG_PHBC_M32A_EN; + if (iommu_is_off) + val |= PCIE_REG_PHBC_DMA_XLATE_BYPASS; + pr_debug("Will write config: 0x%llx\n", val); + out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG, val); + + /* Enable error reporting */ + out_be64(hose->cfg_data + 0xe00, + in_be64(hose->cfg_data + 0xe00) | 0x0008000000000000ull); + + /* Mask an error that's generated when doing config space probe + * + * XXX Maybe we should only mask it around config space cycles... that or + * ignore it when we know we had a config space cycle recently ? + */ + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS_MASK, 0x8000000000000000ull); + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS_MASK, 0x8000000000000000ull); + + /* Enable UTL errors, for now, all of them got to UTL irq 1 + * + * We similarily mask one UTL error caused apparently during normal + * probing. We also mask the link up error + */ + out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_ERR_SEV, 0); + out_be64(hose->cfg_data + PCIE_UTL_RC_ERR_SEVERITY, 0); + out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_ERROR_SEV, 0); + out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_IRQ_EN, 0xffffffff00000000ull); + out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_IRQ_EN, 0xff5fffff00000000ull); + out_be64(hose->cfg_data + PCIE_UTL_EP_ERR_IRQ_EN, 0xffffffff00000000ull); + + DUMP_REG(PCIE_REG_IO_BASE_ADDR); + DUMP_REG(PCIE_REG_IO_BASE_MASK); + DUMP_REG(PCIE_REG_IO_START_ADDR); + DUMP_REG(PCIE_REG_M32A_BASE_ADDR); + DUMP_REG(PCIE_REG_M32A_BASE_MASK); + DUMP_REG(PCIE_REG_M32A_START_ADDR); + DUMP_REG(PCIE_REG_M32B_BASE_ADDR); + DUMP_REG(PCIE_REG_M32B_BASE_MASK); + DUMP_REG(PCIE_REG_M32B_START_ADDR); + DUMP_REG(PCIE_REG_M64_BASE_ADDR); + DUMP_REG(PCIE_REG_M64_BASE_MASK); + DUMP_REG(PCIE_REG_M64_START_ADDR); + DUMP_REG(PCIE_REG_PHB_CONFIG); +} + +static void wsp_pci_wait_io_idle(struct wsp_phb *phb, unsigned long port) +{ + u64 val; + int i; + + for (i = 0; i < 10000; i++) { + val = in_be64(phb->hose->cfg_data + 0xe08); + if ((val & 0x1900000000000000ull) == 0x0100000000000000ull) + return; + udelay(1); + } + pr_warning("PCI IO timeout on domain %d port 0x%lx\n", + phb->hose->global_number, port); +} + +#define DEF_PCI_AC_RET_pio(name, ret, at, al, aa) \ +static ret wsp_pci_##name at \ +{ \ + struct iowa_bus *bus; \ + struct wsp_phb *phb; \ + unsigned long flags; \ + ret rval; \ + bus = iowa_pio_find_bus(aa); \ + WARN_ON(!bus); \ + phb = bus->private; \ + spin_lock_irqsave(&phb->lock, flags); \ + wsp_pci_wait_io_idle(phb, aa); \ + rval = __do_##name al; \ + spin_unlock_irqrestore(&phb->lock, flags); \ + return rval; \ +} + +#define DEF_PCI_AC_NORET_pio(name, at, al, aa) \ +static void wsp_pci_##name at \ +{ \ + struct iowa_bus *bus; \ + struct wsp_phb *phb; \ + unsigned long flags; \ + bus = iowa_pio_find_bus(aa); \ + WARN_ON(!bus); \ + phb = bus->private; \ + spin_lock_irqsave(&phb->lock, flags); \ + wsp_pci_wait_io_idle(phb, aa); \ + __do_##name al; \ + spin_unlock_irqrestore(&phb->lock, flags); \ +} + +#define DEF_PCI_AC_RET_mem(name, ret, at, al, aa) +#define DEF_PCI_AC_NORET_mem(name, at, al, aa) + +#define DEF_PCI_AC_RET(name, ret, at, al, space, aa) \ + DEF_PCI_AC_RET_##space(name, ret, at, al, aa) + +#define DEF_PCI_AC_NORET(name, at, al, space, aa) \ + DEF_PCI_AC_NORET_##space(name, at, al, aa) \ + + +#include + +#undef DEF_PCI_AC_RET +#undef DEF_PCI_AC_NORET + +static struct ppc_pci_io wsp_pci_iops = { + .inb = wsp_pci_inb, + .inw = wsp_pci_inw, + .inl = wsp_pci_inl, + .outb = wsp_pci_outb, + .outw = wsp_pci_outw, + .outl = wsp_pci_outl, + .insb = wsp_pci_insb, + .insw = wsp_pci_insw, + .insl = wsp_pci_insl, + .outsb = wsp_pci_outsb, + .outsw = wsp_pci_outsw, + .outsl = wsp_pci_outsl, +}; + +static int __init wsp_setup_one_phb(struct device_node *np) +{ + struct pci_controller *hose; + struct wsp_phb *phb; + + pr_info("PCI: Setting up PCIe host bridge 0x%s\n", np->full_name); + + phb = zalloc_maybe_bootmem(sizeof(struct wsp_phb), GFP_KERNEL); + if (!phb) + return -ENOMEM; + hose = pcibios_alloc_controller(np); + if (!hose) { + /* Can't really free the phb */ + return -ENOMEM; + } + hose->private_data = phb; + phb->hose = hose; + + INIT_LIST_HEAD(&phb->dma_tables); + spin_lock_init(&phb->lock); + + /* XXX Use bus-range property ? */ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* We use cfg_data as the address for the whole bridge MMIO space + */ + hose->cfg_data = of_iomap(hose->dn, 0); + + pr_debug("PCIe registers mapped at 0x%p\n", hose->cfg_data); + + /* Get the ranges of the device-tree */ + pci_process_bridge_OF_ranges(hose, np, 0); + + /* XXX Force re-assigning of everything for now */ + pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC | + PCI_ENABLE_PROC_DOMAINS); + pci_probe_only = 0; + + /* Calculate how the TCE space is divided */ + phb->dma32_base = 0; + phb->dma32_num_regions = NUM_DMA32_REGIONS; + if (phb->dma32_num_regions > MAX_TABLE_TVT_COUNT) { + pr_warning("IOMMU: Clamped to %d DMA32 regions\n", + MAX_TABLE_TVT_COUNT); + phb->dma32_num_regions = MAX_TABLE_TVT_COUNT; + } + phb->dma32_region_size = 0x80000000 / phb->dma32_num_regions; + + BUG_ON(!is_power_of_2(phb->dma32_region_size)); + + /* Setup config ops */ + hose->ops = &wsp_pcie_pci_ops; + + /* Configure the HW */ + wsp_pcie_configure_hw(hose); + + /* Instanciate IO workarounds */ + iowa_register_bus(hose, &wsp_pci_iops, NULL, phb); +#ifdef CONFIG_PCI_MSI + wsp_setup_phb_msi(hose); +#endif + + /* Add to global list */ + list_add(&phb->all, &wsp_phbs); + + return 0; +} + +void __init wsp_setup_pci(void) +{ + struct device_node *np; + int rc; + + /* Find host bridges */ + for_each_compatible_node(np, "pciex", PCIE_COMPATIBLE) { + rc = wsp_setup_one_phb(np); + if (rc) + pr_err("Failed to setup PCIe bridge %s, rc=%d\n", + np->full_name, rc); + } + + /* Establish device-tree linkage */ + pci_devs_phb_init(); + + /* Set DMA ops to use TCEs */ + if (iommu_is_off) { + pr_info("PCI-E: Disabled TCEs, using direct DMA\n"); + set_pci_dma_ops(&dma_direct_ops); + } else { + ppc_md.pci_dma_dev_setup = wsp_pci_dma_dev_setup; + ppc_md.tce_build = tce_build_wsp; + ppc_md.tce_free = tce_free_wsp; + set_pci_dma_ops(&dma_iommu_ops); + } +} + +#define err_debug(fmt...) pr_debug(fmt) +//#define err_debug(fmt...) + +static int __init wsp_pci_get_err_irq_no_dt(struct device_node *np) +{ + const u32 *prop; + int hw_irq; + + /* Ok, no interrupts property, let's try to find our child P2P */ + np = of_get_next_child(np, NULL); + if (np == NULL) + return 0; + + /* Grab it's interrupt map */ + prop = of_get_property(np, "interrupt-map", NULL); + if (prop == NULL) + return 0; + + /* Grab one of the interrupts in there, keep the low 4 bits */ + hw_irq = prop[5] & 0xf; + + /* 0..4 for PHB 0 and 5..9 for PHB 1 */ + if (hw_irq < 5) + hw_irq = 4; + else + hw_irq = 9; + hw_irq |= prop[5] & ~0xf; + + err_debug("PCI: Using 0x%x as error IRQ for %s\n", + hw_irq, np->parent->full_name); + return irq_create_mapping(NULL, hw_irq); +} + +static const struct { + u32 offset; + const char *name; +} wsp_pci_regs[] = { +#define DREG(x) { PCIE_REG_##x, #x } +#define DUTL(x) { PCIE_UTL_##x, "UTL_" #x } + /* Architected registers except CONFIG_ and IODA + * to avoid side effects + */ + DREG(DMA_CHAN_STATUS), + DREG(CPU_LOADSTORE_STATUS), + DREG(LOCK0), + DREG(LOCK1), + DREG(PHB_CONFIG), + DREG(IO_BASE_ADDR), + DREG(IO_BASE_MASK), + DREG(IO_START_ADDR), + DREG(M32A_BASE_ADDR), + DREG(M32A_BASE_MASK), + DREG(M32A_START_ADDR), + DREG(M32B_BASE_ADDR), + DREG(M32B_BASE_MASK), + DREG(M32B_START_ADDR), + DREG(M64_BASE_ADDR), + DREG(M64_BASE_MASK), + DREG(M64_START_ADDR), + DREG(TCE_KILL), + DREG(LOCK2), + DREG(PHB_GEN_CAP), + DREG(PHB_TCE_CAP), + DREG(PHB_IRQ_CAP), + DREG(PHB_EEH_CAP), + DREG(PAPR_ERR_INJ_CONTROL), + DREG(PAPR_ERR_INJ_ADDR), + DREG(PAPR_ERR_INJ_MASK), + + /* UTL core regs */ + DUTL(SYS_BUS_CONTROL), + DUTL(STATUS), + DUTL(SYS_BUS_AGENT_STATUS), + DUTL(SYS_BUS_AGENT_ERR_SEV), + DUTL(SYS_BUS_AGENT_IRQ_EN), + DUTL(SYS_BUS_BURST_SZ_CONF), + DUTL(REVISION_ID), + DUTL(OUT_POST_HDR_BUF_ALLOC), + DUTL(OUT_POST_DAT_BUF_ALLOC), + DUTL(IN_POST_HDR_BUF_ALLOC), + DUTL(IN_POST_DAT_BUF_ALLOC), + DUTL(OUT_NP_BUF_ALLOC), + DUTL(IN_NP_BUF_ALLOC), + DUTL(PCIE_TAGS_ALLOC), + DUTL(GBIF_READ_TAGS_ALLOC), + + DUTL(PCIE_PORT_CONTROL), + DUTL(PCIE_PORT_STATUS), + DUTL(PCIE_PORT_ERROR_SEV), + DUTL(PCIE_PORT_IRQ_EN), + DUTL(RC_STATUS), + DUTL(RC_ERR_SEVERITY), + DUTL(RC_IRQ_EN), + DUTL(EP_STATUS), + DUTL(EP_ERR_SEVERITY), + DUTL(EP_ERR_IRQ_EN), + DUTL(PCI_PM_CTRL1), + DUTL(PCI_PM_CTRL2), + + /* PCIe stack regs */ + DREG(SYSTEM_CONFIG1), + DREG(SYSTEM_CONFIG2), + DREG(EP_SYSTEM_CONFIG), + DREG(EP_FLR), + DREG(EP_BAR_CONFIG), + DREG(LINK_CONFIG), + DREG(PM_CONFIG), + DREG(DLP_CONTROL), + DREG(DLP_STATUS), + DREG(ERR_REPORT_CONTROL), + DREG(SLOT_CONTROL1), + DREG(SLOT_CONTROL2), + DREG(UTL_CONFIG), + DREG(BUFFERS_CONFIG), + DREG(ERROR_INJECT), + DREG(SRIOV_CONFIG), + DREG(PF0_SRIOV_STATUS), + DREG(PF1_SRIOV_STATUS), + DREG(PORT_NUMBER), + DREG(POR_SYSTEM_CONFIG), + + /* Internal logic regs */ + DREG(PHB_VERSION), + DREG(RESET), + DREG(PHB_CONTROL), + DREG(PHB_TIMEOUT_CONTROL1), + DREG(PHB_QUIESCE_DMA), + DREG(PHB_DMA_READ_TAG_ACTV), + DREG(PHB_TCE_READ_TAG_ACTV), + + /* FIR registers */ + DREG(LEM_FIR_ACCUM), + DREG(LEM_FIR_AND_MASK), + DREG(LEM_FIR_OR_MASK), + DREG(LEM_ACTION0), + DREG(LEM_ACTION1), + DREG(LEM_ERROR_MASK), + DREG(LEM_ERROR_AND_MASK), + DREG(LEM_ERROR_OR_MASK), + + /* Error traps registers */ + DREG(PHB_ERR_STATUS), + DREG(PHB_ERR_STATUS), + DREG(PHB_ERR1_STATUS), + DREG(PHB_ERR_INJECT), + DREG(PHB_ERR_LEM_ENABLE), + DREG(PHB_ERR_IRQ_ENABLE), + DREG(PHB_ERR_FREEZE_ENABLE), + DREG(PHB_ERR_SIDE_ENABLE), + DREG(PHB_ERR_LOG_0), + DREG(PHB_ERR_LOG_1), + DREG(PHB_ERR_STATUS_MASK), + DREG(PHB_ERR1_STATUS_MASK), + DREG(MMIO_ERR_STATUS), + DREG(MMIO_ERR1_STATUS), + DREG(MMIO_ERR_INJECT), + DREG(MMIO_ERR_LEM_ENABLE), + DREG(MMIO_ERR_IRQ_ENABLE), + DREG(MMIO_ERR_FREEZE_ENABLE), + DREG(MMIO_ERR_SIDE_ENABLE), + DREG(MMIO_ERR_LOG_0), + DREG(MMIO_ERR_LOG_1), + DREG(MMIO_ERR_STATUS_MASK), + DREG(MMIO_ERR1_STATUS_MASK), + DREG(DMA_ERR_STATUS), + DREG(DMA_ERR1_STATUS), + DREG(DMA_ERR_INJECT), + DREG(DMA_ERR_LEM_ENABLE), + DREG(DMA_ERR_IRQ_ENABLE), + DREG(DMA_ERR_FREEZE_ENABLE), + DREG(DMA_ERR_SIDE_ENABLE), + DREG(DMA_ERR_LOG_0), + DREG(DMA_ERR_LOG_1), + DREG(DMA_ERR_STATUS_MASK), + DREG(DMA_ERR1_STATUS_MASK), + + /* Debug and Trace registers */ + DREG(PHB_DEBUG_CONTROL0), + DREG(PHB_DEBUG_STATUS0), + DREG(PHB_DEBUG_CONTROL1), + DREG(PHB_DEBUG_STATUS1), + DREG(PHB_DEBUG_CONTROL2), + DREG(PHB_DEBUG_STATUS2), + DREG(PHB_DEBUG_CONTROL3), + DREG(PHB_DEBUG_STATUS3), + DREG(PHB_DEBUG_CONTROL4), + DREG(PHB_DEBUG_STATUS4), + DREG(PHB_DEBUG_CONTROL5), + DREG(PHB_DEBUG_STATUS5), + + /* Don't seem to exist ... + DREG(PHB_DEBUG_CONTROL6), + DREG(PHB_DEBUG_STATUS6), + */ +}; + +static int wsp_pci_regs_show(struct seq_file *m, void *private) +{ + struct wsp_phb *phb = m->private; + struct pci_controller *hose = phb->hose; + int i; + + for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) { + /* Skip write-only regs */ + if (wsp_pci_regs[i].offset == 0xc08 || + wsp_pci_regs[i].offset == 0xc10 || + wsp_pci_regs[i].offset == 0xc38 || + wsp_pci_regs[i].offset == 0xc40) + continue; + seq_printf(m, "0x%03x: 0x%016llx %s\n", + wsp_pci_regs[i].offset, + in_be64(hose->cfg_data + wsp_pci_regs[i].offset), + wsp_pci_regs[i].name); + } + return 0; +} + +static int wsp_pci_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, wsp_pci_regs_show, inode->i_private); +} + +static const struct file_operations wsp_pci_regs_fops = { + .open = wsp_pci_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int wsp_pci_reg_set(void *data, u64 val) +{ + out_be64((void __iomem *)data, val); + return 0; +} + +static int wsp_pci_reg_get(void *data, u64 *val) +{ + *val = in_be64((void __iomem *)data); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(wsp_pci_reg_fops, wsp_pci_reg_get, wsp_pci_reg_set, "0x%llx\n"); + +static irqreturn_t wsp_pci_err_irq(int irq, void *dev_id) +{ + struct wsp_phb *phb = dev_id; + struct pci_controller *hose = phb->hose; + irqreturn_t handled = IRQ_NONE; + struct wsp_pcie_err_log_data ed; + + pr_err("PCI: Error interrupt on %s (PHB %d)\n", + hose->dn->full_name, hose->global_number); + again: + memset(&ed, 0, sizeof(ed)); + + /* Read and clear UTL errors */ + ed.utl_sys_err = in_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS); + if (ed.utl_sys_err) + out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS, ed.utl_sys_err); + ed.utl_port_err = in_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS); + if (ed.utl_port_err) + out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS, ed.utl_port_err); + ed.utl_rc_err = in_be64(hose->cfg_data + PCIE_UTL_RC_STATUS); + if (ed.utl_rc_err) + out_be64(hose->cfg_data + PCIE_UTL_RC_STATUS, ed.utl_rc_err); + + /* Read and clear main trap errors */ + ed.phb_err = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS); + if (ed.phb_err) { + ed.phb_err1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS); + ed.phb_log0 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_0); + ed.phb_log1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_1); + out_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS, 0); + out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS, 0); + } + ed.mmio_err = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS); + if (ed.mmio_err) { + ed.mmio_err1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS); + ed.mmio_log0 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_0); + ed.mmio_log1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_1); + out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS, 0); + out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS, 0); + } + ed.dma_err = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS); + if (ed.dma_err) { + ed.dma_err1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS); + ed.dma_log0 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_0); + ed.dma_log1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_1); + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS, 0); + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS, 0); + } + + /* Now print things out */ + if (ed.phb_err) { + pr_err(" PHB Error Status : 0x%016llx\n", ed.phb_err); + pr_err(" PHB First Error Status: 0x%016llx\n", ed.phb_err1); + pr_err(" PHB Error Log 0 : 0x%016llx\n", ed.phb_log0); + pr_err(" PHB Error Log 1 : 0x%016llx\n", ed.phb_log1); + } + if (ed.mmio_err) { + pr_err(" MMIO Error Status : 0x%016llx\n", ed.mmio_err); + pr_err(" MMIO First Error Status: 0x%016llx\n", ed.mmio_err1); + pr_err(" MMIO Error Log 0 : 0x%016llx\n", ed.mmio_log0); + pr_err(" MMIO Error Log 1 : 0x%016llx\n", ed.mmio_log1); + } + if (ed.dma_err) { + pr_err(" DMA Error Status : 0x%016llx\n", ed.dma_err); + pr_err(" DMA First Error Status: 0x%016llx\n", ed.dma_err1); + pr_err(" DMA Error Log 0 : 0x%016llx\n", ed.dma_log0); + pr_err(" DMA Error Log 1 : 0x%016llx\n", ed.dma_log1); + } + if (ed.utl_sys_err) + pr_err(" UTL Sys Error Status : 0x%016llx\n", ed.utl_sys_err); + if (ed.utl_port_err) + pr_err(" UTL Port Error Status : 0x%016llx\n", ed.utl_port_err); + if (ed.utl_rc_err) + pr_err(" UTL RC Error Status : 0x%016llx\n", ed.utl_rc_err); + + /* Interrupts are caused by the error traps. If we had any error there + * we loop again in case the UTL buffered some new stuff between + * going there and going to the traps + */ + if (ed.dma_err || ed.mmio_err || ed.phb_err) { + handled = IRQ_HANDLED; + goto again; + } + return handled; +} + +static void __init wsp_setup_pci_err_reporting(struct wsp_phb *phb) +{ + struct pci_controller *hose = phb->hose; + int err_irq, i, rc; + char fname[16]; + + /* Create a debugfs file for that PHB */ + sprintf(fname, "phb%d", phb->hose->global_number); + phb->ddir = debugfs_create_dir(fname, powerpc_debugfs_root); + + /* Some useful debug output */ + if (phb->ddir) { + struct dentry *d = debugfs_create_dir("regs", phb->ddir); + char tmp[64]; + + for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) { + sprintf(tmp, "%03x_%s", wsp_pci_regs[i].offset, + wsp_pci_regs[i].name); + debugfs_create_file(tmp, 0600, d, + hose->cfg_data + wsp_pci_regs[i].offset, + &wsp_pci_reg_fops); + } + debugfs_create_file("all_regs", 0600, phb->ddir, phb, &wsp_pci_regs_fops); + } + + /* Find the IRQ number for that PHB */ + err_irq = irq_of_parse_and_map(hose->dn, 0); + if (err_irq == 0) + /* XXX Error IRQ lacking from device-tree */ + err_irq = wsp_pci_get_err_irq_no_dt(hose->dn); + if (err_irq == 0) { + pr_err("PCI: Failed to fetch error interrupt for %s\n", + hose->dn->full_name); + return; + } + /* Request it */ + rc = request_irq(err_irq, wsp_pci_err_irq, 0, "wsp_pci error", phb); + if (rc) { + pr_err("PCI: Failed to request interrupt for %s\n", + hose->dn->full_name); + } + /* Enable interrupts for all errors for now */ + out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_IRQ_ENABLE, 0xffffffffffffffffull); + out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_IRQ_ENABLE, 0xffffffffffffffffull); + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_IRQ_ENABLE, 0xffffffffffffffffull); +} + +/* + * This is called later to hookup with the error interrupt + */ +static int __init wsp_setup_pci_late(void) +{ + struct wsp_phb *phb; + + list_for_each_entry(phb, &wsp_phbs, all) + wsp_setup_pci_err_reporting(phb); + + return 0; +} +arch_initcall(wsp_setup_pci_late); diff --git a/arch/powerpc/platforms/wsp/wsp_pci.h b/arch/powerpc/platforms/wsp/wsp_pci.h new file mode 100644 index 00000000000..52e9bd95250 --- /dev/null +++ b/arch/powerpc/platforms/wsp/wsp_pci.h @@ -0,0 +1,268 @@ +/* + * Copyright 2010 Ben Herrenschmidt, 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 __WSP_PCI_H +#define __WSP_PCI_H + +/* Architected registers */ +#define PCIE_REG_DMA_CHAN_STATUS 0x110 +#define PCIE_REG_CPU_LOADSTORE_STATUS 0x120 + +#define PCIE_REG_CONFIG_DATA 0x130 +#define PCIE_REG_LOCK0 0x138 +#define PCIE_REG_CONFIG_ADDRESS 0x140 +#define PCIE_REG_CA_ENABLE 0x8000000000000000ull +#define PCIE_REG_CA_BUS_MASK 0x0ff0000000000000ull +#define PCIE_REG_CA_BUS_SHIFT (20+32) +#define PCIE_REG_CA_DEV_MASK 0x000f800000000000ull +#define PCIE_REG_CA_DEV_SHIFT (15+32) +#define PCIE_REG_CA_FUNC_MASK 0x0000700000000000ull +#define PCIE_REG_CA_FUNC_SHIFT (12+32) +#define PCIE_REG_CA_REG_MASK 0x00000fff00000000ull +#define PCIE_REG_CA_REG_SHIFT ( 0+32) +#define PCIE_REG_CA_BE_MASK 0x00000000f0000000ull +#define PCIE_REG_CA_BE_SHIFT ( 28) +#define PCIE_REG_LOCK1 0x148 + +#define PCIE_REG_PHB_CONFIG 0x160 +#define PCIE_REG_PHBC_64B_TCE_EN 0x2000000000000000ull +#define PCIE_REG_PHBC_MMIO_DMA_FREEZE_EN 0x1000000000000000ull +#define PCIE_REG_PHBC_32BIT_MSI_EN 0x0080000000000000ull +#define PCIE_REG_PHBC_M64_EN 0x0040000000000000ull +#define PCIE_REG_PHBC_IO_EN 0x0008000000000000ull +#define PCIE_REG_PHBC_64BIT_MSI_EN 0x0002000000000000ull +#define PCIE_REG_PHBC_M32A_EN 0x0000800000000000ull +#define PCIE_REG_PHBC_M32B_EN 0x0000400000000000ull +#define PCIE_REG_PHBC_MSI_PE_VALIDATE 0x0000200000000000ull +#define PCIE_REG_PHBC_DMA_XLATE_BYPASS 0x0000100000000000ull + +#define PCIE_REG_IO_BASE_ADDR 0x170 +#define PCIE_REG_IO_BASE_MASK 0x178 +#define PCIE_REG_IO_START_ADDR 0x180 + +#define PCIE_REG_M32A_BASE_ADDR 0x190 +#define PCIE_REG_M32A_BASE_MASK 0x198 +#define PCIE_REG_M32A_START_ADDR 0x1a0 + +#define PCIE_REG_M32B_BASE_ADDR 0x1b0 +#define PCIE_REG_M32B_BASE_MASK 0x1b8 +#define PCIE_REG_M32B_START_ADDR 0x1c0 + +#define PCIE_REG_M64_BASE_ADDR 0x1e0 +#define PCIE_REG_M64_BASE_MASK 0x1e8 +#define PCIE_REG_M64_START_ADDR 0x1f0 + +#define PCIE_REG_TCE_KILL 0x210 +#define PCIE_REG_TCEKILL_SINGLE 0x8000000000000000ull +#define PCIE_REG_TCEKILL_ADDR_MASK 0x000003fffffffff8ull +#define PCIE_REG_TCEKILL_PS_4K 0 +#define PCIE_REG_TCEKILL_PS_64K 1 +#define PCIE_REG_TCEKILL_PS_16M 2 +#define PCIE_REG_TCEKILL_PS_16G 3 + +#define PCIE_REG_IODA_ADDR 0x220 +#define PCIE_REG_IODA_AD_AUTOINC 0x8000000000000000ull +#define PCIE_REG_IODA_AD_TBL_MVT 0x0005000000000000ull +#define PCIE_REG_IODA_AD_TBL_PELT 0x0006000000000000ull +#define PCIE_REG_IODA_AD_TBL_PESTA 0x0007000000000000ull +#define PCIE_REG_IODA_AD_TBL_PESTB 0x0008000000000000ull +#define PCIE_REG_IODA_AD_TBL_TVT 0x0009000000000000ull +#define PCIE_REG_IODA_AD_TBL_TCE 0x000a000000000000ull +#define PCIE_REG_IODA_DATA0 0x228 +#define PCIE_REG_IODA_DATA1 0x230 + +#define PCIE_REG_LOCK2 0x240 + +#define PCIE_REG_PHB_GEN_CAP 0x250 +#define PCIE_REG_PHB_TCE_CAP 0x258 +#define PCIE_REG_PHB_IRQ_CAP 0x260 +#define PCIE_REG_PHB_EEH_CAP 0x268 + +#define PCIE_REG_PAPR_ERR_INJ_CONTROL 0x2b0 +#define PCIE_REG_PAPR_ERR_INJ_ADDR 0x2b8 +#define PCIE_REG_PAPR_ERR_INJ_MASK 0x2c0 + + +#define PCIE_REG_SYS_CFG1 0x600 +#define PCIE_REG_SYS_CFG1_CLASS_CODE 0x0000000000ffffffull + +#define IODA_TVT0_TTA_MASK 0x000fffffffff0000ull +#define IODA_TVT0_TTA_SHIFT 4 +#define IODA_TVT0_BUSNUM_VALID_MASK 0x000000000000e000ull +#define IODA_TVT0_TCE_TABLE_SIZE_MASK 0x0000000000001f00ull +#define IODA_TVT0_TCE_TABLE_SIZE_SHIFT 8 +#define IODA_TVT0_BUSNUM_VALUE_MASK 0x00000000000000ffull +#define IODA_TVT0_BUSNUM_VALID_SHIFT 0 +#define IODA_TVT1_DEVNUM_VALID 0x2000000000000000ull +#define IODA_TVT1_DEVNUM_VALUE_MASK 0x1f00000000000000ull +#define IODA_TVT1_DEVNUM_VALUE_SHIFT 56 +#define IODA_TVT1_FUNCNUM_VALID 0x0008000000000000ull +#define IODA_TVT1_FUNCNUM_VALUE_MASK 0x0007000000000000ull +#define IODA_TVT1_FUNCNUM_VALUE_SHIFT 48 +#define IODA_TVT1_IO_PAGE_SIZE_MASK 0x00001f0000000000ull +#define IODA_TVT1_IO_PAGE_SIZE_SHIFT 40 +#define IODA_TVT1_PE_NUMBER_MASK 0x000000000000003full +#define IODA_TVT1_PE_NUMBER_SHIFT 0 + +#define IODA_TVT_COUNT 64 + +/* UTL Core registers */ +#define PCIE_UTL_SYS_BUS_CONTROL 0x400 +#define PCIE_UTL_STATUS 0x408 +#define PCIE_UTL_SYS_BUS_AGENT_STATUS 0x410 +#define PCIE_UTL_SYS_BUS_AGENT_ERR_SEV 0x418 +#define PCIE_UTL_SYS_BUS_AGENT_IRQ_EN 0x420 +#define PCIE_UTL_SYS_BUS_BURST_SZ_CONF 0x440 +#define PCIE_UTL_REVISION_ID 0x448 + +#define PCIE_UTL_OUT_POST_HDR_BUF_ALLOC 0x4c0 +#define PCIE_UTL_OUT_POST_DAT_BUF_ALLOC 0x4d0 +#define PCIE_UTL_IN_POST_HDR_BUF_ALLOC 0x4e0 +#define PCIE_UTL_IN_POST_DAT_BUF_ALLOC 0x4f0 +#define PCIE_UTL_OUT_NP_BUF_ALLOC 0x500 +#define PCIE_UTL_IN_NP_BUF_ALLOC 0x510 +#define PCIE_UTL_PCIE_TAGS_ALLOC 0x520 +#define PCIE_UTL_GBIF_READ_TAGS_ALLOC 0x530 + +#define PCIE_UTL_PCIE_PORT_CONTROL 0x540 +#define PCIE_UTL_PCIE_PORT_STATUS 0x548 +#define PCIE_UTL_PCIE_PORT_ERROR_SEV 0x550 +#define PCIE_UTL_PCIE_PORT_IRQ_EN 0x558 +#define PCIE_UTL_RC_STATUS 0x560 +#define PCIE_UTL_RC_ERR_SEVERITY 0x568 +#define PCIE_UTL_RC_IRQ_EN 0x570 +#define PCIE_UTL_EP_STATUS 0x578 +#define PCIE_UTL_EP_ERR_SEVERITY 0x580 +#define PCIE_UTL_EP_ERR_IRQ_EN 0x588 + +#define PCIE_UTL_PCI_PM_CTRL1 0x590 +#define PCIE_UTL_PCI_PM_CTRL2 0x598 + +/* PCIe stack registers */ +#define PCIE_REG_SYSTEM_CONFIG1 0x600 +#define PCIE_REG_SYSTEM_CONFIG2 0x608 +#define PCIE_REG_EP_SYSTEM_CONFIG 0x618 +#define PCIE_REG_EP_FLR 0x620 +#define PCIE_REG_EP_BAR_CONFIG 0x628 +#define PCIE_REG_LINK_CONFIG 0x630 +#define PCIE_REG_PM_CONFIG 0x640 +#define PCIE_REG_DLP_CONTROL 0x650 +#define PCIE_REG_DLP_STATUS 0x658 +#define PCIE_REG_ERR_REPORT_CONTROL 0x660 +#define PCIE_REG_SLOT_CONTROL1 0x670 +#define PCIE_REG_SLOT_CONTROL2 0x678 +#define PCIE_REG_UTL_CONFIG 0x680 +#define PCIE_REG_BUFFERS_CONFIG 0x690 +#define PCIE_REG_ERROR_INJECT 0x698 +#define PCIE_REG_SRIOV_CONFIG 0x6a0 +#define PCIE_REG_PF0_SRIOV_STATUS 0x6a8 +#define PCIE_REG_PF1_SRIOV_STATUS 0x6b0 +#define PCIE_REG_PORT_NUMBER 0x700 +#define PCIE_REG_POR_SYSTEM_CONFIG 0x708 + +/* PHB internal logic registers */ +#define PCIE_REG_PHB_VERSION 0x800 +#define PCIE_REG_RESET 0x808 +#define PCIE_REG_PHB_CONTROL 0x810 +#define PCIE_REG_PHB_TIMEOUT_CONTROL1 0x878 +#define PCIE_REG_PHB_QUIESCE_DMA 0x888 +#define PCIE_REG_PHB_DMA_READ_TAG_ACTV 0x900 +#define PCIE_REG_PHB_TCE_READ_TAG_ACTV 0x908 + +/* FIR registers */ +#define PCIE_REG_LEM_FIR_ACCUM 0xc00 +#define PCIE_REG_LEM_FIR_AND_MASK 0xc08 +#define PCIE_REG_LEM_FIR_OR_MASK 0xc10 +#define PCIE_REG_LEM_ACTION0 0xc18 +#define PCIE_REG_LEM_ACTION1 0xc20 +#define PCIE_REG_LEM_ERROR_MASK 0xc30 +#define PCIE_REG_LEM_ERROR_AND_MASK 0xc38 +#define PCIE_REG_LEM_ERROR_OR_MASK 0xc40 + +/* PHB Error registers */ +#define PCIE_REG_PHB_ERR_STATUS 0xc80 +#define PCIE_REG_PHB_ERR1_STATUS 0xc88 +#define PCIE_REG_PHB_ERR_INJECT 0xc90 +#define PCIE_REG_PHB_ERR_LEM_ENABLE 0xc98 +#define PCIE_REG_PHB_ERR_IRQ_ENABLE 0xca0 +#define PCIE_REG_PHB_ERR_FREEZE_ENABLE 0xca8 +#define PCIE_REG_PHB_ERR_SIDE_ENABLE 0xcb8 +#define PCIE_REG_PHB_ERR_LOG_0 0xcc0 +#define PCIE_REG_PHB_ERR_LOG_1 0xcc8 +#define PCIE_REG_PHB_ERR_STATUS_MASK 0xcd0 +#define PCIE_REG_PHB_ERR1_STATUS_MASK 0xcd8 + +#define PCIE_REG_MMIO_ERR_STATUS 0xd00 +#define PCIE_REG_MMIO_ERR1_STATUS 0xd08 +#define PCIE_REG_MMIO_ERR_INJECT 0xd10 +#define PCIE_REG_MMIO_ERR_LEM_ENABLE 0xd18 +#define PCIE_REG_MMIO_ERR_IRQ_ENABLE 0xd20 +#define PCIE_REG_MMIO_ERR_FREEZE_ENABLE 0xd28 +#define PCIE_REG_MMIO_ERR_SIDE_ENABLE 0xd38 +#define PCIE_REG_MMIO_ERR_LOG_0 0xd40 +#define PCIE_REG_MMIO_ERR_LOG_1 0xd48 +#define PCIE_REG_MMIO_ERR_STATUS_MASK 0xd50 +#define PCIE_REG_MMIO_ERR1_STATUS_MASK 0xd58 + +#define PCIE_REG_DMA_ERR_STATUS 0xd80 +#define PCIE_REG_DMA_ERR1_STATUS 0xd88 +#define PCIE_REG_DMA_ERR_INJECT 0xd90 +#define PCIE_REG_DMA_ERR_LEM_ENABLE 0xd98 +#define PCIE_REG_DMA_ERR_IRQ_ENABLE 0xda0 +#define PCIE_REG_DMA_ERR_FREEZE_ENABLE 0xda8 +#define PCIE_REG_DMA_ERR_SIDE_ENABLE 0xdb8 +#define PCIE_REG_DMA_ERR_LOG_0 0xdc0 +#define PCIE_REG_DMA_ERR_LOG_1 0xdc8 +#define PCIE_REG_DMA_ERR_STATUS_MASK 0xdd0 +#define PCIE_REG_DMA_ERR1_STATUS_MASK 0xdd8 + +/* Shortcuts for access to the above using the PHB definitions + * with an offset + */ +#define PCIE_REG_ERR_PHB_OFFSET 0x0 +#define PCIE_REG_ERR_MMIO_OFFSET 0x80 +#define PCIE_REG_ERR_DMA_OFFSET 0x100 + +/* Debug and Trace registers */ +#define PCIE_REG_PHB_DEBUG_CONTROL0 0xe00 +#define PCIE_REG_PHB_DEBUG_STATUS0 0xe08 +#define PCIE_REG_PHB_DEBUG_CONTROL1 0xe10 +#define PCIE_REG_PHB_DEBUG_STATUS1 0xe18 +#define PCIE_REG_PHB_DEBUG_CONTROL2 0xe20 +#define PCIE_REG_PHB_DEBUG_STATUS2 0xe28 +#define PCIE_REG_PHB_DEBUG_CONTROL3 0xe30 +#define PCIE_REG_PHB_DEBUG_STATUS3 0xe38 +#define PCIE_REG_PHB_DEBUG_CONTROL4 0xe40 +#define PCIE_REG_PHB_DEBUG_STATUS4 0xe48 +#define PCIE_REG_PHB_DEBUG_CONTROL5 0xe50 +#define PCIE_REG_PHB_DEBUG_STATUS5 0xe58 +#define PCIE_REG_PHB_DEBUG_CONTROL6 0xe60 +#define PCIE_REG_PHB_DEBUG_STATUS6 0xe68 + +/* Definition for PCIe errors */ +struct wsp_pcie_err_log_data { + __u64 phb_err; + __u64 phb_err1; + __u64 phb_log0; + __u64 phb_log1; + __u64 mmio_err; + __u64 mmio_err1; + __u64 mmio_log0; + __u64 mmio_log1; + __u64 dma_err; + __u64 dma_err1; + __u64 dma_log0; + __u64 dma_log1; + __u64 utl_sys_err; + __u64 utl_port_err; + __u64 utl_rc_err; + __u64 unused; +}; + +#endif /* __WSP_PCI_H */ -- cgit v1.2.3-18-g5258 From f9a71e0fd1b44148d7af6ce2fecfb2cf7a4df636 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 8 Aug 2011 12:30:55 +0000 Subject: powerpc/wsp: Add MSI support for PCI on PowerEN Based on a patch by Michael Ellerman Patch was simply forward ported upstream. Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/wsp/Makefile | 1 + arch/powerpc/platforms/wsp/ics.c | 48 +++++++++++++++++ arch/powerpc/platforms/wsp/ics.h | 5 ++ arch/powerpc/platforms/wsp/msi.c | 102 ++++++++++++++++++++++++++++++++++++ arch/powerpc/platforms/wsp/msi.h | 19 +++++++ 5 files changed, 175 insertions(+) create mode 100644 arch/powerpc/platforms/wsp/msi.c create mode 100644 arch/powerpc/platforms/wsp/msi.h diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile index 84519f8d894..a1486b436f0 100644 --- a/arch/powerpc/platforms/wsp/Makefile +++ b/arch/powerpc/platforms/wsp/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PPC_PSR2) += psr2.o opb_pic.o obj-$(CONFIG_PPC_WSP) += scom_wsp.o obj-$(CONFIG_SMP) += smp.o scom_smp.o obj-$(CONFIG_PCI) += wsp_pci.o +obj-$(CONFIG_PCI_MSI) += msi.o \ No newline at end of file diff --git a/arch/powerpc/platforms/wsp/ics.c b/arch/powerpc/platforms/wsp/ics.c index e53bd9e7b12..57687439254 100644 --- a/arch/powerpc/platforms/wsp/ics.c +++ b/arch/powerpc/platforms/wsp/ics.c @@ -710,3 +710,51 @@ void __init wsp_init_irq(void) /* We need to patch our irq chip's EOI to point to the right ICP */ wsp_irq_chip.irq_eoi = icp_ops->eoi; } + +#ifdef CONFIG_PCI_MSI +static void wsp_ics_msi_unmask_irq(struct irq_data *d) +{ + wsp_chip_unmask_irq(d); + unmask_msi_irq(d); +} + +static unsigned int wsp_ics_msi_startup(struct irq_data *d) +{ + wsp_ics_msi_unmask_irq(d); + return 0; +} + +static void wsp_ics_msi_mask_irq(struct irq_data *d) +{ + mask_msi_irq(d); + wsp_chip_mask_irq(d); +} + +/* + * we do it this way because we reassinge default EOI handling in + * irq_init() above + */ +static void wsp_ics_eoi(struct irq_data *data) +{ + wsp_irq_chip.irq_eoi(data); +} + +static struct irq_chip wsp_ics_msi = { + .name = "WSP ICS MSI", + .irq_startup = wsp_ics_msi_startup, + .irq_mask = wsp_ics_msi_mask_irq, + .irq_unmask = wsp_ics_msi_unmask_irq, + .irq_eoi = wsp_ics_eoi, + .irq_set_affinity = wsp_chip_set_affinity +}; + +void wsp_ics_set_msi_chip(unsigned int irq) +{ + irq_set_chip(irq, &wsp_ics_msi); +} + +void wsp_ics_set_std_chip(unsigned int irq) +{ + irq_set_chip(irq, &wsp_irq_chip); +} +#endif /* CONFIG_PCI_MSI */ diff --git a/arch/powerpc/platforms/wsp/ics.h b/arch/powerpc/platforms/wsp/ics.h index e34d5310264..07b644e0cf9 100644 --- a/arch/powerpc/platforms/wsp/ics.h +++ b/arch/powerpc/platforms/wsp/ics.h @@ -17,4 +17,9 @@ 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); +#ifdef CONFIG_PCI_MSI +extern void wsp_ics_set_msi_chip(unsigned int irq); +extern void wsp_ics_set_std_chip(unsigned int irq); +#endif /* CONFIG_PCI_MSI */ + #endif /* __ICS_H */ diff --git a/arch/powerpc/platforms/wsp/msi.c b/arch/powerpc/platforms/wsp/msi.c new file mode 100644 index 00000000000..380882f27ad --- /dev/null +++ b/arch/powerpc/platforms/wsp/msi.c @@ -0,0 +1,102 @@ +/* + * Copyright 2011 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 +#include +#include +#include +#include + +#include "msi.h" +#include "ics.h" +#include "wsp_pci.h" + +/* Magic addresses for 32 & 64-bit MSIs with hardcoded MVE 0 */ +#define MSI_ADDR_32 0xFFFF0000ul +#define MSI_ADDR_64 0x1000000000000000ul + +int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + struct pci_controller *phb; + struct msi_desc *entry; + struct msi_msg msg; + unsigned int virq; + int hwirq; + + phb = pci_bus_to_host(dev->bus); + if (!phb) + return -ENOENT; + + entry = list_first_entry(&dev->msi_list, struct msi_desc, list); + if (entry->msi_attrib.is_64) { + msg.address_lo = 0; + msg.address_hi = MSI_ADDR_64 >> 32; + } else { + msg.address_lo = MSI_ADDR_32; + msg.address_hi = 0; + } + + list_for_each_entry(entry, &dev->msi_list, list) { + hwirq = wsp_ics_alloc_irq(phb->dn, 1); + if (hwirq < 0) { + dev_warn(&dev->dev, "wsp_msi: hwirq alloc failed!\n"); + return hwirq; + } + + virq = irq_create_mapping(NULL, hwirq); + if (virq == NO_IRQ) { + dev_warn(&dev->dev, "wsp_msi: virq alloc failed!\n"); + return -1; + } + + dev_dbg(&dev->dev, "wsp_msi: allocated irq %#x/%#x\n", + hwirq, virq); + + wsp_ics_set_msi_chip(virq); + irq_set_msi_desc(virq, entry); + msg.data = hwirq & XIVE_ADDR_MASK; + write_msi_msg(virq, &msg); + } + + return 0; +} + +void wsp_teardown_msi_irqs(struct pci_dev *dev) +{ + struct pci_controller *phb; + struct msi_desc *entry; + int hwirq; + + phb = pci_bus_to_host(dev->bus); + + dev_dbg(&dev->dev, "wsp_msi: tearing down msi irqs\n"); + + list_for_each_entry(entry, &dev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + + irq_set_msi_desc(entry->irq, NULL); + wsp_ics_set_std_chip(entry->irq); + + hwirq = virq_to_hw(entry->irq); + /* In this order to avoid racing with irq_create_mapping() */ + irq_dispose_mapping(entry->irq); + wsp_ics_free_irq(phb->dn, hwirq); + } +} + +void wsp_setup_phb_msi(struct pci_controller *phb) +{ + /* Create a single MVE at offset 0 that matches everything */ + out_be64(phb->cfg_data + PCIE_REG_IODA_ADDR, PCIE_REG_IODA_AD_TBL_MVT); + out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63); + + ppc_md.setup_msi_irqs = wsp_setup_msi_irqs; + ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs; +} diff --git a/arch/powerpc/platforms/wsp/msi.h b/arch/powerpc/platforms/wsp/msi.h new file mode 100644 index 00000000000..0ab27b71b24 --- /dev/null +++ b/arch/powerpc/platforms/wsp/msi.h @@ -0,0 +1,19 @@ +/* + * Copyright 2011 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. + */ + +#ifndef __WSP_MSI_H +#define __WSP_MSI_H + +#ifdef CONFIG_PCI_MSI +extern void wsp_setup_phb_msi(struct pci_controller *phb); +#else +static inline void wsp_setup_phb_msi(struct pci_controller *phb) { } +#endif + +#endif /* __WSP_MSI_H */ -- cgit v1.2.3-18-g5258 From 9c57a32b2f1615e0ad77e9d3b68fd720f43430da Mon Sep 17 00:00:00 2001 From: Tony Breeds Date: Wed, 10 Aug 2011 20:16:54 +0000 Subject: powerpc/4xx/pci: Add __init annotations for *init_port_hw() functions. The various port_init_hw methods of ppc4xx_pciex_hwops should have been marked __init and when I added ppc4xx_pciex_port_reset_sdr(), which is __init. This added many section mismatch warnings like: WARNING: arch/powerpc/sysdev/built-in.o(.text+0x5c68): Section mismatch in reference from the function ppc440spe_pciex_init_port_hw() to the function .init.text:ppc4xx_pciex_port_reset_sdr() The function ppc440spe_pciex_init_port_hw() references the function __init ppc4xx_pciex_port_reset_sdr(). This is often because ppc440spe_pciex_init_port_hw lacks a __init annotation or the annotation of ppc4xx_pciex_port_reset_sdr is wrong. Trivial patch to silence those warnings. Reported-By: Stephen Rothwell Signed-off-by: Tony Breeds Yours Tony Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/sysdev/ppc4xx_pci.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index 2ec046d0a39..862f11b3821 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -834,7 +834,7 @@ static int __init ppc440spe_pciex_core_init(struct device_node *np) return 3; } -static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { u32 val = 1 << 24; @@ -872,12 +872,12 @@ static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) return ppc4xx_pciex_port_reset_sdr(port); } -static int ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { return ppc440spe_pciex_init_port_hw(port); } -static int ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { int rc = ppc440spe_pciex_init_port_hw(port); @@ -936,7 +936,7 @@ static int __init ppc460ex_pciex_core_init(struct device_node *np) return 2; } -static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { u32 val; u32 utlset1; @@ -1126,7 +1126,7 @@ static int __init ppc460sx_pciex_core_init(struct device_node *np) return 2; } -static int ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { if (port->endpoint) @@ -1218,7 +1218,7 @@ static void ppc405ex_pcie_phy_reset(struct ppc4xx_pciex_port *port) mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x00101000); } -static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { u32 val; -- cgit v1.2.3-18-g5258 From 0330581ab3b9002d55ee66f377ccbbb742175c01 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Tue, 16 Aug 2011 19:51:33 +0000 Subject: powerpc/mm: Fix the call trace when resumed from hibernation In SMP mode, the kernel would produce call trace when resumed from hibernation. The reason is when the function destroy_context is called to drop the resuming mm context, the mm->context.active is 1 which is wrong and should be zero. We pass the current->active_mm as previous mm context to function switch_mmu_context to decrease the context.active by 1. In UP mode, there is no effect. Signed-off-by: Tang Yuantian Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/swsusp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/swsusp.c b/arch/powerpc/kernel/swsusp.c index aa17b76dd42..641f9adc620 100644 --- a/arch/powerpc/kernel/swsusp.c +++ b/arch/powerpc/kernel/swsusp.c @@ -33,6 +33,6 @@ void save_processor_state(void) void restore_processor_state(void) { #ifdef CONFIG_PPC32 - switch_mmu_context(NULL, current->active_mm); + switch_mmu_context(current->active_mm, current->active_mm); #endif } -- cgit v1.2.3-18-g5258 From 9c740025c51a26ab00192cfc464064d4ccbfe3fc Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 14 Aug 2011 14:30:30 +0000 Subject: powerpc/pseries: Avoid spurious error during hotplug CPU add During hotplug CPU add we get the following error: Unexpected Error (0) returned from configure-connector ibm,configure-connector returns 0 for configuration complete, so catch this and avoid the error. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt Cc: --- arch/powerpc/platforms/pseries/dlpar.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index e9be25bc571..0f1b706506e 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -112,6 +112,7 @@ void dlpar_free_cc_nodes(struct device_node *dn) dlpar_free_one_cc_node(dn); } +#define COMPLETE 0 #define NEXT_SIBLING 1 #define NEXT_CHILD 2 #define NEXT_PROPERTY 3 @@ -158,6 +159,9 @@ struct device_node *dlpar_configure_connector(u32 drc_index) spin_unlock(&rtas_data_buf_lock); switch (rc) { + case COMPLETE: + break; + case NEXT_SIBLING: dn = dlpar_parse_cc_node(ccwa); if (!dn) -- cgit v1.2.3-18-g5258 From cf01a404d96f9bf6f514074579f0a2d355bfa38e Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Thu, 25 Aug 2011 06:07:13 +0000 Subject: powerpc/xics: Add __init to marker icp_native_init() This should fix the following warning: LD arch/powerpc/sysdev/xics/built-in.o WARNING: arch/powerpc/sysdev/xics/built-in.o(.text+0x1310): Section mismatch in reference from the function .icp_native_init() to the function .init.text:.icp_native_init_one_node() The function .icp_native_init() references the function __init .icp_native_init_one_node(). This is often because .icp_native_init lacks a __init annotation or the annotation of .icp_native_init_one_node is wrong. icp_native_init() is only referenced in `arch/powerpc/sysdev/xics/xics-common.c' by xics_init() which is itself marked with __init. = not built-tested = Reported-by: Timur Tabi Signed-off-by: Arnaud Lacombe Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/sysdev/xics/icp-native.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c index 50e32afe392..4c79b6fbee1 100644 --- a/arch/powerpc/sysdev/xics/icp-native.c +++ b/arch/powerpc/sysdev/xics/icp-native.c @@ -276,7 +276,7 @@ static const struct icp_ops icp_native_ops = { #endif }; -int icp_native_init(void) +int __init icp_native_init(void) { struct device_node *np; u32 indx = 0; -- cgit v1.2.3-18-g5258 From 8feaa43494cee5e938fd5a57b9e9bf1c827e6ccd Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 26 Aug 2011 10:36:31 +0000 Subject: powerpc/eeh: Fix /proc/ppc64/eeh creation Since commit 188917e183cf9ad0374b571006d0fc6d48a7f447, /proc/ppc64 is a symlink to /proc/powerpc/. That means that creating /proc/ppc64/eeh will end up with a unaccessible file, that is not listed under /proc/powerpc/ and, then, not listed under /proc/ppc64/. Creating /proc/powerpc/eeh fixes that problem and maintain the compatibility intended with the ppc64 symlink. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Benjamin Herrenschmidt Cc: [3.x] --- arch/powerpc/platforms/pseries/eeh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index ada6e07532e..d42f37d8a44 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -1338,7 +1338,7 @@ static const struct file_operations proc_eeh_operations = { static int __init eeh_init_proc(void) { if (machine_is(pseries)) - proc_create("ppc64/eeh", 0, NULL, &proc_eeh_operations); + proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations); return 0; } __initcall(eeh_init_proc); -- cgit v1.2.3-18-g5258 From c26afe9e8591f306d79aab8071f1d34e4f60b700 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 31 Aug 2011 06:32:26 +0000 Subject: powerpc/ps3: Add gelic udbg driver Add a new udbg driver for the PS3 gelic Ehthernet device. This driver shares only a few stucture and constant definitions with the gelic Ethernet device driver, so is implemented as a stand-alone driver with no dependencies on the gelic Ethernet device driver. Signed-off-by: Hector Martin Signed-off-by: Andre Heider Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig.debug | 8 + arch/powerpc/include/asm/udbg.h | 1 + arch/powerpc/kernel/udbg.c | 2 + arch/powerpc/platforms/ps3/Kconfig | 12 ++ arch/powerpc/platforms/ps3/Makefile | 1 + arch/powerpc/platforms/ps3/gelic_udbg.c | 273 ++++++++++++++++++++++++++++++++ drivers/net/ps3_gelic_net.c | 3 + drivers/net/ps3_gelic_net.h | 6 + 8 files changed, 306 insertions(+) create mode 100644 arch/powerpc/platforms/ps3/gelic_udbg.c diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 067cb848074..ab2335f160a 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -258,6 +258,14 @@ config PPC_EARLY_DEBUG_WSP depends on PPC_WSP select PPC_UDBG_16550 +config PPC_EARLY_DEBUG_PS3GELIC + bool "Early debugging through the PS3 Ethernet port" + depends on PPC_PS3 + select PS3GELIC_UDBG + help + Select this to enable early debugging for the PlayStation3 via + UDP broadcasts sent out through the Ethernet port. + endchoice config PPC_EARLY_DEBUG_HVSI_VTERMNO diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h index 93e05d1b34b..7cf796fa03f 100644 --- a/arch/powerpc/include/asm/udbg.h +++ b/arch/powerpc/include/asm/udbg.h @@ -54,6 +54,7 @@ extern void __init udbg_init_40x_realmode(void); extern void __init udbg_init_cpm(void); extern void __init udbg_init_usbgecko(void); extern void __init udbg_init_wsp(void); +extern void __init udbg_init_ps3gelic(void); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_UDBG_H */ diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index faa82c1f3f6..5b3e98e0315 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -67,6 +67,8 @@ void __init udbg_early_init(void) udbg_init_usbgecko(); #elif defined(CONFIG_PPC_EARLY_DEBUG_WSP) udbg_init_wsp(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_PS3GELIC) + udbg_init_ps3gelic(); #endif #ifdef CONFIG_PPC_EARLY_DEBUG diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index dfe316b161a..476d9d9b240 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -148,4 +148,16 @@ config PS3_LPM profiling support of the Cell processor with programs like oprofile and perfmon2, then say Y or M, otherwise say N. +config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 + help + Enables udbg early debugging output by sending broadcast UDP + via the Ethernet port (UDP port number 18194). + + This driver uses a trivial implementation and is independent + from the main network driver. + + If in doubt, say N here. + endmenu diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index ac1bdf844ec..02b9e636dab 100644 --- a/arch/powerpc/platforms/ps3/Makefile +++ b/arch/powerpc/platforms/ps3/Makefile @@ -2,6 +2,7 @@ obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o obj-y += interrupt.o exports.o os-area.o obj-y += system-bus.o +obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SPU_BASE) += spu.o obj-y += device-init.o diff --git a/arch/powerpc/platforms/ps3/gelic_udbg.c b/arch/powerpc/platforms/ps3/gelic_udbg.c new file mode 100644 index 00000000000..20b46a19a48 --- /dev/null +++ b/arch/powerpc/platforms/ps3/gelic_udbg.c @@ -0,0 +1,273 @@ +/* + * udbg debug output routine via GELIC UDP broadcasts + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * Copyright (C) 2010 Hector Martin + * Copyright (C) 2011 Andre Heider + * + * 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 +#include +#include + +#define GELIC_BUS_ID 1 +#define GELIC_DEVICE_ID 0 +#define GELIC_DEBUG_PORT 18194 +#define GELIC_MAX_MESSAGE_SIZE 1000 + +#define GELIC_LV1_GET_MAC_ADDRESS 1 +#define GELIC_LV1_GET_VLAN_ID 4 +#define GELIC_LV1_VLAN_TX_ETHERNET_0 2 + +#define GELIC_DESCR_DMA_STAT_MASK 0xf0000000 +#define GELIC_DESCR_DMA_CARDOWNED 0xa0000000 + +#define GELIC_DESCR_TX_DMA_IKE 0x00080000 +#define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000 +#define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000 + +#define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \ + GELIC_DESCR_TX_DMA_IKE | \ + GELIC_DESCR_TX_DMA_NO_CHKSUM) + +static u64 bus_addr; + +struct gelic_descr { + /* as defined by the hardware */ + __be32 buf_addr; + __be32 buf_size; + __be32 next_descr_addr; + __be32 dmac_cmd_status; + __be32 result_size; + __be32 valid_size; /* all zeroes for tx */ + __be32 data_status; + __be32 data_error; /* all zeroes for tx */ +} __attribute__((aligned(32))); + +struct debug_block { + struct gelic_descr descr; + u8 pkt[1520]; +} __packed; + +struct ethhdr { + u8 dest[6]; + u8 src[6]; + u16 type; +} __packed; + +struct vlantag { + u16 vlan; + u16 subtype; +} __packed; + +struct iphdr { + u8 ver_len; + u8 dscp_ecn; + u16 total_length; + u16 ident; + u16 frag_off_flags; + u8 ttl; + u8 proto; + u16 checksum; + u32 src; + u32 dest; +} __packed; + +struct udphdr { + u16 src; + u16 dest; + u16 len; + u16 checksum; +} __packed; + +static __iomem struct ethhdr *h_eth; +static __iomem struct vlantag *h_vlan; +static __iomem struct iphdr *h_ip; +static __iomem struct udphdr *h_udp; + +static __iomem char *pmsg; +static __iomem char *pmsgc; + +static __iomem struct debug_block dbg __attribute__((aligned(32))); + +static int header_size; + +static void map_dma_mem(int bus_id, int dev_id, void *start, size_t len, + u64 *real_bus_addr) +{ + s64 result; + u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL; + u64 real_end = real_addr + len; + u64 map_start = real_addr & ~0xfff; + u64 map_end = (real_end + 0xfff) & ~0xfff; + u64 bus_addr = 0; + + u64 flags = 0xf800000000000000UL; + + result = lv1_allocate_device_dma_region(bus_id, dev_id, + map_end - map_start, 12, 0, + &bus_addr); + if (result) + lv1_panic(0); + + result = lv1_map_device_dma_region(bus_id, dev_id, map_start, + bus_addr, map_end - map_start, + flags); + if (result) + lv1_panic(0); + + *real_bus_addr = bus_addr + real_addr - map_start; +} + +static int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len) +{ + s64 result; + u64 real_bus_addr; + + real_bus_addr = bus_addr & ~0xfff; + len += bus_addr - real_bus_addr; + len = (len + 0xfff) & ~0xfff; + + result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr, + len); + if (result) + return result; + + return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr); +} + +static void gelic_debug_init(void) +{ + s64 result; + u64 v2; + u64 mac; + u64 vlan_id; + + result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0); + if (result) + lv1_panic(0); + + map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg), + &bus_addr); + + memset(&dbg, 0, sizeof(dbg)); + + dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt); + + wmb(); + + result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, + GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0, + &mac, &v2); + if (result) + lv1_panic(0); + + mac <<= 16; + + h_eth = (struct ethhdr *)dbg.pkt; + + memset(&h_eth->dest, 0xff, 6); + memcpy(&h_eth->src, &mac, 6); + + header_size = sizeof(struct ethhdr); + + result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, + GELIC_LV1_GET_VLAN_ID, + GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0, + &vlan_id, &v2); + if (!result) { + h_eth->type = 0x8100; + + header_size += sizeof(struct vlantag); + h_vlan = (struct vlantag *)(h_eth + 1); + h_vlan->vlan = vlan_id; + h_vlan->subtype = 0x0800; + h_ip = (struct iphdr *)(h_vlan + 1); + } else { + h_eth->type = 0x0800; + h_ip = (struct iphdr *)(h_eth + 1); + } + + header_size += sizeof(struct iphdr); + h_ip->ver_len = 0x45; + h_ip->ttl = 10; + h_ip->proto = 0x11; + h_ip->src = 0x00000000; + h_ip->dest = 0xffffffff; + + header_size += sizeof(struct udphdr); + h_udp = (struct udphdr *)(h_ip + 1); + h_udp->src = GELIC_DEBUG_PORT; + h_udp->dest = GELIC_DEBUG_PORT; + + pmsgc = pmsg = (char *)(h_udp + 1); +} + +static void gelic_debug_shutdown(void) +{ + if (bus_addr) + unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, + bus_addr, sizeof(dbg)); + lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID); +} + +static void gelic_sendbuf(int msgsize) +{ + u16 *p; + u32 sum; + int i; + + dbg.descr.buf_size = header_size + msgsize; + h_ip->total_length = msgsize + sizeof(struct udphdr) + + sizeof(struct iphdr); + h_udp->len = msgsize + sizeof(struct udphdr); + + h_ip->checksum = 0; + sum = 0; + p = (u16 *)h_ip; + for (i = 0; i < 5; i++) + sum += *p++; + h_ip->checksum = ~(sum + (sum >> 16)); + + dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM | + GELIC_DESCR_TX_DMA_FRAME_TAIL; + dbg.descr.result_size = 0; + dbg.descr.data_status = 0; + + wmb(); + + lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0); + + while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) == + GELIC_DESCR_DMA_CARDOWNED) + cpu_relax(); +} + +static void ps3gelic_udbg_putc(char ch) +{ + *pmsgc++ = ch; + if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) { + gelic_sendbuf(pmsgc-pmsg); + pmsgc = pmsg; + } +} + +void __init udbg_init_ps3gelic(void) +{ + gelic_debug_init(); + udbg_putc = ps3gelic_udbg_putc; +} + +void udbg_shutdown_ps3gelic(void) +{ + udbg_putc = NULL; + gelic_debug_shutdown(); +} +EXPORT_SYMBOL(udbg_shutdown_ps3gelic); diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index d82a82d9870..e743c9418ac 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c @@ -1674,6 +1674,9 @@ static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev) int result; pr_debug("%s: called\n", __func__); + + udbg_shutdown_ps3gelic(); + result = ps3_open_hv_device(dev); if (result) { diff --git a/drivers/net/ps3_gelic_net.h b/drivers/net/ps3_gelic_net.h index d3fadfbc3bc..a93df6ac190 100644 --- a/drivers/net/ps3_gelic_net.h +++ b/drivers/net/ps3_gelic_net.h @@ -359,6 +359,12 @@ static inline void *port_priv(struct gelic_port *port) return port->priv; } +#ifdef CONFIG_PPC_EARLY_DEBUG_PS3GELIC +extern void udbg_shutdown_ps3gelic(void); +#else +static inline void udbg_shutdown_ps3gelic(void) {} +#endif + extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask); /* shared netdev ops */ extern void gelic_card_up(struct gelic_card *card); -- cgit v1.2.3-18-g5258 From a200d8e44649de2cbb39de95f42ad4ef5dc8dc22 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:12 +0000 Subject: powerpc/numa: Enable SD_WAKE_AFFINE in node definition When chasing a performance issue on ppc64, I noticed tasks communicating via a pipe would often end up on different nodes. It turns out SD_WAKE_AFFINE is not set in our node defition. Commit 9fcd18c9e63e (sched: re-tune balancing) enabled SD_WAKE_AFFINE in the node definition for x86 and we need a similar change for ppc64. I used lmbench lat_ctx and perf bench pipe to verify this fix. Each benchmark was run 10 times and the average taken. lmbench lat_ctx: before: 66565 ops/sec after: 204700 ops/sec 3.1x faster perf bench pipe: before: 5.6570 usecs after: 1.3470 usecs 4.2x faster Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/topology.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 7ef0d90defc..6a7e7251cc0 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -73,7 +73,7 @@ static inline int pcibus_to_node(struct pci_bus *bus) | 1*SD_BALANCE_EXEC \ | 1*SD_BALANCE_FORK \ | 0*SD_BALANCE_WAKE \ - | 0*SD_WAKE_AFFINE \ + | 1*SD_WAKE_AFFINE \ | 0*SD_PREFER_LOCAL \ | 0*SD_SHARE_CPUPOWER \ | 0*SD_POWERSAVINGS_BALANCE \ -- cgit v1.2.3-18-g5258 From 590e4d857153c5d4cf86052cdfd42cf9b0779841 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:13 +0000 Subject: sched: Allow SD_NODES_PER_DOMAIN to be overridden We want to override the default value of SD_NODES_PER_DOMAIN on ppc64, so move it into linux/topology.h. Signed-off-by: Anton Blanchard Acked-by: Peter Zijlstra Signed-off-by: Benjamin Herrenschmidt --- include/linux/topology.h | 4 ++++ kernel/sched.c | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/topology.h b/include/linux/topology.h index fc839bfa793..e26db031303 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -201,6 +201,10 @@ int arch_update_cpu_topology(void); .balance_interval = 64, \ } +#ifndef SD_NODES_PER_DOMAIN +#define SD_NODES_PER_DOMAIN 16 +#endif + #ifdef CONFIG_SCHED_BOOK #ifndef SD_BOOK_INIT #error Please define an appropriate SD_BOOK_INIT in include/asm/topology.h!!! diff --git a/kernel/sched.c b/kernel/sched.c index ec5f472bc5b..d258b2d9a66 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -6947,8 +6947,6 @@ static int __init isolated_cpu_setup(char *str) __setup("isolcpus=", isolated_cpu_setup); -#define SD_NODES_PER_DOMAIN 16 - #ifdef CONFIG_NUMA /** -- cgit v1.2.3-18-g5258 From d4761ad2ef18ec2c9a0037d6649c0afc4a7b907d Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:14 +0000 Subject: powerpc/numa: Increase SD_NODES_PER_DOMAIN to 32. The largest POWER7 boxes have 32 nodes. SD_NODES_PER_DOMAIN groups nodes into chunks of 16 and adds a global balancing domain (SD_ALLNODES) above it. If we bump SD_NODES_PER_DOMAIN to 32, then we avoid this extra level of balancing on our largest boxes. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/topology.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 6a7e7251cc0..d1c1d312f80 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -28,6 +28,12 @@ struct device_node; */ #define RECLAIM_DISTANCE 10 +/* + * Avoid creating an extra level of balancing (SD_ALLNODES) on the largest + * POWER7 boxes which have a maximum of 32 nodes. + */ +#define SD_NODES_PER_DOMAIN 32 + #include static inline int cpu_to_node(int cpu) -- cgit v1.2.3-18-g5258 From 7bebcf0925f09224393a8992af706fa39aa10395 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:15 +0000 Subject: powerpc/numa: Disable NEWIDLE balancing at node level On big POWER7 boxes we see large amounts of CPU time in system processes like workqueue and watchdog kernel threads. We currently rebalance the entire machine each time a task goes idle and this is very expensive on large machines. Disable newidle balancing at the node level and rely on the scheduler tick to rebalance across nodes. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/topology.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index d1c1d312f80..13efa4d95e7 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -75,7 +75,7 @@ static inline int pcibus_to_node(struct pci_bus *bus) .forkexec_idx = 0, \ \ .flags = 1*SD_LOAD_BALANCE \ - | 1*SD_BALANCE_NEWIDLE \ + | 0*SD_BALANCE_NEWIDLE \ | 1*SD_BALANCE_EXEC \ | 1*SD_BALANCE_FORK \ | 0*SD_BALANCE_WAKE \ -- cgit v1.2.3-18-g5258 From e377bc5d49fdbcb5f0e559b644d806a15454d407 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:16 +0000 Subject: powerpc/numa: Remove duplicate RECLAIM_DISTANCE definition We have two identical definitions of RECLAIM_DISTANCE, looks like the patch got applied twice. Remove one. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/topology.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 13efa4d95e7..1e104af0848 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -18,16 +18,6 @@ struct device_node; */ #define RECLAIM_DISTANCE 10 -/* - * Before going off node we want the VM to try and reclaim from the local - * node. It does this if the remote distance is larger than RECLAIM_DISTANCE. - * With the default REMOTE_DISTANCE of 20 and the default RECLAIM_DISTANCE of - * 20, we never reclaim and go off node straight away. - * - * To fix this we choose a smaller value of RECLAIM_DISTANCE. - */ -#define RECLAIM_DISTANCE 10 - /* * Avoid creating an extra level of balancing (SD_ALLNODES) on the largest * POWER7 boxes which have a maximum of 32 nodes. -- cgit v1.2.3-18-g5258 From 6083184269fd723affca4f6340e491950267622a Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 10 Aug 2011 20:44:21 +0000 Subject: powerpc/numa: Remove double of_node_put in hot_add_node_scn_to_nid During memory hotplug testing, I got the following warning: ERROR: Bad of_node_put() on /memory@0 of_node_release kref_put of_node_put of_find_node_by_type hot_add_node_scn_to_nid hot_add_scn_to_nid memory_add_physaddr_to_nid ... of_find_node_by_type() loop does the of_node_put for us so we only need the handle the case where we terminate the loop early. As suggested by Stephen Rothwell we can do the of_node_put unconditionally outside of the loop since of_node_put handles a NULL argument fine. Signed-off-by: Anton Blanchard Cc: stable@kernel.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/numa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 2164006fe17..2c1ae7a5fb5 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1214,11 +1214,12 @@ int hot_add_node_scn_to_nid(unsigned long scn_addr) break; } - of_node_put(memory); if (nid >= 0) break; } + of_node_put(memory); + return nid; } -- cgit v1.2.3-18-g5258 From 94db7c5e14f44b943febe54e089d077cd983d284 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 10 Aug 2011 20:44:22 +0000 Subject: powerpc: Use for_each_node_by_type instead of open coding it Use for_each_node_by_type instead of open coding it. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/machine_kexec_64.c | 3 +-- arch/powerpc/kernel/setup_64.c | 2 +- arch/powerpc/mm/numa.c | 10 +++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index 583af70c4b1..26ccbf77dd4 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -74,8 +74,7 @@ int default_machine_kexec_prepare(struct kimage *image) } /* We also should not overwrite the tce tables */ - for (node = of_find_node_by_type(NULL, "pci"); node != NULL; - node = of_find_node_by_type(node, "pci")) { + for_each_node_by_type(node, "pci") { basep = of_get_property(node, "linux,tce-base", NULL); sizep = of_get_property(node, "linux,tce-size", NULL); if (basep == NULL || sizep == NULL) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index aebef1320ed..eade1fd8ee2 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -278,7 +278,7 @@ static void __init initialize_cache_info(void) DBG(" -> initialize_cache_info()\n"); - for (np = NULL; (np = of_find_node_by_type(np, "cpu"));) { + for_each_node_by_type(np, "cpu") { num_cpus += 1; /* We're assuming *all* of the CPUs have the same diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 2c1ae7a5fb5..00cc090cd1d 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -710,7 +710,7 @@ static void __init parse_drconf_memory(struct device_node *memory) static int __init parse_numa_properties(void) { struct device_node *cpu = NULL; - struct device_node *memory = NULL; + struct device_node *memory; int default_nid = 0; unsigned long i; @@ -750,8 +750,8 @@ static int __init parse_numa_properties(void) } get_n_mem_cells(&n_mem_addr_cells, &n_mem_size_cells); - memory = NULL; - while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { + + for_each_node_by_type(memory, "memory") { unsigned long start; unsigned long size; int nid; @@ -1187,10 +1187,10 @@ static int hot_add_drconf_scn_to_nid(struct device_node *memory, */ int hot_add_node_scn_to_nid(unsigned long scn_addr) { - struct device_node *memory = NULL; + struct device_node *memory; int nid = -1; - while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { + for_each_node_by_type(memory, "memory") { unsigned long start, size; int ranges; const unsigned int *memcell_buf; -- cgit v1.2.3-18-g5258 From dfbe93a222e74b6f96ad84eff2b04a0f864fac65 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 10 Aug 2011 20:44:23 +0000 Subject: powerpc: Coding style cleanups While converting code to use for_each_node_by_type I noticed a number of coding style issues. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/setup_64.c | 20 +++++++++++++------- arch/powerpc/mm/numa.c | 7 ++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index eade1fd8ee2..d4168c93098 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -281,11 +281,11 @@ static void __init initialize_cache_info(void) for_each_node_by_type(np, "cpu") { num_cpus += 1; - /* We're assuming *all* of the CPUs have the same + /* + * We're assuming *all* of the CPUs have the same * d-cache and i-cache sizes... -Peter */ - - if ( num_cpus == 1 ) { + if (num_cpus == 1) { const u32 *sizep, *lsizep; u32 size, lsize; @@ -294,10 +294,13 @@ static void __init initialize_cache_info(void) sizep = of_get_property(np, "d-cache-size", NULL); if (sizep != NULL) size = *sizep; - lsizep = of_get_property(np, "d-cache-block-size", NULL); + lsizep = of_get_property(np, "d-cache-block-size", + NULL); /* fallback if block size missing */ if (lsizep == NULL) - lsizep = of_get_property(np, "d-cache-line-size", NULL); + lsizep = of_get_property(np, + "d-cache-line-size", + NULL); if (lsizep != NULL) lsize = *lsizep; if (sizep == 0 || lsizep == 0) @@ -314,9 +317,12 @@ static void __init initialize_cache_info(void) sizep = of_get_property(np, "i-cache-size", NULL); if (sizep != NULL) size = *sizep; - lsizep = of_get_property(np, "i-cache-block-size", NULL); + lsizep = of_get_property(np, "i-cache-block-size", + NULL); if (lsizep == NULL) - lsizep = of_get_property(np, "i-cache-line-size", NULL); + lsizep = of_get_property(np, + "i-cache-line-size", + NULL); if (lsizep != NULL) lsize = *lsizep; if (sizep == 0 || lsizep == 0) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 00cc090cd1d..0bfb90c5057 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -709,7 +709,6 @@ static void __init parse_drconf_memory(struct device_node *memory) static int __init parse_numa_properties(void) { - struct device_node *cpu = NULL; struct device_node *memory; int default_nid = 0; unsigned long i; @@ -732,6 +731,7 @@ static int __init parse_numa_properties(void) * each node to be onlined must have NODE_DATA etc backing it. */ for_each_present_cpu(i) { + struct device_node *cpu; int nid; cpu = of_get_cpu_node(i, NULL); @@ -800,8 +800,9 @@ new_range: } /* - * Now do the same thing for each MEMBLOCK listed in the ibm,dynamic-memory - * property in the ibm,dynamic-reconfiguration-memory node. + * Now do the same thing for each MEMBLOCK listed in the + * ibm,dynamic-memory property in the + * ibm,dynamic-reconfiguration-memory node. */ memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); if (memory) -- cgit v1.2.3-18-g5258 From a11940978bd598e65996b4f807cf4904793f7025 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 10 Aug 2011 20:44:24 +0000 Subject: powerpc: Fix oops when echoing bad values to /sys/devices/system/memory/probe If we echo an address the hypervisor doesn't like to /sys/devices/system/memory/probe we oops the box: # echo 0x10000000000 > /sys/devices/system/memory/probe kernel BUG at arch/powerpc/mm/hash_utils_64.c:541! The backtrace is: create_section_mapping arch_add_memory add_memory memory_probe_store sysdev_class_store sysfs_write_file vfs_write SyS_write In create_section_mapping we BUG if htab_bolt_mapping returned an error. A better approach is to return an error which will propagate back to userspace. Rerunning the test with this patch applied: # echo 0x10000000000 > /sys/devices/system/memory/probe -bash: echo: write error: Invalid argument Signed-off-by: Anton Blanchard Cc: stable@kernel.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/sparsemem.h | 2 +- arch/powerpc/mm/hash_utils_64.c | 6 +++--- arch/powerpc/mm/mem.c | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/sparsemem.h b/arch/powerpc/include/asm/sparsemem.h index 54a47ea2c3a..0c5fa314561 100644 --- a/arch/powerpc/include/asm/sparsemem.h +++ b/arch/powerpc/include/asm/sparsemem.h @@ -16,7 +16,7 @@ #endif /* CONFIG_SPARSEMEM */ #ifdef CONFIG_MEMORY_HOTPLUG -extern void create_section_mapping(unsigned long start, unsigned long end); +extern int create_section_mapping(unsigned long start, unsigned long end); extern int remove_section_mapping(unsigned long start, unsigned long end); #ifdef CONFIG_NUMA extern int hot_add_scn_to_nid(unsigned long scn_addr); diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 1f8b2a05e3d..1628201c8ce 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -531,11 +531,11 @@ static unsigned long __init htab_get_table_size(void) } #ifdef CONFIG_MEMORY_HOTPLUG -void create_section_mapping(unsigned long start, unsigned long end) +int create_section_mapping(unsigned long start, unsigned long end) { - BUG_ON(htab_bolt_mapping(start, end, __pa(start), + return htab_bolt_mapping(start, end, __pa(start), pgprot_val(PAGE_KERNEL), mmu_linear_psize, - mmu_kernel_ssize)); + mmu_kernel_ssize); } int remove_section_mapping(unsigned long start, unsigned long end) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index ad9cf49dfb8..5db316cad47 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -123,7 +123,8 @@ int arch_add_memory(int nid, u64 start, u64 size) pgdata = NODE_DATA(nid); start = (unsigned long)__va(start); - create_section_mapping(start, start + size); + if (create_section_mapping(start, start + size)) + return -EINVAL; /* this should work for most non-highmem platforms */ zone = pgdata->node_zones; -- cgit v1.2.3-18-g5258 From 8bdafa39a47265bc029838b35cc6585f69224afa Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 14 Sep 2011 09:43:15 +0000 Subject: powerpc: Fix deadlock in icswx code The icswx code introduced an A-B B-A deadlock: CPU0 CPU1 ---- ---- lock(&anon_vma->mutex); lock(&mm->mmap_sem); lock(&anon_vma->mutex); lock(&mm->mmap_sem); Instead of using the mmap_sem to keep mm_users constant, take the page table spinlock. Signed-off-by: Anton Blanchard Cc: Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/mmu_context_hash64.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/mm/mmu_context_hash64.c b/arch/powerpc/mm/mmu_context_hash64.c index 3bafc3deca6..4ff587e3825 100644 --- a/arch/powerpc/mm/mmu_context_hash64.c +++ b/arch/powerpc/mm/mmu_context_hash64.c @@ -136,8 +136,8 @@ int use_cop(unsigned long acop, struct mm_struct *mm) if (!mm || !acop) return -EINVAL; - /* We need to make sure mm_users doesn't change */ - down_read(&mm->mmap_sem); + /* The page_table_lock ensures mm_users won't change under us */ + spin_lock(&mm->page_table_lock); spin_lock(mm->context.cop_lockp); if (mm->context.cop_pid == COP_PID_NONE) { @@ -164,7 +164,7 @@ int use_cop(unsigned long acop, struct mm_struct *mm) out: spin_unlock(mm->context.cop_lockp); - up_read(&mm->mmap_sem); + spin_unlock(&mm->page_table_lock); return ret; } @@ -185,8 +185,8 @@ void drop_cop(unsigned long acop, struct mm_struct *mm) if (WARN_ON_ONCE(!mm)) return; - /* We need to make sure mm_users doesn't change */ - down_read(&mm->mmap_sem); + /* The page_table_lock ensures mm_users won't change under us */ + spin_lock(&mm->page_table_lock); spin_lock(mm->context.cop_lockp); mm->context.acop &= ~acop; @@ -213,7 +213,7 @@ void drop_cop(unsigned long acop, struct mm_struct *mm) } spin_unlock(mm->context.cop_lockp); - up_read(&mm->mmap_sem); + spin_unlock(&mm->page_table_lock); } EXPORT_SYMBOL_GPL(drop_cop); -- cgit v1.2.3-18-g5258 From ac07a4a57f7408922a0b3d4dcb87104fe8a3d8ca Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 13 Sep 2011 11:22:51 +0000 Subject: hvcs: Ensure page aligned partner info buffer The Power platform requires the partner info buffer to be page aligned otherwise it will fail the partner info hcall with H_PARAMETER. Switch from using kmalloc to allocate this buffer to __get_free_page to ensure page alignment. Signed-off-by: Brian King Signed-off-by: Benjamin Herrenschmidt --- drivers/tty/hvc/hvcs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 4c8b6654693..39291665eca 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1532,7 +1532,7 @@ static int __devinit hvcs_initialize(void) goto register_fail; } - hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL); + hvcs_pi_buff = (unsigned long *) __get_free_page(GFP_KERNEL); if (!hvcs_pi_buff) { rc = -ENOMEM; goto buff_alloc_fail; @@ -1548,7 +1548,7 @@ static int __devinit hvcs_initialize(void) return 0; kthread_fail: - kfree(hvcs_pi_buff); + free_page((unsigned long)hvcs_pi_buff); buff_alloc_fail: tty_unregister_driver(hvcs_tty_driver); register_fail: @@ -1597,7 +1597,7 @@ static void __exit hvcs_module_exit(void) kthread_stop(hvcs_task); spin_lock(&hvcs_pi_lock); - kfree(hvcs_pi_buff); + free_page((unsigned long)hvcs_pi_buff); hvcs_pi_buff = NULL; spin_unlock(&hvcs_pi_lock); -- cgit v1.2.3-18-g5258 From b8bb922c680b7e21af2268bc8eec93b17f5b9ab8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:48 +0000 Subject: powerpc/udbg: Fix Kconfig entry for avoiding 44x early debug with KVM It was preventing the global early debug selection whenever KVM was enabled instead of only preventing the 440 specific one. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig.debug | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index ab2335f160a..06eb62b0fd9 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -141,9 +141,6 @@ config BOOTX_TEXT config PPC_EARLY_DEBUG bool "Early debugging (dangerous)" - # PPC_EARLY_DEBUG on 440 leaves AS=1 mappings above the TLB high water - # mark, which doesn't work with current 440 KVM. - depends on !KVM help Say Y to enable some early debugging facilities that may be available for your processor/board combination. Those facilities are hacks @@ -222,7 +219,9 @@ config PPC_EARLY_DEBUG_BEAT config PPC_EARLY_DEBUG_44x bool "Early serial debugging for IBM/AMCC 44x CPUs" - depends on 44x + # PPC_EARLY_DEBUG on 440 leaves AS=1 mappings above the TLB high water + # mark, which doesn't work with current 440 KVM. + depends on 44x && !KVM help Select this to enable early debugging for IBM 44x chips via the inbuilt serial port. If you enable this, ensure you set -- cgit v1.2.3-18-g5258 From fb82b83970a32263698e54a8779d2ce88cd3b060 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:49 +0000 Subject: powerpc/smp: More generic support for "soft hotplug" This adds more generic support for doing CPU hotplug with a simple idle loop and no actual reset of the processors. The generic smp_generic_kick_cpu() does the hotplug bringup trick if the PACA shows that the CPU has already been started at boot and we provide an accessor for the CPU state. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/smp.h | 1 + arch/powerpc/kernel/smp.c | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h index 15a70b7f638..adba970ce91 100644 --- a/arch/powerpc/include/asm/smp.h +++ b/arch/powerpc/include/asm/smp.h @@ -65,6 +65,7 @@ int generic_cpu_disable(void); void generic_cpu_die(unsigned int cpu); void generic_mach_cpu_die(void); void generic_set_cpu_dead(unsigned int cpu); +int generic_check_cpu_restart(unsigned int cpu); #endif #ifdef CONFIG_PPC64 diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 7bf2187dfd9..af7e7722eca 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -70,6 +70,10 @@ static DEFINE_PER_CPU(struct task_struct *, idle_thread_array); #define get_idle_for_cpu(x) (per_cpu(idle_thread_array, x)) #define set_idle_for_cpu(x, p) (per_cpu(idle_thread_array, x) = (p)) + +/* State of each CPU during hotplug phases */ +static DEFINE_PER_CPU(int, cpu_state) = { 0 }; + #else static struct task_struct *idle_thread_array[NR_CPUS] __cpuinitdata ; #define get_idle_for_cpu(x) (idle_thread_array[(x)]) @@ -104,12 +108,25 @@ int __devinit smp_generic_kick_cpu(int nr) * 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; - smp_mb(); + if (!paca[nr].cpu_start) { + paca[nr].cpu_start = 1; + smp_mb(); + return 0; + } + +#ifdef CONFIG_HOTPLUG_CPU + /* + * Ok it's not there, so it might be soft-unplugged, let's + * try to bring it back + */ + per_cpu(cpu_state, nr) = CPU_UP_PREPARE; + smp_wmb(); + smp_send_reschedule(nr); +#endif /* CONFIG_HOTPLUG_CPU */ return 0; } -#endif +#endif /* CONFIG_PPC64 */ static irqreturn_t call_function_action(int irq, void *data) { @@ -357,8 +374,6 @@ void __devinit smp_prepare_boot_cpu(void) } #ifdef CONFIG_HOTPLUG_CPU -/* State of each CPU during hotplug phases */ -static DEFINE_PER_CPU(int, cpu_state) = { 0 }; int generic_cpu_disable(void) { @@ -406,6 +421,11 @@ void generic_set_cpu_dead(unsigned int cpu) { per_cpu(cpu_state, cpu) = CPU_DEAD; } + +int generic_check_cpu_restart(unsigned int cpu) +{ + return per_cpu(cpu_state, cpu) == CPU_UP_PREPARE; +} #endif struct create_idle { -- cgit v1.2.3-18-g5258 From 781fb7a3e4cdca28236ae23e2c77070ed3ae531f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:50 +0000 Subject: powerpc/pci: Call pcie_bus_configure_settings() This new function is used to properly setup the PCI Express Max Payload Size (and in some circumstances Max Read Request Size). Some systems will not operate properly if these aren't set correctly and the firmware doesn't always do it. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/pci-common.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 32656f10525..1bd47f36b25 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1730,6 +1730,17 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) if (mode == PCI_PROBE_NORMAL) hose->last_busno = bus->subordinate = pci_scan_child_bus(bus); + + /* Configure PCI Express settings */ + if (bus) { + struct pci_bus *child; + list_for_each_entry(child, &bus->children, node) { + struct pci_dev *self = child->self; + if (!self) + continue; + pcie_bus_configure_settings(child, self->pcie_mpss); + } + } } static void fixup_hide_host_resource_fsl(struct pci_dev *dev) -- cgit v1.2.3-18-g5258 From e550592e689cf8d682937f356497f989f3d88292 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:51 +0000 Subject: powerpc/powernv: Don't clobber r9 in relative_toc() With OPAL, r8 and r9 will be used to pass the OPAL base and entry for debugging purposes (those informations are also in the device-tree). We don't want to clobber those registers that early. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/head_64.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 3564c49c683..e708abe576d 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -674,9 +674,9 @@ _GLOBAL(enable_64b_mode) _GLOBAL(relative_toc) mflr r0 bcl 20,31,$+4 -0: mflr r9 - ld r2,(p_toc - 0b)(r9) - add r2,r2,r9 +0: mflr r11 + ld r2,(p_toc - 0b)(r11) + add r2,r2,r11 mtlr r0 blr -- cgit v1.2.3-18-g5258 From 55190f88789ab62a42c3ee050090406b0bcefff8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:52 +0000 Subject: powerpc: Add skeleton PowerNV platform This adds a skeletton for the new Power "Non Virtualized" platform which will be used by machines supporting running without an hypervisor, for example in order to run KVM. These machines will be using a new firmware called OPAL for which the support will be provided by later patches. The PowerNV platform is intended to be also usable under the BML environment used internally for early CPU bringup which is why the code also supports using RTAS instead of OPAL in various places. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/boot/Makefile | 1 + arch/powerpc/platforms/Kconfig | 1 + arch/powerpc/platforms/Makefile | 1 + arch/powerpc/platforms/powernv/Kconfig | 11 +++ arch/powerpc/platforms/powernv/Makefile | 2 + arch/powerpc/platforms/powernv/powernv.h | 10 ++ arch/powerpc/platforms/powernv/setup.c | 153 +++++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/smp.c | 83 +++++++++++++++++ 8 files changed, 262 insertions(+) create mode 100644 arch/powerpc/platforms/powernv/Kconfig create mode 100644 arch/powerpc/platforms/powernv/Makefile create mode 100644 arch/powerpc/platforms/powernv/powernv.h create mode 100644 arch/powerpc/platforms/powernv/setup.c create mode 100644 arch/powerpc/platforms/powernv/smp.c diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index ac6705e7571..72ee8c1fba4 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -171,6 +171,7 @@ quiet_cmd_wrap = WRAP $@ $(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux image-$(CONFIG_PPC_PSERIES) += zImage.pseries +image-$(CONFIG_PPC_POWERNV) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.maple image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries image-$(CONFIG_PPC_PS3) += dtbImage.ps3 diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index b9ba86191ae..6de27d27572 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -1,5 +1,6 @@ menu "Platform support" +source "arch/powerpc/platforms/powernv/Kconfig" source "arch/powerpc/platforms/pseries/Kconfig" source "arch/powerpc/platforms/iseries/Kconfig" source "arch/powerpc/platforms/chrp/Kconfig" diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 73e2116cfee..2635a22bade 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_PPC_82xx) += 82xx/ obj-$(CONFIG_PPC_83xx) += 83xx/ obj-$(CONFIG_FSL_SOC_BOOKE) += 85xx/ obj-$(CONFIG_PPC_86xx) += 86xx/ +obj-$(CONFIG_PPC_POWERNV) += powernv/ obj-$(CONFIG_PPC_PSERIES) += pseries/ obj-$(CONFIG_PPC_ISERIES) += iseries/ obj-$(CONFIG_PPC_MAPLE) += maple/ diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig new file mode 100644 index 00000000000..24700e8df19 --- /dev/null +++ b/arch/powerpc/platforms/powernv/Kconfig @@ -0,0 +1,11 @@ +config PPC_POWERNV + depends on PPC64 && PPC_BOOK3S + bool "IBM PowerNV (Non-Virtualized) platform support" + select PPC_RTAS + select PPC_NATIVE + select PPC_XICS + select PPC_ICP_NATIVE + select PPC_ICS_RTAS + select PPC_P7_NAP + select PPC_PCI_CHOICE if EMBEDDED + default y diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile new file mode 100644 index 00000000000..1c4325056b8 --- /dev/null +++ b/arch/powerpc/platforms/powernv/Makefile @@ -0,0 +1,2 @@ +obj-y += setup.o +obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h new file mode 100644 index 00000000000..35b716008cd --- /dev/null +++ b/arch/powerpc/platforms/powernv/powernv.h @@ -0,0 +1,10 @@ +#ifndef _POWERNV_H +#define _POWERNV_H + +#ifdef CONFIG_SMP +extern void pnv_smp_init(void); +#else +static inline void pnv_smp_init(void) { } +#endif + +#endif /* _POWERNV_H */ diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c new file mode 100644 index 00000000000..569f9cc4eb0 --- /dev/null +++ b/arch/powerpc/platforms/powernv/setup.c @@ -0,0 +1,153 @@ +/* + * PowerNV setup code. + * + * Copyright 2011 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. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "powernv.h" + +static void __init pnv_setup_arch(void) +{ + /* Force console to hvc for now until we have sorted out the + * real console situation for the platform. This will make + * hvc_udbg work at least. + */ + add_preferred_console("hvc", 0, NULL); + + /* Initialize SMP */ + pnv_smp_init(); + + /* XXX PCI */ + + /* XXX NVRAM */ + + /* Enable NAP mode */ + powersave_nap = 1; + + /* XXX PMCS */ +} + +static void __init pnv_init_early(void) +{ + /* XXX IOMMU */ +} + +static void __init pnv_init_IRQ(void) +{ + xics_init(); + + WARN_ON(!ppc_md.get_irq); +} + +static void pnv_show_cpuinfo(struct seq_file *m) +{ + struct device_node *root; + const char *model = ""; + + root = of_find_node_by_path("/"); + if (root) + model = of_get_property(root, "model", NULL); + seq_printf(m, "machine\t\t: PowerNV %s\n", model); + of_node_put(root); +} + +static void pnv_restart(char *cmd) +{ + for (;;); +} + +static void pnv_power_off(void) +{ + for (;;); +} + +static void pnv_halt(void) +{ + for (;;); +} + +static unsigned long __init pnv_get_boot_time(void) +{ + return 0; +} + +static void pnv_get_rtc_time(struct rtc_time *rtc_tm) +{ +} + +static int pnv_set_rtc_time(struct rtc_time *tm) +{ + return 0; +} + +static void pnv_progress(char *s, unsigned short hex) +{ +} + +#ifdef CONFIG_KEXEC +static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) +{ + xics_kexec_teardown_cpu(secondary); +} +#endif /* CONFIG_KEXEC */ + +static int __init pnv_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ibm,powernv")) + return 0; + + hpte_init_native(); + + pr_debug("PowerNV detected !\n"); + + return 1; +} + +define_machine(powernv) { + .name = "PowerNV", + .probe = pnv_probe, + .setup_arch = pnv_setup_arch, + .init_early = pnv_init_early, + .init_IRQ = pnv_init_IRQ, + .show_cpuinfo = pnv_show_cpuinfo, + .restart = pnv_restart, + .power_off = pnv_power_off, + .halt = pnv_halt, + .get_boot_time = pnv_get_boot_time, + .get_rtc_time = pnv_get_rtc_time, + .set_rtc_time = pnv_set_rtc_time, + .progress = pnv_progress, + .power_save = power7_idle, + .calibrate_decr = generic_calibrate_decr, +#ifdef CONFIG_KEXEC + .kexec_cpu_down = pnv_kexec_cpu_down, +#endif +}; diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c new file mode 100644 index 00000000000..36c715102c5 --- /dev/null +++ b/arch/powerpc/platforms/powernv/smp.c @@ -0,0 +1,83 @@ +/* + * SMP support for PowerNV machines. + * + * Copyright 2011 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "powernv.h" + +static void __devinit pnv_smp_setup_cpu(int cpu) +{ + if (cpu != boot_cpuid) + xics_setup_cpu(); +} + +static int pnv_smp_cpu_bootable(unsigned int nr) +{ + /* Special case - we inhibit secondary thread startup + * during boot if the user requests it. + */ + if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) { + if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0) + return 0; + if (smt_enabled_at_boot + && cpu_thread_in_core(nr) >= smt_enabled_at_boot) + return 0; + } + + return 1; +} + +static struct smp_ops_t pnv_smp_ops = { + .message_pass = smp_muxed_ipi_message_pass, + .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ + .probe = xics_smp_probe, + .kick_cpu = smp_generic_kick_cpu, + .setup_cpu = pnv_smp_setup_cpu, + .cpu_bootable = pnv_smp_cpu_bootable, +}; + +/* This is called very early during platform setup_arch */ +void __init pnv_smp_init(void) +{ + smp_ops = &pnv_smp_ops; + + /* XXX We don't yet have a proper entry point from HAL, for + * now we rely on kexec-style entry from BML + */ + +#ifdef CONFIG_PPC_RTAS + /* Non-lpar has additional take/give timebase */ + if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { + smp_ops->give_timebase = rtas_give_timebase; + smp_ops->take_timebase = rtas_take_timebase; + } +#endif /* CONFIG_PPC_RTAS */ +} -- cgit v1.2.3-18-g5258 From 78b782cb788cadbda151ecb61753c109602a250c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 18:50:15 +0000 Subject: of: Change logic to overwrite cmd_line with CONFIG_CMDLINE We used to overwrite with CONFIG_CMDLINE if we found a chosen node but failed to get bootargs out of it or they were empty, unless CONFIG_CMDLINE_FORCE is set. Instead change that to overwrite if "data" is non empty after the bootargs check. It allows arch code to have other mechanisms to retrieve the command line prior to parsing the device-tree. Note: CONFIG_CMDLINE_FORCE case should ideally be handled elsewhere as it won't work as it-is if the device-tree has no /chosen node Signed-off-by: Benjamin Herrenschmidt CC: devicetree-discuss@lists-ozlabs.org CC: Grant Likely Acked-by: Grant Likely --- drivers/of/fdt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 65200af29c5..4af69a3564c 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -681,9 +681,14 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, if (p != NULL && l > 0) strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); + /* + * CONFIG_CMDLINE is meant to be a default in case nothing else + * managed to set the command line, unless CONFIG_CMDLINE_FORCE + * is set in which case we override whatever was found earlier. + */ #ifdef CONFIG_CMDLINE #ifndef CONFIG_CMDLINE_FORCE - if (p == NULL || l == 0 || (l == 1 && (*p) == 0)) + if (!((char *)data)[0]) #endif strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif /* CONFIG_CMDLINE */ -- cgit v1.2.3-18-g5258 From 344eb010b2e399069bac474a9fd0ba04908a2601 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:54 +0000 Subject: powerpc/powernv: Add CPU hotplug support Unplugged CPU go into NAP mode in a loop until woken up Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 2 +- arch/powerpc/platforms/powernv/smp.c | 78 +++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 62711421cd6..8523bd1b8d7 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -323,7 +323,7 @@ config SWIOTLB config HOTPLUG_CPU bool "Support for enabling/disabling CPUs" - depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC) + depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC || PPC_POWERNV) ---help--- Say Y here to be able to disable and re-enable individual CPUs at runtime on SMP machines. diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 36c715102c5..4f4ec3797eb 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -33,7 +33,14 @@ #include "powernv.h" -static void __devinit pnv_smp_setup_cpu(int cpu) +#ifdef DEBUG +#include +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +static void __cpuinit pnv_smp_setup_cpu(int cpu) { if (cpu != boot_cpuid) xics_setup_cpu(); @@ -55,6 +62,67 @@ static int pnv_smp_cpu_bootable(unsigned int nr) return 1; } +#ifdef CONFIG_HOTPLUG_CPU + +static int pnv_smp_cpu_disable(void) +{ + int cpu = smp_processor_id(); + + /* This is identical to pSeries... might consolidate by + * moving migrate_irqs_away to a ppc_md with default to + * the generic fixup_irqs. --BenH. + */ + set_cpu_online(cpu, false); + vdso_data->processorCount--; + if (cpu == boot_cpuid) + boot_cpuid = cpumask_any(cpu_online_mask); + xics_migrate_irqs_away(); + return 0; +} + +static void pnv_smp_cpu_kill_self(void) +{ + unsigned int cpu; + + /* If powersave_nap is enabled, use NAP mode, else just + * spin aimlessly + */ + if (!powersave_nap) { + generic_mach_cpu_die(); + return; + } + + /* Standard hot unplug procedure */ + local_irq_disable(); + idle_task_exit(); + current->active_mm = NULL; /* for sanity */ + cpu = smp_processor_id(); + DBG("CPU%d offline\n", cpu); + generic_set_cpu_dead(cpu); + smp_wmb(); + + /* We don't want to take decrementer interrupts while we are offline, + * so clear LPCR:PECE1. We keep PECE2 enabled. + */ + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); + while (!generic_check_cpu_restart(cpu)) { + power7_idle(); + if (!generic_check_cpu_restart(cpu)) { + DBG("CPU%d Unexpected exit while offline !\n", cpu); + /* We may be getting an IPI, so we re-enable + * interrupts to process it, it will be ignored + * since we aren't online (hopefully) + */ + local_irq_enable(); + local_irq_disable(); + } + } + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); + DBG("CPU%d coming online...\n", cpu); +} + +#endif /* CONFIG_HOTPLUG_CPU */ + static struct smp_ops_t pnv_smp_ops = { .message_pass = smp_muxed_ipi_message_pass, .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ @@ -62,6 +130,10 @@ static struct smp_ops_t pnv_smp_ops = { .kick_cpu = smp_generic_kick_cpu, .setup_cpu = pnv_smp_setup_cpu, .cpu_bootable = pnv_smp_cpu_bootable, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_disable = pnv_smp_cpu_disable, + .cpu_die = generic_cpu_die, +#endif /* CONFIG_HOTPLUG_CPU */ }; /* This is called very early during platform setup_arch */ @@ -80,4 +152,8 @@ void __init pnv_smp_init(void) smp_ops->take_timebase = rtas_take_timebase; } #endif /* CONFIG_PPC_RTAS */ + +#ifdef CONFIG_HOTPLUG_CPU + ppc_md.cpu_die = pnv_smp_cpu_kill_self; +#endif } -- cgit v1.2.3-18-g5258 From 27f4488872d9ef2a4b9aa2be58fb0789d6c0ba84 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 18:27:58 +0000 Subject: powerpc/powernv: Add OPAL takeover from PowerVM On machines supporting the OPAL firmware version 1, the system is initially booted under pHyp. We then use a special hypercall to verify if OPAL is available and if it is, we then trigger a "takeover" which disables pHyp and loads the OPAL runtime firmware, giving control to the kernel in hypervisor mode. This patch add the necessary code to detect that the OPAL takeover capability is present when running under PowerVM (aka pHyp) and perform said takeover to get hypervisor control of the processor. To perform the takeover, we must first use RTAS (within Open Firmware runtime environment) to start all processors & threads, in order to give control to OPAL on all of them. We then call the takeover hypercall on everybody, OPAL will re-enter the kernel main entry point passing it a flat device-tree. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/opal.h | 50 ++++++ arch/powerpc/kernel/head_64.S | 4 + arch/powerpc/kernel/prom_init.c | 239 +++++++++++++++++++++++-- arch/powerpc/kernel/prom_init_check.sh | 3 +- arch/powerpc/platforms/powernv/Makefile | 2 +- arch/powerpc/platforms/powernv/opal-takeover.S | 140 +++++++++++++++ 6 files changed, 419 insertions(+), 19 deletions(-) create mode 100644 arch/powerpc/include/asm/opal.h create mode 100644 arch/powerpc/platforms/powernv/opal-takeover.S diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h new file mode 100644 index 00000000000..ecdb283f8b7 --- /dev/null +++ b/arch/powerpc/include/asm/opal.h @@ -0,0 +1,50 @@ +/* + * PowerNV OPAL definitions. + * + * Copyright 2011 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. + */ + +#ifndef __OPAL_H +#define __OPAL_H + +/****** Takeover interface ********/ + +/* PAPR H-Call used to querty the HAL existence and/or instanciate + * it from within pHyp (tech preview only). + * + * This is exclusively used in prom_init.c + */ + +#ifndef __ASSEMBLY__ + +struct opal_takeover_args { + u64 k_image; /* r4 */ + u64 k_size; /* r5 */ + u64 k_entry; /* r6 */ + u64 k_entry2; /* r7 */ + u64 hal_addr; /* r8 */ + u64 rd_image; /* r9 */ + u64 rd_size; /* r10 */ + u64 rd_loc; /* r11 */ +}; + +extern long opal_query_takeover(u64 *hal_size, u64 *hal_align); + +extern long opal_do_takeover(struct opal_takeover_args *args); + +extern int opal_enter_rtas(struct rtas_args *args, + unsigned long data, + unsigned long entry); + + +#endif /* __ASSEMBLY__ */ + +/****** OPAL APIs ******/ + + +#endif /* __OPAL_H */ diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index e708abe576d..dea8191253d 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -51,6 +51,10 @@ * For pSeries or server processors: * 1. The MMU is off & open firmware is running in real mode. * 2. The kernel is entered at __start + * -or- For OPAL entry: + * 1. The MMU is off, processor in HV mode, primary CPU enters at 0 + * with device-tree in gpr3 + * 2. Secondary processors enter at 0x60 with PIR in gpr3 * * For iSeries: * 1. The MMU is on (as it always is for iSeries) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index a909f4e9343..9369287aa8c 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -185,6 +186,7 @@ static unsigned long __initdata prom_tce_alloc_end; #define PLATFORM_LPAR 0x0001 #define PLATFORM_POWERMAC 0x0400 #define PLATFORM_GENERIC 0x0500 +#define PLATFORM_OPAL 0x0600 static int __initdata of_platform; @@ -644,7 +646,7 @@ static void __init early_cmdline_parse(void) } } -#ifdef CONFIG_PPC_PSERIES +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* * There are two methods for telling firmware what our capabilities are. * Newer machines have an "ibm,client-architecture-support" method on the @@ -1274,6 +1276,195 @@ static void __init prom_init_mem(void) prom_printf(" ram_top : %x\n", RELOC(ram_top)); } +static void __init prom_close_stdin(void) +{ + struct prom_t *_prom = &RELOC(prom); + ihandle val; + + if (prom_getprop(_prom->chosen, "stdin", &val, sizeof(val)) > 0) + call_prom("close", 1, 0, val); +} + +#ifdef CONFIG_PPC_POWERNV + +static u64 __initdata prom_opal_size; +static u64 __initdata prom_opal_align; +static int __initdata prom_rtas_start_cpu; +static u64 __initdata prom_rtas_data; +static u64 __initdata prom_rtas_entry; + +/* XXX Don't change this structure without updating opal-takeover.S */ +static struct opal_secondary_data { + s64 ack; /* 0 */ + u64 go; /* 8 */ + struct opal_takeover_args args; /* 16 */ +} opal_secondary_data; + +extern char opal_secondary_entry; + +static void prom_query_opal(void) +{ + long rc; + + prom_printf("Querying for OPAL presence... "); + rc = opal_query_takeover(&RELOC(prom_opal_size), + &RELOC(prom_opal_align)); + prom_debug("(rc = %ld) ", rc); + if (rc != 0) { + prom_printf("not there.\n"); + return; + } + RELOC(of_platform) = PLATFORM_OPAL; + prom_printf(" there !\n"); + prom_debug(" opal_size = 0x%lx\n", RELOC(prom_opal_size)); + prom_debug(" opal_align = 0x%lx\n", RELOC(prom_opal_align)); + if (RELOC(prom_opal_align) < 0x10000) + RELOC(prom_opal_align) = 0x10000; +} + +static int prom_rtas_call(int token, int nargs, int nret, int *outputs, ...) +{ + struct rtas_args rtas_args; + va_list list; + int i; + + rtas_args.token = token; + rtas_args.nargs = nargs; + rtas_args.nret = nret; + rtas_args.rets = (rtas_arg_t *)&(rtas_args.args[nargs]); + va_start(list, outputs); + for (i = 0; i < nargs; ++i) + rtas_args.args[i] = va_arg(list, rtas_arg_t); + va_end(list); + + for (i = 0; i < nret; ++i) + rtas_args.rets[i] = 0; + + opal_enter_rtas(&rtas_args, RELOC(prom_rtas_data), + RELOC(prom_rtas_entry)); + + if (nret > 1 && outputs != NULL) + for (i = 0; i < nret-1; ++i) + outputs[i] = rtas_args.rets[i+1]; + return (nret > 0)? rtas_args.rets[0]: 0; +} + +static void __init prom_opal_hold_cpus(void) +{ + int i, cnt, cpu, rc; + long j; + phandle node; + char type[64]; + u32 servers[8]; + struct prom_t *_prom = &RELOC(prom); + void *entry = (unsigned long *)&RELOC(opal_secondary_entry); + struct opal_secondary_data *data = &RELOC(opal_secondary_data); + + prom_debug("prom_opal_hold_cpus: start...\n"); + prom_debug(" - entry = 0x%x\n", entry); + prom_debug(" - data = 0x%x\n", data); + + data->ack = -1; + data->go = 0; + + /* look for cpus */ + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + prom_getprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + + /* Skip non-configured cpus. */ + if (prom_getprop(node, "status", type, sizeof(type)) > 0) + if (strcmp(type, RELOC("okay")) != 0) + continue; + + cnt = prom_getprop(node, "ibm,ppc-interrupt-server#s", servers, + sizeof(servers)); + if (cnt == PROM_ERROR) + break; + cnt >>= 2; + for (i = 0; i < cnt; i++) { + cpu = servers[i]; + prom_debug("CPU %d ... ", cpu); + if (cpu == _prom->cpu) { + prom_debug("booted !\n"); + continue; + } + prom_debug("starting ... "); + + /* Init the acknowledge var which will be reset by + * the secondary cpu when it awakens from its OF + * spinloop. + */ + data->ack = -1; + rc = prom_rtas_call(RELOC(prom_rtas_start_cpu), 3, 1, + NULL, cpu, entry, data); + prom_debug("rtas rc=%d ...", rc); + + for (j = 0; j < 100000000 && data->ack == -1; j++) { + HMT_low(); + mb(); + } + HMT_medium(); + if (data->ack != -1) + prom_debug("done, PIR=0x%x\n", data->ack); + else + prom_debug("timeout !\n"); + } + } + prom_debug("prom_opal_hold_cpus: end...\n"); +} + +static void prom_opal_takeover(void) +{ + struct opal_secondary_data *data = &RELOC(opal_secondary_data); + struct opal_takeover_args *args = &data->args; + u64 align = RELOC(prom_opal_align); + u64 top_addr, opal_addr; + + args->k_image = (u64)RELOC(_stext); + args->k_size = _end - _stext; + args->k_entry = 0; + args->k_entry2 = 0x60; + + top_addr = _ALIGN_UP(args->k_size, align); + + if (RELOC(prom_initrd_start) != 0) { + args->rd_image = RELOC(prom_initrd_start); + args->rd_size = RELOC(prom_initrd_end) - args->rd_image; + args->rd_loc = top_addr; + top_addr = _ALIGN_UP(args->rd_loc + args->rd_size, align); + } + + /* Pickup an address for the HAL. We want to go really high + * up to avoid problem with future kexecs. On the other hand + * we don't want to be all over the TCEs on P5IOC2 machines + * which are going to be up there too. We assume the machine + * has plenty of memory, and we ask for the HAL for now to + * be just below the 1G point, or above the initrd + */ + opal_addr = _ALIGN_DOWN(0x40000000 - RELOC(prom_opal_size), align); + if (opal_addr < top_addr) + opal_addr = top_addr; + args->hal_addr = opal_addr; + + prom_debug(" k_image = 0x%lx\n", args->k_image); + prom_debug(" k_size = 0x%lx\n", args->k_size); + prom_debug(" k_entry = 0x%lx\n", args->k_entry); + prom_debug(" k_entry2 = 0x%lx\n", args->k_entry2); + prom_debug(" hal_addr = 0x%lx\n", args->hal_addr); + prom_debug(" rd_image = 0x%lx\n", args->rd_image); + prom_debug(" rd_size = 0x%lx\n", args->rd_size); + prom_debug(" rd_loc = 0x%lx\n", args->rd_loc); + prom_printf("Performing OPAL takeover,this can take a few minutes..\n"); + prom_close_stdin(); + mb(); + data->go = 1; + for (;;) + opal_do_takeover(args); +} +#endif /* CONFIG_PPC_POWERNV */ /* * Allocate room for and instantiate RTAS @@ -1326,6 +1517,12 @@ static void __init prom_instantiate_rtas(void) prom_setprop(rtas_node, "/rtas", "linux,rtas-entry", &entry, sizeof(entry)); +#ifdef CONFIG_PPC_POWERNV + /* PowerVN takeover hack */ + RELOC(prom_rtas_data) = base; + RELOC(prom_rtas_entry) = entry; + prom_getprop(rtas_node, "start-cpu", &RELOC(prom_rtas_start_cpu), 4); +#endif prom_debug("rtas base = 0x%x\n", base); prom_debug("rtas entry = 0x%x\n", entry); prom_debug("rtas size = 0x%x\n", (long)size); @@ -1543,7 +1740,7 @@ static void __init prom_hold_cpus(void) *acknowledge = (unsigned long)-1; if (reg != _prom->cpu) { - /* Primary Thread of non-boot cpu */ + /* Primary Thread of non-boot cpu or any thread */ prom_printf("starting cpu hw idx %lu... ", reg); call_prom("start-cpu", 3, 0, node, secondary_hold, reg); @@ -1652,15 +1849,6 @@ static void __init prom_init_stdout(void) prom_setprop(val, path, "linux,boot-display", NULL, 0); } -static void __init prom_close_stdin(void) -{ - struct prom_t *_prom = &RELOC(prom); - ihandle val; - - if (prom_getprop(_prom->chosen, "stdin", &val, sizeof(val)) > 0) - call_prom("close", 1, 0, val); -} - static int __init prom_find_machine_type(void) { struct prom_t *_prom = &RELOC(prom); @@ -2504,6 +2692,7 @@ static void __init prom_check_initrd(unsigned long r3, unsigned long r4) #endif /* CONFIG_BLK_DEV_INITRD */ } + /* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. @@ -2565,7 +2754,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, */ prom_check_initrd(r3, r4); -#ifdef CONFIG_PPC_PSERIES +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* * On pSeries, inform the firmware about our capabilities */ @@ -2611,14 +2800,30 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, #endif /* - * On non-powermacs, try to instantiate RTAS and puts all CPUs - * in spin-loops. PowerMacs don't have a working RTAS and use - * a different way to spin CPUs + * On non-powermacs, try to instantiate RTAS. PowerMacs don't + * have a usable RTAS implementation. */ - if (RELOC(of_platform) != PLATFORM_POWERMAC) { + if (RELOC(of_platform) != PLATFORM_POWERMAC) prom_instantiate_rtas(); - prom_hold_cpus(); + +#ifdef CONFIG_PPC_POWERNV + /* Detect HAL and try instanciating it & doing takeover */ + if (RELOC(of_platform) == PLATFORM_PSERIES_LPAR) { + prom_query_opal(); + if (RELOC(of_platform) == PLATFORM_OPAL) { + prom_opal_hold_cpus(); + prom_opal_takeover(); + } } +#endif + + /* + * On non-powermacs, put all CPUs in spin-loops. + * + * PowerMacs use a different mechanism to spin CPUs + */ + if (RELOC(of_platform) != PLATFORM_POWERMAC) + prom_hold_cpus(); /* * Fill in some infos for use by the kernel later on diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index 9f82f493789..20af6aada51 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -20,7 +20,8 @@ WHITELIST="add_reloc_offset __bss_start __bss_stop copy_and_flush _end enter_prom memcpy memset reloc_offset __secondary_hold __secondary_hold_acknowledge __secondary_hold_spinloop __start strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224 -reloc_got2 kernstart_addr memstart_addr linux_banner" +reloc_got2 kernstart_addr memstart_addr linux_banner _stext +opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry" NM="$1" OBJ="$2" diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 1c4325056b8..497133027ae 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,2 +1,2 @@ -obj-y += setup.o +obj-y += setup.o opal-takeover.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/opal-takeover.S b/arch/powerpc/platforms/powernv/opal-takeover.S new file mode 100644 index 00000000000..77b48b2b930 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-takeover.S @@ -0,0 +1,140 @@ +/* + * PowerNV OPAL takeover assembly code, for use by prom_init.c + * + * Copyright 2011 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 +#include +#include +#include + +#define STK_PARAM(i) (48 + ((i)-3)*8) + +#define H_HAL_TAKEOVER 0x5124 +#define H_HAL_TAKEOVER_QUERY_MAGIC -1 + + .text +_GLOBAL(opal_query_takeover) + mfcr r0 + stw r0,8(r1) + std r3,STK_PARAM(r3)(r1) + std r4,STK_PARAM(r4)(r1) + li r3,H_HAL_TAKEOVER + li r4,H_HAL_TAKEOVER_QUERY_MAGIC + HVSC + ld r10,STK_PARAM(r3)(r1) + std r4,0(r10) + ld r10,STK_PARAM(r4)(r1) + std r5,0(r10) + lwz r0,8(r1) + mtcrf 0xff,r0 + blr + +_GLOBAL(opal_do_takeover) + mfcr r0 + stw r0,8(r1) + mflr r0 + std r0,16(r1) + bl __opal_do_takeover + ld r0,16(r1) + mtlr r0 + lwz r0,8(r1) + mtcrf 0xff,r0 + blr + +__opal_do_takeover: + ld r4,0(r3) + ld r5,0x8(r3) + ld r6,0x10(r3) + ld r7,0x18(r3) + ld r8,0x20(r3) + ld r9,0x28(r3) + ld r10,0x30(r3) + ld r11,0x38(r3) + li r3,H_HAL_TAKEOVER + HVSC + blr + + .globl opal_secondary_entry +opal_secondary_entry: + mr r31,r3 + mfmsr r11 + li r12,(MSR_SF | MSR_ISF)@highest + sldi r12,r12,48 + or r11,r11,r12 + mtmsrd r11 + isync + mfspr r4,SPRN_PIR + std r4,0(r3) +1: HMT_LOW + ld r4,8(r3) + cmpli cr0,r4,0 + beq 1b + HMT_MEDIUM +1: addi r3,r31,16 + bl __opal_do_takeover + b 1b + +_GLOBAL(opal_enter_rtas) + mflr r0 + std r0,16(r1) + stdu r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */ + + /* Because PROM is running in 32b mode, it clobbers the high order half + * of all registers that it saves. We therefore save those registers + * PROM might touch to the stack. (r0, r3-r13 are caller saved) + */ + SAVE_GPR(2, r1) + SAVE_GPR(13, r1) + SAVE_8GPRS(14, r1) + SAVE_10GPRS(22, r1) + mfcr r10 + mfmsr r11 + std r10,_CCR(r1) + std r11,_MSR(r1) + + /* Get the PROM entrypoint */ + mtlr r5 + + /* Switch MSR to 32 bits mode + */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + andc r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + andc r11,r11,r12 + mtmsrd r11 + isync + + /* Enter RTAS here... */ + blrl + + /* Just make sure that r1 top 32 bits didn't get + * corrupt by OF + */ + rldicl r1,r1,0,32 + + /* Restore the MSR (back to 64 bits) */ + ld r0,_MSR(r1) + MTMSRD(r0) + isync + + /* Restore other registers */ + REST_GPR(2, r1) + REST_GPR(13, r1) + REST_8GPRS(14, r1) + REST_10GPRS(22, r1) + ld r4,_CCR(r1) + mtcr r4 + + addi r1,r1,PROM_FRAME_SIZE + ld r0,16(r1) + mtlr r0 + blr -- cgit v1.2.3-18-g5258 From 817c21ad9a1f00926f080265493923ada3458c63 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:56 +0000 Subject: powerpc/powernv: Get kernel command line accross OPAL takeover We stash it in boot_command_line which isn't in BSS and so won't be overwritten. We then use that as a default cmd_line before we walk the device-tree. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom.c | 7 +++++++ arch/powerpc/kernel/prom_init.c | 4 ++++ arch/powerpc/kernel/prom_init_check.sh | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 174e1e96175..7b90c564e93 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -712,6 +712,13 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL); #endif + /* Pre-initialize the cmd_line with the content of boot_commmand_line, + * which will be empty except when the content of the variable has + * been overriden by a bootloading mechanism. This happens typically + * with HAL takeover + */ + strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); + /* Retrieve various informations from the /chosen node of the * device-tree, including the platform type, initrd location and * size, TCE reserve, and more ... diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 9369287aa8c..e3f39042721 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1449,6 +1449,10 @@ static void prom_opal_takeover(void) opal_addr = top_addr; args->hal_addr = opal_addr; + /* Copy the command line to the kernel image */ + strlcpy(RELOC(boot_command_line), RELOC(prom_cmd_line), + COMMAND_LINE_SIZE); + prom_debug(" k_image = 0x%lx\n", args->k_image); prom_debug(" k_size = 0x%lx\n", args->k_size); prom_debug(" k_entry = 0x%lx\n", args->k_entry); diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index 20af6aada51..70f4286eaa7 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -21,7 +21,8 @@ _end enter_prom memcpy memset reloc_offset __secondary_hold __secondary_hold_acknowledge __secondary_hold_spinloop __start strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224 reloc_got2 kernstart_addr memstart_addr linux_banner _stext -opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry" +opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry +boot_command_line" NM="$1" OBJ="$2" -- cgit v1.2.3-18-g5258 From 14a43e69ed257a1fadadf9fea2c05adb1686419f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:57 +0000 Subject: powerpc/powernv: Basic support for OPAL Add definition of OPAL interfaces along with the wrappers to call into OPAL runtime and the early device-tree parsing hook to locate the OPAL runtime firmware. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/firmware.h | 10 + arch/powerpc/include/asm/opal.h | 382 ++++++++++++++++++++++++- arch/powerpc/kernel/prom.c | 7 + arch/powerpc/platforms/powernv/Kconfig | 9 +- arch/powerpc/platforms/powernv/Makefile | 2 +- arch/powerpc/platforms/powernv/opal-wrappers.S | 101 +++++++ arch/powerpc/platforms/powernv/opal.c | 154 ++++++++++ arch/powerpc/platforms/powernv/setup.c | 6 + arch/powerpc/platforms/powernv/smp.c | 25 +- 9 files changed, 691 insertions(+), 5 deletions(-) create mode 100644 arch/powerpc/platforms/powernv/opal-wrappers.S create mode 100644 arch/powerpc/platforms/powernv/opal.c diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h index 3a6c586c4e4..14db29b18d0 100644 --- a/arch/powerpc/include/asm/firmware.h +++ b/arch/powerpc/include/asm/firmware.h @@ -48,6 +48,8 @@ #define FW_FEATURE_CMO ASM_CONST(0x0000000002000000) #define FW_FEATURE_VPHN ASM_CONST(0x0000000004000000) #define FW_FEATURE_XCMO ASM_CONST(0x0000000008000000) +#define FW_FEATURE_OPAL ASM_CONST(0x0000000010000000) +#define FW_FEATURE_OPALv2 ASM_CONST(0x0000000020000000) #ifndef __ASSEMBLY__ @@ -65,6 +67,8 @@ enum { FW_FEATURE_PSERIES_ALWAYS = 0, FW_FEATURE_ISERIES_POSSIBLE = FW_FEATURE_ISERIES | FW_FEATURE_LPAR, FW_FEATURE_ISERIES_ALWAYS = FW_FEATURE_ISERIES | FW_FEATURE_LPAR, + FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL | FW_FEATURE_OPALv2, + FW_FEATURE_POWERNV_ALWAYS = 0, FW_FEATURE_PS3_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1, FW_FEATURE_PS3_ALWAYS = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1, FW_FEATURE_CELLEB_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_BEAT, @@ -78,6 +82,9 @@ enum { #ifdef CONFIG_PPC_ISERIES FW_FEATURE_ISERIES_POSSIBLE | #endif +#ifdef CONFIG_PPC_POWERNV + FW_FEATURE_POWERNV_POSSIBLE | +#endif #ifdef CONFIG_PPC_PS3 FW_FEATURE_PS3_POSSIBLE | #endif @@ -95,6 +102,9 @@ enum { #ifdef CONFIG_PPC_ISERIES FW_FEATURE_ISERIES_ALWAYS & #endif +#ifdef CONFIG_PPC_POWERNV + FW_FEATURE_POWERNV_ALWAYS & +#endif #ifdef CONFIG_PPC_PS3 FW_FEATURE_PS3_ALWAYS & #endif diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index ecdb283f8b7..c7a3202d10a 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -37,14 +37,394 @@ extern long opal_query_takeover(u64 *hal_size, u64 *hal_align); extern long opal_do_takeover(struct opal_takeover_args *args); +struct rtas_args; extern int opal_enter_rtas(struct rtas_args *args, unsigned long data, unsigned long entry); - #endif /* __ASSEMBLY__ */ /****** OPAL APIs ******/ +/* Return codes */ +#define OPAL_SUCCESS 0 +#define OPAL_PARAMETER -1 +#define OPAL_BUSY -2 +#define OPAL_PARTIAL -3 +#define OPAL_CONSTRAINED -4 +#define OPAL_CLOSED -5 +#define OPAL_HARDWARE -6 +#define OPAL_UNSUPPORTED -7 +#define OPAL_PERMISSION -8 +#define OPAL_NO_MEM -9 +#define OPAL_RESOURCE -10 +#define OPAL_INTERNAL_ERROR -11 +#define OPAL_BUSY_EVENT -12 +#define OPAL_HARDWARE_FROZEN -13 + +/* API Tokens (in r0) */ +#define OPAL_CONSOLE_WRITE 1 +#define OPAL_CONSOLE_READ 2 +#define OPAL_RTC_READ 3 +#define OPAL_RTC_WRITE 4 +#define OPAL_CEC_POWER_DOWN 5 +#define OPAL_CEC_REBOOT 6 +#define OPAL_READ_NVRAM 7 +#define OPAL_WRITE_NVRAM 8 +#define OPAL_HANDLE_INTERRUPT 9 +#define OPAL_POLL_EVENTS 10 +#define OPAL_PCI_SET_HUB_TCE_MEMORY 11 +#define OPAL_PCI_SET_PHB_TCE_MEMORY 12 +#define OPAL_PCI_CONFIG_READ_BYTE 13 +#define OPAL_PCI_CONFIG_READ_HALF_WORD 14 +#define OPAL_PCI_CONFIG_READ_WORD 15 +#define OPAL_PCI_CONFIG_WRITE_BYTE 16 +#define OPAL_PCI_CONFIG_WRITE_HALF_WORD 17 +#define OPAL_PCI_CONFIG_WRITE_WORD 18 +#define OPAL_SET_XIVE 19 +#define OPAL_GET_XIVE 20 +#define OPAL_GET_COMPLETION_TOKEN_STATUS 21 /* obsolete */ +#define OPAL_REGISTER_OPAL_EXCEPTION_HANDLER 22 +#define OPAL_PCI_EEH_FREEZE_STATUS 23 +#define OPAL_PCI_SHPC 24 +#define OPAL_CONSOLE_WRITE_BUFFER_SPACE 25 +#define OPAL_PCI_EEH_FREEZE_CLEAR 26 +#define OPAL_PCI_PHB_MMIO_ENABLE 27 +#define OPAL_PCI_SET_PHB_MEM_WINDOW 28 +#define OPAL_PCI_MAP_PE_MMIO_WINDOW 29 +#define OPAL_PCI_SET_PHB_TABLE_MEMORY 30 +#define OPAL_PCI_SET_PE 31 +#define OPAL_PCI_SET_PELTV 32 +#define OPAL_PCI_SET_MVE 33 +#define OPAL_PCI_SET_MVE_ENABLE 34 +#define OPAL_PCI_GET_XIVE_REISSUE 35 +#define OPAL_PCI_SET_XIVE_REISSUE 36 +#define OPAL_PCI_SET_XIVE_PE 37 +#define OPAL_GET_XIVE_SOURCE 38 +#define OPAL_GET_MSI_32 39 +#define OPAL_GET_MSI_64 40 +#define OPAL_START_CPU 41 +#define OPAL_QUERY_CPU_STATUS 42 +#define OPAL_WRITE_OPPANEL 43 +#define OPAL_PCI_MAP_PE_DMA_WINDOW 44 +#define OPAL_PCI_MAP_PE_DMA_WINDOW_REAL 45 +#define OPAL_PCI_RESET 49 + +#ifndef __ASSEMBLY__ + +/* Other enums */ +enum OpalVendorApiTokens { + OPAL_START_VENDOR_API_RANGE = 1000, OPAL_END_VENDOR_API_RANGE = 1999 +}; +enum OpalFreezeState { + OPAL_EEH_STOPPED_NOT_FROZEN = 0, + OPAL_EEH_STOPPED_MMIO_FREEZE = 1, + OPAL_EEH_STOPPED_DMA_FREEZE = 2, + OPAL_EEH_STOPPED_MMIO_DMA_FREEZE = 3, + OPAL_EEH_STOPPED_RESET = 4, + OPAL_EEH_STOPPED_TEMP_UNAVAIL = 5, + OPAL_EEH_STOPPED_PERM_UNAVAIL = 6 +}; +enum OpalEehFreezeActionToken { + OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO = 1, + OPAL_EEH_ACTION_CLEAR_FREEZE_DMA = 2, + OPAL_EEH_ACTION_CLEAR_FREEZE_ALL = 3 +}; +enum OpalPciStatusToken { + OPAL_EEH_PHB_NO_ERROR = 0, + OPAL_EEH_PHB_FATAL = 1, + OPAL_EEH_PHB_RECOVERABLE = 2, + OPAL_EEH_PHB_BUS_ERROR = 3, + OPAL_EEH_PCI_NO_DEVSEL = 4, + OPAL_EEH_PCI_TA = 5, + OPAL_EEH_PCIEX_UR = 6, + OPAL_EEH_PCIEX_CA = 7, + OPAL_EEH_PCI_MMIO_ERROR = 8, + OPAL_EEH_PCI_DMA_ERROR = 9 +}; +enum OpalShpcAction { + OPAL_SHPC_GET_LINK_STATE = 0, + OPAL_SHPC_GET_SLOT_STATE = 1 +}; +enum OpalShpcLinkState { + OPAL_SHPC_LINK_DOWN = 0, + OPAL_SHPC_LINK_UP = 1 +}; +enum OpalMmioWindowType { + OPAL_M32_WINDOW_TYPE = 1, + OPAL_M64_WINDOW_TYPE = 2, + OPAL_IO_WINDOW_TYPE = 3 +}; +enum OpalShpcSlotState { + OPAL_SHPC_DEV_NOT_PRESENT = 0, + OPAL_SHPC_DEV_PRESENT = 1 +}; +enum OpalExceptionHandler { + OPAL_MACHINE_CHECK_HANDLER = 1, + OPAL_HYPERVISOR_MAINTENANCE_HANDLER = 2, + OPAL_SOFTPATCH_HANDLER = 3 +}; +enum OpalPendingState { + OPAL_EVENT_OPAL_INTERNAL = 0x1, + OPAL_EVENT_NVRAM = 0x2, + OPAL_EVENT_RTC = 0x4, + OPAL_EVENT_CONSOLE_OUTPUT = 0x8, + OPAL_EVENT_CONSOLE_INPUT = 0x10 +}; + +/* Machine check related definitions */ +enum OpalMCE_Version { + OpalMCE_V1 = 1, +}; + +enum OpalMCE_Severity { + OpalMCE_SEV_NO_ERROR = 0, + OpalMCE_SEV_WARNING = 1, + OpalMCE_SEV_ERROR_SYNC = 2, + OpalMCE_SEV_FATAL = 3, +}; + +enum OpalMCE_Disposition { + OpalMCE_DISPOSITION_RECOVERED = 0, + OpalMCE_DISPOSITION_NOT_RECOVERED = 1, +}; + +enum OpalMCE_Initiator { + OpalMCE_INITIATOR_UNKNOWN = 0, + OpalMCE_INITIATOR_CPU = 1, +}; + +enum OpalMCE_ErrorType { + OpalMCE_ERROR_TYPE_UNKNOWN = 0, + OpalMCE_ERROR_TYPE_UE = 1, + OpalMCE_ERROR_TYPE_SLB = 2, + OpalMCE_ERROR_TYPE_ERAT = 3, + OpalMCE_ERROR_TYPE_TLB = 4, +}; + +enum OpalMCE_UeErrorType { + OpalMCE_UE_ERROR_INDETERMINATE = 0, + OpalMCE_UE_ERROR_IFETCH = 1, + OpalMCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH = 2, + OpalMCE_UE_ERROR_LOAD_STORE = 3, + OpalMCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE = 4, +}; + +enum OpalMCE_SlbErrorType { + OpalMCE_SLB_ERROR_INDETERMINATE = 0, + OpalMCE_SLB_ERROR_PARITY = 1, + OpalMCE_SLB_ERROR_MULTIHIT = 2, +}; + +enum OpalMCE_EratErrorType { + OpalMCE_ERAT_ERROR_INDETERMINATE = 0, + OpalMCE_ERAT_ERROR_PARITY = 1, + OpalMCE_ERAT_ERROR_MULTIHIT = 2, +}; + +enum OpalMCE_TlbErrorType { + OpalMCE_TLB_ERROR_INDETERMINATE = 0, + OpalMCE_TLB_ERROR_PARITY = 1, + OpalMCE_TLB_ERROR_MULTIHIT = 2, +}; + +enum OpalThreadStatus { + OPAL_THREAD_INACTIVE = 0x0, + OPAL_THREAD_STARTED = 0x1 +}; + +enum OpalPciBusCompare { + OpalPciBusAny = 0, /* Any bus number match */ + OpalPciBus3Bits = 2, /* Match top 3 bits of bus number */ + OpalPciBus4Bits = 3, /* Match top 4 bits of bus number */ + OpalPciBus5Bits = 4, /* Match top 5 bits of bus number */ + OpalPciBus6Bits = 5, /* Match top 6 bits of bus number */ + OpalPciBus7Bits = 6, /* Match top 7 bits of bus number */ + OpalPciBusAll = 7, /* Match bus number exactly */ +}; + +enum OpalDeviceCompare { + OPAL_IGNORE_RID_DEVICE_NUMBER = 0, + OPAL_COMPARE_RID_DEVICE_NUMBER = 1 +}; + +enum OpalFuncCompare { + OPAL_IGNORE_RID_FUNCTION_NUMBER = 0, + OPAL_COMPARE_RID_FUNCTION_NUMBER = 1 +}; + +enum OpalPeAction { + OPAL_UNMAP_PE = 0, + OPAL_MAP_PE = 1 +}; + +enum OpalPciResetAndReinitScope { + OPAL_PHB_COMPLETE = 1, OPAL_PCI_LINK = 2, OPAL_PHB_ERROR = 3, + OPAL_PCI_HOT_RESET = 4, OPAL_PCI_FUNDAMENTAL_RESET = 5, + OPAL_PCI_IODA_RESET = 6, +}; + +enum OpalPciResetState { OPAL_DEASSERT_RESET = 0, OPAL_ASSERT_RESET = 1 }; + +struct opal_machine_check_event { + enum OpalMCE_Version version:8; /* 0x00 */ + uint8_t in_use; /* 0x01 */ + enum OpalMCE_Severity severity:8; /* 0x02 */ + enum OpalMCE_Initiator initiator:8; /* 0x03 */ + enum OpalMCE_ErrorType error_type:8; /* 0x04 */ + enum OpalMCE_Disposition disposition:8; /* 0x05 */ + uint8_t reserved_1[2]; /* 0x06 */ + uint64_t gpr3; /* 0x08 */ + uint64_t srr0; /* 0x10 */ + uint64_t srr1; /* 0x18 */ + union { /* 0x20 */ + struct { + enum OpalMCE_UeErrorType ue_error_type:8; + uint8_t effective_address_provided; + uint8_t physical_address_provided; + uint8_t reserved_1[5]; + uint64_t effective_address; + uint64_t physical_address; + uint8_t reserved_2[8]; + } ue_error; + + struct { + enum OpalMCE_SlbErrorType slb_error_type:8; + uint8_t effective_address_provided; + uint8_t reserved_1[6]; + uint64_t effective_address; + uint8_t reserved_2[16]; + } slb_error; + + struct { + enum OpalMCE_EratErrorType erat_error_type:8; + uint8_t effective_address_provided; + uint8_t reserved_1[6]; + uint64_t effective_address; + uint8_t reserved_2[16]; + } erat_error; + + struct { + enum OpalMCE_TlbErrorType tlb_error_type:8; + uint8_t effective_address_provided; + uint8_t reserved_1[6]; + uint64_t effective_address; + uint8_t reserved_2[16]; + } tlb_error; + } u; +}; + +typedef struct oppanel_line { + /* XXX */ +} oppanel_line_t; + +/* API functions */ +int64_t opal_console_write(int64_t term_number, int64_t *length, + const uint8_t *buffer); +int64_t opal_console_read(int64_t term_number, int64_t *length, + uint8_t *buffer); +int64_t opal_console_write_buffer_space(int64_t term_number, + int64_t *length); +int64_t opal_rtc_read(uint32_t *year_month_day, + uint64_t *hour_minute_second_millisecond); +int64_t opal_rtc_write(uint32_t year_month_day, + uint64_t hour_minute_second_millisecond); +int64_t opal_cec_power_down(uint64_t request); +int64_t opal_cec_reboot(void); +int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset); +int64_t opal_write_nvram(uint64_t buffer, uint64_t size, uint64_t offset); +int64_t opal_handle_interrupt(uint64_t isn, uint64_t *outstanding_event_mask); +int64_t opal_poll_events(uint64_t *outstanding_event_mask); +int64_t opal_pci_set_hub_tce_memory(uint64_t hub_id, uint64_t tce_mem_addr, + uint64_t tce_mem_size); +int64_t opal_pci_set_phb_tce_memory(uint64_t phb_id, uint64_t tce_mem_addr, + uint64_t tce_mem_size); +int64_t opal_pci_config_read_byte(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint8_t *data); +int64_t opal_pci_config_read_half_word(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint16_t *data); +int64_t opal_pci_config_read_word(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint32_t *data); +int64_t opal_pci_config_write_byte(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint8_t data); +int64_t opal_pci_config_write_half_word(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint16_t data); +int64_t opal_pci_config_write_word(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint32_t data); +int64_t opal_set_xive(uint32_t isn, uint16_t server, uint8_t priority); +int64_t opal_get_xive(uint32_t isn, uint16_t *server, uint8_t *priority); +int64_t opal_register_exception_handler(uint64_t opal_exception, + uint64_t handler_address, + uint64_t glue_cache_line); +int64_t opal_pci_eeh_freeze_status(uint64_t phb_id, uint64_t pe_number, + uint8_t *freeze_state, + uint16_t *pci_error_type, + uint64_t *phb_status); +int64_t opal_pci_eeh_freeze_clear(uint64_t phb_id, uint64_t pe_number, + uint64_t eeh_action_token); +int64_t opal_pci_shpc(uint64_t phb_id, uint64_t shpc_action, uint8_t *state); + + + +int64_t opal_pci_phb_mmio_enable(uint64_t phb_id, uint16_t window_type, + uint16_t window_num, uint16_t enable); +int64_t opal_pci_set_phb_mem_window(uint64_t phb_id, uint16_t window_type, + uint16_t window_num, + uint64_t starting_real_address, + uint64_t starting_pci_address, + uint16_t segment_size); +int64_t opal_pci_map_pe_mmio_window(uint64_t phb_id, uint16_t pe_number, + uint16_t window_type, uint16_t window_num, + uint16_t segment_num); +int64_t opal_pci_set_phb_table_memory(uint64_t phb_id, uint64_t rtt_addr, + uint64_t ivt_addr, uint64_t ivt_len, + uint64_t reject_array_addr, + uint64_t peltv_addr); +int64_t opal_pci_set_pe(uint64_t phb_id, uint64_t pe_number, uint64_t bus_dev_func, + uint8_t bus_compare, uint8_t dev_compare, uint8_t func_compare, + uint8_t pe_action); +int64_t opal_pci_set_peltv(uint64_t phb_id, uint32_t parent_pe, uint32_t child_pe, + uint8_t state); +int64_t opal_pci_set_mve(uint64_t phb_id, uint32_t mve_number, uint32_t pe_number); +int64_t opal_pci_set_mve_enable(uint64_t phb_id, uint32_t mve_number, + uint32_t state); +int64_t opal_pci_get_xive_reissue(uint64_t phb_id, uint32_t xive_number, + uint8_t *p_bit, uint8_t *q_bit); +int64_t opal_pci_set_xive_reissue(uint64_t phb_id, uint32_t xive_number, + uint8_t p_bit, uint8_t q_bit); +int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint32_t pe_number, + uint32_t xive_num); +int64_t opal_get_xive_source(uint64_t phb_id, uint32_t xive_num, + int32_t *interrupt_source_number); +int64_t opal_get_msi_32(uint64_t phb_id, uint32_t mve_number, uint32_t xive_num, + uint8_t msi_range, uint32_t *msi_address, + uint32_t *message_data); +int64_t opal_get_msi_64(uint64_t phb_id, uint32_t mve_number, + uint32_t xive_num, uint8_t msi_range, + uint64_t *msi_address, uint32_t *message_data); +int64_t opal_start_cpu(uint64_t thread_number, uint64_t start_address); +int64_t opal_query_cpu_status(uint64_t thread_number, uint8_t *thread_status); +int64_t opal_write_oppanel(oppanel_line_t *lines, uint64_t num_lines); +int64_t opal_pci_map_pe_dma_window(uint64_t phb_id, uint16_t pe_number, uint16_t window_id, + uint16_t tce_levels, uint64_t tce_table_addr, + uint64_t tce_table_size, uint64_t tce_page_size); +int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id, uint16_t pe_number, + uint16_t dma_window_number, uint64_t pci_start_addr, + uint64_t pci_mem_size); +int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope, uint8_t assert_state); + +/* Internal functions */ +extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data); + +extern int opal_get_chars(uint32_t vtermno, char *buf, int count); +extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); + +extern void hvc_opal_init_early(void); + +/* Internal functions */ +extern int early_init_dt_scan_opal(unsigned long node, const char *uname, + int depth, void *data); + +#endif /* __ASSEMBLY__ */ #endif /* __OPAL_H */ diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 7b90c564e93..831a201e03d 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -54,6 +54,8 @@ #include #include #include +#include + #include #ifdef DEBUG @@ -707,6 +709,11 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_rtas, NULL); #endif +#ifdef CONFIG_PPC_POWERNV + /* Some machines might need OPAL info for debugging, grab it now. */ + of_scan_flat_dt(early_init_dt_scan_opal, NULL); +#endif + #ifdef CONFIG_PHYP_DUMP /* scan tree to see if dump occurred during last boot */ of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL); diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig index 24700e8df19..74fea5c2183 100644 --- a/arch/powerpc/platforms/powernv/Kconfig +++ b/arch/powerpc/platforms/powernv/Kconfig @@ -1,11 +1,16 @@ config PPC_POWERNV depends on PPC64 && PPC_BOOK3S bool "IBM PowerNV (Non-Virtualized) platform support" - select PPC_RTAS select PPC_NATIVE select PPC_XICS select PPC_ICP_NATIVE - select PPC_ICS_RTAS select PPC_P7_NAP select PPC_PCI_CHOICE if EMBEDDED default y + +config PPC_POWERNV_RTAS + depends on PPC_POWERNV + bool "Support for RTAS based PowerNV platforms such as BML" + default y + select PPC_ICS_RTAS + select PPC_RTAS diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 497133027ae..8f69c0db612 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,2 +1,2 @@ -obj-y += setup.o opal-takeover.o +obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S new file mode 100644 index 00000000000..4a3f46d8533 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S @@ -0,0 +1,101 @@ +/* + * PowerNV OPAL API wrappers + * + * Copyright 2011 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 +#include +#include +#include + +/* TODO: + * + * - Trace irqs in/off (needs saving/restoring all args, argh...) + * - Get r11 feed up by Dave so I can have better register usage + */ +#define OPAL_CALL(name, token) \ + _GLOBAL(name); \ + mflr r0; \ + mfcr r12; \ + std r0,16(r1); \ + std r12,8(r1); \ + std r1,PACAR1(r13); \ + li r0,0; \ + mfmsr r12; \ + ori r0,r0,MSR_EE; \ + std r12,PACASAVEDMSR(r13); \ + andc r12,r12,r0; \ + mtmsrd r12,1; \ + LOAD_REG_ADDR(r0,.opal_return); \ + mtlr r0; \ + li r0,MSR_DR|MSR_IR; \ + andc r12,r12,r0; \ + li r0,token; \ + mtspr SPRN_HSRR1,r12; \ + LOAD_REG_ADDR(r11,opal); \ + ld r12,8(r11); \ + ld r2,0(r11); \ + mtspr SPRN_HSRR0,r12; \ + hrfid + +_STATIC(opal_return) + ld r2,PACATOC(r13); + ld r4,8(r1); + ld r5,16(r1); + ld r6,PACASAVEDMSR(r13); + mtspr SPRN_SRR0,r5; + mtspr SPRN_SRR1,r6; + mtcr r4; + rfid + +OPAL_CALL(opal_console_write, OPAL_CONSOLE_WRITE); +OPAL_CALL(opal_console_read, OPAL_CONSOLE_READ); +OPAL_CALL(opal_console_write_buffer_space, OPAL_CONSOLE_WRITE_BUFFER_SPACE); +OPAL_CALL(opal_rtc_read, OPAL_RTC_READ); +OPAL_CALL(opal_rtc_write, OPAL_RTC_WRITE); +OPAL_CALL(opal_cec_power_down, OPAL_CEC_POWER_DOWN); +OPAL_CALL(opal_cec_reboot, OPAL_CEC_REBOOT); +OPAL_CALL(opal_read_nvram, OPAL_READ_NVRAM); +OPAL_CALL(opal_write_nvram, OPAL_WRITE_NVRAM); +OPAL_CALL(opal_handle_interrupt, OPAL_HANDLE_INTERRUPT); +OPAL_CALL(opal_poll_events, OPAL_POLL_EVENTS); +OPAL_CALL(opal_pci_set_hub_tce_memory, OPAL_PCI_SET_HUB_TCE_MEMORY); +OPAL_CALL(opal_pci_set_phb_tce_memory, OPAL_PCI_SET_PHB_TCE_MEMORY); +OPAL_CALL(opal_pci_config_read_byte, OPAL_PCI_CONFIG_READ_BYTE); +OPAL_CALL(opal_pci_config_read_half_word, OPAL_PCI_CONFIG_READ_HALF_WORD); +OPAL_CALL(opal_pci_config_read_word, OPAL_PCI_CONFIG_READ_WORD); +OPAL_CALL(opal_pci_config_write_byte, OPAL_PCI_CONFIG_WRITE_BYTE); +OPAL_CALL(opal_pci_config_write_half_word, OPAL_PCI_CONFIG_WRITE_HALF_WORD); +OPAL_CALL(opal_pci_config_write_word, OPAL_PCI_CONFIG_WRITE_WORD); +OPAL_CALL(opal_set_xive, OPAL_SET_XIVE); +OPAL_CALL(opal_get_xive, OPAL_GET_XIVE); +OPAL_CALL(opal_register_exception_handler, OPAL_REGISTER_OPAL_EXCEPTION_HANDLER); +OPAL_CALL(opal_pci_eeh_freeze_status, OPAL_PCI_EEH_FREEZE_STATUS); +OPAL_CALL(opal_pci_eeh_freeze_clear, OPAL_PCI_EEH_FREEZE_CLEAR); +OPAL_CALL(opal_pci_shpc, OPAL_PCI_SHPC); +OPAL_CALL(opal_pci_phb_mmio_enable, OPAL_PCI_PHB_MMIO_ENABLE); +OPAL_CALL(opal_pci_set_phb_mem_window, OPAL_PCI_SET_PHB_MEM_WINDOW); +OPAL_CALL(opal_pci_map_pe_mmio_window, OPAL_PCI_MAP_PE_MMIO_WINDOW); +OPAL_CALL(opal_pci_set_phb_table_memory, OPAL_PCI_SET_PHB_TABLE_MEMORY); +OPAL_CALL(opal_pci_set_pe, OPAL_PCI_SET_PE); +OPAL_CALL(opal_pci_set_peltv, OPAL_PCI_SET_PELTV); +OPAL_CALL(opal_pci_set_mve, OPAL_PCI_SET_MVE); +OPAL_CALL(opal_pci_set_mve_enable, OPAL_PCI_SET_MVE_ENABLE); +OPAL_CALL(opal_pci_get_xive_reissue, OPAL_PCI_GET_XIVE_REISSUE); +OPAL_CALL(opal_pci_set_xive_reissue, OPAL_PCI_SET_XIVE_REISSUE); +OPAL_CALL(opal_pci_set_xive_pe, OPAL_PCI_SET_XIVE_PE); +OPAL_CALL(opal_get_xive_source, OPAL_GET_XIVE_SOURCE); +OPAL_CALL(opal_get_msi_32, OPAL_GET_MSI_32); +OPAL_CALL(opal_get_msi_64, OPAL_GET_MSI_64); +OPAL_CALL(opal_start_cpu, OPAL_START_CPU); +OPAL_CALL(opal_query_cpu_status, OPAL_QUERY_CPU_STATUS); +OPAL_CALL(opal_write_oppanel, OPAL_WRITE_OPPANEL); +OPAL_CALL(opal_pci_map_pe_dma_window, OPAL_PCI_MAP_PE_DMA_WINDOW); +OPAL_CALL(opal_pci_map_pe_dma_window_real, OPAL_PCI_MAP_PE_DMA_WINDOW_REAL); +OPAL_CALL(opal_pci_reset, OPAL_PCI_RESET); diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c new file mode 100644 index 00000000000..8d5510784cc --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal.c @@ -0,0 +1,154 @@ +/* + * PowerNV OPAL high level interfaces + * + * Copyright 2011 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. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include + +#include "powernv.h" + +struct opal { + u64 base; + u64 entry; +} opal; + +static struct device_node *opal_node; +static DEFINE_SPINLOCK(opal_write_lock); + +int __init early_init_dt_scan_opal(unsigned long node, + const char *uname, int depth, void *data) +{ + const void *basep, *entryp; + unsigned long basesz, entrysz; + + if (depth != 1 || strcmp(uname, "ibm,opal") != 0) + return 0; + + basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); + entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); + + if (!basep || !entryp) + return 1; + + opal.base = of_read_number(basep, basesz/4); + opal.entry = of_read_number(entryp, entrysz/4); + + pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", + opal.base, basep, basesz); + pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", + opal.entry, entryp, entrysz); + + powerpc_firmware_features |= FW_FEATURE_OPAL; + if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { + powerpc_firmware_features |= FW_FEATURE_OPALv2; + printk("OPAL V2 detected !\n"); + } else { + printk("OPAL V1 detected !\n"); + } + + return 1; +} + +int opal_get_chars(uint32_t vtermno, char *buf, int count) +{ + s64 len, rc; + u64 evt; + + if (!opal.entry) + return 0; + opal_poll_events(&evt); + if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) + return 0; + len = count; + rc = opal_console_read(vtermno, &len, buf); + if (rc == OPAL_SUCCESS) + return len; + return 0; +} + +int opal_put_chars(uint32_t vtermno, const char *data, int total_len) +{ + int written = 0; + s64 len, rc = OPAL_BUSY; + unsigned long flags; + u64 evt; + + if (!opal.entry) + return 0; + + /* We want put_chars to be atomic to avoid mangling of hvsi + * packets. To do that, we first test for room and return + * -EAGAIN if there isn't enough + */ + spin_lock_irqsave(&opal_write_lock, flags); + rc = opal_console_write_buffer_space(vtermno, &len); + if (rc || len < total_len) { + spin_unlock_irqrestore(&opal_write_lock, flags); + /* Closed -> drop characters */ + if (rc) + return total_len; + opal_poll_events(&evt); + return -EAGAIN; + } + + /* We still try to handle partial completions, though they + * should no longer happen. + */ + while(total_len > 0 && (rc == OPAL_BUSY || + rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { + len = total_len; + rc = opal_console_write(vtermno, &len, data); + if (rc == OPAL_SUCCESS) { + total_len -= len; + data += len; + written += len; + } + /* This is a bit nasty but we need that for the console to + * flush when there aren't any interrupts. We will clean + * things a bit later to limit that to synchronous path + * such as the kernel console and xmon/udbg + */ + do + opal_poll_events(&evt); + while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT)); + } + spin_unlock_irqrestore(&opal_write_lock, flags); + return written; +} + +static int __init opal_init(void) +{ + struct device_node *np, *consoles; + + opal_node = of_find_node_by_path("/ibm,opal"); + if (!opal_node) { + pr_warn("opal: Node not found\n"); + return -ENODEV; + } + if (firmware_has_feature(FW_FEATURE_OPALv2)) + consoles = of_find_node_by_path("/ibm,opal/consoles"); + else + consoles = of_node_get(opal_node); + + /* Register serial ports */ + for_each_child_of_node(consoles, np) { + if (strcmp(np->name, "serial")) + continue; + of_platform_device_create(np, NULL, NULL); + } + of_node_put(consoles); + return 0; +} +subsys_initcall(opal_init); diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 569f9cc4eb0..b6e5ff85cc6 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -74,6 +74,12 @@ static void pnv_show_cpuinfo(struct seq_file *m) if (root) model = of_get_property(root, "model", NULL); seq_printf(m, "machine\t\t: PowerNV %s\n", model); + if (firmware_has_feature(FW_FEATURE_OPALv2)) + seq_printf(m, "firmware\t: OPAL v2\n"); + else if (firmware_has_feature(FW_FEATURE_OPAL)) + seq_printf(m, "firmware\t: OPAL v1\n"); + else + seq_printf(m, "firmware\t: BML\n"); of_node_put(root); } diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 4f4ec3797eb..e8773668524 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "powernv.h" @@ -62,6 +63,28 @@ static int pnv_smp_cpu_bootable(unsigned int nr) return 1; } +int __devinit pnv_smp_kick_cpu(int nr) +{ + unsigned int pcpu = get_hard_smp_processor_id(nr); + unsigned long start_here = __pa(*((unsigned long *) + generic_secondary_smp_init)); + long rc; + + BUG_ON(nr < 0 || nr >= NR_CPUS); + + /* On OPAL v2 the CPU are still spinning inside OPAL itself, + * get them back now + */ + if (firmware_has_feature(FW_FEATURE_OPALv2)) { + pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); + rc = opal_start_cpu(pcpu, start_here); + if (rc != OPAL_SUCCESS) + pr_warn("OPAL Error %ld starting CPU %d\n", + rc, nr); + } + return smp_generic_kick_cpu(nr); +} + #ifdef CONFIG_HOTPLUG_CPU static int pnv_smp_cpu_disable(void) @@ -127,7 +150,7 @@ static struct smp_ops_t pnv_smp_ops = { .message_pass = smp_muxed_ipi_message_pass, .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ .probe = xics_smp_probe, - .kick_cpu = smp_generic_kick_cpu, + .kick_cpu = pnv_smp_kick_cpu, .setup_cpu = pnv_smp_setup_cpu, .cpu_bootable = pnv_smp_cpu_bootable, #ifdef CONFIG_HOTPLUG_CPU -- cgit v1.2.3-18-g5258 From 6e35d5dac0c83ebb616ff3b9c2d6155c9a9ccb86 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 18:28:01 +0000 Subject: powerpc/powernv: Add support for instanciating OPAL v2 from Open Firmware OPAL v2 is instantiated in a way similar to RTAS using Open Firmware client interface calls, and the resulting address and entry point are put in the device-tree Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom_init.c | 136 ++++++++++++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 19 deletions(-) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index e3f39042721..e96f5d0d2c7 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -140,7 +140,9 @@ struct mem_map_entry { typedef u32 cell_t; -extern void __start(unsigned long r3, unsigned long r4, unsigned long r5); +extern void __start(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, unsigned long r8, + unsigned long r9); #ifdef CONFIG_PPC64 extern int enter_prom(struct prom_args *args, unsigned long entry); @@ -1293,6 +1295,11 @@ static int __initdata prom_rtas_start_cpu; static u64 __initdata prom_rtas_data; static u64 __initdata prom_rtas_entry; +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL +static u64 __initdata prom_opal_base; +static u64 __initdata prom_opal_entry; +#endif + /* XXX Don't change this structure without updating opal-takeover.S */ static struct opal_secondary_data { s64 ack; /* 0 */ @@ -1468,6 +1475,76 @@ static void prom_opal_takeover(void) for (;;) opal_do_takeover(args); } + +/* + * Allocate room for and instantiate OPAL + */ +static void __init prom_instantiate_opal(void) +{ + phandle opal_node; + ihandle opal_inst; + u64 base, entry; + u64 size = 0, align = 0x10000; + u32 rets[2]; + + prom_debug("prom_instantiate_opal: start...\n"); + + opal_node = call_prom("finddevice", 1, 1, ADDR("/ibm,opal")); + prom_debug("opal_node: %x\n", opal_node); + if (!PHANDLE_VALID(opal_node)) + return; + + prom_getprop(opal_node, "opal-runtime-size", &size, sizeof(size)); + if (size == 0) + return; + prom_getprop(opal_node, "opal-runtime-alignment", &align, + sizeof(align)); + + base = alloc_down(size, align, 0); + if (base == 0) { + prom_printf("OPAL allocation failed !\n"); + return; + } + + opal_inst = call_prom("open", 1, 1, ADDR("/ibm,opal")); + if (!IHANDLE_VALID(opal_inst)) { + prom_printf("opening opal package failed (%x)\n", opal_inst); + return; + } + + prom_printf("instantiating opal at 0x%x...", base); + + if (call_prom_ret("call-method", 4, 3, rets, + ADDR("load-opal-runtime"), + opal_inst, + base >> 32, base & 0xffffffff) != 0 + || (rets[0] == 0 && rets[1] == 0)) { + prom_printf(" failed\n"); + return; + } + entry = (((u64)rets[0]) << 32) | rets[1]; + + prom_printf(" done\n"); + + reserve_mem(base, size); + + prom_debug("opal base = 0x%x\n", base); + prom_debug("opal align = 0x%x\n", align); + prom_debug("opal entry = 0x%x\n", entry); + prom_debug("opal size = 0x%x\n", (long)size); + + prom_setprop(opal_node, "/ibm,opal", "opal-base-address", + &base, sizeof(base)); + prom_setprop(opal_node, "/ibm,opal", "opal-entry-address", + &entry, sizeof(entry)); + +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL + RELOC(prom_opal_base) = base; + RELOC(prom_opal_entry) = entry; +#endif + prom_debug("prom_instantiate_opal: end...\n"); +} + #endif /* CONFIG_PPC_POWERNV */ /* @@ -1863,7 +1940,7 @@ static int __init prom_find_machine_type(void) int x; #endif - /* Look for a PowerMac */ + /* Look for a PowerMac or a Cell */ len = prom_getprop(_prom->root, "compatible", compat, sizeof(compat)-1); if (len > 0) { @@ -1889,7 +1966,11 @@ static int __init prom_find_machine_type(void) } } #ifdef CONFIG_PPC64 - /* If not a mac, try to figure out if it's an IBM pSeries or any other + /* Try to detect OPAL */ + if (PHANDLE_VALID(call_prom("finddevice", 1, 1, ADDR("/ibm,opal")))) + return PLATFORM_OPAL; + + /* Try to figure out if it's an IBM pSeries or any other * PAPR compliant platform. We assume it is if : * - /device_type is "chrp" (please, do NOT use that for future * non-IBM designs ! @@ -2116,7 +2197,7 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, unsigned long soff; unsigned char *valp; static char pname[MAX_PROPERTY_NAME]; - int l, room; + int l, room, has_phandle = 0; dt_push_token(OF_DT_BEGIN_NODE, mem_start, mem_end); @@ -2200,19 +2281,26 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, valp = make_room(mem_start, mem_end, l, 4); call_prom("getprop", 4, 1, node, RELOC(pname), valp, l); *mem_start = _ALIGN(*mem_start, 4); + + if (!strcmp(RELOC(pname), RELOC("phandle"))) + has_phandle = 1; } - /* Add a "linux,phandle" property. */ - soff = dt_find_string(RELOC("linux,phandle")); - if (soff == 0) - prom_printf("WARNING: Can't find string index for" - " node %s\n", path); - else { - dt_push_token(OF_DT_PROP, mem_start, mem_end); - dt_push_token(4, mem_start, mem_end); - dt_push_token(soff, mem_start, mem_end); - valp = make_room(mem_start, mem_end, 4, 4); - *(u32 *)valp = node; + /* Add a "linux,phandle" property if no "phandle" property already + * existed (can happen with OPAL) + */ + if (!has_phandle) { + soff = dt_find_string(RELOC("linux,phandle")); + if (soff == 0) + prom_printf("WARNING: Can't find string index for" + " node %s\n", path); + else { + dt_push_token(OF_DT_PROP, mem_start, mem_end); + dt_push_token(4, mem_start, mem_end); + dt_push_token(soff, mem_start, mem_end); + valp = make_room(mem_start, mem_end, 4, 4); + *(u32 *)valp = node; + } } /* do all our children */ @@ -2746,6 +2834,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * between pSeries SMP and pSeries LPAR */ RELOC(of_platform) = prom_find_machine_type(); + prom_printf("Detected machine type: %x\n", RELOC(of_platform)); #ifndef CONFIG_RELOCATABLE /* Bail if this is a kdump kernel. */ @@ -2807,7 +2896,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * On non-powermacs, try to instantiate RTAS. PowerMacs don't * have a usable RTAS implementation. */ - if (RELOC(of_platform) != PLATFORM_POWERMAC) + if (RELOC(of_platform) != PLATFORM_POWERMAC && + RELOC(of_platform) != PLATFORM_OPAL) prom_instantiate_rtas(); #ifdef CONFIG_PPC_POWERNV @@ -2818,7 +2908,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, prom_opal_hold_cpus(); prom_opal_takeover(); } - } + } else if (RELOC(of_platform) == PLATFORM_OPAL) + prom_instantiate_opal(); #endif /* @@ -2826,7 +2917,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * * PowerMacs use a different mechanism to spin CPUs */ - if (RELOC(of_platform) != PLATFORM_POWERMAC) + if (RELOC(of_platform) != PLATFORM_POWERMAC && + RELOC(of_platform) != PLATFORM_OPAL) prom_hold_cpus(); /* @@ -2894,7 +2986,13 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, reloc_got2(-offset); #endif - __start(hdr, kbase, 0); +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL + /* OPAL early debug gets the OPAL base & entry in r8 and r9 */ + __start(hdr, kbase, 0, 0, 0, + RELOC(prom_opal_base), RELOC(prom_opal_entry)); +#else + __start(hdr, kbase, 0, 0, 0, 0, 0); +#endif return 0; } -- cgit v1.2.3-18-g5258 From daea1175a9f0f70eab5b33e2827d57ba8c686816 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:59 +0000 Subject: powerpc/powernv: Support for OPAL console This adds a udbg and an hvc console backend for supporting a console using the OPAL console interfaces. On OPAL v1 we have hvc0 mapped to whatever console the system was configured for (network or hvsi serial port) via the service processor. On OPAL v2 we have hvcN mapped to the Nth console provided by OPAL which generally corresponds to: hvc0 : network console (raw protocol) hvc1 : serial port S1 (hvsi) hvc2 : serial port S2 (hvsi) Note: At this point, early debug console only works with OPAL v1 and shouldn't be enabled in a normal kernel. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig.debug | 31 +++ arch/powerpc/include/asm/opal.h | 5 + arch/powerpc/include/asm/udbg.h | 2 + arch/powerpc/kernel/head_64.S | 14 +- arch/powerpc/kernel/udbg.c | 4 + arch/powerpc/platforms/powernv/opal.c | 31 ++- arch/powerpc/platforms/powernv/setup.c | 14 +- drivers/tty/hvc/Kconfig | 9 + drivers/tty/hvc/Makefile | 1 + drivers/tty/hvc/hvc_opal.c | 424 +++++++++++++++++++++++++++++++++ drivers/tty/hvc/hvsi_lib.c | 4 +- 11 files changed, 517 insertions(+), 22 deletions(-) create mode 100644 drivers/tty/hvc/hvc_opal.c diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 06eb62b0fd9..1b8a9c905cf 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -265,8 +265,27 @@ config PPC_EARLY_DEBUG_PS3GELIC Select this to enable early debugging for the PlayStation3 via UDP broadcasts sent out through the Ethernet port. +config PPC_EARLY_DEBUG_OPAL_RAW + bool "OPAL raw console" + depends on HVC_OPAL + help + Select this to enable early debugging for the PowerNV platform + using a "raw" console + +config PPC_EARLY_DEBUG_OPAL_HVSI + bool "OPAL hvsi console" + depends on HVC_OPAL + help + Select this to enable early debugging for the PowerNV platform + using an "hvsi" console + endchoice +config PPC_EARLY_DEBUG_OPAL + def_bool y + depends on PPC_EARLY_DEBUG_OPAL_RAW || PPC_EARLY_DEBUG_OPAL_HVSI + + config PPC_EARLY_DEBUG_HVSI_VTERMNO hex "vterm number to use with early debug HVSI" depends on PPC_EARLY_DEBUG_LPAR_HVSI @@ -275,6 +294,18 @@ config PPC_EARLY_DEBUG_HVSI_VTERMNO You probably want 0x30000000 for your first serial port and 0x30000001 for your second one +config PPC_EARLY_DEBUG_OPAL_VTERMNO + hex "vterm number to use with OPAL early debug" + depends on PPC_EARLY_DEBUG_OPAL + default "0" + help + This correspond to which /dev/hvcN you want to use for early + debug. + + On OPAL v1 (takeover) this should always be 0 + On OPAL v2, this will be 0 for network console and 1 or 2 for + the machine built-in serial ports. + config PPC_EARLY_DEBUG_44x_PHYSLOW hex "Low 32 bits of early debug UART physical address" depends on PPC_EARLY_DEBUG_44x diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index c7a3202d10a..749de00a02d 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -425,6 +425,11 @@ extern void hvc_opal_init_early(void); extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data); +extern int opal_get_chars(uint32_t vtermno, char *buf, int count); +extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); + +extern void hvc_opal_init_early(void); + #endif /* __ASSEMBLY__ */ #endif /* __OPAL_H */ diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h index 7cf796fa03f..6587ec7bc6e 100644 --- a/arch/powerpc/include/asm/udbg.h +++ b/arch/powerpc/include/asm/udbg.h @@ -55,6 +55,8 @@ extern void __init udbg_init_cpm(void); extern void __init udbg_init_usbgecko(void); extern void __init udbg_init_wsp(void); extern void __init udbg_init_ps3gelic(void); +extern void __init udbg_init_debug_opal_raw(void); +extern void __init udbg_init_debug_opal_hvsi(void); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_UDBG_H */ diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index dea8191253d..06c7251c1bf 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -53,7 +53,8 @@ * 2. The kernel is entered at __start * -or- For OPAL entry: * 1. The MMU is off, processor in HV mode, primary CPU enters at 0 - * with device-tree in gpr3 + * with device-tree in gpr3. We also get OPAL base in r8 and + * entry in r9 for debugging purposes * 2. Secondary processors enter at 0x60 with PIR in gpr3 * * For iSeries: @@ -335,6 +336,11 @@ _GLOBAL(__start_initialization_multiplatform) /* Save parameters */ mr r31,r3 mr r30,r4 +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL + /* Save OPAL entry */ + mr r28,r8 + mr r29,r9 +#endif #ifdef CONFIG_PPC_BOOK3E bl .start_initialization_book3e @@ -711,6 +717,12 @@ _INIT_STATIC(start_here_multiplatform) bdnz 3b 4: +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL + /* Setup OPAL entry */ + std r28,0(r11); + std r29,8(r11); +#endif + #ifndef CONFIG_PPC_BOOK3E mfmsr r6 ori r6,r6,MSR_RI diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index 5b3e98e0315..35f948203ec 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -69,6 +69,10 @@ void __init udbg_early_init(void) udbg_init_wsp(); #elif defined(CONFIG_PPC_EARLY_DEBUG_PS3GELIC) udbg_init_ps3gelic(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_RAW) + udbg_init_debug_opal_raw(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI) + udbg_init_debug_opal_hvsi(); #endif #ifdef CONFIG_PPC_EARLY_DEBUG diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 8d5510784cc..7887733b9b3 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -67,7 +67,7 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count) u64 evt; if (!opal.entry) - return 0; + return -ENODEV; opal_poll_events(&evt); if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) return 0; @@ -81,31 +81,38 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count) int opal_put_chars(uint32_t vtermno, const char *data, int total_len) { int written = 0; - s64 len, rc = OPAL_BUSY; + s64 len, rc; unsigned long flags; u64 evt; if (!opal.entry) - return 0; + return -ENODEV; /* We want put_chars to be atomic to avoid mangling of hvsi * packets. To do that, we first test for room and return - * -EAGAIN if there isn't enough + * -EAGAIN if there isn't enough. + * + * Unfortunately, opal_console_write_buffer_space() doesn't + * appear to work on opal v1, so we just assume there is + * enough room and be done with it */ spin_lock_irqsave(&opal_write_lock, flags); - rc = opal_console_write_buffer_space(vtermno, &len); - if (rc || len < total_len) { - spin_unlock_irqrestore(&opal_write_lock, flags); - /* Closed -> drop characters */ - if (rc) - return total_len; - opal_poll_events(&evt); - return -EAGAIN; + if (firmware_has_feature(FW_FEATURE_OPALv2)) { + rc = opal_console_write_buffer_space(vtermno, &len); + if (rc || len < total_len) { + spin_unlock_irqrestore(&opal_write_lock, flags); + /* Closed -> drop characters */ + if (rc) + return total_len; + opal_poll_events(&evt); + return -EAGAIN; + } } /* We still try to handle partial completions, though they * should no longer happen. */ + rc = OPAL_BUSY; while(total_len > 0 && (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { len = total_len; diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index b6e5ff85cc6..07ba1ecd180 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -29,17 +29,12 @@ #include #include #include +#include #include "powernv.h" static void __init pnv_setup_arch(void) { - /* Force console to hvc for now until we have sorted out the - * real console situation for the platform. This will make - * hvc_udbg work at least. - */ - add_preferred_console("hvc", 0, NULL); - /* Initialize SMP */ pnv_smp_init(); @@ -55,7 +50,12 @@ static void __init pnv_setup_arch(void) static void __init pnv_init_early(void) { - /* XXX IOMMU */ +#ifdef CONFIG_HVC_OPAL + if (firmware_has_feature(FW_FEATURE_OPAL)) + hvc_opal_init_early(); + else +#endif + add_preferred_console("hvc", 0, NULL); } static void __init pnv_init_IRQ(void) diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index e371753ba92..4222035acfb 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -34,6 +34,15 @@ config HVC_ISERIES help iSeries machines support a hypervisor virtual console. +config HVC_OPAL + bool "OPAL Console support" + depends on PPC_POWERNV + select HVC_DRIVER + select HVC_IRQ + default y + help + PowerNV machines running under OPAL need that driver to get a console + config HVC_RTAS bool "IBM RTAS Console support" depends on PPC_RTAS diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index e2920531637..89abf40bc73 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o +obj-$(CONFIG_HVC_OPAL) += hvc_opal.o hvsi_lib.o obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c new file mode 100644 index 00000000000..7b38512d6c4 --- /dev/null +++ b/drivers/tty/hvc/hvc_opal.c @@ -0,0 +1,424 @@ +/* + * opal driver interface to hvc_console.c + * + * Copyright 2011 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "hvc_console.h" + +static const char hvc_opal_name[] = "hvc_opal"; + +static struct of_device_id hvc_opal_match[] __devinitdata = { + { .name = "serial", .compatible = "ibm,opal-console-raw" }, + { .name = "serial", .compatible = "ibm,opal-console-hvsi" }, + { }, +}; + +typedef enum hv_protocol { + HV_PROTOCOL_RAW, + HV_PROTOCOL_HVSI +} hv_protocol_t; + +struct hvc_opal_priv { + hv_protocol_t proto; /* Raw data or HVSI packets */ + struct hvsi_priv hvsi; /* HVSI specific data */ +}; +static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES]; + +/* For early boot console */ +static struct hvc_opal_priv hvc_opal_boot_priv; +static u32 hvc_opal_boot_termno; + +static const struct hv_ops hvc_opal_raw_ops = { + .get_chars = opal_get_chars, + .put_chars = opal_put_chars, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, +}; + +static int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[vtermno]; + + if (WARN_ON(!pv)) + return -ENODEV; + + return hvsilib_get_chars(&pv->hvsi, buf, count); +} + +static int hvc_opal_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[vtermno]; + + if (WARN_ON(!pv)) + return -ENODEV; + + return hvsilib_put_chars(&pv->hvsi, buf, count); +} + +static int hvc_opal_hvsi_open(struct hvc_struct *hp, int data) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + int rc; + + pr_devel("HVSI@%x: do open !\n", hp->vtermno); + + rc = notifier_add_irq(hp, data); + if (rc) + return rc; + + return hvsilib_open(&pv->hvsi, hp); +} + +static void hvc_opal_hvsi_close(struct hvc_struct *hp, int data) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + + pr_devel("HVSI@%x: do close !\n", hp->vtermno); + + hvsilib_close(&pv->hvsi, hp); + + notifier_del_irq(hp, data); +} + +void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + + pr_devel("HVSI@%x: do hangup !\n", hp->vtermno); + + hvsilib_close(&pv->hvsi, hp); + + notifier_hangup_irq(hp, data); +} + +static int hvc_opal_hvsi_tiocmget(struct hvc_struct *hp) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + + if (!pv) + return -EINVAL; + return pv->hvsi.mctrl; +} + +static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, + unsigned int clear) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + + pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n", + hp->vtermno, set, clear); + + if (set & TIOCM_DTR) + hvsilib_write_mctrl(&pv->hvsi, 1); + else if (clear & TIOCM_DTR) + hvsilib_write_mctrl(&pv->hvsi, 0); + + return 0; +} + +static const struct hv_ops hvc_opal_hvsi_ops = { + .get_chars = hvc_opal_hvsi_get_chars, + .put_chars = hvc_opal_hvsi_put_chars, + .notifier_add = hvc_opal_hvsi_open, + .notifier_del = hvc_opal_hvsi_close, + .notifier_hangup = hvc_opal_hvsi_hangup, + .tiocmget = hvc_opal_hvsi_tiocmget, + .tiocmset = hvc_opal_hvsi_tiocmset, +}; + +static int __devinit hvc_opal_probe(struct platform_device *dev) +{ + const struct hv_ops *ops; + struct hvc_struct *hp; + struct hvc_opal_priv *pv; + hv_protocol_t proto; + unsigned int termno, boot = 0; + const __be32 *reg; + + if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) { + proto = HV_PROTOCOL_RAW; + ops = &hvc_opal_raw_ops; + } else if (of_device_is_compatible(dev->dev.of_node, + "ibm,opal-console-hvsi")) { + proto = HV_PROTOCOL_HVSI; + ops = &hvc_opal_hvsi_ops; + } else { + pr_err("hvc_opal: Unkown protocol for %s\n", + dev->dev.of_node->full_name); + return -ENXIO; + } + + reg = of_get_property(dev->dev.of_node, "reg", NULL); + termno = reg ? be32_to_cpup(reg) : 0; + + /* Is it our boot one ? */ + if (hvc_opal_privs[termno] == &hvc_opal_boot_priv) { + pv = hvc_opal_privs[termno]; + boot = 1; + } else if (hvc_opal_privs[termno] == NULL) { + pv = kzalloc(sizeof(struct hvc_opal_priv), GFP_KERNEL); + if (!pv) + return -ENOMEM; + pv->proto = proto; + hvc_opal_privs[termno] = pv; + if (proto == HV_PROTOCOL_HVSI) + hvsilib_init(&pv->hvsi, opal_get_chars, opal_put_chars, + termno, 0); + + /* Instanciate now to establish a mapping index==vtermno */ + hvc_instantiate(termno, termno, ops); + } else { + pr_err("hvc_opal: Device %s has duplicate terminal number #%d\n", + dev->dev.of_node->full_name, termno); + return -ENXIO; + } + + pr_info("hvc%d: %s protocol on %s%s\n", termno, + proto == HV_PROTOCOL_RAW ? "raw" : "hvsi", + dev->dev.of_node->full_name, + boot ? " (boot console)" : ""); + + /* We don't do IRQ yet */ + hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS); + if (IS_ERR(hp)) + return PTR_ERR(hp); + dev_set_drvdata(&dev->dev, hp); + + return 0; +} + +static int __devexit hvc_opal_remove(struct platform_device *dev) +{ + struct hvc_struct *hp = dev_get_drvdata(&dev->dev); + int rc, termno; + + termno = hp->vtermno; + rc = hvc_remove(hp); + if (rc == 0) { + if (hvc_opal_privs[termno] != &hvc_opal_boot_priv) + kfree(hvc_opal_privs[termno]); + hvc_opal_privs[termno] = NULL; + } + return rc; +} + +static struct platform_driver hvc_opal_driver = { + .probe = hvc_opal_probe, + .remove = __devexit_p(hvc_opal_remove), + .driver = { + .name = hvc_opal_name, + .owner = THIS_MODULE, + .of_match_table = hvc_opal_match, + } +}; + +static int __init hvc_opal_init(void) +{ + if (!firmware_has_feature(FW_FEATURE_OPAL)) + return -ENODEV; + + /* Register as a vio device to receive callbacks */ + return platform_driver_register(&hvc_opal_driver); +} +module_init(hvc_opal_init); + +static void __exit hvc_opal_exit(void) +{ + platform_driver_unregister(&hvc_opal_driver); +} +module_exit(hvc_opal_exit); + +static void udbg_opal_putc(char c) +{ + unsigned int termno = hvc_opal_boot_termno; + int count = -1; + + if (c == '\n') + udbg_opal_putc('\r'); + + do { + switch(hvc_opal_boot_priv.proto) { + case HV_PROTOCOL_RAW: + count = opal_put_chars(termno, &c, 1); + break; + case HV_PROTOCOL_HVSI: + count = hvc_opal_hvsi_put_chars(termno, &c, 1); + break; + } + } while(count == 0 || count == -EAGAIN); +} + +static int udbg_opal_getc_poll(void) +{ + unsigned int termno = hvc_opal_boot_termno; + int rc = 0; + char c; + + switch(hvc_opal_boot_priv.proto) { + case HV_PROTOCOL_RAW: + rc = opal_get_chars(termno, &c, 1); + break; + case HV_PROTOCOL_HVSI: + rc = hvc_opal_hvsi_get_chars(termno, &c, 1); + break; + } + if (!rc) + return -1; + return c; +} + +static int udbg_opal_getc(void) +{ + int ch; + for (;;) { + ch = udbg_opal_getc_poll(); + if (ch == -1) { + /* This shouldn't be needed...but... */ + volatile unsigned long delay; + for (delay=0; delay < 2000000; delay++) + ; + } else { + return ch; + } + } +} + +static void udbg_init_opal_common(void) +{ + udbg_putc = udbg_opal_putc; + udbg_getc = udbg_opal_getc; + udbg_getc_poll = udbg_opal_getc_poll; + tb_ticks_per_usec = 0x200; /* Make udelay not suck */ +} + +void __init hvc_opal_init_early(void) +{ + struct device_node *stdout_node = NULL; + const u32 *termno; + const char *name = NULL; + const struct hv_ops *ops; + u32 index; + + /* find the boot console from /chosen/stdout */ + if (of_chosen) + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name) { + stdout_node = of_find_node_by_path(name); + if (!stdout_node) { + pr_err("hvc_opal: Failed to locate default console!\n"); + return; + } + } else { + struct device_node *opal, *np; + + /* Current OPAL takeover doesn't provide the stdout + * path, so we hard wire it + */ + opal = of_find_node_by_path("/ibm,opal/consoles"); + if (opal) + pr_devel("hvc_opal: Found consoles in new location\n"); + if (!opal) { + opal = of_find_node_by_path("/ibm,opal"); + if (opal) + pr_devel("hvc_opal: " + "Found consoles in old location\n"); + } + if (!opal) + return; + for_each_child_of_node(opal, np) { + if (!strcmp(np->name, "serial")) { + stdout_node = np; + break; + } + } + of_node_put(opal); + } + if (!stdout_node) + return; + termno = of_get_property(stdout_node, "reg", NULL); + index = termno ? *termno : 0; + if (index >= MAX_NR_HVC_CONSOLES) + return; + hvc_opal_privs[index] = &hvc_opal_boot_priv; + + /* Check the protocol */ + if (of_device_is_compatible(stdout_node, "ibm,opal-console-raw")) { + hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW; + ops = &hvc_opal_raw_ops; + pr_devel("hvc_opal: Found RAW console\n"); + } + else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) { + hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI; + ops = &hvc_opal_hvsi_ops; + hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars, + opal_put_chars, index, 1); + /* HVSI, perform the handshake now */ + hvsilib_establish(&hvc_opal_boot_priv.hvsi); + pr_devel("hvc_opal: Found HVSI console\n"); + } else + goto out; + hvc_opal_boot_termno = index; + udbg_init_opal_common(); + add_preferred_console("hvc", index, NULL); + hvc_instantiate(index, index, ops); +out: + of_node_put(stdout_node); +} + +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_RAW +void __init udbg_init_debug_opal(void) +{ + u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO; + hvc_opal_privs[index] = &hvc_opal_boot_priv; + hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW; + hvc_opal_boot_termno = index; + udbg_init_opal_common(); +} +#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_RAW */ + +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI +void __init udbg_init_debug_opal_hvsi(void) +{ + u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO; + hvc_opal_privs[index] = &hvc_opal_boot_priv; + hvc_opal_boot_termno = index; + udbg_init_opal_common(); + hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars, opal_put_chars, + index, 1); + hvsilib_establish(&hvc_opal_boot_priv.hvsi); +} +#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI */ diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index bd9b09827b2..6f4dd83d869 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -183,7 +183,7 @@ int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count) unsigned int tries, read = 0; if (WARN_ON(!pv)) - return 0; + return -ENXIO; /* If we aren't open, don't do anything in order to avoid races * with connection establishment. The hvc core will call this @@ -234,7 +234,7 @@ int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count) int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); if (WARN_ON(!pv)) - return 0; + return -ENODEV; dp.hdr.type = VS_DATA_PACKET_HEADER; dp.hdr.len = adjcount + sizeof(struct hvsi_header); -- cgit v1.2.3-18-g5258 From ec27329ffb3b4f619be9f0065c473fcb36ea52ce Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 18:28:03 +0000 Subject: powerpc/powernv: Hookup reboot and poweroff functions This calls the respective HAL functions, and spin on hal_poll_event() to ensure the HAL has a chance to communicate with the FSP to trigger the reboot or shutdown operation Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/setup.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 07ba1ecd180..0fac0a6c951 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -83,19 +83,39 @@ static void pnv_show_cpuinfo(struct seq_file *m) of_node_put(root); } -static void pnv_restart(char *cmd) +static void __noreturn pnv_restart(char *cmd) { - for (;;); + long rc = OPAL_BUSY; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_cec_reboot(); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + for (;;) + opal_poll_events(NULL); } -static void pnv_power_off(void) +static void __noreturn pnv_power_off(void) { - for (;;); + long rc = OPAL_BUSY; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_cec_power_down(0); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + for (;;) + opal_poll_events(NULL); } -static void pnv_halt(void) +static void __noreturn pnv_halt(void) { - for (;;); + pnv_power_off(); } static unsigned long __init pnv_get_boot_time(void) -- cgit v1.2.3-18-g5258 From 628daa8d5abfd904a7329a660c5c374212230123 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:01 +0000 Subject: powerpc/powernv: Add RTC and NVRAM support plus RTAS fallbacks Implements OPAL RTC and NVRAM support and wire all that up to the powernv platform. We use RTAS for RTC as a fallback if available. Using RTAS for nvram is not supported yet, pending some rework/cleanup and generalization of the pSeries & CHRP code. We also use RTAS fallbacks for power off and reboot Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/opal.h | 6 ++ arch/powerpc/platforms/powernv/Makefile | 2 + arch/powerpc/platforms/powernv/opal-nvram.c | 88 ++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/opal-rtc.c | 97 +++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/setup.c | 57 ++++++++++------- 5 files changed, 229 insertions(+), 21 deletions(-) create mode 100644 arch/powerpc/platforms/powernv/opal-nvram.c create mode 100644 arch/powerpc/platforms/powernv/opal-rtc.c diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 749de00a02d..77ebe50020a 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -430,6 +430,12 @@ extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); extern void hvc_opal_init_early(void); +struct rtc_time; +extern int opal_set_rtc_time(struct rtc_time *tm); +extern void opal_get_rtc_time(struct rtc_time *tm); +extern unsigned long opal_get_boot_time(void); +extern void opal_nvram_init(void); + #endif /* __ASSEMBLY__ */ #endif /* __OPAL_H */ diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 8f69c0db612..618ad836f28 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,2 +1,4 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o +obj-y += opal-rtc.o opal-nvram.o + obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c new file mode 100644 index 00000000000..3f83e1ae26a --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-nvram.c @@ -0,0 +1,88 @@ +/* + * PowerNV nvram code. + * + * Copyright 2011 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. + */ + +#define DEBUG + +#include +#include +#include + +#include +#include + +static unsigned int nvram_size; + +static ssize_t opal_nvram_size(void) +{ + return nvram_size; +} + +static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index) +{ + s64 rc; + int off; + + if (*index >= nvram_size) + return 0; + off = *index; + if ((off + count) > nvram_size) + count = nvram_size - off; + rc = opal_read_nvram(__pa(buf), count, off); + if (rc != OPAL_SUCCESS) + return -EIO; + *index += count; + return count; +} + +static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index) +{ + s64 rc = OPAL_BUSY; + int off; + + if (*index >= nvram_size) + return 0; + off = *index; + if ((off + count) > nvram_size) + count = nvram_size - off; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_write_nvram(__pa(buf), count, off); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + } + *index += count; + return count; +} + +void __init opal_nvram_init(void) +{ + struct device_node *np; + const u32 *nbytes_p; + + np = of_find_compatible_node(NULL, NULL, "ibm,opal-nvram"); + if (np == NULL) + return; + + nbytes_p = of_get_property(np, "#bytes", NULL); + if (!nbytes_p) { + of_node_put(np); + return; + } + nvram_size = *nbytes_p; + + printk(KERN_INFO "OPAL nvram setup, %u bytes\n", nvram_size); + of_node_put(np); + + ppc_md.nvram_read = opal_nvram_read; + ppc_md.nvram_write = opal_nvram_write; + ppc_md.nvram_size = opal_nvram_size; +} + diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c new file mode 100644 index 00000000000..2aa7641aac9 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-rtc.c @@ -0,0 +1,97 @@ +/* + * PowerNV Real Time Clock. + * + * Copyright 2011 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 +#include +#include +#include +#include + +#include +#include + +static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm) +{ + tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) + + bcd2bin((y_m_d >> 16) & 0xff)) - 1900; + tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1; + tm->tm_mday = bcd2bin(y_m_d & 0xff); + tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff); + tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff); + tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff); + + GregorianDay(tm); +} + +unsigned long __init opal_get_boot_time(void) +{ + struct rtc_time tm; + u32 y_m_d; + u64 h_m_s_ms; + long rc = OPAL_BUSY; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_rtc_read(&y_m_d, &h_m_s_ms); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + if (rc != OPAL_SUCCESS) + return 0; + opal_to_tm(y_m_d, h_m_s_ms, &tm); + return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +void opal_get_rtc_time(struct rtc_time *tm) +{ + long rc = OPAL_BUSY; + u32 y_m_d; + u64 h_m_s_ms; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_rtc_read(&y_m_d, &h_m_s_ms); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + if (rc != OPAL_SUCCESS) + return; + opal_to_tm(y_m_d, h_m_s_ms, tm); +} + +int opal_set_rtc_time(struct rtc_time *tm) +{ + long rc = OPAL_BUSY; + u32 y_m_d = 0; + u64 h_m_s_ms = 0; + + y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24; + y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16; + y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8; + y_m_d |= ((u32)bin2bcd(tm->tm_mday)); + + h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56; + h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48; + h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_rtc_write(y_m_d, h_m_s_ms); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + return rc == OPAL_SUCCESS ? 0 : -EIO; +} diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 0fac0a6c951..4a2b2e27959 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -29,7 +29,9 @@ #include #include #include +#include #include +#include #include "powernv.h" @@ -40,7 +42,9 @@ static void __init pnv_setup_arch(void) /* XXX PCI */ - /* XXX NVRAM */ + /* Setup RTC and NVRAM callbacks */ + if (firmware_has_feature(FW_FEATURE_OPAL)) + opal_nvram_init(); /* Enable NAP mode */ powersave_nap = 1; @@ -118,30 +122,40 @@ static void __noreturn pnv_halt(void) pnv_power_off(); } -static unsigned long __init pnv_get_boot_time(void) -{ - return 0; -} - -static void pnv_get_rtc_time(struct rtc_time *rtc_tm) +static void pnv_progress(char *s, unsigned short hex) { } -static int pnv_set_rtc_time(struct rtc_time *tm) +#ifdef CONFIG_KEXEC +static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) { - return 0; + xics_kexec_teardown_cpu(secondary); } +#endif /* CONFIG_KEXEC */ -static void pnv_progress(char *s, unsigned short hex) +static void __init pnv_setup_machdep_opal(void) { + ppc_md.get_boot_time = opal_get_boot_time; + ppc_md.get_rtc_time = opal_get_rtc_time; + ppc_md.set_rtc_time = opal_set_rtc_time; + ppc_md.restart = pnv_restart; + ppc_md.power_off = pnv_power_off; + ppc_md.halt = pnv_halt; } -#ifdef CONFIG_KEXEC -static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) +#ifdef CONFIG_PPC_POWERNV_RTAS +static void __init pnv_setup_machdep_rtas(void) { - xics_kexec_teardown_cpu(secondary); + if (rtas_token("get-time-of-day") != RTAS_UNKNOWN_SERVICE) { + ppc_md.get_boot_time = rtas_get_boot_time; + ppc_md.get_rtc_time = rtas_get_rtc_time; + ppc_md.set_rtc_time = rtas_set_rtc_time; + } + ppc_md.restart = rtas_restart; + ppc_md.power_off = rtas_power_off; + ppc_md.halt = rtas_halt; } -#endif /* CONFIG_KEXEC */ +#endif /* CONFIG_PPC_POWERNV_RTAS */ static int __init pnv_probe(void) { @@ -152,6 +166,13 @@ static int __init pnv_probe(void) hpte_init_native(); + if (firmware_has_feature(FW_FEATURE_OPAL)) + pnv_setup_machdep_opal(); +#ifdef CONFIG_PPC_POWERNV_RTAS + else if (rtas.base) + pnv_setup_machdep_rtas(); +#endif /* CONFIG_PPC_POWERNV_RTAS */ + pr_debug("PowerNV detected !\n"); return 1; @@ -160,16 +181,10 @@ static int __init pnv_probe(void) define_machine(powernv) { .name = "PowerNV", .probe = pnv_probe, - .setup_arch = pnv_setup_arch, .init_early = pnv_init_early, + .setup_arch = pnv_setup_arch, .init_IRQ = pnv_init_IRQ, .show_cpuinfo = pnv_show_cpuinfo, - .restart = pnv_restart, - .power_off = pnv_power_off, - .halt = pnv_halt, - .get_boot_time = pnv_get_boot_time, - .get_rtc_time = pnv_get_rtc_time, - .set_rtc_time = pnv_set_rtc_time, .progress = pnv_progress, .power_save = power7_idle, .calibrate_decr = generic_calibrate_decr, -- cgit v1.2.3-18-g5258 From 5c7c1e9444d8bfb721a27a35bba3eeb5236c75d8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:02 +0000 Subject: powerpc/powernv: Add OPAL ICS backend OPAL handles HW access to the various ICS or equivalent chips for us (with the exception of p5ioc2 based HEA which uses a different backend) similarily to what RTAS does on pSeries. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/xics.h | 19 +++ arch/powerpc/sysdev/xics/Makefile | 1 + arch/powerpc/sysdev/xics/ics-opal.c | 244 +++++++++++++++++++++++++++++++++ arch/powerpc/sysdev/xics/xics-common.c | 8 +- 4 files changed, 266 insertions(+), 6 deletions(-) create mode 100644 arch/powerpc/sysdev/xics/ics-opal.c diff --git a/arch/powerpc/include/asm/xics.h b/arch/powerpc/include/asm/xics.h index b183a406201..bd6c401c0ee 100644 --- a/arch/powerpc/include/asm/xics.h +++ b/arch/powerpc/include/asm/xics.h @@ -27,10 +27,18 @@ #define MAX_NUM_PRIORITIES 3 /* Native ICP */ +#ifdef CONFIG_PPC_ICP_NATIVE extern int icp_native_init(void); +#else +static inline int icp_native_init(void) { return -ENODEV; } +#endif /* PAPR ICP */ +#ifdef CONFIG_PPC_ICP_HV extern int icp_hv_init(void); +#else +static inline int icp_hv_init(void) { return -ENODEV; } +#endif /* ICP ops */ struct icp_ops { @@ -51,7 +59,18 @@ extern const struct icp_ops *icp_ops; extern int ics_native_init(void); /* RTAS ICS */ +#ifdef CONFIG_PPC_ICS_RTAS extern int ics_rtas_init(void); +#else +static inline int ics_rtas_init(void) { return -ENODEV; } +#endif + +/* HAL ICS */ +#ifdef CONFIG_PPC_POWERNV +extern int ics_opal_init(void); +#else +static inline int ics_opal_init(void) { return -ENODEV; } +#endif /* ICS instance, hooked up to chip_data of an irq */ struct ics { diff --git a/arch/powerpc/sysdev/xics/Makefile b/arch/powerpc/sysdev/xics/Makefile index b75a6059337..c606aa8ba60 100644 --- a/arch/powerpc/sysdev/xics/Makefile +++ b/arch/powerpc/sysdev/xics/Makefile @@ -4,3 +4,4 @@ obj-y += xics-common.o obj-$(CONFIG_PPC_ICP_NATIVE) += icp-native.o obj-$(CONFIG_PPC_ICP_HV) += icp-hv.o obj-$(CONFIG_PPC_ICS_RTAS) += ics-rtas.o +obj-$(CONFIG_PPC_POWERNV) += ics-opal.o diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c new file mode 100644 index 00000000000..f7e8609df0d --- /dev/null +++ b/arch/powerpc/sysdev/xics/ics-opal.c @@ -0,0 +1,244 @@ +/* + * ICS backend for OPAL managed interrupts. + * + * Copyright 2011 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. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int ics_opal_mangle_server(int server) +{ + /* No link for now */ + return server << 2; +} + +static int ics_opal_unmangle_server(int server) +{ + /* No link for now */ + return server >> 2; +} + +static void ics_opal_unmask_irq(struct irq_data *d) +{ + unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + int64_t rc; + int server; + + pr_devel("ics-hal: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq); + + if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) + return; + + server = xics_get_irq_server(d->irq, d->affinity, 0); + server = ics_opal_mangle_server(server); + + rc = opal_set_xive(hw_irq, server, DEFAULT_PRIORITY); + if (rc != OPAL_SUCCESS) + pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" + " error %lld\n", + __func__, d->irq, hw_irq, server, rc); +} + +static unsigned int ics_opal_startup(struct irq_data *d) +{ +#ifdef CONFIG_PCI_MSI + /* + * 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); +#endif + + /* unmask it */ + ics_opal_unmask_irq(d); + return 0; +} + +static void ics_opal_mask_real_irq(unsigned int hw_irq) +{ + int server = ics_opal_mangle_server(xics_default_server); + int64_t rc; + + if (hw_irq == XICS_IPI) + return; + + /* Have to set XIVE to 0xff to be able to remove a slot */ + rc = opal_set_xive(hw_irq, server, 0xff); + if (rc != OPAL_SUCCESS) + pr_err("%s: opal_set_xive(0xff) irq=%u returned %lld\n", + __func__, hw_irq, rc); +} + +static void ics_opal_mask_irq(struct irq_data *d) +{ + unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + + pr_devel("ics-hal: mask virq %d [hw 0x%x]\n", d->irq, hw_irq); + + if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) + return; + ics_opal_mask_real_irq(hw_irq); +} + +static int ics_opal_set_affinity(struct irq_data *d, + const struct cpumask *cpumask, + bool force) +{ + unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + int16_t server; + int8_t priority; + int64_t rc; + int wanted_server; + + if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) + return -1; + + rc = opal_get_xive(hw_irq, &server, &priority); + if (rc != OPAL_SUCCESS) { + pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" + " error %lld\n", + __func__, d->irq, hw_irq, server, rc); + return -1; + } + + wanted_server = xics_get_irq_server(d->irq, cpumask, 1); + if (wanted_server < 0) { + 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; + } + server = ics_opal_mangle_server(wanted_server); + + pr_devel("ics-hal: set-affinity irq %d [hw 0x%x] server: 0x%x/0x%x\n", + d->irq, hw_irq, wanted_server, server); + + rc = opal_set_xive(hw_irq, server, priority); + if (rc != OPAL_SUCCESS) { + pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" + " error %lld\n", + __func__, d->irq, hw_irq, server, rc); + return -1; + } + return 0; +} + +static struct irq_chip ics_opal_irq_chip = { + .name = "OPAL ICS", + .irq_startup = ics_opal_startup, + .irq_mask = ics_opal_mask_irq, + .irq_unmask = ics_opal_unmask_irq, + .irq_eoi = NULL, /* Patched at init time */ + .irq_set_affinity = ics_opal_set_affinity +}; + +static int ics_opal_map(struct ics *ics, unsigned int virq); +static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec); +static long ics_opal_get_server(struct ics *ics, unsigned long vec); + +static int ics_opal_host_match(struct ics *ics, struct device_node *node) +{ + return 1; +} + +/* Only one global & state struct ics */ +static struct ics ics_hal = { + .map = ics_opal_map, + .mask_unknown = ics_opal_mask_unknown, + .get_server = ics_opal_get_server, + .host_match = ics_opal_host_match, +}; + +static int ics_opal_map(struct ics *ics, unsigned int virq) +{ + unsigned int hw_irq = (unsigned int)virq_to_hw(virq); + int64_t rc; + int16_t server; + int8_t priority; + + if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)) + return -EINVAL; + + /* Check if HAL knows about this interrupt */ + rc = opal_get_xive(hw_irq, &server, &priority); + if (rc != OPAL_SUCCESS) + return -ENXIO; + + irq_set_chip_and_handler(virq, &ics_opal_irq_chip, handle_fasteoi_irq); + irq_set_chip_data(virq, &ics_hal); + + return 0; +} + +static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec) +{ + int64_t rc; + int16_t server; + int8_t priority; + + /* Check if HAL knows about this interrupt */ + rc = opal_get_xive(vec, &server, &priority); + if (rc != OPAL_SUCCESS) + return; + + ics_opal_mask_real_irq(vec); +} + +static long ics_opal_get_server(struct ics *ics, unsigned long vec) +{ + int64_t rc; + int16_t server; + int8_t priority; + + /* Check if HAL knows about this interrupt */ + rc = opal_get_xive(vec, &server, &priority); + if (rc != OPAL_SUCCESS) + return -1; + return ics_opal_unmangle_server(server); +} + +int __init ics_opal_init(void) +{ + if (!firmware_has_feature(FW_FEATURE_OPAL)) + return -ENODEV; + + /* We need to patch our irq chip's EOI to point to the + * right ICP + */ + ics_opal_irq_chip.irq_eoi = icp_ops->eoi; + + /* Register ourselves */ + xics_register_ics(&ics_hal); + + pr_info("ICS OPAL backend registered\n"); + + return 0; +} diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c index 445c5a01b76..3d93a8ded0f 100644 --- a/arch/powerpc/sysdev/xics/xics-common.c +++ b/arch/powerpc/sysdev/xics/xics-common.c @@ -409,14 +409,10 @@ void __init xics_init(void) int rc = -1; /* Fist locate ICP */ -#ifdef CONFIG_PPC_ICP_HV if (firmware_has_feature(FW_FEATURE_LPAR)) rc = icp_hv_init(); -#endif -#ifdef CONFIG_PPC_ICP_NATIVE if (rc < 0) rc = icp_native_init(); -#endif if (rc < 0) { pr_warning("XICS: Cannot find a Presentation Controller !\n"); return; @@ -429,9 +425,9 @@ void __init xics_init(void) xics_ipi_chip.irq_eoi = icp_ops->eoi; /* Now locate ICS */ -#ifdef CONFIG_PPC_ICS_RTAS rc = ics_rtas_init(); -#endif + if (rc < 0) + rc = ics_opal_init(); if (rc < 0) pr_warning("XICS: Cannot find a Source Controller !\n"); -- cgit v1.2.3-18-g5258 From a125e0928c736bc50cdd9a13151d4f4ee7821266 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:03 +0000 Subject: powerpc/powernv: Register and handle OPAL interrupts We do the minimum which is to "pass" interrupts to HAL, which makes the console smoother and will allow us to implement interrupt based completion and console. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/opal.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 7887733b9b3..5a598caefcb 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -135,9 +136,22 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) return written; } +static irqreturn_t opal_interrupt(int irq, void *data) +{ + uint64_t events; + + opal_handle_interrupt(virq_to_hw(irq), &events); + + /* XXX TODO: Do something with the events */ + + return IRQ_HANDLED; +} + static int __init opal_init(void) { struct device_node *np, *consoles; + const u32 *irqs; + int rc, i, irqlen; opal_node = of_find_node_by_path("/ibm,opal"); if (!opal_node) { @@ -156,6 +170,23 @@ static int __init opal_init(void) of_platform_device_create(np, NULL, NULL); } of_node_put(consoles); + + /* Find all OPAL interrupts and request them */ + irqs = of_get_property(opal_node, "opal-interrupts", &irqlen); + pr_debug("opal: Found %d interrupts reserved for OPAL\n", + irqs ? (irqlen / 4) : 0); + for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) { + unsigned int hwirq = be32_to_cpup(irqs); + unsigned int irq = irq_create_mapping(NULL, hwirq); + if (irq == NO_IRQ) { + pr_warning("opal: Failed to map irq 0x%x\n", hwirq); + continue; + } + rc = request_irq(irq, opal_interrupt, 0, "opal", NULL); + if (rc) + pr_warning("opal: Error %d requesting irq %d" + " (0x%x)\n", rc, irq, hwirq); + } return 0; } subsys_initcall(opal_init); -- cgit v1.2.3-18-g5258 From ed79ba9e15f84cef05aba5cbfe6e93f9b43c31f4 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:04 +0000 Subject: powerpc/powernv: Machine check and other system interrupts OPAL can handle various interrupt for us such as Machine Checks (it performs all sorts of recovery tasks and passes back control to us with informations about the error), Hardware Management Interrupts and Softpatch interrupts. This wires up the mechanisms and prints out specific informations returned by HAL when a machine check occurs. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/opal.h | 2 + arch/powerpc/include/asm/paca.h | 8 ++ arch/powerpc/kernel/asm-offsets.c | 10 +++ arch/powerpc/kernel/exceptions-64s.S | 27 ++++++- arch/powerpc/platforms/powernv/opal.c | 130 +++++++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/setup.c | 1 + 6 files changed, 174 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 77ebe50020a..2893e8f5406 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -436,6 +436,8 @@ extern void opal_get_rtc_time(struct rtc_time *tm); extern unsigned long opal_get_boot_time(void); extern void opal_nvram_init(void); +extern int opal_machine_check(struct pt_regs *regs); + #endif /* __ASSEMBLY__ */ #endif /* __OPAL_H */ diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index 516bfb3f47d..17722c73ba2 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -43,6 +43,7 @@ extern unsigned int debug_smp_processor_id(void); /* from linux/smp.h */ #define get_slb_shadow() (get_paca()->slb_shadow_ptr) struct task_struct; +struct opal_machine_check_event; /* * Defines the layout of the paca. @@ -135,6 +136,13 @@ struct paca_struct { u8 io_sync; /* writel() needs spin_unlock sync */ u8 irq_work_pending; /* IRQ_WORK interrupt while soft-disable */ +#ifdef CONFIG_PPC_POWERNV + /* Pointer to OPAL machine check event structure set by the + * early exception handler for use by high level C handler + */ + struct opal_machine_check_event *opal_mc_evt; +#endif + /* Stuff for accurate time accounting */ u64 user_time; /* accumulated usermode TB ticks */ u64 system_time; /* accumulated system TB ticks */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 5f078bc2063..536ffa897c6 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -48,6 +48,9 @@ #ifdef CONFIG_PPC_ISERIES #include #endif +#ifdef CONFIG_PPC_POWERNV +#include +#endif #if defined(CONFIG_KVM) || defined(CONFIG_KVM_GUEST) #include #endif @@ -609,5 +612,12 @@ int main(void) arch.timing_last_enter.tv32.tbl)); #endif +#ifdef CONFIG_PPC_POWERNV + DEFINE(OPAL_MC_GPR3, offsetof(struct opal_machine_check_event, gpr3)); + DEFINE(OPAL_MC_SRR0, offsetof(struct opal_machine_check_event, srr0)); + DEFINE(OPAL_MC_SRR1, offsetof(struct opal_machine_check_event, srr1)); + DEFINE(PACA_OPAL_MC_EVT, offsetof(struct paca_struct, opal_mc_evt)); +#endif + return 0; } diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 41b02c792aa..d51458fa8de 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -1143,7 +1143,7 @@ _GLOBAL(do_stab_bolted) rfid b . /* prevent speculative execution */ -#ifdef CONFIG_PPC_PSERIES +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* * Data area reserved for FWNMI option. * This address (0x7000) is fixed by the RPA. @@ -1151,7 +1151,7 @@ _GLOBAL(do_stab_bolted) .= 0x7000 .globl fwnmi_data_area fwnmi_data_area: -#endif /* CONFIG_PPC_PSERIES */ +#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */ /* iSeries does not use the FWNMI stuff, so it is safe to put * this here, even if we later allow kernels that will boot on @@ -1176,9 +1176,12 @@ xLparMap: #endif /* CONFIG_PPC_ISERIES */ -#ifdef CONFIG_PPC_PSERIES +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) + /* pseries and powernv need to keep the whole page from + * 0x7000 to 0x8000 free for use by the firmware + */ . = 0x8000 -#endif /* CONFIG_PPC_PSERIES */ +#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */ /* * Space for CPU0's segment table. @@ -1193,3 +1196,19 @@ xLparMap: .globl initial_stab initial_stab: .space 4096 +#ifdef CONFIG_PPC_POWERNV +_GLOBAL(opal_mc_secondary_handler) + HMT_MEDIUM + SET_SCRATCH0(r13) + GET_PACA(r13) + clrldi r3,r3,2 + tovirt(r3,r3) + std r3,PACA_OPAL_MC_EVT(r13) + ld r13,OPAL_MC_SRR0(r3) + mtspr SPRN_SRR0,r13 + ld r13,OPAL_MC_SRR1(r3) + mtspr SPRN_SRR1,r13 + ld r3,OPAL_MC_GPR3(r3) + GET_SCRATCH0(r13) + b machine_check_pSeries +#endif /* CONFIG_PPC_POWERNV */ diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 5a598caefcb..aaa0dba4947 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -27,12 +27,14 @@ struct opal { static struct device_node *opal_node; static DEFINE_SPINLOCK(opal_write_lock); +extern u64 opal_mc_secondary_handler[]; int __init early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data) { const void *basep, *entryp; unsigned long basesz, entrysz; + u64 glue; if (depth != 1 || strcmp(uname, "ibm,opal") != 0) return 0; @@ -59,6 +61,19 @@ int __init early_init_dt_scan_opal(unsigned long node, printk("OPAL V1 detected !\n"); } + /* Hookup some exception handlers. We use the fwnmi area at 0x7000 + * to provide the glue space to OPAL + */ + glue = 0x7000; + opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER, + __pa(opal_mc_secondary_handler[0]), + glue); + glue += 128; + opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER, + 0, glue); + glue += 128; + opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); + return 1; } @@ -136,6 +151,121 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) return written; } +int opal_machine_check(struct pt_regs *regs) +{ + struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt; + struct opal_machine_check_event evt; + const char *level, *sevstr, *subtype; + static const char *opal_mc_ue_types[] = { + "Indeterminate", + "Instruction fetch", + "Page table walk ifetch", + "Load/Store", + "Page table walk Load/Store", + }; + static const char *opal_mc_slb_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + static const char *opal_mc_erat_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + static const char *opal_mc_tlb_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + + /* Copy the event structure and release the original */ + evt = *opal_evt; + opal_evt->in_use = 0; + + /* Print things out */ + if (evt.version != OpalMCE_V1) { + pr_err("Machine Check Exception, Unknown event version %d !\n", + evt.version); + return 0; + } + switch(evt.severity) { + case OpalMCE_SEV_NO_ERROR: + level = KERN_INFO; + sevstr = "Harmless"; + break; + case OpalMCE_SEV_WARNING: + level = KERN_WARNING; + sevstr = ""; + break; + case OpalMCE_SEV_ERROR_SYNC: + level = KERN_ERR; + sevstr = "Severe"; + break; + case OpalMCE_SEV_FATAL: + default: + level = KERN_ERR; + sevstr = "Fatal"; + break; + } + + printk("%s%s Machine check interrupt [%s]\n", level, sevstr, + evt.disposition == OpalMCE_DISPOSITION_RECOVERED ? + "Recovered" : "[Not recovered"); + printk("%s Initiator: %s\n", level, + evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown"); + switch(evt.error_type) { + case OpalMCE_ERROR_TYPE_UE: + subtype = evt.u.ue_error.ue_error_type < + ARRAY_SIZE(opal_mc_ue_types) ? + opal_mc_ue_types[evt.u.ue_error.ue_error_type] + : "Unknown"; + printk("%s Error type: UE [%s]\n", level, subtype); + if (evt.u.ue_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.ue_error.effective_address); + if (evt.u.ue_error.physical_address_provided) + printk("%s Physial address: %016llx\n", + level, evt.u.ue_error.physical_address); + break; + case OpalMCE_ERROR_TYPE_SLB: + subtype = evt.u.slb_error.slb_error_type < + ARRAY_SIZE(opal_mc_slb_types) ? + opal_mc_slb_types[evt.u.slb_error.slb_error_type] + : "Unknown"; + printk("%s Error type: SLB [%s]\n", level, subtype); + if (evt.u.slb_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.slb_error.effective_address); + break; + case OpalMCE_ERROR_TYPE_ERAT: + subtype = evt.u.erat_error.erat_error_type < + ARRAY_SIZE(opal_mc_erat_types) ? + opal_mc_erat_types[evt.u.erat_error.erat_error_type] + : "Unknown"; + printk("%s Error type: ERAT [%s]\n", level, subtype); + if (evt.u.erat_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.erat_error.effective_address); + break; + case OpalMCE_ERROR_TYPE_TLB: + subtype = evt.u.tlb_error.tlb_error_type < + ARRAY_SIZE(opal_mc_tlb_types) ? + opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type] + : "Unknown"; + printk("%s Error type: TLB [%s]\n", level, subtype); + if (evt.u.tlb_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.tlb_error.effective_address); + break; + default: + case OpalMCE_ERROR_TYPE_UNKNOWN: + printk("%s Error type: Unknown\n", level); + break; + } + return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1; +} + static irqreturn_t opal_interrupt(int irq, void *data) { uint64_t events; diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 4a2b2e27959..f0242f3fd3e 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -141,6 +141,7 @@ static void __init pnv_setup_machdep_opal(void) ppc_md.restart = pnv_restart; ppc_md.power_off = pnv_power_off; ppc_md.halt = pnv_halt; + ppc_md.machine_check_exception = opal_machine_check; } #ifdef CONFIG_PPC_POWERNV_RTAS -- cgit v1.2.3-18-g5258 From 61305a96fad622ae0f0e78cb06f67ad721d378f9 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:05 +0000 Subject: powerpc/powernv: Add support for p5ioc2 PCI-X and PCIe This adds support for PCI-X and PCIe on the p5ioc2 IO hub using OPAL. This includes allocating & setting up TCE tables and config space access routines. This also supports fallbacks via RTAS when OPAL is absent, using legacy TCE format pre-allocated via the device-tree (BML style) Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/Makefile | 1 + arch/powerpc/platforms/powernv/pci-p5ioc2.c | 185 ++++++++++++++++++ arch/powerpc/platforms/powernv/pci.c | 286 ++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/pci.h | 38 ++++ arch/powerpc/platforms/powernv/powernv.h | 6 + arch/powerpc/platforms/powernv/setup.c | 3 +- 6 files changed, 518 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/platforms/powernv/pci-p5ioc2.c create mode 100644 arch/powerpc/platforms/powernv/pci.c create mode 100644 arch/powerpc/platforms/powernv/pci.h diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 618ad836f28..31853008b41 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -2,3 +2,4 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o obj-y += opal-rtc.o opal-nvram.o obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c new file mode 100644 index 00000000000..afabc2bcae4 --- /dev/null +++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c @@ -0,0 +1,185 @@ +/* + * Support PCI/PCIe on PowerNV platforms + * + * Currently supports only P5IOC2 + * + * Copyright 2011 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "powernv.h" +#include "pci.h" + +/* For now, use a fixed amount of TCE memory for each p5ioc2 + * hub, 16M will do + */ +#define P5IOC2_TCE_MEMORY 0x01000000 + +static void __devinit pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb, + struct pci_dev *pdev) +{ + if (phb->p5ioc2.iommu_table.it_map == NULL) + iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node); + + set_iommu_table_base(&pdev->dev, &phb->p5ioc2.iommu_table); +} + +static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, + void *tce_mem, u64 tce_size) +{ + struct pnv_phb *phb; + const u64 *prop64; + u64 phb_id; + int64_t rc; + static int primary = 1; + + pr_info(" Initializing p5ioc2 PHB %s\n", np->full_name); + + prop64 = of_get_property(np, "ibm,opal-phbid", NULL); + if (!prop64) { + pr_err(" Missing \"ibm,opal-phbid\" property !\n"); + return; + } + phb_id = be64_to_cpup(prop64); + pr_devel(" PHB-ID : 0x%016llx\n", phb_id); + pr_devel(" TCE AT : 0x%016lx\n", __pa(tce_mem)); + pr_devel(" TCE SZ : 0x%016llx\n", tce_size); + + rc = opal_pci_set_phb_tce_memory(phb_id, __pa(tce_mem), tce_size); + if (rc != OPAL_SUCCESS) { + pr_err(" Failed to set TCE memory, OPAL error %lld\n", rc); + return; + } + + phb = alloc_bootmem(sizeof(struct pnv_phb)); + if (phb) { + memset(phb, 0, sizeof(struct pnv_phb)); + phb->hose = pcibios_alloc_controller(np); + } + if (!phb || !phb->hose) { + pr_err(" Failed to allocate PCI controller\n"); + return; + } + + spin_lock_init(&phb->lock); + phb->hose->first_busno = 0; + phb->hose->last_busno = 0xff; + phb->hose->private_data = phb; + phb->opal_id = phb_id; + phb->type = PNV_PHB_P5IOC2; + + phb->regs = of_iomap(np, 0); + + if (phb->regs == NULL) + pr_err(" Failed to map registers !\n"); + else { + pr_devel(" P_BUID = 0x%08x\n", in_be32(phb->regs + 0x100)); + pr_devel(" P_IOSZ = 0x%08x\n", in_be32(phb->regs + 0x1b0)); + pr_devel(" P_IO_ST = 0x%08x\n", in_be32(phb->regs + 0x1e0)); + pr_devel(" P_MEM1_H = 0x%08x\n", in_be32(phb->regs + 0x1a0)); + pr_devel(" P_MEM1_L = 0x%08x\n", in_be32(phb->regs + 0x190)); + pr_devel(" P_MSZ1_L = 0x%08x\n", in_be32(phb->regs + 0x1c0)); + pr_devel(" P_MEM_ST = 0x%08x\n", in_be32(phb->regs + 0x1d0)); + pr_devel(" P_MEM2_H = 0x%08x\n", in_be32(phb->regs + 0x2c0)); + pr_devel(" P_MEM2_L = 0x%08x\n", in_be32(phb->regs + 0x2b0)); + pr_devel(" P_MSZ2_H = 0x%08x\n", in_be32(phb->regs + 0x2d0)); + pr_devel(" P_MSZ2_L = 0x%08x\n", in_be32(phb->regs + 0x2e0)); + } + + /* Interpret the "ranges" property */ + /* This also maps the I/O region and sets isa_io/mem_base */ + pci_process_bridge_OF_ranges(phb->hose, np, primary); + primary = 0; + + phb->hose->ops = &pnv_pci_ops; + + /* Setup TCEs */ + phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup; + pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table, + tce_mem, tce_size, 0); +} + +void __init pnv_pci_init_p5ioc2_hub(struct device_node *np) +{ + struct device_node *phbn; + const u64 *prop64; + u64 hub_id; + void *tce_mem; + uint64_t tce_per_phb; + int64_t rc; + int phb_count = 0; + + pr_info("Probing p5ioc2 IO-Hub %s\n", np->full_name); + + prop64 = of_get_property(np, "ibm,opal-hubid", NULL); + if (!prop64) { + pr_err(" Missing \"ibm,opal-hubid\" property !\n"); + return; + } + hub_id = be64_to_cpup(prop64); + pr_info(" HUB-ID : 0x%016llx\n", hub_id); + + /* Currently allocate 16M of TCE memory for every Hub + * + * XXX TODO: Make it chip local if possible + */ + tce_mem = __alloc_bootmem(P5IOC2_TCE_MEMORY, P5IOC2_TCE_MEMORY, + __pa(MAX_DMA_ADDRESS)); + if (!tce_mem) { + pr_err(" Failed to allocate TCE Memory !\n"); + return; + } + pr_debug(" TCE : 0x%016lx..0x%016lx\n", + __pa(tce_mem), __pa(tce_mem) + P5IOC2_TCE_MEMORY - 1); + rc = opal_pci_set_hub_tce_memory(hub_id, __pa(tce_mem), + P5IOC2_TCE_MEMORY); + if (rc != OPAL_SUCCESS) { + pr_err(" Failed to allocate TCE memory, OPAL error %lld\n", rc); + return; + } + + /* Count child PHBs */ + for_each_child_of_node(np, phbn) { + if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") || + of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) + phb_count++; + } + + /* Calculate how much TCE space we can give per PHB */ + tce_per_phb = __rounddown_pow_of_two(P5IOC2_TCE_MEMORY / phb_count); + pr_info(" Allocating %lld MB of TCE memory per PHB\n", + tce_per_phb >> 20); + + /* Initialize PHBs */ + for_each_child_of_node(np, phbn) { + if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") || + of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) { + pnv_pci_init_p5ioc2_phb(phbn, tce_mem, tce_per_phb); + tce_mem += tce_per_phb; + } + } +} diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c new file mode 100644 index 00000000000..746ce5e5120 --- /dev/null +++ b/arch/powerpc/platforms/powernv/pci.c @@ -0,0 +1,286 @@ +/* + * Support PCI/PCIe on PowerNV platforms + * + * Currently supports only P5IOC2 + * + * Copyright 2011 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "powernv.h" +#include "pci.h" + + +#define cfg_dbg(fmt...) do { } while(0) +//#define cfg_dbg(fmt...) printk(fmt) + + +static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, + u32 bdfn) +{ + s64 rc; + u8 fstate; + u16 pcierr; + u32 pe_no; + + /* Get PE# if we support IODA */ + pe_no = phb->bdfn_to_pe ? phb->bdfn_to_pe(phb, bus, bdfn & 0xff) : 0; + + /* Read freeze status */ + rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr, + NULL); + if (rc) { + pr_warning("PCI %d: Failed to read EEH status for PE#%d," + " err %lld\n", phb->hose->global_number, pe_no, rc); + return; + } + cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n", + bdfn, pe_no, fstate); + if (fstate != 0) { + rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no, + OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); + if (rc) { + pr_warning("PCI %d: Failed to clear EEH freeze state" + " for PE#%d, err %lld\n", + phb->hose->global_number, pe_no, rc); + } + } +} + +static int pnv_pci_read_config(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 *val) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + struct pnv_phb *phb = hose->private_data; + u32 bdfn = (((uint64_t)bus->number) << 8) | devfn; + s64 rc; + + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: { + u8 v8; + rc = opal_pci_config_read_byte(phb->opal_id, bdfn, where, &v8); + *val = (rc == OPAL_SUCCESS) ? v8 : 0xff; + break; + } + case 2: { + u16 v16; + rc = opal_pci_config_read_half_word(phb->opal_id, bdfn, where, + &v16); + *val = (rc == OPAL_SUCCESS) ? v16 : 0xffff; + break; + } + case 4: { + u32 v32; + rc = opal_pci_config_read_word(phb->opal_id, bdfn, where, &v32); + *val = (rc == OPAL_SUCCESS) ? v32 : 0xffffffff; + break; + } + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + cfg_dbg("pnv_pci_read_config bus: %x devfn: %x +%x/%x -> %08x\n", + bus->number, devfn, where, size, *val); + + /* Check if the PHB got frozen due to an error (no response) */ + pnv_pci_config_check_eeh(phb, bus, bdfn); + + return PCIBIOS_SUCCESSFUL; +} + +static int pnv_pci_write_config(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 val) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + struct pnv_phb *phb = hose->private_data; + u32 bdfn = (((uint64_t)bus->number) << 8) | devfn; + + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + cfg_dbg("pnv_pci_write_config bus: %x devfn: %x +%x/%x -> %08x\n", + bus->number, devfn, where, size, val); + switch (size) { + case 1: + opal_pci_config_write_byte(phb->opal_id, bdfn, where, val); + break; + case 2: + opal_pci_config_write_half_word(phb->opal_id, bdfn, where, val); + break; + case 4: + opal_pci_config_write_word(phb->opal_id, bdfn, where, val); + break; + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + /* Check if the PHB got frozen due to an error (no response) */ + pnv_pci_config_check_eeh(phb, bus, bdfn); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops pnv_pci_ops = { + .read = pnv_pci_read_config, + .write = pnv_pci_write_config, +}; + +static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, + unsigned long uaddr, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + u64 proto_tce; + u64 *tcep; + u64 rpn; + + proto_tce = TCE_PCI_READ; // Read allowed + + if (direction != DMA_TO_DEVICE) + proto_tce |= TCE_PCI_WRITE; + + tcep = ((u64 *)tbl->it_base) + index; + + while (npages--) { + /* can't move this out since we might cross LMB boundary */ + rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; + *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; + + uaddr += TCE_PAGE_SIZE; + tcep++; + } + return 0; +} + +static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) +{ + u64 *tcep = ((u64 *)tbl->it_base) + index; + + while (npages--) + *(tcep++) = 0; +} + +void pnv_pci_setup_iommu_table(struct iommu_table *tbl, + void *tce_mem, u64 tce_size, + u64 dma_offset) +{ + tbl->it_blocksize = 16; + tbl->it_base = (unsigned long)tce_mem; + tbl->it_offset = dma_offset >> IOMMU_PAGE_SHIFT; + tbl->it_index = 0; + tbl->it_size = tce_size >> 3; + tbl->it_busno = 0; + tbl->it_type = TCE_PCI; +} + +static struct iommu_table * __devinit +pnv_pci_setup_bml_iommu(struct pci_controller *hose) +{ + struct iommu_table *tbl; + const __be64 *basep; + const __be32 *sizep; + + basep = of_get_property(hose->dn, "linux,tce-base", NULL); + sizep = of_get_property(hose->dn, "linux,tce-size", NULL); + if (basep == NULL || sizep == NULL) { + pr_err("PCI: %s has missing tce entries !\n", hose->dn->full_name); + return NULL; + } + tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, hose->node); + if (WARN_ON(!tbl)) + return NULL; + pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)), + be32_to_cpup(sizep), 0); + iommu_init_table(tbl, hose->node); + return tbl; +} + +static void __devinit pnv_pci_dma_fallback_setup(struct pci_controller *hose, + struct pci_dev *pdev) +{ + struct device_node *np = pci_bus_to_OF_node(hose->bus); + struct pci_dn *pdn; + + if (np == NULL) + return; + pdn = PCI_DN(np); + if (!pdn->iommu_table) + pdn->iommu_table = pnv_pci_setup_bml_iommu(hose); + if (!pdn->iommu_table) + return; + set_iommu_table_base(&pdev->dev, pdn->iommu_table); +} + +static void __devinit pnv_pci_dma_dev_setup(struct pci_dev *pdev) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + + /* If we have no phb structure, try to setup a fallback based on + * the device-tree (RTAS PCI for example) + */ + if (phb && phb->dma_dev_setup) + phb->dma_dev_setup(phb, pdev); + else + pnv_pci_dma_fallback_setup(hose, pdev); +} + +void __init pnv_pci_init(void) +{ + struct device_node *np; + + pci_set_flags(PCI_CAN_SKIP_ISA_ALIGN); + + /* We do not want to just probe */ + pci_probe_only = 0; + + /* OPAL absent, try POPAL first then RTAS detection of PHBs */ + if (!firmware_has_feature(FW_FEATURE_OPAL)) { +#ifdef CONFIG_PPC_POWERNV_RTAS + init_pci_config_tokens(); + find_and_init_phbs(); +#endif /* CONFIG_PPC_POWERNV_RTAS */ + } else { + /* OPAL is here, do our normal stuff */ + + /* Look for p5ioc2 IO-Hubs */ + for_each_compatible_node(np, NULL, "ibm,p5ioc2") + pnv_pci_init_p5ioc2_hub(np); + } + + /* Setup the linkage between OF nodes and PHBs */ + pci_devs_phb_init(); + + /* Configure IOMMU DMA hooks */ + ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup; + ppc_md.tce_build = pnv_tce_build; + ppc_md.tce_free = pnv_tce_free; + set_pci_dma_ops(&dma_iommu_ops); + +} diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h new file mode 100644 index 00000000000..6730a10d736 --- /dev/null +++ b/arch/powerpc/platforms/powernv/pci.h @@ -0,0 +1,38 @@ +#ifndef __POWERNV_PCI_H +#define __POWERNV_PCI_H + +struct pci_dn; + +enum pnv_phb_type { + PNV_PHB_P5IOC2, + PNV_PHB_IODA1, + PNV_PHB_IODA2, +}; + +struct pnv_phb { + struct pci_controller *hose; + enum pnv_phb_type type; + u64 opal_id; + void __iomem *regs; + spinlock_t lock; + + void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev); + void (*fixup_phb)(struct pci_controller *hose); + u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn); + + union { + struct { + struct iommu_table iommu_table; + } p5ioc2; + }; +}; + +extern struct pci_ops pnv_pci_ops; + +extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl, + void *tce_mem, u64 tce_size, + u64 dma_offset); +extern void pnv_pci_init_p5ioc2_hub(struct device_node *np); + + +#endif /* __POWERNV_PCI_H */ diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h index 35b716008cd..8a9df7f9667 100644 --- a/arch/powerpc/platforms/powernv/powernv.h +++ b/arch/powerpc/platforms/powernv/powernv.h @@ -7,4 +7,10 @@ extern void pnv_smp_init(void); static inline void pnv_smp_init(void) { } #endif +#ifdef CONFIG_PCI +extern void pnv_pci_init(void); +#else +static inline void pnv_pci_init(void) { } +#endif + #endif /* _POWERNV_H */ diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index f0242f3fd3e..467bd4ac682 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -40,7 +40,8 @@ static void __init pnv_setup_arch(void) /* Initialize SMP */ pnv_smp_init(); - /* XXX PCI */ + /* Setup PCI */ + pnv_pci_init(); /* Setup RTC and NVRAM callbacks */ if (firmware_has_feature(FW_FEATURE_OPAL)) -- cgit v1.2.3-18-g5258 From c1a2562ac5edcb3965760f4a37368122d85657af Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:06 +0000 Subject: powerpc/powernv: Implement MSI support for p5ioc2 PCIe This implements support for MSIs on p5ioc2 PHBs. We only support MSIs on the PCIe PHBs, not the PCI-X ones as the later hasn't been properly verified in HW. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/pci-p5ioc2.c | 49 +++++++++++++ arch/powerpc/platforms/powernv/pci.c | 109 ++++++++++++++++++++++++++++ arch/powerpc/platforms/powernv/pci.h | 10 +++ 3 files changed, 168 insertions(+) diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c index afabc2bcae4..4c80f7c77d5 100644 --- a/arch/powerpc/platforms/powernv/pci-p5ioc2.c +++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,51 @@ */ #define P5IOC2_TCE_MEMORY 0x01000000 +#ifdef CONFIG_PCI_MSI +static int pnv_pci_p5ioc2_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, + unsigned int hwirq, unsigned int is_64, + struct msi_msg *msg) +{ + if (WARN_ON(!is_64)) + return -ENXIO; + msg->data = hwirq - phb->msi_base; + msg->address_hi = 0x10000000; + msg->address_lo = 0; + + return 0; +} + +static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) +{ + unsigned int bmap_size; + const __be32 *prop = of_get_property(phb->hose->dn, + "ibm,opal-msi-ranges", NULL); + if (!prop) + return; + + /* Don't do MSI's on p5ioc2 PCI-X are they are not properly + * verified in HW + */ + if (of_device_is_compatible(phb->hose->dn, "ibm,p5ioc2-pcix")) + return; + phb->msi_base = be32_to_cpup(prop); + phb->msi_count = be32_to_cpup(prop + 1); + bmap_size = BITS_TO_LONGS(phb->msi_count) * sizeof(unsigned long); + phb->msi_map = zalloc_maybe_bootmem(bmap_size, GFP_KERNEL); + if (!phb->msi_map) { + pr_err("PCI %d: Failed to allocate MSI bitmap !\n", + phb->hose->global_number); + return; + } + phb->msi_setup = pnv_pci_p5ioc2_msi_setup; + phb->msi32_support = 0; + pr_info(" Allocated bitmap for %d MSIs (base IRQ 0x%x)\n", + phb->msi_count, phb->msi_base); +} +#else +static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) { } +#endif /* CONFIG_PCI_MSI */ + static void __devinit pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb, struct pci_dev *pdev) { @@ -117,6 +163,9 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, phb->hose->ops = &pnv_pci_ops; + /* Setup MSI support */ + pnv_pci_init_p5ioc2_msis(phb); + /* Setup TCEs */ phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup; pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table, diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 746ce5e5120..5c175519cf9 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,108 @@ #define cfg_dbg(fmt...) do { } while(0) //#define cfg_dbg(fmt...) printk(fmt) +#ifdef CONFIG_PCI_MSI +static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + + return (phb && phb->msi_map) ? 0 : -ENODEV; +} + +static unsigned int pnv_get_one_msi(struct pnv_phb *phb) +{ + unsigned int id; + + spin_lock(&phb->lock); + id = find_next_zero_bit(phb->msi_map, phb->msi_count, phb->msi_next); + if (id >= phb->msi_count && phb->msi_next) + id = find_next_zero_bit(phb->msi_map, phb->msi_count, 0); + if (id >= phb->msi_count) { + spin_unlock(&phb->lock); + return 0; + } + __set_bit(id, phb->msi_map); + spin_unlock(&phb->lock); + return id + phb->msi_base; +} + +static void pnv_put_msi(struct pnv_phb *phb, unsigned int hwirq) +{ + unsigned int id; + + if (WARN_ON(hwirq < phb->msi_base || + hwirq >= (phb->msi_base + phb->msi_count))) + return; + id = hwirq - phb->msi_base; + spin_lock(&phb->lock); + __clear_bit(id, phb->msi_map); + spin_unlock(&phb->lock); +} + +static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + struct msi_desc *entry; + struct msi_msg msg; + unsigned int hwirq, virq; + int rc; + + if (WARN_ON(!phb)) + return -ENODEV; + + list_for_each_entry(entry, &pdev->msi_list, list) { + if (!entry->msi_attrib.is_64 && !phb->msi32_support) { + pr_warn("%s: Supports only 64-bit MSIs\n", + pci_name(pdev)); + return -ENXIO; + } + hwirq = pnv_get_one_msi(phb); + if (!hwirq) { + pr_warn("%s: Failed to find a free MSI\n", + pci_name(pdev)); + return -ENOSPC; + } + virq = irq_create_mapping(NULL, hwirq); + if (virq == NO_IRQ) { + pr_warn("%s: Failed to map MSI to linux irq\n", + pci_name(pdev)); + pnv_put_msi(phb, hwirq); + return -ENOMEM; + } + rc = phb->msi_setup(phb, pdev, hwirq, entry->msi_attrib.is_64, + &msg); + if (rc) { + pr_warn("%s: Failed to setup MSI\n", pci_name(pdev)); + irq_dispose_mapping(virq); + pnv_put_msi(phb, hwirq); + return rc; + } + irq_set_msi_desc(virq, entry); + write_msi_msg(virq, &msg); + } + return 0; +} + +static void pnv_teardown_msi_irqs(struct pci_dev *pdev) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + struct msi_desc *entry; + + if (WARN_ON(!phb)) + return; + + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + irq_set_msi_desc(entry->irq, NULL); + pnv_put_msi(phb, virq_to_hw(entry->irq)); + irq_dispose_mapping(entry->irq); + } +} +#endif /* CONFIG_PCI_MSI */ static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, u32 bdfn) @@ -283,4 +386,10 @@ void __init pnv_pci_init(void) ppc_md.tce_free = pnv_tce_free; set_pci_dma_ops(&dma_iommu_ops); + /* Configure MSIs */ +#ifdef CONFIG_PCI_MSI + ppc_md.msi_check_device = pnv_msi_check_device; + ppc_md.setup_msi_irqs = pnv_setup_msi_irqs; + ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs; +#endif } diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 6730a10d736..d4dbc495093 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -16,6 +16,16 @@ struct pnv_phb { void __iomem *regs; spinlock_t lock; +#ifdef CONFIG_PCI_MSI + unsigned long *msi_map; + unsigned int msi_base; + unsigned int msi_count; + unsigned int msi_next; + unsigned int msi32_support; +#endif + int (*msi_setup)(struct pnv_phb *phb, struct pci_dev *dev, + unsigned int hwirq, unsigned int is_64, + struct msi_msg *msg); void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev); void (*fixup_phb)(struct pci_controller *hose); u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn); -- cgit v1.2.3-18-g5258 From 82ba129baeb1ff72e75d93e70534ba50312153f3 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:07 +0000 Subject: powerpc/powernv: Handle PCI-X/PCIe reset delay The firmware doesn't wait after lifting the PCI reset. However it does timestamp it in the device tree. We use that to ensure we wait long enough (3s is our current arbitrary setting) from that timestamp to actually probing the bus. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/pci.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 5c175519cf9..85bb66d7f93 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -35,6 +35,8 @@ #include "powernv.h" #include "pci.h" +/* Delay in usec */ +#define PCI_RESET_DELAY_US 3000000 #define cfg_dbg(fmt...) do { } while(0) //#define cfg_dbg(fmt...) printk(fmt) @@ -354,6 +356,35 @@ static void __devinit pnv_pci_dma_dev_setup(struct pci_dev *pdev) pnv_pci_dma_fallback_setup(hose, pdev); } +static int pnv_pci_probe_mode(struct pci_bus *bus) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + const __be64 *tstamp; + u64 now, target; + + + /* We hijack this as a way to ensure we have waited long + * enough since the reset was lifted on the PCI bus + */ + if (bus != hose->bus) + return PCI_PROBE_NORMAL; + tstamp = of_get_property(hose->dn, "reset-clear-timestamp", NULL); + if (!tstamp || !*tstamp) + return PCI_PROBE_NORMAL; + + now = mftb() / tb_ticks_per_usec; + target = (be64_to_cpup(tstamp) / tb_ticks_per_usec) + + PCI_RESET_DELAY_US; + + pr_devel("pci %04d: Reset target: 0x%llx now: 0x%llx\n", + hose->global_number, target, now); + + if (now < target) + msleep((target - now + 999) / 1000); + + return PCI_PROBE_NORMAL; +} + void __init pnv_pci_init(void) { struct device_node *np; @@ -384,6 +415,7 @@ void __init pnv_pci_init(void) ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup; ppc_md.tce_build = pnv_tce_build; ppc_md.tce_free = pnv_tce_free; + ppc_md.pci_probe_mode = pnv_pci_probe_mode; set_pci_dma_ops(&dma_iommu_ops); /* Configure MSIs */ -- cgit v1.2.3-18-g5258 From a120db06c3f435c37d028b6e5a1968dad06b7df0 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 8 Sep 2011 21:12:06 +0000 Subject: perf events, powerpc: Add POWER7 stalled-cycles-frontend/backend events perf events, powerpc: Add POWER7 stalled-cycles-frontend/backend events Extent the POWER7 PMU driver with definitions for generic front-end and back-end stall events. As explained in Ingo's original comment(8f62242246351b5a4bc0c1f00c0c7003edea128a ), the exact definitions of the stall events are very much processor specific as different things mean different in their respective instruction pipeline. These two Power7 raw events are the closest approximation to the concept detailed in Ingo's comment. [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x100f8, /* GCT_NOSLOT_CYC */ It means cycles when the Global Completion Table has no slots from this thread [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x4000a, /* CMPLU_STALL */ It means no groups completed and GCT not empty for this thread Signed-off-by: Anshuman Khandual Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/power7-pmu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/kernel/power7-pmu.c b/arch/powerpc/kernel/power7-pmu.c index de83d6060dd..1251e4d7e26 100644 --- a/arch/powerpc/kernel/power7-pmu.c +++ b/arch/powerpc/kernel/power7-pmu.c @@ -297,6 +297,8 @@ static void power7_disable_pmc(unsigned int pmc, unsigned long mmcr[]) static int power7_generic_events[] = { [PERF_COUNT_HW_CPU_CYCLES] = 0x1e, + [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x100f8, /* GCT_NOSLOT_CYC */ + [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x4000a, /* CMPLU_STALL */ [PERF_COUNT_HW_INSTRUCTIONS] = 2, [PERF_COUNT_HW_CACHE_REFERENCES] = 0xc880, /* LD_REF_L1_LSU*/ [PERF_COUNT_HW_CACHE_MISSES] = 0x400f0, /* LD_MISS_L1 */ -- cgit v1.2.3-18-g5258 From 6cf1d0b806a9e621ef1a8e90009e37f3446c2b9e Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 16 May 2011 18:02:43 +0200 Subject: powerpc/5200: mpc5200b.dtsi: add spi node address- and size-cells properties Both, #address-cells and #size-cells properties are required for spi bus node, so add them. Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/mpc5200b.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/boot/dts/mpc5200b.dtsi b/arch/powerpc/boot/dts/mpc5200b.dtsi index bc27548e895..7ab286ab530 100644 --- a/arch/powerpc/boot/dts/mpc5200b.dtsi +++ b/arch/powerpc/boot/dts/mpc5200b.dtsi @@ -147,6 +147,8 @@ }; spi@f00 { + #address-cells = <1>; + #size-cells = <0>; compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi"; reg = <0xf00 0x20>; interrupts = <2 13 0 2 14 0>; -- cgit v1.2.3-18-g5258 From aa4593f2741946df5a47cbd679e1bc7da476e4bf Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 16 May 2011 18:02:44 +0200 Subject: powerpc/5200: dts: digsy_mtc.dts: update to add can, pci, serial and spi Add new nodes to describe more hardware the board is equipped with: - two can nodes for SJA1000 on localbus - pci node to support Coral-PA graphics controller - serial node for SC28L92 DUART on localbus - spi node for MSP430 device Also correct i2c eeprom node name. Signed-off-by: Heiko Schocher Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/digsy_mtc.dts | 50 ++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/boot/dts/digsy_mtc.dts b/arch/powerpc/boot/dts/digsy_mtc.dts index 27bd267d631..e205d17fd1f 100644 --- a/arch/powerpc/boot/dts/digsy_mtc.dts +++ b/arch/powerpc/boot/dts/digsy_mtc.dts @@ -38,6 +38,14 @@ status = "disabled"; }; + spi@f00 { + msp430@0 { + compatible = "spidev"; + spi-max-frequency = <32000>; + reg = <0>; + }; + }; + psc@2000 { // PSC1 status = "disabled"; }; @@ -73,11 +81,16 @@ }; i2c@3d00 { - rtc@50 { + eeprom@50 { compatible = "at,24c08"; reg = <0x50>; }; + rtc@56 { + compatible = "mc,rv3029c2"; + reg = <0x56>; + }; + rtc@68 { compatible = "dallas,ds1339"; reg = <0x68>; @@ -90,11 +103,22 @@ }; pci@f0000d00 { - status = "disabled"; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = <0xc000 0 0 1 &mpc5200_pic 0 0 3 + 0xc000 0 0 2 &mpc5200_pic 0 0 3 + 0xc000 0 0 3 &mpc5200_pic 0 0 3 + 0xc000 0 0 4 &mpc5200_pic 0 0 3>; + clock-frequency = <0>; // From boot loader + interrupts = <2 8 0 2 9 0 2 10 0>; + bus-range = <0 0>; + ranges = <0x42000000 0 0x80000000 0x80000000 0 0x10000000 + 0x02000000 0 0x90000000 0x90000000 0 0x10000000 + 0x01000000 0 0x00000000 0xa0000000 0 0x01000000>; }; localbus { - ranges = <0 0 0xff000000 0x1000000>; + ranges = <0 0 0xff000000 0x1000000 + 4 0 0x60000000 0x0001000>; // 16-bit flash device at LocalPlus Bus CS0 flash@0,0 { @@ -122,5 +146,25 @@ reg = <0x00f00000 0x100000>; }; }; + + can@4,0 { + compatible = "nxp,sja1000"; + reg = <4 0x000 0x80>; + nxp,external-clock-frequency = <24000000>; + interrupts = <1 2 3>; // Level-low + }; + + can@4,100 { + compatible = "nxp,sja1000"; + reg = <4 0x100 0x80>; + nxp,external-clock-frequency = <24000000>; + interrupts = <1 2 3>; // Level-low + }; + + serial@4,200 { + compatible = "nxp,sc28l92"; + reg = <4 0x200 0x10>; + interrupts = <1 3 3>; + }; }; }; -- cgit v1.2.3-18-g5258 From 1982c09a64b10c4407ef0b177ea7fcabc03d4add Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Tue, 7 Jun 2011 00:27:05 +0200 Subject: powerpc/5200: dts: digsy_mtc.dts: add timer0 and timer1 gpio properties timer0 and timer1 pins are used as simple GPIO on this board. Add gpio-controller and #gpio-cells properties to timer nodes so that we can control gpio lines using available MPC52xx GPT driver. Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/digsy_mtc.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/powerpc/boot/dts/digsy_mtc.dts b/arch/powerpc/boot/dts/digsy_mtc.dts index e205d17fd1f..2aad7ae435d 100644 --- a/arch/powerpc/boot/dts/digsy_mtc.dts +++ b/arch/powerpc/boot/dts/digsy_mtc.dts @@ -23,7 +23,14 @@ soc5200@f0000000 { timer@600 { // General Purpose Timer + #gpio-cells = <2>; fsl,has-wdt; + gpio-controller; + }; + + timer@610 { + #gpio-cells = <2>; + gpio-controller; }; rtc@800 { -- cgit v1.2.3-18-g5258 From 5d9e6ac84363f28a7461e4afe4b7f24f4824a233 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Thu, 7 Jul 2011 14:49:50 +0200 Subject: powerpc/5200: dts: digsy_mtc.dts: enable both MSCAN nodes We use both MSCAN controllers on this board, so do not disable them in the device tree. Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/digsy_mtc.dts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/powerpc/boot/dts/digsy_mtc.dts b/arch/powerpc/boot/dts/digsy_mtc.dts index 2aad7ae435d..a7511f2d844 100644 --- a/arch/powerpc/boot/dts/digsy_mtc.dts +++ b/arch/powerpc/boot/dts/digsy_mtc.dts @@ -37,14 +37,6 @@ status = "disabled"; }; - can@900 { - status = "disabled"; - }; - - can@980 { - status = "disabled"; - }; - spi@f00 { msp430@0 { compatible = "spidev"; -- cgit v1.2.3-18-g5258 From 40dc7e8b4ac5cdfcf13e3d80b71240f90ceb1f5b Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 1 Sep 2011 17:31:22 -0500 Subject: powerpc/5200: enable audio in the defconfig Audio support for the MPC5200 exists, so enable it by default. Signed-off-by: Timur Tabi Acked-by: Wolfram Sang Signed-off-by: Anatolij Gustschin --- arch/powerpc/configs/mpc5200_defconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/powerpc/configs/mpc5200_defconfig b/arch/powerpc/configs/mpc5200_defconfig index e63f537b854..2a1320fb272 100644 --- a/arch/powerpc/configs/mpc5200_defconfig +++ b/arch/powerpc/configs/mpc5200_defconfig @@ -88,6 +88,18 @@ CONFIG_FB_RADEON=y # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_DRIVERS is not set +# CONFIG_SND_PCI is not set +# CONFIG_SND_PPC is not set +# CONFIG_SND_SPI is not set +# CONFIG_SND_USB is not set +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MPC5200_I2S=y +CONFIG_SND_MPC52xx_SOC_PCM030=y +CONFIG_SND_MPC52xx_SOC_EFIKA=y CONFIG_HID_DRAGONRISE=y CONFIG_HID_GYRATION=y CONFIG_HID_TWINHAN=y -- cgit v1.2.3-18-g5258 From c68308dd50c3827a4ce77a1d70e0eb2d2521cafd Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 21 Sep 2011 12:49:20 +0200 Subject: gpio: move mpc8xxx/512x gpio driver to drivers/gpio Move the driver to the place where it is expected to be nowadays. Also rename its CONFIG-name to match the rest and adapt the defconfigs. Finally, move selection of REQUIRE_GPIOLIB or WANTS_OPTIONAL_GPIOLIB to the platforms, because this option is per-platform and not per-driver. Signed-off-by: Wolfram Sang Cc: Anatolij Gustschin Cc: Grant Likely Cc: Benjamin Herrenschmidt Acked-by: Grant Likely Signed-off-by: Anatolij Gustschin --- arch/powerpc/configs/85xx/p1023rds_defconfig | 2 +- arch/powerpc/configs/85xx/xes_mpc85xx_defconfig | 2 +- arch/powerpc/configs/mpc85xx_defconfig | 2 +- arch/powerpc/configs/mpc85xx_smp_defconfig | 2 +- arch/powerpc/configs/ppc6xx_defconfig | 2 +- arch/powerpc/platforms/512x/Kconfig | 1 + arch/powerpc/platforms/83xx/Kconfig | 9 +- arch/powerpc/platforms/85xx/Kconfig | 12 +- arch/powerpc/platforms/86xx/Kconfig | 1 + arch/powerpc/platforms/Kconfig | 10 - arch/powerpc/sysdev/Makefile | 1 - arch/powerpc/sysdev/mpc8xxx_gpio.c | 395 ------------------------ drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-mpc8xxx.c | 395 ++++++++++++++++++++++++ 15 files changed, 425 insertions(+), 418 deletions(-) delete mode 100644 arch/powerpc/sysdev/mpc8xxx_gpio.c create mode 100644 drivers/gpio/gpio-mpc8xxx.c diff --git a/arch/powerpc/configs/85xx/p1023rds_defconfig b/arch/powerpc/configs/85xx/p1023rds_defconfig index 3ff5a81c709..c091aaf7685 100644 --- a/arch/powerpc/configs/85xx/p1023rds_defconfig +++ b/arch/powerpc/configs/85xx/p1023rds_defconfig @@ -24,7 +24,7 @@ CONFIG_P1023_RDS=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y CONFIG_CPM2=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y diff --git a/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig b/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig index 5ea3124518f..1cd6fcb368e 100644 --- a/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig +++ b/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig @@ -20,7 +20,7 @@ CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set CONFIG_XES_MPC85xx=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_MATH_EMULATION=y CONFIG_SPARSE_IRQ=y diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index a3467bfb767..25009120745 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -41,7 +41,7 @@ CONFIG_TQM8560=y CONFIG_SBC8548=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index 9693f6ed3da..a4ba13b0ef1 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -42,7 +42,7 @@ CONFIG_TQM8560=y CONFIG_SBC8548=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 04360f9b010..c47f2becfbc 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -70,7 +70,7 @@ CONFIG_TAU_AVERAGE=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y CONFIG_PPC_BESTCOMM=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_MCU_MPC8349EMITX=m CONFIG_HIGHMEM=y CONFIG_NO_HZ=y diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig index 27b0651221d..b3ebce1aec0 100644 --- a/arch/powerpc/platforms/512x/Kconfig +++ b/arch/powerpc/platforms/512x/Kconfig @@ -6,6 +6,7 @@ config PPC_MPC512x select PPC_CLOCK select PPC_PCI_CHOICE select FSL_PCI if PCI + select ARCH_WANT_OPTIONAL_GPIOLIB config MPC5121_ADS bool "Freescale MPC5121E ADS" diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig index 73f4135f3a1..670a033264c 100644 --- a/arch/powerpc/platforms/83xx/Kconfig +++ b/arch/powerpc/platforms/83xx/Kconfig @@ -114,18 +114,21 @@ config KMETER1 endif -# used for usb +# used for usb & gpio config PPC_MPC831x bool + select ARCH_WANT_OPTIONAL_GPIOLIB # used for math-emu config PPC_MPC832x bool -# used for usb +# used for usb & gpio config PPC_MPC834x bool + select ARCH_WANT_OPTIONAL_GPIOLIB -# used for usb +# used for usb & gpio config PPC_MPC837x bool + select ARCH_WANT_OPTIONAL_GPIOLIB diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 498534cd526..1b393f40c63 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -177,7 +177,8 @@ config P2040_RDB select PPC_E500MC select PHYS_64BIT select SWIOTLB - select MPC8xxx_GPIO + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help @@ -189,7 +190,8 @@ config P3041_DS select PPC_E500MC select PHYS_64BIT select SWIOTLB - select MPC8xxx_GPIO + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help @@ -201,7 +203,8 @@ config P4080_DS select PPC_E500MC select PHYS_64BIT select SWIOTLB - select MPC8xxx_GPIO + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help @@ -216,7 +219,8 @@ config P5020_DS select PPC_E500MC select PHYS_64BIT select SWIOTLB - select MPC8xxx_GPIO + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index a0b5638c5dc..8d6599d54ea 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -4,6 +4,7 @@ menuconfig PPC_86xx depends on 6xx select FSL_SOC select ALTIVEC + select ARCH_WANT_OPTIONAL_GPIOLIB help The Freescale E600 SoCs have 74xx cores. diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 6de27d27572..9fdb9b6ab37 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -334,16 +334,6 @@ config OF_RTC source "arch/powerpc/sysdev/bestcomm/Kconfig" -config MPC8xxx_GPIO - bool "MPC512x/MPC8xxx GPIO support" - depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \ - FSL_SOC_BOOKE || PPC_86xx - select GENERIC_GPIO - select ARCH_REQUIRE_GPIOLIB - help - Say Y here if you're going to use hardware that connects to the - MPC512x/831x/834x/837x/8572/8610 GPIOs. - config SIMPLE_GPIO bool "Support for simple, memory-mapped GPIO controllers" depends on PPC diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index cf736ca0cf0..84e13253aec 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) obj-$(CONFIG_FSL_PMC) += fsl_pmc.o obj-$(CONFIG_FSL_LBC) += fsl_lbc.o obj-$(CONFIG_FSL_GTM) += fsl_gtm.o -obj-$(CONFIG_MPC8xxx_GPIO) += mpc8xxx_gpio.o obj-$(CONFIG_FSL_85XX_CACHE_SRAM) += fsl_85xx_l2ctlr.o fsl_85xx_cache_sram.o obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o obj-$(CONFIG_FSL_RIO) += fsl_rio.o diff --git a/arch/powerpc/sysdev/mpc8xxx_gpio.c b/arch/powerpc/sysdev/mpc8xxx_gpio.c deleted file mode 100644 index fb4963abdf5..00000000000 --- a/arch/powerpc/sysdev/mpc8xxx_gpio.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * GPIOs on MPC512x/8349/8572/8610 and compatible - * - * Copyright (C) 2008 Peter Korsgaard - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MPC8XXX_GPIO_PINS 32 - -#define GPIO_DIR 0x00 -#define GPIO_ODR 0x04 -#define GPIO_DAT 0x08 -#define GPIO_IER 0x0c -#define GPIO_IMR 0x10 -#define GPIO_ICR 0x14 -#define GPIO_ICR2 0x18 - -struct mpc8xxx_gpio_chip { - struct of_mm_gpio_chip mm_gc; - spinlock_t lock; - - /* - * shadowed data register to be able to clear/set output pins in - * open drain mode safely - */ - u32 data; - struct irq_host *irq; - void *of_dev_id_data; -}; - -static inline u32 mpc8xxx_gpio2mask(unsigned int gpio) -{ - return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio); -} - -static inline struct mpc8xxx_gpio_chip * -to_mpc8xxx_gpio_chip(struct of_mm_gpio_chip *mm) -{ - return container_of(mm, struct mpc8xxx_gpio_chip, mm_gc); -} - -static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); - - mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT); -} - -/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs - * defined as output cannot be determined by reading GPDAT register, - * so we use shadow data register instead. The status of input pins - * is determined by reading GPDAT register. - */ -static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio) -{ - u32 val; - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); - - val = in_be32(mm->regs + GPIO_DAT) & ~in_be32(mm->regs + GPIO_DIR); - - return (val | mpc8xxx_gc->data) & mpc8xxx_gpio2mask(gpio); -} - -static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - - return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio); -} - -static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); - unsigned long flags; - - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - if (val) - mpc8xxx_gc->data |= mpc8xxx_gpio2mask(gpio); - else - mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio); - - out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data); - - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); -} - -static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); - unsigned long flags; - - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); - - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - - return 0; -} - -static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); - unsigned long flags; - - mpc8xxx_gpio_set(gc, gpio, val); - - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); - - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - - return 0; -} - -static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) -{ - struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); - - if (mpc8xxx_gc->irq && offset < MPC8XXX_GPIO_PINS) - return irq_create_mapping(mpc8xxx_gc->irq, offset); - else - return -ENXIO; -} - -static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; - unsigned int mask; - - mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR); - if (mask) - generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, - 32 - ffs(mask))); -} - -static void mpc8xxx_irq_unmask(struct irq_data *d) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; - unsigned long flags; - - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); - - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); -} - -static void mpc8xxx_irq_mask(struct irq_data *d) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; - unsigned long flags; - - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - - clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); - - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); -} - -static void mpc8xxx_irq_ack(struct irq_data *d) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; - - out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); -} - -static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; - unsigned long flags; - - switch (flow_type) { - case IRQ_TYPE_EDGE_FALLING: - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - setbits32(mm->regs + GPIO_ICR, - mpc8xxx_gpio2mask(irqd_to_hwirq(d))); - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - break; - - case IRQ_TYPE_EDGE_BOTH: - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(mm->regs + GPIO_ICR, - mpc8xxx_gpio2mask(irqd_to_hwirq(d))); - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; - unsigned long gpio = irqd_to_hwirq(d); - void __iomem *reg; - unsigned int shift; - unsigned long flags; - - if (gpio < 16) { - reg = mm->regs + GPIO_ICR; - shift = (15 - gpio) * 2; - } else { - reg = mm->regs + GPIO_ICR2; - shift = (15 - (gpio % 16)) * 2; - } - - switch (flow_type) { - case IRQ_TYPE_EDGE_FALLING: - case IRQ_TYPE_LEVEL_LOW: - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrsetbits_be32(reg, 3 << shift, 2 << shift); - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - break; - - case IRQ_TYPE_EDGE_RISING: - case IRQ_TYPE_LEVEL_HIGH: - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrsetbits_be32(reg, 3 << shift, 1 << shift); - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - break; - - case IRQ_TYPE_EDGE_BOTH: - spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(reg, 3 << shift); - spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static struct irq_chip mpc8xxx_irq_chip = { - .name = "mpc8xxx-gpio", - .irq_unmask = mpc8xxx_irq_unmask, - .irq_mask = mpc8xxx_irq_mask, - .irq_ack = mpc8xxx_irq_ack, - .irq_set_type = mpc8xxx_irq_set_type, -}; - -static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc = h->host_data; - - if (mpc8xxx_gc->of_dev_id_data) - mpc8xxx_irq_chip.irq_set_type = mpc8xxx_gc->of_dev_id_data; - - irq_set_chip_data(virq, h->host_data); - irq_set_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq); - irq_set_irq_type(virq, IRQ_TYPE_NONE); - - return 0; -} - -static int mpc8xxx_gpio_irq_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) - -{ - /* interrupt sense values coming from the device tree equal either - * EDGE_FALLING or EDGE_BOTH - */ - *out_hwirq = intspec[0]; - *out_flags = intspec[1]; - - return 0; -} - -static struct irq_host_ops mpc8xxx_gpio_irq_ops = { - .map = mpc8xxx_gpio_irq_map, - .xlate = mpc8xxx_gpio_irq_xlate, -}; - -static struct of_device_id mpc8xxx_gpio_ids[] __initdata = { - { .compatible = "fsl,mpc8349-gpio", }, - { .compatible = "fsl,mpc8572-gpio", }, - { .compatible = "fsl,mpc8610-gpio", }, - { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, }, - { .compatible = "fsl,qoriq-gpio", }, - {} -}; - -static void __init mpc8xxx_add_controller(struct device_node *np) -{ - struct mpc8xxx_gpio_chip *mpc8xxx_gc; - struct of_mm_gpio_chip *mm_gc; - struct gpio_chip *gc; - const struct of_device_id *id; - unsigned hwirq; - int ret; - - mpc8xxx_gc = kzalloc(sizeof(*mpc8xxx_gc), GFP_KERNEL); - if (!mpc8xxx_gc) { - ret = -ENOMEM; - goto err; - } - - spin_lock_init(&mpc8xxx_gc->lock); - - mm_gc = &mpc8xxx_gc->mm_gc; - gc = &mm_gc->gc; - - mm_gc->save_regs = mpc8xxx_gpio_save_regs; - gc->ngpio = MPC8XXX_GPIO_PINS; - gc->direction_input = mpc8xxx_gpio_dir_in; - gc->direction_output = mpc8xxx_gpio_dir_out; - if (of_device_is_compatible(np, "fsl,mpc8572-gpio")) - gc->get = mpc8572_gpio_get; - else - gc->get = mpc8xxx_gpio_get; - gc->set = mpc8xxx_gpio_set; - gc->to_irq = mpc8xxx_gpio_to_irq; - - ret = of_mm_gpiochip_add(np, mm_gc); - if (ret) - goto err; - - hwirq = irq_of_parse_and_map(np, 0); - if (hwirq == NO_IRQ) - goto skip_irq; - - mpc8xxx_gc->irq = - irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, MPC8XXX_GPIO_PINS, - &mpc8xxx_gpio_irq_ops, MPC8XXX_GPIO_PINS); - if (!mpc8xxx_gc->irq) - goto skip_irq; - - id = of_match_node(mpc8xxx_gpio_ids, np); - if (id) - mpc8xxx_gc->of_dev_id_data = id->data; - - mpc8xxx_gc->irq->host_data = mpc8xxx_gc; - - /* ack and mask all irqs */ - out_be32(mm_gc->regs + GPIO_IER, 0xffffffff); - out_be32(mm_gc->regs + GPIO_IMR, 0); - - irq_set_handler_data(hwirq, mpc8xxx_gc); - irq_set_chained_handler(hwirq, mpc8xxx_gpio_irq_cascade); - -skip_irq: - return; - -err: - pr_err("%s: registration failed with status %d\n", - np->full_name, ret); - kfree(mpc8xxx_gc); - - return; -} - -static int __init mpc8xxx_add_gpiochips(void) -{ - struct device_node *np; - - for_each_matching_node(np, mpc8xxx_gpio_ids) - mpc8xxx_add_controller(np); - - return 0; -} -arch_initcall(mpc8xxx_add_gpiochips); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4..4744cf246d4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -103,6 +103,14 @@ config GPIO_MPC5200 def_bool y depends on PPC_MPC52xx +config GPIO_MPC8XXX + bool "MPC512x/MPC8xxx GPIO support" + depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \ + FSL_SOC_BOOKE || PPC_86xx + help + Say Y here if you're going to use hardware that connects to the + MPC512x/831x/834x/837x/8572/8610 GPIOs. + config GPIO_MSM_V1 tristate "Qualcomm MSM GPIO v1" depends on GPIOLIB && ARCH_MSM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9588948c96f..828bba9883c 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o +obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c new file mode 100644 index 00000000000..fb4963abdf5 --- /dev/null +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -0,0 +1,395 @@ +/* + * GPIOs on MPC512x/8349/8572/8610 and compatible + * + * Copyright (C) 2008 Peter Korsgaard + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPC8XXX_GPIO_PINS 32 + +#define GPIO_DIR 0x00 +#define GPIO_ODR 0x04 +#define GPIO_DAT 0x08 +#define GPIO_IER 0x0c +#define GPIO_IMR 0x10 +#define GPIO_ICR 0x14 +#define GPIO_ICR2 0x18 + +struct mpc8xxx_gpio_chip { + struct of_mm_gpio_chip mm_gc; + spinlock_t lock; + + /* + * shadowed data register to be able to clear/set output pins in + * open drain mode safely + */ + u32 data; + struct irq_host *irq; + void *of_dev_id_data; +}; + +static inline u32 mpc8xxx_gpio2mask(unsigned int gpio) +{ + return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio); +} + +static inline struct mpc8xxx_gpio_chip * +to_mpc8xxx_gpio_chip(struct of_mm_gpio_chip *mm) +{ + return container_of(mm, struct mpc8xxx_gpio_chip, mm_gc); +} + +static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + + mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT); +} + +/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs + * defined as output cannot be determined by reading GPDAT register, + * so we use shadow data register instead. The status of input pins + * is determined by reading GPDAT register. + */ +static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + u32 val; + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + + val = in_be32(mm->regs + GPIO_DAT) & ~in_be32(mm->regs + GPIO_DIR); + + return (val | mpc8xxx_gc->data) & mpc8xxx_gpio2mask(gpio); +} + +static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + + return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio); +} + +static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + unsigned long flags; + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + if (val) + mpc8xxx_gc->data |= mpc8xxx_gpio2mask(gpio); + else + mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio); + + out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); +} + +static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + unsigned long flags; + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + + return 0; +} + +static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + unsigned long flags; + + mpc8xxx_gpio_set(gc, gpio, val); + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + + return 0; +} + +static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); + + if (mpc8xxx_gc->irq && offset < MPC8XXX_GPIO_PINS) + return irq_create_mapping(mpc8xxx_gc->irq, offset); + else + return -ENXIO; +} + +static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned int mask; + + mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR); + if (mask) + generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, + 32 - ffs(mask))); +} + +static void mpc8xxx_irq_unmask(struct irq_data *d) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned long flags; + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); +} + +static void mpc8xxx_irq_mask(struct irq_data *d) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned long flags; + + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + + clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); +} + +static void mpc8xxx_irq_ack(struct irq_data *d) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + + out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); +} + +static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned long flags; + + switch (flow_type) { + case IRQ_TYPE_EDGE_FALLING: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + setbits32(mm->regs + GPIO_ICR, + mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + case IRQ_TYPE_EDGE_BOTH: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + clrbits32(mm->regs + GPIO_ICR, + mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; + unsigned long gpio = irqd_to_hwirq(d); + void __iomem *reg; + unsigned int shift; + unsigned long flags; + + if (gpio < 16) { + reg = mm->regs + GPIO_ICR; + shift = (15 - gpio) * 2; + } else { + reg = mm->regs + GPIO_ICR2; + shift = (15 - (gpio % 16)) * 2; + } + + switch (flow_type) { + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_LEVEL_LOW: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + clrsetbits_be32(reg, 3 << shift, 2 << shift); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + clrsetbits_be32(reg, 3 << shift, 1 << shift); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + case IRQ_TYPE_EDGE_BOTH: + spin_lock_irqsave(&mpc8xxx_gc->lock, flags); + clrbits32(reg, 3 << shift); + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static struct irq_chip mpc8xxx_irq_chip = { + .name = "mpc8xxx-gpio", + .irq_unmask = mpc8xxx_irq_unmask, + .irq_mask = mpc8xxx_irq_mask, + .irq_ack = mpc8xxx_irq_ack, + .irq_set_type = mpc8xxx_irq_set_type, +}; + +static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = h->host_data; + + if (mpc8xxx_gc->of_dev_id_data) + mpc8xxx_irq_chip.irq_set_type = mpc8xxx_gc->of_dev_id_data; + + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static int mpc8xxx_gpio_irq_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) + +{ + /* interrupt sense values coming from the device tree equal either + * EDGE_FALLING or EDGE_BOTH + */ + *out_hwirq = intspec[0]; + *out_flags = intspec[1]; + + return 0; +} + +static struct irq_host_ops mpc8xxx_gpio_irq_ops = { + .map = mpc8xxx_gpio_irq_map, + .xlate = mpc8xxx_gpio_irq_xlate, +}; + +static struct of_device_id mpc8xxx_gpio_ids[] __initdata = { + { .compatible = "fsl,mpc8349-gpio", }, + { .compatible = "fsl,mpc8572-gpio", }, + { .compatible = "fsl,mpc8610-gpio", }, + { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, }, + { .compatible = "fsl,qoriq-gpio", }, + {} +}; + +static void __init mpc8xxx_add_controller(struct device_node *np) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc; + struct of_mm_gpio_chip *mm_gc; + struct gpio_chip *gc; + const struct of_device_id *id; + unsigned hwirq; + int ret; + + mpc8xxx_gc = kzalloc(sizeof(*mpc8xxx_gc), GFP_KERNEL); + if (!mpc8xxx_gc) { + ret = -ENOMEM; + goto err; + } + + spin_lock_init(&mpc8xxx_gc->lock); + + mm_gc = &mpc8xxx_gc->mm_gc; + gc = &mm_gc->gc; + + mm_gc->save_regs = mpc8xxx_gpio_save_regs; + gc->ngpio = MPC8XXX_GPIO_PINS; + gc->direction_input = mpc8xxx_gpio_dir_in; + gc->direction_output = mpc8xxx_gpio_dir_out; + if (of_device_is_compatible(np, "fsl,mpc8572-gpio")) + gc->get = mpc8572_gpio_get; + else + gc->get = mpc8xxx_gpio_get; + gc->set = mpc8xxx_gpio_set; + gc->to_irq = mpc8xxx_gpio_to_irq; + + ret = of_mm_gpiochip_add(np, mm_gc); + if (ret) + goto err; + + hwirq = irq_of_parse_and_map(np, 0); + if (hwirq == NO_IRQ) + goto skip_irq; + + mpc8xxx_gc->irq = + irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, MPC8XXX_GPIO_PINS, + &mpc8xxx_gpio_irq_ops, MPC8XXX_GPIO_PINS); + if (!mpc8xxx_gc->irq) + goto skip_irq; + + id = of_match_node(mpc8xxx_gpio_ids, np); + if (id) + mpc8xxx_gc->of_dev_id_data = id->data; + + mpc8xxx_gc->irq->host_data = mpc8xxx_gc; + + /* ack and mask all irqs */ + out_be32(mm_gc->regs + GPIO_IER, 0xffffffff); + out_be32(mm_gc->regs + GPIO_IMR, 0); + + irq_set_handler_data(hwirq, mpc8xxx_gc); + irq_set_chained_handler(hwirq, mpc8xxx_gpio_irq_cascade); + +skip_irq: + return; + +err: + pr_err("%s: registration failed with status %d\n", + np->full_name, ret); + kfree(mpc8xxx_gc); + + return; +} + +static int __init mpc8xxx_add_gpiochips(void) +{ + struct device_node *np; + + for_each_matching_node(np, mpc8xxx_gpio_ids) + mpc8xxx_add_controller(np); + + return 0; +} +arch_initcall(mpc8xxx_add_gpiochips); -- cgit v1.2.3-18-g5258 From 7b72c9f8750e32b3b08fab1bfef43350866ae8d1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 21 Sep 2011 12:49:21 +0200 Subject: powerpc: update 512x-defconfig Activate all MPC512x related boards. Also enable GPIO-driver, SPI driver and at25 to test SPI. Enable DEVTMPFS. Bump to 3.1-rc6. Signed-off-by: Wolfram Sang Cc: Anatolij Gustschin Cc: Benjamin Herrenschmidt Signed-off-by: Anatolij Gustschin --- arch/powerpc/configs/mpc512x_defconfig | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/configs/mpc512x_defconfig b/arch/powerpc/configs/mpc512x_defconfig index c02bbb2fddf..211fcc9ed70 100644 --- a/arch/powerpc/configs/mpc512x_defconfig +++ b/arch/powerpc/configs/mpc512x_defconfig @@ -1,9 +1,9 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_SYSVIPC=y +CONFIG_SPARSE_IRQ=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y CONFIG_MODULES=y @@ -13,10 +13,11 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_PPC_CHRP is not set CONFIG_PPC_MPC512x=y CONFIG_MPC5121_ADS=y +CONFIG_MPC5121_GENERIC=y +CONFIG_PDM360NG=y # CONFIG_PPC_PMAC is not set CONFIG_NO_HZ=y CONFIG_HZ_1000=y -CONFIG_SPARSE_IRQ=y # CONFIG_MIGRATION is not set # CONFIG_SECCOMP is not set # CONFIG_PCI is not set @@ -35,18 +36,16 @@ CONFIG_CAN=y CONFIG_CAN_RAW=y CONFIG_CAN_BCM=y CONFIG_CAN_VCAN=y -CONFIG_CAN_DEV=y CONFIG_CAN_MSCAN=y CONFIG_CAN_DEBUG_DEVICES=y # CONFIG_WIRELESS is not set CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y # CONFIG_PREVENT_FIRMWARE_BUILD is not set # CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_MTD=y -CONFIG_MTD_CONCAT=y -CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_OF_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y @@ -63,6 +62,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_BLK_DEV_XIP=y CONFIG_MISC_DEVICES=y CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y CONFIG_SCSI=y # CONFIG_SCSI_PROC_FS is not set CONFIG_BLK_DEV_SD=y @@ -99,10 +99,14 @@ CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD=115200 CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MPC=y +CONFIG_SPI=y +CONFIG_SPI_MPC512x_PSC=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_MPC8XXX=y # CONFIG_HWMON is not set CONFIG_MEDIA_SUPPORT=y CONFIG_VIDEO_DEV=y -# CONFIG_VIDEO_ALLOW_V4L1 is not set CONFIG_VIDEO_ADV_DEBUG=y # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set CONFIG_VIDEO_SAA711X=y @@ -132,6 +136,5 @@ CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y # CONFIG_ENABLE_WARN_DEPRECATED is not set # CONFIG_ENABLE_MUST_CHECK is not set -# CONFIG_RCU_CPU_STALL_DETECTOR is not set # CONFIG_CRYPTO_ANSI_CPRNG is not set # CONFIG_CRYPTO_HW is not set -- cgit v1.2.3-18-g5258 From 25c29f9e3242071bca1bee7ad919baf1888ae436 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 20 Sep 2011 19:58:10 +0000 Subject: powerpc: Fix hugetlb with CONFIG_PPC_MM_SLICES=y Commit 41151e77a4 ("powerpc: Hugetlb for BookE") added some #ifdef CONFIG_MM_SLICES conditionals to hugetlb_get_unmapped_area() and vma_mmu_pagesize(). Unfortunately this is not the correct config symbol; it should be CONFIG_PPC_MM_SLICES. The result is that attempting to use hugetlbfs on 64-bit Power server processors results in an infinite stack recursion between get_unmapped_area() and hugetlb_get_unmapped_area(). This fixes it by changing the #ifdef to use CONFIG_PPC_MM_SLICES in those functions and also in book3e_hugetlb_preload(). Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/hugetlbpage-book3e.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/mm/hugetlbpage-book3e.c b/arch/powerpc/mm/hugetlbpage-book3e.c index 1295b7c1cda..343ad0b8726 100644 --- a/arch/powerpc/mm/hugetlbpage-book3e.c +++ b/arch/powerpc/mm/hugetlbpage-book3e.c @@ -52,7 +52,7 @@ void book3e_hugetlb_preload(struct mm_struct *mm, unsigned long ea, pte_t pte) if (unlikely(is_kernel_addr(ea))) return; -#ifdef CONFIG_MM_SLICES +#ifdef CONFIG_PPC_MM_SLICES psize = mmu_get_tsize(get_slice_psize(mm, ea)); tsize = mmu_get_psize(psize); shift = mmu_psize_defs[psize].shift; diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 3a5f59dcbb3..48b65bedc04 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -690,7 +690,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { -#ifdef CONFIG_MM_SLICES +#ifdef CONFIG_PPC_MM_SLICES struct hstate *hstate = hstate_file(file); int mmu_psize = shift_to_mmu_psize(huge_page_shift(hstate)); @@ -702,7 +702,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long vma_mmu_pagesize(struct vm_area_struct *vma) { -#ifdef CONFIG_MM_SLICES +#ifdef CONFIG_PPC_MM_SLICES unsigned int psize = get_slice_psize(vma->vm_mm, vma->vm_start); return 1UL << mmu_psize_to_shift(psize); -- cgit v1.2.3-18-g5258 From d12b524f8b2f4e45cabe8bc1501e8b967d543111 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 20 Sep 2011 03:07:24 +0000 Subject: powerpc: Reserve iommu page 0 Some devices have a dma-window that starts at the address 0. This allows DMA addresses to be mapped to this address and returned to drivers as a valid DMA address. Some drivers may not behave well in this case, since the address 0 is considered an error or not allocated. The solution to avoid this kind of error from happening is reserve the page addressed as 0 so it cannot be allocated for a DMA mapping. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/iommu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index 961bb03413f..0cfcf98aafc 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -501,6 +501,14 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid) tbl->it_map = page_address(page); memset(tbl->it_map, 0, sz); + /* + * Reserve page 0 so it will not be used for any mappings. + * This avoids buggy drivers that consider page 0 to be invalid + * to crash the machine or even lose data. + */ + if (tbl->it_offset == 0) + set_bit(0, tbl->it_map); + tbl->it_hint = 0; tbl->it_largehint = tbl->it_halfpoint; spin_lock_init(&tbl->it_lock); -- cgit v1.2.3-18-g5258 From bb36c44557a4fcbaa17c0f2776e12a05a691b432 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 26 Sep 2011 14:22:39 +1000 Subject: powerpc/pci: Don't configure PCIe settings when PCI_PROBE_ONLY is set We don't want to configure PCI Express Max Payload Size or Max Read Request Size on systems that set that flag. The firmware will have done it for us, and under hypervisors such as pHyp we don't even see the parent switches and bridges and thus can make no assumption on what values are safe to use. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/pci-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 1bd47f36b25..677ecccbe10 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1732,7 +1732,7 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) hose->last_busno = bus->subordinate = pci_scan_child_bus(bus); /* Configure PCI Express settings */ - if (bus) { + if (bus && !pci_has_flag(PCI_PROBE_ONLY)) { struct pci_bus *child; list_for_each_entry(child, &bus->children, node) { struct pci_dev *self = child->self; -- cgit v1.2.3-18-g5258 From 66857b3a9e88ac6f6e279eaa06b84367e662c0dd Mon Sep 17 00:00:00 2001 From: Jimi Xenidis Date: Fri, 23 Sep 2011 05:40:46 +0000 Subject: powerpc: Fix xmon for systems without MSR[RI] Based on patch by David Gibson xmon has a longstanding bug on systems which are SMP-capable but lack the MSR[RI] bit. In these cases, xmon invoked by IPI on secondary CPUs will not properly keep quiet, but will print stuff, thereby garbling the primary xmon's output. This patch fixes it, by ignoring the RI bit if the processor does not support it. There's already a version of this for 4xx upstream, which we'll need to extend to other RI-lacking CPUs at some point. For now this adds Book3e processors to the mix. Signed-off-by: Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/xmon/xmon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 42541bbcc7f..13f82f84766 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -340,8 +340,8 @@ int cpus_are_in_xmon(void) static inline int unrecoverable_excp(struct pt_regs *regs) { -#ifdef CONFIG_4xx - /* We have no MSR_RI bit on 4xx, so we simply return false */ +#if defined(CONFIG_4xx) || defined(CONFIG_BOOK3E) + /* We have no MSR_RI bit on 4xx or Book3e, so we simply return false */ return 0; #else return ((regs->msr & MSR_RI) == 0); -- cgit v1.2.3-18-g5258 From e69b742a6793dc5bf16f6eedca534d4bc10d68b2 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 26 Sep 2011 19:37:57 +0000 Subject: powerpc/ptrace: Fix build with gcc 4.6 gcc (rightfully) complains that we are accessing beyond the end of the fpr array (we do, to access the fpscr). The only sane thing to do (whether anything in that code can be called remotely sane is debatable) is to special case fpscr and handle it as a separate statement. I initially tried to do it it by making the array access conditional to index < PT_FPSCR and using a 3rd else leg but for some reason gcc was unable to understand it and still spewed the warning. So I ended up with something a tad more intricated but it seems to build on 32-bit and on 64-bit with and without VSX. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/ptrace.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 05b7dd217f6..18447c4fbad 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1497,9 +1497,14 @@ long arch_ptrace(struct task_struct *child, long request, if (index < PT_FPR0) { tmp = ptrace_get_reg(child, (int) index); } else { + unsigned int fpidx = index - PT_FPR0; + flush_fp_to_thread(child); - tmp = ((unsigned long *)child->thread.fpr) - [TS_FPRWIDTH * (index - PT_FPR0)]; + if (fpidx < (PT_FPSCR - PT_FPR0)) + tmp = ((unsigned long *)child->thread.fpr) + [fpidx * TS_FPRWIDTH]; + else + tmp = child->thread.fpscr.val; } ret = put_user(tmp, datalp); break; @@ -1525,9 +1530,14 @@ long arch_ptrace(struct task_struct *child, long request, if (index < PT_FPR0) { ret = ptrace_put_reg(child, index, data); } else { + unsigned int fpidx = index - PT_FPR0; + flush_fp_to_thread(child); - ((unsigned long *)child->thread.fpr) - [TS_FPRWIDTH * (index - PT_FPR0)] = data; + if (fpidx < (PT_FPSCR - PT_FPR0)) + ((unsigned long *)child->thread.fpr) + [fpidx * TS_FPRWIDTH] = data; + else + child->thread.fpscr.val = data; ret = 0; } break; -- cgit v1.2.3-18-g5258 From d15f02eb4e8992cfacfca2ff306e5585bcf721d1 Mon Sep 17 00:00:00 2001 From: "Carl E. Love" Date: Wed, 28 Sep 2011 11:23:33 +0000 Subject: powerpc/perf_event: Fix Power6 L1 cache read & write event codes] The current L1 cache read event code 0x80082 only counts for thread 0. The event code 0x280030 should be used to count events on thread 0 and 1. The patch fixes the event code for the L1 cache read. The current L1 cache write event code 0x80086 only counts for thread 0. The event code 0x180032 should be used to count events on thread 0 and 1. The patch fixes the event code for the L1 cache write. FYI, the documentation lists three event codes for the L1 cache read event and three event codes for the L1 cache write event. The event description for the event codes is as follows: L1 cache read requests 0x80082 LSU 0 only L1 cache read requests 0x8008A LSU 1 only L1 cache read requests 0x80030 LSU 1 or LSU 0, counter 2 only. L1 cache store requests 0x80086 LSU 0 only L1 cache store requests 0x8008E LSU 1 only L1 cache store requests 0x80032 LSU 0 or LSU 1, counter 1 only. There can only be one request from either LSU 0 or 1 active at a time. Signed-off-by: Carl Love Acked-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/power6-pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/power6-pmu.c b/arch/powerpc/kernel/power6-pmu.c index 03b95e2c6d6..0bbc901e7ef 100644 --- a/arch/powerpc/kernel/power6-pmu.c +++ b/arch/powerpc/kernel/power6-pmu.c @@ -487,8 +487,8 @@ static int power6_generic_events[] = { */ static int power6_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x80082, 0x80080 }, - [C(OP_WRITE)] = { 0x80086, 0x80088 }, + [C(OP_READ)] = { 0x280030, 0x80080 }, + [C(OP_WRITE)] = { 0x180032, 0x80088 }, [C(OP_PREFETCH)] = { 0x810a4, 0 }, }, [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ -- cgit v1.2.3-18-g5258 From 7680057cc4c7d9caada12767831bfd9738dd7b43 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 28 Sep 2011 20:51:46 +0000 Subject: powerpc: Don't try OPAL takeover on old 970 blades The firmware on old 970 blades supports some kind of takeover called "TNK takeover" which will crash if we try to probe for OPAL takeover, so don't do it. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom_init.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index e96f5d0d2c7..b4fa6612749 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1313,6 +1313,16 @@ static void prom_query_opal(void) { long rc; + /* We must not query for OPAL presence on a machine that + * supports TNK takeover (970 blades), as this uses the same + * h-call with different arguments and will crash + */ + if (PHANDLE_VALID(call_prom("finddevice", 1, 1, + ADDR("/tnk-memory-map")))) { + prom_printf("TNK takeover detected, skipping OPAL check\n"); + return; + } + prom_printf("Querying for OPAL presence... "); rc = opal_query_takeover(&RELOC(prom_opal_size), &RELOC(prom_opal_align)); -- cgit v1.2.3-18-g5258 From c72fa7df3758c047660e27c34dfd2f9a14181c53 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Tue, 19 Jul 2011 12:53:43 +0400 Subject: powerpc/85xx: sbc8560 - correct compilation if CONFIG_PHYS_ADDR_T_64BIT is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_PHYS_ADDR_T_64BIT is set, compilation of sbc8560 fails with the following error: arch/powerpc/platforms/85xx/sbc8560.c: In function ‘sbc8560_bdrstcr_init’: arch/powerpc/platforms/85xx/sbc8560.c:286: error: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘resource_size_t’ Fix that by using %pR format instead of just printing the start of resource. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/sbc8560.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/85xx/sbc8560.c b/arch/powerpc/platforms/85xx/sbc8560.c index 09ced722175..cebd786dc33 100644 --- a/arch/powerpc/platforms/85xx/sbc8560.c +++ b/arch/powerpc/platforms/85xx/sbc8560.c @@ -283,7 +283,7 @@ static int __init sbc8560_bdrstcr_init(void) of_address_to_resource(np, 0, &res); - printk(KERN_INFO "sbc8560: Found BRSTCR at i/o 0x%x\n", res.start); + printk(KERN_INFO "sbc8560: Found BRSTCR at %pR\n", &res); brstcr = ioremap(res.start, resource_size(&res)); if(!brstcr) -- cgit v1.2.3-18-g5258 From c88f92a261e7637bcd9d1c9802e61eeda2f125d8 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Tue, 19 Jul 2011 12:53:44 +0400 Subject: powerpc/85xx: ksi8560 - declare that localbus is compatbile with simple-bus Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/ksi8560.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/dts/ksi8560.dts b/arch/powerpc/boot/dts/ksi8560.dts index bdb7fc0fa33..296c572ea60 100644 --- a/arch/powerpc/boot/dts/ksi8560.dts +++ b/arch/powerpc/boot/dts/ksi8560.dts @@ -306,7 +306,7 @@ localbus@fdf05000 { #address-cells = <2>; #size-cells = <1>; - compatible = "fsl,mpc8560-localbus"; + compatible = "fsl,mpc8560-localbus", "simple-bus"; reg = <0xfdf05000 0x68>; ranges = <0x0 0x0 0xe0000000 0x00800000 -- cgit v1.2.3-18-g5258 From 385c056b56226f554c6a3a220487762e35eeb2fa Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Tue, 19 Jul 2011 12:53:45 +0400 Subject: powerpc/85xx: sbc8560 - declare that localbus is compatbile with simple-bus Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/sbc8560.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/dts/sbc8560.dts b/arch/powerpc/boot/dts/sbc8560.dts index 9e13ed8a119..72078eb1561 100644 --- a/arch/powerpc/boot/dts/sbc8560.dts +++ b/arch/powerpc/boot/dts/sbc8560.dts @@ -331,7 +331,7 @@ }; localbus@ff705000 { - compatible = "fsl,mpc8560-localbus"; + compatible = "fsl,mpc8560-localbus", "simple-bus"; #address-cells = <2>; #size-cells = <1>; reg = <0xff705000 0x100>; // BRx, ORx, etc. -- cgit v1.2.3-18-g5258 From 66b77a7540e8c08e19d58657689269487e1349da Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Tue, 19 Jul 2011 12:53:40 +0400 Subject: powerpc/mpc8349emitx: mark localbus as compatible with simple-bus Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/mpc8349emitx.dts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts index b53d1df11e2..505dc842d80 100644 --- a/arch/powerpc/boot/dts/mpc8349emitx.dts +++ b/arch/powerpc/boot/dts/mpc8349emitx.dts @@ -390,7 +390,8 @@ #address-cells = <2>; #size-cells = <1>; compatible = "fsl,mpc8349e-localbus", - "fsl,pq2pro-localbus"; + "fsl,pq2pro-localbus", + "simple-bus"; reg = <0xe0005000 0xd8>; ranges = <0x0 0x0 0xfe000000 0x1000000 /* flash */ 0x1 0x0 0xf8000000 0x20000 /* VSC 7385 */ -- cgit v1.2.3-18-g5258 From d70cb31de8b33f19a381132ffb69cf99d45b48e6 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Tue, 30 Aug 2011 22:27:59 -0500 Subject: powerpc/85xx: Rename PowerPC core nodes to match other e500mc based .dts The P4080 silicon device tree was using PowerPC,4080 while the other e500mc based SoCs used PowerPC,e500mc. Use the core name to be consistent going forward. Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/p4080si.dtsi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/boot/dts/p4080si.dtsi b/arch/powerpc/boot/dts/p4080si.dtsi index b71051f506c..4984edbb63a 100644 --- a/arch/powerpc/boot/dts/p4080si.dtsi +++ b/arch/powerpc/boot/dts/p4080si.dtsi @@ -77,7 +77,7 @@ #address-cells = <1>; #size-cells = <0>; - cpu0: PowerPC,4080@0 { + cpu0: PowerPC,e500mc@0 { device_type = "cpu"; reg = <0>; next-level-cache = <&L2_0>; @@ -85,7 +85,7 @@ next-level-cache = <&cpc>; }; }; - cpu1: PowerPC,4080@1 { + cpu1: PowerPC,e500mc@1 { device_type = "cpu"; reg = <1>; next-level-cache = <&L2_1>; @@ -93,7 +93,7 @@ next-level-cache = <&cpc>; }; }; - cpu2: PowerPC,4080@2 { + cpu2: PowerPC,e500mc@2 { device_type = "cpu"; reg = <2>; next-level-cache = <&L2_2>; @@ -101,7 +101,7 @@ next-level-cache = <&cpc>; }; }; - cpu3: PowerPC,4080@3 { + cpu3: PowerPC,e500mc@3 { device_type = "cpu"; reg = <3>; next-level-cache = <&L2_3>; @@ -109,7 +109,7 @@ next-level-cache = <&cpc>; }; }; - cpu4: PowerPC,4080@4 { + cpu4: PowerPC,e500mc@4 { device_type = "cpu"; reg = <4>; next-level-cache = <&L2_4>; @@ -117,7 +117,7 @@ next-level-cache = <&cpc>; }; }; - cpu5: PowerPC,4080@5 { + cpu5: PowerPC,e500mc@5 { device_type = "cpu"; reg = <5>; next-level-cache = <&L2_5>; @@ -125,7 +125,7 @@ next-level-cache = <&cpc>; }; }; - cpu6: PowerPC,4080@6 { + cpu6: PowerPC,e500mc@6 { device_type = "cpu"; reg = <6>; next-level-cache = <&L2_6>; @@ -133,7 +133,7 @@ next-level-cache = <&cpc>; }; }; - cpu7: PowerPC,4080@7 { + cpu7: PowerPC,e500mc@7 { device_type = "cpu"; reg = <7>; next-level-cache = <&L2_7>; -- cgit v1.2.3-18-g5258 From d31337657ba931253e6dd8c5b1e856c121e41bf8 Mon Sep 17 00:00:00 2001 From: Mingkai Hu Date: Fri, 26 Aug 2011 18:45:03 +0800 Subject: powerpc/85xx: Rename p2040_rdb.c to p2041_rdb.c There's only p2041rdb board for official release, but the p2041 silicon on the board can be converted to p2040 silicon without XAUI and L2 cache function, then the board becomes p2040rdb board. so we use the file name p2041_rdb.c to handle P2040RDB board and P2041RDB board which is also consistent with the board name under U-Boot. During the rename we make few other minor changes to the device tree: * Move USB phy setting into p2041si.dtsi as its SoC not board defined * Convert PCI clock-frequency to decimal to be more readable Signed-off-by: Mingkai Hu Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/p2040rdb.dts | 166 ------- arch/powerpc/boot/dts/p2040si.dtsi | 623 --------------------------- arch/powerpc/boot/dts/p2041rdb.dts | 161 +++++++ arch/powerpc/boot/dts/p2041si.dtsi | 623 +++++++++++++++++++++++++++ arch/powerpc/configs/corenet32_smp_defconfig | 2 +- arch/powerpc/platforms/85xx/Kconfig | 6 +- arch/powerpc/platforms/85xx/Makefile | 2 +- arch/powerpc/platforms/85xx/p2040_rdb.c | 88 ---- arch/powerpc/platforms/85xx/p2041_rdb.c | 88 ++++ 9 files changed, 877 insertions(+), 882 deletions(-) delete mode 100644 arch/powerpc/boot/dts/p2040rdb.dts delete mode 100644 arch/powerpc/boot/dts/p2040si.dtsi create mode 100644 arch/powerpc/boot/dts/p2041rdb.dts create mode 100644 arch/powerpc/boot/dts/p2041si.dtsi delete mode 100644 arch/powerpc/platforms/85xx/p2040_rdb.c create mode 100644 arch/powerpc/platforms/85xx/p2041_rdb.c diff --git a/arch/powerpc/boot/dts/p2040rdb.dts b/arch/powerpc/boot/dts/p2040rdb.dts deleted file mode 100644 index 7d84e391c63..00000000000 --- a/arch/powerpc/boot/dts/p2040rdb.dts +++ /dev/null @@ -1,166 +0,0 @@ -/* - * P2040RDB Device Tree Source - * - * Copyright 2011 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/include/ "p2040si.dtsi" - -/ { - model = "fsl,P2040RDB"; - compatible = "fsl,P2040RDB"; - #address-cells = <2>; - #size-cells = <2>; - interrupt-parent = <&mpic>; - - memory { - device_type = "memory"; - }; - - soc: soc@ffe000000 { - spi@110000 { - flash@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "spansion,s25sl12801"; - reg = <0>; - spi-max-frequency = <40000000>; /* input clock */ - partition@u-boot { - label = "u-boot"; - reg = <0x00000000 0x00100000>; - read-only; - }; - partition@kernel { - label = "kernel"; - reg = <0x00100000 0x00500000>; - read-only; - }; - partition@dtb { - label = "dtb"; - reg = <0x00600000 0x00100000>; - read-only; - }; - partition@fs { - label = "file system"; - reg = <0x00700000 0x00900000>; - }; - }; - }; - - i2c@118000 { - lm75b@48 { - compatible = "nxp,lm75a"; - reg = <0x48>; - }; - eeprom@50 { - compatible = "at24,24c256"; - reg = <0x50>; - }; - rtc@68 { - compatible = "pericom,pt7c4338"; - reg = <0x68>; - }; - }; - - i2c@118100 { - eeprom@50 { - compatible = "at24,24c256"; - reg = <0x50>; - }; - }; - - usb0: usb@210000 { - phy_type = "utmi"; - }; - - usb1: usb@211000 { - dr_mode = "host"; - phy_type = "utmi"; - }; - }; - - localbus@ffe124000 { - reg = <0xf 0xfe124000 0 0x1000>; - ranges = <0 0 0xf 0xe8000000 0x08000000>; - - flash@0,0 { - compatible = "cfi-flash"; - reg = <0 0 0x08000000>; - bank-width = <2>; - device-width = <2>; - }; - }; - - pci0: pcie@ffe200000 { - reg = <0xf 0xfe200000 0 0x1000>; - ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x20000000 - 0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>; - pcie@0 { - ranges = <0x02000000 0 0xe0000000 - 0x02000000 0 0xe0000000 - 0 0x20000000 - - 0x01000000 0 0x00000000 - 0x01000000 0 0x00000000 - 0 0x00010000>; - }; - }; - - pci1: pcie@ffe201000 { - reg = <0xf 0xfe201000 0 0x1000>; - ranges = <0x02000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 - 0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>; - pcie@0 { - ranges = <0x02000000 0 0xe0000000 - 0x02000000 0 0xe0000000 - 0 0x20000000 - - 0x01000000 0 0x00000000 - 0x01000000 0 0x00000000 - 0 0x00010000>; - }; - }; - - pci2: pcie@ffe202000 { - reg = <0xf 0xfe202000 0 0x1000>; - ranges = <0x02000000 0 0xe0000000 0xc 0x40000000 0 0x20000000 - 0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>; - pcie@0 { - ranges = <0x02000000 0 0xe0000000 - 0x02000000 0 0xe0000000 - 0 0x20000000 - - 0x01000000 0 0x00000000 - 0x01000000 0 0x00000000 - 0 0x00010000>; - }; - }; -}; diff --git a/arch/powerpc/boot/dts/p2040si.dtsi b/arch/powerpc/boot/dts/p2040si.dtsi deleted file mode 100644 index 5fdbb24c076..00000000000 --- a/arch/powerpc/boot/dts/p2040si.dtsi +++ /dev/null @@ -1,623 +0,0 @@ -/* - * P2040 Silicon Device Tree Source - * - * Copyright 2011 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/dts-v1/; - -/ { - compatible = "fsl,P2040"; - #address-cells = <2>; - #size-cells = <2>; - interrupt-parent = <&mpic>; - - aliases { - ccsr = &soc; - - serial0 = &serial0; - serial1 = &serial1; - serial2 = &serial2; - serial3 = &serial3; - pci0 = &pci0; - pci1 = &pci1; - pci2 = &pci2; - usb0 = &usb0; - usb1 = &usb1; - dma0 = &dma0; - dma1 = &dma1; - sdhc = &sdhc; - msi0 = &msi0; - msi1 = &msi1; - msi2 = &msi2; - - crypto = &crypto; - sec_jr0 = &sec_jr0; - sec_jr1 = &sec_jr1; - sec_jr2 = &sec_jr2; - sec_jr3 = &sec_jr3; - rtic_a = &rtic_a; - rtic_b = &rtic_b; - rtic_c = &rtic_c; - rtic_d = &rtic_d; - sec_mon = &sec_mon; - }; - - cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu0: PowerPC,e500mc@0 { - device_type = "cpu"; - reg = <0>; - next-level-cache = <&L2_0>; - L2_0: l2-cache { - next-level-cache = <&cpc>; - }; - }; - cpu1: PowerPC,e500mc@1 { - device_type = "cpu"; - reg = <1>; - next-level-cache = <&L2_1>; - L2_1: l2-cache { - next-level-cache = <&cpc>; - }; - }; - cpu2: PowerPC,e500mc@2 { - device_type = "cpu"; - reg = <2>; - next-level-cache = <&L2_2>; - L2_2: l2-cache { - next-level-cache = <&cpc>; - }; - }; - cpu3: PowerPC,e500mc@3 { - device_type = "cpu"; - reg = <3>; - next-level-cache = <&L2_3>; - L2_3: l2-cache { - next-level-cache = <&cpc>; - }; - }; - }; - - soc: soc@ffe000000 { - #address-cells = <1>; - #size-cells = <1>; - device_type = "soc"; - compatible = "simple-bus"; - ranges = <0x00000000 0xf 0xfe000000 0x1000000>; - reg = <0xf 0xfe000000 0 0x00001000>; - - soc-sram-error { - compatible = "fsl,soc-sram-error"; - interrupts = <16 2 1 29>; - }; - - corenet-law@0 { - compatible = "fsl,corenet-law"; - reg = <0x0 0x1000>; - fsl,num-laws = <32>; - }; - - memory-controller@8000 { - compatible = "fsl,qoriq-memory-controller-v4.5", "fsl,qoriq-memory-controller"; - reg = <0x8000 0x1000>; - interrupts = <16 2 1 23>; - }; - - cpc: l3-cache-controller@10000 { - compatible = "fsl,p2040-l3-cache-controller", "fsl,p4080-l3-cache-controller", "cache"; - reg = <0x10000 0x1000>; - interrupts = <16 2 1 27>; - }; - - corenet-cf@18000 { - compatible = "fsl,corenet-cf"; - reg = <0x18000 0x1000>; - interrupts = <16 2 1 31>; - fsl,ccf-num-csdids = <32>; - fsl,ccf-num-snoopids = <32>; - }; - - iommu@20000 { - compatible = "fsl,pamu-v1.0", "fsl,pamu"; - reg = <0x20000 0x4000>; - interrupts = < - 24 2 0 0 - 16 2 1 30>; - }; - - mpic: pic@40000 { - clock-frequency = <0>; - interrupt-controller; - #address-cells = <0>; - #interrupt-cells = <4>; - reg = <0x40000 0x40000>; - compatible = "fsl,mpic", "chrp,open-pic"; - device_type = "open-pic"; - }; - - msi0: msi@41600 { - compatible = "fsl,mpic-msi"; - reg = <0x41600 0x200>; - msi-available-ranges = <0 0x100>; - interrupts = < - 0xe0 0 0 0 - 0xe1 0 0 0 - 0xe2 0 0 0 - 0xe3 0 0 0 - 0xe4 0 0 0 - 0xe5 0 0 0 - 0xe6 0 0 0 - 0xe7 0 0 0>; - }; - - msi1: msi@41800 { - compatible = "fsl,mpic-msi"; - reg = <0x41800 0x200>; - msi-available-ranges = <0 0x100>; - interrupts = < - 0xe8 0 0 0 - 0xe9 0 0 0 - 0xea 0 0 0 - 0xeb 0 0 0 - 0xec 0 0 0 - 0xed 0 0 0 - 0xee 0 0 0 - 0xef 0 0 0>; - }; - - msi2: msi@41a00 { - compatible = "fsl,mpic-msi"; - reg = <0x41a00 0x200>; - msi-available-ranges = <0 0x100>; - interrupts = < - 0xf0 0 0 0 - 0xf1 0 0 0 - 0xf2 0 0 0 - 0xf3 0 0 0 - 0xf4 0 0 0 - 0xf5 0 0 0 - 0xf6 0 0 0 - 0xf7 0 0 0>; - }; - - guts: global-utilities@e0000 { - compatible = "fsl,qoriq-device-config-1.0"; - reg = <0xe0000 0xe00>; - fsl,has-rstcr; - #sleep-cells = <1>; - fsl,liodn-bits = <12>; - }; - - pins: global-utilities@e0e00 { - compatible = "fsl,qoriq-pin-control-1.0"; - reg = <0xe0e00 0x200>; - #sleep-cells = <2>; - }; - - clockgen: global-utilities@e1000 { - compatible = "fsl,p2040-clockgen", "fsl,qoriq-clockgen-1.0"; - reg = <0xe1000 0x1000>; - clock-frequency = <0>; - }; - - rcpm: global-utilities@e2000 { - compatible = "fsl,qoriq-rcpm-1.0"; - reg = <0xe2000 0x1000>; - #sleep-cells = <1>; - }; - - sfp: sfp@e8000 { - compatible = "fsl,p2040-sfp", "fsl,qoriq-sfp-1.0"; - reg = <0xe8000 0x1000>; - }; - - serdes: serdes@ea000 { - compatible = "fsl,p2040-serdes"; - reg = <0xea000 0x1000>; - }; - - dma0: dma@100300 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "fsl,p2040-dma", "fsl,eloplus-dma"; - reg = <0x100300 0x4>; - ranges = <0x0 0x100100 0x200>; - cell-index = <0>; - dma-channel@0 { - compatible = "fsl,p2040-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x0 0x80>; - cell-index = <0>; - interrupts = <28 2 0 0>; - }; - dma-channel@80 { - compatible = "fsl,p2040-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x80 0x80>; - cell-index = <1>; - interrupts = <29 2 0 0>; - }; - dma-channel@100 { - compatible = "fsl,p2040-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x100 0x80>; - cell-index = <2>; - interrupts = <30 2 0 0>; - }; - dma-channel@180 { - compatible = "fsl,p2040-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x180 0x80>; - cell-index = <3>; - interrupts = <31 2 0 0>; - }; - }; - - dma1: dma@101300 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "fsl,p2040-dma", "fsl,eloplus-dma"; - reg = <0x101300 0x4>; - ranges = <0x0 0x101100 0x200>; - cell-index = <1>; - dma-channel@0 { - compatible = "fsl,p2040-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x0 0x80>; - cell-index = <0>; - interrupts = <32 2 0 0>; - }; - dma-channel@80 { - compatible = "fsl,p2040-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x80 0x80>; - cell-index = <1>; - interrupts = <33 2 0 0>; - }; - dma-channel@100 { - compatible = "fsl,p2040-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x100 0x80>; - cell-index = <2>; - interrupts = <34 2 0 0>; - }; - dma-channel@180 { - compatible = "fsl,p2040-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x180 0x80>; - cell-index = <3>; - interrupts = <35 2 0 0>; - }; - }; - - spi@110000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,p2040-espi", "fsl,mpc8536-espi"; - reg = <0x110000 0x1000>; - interrupts = <53 0x2 0 0>; - fsl,espi-num-chipselects = <4>; - - }; - - sdhc: sdhc@114000 { - compatible = "fsl,p2040-esdhc", "fsl,esdhc"; - reg = <0x114000 0x1000>; - interrupts = <48 2 0 0>; - sdhci,auto-cmd12; - clock-frequency = <0>; - }; - - - i2c@118000 { - #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - compatible = "fsl-i2c"; - reg = <0x118000 0x100>; - interrupts = <38 2 0 0>; - dfsrr; - }; - - i2c@118100 { - #address-cells = <1>; - #size-cells = <0>; - cell-index = <1>; - compatible = "fsl-i2c"; - reg = <0x118100 0x100>; - interrupts = <38 2 0 0>; - dfsrr; - }; - - i2c@119000 { - #address-cells = <1>; - #size-cells = <0>; - cell-index = <2>; - compatible = "fsl-i2c"; - reg = <0x119000 0x100>; - interrupts = <39 2 0 0>; - dfsrr; - }; - - i2c@119100 { - #address-cells = <1>; - #size-cells = <0>; - cell-index = <3>; - compatible = "fsl-i2c"; - reg = <0x119100 0x100>; - interrupts = <39 2 0 0>; - dfsrr; - }; - - serial0: serial@11c500 { - cell-index = <0>; - device_type = "serial"; - compatible = "ns16550"; - reg = <0x11c500 0x100>; - clock-frequency = <0>; - interrupts = <36 2 0 0>; - }; - - serial1: serial@11c600 { - cell-index = <1>; - device_type = "serial"; - compatible = "ns16550"; - reg = <0x11c600 0x100>; - clock-frequency = <0>; - interrupts = <36 2 0 0>; - }; - - serial2: serial@11d500 { - cell-index = <2>; - device_type = "serial"; - compatible = "ns16550"; - reg = <0x11d500 0x100>; - clock-frequency = <0>; - interrupts = <37 2 0 0>; - }; - - serial3: serial@11d600 { - cell-index = <3>; - device_type = "serial"; - compatible = "ns16550"; - reg = <0x11d600 0x100>; - clock-frequency = <0>; - interrupts = <37 2 0 0>; - }; - - gpio0: gpio@130000 { - compatible = "fsl,p2040-gpio", "fsl,qoriq-gpio"; - reg = <0x130000 0x1000>; - interrupts = <55 2 0 0>; - #gpio-cells = <2>; - gpio-controller; - }; - - usb0: usb@210000 { - compatible = "fsl,p2040-usb2-mph", - "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; - reg = <0x210000 0x1000>; - #address-cells = <1>; - #size-cells = <0>; - interrupts = <44 0x2 0 0>; - port0; - }; - - usb1: usb@211000 { - compatible = "fsl,p2040-usb2-dr", - "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; - reg = <0x211000 0x1000>; - #address-cells = <1>; - #size-cells = <0>; - interrupts = <45 0x2 0 0>; - }; - - sata@220000 { - compatible = "fsl,p2040-sata", "fsl,pq-sata-v2"; - reg = <0x220000 0x1000>; - interrupts = <68 0x2 0 0>; - }; - - sata@221000 { - compatible = "fsl,p2040-sata", "fsl,pq-sata-v2"; - reg = <0x221000 0x1000>; - interrupts = <69 0x2 0 0>; - }; - - crypto: crypto@300000 { - compatible = "fsl,sec-v4.2", "fsl,sec-v4.0"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x300000 0x10000>; - ranges = <0 0x300000 0x10000>; - interrupts = <92 2 0 0>; - - sec_jr0: jr@1000 { - compatible = "fsl,sec-v4.2-job-ring", - "fsl,sec-v4.0-job-ring"; - reg = <0x1000 0x1000>; - interrupts = <88 2 0 0>; - }; - - sec_jr1: jr@2000 { - compatible = "fsl,sec-v4.2-job-ring", - "fsl,sec-v4.0-job-ring"; - reg = <0x2000 0x1000>; - interrupts = <89 2 0 0>; - }; - - sec_jr2: jr@3000 { - compatible = "fsl,sec-v4.2-job-ring", - "fsl,sec-v4.0-job-ring"; - reg = <0x3000 0x1000>; - interrupts = <90 2 0 0>; - }; - - sec_jr3: jr@4000 { - compatible = "fsl,sec-v4.2-job-ring", - "fsl,sec-v4.0-job-ring"; - reg = <0x4000 0x1000>; - interrupts = <91 2 0 0>; - }; - - rtic@6000 { - compatible = "fsl,sec-v4.2-rtic", - "fsl,sec-v4.0-rtic"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x6000 0x100>; - ranges = <0x0 0x6100 0xe00>; - - rtic_a: rtic-a@0 { - compatible = "fsl,sec-v4.2-rtic-memory", - "fsl,sec-v4.0-rtic-memory"; - reg = <0x00 0x20 0x100 0x80>; - }; - - rtic_b: rtic-b@20 { - compatible = "fsl,sec-v4.2-rtic-memory", - "fsl,sec-v4.0-rtic-memory"; - reg = <0x20 0x20 0x200 0x80>; - }; - - rtic_c: rtic-c@40 { - compatible = "fsl,sec-v4.2-rtic-memory", - "fsl,sec-v4.0-rtic-memory"; - reg = <0x40 0x20 0x300 0x80>; - }; - - rtic_d: rtic-d@60 { - compatible = "fsl,sec-v4.2-rtic-memory", - "fsl,sec-v4.0-rtic-memory"; - reg = <0x60 0x20 0x500 0x80>; - }; - }; - }; - - sec_mon: sec_mon@314000 { - compatible = "fsl,sec-v4.2-mon", "fsl,sec-v4.0-mon"; - reg = <0x314000 0x1000>; - interrupts = <93 2 0 0>; - }; - - }; - - localbus@ffe124000 { - compatible = "fsl,p2040-elbc", "fsl,elbc", "simple-bus"; - interrupts = <25 2 0 0>; - #address-cells = <2>; - #size-cells = <1>; - }; - - pci0: pcie@ffe200000 { - compatible = "fsl,p2040-pcie", "fsl,qoriq-pcie-v2.2"; - device_type = "pci"; - #size-cells = <2>; - #address-cells = <3>; - bus-range = <0x0 0xff>; - clock-frequency = <0x1fca055>; - fsl,msi = <&msi0>; - interrupts = <16 2 1 15>; - pcie@0 { - reg = <0 0 0 0 0>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - device_type = "pci"; - interrupts = <16 2 1 15>; - interrupt-map-mask = <0xf800 0 0 7>; - interrupt-map = < - /* IDSEL 0x0 */ - 0000 0 0 1 &mpic 40 1 0 0 - 0000 0 0 2 &mpic 1 1 0 0 - 0000 0 0 3 &mpic 2 1 0 0 - 0000 0 0 4 &mpic 3 1 0 0 - >; - }; - }; - - pci1: pcie@ffe201000 { - compatible = "fsl,p2040-pcie", "fsl,qoriq-pcie-v2.2"; - device_type = "pci"; - #size-cells = <2>; - #address-cells = <3>; - bus-range = <0 0xff>; - clock-frequency = <0x1fca055>; - fsl,msi = <&msi1>; - interrupts = <16 2 1 14>; - pcie@0 { - reg = <0 0 0 0 0>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - device_type = "pci"; - interrupts = <16 2 1 14>; - interrupt-map-mask = <0xf800 0 0 7>; - interrupt-map = < - /* IDSEL 0x0 */ - 0000 0 0 1 &mpic 41 1 0 0 - 0000 0 0 2 &mpic 5 1 0 0 - 0000 0 0 3 &mpic 6 1 0 0 - 0000 0 0 4 &mpic 7 1 0 0 - >; - }; - }; - - pci2: pcie@ffe202000 { - compatible = "fsl,p2040-pcie", "fsl,qoriq-pcie-v2.2"; - device_type = "pci"; - #size-cells = <2>; - #address-cells = <3>; - bus-range = <0x0 0xff>; - clock-frequency = <0x1fca055>; - fsl,msi = <&msi2>; - interrupts = <16 2 1 13>; - pcie@0 { - reg = <0 0 0 0 0>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - device_type = "pci"; - interrupts = <16 2 1 13>; - interrupt-map-mask = <0xf800 0 0 7>; - interrupt-map = < - /* IDSEL 0x0 */ - 0000 0 0 1 &mpic 42 1 0 0 - 0000 0 0 2 &mpic 9 1 0 0 - 0000 0 0 3 &mpic 10 1 0 0 - 0000 0 0 4 &mpic 11 1 0 0 - >; - }; - }; -}; diff --git a/arch/powerpc/boot/dts/p2041rdb.dts b/arch/powerpc/boot/dts/p2041rdb.dts new file mode 100644 index 00000000000..47bb461cf72 --- /dev/null +++ b/arch/powerpc/boot/dts/p2041rdb.dts @@ -0,0 +1,161 @@ +/* + * P2041RDB Device Tree Source + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "p2041si.dtsi" + +/ { + model = "fsl,P2041RDB"; + compatible = "fsl,P2041RDB"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + memory { + device_type = "memory"; + }; + + soc: soc@ffe000000 { + spi@110000 { + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25sl12801"; + reg = <0>; + spi-max-frequency = <40000000>; /* input clock */ + partition@u-boot { + label = "u-boot"; + reg = <0x00000000 0x00100000>; + read-only; + }; + partition@kernel { + label = "kernel"; + reg = <0x00100000 0x00500000>; + read-only; + }; + partition@dtb { + label = "dtb"; + reg = <0x00600000 0x00100000>; + read-only; + }; + partition@fs { + label = "file system"; + reg = <0x00700000 0x00900000>; + }; + }; + }; + + i2c@118000 { + lm75b@48 { + compatible = "nxp,lm75a"; + reg = <0x48>; + }; + eeprom@50 { + compatible = "at24,24c256"; + reg = <0x50>; + }; + rtc@68 { + compatible = "pericom,pt7c4338"; + reg = <0x68>; + }; + }; + + i2c@118100 { + eeprom@50 { + compatible = "at24,24c256"; + reg = <0x50>; + }; + }; + + usb1: usb@211000 { + dr_mode = "host"; + }; + }; + + localbus@ffe124000 { + reg = <0xf 0xfe124000 0 0x1000>; + ranges = <0 0 0xf 0xe8000000 0x08000000>; + + flash@0,0 { + compatible = "cfi-flash"; + reg = <0 0 0x08000000>; + bank-width = <2>; + device-width = <2>; + }; + }; + + pci0: pcie@ffe200000 { + reg = <0xf 0xfe200000 0 0x1000>; + ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x20000000 + 0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>; + pcie@0 { + ranges = <0x02000000 0 0xe0000000 + 0x02000000 0 0xe0000000 + 0 0x20000000 + + 0x01000000 0 0x00000000 + 0x01000000 0 0x00000000 + 0 0x00010000>; + }; + }; + + pci1: pcie@ffe201000 { + reg = <0xf 0xfe201000 0 0x1000>; + ranges = <0x02000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 + 0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>; + pcie@0 { + ranges = <0x02000000 0 0xe0000000 + 0x02000000 0 0xe0000000 + 0 0x20000000 + + 0x01000000 0 0x00000000 + 0x01000000 0 0x00000000 + 0 0x00010000>; + }; + }; + + pci2: pcie@ffe202000 { + reg = <0xf 0xfe202000 0 0x1000>; + ranges = <0x02000000 0 0xe0000000 0xc 0x40000000 0 0x20000000 + 0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>; + pcie@0 { + ranges = <0x02000000 0 0xe0000000 + 0x02000000 0 0xe0000000 + 0 0x20000000 + + 0x01000000 0 0x00000000 + 0x01000000 0 0x00000000 + 0 0x00010000>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/p2041si.dtsi b/arch/powerpc/boot/dts/p2041si.dtsi new file mode 100644 index 00000000000..420cdb0f403 --- /dev/null +++ b/arch/powerpc/boot/dts/p2041si.dtsi @@ -0,0 +1,623 @@ +/* + * P2041 Silicon Device Tree Source + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/dts-v1/; + +/ { + compatible = "fsl,P2041"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + aliases { + ccsr = &soc; + + serial0 = &serial0; + serial1 = &serial1; + serial2 = &serial2; + serial3 = &serial3; + pci0 = &pci0; + pci1 = &pci1; + pci2 = &pci2; + usb0 = &usb0; + usb1 = &usb1; + dma0 = &dma0; + dma1 = &dma1; + sdhc = &sdhc; + msi0 = &msi0; + msi1 = &msi1; + msi2 = &msi2; + + crypto = &crypto; + sec_jr0 = &sec_jr0; + sec_jr1 = &sec_jr1; + sec_jr2 = &sec_jr2; + sec_jr3 = &sec_jr3; + rtic_a = &rtic_a; + rtic_b = &rtic_b; + rtic_c = &rtic_c; + rtic_d = &rtic_d; + sec_mon = &sec_mon; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: PowerPC,e500mc@0 { + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2_0>; + L2_0: l2-cache { + next-level-cache = <&cpc>; + }; + }; + cpu1: PowerPC,e500mc@1 { + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2_1>; + L2_1: l2-cache { + next-level-cache = <&cpc>; + }; + }; + cpu2: PowerPC,e500mc@2 { + device_type = "cpu"; + reg = <2>; + next-level-cache = <&L2_2>; + L2_2: l2-cache { + next-level-cache = <&cpc>; + }; + }; + cpu3: PowerPC,e500mc@3 { + device_type = "cpu"; + reg = <3>; + next-level-cache = <&L2_3>; + L2_3: l2-cache { + next-level-cache = <&cpc>; + }; + }; + }; + + soc: soc@ffe000000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "soc"; + compatible = "simple-bus"; + ranges = <0x00000000 0xf 0xfe000000 0x1000000>; + reg = <0xf 0xfe000000 0 0x00001000>; + + soc-sram-error { + compatible = "fsl,soc-sram-error"; + interrupts = <16 2 1 29>; + }; + + corenet-law@0 { + compatible = "fsl,corenet-law"; + reg = <0x0 0x1000>; + fsl,num-laws = <32>; + }; + + memory-controller@8000 { + compatible = "fsl,qoriq-memory-controller-v4.5", "fsl,qoriq-memory-controller"; + reg = <0x8000 0x1000>; + interrupts = <16 2 1 23>; + }; + + cpc: l3-cache-controller@10000 { + compatible = "fsl,p2041-l3-cache-controller", "fsl,p4080-l3-cache-controller", "cache"; + reg = <0x10000 0x1000>; + interrupts = <16 2 1 27>; + }; + + corenet-cf@18000 { + compatible = "fsl,corenet-cf"; + reg = <0x18000 0x1000>; + interrupts = <16 2 1 31>; + fsl,ccf-num-csdids = <32>; + fsl,ccf-num-snoopids = <32>; + }; + + iommu@20000 { + compatible = "fsl,pamu-v1.0", "fsl,pamu"; + reg = <0x20000 0x4000>; + interrupts = < + 24 2 0 0 + 16 2 1 30>; + }; + + mpic: pic@40000 { + clock-frequency = <0>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <4>; + reg = <0x40000 0x40000>; + compatible = "fsl,mpic", "chrp,open-pic"; + device_type = "open-pic"; + }; + + msi0: msi@41600 { + compatible = "fsl,mpic-msi"; + reg = <0x41600 0x200>; + msi-available-ranges = <0 0x100>; + interrupts = < + 0xe0 0 0 0 + 0xe1 0 0 0 + 0xe2 0 0 0 + 0xe3 0 0 0 + 0xe4 0 0 0 + 0xe5 0 0 0 + 0xe6 0 0 0 + 0xe7 0 0 0>; + }; + + msi1: msi@41800 { + compatible = "fsl,mpic-msi"; + reg = <0x41800 0x200>; + msi-available-ranges = <0 0x100>; + interrupts = < + 0xe8 0 0 0 + 0xe9 0 0 0 + 0xea 0 0 0 + 0xeb 0 0 0 + 0xec 0 0 0 + 0xed 0 0 0 + 0xee 0 0 0 + 0xef 0 0 0>; + }; + + msi2: msi@41a00 { + compatible = "fsl,mpic-msi"; + reg = <0x41a00 0x200>; + msi-available-ranges = <0 0x100>; + interrupts = < + 0xf0 0 0 0 + 0xf1 0 0 0 + 0xf2 0 0 0 + 0xf3 0 0 0 + 0xf4 0 0 0 + 0xf5 0 0 0 + 0xf6 0 0 0 + 0xf7 0 0 0>; + }; + + guts: global-utilities@e0000 { + compatible = "fsl,qoriq-device-config-1.0"; + reg = <0xe0000 0xe00>; + fsl,has-rstcr; + #sleep-cells = <1>; + fsl,liodn-bits = <12>; + }; + + pins: global-utilities@e0e00 { + compatible = "fsl,qoriq-pin-control-1.0"; + reg = <0xe0e00 0x200>; + #sleep-cells = <2>; + }; + + clockgen: global-utilities@e1000 { + compatible = "fsl,p2041-clockgen", "fsl,qoriq-clockgen-1.0"; + reg = <0xe1000 0x1000>; + clock-frequency = <0>; + }; + + rcpm: global-utilities@e2000 { + compatible = "fsl,qoriq-rcpm-1.0"; + reg = <0xe2000 0x1000>; + #sleep-cells = <1>; + }; + + sfp: sfp@e8000 { + compatible = "fsl,p2041-sfp", "fsl,qoriq-sfp-1.0"; + reg = <0xe8000 0x1000>; + }; + + serdes: serdes@ea000 { + compatible = "fsl,p2041-serdes"; + reg = <0xea000 0x1000>; + }; + + dma0: dma@100300 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,p2041-dma", "fsl,eloplus-dma"; + reg = <0x100300 0x4>; + ranges = <0x0 0x100100 0x200>; + cell-index = <0>; + dma-channel@0 { + compatible = "fsl,p2041-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x0 0x80>; + cell-index = <0>; + interrupts = <28 2 0 0>; + }; + dma-channel@80 { + compatible = "fsl,p2041-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x80 0x80>; + cell-index = <1>; + interrupts = <29 2 0 0>; + }; + dma-channel@100 { + compatible = "fsl,p2041-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x100 0x80>; + cell-index = <2>; + interrupts = <30 2 0 0>; + }; + dma-channel@180 { + compatible = "fsl,p2041-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x180 0x80>; + cell-index = <3>; + interrupts = <31 2 0 0>; + }; + }; + + dma1: dma@101300 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,p2041-dma", "fsl,eloplus-dma"; + reg = <0x101300 0x4>; + ranges = <0x0 0x101100 0x200>; + cell-index = <1>; + dma-channel@0 { + compatible = "fsl,p2041-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x0 0x80>; + cell-index = <0>; + interrupts = <32 2 0 0>; + }; + dma-channel@80 { + compatible = "fsl,p2041-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x80 0x80>; + cell-index = <1>; + interrupts = <33 2 0 0>; + }; + dma-channel@100 { + compatible = "fsl,p2041-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x100 0x80>; + cell-index = <2>; + interrupts = <34 2 0 0>; + }; + dma-channel@180 { + compatible = "fsl,p2041-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x180 0x80>; + cell-index = <3>; + interrupts = <35 2 0 0>; + }; + }; + + spi@110000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,p2041-espi", "fsl,mpc8536-espi"; + reg = <0x110000 0x1000>; + interrupts = <53 0x2 0 0>; + fsl,espi-num-chipselects = <4>; + }; + + sdhc: sdhc@114000 { + compatible = "fsl,p2041-esdhc", "fsl,esdhc"; + reg = <0x114000 0x1000>; + interrupts = <48 2 0 0>; + sdhci,auto-cmd12; + clock-frequency = <0>; + }; + + i2c@118000 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + compatible = "fsl-i2c"; + reg = <0x118000 0x100>; + interrupts = <38 2 0 0>; + dfsrr; + }; + + i2c@118100 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <1>; + compatible = "fsl-i2c"; + reg = <0x118100 0x100>; + interrupts = <38 2 0 0>; + dfsrr; + }; + + i2c@119000 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <2>; + compatible = "fsl-i2c"; + reg = <0x119000 0x100>; + interrupts = <39 2 0 0>; + dfsrr; + }; + + i2c@119100 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <3>; + compatible = "fsl-i2c"; + reg = <0x119100 0x100>; + interrupts = <39 2 0 0>; + dfsrr; + }; + + serial0: serial@11c500 { + cell-index = <0>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x11c500 0x100>; + clock-frequency = <0>; + interrupts = <36 2 0 0>; + }; + + serial1: serial@11c600 { + cell-index = <1>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x11c600 0x100>; + clock-frequency = <0>; + interrupts = <36 2 0 0>; + }; + + serial2: serial@11d500 { + cell-index = <2>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x11d500 0x100>; + clock-frequency = <0>; + interrupts = <37 2 0 0>; + }; + + serial3: serial@11d600 { + cell-index = <3>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x11d600 0x100>; + clock-frequency = <0>; + interrupts = <37 2 0 0>; + }; + + gpio0: gpio@130000 { + compatible = "fsl,p2041-gpio", "fsl,qoriq-gpio"; + reg = <0x130000 0x1000>; + interrupts = <55 2 0 0>; + #gpio-cells = <2>; + gpio-controller; + }; + + usb0: usb@210000 { + compatible = "fsl,p2041-usb2-mph", + "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; + reg = <0x210000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <44 0x2 0 0>; + phy_type = "utmi"; + port0; + }; + + usb1: usb@211000 { + compatible = "fsl,p2041-usb2-dr", + "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; + reg = <0x211000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <45 0x2 0 0>; + phy_type = "utmi"; + }; + + sata@220000 { + compatible = "fsl,p2041-sata", "fsl,pq-sata-v2"; + reg = <0x220000 0x1000>; + interrupts = <68 0x2 0 0>; + }; + + sata@221000 { + compatible = "fsl,p2041-sata", "fsl,pq-sata-v2"; + reg = <0x221000 0x1000>; + interrupts = <69 0x2 0 0>; + }; + + crypto: crypto@300000 { + compatible = "fsl,sec-v4.2", "fsl,sec-v4.0"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x300000 0x10000>; + ranges = <0 0x300000 0x10000>; + interrupts = <92 2 0 0>; + + sec_jr0: jr@1000 { + compatible = "fsl,sec-v4.2-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x1000 0x1000>; + interrupts = <88 2 0 0>; + }; + + sec_jr1: jr@2000 { + compatible = "fsl,sec-v4.2-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x2000 0x1000>; + interrupts = <89 2 0 0>; + }; + + sec_jr2: jr@3000 { + compatible = "fsl,sec-v4.2-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x3000 0x1000>; + interrupts = <90 2 0 0>; + }; + + sec_jr3: jr@4000 { + compatible = "fsl,sec-v4.2-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x4000 0x1000>; + interrupts = <91 2 0 0>; + }; + + rtic@6000 { + compatible = "fsl,sec-v4.2-rtic", + "fsl,sec-v4.0-rtic"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x6000 0x100>; + ranges = <0x0 0x6100 0xe00>; + + rtic_a: rtic-a@0 { + compatible = "fsl,sec-v4.2-rtic-memory", + "fsl,sec-v4.0-rtic-memory"; + reg = <0x00 0x20 0x100 0x80>; + }; + + rtic_b: rtic-b@20 { + compatible = "fsl,sec-v4.2-rtic-memory", + "fsl,sec-v4.0-rtic-memory"; + reg = <0x20 0x20 0x200 0x80>; + }; + + rtic_c: rtic-c@40 { + compatible = "fsl,sec-v4.2-rtic-memory", + "fsl,sec-v4.0-rtic-memory"; + reg = <0x40 0x20 0x300 0x80>; + }; + + rtic_d: rtic-d@60 { + compatible = "fsl,sec-v4.2-rtic-memory", + "fsl,sec-v4.0-rtic-memory"; + reg = <0x60 0x20 0x500 0x80>; + }; + }; + }; + + sec_mon: sec_mon@314000 { + compatible = "fsl,sec-v4.2-mon", "fsl,sec-v4.0-mon"; + reg = <0x314000 0x1000>; + interrupts = <93 2 0 0>; + }; + + }; + + localbus@ffe124000 { + compatible = "fsl,p2041-elbc", "fsl,elbc", "simple-bus"; + interrupts = <25 2 0 0>; + #address-cells = <2>; + #size-cells = <1>; + }; + + pci0: pcie@ffe200000 { + compatible = "fsl,p2041-pcie", "fsl,qoriq-pcie-v2.2"; + device_type = "pci"; + #size-cells = <2>; + #address-cells = <3>; + bus-range = <0x0 0xff>; + clock-frequency = <33333333>; + fsl,msi = <&msi0>; + interrupts = <16 2 1 15>; + pcie@0 { + reg = <0 0 0 0 0>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + interrupts = <16 2 1 15>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 40 1 0 0 + 0000 0 0 2 &mpic 1 1 0 0 + 0000 0 0 3 &mpic 2 1 0 0 + 0000 0 0 4 &mpic 3 1 0 0 + >; + }; + }; + + pci1: pcie@ffe201000 { + compatible = "fsl,p2041-pcie", "fsl,qoriq-pcie-v2.2"; + device_type = "pci"; + #size-cells = <2>; + #address-cells = <3>; + bus-range = <0 0xff>; + clock-frequency = <33333333>; + fsl,msi = <&msi1>; + interrupts = <16 2 1 14>; + pcie@0 { + reg = <0 0 0 0 0>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + interrupts = <16 2 1 14>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 41 1 0 0 + 0000 0 0 2 &mpic 5 1 0 0 + 0000 0 0 3 &mpic 6 1 0 0 + 0000 0 0 4 &mpic 7 1 0 0 + >; + }; + }; + + pci2: pcie@ffe202000 { + compatible = "fsl,p2041-pcie", "fsl,qoriq-pcie-v2.2"; + device_type = "pci"; + #size-cells = <2>; + #address-cells = <3>; + bus-range = <0x0 0xff>; + clock-frequency = <33333333>; + fsl,msi = <&msi2>; + interrupts = <16 2 1 13>; + pcie@0 { + reg = <0 0 0 0 0>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + interrupts = <16 2 1 13>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 42 1 0 0 + 0000 0 0 2 &mpic 9 1 0 0 + 0000 0 0 3 &mpic 10 1 0 0 + 0000 0 0 4 &mpic 11 1 0 0 + >; + }; + }; +}; diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig index 4311d02a3bf..49cfe85232f 100644 --- a/arch/powerpc/configs/corenet32_smp_defconfig +++ b/arch/powerpc/configs/corenet32_smp_defconfig @@ -23,7 +23,7 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set -CONFIG_P2040_RDB=y +CONFIG_P2041_RDB=y CONFIG_P3041_DS=y CONFIG_P4080_DS=y CONFIG_P5020_DS=y diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 1b393f40c63..1e66edb95d2 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -171,8 +171,8 @@ config SBC8560 help This option enables support for the Wind River SBC8560 board -config P2040_RDB - bool "Freescale P2040 RDB" +config P2041_RDB + bool "Freescale P2041 RDB" select DEFAULT_UIMAGE select PPC_E500MC select PHYS_64BIT @@ -182,7 +182,7 @@ config P2040_RDB select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help - This option enables support for the P2040 RDB board + This option enables support for the P2041 RDB board config P3041_DS bool "Freescale P3041 DS" diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index a971b32c5c0..39e6c22f06f 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MPC85xx_RDB) += mpc85xx_rdb.o obj-$(CONFIG_P1010_RDB) += p1010rdb.o obj-$(CONFIG_P1022_DS) += p1022_ds.o obj-$(CONFIG_P1023_RDS) += p1023_rds.o -obj-$(CONFIG_P2040_RDB) += p2040_rdb.o corenet_ds.o +obj-$(CONFIG_P2041_RDB) += p2041_rdb.o corenet_ds.o obj-$(CONFIG_P3041_DS) += p3041_ds.o corenet_ds.o obj-$(CONFIG_P4080_DS) += p4080_ds.o corenet_ds.o obj-$(CONFIG_P5020_DS) += p5020_ds.o corenet_ds.o diff --git a/arch/powerpc/platforms/85xx/p2040_rdb.c b/arch/powerpc/platforms/85xx/p2040_rdb.c deleted file mode 100644 index 32b56ac73df..00000000000 --- a/arch/powerpc/platforms/85xx/p2040_rdb.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * P2040 RDB Setup - * - * Copyright 2011 Freescale Semiconductor Inc. - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "corenet_ds.h" - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init p2040_rdb_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); -#ifdef CONFIG_SMP - extern struct smp_ops_t smp_85xx_ops; -#endif - - if (of_flat_dt_is_compatible(root, "fsl,P2040RDB")) - return 1; - - /* Check if we're running under the Freescale hypervisor */ - if (of_flat_dt_is_compatible(root, "fsl,P2040RDB-hv")) { - ppc_md.init_IRQ = ehv_pic_init; - ppc_md.get_irq = ehv_pic_get_irq; - ppc_md.restart = fsl_hv_restart; - ppc_md.power_off = fsl_hv_halt; - ppc_md.halt = fsl_hv_halt; -#ifdef CONFIG_SMP - /* - * Disable the timebase sync operations because we can't write - * to the timebase registers under the hypervisor. - */ - smp_85xx_ops.give_timebase = NULL; - smp_85xx_ops.take_timebase = NULL; -#endif - return 1; - } - - return 0; -} - -define_machine(p2040_rdb) { - .name = "P2040 RDB", - .probe = p2040_rdb_probe, - .setup_arch = corenet_ds_setup_arch, - .init_IRQ = corenet_ds_pic_init, -#ifdef CONFIG_PCI - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, -#endif - .get_irq = mpic_get_coreint_irq, - .restart = fsl_rstcr_restart, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, - .power_save = e500_idle, -}; - -machine_device_initcall(p2040_rdb, corenet_ds_publish_devices); - -#ifdef CONFIG_SWIOTLB -machine_arch_initcall(p2040_rdb, swiotlb_setup_bus_notifier); -#endif diff --git a/arch/powerpc/platforms/85xx/p2041_rdb.c b/arch/powerpc/platforms/85xx/p2041_rdb.c new file mode 100644 index 00000000000..eda6ed5683e --- /dev/null +++ b/arch/powerpc/platforms/85xx/p2041_rdb.c @@ -0,0 +1,88 @@ +/* + * P2041 RDB Setup + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "corenet_ds.h" + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p2041_rdb_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); +#ifdef CONFIG_SMP + extern struct smp_ops_t smp_85xx_ops; +#endif + + if (of_flat_dt_is_compatible(root, "fsl,P2041RDB")) + return 1; + + /* Check if we're running under the Freescale hypervisor */ + if (of_flat_dt_is_compatible(root, "fsl,P2041RDB-hv")) { + ppc_md.init_IRQ = ehv_pic_init; + ppc_md.get_irq = ehv_pic_get_irq; + ppc_md.restart = fsl_hv_restart; + ppc_md.power_off = fsl_hv_halt; + ppc_md.halt = fsl_hv_halt; +#ifdef CONFIG_SMP + /* + * Disable the timebase sync operations because we can't write + * to the timebase registers under the hypervisor. + */ + smp_85xx_ops.give_timebase = NULL; + smp_85xx_ops.take_timebase = NULL; +#endif + return 1; + } + + return 0; +} + +define_machine(p2041_rdb) { + .name = "P2041 RDB", + .probe = p2041_rdb_probe, + .setup_arch = corenet_ds_setup_arch, + .init_IRQ = corenet_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_coreint_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, + .power_save = e500_idle, +}; + +machine_device_initcall(p2041_rdb, corenet_ds_publish_devices); + +#ifdef CONFIG_SWIOTLB +machine_arch_initcall(p2041_rdb, swiotlb_setup_bus_notifier); +#endif -- cgit v1.2.3-18-g5258 From b430abc4d10537992a5ba38eff6c46b3c9009a19 Mon Sep 17 00:00:00 2001 From: Liu Yu Date: Mon, 5 Sep 2011 17:01:21 +0800 Subject: powerpc/math_emu/efp: Use pr_debug instead of printk Signed-off-by: Liu Yu Signed-off-by: Kumar Gala --- arch/powerpc/math-emu/math_efp.c | 83 ++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/arch/powerpc/math-emu/math_efp.c b/arch/powerpc/math-emu/math_efp.c index 62279200d96..10fda0f13a0 100644 --- a/arch/powerpc/math-emu/math_efp.c +++ b/arch/powerpc/math-emu/math_efp.c @@ -212,12 +212,10 @@ int do_spe_mathemu(struct pt_regs *regs) __FPU_FPSCR = mfspr(SPRN_SPEFSCR); -#ifdef DEBUG - printk("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); - printk("vc: %08x %08x\n", vc.wp[0], vc.wp[1]); - printk("va: %08x %08x\n", va.wp[0], va.wp[1]); - printk("vb: %08x %08x\n", vb.wp[0], vb.wp[1]); -#endif + pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); + pr_debug("vc: %08x %08x\n", vc.wp[0], vc.wp[1]); + pr_debug("va: %08x %08x\n", va.wp[0], va.wp[1]); + pr_debug("vb: %08x %08x\n", vb.wp[0], vb.wp[1]); switch (src) { case SPFP: { @@ -235,10 +233,8 @@ int do_spe_mathemu(struct pt_regs *regs) break; } -#ifdef DEBUG - printk("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c); - printk("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c); -#endif + pr_debug("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c); + pr_debug("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c); switch (func) { case EFSABS: @@ -305,10 +301,10 @@ int do_spe_mathemu(struct pt_regs *regs) FP_DECL_D(DB); FP_CLEAR_EXCEPTIONS; FP_UNPACK_DP(DB, vb.dp); -#ifdef DEBUG - printk("DB: %ld %08lx %08lx %ld (%ld)\n", + + pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n", DB_s, DB_f1, DB_f0, DB_e, DB_c); -#endif + FP_CONV(S, D, 1, 2, SR, DB); goto pack_s; } @@ -332,9 +328,8 @@ int do_spe_mathemu(struct pt_regs *regs) break; pack_s: -#ifdef DEBUG - printk("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c); -#endif + pr_debug("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c); + FP_PACK_SP(vc.wp + 1, SR); goto update_regs; @@ -365,12 +360,10 @@ cmp_s: break; } -#ifdef DEBUG - printk("DA: %ld %08lx %08lx %ld (%ld)\n", + pr_debug("DA: %ld %08lx %08lx %ld (%ld)\n", DA_s, DA_f1, DA_f0, DA_e, DA_c); - printk("DB: %ld %08lx %08lx %ld (%ld)\n", + pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n", DB_s, DB_f1, DB_f0, DB_e, DB_c); -#endif switch (func) { case EFDABS: @@ -438,10 +431,10 @@ cmp_s: FP_DECL_S(SB); FP_CLEAR_EXCEPTIONS; FP_UNPACK_SP(SB, vb.wp + 1); -#ifdef DEBUG - printk("SB: %ld %08lx %ld (%ld)\n", + + pr_debug("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c); -#endif + FP_CONV(D, S, 2, 1, DR, SB); goto pack_d; } @@ -471,10 +464,9 @@ cmp_s: break; pack_d: -#ifdef DEBUG - printk("DR: %ld %08lx %08lx %ld (%ld)\n", + pr_debug("DR: %ld %08lx %08lx %ld (%ld)\n", DR_s, DR_f1, DR_f0, DR_e, DR_c); -#endif + FP_PACK_DP(vc.dp, DR); goto update_regs; @@ -511,12 +503,14 @@ cmp_d: break; } -#ifdef DEBUG - printk("SA0: %ld %08lx %ld (%ld)\n", SA0_s, SA0_f, SA0_e, SA0_c); - printk("SA1: %ld %08lx %ld (%ld)\n", SA1_s, SA1_f, SA1_e, SA1_c); - printk("SB0: %ld %08lx %ld (%ld)\n", SB0_s, SB0_f, SB0_e, SB0_c); - printk("SB1: %ld %08lx %ld (%ld)\n", SB1_s, SB1_f, SB1_e, SB1_c); -#endif + pr_debug("SA0: %ld %08lx %ld (%ld)\n", + SA0_s, SA0_f, SA0_e, SA0_c); + pr_debug("SA1: %ld %08lx %ld (%ld)\n", + SA1_s, SA1_f, SA1_e, SA1_c); + pr_debug("SB0: %ld %08lx %ld (%ld)\n", + SB0_s, SB0_f, SB0_e, SB0_c); + pr_debug("SB1: %ld %08lx %ld (%ld)\n", + SB1_s, SB1_f, SB1_e, SB1_c); switch (func) { case EVFSABS: @@ -605,10 +599,11 @@ cmp_d: break; pack_vs: -#ifdef DEBUG - printk("SR0: %ld %08lx %ld (%ld)\n", SR0_s, SR0_f, SR0_e, SR0_c); - printk("SR1: %ld %08lx %ld (%ld)\n", SR1_s, SR1_f, SR1_e, SR1_c); -#endif + pr_debug("SR0: %ld %08lx %ld (%ld)\n", + SR0_s, SR0_f, SR0_e, SR0_c); + pr_debug("SR1: %ld %08lx %ld (%ld)\n", + SR1_s, SR1_f, SR1_e, SR1_c); + FP_PACK_SP(vc.wp, SR0); FP_PACK_SP(vc.wp + 1, SR1); goto update_regs; @@ -646,14 +641,12 @@ update_regs: current->thread.evr[fc] = vc.wp[0]; regs->gpr[fc] = vc.wp[1]; -#ifdef DEBUG - printk("ccr = %08lx\n", regs->ccr); - printk("cur exceptions = %08x spefscr = %08lx\n", + pr_debug("ccr = %08lx\n", regs->ccr); + pr_debug("cur exceptions = %08x spefscr = %08lx\n", FP_CUR_EXCEPTIONS, __FPU_FPSCR); - printk("vc: %08x %08x\n", vc.wp[0], vc.wp[1]); - printk("va: %08x %08x\n", va.wp[0], va.wp[1]); - printk("vb: %08x %08x\n", vb.wp[0], vb.wp[1]); -#endif + pr_debug("vc: %08x %08x\n", vc.wp[0], vc.wp[1]); + pr_debug("va: %08x %08x\n", va.wp[0], va.wp[1]); + pr_debug("vb: %08x %08x\n", vb.wp[0], vb.wp[1]); return 0; @@ -661,9 +654,7 @@ illegal: if (have_e500_cpu_a005_erratum) { /* according to e500 cpu a005 erratum, reissue efp inst */ regs->nip -= 4; -#ifdef DEBUG - printk(KERN_DEBUG "re-issue efp inst: %08lx\n", speinsn); -#endif + pr_debug("re-issue efp inst: %08lx\n", speinsn); return 0; } -- cgit v1.2.3-18-g5258 From d5755e6f3a412f012377e28b655d89eff3311f1c Mon Sep 17 00:00:00 2001 From: Liu Yu Date: Mon, 5 Sep 2011 17:01:22 +0800 Subject: powerpc/math_emu/efp: No need to round if the result is exact Signed-off-by: Liu Yu Signed-off-by: Kumar Gala --- arch/powerpc/math-emu/math_efp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/math-emu/math_efp.c b/arch/powerpc/math-emu/math_efp.c index 10fda0f13a0..96dbbae1a59 100644 --- a/arch/powerpc/math-emu/math_efp.c +++ b/arch/powerpc/math-emu/math_efp.c @@ -676,13 +676,20 @@ int speround_handler(struct pt_regs *regs) type = insn_type(speinsn & 0x7ff); if (type == XCR) return -ENOSYS; + __FPU_FPSCR = mfspr(SPRN_SPEFSCR); + pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); + + /* No need to round if the result is exact */ + if (!(__FPU_FPSCR & FP_EX_INEXACT)) + return 0; + fc = (speinsn >> 21) & 0x1f; s_lo = regs->gpr[fc] & SIGN_BIT_S; s_hi = current->thread.evr[fc] & SIGN_BIT_S; fgpr.wp[0] = current->thread.evr[fc]; fgpr.wp[1] = regs->gpr[fc]; - __FPU_FPSCR = mfspr(SPRN_SPEFSCR); + pr_debug("round fgpr: %08x %08x\n", fgpr.wp[0], fgpr.wp[1]); switch ((speinsn >> 5) & 0x7) { /* Since SPE instructions on E500 core can handle round to nearest @@ -722,6 +729,8 @@ int speround_handler(struct pt_regs *regs) current->thread.evr[fc] = fgpr.wp[0]; regs->gpr[fc] = fgpr.wp[1]; + pr_debug(" to fgpr: %08x %08x\n", fgpr.wp[0], fgpr.wp[1]); + return 0; } -- cgit v1.2.3-18-g5258 From 09af52f78e58d9aee0980276833bf3adee3ed07b Mon Sep 17 00:00:00 2001 From: Liu Yu Date: Mon, 5 Sep 2011 17:01:23 +0800 Subject: powerpc/math_emu/efp: Look for errata handler when type mismatches We already have cpu a005 errata handler when instruction cannot be recognized. Before we lookup the inst, there's type checking, and we also need to handle it in errata handler when the type checking failed. Signed-off-by: Liu Yu Signed-off-by: Kumar Gala --- arch/powerpc/math-emu/math_efp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/powerpc/math-emu/math_efp.c b/arch/powerpc/math-emu/math_efp.c index 96dbbae1a59..a73f0884d35 100644 --- a/arch/powerpc/math-emu/math_efp.c +++ b/arch/powerpc/math-emu/math_efp.c @@ -171,10 +171,6 @@ static unsigned long insn_type(unsigned long speinsn) case EFDNABS: ret = XA; break; case EFDNEG: ret = XA; break; case EFDSUB: ret = AB; break; - - default: - printk(KERN_ERR "\nOoops! SPE instruction no type found."); - printk(KERN_ERR "\ninst code: %08lx\n", speinsn); } return ret; @@ -195,7 +191,7 @@ int do_spe_mathemu(struct pt_regs *regs) type = insn_type(speinsn); if (type == NOTYPE) - return -ENOSYS; + goto illegal; func = speinsn & 0x7ff; fc = (speinsn >> 21) & 0x1f; -- cgit v1.2.3-18-g5258 From 37caf9f2a1b99d11ba71e17168d221da9ca13f24 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Sat, 27 Aug 2011 06:14:23 -0500 Subject: powerpc/fsl-booke: Handle L1 D-cache parity error correctly on e500mc If the L1 D-Cache is in write shadow mode the HW will auto-recover the error. However we might still log the error and cause a machine check (if L1CSR0[CPE] - Cache error checking enable). We should only treat the non-write shadow case as non-recoverable. Signed-off-by: Kumar Gala --- arch/powerpc/include/asm/reg_booke.h | 3 +++ arch/powerpc/kernel/traps.c | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h index 9ec0b39f9dd..28cdbd9f399 100644 --- a/arch/powerpc/include/asm/reg_booke.h +++ b/arch/powerpc/include/asm/reg_booke.h @@ -548,6 +548,9 @@ #define L1CSR1_ICFI 0x00000002 /* Instr Cache Flash Invalidate */ #define L1CSR1_ICE 0x00000001 /* Instr Cache Enable */ +/* Bit definitions for L1CSR2. */ +#define L1CSR2_DCWS 0x40000000 /* Data Cache write shadow */ + /* Bit definitions for L2CSR0. */ #define L2CSR0_L2E 0x80000000 /* L2 Cache Enable */ #define L2CSR0_L2PE 0x40000000 /* L2 Cache Parity/ECC Enable */ diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index f19d9777d3c..4e5908264d1 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -457,7 +457,14 @@ int machine_check_e500mc(struct pt_regs *regs) if (reason & MCSR_DCPERR_MC) { printk("Data Cache Parity Error\n"); - recoverable = 0; + + /* + * In write shadow mode we auto-recover from the error, but it + * may still get logged and cause a machine check. We should + * only treat the non-write shadow case as non-recoverable. + */ + if (!(mfspr(SPRN_L1CSR2) & L1CSR2_DCWS)) + recoverable = 0; } if (reason & MCSR_L2MMU_MHIT) { -- cgit v1.2.3-18-g5258 From 2a05e333c2cee711c828d79300010b721c81574f Mon Sep 17 00:00:00 2001 From: Holger Brunck Date: Tue, 27 Sep 2011 09:44:50 +0200 Subject: powerpc/82xx: updates for mgcoge Add: - Setup dts node for USB - pin description and setup for SMC1 (serial interface) Update and cleanup mgcoge_defconfig: - enable: TIPC, UBIFS, USB_GADGET driver, SQUASHFS, HIGHRES timers POSIX_MQUEUE, EMBEDDED - disable: EXT3, PPC_PMAC Signed-off-by: Holger Brunck Acked-by: Heiko Schocher cc: Kumar Gala Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/mgcoge.dts | 9 +++++++++ arch/powerpc/configs/mgcoge_defconfig | 27 ++++++++++++++++----------- arch/powerpc/platforms/82xx/km82xx.c | 4 ++++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/boot/dts/mgcoge.dts b/arch/powerpc/boot/dts/mgcoge.dts index 1360d2f6902..ededaf5ac01 100644 --- a/arch/powerpc/boot/dts/mgcoge.dts +++ b/arch/powerpc/boot/dts/mgcoge.dts @@ -213,6 +213,15 @@ linux,network-index = <2>; fsl,cpm-command = <0x16200300>; }; + + usb@11b60 { + compatible = "fsl,mpc8272-cpm-usb"; + mode = "peripheral"; + reg = <0x11b60 0x40 0x8b00 0x100>; + interrupts = <11 8>; + interrupt-parent = <&PIC>; + usb-clock = <5>; + }; }; cpm2_pio_c: gpio-controller@10d40 { diff --git a/arch/powerpc/configs/mgcoge_defconfig b/arch/powerpc/configs/mgcoge_defconfig index 6cb588a7d42..0d36b0e1e26 100644 --- a/arch/powerpc/configs/mgcoge_defconfig +++ b/arch/powerpc/configs/mgcoge_defconfig @@ -1,15 +1,22 @@ +CONFIG_EXPERIMENTAL=y +# CONFIG_SWAP is not set CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y CONFIG_SPARSE_IRQ=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EXPERT=y +# CONFIG_RD_GZIP is not set CONFIG_KALLSYMS_ALL=y +# CONFIG_PCSPKR_PLATFORM is not set +CONFIG_EMBEDDED=y CONFIG_SLAB=y # CONFIG_IOSCHED_CFQ is not set +# CONFIG_PPC_PMAC is not set CONFIG_PPC_82xx=y CONFIG_MGCOGE=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_BINFMT_MISC=y # CONFIG_SECCOMP is not set CONFIG_NET=y @@ -24,11 +31,10 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_LRO is not set # CONFIG_IPV6 is not set CONFIG_NETFILTER=y +CONFIG_TIPC=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" # CONFIG_FW_LOADER is not set CONFIG_MTD=y -CONFIG_MTD_CONCAT=y -CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLKDEVS=y @@ -42,7 +48,6 @@ CONFIG_MTD_PHYSMAP_OF=y CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y -# CONFIG_MACINTOSH_DRIVERS is not set CONFIG_NETDEVICES=y CONFIG_FIXED_PHY=y CONFIG_NET_ETHERNET=y @@ -50,6 +55,7 @@ CONFIG_FS_ENET=y CONFIG_FS_ENET_MDIO_FCC=y # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set +# CONFIG_WLAN is not set # CONFIG_INPUT is not set # CONFIG_SERIO is not set # CONFIG_VT is not set @@ -57,24 +63,24 @@ CONFIG_SERIAL_CPM=y CONFIG_SERIAL_CPM_CONSOLE=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=y -# CONFIG_I2C_POWERMAC is not set CONFIG_I2C_CPM=y # CONFIG_HWMON is not set -# CONFIG_USB_SUPPORT is not set +CONFIG_USB_GADGET=y +CONFIG_USB_FSL_USB2=y +CONFIG_USB_G_SERIAL=y +CONFIG_UIO=y +CONFIG_UIO_PDRV=y CONFIG_EXT2_FS=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set -# CONFIG_EXT3_FS_XATTR is not set CONFIG_AUTOFS4_FS=y CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_JFFS2_FS=y CONFIG_CRAMFS=y +CONFIG_SQUASHFS=y CONFIG_NFS_FS=y CONFIG_NFS_V3=y CONFIG_ROOT_NFS=y CONFIG_PARTITION_ADVANCED=y -# CONFIG_MAC_PARTITION is not set CONFIG_NLS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y @@ -82,7 +88,6 @@ CONFIG_NLS_ISO8859_1=y CONFIG_NLS_UTF8=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y # CONFIG_SCHED_DEBUG is not set CONFIG_DEBUG_INFO=y CONFIG_SYSCTL_SYSCALL_CHECK=y diff --git a/arch/powerpc/platforms/82xx/km82xx.c b/arch/powerpc/platforms/82xx/km82xx.c index 428c5e0a0e7..3661bcdc326 100644 --- a/arch/powerpc/platforms/82xx/km82xx.c +++ b/arch/powerpc/platforms/82xx/km82xx.c @@ -49,6 +49,9 @@ struct cpm_pin { }; static __initdata struct cpm_pin km82xx_pins[] = { + /* SMC1 */ + {2, 4, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 5, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, /* SMC2 */ {0, 8, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, @@ -137,6 +140,7 @@ static void __init init_ioports(void) } cpm2_smc_clk_setup(CPM_CLK_SMC2, CPM_BRG8); + cpm2_smc_clk_setup(CPM_CLK_SMC1, CPM_BRG7); cpm2_clk_setup(CPM_CLK_SCC1, CPM_CLK11, CPM_CLK_RX); cpm2_clk_setup(CPM_CLK_SCC1, CPM_CLK11, CPM_CLK_TX); cpm2_clk_setup(CPM_CLK_SCC3, CPM_CLK5, CPM_CLK_RTX); -- cgit v1.2.3-18-g5258 From 2228511833e267e833514cc85ba9626f9dacd661 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 13 Sep 2011 16:17:00 -0500 Subject: powerpc/fsl_msi: fix support for multiple MSI ranges Commit 6820fead ("powerpc/fsl_msi: Handle msi-available-ranges better") added support for multiple ranges in the msi-available-ranges property, but it miscalculated the MSIR index when multiple ranges are used. Signed-off-by: Timur Tabi Acked-by: Scott Wood Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index 419a77239bd..1cca25146b1 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -296,7 +296,7 @@ static int __devinit fsl_msi_setup_hwirq(struct fsl_msi *msi, } msi->msi_virqs[irq_index] = virt_msir; - cascade_data->index = offset + irq_index; + cascade_data->index = offset; cascade_data->msi_data = msi; irq_set_handler_data(virt_msir, cascade_data); irq_set_chained_handler(virt_msir, fsl_msi_cascade); @@ -376,8 +376,10 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) goto error_out; } - if (!p) + if (!p) { p = all_avail; + len = sizeof(all_avail); + } for (irq_index = 0, i = 0; i < len / (2 * sizeof(u32)); i++) { if (p[i * 2] % IRQS_PER_MSI_REG || @@ -393,7 +395,7 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) count = p[i * 2 + 1] / IRQS_PER_MSI_REG; for (j = 0; j < count; j++, irq_index++) { - err = fsl_msi_setup_hwirq(msi, dev, offset, irq_index); + err = fsl_msi_setup_hwirq(msi, dev, offset + j, irq_index); if (err) goto error_out; } -- cgit v1.2.3-18-g5258 From e33ee8b6f473b33a41c281f5329d9d7b3bed8ff5 Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Tue, 11 Oct 2011 11:26:08 +0530 Subject: powerpc: e500mc: Fix: use CONFIG_PPC_E500MC in idle_e500.S It is wrongly using undefined CONFIG_E500MC. Signed-off-by: Bharat Bhushan Signed-off-by: Kumar Gala --- arch/powerpc/kernel/idle_e500.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/idle_e500.S b/arch/powerpc/kernel/idle_e500.S index 3e2b95c6ae6..4f0ab85f378 100644 --- a/arch/powerpc/kernel/idle_e500.S +++ b/arch/powerpc/kernel/idle_e500.S @@ -26,7 +26,7 @@ _GLOBAL(e500_idle) ori r4,r4,_TLF_NAPPING /* so when we take an exception */ stw r4,TI_LOCAL_FLAGS(r3) /* it will return to our caller */ -#ifdef CONFIG_E500MC +#ifdef CONFIG_PPC_E500MC wrteei 1 1: wait -- cgit v1.2.3-18-g5258 From de423ff5b0b50746616dfacbd67a2290ed70b590 Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Tue, 11 Oct 2011 19:06:42 -0500 Subject: powerpc/85xx: Fix support for enabling doorbells for IPIs Commit 765342526246c97600e5344c0949824d94bb51c3 made some small changes to IPI, message_pass in smp_ops was initialized to NULL for other platforms but not for 85xx which causes us to always use the mpic for IPI's even if we support doorbells in HW. Signed-off-by: Matthew McClintock Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/smp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index 5b9b901f644..d6e47464a0d 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -243,6 +243,7 @@ void __init mpc85xx_smp_init(void) * If left NULL, .message_pass defaults to * smp_muxed_ipi_message_pass */ + smp_85xx_ops.message_pass = NULL; smp_85xx_ops.cause_ipi = doorbell_cause_ipi; } -- cgit v1.2.3-18-g5258 From 7c4b2f099f9b9c3580a3b606aff1fe13218fce48 Mon Sep 17 00:00:00 2001 From: Becky Bruce Date: Mon, 10 Oct 2011 15:50:45 -0500 Subject: powerpc: Update mpc85xx/corenet 32-bit defconfigs Results from updates via make savedefconfig. Signed-off-by: Becky Bruce Signed-off-by: Kumar Gala --- arch/powerpc/configs/corenet32_smp_defconfig | 8 -------- arch/powerpc/configs/mpc85xx_defconfig | 5 +---- arch/powerpc/configs/mpc85xx_smp_defconfig | 6 +----- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig index 49cfe85232f..797e1fd1a5b 100644 --- a/arch/powerpc/configs/corenet32_smp_defconfig +++ b/arch/powerpc/configs/corenet32_smp_defconfig @@ -12,9 +12,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_KALLSYMS_ALL=y -CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y CONFIG_SLAB=y @@ -69,7 +67,6 @@ CONFIG_IPV6=y CONFIG_IP_SCTP=m CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_MTD=y -CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y @@ -107,7 +104,6 @@ CONFIG_FSL_PQ_MDIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_SERIO_LIBPS2=y # CONFIG_LEGACY_PTYS is not set -CONFIG_PPC_EPAPR_HV_BYTECHAN=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_EXTENDED=y @@ -136,8 +132,6 @@ CONFIG_USB_OHCI_HCD_PPC_OF_LE=y CONFIG_USB_STORAGE=y CONFIG_MMC=y CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_OF=y -CONFIG_MMC_SDHCI_OF_ESDHC=y CONFIG_EDAC=y CONFIG_EDAC_MM_EDAC=y CONFIG_EDAC_MPC85XX=y @@ -146,7 +140,6 @@ CONFIG_RTC_DRV_DS3232=y CONFIG_RTC_DRV_CMOS=y CONFIG_UIO=y CONFIG_STAGING=y -# CONFIG_STAGING_EXCLUDE_BUILD is not set CONFIG_VIRT_DRIVERS=y CONFIG_FSL_HV_MANAGER=y CONFIG_EXT2_FS=y @@ -173,7 +166,6 @@ CONFIG_MAC_PARTITION=y CONFIG_NLS_ISO8859_1=y CONFIG_NLS_UTF8=m CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_SHIRQ=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_INFO=y diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index 25009120745..a1e5a178a4a 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -10,10 +10,8 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y -CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y @@ -41,7 +39,6 @@ CONFIG_TQM8560=y CONFIG_SBC8548=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y -CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y @@ -123,6 +120,7 @@ CONFIG_NVRAM=y CONFIG_I2C=y CONFIG_I2C_CPM=m CONFIG_I2C_MPC=y +CONFIG_GPIO_MPC8XXX=y # CONFIG_HWMON is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y @@ -206,7 +204,6 @@ CONFIG_PARTITION_ADVANCED=y CONFIG_MAC_PARTITION=y CONFIG_CRC_T10DIF=y CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_INFO=y CONFIG_SYSCTL_SYSCALL_CHECK=y diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index a4ba13b0ef1..dd1e41386c4 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -12,10 +12,8 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y -CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y @@ -42,7 +40,6 @@ CONFIG_TQM8560=y CONFIG_SBC8548=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y -CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y @@ -124,6 +121,7 @@ CONFIG_NVRAM=y CONFIG_I2C=y CONFIG_I2C_CPM=m CONFIG_I2C_MPC=y +CONFIG_GPIO_MPC8XXX=y # CONFIG_HWMON is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y @@ -207,10 +205,8 @@ CONFIG_PARTITION_ADVANCED=y CONFIG_MAC_PARTITION=y CONFIG_CRC_T10DIF=y CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_INFO=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_VIRQ_DEBUG=y CONFIG_CRYPTO_PCBC=m -- cgit v1.2.3-18-g5258 From 5c4a5b8de65d08a436d112307204542a4d559ee2 Mon Sep 17 00:00:00 2001 From: Becky Bruce Date: Mon, 10 Oct 2011 15:50:47 -0500 Subject: powerpc: Update corenet64_smp_defconfig Updates from make savedefconfig. Signed-off-by: Becky Bruce Signed-off-by: Kumar Gala --- arch/powerpc/configs/corenet64_smp_defconfig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/powerpc/configs/corenet64_smp_defconfig b/arch/powerpc/configs/corenet64_smp_defconfig index c92c204a204..782822c32d1 100644 --- a/arch/powerpc/configs/corenet64_smp_defconfig +++ b/arch/powerpc/configs/corenet64_smp_defconfig @@ -11,10 +11,8 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y -CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y @@ -25,7 +23,6 @@ CONFIG_P5020_DS=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_BINFMT_MISC=m -# CONFIG_PCI is not set CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @@ -93,10 +90,8 @@ CONFIG_CRC_T10DIF=y CONFIG_CRC_ITU_T=m CONFIG_FRAME_WARN=1024 CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_INFO=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_VIRQ_DEBUG=y CONFIG_CRYPTO_PCBC=m -- cgit v1.2.3-18-g5258 From ba14f6491768acad5cf50a3c7dc8927b7614d692 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 16 Sep 2011 10:39:58 -0500 Subject: powerpc: respect mem= setting for early memory limit setup For those MMUs that have some form of bolt'd linear mapping (TLB) required its rare that one ever sets mem= smaller than the size of that mapping. However, on Book-E 64 parts the initial linear mapping is quite large (1G) so its quite reasonable that mem= is set smaller than that. We need to parse the command line for mem= limit and constrain the amount of memory we map initially by it if need be. Signed-off-by: Kumar Gala --- arch/powerpc/kernel/prom.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 831a201e03d..8ad825c063c 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -737,12 +737,15 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_root, NULL); of_scan_flat_dt(early_init_dt_scan_memory_ppc, NULL); - setup_initial_memory_limit(memstart_addr, first_memblock_size); /* Save command line for /proc/cmdline and then parse parameters */ strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); parse_early_param(); + /* make sure we've parsed cmdline for mem= before this */ + if (memory_limit) + first_memblock_size = min(first_memblock_size, memory_limit); + setup_initial_memory_limit(memstart_addr, first_memblock_size); /* Reserve MEMBLOCK regions used by kernel, initrd, dt, etc... */ memblock_reserve(PHYSICAL_START, __pa(klimit) - PHYSICAL_START); /* If relocatable, reserve first 32k for interrupt vectors etc. */ -- cgit v1.2.3-18-g5258 From 1dc91c3eb374ca01ec99dc0ca2a38babc509beb3 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 16 Sep 2011 10:39:59 -0500 Subject: powerpc/fsl-booke: Fix setup_initial_memory_limit to not blindly map On FSL Book-E devices we support multiple large TLB sizes and so we can get into situations in which the initial 1G TLB size is too big and we're asked for a size that is not mappable by a single entry (like 512M). The single entry is important because when we bring up secondary cores they need to ensure any data structure they need to access (eg PACA or stack) is always mapped. So we really need to determine what size will actually be mapped by the first TLB entry to ensure we limit early memory references to that region. We refactor the map_mem_in_cams() code to provider a helper function that we can utilize to determine the size of the first TLB entry while taking into account size and alignment constraints. Signed-off-by: Kumar Gala --- arch/powerpc/mm/fsl_booke_mmu.c | 31 +++++++++++++++++++------------ arch/powerpc/mm/mmu_decl.h | 2 ++ arch/powerpc/mm/tlb_nohash.c | 21 ++++++++++++++++++--- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/mm/fsl_booke_mmu.c b/arch/powerpc/mm/fsl_booke_mmu.c index f7802c8bba0..6f593bd2717 100644 --- a/arch/powerpc/mm/fsl_booke_mmu.c +++ b/arch/powerpc/mm/fsl_booke_mmu.c @@ -146,29 +146,36 @@ static void settlbcam(int index, unsigned long virt, phys_addr_t phys, loadcam_entry(index); } +unsigned long calc_cam_sz(unsigned long ram, unsigned long virt, + phys_addr_t phys) +{ + unsigned int camsize = __ilog2(ram) & ~1U; + unsigned int align = __ffs(virt | phys) & ~1U; + unsigned long max_cam = (mfspr(SPRN_TLB1CFG) >> 16) & 0xf; + + /* Convert (4^max) kB to (2^max) bytes */ + max_cam = max_cam * 2 + 10; + + if (camsize > align) + camsize = align; + if (camsize > max_cam) + camsize = max_cam; + + return 1UL << camsize; +} + unsigned long map_mem_in_cams(unsigned long ram, int max_cam_idx) { int i; unsigned long virt = PAGE_OFFSET; phys_addr_t phys = memstart_addr; unsigned long amount_mapped = 0; - unsigned long max_cam = (mfspr(SPRN_TLB1CFG) >> 16) & 0xf; - - /* Convert (4^max) kB to (2^max) bytes */ - max_cam = max_cam * 2 + 10; /* Calculate CAM values */ for (i = 0; ram && i < max_cam_idx; i++) { - unsigned int camsize = __ilog2(ram) & ~1U; - unsigned int align = __ffs(virt | phys) & ~1U; unsigned long cam_sz; - if (camsize > align) - camsize = align; - if (camsize > max_cam) - camsize = max_cam; - - cam_sz = 1UL << camsize; + cam_sz = calc_cam_sz(ram, virt, phys); settlbcam(i, virt, phys, cam_sz, PAGE_KERNEL_X, 0); ram -= cam_sz; diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h index dd0a2589591..83eb5d5f53d 100644 --- a/arch/powerpc/mm/mmu_decl.h +++ b/arch/powerpc/mm/mmu_decl.h @@ -142,6 +142,8 @@ extern unsigned long mmu_mapin_ram(unsigned long top); #elif defined(CONFIG_PPC_FSL_BOOK3E) extern unsigned long map_mem_in_cams(unsigned long ram, int max_cam_idx); +extern unsigned long calc_cam_sz(unsigned long ram, unsigned long virt, + phys_addr_t phys); #ifdef CONFIG_PPC32 extern void MMU_init_hw(void); extern unsigned long mmu_mapin_ram(unsigned long top); diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c index afc95c7304a..6c2eabf707b 100644 --- a/arch/powerpc/mm/tlb_nohash.c +++ b/arch/powerpc/mm/tlb_nohash.c @@ -642,13 +642,28 @@ void __cpuinit early_init_mmu_secondary(void) void setup_initial_memory_limit(phys_addr_t first_memblock_base, phys_addr_t first_memblock_size) { - /* On Embedded 64-bit, we adjust the RMA size to match + /* On non-FSL Embedded 64-bit, we adjust the RMA size to match * the bolted TLB entry. We know for now that only 1G * entries are supported though that may eventually - * change. We crop it to the size of the first MEMBLOCK to + * change. + * + * on FSL Embedded 64-bit, we adjust the RMA size to match the + * first bolted TLB entry size. We still limit max to 1G even if + * the TLB could cover more. This is due to what the early init + * code is setup to do. + * + * We crop it to the size of the first MEMBLOCK to * avoid going over total available memory just in case... */ - ppc64_rma_size = min_t(u64, first_memblock_size, 0x40000000); +#ifdef CONFIG_PPC_FSL_BOOK3E + if (mmu_has_feature(MMU_FTR_TYPE_FSL_E)) { + unsigned long linear_sz; + linear_sz = calc_cam_sz(first_memblock_size, PAGE_OFFSET, + first_memblock_base); + ppc64_rma_size = min_t(u64, linear_sz, 0x40000000); + } else +#endif + ppc64_rma_size = min_t(u64, first_memblock_size, 0x40000000); /* Finally limit subsequent allocations */ memblock_set_current_limit(first_memblock_base + ppc64_rma_size); -- cgit v1.2.3-18-g5258 From 878e3cb5f7763f388a1fa7d2a33b66601f75b43e Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 23 Sep 2011 21:32:50 +0200 Subject: powerpc/85xx: fix PHYS_64BIT selection for P1022DS Remove wrong CONFIG_ prefix in Kconfig file. Signed-off-by: Anatolij Gustschin Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 1e66edb95d2..7f84cfd1726 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -80,7 +80,7 @@ config P1010_RDB config P1022_DS bool "Freescale P1022 DS" select DEFAULT_UIMAGE - select CONFIG_PHYS_64BIT # The DTS has 36-bit addresses + select PHYS_64BIT # The DTS has 36-bit addresses select SWIOTLB help This option enables support for the Freescale P1022DS reference board. -- cgit v1.2.3-18-g5258 From 499ccb27a89ecd08475f73710fe27fb600431a91 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 15 Sep 2011 13:04:13 -0500 Subject: powerpc/85xx: clean up FPGA device tree nodes for Freecsale QorIQ boards Standarize and document the FPGA nodes used on Freescale QorIQ reference boards. There are different kinds of FPGAs used on the boards, but only two are currently standard: "pixis", "ngpixis", and "qixis". Although there are minor differences among the boards that have one kind of FPGA, most of the functionality is the same, so it makes sense to create common compatibility strings. We also need to update the P1022DS platform file, because the compatible string for its PIXIS node has changed. This means that older kernels are not compatible with newer device trees. This is not a real problem, however, since that particular function doesn't work anyway. When the DIU is active, the PIXIS is in "indirect mode", and so cannot be accessed as a memory-mapped device. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- .../devicetree/bindings/powerpc/fsl/board.txt | 30 +++++++++++++--------- arch/powerpc/boot/dts/p1022ds.dts | 2 +- arch/powerpc/boot/dts/p2020ds.dts | 5 ++++ arch/powerpc/boot/dts/p3041ds.dts | 4 +-- arch/powerpc/boot/dts/p4080ds.dts | 8 +++++- arch/powerpc/boot/dts/p5020ds.dts | 4 +-- arch/powerpc/platforms/85xx/p1022_ds.c | 11 +++++--- 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/Documentation/devicetree/bindings/powerpc/fsl/board.txt b/Documentation/devicetree/bindings/powerpc/fsl/board.txt index 39e941515a3..380914e965e 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/board.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/board.txt @@ -1,3 +1,8 @@ +Freescale Reference Board Bindings + +This document describes device tree bindings for various devices that +exist on some Freescale reference boards. + * Board Control and Status (BCSR) Required properties: @@ -12,25 +17,26 @@ Example: reg = ; }; -* Freescale on board FPGA +* Freescale on-board FPGA This is the memory-mapped registers for on board FPGA. Required properities: -- compatible : should be "fsl,fpga-pixis". -- reg : should contain the address and the length of the FPPGA register - set. +- compatible: should be a board-specific string followed by a string + indicating the type of FPGA. Example: + "fsl,-fpga", "fsl,fpga-pixis" +- reg: should contain the address and the length of the FPGA register set. - interrupt-parent: should specify phandle for the interrupt controller. -- interrupts : should specify event (wakeup) IRQ. +- interrupts: should specify event (wakeup) IRQ. -Example (MPC8610HPCD): +Example (P1022DS): - board-control@e8000000 { - compatible = "fsl,fpga-pixis"; - reg = <0xe8000000 32>; - interrupt-parent = <&mpic>; - interrupts = <8 8>; - }; + board-control@3,0 { + compatible = "fsl,p1022ds-fpga", "fsl,fpga-ngpixis"; + reg = <3 0 0x30>; + interrupt-parent = <&mpic>; + interrupts = <8 8 0 0>; + }; * Freescale BCSR GPIO banks diff --git a/arch/powerpc/boot/dts/p1022ds.dts b/arch/powerpc/boot/dts/p1022ds.dts index 1be9743ab5e..b9b8719a620 100644 --- a/arch/powerpc/boot/dts/p1022ds.dts +++ b/arch/powerpc/boot/dts/p1022ds.dts @@ -150,7 +150,7 @@ }; board-control@3,0 { - compatible = "fsl,p1022ds-pixis"; + compatible = "fsl,p1022ds-fpga", "fsl,fpga-ngpixis"; reg = <3 0 0x30>; interrupt-parent = <&mpic>; /* diff --git a/arch/powerpc/boot/dts/p2020ds.dts b/arch/powerpc/boot/dts/p2020ds.dts index dae403100f2..66f03d6477b 100644 --- a/arch/powerpc/boot/dts/p2020ds.dts +++ b/arch/powerpc/boot/dts/p2020ds.dts @@ -118,6 +118,11 @@ }; }; + board-control@3,0 { + compatible = "fsl,p2020ds-fpga", "fsl,fpga-ngpixis"; + reg = <0x3 0x0 0x30>; + }; + nand@4,0 { compatible = "fsl,elbc-fcm-nand"; reg = <0x4 0x0 0x40000>; diff --git a/arch/powerpc/boot/dts/p3041ds.dts b/arch/powerpc/boot/dts/p3041ds.dts index 69cae674f39..961a74cd1e9 100644 --- a/arch/powerpc/boot/dts/p3041ds.dts +++ b/arch/powerpc/boot/dts/p3041ds.dts @@ -147,8 +147,8 @@ }; board-control@3,0 { - compatible = "fsl,p3041ds-pixis"; - reg = <3 0 0x20>; + compatible = "fsl,p3041ds-fpga", "fsl,fpga-ngpixis"; + reg = <3 0 0x30>; }; }; diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts index eb11098bb68..ccd53aff0e9 100644 --- a/arch/powerpc/boot/dts/p4080ds.dts +++ b/arch/powerpc/boot/dts/p4080ds.dts @@ -108,7 +108,8 @@ localbus@ffe124000 { reg = <0xf 0xfe124000 0 0x1000>; - ranges = <0 0 0xf 0xe8000000 0x08000000>; + ranges = <0 0 0xf 0xe8000000 0x08000000 + 3 0 0xf 0xffdf0000 0x00008000>; flash@0,0 { compatible = "cfi-flash"; @@ -116,6 +117,11 @@ bank-width = <2>; device-width = <2>; }; + + board-control@3,0 { + compatible = "fsl,p4080ds-fpga", "fsl,fpga-ngpixis"; + reg = <3 0 0x30>; + }; }; pci0: pcie@ffe200000 { diff --git a/arch/powerpc/boot/dts/p5020ds.dts b/arch/powerpc/boot/dts/p5020ds.dts index 8366e2fd2fb..ca4d592c91d 100644 --- a/arch/powerpc/boot/dts/p5020ds.dts +++ b/arch/powerpc/boot/dts/p5020ds.dts @@ -147,8 +147,8 @@ }; board-control@3,0 { - compatible = "fsl,p5020ds-pixis"; - reg = <3 0 0x20>; + compatible = "fsl,p5020ds-fpga", "fsl,fpga-ngpixis"; + reg = <3 0 0x30>; }; }; diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index 266b3aadfe5..6a0b9731b25 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -128,17 +128,20 @@ static void p1022ds_set_gamma_table(int monitor_port, char *gamma_table_base) */ static void p1022ds_set_monitor_port(int monitor_port) { - struct device_node *pixis_node; + struct device_node *np; void __iomem *pixis; u8 __iomem *brdcfg1; - pixis_node = of_find_compatible_node(NULL, NULL, "fsl,p1022ds-pixis"); - if (!pixis_node) { + np = of_find_compatible_node(NULL, NULL, "fsl,p1022ds-fpga"); + if (!np) + /* older device trees used "fsl,p1022ds-pixis" */ + np = of_find_compatible_node(NULL, NULL, "fsl,p1022ds-pixis"); + if (!np) { pr_err("p1022ds: missing ngPIXIS node\n"); return; } - pixis = of_iomap(pixis_node, 0); + pixis = of_iomap(np, 0); if (!pixis) { pr_err("p1022ds: could not map ngPIXIS registers\n"); return; -- cgit v1.2.3-18-g5258 From b9df02231930c01eaaf3c37b192bd75ea0d1c0bb Mon Sep 17 00:00:00 2001 From: Stephen George Date: Fri, 16 Sep 2011 10:36:34 -0500 Subject: powerpc/85xx: Adding DCSR node to dtsi device trees Adding new device tree binding file for the DCSR node. Modifying device tree dtsi files to add DCSR node for P2041, P3041, P4080, & P5020. Signed-off-by: Stephen George Signed-off-by: Kumar Gala --- .../devicetree/bindings/powerpc/fsl/dcsr.txt | 395 +++++++++++++++++++++ arch/powerpc/boot/dts/p2041rdb.dts | 4 + arch/powerpc/boot/dts/p2041si.dtsi | 71 +++- arch/powerpc/boot/dts/p3041ds.dts | 4 + arch/powerpc/boot/dts/p3041si.dtsi | 71 +++- arch/powerpc/boot/dts/p4080ds.dts | 4 + arch/powerpc/boot/dts/p4080si.dtsi | 98 ++++- arch/powerpc/boot/dts/p5020ds.dts | 4 + arch/powerpc/boot/dts/p5020si.dtsi | 68 +++- 9 files changed, 713 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/powerpc/fsl/dcsr.txt diff --git a/Documentation/devicetree/bindings/powerpc/fsl/dcsr.txt b/Documentation/devicetree/bindings/powerpc/fsl/dcsr.txt new file mode 100644 index 00000000000..9d54eb5a295 --- /dev/null +++ b/Documentation/devicetree/bindings/powerpc/fsl/dcsr.txt @@ -0,0 +1,395 @@ +=================================================================== +Debug Control and Status Register (DCSR) Binding +Copyright 2011 Freescale Semiconductor Inc. + +NOTE: The bindings described in this document are preliminary and subject +to change. Some of the compatible strings that contain only generic names +may turn out to be inappropriate, or need additional properties to describe +the integration of the block with the rest of the chip. + +===================================================================== +Debug Control and Status Register Memory Map + +Description + +This node defines the base address and range for the +defined DCSR Memory Map. Child nodes will describe the individual +debug blocks defined within this memory space. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,dcsr" and "simple-bus". + The DCSR space exists in the memory-mapped bus. + + - #address-cells + Usage: required + Value type: + Definition: A standard property. Defines the number of cells + or representing physical addresses in child nodes. + + - #size-cells + Usage: required + Value type: + Definition: A standard property. Defines the number of cells + or representing the size of physical addresses in + child nodes. + + - ranges + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + range of the DCSR space. + +EXAMPLE + dcsr: dcsr@f00000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,dcsr", "simple-bus"; + ranges = <0x00000000 0xf 0x00000000 0x01008000>; + }; + +===================================================================== +Event Processing Unit + +This node represents the region of DCSR space allocated to the EPU + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,dcsr-epu" + + - interrupts + Usage: required + Value type: + Definition: Specifies the interrupts generated by the EPU. + The value of the interrupts property consists of three + interrupt specifiers. The format of the specifier is defined + by the binding document describing the node's interrupt parent. + + The EPU counters can be configured to assert the performance + monitor interrupt signal based on either counter overflow or value + match. Which counter asserted the interrupt is captured in an EPU + Counter Interrupt Status Register (EPCPUISR). + + The EPU unit can also be configured to assert either or both of + two interrupt signals based on debug event sources within the SoC. + The interrupt signals are epu_xt_int0 and epu_xt_int1. + Which event source asserted the interrupt is captured in an EPU + Interrupt Status Register (EPISR0,EPISR1). + + Interrupt numbers are lised in order (perfmon, event0, event1). + + - interrupt-parent + Usage: required + Value type: + Definition: A single value that points + to the interrupt parent to which the child domain + is being mapped. Value must be "&mpic" + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + +EXAMPLE + dcsr-epu@0 { + compatible = "fsl,dcsr-epu"; + interrupts = <52 2 0 0 + 84 2 0 0 + 85 2 0 0>; + interrupt-parent = <&mpic>; + reg = <0x0 0x1000>; + }; + +======================================================================= +Nexus Port Controller + +This node represents the region of DCSR space allocated to the NPC + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,dcsr-npc" + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + The Nexus Port controller occupies two regions in the DCSR space + with distinct functionality. + + The first register range describes the Nexus Port Controller + control and status registers. + + The second register range describes the Nexus Port Controller + internal trace buffer. The NPC trace buffer is a small memory buffer + which stages the nexus trace data for transmission via the Aurora port + or to a DDR based trace buffer. In some configurations the NPC trace + buffer can be the only trace buffer used. + + +EXAMPLE + dcsr-npc { + compatible = "fsl,dcsr-npc"; + reg = <0x1000 0x1000 0x1000000 0x8000>; + }; + +======================================================================= +Nexus Concentrator + +This node represents the region of DCSR space allocated to the NXC + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,dcsr-nxc" + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + +EXAMPLE + dcsr-nxc@2000 { + compatible = "fsl,dcsr-nxc"; + reg = <0x2000 0x1000>; + }; +======================================================================= +CoreNet Debug Controller + +This node represents the region of DCSR space allocated to +the CoreNet Debug controller. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,dcsr-corenet" + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + The CoreNet Debug controller occupies two regions in the DCSR space + with distinct functionality. + + The first register range describes the CoreNet Debug Controller + functionalty to perform transaction and transaction attribute matches. + + The second register range describes the CoreNet Debug Controller + functionalty to trigger event notifications and debug traces. + +EXAMPLE + dcsr-corenet { + compatible = "fsl,dcsr-corenet"; + reg = <0x8000 0x1000 0xB0000 0x1000>; + }; + +======================================================================= +Data Path Debug controller + +This node represents the region of DCSR space allocated to +the DPAA Debug Controller. This controller controls debug configuration +for the QMAN and FMAN blocks. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include both an identifier specific to the SoC + or Debug IP of the form "fsl,-dcsr-dpaa" in addition to the + generic compatible string "fsl,dcsr-dpaa". + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + +EXAMPLE + dcsr-dpaa@9000 { + compatible = "fsl,p4080-dcsr-dpaa", "fsl,dcsr-dpaa"; + reg = <0x9000 0x1000>; + }; + +======================================================================= +OCeaN Debug controller + +This node represents the region of DCSR space allocated to +the OCN Debug Controller. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include both an identifier specific to the SoC + or Debug IP of the form "fsl,-dcsr-ocn" in addition to the + generic compatible string "fsl,dcsr-ocn". + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + +EXAMPLE + dcsr-ocn@11000 { + compatible = "fsl,p4080-dcsr-ocn", "fsl,dcsr-ocn"; + reg = <0x11000 0x1000>; + }; + +======================================================================= +DDR Controller Debug controller + +This node represents the region of DCSR space allocated to +the OCN Debug Controller. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,dcsr-ddr" + + - dev-handle + Usage: required + Definition: A phandle to associate this debug node with its + component controller. + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + +EXAMPLE + dcsr-ddr@12000 { + compatible = "fsl,dcsr-ddr"; + dev-handle = <&ddr1>; + reg = <0x12000 0x1000>; + }; + +======================================================================= +Nexus Aurora Link Controller + +This node represents the region of DCSR space allocated to +the NAL Controller. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include both an identifier specific to the SoC + or Debug IP of the form "fsl,-dcsr-nal" in addition to the + generic compatible string "fsl,dcsr-nal". + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + +EXAMPLE + dcsr-nal@18000 { + compatible = "fsl,p4080-dcsr-nal", "fsl,dcsr-nal"; + reg = <0x18000 0x1000>; + }; + + +======================================================================= +Run Control and Power Management + +This node represents the region of DCSR space allocated to +the RCPM Debug Controller. This functionlity is limited to the +control the debug operations of the SoC and cores. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include both an identifier specific to the SoC + or Debug IP of the form "fsl,-dcsr-rcpm" in addition to the + generic compatible string "fsl,dcsr-rcpm". + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + +EXAMPLE + dcsr-rcpm@22000 { + compatible = "fsl,p4080-dcsr-rcpm", "fsl,dcsr-rcpm"; + reg = <0x22000 0x1000>; + }; + +======================================================================= +Core Service Bridge Proxy + +This node represents the region of DCSR space allocated to +the Core Service Bridge Proxies. +There is one Core Service Bridge Proxy device for each CPU in the system. +This functionlity provides access to the debug operations of the CPU. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include both an identifier specific to the cpu + of the form "fsl,dcsr--sb-proxy" in addition to the + generic compatible string "fsl,dcsr-cpu-sb-proxy". + + - cpu-handle + Usage: required + Definition: A phandle to associate this debug node with its cpu. + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + offset and length of the DCSR space registers of the device + configuration block. + +EXAMPLE + dcsr-cpu-sb-proxy@40000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", + "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu0>; + reg = <0x40000 0x1000>; + }; + dcsr-cpu-sb-proxy@41000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", + "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu1>; + reg = <0x41000 0x1000>; + }; + +======================================================================= diff --git a/arch/powerpc/boot/dts/p2041rdb.dts b/arch/powerpc/boot/dts/p2041rdb.dts index 47bb461cf72..79b6895027c 100644 --- a/arch/powerpc/boot/dts/p2041rdb.dts +++ b/arch/powerpc/boot/dts/p2041rdb.dts @@ -45,6 +45,10 @@ device_type = "memory"; }; + dcsr: dcsr@f00000000 { + ranges = <0x00000000 0xf 0x00000000 0x01008000>; + }; + soc: soc@ffe000000 { spi@110000 { flash@0 { diff --git a/arch/powerpc/boot/dts/p2041si.dtsi b/arch/powerpc/boot/dts/p2041si.dtsi index 420cdb0f403..f7492edd0df 100644 --- a/arch/powerpc/boot/dts/p2041si.dtsi +++ b/arch/powerpc/boot/dts/p2041si.dtsi @@ -42,6 +42,7 @@ aliases { ccsr = &soc; + dcsr = &dcsr; serial0 = &serial0; serial1 = &serial1; @@ -109,6 +110,74 @@ }; }; + dcsr: dcsr@f00000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,dcsr", "simple-bus"; + + dcsr-epu@0 { + compatible = "fsl,dcsr-epu"; + interrupts = <52 2 0 0 + 84 2 0 0 + 85 2 0 0>; + interrupt-parent = <&mpic>; + reg = <0x0 0x1000>; + }; + dcsr-npc { + compatible = "fsl,dcsr-npc"; + reg = <0x1000 0x1000 0x1000000 0x8000>; + }; + dcsr-nxc@2000 { + compatible = "fsl,dcsr-nxc"; + reg = <0x2000 0x1000>; + }; + dcsr-corenet { + compatible = "fsl,dcsr-corenet"; + reg = <0x8000 0x1000 0xB0000 0x1000>; + }; + dcsr-dpaa@9000 { + compatible = "fsl,p2041-dcsr-dpaa", "fsl,dcsr-dpaa"; + reg = <0x9000 0x1000>; + }; + dcsr-ocn@11000 { + compatible = "fsl,p2041-dcsr-ocn", "fsl,dcsr-ocn"; + reg = <0x11000 0x1000>; + }; + dcsr-ddr@12000 { + compatible = "fsl,dcsr-ddr"; + dev-handle = <&ddr>; + reg = <0x12000 0x1000>; + }; + dcsr-nal@18000 { + compatible = "fsl,p2041-dcsr-nal", "fsl,dcsr-nal"; + reg = <0x18000 0x1000>; + }; + dcsr-rcpm@22000 { + compatible = "fsl,p2041-dcsr-rcpm", "fsl,dcsr-rcpm"; + reg = <0x22000 0x1000>; + }; + dcsr-cpu-sb-proxy@40000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu0>; + reg = <0x40000 0x1000>; + }; + dcsr-cpu-sb-proxy@41000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu1>; + reg = <0x41000 0x1000>; + }; + dcsr-cpu-sb-proxy@42000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu2>; + reg = <0x42000 0x1000>; + }; + dcsr-cpu-sb-proxy@43000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu3>; + reg = <0x43000 0x1000>; + }; + }; + soc: soc@ffe000000 { #address-cells = <1>; #size-cells = <1>; @@ -128,7 +197,7 @@ fsl,num-laws = <32>; }; - memory-controller@8000 { + ddr: memory-controller@8000 { compatible = "fsl,qoriq-memory-controller-v4.5", "fsl,qoriq-memory-controller"; reg = <0x8000 0x1000>; interrupts = <16 2 1 23>; diff --git a/arch/powerpc/boot/dts/p3041ds.dts b/arch/powerpc/boot/dts/p3041ds.dts index 961a74cd1e9..bbd113b49a8 100644 --- a/arch/powerpc/boot/dts/p3041ds.dts +++ b/arch/powerpc/boot/dts/p3041ds.dts @@ -45,6 +45,10 @@ device_type = "memory"; }; + dcsr: dcsr@f00000000 { + ranges = <0x00000000 0xf 0x00000000 0x01008000>; + }; + soc: soc@ffe000000 { spi@110000 { flash@0 { diff --git a/arch/powerpc/boot/dts/p3041si.dtsi b/arch/powerpc/boot/dts/p3041si.dtsi index 8b695801f50..87130b732bc 100644 --- a/arch/powerpc/boot/dts/p3041si.dtsi +++ b/arch/powerpc/boot/dts/p3041si.dtsi @@ -42,6 +42,7 @@ aliases { ccsr = &soc; + dcsr = &dcsr; serial0 = &serial0; serial1 = &serial1; @@ -114,6 +115,74 @@ }; }; + dcsr: dcsr@f00000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,dcsr", "simple-bus"; + + dcsr-epu@0 { + compatible = "fsl,dcsr-epu"; + interrupts = <52 2 0 0 + 84 2 0 0 + 85 2 0 0>; + interrupt-parent = <&mpic>; + reg = <0x0 0x1000>; + }; + dcsr-npc { + compatible = "fsl,dcsr-npc"; + reg = <0x1000 0x1000 0x1000000 0x8000>; + }; + dcsr-nxc@2000 { + compatible = "fsl,dcsr-nxc"; + reg = <0x2000 0x1000>; + }; + dcsr-corenet { + compatible = "fsl,dcsr-corenet"; + reg = <0x8000 0x1000 0xB0000 0x1000>; + }; + dcsr-dpaa@9000 { + compatible = "fsl,p43041-dcsr-dpaa", "fsl,dcsr-dpaa"; + reg = <0x9000 0x1000>; + }; + dcsr-ocn@11000 { + compatible = "fsl,p43041-dcsr-ocn", "fsl,dcsr-ocn"; + reg = <0x11000 0x1000>; + }; + dcsr-ddr@12000 { + compatible = "fsl,dcsr-ddr"; + dev-handle = <&ddr>; + reg = <0x12000 0x1000>; + }; + dcsr-nal@18000 { + compatible = "fsl,p43041-dcsr-nal", "fsl,dcsr-nal"; + reg = <0x18000 0x1000>; + }; + dcsr-rcpm@22000 { + compatible = "fsl,p43041-dcsr-rcpm", "fsl,dcsr-rcpm"; + reg = <0x22000 0x1000>; + }; + dcsr-cpu-sb-proxy@40000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu0>; + reg = <0x40000 0x1000>; + }; + dcsr-cpu-sb-proxy@41000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu1>; + reg = <0x41000 0x1000>; + }; + dcsr-cpu-sb-proxy@42000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu2>; + reg = <0x42000 0x1000>; + }; + dcsr-cpu-sb-proxy@43000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu3>; + reg = <0x43000 0x1000>; + }; + }; + soc: soc@ffe000000 { #address-cells = <1>; #size-cells = <1>; @@ -133,7 +202,7 @@ fsl,num-laws = <32>; }; - memory-controller@8000 { + ddr: memory-controller@8000 { compatible = "fsl,qoriq-memory-controller-v4.5", "fsl,qoriq-memory-controller"; reg = <0x8000 0x1000>; interrupts = <16 2 1 23>; diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts index ccd53aff0e9..c7916dc2801 100644 --- a/arch/powerpc/boot/dts/p4080ds.dts +++ b/arch/powerpc/boot/dts/p4080ds.dts @@ -45,6 +45,10 @@ device_type = "memory"; }; + dcsr: dcsr@f00000000 { + ranges = <0x00000000 0xf 0x00000000 0x01008000>; + }; + soc: soc@ffe000000 { spi@110000 { flash@0 { diff --git a/arch/powerpc/boot/dts/p4080si.dtsi b/arch/powerpc/boot/dts/p4080si.dtsi index 4984edbb63a..f20c01ab247 100644 --- a/arch/powerpc/boot/dts/p4080si.dtsi +++ b/arch/powerpc/boot/dts/p4080si.dtsi @@ -42,6 +42,7 @@ aliases { ccsr = &soc; + dcsr = &dcsr; serial0 = &serial0; serial1 = &serial1; @@ -143,6 +144,99 @@ }; }; + dcsr: dcsr@f00000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,dcsr", "simple-bus"; + + dcsr-epu@0 { + compatible = "fsl,dcsr-epu"; + interrupts = <52 2 0 0 + 84 2 0 0 + 85 2 0 0>; + interrupt-parent = <&mpic>; + reg = <0x0 0x1000>; + }; + dcsr-npc { + compatible = "fsl,dcsr-npc"; + reg = <0x1000 0x1000 0x1000000 0x8000>; + }; + dcsr-nxc@2000 { + compatible = "fsl,dcsr-nxc"; + reg = <0x2000 0x1000>; + }; + dcsr-corenet { + compatible = "fsl,dcsr-corenet"; + reg = <0x8000 0x1000 0xB0000 0x1000>; + }; + dcsr-dpaa@9000 { + compatible = "fsl,p4080-dcsr-dpaa", "fsl,dcsr-dpaa"; + reg = <0x9000 0x1000>; + }; + dcsr-ocn@11000 { + compatible = "fsl,p4080-dcsr-ocn", "fsl,dcsr-ocn"; + reg = <0x11000 0x1000>; + }; + dcsr-ddr@12000 { + compatible = "fsl,dcsr-ddr"; + dev-handle = <&ddr1>; + reg = <0x12000 0x1000>; + }; + dcsr-ddr@13000 { + compatible = "fsl,dcsr-ddr"; + dev-handle = <&ddr2>; + reg = <0x13000 0x1000>; + }; + dcsr-nal@18000 { + compatible = "fsl,p4080-dcsr-nal", "fsl,dcsr-nal"; + reg = <0x18000 0x1000>; + }; + dcsr-rcpm@22000 { + compatible = "fsl,p4080-dcsr-rcpm", "fsl,dcsr-rcpm"; + reg = <0x22000 0x1000>; + }; + dcsr-cpu-sb-proxy@40000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu0>; + reg = <0x40000 0x1000>; + }; + dcsr-cpu-sb-proxy@41000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu1>; + reg = <0x41000 0x1000>; + }; + dcsr-cpu-sb-proxy@42000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu2>; + reg = <0x42000 0x1000>; + }; + dcsr-cpu-sb-proxy@43000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu3>; + reg = <0x43000 0x1000>; + }; + dcsr-cpu-sb-proxy@44000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu4>; + reg = <0x44000 0x1000>; + }; + dcsr-cpu-sb-proxy@45000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu5>; + reg = <0x45000 0x1000>; + }; + dcsr-cpu-sb-proxy@46000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu6>; + reg = <0x46000 0x1000>; + }; + dcsr-cpu-sb-proxy@47000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu7>; + reg = <0x47000 0x1000>; + }; + }; + soc: soc@ffe000000 { #address-cells = <1>; #size-cells = <1>; @@ -162,13 +256,13 @@ fsl,num-laws = <32>; }; - memory-controller@8000 { + ddr1: memory-controller@8000 { compatible = "fsl,qoriq-memory-controller-v4.4", "fsl,qoriq-memory-controller"; reg = <0x8000 0x1000>; interrupts = <16 2 1 23>; }; - memory-controller@9000 { + ddr2: memory-controller@9000 { compatible = "fsl,qoriq-memory-controller-v4.4","fsl,qoriq-memory-controller"; reg = <0x9000 0x1000>; interrupts = <16 2 1 22>; diff --git a/arch/powerpc/boot/dts/p5020ds.dts b/arch/powerpc/boot/dts/p5020ds.dts index ca4d592c91d..e6d40999ccd 100644 --- a/arch/powerpc/boot/dts/p5020ds.dts +++ b/arch/powerpc/boot/dts/p5020ds.dts @@ -45,6 +45,10 @@ device_type = "memory"; }; + dcsr: dcsr@f00000000 { + ranges = <0x00000000 0xf 0x00000000 0x01008000>; + }; + soc: soc@ffe000000 { spi@110000 { flash@0 { diff --git a/arch/powerpc/boot/dts/p5020si.dtsi b/arch/powerpc/boot/dts/p5020si.dtsi index 5e6048ec55b..e7948ad71fa 100644 --- a/arch/powerpc/boot/dts/p5020si.dtsi +++ b/arch/powerpc/boot/dts/p5020si.dtsi @@ -42,6 +42,7 @@ aliases { ccsr = &soc; + dcsr = &dcsr; serial0 = &serial0; serial1 = &serial1; @@ -98,6 +99,69 @@ }; }; + dcsr: dcsr@f00000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,dcsr", "simple-bus"; + + dcsr-epu@0 { + compatible = "fsl,dcsr-epu"; + interrupts = <52 2 0 0 + 84 2 0 0 + 85 2 0 0>; + interrupt-parent = <&mpic>; + reg = <0x0 0x1000>; + }; + dcsr-npc { + compatible = "fsl,dcsr-npc"; + reg = <0x1000 0x1000 0x1000000 0x8000>; + }; + dcsr-nxc@2000 { + compatible = "fsl,dcsr-nxc"; + reg = <0x2000 0x1000>; + }; + dcsr-corenet { + compatible = "fsl,dcsr-corenet"; + reg = <0x8000 0x1000 0xB0000 0x1000>; + }; + dcsr-dpaa@9000 { + compatible = "fsl,p5020-dcsr-dpaa", "fsl,dcsr-dpaa"; + reg = <0x9000 0x1000>; + }; + dcsr-ocn@11000 { + compatible = "fsl,p5020-dcsr-ocn", "fsl,dcsr-ocn"; + reg = <0x11000 0x1000>; + }; + dcsr-ddr@12000 { + compatible = "fsl,dcsr-ddr"; + dev-handle = <&ddr1>; + reg = <0x12000 0x1000>; + }; + dcsr-ddr@13000 { + compatible = "fsl,dcsr-ddr"; + dev-handle = <&ddr2>; + reg = <0x13000 0x1000>; + }; + dcsr-nal@18000 { + compatible = "fsl,p5020-dcsr-nal", "fsl,dcsr-nal"; + reg = <0x18000 0x1000>; + }; + dcsr-rcpm@22000 { + compatible = "fsl,p5020-dcsr-rcpm", "fsl,dcsr-rcpm"; + reg = <0x22000 0x1000>; + }; + dcsr-cpu-sb-proxy@40000 { + compatible = "fsl,dcsr-e5500-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu0>; + reg = <0x40000 0x1000>; + }; + dcsr-cpu-sb-proxy@41000 { + compatible = "fsl,dcsr-e5500-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu1>; + reg = <0x41000 0x1000>; + }; + }; + soc: soc@ffe000000 { #address-cells = <1>; #size-cells = <1>; @@ -117,13 +181,13 @@ fsl,num-laws = <32>; }; - memory-controller@8000 { + ddr1: memory-controller@8000 { compatible = "fsl,qoriq-memory-controller-v4.5", "fsl,qoriq-memory-controller"; reg = <0x8000 0x1000>; interrupts = <16 2 1 23>; }; - memory-controller@9000 { + ddr2: memory-controller@9000 { compatible = "fsl,qoriq-memory-controller-v4.5", "fsl,qoriq-memory-controller"; reg = <0x9000 0x1000>; interrupts = <16 2 1 22>; -- cgit v1.2.3-18-g5258 From 4559424a0c34f0cb22fa31bc24015a06dc064b32 Mon Sep 17 00:00:00 2001 From: Becky Bruce Date: Wed, 12 Oct 2011 16:17:02 -0500 Subject: powerpc/fsl-booke: Fix settlbcam for 64-bit Currently, it does a cntlzd on the size and then subtracts it from 21.... this doesn't take into account the varying size of a "long". Just use __ilog instead (and subtract the 10 we have to subtract to get to the tsize encoding). Also correct the comment about page sizes supported. Signed-off-by: Becky Bruce Signed-off-by: Kumar Gala --- arch/powerpc/mm/fsl_booke_mmu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/mm/fsl_booke_mmu.c b/arch/powerpc/mm/fsl_booke_mmu.c index 6f593bd2717..66a6fd38e9c 100644 --- a/arch/powerpc/mm/fsl_booke_mmu.c +++ b/arch/powerpc/mm/fsl_booke_mmu.c @@ -101,17 +101,17 @@ unsigned long p_mapped_by_tlbcam(phys_addr_t pa) /* * Set up a variable-size TLB entry (tlbcam). The parameters are not checked; - * in particular size must be a power of 4 between 4k and 256M (or 1G, for cpus - * that support extended page sizes). Note that while some cpus support a - * page size of 4G, we don't allow its use here. + * in particular size must be a power of 4 between 4k and the max supported by + * an implementation; max may further be limited by what can be represented in + * an unsigned long (for example, 32-bit implementations cannot support a 4GB + * size). */ static void settlbcam(int index, unsigned long virt, phys_addr_t phys, unsigned long size, unsigned long flags, unsigned int pid) { - unsigned int tsize, lz; + unsigned int tsize; - asm (PPC_CNTLZL "%0,%1" : "=r" (lz) : "r" (size)); - tsize = 21 - lz; + tsize = __ilog2(size) - 10; #ifdef CONFIG_SMP if ((flags & _PAGE_NO_CACHE) == 0) -- cgit v1.2.3-18-g5258 From 45116806139743cf35baa01a584e5972e5d833f6 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 13 Oct 2011 10:13:09 -0500 Subject: powerpc/85xx: Setup secondary cores PIR with hard SMP id Normally logical and hard cpu ID are the same, however in same cases like on the P3060 they may differ. Where the logical is 0..5, the hard id goes 0,1,4..7. This can causes issues for places we utilize PIR to index into array like in debug exception handlers for finding the exception stack. Move to setting up PIR with hard_smp_processor_id fixes the issue. Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/smp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index d6e47464a0d..190d111fc11 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -48,10 +48,11 @@ smp_85xx_kick_cpu(int nr) const u64 *cpu_rel_addr; __iomem u32 *bptr_vaddr; struct device_node *np; - int n = 0; + int n = 0, hw_cpu = get_hard_smp_processor_id(nr); int ioremappable; - WARN_ON (nr < 0 || nr >= NR_CPUS); + WARN_ON(nr < 0 || nr >= NR_CPUS); + WARN_ON(hw_cpu < 0 || hw_cpu >= NR_CPUS); pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr); @@ -79,7 +80,7 @@ smp_85xx_kick_cpu(int nr) local_irq_save(flags); - out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr); + out_be32(bptr_vaddr + BOOT_ENTRY_PIR, hw_cpu); #ifdef CONFIG_PPC32 out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start)); @@ -88,7 +89,7 @@ smp_85xx_kick_cpu(int nr) (ulong)(bptr_vaddr + SIZE_BOOT_ENTRY)); /* Wait a bit for the CPU to ack. */ - while ((__secondary_hold_acknowledge != nr) && (++n < 1000)) + while ((__secondary_hold_acknowledge != hw_cpu) && (++n < 1000)) mdelay(1); #else smp_generic_kick_cpu(nr); -- cgit v1.2.3-18-g5258 From 2bcd1c0cfcf53a384159c272c972645e7e822140 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 23 Sep 2011 12:41:35 -0500 Subject: powerpc/fsl_msi: add support for "msi-address-64" property Add support for the msi-address-64 property of a PCI node. This property specifies the PCI address of MSIIR (message signaled interrupt index register). In commit 3da34aae ("powerpc/fsl: Support unique MSI addresses per PCIe Root Complex"), the msi_addr_hi/msi_addr_lo fields of struct fsl_msi were redefined from an actual address to just an offset, but the fields were not renamed accordingly. These fields are replace with a single field, msiir_offset, to reflect the new meaning. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- .../devicetree/bindings/powerpc/fsl/msi-pic.txt | 42 ++++++++++++++++++++++ arch/powerpc/sysdev/fsl_msi.c | 20 +++++++---- arch/powerpc/sysdev/fsl_msi.h | 3 +- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt index 70558c3f368..5d586e1ccaf 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt @@ -25,6 +25,16 @@ Required properties: are routed to IPIC, and for 85xx/86xx cpu the interrupts are routed to MPIC. +Optional properties: +- msi-address-64: 64-bit PCI address of the MSIIR register. The MSIIR register + is used for MSI messaging. The address of MSIIR in PCI address space is + the MSI message address. + + This property may be used in virtualized environments where the hypervisor + has created an alternate mapping for the MSIR block. See below for an + explanation. + + Example: msi@41600 { compatible = "fsl,mpc8610-msi", "fsl,mpic-msi"; @@ -41,3 +51,35 @@ Example: 0xe7 0>; interrupt-parent = <&mpic>; }; + +The Freescale hypervisor and msi-address-64 +------------------------------------------- +Normally, PCI devices have access to all of CCSR via an ATMU mapping. The +Freescale MSI driver calculates the address of MSIIR (in the MSI register +block) and sets that address as the MSI message address. + +In a virtualized environment, the hypervisor may need to create an IOMMU +mapping for MSIIR. The Freescale ePAPR hypervisor has this requirement +because of hardware limitations of the Peripheral Access Management Unit +(PAMU), which is currently the only IOMMU that the hypervisor supports. +The ATMU is programmed with the guest physical address, and the PAMU +intercepts transactions and reroutes them to the true physical address. + +In the PAMU, each PCI controller is given only one primary window. The +PAMU restricts DMA operations so that they can only occur within a window. +Because PCI devices must be able to DMA to memory, the primary window must +be used to cover all of the guest's memory space. + +PAMU primary windows can be divided into 256 subwindows, and each +subwindow can have its own address mapping ("guest physical" to "true +physical"). However, each subwindow has to have the same alignment, which +means they cannot be located at just any address. Because of these +restrictions, it is usually impossible to create a 4KB subwindow that +covers MSIIR where it's normally located. + +Therefore, the hypervisor has to create a subwindow inside the same +primary window used for memory, but mapped to the MSIR block (where MSIIR +lives). The first subwindow after the end of guest memory is used for +this. The address specified in the msi-address-64 property is the PCI +address of MSIIR. The hypervisor configures the PAMU to map that address to +the true physical address of MSIIR. diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index 1cca25146b1..e5c344d336e 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -30,7 +30,7 @@ LIST_HEAD(msi_head); struct fsl_msi_feature { u32 fsl_pic_ip; - u32 msiir_offset; + u32 msiir_offset; /* Offset of MSIIR, relative to start of MSIR bank */ }; struct fsl_msi_cascade_data { @@ -126,10 +126,19 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, { struct fsl_msi *msi_data = fsl_msi_data; struct pci_controller *hose = pci_bus_to_host(pdev->bus); - u64 base = fsl_pci_immrbar_base(hose); + u64 address; /* Physical address of the MSIIR */ + int len; + const u64 *reg; + + /* If the msi-address-64 property exists, then use it */ + reg = of_get_property(hose->dn, "msi-address-64", &len); + if (reg && (len == sizeof(u64))) + address = be64_to_cpup(reg); + else + address = fsl_pci_immrbar_base(hose) + msi_data->msiir_offset; - msg->address_lo = msi_data->msi_addr_lo + lower_32_bits(base); - msg->address_hi = msi_data->msi_addr_hi + upper_32_bits(base); + msg->address_lo = lower_32_bits(address); + msg->address_hi = upper_32_bits(address); msg->data = hwirq; @@ -359,8 +368,7 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) msi->irqhost->host_data = msi; - msi->msi_addr_hi = 0x0; - msi->msi_addr_lo = features->msiir_offset + (res.start & 0xfffff); + msi->msiir_offset = features->msiir_offset + (res.start & 0xfffff); rc = fsl_msi_init_allocator(msi); if (rc) { diff --git a/arch/powerpc/sysdev/fsl_msi.h b/arch/powerpc/sysdev/fsl_msi.h index 624580c252d..1313abbc520 100644 --- a/arch/powerpc/sysdev/fsl_msi.h +++ b/arch/powerpc/sysdev/fsl_msi.h @@ -28,8 +28,7 @@ struct fsl_msi { unsigned long cascade_irq; - u32 msi_addr_lo; - u32 msi_addr_hi; + u32 msiir_offset; /* Offset of MSIIR, relative to start of CCSR */ void __iomem *msi_regs; u32 feature; int msi_virqs[NR_MSI_REG]; -- cgit v1.2.3-18-g5258 From c031ab15fff38562cc9e5921c9acc118bd99f42d Mon Sep 17 00:00:00 2001 From: Mihai Caraman Date: Thu, 13 Oct 2011 18:05:21 +0300 Subject: drivers/virt: add ioctl for 32-bit compat on 64-bit to fsl-hv-manager Add ioctl to Freescale hypervisor management driver for 32-bit user-space applications running on 64-bit guests. Signed-off-by: Mihai Caraman Acked-by: Timur Tabi Signed-off-by: Kumar Gala --- drivers/virt/fsl_hypervisor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c index 3d9162151fd..4939e0ccc4e 100644 --- a/drivers/virt/fsl_hypervisor.c +++ b/drivers/virt/fsl_hypervisor.c @@ -706,6 +706,7 @@ static const struct file_operations fsl_hv_fops = { .poll = fsl_hv_poll, .read = fsl_hv_read, .unlocked_ioctl = fsl_hv_ioctl, + .compat_ioctl = fsl_hv_ioctl, }; static struct miscdevice fsl_hv_misc_dev = { -- cgit v1.2.3-18-g5258 From 1661e5bd550bf024b04dfa7abf2963d04ab119e3 Mon Sep 17 00:00:00 2001 From: Hongjun Chen Date: Fri, 26 Mar 2010 16:43:46 +0800 Subject: powerpc/cpm: Clear muram before it is in use. We need to ensure that MURAM is in a known and cleared out state before using it as the bootloader could have utilized it from its own purposes and left it in an unknown state. If we don't clear it out we've seen issues with UCC ethernet: * Multi ethernet interfaces can't work simultanously. * Multi up/down Ethernet interfaces will halt these ports. * UCC1 RGMII can't work when kernel boots from some hosts. Signed-off-by: Kai.Jiang Signed-off-by: Hongjun Chen Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/cpm_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c index d55d0ad0dea..8db10bb9004 100644 --- a/arch/powerpc/sysdev/cpm_common.c +++ b/arch/powerpc/sysdev/cpm_common.c @@ -3,7 +3,7 @@ * * Author: Scott Wood * - * Copyright 2007 Freescale Semiconductor, Inc. + * Copyright 2007-2008,2010 Freescale Semiconductor, Inc. * * Some parts derived from commproc.c/cpm2_common.c, which is: * Copyright (c) 1997 Dan error_act (dmalek@jlc.net) @@ -146,6 +146,7 @@ unsigned long cpm_muram_alloc(unsigned long size, unsigned long align) spin_lock_irqsave(&cpm_muram_lock, flags); cpm_muram_info.alignment = align; start = rh_alloc(&cpm_muram_info, size, "commproc"); + memset(cpm_muram_addr(start), 0, size); spin_unlock_irqrestore(&cpm_muram_lock, flags); return start; -- cgit v1.2.3-18-g5258 From 62f3de91e4cfb113bf114009c6660a7f04901288 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Thu, 3 Nov 2011 17:37:47 +0000 Subject: powerpc/86xx: Correct Gianfar support for GE boards The GE DTBs were not updated when the Gianfar driver was converted to an of_platform_driver in commit b31a1d8b41513b96e9c7ec2f68c5734cef0b26a4. Update the DTBs, adding the required TBI entries. Signed-off-by: Martyn Welch Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/gef_ppc9a.dts | 33 +++++++++++++++++++++++++++++---- arch/powerpc/boot/dts/gef_sbc310.dts | 33 +++++++++++++++++++++++++++++---- arch/powerpc/boot/dts/gef_sbc610.dts | 33 +++++++++++++++++++++++++++++---- 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/boot/dts/gef_ppc9a.dts b/arch/powerpc/boot/dts/gef_ppc9a.dts index 83f4b79dff8..2266bbb303d 100644 --- a/arch/powerpc/boot/dts/gef_ppc9a.dts +++ b/arch/powerpc/boot/dts/gef_ppc9a.dts @@ -269,14 +269,16 @@ enet0: ethernet@24000 { #address-cells = <1>; #size-cells = <1>; + cell-index = <0>; device_type = "network"; - model = "eTSEC"; + model = "TSEC"; compatible = "gianfar"; reg = <0x24000 0x1000>; ranges = <0x0 0x24000 0x1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <0x1d 0x2 0x1e 0x2 0x22 0x2>; + interrupts = <29 2 30 2 34 2>; interrupt-parent = <&mpic>; + tbi-handle = <&tbi0>; phy-handle = <&phy0>; phy-connection-type = "gmii"; @@ -290,25 +292,48 @@ interrupt-parent = <&gef_pic>; interrupts = <0x9 0x4>; reg = <1>; + device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&gef_pic>; interrupts = <0x8 0x4>; reg = <3>; + device_type = "ethernet-phy"; + }; + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; }; }; }; enet1: ethernet@26000 { + #address-cells = <1>; + #size-cells = <1>; + cell-index = <2>; device_type = "network"; - model = "eTSEC"; + model = "TSEC"; compatible = "gianfar"; reg = <0x26000 0x1000>; + ranges = <0x0 0x26000 0x1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <0x1f 0x2 0x20 0x2 0x21 0x2>; + interrupts = <31 2 32 2 33 2>; interrupt-parent = <&mpic>; + tbi-handle = <&tbi2>; phy-handle = <&phy2>; phy-connection-type = "gmii"; + + mdio@520 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,gianfar-tbi"; + reg = <0x520 0x20>; + + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; }; serial0: serial@4500 { diff --git a/arch/powerpc/boot/dts/gef_sbc310.dts b/arch/powerpc/boot/dts/gef_sbc310.dts index fc3a331dd39..429e87d9ace 100644 --- a/arch/powerpc/boot/dts/gef_sbc310.dts +++ b/arch/powerpc/boot/dts/gef_sbc310.dts @@ -267,14 +267,16 @@ enet0: ethernet@24000 { #address-cells = <1>; #size-cells = <1>; + cell-index = <0>; device_type = "network"; - model = "eTSEC"; + model = "TSEC"; compatible = "gianfar"; reg = <0x24000 0x1000>; ranges = <0x0 0x24000 0x1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <0x1d 0x2 0x1e 0x2 0x22 0x2>; + interrupts = <29 2 30 2 34 2>; interrupt-parent = <&mpic>; + tbi-handle = <&tbi0>; phy-handle = <&phy0>; phy-connection-type = "gmii"; @@ -288,25 +290,48 @@ interrupt-parent = <&gef_pic>; interrupts = <0x9 0x4>; reg = <1>; + device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&gef_pic>; interrupts = <0x8 0x4>; reg = <3>; + device_type = "ethernet-phy"; + }; + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; }; }; }; enet1: ethernet@26000 { + #address-cells = <1>; + #size-cells = <1>; + cell-index = <2>; device_type = "network"; - model = "eTSEC"; + model = "TSEC"; compatible = "gianfar"; reg = <0x26000 0x1000>; + ranges = <0x0 0x26000 0x1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <0x1f 0x2 0x20 0x2 0x21 0x2>; + interrupts = <31 2 32 2 33 2>; interrupt-parent = <&mpic>; + tbi-handle = <&tbi2>; phy-handle = <&phy2>; phy-connection-type = "gmii"; + + mdio@520 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,gianfar-tbi"; + reg = <0x520 0x20>; + + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; }; serial0: serial@4500 { diff --git a/arch/powerpc/boot/dts/gef_sbc610.dts b/arch/powerpc/boot/dts/gef_sbc610.dts index c0671cc9812..d81201ac2ca 100644 --- a/arch/powerpc/boot/dts/gef_sbc610.dts +++ b/arch/powerpc/boot/dts/gef_sbc610.dts @@ -267,14 +267,16 @@ enet0: ethernet@24000 { #address-cells = <1>; #size-cells = <1>; + cell-index = <0>; device_type = "network"; - model = "eTSEC"; + model = "TSEC"; compatible = "gianfar"; reg = <0x24000 0x1000>; ranges = <0x0 0x24000 0x1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <0x1d 0x2 0x1e 0x2 0x22 0x2>; + interrupts = <29 2 30 2 34 2>; interrupt-parent = <&mpic>; + tbi-handle = <&tbi0>; phy-handle = <&phy0>; phy-connection-type = "gmii"; @@ -288,25 +290,48 @@ interrupt-parent = <&gef_pic>; interrupts = <0x9 0x4>; reg = <1>; + device_type = "ethernet-phy"; }; phy2: ethernet-phy@2 { interrupt-parent = <&gef_pic>; interrupts = <0x8 0x4>; reg = <3>; + device_type = "ethernet-phy"; + }; + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; }; }; }; enet1: ethernet@26000 { + #address-cells = <1>; + #size-cells = <1>; + cell-index = <2>; device_type = "network"; - model = "eTSEC"; + model = "TSEC"; compatible = "gianfar"; reg = <0x26000 0x1000>; + ranges = <0x0 0x26000 0x1000>; local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <0x1f 0x2 0x20 0x2 0x21 0x2>; + interrupts = <31 2 32 2 33 2>; interrupt-parent = <&mpic>; + tbi-handle = <&tbi2>; phy-handle = <&phy2>; phy-connection-type = "gmii"; + + mdio@520 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,gianfar-tbi"; + reg = <0x520 0x20>; + + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; }; serial0: serial@4500 { -- cgit v1.2.3-18-g5258 From 15a5148c6fa6d786d4ed94c6da3b6ed1b2b44f25 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Sat, 22 Oct 2011 16:20:42 -0500 Subject: powerpc/85xx: Add 'fsl,pq3-gpio' compatiable for GPIO driver Support MPC85xx platforms outside of MPC8572/MPC8536. The MPC8572/MPC8536 have an erratum that is worked around based on having "fsl,mpc8572-gpio" in the compatiable list. All other MPC85xx SoCs don't require this workaround and thus utilize the 'fsl,pq3-gpio' compatiable. Signed-off-by: Kumar Gala --- drivers/gpio/gpio-mpc8xxx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index fb4963abdf5..d74d19bf3af 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -310,6 +310,7 @@ static struct of_device_id mpc8xxx_gpio_ids[] __initdata = { { .compatible = "fsl,mpc8572-gpio", }, { .compatible = "fsl,mpc8610-gpio", }, { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, }, + { .compatible = "fsl,pq3-gpio", }, { .compatible = "fsl,qoriq-gpio", }, {} }; -- cgit v1.2.3-18-g5258 From cfadd83852eb6b9a015bc1ea396b98f395f073e0 Mon Sep 17 00:00:00 2001 From: Felix Radensky Date: Tue, 11 Oct 2011 10:24:21 +0200 Subject: powerpc/8xxx: Fix interrupt handling in MPC8xxx GPIO driver Interrupt handler in MPC8xxx GPIO driver is missing the call to PIC EOI (end of interrupt) handler. As a result, at least on 85XX systems, GPIO interrupt is delivered only once. This patch adds the missing EOI call. Tested on custom P1022 board. Signed-off-by: Felix Radensky Signed-off-by: Kumar Gala --- drivers/gpio/gpio-mpc8xxx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index d74d19bf3af..ec3fcf0a7e1 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -146,6 +146,7 @@ static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; unsigned int mask; @@ -153,6 +154,7 @@ static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) if (mask) generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, 32 - ffs(mask))); + chip->irq_eoi(&desc->irq_data); } static void mpc8xxx_irq_unmask(struct irq_data *d) -- cgit v1.2.3-18-g5258 From 44f16fcf2fba79cffb268d8f3c1f744efefa5dc9 Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Wed, 26 Oct 2011 13:46:57 -0500 Subject: powerpc/85xx: issue 15 EOI after core reset for FSL CoreNet devices This is listed as a requirement for Freescale CoreNet based devices (e.g p4080ds with MPIC v4.x) after issuing a core reset to properly clear pending interrupts. Signed-off-by: Matthew McClintock Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/mpic.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 9678081dc4e..0842c6f8a3e 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1748,6 +1748,7 @@ void mpic_reset_core(int cpu) struct mpic *mpic = mpic_primary; u32 pir; int cpuid = get_hard_smp_processor_id(cpu); + int i; /* Set target bit for core reset */ pir = mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); @@ -1759,6 +1760,15 @@ void mpic_reset_core(int cpu) pir &= ~(1 << cpuid); mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir); mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); + + /* Perform 15 EOI on each reset core to clear pending interrupts. + * This is required for FSL CoreNet based devices */ + if (mpic->flags & MPIC_FSL) { + for (i = 0; i < 15; i++) { + _mpic_write(mpic->reg_type, &mpic->cpuregs[cpuid], + MPIC_CPU_EOI, 0); + } + } } #endif /* CONFIG_SMP */ -- cgit v1.2.3-18-g5258 From 7d0d3ad5e3cfbf96aa4a2d6ee26a20c98a29d4a1 Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Tue, 25 Oct 2011 17:54:03 -0500 Subject: powerpc/fsl_booke: Fix comment in head_fsl_booke.S Fix typo in comments introduced by: commit 6dece0eb69b2a28e18d104bc5d707f1cb673f5e0 Author: Scott Wood Date: Mon Jul 25 11:29:33 2011 +0000 powerpc/32: Pass device tree address as u64 to machine_init Signed-off-by: Matthew McClintock cc: Scott Wood Signed-off-by: Kumar Gala --- arch/powerpc/kernel/head_fsl_booke.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index e1c699f3b7a..9f5d210ddf3 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -80,8 +80,8 @@ _ENTRY(_start); slw r18,r18,r17 /* r18 = page size */ addi r18,r18,-1 and r19,r3,r18 /* r19 = page offset */ - andc r31,r20,r18 /* r3 = page base */ - or r31,r31,r19 /* r3 = devtree phys addr */ + andc r31,r20,r18 /* r31 = page base */ + or r31,r31,r19 /* r31 = devtree phys addr */ mfspr r30,SPRN_MAS7 li r25,0 /* phys kernel start (low) */ -- cgit v1.2.3-18-g5258 From 43a327b79c3945049ccf675452d2d519084c9bc6 Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Tue, 25 Oct 2011 17:54:04 -0500 Subject: powerpc/85xx: Make kexec to interate over online cpus This is not strictly required, because this iterates over logical cpus and they are not (currently) discontigous. But, it's cleaner code and more obvious what is going on Signed-off-by: Matthew McClintock Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index 190d111fc11..2df4785ffd4 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -207,7 +207,7 @@ static void mpc85xx_smp_machine_kexec(struct kimage *image) if ( !timeout ) printk(KERN_ERR "Unable to bring down secondary cpu(s)"); - for (i = 0; i < num_cpus; i++) + for_each_online_cpu(i) { if ( i == smp_processor_id() ) continue; mpic_reset_core(i); -- cgit v1.2.3-18-g5258 From 6ca6ca5d813845533bcaaaeb2ca59d0805a028ca Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Mon, 15 Aug 2011 16:19:30 +0200 Subject: powerpc/83xx: Add shutdown request support to MCU handling on MPC8349 MITX This patch add support for calling ctrl_alt_del() when the power button is pressed for more than about 2 seconds on some freescale MPC83xx evaluation boards and reference design. The code uses a kthread to poll the CTRL_BTN bit each second. Also change Kconfig entry of the driver to bool, as device's gpio registration is broken when loading as module. Tested on an MPC8315E RDB board. Signed-off-by: Fabio Baltieri Signed-off-by: Kumar Gala --- arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c | 58 +++++++++++++++++++++++++- arch/powerpc/platforms/Kconfig | 2 +- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c index 70798ac911e..ef6537b8ed3 100644 --- a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c +++ b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -30,6 +32,7 @@ */ #define MCU_REG_CTRL 0x20 #define MCU_CTRL_POFF 0x40 +#define MCU_CTRL_BTN 0x80 #define MCU_NUM_GPIO 2 @@ -42,13 +45,55 @@ struct mcu { static struct mcu *glob_mcu; +struct task_struct *shutdown_thread; +static int shutdown_thread_fn(void *data) +{ + int ret; + struct mcu *mcu = glob_mcu; + + while (!kthread_should_stop()) { + ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL); + if (ret < 0) + pr_err("MCU status reg read failed.\n"); + mcu->reg_ctrl = ret; + + + if (mcu->reg_ctrl & MCU_CTRL_BTN) { + i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, + mcu->reg_ctrl & ~MCU_CTRL_BTN); + + ctrl_alt_del(); + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + + return 0; +} + +static ssize_t show_status(struct device *d, + struct device_attribute *attr, char *buf) +{ + int ret; + struct mcu *mcu = glob_mcu; + + ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL); + if (ret < 0) + return -ENODEV; + mcu->reg_ctrl = ret; + + return sprintf(buf, "%02x\n", ret); +} +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); + static void mcu_power_off(void) { struct mcu *mcu = glob_mcu; pr_info("Sending power-off request to the MCU...\n"); mutex_lock(&mcu->lock); - i2c_smbus_write_byte_data(glob_mcu->client, MCU_REG_CTRL, + i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, mcu->reg_ctrl | MCU_CTRL_POFF); mutex_unlock(&mcu->lock); } @@ -130,6 +175,13 @@ static int __devinit mcu_probe(struct i2c_client *client, dev_info(&client->dev, "will provide power-off service\n"); } + if (device_create_file(&client->dev, &dev_attr_status)) + dev_err(&client->dev, + "couldn't create device file for status\n"); + + shutdown_thread = kthread_run(shutdown_thread_fn, NULL, + "mcu-i2c-shdn"); + return 0; err: kfree(mcu); @@ -141,6 +193,10 @@ static int __devexit mcu_remove(struct i2c_client *client) struct mcu *mcu = i2c_get_clientdata(client); int ret; + kthread_stop(shutdown_thread); + + device_remove_file(&client->dev, &dev_attr_status); + if (glob_mcu == mcu) { ppc_md.power_off = NULL; glob_mcu = NULL; diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 9fdb9b6ab37..e4588721ef3 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -346,7 +346,7 @@ config SIMPLE_GPIO on-board peripherals. config MCU_MPC8349EMITX - tristate "MPC8349E-mITX MCU driver" + bool "MPC8349E-mITX MCU driver" depends on I2C && PPC_83xx select GENERIC_GPIO select ARCH_REQUIRE_GPIOLIB -- cgit v1.2.3-18-g5258 From 96cc017c5b7ec095ef047d3c1952b6b6bbf98943 Mon Sep 17 00:00:00 2001 From: Shengzhou Liu Date: Fri, 26 Aug 2011 11:18:21 +0800 Subject: powerpc/p3060qds: Add support for P3060QDS board The P3060QDS is a Freescale reference board that hosts the six-core P3060 SOC. The P3060 Processor combines six e500mc Power Architecture processor cores with high-performance datapath acceleration architecture(DPAA), CoreNet fabric infrastructure, as well as network and peripheral interfaces. P3060QDS Board Overview: Memory subsystem: - 2G Bytes unbuffered DDR3 SDRAM SO-DIMM(64bit bus) - 128M Bytes NOR flash single-chip memory - 16M Bytes SPI flash - 8K Bytes AT24C64 I2C EEPROM Ethernet: - 4x1G + 4x1G/2.5G Ethernet controllers - 2xRGMII + 1xMII, three VSC8641 PHYs on board - Suport multiple Vitesse VSC8234 SGMII Cards in Slot1/2/3 PCIe: Two PCI Express 2.0 controllers/ports USB: Two USB2.0, USB1(TYPE-A) and USB2(TYPE-AB) on board I2C: Four I2C controllers UART: Supports up to four UARTs RapidIO: Supports two serial RapidIO ports Signed-off-by: Shengzhou Liu Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/p3060qds.dts | 238 +++++++++ arch/powerpc/boot/dts/p3060si.dtsi | 719 +++++++++++++++++++++++++++ arch/powerpc/configs/corenet32_smp_defconfig | 1 + arch/powerpc/platforms/85xx/Kconfig | 12 + arch/powerpc/platforms/85xx/Makefile | 1 + arch/powerpc/platforms/85xx/p3060_qds.c | 77 +++ 6 files changed, 1048 insertions(+) create mode 100644 arch/powerpc/boot/dts/p3060qds.dts create mode 100644 arch/powerpc/boot/dts/p3060si.dtsi create mode 100644 arch/powerpc/platforms/85xx/p3060_qds.c diff --git a/arch/powerpc/boot/dts/p3060qds.dts b/arch/powerpc/boot/dts/p3060qds.dts new file mode 100644 index 00000000000..08b9193213e --- /dev/null +++ b/arch/powerpc/boot/dts/p3060qds.dts @@ -0,0 +1,238 @@ +/* + * P3060QDS Device Tree Source + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "p3060si.dtsi" + +/ { + model = "fsl,P3060QDS"; + compatible = "fsl,P3060QDS"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + memory { + device_type = "memory"; + }; + + dcsr: dcsr@f00000000 { + ranges = <0x00000000 0xf 0x00000000 0x01008000>; + }; + + soc: soc@ffe000000 { + spi@110000 { + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25sl12801"; + reg = <0>; + spi-max-frequency = <40000000>; /* input clock */ + partition@u-boot { + label = "u-boot"; + reg = <0x00000000 0x00100000>; + read-only; + }; + partition@kernel { + label = "kernel"; + reg = <0x00100000 0x00500000>; + read-only; + }; + partition@dtb { + label = "dtb"; + reg = <0x00600000 0x00100000>; + read-only; + }; + partition@fs { + label = "file system"; + reg = <0x00700000 0x00900000>; + }; + }; + flash@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,en25q32b"; + reg = <1>; + spi-max-frequency = <40000000>; /* input clock */ + partition@spi1 { + label = "spi1"; + reg = <0x00000000 0x00400000>; + }; + }; + flash@2 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "atmel,at45db081d"; + reg = <2>; + spi-max-frequency = <40000000>; /* input clock */ + partition@spi1 { + label = "spi2"; + reg = <0x00000000 0x00100000>; + }; + }; + flash@3 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,sst25wf040"; + reg = <3>; + spi-max-frequency = <40000000>; /* input clock */ + partition@spi3 { + label = "spi3"; + reg = <0x00000000 0x00080000>; + }; + }; + }; + + i2c@118000 { + eeprom@51 { + compatible = "at24,24c256"; + reg = <0x51>; + }; + eeprom@53 { + compatible = "at24,24c256"; + reg = <0x53>; + }; + rtc@68 { + compatible = "dallas,ds3232"; + reg = <0x68>; + interrupts = <0x1 0x1 0 0>; + }; + }; + + usb0: usb@210000 { + phy_type = "ulpi"; + }; + + usb1: usb@211000 { + dr_mode = "host"; + phy_type = "ulpi"; + }; + }; + + rapidio@ffe0c0000 { + reg = <0xf 0xfe0c0000 0 0x11000>; + + port1 { + ranges = <0 0 0xc 0x20000000 0 0x10000000>; + }; + port2 { + ranges = <0 0 0xc 0x30000000 0 0x10000000>; + }; + }; + + localbus@ffe124000 { + reg = <0xf 0xfe124000 0 0x1000>; + ranges = <0 0 0xf 0xe8000000 0x08000000 + 2 0 0xf 0xffa00000 0x00040000 + 3 0 0xf 0xffdf0000 0x00008000>; + + flash@0,0 { + compatible = "cfi-flash"; + reg = <0 0 0x08000000>; + bank-width = <2>; + device-width = <2>; + }; + + nand@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,elbc-fcm-nand"; + reg = <0x2 0x0 0x40000>; + + partition@0 { + label = "NAND U-Boot Image"; + reg = <0x0 0x02000000>; + read-only; + }; + + partition@2000000 { + label = "NAND Root File System"; + reg = <0x02000000 0x10000000>; + }; + + partition@12000000 { + label = "NAND Compressed RFS Image"; + reg = <0x12000000 0x08000000>; + }; + + partition@1a000000 { + label = "NAND Linux Kernel Image"; + reg = <0x1a000000 0x04000000>; + }; + + partition@1e000000 { + label = "NAND DTB Image"; + reg = <0x1e000000 0x01000000>; + }; + + partition@1f000000 { + label = "NAND Writable User area"; + reg = <0x1f000000 0x21000000>; + }; + }; + + board-control@3,0 { + compatible = "fsl,p3060qds-fpga", "fsl,fpga-qixis"; + reg = <3 0 0x100>; + }; + }; + + pci0: pcie@ffe200000 { + reg = <0xf 0xfe200000 0 0x1000>; + ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0x0 0x20000000 + 0x01000000 0 0x00000000 0xf 0xf8000000 0x0 0x00010000>; + pcie@0 { + ranges = <0x02000000 0 0xe0000000 + 0x02000000 0 0xe0000000 + 0 0x20000000 + + 0x01000000 0 0x00000000 + 0x01000000 0 0x00000000 + 0 0x00010000>; + }; + }; + + pci1: pcie@ffe201000 { + reg = <0xf 0xfe201000 0 0x1000>; + ranges = <0x02000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 + 0x01000000 0x0 0x00000000 0xf 0xf8010000 0x0 0x00010000>; + pcie@0 { + ranges = <0x02000000 0 0xe0000000 + 0x02000000 0 0xe0000000 + 0 0x20000000 + + 0x01000000 0 0x00000000 + 0x01000000 0 0x00000000 + 0 0x00010000>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/p3060si.dtsi b/arch/powerpc/boot/dts/p3060si.dtsi new file mode 100644 index 00000000000..68947e157bb --- /dev/null +++ b/arch/powerpc/boot/dts/p3060si.dtsi @@ -0,0 +1,719 @@ +/* + * P3060 Silicon Device Tree Source + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/dts-v1/; + +/ { + compatible = "fsl,P3060"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + aliases { + ccsr = &soc; + dcsr = &dcsr; + + serial0 = &serial0; + serial1 = &serial1; + serial2 = &serial2; + serial3 = &serial3; + pci0 = &pci0; + pci1 = &pci1; + usb0 = &usb0; + usb1 = &usb1; + dma0 = &dma0; + dma1 = &dma1; + msi0 = &msi0; + msi1 = &msi1; + msi2 = &msi2; + + crypto = &crypto; + sec_jr0 = &sec_jr0; + sec_jr1 = &sec_jr1; + sec_jr2 = &sec_jr2; + sec_jr3 = &sec_jr3; + rtic_a = &rtic_a; + rtic_b = &rtic_b; + rtic_c = &rtic_c; + rtic_d = &rtic_d; + sec_mon = &sec_mon; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: PowerPC,e500mc@0 { + device_type = "cpu"; + reg = <0>; + next-level-cache = <&L2_0>; + L2_0: l2-cache { + next-level-cache = <&cpc>; + }; + }; + cpu1: PowerPC,e500mc@1 { + device_type = "cpu"; + reg = <1>; + next-level-cache = <&L2_1>; + L2_1: l2-cache { + next-level-cache = <&cpc>; + }; + }; + cpu4: PowerPC,e500mc@4 { + device_type = "cpu"; + reg = <4>; + next-level-cache = <&L2_4>; + L2_4: l2-cache { + next-level-cache = <&cpc>; + }; + }; + cpu5: PowerPC,e500mc@5 { + device_type = "cpu"; + reg = <5>; + next-level-cache = <&L2_5>; + L2_5: l2-cache { + next-level-cache = <&cpc>; + }; + }; + cpu6: PowerPC,e500mc@6 { + device_type = "cpu"; + reg = <6>; + next-level-cache = <&L2_6>; + L2_6: l2-cache { + next-level-cache = <&cpc>; + }; + }; + cpu7: PowerPC,e500mc@7 { + device_type = "cpu"; + reg = <7>; + next-level-cache = <&L2_7>; + L2_7: l2-cache { + next-level-cache = <&cpc>; + }; + }; + }; + + dcsr: dcsr@f00000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,dcsr", "simple-bus"; + + dcsr-epu@0 { + compatible = "fsl,dcsr-epu"; + interrupts = <52 2 0 0 + 84 2 0 0 + 85 2 0 0>; + interrupt-parent = <&mpic>; + reg = <0x0 0x1000>; + }; + dcsr-npc { + compatible = "fsl,dcsr-npc"; + reg = <0x1000 0x1000 0x1000000 0x8000>; + }; + dcsr-nxc@2000 { + compatible = "fsl,dcsr-nxc"; + reg = <0x2000 0x1000>; + }; + dcsr-corenet { + compatible = "fsl,dcsr-corenet"; + reg = <0x8000 0x1000 0xB0000 0x1000>; + }; + dcsr-dpaa@9000 { + compatible = "fsl,p3060-dcsr-dpaa", "fsl,dcsr-dpaa"; + reg = <0x9000 0x1000>; + }; + dcsr-ocn@11000 { + compatible = "fsl,p3060-dcsr-ocn", "fsl,dcsr-ocn"; + reg = <0x11000 0x1000>; + }; + dcsr-ddr@12000 { + compatible = "fsl,dcsr-ddr"; + dev-handle = <&ddr>; + reg = <0x12000 0x1000>; + }; + dcsr-nal@18000 { + compatible = "fsl,p3060-dcsr-nal", "fsl,dcsr-nal"; + reg = <0x18000 0x1000>; + }; + dcsr-rcpm@22000 { + compatible = "fsl,p3060-dcsr-rcpm", "fsl,dcsr-rcpm"; + reg = <0x22000 0x1000>; + }; + dcsr-cpu-sb-proxy@40000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu0>; + reg = <0x40000 0x1000>; + }; + dcsr-cpu-sb-proxy@41000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu1>; + reg = <0x41000 0x1000>; + }; + dcsr-cpu-sb-proxy@44000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu4>; + reg = <0x44000 0x1000>; + }; + dcsr-cpu-sb-proxy@45000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu5>; + reg = <0x45000 0x1000>; + }; + dcsr-cpu-sb-proxy@46000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu6>; + reg = <0x46000 0x1000>; + }; + dcsr-cpu-sb-proxy@47000 { + compatible = "fsl,dcsr-e500mc-sb-proxy", "fsl,dcsr-cpu-sb-proxy"; + cpu-handle = <&cpu7>; + reg = <0x47000 0x1000>; + }; + }; + + soc: soc@ffe000000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "soc"; + compatible = "simple-bus"; + ranges = <0x00000000 0xf 0xfe000000 0x1000000>; + reg = <0xf 0xfe000000 0 0x00001000>; + + soc-sram-error { + compatible = "fsl,soc-sram-error"; + interrupts = <16 2 1 29>; + }; + + corenet-law@0 { + compatible = "fsl,corenet-law"; + reg = <0x0 0x1000>; + fsl,num-laws = <32>; + }; + + ddr: memory-controller@8000 { + compatible = "fsl,qoriq-memory-controller-v4.4", "fsl,qoriq-memory-controller"; + reg = <0x8000 0x1000>; + interrupts = <16 2 1 23>; + }; + + cpc: l3-cache-controller@10000 { + compatible = "fsl,p3060-l3-cache-controller", "cache"; + reg = <0x10000 0x1000 + 0x11000 0x1000>; + interrupts = <16 2 1 27>; + }; + + corenet-cf@18000 { + compatible = "fsl,corenet-cf"; + reg = <0x18000 0x1000>; + interrupts = <16 2 1 31>; + fsl,ccf-num-csdids = <32>; + fsl,ccf-num-snoopids = <32>; + }; + + iommu@20000 { + compatible = "fsl,pamu-v1.0", "fsl,pamu"; + reg = <0x20000 0x5000>; + interrupts = < + 24 2 0 0 + 16 2 1 30>; + }; + + mpic: pic@40000 { + clock-frequency = <0>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <4>; + reg = <0x40000 0x40000>; + compatible = "fsl,mpic", "chrp,open-pic"; + device_type = "open-pic"; + }; + + msi0: msi@41600 { + compatible = "fsl,mpic-msi"; + reg = <0x41600 0x200>; + msi-available-ranges = <0 0x100>; + interrupts = < + 0xe0 0 0 0 + 0xe1 0 0 0 + 0xe2 0 0 0 + 0xe3 0 0 0 + 0xe4 0 0 0 + 0xe5 0 0 0 + 0xe6 0 0 0 + 0xe7 0 0 0>; + }; + + msi1: msi@41800 { + compatible = "fsl,mpic-msi"; + reg = <0x41800 0x200>; + msi-available-ranges = <0 0x100>; + interrupts = < + 0xe8 0 0 0 + 0xe9 0 0 0 + 0xea 0 0 0 + 0xeb 0 0 0 + 0xec 0 0 0 + 0xed 0 0 0 + 0xee 0 0 0 + 0xef 0 0 0>; + }; + + msi2: msi@41a00 { + compatible = "fsl,mpic-msi"; + reg = <0x41a00 0x200>; + msi-available-ranges = <0 0x100>; + interrupts = < + 0xf0 0 0 0 + 0xf1 0 0 0 + 0xf2 0 0 0 + 0xf3 0 0 0 + 0xf4 0 0 0 + 0xf5 0 0 0 + 0xf6 0 0 0 + 0xf7 0 0 0>; + }; + + rmu: rmu@d3000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,srio-rmu"; + reg = <0xd3000 0x500>; + ranges = <0x0 0xd3000 0x500>; + + message-unit@0 { + compatible = "fsl,srio-msg-unit"; + reg = <0x0 0x100>; + interrupts = < + 60 2 0 0 /* msg1_tx_irq */ + 61 2 0 0>;/* msg1_rx_irq */ + }; + message-unit@100 { + compatible = "fsl,srio-msg-unit"; + reg = <0x100 0x100>; + interrupts = < + 62 2 0 0 /* msg2_tx_irq */ + 63 2 0 0>;/* msg2_rx_irq */ + }; + doorbell-unit@400 { + compatible = "fsl,srio-dbell-unit"; + reg = <0x400 0x80>; + interrupts = < + 56 2 0 0 /* bell_outb_irq */ + 57 2 0 0>;/* bell_inb_irq */ + }; + port-write-unit@4e0 { + compatible = "fsl,srio-port-write-unit"; + reg = <0x4e0 0x20>; + interrupts = <16 2 1 11>; + }; + }; + + guts: global-utilities@e0000 { + compatible = "fsl,qoriq-device-config-1.0"; + reg = <0xe0000 0xe00>; + fsl,has-rstcr; + #sleep-cells = <1>; + fsl,liodn-bits = <12>; + }; + + pins: global-utilities@e0e00 { + compatible = "fsl,qoriq-pin-control-1.0"; + reg = <0xe0e00 0x200>; + #sleep-cells = <2>; + }; + + clockgen: global-utilities@e1000 { + compatible = "fsl,p3060-clockgen", "fsl,qoriq-clockgen-1.0"; + reg = <0xe1000 0x1000>; + clock-frequency = <0>; + }; + + rcpm: global-utilities@e2000 { + compatible = "fsl,qoriq-rcpm-1.0"; + reg = <0xe2000 0x1000>; + #sleep-cells = <1>; + }; + + sfp: sfp@e8000 { + compatible = "fsl,p3060-sfp", "fsl,qoriq-sfp-1.0"; + reg = <0xe8000 0x1000>; + }; + + serdes: serdes@ea000 { + compatible = "fsl,p3060-serdes"; + reg = <0xea000 0x1000>; + }; + + dma0: dma@100300 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,p3060-dma", "fsl,eloplus-dma"; + reg = <0x100300 0x4>; + ranges = <0x0 0x100100 0x200>; + cell-index = <0>; + dma-channel@0 { + compatible = "fsl,p3060-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x0 0x80>; + cell-index = <0>; + interrupts = <28 2 0 0>; + }; + dma-channel@80 { + compatible = "fsl,p3060-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x80 0x80>; + cell-index = <1>; + interrupts = <29 2 0 0>; + }; + dma-channel@100 { + compatible = "fsl,p3060-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x100 0x80>; + cell-index = <2>; + interrupts = <30 2 0 0>; + }; + dma-channel@180 { + compatible = "fsl,p3060-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x180 0x80>; + cell-index = <3>; + interrupts = <31 2 0 0>; + }; + }; + + dma1: dma@101300 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,p3060-dma", "fsl,eloplus-dma"; + reg = <0x101300 0x4>; + ranges = <0x0 0x101100 0x200>; + cell-index = <1>; + dma-channel@0 { + compatible = "fsl,p3060-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x0 0x80>; + cell-index = <0>; + interrupts = <32 2 0 0>; + }; + dma-channel@80 { + compatible = "fsl,p3060-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x80 0x80>; + cell-index = <1>; + interrupts = <33 2 0 0>; + }; + dma-channel@100 { + compatible = "fsl,p3060-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x100 0x80>; + cell-index = <2>; + interrupts = <34 2 0 0>; + }; + dma-channel@180 { + compatible = "fsl,p3060-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x180 0x80>; + cell-index = <3>; + interrupts = <35 2 0 0>; + }; + }; + + spi@110000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,p3060-espi", "fsl,mpc8536-espi"; + reg = <0x110000 0x1000>; + interrupts = <53 0x2 0 0>; + fsl,espi-num-chipselects = <4>; + }; + + i2c@118000 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + compatible = "fsl-i2c"; + reg = <0x118000 0x100>; + interrupts = <38 2 0 0>; + dfsrr; + }; + + i2c@118100 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <1>; + compatible = "fsl-i2c"; + reg = <0x118100 0x100>; + interrupts = <38 2 0 0>; + dfsrr; + }; + + i2c@119000 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <2>; + compatible = "fsl-i2c"; + reg = <0x119000 0x100>; + interrupts = <39 2 0 0>; + dfsrr; + }; + + i2c@119100 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <3>; + compatible = "fsl-i2c"; + reg = <0x119100 0x100>; + interrupts = <39 2 0 0>; + dfsrr; + }; + + serial0: serial@11c500 { + cell-index = <0>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x11c500 0x100>; + clock-frequency = <0>; + interrupts = <36 2 0 0>; + }; + + serial1: serial@11c600 { + cell-index = <1>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x11c600 0x100>; + clock-frequency = <0>; + interrupts = <36 2 0 0>; + }; + + serial2: serial@11d500 { + cell-index = <2>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x11d500 0x100>; + clock-frequency = <0>; + interrupts = <37 2 0 0>; + }; + + serial3: serial@11d600 { + cell-index = <3>; + device_type = "serial"; + compatible = "ns16550"; + reg = <0x11d600 0x100>; + clock-frequency = <0>; + interrupts = <37 2 0 0>; + }; + + gpio0: gpio@130000 { + compatible = "fsl,p3060-gpio", "fsl,qoriq-gpio"; + reg = <0x130000 0x1000>; + interrupts = <55 2 0 0>; + #gpio-cells = <2>; + gpio-controller; + }; + + usb0: usb@210000 { + compatible = "fsl,p3060-usb2-mph", + "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; + reg = <0x210000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <44 0x2 0 0>; + }; + + usb1: usb@211000 { + compatible = "fsl,p3060-usb2-dr", + "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; + reg = <0x211000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <45 0x2 0 0>; + }; + + crypto: crypto@300000 { + compatible = "fsl,sec-v4.1", "fsl,sec-v4.0"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x300000 0x10000>; + ranges = <0 0x300000 0x10000>; + interrupt-parent = <&mpic>; + interrupts = <92 2 0 0>; + + sec_jr0: jr@1000 { + compatible = "fsl,sec-v4.1-job-ring", "fsl,sec-v4.0-job-ring"; + reg = <0x1000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <88 2 0 0>; + }; + + sec_jr1: jr@2000 { + compatible = "fsl,sec-v4.1-job-ring", "fsl,sec-v4.0-job-ring"; + reg = <0x2000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <89 2 0 0>; + }; + + sec_jr2: jr@3000 { + compatible = "fsl,sec-v4.1-job-ring", "fsl,sec-v4.0-job-ring"; + reg = <0x3000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <90 2 0 0>; + }; + + sec_jr3: jr@4000 { + compatible = "fsl,sec-v4.1-job-ring", "fsl,sec-v4.0-job-ring"; + reg = <0x4000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <91 2 0 0>; + }; + + rtic@6000 { + compatible = "fsl,sec-v4.1-rtic", "fsl,sec-v4.0-rtic"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x6000 0x100>; + ranges = <0x0 0x6100 0xe00>; + + rtic_a: rtic-a@0 { + compatible = "fsl,sec-v4.1-rtic-memory", "fsl,sec-v4.0-rtic-memory"; + reg = <0x00 0x20 0x100 0x80>; + }; + + rtic_b: rtic-b@20 { + compatible = "fsl,sec-v4.1-rtic-memory", "fsl,sec-v4.0-rtic-memory"; + reg = <0x20 0x20 0x200 0x80>; + }; + + rtic_c: rtic-c@40 { + compatible = "fsl,sec-v4.1-rtic-memory", "fsl,sec-v4.0-rtic-memory"; + reg = <0x40 0x20 0x300 0x80>; + }; + + rtic_d: rtic-d@60 { + compatible = "fsl,sec-v4.1-rtic-memory", "fsl,sec-v4.0-rtic-memory"; + reg = <0x60 0x20 0x500 0x80>; + }; + }; + }; + + sec_mon: sec_mon@314000 { + compatible = "fsl,sec-v4.1-mon", "fsl,sec-v4.0-mon"; + reg = <0x314000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <93 2 0 0>; + }; + }; + + rapidio@ffe0c0000 { + compatible = "fsl,srio"; + interrupts = <16 2 1 11>; + #address-cells = <2>; + #size-cells = <2>; + fsl,srio-rmu-handle = <&rmu>; + ranges; + + port1 { + #address-cells = <2>; + #size-cells = <2>; + cell-index = <1>; + }; + + port2 { + #address-cells = <2>; + #size-cells = <2>; + cell-index = <2>; + }; + }; + + localbus@ffe124000 { + compatible = "fsl,p3060-elbc", "fsl,elbc", "simple-bus"; + interrupts = <25 2 0 0>; + #address-cells = <2>; + #size-cells = <1>; + }; + + pci0: pcie@ffe200000 { + compatible = "fsl,p3060-pcie", "fsl,qoriq-pcie-v2.2"; + device_type = "pci"; + #size-cells = <2>; + #address-cells = <3>; + bus-range = <0x0 0xff>; + clock-frequency = <33333333>; + fsl,msi = <&msi0>; + interrupts = <16 2 1 15>; + pcie@0 { + reg = <0 0 0 0 0>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + interrupts = <16 2 1 15>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 40 1 0 0 + 0000 0 0 2 &mpic 1 1 0 0 + 0000 0 0 3 &mpic 2 1 0 0 + 0000 0 0 4 &mpic 3 1 0 0 + >; + }; + }; + + pci1: pcie@ffe201000 { + compatible = "fsl,p3060-pcie", "fsl,qoriq-pcie-v2.2"; + device_type = "pci"; + #size-cells = <2>; + #address-cells = <3>; + bus-range = <0 0xff>; + clock-frequency = <33333333>; + fsl,msi = <&msi1>; + interrupts = <16 2 1 14>; + pcie@0 { + reg = <0 0 0 0 0>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + interrupts = <16 2 1 14>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x0 */ + 0000 0 0 1 &mpic 41 1 0 0 + 0000 0 0 2 &mpic 5 1 0 0 + 0000 0 0 3 &mpic 6 1 0 0 + 0000 0 0 4 &mpic 7 1 0 0 + >; + }; + }; +}; diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig index 797e1fd1a5b..f087de6ec03 100644 --- a/arch/powerpc/configs/corenet32_smp_defconfig +++ b/arch/powerpc/configs/corenet32_smp_defconfig @@ -23,6 +23,7 @@ CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set CONFIG_P2041_RDB=y CONFIG_P3041_DS=y +CONFIG_P3060_QDS=y CONFIG_P4080_DS=y CONFIG_P5020_DS=y CONFIG_HIGHMEM=y diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 7f84cfd1726..45023e26aea 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -197,6 +197,18 @@ config P3041_DS help This option enables support for the P3041 DS board +config P3060_QDS + bool "Freescale P3060 QDS" + select DEFAULT_UIMAGE + select PPC_E500MC + select PHYS_64BIT + select SWIOTLB + select MPC8xxx_GPIO + select HAS_RAPIDIO + select PPC_EPAPR_HV_PIC + help + This option enables support for the P3060 QDS board + config P4080_DS bool "Freescale P4080 DS" select DEFAULT_UIMAGE diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 39e6c22f06f..bc5acb95917 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_P1022_DS) += p1022_ds.o obj-$(CONFIG_P1023_RDS) += p1023_rds.o obj-$(CONFIG_P2041_RDB) += p2041_rdb.o corenet_ds.o obj-$(CONFIG_P3041_DS) += p3041_ds.o corenet_ds.o +obj-$(CONFIG_P3060_QDS) += p3060_qds.o corenet_ds.o obj-$(CONFIG_P4080_DS) += p4080_ds.o corenet_ds.o obj-$(CONFIG_P5020_DS) += p5020_ds.o corenet_ds.o obj-$(CONFIG_STX_GP3) += stx_gp3.o diff --git a/arch/powerpc/platforms/85xx/p3060_qds.c b/arch/powerpc/platforms/85xx/p3060_qds.c new file mode 100644 index 00000000000..01dcf44871e --- /dev/null +++ b/arch/powerpc/platforms/85xx/p3060_qds.c @@ -0,0 +1,77 @@ +/* + * P3060 QDS Setup + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "corenet_ds.h" + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p3060_qds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); +#ifdef CONFIG_SMP + extern struct smp_ops_t smp_85xx_ops; +#endif + + if (of_flat_dt_is_compatible(root, "fsl,P3060QDS")) + return 1; + + /* Check if we're running under the Freescale hypervisor */ + if (of_flat_dt_is_compatible(root, "fsl,P3060QDS-hv")) { + ppc_md.init_IRQ = ehv_pic_init; + ppc_md.get_irq = ehv_pic_get_irq; + ppc_md.restart = fsl_hv_restart; + ppc_md.power_off = fsl_hv_halt; + ppc_md.halt = fsl_hv_halt; +#ifdef CONFIG_SMP + /* + * Disable the timebase sync operations because we can't write + * to the timebase registers under the hypervisor. + */ + smp_85xx_ops.give_timebase = NULL; + smp_85xx_ops.take_timebase = NULL; +#endif + return 1; + } + + return 0; +} + +define_machine(p3060_qds) { + .name = "P3060 QDS", + .probe = p3060_qds_probe, + .setup_arch = corenet_ds_setup_arch, + .init_IRQ = corenet_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_coreint_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, + .power_save = e500_idle, +}; + +machine_device_initcall(p3060_qds, declare_of_platform_devices); + +#ifdef CONFIG_SWIOTLB +machine_arch_initcall(p3060_qds, swiotlb_setup_bus_notifier); +#endif -- cgit v1.2.3-18-g5258