From 7e3bfc7cfc402458b0386086ab650ce811720927 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 5 Apr 2006 20:42:04 +0100 Subject: [PATCH] [MIPS] Handle IDE PIO cache aliases on SMP. Signed-off-by: Ralf Baechle --- arch/mips/mm/c-r3k.c | 5 ++++ arch/mips/mm/c-r4k.c | 1 + arch/mips/mm/c-sb1.c | 1 + arch/mips/mm/c-tx39.c | 7 +++++ arch/mips/mm/cache.c | 1 + include/asm-mips/cacheflush.h | 1 + include/asm-mips/mach-generic/ide.h | 46 +++++++++++++++++++++++++++-- 7 files changed, 60 insertions(+), 2 deletions(-) diff --git a/arch/mips/mm/c-r3k.c b/arch/mips/mm/c-r3k.c index 9dd1352d574..bb041a22f20 100644 --- a/arch/mips/mm/c-r3k.c +++ b/arch/mips/mm/c-r3k.c @@ -260,6 +260,10 @@ static void r3k_flush_cache_page(struct vm_area_struct *vma, unsigned long page, { } +static void local_r3k_flush_data_cache_page(unsigned long addr) +{ +} + static void r3k_flush_data_cache_page(unsigned long addr) { } @@ -335,6 +339,7 @@ void __init r3k_cache_init(void) flush_icache_range = r3k_flush_icache_range; flush_cache_sigtramp = r3k_flush_cache_sigtramp; + local_flush_data_cache_page = local_r3k_flush_data_cache_page; flush_data_cache_page = r3k_flush_data_cache_page; _dma_cache_wback_inv = r3k_dma_cache_wback_inv; diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index c4c208449d8..d88c6686413 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -1199,6 +1199,7 @@ void __init r4k_cache_init(void) flush_cache_sigtramp = r4k_flush_cache_sigtramp; flush_icache_all = r4k_flush_icache_all; + local_flush_data_cache_page = local_r4k_flush_data_cache_page; flush_data_cache_page = r4k_flush_data_cache_page; flush_icache_range = r4k_flush_icache_range; diff --git a/arch/mips/mm/c-sb1.c b/arch/mips/mm/c-sb1.c index 2f08b535f20..f9b129491b1 100644 --- a/arch/mips/mm/c-sb1.c +++ b/arch/mips/mm/c-sb1.c @@ -528,6 +528,7 @@ void sb1_cache_init(void) flush_cache_page = sb1_flush_cache_page; flush_cache_sigtramp = sb1_flush_cache_sigtramp; + local_flush_data_cache_page = (void *) sb1_nop; flush_data_cache_page = (void *) sb1_nop; /* Full flush */ diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c index fe232e3988e..5dfc9b1901f 100644 --- a/arch/mips/mm/c-tx39.c +++ b/arch/mips/mm/c-tx39.c @@ -216,6 +216,11 @@ static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page tx39_blast_icache_page_indexed(page); } +static void local_tx39_flush_data_cache_page(void * addr) +{ + tx39_blast_dcache_page(addr); +} + static void tx39_flush_data_cache_page(unsigned long addr) { tx39_blast_dcache_page(addr); @@ -381,6 +386,7 @@ void __init tx39_cache_init(void) flush_icache_range = (void *) tx39h_flush_icache_all; flush_cache_sigtramp = (void *) tx39h_flush_icache_all; + local_flush_data_cache_page = (void *) tx39h_flush_icache_all; flush_data_cache_page = (void *) tx39h_flush_icache_all; _dma_cache_wback_inv = tx39h_dma_cache_wback_inv; @@ -406,6 +412,7 @@ void __init tx39_cache_init(void) flush_icache_range = tx39_flush_icache_range; flush_cache_sigtramp = tx39_flush_cache_sigtramp; + local_flush_data_cache_page = local_tx39_flush_data_cache_page; flush_data_cache_page = tx39_flush_data_cache_page; _dma_cache_wback_inv = tx39_dma_cache_wback_inv; diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index 591c22b080e..83a56296be8 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -30,6 +30,7 @@ void (*flush_icache_page)(struct vm_area_struct *vma, struct page *page); /* MIPS specific cache operations */ void (*flush_cache_sigtramp)(unsigned long addr); +void (*local_flush_data_cache_page)(void * addr); void (*flush_data_cache_page)(unsigned long addr); void (*flush_icache_all)(void); diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h index aeae9fabf4a..47bc8f6c20d 100644 --- a/include/asm-mips/cacheflush.h +++ b/include/asm-mips/cacheflush.h @@ -74,6 +74,7 @@ static inline void copy_from_user_page(struct vm_area_struct *vma, extern void (*flush_cache_sigtramp)(unsigned long addr); extern void (*flush_icache_all)(void); +extern void (*local_flush_data_cache_page)(void * addr); extern void (*flush_data_cache_page)(unsigned long addr); /* diff --git a/include/asm-mips/mach-generic/ide.h b/include/asm-mips/mach-generic/ide.h index 550979a9ea9..e3315359500 100644 --- a/include/asm-mips/mach-generic/ide.h +++ b/include/asm-mips/mach-generic/ide.h @@ -104,65 +104,107 @@ static __inline__ unsigned long ide_default_io_base(int index) #endif /* MIPS port and memory-mapped I/O string operations. */ +static inline void __ide_flush_prologue(void) +{ +#ifdef CONFIG_SMP + if (cpu_has_dc_aliases) + preempt_disable(); +#endif +} + +static inline void __ide_flush_epilogue(void) +{ +#ifdef CONFIG_SMP + if (cpu_has_dc_aliases) + preempt_enable(); +#endif +} static inline void __ide_flush_dcache_range(unsigned long addr, unsigned long size) { if (cpu_has_dc_aliases) { unsigned long end = addr + size; - for (; addr < end; addr += PAGE_SIZE) - flush_dcache_page(virt_to_page(addr)); + + while (addr < end) { + local_flush_data_cache_page((void *)addr); + addr += PAGE_SIZE; + } } } +/* + * insw() and gang might be called with interrupts disabled, so we can't + * send IPIs for flushing due to the potencial of deadlocks, see the comment + * above smp_call_function() in arch/mips/kernel/smp.c. We work around the + * problem by disabling preemption so we know we actually perform the flush + * on the processor that actually has the lines to be flushed which hopefully + * is even better for performance anyway. + */ static inline void __ide_insw(unsigned long port, void *addr, unsigned int count) { + __ide_flush_prologue(); insw(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 2); + __ide_flush_epilogue(); } static inline void __ide_insl(unsigned long port, void *addr, unsigned int count) { + __ide_flush_prologue(); insl(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 4); + __ide_flush_epilogue(); } static inline void __ide_outsw(unsigned long port, const void *addr, unsigned long count) { + __ide_flush_prologue(); outsw(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 2); + __ide_flush_epilogue(); } static inline void __ide_outsl(unsigned long port, const void *addr, unsigned long count) { + __ide_flush_prologue(); outsl(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 4); + __ide_flush_epilogue(); } static inline void __ide_mm_insw(void __iomem *port, void *addr, u32 count) { + __ide_flush_prologue(); readsw(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 2); + __ide_flush_epilogue(); } static inline void __ide_mm_insl(void __iomem *port, void *addr, u32 count) { + __ide_flush_prologue(); readsl(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 4); + __ide_flush_epilogue(); } static inline void __ide_mm_outsw(void __iomem *port, void *addr, u32 count) { + __ide_flush_prologue(); writesw(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 2); + __ide_flush_epilogue(); } static inline void __ide_mm_outsl(void __iomem * port, void *addr, u32 count) { + __ide_flush_prologue(); writesl(port, addr, count); __ide_flush_dcache_range((unsigned long)addr, count * 4); + __ide_flush_epilogue(); } /* ide_insw calls insw, not __ide_insw. Why? */ -- 2.41.0