1*5f819ca3Schs /* $NetBSD: memc.c,v 1.11 2012/10/27 17:18:27 chs Exp $ */
208bde987Sscw
308bde987Sscw /*-
408bde987Sscw * Copyright (c) 2000, 2002 The NetBSD Foundation, Inc.
508bde987Sscw * All rights reserved.
608bde987Sscw *
708bde987Sscw * This code is derived from software contributed to The NetBSD Foundation
808bde987Sscw * by Steve C. Woodford.
908bde987Sscw *
1008bde987Sscw * Redistribution and use in source and binary forms, with or without
1108bde987Sscw * modification, are permitted provided that the following conditions
1208bde987Sscw * are met:
1308bde987Sscw * 1. Redistributions of source code must retain the above copyright
1408bde987Sscw * notice, this list of conditions and the following disclaimer.
1508bde987Sscw * 2. Redistributions in binary form must reproduce the above copyright
1608bde987Sscw * notice, this list of conditions and the following disclaimer in the
1708bde987Sscw * documentation and/or other materials provided with the distribution.
1808bde987Sscw *
1908bde987Sscw * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2008bde987Sscw * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2108bde987Sscw * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2208bde987Sscw * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2308bde987Sscw * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2408bde987Sscw * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2508bde987Sscw * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2608bde987Sscw * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2708bde987Sscw * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2808bde987Sscw * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2908bde987Sscw * POSSIBILITY OF SUCH DAMAGE.
3008bde987Sscw */
3108bde987Sscw
3208bde987Sscw /*
3308bde987Sscw * Support for the MEMECC and MEMC40 memory controllers on MVME68K
3408bde987Sscw * and MVME88K boards.
3508bde987Sscw */
3608bde987Sscw
37365cbd94Slukem #include <sys/cdefs.h>
38*5f819ca3Schs __KERNEL_RCSID(0, "$NetBSD: memc.c,v 1.11 2012/10/27 17:18:27 chs Exp $");
39365cbd94Slukem
4008bde987Sscw #include <sys/param.h>
4108bde987Sscw #include <sys/kernel.h>
4208bde987Sscw #include <sys/systm.h>
4308bde987Sscw #include <sys/device.h>
4408bde987Sscw #include <sys/malloc.h>
4508bde987Sscw
46a2a38285Sad #include <sys/cpu.h>
47a2a38285Sad #include <sys/bus.h>
4808bde987Sscw
4908bde987Sscw #include <dev/mvme/memcvar.h>
5008bde987Sscw #include <dev/mvme/memcreg.h>
5108bde987Sscw #include <dev/mvme/pcctwovar.h>
5208bde987Sscw #include <dev/mvme/pcctworeg.h>
5308bde987Sscw
5408bde987Sscw #include <dev/vme/vmevar.h>
5508bde987Sscw #include <dev/mvme/mvmebus.h>
5608bde987Sscw #include <dev/mvme/vme_twovar.h>
5708bde987Sscw #include <dev/mvme/vme_tworeg.h>
5808bde987Sscw
5908bde987Sscw
6008bde987Sscw static struct memc_softc *memc_softcs[MEMC_NDEVS];
6108bde987Sscw static int memc_softc_count;
6208bde987Sscw
6308bde987Sscw static void memc040_attach(struct memc_softc *);
6408bde987Sscw static void memecc_attach(struct memc_softc *);
6508bde987Sscw static void memc_hook_error_intr(struct memc_softc *, int (*)(void *));
6608bde987Sscw
6708bde987Sscw static int memecc_err_intr(void *);
6808bde987Sscw static void memecc_log_error(struct memc_softc *, u_int8_t, int, int);
6908bde987Sscw
7008bde987Sscw #define MEMECC_SCRUBBER_PERIOD 86400 /* ~24 hours */
7108bde987Sscw
7208bde987Sscw /*
7308bde987Sscw * The following stuff is used to decode the ECC syndrome code so
7408bde987Sscw * that we can figure out exactly which address/bit needed to be
7508bde987Sscw * corrected.
7608bde987Sscw */
7708bde987Sscw #define MEMECC_SYN_BIT_MASK 0x0fu
7808bde987Sscw #define MEMECC_SYN_BANK_A (0x00u << 4)
7908bde987Sscw #define MEMECC_SYN_BANK_B (0x01u << 4)
8008bde987Sscw #define MEMECC_SYN_BANK_C (0x02u << 4)
8108bde987Sscw #define MEMECC_SYN_BANK_D (0x03u << 4)
8208bde987Sscw #define MEMECC_SYN_BANK_SHIFT 4
8308bde987Sscw #define MEMECC_SYN_BANK_MASK 0x03u
8408bde987Sscw #define MEMECC_SYN_CHECKBIT_ERR 0x80u
8508bde987Sscw #define MEMECC_SYN_INVALID 0xffu
8608bde987Sscw
8708bde987Sscw static u_int8_t memc_syn_decode[256] = {
8808bde987Sscw MEMECC_SYN_INVALID, /* 0x00 */
8908bde987Sscw MEMECC_SYN_CHECKBIT_ERR | 0, /* 0x01: Checkbit 0 */
9008bde987Sscw MEMECC_SYN_CHECKBIT_ERR | 1, /* 0x02: Checkbit 1 */
9108bde987Sscw MEMECC_SYN_INVALID, /* 0x03 */
9208bde987Sscw MEMECC_SYN_CHECKBIT_ERR | 2, /* 0x04: Checkbit 2 */
9308bde987Sscw MEMECC_SYN_INVALID, /* 0x05 */
9408bde987Sscw MEMECC_SYN_INVALID, /* 0x06 */
9508bde987Sscw MEMECC_SYN_BANK_C | 10, /* 0x07: Bank C 10/26 */
9608bde987Sscw MEMECC_SYN_CHECKBIT_ERR | 3, /* 0x08: Checkbit 3 */
9708bde987Sscw MEMECC_SYN_INVALID, /* 0x09 */
9808bde987Sscw MEMECC_SYN_INVALID, /* 0x0a */
9908bde987Sscw MEMECC_SYN_BANK_C | 13, /* 0x0b: Bank C 13/29 */
10008bde987Sscw MEMECC_SYN_INVALID, /* 0x0c */
10108bde987Sscw MEMECC_SYN_BANK_D | 1, /* 0x0d: Bank D 1/17 */
10208bde987Sscw MEMECC_SYN_BANK_D | 2, /* 0x0e: Bank D 2/18 */
10308bde987Sscw MEMECC_SYN_INVALID, /* 0x0f */
10408bde987Sscw MEMECC_SYN_CHECKBIT_ERR | 4, /* 0x10: Checkbit 4 */
10508bde987Sscw MEMECC_SYN_INVALID, /* 0x11 */
10608bde987Sscw MEMECC_SYN_INVALID, /* 0x12 */
10708bde987Sscw MEMECC_SYN_BANK_C | 14, /* 0x13: Bank C 14/30 */
10808bde987Sscw MEMECC_SYN_INVALID, /* 0x14 */
10908bde987Sscw MEMECC_SYN_BANK_D | 4, /* 0x15: Bank D 4/20 */
11008bde987Sscw MEMECC_SYN_BANK_D | 5, /* 0x16: Bank D 5/21 */
11108bde987Sscw MEMECC_SYN_INVALID, /* 0x17 */
11208bde987Sscw MEMECC_SYN_INVALID, /* 0x18 */
11308bde987Sscw MEMECC_SYN_BANK_D | 8, /* 0x19: Bank D 8/24 */
11408bde987Sscw MEMECC_SYN_BANK_D | 9, /* 0x1a: Bank D 9/25 */
11508bde987Sscw MEMECC_SYN_INVALID, /* 0x1b */
11608bde987Sscw MEMECC_SYN_BANK_D | 10, /* 0x1c: Bank D 10/26 */
11708bde987Sscw MEMECC_SYN_INVALID, /* 0x1d */
11808bde987Sscw MEMECC_SYN_INVALID, /* 0x1e */
11908bde987Sscw MEMECC_SYN_INVALID, /* 0x1f */
12008bde987Sscw MEMECC_SYN_CHECKBIT_ERR | 5, /* 0x20: Checkbit 5 */
12108bde987Sscw MEMECC_SYN_INVALID, /* 0x21 */
12208bde987Sscw MEMECC_SYN_INVALID, /* 0x22 */
12308bde987Sscw MEMECC_SYN_BANK_C | 0, /* 0x23: Bank C 0/16 */
12408bde987Sscw MEMECC_SYN_INVALID, /* 0x24 */
12508bde987Sscw MEMECC_SYN_BANK_D | 7, /* 0x25: Bank D 7/23 */
12608bde987Sscw MEMECC_SYN_BANK_D | 6, /* 0x26: Bank D 6/22 */
12708bde987Sscw MEMECC_SYN_INVALID, /* 0x27 */
12808bde987Sscw MEMECC_SYN_INVALID, /* 0x28 */
12908bde987Sscw MEMECC_SYN_BANK_A | 15, /* 0x29: Bank A 15/31 */
13008bde987Sscw MEMECC_SYN_BANK_D | 12, /* 0x2a: Bank D 12/28 */
13108bde987Sscw MEMECC_SYN_INVALID, /* 0x2b */
13208bde987Sscw MEMECC_SYN_BANK_D | 13, /* 0x2c: Bank D 13/29 */
13308bde987Sscw MEMECC_SYN_INVALID, /* 0x2d */
13408bde987Sscw MEMECC_SYN_INVALID, /* 0x2e */
13508bde987Sscw MEMECC_SYN_INVALID, /* 0x2f */
13608bde987Sscw MEMECC_SYN_INVALID, /* 0x30 */
13708bde987Sscw MEMECC_SYN_BANK_A | 14, /* 0x31: Bank A 14/30 */
13808bde987Sscw MEMECC_SYN_BANK_A | 0, /* 0x32: Bank A 0/16 */
13908bde987Sscw MEMECC_SYN_INVALID, /* 0x33 */
14008bde987Sscw MEMECC_SYN_BANK_A | 1, /* 0x34: Bank A 1/17 */
14108bde987Sscw MEMECC_SYN_INVALID, /* 0x35 */
14208bde987Sscw MEMECC_SYN_INVALID, /* 0x36 */
14308bde987Sscw MEMECC_SYN_INVALID, /* 0x37 */
14408bde987Sscw MEMECC_SYN_BANK_A | 2, /* 0x38: Bank A 2/18 */
14508bde987Sscw MEMECC_SYN_INVALID, /* 0x39 */
14608bde987Sscw MEMECC_SYN_INVALID, /* 0x3a */
14708bde987Sscw MEMECC_SYN_INVALID, /* 0x3b */
14808bde987Sscw MEMECC_SYN_INVALID, /* 0x3c */
14908bde987Sscw MEMECC_SYN_BANK_C | 3, /* 0x3d: Bank C 3/19 */
15008bde987Sscw MEMECC_SYN_INVALID, /* 0x3e */
15108bde987Sscw MEMECC_SYN_INVALID, /* 0x3f */
15208bde987Sscw MEMECC_SYN_CHECKBIT_ERR | 6, /* 0x40: Checkbit 6 */
15308bde987Sscw MEMECC_SYN_INVALID, /* 0x41 */
15408bde987Sscw MEMECC_SYN_INVALID, /* 0x42 */
15508bde987Sscw MEMECC_SYN_BANK_C | 1, /* 0x43: Bank C 1/17 */
15608bde987Sscw MEMECC_SYN_INVALID, /* 0x44 */
15708bde987Sscw MEMECC_SYN_BANK_C | 4, /* 0x45: Bank C 4/20 */
15808bde987Sscw MEMECC_SYN_BANK_C | 8, /* 0x46: Bank C 8/24 */
15908bde987Sscw MEMECC_SYN_INVALID, /* 0x47 */
16008bde987Sscw MEMECC_SYN_INVALID, /* 0x48 */
16108bde987Sscw MEMECC_SYN_BANK_C | 7, /* 0x49: Bank C 7/23 */
16208bde987Sscw MEMECC_SYN_BANK_D | 15, /* 0x4a: Bank D 15/31 */
16308bde987Sscw MEMECC_SYN_INVALID, /* 0x4b */
16408bde987Sscw MEMECC_SYN_BANK_D | 14, /* 0x4c: Bank D 14/30 */
16508bde987Sscw MEMECC_SYN_INVALID, /* 0x4d */
16608bde987Sscw MEMECC_SYN_INVALID, /* 0x4e */
16708bde987Sscw MEMECC_SYN_BANK_B | 3, /* 0x4f: Bank B 3/19 */
16808bde987Sscw MEMECC_SYN_INVALID, /* 0x50 */
16908bde987Sscw MEMECC_SYN_BANK_B | 4, /* 0x51: Bank B 4/20 */
17008bde987Sscw MEMECC_SYN_BANK_B | 7, /* 0x52: Bank B 7/23 */
17108bde987Sscw MEMECC_SYN_INVALID, /* 0x53 */
17208bde987Sscw MEMECC_SYN_BANK_A | 4, /* 0x54: Bank A 4/20 */
17308bde987Sscw MEMECC_SYN_INVALID, /* 0x55 */
17408bde987Sscw MEMECC_SYN_INVALID, /* 0x56 */
17508bde987Sscw MEMECC_SYN_INVALID, /* 0x57 */
17608bde987Sscw MEMECC_SYN_BANK_A | 5, /* 0x58: Bank A 5/21 */
17708bde987Sscw MEMECC_SYN_INVALID, /* 0x59 */
17808bde987Sscw MEMECC_SYN_INVALID, /* 0x5a */
17908bde987Sscw MEMECC_SYN_INVALID, /* 0x5b */
18008bde987Sscw MEMECC_SYN_INVALID, /* 0x5c */
18108bde987Sscw MEMECC_SYN_INVALID, /* 0x5d */
18208bde987Sscw MEMECC_SYN_INVALID, /* 0x5e */
18308bde987Sscw MEMECC_SYN_INVALID, /* 0x5f */
18408bde987Sscw MEMECC_SYN_INVALID, /* 0x60 */
18508bde987Sscw MEMECC_SYN_BANK_B | 5, /* 0x61: Bank B 5/21 */
18608bde987Sscw MEMECC_SYN_BANK_B | 6, /* 0x62: Bank B 6/22 */
18708bde987Sscw MEMECC_SYN_INVALID, /* 0x63 */
18808bde987Sscw MEMECC_SYN_BANK_A | 8, /* 0x64: Bank A 8/24 */
18908bde987Sscw MEMECC_SYN_INVALID, /* 0x65 */
19008bde987Sscw MEMECC_SYN_INVALID, /* 0x66 */
19108bde987Sscw MEMECC_SYN_INVALID, /* 0x67 */
19208bde987Sscw MEMECC_SYN_BANK_A | 9, /* 0x68: Bank A 9/25 */
19308bde987Sscw MEMECC_SYN_INVALID, /* 0x69 */
19408bde987Sscw MEMECC_SYN_INVALID, /* 0x6a */
19508bde987Sscw MEMECC_SYN_INVALID, /* 0x6b */
19608bde987Sscw MEMECC_SYN_INVALID, /* 0x6c */
19708bde987Sscw MEMECC_SYN_INVALID, /* 0x6d */
19808bde987Sscw MEMECC_SYN_INVALID, /* 0x6e */
19908bde987Sscw MEMECC_SYN_INVALID, /* 0x6f */
20008bde987Sscw MEMECC_SYN_BANK_A | 10, /* 0x70: Bank A 10/26 */
20108bde987Sscw MEMECC_SYN_INVALID, /* 0x71 */
20208bde987Sscw MEMECC_SYN_INVALID, /* 0x72 */
20308bde987Sscw MEMECC_SYN_INVALID, /* 0x73 */
20408bde987Sscw MEMECC_SYN_INVALID, /* 0x74 */
20508bde987Sscw MEMECC_SYN_INVALID, /* 0x75 */
20608bde987Sscw MEMECC_SYN_INVALID, /* 0x76 */
20708bde987Sscw MEMECC_SYN_INVALID, /* 0x77 */
20808bde987Sscw MEMECC_SYN_INVALID, /* 0x78 */
20908bde987Sscw MEMECC_SYN_INVALID, /* 0x79 */
21008bde987Sscw MEMECC_SYN_BANK_C | 11, /* 0x7a: Bank C 11/27 */
21108bde987Sscw MEMECC_SYN_INVALID, /* 0x7b */
21208bde987Sscw MEMECC_SYN_INVALID, /* 0x7c */
21308bde987Sscw MEMECC_SYN_INVALID, /* 0x7d */
21408bde987Sscw MEMECC_SYN_INVALID, /* 0x7e */
21508bde987Sscw MEMECC_SYN_INVALID, /* 0x7f */
21608bde987Sscw MEMECC_SYN_CHECKBIT_ERR | 7, /* 0x80: Checkbit 7 */
21708bde987Sscw MEMECC_SYN_INVALID, /* 0x81 */
21808bde987Sscw MEMECC_SYN_INVALID, /* 0x82 */
21908bde987Sscw MEMECC_SYN_BANK_C | 2, /* 0x83: Bank C 2/18 */
22008bde987Sscw MEMECC_SYN_INVALID, /* 0x84 */
22108bde987Sscw MEMECC_SYN_BANK_C | 5, /* 0x85: Bank C 5/21 */
22208bde987Sscw MEMECC_SYN_BANK_C | 9, /* 0x86: Bank C 9/25 */
22308bde987Sscw MEMECC_SYN_INVALID, /* 0x87 */
22408bde987Sscw MEMECC_SYN_INVALID, /* 0x88 */
22508bde987Sscw MEMECC_SYN_BANK_C | 6, /* 0x89: Bank C 6/22 */
22608bde987Sscw MEMECC_SYN_BANK_C | 12, /* 0x8a: Bank C 12/28 */
22708bde987Sscw MEMECC_SYN_INVALID, /* 0x8b */
22808bde987Sscw MEMECC_SYN_BANK_D | 0, /* 0x8c: Bank D 0/16 */
22908bde987Sscw MEMECC_SYN_INVALID, /* 0x8d */
23008bde987Sscw MEMECC_SYN_INVALID, /* 0x8e */
23108bde987Sscw MEMECC_SYN_INVALID, /* 0x8f */
23208bde987Sscw MEMECC_SYN_INVALID, /* 0x90 */
23308bde987Sscw MEMECC_SYN_BANK_B | 8, /* 0x91: Bank B 8/24 */
23408bde987Sscw MEMECC_SYN_BANK_C | 15, /* 0x92: Bank C 15/31 */
23508bde987Sscw MEMECC_SYN_INVALID, /* 0x93 */
23608bde987Sscw MEMECC_SYN_BANK_A | 7, /* 0x94: Bank A 7/23 */
23708bde987Sscw MEMECC_SYN_INVALID, /* 0x95 */
23808bde987Sscw MEMECC_SYN_INVALID, /* 0x96 */
23908bde987Sscw MEMECC_SYN_INVALID, /* 0x97 */
24008bde987Sscw MEMECC_SYN_BANK_A | 6, /* 0x98: Bank A 6/22 */
24108bde987Sscw MEMECC_SYN_INVALID, /* 0x99 */
24208bde987Sscw MEMECC_SYN_INVALID, /* 0x9a */
24308bde987Sscw MEMECC_SYN_INVALID, /* 0x9b */
24408bde987Sscw MEMECC_SYN_INVALID, /* 0x9c */
24508bde987Sscw MEMECC_SYN_INVALID, /* 0x9d */
24608bde987Sscw MEMECC_SYN_BANK_B | 11, /* 0x9e: Bank B 11/27 */
24708bde987Sscw MEMECC_SYN_INVALID, /* 0x9f */
24808bde987Sscw MEMECC_SYN_INVALID, /* 0xa0 */
24908bde987Sscw MEMECC_SYN_BANK_B | 9, /* 0xa1: Bank B 9/25 */
25008bde987Sscw MEMECC_SYN_BANK_B | 12, /* 0xa2: Bank B 12/28 */
25108bde987Sscw MEMECC_SYN_INVALID, /* 0xa3 */
25208bde987Sscw MEMECC_SYN_BANK_B | 15, /* 0xa4: Bank B 15/31 */
25308bde987Sscw MEMECC_SYN_INVALID, /* 0xa5 */
25408bde987Sscw MEMECC_SYN_INVALID, /* 0xa6 */
25508bde987Sscw MEMECC_SYN_BANK_A | 11, /* 0xa7: Bank A 11/27 */
25608bde987Sscw MEMECC_SYN_BANK_A | 12, /* 0xa8: Bank A 12/28 */
25708bde987Sscw MEMECC_SYN_INVALID, /* 0xa9 */
25808bde987Sscw MEMECC_SYN_INVALID, /* 0xaa */
25908bde987Sscw MEMECC_SYN_INVALID, /* 0xab */
26008bde987Sscw MEMECC_SYN_INVALID, /* 0xac */
26108bde987Sscw MEMECC_SYN_INVALID, /* 0xad */
26208bde987Sscw MEMECC_SYN_INVALID, /* 0xae */
26308bde987Sscw MEMECC_SYN_INVALID, /* 0xaf */
26408bde987Sscw MEMECC_SYN_BANK_A | 13, /* 0xb0: Bank A 13/29 */
26508bde987Sscw MEMECC_SYN_INVALID, /* 0xb1 */
26608bde987Sscw MEMECC_SYN_INVALID, /* 0xb2 */
26708bde987Sscw MEMECC_SYN_INVALID, /* 0xb3 */
26808bde987Sscw MEMECC_SYN_INVALID, /* 0xb4 */
26908bde987Sscw MEMECC_SYN_INVALID, /* 0xb5 */
27008bde987Sscw MEMECC_SYN_INVALID, /* 0xb6 */
27108bde987Sscw MEMECC_SYN_INVALID, /* 0xb7 */
27208bde987Sscw MEMECC_SYN_INVALID, /* 0xb8 */
27308bde987Sscw MEMECC_SYN_INVALID, /* 0xb9 */
27408bde987Sscw MEMECC_SYN_INVALID, /* 0xba */
27508bde987Sscw MEMECC_SYN_INVALID, /* 0xbb */
27608bde987Sscw MEMECC_SYN_INVALID, /* 0xbc */
27708bde987Sscw MEMECC_SYN_INVALID, /* 0xbd */
27808bde987Sscw MEMECC_SYN_INVALID, /* 0xbe */
27908bde987Sscw MEMECC_SYN_INVALID, /* 0xbf */
28008bde987Sscw MEMECC_SYN_INVALID, /* 0xc0 */
28108bde987Sscw MEMECC_SYN_BANK_B | 10, /* 0xc1: Bank B 10/26 */
28208bde987Sscw MEMECC_SYN_BANK_B | 13, /* 0xc2: Bank B 13/29 */
28308bde987Sscw MEMECC_SYN_INVALID, /* 0xc3 */
28408bde987Sscw MEMECC_SYN_BANK_B | 14, /* 0xc4: Bank B 14/30 */
28508bde987Sscw MEMECC_SYN_INVALID, /* 0xc5 */
28608bde987Sscw MEMECC_SYN_INVALID, /* 0xc6 */
28708bde987Sscw MEMECC_SYN_INVALID, /* 0xc7 */
28808bde987Sscw MEMECC_SYN_BANK_B | 0, /* 0xc8: Bank B 0/16 */
28908bde987Sscw MEMECC_SYN_INVALID, /* 0xc9 */
29008bde987Sscw MEMECC_SYN_INVALID, /* 0xca */
29108bde987Sscw MEMECC_SYN_INVALID, /* 0xcb */
29208bde987Sscw MEMECC_SYN_INVALID, /* 0xcc */
29308bde987Sscw MEMECC_SYN_INVALID, /* 0xcd */
29408bde987Sscw MEMECC_SYN_INVALID, /* 0xce */
29508bde987Sscw MEMECC_SYN_INVALID, /* 0xcf */
29608bde987Sscw MEMECC_SYN_BANK_B | 1, /* 0xd0: Bank B 1/17 */
29708bde987Sscw MEMECC_SYN_INVALID, /* 0xd1 */
29808bde987Sscw MEMECC_SYN_INVALID, /* 0xd2 */
29908bde987Sscw MEMECC_SYN_BANK_A | 3, /* 0xd3: Bank A 3/19 */
30008bde987Sscw MEMECC_SYN_INVALID, /* 0xd4 */
30108bde987Sscw MEMECC_SYN_INVALID, /* 0xd5 */
30208bde987Sscw MEMECC_SYN_INVALID, /* 0xd6 */
30308bde987Sscw MEMECC_SYN_INVALID, /* 0xd7 */
30408bde987Sscw MEMECC_SYN_INVALID, /* 0xd8 */
30508bde987Sscw MEMECC_SYN_INVALID, /* 0xd9 */
30608bde987Sscw MEMECC_SYN_INVALID, /* 0xda */
30708bde987Sscw MEMECC_SYN_INVALID, /* 0xdb */
30808bde987Sscw MEMECC_SYN_INVALID, /* 0xdc */
30908bde987Sscw MEMECC_SYN_INVALID, /* 0xdd */
31008bde987Sscw MEMECC_SYN_INVALID, /* 0xde */
31108bde987Sscw MEMECC_SYN_INVALID, /* 0xdf */
31208bde987Sscw MEMECC_SYN_BANK_B | 2, /* 0xe0: Bank B 2/18 */
31308bde987Sscw MEMECC_SYN_INVALID, /* 0xe1 */
31408bde987Sscw MEMECC_SYN_INVALID, /* 0xe2 */
31508bde987Sscw MEMECC_SYN_INVALID, /* 0xe3 */
31608bde987Sscw MEMECC_SYN_INVALID, /* 0xe4 */
31708bde987Sscw MEMECC_SYN_INVALID, /* 0xe5 */
31808bde987Sscw MEMECC_SYN_INVALID, /* 0xe6 */
31908bde987Sscw MEMECC_SYN_INVALID, /* 0xe7 */
32008bde987Sscw MEMECC_SYN_INVALID, /* 0xe8 */
32108bde987Sscw MEMECC_SYN_BANK_D | 11, /* 0xe9: Bank D 11/27 */
32208bde987Sscw MEMECC_SYN_INVALID, /* 0xea */
32308bde987Sscw MEMECC_SYN_INVALID, /* 0xeb */
32408bde987Sscw MEMECC_SYN_INVALID, /* 0xec */
32508bde987Sscw MEMECC_SYN_INVALID, /* 0xed */
32608bde987Sscw MEMECC_SYN_INVALID, /* 0xee */
32708bde987Sscw MEMECC_SYN_INVALID, /* 0xef */
32808bde987Sscw MEMECC_SYN_INVALID, /* 0xf0 */
32908bde987Sscw MEMECC_SYN_INVALID, /* 0xf1 */
33008bde987Sscw MEMECC_SYN_INVALID, /* 0xf2 */
33108bde987Sscw MEMECC_SYN_INVALID, /* 0xf3 */
33208bde987Sscw MEMECC_SYN_BANK_D | 3, /* 0xf4: Bank D 3/19 */
33308bde987Sscw MEMECC_SYN_INVALID, /* 0xf5 */
33408bde987Sscw MEMECC_SYN_INVALID, /* 0xf6 */
33508bde987Sscw MEMECC_SYN_INVALID, /* 0xf7 */
33608bde987Sscw MEMECC_SYN_INVALID, /* 0xf8 */
33708bde987Sscw MEMECC_SYN_INVALID, /* 0xf9 */
33808bde987Sscw MEMECC_SYN_INVALID, /* 0xfa */
33908bde987Sscw MEMECC_SYN_INVALID, /* 0xfb */
34008bde987Sscw MEMECC_SYN_INVALID, /* 0xfc */
34108bde987Sscw MEMECC_SYN_INVALID, /* 0xfd */
34208bde987Sscw MEMECC_SYN_INVALID, /* 0xfe */
34308bde987Sscw MEMECC_SYN_INVALID /* 0xff */
34408bde987Sscw };
34508bde987Sscw
34608bde987Sscw
34708bde987Sscw /* ARGSUSED */
34808bde987Sscw void
memc_init(struct memc_softc * sc)349454af1c0Sdsl memc_init(struct memc_softc *sc)
35008bde987Sscw {
35108bde987Sscw u_int8_t chipid;
35208bde987Sscw u_int8_t memcfg;
35308bde987Sscw
35408bde987Sscw if (memc_softc_count == MEMC_NDEVS)
35508bde987Sscw panic("memc_attach: too many memc devices!");
35608bde987Sscw
35708bde987Sscw memc_softcs[memc_softc_count++] = sc;
35808bde987Sscw
35908bde987Sscw chipid = memc_reg_read(sc, MEMC_REG_CHIP_ID);
36008bde987Sscw memcfg = memc_reg_read(sc, MEMC_REG_MEMORY_CONFIG);
36108bde987Sscw
36208bde987Sscw printf(": %dMB %s Memory Controller Chip (Rev %d)\n",
36308bde987Sscw MEMC_MEMORY_CONFIG_2_MB(memcfg),
36408bde987Sscw (chipid == MEMC_CHIP_ID_MEMC040) ? "Parity" : "ECC",
36508bde987Sscw memc_reg_read(sc, MEMC_REG_CHIP_REVISION));
36608bde987Sscw
367*5f819ca3Schs printf("%s: Base Address: 0x%x, ", device_xname(sc->sc_dev),
36808bde987Sscw MEMC_BASE_ADDRESS(memc_reg_read(sc, MEMC_REG_BASE_ADDRESS_HI),
36908bde987Sscw memc_reg_read(sc, MEMC_REG_BASE_ADDRESS_LO)));
37008bde987Sscw
37108bde987Sscw printf("Fast RAM Read %sabled\n", (memc_reg_read(sc,
37208bde987Sscw MEMC_REG_MEMORY_CONFIG) & MEMC_MEMORY_CONFIG_FSTRD) ?
37308bde987Sscw "En" : "Dis");
37408bde987Sscw
37508bde987Sscw switch (chipid) {
37608bde987Sscw case MEMC_CHIP_ID_MEMC040:
37708bde987Sscw memc040_attach(sc);
37808bde987Sscw break;
37908bde987Sscw case MEMC_CHIP_ID_MEMECC:
38008bde987Sscw memecc_attach(sc);
38108bde987Sscw break;
38208bde987Sscw }
38308bde987Sscw }
38408bde987Sscw
38508bde987Sscw static void
memc040_attach(struct memc_softc * sc)38608bde987Sscw memc040_attach(struct memc_softc *sc)
38708bde987Sscw {
38808bde987Sscw
38908bde987Sscw /* XXX: TBD */
39008bde987Sscw }
39108bde987Sscw
39208bde987Sscw static void
memecc_attach(struct memc_softc * sc)39308bde987Sscw memecc_attach(struct memc_softc *sc)
39408bde987Sscw {
39508bde987Sscw u_int8_t rv;
39608bde987Sscw
39708bde987Sscw /*
39808bde987Sscw * First, disable bus-error and interrupts on ECC errors.
39908bde987Sscw * Also switch off SWAIT to enhance performance.
40008bde987Sscw */
40108bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_DRAM_CONTROL);
40208bde987Sscw rv &= ~(MEMECC_DRAM_CONTROL_NCEBEN |
40308bde987Sscw MEMECC_DRAM_CONTROL_NCEIEN |
40408bde987Sscw MEMECC_DRAM_CONTROL_SWAIT);
40508bde987Sscw rv |= MEMECC_DRAM_CONTROL_RAMEN;
40608bde987Sscw memc_reg_write(sc, MEMECC_REG_DRAM_CONTROL, rv);
40708bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_SCRUB_CONTROL);
40808bde987Sscw rv &= ~(MEMECC_SCRUB_CONTROL_SCRBEN | MEMECC_SCRUB_CONTROL_SBEIEN);
40908bde987Sscw memc_reg_write(sc, MEMECC_REG_SCRUB_CONTROL, rv);
41008bde987Sscw
41108bde987Sscw /*
41208bde987Sscw * Ensure error correction is enabled
41308bde987Sscw */
41408bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_DATA_CONTROL);
41508bde987Sscw rv &= ~MEMECC_DATA_CONTROL_DERC;
41608bde987Sscw memc_reg_write(sc, MEMECC_REG_DATA_CONTROL, rv);
41708bde987Sscw
41808bde987Sscw /*
41908bde987Sscw * Clear any error currently in the logs
42008bde987Sscw */
42108bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_ERROR_LOGGER);
42208bde987Sscw #ifdef DIAGNOSTIC
42308bde987Sscw if ((rv & MEMECC_ERROR_LOGGER_MASK) != 0)
42408bde987Sscw memecc_log_error(sc, rv, 0, 0);
42508bde987Sscw #endif
42608bde987Sscw memc_reg_write(sc, MEMECC_REG_ERROR_LOGGER,
42708bde987Sscw MEMECC_ERROR_LOGGER_ERRLOG);
42808bde987Sscw
42908bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_ERROR_LOGGER + 2);
43008bde987Sscw #ifdef DIAGNOSTIC
43108bde987Sscw if ((rv & MEMECC_ERROR_LOGGER_MASK) != 0)
43208bde987Sscw memecc_log_error(sc, rv, 2, 0);
43308bde987Sscw #endif
43408bde987Sscw memc_reg_write(sc, MEMECC_REG_ERROR_LOGGER + 2,
43508bde987Sscw MEMECC_ERROR_LOGGER_ERRLOG);
43608bde987Sscw
43708bde987Sscw /*
43808bde987Sscw * Now hook the ECC error interrupt
43908bde987Sscw */
44008bde987Sscw if (memc_softc_count == 1)
44108bde987Sscw memc_hook_error_intr(sc, memecc_err_intr);
44208bde987Sscw
44308bde987Sscw /*
44408bde987Sscw * Enable bus-error and interrupt on uncorrectable ECC
44508bde987Sscw */
44608bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_DRAM_CONTROL);
44708bde987Sscw rv |= MEMECC_DRAM_CONTROL_NCEBEN | MEMECC_DRAM_CONTROL_NCEIEN;
44808bde987Sscw memc_reg_write(sc, MEMECC_REG_DRAM_CONTROL, rv);
44908bde987Sscw
45008bde987Sscw /*
45108bde987Sscw * Set up the scrubber to run roughly once every 24 hours
45208bde987Sscw * with minimal impact on the local bus. With these on/off
45308bde987Sscw * time settings, a scrub of a 32MB DRAM board will take
45408bde987Sscw * roughly half a minute.
45508bde987Sscw */
45608bde987Sscw memc_reg_write(sc, MEMECC_REG_SCRUB_PERIOD_HI,
45708bde987Sscw MEMECC_SCRUB_PERIOD_HI(MEMECC_SCRUBBER_PERIOD));
45808bde987Sscw memc_reg_write(sc, MEMECC_REG_SCRUB_PERIOD_LO,
45908bde987Sscw MEMECC_SCRUB_PERIOD_LO(MEMECC_SCRUBBER_PERIOD));
46008bde987Sscw memc_reg_write(sc, MEMECC_REG_SCRUB_TIME_ONOFF,
46108bde987Sscw MEMECC_SCRUB_TIME_ON_1 | MEMECC_SCRUB_TIME_OFF_16);
46208bde987Sscw
46308bde987Sscw /*
46408bde987Sscw * Start the scrubber, and enable interrupts on Correctable errors
46508bde987Sscw */
46608bde987Sscw memc_reg_write(sc, MEMECC_REG_SCRUB_CONTROL,
46708bde987Sscw memc_reg_read(sc, MEMECC_REG_SCRUB_CONTROL) |
46808bde987Sscw MEMECC_SCRUB_CONTROL_SCRBEN | MEMECC_SCRUB_CONTROL_SBEIEN);
46908bde987Sscw
470*5f819ca3Schs printf("%s: Logging ECC errors at ipl %d\n", device_xname(sc->sc_dev),
47108bde987Sscw MEMC_IRQ_LEVEL);
47208bde987Sscw }
47308bde987Sscw
47408bde987Sscw static void
memc_hook_error_intr(struct memc_softc * sc,int (* func)(void *))47508bde987Sscw memc_hook_error_intr(struct memc_softc *sc, int (*func)(void *))
47608bde987Sscw {
47708bde987Sscw
47808bde987Sscw #if 0
47908bde987Sscw evcnt_attach_dynamic(&sc->sc_evcnt, EVCNT_TYPE_INTR,
48008bde987Sscw (*sc->sc_isrevcnt)(sc->sc_isrcookie, MEMC_IRQ_LEVEL),
48108bde987Sscw "memory", "ecc errors");
48208bde987Sscw #endif
48308bde987Sscw
48408bde987Sscw /*
48508bde987Sscw * On boards without a VMEChip2, the interrupt is routed
48608bde987Sscw * via the MCChip (mvme162/mvme172).
48708bde987Sscw */
48808bde987Sscw if (vmetwo_not_present)
48908bde987Sscw pcctwointr_establish(MCCHIPV_PARITY_ERR, func, MEMC_IRQ_LEVEL,
49008bde987Sscw sc, &sc->sc_evcnt);
49108bde987Sscw else
49208bde987Sscw vmetwo_local_intr_establish(MEMC_IRQ_LEVEL,
49308bde987Sscw VME2_VEC_PARITY_ERROR, func, sc, &sc->sc_evcnt);
49408bde987Sscw }
49508bde987Sscw
49608bde987Sscw /* ARGSUSED */
49708bde987Sscw static int
memecc_err_intr(void * arg)49808bde987Sscw memecc_err_intr(void *arg)
49908bde987Sscw {
50008bde987Sscw struct memc_softc *sc;
50108bde987Sscw u_int8_t rv;
50208bde987Sscw int i, j, cnt = 0;
50308bde987Sscw
50408bde987Sscw /*
50508bde987Sscw * For each memory controller we found ...
50608bde987Sscw */
50708bde987Sscw for (i = 0; i < memc_softc_count; i++) {
50808bde987Sscw sc = memc_softcs[i];
50908bde987Sscw
51008bde987Sscw /*
51108bde987Sscw * There are two error loggers per controller, the registers of
51208bde987Sscw * the 2nd are offset from the 1st by 2 bytes.
51308bde987Sscw */
51408bde987Sscw for (j = 0; j <= 2; j += 2) {
51508bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_ERROR_LOGGER + j);
51608bde987Sscw if ((rv & MEMECC_ERROR_LOGGER_MASK) != 0) {
51708bde987Sscw memecc_log_error(sc, rv, j, 1);
51808bde987Sscw memc_reg_write(sc, MEMECC_REG_ERROR_LOGGER + j,
51908bde987Sscw MEMECC_ERROR_LOGGER_ERRLOG);
52008bde987Sscw cnt++;
52108bde987Sscw }
52208bde987Sscw }
52308bde987Sscw }
52408bde987Sscw
52508bde987Sscw return (cnt);
52608bde987Sscw }
52708bde987Sscw
52808bde987Sscw /*
52908bde987Sscw * Log an ECC error to the console.
53008bde987Sscw * Note: Since this usually runs at an elevated ipl (above clock), we
53108bde987Sscw * should probably schedule a soft interrupt to log the error details.
53208bde987Sscw * (But only for errors where we would not normally panic.)
53308bde987Sscw */
53408bde987Sscw static void
memecc_log_error(struct memc_softc * sc,u_int8_t errlog,int off,int mbepanic)53508bde987Sscw memecc_log_error(struct memc_softc *sc, u_int8_t errlog, int off, int mbepanic)
53608bde987Sscw {
53708bde987Sscw u_int32_t addr;
53808bde987Sscw u_int8_t rv, syndrome;
53908bde987Sscw const char *bm = "CPU";
54008bde987Sscw const char *rdwr;
54108bde987Sscw const char *etype;
54208bde987Sscw char syntext[32];
54308bde987Sscw
54408bde987Sscw /*
54508bde987Sscw * Get the address associated with the error.
54608bde987Sscw */
54708bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_ERROR_ADDRESS_HIHI + off);
54808bde987Sscw addr = (u_int32_t)rv;
54908bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_ERROR_ADDRESS_HI + off);
55008bde987Sscw addr = (addr << 8) | (u_int32_t)rv;
55108bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_ERROR_ADDRESS_MID + off);
55208bde987Sscw addr = (addr << 8) | (u_int32_t)rv;
55308bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_ERROR_ADDRESS_LO + off);
55408bde987Sscw addr = (addr << 8) | (u_int32_t)rv;
55508bde987Sscw
55608bde987Sscw /*
55708bde987Sscw * And the Syndrome bits
55808bde987Sscw */
55908bde987Sscw syndrome = memc_reg_read(sc, MEMECC_REG_ERROR_SYNDROME + off);
56008bde987Sscw
56108bde987Sscw rdwr = ((errlog & MEMECC_ERROR_LOGGER_ERD) != 0) ? " read" : " write";
56208bde987Sscw
56308bde987Sscw if ((errlog & MEMECC_ERROR_LOGGER_EALT) != 0)
56408bde987Sscw bm = "Peripheral Device";
56508bde987Sscw else
56608bde987Sscw if ((errlog & MEMECC_ERROR_LOGGER_ESCRB) != 0) {
56708bde987Sscw bm = "Scrubber";
56808bde987Sscw rdwr = "";
56908bde987Sscw }
57008bde987Sscw
57108bde987Sscw if ((errlog & MEMECC_ERROR_LOGGER_SBE) != 0) {
57208bde987Sscw int syncode, bank, bitnum;
57308bde987Sscw
57408bde987Sscw etype = "Correctable";
57508bde987Sscw syncode = memc_syn_decode[syndrome];
57608bde987Sscw bitnum = (syncode & MEMECC_SYN_BIT_MASK) + (off ? 16 : 0);
57708bde987Sscw bank = (syncode >> MEMECC_SYN_BANK_SHIFT) &MEMECC_SYN_BANK_MASK;
57808bde987Sscw
57908bde987Sscw if (syncode == MEMECC_SYN_INVALID)
58008bde987Sscw strcpy(syntext, "Invalid!");
58108bde987Sscw else
58208bde987Sscw if ((syncode & MEMECC_SYN_CHECKBIT_ERR) != 0)
583aca4c091Sitojun snprintf(syntext, sizeof(syntext),
584aca4c091Sitojun "Checkbit#%d", bitnum);
58508bde987Sscw else {
58608bde987Sscw addr |= (u_int32_t) (bank << 2);
587aca4c091Sitojun snprintf(syntext, sizeof(syntext),
588aca4c091Sitojun "DRAM Bank %c, Bit#%d", 'A' + bank, bitnum);
58908bde987Sscw }
59008bde987Sscw } else if ((errlog & MEMECC_ERROR_LOGGER_MBE) != 0)
59108bde987Sscw etype = "Uncorrectable";
59208bde987Sscw else
59308bde987Sscw etype = "Spurious";
59408bde987Sscw
59508bde987Sscw printf("%s: %s error on %s%s access to 0x%08x.\n",
596*5f819ca3Schs device_xname(sc->sc_dev), etype, bm, rdwr, addr);
59708bde987Sscw
59808bde987Sscw if ((errlog & MEMECC_ERROR_LOGGER_SBE) != 0)
599*5f819ca3Schs printf("%s: ECC Syndrome 0x%02x (%s)\n", device_xname(sc->sc_dev),
60008bde987Sscw syndrome, syntext);
60108bde987Sscw
60208bde987Sscw /*
60308bde987Sscw * If an uncorrectable error was detected by an alternate
60408bde987Sscw * bus master or the scrubber, panic immediately.
60508bde987Sscw * We can't rely on the contents of memory at this point.
60608bde987Sscw *
60708bde987Sscw * Uncorrectable errors detected when the CPU was accessing
60808bde987Sscw * DRAM will cause the CPU to take a bus error trap. Depending
60908bde987Sscw * on whether the error was in kernel or user mode, the system
61008bde987Sscw * with either panic or kill the affected process. Basically,
61108bde987Sscw * we don't have to deal with it here.
61208bde987Sscw *
61308bde987Sscw * XXX: I'm not sure whether it's our responsibility to
61408bde987Sscw * perform some dummy writes to the offending address in this
61508bde987Sscw * case to re-generate a good ECC. Note that we'd have to write
61608bde987Sscw * an entire block of 4 words since we can only narrow down the
61708bde987Sscw * faulty address for correctable errors...
61808bde987Sscw */
61908bde987Sscw if (mbepanic && (errlog & MEMECC_ERROR_LOGGER_MBE) &&
62008bde987Sscw (errlog & (MEMECC_ERROR_LOGGER_ESCRB|MEMECC_ERROR_LOGGER_EALT))) {
62108bde987Sscw /*
62212490842Swiz * Ensure we don't get a Bus Error while panicking...
62308bde987Sscw */
62408bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_DRAM_CONTROL + off);
62508bde987Sscw rv &= ~(MEMECC_DRAM_CONTROL_NCEBEN |
62608bde987Sscw MEMECC_DRAM_CONTROL_NCEIEN);
62708bde987Sscw memc_reg_write(sc, MEMECC_REG_DRAM_CONTROL + off, rv);
62808bde987Sscw rv = memc_reg_read(sc, MEMECC_REG_SCRUB_CONTROL + off);
62908bde987Sscw rv &= ~(MEMECC_SCRUB_CONTROL_SBEIEN |
63008bde987Sscw MEMECC_SCRUB_CONTROL_SCRBEN);
63108bde987Sscw memc_reg_write(sc, MEMECC_REG_SCRUB_CONTROL + off, rv);
63208bde987Sscw
63308bde987Sscw panic("%s: Halting system to preserve data integrity.",
634*5f819ca3Schs device_xname(sc->sc_dev));
63508bde987Sscw }
63608bde987Sscw }
637