Date: 2012-05-27 16:51:30 From: Stefan Richter Subject: firewire: ohci: handle register access failure in SClk domain One of the changes from OHCI-1394 v1.0 to v1.1 is that the PHY's SClk signal may sometimes not be present during normal operation, and accesses to certain registers fail then. See OHCI-1394 v1.1 sections 1.4.1, 4., and 6.1. The specification does not tell us though how to recover from this condition. So far, regAccessFail has been seen only with the following devices: - Texas Instruments PCIxx21 FireWire + CardBus + flash memory card controller in a Toshiba Satellite: https://bugzilla.redhat.com/show_bug.cgi?id=608544 - O2 Micro FireWire + flash memory card controller in various Dell laptops: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/801719 https://bugs.launchpad.net/ubuntu/+source/linux/+bug/881688 http://marc.info/?l=linux1394-devel&m=132309283531423 http://marc.info/?l=linux1394-devel&m=132368567907469 https://bugzilla.kernel.org/show_bug.cgi?id=43247 and several more reports. - Pinnacle MovieBoard: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commit;h=7f7e37115a8b http://marc.info/?l=linux1394-devel&m=130714243325962 In case of the Movieboard, the regAccessFail sightings are misleading: This controller simply starts to throw random corrupt IRQ event flags at some point. This patch adds a check for regAccessFail at each and every register access within the SClk domain. If the access failure is encountered, an error is propagated to upper layers. Tests with a prior version of this patch on an O2Micro controller have shown that retry loops do not get us out of a regAccessFail situation. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 13 - drivers/firewire/core-cdev.c | 4 drivers/firewire/core-iso.c | 4 drivers/firewire/core-transaction.c | 26 +- drivers/firewire/core.h | 6 drivers/firewire/net.c | 4 drivers/firewire/ohci.c | 423 ++++++++++++++++++++-------- drivers/firewire/ohci.h | 47 ++- include/linux/firewire.h | 2 9 files changed, 351 insertions(+), 178 deletions(-) --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -607,10 +607,7 @@ static void dummy_send_request(struct fw packet->callback(packet, card, RCODE_CANCELLED); } -static void dummy_send_response(struct fw_card *card, struct fw_packet *packet) -{ - packet->callback(packet, card, RCODE_CANCELLED); -} +#define dummy_send_response dummy_send_request static int dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet) { @@ -646,15 +643,13 @@ static int dummy_queue_iso(struct fw_iso return -ENODEV; } -static void dummy_flush_queue_iso(struct fw_iso_context *ctx) -{ -} - -static int dummy_flush_iso_completions(struct fw_iso_context *ctx) +static int dummy_flush_queue_iso(struct fw_iso_context *ctx) { return -ENODEV; } +#define dummy_flush_iso_completions dummy_flush_queue_iso + static const struct fw_card_driver dummy_driver_template = { .read_phy_reg = dummy_read_phy_reg, .update_phy_reg = dummy_update_phy_reg, --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1209,11 +1209,11 @@ static int ioctl_get_cycle_timer2(struct struct fw_card *card = client->device->card; struct timespec ts = {0, 0}; u32 cycle_time; - int ret = 0; + int ret; local_irq_disable(); - cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME); + ret = card->driver->read_csr(card, CSR_CYCLE_TIME, &cycle_time); switch (a->clk_id) { case CLOCK_REALTIME: getnstimeofday(&ts); break; --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -210,9 +210,9 @@ int fw_iso_context_queue(struct fw_iso_c } EXPORT_SYMBOL(fw_iso_context_queue); -void fw_iso_context_queue_flush(struct fw_iso_context *ctx) +int fw_iso_context_queue_flush(struct fw_iso_context *ctx) { - ctx->card->driver->flush_queue_iso(ctx); + return ctx->card->driver->flush_queue_iso(ctx); } EXPORT_SYMBOL(fw_iso_context_queue_flush); --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1109,20 +1109,28 @@ static void handle_registers(struct fw_c case CSR_CYCLE_TIME: case CSR_BUS_TIME: case CSR_BUSY_TIMEOUT: - if (tcode == TCODE_READ_QUADLET_REQUEST) - *data = cpu_to_be32(card->driver->read_csr(card, reg)); - else if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr(card, reg, be32_to_cpu(*data)); - else + if (tcode == TCODE_READ_QUADLET_REQUEST) { + if (card->driver->read_csr(card, reg, payload) < 0) + rcode = RCODE_CONFLICT_ERROR; + else + cpu_to_be32s(payload); + } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + if (card->driver->write_csr(card, reg, + be32_to_cpu(*data)) < 0) + rcode = RCODE_CONFLICT_ERROR; + } else { rcode = RCODE_TYPE_ERROR; + } break; case CSR_RESET_START: - if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr(card, CSR_STATE_CLEAR, - CSR_STATE_BIT_ABDICATE); - else + if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + if (card->driver->write_csr(card, CSR_STATE_CLEAR, + CSR_STATE_BIT_ABDICATE) < 0) + rcode = RCODE_CONFLICT_ERROR; + } else { rcode = RCODE_TYPE_ERROR; + } break; case CSR_SPLIT_TIMEOUT_HI: --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -87,8 +87,8 @@ struct fw_card_driver { int (*enable_phys_dma)(struct fw_card *card, int node_id, int generation); - u32 (*read_csr)(struct fw_card *card, int csr_offset); - void (*write_csr)(struct fw_card *card, int csr_offset, u32 value); + int (*read_csr)(struct fw_card *card, int csr_offset, u32 *value); + int (*write_csr)(struct fw_card *card, int csr_offset, u32 value); struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, @@ -105,7 +105,7 @@ struct fw_card_driver { struct fw_iso_buffer *buffer, unsigned long payload); - void (*flush_queue_iso)(struct fw_iso_context *ctx); + int (*flush_queue_iso)(struct fw_iso_context *ctx); int (*flush_iso_completions)(struct fw_iso_context *ctx); --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -880,8 +880,8 @@ static void fwnet_receive_broadcast(stru spin_unlock_irqrestore(&dev->lock, flags); if (retval >= 0) - fw_iso_context_queue_flush(dev->broadcast_rcv_context); - else + retval = fw_iso_context_queue_flush(dev->broadcast_rcv_context); + if (retval < 0) dev_err(&dev->netdev->dev, "requeue failed\n"); } --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -203,6 +203,7 @@ struct fw_ohci { */ spinlock_t lock; + spinlock_t sclk_domain_reg_lock; struct mutex phy_reg_mutex; void *misc_buffer; @@ -537,6 +538,60 @@ static inline void flush_writes(const st reg_read(ohci, OHCI1394_Version); } +/* caller must hold sclk_domain_reg_lock */ +static int check_reg_access_fail(const struct fw_ohci *ohci) +{ + u32 reg = reg_read(ohci, OHCI1394_IntEventSet); + + if (!~reg) + return -ENODEV; /* Card was ejected. */ + + if (reg & OHCI1394_regAccessFail) { + reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_regAccessFail); + /* Clear the bit before any other reg_write in SCLK domain. */ + mmiowb(); + return -EAGAIN; + } + + return 0; +} + +static int reg_rw_sclk(struct fw_ohci *ohci, int offset, u32 *data, bool read) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&ohci->sclk_domain_reg_lock, flags); + if (read) + *data = reg_read(ohci, offset); + else + reg_write(ohci, offset, *data); + ret = check_reg_access_fail(ohci); + spin_unlock_irqrestore(&ohci->sclk_domain_reg_lock, flags); + + if (ret == -EAGAIN) + dev_err(ohci->card.device, + "SClk is off, cannot %s register 0x%03x\n", + read ? "read" : "write", offset); + return ret; +} + +static int reg_read_sclk(struct fw_ohci *ohci, int offset, u32 *data) +{ + return reg_rw_sclk(ohci, offset, data, true); +} + +static int reg_write_sclk(struct fw_ohci *ohci, int offset, u32 data) +{ + return reg_rw_sclk(ohci, offset, &data, false); +} + +static int reg_write_sclk_flush(struct fw_ohci *ohci, int offset, u32 data) +{ + /* Just for documentation. reg_rw_sclk() already flushes MMIO. */ + return reg_rw_sclk(ohci, offset, &data, false); +} + /* * Beware! read_phy_reg(), write_phy_reg(), update_phy_reg(), and * read_paged_phy_reg() require the caller to hold ohci->phy_reg_mutex. @@ -546,13 +601,16 @@ static inline void flush_writes(const st static int read_phy_reg(struct fw_ohci *ohci, int addr) { u32 val; - int i; + int i, ret; - reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Read(addr)); + ret = reg_write_sclk(ohci, OHCI1394_PhyControl, + OHCI1394_PhyControl_Read(addr)); + if (ret < 0) + return ret; for (i = 0; i < 3 + 100; i++) { - val = reg_read(ohci, OHCI1394_PhyControl); - if (!~val) - return -ENODEV; /* Card was ejected. */ + ret = reg_read_sclk(ohci, OHCI1394_PhyControl, &val); + if (ret < 0) + return ret; if (val & OHCI1394_PhyControl_ReadDone) return OHCI1394_PhyControl_ReadData(val); @@ -569,16 +627,18 @@ static int read_phy_reg(struct fw_ohci * return -EBUSY; } -static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val) +static int write_phy_reg(struct fw_ohci *ohci, int addr, u32 val) { - int i; + int i, ret; - reg_write(ohci, OHCI1394_PhyControl, - OHCI1394_PhyControl_Write(addr, val)); + ret = reg_write_sclk(ohci, OHCI1394_PhyControl, + OHCI1394_PhyControl_Write(addr, val)); + if (ret < 0) + return ret; for (i = 0; i < 3 + 100; i++) { - val = reg_read(ohci, OHCI1394_PhyControl); - if (!~val) - return -ENODEV; /* Card was ejected. */ + ret = reg_read_sclk(ohci, OHCI1394_PhyControl, &val); + if (ret < 0) + return ret; if (!(val & OHCI1394_PhyControl_WritePending)) return 0; @@ -1208,13 +1268,22 @@ static struct descriptor *context_get_de static void context_run(struct context *ctx, u32 extra) { struct fw_ohci *ohci = ctx->ohci; + int regs_base = ctx->regs; - reg_write(ohci, COMMAND_PTR(ctx->regs), + reg_write(ohci, COMMAND_PTR(regs_base), le32_to_cpu(ctx->last->branch_address)); - reg_write(ohci, CONTROL_CLEAR(ctx->regs), ~0); - reg_write(ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN | extra); + + if (regs_base < OHCI1394_IsoRcvContextBase(0)) { + reg_write(ohci, CONTROL_CLEAR(regs_base), ~0); + reg_write(ohci, CONTROL_SET(regs_base), CONTEXT_RUN | extra); + flush_writes(ohci); + } else { + if (reg_write_sclk(ohci, CONTROL_CLEAR(regs_base), ~0) == 0) + reg_write_sclk_flush(ohci, CONTROL_SET(regs_base), + CONTEXT_RUN | extra); + /* FIXME handle regAccessFail? */ + } ctx->running = true; - flush_writes(ohci); } static void context_append(struct context *ctx, @@ -1235,17 +1304,30 @@ static void context_append(struct contex static void context_stop(struct context *ctx) { struct fw_ohci *ohci = ctx->ohci; + int i, ret, regs_base = ctx->regs; u32 reg; - int i; - reg_write(ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN); + if (regs_base < OHCI1394_IsoRcvContextBase(0)) + reg_write(ohci, CONTROL_CLEAR(regs_base), CONTEXT_RUN); + else + reg_write_sclk(ohci, CONTROL_CLEAR(regs_base), CONTEXT_RUN); ctx->running = false; for (i = 0; i < 1000; i++) { - reg = reg_read(ohci, CONTROL_SET(ctx->regs)); + if (regs_base < OHCI1394_IsoRcvContextBase(0)) { + reg = reg_read(ohci, CONTROL_SET(regs_base)); + if (!~reg) + return; + } else { + ret = reg_read_sclk(ohci, CONTROL_SET(regs_base), ®); + if (ret == -ENODEV) + return; + if (ret < 0) + reg = ~0; + } + if ((reg & CONTEXT_ACTIVE) == 0) return; - if (i) udelay(10); } @@ -1523,7 +1605,7 @@ static void handle_local_lock(struct fw_ struct fw_packet response; int tcode, length, ext_tcode, sel, try; __be32 *payload, lock_old; - u32 lock_arg, lock_data; + u32 lock_arg, lock_data, reg; tcode = HEADER_GET_TCODE(packet->header[0]); length = HEADER_GET_DATA_LENGTH(packet->header[3]); @@ -1548,19 +1630,23 @@ static void handle_local_lock(struct fw_ reg_write(ohci, OHCI1394_CSRCompareData, lock_arg); reg_write(ohci, OHCI1394_CSRControl, sel); - for (try = 0; try < 20; try++) - if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000) { - lock_old = cpu_to_be32(reg_read(ohci, - OHCI1394_CSRData)); - fw_fill_response(&response, packet->header, - RCODE_COMPLETE, - &lock_old, sizeof(lock_old)); - goto out; - } + for (try = 0; try < 20; try++) { + if (reg_read_sclk(ohci, OHCI1394_CSRControl, ®) < 0) + break; + + if (!(reg & OHCI1394_CSRControl_csrDone)) + continue; + + if (reg_read_sclk(ohci, OHCI1394_CSRData, ®) < 0) + break; + lock_old = cpu_to_be32(reg); + fw_fill_response(&response, packet->header, RCODE_COMPLETE, + &lock_old, sizeof(lock_old)); + goto out; + } dev_err(ohci->card.device, "swap not done (CSR lock timeout)\n"); fw_fill_response(&response, packet->header, RCODE_BUSY, NULL, 0); - out: fw_core_handle_response(&ohci->card, &response); } @@ -1631,7 +1717,10 @@ static void detect_dead_context(struct f { u32 ctl; - ctl = reg_read(ohci, CONTROL_SET(regs)); + if (regs < OHCI1394_IsoRcvContextBase(0)) + ctl = reg_read(ohci, CONTROL_SET(regs)); + else if (reg_read_sclk(ohci, CONTROL_SET(regs), &ctl) < 0) + ctl = 0; if (ctl & CONTEXT_DEAD) dev_err(ohci->card.device, "DMA context %s has stopped, error code: %s\n", @@ -1688,7 +1777,7 @@ static u32 cycle_timer_ticks(u32 cycle_t * error. (A PCI read should take at least 20 ticks of the 24.576 MHz timer to * execute, so we have enough precision to compute the ratio of the differences.) */ -static u32 get_cycle_time(struct fw_ohci *ohci) +static u32 __get_cycle_time(struct fw_ohci *ohci) { u32 c0, c1, c2; u32 t0, t1, t2; @@ -1718,16 +1807,38 @@ static u32 get_cycle_time(struct fw_ohci return c2; } +static int get_cycle_time(struct fw_ohci *ohci, u32 *value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&ohci->sclk_domain_reg_lock, flags); + *value = __get_cycle_time(ohci); + ret = check_reg_access_fail(ohci); + spin_unlock_irqrestore(&ohci->sclk_domain_reg_lock, flags); + + if (ret == -EAGAIN) + dev_err(ohci->card.device, + "SClk is off, cannot read cycle timer\n"); + return ret; +} + /* * This function has to be called at least every 64 seconds. The bus_time * field stores not only the upper 25 bits of the BUS_TIME register but also * the most significant bit of the cycle timer in bit 6 so that we can detect * changes in this bit. */ -static u32 update_bus_time(struct fw_ohci *ohci) +static int update_bus_time(struct fw_ohci *ohci, u32 *value) { - u32 cycle_time_seconds = get_cycle_time(ohci) >> 25; + u32 cycle_time, cycle_time_seconds; + int ret; + ret = get_cycle_time(ohci, &cycle_time); + if (ret < 0) + return ret; + + cycle_time_seconds = cycle_time >> 25; if (unlikely(!ohci->bus_time_running)) { reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_cycle64Seconds); ohci->bus_time = (lower_32_bits(get_seconds()) & ~0x7f) | @@ -1737,8 +1848,10 @@ static u32 update_bus_time(struct fw_ohc if ((ohci->bus_time & 0x40) != (cycle_time_seconds & 0x40)) ohci->bus_time += 0x40; + if (value) + *value = ohci->bus_time | cycle_time_seconds; - return ohci->bus_time | cycle_time_seconds; + return 0; } static int get_status_for_port(struct fw_ohci *ohci, int port_index) @@ -1786,11 +1899,13 @@ static int get_self_id_pos(struct fw_ohc */ static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count) { - int reg, i, pos, status; + int i, pos, status, ret; /* link active 1, speed 3, bridge 0, contender 1, more packets 0 */ - u32 self_id = 0x8040c800; + u32 reg, self_id = 0x8040c800; - reg = reg_read(ohci, OHCI1394_NodeID); + ret = reg_read_sclk(ohci, OHCI1394_NodeID, ®); + if (ret < 0) + return ret; if (!(reg & OHCI1394_NodeID_idValid)) { dev_notice(ohci->card.device, "node ID not valid, new bus reset in progress\n"); @@ -1836,7 +1951,8 @@ static void bus_reset_work(struct work_s dma_addr_t free_rom_bus = 0; bool is_new_root; - reg = reg_read(ohci, OHCI1394_NodeID); + if (reg_read_sclk(ohci, OHCI1394_NodeID, ®) < 0) + return; if (!(reg & OHCI1394_NodeID_idValid)) { dev_notice(ohci->card.device, "node ID not valid, new bus reset in progress\n"); @@ -1851,8 +1967,8 @@ static void bus_reset_work(struct work_s is_new_root = (reg & OHCI1394_NodeID_root) != 0; if (!(ohci->is_root && is_new_root)) - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_cycleMaster); + reg_write_sclk(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_cycleMaster); ohci->is_root = is_new_root; reg = reg_read(ohci, OHCI1394_SelfIDCount); @@ -1994,8 +2110,8 @@ static void bus_reset_work(struct work_s } #ifdef CONFIG_FIREWIRE_OHCI_REMOTE_DMA - reg_write(ohci, OHCI1394_PhyReqFilterHiSet, ~0); - reg_write(ohci, OHCI1394_PhyReqFilterLoSet, ~0); + reg_write_sclk(ohci, OHCI1394_PhyReqFilterHiSet, ~0); + reg_write_sclk(ohci, OHCI1394_PhyReqFilterLoSet, ~0); #endif spin_unlock_irq(&ohci->lock); @@ -2070,9 +2186,6 @@ static irqreturn_t irq_handler(int irq, } } - if (unlikely(event & OHCI1394_regAccessFail)) - dev_err(ohci->card.device, "register access failure\n"); - if (unlikely(event & OHCI1394_postedWriteErr)) { reg_read(ohci, OHCI1394_PostedWriteAddressHi); reg_read(ohci, OHCI1394_PostedWriteAddressLo); @@ -2086,8 +2199,8 @@ static irqreturn_t irq_handler(int irq, if (printk_ratelimit()) dev_notice(ohci->card.device, "isochronous cycle too long\n"); - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_cycleMaster); + reg_write_sclk(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_cycleMaster); } if (unlikely(event & OHCI1394_cycleInconsistent)) { @@ -2107,7 +2220,7 @@ static irqreturn_t irq_handler(int irq, if (event & OHCI1394_cycle64Seconds) { spin_lock(&ohci->lock); - update_bus_time(ohci); + update_bus_time(ohci, NULL); spin_unlock(&ohci->lock); } else flush_writes(ohci); @@ -2222,7 +2335,7 @@ static int ohci_enable(struct fw_card *c { struct fw_ohci *ohci = fw_ohci(card); struct pci_dev *dev = to_pci_dev(card->device); - u32 lps, version, irqs; + u32 lps, version, irqs, reg; int i, ret; if (software_reset(ohci)) { @@ -2253,6 +2366,7 @@ static int ohci_enable(struct fw_card *c dev_err(card->device, "failed to set Link Power Status\n"); return -EIO; } + reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_regAccessFail); if (ohci->quirks & QUIRK_TI_SLLZ059) { ret = probe_tsb41ba3d(ohci); @@ -2268,9 +2382,11 @@ static int ohci_enable(struct fw_card *c OHCI1394_HCControl_noByteSwapData); reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_cycleTimerEnable | - OHCI1394_LinkControl_cycleMaster); + ret = reg_write_sclk(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_cycleTimerEnable | + OHCI1394_LinkControl_cycleMaster); + if (ret < 0) + return ret; reg_write(ohci, OHCI1394_ATRetries, OHCI1394_MAX_AT_REQ_RETRIES | @@ -2288,9 +2404,16 @@ static int ohci_enable(struct fw_card *c } /* Get implemented bits of the priority arbitration request counter. */ - reg_write(ohci, OHCI1394_FairnessControl, 0x3f); - ohci->pri_req_max = reg_read(ohci, OHCI1394_FairnessControl) & 0x3f; - reg_write(ohci, OHCI1394_FairnessControl, 0); + ret = reg_write_sclk(ohci, OHCI1394_FairnessControl, 0x3f); + if (ret < 0) + return ret; + ret = reg_read_sclk(ohci, OHCI1394_FairnessControl, ®); + if (ret < 0) + return ret; + ret = reg_write_sclk(ohci, OHCI1394_FairnessControl, 0); + if (ret < 0) + return ret; + ohci->pri_req_max = reg & 0x3f; card->priority_budget_implemented = ohci->pri_req_max != 0; reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000); @@ -2350,24 +2473,20 @@ static int ohci_enable(struct fw_card *c be32_to_cpu(ohci->next_config_rom[2])); reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus); - reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); + ret = reg_write_sclk(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); + if (ret < 0) + goto out; if (!(ohci->quirks & QUIRK_NO_MSI)) pci_enable_msi(dev); - if (request_irq(dev->irq, irq_handler, - pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, - ohci_driver_name, ohci)) { + ret = request_irq(dev->irq, irq_handler, + pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, + ohci_driver_name, ohci); + if (ret < 0) { dev_err(card->device, "failed to allocate interrupt %d\n", dev->irq); pci_disable_msi(dev); - - if (config_rom) { - dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, - ohci->next_config_rom, - ohci->next_config_rom_bus); - ohci->next_config_rom = NULL; - } - return -EIO; + goto out; } irqs = OHCI1394_reqTxComplete | OHCI1394_respTxComplete | @@ -2375,7 +2494,6 @@ static int ohci_enable(struct fw_card *c OHCI1394_isochTx | OHCI1394_isochRx | OHCI1394_postedWriteErr | OHCI1394_selfIDComplete | - OHCI1394_regAccessFail | OHCI1394_cycleInconsistent | OHCI1394_unrecoverableError | OHCI1394_cycleTooLong | @@ -2388,9 +2506,11 @@ static int ohci_enable(struct fw_card *c OHCI1394_HCControl_linkEnable | OHCI1394_HCControl_BIBimageValid); - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_rcvSelfID | - OHCI1394_LinkControl_rcvPhyPkt); + ret = reg_write_sclk(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_rcvSelfID | + OHCI1394_LinkControl_rcvPhyPkt); + if (ret < 0) + goto out; ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -2401,6 +2521,14 @@ static int ohci_enable(struct fw_card *c fw_schedule_bus_reset(&ohci->card, false, true); return 0; + out: + if (config_rom) { + dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, + ohci->next_config_rom, + ohci->next_config_rom_bus); + ohci->next_config_rom = NULL; + } + return ret; } static int ohci_set_config_rom(struct fw_card *card, @@ -2540,7 +2668,7 @@ static int ohci_enable_phys_dma(struct f #else struct fw_ohci *ohci = fw_ohci(card); unsigned long flags; - int n, ret = 0; + int n, ret; /* * FIXME: Make sure this bitmask is cleared when we clear the busReset @@ -2561,11 +2689,11 @@ static int ohci_enable_phys_dma(struct f n = (node_id & 0xffc0) == LOCAL_BUS ? node_id & 0x3f : 63; if (n < 32) - reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 1 << n); + ret = reg_write_sclk_flush(ohci, OHCI1394_PhyReqFilterLoSet, + 1 << n); else - reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 1 << (n - 32)); - - flush_writes(ohci); + ret = reg_write_sclk_flush(ohci, OHCI1394_PhyReqFilterHiSet, + 1 << (n - 32)); out: spin_unlock_irqrestore(&ohci->lock, flags); @@ -2573,31 +2701,37 @@ static int ohci_enable_phys_dma(struct f #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */ } -static u32 ohci_read_csr(struct fw_card *card, int csr_offset) +static int ohci_read_csr(struct fw_card *card, int csr_offset, u32 *value) { struct fw_ohci *ohci = fw_ohci(card); unsigned long flags; - u32 value; + u32 tmp; + int ret; switch (csr_offset) { case CSR_STATE_CLEAR: case CSR_STATE_SET: - if (ohci->is_root && - (reg_read(ohci, OHCI1394_LinkControlSet) & - OHCI1394_LinkControl_cycleMaster)) - value = CSR_STATE_BIT_CMSTR; - else - value = 0; + if (ohci->is_root) { + ret = reg_read_sclk(ohci, OHCI1394_LinkControlSet, &tmp); + *value = tmp & OHCI1394_LinkControl_cycleMaster ? + CSR_STATE_BIT_CMSTR : 0; + } else { + ret = 0; + *value = 0; + } if (ohci->csr_state_setclear_abdicate) - value |= CSR_STATE_BIT_ABDICATE; - - return value; + *value |= CSR_STATE_BIT_ABDICATE; + break; case CSR_NODE_IDS: - return reg_read(ohci, OHCI1394_NodeID) << 16; + ret = reg_read_sclk(ohci, OHCI1394_NodeID, &tmp); + *value = tmp << 16; + ret = 0; + break; case CSR_CYCLE_TIME: - return get_cycle_time(ohci); + ret = get_cycle_time(ohci, value); + break; case CSR_BUS_TIME: /* @@ -2606,67 +2740,75 @@ static u32 ohci_read_csr(struct fw_card * better check here, too, if the bus time needs to be updated. */ spin_lock_irqsave(&ohci->lock, flags); - value = update_bus_time(ohci); + ret = update_bus_time(ohci, value); spin_unlock_irqrestore(&ohci->lock, flags); - return value; + break; case CSR_BUSY_TIMEOUT: - value = reg_read(ohci, OHCI1394_ATRetries); - return (value >> 4) & 0x0ffff00f; + *value = (reg_read(ohci, OHCI1394_ATRetries) >> 4) & 0x0ffff00f; + ret = 0; + break; case CSR_PRIORITY_BUDGET: - return (reg_read(ohci, OHCI1394_FairnessControl) & 0x3f) | - (ohci->pri_req_max << 8); + ret = reg_read_sclk(ohci, OHCI1394_FairnessControl, &tmp); + *value = (tmp & 0x3f) | (ohci->pri_req_max << 8); + break; default: - WARN_ON(1); - return 0; + ret = -EINVAL; + break; } + + return ret; } -static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value) +static int ohci_write_csr(struct fw_card *card, int csr_offset, u32 value) { struct fw_ohci *ohci = fw_ohci(card); unsigned long flags; + int ret; switch (csr_offset) { case CSR_STATE_CLEAR: - if ((value & CSR_STATE_BIT_CMSTR) && ohci->is_root) { - reg_write(ohci, OHCI1394_LinkControlClear, - OHCI1394_LinkControl_cycleMaster); - flush_writes(ohci); - } - if (value & CSR_STATE_BIT_ABDICATE) + if ((value & CSR_STATE_BIT_CMSTR) && ohci->is_root) + ret = reg_write_sclk_flush(ohci, + OHCI1394_LinkControlClear, + OHCI1394_LinkControl_cycleMaster); + else + ret = 0; + if (value & CSR_STATE_BIT_ABDICATE && ret == 0) ohci->csr_state_setclear_abdicate = false; break; case CSR_STATE_SET: - if ((value & CSR_STATE_BIT_CMSTR) && ohci->is_root) { - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_cycleMaster); - flush_writes(ohci); - } - if (value & CSR_STATE_BIT_ABDICATE) + if ((value & CSR_STATE_BIT_CMSTR) && ohci->is_root) + ret = reg_write_sclk_flush(ohci, + OHCI1394_LinkControlSet, + OHCI1394_LinkControl_cycleMaster); + else + ret = 0; + if (value & CSR_STATE_BIT_ABDICATE && ret == 0) ohci->csr_state_setclear_abdicate = true; break; case CSR_NODE_IDS: - reg_write(ohci, OHCI1394_NodeID, value >> 16); - flush_writes(ohci); + ret = reg_write_sclk_flush(ohci, OHCI1394_NodeID, value >> 16); break; case CSR_CYCLE_TIME: - reg_write(ohci, OHCI1394_IsochronousCycleTimer, value); - reg_write(ohci, OHCI1394_IntEventSet, - OHCI1394_cycleInconsistent); - flush_writes(ohci); + ret = reg_write_sclk_flush(ohci, + OHCI1394_IsochronousCycleTimer, value); + if (ret == 0) + reg_write(ohci, OHCI1394_IntEventSet, + OHCI1394_cycleInconsistent); break; case CSR_BUS_TIME: spin_lock_irqsave(&ohci->lock, flags); - ohci->bus_time = (update_bus_time(ohci) & 0x40) | + ohci->bus_time = (update_bus_time(ohci, NULL) & 0x40) | (value & ~0x7f); spin_unlock_irqrestore(&ohci->lock, flags); + ret = 0; break; case CSR_BUSY_TIMEOUT: @@ -2674,17 +2816,21 @@ static void ohci_write_csr(struct fw_car ((value & 0xf) << 8) | ((value & 0x0ffff000) << 4); reg_write(ohci, OHCI1394_ATRetries, value); flush_writes(ohci); + ret = 0; break; case CSR_PRIORITY_BUDGET: - reg_write(ohci, OHCI1394_FairnessControl, value & 0x3f); - flush_writes(ohci); + ret = reg_write_sclk_flush(ohci, OHCI1394_FairnessControl, + value & 0x3f); break; default: WARN_ON(1); + ret = -EINVAL; break; } + + return ret; } static void flush_iso_completions(struct iso_context *ctx) @@ -2876,16 +3022,26 @@ static int handle_it_packet(struct conte return 1; } -static void set_multichannel_mask(struct fw_ohci *ohci, u64 channels) +static int set_multichannel_mask(struct fw_ohci *ohci, u64 channels) { u32 hi = channels >> 32, lo = channels; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ohci->sclk_domain_reg_lock, flags); reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, ~hi); reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, ~lo); reg_write(ohci, OHCI1394_IRMultiChanMaskHiSet, hi); reg_write(ohci, OHCI1394_IRMultiChanMaskLoSet, lo); - mmiowb(); - ohci->mc_channels = channels; + ret = check_reg_access_fail(ohci); + if (ret == 0) + ohci->mc_channels = channels; + /* Required mmiowb() is provided by check_reg_access_fail(). */ + + spin_unlock_irqrestore(&ohci->sclk_domain_reg_lock, flags); + + return ret; } static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, @@ -2959,12 +3115,16 @@ static struct fw_iso_context *ohci_alloc goto out_with_header; if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) { - set_multichannel_mask(ohci, 0); ctx->mc_completed = 0; + ret = set_multichannel_mask(ohci, 0); + if (ret < 0) + goto out_with_context; } return &ctx->base; + out_with_context: + context_release(&ctx->context); out_with_header: free_page((unsigned long)ctx->header); out: @@ -3415,12 +3575,20 @@ static int ohci_queue_iso(struct fw_iso_ return ret; } -static void ohci_flush_queue_iso(struct fw_iso_context *base) +static int ohci_flush_queue_iso(struct fw_iso_context *base) { struct context *ctx = &container_of(base, struct iso_context, base)->context; + int ret, regs_base = ctx->regs; - reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); + if (regs_base < OHCI1394_IsoRcvContextBase(0)) { + ret = 0; + reg_write(ctx->ohci, CONTROL_SET(regs_base), CONTEXT_WAKE); + } else { + ret = reg_write_sclk(ctx->ohci, CONTROL_SET(regs_base), + CONTEXT_WAKE); + } + return ret; } static int ohci_flush_iso_completions(struct fw_iso_context *base) @@ -3542,6 +3710,7 @@ static int __devinit pci_probe(struct pc pci_set_drvdata(dev, ohci); spin_lock_init(&ohci->lock); + spin_lock_init(&ohci->sclk_domain_reg_lock); mutex_init(&ohci->phy_reg_mutex); INIT_WORK(&ohci->bus_reset_work, bus_reset_work); --- a/drivers/firewire/ohci.h +++ b/drivers/firewire/ohci.h @@ -6,9 +6,10 @@ #define OHCI1394_Version 0x000 #define OHCI1394_GUID_ROM 0x004 #define OHCI1394_ATRetries 0x008 -#define OHCI1394_CSRData 0x00C -#define OHCI1394_CSRCompareData 0x010 -#define OHCI1394_CSRControl 0x014 +#define OHCI1394_CSRData 0x00C /* SCLK domain */ +#define OHCI1394_CSRCompareData 0x010 /* SCLK domain */ +#define OHCI1394_CSRControl 0x014 /* SCLK domain */ +#define OHCI1394_CSRControl_csrDone 0x80000000 #define OHCI1394_ConfigROMhdr 0x018 #define OHCI1394_BusID 0x01C #define OHCI1394_BusOptions 0x020 @@ -31,10 +32,10 @@ #define OHCI1394_SelfIDBuffer 0x064 #define OHCI1394_SelfIDCount 0x068 #define OHCI1394_SelfIDCount_selfIDError 0x80000000 -#define OHCI1394_IRMultiChanMaskHiSet 0x070 -#define OHCI1394_IRMultiChanMaskHiClear 0x074 -#define OHCI1394_IRMultiChanMaskLoSet 0x078 -#define OHCI1394_IRMultiChanMaskLoClear 0x07C +#define OHCI1394_IRMultiChanMaskHiSet 0x070 /* SCLK domain */ +#define OHCI1394_IRMultiChanMaskHiClear 0x074 /* SCLK domain */ +#define OHCI1394_IRMultiChanMaskLoSet 0x078 /* SCLK domain */ +#define OHCI1394_IRMultiChanMaskLoClear 0x07C /* SCLK domain */ #define OHCI1394_IntEventSet 0x080 #define OHCI1394_IntEventClear 0x084 #define OHCI1394_IntMaskSet 0x088 @@ -50,34 +51,34 @@ #define OHCI1394_InitialBandwidthAvailable 0x0B0 #define OHCI1394_InitialChannelsAvailableHi 0x0B4 #define OHCI1394_InitialChannelsAvailableLo 0x0B8 -#define OHCI1394_FairnessControl 0x0DC -#define OHCI1394_LinkControlSet 0x0E0 -#define OHCI1394_LinkControlClear 0x0E4 +#define OHCI1394_FairnessControl 0x0DC /* SCLK domain */ +#define OHCI1394_LinkControlSet 0x0E0 /* SCLK domain */ +#define OHCI1394_LinkControlClear 0x0E4 /* SCLK domain */ #define OHCI1394_LinkControl_rcvSelfID (1 << 9) #define OHCI1394_LinkControl_rcvPhyPkt (1 << 10) #define OHCI1394_LinkControl_cycleTimerEnable (1 << 20) #define OHCI1394_LinkControl_cycleMaster (1 << 21) #define OHCI1394_LinkControl_cycleSource (1 << 22) -#define OHCI1394_NodeID 0x0E8 +#define OHCI1394_NodeID 0x0E8 /* SCLK domain */ #define OHCI1394_NodeID_idValid 0x80000000 #define OHCI1394_NodeID_root 0x40000000 #define OHCI1394_NodeID_nodeNumber 0x0000003f #define OHCI1394_NodeID_busNumber 0x0000ffc0 -#define OHCI1394_PhyControl 0x0EC +#define OHCI1394_PhyControl 0x0EC /* SCLK domain */ #define OHCI1394_PhyControl_Read(addr) (((addr) << 8) | 0x00008000) #define OHCI1394_PhyControl_ReadDone 0x80000000 #define OHCI1394_PhyControl_ReadData(r) (((r) & 0x00ff0000) >> 16) #define OHCI1394_PhyControl_Write(addr, data) (((addr) << 8) | (data) | 0x00004000) #define OHCI1394_PhyControl_WritePending 0x00004000 -#define OHCI1394_IsochronousCycleTimer 0x0F0 -#define OHCI1394_AsReqFilterHiSet 0x100 -#define OHCI1394_AsReqFilterHiClear 0x104 -#define OHCI1394_AsReqFilterLoSet 0x108 -#define OHCI1394_AsReqFilterLoClear 0x10C -#define OHCI1394_PhyReqFilterHiSet 0x110 -#define OHCI1394_PhyReqFilterHiClear 0x114 -#define OHCI1394_PhyReqFilterLoSet 0x118 -#define OHCI1394_PhyReqFilterLoClear 0x11C +#define OHCI1394_IsochronousCycleTimer 0x0F0 /* SCLK domain */ +#define OHCI1394_AsReqFilterHiSet 0x100 /* SCLK domain */ +#define OHCI1394_AsReqFilterHiClear 0x104 /* SCLK domain */ +#define OHCI1394_AsReqFilterLoSet 0x108 /* SCLK domain */ +#define OHCI1394_AsReqFilterLoClear 0x10C /* SCLK domain */ +#define OHCI1394_PhyReqFilterHiSet 0x110 /* SCLK domain */ +#define OHCI1394_PhyReqFilterHiClear 0x114 /* SCLK domain */ +#define OHCI1394_PhyReqFilterLoSet 0x118 /* SCLK domain */ +#define OHCI1394_PhyReqFilterLoClear 0x11C /* SCLK domain */ #define OHCI1394_PhyUpperBound 0x120 #define OHCI1394_AsReqTrContextBase 0x180 @@ -108,8 +109,8 @@ /* Isochronous receive registers */ #define OHCI1394_IsoRcvContextBase(n) (0x400 + 32 * (n)) -#define OHCI1394_IsoRcvContextControlSet(n) (0x400 + 32 * (n)) -#define OHCI1394_IsoRcvContextControlClear(n) (0x404 + 32 * (n)) +#define OHCI1394_IsoRcvContextControlSet(n) (0x400 + 32 * (n)) /* SCLK domain */ +#define OHCI1394_IsoRcvContextControlClear(n) (0x404 + 32 * (n)) /* SCLK domain */ #define OHCI1394_IsoRcvCommandPtr(n) (0x40C + 32 * (n)) #define OHCI1394_IsoRcvContextMatch(n) (0x410 + 32 * (n)) --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -442,7 +442,7 @@ int fw_iso_context_queue(struct fw_iso_c struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, unsigned long payload); -void fw_iso_context_queue_flush(struct fw_iso_context *ctx); +int fw_iso_context_queue_flush(struct fw_iso_context *ctx); int fw_iso_context_flush_completions(struct fw_iso_context *ctx); int fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags);