Skip to content

Commit e492217

Browse files
lpovlsengroeck
authored andcommitted
hwmon: sparx5: Add Sparx5 SoC temperature driver
This patch adds a temperature sensor driver to the Sparx5 SoC. Signed-off-by: Lars Povlsen <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Guenter Roeck <[email protected]>
1 parent f552075 commit e492217

File tree

4 files changed

+212
-0
lines changed

4 files changed

+212
-0
lines changed

Documentation/hwmon/sparx5-temp.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.. SPDX-License-Identifier: GPL-2.0-only
2+
3+
Microchip SparX-5 SoC
4+
=====================
5+
6+
Supported chips:
7+
8+
* VSC7546, VSC7549, VSC755, VSC7556, and VSC7558 (Sparx5 series)
9+
10+
Prefix: 'sparx5-temp'
11+
12+
Addresses scanned: -
13+
14+
Datasheet: Provided by Microchip upon request and under NDA
15+
16+
Author: Lars Povlsen <[email protected]>
17+
18+
Description
19+
-----------
20+
21+
The Sparx5 SoC contains a temperature sensor based on the MR74060
22+
Moortec IP.
23+
24+
The sensor has a range of -40°C to +125°C and an accuracy of +/-5°C.
25+
26+
Sysfs entries
27+
-------------
28+
29+
The following attributes are supported.
30+
31+
======================= ========================================================
32+
temp1_input Die temperature (in millidegree Celsius.)
33+
======================= ========================================================

drivers/hwmon/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,16 @@ config SENSORS_I5K_AMB
525525
This driver can also be built as a module. If so, the module
526526
will be called i5k_amb.
527527

528+
config SENSORS_SPARX5
529+
tristate "Sparx5 SoC temperature sensor"
530+
depends on ARCH_SPARX5 || COMPILE_TEST
531+
help
532+
If you say yes here you get support for temperature monitoring
533+
with the Microchip Sparx5 SoC.
534+
535+
This driver can also be built as a module. If so, the module
536+
will be called sparx5-temp.
537+
528538
config SENSORS_F71805F
529539
tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG"
530540
depends on !PPC

drivers/hwmon/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ obj-$(CONFIG_SENSORS_SMM665) += smm665.o
168168
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
169169
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
170170
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
171+
obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
171172
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
172173
obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
173174
obj-$(CONFIG_SENSORS_TC74) += tc74.o

drivers/hwmon/sparx5-temp.c

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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+
7+
#include <linux/bitfield.h>
8+
#include <linux/clk.h>
9+
#include <linux/hwmon.h>
10+
#include <linux/init.h>
11+
#include <linux/io.h>
12+
#include <linux/mod_devicetable.h>
13+
#include <linux/module.h>
14+
#include <linux/platform_device.h>
15+
16+
#define TEMP_CTRL 0
17+
#define TEMP_CFG 4
18+
#define TEMP_CFG_CYCLES GENMASK(24, 15)
19+
#define TEMP_CFG_ENA BIT(0)
20+
#define TEMP_STAT 8
21+
#define TEMP_STAT_VALID BIT(12)
22+
#define TEMP_STAT_TEMP GENMASK(11, 0)
23+
24+
struct s5_hwmon {
25+
void __iomem *base;
26+
struct clk *clk;
27+
};
28+
29+
static void s5_temp_clk_disable(void *data)
30+
{
31+
struct clk *clk = data;
32+
33+
clk_disable_unprepare(clk);
34+
}
35+
36+
static void s5_temp_enable(struct s5_hwmon *hwmon)
37+
{
38+
u32 val = readl(hwmon->base + TEMP_CFG);
39+
u32 clk = clk_get_rate(hwmon->clk) / USEC_PER_SEC;
40+
41+
val &= ~TEMP_CFG_CYCLES;
42+
val |= FIELD_PREP(TEMP_CFG_CYCLES, clk);
43+
val |= TEMP_CFG_ENA;
44+
45+
writel(val, hwmon->base + TEMP_CFG);
46+
}
47+
48+
static int s5_read(struct device *dev, enum hwmon_sensor_types type,
49+
u32 attr, int channel, long *temp)
50+
{
51+
struct s5_hwmon *hwmon = dev_get_drvdata(dev);
52+
int rc = 0, value;
53+
u32 stat;
54+
55+
switch (attr) {
56+
case hwmon_temp_input:
57+
stat = readl_relaxed(hwmon->base + TEMP_STAT);
58+
if (!(stat & TEMP_STAT_VALID))
59+
return -EIO;
60+
value = stat & TEMP_STAT_TEMP;
61+
/*
62+
* From register documentation:
63+
* Temp(C) = TEMP_SENSOR_STAT.TEMP / 4096 * 352.2 - 109.4
64+
*/
65+
value = DIV_ROUND_CLOSEST(value * 3522, 4096) - 1094;
66+
/*
67+
* Scale down by 10 from above and multiply by 1000 to
68+
* have millidegrees as specified by the hwmon sysfs
69+
* interface.
70+
*/
71+
value *= 100;
72+
*temp = value;
73+
break;
74+
default:
75+
rc = -EOPNOTSUPP;
76+
break;
77+
}
78+
79+
return rc;
80+
}
81+
82+
static umode_t s5_is_visible(const void *_data, enum hwmon_sensor_types type,
83+
u32 attr, int channel)
84+
{
85+
if (type != hwmon_temp)
86+
return 0;
87+
88+
switch (attr) {
89+
case hwmon_temp_input:
90+
return 0444;
91+
default:
92+
return 0;
93+
}
94+
}
95+
96+
static const struct hwmon_channel_info *s5_info[] = {
97+
HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
98+
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
99+
NULL
100+
};
101+
102+
static const struct hwmon_ops s5_hwmon_ops = {
103+
.is_visible = s5_is_visible,
104+
.read = s5_read,
105+
};
106+
107+
static const struct hwmon_chip_info s5_chip_info = {
108+
.ops = &s5_hwmon_ops,
109+
.info = s5_info,
110+
};
111+
112+
static int s5_temp_probe(struct platform_device *pdev)
113+
{
114+
struct device *hwmon_dev;
115+
struct s5_hwmon *hwmon;
116+
int ret;
117+
118+
hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
119+
if (!hwmon)
120+
return -ENOMEM;
121+
122+
hwmon->base = devm_platform_ioremap_resource(pdev, 0);
123+
if (IS_ERR(hwmon->base))
124+
return PTR_ERR(hwmon->base);
125+
126+
hwmon->clk = devm_clk_get(&pdev->dev, NULL);
127+
if (IS_ERR(hwmon->clk))
128+
return PTR_ERR(hwmon->clk);
129+
130+
ret = clk_prepare_enable(hwmon->clk);
131+
if (ret)
132+
return ret;
133+
134+
ret = devm_add_action_or_reset(&pdev->dev, s5_temp_clk_disable,
135+
hwmon->clk);
136+
if (ret)
137+
return ret;
138+
139+
s5_temp_enable(hwmon);
140+
141+
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
142+
"s5_temp",
143+
hwmon,
144+
&s5_chip_info,
145+
NULL);
146+
147+
return PTR_ERR_OR_ZERO(hwmon_dev);
148+
}
149+
150+
const struct of_device_id s5_temp_match[] = {
151+
{ .compatible = "microchip,sparx5-temp" },
152+
{},
153+
};
154+
MODULE_DEVICE_TABLE(of, s5_temp_match);
155+
156+
static struct platform_driver s5_temp_driver = {
157+
.probe = s5_temp_probe,
158+
.driver = {
159+
.name = "sparx5-temp",
160+
.of_match_table = s5_temp_match,
161+
},
162+
};
163+
164+
module_platform_driver(s5_temp_driver);
165+
166+
MODULE_AUTHOR("Lars Povlsen <[email protected]>");
167+
MODULE_DESCRIPTION("Sparx5 SoC temperature sensor driver");
168+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)