1// SPDX-License-Identifier: GPL-2.0-only
2/* Atlantic Network Driver
3 *
4 * Copyright (C) 2014-2019 aQuantia Corporation
5 * Copyright (C) 2019-2020 Marvell International Ltd.
6 */
7
8/* File aq_drvinfo.c: Definition of common code for firmware info in sys.*/
9
10#include <linux/init.h>
11#include <linux/kobject.h>
12#include <linux/module.h>
13#include <linux/stat.h>
14#include <linux/string.h>
15#include <linux/hwmon.h>
16#include <linux/uaccess.h>
17
18#include "aq_drvinfo.h"
19#include "aq_nic.h"
20
21#if IS_REACHABLE(CONFIG_HWMON)
22static const char * const atl_temp_label[] = {
23	"PHY Temperature",
24	"MAC Temperature",
25};
26
27static int aq_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
28			 u32 attr, int channel, long *value)
29{
30	struct aq_nic_s *aq_nic = dev_get_drvdata(dev);
31	int err = 0;
32	int temp;
33
34	if (!aq_nic)
35		return -EIO;
36
37	if (type != hwmon_temp || attr != hwmon_temp_input)
38		return -EOPNOTSUPP;
39
40	switch (channel) {
41	case 0:
42		if (!aq_nic->aq_fw_ops->get_phy_temp)
43			return -EOPNOTSUPP;
44
45		err = aq_nic->aq_fw_ops->get_phy_temp(aq_nic->aq_hw, &temp);
46		*value = temp;
47		break;
48	case 1:
49		if (!aq_nic->aq_fw_ops->get_mac_temp &&
50		    !aq_nic->aq_hw_ops->hw_get_mac_temp)
51			return -EOPNOTSUPP;
52
53		if (aq_nic->aq_fw_ops->get_mac_temp)
54			err = aq_nic->aq_fw_ops->get_mac_temp(aq_nic->aq_hw, &temp);
55		else
56			err = aq_nic->aq_hw_ops->hw_get_mac_temp(aq_nic->aq_hw, &temp);
57		*value = temp;
58		break;
59	default:
60		return -EOPNOTSUPP;
61	}
62
63	return err;
64}
65
66static int aq_hwmon_read_string(struct device *dev,
67				enum hwmon_sensor_types type,
68				u32 attr, int channel, const char **str)
69{
70	struct aq_nic_s *aq_nic = dev_get_drvdata(dev);
71
72	if (!aq_nic)
73		return -EIO;
74
75	if (type != hwmon_temp || attr != hwmon_temp_label)
76		return -EOPNOTSUPP;
77
78	if (channel < ARRAY_SIZE(atl_temp_label))
79		*str = atl_temp_label[channel];
80	else
81		return -EOPNOTSUPP;
82
83	return 0;
84}
85
86static umode_t aq_hwmon_is_visible(const void *data,
87				   enum hwmon_sensor_types type,
88				   u32 attr, int channel)
89{
90	const struct aq_nic_s *nic = data;
91
92	if (type != hwmon_temp)
93		return 0;
94
95	if (channel == 0 && !nic->aq_fw_ops->get_phy_temp)
96		return 0;
97	else if (channel == 1 && !nic->aq_fw_ops->get_mac_temp &&
98		 !nic->aq_hw_ops->hw_get_mac_temp)
99		return 0;
100
101	switch (attr) {
102	case hwmon_temp_input:
103	case hwmon_temp_label:
104		return 0444;
105	default:
106		return 0;
107	}
108}
109
110static const struct hwmon_ops aq_hwmon_ops = {
111	.is_visible = aq_hwmon_is_visible,
112	.read = aq_hwmon_read,
113	.read_string = aq_hwmon_read_string,
114};
115
116static u32 aq_hwmon_temp_config[] = {
117	HWMON_T_INPUT | HWMON_T_LABEL,
118	HWMON_T_INPUT | HWMON_T_LABEL,
119	0,
120};
121
122static const struct hwmon_channel_info aq_hwmon_temp = {
123	.type = hwmon_temp,
124	.config = aq_hwmon_temp_config,
125};
126
127static const struct hwmon_channel_info *aq_hwmon_info[] = {
128	&aq_hwmon_temp,
129	NULL,
130};
131
132static const struct hwmon_chip_info aq_hwmon_chip_info = {
133	.ops = &aq_hwmon_ops,
134	.info = aq_hwmon_info,
135};
136
137int aq_drvinfo_init(struct net_device *ndev)
138{
139	struct aq_nic_s *aq_nic = netdev_priv(ndev);
140	struct device *dev = &aq_nic->pdev->dev;
141	struct device *hwmon_dev;
142	int err = 0;
143
144	hwmon_dev = devm_hwmon_device_register_with_info(dev,
145							 ndev->name,
146							 aq_nic,
147							 &aq_hwmon_chip_info,
148							 NULL);
149
150	if (IS_ERR(hwmon_dev))
151		err = PTR_ERR(hwmon_dev);
152
153	return err;
154}
155
156#else
157int aq_drvinfo_init(struct net_device *ndev) { return 0; }
158#endif
159