xref: /dflybsd-src/sys/dev/powermng/memtemp/memtemp_e5.c (revision acbc630d902fb9fc35d9b4ac0944b2edfb1ec681)
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