diff options
author | Guenter Roeck <linux@roeck-us.net> | 2014-10-29 13:44:58 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-10-30 14:54:11 -0400 |
commit | 51579c3f1a9192b75365576227d40c7619493285 (patch) | |
tree | e8609bf267a534ab1e5ea4418e868e476a6f517a | |
parent | 2716777b4f21649fb907b4a4fb96e1c8d0a5ec16 (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.h | 16 | ||||
-rw-r--r-- | net/dsa/Kconfig | 11 | ||||
-rw-r--r-- | net/dsa/dsa.c | 131 |
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 | ||
247 | void register_switch_driver(struct dsa_switch_driver *type); | 263 | void 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 | ||
12 | if NET_DSA | 12 | if NET_DSA |
13 | 13 | ||
14 | config 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 |
15 | config NET_DSA_TAG_BRCM | 26 | config 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 | ||
22 | char dsa_driver_version[] = "0.1"; | 26 | char 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 | |||
82 | static 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 | } | ||
94 | static DEVICE_ATTR_RO(temp1_input); | ||
95 | |||
96 | static 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 | |||
109 | static 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 | } | ||
126 | static DEVICE_ATTR(temp1_max, S_IRUGO, temp1_max_show, temp1_max_store); | ||
127 | |||
128 | static 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 | } | ||
141 | static DEVICE_ATTR_RO(temp1_max_alarm); | ||
142 | |||
143 | static 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 | |||
150 | static 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 | |||
169 | static 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 **************************************************/ |
76 | static struct dsa_switch * | 178 | static 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 | ||
230 | out_free: | 357 | out_free: |
@@ -236,6 +363,10 @@ out: | |||
236 | 363 | ||
237 | static void dsa_switch_destroy(struct dsa_switch *ds) | 364 | static 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 |