paul@33 | 1 | /* |
paul@33 | 2 | * Common routines supporting board initialisation. |
paul@33 | 3 | * |
paul@33 | 4 | * Copyright (C) 2000-2009 Wolfgang Denk, DENX Software Engineering, <wd@denx.de> |
paul@33 | 5 | * Copyright (C) 2005-2006 Ingenic Semiconductor, <jlwei@ingenic.cn> |
paul@33 | 6 | * Copyright (C) Xiangfu Liu <xiangfu.z@gmail.com> |
paul@33 | 7 | * Copyright (C) 2015 Paul Boddie <paul@boddie.org.uk> |
paul@33 | 8 | * |
paul@33 | 9 | * This program is free software; you can redistribute it and/or modify it under |
paul@33 | 10 | * the terms of the GNU General Public License as published by the Free Software |
paul@33 | 11 | * Foundation; either version 3 of the License, or (at your option) any later |
paul@33 | 12 | * version. |
paul@33 | 13 | * |
paul@33 | 14 | * This program is distributed in the hope that it will be useful, but WITHOUT |
paul@33 | 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@33 | 16 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@33 | 17 | * details. |
paul@33 | 18 | * |
paul@33 | 19 | * You should have received a copy of the GNU General Public License along with |
paul@33 | 20 | * this program. If not, see <http://www.gnu.org/licenses/>. |
paul@33 | 21 | */ |
paul@33 | 22 | |
paul@33 | 23 | #include "sdram.h" |
paul@33 | 24 | #include "board.h" |
paul@33 | 25 | |
paul@33 | 26 | unsigned long get_memory_size(void) |
paul@33 | 27 | { |
paul@33 | 28 | unsigned int dmcr; |
paul@33 | 29 | unsigned int rows, cols, dw, banks; |
paul@33 | 30 | unsigned long size; |
paul@33 | 31 | |
paul@33 | 32 | dmcr = REG_EMC_DMCR; |
paul@33 | 33 | rows = SDRAM_ROW0 + ((dmcr & EMC_DMCR_RA_MASK) >> EMC_DMCR_RA_BIT); |
paul@33 | 34 | cols = SDRAM_COL0 + ((dmcr & EMC_DMCR_CA_MASK) >> EMC_DMCR_CA_BIT); |
paul@33 | 35 | dw = (dmcr & EMC_DMCR_BW) ? 2 : 4; |
paul@33 | 36 | banks = (dmcr & EMC_DMCR_BA) ? 4 : 2; |
paul@33 | 37 | |
paul@33 | 38 | size = (1 << (rows + cols)) * dw * banks; |
paul@33 | 39 | |
paul@33 | 40 | return size; |
paul@33 | 41 | } |
paul@33 | 42 | |
paul@33 | 43 | /* Timer routines. */ |
paul@33 | 44 | |
paul@33 | 45 | extern unsigned long timestamp; |
paul@33 | 46 | extern unsigned long lastdec; |
paul@33 | 47 | |
paul@33 | 48 | unsigned long get_timer_masked (void) |
paul@33 | 49 | { |
paul@33 | 50 | unsigned long now = READ_TIMER; |
paul@33 | 51 | |
paul@33 | 52 | if (lastdec <= now) { |
paul@33 | 53 | /* normal mode */ |
paul@33 | 54 | timestamp += (now - lastdec); |
paul@33 | 55 | } else { |
paul@33 | 56 | /* we have an overflow ... */ |
paul@33 | 57 | timestamp += TIMER_FDATA + now - lastdec; |
paul@33 | 58 | } |
paul@33 | 59 | lastdec = now; |
paul@33 | 60 | |
paul@33 | 61 | return timestamp; |
paul@33 | 62 | } |
paul@33 | 63 | |
paul@33 | 64 | void reset_timer_masked (void) |
paul@33 | 65 | { |
paul@33 | 66 | /* reset time */ |
paul@33 | 67 | lastdec = READ_TIMER; |
paul@33 | 68 | timestamp = 0; |
paul@33 | 69 | } |
paul@33 | 70 | |
paul@33 | 71 | void reset_timer(void) |
paul@33 | 72 | { |
paul@33 | 73 | reset_timer_masked (); |
paul@33 | 74 | } |
paul@33 | 75 | |
paul@33 | 76 | unsigned long get_timer(unsigned long base) |
paul@33 | 77 | { |
paul@33 | 78 | return get_timer_masked () - base; |
paul@33 | 79 | } |
paul@33 | 80 | |
paul@33 | 81 | void set_timer(unsigned long t) |
paul@33 | 82 | { |
paul@33 | 83 | timestamp = t; |
paul@33 | 84 | } |
paul@33 | 85 | |
paul@33 | 86 | void udelay (unsigned long usec) |
paul@33 | 87 | { |
paul@33 | 88 | unsigned long tmo,tmp; |
paul@33 | 89 | |
paul@33 | 90 | /* normalize */ |
paul@33 | 91 | if (usec >= 1000) { |
paul@33 | 92 | tmo = usec / 1000; |
paul@33 | 93 | tmo *= TIMER_HZ; |
paul@33 | 94 | tmo /= 1000; |
paul@33 | 95 | } |
paul@33 | 96 | else { |
paul@33 | 97 | if (usec >= 1) { |
paul@33 | 98 | tmo = usec * TIMER_HZ; |
paul@33 | 99 | tmo /= (1000*1000); |
paul@33 | 100 | } |
paul@33 | 101 | else |
paul@33 | 102 | tmo = 1; |
paul@33 | 103 | } |
paul@33 | 104 | |
paul@33 | 105 | /* check for rollover during this delay */ |
paul@33 | 106 | tmp = get_timer (0); |
paul@33 | 107 | if ((tmp + tmo) < tmp ) |
paul@33 | 108 | reset_timer_masked(); /* timer would roll over */ |
paul@33 | 109 | else |
paul@33 | 110 | tmo += tmp; |
paul@33 | 111 | |
paul@33 | 112 | while (get_timer_masked () < tmo); |
paul@33 | 113 | } |
paul@33 | 114 | |
paul@33 | 115 | void udelay_masked (unsigned long usec) |
paul@33 | 116 | { |
paul@33 | 117 | unsigned long tmo; |
paul@33 | 118 | unsigned long endtime; |
paul@33 | 119 | signed long diff; |
paul@33 | 120 | |
paul@33 | 121 | /* normalize */ |
paul@33 | 122 | if (usec >= 1000) { |
paul@33 | 123 | tmo = usec / 1000; |
paul@33 | 124 | tmo *= TIMER_HZ; |
paul@33 | 125 | tmo /= 1000; |
paul@33 | 126 | } else { |
paul@33 | 127 | if (usec > 1) { |
paul@33 | 128 | tmo = usec * TIMER_HZ; |
paul@33 | 129 | tmo /= (1000*1000); |
paul@33 | 130 | } else { |
paul@33 | 131 | tmo = 1; |
paul@33 | 132 | } |
paul@33 | 133 | } |
paul@33 | 134 | |
paul@33 | 135 | endtime = get_timer_masked () + tmo; |
paul@33 | 136 | |
paul@33 | 137 | do { |
paul@33 | 138 | unsigned long now = get_timer_masked (); |
paul@33 | 139 | diff = endtime - now; |
paul@33 | 140 | } while (diff >= 0); |
paul@33 | 141 | } |
paul@33 | 142 | |
paul@33 | 143 | /* |
paul@33 | 144 | * This function is derived from PowerPC code (read timebase as long long). |
paul@33 | 145 | * On MIPS it just returns the timer value. |
paul@33 | 146 | */ |
paul@33 | 147 | unsigned long long get_ticks(void) |
paul@33 | 148 | { |
paul@33 | 149 | return get_timer(0); |
paul@33 | 150 | } |
paul@33 | 151 | |
paul@33 | 152 | /* |
paul@33 | 153 | * This function is derived from PowerPC code (timebase clock frequency). |
paul@33 | 154 | * On MIPS it returns the number of timer ticks per second. |
paul@33 | 155 | */ |
paul@33 | 156 | unsigned long get_tbclk (void) |
paul@33 | 157 | { |
paul@33 | 158 | return TIMER_HZ; |
paul@33 | 159 | } |
paul@33 | 160 | |
paul@33 | 161 | /* CPU-specific routines from U-Boot. |
paul@33 | 162 | See: uboot-xburst/files/arch/mips/cpu/xburst/cpu.c |
paul@33 | 163 | See: u-boot/arch/mips/include/asm/cacheops.h |
paul@33 | 164 | */ |
paul@33 | 165 | |
paul@33 | 166 | #define Index_Store_Tag_I 0x08 |
paul@33 | 167 | #define Index_Writeback_Inv_D 0x15 |
paul@33 | 168 | |
paul@33 | 169 | void flush_icache_all(void) |
paul@33 | 170 | { |
paul@33 | 171 | u32 addr, t = 0; |
paul@33 | 172 | |
paul@33 | 173 | asm volatile ("mtc0 $0, $28"); /* Clear Taglo */ |
paul@33 | 174 | asm volatile ("mtc0 $0, $29"); /* Clear TagHi */ |
paul@33 | 175 | |
paul@33 | 176 | for (addr = KSEG0; addr < KSEG0 + CONFIG_SYS_ICACHE_SIZE; |
paul@33 | 177 | addr += CONFIG_SYS_CACHELINE_SIZE) { |
paul@33 | 178 | asm volatile ( |
paul@33 | 179 | ".set mips3\n\t" |
paul@33 | 180 | " cache %0, 0(%1)\n\t" |
paul@33 | 181 | ".set mips2\n\t" |
paul@33 | 182 | : |
paul@33 | 183 | : "I" (Index_Store_Tag_I), "r"(addr)); |
paul@33 | 184 | } |
paul@33 | 185 | |
paul@33 | 186 | /* invalicate btb */ |
paul@33 | 187 | asm volatile ( |
paul@33 | 188 | ".set mips32\n\t" |
paul@33 | 189 | "mfc0 %0, $16, 7\n\t" |
paul@33 | 190 | "nop\n\t" |
paul@33 | 191 | "ori %0,2\n\t" |
paul@33 | 192 | "mtc0 %0, $16, 7\n\t" |
paul@33 | 193 | ".set mips2\n\t" |
paul@33 | 194 | : |
paul@33 | 195 | : "r" (t)); |
paul@33 | 196 | } |
paul@33 | 197 | |
paul@33 | 198 | void flush_dcache_all(void) |
paul@33 | 199 | { |
paul@33 | 200 | u32 addr; |
paul@33 | 201 | |
paul@33 | 202 | for (addr = KSEG0; addr < KSEG0 + CONFIG_SYS_DCACHE_SIZE; |
paul@33 | 203 | addr += CONFIG_SYS_CACHELINE_SIZE) { |
paul@33 | 204 | asm volatile ( |
paul@33 | 205 | ".set mips3\n\t" |
paul@33 | 206 | " cache %0, 0(%1)\n\t" |
paul@33 | 207 | ".set mips2\n\t" |
paul@33 | 208 | : |
paul@33 | 209 | : "I" (Index_Writeback_Inv_D), "r"(addr)); |
paul@33 | 210 | } |
paul@33 | 211 | |
paul@33 | 212 | asm volatile ("sync"); |
paul@33 | 213 | } |
paul@33 | 214 | |
paul@33 | 215 | void flush_cache_all(void) |
paul@33 | 216 | { |
paul@33 | 217 | flush_dcache_all(); |
paul@33 | 218 | flush_icache_all(); |
paul@33 | 219 | } |