From 0eb2e6019ae8e76a537bdca07b31e1524a3bba48 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 1 Nov 2006 15:53:00 +1100 Subject: [POWERPC] pmac_feature_call checks platform This patch makes sure that a caller of pmac_call_feature() won't try to call into ppc_md.feature_call of another platform, which might happen if some powermac drivers are loaded on non-powermac machines. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- include/asm-powerpc/pmac_feature.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-powerpc/pmac_feature.h b/include/asm-powerpc/pmac_feature.h index d3599cc9aa7..d43d91beba9 100644 --- a/include/asm-powerpc/pmac_feature.h +++ b/include/asm-powerpc/pmac_feature.h @@ -146,7 +146,7 @@ struct device_node; static inline long pmac_call_feature(int selector, struct device_node* node, long param, long value) { - if (!ppc_md.feature_call) + if (!ppc_md.feature_call || !machine_is(powermac)) return -ENODEV; return ppc_md.feature_call(selector, node, param, value); } -- cgit v1.2.3 From c0b3ae14f14f53fb0d0453feeeb830a080726155 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Tue, 6 Feb 2007 12:03:31 +1100 Subject: [POWERPC] Move of_irq_to_resource from prom.h to prom_parse.c In the powerpc architecture, of_irq_to_resource, currently sitting in prom.h, needs irq_of_parse_and_map and NO_IRQ from asm-powerpc/irq.h. The solution suggested by Benjamin Herrenschmidt is to move it to arch/powerpc/kernel/prom_parse.c. Signed-off-by: Mathieu Desnoyers Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/prom_parse.c | 14 ++++++++++++++ include/asm-powerpc/prom.h | 15 ++------------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index aa40a530729..78ee8d5b07c 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c @@ -1042,3 +1042,17 @@ const void *of_get_mac_address(struct device_node *np) } EXPORT_SYMBOL(of_get_mac_address); +int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) +{ + int irq = irq_of_parse_and_map(dev, index); + + /* Only dereference the resource if both the + * resource and the irq are valid. */ + if (r && irq != NO_IRQ) { + r->start = r->end = irq; + r->flags = IORESOURCE_IRQ; + } + + return irq; +} +EXPORT_SYMBOL_GPL(of_irq_to_resource); diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index ec400f608e1..1375f8c9161 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -334,19 +334,8 @@ extern int of_irq_map_one(struct device_node *device, int index, struct pci_dev; extern int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq); -static inline int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) -{ - int irq = irq_of_parse_and_map(dev, index); - - /* Only dereference the resource if both the - * resource and the irq are valid. */ - if (r && irq != NO_IRQ) { - r->start = r->end = irq; - r->flags = IORESOURCE_IRQ; - } - - return irq; -} +extern int of_irq_to_resource(struct device_node *dev, int index, + struct resource *r); static inline void __iomem *of_iomap(struct device_node *np, int index) { -- cgit v1.2.3 From c3e8011ad1bf4791a9c6d70ac1b377df93a9f5af Mon Sep 17 00:00:00 2001 From: Christian Krafft Date: Wed, 25 Apr 2007 01:32:02 +1000 Subject: [POWERPC] Uninline of_iomap function There is no big reason to have that function inlined. Signed-off-by: Christian Krafft Acked-by: Arnd Bergmann Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/prom_parse.c | 11 +++++++++++ include/asm-powerpc/prom.h | 19 ++++++++----------- 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index 78ee8d5b07c..b5c96af955c 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c @@ -1056,3 +1056,14 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) return irq; } EXPORT_SYMBOL_GPL(of_irq_to_resource); + +void __iomem *of_iomap(struct device_node *np, int index) +{ + struct resource res; + + if (of_address_to_resource(np, index, &res)) + return NULL; + + return ioremap(res.start, 1 + res.end - res.start); +} +EXPORT_SYMBOL(of_iomap); diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index 1375f8c9161..6845af93ba9 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -20,7 +20,6 @@ #include #include #include -#include /* Definitions used by the flattened device tree */ #define OF_DT_HEADER 0xd00dfeed /* marker */ @@ -337,16 +336,14 @@ extern int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq); extern int of_irq_to_resource(struct device_node *dev, int index, struct resource *r); -static inline void __iomem *of_iomap(struct device_node *np, int index) -{ - struct resource res; - - if (of_address_to_resource(np, index, &res)) - return NULL; - - return ioremap(res.start, 1 + res.end - res.start); -} - +/** + * of_iomap - Maps the memory mapped IO for a given device_node + * @device: the device whose io range will be mapped + * @index: index of the io range + * + * Returns a pointer to the mapped memory + */ +extern void __iomem *of_iomap(struct device_node *device, int index); #endif /* __KERNEL__ */ #endif /* _POWERPC_PROM_H */ -- cgit v1.2.3 From 57d7909e0d2dd54567ae775e22b14076b777042a Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 30 Apr 2007 14:06:25 +1000 Subject: [POWERPC] Revise PPC44x MMU code for arch/powerpc This patch takes the definitions for the PPC44x MMU (a software loaded TLB) from asm-ppc/mmu.h, cleans them up of things no longer necessary in arch/powerpc and puts them in a new asm-powerpc/mmu_44x.h file. It also substantially simplifies arch/powerpc/mm/44x_mmu.c and makes a couple of small fixes necessary for the 44x MMU code to build and work properly in arch/powerpc. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/head_44x.S | 4 +-- arch/powerpc/mm/44x_mmu.c | 82 ++++++++++-------------------------------- arch/powerpc/mm/mmu_decl.h | 3 +- include/asm-powerpc/mmu-44x.h | 72 +++++++++++++++++++++++++++++++++++++ include/asm-powerpc/mmu.h | 7 ++-- 5 files changed, 100 insertions(+), 68 deletions(-) create mode 100644 include/asm-powerpc/mmu-44x.h (limited to 'include') diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S index a15d4b8cce4..9ee6773cf02 100644 --- a/arch/powerpc/kernel/head_44x.S +++ b/arch/powerpc/kernel/head_44x.S @@ -120,8 +120,8 @@ skpinv: addi r4,r4,1 /* Increment */ * Configure and load pinned entry into TLB slot 63. */ - lis r3,KERNELBASE@h /* Load the kernel virtual address */ - ori r3,r3,KERNELBASE@l + lis r3,PAGE_OFFSET@h + ori r3,r3,PAGE_OFFSET@l /* Kernel is at the base of RAM */ li r4, 0 /* Load the kernel physical address */ diff --git a/arch/powerpc/mm/44x_mmu.c b/arch/powerpc/mm/44x_mmu.c index 0a0a0487b33..ca4dcb07a93 100644 --- a/arch/powerpc/mm/44x_mmu.c +++ b/arch/powerpc/mm/44x_mmu.c @@ -24,73 +24,38 @@ * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include - -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include +#include +#include #include "mmu_decl.h" -extern char etext[], _stext[]; - /* Used by the 44x TLB replacement exception handler. * Just needed it declared someplace. */ -unsigned int tlb_44x_index = 0; -unsigned int tlb_44x_hwater = 62; +unsigned int tlb_44x_index; /* = 0 */ +unsigned int tlb_44x_hwater = PPC44x_TLB_SIZE - 1 - PPC44x_EARLY_TLBS; /* * "Pins" a 256MB TLB entry in AS0 for kernel lowmem */ -static void __init -ppc44x_pin_tlb(int slot, unsigned int virt, unsigned int phys) +static void __init ppc44x_pin_tlb(unsigned int virt, unsigned int phys) { - unsigned long attrib = 0; - - __asm__ __volatile__("\ - clrrwi %2,%2,10\n\ - ori %2,%2,%4\n\ - clrrwi %1,%1,10\n\ - li %0,0\n\ - ori %0,%0,%5\n\ - tlbwe %2,%3,%6\n\ - tlbwe %1,%3,%7\n\ - tlbwe %0,%3,%8" + __asm__ __volatile__( + "tlbwe %2,%3,%4\n" + "tlbwe %1,%3,%5\n" + "tlbwe %0,%3,%6\n" : - : "r" (attrib), "r" (phys), "r" (virt), "r" (slot), - "i" (PPC44x_TLB_VALID | PPC44x_TLB_256M), - "i" (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G), + : "r" (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G), + "r" (phys), + "r" (virt | PPC44x_TLB_VALID | PPC44x_TLB_256M), + "r" (tlb_44x_hwater--), /* slot for this TLB entry */ "i" (PPC44x_TLB_PAGEID), "i" (PPC44x_TLB_XLAT), "i" (PPC44x_TLB_ATTRIB)); } -/* - * MMU_init_hw does the chip-specific initialization of the MMU hardware. - */ void __init MMU_init_hw(void) { flush_instruction_cache(); @@ -98,22 +63,13 @@ void __init MMU_init_hw(void) unsigned long __init mmu_mapin_ram(void) { - unsigned int pinned_tlbs = 1; - int i; - - /* Determine number of entries necessary to cover lowmem */ - pinned_tlbs = (unsigned int) - (_ALIGN(total_lowmem, PPC_PIN_SIZE) >> PPC44x_PIN_SHIFT); - - /* Write upper watermark to save location */ - tlb_44x_hwater = PPC44x_LOW_SLOT - pinned_tlbs; + unsigned long addr; - /* If necessary, set additional pinned TLBs */ - if (pinned_tlbs > 1) - for (i = (PPC44x_LOW_SLOT-(pinned_tlbs-1)); i < PPC44x_LOW_SLOT; i++) { - unsigned int phys_addr = (PPC44x_LOW_SLOT-i) * PPC_PIN_SIZE; - ppc44x_pin_tlb(i, phys_addr+PAGE_OFFSET, phys_addr); - } + /* Pin in enough TLBs to cover any lowmem not covered by the + * initial 256M mapping established in head_44x.S */ + for (addr = PPC_PIN_SIZE; addr < total_lowmem; + addr += PPC_PIN_SIZE) + ppc44x_pin_tlb(addr + PAGE_OFFSET, addr); return total_lowmem; } diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h index 9c4538bb04b..2558c34eeda 100644 --- a/arch/powerpc/mm/mmu_decl.h +++ b/arch/powerpc/mm/mmu_decl.h @@ -40,7 +40,8 @@ extern int __map_without_bats; extern unsigned long ioremap_base; extern unsigned int rtas_data, rtas_size; -extern PTE *Hash, *Hash_end; +struct _PTE; +extern struct _PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern unsigned int num_tlbcam_entries; diff --git a/include/asm-powerpc/mmu-44x.h b/include/asm-powerpc/mmu-44x.h new file mode 100644 index 00000000000..7bbc37e27d3 --- /dev/null +++ b/include/asm-powerpc/mmu-44x.h @@ -0,0 +1,72 @@ +#ifndef _ASM_POWERPC_MMU_44X_H_ +#define _ASM_POWERPC_MMU_44X_H_ +/* + * PPC440 support + */ + +#define PPC44x_MMUCR_TID 0x000000ff +#define PPC44x_MMUCR_STS 0x00010000 + +#define PPC44x_TLB_PAGEID 0 +#define PPC44x_TLB_XLAT 1 +#define PPC44x_TLB_ATTRIB 2 + +/* Page identification fields */ +#define PPC44x_TLB_EPN_MASK 0xfffffc00 /* Effective Page Number */ +#define PPC44x_TLB_VALID 0x00000200 /* Valid flag */ +#define PPC44x_TLB_TS 0x00000100 /* Translation address space */ +#define PPC44x_TLB_1K 0x00000000 /* Page sizes */ +#define PPC44x_TLB_4K 0x00000010 +#define PPC44x_TLB_16K 0x00000020 +#define PPC44x_TLB_64K 0x00000030 +#define PPC44x_TLB_256K 0x00000040 +#define PPC44x_TLB_1M 0x00000050 +#define PPC44x_TLB_16M 0x00000070 +#define PPC44x_TLB_256M 0x00000090 + +/* Translation fields */ +#define PPC44x_TLB_RPN_MASK 0xfffffc00 /* Real Page Number */ +#define PPC44x_TLB_ERPN_MASK 0x0000000f + +/* Storage attribute and access control fields */ +#define PPC44x_TLB_ATTR_MASK 0x0000ff80 +#define PPC44x_TLB_U0 0x00008000 /* User 0 */ +#define PPC44x_TLB_U1 0x00004000 /* User 1 */ +#define PPC44x_TLB_U2 0x00002000 /* User 2 */ +#define PPC44x_TLB_U3 0x00001000 /* User 3 */ +#define PPC44x_TLB_W 0x00000800 /* Caching is write-through */ +#define PPC44x_TLB_I 0x00000400 /* Caching is inhibited */ +#define PPC44x_TLB_M 0x00000200 /* Memory is coherent */ +#define PPC44x_TLB_G 0x00000100 /* Memory is guarded */ +#define PPC44x_TLB_E 0x00000080 /* Memory is guarded */ + +#define PPC44x_TLB_PERM_MASK 0x0000003f +#define PPC44x_TLB_UX 0x00000020 /* User execution */ +#define PPC44x_TLB_UW 0x00000010 /* User write */ +#define PPC44x_TLB_UR 0x00000008 /* User read */ +#define PPC44x_TLB_SX 0x00000004 /* Super execution */ +#define PPC44x_TLB_SW 0x00000002 /* Super write */ +#define PPC44x_TLB_SR 0x00000001 /* Super read */ + +/* Number of TLB entries */ +#define PPC44x_TLB_SIZE 64 + +#ifndef __ASSEMBLY__ + +typedef unsigned long long phys_addr_t; + +extern phys_addr_t fixup_bigphys_addr(phys_addr_t, phys_addr_t); + +typedef struct { + unsigned long id; + unsigned long vdso_base; +} mm_context_t; + +#endif /* !__ASSEMBLY__ */ + +#define PPC44x_EARLY_TLBS 1 + +/* Size of the TLBs used for pinning in lowmem */ +#define PPC_PIN_SIZE (1 << 28) /* 256M */ + +#endif /* _ASM_POWERPC_MMU_44X_H_ */ diff --git a/include/asm-powerpc/mmu.h b/include/asm-powerpc/mmu.h index 06b3e6d336c..fe510fff890 100644 --- a/include/asm-powerpc/mmu.h +++ b/include/asm-powerpc/mmu.h @@ -5,9 +5,12 @@ #ifdef CONFIG_PPC64 /* 64-bit classic hash table MMU */ # include +#elif defined(CONFIG_44x) +/* 44x-style software loaded TLB */ +# include #else -/* 32-bit. FIXME: split up the 32-bit MMU types, and revise for - * arch/powerpc */ +/* Other 32-bit. FIXME: split up the other 32-bit MMU types, and + * revise for arch/powerpc */ # include #endif -- cgit v1.2.3 From 69d48b409cac747cc0707b05b769e38488a6ad35 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 30 Apr 2007 15:37:06 +1000 Subject: [POWERPC] Fix STRICT_MM_TYPECHECKS Since we don't have it active by default, the STRICT_MM_TYPECHECKS option has bitrotted again. This patch fixes a couple of simple build fixes if the option is selected. First, pud_t mustn't be defined in page.h on 32-bit systems, because it conflicts with the version in the generic pud-folding code. Second, pci_32.c is missing a __pgprot() wrapper call. Third, a couple of PS3 files use constants of type pgprot_t when they need the raw values, we add pgprot_val() calls to fix this. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/pci_32.c | 2 +- arch/powerpc/platforms/ps3/htab.c | 3 ++- arch/powerpc/platforms/ps3/spu.c | 2 +- include/asm-powerpc/page.h | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index f022862de34..e66064b5093 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c @@ -1658,7 +1658,7 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, int i; if (page_is_ram(pfn)) - return prot; + return __pgprot(prot); prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index ea60c451cf8..a1409e450c7 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -273,7 +273,8 @@ void __init ps3_map_htab(void) result = lv1_map_htab(0, &htab_addr); - htab = (hpte_t *)__ioremap(htab_addr, htab_size, PAGE_READONLY_X); + htab = (hpte_t *)__ioremap(htab_addr, htab_size, + pgprot_val(PAGE_READONLY_X)); DBG("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__, htab_addr, (unsigned long)htab); diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index a397e4e17c1..af8efe3c459 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -184,7 +184,7 @@ static int __init setup_areas(struct spu *spu) spu_pdata(spu)->shadow = __ioremap( spu_pdata(spu)->shadow_addr, sizeof(struct spe_shadow), - PAGE_READONLY | _PAGE_NO_CACHE | _PAGE_GUARDED); + pgprot_val(PAGE_READONLY) | _PAGE_NO_CACHE | _PAGE_GUARDED); if (!spu_pdata(spu)->shadow) { pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__); goto fail_ioremap; diff --git a/include/asm-powerpc/page.h b/include/asm-powerpc/page.h index b4d38b0b15f..f37bff0ee88 100644 --- a/include/asm-powerpc/page.h +++ b/include/asm-powerpc/page.h @@ -126,7 +126,7 @@ typedef struct { unsigned long pmd; } pmd_t; #define __pmd(x) ((pmd_t) { (x) }) /* PUD level exusts only on 4k pages */ -#ifndef CONFIG_PPC_64K_PAGES +#if defined(CONFIG_PPC64) && !defined(CONFIG_PPC_64K_PAGES) typedef struct { unsigned long pud; } pud_t; #define pud_val(x) ((x).pud) #define __pud(x) ((pud_t) { (x) }) -- cgit v1.2.3 From f88df14b1f15cdeffa060580a40c1ce3e13bb79e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 30 Apr 2007 16:30:56 +1000 Subject: [POWERPC] Remove arch/powerpc's dependence on asm-ppc/pg{alloc,table}.h Currently, all 32-bit powerpc platforms use asm-ppc/pgtable.h and asm-ppc/pgalloc.h, even when otherwise compiled with ARCH=powerpc. Those asm-ppc files are a fairly nasty tangle of #ifdefs including a bunch of things which shouldn't be necessary any more in arch/powerpc. Cleaning up that mess is going to take a while, but this patch is a first step. It separates the asm-powerpc/pg{alloc,table}.h into 64 bit and 32 bit versions in asm-powerpc, which the basic .h files in asm-powerpc select based on config. We make a few tiny tweaks to the innards of the files along the way, making the outermost ifdefs (double-inclusion protection and __KERNEL__) a little cleaner, and #including asm-generic/pgtable.h from the top-level asm-powerpc/pgtable.h (since both the old 32-bit and 64-bit versions ended with such an #include). Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- include/asm-powerpc/pgalloc-32.h | 41 ++ include/asm-powerpc/pgalloc-64.h | 152 +++++++ include/asm-powerpc/pgalloc.h | 154 +------ include/asm-powerpc/pgtable-4k.h | 3 + include/asm-powerpc/pgtable-64k.h | 5 - include/asm-powerpc/pgtable-ppc32.h | 838 ++++++++++++++++++++++++++++++++++++ include/asm-powerpc/pgtable-ppc64.h | 492 +++++++++++++++++++++ include/asm-powerpc/pgtable.h | 493 +-------------------- 8 files changed, 1532 insertions(+), 646 deletions(-) create mode 100644 include/asm-powerpc/pgalloc-32.h create mode 100644 include/asm-powerpc/pgalloc-64.h create mode 100644 include/asm-powerpc/pgtable-ppc32.h create mode 100644 include/asm-powerpc/pgtable-ppc64.h (limited to 'include') diff --git a/include/asm-powerpc/pgalloc-32.h b/include/asm-powerpc/pgalloc-32.h new file mode 100644 index 00000000000..235aef283ed --- /dev/null +++ b/include/asm-powerpc/pgalloc-32.h @@ -0,0 +1,41 @@ +#ifndef _ASM_POWERPC_PGALLOC_32_H +#define _ASM_POWERPC_PGALLOC_32_H + +#include + +extern void __bad_pte(pmd_t *pmd); + +extern pgd_t *pgd_alloc(struct mm_struct *mm); +extern void pgd_free(pgd_t *pgd); + +/* + * We don't have any real pmd's, and this code never triggers because + * the pgd will always be present.. + */ +#define pmd_alloc_one(mm,address) ({ BUG(); ((pmd_t *)2); }) +#define pmd_free(x) do { } while (0) +#define __pmd_free_tlb(tlb,x) do { } while (0) +#define pgd_populate(mm, pmd, pte) BUG() + +#ifndef CONFIG_BOOKE +#define pmd_populate_kernel(mm, pmd, pte) \ + (pmd_val(*(pmd)) = __pa(pte) | _PMD_PRESENT) +#define pmd_populate(mm, pmd, pte) \ + (pmd_val(*(pmd)) = (page_to_pfn(pte) << PAGE_SHIFT) | _PMD_PRESENT) +#else +#define pmd_populate_kernel(mm, pmd, pte) \ + (pmd_val(*(pmd)) = (unsigned long)pte | _PMD_PRESENT) +#define pmd_populate(mm, pmd, pte) \ + (pmd_val(*(pmd)) = (unsigned long)lowmem_page_address(pte) | _PMD_PRESENT) +#endif + +extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr); +extern struct page *pte_alloc_one(struct mm_struct *mm, unsigned long addr); +extern void pte_free_kernel(pte_t *pte); +extern void pte_free(struct page *pte); + +#define __pte_free_tlb(tlb, pte) pte_free((pte)) + +#define check_pgt_cache() do { } while (0) + +#endif /* _ASM_POWERPC_PGALLOC_32_H */ diff --git a/include/asm-powerpc/pgalloc-64.h b/include/asm-powerpc/pgalloc-64.h new file mode 100644 index 00000000000..30b50cf56e2 --- /dev/null +++ b/include/asm-powerpc/pgalloc-64.h @@ -0,0 +1,152 @@ +#ifndef _ASM_POWERPC_PGALLOC_64_H +#define _ASM_POWERPC_PGALLOC_64_H +/* + * 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 + +extern struct kmem_cache *pgtable_cache[]; + +#ifdef CONFIG_PPC_64K_PAGES +#define PTE_CACHE_NUM 0 +#define PMD_CACHE_NUM 1 +#define PGD_CACHE_NUM 2 +#define HUGEPTE_CACHE_NUM 3 +#else +#define PTE_CACHE_NUM 0 +#define PMD_CACHE_NUM 1 +#define PUD_CACHE_NUM 1 +#define PGD_CACHE_NUM 0 +#define HUGEPTE_CACHE_NUM 2 +#endif + +static inline pgd_t *pgd_alloc(struct mm_struct *mm) +{ + return kmem_cache_alloc(pgtable_cache[PGD_CACHE_NUM], GFP_KERNEL); +} + +static inline void pgd_free(pgd_t *pgd) +{ + kmem_cache_free(pgtable_cache[PGD_CACHE_NUM], pgd); +} + +#ifndef CONFIG_PPC_64K_PAGES + +#define pgd_populate(MM, PGD, PUD) pgd_set(PGD, PUD) + +static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) +{ + return kmem_cache_alloc(pgtable_cache[PUD_CACHE_NUM], + GFP_KERNEL|__GFP_REPEAT); +} + +static inline void pud_free(pud_t *pud) +{ + kmem_cache_free(pgtable_cache[PUD_CACHE_NUM], pud); +} + +static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) +{ + pud_set(pud, (unsigned long)pmd); +} + +#define pmd_populate(mm, pmd, pte_page) \ + pmd_populate_kernel(mm, pmd, page_address(pte_page)) +#define pmd_populate_kernel(mm, pmd, pte) pmd_set(pmd, (unsigned long)(pte)) + + +#else /* CONFIG_PPC_64K_PAGES */ + +#define pud_populate(mm, pud, pmd) pud_set(pud, (unsigned long)pmd) + +static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, + pte_t *pte) +{ + pmd_set(pmd, (unsigned long)pte); +} + +#define pmd_populate(mm, pmd, pte_page) \ + pmd_populate_kernel(mm, pmd, page_address(pte_page)) + +#endif /* CONFIG_PPC_64K_PAGES */ + +static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) +{ + return kmem_cache_alloc(pgtable_cache[PMD_CACHE_NUM], + GFP_KERNEL|__GFP_REPEAT); +} + +static inline void pmd_free(pmd_t *pmd) +{ + kmem_cache_free(pgtable_cache[PMD_CACHE_NUM], pmd); +} + +static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, + unsigned long address) +{ + return kmem_cache_alloc(pgtable_cache[PTE_CACHE_NUM], + GFP_KERNEL|__GFP_REPEAT); +} + +static inline struct page *pte_alloc_one(struct mm_struct *mm, + unsigned long address) +{ + return virt_to_page(pte_alloc_one_kernel(mm, address)); +} + +static inline void pte_free_kernel(pte_t *pte) +{ + kmem_cache_free(pgtable_cache[PTE_CACHE_NUM], pte); +} + +static inline void pte_free(struct page *ptepage) +{ + pte_free_kernel(page_address(ptepage)); +} + +#define PGF_CACHENUM_MASK 0x3 + +typedef struct pgtable_free { + unsigned long val; +} pgtable_free_t; + +static inline pgtable_free_t pgtable_free_cache(void *p, int cachenum, + unsigned long mask) +{ + BUG_ON(cachenum > PGF_CACHENUM_MASK); + + return (pgtable_free_t){.val = ((unsigned long) p & ~mask) | cachenum}; +} + +static inline void pgtable_free(pgtable_free_t pgf) +{ + void *p = (void *)(pgf.val & ~PGF_CACHENUM_MASK); + int cachenum = pgf.val & PGF_CACHENUM_MASK; + + kmem_cache_free(pgtable_cache[cachenum], p); +} + +extern void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf); + +#define __pte_free_tlb(tlb, ptepage) \ + pgtable_free_tlb(tlb, pgtable_free_cache(page_address(ptepage), \ + PTE_CACHE_NUM, PTE_TABLE_SIZE-1)) +#define __pmd_free_tlb(tlb, pmd) \ + pgtable_free_tlb(tlb, pgtable_free_cache(pmd, \ + PMD_CACHE_NUM, PMD_TABLE_SIZE-1)) +#ifndef CONFIG_PPC_64K_PAGES +#define __pud_free_tlb(tlb, pud) \ + pgtable_free_tlb(tlb, pgtable_free_cache(pud, \ + PUD_CACHE_NUM, PUD_TABLE_SIZE-1)) +#endif /* CONFIG_PPC_64K_PAGES */ + +#define check_pgt_cache() do { } while (0) + +#endif /* _ASM_POWERPC_PGALLOC_64_H */ diff --git a/include/asm-powerpc/pgalloc.h b/include/asm-powerpc/pgalloc.h index b0830db68f8..b4505ed0f0f 100644 --- a/include/asm-powerpc/pgalloc.h +++ b/include/asm-powerpc/pgalloc.h @@ -2,159 +2,11 @@ #define _ASM_POWERPC_PGALLOC_H #ifdef __KERNEL__ -#ifndef CONFIG_PPC64 -#include +#ifdef CONFIG_PPC64 +#include #else - -#include -#include -#include -#include - -extern struct kmem_cache *pgtable_cache[]; - -#ifdef CONFIG_PPC_64K_PAGES -#define PTE_CACHE_NUM 0 -#define PMD_CACHE_NUM 1 -#define PGD_CACHE_NUM 2 -#define HUGEPTE_CACHE_NUM 3 -#else -#define PTE_CACHE_NUM 0 -#define PMD_CACHE_NUM 1 -#define PUD_CACHE_NUM 1 -#define PGD_CACHE_NUM 0 -#define HUGEPTE_CACHE_NUM 2 +#include #endif -/* - * 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. - */ - -static inline pgd_t *pgd_alloc(struct mm_struct *mm) -{ - return kmem_cache_alloc(pgtable_cache[PGD_CACHE_NUM], GFP_KERNEL); -} - -static inline void pgd_free(pgd_t *pgd) -{ - kmem_cache_free(pgtable_cache[PGD_CACHE_NUM], pgd); -} - -#ifndef CONFIG_PPC_64K_PAGES - -#define pgd_populate(MM, PGD, PUD) pgd_set(PGD, PUD) - -static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return kmem_cache_alloc(pgtable_cache[PUD_CACHE_NUM], - GFP_KERNEL|__GFP_REPEAT); -} - -static inline void pud_free(pud_t *pud) -{ - kmem_cache_free(pgtable_cache[PUD_CACHE_NUM], pud); -} - -static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) -{ - pud_set(pud, (unsigned long)pmd); -} - -#define pmd_populate(mm, pmd, pte_page) \ - pmd_populate_kernel(mm, pmd, page_address(pte_page)) -#define pmd_populate_kernel(mm, pmd, pte) pmd_set(pmd, (unsigned long)(pte)) - - -#else /* CONFIG_PPC_64K_PAGES */ - -#define pud_populate(mm, pud, pmd) pud_set(pud, (unsigned long)pmd) - -static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, - pte_t *pte) -{ - pmd_set(pmd, (unsigned long)pte); -} - -#define pmd_populate(mm, pmd, pte_page) \ - pmd_populate_kernel(mm, pmd, page_address(pte_page)) - -#endif /* CONFIG_PPC_64K_PAGES */ - -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return kmem_cache_alloc(pgtable_cache[PMD_CACHE_NUM], - GFP_KERNEL|__GFP_REPEAT); -} - -static inline void pmd_free(pmd_t *pmd) -{ - kmem_cache_free(pgtable_cache[PMD_CACHE_NUM], pmd); -} - -static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, - unsigned long address) -{ - return kmem_cache_alloc(pgtable_cache[PTE_CACHE_NUM], - GFP_KERNEL|__GFP_REPEAT); -} - -static inline struct page *pte_alloc_one(struct mm_struct *mm, - unsigned long address) -{ - return virt_to_page(pte_alloc_one_kernel(mm, address)); -} - -static inline void pte_free_kernel(pte_t *pte) -{ - kmem_cache_free(pgtable_cache[PTE_CACHE_NUM], pte); -} - -static inline void pte_free(struct page *ptepage) -{ - pte_free_kernel(page_address(ptepage)); -} - -#define PGF_CACHENUM_MASK 0x3 - -typedef struct pgtable_free { - unsigned long val; -} pgtable_free_t; - -static inline pgtable_free_t pgtable_free_cache(void *p, int cachenum, - unsigned long mask) -{ - BUG_ON(cachenum > PGF_CACHENUM_MASK); - - return (pgtable_free_t){.val = ((unsigned long) p & ~mask) | cachenum}; -} - -static inline void pgtable_free(pgtable_free_t pgf) -{ - void *p = (void *)(pgf.val & ~PGF_CACHENUM_MASK); - int cachenum = pgf.val & PGF_CACHENUM_MASK; - - kmem_cache_free(pgtable_cache[cachenum], p); -} - -extern void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf); - -#define __pte_free_tlb(tlb, ptepage) \ - pgtable_free_tlb(tlb, pgtable_free_cache(page_address(ptepage), \ - PTE_CACHE_NUM, PTE_TABLE_SIZE-1)) -#define __pmd_free_tlb(tlb, pmd) \ - pgtable_free_tlb(tlb, pgtable_free_cache(pmd, \ - PMD_CACHE_NUM, PMD_TABLE_SIZE-1)) -#ifndef CONFIG_PPC_64K_PAGES -#define __pud_free_tlb(tlb, pud) \ - pgtable_free_tlb(tlb, pgtable_free_cache(pud, \ - PUD_CACHE_NUM, PUD_TABLE_SIZE-1)) -#endif /* CONFIG_PPC_64K_PAGES */ - -#define check_pgt_cache() do { } while (0) - -#endif /* CONFIG_PPC64 */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PGALLOC_H */ diff --git a/include/asm-powerpc/pgtable-4k.h b/include/asm-powerpc/pgtable-4k.h index a28fa8bc01d..1744d6ac12a 100644 --- a/include/asm-powerpc/pgtable-4k.h +++ b/include/asm-powerpc/pgtable-4k.h @@ -1,3 +1,5 @@ +#ifndef _ASM_POWERPC_PGTABLE_4K_H +#define _ASM_POWERPC_PGTABLE_4K_H /* * Entries per page directory level. The PTE level must use a 64b record * for each page table entry. The PMD and PGD level use a 32b record for @@ -100,3 +102,4 @@ #define remap_4k_pfn(vma, addr, pfn, prot) \ remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, (prot)) +#endif /* _ASM_POWERPC_PGTABLE_4K_H */ diff --git a/include/asm-powerpc/pgtable-64k.h b/include/asm-powerpc/pgtable-64k.h index 5e84f070eaf..16ef4978520 100644 --- a/include/asm-powerpc/pgtable-64k.h +++ b/include/asm-powerpc/pgtable-64k.h @@ -1,6 +1,5 @@ #ifndef _ASM_POWERPC_PGTABLE_64K_H #define _ASM_POWERPC_PGTABLE_64K_H -#ifdef __KERNEL__ #include @@ -65,8 +64,6 @@ /* Bits to mask out from a PGD/PUD to get to the PMD page */ #define PUD_MASKED_BITS 0x1ff -#ifndef __ASSEMBLY__ - /* Manipulate "rpte" values */ #define __real_pte(e,p) ((real_pte_t) { \ (e), pte_val(*((p) + PTRS_PER_PTE)) }) @@ -98,6 +95,4 @@ remap_pfn_range((vma), (addr), (pfn), PAGE_SIZE, \ __pgprot(pgprot_val((prot)) | _PAGE_4K_PFN)) -#endif /* __ASSEMBLY__ */ -#endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PGTABLE_64K_H */ diff --git a/include/asm-powerpc/pgtable-ppc32.h b/include/asm-powerpc/pgtable-ppc32.h new file mode 100644 index 00000000000..80c75474c65 --- /dev/null +++ b/include/asm-powerpc/pgtable-ppc32.h @@ -0,0 +1,838 @@ +#ifndef _ASM_POWERPC_PGTABLE_PPC32_H +#define _ASM_POWERPC_PGTABLE_PPC32_H + +#include + +#ifndef __ASSEMBLY__ +#include +#include +#include /* For TASK_SIZE */ +#include +#include +#include /* For sub-arch specific PPC_PIN_SIZE */ +struct mm_struct; + +extern unsigned long va_to_phys(unsigned long address); +extern pte_t *va_to_pte(unsigned long address); +extern unsigned long ioremap_bot, ioremap_base; +#endif /* __ASSEMBLY__ */ + +/* + * The PowerPC MMU uses a hash table containing PTEs, together with + * a set of 16 segment registers (on 32-bit implementations), to define + * the virtual to physical address mapping. + * + * We use the hash table as an extended TLB, i.e. a cache of currently + * active mappings. We maintain a two-level page table tree, much + * like that used by the i386, for the sake of the Linux memory + * management code. Low-level assembler code in hashtable.S + * (procedure hash_page) is responsible for extracting ptes from the + * tree and putting them into the hash table when necessary, and + * updating the accessed and modified bits in the page table tree. + */ + +/* + * The PowerPC MPC8xx uses a TLB with hardware assisted, software tablewalk. + * We also use the two level tables, but we can put the real bits in them + * needed for the TLB and tablewalk. These definitions require Mx_CTR.PPM = 0, + * Mx_CTR.PPCS = 0, and MD_CTR.TWAM = 1. The level 2 descriptor has + * additional page protection (when Mx_CTR.PPCS = 1) that allows TLB hit + * based upon user/super access. The TLB does not have accessed nor write + * protect. We assume that if the TLB get loaded with an entry it is + * accessed, and overload the changed bit for write protect. We use + * two bits in the software pte that are supposed to be set to zero in + * the TLB entry (24 and 25) for these indicators. Although the level 1 + * descriptor contains the guarded and writethrough/copyback bits, we can + * set these at the page level since they get copied from the Mx_TWC + * register when the TLB entry is loaded. We will use bit 27 for guard, since + * that is where it exists in the MD_TWC, and bit 26 for writethrough. + * These will get masked from the level 2 descriptor at TLB load time, and + * copied to the MD_TWC before it gets loaded. + * Large page sizes added. We currently support two sizes, 4K and 8M. + * This also allows a TLB hander optimization because we can directly + * load the PMD into MD_TWC. The 8M pages are only used for kernel + * mapping of well known areas. The PMD (PGD) entries contain control + * flags in addition to the address, so care must be taken that the + * software no longer assumes these are only pointers. + */ + +/* + * At present, all PowerPC 400-class processors share a similar TLB + * architecture. The instruction and data sides share a unified, + * 64-entry, fully-associative TLB which is maintained totally under + * software control. In addition, the instruction side has a + * hardware-managed, 4-entry, fully-associative TLB which serves as a + * first level to the shared TLB. These two TLBs are known as the UTLB + * and ITLB, respectively (see "mmu.h" for definitions). + */ + +/* + * The normal case is that PTEs are 32-bits and we have a 1-page + * 1024-entry pgdir pointing to 1-page 1024-entry PTE pages. -- paulus + * + * For any >32-bit physical address platform, we can use the following + * two level page table layout where the pgdir is 8KB and the MS 13 bits + * are an index to the second level table. The combined pgdir/pmd first + * level has 2048 entries and the second level has 512 64-bit PTE entries. + * -Matt + */ +/* PMD_SHIFT determines the size of the area mapped by the PTE pages */ +#define PMD_SHIFT (PAGE_SHIFT + PTE_SHIFT) +#define PMD_SIZE (1UL << PMD_SHIFT) +#define PMD_MASK (~(PMD_SIZE-1)) + +/* PGDIR_SHIFT determines what a top-level page table entry can map */ +#define PGDIR_SHIFT PMD_SHIFT +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) + +/* + * entries per page directory level: our page-table tree is two-level, so + * we don't really have any PMD directory. + */ +#define PTRS_PER_PTE (1 << PTE_SHIFT) +#define PTRS_PER_PMD 1 +#define PTRS_PER_PGD (1 << (32 - PGDIR_SHIFT)) + +#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) +#define FIRST_USER_ADDRESS 0 + +#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) +#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte "PTE_FMT".\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + +/* + * Just any arbitrary offset to the start of the vmalloc VM area: the + * current 64MB value just means that there will be a 64MB "hole" after the + * physical memory until the kernel virtual memory starts. That means that + * any out-of-bounds memory accesses will hopefully be caught. + * The vmalloc() routines leaves a hole of 4kB between each vmalloced + * area for the same reason. ;) + * + * We no longer map larger than phys RAM with the BATs so we don't have + * to worry about the VMALLOC_OFFSET causing problems. We do have to worry + * about clashes between our early calls to ioremap() that start growing down + * from ioremap_base being run into the VM area allocations (growing upwards + * from VMALLOC_START). For this reason we have ioremap_bot to check when + * we actually run into our mappings setup in the early boot with the VM + * system. This really does become a problem for machines with good amounts + * of RAM. -- Cort + */ +#define VMALLOC_OFFSET (0x1000000) /* 16M */ +#ifdef PPC_PIN_SIZE +#define VMALLOC_START (((_ALIGN((long)high_memory, PPC_PIN_SIZE) + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))) +#else +#define VMALLOC_START ((((long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))) +#endif +#define VMALLOC_END ioremap_bot + +/* + * Bits in a linux-style PTE. These match the bits in the + * (hardware-defined) PowerPC PTE as closely as possible. + */ + +#if defined(CONFIG_40x) + +/* There are several potential gotchas here. The 40x hardware TLBLO + field looks like this: + + 0 1 2 3 4 ... 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + RPN..................... 0 0 EX WR ZSEL....... W I M G + + Where possible we make the Linux PTE bits match up with this + + - bits 20 and 21 must be cleared, because we use 4k pages (40x can + support down to 1k pages), this is done in the TLBMiss exception + handler. + - We use only zones 0 (for kernel pages) and 1 (for user pages) + of the 16 available. Bit 24-26 of the TLB are cleared in the TLB + miss handler. Bit 27 is PAGE_USER, thus selecting the correct + zone. + - PRESENT *must* be in the bottom two bits because swap cache + entries use the top 30 bits. Because 40x doesn't support SMP + anyway, M is irrelevant so we borrow it for PAGE_PRESENT. Bit 30 + is cleared in the TLB miss handler before the TLB entry is loaded. + - All other bits of the PTE are loaded into TLBLO without + modification, leaving us only the bits 20, 21, 24, 25, 26, 30 for + software PTE bits. We actually use use bits 21, 24, 25, and + 30 respectively for the software bits: ACCESSED, DIRTY, RW, and + PRESENT. +*/ + +/* Definitions for 40x embedded chips. */ +#define _PAGE_GUARDED 0x001 /* G: page is guarded from prefetch */ +#define _PAGE_FILE 0x001 /* when !present: nonlinear file mapping */ +#define _PAGE_PRESENT 0x002 /* software: PTE contains a translation */ +#define _PAGE_NO_CACHE 0x004 /* I: caching is inhibited */ +#define _PAGE_WRITETHRU 0x008 /* W: caching is write-through */ +#define _PAGE_USER 0x010 /* matches one of the zone permission bits */ +#define _PAGE_RW 0x040 /* software: Writes permitted */ +#define _PAGE_DIRTY 0x080 /* software: dirty page */ +#define _PAGE_HWWRITE 0x100 /* hardware: Dirty & RW, set in exception */ +#define _PAGE_HWEXEC 0x200 /* hardware: EX permission */ +#define _PAGE_ACCESSED 0x400 /* software: R: page referenced */ + +#define _PMD_PRESENT 0x400 /* PMD points to page of PTEs */ +#define _PMD_BAD 0x802 +#define _PMD_SIZE 0x0e0 /* size field, != 0 for large-page PMD entry */ +#define _PMD_SIZE_4M 0x0c0 +#define _PMD_SIZE_16M 0x0e0 +#define PMD_PAGE_SIZE(pmdval) (1024 << (((pmdval) & _PMD_SIZE) >> 4)) + +#elif defined(CONFIG_44x) +/* + * Definitions for PPC440 + * + * Because of the 3 word TLB entries to support 36-bit addressing, + * the attribute are difficult to map in such a fashion that they + * are easily loaded during exception processing. I decided to + * organize the entry so the ERPN is the only portion in the + * upper word of the PTE and the attribute bits below are packed + * in as sensibly as they can be in the area below a 4KB page size + * oriented RPN. This at least makes it easy to load the RPN and + * ERPN fields in the TLB. -Matt + * + * Note that these bits preclude future use of a page size + * less than 4KB. + * + * + * PPC 440 core has following TLB attribute fields; + * + * TLB1: + * 0 1 2 3 4 ... 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + * RPN................................. - - - - - - ERPN....... + * + * TLB2: + * 0 1 2 3 4 ... 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + * - - - - - - U0 U1 U2 U3 W I M G E - UX UW UR SX SW SR + * + * There are some constrains and options, to decide mapping software bits + * into TLB entry. + * + * - PRESENT *must* be in the bottom three bits because swap cache + * entries use the top 29 bits for TLB2. + * + * - FILE *must* be in the bottom three bits because swap cache + * entries use the top 29 bits for TLB2. + * + * - CACHE COHERENT bit (M) has no effect on PPC440 core, because it + * doesn't support SMP. So we can use this as software bit, like + * DIRTY. + * + * With the PPC 44x Linux implementation, the 0-11th LSBs of the PTE are used + * for memory protection related functions (see PTE structure in + * include/asm-ppc/mmu.h). The _PAGE_XXX definitions in this file map to the + * above bits. Note that the bit values are CPU specific, not architecture + * specific. + * + * The kernel PTE entry holds an arch-dependent swp_entry structure under + * certain situations. In other words, in such situations some portion of + * the PTE bits are used as a swp_entry. In the PPC implementation, the + * 3-24th LSB are shared with swp_entry, however the 0-2nd three LSB still + * hold protection values. That means the three protection bits are + * reserved for both PTE and SWAP entry at the most significant three + * LSBs. + * + * There are three protection bits available for SWAP entry: + * _PAGE_PRESENT + * _PAGE_FILE + * _PAGE_HASHPTE (if HW has) + * + * So those three bits have to be inside of 0-2nd LSB of PTE. + * + */ + +#define _PAGE_PRESENT 0x00000001 /* S: PTE valid */ +#define _PAGE_RW 0x00000002 /* S: Write permission */ +#define _PAGE_FILE 0x00000004 /* S: nonlinear file mapping */ +#define _PAGE_ACCESSED 0x00000008 /* S: Page referenced */ +#define _PAGE_HWWRITE 0x00000010 /* H: Dirty & RW */ +#define _PAGE_HWEXEC 0x00000020 /* H: Execute permission */ +#define _PAGE_USER 0x00000040 /* S: User page */ +#define _PAGE_ENDIAN 0x00000080 /* H: E bit */ +#define _PAGE_GUARDED 0x00000100 /* H: G bit */ +#define _PAGE_DIRTY 0x00000200 /* S: Page dirty */ +#define _PAGE_NO_CACHE 0x00000400 /* H: I bit */ +#define _PAGE_WRITETHRU 0x00000800 /* H: W bit */ + +/* TODO: Add large page lowmem mapping support */ +#define _PMD_PRESENT 0 +#define _PMD_PRESENT_MASK (PAGE_MASK) +#define _PMD_BAD (~PAGE_MASK) + +/* ERPN in a PTE never gets cleared, ignore it */ +#define _PTE_NONE_MASK 0xffffffff00000000ULL + +#elif defined(CONFIG_FSL_BOOKE) +/* + MMU Assist Register 3: + + 32 33 34 35 36 ... 50 51 52 53 54 55 56 57 58 59 60 61 62 63 + RPN...................... 0 0 U0 U1 U2 U3 UX SX UW SW UR SR + + - PRESENT *must* be in the bottom three bits because swap cache + entries use the top 29 bits. + + - FILE *must* be in the bottom three bits because swap cache + entries use the top 29 bits. +*/ + +/* Definitions for FSL Book-E Cores */ +#define _PAGE_PRESENT 0x00001 /* S: PTE contains a translation */ +#define _PAGE_USER 0x00002 /* S: User page (maps to UR) */ +#define _PAGE_FILE 0x00002 /* S: when !present: nonlinear file mapping */ +#define _PAGE_ACCESSED 0x00004 /* S: Page referenced */ +#define _PAGE_HWWRITE 0x00008 /* H: Dirty & RW, set in exception */ +#define _PAGE_RW 0x00010 /* S: Write permission */ +#define _PAGE_HWEXEC 0x00020 /* H: UX permission */ + +#define _PAGE_ENDIAN 0x00040 /* H: E bit */ +#define _PAGE_GUARDED 0x00080 /* H: G bit */ +#define _PAGE_COHERENT 0x00100 /* H: M bit */ +#define _PAGE_NO_CACHE 0x00200 /* H: I bit */ +#define _PAGE_WRITETHRU 0x00400 /* H: W bit */ + +#ifdef CONFIG_PTE_64BIT +#define _PAGE_DIRTY 0x08000 /* S: Page dirty */ + +/* ERPN in a PTE never gets cleared, ignore it */ +#define _PTE_NONE_MASK 0xffffffffffff0000ULL +#else +#define _PAGE_DIRTY 0x00800 /* S: Page dirty */ +#endif + +#define _PMD_PRESENT 0 +#define _PMD_PRESENT_MASK (PAGE_MASK) +#define _PMD_BAD (~PAGE_MASK) + +#elif defined(CONFIG_8xx) +/* Definitions for 8xx embedded chips. */ +#define _PAGE_PRESENT 0x0001 /* Page is valid */ +#define _PAGE_FILE 0x0002 /* when !present: nonlinear file mapping */ +#define _PAGE_NO_CACHE 0x0002 /* I: cache inhibit */ +#define _PAGE_SHARED 0x0004 /* No ASID (context) compare */ + +/* These five software bits must be masked out when the entry is loaded + * into the TLB. + */ +#define _PAGE_EXEC 0x0008 /* software: i-cache coherency required */ +#define _PAGE_GUARDED 0x0010 /* software: guarded access */ +#define _PAGE_DIRTY 0x0020 /* software: page changed */ +#define _PAGE_RW 0x0040 /* software: user write access allowed */ +#define _PAGE_ACCESSED 0x0080 /* software: page referenced */ + +/* Setting any bits in the nibble with the follow two controls will + * require a TLB exception handler change. It is assumed unused bits + * are always zero. + */ +#define _PAGE_HWWRITE 0x0100 /* h/w write enable: never set in Linux PTE */ +#define _PAGE_USER 0x0800 /* One of the PP bits, the other is USER&~RW */ + +#define _PMD_PRESENT 0x0001 +#define _PMD_BAD 0x0ff0 +#define _PMD_PAGE_MASK 0x000c +#define _PMD_PAGE_8M 0x000c + +/* + * The 8xx TLB miss handler allegedly sets _PAGE_ACCESSED in the PTE + * for an address even if _PAGE_PRESENT is not set, as a performance + * optimization. This is a bug if you ever want to use swap unless + * _PAGE_ACCESSED is 2, which it isn't, or unless you have 8xx-specific + * definitions for __swp_entry etc. below, which would be gross. + * -- paulus + */ +#define _PTE_NONE_MASK _PAGE_ACCESSED + +#else /* CONFIG_6xx */ +/* Definitions for 60x, 740/750, etc. */ +#define _PAGE_PRESENT 0x001 /* software: pte contains a translation */ +#define _PAGE_HASHPTE 0x002 /* hash_page has made an HPTE for this pte */ +#define _PAGE_FILE 0x004 /* when !present: nonlinear file mapping */ +#define _PAGE_USER 0x004 /* usermode access allowed */ +#define _PAGE_GUARDED 0x008 /* G: prohibit speculative access */ +#define _PAGE_COHERENT 0x010 /* M: enforce memory coherence (SMP systems) */ +#define _PAGE_NO_CACHE 0x020 /* I: cache inhibit */ +#define _PAGE_WRITETHRU 0x040 /* W: cache write-through */ +#define _PAGE_DIRTY 0x080 /* C: page changed */ +#define _PAGE_ACCESSED 0x100 /* R: page referenced */ +#define _PAGE_EXEC 0x200 /* software: i-cache coherency required */ +#define _PAGE_RW 0x400 /* software: user write access allowed */ + +#define _PTE_NONE_MASK _PAGE_HASHPTE + +#define _PMD_PRESENT 0 +#define _PMD_PRESENT_MASK (PAGE_MASK) +#define _PMD_BAD (~PAGE_MASK) +#endif + +/* + * Some bits are only used on some cpu families... + */ +#ifndef _PAGE_HASHPTE +#define _PAGE_HASHPTE 0 +#endif +#ifndef _PTE_NONE_MASK +#define _PTE_NONE_MASK 0 +#endif +#ifndef _PAGE_SHARED +#define _PAGE_SHARED 0 +#endif +#ifndef _PAGE_HWWRITE +#define _PAGE_HWWRITE 0 +#endif +#ifndef _PAGE_HWEXEC +#define _PAGE_HWEXEC 0 +#endif +#ifndef _PAGE_EXEC +#define _PAGE_EXEC 0 +#endif +#ifndef _PMD_PRESENT_MASK +#define _PMD_PRESENT_MASK _PMD_PRESENT +#endif +#ifndef _PMD_SIZE +#define _PMD_SIZE 0 +#define PMD_PAGE_SIZE(pmd) bad_call_to_PMD_PAGE_SIZE() +#endif + +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY) + +/* + * Note: the _PAGE_COHERENT bit automatically gets set in the hardware + * PTE if CONFIG_SMP is defined (hash_page does this); there is no need + * to have it in the Linux PTE, and in fact the bit could be reused for + * another purpose. -- paulus. + */ + +#ifdef CONFIG_44x +#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_GUARDED) +#else +#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED) +#endif +#define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE) +#define _PAGE_KERNEL (_PAGE_BASE | _PAGE_SHARED | _PAGE_WRENABLE) + +#ifdef CONFIG_PPC_STD_MMU +/* On standard PPC MMU, no user access implies kernel read/write access, + * so to write-protect kernel memory we must turn on user access */ +#define _PAGE_KERNEL_RO (_PAGE_BASE | _PAGE_SHARED | _PAGE_USER) +#else +#define _PAGE_KERNEL_RO (_PAGE_BASE | _PAGE_SHARED) +#endif + +#define _PAGE_IO (_PAGE_KERNEL | _PAGE_NO_CACHE | _PAGE_GUARDED) +#define _PAGE_RAM (_PAGE_KERNEL | _PAGE_HWEXEC) + +#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) || defined(CONFIG_BDI_SWITCH) +/* We want the debuggers to be able to set breakpoints anywhere, so + * don't write protect the kernel text */ +#define _PAGE_RAM_TEXT _PAGE_RAM +#else +#define _PAGE_RAM_TEXT (_PAGE_KERNEL_RO | _PAGE_HWEXEC) +#endif + +#define PAGE_NONE __pgprot(_PAGE_BASE) +#define PAGE_READONLY __pgprot(_PAGE_BASE | _PAGE_USER) +#define PAGE_READONLY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) +#define PAGE_SHARED __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW) +#define PAGE_SHARED_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_EXEC) +#define PAGE_COPY __pgprot(_PAGE_BASE | _PAGE_USER) +#define PAGE_COPY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) + +#define PAGE_KERNEL __pgprot(_PAGE_RAM) +#define PAGE_KERNEL_NOCACHE __pgprot(_PAGE_IO) + +/* + * The PowerPC can only do execute protection on a segment (256MB) basis, + * not on a page basis. So we consider execute permission the same as read. + * Also, write permissions imply read permissions. + * This is the closest we can get.. + */ +#define __P000 PAGE_NONE +#define __P001 PAGE_READONLY_X +#define __P010 PAGE_COPY +#define __P011 PAGE_COPY_X +#define __P100 PAGE_READONLY +#define __P101 PAGE_READONLY_X +#define __P110 PAGE_COPY +#define __P111 PAGE_COPY_X + +#define __S000 PAGE_NONE +#define __S001 PAGE_READONLY_X +#define __S010 PAGE_SHARED +#define __S011 PAGE_SHARED_X +#define __S100 PAGE_READONLY +#define __S101 PAGE_READONLY_X +#define __S110 PAGE_SHARED +#define __S111 PAGE_SHARED_X + +#ifndef __ASSEMBLY__ +/* Make sure we get a link error if PMD_PAGE_SIZE is ever called on a + * kernel without large page PMD support */ +extern unsigned long bad_call_to_PMD_PAGE_SIZE(void); + +/* + * Conversions between PTE values and page frame numbers. + */ + +/* in some case we want to additionaly adjust where the pfn is in the pte to + * allow room for more flags */ +#if defined(CONFIG_FSL_BOOKE) && defined(CONFIG_PTE_64BIT) +#define PFN_SHIFT_OFFSET (PAGE_SHIFT + 8) +#else +#define PFN_SHIFT_OFFSET (PAGE_SHIFT) +#endif + +#define pte_pfn(x) (pte_val(x) >> PFN_SHIFT_OFFSET) +#define pte_page(x) pfn_to_page(pte_pfn(x)) + +#define pfn_pte(pfn, prot) __pte(((pte_basic_t)(pfn) << PFN_SHIFT_OFFSET) |\ + pgprot_val(prot)) +#define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot) + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[1024]; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +#endif /* __ASSEMBLY__ */ + +#define pte_none(pte) ((pte_val(pte) & ~_PTE_NONE_MASK) == 0) +#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) +#define pte_clear(mm,addr,ptep) do { set_pte_at((mm), (addr), (ptep), __pte(0)); } while (0) + +#define pmd_none(pmd) (!pmd_val(pmd)) +#define pmd_bad(pmd) (pmd_val(pmd) & _PMD_BAD) +#define pmd_present(pmd) (pmd_val(pmd) & _PMD_PRESENT_MASK) +#define pmd_clear(pmdp) do { pmd_val(*(pmdp)) = 0; } while (0) + +#ifndef __ASSEMBLY__ +/* + * The "pgd_xxx()" functions here are trivial for a folded two-level + * setup: the pgd is never bad, and a pmd always exists (as it's folded + * into the pgd entry) + */ +static inline int pgd_none(pgd_t pgd) { return 0; } +static inline int pgd_bad(pgd_t pgd) { return 0; } +static inline int pgd_present(pgd_t pgd) { return 1; } +#define pgd_clear(xp) do { } while (0) + +#define pgd_page_vaddr(pgd) \ + ((unsigned long) __va(pgd_val(pgd) & PAGE_MASK)) + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +static inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_USER; } +static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_RW; } +static inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_EXEC; } +static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; } +static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } +static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE; } + +static inline void pte_uncache(pte_t pte) { pte_val(pte) |= _PAGE_NO_CACHE; } +static inline void pte_cache(pte_t pte) { pte_val(pte) &= ~_PAGE_NO_CACHE; } + +static inline pte_t pte_rdprotect(pte_t pte) { + pte_val(pte) &= ~_PAGE_USER; return pte; } +static inline pte_t pte_wrprotect(pte_t pte) { + pte_val(pte) &= ~(_PAGE_RW | _PAGE_HWWRITE); return pte; } +static inline pte_t pte_exprotect(pte_t pte) { + pte_val(pte) &= ~_PAGE_EXEC; return pte; } +static inline pte_t pte_mkclean(pte_t pte) { + pte_val(pte) &= ~(_PAGE_DIRTY | _PAGE_HWWRITE); return pte; } +static inline pte_t pte_mkold(pte_t pte) { + pte_val(pte) &= ~_PAGE_ACCESSED; return pte; } + +static inline pte_t pte_mkread(pte_t pte) { + pte_val(pte) |= _PAGE_USER; return pte; } +static inline pte_t pte_mkexec(pte_t pte) { + pte_val(pte) |= _PAGE_USER | _PAGE_EXEC; return pte; } +static inline pte_t pte_mkwrite(pte_t pte) { + pte_val(pte) |= _PAGE_RW; return pte; } +static inline pte_t pte_mkdirty(pte_t pte) { + pte_val(pte) |= _PAGE_DIRTY; return pte; } +static inline pte_t pte_mkyoung(pte_t pte) { + pte_val(pte) |= _PAGE_ACCESSED; return pte; } + +static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ + pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); + return pte; +} + +/* + * When flushing the tlb entry for a page, we also need to flush the hash + * table entry. flush_hash_pages is assembler (for speed) in hashtable.S. + */ +extern int flush_hash_pages(unsigned context, unsigned long va, + unsigned long pmdval, int count); + +/* Add an HPTE to the hash table */ +extern void add_hash_page(unsigned context, unsigned long va, + unsigned long pmdval); + +/* + * Atomic PTE updates. + * + * pte_update clears and sets bit atomically, and returns + * the old pte value. In the 64-bit PTE case we lock around the + * low PTE word since we expect ALL flag bits to be there + */ +#ifndef CONFIG_PTE_64BIT +static inline unsigned long pte_update(pte_t *p, unsigned long clr, + unsigned long set) +{ + unsigned long old, tmp; + + __asm__ __volatile__("\ +1: lwarx %0,0,%3\n\ + andc %1,%0,%4\n\ + or %1,%1,%5\n" + PPC405_ERR77(0,%3) +" stwcx. %1,0,%3\n\ + bne- 1b" + : "=&r" (old), "=&r" (tmp), "=m" (*p) + : "r" (p), "r" (clr), "r" (set), "m" (*p) + : "cc" ); + return old; +} +#else +static inline unsigned long long pte_update(pte_t *p, unsigned long clr, + unsigned long set) +{ + unsigned long long old; + unsigned long tmp; + + __asm__ __volatile__("\ +1: lwarx %L0,0,%4\n\ + lwzx %0,0,%3\n\ + andc %1,%L0,%5\n\ + or %1,%1,%6\n" + PPC405_ERR77(0,%3) +" stwcx. %1,0,%4\n\ + bne- 1b" + : "=&r" (old), "=&r" (tmp), "=m" (*p) + : "r" (p), "r" ((unsigned long)(p) + 4), "r" (clr), "r" (set), "m" (*p) + : "cc" ); + return old; +} +#endif + +/* + * set_pte stores a linux PTE into the linux page table. + * On machines which use an MMU hash table we avoid changing the + * _PAGE_HASHPTE bit. + */ +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte) +{ +#if _PAGE_HASHPTE != 0 + pte_update(ptep, ~_PAGE_HASHPTE, pte_val(pte) & ~_PAGE_HASHPTE); +#else + *ptep = pte; +#endif +} + +/* + * 2.6 calles this without flushing the TLB entry, this is wrong + * for our hash-based implementation, we fix that up here + */ +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG +static inline int __ptep_test_and_clear_young(unsigned int context, unsigned long addr, pte_t *ptep) +{ + unsigned long old; + old = pte_update(ptep, _PAGE_ACCESSED, 0); +#if _PAGE_HASHPTE != 0 + if (old & _PAGE_HASHPTE) { + unsigned long ptephys = __pa(ptep) & PAGE_MASK; + flush_hash_pages(context, addr, ptephys, 1); + } +#endif + return (old & _PAGE_ACCESSED) != 0; +} +#define ptep_test_and_clear_young(__vma, __addr, __ptep) \ + __ptep_test_and_clear_young((__vma)->vm_mm->context.id, __addr, __ptep) + +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY +static inline int ptep_test_and_clear_dirty(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ + return (pte_update(ptep, (_PAGE_DIRTY | _PAGE_HWWRITE), 0) & _PAGE_DIRTY) != 0; +} + +#define __HAVE_ARCH_PTEP_GET_AND_CLEAR +static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, + pte_t *ptep) +{ + return __pte(pte_update(ptep, ~_PAGE_HASHPTE, 0)); +} + +#define __HAVE_ARCH_PTEP_SET_WRPROTECT +static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, + pte_t *ptep) +{ + pte_update(ptep, (_PAGE_RW | _PAGE_HWWRITE), 0); +} + +#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS +static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry, int dirty) +{ + unsigned long bits = pte_val(entry) & + (_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW); + pte_update(ptep, 0, bits); +} + +#define ptep_set_access_flags(__vma, __address, __ptep, __entry, __dirty) \ + do { \ + __ptep_set_access_flags(__ptep, __entry, __dirty); \ + flush_tlb_page_nohash(__vma, __address); \ + } while(0) + +/* + * Macro to mark a page protection value as "uncacheable". + */ +#define pgprot_noncached(prot) (__pgprot(pgprot_val(prot) | _PAGE_NO_CACHE | _PAGE_GUARDED)) + +struct file; +extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, + unsigned long size, pgprot_t vma_prot); +#define __HAVE_PHYS_MEM_ACCESS_PROT + +#define __HAVE_ARCH_PTE_SAME +#define pte_same(A,B) (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HASHPTE) == 0) + +/* + * Note that on Book E processors, the pmd contains the kernel virtual + * (lowmem) address of the pte page. The physical address is less useful + * because everything runs with translation enabled (even the TLB miss + * handler). On everything else the pmd contains the physical address + * of the pte page. -- paulus + */ +#ifndef CONFIG_BOOKE +#define pmd_page_vaddr(pmd) \ + ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) +#define pmd_page(pmd) \ + (mem_map + (pmd_val(pmd) >> PAGE_SHIFT)) +#else +#define pmd_page_vaddr(pmd) \ + ((unsigned long) (pmd_val(pmd) & PAGE_MASK)) +#define pmd_page(pmd) \ + (mem_map + (__pa(pmd_val(pmd)) >> PAGE_SHIFT)) +#endif + +/* to find an entry in a kernel page-table-directory */ +#define pgd_offset_k(address) pgd_offset(&init_mm, address) + +/* to find an entry in a page-table-directory */ +#define pgd_index(address) ((address) >> PGDIR_SHIFT) +#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address)) + +/* Find an entry in the second-level page table.. */ +static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address) +{ + return (pmd_t *) dir; +} + +/* Find an entry in the third-level page table.. */ +#define pte_index(address) \ + (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#define pte_offset_kernel(dir, addr) \ + ((pte_t *) pmd_page_vaddr(*(dir)) + pte_index(addr)) +#define pte_offset_map(dir, addr) \ + ((pte_t *) kmap_atomic(pmd_page(*(dir)), KM_PTE0) + pte_index(addr)) +#define pte_offset_map_nested(dir, addr) \ + ((pte_t *) kmap_atomic(pmd_page(*(dir)), KM_PTE1) + pte_index(addr)) + +#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0) +#define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1) + +extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; + +extern void paging_init(void); + +/* + * Encode and decode a swap entry. + * Note that the bits we use in a PTE for representing a swap entry + * must not include the _PAGE_PRESENT bit, the _PAGE_FILE bit, or the + *_PAGE_HASHPTE bit (if used). -- paulus + */ +#define __swp_type(entry) ((entry).val & 0x1f) +#define __swp_offset(entry) ((entry).val >> 5) +#define __swp_entry(type, offset) ((swp_entry_t) { (type) | ((offset) << 5) }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) >> 3 }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val << 3 }) + +/* Encode and decode a nonlinear file mapping entry */ +#define PTE_FILE_MAX_BITS 29 +#define pte_to_pgoff(pte) (pte_val(pte) >> 3) +#define pgoff_to_pte(off) ((pte_t) { ((off) << 3) | _PAGE_FILE }) + +/* CONFIG_APUS */ +/* For virtual address to physical address conversion */ +extern void cache_clear(__u32 addr, int length); +extern void cache_push(__u32 addr, int length); +extern int mm_end_of_chunk (unsigned long addr, int len); +extern unsigned long iopa(unsigned long addr); +extern unsigned long mm_ptov(unsigned long addr) __attribute_const__; + +/* Values for nocacheflag and cmode */ +/* These are not used by the APUS kernel_map, but prevents + compilation errors. */ +#define KERNELMAP_FULL_CACHING 0 +#define KERNELMAP_NOCACHE_SER 1 +#define KERNELMAP_NOCACHE_NONSER 2 +#define KERNELMAP_NO_COPYBACK 3 + +/* + * Map some physical address range into the kernel address space. + */ +extern unsigned long kernel_map(unsigned long paddr, unsigned long size, + int nocacheflag, unsigned long *memavailp ); + +/* + * Set cache mode of (kernel space) address range. + */ +extern void kernel_set_cachemode (unsigned long address, unsigned long size, + unsigned int cmode); + +/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ +#define kern_addr_valid(addr) (1) + +#ifdef CONFIG_PHYS_64BIT +extern int remap_pfn_range(struct vm_area_struct *vma, unsigned long from, + unsigned long paddr, unsigned long size, pgprot_t prot); + +static inline int io_remap_pfn_range(struct vm_area_struct *vma, + unsigned long vaddr, + unsigned long pfn, + unsigned long size, + pgprot_t prot) +{ + phys_addr_t paddr64 = fixup_bigphys_addr(pfn << PAGE_SHIFT, size); + return remap_pfn_range(vma, vaddr, paddr64 >> PAGE_SHIFT, size, prot); +} +#else +#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ + remap_pfn_range(vma, vaddr, pfn, size, prot) +#endif + +/* + * No page table caches to initialise + */ +#define pgtable_cache_init() do { } while (0) + +extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep, + pmd_t **pmdp); + +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_POWERPC_PGTABLE_PPC32_H */ diff --git a/include/asm-powerpc/pgtable-ppc64.h b/include/asm-powerpc/pgtable-ppc64.h new file mode 100644 index 00000000000..704c4e669fe --- /dev/null +++ b/include/asm-powerpc/pgtable-ppc64.h @@ -0,0 +1,492 @@ +#ifndef _ASM_POWERPC_PGTABLE_PPC64_H_ +#define _ASM_POWERPC_PGTABLE_PPC64_H_ +/* + * This file contains the functions and defines necessary to modify and use + * the ppc64 hashed page table. + */ + +#ifndef __ASSEMBLY__ +#include +#include /* For TASK_SIZE */ +#include +#include +#include +struct mm_struct; +#endif /* __ASSEMBLY__ */ + +#ifdef CONFIG_PPC_64K_PAGES +#include +#else +#include +#endif + +#define FIRST_USER_ADDRESS 0 + +/* + * Size of EA range mapped by our pagetables. + */ +#define PGTABLE_EADDR_SIZE (PTE_INDEX_SIZE + PMD_INDEX_SIZE + \ + PUD_INDEX_SIZE + PGD_INDEX_SIZE + PAGE_SHIFT) +#define PGTABLE_RANGE (1UL << PGTABLE_EADDR_SIZE) + +#if TASK_SIZE_USER64 > PGTABLE_RANGE +#error TASK_SIZE_USER64 exceeds pagetable range +#endif + +#if TASK_SIZE_USER64 > (1UL << (USER_ESID_BITS + SID_SHIFT)) +#error TASK_SIZE_USER64 exceeds user VSID range +#endif + +/* + * Define the address range of the vmalloc VM area. + */ +#define VMALLOC_START ASM_CONST(0xD000000000000000) +#define VMALLOC_SIZE ASM_CONST(0x80000000000) +#define VMALLOC_END (VMALLOC_START + VMALLOC_SIZE) + +/* + * Define the address range of the imalloc VM area. + */ +#define PHBS_IO_BASE VMALLOC_END +#define IMALLOC_BASE (PHBS_IO_BASE + 0x80000000ul) /* Reserve 2 gigs for PHBs */ +#define IMALLOC_END (VMALLOC_START + PGTABLE_RANGE) + +/* + * Region IDs + */ +#define REGION_SHIFT 60UL +#define REGION_MASK (0xfUL << REGION_SHIFT) +#define REGION_ID(ea) (((unsigned long)(ea)) >> REGION_SHIFT) + +#define VMALLOC_REGION_ID (REGION_ID(VMALLOC_START)) +#define KERNEL_REGION_ID (REGION_ID(PAGE_OFFSET)) +#define USER_REGION_ID (0UL) + +/* + * Common bits in a linux-style PTE. These match the bits in the + * (hardware-defined) PowerPC PTE as closely as possible. Additional + * bits may be defined in pgtable-*.h + */ +#define _PAGE_PRESENT 0x0001 /* software: pte contains a translation */ +#define _PAGE_USER 0x0002 /* matches one of the PP bits */ +#define _PAGE_FILE 0x0002 /* (!present only) software: pte holds file offset */ +#define _PAGE_EXEC 0x0004 /* No execute on POWER4 and newer (we invert) */ +#define _PAGE_GUARDED 0x0008 +#define _PAGE_COHERENT 0x0010 /* M: enforce memory coherence (SMP systems) */ +#define _PAGE_NO_CACHE 0x0020 /* I: cache inhibit */ +#define _PAGE_WRITETHRU 0x0040 /* W: cache write-through */ +#define _PAGE_DIRTY 0x0080 /* C: page changed */ +#define _PAGE_ACCESSED 0x0100 /* R: page referenced */ +#define _PAGE_RW 0x0200 /* software: user write access allowed */ +#define _PAGE_HASHPTE 0x0400 /* software: pte has an associated HPTE */ +#define _PAGE_BUSY 0x0800 /* software: PTE & hash are busy */ + +#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT) + +#define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY) + +/* __pgprot defined in asm-powerpc/page.h */ +#define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED) + +#define PAGE_SHARED __pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_USER) +#define PAGE_SHARED_X __pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_USER | _PAGE_EXEC) +#define PAGE_COPY __pgprot(_PAGE_BASE | _PAGE_USER) +#define PAGE_COPY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) +#define PAGE_READONLY __pgprot(_PAGE_BASE | _PAGE_USER) +#define PAGE_READONLY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) +#define PAGE_KERNEL __pgprot(_PAGE_BASE | _PAGE_WRENABLE) +#define PAGE_KERNEL_CI __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \ + _PAGE_WRENABLE | _PAGE_NO_CACHE | _PAGE_GUARDED) +#define PAGE_KERNEL_EXEC __pgprot(_PAGE_BASE | _PAGE_WRENABLE | _PAGE_EXEC) + +#define PAGE_AGP __pgprot(_PAGE_BASE | _PAGE_WRENABLE | _PAGE_NO_CACHE) +#define HAVE_PAGE_AGP + +/* PTEIDX nibble */ +#define _PTEIDX_SECONDARY 0x8 +#define _PTEIDX_GROUP_IX 0x7 + + +/* + * POWER4 and newer have per page execute protection, older chips can only + * do this on a segment (256MB) basis. + * + * Also, write permissions imply read permissions. + * This is the closest we can get.. + * + * Note due to the way vm flags are laid out, the bits are XWR + */ +#define __P000 PAGE_NONE +#define __P001 PAGE_READONLY +#define __P010 PAGE_COPY +#define __P011 PAGE_COPY +#define __P100 PAGE_READONLY_X +#define __P101 PAGE_READONLY_X +#define __P110 PAGE_COPY_X +#define __P111 PAGE_COPY_X + +#define __S000 PAGE_NONE +#define __S001 PAGE_READONLY +#define __S010 PAGE_SHARED +#define __S011 PAGE_SHARED +#define __S100 PAGE_READONLY_X +#define __S101 PAGE_READONLY_X +#define __S110 PAGE_SHARED_X +#define __S111 PAGE_SHARED_X + +#ifndef __ASSEMBLY__ + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[PAGE_SIZE/sizeof(unsigned long)]; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) +#endif /* __ASSEMBLY__ */ + +#ifdef CONFIG_HUGETLB_PAGE + +#define HAVE_ARCH_UNMAPPED_AREA +#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN + +#endif + +#ifndef __ASSEMBLY__ + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + * + * mk_pte takes a (struct page *) as input + */ +#define mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), (pgprot)) + +static inline pte_t pfn_pte(unsigned long pfn, pgprot_t pgprot) +{ + pte_t pte; + + + pte_val(pte) = (pfn << PTE_RPN_SHIFT) | pgprot_val(pgprot); + return pte; +} + +#define pte_modify(_pte, newprot) \ + (__pte((pte_val(_pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))) + +#define pte_none(pte) ((pte_val(pte) & ~_PAGE_HPTEFLAGS) == 0) +#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) + +/* pte_clear moved to later in this file */ + +#define pte_pfn(x) ((unsigned long)((pte_val(x)>>PTE_RPN_SHIFT))) +#define pte_page(x) pfn_to_page(pte_pfn(x)) + +#define PMD_BAD_BITS (PTE_TABLE_SIZE-1) +#define PUD_BAD_BITS (PMD_TABLE_SIZE-1) + +#define pmd_set(pmdp, pmdval) (pmd_val(*(pmdp)) = (pmdval)) +#define pmd_none(pmd) (!pmd_val(pmd)) +#define pmd_bad(pmd) (!is_kernel_addr(pmd_val(pmd)) \ + || (pmd_val(pmd) & PMD_BAD_BITS)) +#define pmd_present(pmd) (pmd_val(pmd) != 0) +#define pmd_clear(pmdp) (pmd_val(*(pmdp)) = 0) +#define pmd_page_vaddr(pmd) (pmd_val(pmd) & ~PMD_MASKED_BITS) +#define pmd_page(pmd) virt_to_page(pmd_page_vaddr(pmd)) + +#define pud_set(pudp, pudval) (pud_val(*(pudp)) = (pudval)) +#define pud_none(pud) (!pud_val(pud)) +#define pud_bad(pud) (!is_kernel_addr(pud_val(pud)) \ + || (pud_val(pud) & PUD_BAD_BITS)) +#define pud_present(pud) (pud_val(pud) != 0) +#define pud_clear(pudp) (pud_val(*(pudp)) = 0) +#define pud_page_vaddr(pud) (pud_val(pud) & ~PUD_MASKED_BITS) +#define pud_page(pud) virt_to_page(pud_page_vaddr(pud)) + +#define pgd_set(pgdp, pudp) ({pgd_val(*(pgdp)) = (unsigned long)(pudp);}) + +/* + * Find an entry in a page-table-directory. We combine the address region + * (the high order N bits) and the pgd portion of the address. + */ +/* to avoid overflow in free_pgtables we don't use PTRS_PER_PGD here */ +#define pgd_index(address) (((address) >> (PGDIR_SHIFT)) & 0x1ff) + +#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address)) + +#define pmd_offset(pudp,addr) \ + (((pmd_t *) pud_page_vaddr(*(pudp))) + (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))) + +#define pte_offset_kernel(dir,addr) \ + (((pte_t *) pmd_page_vaddr(*(dir))) + (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) + +#define pte_offset_map(dir,addr) pte_offset_kernel((dir), (addr)) +#define pte_offset_map_nested(dir,addr) pte_offset_kernel((dir), (addr)) +#define pte_unmap(pte) do { } while(0) +#define pte_unmap_nested(pte) do { } while(0) + +/* to find an entry in a kernel page-table-directory */ +/* This now only contains the vmalloc pages */ +#define pgd_offset_k(address) pgd_offset(&init_mm, address) + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +static inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_USER;} +static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_RW;} +static inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_EXEC;} +static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY;} +static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED;} +static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE;} + +static inline void pte_uncache(pte_t pte) { pte_val(pte) |= _PAGE_NO_CACHE; } +static inline void pte_cache(pte_t pte) { pte_val(pte) &= ~_PAGE_NO_CACHE; } + +static inline pte_t pte_rdprotect(pte_t pte) { + pte_val(pte) &= ~_PAGE_USER; return pte; } +static inline pte_t pte_exprotect(pte_t pte) { + pte_val(pte) &= ~_PAGE_EXEC; return pte; } +static inline pte_t pte_wrprotect(pte_t pte) { + pte_val(pte) &= ~(_PAGE_RW); return pte; } +static inline pte_t pte_mkclean(pte_t pte) { + pte_val(pte) &= ~(_PAGE_DIRTY); return pte; } +static inline pte_t pte_mkold(pte_t pte) { + pte_val(pte) &= ~_PAGE_ACCESSED; return pte; } +static inline pte_t pte_mkread(pte_t pte) { + pte_val(pte) |= _PAGE_USER; return pte; } +static inline pte_t pte_mkexec(pte_t pte) { + pte_val(pte) |= _PAGE_USER | _PAGE_EXEC; return pte; } +static inline pte_t pte_mkwrite(pte_t pte) { + pte_val(pte) |= _PAGE_RW; return pte; } +static inline pte_t pte_mkdirty(pte_t pte) { + pte_val(pte) |= _PAGE_DIRTY; return pte; } +static inline pte_t pte_mkyoung(pte_t pte) { + pte_val(pte) |= _PAGE_ACCESSED; return pte; } +static inline pte_t pte_mkhuge(pte_t pte) { + return pte; } + +/* Atomic PTE updates */ +static inline unsigned long pte_update(struct mm_struct *mm, + unsigned long addr, + pte_t *ptep, unsigned long clr, + int huge) +{ + unsigned long old, tmp; + + __asm__ __volatile__( + "1: ldarx %0,0,%3 # pte_update\n\ + andi. %1,%0,%6\n\ + bne- 1b \n\ + andc %1,%0,%4 \n\ + stdcx. %1,0,%3 \n\ + bne- 1b" + : "=&r" (old), "=&r" (tmp), "=m" (*ptep) + : "r" (ptep), "r" (clr), "m" (*ptep), "i" (_PAGE_BUSY) + : "cc" ); + + if (old & _PAGE_HASHPTE) + hpte_need_flush(mm, addr, ptep, old, huge); + return old; +} + +static inline int __ptep_test_and_clear_young(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + unsigned long old; + + if ((pte_val(*ptep) & (_PAGE_ACCESSED | _PAGE_HASHPTE)) == 0) + return 0; + old = pte_update(mm, addr, ptep, _PAGE_ACCESSED, 0); + return (old & _PAGE_ACCESSED) != 0; +} +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG +#define ptep_test_and_clear_young(__vma, __addr, __ptep) \ +({ \ + int __r; \ + __r = __ptep_test_and_clear_young((__vma)->vm_mm, __addr, __ptep); \ + __r; \ +}) + +/* + * On RW/DIRTY bit transitions we can avoid flushing the hpte. For the + * moment we always flush but we need to fix hpte_update and test if the + * optimisation is worth it. + */ +static inline int __ptep_test_and_clear_dirty(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + unsigned long old; + + if ((pte_val(*ptep) & _PAGE_DIRTY) == 0) + return 0; + old = pte_update(mm, addr, ptep, _PAGE_DIRTY, 0); + return (old & _PAGE_DIRTY) != 0; +} +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY +#define ptep_test_and_clear_dirty(__vma, __addr, __ptep) \ +({ \ + int __r; \ + __r = __ptep_test_and_clear_dirty((__vma)->vm_mm, __addr, __ptep); \ + __r; \ +}) + +#define __HAVE_ARCH_PTEP_SET_WRPROTECT +static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, + pte_t *ptep) +{ + unsigned long old; + + if ((pte_val(*ptep) & _PAGE_RW) == 0) + return; + old = pte_update(mm, addr, ptep, _PAGE_RW, 0); +} + +/* + * We currently remove entries from the hashtable regardless of whether + * the entry was young or dirty. The generic routines only flush if the + * entry was young or dirty which is not good enough. + * + * We should be more intelligent about this but for the moment we override + * these functions and force a tlb flush unconditionally + */ +#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH +#define ptep_clear_flush_young(__vma, __address, __ptep) \ +({ \ + int __young = __ptep_test_and_clear_young((__vma)->vm_mm, __address, \ + __ptep); \ + __young; \ +}) + +#define __HAVE_ARCH_PTEP_CLEAR_DIRTY_FLUSH +#define ptep_clear_flush_dirty(__vma, __address, __ptep) \ +({ \ + int __dirty = __ptep_test_and_clear_dirty((__vma)->vm_mm, __address, \ + __ptep); \ + __dirty; \ +}) + +#define __HAVE_ARCH_PTEP_GET_AND_CLEAR +static inline pte_t ptep_get_and_clear(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + unsigned long old = pte_update(mm, addr, ptep, ~0UL, 0); + return __pte(old); +} + +static inline void pte_clear(struct mm_struct *mm, unsigned long addr, + pte_t * ptep) +{ + pte_update(mm, addr, ptep, ~0UL, 0); +} + +/* + * set_pte stores a linux PTE into the linux page table. + */ +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte) +{ + if (pte_present(*ptep)) + pte_clear(mm, addr, ptep); + pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS); + *ptep = pte; +} + +/* Set the dirty and/or accessed bits atomically in a linux PTE, this + * function doesn't need to flush the hash entry + */ +#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS +static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry, int dirty) +{ + unsigned long bits = pte_val(entry) & + (_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC); + unsigned long old, tmp; + + __asm__ __volatile__( + "1: ldarx %0,0,%4\n\ + andi. %1,%0,%6\n\ + bne- 1b \n\ + or %0,%3,%0\n\ + stdcx. %0,0,%4\n\ + bne- 1b" + :"=&r" (old), "=&r" (tmp), "=m" (*ptep) + :"r" (bits), "r" (ptep), "m" (*ptep), "i" (_PAGE_BUSY) + :"cc"); +} +#define ptep_set_access_flags(__vma, __address, __ptep, __entry, __dirty) \ + do { \ + __ptep_set_access_flags(__ptep, __entry, __dirty); \ + flush_tlb_page_nohash(__vma, __address); \ + } while(0) + +/* + * Macro to mark a page protection value as "uncacheable". + */ +#define pgprot_noncached(prot) (__pgprot(pgprot_val(prot) | _PAGE_NO_CACHE | _PAGE_GUARDED)) + +struct file; +extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, + unsigned long size, pgprot_t vma_prot); +#define __HAVE_PHYS_MEM_ACCESS_PROT + +#define __HAVE_ARCH_PTE_SAME +#define pte_same(A,B) (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HPTEFLAGS) == 0) + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + +extern pgd_t swapper_pg_dir[]; + +extern void paging_init(void); + +/* Encode and de-code a swap entry */ +#define __swp_type(entry) (((entry).val >> 1) & 0x3f) +#define __swp_offset(entry) ((entry).val >> 8) +#define __swp_entry(type, offset) ((swp_entry_t){((type)<< 1)|((offset)<<8)}) +#define __pte_to_swp_entry(pte) ((swp_entry_t){pte_val(pte) >> PTE_RPN_SHIFT}) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val << PTE_RPN_SHIFT }) +#define pte_to_pgoff(pte) (pte_val(pte) >> PTE_RPN_SHIFT) +#define pgoff_to_pte(off) ((pte_t) {((off) << PTE_RPN_SHIFT)|_PAGE_FILE}) +#define PTE_FILE_MAX_BITS (BITS_PER_LONG - PTE_RPN_SHIFT) + +/* + * kern_addr_valid is intended to indicate whether an address is a valid + * kernel address. Most 32-bit archs define it as always true (like this) + * but most 64-bit archs actually perform a test. What should we do here? + * The only use is in fs/ncpfs/dir.c + */ +#define kern_addr_valid(addr) (1) + +#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ + remap_pfn_range(vma, vaddr, pfn, size, prot) + +void pgtable_cache_init(void); + +/* + * find_linux_pte returns the address of a linux pte for a given + * effective address and directory. If not found, it returns zero. + */static inline pte_t *find_linux_pte(pgd_t *pgdir, unsigned long ea) +{ + pgd_t *pg; + pud_t *pu; + pmd_t *pm; + pte_t *pt = NULL; + + pg = pgdir + pgd_index(ea); + if (!pgd_none(*pg)) { + pu = pud_offset(pg, ea); + if (!pud_none(*pu)) { + pm = pmd_offset(pu, ea); + if (pmd_present(*pm)) + pt = pte_offset_kernel(pm, ea); + } + } + return pt; +} + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_POWERPC_PGTABLE_PPC64_H_ */ diff --git a/include/asm-powerpc/pgtable.h b/include/asm-powerpc/pgtable.h index 19edb6982b8..78bf4ae712a 100644 --- a/include/asm-powerpc/pgtable.h +++ b/include/asm-powerpc/pgtable.h @@ -2,502 +2,15 @@ #define _ASM_POWERPC_PGTABLE_H #ifdef __KERNEL__ -#ifndef CONFIG_PPC64 -#include +#if defined(CONFIG_PPC64) +# include #else - -/* - * This file contains the functions and defines necessary to modify and use - * the ppc64 hashed page table. - */ - -#ifndef __ASSEMBLY__ -#include -#include /* For TASK_SIZE */ -#include -#include -#include -struct mm_struct; -#endif /* __ASSEMBLY__ */ - -#ifdef CONFIG_PPC_64K_PAGES -#include -#else -#include -#endif - -#define FIRST_USER_ADDRESS 0 - -/* - * Size of EA range mapped by our pagetables. - */ -#define PGTABLE_EADDR_SIZE (PTE_INDEX_SIZE + PMD_INDEX_SIZE + \ - PUD_INDEX_SIZE + PGD_INDEX_SIZE + PAGE_SHIFT) -#define PGTABLE_RANGE (1UL << PGTABLE_EADDR_SIZE) - -#if TASK_SIZE_USER64 > PGTABLE_RANGE -#error TASK_SIZE_USER64 exceeds pagetable range -#endif - -#if TASK_SIZE_USER64 > (1UL << (USER_ESID_BITS + SID_SHIFT)) -#error TASK_SIZE_USER64 exceeds user VSID range +# include #endif -/* - * Define the address range of the vmalloc VM area. - */ -#define VMALLOC_START ASM_CONST(0xD000000000000000) -#define VMALLOC_SIZE ASM_CONST(0x80000000000) -#define VMALLOC_END (VMALLOC_START + VMALLOC_SIZE) - -/* - * Define the address range of the imalloc VM area. - */ -#define PHBS_IO_BASE VMALLOC_END -#define IMALLOC_BASE (PHBS_IO_BASE + 0x80000000ul) /* Reserve 2 gigs for PHBs */ -#define IMALLOC_END (VMALLOC_START + PGTABLE_RANGE) - -/* - * Region IDs - */ -#define REGION_SHIFT 60UL -#define REGION_MASK (0xfUL << REGION_SHIFT) -#define REGION_ID(ea) (((unsigned long)(ea)) >> REGION_SHIFT) - -#define VMALLOC_REGION_ID (REGION_ID(VMALLOC_START)) -#define KERNEL_REGION_ID (REGION_ID(PAGE_OFFSET)) -#define USER_REGION_ID (0UL) - -/* - * Common bits in a linux-style PTE. These match the bits in the - * (hardware-defined) PowerPC PTE as closely as possible. Additional - * bits may be defined in pgtable-*.h - */ -#define _PAGE_PRESENT 0x0001 /* software: pte contains a translation */ -#define _PAGE_USER 0x0002 /* matches one of the PP bits */ -#define _PAGE_FILE 0x0002 /* (!present only) software: pte holds file offset */ -#define _PAGE_EXEC 0x0004 /* No execute on POWER4 and newer (we invert) */ -#define _PAGE_GUARDED 0x0008 -#define _PAGE_COHERENT 0x0010 /* M: enforce memory coherence (SMP systems) */ -#define _PAGE_NO_CACHE 0x0020 /* I: cache inhibit */ -#define _PAGE_WRITETHRU 0x0040 /* W: cache write-through */ -#define _PAGE_DIRTY 0x0080 /* C: page changed */ -#define _PAGE_ACCESSED 0x0100 /* R: page referenced */ -#define _PAGE_RW 0x0200 /* software: user write access allowed */ -#define _PAGE_HASHPTE 0x0400 /* software: pte has an associated HPTE */ -#define _PAGE_BUSY 0x0800 /* software: PTE & hash are busy */ - -#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT) - -#define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY) - -/* __pgprot defined in asm-powerpc/page.h */ -#define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED) - -#define PAGE_SHARED __pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_USER) -#define PAGE_SHARED_X __pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_USER | _PAGE_EXEC) -#define PAGE_COPY __pgprot(_PAGE_BASE | _PAGE_USER) -#define PAGE_COPY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) -#define PAGE_READONLY __pgprot(_PAGE_BASE | _PAGE_USER) -#define PAGE_READONLY_X __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC) -#define PAGE_KERNEL __pgprot(_PAGE_BASE | _PAGE_WRENABLE) -#define PAGE_KERNEL_CI __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \ - _PAGE_WRENABLE | _PAGE_NO_CACHE | _PAGE_GUARDED) -#define PAGE_KERNEL_EXEC __pgprot(_PAGE_BASE | _PAGE_WRENABLE | _PAGE_EXEC) - -#define PAGE_AGP __pgprot(_PAGE_BASE | _PAGE_WRENABLE | _PAGE_NO_CACHE) -#define HAVE_PAGE_AGP - -/* PTEIDX nibble */ -#define _PTEIDX_SECONDARY 0x8 -#define _PTEIDX_GROUP_IX 0x7 - - -/* - * POWER4 and newer have per page execute protection, older chips can only - * do this on a segment (256MB) basis. - * - * Also, write permissions imply read permissions. - * This is the closest we can get.. - * - * Note due to the way vm flags are laid out, the bits are XWR - */ -#define __P000 PAGE_NONE -#define __P001 PAGE_READONLY -#define __P010 PAGE_COPY -#define __P011 PAGE_COPY -#define __P100 PAGE_READONLY_X -#define __P101 PAGE_READONLY_X -#define __P110 PAGE_COPY_X -#define __P111 PAGE_COPY_X - -#define __S000 PAGE_NONE -#define __S001 PAGE_READONLY -#define __S010 PAGE_SHARED -#define __S011 PAGE_SHARED -#define __S100 PAGE_READONLY_X -#define __S101 PAGE_READONLY_X -#define __S110 PAGE_SHARED_X -#define __S111 PAGE_SHARED_X - #ifndef __ASSEMBLY__ - -/* - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc.. - */ -extern unsigned long empty_zero_page[PAGE_SIZE/sizeof(unsigned long)]; -#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) -#endif /* __ASSEMBLY__ */ - -#ifdef CONFIG_HUGETLB_PAGE - -#define HAVE_ARCH_UNMAPPED_AREA -#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN - -#endif - -#ifndef __ASSEMBLY__ - -/* - * Conversion functions: convert a page and protection to a page entry, - * and a page entry and page directory to the page they refer to. - * - * mk_pte takes a (struct page *) as input - */ -#define mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), (pgprot)) - -static inline pte_t pfn_pte(unsigned long pfn, pgprot_t pgprot) -{ - pte_t pte; - - - pte_val(pte) = (pfn << PTE_RPN_SHIFT) | pgprot_val(pgprot); - return pte; -} - -#define pte_modify(_pte, newprot) \ - (__pte((pte_val(_pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))) - -#define pte_none(pte) ((pte_val(pte) & ~_PAGE_HPTEFLAGS) == 0) -#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) - -/* pte_clear moved to later in this file */ - -#define pte_pfn(x) ((unsigned long)((pte_val(x)>>PTE_RPN_SHIFT))) -#define pte_page(x) pfn_to_page(pte_pfn(x)) - -#define PMD_BAD_BITS (PTE_TABLE_SIZE-1) -#define PUD_BAD_BITS (PMD_TABLE_SIZE-1) - -#define pmd_set(pmdp, pmdval) (pmd_val(*(pmdp)) = (pmdval)) -#define pmd_none(pmd) (!pmd_val(pmd)) -#define pmd_bad(pmd) (!is_kernel_addr(pmd_val(pmd)) \ - || (pmd_val(pmd) & PMD_BAD_BITS)) -#define pmd_present(pmd) (pmd_val(pmd) != 0) -#define pmd_clear(pmdp) (pmd_val(*(pmdp)) = 0) -#define pmd_page_vaddr(pmd) (pmd_val(pmd) & ~PMD_MASKED_BITS) -#define pmd_page(pmd) virt_to_page(pmd_page_vaddr(pmd)) - -#define pud_set(pudp, pudval) (pud_val(*(pudp)) = (pudval)) -#define pud_none(pud) (!pud_val(pud)) -#define pud_bad(pud) (!is_kernel_addr(pud_val(pud)) \ - || (pud_val(pud) & PUD_BAD_BITS)) -#define pud_present(pud) (pud_val(pud) != 0) -#define pud_clear(pudp) (pud_val(*(pudp)) = 0) -#define pud_page_vaddr(pud) (pud_val(pud) & ~PUD_MASKED_BITS) -#define pud_page(pud) virt_to_page(pud_page_vaddr(pud)) - -#define pgd_set(pgdp, pudp) ({pgd_val(*(pgdp)) = (unsigned long)(pudp);}) - -/* - * Find an entry in a page-table-directory. We combine the address region - * (the high order N bits) and the pgd portion of the address. - */ -/* to avoid overflow in free_pgtables we don't use PTRS_PER_PGD here */ -#define pgd_index(address) (((address) >> (PGDIR_SHIFT)) & 0x1ff) - -#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address)) - -#define pmd_offset(pudp,addr) \ - (((pmd_t *) pud_page_vaddr(*(pudp))) + (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))) - -#define pte_offset_kernel(dir,addr) \ - (((pte_t *) pmd_page_vaddr(*(dir))) + (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) - -#define pte_offset_map(dir,addr) pte_offset_kernel((dir), (addr)) -#define pte_offset_map_nested(dir,addr) pte_offset_kernel((dir), (addr)) -#define pte_unmap(pte) do { } while(0) -#define pte_unmap_nested(pte) do { } while(0) - -/* to find an entry in a kernel page-table-directory */ -/* This now only contains the vmalloc pages */ -#define pgd_offset_k(address) pgd_offset(&init_mm, address) - -/* - * The following only work if pte_present() is true. - * Undefined behaviour if not.. - */ -static inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_USER;} -static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_RW;} -static inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_EXEC;} -static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY;} -static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED;} -static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE;} - -static inline void pte_uncache(pte_t pte) { pte_val(pte) |= _PAGE_NO_CACHE; } -static inline void pte_cache(pte_t pte) { pte_val(pte) &= ~_PAGE_NO_CACHE; } - -static inline pte_t pte_rdprotect(pte_t pte) { - pte_val(pte) &= ~_PAGE_USER; return pte; } -static inline pte_t pte_exprotect(pte_t pte) { - pte_val(pte) &= ~_PAGE_EXEC; return pte; } -static inline pte_t pte_wrprotect(pte_t pte) { - pte_val(pte) &= ~(_PAGE_RW); return pte; } -static inline pte_t pte_mkclean(pte_t pte) { - pte_val(pte) &= ~(_PAGE_DIRTY); return pte; } -static inline pte_t pte_mkold(pte_t pte) { - pte_val(pte) &= ~_PAGE_ACCESSED; return pte; } -static inline pte_t pte_mkread(pte_t pte) { - pte_val(pte) |= _PAGE_USER; return pte; } -static inline pte_t pte_mkexec(pte_t pte) { - pte_val(pte) |= _PAGE_USER | _PAGE_EXEC; return pte; } -static inline pte_t pte_mkwrite(pte_t pte) { - pte_val(pte) |= _PAGE_RW; return pte; } -static inline pte_t pte_mkdirty(pte_t pte) { - pte_val(pte) |= _PAGE_DIRTY; return pte; } -static inline pte_t pte_mkyoung(pte_t pte) { - pte_val(pte) |= _PAGE_ACCESSED; return pte; } -static inline pte_t pte_mkhuge(pte_t pte) { - return pte; } - -/* Atomic PTE updates */ -static inline unsigned long pte_update(struct mm_struct *mm, - unsigned long addr, - pte_t *ptep, unsigned long clr, - int huge) -{ - unsigned long old, tmp; - - __asm__ __volatile__( - "1: ldarx %0,0,%3 # pte_update\n\ - andi. %1,%0,%6\n\ - bne- 1b \n\ - andc %1,%0,%4 \n\ - stdcx. %1,0,%3 \n\ - bne- 1b" - : "=&r" (old), "=&r" (tmp), "=m" (*ptep) - : "r" (ptep), "r" (clr), "m" (*ptep), "i" (_PAGE_BUSY) - : "cc" ); - - if (old & _PAGE_HASHPTE) - hpte_need_flush(mm, addr, ptep, old, huge); - return old; -} - -static inline int __ptep_test_and_clear_young(struct mm_struct *mm, - unsigned long addr, pte_t *ptep) -{ - unsigned long old; - - if ((pte_val(*ptep) & (_PAGE_ACCESSED | _PAGE_HASHPTE)) == 0) - return 0; - old = pte_update(mm, addr, ptep, _PAGE_ACCESSED, 0); - return (old & _PAGE_ACCESSED) != 0; -} -#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG -#define ptep_test_and_clear_young(__vma, __addr, __ptep) \ -({ \ - int __r; \ - __r = __ptep_test_and_clear_young((__vma)->vm_mm, __addr, __ptep); \ - __r; \ -}) - -/* - * On RW/DIRTY bit transitions we can avoid flushing the hpte. For the - * moment we always flush but we need to fix hpte_update and test if the - * optimisation is worth it. - */ -static inline int __ptep_test_and_clear_dirty(struct mm_struct *mm, - unsigned long addr, pte_t *ptep) -{ - unsigned long old; - - if ((pte_val(*ptep) & _PAGE_DIRTY) == 0) - return 0; - old = pte_update(mm, addr, ptep, _PAGE_DIRTY, 0); - return (old & _PAGE_DIRTY) != 0; -} -#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY -#define ptep_test_and_clear_dirty(__vma, __addr, __ptep) \ -({ \ - int __r; \ - __r = __ptep_test_and_clear_dirty((__vma)->vm_mm, __addr, __ptep); \ - __r; \ -}) - -#define __HAVE_ARCH_PTEP_SET_WRPROTECT -static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, - pte_t *ptep) -{ - unsigned long old; - - if ((pte_val(*ptep) & _PAGE_RW) == 0) - return; - old = pte_update(mm, addr, ptep, _PAGE_RW, 0); -} - -/* - * We currently remove entries from the hashtable regardless of whether - * the entry was young or dirty. The generic routines only flush if the - * entry was young or dirty which is not good enough. - * - * We should be more intelligent about this but for the moment we override - * these functions and force a tlb flush unconditionally - */ -#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH -#define ptep_clear_flush_young(__vma, __address, __ptep) \ -({ \ - int __young = __ptep_test_and_clear_young((__vma)->vm_mm, __address, \ - __ptep); \ - __young; \ -}) - -#define __HAVE_ARCH_PTEP_CLEAR_DIRTY_FLUSH -#define ptep_clear_flush_dirty(__vma, __address, __ptep) \ -({ \ - int __dirty = __ptep_test_and_clear_dirty((__vma)->vm_mm, __address, \ - __ptep); \ - __dirty; \ -}) - -#define __HAVE_ARCH_PTEP_GET_AND_CLEAR -static inline pte_t ptep_get_and_clear(struct mm_struct *mm, - unsigned long addr, pte_t *ptep) -{ - unsigned long old = pte_update(mm, addr, ptep, ~0UL, 0); - return __pte(old); -} - -static inline void pte_clear(struct mm_struct *mm, unsigned long addr, - pte_t * ptep) -{ - pte_update(mm, addr, ptep, ~0UL, 0); -} - -/* - * set_pte stores a linux PTE into the linux page table. - */ -static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t pte) -{ - if (pte_present(*ptep)) - pte_clear(mm, addr, ptep); - pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS); - *ptep = pte; -} - -/* Set the dirty and/or accessed bits atomically in a linux PTE, this - * function doesn't need to flush the hash entry - */ -#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS -static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry, int dirty) -{ - unsigned long bits = pte_val(entry) & - (_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC); - unsigned long old, tmp; - - __asm__ __volatile__( - "1: ldarx %0,0,%4\n\ - andi. %1,%0,%6\n\ - bne- 1b \n\ - or %0,%3,%0\n\ - stdcx. %0,0,%4\n\ - bne- 1b" - :"=&r" (old), "=&r" (tmp), "=m" (*ptep) - :"r" (bits), "r" (ptep), "m" (*ptep), "i" (_PAGE_BUSY) - :"cc"); -} -#define ptep_set_access_flags(__vma, __address, __ptep, __entry, __dirty) \ - do { \ - __ptep_set_access_flags(__ptep, __entry, __dirty); \ - flush_tlb_page_nohash(__vma, __address); \ - } while(0) - -/* - * Macro to mark a page protection value as "uncacheable". - */ -#define pgprot_noncached(prot) (__pgprot(pgprot_val(prot) | _PAGE_NO_CACHE | _PAGE_GUARDED)) - -struct file; -extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, - unsigned long size, pgprot_t vma_prot); -#define __HAVE_PHYS_MEM_ACCESS_PROT - -#define __HAVE_ARCH_PTE_SAME -#define pte_same(A,B) (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HPTEFLAGS) == 0) - -#define pte_ERROR(e) \ - printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) -#define pmd_ERROR(e) \ - printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) -#define pgd_ERROR(e) \ - printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) - -extern pgd_t swapper_pg_dir[]; - -extern void paging_init(void); - -/* Encode and de-code a swap entry */ -#define __swp_type(entry) (((entry).val >> 1) & 0x3f) -#define __swp_offset(entry) ((entry).val >> 8) -#define __swp_entry(type, offset) ((swp_entry_t){((type)<< 1)|((offset)<<8)}) -#define __pte_to_swp_entry(pte) ((swp_entry_t){pte_val(pte) >> PTE_RPN_SHIFT}) -#define __swp_entry_to_pte(x) ((pte_t) { (x).val << PTE_RPN_SHIFT }) -#define pte_to_pgoff(pte) (pte_val(pte) >> PTE_RPN_SHIFT) -#define pgoff_to_pte(off) ((pte_t) {((off) << PTE_RPN_SHIFT)|_PAGE_FILE}) -#define PTE_FILE_MAX_BITS (BITS_PER_LONG - PTE_RPN_SHIFT) - -/* - * kern_addr_valid is intended to indicate whether an address is a valid - * kernel address. Most 32-bit archs define it as always true (like this) - * but most 64-bit archs actually perform a test. What should we do here? - * The only use is in fs/ncpfs/dir.c - */ -#define kern_addr_valid(addr) (1) - -#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ - remap_pfn_range(vma, vaddr, pfn, size, prot) - -void pgtable_cache_init(void); - -/* - * find_linux_pte returns the address of a linux pte for a given - * effective address and directory. If not found, it returns zero. - */static inline pte_t *find_linux_pte(pgd_t *pgdir, unsigned long ea) -{ - pgd_t *pg; - pud_t *pu; - pmd_t *pm; - pte_t *pt = NULL; - - pg = pgdir + pgd_index(ea); - if (!pgd_none(*pg)) { - pu = pud_offset(pg, ea); - if (!pud_none(*pu)) { - pm = pmd_offset(pu, ea); - if (pmd_present(*pm)) - pt = pte_offset_kernel(pm, ea); - } - } - return pt; -} - - #include - #endif /* __ASSEMBLY__ */ -#endif /* CONFIG_PPC64 */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PGTABLE_H */ -- cgit v1.2.3 From be9c94dd7776467813419f49fabe8017bc2d4c81 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 30 Apr 2007 21:37:15 +1000 Subject: [POWERPC] Fix suspend states again In commit 0fba3a1f39f8b0a50b56c8b068fa52131cbc84c2 (a very long time ago, May 2006), I fixed a bug that caused powermacs to crash when you tried entering standby/mem suspend states. As I'm now getting more familiar with the suspend code I notice a few more things: 1. we previously misunderstood what pm_ops is for, it isn't supposed to be for doing platform dependent suspend/resume stuff that needs to be done for suspend to disk (as we currently try to use it!), it is instead for entering platform dependent suspend states ("standby", "mem"). 2. due to the first point, we never properly save FPU and altivec states when suspending to disk. It probably hasn't hurt yet because the process that writes the "disk" to /sys/power/state uses neither and its context is used. This patch addresses these points as follows: 1. remove all pm_ops from powermac, powermac suspend to ram isn't currently usable via /sys/power/state but is done via the PMU instead. 2. move the code responsible for storing FPU/altivec state into save_processor_state and the set_context() call to restore_processor_state. 3. add a call to kernel_enable_spe() It may look like there is some code removal missing but that is actually because the new suspend.h file overrides the ppc/suspend.h one which was previously used. Signed-off-by: Johannes Berg Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/Makefile | 1 + arch/powerpc/kernel/swsusp.c | 34 +++++++++++++++++ arch/powerpc/platforms/powermac/setup.c | 65 --------------------------------- include/asm-powerpc/suspend.h | 9 +++++ 4 files changed, 44 insertions(+), 65 deletions(-) create mode 100644 arch/powerpc/kernel/swsusp.c create mode 100644 include/asm-powerpc/suspend.h (limited to 'include') diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index e0fa80eca36..949f36a62aa 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_6xx) += idle_6xx.o l2cr_6xx.o cpu_setup_6xx.o obj-$(CONFIG_TAU) += tau_6xx.o +obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj32-$(CONFIG_SOFTWARE_SUSPEND) += swsusp_32.o obj32-$(CONFIG_MODULES) += module_32.o diff --git a/arch/powerpc/kernel/swsusp.c b/arch/powerpc/kernel/swsusp.c new file mode 100644 index 00000000000..b89e4f5a0b0 --- /dev/null +++ b/arch/powerpc/kernel/swsusp.c @@ -0,0 +1,34 @@ +/* + * Common powerpc suspend code for 32 and 64 bits + * + * Copyright 2007 Johannes Berg + * + * 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 + +void save_processor_state(void) +{ + /* + * flush out all the special registers so we don't need + * to save them in the snapshot + */ + flush_fp_to_thread(current); + flush_altivec_to_thread(current); + flush_spe_to_thread(current); +} + +void restore_processor_state(void) +{ +#ifdef CONFIG_PPC32 + set_context(current->active_mm->context.id, current->active_mm->pgd); +#endif +} diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index b820cabac69..5ae57e17d2b 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -439,76 +439,11 @@ static void __init find_boot_device(void) #endif } -/* TODO: Merge the suspend-to-ram with the common code !!! - * currently, this is a stub implementation for suspend-to-disk - * only - */ - -#ifdef CONFIG_SOFTWARE_SUSPEND - -static int pmac_pm_prepare(suspend_state_t state) -{ - printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); - - return 0; -} - -static int pmac_pm_enter(suspend_state_t state) -{ - printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); - - /* Giveup the lazy FPU & vec so we don't have to back them - * up from the low level code - */ - enable_kernel_fp(); - -#ifdef CONFIG_ALTIVEC - if (cur_cpu_spec->cpu_features & CPU_FTR_ALTIVEC) - enable_kernel_altivec(); -#endif /* CONFIG_ALTIVEC */ - - return 0; -} - -static int pmac_pm_finish(suspend_state_t state) -{ - printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state); - - /* Restore userland MMU context */ - set_context(current->active_mm->context.id, current->active_mm->pgd); - - return 0; -} - -static int pmac_pm_valid(suspend_state_t state) -{ - switch (state) { - case PM_SUSPEND_DISK: - return 1; - /* can't do any other states via generic mechanism yet */ - default: - return 0; - } -} - -static struct pm_ops pmac_pm_ops = { - .pm_disk_mode = PM_DISK_SHUTDOWN, - .prepare = pmac_pm_prepare, - .enter = pmac_pm_enter, - .finish = pmac_pm_finish, - .valid = pmac_pm_valid, -}; - -#endif /* CONFIG_SOFTWARE_SUSPEND */ - static int initializing = 1; static int pmac_late_init(void) { initializing = 0; -#ifdef CONFIG_SOFTWARE_SUSPEND - pm_set_ops(&pmac_pm_ops); -#endif /* CONFIG_SOFTWARE_SUSPEND */ return 0; } diff --git a/include/asm-powerpc/suspend.h b/include/asm-powerpc/suspend.h new file mode 100644 index 00000000000..cbf2c9404c3 --- /dev/null +++ b/include/asm-powerpc/suspend.h @@ -0,0 +1,9 @@ +#ifndef __ASM_POWERPC_SUSPEND_H +#define __ASM_POWERPC_SUSPEND_H + +static inline int arch_prepare_suspend(void) { return 0; } + +void save_processor_state(void); +void restore_processor_state(void); + +#endif /* __ASM_POWERPC_SUSPEND_H */ -- cgit v1.2.3 From dc4f60c25ae71e8278dcf909486e4aa34de7eecb Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 1 May 2007 07:01:01 +1000 Subject: [POWERPC] PS3: Interrupt routine fixups. Fixups for the ps3 interrupt routines to support all HV device in a generic way. Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/ps3/interrupt.c | 234 +++++++++++++++++++++++---------- arch/powerpc/platforms/ps3/smp.c | 6 +- arch/powerpc/platforms/ps3/spu.c | 16 +-- drivers/ps3/vuart.c | 8 +- drivers/usb/host/ehci-ps3.c | 4 +- drivers/usb/host/ohci-ps3.c | 4 +- drivers/video/ps3fb.c | 12 +- include/asm-powerpc/ps3.h | 33 +++-- 8 files changed, 211 insertions(+), 106 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index 631c3009561..9da82c266ba 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -89,7 +89,18 @@ struct ps3_private { static DEFINE_PER_CPU(struct ps3_private, ps3_private); -int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, +/** + * ps3_virq_setup - virq related setup. + * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be + * serviced on. + * @outlet: The HV outlet from the various create outlet routines. + * @virq: The assigned Linux virq. + * + * Calls irq_create_mapping() to get a virq and sets the chip data to + * ps3_private data. + */ + +int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, unsigned int *virq) { int result; @@ -111,17 +122,6 @@ int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, goto fail_create; } - /* Binds outlet to cpu + virq. */ - - result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); - - if (result) { - pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", - __func__, __LINE__, ps3_result(result)); - result = -EPERM; - goto fail_connect; - } - pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, outlet, cpu, *virq); @@ -136,94 +136,118 @@ int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, return result; fail_set: - lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq); -fail_connect: irq_dispose_mapping(*virq); fail_create: return result; } -EXPORT_SYMBOL_GPL(ps3_alloc_irq); -int ps3_free_irq(unsigned int virq) +/** + * ps3_virq_destroy - virq related teardown. + * @virq: The assigned Linux virq. + * + * Clears chip data and calls irq_dispose_mapping() for the virq. + */ + +int ps3_virq_destroy(unsigned int virq) { - int result; const struct ps3_private *pd = get_irq_chip_data(virq); pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, pd->node, pd->cpu, virq); - result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); - - if (result) - pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", - __func__, __LINE__, ps3_result(result)); - set_irq_chip_data(virq, NULL); irq_dispose_mapping(virq); - return result; + + pr_debug("%s:%d <-\n", __func__, __LINE__); + return 0; } -EXPORT_SYMBOL_GPL(ps3_free_irq); /** - * ps3_alloc_io_irq - Assign a virq to a system bus device. + * ps3_irq_plug_setup - Generic outlet and virq related setup. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. - * @interrupt_id: The device interrupt id read from the system repository. + * @outlet: The HV outlet from the various create outlet routines. * @virq: The assigned Linux virq. * - * An io irq represents a non-virtualized device interrupt. interrupt_id - * coresponds to the interrupt number of the interrupt controller. + * Sets up virq and connects the irq plug. */ -int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id, +int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, unsigned int *virq) { int result; - unsigned long outlet; + struct ps3_private *pd; - result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); + result = ps3_virq_setup(cpu, outlet, virq); if (result) { - pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", - __func__, __LINE__, ps3_result(result)); - return result; + pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); + goto fail_setup; } - result = ps3_alloc_irq(cpu, outlet, virq); - BUG_ON(result); + pd = get_irq_chip_data(*virq); + + /* Binds outlet to cpu + virq. */ + + result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); + if (result) { + pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", + __func__, __LINE__, ps3_result(result)); + result = -EPERM; + goto fail_connect; + } + + return result; + +fail_connect: + ps3_virq_destroy(*virq); +fail_setup: return result; } -EXPORT_SYMBOL_GPL(ps3_alloc_io_irq); +EXPORT_SYMBOL_GPL(ps3_irq_plug_setup); + +/** + * ps3_irq_plug_destroy - Generic outlet and virq related teardown. + * @virq: The assigned Linux virq. + * + * Disconnects the irq plug and tears down virq. + * Do not call for system bus event interrupts setup with + * ps3_sb_event_receive_port_setup(). + */ -int ps3_free_io_irq(unsigned int virq) +int ps3_irq_plug_destroy(unsigned int virq) { int result; + const struct ps3_private *pd = get_irq_chip_data(virq); - result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); + pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, + pd->node, pd->cpu, virq); + + result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); if (result) - pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", - __func__, __LINE__, ps3_result(result)); + pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", + __func__, __LINE__, ps3_result(result)); - ps3_free_irq(virq); + ps3_virq_destroy(virq); return result; } -EXPORT_SYMBOL_GPL(ps3_free_io_irq); +EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy); /** - * ps3_alloc_event_irq - Allocate a virq for use with a system event. + * ps3_event_receive_port_setup - Setup an event receive port. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. * @virq: The assigned Linux virq. * * The virq can be used with lv1_connect_interrupt_event_receive_port() to - * arrange to receive events, or with ps3_send_event_locally() to signal - * events. + * arrange to receive interrupts from system-bus devices, or with + * ps3_send_event_locally() to signal events. */ -int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq) +int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq) { int result; unsigned long outlet; @@ -237,17 +261,27 @@ int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq) return result; } - result = ps3_alloc_irq(cpu, outlet, virq); + result = ps3_irq_plug_setup(cpu, outlet, virq); BUG_ON(result); return result; } +EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup); + +/** + * ps3_event_receive_port_destroy - Destroy an event receive port. + * @virq: The assigned Linux virq. + * + * Since ps3_event_receive_port_destroy destroys the receive port outlet, + * SB devices need to call disconnect_interrupt_event_receive_port() before + * this. + */ -int ps3_free_event_irq(unsigned int virq) +int ps3_event_receive_port_destroy(unsigned int virq) { int result; - pr_debug(" -> %s:%d\n", __func__, __LINE__); + pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq); result = lv1_destruct_event_receive_port(virq_to_hw(virq)); @@ -255,11 +289,17 @@ int ps3_free_event_irq(unsigned int virq) pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", __func__, __LINE__, ps3_result(result)); - ps3_free_irq(virq); + /* lv1_destruct_event_receive_port() destroys the IRQ plug, + * so don't call ps3_irq_plug_destroy() here. + */ + + result = ps3_virq_destroy(virq); + BUG_ON(result); pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } +EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy); int ps3_send_event_locally(unsigned int virq) { @@ -267,7 +307,7 @@ int ps3_send_event_locally(unsigned int virq) } /** - * ps3_connect_event_irq - Assign a virq to a system bus device. + * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. * @did: The HV device identifier read from the system repository. @@ -278,13 +318,15 @@ int ps3_send_event_locally(unsigned int virq) * coresponds to the software interrupt number. */ -int ps3_connect_event_irq(enum ps3_cpu_binding cpu, +int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, const struct ps3_device_id *did, unsigned int interrupt_id, unsigned int *virq) { + /* this should go in system-bus.c */ + int result; - result = ps3_alloc_event_irq(cpu, virq); + result = ps3_event_receive_port_setup(cpu, virq); if (result) return result; @@ -296,7 +338,7 @@ int ps3_connect_event_irq(enum ps3_cpu_binding cpu, pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" " failed: %s\n", __func__, __LINE__, ps3_result(result)); - ps3_free_event_irq(*virq); + ps3_event_receive_port_destroy(*virq); *virq = NO_IRQ; return result; } @@ -306,10 +348,13 @@ int ps3_connect_event_irq(enum ps3_cpu_binding cpu, return 0; } +EXPORT_SYMBOL(ps3_sb_event_receive_port_setup); -int ps3_disconnect_event_irq(const struct ps3_device_id *did, +int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, unsigned int interrupt_id, unsigned int virq) { + /* this should go in system-bus.c */ + int result; pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, @@ -323,14 +368,65 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did, " failed: %s\n", __func__, __LINE__, ps3_result(result)); - ps3_free_event_irq(virq); + result = ps3_event_receive_port_destroy(virq); + BUG_ON(result); pr_debug(" <- %s:%d\n", __func__, __LINE__); return result; } +EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy); /** - * ps3_alloc_vuart_irq - Configure the system virtual uart virq. + * ps3_io_irq_setup - Setup a system bus io irq. + * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be + * serviced on. + * @interrupt_id: The device interrupt id read from the system repository. + * @virq: The assigned Linux virq. + * + * An io irq represents a non-virtualized device interrupt. interrupt_id + * coresponds to the interrupt number of the interrupt controller. + */ + +int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, + unsigned int *virq) +{ + int result; + unsigned long outlet; + + result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); + + if (result) { + pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return result; + } + + result = ps3_irq_plug_setup(cpu, outlet, virq); + BUG_ON(result); + + return result; +} +EXPORT_SYMBOL_GPL(ps3_io_irq_setup); + +int ps3_io_irq_destroy(unsigned int virq) +{ + int result; + + result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); + + if (result) + pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + result = ps3_irq_plug_destroy(virq); + BUG_ON(result); + + return result; +} +EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); + +/** + * ps3_vuart_irq_setup - Setup the system virtual uart virq. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. @@ -340,7 +436,7 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did, * freeing the interrupt will return a wrong state error. */ -int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, +int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, unsigned int *virq) { int result; @@ -359,13 +455,13 @@ int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, return result; } - result = ps3_alloc_irq(cpu, outlet, virq); + result = ps3_irq_plug_setup(cpu, outlet, virq); BUG_ON(result); return result; } -int ps3_free_vuart_irq(unsigned int virq) +int ps3_vuart_irq_destroy(unsigned int virq) { int result; @@ -377,13 +473,14 @@ int ps3_free_vuart_irq(unsigned int virq) return result; } - ps3_free_irq(virq); + result = ps3_irq_plug_destroy(virq); + BUG_ON(result); return result; } /** - * ps3_alloc_spe_irq - Configure an spe virq. + * ps3_spe_irq_setup - Setup an spe virq. * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be * serviced on. * @spe_id: The spe_id returned from lv1_construct_logical_spe(). @@ -392,7 +489,7 @@ int ps3_free_vuart_irq(unsigned int virq) * */ -int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, +int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, unsigned int class, unsigned int *virq) { int result; @@ -408,15 +505,16 @@ int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, return result; } - result = ps3_alloc_irq(cpu, outlet, virq); + result = ps3_irq_plug_setup(cpu, outlet, virq); BUG_ON(result); return result; } -int ps3_free_spe_irq(unsigned int virq) +int ps3_spe_irq_destroy(unsigned int virq) { - ps3_free_irq(virq); + int result = ps3_irq_plug_destroy(virq); + BUG_ON(result); return 0; } diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c index 6fb887961a6..8729348c060 100644 --- a/arch/powerpc/platforms/ps3/smp.c +++ b/arch/powerpc/platforms/ps3/smp.c @@ -110,7 +110,7 @@ static void __init ps3_smp_setup_cpu(int cpu) BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3); for (i = 0; i < MSG_COUNT; i++) { - result = ps3_alloc_event_irq(cpu, &virqs[i]); + result = ps3_event_receive_port_setup(cpu, &virqs[i]); if (result) continue; @@ -134,11 +134,13 @@ void ps3_smp_cleanup_cpu(int cpu) int i; DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu); + for (i = 0; i < MSG_COUNT; i++) { - ps3_free_event_irq(virqs[i]); free_irq(virqs[i], (void*)(long)i); + ps3_event_receive_port_destroy(virqs[i]); virqs[i] = NO_IRQ; } + DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu); } diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index af8efe3c459..651437cb2c1 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -230,19 +230,19 @@ static int __init setup_interrupts(struct spu *spu) { int result; - result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, + result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, 0, &spu->irqs[0]); if (result) goto fail_alloc_0; - result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, + result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, 1, &spu->irqs[1]); if (result) goto fail_alloc_1; - result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, + result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, 2, &spu->irqs[2]); if (result) @@ -251,9 +251,9 @@ static int __init setup_interrupts(struct spu *spu) return result; fail_alloc_2: - ps3_free_spe_irq(spu->irqs[1]); + ps3_spe_irq_destroy(spu->irqs[1]); fail_alloc_1: - ps3_free_spe_irq(spu->irqs[0]); + ps3_spe_irq_destroy(spu->irqs[0]); fail_alloc_0: spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ; return result; @@ -301,9 +301,9 @@ static int ps3_destroy_spu(struct spu *spu) result = lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0); BUG_ON(result); - ps3_free_spe_irq(spu->irqs[2]); - ps3_free_spe_irq(spu->irqs[1]); - ps3_free_spe_irq(spu->irqs[0]); + ps3_spe_irq_destroy(spu->irqs[2]); + ps3_spe_irq_destroy(spu->irqs[1]); + ps3_spe_irq_destroy(spu->irqs[0]); spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ; diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c index 7d7cab1d91b..ec2d36a1bc6 100644 --- a/drivers/ps3/vuart.c +++ b/drivers/ps3/vuart.c @@ -886,12 +886,12 @@ static int ps3_vuart_probe(struct device *_dev) if (++vuart_bus_priv.use_count == 1) { - result = ps3_alloc_vuart_irq(PS3_BINDING_CPU_ANY, + result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, (void*)&vuart_bus_priv.bmp.status, &vuart_bus_priv.virq); if (result) { dev_dbg(&dev->core, - "%s:%d: ps3_alloc_vuart_irq failed (%d)\n", + "%s:%d: ps3_vuart_irq_setup failed (%d)\n", __func__, __LINE__, result); result = -EPERM; goto fail_alloc_irq; @@ -937,7 +937,7 @@ static int ps3_vuart_probe(struct device *_dev) fail_probe: ps3_vuart_set_interrupt_mask(dev, 0); fail_request_irq: - ps3_free_vuart_irq(vuart_bus_priv.virq); + ps3_vuart_irq_destroy(vuart_bus_priv.virq); vuart_bus_priv.virq = NO_IRQ; fail_alloc_irq: --vuart_bus_priv.use_count; @@ -975,7 +975,7 @@ static int ps3_vuart_remove(struct device *_dev) if (--vuart_bus_priv.use_count == 0) { BUG(); free_irq(vuart_bus_priv.virq, &vuart_bus_priv); - ps3_free_vuart_irq(vuart_bus_priv.virq); + ps3_vuart_irq_destroy(vuart_bus_priv.virq); vuart_bus_priv.virq = NO_IRQ; } diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 4d781a2a980..29dcd27b55d 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -104,7 +104,7 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev) dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__, __LINE__, dev->m_region->lpar_addr); - result = ps3_alloc_io_irq(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq); + result = ps3_io_irq_setup(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq); if (result) { dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n", @@ -162,7 +162,7 @@ fail_add_hcd: fail_ioremap: usb_put_hcd(hcd); fail_create_hcd: - ps3_free_io_irq(virq); + ps3_io_irq_destroy(virq); fail_irq: ps3_free_mmio_region(dev->m_region); fail_mmio: diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 62283a3926d..93a6eb0de2d 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -107,7 +107,7 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev) dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__, __LINE__, dev->m_region->lpar_addr); - result = ps3_alloc_io_irq(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq); + result = ps3_io_irq_setup(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq); if (result) { dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n", @@ -165,7 +165,7 @@ fail_add_hcd: fail_ioremap: usb_put_hcd(hcd); fail_create_hcd: - ps3_free_io_irq(virq); + ps3_io_irq_destroy(virq); fail_irq: ps3_free_mmio_region(dev->m_region); fail_mmio: diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 81e43cda7d8..a9ca4986645 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -885,8 +885,8 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) } ps3fb.dev = dev; - error = ps3_alloc_irq(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, - &ps3fb.irq_no); + error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, + &ps3fb.irq_no); if (error) { printk(KERN_ERR "%s: ps3_alloc_irq failed %d\n", __FUNCTION__, error); @@ -898,7 +898,7 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) if (error) { printk(KERN_ERR "%s: request_irq failed %d\n", __FUNCTION__, error); - ps3_free_irq(ps3fb.irq_no); + ps3_irq_plug_destroy(ps3fb.irq_no); return error; } @@ -1059,7 +1059,7 @@ err_framebuffer_release: framebuffer_release(info); err_free_irq: free_irq(ps3fb.irq_no, ps3fb.dev); - ps3_free_irq(ps3fb.irq_no); + ps3_irq_plug_destroy(ps3fb.irq_no); err_iounmap_dinfo: iounmap((u8 __iomem *)ps3fb.dinfo); err_gpu_context_free: @@ -1075,7 +1075,7 @@ static void ps3fb_shutdown(struct platform_device *dev) ps3fb_flip_ctl(0); /* flip off */ ps3fb.dinfo->irq.mask = 0; free_irq(ps3fb.irq_no, ps3fb.dev); - ps3_free_irq(ps3fb.irq_no); + ps3_irq_plug_destroy(ps3fb.irq_no); iounmap((u8 __iomem *)ps3fb.dinfo); } @@ -1085,7 +1085,7 @@ void ps3fb_cleanup(void) if (ps3fb.irq_no) { free_irq(ps3fb.irq_no, ps3fb.dev); - ps3_free_irq(ps3fb.irq_no); + ps3_irq_plug_destroy(ps3fb.irq_no); } iounmap((u8 __iomem *)ps3fb.dinfo); diff --git a/include/asm-powerpc/ps3.h b/include/asm-powerpc/ps3.h index 821581a8b64..13c372df99e 100644 --- a/include/asm-powerpc/ps3.h +++ b/include/asm-powerpc/ps3.h @@ -167,26 +167,31 @@ enum ps3_cpu_binding { PS3_BINDING_CPU_1 = 1, }; -int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id, +int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, unsigned int *virq); -int ps3_free_io_irq(unsigned int virq); -int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq); -int ps3_free_event_irq(unsigned int virq); +int ps3_virq_destroy(unsigned int virq); +int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, + unsigned int *virq); +int ps3_irq_plug_destroy(unsigned int virq); +int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq); +int ps3_event_receive_port_destroy(unsigned int virq); int ps3_send_event_locally(unsigned int virq); -int ps3_connect_event_irq(enum ps3_cpu_binding cpu, - const struct ps3_device_id *did, unsigned int interrupt_id, + +int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, unsigned int *virq); -int ps3_disconnect_event_irq(const struct ps3_device_id *did, - unsigned int interrupt_id, unsigned int virq); -int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, +int ps3_io_irq_destroy(unsigned int virq); +int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, unsigned int *virq); -int ps3_free_vuart_irq(unsigned int virq); -int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, +int ps3_vuart_irq_destroy(unsigned int virq); +int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, unsigned int class, unsigned int *virq); -int ps3_free_spe_irq(unsigned int virq); -int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, +int ps3_spe_irq_destroy(unsigned int virq); + +int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu, + const struct ps3_device_id *did, unsigned int interrupt_id, unsigned int *virq); -int ps3_free_irq(unsigned int virq); +int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did, + unsigned int interrupt_id, unsigned int virq); /* lv1 result codes */ -- cgit v1.2.3 From eb609e52d188775da738a1ffd1e982e6212c77d7 Mon Sep 17 00:00:00 2001 From: Srinivasa Ds Date: Mon, 23 Apr 2007 11:28:49 +0530 Subject: [POWERPC] Transparently handle <.symbol> lookup for kprobes When data symbols are not present in kernel image, user needs to add dot(".") before function name explicitly, that he wants to probe in kprobe module on ppc64. for ex:- When data symbols are missing on ppc64, ==================== [root@llm27lp1 ~]# cat /proc/kallsyms | grep do_fork c00000000006283c T .do_fork ============================== User needs add "." to "do_fork" kp.symbol_name = ".do_fork"; ============================ This makes kprobe modules unportable. This fixes the problem. Signed-off-by: Srinivasa Ds Signed-off-by: Ananth N Mavinakayanahalli Signed-off-by: Paul Mackerras --- include/asm-powerpc/kprobes.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/asm-powerpc/kprobes.h b/include/asm-powerpc/kprobes.h index f850ca7020e..fc713e59598 100644 --- a/include/asm-powerpc/kprobes.h +++ b/include/asm-powerpc/kprobes.h @@ -64,6 +64,12 @@ typedef unsigned int kprobe_opcode_t; addr = *(kprobe_opcode_t **)addr; \ } else if (name[0] != '.') \ addr = *(kprobe_opcode_t **)addr; \ + } else { \ + char dot_name[KSYM_NAME_LEN+1]; \ + dot_name[0] = '.'; \ + dot_name[1] = '\0'; \ + strncat(dot_name, name, KSYM_NAME_LEN); \ + addr = (kprobe_opcode_t *)kallsyms_lookup_name(dot_name); \ } \ } -- cgit v1.2.3 From 9f90b997de4efd5404a8c52f89c400f0f4e2d216 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 30 Apr 2007 11:56:46 +0100 Subject: [POWERPC] Minor fault path optimization Call the kprobes pagefault handler directly instead of going through the complex notifier chain. Signed-off-by: Christoph Hellwig Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/kprobes.c | 10 +--------- arch/powerpc/mm/fault.c | 42 +++++++++++++++--------------------------- include/asm-powerpc/kdebug.h | 17 ++++++++++++++--- include/asm-powerpc/kprobes.h | 1 + 4 files changed, 31 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index ef647e7a9dc..c27f41870c8 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -410,7 +410,7 @@ out: return 1; } -static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) +int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) { struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); @@ -495,14 +495,6 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, if (post_kprobe_handler(args->regs)) ret = NOTIFY_STOP; break; - case DIE_PAGE_FAULT: - /* kprobe_running() needs smp_processor_id() */ - preempt_disable(); - if (kprobe_running() && - kprobe_fault_handler(args->regs, args->trapnr)) - ret = NOTIFY_STOP; - preempt_enable(); - break; default: break; } diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 03aeb3a4607..a0f88026e46 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -39,37 +39,26 @@ #include #include -#ifdef CONFIG_KPROBES -ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); -/* Hook to register for page fault notifications */ -int register_page_fault_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(¬ify_page_fault_chain, nb); -} - -int unregister_page_fault_notifier(struct notifier_block *nb) +#ifdef CONFIG_KPROBES +static inline int notify_page_fault(struct pt_regs *regs) { - return atomic_notifier_chain_unregister(¬ify_page_fault_chain, nb); -} + int ret = 0; + + /* kprobe_running() needs smp_processor_id() */ + if (!user_mode(regs)) { + preempt_disable(); + if (kprobe_running() && kprobe_fault_handler(regs, 11)) + ret = 1; + preempt_enable(); + } -static inline int notify_page_fault(enum die_val val, const char *str, - struct pt_regs *regs, long err, int trap, int sig) -{ - struct die_args args = { - .regs = regs, - .str = str, - .err = err, - .trapnr = trap, - .signr = sig - }; - return atomic_notifier_call_chain(¬ify_page_fault_chain, val, &args); + return ret; } #else -static inline int notify_page_fault(enum die_val val, const char *str, - struct pt_regs *regs, long err, int trap, int sig) +static inline int notify_page_fault(struct pt_regs *regs) { - return NOTIFY_DONE; + return 0; } #endif @@ -175,8 +164,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, is_write = error_code & ESR_DST; #endif /* CONFIG_4xx || CONFIG_BOOKE */ - if (notify_page_fault(DIE_PAGE_FAULT, "page_fault", regs, error_code, - 11, SIGSEGV) == NOTIFY_STOP) + if (notify_page_fault(regs)) return 0; if (trap == 0x300) { diff --git a/include/asm-powerpc/kdebug.h b/include/asm-powerpc/kdebug.h index 532bfee934f..df996b933de 100644 --- a/include/asm-powerpc/kdebug.h +++ b/include/asm-powerpc/kdebug.h @@ -18,8 +18,20 @@ struct die_args { extern int register_die_notifier(struct notifier_block *); extern int unregister_die_notifier(struct notifier_block *); -extern int register_page_fault_notifier(struct notifier_block *); -extern int unregister_page_fault_notifier(struct notifier_block *); + +/* + * These are only here because kprobes.c wants them to implement a + * blatant layering violation. Will hopefully go away soon once all + * architectures are updated. + */ +static inline int register_page_fault_notifier(struct notifier_block *nb) +{ + return 0; +} +static inline int unregister_page_fault_notifier(struct notifier_block *nb) +{ + return 0; +} extern struct atomic_notifier_head powerpc_die_chain; /* Grossly misnamed. */ @@ -29,7 +41,6 @@ enum die_val { DIE_DABR_MATCH, DIE_BPT, DIE_SSTEP, - DIE_PAGE_FAULT, }; static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig) diff --git a/include/asm-powerpc/kprobes.h b/include/asm-powerpc/kprobes.h index fc713e59598..b0e40ff32ee 100644 --- a/include/asm-powerpc/kprobes.h +++ b/include/asm-powerpc/kprobes.h @@ -116,5 +116,6 @@ struct kprobe_ctlblk { extern int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data); +extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_KPROBES_H */ -- cgit v1.2.3 From 3669e930481d6dd510718279cd4bacb15ca3ae91 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 May 2007 16:33:41 +1000 Subject: [POWERPC] MPIC sys_device & suspend/resume This adds mpic to the system devices and implements suspend and resume for them. This is necessary to get interrupts for modules back to where they were before a suspend to disk. Signed-off-by: Johannes Berg Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/sysdev/mpic.c | 96 +++++++++++++++++++++++++++++++++++++++++++++- include/asm-powerpc/mpic.h | 15 ++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 57b1208ef1c..e88bbd5ec4c 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -354,6 +354,12 @@ static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, tmp |= 0x22; writel(tmp, fixup->base + 4); spin_unlock_irqrestore(&mpic->fixup_lock, flags); + +#ifdef CONFIG_PM + /* use the lowest bit inverted to the actual HW, + * set if this fixup was enabled, clear otherwise */ + mpic->save_data[source].fixup_data = tmp | 1; +#endif } static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, @@ -375,6 +381,12 @@ static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, tmp |= 1; writel(tmp, fixup->base + 4); spin_unlock_irqrestore(&mpic->fixup_lock, flags); + +#ifdef CONFIG_PM + /* use the lowest bit inverted to the actual HW, + * set if this fixup was enabled, clear otherwise */ + mpic->save_data[source].fixup_data = tmp & ~1; +#endif } static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, @@ -1143,7 +1155,7 @@ void __init mpic_init(struct mpic *mpic) /* Do the HT PIC fixups on U3 broken mpic */ DBG("MPIC flags: %x\n", mpic->flags); if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY)) - mpic_scan_ht_pics(mpic); + mpic_scan_ht_pics(mpic); for (i = 0; i < mpic->num_sources; i++) { /* start with vector = source number, and masked */ @@ -1167,6 +1179,12 @@ void __init mpic_init(struct mpic *mpic) /* Set current processor priority to 0 */ mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); + +#ifdef CONFIG_PM + /* allocate memory to save mpic state */ + mpic->save_data = alloc_bootmem(mpic->num_sources * sizeof(struct mpic_irq_save)); + BUG_ON(mpic->save_data == NULL); +#endif } void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio) @@ -1420,3 +1438,79 @@ void __devinit smp_mpic_setup_cpu(int cpu) mpic_setup_this_cpu(); } #endif /* CONFIG_SMP */ + +#ifdef CONFIG_PM +static int mpic_suspend(struct sys_device *dev, pm_message_t state) +{ + struct mpic *mpic = container_of(dev, struct mpic, sysdev); + int i; + + for (i = 0; i < mpic->num_sources; i++) { + mpic->save_data[i].vecprio = + mpic_irq_read(i, MPIC_INFO(IRQ_VECTOR_PRI)); + mpic->save_data[i].dest = + mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)); + } + + return 0; +} + +static int mpic_resume(struct sys_device *dev) +{ + struct mpic *mpic = container_of(dev, struct mpic, sysdev); + int i; + + for (i = 0; i < mpic->num_sources; i++) { + mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), + mpic->save_data[i].vecprio); + mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), + mpic->save_data[i].dest); + +#ifdef CONFIG_MPIC_U3_HT_IRQS + { + struct mpic_irq_fixup *fixup = &mpic->fixups[i]; + + if (fixup->base) { + /* we use the lowest bit in an inverted meaning */ + if ((mpic->save_data[i].fixup_data & 1) == 0) + continue; + + /* Enable and configure */ + writeb(0x10 + 2 * fixup->index, fixup->base + 2); + + writel(mpic->save_data[i].fixup_data & ~1, + fixup->base + 4); + } + } +#endif + } /* end for loop */ + + return 0; +} +#endif + +static struct sysdev_class mpic_sysclass = { +#ifdef CONFIG_PM + .resume = mpic_resume, + .suspend = mpic_suspend, +#endif + set_kset_name("mpic"), +}; + +static int mpic_init_sys(void) +{ + struct mpic *mpic = mpics; + int error, id = 0; + + error = sysdev_class_register(&mpic_sysclass); + + while (mpic && !error) { + mpic->sysdev.cls = &mpic_sysclass; + mpic->sysdev.id = id++; + error = sysdev_register(&mpic->sysdev); + mpic = mpic->next; + } + return error; +} + +device_initcall(mpic_init_sys); diff --git a/include/asm-powerpc/mpic.h b/include/asm-powerpc/mpic.h index e4d5fc5362a..22c85c4f5b2 100644 --- a/include/asm-powerpc/mpic.h +++ b/include/asm-powerpc/mpic.h @@ -3,6 +3,7 @@ #ifdef __KERNEL__ #include +#include #include /* @@ -228,6 +229,14 @@ struct mpic_reg_bank { #endif /* CONFIG_PPC_DCR */ }; +struct mpic_irq_save { + u32 vecprio, + dest; +#ifdef CONFIG_MPIC_U3_HT_IRQS + u32 fixup_data; +#endif +}; + /* The instance data of a given MPIC */ struct mpic { @@ -294,6 +303,12 @@ struct mpic /* link */ struct mpic *next; + + struct sys_device sysdev; + +#ifdef CONFIG_PM + struct mpic_irq_save *save_data; +#endif }; /* -- cgit v1.2.3 From d9333afd6a714760c13f76ba275a32ec7bd979c1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 3 May 2007 06:33:51 +1000 Subject: [POWERPC] powermac: Support G5 CPU hotplug This allows "hotplugging" of CPUs on G5 machines. CPUs that are disabled are put into an idle loop with the decrementer frequency set to minimum. To wake them up again we kick them just like when bringing them up. To stop those CPUs from messing with any global state we stop them from entering the timer interrupt. Signed-off-by: Johannes Berg Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/idle_power4.S | 21 +++++++++++++ arch/powerpc/platforms/powermac/setup.c | 52 +++++++++++++++++++++++++++++++-- arch/powerpc/platforms/powermac/smp.c | 12 ++++++-- include/asm-powerpc/machdep.h | 1 + 4 files changed, 82 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/idle_power4.S b/arch/powerpc/kernel/idle_power4.S index ba319547860..5328709eeed 100644 --- a/arch/powerpc/kernel/idle_power4.S +++ b/arch/powerpc/kernel/idle_power4.S @@ -53,3 +53,24 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) isync b 1b +_GLOBAL(power4_cpu_offline_powersave) + /* Go to NAP now */ + mfmsr r7 + rldicl r0,r7,48,1 + rotldi r0,r0,16 + mtmsrd r0,1 /* hard-disable interrupts */ + li r0,1 + li r6,0 + stb r0,PACAHARDIRQEN(r13) /* we'll hard-enable shortly */ + stb r6,PACASOFTIRQEN(r13) /* soft-disable irqs */ +BEGIN_FTR_SECTION + DSSALL + sync +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + ori r7,r7,MSR_EE + oris r7,r7,MSR_POW@h + sync + isync + mtmsrd r7 + isync + blr diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 5ae57e17d2b..e365bff74d8 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -444,6 +444,9 @@ static int initializing = 1; static int pmac_late_init(void) { initializing = 0; + /* this is udbg (which is __init) and we can later use it during + * cpu hotplug (in smp_core99_kick_cpu) */ + ppc_md.progress = NULL; return 0; } @@ -661,7 +664,52 @@ static int pmac_pci_probe_mode(struct pci_bus *bus) return PCI_PROBE_NORMAL; return PCI_PROBE_DEVTREE; } -#endif + +#ifdef CONFIG_HOTPLUG_CPU +/* access per cpu vars from generic smp.c */ +DECLARE_PER_CPU(int, cpu_state); + +static void pmac_cpu_die(void) +{ + /* + * turn off as much as possible, we'll be + * kicked out as this will only be invoked + * on core99 platforms for now ... + */ + + printk(KERN_INFO "CPU#%d offline\n", smp_processor_id()); + __get_cpu_var(cpu_state) = CPU_DEAD; + smp_wmb(); + + /* + * during the path that leads here preemption is disabled, + * reenable it now so that when coming up preempt count is + * zero correctly + */ + preempt_enable(); + + /* + * hard-disable interrupts for the non-NAP case, the NAP code + * needs to re-enable interrupts (but soft-disables them) + */ + hard_irq_disable(); + + while (1) { + /* let's not take timer interrupts too often ... */ + set_dec(0x7fffffff); + + /* should always be true at this point */ + if (cpu_has_feature(CPU_FTR_CAN_NAP)) + power4_cpu_offline_powersave(); + else { + HMT_low(); + HMT_very_low(); + } + } +} +#endif /* CONFIG_HOTPLUG_CPU */ + +#endif /* CONFIG_PPC64 */ define_machine(powermac) { .name = "PowerMac", @@ -698,6 +746,6 @@ define_machine(powermac) { .phys_mem_access_prot = pci_phys_mem_access_prot, #endif #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC64) - .cpu_die = generic_mach_cpu_die, + .cpu_die = pmac_cpu_die, #endif }; diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index 6f32c4eca6e..33035411318 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -900,7 +900,7 @@ void smp_core99_cpu_die(unsigned int cpu) cpu_dead[cpu] = 0; } -#endif +#endif /* CONFIG_HOTPLUG_CPU && CONFIG_PP32 */ /* Core99 Macs (dual G4s and G5s) */ struct smp_ops_t core99_smp_ops = { @@ -910,8 +910,16 @@ struct smp_ops_t core99_smp_ops = { .setup_cpu = smp_core99_setup_cpu, .give_timebase = smp_core99_give_timebase, .take_timebase = smp_core99_take_timebase, -#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC32) +#if defined(CONFIG_HOTPLUG_CPU) +# if defined(CONFIG_PPC32) .cpu_disable = smp_core99_cpu_disable, .cpu_die = smp_core99_cpu_die, +# endif +# if defined(CONFIG_PPC64) + .cpu_disable = generic_cpu_disable, + .cpu_die = generic_cpu_die, + /* intentionally do *NOT* assign cpu_enable, + * the generic code will use kick_cpu then! */ +# endif #endif }; diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index b204926ce91..bbd17d0e170 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -248,6 +248,7 @@ struct machdep_calls { }; extern void power4_idle(void); +extern void power4_cpu_offline_powersave(void); extern void ppc6xx_idle(void); /* -- cgit v1.2.3 From 7e11580b362fc64693de7ad5c11fbf3d1d9d0e50 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 3 May 2007 22:28:32 +1000 Subject: [POWERPC] DART iommu suspend This implements save and restore hooks for IOMMUs and implements it the DART iommu. Signed-off-by: Johannes Berg Acked-by: Benjamin Herrenschmidt Cc: Olof Johansson Signed-off-by: Paul Mackerras --- arch/powerpc/sysdev/dart_iommu.c | 46 ++++++++++++++++++++++++++++++++++++++++ include/asm-powerpc/iommu.h | 14 ++++++++++++ include/asm-powerpc/machdep.h | 5 +++++ 3 files changed, 65 insertions(+) (limited to 'include') diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c index 336186dd7f1..a1d2042bb30 100644 --- a/arch/powerpc/sysdev/dart_iommu.c +++ b/arch/powerpc/sysdev/dart_iommu.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,9 @@ static unsigned long dart_tablesize; /* Virtual base address of the DART table */ static u32 *dart_vbase; +#ifdef CONFIG_PM +static u32 *dart_copy; +#endif /* Mapped base address for the dart */ static unsigned int __iomem *dart; @@ -346,6 +350,48 @@ void iommu_init_early_dart(void) set_pci_dma_ops(&dma_direct_ops); } +#ifdef CONFIG_PM +static void iommu_dart_save(void) +{ + memcpy(dart_copy, dart_vbase, 2*1024*1024); +} + +static void iommu_dart_restore(void) +{ + memcpy(dart_vbase, dart_copy, 2*1024*1024); + dart_tlb_invalidate_all(); +} + +static int __init iommu_init_late_dart(void) +{ + unsigned long tbasepfn; + struct page *p; + + /* if no dart table exists then we won't need to save it + * and the area has also not been reserved */ + if (!dart_tablebase) + return 0; + + tbasepfn = __pa(dart_tablebase) >> PAGE_SHIFT; + register_nosave_region_late(tbasepfn, + tbasepfn + ((1<<24) >> PAGE_SHIFT)); + + /* For suspend we need to copy the dart contents because + * it is not part of the regular mapping (see above) and + * thus not saved automatically. The memory for this copy + * must be allocated early because we need 2 MB. */ + p = alloc_pages(GFP_KERNEL, 21 - PAGE_SHIFT); + BUG_ON(!p); + dart_copy = page_address(p); + + ppc_md.iommu_save = iommu_dart_save; + ppc_md.iommu_restore = iommu_dart_restore; + + return 0; +} + +late_initcall(iommu_init_late_dart); +#endif void __init alloc_dart_table(void) { diff --git a/include/asm-powerpc/iommu.h b/include/asm-powerpc/iommu.h index b2e56b30306..870967e4720 100644 --- a/include/asm-powerpc/iommu.h +++ b/include/asm-powerpc/iommu.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,19 @@ static inline void pci_iommu_init(void) { } #endif extern void alloc_dart_table(void); +#if defined(CONFIG_PPC64) && defined(CONFIG_PM) +static inline void iommu_save(void) +{ + if (ppc_md.iommu_save) + ppc_md.iommu_save(); +} + +static inline void iommu_restore(void) +{ + if (ppc_md.iommu_restore) + ppc_md.iommu_restore(); +} +#endif #endif /* __KERNEL__ */ #endif /* _ASM_IOMMU_H */ diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index bbd17d0e170..6fdabd6dfb3 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -91,6 +91,11 @@ struct machdep_calls { void __iomem * (*ioremap)(phys_addr_t addr, unsigned long size, unsigned long flags); void (*iounmap)(volatile void __iomem *token); + +#ifdef CONFIG_PM + void (*iommu_save)(void); + void (*iommu_restore)(void); +#endif #endif /* CONFIG_PPC64 */ int (*probe)(void); -- cgit v1.2.3 From 543b9fd3528f64c4b20439de0edb453764482de7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 3 May 2007 22:31:38 +1000 Subject: [POWERPC] powermac: Suspend to disk on G5 Powermac G5 suspend to disk implementation. The code is platform agnostic but only tested on powermac, no other 64-bit powerpc machines. Because nvidiafb still breaks suspend I have marked it EXPERIMENTAL on powermac and because I can't test it and some lowlevel code will need changes it is BROKEN on all other 64-bit platforms. Signed-off-by: Johannes Berg Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/Kconfig | 5 + arch/powerpc/kernel/Makefile | 1 + arch/powerpc/kernel/asm-offsets.c | 4 +- arch/powerpc/kernel/idle.c | 5 +- arch/powerpc/kernel/swsusp.c | 9 ++ arch/powerpc/kernel/swsusp_64.c | 24 ++++ arch/powerpc/kernel/swsusp_asm64.S | 228 +++++++++++++++++++++++++++++++++++++ include/linux/suspend.h | 2 +- kernel/power/Kconfig | 4 +- 9 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 arch/powerpc/kernel/swsusp_64.c create mode 100644 arch/powerpc/kernel/swsusp_asm64.S (limited to 'include') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index a8e08f4b62d..49b1ea275eb 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -126,6 +126,11 @@ config DEFAULT_UIMAGE Used to allow a board to specify it wants a uImage built by default default n +config PPC64_SWSUSP + bool + depends on PPC64 && (BROKEN || (PPC_PMAC64 && EXPERIMENTAL)) + default y + menu "Processor support" choice prompt "Processor Type" diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 949f36a62aa..4dc73b8c25b 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_6xx) += idle_6xx.o l2cr_6xx.o cpu_setup_6xx.o obj-$(CONFIG_TAU) += tau_6xx.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj32-$(CONFIG_SOFTWARE_SUSPEND) += swsusp_32.o +obj64-$(CONFIG_SOFTWARE_SUSPEND) += swsusp_64.o swsusp_asm64.o obj32-$(CONFIG_MODULES) += module_32.o ifeq ($(CONFIG_PPC_MERGE),y) diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 0c5150c6917..8f48560b7ee 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -21,12 +21,12 @@ #include #include #include +#include #ifdef CONFIG_PPC64 #include #include #else #include -#include #endif #include @@ -257,11 +257,11 @@ int main(void) DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup)); DEFINE(CPU_SPEC_RESTORE, offsetof(struct cpu_spec, cpu_restore)); -#ifndef CONFIG_PPC64 DEFINE(pbe_address, offsetof(struct pbe, address)); DEFINE(pbe_orig_address, offsetof(struct pbe, orig_address)); DEFINE(pbe_next, offsetof(struct pbe, next)); +#ifndef CONFIG_PPC64 DEFINE(TASK_SIZE, TASK_SIZE); DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28); #endif /* ! CONFIG_PPC64 */ diff --git a/arch/powerpc/kernel/idle.c b/arch/powerpc/kernel/idle.c index 6e7f50967ba..a9e9cbd3297 100644 --- a/arch/powerpc/kernel/idle.c +++ b/arch/powerpc/kernel/idle.c @@ -33,8 +33,11 @@ #include #ifdef CONFIG_HOTPLUG_CPU +/* this is used for software suspend, and that shuts down + * CPUs even while the system is still booting... */ #define cpu_should_die() (cpu_is_offline(smp_processor_id()) && \ - system_state == SYSTEM_RUNNING) + (system_state == SYSTEM_RUNNING \ + || system_state == SYSTEM_BOOTING)) #else #define cpu_should_die() 0 #endif diff --git a/arch/powerpc/kernel/swsusp.c b/arch/powerpc/kernel/swsusp.c index b89e4f5a0b0..064a7ba4f02 100644 --- a/arch/powerpc/kernel/swsusp.c +++ b/arch/powerpc/kernel/swsusp.c @@ -24,6 +24,11 @@ void save_processor_state(void) flush_fp_to_thread(current); flush_altivec_to_thread(current); flush_spe_to_thread(current); + +#ifdef CONFIG_PPC64 + hard_irq_disable(); +#endif + } void restore_processor_state(void) @@ -31,4 +36,8 @@ void restore_processor_state(void) #ifdef CONFIG_PPC32 set_context(current->active_mm->context.id, current->active_mm->pgd); #endif + +#ifdef CONFIG_PPC64 + hard_irq_enable(); +#endif } diff --git a/arch/powerpc/kernel/swsusp_64.c b/arch/powerpc/kernel/swsusp_64.c new file mode 100644 index 00000000000..6f3f0697274 --- /dev/null +++ b/arch/powerpc/kernel/swsusp_64.c @@ -0,0 +1,24 @@ +/* + * PowerPC 64-bit swsusp implementation + * + * Copyright 2006 Johannes Berg + * + * GPLv2 + */ + +#include +#include +#include +#include + +void do_after_copyback(void) +{ + iommu_restore(); + touch_softlockup_watchdog(); + mb(); +} + +void _iommu_save(void) +{ + iommu_save(); +} diff --git a/arch/powerpc/kernel/swsusp_asm64.S b/arch/powerpc/kernel/swsusp_asm64.S new file mode 100644 index 00000000000..e092c3cbdb9 --- /dev/null +++ b/arch/powerpc/kernel/swsusp_asm64.S @@ -0,0 +1,228 @@ +/* + * PowerPC 64-bit swsusp implementation + * + * Copyright 2006 Johannes Berg + * + * GPLv2 + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Structure for storing CPU registers on the save area. + */ +#define SL_r1 0x00 /* stack pointer */ +#define SL_PC 0x08 +#define SL_MSR 0x10 +#define SL_SDR1 0x18 +#define SL_XER 0x20 +#define SL_TB 0x40 +#define SL_r2 0x48 +#define SL_CR 0x50 +#define SL_LR 0x58 +#define SL_r12 0x60 +#define SL_r13 0x68 +#define SL_r14 0x70 +#define SL_r15 0x78 +#define SL_r16 0x80 +#define SL_r17 0x88 +#define SL_r18 0x90 +#define SL_r19 0x98 +#define SL_r20 0xa0 +#define SL_r21 0xa8 +#define SL_r22 0xb0 +#define SL_r23 0xb8 +#define SL_r24 0xc0 +#define SL_r25 0xc8 +#define SL_r26 0xd0 +#define SL_r27 0xd8 +#define SL_r28 0xe0 +#define SL_r29 0xe8 +#define SL_r30 0xf0 +#define SL_r31 0xf8 +#define SL_SIZE SL_r31+8 + +/* these macros rely on the save area being + * pointed to by r11 */ +#define SAVE_SPECIAL(special) \ + mf##special r0 ;\ + std r0, SL_##special(r11) +#define RESTORE_SPECIAL(special) \ + ld r0, SL_##special(r11) ;\ + mt##special r0 +#define SAVE_REGISTER(reg) \ + std reg, SL_##reg(r11) +#define RESTORE_REGISTER(reg) \ + ld reg, SL_##reg(r11) + +/* space for storing cpu state */ + .section .data + .align 5 +swsusp_save_area: + .space SL_SIZE + + .section ".toc","aw" +swsusp_save_area_ptr: + .tc swsusp_save_area[TC],swsusp_save_area +restore_pblist_ptr: + .tc restore_pblist[TC],restore_pblist + + .section .text + .align 5 +_GLOBAL(swsusp_arch_suspend) + ld r11,swsusp_save_area_ptr@toc(r2) + SAVE_SPECIAL(LR) + SAVE_REGISTER(r1) + SAVE_SPECIAL(CR) + SAVE_SPECIAL(TB) + SAVE_REGISTER(r2) + SAVE_REGISTER(r12) + SAVE_REGISTER(r13) + SAVE_REGISTER(r14) + SAVE_REGISTER(r15) + SAVE_REGISTER(r16) + SAVE_REGISTER(r17) + SAVE_REGISTER(r18) + SAVE_REGISTER(r19) + SAVE_REGISTER(r20) + SAVE_REGISTER(r21) + SAVE_REGISTER(r22) + SAVE_REGISTER(r23) + SAVE_REGISTER(r24) + SAVE_REGISTER(r25) + SAVE_REGISTER(r26) + SAVE_REGISTER(r27) + SAVE_REGISTER(r28) + SAVE_REGISTER(r29) + SAVE_REGISTER(r30) + SAVE_REGISTER(r31) + SAVE_SPECIAL(MSR) + SAVE_SPECIAL(SDR1) + SAVE_SPECIAL(XER) + + /* we push the stack up 128 bytes but don't store the + * stack pointer on the stack like a real stackframe */ + addi r1,r1,-128 + + bl _iommu_save + bl swsusp_save + + /* restore LR */ + ld r11,swsusp_save_area_ptr@toc(r2) + RESTORE_SPECIAL(LR) + addi r1,r1,128 + + blr + +/* Resume code */ +_GLOBAL(swsusp_arch_resume) + /* Stop pending alitvec streams and memory accesses */ +BEGIN_FTR_SECTION + DSSALL +END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) + sync + + ld r12,restore_pblist_ptr@toc(r2) + ld r12,0(r12) + + cmpdi r12,0 + beq- nothing_to_copy + li r15,512 +copyloop: + ld r13,pbe_address(r12) + ld r14,pbe_orig_address(r12) + + mtctr r15 + li r10,0 +copy_page_loop: + ldx r0,r10,r13 + stdx r0,r10,r14 + addi r10,r10,8 + bdnz copy_page_loop + + ld r12,pbe_next(r12) + cmpdi r12,0 + bne+ copyloop +nothing_to_copy: + + /* flush caches */ + lis r3, 0x10 + mtctr r3 + li r3, 0 + ori r3, r3, CONFIG_KERNEL_START>>48 + li r0, 48 + sld r3, r3, r0 + li r0, 0 +1: + dcbf r0,r3 + addi r3,r3,0x20 + bdnz 1b + + sync + + tlbia + + ld r11,swsusp_save_area_ptr@toc(r2) + + RESTORE_SPECIAL(CR) + + /* restore timebase */ + /* load saved tb */ + ld r1, SL_TB(r11) + /* get upper 32 bits of it */ + srdi r2, r1, 32 + /* clear tb lower to avoid wrap */ + li r0, 0 + mttbl r0 + /* set tb upper */ + mttbu r2 + /* set tb lower */ + mttbl r1 + + /* restore registers */ + RESTORE_REGISTER(r1) + RESTORE_REGISTER(r2) + RESTORE_REGISTER(r12) + RESTORE_REGISTER(r13) + RESTORE_REGISTER(r14) + RESTORE_REGISTER(r15) + RESTORE_REGISTER(r16) + RESTORE_REGISTER(r17) + RESTORE_REGISTER(r18) + RESTORE_REGISTER(r19) + RESTORE_REGISTER(r20) + RESTORE_REGISTER(r21) + RESTORE_REGISTER(r22) + RESTORE_REGISTER(r23) + RESTORE_REGISTER(r24) + RESTORE_REGISTER(r25) + RESTORE_REGISTER(r26) + RESTORE_REGISTER(r27) + RESTORE_REGISTER(r28) + RESTORE_REGISTER(r29) + RESTORE_REGISTER(r30) + RESTORE_REGISTER(r31) + /* can't use RESTORE_SPECIAL(MSR) */ + ld r0, SL_MSR(r11) + mtmsrd r0, 0 + RESTORE_SPECIAL(SDR1) + RESTORE_SPECIAL(XER) + + sync + + addi r1,r1,-128 + bl slb_flush_and_rebolt + bl do_after_copyback + addi r1,r1,128 + + ld r11,swsusp_save_area_ptr@toc(r2) + RESTORE_SPECIAL(LR) + + li r3, 0 + blr diff --git a/include/linux/suspend.h b/include/linux/suspend.h index bf99bd49f8e..a34835ae0e7 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -1,7 +1,7 @@ #ifndef _LINUX_SWSUSP_H #define _LINUX_SWSUSP_H -#if defined(CONFIG_X86) || defined(CONFIG_FRV) || defined(CONFIG_PPC32) +#if defined(CONFIG_X86) || defined(CONFIG_FRV) || defined(CONFIG_PPC32) || defined(CONFIG_PPC64) #include #endif #include diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 51a4dd0f1b7..5001c652028 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -79,7 +79,7 @@ config PM_SYSFS_DEPRECATED config SOFTWARE_SUSPEND bool "Software Suspend" - depends on PM && SWAP && ((X86 && (!SMP || SUSPEND_SMP)) || ((FRV || PPC32) && !SMP)) + depends on PM && SWAP && (((X86 || PPC64_SWSUSP) && (!SMP || SUSPEND_SMP)) || ((FRV || PPC32) && !SMP)) ---help--- Enable the suspend to disk (STD) functionality. @@ -134,7 +134,7 @@ config PM_STD_PARTITION config SUSPEND_SMP bool - depends on HOTPLUG_CPU && X86 && PM + depends on HOTPLUG_CPU && (X86 || PPC64) && PM default y config APM_EMULATION -- cgit v1.2.3 From 0aeafb0cef401807fe7d2a50f298203659b668af Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 4 May 2007 16:47:51 +1000 Subject: [POWERPC] Kill off the PTE_FMT macro 32-bit powerpc uses a PTE_FMT macro to handle printk() formatting of PTE entries (which can vary in type and size). Apparently there was a good reason for it once, but with current compilers it's simpler just to workaround the variation with a cast in the printk() itself (there's only one use). Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- include/asm-powerpc/page_32.h | 2 -- include/asm-powerpc/pgtable-ppc32.h | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/asm-powerpc/page_32.h b/include/asm-powerpc/page_32.h index 07f6d3cf5e5..374d0db37e1 100644 --- a/include/asm-powerpc/page_32.h +++ b/include/asm-powerpc/page_32.h @@ -14,11 +14,9 @@ #ifdef CONFIG_PTE_64BIT typedef unsigned long long pte_basic_t; #define PTE_SHIFT (PAGE_SHIFT - 3) /* 512 ptes per page */ -#define PTE_FMT "%16Lx" #else typedef unsigned long pte_basic_t; #define PTE_SHIFT (PAGE_SHIFT - 2) /* 1024 ptes per page */ -#define PTE_FMT "%.8lx" #endif struct page; diff --git a/include/asm-powerpc/pgtable-ppc32.h b/include/asm-powerpc/pgtable-ppc32.h index 80c75474c65..e704640b3e4 100644 --- a/include/asm-powerpc/pgtable-ppc32.h +++ b/include/asm-powerpc/pgtable-ppc32.h @@ -101,7 +101,8 @@ extern unsigned long ioremap_bot, ioremap_base; #define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) #define pte_ERROR(e) \ - printk("%s:%d: bad pte "PTE_FMT".\n", __FILE__, __LINE__, pte_val(e)) + printk("%s:%d: bad pte %llx.\n", __FILE__, __LINE__, \ + (unsigned long long)pte_val(e)) #define pmd_ERROR(e) \ printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) #define pgd_ERROR(e) \ -- cgit v1.2.3 From de41189bf6838c1c885d360e5cdf7ec6b4d336d6 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Mon, 7 May 2007 01:38:46 +1000 Subject: [POWERPC] Export of_device_get_modalias Apparently other parts of the kernel need to know the modalias internally (like the sysfs code in macintosh driver). To avoid consistency issues, we export this code and use it everywhere it's needed rather than repeat it ... Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/of_device.c | 5 +++-- include/asm-powerpc/of_device.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/of_device.c b/arch/powerpc/kernel/of_device.c index e2c765f5f3f..a464d67248d 100644 --- a/arch/powerpc/kernel/of_device.c +++ b/arch/powerpc/kernel/of_device.c @@ -120,8 +120,8 @@ void of_device_unregister(struct of_device *ofdev) } -static ssize_t of_device_get_modalias(struct of_device *ofdev, - char *str, ssize_t len) +ssize_t of_device_get_modalias(struct of_device *ofdev, + char *str, ssize_t len) { const char *compat; int cplen, i; @@ -239,3 +239,4 @@ EXPORT_SYMBOL(of_dev_get); EXPORT_SYMBOL(of_dev_put); EXPORT_SYMBOL(of_release_dev); EXPORT_SYMBOL(of_device_uevent); +EXPORT_SYMBOL(of_device_get_modalias); diff --git a/include/asm-powerpc/of_device.h b/include/asm-powerpc/of_device.h index 4f1aabe0ce7..e9af49eb1aa 100644 --- a/include/asm-powerpc/of_device.h +++ b/include/asm-powerpc/of_device.h @@ -32,6 +32,8 @@ extern int of_device_register(struct of_device *ofdev); extern void of_device_unregister(struct of_device *ofdev); extern void of_release_dev(struct device *dev); +extern ssize_t of_device_get_modalias(struct of_device *ofdev, + char *str, ssize_t len); extern int of_device_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); -- cgit v1.2.3 From 2e1ee1f76684c5d4dd8e5a08cbf22d57f88769ed Mon Sep 17 00:00:00 2001 From: Domen Puncer Date: Mon, 7 May 2007 01:38:52 +1000 Subject: [POWERPC] mpc52xx suspend to deep-sleep Implement deep-sleep on MPC52xx. SDRAM is put into self-refresh with help of SRAM code (alternatives would be code in FLASH, I-cache). Interrupt code must also not be in SDRAM, so put it in I-cache. MPC52xx core is static, so contents will remain intact even with clocks turned off. Signed-off-by: Domen Puncer Acked-by: Grant Likely Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/52xx/Makefile | 2 + arch/powerpc/platforms/52xx/efika.c | 15 +++ arch/powerpc/platforms/52xx/lite5200.c | 28 ++++ arch/powerpc/platforms/52xx/mpc52xx_pm.c | 191 ++++++++++++++++++++++++++++ arch/powerpc/platforms/52xx/mpc52xx_sleep.S | 154 ++++++++++++++++++++++ include/asm-powerpc/mpc52xx.h | 11 ++ 6 files changed, 401 insertions(+) create mode 100644 arch/powerpc/platforms/52xx/mpc52xx_pm.c create mode 100644 arch/powerpc/platforms/52xx/mpc52xx_sleep.S (limited to 'include') diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile index 07cdbcacf15..b91e39c84d4 100644 --- a/arch/powerpc/platforms/52xx/Makefile +++ b/arch/powerpc/platforms/52xx/Makefile @@ -8,3 +8,5 @@ endif obj-$(CONFIG_PPC_EFIKA) += efika.o obj-$(CONFIG_PPC_LITE5200) += lite5200.o + +obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c index a6bba97314e..f591a9fc19b 100644 --- a/arch/powerpc/platforms/52xx/efika.c +++ b/arch/powerpc/platforms/52xx/efika.c @@ -184,6 +184,16 @@ static void efika_show_cpuinfo(struct seq_file *m) of_node_put(root); } +#ifdef CONFIG_PM +static void efika_suspend_prepare(void __iomem *mbar) +{ + u8 pin = 4; /* GPIO_WKUP_4 (GPIO_PSC6_0 - IRDA_RX) */ + u8 level = 1; /* wakeup on high level */ + /* IOW. to wake it up, short pins 1 and 3 on IRDA connector */ + mpc52xx_set_wakeup_gpio(pin, level); +} +#endif + static void __init efika_setup_arch(void) { rtas_initialize(); @@ -199,6 +209,11 @@ static void __init efika_setup_arch(void) efika_pcisetup(); +#ifdef CONFIG_PM + mpc52xx_suspend.board_suspend_prepare = efika_suspend_prepare; + mpc52xx_pm_init(); +#endif + if (ppc_md.progress) ppc_md.progress("Linux/PPC " UTS_RELEASE " running on Efika ;-)\n", 0x0); } diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index 8e2646ac417..1cfc00dfb99 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -85,6 +85,28 @@ error: iounmap(gpio); } +#ifdef CONFIG_PM +static u32 descr_a; +static void lite5200_suspend_prepare(void __iomem *mbar) +{ + u8 pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */ + u8 level = 0; /* wakeup on low level */ + mpc52xx_set_wakeup_gpio(pin, level); + + /* + * power down usb port + * this needs to be called before of-ohci suspend code + */ + descr_a = in_be32(mbar + 0x1048); + out_be32(mbar + 0x1048, (descr_a & ~0x200) | 0x100); +} + +static void lite5200_resume_finish(void __iomem *mbar) +{ + out_be32(mbar + 0x1048, descr_a); +} +#endif + static void __init lite5200_setup_arch(void) { struct device_node *np; @@ -107,6 +129,12 @@ static void __init lite5200_setup_arch(void) mpc52xx_setup_cpu(); /* Generic */ lite5200_setup_cpu(); /* Platorm specific */ +#ifdef CONFIG_PM + mpc52xx_suspend.board_suspend_prepare = lite5200_suspend_prepare; + mpc52xx_suspend.board_resume_finish = lite5200_resume_finish; + mpc52xx_pm_init(); +#endif + #ifdef CONFIG_PCI np = of_find_node_by_type(NULL, "pci"); if (np) { diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pm.c b/arch/powerpc/platforms/52xx/mpc52xx_pm.c new file mode 100644 index 00000000000..fd40044d16c --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_pm.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include + +#include "mpc52xx_pic.h" + + +/* these are defined in mpc52xx_sleep.S, and only used here */ +extern void mpc52xx_deep_sleep(void *sram, void *sdram_regs, + struct mpc52xx_cdm *, struct mpc52xx_intr *); +extern void mpc52xx_ds_sram(void); +extern const long mpc52xx_ds_sram_size; +extern void mpc52xx_ds_cached(void); +extern const long mpc52xx_ds_cached_size; + +static void __iomem *mbar; +static void __iomem *sdram; +static struct mpc52xx_cdm __iomem *cdm; +static struct mpc52xx_intr __iomem *intr; +static struct mpc52xx_gpio_wkup __iomem *gpiow; +static void *sram; +static int sram_size; + +struct mpc52xx_suspend mpc52xx_suspend; + +static int mpc52xx_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + return 1; + default: + return 0; + } +} + +int mpc52xx_set_wakeup_gpio(u8 pin, u8 level) +{ + u16 tmp; + + /* enable gpio */ + out_8(&gpiow->wkup_gpioe, in_8(&gpiow->wkup_gpioe) | (1 << pin)); + /* set as input */ + out_8(&gpiow->wkup_ddr, in_8(&gpiow->wkup_ddr) & ~(1 << pin)); + /* enable deep sleep interrupt */ + out_8(&gpiow->wkup_inten, in_8(&gpiow->wkup_inten) | (1 << pin)); + /* low/high level creates wakeup interrupt */ + tmp = in_be16(&gpiow->wkup_itype); + tmp &= ~(0x3 << (pin * 2)); + tmp |= (!level + 1) << (pin * 2); + out_be16(&gpiow->wkup_itype, tmp); + /* master enable */ + out_8(&gpiow->wkup_maste, 1); + + return 0; +} + +int mpc52xx_pm_prepare(suspend_state_t state) +{ + if (state != PM_SUSPEND_STANDBY) + return -EINVAL; + + /* map the whole register space */ + mbar = mpc52xx_find_and_map("mpc5200"); + if (!mbar) { + printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); + return -ENOSYS; + } + /* these offsets are from mpc5200 users manual */ + sdram = mbar + 0x100; + cdm = mbar + 0x200; + intr = mbar + 0x500; + gpiow = mbar + 0xc00; + sram = mbar + 0x8000; /* Those will be handled by the */ + sram_size = 0x4000; /* bestcomm driver soon */ + + /* call board suspend code, if applicable */ + if (mpc52xx_suspend.board_suspend_prepare) + mpc52xx_suspend.board_suspend_prepare(mbar); + else { + printk(KERN_ALERT "%s: %i don't know how to wake up the board\n", + __func__, __LINE__); + goto out_unmap; + } + + return 0; + + out_unmap: + iounmap(mbar); + return -ENOSYS; +} + + +char saved_sram[0x4000]; + +int mpc52xx_pm_enter(suspend_state_t state) +{ + u32 clk_enables; + u32 msr, hid0; + u32 intr_main_mask; + void __iomem * irq_0x500 = (void *)CONFIG_KERNEL_START + 0x500; + unsigned long irq_0x500_stop = (unsigned long)irq_0x500 + mpc52xx_ds_cached_size; + char saved_0x500[mpc52xx_ds_cached_size]; + + /* disable all interrupts in PIC */ + intr_main_mask = in_be32(&intr->main_mask); + out_be32(&intr->main_mask, intr_main_mask | 0x1ffff); + + /* don't let DEC expire any time soon */ + mtspr(SPRN_DEC, 0x7fffffff); + + /* save SRAM */ + memcpy(saved_sram, sram, sram_size); + + /* copy low level suspend code to sram */ + memcpy(sram, mpc52xx_ds_sram, mpc52xx_ds_sram_size); + + out_8(&cdm->ccs_sleep_enable, 1); + out_8(&cdm->osc_sleep_enable, 1); + out_8(&cdm->ccs_qreq_test, 1); + + /* disable all but SDRAM and bestcomm (SRAM) clocks */ + clk_enables = in_be32(&cdm->clk_enables); + out_be32(&cdm->clk_enables, clk_enables & 0x00088000); + + /* disable power management */ + msr = mfmsr(); + mtmsr(msr & ~MSR_POW); + + /* enable sleep mode, disable others */ + hid0 = mfspr(SPRN_HID0); + mtspr(SPRN_HID0, (hid0 & ~(HID0_DOZE | HID0_NAP | HID0_DPM)) | HID0_SLEEP); + + /* save original, copy our irq handler, flush from dcache and invalidate icache */ + memcpy(saved_0x500, irq_0x500, mpc52xx_ds_cached_size); + memcpy(irq_0x500, mpc52xx_ds_cached, mpc52xx_ds_cached_size); + flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop); + + /* call low-level sleep code */ + mpc52xx_deep_sleep(sram, sdram, cdm, intr); + + /* restore original irq handler */ + memcpy(irq_0x500, saved_0x500, mpc52xx_ds_cached_size); + flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop); + + /* restore old power mode */ + mtmsr(msr & ~MSR_POW); + mtspr(SPRN_HID0, hid0); + mtmsr(msr); + + out_be32(&cdm->clk_enables, clk_enables); + out_8(&cdm->ccs_sleep_enable, 0); + out_8(&cdm->osc_sleep_enable, 0); + + /* restore SRAM */ + memcpy(sram, saved_sram, sram_size); + + /* restart jiffies */ + wakeup_decrementer(); + + /* reenable interrupts in PIC */ + out_be32(&intr->main_mask, intr_main_mask); + + return 0; +} + +int mpc52xx_pm_finish(suspend_state_t state) +{ + /* call board resume code */ + if (mpc52xx_suspend.board_resume_finish) + mpc52xx_suspend.board_resume_finish(mbar); + + iounmap(mbar); + + return 0; +} + +static struct pm_ops mpc52xx_pm_ops = { + .valid = mpc52xx_pm_valid, + .prepare = mpc52xx_pm_prepare, + .enter = mpc52xx_pm_enter, + .finish = mpc52xx_pm_finish, +}; + +int __init mpc52xx_pm_init(void) +{ + pm_set_ops(&mpc52xx_pm_ops); + return 0; +} diff --git a/arch/powerpc/platforms/52xx/mpc52xx_sleep.S b/arch/powerpc/platforms/52xx/mpc52xx_sleep.S new file mode 100644 index 00000000000..4dc170b0ae1 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_sleep.S @@ -0,0 +1,154 @@ +#include +#include +#include + + +.text + +_GLOBAL(mpc52xx_deep_sleep) +mpc52xx_deep_sleep: /* args r3-r6: SRAM, SDRAM regs, CDM regs, INTR regs */ + + /* enable interrupts */ + mfmsr r7 + ori r7, r7, 0x8000 /* EE */ + mtmsr r7 + sync; isync; + + li r10, 0 /* flag that irq handler sets */ + + /* enable tmr7 (or any other) interrupt */ + lwz r8, 0x14(r6) /* intr->main_mask */ + ori r8, r8, 0x1 + xori r8, r8, 0x1 + stw r8, 0x14(r6) + sync + + /* emulate tmr7 interrupt */ + li r8, 0x1 + stw r8, 0x40(r6) /* intr->main_emulate */ + sync + + /* wait for it to happen */ +1: + cmpi cr0, r10, 1 + bne cr0, 1b + + /* lock icache */ + mfspr r10, SPRN_HID0 + ori r10, r10, 0x2000 + sync; isync; + mtspr SPRN_HID0, r10 + sync; isync; + + + mflr r9 /* save LR */ + + /* jump to sram */ + mtlr r3 + blrl + + mtlr r9 /* restore LR */ + + /* unlock icache */ + mfspr r10, SPRN_HID0 + ori r10, r10, 0x2000 + xori r10, r10, 0x2000 + sync; isync; + mtspr SPRN_HID0, r10 + sync; isync; + + + /* return to C code */ + blr + + +_GLOBAL(mpc52xx_ds_sram) +mpc52xx_ds_sram: + /* put SDRAM into self-refresh */ + lwz r8, 0x4(r4) /* sdram->ctrl */ + + oris r8, r8, 0x8000 /* mode_en */ + stw r8, 0x4(r4) + sync + + ori r8, r8, 0x0002 /* soft_pre */ + stw r8, 0x4(r4) + sync + xori r8, r8, 0x0002 + + xoris r8, r8, 0x8000 /* !mode_en */ + stw r8, 0x4(r4) + sync + + oris r8, r8, 0x5000 + xoris r8, r8, 0x4000 /* ref_en !cke */ + stw r8, 0x4(r4) + sync + + /* disable SDRAM clock */ + lwz r8, 0x14(r5) /* cdm->clkenable */ + ori r8, r8, 0x0008 + xori r8, r8, 0x0008 + stw r8, 0x14(r5) + sync + + + /* put mpc5200 to sleep */ + mfmsr r10 + oris r10, r10, 0x0004 /* POW = 1 */ + sync; isync; + mtmsr r10 + sync; isync; + + + /* enable clock */ + lwz r8, 0x14(r5) + ori r8, r8, 0x0008 + stw r8, 0x14(r5) + sync + + /* get ram out of self-refresh */ + lwz r8, 0x4(r4) + oris r8, r8, 0x5000 /* cke ref_en */ + stw r8, 0x4(r4) + sync + + blr +_GLOBAL(mpc52xx_ds_sram_size) +mpc52xx_ds_sram_size: + .long $-mpc52xx_ds_sram + + +/* ### interrupt handler for wakeup from deep-sleep ### */ +_GLOBAL(mpc52xx_ds_cached) +mpc52xx_ds_cached: + mtspr SPRN_SPRG0, r7 + mtspr SPRN_SPRG1, r8 + + /* disable emulated interrupt */ + mfspr r7, 311 /* MBAR */ + addi r7, r7, 0x540 /* intr->main_emul */ + li r8, 0 + stw r8, 0(r7) + sync + dcbf 0, r7 + + /* acknowledge wakeup, so CCS releases power pown */ + mfspr r7, 311 /* MBAR */ + addi r7, r7, 0x524 /* intr->enc_status */ + lwz r8, 0(r7) + ori r8, r8, 0x0400 + stw r8, 0(r7) + sync + dcbf 0, r7 + + /* flag - we handled the interrupt */ + li r10, 1 + + mfspr r8, SPRN_SPRG1 + mfspr r7, SPRN_SPRG0 + + rfi +_GLOBAL(mpc52xx_ds_cached_size) +mpc52xx_ds_cached_size: + .long $-mpc52xx_ds_cached diff --git a/include/asm-powerpc/mpc52xx.h b/include/asm-powerpc/mpc52xx.h index 7afd5bf9452..c4631f6dd4f 100644 --- a/include/asm-powerpc/mpc52xx.h +++ b/include/asm-powerpc/mpc52xx.h @@ -253,5 +253,16 @@ extern int __init mpc52xx_add_bridge(struct device_node *node); #endif /* __ASSEMBLY__ */ +#ifdef CONFIG_PM +struct mpc52xx_suspend { + void (*board_suspend_prepare)(void __iomem *mbar); + void (*board_resume_finish)(void __iomem *mbar); +}; + +extern struct mpc52xx_suspend mpc52xx_suspend; +extern int __init mpc52xx_pm_init(void); +extern int mpc52xx_set_wakeup_gpio(u8 pin, u8 level); +#endif /* CONFIG_PM */ + #endif /* __ASM_POWERPC_MPC52xx_H__ */ -- cgit v1.2.3 From f596575e81999c0faf01a2fd340bc96dda058f80 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 8 May 2007 01:08:00 +1000 Subject: [POWERPC] via-pmu: remove LED sleep notifier The generic LED code now makes sure that suspended devices don't blink, so we no longer need to do it ourselves. For the suspend to disk case, however, we need to make sure that we don't blink if the PMU sysdev was suspended before the LED device. Signed-off-by: Johannes Berg Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/macintosh/via-pmu-led.c | 35 +++-------------------------------- drivers/macintosh/via-pmu.c | 2 +- include/linux/pmu.h | 8 ++++++++ 3 files changed, 12 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c index fc89a7047cd..55ad9567138 100644 --- a/drivers/macintosh/via-pmu-led.c +++ b/drivers/macintosh/via-pmu-led.c @@ -31,7 +31,6 @@ static spinlock_t pmu_blink_lock; static struct adb_request pmu_blink_req; /* -1: no change, 0: request off, 1: request on */ static int requested_change; -static int sleeping; static void pmu_req_done(struct adb_request * req) { @@ -41,7 +40,7 @@ static void pmu_req_done(struct adb_request * req) /* if someone requested a change in the meantime * (we only see the last one which is fine) * then apply it now */ - if (requested_change != -1 && !sleeping) + if (requested_change != -1 && !pmu_sys_suspended) pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change); /* reset requested change */ requested_change = -1; @@ -66,7 +65,7 @@ static void pmu_led_set(struct led_classdev *led_cdev, break; } /* if request isn't done, then don't do anything */ - if (pmu_blink_req.complete && !sleeping) + if (pmu_blink_req.complete && !pmu_sys_suspended) pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change); out: spin_unlock_irqrestore(&pmu_blink_lock, flags); @@ -80,32 +79,6 @@ static struct led_classdev pmu_led = { .brightness_set = pmu_led_set, }; -#ifdef CONFIG_PM -static void pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when) -{ - unsigned long flags; - - spin_lock_irqsave(&pmu_blink_lock, flags); - - switch (when) { - case PBOOK_SLEEP_REQUEST: - sleeping = 1; - break; - case PBOOK_WAKE: - sleeping = 0; - break; - default: - /* do nothing */ - break; - } - spin_unlock_irqrestore(&pmu_blink_lock, flags); -} - -static struct pmu_sleep_notifier via_pmu_led_sleep_notif = { - .notifier_call = pmu_led_sleep_call, -}; -#endif - static int __init via_pmu_led_init(void) { struct device_node *dt; @@ -135,9 +108,7 @@ static int __init via_pmu_led_init(void) /* no outstanding req */ pmu_blink_req.complete = 1; pmu_blink_req.done = pmu_req_done; -#ifdef CONFIG_PM - pmu_register_sleep_notifier(&via_pmu_led_sleep_notif); -#endif + return led_classdev_register(NULL, &pmu_led); } diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 0707624dfad..157080b3b46 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -2759,7 +2759,7 @@ pmu_polled_request(struct adb_request *req) #if defined(CONFIG_PM) && defined(CONFIG_PPC32) -static int pmu_sys_suspended; +int pmu_sys_suspended; static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state) { diff --git a/include/linux/pmu.h b/include/linux/pmu.h index b0952e532ed..37ca57392ad 100644 --- a/include/linux/pmu.h +++ b/include/linux/pmu.h @@ -225,4 +225,12 @@ extern unsigned int pmu_power_flags; /* Backlight */ extern void pmu_backlight_init(void); +/* some code needs to know if the PMU was suspended for hibernation */ +#ifdef CONFIG_PM +extern int pmu_sys_suspended; +#else +/* if power management is not configured it can't be suspended */ +#define pmu_sys_suspended 0 +#endif + #endif /* __KERNEL__ */ -- cgit v1.2.3 From 08390db07a012b972189629a30eb695cdcb0ec14 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 8 May 2007 07:25:22 +1000 Subject: [POWERPC] Add tsi108_pci.h for common PCI functions Add a header file for the common PCI routines used for the TSI bridge Signed-off-by: Josh Boyer Acked-by: Olof Johansson Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c | 5 +-- arch/powerpc/sysdev/tsi108_pci.c | 1 + include/asm-powerpc/tsi108_pci.h | 45 +++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 include/asm-powerpc/tsi108_pci.h (limited to 'include') diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index 366702e8c03..06c86a1b4fb 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -41,6 +41,7 @@ #include #include #include "mpc7448_hpc2.h" +#include #include #include @@ -57,10 +58,7 @@ isa_mem_base = MPC7448_HPC2_ISA_MEM_BASE; pci_dram_offset = MPC7448_HPC2_PCI_MEM_OFFSET; #endif -extern int tsi108_setup_pci(struct device_node *dev); extern void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val); -extern void tsi108_pci_int_init(struct device_node *node); -extern void tsi108_irq_cascade(unsigned int irq, struct irq_desc *desc); int mpc7448_hpc2_exclude_device(u_char bus, u_char devfn) { @@ -210,7 +208,6 @@ static int __init mpc7448_hpc2_probe(void) static int mpc7448_machine_check_exception(struct pt_regs *regs) { - extern void tsi108_clear_pci_cfg_error(void); const struct exception_table_entry *entry; /* Are we prepared to handle this fault */ diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c index 58b9e7f8abf..76c32be4a6b 100644 --- a/arch/powerpc/sysdev/tsi108_pci.c +++ b/arch/powerpc/sysdev/tsi108_pci.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include diff --git a/include/asm-powerpc/tsi108_pci.h b/include/asm-powerpc/tsi108_pci.h new file mode 100644 index 00000000000..f3be7d90d26 --- /dev/null +++ b/include/asm-powerpc/tsi108_pci.h @@ -0,0 +1,45 @@ +/* + * Copyright 2007 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 + */ + +#ifndef _ASM_PPC_TSI108_PCI_H +#define _ASM_PPC_TSI108_PCI_H + +#include + +/* Register definitions */ +#define TSI108_PCI_P2O_BAR0 (TSI108_PCI_OFFSET + 0x10) +#define TSI108_PCI_P2O_BAR0_UPPER (TSI108_PCI_OFFSET + 0x14) +#define TSI108_PCI_P2O_BAR2 (TSI108_PCI_OFFSET + 0x18) +#define TSI108_PCI_P2O_BAR2_UPPER (TSI108_PCI_OFFSET + 0x1c) +#define TSI108_PCI_P2O_PAGE_SIZES (TSI108_PCI_OFFSET + 0x4c) +#define TSI108_PCI_PFAB_BAR0 (TSI108_PCI_OFFSET + 0x204) +#define TSI108_PCI_PFAB_BAR0_UPPER (TSI108_PCI_OFFSET + 0x208) +#define TSI108_PCI_PFAB_IO (TSI108_PCI_OFFSET + 0x20c) +#define TSI108_PCI_PFAB_IO_UPPER (TSI108_PCI_OFFSET + 0x210) +#define TSI108_PCI_PFAB_MEM32 (TSI108_PCI_OFFSET + 0x214) +#define TSI108_PCI_PFAB_PFM3 (TSI108_PCI_OFFSET + 0x220) +#define TSI108_PCI_PFAB_PFM4 (TSI108_PCI_OFFSET + 0x230) + +extern int tsi108_setup_pci(struct device_node *dev); +extern void tsi108_pci_int_init(struct device_node *node); +extern void tsi108_irq_cascade(unsigned int irq, struct irq_desc *desc); +extern void tsi108_clear_pci_cfg_error(void); + +#endif /* _ASM_PPC_TSI108_PCI_H */ -- cgit v1.2.3 From c1b78d05b3281d6f84284d421fc20eed8b8b78ce Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 8 May 2007 07:26:22 +1000 Subject: [POWERPC] Generalize tsi108 PHY types Add a phy_type field to the tsi108 ethernet structures to indicate which PHY is used on a board. This is derived from the "compatible" property in the ethernet-phy node of the device tree. The default remains the MV88E PHY. Also, convert the setup code to use of_get_mac_address instead of hard coding a lookup for the "address" property in the ethernet node. Signed-off-by: Josh Boyer Acked-by: Olof Johansson Signed-off-by: Paul Mackerras --- arch/powerpc/sysdev/tsi108_dev.c | 7 +++++-- drivers/net/tsi108_eth.c | 12 +++++++----- drivers/net/tsi108_eth.h | 9 --------- include/asm-powerpc/tsi108.h | 11 +++++++++++ 4 files changed, 23 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c index 337039ee51e..7d3b09b7d54 100644 --- a/arch/powerpc/sysdev/tsi108_dev.c +++ b/arch/powerpc/sysdev/tsi108_dev.c @@ -107,8 +107,9 @@ static int __init tsi108_eth_of_init(void) goto err; } - mac_addr = of_get_property(np, "address", NULL); - memcpy(tsi_eth_data.mac_addr, mac_addr, 6); + mac_addr = of_get_mac_address(np); + if (mac_addr) + memcpy(tsi_eth_data.mac_addr, mac_addr, 6); ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); @@ -129,6 +130,8 @@ static int __init tsi108_eth_of_init(void) tsi_eth_data.phyregs = res.start; tsi_eth_data.phy = *phy_id; tsi_eth_data.irq_num = irq_of_parse_and_map(np, 0); + if (of_device_is_compatible(phy, "bcm54xx")) + tsi_eth_data.phy_type = TSI108_PHY_BCM54XX; of_node_put(phy); ret = platform_device_add_data(tsi_eth_dev, &tsi_eth_data, diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index 0bfc2c9c1c0..1aabc91f645 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -82,6 +82,7 @@ struct tsi108_prv_data { unsigned int phy; /* Index of PHY for this interface */ unsigned int irq_num; unsigned int id; + unsigned int phy_type; struct timer_list timer;/* Timer that triggers the check phy function */ unsigned int rxtail; /* Next entry in rxring to read */ @@ -1256,11 +1257,11 @@ static void tsi108_init_phy(struct net_device *dev) if (i == 0) printk(KERN_ERR "%s function time out \n", __FUNCTION__); -#if (TSI108_PHY_TYPE == PHY_BCM54XX) /* Broadcom BCM54xx PHY */ - tsi108_write_mii(data, 0x09, 0x0300); - tsi108_write_mii(data, 0x10, 0x1020); - tsi108_write_mii(data, 0x1c, 0x8c00); -#endif + if (data->phy_type == TSI108_PHY_BCM54XX) { + tsi108_write_mii(data, 0x09, 0x0300); + tsi108_write_mii(data, 0x10, 0x1020); + tsi108_write_mii(data, 0x1c, 0x8c00); + } tsi108_write_mii(data, MII_BMCR, @@ -1587,6 +1588,7 @@ tsi108_init_one(struct platform_device *pdev) data->mii_if.supports_gmii = mii_check_gmii_support(&data->mii_if); data->phy = einfo->phy; + data->phy_type = einfo->phy_type; data->irq_num = einfo->irq_num; data->id = pdev->id; dev->open = tsi108_open; diff --git a/drivers/net/tsi108_eth.h b/drivers/net/tsi108_eth.h index 77a769df228..5a77ae6c5f3 100644 --- a/drivers/net/tsi108_eth.h +++ b/drivers/net/tsi108_eth.h @@ -42,15 +42,6 @@ #define TSI_READ_PHY(offset) \ in_be32((data->phyregs + (offset))) -/* - * PHY Configuration Options - * - * NOTE: Enable set of definitions corresponding to your board type - */ -#define PHY_MV88E 1 /* Marvel 88Exxxx PHY */ -#define PHY_BCM54XX 2 /* Broardcom BCM54xx PHY */ -#define TSI108_PHY_TYPE PHY_MV88E - /* * TSI108 GIGE port registers */ diff --git a/include/asm-powerpc/tsi108.h b/include/asm-powerpc/tsi108.h index 4e95d153be8..20cb072d45a 100644 --- a/include/asm-powerpc/tsi108.h +++ b/include/asm-powerpc/tsi108.h @@ -70,6 +70,16 @@ #define TSI108_PCI_CFG_BASE_PHYS (0xfb000000) #define TSI108_PCI_CFG_SIZE (0x01000000) + +/* + * PHY Configuration Options + * + * Specify "bcm54xx" in the compatible property of your device tree phy + * nodes if your board uses the Broadcom PHYs + */ +#define TSI108_PHY_MV88E 0 /* Marvel 88Exxxx PHY */ +#define TSI108_PHY_BCM54XX 1 /* Broardcom BCM54xx PHY */ + /* Global variables */ extern u32 tsi108_pci_cfg_base; @@ -93,6 +103,7 @@ typedef struct { u16 phy; /* phy address */ u16 irq_num; /* irq number */ u8 mac_addr[6]; /* phy mac address */ + u16 phy_type; /* type of phy on board */ } hw_info; extern u32 get_vir_csrbase(void); -- cgit v1.2.3 From 05ad6a9159401804c9bcec8922a9c4a1cb2bfb59 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 8 May 2007 07:27:15 +1000 Subject: [POWERPC] Generalize tsi108 PCI setup Generalize tsi108_setup_pci to take the config space physical address and primary bus designator as a parameter. Signed-off-by: Josh Boyer Acked-by: Olof Johansson Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c | 4 +++- arch/powerpc/sysdev/tsi108_pci.c | 11 ++++++----- include/asm-powerpc/tsi108.h | 1 - include/asm-powerpc/tsi108_pci.h | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index 06c86a1b4fb..4542e0c837c 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -52,6 +52,8 @@ #define DBG(fmt...) do { } while(0) #endif +#define MPC7448HPC2_PCI_CFG_PHYS 0xfb000000 + #ifndef CONFIG_PCI isa_io_base = MPC7448_HPC2_ISA_IO_BASE; isa_mem_base = MPC7448_HPC2_ISA_MEM_BASE; @@ -79,7 +81,7 @@ static void __init mpc7448_hpc2_setup_arch(void) /* setup PCI host bridge */ #ifdef CONFIG_PCI for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - tsi108_setup_pci(np); + tsi108_setup_pci(np, MPC7448HPC2_PCI_CFG_PHYS, 0); ppc_md.pci_exclude_device = mpc7448_hpc2_exclude_device; if (ppc_md.progress) diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c index 76c32be4a6b..2153163fa59 100644 --- a/arch/powerpc/sysdev/tsi108_pci.c +++ b/arch/powerpc/sysdev/tsi108_pci.c @@ -50,6 +50,7 @@ ((((bus)<<16) | ((devfunc)<<8) | (offset & 0xfc)) + tsi108_pci_cfg_base) u32 tsi108_pci_cfg_base; +static u32 tsi108_pci_cfg_phys; u32 tsi108_csr_vir_base; static struct device_node *pci_irq_node; static struct irq_host *pci_irq_host; @@ -186,7 +187,7 @@ tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset, void tsi108_clear_pci_cfg_error(void) { - tsi108_clear_pci_error(TSI108_PCI_CFG_BASE_PHYS); + tsi108_clear_pci_error(tsi108_pci_cfg_phys); } static struct pci_ops tsi108_direct_pci_ops = { @@ -194,17 +195,17 @@ static struct pci_ops tsi108_direct_pci_ops = { tsi108_direct_write_config }; -int __init tsi108_setup_pci(struct device_node *dev) +int __init tsi108_setup_pci(struct device_node *dev, u32 cfg_phys, int primary) { int len; struct pci_controller *hose; struct resource rsrc; const int *bus_range; - int primary = 0, has_address = 0; + int has_address = 0; /* PCI Config mapping */ - tsi108_pci_cfg_base = (u32)ioremap(TSI108_PCI_CFG_BASE_PHYS, - TSI108_PCI_CFG_SIZE); + tsi108_pci_cfg_base = (u32)ioremap(cfg_phys, TSI108_PCI_CFG_SIZE); + tsi108_pci_cfg_phys = cfg_phys; DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __FUNCTION__, tsi108_pci_cfg_base); diff --git a/include/asm-powerpc/tsi108.h b/include/asm-powerpc/tsi108.h index 20cb072d45a..f8b60793b7a 100644 --- a/include/asm-powerpc/tsi108.h +++ b/include/asm-powerpc/tsi108.h @@ -68,7 +68,6 @@ #define TSI108_PB_ERRCS_ES (1 << 1) #define TSI108_PB_ISR_PBS_RD_ERR (1 << 8) -#define TSI108_PCI_CFG_BASE_PHYS (0xfb000000) #define TSI108_PCI_CFG_SIZE (0x01000000) /* diff --git a/include/asm-powerpc/tsi108_pci.h b/include/asm-powerpc/tsi108_pci.h index f3be7d90d26..a9f92f73232 100644 --- a/include/asm-powerpc/tsi108_pci.h +++ b/include/asm-powerpc/tsi108_pci.h @@ -37,7 +37,7 @@ #define TSI108_PCI_PFAB_PFM3 (TSI108_PCI_OFFSET + 0x220) #define TSI108_PCI_PFAB_PFM4 (TSI108_PCI_OFFSET + 0x230) -extern int tsi108_setup_pci(struct device_node *dev); +extern int tsi108_setup_pci(struct device_node *dev, u32 cfg_phys, int primary); extern void tsi108_pci_int_init(struct device_node *node); extern void tsi108_irq_cascade(unsigned int irq, struct irq_desc *desc); extern void tsi108_clear_pci_cfg_error(void); -- cgit v1.2.3 From d1953c8888ef034b912ee33bc2ea2cce6a414402 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 8 May 2007 12:46:49 +1000 Subject: [POWERPC] Remove use of 4level-fixup.h for ppc32 For 32-bit systems, powerpc still relies on the 4level-fixup.h hack, to pretend that the generic pagetable handling stuff is 3-levels rather than 4. This patch removes this, instead using the newer pgtable-nopmd.h to handle the elision of both the pud and pmd pagetable levels (ppc32 pagetables are actually 2 levels). This removes a little extraneous code, and makes it more easily compared to the 64-bit pagetable code. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/lib/dma-noncoherent.c | 4 +++- arch/powerpc/mm/pgtable_32.c | 28 ++++++++++++++++------------ include/asm-powerpc/page.h | 12 ++++++++---- include/asm-powerpc/pgalloc-32.h | 4 ++-- include/asm-powerpc/pgtable-ppc32.h | 30 ++---------------------------- 5 files changed, 31 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/arch/powerpc/lib/dma-noncoherent.c b/arch/powerpc/lib/dma-noncoherent.c index 48f3d13a3de..6656d47841d 100644 --- a/arch/powerpc/lib/dma-noncoherent.c +++ b/arch/powerpc/lib/dma-noncoherent.c @@ -306,13 +306,15 @@ EXPORT_SYMBOL(__dma_free_coherent); static int __init dma_alloc_init(void) { pgd_t *pgd; + pud_t *pud; pmd_t *pmd; pte_t *pte; int ret = 0; do { pgd = pgd_offset(&init_mm, CONSISTENT_BASE); - pmd = pmd_alloc(&init_mm, pgd, CONSISTENT_BASE); + pud = pud_alloc(&init_mm, pgd, CONSISTENT_BASE); + pmd = pmd_alloc(&init_mm, pud, CONSISTENT_BASE); if (!pmd) { printk(KERN_ERR "%s: no pmd tables\n", __func__); ret = -ENOMEM; diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index bca56037492..d8232b7a08f 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -261,7 +261,7 @@ int map_page(unsigned long va, phys_addr_t pa, int flags) int err = -ENOMEM; /* Use upper 10 bits of VA to index the first level map */ - pd = pmd_offset(pgd_offset_k(va), va); + pd = pmd_offset(pud_offset(pgd_offset_k(va), va), va); /* Use middle 10 bits of VA to index the second-level map */ pg = pte_alloc_kernel(pd, va); if (pg != 0) { @@ -354,23 +354,27 @@ int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep, pmd_t **pmdp) { pgd_t *pgd; + pud_t *pud; pmd_t *pmd; pte_t *pte; int retval = 0; pgd = pgd_offset(mm, addr & PAGE_MASK); if (pgd) { - pmd = pmd_offset(pgd, addr & PAGE_MASK); - if (pmd_present(*pmd)) { - pte = pte_offset_map(pmd, addr & PAGE_MASK); - if (pte) { - retval = 1; - *ptep = pte; - if (pmdp) - *pmdp = pmd; - /* XXX caller needs to do pte_unmap, yuck */ - } - } + pud = pud_offset(pgd, addr & PAGE_MASK); + if (pud && pud_present(*pud)) { + pmd = pmd_offset(pud, addr & PAGE_MASK); + if (pmd_present(*pmd)) { + pte = pte_offset_map(pmd, addr & PAGE_MASK); + if (pte) { + retval = 1; + *ptep = pte; + if (pmdp) + *pmdp = pmd; + /* XXX caller needs to do pte_unmap, yuck */ + } + } + } } return(retval); } diff --git a/include/asm-powerpc/page.h b/include/asm-powerpc/page.h index f37bff0ee88..10c51f457d4 100644 --- a/include/asm-powerpc/page.h +++ b/include/asm-powerpc/page.h @@ -121,16 +121,18 @@ typedef struct { pte_t pte; } real_pte_t; #endif /* PMD level */ +#ifdef CONFIG_PPC64 typedef struct { unsigned long pmd; } pmd_t; #define pmd_val(x) ((x).pmd) #define __pmd(x) ((pmd_t) { (x) }) /* PUD level exusts only on 4k pages */ -#if defined(CONFIG_PPC64) && !defined(CONFIG_PPC_64K_PAGES) +#ifndef CONFIG_PPC_64K_PAGES typedef struct { unsigned long pud; } pud_t; #define pud_val(x) ((x).pud) #define __pud(x) ((pud_t) { (x) }) -#endif +#endif /* !CONFIG_PPC_64K_PAGES */ +#endif /* CONFIG_PPC64 */ /* PGD level */ typedef struct { unsigned long pgd; } pgd_t; @@ -159,15 +161,17 @@ typedef unsigned long real_pte_t; #endif +#ifdef CONFIG_PPC64 typedef unsigned long pmd_t; #define pmd_val(x) (x) #define __pmd(x) (x) -#if defined(CONFIG_PPC64) && !defined(CONFIG_PPC_64K_PAGES) +#ifndef CONFIG_PPC_64K_PAGES typedef unsigned long pud_t; #define pud_val(x) (x) #define __pud(x) (x) -#endif +#endif /* !CONFIG_PPC_64K_PAGES */ +#endif /* CONFIG_PPC64 */ typedef unsigned long pgd_t; #define pgd_val(x) (x) diff --git a/include/asm-powerpc/pgalloc-32.h b/include/asm-powerpc/pgalloc-32.h index 235aef283ed..e1307432163 100644 --- a/include/asm-powerpc/pgalloc-32.h +++ b/include/asm-powerpc/pgalloc-32.h @@ -12,10 +12,10 @@ extern void pgd_free(pgd_t *pgd); * We don't have any real pmd's, and this code never triggers because * the pgd will always be present.. */ -#define pmd_alloc_one(mm,address) ({ BUG(); ((pmd_t *)2); }) +/* #define pmd_alloc_one(mm,address) ({ BUG(); ((pmd_t *)2); }) */ #define pmd_free(x) do { } while (0) #define __pmd_free_tlb(tlb,x) do { } while (0) -#define pgd_populate(mm, pmd, pte) BUG() +/* #define pgd_populate(mm, pmd, pte) BUG() */ #ifndef CONFIG_BOOKE #define pmd_populate_kernel(mm, pmd, pte) \ diff --git a/include/asm-powerpc/pgtable-ppc32.h b/include/asm-powerpc/pgtable-ppc32.h index e704640b3e4..09662a24f22 100644 --- a/include/asm-powerpc/pgtable-ppc32.h +++ b/include/asm-powerpc/pgtable-ppc32.h @@ -1,7 +1,7 @@ #ifndef _ASM_POWERPC_PGTABLE_PPC32_H #define _ASM_POWERPC_PGTABLE_PPC32_H -#include +#include #ifndef __ASSEMBLY__ #include @@ -76,13 +76,8 @@ extern unsigned long ioremap_bot, ioremap_base; * level has 2048 entries and the second level has 512 64-bit PTE entries. * -Matt */ -/* PMD_SHIFT determines the size of the area mapped by the PTE pages */ -#define PMD_SHIFT (PAGE_SHIFT + PTE_SHIFT) -#define PMD_SIZE (1UL << PMD_SHIFT) -#define PMD_MASK (~(PMD_SIZE-1)) - /* PGDIR_SHIFT determines what a top-level page table entry can map */ -#define PGDIR_SHIFT PMD_SHIFT +#define PGDIR_SHIFT (PAGE_SHIFT + PTE_SHIFT) #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1)) @@ -103,8 +98,6 @@ extern unsigned long ioremap_bot, ioremap_base; #define pte_ERROR(e) \ printk("%s:%d: bad pte %llx.\n", __FILE__, __LINE__, \ (unsigned long long)pte_val(e)) -#define pmd_ERROR(e) \ - printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) #define pgd_ERROR(e) \ printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) @@ -515,19 +508,6 @@ extern unsigned long empty_zero_page[1024]; #define pmd_clear(pmdp) do { pmd_val(*(pmdp)) = 0; } while (0) #ifndef __ASSEMBLY__ -/* - * The "pgd_xxx()" functions here are trivial for a folded two-level - * setup: the pgd is never bad, and a pmd always exists (as it's folded - * into the pgd entry) - */ -static inline int pgd_none(pgd_t pgd) { return 0; } -static inline int pgd_bad(pgd_t pgd) { return 0; } -static inline int pgd_present(pgd_t pgd) { return 1; } -#define pgd_clear(xp) do { } while (0) - -#define pgd_page_vaddr(pgd) \ - ((unsigned long) __va(pgd_val(pgd) & PAGE_MASK)) - /* * The following only work if pte_present() is true. * Undefined behaviour if not.. @@ -737,12 +717,6 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, #define pgd_index(address) ((address) >> PGDIR_SHIFT) #define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address)) -/* Find an entry in the second-level page table.. */ -static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address) -{ - return (pmd_t *) dir; -} - /* Find an entry in the third-level page table.. */ #define pte_index(address) \ (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) -- cgit v1.2.3 From f728b5c3a599d0410a079f447f921a10be7d59d6 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 8 May 2007 12:58:33 +1000 Subject: [POWERPC] Rip out the existing powerpc msi stubs Rip out the existing powerpc msi stubs. These were the start of an implementation based on ppc_md calls, but were never used in mainline. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/irq.c | 27 --------------------------- include/asm-powerpc/machdep.h | 5 ----- 2 files changed, 32 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 5719a530f2d..9ed4931af16 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -958,33 +958,6 @@ arch_initcall(irq_late_init); #endif /* CONFIG_PPC_MERGE */ -#ifdef CONFIG_PCI_MSI -int pci_enable_msi(struct pci_dev * pdev) -{ - if (ppc_md.enable_msi) - return ppc_md.enable_msi(pdev); - else - return -1; -} -EXPORT_SYMBOL(pci_enable_msi); - -void pci_disable_msi(struct pci_dev * pdev) -{ - if (ppc_md.disable_msi) - ppc_md.disable_msi(pdev); -} -EXPORT_SYMBOL(pci_disable_msi); - -void pci_scan_msi_device(struct pci_dev *dev) {} -int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) {return -1;} -void pci_disable_msix(struct pci_dev *dev) {} -void msi_remove_pci_irq_vectors(struct pci_dev *dev) {} -void pci_no_msi(void) {} -EXPORT_SYMBOL(pci_enable_msix); -EXPORT_SYMBOL(pci_disable_msix); - -#endif - #ifdef CONFIG_PPC64 static int __init setup_noirqdistrib(char *str) { diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index 6fdabd6dfb3..daa08b1c212 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -245,11 +245,6 @@ struct machdep_calls { */ void (*machine_kexec)(struct kimage *image); #endif /* CONFIG_KEXEC */ - -#ifdef CONFIG_PCI_MSI - int (*enable_msi)(struct pci_dev *pdev); - void (*disable_msi)(struct pci_dev *pdev); -#endif /* CONFIG_PCI_MSI */ }; extern void power4_idle(void); -- cgit v1.2.3 From df87ef5508b40fc655b6c4771be31741d8ec1596 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 8 May 2007 12:58:34 +1000 Subject: [POWERPC] PowerPC MSI infrastructure This provides the architecture specific hooks to support MSI on powerpc. We implement the newly added arch_setup_msi_irqs() and arch_teardown_msi_irqs(), and then delegate to ppc_md routines. Platforms that don't implement MSI will leave the ppc_md calls blank, arch_msi_check_device() will detect this and return ENOSYS. Drivers should detect this error and continue to use LSI. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/Kconfig | 1 + arch/powerpc/kernel/Makefile | 1 + arch/powerpc/kernel/msi.c | 38 ++++++++++++++++++++++++++++++++++++++ include/asm-powerpc/machdep.h | 8 ++++++++ 4 files changed, 48 insertions(+) create mode 100644 arch/powerpc/kernel/msi.c (limited to 'include') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 67668fdb029..abb13cfd2ef 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -682,6 +682,7 @@ config PCI && !PPC_85xx && !PPC_86xx default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS default PCI_QSPAN if !4xx && !CPM2 && 8xx + select ARCH_SUPPORTS_MSI help Find out whether your system includes a PCI bus. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 94c703a9501..3e779f07f21 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MODULES) += $(module-y) pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o pci32-$(CONFIG_PPC32) := pci_32.o obj-$(CONFIG_PCI) += $(pci64-y) $(pci32-y) +obj-$(CONFIG_PCI_MSI) += msi.o kexec-$(CONFIG_PPC64) := machine_kexec_64.o kexec-$(CONFIG_PPC32) := machine_kexec_32.o obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o $(kexec-y) diff --git a/arch/powerpc/kernel/msi.c b/arch/powerpc/kernel/msi.c new file mode 100644 index 00000000000..c62d1012c01 --- /dev/null +++ b/arch/powerpc/kernel/msi.c @@ -0,0 +1,38 @@ +/* + * Copyright 2006-2007, Michael Ellerman, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +#include + +int arch_msi_check_device(struct pci_dev* dev, int nvec, int type) +{ + if (!ppc_md.setup_msi_irqs || !ppc_md.teardown_msi_irqs) { + pr_debug("msi: Platform doesn't provide MSI callbacks.\n"); + return -ENOSYS; + } + + if (ppc_md.msi_check_device) { + pr_debug("msi: Using platform check routine.\n"); + return ppc_md.msi_check_device(dev, nvec, type); + } + + return 0; +} + +int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + return ppc_md.setup_msi_irqs(dev, nvec, type); +} + +void arch_teardown_msi_irqs(struct pci_dev *dev) +{ + return ppc_md.teardown_msi_irqs(dev); +} diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index daa08b1c212..6cf1a831f55 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -120,6 +120,14 @@ struct machdep_calls { /* To setup PHBs when using automatic OF platform driver for PCI */ int (*pci_setup_phb)(struct pci_controller *host); +#ifdef CONFIG_PCI_MSI + int (*msi_check_device)(struct pci_dev* dev, + int nvec, int type); + int (*setup_msi_irqs)(struct pci_dev *dev, + int nvec, int type); + void (*teardown_msi_irqs)(struct pci_dev *dev); +#endif + void (*restart)(char *cmd); void (*power_off)(void); void (*halt)(void); -- cgit v1.2.3 From a7de7c74227edda719b257eb15aecd73790ff894 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 8 May 2007 12:58:36 +1000 Subject: [POWERPC] MPIC MSI allocator To support MSI on MPIC we need a way to reserve and allocate hardware irq numbers, this patch implements an allocator for that purpose. New firmware platforms must define a "msi-available-ranges" property on their MPIC node for MSI to work. For U3/U4 we do a best-guess setup. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/sysdev/Makefile | 4 +- arch/powerpc/sysdev/mpic.c | 4 + arch/powerpc/sysdev/mpic.h | 27 ++++++ arch/powerpc/sysdev/mpic_msi.c | 183 +++++++++++++++++++++++++++++++++++++++++ include/asm-powerpc/mpic.h | 5 ++ 5 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/sysdev/mpic.h create mode 100644 arch/powerpc/sysdev/mpic_msi.c (limited to 'include') diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index e96ca9618db..a1702053297 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -2,7 +2,9 @@ ifeq ($(CONFIG_PPC64),y) EXTRA_CFLAGS += -mno-minimal-toc endif -obj-$(CONFIG_MPIC) += mpic.o +mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o +obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) + obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_MPC106) += grackle.o obj-$(CONFIG_PPC_DCR) += dcr.o diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 33ecf89702d..fc0428aea0c 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -36,6 +36,8 @@ #include #include +#include "mpic.h" + #ifdef DEBUG #define DBG(fmt...) printk(fmt) #else @@ -891,6 +893,8 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq, if (hw >= mpic->irq_count) return -EINVAL; + mpic_msi_reserve_hwirq(mpic, hw); + /* Default chip */ chip = &mpic->hc_irq; diff --git a/arch/powerpc/sysdev/mpic.h b/arch/powerpc/sysdev/mpic.h new file mode 100644 index 00000000000..87cb2aea2fb --- /dev/null +++ b/arch/powerpc/sysdev/mpic.h @@ -0,0 +1,27 @@ +#ifndef _POWERPC_SYSDEV_MPIC_H +#define _POWERPC_SYSDEV_MPIC_H + +/* + * Copyright 2006-2007, Michael Ellerman, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the + * License. + * + */ + +#ifdef CONFIG_PCI_MSI +extern void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq); +extern int mpic_msi_init_allocator(struct mpic *mpic); +extern irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num); +extern void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num); +#else +static inline void mpic_msi_reserve_hwirq(struct mpic *mpic, + irq_hw_number_t hwirq) +{ + return; +} +#endif + +#endif /* _POWERPC_SYSDEV_MPIC_H */ diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c new file mode 100644 index 00000000000..b076793033c --- /dev/null +++ b/arch/powerpc/sysdev/mpic_msi.c @@ -0,0 +1,183 @@ +/* + * Copyright 2006-2007, Michael Ellerman, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the + * License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +static void __mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq) +{ + pr_debug("mpic: reserving hwirq 0x%lx\n", hwirq); + bitmap_allocate_region(mpic->hwirq_bitmap, hwirq, 0); +} + +void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq) +{ + unsigned long flags; + + /* The mpic calls this even when there is no allocator setup */ + if (!mpic->hwirq_bitmap) + return; + + spin_lock_irqsave(&mpic->bitmap_lock, flags); + __mpic_msi_reserve_hwirq(mpic, hwirq); + spin_unlock_irqrestore(&mpic->bitmap_lock, flags); +} + +irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num) +{ + unsigned long flags; + int offset, order = get_count_order(num); + + spin_lock_irqsave(&mpic->bitmap_lock, flags); + /* + * This is fast, but stricter than we need. We might want to add + * a fallback routine which does a linear search with no alignment. + */ + offset = bitmap_find_free_region(mpic->hwirq_bitmap, mpic->irq_count, + order); + spin_unlock_irqrestore(&mpic->bitmap_lock, flags); + + pr_debug("mpic: allocated 0x%x (2^%d) at offset 0x%x\n", + num, order, offset); + + return offset; +} + +void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num) +{ + unsigned long flags; + int order = get_count_order(num); + + pr_debug("mpic: freeing 0x%x (2^%d) at offset 0x%x\n", + num, order, offset); + + spin_lock_irqsave(&mpic->bitmap_lock, flags); + bitmap_release_region(mpic->hwirq_bitmap, offset, order); + spin_unlock_irqrestore(&mpic->bitmap_lock, flags); +} + +#ifdef CONFIG_MPIC_U3_HT_IRQS +static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic) +{ + irq_hw_number_t hwirq; + struct irq_host_ops *ops = mpic->irqhost->ops; + struct device_node *np; + int flags, index, i; + struct of_irq oirq; + + pr_debug("mpic: found U3, guessing msi allocator setup\n"); + + /* Reserve source numbers we know are reserved in the HW */ + for (i = 0; i < 8; i++) + __mpic_msi_reserve_hwirq(mpic, i); + + for (i = 42; i < 46; i++) + __mpic_msi_reserve_hwirq(mpic, i); + + for (i = 100; i < 105; i++) + __mpic_msi_reserve_hwirq(mpic, i); + + np = NULL; + while ((np = of_find_all_nodes(np))) { + pr_debug("mpic: mapping hwirqs for %s\n", np->full_name); + + index = 0; + while (of_irq_map_one(np, index++, &oirq) == 0) { + ops->xlate(mpic->irqhost, NULL, oirq.specifier, + oirq.size, &hwirq, &flags); + __mpic_msi_reserve_hwirq(mpic, hwirq); + } + } + + return 0; +} +#else +static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic) +{ + return -1; +} +#endif + +static int mpic_msi_reserve_dt_hwirqs(struct mpic *mpic) +{ + int i, len; + const u32 *p; + + p = of_get_property(mpic->of_node, "msi-available-ranges", &len); + if (!p) { + pr_debug("mpic: no msi-available-ranges property found on %s\n", + mpic->of_node->full_name); + return -ENODEV; + } + + if (len % 8 != 0) { + printk(KERN_WARNING "mpic: Malformed msi-available-ranges " + "property on %s\n", mpic->of_node->full_name); + return -EINVAL; + } + + bitmap_allocate_region(mpic->hwirq_bitmap, 0, + get_count_order(mpic->irq_count)); + + /* Format is: ( )+ */ + len /= sizeof(u32); + for (i = 0; i < len / 2; i++, p += 2) + mpic_msi_free_hwirqs(mpic, *p, *(p + 1)); + + return 0; +} + +int mpic_msi_init_allocator(struct mpic *mpic) +{ + int rc, size; + + BUG_ON(mpic->hwirq_bitmap); + spin_lock_init(&mpic->bitmap_lock); + + size = BITS_TO_LONGS(mpic->irq_count) * sizeof(long); + pr_debug("mpic: allocator bitmap size is 0x%x bytes\n", size); + + if (mem_init_done) + mpic->hwirq_bitmap = kmalloc(size, GFP_KERNEL); + else + mpic->hwirq_bitmap = alloc_bootmem(size); + + if (!mpic->hwirq_bitmap) { + pr_debug("mpic: ENOMEM allocating allocator bitmap!\n"); + return -ENOMEM; + } + + memset(mpic->hwirq_bitmap, 0, size); + + rc = mpic_msi_reserve_dt_hwirqs(mpic); + if (rc) { + if (mpic->flags & MPIC_U3_HT_IRQS) + rc = mpic_msi_reserve_u3_hwirqs(mpic); + + if (rc) + goto out_free; + } + + return 0; + + out_free: + if (mem_init_done) + kfree(mpic->hwirq_bitmap); + + mpic->hwirq_bitmap = NULL; + return rc; +} diff --git a/include/asm-powerpc/mpic.h b/include/asm-powerpc/mpic.h index 22c85c4f5b2..2ffb06abe88 100644 --- a/include/asm-powerpc/mpic.h +++ b/include/asm-powerpc/mpic.h @@ -301,6 +301,11 @@ struct mpic u32 *hw_set; #endif +#ifdef CONFIG_PCI_MSI + spinlock_t bitmap_lock; + unsigned long *hwirq_bitmap; +#endif + /* link */ struct mpic *next; -- cgit v1.2.3 From d9b55a03611ff2e2e54fb4e1ad2648d5eb870fa3 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 8 May 2007 12:59:31 +1000 Subject: [POWERPC] Early serial debug support for PPC44x This adds support for early serial debugging via the built in port on IBM/AMCC PowerPC 44x CPUs. It uses a bolted TLB entry in address space 1 for the UART's mapping, allowing robust debugging both before and after the initialization of the MMU. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/Kconfig.debug | 22 ++++++++++++++++++---- arch/powerpc/kernel/head_44x.S | 34 +++++++++++++--------------------- arch/powerpc/kernel/of_platform.c | 1 - arch/powerpc/kernel/udbg.c | 3 +++ arch/powerpc/kernel/udbg_16550.c | 23 +++++++++++++++++++++++ arch/powerpc/platforms/44x/44x.h | 2 ++ arch/powerpc/platforms/44x/misc_44x.S | 31 +++++++++++++++++++++++++++++++ include/asm-powerpc/mmu-44x.h | 6 ++++++ include/asm-powerpc/udbg.h | 1 + 9 files changed, 97 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 86aa3745af7..f70e795c262 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -139,10 +139,6 @@ config BOOTX_TEXT Say Y here to see progress messages from the boot firmware in text mode. Requires either BootX or Open Firmware. -config SERIAL_TEXT_DEBUG - bool "Support for early boot texts over serial port" - depends on 4xx - config PPC_EARLY_DEBUG bool "Early debugging (dangerous)" @@ -207,6 +203,24 @@ config PPC_EARLY_DEBUG_BEAT help Select this to enable early debugging for Celleb with Beat. +config PPC_EARLY_DEBUG_44x + bool "Early serial debugging for IBM/AMCC 44x CPUs" + depends on 44x + select PPC_UDBG_16550 + help + Select this to enable early debugging for IBM 44x chips via the + inbuilt serial port. + endchoice +config PPC_EARLY_DEBUG_44x_PHYSLOW + hex "Low 32 bits of early debug UART physical address" + depends PPC_EARLY_DEBUG_44x + default "0x40000200" + +config PPC_EARLY_DEBUG_44x_PHYSHIGH + hex "EPRN of early debug UART physical address" + depends PPC_EARLY_DEBUG_44x + default "0x1" + endmenu diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S index a51026f0a9d..88695963f58 100644 --- a/arch/powerpc/kernel/head_44x.S +++ b/arch/powerpc/kernel/head_44x.S @@ -172,36 +172,28 @@ skpinv: addi r4,r4,1 /* Increment */ isync 4: -#ifdef CONFIG_SERIAL_TEXT_DEBUG - /* - * Add temporary UART mapping for early debug. - * We can map UART registers wherever we want as long as they don't - * interfere with other system mappings (e.g. with pinned entries). - * For an example of how we handle this - see ocotea.h. --ebs - */ +#ifdef CONFIG_PPC_EARLY_DEBUG_44x + /* Add UART mapping for early debug. */ + /* pageid fields */ - lis r3,UART0_IO_BASE@h - ori r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_4K + lis r3,PPC44x_EARLY_DEBUG_VIRTADDR@h + ori r3,r3,PPC44x_TLB_VALID|PPC44x_TLB_TS|PPC44x_TLB_64K /* xlat fields */ - lis r4,UART0_PHYS_IO_BASE@h /* RPN depends on SoC */ -#ifndef CONFIG_440EP - ori r4,r4,0x0001 /* ERPN is 1 for second 4GB page */ -#endif + lis r4,CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW@h + ori r4,r4,CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH /* attrib fields */ - li r5,0 - ori r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_I | PPC44x_TLB_G) + li r5,(PPC44x_TLB_SW|PPC44x_TLB_SR|PPC44x_TLB_I|PPC44x_TLB_G) + li r0,62 /* TLB slot 0 */ - li r0,0 /* TLB slot 0 */ - - tlbwe r3,r0,PPC44x_TLB_PAGEID /* Load the pageid fields */ - tlbwe r4,r0,PPC44x_TLB_XLAT /* Load the translation fields */ - tlbwe r5,r0,PPC44x_TLB_ATTRIB /* Load the attrib/access fields */ + tlbwe r3,r0,PPC44x_TLB_PAGEID + tlbwe r4,r0,PPC44x_TLB_XLAT + tlbwe r5,r0,PPC44x_TLB_ATTRIB /* Force context change */ isync -#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#endif /* CONFIG_PPC_EARLY_DEBUG_44x */ /* Establish the interrupt vector offsets */ SET_IVOR(0, CriticalInput); diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index 908ed7926db..84c34d979a8 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -29,7 +29,6 @@ #include #include - /* * The list of OF IDs below is used for matching bus types in the * system whose devices are to be exposed as of_platform_devices. diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index 7e0971868fc..7ef97ea8b3e 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -51,6 +51,9 @@ void __init udbg_early_init(void) udbg_init_pas_realmode(); #elif defined(CONFIG_BOOTX_TEXT) udbg_init_btext(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_44x) + /* PPC44x debug */ + udbg_init_44x_as1(); #endif } diff --git a/arch/powerpc/kernel/udbg_16550.c b/arch/powerpc/kernel/udbg_16550.c index a963f657222..7afab5bcd61 100644 --- a/arch/powerpc/kernel/udbg_16550.c +++ b/arch/powerpc/kernel/udbg_16550.c @@ -191,3 +191,26 @@ void udbg_init_pas_realmode(void) udbg_getc_poll = NULL; } #endif /* CONFIG_PPC_MAPLE */ + +#ifdef CONFIG_PPC_EARLY_DEBUG_44x +#include + +static void udbg_44x_as1_putc(char c) +{ + if (udbg_comport) { + while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0) + /* wait for idle */; + as1_writeb(c, &udbg_comport->thr); eieio(); + if (c == '\n') + udbg_44x_as1_putc('\r'); + } +} + +void __init udbg_init_44x_as1(void) +{ + udbg_comport = + (volatile struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR; + + udbg_putc = udbg_44x_as1_putc; +} +#endif /* CONFIG_PPC_EARLY_DEBUG_44x */ diff --git a/arch/powerpc/platforms/44x/44x.h b/arch/powerpc/platforms/44x/44x.h index 45f24b166f4..42eabf87fea 100644 --- a/arch/powerpc/platforms/44x/44x.h +++ b/arch/powerpc/platforms/44x/44x.h @@ -1,6 +1,8 @@ #ifndef __POWERPC_PLATFORMS_44X_44X_H #define __POWERPC_PLATFORMS_44X_44X_H +extern u8 as1_readb(volatile u8 __iomem *addr); +extern void as1_writeb(u8 data, volatile u8 __iomem *addr); extern void ppc44x_reset_system(char *cmd); #endif /* __POWERPC_PLATFORMS_44X_44X_H */ diff --git a/arch/powerpc/platforms/44x/misc_44x.S b/arch/powerpc/platforms/44x/misc_44x.S index 52bde71485b..3bce71d5d75 100644 --- a/arch/powerpc/platforms/44x/misc_44x.S +++ b/arch/powerpc/platforms/44x/misc_44x.S @@ -14,6 +14,37 @@ .text +/* + * Do an IO access in AS1 + */ +_GLOBAL(as1_readb) + mfmsr r7 + ori r0,r7,MSR_DS + sync + mtmsr r0 + sync + isync + lbz r3,0(r3) + sync + mtmsr r7 + sync + isync + blr + +_GLOBAL(as1_writeb) + mfmsr r7 + ori r0,r7,MSR_DS + sync + mtmsr r0 + sync + isync + stb r3,0(r4) + sync + mtmsr r7 + sync + isync + blr + /* * void ppc44x_reset_system(char *cmd) * diff --git a/include/asm-powerpc/mmu-44x.h b/include/asm-powerpc/mmu-44x.h index 7bbc37e27d3..d5ce7a8dfe9 100644 --- a/include/asm-powerpc/mmu-44x.h +++ b/include/asm-powerpc/mmu-44x.h @@ -64,7 +64,13 @@ typedef struct { #endif /* !__ASSEMBLY__ */ +#ifndef CONFIG_PPC_EARLY_DEBUG_44x #define PPC44x_EARLY_TLBS 1 +#else +#define PPC44x_EARLY_TLBS 2 +#define PPC44x_EARLY_DEBUG_VIRTADDR (ASM_CONST(0xf0000000) \ + | (ASM_CONST(CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW) & 0xffff)) +#endif /* Size of the TLBs used for pinning in lowmem */ #define PPC_PIN_SIZE (1 << 28) /* 256M */ diff --git a/include/asm-powerpc/udbg.h b/include/asm-powerpc/udbg.h index d03d8557f70..ce9d82fb7b6 100644 --- a/include/asm-powerpc/udbg.h +++ b/include/asm-powerpc/udbg.h @@ -47,6 +47,7 @@ extern void __init udbg_init_rtas_panel(void); extern void __init udbg_init_rtas_console(void); extern void __init udbg_init_debug_beat(void); extern void __init udbg_init_btext(void); +extern void __init udbg_init_44x_as1(void); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_UDBG_H */ -- cgit v1.2.3