432 lines · c
1/*2 * intel_quark_dts_thermal.c3 *4 * This file is provided under a dual BSD/GPLv2 license. When using or5 * redistributing this file, you may do so under either license.6 *7 * GPL LICENSE SUMMARY8 *9 * Copyright(c) 2015 Intel Corporation.10 *11 * This program is free software; you can redistribute it and/or modify12 * it under the terms of version 2 of the GNU General Public License as13 * published by the Free Software Foundation.14 *15 * This program is distributed in the hope that it will be useful, but16 * WITHOUT ANY WARRANTY; without even the implied warranty of17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU18 * General Public License for more details.19 *20 * Contact Information:21 * Ong Boon Leong <boon.leong.ong@intel.com>22 * Intel Malaysia, Penang23 *24 * BSD LICENSE25 *26 * Copyright(c) 2015 Intel Corporation.27 *28 * Redistribution and use in source and binary forms, with or without29 * modification, are permitted provided that the following conditions30 * are met:31 *32 * * Redistributions of source code must retain the above copyright33 * notice, this list of conditions and the following disclaimer.34 * * Redistributions in binary form must reproduce the above copyright35 * notice, this list of conditions and the following disclaimer in36 * the documentation and/or other materials provided with the37 * distribution.38 * * Neither the name of Intel Corporation nor the names of its39 * contributors may be used to endorse or promote products derived40 * from this software without specific prior written permission.41 *42 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS43 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT44 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR45 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT46 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,47 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT48 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,49 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY50 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT51 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE52 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.53 *54 * Quark DTS thermal driver is implemented by referencing55 * intel_soc_dts_thermal.c.56 */57 58#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt59 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 0x3469#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 0xB073#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 0xB177#define QRK_DTS_MASK_TEMP 0xFF78#define QRK_DTS_OFFSET_TEMP 079#define QRK_DTS_OFFSET_REL_TEMP 1680#define QRK_DTS_TEMP_BASE 5081 82/* Programmable Trip Point Register is configured via QRK_MBI_UNIT_RMU */83#define QRK_DTS_REG_OFFSET_PTPS 0xB284#define QRK_DTS_MASK_TP_THRES 0xFF85#define QRK_DTS_SHIFT_TP 886#define QRK_DTS_ID_TP_CRITICAL 087#define QRK_DTS_ID_TP_HOT 188#define QRK_DTS_SAFE_TP_THRES 10589 90/* Thermal Sensor Register Lock */91#define QRK_DTS_REG_OFFSET_LOCK 0x7192#define QRK_DTS_LOCK_BIT BIT(5)93 94/* Quark DTS has 2 trip points: hot & catastrophic */95#define QRK_MAX_DTS_TRIPS 296 97#define DEFAULT_POLL_DELAY 200098 99struct soc_sensor_entry {100 bool locked;101 u32 store_ptps;102 u32 store_dts_enable;103 struct thermal_zone_device *tzone;104};105 106static struct soc_sensor_entry *soc_dts;107 108static int polling_delay = DEFAULT_POLL_DELAY;109module_param(polling_delay, int, 0644);110MODULE_PARM_DESC(polling_delay,111 "Polling interval for checking trip points (in milliseconds)");112 113static DEFINE_MUTEX(dts_update_mutex);114 115static 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 143static 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 172static 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-bit187 * fields for critical (catastrophic) and hot set trip point188 * thresholds. The threshold value is always offset by its189 * 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 197static int update_trip_temp(struct soc_sensor_entry *aux_entry,198 int trip_index, 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 recommendation219 * regarding the safe trip point threshold value to use, we choose220 * 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-bit227 * fields for critical (catastrophic) and hot set trip point228 * thresholds. The threshold value is always offset by its229 * 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_index * QRK_DTS_SHIFT_TP)));234 out |= (temp_out & QRK_DTS_MASK_TP_THRES) <<235 (trip_index * 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 240failed:241 mutex_unlock(&dts_update_mutex);242 return ret;243}244 245static inline int sys_set_trip_temp(struct thermal_zone_device *tzd,246 const struct thermal_trip *trip,247 int temp)248{249 unsigned int trip_index;250 251 switch (trip->type) {252 case THERMAL_TRIP_HOT:253 trip_index = QRK_DTS_ID_TP_HOT;254 break;255 256 case THERMAL_TRIP_CRITICAL:257 trip_index = QRK_DTS_ID_TP_CRITICAL;258 break;259 260 default:261 return -EINVAL;262 }263 264 return update_trip_temp(thermal_zone_device_priv(tzd), trip_index, temp);265}266 267static int sys_get_curr_temp(struct thermal_zone_device *tzd,268 int *temp)269{270 u32 out;271 int ret;272 273 mutex_lock(&dts_update_mutex);274 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,275 QRK_DTS_REG_OFFSET_TEMP, &out);276 mutex_unlock(&dts_update_mutex);277 278 if (ret)279 return ret;280 281 /*282 * Thermal Sensor Temperature Register has 8-bit field283 * for temperature value (offset by temperature base284 * 50 degree Celsius).285 */286 out = (out >> QRK_DTS_OFFSET_TEMP) & QRK_DTS_MASK_TEMP;287 *temp = out - QRK_DTS_TEMP_BASE;288 289 return 0;290}291 292static int sys_change_mode(struct thermal_zone_device *tzd,293 enum thermal_device_mode mode)294{295 int ret;296 297 mutex_lock(&dts_update_mutex);298 if (mode == THERMAL_DEVICE_ENABLED)299 ret = soc_dts_enable(tzd);300 else301 ret = soc_dts_disable(tzd);302 mutex_unlock(&dts_update_mutex);303 304 return ret;305}306 307static const struct thermal_zone_device_ops tzone_ops = {308 .get_temp = sys_get_curr_temp,309 .set_trip_temp = sys_set_trip_temp,310 .change_mode = sys_change_mode,311};312 313static void free_soc_dts(struct soc_sensor_entry *aux_entry)314{315 if (aux_entry) {316 if (!aux_entry->locked) {317 mutex_lock(&dts_update_mutex);318 iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,319 QRK_DTS_REG_OFFSET_ENABLE,320 aux_entry->store_dts_enable);321 322 iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,323 QRK_DTS_REG_OFFSET_PTPS,324 aux_entry->store_ptps);325 mutex_unlock(&dts_update_mutex);326 }327 thermal_zone_device_unregister(aux_entry->tzone);328 kfree(aux_entry);329 }330}331 332static struct soc_sensor_entry *alloc_soc_dts(void)333{334 struct thermal_trip trips[QRK_MAX_DTS_TRIPS] = { 0 };335 struct soc_sensor_entry *aux_entry;336 int err;337 u32 out;338 339 aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL);340 if (!aux_entry) {341 err = -ENOMEM;342 return ERR_PTR(-ENOMEM);343 }344 345 /* Check if DTS register is locked */346 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,347 QRK_DTS_REG_OFFSET_LOCK, &out);348 if (err)349 goto err_ret;350 351 aux_entry->locked = !!(out & QRK_DTS_LOCK_BIT);352 353 /* Store DTS default state if DTS registers are not locked */354 if (!aux_entry->locked) {355 /* Store DTS default enable for restore on exit */356 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,357 QRK_DTS_REG_OFFSET_ENABLE,358 &aux_entry->store_dts_enable);359 if (err)360 goto err_ret;361 362 /* Store DTS default PTPS register for restore on exit */363 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,364 QRK_DTS_REG_OFFSET_PTPS,365 &aux_entry->store_ptps);366 if (err)367 goto err_ret;368 369 trips[QRK_DTS_ID_TP_CRITICAL].flags |= THERMAL_TRIP_FLAG_RW_TEMP;370 trips[QRK_DTS_ID_TP_HOT].flags |= THERMAL_TRIP_FLAG_RW_TEMP;371 }372 373 trips[QRK_DTS_ID_TP_CRITICAL].temperature = get_trip_temp(QRK_DTS_ID_TP_CRITICAL);374 trips[QRK_DTS_ID_TP_CRITICAL].type = THERMAL_TRIP_CRITICAL;375 376 trips[QRK_DTS_ID_TP_HOT].temperature = get_trip_temp(QRK_DTS_ID_TP_HOT);377 trips[QRK_DTS_ID_TP_HOT].type = THERMAL_TRIP_HOT;378 379 aux_entry->tzone = thermal_zone_device_register_with_trips("quark_dts",380 trips,381 QRK_MAX_DTS_TRIPS,382 aux_entry,383 &tzone_ops,384 NULL, 0, polling_delay);385 if (IS_ERR(aux_entry->tzone)) {386 err = PTR_ERR(aux_entry->tzone);387 goto err_ret;388 }389 390 err = thermal_zone_device_enable(aux_entry->tzone);391 if (err)392 goto err_aux_status;393 394 return aux_entry;395 396err_aux_status:397 thermal_zone_device_unregister(aux_entry->tzone);398err_ret:399 kfree(aux_entry);400 return ERR_PTR(err);401}402 403static const struct x86_cpu_id qrk_thermal_ids[] __initconst = {404 X86_MATCH_VENDOR_FAM_MODEL(INTEL, 5, INTEL_FAM5_QUARK_X1000, NULL),405 {}406};407MODULE_DEVICE_TABLE(x86cpu, qrk_thermal_ids);408 409static int __init intel_quark_thermal_init(void)410{411 if (!x86_match_cpu(qrk_thermal_ids) || !iosf_mbi_available())412 return -ENODEV;413 414 soc_dts = alloc_soc_dts();415 if (IS_ERR(soc_dts))416 return PTR_ERR(soc_dts);417 418 return 0;419}420 421static void __exit intel_quark_thermal_exit(void)422{423 free_soc_dts(soc_dts);424}425 426module_init(intel_quark_thermal_init)427module_exit(intel_quark_thermal_exit)428 429MODULE_DESCRIPTION("Intel Quark DTS Thermal Driver");430MODULE_AUTHOR("Ong Boon Leong <boon.leong.ong@intel.com>");431MODULE_LICENSE("Dual BSD/GPL");432