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