summaryrefslogtreecommitdiff
path: root/arch/sh
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-03-04 16:44:20 +0900
committerPaul Mundt <lethal@linux-sh.org>2010-03-04 16:44:20 +0900
commit281983d6ff2674ca2e4868de628c65809d84fa4c (patch)
treeabdf15ec83c5086220aff0c92d2112f8e05c3041 /arch/sh
parent09e1172317d1038918c5a139ba31155610f802b5 (diff)
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 <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh')
-rw-r--r--arch/sh/mm/pmb.c37
1 files changed, 31 insertions, 6 deletions
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 <linux/io.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
+#include <asm/cacheflush.h>
#include <asm/sizes.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -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);