From 4b62c0f1e76fe3327b695c49195af8b58e4da057 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Sat, 27 Feb 2010 18:35:08 +0000 Subject: sh: No need to explicitly include . Since already includes , and the latter file will warn about not having included the former file anyway, there is no value in including rwlock.h explicitly. Signed-off-by: Robert P. J. Day Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 1 - 1 file changed, 1 deletion(-) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 198bcff5e96f..35b364f931ea 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 90e7d649d86f21d478dc134f74c88e19dd472393 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 23 Feb 2010 16:20:53 +0900 Subject: sh: reworked dynamic PMB mapping. This implements a fairly significant overhaul of the dynamic PMB mapping code. The primary change here is that the PMB gets its own VMA that follows the uncached mapping and we attempt to be a bit more intelligent with dynamic sizing, multi-entry mapping, and so forth. Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 256 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 158 insertions(+), 98 deletions(-) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 35b364f931ea..9a516b89839a 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,16 @@ struct pmb_entry { struct pmb_entry *link; }; +static struct { + unsigned long size; + int flag; +} pmb_sizes[] = { + { .size = SZ_512M, .flag = PMB_SZ_512M, }, + { .size = SZ_128M, .flag = PMB_SZ_128M, }, + { .size = SZ_64M, .flag = PMB_SZ_64M, }, + { .size = SZ_16M, .flag = PMB_SZ_16M, }, +}; + static void pmb_unmap_entry(struct pmb_entry *, int depth); static DEFINE_RWLOCK(pmb_rwlock); @@ -72,6 +83,88 @@ static __always_inline unsigned long mk_pmb_data(unsigned int entry) return mk_pmb_entry(entry) | PMB_DATA; } +static __always_inline unsigned int pmb_ppn_in_range(unsigned long ppn) +{ + return ppn >= __pa(memory_start) && ppn < __pa(memory_end); +} + +/* + * Ensure that the PMB entries match our cache configuration. + * + * When we are in 32-bit address extended mode, CCR.CB becomes + * invalid, so care must be taken to manually adjust cacheable + * translations. + */ +static __always_inline unsigned long pmb_cache_flags(void) +{ + unsigned long flags = 0; + +#if defined(CONFIG_CACHE_OFF) + flags |= PMB_WT | PMB_UB; +#elif defined(CONFIG_CACHE_WRITETHROUGH) + flags |= PMB_C | PMB_WT | PMB_UB; +#elif defined(CONFIG_CACHE_WRITEBACK) + flags |= PMB_C; +#endif + + return flags; +} + +/* + * Convert typical pgprot value to the PMB equivalent + */ +static inline unsigned long pgprot_to_pmb_flags(pgprot_t prot) +{ + unsigned long pmb_flags = 0; + u64 flags = pgprot_val(prot); + + if (flags & _PAGE_CACHABLE) + pmb_flags |= PMB_C; + if (flags & _PAGE_WT) + pmb_flags |= PMB_WT | PMB_UB; + + return pmb_flags; +} + +static bool pmb_can_merge(struct pmb_entry *a, struct pmb_entry *b) +{ + return (b->vpn == (a->vpn + a->size)) && + (b->ppn == (a->ppn + a->size)) && + (b->flags == a->flags); +} + +static bool pmb_size_valid(unsigned long size) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) + if (pmb_sizes[i].size == size) + return true; + + return false; +} + +static inline bool pmb_addr_valid(unsigned long addr, unsigned long size) +{ + return (addr >= P1SEG && (addr + size - 1) < P3SEG); +} + +static inline bool pmb_prot_valid(pgprot_t prot) +{ + return (pgprot_val(prot) & _PAGE_USER) == 0; +} + +static int pmb_size_to_flags(unsigned long size) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) + if (pmb_sizes[i].size == size) + return pmb_sizes[i].flag; + + return 0; +} + static int pmb_alloc_entry(void) { int pos; @@ -138,34 +231,14 @@ static void pmb_free(struct pmb_entry *pmbe) pmbe->link = NULL; } -/* - * Ensure that the PMB entries match our cache configuration. - * - * When we are in 32-bit address extended mode, CCR.CB becomes - * invalid, so care must be taken to manually adjust cacheable - * translations. - */ -static __always_inline unsigned long pmb_cache_flags(void) -{ - unsigned long flags = 0; - -#if defined(CONFIG_CACHE_WRITETHROUGH) - flags |= PMB_C | PMB_WT | PMB_UB; -#elif defined(CONFIG_CACHE_WRITEBACK) - flags |= PMB_C; -#endif - - return flags; -} - /* * Must be run uncached. */ static void __set_pmb_entry(struct pmb_entry *pmbe) { - writel_uncached(pmbe->vpn | PMB_V, mk_pmb_addr(pmbe->entry)); - writel_uncached(pmbe->ppn | pmbe->flags | PMB_V, - mk_pmb_data(pmbe->entry)); + /* Set V-bit */ + __raw_writel(pmbe->ppn | pmbe->flags | PMB_V, mk_pmb_data(pmbe->entry)); + __raw_writel(pmbe->vpn | PMB_V, mk_pmb_addr(pmbe->entry)); } static void __clear_pmb_entry(struct pmb_entry *pmbe) @@ -193,39 +266,56 @@ static void set_pmb_entry(struct pmb_entry *pmbe) spin_unlock_irqrestore(&pmbe->lock, flags); } -static struct { - unsigned long size; - int flag; -} pmb_sizes[] = { - { .size = SZ_512M, .flag = PMB_SZ_512M, }, - { .size = SZ_128M, .flag = PMB_SZ_128M, }, - { .size = SZ_64M, .flag = PMB_SZ_64M, }, - { .size = SZ_16M, .flag = PMB_SZ_16M, }, -}; +int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, + unsigned long size, pgprot_t prot) +{ + return 0; +} -long pmb_remap(unsigned long vaddr, unsigned long phys, - unsigned long size, pgprot_t prot) +void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size, + pgprot_t prot, void *caller) { struct pmb_entry *pmbp, *pmbe; - unsigned long wanted; - int pmb_flags, i; - long err; - u64 flags; + unsigned long pmb_flags; + int i, mapped; + unsigned long orig_addr, vaddr; + phys_addr_t offset, last_addr; + phys_addr_t align_mask; + unsigned long aligned; + struct vm_struct *area; - flags = pgprot_val(prot); + /* + * Small mappings need to go through the TLB. + */ + if (size < SZ_16M) + return ERR_PTR(-EINVAL); + if (!pmb_prot_valid(prot)) + return ERR_PTR(-EINVAL); - pmb_flags = PMB_WT | PMB_UB; + pmbp = NULL; + pmb_flags = pgprot_to_pmb_flags(prot); + mapped = 0; - /* Convert typical pgprot value to the PMB equivalent */ - if (flags & _PAGE_CACHABLE) { - pmb_flags |= PMB_C; + for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) + if (size >= pmb_sizes[i].size) + break; - if ((flags & _PAGE_WT) == 0) - pmb_flags &= ~(PMB_WT | PMB_UB); - } + last_addr = phys + size; + align_mask = ~(pmb_sizes[i].size - 1); + offset = phys & ~align_mask; + phys &= align_mask; + aligned = ALIGN(last_addr, pmb_sizes[i].size) - phys; - pmbp = NULL; - wanted = size; + area = __get_vm_area_caller(aligned, VM_IOREMAP, uncached_end, + P3SEG, caller); + if (!area) + return NULL; + + area->phys_addr = phys; + orig_addr = vaddr = (unsigned long)area->addr; + + if (!pmb_addr_valid(vaddr, aligned)) + return ERR_PTR(-EFAULT); again: for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) { @@ -237,19 +327,19 @@ again: pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag, PMB_NO_ENTRY); if (IS_ERR(pmbe)) { - err = PTR_ERR(pmbe); - goto out; + pmb_unmap_entry(pmbp, mapped); + return pmbe; } spin_lock_irqsave(&pmbe->lock, flags); + pmbe->size = pmb_sizes[i].size; + __set_pmb_entry(pmbe); - phys += pmb_sizes[i].size; - vaddr += pmb_sizes[i].size; - size -= pmb_sizes[i].size; - - pmbe->size = pmb_sizes[i].size; + phys += pmbe->size; + vaddr += pmbe->size; + size -= pmbe->size; /* * Link adjacent entries that span multiple PMB entries @@ -269,6 +359,7 @@ again: * pmb_sizes[i].size again. */ i--; + mapped++; spin_unlock_irqrestore(&pmbe->lock, flags); } @@ -276,61 +367,35 @@ again: if (size >= SZ_16M) goto again; - return wanted - size; - -out: - pmb_unmap_entry(pmbp, NR_PMB_ENTRIES); - - return err; + return (void __iomem *)(offset + (char *)orig_addr); } -void pmb_unmap(unsigned long addr) +int pmb_unmap(void __iomem *addr) { struct pmb_entry *pmbe = NULL; - int i; + unsigned long vaddr = (unsigned long __force)addr; + int i, found = 0; read_lock(&pmb_rwlock); for (i = 0; i < ARRAY_SIZE(pmb_entry_list); i++) { if (test_bit(i, pmb_map)) { pmbe = &pmb_entry_list[i]; - if (pmbe->vpn == addr) + if (pmbe->vpn == vaddr) { + found = 1; break; + } } } read_unlock(&pmb_rwlock); - pmb_unmap_entry(pmbe, NR_PMB_ENTRIES); -} - -static bool pmb_can_merge(struct pmb_entry *a, struct pmb_entry *b) -{ - return (b->vpn == (a->vpn + a->size)) && - (b->ppn == (a->ppn + a->size)) && - (b->flags == a->flags); -} - -static bool pmb_size_valid(unsigned long size) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) - if (pmb_sizes[i].size == size) - return true; - - return false; -} - -static int pmb_size_to_flags(unsigned long size) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) - if (pmb_sizes[i].size == size) - return pmb_sizes[i].flag; + if (found) { + pmb_unmap_entry(pmbe, NR_PMB_ENTRIES); + return 0; + } - return 0; + return -EINVAL; } static void __pmb_unmap_entry(struct pmb_entry *pmbe, int depth) @@ -368,11 +433,6 @@ static void pmb_unmap_entry(struct pmb_entry *pmbe, int depth) write_unlock_irqrestore(&pmb_rwlock, flags); } -static __always_inline unsigned int pmb_ppn_in_range(unsigned long ppn) -{ - return ppn >= __pa(memory_start) && ppn < __pa(memory_end); -} - static void __init pmb_notify(void) { int i; -- cgit v1.2.3 From 4cfa8e75d6854699597e21fd570721d63f899934 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 2 Mar 2010 16:49:50 +0900 Subject: sh: make pmb iomapping configurable. This plugs in an early_param for permitting transparent PMB-backed ioremapping to be enabled/disabled. For the time being, we use a default-disabled policy. Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 9a516b89839a..9429355c18ca 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -68,6 +68,8 @@ static DEFINE_RWLOCK(pmb_rwlock); static struct pmb_entry pmb_entry_list[NR_PMB_ENTRIES]; static DECLARE_BITMAP(pmb_map, NR_PMB_ENTRIES); +static unsigned int pmb_iomapping_enabled; + static __always_inline unsigned long mk_pmb_entry(unsigned int entry) { return (entry & PMB_E_MASK) << PMB_E_SHIFT; @@ -284,6 +286,9 @@ void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size, unsigned long aligned; struct vm_struct *area; + if (!pmb_iomapping_enabled) + return NULL; + /* * Small mappings need to go through the TLB. */ @@ -684,6 +689,18 @@ static void __init pmb_resize(void) } #endif +static int __init early_pmb(char *p) +{ + if (!p) + return 0; + + if (strstr(p, "iomap")) + pmb_iomapping_enabled = 1; + + return 0; +} +early_param("pmb", early_pmb); + void __init pmb_init(void) { /* Synchronize software state */ -- cgit v1.2.3 From 6eb3c735d29e799810ce82118f9260d0044327b7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 2 Mar 2010 17:22:29 +0900 Subject: sh: fixed virt/phys mapping helpers for PMB. This moves the pmb_remap_caller() mapping logic out in to pmb_bolt_mapping(), which enables us to establish fixed mappings in places such as the NUMA code. Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 97 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 46 deletions(-) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 9429355c18ca..55d21902d707 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -270,60 +270,19 @@ static void set_pmb_entry(struct pmb_entry *pmbe) int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, unsigned long size, pgprot_t prot) -{ - return 0; -} - -void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size, - pgprot_t prot, void *caller) { struct pmb_entry *pmbp, *pmbe; unsigned long pmb_flags; int i, mapped; - unsigned long orig_addr, vaddr; - phys_addr_t offset, last_addr; - phys_addr_t align_mask; - unsigned long aligned; - struct vm_struct *area; - if (!pmb_iomapping_enabled) - return NULL; - - /* - * Small mappings need to go through the TLB. - */ - if (size < SZ_16M) - return ERR_PTR(-EINVAL); - if (!pmb_prot_valid(prot)) - return ERR_PTR(-EINVAL); + if (!pmb_addr_valid(vaddr, size)) + return -EFAULT; - pmbp = NULL; pmb_flags = pgprot_to_pmb_flags(prot); - mapped = 0; - - for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) - if (size >= pmb_sizes[i].size) - break; - - last_addr = phys + size; - align_mask = ~(pmb_sizes[i].size - 1); - offset = phys & ~align_mask; - phys &= align_mask; - aligned = ALIGN(last_addr, pmb_sizes[i].size) - phys; - - area = __get_vm_area_caller(aligned, VM_IOREMAP, uncached_end, - P3SEG, caller); - if (!area) - return NULL; - - area->phys_addr = phys; - orig_addr = vaddr = (unsigned long)area->addr; - - if (!pmb_addr_valid(vaddr, aligned)) - return ERR_PTR(-EFAULT); + pmbp = NULL; again: - for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) { + for (i = mapped = 0; i < ARRAY_SIZE(pmb_sizes); i++) { unsigned long flags; if (size < pmb_sizes[i].size) @@ -333,7 +292,7 @@ again: PMB_NO_ENTRY); if (IS_ERR(pmbe)) { pmb_unmap_entry(pmbp, mapped); - return pmbe; + return PTR_ERR(pmbe); } spin_lock_irqsave(&pmbe->lock, flags); @@ -372,6 +331,52 @@ again: if (size >= SZ_16M) goto again; + return 0; +} + +void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size, + pgprot_t prot, void *caller) +{ + unsigned long orig_addr, vaddr; + phys_addr_t offset, last_addr; + phys_addr_t align_mask; + unsigned long aligned; + struct vm_struct *area; + int i, ret; + + if (!pmb_iomapping_enabled) + return NULL; + + /* + * Small mappings need to go through the TLB. + */ + if (size < SZ_16M) + return ERR_PTR(-EINVAL); + if (!pmb_prot_valid(prot)) + return ERR_PTR(-EINVAL); + + for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) + if (size >= pmb_sizes[i].size) + break; + + last_addr = phys + size; + align_mask = ~(pmb_sizes[i].size - 1); + offset = phys & ~align_mask; + phys &= align_mask; + aligned = ALIGN(last_addr, pmb_sizes[i].size) - phys; + + area = __get_vm_area_caller(aligned, VM_IOREMAP, uncached_end, + P3SEG, caller); + if (!area) + return NULL; + + area->phys_addr = phys; + orig_addr = vaddr = (unsigned long)area->addr; + + ret = pmb_bolt_mapping(vaddr, phys, size, prot); + if (ret != 0) + return ERR_PTR(ret); + return (void __iomem *)(offset + (char *)orig_addr); } -- cgit v1.2.3 From a1042aa248e4ea7f39d5ce13f080cbf3b6c42618 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 3 Mar 2010 13:13:25 +0900 Subject: sh: check for existing mappings for bolted PMB entries. When entries are being bolted unconditionally it's possible that the boot loader has established mappings that are within range that we don't want to clobber. Perform some basic validation to ensure that the new mapping is out of range before allowing the entry setup to take place. Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 140 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 44 deletions(-) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 55d21902d707..75b8861ec624 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -128,13 +128,67 @@ static inline unsigned long pgprot_to_pmb_flags(pgprot_t prot) return pmb_flags; } -static bool pmb_can_merge(struct pmb_entry *a, struct pmb_entry *b) +static inline bool pmb_can_merge(struct pmb_entry *a, struct pmb_entry *b) { return (b->vpn == (a->vpn + a->size)) && (b->ppn == (a->ppn + a->size)) && (b->flags == a->flags); } +static bool pmb_mapping_exists(unsigned long vaddr, phys_addr_t phys, + unsigned long size) +{ + int i; + + read_lock(&pmb_rwlock); + + for (i = 0; i < ARRAY_SIZE(pmb_entry_list); i++) { + struct pmb_entry *pmbe, *iter; + unsigned long span; + + if (!test_bit(i, pmb_map)) + continue; + + pmbe = &pmb_entry_list[i]; + + /* + * See if VPN and PPN are bounded by an existing mapping. + */ + if ((vaddr < pmbe->vpn) || (vaddr >= (pmbe->vpn + pmbe->size))) + continue; + if ((phys < pmbe->ppn) || (phys >= (pmbe->ppn + pmbe->size))) + continue; + + /* + * Now see if we're in range of a simple mapping. + */ + if (size <= pmbe->size) { + read_unlock(&pmb_rwlock); + return true; + } + + span = pmbe->size; + + /* + * Finally for sizes that involve compound mappings, walk + * the chain. + */ + for (iter = pmbe->link; iter; iter = iter->link) + span += iter->size; + + /* + * Nothing else to do if the range requirements are met. + */ + if (size <= span) { + read_unlock(&pmb_rwlock); + return true; + } + } + + read_unlock(&pmb_rwlock); + return false; +} + static bool pmb_size_valid(unsigned long size) { int i; @@ -272,64 +326,62 @@ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, unsigned long size, pgprot_t prot) { struct pmb_entry *pmbp, *pmbe; - unsigned long pmb_flags; + unsigned long flags, pmb_flags; int i, mapped; if (!pmb_addr_valid(vaddr, size)) return -EFAULT; + if (pmb_mapping_exists(vaddr, phys, size)) + return 0; pmb_flags = pgprot_to_pmb_flags(prot); pmbp = NULL; -again: - for (i = mapped = 0; i < ARRAY_SIZE(pmb_sizes); i++) { - unsigned long flags; - - if (size < pmb_sizes[i].size) - continue; - - pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag, - PMB_NO_ENTRY); - if (IS_ERR(pmbe)) { - pmb_unmap_entry(pmbp, mapped); - return PTR_ERR(pmbe); - } - - spin_lock_irqsave(&pmbe->lock, flags); + do { + for (i = mapped = 0; i < ARRAY_SIZE(pmb_sizes); i++) { + if (size < pmb_sizes[i].size) + continue; + + pmbe = pmb_alloc(vaddr, phys, pmb_flags | + pmb_sizes[i].flag, PMB_NO_ENTRY); + if (IS_ERR(pmbe)) { + pmb_unmap_entry(pmbp, mapped); + return PTR_ERR(pmbe); + } - pmbe->size = pmb_sizes[i].size; + spin_lock_irqsave(&pmbe->lock, flags); - __set_pmb_entry(pmbe); + pmbe->size = pmb_sizes[i].size; - phys += pmbe->size; - vaddr += pmbe->size; - size -= pmbe->size; + __set_pmb_entry(pmbe); - /* - * Link adjacent entries that span multiple PMB entries - * for easier tear-down. - */ - if (likely(pmbp)) { - spin_lock(&pmbp->lock); - pmbp->link = pmbe; - spin_unlock(&pmbp->lock); - } + phys += pmbe->size; + vaddr += pmbe->size; + size -= pmbe->size; - pmbp = pmbe; + /* + * Link adjacent entries that span multiple PMB + * entries for easier tear-down. + */ + if (likely(pmbp)) { + spin_lock(&pmbp->lock); + pmbp->link = pmbe; + spin_unlock(&pmbp->lock); + } - /* - * Instead of trying smaller sizes on every iteration - * (even if we succeed in allocating space), try using - * pmb_sizes[i].size again. - */ - i--; - mapped++; + pmbp = pmbe; - spin_unlock_irqrestore(&pmbe->lock, flags); - } + /* + * Instead of trying smaller sizes on every + * iteration (even if we succeed in allocating + * space), try using pmb_sizes[i].size again. + */ + i--; + mapped++; - if (size >= SZ_16M) - goto again; + spin_unlock_irqrestore(&pmbe->lock, flags); + } + } while (size >= SZ_16M); return 0; } @@ -374,7 +426,7 @@ void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size, orig_addr = vaddr = (unsigned long)area->addr; ret = pmb_bolt_mapping(vaddr, phys, size, prot); - if (ret != 0) + if (unlikely(ret != 0)) return ERR_PTR(ret); return (void __iomem *)(offset + (char *)orig_addr); -- cgit v1.2.3 From 281983d6ff2674ca2e4868de628c65809d84fa4c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 4 Mar 2010 16:44:20 +0900 Subject: sh: fix up MMU reset with variable PMB mapping sizes. Presently we run in to issues with the MMU resetting the CPU when variable sized mappings are employed. This takes a slightly more aggressive approach to keeping the TLB and cache state sane before establishing the mappings in order to cut down on races observed on SMP configurations. At the same time, we bump the VMA range up to the 0xb000...0xc000 range, as there still seems to be some undocumented behaviour in setting up variable mappings in the 0xa000...0xb000 range, resulting in reset by the TLB. Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 75b8861ec624..30035caeb73a 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -292,9 +293,18 @@ static void pmb_free(struct pmb_entry *pmbe) */ static void __set_pmb_entry(struct pmb_entry *pmbe) { + unsigned long addr, data; + + addr = mk_pmb_addr(pmbe->entry); + data = mk_pmb_data(pmbe->entry); + + jump_to_uncached(); + /* Set V-bit */ - __raw_writel(pmbe->ppn | pmbe->flags | PMB_V, mk_pmb_data(pmbe->entry)); - __raw_writel(pmbe->vpn | PMB_V, mk_pmb_addr(pmbe->entry)); + __raw_writel(pmbe->vpn | PMB_V, addr); + __raw_writel(pmbe->ppn | pmbe->flags | PMB_V, data); + + back_to_cached(); } static void __clear_pmb_entry(struct pmb_entry *pmbe) @@ -326,6 +336,7 @@ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, unsigned long size, pgprot_t prot) { struct pmb_entry *pmbp, *pmbe; + unsigned long orig_addr, orig_size; unsigned long flags, pmb_flags; int i, mapped; @@ -334,6 +345,11 @@ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, if (pmb_mapping_exists(vaddr, phys, size)) return 0; + orig_addr = vaddr; + orig_size = size; + + flush_tlb_kernel_range(vaddr, vaddr + size); + pmb_flags = pgprot_to_pmb_flags(prot); pmbp = NULL; @@ -383,13 +399,15 @@ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, } } while (size >= SZ_16M); + flush_cache_vmap(orig_addr, orig_addr + orig_size); + return 0; } void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size, pgprot_t prot, void *caller) { - unsigned long orig_addr, vaddr; + unsigned long vaddr; phys_addr_t offset, last_addr; phys_addr_t align_mask; unsigned long aligned; @@ -417,19 +435,24 @@ void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size, phys &= align_mask; aligned = ALIGN(last_addr, pmb_sizes[i].size) - phys; - area = __get_vm_area_caller(aligned, VM_IOREMAP, uncached_end, + /* + * XXX: This should really start from uncached_end, but this + * causes the MMU to reset, so for now we restrict it to the + * 0xb000...0xc000 range. + */ + area = __get_vm_area_caller(aligned, VM_IOREMAP, 0xb0000000, P3SEG, caller); if (!area) return NULL; area->phys_addr = phys; - orig_addr = vaddr = (unsigned long)area->addr; + vaddr = (unsigned long)area->addr; ret = pmb_bolt_mapping(vaddr, phys, size, prot); if (unlikely(ret != 0)) return ERR_PTR(ret); - return (void __iomem *)(offset + (char *)orig_addr); + return (void __iomem *)(offset + (char *)vaddr); } int pmb_unmap(void __iomem *addr) @@ -477,6 +500,8 @@ static void __pmb_unmap_entry(struct pmb_entry *pmbe, int depth) */ __clear_pmb_entry(pmbe); + flush_cache_vunmap(pmbe->vpn, pmbe->vpn + pmbe->size); + pmbe = pmblink->link; pmb_free(pmblink); -- cgit v1.2.3 From 62c8cbbfc2367e706317f56ac21959120ae72773 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Fri, 19 Feb 2010 10:26:31 +0000 Subject: sh: Move PMB debugfs entry initialization to later stage ... so the "sh_debugfs_root" is already available. Previously it wasn't and in result its path was "/sys/kernel/debug/pmb" instead of "/sys/kernel/debug/sh/pmb". Signed-off-by: Pawel Moll Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 30035caeb73a..a4662e2782c3 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -871,7 +871,7 @@ static int __init pmb_debugfs_init(void) return 0; } -postcore_initcall(pmb_debugfs_init); +subsys_initcall(pmb_debugfs_init); #ifdef CONFIG_PM static int pmb_sysdev_suspend(struct sys_device *dev, pm_message_t state) -- cgit v1.2.3 From b5b6c7eea1124de5b110a48ac62650a690ed2419 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 21 Mar 2010 19:51:52 +0000 Subject: sh: Replace unsafe manipulation of MMUCR Setting the TI in MMUCR causes all the TLB bits in MMUCR to be cleared. Unfortunately, the TLB wired bits are also cleared when setting the TI bit, causing any wired TLB entries to become unwired. Use local_flush_tlb_all() which implements TLB flushing in a safer manner by using the memory-mapped TLB registers. As each CPU has its own PMB the modifications in pmb_init() only affect the local CPU, so only flush the local CPU's TLB. Signed-off-by: Matt Fleming Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index a4662e2782c3..0b14dcf05da7 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -802,7 +802,7 @@ void __init pmb_init(void) writel_uncached(0, PMB_IRMCR); /* Flush out the TLB */ - __raw_writel(__raw_readl(MMUCR) | MMUCR_TI, MMUCR); + local_flush_tlb_all(); ctrl_barrier(); } -- cgit v1.2.3 From 3fe0f36c7edcd20af0a3cafc68bdd62534c0a7f0 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Mon, 22 Mar 2010 22:09:58 +0000 Subject: sh: Fix build after dynamic PMB rework set_pmb_entry() is now only used by a function that is wrapped in #ifdef CONFIG_PM, so wrap set_pmb_entry() in CONFIG_PM too. Signed-off-by: Matt Fleming Signed-off-by: Paul Mundt --- arch/sh/mm/pmb.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/sh/mm/pmb.c') diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 0b14dcf05da7..3cc21933063b 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -323,6 +323,7 @@ static void __clear_pmb_entry(struct pmb_entry *pmbe) writel_uncached(data_val & ~PMB_V, data); } +#ifdef CONFIG_PM static void set_pmb_entry(struct pmb_entry *pmbe) { unsigned long flags; @@ -331,6 +332,7 @@ static void set_pmb_entry(struct pmb_entry *pmbe) __set_pmb_entry(pmbe); spin_unlock_irqrestore(&pmbe->lock, flags); } +#endif /* CONFIG_PM */ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, unsigned long size, pgprot_t prot) -- cgit v1.2.3