Commit c1997054 authored by Amit Kucheria's avatar Amit Kucheria Committed by Eduardo Valentin

drivers: thermal: tsens: Introduce reg_fields to deal with register description

As we add support for newer versions of the TSENS IP, the current
approach isn't scaling because registers and bitfields get moved around,
requiring platform-specific hacks in the code. By moving to regmap, we
can hide the register level differences away from the code.

Define a common set of registers and bit-fields that we care about
across the various tsens IP versions.
Signed-off-by: default avatarAmit Kucheria <amit.kucheria@linaro.org>
Signed-off-by: default avatarEduardo Valentin <edubezval@gmail.com>
parent 582a0c41
......@@ -12,13 +12,6 @@
#include <linux/regmap.h>
#include "tsens.h"
/* SROT */
#define TSENS_EN BIT(0)
/* TM */
#define STATUS_OFFSET 0x30
#define SN_ADDR_OFFSET 0x4
#define SN_ST_TEMP_MASK 0x3ff
#define CAL_DEGC_PT1 30
#define CAL_DEGC_PT2 120
#define SLOPE_FACTOR 1000
......@@ -95,18 +88,14 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
return degc;
}
int get_temp_common(struct tsens_priv *priv, int id, int *temp)
int get_temp_common(struct tsens_priv *priv, int i, int *temp)
{
struct tsens_sensor *s = &priv->sensor[id];
u32 code;
unsigned int status_reg;
struct tsens_sensor *s = &priv->sensor[i];
int last_temp = 0, ret;
status_reg = priv->tm_offset + STATUS_OFFSET + s->hw_id * SN_ADDR_OFFSET;
ret = regmap_read(priv->tm_map, status_reg, &code);
ret = regmap_field_read(priv->rf[LAST_TEMP_0 + s->hw_id], &last_temp);
if (ret)
return ret;
last_temp = code & SN_ST_TEMP_MASK;
*temp = code_to_degc(last_temp, s) * 1000;
......@@ -131,10 +120,9 @@ int __init init_common(struct tsens_priv *priv)
{
void __iomem *tm_base, *srot_base;
struct resource *res;
u32 code;
int ret;
u32 enabled;
int ret, i, j;
struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
u16 ctrl_offset = priv->reg_offsets[SROT_CTRL_OFFSET];
if (!op)
return -EINVAL;
......@@ -173,15 +161,43 @@ int __init init_common(struct tsens_priv *priv)
goto err_put_device;
}
if (priv->srot_map) {
ret = regmap_read(priv->srot_map, ctrl_offset, &code);
priv->rf[TSENS_EN] = devm_regmap_field_alloc(priv->dev, priv->srot_map,
priv->fields[TSENS_EN]);
if (IS_ERR(priv->rf[TSENS_EN])) {
ret = PTR_ERR(priv->rf[TSENS_EN]);
goto err_put_device;
}
ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
if (ret)
goto err_put_device;
if (!(code & TSENS_EN)) {
if (!enabled) {
dev_err(priv->dev, "tsens device is not enabled\n");
ret = -ENODEV;
goto err_put_device;
}
priv->rf[SENSOR_EN] = devm_regmap_field_alloc(priv->dev, priv->srot_map,
priv->fields[SENSOR_EN]);
if (IS_ERR(priv->rf[SENSOR_EN])) {
ret = PTR_ERR(priv->rf[SENSOR_EN]);
goto err_put_device;
}
/* now alloc regmap_fields in tm_map */
for (i = 0, j = LAST_TEMP_0; i < priv->num_sensors; i++, j++) {
priv->rf[j] = devm_regmap_field_alloc(priv->dev, priv->tm_map,
priv->fields[j]);
if (IS_ERR(priv->rf[j])) {
ret = PTR_ERR(priv->rf[j]);
goto err_put_device;
}
}
for (i = 0, j = VALID_0; i < priv->num_sensors; i++, j++) {
priv->rf[j] = devm_regmap_field_alloc(priv->dev, priv->tm_map,
priv->fields[j]);
if (IS_ERR(priv->rf[j])) {
ret = PTR_ERR(priv->rf[j]);
goto err_put_device;
}
}
return 0;
......
......@@ -6,6 +6,15 @@
#include <linux/platform_device.h>
#include "tsens.h"
/* ----- SROT ------ */
#define SROT_CTRL_OFF 0x0000
/* ----- TM ------ */
#define TM_INT_EN_OFF 0x0000
#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004
#define TM_Sn_STATUS_OFF 0x0030
#define TM_TRDY_OFF 0x005c
/* eeprom layout data for 8916 */
#define MSM8916_BASE0_MASK 0x0000007f
#define MSM8916_BASE1_MASK 0xfe000000
......@@ -308,6 +317,40 @@ static int calibrate_8974(struct tsens_priv *priv)
return 0;
}
/* v0.1: 8916, 8974 */
static const struct tsens_features tsens_v0_1_feat = {
.ver_major = VER_0_1,
.crit_int = 0,
.adc = 1,
.srot_split = 1,
};
static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* No VERSION information */
/* CTRL_OFFSET */
[TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
[TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
/* ----- TM ------ */
/* INTERRUPT ENABLE */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0),
/* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9),
/* No VALID field on v0.1 */
REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10),
REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11),
REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12),
/* No CRITICAL field on v0.1 */
REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13),
/* TRDY: 1=ready, 0=in progress */
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
};
static const struct tsens_ops ops_8916 = {
.init = init_common,
.calibrate = calibrate_8916,
......@@ -317,8 +360,10 @@ static const struct tsens_ops ops_8916 = {
const struct tsens_plat_data data_8916 = {
.num_sensors = 5,
.ops = &ops_8916,
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 },
.hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
static const struct tsens_ops ops_8974 = {
......@@ -330,5 +375,6 @@ static const struct tsens_ops ops_8974 = {
const struct tsens_plat_data data_8974 = {
.num_sensors = 11,
.ops = &ops_8974,
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 },
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
......@@ -4,50 +4,70 @@
* Copyright (c) 2018, Linaro Limited
*/
#include <linux/regmap.h>
#include <linux/bitops.h>
#include <linux/regmap.h>
#include "tsens.h"
#define STATUS_OFFSET 0xa0
/* ----- SROT ------ */
#define SROT_HW_VER_OFF 0x0000
#define SROT_CTRL_OFF 0x0004
/* ----- TM ------ */
#define TM_INT_EN_OFF 0x0004
#define TM_UPPER_LOWER_INT_STATUS_OFF 0x0008
#define TM_UPPER_LOWER_INT_CLEAR_OFF 0x000c
#define TM_UPPER_LOWER_INT_MASK_OFF 0x0010
#define TM_CRITICAL_INT_STATUS_OFF 0x0014
#define TM_CRITICAL_INT_CLEAR_OFF 0x0018
#define TM_CRITICAL_INT_MASK_OFF 0x001c
#define TM_Sn_UPPER_LOWER_THRESHOLD_OFF 0x0020
#define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060
#define TM_Sn_STATUS_OFF 0x00a0
#define TM_TRDY_OFF 0x00e4
#define LAST_TEMP_MASK 0xfff
#define STATUS_VALID_BIT BIT(21)
static int get_temp_tsens_v2(struct tsens_priv *priv, int id, int *temp)
{
struct tsens_sensor *s = &priv->sensor[id];
u32 code;
unsigned int status_reg;
u32 last_temp = 0, last_temp2 = 0, last_temp3 = 0;
u32 temp_idx = LAST_TEMP_0 + s->hw_id;
u32 valid_idx = VALID_0 + s->hw_id;
u32 last_temp = 0, last_temp2 = 0, last_temp3 = 0, valid;
int ret;
status_reg = priv->tm_offset + STATUS_OFFSET + s->hw_id * 4;
ret = regmap_read(priv->tm_map, status_reg, &code);
ret = regmap_field_read(priv->rf[temp_idx], &last_temp);
if (ret)
return ret;
last_temp = code & LAST_TEMP_MASK;
if (code & STATUS_VALID_BIT)
ret = regmap_field_read(priv->rf[valid_idx], &valid);
if (ret)
return ret;
if (valid)
goto done;
/* Try a second time */
ret = regmap_read(priv->tm_map, status_reg, &code);
ret = regmap_field_read(priv->rf[valid_idx], &valid);
if (ret)
return ret;
if (code & STATUS_VALID_BIT) {
last_temp = code & LAST_TEMP_MASK;
ret = regmap_field_read(priv->rf[temp_idx], &last_temp2);
if (ret)
return ret;
if (valid) {
last_temp = last_temp2;
goto done;
} else {
last_temp2 = code & LAST_TEMP_MASK;
}
/* Try a third/last time */
ret = regmap_read(priv->tm_map, status_reg, &code);
ret = regmap_field_read(priv->rf[valid_idx], &valid);
if (ret)
return ret;
if (code & STATUS_VALID_BIT) {
last_temp = code & LAST_TEMP_MASK;
ret = regmap_field_read(priv->rf[temp_idx], &last_temp3);
if (ret)
return ret;
if (valid) {
last_temp = last_temp3;
goto done;
} else {
last_temp3 = code & LAST_TEMP_MASK;
}
if (last_temp == last_temp2)
......@@ -61,6 +81,43 @@ static int get_temp_tsens_v2(struct tsens_priv *priv, int id, int *temp)
return 0;
}
/* v2.x: 8996, 8998, sdm845 */
static const struct tsens_features tsens_v2_feat = {
.ver_major = VER_2_X,
.crit_int = 1,
.adc = 0,
.srot_split = 1,
};
static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* VERSION */
[VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31),
[VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27),
[VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15),
/* CTRL_OFF */
[TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
[TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
/* ----- TM ------ */
/* INTERRUPT ENABLE */
/* v2 has separate enables for UPPER/LOWER/CRITICAL interrupts */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2),
/* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 11),
REG_FIELD_FOR_EACH_SENSOR16(VALID, TM_Sn_STATUS_OFF, 21, 21),
REG_FIELD_FOR_EACH_SENSOR16(MIN_STATUS, TM_Sn_STATUS_OFF, 16, 16),
REG_FIELD_FOR_EACH_SENSOR16(LOWER_STATUS, TM_Sn_STATUS_OFF, 17, 17),
REG_FIELD_FOR_EACH_SENSOR16(UPPER_STATUS, TM_Sn_STATUS_OFF, 18, 18),
REG_FIELD_FOR_EACH_SENSOR16(CRITICAL_STATUS, TM_Sn_STATUS_OFF, 19, 19),
REG_FIELD_FOR_EACH_SENSOR16(MAX_STATUS, TM_Sn_STATUS_OFF, 20, 20),
/* TRDY: 1=ready, 0=in progress */
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
};
static const struct tsens_ops ops_generic_v2 = {
.init = init_common,
.get_temp = get_temp_tsens_v2,
......@@ -68,12 +125,14 @@ static const struct tsens_ops ops_generic_v2 = {
const struct tsens_plat_data data_tsens_v2 = {
.ops = &ops_generic_v2,
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 },
.feat = &tsens_v2_feat,
.fields = tsens_v2_regfields,
};
/* Kept around for backward compatibility with old msm8996.dtsi */
const struct tsens_plat_data data_8996 = {
.num_sensors = 13,
.ops = &ops_generic_v2,
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 },
.feat = &tsens_v2_feat,
.fields = tsens_v2_regfields,
};
......@@ -144,9 +144,8 @@ static int tsens_probe(struct platform_device *pdev)
else
priv->sensor[i].hw_id = i;
}
for (i = 0; i < REG_ARRAY_SIZE; i++) {
priv->reg_offsets[i] = data->reg_offsets[i];
}
priv->feat = data->feat;
priv->fields = data->fields;
if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
return -EINVAL;
......
......@@ -11,9 +11,16 @@
#define TWO_PT_CALIB 0x3
#include <linux/thermal.h>
#include <linux/regmap.h>
struct tsens_priv;
enum tsens_ver {
VER_0_1 = 0,
VER_1_X,
VER_2_X,
};
/**
* struct tsens_sensor - data for each sensor connected to the tsens device
* @priv: tsens device instance that this sensor is connected to
......@@ -58,10 +65,189 @@ struct tsens_ops {
int (*get_trend)(struct tsens_priv *priv, int i, enum thermal_trend *trend);
};
enum reg_list {
SROT_CTRL_OFFSET,
#define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \
[_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \
[_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \
[_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \
[_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \
[_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \
[_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \
[_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \
[_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \
[_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \
[_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \
[_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit)
#define REG_FIELD_FOR_EACH_SENSOR16(_name, _offset, _startbit, _stopbit) \
[_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \
[_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \
[_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \
[_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \
[_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \
[_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \
[_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \
[_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \
[_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \
[_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \
[_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit), \
[_name##_##11] = REG_FIELD(_offset + 44, _startbit, _stopbit), \
[_name##_##12] = REG_FIELD(_offset + 48, _startbit, _stopbit), \
[_name##_##13] = REG_FIELD(_offset + 52, _startbit, _stopbit), \
[_name##_##14] = REG_FIELD(_offset + 56, _startbit, _stopbit), \
[_name##_##15] = REG_FIELD(_offset + 60, _startbit, _stopbit)
/* reg_field IDs to use as an index into an array */
enum regfield_ids {
/* ----- SROT ------ */
/* HW_VER */
VER_MAJOR = 0,
VER_MINOR,
VER_STEP,
/* CTRL_OFFSET */
TSENS_EN = 3,
TSENS_SW_RST,
SENSOR_EN,
CODE_OR_TEMP,
/* ----- TM ------ */
/* STATUS */
LAST_TEMP_0 = 7, /* Last temperature reading */
LAST_TEMP_1,
LAST_TEMP_2,
LAST_TEMP_3,
LAST_TEMP_4,
LAST_TEMP_5,
LAST_TEMP_6,
LAST_TEMP_7,
LAST_TEMP_8,
LAST_TEMP_9,
LAST_TEMP_10,
LAST_TEMP_11,
LAST_TEMP_12,
LAST_TEMP_13,
LAST_TEMP_14,
LAST_TEMP_15,
VALID_0 = 23, /* VALID reading or not */
VALID_1,
VALID_2,
VALID_3,
VALID_4,
VALID_5,
VALID_6,
VALID_7,
VALID_8,
VALID_9,
VALID_10,
VALID_11,
VALID_12,
VALID_13,
VALID_14,
VALID_15,
MIN_STATUS_0, /* MIN threshold violated */
MIN_STATUS_1,
MIN_STATUS_2,
MIN_STATUS_3,
MIN_STATUS_4,
MIN_STATUS_5,
MIN_STATUS_6,
MIN_STATUS_7,
MIN_STATUS_8,
MIN_STATUS_9,
MIN_STATUS_10,
MIN_STATUS_11,
MIN_STATUS_12,
MIN_STATUS_13,
MIN_STATUS_14,
MIN_STATUS_15,
MAX_STATUS_0, /* MAX threshold violated */
MAX_STATUS_1,
MAX_STATUS_2,
MAX_STATUS_3,
MAX_STATUS_4,
MAX_STATUS_5,
MAX_STATUS_6,
MAX_STATUS_7,
MAX_STATUS_8,
MAX_STATUS_9,
MAX_STATUS_10,
MAX_STATUS_11,
MAX_STATUS_12,
MAX_STATUS_13,
MAX_STATUS_14,
MAX_STATUS_15,
LOWER_STATUS_0, /* LOWER threshold violated */
LOWER_STATUS_1,
LOWER_STATUS_2,
LOWER_STATUS_3,
LOWER_STATUS_4,
LOWER_STATUS_5,
LOWER_STATUS_6,
LOWER_STATUS_7,
LOWER_STATUS_8,
LOWER_STATUS_9,
LOWER_STATUS_10,
LOWER_STATUS_11,
LOWER_STATUS_12,
LOWER_STATUS_13,
LOWER_STATUS_14,
LOWER_STATUS_15,
UPPER_STATUS_0, /* UPPER threshold violated */
UPPER_STATUS_1,
UPPER_STATUS_2,
UPPER_STATUS_3,
UPPER_STATUS_4,
UPPER_STATUS_5,
UPPER_STATUS_6,
UPPER_STATUS_7,
UPPER_STATUS_8,
UPPER_STATUS_9,
UPPER_STATUS_10,
UPPER_STATUS_11,
UPPER_STATUS_12,
UPPER_STATUS_13,
UPPER_STATUS_14,
UPPER_STATUS_15,
CRITICAL_STATUS_0, /* CRITICAL threshold violated */
CRITICAL_STATUS_1,
CRITICAL_STATUS_2,
CRITICAL_STATUS_3,
CRITICAL_STATUS_4,
CRITICAL_STATUS_5,
CRITICAL_STATUS_6,
CRITICAL_STATUS_7,
CRITICAL_STATUS_8,
CRITICAL_STATUS_9,
CRITICAL_STATUS_10,
CRITICAL_STATUS_11,
CRITICAL_STATUS_12,
CRITICAL_STATUS_13,
CRITICAL_STATUS_14,
CRITICAL_STATUS_15,
/* TRDY */
TRDY,
/* INTERRUPT ENABLE */
INT_EN, /* Pre-V1, V1.x */
LOW_INT_EN, /* V2.x */
UP_INT_EN, /* V2.x */
CRIT_INT_EN, /* V2.x */
REG_ARRAY_SIZE,
/* Keep last */
MAX_REGFIELDS
};
/**
* struct tsens_features - Features supported by the IP
* @ver_major: Major number of IP version
* @crit_int: does the IP support critical interrupts?
* @adc: do the sensors only output adc code (instead of temperature)?
* @srot_split: does the IP neatly splits the register space into SROT and TM,
* with SROT only being available to secure boot firmware?
*/
struct tsens_features {
unsigned int ver_major;
unsigned int crit_int:1;
unsigned int adc:1;
unsigned int srot_split:1;
};
/**
......@@ -69,13 +255,15 @@ enum reg_list {
* @num_sensors: Number of sensors supported by platform
* @ops: operations the tsens instance supports
* @hw_ids: Subset of sensors ids supported by platform, if not the first n
* @reg_offsets: Register offsets for commonly used registers
* @feat: features of the IP
* @fields: bitfield locations
*/
struct tsens_plat_data {
const u32 num_sensors;
const struct tsens_ops *ops;
const u16 reg_offsets[REG_ARRAY_SIZE];
unsigned int *hw_ids;
const struct tsens_features *feat;
const struct reg_field *fields;
};
/**
......@@ -94,8 +282,10 @@ struct tsens_context {
* @srot_map: pointer to SROT register address space
* @tm_offset: deal with old device trees that don't address TM and SROT
* address space separately
* @reg_offsets: array of offsets to important regs for this version of IP
* @rf: array of regmap_fields used to store value of the field
* @ctx: registers to be saved and restored during suspend/resume
* @feat: features of the IP
* @fields: bitfield locations
* @ops: pointer to list of callbacks supported by this device
* @sensor: list of sensors attached to this device
*/
......@@ -105,8 +295,10 @@ struct tsens_priv {
struct regmap *tm_map;
struct regmap *srot_map;
u32 tm_offset;
u16 reg_offsets[REG_ARRAY_SIZE];
struct regmap_field *rf[MAX_REGFIELDS];
struct tsens_context ctx;
const struct tsens_features *feat;
const struct reg_field *fields;
const struct tsens_ops *ops;
struct tsens_sensor sensor[0];
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment