1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * SolidRun DPU driver for control plane
4 *
5 * Copyright (C) 2022-2023 SolidRun
6 *
7 * Author: Alvaro Karsz <alvaro.karsz@solid-run.com>
8 *
9 */
10#include <linux/hwmon.h>
11
12#include "snet_vdpa.h"
13
14/* Monitor offsets */
15#define SNET_MON_TMP0_IN_OFF      0x00
16#define SNET_MON_TMP0_MAX_OFF     0x08
17#define SNET_MON_TMP0_CRIT_OFF    0x10
18#define SNET_MON_TMP1_IN_OFF      0x18
19#define SNET_MON_TMP1_CRIT_OFF    0x20
20#define SNET_MON_CURR_IN_OFF      0x28
21#define SNET_MON_CURR_MAX_OFF     0x30
22#define SNET_MON_CURR_CRIT_OFF    0x38
23#define SNET_MON_PWR_IN_OFF       0x40
24#define SNET_MON_VOLT_IN_OFF      0x48
25#define SNET_MON_VOLT_CRIT_OFF    0x50
26#define SNET_MON_VOLT_LCRIT_OFF   0x58
27
28static void snet_hwmon_read_reg(struct psnet *psnet, u32 reg, long *out)
29{
30	*out = psnet_read64(psnet, psnet->cfg.hwmon_off + reg);
31}
32
33static umode_t snet_howmon_is_visible(const void *data,
34				      enum hwmon_sensor_types type,
35				      u32 attr, int channel)
36{
37	return 0444;
38}
39
40static int snet_howmon_read(struct device *dev, enum hwmon_sensor_types type,
41			    u32 attr, int channel, long *val)
42{
43	struct psnet *psnet = dev_get_drvdata(dev);
44	int ret = 0;
45
46	switch (type) {
47	case hwmon_in:
48		switch (attr) {
49		case hwmon_in_lcrit:
50			snet_hwmon_read_reg(psnet, SNET_MON_VOLT_LCRIT_OFF, val);
51			break;
52		case hwmon_in_crit:
53			snet_hwmon_read_reg(psnet, SNET_MON_VOLT_CRIT_OFF, val);
54			break;
55		case hwmon_in_input:
56			snet_hwmon_read_reg(psnet, SNET_MON_VOLT_IN_OFF, val);
57			break;
58		default:
59			ret = -EOPNOTSUPP;
60			break;
61		}
62		break;
63
64	case hwmon_power:
65		switch (attr) {
66		case hwmon_power_input:
67			snet_hwmon_read_reg(psnet, SNET_MON_PWR_IN_OFF, val);
68			break;
69
70		default:
71			ret = -EOPNOTSUPP;
72			break;
73		}
74		break;
75
76	case hwmon_curr:
77		switch (attr) {
78		case hwmon_curr_input:
79			snet_hwmon_read_reg(psnet, SNET_MON_CURR_IN_OFF, val);
80			break;
81		case hwmon_curr_max:
82			snet_hwmon_read_reg(psnet, SNET_MON_CURR_MAX_OFF, val);
83			break;
84		case hwmon_curr_crit:
85			snet_hwmon_read_reg(psnet, SNET_MON_CURR_CRIT_OFF, val);
86			break;
87		default:
88			ret = -EOPNOTSUPP;
89			break;
90		}
91		break;
92
93	case hwmon_temp:
94		switch (attr) {
95		case hwmon_temp_input:
96			if (channel == 0)
97				snet_hwmon_read_reg(psnet, SNET_MON_TMP0_IN_OFF, val);
98			else
99				snet_hwmon_read_reg(psnet, SNET_MON_TMP1_IN_OFF, val);
100			break;
101		case hwmon_temp_max:
102			if (channel == 0)
103				snet_hwmon_read_reg(psnet, SNET_MON_TMP0_MAX_OFF, val);
104			else
105				ret = -EOPNOTSUPP;
106			break;
107		case hwmon_temp_crit:
108			if (channel == 0)
109				snet_hwmon_read_reg(psnet, SNET_MON_TMP0_CRIT_OFF, val);
110			else
111				snet_hwmon_read_reg(psnet, SNET_MON_TMP1_CRIT_OFF, val);
112			break;
113
114		default:
115			ret = -EOPNOTSUPP;
116			break;
117		}
118		break;
119
120	default:
121		ret = -EOPNOTSUPP;
122		break;
123	}
124	return ret;
125}
126
127static int snet_hwmon_read_string(struct device *dev,
128				  enum hwmon_sensor_types type, u32 attr,
129				  int channel, const char **str)
130{
131	int ret = 0;
132
133	switch (type) {
134	case hwmon_in:
135		*str = "main_vin";
136		break;
137	case hwmon_power:
138		*str = "soc_pin";
139		break;
140	case hwmon_curr:
141		*str = "soc_iin";
142		break;
143	case hwmon_temp:
144		if (channel == 0)
145			*str = "power_stage_temp";
146		else
147			*str = "ic_junction_temp";
148		break;
149	default:
150		ret = -EOPNOTSUPP;
151		break;
152	}
153	return ret;
154}
155
156static const struct hwmon_ops snet_hwmon_ops = {
157	.is_visible = snet_howmon_is_visible,
158	.read = snet_howmon_read,
159	.read_string = snet_hwmon_read_string
160};
161
162static const struct hwmon_channel_info * const snet_hwmon_info[] = {
163	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_LABEL,
164			   HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL),
165	HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_LABEL),
166	HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | HWMON_C_LABEL),
167	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_CRIT | HWMON_I_LCRIT | HWMON_I_LABEL),
168			   NULL
169};
170
171static const struct hwmon_chip_info snet_hwmono_info = {
172	.ops = &snet_hwmon_ops,
173	.info = snet_hwmon_info,
174};
175
176/* Create an HW monitor device */
177void psnet_create_hwmon(struct pci_dev *pdev)
178{
179	struct device *hwmon;
180	struct psnet *psnet = pci_get_drvdata(pdev);
181
182	snprintf(psnet->hwmon_name, SNET_NAME_SIZE, "snet_%s", pci_name(pdev));
183	hwmon = devm_hwmon_device_register_with_info(&pdev->dev, psnet->hwmon_name, psnet,
184						     &snet_hwmono_info, NULL);
185	/* The monitor is not mandatory, Just alert user in case of an error */
186	if (IS_ERR(hwmon))
187		SNET_WARN(pdev, "Failed to create SNET hwmon, error %ld\n", PTR_ERR(hwmon));
188}
189