1065 lines
32 KiB
C
1065 lines
32 KiB
C
|
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 and
|
||
|
* only version 2 as published by the Free Software Foundation.
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
* Qualcomm MSM8960 TSENS driver
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/thermal.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/msm_tsens.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/pm.h>
|
||
|
|
||
|
#include <mach/msm_iomap.h>
|
||
|
#include <mach/socinfo.h>
|
||
|
|
||
|
/* Trips: from very hot to very cold */
|
||
|
enum tsens_trip_type {
|
||
|
TSENS_TRIP_STAGE3 = 0,
|
||
|
TSENS_TRIP_STAGE2,
|
||
|
TSENS_TRIP_STAGE1,
|
||
|
TSENS_TRIP_STAGE0,
|
||
|
TSENS_TRIP_NUM,
|
||
|
};
|
||
|
|
||
|
/* MSM8960 TSENS register info */
|
||
|
#define TSENS_CAL_DEGC 30
|
||
|
#define TSENS_MAIN_SENSOR 0
|
||
|
|
||
|
#define TSENS_8960_QFPROM_ADDR0 (MSM_QFPROM_BASE + 0x00000404)
|
||
|
#define TSENS_8960_QFPROM_SPARE_ADDR0 (MSM_QFPROM_BASE + 0x00000414)
|
||
|
#define TSENS_8960_CONFIG 0x9b
|
||
|
#define TSENS_8960_CONFIG_SHIFT 0
|
||
|
#define TSENS_8960_CONFIG_MASK (0xf << TSENS_8960_CONFIG_SHIFT)
|
||
|
#define TSENS_CNTL_ADDR (MSM_CLK_CTL_BASE + 0x00003620)
|
||
|
#define TSENS_EN BIT(0)
|
||
|
#define TSENS_SW_RST BIT(1)
|
||
|
#define TSENS_ADC_CLK_SEL BIT(2)
|
||
|
#define SENSOR0_EN BIT(3)
|
||
|
#define SENSOR1_EN BIT(4)
|
||
|
#define SENSOR2_EN BIT(5)
|
||
|
#define SENSOR3_EN BIT(6)
|
||
|
#define SENSOR4_EN BIT(7)
|
||
|
#define SENSORS_EN (SENSOR0_EN | SENSOR1_EN | \
|
||
|
SENSOR2_EN | SENSOR3_EN | SENSOR4_EN)
|
||
|
#define TSENS_STATUS_CNTL_OFFSET 8
|
||
|
#define TSENS_MIN_STATUS_MASK BIT((tsens_status_cntl_start))
|
||
|
#define TSENS_LOWER_STATUS_CLR BIT((tsens_status_cntl_start + 1))
|
||
|
#define TSENS_UPPER_STATUS_CLR BIT((tsens_status_cntl_start + 2))
|
||
|
#define TSENS_MAX_STATUS_MASK BIT((tsens_status_cntl_start + 3))
|
||
|
|
||
|
#define TSENS_MEASURE_PERIOD 1
|
||
|
#define TSENS_8960_SLP_CLK_ENA BIT(26)
|
||
|
|
||
|
#define TSENS_THRESHOLD_ADDR (MSM_CLK_CTL_BASE + 0x00003624)
|
||
|
#define TSENS_THRESHOLD_MAX_CODE 0xff
|
||
|
#define TSENS_THRESHOLD_MIN_CODE 0
|
||
|
#define TSENS_THRESHOLD_MAX_LIMIT_SHIFT 24
|
||
|
#define TSENS_THRESHOLD_MIN_LIMIT_SHIFT 16
|
||
|
#define TSENS_THRESHOLD_UPPER_LIMIT_SHIFT 8
|
||
|
#define TSENS_THRESHOLD_LOWER_LIMIT_SHIFT 0
|
||
|
#define TSENS_THRESHOLD_MAX_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << \
|
||
|
TSENS_THRESHOLD_MAX_LIMIT_SHIFT)
|
||
|
#define TSENS_THRESHOLD_MIN_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << \
|
||
|
TSENS_THRESHOLD_MIN_LIMIT_SHIFT)
|
||
|
#define TSENS_THRESHOLD_UPPER_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << \
|
||
|
TSENS_THRESHOLD_UPPER_LIMIT_SHIFT)
|
||
|
#define TSENS_THRESHOLD_LOWER_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << \
|
||
|
TSENS_THRESHOLD_LOWER_LIMIT_SHIFT)
|
||
|
/* Initial temperature threshold values */
|
||
|
#define TSENS_LOWER_LIMIT_TH 0x50
|
||
|
#define TSENS_UPPER_LIMIT_TH 0xdf
|
||
|
#define TSENS_MIN_LIMIT_TH 0x0
|
||
|
#define TSENS_MAX_LIMIT_TH 0xff
|
||
|
|
||
|
#define TSENS_S0_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x00003628)
|
||
|
#define TSENS_STATUS_ADDR_OFFSET 2
|
||
|
#define TSENS_SENSOR_STATUS_SIZE 4
|
||
|
#define TSENS_INT_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x0000363c)
|
||
|
|
||
|
#define TSENS_LOWER_INT_MASK BIT(1)
|
||
|
#define TSENS_UPPER_INT_MASK BIT(2)
|
||
|
#define TSENS_MAX_INT_MASK BIT(3)
|
||
|
#define TSENS_TRDY_MASK BIT(7)
|
||
|
|
||
|
#define TSENS_8960_CONFIG_ADDR (MSM_CLK_CTL_BASE + 0x00003640)
|
||
|
#define TSENS_TRDY_RDY_MIN_TIME 1000
|
||
|
#define TSENS_TRDY_RDY_MAX_TIME 1100
|
||
|
#define TSENS_SENSOR_SHIFT 16
|
||
|
#define TSENS_RED_SHIFT 8
|
||
|
#define TSENS_8960_QFPROM_SHIFT 4
|
||
|
#define TSENS_SENSOR_QFPROM_SHIFT 2
|
||
|
#define TSENS_SENSOR0_SHIFT 3
|
||
|
#define TSENS_MASK1 1
|
||
|
|
||
|
#define TSENS_8660_QFPROM_ADDR (MSM_QFPROM_BASE + 0x000000bc)
|
||
|
#define TSENS_8660_QFPROM_RED_TEMP_SENSOR0_SHIFT 24
|
||
|
#define TSENS_8660_QFPROM_TEMP_SENSOR0_SHIFT 16
|
||
|
#define TSENS_8660_QFPROM_TEMP_SENSOR0_MASK (255 \
|
||
|
<< TSENS_8660_QFPROM_TEMP_SENSOR0_SHIFT)
|
||
|
#define TSENS_8660_CONFIG 01
|
||
|
#define TSENS_8660_CONFIG_SHIFT 28
|
||
|
#define TSENS_8660_CONFIG_MASK (3 << TSENS_8660_CONFIG_SHIFT)
|
||
|
#define TSENS_8660_SLP_CLK_ENA BIT(24)
|
||
|
|
||
|
#define TSENS_8064_SENSOR5_EN BIT(8)
|
||
|
#define TSENS_8064_SENSOR6_EN BIT(9)
|
||
|
#define TSENS_8064_SENSOR7_EN BIT(10)
|
||
|
#define TSENS_8064_SENSOR8_EN BIT(11)
|
||
|
#define TSENS_8064_SENSOR9_EN BIT(12)
|
||
|
#define TSENS_8064_SENSOR10_EN BIT(13)
|
||
|
#define TSENS_8064_SENSORS_EN (SENSORS_EN | \
|
||
|
TSENS_8064_SENSOR5_EN | \
|
||
|
TSENS_8064_SENSOR6_EN | \
|
||
|
TSENS_8064_SENSOR7_EN | \
|
||
|
TSENS_8064_SENSOR8_EN | \
|
||
|
TSENS_8064_SENSOR9_EN | \
|
||
|
TSENS_8064_SENSOR10_EN)
|
||
|
#define TSENS_8064_STATUS_CNTL (MSM_CLK_CTL_BASE + 0x00003660)
|
||
|
#define TSENS_8064_S5_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x00003664)
|
||
|
#define TSENS_8064_SEQ_SENSORS 5
|
||
|
#define TSENS_8064_S4_S5_OFFSET 40
|
||
|
#define TSENS_CNTL_RESUME_MASK 0xfffffff9
|
||
|
#define TSENS_8960_SENSOR_MASK 0xf8
|
||
|
#define TSENS_8064_SENSOR_MASK 0x3ff8
|
||
|
|
||
|
static int tsens_status_cntl_start;
|
||
|
|
||
|
struct tsens_tm_device_sensor {
|
||
|
struct thermal_zone_device *tz_dev;
|
||
|
enum thermal_device_mode mode;
|
||
|
unsigned int sensor_num;
|
||
|
struct work_struct work;
|
||
|
int offset;
|
||
|
int calib_data;
|
||
|
int calib_data_backup;
|
||
|
uint32_t slope_mul_tsens_factor;
|
||
|
};
|
||
|
|
||
|
struct tsens_tm_device {
|
||
|
bool prev_reading_avail;
|
||
|
int tsens_factor;
|
||
|
uint32_t tsens_num_sensor;
|
||
|
enum platform_type hw_type;
|
||
|
int pm_tsens_thr_data;
|
||
|
int pm_tsens_cntl;
|
||
|
struct work_struct tsens_work;
|
||
|
struct tsens_tm_device_sensor sensor[0];
|
||
|
};
|
||
|
|
||
|
struct tsens_tm_device *tmdev;
|
||
|
|
||
|
/* Temperature on y axis and ADC-code on x-axis */
|
||
|
static int tsens_tz_code_to_degC(int adc_code, int sensor_num)
|
||
|
{
|
||
|
int degcbeforefactor, degc;
|
||
|
degcbeforefactor = (adc_code *
|
||
|
tmdev->sensor[sensor_num].slope_mul_tsens_factor
|
||
|
+ tmdev->sensor[sensor_num].offset);
|
||
|
|
||
|
if (degcbeforefactor == 0)
|
||
|
degc = degcbeforefactor;
|
||
|
else if (degcbeforefactor > 0)
|
||
|
degc = (degcbeforefactor + tmdev->tsens_factor/2)
|
||
|
/ tmdev->tsens_factor;
|
||
|
else
|
||
|
degc = (degcbeforefactor - tmdev->tsens_factor/2)
|
||
|
/ tmdev->tsens_factor;
|
||
|
return degc;
|
||
|
}
|
||
|
|
||
|
static int tsens_tz_degC_to_code(int degC, int sensor_num)
|
||
|
{
|
||
|
int code = (degC * tmdev->tsens_factor -
|
||
|
tmdev->sensor[sensor_num].offset
|
||
|
+ tmdev->sensor[sensor_num].slope_mul_tsens_factor/2)
|
||
|
/ tmdev->sensor[sensor_num].slope_mul_tsens_factor;
|
||
|
|
||
|
if (code > TSENS_THRESHOLD_MAX_CODE)
|
||
|
code = TSENS_THRESHOLD_MAX_CODE;
|
||
|
else if (code < TSENS_THRESHOLD_MIN_CODE)
|
||
|
code = TSENS_THRESHOLD_MIN_CODE;
|
||
|
return code;
|
||
|
}
|
||
|
|
||
|
static void tsens8960_get_temp(int sensor_num, unsigned long *temp)
|
||
|
{
|
||
|
unsigned int code, offset = 0, sensor_addr;
|
||
|
|
||
|
if (!tmdev->prev_reading_avail) {
|
||
|
while (!(readl_relaxed(TSENS_INT_STATUS_ADDR)
|
||
|
& TSENS_TRDY_MASK))
|
||
|
usleep_range(TSENS_TRDY_RDY_MIN_TIME,
|
||
|
TSENS_TRDY_RDY_MAX_TIME);
|
||
|
tmdev->prev_reading_avail = true;
|
||
|
}
|
||
|
|
||
|
sensor_addr = (unsigned int)TSENS_S0_STATUS_ADDR;
|
||
|
if (tmdev->hw_type == APQ_8064 &&
|
||
|
sensor_num >= TSENS_8064_SEQ_SENSORS)
|
||
|
offset = TSENS_8064_S4_S5_OFFSET;
|
||
|
code = readl_relaxed(sensor_addr + offset +
|
||
|
(sensor_num << TSENS_STATUS_ADDR_OFFSET));
|
||
|
*temp = tsens_tz_code_to_degC(code, sensor_num);
|
||
|
}
|
||
|
|
||
|
static int tsens_tz_get_temp(struct thermal_zone_device *thermal,
|
||
|
unsigned long *temp)
|
||
|
{
|
||
|
struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
|
||
|
|
||
|
if (!tm_sensor || tm_sensor->mode != THERMAL_DEVICE_ENABLED || !temp)
|
||
|
return -EINVAL;
|
||
|
|
||
|
tsens8960_get_temp(tm_sensor->sensor_num, temp);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int tsens_get_temp(struct tsens_device *device, unsigned long *temp)
|
||
|
{
|
||
|
if (!tmdev)
|
||
|
return -ENODEV;
|
||
|
|
||
|
tsens8960_get_temp(device->sensor_num, temp);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(tsens_get_temp);
|
||
|
|
||
|
int tsens_get_max_sensor_num(uint32_t *tsens_num_sensors)
|
||
|
{
|
||
|
if (!tmdev)
|
||
|
return -ENODEV;
|
||
|
|
||
|
*tsens_num_sensors = tmdev->tsens_num_sensor;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(tsens_get_max_sensor_num);
|
||
|
|
||
|
static int tsens_tz_get_mode(struct thermal_zone_device *thermal,
|
||
|
enum thermal_device_mode *mode)
|
||
|
{
|
||
|
struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
|
||
|
|
||
|
if (!tm_sensor || !mode)
|
||
|
return -EINVAL;
|
||
|
|
||
|
*mode = tm_sensor->mode;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Function to enable the mode.
|
||
|
* If the main sensor is disabled all the sensors are disable and
|
||
|
* the clock is disabled.
|
||
|
* If the main sensor is not enabled and sub sensor is enabled
|
||
|
* returns with an error stating the main sensor is not enabled.
|
||
|
*/
|
||
|
static int tsens_tz_set_mode(struct thermal_zone_device *thermal,
|
||
|
enum thermal_device_mode mode)
|
||
|
{
|
||
|
struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
|
||
|
unsigned int reg, mask, i;
|
||
|
|
||
|
if (!tm_sensor)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (mode != tm_sensor->mode) {
|
||
|
reg = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
|
||
|
mask = 1 << (tm_sensor->sensor_num + TSENS_SENSOR0_SHIFT);
|
||
|
if (mode == THERMAL_DEVICE_ENABLED) {
|
||
|
if ((mask != SENSOR0_EN) && !(reg & SENSOR0_EN)) {
|
||
|
pr_info("Main sensor not enabled\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
writel_relaxed(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
|
||
|
if (tmdev->hw_type == MSM_8960 ||
|
||
|
tmdev->hw_type == MDM_9615 ||
|
||
|
tmdev->hw_type == APQ_8064)
|
||
|
reg |= mask | TSENS_8960_SLP_CLK_ENA
|
||
|
| TSENS_EN;
|
||
|
else
|
||
|
reg |= mask | TSENS_8660_SLP_CLK_ENA
|
||
|
| TSENS_EN;
|
||
|
tmdev->prev_reading_avail = false;
|
||
|
} else {
|
||
|
reg &= ~mask;
|
||
|
if (!(reg & SENSOR0_EN)) {
|
||
|
if (tmdev->hw_type == APQ_8064)
|
||
|
reg &= ~(TSENS_8064_SENSORS_EN |
|
||
|
TSENS_8960_SLP_CLK_ENA |
|
||
|
TSENS_EN);
|
||
|
else if (tmdev->hw_type == MSM_8960 ||
|
||
|
tmdev->hw_type == MDM_9615)
|
||
|
reg &= ~(SENSORS_EN |
|
||
|
TSENS_8960_SLP_CLK_ENA |
|
||
|
TSENS_EN);
|
||
|
else
|
||
|
reg &= ~(SENSORS_EN |
|
||
|
TSENS_8660_SLP_CLK_ENA |
|
||
|
TSENS_EN);
|
||
|
|
||
|
for (i = 1; i < tmdev->tsens_num_sensor; i++)
|
||
|
tmdev->sensor[i].mode = mode;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
writel_relaxed(reg, TSENS_CNTL_ADDR);
|
||
|
}
|
||
|
tm_sensor->mode = mode;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tsens_tz_get_trip_type(struct thermal_zone_device *thermal,
|
||
|
int trip, enum thermal_trip_type *type)
|
||
|
{
|
||
|
struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
|
||
|
|
||
|
if (!tm_sensor || trip < 0 || !type)
|
||
|
return -EINVAL;
|
||
|
|
||
|
switch (trip) {
|
||
|
case TSENS_TRIP_STAGE3:
|
||
|
*type = THERMAL_TRIP_CRITICAL;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE2:
|
||
|
*type = THERMAL_TRIP_CONFIGURABLE_HI;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE1:
|
||
|
*type = THERMAL_TRIP_CONFIGURABLE_LOW;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE0:
|
||
|
*type = THERMAL_TRIP_CRITICAL_LOW;
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tsens_tz_activate_trip_type(struct thermal_zone_device *thermal,
|
||
|
int trip, enum thermal_trip_activation_mode mode)
|
||
|
{
|
||
|
struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
|
||
|
unsigned int reg_cntl, reg_th, code, hi_code, lo_code, mask;
|
||
|
|
||
|
if (!tm_sensor || trip < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
lo_code = TSENS_THRESHOLD_MIN_CODE;
|
||
|
hi_code = TSENS_THRESHOLD_MAX_CODE;
|
||
|
|
||
|
if (tmdev->hw_type == APQ_8064)
|
||
|
reg_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
|
||
|
else
|
||
|
reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
|
||
|
reg_th = readl_relaxed(TSENS_THRESHOLD_ADDR);
|
||
|
switch (trip) {
|
||
|
case TSENS_TRIP_STAGE3:
|
||
|
code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
mask = TSENS_MAX_STATUS_MASK;
|
||
|
|
||
|
if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE2:
|
||
|
code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
mask = TSENS_UPPER_STATUS_CLR;
|
||
|
|
||
|
if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE1:
|
||
|
code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
mask = TSENS_LOWER_STATUS_CLR;
|
||
|
|
||
|
if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE0:
|
||
|
code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
mask = TSENS_MIN_STATUS_MASK;
|
||
|
|
||
|
if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) {
|
||
|
if (tmdev->hw_type == APQ_8064)
|
||
|
writel_relaxed(reg_cntl | mask, TSENS_8064_STATUS_CNTL);
|
||
|
else
|
||
|
writel_relaxed(reg_cntl | mask, TSENS_CNTL_ADDR);
|
||
|
} else {
|
||
|
if (code < lo_code || code > hi_code) {
|
||
|
pr_info("%s with invalid code %x\n", __func__, code);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
if (tmdev->hw_type == APQ_8064)
|
||
|
writel_relaxed(reg_cntl & ~mask,
|
||
|
TSENS_8064_STATUS_CNTL);
|
||
|
else
|
||
|
writel_relaxed(reg_cntl & ~mask, TSENS_CNTL_ADDR);
|
||
|
}
|
||
|
mb();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tsens_tz_get_trip_temp(struct thermal_zone_device *thermal,
|
||
|
int trip, unsigned long *temp)
|
||
|
{
|
||
|
struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
|
||
|
unsigned int reg;
|
||
|
|
||
|
if (!tm_sensor || trip < 0 || !temp)
|
||
|
return -EINVAL;
|
||
|
|
||
|
reg = readl_relaxed(TSENS_THRESHOLD_ADDR);
|
||
|
switch (trip) {
|
||
|
case TSENS_TRIP_STAGE3:
|
||
|
reg = (reg & TSENS_THRESHOLD_MAX_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE2:
|
||
|
reg = (reg & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE1:
|
||
|
reg = (reg & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE0:
|
||
|
reg = (reg & TSENS_THRESHOLD_MIN_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
*temp = tsens_tz_code_to_degC(reg, tm_sensor->sensor_num);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tsens_tz_get_crit_temp(struct thermal_zone_device *thermal,
|
||
|
unsigned long *temp)
|
||
|
{
|
||
|
return tsens_tz_get_trip_temp(thermal, TSENS_TRIP_STAGE3, temp);
|
||
|
}
|
||
|
|
||
|
static int tsens_tz_notify(struct thermal_zone_device *thermal,
|
||
|
int count, enum thermal_trip_type type)
|
||
|
{
|
||
|
/* TSENS driver does not shutdown the device.
|
||
|
All Thermal notification are sent to the
|
||
|
thermal daemon to take appropriate action */
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int tsens_tz_set_trip_temp(struct thermal_zone_device *thermal,
|
||
|
int trip, long temp)
|
||
|
{
|
||
|
struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
|
||
|
unsigned int reg_th, reg_cntl;
|
||
|
int code, hi_code, lo_code, code_err_chk;
|
||
|
|
||
|
code_err_chk = code = tsens_tz_degC_to_code(temp,
|
||
|
tm_sensor->sensor_num);
|
||
|
if (!tm_sensor || trip < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
lo_code = TSENS_THRESHOLD_MIN_CODE;
|
||
|
hi_code = TSENS_THRESHOLD_MAX_CODE;
|
||
|
|
||
|
if (tmdev->hw_type == APQ_8064)
|
||
|
reg_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
|
||
|
else
|
||
|
reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
reg_th = readl_relaxed(TSENS_THRESHOLD_ADDR);
|
||
|
switch (trip) {
|
||
|
case TSENS_TRIP_STAGE3:
|
||
|
code <<= TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
reg_th &= ~TSENS_THRESHOLD_MAX_LIMIT_MASK;
|
||
|
|
||
|
if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE2:
|
||
|
code <<= TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
reg_th &= ~TSENS_THRESHOLD_UPPER_LIMIT_MASK;
|
||
|
|
||
|
if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE1:
|
||
|
code <<= TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
reg_th &= ~TSENS_THRESHOLD_LOWER_LIMIT_MASK;
|
||
|
|
||
|
if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
|
||
|
lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
break;
|
||
|
case TSENS_TRIP_STAGE0:
|
||
|
code <<= TSENS_THRESHOLD_MIN_LIMIT_SHIFT;
|
||
|
reg_th &= ~TSENS_THRESHOLD_MIN_LIMIT_MASK;
|
||
|
|
||
|
if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
|
||
|
hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_MAX_LIMIT_SHIFT;
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (code_err_chk < lo_code || code_err_chk > hi_code)
|
||
|
return -EINVAL;
|
||
|
|
||
|
writel_relaxed(reg_th | code, TSENS_THRESHOLD_ADDR);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct thermal_zone_device_ops tsens_thermal_zone_ops = {
|
||
|
.get_temp = tsens_tz_get_temp,
|
||
|
.get_mode = tsens_tz_get_mode,
|
||
|
.set_mode = tsens_tz_set_mode,
|
||
|
.get_trip_type = tsens_tz_get_trip_type,
|
||
|
.activate_trip_type = tsens_tz_activate_trip_type,
|
||
|
.get_trip_temp = tsens_tz_get_trip_temp,
|
||
|
.set_trip_temp = tsens_tz_set_trip_temp,
|
||
|
.get_crit_temp = tsens_tz_get_crit_temp,
|
||
|
.notify = tsens_tz_notify,
|
||
|
};
|
||
|
|
||
|
static void notify_uspace_tsens_fn(struct work_struct *work)
|
||
|
{
|
||
|
struct tsens_tm_device_sensor *tm = container_of(work,
|
||
|
struct tsens_tm_device_sensor, work);
|
||
|
|
||
|
sysfs_notify(&tm->tz_dev->device.kobj,
|
||
|
NULL, "type");
|
||
|
}
|
||
|
|
||
|
static void tsens_scheduler_fn(struct work_struct *work)
|
||
|
{
|
||
|
struct tsens_tm_device *tm = container_of(work, struct tsens_tm_device,
|
||
|
tsens_work);
|
||
|
unsigned int threshold, threshold_low, i, code, reg, sensor, mask;
|
||
|
unsigned int sensor_addr;
|
||
|
bool upper_th_x, lower_th_x;
|
||
|
int adc_code;
|
||
|
|
||
|
if (tmdev->hw_type == APQ_8064) {
|
||
|
reg = readl_relaxed(TSENS_8064_STATUS_CNTL);
|
||
|
writel_relaxed(reg | TSENS_LOWER_STATUS_CLR |
|
||
|
TSENS_UPPER_STATUS_CLR, TSENS_8064_STATUS_CNTL);
|
||
|
} else {
|
||
|
reg = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
writel_relaxed(reg | TSENS_LOWER_STATUS_CLR |
|
||
|
TSENS_UPPER_STATUS_CLR, TSENS_CNTL_ADDR);
|
||
|
}
|
||
|
|
||
|
mask = ~(TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR);
|
||
|
threshold = readl_relaxed(TSENS_THRESHOLD_ADDR);
|
||
|
threshold_low = (threshold & TSENS_THRESHOLD_LOWER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT;
|
||
|
threshold = (threshold & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
|
||
|
>> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT;
|
||
|
sensor = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
if (tmdev->hw_type == APQ_8064) {
|
||
|
reg = readl_relaxed(TSENS_8064_STATUS_CNTL);
|
||
|
sensor &= (uint32_t) TSENS_8064_SENSORS_EN;
|
||
|
} else {
|
||
|
reg = sensor;
|
||
|
sensor &= (uint32_t) SENSORS_EN;
|
||
|
}
|
||
|
sensor >>= TSENS_SENSOR0_SHIFT;
|
||
|
sensor_addr = (unsigned int)TSENS_S0_STATUS_ADDR;
|
||
|
for (i = 0; i < tmdev->tsens_num_sensor; i++) {
|
||
|
if (i == TSENS_8064_SEQ_SENSORS)
|
||
|
sensor_addr += TSENS_8064_S4_S5_OFFSET;
|
||
|
if (sensor & TSENS_MASK1) {
|
||
|
code = readl_relaxed(sensor_addr);
|
||
|
upper_th_x = code >= threshold;
|
||
|
lower_th_x = code <= threshold_low;
|
||
|
if (upper_th_x)
|
||
|
mask |= TSENS_UPPER_STATUS_CLR;
|
||
|
if (lower_th_x)
|
||
|
mask |= TSENS_LOWER_STATUS_CLR;
|
||
|
if (upper_th_x || lower_th_x) {
|
||
|
/* Notify user space */
|
||
|
schedule_work(&tm->sensor[i].work);
|
||
|
adc_code = readl_relaxed(sensor_addr);
|
||
|
pr_debug("Trigger (%d degrees) for sensor %d\n",
|
||
|
tsens_tz_code_to_degC(adc_code, i), i);
|
||
|
}
|
||
|
}
|
||
|
sensor >>= 1;
|
||
|
sensor_addr += TSENS_SENSOR_STATUS_SIZE;
|
||
|
}
|
||
|
if (tmdev->hw_type == APQ_8064)
|
||
|
writel_relaxed(reg & mask, TSENS_8064_STATUS_CNTL);
|
||
|
else
|
||
|
writel_relaxed(reg & mask, TSENS_CNTL_ADDR);
|
||
|
mb();
|
||
|
}
|
||
|
|
||
|
static irqreturn_t tsens_isr(int irq, void *data)
|
||
|
{
|
||
|
schedule_work(&tmdev->tsens_work);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_PM
|
||
|
static int tsens_suspend(struct device *dev)
|
||
|
{
|
||
|
int i = 0;
|
||
|
|
||
|
tmdev->pm_tsens_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR);
|
||
|
tmdev->pm_tsens_cntl = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
writel_relaxed(tmdev->pm_tsens_cntl &
|
||
|
~(TSENS_8960_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
|
||
|
tmdev->prev_reading_avail = 0;
|
||
|
for (i = 0; i < tmdev->tsens_num_sensor; i++)
|
||
|
tmdev->sensor[i].mode = THERMAL_DEVICE_DISABLED;
|
||
|
disable_irq_nosync(TSENS_UPPER_LOWER_INT);
|
||
|
mb();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tsens_resume(struct device *dev)
|
||
|
{
|
||
|
unsigned int reg_cntl = 0, reg_cfg = 0, reg_sensor_mask = 0;
|
||
|
unsigned int reg_status_cntl = 0, reg_thr_data = 0, i = 0;
|
||
|
|
||
|
reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
writel_relaxed(reg_cntl | TSENS_SW_RST, TSENS_CNTL_ADDR);
|
||
|
|
||
|
if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) {
|
||
|
reg_cntl |= TSENS_8960_SLP_CLK_ENA |
|
||
|
(TSENS_MEASURE_PERIOD << 18) |
|
||
|
TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
|
||
|
SENSORS_EN;
|
||
|
writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
|
||
|
} else if (tmdev->hw_type == APQ_8064) {
|
||
|
reg_cntl |= TSENS_8960_SLP_CLK_ENA |
|
||
|
(TSENS_MEASURE_PERIOD << 18) |
|
||
|
(((1 << tmdev->tsens_num_sensor) - 1)
|
||
|
<< TSENS_SENSOR0_SHIFT);
|
||
|
writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
|
||
|
reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
|
||
|
reg_status_cntl |= TSENS_MIN_STATUS_MASK |
|
||
|
TSENS_MAX_STATUS_MASK;
|
||
|
writel_relaxed(reg_status_cntl, TSENS_8064_STATUS_CNTL);
|
||
|
}
|
||
|
|
||
|
reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR);
|
||
|
reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) |
|
||
|
(TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT);
|
||
|
writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR);
|
||
|
|
||
|
writel_relaxed((tmdev->pm_tsens_cntl & TSENS_CNTL_RESUME_MASK),
|
||
|
TSENS_CNTL_ADDR);
|
||
|
reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
writel_relaxed(tmdev->pm_tsens_thr_data, TSENS_THRESHOLD_ADDR);
|
||
|
reg_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR);
|
||
|
if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615)
|
||
|
reg_sensor_mask = ((reg_cntl & TSENS_8960_SENSOR_MASK)
|
||
|
>> TSENS_SENSOR0_SHIFT);
|
||
|
else {
|
||
|
reg_sensor_mask = ((reg_cntl & TSENS_8064_SENSOR_MASK)
|
||
|
>> TSENS_SENSOR0_SHIFT);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < tmdev->tsens_num_sensor; i++) {
|
||
|
if (reg_sensor_mask & TSENS_MASK1)
|
||
|
tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
|
||
|
reg_sensor_mask >>= 1;
|
||
|
}
|
||
|
|
||
|
enable_irq(TSENS_UPPER_LOWER_INT);
|
||
|
mb();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct dev_pm_ops tsens_pm_ops = {
|
||
|
.suspend = tsens_suspend,
|
||
|
.resume = tsens_resume,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static void tsens_disable_mode(void)
|
||
|
{
|
||
|
unsigned int reg_cntl = 0;
|
||
|
|
||
|
reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615 ||
|
||
|
tmdev->hw_type == APQ_8064)
|
||
|
writel_relaxed(reg_cntl &
|
||
|
~((((1 << tmdev->tsens_num_sensor) - 1) <<
|
||
|
TSENS_SENSOR0_SHIFT) | TSENS_8960_SLP_CLK_ENA
|
||
|
| TSENS_EN), TSENS_CNTL_ADDR);
|
||
|
else if (tmdev->hw_type == MSM_8660)
|
||
|
writel_relaxed(reg_cntl &
|
||
|
~((((1 << tmdev->tsens_num_sensor) - 1) <<
|
||
|
TSENS_SENSOR0_SHIFT) | TSENS_8660_SLP_CLK_ENA
|
||
|
| TSENS_EN), TSENS_CNTL_ADDR);
|
||
|
}
|
||
|
|
||
|
static void tsens_hw_init(void)
|
||
|
{
|
||
|
unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
|
||
|
unsigned int reg_status_cntl = 0;
|
||
|
|
||
|
reg_cntl = readl_relaxed(TSENS_CNTL_ADDR);
|
||
|
writel_relaxed(reg_cntl | TSENS_SW_RST, TSENS_CNTL_ADDR);
|
||
|
|
||
|
if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) {
|
||
|
reg_cntl |= TSENS_8960_SLP_CLK_ENA |
|
||
|
(TSENS_MEASURE_PERIOD << 18) |
|
||
|
TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR |
|
||
|
TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
|
||
|
SENSORS_EN;
|
||
|
writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
|
||
|
reg_cntl |= TSENS_EN;
|
||
|
writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
|
||
|
|
||
|
reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR);
|
||
|
reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) |
|
||
|
(TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT);
|
||
|
writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR);
|
||
|
} else if (tmdev->hw_type == MSM_8660) {
|
||
|
reg_cntl |= TSENS_8660_SLP_CLK_ENA | TSENS_EN |
|
||
|
(TSENS_MEASURE_PERIOD << 16) |
|
||
|
TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR |
|
||
|
TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
|
||
|
(((1 << tmdev->tsens_num_sensor) - 1) <<
|
||
|
TSENS_SENSOR0_SHIFT);
|
||
|
|
||
|
/* set TSENS_CONFIG bits (bits 29:28 of TSENS_CNTL) to '01';
|
||
|
this setting found to be optimal. */
|
||
|
reg_cntl = (reg_cntl & ~TSENS_8660_CONFIG_MASK) |
|
||
|
(TSENS_8660_CONFIG << TSENS_8660_CONFIG_SHIFT);
|
||
|
|
||
|
writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
|
||
|
} else if (tmdev->hw_type == APQ_8064) {
|
||
|
reg_cntl |= TSENS_8960_SLP_CLK_ENA |
|
||
|
(TSENS_MEASURE_PERIOD << 18) |
|
||
|
(((1 << tmdev->tsens_num_sensor) - 1)
|
||
|
<< TSENS_SENSOR0_SHIFT);
|
||
|
writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
|
||
|
reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL);
|
||
|
reg_status_cntl |= TSENS_LOWER_STATUS_CLR |
|
||
|
TSENS_UPPER_STATUS_CLR |
|
||
|
TSENS_MIN_STATUS_MASK |
|
||
|
TSENS_MAX_STATUS_MASK;
|
||
|
writel_relaxed(reg_status_cntl, TSENS_8064_STATUS_CNTL);
|
||
|
reg_cntl |= TSENS_EN;
|
||
|
writel_relaxed(reg_cntl, TSENS_CNTL_ADDR);
|
||
|
|
||
|
reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR);
|
||
|
reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) |
|
||
|
(TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT);
|
||
|
writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR);
|
||
|
}
|
||
|
|
||
|
reg_thr |= (TSENS_LOWER_LIMIT_TH << TSENS_THRESHOLD_LOWER_LIMIT_SHIFT) |
|
||
|
(TSENS_UPPER_LIMIT_TH << TSENS_THRESHOLD_UPPER_LIMIT_SHIFT) |
|
||
|
(TSENS_MIN_LIMIT_TH << TSENS_THRESHOLD_MIN_LIMIT_SHIFT) |
|
||
|
(TSENS_MAX_LIMIT_TH << TSENS_THRESHOLD_MAX_LIMIT_SHIFT);
|
||
|
writel_relaxed(reg_thr, TSENS_THRESHOLD_ADDR);
|
||
|
}
|
||
|
|
||
|
static int tsens_calib_sensors8660(void)
|
||
|
{
|
||
|
uint32_t *main_sensor_addr, sensor_shift, red_sensor_shift;
|
||
|
uint32_t sensor_mask, red_sensor_mask;
|
||
|
|
||
|
main_sensor_addr = TSENS_8660_QFPROM_ADDR;
|
||
|
sensor_shift = TSENS_SENSOR_SHIFT;
|
||
|
red_sensor_shift = sensor_shift + TSENS_RED_SHIFT;
|
||
|
sensor_mask = TSENS_THRESHOLD_MAX_CODE << sensor_shift;
|
||
|
red_sensor_mask = TSENS_THRESHOLD_MAX_CODE << red_sensor_shift;
|
||
|
tmdev->sensor[TSENS_MAIN_SENSOR].calib_data =
|
||
|
(readl_relaxed(main_sensor_addr) & sensor_mask)
|
||
|
>> sensor_shift;
|
||
|
tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup =
|
||
|
(readl_relaxed(main_sensor_addr)
|
||
|
& red_sensor_mask) >> red_sensor_shift;
|
||
|
if (tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup)
|
||
|
tmdev->sensor[TSENS_MAIN_SENSOR].calib_data =
|
||
|
tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup;
|
||
|
if (!tmdev->sensor[TSENS_MAIN_SENSOR].calib_data) {
|
||
|
pr_err("QFPROM TSENS calibration data not present\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
tmdev->sensor[TSENS_MAIN_SENSOR].offset = tmdev->tsens_factor *
|
||
|
TSENS_CAL_DEGC -
|
||
|
tmdev->sensor[TSENS_MAIN_SENSOR].slope_mul_tsens_factor *
|
||
|
tmdev->sensor[TSENS_MAIN_SENSOR].calib_data;
|
||
|
|
||
|
tmdev->prev_reading_avail = false;
|
||
|
INIT_WORK(&tmdev->sensor[TSENS_MAIN_SENSOR].work,
|
||
|
notify_uspace_tsens_fn);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tsens_calib_sensors8960(void)
|
||
|
{
|
||
|
uint32_t i;
|
||
|
uint8_t *main_sensor_addr, *backup_sensor_addr;
|
||
|
for (i = 0; i < tmdev->tsens_num_sensor; i++) {
|
||
|
main_sensor_addr = TSENS_8960_QFPROM_ADDR0 + i;
|
||
|
backup_sensor_addr = TSENS_8960_QFPROM_SPARE_ADDR0 + i;
|
||
|
|
||
|
tmdev->sensor[i].calib_data = readb_relaxed(main_sensor_addr);
|
||
|
tmdev->sensor[i].calib_data_backup =
|
||
|
readb_relaxed(backup_sensor_addr);
|
||
|
if (tmdev->sensor[i].calib_data_backup)
|
||
|
tmdev->sensor[i].calib_data =
|
||
|
tmdev->sensor[i].calib_data_backup;
|
||
|
if (!tmdev->sensor[i].calib_data) {
|
||
|
pr_err("QFPROM TSENS calibration data not present\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
tmdev->sensor[i].offset = (TSENS_CAL_DEGC *
|
||
|
tmdev->tsens_factor)
|
||
|
- (tmdev->sensor[i].calib_data *
|
||
|
tmdev->sensor[i].slope_mul_tsens_factor);
|
||
|
tmdev->prev_reading_avail = false;
|
||
|
INIT_WORK(&tmdev->sensor[i].work, notify_uspace_tsens_fn);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tsens_calib_sensors(void)
|
||
|
{
|
||
|
int rc = -ENODEV;
|
||
|
|
||
|
if (tmdev->hw_type == MSM_8660)
|
||
|
rc = tsens_calib_sensors8660();
|
||
|
else if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615 ||
|
||
|
tmdev->hw_type == APQ_8064)
|
||
|
rc = tsens_calib_sensors8960();
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
int msm_tsens_early_init(struct tsens_platform_data *pdata)
|
||
|
{
|
||
|
int rc = 0, i;
|
||
|
|
||
|
if (!pdata) {
|
||
|
pr_err("No TSENS Platform data\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
tmdev = kzalloc(sizeof(struct tsens_tm_device) +
|
||
|
pdata->tsens_num_sensor *
|
||
|
sizeof(struct tsens_tm_device_sensor),
|
||
|
GFP_ATOMIC);
|
||
|
if (tmdev == NULL) {
|
||
|
pr_err("%s: kzalloc() failed.\n", __func__);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < pdata->tsens_num_sensor; i++)
|
||
|
tmdev->sensor[i].slope_mul_tsens_factor = pdata->slope[i];
|
||
|
tmdev->tsens_factor = pdata->tsens_factor;
|
||
|
tmdev->tsens_num_sensor = pdata->tsens_num_sensor;
|
||
|
tmdev->hw_type = pdata->hw_type;
|
||
|
|
||
|
rc = tsens_calib_sensors();
|
||
|
if (rc < 0) {
|
||
|
kfree(tmdev);
|
||
|
tmdev = NULL;
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
if (tmdev->hw_type == APQ_8064)
|
||
|
tsens_status_cntl_start = 0;
|
||
|
else
|
||
|
tsens_status_cntl_start = TSENS_STATUS_CNTL_OFFSET;
|
||
|
|
||
|
tsens_hw_init();
|
||
|
|
||
|
pr_debug("msm_tsens_early_init: done\n");
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
static int __devinit tsens_tm_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
int rc, i;
|
||
|
|
||
|
if (!tmdev) {
|
||
|
pr_info("%s : TSENS early init not done.\n", __func__);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < tmdev->tsens_num_sensor; i++) {
|
||
|
char name[18];
|
||
|
snprintf(name, sizeof(name), "tsens_tz_sensor%d", i);
|
||
|
tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
|
||
|
tmdev->sensor[i].sensor_num = i;
|
||
|
tmdev->sensor[i].tz_dev = thermal_zone_device_register(name,
|
||
|
TSENS_TRIP_NUM, &tmdev->sensor[i],
|
||
|
&tsens_thermal_zone_ops, 0, 0, 0, 0);
|
||
|
if (IS_ERR(tmdev->sensor[i].tz_dev)) {
|
||
|
pr_err("%s: thermal_zone_device_register() failed.\n",
|
||
|
__func__);
|
||
|
rc = -ENODEV;
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rc = request_irq(TSENS_UPPER_LOWER_INT, tsens_isr,
|
||
|
IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev);
|
||
|
if (rc < 0) {
|
||
|
pr_err("%s: request_irq FAIL: %d\n", __func__, rc);
|
||
|
for (i = 0; i < tmdev->tsens_num_sensor; i++)
|
||
|
thermal_zone_device_unregister(tmdev->sensor[i].tz_dev);
|
||
|
goto fail;
|
||
|
}
|
||
|
INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn);
|
||
|
|
||
|
pr_debug("%s: OK\n", __func__);
|
||
|
mb();
|
||
|
return 0;
|
||
|
fail:
|
||
|
tsens_disable_mode();
|
||
|
kfree(tmdev);
|
||
|
tmdev = NULL;
|
||
|
mb();
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
static int __devexit tsens_tm_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
tsens_disable_mode();
|
||
|
mb();
|
||
|
free_irq(TSENS_UPPER_LOWER_INT, tmdev);
|
||
|
for (i = 0; i < tmdev->tsens_num_sensor; i++)
|
||
|
thermal_zone_device_unregister(tmdev->sensor[i].tz_dev);
|
||
|
kfree(tmdev);
|
||
|
tmdev = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct platform_driver tsens_tm_driver = {
|
||
|
.probe = tsens_tm_probe,
|
||
|
.remove = tsens_tm_remove,
|
||
|
.driver = {
|
||
|
.name = "tsens8960-tm",
|
||
|
.owner = THIS_MODULE,
|
||
|
#ifdef CONFIG_PM
|
||
|
.pm = &tsens_pm_ops,
|
||
|
#endif
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static int __init _tsens_tm_init(void)
|
||
|
{
|
||
|
return platform_driver_register(&tsens_tm_driver);
|
||
|
}
|
||
|
module_init(_tsens_tm_init);
|
||
|
|
||
|
static void __exit _tsens_tm_remove(void)
|
||
|
{
|
||
|
platform_driver_unregister(&tsens_tm_driver);
|
||
|
}
|
||
|
module_exit(_tsens_tm_remove);
|
||
|
|
||
|
MODULE_LICENSE("GPL v2");
|
||
|
MODULE_DESCRIPTION("MSM8960 Temperature Sensor driver");
|
||
|
MODULE_VERSION("1.0");
|
||
|
MODULE_ALIAS("platform:tsens8960-tm");
|