aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2014-10-29 13:44:58 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-30 14:54:11 -0400
commit51579c3f1a9192b75365576227d40c7619493285 (patch)
treee8609bf267a534ab1e5ea4418e868e476a6f517a
parent2716777b4f21649fb907b4a4fb96e1c8d0a5ec16 (diff)
net: dsa: Add support for reporting switch chip temperatures
Some switches provide chip temperature data. Add support for reporting it through the hwmon subsystem. Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/dsa.h16
-rw-r--r--net/dsa/Kconfig11
-rw-r--r--net/dsa/dsa.c131
3 files changed, 158 insertions, 0 deletions
diff --git a/include/net/dsa.h b/include/net/dsa.h
index b76559293535..55e75e7e8d41 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -139,6 +139,14 @@ struct dsa_switch {
139 */ 139 */
140 struct device *master_dev; 140 struct device *master_dev;
141 141
142#ifdef CONFIG_NET_DSA_HWMON
143 /*
144 * Hardware monitoring information
145 */
146 char hwmon_name[IFNAMSIZ + 8];
147 struct device *hwmon_dev;
148#endif
149
142 /* 150 /*
143 * Slave mii_bus and devices for the individual ports. 151 * Slave mii_bus and devices for the individual ports.
144 */ 152 */
@@ -242,6 +250,14 @@ struct dsa_switch_driver {
242 struct ethtool_eee *e); 250 struct ethtool_eee *e);
243 int (*get_eee)(struct dsa_switch *ds, int port, 251 int (*get_eee)(struct dsa_switch *ds, int port,
244 struct ethtool_eee *e); 252 struct ethtool_eee *e);
253
254#ifdef CONFIG_NET_DSA_HWMON
255 /* Hardware monitoring */
256 int (*get_temp)(struct dsa_switch *ds, int *temp);
257 int (*get_temp_limit)(struct dsa_switch *ds, int *temp);
258 int (*set_temp_limit)(struct dsa_switch *ds, int temp);
259 int (*get_temp_alarm)(struct dsa_switch *ds, bool *alarm);
260#endif
245}; 261};
246 262
247void register_switch_driver(struct dsa_switch_driver *type); 263void register_switch_driver(struct dsa_switch_driver *type);
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index a585fd6352eb..5f8ac404535b 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -11,6 +11,17 @@ config NET_DSA
11 11
12if NET_DSA 12if NET_DSA
13 13
14config NET_DSA_HWMON
15 bool "Distributed Switch Architecture HWMON support"
16 default y
17 depends on HWMON && !(NET_DSA=y && HWMON=m)
18 ---help---
19 Say Y if you want to expose thermal sensor data on switches supported
20 by the Distributed Switch Architecture.
21
22 Some of those switches contain thermal sensors. This data is available
23 via the hwmon sysfs interface and exposes the onboard sensors.
24
14# tagging formats 25# tagging formats
15config NET_DSA_TAG_BRCM 26config NET_DSA_TAG_BRCM
16 bool 27 bool
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 22f34cf4cb27..5edbbca89f1f 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -9,6 +9,9 @@
9 * (at your option) any later version. 9 * (at your option) any later version.
10 */ 10 */
11 11
12#include <linux/ctype.h>
13#include <linux/device.h>
14#include <linux/hwmon.h>
12#include <linux/list.h> 15#include <linux/list.h>
13#include <linux/platform_device.h> 16#include <linux/platform_device.h>
14#include <linux/slab.h> 17#include <linux/slab.h>
@@ -17,6 +20,7 @@
17#include <linux/of.h> 20#include <linux/of.h>
18#include <linux/of_mdio.h> 21#include <linux/of_mdio.h>
19#include <linux/of_platform.h> 22#include <linux/of_platform.h>
23#include <linux/sysfs.h>
20#include "dsa_priv.h" 24#include "dsa_priv.h"
21 25
22char dsa_driver_version[] = "0.1"; 26char dsa_driver_version[] = "0.1";
@@ -71,6 +75,104 @@ dsa_switch_probe(struct device *host_dev, int sw_addr, char **_name)
71 return ret; 75 return ret;
72} 76}
73 77
78/* hwmon support ************************************************************/
79
80#ifdef CONFIG_NET_DSA_HWMON
81
82static ssize_t temp1_input_show(struct device *dev,
83 struct device_attribute *attr, char *buf)
84{
85 struct dsa_switch *ds = dev_get_drvdata(dev);
86 int temp, ret;
87
88 ret = ds->drv->get_temp(ds, &temp);
89 if (ret < 0)
90 return ret;
91
92 return sprintf(buf, "%d\n", temp * 1000);
93}
94static DEVICE_ATTR_RO(temp1_input);
95
96static ssize_t temp1_max_show(struct device *dev,
97 struct device_attribute *attr, char *buf)
98{
99 struct dsa_switch *ds = dev_get_drvdata(dev);
100 int temp, ret;
101
102 ret = ds->drv->get_temp_limit(ds, &temp);
103 if (ret < 0)
104 return ret;
105
106 return sprintf(buf, "%d\n", temp * 1000);
107}
108
109static ssize_t temp1_max_store(struct device *dev,
110 struct device_attribute *attr, const char *buf,
111 size_t count)
112{
113 struct dsa_switch *ds = dev_get_drvdata(dev);
114 int temp, ret;
115
116 ret = kstrtoint(buf, 0, &temp);
117 if (ret < 0)
118 return ret;
119
120 ret = ds->drv->set_temp_limit(ds, DIV_ROUND_CLOSEST(temp, 1000));
121 if (ret < 0)
122 return ret;
123
124 return count;
125}
126static DEVICE_ATTR(temp1_max, S_IRUGO, temp1_max_show, temp1_max_store);
127
128static ssize_t temp1_max_alarm_show(struct device *dev,
129 struct device_attribute *attr, char *buf)
130{
131 struct dsa_switch *ds = dev_get_drvdata(dev);
132 bool alarm;
133 int ret;
134
135 ret = ds->drv->get_temp_alarm(ds, &alarm);
136 if (ret < 0)
137 return ret;
138
139 return sprintf(buf, "%d\n", alarm);
140}
141static DEVICE_ATTR_RO(temp1_max_alarm);
142
143static struct attribute *dsa_hwmon_attrs[] = {
144 &dev_attr_temp1_input.attr, /* 0 */
145 &dev_attr_temp1_max.attr, /* 1 */
146 &dev_attr_temp1_max_alarm.attr, /* 2 */
147 NULL
148};
149
150static umode_t dsa_hwmon_attrs_visible(struct kobject *kobj,
151 struct attribute *attr, int index)
152{
153 struct device *dev = container_of(kobj, struct device, kobj);
154 struct dsa_switch *ds = dev_get_drvdata(dev);
155 struct dsa_switch_driver *drv = ds->drv;
156 umode_t mode = attr->mode;
157
158 if (index == 1) {
159 if (!drv->get_temp_limit)
160 mode = 0;
161 else if (drv->set_temp_limit)
162 mode |= S_IWUSR;
163 } else if (index == 2 && !drv->get_temp_alarm) {
164 mode = 0;
165 }
166 return mode;
167}
168
169static const struct attribute_group dsa_hwmon_group = {
170 .attrs = dsa_hwmon_attrs,
171 .is_visible = dsa_hwmon_attrs_visible,
172};
173__ATTRIBUTE_GROUPS(dsa_hwmon);
174
175#endif /* CONFIG_NET_DSA_HWMON */
74 176
75/* basic switch operations **************************************************/ 177/* basic switch operations **************************************************/
76static struct dsa_switch * 178static struct dsa_switch *
@@ -225,6 +327,31 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
225 ds->ports[i] = slave_dev; 327 ds->ports[i] = slave_dev;
226 } 328 }
227 329
330#ifdef CONFIG_NET_DSA_HWMON
331 /* If the switch provides a temperature sensor,
332 * register with hardware monitoring subsystem.
333 * Treat registration error as non-fatal and ignore it.
334 */
335 if (drv->get_temp) {
336 const char *netname = netdev_name(dst->master_netdev);
337 char hname[IFNAMSIZ + 1];
338 int i, j;
339
340 /* Create valid hwmon 'name' attribute */
341 for (i = j = 0; i < IFNAMSIZ && netname[i]; i++) {
342 if (isalnum(netname[i]))
343 hname[j++] = netname[i];
344 }
345 hname[j] = '\0';
346 scnprintf(ds->hwmon_name, sizeof(ds->hwmon_name), "%s_dsa%d",
347 hname, index);
348 ds->hwmon_dev = hwmon_device_register_with_groups(NULL,
349 ds->hwmon_name, ds, dsa_hwmon_groups);
350 if (IS_ERR(ds->hwmon_dev))
351 ds->hwmon_dev = NULL;
352 }
353#endif /* CONFIG_NET_DSA_HWMON */
354
228 return ds; 355 return ds;
229 356
230out_free: 357out_free:
@@ -236,6 +363,10 @@ out:
236 363
237static void dsa_switch_destroy(struct dsa_switch *ds) 364static void dsa_switch_destroy(struct dsa_switch *ds)
238{ 365{
366#ifdef CONFIG_NET_DSA_HWMON
367 if (ds->hwmon_dev)
368 hwmon_device_unregister(ds->hwmon_dev);
369#endif
239} 370}
240 371
241#ifdef CONFIG_PM_SLEEP 372#ifdef CONFIG_PM_SLEEP