Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
# SPDX-License-Identifier: GPL-2.0-only
config QCOM_TSENS
tristate "Qualcomm TSENS Temperature Alarm"
depends on NVMEM_QCOM_QFPROM
depends on ARCH_QCOM || COMPILE_TEST
help
This enables the thermal sysfs driver for the TSENS device. It shows
up in Sysfs as a thermal zone with multiple trip points. Disabling the
thermal zone device via the mode file results in disabling the sensor.
Also able to set threshold temperature for both hot and cold and update
when a threshold is reached.
config QCOM_SPMI_ADC_TM5
tristate "Qualcomm SPMI PMIC Thermal Monitor ADC5"
depends on OF && SPMI && IIO
select REGMAP_SPMI
select QCOM_VADC_COMMON
help
This enables the thermal driver for the ADC thermal monitoring
device. It shows up as a thermal zone with multiple trip points.
Thermal client sets threshold temperature for both warm and cool and
gets updated when a threshold is reached.
config QCOM_SPMI_TEMP_ALARM
tristate "Qualcomm SPMI PMIC Temperature Alarm"
depends on OF && SPMI && IIO
select REGMAP_SPMI
help
This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP)
PMIC devices. It shows up in sysfs as a thermal sensor with multiple
trip points. The temperature reported by the thermal sensor reflects the
real time die temperature if an ADC is present or an estimate of the
temperature based upon the over temperature stage value.
config QCOM_LMH
tristate "Qualcomm Limits Management Hardware"
depends on ARCH_QCOM || COMPILE_TEST
select QCOM_SCM
help
This enables initialization of Qualcomm limits management
hardware(LMh). LMh allows for hardware-enforced mitigation for cpus based on
input from temperature and current sensors. On many newer Qualcomm SoCs
LMh is configured in the firmware and this feature need not be enabled.
However, on certain SoCs like sdm845 LMh has to be configured from kernel.
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \
tsens-8960.o
obj-$(CONFIG_QCOM_SPMI_ADC_TM5) += qcom-spmi-adc-tm5.o
obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
obj-$(CONFIG_QCOM_LMH) += lmh.o
+253
View File
@@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021, Linaro Limited. All rights reserved.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/firmware/qcom/qcom_scm.h>
#define LMH_NODE_DCVS 0x44435653
#define LMH_CLUSTER0_NODE_ID 0x6370302D
#define LMH_CLUSTER1_NODE_ID 0x6370312D
#define LMH_SUB_FN_THERMAL 0x54484D4C
#define LMH_SUB_FN_CRNT 0x43524E54
#define LMH_SUB_FN_REL 0x52454C00
#define LMH_SUB_FN_BCL 0x42434C00
#define LMH_ALGO_MODE_ENABLE 0x454E424C
#define LMH_TH_HI_THRESHOLD 0x48494748
#define LMH_TH_LOW_THRESHOLD 0x4C4F5700
#define LMH_TH_ARM_THRESHOLD 0x41524D00
#define LMH_REG_DCVS_INTR_CLR 0x8
#define LMH_ENABLE_ALGOS 1
struct lmh_hw_data {
void __iomem *base;
struct irq_domain *domain;
int irq;
};
static irqreturn_t lmh_handle_irq(int hw_irq, void *data)
{
struct lmh_hw_data *lmh_data = data;
int irq = irq_find_mapping(lmh_data->domain, 0);
/* Call the cpufreq driver to handle the interrupt */
if (irq)
generic_handle_irq(irq);
return IRQ_HANDLED;
}
static void lmh_enable_interrupt(struct irq_data *d)
{
struct lmh_hw_data *lmh_data = irq_data_get_irq_chip_data(d);
/* Clear the existing interrupt */
writel(0xff, lmh_data->base + LMH_REG_DCVS_INTR_CLR);
enable_irq(lmh_data->irq);
}
static void lmh_disable_interrupt(struct irq_data *d)
{
struct lmh_hw_data *lmh_data = irq_data_get_irq_chip_data(d);
disable_irq_nosync(lmh_data->irq);
}
static struct irq_chip lmh_irq_chip = {
.name = "lmh",
.irq_enable = lmh_enable_interrupt,
.irq_disable = lmh_disable_interrupt
};
static int lmh_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
struct lmh_hw_data *lmh_data = d->host_data;
static struct lock_class_key lmh_lock_key;
static struct lock_class_key lmh_request_key;
/*
* This lock class tells lockdep that GPIO irqs are in a different
* category than their parents, so it won't report false recursion.
*/
irq_set_lockdep_class(irq, &lmh_lock_key, &lmh_request_key);
irq_set_chip_and_handler(irq, &lmh_irq_chip, handle_simple_irq);
irq_set_chip_data(irq, lmh_data);
return 0;
}
static const struct irq_domain_ops lmh_irq_ops = {
.map = lmh_irq_map,
.xlate = irq_domain_xlate_onecell,
};
static int lmh_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *cpu_node;
struct lmh_hw_data *lmh_data;
int temp_low, temp_high, temp_arm, cpu_id, ret;
unsigned int enable_alg;
u32 node_id;
if (!qcom_scm_is_available())
return -EPROBE_DEFER;
lmh_data = devm_kzalloc(dev, sizeof(*lmh_data), GFP_KERNEL);
if (!lmh_data)
return -ENOMEM;
lmh_data->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(lmh_data->base))
return PTR_ERR(lmh_data->base);
cpu_node = of_parse_phandle(np, "cpus", 0);
if (!cpu_node)
return -EINVAL;
cpu_id = of_cpu_node_to_id(cpu_node);
of_node_put(cpu_node);
ret = of_property_read_u32(np, "qcom,lmh-temp-high-millicelsius", &temp_high);
if (ret) {
dev_err(dev, "missing qcom,lmh-temp-high-millicelsius property\n");
return ret;
}
ret = of_property_read_u32(np, "qcom,lmh-temp-low-millicelsius", &temp_low);
if (ret) {
dev_err(dev, "missing qcom,lmh-temp-low-millicelsius property\n");
return ret;
}
ret = of_property_read_u32(np, "qcom,lmh-temp-arm-millicelsius", &temp_arm);
if (ret) {
dev_err(dev, "missing qcom,lmh-temp-arm-millicelsius property\n");
return ret;
}
/*
* Only sdm845 has lmh hardware currently enabled from hlos. If this is needed
* for other platforms, revisit this to check if the <cpu-id, node-id> should be part
* of a dt match table.
*/
if (cpu_id == 0) {
node_id = LMH_CLUSTER0_NODE_ID;
} else if (cpu_id == 4) {
node_id = LMH_CLUSTER1_NODE_ID;
} else {
dev_err(dev, "Wrong CPU id associated with LMh node\n");
return -EINVAL;
}
if (!qcom_scm_lmh_dcvsh_available())
return -EINVAL;
enable_alg = (uintptr_t)of_device_get_match_data(dev);
if (enable_alg) {
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_CRNT, LMH_ALGO_MODE_ENABLE, 1,
LMH_NODE_DCVS, node_id, 0);
if (ret)
dev_err(dev, "Error %d enabling current subfunction\n", ret);
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_REL, LMH_ALGO_MODE_ENABLE, 1,
LMH_NODE_DCVS, node_id, 0);
if (ret)
dev_err(dev, "Error %d enabling reliability subfunction\n", ret);
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_BCL, LMH_ALGO_MODE_ENABLE, 1,
LMH_NODE_DCVS, node_id, 0);
if (ret)
dev_err(dev, "Error %d enabling BCL subfunction\n", ret);
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_ALGO_MODE_ENABLE, 1,
LMH_NODE_DCVS, node_id, 0);
if (ret) {
dev_err(dev, "Error %d enabling thermal subfunction\n", ret);
return ret;
}
ret = qcom_scm_lmh_profile_change(0x1);
if (ret) {
dev_err(dev, "Error %d changing profile\n", ret);
return ret;
}
}
/* Set default thermal trips */
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_TH_ARM_THRESHOLD, temp_arm,
LMH_NODE_DCVS, node_id, 0);
if (ret) {
dev_err(dev, "Error setting thermal ARM threshold%d\n", ret);
return ret;
}
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_TH_HI_THRESHOLD, temp_high,
LMH_NODE_DCVS, node_id, 0);
if (ret) {
dev_err(dev, "Error setting thermal HI threshold%d\n", ret);
return ret;
}
ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_TH_LOW_THRESHOLD, temp_low,
LMH_NODE_DCVS, node_id, 0);
if (ret) {
dev_err(dev, "Error setting thermal LOW threshold%d\n", ret);
return ret;
}
lmh_data->irq = platform_get_irq(pdev, 0);
lmh_data->domain = irq_domain_create_linear(dev_fwnode(dev), 1, &lmh_irq_ops, lmh_data);
if (!lmh_data->domain) {
dev_err(dev, "Error adding irq_domain\n");
return -EINVAL;
}
/* Disable the irq and let cpufreq enable it when ready to handle the interrupt */
irq_set_status_flags(lmh_data->irq, IRQ_NOAUTOEN);
ret = devm_request_irq(dev, lmh_data->irq, lmh_handle_irq,
IRQF_NO_THREAD | IRQF_NO_SUSPEND,
"lmh-irq", lmh_data);
if (ret) {
dev_err(dev, "Error %d registering irq %x\n", ret, lmh_data->irq);
irq_domain_remove(lmh_data->domain);
return ret;
}
return 0;
}
static const struct of_device_id lmh_table[] = {
{ .compatible = "qcom,sc8180x-lmh", },
{ .compatible = "qcom,sdm845-lmh", .data = (void *)LMH_ENABLE_ALGOS},
{ .compatible = "qcom,sm8150-lmh", },
{}
};
MODULE_DEVICE_TABLE(of, lmh_table);
static struct platform_driver lmh_driver = {
.probe = lmh_probe,
.driver = {
.name = "qcom-lmh",
.of_match_table = lmh_table,
.suppress_bind_attrs = true,
},
};
module_platform_driver(lmh_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("QCOM LMh driver");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,906 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2011-2015, 2017, 2020, The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/iio/consumer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include "../thermal_hwmon.h"
#define QPNP_TM_REG_DIG_MINOR 0x00
#define QPNP_TM_REG_DIG_MAJOR 0x01
#define QPNP_TM_REG_TYPE 0x04
#define QPNP_TM_REG_SUBTYPE 0x05
#define QPNP_TM_REG_STATUS 0x08
#define QPNP_TM_REG_IRQ_STATUS 0x10
#define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40
#define QPNP_TM_REG_ALARM_CTRL 0x46
/* TEMP_DAC_STGx registers are only present for TEMP_GEN2 v2.0 */
#define QPNP_TM_REG_TEMP_DAC_STG1 0x47
#define QPNP_TM_REG_TEMP_DAC_STG2 0x48
#define QPNP_TM_REG_TEMP_DAC_STG3 0x49
#define QPNP_TM_REG_LITE_TEMP_CFG1 0x50
#define QPNP_TM_REG_LITE_TEMP_CFG2 0x51
#define QPNP_TM_TYPE 0x09
#define QPNP_TM_SUBTYPE_GEN1 0x08
#define QPNP_TM_SUBTYPE_GEN2 0x09
#define QPNP_TM_SUBTYPE_LITE 0xC0
#define STATUS_GEN1_STAGE_MASK GENMASK(1, 0)
#define STATUS_GEN2_STATE_MASK GENMASK(6, 4)
/* IRQ status only needed for TEMP_ALARM_LITE */
#define IRQ_STATUS_MASK BIT(0)
#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 BIT(6)
#define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
#define SHUTDOWN_CTRL1_RATE_25HZ BIT(3)
#define ALARM_CTRL_FORCE_ENABLE BIT(7)
#define LITE_TEMP_CFG_THRESHOLD_MASK GENMASK(3, 2)
#define THRESH_COUNT 4
#define STAGE_COUNT 3
enum overtemp_stage {
STAGE1 = 0,
STAGE2,
STAGE3,
};
/* Over-temperature trip point values in mC */
static const long temp_map_gen1[THRESH_COUNT][STAGE_COUNT] = {
{ 105000, 125000, 145000 },
{ 110000, 130000, 150000 },
{ 115000, 135000, 155000 },
{ 120000, 140000, 160000 },
};
static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = {
{ 90000, 110000, 140000 },
{ 95000, 115000, 145000 },
{ 100000, 120000, 150000 },
{ 105000, 125000, 155000 },
};
#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
#define THRESH_MIN 0
#define THRESH_MAX 3
#define TEMP_STAGE_HYSTERESIS 2000
/*
* For TEMP_GEN2 v2.0, TEMP_DAC_STG1/2/3 registers are used to set the threshold
* for each stage independently.
* TEMP_DAC_STG* = 0 --> 80 C
* Each 8 step increase in TEMP_DAC_STG* value corresponds to 5 C (5000 mC).
*/
#define TEMP_DAC_MIN 80000
#define TEMP_DAC_SCALE_NUM 8
#define TEMP_DAC_SCALE_DEN 5000
#define TEMP_DAC_TEMP_TO_REG(temp) \
(((temp) - TEMP_DAC_MIN) * TEMP_DAC_SCALE_NUM / TEMP_DAC_SCALE_DEN)
#define TEMP_DAC_REG_TO_TEMP(reg) \
(TEMP_DAC_MIN + (reg) * TEMP_DAC_SCALE_DEN / TEMP_DAC_SCALE_NUM)
static const long temp_dac_max[STAGE_COUNT] = {
119375, 159375, 159375
};
/*
* TEMP_ALARM_LITE has two stages: warning and shutdown with independently
* configured threshold temperatures.
*/
static const long temp_lite_warning_map[THRESH_COUNT] = {
115000, 125000, 135000, 145000
};
static const long temp_lite_shutdown_map[THRESH_COUNT] = {
135000, 145000, 160000, 175000
};
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
#define DEFAULT_TEMP 37000
struct qpnp_tm_chip;
struct spmi_temp_alarm_data {
const struct thermal_zone_device_ops *ops;
const long (*temp_map)[THRESH_COUNT][STAGE_COUNT];
int (*sync_thresholds)(struct qpnp_tm_chip *chip);
int (*get_temp_stage)(struct qpnp_tm_chip *chip);
int (*configure_trip_temps)(struct qpnp_tm_chip *chip);
};
struct qpnp_tm_chip {
struct regmap *map;
struct device *dev;
struct thermal_zone_device *tz_dev;
const struct spmi_temp_alarm_data *data;
unsigned int subtype;
long temp;
unsigned int stage;
unsigned int base;
unsigned int ntrips;
/* protects .thresh, .stage and chip registers */
struct mutex lock;
bool initialized;
bool require_stage2_shutdown;
long temp_thresh_map[STAGE_COUNT];
struct iio_channel *adc;
};
/* This array maps from GEN2 alarm state to GEN1 alarm stage */
static const unsigned int alarm_state_map[8] = {0, 1, 1, 2, 2, 3, 3, 3};
static int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *data)
{
unsigned int val;
int ret;
ret = regmap_read(chip->map, chip->base + addr, &val);
if (ret < 0)
return ret;
*data = val;
return 0;
}
static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
{
return regmap_write(chip->map, chip->base + addr, data);
}
/**
* qpnp_tm_decode_temp() - return temperature in mC corresponding to the
* specified over-temperature stage
* @chip: Pointer to the qpnp_tm chip
* @stage: Over-temperature stage
*
* Return: temperature in mC
*/
static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
{
if (stage == 0 || stage > STAGE_COUNT)
return 0;
return chip->temp_thresh_map[stage - 1];
}
/**
* qpnp_tm_gen1_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
* Return: stage on success, or errno on failure.
*/
static int qpnp_tm_gen1_get_temp_stage(struct qpnp_tm_chip *chip)
{
int ret;
u8 reg;
ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
if (ret < 0)
return ret;
return FIELD_GET(STATUS_GEN1_STAGE_MASK, reg);
}
/**
* qpnp_tm_gen2_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
* Return: stage on success, or errno on failure.
*/
static int qpnp_tm_gen2_get_temp_stage(struct qpnp_tm_chip *chip)
{
int ret;
u8 reg;
ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
if (ret < 0)
return ret;
ret = FIELD_GET(STATUS_GEN2_STATE_MASK, reg);
return alarm_state_map[ret];
}
/**
* qpnp_tm_lite_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
* Return: alarm interrupt state on success, or errno on failure.
*/
static int qpnp_tm_lite_get_temp_stage(struct qpnp_tm_chip *chip)
{
u8 reg = 0;
int ret;
ret = qpnp_tm_read(chip, QPNP_TM_REG_IRQ_STATUS, &reg);
if (ret < 0)
return ret;
return FIELD_GET(IRQ_STATUS_MASK, reg);
}
/*
* This function updates the internal temp value based on the
* current thermal stage and threshold as well as the previous stage
*/
static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
{
unsigned int stage_new, stage_old;
int ret;
WARN_ON(!mutex_is_locked(&chip->lock));
ret = chip->data->get_temp_stage(chip);
if (ret < 0)
return ret;
stage_new = ret;
stage_old = chip->stage;
if (stage_new > stage_old) {
/* increasing stage, use lower bound */
chip->temp = qpnp_tm_decode_temp(chip, stage_new)
+ TEMP_STAGE_HYSTERESIS;
} else if (stage_new < stage_old) {
/* decreasing stage, use upper bound */
chip->temp = qpnp_tm_decode_temp(chip, stage_new + 1)
- TEMP_STAGE_HYSTERESIS;
}
chip->stage = stage_new;
return 0;
}
static int qpnp_tm_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
int ret, mili_celsius;
if (!temp)
return -EINVAL;
if (!chip->initialized) {
*temp = DEFAULT_TEMP;
return 0;
}
if (!chip->adc) {
mutex_lock(&chip->lock);
ret = qpnp_tm_update_temp_no_adc(chip);
mutex_unlock(&chip->lock);
if (ret < 0)
return ret;
} else {
ret = iio_read_channel_processed(chip->adc, &mili_celsius);
if (ret < 0)
return ret;
chip->temp = mili_celsius;
}
*temp = chip->temp;
return 0;
}
static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
int temp)
{
long stage2_threshold_min = (*chip->data->temp_map)[THRESH_MIN][STAGE2];
long stage2_threshold_max = (*chip->data->temp_map)[THRESH_MAX][STAGE2];
bool disable_stage2_shutdown = false;
u8 reg, threshold;
WARN_ON(!mutex_is_locked(&chip->lock));
/*
* Default: Stage 2 and Stage 3 shutdown enabled, thresholds at
* lowest threshold set, monitoring at 25Hz
*/
reg = SHUTDOWN_CTRL1_RATE_25HZ;
if (temp == THERMAL_TEMP_INVALID ||
temp < stage2_threshold_min) {
threshold = THRESH_MIN;
goto skip;
}
if (temp <= stage2_threshold_max) {
threshold = THRESH_MAX -
((stage2_threshold_max - temp) /
TEMP_THRESH_STEP);
disable_stage2_shutdown = true;
} else {
threshold = THRESH_MAX;
if (chip->adc)
disable_stage2_shutdown = true;
else
dev_warn(chip->dev,
"No ADC is configured and critical temperature %d mC is above the maximum stage 2 threshold of %ld mC! Configuring stage 2 shutdown at %ld mC.\n",
temp, stage2_threshold_max, stage2_threshold_max);
}
skip:
memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold],
sizeof(chip->temp_thresh_map));
reg |= threshold;
if (disable_stage2_shutdown && !chip->require_stage2_shutdown)
reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2;
return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
}
static int qpnp_tm_set_trip_temp(struct thermal_zone_device *tz,
const struct thermal_trip *trip, int temp)
{
struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
int ret;
if (trip->type != THERMAL_TRIP_CRITICAL)
return 0;
mutex_lock(&chip->lock);
ret = qpnp_tm_update_critical_trip_temp(chip, temp);
mutex_unlock(&chip->lock);
return ret;
}
static const struct thermal_zone_device_ops qpnp_tm_sensor_ops = {
.get_temp = qpnp_tm_get_temp,
.set_trip_temp = qpnp_tm_set_trip_temp,
};
static int qpnp_tm_gen2_rev2_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp)
{
int ret, temp_cfg;
u8 reg;
WARN_ON(!mutex_is_locked(&chip->lock));
if (trip >= STAGE_COUNT) {
dev_err(chip->dev, "invalid TEMP_DAC trip = %d\n", trip);
return -EINVAL;
} else if (temp < TEMP_DAC_MIN || temp > temp_dac_max[trip]) {
dev_err(chip->dev, "invalid TEMP_DAC temp = %d\n", temp);
return -EINVAL;
}
reg = TEMP_DAC_TEMP_TO_REG(temp);
temp_cfg = TEMP_DAC_REG_TO_TEMP(reg);
ret = qpnp_tm_write(chip, QPNP_TM_REG_TEMP_DAC_STG1 + trip, reg);
if (ret < 0) {
dev_err(chip->dev, "TEMP_DAC_STG write failed, ret=%d\n", ret);
return ret;
}
chip->temp_thresh_map[trip] = temp_cfg;
return 0;
}
static int qpnp_tm_gen2_rev2_set_trip_temp(struct thermal_zone_device *tz,
const struct thermal_trip *trip, int temp)
{
unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);
struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
int ret;
mutex_lock(&chip->lock);
ret = qpnp_tm_gen2_rev2_set_temp_thresh(chip, trip_index, temp);
mutex_unlock(&chip->lock);
return ret;
}
static const struct thermal_zone_device_ops qpnp_tm_gen2_rev2_sensor_ops = {
.get_temp = qpnp_tm_get_temp,
.set_trip_temp = qpnp_tm_gen2_rev2_set_trip_temp,
};
static int qpnp_tm_lite_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp)
{
int ret, temp_cfg, i;
const long *temp_map;
u8 reg, thresh;
u16 addr;
WARN_ON(!mutex_is_locked(&chip->lock));
if (trip >= STAGE_COUNT) {
dev_err(chip->dev, "invalid TEMP_LITE trip = %d\n", trip);
return -EINVAL;
}
switch (trip) {
case 0:
temp_map = temp_lite_warning_map;
addr = QPNP_TM_REG_LITE_TEMP_CFG1;
break;
case 1:
/*
* The second trip point is purely in software to facilitate
* a controlled shutdown after the warning threshold is crossed
* but before the automatic hardware shutdown threshold is
* crossed.
*/
return 0;
case 2:
temp_map = temp_lite_shutdown_map;
addr = QPNP_TM_REG_LITE_TEMP_CFG2;
break;
default:
return 0;
}
if (temp < temp_map[THRESH_MIN] || temp > temp_map[THRESH_MAX]) {
dev_err(chip->dev, "invalid TEMP_LITE temp = %d\n", temp);
return -EINVAL;
}
thresh = 0;
temp_cfg = temp_map[thresh];
for (i = THRESH_MAX; i >= THRESH_MIN; i--) {
if (temp >= temp_map[i]) {
thresh = i;
temp_cfg = temp_map[i];
break;
}
}
if (temp_cfg == chip->temp_thresh_map[trip])
return 0;
ret = qpnp_tm_read(chip, addr, &reg);
if (ret < 0) {
dev_err(chip->dev, "LITE_TEMP_CFG read failed, ret=%d\n", ret);
return ret;
}
reg &= ~LITE_TEMP_CFG_THRESHOLD_MASK;
reg |= FIELD_PREP(LITE_TEMP_CFG_THRESHOLD_MASK, thresh);
ret = qpnp_tm_write(chip, addr, reg);
if (ret < 0) {
dev_err(chip->dev, "LITE_TEMP_CFG write failed, ret=%d\n", ret);
return ret;
}
chip->temp_thresh_map[trip] = temp_cfg;
return 0;
}
static int qpnp_tm_lite_set_trip_temp(struct thermal_zone_device *tz,
const struct thermal_trip *trip, int temp)
{
unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);
struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz);
int ret;
mutex_lock(&chip->lock);
ret = qpnp_tm_lite_set_temp_thresh(chip, trip_index, temp);
mutex_unlock(&chip->lock);
return ret;
}
static const struct thermal_zone_device_ops qpnp_tm_lite_sensor_ops = {
.get_temp = qpnp_tm_get_temp,
.set_trip_temp = qpnp_tm_lite_set_trip_temp,
};
static irqreturn_t qpnp_tm_isr(int irq, void *data)
{
struct qpnp_tm_chip *chip = data;
thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED);
return IRQ_HANDLED;
}
/* Read the hardware default stage threshold temperatures */
static int qpnp_tm_sync_thresholds(struct qpnp_tm_chip *chip)
{
u8 reg, threshold;
int ret;
ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg);
if (ret < 0)
return ret;
threshold = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold],
sizeof(chip->temp_thresh_map));
return ret;
}
static int qpnp_tm_configure_trip_temp(struct qpnp_tm_chip *chip)
{
int crit_temp, ret;
ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp);
if (ret)
crit_temp = THERMAL_TEMP_INVALID;
mutex_lock(&chip->lock);
ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
mutex_unlock(&chip->lock);
return ret;
}
/* Configure TEMP_DAC registers based on DT thermal_zone trips */
static int qpnp_tm_gen2_rev2_configure_trip_temps_cb(struct thermal_trip *trip, void *data)
{
struct qpnp_tm_chip *chip = data;
int ret;
mutex_lock(&chip->lock);
trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips);
ret = qpnp_tm_gen2_rev2_set_temp_thresh(chip, chip->ntrips, trip->temperature);
chip->ntrips++;
mutex_unlock(&chip->lock);
return ret;
}
static int qpnp_tm_gen2_rev2_configure_trip_temps(struct qpnp_tm_chip *chip)
{
int ret, i;
ret = thermal_zone_for_each_trip(chip->tz_dev,
qpnp_tm_gen2_rev2_configure_trip_temps_cb, chip);
if (ret < 0)
return ret;
/* Verify that trips are strictly increasing. */
for (i = 1; i < STAGE_COUNT; i++) {
if (chip->temp_thresh_map[i] <= chip->temp_thresh_map[i - 1]) {
dev_err(chip->dev, "Threshold %d=%ld <= threshold %d=%ld\n",
i, chip->temp_thresh_map[i], i - 1,
chip->temp_thresh_map[i - 1]);
return -EINVAL;
}
}
return 0;
}
/* Read the hardware default TEMP_DAC stage threshold temperatures */
static int qpnp_tm_gen2_rev2_sync_thresholds(struct qpnp_tm_chip *chip)
{
int ret, i;
u8 reg = 0;
for (i = 0; i < STAGE_COUNT; i++) {
ret = qpnp_tm_read(chip, QPNP_TM_REG_TEMP_DAC_STG1 + i, &reg);
if (ret < 0)
return ret;
chip->temp_thresh_map[i] = TEMP_DAC_REG_TO_TEMP(reg);
}
return 0;
}
/* Configure TEMP_LITE registers based on DT thermal_zone trips */
static int qpnp_tm_lite_configure_trip_temps_cb(struct thermal_trip *trip, void *data)
{
struct qpnp_tm_chip *chip = data;
int ret;
mutex_lock(&chip->lock);
trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips);
ret = qpnp_tm_lite_set_temp_thresh(chip, chip->ntrips, trip->temperature);
chip->ntrips++;
mutex_unlock(&chip->lock);
return ret;
}
static int qpnp_tm_lite_configure_trip_temps(struct qpnp_tm_chip *chip)
{
int ret;
ret = thermal_zone_for_each_trip(chip->tz_dev, qpnp_tm_lite_configure_trip_temps_cb, chip);
if (ret < 0)
return ret;
/* Verify that trips are strictly increasing. */
if (chip->temp_thresh_map[2] <= chip->temp_thresh_map[0]) {
dev_err(chip->dev, "Threshold 2=%ld <= threshold 0=%ld\n",
chip->temp_thresh_map[2], chip->temp_thresh_map[0]);
return -EINVAL;
}
return 0;
}
/* Read the hardware default TEMP_LITE stage threshold temperatures */
static int qpnp_tm_lite_sync_thresholds(struct qpnp_tm_chip *chip)
{
int ret, thresh;
u8 reg = 0;
/*
* Store the warning trip temp in temp_thresh_map[0] and the shutdown trip
* temp in temp_thresh_map[2]. The second trip point is purely in software
* to facilitate a controlled shutdown after the warning threshold is
* crossed but before the automatic hardware shutdown threshold is
* crossed. Thus, there is no register to read for the second trip
* point.
*/
ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG1, &reg);
if (ret < 0)
return ret;
thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
chip->temp_thresh_map[0] = temp_lite_warning_map[thresh];
ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG2, &reg);
if (ret < 0)
return ret;
thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg);
chip->temp_thresh_map[2] = temp_lite_shutdown_map[thresh];
return 0;
}
static const struct spmi_temp_alarm_data spmi_temp_alarm_data = {
.ops = &qpnp_tm_sensor_ops,
.temp_map = &temp_map_gen1,
.sync_thresholds = qpnp_tm_sync_thresholds,
.configure_trip_temps = qpnp_tm_configure_trip_temp,
.get_temp_stage = qpnp_tm_gen1_get_temp_stage,
};
static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_data = {
.ops = &qpnp_tm_sensor_ops,
.temp_map = &temp_map_gen1,
.sync_thresholds = qpnp_tm_sync_thresholds,
.configure_trip_temps = qpnp_tm_configure_trip_temp,
.get_temp_stage = qpnp_tm_gen2_get_temp_stage,
};
static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev1_data = {
.ops = &qpnp_tm_sensor_ops,
.temp_map = &temp_map_gen2_v1,
.sync_thresholds = qpnp_tm_sync_thresholds,
.configure_trip_temps = qpnp_tm_configure_trip_temp,
.get_temp_stage = qpnp_tm_gen2_get_temp_stage,
};
static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev2_data = {
.ops = &qpnp_tm_gen2_rev2_sensor_ops,
.sync_thresholds = qpnp_tm_gen2_rev2_sync_thresholds,
.configure_trip_temps = qpnp_tm_gen2_rev2_configure_trip_temps,
.get_temp_stage = qpnp_tm_gen2_get_temp_stage,
};
static const struct spmi_temp_alarm_data spmi_temp_alarm_lite_data = {
.ops = &qpnp_tm_lite_sensor_ops,
.sync_thresholds = qpnp_tm_lite_sync_thresholds,
.configure_trip_temps = qpnp_tm_lite_configure_trip_temps,
.get_temp_stage = qpnp_tm_lite_get_temp_stage,
};
/*
* This function initializes the internal temp value based on only the
* current thermal stage and threshold.
*/
static int qpnp_tm_threshold_init(struct qpnp_tm_chip *chip)
{
int ret;
ret = chip->data->sync_thresholds(chip);
if (ret < 0)
return ret;
ret = chip->data->get_temp_stage(chip);
if (ret < 0)
return ret;
chip->stage = ret;
chip->temp = DEFAULT_TEMP;
if (chip->stage)
chip->temp = qpnp_tm_decode_temp(chip, chip->stage);
return ret;
}
/* This function initializes threshold control and disables shutdown override. */
static int qpnp_tm_init(struct qpnp_tm_chip *chip)
{
int ret;
u8 reg;
ret = chip->data->configure_trip_temps(chip);
if (ret < 0)
return ret;
/* Enable the thermal alarm PMIC module in always-on mode. */
reg = ALARM_CTRL_FORCE_ENABLE;
ret = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, reg);
chip->initialized = true;
return ret;
}
static int qpnp_tm_probe(struct platform_device *pdev)
{
struct qpnp_tm_chip *chip;
struct device_node *node;
u8 type, subtype, dig_major, dig_minor;
u32 res, dig_revision;
int ret, irq;
node = pdev->dev.of_node;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->dev = &pdev->dev;
mutex_init(&chip->lock);
chip->map = dev_get_regmap(pdev->dev.parent, NULL);
if (!chip->map)
return -ENXIO;
ret = of_property_read_u32(node, "reg", &res);
if (ret < 0)
return ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
/* ADC based measurements are optional */
chip->adc = devm_iio_channel_get(&pdev->dev, "thermal");
if (IS_ERR(chip->adc)) {
ret = PTR_ERR(chip->adc);
chip->adc = NULL;
if (ret == -EPROBE_DEFER)
return ret;
}
chip->base = res;
ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"could not read type\n");
ret = qpnp_tm_read(chip, QPNP_TM_REG_SUBTYPE, &subtype);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"could not read subtype\n");
ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MAJOR, &dig_major);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"could not read dig_major\n");
ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MINOR, &dig_minor);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"could not read dig_minor\n");
if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
&& subtype != QPNP_TM_SUBTYPE_GEN2
&& subtype != QPNP_TM_SUBTYPE_LITE)) {
dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
type, subtype);
return -ENODEV;
}
chip->subtype = subtype;
if (subtype == QPNP_TM_SUBTYPE_GEN1)
chip->data = &spmi_temp_alarm_data;
else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 0)
chip->data = &spmi_temp_alarm_gen2_data;
else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 1)
chip->data = &spmi_temp_alarm_gen2_rev1_data;
else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 2)
chip->data = &spmi_temp_alarm_gen2_rev2_data;
else if (subtype == QPNP_TM_SUBTYPE_LITE)
chip->data = &spmi_temp_alarm_lite_data;
else
return -ENODEV;
if (chip->subtype == QPNP_TM_SUBTYPE_GEN2) {
dig_revision = (dig_major << 8) | dig_minor;
/*
* Check if stage 2 automatic partial shutdown must remain
* enabled to avoid potential repeated faults upon reaching
* over-temperature stage 3.
*/
switch (dig_revision) {
case 0x0001:
case 0x0002:
case 0x0100:
case 0x0101:
chip->require_stage2_shutdown = true;
break;
}
}
ret = qpnp_tm_threshold_init(chip);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret, "threshold init failed\n");
/*
* Register the sensor before initializing the hardware to be able to
* read the trip points. get_temp() returns the default temperature
* before the hardware initialization is completed.
*/
chip->tz_dev = devm_thermal_of_zone_register(
&pdev->dev, 0, chip, chip->data->ops);
if (IS_ERR(chip->tz_dev))
return dev_err_probe(&pdev->dev, PTR_ERR(chip->tz_dev),
"failed to register sensor\n");
ret = qpnp_tm_init(chip);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret, "init failed\n");
devm_thermal_add_hwmon_sysfs(&pdev->dev, chip->tz_dev);
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qpnp_tm_isr,
IRQF_ONESHOT, node->name, chip);
if (ret < 0)
return ret;
thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED);
return 0;
}
static const struct of_device_id qpnp_tm_match_table[] = {
{ .compatible = "qcom,spmi-temp-alarm" },
{ }
};
MODULE_DEVICE_TABLE(of, qpnp_tm_match_table);
static struct platform_driver qpnp_tm_driver = {
.driver = {
.name = "spmi-temp-alarm",
.of_match_table = qpnp_tm_match_table,
},
.probe = qpnp_tm_probe,
};
module_platform_driver(qpnp_tm_driver);
MODULE_ALIAS("platform:spmi-temp-alarm");
MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include "tsens.h"
#define CONFIG_ADDR 0x3640
#define CONFIG_ADDR_8660 0x3620
/* CONFIG_ADDR bitmasks */
#define CONFIG 0x9b
#define CONFIG_MASK 0xf
#define CONFIG_8660 1
#define CONFIG_SHIFT_8660 28
#define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660)
#define CNTL_ADDR 0x3620
/* CNTL_ADDR bitmasks */
#define EN BIT(0)
#define SW_RST BIT(1)
#define MEASURE_PERIOD BIT(18)
#define SLP_CLK_ENA BIT(26)
#define SLP_CLK_ENA_8660 BIT(24)
#define SENSOR0_SHIFT 3
#define THRESHOLD_ADDR 0x3624
#define INT_STATUS_ADDR 0x363c
#define S0_STATUS_OFF 0x3628
#define S1_STATUS_OFF 0x362c
#define S2_STATUS_OFF 0x3630
#define S3_STATUS_OFF 0x3634
#define S4_STATUS_OFF 0x3638
#define S5_STATUS_OFF 0x3664 /* Sensors 5-10 found on apq8064/msm8960 */
#define S6_STATUS_OFF 0x3668
#define S7_STATUS_OFF 0x366c
#define S8_STATUS_OFF 0x3670
#define S9_STATUS_OFF 0x3674
#define S10_STATUS_OFF 0x3678
/* Original slope - 350 to compensate mC to C inaccuracy */
static u32 tsens_msm8960_slope[] = {
826, 826, 804, 826,
761, 782, 782, 849,
782, 849, 782
};
static int suspend_8960(struct tsens_priv *priv)
{
int ret;
unsigned int mask;
struct regmap *map = priv->tm_map;
ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold);
if (ret)
return ret;
ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control);
if (ret)
return ret;
if (priv->num_sensors > 1)
mask = SLP_CLK_ENA | EN;
else
mask = SLP_CLK_ENA_8660 | EN;
ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
if (ret)
return ret;
return 0;
}
static int resume_8960(struct tsens_priv *priv)
{
int ret;
struct regmap *map = priv->tm_map;
ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
if (ret)
return ret;
/*
* Separate CONFIG restore is not needed only for 8660 as
* config is part of CTRL Addr and its restored as such
*/
if (priv->num_sensors > 1) {
ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
if (ret)
return ret;
}
ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold);
if (ret)
return ret;
ret = regmap_write(map, CNTL_ADDR, priv->ctx.control);
if (ret)
return ret;
return 0;
}
static int enable_8960(struct tsens_priv *priv, int id)
{
int ret;
u32 reg, mask = BIT(id);
ret = regmap_read(priv->tm_map, CNTL_ADDR, &reg);
if (ret)
return ret;
/* HARDWARE BUG:
* On platforms with more than 6 sensors, all remaining sensors
* must be enabled together, otherwise undefined results are expected.
* (Sensor 6-7 disabled, Sensor 3 disabled...) In the original driver,
* all the sensors are enabled in one step hence this bug is not
* triggered.
*/
if (id > 5)
mask = GENMASK(10, 6);
mask <<= SENSOR0_SHIFT;
/* Sensors already enabled. Skip. */
if ((reg & mask) == mask)
return 0;
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST);
if (ret)
return ret;
reg |= MEASURE_PERIOD;
if (priv->num_sensors > 1)
reg |= mask | SLP_CLK_ENA | EN;
else
reg |= mask | SLP_CLK_ENA_8660 | EN;
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg);
if (ret)
return ret;
return 0;
}
static void disable_8960(struct tsens_priv *priv)
{
int ret;
u32 reg_cntl;
u32 mask;
mask = GENMASK(priv->num_sensors - 1, 0);
mask <<= SENSOR0_SHIFT;
mask |= EN;
ret = regmap_read(priv->tm_map, CNTL_ADDR, &reg_cntl);
if (ret)
return;
reg_cntl &= ~mask;
if (priv->num_sensors > 1)
reg_cntl &= ~SLP_CLK_ENA;
else
reg_cntl &= ~SLP_CLK_ENA_8660;
regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
}
static int calibrate_8960(struct tsens_priv *priv)
{
int i;
char *data;
u32 p1[11];
data = qfprom_read(priv->dev, "calib");
if (IS_ERR(data))
data = qfprom_read(priv->dev, "calib_backup");
if (IS_ERR(data))
return PTR_ERR(data);
for (i = 0; i < priv->num_sensors; i++) {
p1[i] = data[i];
priv->sensor[i].slope = tsens_msm8960_slope[i];
}
compute_intercept_slope(priv, p1, NULL, ONE_PT_CALIB);
kfree(data);
return 0;
}
static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* No VERSION information */
/* CNTL */
[TSENS_EN] = REG_FIELD(CNTL_ADDR, 0, 0),
[TSENS_SW_RST] = REG_FIELD(CNTL_ADDR, 1, 1),
/* 8960 has 5 sensors, 8660 has 11, we only handle 5 */
[SENSOR_EN] = REG_FIELD(CNTL_ADDR, 3, 7),
/* ----- TM ------ */
/* INTERRUPT ENABLE */
/* NO INTERRUPT ENABLE */
/* Single UPPER/LOWER TEMPERATURE THRESHOLD for all sensors */
[LOW_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 0, 7),
[UP_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 8, 15),
/* MIN_THRESH_0 and MAX_THRESH_0 are not present in the regfield
* Recycle CRIT_THRESH_0 and 1 to set the required regs to hardcoded temp
* MIN_THRESH_0 -> CRIT_THRESH_1
* MAX_THRESH_0 -> CRIT_THRESH_0
*/
[CRIT_THRESH_1] = REG_FIELD(THRESHOLD_ADDR, 16, 23),
[CRIT_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 24, 31),
/* UPPER/LOWER INTERRUPT [CLEAR/STATUS] */
/* 1 == clear, 0 == normal operation */
[LOW_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 9, 9),
[UP_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 10, 10),
/* NO CRITICAL INTERRUPT SUPPORT on 8960 */
/* Sn_STATUS */
[LAST_TEMP_0] = REG_FIELD(S0_STATUS_OFF, 0, 7),
[LAST_TEMP_1] = REG_FIELD(S1_STATUS_OFF, 0, 7),
[LAST_TEMP_2] = REG_FIELD(S2_STATUS_OFF, 0, 7),
[LAST_TEMP_3] = REG_FIELD(S3_STATUS_OFF, 0, 7),
[LAST_TEMP_4] = REG_FIELD(S4_STATUS_OFF, 0, 7),
[LAST_TEMP_5] = REG_FIELD(S5_STATUS_OFF, 0, 7),
[LAST_TEMP_6] = REG_FIELD(S6_STATUS_OFF, 0, 7),
[LAST_TEMP_7] = REG_FIELD(S7_STATUS_OFF, 0, 7),
[LAST_TEMP_8] = REG_FIELD(S8_STATUS_OFF, 0, 7),
[LAST_TEMP_9] = REG_FIELD(S9_STATUS_OFF, 0, 7),
[LAST_TEMP_10] = REG_FIELD(S10_STATUS_OFF, 0, 7),
/* No VALID field on 8960 */
/* TSENS_INT_STATUS bits: 1 == threshold violated */
[MIN_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 0, 0),
[LOWER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 1, 1),
[UPPER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 2, 2),
/* No CRITICAL field on 8960 */
[MAX_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 3, 3),
/* TRDY: 1=ready, 0=in progress */
[TRDY] = REG_FIELD(INT_STATUS_ADDR, 7, 7),
};
static const struct tsens_ops ops_8960 = {
.init = init_common,
.calibrate = calibrate_8960,
.get_temp = get_temp_common,
.enable = enable_8960,
.disable = disable_8960,
.suspend = suspend_8960,
.resume = resume_8960,
};
static struct tsens_features tsens_8960_feat = {
.ver_major = VER_0,
.crit_int = 0,
.combo_int = 0,
.adc = 1,
.srot_split = 0,
.max_sensors = 11,
.trip_min_temp = -40000,
.trip_max_temp = 120000,
};
struct tsens_plat_data data_8960 = {
.num_sensors = 11,
.ops = &ops_8960,
.feat = &tsens_8960_feat,
.fields = tsens_8960_regfields,
};
@@ -0,0 +1,408 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/nvmem-consumer.h>
#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
/* extra data for 8974 */
#define BKP_SEL 0x3
#define BKP_REDUN_SEL 0xe0000000
#define BIT_APPEND 0x3
static struct tsens_legacy_calibration_format tsens_8916_nvmem = {
.base_len = 7,
.base_shift = 3,
.sp_len = 5,
.mode = { 0, 29, 1 },
.invalid = { 0, 31, 1 },
.base = { { 0, 0 }, { 1, 25 } },
.sp = {
{ { 0, 7 }, { 0, 12 } },
{ { 0, 17 }, { 0, 22 } },
{ { 0, 27 }, { 1, 0 } },
{ { 1, 5 }, { 1, 10 } },
{ { 1, 15 }, { 1, 20 } },
},
};
static struct tsens_legacy_calibration_format tsens_8974_nvmem = {
.base_len = 8,
.base_shift = 2,
.sp_len = 6,
.mode = { 1, 30 },
.invalid = { 3, 30 },
.base = { { 0, 0 }, { 2, 12 } },
.sp = {
{ { 0, 8 }, { 2, 20 } },
{ { 0, 14 }, { 2, 26 } },
{ { 0, 20 }, { 3, 0 } },
{ { 0, 26 }, { 3, 6 } },
{ { 1, 0 }, { 3, 12 } },
{ { 1, 6 }, { 3, 18 } },
{ { 1, 12 }, { 3, 24 } },
{ { 1, 18 }, { 4, 0 } },
{ { 1, 24 }, { 4, 6 } },
{ { 2, 0 }, { 4, 12 } },
{ { 2, 6 }, { 4, 18 } },
},
};
static struct tsens_legacy_calibration_format tsens_8974_backup_nvmem = {
.base_len = 8,
.base_shift = 2,
.sp_len = 6,
.mode = { 4, 30, 1 },
.invalid = { 5, 30, 1 },
.base = { { 0, 0 }, { 2, 18 } },
.sp = {
{ { 0, 8 }, { 2, 26 } },
{ { 0, 14 }, { 3, 0 } },
{ { 0, 20 }, { 3, 6 } },
{ { 0, 26 }, { 3, 12 } },
{ { 1, 0 }, { 3, 18 } },
{ { 1, 6 }, { 3, 24, 1 } },
{ { 1, 12 }, { 4, 0, 1 } },
{ { 1, 18 }, { 4, 6, 1 } },
{ { 2, 0 }, { 4, 12, 1 } },
{ { 2, 6 }, { 4, 18, 1 } },
{ { 2, 12 }, { 4, 24, 1 } },
},
};
static int calibrate_8916(struct tsens_priv *priv)
{
u32 p1[5], p2[5];
u32 *qfprom_cdata, *qfprom_csel;
int mode, ret;
ret = tsens_calibrate_nvmem(priv, 3);
if (!ret)
return 0;
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
if (IS_ERR(qfprom_cdata))
return PTR_ERR(qfprom_cdata);
qfprom_csel = (u32 *)qfprom_read(priv->dev, "calib_sel");
if (IS_ERR(qfprom_csel)) {
kfree(qfprom_cdata);
return PTR_ERR(qfprom_csel);
}
mode = tsens_read_calibration_legacy(priv, &tsens_8916_nvmem,
p1, p2,
qfprom_cdata, qfprom_csel);
compute_intercept_slope(priv, p1, p2, mode);
kfree(qfprom_cdata);
kfree(qfprom_csel);
return 0;
}
static void fixup_8974_points(int mode, u32 *p1, u32 *p2)
{
int i;
if (mode == NO_PT_CALIB) {
p1[0] += 2;
p1[1] += 9;
p1[2] += 3;
p1[3] += 9;
p1[4] += 5;
p1[5] += 9;
p1[6] += 7;
p1[7] += 10;
p1[8] += 8;
p1[9] += 9;
p1[10] += 8;
} else {
for (i = 0; i < 11; i++) {
/*
* ONE_PT_CALIB requires using addition here instead of
* using OR operation.
*/
p1[i] += BIT_APPEND;
p2[i] += BIT_APPEND;
}
}
}
static int calibrate_8974_nvmem(struct tsens_priv *priv)
{
u32 p1[11], p2[11];
u32 backup;
int ret, mode;
ret = nvmem_cell_read_variable_le_u32(priv->dev, "use_backup", &backup);
if (ret == -ENOENT)
dev_warn(priv->dev, "Please migrate to separate nvmem cells for calibration data\n");
if (ret < 0)
return ret;
mode = tsens_read_calibration(priv, 2, p1, p2, backup == BKP_SEL);
if (mode < 0)
return mode;
fixup_8974_points(mode, p1, p2);
compute_intercept_slope(priv, p1, p2, mode);
return 0;
}
static int calibrate_8974(struct tsens_priv *priv)
{
u32 p1[11], p2[11];
u32 *calib, *bkp;
u32 calib_redun_sel;
int mode, ret;
ret = calibrate_8974_nvmem(priv);
if (ret == 0)
return 0;
calib = (u32 *)qfprom_read(priv->dev, "calib");
if (IS_ERR(calib))
return PTR_ERR(calib);
bkp = (u32 *)qfprom_read(priv->dev, "calib_backup");
if (IS_ERR(bkp)) {
kfree(calib);
return PTR_ERR(bkp);
}
calib_redun_sel = FIELD_GET(BKP_REDUN_SEL, bkp[1]);
if (calib_redun_sel == BKP_SEL)
mode = tsens_read_calibration_legacy(priv, &tsens_8974_backup_nvmem,
p1, p2,
bkp, calib);
else
mode = tsens_read_calibration_legacy(priv, &tsens_8974_nvmem,
p1, p2,
calib, NULL);
fixup_8974_points(mode, p1, p2);
compute_intercept_slope(priv, p1, p2, mode);
kfree(calib);
kfree(bkp);
return 0;
}
static int __init init_8226(struct tsens_priv *priv)
{
priv->sensor[0].slope = 2901;
priv->sensor[1].slope = 2846;
priv->sensor[2].slope = 3038;
priv->sensor[3].slope = 2955;
priv->sensor[4].slope = 2901;
priv->sensor[5].slope = 2846;
return init_common(priv);
}
static int __init init_8909(struct tsens_priv *priv)
{
int i;
for (i = 0; i < priv->num_sensors; ++i)
priv->sensor[i].slope = 3000;
priv->sensor[0].p1_calib_offset = 0;
priv->sensor[0].p2_calib_offset = 0;
priv->sensor[1].p1_calib_offset = -10;
priv->sensor[1].p2_calib_offset = -6;
priv->sensor[2].p1_calib_offset = 0;
priv->sensor[2].p2_calib_offset = 0;
priv->sensor[3].p1_calib_offset = -9;
priv->sensor[3].p2_calib_offset = -9;
priv->sensor[4].p1_calib_offset = -8;
priv->sensor[4].p2_calib_offset = -10;
return init_common(priv);
}
static int __init init_8939(struct tsens_priv *priv) {
priv->sensor[0].slope = 2911;
priv->sensor[1].slope = 2789;
priv->sensor[2].slope = 2906;
priv->sensor[3].slope = 2763;
priv->sensor[4].slope = 2922;
priv->sensor[5].slope = 2867;
priv->sensor[6].slope = 2833;
priv->sensor[7].slope = 2838;
priv->sensor[8].slope = 2840;
/* priv->sensor[9].slope = 2852; */
return init_common(priv);
}
static int __init init_9607(struct tsens_priv *priv)
{
int i;
for (i = 0; i < priv->num_sensors; ++i)
priv->sensor[i].slope = 3000;
priv->sensor[0].p1_calib_offset = 1;
priv->sensor[0].p2_calib_offset = 1;
priv->sensor[1].p1_calib_offset = -4;
priv->sensor[1].p2_calib_offset = -2;
priv->sensor[2].p1_calib_offset = 4;
priv->sensor[2].p2_calib_offset = 8;
priv->sensor[3].p1_calib_offset = -3;
priv->sensor[3].p2_calib_offset = -5;
priv->sensor[4].p1_calib_offset = -4;
priv->sensor[4].p2_calib_offset = -4;
return init_common(priv);
}
/* v0.1: 8226, 8909, 8916, 8939, 8974, 9607 */
static struct tsens_features tsens_v0_1_feat = {
.ver_major = VER_0_1,
.crit_int = 0,
.combo_int = 0,
.adc = 1,
.srot_split = 1,
.max_sensors = 11,
.trip_min_temp = -40000,
.trip_max_temp = 120000,
};
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),
/* UPPER/LOWER TEMPERATURE THRESHOLDS */
REG_FIELD_FOR_EACH_SENSOR11(LOW_THRESH, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 0, 9),
REG_FIELD_FOR_EACH_SENSOR11(UP_THRESH, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 10, 19),
/* UPPER/LOWER INTERRUPTS [CLEAR/STATUS] */
REG_FIELD_FOR_EACH_SENSOR11(LOW_INT_CLEAR, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 20, 20),
REG_FIELD_FOR_EACH_SENSOR11(UP_INT_CLEAR, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 21, 21),
/* NO CRITICAL INTERRUPT SUPPORT on v0.1 */
/* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9),
/* No VALID field on v0.1 */
/* xxx_STATUS bits: 1 == threshold violated */
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_8226 = {
.init = init_8226,
.calibrate = tsens_calibrate_common,
.get_temp = get_temp_common,
};
struct tsens_plat_data data_8226 = {
.num_sensors = 6,
.ops = &ops_8226,
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
static const struct tsens_ops ops_8909 = {
.init = init_8909,
.calibrate = tsens_calibrate_common,
.get_temp = get_temp_common,
};
struct tsens_plat_data data_8909 = {
.num_sensors = 5,
.ops = &ops_8909,
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
static const struct tsens_ops ops_8916 = {
.init = init_common,
.calibrate = calibrate_8916,
.get_temp = get_temp_common,
};
struct tsens_plat_data data_8916 = {
.num_sensors = 5,
.ops = &ops_8916,
.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_8939 = {
.init = init_8939,
.calibrate = tsens_calibrate_common,
.get_temp = get_temp_common,
};
struct tsens_plat_data data_8939 = {
.num_sensors = 9,
.ops = &ops_8939,
.hw_ids = (unsigned int []){ 0, 1, 2, 3, 5, 6, 7, 8, 9, /* 10 */ },
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
static const struct tsens_ops ops_8974 = {
.init = init_common,
.calibrate = calibrate_8974,
.get_temp = get_temp_common,
};
struct tsens_plat_data data_8974 = {
.num_sensors = 11,
.ops = &ops_8974,
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
static const struct tsens_ops ops_9607 = {
.init = init_9607,
.calibrate = tsens_calibrate_common,
.get_temp = get_temp_common,
};
struct tsens_plat_data data_9607 = {
.num_sensors = 5,
.ops = &ops_9607,
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
+258
View File
@@ -0,0 +1,258 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019, Linaro Limited
*/
#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include "tsens.h"
/* ----- SROT ------ */
#define SROT_HW_VER_OFF 0x0000
#define SROT_CTRL_OFF 0x0004
/* ----- TM ------ */
#define TM_INT_EN_OFF 0x0000
#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004
#define TM_Sn_STATUS_OFF 0x0044
#define TM_TRDY_OFF 0x0084
#define TM_HIGH_LOW_INT_STATUS_OFF 0x0088
#define TM_HIGH_LOW_Sn_INT_THRESHOLD_OFF 0x0090
static struct tsens_legacy_calibration_format tsens_qcs404_nvmem = {
.base_len = 8,
.base_shift = 2,
.sp_len = 6,
.mode = { 4, 0 },
.invalid = { 4, 2 },
.base = { { 4, 3 }, { 4, 11 } },
.sp = {
{ { 0, 0 }, { 0, 6 } },
{ { 0, 12 }, { 0, 18 } },
{ { 0, 24 }, { 0, 30 } },
{ { 1, 4 }, { 1, 10 } },
{ { 1, 16 }, { 1, 22 } },
{ { 2, 0 }, { 2, 6 } },
{ { 2, 12 }, { 2, 18 } },
{ { 2, 24 }, { 2, 30 } },
{ { 3, 4 }, { 3, 10 } },
{ { 3, 16 }, { 3, 22 } },
},
};
static int calibrate_v1(struct tsens_priv *priv)
{
u32 p1[10], p2[10];
u32 *qfprom_cdata;
int mode, ret;
ret = tsens_calibrate_common(priv);
if (!ret)
return 0;
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
if (IS_ERR(qfprom_cdata))
return PTR_ERR(qfprom_cdata);
mode = tsens_read_calibration_legacy(priv, &tsens_qcs404_nvmem,
p1, p2,
qfprom_cdata, NULL);
compute_intercept_slope(priv, p1, p2, mode);
kfree(qfprom_cdata);
return 0;
}
/* v1.x: msm8956,8976,qcs404,405 */
static struct tsens_features tsens_v1_feat = {
.ver_major = VER_1_X,
.crit_int = 0,
.combo_int = 0,
.adc = 1,
.srot_split = 1,
.max_sensors = 11,
.trip_min_temp = -40000,
.trip_max_temp = 120000,
};
static struct tsens_features tsens_v1_no_rpm_feat = {
.ver_major = VER_1_X_NO_RPM,
.crit_int = 0,
.combo_int = 0,
.adc = 1,
.srot_split = 1,
.max_sensors = 11,
.trip_min_temp = -40000,
.trip_max_temp = 120000,
};
static const struct reg_field tsens_v1_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_OFFSET */
[TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
[TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
[SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 13),
/* ----- TM ------ */
/* INTERRUPT ENABLE */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0),
/* UPPER/LOWER TEMPERATURE THRESHOLDS */
REG_FIELD_FOR_EACH_SENSOR11(LOW_THRESH, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 0, 9),
REG_FIELD_FOR_EACH_SENSOR11(UP_THRESH, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 10, 19),
/* UPPER/LOWER INTERRUPTS [CLEAR/STATUS] */
REG_FIELD_FOR_EACH_SENSOR11(LOW_INT_CLEAR, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 20, 20),
REG_FIELD_FOR_EACH_SENSOR11(UP_INT_CLEAR, TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF, 21, 21),
[LOW_INT_STATUS_0] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 0, 0),
[LOW_INT_STATUS_1] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 1, 1),
[LOW_INT_STATUS_2] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 2, 2),
[LOW_INT_STATUS_3] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 3, 3),
[LOW_INT_STATUS_4] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 4, 4),
[LOW_INT_STATUS_5] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 5, 5),
[LOW_INT_STATUS_6] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 6, 6),
[LOW_INT_STATUS_7] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 7, 7),
[UP_INT_STATUS_0] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 8, 8),
[UP_INT_STATUS_1] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 9, 9),
[UP_INT_STATUS_2] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 10, 10),
[UP_INT_STATUS_3] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 11, 11),
[UP_INT_STATUS_4] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 12, 12),
[UP_INT_STATUS_5] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 13, 13),
[UP_INT_STATUS_6] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 14, 14),
[UP_INT_STATUS_7] = REG_FIELD(TM_HIGH_LOW_INT_STATUS_OFF, 15, 15),
/* NO CRITICAL INTERRUPT SUPPORT on v1 */
/* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9),
REG_FIELD_FOR_EACH_SENSOR11(VALID, TM_Sn_STATUS_OFF, 14, 14),
/* xxx_STATUS bits: 1 == threshold violated */
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 v1.x */
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 int __init init_8956(struct tsens_priv *priv) {
priv->sensor[0].slope = 3313;
priv->sensor[1].slope = 3275;
priv->sensor[2].slope = 3320;
priv->sensor[3].slope = 3246;
priv->sensor[4].slope = 3279;
priv->sensor[5].slope = 3257;
priv->sensor[6].slope = 3234;
priv->sensor[7].slope = 3269;
priv->sensor[8].slope = 3255;
priv->sensor[9].slope = 3239;
priv->sensor[10].slope = 3286;
return init_common(priv);
}
static int __init init_tsens_v1_no_rpm(struct tsens_priv *priv)
{
int i, ret;
u32 mask = 0;
ret = init_common(priv);
if (ret < 0) {
dev_err(priv->dev, "Init common failed %d\n", ret);
return ret;
}
ret = regmap_field_write(priv->rf[TSENS_SW_RST], 1);
if (ret) {
dev_err(priv->dev, "Reset failed\n");
return ret;
}
for (i = 0; i < priv->num_sensors; i++)
mask |= BIT(priv->sensor[i].hw_id);
ret = regmap_field_update_bits(priv->rf[SENSOR_EN], mask, mask);
if (ret) {
dev_err(priv->dev, "Sensor Enable failed\n");
return ret;
}
ret = regmap_field_write(priv->rf[TSENS_EN], 1);
if (ret) {
dev_err(priv->dev, "Enable failed\n");
return ret;
}
ret = regmap_field_write(priv->rf[TSENS_SW_RST], 0);
return ret;
}
static const struct tsens_ops ops_generic_v1 = {
.init = init_common,
.calibrate = calibrate_v1,
.get_temp = get_temp_tsens_valid,
};
struct tsens_plat_data data_tsens_v1 = {
.ops = &ops_generic_v1,
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};
static const struct tsens_ops ops_common = {
.init = init_common,
.calibrate = tsens_calibrate_common,
.get_temp = get_temp_tsens_valid,
};
struct tsens_plat_data data_8937 = {
.num_sensors = 11,
.ops = &ops_common,
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};
static const struct tsens_ops ops_8956 = {
.init = init_8956,
.calibrate = tsens_calibrate_common,
.get_temp = get_temp_tsens_valid,
};
struct tsens_plat_data data_8956 = {
.num_sensors = 11,
.ops = &ops_8956,
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};
struct tsens_plat_data data_8976 = {
.num_sensors = 11,
.ops = &ops_common,
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};
static const struct tsens_ops ops_ipq5018 = {
.init = init_tsens_v1_no_rpm,
.calibrate = tsens_calibrate_common,
.get_temp = get_temp_tsens_valid,
};
const struct tsens_plat_data data_ipq5018 = {
.num_sensors = 5,
.ops = &ops_ipq5018,
.hw_ids = (unsigned int []){0, 1, 2, 3, 4},
.feat = &tsens_v1_no_rpm_feat,
.fields = tsens_v1_regfields,
};
+316
View File
@@ -0,0 +1,316 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
* Copyright (c) 2018, Linaro Limited
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/nvmem-consumer.h>
#include <linux/regmap.h>
#include "tsens.h"
/* ----- SROT ------ */
#define SROT_HW_VER_OFF 0x0000
#define SROT_CTRL_OFF 0x0004
#define SROT_MEASURE_PERIOD 0x0008
#define SROT_Sn_CONVERSION 0x0060
#define V2_SHIFT_DEFAULT 0x0003
#define V2_SLOPE_DEFAULT 0x0cd0
#define V2_CZERO_DEFAULT 0x016a
#define ONE_PT_SLOPE 0x0cd0
#define TWO_PT_SHIFTED_GAIN 921600
#define ONE_PT_CZERO_CONST 94
#define SW_RST_DEASSERT 0x0
#define SW_RST_ASSERT 0x1
#define MEASURE_PERIOD_2mSEC 0x1
#define RESULT_FORMAT_TEMP 0x1
#define TSENS_ENABLE 0x1
#define SENSOR_CONVERSION(n) (((n) * 4) + SROT_Sn_CONVERSION)
#define CONVERSION_SHIFT_MASK GENMASK(24, 23)
#define CONVERSION_SLOPE_MASK GENMASK(22, 10)
#define CONVERSION_CZERO_MASK GENMASK(9, 0)
/* ----- 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 TM_WDOG_LOG_OFF 0x013c
/* v2.x: 8996, 8998, sdm845 */
static struct tsens_features tsens_v2_feat = {
.ver_major = VER_2_X,
.crit_int = 1,
.combo_int = 0,
.adc = 0,
.srot_split = 1,
.max_sensors = 16,
.trip_min_temp = -40000,
.trip_max_temp = 120000,
};
static struct tsens_features ipq8074_feat = {
.ver_major = VER_2_X,
.crit_int = 1,
.combo_int = 1,
.adc = 0,
.srot_split = 1,
.max_sensors = 16,
.trip_min_temp = 0,
.trip_max_temp = 204000,
};
static struct tsens_features ipq5332_feat = {
.ver_major = VER_2_X_NO_RPM,
.crit_int = 1,
.combo_int = 1,
.adc = 0,
.srot_split = 1,
.max_sensors = 16,
.trip_min_temp = 0,
.trip_max_temp = 204000,
};
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),
[SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 18),
[CODE_OR_TEMP] = REG_FIELD(SROT_CTRL_OFF, 21, 21),
[MAIN_MEASURE_PERIOD] = REG_FIELD(SROT_MEASURE_PERIOD, 0, 7),
/* ----- TM ------ */
/* INTERRUPT ENABLE */
/* v2 has separate enables for UPPER/LOWER/CRITICAL interrupts */
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2),
/* TEMPERATURE THRESHOLDS */
REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 0, 11),
REG_FIELD_FOR_EACH_SENSOR16(UP_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 12, 23),
REG_FIELD_FOR_EACH_SENSOR16(CRIT_THRESH, TM_Sn_CRITICAL_THRESHOLD_OFF, 0, 11),
/* INTERRUPTS [CLEAR/STATUS/MASK] */
REG_FIELD_SPLIT_BITS_0_15(LOW_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF),
REG_FIELD_SPLIT_BITS_0_15(LOW_INT_CLEAR, TM_UPPER_LOWER_INT_CLEAR_OFF),
REG_FIELD_SPLIT_BITS_0_15(LOW_INT_MASK, TM_UPPER_LOWER_INT_MASK_OFF),
REG_FIELD_SPLIT_BITS_16_31(UP_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF),
REG_FIELD_SPLIT_BITS_16_31(UP_INT_CLEAR, TM_UPPER_LOWER_INT_CLEAR_OFF),
REG_FIELD_SPLIT_BITS_16_31(UP_INT_MASK, TM_UPPER_LOWER_INT_MASK_OFF),
REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_STATUS, TM_CRITICAL_INT_STATUS_OFF),
REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_CLEAR, TM_CRITICAL_INT_CLEAR_OFF),
REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_MASK, TM_CRITICAL_INT_MASK_OFF),
/* WATCHDOG on v2.3 or later */
[WDOG_BARK_STATUS] = REG_FIELD(TM_CRITICAL_INT_STATUS_OFF, 31, 31),
[WDOG_BARK_CLEAR] = REG_FIELD(TM_CRITICAL_INT_CLEAR_OFF, 31, 31),
[WDOG_BARK_MASK] = REG_FIELD(TM_CRITICAL_INT_MASK_OFF, 31, 31),
[CC_MON_STATUS] = REG_FIELD(TM_CRITICAL_INT_STATUS_OFF, 30, 30),
[CC_MON_CLEAR] = REG_FIELD(TM_CRITICAL_INT_CLEAR_OFF, 30, 30),
[CC_MON_MASK] = REG_FIELD(TM_CRITICAL_INT_MASK_OFF, 30, 30),
[WDOG_BARK_COUNT] = REG_FIELD(TM_WDOG_LOG_OFF, 0, 7),
/* 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),
/* xxx_STATUS bits: 1 == threshold violated */
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 int tsens_v2_calibrate_sensor(struct device *dev, struct tsens_sensor *sensor,
struct regmap *map, u32 mode, u32 base0, u32 base1)
{
u32 shift = V2_SHIFT_DEFAULT;
u32 slope = V2_SLOPE_DEFAULT;
u32 czero = V2_CZERO_DEFAULT;
char name[20];
u32 val;
int ret;
/* Read offset value */
ret = snprintf(name, sizeof(name), "tsens_sens%d_off", sensor->hw_id);
if (ret < 0)
return ret;
ret = nvmem_cell_read_variable_le_u32(dev, name, &sensor->offset);
if (ret)
return ret;
/* Based on calib mode, program SHIFT, SLOPE and CZERO */
switch (mode) {
case TWO_PT_CALIB:
slope = (TWO_PT_SHIFTED_GAIN / (base1 - base0));
czero = (base0 + sensor->offset - ((base1 - base0) / 3));
break;
case ONE_PT_CALIB2:
czero = base0 + sensor->offset - ONE_PT_CZERO_CONST;
slope = ONE_PT_SLOPE;
break;
default:
dev_dbg(dev, "calibrationless mode\n");
}
val = FIELD_PREP(CONVERSION_SHIFT_MASK, shift) |
FIELD_PREP(CONVERSION_SLOPE_MASK, slope) |
FIELD_PREP(CONVERSION_CZERO_MASK, czero);
regmap_write(map, SENSOR_CONVERSION(sensor->hw_id), val);
return 0;
}
static int tsens_v2_calibration(struct tsens_priv *priv)
{
struct device *dev = priv->dev;
u32 mode, base0, base1;
int i, ret;
if (priv->num_sensors > MAX_SENSORS)
return -EINVAL;
ret = nvmem_cell_read_variable_le_u32(priv->dev, "mode", &mode);
if (ret == -ENOENT)
dev_warn(priv->dev, "Calibration data not present in DT\n");
if (ret < 0)
return ret;
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
ret = nvmem_cell_read_variable_le_u32(priv->dev, "base0", &base0);
if (ret < 0)
return ret;
ret = nvmem_cell_read_variable_le_u32(priv->dev, "base1", &base1);
if (ret < 0)
return ret;
/* Calibrate each sensor */
for (i = 0; i < priv->num_sensors; i++) {
ret = tsens_v2_calibrate_sensor(dev, &priv->sensor[i], priv->srot_map,
mode, base0, base1);
if (ret < 0)
return ret;
}
return 0;
}
static int __init init_tsens_v2_no_rpm(struct tsens_priv *priv)
{
struct device *dev = priv->dev;
int i, ret;
u32 val = 0;
ret = init_common(priv);
if (ret < 0)
return ret;
priv->rf[CODE_OR_TEMP] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[CODE_OR_TEMP]);
if (IS_ERR(priv->rf[CODE_OR_TEMP]))
return PTR_ERR(priv->rf[CODE_OR_TEMP]);
priv->rf[MAIN_MEASURE_PERIOD] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[MAIN_MEASURE_PERIOD]);
if (IS_ERR(priv->rf[MAIN_MEASURE_PERIOD]))
return PTR_ERR(priv->rf[MAIN_MEASURE_PERIOD]);
regmap_field_write(priv->rf[TSENS_SW_RST], SW_RST_ASSERT);
regmap_field_write(priv->rf[MAIN_MEASURE_PERIOD], MEASURE_PERIOD_2mSEC);
/* Enable available sensors */
for (i = 0; i < priv->num_sensors; i++)
val |= 1 << priv->sensor[i].hw_id;
regmap_field_write(priv->rf[SENSOR_EN], val);
/* Select temperature format, unit is deci-Celsius */
regmap_field_write(priv->rf[CODE_OR_TEMP], RESULT_FORMAT_TEMP);
regmap_field_write(priv->rf[TSENS_SW_RST], SW_RST_DEASSERT);
regmap_field_write(priv->rf[TSENS_EN], TSENS_ENABLE);
return 0;
}
static const struct tsens_ops ops_generic_v2 = {
.init = init_common,
.get_temp = get_temp_tsens_valid,
};
struct tsens_plat_data data_tsens_v2 = {
.ops = &ops_generic_v2,
.feat = &tsens_v2_feat,
.fields = tsens_v2_regfields,
};
struct tsens_plat_data data_ipq8074 = {
.ops = &ops_generic_v2,
.feat = &ipq8074_feat,
.fields = tsens_v2_regfields,
};
static const struct tsens_ops ops_ipq5332 = {
.init = init_tsens_v2_no_rpm,
.get_temp = get_temp_tsens_valid,
.calibrate = tsens_v2_calibration,
};
const struct tsens_plat_data data_ipq5332 = {
.num_sensors = 5,
.ops = &ops_ipq5332,
.hw_ids = (unsigned int []){11, 12, 13, 14, 15},
.feat = &ipq5332_feat,
.fields = tsens_v2_regfields,
};
const struct tsens_plat_data data_ipq5424 = {
.num_sensors = 7,
.ops = &ops_ipq5332,
.hw_ids = (unsigned int []){9, 10, 11, 12, 13, 14, 15},
.feat = &ipq5332_feat,
.fields = tsens_v2_regfields,
};
/* Kept around for backward compatibility with old msm8996.dtsi */
struct tsens_plat_data data_8996 = {
.num_sensors = 13,
.ops = &ops_generic_v2,
.feat = &tsens_v2_feat,
.fields = tsens_v2_regfields,
};
/* Do not enable wakeup capable interrupts for automotive platforms */
struct tsens_plat_data data_automotive_v2 = {
.ops = &ops_generic_v2,
.feat = &tsens_v2_feat,
.fields = tsens_v2_regfields,
.no_irq_wake = true,
};
File diff suppressed because it is too large Load Diff
+684
View File
@@ -0,0 +1,684 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#ifndef __QCOM_TSENS_H__
#define __QCOM_TSENS_H__
#define NO_PT_CALIB 0x0
#define ONE_PT_CALIB 0x1
#define ONE_PT_CALIB2 0x2
#define TWO_PT_CALIB 0x3
#define ONE_PT_CALIB2_NO_OFFSET 0x6
#define TWO_PT_CALIB_NO_OFFSET 0x7
#define CAL_DEGC_PT1 30
#define CAL_DEGC_PT2 120
#define SLOPE_FACTOR 1000
#define SLOPE_DEFAULT 3200
#define TIMEOUT_US 100
#define THRESHOLD_MAX_ADC_CODE 0x3ff
#define THRESHOLD_MIN_ADC_CODE 0x0
#define MAX_SENSORS 16
#define MAX_READ_RETRY 3
#include <linux/interrupt.h>
#include <linux/thermal.h>
#include <linux/regmap.h>
#include <linux/slab.h>
struct tsens_priv;
/* IP version numbers in ascending order */
enum tsens_ver {
VER_0 = 0,
VER_0_1,
VER_1_X,
VER_1_X_NO_RPM,
VER_2_X,
VER_2_X_NO_RPM,
};
enum tsens_irq_type {
LOWER,
UPPER,
CRITICAL,
};
/**
* struct tsens_sensor - data for each sensor connected to the tsens device
* @priv: tsens device instance that this sensor is connected to
* @tzd: pointer to the thermal zone that this sensor is in
* @offset: offset of temperature adjustment curve
* @hw_id: HW ID can be used in case of platform-specific IDs
* @slope: slope of temperature adjustment curve
* @status: 8960-specific variable to track 8960 and 8660 status register offset
*/
struct tsens_sensor {
struct tsens_priv *priv;
struct thermal_zone_device *tzd;
int offset;
unsigned int hw_id;
int slope;
u32 status;
int p1_calib_offset;
int p2_calib_offset;
};
/**
* struct tsens_ops - operations as supported by the tsens device
* @init: Function to initialize the tsens device
* @calibrate: Function to calibrate the tsens device
* @get_temp: Function which returns the temp in millidegC
* @enable: Function to enable (clocks/power) tsens device
* @disable: Function to disable the tsens device
* @suspend: Function to suspend the tsens device
* @resume: Function to resume the tsens device
*/
struct tsens_ops {
/* mandatory callbacks */
int (*init)(struct tsens_priv *priv);
int (*calibrate)(struct tsens_priv *priv);
int (*get_temp)(const struct tsens_sensor *s, int *temp);
/* optional callbacks */
int (*enable)(struct tsens_priv *priv, int i);
void (*disable)(struct tsens_priv *priv);
int (*suspend)(struct tsens_priv *priv);
int (*resume)(struct tsens_priv *priv);
};
#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)
#define REG_FIELD_SPLIT_BITS_0_15(_name, _offset) \
[_name##_##0] = REG_FIELD(_offset, 0, 0), \
[_name##_##1] = REG_FIELD(_offset, 1, 1), \
[_name##_##2] = REG_FIELD(_offset, 2, 2), \
[_name##_##3] = REG_FIELD(_offset, 3, 3), \
[_name##_##4] = REG_FIELD(_offset, 4, 4), \
[_name##_##5] = REG_FIELD(_offset, 5, 5), \
[_name##_##6] = REG_FIELD(_offset, 6, 6), \
[_name##_##7] = REG_FIELD(_offset, 7, 7), \
[_name##_##8] = REG_FIELD(_offset, 8, 8), \
[_name##_##9] = REG_FIELD(_offset, 9, 9), \
[_name##_##10] = REG_FIELD(_offset, 10, 10), \
[_name##_##11] = REG_FIELD(_offset, 11, 11), \
[_name##_##12] = REG_FIELD(_offset, 12, 12), \
[_name##_##13] = REG_FIELD(_offset, 13, 13), \
[_name##_##14] = REG_FIELD(_offset, 14, 14), \
[_name##_##15] = REG_FIELD(_offset, 15, 15)
#define REG_FIELD_SPLIT_BITS_16_31(_name, _offset) \
[_name##_##0] = REG_FIELD(_offset, 16, 16), \
[_name##_##1] = REG_FIELD(_offset, 17, 17), \
[_name##_##2] = REG_FIELD(_offset, 18, 18), \
[_name##_##3] = REG_FIELD(_offset, 19, 19), \
[_name##_##4] = REG_FIELD(_offset, 20, 20), \
[_name##_##5] = REG_FIELD(_offset, 21, 21), \
[_name##_##6] = REG_FIELD(_offset, 22, 22), \
[_name##_##7] = REG_FIELD(_offset, 23, 23), \
[_name##_##8] = REG_FIELD(_offset, 24, 24), \
[_name##_##9] = REG_FIELD(_offset, 25, 25), \
[_name##_##10] = REG_FIELD(_offset, 26, 26), \
[_name##_##11] = REG_FIELD(_offset, 27, 27), \
[_name##_##12] = REG_FIELD(_offset, 28, 28), \
[_name##_##13] = REG_FIELD(_offset, 29, 29), \
[_name##_##14] = REG_FIELD(_offset, 30, 30), \
[_name##_##15] = REG_FIELD(_offset, 31, 31)
/*
* reg_field IDs to use as an index into an array
* If you change the order of the entries, check the devm_regmap_field_alloc()
* calls in init_common()
*/
enum regfield_ids {
/* ----- SROT ------ */
/* HW_VER */
VER_MAJOR,
VER_MINOR,
VER_STEP,
/* CTRL_OFFSET */
TSENS_EN,
TSENS_SW_RST,
SENSOR_EN,
CODE_OR_TEMP,
MAIN_MEASURE_PERIOD,
/* ----- TM ------ */
/* TRDY */
TRDY,
/* INTERRUPT ENABLE */
INT_EN, /* v2+ has separate enables for crit, upper and lower irq */
/* STATUS */
LAST_TEMP_0, /* 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, /* 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,
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,
LOW_INT_STATUS_0, /* LOWER interrupt status */
LOW_INT_STATUS_1,
LOW_INT_STATUS_2,
LOW_INT_STATUS_3,
LOW_INT_STATUS_4,
LOW_INT_STATUS_5,
LOW_INT_STATUS_6,
LOW_INT_STATUS_7,
LOW_INT_STATUS_8,
LOW_INT_STATUS_9,
LOW_INT_STATUS_10,
LOW_INT_STATUS_11,
LOW_INT_STATUS_12,
LOW_INT_STATUS_13,
LOW_INT_STATUS_14,
LOW_INT_STATUS_15,
LOW_INT_CLEAR_0, /* LOWER interrupt clear */
LOW_INT_CLEAR_1,
LOW_INT_CLEAR_2,
LOW_INT_CLEAR_3,
LOW_INT_CLEAR_4,
LOW_INT_CLEAR_5,
LOW_INT_CLEAR_6,
LOW_INT_CLEAR_7,
LOW_INT_CLEAR_8,
LOW_INT_CLEAR_9,
LOW_INT_CLEAR_10,
LOW_INT_CLEAR_11,
LOW_INT_CLEAR_12,
LOW_INT_CLEAR_13,
LOW_INT_CLEAR_14,
LOW_INT_CLEAR_15,
LOW_INT_MASK_0, /* LOWER interrupt mask */
LOW_INT_MASK_1,
LOW_INT_MASK_2,
LOW_INT_MASK_3,
LOW_INT_MASK_4,
LOW_INT_MASK_5,
LOW_INT_MASK_6,
LOW_INT_MASK_7,
LOW_INT_MASK_8,
LOW_INT_MASK_9,
LOW_INT_MASK_10,
LOW_INT_MASK_11,
LOW_INT_MASK_12,
LOW_INT_MASK_13,
LOW_INT_MASK_14,
LOW_INT_MASK_15,
LOW_THRESH_0, /* LOWER threshold values */
LOW_THRESH_1,
LOW_THRESH_2,
LOW_THRESH_3,
LOW_THRESH_4,
LOW_THRESH_5,
LOW_THRESH_6,
LOW_THRESH_7,
LOW_THRESH_8,
LOW_THRESH_9,
LOW_THRESH_10,
LOW_THRESH_11,
LOW_THRESH_12,
LOW_THRESH_13,
LOW_THRESH_14,
LOW_THRESH_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,
UP_INT_STATUS_0, /* UPPER interrupt status */
UP_INT_STATUS_1,
UP_INT_STATUS_2,
UP_INT_STATUS_3,
UP_INT_STATUS_4,
UP_INT_STATUS_5,
UP_INT_STATUS_6,
UP_INT_STATUS_7,
UP_INT_STATUS_8,
UP_INT_STATUS_9,
UP_INT_STATUS_10,
UP_INT_STATUS_11,
UP_INT_STATUS_12,
UP_INT_STATUS_13,
UP_INT_STATUS_14,
UP_INT_STATUS_15,
UP_INT_CLEAR_0, /* UPPER interrupt clear */
UP_INT_CLEAR_1,
UP_INT_CLEAR_2,
UP_INT_CLEAR_3,
UP_INT_CLEAR_4,
UP_INT_CLEAR_5,
UP_INT_CLEAR_6,
UP_INT_CLEAR_7,
UP_INT_CLEAR_8,
UP_INT_CLEAR_9,
UP_INT_CLEAR_10,
UP_INT_CLEAR_11,
UP_INT_CLEAR_12,
UP_INT_CLEAR_13,
UP_INT_CLEAR_14,
UP_INT_CLEAR_15,
UP_INT_MASK_0, /* UPPER interrupt mask */
UP_INT_MASK_1,
UP_INT_MASK_2,
UP_INT_MASK_3,
UP_INT_MASK_4,
UP_INT_MASK_5,
UP_INT_MASK_6,
UP_INT_MASK_7,
UP_INT_MASK_8,
UP_INT_MASK_9,
UP_INT_MASK_10,
UP_INT_MASK_11,
UP_INT_MASK_12,
UP_INT_MASK_13,
UP_INT_MASK_14,
UP_INT_MASK_15,
UP_THRESH_0, /* UPPER threshold values */
UP_THRESH_1,
UP_THRESH_2,
UP_THRESH_3,
UP_THRESH_4,
UP_THRESH_5,
UP_THRESH_6,
UP_THRESH_7,
UP_THRESH_8,
UP_THRESH_9,
UP_THRESH_10,
UP_THRESH_11,
UP_THRESH_12,
UP_THRESH_13,
UP_THRESH_14,
UP_THRESH_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,
CRIT_INT_STATUS_0, /* CRITICAL interrupt status */
CRIT_INT_STATUS_1,
CRIT_INT_STATUS_2,
CRIT_INT_STATUS_3,
CRIT_INT_STATUS_4,
CRIT_INT_STATUS_5,
CRIT_INT_STATUS_6,
CRIT_INT_STATUS_7,
CRIT_INT_STATUS_8,
CRIT_INT_STATUS_9,
CRIT_INT_STATUS_10,
CRIT_INT_STATUS_11,
CRIT_INT_STATUS_12,
CRIT_INT_STATUS_13,
CRIT_INT_STATUS_14,
CRIT_INT_STATUS_15,
CRIT_INT_CLEAR_0, /* CRITICAL interrupt clear */
CRIT_INT_CLEAR_1,
CRIT_INT_CLEAR_2,
CRIT_INT_CLEAR_3,
CRIT_INT_CLEAR_4,
CRIT_INT_CLEAR_5,
CRIT_INT_CLEAR_6,
CRIT_INT_CLEAR_7,
CRIT_INT_CLEAR_8,
CRIT_INT_CLEAR_9,
CRIT_INT_CLEAR_10,
CRIT_INT_CLEAR_11,
CRIT_INT_CLEAR_12,
CRIT_INT_CLEAR_13,
CRIT_INT_CLEAR_14,
CRIT_INT_CLEAR_15,
CRIT_INT_MASK_0, /* CRITICAL interrupt mask */
CRIT_INT_MASK_1,
CRIT_INT_MASK_2,
CRIT_INT_MASK_3,
CRIT_INT_MASK_4,
CRIT_INT_MASK_5,
CRIT_INT_MASK_6,
CRIT_INT_MASK_7,
CRIT_INT_MASK_8,
CRIT_INT_MASK_9,
CRIT_INT_MASK_10,
CRIT_INT_MASK_11,
CRIT_INT_MASK_12,
CRIT_INT_MASK_13,
CRIT_INT_MASK_14,
CRIT_INT_MASK_15,
CRIT_THRESH_0, /* CRITICAL threshold values */
CRIT_THRESH_1,
CRIT_THRESH_2,
CRIT_THRESH_3,
CRIT_THRESH_4,
CRIT_THRESH_5,
CRIT_THRESH_6,
CRIT_THRESH_7,
CRIT_THRESH_8,
CRIT_THRESH_9,
CRIT_THRESH_10,
CRIT_THRESH_11,
CRIT_THRESH_12,
CRIT_THRESH_13,
CRIT_THRESH_14,
CRIT_THRESH_15,
/* WATCHDOG */
WDOG_BARK_STATUS,
WDOG_BARK_CLEAR,
WDOG_BARK_MASK,
WDOG_BARK_COUNT,
/* CYCLE COMPLETION MONITOR */
CC_MON_STATUS,
CC_MON_CLEAR,
CC_MON_MASK,
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,
/* 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?
* @combo_int: does the IP use one IRQ for up, low and critical thresholds?
* @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?
* @has_watchdog: does this IP support watchdog functionality?
* @max_sensors: maximum sensors supported by this version of the IP
* @trip_min_temp: minimum trip temperature supported by this version of the IP
* @trip_max_temp: maximum trip temperature supported by this version of the IP
*/
struct tsens_features {
unsigned int ver_major;
unsigned int crit_int:1;
unsigned int combo_int:1;
unsigned int adc:1;
unsigned int srot_split:1;
unsigned int has_watchdog:1;
unsigned int max_sensors;
int trip_min_temp;
int trip_max_temp;
};
/**
* struct tsens_plat_data - tsens compile-time platform data
* @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
* @feat: features of the IP
* @fields: bitfield locations
* @no_irq_wake: if set, TSENS interrupts will not be configured as wakeup sources
*/
struct tsens_plat_data {
const u32 num_sensors;
const struct tsens_ops *ops;
unsigned int *hw_ids;
struct tsens_features *feat;
const struct reg_field *fields;
bool no_irq_wake;
};
/**
* struct tsens_context - Registers to be saved/restored across a context loss
* @threshold: Threshold register value
* @control: Control register value
*/
struct tsens_context {
int threshold;
int control;
};
/**
* struct tsens_priv - private data for each instance of the tsens IP
* @dev: pointer to struct device
* @num_sensors: number of sensors enabled on this device
* @tm_map: pointer to TM register address space
* @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
* @ul_lock: lock while processing upper/lower threshold interrupts
* @crit_lock: lock while processing critical threshold interrupts
* @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
* @debug_root: pointer to debugfs dentry for all tsens
* @debug: pointer to debugfs dentry for tsens controller
* @uplow_irq: IRQ number for uplow (upper/lower) threshold interrupts
* @crit_irq: IRQ number for critical threshold interrupts
* @combined_irq: IRQ number for combined threshold interrupts
* @sensor: list of sensors attached to this device
*/
struct tsens_priv {
struct device *dev;
u32 num_sensors;
struct regmap *tm_map;
struct regmap *srot_map;
u32 tm_offset;
/* lock for upper/lower threshold interrupts */
spinlock_t ul_lock;
struct regmap_field *rf[MAX_REGFIELDS];
struct tsens_context ctx;
struct tsens_features *feat;
const struct reg_field *fields;
const struct tsens_ops *ops;
struct dentry *debug_root;
struct dentry *debug;
int uplow_irq;
int crit_irq;
int combined_irq;
struct tsens_sensor sensor[] __counted_by(num_sensors);
};
/**
* struct tsens_single_value - internal representation of a single field inside nvmem calibration data
* @idx: index into the u32 data array
* @shift: the shift of the first bit in the value
* @blob: index of the data blob to use for this cell
*/
struct tsens_single_value {
u8 idx;
u8 shift;
u8 blob;
};
/**
* struct tsens_legacy_calibration_format - description of calibration data used when parsing the legacy nvmem blob
* @base_len: the length of the base fields inside calibration data
* @base_shift: the shift to be applied to base data
* @sp_len: the length of the sN_pM fields inside calibration data
* @mode: descriptor of the calibration mode field
* @invalid: descriptor of the calibration mode invalid field
* @base: descriptors of the base0 and base1 fields
* @sp: descriptors of the sN_pM fields
*/
struct tsens_legacy_calibration_format {
unsigned int base_len;
unsigned int base_shift;
unsigned int sp_len;
/* just two bits */
struct tsens_single_value mode;
/* on all platforms except 8974 invalid is the third bit of what downstream calls 'mode' */
struct tsens_single_value invalid;
struct tsens_single_value base[2];
struct tsens_single_value sp[][2];
};
char *qfprom_read(struct device *dev, const char *cname);
int tsens_read_calibration_legacy(struct tsens_priv *priv,
const struct tsens_legacy_calibration_format *format,
u32 *p1, u32 *p2,
u32 *cdata, u32 *csel);
int tsens_read_calibration(struct tsens_priv *priv, int shift, u32 *p1, u32 *p2, bool backup);
int tsens_calibrate_nvmem(struct tsens_priv *priv, int shift);
int tsens_calibrate_common(struct tsens_priv *priv);
void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode);
int init_common(struct tsens_priv *priv);
int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
int get_temp_common(const struct tsens_sensor *s, int *temp);
#ifdef CONFIG_SUSPEND
int tsens_resume_common(struct tsens_priv *priv);
int tsens_suspend_common(struct tsens_priv *priv);
#else
static inline int tsens_resume_common(struct tsens_priv *priv)
{
return 0;
}
static inline int tsens_suspend_common(struct tsens_priv *priv)
{
return 0;
}
#endif
/* TSENS target */
extern struct tsens_plat_data data_8960;
/* TSENS v0.1 targets */
extern struct tsens_plat_data data_8226, data_8909, data_8916, data_8939, data_8974, data_9607;
/* TSENS v1 targets */
extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956;
/* TSENS v1 with no RPM targets */
extern const struct tsens_plat_data data_ipq5018;
/* TSENS v2 targets */
extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2;
extern const struct tsens_plat_data data_ipq5332, data_ipq5424;
/* TSENS automotive targets */
extern struct tsens_plat_data data_automotive_v2;
#endif /* __QCOM_TSENS_H__ */