Skip to content

Commit ae332ec

Browse files
Nicolas Frattarolidlezcano
authored andcommitted
thermal/drivers/rockchip: Support reading trim values from OTP
Many of the Rockchip SoCs support storing trim values for the sensors in factory programmable memory. These values specify a fixed offset from the sensor's returned temperature to get a more accurate picture of what temperature the silicon is actually at. The way this is implemented is with various OTP cells, which may be absent. There may both be whole-TSADC trim values, as well as per-sensor trim values. In the downstream driver, whole-chip trim values override the per-sensor trim values. This rewrite of the functionality changes the semantics to something I see as slightly more useful: allow the whole-chip trim values to serve as a fallback for lacking per-sensor trim values, instead of overriding already present sensor trim values. Additionally, the chip may specify an offset (trim_base, trim_base_frac) in degrees celsius and degrees decicelsius respectively which defines what the basis is from which the trim, if any, should be calculated from. By default, this is 30 degrees Celsius, but the chip can once again specify a different value through OTP cells. The implementation of these trim calculations have been tested extensively on an RK3576, where it was confirmed to get rid of pesky 1.8 degree Celsius offsets between certain sensors. Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com> Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-5-b6e9efbf1015@collabora.com Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
1 parent 75b98a2 commit ae332ec

1 file changed

Lines changed: 202 additions & 19 deletions

File tree

drivers/thermal/rockchip_thermal.c

Lines changed: 202 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/interrupt.h>
1010
#include <linux/io.h>
1111
#include <linux/module.h>
12+
#include <linux/nvmem-consumer.h>
1213
#include <linux/of.h>
1314
#include <linux/of_address.h>
1415
#include <linux/of_irq.h>
@@ -69,23 +70,28 @@ struct chip_tsadc_table {
6970
* struct rockchip_tsadc_chip - hold the private data of tsadc chip
7071
* @chn_offset: the channel offset of the first channel
7172
* @chn_num: the channel number of tsadc chip
72-
* @tshut_temp: the hardware-controlled shutdown temperature value
73+
* @trim_slope: used to convert the trim code to a temperature in millicelsius
74+
* @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
7375
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
7476
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
7577
* @initialize: SoC special initialize tsadc controller method
7678
* @irq_ack: clear the interrupt
7779
* @control: enable/disable method for the tsadc controller
78-
* @get_temp: get the temperature
80+
* @get_temp: get the raw temperature, unadjusted by trim
7981
* @set_alarm_temp: set the high temperature interrupt
8082
* @set_tshut_temp: set the hardware-controlled shutdown temperature
8183
* @set_tshut_mode: set the hardware-controlled shutdown mode
84+
* @get_trim_code: convert a hardware temperature code to one adjusted for by trim
8285
* @table: the chip-specific conversion table
8386
*/
8487
struct rockchip_tsadc_chip {
8588
/* The sensor id of chip correspond to the ADC channel */
8689
int chn_offset;
8790
int chn_num;
8891

92+
/* Used to convert trim code to trim temp */
93+
int trim_slope;
94+
8995
/* The hardware-controlled tshut property */
9096
int tshut_temp;
9197
enum tshut_mode tshut_mode;
@@ -105,6 +111,8 @@ struct rockchip_tsadc_chip {
105111
int (*set_tshut_temp)(const struct chip_tsadc_table *table,
106112
int chn, void __iomem *reg, int temp);
107113
void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
114+
int (*get_trim_code)(const struct chip_tsadc_table *table,
115+
int code, int trim_base, int trim_base_frac);
108116

109117
/* Per-table methods */
110118
struct chip_tsadc_table table;
@@ -114,12 +122,16 @@ struct rockchip_tsadc_chip {
114122
* struct rockchip_thermal_sensor - hold the information of thermal sensor
115123
* @thermal: pointer to the platform/configuration data
116124
* @tzd: pointer to a thermal zone
125+
* @of_node: pointer to the device_node representing this sensor, if any
117126
* @id: identifier of the thermal sensor
127+
* @trim_temp: per-sensor trim temperature value
118128
*/
119129
struct rockchip_thermal_sensor {
120130
struct rockchip_thermal_data *thermal;
121131
struct thermal_zone_device *tzd;
132+
struct device_node *of_node;
122133
int id;
134+
int trim_temp;
123135
};
124136

125137
/**
@@ -132,7 +144,11 @@ struct rockchip_thermal_sensor {
132144
* @pclk: the advanced peripherals bus clock
133145
* @grf: the general register file will be used to do static set by software
134146
* @regs: the base address of tsadc controller
135-
* @tshut_temp: the hardware-controlled shutdown temperature value
147+
* @trim_base: major component of sensor trim value, in Celsius
148+
* @trim_base_frac: minor component of sensor trim value, in Decicelsius
149+
* @trim: fallback thermal trim value for each channel
150+
* @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
151+
* @trim_temp: the fallback trim temperature for the whole sensor
136152
* @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
137153
* @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
138154
*/
@@ -149,7 +165,12 @@ struct rockchip_thermal_data {
149165
struct regmap *grf;
150166
void __iomem *regs;
151167

168+
int trim_base;
169+
int trim_base_frac;
170+
int trim;
171+
152172
int tshut_temp;
173+
int trim_temp;
153174
enum tshut_mode tshut_mode;
154175
enum tshut_polarity tshut_polarity;
155176
};
@@ -249,6 +270,9 @@ struct rockchip_thermal_data {
249270

250271
#define GRF_CON_TSADC_CH_INV (0x10001 << 1)
251272

273+
274+
#define RK_MAX_TEMP (180000)
275+
252276
/**
253277
* struct tsadc_table - code to temperature conversion table
254278
* @code: the value of adc channel
@@ -1061,6 +1085,15 @@ static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs,
10611085
writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN);
10621086
}
10631087

1088+
static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table,
1089+
int code, int trim_base, int trim_base_frac)
1090+
{
1091+
int temp = trim_base * 1000 + trim_base_frac * 100;
1092+
u32 base_code = rk_tsadcv2_temp_to_code(table, temp);
1093+
1094+
return code - base_code;
1095+
}
1096+
10641097
static const struct rockchip_tsadc_chip px30_tsadc_data = {
10651098
/* cpu, gpu */
10661099
.chn_offset = 0,
@@ -1298,6 +1331,8 @@ static const struct rockchip_tsadc_chip rk3576_tsadc_data = {
12981331
.set_alarm_temp = rk_tsadcv3_alarm_temp,
12991332
.set_tshut_temp = rk_tsadcv3_tshut_temp,
13001333
.set_tshut_mode = rk_tsadcv4_tshut_mode,
1334+
.get_trim_code = rk_tsadcv2_get_trim_code,
1335+
.trim_slope = 923,
13011336
.table = {
13021337
.id = rk3588_code_table,
13031338
.length = ARRAY_SIZE(rk3588_code_table),
@@ -1413,7 +1448,7 @@ static int rockchip_thermal_set_trips(struct thermal_zone_device *tz, int low, i
14131448
__func__, sensor->id, low, high);
14141449

14151450
return tsadc->set_alarm_temp(&tsadc->table,
1416-
sensor->id, thermal->regs, high);
1451+
sensor->id, thermal->regs, high + sensor->trim_temp);
14171452
}
14181453

14191454
static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp)
@@ -1425,6 +1460,8 @@ static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_te
14251460

14261461
retval = tsadc->get_temp(&tsadc->table,
14271462
sensor->id, thermal->regs, out_temp);
1463+
*out_temp -= sensor->trim_temp;
1464+
14281465
return retval;
14291466
}
14301467

@@ -1433,6 +1470,104 @@ static const struct thermal_zone_device_ops rockchip_of_thermal_ops = {
14331470
.set_trips = rockchip_thermal_set_trips,
14341471
};
14351472

1473+
/**
1474+
* rockchip_get_efuse_value - read an OTP cell from a device node
1475+
* @np: pointer to the device node with the nvmem-cells property
1476+
* @cell_name: name of cell that should be read
1477+
* @value: pointer to where the read value will be placed
1478+
*
1479+
* Return: Negative errno on failure, during which *value will not be touched,
1480+
* or 0 on success.
1481+
*/
1482+
static int rockchip_get_efuse_value(struct device_node *np, const char *cell_name,
1483+
int *value)
1484+
{
1485+
struct nvmem_cell *cell;
1486+
int ret = 0;
1487+
size_t len;
1488+
u8 *buf;
1489+
int i;
1490+
1491+
cell = of_nvmem_cell_get(np, cell_name);
1492+
if (IS_ERR(cell))
1493+
return PTR_ERR(cell);
1494+
1495+
buf = nvmem_cell_read(cell, &len);
1496+
1497+
nvmem_cell_put(cell);
1498+
1499+
if (IS_ERR(buf))
1500+
return PTR_ERR(buf);
1501+
1502+
if (len > sizeof(*value)) {
1503+
ret = -ERANGE;
1504+
goto exit;
1505+
}
1506+
1507+
/* Copy with implicit endian conversion */
1508+
*value = 0;
1509+
for (i = 0; i < len; i++)
1510+
*value |= (int) buf[i] << (8 * i);
1511+
1512+
exit:
1513+
kfree(buf);
1514+
return ret;
1515+
}
1516+
1517+
static int rockchip_get_trim_configuration(struct device *dev, struct device_node *np,
1518+
struct rockchip_thermal_data *thermal)
1519+
{
1520+
const struct rockchip_tsadc_chip *tsadc = thermal->chip;
1521+
int trim_base = 0, trim_base_frac = 0, trim = 0;
1522+
int trim_code;
1523+
int ret;
1524+
1525+
thermal->trim_base = 0;
1526+
thermal->trim_base_frac = 0;
1527+
thermal->trim = 0;
1528+
1529+
if (!tsadc->get_trim_code)
1530+
return 0;
1531+
1532+
ret = rockchip_get_efuse_value(np, "trim_base", &trim_base);
1533+
if (ret < 0) {
1534+
if (ret == -ENOENT) {
1535+
trim_base = 30;
1536+
dev_dbg(dev, "trim_base is absent, defaulting to 30\n");
1537+
} else {
1538+
dev_err(dev, "failed reading nvmem value of trim_base: %pe\n",
1539+
ERR_PTR(ret));
1540+
return ret;
1541+
}
1542+
}
1543+
ret = rockchip_get_efuse_value(np, "trim_base_frac", &trim_base_frac);
1544+
if (ret < 0) {
1545+
if (ret == -ENOENT) {
1546+
dev_dbg(dev, "trim_base_frac is absent, defaulting to 0\n");
1547+
} else {
1548+
dev_err(dev, "failed reading nvmem value of trim_base_frac: %pe\n",
1549+
ERR_PTR(ret));
1550+
return ret;
1551+
}
1552+
}
1553+
thermal->trim_base = trim_base;
1554+
thermal->trim_base_frac = trim_base_frac;
1555+
1556+
/*
1557+
* If the tsadc node contains the trim property, then it is used in the
1558+
* absence of per-channel trim values
1559+
*/
1560+
if (!rockchip_get_efuse_value(np, "trim", &trim))
1561+
thermal->trim = trim;
1562+
if (trim) {
1563+
trim_code = tsadc->get_trim_code(&tsadc->table, trim,
1564+
trim_base, trim_base_frac);
1565+
thermal->trim_temp = thermal->chip->trim_slope * trim_code;
1566+
}
1567+
1568+
return 0;
1569+
}
1570+
14361571
static int rockchip_configure_from_dt(struct device *dev,
14371572
struct device_node *np,
14381573
struct rockchip_thermal_data *thermal)
@@ -1493,6 +1628,8 @@ static int rockchip_configure_from_dt(struct device *dev,
14931628
if (IS_ERR(thermal->grf))
14941629
dev_warn(dev, "Missing rockchip,grf property\n");
14951630

1631+
rockchip_get_trim_configuration(dev, np, thermal);
1632+
14961633
return 0;
14971634
}
14981635

@@ -1503,23 +1640,50 @@ rockchip_thermal_register_sensor(struct platform_device *pdev,
15031640
int id)
15041641
{
15051642
const struct rockchip_tsadc_chip *tsadc = thermal->chip;
1643+
struct device *dev = &pdev->dev;
1644+
int trim = thermal->trim;
1645+
int trim_code, tshut_temp;
1646+
int trim_temp = 0;
15061647
int error;
15071648

1649+
if (thermal->trim_temp)
1650+
trim_temp = thermal->trim_temp;
1651+
1652+
if (tsadc->get_trim_code && sensor->of_node) {
1653+
error = rockchip_get_efuse_value(sensor->of_node, "trim", &trim);
1654+
if (error < 0 && error != -ENOENT) {
1655+
dev_err(dev, "failed reading trim of sensor %d: %pe\n",
1656+
id, ERR_PTR(error));
1657+
return error;
1658+
}
1659+
if (trim) {
1660+
trim_code = tsadc->get_trim_code(&tsadc->table, trim,
1661+
thermal->trim_base,
1662+
thermal->trim_base_frac);
1663+
trim_temp = thermal->chip->trim_slope * trim_code;
1664+
}
1665+
}
1666+
1667+
sensor->trim_temp = trim_temp;
1668+
1669+
dev_dbg(dev, "trim of sensor %d is %d\n", id, sensor->trim_temp);
1670+
1671+
tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, RK_MAX_TEMP);
1672+
15081673
tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode);
15091674

1510-
error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs,
1511-
thermal->tshut_temp);
1675+
error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, tshut_temp);
15121676
if (error)
1513-
dev_err(&pdev->dev, "%s: invalid tshut=%d, error=%d\n",
1514-
__func__, thermal->tshut_temp, error);
1677+
dev_err(dev, "%s: invalid tshut=%d, error=%d\n",
1678+
__func__, tshut_temp, error);
15151679

15161680
sensor->thermal = thermal;
15171681
sensor->id = id;
1518-
sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, id, sensor,
1682+
sensor->tzd = devm_thermal_of_zone_register(dev, id, sensor,
15191683
&rockchip_of_thermal_ops);
15201684
if (IS_ERR(sensor->tzd)) {
15211685
error = PTR_ERR(sensor->tzd);
1522-
dev_err(&pdev->dev, "failed to register sensor %d: %d\n",
1686+
dev_err(dev, "failed to register sensor %d: %d\n",
15231687
id, error);
15241688
return error;
15251689
}
@@ -1542,9 +1706,11 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
15421706
{
15431707
struct device_node *np = pdev->dev.of_node;
15441708
struct rockchip_thermal_data *thermal;
1709+
struct device_node *child;
15451710
int irq;
15461711
int i;
15471712
int error;
1713+
u32 chn;
15481714

15491715
irq = platform_get_irq(pdev, 0);
15501716
if (irq < 0)
@@ -1595,6 +1761,18 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
15951761
thermal->chip->initialize(thermal->grf, thermal->regs,
15961762
thermal->tshut_polarity);
15971763

1764+
for_each_available_child_of_node(np, child) {
1765+
if (!of_property_read_u32(child, "reg", &chn)) {
1766+
if (chn < thermal->chip->chn_num)
1767+
thermal->sensors[chn].of_node = child;
1768+
else
1769+
dev_warn(&pdev->dev,
1770+
"sensor address (%d) too large, ignoring its trim\n",
1771+
chn);
1772+
}
1773+
1774+
}
1775+
15981776
for (i = 0; i < thermal->chip->chn_num; i++) {
15991777
error = rockchip_thermal_register_sensor(pdev, thermal,
16001778
&thermal->sensors[i],
@@ -1664,8 +1842,11 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev)
16641842
static int __maybe_unused rockchip_thermal_resume(struct device *dev)
16651843
{
16661844
struct rockchip_thermal_data *thermal = dev_get_drvdata(dev);
1667-
int i;
1845+
const struct rockchip_tsadc_chip *tsadc = thermal->chip;
1846+
struct rockchip_thermal_sensor *sensor;
1847+
int tshut_temp;
16681848
int error;
1849+
int i;
16691850

16701851
error = clk_enable(thermal->clk);
16711852
if (error)
@@ -1679,21 +1860,23 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
16791860

16801861
rockchip_thermal_reset_controller(thermal->reset);
16811862

1682-
thermal->chip->initialize(thermal->grf, thermal->regs,
1683-
thermal->tshut_polarity);
1863+
tsadc->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity);
16841864

16851865
for (i = 0; i < thermal->chip->chn_num; i++) {
1686-
int id = thermal->sensors[i].id;
1866+
sensor = &thermal->sensors[i];
1867+
1868+
tshut_temp = min(thermal->tshut_temp + sensor->trim_temp,
1869+
RK_MAX_TEMP);
16871870

1688-
thermal->chip->set_tshut_mode(id, thermal->regs,
1871+
tsadc->set_tshut_mode(sensor->id, thermal->regs,
16891872
thermal->tshut_mode);
16901873

1691-
error = thermal->chip->set_tshut_temp(&thermal->chip->table,
1692-
id, thermal->regs,
1693-
thermal->tshut_temp);
1874+
error = tsadc->set_tshut_temp(&thermal->chip->table,
1875+
sensor->id, thermal->regs,
1876+
tshut_temp);
16941877
if (error)
16951878
dev_err(dev, "%s: invalid tshut=%d, error=%d\n",
1696-
__func__, thermal->tshut_temp, error);
1879+
__func__, tshut_temp, error);
16971880
}
16981881

16991882
thermal->chip->control(thermal->regs, true);

0 commit comments

Comments
 (0)