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/pci_cfgreg.h>
45
46 #include <vm/pmap.h>
47
48 #include "coremctl_if.h"
49 #include "pcib_if.h"
50
51 #include <dev/misc/coremctl/coremctl_reg.h>
52
53 #define COREMCTL_VER_1 1 /* Sandy Bridge */
54 #define COREMCTL_VER_2 2 /* Ivy Bridge */
55 #define COREMCTL_VER_3 3 /* Haswell */
56
57 struct coremctl_type {
58 uint16_t did;
59 const char *desc;
60 int ver; /* COREMCTL_VER_ */
61 };
62
63 struct coremctl_softc {
64 device_t sc_dev;
65 int sc_ver; /* COREMCTL_VER_ */
66 device_t sc_ecc;
67 device_t sc_temp;
68 volatile uint8_t *sc_mch;
69 };
70
71 #define CSR_READ_4(sc, ofs) \
72 (*(volatile uint32_t *)((sc)->sc_mch + (ofs)))
73 #define CSR_WRITE_4(sc, ofs, val) \
74 (*(volatile uint32_t *)((sc)->sc_mch + (ofs)) = (val))
75
76 static void coremctl_identify(driver_t *, device_t);
77 static int coremctl_probe(device_t);
78 static int coremctl_attach(device_t);
79 static int coremctl_detach(device_t);
80 static int coremctl_mch_readreg(device_t, int, uint32_t *);
81 static int coremctl_mch_writereg(device_t, int, uint32_t);
82 static int coremctl_pci_read_ivar(device_t, device_t, int, uintptr_t *);
83 static uint32_t coremctl_pci_read_config(device_t, device_t, int, int);
84 static void coremctl_pci_write_config(device_t, device_t, int, uint32_t,
85 int);
86
87 static void coremctl_chaninfo(struct coremctl_softc *, uint32_t,
88 const char *);
89
90 static const struct coremctl_type coremctl_types[] = {
91 { PCI_E3V1_MEMCTL_DID, "Intel E3 memory controller",
92 COREMCTL_VER_1 },
93
94 { PCI_E3V2_MEMCTL_DID, "Intel E3 v2 memory controller",
95 COREMCTL_VER_2 },
96
97 { PCI_E3V3_MEMCTL_DID, "Intel E3 v3 memory controller",
98 COREMCTL_VER_3 },
99
100 { PCI_COREV3_MEMCTL_DID, "Intel i3/i5/i7 Haswell memory controller",
101 COREMCTL_VER_3 },
102
103 { 0, NULL, 0 } /* required last entry */
104 };
105
106 static device_method_t coremctl_methods[] = {
107 /* Device interface */
108 DEVMETHOD(device_identify, coremctl_identify),
109 DEVMETHOD(device_probe, coremctl_probe),
110 DEVMETHOD(device_attach, coremctl_attach),
111 DEVMETHOD(device_detach, coremctl_detach),
112 DEVMETHOD(device_shutdown, bus_generic_shutdown),
113 DEVMETHOD(device_suspend, bus_generic_suspend),
114 DEVMETHOD(device_resume, bus_generic_resume),
115
116 /* Bus interface */
117 DEVMETHOD(bus_read_ivar, coremctl_pci_read_ivar),
118
119 /* PCI interface */
120 DEVMETHOD(pci_read_config, coremctl_pci_read_config),
121 DEVMETHOD(pci_write_config, coremctl_pci_write_config),
122
123 /* Core memory controller interface */
124 DEVMETHOD(coremctl_mch_read, coremctl_mch_readreg),
125 DEVMETHOD(coremctl_mch_write, coremctl_mch_writereg),
126
127 DEVMETHOD_END
128 };
129
130 static driver_t coremctl_driver = {
131 "coremctl",
132 coremctl_methods,
133 sizeof(struct coremctl_softc)
134 };
135 static devclass_t coremctl_devclass;
136
137 DRIVER_MODULE(coremctl, hostb, coremctl_driver, coremctl_devclass, NULL, NULL);
138 MODULE_VERSION(coremctl, 1);
139 MODULE_DEPEND(coremctl, pci, 1, 1, 1);
140
141 static void
coremctl_identify(driver_t * driver,device_t parent)142 coremctl_identify(driver_t *driver, device_t parent)
143 {
144 const struct coremctl_type *t;
145 uint16_t did;
146
147 /* Already identified */
148 if (device_find_child(parent, "coremctl", -1) != NULL)
149 return;
150
151 if (pci_get_vendor(parent) != PCI_CORE_MEMCTL_VID)
152 return;
153
154 did = pci_get_device(parent);
155 for (t = coremctl_types; t->desc != NULL; ++t) {
156 if (t->did == did) {
157 if (device_add_child(parent, "coremctl", -1) == NULL)
158 device_printf(parent, "add coremctl failed\n");
159 return;
160 }
161 }
162 }
163
164 static int
coremctl_probe(device_t dev)165 coremctl_probe(device_t dev)
166 {
167 const struct coremctl_type *t;
168 uint16_t did;
169
170 if (pci_get_vendor(dev) != PCI_CORE_MEMCTL_VID)
171 return ENXIO;
172
173 did = pci_get_device(dev);
174 for (t = coremctl_types; t->desc != NULL; ++t) {
175 if (t->did == did) {
176 struct coremctl_softc *sc = device_get_softc(dev);
177
178 device_set_desc(dev, t->desc);
179 sc->sc_ver = t->ver;
180 return 0;
181 }
182 }
183 return ENXIO;
184 }
185
186 static int
coremctl_attach(device_t dev)187 coremctl_attach(device_t dev)
188 {
189 struct coremctl_softc *sc = device_get_softc(dev);
190 uint32_t capa, dmfc, mch_barlo, mch_barhi;
191 uint64_t mch_bar;
192 int dmfc_parsed = 1;
193
194 sc->sc_dev = dev;
195
196 capa = pci_read_config(dev, PCI_CORE_CAPID0_A, 4);
197
198 if (sc->sc_ver == COREMCTL_VER_1) {
199 dmfc = __SHIFTOUT(capa, PCI_CORE_CAPID0_A_DMFC);
200 } else { /* v2/v3 */
201 uint32_t capb;
202
203 capb = pci_read_config(dev, PCI_CORE_CAPID0_B, 4);
204 dmfc = __SHIFTOUT(capb, PCI_CORE_CAPID0_B_DMFC);
205 }
206
207 if (dmfc == PCI_CORE_CAPID0_DMFC_1067) {
208 device_printf(dev, "CAP DDR3 1067 ");
209 } else if (dmfc == PCI_CORE_CAPID0_DMFC_1333) {
210 device_printf(dev, "CAP DDR3 1333 ");
211 } else {
212 if (sc->sc_ver == COREMCTL_VER_1) {
213 if (dmfc == PCI_CORE_CAPID0_DMFC_V1_ALL)
214 device_printf(dev, "no CAP ");
215 else
216 dmfc_parsed = 0;
217 } else { /* v2/v3 */
218 if (dmfc == PCI_CORE_CAPID0_DMFC_1600)
219 device_printf(dev, "CAP DDR3 1600 ");
220 else if (dmfc == PCI_CORE_CAPID0_DMFC_1867)
221 device_printf(dev, "CAP DDR3 1867 ");
222 else if (dmfc == PCI_CORE_CAPID0_DMFC_2133)
223 device_printf(dev, "CAP DDR3 2133 ");
224 else if (dmfc == PCI_CORE_CAPID0_DMFC_2400)
225 device_printf(dev, "CAP DDR3 2400 ");
226 else if (dmfc == PCI_CORE_CAPID0_DMFC_2667)
227 device_printf(dev, "CAP DDR3 2667 ");
228 else if (dmfc == PCI_CORE_CAPID0_DMFC_2933)
229 device_printf(dev, "CAP DDR3 2933 ");
230 else
231 dmfc_parsed = 0;
232 }
233 }
234 if (!dmfc_parsed) {
235 device_printf(dev, "unknown DMFC %#x\n", dmfc);
236 return 0;
237 }
238
239 if (capa & PCI_CORE_CAPID0_A_ECCDIS) {
240 kprintf("NON-ECC\n");
241 } else {
242 kprintf("ECC\n");
243 sc->sc_ecc = device_add_child(dev, "ecc", -1);
244 if (sc->sc_ecc == NULL)
245 device_printf(dev, "add ecc failed\n");
246 }
247
248 mch_barlo = pci_read_config(dev, PCI_CORE_MCHBAR_LO, 4);
249 mch_barhi = pci_read_config(dev, PCI_CORE_MCHBAR_HI, 4);
250
251 mch_bar = (uint64_t)mch_barlo | (((uint64_t)mch_barhi) << 32);
252 if (bootverbose)
253 device_printf(dev, "MCHBAR 0x%jx\n", (uintmax_t)mch_bar);
254
255 if (mch_bar & PCI_CORE_MCHBAR_LO_EN) {
256 uint64_t map_addr = mch_bar & PCI_CORE_MCHBAR_ADDRMASK;
257
258 sc->sc_mch = pmap_mapdev_uncacheable(map_addr, MCH_CORE_SIZE);
259
260 if (bootverbose) {
261 uint32_t dimm_ch0, dimm_ch1;
262
263 dimm_ch0 = CSR_READ_4(sc, MCH_CORE_DIMM_CH0);
264 dimm_ch1 = CSR_READ_4(sc, MCH_CORE_DIMM_CH1);
265
266 coremctl_chaninfo(sc, dimm_ch0, "channel0");
267 coremctl_chaninfo(sc, dimm_ch1, "channel1");
268 }
269 } else {
270 device_printf(dev, "MCHBAR is not enabled\n");
271 }
272
273 if (sc->sc_ver == COREMCTL_VER_3 && sc->sc_mch != NULL) {
274 uint32_t ptm_ctl;
275
276 /*
277 * XXX
278 * It seems that memory thermal sensor is available,
279 * if any of the following bits are set.
280 */
281 ptm_ctl = CSR_READ_4(sc, MCH_CORE_DDR_PTM_CTL0);
282 if (ptm_ctl & (MCH_CORE_DDR_PTM_CTL0_CLTM |
283 MCH_CORE_DDR_PTM_CTL0_EXTTS | MCH_CORE_DDR_PTM_CTL0_OLTM)) {
284 sc->sc_temp = device_add_child(dev, "memtemp", -1);
285 if (sc->sc_temp == NULL)
286 device_printf(dev, "add memtemp failed\n");
287 }
288 }
289
290 bus_generic_attach(dev);
291
292 return 0;
293 }
294
295 static void
coremctl_chaninfo(struct coremctl_softc * sc,uint32_t dimm_ch,const char * desc)296 coremctl_chaninfo(struct coremctl_softc *sc, uint32_t dimm_ch,
297 const char *desc)
298 {
299 int size_a, size_b;
300 int dimma_id, dimmb_id;
301
302 dimma_id = 0;
303 dimmb_id = 1;
304 if (dimm_ch & MCH_CORE_DIMM_A_SELECT) {
305 dimma_id = 1;
306 dimmb_id = 0;
307 }
308
309 size_a = __SHIFTOUT(dimm_ch, MCH_CORE_DIMM_A_SIZE);
310 if (size_a != 0) {
311 device_printf(sc->sc_dev, "%s, DIMM%d %dMB %dx%d\n", desc,
312 dimma_id, size_a * MCH_CORE_DIMM_SIZE_UNIT,
313 (dimm_ch & MCH_CORE_DIMM_A_DUAL_RANK) ? 2 : 1,
314 (dimm_ch & MCH_CORE_DIMM_A_X16) ? 16 : 8);
315 }
316
317 size_b = __SHIFTOUT(dimm_ch, MCH_CORE_DIMM_B_SIZE);
318 if (size_b != 0) {
319 device_printf(sc->sc_dev, "%s, DIMM%d %dMB %dx%d\n", desc,
320 dimmb_id, size_b * MCH_CORE_DIMM_SIZE_UNIT,
321 (dimm_ch & MCH_CORE_DIMM_B_DUAL_RANK) ? 2 : 1,
322 (dimm_ch & MCH_CORE_DIMM_B_X16) ? 16 : 8);
323 }
324
325 if (size_a == 0 && size_b == 0)
326 return;
327
328 if (sc->sc_ver == COREMCTL_VER_1 || sc->sc_ver == COREMCTL_VER_2) {
329 /* This bit is v3 only */
330 dimm_ch &= ~MCH_CORE_DIMM_HORI;
331 }
332 if (dimm_ch & (MCH_CORE_DIMM_ENHI | MCH_CORE_DIMM_RI |
333 MCH_CORE_DIMM_HORI)) {
334 device_printf(sc->sc_dev, "%s", desc);
335 if (dimm_ch & MCH_CORE_DIMM_RI)
336 kprintf(", rank interleave");
337 if (dimm_ch & MCH_CORE_DIMM_ENHI)
338 kprintf(", enhanced interleave");
339 if (dimm_ch & MCH_CORE_DIMM_HORI)
340 kprintf(", high order rank interleave");
341 kprintf("\n");
342 }
343 }
344
345 static int
coremctl_detach(device_t dev)346 coremctl_detach(device_t dev)
347 {
348 struct coremctl_softc *sc = device_get_softc(dev);
349
350 if (sc->sc_ecc != NULL)
351 device_delete_child(dev, sc->sc_ecc);
352 if (sc->sc_temp != NULL)
353 device_delete_child(dev, sc->sc_temp);
354 bus_generic_detach(dev);
355
356 if (sc->sc_mch != NULL)
357 pmap_unmapdev((vm_offset_t)sc->sc_mch, MCH_CORE_SIZE);
358 return 0;
359 }
360
361 static int
coremctl_mch_readreg(device_t dev,int reg,uint32_t * val)362 coremctl_mch_readreg(device_t dev, int reg, uint32_t *val)
363 {
364 struct coremctl_softc *sc = device_get_softc(dev);
365
366 if (sc->sc_mch == NULL)
367 return EOPNOTSUPP;
368
369 *val = CSR_READ_4(sc, reg);
370 return 0;
371 }
372
373 static int
coremctl_mch_writereg(device_t dev,int reg,uint32_t val)374 coremctl_mch_writereg(device_t dev, int reg, uint32_t val)
375 {
376 struct coremctl_softc *sc = device_get_softc(dev);
377
378 if (sc->sc_mch == NULL)
379 return EOPNOTSUPP;
380
381 CSR_WRITE_4(sc, reg, val);
382 return 0;
383 }
384
385 static int
coremctl_pci_read_ivar(device_t dev,device_t child,int which,uintptr_t * result)386 coremctl_pci_read_ivar(device_t dev, device_t child, int which,
387 uintptr_t *result)
388 {
389 return BUS_READ_IVAR(device_get_parent(dev), dev, which, result);
390 }
391
392 static uint32_t
coremctl_pci_read_config(device_t dev,device_t child,int reg,int width)393 coremctl_pci_read_config(device_t dev, device_t child, int reg, int width)
394 {
395 return pci_read_config(dev, reg, width);
396 }
397
398 static void
coremctl_pci_write_config(device_t dev,device_t child,int reg,uint32_t val,int width)399 coremctl_pci_write_config(device_t dev, device_t child, int reg, uint32_t val,
400 int width)
401 {
402 pci_write_config(dev, reg, val, width);
403 }
404