blob: 9487a094855566ec688b5f9d43bb20bc15d94440 [file] [log] [blame]
/*
* intel_byt_thermal.c - Intel Baytrail Platform Thermal Driver
*
* Copyright (C) 2013 Intel Corporation
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Author: Durgadoss R <durgadoss.r@intel.com>
*/
#define pr_fmt(fmt) "intel_byt_thermal: " fmt
#include <linux/pm.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kfifo.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/thermal.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/platform_device.h>
#include <linux/mfd/intel_mid_pmic.h>
#include <asm/processor.h>
#include <asm/intel_mid_thermal.h>
#include <asm/intel_crystalcove_gpadc.h>
#include <linux/iio/consumer.h>
#define DEVICE_NAME "crystal_cove_thermal"
/* Number of Thermal sensors on the PMIC */
#define PMIC_THERMAL_SENSORS 4
/* Registers that govern Thermal Monitoring */
#define THRMIRQ0 0x04
#define THRMIRQ1 0x05
#define MTHRMIRQ0 0x11
#define MTHRMIRQ1 0x12
#define IRQLVL1 0x02
#define MIRQLVL1 0x0E
#define IRQ_MASK_ALL 0x0F
#define THRM_MON_CTRL0 0x8E
#define THRM_MON_CTRL1 0x8F
#define TS_ENABLE 0x90
#define TS_CRIT_ENABLE 0x91
#define TS_A0_STS 0x92
#define TS_A1_STS 0x93
#define TS_A2_STS 0xBD
#define PMICSTS (1 << 5)
#define PMICALRT (1 << 3)
#define SYS2ALRT (1 << 2)
#define SYS1ALRT (1 << 1)
#define SYS0ALRT (1 << 0)
#define THERM_EN (1 << 0)
#define ALERT_EN (1 << 6)
#define PROCHOT_EN (1 << 7)
#define IRQ_LVL1_EN (1 << 1)
#define TS_ENABLE_ALL 0x27
/* ADC to Temperature conversion table length */
#define TABLE_LENGTH 35
#define TEMP_INTERVAL 5
/* Default Alert threshold 85 C */
#define DEFAULT_MAX_TEMP 85
#define MIN_CRIT_TEMP 55
#define PMIC_DIE_MIN_CRIT_TEMP 120
/*
* Default Hysteresis value: 15 corresponds to 3C.
* Why 15? : Hysteresis value is 4 bits wide. This is
* the maximum possible value that can be supported.
* Can be changed at run-time through Sysfs interface
*/
#define DEFAULT_HYST 15
#define NUM_ALERT_LEVELS 3
#define ALERT_RW_MASK 0x07
#define LEVEL_ALERT0 0
#define LEVEL_ALERT1 1
#define LEVEL_ALERT2 2
/*
* LOW event is defined as 0 (implicit)
* HIGH event is defined as 1 (implicit)
* Hence this event is defined as 2.
*/
#define EMUL_TEMP_EVENT 2
#define TEMP_WRITE_TIMEOUT (2 * HZ)
/* Constants defined in CrystalCove PMIC spec */
#define PMIC_DIE_SENSOR 3
#define PMIC_DIE_ADC_MIN 488
#define PMIC_DIE_ADC_MAX 802
#define PMIC_DIE_TEMP_MIN -40
#define PMIC_DIE_TEMP_MAX 125
#define IRQ_FIFO_MAX 16
struct thermal_event {
int sensor; /* For which sensor ? */
int event; /* Whether LOW or HIGH event ? */
int level; /* Which alert level 0/1/2 ? */
};
static DEFINE_KFIFO(irq_fifo, struct thermal_event, IRQ_FIFO_MAX);
static const int params[3][PMIC_THERMAL_SENSORS] = {
{ TS_A0_STS, TS_A1_STS, TS_A2_STS, 0 }, /* status register */
{ SYS0ALRT, SYS1ALRT, SYS2ALRT, PMICALRT }, /* Interrupt bit */
{ SYS0ALRT, SYS1ALRT, SYS2ALRT, PMICSTS }, /* Status bit */
};
/*
* ADC Result registers: The 10 bit ADC code is stored in two registers.
* The 'high' register holds D[8:9] of the ADC code, in D[0:1]. The 'low'
* register holds D[0:7] of the ADC code. These register addresses are
* consecutive.
*/
static const int adc_res_reg_l[PMIC_THERMAL_SENSORS] = {
0x75, 0x77, 0x79, 0x7F };
/*
* Alert registers store the 'alert' temperature for each sensor,
* as 10 bit ADC code. The higher two bits are stored in bits[0:1] of
* alert_regs_h. The lower eight bits are stored in alert_regs_l.
* The hysteresis value is stored in bits[2:5] of alert_regs_h.
* Alert level 2 (also known as 'critical level') is 8 bits wide
* and hence does not have a 'high' register.
*
* static const int alert_regs_h[3][4] = {
* SYS0, SYS1, SYS2, PMIC_DIE
* Alert 0 { 0x94, 0x99, 0x9E, 0xAF },
* Alert 1 { 0x96, 0x9B, 0xA0, 0xB1 },
* Alert 2 { -, -, -, - },
* };
*/
static const int alert_regs_l[3][4] = {
/* SYS0, SYS1, SYS2, PMIC_DIE */
/* Alert 0 */ { 0x95, 0x9A, 0x9F, 0xB0 },
/* Alert 1 */ { 0x97, 0x9C, 0xA1, 0xB2 },
/* Alert 2 */ { 0x98, 0x9D, 0xA2, 0xB3 },
};
/*
* ADC code vs Temperature table
* This table will be different for different thermistors
* Row 0: ADC code
* Row 1: Temperature (in degree celsius)
*/
static const int adc_code[2][TABLE_LENGTH] = {
{977, 961, 941, 917, 887, 853, 813, 769, 720, 669, 615, 561, 508, 456,
407, 357, 315, 277, 243, 212, 186, 162, 140, 107,
94, 82, 72, 64, 56, 50, 44, 39, 35, 31},
{-20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,
65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125,
130, 135, 140, 145, 150},
};
static DEFINE_MUTEX(thrm_update_lock);
struct thermal_device_info {
struct intel_mid_thermal_sensor *sensor;
long trip_temp[NUM_ALERT_LEVELS];
struct completion temp_write_complete;
int sensor_index;
};
struct thermal_data {
struct platform_device *pdev;
struct iio_channel *iio_chan;
struct work_struct thermal_work;
struct mutex thrm_irq_lock;
struct thermal_zone_device **tzd;
/* Caching information */
bool is_initialized;
unsigned long last_updated;
int cached_vals[PMIC_THERMAL_SENSORS];
/* Details obtained from platform data */
int num_sensors;
int num_virtual_sensors;
unsigned int irq;
struct intel_mid_thermal_sensor *sensors;
/* CPU data */
bool is_vlv;
};
static struct thermal_data *tdata;
#ifdef CONFIG_DEBUG_FS
static struct thermal_regs {
char *name;
int addr;
} thermal_regs[] = {
/* Thermal Management Registers */
{"THRM_MON_CTRL0", 0x8E},
{"THRM_MON_CTRL1", 0x8F},
{"TS_ENABLE", 0x90},
{"TS_CRIT_ENABLE", 0x91},
{"TS_A0_STATUS", 0x92},
{"TS_A1_STATUS", 0x93},
{"TS_CRIT_STATUS", 0xBD},
{"IRQLVL1", 0x02},
{"MIRQLVL1", 0x0E},
{"THRMIRQ0", 0x04},
{"THRMIRQ1", 0x05},
{"MTHRMIRQ0", 0x11},
{"MTHRMIRQ1", 0x12},
{"A0_SYS0_H", 0x94},
{"A0_SYS1_H", 0x99},
{"A0_SYS2_H", 0x9E},
{"A0_BAT0_H", 0xA3},
{"A0_BAT1_H", 0xA9},
{"A0_PMIC_H", 0xAF},
};
static struct dentry *thermal_dent[ARRAY_SIZE(thermal_regs)];
static struct dentry *ccove_thermal_dir;
static int ccove_thermal_debugfs_show(struct seq_file *s, void *unused)
{
int addr = *((int *)s->private);
int val = intel_mid_pmic_readb(addr);
seq_printf(s, "Addr[0x%X] Val: 0x%X\n", addr, val);
return 0;
}
static int debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, ccove_thermal_debugfs_show, inode->i_private);
}
static const struct file_operations ccove_thermal_debugfs_ops = {
.open = debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void create_ccove_thermal_debugfs(void)
{
int i, err;
/* /sys/kernel/debug/ccove_thermal/ */
ccove_thermal_dir = debugfs_create_dir("ccove_thermal", NULL);
if (IS_ERR(ccove_thermal_dir)) {
err = PTR_ERR(ccove_thermal_dir);
pr_err("debugfs_create_dir failed:%d\n", err);
return;
}
/* /sys/kernel/debug/ccove_thermal/REG_NAME */
for (i = 0; i < ARRAY_SIZE(thermal_regs); i++) {
thermal_dent[i] = debugfs_create_file(thermal_regs[i].name,
S_IFREG | S_IRUGO,
ccove_thermal_dir,
&thermal_regs[i].addr,
&ccove_thermal_debugfs_ops);
if (IS_ERR(thermal_dent[i])) {
err = PTR_ERR(thermal_dent[i]);
debugfs_remove_recursive(ccove_thermal_dir);
pr_debug("debugfs_create_file failed:%d\n", err);
}
}
}
static void remove_ccove_thermal_debugfs(void)
{
debugfs_remove_recursive(ccove_thermal_dir);
}
#else
static inline void create_pmic_thermal_debugfs(void) { }
static inline void remove_pmic_thermal_debugfs(void) { }
#endif
static inline int adc_to_pmic_die_temp(unsigned int val)
{
/* return temperature in mC */
return 382100 - val * 526;
}
static inline int pmic_die_temp_to_adc(int temp)
{
/* 'temp' is in C, convert to mC and then do calculations */
return (382100 - temp * 1000) / 526;
}
/**
* find_adc_code - searches the ADC code using binary search
* @val: value to find in the array
*
* This function does binary search on an array sorted in 'descending' order
* Can sleep
*/
static int find_adc_code(uint16_t val)
{
int left = 0;
int right = TABLE_LENGTH - 1;
int mid;
while (left <= right) {
mid = (left + right)/2;
if (val == adc_code[0][mid] ||
(mid > 0 &&
val > adc_code[0][mid] && val < adc_code[0][mid-1]))
return mid;
else if (val > adc_code[0][mid])
right = mid - 1;
else if (val < adc_code[0][mid])
left = mid + 1;
}
return -EINVAL;
}
/**
* adc_to_temp - converts the ADC code to temperature in mC
* @direct: true if the sensor uses direct conversion
* @adc_val: the ADC code to be converted
* @tp: temperature return value
*
* Can sleep
*/
static int adc_to_temp(int direct, uint16_t adc_val, long *tp)
{
int x0, x1, y0, y1;
int nr, dr; /* Numerator & Denominator */
int indx;
int x = adc_val;
/* Direct conversion for pmic die temperature */
if (direct) {
if (adc_val < PMIC_DIE_ADC_MIN || adc_val > PMIC_DIE_ADC_MAX)
return -EINVAL;
*tp = adc_to_pmic_die_temp(adc_val);
return 0;
}
indx = find_adc_code(adc_val);
if (indx < 0)
return -EINVAL;
if (adc_code[0][indx] == adc_val) {
*tp = adc_code[1][indx] * 1000;
return 0;
}
/*
* The ADC code is in between two values directly defined in the
* table. So, do linear interpolation to calculate the temperature.
*/
x0 = adc_code[0][indx];
x1 = adc_code[0][indx - 1];
y0 = adc_code[1][indx];
y1 = adc_code[1][indx - 1];
/*
* Find y:
* Of course, we can avoid these variables, but keep them
* for readability and maintainability.
*/
nr = (x-x0)*y1 + (x1-x)*y0;
dr = x1-x0;
if (!dr)
return -EINVAL;
/*
* We have to report the temperature in milli degree celsius.
* So, to reduce the loss of precision, do (Nr*1000)/Dr, instead
* of (Nr/Dr)*1000.
*/
*tp = (nr * 1000)/dr;
return 0;
}
/**
* temp_to_adc - converts the temperature(in C) to ADC code
* @direct: true if the sensor uses direct conversion
* @temp: the temperature to be converted
* @adc_val: ADC code return value
*
* Can sleep
*/
static int temp_to_adc(int direct, int temp, int *adc_val)
{
int indx;
int x0, x1, y0, y1;
int nr, dr; /* Numerator & Denominator */
int x = temp;
/* Direct conversion for pmic die temperature */
if (direct) {
if (temp < PMIC_DIE_TEMP_MIN || temp > PMIC_DIE_TEMP_MAX)
return -EINVAL;
*adc_val = pmic_die_temp_to_adc(temp);
return 0;
}
if (temp < adc_code[1][0] || temp > adc_code[1][TABLE_LENGTH - 1])
return -EINVAL;
/* Find the 'indx' of this 'temp' in the table */
indx = (temp - adc_code[1][0]) / TEMP_INTERVAL;
if (temp == adc_code[1][indx]) {
*adc_val = adc_code[0][indx];
return 0;
}
/*
* Temperature is not a multiple of 'TEMP_INTERVAL'. So,
* do linear interpolation to obtain a better ADC code.
*/
x0 = adc_code[1][indx];
x1 = adc_code[1][indx + 1];
y0 = adc_code[0][indx];
y1 = adc_code[0][indx + 1];
nr = (x-x0)*y1 + (x1-x)*y0;
dr = x1-x0;
if (!dr)
return -EINVAL;
*adc_val = nr/dr;
return 0;
}
/**
* set_hyst_val - sets the given 'val' as the 'hysteresis'
* @alert_reg_h: The 'high' register address
* @hyst: Hysteresis value (in ADC codes) to be programmed
*
* Not protected. Calling function should handle synchronization.
* Can sleep
*/
static int set_hyst_val(int alert_reg_h, int hyst)
{
int ret;
ret = intel_mid_pmic_readb(alert_reg_h);
if (ret < 0)
return ret;
/* Set bits [2:5] to value of hyst */
ret = (ret & 0xC3) | (hyst << 2);
return intel_mid_pmic_writeb(alert_reg_h, ret);
}
/**
* set_alert_temp - sets the given 'adc_val' to the 'alert_reg'
* @alert_reg_l: The 'low' register address
* @adc_val: ADC value to be programmed
* @level: 0 - alert0, 1 - alert1, 2 - alert2(only 8 bits wide)
*
* Not protected. Calling function should handle synchronization.
* Can sleep
*/
static int set_alert_temp(int alert_reg_l, int adc_val, int level)
{
int ret;
/*
* Method used for VLV-CRC PMICs:
* The alert register stores B[1:8] of val and the HW
* while comparing prefixes and suffixes this value with
* a 0; i.e B[0] and B[9] are 0.
*
* Method used for CHV-CRC+ PMICs:
* Use B[2..9]; B[0] and B[1] are assumed to be 0 by the HW.
*/
if (level == LEVEL_ALERT2) {
if (tdata->is_vlv)
adc_val = (adc_val & 0x1FF) >> 1;
else
adc_val = (adc_val & 0x3FF) >> 2;
return intel_mid_pmic_writeb(alert_reg_l, adc_val);
}
/* Extract bits[0:7] of 'adc_val' and write them into alert_reg_l */
ret = intel_mid_pmic_writeb(alert_reg_l, adc_val & 0xFF);
if (ret < 0)
return ret;
/* Get the address of alert_reg_h */
--alert_reg_l;
ret = intel_mid_pmic_readb(alert_reg_l);
if (ret < 0)
return ret;
/* Set bits[0:1] of alert_reg_h to bits[8:9] of 'adc_val' */
ret = (ret & ~0x03) | (adc_val >> 8);
return intel_mid_pmic_writeb(alert_reg_l, ret);
}
/**
* get_alert_temp - gets the ADC code from the alert register
* @alert_reg_l: The 'low' register address
* @level: 0 - alert0, 1 - alert1, 2 - alert2(only 8 bits wide)
*
* Not protected. Calling function should handle synchronization.
* Can sleep
*/
static int get_alert_temp(int alert_reg_l, int level)
{
int l, h;
l = intel_mid_pmic_readb(alert_reg_l);
if (l < 0)
return l;
if (level == LEVEL_ALERT2) {
/* For VLV-CRC based platforms */
if (tdata->is_vlv)
return l << 1;
/* For other platforms */
return l << 2;
}
/* Get the address of alert_reg_h */
--alert_reg_l;
h = intel_mid_pmic_readb(alert_reg_l);
if (h < 0)
return h;
/* Concatenate 'h' and 'l' to get 10-bit ADC code */
return ((h & 0x03) << 8) | l;
}
static int disable_prochot(void)
{
int i, reg, ret;
mutex_lock(&thrm_update_lock);
for (i = 0; i < PMIC_THERMAL_SENSORS; i++) {
reg = alert_regs_l[0][i] - 1;
ret = intel_mid_pmic_clearb(reg, PROCHOT_EN);
if (ret < 0)
goto exit;
}
exit:
mutex_unlock(&thrm_update_lock);
return ret;
}
/**
* program_tmax - programs a default _max value for each sensor
* @dev: device pointer
*
* Can sleep
*/
static int program_tmax(struct device *dev)
{
int i, ret, level;
int pmic_die_val, adc_val, val;
int pmic_die_crit_val;
ret = temp_to_adc(1, PMIC_DIE_MIN_CRIT_TEMP, &pmic_die_crit_val);
if (ret)
return ret;
ret = temp_to_adc(1, DEFAULT_MAX_TEMP, &pmic_die_val);
if (ret)
return ret;
/* ADC code corresponding to max Temp 85 C */
ret = temp_to_adc(0, DEFAULT_MAX_TEMP, &adc_val);
if (ret)
return ret;
for (level = 0; level <= LEVEL_ALERT2; level++) {
for (i = 0; i < PMIC_THERMAL_SENSORS; i++) {
val = (i == PMIC_DIE_SENSOR) ? pmic_die_val : adc_val;
if (level == LEVEL_ALERT2 && i == PMIC_DIE_SENSOR)
val = pmic_die_crit_val;
ret = set_alert_temp(alert_regs_l[level][i],
val, level);
if (ret < 0)
goto exit_err;
/* Set default Hysteresis for Alerts 0,1 only */
if (level == LEVEL_ALERT2)
continue;
ret = set_hyst_val(alert_regs_l[level][i] - 1,
DEFAULT_HYST);
if (ret < 0)
goto exit_err;
}
}
return ret;
exit_err:
dev_err(dev, "set alert %d for channel %d failed:%d\n", level, i, ret);
return ret;
}
static ssize_t store_trip_hyst(struct thermal_zone_device *tzd,
int trip, long hyst)
{
int ret;
struct thermal_device_info *td_info = tzd->devdata;
int alert_reg = alert_regs_l[trip][td_info->sensor_index] - 1;
/*
* Alert level 2 does not support hysteresis; and (for
* other levels) the hysteresis value is 4 bits wide.
*/
if (trip == LEVEL_ALERT2 || hyst > DEFAULT_HYST)
return -EINVAL;
mutex_lock(&thrm_update_lock);
ret = set_hyst_val(alert_reg, hyst);
mutex_unlock(&thrm_update_lock);
return ret;
}
static ssize_t show_trip_hyst(struct thermal_zone_device *tzd,
int trip, long *hyst)
{
int ret;
struct thermal_device_info *td_info = tzd->devdata;
int alert_reg_l = alert_regs_l[trip][td_info->sensor_index];
if (trip == LEVEL_ALERT2) {
*hyst = 0;
return 0;
}
mutex_lock(&thrm_update_lock);
/* Get the address of alert_reg_h */
--alert_reg_l;
ret = intel_mid_pmic_readb(alert_reg_l);
if (ret >= 0)
*hyst = (ret >> 2) & 0x0F; /* Extract bits[2:5] of data */
mutex_unlock(&thrm_update_lock);
return 0;
}
static ssize_t store_trip_temp(struct thermal_zone_device *tzd,
int trip, long trip_temp)
{
int ret, adc_val, min_tcrit = MIN_CRIT_TEMP;
struct thermal_device_info *td_info = tzd->devdata;
int alert_reg_l = alert_regs_l[trip][td_info->sensor_index];
if (trip_temp != 0 && trip_temp < 1000) {
dev_err(&tzd->device, "Temperature should be in mC\n");
return -EINVAL;
}
/* Convert to C */
trip_temp /= 1000;
/* Minimum Tcrit for PMIC DIE is different from that of others */
if (td_info->sensor->direct)
min_tcrit = PMIC_DIE_MIN_CRIT_TEMP;
if (trip == LEVEL_ALERT2 && trip_temp < min_tcrit) {
dev_err(&tzd->device,
"Tcrit should be more than %dC\n", min_tcrit);
return -EINVAL;
}
mutex_lock(&thrm_update_lock);
ret = temp_to_adc(td_info->sensor->direct, (int)trip_temp, &adc_val);
if (ret) {
adc_val = trip_temp > 0 ?
adc_code[0][TABLE_LENGTH - 1] : adc_code[0][0];
dev_err(&tzd->device,
"ADC code out of range. Capping it to %s possible\n",
trip_temp > 0 ? "highest" : "lowest");
}
/*
* Hold the irq lock, so that no alert threshold for any sensor
* is being programmed while we are handling an interrupt
*/
mutex_lock(&tdata->thrm_irq_lock);
ret = set_alert_temp(alert_reg_l, adc_val, trip);
mutex_unlock(&tdata->thrm_irq_lock);
/* Store the trip point value written into the register */
ret = adc_to_temp(td_info->sensor->direct, adc_val,
&td_info->trip_temp[trip]);
if (ret)
dev_err(&tzd->device,
"adc_to_temp for trip%d failed:%d\n", trip, ret);
mutex_unlock(&thrm_update_lock);
return ret;
}
static ssize_t show_trip_temp(struct thermal_zone_device *tzd,
int trip, long *trip_temp)
{
int ret = -EINVAL, adc_val;
struct thermal_device_info *td_info = tzd->devdata;
int alert_reg_l = alert_regs_l[trip][td_info->sensor_index];
mutex_lock(&thrm_update_lock);
adc_val = get_alert_temp(alert_reg_l, trip);
if (adc_val < 0)
goto exit;
ret = adc_to_temp(td_info->sensor->direct, adc_val, trip_temp);
exit:
mutex_unlock(&thrm_update_lock);
return ret;
}
static ssize_t show_trip_type(struct thermal_zone_device *tzd,
int trip, enum thermal_trip_type *trip_type)
{
/* All are passive trip points */
*trip_type = THERMAL_TRIP_PASSIVE;
return 0;
}
static int read_result_regs(void)
{
int i, ret;
for (i = 0; i < PMIC_THERMAL_SENSORS; i++) {
/*
* Exploit the fact that the result registers store
* the value in the same format as that of the alert
* registers. So, use get_alert_temp but pass the
* result register address, and level as 0.
*/
ret = get_alert_temp(adc_res_reg_l[i], 0);
if (ret < 0)
return ret;
tdata->cached_vals[i] = ret;
}
return 0;
}
static int update_temp(struct thermal_zone_device *tzd, long *temp)
{
int ret;
struct thermal_device_info *td_info = tzd->devdata;
int indx = td_info->sensor_index;
if (!tdata->iio_chan)
return -EINVAL;
if (!tdata->is_initialized ||
time_after(jiffies, tdata->last_updated + HZ)) {
ret = iio_read_channel_all_raw(tdata->iio_chan,
tdata->cached_vals);
if (ret == -ETIMEDOUT) {
dev_err(&tzd->device,
"ADC sampling failed:%d Reading rslt regs\n",
ret);
ret = read_result_regs();
if (ret)
return ret;
}
tdata->last_updated = jiffies;
tdata->is_initialized = true;
}
ret = adc_to_temp(td_info->sensor->direct,
tdata->cached_vals[indx], temp);
return ret;
}
static ssize_t show_emul_temp(struct thermal_zone_device *tzd, long *temp)
{
int ret = 0;
char *thermal_event[3];
unsigned long timeout;
struct thermal_device_info *td_info = tzd->devdata;
thermal_event[0] = kasprintf(GFP_KERNEL, "NAME=%s", tzd->type);
thermal_event[1] = kasprintf(GFP_KERNEL, "EVENT=%d", EMUL_TEMP_EVENT);
thermal_event[2] = NULL;
INIT_COMPLETION(td_info->temp_write_complete);
kobject_uevent_env(&tzd->device.kobj, KOBJ_CHANGE, thermal_event);
timeout = wait_for_completion_timeout(&td_info->temp_write_complete,
TEMP_WRITE_TIMEOUT);
if (timeout == 0) {
/* Waiting timed out */
ret = -ETIMEDOUT;
goto exit;
}
*temp = tzd->emul_temperature;
exit:
kfree(thermal_event[1]);
kfree(thermal_event[0]);
return ret;
}
static ssize_t store_emul_temp(struct thermal_zone_device *tzd,
unsigned long temp)
{
struct thermal_device_info *td_info = tzd->devdata;
tzd->emul_temperature = temp;
complete(&td_info->temp_write_complete);
return 0;
}
static ssize_t show_temp(struct thermal_zone_device *tzd, long *temp)
{
int ret;
mutex_lock(&thrm_update_lock);
ret = update_temp(tzd, temp);
mutex_unlock(&thrm_update_lock);
return ret;
}
static int enable_tm(void)
{
int i, reg, ret, level;
mutex_lock(&thrm_update_lock);
/* Setting these bits enables ADC to poll for these Thermistors */
ret = intel_mid_pmic_setb(TS_ENABLE, TS_ENABLE_ALL);
if (ret < 0)
goto exit;
/*
* Enable Interrupts for Alert level 0 and 1. Alert
* level 2 is critical and is enabled by default in the HW.
*/
for (level = 0; level <= LEVEL_ALERT1; level++) {
/* Unmask the 2nd level interrupts for Alerts 0,1,2 */
ret = intel_mid_pmic_writeb(MTHRMIRQ0 + level, 0x00);
if (ret < 0)
goto exit;
for (i = 0; i < PMIC_THERMAL_SENSORS; i++) {
reg = alert_regs_l[level][i] - 1;
ret = intel_mid_pmic_setb(reg, ALERT_EN);
if (ret < 0)
goto exit;
}
}
/* Unmask the first level IRQ bit for Thermal alerts */
ret = intel_mid_pmic_clearb(MIRQLVL1, IRQ_LVL1_EN);
exit:
mutex_unlock(&thrm_update_lock);
return ret;
}
static struct thermal_device_info *initialize_sensor(int index,
struct intel_mid_thermal_sensor *sensor)
{
struct thermal_device_info *td_info =
kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL);
if (!td_info)
return NULL;
td_info->sensor = sensor;
td_info->sensor_index = index;
init_completion(&td_info->temp_write_complete);
return td_info;
}
static void notify_thermal_event(struct thermal_event te)
{
int ret;
long cur_temp;
char *thermal_event[5];
struct thermal_zone_device *tzd = tdata->tzd[te.sensor];
struct thermal_device_info *td_info = tzd->devdata;
mutex_lock(&thrm_update_lock);
/*
* We want to get the 'latest' temperature value after an
* interrupt. So, make sure update_temp() actually samples and
* returns the 'latest' temperature values; and not
* the previously cached ones.
*/
tdata->is_initialized = false;
/*
* Read the current Temperature and send it to user land;
* so that the user space can avoid a sysfs read.
*/
ret = update_temp(tzd, &cur_temp);
if (ret) {
dev_err(&tzd->device, "Cannot update temperature\n");
goto exit;
}
pr_info("Thermal Event: sensor: %s, cur_temp: %ld, event: %d, level: %d\n",
tzd->type, cur_temp, te.event, te.level);
/*
* Send UEvents only when temperature goes below
* ALERT0 or goes above ALERT1. No UEvents for
* ALERT2 as the actions are taken by Hardware.
*/
if (te.level == LEVEL_ALERT2 ||
(te.event == 1 && te.level == LEVEL_ALERT0) ||
(te.event == 0 && te.level == LEVEL_ALERT1)) {
goto exit;
}
/*
* For Cold-to-Hot events, make sure the temperature is
* 'at least' as high as the programmed threshold.
*/
if (te.event == 1) {
if (cur_temp < td_info->trip_temp[te.level])
cur_temp = td_info->trip_temp[te.level];
}
thermal_event[0] = kasprintf(GFP_KERNEL, "NAME=%s", tzd->type);
thermal_event[1] = kasprintf(GFP_KERNEL, "TEMP=%ld", cur_temp);
thermal_event[2] = kasprintf(GFP_KERNEL, "EVENT=%d", te.event);
thermal_event[3] = kasprintf(GFP_KERNEL, "LEVEL=%d", te.level);
thermal_event[4] = NULL;
kobject_uevent_env(&tzd->device.kobj, KOBJ_CHANGE, thermal_event);
kfree(thermal_event[3]);
kfree(thermal_event[2]);
kfree(thermal_event[1]);
kfree(thermal_event[0]);
exit:
mutex_unlock(&thrm_update_lock);
return;
}
static int update_intrpt_params(int irq, int level)
{
int i, sts;
struct thermal_event te;
/* Read the appropriate status register */
sts = intel_mid_pmic_readb(params[0][level]);
if (sts < 0)
return sts;
for (i = 0; i < PMIC_THERMAL_SENSORS; i++) {
if (irq & params[1][i]) {
te.level = level;
te.sensor = i;
te.event = !!(sts & params[2][i]);
kfifo_put(&irq_fifo, &te);
}
}
return 0;
}
static void thermal_work_func(struct work_struct *work)
{
int gotten;
struct thermal_event te;
while (!kfifo_is_empty(&irq_fifo)) {
gotten = kfifo_get(&irq_fifo, &te);
if (!gotten) {
pr_err("kfifo empty\n");
return;
}
/* Notify the user space through UEvent */
notify_thermal_event(te);
}
}
static irqreturn_t thermal_intrpt(int irq_nr, void *id)
{
int ret, irq, irq_cache, reg, level;
struct thermal_data *tdata = (struct thermal_data *)id;
if (!tdata)
return IRQ_HANDLED;
mutex_lock(&tdata->thrm_irq_lock);
/*
* Assertion of THRMIRQ1[0:3] indicates Alert2 ('critical')
* event. Register Layout:
*
* THRMIRQ0: PMIC_DIE SYS2 SYS1 SYS0 PMIC_DIE SYS2 SYS1 SYS0
* Alert0-1: Alert1 A1 A1 A1 Alert0 A0 A0 A0
*
* THRMIRQ1: RSVD RSVD RSVD RSVD PMIC_DIE SYS2 SYS1 SYS0
* Alert 2: Alert2 A2 A2 A2
*/
for (level = 0; level <= LEVEL_ALERT2; level++) {
reg = THRMIRQ0 + (level == LEVEL_ALERT2);
irq = intel_mid_pmic_readb(reg);
if (irq < 0)
goto exit;
irq_cache = irq;
irq = irq >> (PMIC_THERMAL_SENSORS * (level == LEVEL_ALERT1));
if (irq & 0x0F)
goto handle_event;
}
/*
* If we are here, then this event is none of Alert0/1/2
* events but somehow we got the interrupt. Just exit.
* Very unlikely case though.
*/
goto exit;
handle_event:
/*
* From the interrupt and status register, find out
* which sensor caused the event, and for what transition
* [either 'Hot to Cold' or 'Cold to Hot']
*/
ret = update_intrpt_params(irq, level);
if (ret < 0)
goto exit_err;
ret = intel_mid_pmic_writeb(reg, irq_cache);
if (ret < 0)
goto exit_err;
schedule_work(&tdata->thermal_work);
mutex_unlock(&tdata->thrm_irq_lock);
return IRQ_HANDLED;
exit_err:
pr_err("I2C read/write failed:%d\n", ret);
exit:
mutex_unlock(&tdata->thrm_irq_lock);
return IRQ_HANDLED;
}
static struct thermal_zone_device_ops tzd_emul_ops = {
.get_temp = show_emul_temp,
.set_emul_temp = store_emul_temp,
};
static struct thermal_zone_device_ops tzd_ops = {
.get_temp = show_temp,
.get_trip_type = show_trip_type,
.get_trip_temp = show_trip_temp,
.set_trip_temp = store_trip_temp,
.get_trip_hyst = show_trip_hyst,
.set_trip_hyst = store_trip_hyst,
};
static int byt_thermal_probe(struct platform_device *pdev)
{
int i, size, ret;
int total_sensors; /* real + virtual sensors */
struct intel_mid_thermal_platform_data *pdata;
struct cpuinfo_x86 *c = &cpu_data(0);
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "Unable to fetch platform data\n");
return -EINVAL;
}
tdata = kzalloc(sizeof(struct thermal_data), GFP_KERNEL);
if (!tdata) {
dev_err(&pdev->dev, "kzalloc failed\n");
return -ENOMEM;
}
tdata->pdev = pdev;
tdata->num_sensors = pdata->num_sensors;
tdata->num_virtual_sensors = pdata->num_virtual_sensors;
tdata->sensors = pdata->sensors;
tdata->irq = platform_get_irq(pdev, 0);
platform_set_drvdata(pdev, tdata);
mutex_init(&tdata->thrm_irq_lock);
/*
* Identify whether this is VLV(0x37) or CHV(0x4c) board.
* TODO: Use PMIC registers on I2C space to differentiate this.
*/
if (c->x86_model == 0x37)
tdata->is_vlv = true;
else
tdata->is_vlv = false;
total_sensors = tdata->num_sensors;
#ifdef CONFIG_THERMAL_EMULATION
total_sensors += tdata->num_virtual_sensors;
#endif
size = sizeof(struct thermal_zone_device *) * total_sensors;
tdata->tzd = kzalloc(size, GFP_KERNEL);
if (!tdata->tzd) {
dev_err(&pdev->dev, "kzalloc failed\n");
ret = -ENOMEM;
goto exit_free;
}
/* Disable prochot on alert0 crossing */
ret = disable_prochot();
if (ret) {
dev_err(&pdev->dev, "Disabling prochot failed:%d\n", ret);
goto exit_tzd;
}
/* Program a default _max value for each sensor */
ret = program_tmax(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Programming _max failed:%d\n", ret);
goto exit_tzd;
}
/* Register with IIO to sample temperature values */
tdata->iio_chan = iio_channel_get_all(&pdev->dev);
if (tdata->iio_chan == NULL) {
dev_err(&pdev->dev, "tdata->iio_chan is null\n");
ret = -EINVAL;
goto exit_tzd;
}
/* Check whether we got all the four channels */
ret = iio_channel_get_num(tdata->iio_chan);
if (ret != PMIC_THERMAL_SENSORS) {
dev_err(&pdev->dev, "incorrect number of channels:%d\n", ret);
ret = -EFAULT;
goto exit_iio;
}
/* Register each sensor with the generic thermal framework */
for (i = 0; i < total_sensors; i++) {
if (i < tdata->num_sensors) {
tdata->tzd[i] = thermal_zone_device_register(
tdata->sensors[i].name,
NUM_ALERT_LEVELS, ALERT_RW_MASK,
initialize_sensor(i, &tdata->sensors[i]),
&tzd_ops,
NULL, 0, 0);
} else {
tdata->tzd[i] = thermal_zone_device_register(
tdata->sensors[i].name,
0, 0,
initialize_sensor(i, &tdata->sensors[i]),
&tzd_emul_ops,
NULL, 0, 0);
}
if (IS_ERR(tdata->tzd[i])) {
ret = PTR_ERR(tdata->tzd[i]);
dev_err(&pdev->dev,
"registering thermal sensor %s failed: %d\n",
tdata->sensors[i].name, ret);
goto exit_reg;
}
}
INIT_WORK(&tdata->thermal_work, thermal_work_func);
/* Register for Interrupt Handler */
ret = request_threaded_irq(tdata->irq, NULL, thermal_intrpt,
IRQF_TRIGGER_RISING,
DEVICE_NAME, tdata);
if (ret) {
dev_err(&pdev->dev, "request_threaded_irq failed:%d\n", ret);
goto exit_reg;
}
/* Enable Thermal Monitoring */
ret = enable_tm();
if (ret) {
dev_err(&pdev->dev, "Enabling TM failed:%d\n", ret);
goto exit_irq;
}
create_ccove_thermal_debugfs();
return 0;
exit_irq:
free_irq(tdata->irq, tdata);
exit_reg:
while (--i >= 0)
thermal_zone_device_unregister(tdata->tzd[i]);
exit_iio:
iio_channel_release_all(tdata->iio_chan);
exit_tzd:
kfree(tdata->tzd);
exit_free:
mutex_destroy(&tdata->thrm_irq_lock);
kfree(tdata);
return ret;
}
static int byt_thermal_resume(struct device *dev)
{
return 0;
}
static int byt_thermal_suspend(struct device *dev)
{
return 0;
}
static int byt_thermal_remove(struct platform_device *pdev)
{
int i, total_sensors;
struct thermal_data *tdata = platform_get_drvdata(pdev);
if (!tdata)
return 0;
total_sensors = tdata->num_sensors;
#ifdef CONFIG_THERMAL_EMULATION
total_sensors += tdata->num_virtual_sensors;
#endif
for (i = 0; i < total_sensors; i++)
thermal_zone_device_unregister(tdata->tzd[i]);
iio_channel_release_all(tdata->iio_chan);
free_irq(tdata->irq, tdata);
mutex_destroy(&tdata->thrm_irq_lock);
kfree(tdata->tzd);
kfree(tdata);
remove_ccove_thermal_debugfs();
return 0;
}
/*********************************************************************
* Driver initialization and finalization
*********************************************************************/
static const struct dev_pm_ops thermal_pm_ops = {
.suspend = byt_thermal_suspend,
.resume = byt_thermal_resume,
};
static struct platform_driver byt_thermal_driver = {
.driver = {
.name = DEVICE_NAME,
.owner = THIS_MODULE,
.pm = &thermal_pm_ops,
},
.probe = byt_thermal_probe,
.remove = byt_thermal_remove,
};
static int byt_thermal_module_init(void)
{
return platform_driver_register(&byt_thermal_driver);
}
static void byt_thermal_module_exit(void)
{
platform_driver_unregister(&byt_thermal_driver);
}
late_initcall(byt_thermal_module_init);
module_exit(byt_thermal_module_exit);
MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
MODULE_DESCRIPTION("Intel Baytrail Platform Thermal Driver");
MODULE_LICENSE("GPL");