diff options
author | Kristian Høgsberg <krh@redhat.com> | 2007-01-26 00:38:45 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-03-09 16:02:48 -0500 |
commit | 931c4834c8d1e1bf0dcc256b89449a01711f970d (patch) | |
tree | 2d1b26b4418a034528acc98d38e179b57e2b3a56 /drivers/firewire | |
parent | 9fc82689bf2920e9b3a8cc1766bcb6ad6454a7c4 (diff) |
firewire: Implement compliant bus management.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire')
-rw-r--r-- | drivers/firewire/fw-card.c | 127 | ||||
-rw-r--r-- | drivers/firewire/fw-device.c | 2 | ||||
-rw-r--r-- | drivers/firewire/fw-topology.c | 7 | ||||
-rw-r--r-- | drivers/firewire/fw-transaction.h | 6 |
4 files changed, 117 insertions, 25 deletions
diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index 307c8b851382..f94874ce58af 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c | |||
@@ -92,7 +92,7 @@ generate_config_rom (struct fw_card *card, size_t *config_rom_length) | |||
92 | bib_generation(card->config_rom_generation++ % 14 + 2) | | 92 | bib_generation(card->config_rom_generation++ % 14 + 2) | |
93 | bib_max_rom(2) | | 93 | bib_max_rom(2) | |
94 | bib_max_receive(card->max_receive) | | 94 | bib_max_receive(card->max_receive) | |
95 | bib_isc | bib_cmc | bib_imc; | 95 | bib_bmc | bib_isc | bib_cmc | bib_imc; |
96 | config_rom[3] = card->guid >> 32; | 96 | config_rom[3] = card->guid >> 32; |
97 | config_rom[4] = card->guid; | 97 | config_rom[4] = card->guid; |
98 | 98 | ||
@@ -190,48 +190,137 @@ static const char gap_count_table[] = { | |||
190 | 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40 | 190 | 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40 |
191 | }; | 191 | }; |
192 | 192 | ||
193 | struct bm_data { | ||
194 | struct fw_transaction t; | ||
195 | struct { | ||
196 | __be32 arg; | ||
197 | __be32 data; | ||
198 | } lock; | ||
199 | u32 old; | ||
200 | int rcode; | ||
201 | struct completion done; | ||
202 | }; | ||
203 | |||
193 | static void | 204 | static void |
194 | fw_card_irm_work(struct work_struct *work) | 205 | complete_bm_lock(struct fw_card *card, int rcode, |
206 | void *payload, size_t length, void *data) | ||
207 | { | ||
208 | struct bm_data *bmd = data; | ||
209 | |||
210 | if (rcode == RCODE_COMPLETE) | ||
211 | bmd->old = be32_to_cpu(*(__be32 *) payload); | ||
212 | bmd->rcode = rcode; | ||
213 | complete(&bmd->done); | ||
214 | } | ||
215 | |||
216 | static void | ||
217 | fw_card_bm_work(struct work_struct *work) | ||
195 | { | 218 | { |
196 | struct fw_card *card = container_of(work, struct fw_card, work.work); | 219 | struct fw_card *card = container_of(work, struct fw_card, work.work); |
197 | struct fw_device *root; | 220 | struct fw_device *root; |
221 | struct bm_data bmd; | ||
198 | unsigned long flags; | 222 | unsigned long flags; |
199 | int root_id, new_irm_id, gap_count, generation, do_reset = 0; | 223 | int root_id, new_root_id, irm_id, gap_count, generation, grace; |
200 | 224 | int do_reset = 0; | |
201 | /* FIXME: This simple bus management unconditionally picks a | ||
202 | * cycle master if the current root can't do it. We need to | ||
203 | * not do this if there is a bus manager already. Also, some | ||
204 | * hubs set the contender bit, which is bogus, so we should | ||
205 | * probably do a little sanity check on the IRM (like, read | ||
206 | * the bandwidth register) if it's not us. */ | ||
207 | 225 | ||
208 | spin_lock_irqsave(&card->lock, flags); | 226 | spin_lock_irqsave(&card->lock, flags); |
209 | 227 | ||
210 | generation = card->generation; | 228 | generation = card->generation; |
211 | root = card->root_node->data; | 229 | root = card->root_node->data; |
212 | root_id = card->root_node->node_id; | 230 | root_id = card->root_node->node_id; |
231 | grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10)); | ||
232 | |||
233 | if (card->bm_generation + 1 == generation || | ||
234 | (card->bm_generation != generation && grace)) { | ||
235 | /* This first step is to figure out who is IRM and | ||
236 | * then try to become bus manager. If the IRM is not | ||
237 | * well defined (e.g. does not have an active link | ||
238 | * layer or does not responds to our lock request, we | ||
239 | * will have to do a little vigilante bus management. | ||
240 | * In that case, we do a goto into the gap count logic | ||
241 | * so that when we do the reset, we still optimize the | ||
242 | * gap count. That could well save a reset in the | ||
243 | * next generation. */ | ||
244 | |||
245 | irm_id = card->irm_node->node_id; | ||
246 | if (!card->irm_node->link_on) { | ||
247 | new_root_id = card->local_node->node_id; | ||
248 | fw_notify("IRM has link off, making local node (%02x) root.\n", | ||
249 | new_root_id); | ||
250 | goto pick_me; | ||
251 | } | ||
252 | |||
253 | bmd.lock.arg = cpu_to_be32(0x3f); | ||
254 | bmd.lock.data = cpu_to_be32(card->local_node->node_id); | ||
255 | |||
256 | spin_unlock_irqrestore(&card->lock, flags); | ||
257 | |||
258 | init_completion(&bmd.done); | ||
259 | fw_send_request(card, &bmd.t, TCODE_LOCK_COMPARE_SWAP, | ||
260 | irm_id, generation, | ||
261 | SCODE_100, CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID, | ||
262 | &bmd.lock, sizeof bmd.lock, | ||
263 | complete_bm_lock, &bmd); | ||
264 | wait_for_completion(&bmd.done); | ||
265 | |||
266 | if (bmd.rcode == RCODE_GENERATION) { | ||
267 | /* Another bus reset happened. Just return, | ||
268 | * the BM work has been rescheduled. */ | ||
269 | return; | ||
270 | } | ||
271 | |||
272 | if (bmd.rcode == RCODE_COMPLETE && bmd.old != 0x3f) | ||
273 | /* Somebody else is BM, let them do the work. */ | ||
274 | return; | ||
275 | |||
276 | spin_lock_irqsave(&card->lock, flags); | ||
277 | if (bmd.rcode != RCODE_COMPLETE) { | ||
278 | /* The lock request failed, maybe the IRM | ||
279 | * isn't really IRM capable after all. Let's | ||
280 | * do a bus reset and pick the local node as | ||
281 | * root, and thus, IRM. */ | ||
282 | new_root_id = card->local_node->node_id; | ||
283 | fw_notify("BM lock failed, making local node (%02x) root.\n", | ||
284 | new_root_id); | ||
285 | goto pick_me; | ||
286 | } | ||
287 | } else if (card->bm_generation != generation) { | ||
288 | /* OK, we weren't BM in the last generation, and it's | ||
289 | * less than 100ms since last bus reset. Reschedule | ||
290 | * this task 100ms from now. */ | ||
291 | spin_unlock_irqrestore(&card->lock, flags); | ||
292 | schedule_delayed_work(&card->work, DIV_ROUND_UP(HZ, 10)); | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | /* We're bus manager for this generation, so next step is to | ||
297 | * make sure we have an active cycle master and do gap count | ||
298 | * optimization. */ | ||
299 | card->bm_generation = generation; | ||
213 | 300 | ||
214 | if (root == NULL) { | 301 | if (root == NULL) { |
215 | /* Either link_on is false, or we failed to read the | 302 | /* Either link_on is false, or we failed to read the |
216 | * config rom. In either case, pick another root. */ | 303 | * config rom. In either case, pick another root. */ |
217 | new_irm_id = card->local_node->node_id; | 304 | new_root_id = card->local_node->node_id; |
218 | } else if (root->state != FW_DEVICE_RUNNING) { | 305 | } else if (root->state != FW_DEVICE_RUNNING) { |
219 | /* If we haven't probed this device yet, bail out now | 306 | /* If we haven't probed this device yet, bail out now |
220 | * and let's try again once that's done. */ | 307 | * and let's try again once that's done. */ |
221 | new_irm_id = root_id; | 308 | spin_unlock_irqrestore(&card->lock, flags); |
309 | return; | ||
222 | } else if (root->config_rom[2] & bib_cmc) { | 310 | } else if (root->config_rom[2] & bib_cmc) { |
223 | /* FIXME: I suppose we should set the cmstr bit in the | 311 | /* FIXME: I suppose we should set the cmstr bit in the |
224 | * STATE_CLEAR register of this node, as described in | 312 | * STATE_CLEAR register of this node, as described in |
225 | * 1394-1995, 8.4.2.6. Also, send out a force root | 313 | * 1394-1995, 8.4.2.6. Also, send out a force root |
226 | * packet for this node. */ | 314 | * packet for this node. */ |
227 | new_irm_id = root_id; | 315 | new_root_id = root_id; |
228 | } else { | 316 | } else { |
229 | /* Current root has an active link layer and we | 317 | /* Current root has an active link layer and we |
230 | * successfully read the config rom, but it's not | 318 | * successfully read the config rom, but it's not |
231 | * cycle master capable. */ | 319 | * cycle master capable. */ |
232 | new_irm_id = card->local_node->node_id; | 320 | new_root_id = card->local_node->node_id; |
233 | } | 321 | } |
234 | 322 | ||
323 | pick_me: | ||
235 | /* Now figure out what gap count to set. */ | 324 | /* Now figure out what gap count to set. */ |
236 | if (card->topology_type == FW_TOPOLOGY_A && | 325 | if (card->topology_type == FW_TOPOLOGY_A && |
237 | card->root_node->max_hops < ARRAY_SIZE(gap_count_table)) | 326 | card->root_node->max_hops < ARRAY_SIZE(gap_count_table)) |
@@ -243,16 +332,16 @@ fw_card_irm_work(struct work_struct *work) | |||
243 | * done less that 5 resets with the same physical topology and we | 332 | * done less that 5 resets with the same physical topology and we |
244 | * have either a new root or a new gap count setting, let's do it. */ | 333 | * have either a new root or a new gap count setting, let's do it. */ |
245 | 334 | ||
246 | if (card->irm_retries++ < 5 && | 335 | if (card->bm_retries++ < 5 && |
247 | (card->gap_count != gap_count || new_irm_id != root_id)) | 336 | (card->gap_count != gap_count || new_root_id != root_id)) |
248 | do_reset = 1; | 337 | do_reset = 1; |
249 | 338 | ||
250 | spin_unlock_irqrestore(&card->lock, flags); | 339 | spin_unlock_irqrestore(&card->lock, flags); |
251 | 340 | ||
252 | if (do_reset) { | 341 | if (do_reset) { |
253 | fw_notify("phy config: card %d, new root=%x, gap_count=%d\n", | 342 | fw_notify("phy config: card %d, new root=%x, gap_count=%d\n", |
254 | card->index, new_irm_id, gap_count); | 343 | card->index, new_root_id, gap_count); |
255 | fw_send_phy_config(card, new_irm_id, generation, gap_count); | 344 | fw_send_phy_config(card, new_root_id, generation, gap_count); |
256 | fw_core_initiate_bus_reset(card, 1); | 345 | fw_core_initiate_bus_reset(card, 1); |
257 | } | 346 | } |
258 | } | 347 | } |
@@ -294,7 +383,7 @@ fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver, | |||
294 | 383 | ||
295 | card->local_node = NULL; | 384 | card->local_node = NULL; |
296 | 385 | ||
297 | INIT_DELAYED_WORK(&card->work, fw_card_irm_work); | 386 | INIT_DELAYED_WORK(&card->work, fw_card_bm_work); |
298 | 387 | ||
299 | card->card_device.bus = &fw_bus_type; | 388 | card->card_device.bus = &fw_bus_type; |
300 | card->card_device.release = release_card; | 389 | card->card_device.release = release_card; |
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index d71824bb61cb..4ea8d4d08a61 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c | |||
@@ -449,6 +449,8 @@ static void fw_device_init(struct work_struct *work) | |||
449 | } else { | 449 | } else { |
450 | fw_notify("giving up on config rom for node id %x\n", | 450 | fw_notify("giving up on config rom for node id %x\n", |
451 | device->node_id); | 451 | device->node_id); |
452 | if (device->node == device->card->root_node) | ||
453 | schedule_delayed_work(&device->card->work, 0); | ||
452 | fw_device_release(&device->device); | 454 | fw_device_release(&device->device); |
453 | } | 455 | } |
454 | return; | 456 | return; |
diff --git a/drivers/firewire/fw-topology.c b/drivers/firewire/fw-topology.c index fc8f1e7d3b07..27c6cb9c8367 100644 --- a/drivers/firewire/fw-topology.c +++ b/drivers/firewire/fw-topology.c | |||
@@ -476,12 +476,13 @@ fw_core_handle_bus_reset(struct fw_card *card, | |||
476 | * changed, either nodes were added or removed. In that case we | 476 | * changed, either nodes were added or removed. In that case we |
477 | * reset the IRM reset counter. */ | 477 | * reset the IRM reset counter. */ |
478 | if (card->self_id_count != self_id_count) | 478 | if (card->self_id_count != self_id_count) |
479 | card->irm_retries = 0; | 479 | card->bm_retries = 0; |
480 | 480 | ||
481 | card->node_id = node_id; | 481 | card->node_id = node_id; |
482 | card->self_id_count = self_id_count; | 482 | card->self_id_count = self_id_count; |
483 | card->generation = generation; | 483 | card->generation = generation; |
484 | memcpy(card->self_ids, self_ids, self_id_count * 4); | 484 | memcpy(card->self_ids, self_ids, self_id_count * 4); |
485 | card->reset_jiffies = jiffies; | ||
485 | 486 | ||
486 | local_node = build_tree(card); | 487 | local_node = build_tree(card); |
487 | 488 | ||
@@ -497,9 +498,7 @@ fw_core_handle_bus_reset(struct fw_card *card, | |||
497 | update_tree(card, local_node); | 498 | update_tree(card, local_node); |
498 | } | 499 | } |
499 | 500 | ||
500 | /* If we're not the root node, we may have to do some IRM work. */ | 501 | schedule_delayed_work(&card->work, 0); |
501 | if (card->local_node != card->root_node) | ||
502 | schedule_delayed_work(&card->work, 0); | ||
503 | 502 | ||
504 | spin_unlock_irqrestore(&card->lock, flags); | 503 | spin_unlock_irqrestore(&card->lock, flags); |
505 | } | 504 | } |
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index ad7ba32755eb..50ec2c683a31 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h | |||
@@ -276,6 +276,7 @@ struct fw_card { | |||
276 | int current_tlabel, tlabel_mask; | 276 | int current_tlabel, tlabel_mask; |
277 | struct list_head transaction_list; | 277 | struct list_head transaction_list; |
278 | struct timer_list flush_timer; | 278 | struct timer_list flush_timer; |
279 | unsigned long reset_jiffies; | ||
279 | 280 | ||
280 | unsigned long long guid; | 281 | unsigned long long guid; |
281 | int max_receive; | 282 | int max_receive; |
@@ -301,9 +302,10 @@ struct fw_card { | |||
301 | 302 | ||
302 | struct list_head link; | 303 | struct list_head link; |
303 | 304 | ||
304 | /* Work struct for IRM duties. */ | 305 | /* Work struct for BM duties. */ |
305 | struct delayed_work work; | 306 | struct delayed_work work; |
306 | int irm_retries; | 307 | int bm_retries; |
308 | int bm_generation; | ||
307 | }; | 309 | }; |
308 | 310 | ||
309 | struct fw_card *fw_card_get(struct fw_card *card); | 311 | struct fw_card *fw_card_get(struct fw_card *card); |