18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel MID platform thermal driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Durgadoss R <durgadoss.r@intel.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "intel_mid_thermal: " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/intel_msic.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/param.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/pm.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/thermal.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Number of thermal sensors */ 238c2ecf20Sopenharmony_ci#define MSIC_THERMAL_SENSORS 4 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* ADC1 - thermal registers */ 268c2ecf20Sopenharmony_ci#define MSIC_ADC_ENBL 0x10 278c2ecf20Sopenharmony_ci#define MSIC_ADC_START 0x08 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define MSIC_ADCTHERM_ENBL 0x04 308c2ecf20Sopenharmony_ci#define MSIC_ADCRRDATA_ENBL 0x05 318c2ecf20Sopenharmony_ci#define MSIC_CHANL_MASK_VAL 0x0F 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MSIC_STOPBIT_MASK 16 348c2ecf20Sopenharmony_ci#define MSIC_ADCTHERM_MASK 4 358c2ecf20Sopenharmony_ci/* Number of ADC channels */ 368c2ecf20Sopenharmony_ci#define ADC_CHANLS_MAX 15 378c2ecf20Sopenharmony_ci#define ADC_LOOP_MAX (ADC_CHANLS_MAX - MSIC_THERMAL_SENSORS) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* ADC channel code values */ 408c2ecf20Sopenharmony_ci#define SKIN_SENSOR0_CODE 0x08 418c2ecf20Sopenharmony_ci#define SKIN_SENSOR1_CODE 0x09 428c2ecf20Sopenharmony_ci#define SYS_SENSOR_CODE 0x0A 438c2ecf20Sopenharmony_ci#define MSIC_DIE_SENSOR_CODE 0x03 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define SKIN_THERM_SENSOR0 0 468c2ecf20Sopenharmony_ci#define SKIN_THERM_SENSOR1 1 478c2ecf20Sopenharmony_ci#define SYS_THERM_SENSOR2 2 488c2ecf20Sopenharmony_ci#define MSIC_DIE_THERM_SENSOR3 3 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* ADC code range */ 518c2ecf20Sopenharmony_ci#define ADC_MAX 977 528c2ecf20Sopenharmony_ci#define ADC_MIN 162 538c2ecf20Sopenharmony_ci#define ADC_VAL0C 887 548c2ecf20Sopenharmony_ci#define ADC_VAL20C 720 558c2ecf20Sopenharmony_ci#define ADC_VAL40C 508 568c2ecf20Sopenharmony_ci#define ADC_VAL60C 315 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* ADC base addresses */ 598c2ecf20Sopenharmony_ci#define ADC_CHNL_START_ADDR INTEL_MSIC_ADC1ADDR0 /* increments by 1 */ 608c2ecf20Sopenharmony_ci#define ADC_DATA_START_ADDR INTEL_MSIC_ADC1SNS0H /* increments by 2 */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* MSIC die attributes */ 638c2ecf20Sopenharmony_ci#define MSIC_DIE_ADC_MIN 488 648c2ecf20Sopenharmony_ci#define MSIC_DIE_ADC_MAX 1004 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* This holds the address of the first free ADC channel, 678c2ecf20Sopenharmony_ci * among the 15 channels 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_cistatic int channel_index; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct platform_info { 728c2ecf20Sopenharmony_ci struct platform_device *pdev; 738c2ecf20Sopenharmony_ci struct thermal_zone_device *tzd[MSIC_THERMAL_SENSORS]; 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct thermal_device_info { 778c2ecf20Sopenharmony_ci unsigned int chnl_addr; 788c2ecf20Sopenharmony_ci int direct; 798c2ecf20Sopenharmony_ci /* This holds the current temperature in millidegree celsius */ 808c2ecf20Sopenharmony_ci long curr_temp; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/** 848c2ecf20Sopenharmony_ci * to_msic_die_temp - converts adc_val to msic_die temperature 858c2ecf20Sopenharmony_ci * @adc_val: ADC value to be converted 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Can sleep 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic int to_msic_die_temp(uint16_t adc_val) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci return (368 * (adc_val) / 1000) - 220; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/** 958c2ecf20Sopenharmony_ci * is_valid_adc - checks whether the adc code is within the defined range 968c2ecf20Sopenharmony_ci * @min: minimum value for the sensor 978c2ecf20Sopenharmony_ci * @max: maximum value for the sensor 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * Can sleep 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci return (adc_val >= min) && (adc_val <= max); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/** 1078c2ecf20Sopenharmony_ci * adc_to_temp - converts the ADC code to temperature in C 1088c2ecf20Sopenharmony_ci * @direct: true if ths channel is direct index 1098c2ecf20Sopenharmony_ci * @adc_val: the adc_val that needs to be converted 1108c2ecf20Sopenharmony_ci * @tp: temperature return value 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * Linear approximation is used to covert the skin adc value into temperature. 1138c2ecf20Sopenharmony_ci * This technique is used to avoid very long look-up table to get 1148c2ecf20Sopenharmony_ci * the appropriate temp value from ADC value. 1158c2ecf20Sopenharmony_ci * The adc code vs sensor temp curve is split into five parts 1168c2ecf20Sopenharmony_ci * to achieve very close approximate temp value with less than 1178c2ecf20Sopenharmony_ci * 0.5C error 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic int adc_to_temp(int direct, uint16_t adc_val, int *tp) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci int temp; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Direct conversion for die temperature */ 1248c2ecf20Sopenharmony_ci if (direct) { 1258c2ecf20Sopenharmony_ci if (is_valid_adc(adc_val, MSIC_DIE_ADC_MIN, MSIC_DIE_ADC_MAX)) { 1268c2ecf20Sopenharmony_ci *tp = to_msic_die_temp(adc_val) * 1000; 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci return -ERANGE; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!is_valid_adc(adc_val, ADC_MIN, ADC_MAX)) 1338c2ecf20Sopenharmony_ci return -ERANGE; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Linear approximation for skin temperature */ 1368c2ecf20Sopenharmony_ci if (adc_val > ADC_VAL0C) 1378c2ecf20Sopenharmony_ci temp = 177 - (adc_val/5); 1388c2ecf20Sopenharmony_ci else if ((adc_val <= ADC_VAL0C) && (adc_val > ADC_VAL20C)) 1398c2ecf20Sopenharmony_ci temp = 111 - (adc_val/8); 1408c2ecf20Sopenharmony_ci else if ((adc_val <= ADC_VAL20C) && (adc_val > ADC_VAL40C)) 1418c2ecf20Sopenharmony_ci temp = 92 - (adc_val/10); 1428c2ecf20Sopenharmony_ci else if ((adc_val <= ADC_VAL40C) && (adc_val > ADC_VAL60C)) 1438c2ecf20Sopenharmony_ci temp = 91 - (adc_val/10); 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci temp = 112 - (adc_val/6); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Convert temperature in celsius to milli degree celsius */ 1488c2ecf20Sopenharmony_ci *tp = temp * 1000; 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * mid_read_temp - read sensors for temperature 1548c2ecf20Sopenharmony_ci * @temp: holds the current temperature for the sensor after reading 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * reads the adc_code from the channel and converts it to real 1578c2ecf20Sopenharmony_ci * temperature. The converted value is stored in temp. 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * Can sleep 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic int mid_read_temp(struct thermal_zone_device *tzd, int *temp) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct thermal_device_info *td_info = tzd->devdata; 1648c2ecf20Sopenharmony_ci uint16_t adc_val, addr; 1658c2ecf20Sopenharmony_ci uint8_t data = 0; 1668c2ecf20Sopenharmony_ci int ret; 1678c2ecf20Sopenharmony_ci int curr_temp; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci addr = td_info->chnl_addr; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Enable the msic for conversion before reading */ 1728c2ecf20Sopenharmony_ci ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCRRDATA_ENBL); 1738c2ecf20Sopenharmony_ci if (ret) 1748c2ecf20Sopenharmony_ci return ret; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* Re-toggle the RRDATARD bit (temporary workaround) */ 1778c2ecf20Sopenharmony_ci ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCTHERM_ENBL); 1788c2ecf20Sopenharmony_ci if (ret) 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Read the higher bits of data */ 1828c2ecf20Sopenharmony_ci ret = intel_msic_reg_read(addr, &data); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Shift bits to accommodate the lower two data bits */ 1878c2ecf20Sopenharmony_ci adc_val = (data << 2); 1888c2ecf20Sopenharmony_ci addr++; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = intel_msic_reg_read(addr, &data);/* Read lower bits */ 1918c2ecf20Sopenharmony_ci if (ret) 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Adding lower two bits to the higher bits */ 1958c2ecf20Sopenharmony_ci data &= 03; 1968c2ecf20Sopenharmony_ci adc_val += data; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Convert ADC value to temperature */ 1998c2ecf20Sopenharmony_ci ret = adc_to_temp(td_info->direct, adc_val, &curr_temp); 2008c2ecf20Sopenharmony_ci if (ret == 0) 2018c2ecf20Sopenharmony_ci *temp = td_info->curr_temp = curr_temp; 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/** 2068c2ecf20Sopenharmony_ci * configure_adc - enables/disables the ADC for conversion 2078c2ecf20Sopenharmony_ci * @val: zero: disables the ADC non-zero:enables the ADC 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * Enable/Disable the ADC depending on the argument 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Can sleep 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_cistatic int configure_adc(int val) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci int ret; 2168c2ecf20Sopenharmony_ci uint8_t data; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data); 2198c2ecf20Sopenharmony_ci if (ret) 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (val) { 2238c2ecf20Sopenharmony_ci /* Enable and start the ADC */ 2248c2ecf20Sopenharmony_ci data |= (MSIC_ADC_ENBL | MSIC_ADC_START); 2258c2ecf20Sopenharmony_ci } else { 2268c2ecf20Sopenharmony_ci /* Just stop the ADC */ 2278c2ecf20Sopenharmony_ci data &= (~MSIC_ADC_START); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci return intel_msic_reg_write(INTEL_MSIC_ADC1CNTL1, data); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/** 2338c2ecf20Sopenharmony_ci * set_up_therm_channel - enable thermal channel for conversion 2348c2ecf20Sopenharmony_ci * @base_addr: index of free msic ADC channel 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * Enable all the three channels for conversion 2378c2ecf20Sopenharmony_ci * 2388c2ecf20Sopenharmony_ci * Can sleep 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic int set_up_therm_channel(u16 base_addr) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci int ret; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Enable all the sensor channels */ 2458c2ecf20Sopenharmony_ci ret = intel_msic_reg_write(base_addr, SKIN_SENSOR0_CODE); 2468c2ecf20Sopenharmony_ci if (ret) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ret = intel_msic_reg_write(base_addr + 1, SKIN_SENSOR1_CODE); 2508c2ecf20Sopenharmony_ci if (ret) 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ret = intel_msic_reg_write(base_addr + 2, SYS_SENSOR_CODE); 2548c2ecf20Sopenharmony_ci if (ret) 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Since this is the last channel, set the stop bit 2588c2ecf20Sopenharmony_ci * to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ 2598c2ecf20Sopenharmony_ci ret = intel_msic_reg_write(base_addr + 3, 2608c2ecf20Sopenharmony_ci (MSIC_DIE_SENSOR_CODE | 0x10)); 2618c2ecf20Sopenharmony_ci if (ret) 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Enable ADC and start it */ 2658c2ecf20Sopenharmony_ci return configure_adc(1); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/** 2698c2ecf20Sopenharmony_ci * reset_stopbit - sets the stop bit to 0 on the given channel 2708c2ecf20Sopenharmony_ci * @addr: address of the channel 2718c2ecf20Sopenharmony_ci * 2728c2ecf20Sopenharmony_ci * Can sleep 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_cistatic int reset_stopbit(uint16_t addr) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci int ret; 2778c2ecf20Sopenharmony_ci uint8_t data; 2788c2ecf20Sopenharmony_ci ret = intel_msic_reg_read(addr, &data); 2798c2ecf20Sopenharmony_ci if (ret) 2808c2ecf20Sopenharmony_ci return ret; 2818c2ecf20Sopenharmony_ci /* Set the stop bit to zero */ 2828c2ecf20Sopenharmony_ci return intel_msic_reg_write(addr, (data & 0xEF)); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/** 2868c2ecf20Sopenharmony_ci * find_free_channel - finds an empty channel for conversion 2878c2ecf20Sopenharmony_ci * 2888c2ecf20Sopenharmony_ci * If the ADC is not enabled then start using 0th channel 2898c2ecf20Sopenharmony_ci * itself. Otherwise find an empty channel by looking for a 2908c2ecf20Sopenharmony_ci * channel in which the stopbit is set to 1. returns the index 2918c2ecf20Sopenharmony_ci * of the first free channel if succeeds or an error code. 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * Context: can sleep 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * FIXME: Ultimately the channel allocator will move into the intel_scu_ipc 2968c2ecf20Sopenharmony_ci * code. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_cistatic int find_free_channel(void) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci int ret; 3018c2ecf20Sopenharmony_ci int i; 3028c2ecf20Sopenharmony_ci uint8_t data; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* check whether ADC is enabled */ 3058c2ecf20Sopenharmony_ci ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data); 3068c2ecf20Sopenharmony_ci if (ret) 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if ((data & MSIC_ADC_ENBL) == 0) 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* ADC is already enabled; Looking for an empty channel */ 3138c2ecf20Sopenharmony_ci for (i = 0; i < ADC_CHANLS_MAX; i++) { 3148c2ecf20Sopenharmony_ci ret = intel_msic_reg_read(ADC_CHNL_START_ADDR + i, &data); 3158c2ecf20Sopenharmony_ci if (ret) 3168c2ecf20Sopenharmony_ci return ret; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (data & MSIC_STOPBIT_MASK) { 3198c2ecf20Sopenharmony_ci ret = i; 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/** 3278c2ecf20Sopenharmony_ci * mid_initialize_adc - initializing the ADC 3288c2ecf20Sopenharmony_ci * @dev: our device structure 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * Initialize the ADC for reading thermistor values. Can sleep. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_cistatic int mid_initialize_adc(struct device *dev) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci u8 data; 3358c2ecf20Sopenharmony_ci u16 base_addr; 3368c2ecf20Sopenharmony_ci int ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* 3398c2ecf20Sopenharmony_ci * Ensure that adctherm is disabled before we 3408c2ecf20Sopenharmony_ci * initialize the ADC 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL3, &data); 3438c2ecf20Sopenharmony_ci if (ret) 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci data &= ~MSIC_ADCTHERM_MASK; 3478c2ecf20Sopenharmony_ci ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, data); 3488c2ecf20Sopenharmony_ci if (ret) 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Index of the first channel in which the stop bit is set */ 3528c2ecf20Sopenharmony_ci channel_index = find_free_channel(); 3538c2ecf20Sopenharmony_ci if (channel_index < 0) { 3548c2ecf20Sopenharmony_ci dev_err(dev, "No free ADC channels"); 3558c2ecf20Sopenharmony_ci return channel_index; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci base_addr = ADC_CHNL_START_ADDR + channel_index; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) { 3618c2ecf20Sopenharmony_ci /* Reset stop bit for channels other than 0 and 12 */ 3628c2ecf20Sopenharmony_ci ret = reset_stopbit(base_addr); 3638c2ecf20Sopenharmony_ci if (ret) 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* Index of the first free channel */ 3678c2ecf20Sopenharmony_ci base_addr++; 3688c2ecf20Sopenharmony_ci channel_index++; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ret = set_up_therm_channel(base_addr); 3728c2ecf20Sopenharmony_ci if (ret) { 3738c2ecf20Sopenharmony_ci dev_err(dev, "unable to enable ADC"); 3748c2ecf20Sopenharmony_ci return ret; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci dev_dbg(dev, "ADC initialization successful"); 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/** 3818c2ecf20Sopenharmony_ci * initialize_sensor - sets default temp and timer ranges 3828c2ecf20Sopenharmony_ci * @index: index of the sensor 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * Context: can sleep 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic struct thermal_device_info *initialize_sensor(int index) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct thermal_device_info *td_info = 3898c2ecf20Sopenharmony_ci kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!td_info) 3928c2ecf20Sopenharmony_ci return NULL; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Set the base addr of the channel for this sensor */ 3958c2ecf20Sopenharmony_ci td_info->chnl_addr = ADC_DATA_START_ADDR + 2 * (channel_index + index); 3968c2ecf20Sopenharmony_ci /* Sensor 3 is direct conversion */ 3978c2ecf20Sopenharmony_ci if (index == 3) 3988c2ecf20Sopenharmony_ci td_info->direct = 1; 3998c2ecf20Sopenharmony_ci return td_info; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4038c2ecf20Sopenharmony_ci/** 4048c2ecf20Sopenharmony_ci * mid_thermal_resume - resume routine 4058c2ecf20Sopenharmony_ci * @dev: device structure 4068c2ecf20Sopenharmony_ci * 4078c2ecf20Sopenharmony_ci * mid thermal resume: re-initializes the adc. Can sleep. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_cistatic int mid_thermal_resume(struct device *dev) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci return mid_initialize_adc(dev); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/** 4158c2ecf20Sopenharmony_ci * mid_thermal_suspend - suspend routine 4168c2ecf20Sopenharmony_ci * @dev: device structure 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * mid thermal suspend implements the suspend functionality 4198c2ecf20Sopenharmony_ci * by stopping the ADC. Can sleep. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_cistatic int mid_thermal_suspend(struct device *dev) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci /* 4248c2ecf20Sopenharmony_ci * This just stops the ADC and does not disable it. 4258c2ecf20Sopenharmony_ci * temporary workaround until we have a generic ADC driver. 4268c2ecf20Sopenharmony_ci * If 0 is passed, it disables the ADC. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ci return configure_adc(0); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci#endif 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mid_thermal_pm, 4338c2ecf20Sopenharmony_ci mid_thermal_suspend, mid_thermal_resume); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/** 4368c2ecf20Sopenharmony_ci * read_curr_temp - reads the current temperature and stores in temp 4378c2ecf20Sopenharmony_ci * @temp: holds the current temperature value after reading 4388c2ecf20Sopenharmony_ci * 4398c2ecf20Sopenharmony_ci * Can sleep 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_cistatic int read_curr_temp(struct thermal_zone_device *tzd, int *temp) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci WARN_ON(tzd == NULL); 4448c2ecf20Sopenharmony_ci return mid_read_temp(tzd, temp); 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* Can't be const */ 4488c2ecf20Sopenharmony_cistatic struct thermal_zone_device_ops tzd_ops = { 4498c2ecf20Sopenharmony_ci .get_temp = read_curr_temp, 4508c2ecf20Sopenharmony_ci}; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/** 4538c2ecf20Sopenharmony_ci * mid_thermal_probe - mfld thermal initialize 4548c2ecf20Sopenharmony_ci * @pdev: platform device structure 4558c2ecf20Sopenharmony_ci * 4568c2ecf20Sopenharmony_ci * mid thermal probe initializes the hardware and registers 4578c2ecf20Sopenharmony_ci * all the sensors with the generic thermal framework. Can sleep. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_cistatic int mid_thermal_probe(struct platform_device *pdev) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci static char *name[MSIC_THERMAL_SENSORS] = { 4628c2ecf20Sopenharmony_ci "skin0", "skin1", "sys", "msicdie" 4638c2ecf20Sopenharmony_ci }; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci int ret; 4668c2ecf20Sopenharmony_ci int i; 4678c2ecf20Sopenharmony_ci struct platform_info *pinfo; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci pinfo = devm_kzalloc(&pdev->dev, sizeof(struct platform_info), 4708c2ecf20Sopenharmony_ci GFP_KERNEL); 4718c2ecf20Sopenharmony_ci if (!pinfo) 4728c2ecf20Sopenharmony_ci return -ENOMEM; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Initializing the hardware */ 4758c2ecf20Sopenharmony_ci ret = mid_initialize_adc(&pdev->dev); 4768c2ecf20Sopenharmony_ci if (ret) { 4778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ADC init failed"); 4788c2ecf20Sopenharmony_ci return ret; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Register each sensor with the generic thermal framework*/ 4828c2ecf20Sopenharmony_ci for (i = 0; i < MSIC_THERMAL_SENSORS; i++) { 4838c2ecf20Sopenharmony_ci struct thermal_device_info *td_info = initialize_sensor(i); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (!td_info) { 4868c2ecf20Sopenharmony_ci ret = -ENOMEM; 4878c2ecf20Sopenharmony_ci goto err; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci pinfo->tzd[i] = thermal_zone_device_register(name[i], 4908c2ecf20Sopenharmony_ci 0, 0, td_info, &tzd_ops, NULL, 0, 0); 4918c2ecf20Sopenharmony_ci if (IS_ERR(pinfo->tzd[i])) { 4928c2ecf20Sopenharmony_ci kfree(td_info); 4938c2ecf20Sopenharmony_ci ret = PTR_ERR(pinfo->tzd[i]); 4948c2ecf20Sopenharmony_ci goto err; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci ret = thermal_zone_device_enable(pinfo->tzd[i]); 4978c2ecf20Sopenharmony_ci if (ret) { 4988c2ecf20Sopenharmony_ci kfree(td_info); 4998c2ecf20Sopenharmony_ci thermal_zone_device_unregister(pinfo->tzd[i]); 5008c2ecf20Sopenharmony_ci goto err; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci pinfo->pdev = pdev; 5058c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pinfo); 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cierr: 5098c2ecf20Sopenharmony_ci while (--i >= 0) { 5108c2ecf20Sopenharmony_ci kfree(pinfo->tzd[i]->devdata); 5118c2ecf20Sopenharmony_ci thermal_zone_device_unregister(pinfo->tzd[i]); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci configure_adc(0); 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci/** 5188c2ecf20Sopenharmony_ci * mid_thermal_remove - mfld thermal finalize 5198c2ecf20Sopenharmony_ci * @dev: platform device structure 5208c2ecf20Sopenharmony_ci * 5218c2ecf20Sopenharmony_ci * MLFD thermal remove unregisters all the sensors from the generic 5228c2ecf20Sopenharmony_ci * thermal framework. Can sleep. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_cistatic int mid_thermal_remove(struct platform_device *pdev) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci int i; 5278c2ecf20Sopenharmony_ci struct platform_info *pinfo = platform_get_drvdata(pdev); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci for (i = 0; i < MSIC_THERMAL_SENSORS; i++) { 5308c2ecf20Sopenharmony_ci kfree(pinfo->tzd[i]->devdata); 5318c2ecf20Sopenharmony_ci thermal_zone_device_unregister(pinfo->tzd[i]); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Stop the ADC */ 5358c2ecf20Sopenharmony_ci return configure_adc(0); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci#define DRIVER_NAME "msic_thermal" 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic const struct platform_device_id therm_id_table[] = { 5418c2ecf20Sopenharmony_ci { DRIVER_NAME, 1 }, 5428c2ecf20Sopenharmony_ci { } 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, therm_id_table); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic struct platform_driver mid_thermal_driver = { 5478c2ecf20Sopenharmony_ci .driver = { 5488c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 5498c2ecf20Sopenharmony_ci .pm = &mid_thermal_pm, 5508c2ecf20Sopenharmony_ci }, 5518c2ecf20Sopenharmony_ci .probe = mid_thermal_probe, 5528c2ecf20Sopenharmony_ci .remove = mid_thermal_remove, 5538c2ecf20Sopenharmony_ci .id_table = therm_id_table, 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cimodule_platform_driver(mid_thermal_driver); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>"); 5598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); 5608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 561