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