uboot/arch/powerpc/cpu/ppc4xx/cmd_ecctest.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2010
   3 * Stefan Roese, DENX Software Engineering, sr@denx.de.
   4 *
   5 * See file CREDITS for list of people who contributed to this
   6 * project.
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 of
  11 * the License, or (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  21 * MA 02111-1307 USA
  22 *
  23 */
  24
  25#include <common.h>
  26#include <asm/ppc4xx.h>
  27#include <asm/processor.h>
  28#include <asm/io.h>
  29#include <asm/cache.h>
  30
  31#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) || \
  32    defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2)
  33#if defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC)
  34
  35#if defined(CONFIG_405EX)
  36/*
  37 * Currently only 405EX uses 16bit data bus width as an alternative
  38 * option to 32bit data width (SDRAM0_MCOPT1_WDTH)
  39 */
  40#define SDRAM_DATA_ALT_WIDTH    2
  41#else
  42#define SDRAM_DATA_ALT_WIDTH    8
  43#endif
  44
  45#if defined(CONFIG_SYS_OCM_BASE)
  46#define CONFIG_FUNC_ISRAM_ADDR  CONFIG_SYS_OCM_BASE
  47#endif
  48
  49#if defined(CONFIG_SYS_ISRAM_BASE)
  50#define CONFIG_FUNC_ISRAM_ADDR  CONFIG_SYS_ISRAM_BASE
  51#endif
  52
  53#if !defined(CONFIG_FUNC_ISRAM_ADDR)
  54#error "No internal SRAM/OCM provided!"
  55#endif
  56
  57#define force_inline inline __attribute__ ((always_inline))
  58
  59static inline void machine_check_disable(void)
  60{
  61        mtmsr(mfmsr() & ~MSR_ME);
  62}
  63
  64static inline void machine_check_enable(void)
  65{
  66        mtmsr(mfmsr() | MSR_ME);
  67}
  68
  69/*
  70 * These helper functions need to be inlined, since they
  71 * are called from the functions running from internal SRAM.
  72 * SDRAM operation is forbidden at that time, so calling
  73 * functions in SDRAM has to be avoided.
  74 */
  75static force_inline void wait_ddr_idle(void)
  76{
  77        u32 val;
  78
  79        do {
  80                mfsdram(SDRAM_MCSTAT, val);
  81        } while ((val & SDRAM_MCSTAT_IDLE_MASK) == SDRAM_MCSTAT_IDLE_NOT);
  82}
  83
  84static force_inline void recalibrate_ddr(void)
  85{
  86        u32 val;
  87
  88        /*
  89         * Rewrite RQDC & RFDC to calibrate again. If this is not
  90         * done, the SDRAM controller is working correctly after
  91         * changing the MCOPT1_MCHK bits.
  92         */
  93        mfsdram(SDRAM_RQDC, val);
  94        mtsdram(SDRAM_RQDC, val);
  95        mfsdram(SDRAM_RFDC, val);
  96        mtsdram(SDRAM_RFDC, val);
  97}
  98
  99static force_inline void set_mcopt1_mchk(u32 bits)
 100{
 101        u32 val;
 102
 103        wait_ddr_idle();
 104        mfsdram(SDRAM_MCOPT1, val);
 105        mtsdram(SDRAM_MCOPT1, (val & ~SDRAM_MCOPT1_MCHK_MASK) | bits);
 106        recalibrate_ddr();
 107}
 108
 109/*
 110 * The next 2 functions are copied to internal SRAM/OCM and run
 111 * there. No function calls allowed here. No SDRAM acitivity should
 112 * be done here.
 113 */
 114static void inject_ecc_error(void *ptr, int par)
 115{
 116        u32 val;
 117
 118        /*
 119         * Taken from PPC460EX/EXr/GT users manual (Rev 1.21)
 120         * 22.2.17.13 ECC Diagnostics
 121         *
 122         * Items 1 ... 5 are already done by now, running from RAM
 123         * with ECC enabled
 124         */
 125
 126        out_be32(ptr, 0x00000000);
 127        val = in_be32(ptr);
 128
 129        /* 6. Set memory controller to no error checking */
 130        set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_NON);
 131
 132        /* 7. Modify one or two bits for error simulation */
 133        if (par == 1)
 134                out_be32(ptr, in_be32(ptr) ^ 0x00000001);
 135        else
 136                out_be32(ptr, in_be32(ptr) ^ 0x00000003);
 137
 138        /* 8. Wait for SDRAM idle */
 139        val = in_be32(ptr);
 140        set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_CHK_REP);
 141
 142        /* Wait for SDRAM idle */
 143        wait_ddr_idle();
 144
 145        /* Continue with 9. in calling function... */
 146}
 147
 148static void rewrite_ecc_parity(void *ptr, int par)
 149{
 150        u32 current_address = (u32)ptr;
 151        u32 end_address;
 152        u32 address_increment;
 153        u32 mcopt1;
 154        u32 val;
 155
 156        /*
 157         * Fill ECC parity byte again. Otherwise further accesses to
 158         * the failure address will result in exceptions.
 159         */
 160
 161        /* Wait for SDRAM idle */
 162        val = in_be32(0x00000000);
 163        set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_GEN);
 164
 165        /* ECC bit set method for non-cached memory */
 166        mfsdram(SDRAM_MCOPT1, mcopt1);
 167        if ((mcopt1 & SDRAM_MCOPT1_DMWD_MASK) == SDRAM_MCOPT1_DMWD_32)
 168                address_increment = 4;
 169        else
 170                address_increment = SDRAM_DATA_ALT_WIDTH;
 171        end_address = current_address + CONFIG_SYS_CACHELINE_SIZE;
 172
 173        while (current_address < end_address) {
 174                *((unsigned long *)current_address) = 0;
 175                current_address += address_increment;
 176        }
 177
 178        set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_CHK_REP);
 179
 180        /* Wait for SDRAM idle */
 181        wait_ddr_idle();
 182}
 183
 184static int do_ecctest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 185{
 186        u32 old_val;
 187        u32 val;
 188        u32 *ptr;
 189        void (*sram_func)(u32 *, int);
 190        int error;
 191
 192        if (argc < 3) {
 193                return cmd_usage(cmdtp);
 194        }
 195
 196        ptr = (u32 *)simple_strtoul(argv[1], NULL, 16);
 197        error = simple_strtoul(argv[2], NULL, 16);
 198        if ((error < 1) || (error > 2)) {
 199                return cmd_usage(cmdtp);
 200        }
 201
 202        printf("Using address %p for %d bit ECC error injection\n",
 203               ptr, error);
 204
 205        /*
 206         * Save value to restore it later on
 207         */
 208        old_val = in_be32(ptr);
 209
 210        /*
 211         * Copy ECC injection function into internal SRAM/OCM
 212         */
 213        sram_func = (void *)CONFIG_FUNC_ISRAM_ADDR;
 214        memcpy((void *)CONFIG_FUNC_ISRAM_ADDR, inject_ecc_error, 0x10000);
 215
 216        /*
 217         * Disable interrupts and exceptions before calling this
 218         * function in internal SRAM/OCM
 219         */
 220        disable_interrupts();
 221        machine_check_disable();
 222        eieio();
 223
 224        /*
 225         * Jump to ECC simulation function in internal SRAM/OCM
 226         */
 227        (*sram_func)(ptr, error);
 228
 229        /* 10. Read the corresponding address */
 230        val = in_be32(ptr);
 231
 232        /*
 233         * Read and print ECC status register/info:
 234         * The faulting address is only known upon uncorrectable ECC
 235         * errors.
 236         */
 237        mfsdram(SDRAM_ECCES, val);
 238        if (val & SDRAM_ECCES_CE)
 239                printf("ECC: Correctable error\n");
 240        if (val & SDRAM_ECCES_UE) {
 241                printf("ECC: Uncorrectable error at 0x%02x%08x\n",
 242                       mfdcr(SDRAM_ERRADDULL), mfdcr(SDRAM_ERRADDLLL));
 243        }
 244
 245        /*
 246         * Clear pending interrupts/exceptions
 247         */
 248        mtsdram(SDRAM_ECCES, 0xffffffff);
 249        mtdcr(SDRAM_ERRSTATLL, 0xff000000);
 250        set_mcsr(get_mcsr());
 251
 252        /* Now enable interrupts and exceptions again */
 253        eieio();
 254        machine_check_enable();
 255        enable_interrupts();
 256
 257        /*
 258         * The ECC parity byte need to be re-written for the
 259         * corresponding address. Otherwise future accesses to it
 260         * will result in exceptions.
 261         *
 262         * Jump to ECC parity generation function
 263         */
 264        memcpy((void *)CONFIG_FUNC_ISRAM_ADDR, rewrite_ecc_parity, 0x10000);
 265        (*sram_func)(ptr, 0);
 266
 267        /*
 268         * Restore value in corresponding address
 269         */
 270        out_be32(ptr, old_val);
 271
 272        return 0;
 273}
 274
 275U_BOOT_CMD(
 276        ecctest,        3,      0,      do_ecctest,
 277        "Test ECC by single and double error bit injection",
 278        "address 1/2"
 279);
 280
 281#endif /* defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) */
 282#endif /* defined(CONFIG_SDRAM_PPC4xx_IBM_DDR)... */
 283