summaryrefslogtreecommitdiffstats
path: root/net/dsa/dsa.c
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 /net/dsa/dsa.c
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>
Diffstat (limited to 'net/dsa/dsa.c')
-rw-r--r--net/dsa/dsa.c131
1 files changed, 131 insertions, 0 deletions
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