1 /* 2 * intel_quark_dts_thermal.c 3 * 4 * This file is provided under a dual BSD/GPLv2 license. When using or 5 * redistributing this file, you may do so under either license. 6 * 7 * GPL LICENSE SUMMARY 8 * 9 * Copyright(c) 2015 Intel Corporation. 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of version 2 of the GNU General Public License as 13 * published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * Contact Information: 21 * Ong Boon Leong <boon.leong.ong@intel.com> 22 * Intel Malaysia, Penang 23 * 24 * BSD LICENSE 25 * 26 * Copyright(c) 2015 Intel Corporation. 27 * 28 * Redistribution and use in source and binary forms, with or without 29 * modification, are permitted provided that the following conditions 30 * are met: 31 * 32 * * Redistributions of source code must retain the above copyright 33 * notice, this list of conditions and the following disclaimer. 34 * * Redistributions in binary form must reproduce the above copyright 35 * notice, this list of conditions and the following disclaimer in 36 * the documentation and/or other materials provided with the 37 * distribution. 38 * * Neither the name of Intel Corporation nor the names of its 39 * contributors may be used to endorse or promote products derived 40 * from this software without specific prior written permission. 41 * 42 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 43 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 44 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 45 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 46 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 47 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 48 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 49 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 50 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 51 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 52 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 * 54 * Quark DTS thermal driver is implemented by referencing 55 * intel_soc_dts_thermal.c. 56 */ 57 58 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 59 60 #include <linux/module.h> 61 #include <linux/slab.h> 62 #include <linux/interrupt.h> 63 #include <linux/thermal.h> 64 #include <asm/cpu_device_id.h> 65 #include <asm/iosf_mbi.h> 66 67 /* DTS reset is programmed via QRK_MBI_UNIT_SOC */ 68 #define QRK_DTS_REG_OFFSET_RESET 0x34 69 #define QRK_DTS_RESET_BIT BIT(0) 70 71 /* DTS enable is programmed via QRK_MBI_UNIT_RMU */ 72 #define QRK_DTS_REG_OFFSET_ENABLE 0xB0 73 #define QRK_DTS_ENABLE_BIT BIT(15) 74 75 /* Temperature Register is read via QRK_MBI_UNIT_RMU */ 76 #define QRK_DTS_REG_OFFSET_TEMP 0xB1 77 #define QRK_DTS_MASK_TEMP 0xFF 78 #define QRK_DTS_OFFSET_TEMP 0 79 #define QRK_DTS_OFFSET_REL_TEMP 16 80 #define QRK_DTS_TEMP_BASE 50 81 82 /* Programmable Trip Point Register is configured via QRK_MBI_UNIT_RMU */ 83 #define QRK_DTS_REG_OFFSET_PTPS 0xB2 84 #define QRK_DTS_MASK_TP_THRES 0xFF 85 #define QRK_DTS_SHIFT_TP 8 86 #define QRK_DTS_ID_TP_CRITICAL 0 87 #define QRK_DTS_ID_TP_HOT 1 88 #define QRK_DTS_SAFE_TP_THRES 105 89 90 /* Thermal Sensor Register Lock */ 91 #define QRK_DTS_REG_OFFSET_LOCK 0x71 92 #define QRK_DTS_LOCK_BIT BIT(5) 93 94 /* Quark DTS has 2 trip points: hot & catastrophic */ 95 #define QRK_MAX_DTS_TRIPS 2 96 97 #define DEFAULT_POLL_DELAY 2000 98 99 struct soc_sensor_entry { 100 bool locked; 101 u32 store_ptps; 102 u32 store_dts_enable; 103 struct thermal_zone_device *tzone; 104 }; 105 106 static struct soc_sensor_entry *soc_dts; 107 108 static int polling_delay = DEFAULT_POLL_DELAY; 109 module_param(polling_delay, int, 0644); 110 MODULE_PARM_DESC(polling_delay, 111 "Polling interval for checking trip points (in milliseconds)"); 112 113 static DEFINE_MUTEX(dts_update_mutex); 114 115 static int soc_dts_enable(struct thermal_zone_device *tzd) 116 { 117 u32 out; 118 struct soc_sensor_entry *aux_entry = thermal_zone_device_priv(tzd); 119 int ret; 120 121 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 122 QRK_DTS_REG_OFFSET_ENABLE, &out); 123 if (ret) 124 return ret; 125 126 if (out & QRK_DTS_ENABLE_BIT) 127 return 0; 128 129 if (!aux_entry->locked) { 130 out |= QRK_DTS_ENABLE_BIT; 131 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 132 QRK_DTS_REG_OFFSET_ENABLE, out); 133 if (ret) 134 return ret; 135 } else { 136 pr_info("DTS is locked. Cannot enable DTS\n"); 137 ret = -EPERM; 138 } 139 140 return ret; 141 } 142 143 static int soc_dts_disable(struct thermal_zone_device *tzd) 144 { 145 u32 out; 146 struct soc_sensor_entry *aux_entry = thermal_zone_device_priv(tzd); 147 int ret; 148 149 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 150 QRK_DTS_REG_OFFSET_ENABLE, &out); 151 if (ret) 152 return ret; 153 154 if (!(out & QRK_DTS_ENABLE_BIT)) 155 return 0; 156 157 if (!aux_entry->locked) { 158 out &= ~QRK_DTS_ENABLE_BIT; 159 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 160 QRK_DTS_REG_OFFSET_ENABLE, out); 161 162 if (ret) 163 return ret; 164 } else { 165 pr_info("DTS is locked. Cannot disable DTS\n"); 166 ret = -EPERM; 167 } 168 169 return ret; 170 } 171 172 static int get_trip_temp(int trip) 173 { 174 int status, temp; 175 u32 out; 176 177 mutex_lock(&dts_update_mutex); 178 status = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 179 QRK_DTS_REG_OFFSET_PTPS, &out); 180 mutex_unlock(&dts_update_mutex); 181 182 if (status) 183 return THERMAL_TEMP_INVALID; 184 185 /* 186 * Thermal Sensor Programmable Trip Point Register has 8-bit 187 * fields for critical (catastrophic) and hot set trip point 188 * thresholds. The threshold value is always offset by its 189 * temperature base (50 degree Celsius). 190 */ 191 temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES; 192 temp -= QRK_DTS_TEMP_BASE; 193 194 return temp; 195 } 196 197 static int update_trip_temp(struct soc_sensor_entry *aux_entry, 198 int trip, int temp) 199 { 200 u32 out; 201 u32 temp_out; 202 u32 store_ptps; 203 int ret; 204 205 mutex_lock(&dts_update_mutex); 206 if (aux_entry->locked) { 207 ret = -EPERM; 208 goto failed; 209 } 210 211 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 212 QRK_DTS_REG_OFFSET_PTPS, &store_ptps); 213 if (ret) 214 goto failed; 215 216 /* 217 * Protection against unsafe trip point thresdhold value. 218 * As Quark X1000 data-sheet does not provide any recommendation 219 * regarding the safe trip point threshold value to use, we choose 220 * the safe value according to the threshold value set by UEFI BIOS. 221 */ 222 if (temp > QRK_DTS_SAFE_TP_THRES) 223 temp = QRK_DTS_SAFE_TP_THRES; 224 225 /* 226 * Thermal Sensor Programmable Trip Point Register has 8-bit 227 * fields for critical (catastrophic) and hot set trip point 228 * thresholds. The threshold value is always offset by its 229 * temperature base (50 degree Celsius). 230 */ 231 temp_out = temp + QRK_DTS_TEMP_BASE; 232 out = (store_ptps & ~(QRK_DTS_MASK_TP_THRES << 233 (trip * QRK_DTS_SHIFT_TP))); 234 out |= (temp_out & QRK_DTS_MASK_TP_THRES) << 235 (trip * QRK_DTS_SHIFT_TP); 236 237 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 238 QRK_DTS_REG_OFFSET_PTPS, out); 239 240 failed: 241 mutex_unlock(&dts_update_mutex); 242 return ret; 243 } 244 245 static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, 246 int temp) 247 { 248 return update_trip_temp(thermal_zone_device_priv(tzd), trip, temp); 249 } 250 251 static int sys_get_curr_temp(struct thermal_zone_device *tzd, 252 int *temp) 253 { 254 u32 out; 255 int ret; 256 257 mutex_lock(&dts_update_mutex); 258 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 259 QRK_DTS_REG_OFFSET_TEMP, &out); 260 mutex_unlock(&dts_update_mutex); 261 262 if (ret) 263 return ret; 264 265 /* 266 * Thermal Sensor Temperature Register has 8-bit field 267 * for temperature value (offset by temperature base 268 * 50 degree Celsius). 269 */ 270 out = (out >> QRK_DTS_OFFSET_TEMP) & QRK_DTS_MASK_TEMP; 271 *temp = out - QRK_DTS_TEMP_BASE; 272 273 return 0; 274 } 275 276 static int sys_change_mode(struct thermal_zone_device *tzd, 277 enum thermal_device_mode mode) 278 { 279 int ret; 280 281 mutex_lock(&dts_update_mutex); 282 if (mode == THERMAL_DEVICE_ENABLED) 283 ret = soc_dts_enable(tzd); 284 else 285 ret = soc_dts_disable(tzd); 286 mutex_unlock(&dts_update_mutex); 287 288 return ret; 289 } 290 291 static const struct thermal_zone_device_ops tzone_ops = { 292 .get_temp = sys_get_curr_temp, 293 .set_trip_temp = sys_set_trip_temp, 294 .change_mode = sys_change_mode, 295 }; 296 297 static void free_soc_dts(struct soc_sensor_entry *aux_entry) 298 { 299 if (aux_entry) { 300 if (!aux_entry->locked) { 301 mutex_lock(&dts_update_mutex); 302 iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 303 QRK_DTS_REG_OFFSET_ENABLE, 304 aux_entry->store_dts_enable); 305 306 iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 307 QRK_DTS_REG_OFFSET_PTPS, 308 aux_entry->store_ptps); 309 mutex_unlock(&dts_update_mutex); 310 } 311 thermal_zone_device_unregister(aux_entry->tzone); 312 kfree(aux_entry); 313 } 314 } 315 316 static struct soc_sensor_entry *alloc_soc_dts(void) 317 { 318 struct thermal_trip trips[QRK_MAX_DTS_TRIPS] = { 0 }; 319 struct soc_sensor_entry *aux_entry; 320 int err; 321 u32 out; 322 323 aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL); 324 if (!aux_entry) { 325 err = -ENOMEM; 326 return ERR_PTR(-ENOMEM); 327 } 328 329 /* Check if DTS register is locked */ 330 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 331 QRK_DTS_REG_OFFSET_LOCK, &out); 332 if (err) 333 goto err_ret; 334 335 aux_entry->locked = !!(out & QRK_DTS_LOCK_BIT); 336 337 /* Store DTS default state if DTS registers are not locked */ 338 if (!aux_entry->locked) { 339 /* Store DTS default enable for restore on exit */ 340 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 341 QRK_DTS_REG_OFFSET_ENABLE, 342 &aux_entry->store_dts_enable); 343 if (err) 344 goto err_ret; 345 346 /* Store DTS default PTPS register for restore on exit */ 347 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 348 QRK_DTS_REG_OFFSET_PTPS, 349 &aux_entry->store_ptps); 350 if (err) 351 goto err_ret; 352 353 trips[QRK_DTS_ID_TP_CRITICAL].flags |= THERMAL_TRIP_FLAG_RW_TEMP; 354 trips[QRK_DTS_ID_TP_HOT].flags |= THERMAL_TRIP_FLAG_RW_TEMP; 355 } 356 357 trips[QRK_DTS_ID_TP_CRITICAL].temperature = get_trip_temp(QRK_DTS_ID_TP_CRITICAL); 358 trips[QRK_DTS_ID_TP_CRITICAL].type = THERMAL_TRIP_CRITICAL; 359 360 trips[QRK_DTS_ID_TP_HOT].temperature = get_trip_temp(QRK_DTS_ID_TP_HOT); 361 trips[QRK_DTS_ID_TP_HOT].type = THERMAL_TRIP_HOT; 362 363 aux_entry->tzone = thermal_zone_device_register_with_trips("quark_dts", 364 trips, 365 QRK_MAX_DTS_TRIPS, 366 aux_entry, 367 &tzone_ops, 368 NULL, 0, polling_delay); 369 if (IS_ERR(aux_entry->tzone)) { 370 err = PTR_ERR(aux_entry->tzone); 371 goto err_ret; 372 } 373 374 err = thermal_zone_device_enable(aux_entry->tzone); 375 if (err) 376 goto err_aux_status; 377 378 return aux_entry; 379 380 err_aux_status: 381 thermal_zone_device_unregister(aux_entry->tzone); 382 err_ret: 383 kfree(aux_entry); 384 return ERR_PTR(err); 385 } 386 387 static const struct x86_cpu_id qrk_thermal_ids[] __initconst = { 388 X86_MATCH_VENDOR_FAM_MODEL(INTEL, 5, INTEL_FAM5_QUARK_X1000, NULL), 389 {} 390 }; 391 MODULE_DEVICE_TABLE(x86cpu, qrk_thermal_ids); 392 393 static int __init intel_quark_thermal_init(void) 394 { 395 if (!x86_match_cpu(qrk_thermal_ids) || !iosf_mbi_available()) 396 return -ENODEV; 397 398 soc_dts = alloc_soc_dts(); 399 if (IS_ERR(soc_dts)) 400 return PTR_ERR(soc_dts); 401 402 return 0; 403 } 404 405 static void __exit intel_quark_thermal_exit(void) 406 { 407 free_soc_dts(soc_dts); 408 } 409 410 module_init(intel_quark_thermal_init) 411 module_exit(intel_quark_thermal_exit) 412 413 MODULE_DESCRIPTION("Intel Quark DTS Thermal Driver"); 414 MODULE_AUTHOR("Ong Boon Leong <boon.leong.ong@intel.com>"); 415 MODULE_LICENSE("Dual BSD/GPL"); 416