0001
0002
0003
0004
0005
0006
0007 #include <linux/bitfield.h>
0008 #include <linux/clk.h>
0009 #include <linux/hwmon.h>
0010 #include <linux/init.h>
0011 #include <linux/io.h>
0012 #include <linux/mod_devicetable.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_device.h>
0015
0016 #define TEMP_CTRL 0
0017 #define TEMP_CFG 4
0018 #define TEMP_CFG_CYCLES GENMASK(24, 15)
0019 #define TEMP_CFG_ENA BIT(0)
0020 #define TEMP_STAT 8
0021 #define TEMP_STAT_VALID BIT(12)
0022 #define TEMP_STAT_TEMP GENMASK(11, 0)
0023
0024 struct s5_hwmon {
0025 void __iomem *base;
0026 struct clk *clk;
0027 };
0028
0029 static void s5_temp_clk_disable(void *data)
0030 {
0031 struct clk *clk = data;
0032
0033 clk_disable_unprepare(clk);
0034 }
0035
0036 static void s5_temp_enable(struct s5_hwmon *hwmon)
0037 {
0038 u32 val = readl(hwmon->base + TEMP_CFG);
0039 u32 clk = clk_get_rate(hwmon->clk) / USEC_PER_SEC;
0040
0041 val &= ~TEMP_CFG_CYCLES;
0042 val |= FIELD_PREP(TEMP_CFG_CYCLES, clk);
0043 val |= TEMP_CFG_ENA;
0044
0045 writel(val, hwmon->base + TEMP_CFG);
0046 }
0047
0048 static int s5_read(struct device *dev, enum hwmon_sensor_types type,
0049 u32 attr, int channel, long *temp)
0050 {
0051 struct s5_hwmon *hwmon = dev_get_drvdata(dev);
0052 int rc = 0, value;
0053 u32 stat;
0054
0055 switch (attr) {
0056 case hwmon_temp_input:
0057 stat = readl_relaxed(hwmon->base + TEMP_STAT);
0058 if (!(stat & TEMP_STAT_VALID))
0059 return -EAGAIN;
0060 value = stat & TEMP_STAT_TEMP;
0061
0062
0063
0064
0065 value = DIV_ROUND_CLOSEST(value * 3522, 4096) - 1094;
0066
0067
0068
0069
0070
0071 value *= 100;
0072 *temp = value;
0073 break;
0074 default:
0075 rc = -EOPNOTSUPP;
0076 break;
0077 }
0078
0079 return rc;
0080 }
0081
0082 static umode_t s5_is_visible(const void *_data, enum hwmon_sensor_types type,
0083 u32 attr, int channel)
0084 {
0085 if (type != hwmon_temp)
0086 return 0;
0087
0088 switch (attr) {
0089 case hwmon_temp_input:
0090 return 0444;
0091 default:
0092 return 0;
0093 }
0094 }
0095
0096 static const struct hwmon_channel_info *s5_info[] = {
0097 HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
0098 HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
0099 NULL
0100 };
0101
0102 static const struct hwmon_ops s5_hwmon_ops = {
0103 .is_visible = s5_is_visible,
0104 .read = s5_read,
0105 };
0106
0107 static const struct hwmon_chip_info s5_chip_info = {
0108 .ops = &s5_hwmon_ops,
0109 .info = s5_info,
0110 };
0111
0112 static int s5_temp_probe(struct platform_device *pdev)
0113 {
0114 struct device *hwmon_dev;
0115 struct s5_hwmon *hwmon;
0116 int ret;
0117
0118 hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
0119 if (!hwmon)
0120 return -ENOMEM;
0121
0122 hwmon->base = devm_platform_ioremap_resource(pdev, 0);
0123 if (IS_ERR(hwmon->base))
0124 return PTR_ERR(hwmon->base);
0125
0126 hwmon->clk = devm_clk_get(&pdev->dev, NULL);
0127 if (IS_ERR(hwmon->clk))
0128 return PTR_ERR(hwmon->clk);
0129
0130 ret = clk_prepare_enable(hwmon->clk);
0131 if (ret)
0132 return ret;
0133
0134 ret = devm_add_action_or_reset(&pdev->dev, s5_temp_clk_disable,
0135 hwmon->clk);
0136 if (ret)
0137 return ret;
0138
0139 s5_temp_enable(hwmon);
0140
0141 hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
0142 "s5_temp",
0143 hwmon,
0144 &s5_chip_info,
0145 NULL);
0146
0147 return PTR_ERR_OR_ZERO(hwmon_dev);
0148 }
0149
0150 static const struct of_device_id s5_temp_match[] = {
0151 { .compatible = "microchip,sparx5-temp" },
0152 {},
0153 };
0154 MODULE_DEVICE_TABLE(of, s5_temp_match);
0155
0156 static struct platform_driver s5_temp_driver = {
0157 .probe = s5_temp_probe,
0158 .driver = {
0159 .name = "sparx5-temp",
0160 .of_match_table = s5_temp_match,
0161 },
0162 };
0163
0164 module_platform_driver(s5_temp_driver);
0165
0166 MODULE_AUTHOR("Lars Povlsen <lars.povlsen@microchip.com>");
0167 MODULE_DESCRIPTION("Sparx5 SoC temperature sensor driver");
0168 MODULE_LICENSE("GPL");