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
34static 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
60typedef 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
74status_t pch_thermal_control(void* _cookie, uint32 op, void* arg, size_t len);
75
76
77static status_t
78pch_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
87static status_t
88pch_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
128static status_t
129pch_thermal_write(void* cookie, off_t position, const void* buffer,
130	size_t* num_bytes)
131{
132	return B_ERROR;
133}
134
135
136status_t
137pch_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
165static status_t
166pch_thermal_close (void* cookie)
167{
168	return B_OK;
169}
170
171
172static status_t
173pch_thermal_free (void* cookie)
174{
175	return B_OK;
176}
177
178
179//	#pragma mark - driver module API
180
181
182static float
183pch_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
220static status_t
221pch_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
233static status_t
234pch_thermal_init_driver(device_node *node, void **_driverCookie)
235{
236	*_driverCookie = node;
237
238	return B_OK;
239}
240
241
242static void
243pch_thermal_uninit_driver(void *driverCookie)
244{
245}
246
247
248static status_t
249pch_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
269static status_t
270pch_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
346err:
347	free(device);
348	return status;
349}
350
351
352static void
353pch_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
363module_dependency module_dependencies[] = {
364	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
365	{}
366};
367
368
369driver_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
386struct 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
409module_info *modules[] = {
410	(module_info *)&pch_thermal_driver_module,
411	(module_info *)&pch_thermal_device_module,
412	NULL
413};
414