Create an #include layer for bus/pci and bus/isa so source files do not
[dragonfly.git] / sys / dev / bridge / ecc / ecc.c
1 /*
2  * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>.   AMD register addresses and
6  * values were pulled from MemTest-86 and Linux.
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 
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
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  * 
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  * 
35  * $DragonFly: src/sys/dev/bridge/ecc/ecc.c,v 1.2 2008/08/02 01:14:42 dillon Exp $
36  */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/kernel.h>
42 #include <sys/malloc.h>
43
44 #include <bus/pci/pcivar.h>
45 #include <bus/pci/pcireg.h>
46 #include <bus/pci/pcibus.h>
47 #include <bus/pci/pci_cfgreg.h>
48 #include <bus/pci/pcib_private.h>
49
50 #include "pcib_if.h"
51
52 #define arysize(ary)    (sizeof(ary)/sizeof((ary)[0]))
53
54 static int setup_none(device_t dev);
55 static int setup_amd64(device_t dev);
56 static void poll_amd64(void *dev_arg);
57
58 struct pci_memory_controller { 
59         uint16_t        vid;
60         uint16_t        did;
61         const char      *desc;
62         int (*setup)(device_t dev);
63 };
64
65 struct pci_ecc_softc {
66         struct pci_memory_controller *config;
67         struct callout poll_callout;
68         int poll_enable;
69 };
70
71 static struct pci_memory_controller mem_controllers[] = {
72         /* AMD */
73         { 0x1022, 0x7006, "AMD 751", setup_none },
74         { 0x1022, 0x700c, "AMD 762", setup_none },
75         { 0x1022, 0x700e, "AMD 761", setup_none },
76         { 0x1022, 0x1100, "AMD 8000", setup_amd64 },
77         { 0x1022, 0x7454, "AMD 8000", setup_amd64 }
78 };
79
80 static int
81 pci_ecc_probe(device_t dev)
82 {
83         struct pci_ecc_softc *sc;
84         uint16_t vid;
85         uint16_t did;
86         int i;
87
88         vid = pci_get_vendor(dev);
89         did = pci_get_device(dev);
90
91         for (i = 0; i < arysize(mem_controllers); ++i) {
92                 if (mem_controllers[i].vid == vid &&
93                     mem_controllers[i].did == did
94                 ) {
95                         sc = device_get_softc(dev);
96                         sc->config = &mem_controllers[i];
97                         return(0);
98                 }
99         }
100         return (ENXIO);
101 }
102
103 static int
104 pci_ecc_attach(device_t dev)
105 {
106         struct pci_ecc_softc *sc = device_get_softc(dev);
107
108         return (sc->config->setup(dev));
109 }
110
111 static device_method_t pci_ecc_methods[] = {
112         /* Device interface */
113         DEVMETHOD(device_probe,         pci_ecc_probe),
114         DEVMETHOD(device_attach,        pci_ecc_attach),
115         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
116         DEVMETHOD(device_suspend,       bus_generic_suspend),
117         DEVMETHOD(device_resume,        bus_generic_resume),
118         { 0, 0 }
119 };
120
121 static driver_t pci_ecc_driver = {
122         "ecc",
123         pci_ecc_methods,
124         sizeof(struct pci_ecc_softc)
125 };
126 static devclass_t ecc_devclass;
127 DRIVER_MODULE(ecc, pci, pci_ecc_driver, ecc_devclass, 0, 0);
128 MODULE_DEPEND(ecc, pci, 1, 1, 1);
129
130 /*
131  * Architecture-specific procedures
132  */
133 static
134 int
135 setup_none(device_t dev)
136 {
137         return(0);
138 }
139
140 static
141 int
142 setup_amd64(device_t dev)
143 {
144         struct pci_ecc_softc *sc = device_get_softc(dev);
145         uint32_t draminfo;
146         uint32_t eccinfo;
147         int bus = pci_get_bus(dev);
148         int slot = pci_get_slot(dev);
149
150         /*
151          * The memory bridge is recognized as four PCI devices
152          * using function codes 0, 1, 2, and 3.  We probe for the
153          * device at function code 0 and assume that all four exist.
154          */
155         draminfo = pcib_read_config(dev, bus, slot, 2, 0x90, 4);
156         eccinfo = pcib_read_config(dev, bus, slot, 3, 0x44, 4);
157
158         device_printf(dev, "attached %s memory controller\n", sc->config->desc);
159         if ((draminfo >> 17) & 1)
160                 device_printf(dev, "memory type: ECC\n");
161         else
162                 device_printf(dev, "memory type: NON-ECC\n");
163         switch((eccinfo >> 22) & 3) {
164         case 0:
165                 device_printf(dev, "ecc mode: DISABLED\n");
166                 break;
167         case 1:
168                 device_printf(dev, "ecc mode: ENABLED/CORRECT-MODE\n");
169                 sc->poll_enable = 1;
170                 break;
171         case 2:
172                 device_printf(dev, "ecc mode: ENABLED/RESERVED (disabled)\n");
173                 break;
174         case 3:
175                 device_printf(dev, "ecc mode: ENABLED/CHIPKILL-MODE\n");
176                 sc->poll_enable = 1;
177                 break;
178         }
179
180         /*
181          * Enable ECC logging and clear any previous error.
182          */
183         if (sc->poll_enable) {
184                 uint64_t v64;
185                 uint32_t v32;
186
187                 v64 = rdmsr(0x017B);
188                 wrmsr(0x17B, (v64 & ~0xFFFFFFFFLL) | 0x00000010LL);
189                 v32 = pcib_read_config(dev, bus, slot, 3, 0x4C, 4);
190                 v32 &= 0x7F801EFC;
191                 pcib_write_config(dev, bus, slot, 3, 0x4C, v32, 4);
192
193                 callout_init(&sc->poll_callout);
194                 callout_reset(&sc->poll_callout, hz, poll_amd64, dev);
195         }
196         return(0);
197 }
198
199 static
200 void
201 poll_amd64(void *dev_arg)
202 {
203         device_t dev = dev_arg;
204         struct pci_ecc_softc *sc = device_get_softc(dev);
205         int bus = pci_get_bus(dev);
206         int slot = pci_get_slot(dev);
207         uint32_t v32;
208         uint32_t addr;
209
210         /*
211          * The address calculation is not entirely correct.  We need to
212          * look at the AMD chipset documentation.
213          */
214         v32 = pcib_read_config(dev, bus, slot, 3, 0x4C, 4);
215         if ((v32 & 0x80004000) == 0x80004000) {
216                 addr = pcib_read_config(dev, bus, slot, 3, 0x50, 4);
217                 device_printf(dev, "Correctable ECC error at %08x\n", addr);
218                 pcib_write_config(dev, bus, slot, 3, 0x4C, v32 & 0x7F801EFC, 4);
219         } else if ((v32 & 0x80002000) == 0x80002000) {
220                 addr = pcib_read_config(dev, bus ,slot, 3, 0x50, 4);
221                 device_printf(dev, "Uncorrectable ECC error at %08x\n", addr);
222                 pcib_write_config(dev, bus, slot, 3, 0x4C, v32 & 0x7F801EFC, 4);
223         }
224         callout_reset(&sc->poll_callout, hz, poll_amd64, dev);
225 }
226