Skip to content

Commit

Permalink
sh: fix up MMU reset with variable PMB mapping sizes.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Paul Mundt committed Mar 4, 2010
1 parent 09e1172 commit 281983d
Showing 1 changed file with 31 additions and 6 deletions.
37 changes: 31 additions & 6 deletions arch/sh/mm/pmb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 281983d

Please sign in to comment.