| 1 | /* |
| 2 | * arch/ubicom32/mach-common/cachectl.c |
| 3 | * Architecture cache control support |
| 4 | * |
| 5 | * (C) Copyright 2009, Ubicom, Inc. |
| 6 | * |
| 7 | * This file is part of the Ubicom32 Linux Kernel Port. |
| 8 | * |
| 9 | * The Ubicom32 Linux Kernel Port is free software: you can redistribute |
| 10 | * it and/or modify it under the terms of the GNU General Public License |
| 11 | * as published by the Free Software Foundation, either version 2 of the |
| 12 | * License, or (at your option) any later version. |
| 13 | * |
| 14 | * The Ubicom32 Linux Kernel Port is distributed in the hope that it |
| 15 | * will be useful, but WITHOUT ANY WARRANTY; without even the implied |
| 16 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| 17 | * the GNU General Public License for more details. |
| 18 | * |
| 19 | * You should have received a copy of the GNU General Public License |
| 20 | * along with the Ubicom32 Linux Kernel Port. If not, |
| 21 | * see <http://www.gnu.org/licenses/>. |
| 22 | * |
| 23 | * Ubicom32 implementation derived from (with many thanks): |
| 24 | * arch/m68knommu |
| 25 | * arch/blackfin |
| 26 | * arch/parisc |
| 27 | */ |
| 28 | |
| 29 | #include <linux/types.h> |
| 30 | #include <linux/module.h> |
| 31 | #include <asm/cachectl.h> |
| 32 | |
| 33 | /* |
| 34 | * The write queue flush procedure in mem_cache_control needs to make |
| 35 | * DCACHE_WRITE_QUEUE_LENGTH writes to DDR (not OCM). Here we reserve some |
| 36 | * memory for this operation. |
| 37 | * Allocate array of cache lines of least DCACHE_WRITE_QUEUE_LENGTH + 1 words in |
| 38 | * length rounded up to the nearest cache line. |
| 39 | */ |
| 40 | #define CACHE_WRITE_QUEUE_FLUSH_AREA_SIZE \ |
| 41 | ALIGN(sizeof(int) * (DCACHE_WRITE_QUEUE_LENGTH + 1), CACHE_LINE_SIZE) |
| 42 | |
| 43 | static char cache_write_queue_flush_area[CACHE_WRITE_QUEUE_FLUSH_AREA_SIZE] |
| 44 | __attribute__((aligned(CACHE_LINE_SIZE))); |
| 45 | |
| 46 | /* |
| 47 | * ONE_CCR_ADDR_OP is a helper macro that executes a single CCR operation. |
| 48 | */ |
| 49 | #define ONE_CCR_ADDR_OP(cc, op_addr, op) \ |
| 50 | do { \ |
| 51 | asm volatile ( \ |
| 52 | " btst "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_VALID)" \n\t" \ |
| 53 | " jmpne.f .-4 \n\t" \ |
| 54 | " move.4 "D(CCR_ADDR)"(%0), %1 \n\t" \ |
| 55 | " move.1 "D(CCR_CTRL+3)"(%0), %2 \n\t" \ |
| 56 | " bset "D(CCR_CTRL)"(%0), "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_VALID)" \n\t" \ |
| 57 | " cycles 2 \n\t" \ |
| 58 | " btst "D(CCR_CTRL)"(%0), #"D(CCR_CTRL_DONE)" \n\t" \ |
| 59 | " jmpeq.f .-4 \n\t" \ |
| 60 | : \ |
| 61 | : "a"(cc), "r"(op_addr), "r"(op & 0xff) \ |
| 62 | : "cc" \ |
| 63 | ); \ |
| 64 | } while (0) |
| 65 | |
| 66 | /* |
| 67 | * mem_cache_control() |
| 68 | * Special cache control operation |
| 69 | */ |
| 70 | void mem_cache_control(unsigned long cc, unsigned long begin_addr, |
| 71 | unsigned long end_addr, unsigned long op) |
| 72 | { |
| 73 | unsigned long op_addr; |
| 74 | int dccr = cc == DCCR_BASE; |
| 75 | if (dccr && op == CCR_CTRL_FLUSH_ADDR) { |
| 76 | /* |
| 77 | * We ensure all previous writes have left the data cache write |
| 78 | * queue by sending DCACHE_WRITE_QUEUE_LENGTH writes (to |
| 79 | * different words) down the queue. If this is not done it's |
| 80 | * possible that the data we are trying to flush hasn't even |
| 81 | * entered the data cache. |
| 82 | * The +1 ensure that the final 'flush' is actually a flush. |
| 83 | */ |
| 84 | int *flush_area = (int *)cache_write_queue_flush_area; |
| 85 | asm volatile( |
| 86 | " .rept "D(DCACHE_WRITE_QUEUE_LENGTH + 1)" \n\t" |
| 87 | " move.4 (%0)4++, d0 \n\t" |
| 88 | " .endr \n\t" |
| 89 | : "+a"(flush_area) |
| 90 | ); |
| 91 | } |
| 92 | |
| 93 | if (dccr) |
| 94 | UBICOM32_LOCK(DCCR_LOCK_BIT); |
| 95 | else |
| 96 | UBICOM32_LOCK(ICCR_LOCK_BIT); |
| 97 | |
| 98 | /* |
| 99 | * Calculate the cache lines we need to operate on that include |
| 100 | * begin_addr though end_addr. |
| 101 | */ |
| 102 | begin_addr = begin_addr & ~(CACHE_LINE_SIZE - 1); |
| 103 | end_addr = (end_addr + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1); |
| 104 | op_addr = begin_addr; |
| 105 | |
| 106 | do { |
| 107 | ONE_CCR_ADDR_OP(cc, op_addr, op); |
| 108 | op_addr += CACHE_LINE_SIZE; |
| 109 | } while (likely(op_addr < end_addr)); |
| 110 | |
| 111 | if (dccr && op == CCR_CTRL_FLUSH_ADDR) { |
| 112 | /* |
| 113 | * It turns out that when flushing the data cache the last flush |
| 114 | * isn't actually complete at this point. This is because there |
| 115 | * is another write buffer on the DDR side of the cache that is |
| 116 | * arbitrated with the I-Cache. |
| 117 | * |
| 118 | * The only foolproof method that ensures that the last data |
| 119 | * cache flush *actually* completed is to do another flush on a |
| 120 | * dirty cache line. This flush will block until the DDR write |
| 121 | * buffer is empty. |
| 122 | * |
| 123 | * Rather than creating a another dirty cache line, we use the |
| 124 | * flush_area above as we know that it is dirty from previous |
| 125 | * writes. |
| 126 | */ |
| 127 | ONE_CCR_ADDR_OP(cc, cache_write_queue_flush_area, op); |
| 128 | } |
| 129 | |
| 130 | if (dccr) |
| 131 | UBICOM32_UNLOCK(DCCR_LOCK_BIT); |
| 132 | else |
| 133 | UBICOM32_UNLOCK(ICCR_LOCK_BIT); |
| 134 | |
| 135 | } |
| 136 | EXPORT_SYMBOL(mem_cache_control); |
| 137 | |