diff options
author | Matt Porter <mporter@kernel.crashing.org> | 2005-11-07 04:00:17 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-07 10:53:46 -0500 |
commit | eb188d0e857c436b5d365d5ccc629da5a06ed102 (patch) | |
tree | 1a80ceab7d2d18a672f5753d26bbd972543ce959 /drivers | |
parent | 70a50ebd9a94533964c19f918dbbd66763e3f9e5 (diff) |
[PATCH] RapidIO support: core enum
Adds RapidIO enumeration/discovery.
The core code implements enumeration/discovery, management of
devices/resources, and interfaces for RIO drivers.
Signed-off-by: Matt Porter <mporter@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/rapidio/rio-scan.c | 960 | ||||
-rw-r--r-- | drivers/rapidio/switches/Makefile | 5 | ||||
-rw-r--r-- | drivers/rapidio/switches/tsi500.c | 60 |
3 files changed, 1025 insertions, 0 deletions
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c new file mode 100644 index 000000000000..20e1d8f74597 --- /dev/null +++ b/drivers/rapidio/rio-scan.c | |||
@@ -0,0 +1,960 @@ | |||
1 | /* | ||
2 | * RapidIO enumeration and discovery 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/config.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/kernel.h> | ||
16 | |||
17 | #include <linux/delay.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/rio.h> | ||
20 | #include <linux/rio_drv.h> | ||
21 | #include <linux/rio_ids.h> | ||
22 | #include <linux/rio_regs.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/spinlock.h> | ||
25 | #include <linux/timer.h> | ||
26 | |||
27 | #include "rio.h" | ||
28 | |||
29 | LIST_HEAD(rio_devices); | ||
30 | static LIST_HEAD(rio_switches); | ||
31 | |||
32 | #define RIO_ENUM_CMPL_MAGIC 0xdeadbeef | ||
33 | |||
34 | static void rio_enum_timeout(unsigned long); | ||
35 | |||
36 | spinlock_t rio_global_list_lock = SPIN_LOCK_UNLOCKED; | ||
37 | static int next_destid = 0; | ||
38 | static int next_switchid = 0; | ||
39 | static int next_net = 0; | ||
40 | |||
41 | static struct timer_list rio_enum_timer = | ||
42 | TIMER_INITIALIZER(rio_enum_timeout, 0, 0); | ||
43 | |||
44 | static int rio_mport_phys_table[] = { | ||
45 | RIO_EFB_PAR_EP_ID, | ||
46 | RIO_EFB_PAR_EP_REC_ID, | ||
47 | RIO_EFB_SER_EP_ID, | ||
48 | RIO_EFB_SER_EP_REC_ID, | ||
49 | -1, | ||
50 | }; | ||
51 | |||
52 | static int rio_sport_phys_table[] = { | ||
53 | RIO_EFB_PAR_EP_FREE_ID, | ||
54 | RIO_EFB_SER_EP_FREE_ID, | ||
55 | -1, | ||
56 | }; | ||
57 | |||
58 | extern struct rio_route_ops __start_rio_route_ops[]; | ||
59 | extern struct rio_route_ops __end_rio_route_ops[]; | ||
60 | |||
61 | /** | ||
62 | * rio_get_device_id - Get the base/extended device id for a device | ||
63 | * @port: RIO master port | ||
64 | * @destid: Destination ID of device | ||
65 | * @hopcount: Hopcount to device | ||
66 | * | ||
67 | * Reads the base/extended device id from a device. Returns the | ||
68 | * 8/16-bit device ID. | ||
69 | */ | ||
70 | static u16 rio_get_device_id(struct rio_mport *port, u16 destid, u8 hopcount) | ||
71 | { | ||
72 | u32 result; | ||
73 | |||
74 | rio_mport_read_config_32(port, destid, hopcount, RIO_DID_CSR, &result); | ||
75 | |||
76 | return RIO_GET_DID(result); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * rio_set_device_id - Set the base/extended device id for a device | ||
81 | * @port: RIO master port | ||
82 | * @destid: Destination ID of device | ||
83 | * @hopcount: Hopcount to device | ||
84 | * @did: Device ID value to be written | ||
85 | * | ||
86 | * Writes the base/extended device id from a device. | ||
87 | */ | ||
88 | static void | ||
89 | rio_set_device_id(struct rio_mport *port, u16 destid, u8 hopcount, u16 did) | ||
90 | { | ||
91 | rio_mport_write_config_32(port, destid, hopcount, RIO_DID_CSR, | ||
92 | RIO_SET_DID(did)); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * rio_local_set_device_id - Set the base/extended device id for a port | ||
97 | * @port: RIO master port | ||
98 | * @did: Device ID value to be written | ||
99 | * | ||
100 | * Writes the base/extended device id from a device. | ||
101 | */ | ||
102 | static void rio_local_set_device_id(struct rio_mport *port, u16 did) | ||
103 | { | ||
104 | rio_local_write_config_32(port, RIO_DID_CSR, RIO_SET_DID(did)); | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * rio_clear_locks- Release all host locks and signal enumeration complete | ||
109 | * @port: Master port to issue transaction | ||
110 | * | ||
111 | * Marks the component tag CSR on each device with the enumeration | ||
112 | * complete flag. When complete, it then release the host locks on | ||
113 | * each device. Returns 0 on success or %-EINVAL on failure. | ||
114 | */ | ||
115 | static int rio_clear_locks(struct rio_mport *port) | ||
116 | { | ||
117 | struct rio_dev *rdev; | ||
118 | u32 result; | ||
119 | int ret = 0; | ||
120 | |||
121 | /* Write component tag CSR magic complete value */ | ||
122 | rio_local_write_config_32(port, RIO_COMPONENT_TAG_CSR, | ||
123 | RIO_ENUM_CMPL_MAGIC); | ||
124 | list_for_each_entry(rdev, &rio_devices, global_list) | ||
125 | rio_write_config_32(rdev, RIO_COMPONENT_TAG_CSR, | ||
126 | RIO_ENUM_CMPL_MAGIC); | ||
127 | |||
128 | /* Release host device id locks */ | ||
129 | rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR, | ||
130 | port->host_deviceid); | ||
131 | rio_local_read_config_32(port, RIO_HOST_DID_LOCK_CSR, &result); | ||
132 | if ((result & 0xffff) != 0xffff) { | ||
133 | printk(KERN_INFO | ||
134 | "RIO: badness when releasing host lock on master port, result %8.8x\n", | ||
135 | result); | ||
136 | ret = -EINVAL; | ||
137 | } | ||
138 | list_for_each_entry(rdev, &rio_devices, global_list) { | ||
139 | rio_write_config_32(rdev, RIO_HOST_DID_LOCK_CSR, | ||
140 | port->host_deviceid); | ||
141 | rio_read_config_32(rdev, RIO_HOST_DID_LOCK_CSR, &result); | ||
142 | if ((result & 0xffff) != 0xffff) { | ||
143 | printk(KERN_INFO | ||
144 | "RIO: badness when releasing host lock on vid %4.4x did %4.4x\n", | ||
145 | rdev->vid, rdev->did); | ||
146 | ret = -EINVAL; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | return ret; | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * rio_enum_host- Set host lock and initialize host destination ID | ||
155 | * @port: Master port to issue transaction | ||
156 | * | ||
157 | * Sets the local host master port lock and destination ID register | ||
158 | * with the host device ID value. The host device ID value is provided | ||
159 | * by the platform. Returns %0 on success or %-1 on failure. | ||
160 | */ | ||
161 | static int rio_enum_host(struct rio_mport *port) | ||
162 | { | ||
163 | u32 result; | ||
164 | |||
165 | /* Set master port host device id lock */ | ||
166 | rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR, | ||
167 | port->host_deviceid); | ||
168 | |||
169 | rio_local_read_config_32(port, RIO_HOST_DID_LOCK_CSR, &result); | ||
170 | if ((result & 0xffff) != port->host_deviceid) | ||
171 | return -1; | ||
172 | |||
173 | /* Set master port destid and init destid ctr */ | ||
174 | rio_local_set_device_id(port, port->host_deviceid); | ||
175 | |||
176 | if (next_destid == port->host_deviceid) | ||
177 | next_destid++; | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * rio_device_has_destid- Test if a device contains a destination ID register | ||
184 | * @port: Master port to issue transaction | ||
185 | * @src_ops: RIO device source operations | ||
186 | * @dst_ops: RIO device destination operations | ||
187 | * | ||
188 | * Checks the provided @src_ops and @dst_ops for the necessary transaction | ||
189 | * capabilities that indicate whether or not a device will implement a | ||
190 | * destination ID register. Returns 1 if true or 0 if false. | ||
191 | */ | ||
192 | static int rio_device_has_destid(struct rio_mport *port, int src_ops, | ||
193 | int dst_ops) | ||
194 | { | ||
195 | if (((src_ops & RIO_SRC_OPS_READ) || | ||
196 | (src_ops & RIO_SRC_OPS_WRITE) || | ||
197 | (src_ops & RIO_SRC_OPS_ATOMIC_TST_SWP) || | ||
198 | (src_ops & RIO_SRC_OPS_ATOMIC_INC) || | ||
199 | (src_ops & RIO_SRC_OPS_ATOMIC_DEC) || | ||
200 | (src_ops & RIO_SRC_OPS_ATOMIC_SET) || | ||
201 | (src_ops & RIO_SRC_OPS_ATOMIC_CLR)) && | ||
202 | ((dst_ops & RIO_DST_OPS_READ) || | ||
203 | (dst_ops & RIO_DST_OPS_WRITE) || | ||
204 | (dst_ops & RIO_DST_OPS_ATOMIC_TST_SWP) || | ||
205 | (dst_ops & RIO_DST_OPS_ATOMIC_INC) || | ||
206 | (dst_ops & RIO_DST_OPS_ATOMIC_DEC) || | ||
207 | (dst_ops & RIO_DST_OPS_ATOMIC_SET) || | ||
208 | (dst_ops & RIO_DST_OPS_ATOMIC_CLR))) { | ||
209 | return 1; | ||
210 | } else | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * rio_release_dev- Frees a RIO device struct | ||
216 | * @dev: LDM device associated with a RIO device struct | ||
217 | * | ||
218 | * Gets the RIO device struct associated a RIO device struct. | ||
219 | * The RIO device struct is freed. | ||
220 | */ | ||
221 | static void rio_release_dev(struct device *dev) | ||
222 | { | ||
223 | struct rio_dev *rdev; | ||
224 | |||
225 | rdev = to_rio_dev(dev); | ||
226 | kfree(rdev); | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * rio_is_switch- Tests if a RIO device has switch capabilities | ||
231 | * @rdev: RIO device | ||
232 | * | ||
233 | * Gets the RIO device Processing Element Features register | ||
234 | * contents and tests for switch capabilities. Returns 1 if | ||
235 | * the device is a switch or 0 if it is not a switch. | ||
236 | * The RIO device struct is freed. | ||
237 | */ | ||
238 | static int rio_is_switch(struct rio_dev *rdev) | ||
239 | { | ||
240 | if (rdev->pef & RIO_PEF_SWITCH) | ||
241 | return 1; | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * rio_route_set_ops- Sets routing operations for a particular vendor switch | ||
247 | * @rdev: RIO device | ||
248 | * | ||
249 | * Searches the RIO route ops table for known switch types. If the vid | ||
250 | * and did match a switch table entry, then set the add_entry() and | ||
251 | * get_entry() ops to the table entry values. | ||
252 | */ | ||
253 | static void rio_route_set_ops(struct rio_dev *rdev) | ||
254 | { | ||
255 | struct rio_route_ops *cur = __start_rio_route_ops; | ||
256 | struct rio_route_ops *end = __end_rio_route_ops; | ||
257 | |||
258 | while (cur < end) { | ||
259 | if ((cur->vid == rdev->vid) && (cur->did == rdev->did)) { | ||
260 | pr_debug("RIO: adding routing ops for %s\n", rio_name(rdev)); | ||
261 | rdev->rswitch->add_entry = cur->add_hook; | ||
262 | rdev->rswitch->get_entry = cur->get_hook; | ||
263 | } | ||
264 | cur++; | ||
265 | } | ||
266 | |||
267 | if (!rdev->rswitch->add_entry || !rdev->rswitch->get_entry) | ||
268 | printk(KERN_ERR "RIO: missing routing ops for %s\n", | ||
269 | rio_name(rdev)); | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * rio_add_device- Adds a RIO device to the device model | ||
274 | * @rdev: RIO device | ||
275 | * | ||
276 | * Adds the RIO device to the global device list and adds the RIO | ||
277 | * device to the RIO device list. Creates the generic sysfs nodes | ||
278 | * for an RIO device. | ||
279 | */ | ||
280 | static void __devinit rio_add_device(struct rio_dev *rdev) | ||
281 | { | ||
282 | device_add(&rdev->dev); | ||
283 | |||
284 | spin_lock(&rio_global_list_lock); | ||
285 | list_add_tail(&rdev->global_list, &rio_devices); | ||
286 | spin_unlock(&rio_global_list_lock); | ||
287 | |||
288 | rio_create_sysfs_dev_files(rdev); | ||
289 | } | ||
290 | |||
291 | /** | ||
292 | * rio_setup_device- Allocates and sets up a RIO device | ||
293 | * @net: RIO network | ||
294 | * @port: Master port to send transactions | ||
295 | * @destid: Current destination ID | ||
296 | * @hopcount: Current hopcount | ||
297 | * @do_enum: Enumeration/Discovery mode flag | ||
298 | * | ||
299 | * Allocates a RIO device and configures fields based on configuration | ||
300 | * space contents. If device has a destination ID register, a destination | ||
301 | * ID is either assigned in enumeration mode or read from configuration | ||
302 | * space in discovery mode. If the device has switch capabilities, then | ||
303 | * a switch is allocated and configured appropriately. Returns a pointer | ||
304 | * to a RIO device on success or NULL on failure. | ||
305 | * | ||
306 | */ | ||
307 | static struct rio_dev *rio_setup_device(struct rio_net *net, | ||
308 | struct rio_mport *port, u16 destid, | ||
309 | u8 hopcount, int do_enum) | ||
310 | { | ||
311 | struct rio_dev *rdev; | ||
312 | struct rio_switch *rswitch; | ||
313 | int result, rdid; | ||
314 | |||
315 | rdev = kmalloc(sizeof(struct rio_dev), GFP_KERNEL); | ||
316 | if (!rdev) | ||
317 | goto out; | ||
318 | |||
319 | memset(rdev, 0, sizeof(struct rio_dev)); | ||
320 | rdev->net = net; | ||
321 | rio_mport_read_config_32(port, destid, hopcount, RIO_DEV_ID_CAR, | ||
322 | &result); | ||
323 | rdev->did = result >> 16; | ||
324 | rdev->vid = result & 0xffff; | ||
325 | rio_mport_read_config_32(port, destid, hopcount, RIO_DEV_INFO_CAR, | ||
326 | &rdev->device_rev); | ||
327 | rio_mport_read_config_32(port, destid, hopcount, RIO_ASM_ID_CAR, | ||
328 | &result); | ||
329 | rdev->asm_did = result >> 16; | ||
330 | rdev->asm_vid = result & 0xffff; | ||
331 | rio_mport_read_config_32(port, destid, hopcount, RIO_ASM_INFO_CAR, | ||
332 | &result); | ||
333 | rdev->asm_rev = result >> 16; | ||
334 | rio_mport_read_config_32(port, destid, hopcount, RIO_PEF_CAR, | ||
335 | &rdev->pef); | ||
336 | if (rdev->pef & RIO_PEF_EXT_FEATURES) | ||
337 | rdev->efptr = result & 0xffff; | ||
338 | |||
339 | rio_mport_read_config_32(port, destid, hopcount, RIO_SRC_OPS_CAR, | ||
340 | &rdev->src_ops); | ||
341 | rio_mport_read_config_32(port, destid, hopcount, RIO_DST_OPS_CAR, | ||
342 | &rdev->dst_ops); | ||
343 | |||
344 | if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops) | ||
345 | && do_enum) { | ||
346 | rio_set_device_id(port, destid, hopcount, next_destid); | ||
347 | rdev->destid = next_destid++; | ||
348 | if (next_destid == port->host_deviceid) | ||
349 | next_destid++; | ||
350 | } else | ||
351 | rdev->destid = rio_get_device_id(port, destid, hopcount); | ||
352 | |||
353 | /* If a PE has both switch and other functions, show it as a switch */ | ||
354 | if (rio_is_switch(rdev)) { | ||
355 | rio_mport_read_config_32(port, destid, hopcount, | ||
356 | RIO_SWP_INFO_CAR, &rdev->swpinfo); | ||
357 | rswitch = kmalloc(sizeof(struct rio_switch), GFP_KERNEL); | ||
358 | if (!rswitch) { | ||
359 | kfree(rdev); | ||
360 | rdev = NULL; | ||
361 | goto out; | ||
362 | } | ||
363 | rswitch->switchid = next_switchid; | ||
364 | rswitch->hopcount = hopcount; | ||
365 | rswitch->destid = 0xffff; | ||
366 | /* Initialize switch route table */ | ||
367 | for (rdid = 0; rdid < RIO_MAX_ROUTE_ENTRIES; rdid++) | ||
368 | rswitch->route_table[rdid] = RIO_INVALID_ROUTE; | ||
369 | rdev->rswitch = rswitch; | ||
370 | sprintf(rio_name(rdev), "%02x:s:%04x", rdev->net->id, | ||
371 | rdev->rswitch->switchid); | ||
372 | rio_route_set_ops(rdev); | ||
373 | |||
374 | list_add_tail(&rswitch->node, &rio_switches); | ||
375 | |||
376 | } else | ||
377 | sprintf(rio_name(rdev), "%02x:e:%04x", rdev->net->id, | ||
378 | rdev->destid); | ||
379 | |||
380 | rdev->dev.bus = &rio_bus_type; | ||
381 | |||
382 | device_initialize(&rdev->dev); | ||
383 | rdev->dev.release = rio_release_dev; | ||
384 | rio_dev_get(rdev); | ||
385 | |||
386 | rdev->dev.dma_mask = (u64 *) 0xffffffff; | ||
387 | rdev->dev.coherent_dma_mask = 0xffffffffULL; | ||
388 | |||
389 | if ((rdev->pef & RIO_PEF_INB_DOORBELL) && | ||
390 | (rdev->dst_ops & RIO_DST_OPS_DOORBELL)) | ||
391 | rio_init_dbell_res(&rdev->riores[RIO_DOORBELL_RESOURCE], | ||
392 | 0, 0xffff); | ||
393 | |||
394 | rio_add_device(rdev); | ||
395 | |||
396 | out: | ||
397 | return rdev; | ||
398 | } | ||
399 | |||
400 | /** | ||
401 | * rio_sport_is_active- Tests if a switch port has an active connection. | ||
402 | * @port: Master port to send transaction | ||
403 | * @destid: Associated destination ID for switch | ||
404 | * @hopcount: Hopcount to reach switch | ||
405 | * @sport: Switch port number | ||
406 | * | ||
407 | * Reads the port error status CSR for a particular switch port to | ||
408 | * determine if the port has an active link. Returns | ||
409 | * %PORT_N_ERR_STS_PORT_OK if the port is active or %0 if it is | ||
410 | * inactive. | ||
411 | */ | ||
412 | static int | ||
413 | rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport) | ||
414 | { | ||
415 | u32 result; | ||
416 | u32 ext_ftr_ptr; | ||
417 | |||
418 | int *entry = rio_sport_phys_table; | ||
419 | |||
420 | do { | ||
421 | if ((ext_ftr_ptr = | ||
422 | rio_mport_get_feature(port, 0, destid, hopcount, *entry))) | ||
423 | |||
424 | break; | ||
425 | } while (*++entry >= 0); | ||
426 | |||
427 | if (ext_ftr_ptr) | ||
428 | rio_mport_read_config_32(port, destid, hopcount, | ||
429 | ext_ftr_ptr + | ||
430 | RIO_PORT_N_ERR_STS_CSR(sport), | ||
431 | &result); | ||
432 | |||
433 | return (result & PORT_N_ERR_STS_PORT_OK); | ||
434 | } | ||
435 | |||
436 | /** | ||
437 | * rio_route_add_entry- Add a route entry to a switch routing table | ||
438 | * @mport: Master port to send transaction | ||
439 | * @rdev: Switch device | ||
440 | * @table: Routing table ID | ||
441 | * @route_destid: Destination ID to be routed | ||
442 | * @route_port: Port number to be routed | ||
443 | * | ||
444 | * Calls the switch specific add_entry() method to add a route entry | ||
445 | * on a switch. The route table can be specified using the @table | ||
446 | * argument if a switch has per port routing tables or the normal | ||
447 | * use is to specific all tables (or the global table) by passing | ||
448 | * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL | ||
449 | * on failure. | ||
450 | */ | ||
451 | static int rio_route_add_entry(struct rio_mport *mport, struct rio_dev *rdev, | ||
452 | u16 table, u16 route_destid, u8 route_port) | ||
453 | { | ||
454 | return rdev->rswitch->add_entry(mport, rdev->rswitch->destid, | ||
455 | rdev->rswitch->hopcount, table, | ||
456 | route_destid, route_port); | ||
457 | } | ||
458 | |||
459 | /** | ||
460 | * rio_route_get_entry- Read a route entry in a switch routing table | ||
461 | * @mport: Master port to send transaction | ||
462 | * @rdev: Switch device | ||
463 | * @table: Routing table ID | ||
464 | * @route_destid: Destination ID to be routed | ||
465 | * @route_port: Pointer to read port number into | ||
466 | * | ||
467 | * Calls the switch specific get_entry() method to read a route entry | ||
468 | * in a switch. The route table can be specified using the @table | ||
469 | * argument if a switch has per port routing tables or the normal | ||
470 | * use is to specific all tables (or the global table) by passing | ||
471 | * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL | ||
472 | * on failure. | ||
473 | */ | ||
474 | static int | ||
475 | rio_route_get_entry(struct rio_mport *mport, struct rio_dev *rdev, u16 table, | ||
476 | u16 route_destid, u8 * route_port) | ||
477 | { | ||
478 | return rdev->rswitch->get_entry(mport, rdev->rswitch->destid, | ||
479 | rdev->rswitch->hopcount, table, | ||
480 | route_destid, route_port); | ||
481 | } | ||
482 | |||
483 | /** | ||
484 | * rio_get_host_deviceid_lock- Reads the Host Device ID Lock CSR on a device | ||
485 | * @port: Master port to send transaction | ||
486 | * @hopcount: Number of hops to the device | ||
487 | * | ||
488 | * Used during enumeration to read the Host Device ID Lock CSR on a | ||
489 | * RIO device. Returns the value of the lock register. | ||
490 | */ | ||
491 | static u16 rio_get_host_deviceid_lock(struct rio_mport *port, u8 hopcount) | ||
492 | { | ||
493 | u32 result; | ||
494 | |||
495 | rio_mport_read_config_32(port, RIO_ANY_DESTID, hopcount, | ||
496 | RIO_HOST_DID_LOCK_CSR, &result); | ||
497 | |||
498 | return (u16) (result & 0xffff); | ||
499 | } | ||
500 | |||
501 | /** | ||
502 | * rio_get_swpinfo_inport- Gets the ingress port number | ||
503 | * @mport: Master port to send transaction | ||
504 | * @destid: Destination ID associated with the switch | ||
505 | * @hopcount: Number of hops to the device | ||
506 | * | ||
507 | * Returns port number being used to access the switch device. | ||
508 | */ | ||
509 | static u8 | ||
510 | rio_get_swpinfo_inport(struct rio_mport *mport, u16 destid, u8 hopcount) | ||
511 | { | ||
512 | u32 result; | ||
513 | |||
514 | rio_mport_read_config_32(mport, destid, hopcount, RIO_SWP_INFO_CAR, | ||
515 | &result); | ||
516 | |||
517 | return (u8) (result & 0xff); | ||
518 | } | ||
519 | |||
520 | /** | ||
521 | * rio_get_swpinfo_tports- Gets total number of ports on the switch | ||
522 | * @mport: Master port to send transaction | ||
523 | * @destid: Destination ID associated with the switch | ||
524 | * @hopcount: Number of hops to the device | ||
525 | * | ||
526 | * Returns total numbers of ports implemented by the switch device. | ||
527 | */ | ||
528 | static u8 rio_get_swpinfo_tports(struct rio_mport *mport, u16 destid, | ||
529 | u8 hopcount) | ||
530 | { | ||
531 | u32 result; | ||
532 | |||
533 | rio_mport_read_config_32(mport, destid, hopcount, RIO_SWP_INFO_CAR, | ||
534 | &result); | ||
535 | |||
536 | return RIO_GET_TOTAL_PORTS(result); | ||
537 | } | ||
538 | |||
539 | /** | ||
540 | * rio_net_add_mport- Add a master port to a RIO network | ||
541 | * @net: RIO network | ||
542 | * @port: Master port to add | ||
543 | * | ||
544 | * Adds a master port to the network list of associated master | ||
545 | * ports.. | ||
546 | */ | ||
547 | static void rio_net_add_mport(struct rio_net *net, struct rio_mport *port) | ||
548 | { | ||
549 | spin_lock(&rio_global_list_lock); | ||
550 | list_add_tail(&port->nnode, &net->mports); | ||
551 | spin_unlock(&rio_global_list_lock); | ||
552 | } | ||
553 | |||
554 | /** | ||
555 | * rio_enum_peer- Recursively enumerate a RIO network through a master port | ||
556 | * @net: RIO network being enumerated | ||
557 | * @port: Master port to send transactions | ||
558 | * @hopcount: Number of hops into the network | ||
559 | * | ||
560 | * Recursively enumerates a RIO network. Transactions are sent via the | ||
561 | * master port passed in @port. | ||
562 | */ | ||
563 | static int rio_enum_peer(struct rio_net *net, struct rio_mport *port, | ||
564 | u8 hopcount) | ||
565 | { | ||
566 | int port_num; | ||
567 | int num_ports; | ||
568 | int cur_destid; | ||
569 | struct rio_dev *rdev; | ||
570 | u16 destid; | ||
571 | int tmp; | ||
572 | |||
573 | if (rio_get_host_deviceid_lock(port, hopcount) == port->host_deviceid) { | ||
574 | pr_debug("RIO: PE already discovered by this host\n"); | ||
575 | /* | ||
576 | * Already discovered by this host. Add it as another | ||
577 | * master port for the current network. | ||
578 | */ | ||
579 | rio_net_add_mport(net, port); | ||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | /* Attempt to acquire device lock */ | ||
584 | rio_mport_write_config_32(port, RIO_ANY_DESTID, hopcount, | ||
585 | RIO_HOST_DID_LOCK_CSR, port->host_deviceid); | ||
586 | while ((tmp = rio_get_host_deviceid_lock(port, hopcount)) | ||
587 | < port->host_deviceid) { | ||
588 | /* Delay a bit */ | ||
589 | mdelay(1); | ||
590 | /* Attempt to acquire device lock again */ | ||
591 | rio_mport_write_config_32(port, RIO_ANY_DESTID, hopcount, | ||
592 | RIO_HOST_DID_LOCK_CSR, | ||
593 | port->host_deviceid); | ||
594 | } | ||
595 | |||
596 | if (rio_get_host_deviceid_lock(port, hopcount) > port->host_deviceid) { | ||
597 | pr_debug( | ||
598 | "RIO: PE locked by a higher priority host...retreating\n"); | ||
599 | return -1; | ||
600 | } | ||
601 | |||
602 | /* Setup new RIO device */ | ||
603 | if ((rdev = rio_setup_device(net, port, RIO_ANY_DESTID, hopcount, 1))) { | ||
604 | /* Add device to the global and bus/net specific list. */ | ||
605 | list_add_tail(&rdev->net_list, &net->devices); | ||
606 | } else | ||
607 | return -1; | ||
608 | |||
609 | if (rio_is_switch(rdev)) { | ||
610 | next_switchid++; | ||
611 | |||
612 | for (destid = 0; destid < next_destid; destid++) { | ||
613 | rio_route_add_entry(port, rdev, RIO_GLOBAL_TABLE, | ||
614 | destid, rio_get_swpinfo_inport(port, | ||
615 | RIO_ANY_DESTID, | ||
616 | hopcount)); | ||
617 | rdev->rswitch->route_table[destid] = | ||
618 | rio_get_swpinfo_inport(port, RIO_ANY_DESTID, | ||
619 | hopcount); | ||
620 | } | ||
621 | |||
622 | num_ports = | ||
623 | rio_get_swpinfo_tports(port, RIO_ANY_DESTID, hopcount); | ||
624 | pr_debug( | ||
625 | "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n", | ||
626 | rio_name(rdev), rdev->vid, rdev->did, num_ports); | ||
627 | for (port_num = 0; port_num < num_ports; port_num++) { | ||
628 | if (rio_get_swpinfo_inport | ||
629 | (port, RIO_ANY_DESTID, hopcount) == port_num) | ||
630 | continue; | ||
631 | |||
632 | cur_destid = next_destid; | ||
633 | |||
634 | if (rio_sport_is_active | ||
635 | (port, RIO_ANY_DESTID, hopcount, port_num)) { | ||
636 | pr_debug( | ||
637 | "RIO: scanning device on port %d\n", | ||
638 | port_num); | ||
639 | rio_route_add_entry(port, rdev, | ||
640 | RIO_GLOBAL_TABLE, | ||
641 | RIO_ANY_DESTID, port_num); | ||
642 | |||
643 | if (rio_enum_peer(net, port, hopcount + 1) < 0) | ||
644 | return -1; | ||
645 | |||
646 | /* Update routing tables */ | ||
647 | if (next_destid > cur_destid) { | ||
648 | for (destid = cur_destid; | ||
649 | destid < next_destid; destid++) { | ||
650 | rio_route_add_entry(port, rdev, | ||
651 | RIO_GLOBAL_TABLE, | ||
652 | destid, | ||
653 | port_num); | ||
654 | rdev->rswitch-> | ||
655 | route_table[destid] = | ||
656 | port_num; | ||
657 | } | ||
658 | rdev->rswitch->destid = cur_destid; | ||
659 | } | ||
660 | } | ||
661 | } | ||
662 | } else | ||
663 | pr_debug("RIO: found %s (vid %4.4x did %4.4x)\n", | ||
664 | rio_name(rdev), rdev->vid, rdev->did); | ||
665 | |||
666 | return 0; | ||
667 | } | ||
668 | |||
669 | /** | ||
670 | * rio_enum_complete- Tests if enumeration of a network is complete | ||
671 | * @port: Master port to send transaction | ||
672 | * | ||
673 | * Tests the Component Tag CSR for presence of the magic enumeration | ||
674 | * complete flag. Return %1 if enumeration is complete or %0 if | ||
675 | * enumeration is incomplete. | ||
676 | */ | ||
677 | static int rio_enum_complete(struct rio_mport *port) | ||
678 | { | ||
679 | u32 tag_csr; | ||
680 | int ret = 0; | ||
681 | |||
682 | rio_local_read_config_32(port, RIO_COMPONENT_TAG_CSR, &tag_csr); | ||
683 | |||
684 | if (tag_csr == RIO_ENUM_CMPL_MAGIC) | ||
685 | ret = 1; | ||
686 | |||
687 | return ret; | ||
688 | } | ||
689 | |||
690 | /** | ||
691 | * rio_disc_peer- Recursively discovers a RIO network through a master port | ||
692 | * @net: RIO network being discovered | ||
693 | * @port: Master port to send transactions | ||
694 | * @destid: Current destination ID in network | ||
695 | * @hopcount: Number of hops into the network | ||
696 | * | ||
697 | * Recursively discovers a RIO network. Transactions are sent via the | ||
698 | * master port passed in @port. | ||
699 | */ | ||
700 | static int | ||
701 | rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid, | ||
702 | u8 hopcount) | ||
703 | { | ||
704 | u8 port_num, route_port; | ||
705 | int num_ports; | ||
706 | struct rio_dev *rdev; | ||
707 | u16 ndestid; | ||
708 | |||
709 | /* Setup new RIO device */ | ||
710 | if ((rdev = rio_setup_device(net, port, destid, hopcount, 0))) { | ||
711 | /* Add device to the global and bus/net specific list. */ | ||
712 | list_add_tail(&rdev->net_list, &net->devices); | ||
713 | } else | ||
714 | return -1; | ||
715 | |||
716 | if (rio_is_switch(rdev)) { | ||
717 | next_switchid++; | ||
718 | |||
719 | /* Associated destid is how we accessed this switch */ | ||
720 | rdev->rswitch->destid = destid; | ||
721 | |||
722 | num_ports = rio_get_swpinfo_tports(port, destid, hopcount); | ||
723 | pr_debug( | ||
724 | "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n", | ||
725 | rio_name(rdev), rdev->vid, rdev->did, num_ports); | ||
726 | for (port_num = 0; port_num < num_ports; port_num++) { | ||
727 | if (rio_get_swpinfo_inport(port, destid, hopcount) == | ||
728 | port_num) | ||
729 | continue; | ||
730 | |||
731 | if (rio_sport_is_active | ||
732 | (port, destid, hopcount, port_num)) { | ||
733 | pr_debug( | ||
734 | "RIO: scanning device on port %d\n", | ||
735 | port_num); | ||
736 | for (ndestid = 0; ndestid < RIO_ANY_DESTID; | ||
737 | ndestid++) { | ||
738 | rio_route_get_entry(port, rdev, | ||
739 | RIO_GLOBAL_TABLE, | ||
740 | ndestid, | ||
741 | &route_port); | ||
742 | if (route_port == port_num) | ||
743 | break; | ||
744 | } | ||
745 | |||
746 | if (rio_disc_peer | ||
747 | (net, port, ndestid, hopcount + 1) < 0) | ||
748 | return -1; | ||
749 | } | ||
750 | } | ||
751 | } else | ||
752 | pr_debug("RIO: found %s (vid %4.4x did %4.4x)\n", | ||
753 | rio_name(rdev), rdev->vid, rdev->did); | ||
754 | |||
755 | return 0; | ||
756 | } | ||
757 | |||
758 | /** | ||
759 | * rio_mport_is_active- Tests if master port link is active | ||
760 | * @port: Master port to test | ||
761 | * | ||
762 | * Reads the port error status CSR for the master port to | ||
763 | * determine if the port has an active link. Returns | ||
764 | * %PORT_N_ERR_STS_PORT_OK if the master port is active | ||
765 | * or %0 if it is inactive. | ||
766 | */ | ||
767 | static int rio_mport_is_active(struct rio_mport *port) | ||
768 | { | ||
769 | u32 result = 0; | ||
770 | u32 ext_ftr_ptr; | ||
771 | int *entry = rio_mport_phys_table; | ||
772 | |||
773 | do { | ||
774 | if ((ext_ftr_ptr = | ||
775 | rio_mport_get_feature(port, 1, 0, 0, *entry))) | ||
776 | break; | ||
777 | } while (*++entry >= 0); | ||
778 | |||
779 | if (ext_ftr_ptr) | ||
780 | rio_local_read_config_32(port, | ||
781 | ext_ftr_ptr + | ||
782 | RIO_PORT_N_ERR_STS_CSR(port->index), | ||
783 | &result); | ||
784 | |||
785 | return (result & PORT_N_ERR_STS_PORT_OK); | ||
786 | } | ||
787 | |||
788 | /** | ||
789 | * rio_alloc_net- Allocate and configure a new RIO network | ||
790 | * @port: Master port associated with the RIO network | ||
791 | * | ||
792 | * Allocates a RIO network structure, initializes per-network | ||
793 | * list heads, and adds the associated master port to the | ||
794 | * network list of associated master ports. Returns a | ||
795 | * RIO network pointer on success or %NULL on failure. | ||
796 | */ | ||
797 | static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port) | ||
798 | { | ||
799 | struct rio_net *net; | ||
800 | |||
801 | net = kmalloc(sizeof(struct rio_net), GFP_KERNEL); | ||
802 | if (net) { | ||
803 | memset(net, 0, sizeof(struct rio_net)); | ||
804 | INIT_LIST_HEAD(&net->node); | ||
805 | INIT_LIST_HEAD(&net->devices); | ||
806 | INIT_LIST_HEAD(&net->mports); | ||
807 | list_add_tail(&port->nnode, &net->mports); | ||
808 | net->hport = port; | ||
809 | net->id = next_net++; | ||
810 | } | ||
811 | return net; | ||
812 | } | ||
813 | |||
814 | /** | ||
815 | * rio_enum_mport- Start enumeration through a master port | ||
816 | * @mport: Master port to send transactions | ||
817 | * | ||
818 | * Starts the enumeration process. If somebody has enumerated our | ||
819 | * master port device, then give up. If not and we have an active | ||
820 | * link, then start recursive peer enumeration. Returns %0 if | ||
821 | * enumeration succeeds or %-EBUSY if enumeration fails. | ||
822 | */ | ||
823 | int rio_enum_mport(struct rio_mport *mport) | ||
824 | { | ||
825 | struct rio_net *net = NULL; | ||
826 | int rc = 0; | ||
827 | |||
828 | printk(KERN_INFO "RIO: enumerate master port %d, %s\n", mport->id, | ||
829 | mport->name); | ||
830 | /* If somebody else enumerated our master port device, bail. */ | ||
831 | if (rio_enum_host(mport) < 0) { | ||
832 | printk(KERN_INFO | ||
833 | "RIO: master port %d device has been enumerated by a remote host\n", | ||
834 | mport->id); | ||
835 | rc = -EBUSY; | ||
836 | goto out; | ||
837 | } | ||
838 | |||
839 | /* If master port has an active link, allocate net and enum peers */ | ||
840 | if (rio_mport_is_active(mport)) { | ||
841 | if (!(net = rio_alloc_net(mport))) { | ||
842 | printk(KERN_ERR "RIO: failed to allocate new net\n"); | ||
843 | rc = -ENOMEM; | ||
844 | goto out; | ||
845 | } | ||
846 | if (rio_enum_peer(net, mport, 0) < 0) { | ||
847 | /* A higher priority host won enumeration, bail. */ | ||
848 | printk(KERN_INFO | ||
849 | "RIO: master port %d device has lost enumeration to a remote host\n", | ||
850 | mport->id); | ||
851 | rio_clear_locks(mport); | ||
852 | rc = -EBUSY; | ||
853 | goto out; | ||
854 | } | ||
855 | rio_clear_locks(mport); | ||
856 | } else { | ||
857 | printk(KERN_INFO "RIO: master port %d link inactive\n", | ||
858 | mport->id); | ||
859 | rc = -EINVAL; | ||
860 | } | ||
861 | |||
862 | out: | ||
863 | return rc; | ||
864 | } | ||
865 | |||
866 | /** | ||
867 | * rio_build_route_tables- Generate route tables from switch route entries | ||
868 | * | ||
869 | * For each switch device, generate a route table by copying existing | ||
870 | * route entries from the switch. | ||
871 | */ | ||
872 | static void rio_build_route_tables(void) | ||
873 | { | ||
874 | struct rio_dev *rdev; | ||
875 | int i; | ||
876 | u8 sport; | ||
877 | |||
878 | list_for_each_entry(rdev, &rio_devices, global_list) | ||
879 | if (rio_is_switch(rdev)) | ||
880 | for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++) { | ||
881 | if (rio_route_get_entry | ||
882 | (rdev->net->hport, rdev, RIO_GLOBAL_TABLE, i, | ||
883 | &sport) < 0) | ||
884 | continue; | ||
885 | rdev->rswitch->route_table[i] = sport; | ||
886 | } | ||
887 | } | ||
888 | |||
889 | /** | ||
890 | * rio_enum_timeout- Signal that enumeration timed out | ||
891 | * @data: Address of timeout flag. | ||
892 | * | ||
893 | * When the enumeration complete timer expires, set a flag that | ||
894 | * signals to the discovery process that enumeration did not | ||
895 | * complete in a sane amount of time. | ||
896 | */ | ||
897 | static void rio_enum_timeout(unsigned long data) | ||
898 | { | ||
899 | /* Enumeration timed out, set flag */ | ||
900 | *(int *)data = 1; | ||
901 | } | ||
902 | |||
903 | /** | ||
904 | * rio_disc_mport- Start discovery through a master port | ||
905 | * @mport: Master port to send transactions | ||
906 | * | ||
907 | * Starts the discovery process. If we have an active link, | ||
908 | * then wait for the signal that enumeration is complete. | ||
909 | * When enumeration completion is signaled, start recursive | ||
910 | * peer discovery. Returns %0 if discovery succeeds or %-EBUSY | ||
911 | * on failure. | ||
912 | */ | ||
913 | int rio_disc_mport(struct rio_mport *mport) | ||
914 | { | ||
915 | struct rio_net *net = NULL; | ||
916 | int enum_timeout_flag = 0; | ||
917 | |||
918 | printk(KERN_INFO "RIO: discover master port %d, %s\n", mport->id, | ||
919 | mport->name); | ||
920 | |||
921 | /* If master port has an active link, allocate net and discover peers */ | ||
922 | if (rio_mport_is_active(mport)) { | ||
923 | if (!(net = rio_alloc_net(mport))) { | ||
924 | printk(KERN_ERR "RIO: Failed to allocate new net\n"); | ||
925 | goto bail; | ||
926 | } | ||
927 | |||
928 | pr_debug("RIO: wait for enumeration complete..."); | ||
929 | |||
930 | rio_enum_timer.expires = | ||
931 | jiffies + CONFIG_RAPIDIO_DISC_TIMEOUT * HZ; | ||
932 | rio_enum_timer.data = (unsigned long)&enum_timeout_flag; | ||
933 | add_timer(&rio_enum_timer); | ||
934 | while (!rio_enum_complete(mport)) { | ||
935 | mdelay(1); | ||
936 | if (enum_timeout_flag) { | ||
937 | del_timer_sync(&rio_enum_timer); | ||
938 | goto timeout; | ||
939 | } | ||
940 | } | ||
941 | del_timer_sync(&rio_enum_timer); | ||
942 | |||
943 | pr_debug("done\n"); | ||
944 | if (rio_disc_peer(net, mport, RIO_ANY_DESTID, 0) < 0) { | ||
945 | printk(KERN_INFO | ||
946 | "RIO: master port %d device has failed discovery\n", | ||
947 | mport->id); | ||
948 | goto bail; | ||
949 | } | ||
950 | |||
951 | rio_build_route_tables(); | ||
952 | } | ||
953 | |||
954 | return 0; | ||
955 | |||
956 | timeout: | ||
957 | pr_debug("timeout\n"); | ||
958 | bail: | ||
959 | return -EBUSY; | ||
960 | } | ||
diff --git a/drivers/rapidio/switches/Makefile b/drivers/rapidio/switches/Makefile new file mode 100644 index 000000000000..b924f8301761 --- /dev/null +++ b/drivers/rapidio/switches/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for RIO switches | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_RAPIDIO) += tsi500.o | ||
diff --git a/drivers/rapidio/switches/tsi500.c b/drivers/rapidio/switches/tsi500.c new file mode 100644 index 000000000000..c77c23bd9840 --- /dev/null +++ b/drivers/rapidio/switches/tsi500.c | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * RapidIO Tsi500 switch 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/rio.h> | ||
14 | #include <linux/rio_drv.h> | ||
15 | #include <linux/rio_ids.h> | ||
16 | #include "../rio.h" | ||
17 | |||
18 | static int | ||
19 | tsi500_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table, u16 route_destid, u8 route_port) | ||
20 | { | ||
21 | int i; | ||
22 | u32 offset = 0x10000 + 0xa00 + ((route_destid / 2)&~0x3); | ||
23 | u32 result; | ||
24 | |||
25 | if (table == 0xff) { | ||
26 | rio_mport_read_config_32(mport, destid, hopcount, offset, &result); | ||
27 | result &= ~(0xf << (4*(route_destid & 0x7))); | ||
28 | for (i=0;i<4;i++) | ||
29 | rio_mport_write_config_32(mport, destid, hopcount, offset + (0x20000*i), result | (route_port << (4*(route_destid & 0x7)))); | ||
30 | } | ||
31 | else { | ||
32 | rio_mport_read_config_32(mport, destid, hopcount, offset + (0x20000*table), &result); | ||
33 | result &= ~(0xf << (4*(route_destid & 0x7))); | ||
34 | rio_mport_write_config_32(mport, destid, hopcount, offset + (0x20000*table), result | (route_port << (4*(route_destid & 0x7)))); | ||
35 | } | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static int | ||
41 | tsi500_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table, u16 route_destid, u8 *route_port) | ||
42 | { | ||
43 | int ret = 0; | ||
44 | u32 offset = 0x10000 + 0xa00 + ((route_destid / 2)&~0x3); | ||
45 | u32 result; | ||
46 | |||
47 | if (table == 0xff) | ||
48 | rio_mport_read_config_32(mport, destid, hopcount, offset, &result); | ||
49 | else | ||
50 | rio_mport_read_config_32(mport, destid, hopcount, offset + (0x20000*table), &result); | ||
51 | |||
52 | result &= 0xf << (4*(route_destid & 0x7)); | ||
53 | *route_port = result >> (4*(route_destid & 0x7)); | ||
54 | if (*route_port > 3) | ||
55 | ret = -1; | ||
56 | |||
57 | return ret; | ||
58 | } | ||
59 | |||
60 | DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI500, tsi500_route_add_entry, tsi500_route_get_entry); | ||