1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2019 Joyent, Inc. 14 */ 15 16 /* 17 * Intel Platform Controller Hub (PCH) Thermal Sensor Driver 18 * 19 * The Intel PCH is a chip that was introduced around the Nehalem generation 20 * that provides many services for the broader system on a discrete chip from 21 * the CPU. While it existed prior to the Nehalem generation, it was previously 22 * two discrete chips called the Northbridge and Southbridge. Sometimes this 23 * device is also called a 'chipset'. 24 * 25 * The PCH contains everything from a USB controller, to an AHCI controller, to 26 * clocks, the Intel Management Engine, and more. Relevant to this driver is its 27 * thermal sensor which gives us the ability to read the temperature sensor that 28 * is embedded in the PCH. 29 * 30 * The format of this sensor varies based on the generation of the chipset. The 31 * current driver supports the following chipsets organized by datasheet, which 32 * corresponds with a change in format that was introduced in the Haswell 33 * generation: 34 * 35 * - Intel 8 Series PCH 36 * - Intel 9 Series PCH 37 * - Intel C610 Series and X99 PCH 38 * - Intel C620 Series PCH 39 * - Intel 100 Series PCH 40 * - Intel 200 Series and Z730 PCH 41 * - Intel Sunrise Point-LP (Kaby Lake-U) PCH 42 * - Intel Cannon Lake (Whiskey Lake-U) PCH 43 * - Intel 300 Series and C240 Chipset 44 * 45 * The following chipsets use a different format and are not currently 46 * supported: 47 * 48 * - Intel 5 Series and Xeon 3400 PCH 49 * - Intel 6 Series PCH 50 * - Intel 7 Series PCH 51 * - Intel C600 Series and X79 PCH 52 */ 53 54 #include <sys/modctl.h> 55 #include <sys/conf.h> 56 #include <sys/devops.h> 57 #include <sys/types.h> 58 #include <sys/file.h> 59 #include <sys/open.h> 60 #include <sys/cred.h> 61 #include <sys/ddi.h> 62 #include <sys/sunddi.h> 63 #include <sys/cmn_err.h> 64 #include <sys/stat.h> 65 #include <sys/sensors.h> 66 67 /* 68 * In all cases the data we care about is in the first PCI bar, bar 0. Per 69 * pci(4)/pcie(4), this is always going to be register number 1. 70 */ 71 #define PCHTEMP_RNUMBER 1 72 73 /* 74 * The PCH Temperature Sensor has a resolution of 1/2 a degree. This is a 75 * resolution of 2 in our parlance. The register reads 50 C higher than it is. 76 * Therefore our offset is 50 shifted over by one. 77 */ 78 #define PCHTEMP_TEMP_RESOLUTION 2 79 #define PCHTEMP_TEMP_OFFSET (50 << 1) 80 81 /* 82 * This register offset has the temperature that we want to read in the lower 83 * 8-bits. The resolution and offset are described above. 84 */ 85 #define PCHTEMP_REG_TEMP 0x00 86 #define PCHTEMP_REG_TEMP_TSR 0x00ff 87 88 /* 89 * Thermal Sensor Enable and Lock (TSEL) register. This register is a byte wide 90 * and has two bits that we care about. The ETS bit, enable thermal sensor, 91 * indicates whether or not the sensor is enabled. The control for this can be 92 * locked which is the PLDB, Policy Lock-Down Bit, bit. Which restricts 93 * additional control of this register. 94 */ 95 #define PCHTEMP_REG_TSEL 0x08 96 #define PCHTEMP_REG_TSEL_ETS 0x01 97 #define PCHTEMP_REG_TSEL_PLDB 0x80 98 99 /* 100 * Threshold registers for the thermal sensors. These indicate the catastrophic, 101 * the high alert threshold, and the low alert threshold respectively. 102 */ 103 #define PCHTEMP_REG_CTT 0x10 104 #define PCHTEMP_REG_TAHV 0x14 105 #define PCHTEMP_REG_TALV 0x18 106 107 typedef struct pchtemp { 108 dev_info_t *pcht_dip; 109 int pcht_fm_caps; 110 caddr_t pcht_base; 111 ddi_acc_handle_t pcht_handle; 112 kmutex_t pcht_mutex; /* Protects members below */ 113 uint16_t pcht_temp_raw; 114 uint8_t pcht_tsel_raw; 115 uint16_t pcht_ctt_raw; 116 uint16_t pcht_tahv_raw; 117 uint16_t pcht_talv_raw; 118 int64_t pcht_temp; 119 } pchtemp_t; 120 121 void *pchtemp_state; 122 123 static pchtemp_t * 124 pchtemp_find_by_dev(dev_t dev) 125 { 126 return (ddi_get_soft_state(pchtemp_state, getminor(dev))); 127 } 128 129 static int 130 pchtemp_read_check(pchtemp_t *pch) 131 { 132 ddi_fm_error_t de; 133 134 if (!DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) { 135 return (DDI_FM_OK); 136 } 137 138 ddi_fm_acc_err_get(pch->pcht_handle, &de, DDI_FME_VERSION); 139 ddi_fm_acc_err_clear(pch->pcht_handle, DDI_FME_VERSION); 140 return (de.fme_status); 141 } 142 143 static int 144 pchtemp_read(pchtemp_t *pch) 145 { 146 uint16_t temp, ctt, tahv, talv; 147 uint8_t tsel; 148 149 ASSERT(MUTEX_HELD(&pch->pcht_mutex)); 150 151 temp = ddi_get16(pch->pcht_handle, 152 (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TEMP)); 153 tsel = ddi_get8(pch->pcht_handle, 154 (uint8_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TSEL)); 155 ctt = ddi_get16(pch->pcht_handle, 156 (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_CTT)); 157 tahv = ddi_get16(pch->pcht_handle, 158 (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TAHV)); 159 talv = ddi_get16(pch->pcht_handle, 160 (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TALV)); 161 162 if (pchtemp_read_check(pch) != DDI_FM_OK) { 163 dev_err(pch->pcht_dip, CE_WARN, "failed to read temperature " 164 "data due to FM device error"); 165 return (EIO); 166 } 167 168 pch->pcht_temp_raw = temp; 169 pch->pcht_tsel_raw = tsel; 170 pch->pcht_ctt_raw = ctt; 171 pch->pcht_tahv_raw = tahv; 172 pch->pcht_talv_raw = talv; 173 174 if ((tsel & PCHTEMP_REG_TSEL_ETS) == 0) { 175 return (ENXIO); 176 } 177 178 pch->pcht_temp = (temp & PCHTEMP_REG_TEMP_TSR) - PCHTEMP_TEMP_OFFSET; 179 180 return (0); 181 } 182 183 static int 184 pchtemp_open(dev_t *devp, int flags, int otype, cred_t *credp) 185 { 186 pchtemp_t *pch; 187 188 if (crgetzoneid(credp) != GLOBAL_ZONEID || drv_priv(credp)) { 189 return (EPERM); 190 } 191 192 if ((flags & (FEXCL | FNDELAY | FWRITE)) != 0) { 193 return (EINVAL); 194 } 195 196 if (otype != OTYP_CHR) { 197 return (EINVAL); 198 } 199 200 pch = pchtemp_find_by_dev(*devp); 201 if (pch == NULL) { 202 return (ENXIO); 203 } 204 205 return (0); 206 } 207 208 static int 209 pchtemp_ioctl_kind(intptr_t arg, int mode) 210 { 211 sensor_ioctl_kind_t kind; 212 213 bzero(&kind, sizeof (sensor_ioctl_kind_t)); 214 kind.sik_kind = SENSOR_KIND_TEMPERATURE; 215 216 if (ddi_copyout((void *)&kind, (void *)arg, sizeof (kind), 217 mode & FKIOCTL) != 0) { 218 return (EFAULT); 219 } 220 221 return (0); 222 } 223 224 static int 225 pchtemp_ioctl_temp(pchtemp_t *pch, intptr_t arg, int mode) 226 { 227 int ret; 228 sensor_ioctl_temperature_t temp; 229 230 bzero(&temp, sizeof (temp)); 231 232 mutex_enter(&pch->pcht_mutex); 233 if ((ret = pchtemp_read(pch)) != 0) { 234 mutex_exit(&pch->pcht_mutex); 235 return (ret); 236 } 237 238 temp.sit_unit = SENSOR_UNIT_CELSIUS; 239 temp.sit_gran = PCHTEMP_TEMP_RESOLUTION; 240 temp.sit_temp = pch->pcht_temp; 241 mutex_exit(&pch->pcht_mutex); 242 243 if (ddi_copyout(&temp, (void *)arg, sizeof (temp), 244 mode & FKIOCTL) != 0) { 245 return (EFAULT); 246 } 247 248 return (0); 249 } 250 251 static int 252 pchtemp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 253 int *rvalp) 254 { 255 pchtemp_t *pch; 256 257 pch = pchtemp_find_by_dev(dev); 258 if (pch == NULL) { 259 return (ENXIO); 260 } 261 262 if ((mode & FREAD) == 0) { 263 return (EINVAL); 264 } 265 266 switch (cmd) { 267 case SENSOR_IOCTL_TYPE: 268 return (pchtemp_ioctl_kind(arg, mode)); 269 case SENSOR_IOCTL_TEMPERATURE: 270 return (pchtemp_ioctl_temp(pch, arg, mode)); 271 default: 272 return (ENOTTY); 273 } 274 } 275 276 static int 277 pchtemp_close(dev_t dev, int flags, int otype, cred_t *credp) 278 { 279 return (0); 280 } 281 282 static void 283 pchtemp_cleanup(pchtemp_t *pch) 284 { 285 int inst; 286 287 ASSERT3P(pch->pcht_dip, !=, NULL); 288 inst = ddi_get_instance(pch->pcht_dip); 289 290 ddi_remove_minor_node(pch->pcht_dip, NULL); 291 292 if (pch->pcht_handle != NULL) { 293 ddi_regs_map_free(&pch->pcht_handle); 294 } 295 296 if (pch->pcht_fm_caps != DDI_FM_NOT_CAPABLE) { 297 ddi_fm_fini(pch->pcht_dip); 298 } 299 300 mutex_destroy(&pch->pcht_mutex); 301 ddi_soft_state_free(pchtemp_state, inst); 302 } 303 304 static int 305 pchtemp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 306 { 307 int inst, ret; 308 pchtemp_t *pch; 309 off_t memsize; 310 ddi_device_acc_attr_t da; 311 ddi_iblock_cookie_t iblk; 312 char name[1024]; 313 314 switch (cmd) { 315 case DDI_RESUME: 316 return (DDI_SUCCESS); 317 case DDI_ATTACH: 318 break; 319 default: 320 return (DDI_FAILURE); 321 } 322 323 inst = ddi_get_instance(dip); 324 if (ddi_soft_state_zalloc(pchtemp_state, inst) != DDI_SUCCESS) { 325 dev_err(dip, CE_WARN, "failed to allocate soft state entry %d", 326 inst); 327 return (DDI_FAILURE); 328 } 329 330 pch = ddi_get_soft_state(pchtemp_state, inst); 331 if (pch == NULL) { 332 dev_err(dip, CE_WARN, "failed to retrieve soft state entry %d", 333 inst); 334 return (DDI_FAILURE); 335 } 336 pch->pcht_dip = dip; 337 338 pch->pcht_fm_caps = DDI_FM_ACCCHK_CAPABLE; 339 ddi_fm_init(dip, &pch->pcht_fm_caps, &iblk); 340 341 mutex_init(&pch->pcht_mutex, NULL, MUTEX_DRIVER, NULL); 342 343 if (ddi_dev_regsize(dip, PCHTEMP_RNUMBER, &memsize) != DDI_SUCCESS) { 344 dev_err(dip, CE_WARN, "failed to obtain register size for " 345 "register set %d", PCHTEMP_RNUMBER); 346 goto err; 347 } 348 349 bzero(&da, sizeof (ddi_device_acc_attr_t)); 350 da.devacc_attr_version = DDI_DEVICE_ATTR_V0; 351 da.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 352 da.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 353 354 if (DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) { 355 da.devacc_attr_access = DDI_FLAGERR_ACC; 356 } else { 357 da.devacc_attr_access = DDI_DEFAULT_ACC; 358 } 359 360 if ((ret = ddi_regs_map_setup(dip, PCHTEMP_RNUMBER, &pch->pcht_base, 361 0, memsize, &da, &pch->pcht_handle)) != DDI_SUCCESS) { 362 dev_err(dip, CE_WARN, "failed to map register set %d: %d", 363 PCHTEMP_RNUMBER, ret); 364 goto err; 365 } 366 367 if (snprintf(name, sizeof (name), "ts.%d", inst) >= sizeof (name)) { 368 dev_err(dip, CE_WARN, "failed to construct minor node name, " 369 "name too long"); 370 goto err; 371 } 372 373 if (ddi_create_minor_node(pch->pcht_dip, name, S_IFCHR, (minor_t)inst, 374 DDI_NT_SENSOR_TEMP_PCH, 0) != DDI_SUCCESS) { 375 dev_err(dip, CE_WARN, "failed to create minor node %s", name); 376 goto err; 377 } 378 379 /* 380 * Attempt a single read to lock in the temperature. We don't mind if 381 * this fails for some reason. 382 */ 383 mutex_enter(&pch->pcht_mutex); 384 (void) pchtemp_read(pch); 385 mutex_exit(&pch->pcht_mutex); 386 387 return (DDI_SUCCESS); 388 389 err: 390 pchtemp_cleanup(pch); 391 return (DDI_FAILURE); 392 } 393 394 static int 395 pchtemp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, 396 void **resultp) 397 { 398 pchtemp_t *pch; 399 400 switch (cmd) { 401 case DDI_INFO_DEVT2DEVINFO: 402 pch = pchtemp_find_by_dev((dev_t)arg); 403 if (pch == NULL) { 404 return (DDI_FAILURE); 405 } 406 407 *resultp = pch->pcht_dip; 408 break; 409 case DDI_INFO_DEVT2INSTANCE: 410 *resultp = (void *)(uintptr_t)getminor((dev_t)arg); 411 break; 412 default: 413 return (DDI_FAILURE); 414 } 415 416 return (DDI_SUCCESS); 417 } 418 419 static int 420 pchtemp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 421 { 422 int inst; 423 pchtemp_t *pch; 424 425 switch (cmd) { 426 case DDI_DETACH: 427 break; 428 case DDI_SUSPEND: 429 return (DDI_SUCCESS); 430 default: 431 return (DDI_FAILURE); 432 } 433 434 inst = ddi_get_instance(dip); 435 pch = ddi_get_soft_state(pchtemp_state, inst); 436 if (pch == NULL) { 437 dev_err(dip, CE_WARN, "asked to detached instance %d, but " 438 "it does not exist in soft state", inst); 439 return (DDI_FAILURE); 440 } 441 442 pchtemp_cleanup(pch); 443 return (DDI_SUCCESS); 444 } 445 446 static struct cb_ops pchtemp_cb_ops = { 447 .cb_open = pchtemp_open, 448 .cb_close = pchtemp_close, 449 .cb_strategy = nodev, 450 .cb_print = nodev, 451 .cb_dump = nodev, 452 .cb_read = nodev, 453 .cb_write = nodev, 454 .cb_ioctl = pchtemp_ioctl, 455 .cb_devmap = nodev, 456 .cb_mmap = nodev, 457 .cb_segmap = nodev, 458 .cb_chpoll = nochpoll, 459 .cb_prop_op = ddi_prop_op, 460 .cb_flag = D_MP, 461 .cb_rev = CB_REV, 462 .cb_aread = nodev, 463 .cb_awrite = nodev 464 }; 465 466 static struct dev_ops pchtemp_dev_ops = { 467 .devo_rev = DEVO_REV, 468 .devo_refcnt = 0, 469 .devo_getinfo = pchtemp_getinfo, 470 .devo_identify = nulldev, 471 .devo_probe = nulldev, 472 .devo_attach = pchtemp_attach, 473 .devo_detach = pchtemp_detach, 474 .devo_reset = nodev, 475 .devo_power = ddi_power, 476 .devo_quiesce = ddi_quiesce_not_needed, 477 .devo_cb_ops = &pchtemp_cb_ops 478 }; 479 480 static struct modldrv pchtemp_modldrv = { 481 .drv_modops = &mod_driverops, 482 .drv_linkinfo = "Intel PCH Thermal Sensor", 483 .drv_dev_ops = &pchtemp_dev_ops 484 }; 485 486 static struct modlinkage pchtemp_modlinkage = { 487 .ml_rev = MODREV_1, 488 .ml_linkage = { &pchtemp_modldrv, NULL } 489 }; 490 491 int 492 _init(void) 493 { 494 int ret; 495 496 if (ddi_soft_state_init(&pchtemp_state, sizeof (pchtemp_t), 1) != 497 DDI_SUCCESS) { 498 return (ENOMEM); 499 } 500 501 if ((ret = mod_install(&pchtemp_modlinkage)) != 0) { 502 ddi_soft_state_fini(&pchtemp_state); 503 return (ret); 504 } 505 506 return (ret); 507 } 508 509 int 510 _info(struct modinfo *modinfop) 511 { 512 return (mod_info(&pchtemp_modlinkage, modinfop)); 513 } 514 515 int 516 _fini(void) 517 { 518 int ret; 519 520 if ((ret = mod_remove(&pchtemp_modlinkage)) != 0) { 521 return (ret); 522 } 523 524 ddi_soft_state_fini(&pchtemp_state); 525 return (ret); 526 } 527