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/e5_imc_reg.h> 53 #include <dev/misc/ecc/e5_imc_var.h> 54 55 struct memtemp_e5_softc; 56 57 struct memtemp_e5_dimm { 58 TAILQ_ENTRY(memtemp_e5_dimm) dimm_link; 59 struct ksensordev dimm_sensordev; 60 struct ksensor dimm_sensor; 61 struct memtemp_e5_softc *dimm_parent; 62 int dimm_id; 63 int dimm_extid; 64 }; 65 66 struct memtemp_e5_softc { 67 device_t temp_dev; 68 const struct e5_imc_chan *temp_chan; 69 int temp_node; 70 TAILQ_HEAD(, memtemp_e5_dimm) temp_dimm; 71 }; 72 73 static int memtemp_e5_probe(device_t); 74 static int memtemp_e5_attach(device_t); 75 static int memtemp_e5_detach(device_t); 76 77 static void memtemp_e5_sensor_task(void *); 78 79 #define MEMTEMP_E5_CHAN(v, imc, c, c_ext) \ 80 { \ 81 .did = PCI_E5V##v##_IMC##imc##_THERMAL_CHN##c##_DID_ID, \ 82 .slot = PCISLOT_E5V##v##_IMC##imc##_THERMAL_CHN##c, \ 83 .func = PCIFUNC_E5V##v##_IMC##imc##_THERMAL_CHN##c, \ 84 .desc = "Intel E5 v" #v " memory thermal sensor", \ 85 \ 86 E5_IMC_CHAN_FIELDS(v, imc, c, c_ext) \ 87 } 88 89 #define MEMTEMP_E5_CHAN_V2(c) MEMTEMP_E5_CHAN(2, 0, c, c) 90 #define MEMTEMP_E5_CHAN_IMC0_V3(c) MEMTEMP_E5_CHAN(3, 0, c, c) 91 #define MEMTEMP_E5_CHAN_IMC1_V3(c, c_ext) \ 92 MEMTEMP_E5_CHAN(3, 1, c, c_ext) 93 #define MEMTEMP_E5_CHAN_END E5_IMC_CHAN_END 94 95 static const struct e5_imc_chan memtemp_e5_chans[] = { 96 MEMTEMP_E5_CHAN_V2(0), 97 MEMTEMP_E5_CHAN_V2(1), 98 MEMTEMP_E5_CHAN_V2(2), 99 MEMTEMP_E5_CHAN_V2(3), 100 101 MEMTEMP_E5_CHAN_IMC0_V3(0), 102 MEMTEMP_E5_CHAN_IMC0_V3(1), 103 MEMTEMP_E5_CHAN_IMC0_V3(2), 104 MEMTEMP_E5_CHAN_IMC0_V3(3), 105 MEMTEMP_E5_CHAN_IMC1_V3(0, 2), /* IMC1 chan0 -> channel2 */ 106 MEMTEMP_E5_CHAN_IMC1_V3(1, 3), /* IMC1 chan1 -> channel3 */ 107 108 MEMTEMP_E5_CHAN_END 109 }; 110 111 #undef MEMTEMP_E5_CHAN_END 112 #undef MEMTEMP_E5_CHAN_V2 113 #undef MEMTEMP_E5_CHAN 114 115 static device_method_t memtemp_e5_methods[] = { 116 /* Device interface */ 117 DEVMETHOD(device_probe, memtemp_e5_probe), 118 DEVMETHOD(device_attach, memtemp_e5_attach), 119 DEVMETHOD(device_detach, memtemp_e5_detach), 120 DEVMETHOD(device_shutdown, bus_generic_shutdown), 121 DEVMETHOD(device_suspend, bus_generic_suspend), 122 DEVMETHOD(device_resume, bus_generic_resume), 123 DEVMETHOD_END 124 }; 125 126 static driver_t memtemp_e5_driver = { 127 "memtemp", 128 memtemp_e5_methods, 129 sizeof(struct memtemp_e5_softc) 130 }; 131 static devclass_t memtemp_devclass; 132 DRIVER_MODULE(memtemp_e5, pci, memtemp_e5_driver, memtemp_devclass, NULL, NULL); 133 MODULE_DEPEND(memtemp_e5, pci, 1, 1, 1); 134 135 static int 136 memtemp_e5_probe(device_t dev) 137 { 138 const struct e5_imc_chan *c; 139 uint16_t vid, did; 140 int slot, func; 141 142 vid = pci_get_vendor(dev); 143 if (vid != PCI_E5_IMC_VID_ID) 144 return ENXIO; 145 146 did = pci_get_device(dev); 147 slot = pci_get_slot(dev); 148 func = pci_get_function(dev); 149 150 for (c = memtemp_e5_chans; c->desc != NULL; ++c) { 151 if (c->did == did && c->slot == slot && c->func == func) { 152 struct memtemp_e5_softc *sc = device_get_softc(dev); 153 char desc[128]; 154 int node; 155 156 node = e5_imc_node_probe(dev, c); 157 if (node < 0) 158 break; 159 160 ksnprintf(desc, sizeof(desc), "%s node%d channel%d", 161 c->desc, node, c->chan_ext); 162 device_set_desc_copy(dev, desc); 163 164 sc->temp_chan = c; 165 sc->temp_node = node; 166 167 return 0; 168 } 169 } 170 return ENXIO; 171 } 172 173 static int 174 memtemp_e5_attach(device_t dev) 175 { 176 struct memtemp_e5_softc *sc = device_get_softc(dev); 177 int dimm; 178 179 sc->temp_dev = dev; 180 TAILQ_INIT(&sc->temp_dimm); 181 182 for (dimm = 0; dimm < PCI_E5_IMC_CHN_DIMM_MAX; ++dimm) { 183 struct memtemp_e5_dimm *dimm_sc; 184 uint32_t dimmmtr; 185 186 dimmmtr = IMC_CTAD_READ_4(sc->temp_dev, sc->temp_chan, 187 PCI_E5_IMC_CTAD_DIMMMTR(dimm)); 188 189 if ((dimmmtr & PCI_E5_IMC_CTAD_DIMMMTR_DIMM_POP) == 0) 190 continue; 191 192 dimm_sc = kmalloc(sizeof(*dimm_sc), M_DEVBUF, 193 M_WAITOK | M_ZERO); 194 dimm_sc->dimm_id = dimm; 195 dimm_sc->dimm_parent = sc; 196 dimm_sc->dimm_extid = 197 (sc->temp_node * PCI_E5_IMC_CHN_MAX * PCI_E5_IMC_CHN_DIMM_MAX) + 198 (sc->temp_chan->chan_ext * PCI_E5_IMC_CHN_DIMM_MAX) + dimm; 199 200 ksnprintf(dimm_sc->dimm_sensordev.xname, 201 sizeof(dimm_sc->dimm_sensordev.xname), 202 "dimm%d", dimm_sc->dimm_extid); 203 dimm_sc->dimm_sensor.type = SENSOR_TEMP; 204 sensor_attach(&dimm_sc->dimm_sensordev, &dimm_sc->dimm_sensor); 205 if (sensor_task_register(dimm_sc, memtemp_e5_sensor_task, 2)) { 206 device_printf(sc->temp_dev, "DIMM%d sensor task " 207 "register failed\n", dimm); 208 kfree(dimm_sc, M_DEVBUF); 209 continue; 210 } 211 sensordev_install(&dimm_sc->dimm_sensordev); 212 213 TAILQ_INSERT_TAIL(&sc->temp_dimm, dimm_sc, dimm_link); 214 } 215 return 0; 216 } 217 218 static int 219 memtemp_e5_detach(device_t dev) 220 { 221 struct memtemp_e5_softc *sc = device_get_softc(dev); 222 struct memtemp_e5_dimm *dimm_sc; 223 224 while ((dimm_sc = TAILQ_FIRST(&sc->temp_dimm)) != NULL) { 225 TAILQ_REMOVE(&sc->temp_dimm, dimm_sc, dimm_link); 226 227 sensordev_deinstall(&dimm_sc->dimm_sensordev); 228 sensor_task_unregister(dimm_sc); 229 230 kfree(dimm_sc, M_DEVBUF); 231 } 232 return 0; 233 } 234 235 static void 236 memtemp_e5_sensor_task(void *xdimm_sc) 237 { 238 struct memtemp_e5_dimm *dimm_sc = xdimm_sc; 239 struct ksensor *sensor = &dimm_sc->dimm_sensor; 240 uint32_t val; 241 int temp; 242 243 val = pci_read_config(dimm_sc->dimm_parent->temp_dev, 244 PCI_E5_IMC_THERMAL_DIMMTEMPSTAT(dimm_sc->dimm_id), 4); 245 temp = __SHIFTOUT(val, PCI_E5_IMC_THERMAL_DIMMTEMPSTAT_TEMP); 246 247 sensor->flags &= ~SENSOR_FINVALID; 248 sensor->value = (temp * 1000000) + 273150000; 249 } 250