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/bitops.h> 38 #include <sys/bus.h> 39 #include <sys/kernel.h> 40 #include <sys/malloc.h> 41 #include <sys/sensors.h> 42 #include <sys/sysctl.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 #include <dev/misc/ecc/ecc_e5_reg.h> 53 54 #define UBOX_READ(dev, ofs, w) \ 55 pcib_read_config((dev), pci_get_bus((dev)), \ 56 PCISLOT_E5_UBOX0, PCIFUNC_E5_UBOX0, (ofs), w) 57 #define UBOX_READ_2(dev, ofs) UBOX_READ((dev), (ofs), 2) 58 #define UBOX_READ_4(dev, ofs) UBOX_READ((dev), (ofs), 4) 59 60 #define IMC_CPGC_READ(dev, ofs, w) \ 61 pcib_read_config((dev), pci_get_bus((dev)), \ 62 PCISLOT_E5_IMC_CPGC, PCIFUNC_E5_IMC_CPGC, (ofs), w) 63 #define IMC_CPGC_READ_2(dev, ofs) IMC_CPGC_READ((dev), (ofs), 2) 64 #define IMC_CPGC_READ_4(dev, ofs) IMC_CPGC_READ((dev), (ofs), 4) 65 66 #define IMC_CTAD_READ(dev, c, ofs, w) \ 67 pcib_read_config((dev), pci_get_bus((dev)), \ 68 PCISLOT_E5_IMC_CTAD, PCIFUNC_E5_IMC_CTAD((c)), (ofs), w) 69 #define IMC_CTAD_READ_2(dev, c, ofs) IMC_CTAD_READ((dev), (c), (ofs), 2) 70 #define IMC_CTAD_READ_4(dev, c, ofs) IMC_CTAD_READ((dev), (c), (ofs), 4) 71 72 struct memtemp_e5_type { 73 uint16_t did; 74 int slot; 75 int func; 76 int chan; 77 const char *desc; 78 }; 79 80 struct memtemp_e5_softc; 81 82 struct memtemp_e5_dimm { 83 TAILQ_ENTRY(memtemp_e5_dimm) dimm_link; 84 struct ksensordev dimm_sensordev; 85 struct ksensor dimm_sensor; 86 struct memtemp_e5_softc *dimm_parent; 87 int dimm_id; 88 int dimm_extid; 89 }; 90 91 struct memtemp_e5_softc { 92 device_t temp_dev; 93 int temp_chan; 94 int temp_node; 95 TAILQ_HEAD(, memtemp_e5_dimm) temp_dimm; 96 }; 97 98 static int memtemp_e5_probe(device_t); 99 static int memtemp_e5_attach(device_t); 100 static int memtemp_e5_detach(device_t); 101 102 static void memtemp_e5_sensor_task(void *); 103 104 #define MEMTEMP_E5_TYPE_V2(c) \ 105 { \ 106 .did = PCI_E5_IMC_THERMAL_CHN##c##_DID_ID, \ 107 .slot = PCISLOT_E5_IMC_THERMAL, \ 108 .func = PCIFUNC_E5_IMC_THERMAL_CHN##c, \ 109 .chan = c, \ 110 .desc = "Intel E5 v2 memory thermal sensor" \ 111 } 112 113 #define MEMTEMP_E5_TYPE_END { 0, 0, 0, 0, NULL } 114 115 static const struct memtemp_e5_type memtemp_types[] = { 116 MEMTEMP_E5_TYPE_V2(0), 117 MEMTEMP_E5_TYPE_V2(1), 118 MEMTEMP_E5_TYPE_V2(2), 119 MEMTEMP_E5_TYPE_V2(3), 120 121 MEMTEMP_E5_TYPE_END 122 }; 123 124 #undef MEMTEMP_E5_TYPE_V2 125 #undef MEMTEMP_E5_TYPE_END 126 127 static device_method_t memtemp_e5_methods[] = { 128 /* Device interface */ 129 DEVMETHOD(device_probe, memtemp_e5_probe), 130 DEVMETHOD(device_attach, memtemp_e5_attach), 131 DEVMETHOD(device_detach, memtemp_e5_detach), 132 DEVMETHOD(device_shutdown, bus_generic_shutdown), 133 DEVMETHOD(device_suspend, bus_generic_suspend), 134 DEVMETHOD(device_resume, bus_generic_resume), 135 DEVMETHOD_END 136 }; 137 138 static driver_t memtemp_e5_driver = { 139 "memtemp", 140 memtemp_e5_methods, 141 sizeof(struct memtemp_e5_softc) 142 }; 143 static devclass_t memtemp_devclass; 144 DRIVER_MODULE(memtemp_e5, pci, memtemp_e5_driver, memtemp_devclass, NULL, NULL); 145 MODULE_DEPEND(memtemp_e5, pci, 1, 1, 1); 146 147 static int 148 memtemp_e5_probe(device_t dev) 149 { 150 const struct memtemp_e5_type *t; 151 uint16_t vid, did; 152 int slot, func; 153 154 vid = pci_get_vendor(dev); 155 if (vid != PCI_E5_VID_ID) 156 return ENXIO; 157 158 did = pci_get_device(dev); 159 slot = pci_get_slot(dev); 160 func = pci_get_function(dev); 161 162 for (t = memtemp_types; t->desc != NULL; ++t) { 163 if (t->did == did && t->slot == slot && t->func == func) { 164 struct memtemp_e5_softc *sc = device_get_softc(dev); 165 char desc[128]; 166 uint32_t val; 167 int node, dimm; 168 169 /* Check CPGC vid/did */ 170 if (IMC_CPGC_READ_2(dev, PCIR_VENDOR) != 171 PCI_E5_VID_ID || 172 IMC_CPGC_READ_2(dev, PCIR_DEVICE) != 173 PCI_E5_IMC_CPGC_DID_ID) 174 break; 175 176 /* Is this channel disabled */ 177 val = IMC_CPGC_READ_4(dev, PCI_E5_IMC_CPGC_MCMTR); 178 if (val & PCI_E5_IMC_CPGC_MCMTR_CHN_DISABLE(t->chan)) 179 break; 180 181 /* Check CTAD vid/did */ 182 if (IMC_CTAD_READ_2(dev, t->chan, PCIR_VENDOR) != 183 PCI_E5_VID_ID || 184 IMC_CTAD_READ_2(dev, t->chan, PCIR_DEVICE) != 185 PCI_E5_IMC_CTAD_DID_ID(t->chan)) 186 break; 187 188 /* Are there any DIMMs populated? */ 189 for (dimm = 0; dimm < PCI_E5_IMC_DIMM_MAX; ++dimm) { 190 val = IMC_CTAD_READ_4(dev, t->chan, 191 PCI_E5_IMC_CTAD_DIMMMTR(dimm)); 192 if (val & PCI_E5_IMC_CTAD_DIMMMTR_DIMM_POP) 193 break; 194 } 195 if (dimm == PCI_E5_IMC_DIMM_MAX) 196 break; 197 198 /* Check UBOX vid/did */ 199 if (UBOX_READ_2(dev, PCIR_VENDOR) != PCI_E5_VID_ID || 200 UBOX_READ_2(dev, PCIR_DEVICE) != 201 PCI_E5_UBOX0_DID_ID) 202 break; 203 204 val = UBOX_READ_4(dev, PCI_E5_UBOX0_CPUNODEID); 205 node = __SHIFTOUT(val, 206 PCI_E5_UBOX0_CPUNODEID_LCLNODEID); 207 208 ksnprintf(desc, sizeof(desc), "%s node%d channel%d", 209 t->desc, node, t->chan); 210 device_set_desc_copy(dev, desc); 211 212 sc->temp_chan = t->chan; 213 sc->temp_node = node; 214 215 return 0; 216 } 217 } 218 return ENXIO; 219 } 220 221 static int 222 memtemp_e5_attach(device_t dev) 223 { 224 struct memtemp_e5_softc *sc = device_get_softc(dev); 225 int dimm; 226 227 sc->temp_dev = dev; 228 TAILQ_INIT(&sc->temp_dimm); 229 230 for (dimm = 0; dimm < PCI_E5_IMC_DIMM_MAX; ++dimm) { 231 struct memtemp_e5_dimm *dimm_sc; 232 uint32_t dimmmtr; 233 234 dimmmtr = IMC_CTAD_READ_4(sc->temp_dev, sc->temp_chan, 235 PCI_E5_IMC_CTAD_DIMMMTR(dimm)); 236 237 if ((dimmmtr & PCI_E5_IMC_CTAD_DIMMMTR_DIMM_POP) == 0) 238 continue; 239 240 dimm_sc = kmalloc(sizeof(*dimm_sc), M_DEVBUF, 241 M_WAITOK | M_ZERO); 242 dimm_sc->dimm_id = dimm; 243 dimm_sc->dimm_parent = sc; 244 dimm_sc->dimm_extid = 245 (sc->temp_node * PCI_E5_IMC_CHN_MAX * PCI_E5_IMC_DIMM_MAX) + 246 (sc->temp_chan * PCI_E5_IMC_DIMM_MAX) + dimm; 247 248 ksnprintf(dimm_sc->dimm_sensordev.xname, 249 sizeof(dimm_sc->dimm_sensordev.xname), 250 "dimm%d", dimm_sc->dimm_extid); 251 dimm_sc->dimm_sensor.type = SENSOR_TEMP; 252 sensor_attach(&dimm_sc->dimm_sensordev, &dimm_sc->dimm_sensor); 253 if (sensor_task_register(dimm_sc, memtemp_e5_sensor_task, 2)) { 254 device_printf(sc->temp_dev, "DIMM%d sensor task " 255 "register failed\n", dimm); 256 kfree(dimm_sc, M_DEVBUF); 257 continue; 258 } 259 sensordev_install(&dimm_sc->dimm_sensordev); 260 261 TAILQ_INSERT_TAIL(&sc->temp_dimm, dimm_sc, dimm_link); 262 } 263 return 0; 264 } 265 266 static int 267 memtemp_e5_detach(device_t dev) 268 { 269 struct memtemp_e5_softc *sc = device_get_softc(dev); 270 struct memtemp_e5_dimm *dimm_sc; 271 272 while ((dimm_sc = TAILQ_FIRST(&sc->temp_dimm)) != NULL) { 273 TAILQ_REMOVE(&sc->temp_dimm, dimm_sc, dimm_link); 274 275 sensordev_deinstall(&dimm_sc->dimm_sensordev); 276 sensor_task_unregister(dimm_sc); 277 278 kfree(dimm_sc, M_DEVBUF); 279 } 280 return 0; 281 } 282 283 static void 284 memtemp_e5_sensor_task(void *xdimm_sc) 285 { 286 struct memtemp_e5_dimm *dimm_sc = xdimm_sc; 287 struct ksensor *sensor = &dimm_sc->dimm_sensor; 288 uint32_t val; 289 int temp; 290 291 val = pci_read_config(dimm_sc->dimm_parent->temp_dev, 292 PCI_E5_IMC_THERMAL_DIMMTEMPSTAT(dimm_sc->dimm_id), 4); 293 temp = __SHIFTOUT(val, PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMP); 294 295 sensor->flags &= ~SENSOR_FINVALID; 296 sensor->value = (temp * 1000000) + 273150000; 297 } 298