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

ASoC: tas2770: expose die temp to hwmon

TAS2770 includes an ADC which reports the chip's die temperature.
As per the datasheet, the temperature in degrees Celsius is derived
by taking the raw value stored in the ADC's registers, dividing by 16,
then subtracting 93.

Create and register a hwmon device to expose the chip's die temperature
to the hwmon interface.

The ADC is shut down during software shutdown mode, and its registers
are initialised to 0 on reset. This means that the die temperature will
read -93 *C until the chip has been fully powered up at least once (e.g.
the PCM its attached to is opened). Once the chip is put into software
shutdown again, the ADC will also shut down. The last value sampled
before this happens will persist in the ADC's registers.

Co-developed-by: Martin Povišer <povik+lin@cutebit.org>
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Link: https://patch.msgid.link/20250406-apple-codec-changes-v5-7-50a00ec850a3@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

James Calligeros and committed by
Mark Brown
ff73e278 f33b01e0

+96
+96
sound/soc/codecs/tas2770.c
··· 12 12 #include <linux/err.h> 13 13 #include <linux/init.h> 14 14 #include <linux/delay.h> 15 + #include <linux/hwmon.h> 15 16 #include <linux/pm.h> 16 17 #include <linux/i2c.h> 17 18 #include <linux/gpio/consumer.h> ··· 531 530 }, 532 531 }; 533 532 533 + static int tas2770_read_die_temp(struct tas2770_priv *tas2770, long *result) 534 + { 535 + int ret = 0; 536 + int reading, msb, lsb; 537 + 538 + ret = regmap_read(tas2770->regmap, TAS2770_TEMP_MSB, &msb); 539 + if (ret) 540 + return ret; 541 + 542 + ret = regmap_read(tas2770->regmap, TAS2770_TEMP_LSB, &lsb); 543 + if (ret) 544 + return ret; 545 + 546 + reading = (msb << 4) | (lsb >> 4); 547 + 548 + /* 549 + * As per datasheet: divide register by 16 and subtract 93 to get 550 + * degrees Celsius. hwmon requires millidegrees. Let's avoid rounding 551 + * errors by subtracting 93 * 16 then multiplying by 1000 / 16. 552 + * 553 + * NOTE: The ADC registers are initialised to 0 on reset. This means 554 + * that the temperature will read -93 *C until the chip is brought out 555 + * of software shutdown (e.g. the PCM it's attached to is opened). The 556 + * ADC is also shut down in software shutdown/low-power mode, so the 557 + * value read back from its registers will be the last value sampled 558 + * before entering software shutdown. 559 + */ 560 + *result = (reading - (93 * 16)) * (1000 / 16); 561 + return 0; 562 + } 563 + 564 + static umode_t tas2770_hwmon_is_visible(const void *data, 565 + enum hwmon_sensor_types type, u32 attr, 566 + int channel) 567 + { 568 + if (type != hwmon_temp) 569 + return 0; 570 + 571 + switch (attr) { 572 + case hwmon_temp_input: 573 + return 0444; 574 + default: 575 + break; 576 + } 577 + 578 + return 0; 579 + } 580 + 581 + static int tas2770_hwmon_read(struct device *dev, 582 + enum hwmon_sensor_types type, 583 + u32 attr, int channel, long *val) 584 + { 585 + struct tas2770_priv *tas2770 = dev_get_drvdata(dev); 586 + int ret; 587 + 588 + switch (attr) { 589 + case hwmon_temp_input: 590 + ret = tas2770_read_die_temp(tas2770, val); 591 + break; 592 + default: 593 + ret = -EOPNOTSUPP; 594 + break; 595 + } 596 + 597 + return ret; 598 + } 599 + 600 + static const struct hwmon_channel_info *const tas2770_hwmon_info[] = { 601 + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 602 + NULL 603 + }; 604 + 605 + static const struct hwmon_ops tas2770_hwmon_ops = { 606 + .is_visible = tas2770_hwmon_is_visible, 607 + .read = tas2770_hwmon_read, 608 + }; 609 + 610 + static const struct hwmon_chip_info tas2770_hwmon_chip_info = { 611 + .ops = &tas2770_hwmon_ops, 612 + .info = tas2770_hwmon_info, 613 + }; 614 + 534 615 static const struct regmap_config tas2770_i2c_regmap; 535 616 536 617 static int tas2770_codec_probe(struct snd_soc_component *component) ··· 838 755 if (PTR_ERR(tas2770->reset_gpio) == -EPROBE_DEFER) { 839 756 tas2770->reset_gpio = NULL; 840 757 return -EPROBE_DEFER; 758 + } 759 + } 760 + 761 + if (IS_REACHABLE(CONFIG_HWMON)) { 762 + struct device *hwmon; 763 + 764 + hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2770", 765 + tas2770, 766 + &tas2770_hwmon_chip_info, 767 + NULL); 768 + if (IS_ERR(hwmon)) { 769 + return dev_err_probe(&client->dev, PTR_ERR(hwmon), 770 + "Failed to register temp sensor\n"); 841 771 } 842 772 } 843 773