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/* If DTS not locked, all trip points are configurable */ 97#define QRK_DTS_WR_MASK_SET 0x3 98/* If DTS locked, all trip points are not configurable */ 99#define QRK_DTS_WR_MASK_CLR 0 100 101#define DEFAULT_POLL_DELAY 2000 102 103struct soc_sensor_entry { 104 bool locked; 105 u32 store_ptps; 106 u32 store_dts_enable; 107 struct thermal_zone_device *tzone; 108 struct thermal_trip trips[QRK_MAX_DTS_TRIPS]; 109}; 110 111static struct soc_sensor_entry *soc_dts; 112 113static int polling_delay = DEFAULT_POLL_DELAY; 114module_param(polling_delay, int, 0644); 115MODULE_PARM_DESC(polling_delay, 116 "Polling interval for checking trip points (in milliseconds)"); 117 118static DEFINE_MUTEX(dts_update_mutex); 119 120static int soc_dts_enable(struct thermal_zone_device *tzd) 121{ 122 u32 out; 123 struct soc_sensor_entry *aux_entry = thermal_zone_device_priv(tzd); 124 int ret; 125 126 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 127 QRK_DTS_REG_OFFSET_ENABLE, &out); 128 if (ret) 129 return ret; 130 131 if (out & QRK_DTS_ENABLE_BIT) 132 return 0; 133 134 if (!aux_entry->locked) { 135 out |= QRK_DTS_ENABLE_BIT; 136 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 137 QRK_DTS_REG_OFFSET_ENABLE, out); 138 if (ret) 139 return ret; 140 } else { 141 pr_info("DTS is locked. Cannot enable DTS\n"); 142 ret = -EPERM; 143 } 144 145 return ret; 146} 147 148static int soc_dts_disable(struct thermal_zone_device *tzd) 149{ 150 u32 out; 151 struct soc_sensor_entry *aux_entry = thermal_zone_device_priv(tzd); 152 int ret; 153 154 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 155 QRK_DTS_REG_OFFSET_ENABLE, &out); 156 if (ret) 157 return ret; 158 159 if (!(out & QRK_DTS_ENABLE_BIT)) 160 return 0; 161 162 if (!aux_entry->locked) { 163 out &= ~QRK_DTS_ENABLE_BIT; 164 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 165 QRK_DTS_REG_OFFSET_ENABLE, out); 166 167 if (ret) 168 return ret; 169 } else { 170 pr_info("DTS is locked. Cannot disable DTS\n"); 171 ret = -EPERM; 172 } 173 174 return ret; 175} 176 177static int get_trip_temp(int trip) 178{ 179 int status, temp; 180 u32 out; 181 182 mutex_lock(&dts_update_mutex); 183 status = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 184 QRK_DTS_REG_OFFSET_PTPS, &out); 185 mutex_unlock(&dts_update_mutex); 186 187 if (status) 188 return THERMAL_TEMP_INVALID; 189 190 /* 191 * Thermal Sensor Programmable Trip Point Register has 8-bit 192 * fields for critical (catastrophic) and hot set trip point 193 * thresholds. The threshold value is always offset by its 194 * temperature base (50 degree Celsius). 195 */ 196 temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES; 197 temp -= QRK_DTS_TEMP_BASE; 198 199 return temp; 200} 201 202static int update_trip_temp(struct soc_sensor_entry *aux_entry, 203 int trip, int temp) 204{ 205 u32 out; 206 u32 temp_out; 207 u32 store_ptps; 208 int ret; 209 210 mutex_lock(&dts_update_mutex); 211 if (aux_entry->locked) { 212 ret = -EPERM; 213 goto failed; 214 } 215 216 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 217 QRK_DTS_REG_OFFSET_PTPS, &store_ptps); 218 if (ret) 219 goto failed; 220 221 /* 222 * Protection against unsafe trip point thresdhold value. 223 * As Quark X1000 data-sheet does not provide any recommendation 224 * regarding the safe trip point threshold value to use, we choose 225 * the safe value according to the threshold value set by UEFI BIOS. 226 */ 227 if (temp > QRK_DTS_SAFE_TP_THRES) 228 temp = QRK_DTS_SAFE_TP_THRES; 229 230 /* 231 * Thermal Sensor Programmable Trip Point Register has 8-bit 232 * fields for critical (catastrophic) and hot set trip point 233 * thresholds. The threshold value is always offset by its 234 * temperature base (50 degree Celsius). 235 */ 236 temp_out = temp + QRK_DTS_TEMP_BASE; 237 out = (store_ptps & ~(QRK_DTS_MASK_TP_THRES << 238 (trip * QRK_DTS_SHIFT_TP))); 239 out |= (temp_out & QRK_DTS_MASK_TP_THRES) << 240 (trip * QRK_DTS_SHIFT_TP); 241 242 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 243 QRK_DTS_REG_OFFSET_PTPS, out); 244 245failed: 246 mutex_unlock(&dts_update_mutex); 247 return ret; 248} 249 250static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, 251 int temp) 252{ 253 return update_trip_temp(thermal_zone_device_priv(tzd), trip, temp); 254} 255 256static int sys_get_curr_temp(struct thermal_zone_device *tzd, 257 int *temp) 258{ 259 u32 out; 260 int ret; 261 262 mutex_lock(&dts_update_mutex); 263 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 264 QRK_DTS_REG_OFFSET_TEMP, &out); 265 mutex_unlock(&dts_update_mutex); 266 267 if (ret) 268 return ret; 269 270 /* 271 * Thermal Sensor Temperature Register has 8-bit field 272 * for temperature value (offset by temperature base 273 * 50 degree Celsius). 274 */ 275 out = (out >> QRK_DTS_OFFSET_TEMP) & QRK_DTS_MASK_TEMP; 276 *temp = out - QRK_DTS_TEMP_BASE; 277 278 return 0; 279} 280 281static int sys_change_mode(struct thermal_zone_device *tzd, 282 enum thermal_device_mode mode) 283{ 284 int ret; 285 286 mutex_lock(&dts_update_mutex); 287 if (mode == THERMAL_DEVICE_ENABLED) 288 ret = soc_dts_enable(tzd); 289 else 290 ret = soc_dts_disable(tzd); 291 mutex_unlock(&dts_update_mutex); 292 293 return ret; 294} 295 296static struct thermal_zone_device_ops tzone_ops = { 297 .get_temp = sys_get_curr_temp, 298 .set_trip_temp = sys_set_trip_temp, 299 .change_mode = sys_change_mode, 300}; 301 302static void free_soc_dts(struct soc_sensor_entry *aux_entry) 303{ 304 if (aux_entry) { 305 if (!aux_entry->locked) { 306 mutex_lock(&dts_update_mutex); 307 iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 308 QRK_DTS_REG_OFFSET_ENABLE, 309 aux_entry->store_dts_enable); 310 311 iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 312 QRK_DTS_REG_OFFSET_PTPS, 313 aux_entry->store_ptps); 314 mutex_unlock(&dts_update_mutex); 315 } 316 thermal_zone_device_unregister(aux_entry->tzone); 317 kfree(aux_entry); 318 } 319} 320 321static struct soc_sensor_entry *alloc_soc_dts(void) 322{ 323 struct soc_sensor_entry *aux_entry; 324 int err; 325 u32 out; 326 int wr_mask; 327 328 aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL); 329 if (!aux_entry) { 330 err = -ENOMEM; 331 return ERR_PTR(-ENOMEM); 332 } 333 334 /* Check if DTS register is locked */ 335 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 336 QRK_DTS_REG_OFFSET_LOCK, &out); 337 if (err) 338 goto err_ret; 339 340 if (out & QRK_DTS_LOCK_BIT) { 341 aux_entry->locked = true; 342 wr_mask = QRK_DTS_WR_MASK_CLR; 343 } else { 344 aux_entry->locked = false; 345 wr_mask = QRK_DTS_WR_MASK_SET; 346 } 347 348 /* Store DTS default state if DTS registers are not locked */ 349 if (!aux_entry->locked) { 350 /* Store DTS default enable for restore on exit */ 351 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 352 QRK_DTS_REG_OFFSET_ENABLE, 353 &aux_entry->store_dts_enable); 354 if (err) 355 goto err_ret; 356 357 /* Store DTS default PTPS register for restore on exit */ 358 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 359 QRK_DTS_REG_OFFSET_PTPS, 360 &aux_entry->store_ptps); 361 if (err) 362 goto err_ret; 363 } 364 365 aux_entry->trips[QRK_DTS_ID_TP_CRITICAL].temperature = get_trip_temp(QRK_DTS_ID_TP_CRITICAL); 366 aux_entry->trips[QRK_DTS_ID_TP_CRITICAL].type = THERMAL_TRIP_CRITICAL; 367 368 aux_entry->trips[QRK_DTS_ID_TP_HOT].temperature = get_trip_temp(QRK_DTS_ID_TP_HOT); 369 aux_entry->trips[QRK_DTS_ID_TP_HOT].type = THERMAL_TRIP_HOT; 370 371 aux_entry->tzone = thermal_zone_device_register_with_trips("quark_dts", 372 aux_entry->trips, 373 QRK_MAX_DTS_TRIPS, 374 wr_mask, 375 aux_entry, &tzone_ops, 376 NULL, 0, polling_delay); 377 if (IS_ERR(aux_entry->tzone)) { 378 err = PTR_ERR(aux_entry->tzone); 379 goto err_ret; 380 } 381 382 err = thermal_zone_device_enable(aux_entry->tzone); 383 if (err) 384 goto err_aux_status; 385 386 return aux_entry; 387 388err_aux_status: 389 thermal_zone_device_unregister(aux_entry->tzone); 390err_ret: 391 kfree(aux_entry); 392 return ERR_PTR(err); 393} 394 395static const struct x86_cpu_id qrk_thermal_ids[] __initconst = { 396 X86_MATCH_VENDOR_FAM_MODEL(INTEL, 5, INTEL_FAM5_QUARK_X1000, NULL), 397 {} 398}; 399MODULE_DEVICE_TABLE(x86cpu, qrk_thermal_ids); 400 401static int __init intel_quark_thermal_init(void) 402{ 403 if (!x86_match_cpu(qrk_thermal_ids) || !iosf_mbi_available()) 404 return -ENODEV; 405 406 soc_dts = alloc_soc_dts(); 407 if (IS_ERR(soc_dts)) 408 return PTR_ERR(soc_dts); 409 410 return 0; 411} 412 413static void __exit intel_quark_thermal_exit(void) 414{ 415 free_soc_dts(soc_dts); 416} 417 418module_init(intel_quark_thermal_init) 419module_exit(intel_quark_thermal_exit) 420 421MODULE_DESCRIPTION("Intel Quark DTS Thermal Driver"); 422MODULE_AUTHOR("Ong Boon Leong <boon.leong.ong@intel.com>"); 423MODULE_LICENSE("Dual BSD/GPL"); 424