xref: /haiku/src/add-ons/kernel/drivers/power/pch_thermal/pch_thermal.cpp (revision 7713305bd28f173f1e84e7a68459db64c478f1a0)
1 /*
2  * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com.
3  *
4  * Distributed under the terms of the MIT License.
5  *
6  * PCH Thermal driver.
7  */
8 
9 
10 #include <AreaKeeper.h>
11 #include <Drivers.h>
12 #include <Errors.h>
13 #include <KernelExport.h>
14 #include <PCI.h>
15 #include <bus/PCI.h>
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include "pch_thermal.h"
22 
23 
24 #define PCH_THERMAL_MODULE_NAME "drivers/power/pch_thermal/driver_v1"
25 
26 #define PCH_THERMAL_DEVICE_MODULE_NAME "drivers/power/pch_thermal/device_v1"
27 
28 /* Base Namespace devices are published to */
29 #define PCH_THERMAL_BASENAME "power/pch_thermal/%d"
30 
31 // name of pnp generator of path ids
32 #define PCH_THERMAL_PATHID_GENERATOR "pch_thermal/path_id"
33 
34 static device_manager_info *sDeviceManager;
35 
36 
37 //#define TRACE_PCH_THERMAL
38 #ifdef TRACE_PCH_THERMAL
39 #	define TRACE(x...) dprintf("pch_thermal: " x)
40 #else
41 #	define TRACE(x...)
42 #endif
43 #define ERROR(x...)	dprintf("pch_thermal: " x)
44 
45 
46 #define write8(address, data) \
47 	(*((volatile uint8*)(address)) = (data))
48 #define read8(address) \
49 	(*((volatile uint8*)(address)))
50 #define write16(address, data) \
51 	(*((volatile uint16*)(address)) = (data))
52 #define read16(address) \
53 	(*((volatile uint16*)(address)))
54 #define write32(address, data) \
55 	(*((volatile uint32*)(address)) = (data))
56 #define read32(address) \
57 	(*((volatile uint32*)(address)))
58 
59 
60 typedef struct pch_thermal_device_info {
61 	device_node *node;
62 	pci_device_module_info *pci;
63 	pci_device *pci_cookie;
64 
65 	addr_t		registers;
66 	area_id		registers_area;
67 
68 	uint32	criticalTemp;
69 	uint32	hotTemp;
70 	uint32	passiveTemp;
71 } pch_thermal_device_info;
72 
73 
74 status_t pch_thermal_control(void* _cookie, uint32 op, void* arg, size_t len);
75 
76 
77 static status_t
pch_thermal_open(void * _cookie,const char * path,int flags,void ** cookie)78 pch_thermal_open(void *_cookie, const char *path, int flags, void** cookie)
79 {
80 	pch_thermal_device_info *device = (pch_thermal_device_info *)_cookie;
81 	TRACE("pch_thermal_open %p\n", device);
82 	*cookie = device;
83 	return B_OK;
84 }
85 
86 
87 static status_t
pch_thermal_read(void * _cookie,off_t position,void * buf,size_t * num_bytes)88 pch_thermal_read(void* _cookie, off_t position, void *buf, size_t* num_bytes)
89 {
90 	pch_thermal_device_info* device = (pch_thermal_device_info*)_cookie;
91 	TRACE("pch_thermal_read %p\n", device);
92 	pch_thermal_type therm_info;
93 	if (*num_bytes < 1)
94 		return B_IO_ERROR;
95 
96 	if (position == 0) {
97 		size_t max_len = *num_bytes;
98 		char *str = (char *)buf;
99 		TRACE("pch_thermal: read()\n");
100 		pch_thermal_control(device, drvOpGetThermalType, &therm_info, 0);
101 
102 		snprintf(str, max_len, "  Critical Temperature: %" B_PRIu32 ".%"
103 			B_PRIu32 " C\n", (therm_info.critical_temp / 10),
104 			(therm_info.critical_temp % 10));
105 
106 		max_len -= strlen(str);
107 		str += strlen(str);
108 		snprintf(str, max_len, "  Current Temperature: %" B_PRIu32 ".%"
109 			B_PRIu32 " C\n", (therm_info.current_temp / 10),
110 			(therm_info.current_temp % 10));
111 
112 		if (therm_info.hot_temp > 0) {
113 			max_len -= strlen(str);
114 			str += strlen(str);
115 			snprintf(str, max_len, "  Hot Temperature: %" B_PRIu32 ".%"
116 				B_PRIu32 " C\n", (therm_info.hot_temp / 10),
117 				(therm_info.hot_temp % 10));
118 		}
119 		*num_bytes = strlen((char *)buf);
120 	} else {
121 		*num_bytes = 0;
122 	}
123 
124 	return B_OK;
125 }
126 
127 
128 static status_t
pch_thermal_write(void * cookie,off_t position,const void * buffer,size_t * num_bytes)129 pch_thermal_write(void* cookie, off_t position, const void* buffer,
130 	size_t* num_bytes)
131 {
132 	return B_ERROR;
133 }
134 
135 
136 status_t
pch_thermal_control(void * _cookie,uint32 op,void * arg,size_t len)137 pch_thermal_control(void* _cookie, uint32 op, void* arg, size_t len)
138 {
139 	pch_thermal_device_info* device = (pch_thermal_device_info*)_cookie;
140 	status_t err = B_ERROR;
141 
142 	pch_thermal_type *att = NULL;
143 
144 	switch (op) {
145 		case drvOpGetThermalType: {
146 			att = (pch_thermal_type *)arg;
147 
148 			// Read basic temperature thresholds.
149 			att->critical_temp = device->criticalTemp;
150 			att->hot_temp = device->hotTemp;
151 
152 			uint16 temp = read16(device->registers + PCH_THERMAL_TEMP);
153 			temp = (temp >> PCH_THERMAL_TEMP_TSR_SHIFT)
154 				& PCH_THERMAL_TEMP_TSR_MASK;
155 			att->current_temp = (uint32)temp * 10 / 2 - 500;
156 
157 			err = B_OK;
158 			break;
159 		}
160 	}
161 	return err;
162 }
163 
164 
165 static status_t
pch_thermal_close(void * cookie)166 pch_thermal_close (void* cookie)
167 {
168 	return B_OK;
169 }
170 
171 
172 static status_t
pch_thermal_free(void * cookie)173 pch_thermal_free (void* cookie)
174 {
175 	return B_OK;
176 }
177 
178 
179 //	#pragma mark - driver module API
180 
181 
182 static float
pch_thermal_support(device_node * parent)183 pch_thermal_support(device_node *parent)
184 {
185 	const char *bus;
186 
187 	// make sure parent is really the PCI bus manager
188 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
189 		return -1;
190 
191 	if (strcmp(bus, "pci"))
192 		return 0.0;
193 
194 	uint16 vendorID;
195 	uint16 deviceID;
196 
197 	// get vendor and device ID
198 	if (sDeviceManager->get_attr_uint16(parent, B_DEVICE_VENDOR_ID, &vendorID,
199 			false) != B_OK
200 		|| sDeviceManager->get_attr_uint16(parent, B_DEVICE_ID, &deviceID,
201 			false) != B_OK) {
202 		return -1;
203 	}
204 
205 	// check whether it's really a PCH thermal device
206 	if (vendorID != 0x8086)
207 		return 0.0;
208 
209 	const uint16 devices[] = { 0x9c24, 0x8c24, 0x9ca4, 0x9d31, 0xa131, 0x9df9,
210 		0xa379, 0x06f9, 0x02f9, 0xa1b1, 0x8d24, 0 };
211 	for (const uint16* device = devices; *device != 0; device++) {
212 		if (*device == deviceID)
213 			return 0.6;
214 	}
215 
216 	return 0.0;
217 }
218 
219 
220 static status_t
pch_thermal_register_device(device_node * node)221 pch_thermal_register_device(device_node *node)
222 {
223 	device_attr attrs[] = {
224 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "PCH Thermal" }},
225 		{ NULL }
226 	};
227 
228 	return sDeviceManager->register_node(node, PCH_THERMAL_MODULE_NAME, attrs,
229 		NULL, NULL);
230 }
231 
232 
233 static status_t
pch_thermal_init_driver(device_node * node,void ** _driverCookie)234 pch_thermal_init_driver(device_node *node, void **_driverCookie)
235 {
236 	*_driverCookie = node;
237 
238 	return B_OK;
239 }
240 
241 
242 static void
pch_thermal_uninit_driver(void * driverCookie)243 pch_thermal_uninit_driver(void *driverCookie)
244 {
245 }
246 
247 
248 static status_t
pch_thermal_register_child_devices(void * _cookie)249 pch_thermal_register_child_devices(void *_cookie)
250 {
251 	device_node *node = (device_node*)_cookie;
252 	int path_id;
253 	char name[128];
254 
255 	path_id = sDeviceManager->create_id(PCH_THERMAL_PATHID_GENERATOR);
256 	if (path_id < 0) {
257 		ERROR("pch_thermal_register_child_devices: couldn't create a path_id"
258 			"\n");
259 		return B_ERROR;
260 	}
261 
262 	snprintf(name, sizeof(name), PCH_THERMAL_BASENAME, path_id);
263 
264 	return sDeviceManager->publish_device(node, name,
265 		PCH_THERMAL_DEVICE_MODULE_NAME);
266 }
267 
268 
269 static status_t
pch_thermal_init_device(void * _cookie,void ** cookie)270 pch_thermal_init_device(void *_cookie, void **cookie)
271 {
272 	device_node *node = (device_node *)_cookie;
273 	pch_thermal_device_info *device;
274 	device_node *parent;
275 	status_t status = B_NO_INIT;
276 
277 	device = (pch_thermal_device_info *)calloc(1, sizeof(*device));
278 	if (device == NULL)
279 		return B_NO_MEMORY;
280 
281 	device->node = node;
282 
283 	parent = sDeviceManager->get_parent_node(node);
284 	sDeviceManager->get_driver(parent, (driver_module_info **)&device->pci,
285 		(void **)&device->pci_cookie);
286 	sDeviceManager->put_node(parent);
287 
288 	struct pci_info info;
289 	device->pci->get_pci_info(device->pci_cookie, &info);
290 
291 	// map the registers (low + high for 64-bit when requested)
292 	phys_addr_t physicalAddress = info.u.h0.base_registers[0];
293 	if ((info.u.h0.base_register_flags[0] & PCI_address_type)
294 			== PCI_address_type_64) {
295 		physicalAddress |= (uint64)info.u.h0.base_registers[1] << 32;
296 	}
297 
298 	size_t mapSize = info.u.h0.base_register_sizes[0];
299 
300 	AreaKeeper mmioMapper;
301 	device->registers_area = mmioMapper.Map("intel PCH thermal mmio",
302 		physicalAddress, mapSize, B_ANY_KERNEL_ADDRESS,
303 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&device->registers);
304 
305 	if (mmioMapper.InitCheck() < B_OK) {
306 		ERROR("could not map memory I/O!\n");
307 		status = device->registers_area;
308 		goto err;
309 	}
310 
311 	{
312 		bool enabled = false;
313 		uint8 tsel = read8(device->registers + PCH_THERMAL_TSEL);
314 		if ((tsel & PCH_THERMAL_TSEL_ETS) != 0)
315 			enabled = true;
316 		if (!enabled) {
317 			if ((tsel & PCH_THERMAL_TSEL_PLDB) != 0) {
318 				status = B_DEVICE_NOT_FOUND;
319 				goto err;
320 			}
321 
322 			write8(device->registers + PCH_THERMAL_TSEL,
323 				tsel | PCH_THERMAL_TSEL_ETS);
324 
325 			if ((tsel & PCH_THERMAL_TSEL_ETS) == 0) {
326 				status = B_DEVICE_NOT_FOUND;
327 				goto err;
328 			}
329 		}
330 
331 		// read config temperatures
332 		uint16 ctt = read16(device->registers + PCH_THERMAL_CTT);
333 		ctt = (ctt >> PCH_THERMAL_CTT_CTRIP_SHIFT) & PCH_THERMAL_CTT_CTRIP_MASK;
334 		device->criticalTemp = (ctt != 0) ? (uint32)ctt * 10 / 2 - 500 : 0;
335 
336 		uint16 phl = read16(device->registers + PCH_THERMAL_PHL);
337 		phl = (phl >> PCH_THERMAL_PHL_PHLL_SHIFT) & PCH_THERMAL_PHL_PHLL_MASK;
338 		device->hotTemp = (phl != 0) ? (uint32)phl * 10 / 2 - 500 : 0;
339 	}
340 	TRACE("pch_thermal_init_device %p\n", device);
341 
342 	mmioMapper.Detach();
343 	*cookie = device;
344 	return B_OK;
345 
346 err:
347 	free(device);
348 	return status;
349 }
350 
351 
352 static void
pch_thermal_uninit_device(void * _cookie)353 pch_thermal_uninit_device(void *_cookie)
354 {
355 	pch_thermal_device_info *device = (pch_thermal_device_info *)_cookie;
356 	TRACE("pch_thermal_uninit_device %p\n", device);
357 	delete_area(device->registers_area);
358 	free(device);
359 }
360 
361 
362 
363 module_dependency module_dependencies[] = {
364 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
365 	{}
366 };
367 
368 
369 driver_module_info pch_thermal_driver_module = {
370 	{
371 		PCH_THERMAL_MODULE_NAME,
372 		0,
373 		NULL
374 	},
375 
376 	pch_thermal_support,
377 	pch_thermal_register_device,
378 	pch_thermal_init_driver,
379 	pch_thermal_uninit_driver,
380 	pch_thermal_register_child_devices,
381 	NULL,	// rescan
382 	NULL,	// removed
383 };
384 
385 
386 struct device_module_info pch_thermal_device_module = {
387 	{
388 		PCH_THERMAL_DEVICE_MODULE_NAME,
389 		0,
390 		NULL
391 	},
392 
393 	pch_thermal_init_device,
394 	pch_thermal_uninit_device,
395 	NULL,
396 
397 	pch_thermal_open,
398 	pch_thermal_close,
399 	pch_thermal_free,
400 	pch_thermal_read,
401 	pch_thermal_write,
402 	NULL,
403 	pch_thermal_control,
404 
405 	NULL,
406 	NULL
407 };
408 
409 module_info *modules[] = {
410 	(module_info *)&pch_thermal_driver_module,
411 	(module_info *)&pch_thermal_device_module,
412 	NULL
413 };
414