![Paul Mundt](/assets/img/avatar_default.png)
There were a number of places that made evil PAGE_SIZE == 4k assumptions that ended up breaking when trying to play with 8k and 64k page sizes, this fixes those up. The most significant change is the way we load THREAD_SIZE, previously this was done via: mov #(THREAD_SIZE >> 8), reg shll8 reg to avoid a memory access and allow the immediate load. With a 64k PAGE_SIZE, we're out of range for the immediate load size without resorting to special instructions available in later ISAs (movi20s and so on). The "workaround" for this is to bump up the shift to 10 and insert a shll2, which gives a bit more flexibility while still being much cheaper than a memory access. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
96 lines
2.5 KiB
C
96 lines
2.5 KiB
C
/*
|
|
* arch/sh/mm/pg-dma.c
|
|
*
|
|
* Fast clear_page()/copy_page() implementation using the SH DMAC
|
|
*
|
|
* Copyright (C) 2003 Paul Mundt
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <asm/semaphore.h>
|
|
#include <asm/mmu_context.h>
|
|
#include <asm/addrspace.h>
|
|
#include <asm/atomic.h>
|
|
#include <asm/page.h>
|
|
#include <asm/dma.h>
|
|
#include <asm/io.h>
|
|
|
|
/* Channel to use for page ops, must be dual-address mode capable. */
|
|
static int dma_channel = CONFIG_DMA_PAGE_OPS_CHANNEL;
|
|
|
|
static void copy_page_dma(void *to, void *from)
|
|
{
|
|
/*
|
|
* This doesn't seem to get triggered until further along in the
|
|
* boot process, at which point the DMAC is already initialized.
|
|
* Fix this in the same fashion as clear_page_dma() in the event
|
|
* that this crashes due to the DMAC not being initialized.
|
|
*/
|
|
|
|
flush_icache_range((unsigned long)from, PAGE_SIZE);
|
|
dma_write_page(dma_channel, (unsigned long)from, (unsigned long)to);
|
|
dma_wait_for_completion(dma_channel);
|
|
}
|
|
|
|
static void clear_page_dma(void *to)
|
|
{
|
|
/*
|
|
* We get invoked quite early on, if the DMAC hasn't been initialized
|
|
* yet, fall back on the slow manual implementation.
|
|
*/
|
|
if (dma_info[dma_channel].chan != dma_channel) {
|
|
clear_page_slow(to);
|
|
return;
|
|
}
|
|
|
|
dma_write_page(dma_channel, (unsigned long)empty_zero_page,
|
|
(unsigned long)to);
|
|
|
|
/*
|
|
* FIXME: Something is a bit racy here, if we poll the counter right
|
|
* away, we seem to lock. flushing the page from the dcache doesn't
|
|
* seem to make a difference one way or the other, though either a full
|
|
* icache or dcache flush does.
|
|
*
|
|
* The location of this is important as well, and must happen prior to
|
|
* the completion loop but after the transfer was initiated.
|
|
*
|
|
* Oddly enough, this doesn't appear to be an issue for copy_page()..
|
|
*/
|
|
flush_icache_range((unsigned long)to, PAGE_SIZE);
|
|
|
|
dma_wait_for_completion(dma_channel);
|
|
}
|
|
|
|
static int __init pg_dma_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = request_dma(dma_channel, "page ops");
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
copy_page = copy_page_dma;
|
|
clear_page = clear_page_dma;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit pg_dma_exit(void)
|
|
{
|
|
free_dma(dma_channel);
|
|
}
|
|
|
|
module_init(pg_dma_init);
|
|
module_exit(pg_dma_exit);
|
|
|
|
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
|
|
MODULE_DESCRIPTION("Optimized page copy/clear routines using a dual-address mode capable DMAC channel");
|
|
MODULE_LICENSE("GPL");
|
|
|