ecc/e3: Split it into two drivers, coremctl(4) and ecc(4)
[dragonfly.git] / sys / dev / misc / coremctl / coremctl.c
1 /*
2  * Copyright (c) 2015 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/bitops.h>
41
42 #include <bus/pci/pcivar.h>
43 #include <bus/pci/pcireg.h>
44 #include <bus/pci/pcibus.h>
45 #include <bus/pci/pci_cfgreg.h>
46
47 #include <vm/pmap.h>
48
49 #include "coremctl_if.h"
50 #include "pcib_if.h"
51
52 #include <dev/misc/coremctl/coremctl_reg.h>
53
54 #define COREMCTL_VER_1  1       /* Sandy Bridge */
55 #define COREMCTL_VER_2  2       /* Ivy Bridge */
56 #define COREMCTL_VER_3  3       /* Haswell */
57
58 struct coremctl_type {
59         uint16_t        did;
60         const char      *desc;
61         int             ver;            /* COREMCTL_VER_ */
62 };
63
64 struct coremctl_softc {
65         device_t        sc_dev;
66         int             sc_ver; /* COREMCTL_VER_ */
67         device_t        sc_ecc;
68         volatile uint8_t *sc_mch;
69 };
70
71 #define CSR_READ_4(sc, ofs)     (*(volatile uint32_t *)((sc)->sc_mch + (ofs)))
72
73 static void     coremctl_identify(driver_t *, device_t);
74 static int      coremctl_probe(device_t);
75 static int      coremctl_attach(device_t);
76 static int      coremctl_detach(device_t);
77 static int      coremctl_mch_readreg(device_t, int, uint32_t *);
78 static int      coremctl_pci_read_ivar(device_t, device_t, int, uintptr_t *);
79 static uint32_t coremctl_pci_read_config(device_t, device_t, int, int);
80 static void     coremctl_pci_write_config(device_t, device_t, int, uint32_t,
81                     int);
82
83 static void     coremctl_chaninfo(struct coremctl_softc *, uint32_t,
84                     const char *);
85
86 static const struct coremctl_type coremctl_types[] = {
87         { PCI_E3V1_MEMCTL_DID, "Intel E3 memory controller",
88           COREMCTL_VER_1 },
89
90         { PCI_E3V2_MEMCTL_DID, "Intel E3 v2 memory controller",
91           COREMCTL_VER_2 },
92
93         { PCI_E3V3_MEMCTL_DID, "Intel E3 v3 memory controller",
94           COREMCTL_VER_3 },
95
96         { PCI_COREV3_MEMCTL_DID, "Intel i3/i5/i7 Haswell memory controller",
97           COREMCTL_VER_3 },
98
99         { 0, NULL, 0 } /* required last entry */
100 };
101
102 static device_method_t coremctl_methods[] = {
103         /* Device interface */
104         DEVMETHOD(device_identify,      coremctl_identify),
105         DEVMETHOD(device_probe,         coremctl_probe),
106         DEVMETHOD(device_attach,        coremctl_attach),
107         DEVMETHOD(device_detach,        coremctl_detach),
108         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
109         DEVMETHOD(device_suspend,       bus_generic_suspend),
110         DEVMETHOD(device_resume,        bus_generic_resume),
111
112         /* Bus interface */
113         DEVMETHOD(bus_read_ivar,        coremctl_pci_read_ivar),
114
115         /* PCI interface */
116         DEVMETHOD(pci_read_config,      coremctl_pci_read_config),
117         DEVMETHOD(pci_write_config,     coremctl_pci_write_config),
118
119         /* Core memory controller interface */
120         DEVMETHOD(coremctl_mch_read,    coremctl_mch_readreg),
121
122         DEVMETHOD_END
123 };
124
125 static driver_t coremctl_driver = {
126         "coremctl",
127         coremctl_methods,
128         sizeof(struct coremctl_softc)
129 };
130 static devclass_t coremctl_devclass;
131
132 DRIVER_MODULE(coremctl, hostb, coremctl_driver, coremctl_devclass, NULL, NULL);
133 MODULE_VERSION(coremctl, 1);
134 MODULE_DEPEND(coremctl, pci, 1, 1, 1);
135
136 static void
137 coremctl_identify(driver_t *driver, device_t parent)
138 {
139         const struct coremctl_type *t;
140         uint16_t did;
141
142         /* Already identified */
143         if (device_find_child(parent, "coremctl", -1) != NULL)
144                 return;
145
146         if (pci_get_vendor(parent) != PCI_CORE_MEMCTL_VID)
147                 return;
148
149         did = pci_get_device(parent);
150         for (t = coremctl_types; t->desc != NULL; ++t) {
151                 if (t->did == did) {
152                         if (device_add_child(parent, "coremctl", -1) == NULL)
153                                 device_printf(parent, "add coremctl failed\n");
154                         return;
155                 }
156         }
157 }
158
159 static int
160 coremctl_probe(device_t dev)
161 {
162         const struct coremctl_type *t;
163         uint16_t did;
164
165         if (pci_get_vendor(dev) != PCI_CORE_MEMCTL_VID)
166                 return ENXIO;
167
168         did = pci_get_device(dev);
169         for (t = coremctl_types; t->desc != NULL; ++t) {
170                 if (t->did == did) {
171                         struct coremctl_softc *sc = device_get_softc(dev);
172
173                         device_set_desc(dev, t->desc);
174                         sc->sc_ver = t->ver;
175                         return 0;
176                 }
177         }
178         return ENXIO;
179 }
180
181 static int
182 coremctl_attach(device_t dev)
183 {
184         struct coremctl_softc *sc = device_get_softc(dev);
185         uint32_t capa, dmfc, mch_barlo, mch_barhi;
186         uint64_t mch_bar;
187         int dmfc_parsed = 1;
188
189         sc->sc_dev = dev;
190
191         capa = pci_read_config(dev, PCI_CORE_CAPID0_A, 4);
192
193         if (sc->sc_ver == COREMCTL_VER_1) {
194                 dmfc = __SHIFTOUT(capa, PCI_CORE_CAPID0_A_DMFC);
195         } else { /* v2/v3 */
196                 uint32_t capb;
197
198                 capb = pci_read_config(dev, PCI_CORE_CAPID0_B, 4);
199                 dmfc = __SHIFTOUT(capb, PCI_CORE_CAPID0_B_DMFC);
200         }
201
202         if (dmfc == PCI_CORE_CAPID0_DMFC_1067) {
203                 device_printf(dev, "CAP DDR3 1067 ");
204         } else if (dmfc == PCI_CORE_CAPID0_DMFC_1333) {
205                 device_printf(dev, "CAP DDR3 1333 ");
206         } else {
207                 if (sc->sc_ver == COREMCTL_VER_1) {
208                         if (dmfc == PCI_CORE_CAPID0_DMFC_V1_ALL)
209                                 device_printf(dev, "no CAP ");
210                         else
211                                 dmfc_parsed = 0;
212                 } else { /* v2/v3 */
213                         if (dmfc == PCI_CORE_CAPID0_DMFC_1600)
214                                 device_printf(dev, "CAP DDR3 1600 ");
215                         else if (dmfc == PCI_CORE_CAPID0_DMFC_1867)
216                                 device_printf(dev, "CAP DDR3 1867 ");
217                         else if (dmfc == PCI_CORE_CAPID0_DMFC_2133)
218                                 device_printf(dev, "CAP DDR3 2133 ");
219                         else if (dmfc == PCI_CORE_CAPID0_DMFC_2400)
220                                 device_printf(dev, "CAP DDR3 2400 ");
221                         else if (dmfc == PCI_CORE_CAPID0_DMFC_2667)
222                                 device_printf(dev, "CAP DDR3 2667 ");
223                         else if (dmfc == PCI_CORE_CAPID0_DMFC_2933)
224                                 device_printf(dev, "CAP DDR3 2933 ");
225                         else
226                                 dmfc_parsed = 0;
227                 }
228         }
229         if (!dmfc_parsed) {
230                 device_printf(dev, "unknown DMFC %#x\n", dmfc);
231                 return 0;
232         }
233
234         if (capa & PCI_CORE_CAPID0_A_ECCDIS) {
235                 kprintf("NON-ECC\n");
236         } else {
237                 kprintf("ECC\n");
238                 sc->sc_ecc = device_add_child(dev, "ecc", -1);
239                 if (sc->sc_ecc == NULL)
240                         device_printf(dev, "add ecc failed\n");
241         }
242
243         mch_barlo = pci_read_config(dev, PCI_CORE_MCHBAR_LO, 4);
244         mch_barhi = pci_read_config(dev, PCI_CORE_MCHBAR_HI, 4);
245
246         mch_bar = (uint64_t)mch_barlo | (((uint64_t)mch_barhi) << 32);
247         if (bootverbose)
248                 device_printf(dev, "MCHBAR 0x%jx\n", (uintmax_t)mch_bar);
249
250         if (mch_bar & PCI_CORE_MCHBAR_LO_EN) {
251                 uint64_t map_addr = mch_bar & PCI_CORE_MCHBAR_ADDRMASK;
252
253                 sc->sc_mch = pmap_mapdev_uncacheable(map_addr, MCH_CORE_SIZE);
254
255                 if (bootverbose) {
256                         uint32_t dimm_ch0, dimm_ch1;
257
258                         dimm_ch0 = CSR_READ_4(sc, MCH_CORE_DIMM_CH0);
259                         dimm_ch1 = CSR_READ_4(sc, MCH_CORE_DIMM_CH1);
260
261                         coremctl_chaninfo(sc, dimm_ch0, "channel0");
262                         coremctl_chaninfo(sc, dimm_ch1, "channel1");
263                 }
264         } else {
265                 device_printf(dev, "MCHBAR is not enabled\n");
266         }
267
268         bus_generic_attach(dev);
269
270         return 0;
271 }
272
273 static void
274 coremctl_chaninfo(struct coremctl_softc *sc, uint32_t dimm_ch,
275     const char *desc)
276 {
277         int size_a, size_b;
278         int dimma_id, dimmb_id;
279
280         dimma_id = 0;
281         dimmb_id = 1;
282         if (dimm_ch & MCH_CORE_DIMM_A_SELECT) {
283                 dimma_id = 1;
284                 dimmb_id = 0;
285         }
286
287         size_a = __SHIFTOUT(dimm_ch, MCH_CORE_DIMM_A_SIZE);
288         if (size_a != 0) {
289                 device_printf(sc->sc_dev, "%s, DIMM%d %dMB %dx%d\n", desc,
290                     dimma_id, size_a * MCH_CORE_DIMM_SIZE_UNIT,
291                     (dimm_ch & MCH_CORE_DIMM_A_DUAL_RANK) ? 2 : 1,
292                     (dimm_ch & MCH_CORE_DIMM_A_X16) ? 16 : 8);
293         }
294
295         size_b = __SHIFTOUT(dimm_ch, MCH_CORE_DIMM_B_SIZE);
296         if (size_b != 0) {
297                 device_printf(sc->sc_dev, "%s, DIMM%d %dMB %dx%d\n", desc,
298                     dimmb_id, size_b * MCH_CORE_DIMM_SIZE_UNIT,
299                     (dimm_ch & MCH_CORE_DIMM_B_DUAL_RANK) ? 2 : 1,
300                     (dimm_ch & MCH_CORE_DIMM_B_X16) ? 16 : 8);
301         }
302
303         if (size_a == 0 && size_b == 0)
304                 return;
305
306         if (sc->sc_ver == COREMCTL_VER_1 || sc->sc_ver == COREMCTL_VER_2) {
307                 /* This bit is v3 only */
308                 dimm_ch &= ~MCH_CORE_DIMM_HORI;
309         }
310         if (dimm_ch & (MCH_CORE_DIMM_ENHI | MCH_CORE_DIMM_RI |
311             MCH_CORE_DIMM_HORI)) {
312                 device_printf(sc->sc_dev, "%s", desc);
313                 if (dimm_ch & MCH_CORE_DIMM_RI)
314                         kprintf(", rank interleave");
315                 if (dimm_ch & MCH_CORE_DIMM_ENHI)
316                         kprintf(", enhanced interleave");
317                 if (dimm_ch & MCH_CORE_DIMM_HORI)
318                         kprintf(", high order rank interleave");
319                 kprintf("\n");
320         }
321 }
322
323 static int
324 coremctl_detach(device_t dev)
325 {
326         struct coremctl_softc *sc = device_get_softc(dev);
327
328         if (sc->sc_ecc != NULL)
329                 device_delete_child(dev, sc->sc_ecc);
330         bus_generic_detach(dev);
331
332         if (sc->sc_mch != NULL)
333                 pmap_unmapdev((vm_offset_t)sc->sc_mch, MCH_CORE_SIZE);
334         return 0;
335 }
336
337 static int
338 coremctl_mch_readreg(device_t dev, int reg, uint32_t *val)
339 {
340         struct coremctl_softc *sc = device_get_softc(dev);
341
342         if (sc->sc_mch == NULL)
343                 return EOPNOTSUPP;
344
345         *val = CSR_READ_4(sc, reg);
346         return 0;
347 }
348
349 static int
350 coremctl_pci_read_ivar(device_t dev, device_t child, int which,
351     uintptr_t *result)
352 {
353         return BUS_READ_IVAR(device_get_parent(dev), dev, which, result);
354 }
355
356 static uint32_t
357 coremctl_pci_read_config(device_t dev, device_t child, int reg, int width)
358 {
359         return pci_read_config(dev, reg, width);
360 }
361
362 static void
363 coremctl_pci_write_config(device_t dev, device_t child, int reg, uint32_t val,
364     int width)
365 {
366         pci_write_config(dev, reg, val, width);
367 }