android_kernel_xiaomi_sm7250/drivers/thermal/tsens1xxx.c
Siddartha Mohanadoss 3f67fe6c0a thermal: tsens: Add TSENS driver snapshot
Temperature sensor (TSENS) driver is used by thermal
client to set temperature thresholds and send notification
once set thresholds are crossed. Clients can read from
supported TSENS sensors from the TSENS controller.
There can be multiple instances of the TSENS controllers
to support multiple sensors across the system.

This snapshot is taken as of msm-4.14
'commit <405fe31> ("Merge "ARM: dts: msm: Enable
remotefs for trinket"")'.

Change-Id: I0217c55d8462812bfa545fdadf545897da66bebb
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
2018-12-21 16:43:14 -08:00

523 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/vmalloc.h>
#include "tsens.h"
#include "thermal_core.h"
#define TSENS_DRIVER_NAME "msm-tsens"
#define TSENS_UPPER_LOWER_INTERRUPT_CTRL(n) (n)
#define TSENS_INTERRUPT_EN BIT(0)
#define TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(n) ((n) + 0x04)
#define TSENS_UPPER_STATUS_CLR BIT(21)
#define TSENS_LOWER_STATUS_CLR BIT(20)
#define TSENS_UPPER_THRESHOLD_MASK 0xffc00
#define TSENS_LOWER_THRESHOLD_MASK 0x3ff
#define TSENS_UPPER_THRESHOLD_SHIFT 10
#define TSENS_S0_STATUS_ADDR(n) ((n) + 0x30)
#define TSENS_SN_ADDR_OFFSET 0x4
#define TSENS_SN_STATUS_TEMP_MASK 0x3ff
#define TSENS_SN_STATUS_LOWER_STATUS BIT(11)
#define TSENS_SN_STATUS_UPPER_STATUS BIT(12)
#define TSENS_STATUS_ADDR_OFFSET 2
#define TSENS_TRDY_MASK BIT(0)
#define TSENS_SN_STATUS_ADDR(n) ((n) + 0x44)
#define TSENS_SN_STATUS_VALID BIT(14)
#define TSENS_SN_STATUS_VALID_MASK 0x4000
#define TSENS_TRDY_ADDR(n) ((n) + 0x84)
#define TSENS_CTRL_ADDR(n) (n)
#define TSENS_EN BIT(0)
#define TSENS_CTRL_SENSOR_EN_MASK(n) ((n >> 3) & 0x7ff)
#define TSENS_TRDY_RDY_MIN_TIME 2000
#define TSENS_TRDY_RDY_MAX_TIME 2100
#define TSENS_THRESHOLD_MAX_CODE 0x3ff
#define TSENS_THRESHOLD_MIN_CODE 0x0
#define TSENS_SCALE_MILLIDEG 1000
static int code_to_degc(u32 adc_code, const struct tsens_sensor *sensor)
{
int degc, num, den;
num = (adc_code * SLOPE_FACTOR) - sensor->offset;
den = sensor->slope;
if (num > 0)
degc = num + (den / 2);
else if (num < 0)
degc = num - (den / 2);
else
degc = num;
degc /= den;
return degc;
}
static int degc_to_code(int degc, const struct tsens_sensor *sensor)
{
int code = ((degc * sensor->slope)
+ sensor->offset)/SLOPE_FACTOR;
if (code > TSENS_THRESHOLD_MAX_CODE)
code = TSENS_THRESHOLD_MAX_CODE;
else if (code < TSENS_THRESHOLD_MIN_CODE)
code = TSENS_THRESHOLD_MIN_CODE;
pr_debug("raw_code:0x%x, degc:%d\n",
code, degc);
return code;
}
static int tsens1xxx_get_temp(struct tsens_sensor *sensor, int *temp)
{
struct tsens_device *tmdev = NULL;
unsigned int code;
void __iomem *sensor_addr;
void __iomem *trdy_addr;
int last_temp = 0, last_temp2 = 0, last_temp3 = 0;
bool last_temp_valid = false, last_temp2_valid = false;
bool last_temp3_valid = false;
if (!sensor)
return -EINVAL;
tmdev = sensor->tmdev;
trdy_addr = TSENS_TRDY_ADDR(tmdev->tsens_tm_addr);
sensor_addr = TSENS_SN_STATUS_ADDR(tmdev->tsens_tm_addr);
code = readl_relaxed(sensor_addr +
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
last_temp = code & TSENS_SN_STATUS_TEMP_MASK;
if (tmdev->ctrl_data->valid_status_check) {
if (code & TSENS_SN_STATUS_VALID)
last_temp_valid = true;
else {
code = readl_relaxed(sensor_addr +
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
last_temp2 = code & TSENS_SN_STATUS_TEMP_MASK;
if (code & TSENS_SN_STATUS_VALID) {
last_temp = last_temp2;
last_temp2_valid = true;
} else {
code = readl_relaxed(sensor_addr +
(sensor->hw_id <<
TSENS_STATUS_ADDR_OFFSET));
last_temp3 = code & TSENS_SN_STATUS_TEMP_MASK;
if (code & TSENS_SN_STATUS_VALID) {
last_temp = last_temp3;
last_temp3_valid = true;
}
}
}
}
if ((tmdev->ctrl_data->valid_status_check) &&
(!last_temp_valid && !last_temp2_valid && !last_temp3_valid)) {
if (last_temp == last_temp2)
last_temp = last_temp2;
else if (last_temp2 == last_temp3)
last_temp = last_temp3;
}
*temp = code_to_degc(last_temp, sensor);
*temp = *temp * TSENS_SCALE_MILLIDEG;
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, (u32)sensor->hw_id,
TSENS_DBG_LOG_TEMP_READS, temp);
return 0;
}
static int tsens_tz_activate_trip_type(struct tsens_sensor *tm_sensor,
int trip, enum thermal_device_mode mode)
{
struct tsens_device *tmdev = NULL;
unsigned int reg_cntl, code, hi_code, lo_code, mask;
/* clear the interrupt and unmask */
if (!tm_sensor || trip < 0)
return -EINVAL;
tmdev = tm_sensor->tmdev;
if (!tmdev)
return -EINVAL;
lo_code = TSENS_THRESHOLD_MIN_CODE;
hi_code = TSENS_THRESHOLD_MAX_CODE;
reg_cntl = readl_relaxed((TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET)));
switch (trip) {
case THERMAL_TRIP_CONFIGURABLE_HI:
tmdev->sensor[tm_sensor->hw_id].thr_state.high_th_state = mode;
code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
>> TSENS_UPPER_THRESHOLD_SHIFT;
mask = TSENS_UPPER_STATUS_CLR;
if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
lo_code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
break;
case THERMAL_TRIP_CONFIGURABLE_LOW:
tmdev->sensor[tm_sensor->hw_id].thr_state.low_th_state = mode;
code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK);
mask = TSENS_LOWER_STATUS_CLR;
if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
hi_code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK)
>> TSENS_UPPER_THRESHOLD_SHIFT;
break;
default:
return -EINVAL;
}
if (mode == THERMAL_DEVICE_DISABLED)
writel_relaxed(reg_cntl | mask,
(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id * TSENS_SN_ADDR_OFFSET)));
else
writel_relaxed(reg_cntl & ~mask,
(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id * TSENS_SN_ADDR_OFFSET)));
/* Enable the thresholds */
mb();
return 0;
}
static int tsens1xxx_set_trip_temp(struct tsens_sensor *tm_sensor,
int low_temp, int high_temp)
{
unsigned int reg_cntl;
unsigned long flags;
struct tsens_device *tmdev = NULL;
int high_code, low_code, rc = 0;
if (!tm_sensor)
return -EINVAL;
tmdev = tm_sensor->tmdev;
if (!tmdev)
return -EINVAL;
spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
if (high_temp != INT_MAX) {
high_temp /= TSENS_SCALE_MILLIDEG;
high_code = degc_to_code(high_temp, tm_sensor);
tmdev->sensor[tm_sensor->hw_id].thr_state.high_adc_code =
high_code;
tmdev->sensor[tm_sensor->hw_id].thr_state.high_temp =
high_temp;
reg_cntl = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET));
high_code <<= TSENS_UPPER_THRESHOLD_SHIFT;
reg_cntl &= ~TSENS_UPPER_THRESHOLD_MASK;
writel_relaxed(reg_cntl | high_code,
(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET)));
}
if (low_temp != INT_MIN) {
low_temp /= TSENS_SCALE_MILLIDEG;
low_code = degc_to_code(low_temp, tm_sensor);
tmdev->sensor[tm_sensor->hw_id].thr_state.low_adc_code =
low_code;
tmdev->sensor[tm_sensor->hw_id].thr_state.low_temp =
low_temp;
reg_cntl = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET));
reg_cntl &= ~TSENS_LOWER_THRESHOLD_MASK;
writel_relaxed(reg_cntl | low_code,
(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR
(tmdev->tsens_tm_addr) +
(tm_sensor->hw_id *
TSENS_SN_ADDR_OFFSET)));
}
/* Set trip temperature thresholds */
mb();
if (high_temp != INT_MAX) {
rc = tsens_tz_activate_trip_type(tm_sensor,
THERMAL_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_ENABLED);
if (rc) {
pr_err("trip high enable error :%d\n", rc);
goto fail;
}
} else {
rc = tsens_tz_activate_trip_type(tm_sensor,
THERMAL_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_DISABLED);
if (rc) {
pr_err("trip high disable error :%d\n", rc);
goto fail;
}
}
if (low_temp != INT_MIN) {
rc = tsens_tz_activate_trip_type(tm_sensor,
THERMAL_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_ENABLED);
if (rc) {
pr_err("trip low enable activation error :%d\n", rc);
goto fail;
}
} else {
rc = tsens_tz_activate_trip_type(tm_sensor,
THERMAL_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_DISABLED);
if (rc) {
pr_err("trip low disable error :%d\n", rc);
goto fail;
}
}
fail:
spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
return rc;
}
static irqreturn_t tsens_irq_thread(int irq, void *data)
{
struct tsens_device *tm = data;
unsigned int i, status, threshold, temp, th_temp;
unsigned long flags;
void __iomem *sensor_status_addr;
void __iomem *sensor_status_ctrl_addr;
u32 rc = 0, addr_offset;
sensor_status_addr = TSENS_SN_STATUS_ADDR(tm->tsens_tm_addr);
sensor_status_ctrl_addr =
TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tm->tsens_tm_addr);
for (i = 0; i < tm->ctrl_data->num_sensors; i++) {
bool upper_thr = false, lower_thr = false;
if (IS_ERR(tm->sensor[i].tzd))
continue;
rc = tsens1xxx_get_temp(&tm->sensor[i], &temp);
if (rc) {
pr_debug("Error:%d reading temp sensor:%d\n", rc, i);
continue;
}
spin_lock_irqsave(&tm->tsens_upp_low_lock, flags);
addr_offset = tm->sensor[i].hw_id *
TSENS_SN_ADDR_OFFSET;
status = readl_relaxed(sensor_status_addr + addr_offset);
threshold = readl_relaxed(sensor_status_ctrl_addr +
addr_offset);
if (status & TSENS_SN_STATUS_UPPER_STATUS) {
writel_relaxed(threshold | TSENS_UPPER_STATUS_CLR,
TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(
tm->tsens_tm_addr + addr_offset));
th_temp = code_to_degc((threshold &
TSENS_UPPER_THRESHOLD_MASK) >>
TSENS_UPPER_THRESHOLD_SHIFT,
(tm->sensor + i));
if (th_temp > (temp/TSENS_SCALE_MILLIDEG)) {
pr_debug("Re-arm high threshold\n");
rc = tsens_tz_activate_trip_type(
&tm->sensor[i],
THERMAL_TRIP_CONFIGURABLE_HI,
THERMAL_DEVICE_ENABLED);
if (rc)
pr_err("high rearm failed\n");
} else {
upper_thr = true;
tm->sensor[i].thr_state.high_th_state =
THERMAL_DEVICE_DISABLED;
}
}
if (status & TSENS_SN_STATUS_LOWER_STATUS) {
writel_relaxed(threshold | TSENS_LOWER_STATUS_CLR,
TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(
tm->tsens_tm_addr + addr_offset));
th_temp = code_to_degc((threshold &
TSENS_LOWER_THRESHOLD_MASK),
(tm->sensor + i));
if (th_temp < (temp/TSENS_SCALE_MILLIDEG)) {
pr_debug("Re-arm Low threshold\n");
rc = tsens_tz_activate_trip_type(
&tm->sensor[i],
THERMAL_TRIP_CONFIGURABLE_LOW,
THERMAL_DEVICE_ENABLED);
if (rc)
pr_err("low rearm failed\n");
} else {
lower_thr = true;
tm->sensor[i].thr_state.low_th_state =
THERMAL_DEVICE_DISABLED;
}
}
spin_unlock_irqrestore(&tm->tsens_upp_low_lock, flags);
if (upper_thr || lower_thr) {
pr_debug("sensor:%d trigger temp (%d degC)\n",
tm->sensor[i].hw_id,
code_to_degc((status &
TSENS_SN_STATUS_TEMP_MASK),
(tm->sensor + i)));
of_thermal_handle_trip(tm->sensor[i].tzd);
}
}
/* Disable monitoring sensor trip threshold for triggered sensor */
mb();
if (tm->ops->dbg)
tm->ops->dbg(tm, 0, TSENS_DBG_LOG_INTERRUPT_TIMESTAMP, NULL);
return IRQ_HANDLED;
}
static int tsens1xxx_hw_sensor_en(struct tsens_device *tmdev,
u32 sensor_id)
{
void __iomem *srot_addr;
unsigned int srot_val, sensor_en;
srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
srot_val = readl_relaxed(srot_addr);
srot_val = TSENS_CTRL_SENSOR_EN_MASK(srot_val);
sensor_en = ((1 << sensor_id) & srot_val);
return sensor_en;
}
static int tsens1xxx_hw_init(struct tsens_device *tmdev)
{
void __iomem *srot_addr;
unsigned int srot_val;
srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
srot_val = readl_relaxed(srot_addr);
if (!(srot_val & TSENS_EN)) {
pr_err("TSENS device is not enabled\n");
return -ENODEV;
}
writel_relaxed(TSENS_INTERRUPT_EN,
TSENS_UPPER_LOWER_INTERRUPT_CTRL(tmdev->tsens_tm_addr));
spin_lock_init(&tmdev->tsens_upp_low_lock);
if (tmdev->ctrl_data->mtc) {
if (tmdev->ops->dbg)
tmdev->ops->dbg(tmdev, 0, TSENS_DBG_MTC_DATA, NULL);
}
return 0;
}
static const struct tsens_irqs tsens1xxx_irqs[] = {
{ "tsens-upper-lower", tsens_irq_thread},
};
static int tsens1xxx_register_interrupts(struct tsens_device *tmdev)
{
struct platform_device *pdev;
int i, rc;
if (!tmdev)
return -EINVAL;
pdev = tmdev->pdev;
for (i = 0; i < ARRAY_SIZE(tsens1xxx_irqs); i++) {
int irq;
irq = platform_get_irq_byname(pdev, tsens1xxx_irqs[i].name);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq %s\n",
tsens1xxx_irqs[i].name);
return irq;
}
rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
tsens1xxx_irqs[i].handler,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
tsens1xxx_irqs[i].name, tmdev);
if (rc) {
dev_err(&pdev->dev, "failed to get irq %s\n",
tsens1xxx_irqs[i].name);
return rc;
}
enable_irq_wake(irq);
}
return 0;
}
static const struct tsens_ops ops_tsens1xxx = {
.hw_init = tsens1xxx_hw_init,
.get_temp = tsens1xxx_get_temp,
.set_trips = tsens1xxx_set_trip_temp,
.interrupts_reg = tsens1xxx_register_interrupts,
.sensor_en = tsens1xxx_hw_sensor_en,
.calibrate = calibrate_8937,
.dbg = tsens2xxx_dbg,
};
const struct tsens_data data_tsens14xx = {
.num_sensors = TSENS_NUM_SENSORS_8937,
.ops = &ops_tsens1xxx,
.valid_status_check = true,
.mtc = true,
.ver_major = 1,
.ver_minor = 4,
};
static const struct tsens_ops ops_tsens1xxx_405 = {
.hw_init = tsens1xxx_hw_init,
.get_temp = tsens1xxx_get_temp,
.set_trips = tsens1xxx_set_trip_temp,
.interrupts_reg = tsens1xxx_register_interrupts,
.sensor_en = tsens1xxx_hw_sensor_en,
.calibrate = calibrate_405,
.dbg = tsens2xxx_dbg,
};
const struct tsens_data data_tsens14xx_405 = {
.num_sensors = TSENS_NUM_SENSORS_405,
.ops = &ops_tsens1xxx_405,
.valid_status_check = true,
.mtc = true,
.ver_major = 1,
.ver_minor = 4,
};