summaryrefslogtreecommitdiffstats
path: root/drivers/firewire/core-card.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire/core-card.c')
-rw-r--r--drivers/firewire/core-card.c68
1 files changed, 52 insertions, 16 deletions
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index 6c316cfe70c..2bb5c036e80 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -204,6 +204,45 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc)
}
EXPORT_SYMBOL(fw_core_remove_descriptor);
+static int reset_bus(struct fw_card *card, bool short_reset)
+{
+ int reg = short_reset ? 5 : 1;
+ int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET;
+
+ return card->driver->update_phy_reg(card, reg, 0, bit);
+}
+
+void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset)
+{
+ /* We don't try hard to sort out requests of long vs. short resets. */
+ card->br_short = short_reset;
+
+ /* Use an arbitrary short delay to combine multiple reset requests. */
+ fw_card_get(card);
+ if (!schedule_delayed_work(&card->br_work,
+ delayed ? DIV_ROUND_UP(HZ, 100) : 0))
+ fw_card_put(card);
+}
+EXPORT_SYMBOL(fw_schedule_bus_reset);
+
+static void br_work(struct work_struct *work)
+{
+ struct fw_card *card = container_of(work, struct fw_card, br_work.work);
+
+ /* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */
+ if (card->reset_jiffies != 0 &&
+ time_is_after_jiffies(card->reset_jiffies + 2 * HZ)) {
+ if (!schedule_delayed_work(&card->br_work, 2 * HZ))
+ fw_card_put(card);
+ return;
+ }
+
+ fw_send_phy_config(card, FW_PHY_CONFIG_NO_NODE_ID, card->generation,
+ FW_PHY_CONFIG_CURRENT_GAP_COUNT);
+ reset_bus(card, card->br_short);
+ fw_card_put(card);
+}
+
static void allocate_broadcast_channel(struct fw_card *card, int generation)
{
int channel, bandwidth = 0;
@@ -230,13 +269,13 @@ static const char gap_count_table[] = {
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
{
fw_card_get(card);
- if (!schedule_delayed_work(&card->work, delay))
+ if (!schedule_delayed_work(&card->bm_work, delay))
fw_card_put(card);
}
-static void fw_card_bm_work(struct work_struct *work)
+static void 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, bm_work.work);
struct fw_device *root_device;
struct fw_node *root_node;
int root_id, new_root_id, irm_id, bm_id, local_id;
@@ -413,7 +452,7 @@ static void fw_card_bm_work(struct work_struct *work)
fw_notify("phy config: card %d, new root=%x, gap_count=%d\n",
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);
+ reset_bus(card, true);
/* Will allocate broadcast channel after the reset. */
goto out;
}
@@ -465,7 +504,8 @@ void fw_card_initialize(struct fw_card *card,
card->local_node = NULL;
- INIT_DELAYED_WORK(&card->work, fw_card_bm_work);
+ INIT_DELAYED_WORK(&card->br_work, br_work);
+ INIT_DELAYED_WORK(&card->bm_work, bm_work);
}
EXPORT_SYMBOL(fw_card_initialize);
@@ -491,7 +531,6 @@ int fw_card_add(struct fw_card *card,
}
EXPORT_SYMBOL(fw_card_add);
-
/*
* The next few functions implement a dummy driver that is used once a card
* driver shuts down an fw_card. This allows the driver to cleanly unload,
@@ -507,6 +546,11 @@ static int dummy_enable(struct fw_card *card,
return -1;
}
+static int dummy_read_phy_reg(struct fw_card *card, int address)
+{
+ return -ENODEV;
+}
+
static int dummy_update_phy_reg(struct fw_card *card, int address,
int clear_bits, int set_bits)
{
@@ -547,6 +591,7 @@ static int dummy_enable_phys_dma(struct fw_card *card,
static const struct fw_card_driver dummy_driver_template = {
.enable = dummy_enable,
+ .read_phy_reg = dummy_read_phy_reg,
.update_phy_reg = dummy_update_phy_reg,
.set_config_rom = dummy_set_config_rom,
.send_request = dummy_send_request,
@@ -568,7 +613,7 @@ void fw_core_remove_card(struct fw_card *card)
card->driver->update_phy_reg(card, 4,
PHY_LINK_ACTIVE | PHY_CONTENDER, 0);
- fw_core_initiate_bus_reset(card, 1);
+ fw_schedule_bus_reset(card, false, true);
mutex_lock(&card_mutex);
list_del_init(&card->link);
@@ -588,12 +633,3 @@ void fw_core_remove_card(struct fw_card *card)
WARN_ON(!list_empty(&card->transaction_list));
}
EXPORT_SYMBOL(fw_core_remove_card);
-
-int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
-{
- int reg = short_reset ? 5 : 1;
- int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET;
-
- return card->driver->update_phy_reg(card, reg, 0, bit);
-}
-EXPORT_SYMBOL(fw_core_initiate_bus_reset);