xref: /haiku/src/add-ons/kernel/drivers/power/pch_thermal/pch_thermal.cpp (revision e304f35430187430902fcf5ed57be21f6e0cc301)
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
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
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
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
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
166 pch_thermal_close (void* cookie)
167 {
168 	return B_OK;
169 }
170 
171 
172 static status_t
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
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, 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
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
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
243 pch_thermal_uninit_driver(void *driverCookie)
244 {
245 }
246 
247 
248 static status_t
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
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 	physicalAddress &= PCI_address_memory_32_mask;
294 	if ((info.u.h0.base_register_flags[0] & 0xc) == PCI_address_type_64)
295 		physicalAddress += (phys_addr_t)info.u.h0.base_registers[1] << 32;
296 
297 	size_t mapSize = info.u.h0.base_register_sizes[0];
298 
299 	AreaKeeper mmioMapper;
300 	device->registers_area = mmioMapper.Map("intel PCH thermal mmio",
301 		physicalAddress, mapSize, B_ANY_KERNEL_ADDRESS,
302 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&device->registers);
303 
304 	if (mmioMapper.InitCheck() < B_OK) {
305 		ERROR("could not map memory I/O!\n");
306 		status = device->registers_area;
307 		goto err;
308 	}
309 
310 	{
311 		bool enabled = false;
312 		uint8 tsel = read8(device->registers + PCH_THERMAL_TSEL);
313 		if ((tsel & PCH_THERMAL_TSEL_ETS) != 0)
314 			enabled = true;
315 		if (!enabled) {
316 			if ((tsel & PCH_THERMAL_TSEL_PLDB) != 0) {
317 				status = B_DEVICE_NOT_FOUND;
318 				goto err;
319 			}
320 
321 			write8(device->registers + PCH_THERMAL_TSEL,
322 				tsel | PCH_THERMAL_TSEL_ETS);
323 
324 			if ((tsel & PCH_THERMAL_TSEL_ETS) == 0) {
325 				status = B_DEVICE_NOT_FOUND;
326 				goto err;
327 			}
328 		}
329 
330 		// read config temperatures
331 		uint16 ctt = read16(device->registers + PCH_THERMAL_CTT);
332 		ctt = (ctt >> PCH_THERMAL_CTT_CTRIP_SHIFT) & PCH_THERMAL_CTT_CTRIP_MASK;
333 		device->criticalTemp = (ctt != 0) ? (uint32)ctt * 10 / 2 - 500 : 0;
334 
335 		uint16 phl = read16(device->registers + PCH_THERMAL_PHL);
336 		phl = (phl >> PCH_THERMAL_PHL_PHLL_SHIFT) & PCH_THERMAL_PHL_PHLL_MASK;
337 		device->hotTemp = (phl != 0) ? (uint32)phl * 10 / 2 - 500 : 0;
338 	}
339 	TRACE("pch_thermal_init_device %p\n", device);
340 
341 	mmioMapper.Detach();
342 	*cookie = device;
343 	return B_OK;
344 
345 err:
346 	free(device);
347 	return status;
348 }
349 
350 
351 static void
352 pch_thermal_uninit_device(void *_cookie)
353 {
354 	pch_thermal_device_info *device = (pch_thermal_device_info *)_cookie;
355 	TRACE("pch_thermal_uninit_device %p\n", device);
356 	delete_area(device->registers_area);
357 	free(device);
358 }
359 
360 
361 
362 module_dependency module_dependencies[] = {
363 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
364 	{}
365 };
366 
367 
368 driver_module_info pch_thermal_driver_module = {
369 	{
370 		PCH_THERMAL_MODULE_NAME,
371 		0,
372 		NULL
373 	},
374 
375 	pch_thermal_support,
376 	pch_thermal_register_device,
377 	pch_thermal_init_driver,
378 	pch_thermal_uninit_driver,
379 	pch_thermal_register_child_devices,
380 	NULL,	// rescan
381 	NULL,	// removed
382 };
383 
384 
385 struct device_module_info pch_thermal_device_module = {
386 	{
387 		PCH_THERMAL_DEVICE_MODULE_NAME,
388 		0,
389 		NULL
390 	},
391 
392 	pch_thermal_init_device,
393 	pch_thermal_uninit_device,
394 	NULL,
395 
396 	pch_thermal_open,
397 	pch_thermal_close,
398 	pch_thermal_free,
399 	pch_thermal_read,
400 	pch_thermal_write,
401 	NULL,
402 	pch_thermal_control,
403 
404 	NULL,
405 	NULL
406 };
407 
408 module_info *modules[] = {
409 	(module_info *)&pch_thermal_driver_module,
410 	(module_info *)&pch_thermal_device_module,
411 	NULL
412 };
413