kernel-brax3-ubuntu-touch/drivers/thermal/mediatek/board_temp.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

429 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#define READ_TIA_REG_COUNT_MAX 3
#define get_tia_rc_sel(val, offset, mask) (((val) & (mask)) >> (offset))
#define is_adc_data_valid(val, bit) (((val) & BIT(bit)) != 0)
#define get_adc_data(val, bit) ((val) & GENMASK(bit, 0))
#define adc_data_to_v_in(val) (1900 - (((val) * 1000) >> 14))
/**
* struct tia_data - parameters to parse the data from TIA
* @valid_bit: valid bit in TIA DATA register
* @rc_offset: RC bit offset in TIA DATA register
* @rc_mask: bitmask for RC bit
* @rc_sel_to_value: function to get default pullup resistance value
*/
struct tia_data {
unsigned int valid_bit;
unsigned int rc_offset;
unsigned int rc_mask;
unsigned int (*rc_sel_to_value)(unsigned int sel);
};
/**
* struct pmic_auxadc_data - parameters and callback functions for NTC
* resistance value calculation
* @is_initialized: indicate the auxadc data was been initialized or not
* @default_pullup_v: voltage of internal pullup resistance
* @pullup_v: pullup voltage of each pullup resistance type. It should
* equal to default_pullup_v if no extra input buffer for SDM.
* @pullup_r: pullup resistance value
* @num_of_pullup_r_type: number of pullup resistance type
* @pullup_r_calibration: calculate the parameters for actual NTC resitance
* value calculation. Set to NULL if no extra input
* buffer for SDM.
* @tia_param: parameters to parse auxadc value from TIA DATA register
*/
struct pmic_auxadc_data {
bool is_initialized;
unsigned int default_pullup_v;
unsigned int *pullup_v;
unsigned int *pullup_r;
unsigned int num_of_pullup_r_type;
int (*pullup_r_calibration)(struct device *dev,
struct pmic_auxadc_data *adc_data);
unsigned long long (*adc2volt)(unsigned int adc_raw);
struct tia_data *tia_param;
};
struct board_ntc_info {
struct device *dev;
int *lookup_table;
int lookup_table_num;
void __iomem *data_reg;
void __iomem *dbg_reg;
void __iomem *en_reg;
struct pmic_auxadc_data *adc_data;
};
unsigned int tia2_rc_sel_to_value(unsigned int sel)
{
unsigned int resistance;
switch (sel) {
case 1:
resistance = 30000; /* 30K */
break;
case 2:
resistance = 400000; /* 400K */
break;
case 0:
default:
resistance = 100000; /* 100K */
break;
}
return resistance;
}
unsigned int tia3_rc_sel_to_value(unsigned int sel)
{
unsigned int resistance;
switch (sel) {
case 0:
default:
resistance = 100000; /* 100K */
break;
}
return resistance;
}
unsigned long long mt6685_adc2volt(unsigned int adc_raw)
{
return ((unsigned long long)adc_raw * 184000) >> 15;
}
unsigned long long mt6377_adc2volt(unsigned int adc_raw)
{
return ((unsigned long long)adc_raw * 184000) >> 15;
}
static struct tia_data tia2_data = {
.valid_bit = 15,
.rc_offset = 16,
.rc_mask = GENMASK(17, 16),
.rc_sel_to_value = tia2_rc_sel_to_value,
};
static struct tia_data tia3_data = {
.valid_bit = 15,
.rc_offset = 16,
.rc_mask = GENMASK(17, 16),
.rc_sel_to_value = tia3_rc_sel_to_value,
};
static struct pmic_auxadc_data mt6685_pmic_auxadc_data = {
.default_pullup_v = 184000,
.num_of_pullup_r_type = 3,
.pullup_r_calibration = NULL,
.adc2volt = mt6685_adc2volt,
.tia_param = &tia2_data,
};
static struct pmic_auxadc_data mt6377_pmic_auxadc_data = {
.default_pullup_v = 184000,
.num_of_pullup_r_type = 1,
.pullup_r_calibration = NULL,
.adc2volt = mt6377_adc2volt,
.tia_param = &tia3_data,
};
static const struct of_device_id board_ntc_of_match[] = {
{
.compatible = "mediatek,mt6685-tia-ntc",
.data = (void *)&mt6685_pmic_auxadc_data,
},
{
.compatible = "mediatek,mt6377-tia-ntc",
.data = (void *)&mt6377_pmic_auxadc_data,
},
{},
};
MODULE_DEVICE_TABLE(of, board_ntc_of_match);
static int board_ntc_r_to_temp(struct board_ntc_info *ntc_info,
int val)
{
int temp, temp_hi, temp_lo, r_hi, r_lo;
int i;
for (i = 0; i < ntc_info->lookup_table_num; i++) {
if (val >= ntc_info->lookup_table[2 * i + 1])
break;
}
if (i == 0) {
temp = ntc_info->lookup_table[0];
} else if (i >= ntc_info->lookup_table_num) {
temp = ntc_info->lookup_table[2 *
(ntc_info->lookup_table_num - 1)];
} else {
r_hi = ntc_info->lookup_table[2 * i - 1];
r_lo = ntc_info->lookup_table[2 * i + 1];
temp_hi = ntc_info->lookup_table[2 * i - 2];
temp_lo = ntc_info->lookup_table[2 * i];
temp = temp_hi + mult_frac(temp_lo - temp_hi, val - r_hi,
r_lo - r_hi);
}
return temp;
}
static unsigned int calculate_r_ntc(unsigned long long v_in,
unsigned int pullup_r, unsigned int pullup_v)
{
unsigned int r_ntc;
if (v_in >= pullup_v)
return 0;
r_ntc = (unsigned int)(v_in * pullup_r / (pullup_v - v_in));
return r_ntc;
}
static int board_ntc_get_temp(void *data, int *temp)
{
struct board_ntc_info *ntc_info = (struct board_ntc_info *)data;
struct pmic_auxadc_data *adc_data = ntc_info->adc_data;
struct tia_data *tia_param = ntc_info->adc_data->tia_param;
unsigned int val, r_type, r_ntc, dbg_reg, en_reg, count = 0;
unsigned long long v_in;
bool is_val_valid, is_rtype_valid;
static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 10);
ratelimit_set_flags(&ratelimit, RATELIMIT_MSG_ON_RELEASE);
while (count < READ_TIA_REG_COUNT_MAX) {
val = readl(ntc_info->data_reg);
is_val_valid = is_adc_data_valid(val, tia_param->valid_bit);
if (!is_val_valid) {
if (ntc_info->dbg_reg && ntc_info->en_reg) {
dbg_reg = readl(ntc_info->dbg_reg);
en_reg = readl(ntc_info->en_reg);
dev_info(ntc_info->dev,
"TIA data invalid, 0x%x, dbg=0x%x, en=0x%x\n",
val, dbg_reg, en_reg);
} else {
dev_info(ntc_info->dev, "TIA data invalid, 0x%x\n", val);
}
goto RETRY;
}
r_type = get_tia_rc_sel(val, tia_param->rc_offset, tia_param->rc_mask);
if (r_type >= adc_data->num_of_pullup_r_type) {
dev_info(ntc_info->dev, "Invalid r_type = %d\n", r_type);
is_rtype_valid = false;
goto RETRY;
} else {
is_rtype_valid = true;
}
break;
RETRY:
mdelay(1);
count++;
}
if (!is_val_valid)
return -EAGAIN;
if (!is_rtype_valid)
return -EINVAL;
if (!ntc_info->adc_data->adc2volt) {
dev_err(ntc_info->dev, "adc2volt should exist\n");
return -ENODEV;
}
v_in = ntc_info->adc_data->adc2volt(get_adc_data(val, tia_param->valid_bit - 1));
r_ntc = calculate_r_ntc(v_in, adc_data->pullup_r[r_type],
adc_data->pullup_v[r_type]);
if (!r_ntc) {
dev_err(ntc_info->dev,
"r_ntc is 0! v_in/pullup_r/pullup_v=%d/%d/%d\n",
v_in, adc_data->pullup_r[r_type],
adc_data->pullup_v[r_type]);
*temp = THERMAL_TEMP_INVALID;
} else {
*temp = board_ntc_r_to_temp(ntc_info, r_ntc);
}
if (__ratelimit(&ratelimit)) {
dev_info(ntc_info->dev, "val=0x%x, v_in/r_type/r_ntc/t=%d/%d/%d/%d\n",
val, v_in, r_type, r_ntc, *temp);
}
return 0;
}
static const struct thermal_zone_of_device_ops board_ntc_ops = {
.get_temp = board_ntc_get_temp,
};
static int board_ntc_init_auxadc_data(struct device *dev,
struct pmic_auxadc_data *adc_data)
{
int ret = 0, size, i;
int num = adc_data->num_of_pullup_r_type;
if (num <= 0)
return -EINVAL;
size = sizeof(*adc_data->pullup_v) * num * 2;
adc_data->pullup_v = devm_kzalloc(dev, size, GFP_KERNEL);
if (!adc_data->pullup_v)
return -ENOMEM;
adc_data->pullup_r = (unsigned int *)(adc_data->pullup_v + num);
if (adc_data->pullup_r_calibration) {
ret = adc_data->pullup_r_calibration(dev, adc_data);
} else {
for (i = 0; i < num; i++) {
adc_data->pullup_r[i] =
adc_data->tia_param->rc_sel_to_value(i);
adc_data->pullup_v[i] = adc_data->default_pullup_v;
dev_info(dev, "%d: default pullup_r=%d, pullup_v=%d\n",
i, adc_data->pullup_r[i],
adc_data->pullup_v[i]);
}
}
return ret;
}
static int board_ntc_parse_lookup_table(struct device *dev,
struct board_ntc_info *ntc_info)
{
struct device_node *np = dev->of_node;
int num, ret;
num = of_property_count_elems_of_size(np, "temperature-lookup-table",
sizeof(unsigned int));
if (num < 0) {
dev_err(dev, "lookup table is not found\n");
return num;
}
if (num % 2) {
dev_err(dev, "temp vs ADC value in table are unpaired\n");
return -EINVAL;
}
ntc_info->lookup_table = devm_kcalloc(dev, num,
sizeof(*ntc_info->lookup_table),
GFP_KERNEL);
if (!ntc_info->lookup_table)
return -ENOMEM;
ret = of_property_read_u32_array(np, "temperature-lookup-table",
(unsigned int *)ntc_info->lookup_table,
num);
if (ret < 0) {
dev_err(dev, "Failed to read temperature lookup table: %d\n",
ret);
return ret;
}
ntc_info->lookup_table_num = num / 2;
return 0;
}
static int board_ntc_probe(struct platform_device *pdev)
{
struct board_ntc_info *ntc_info;
struct resource *res;
void __iomem *tia_reg;
struct thermal_zone_device *tz_dev;
int ret;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "Only DT based supported\n");
return -ENODEV;
}
ntc_info = devm_kzalloc(&pdev->dev, sizeof(*ntc_info), GFP_KERNEL);
if (!ntc_info)
return -ENOMEM;
ret = board_ntc_parse_lookup_table(&pdev->dev, ntc_info);
if (ret < 0)
return ret;
ntc_info->dev = &pdev->dev;
ntc_info->adc_data = (struct pmic_auxadc_data *)
of_device_get_match_data(&pdev->dev);
if (!ntc_info->adc_data->is_initialized) {
ret = board_ntc_init_auxadc_data(&pdev->dev,
ntc_info->adc_data);
if (ret)
return ret;
ntc_info->adc_data->is_initialized = true;
}
platform_set_drvdata(pdev, ntc_info);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tia_reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tia_reg))
return PTR_ERR(tia_reg);
ntc_info->data_reg = tia_reg;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
tia_reg = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(tia_reg))
ntc_info->en_reg = tia_reg;
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
tia_reg = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(tia_reg))
ntc_info->dbg_reg = tia_reg;
tz_dev = devm_thermal_zone_of_sensor_register(
&pdev->dev, 0, ntc_info, &board_ntc_ops);
if (IS_ERR(tz_dev)) {
ret = PTR_ERR(tz_dev);
dev_err(&pdev->dev, "Thermal zone sensor register fail:%d\n",
ret);
return ret;
}
return 0;
}
static struct platform_driver board_ntc_driver = {
.probe = board_ntc_probe,
.driver = {
.name = "mtk-board-ntc",
.of_match_table = board_ntc_of_match,
},
};
module_platform_driver(board_ntc_driver);
MODULE_AUTHOR("Shun-Yao Yang <brian-sy.yang@mediatek.com>");
MODULE_DESCRIPTION("Mediatek on board NTC driver via TIA HW");
MODULE_LICENSE("GPL v2");