xref: /linux/drivers/hwmon/sa67mcu-hwmon.c (revision 443b39c82c322c9f3c38bea0389fe927ba00b3b4)
1*443b39c8SMichael Walle // SPDX-License-Identifier: GPL-2.0-only
2*443b39c8SMichael Walle /*
3*443b39c8SMichael Walle  * sl67mcu hardware monitoring driver
4*443b39c8SMichael Walle  *
5*443b39c8SMichael Walle  * Copyright 2025 Kontron Europe GmbH
6*443b39c8SMichael Walle  */
7*443b39c8SMichael Walle 
8*443b39c8SMichael Walle #include <linux/bitfield.h>
9*443b39c8SMichael Walle #include <linux/hwmon.h>
10*443b39c8SMichael Walle #include <linux/kernel.h>
11*443b39c8SMichael Walle #include <linux/mod_devicetable.h>
12*443b39c8SMichael Walle #include <linux/module.h>
13*443b39c8SMichael Walle #include <linux/platform_device.h>
14*443b39c8SMichael Walle #include <linux/property.h>
15*443b39c8SMichael Walle #include <linux/regmap.h>
16*443b39c8SMichael Walle 
17*443b39c8SMichael Walle #define SA67MCU_VOLTAGE(n)	(0x00 + ((n) * 2))
18*443b39c8SMichael Walle #define SA67MCU_TEMP(n)		(0x04 + ((n) * 2))
19*443b39c8SMichael Walle 
20*443b39c8SMichael Walle struct sa67mcu_hwmon {
21*443b39c8SMichael Walle 	struct regmap *regmap;
22*443b39c8SMichael Walle 	u32 offset;
23*443b39c8SMichael Walle };
24*443b39c8SMichael Walle 
25*443b39c8SMichael Walle static int sa67mcu_hwmon_read(struct device *dev,
26*443b39c8SMichael Walle 			      enum hwmon_sensor_types type, u32 attr,
27*443b39c8SMichael Walle 			      int channel, long *input)
28*443b39c8SMichael Walle {
29*443b39c8SMichael Walle 	struct sa67mcu_hwmon *hwmon = dev_get_drvdata(dev);
30*443b39c8SMichael Walle 	unsigned int offset;
31*443b39c8SMichael Walle 	u8 reg[2];
32*443b39c8SMichael Walle 	int ret;
33*443b39c8SMichael Walle 
34*443b39c8SMichael Walle 	switch (type) {
35*443b39c8SMichael Walle 	case hwmon_in:
36*443b39c8SMichael Walle 		switch (attr) {
37*443b39c8SMichael Walle 		case hwmon_in_input:
38*443b39c8SMichael Walle 			offset = hwmon->offset + SA67MCU_VOLTAGE(channel);
39*443b39c8SMichael Walle 			break;
40*443b39c8SMichael Walle 		default:
41*443b39c8SMichael Walle 			return -EOPNOTSUPP;
42*443b39c8SMichael Walle 		}
43*443b39c8SMichael Walle 		break;
44*443b39c8SMichael Walle 	case hwmon_temp:
45*443b39c8SMichael Walle 		switch (attr) {
46*443b39c8SMichael Walle 		case hwmon_temp_input:
47*443b39c8SMichael Walle 			offset = hwmon->offset + SA67MCU_TEMP(channel);
48*443b39c8SMichael Walle 			break;
49*443b39c8SMichael Walle 		default:
50*443b39c8SMichael Walle 			return -EOPNOTSUPP;
51*443b39c8SMichael Walle 		}
52*443b39c8SMichael Walle 		break;
53*443b39c8SMichael Walle 	default:
54*443b39c8SMichael Walle 		return -EOPNOTSUPP;
55*443b39c8SMichael Walle 	}
56*443b39c8SMichael Walle 
57*443b39c8SMichael Walle 	/* Reading the low byte will capture the value */
58*443b39c8SMichael Walle 	ret = regmap_bulk_read(hwmon->regmap, offset, reg, ARRAY_SIZE(reg));
59*443b39c8SMichael Walle 	if (ret)
60*443b39c8SMichael Walle 		return ret;
61*443b39c8SMichael Walle 
62*443b39c8SMichael Walle 	*input = reg[1] << 8 | reg[0];
63*443b39c8SMichael Walle 
64*443b39c8SMichael Walle 	/* Temperatures are s16 and in 0.1degC steps. */
65*443b39c8SMichael Walle 	if (type == hwmon_temp)
66*443b39c8SMichael Walle 		*input = sign_extend32(*input, 15) * 100;
67*443b39c8SMichael Walle 
68*443b39c8SMichael Walle 	return 0;
69*443b39c8SMichael Walle }
70*443b39c8SMichael Walle 
71*443b39c8SMichael Walle static const struct hwmon_channel_info * const sa67mcu_hwmon_info[] = {
72*443b39c8SMichael Walle 	HWMON_CHANNEL_INFO(in,
73*443b39c8SMichael Walle 			   HWMON_I_INPUT | HWMON_I_LABEL,
74*443b39c8SMichael Walle 			   HWMON_I_INPUT | HWMON_I_LABEL),
75*443b39c8SMichael Walle 	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
76*443b39c8SMichael Walle 	NULL
77*443b39c8SMichael Walle };
78*443b39c8SMichael Walle 
79*443b39c8SMichael Walle static const char *const sa67mcu_hwmon_in_labels[] = {
80*443b39c8SMichael Walle 	"VDDIN",
81*443b39c8SMichael Walle 	"VDD_RTC",
82*443b39c8SMichael Walle };
83*443b39c8SMichael Walle 
84*443b39c8SMichael Walle static int sa67mcu_hwmon_read_string(struct device *dev,
85*443b39c8SMichael Walle 				     enum hwmon_sensor_types type, u32 attr,
86*443b39c8SMichael Walle 				     int channel, const char **str)
87*443b39c8SMichael Walle {
88*443b39c8SMichael Walle 	switch (type) {
89*443b39c8SMichael Walle 	case hwmon_in:
90*443b39c8SMichael Walle 		switch (attr) {
91*443b39c8SMichael Walle 		case hwmon_in_label:
92*443b39c8SMichael Walle 			*str = sa67mcu_hwmon_in_labels[channel];
93*443b39c8SMichael Walle 			return 0;
94*443b39c8SMichael Walle 		default:
95*443b39c8SMichael Walle 			return -EOPNOTSUPP;
96*443b39c8SMichael Walle 		}
97*443b39c8SMichael Walle 	default:
98*443b39c8SMichael Walle 		return -EOPNOTSUPP;
99*443b39c8SMichael Walle 	}
100*443b39c8SMichael Walle }
101*443b39c8SMichael Walle 
102*443b39c8SMichael Walle static const struct hwmon_ops sa67mcu_hwmon_ops = {
103*443b39c8SMichael Walle 	.visible = 0444,
104*443b39c8SMichael Walle 	.read = sa67mcu_hwmon_read,
105*443b39c8SMichael Walle 	.read_string = sa67mcu_hwmon_read_string,
106*443b39c8SMichael Walle };
107*443b39c8SMichael Walle 
108*443b39c8SMichael Walle static const struct hwmon_chip_info sa67mcu_hwmon_chip_info = {
109*443b39c8SMichael Walle 	.ops = &sa67mcu_hwmon_ops,
110*443b39c8SMichael Walle 	.info = sa67mcu_hwmon_info,
111*443b39c8SMichael Walle };
112*443b39c8SMichael Walle 
113*443b39c8SMichael Walle static int sa67mcu_hwmon_probe(struct platform_device *pdev)
114*443b39c8SMichael Walle {
115*443b39c8SMichael Walle 	struct sa67mcu_hwmon *hwmon;
116*443b39c8SMichael Walle 	struct device *hwmon_dev;
117*443b39c8SMichael Walle 	int ret;
118*443b39c8SMichael Walle 
119*443b39c8SMichael Walle 	if (!pdev->dev.parent)
120*443b39c8SMichael Walle 		return -ENODEV;
121*443b39c8SMichael Walle 
122*443b39c8SMichael Walle 	hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
123*443b39c8SMichael Walle 	if (!hwmon)
124*443b39c8SMichael Walle 		return -ENOMEM;
125*443b39c8SMichael Walle 
126*443b39c8SMichael Walle 	hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
127*443b39c8SMichael Walle 	if (!hwmon->regmap)
128*443b39c8SMichael Walle 		return -ENODEV;
129*443b39c8SMichael Walle 
130*443b39c8SMichael Walle 	ret = device_property_read_u32(&pdev->dev, "reg", &hwmon->offset);
131*443b39c8SMichael Walle 	if (ret)
132*443b39c8SMichael Walle 		return -EINVAL;
133*443b39c8SMichael Walle 
134*443b39c8SMichael Walle 	hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
135*443b39c8SMichael Walle 							 "sa67mcu_hwmon", hwmon,
136*443b39c8SMichael Walle 							 &sa67mcu_hwmon_chip_info,
137*443b39c8SMichael Walle 							 NULL);
138*443b39c8SMichael Walle 	if (IS_ERR(hwmon_dev))
139*443b39c8SMichael Walle 		dev_err(&pdev->dev, "failed to register as hwmon device");
140*443b39c8SMichael Walle 
141*443b39c8SMichael Walle 	return PTR_ERR_OR_ZERO(hwmon_dev);
142*443b39c8SMichael Walle }
143*443b39c8SMichael Walle 
144*443b39c8SMichael Walle static const struct of_device_id sa67mcu_hwmon_of_match[] = {
145*443b39c8SMichael Walle 	{ .compatible = "kontron,sa67mcu-hwmon", },
146*443b39c8SMichael Walle 	{}
147*443b39c8SMichael Walle };
148*443b39c8SMichael Walle MODULE_DEVICE_TABLE(of, sa67mcu_hwmon_of_match);
149*443b39c8SMichael Walle 
150*443b39c8SMichael Walle static struct platform_driver sa67mcu_hwmon_driver = {
151*443b39c8SMichael Walle 	.probe = sa67mcu_hwmon_probe,
152*443b39c8SMichael Walle 	.driver = {
153*443b39c8SMichael Walle 		.name = "sa67mcu-hwmon",
154*443b39c8SMichael Walle 		.of_match_table = sa67mcu_hwmon_of_match,
155*443b39c8SMichael Walle 	},
156*443b39c8SMichael Walle };
157*443b39c8SMichael Walle module_platform_driver(sa67mcu_hwmon_driver);
158*443b39c8SMichael Walle 
159*443b39c8SMichael Walle MODULE_DESCRIPTION("sa67mcu Hardware Monitoring Driver");
160*443b39c8SMichael Walle MODULE_AUTHOR("Michael Walle <mwalle@kernel.org>");
161*443b39c8SMichael Walle MODULE_LICENSE("GPL");
162