Skip to content

Commit

Permalink
firewire: broadcast channel support
Browse files Browse the repository at this point in the history
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: Jay Fenlason <fenlason@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
  • Loading branch information
Jay Fenlason authored and Stefan Richter committed Mar 24, 2009
1 parent f8c2287 commit 6104ee9
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 6 deletions.
172 changes: 166 additions & 6 deletions drivers/firewire/fw-card.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,147 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc)
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[] = {
63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
};
Expand All @@ -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)
{
struct fw_card *card = container_of(work, struct fw_card, work.work);
struct fw_device *root_device;
struct fw_node *root_node, *local_node;
struct fw_device *root_device, *irm_device, *local_device;
struct fw_node *root_node, *local_node, *irm_node;
unsigned long flags;
int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode;
bool do_reset = false;
Expand All @@ -208,15 +349,18 @@ static void fw_card_bm_work(struct work_struct *work)
__be32 lock_data[2];

spin_lock_irqsave(&card->lock, flags);
card->is_irm = false;
local_node = card->local_node;
root_node = card->root_node;
irm_node = card->irm_node;

if (local_node == NULL) {
spin_unlock_irqrestore(&card->lock, flags);
goto out_put_card;
}
fw_node_get(local_node);
fw_node_get(root_node);
fw_node_get(irm_node);

generation = card->generation;
root_device = root_node->data;
Expand All @@ -225,7 +369,8 @@ static void fw_card_bm_work(struct work_struct *work)
root_device_is_cmc = root_device && root_device->cmc;
root_id = root_node->node_id;
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) ||
(card->bm_generation != generation && grace)) {
/*
Expand All @@ -240,8 +385,8 @@ static void fw_card_bm_work(struct work_struct *work)
* next generation.
*/

irm_id = card->irm_node->node_id;
if (!card->irm_node->link_on) {
irm_id = irm_node->node_id;
if (!irm_node->link_on) {
new_root_id = local_node->node_id;
fw_notify("IRM has link off, making local node (%02x) root.\n",
new_root_id);
Expand All @@ -263,9 +408,15 @@ static void fw_card_bm_work(struct work_struct *work)
goto out;

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. */
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;
}

spin_lock_irqsave(&card->lock, flags);

Expand Down Expand Up @@ -357,10 +508,19 @@ static void fw_card_bm_work(struct work_struct *work)
card->index, new_root_id, gap_count);
fw_send_phy_config(card, new_root_id, generation, gap_count);
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:
fw_node_put(root_node);
fw_node_put(local_node);
fw_node_put(irm_node);
out_put_card:
fw_card_put(card);
}
Expand Down
3 changes: 3 additions & 0 deletions drivers/firewire/fw-device.c
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,9 @@ static void fw_device_init(struct work_struct *work)
device->config_rom[3], device->config_rom[4],
1 << device->max_speed);
device->config_rom_retries = 0;
if (device->card->is_irm)
fw_irm_set_broadcast_channel_register(&device->device,
NULL);
}

/*
Expand Down
8 changes: 8 additions & 0 deletions drivers/firewire/fw-transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ struct fw_card {
u8 color; /* must be u8 to match the definition in struct fw_node */
int gap_count;
bool beta_repeaters_present;
/*
* Set if the local device is the IRM and the broadcast channel
* was allocated.
*/
bool is_irm;

int index;

Expand Down Expand Up @@ -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_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 */

0 comments on commit 6104ee9

Please sign in to comment.