sparx5-temp.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /* Sparx5 SoC temperature sensor driver
  3. *
  4. * Copyright (C) 2020 Lars Povlsen <[email protected]>
  5. */
  6. #include <linux/bitfield.h>
  7. #include <linux/clk.h>
  8. #include <linux/hwmon.h>
  9. #include <linux/init.h>
  10. #include <linux/io.h>
  11. #include <linux/mod_devicetable.h>
  12. #include <linux/module.h>
  13. #include <linux/platform_device.h>
  14. #define TEMP_CTRL 0
  15. #define TEMP_CFG 4
  16. #define TEMP_CFG_CYCLES GENMASK(24, 15)
  17. #define TEMP_CFG_ENA BIT(0)
  18. #define TEMP_STAT 8
  19. #define TEMP_STAT_VALID BIT(12)
  20. #define TEMP_STAT_TEMP GENMASK(11, 0)
  21. struct s5_hwmon {
  22. void __iomem *base;
  23. struct clk *clk;
  24. };
  25. static void s5_temp_enable(struct s5_hwmon *hwmon)
  26. {
  27. u32 val = readl(hwmon->base + TEMP_CFG);
  28. u32 clk = clk_get_rate(hwmon->clk) / USEC_PER_SEC;
  29. val &= ~TEMP_CFG_CYCLES;
  30. val |= FIELD_PREP(TEMP_CFG_CYCLES, clk);
  31. val |= TEMP_CFG_ENA;
  32. writel(val, hwmon->base + TEMP_CFG);
  33. }
  34. static int s5_read(struct device *dev, enum hwmon_sensor_types type,
  35. u32 attr, int channel, long *temp)
  36. {
  37. struct s5_hwmon *hwmon = dev_get_drvdata(dev);
  38. int rc = 0, value;
  39. u32 stat;
  40. switch (attr) {
  41. case hwmon_temp_input:
  42. stat = readl_relaxed(hwmon->base + TEMP_STAT);
  43. if (!(stat & TEMP_STAT_VALID))
  44. return -EAGAIN;
  45. value = stat & TEMP_STAT_TEMP;
  46. /*
  47. * From register documentation:
  48. * Temp(C) = TEMP_SENSOR_STAT.TEMP / 4096 * 352.2 - 109.4
  49. */
  50. value = DIV_ROUND_CLOSEST(value * 3522, 4096) - 1094;
  51. /*
  52. * Scale down by 10 from above and multiply by 1000 to
  53. * have millidegrees as specified by the hwmon sysfs
  54. * interface.
  55. */
  56. value *= 100;
  57. *temp = value;
  58. break;
  59. default:
  60. rc = -EOPNOTSUPP;
  61. break;
  62. }
  63. return rc;
  64. }
  65. static umode_t s5_is_visible(const void *_data, enum hwmon_sensor_types type,
  66. u32 attr, int channel)
  67. {
  68. if (type != hwmon_temp)
  69. return 0;
  70. switch (attr) {
  71. case hwmon_temp_input:
  72. return 0444;
  73. default:
  74. return 0;
  75. }
  76. }
  77. static const struct hwmon_channel_info *s5_info[] = {
  78. HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
  79. HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
  80. NULL
  81. };
  82. static const struct hwmon_ops s5_hwmon_ops = {
  83. .is_visible = s5_is_visible,
  84. .read = s5_read,
  85. };
  86. static const struct hwmon_chip_info s5_chip_info = {
  87. .ops = &s5_hwmon_ops,
  88. .info = s5_info,
  89. };
  90. static int s5_temp_probe(struct platform_device *pdev)
  91. {
  92. struct device *hwmon_dev;
  93. struct s5_hwmon *hwmon;
  94. hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
  95. if (!hwmon)
  96. return -ENOMEM;
  97. hwmon->base = devm_platform_ioremap_resource(pdev, 0);
  98. if (IS_ERR(hwmon->base))
  99. return PTR_ERR(hwmon->base);
  100. hwmon->clk = devm_clk_get_enabled(&pdev->dev, NULL);
  101. if (IS_ERR(hwmon->clk))
  102. return PTR_ERR(hwmon->clk);
  103. s5_temp_enable(hwmon);
  104. hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
  105. "s5_temp",
  106. hwmon,
  107. &s5_chip_info,
  108. NULL);
  109. return PTR_ERR_OR_ZERO(hwmon_dev);
  110. }
  111. static const struct of_device_id s5_temp_match[] = {
  112. { .compatible = "microchip,sparx5-temp" },
  113. {},
  114. };
  115. MODULE_DEVICE_TABLE(of, s5_temp_match);
  116. static struct platform_driver s5_temp_driver = {
  117. .probe = s5_temp_probe,
  118. .driver = {
  119. .name = "sparx5-temp",
  120. .of_match_table = s5_temp_match,
  121. },
  122. };
  123. module_platform_driver(s5_temp_driver);
  124. MODULE_AUTHOR("Lars Povlsen <[email protected]>");
  125. MODULE_DESCRIPTION("Sparx5 SoC temperature sensor driver");
  126. MODULE_LICENSE("GPL");