1 /* $NetBSD: ecc_plb.c,v 1.1 2002/08/23 15:01:08 scw Exp $ */ 2 3 /* 4 * Copyright 2001 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Eduardo Horvath and Simon Burge for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include "locators.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/device.h> 43 #include <sys/properties.h> 44 45 #include <machine/dcr.h> 46 #include <machine/cpu.h> 47 #include <powerpc/ibm4xx/dev/plbvar.h> 48 49 50 struct ecc_plb_softc { 51 struct device sc_dev; 52 u_quad_t sc_ecc_tb; 53 u_quad_t sc_ecc_iv; /* Interval */ 54 u_int32_t sc_ecc_cnt; 55 u_int sc_memsize; 56 int sc_irq; 57 }; 58 59 static int ecc_plbmatch(struct device *, struct cfdata *, void *); 60 static void ecc_plbattach(struct device *, struct device *, void *); 61 static void ecc_plb_deferred(struct device *); 62 static int ecc_plb_intr(void *); 63 64 struct cfattach ecc_plb_ca = { 65 sizeof(struct ecc_plb_softc), ecc_plbmatch, ecc_plbattach 66 }; 67 68 static int ecc_plb_found; 69 70 static int 71 ecc_plbmatch(struct device *parent, struct cfdata *cf, void *aux) 72 { 73 struct plb_attach_args *paa = aux; 74 75 if (strcmp(paa->plb_name, cf->cf_driver->cd_name) != 0) 76 return (0); 77 78 if (cf->cf_loc[PLBCF_IRQ] == PLBCF_IRQ_DEFAULT) 79 panic("ecc_plbmatch: wildcard IRQ not allowed\n"); 80 81 paa->plb_irq = cf->cf_loc[PLBCF_IRQ]; 82 83 return (!ecc_plb_found); 84 } 85 86 static void 87 ecc_plbattach(struct device *parent, struct device *self, void *aux) 88 { 89 struct ecc_plb_softc *sc = (struct ecc_plb_softc *)self; 90 struct plb_attach_args *paa = aux; 91 unsigned int processor_freq; 92 unsigned int memsiz; 93 94 ecc_plb_found++; 95 96 if (board_info_get("processor-frequency", 97 &processor_freq, sizeof(processor_freq)) == -1) 98 panic("no processor-frequency"); 99 100 if (board_info_get("mem-size", &memsiz, sizeof(memsiz)) == -1) 101 panic("no mem-size"); 102 103 printf(": ECC controller\n"); 104 105 sc->sc_ecc_tb = 0; 106 sc->sc_ecc_cnt = 0; 107 sc->sc_ecc_iv = processor_freq; /* Set interval */ 108 sc->sc_memsize = memsiz; 109 sc->sc_irq = paa->plb_irq; 110 111 /* 112 * Defer hooking the interrupt until all PLB devices have attached 113 * since the interrupt controller may well be one of those devices... 114 */ 115 config_defer(self, ecc_plb_deferred); 116 } 117 118 static void 119 ecc_plb_deferred(struct device *self) 120 { 121 struct ecc_plb_softc *sc = (struct ecc_plb_softc *)self; 122 123 intr_establish(sc->sc_irq, IST_LEVEL, IPL_SERIAL, ecc_plb_intr, NULL); 124 } 125 126 /* 127 * ECC fault handler. 128 */ 129 static int 130 ecc_plb_intr(void *arg) 131 { 132 struct ecc_plb_softc *sc = arg; 133 u_int32_t esr, ear; 134 int ce, ue; 135 u_quad_t tb; 136 u_long tmp, msr, dat; 137 138 /* This code needs to be improved to handle double-bit errors */ 139 /* in some intelligent fashion. */ 140 141 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 142 esr = mfdcr(DCR_SDRAM0_CFGDATA); 143 144 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_BEAR); 145 ear = mfdcr(DCR_SDRAM0_CFGDATA); 146 147 /* Always clear the error to stop the intr ASAP. */ 148 149 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 150 mtdcr(DCR_SDRAM0_CFGDATA, 0xffffffff); 151 152 if (esr == 0x00) { 153 /* No current error. Could happen due to intr. nesting */ 154 return(1); 155 } 156 157 /* 158 * Only report errors every once per second max. Do this using the TB, 159 * because the system time (via microtime) may be adjusted when the 160 * date is set and can't reliably be used to measure intervals. 161 */ 162 163 asm ("1: mftbu %0; mftb %0+1; mftbu %1; cmpw %0,%1; bne 1b" 164 : "=r"(tb), "=r"(tmp)); 165 sc->sc_ecc_cnt++; 166 167 if ((tb - sc->sc_ecc_tb) < sc->sc_ecc_iv) 168 return(1); 169 170 ce = (esr & SDRAM0_ECCESR_CE) != 0x00; 171 ue = (esr & SDRAM0_ECCESR_UE) != 0x00; 172 173 printf("ECC: Error CNT=%d ESR=%x EAR=%x %s BKNE=%d%d%d%d " 174 "BLCE=%d%d%d%d CBE=%d%d.\n", 175 sc->sc_ecc_cnt, esr, ear, 176 (ue) ? "Uncorrectable" : "Correctable", 177 ((esr & SDRAM0_ECCESR_BKEN(0)) != 0x00), 178 ((esr & SDRAM0_ECCESR_BKEN(1)) != 0x00), 179 ((esr & SDRAM0_ECCESR_BKEN(2)) != 0x00), 180 ((esr & SDRAM0_ECCESR_BKEN(3)) != 0x00), 181 ((esr & SDRAM0_ECCESR_BLCEN(0)) != 0x00), 182 ((esr & SDRAM0_ECCESR_BLCEN(1)) != 0x00), 183 ((esr & SDRAM0_ECCESR_BLCEN(2)) != 0x00), 184 ((esr & SDRAM0_ECCESR_BLCEN(3)) != 0x00), 185 ((esr & SDRAM0_ECCESR_CBEN(0)) != 0x00), 186 ((esr & SDRAM0_ECCESR_CBEN(1)) != 0x00)); 187 188 /* Should check for uncorrectable errors and panic... */ 189 190 if (sc->sc_ecc_cnt > 1000) { 191 printf("ECC: Too many errors, recycling entire " 192 "SDRAM (size = %d).\n", sc->sc_memsize); 193 194 /* 195 * Can this code be changed to run without disabling data MMU 196 * and disabling intrs? 197 * Does kernel always map all of physical RAM VA=PA? If so, 198 * just loop over lowmem. 199 */ 200 asm volatile( 201 "mfmsr %0;" 202 "li %1, 0x00;" 203 "ori %1, %1, 0x8010;" 204 "andc %1, %0, %1;" 205 "mtmsr %1;" 206 "sync;isync;" 207 "li %1, 0x00;" 208 "1:" 209 "dcbt 0, %1;" 210 "sync;isync;" 211 "lwz %2, 0(%1);" 212 "stw %2, 0(%1);" 213 "sync;isync;" 214 "dcbf 0, %1;" 215 "sync;isync;" 216 "addi %1, %1, 0x20;" 217 "addic. %3, %3, -0x20;" 218 "bge 1b;" 219 "mtmsr %0;" 220 "sync;isync;" 221 : "=&r" (msr), "=&r" (tmp), "=&r" (dat) 222 : "r" (sc->sc_memsize) : "0" ); 223 224 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 225 esr = mfdcr(DCR_SDRAM0_CFGDATA); 226 227 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 228 mtdcr(DCR_SDRAM0_CFGDATA, 0xffffffff); 229 230 /* 231 * Correctable errors here are OK, mem should be clean now. 232 * 233 * Should check for uncorrectable errors and panic... 234 */ 235 printf("ECC: Recycling complete, ESR=%x. " 236 "Checking for persistent errors.\n", esr); 237 238 asm volatile( 239 "mfmsr %0;" 240 "li %1, 0x00;" 241 "ori %1, %1, 0x8010;" 242 "andc %1, %0, %1;" 243 "mtmsr %1;" 244 "sync;isync;" 245 "li %1, 0x00;" 246 "1:" 247 "dcbt 0, %1;" 248 "sync;isync;" 249 "lwz %2, 0(%1);" 250 "stw %2, 0(%1);" 251 "sync;isync;" 252 "dcbf 0, %1;" 253 "sync;isync;" 254 "addi %1, %1, 0x20;" 255 "addic. %3, %3, -0x20;" 256 "bge 1b;" 257 "mtmsr %0;" 258 "sync;isync;" 259 : "=&r" (msr), "=&r" (tmp), "=&r" (dat) 260 : "r" (sc->sc_memsize) : "0" ); 261 262 mtdcr(DCR_SDRAM0_CFGADDR, DCR_SDRAM0_ECCESR); 263 esr = mfdcr(DCR_SDRAM0_CFGDATA); 264 265 /* 266 * If esr is non zero here, we're screwed. 267 * Should check this and panic. 268 */ 269 printf("ECC: Persistent error check complete, " 270 "final ESR=%x.\n", esr); 271 } 272 273 sc->sc_ecc_tb = tb; 274 sc->sc_ecc_cnt = 0; 275 276 return(1); 277 } 278