linux-old/arch/mips/mm/pg-r4k.c
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (C) 2003, 2004 Ralf Baechle (ralf@linux-mips.org)
   7 */
   8#include <linux/config.h>
   9#include <linux/init.h>
  10#include <linux/kernel.h>
  11#include <linux/sched.h>
  12#include <linux/mm.h>
  13#include <linux/module.h>
  14#include <linux/proc_fs.h>
  15
  16#include <asm/cacheops.h>
  17#include <asm/inst.h>
  18#include <asm/io.h>
  19#include <asm/page.h>
  20#include <asm/pgtable.h>
  21#include <asm/prefetch.h>
  22#include <asm/system.h>
  23#include <asm/bootinfo.h>
  24#include <asm/mipsregs.h>
  25#include <asm/mmu_context.h>
  26#include <asm/cpu.h>
  27#include <asm/war.h>
  28
  29#define half_scache_line_size()         (cpu_scache_line_size() >> 1)
  30
  31/*
  32 * Maximum sizes:
  33 *
  34 * R4000 128 bytes S-cache:             0x58 bytes
  35 * R4600 v1.7:                          0x5c bytes
  36 * R4600 v2.0:                          0x60 bytes
  37 * With prefetching, 16 byte strides    0xa0 bytes
  38 */
  39
  40static unsigned int clear_page_array[0x130 / 4];
  41
  42void clear_page(void * page) __attribute__((alias("clear_page_array")));
  43
  44/*
  45 * Maximum sizes:
  46 *
  47 * R4000 128 bytes S-cache:             0x11c bytes
  48 * R4600 v1.7:                          0x080 bytes
  49 * R4600 v2.0:                          0x07c bytes
  50 * With prefetching, 16 byte strides    0x0b8 bytes
  51 */
  52static unsigned int copy_page_array[0x148 / 4];
  53
  54void copy_page(void *to, void *from) __attribute__((alias("copy_page_array")));
  55
  56/*
  57 * An address fits into a single register so it's safe to use 64-bit registers
  58 * if we have 64-bit adresses.
  59 */
  60#define cpu_has_64bit_registers cpu_has_64bit_addresses
  61
  62/*
  63 * This is suboptimal for 32-bit kernels; we assume that R10000 is only used
  64 * with 64-bit kernels.  The prefetch offsets have been experimentally tuned
  65 * an Origin 200.
  66 */
  67static int pref_offset_clear __initdata = 512;
  68static int pref_offset_copy  __initdata = 256;
  69
  70static unsigned int pref_src_mode __initdata;
  71static unsigned int pref_dst_mode __initdata;
  72
  73static int load_offset __initdata;
  74static int store_offset __initdata;
  75
  76static unsigned int __initdata *dest, *epc;
  77
  78static unsigned int instruction_pending;
  79static union mips_instruction delayed_mi;
  80
  81static void __init emit_instruction(union mips_instruction mi)
  82{
  83        if (instruction_pending)
  84                *epc++ = delayed_mi.word;
  85
  86        instruction_pending = 1;
  87        delayed_mi = mi;
  88}
  89
  90static inline void flush_delay_slot_or_nop(void)
  91{
  92        if (instruction_pending) {
  93                *epc++ = delayed_mi.word;
  94                instruction_pending = 0;
  95                return;
  96        }
  97
  98        *epc++ = 0;
  99}
 100
 101static inline unsigned int *label(void)
 102{
 103        if (instruction_pending) {
 104                *epc++ = delayed_mi.word;
 105                instruction_pending = 0;
 106        }
 107
 108        return epc;
 109}
 110
 111static inline void build_insn_word(unsigned int word)
 112{
 113        union mips_instruction mi;
 114
 115        mi.word          = word;
 116
 117        emit_instruction(mi);
 118}
 119
 120static inline void build_nop(void)
 121{
 122        build_insn_word(0);                     /* nop */
 123}
 124
 125static inline void build_src_pref(int advance)
 126{
 127        if (!(load_offset & (cpu_dcache_line_size() - 1))) {
 128                union mips_instruction mi;
 129
 130                mi.i_format.opcode     = pref_op;
 131                mi.i_format.rs         = 5;             /* $a1 */
 132                mi.i_format.rt         = pref_src_mode;
 133                mi.i_format.simmediate = load_offset + advance;
 134
 135                emit_instruction(mi);
 136        }
 137}
 138
 139static inline void __build_load_reg(int reg)
 140{
 141        union mips_instruction mi;
 142        unsigned int width;
 143
 144        if (cpu_has_64bit_registers) {
 145                mi.i_format.opcode     = ld_op;
 146                width = 8;
 147        } else {
 148                mi.i_format.opcode     = lw_op;
 149                width = 4;
 150        }
 151        mi.i_format.rs         = 5;             /* $a1 */
 152        mi.i_format.rt         = reg;           /* $reg */
 153        mi.i_format.simmediate = load_offset;
 154
 155        load_offset += width;
 156        emit_instruction(mi);
 157}
 158
 159static inline void build_load_reg(int reg)
 160{
 161        if (cpu_has_prefetch)
 162                build_src_pref(pref_offset_copy);
 163
 164        __build_load_reg(reg);
 165}
 166
 167static inline void build_dst_pref(int advance)
 168{
 169        if (!(store_offset & (cpu_dcache_line_size() - 1))) {
 170                union mips_instruction mi;
 171
 172                mi.i_format.opcode     = pref_op;
 173                mi.i_format.rs         = 4;             /* $a0 */
 174                mi.i_format.rt         = pref_dst_mode;
 175                mi.i_format.simmediate = store_offset + advance;
 176
 177                emit_instruction(mi);
 178        }
 179}
 180
 181static inline void build_cdex_s(void)
 182{
 183        union mips_instruction mi;
 184
 185        if ((store_offset & (cpu_scache_line_size() - 1)))
 186                return;
 187
 188        mi.c_format.opcode     = cache_op;
 189        mi.c_format.rs         = 4;             /* $a0 */
 190        mi.c_format.c_op       = 3;             /* Create Dirty Exclusive */
 191        mi.c_format.cache      = 3;             /* Secondary Data Cache */
 192        mi.c_format.simmediate = store_offset;
 193
 194        emit_instruction(mi);
 195}
 196
 197static inline void build_cdex_p(void)
 198{
 199        union mips_instruction mi;
 200
 201        if (store_offset & (cpu_dcache_line_size() - 1))
 202                return;
 203
 204        if (R4600_V1_HIT_CACHEOP_WAR && ((read_c0_prid() & 0xfff0) == 0x2010)) {
 205                build_nop();
 206                build_nop();
 207                build_nop();
 208                build_nop();
 209        }
 210
 211        if (R4600_V2_HIT_CACHEOP_WAR && ((read_c0_prid() & 0xfff0) == 0x2020))
 212                build_insn_word(0x8c200000);    /* lw      $zero, ($at) */
 213
 214        mi.c_format.opcode     = cache_op;
 215        mi.c_format.rs         = 4;             /* $a0 */
 216        mi.c_format.c_op       = 3;             /* Create Dirty Exclusive */
 217        mi.c_format.cache      = 1;             /* Data Cache */
 218        mi.c_format.simmediate = store_offset;
 219
 220        emit_instruction(mi);
 221}
 222
 223static void __build_store_reg(int reg)
 224{
 225        union mips_instruction mi;
 226        unsigned int width;
 227
 228        if (cpu_has_64bit_gp_regs ||
 229            (cpu_has_64bit_zero_reg && reg == 0)) {
 230                mi.i_format.opcode     = sd_op;
 231                width = 8;
 232        } else {
 233                mi.i_format.opcode     = sw_op;
 234                width = 4;
 235        }
 236        mi.i_format.rs         = 4;             /* $a0 */
 237        mi.i_format.rt         = reg;           /* $reg */
 238        mi.i_format.simmediate = store_offset;
 239
 240        store_offset += width;
 241        emit_instruction(mi);
 242}
 243
 244static inline void build_store_reg(int reg)
 245{
 246        if (cpu_has_prefetch)
 247                if (reg)
 248                        build_dst_pref(pref_offset_copy);
 249                else
 250                        build_dst_pref(pref_offset_clear);
 251        else if (cpu_has_cache_cdex_s)
 252                build_cdex_s();
 253        else if (cpu_has_cache_cdex_p)
 254                build_cdex_p();
 255
 256        __build_store_reg(reg);
 257}
 258
 259static inline void build_addiu_a2_a0(unsigned long offset)
 260{
 261        union mips_instruction mi;
 262
 263        BUG_ON(offset > 0x7fff);
 264
 265        mi.i_format.opcode     = cpu_has_64bit_addresses ? daddiu_op : addiu_op;
 266        mi.i_format.rs         = 4;             /* $a0 */
 267        mi.i_format.rt         = 6;             /* $a2 */
 268        mi.i_format.simmediate = offset;
 269
 270        emit_instruction(mi);
 271}
 272
 273static inline void build_addiu_a1(unsigned long offset)
 274{
 275        union mips_instruction mi;
 276
 277        BUG_ON(offset > 0x7fff);
 278
 279        mi.i_format.opcode     = cpu_has_64bit_addresses ? daddiu_op : addiu_op;
 280        mi.i_format.rs         = 5;             /* $a1 */
 281        mi.i_format.rt         = 5;             /* $a1 */
 282        mi.i_format.simmediate = offset;
 283
 284        load_offset -= offset;
 285
 286        emit_instruction(mi);
 287}
 288
 289static inline void build_addiu_a0(unsigned long offset)
 290{
 291        union mips_instruction mi;
 292
 293        BUG_ON(offset > 0x7fff);
 294
 295        mi.i_format.opcode     = cpu_has_64bit_addresses ? daddiu_op : addiu_op;
 296        mi.i_format.rs         = 4;             /* $a0 */
 297        mi.i_format.rt         = 4;             /* $a0 */
 298        mi.i_format.simmediate = offset;
 299
 300        store_offset -= offset;
 301
 302        emit_instruction(mi);
 303}
 304
 305static inline void build_bne(unsigned int *dest)
 306{
 307        union mips_instruction mi;
 308
 309        mi.i_format.opcode = bne_op;
 310        mi.i_format.rs     = 6;                 /* $a2 */
 311        mi.i_format.rt     = 4;                 /* $a0 */
 312        mi.i_format.simmediate = dest - epc - 1;
 313
 314        *epc++ = mi.word;
 315        flush_delay_slot_or_nop();
 316}
 317
 318static inline void build_jr_ra(void)
 319{
 320        union mips_instruction mi;
 321
 322        mi.r_format.opcode = spec_op;
 323        mi.r_format.rs     = 31;
 324        mi.r_format.rt     = 0;
 325        mi.r_format.rd     = 0;
 326        mi.r_format.re     = 0;
 327        mi.r_format.func   = jr_op;
 328
 329        *epc++ = mi.word;
 330        flush_delay_slot_or_nop();
 331}
 332
 333void __init build_clear_page(void)
 334{
 335        unsigned int loop_start;
 336
 337        epc = (unsigned int *) &clear_page_array;
 338        instruction_pending = 0;
 339        store_offset = 0;
 340
 341        if (cpu_has_prefetch) {
 342                switch (current_cpu_data.cputype) {
 343                case CPU_RM9000:
 344                        /*
 345                         * As a workaround for erratum G105 which make the
 346                         * PrepareForStore hint unusable we fall back to
 347                         * StoreRetained on the RM9000.  Once it is known which
 348                         * versions of the RM9000 we'll be able to condition-
 349                         * alize this.
 350                         */
 351
 352                case CPU_R10000:
 353                case CPU_R12000:
 354                        pref_src_mode = Pref_LoadStreamed;
 355                        pref_dst_mode = Pref_StoreRetained;
 356                        break;
 357
 358                default:
 359                        pref_src_mode = Pref_LoadStreamed;
 360                        pref_dst_mode = Pref_PrepareForStore;
 361                        break;
 362                }
 363        }
 364
 365        build_addiu_a2_a0(PAGE_SIZE - (cpu_has_prefetch ? pref_offset_clear : 0));
 366
 367        if (R4600_V2_HIT_CACHEOP_WAR && ((read_c0_prid() & 0xfff0) == 0x2020))
 368                build_insn_word(0x3c01a000);    /* lui     $at, 0xa000  */
 369
 370dest = label();
 371        do {
 372                build_store_reg(0);
 373                build_store_reg(0);
 374                build_store_reg(0);
 375                build_store_reg(0);
 376        } while (store_offset < half_scache_line_size());
 377        build_addiu_a0(2 * store_offset);
 378        loop_start = store_offset;
 379        do {
 380                build_store_reg(0);
 381                build_store_reg(0);
 382                build_store_reg(0);
 383                build_store_reg(0);
 384        } while ((store_offset - loop_start) < half_scache_line_size());
 385        build_bne(dest);
 386
 387        if (cpu_has_prefetch && pref_offset_clear) {
 388                build_addiu_a2_a0(pref_offset_clear);
 389        dest = label();
 390                loop_start = store_offset;
 391                do {
 392                        __build_store_reg(0);
 393                        __build_store_reg(0);
 394                        __build_store_reg(0);
 395                        __build_store_reg(0);
 396                } while ((store_offset - loop_start) < half_scache_line_size());
 397                build_addiu_a0(2 * store_offset);
 398                loop_start = store_offset;
 399                do {
 400                        __build_store_reg(0);
 401                        __build_store_reg(0);
 402                        __build_store_reg(0);
 403                        __build_store_reg(0);
 404                } while ((store_offset - loop_start) < half_scache_line_size());
 405                build_bne(dest);
 406        }
 407
 408        build_jr_ra();
 409
 410        flush_icache_range((unsigned long)&clear_page_array,
 411                           (unsigned long) epc);
 412
 413        BUG_ON(epc > clear_page_array + ARRAY_SIZE(clear_page_array));
 414}
 415
 416void __init build_copy_page(void)
 417{
 418        unsigned int loop_start;
 419
 420        epc = (unsigned int *) &copy_page_array;
 421        store_offset = load_offset = 0;
 422        instruction_pending = 0;
 423
 424        build_addiu_a2_a0(PAGE_SIZE - (cpu_has_prefetch ? pref_offset_copy : 0));
 425
 426        if (R4600_V2_HIT_CACHEOP_WAR && ((read_c0_prid() & 0xfff0) == 0x2020))
 427                build_insn_word(0x3c01a000);    /* lui     $at, 0xa000  */
 428
 429dest = label();
 430        loop_start = store_offset;
 431        do {
 432                build_load_reg( 8);
 433                build_load_reg( 9);
 434                build_load_reg(10);
 435                build_load_reg(11);
 436                build_store_reg( 8);
 437                build_store_reg( 9);
 438                build_store_reg(10);
 439                build_store_reg(11);
 440        } while ((store_offset - loop_start) < half_scache_line_size());
 441        build_addiu_a0(2 * store_offset);
 442        build_addiu_a1(2 * load_offset);
 443        loop_start = store_offset;
 444        do {
 445                build_load_reg( 8);
 446                build_load_reg( 9);
 447                build_load_reg(10);
 448                build_load_reg(11);
 449                build_store_reg( 8);
 450                build_store_reg( 9);
 451                build_store_reg(10);
 452                build_store_reg(11);
 453        } while ((store_offset - loop_start) < half_scache_line_size());
 454        build_bne(dest);
 455
 456        if (cpu_has_prefetch && pref_offset_copy) {
 457                build_addiu_a2_a0(pref_offset_copy);
 458        dest = label();
 459                loop_start = store_offset;
 460                do {
 461                        __build_load_reg( 8);
 462                        __build_load_reg( 9);
 463                        __build_load_reg(10);
 464                        __build_load_reg(11);
 465                        __build_store_reg( 8);
 466                        __build_store_reg( 9);
 467                        __build_store_reg(10);
 468                        __build_store_reg(11);
 469                } while ((store_offset - loop_start) < half_scache_line_size());
 470                build_addiu_a0(2 * store_offset);
 471                build_addiu_a1(2 * load_offset);
 472                loop_start = store_offset;
 473                do {
 474                        __build_load_reg( 8);
 475                        __build_load_reg( 9);
 476                        __build_load_reg(10);
 477                        __build_load_reg(11);
 478                        __build_store_reg( 8);
 479                        __build_store_reg( 9);
 480                        __build_store_reg(10);
 481                        __build_store_reg(11);
 482                } while ((store_offset - loop_start) < half_scache_line_size());
 483                build_bne(dest);
 484        }
 485
 486        build_jr_ra();
 487
 488        flush_icache_range((unsigned long)&copy_page_array,
 489                           (unsigned long) epc);
 490
 491        BUG_ON(epc > copy_page_array + ARRAY_SIZE(copy_page_array));
 492}
 493
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.