Commit 6104ee92 authored by Jay Fenlason's avatar Jay Fenlason Committed by Stefan Richter

firewire: broadcast channel support

This patch adds the ISO broadcast channel support that is required of a
1394a IRM.  In specific, if the local device the IRM, it allocates ISO
channel 31 and sets the broadcast channel register of all devices on the
local bus to BROADCAST_CHANNEL_INITIAL | BROADCAST_CHANNEL_VALID to indicate
that channel 31 can be use for broadcast messages.

One minor complication is that on startup the local device may become IRM
before all the devices on the bus have been enumerated by the stack.  Therefore
we have to keep a "the local device is IRM" flag and possibly set the
broadcast channel register of new devices at enumeration time.
Signed-off-by: default avatarJay Fenlason <fenlason@redhat.com>
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent f8c2287c
...@@ -181,6 +181,147 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc) ...@@ -181,6 +181,147 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc)
mutex_unlock(&card_mutex); mutex_unlock(&card_mutex);
} }
/* ------------------------------------------------------------------ */
/* Code to handle 1394a broadcast channel */
#define THIRTY_TWO_CHANNELS (0xFFFFFFFFU)
#define IRM_RETRIES 2
/*
* The abi is set by device_for_each_child(), even though we have no use
* for data, nor do we have a meaningful return value.
*/
int fw_irm_set_broadcast_channel_register(struct device *dev, void *data)
{
struct fw_device *d;
int rcode;
int node_id;
int max_speed;
int retries;
int generation;
__be32 regval;
struct fw_card *card;
d = fw_device(dev);
/* FIXME: do we need locking here? */
generation = d->generation;
smp_rmb(); /* Ensure generation is at least as old as node_id */
node_id = d->node_id;
max_speed = d->max_speed;
retries = IRM_RETRIES;
card = d->card;
tryagain_r:
rcode = fw_run_transaction(card, TCODE_READ_QUADLET_REQUEST,
node_id, generation, max_speed,
CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL,
&regval, 4);
switch (rcode) {
case RCODE_BUSY:
if (retries--)
goto tryagain_r;
fw_notify("node %x read broadcast channel busy\n",
node_id);
return 0;
default:
fw_notify("node %x read broadcast channel failed %x\n",
node_id, rcode);
return 0;
case RCODE_COMPLETE:
/*
* Paranoid reporting of nonstandard broadcast channel
* contents goes here
*/
if (regval != cpu_to_be32(BROADCAST_CHANNEL_INITIAL))
return 0;
break;
}
retries = IRM_RETRIES;
regval = cpu_to_be32(BROADCAST_CHANNEL_INITIAL |
BROADCAST_CHANNEL_VALID);
tryagain_w:
rcode = fw_run_transaction(card,
TCODE_WRITE_QUADLET_REQUEST, node_id,
generation, max_speed,
CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL,
&regval, 4);
switch (rcode) {
case RCODE_BUSY:
if (retries--)
goto tryagain_w;
fw_notify("node %x write broadcast channel busy\n",
node_id);
return 0;
default:
fw_notify("node %x write broadcast channel failed %x\n",
node_id, rcode);
return 0;
case RCODE_COMPLETE:
return 0;
}
return 0;
}
static void
irm_allocate_broadcast(struct fw_device *irm_dev, struct device *locald)
{
u32 generation;
u32 node_id;
u32 max_speed;
u32 retries;
__be32 old_data;
__be32 lock_data[2];
int rcode;
/*
* The device we are updating is the IRM, so we must do
* some extra work.
*/
retries = IRM_RETRIES;
generation = irm_dev->generation;
/* FIXME: do we need locking here? */
smp_rmb();
node_id = irm_dev->node_id;
max_speed = irm_dev->max_speed;
lock_data[0] = cpu_to_be32(THIRTY_TWO_CHANNELS);
lock_data[1] = cpu_to_be32(THIRTY_TWO_CHANNELS & ~1);
tryagain:
old_data = lock_data[0];
rcode = fw_run_transaction(irm_dev->card, TCODE_LOCK_COMPARE_SWAP,
node_id, generation, max_speed,
CSR_REGISTER_BASE+CSR_CHANNELS_AVAILABLE_HI,
&lock_data[0], 8);
switch (rcode) {
case RCODE_BUSY:
if (retries--)
goto tryagain;
/* fallthrough */
default:
fw_error("node %x: allocate broadcast channel failed (%x)\n",
node_id, rcode);
return;
case RCODE_COMPLETE:
if (lock_data[0] == old_data)
break;
if (retries--) {
lock_data[1] = cpu_to_be32(be32_to_cpu(lock_data[0])&~1);
goto tryagain;
}
fw_error("node %x: allocate broadcast channel failed: too many"
" retries\n", node_id);
return;
}
irm_dev->card->is_irm = true;
device_for_each_child(locald, NULL, fw_irm_set_broadcast_channel_register);
}
/* ------------------------------------------------------------------ */
static const char gap_count_table[] = { static const char gap_count_table[] = {
63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
}; };
...@@ -198,8 +339,8 @@ void fw_schedule_bm_work(struct fw_card *card, unsigned long delay) ...@@ -198,8 +339,8 @@ void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
static void fw_card_bm_work(struct work_struct *work) static void fw_card_bm_work(struct work_struct *work)
{ {
struct fw_card *card = container_of(work, struct fw_card, work.work); struct fw_card *card = container_of(work, struct fw_card, work.work);
struct fw_device *root_device; struct fw_device *root_device, *irm_device, *local_device;
struct fw_node *root_node, *local_node; struct fw_node *root_node, *local_node, *irm_node;
unsigned long flags; unsigned long flags;
int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode; int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode;
bool do_reset = false; bool do_reset = false;
...@@ -208,8 +349,10 @@ static void fw_card_bm_work(struct work_struct *work) ...@@ -208,8 +349,10 @@ static void fw_card_bm_work(struct work_struct *work)
__be32 lock_data[2]; __be32 lock_data[2];
spin_lock_irqsave(&card->lock, flags); spin_lock_irqsave(&card->lock, flags);
card->is_irm = false;
local_node = card->local_node; local_node = card->local_node;
root_node = card->root_node; root_node = card->root_node;
irm_node = card->irm_node;
if (local_node == NULL) { if (local_node == NULL) {
spin_unlock_irqrestore(&card->lock, flags); spin_unlock_irqrestore(&card->lock, flags);
...@@ -217,6 +360,7 @@ static void fw_card_bm_work(struct work_struct *work) ...@@ -217,6 +360,7 @@ static void fw_card_bm_work(struct work_struct *work)
} }
fw_node_get(local_node); fw_node_get(local_node);
fw_node_get(root_node); fw_node_get(root_node);
fw_node_get(irm_node);
generation = card->generation; generation = card->generation;
root_device = root_node->data; root_device = root_node->data;
...@@ -225,7 +369,8 @@ static void fw_card_bm_work(struct work_struct *work) ...@@ -225,7 +369,8 @@ static void fw_card_bm_work(struct work_struct *work)
root_device_is_cmc = root_device && root_device->cmc; root_device_is_cmc = root_device && root_device->cmc;
root_id = root_node->node_id; root_id = root_node->node_id;
grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10)); grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10));
irm_device = irm_node->data;
local_device = local_node->data;
if (is_next_generation(generation, card->bm_generation) || if (is_next_generation(generation, card->bm_generation) ||
(card->bm_generation != generation && grace)) { (card->bm_generation != generation && grace)) {
/* /*
...@@ -240,8 +385,8 @@ static void fw_card_bm_work(struct work_struct *work) ...@@ -240,8 +385,8 @@ static void fw_card_bm_work(struct work_struct *work)
* next generation. * next generation.
*/ */
irm_id = card->irm_node->node_id; irm_id = irm_node->node_id;
if (!card->irm_node->link_on) { if (!irm_node->link_on) {
new_root_id = local_node->node_id; new_root_id = local_node->node_id;
fw_notify("IRM has link off, making local node (%02x) root.\n", fw_notify("IRM has link off, making local node (%02x) root.\n",
new_root_id); new_root_id);
...@@ -263,9 +408,15 @@ static void fw_card_bm_work(struct work_struct *work) ...@@ -263,9 +408,15 @@ static void fw_card_bm_work(struct work_struct *work)
goto out; goto out;
if (rcode == RCODE_COMPLETE && if (rcode == RCODE_COMPLETE &&
lock_data[0] != cpu_to_be32(0x3f)) lock_data[0] != cpu_to_be32(0x3f)) {
/* Somebody else is BM, let them do the work. */ /* Somebody else is BM, let them do the work. */
if (irm_id == local_node->node_id) {
/* But we are IRM, so do irm-y things */
irm_allocate_broadcast(irm_device,
card->device);
}
goto out; goto out;
}
spin_lock_irqsave(&card->lock, flags); spin_lock_irqsave(&card->lock, flags);
...@@ -357,10 +508,19 @@ static void fw_card_bm_work(struct work_struct *work) ...@@ -357,10 +508,19 @@ static void fw_card_bm_work(struct work_struct *work)
card->index, new_root_id, gap_count); card->index, new_root_id, gap_count);
fw_send_phy_config(card, new_root_id, generation, gap_count); fw_send_phy_config(card, new_root_id, generation, gap_count);
fw_core_initiate_bus_reset(card, 1); fw_core_initiate_bus_reset(card, 1);
} else if (irm_node->node_id == local_node->node_id) {
/*
* We are IRM, so do irm-y things.
* There's no reason to do this if we're doing a reset. . .
* We'll be back.
*/
irm_allocate_broadcast(irm_device, card->device);
} }
out: out:
fw_node_put(root_node); fw_node_put(root_node);
fw_node_put(local_node); fw_node_put(local_node);
fw_node_put(irm_node);
out_put_card: out_put_card:
fw_card_put(card); fw_card_put(card);
} }
......
...@@ -849,6 +849,9 @@ static void fw_device_init(struct work_struct *work) ...@@ -849,6 +849,9 @@ static void fw_device_init(struct work_struct *work)
device->config_rom[3], device->config_rom[4], device->config_rom[3], device->config_rom[4],
1 << device->max_speed); 1 << device->max_speed);
device->config_rom_retries = 0; device->config_rom_retries = 0;
if (device->card->is_irm)
fw_irm_set_broadcast_channel_register(&device->device,
NULL);
} }
/* /*
......
...@@ -230,6 +230,11 @@ struct fw_card { ...@@ -230,6 +230,11 @@ struct fw_card {
u8 color; /* must be u8 to match the definition in struct fw_node */ u8 color; /* must be u8 to match the definition in struct fw_node */
int gap_count; int gap_count;
bool beta_repeaters_present; bool beta_repeaters_present;
/*
* Set if the local device is the IRM and the broadcast channel
* was allocated.
*/
bool is_irm;
int index; int index;
...@@ -438,4 +443,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, ...@@ -438,4 +443,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id,
void fw_core_handle_request(struct fw_card *card, struct fw_packet *request); void fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
extern int fw_irm_set_broadcast_channel_register(struct device *dev,
void *data);
#endif /* __fw_transaction_h */ #endif /* __fw_transaction_h */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment