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 * Copyright 2020 Oxide Computer Company 15 */ 16 17 /* 18 * Intel Platform Controller Hub (PCH) Thermal Sensor Driver 19 * 20 * The Intel PCH is a chip that was introduced around the Nehalem generation 21 * that provides many services for the broader system on a discrete chip from 22 * the CPU. While it existed prior to the Nehalem generation, it was previously 23 * two discrete chips called the Northbridge and Southbridge. Sometimes this 24 * device is also called a 'chipset'. 25 * 26 * The PCH contains everything from a USB controller, to an AHCI controller, to 27 * clocks, the Intel Management Engine, and more. Relevant to this driver is its 28 * thermal sensor which gives us the ability to read the temperature sensor that 29 * is embedded in the PCH. 30 * 31 * The format of this sensor varies based on the generation of the chipset. The 32 * current driver supports the following chipsets organized by datasheet, which 33 * corresponds with a change in format that was introduced in the Haswell 34 * generation: 35 * 36 * - Intel 8 Series PCH 37 * - Intel 9 Series PCH 38 * - Intel C610 Series and X99 PCH 39 * - Intel C620 Series PCH 40 * - Intel 100 Series PCH 41 * - Intel 200 Series and Z730 PCH 42 * - Intel Sunrise Point-LP (Kaby Lake-U) PCH 43 * - Intel Cannon Lake (Whiskey Lake-U) PCH 44 * - Intel 300 Series and C240 Chipset 45 * 46 * The following chipsets use a different format and are not currently 47 * supported: 48 * 49 * - Intel 5 Series and Xeon 3400 PCH 50 * - Intel 6 Series PCH 51 * - Intel 7 Series PCH 52 * - Intel C600 Series and X79 PCH 53 */ 54 55 #include <sys/modctl.h> 56 #include <sys/conf.h> 57 #include <sys/devops.h> 58 #include <sys/types.h> 59 #include <sys/file.h> 60 #include <sys/open.h> 61 #include <sys/cred.h> 62 #include <sys/ddi.h> 63 #include <sys/sunddi.h> 64 #include <sys/cmn_err.h> 65 #include <sys/stat.h> 66 #include <sys/sensors.h> 67 68 /* 69 * In all cases the data we care about is in the first PCI bar, bar 0. Per 70 * pci(4)/pcie(4), this is always going to be register number 1. 71 */ 72 #define PCHTEMP_RNUMBER 1 73 74 /* 75 * The PCH Temperature Sensor has a resolution of 1/2 a degree. This is a 76 * resolution of 2 in our parlance. The register reads 50 C higher than it is. 77 * Therefore our offset is 50 shifted over by one. 78 */ 79 #define PCHTEMP_TEMP_RESOLUTION 2 80 #define PCHTEMP_TEMP_OFFSET (50 << 1) 81 82 /* 83 * This register offset has the temperature that we want to read in the lower 84 * 8-bits. The resolution and offset are described above. 85 */ 86 #define PCHTEMP_REG_TEMP 0x00 87 #define PCHTEMP_REG_TEMP_TSR 0x00ff 88 89 /* 90 * Thermal Sensor Enable and Lock (TSEL) register. This register is a byte wide 91 * and has two bits that we care about. The ETS bit, enable thermal sensor, 92 * indicates whether or not the sensor is enabled. The control for this can be 93 * locked which is the PLDB, Policy Lock-Down Bit, bit. Which restricts 94 * additional control of this register. 95 */ 96 #define PCHTEMP_REG_TSEL 0x08 97 #define PCHTEMP_REG_TSEL_ETS 0x01 98 #define PCHTEMP_REG_TSEL_PLDB 0x80 99 100 /* 101 * Threshold registers for the thermal sensors. These indicate the catastrophic, 102 * the high alert threshold, and the low alert threshold respectively. 103 */ 104 #define PCHTEMP_REG_CTT 0x10 105 #define PCHTEMP_REG_TAHV 0x14 106 #define PCHTEMP_REG_TALV 0x18 107 108 typedef struct pchtemp { 109 dev_info_t *pcht_dip; 110 int pcht_fm_caps; 111 caddr_t pcht_base; 112 ddi_acc_handle_t pcht_handle; 113 id_t pcht_ksensor; 114 kmutex_t pcht_mutex; /* Protects members below */ 115 uint16_t pcht_temp_raw; 116 uint8_t pcht_tsel_raw; 117 uint16_t pcht_ctt_raw; 118 uint16_t pcht_tahv_raw; 119 uint16_t pcht_talv_raw; 120 int64_t pcht_temp; 121 } pchtemp_t; 122 123 void *pchtemp_state; 124 125 static int 126 pchtemp_read_check(pchtemp_t *pch) 127 { 128 ddi_fm_error_t de; 129 130 if (!DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) { 131 return (DDI_FM_OK); 132 } 133 134 ddi_fm_acc_err_get(pch->pcht_handle, &de, DDI_FME_VERSION); 135 ddi_fm_acc_err_clear(pch->pcht_handle, DDI_FME_VERSION); 136 return (de.fme_status); 137 } 138 139 static int 140 pchtemp_read(void *arg, sensor_ioctl_temperature_t *sit) 141 { 142 uint16_t temp, ctt, tahv, talv; 143 uint8_t tsel; 144 pchtemp_t *pch = arg; 145 146 mutex_enter(&pch->pcht_mutex); 147 148 temp = ddi_get16(pch->pcht_handle, 149 (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TEMP)); 150 tsel = ddi_get8(pch->pcht_handle, 151 (uint8_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TSEL)); 152 ctt = ddi_get16(pch->pcht_handle, 153 (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_CTT)); 154 tahv = ddi_get16(pch->pcht_handle, 155 (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TAHV)); 156 talv = ddi_get16(pch->pcht_handle, 157 (uint16_t *)((uintptr_t)pch->pcht_base + PCHTEMP_REG_TALV)); 158 159 if (pchtemp_read_check(pch) != DDI_FM_OK) { 160 mutex_exit(&pch->pcht_mutex); 161 dev_err(pch->pcht_dip, CE_WARN, "failed to read temperature " 162 "data due to FM device error"); 163 return (EIO); 164 } 165 166 pch->pcht_temp_raw = temp; 167 pch->pcht_tsel_raw = tsel; 168 pch->pcht_ctt_raw = ctt; 169 pch->pcht_tahv_raw = tahv; 170 pch->pcht_talv_raw = talv; 171 172 if ((tsel & PCHTEMP_REG_TSEL_ETS) == 0) { 173 mutex_exit(&pch->pcht_mutex); 174 return (ENXIO); 175 } 176 177 pch->pcht_temp = (temp & PCHTEMP_REG_TEMP_TSR) - PCHTEMP_TEMP_OFFSET; 178 sit->sit_unit = SENSOR_UNIT_CELSIUS; 179 sit->sit_gran = PCHTEMP_TEMP_RESOLUTION; 180 sit->sit_temp = pch->pcht_temp; 181 mutex_exit(&pch->pcht_mutex); 182 183 return (0); 184 } 185 186 static const ksensor_ops_t pchtemp_temp_ops = { 187 .kso_kind = ksensor_kind_temperature, 188 .kso_temp = pchtemp_read 189 }; 190 191 static void 192 pchtemp_cleanup(pchtemp_t *pch) 193 { 194 int inst; 195 196 ASSERT3P(pch->pcht_dip, !=, NULL); 197 inst = ddi_get_instance(pch->pcht_dip); 198 199 (void) ksensor_remove(pch->pcht_dip, KSENSOR_ALL_IDS); 200 201 if (pch->pcht_handle != NULL) { 202 ddi_regs_map_free(&pch->pcht_handle); 203 } 204 205 if (pch->pcht_fm_caps != DDI_FM_NOT_CAPABLE) { 206 ddi_fm_fini(pch->pcht_dip); 207 } 208 209 mutex_destroy(&pch->pcht_mutex); 210 ddi_soft_state_free(pchtemp_state, inst); 211 } 212 213 static int 214 pchtemp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 215 { 216 int inst, ret; 217 pchtemp_t *pch; 218 off_t memsize; 219 ddi_device_acc_attr_t da; 220 ddi_iblock_cookie_t iblk; 221 char name[1024]; 222 223 switch (cmd) { 224 case DDI_RESUME: 225 return (DDI_SUCCESS); 226 case DDI_ATTACH: 227 break; 228 default: 229 return (DDI_FAILURE); 230 } 231 232 inst = ddi_get_instance(dip); 233 if (ddi_soft_state_zalloc(pchtemp_state, inst) != DDI_SUCCESS) { 234 dev_err(dip, CE_WARN, "failed to allocate soft state entry %d", 235 inst); 236 return (DDI_FAILURE); 237 } 238 239 pch = ddi_get_soft_state(pchtemp_state, inst); 240 if (pch == NULL) { 241 dev_err(dip, CE_WARN, "failed to retrieve soft state entry %d", 242 inst); 243 return (DDI_FAILURE); 244 } 245 pch->pcht_dip = dip; 246 247 pch->pcht_fm_caps = DDI_FM_ACCCHK_CAPABLE; 248 ddi_fm_init(dip, &pch->pcht_fm_caps, &iblk); 249 250 mutex_init(&pch->pcht_mutex, NULL, MUTEX_DRIVER, NULL); 251 252 if (ddi_dev_regsize(dip, PCHTEMP_RNUMBER, &memsize) != DDI_SUCCESS) { 253 dev_err(dip, CE_WARN, "failed to obtain register size for " 254 "register set %d", PCHTEMP_RNUMBER); 255 goto err; 256 } 257 258 bzero(&da, sizeof (ddi_device_acc_attr_t)); 259 da.devacc_attr_version = DDI_DEVICE_ATTR_V0; 260 da.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 261 da.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 262 263 if (DDI_FM_ACC_ERR_CAP(pch->pcht_fm_caps)) { 264 da.devacc_attr_access = DDI_FLAGERR_ACC; 265 } else { 266 da.devacc_attr_access = DDI_DEFAULT_ACC; 267 } 268 269 if ((ret = ddi_regs_map_setup(dip, PCHTEMP_RNUMBER, &pch->pcht_base, 270 0, memsize, &da, &pch->pcht_handle)) != DDI_SUCCESS) { 271 dev_err(dip, CE_WARN, "failed to map register set %d: %d", 272 PCHTEMP_RNUMBER, ret); 273 goto err; 274 } 275 276 if (snprintf(name, sizeof (name), "ts.%d", inst) >= sizeof (name)) { 277 dev_err(dip, CE_WARN, "failed to construct minor node name, " 278 "name too long"); 279 goto err; 280 } 281 282 if ((ret = ksensor_create(pch->pcht_dip, &pchtemp_temp_ops, pch, name, 283 DDI_NT_SENSOR_TEMP_PCH, &pch->pcht_ksensor)) != 0) { 284 dev_err(dip, CE_WARN, "failed to create minor node %s", name); 285 goto err; 286 } 287 288 return (DDI_SUCCESS); 289 290 err: 291 pchtemp_cleanup(pch); 292 return (DDI_FAILURE); 293 } 294 295 static int 296 pchtemp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 297 { 298 int inst; 299 pchtemp_t *pch; 300 301 switch (cmd) { 302 case DDI_DETACH: 303 break; 304 case DDI_SUSPEND: 305 return (DDI_SUCCESS); 306 default: 307 return (DDI_FAILURE); 308 } 309 310 inst = ddi_get_instance(dip); 311 pch = ddi_get_soft_state(pchtemp_state, inst); 312 if (pch == NULL) { 313 dev_err(dip, CE_WARN, "asked to detached instance %d, but " 314 "it does not exist in soft state", inst); 315 return (DDI_FAILURE); 316 } 317 318 pchtemp_cleanup(pch); 319 return (DDI_SUCCESS); 320 } 321 322 static struct dev_ops pchtemp_dev_ops = { 323 .devo_rev = DEVO_REV, 324 .devo_refcnt = 0, 325 .devo_getinfo = nodev, 326 .devo_identify = nulldev, 327 .devo_probe = nulldev, 328 .devo_attach = pchtemp_attach, 329 .devo_detach = pchtemp_detach, 330 .devo_reset = nodev, 331 .devo_quiesce = ddi_quiesce_not_needed 332 }; 333 334 static struct modldrv pchtemp_modldrv = { 335 .drv_modops = &mod_driverops, 336 .drv_linkinfo = "Intel PCH Thermal Sensor", 337 .drv_dev_ops = &pchtemp_dev_ops 338 }; 339 340 static struct modlinkage pchtemp_modlinkage = { 341 .ml_rev = MODREV_1, 342 .ml_linkage = { &pchtemp_modldrv, NULL } 343 }; 344 345 int 346 _init(void) 347 { 348 int ret; 349 350 if (ddi_soft_state_init(&pchtemp_state, sizeof (pchtemp_t), 1) != 351 DDI_SUCCESS) { 352 return (ENOMEM); 353 } 354 355 if ((ret = mod_install(&pchtemp_modlinkage)) != 0) { 356 ddi_soft_state_fini(&pchtemp_state); 357 return (ret); 358 } 359 360 return (ret); 361 } 362 363 int 364 _info(struct modinfo *modinfop) 365 { 366 return (mod_info(&pchtemp_modlinkage, modinfop)); 367 } 368 369 int 370 _fini(void) 371 { 372 int ret; 373 374 if ((ret = mod_remove(&pchtemp_modlinkage)) != 0) { 375 return (ret); 376 } 377 378 ddi_soft_state_fini(&pchtemp_state); 379 return (ret); 380 } 381