xref: /dragonfly/sys/dev/misc/coremctl/coremctl.c (revision 0c1d7dca433e727c476aff53acb839b357a28ef6)
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