18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * intel_quark_dts_thermal.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright(c) 2015 Intel Corporation. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 128c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 138c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 168c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 178c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 188c2ecf20Sopenharmony_ci * General Public License for more details. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Contact Information: 218c2ecf20Sopenharmony_ci * Ong Boon Leong <boon.leong.ong@intel.com> 228c2ecf20Sopenharmony_ci * Intel Malaysia, Penang 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * BSD LICENSE 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Copyright(c) 2015 Intel Corporation. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 298c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 308c2ecf20Sopenharmony_ci * are met: 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 338c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 348c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 358c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 368c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 378c2ecf20Sopenharmony_ci * distribution. 388c2ecf20Sopenharmony_ci * * Neither the name of Intel Corporation nor the names of its 398c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 408c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 438c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 448c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 458c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 468c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 478c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 488c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 498c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 508c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 518c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 528c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * Quark DTS thermal driver is implemented by referencing 558c2ecf20Sopenharmony_ci * intel_soc_dts_thermal.c. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#include <linux/module.h> 618c2ecf20Sopenharmony_ci#include <linux/slab.h> 628c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 638c2ecf20Sopenharmony_ci#include <linux/thermal.h> 648c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h> 658c2ecf20Sopenharmony_ci#include <asm/iosf_mbi.h> 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* DTS reset is programmed via QRK_MBI_UNIT_SOC */ 688c2ecf20Sopenharmony_ci#define QRK_DTS_REG_OFFSET_RESET 0x34 698c2ecf20Sopenharmony_ci#define QRK_DTS_RESET_BIT BIT(0) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* DTS enable is programmed via QRK_MBI_UNIT_RMU */ 728c2ecf20Sopenharmony_ci#define QRK_DTS_REG_OFFSET_ENABLE 0xB0 738c2ecf20Sopenharmony_ci#define QRK_DTS_ENABLE_BIT BIT(15) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Temperature Register is read via QRK_MBI_UNIT_RMU */ 768c2ecf20Sopenharmony_ci#define QRK_DTS_REG_OFFSET_TEMP 0xB1 778c2ecf20Sopenharmony_ci#define QRK_DTS_MASK_TEMP 0xFF 788c2ecf20Sopenharmony_ci#define QRK_DTS_OFFSET_TEMP 0 798c2ecf20Sopenharmony_ci#define QRK_DTS_OFFSET_REL_TEMP 16 808c2ecf20Sopenharmony_ci#define QRK_DTS_TEMP_BASE 50 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Programmable Trip Point Register is configured via QRK_MBI_UNIT_RMU */ 838c2ecf20Sopenharmony_ci#define QRK_DTS_REG_OFFSET_PTPS 0xB2 848c2ecf20Sopenharmony_ci#define QRK_DTS_MASK_TP_THRES 0xFF 858c2ecf20Sopenharmony_ci#define QRK_DTS_SHIFT_TP 8 868c2ecf20Sopenharmony_ci#define QRK_DTS_ID_TP_CRITICAL 0 878c2ecf20Sopenharmony_ci#define QRK_DTS_SAFE_TP_THRES 105 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* Thermal Sensor Register Lock */ 908c2ecf20Sopenharmony_ci#define QRK_DTS_REG_OFFSET_LOCK 0x71 918c2ecf20Sopenharmony_ci#define QRK_DTS_LOCK_BIT BIT(5) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Quark DTS has 2 trip points: hot & catastrophic */ 948c2ecf20Sopenharmony_ci#define QRK_MAX_DTS_TRIPS 2 958c2ecf20Sopenharmony_ci/* If DTS not locked, all trip points are configurable */ 968c2ecf20Sopenharmony_ci#define QRK_DTS_WR_MASK_SET 0x3 978c2ecf20Sopenharmony_ci/* If DTS locked, all trip points are not configurable */ 988c2ecf20Sopenharmony_ci#define QRK_DTS_WR_MASK_CLR 0 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define DEFAULT_POLL_DELAY 2000 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct soc_sensor_entry { 1038c2ecf20Sopenharmony_ci bool locked; 1048c2ecf20Sopenharmony_ci u32 store_ptps; 1058c2ecf20Sopenharmony_ci u32 store_dts_enable; 1068c2ecf20Sopenharmony_ci struct thermal_zone_device *tzone; 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct soc_sensor_entry *soc_dts; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int polling_delay = DEFAULT_POLL_DELAY; 1128c2ecf20Sopenharmony_cimodule_param(polling_delay, int, 0644); 1138c2ecf20Sopenharmony_ciMODULE_PARM_DESC(polling_delay, 1148c2ecf20Sopenharmony_ci "Polling interval for checking trip points (in milliseconds)"); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dts_update_mutex); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int soc_dts_enable(struct thermal_zone_device *tzd) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci u32 out; 1218c2ecf20Sopenharmony_ci struct soc_sensor_entry *aux_entry = tzd->devdata; 1228c2ecf20Sopenharmony_ci int ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 1258c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_ENABLE, &out); 1268c2ecf20Sopenharmony_ci if (ret) 1278c2ecf20Sopenharmony_ci return ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (out & QRK_DTS_ENABLE_BIT) 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!aux_entry->locked) { 1338c2ecf20Sopenharmony_ci out |= QRK_DTS_ENABLE_BIT; 1348c2ecf20Sopenharmony_ci ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 1358c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_ENABLE, out); 1368c2ecf20Sopenharmony_ci if (ret) 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci } else { 1398c2ecf20Sopenharmony_ci pr_info("DTS is locked. Cannot enable DTS\n"); 1408c2ecf20Sopenharmony_ci ret = -EPERM; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return ret; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int soc_dts_disable(struct thermal_zone_device *tzd) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci u32 out; 1498c2ecf20Sopenharmony_ci struct soc_sensor_entry *aux_entry = tzd->devdata; 1508c2ecf20Sopenharmony_ci int ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 1538c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_ENABLE, &out); 1548c2ecf20Sopenharmony_ci if (ret) 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!(out & QRK_DTS_ENABLE_BIT)) 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!aux_entry->locked) { 1618c2ecf20Sopenharmony_ci out &= ~QRK_DTS_ENABLE_BIT; 1628c2ecf20Sopenharmony_ci ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 1638c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_ENABLE, out); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (ret) 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci } else { 1688c2ecf20Sopenharmony_ci pr_info("DTS is locked. Cannot disable DTS\n"); 1698c2ecf20Sopenharmony_ci ret = -EPERM; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int _get_trip_temp(int trip, int *temp) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int status; 1788c2ecf20Sopenharmony_ci u32 out; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci mutex_lock(&dts_update_mutex); 1818c2ecf20Sopenharmony_ci status = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 1828c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_PTPS, &out); 1838c2ecf20Sopenharmony_ci mutex_unlock(&dts_update_mutex); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (status) 1868c2ecf20Sopenharmony_ci return status; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * Thermal Sensor Programmable Trip Point Register has 8-bit 1908c2ecf20Sopenharmony_ci * fields for critical (catastrophic) and hot set trip point 1918c2ecf20Sopenharmony_ci * thresholds. The threshold value is always offset by its 1928c2ecf20Sopenharmony_ci * temperature base (50 degree Celsius). 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci *temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES; 1958c2ecf20Sopenharmony_ci *temp -= QRK_DTS_TEMP_BASE; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic inline int sys_get_trip_temp(struct thermal_zone_device *tzd, 2018c2ecf20Sopenharmony_ci int trip, int *temp) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci return _get_trip_temp(trip, temp); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic inline int sys_get_crit_temp(struct thermal_zone_device *tzd, int *temp) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci return _get_trip_temp(QRK_DTS_ID_TP_CRITICAL, temp); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int update_trip_temp(struct soc_sensor_entry *aux_entry, 2128c2ecf20Sopenharmony_ci int trip, int temp) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci u32 out; 2158c2ecf20Sopenharmony_ci u32 temp_out; 2168c2ecf20Sopenharmony_ci u32 store_ptps; 2178c2ecf20Sopenharmony_ci int ret; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci mutex_lock(&dts_update_mutex); 2208c2ecf20Sopenharmony_ci if (aux_entry->locked) { 2218c2ecf20Sopenharmony_ci ret = -EPERM; 2228c2ecf20Sopenharmony_ci goto failed; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 2268c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_PTPS, &store_ptps); 2278c2ecf20Sopenharmony_ci if (ret) 2288c2ecf20Sopenharmony_ci goto failed; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* 2318c2ecf20Sopenharmony_ci * Protection against unsafe trip point thresdhold value. 2328c2ecf20Sopenharmony_ci * As Quark X1000 data-sheet does not provide any recommendation 2338c2ecf20Sopenharmony_ci * regarding the safe trip point threshold value to use, we choose 2348c2ecf20Sopenharmony_ci * the safe value according to the threshold value set by UEFI BIOS. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci if (temp > QRK_DTS_SAFE_TP_THRES) 2378c2ecf20Sopenharmony_ci temp = QRK_DTS_SAFE_TP_THRES; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* 2408c2ecf20Sopenharmony_ci * Thermal Sensor Programmable Trip Point Register has 8-bit 2418c2ecf20Sopenharmony_ci * fields for critical (catastrophic) and hot set trip point 2428c2ecf20Sopenharmony_ci * thresholds. The threshold value is always offset by its 2438c2ecf20Sopenharmony_ci * temperature base (50 degree Celsius). 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci temp_out = temp + QRK_DTS_TEMP_BASE; 2468c2ecf20Sopenharmony_ci out = (store_ptps & ~(QRK_DTS_MASK_TP_THRES << 2478c2ecf20Sopenharmony_ci (trip * QRK_DTS_SHIFT_TP))); 2488c2ecf20Sopenharmony_ci out |= (temp_out & QRK_DTS_MASK_TP_THRES) << 2498c2ecf20Sopenharmony_ci (trip * QRK_DTS_SHIFT_TP); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 2528c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_PTPS, out); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cifailed: 2558c2ecf20Sopenharmony_ci mutex_unlock(&dts_update_mutex); 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, 2608c2ecf20Sopenharmony_ci int temp) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci return update_trip_temp(tzd->devdata, trip, temp); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int sys_get_trip_type(struct thermal_zone_device *thermal, 2668c2ecf20Sopenharmony_ci int trip, enum thermal_trip_type *type) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci if (trip) 2698c2ecf20Sopenharmony_ci *type = THERMAL_TRIP_HOT; 2708c2ecf20Sopenharmony_ci else 2718c2ecf20Sopenharmony_ci *type = THERMAL_TRIP_CRITICAL; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int sys_get_curr_temp(struct thermal_zone_device *tzd, 2778c2ecf20Sopenharmony_ci int *temp) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci u32 out; 2808c2ecf20Sopenharmony_ci int ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci mutex_lock(&dts_update_mutex); 2838c2ecf20Sopenharmony_ci ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 2848c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_TEMP, &out); 2858c2ecf20Sopenharmony_ci mutex_unlock(&dts_update_mutex); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (ret) 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * Thermal Sensor Temperature Register has 8-bit field 2928c2ecf20Sopenharmony_ci * for temperature value (offset by temperature base 2938c2ecf20Sopenharmony_ci * 50 degree Celsius). 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci out = (out >> QRK_DTS_OFFSET_TEMP) & QRK_DTS_MASK_TEMP; 2968c2ecf20Sopenharmony_ci *temp = out - QRK_DTS_TEMP_BASE; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int sys_change_mode(struct thermal_zone_device *tzd, 3028c2ecf20Sopenharmony_ci enum thermal_device_mode mode) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int ret; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci mutex_lock(&dts_update_mutex); 3078c2ecf20Sopenharmony_ci if (mode == THERMAL_DEVICE_ENABLED) 3088c2ecf20Sopenharmony_ci ret = soc_dts_enable(tzd); 3098c2ecf20Sopenharmony_ci else 3108c2ecf20Sopenharmony_ci ret = soc_dts_disable(tzd); 3118c2ecf20Sopenharmony_ci mutex_unlock(&dts_update_mutex); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic struct thermal_zone_device_ops tzone_ops = { 3178c2ecf20Sopenharmony_ci .get_temp = sys_get_curr_temp, 3188c2ecf20Sopenharmony_ci .get_trip_temp = sys_get_trip_temp, 3198c2ecf20Sopenharmony_ci .get_trip_type = sys_get_trip_type, 3208c2ecf20Sopenharmony_ci .set_trip_temp = sys_set_trip_temp, 3218c2ecf20Sopenharmony_ci .get_crit_temp = sys_get_crit_temp, 3228c2ecf20Sopenharmony_ci .change_mode = sys_change_mode, 3238c2ecf20Sopenharmony_ci}; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void free_soc_dts(struct soc_sensor_entry *aux_entry) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci if (aux_entry) { 3288c2ecf20Sopenharmony_ci if (!aux_entry->locked) { 3298c2ecf20Sopenharmony_ci mutex_lock(&dts_update_mutex); 3308c2ecf20Sopenharmony_ci iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 3318c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_ENABLE, 3328c2ecf20Sopenharmony_ci aux_entry->store_dts_enable); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, 3358c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_PTPS, 3368c2ecf20Sopenharmony_ci aux_entry->store_ptps); 3378c2ecf20Sopenharmony_ci mutex_unlock(&dts_update_mutex); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci thermal_zone_device_unregister(aux_entry->tzone); 3408c2ecf20Sopenharmony_ci kfree(aux_entry); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic struct soc_sensor_entry *alloc_soc_dts(void) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct soc_sensor_entry *aux_entry; 3478c2ecf20Sopenharmony_ci int err; 3488c2ecf20Sopenharmony_ci u32 out; 3498c2ecf20Sopenharmony_ci int wr_mask; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL); 3528c2ecf20Sopenharmony_ci if (!aux_entry) { 3538c2ecf20Sopenharmony_ci err = -ENOMEM; 3548c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Check if DTS register is locked */ 3588c2ecf20Sopenharmony_ci err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 3598c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_LOCK, &out); 3608c2ecf20Sopenharmony_ci if (err) 3618c2ecf20Sopenharmony_ci goto err_ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (out & QRK_DTS_LOCK_BIT) { 3648c2ecf20Sopenharmony_ci aux_entry->locked = true; 3658c2ecf20Sopenharmony_ci wr_mask = QRK_DTS_WR_MASK_CLR; 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci aux_entry->locked = false; 3688c2ecf20Sopenharmony_ci wr_mask = QRK_DTS_WR_MASK_SET; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Store DTS default state if DTS registers are not locked */ 3728c2ecf20Sopenharmony_ci if (!aux_entry->locked) { 3738c2ecf20Sopenharmony_ci /* Store DTS default enable for restore on exit */ 3748c2ecf20Sopenharmony_ci err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 3758c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_ENABLE, 3768c2ecf20Sopenharmony_ci &aux_entry->store_dts_enable); 3778c2ecf20Sopenharmony_ci if (err) 3788c2ecf20Sopenharmony_ci goto err_ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* Store DTS default PTPS register for restore on exit */ 3818c2ecf20Sopenharmony_ci err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ, 3828c2ecf20Sopenharmony_ci QRK_DTS_REG_OFFSET_PTPS, 3838c2ecf20Sopenharmony_ci &aux_entry->store_ptps); 3848c2ecf20Sopenharmony_ci if (err) 3858c2ecf20Sopenharmony_ci goto err_ret; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci aux_entry->tzone = thermal_zone_device_register("quark_dts", 3898c2ecf20Sopenharmony_ci QRK_MAX_DTS_TRIPS, 3908c2ecf20Sopenharmony_ci wr_mask, 3918c2ecf20Sopenharmony_ci aux_entry, &tzone_ops, NULL, 0, polling_delay); 3928c2ecf20Sopenharmony_ci if (IS_ERR(aux_entry->tzone)) { 3938c2ecf20Sopenharmony_ci err = PTR_ERR(aux_entry->tzone); 3948c2ecf20Sopenharmony_ci goto err_ret; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci err = thermal_zone_device_enable(aux_entry->tzone); 3988c2ecf20Sopenharmony_ci if (err) 3998c2ecf20Sopenharmony_ci goto err_aux_status; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return aux_entry; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cierr_aux_status: 4048c2ecf20Sopenharmony_ci thermal_zone_device_unregister(aux_entry->tzone); 4058c2ecf20Sopenharmony_cierr_ret: 4068c2ecf20Sopenharmony_ci kfree(aux_entry); 4078c2ecf20Sopenharmony_ci return ERR_PTR(err); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic const struct x86_cpu_id qrk_thermal_ids[] __initconst = { 4118c2ecf20Sopenharmony_ci X86_MATCH_VENDOR_FAM_MODEL(INTEL, 5, INTEL_FAM5_QUARK_X1000, NULL), 4128c2ecf20Sopenharmony_ci {} 4138c2ecf20Sopenharmony_ci}; 4148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, qrk_thermal_ids); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int __init intel_quark_thermal_init(void) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci if (!x86_match_cpu(qrk_thermal_ids) || !iosf_mbi_available()) 4198c2ecf20Sopenharmony_ci return -ENODEV; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci soc_dts = alloc_soc_dts(); 4228c2ecf20Sopenharmony_ci if (IS_ERR(soc_dts)) 4238c2ecf20Sopenharmony_ci return PTR_ERR(soc_dts); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic void __exit intel_quark_thermal_exit(void) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci free_soc_dts(soc_dts); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cimodule_init(intel_quark_thermal_init) 4348c2ecf20Sopenharmony_cimodule_exit(intel_quark_thermal_exit) 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Quark DTS Thermal Driver"); 4378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ong Boon Leong <boon.leong.ong@intel.com>"); 4388c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 439