xref: /linux/drivers/thermal/intel/intel_quark_dts_thermal.c (revision 7f4957be0d5b83c8964491863202136c916107ae)
13e8c4d31SAmit Kucheria /*
23e8c4d31SAmit Kucheria  * intel_quark_dts_thermal.c
33e8c4d31SAmit Kucheria  *
43e8c4d31SAmit Kucheria  * This file is provided under a dual BSD/GPLv2 license.  When using or
53e8c4d31SAmit Kucheria  * redistributing this file, you may do so under either license.
63e8c4d31SAmit Kucheria  *
73e8c4d31SAmit Kucheria  * GPL LICENSE SUMMARY
83e8c4d31SAmit Kucheria  *
93e8c4d31SAmit Kucheria  * Copyright(c) 2015 Intel Corporation.
103e8c4d31SAmit Kucheria  *
113e8c4d31SAmit Kucheria  * This program is free software; you can redistribute it and/or modify
123e8c4d31SAmit Kucheria  * it under the terms of version 2 of the GNU General Public License as
133e8c4d31SAmit Kucheria  * published by the Free Software Foundation.
143e8c4d31SAmit Kucheria  *
153e8c4d31SAmit Kucheria  * This program is distributed in the hope that it will be useful, but
163e8c4d31SAmit Kucheria  *  WITHOUT ANY WARRANTY; without even the implied warranty of
173e8c4d31SAmit Kucheria  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
183e8c4d31SAmit Kucheria  * General Public License for more details.
193e8c4d31SAmit Kucheria  *
203e8c4d31SAmit Kucheria  * Contact Information:
213e8c4d31SAmit Kucheria  *  Ong Boon Leong <boon.leong.ong@intel.com>
223e8c4d31SAmit Kucheria  *  Intel Malaysia, Penang
233e8c4d31SAmit Kucheria  *
243e8c4d31SAmit Kucheria  * BSD LICENSE
253e8c4d31SAmit Kucheria  *
263e8c4d31SAmit Kucheria  * Copyright(c) 2015 Intel Corporation.
273e8c4d31SAmit Kucheria  *
283e8c4d31SAmit Kucheria  * Redistribution and use in source and binary forms, with or without
293e8c4d31SAmit Kucheria  * modification, are permitted provided that the following conditions
303e8c4d31SAmit Kucheria  * are met:
313e8c4d31SAmit Kucheria  *
323e8c4d31SAmit Kucheria  *   * Redistributions of source code must retain the above copyright
333e8c4d31SAmit Kucheria  *     notice, this list of conditions and the following disclaimer.
343e8c4d31SAmit Kucheria  *   * Redistributions in binary form must reproduce the above copyright
353e8c4d31SAmit Kucheria  *     notice, this list of conditions and the following disclaimer in
363e8c4d31SAmit Kucheria  *     the documentation and/or other materials provided with the
373e8c4d31SAmit Kucheria  *     distribution.
383e8c4d31SAmit Kucheria  *   * Neither the name of Intel Corporation nor the names of its
393e8c4d31SAmit Kucheria  *     contributors may be used to endorse or promote products derived
403e8c4d31SAmit Kucheria  *     from this software without specific prior written permission.
413e8c4d31SAmit Kucheria  *
423e8c4d31SAmit Kucheria  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
433e8c4d31SAmit Kucheria  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
443e8c4d31SAmit Kucheria  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
453e8c4d31SAmit Kucheria  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
463e8c4d31SAmit Kucheria  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
473e8c4d31SAmit Kucheria  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
483e8c4d31SAmit Kucheria  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
493e8c4d31SAmit Kucheria  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
503e8c4d31SAmit Kucheria  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
513e8c4d31SAmit Kucheria  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
523e8c4d31SAmit Kucheria  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
533e8c4d31SAmit Kucheria  *
543e8c4d31SAmit Kucheria  * Quark DTS thermal driver is implemented by referencing
553e8c4d31SAmit Kucheria  * intel_soc_dts_thermal.c.
563e8c4d31SAmit Kucheria  */
573e8c4d31SAmit Kucheria 
583e8c4d31SAmit Kucheria #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
593e8c4d31SAmit Kucheria 
603e8c4d31SAmit Kucheria #include <linux/module.h>
613e8c4d31SAmit Kucheria #include <linux/slab.h>
623e8c4d31SAmit Kucheria #include <linux/interrupt.h>
633e8c4d31SAmit Kucheria #include <linux/thermal.h>
643e8c4d31SAmit Kucheria #include <asm/cpu_device_id.h>
653e8c4d31SAmit Kucheria #include <asm/iosf_mbi.h>
663e8c4d31SAmit Kucheria 
673e8c4d31SAmit Kucheria /* DTS reset is programmed via QRK_MBI_UNIT_SOC */
683e8c4d31SAmit Kucheria #define QRK_DTS_REG_OFFSET_RESET	0x34
693e8c4d31SAmit Kucheria #define QRK_DTS_RESET_BIT		BIT(0)
703e8c4d31SAmit Kucheria 
713e8c4d31SAmit Kucheria /* DTS enable is programmed via QRK_MBI_UNIT_RMU */
723e8c4d31SAmit Kucheria #define QRK_DTS_REG_OFFSET_ENABLE	0xB0
733e8c4d31SAmit Kucheria #define QRK_DTS_ENABLE_BIT		BIT(15)
743e8c4d31SAmit Kucheria 
753e8c4d31SAmit Kucheria /* Temperature Register is read via QRK_MBI_UNIT_RMU */
763e8c4d31SAmit Kucheria #define QRK_DTS_REG_OFFSET_TEMP		0xB1
773e8c4d31SAmit Kucheria #define QRK_DTS_MASK_TEMP		0xFF
783e8c4d31SAmit Kucheria #define QRK_DTS_OFFSET_TEMP		0
793e8c4d31SAmit Kucheria #define QRK_DTS_OFFSET_REL_TEMP		16
803e8c4d31SAmit Kucheria #define QRK_DTS_TEMP_BASE		50
813e8c4d31SAmit Kucheria 
823e8c4d31SAmit Kucheria /* Programmable Trip Point Register is configured via QRK_MBI_UNIT_RMU */
833e8c4d31SAmit Kucheria #define QRK_DTS_REG_OFFSET_PTPS		0xB2
843e8c4d31SAmit Kucheria #define QRK_DTS_MASK_TP_THRES		0xFF
853e8c4d31SAmit Kucheria #define QRK_DTS_SHIFT_TP		8
863e8c4d31SAmit Kucheria #define QRK_DTS_ID_TP_CRITICAL		0
873e8c4d31SAmit Kucheria #define QRK_DTS_SAFE_TP_THRES		105
883e8c4d31SAmit Kucheria 
893e8c4d31SAmit Kucheria /* Thermal Sensor Register Lock */
903e8c4d31SAmit Kucheria #define QRK_DTS_REG_OFFSET_LOCK		0x71
913e8c4d31SAmit Kucheria #define QRK_DTS_LOCK_BIT		BIT(5)
923e8c4d31SAmit Kucheria 
933e8c4d31SAmit Kucheria /* Quark DTS has 2 trip points: hot & catastrophic */
943e8c4d31SAmit Kucheria #define QRK_MAX_DTS_TRIPS	2
953e8c4d31SAmit Kucheria /* If DTS not locked, all trip points are configurable */
963e8c4d31SAmit Kucheria #define QRK_DTS_WR_MASK_SET	0x3
973e8c4d31SAmit Kucheria /* If DTS locked, all trip points are not configurable */
983e8c4d31SAmit Kucheria #define QRK_DTS_WR_MASK_CLR	0
993e8c4d31SAmit Kucheria 
1003e8c4d31SAmit Kucheria #define DEFAULT_POLL_DELAY	2000
1013e8c4d31SAmit Kucheria 
1023e8c4d31SAmit Kucheria struct soc_sensor_entry {
1033e8c4d31SAmit Kucheria 	bool locked;
1043e8c4d31SAmit Kucheria 	u32 store_ptps;
1053e8c4d31SAmit Kucheria 	u32 store_dts_enable;
1063e8c4d31SAmit Kucheria 	struct thermal_zone_device *tzone;
1073e8c4d31SAmit Kucheria };
1083e8c4d31SAmit Kucheria 
1093e8c4d31SAmit Kucheria static struct soc_sensor_entry *soc_dts;
1103e8c4d31SAmit Kucheria 
1113e8c4d31SAmit Kucheria static int polling_delay = DEFAULT_POLL_DELAY;
1123e8c4d31SAmit Kucheria module_param(polling_delay, int, 0644);
1133e8c4d31SAmit Kucheria MODULE_PARM_DESC(polling_delay,
1143e8c4d31SAmit Kucheria 	"Polling interval for checking trip points (in milliseconds)");
1153e8c4d31SAmit Kucheria 
1163e8c4d31SAmit Kucheria static DEFINE_MUTEX(dts_update_mutex);
1173e8c4d31SAmit Kucheria 
1183e8c4d31SAmit Kucheria static int soc_dts_enable(struct thermal_zone_device *tzd)
1193e8c4d31SAmit Kucheria {
1203e8c4d31SAmit Kucheria 	u32 out;
1213e8c4d31SAmit Kucheria 	struct soc_sensor_entry *aux_entry = tzd->devdata;
1223e8c4d31SAmit Kucheria 	int ret;
1233e8c4d31SAmit Kucheria 
1243e8c4d31SAmit Kucheria 	ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
1253e8c4d31SAmit Kucheria 			    QRK_DTS_REG_OFFSET_ENABLE, &out);
1263e8c4d31SAmit Kucheria 	if (ret)
1273e8c4d31SAmit Kucheria 		return ret;
1283e8c4d31SAmit Kucheria 
129*7f4957beSAndrzej Pietrasiewicz 	if (out & QRK_DTS_ENABLE_BIT)
1303e8c4d31SAmit Kucheria 		return 0;
1313e8c4d31SAmit Kucheria 
1323e8c4d31SAmit Kucheria 	if (!aux_entry->locked) {
1333e8c4d31SAmit Kucheria 		out |= QRK_DTS_ENABLE_BIT;
1343e8c4d31SAmit Kucheria 		ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
1353e8c4d31SAmit Kucheria 				     QRK_DTS_REG_OFFSET_ENABLE, out);
1363e8c4d31SAmit Kucheria 		if (ret)
1373e8c4d31SAmit Kucheria 			return ret;
1383e8c4d31SAmit Kucheria 	} else {
1393e8c4d31SAmit Kucheria 		pr_info("DTS is locked. Cannot enable DTS\n");
1403e8c4d31SAmit Kucheria 		ret = -EPERM;
1413e8c4d31SAmit Kucheria 	}
1423e8c4d31SAmit Kucheria 
1433e8c4d31SAmit Kucheria 	return ret;
1443e8c4d31SAmit Kucheria }
1453e8c4d31SAmit Kucheria 
1463e8c4d31SAmit Kucheria static int soc_dts_disable(struct thermal_zone_device *tzd)
1473e8c4d31SAmit Kucheria {
1483e8c4d31SAmit Kucheria 	u32 out;
1493e8c4d31SAmit Kucheria 	struct soc_sensor_entry *aux_entry = tzd->devdata;
1503e8c4d31SAmit Kucheria 	int ret;
1513e8c4d31SAmit Kucheria 
1523e8c4d31SAmit Kucheria 	ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
1533e8c4d31SAmit Kucheria 			    QRK_DTS_REG_OFFSET_ENABLE, &out);
1543e8c4d31SAmit Kucheria 	if (ret)
1553e8c4d31SAmit Kucheria 		return ret;
1563e8c4d31SAmit Kucheria 
157*7f4957beSAndrzej Pietrasiewicz 	if (!(out & QRK_DTS_ENABLE_BIT))
1583e8c4d31SAmit Kucheria 		return 0;
1593e8c4d31SAmit Kucheria 
1603e8c4d31SAmit Kucheria 	if (!aux_entry->locked) {
1613e8c4d31SAmit Kucheria 		out &= ~QRK_DTS_ENABLE_BIT;
1623e8c4d31SAmit Kucheria 		ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
1633e8c4d31SAmit Kucheria 				     QRK_DTS_REG_OFFSET_ENABLE, out);
1643e8c4d31SAmit Kucheria 
1653e8c4d31SAmit Kucheria 		if (ret)
1663e8c4d31SAmit Kucheria 			return ret;
1673e8c4d31SAmit Kucheria 	} else {
1683e8c4d31SAmit Kucheria 		pr_info("DTS is locked. Cannot disable DTS\n");
1693e8c4d31SAmit Kucheria 		ret = -EPERM;
1703e8c4d31SAmit Kucheria 	}
1713e8c4d31SAmit Kucheria 
1723e8c4d31SAmit Kucheria 	return ret;
1733e8c4d31SAmit Kucheria }
1743e8c4d31SAmit Kucheria 
1753e8c4d31SAmit Kucheria static int _get_trip_temp(int trip, int *temp)
1763e8c4d31SAmit Kucheria {
1773e8c4d31SAmit Kucheria 	int status;
1783e8c4d31SAmit Kucheria 	u32 out;
1793e8c4d31SAmit Kucheria 
1803e8c4d31SAmit Kucheria 	mutex_lock(&dts_update_mutex);
1813e8c4d31SAmit Kucheria 	status = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
1823e8c4d31SAmit Kucheria 			       QRK_DTS_REG_OFFSET_PTPS, &out);
1833e8c4d31SAmit Kucheria 	mutex_unlock(&dts_update_mutex);
1843e8c4d31SAmit Kucheria 
1853e8c4d31SAmit Kucheria 	if (status)
1863e8c4d31SAmit Kucheria 		return status;
1873e8c4d31SAmit Kucheria 
1883e8c4d31SAmit Kucheria 	/*
1893e8c4d31SAmit Kucheria 	 * Thermal Sensor Programmable Trip Point Register has 8-bit
1903e8c4d31SAmit Kucheria 	 * fields for critical (catastrophic) and hot set trip point
1913e8c4d31SAmit Kucheria 	 * thresholds. The threshold value is always offset by its
1923e8c4d31SAmit Kucheria 	 * temperature base (50 degree Celsius).
1933e8c4d31SAmit Kucheria 	 */
1943e8c4d31SAmit Kucheria 	*temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES;
1953e8c4d31SAmit Kucheria 	*temp -= QRK_DTS_TEMP_BASE;
1963e8c4d31SAmit Kucheria 
1973e8c4d31SAmit Kucheria 	return 0;
1983e8c4d31SAmit Kucheria }
1993e8c4d31SAmit Kucheria 
2003e8c4d31SAmit Kucheria static inline int sys_get_trip_temp(struct thermal_zone_device *tzd,
2013e8c4d31SAmit Kucheria 				int trip, int *temp)
2023e8c4d31SAmit Kucheria {
2033e8c4d31SAmit Kucheria 	return _get_trip_temp(trip, temp);
2043e8c4d31SAmit Kucheria }
2053e8c4d31SAmit Kucheria 
2063e8c4d31SAmit Kucheria static inline int sys_get_crit_temp(struct thermal_zone_device *tzd, int *temp)
2073e8c4d31SAmit Kucheria {
2083e8c4d31SAmit Kucheria 	return _get_trip_temp(QRK_DTS_ID_TP_CRITICAL, temp);
2093e8c4d31SAmit Kucheria }
2103e8c4d31SAmit Kucheria 
2113e8c4d31SAmit Kucheria static int update_trip_temp(struct soc_sensor_entry *aux_entry,
2123e8c4d31SAmit Kucheria 				int trip, int temp)
2133e8c4d31SAmit Kucheria {
2143e8c4d31SAmit Kucheria 	u32 out;
2153e8c4d31SAmit Kucheria 	u32 temp_out;
2163e8c4d31SAmit Kucheria 	u32 store_ptps;
2173e8c4d31SAmit Kucheria 	int ret;
2183e8c4d31SAmit Kucheria 
2193e8c4d31SAmit Kucheria 	mutex_lock(&dts_update_mutex);
2203e8c4d31SAmit Kucheria 	if (aux_entry->locked) {
2213e8c4d31SAmit Kucheria 		ret = -EPERM;
2223e8c4d31SAmit Kucheria 		goto failed;
2233e8c4d31SAmit Kucheria 	}
2243e8c4d31SAmit Kucheria 
2253e8c4d31SAmit Kucheria 	ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
2263e8c4d31SAmit Kucheria 			    QRK_DTS_REG_OFFSET_PTPS, &store_ptps);
2273e8c4d31SAmit Kucheria 	if (ret)
2283e8c4d31SAmit Kucheria 		goto failed;
2293e8c4d31SAmit Kucheria 
2303e8c4d31SAmit Kucheria 	/*
2313e8c4d31SAmit Kucheria 	 * Protection against unsafe trip point thresdhold value.
2323e8c4d31SAmit Kucheria 	 * As Quark X1000 data-sheet does not provide any recommendation
2333e8c4d31SAmit Kucheria 	 * regarding the safe trip point threshold value to use, we choose
2343e8c4d31SAmit Kucheria 	 * the safe value according to the threshold value set by UEFI BIOS.
2353e8c4d31SAmit Kucheria 	 */
2363e8c4d31SAmit Kucheria 	if (temp > QRK_DTS_SAFE_TP_THRES)
2373e8c4d31SAmit Kucheria 		temp = QRK_DTS_SAFE_TP_THRES;
2383e8c4d31SAmit Kucheria 
2393e8c4d31SAmit Kucheria 	/*
2403e8c4d31SAmit Kucheria 	 * Thermal Sensor Programmable Trip Point Register has 8-bit
2413e8c4d31SAmit Kucheria 	 * fields for critical (catastrophic) and hot set trip point
2423e8c4d31SAmit Kucheria 	 * thresholds. The threshold value is always offset by its
2433e8c4d31SAmit Kucheria 	 * temperature base (50 degree Celsius).
2443e8c4d31SAmit Kucheria 	 */
2453e8c4d31SAmit Kucheria 	temp_out = temp + QRK_DTS_TEMP_BASE;
2463e8c4d31SAmit Kucheria 	out = (store_ptps & ~(QRK_DTS_MASK_TP_THRES <<
2473e8c4d31SAmit Kucheria 		(trip * QRK_DTS_SHIFT_TP)));
2483e8c4d31SAmit Kucheria 	out |= (temp_out & QRK_DTS_MASK_TP_THRES) <<
2493e8c4d31SAmit Kucheria 		(trip * QRK_DTS_SHIFT_TP);
2503e8c4d31SAmit Kucheria 
2513e8c4d31SAmit Kucheria 	ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
2523e8c4d31SAmit Kucheria 			     QRK_DTS_REG_OFFSET_PTPS, out);
2533e8c4d31SAmit Kucheria 
2543e8c4d31SAmit Kucheria failed:
2553e8c4d31SAmit Kucheria 	mutex_unlock(&dts_update_mutex);
2563e8c4d31SAmit Kucheria 	return ret;
2573e8c4d31SAmit Kucheria }
2583e8c4d31SAmit Kucheria 
2593e8c4d31SAmit Kucheria static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
2603e8c4d31SAmit Kucheria 				int temp)
2613e8c4d31SAmit Kucheria {
2623e8c4d31SAmit Kucheria 	return update_trip_temp(tzd->devdata, trip, temp);
2633e8c4d31SAmit Kucheria }
2643e8c4d31SAmit Kucheria 
2653e8c4d31SAmit Kucheria static int sys_get_trip_type(struct thermal_zone_device *thermal,
2663e8c4d31SAmit Kucheria 		int trip, enum thermal_trip_type *type)
2673e8c4d31SAmit Kucheria {
2683e8c4d31SAmit Kucheria 	if (trip)
2693e8c4d31SAmit Kucheria 		*type = THERMAL_TRIP_HOT;
2703e8c4d31SAmit Kucheria 	else
2713e8c4d31SAmit Kucheria 		*type = THERMAL_TRIP_CRITICAL;
2723e8c4d31SAmit Kucheria 
2733e8c4d31SAmit Kucheria 	return 0;
2743e8c4d31SAmit Kucheria }
2753e8c4d31SAmit Kucheria 
2763e8c4d31SAmit Kucheria static int sys_get_curr_temp(struct thermal_zone_device *tzd,
2773e8c4d31SAmit Kucheria 				int *temp)
2783e8c4d31SAmit Kucheria {
2793e8c4d31SAmit Kucheria 	u32 out;
2803e8c4d31SAmit Kucheria 	int ret;
2813e8c4d31SAmit Kucheria 
2823e8c4d31SAmit Kucheria 	mutex_lock(&dts_update_mutex);
2833e8c4d31SAmit Kucheria 	ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
2843e8c4d31SAmit Kucheria 			    QRK_DTS_REG_OFFSET_TEMP, &out);
2853e8c4d31SAmit Kucheria 	mutex_unlock(&dts_update_mutex);
2863e8c4d31SAmit Kucheria 
2873e8c4d31SAmit Kucheria 	if (ret)
2883e8c4d31SAmit Kucheria 		return ret;
2893e8c4d31SAmit Kucheria 
2903e8c4d31SAmit Kucheria 	/*
2913e8c4d31SAmit Kucheria 	 * Thermal Sensor Temperature Register has 8-bit field
2923e8c4d31SAmit Kucheria 	 * for temperature value (offset by temperature base
2933e8c4d31SAmit Kucheria 	 * 50 degree Celsius).
2943e8c4d31SAmit Kucheria 	 */
2953e8c4d31SAmit Kucheria 	out = (out >> QRK_DTS_OFFSET_TEMP) & QRK_DTS_MASK_TEMP;
2963e8c4d31SAmit Kucheria 	*temp = out - QRK_DTS_TEMP_BASE;
2973e8c4d31SAmit Kucheria 
2983e8c4d31SAmit Kucheria 	return 0;
2993e8c4d31SAmit Kucheria }
3003e8c4d31SAmit Kucheria 
3013e8c4d31SAmit Kucheria static int sys_set_mode(struct thermal_zone_device *tzd,
3023e8c4d31SAmit Kucheria 				enum thermal_device_mode mode)
3033e8c4d31SAmit Kucheria {
3043e8c4d31SAmit Kucheria 	int ret;
3053e8c4d31SAmit Kucheria 
3063e8c4d31SAmit Kucheria 	mutex_lock(&dts_update_mutex);
3073e8c4d31SAmit Kucheria 	if (mode == THERMAL_DEVICE_ENABLED)
3083e8c4d31SAmit Kucheria 		ret = soc_dts_enable(tzd);
3093e8c4d31SAmit Kucheria 	else
3103e8c4d31SAmit Kucheria 		ret = soc_dts_disable(tzd);
3113e8c4d31SAmit Kucheria 	mutex_unlock(&dts_update_mutex);
3123e8c4d31SAmit Kucheria 
3133e8c4d31SAmit Kucheria 	return ret;
3143e8c4d31SAmit Kucheria }
3153e8c4d31SAmit Kucheria 
3163e8c4d31SAmit Kucheria static struct thermal_zone_device_ops tzone_ops = {
3173e8c4d31SAmit Kucheria 	.get_temp = sys_get_curr_temp,
3183e8c4d31SAmit Kucheria 	.get_trip_temp = sys_get_trip_temp,
3193e8c4d31SAmit Kucheria 	.get_trip_type = sys_get_trip_type,
3203e8c4d31SAmit Kucheria 	.set_trip_temp = sys_set_trip_temp,
3213e8c4d31SAmit Kucheria 	.get_crit_temp = sys_get_crit_temp,
3223e8c4d31SAmit Kucheria 	.set_mode = sys_set_mode,
3233e8c4d31SAmit Kucheria };
3243e8c4d31SAmit Kucheria 
3253e8c4d31SAmit Kucheria static void free_soc_dts(struct soc_sensor_entry *aux_entry)
3263e8c4d31SAmit Kucheria {
3273e8c4d31SAmit Kucheria 	if (aux_entry) {
3283e8c4d31SAmit Kucheria 		if (!aux_entry->locked) {
3293e8c4d31SAmit Kucheria 			mutex_lock(&dts_update_mutex);
3303e8c4d31SAmit Kucheria 			iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
3313e8c4d31SAmit Kucheria 				       QRK_DTS_REG_OFFSET_ENABLE,
3323e8c4d31SAmit Kucheria 				       aux_entry->store_dts_enable);
3333e8c4d31SAmit Kucheria 
3343e8c4d31SAmit Kucheria 			iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
3353e8c4d31SAmit Kucheria 				       QRK_DTS_REG_OFFSET_PTPS,
3363e8c4d31SAmit Kucheria 				       aux_entry->store_ptps);
3373e8c4d31SAmit Kucheria 			mutex_unlock(&dts_update_mutex);
3383e8c4d31SAmit Kucheria 		}
3393e8c4d31SAmit Kucheria 		thermal_zone_device_unregister(aux_entry->tzone);
3403e8c4d31SAmit Kucheria 		kfree(aux_entry);
3413e8c4d31SAmit Kucheria 	}
3423e8c4d31SAmit Kucheria }
3433e8c4d31SAmit Kucheria 
3443e8c4d31SAmit Kucheria static struct soc_sensor_entry *alloc_soc_dts(void)
3453e8c4d31SAmit Kucheria {
3463e8c4d31SAmit Kucheria 	struct soc_sensor_entry *aux_entry;
3473e8c4d31SAmit Kucheria 	int err;
3483e8c4d31SAmit Kucheria 	u32 out;
3493e8c4d31SAmit Kucheria 	int wr_mask;
3503e8c4d31SAmit Kucheria 
3513e8c4d31SAmit Kucheria 	aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL);
3523e8c4d31SAmit Kucheria 	if (!aux_entry) {
3533e8c4d31SAmit Kucheria 		err = -ENOMEM;
3543e8c4d31SAmit Kucheria 		return ERR_PTR(-ENOMEM);
3553e8c4d31SAmit Kucheria 	}
3563e8c4d31SAmit Kucheria 
3573e8c4d31SAmit Kucheria 	/* Check if DTS register is locked */
3583e8c4d31SAmit Kucheria 	err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
3593e8c4d31SAmit Kucheria 			    QRK_DTS_REG_OFFSET_LOCK, &out);
3603e8c4d31SAmit Kucheria 	if (err)
3613e8c4d31SAmit Kucheria 		goto err_ret;
3623e8c4d31SAmit Kucheria 
3633e8c4d31SAmit Kucheria 	if (out & QRK_DTS_LOCK_BIT) {
3643e8c4d31SAmit Kucheria 		aux_entry->locked = true;
3653e8c4d31SAmit Kucheria 		wr_mask = QRK_DTS_WR_MASK_CLR;
3663e8c4d31SAmit Kucheria 	} else {
3673e8c4d31SAmit Kucheria 		aux_entry->locked = false;
3683e8c4d31SAmit Kucheria 		wr_mask = QRK_DTS_WR_MASK_SET;
3693e8c4d31SAmit Kucheria 	}
3703e8c4d31SAmit Kucheria 
3713e8c4d31SAmit Kucheria 	/* Store DTS default state if DTS registers are not locked */
3723e8c4d31SAmit Kucheria 	if (!aux_entry->locked) {
3733e8c4d31SAmit Kucheria 		/* Store DTS default enable for restore on exit */
3743e8c4d31SAmit Kucheria 		err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
3753e8c4d31SAmit Kucheria 				    QRK_DTS_REG_OFFSET_ENABLE,
3763e8c4d31SAmit Kucheria 				    &aux_entry->store_dts_enable);
3773e8c4d31SAmit Kucheria 		if (err)
3783e8c4d31SAmit Kucheria 			goto err_ret;
3793e8c4d31SAmit Kucheria 
3803e8c4d31SAmit Kucheria 		/* Store DTS default PTPS register for restore on exit */
3813e8c4d31SAmit Kucheria 		err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
3823e8c4d31SAmit Kucheria 				    QRK_DTS_REG_OFFSET_PTPS,
3833e8c4d31SAmit Kucheria 				    &aux_entry->store_ptps);
3843e8c4d31SAmit Kucheria 		if (err)
3853e8c4d31SAmit Kucheria 			goto err_ret;
3863e8c4d31SAmit Kucheria 	}
3873e8c4d31SAmit Kucheria 
3883e8c4d31SAmit Kucheria 	aux_entry->tzone = thermal_zone_device_register("quark_dts",
3893e8c4d31SAmit Kucheria 			QRK_MAX_DTS_TRIPS,
3903e8c4d31SAmit Kucheria 			wr_mask,
3913e8c4d31SAmit Kucheria 			aux_entry, &tzone_ops, NULL, 0, polling_delay);
3923e8c4d31SAmit Kucheria 	if (IS_ERR(aux_entry->tzone)) {
3933e8c4d31SAmit Kucheria 		err = PTR_ERR(aux_entry->tzone);
3943e8c4d31SAmit Kucheria 		goto err_ret;
3953e8c4d31SAmit Kucheria 	}
3963e8c4d31SAmit Kucheria 
397*7f4957beSAndrzej Pietrasiewicz 	err = thermal_zone_device_enable(aux_entry->tzone);
3983e8c4d31SAmit Kucheria 	if (err)
3993e8c4d31SAmit Kucheria 		goto err_aux_status;
4003e8c4d31SAmit Kucheria 
4013e8c4d31SAmit Kucheria 	return aux_entry;
4023e8c4d31SAmit Kucheria 
4033e8c4d31SAmit Kucheria err_aux_status:
4043e8c4d31SAmit Kucheria 	thermal_zone_device_unregister(aux_entry->tzone);
4053e8c4d31SAmit Kucheria err_ret:
4063e8c4d31SAmit Kucheria 	kfree(aux_entry);
4073e8c4d31SAmit Kucheria 	return ERR_PTR(err);
4083e8c4d31SAmit Kucheria }
4093e8c4d31SAmit Kucheria 
4103e8c4d31SAmit Kucheria static const struct x86_cpu_id qrk_thermal_ids[] __initconst  = {
4119c51044cSThomas Gleixner 	X86_MATCH_VENDOR_FAM_MODEL(INTEL, 5, INTEL_FAM5_QUARK_X1000, NULL),
4123e8c4d31SAmit Kucheria 	{}
4133e8c4d31SAmit Kucheria };
4143e8c4d31SAmit Kucheria MODULE_DEVICE_TABLE(x86cpu, qrk_thermal_ids);
4153e8c4d31SAmit Kucheria 
4163e8c4d31SAmit Kucheria static int __init intel_quark_thermal_init(void)
4173e8c4d31SAmit Kucheria {
4183e8c4d31SAmit Kucheria 	int err = 0;
4193e8c4d31SAmit Kucheria 
4203e8c4d31SAmit Kucheria 	if (!x86_match_cpu(qrk_thermal_ids) || !iosf_mbi_available())
4213e8c4d31SAmit Kucheria 		return -ENODEV;
4223e8c4d31SAmit Kucheria 
4233e8c4d31SAmit Kucheria 	soc_dts = alloc_soc_dts();
4243e8c4d31SAmit Kucheria 	if (IS_ERR(soc_dts)) {
4253e8c4d31SAmit Kucheria 		err = PTR_ERR(soc_dts);
4263e8c4d31SAmit Kucheria 		goto err_free;
4273e8c4d31SAmit Kucheria 	}
4283e8c4d31SAmit Kucheria 
4293e8c4d31SAmit Kucheria 	return 0;
4303e8c4d31SAmit Kucheria 
4313e8c4d31SAmit Kucheria err_free:
4323e8c4d31SAmit Kucheria 	free_soc_dts(soc_dts);
4333e8c4d31SAmit Kucheria 	return err;
4343e8c4d31SAmit Kucheria }
4353e8c4d31SAmit Kucheria 
4363e8c4d31SAmit Kucheria static void __exit intel_quark_thermal_exit(void)
4373e8c4d31SAmit Kucheria {
4383e8c4d31SAmit Kucheria 	free_soc_dts(soc_dts);
4393e8c4d31SAmit Kucheria }
4403e8c4d31SAmit Kucheria 
4413e8c4d31SAmit Kucheria module_init(intel_quark_thermal_init)
4423e8c4d31SAmit Kucheria module_exit(intel_quark_thermal_exit)
4433e8c4d31SAmit Kucheria 
4443e8c4d31SAmit Kucheria MODULE_DESCRIPTION("Intel Quark DTS Thermal Driver");
4453e8c4d31SAmit Kucheria MODULE_AUTHOR("Ong Boon Leong <boon.leong.ong@intel.com>");
4463e8c4d31SAmit Kucheria MODULE_LICENSE("Dual BSD/GPL");
447