Skip to content

Commit

Permalink
MN10300: Cache: Implement SMP global cache flushing
Browse files Browse the repository at this point in the history
Implement SMP global cache flushing for MN10300.  This will be used by the AM34
which is SMP capable.

Signed-off-by: Akira Takeuchi <takeuchi.akr@jp.panasonic.com>
Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com>
Signed-off-by: David Howells <dhowells@redhat.com>
  • Loading branch information
Akira Takeuchi authored and David Howells committed Oct 27, 2010
1 parent b478491 commit 8be0628
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 15 deletions.
1 change: 1 addition & 0 deletions arch/mn10300/mm/Kconfig.cache
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ choice

config MN10300_CACHE_MANAGE_BY_TAG
bool "Use the cache tag registers directly"
depends on !(SMP && MN10300_CACHE_WTHRU)

config MN10300_CACHE_MANAGE_BY_REG
bool "Flush areas by way of automatic purge registers (AM34 only)"
Expand Down
3 changes: 3 additions & 0 deletions arch/mn10300/mm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
# Makefile for the MN10300-specific memory management code
#

cache-smp-wback-$(CONFIG_MN10300_CACHE_WBACK) := cache-smp-flush.o

cacheflush-y := cache.o
cacheflush-$(CONFIG_SMP) += cache-smp.o cache-smp-inv.o $(cache-smp-wback-y)
cacheflush-$(CONFIG_MN10300_CACHE_INV_ICACHE) += cache-inv-icache.o
cacheflush-$(CONFIG_MN10300_CACHE_FLUSH_ICACHE) += cache-flush-icache.o
cacheflush-$(CONFIG_MN10300_CACHE_INV_BY_TAG) += cache-inv-by-tag.o
Expand Down
36 changes: 27 additions & 9 deletions arch/mn10300/mm/cache-flush-icache.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <asm/smp.h>
#include "cache-smp.h"

/**
* flush_icache_page - Flush a page from the dcache and invalidate the icache
* @vma: The VMA the page is part of.
Expand All @@ -22,9 +25,15 @@
void flush_icache_page(struct vm_area_struct *vma, struct page *page)
{
unsigned long start = page_to_phys(page);
unsigned long flags;

flags = smp_lock_cache();

mn10300_local_dcache_flush_page(start);
mn10300_local_icache_inv_page(start);

mn10300_dcache_flush_page(start);
mn10300_icache_inv_page(start);
smp_cache_call(SMP_IDCACHE_INV_FLUSH_RANGE, start, start + PAGE_SIZE);
smp_unlock_cache(flags);
}
EXPORT_SYMBOL(flush_icache_page);

Expand Down Expand Up @@ -82,8 +91,9 @@ static void flush_icache_page_range(unsigned long start, unsigned long end)

/* flush the dcache and invalidate the icache coverage on that
* region */
mn10300_dcache_flush_range2(addr + off, size);
mn10300_icache_inv_range2(addr + off, size);
mn10300_local_dcache_flush_range2(addr + off, size);
mn10300_local_icache_inv_range2(addr + off, size);
smp_cache_call(SMP_IDCACHE_INV_FLUSH_RANGE, start, end);
}

/**
Expand All @@ -98,28 +108,32 @@ static void flush_icache_page_range(unsigned long start, unsigned long end)
void flush_icache_range(unsigned long start, unsigned long end)
{
unsigned long start_page, end_page;
unsigned long flags;

flags = smp_lock_cache();

if (end > 0x80000000UL) {
/* addresses above 0xa0000000 do not go through the cache */
if (end > 0xa0000000UL) {
end = 0xa0000000UL;
if (start >= end)
return;
goto done;
}

/* kernel addresses between 0x80000000 and 0x9fffffff do not
* require page tables, so we just map such addresses
* directly */
start_page = (start >= 0x80000000UL) ? start : 0x80000000UL;
mn10300_dcache_flush_range(start_page, end);
mn10300_icache_inv_range(start_page, end);
mn10300_local_dcache_flush_range(start_page, end);
mn10300_local_icache_inv_range(start_page, end);
smp_cache_call(SMP_IDCACHE_INV_FLUSH_RANGE, start_page, end);
if (start_page == start)
return;
goto done;
end = start_page;
}

start_page = start & PAGE_MASK;
end_page = end & PAGE_MASK;
end_page = (end - 1) & PAGE_MASK;

if (start_page == end_page) {
/* the first and last bytes are on the same page */
Expand All @@ -132,6 +146,10 @@ void flush_icache_range(unsigned long start, unsigned long end)
/* more than 2 pages; just flush the entire cache */
mn10300_dcache_flush();
mn10300_icache_inv();
smp_cache_call(SMP_IDCACHE_INV_FLUSH, 0, 0);
}

done:
smp_unlock_cache(flags);
}
EXPORT_SYMBOL(flush_icache_range);
22 changes: 16 additions & 6 deletions arch/mn10300/mm/cache-inv-icache.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <asm/smp.h>
#include "cache-smp.h"

/**
* flush_icache_page_range - Flush dcache and invalidate icache for part of a
Expand Down Expand Up @@ -66,7 +68,8 @@ static void flush_icache_page_range(unsigned long start, unsigned long end)
addr = page_to_phys(page);

/* invalidate the icache coverage on that region */
mn10300_icache_inv_range2(addr + off, size);
mn10300_local_icache_inv_range2(addr + off, size);
smp_cache_call(SMP_ICACHE_INV_FLUSH_RANGE, start, end);
}

/**
Expand All @@ -81,28 +84,31 @@ static void flush_icache_page_range(unsigned long start, unsigned long end)
void flush_icache_range(unsigned long start, unsigned long end)
{
unsigned long start_page, end_page;
unsigned long flags;

flags = smp_lock_cache();

if (end > 0x80000000UL) {
/* addresses above 0xa0000000 do not go through the cache */
if (end > 0xa0000000UL) {
end = 0xa0000000UL;
if (start >= end)
return;
goto done;
}

/* kernel addresses between 0x80000000 and 0x9fffffff do not
* require page tables, so we just map such addresses
* directly */
start_page = (start >= 0x80000000UL) ? start : 0x80000000UL;
mn10300_dcache_flush_range(start_page, end);
mn10300_icache_inv_range(start_page, end);
smp_cache_call(SMP_ICACHE_INV_FLUSH_RANGE, start, end);
if (start_page == start)
return;
goto done;
end = start_page;
}

start_page = start & PAGE_MASK;
end_page = end & PAGE_MASK;
end_page = (end - 1) & PAGE_MASK;

if (start_page == end_page) {
/* the first and last bytes are on the same page */
Expand All @@ -113,7 +119,11 @@ void flush_icache_range(unsigned long start, unsigned long end)
flush_icache_page_range(end_page, end);
} else {
/* more than 2 pages; just flush the entire cache */
mn10300_icache_inv();
mn10300_local_icache_inv();
smp_cache_call(SMP_ICACHE_INV, 0, 0);
}

done:
smp_unlock_cache(flags);
}
EXPORT_SYMBOL(flush_icache_range);
156 changes: 156 additions & 0 deletions arch/mn10300/mm/cache-smp-flush.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/* Functions for global dcache flush when writeback caching in SMP
*
* Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/mm.h>
#include <asm/cacheflush.h>
#include "cache-smp.h"

/**
* mn10300_dcache_flush - Globally flush data cache
*
* Flush the data cache on all CPUs.
*/
void mn10300_dcache_flush(void)
{
unsigned long flags;

flags = smp_lock_cache();
mn10300_local_dcache_flush();
smp_cache_call(SMP_DCACHE_FLUSH, 0, 0);
smp_unlock_cache(flags);
}

/**
* mn10300_dcache_flush_page - Globally flush a page of data cache
* @start: The address of the page of memory to be flushed.
*
* Flush a range of addresses in the data cache on all CPUs covering
* the page that includes the given address.
*/
void mn10300_dcache_flush_page(unsigned long start)
{
unsigned long flags;

start &= ~(PAGE_SIZE-1);

flags = smp_lock_cache();
mn10300_local_dcache_flush_page(start);
smp_cache_call(SMP_DCACHE_FLUSH_RANGE, start, start + PAGE_SIZE);
smp_unlock_cache(flags);
}

/**
* mn10300_dcache_flush_range - Globally flush range of data cache
* @start: The start address of the region to be flushed.
* @end: The end address of the region to be flushed.
*
* Flush a range of addresses in the data cache on all CPUs, between start and
* end-1 inclusive.
*/
void mn10300_dcache_flush_range(unsigned long start, unsigned long end)
{
unsigned long flags;

flags = smp_lock_cache();
mn10300_local_dcache_flush_range(start, end);
smp_cache_call(SMP_DCACHE_FLUSH_RANGE, start, end);
smp_unlock_cache(flags);
}

/**
* mn10300_dcache_flush_range2 - Globally flush range of data cache
* @start: The start address of the region to be flushed.
* @size: The size of the region to be flushed.
*
* Flush a range of addresses in the data cache on all CPUs, between start and
* start+size-1 inclusive.
*/
void mn10300_dcache_flush_range2(unsigned long start, unsigned long size)
{
unsigned long flags;

flags = smp_lock_cache();
mn10300_local_dcache_flush_range2(start, size);
smp_cache_call(SMP_DCACHE_FLUSH_RANGE, start, start + size);
smp_unlock_cache(flags);
}

/**
* mn10300_dcache_flush_inv - Globally flush and invalidate data cache
*
* Flush and invalidate the data cache on all CPUs.
*/
void mn10300_dcache_flush_inv(void)
{
unsigned long flags;

flags = smp_lock_cache();
mn10300_local_dcache_flush_inv();
smp_cache_call(SMP_DCACHE_FLUSH_INV, 0, 0);
smp_unlock_cache(flags);
}

/**
* mn10300_dcache_flush_inv_page - Globally flush and invalidate a page of data
* cache
* @start: The address of the page of memory to be flushed and invalidated.
*
* Flush and invalidate a range of addresses in the data cache on all CPUs
* covering the page that includes the given address.
*/
void mn10300_dcache_flush_inv_page(unsigned long start)
{
unsigned long flags;

start &= ~(PAGE_SIZE-1);

flags = smp_lock_cache();
mn10300_local_dcache_flush_inv_page(start);
smp_cache_call(SMP_DCACHE_FLUSH_INV_RANGE, start, start + PAGE_SIZE);
smp_unlock_cache(flags);
}

/**
* mn10300_dcache_flush_inv_range - Globally flush and invalidate range of data
* cache
* @start: The start address of the region to be flushed and invalidated.
* @end: The end address of the region to be flushed and invalidated.
*
* Flush and invalidate a range of addresses in the data cache on all CPUs,
* between start and end-1 inclusive.
*/
void mn10300_dcache_flush_inv_range(unsigned long start, unsigned long end)
{
unsigned long flags;

flags = smp_lock_cache();
mn10300_local_dcache_flush_inv_range(start, end);
smp_cache_call(SMP_DCACHE_FLUSH_INV_RANGE, start, end);
smp_unlock_cache(flags);
}

/**
* mn10300_dcache_flush_inv_range2 - Globally flush and invalidate range of data
* cache
* @start: The start address of the region to be flushed and invalidated.
* @size: The size of the region to be flushed and invalidated.
*
* Flush and invalidate a range of addresses in the data cache on all CPUs,
* between start and start+size-1 inclusive.
*/
void mn10300_dcache_flush_inv_range2(unsigned long start, unsigned long size)
{
unsigned long flags;

flags = smp_lock_cache();
mn10300_local_dcache_flush_inv_range2(start, size);
smp_cache_call(SMP_DCACHE_FLUSH_INV_RANGE, start, start + size);
smp_unlock_cache(flags);
}
Loading

0 comments on commit 8be0628

Please sign in to comment.