Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Nicolas Frattaroli and committed by
Daniel Lezcano
ae332ec0 75b98a2c

+202 -19
+202 -19
drivers/thermal/rockchip_thermal.c
··· 9 9 #include <linux/interrupt.h> 10 10 #include <linux/io.h> 11 11 #include <linux/module.h> 12 + #include <linux/nvmem-consumer.h> 12 13 #include <linux/of.h> 13 14 #include <linux/of_address.h> 14 15 #include <linux/of_irq.h> ··· 70 69 * struct rockchip_tsadc_chip - hold the private data of tsadc chip 71 70 * @chn_offset: the channel offset of the first channel 72 71 * @chn_num: the channel number of tsadc chip 73 - * @tshut_temp: the hardware-controlled shutdown temperature value 72 + * @trim_slope: used to convert the trim code to a temperature in millicelsius 73 + * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim 74 74 * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) 75 75 * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) 76 76 * @initialize: SoC special initialize tsadc controller method 77 77 * @irq_ack: clear the interrupt 78 78 * @control: enable/disable method for the tsadc controller 79 - * @get_temp: get the temperature 79 + * @get_temp: get the raw temperature, unadjusted by trim 80 80 * @set_alarm_temp: set the high temperature interrupt 81 81 * @set_tshut_temp: set the hardware-controlled shutdown temperature 82 82 * @set_tshut_mode: set the hardware-controlled shutdown mode 83 + * @get_trim_code: convert a hardware temperature code to one adjusted for by trim 83 84 * @table: the chip-specific conversion table 84 85 */ 85 86 struct rockchip_tsadc_chip { 86 87 /* The sensor id of chip correspond to the ADC channel */ 87 88 int chn_offset; 88 89 int chn_num; 90 + 91 + /* Used to convert trim code to trim temp */ 92 + int trim_slope; 89 93 90 94 /* The hardware-controlled tshut property */ 91 95 int tshut_temp; ··· 111 105 int (*set_tshut_temp)(const struct chip_tsadc_table *table, 112 106 int chn, void __iomem *reg, int temp); 113 107 void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m); 108 + int (*get_trim_code)(const struct chip_tsadc_table *table, 109 + int code, int trim_base, int trim_base_frac); 114 110 115 111 /* Per-table methods */ 116 112 struct chip_tsadc_table table; ··· 122 114 * struct rockchip_thermal_sensor - hold the information of thermal sensor 123 115 * @thermal: pointer to the platform/configuration data 124 116 * @tzd: pointer to a thermal zone 117 + * @of_node: pointer to the device_node representing this sensor, if any 125 118 * @id: identifier of the thermal sensor 119 + * @trim_temp: per-sensor trim temperature value 126 120 */ 127 121 struct rockchip_thermal_sensor { 128 122 struct rockchip_thermal_data *thermal; 129 123 struct thermal_zone_device *tzd; 124 + struct device_node *of_node; 130 125 int id; 126 + int trim_temp; 131 127 }; 132 128 133 129 /** ··· 144 132 * @pclk: the advanced peripherals bus clock 145 133 * @grf: the general register file will be used to do static set by software 146 134 * @regs: the base address of tsadc controller 147 - * @tshut_temp: the hardware-controlled shutdown temperature value 135 + * @trim_base: major component of sensor trim value, in Celsius 136 + * @trim_base_frac: minor component of sensor trim value, in Decicelsius 137 + * @trim: fallback thermal trim value for each channel 138 + * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim 139 + * @trim_temp: the fallback trim temperature for the whole sensor 148 140 * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) 149 141 * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) 150 142 */ ··· 165 149 struct regmap *grf; 166 150 void __iomem *regs; 167 151 152 + int trim_base; 153 + int trim_base_frac; 154 + int trim; 155 + 168 156 int tshut_temp; 157 + int trim_temp; 169 158 enum tshut_mode tshut_mode; 170 159 enum tshut_polarity tshut_polarity; 171 160 }; ··· 269 248 #define GRF_TSADC_VCM_EN_H (0x10001 << 7) 270 249 271 250 #define GRF_CON_TSADC_CH_INV (0x10001 << 1) 251 + 252 + 253 + #define RK_MAX_TEMP (180000) 272 254 273 255 /** 274 256 * struct tsadc_table - code to temperature conversion table ··· 1085 1061 writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN); 1086 1062 } 1087 1063 1064 + static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table, 1065 + int code, int trim_base, int trim_base_frac) 1066 + { 1067 + int temp = trim_base * 1000 + trim_base_frac * 100; 1068 + u32 base_code = rk_tsadcv2_temp_to_code(table, temp); 1069 + 1070 + return code - base_code; 1071 + } 1072 + 1088 1073 static const struct rockchip_tsadc_chip px30_tsadc_data = { 1089 1074 /* cpu, gpu */ 1090 1075 .chn_offset = 0, ··· 1331 1298 .set_alarm_temp = rk_tsadcv3_alarm_temp, 1332 1299 .set_tshut_temp = rk_tsadcv3_tshut_temp, 1333 1300 .set_tshut_mode = rk_tsadcv4_tshut_mode, 1301 + .get_trim_code = rk_tsadcv2_get_trim_code, 1302 + .trim_slope = 923, 1334 1303 .table = { 1335 1304 .id = rk3588_code_table, 1336 1305 .length = ARRAY_SIZE(rk3588_code_table), ··· 1448 1413 __func__, sensor->id, low, high); 1449 1414 1450 1415 return tsadc->set_alarm_temp(&tsadc->table, 1451 - sensor->id, thermal->regs, high); 1416 + sensor->id, thermal->regs, high + sensor->trim_temp); 1452 1417 } 1453 1418 1454 1419 static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp) ··· 1460 1425 1461 1426 retval = tsadc->get_temp(&tsadc->table, 1462 1427 sensor->id, thermal->regs, out_temp); 1428 + *out_temp -= sensor->trim_temp; 1429 + 1463 1430 return retval; 1464 1431 } 1465 1432 ··· 1469 1432 .get_temp = rockchip_thermal_get_temp, 1470 1433 .set_trips = rockchip_thermal_set_trips, 1471 1434 }; 1435 + 1436 + /** 1437 + * rockchip_get_efuse_value - read an OTP cell from a device node 1438 + * @np: pointer to the device node with the nvmem-cells property 1439 + * @cell_name: name of cell that should be read 1440 + * @value: pointer to where the read value will be placed 1441 + * 1442 + * Return: Negative errno on failure, during which *value will not be touched, 1443 + * or 0 on success. 1444 + */ 1445 + static int rockchip_get_efuse_value(struct device_node *np, const char *cell_name, 1446 + int *value) 1447 + { 1448 + struct nvmem_cell *cell; 1449 + int ret = 0; 1450 + size_t len; 1451 + u8 *buf; 1452 + int i; 1453 + 1454 + cell = of_nvmem_cell_get(np, cell_name); 1455 + if (IS_ERR(cell)) 1456 + return PTR_ERR(cell); 1457 + 1458 + buf = nvmem_cell_read(cell, &len); 1459 + 1460 + nvmem_cell_put(cell); 1461 + 1462 + if (IS_ERR(buf)) 1463 + return PTR_ERR(buf); 1464 + 1465 + if (len > sizeof(*value)) { 1466 + ret = -ERANGE; 1467 + goto exit; 1468 + } 1469 + 1470 + /* Copy with implicit endian conversion */ 1471 + *value = 0; 1472 + for (i = 0; i < len; i++) 1473 + *value |= (int) buf[i] << (8 * i); 1474 + 1475 + exit: 1476 + kfree(buf); 1477 + return ret; 1478 + } 1479 + 1480 + static int rockchip_get_trim_configuration(struct device *dev, struct device_node *np, 1481 + struct rockchip_thermal_data *thermal) 1482 + { 1483 + const struct rockchip_tsadc_chip *tsadc = thermal->chip; 1484 + int trim_base = 0, trim_base_frac = 0, trim = 0; 1485 + int trim_code; 1486 + int ret; 1487 + 1488 + thermal->trim_base = 0; 1489 + thermal->trim_base_frac = 0; 1490 + thermal->trim = 0; 1491 + 1492 + if (!tsadc->get_trim_code) 1493 + return 0; 1494 + 1495 + ret = rockchip_get_efuse_value(np, "trim_base", &trim_base); 1496 + if (ret < 0) { 1497 + if (ret == -ENOENT) { 1498 + trim_base = 30; 1499 + dev_dbg(dev, "trim_base is absent, defaulting to 30\n"); 1500 + } else { 1501 + dev_err(dev, "failed reading nvmem value of trim_base: %pe\n", 1502 + ERR_PTR(ret)); 1503 + return ret; 1504 + } 1505 + } 1506 + ret = rockchip_get_efuse_value(np, "trim_base_frac", &trim_base_frac); 1507 + if (ret < 0) { 1508 + if (ret == -ENOENT) { 1509 + dev_dbg(dev, "trim_base_frac is absent, defaulting to 0\n"); 1510 + } else { 1511 + dev_err(dev, "failed reading nvmem value of trim_base_frac: %pe\n", 1512 + ERR_PTR(ret)); 1513 + return ret; 1514 + } 1515 + } 1516 + thermal->trim_base = trim_base; 1517 + thermal->trim_base_frac = trim_base_frac; 1518 + 1519 + /* 1520 + * If the tsadc node contains the trim property, then it is used in the 1521 + * absence of per-channel trim values 1522 + */ 1523 + if (!rockchip_get_efuse_value(np, "trim", &trim)) 1524 + thermal->trim = trim; 1525 + if (trim) { 1526 + trim_code = tsadc->get_trim_code(&tsadc->table, trim, 1527 + trim_base, trim_base_frac); 1528 + thermal->trim_temp = thermal->chip->trim_slope * trim_code; 1529 + } 1530 + 1531 + return 0; 1532 + } 1472 1533 1473 1534 static int rockchip_configure_from_dt(struct device *dev, 1474 1535 struct device_node *np, ··· 1628 1493 if (IS_ERR(thermal->grf)) 1629 1494 dev_warn(dev, "Missing rockchip,grf property\n"); 1630 1495 1496 + rockchip_get_trim_configuration(dev, np, thermal); 1497 + 1631 1498 return 0; 1632 1499 } 1633 1500 ··· 1640 1503 int id) 1641 1504 { 1642 1505 const struct rockchip_tsadc_chip *tsadc = thermal->chip; 1506 + struct device *dev = &pdev->dev; 1507 + int trim = thermal->trim; 1508 + int trim_code, tshut_temp; 1509 + int trim_temp = 0; 1643 1510 int error; 1511 + 1512 + if (thermal->trim_temp) 1513 + trim_temp = thermal->trim_temp; 1514 + 1515 + if (tsadc->get_trim_code && sensor->of_node) { 1516 + error = rockchip_get_efuse_value(sensor->of_node, "trim", &trim); 1517 + if (error < 0 && error != -ENOENT) { 1518 + dev_err(dev, "failed reading trim of sensor %d: %pe\n", 1519 + id, ERR_PTR(error)); 1520 + return error; 1521 + } 1522 + if (trim) { 1523 + trim_code = tsadc->get_trim_code(&tsadc->table, trim, 1524 + thermal->trim_base, 1525 + thermal->trim_base_frac); 1526 + trim_temp = thermal->chip->trim_slope * trim_code; 1527 + } 1528 + } 1529 + 1530 + sensor->trim_temp = trim_temp; 1531 + 1532 + dev_dbg(dev, "trim of sensor %d is %d\n", id, sensor->trim_temp); 1533 + 1534 + tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, RK_MAX_TEMP); 1644 1535 1645 1536 tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode); 1646 1537 1647 - error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, 1648 - thermal->tshut_temp); 1538 + error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, tshut_temp); 1649 1539 if (error) 1650 - dev_err(&pdev->dev, "%s: invalid tshut=%d, error=%d\n", 1651 - __func__, thermal->tshut_temp, error); 1540 + dev_err(dev, "%s: invalid tshut=%d, error=%d\n", 1541 + __func__, tshut_temp, error); 1652 1542 1653 1543 sensor->thermal = thermal; 1654 1544 sensor->id = id; 1655 - sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, id, sensor, 1545 + sensor->tzd = devm_thermal_of_zone_register(dev, id, sensor, 1656 1546 &rockchip_of_thermal_ops); 1657 1547 if (IS_ERR(sensor->tzd)) { 1658 1548 error = PTR_ERR(sensor->tzd); 1659 - dev_err(&pdev->dev, "failed to register sensor %d: %d\n", 1549 + dev_err(dev, "failed to register sensor %d: %d\n", 1660 1550 id, error); 1661 1551 return error; 1662 1552 } ··· 1706 1542 { 1707 1543 struct device_node *np = pdev->dev.of_node; 1708 1544 struct rockchip_thermal_data *thermal; 1545 + struct device_node *child; 1709 1546 int irq; 1710 1547 int i; 1711 1548 int error; 1549 + u32 chn; 1712 1550 1713 1551 irq = platform_get_irq(pdev, 0); 1714 1552 if (irq < 0) ··· 1760 1594 1761 1595 thermal->chip->initialize(thermal->grf, thermal->regs, 1762 1596 thermal->tshut_polarity); 1597 + 1598 + for_each_available_child_of_node(np, child) { 1599 + if (!of_property_read_u32(child, "reg", &chn)) { 1600 + if (chn < thermal->chip->chn_num) 1601 + thermal->sensors[chn].of_node = child; 1602 + else 1603 + dev_warn(&pdev->dev, 1604 + "sensor address (%d) too large, ignoring its trim\n", 1605 + chn); 1606 + } 1607 + 1608 + } 1763 1609 1764 1610 for (i = 0; i < thermal->chip->chn_num; i++) { 1765 1611 error = rockchip_thermal_register_sensor(pdev, thermal, ··· 1842 1664 static int __maybe_unused rockchip_thermal_resume(struct device *dev) 1843 1665 { 1844 1666 struct rockchip_thermal_data *thermal = dev_get_drvdata(dev); 1845 - int i; 1667 + const struct rockchip_tsadc_chip *tsadc = thermal->chip; 1668 + struct rockchip_thermal_sensor *sensor; 1669 + int tshut_temp; 1846 1670 int error; 1671 + int i; 1847 1672 1848 1673 error = clk_enable(thermal->clk); 1849 1674 if (error) ··· 1860 1679 1861 1680 rockchip_thermal_reset_controller(thermal->reset); 1862 1681 1863 - thermal->chip->initialize(thermal->grf, thermal->regs, 1864 - thermal->tshut_polarity); 1682 + tsadc->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity); 1865 1683 1866 1684 for (i = 0; i < thermal->chip->chn_num; i++) { 1867 - int id = thermal->sensors[i].id; 1685 + sensor = &thermal->sensors[i]; 1868 1686 1869 - thermal->chip->set_tshut_mode(id, thermal->regs, 1687 + tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, 1688 + RK_MAX_TEMP); 1689 + 1690 + tsadc->set_tshut_mode(sensor->id, thermal->regs, 1870 1691 thermal->tshut_mode); 1871 1692 1872 - error = thermal->chip->set_tshut_temp(&thermal->chip->table, 1873 - id, thermal->regs, 1874 - thermal->tshut_temp); 1693 + error = tsadc->set_tshut_temp(&thermal->chip->table, 1694 + sensor->id, thermal->regs, 1695 + tshut_temp); 1875 1696 if (error) 1876 1697 dev_err(dev, "%s: invalid tshut=%d, error=%d\n", 1877 - __func__, thermal->tshut_temp, error); 1698 + __func__, tshut_temp, error); 1878 1699 } 1879 1700 1880 1701 thermal->chip->control(thermal->regs, true);