1 /*
2  * RapidIO sysfs attributes and support
3  *
4  * Copyright 2005 MontaVista Software, Inc.
5  * Matt Porter <mporter@kernel.crashing.org>
6  *
7  * This program is free software; you can redistribute  it and/or modify it
8  * under  the terms of  the GNU General  Public License as published by the
9  * Free Software Foundation;  either version 2 of the  License, or (at your
10  * option) any later version.
11  */
12 
13 #include <linux/kernel.h>
14 #include <linux/rio.h>
15 #include <linux/rio_drv.h>
16 #include <linux/stat.h>
17 #include <linux/capability.h>
18 
19 #include "rio.h"
20 
21 /* Sysfs support */
22 #define rio_config_attr(field, format_string)					\
23 static ssize_t								\
24 field##_show(struct device *dev, struct device_attribute *attr, char *buf)			\
25 {									\
26 	struct rio_dev *rdev = to_rio_dev(dev);				\
27 									\
28 	return sprintf(buf, format_string, rdev->field);		\
29 }									\
30 
31 rio_config_attr(did, "0x%04x\n");
32 rio_config_attr(vid, "0x%04x\n");
33 rio_config_attr(device_rev, "0x%08x\n");
34 rio_config_attr(asm_did, "0x%04x\n");
35 rio_config_attr(asm_vid, "0x%04x\n");
36 rio_config_attr(asm_rev, "0x%04x\n");
37 rio_config_attr(destid, "0x%04x\n");
38 rio_config_attr(hopcount, "0x%02x\n");
39 
routes_show(struct device * dev,struct device_attribute * attr,char * buf)40 static ssize_t routes_show(struct device *dev, struct device_attribute *attr, char *buf)
41 {
42 	struct rio_dev *rdev = to_rio_dev(dev);
43 	char *str = buf;
44 	int i;
45 
46 	for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size);
47 			i++) {
48 		if (rdev->rswitch->route_table[i] == RIO_INVALID_ROUTE)
49 			continue;
50 		str +=
51 		    sprintf(str, "%04x %02x\n", i,
52 			    rdev->rswitch->route_table[i]);
53 	}
54 
55 	return (str - buf);
56 }
57 
lprev_show(struct device * dev,struct device_attribute * attr,char * buf)58 static ssize_t lprev_show(struct device *dev,
59 			  struct device_attribute *attr, char *buf)
60 {
61 	struct rio_dev *rdev = to_rio_dev(dev);
62 
63 	return sprintf(buf, "%s\n",
64 			(rdev->prev) ? rio_name(rdev->prev) : "root");
65 }
66 
lnext_show(struct device * dev,struct device_attribute * attr,char * buf)67 static ssize_t lnext_show(struct device *dev,
68 			  struct device_attribute *attr, char *buf)
69 {
70 	struct rio_dev *rdev = to_rio_dev(dev);
71 	char *str = buf;
72 	int i;
73 
74 	if (rdev->pef & RIO_PEF_SWITCH) {
75 		for (i = 0; i < RIO_GET_TOTAL_PORTS(rdev->swpinfo); i++) {
76 			if (rdev->rswitch->nextdev[i])
77 				str += sprintf(str, "%s\n",
78 					rio_name(rdev->rswitch->nextdev[i]));
79 			else
80 				str += sprintf(str, "null\n");
81 		}
82 	}
83 
84 	return str - buf;
85 }
86 
87 struct device_attribute rio_dev_attrs[] = {
88 	__ATTR_RO(did),
89 	__ATTR_RO(vid),
90 	__ATTR_RO(device_rev),
91 	__ATTR_RO(asm_did),
92 	__ATTR_RO(asm_vid),
93 	__ATTR_RO(asm_rev),
94 	__ATTR_RO(lprev),
95 	__ATTR_RO(destid),
96 	__ATTR_NULL,
97 };
98 
99 static DEVICE_ATTR(routes, S_IRUGO, routes_show, NULL);
100 static DEVICE_ATTR(lnext, S_IRUGO, lnext_show, NULL);
101 static DEVICE_ATTR(hopcount, S_IRUGO, hopcount_show, NULL);
102 
103 static ssize_t
rio_read_config(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)104 rio_read_config(struct file *filp, struct kobject *kobj,
105 		struct bin_attribute *bin_attr,
106 		char *buf, loff_t off, size_t count)
107 {
108 	struct rio_dev *dev =
109 	    to_rio_dev(container_of(kobj, struct device, kobj));
110 	unsigned int size = 0x100;
111 	loff_t init_off = off;
112 	u8 *data = (u8 *) buf;
113 
114 	/* Several chips lock up trying to read undefined config space */
115 	if (capable(CAP_SYS_ADMIN))
116 		size = RIO_MAINT_SPACE_SZ;
117 
118 	if (off >= size)
119 		return 0;
120 	if (off + count > size) {
121 		size -= off;
122 		count = size;
123 	} else {
124 		size = count;
125 	}
126 
127 	if ((off & 1) && size) {
128 		u8 val;
129 		rio_read_config_8(dev, off, &val);
130 		data[off - init_off] = val;
131 		off++;
132 		size--;
133 	}
134 
135 	if ((off & 3) && size > 2) {
136 		u16 val;
137 		rio_read_config_16(dev, off, &val);
138 		data[off - init_off] = (val >> 8) & 0xff;
139 		data[off - init_off + 1] = val & 0xff;
140 		off += 2;
141 		size -= 2;
142 	}
143 
144 	while (size > 3) {
145 		u32 val;
146 		rio_read_config_32(dev, off, &val);
147 		data[off - init_off] = (val >> 24) & 0xff;
148 		data[off - init_off + 1] = (val >> 16) & 0xff;
149 		data[off - init_off + 2] = (val >> 8) & 0xff;
150 		data[off - init_off + 3] = val & 0xff;
151 		off += 4;
152 		size -= 4;
153 	}
154 
155 	if (size >= 2) {
156 		u16 val;
157 		rio_read_config_16(dev, off, &val);
158 		data[off - init_off] = (val >> 8) & 0xff;
159 		data[off - init_off + 1] = val & 0xff;
160 		off += 2;
161 		size -= 2;
162 	}
163 
164 	if (size > 0) {
165 		u8 val;
166 		rio_read_config_8(dev, off, &val);
167 		data[off - init_off] = val;
168 		off++;
169 		--size;
170 	}
171 
172 	return count;
173 }
174 
175 static ssize_t
rio_write_config(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)176 rio_write_config(struct file *filp, struct kobject *kobj,
177 		 struct bin_attribute *bin_attr,
178 		 char *buf, loff_t off, size_t count)
179 {
180 	struct rio_dev *dev =
181 	    to_rio_dev(container_of(kobj, struct device, kobj));
182 	unsigned int size = count;
183 	loff_t init_off = off;
184 	u8 *data = (u8 *) buf;
185 
186 	if (off >= RIO_MAINT_SPACE_SZ)
187 		return 0;
188 	if (off + count > RIO_MAINT_SPACE_SZ) {
189 		size = RIO_MAINT_SPACE_SZ - off;
190 		count = size;
191 	}
192 
193 	if ((off & 1) && size) {
194 		rio_write_config_8(dev, off, data[off - init_off]);
195 		off++;
196 		size--;
197 	}
198 
199 	if ((off & 3) && (size > 2)) {
200 		u16 val = data[off - init_off + 1];
201 		val |= (u16) data[off - init_off] << 8;
202 		rio_write_config_16(dev, off, val);
203 		off += 2;
204 		size -= 2;
205 	}
206 
207 	while (size > 3) {
208 		u32 val = data[off - init_off + 3];
209 		val |= (u32) data[off - init_off + 2] << 8;
210 		val |= (u32) data[off - init_off + 1] << 16;
211 		val |= (u32) data[off - init_off] << 24;
212 		rio_write_config_32(dev, off, val);
213 		off += 4;
214 		size -= 4;
215 	}
216 
217 	if (size >= 2) {
218 		u16 val = data[off - init_off + 1];
219 		val |= (u16) data[off - init_off] << 8;
220 		rio_write_config_16(dev, off, val);
221 		off += 2;
222 		size -= 2;
223 	}
224 
225 	if (size) {
226 		rio_write_config_8(dev, off, data[off - init_off]);
227 		off++;
228 		--size;
229 	}
230 
231 	return count;
232 }
233 
234 static struct bin_attribute rio_config_attr = {
235 	.attr = {
236 		 .name = "config",
237 		 .mode = S_IRUGO | S_IWUSR,
238 		 },
239 	.size = RIO_MAINT_SPACE_SZ,
240 	.read = rio_read_config,
241 	.write = rio_write_config,
242 };
243 
244 /**
245  * rio_create_sysfs_dev_files - create RIO specific sysfs files
246  * @rdev: device whose entries should be created
247  *
248  * Create files when @rdev is added to sysfs.
249  */
rio_create_sysfs_dev_files(struct rio_dev * rdev)250 int rio_create_sysfs_dev_files(struct rio_dev *rdev)
251 {
252 	int err = 0;
253 
254 	err = device_create_bin_file(&rdev->dev, &rio_config_attr);
255 
256 	if (!err && (rdev->pef & RIO_PEF_SWITCH)) {
257 		err |= device_create_file(&rdev->dev, &dev_attr_routes);
258 		err |= device_create_file(&rdev->dev, &dev_attr_lnext);
259 		err |= device_create_file(&rdev->dev, &dev_attr_hopcount);
260 		if (!err && rdev->rswitch->sw_sysfs)
261 			err = rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_CREATE);
262 	}
263 
264 	if (err)
265 		pr_warning("RIO: Failed to create attribute file(s) for %s\n",
266 			   rio_name(rdev));
267 
268 	return err;
269 }
270 
271 /**
272  * rio_remove_sysfs_dev_files - cleanup RIO specific sysfs files
273  * @rdev: device whose entries we should free
274  *
275  * Cleanup when @rdev is removed from sysfs.
276  */
rio_remove_sysfs_dev_files(struct rio_dev * rdev)277 void rio_remove_sysfs_dev_files(struct rio_dev *rdev)
278 {
279 	device_remove_bin_file(&rdev->dev, &rio_config_attr);
280 	if (rdev->pef & RIO_PEF_SWITCH) {
281 		device_remove_file(&rdev->dev, &dev_attr_routes);
282 		device_remove_file(&rdev->dev, &dev_attr_lnext);
283 		device_remove_file(&rdev->dev, &dev_attr_hopcount);
284 		if (rdev->rswitch->sw_sysfs)
285 			rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_REMOVE);
286 	}
287 }
288