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