diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2010-06-10 08:33:07 +0200 |
---|---|---|
committer | Clemens Ladisch <clemens@ladisch.de> | 2010-06-10 08:33:07 +0200 |
commit | a48777e03ad53777ed119a5f86dd22a6c5a378ad (patch) | |
tree | 651a3fb567a2b9d6ab111ff07415d87397ba8202 /drivers/firewire | |
parent | 9ab5071cd4a16001e4ba790172a7da5e4172462b (diff) | |
download | kernel_goldelico_gta04-a48777e03ad53777ed119a5f86dd22a6c5a378ad.zip kernel_goldelico_gta04-a48777e03ad53777ed119a5f86dd22a6c5a378ad.tar.gz kernel_goldelico_gta04-a48777e03ad53777ed119a5f86dd22a6c5a378ad.tar.bz2 |
firewire: add CSR BUS_TIME support
Implement the BUS_TIME register, which is required for cycle master
capable nodes and tested for by the Base 1393 Test Suite. Even when
there is not yet bus master initialization support, this register allows
us to work together with other bus masters.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Diffstat (limited to 'drivers/firewire')
-rw-r--r-- | drivers/firewire/core-transaction.c | 14 | ||||
-rw-r--r-- | drivers/firewire/ohci.c | 168 |
2 files changed, 120 insertions, 62 deletions
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index e3925f6..2a39072 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1104,6 +1104,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + case CSR_BUS_TIME: + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_BUS_TIME)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_BUS_TIME, + be32_to_cpu(*data)); + else + rcode = RCODE_TYPE_ERROR; + break; + case CSR_BROADCAST_CHANNEL: if (tcode == TCODE_READ_QUADLET_REQUEST) *data = cpu_to_be32(card->broadcast_channel); @@ -1132,9 +1143,6 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, case CSR_BUSY_TIMEOUT: /* FIXME: Implement this. */ - case CSR_BUS_TIME: - /* Useless without initialization by the bus manager. */ - default: rcode = RCODE_ADDRESS_ERROR; break; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 777811a..3d4badb 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -170,6 +170,7 @@ struct fw_ohci { int generation; int request_generation; /* for timestamping incoming requests */ unsigned quirks; + u32 bus_time; /* * Spinlock for accessing fw_ohci data. Never call out of @@ -292,7 +293,7 @@ static void log_irqs(u32 evt) !(evt & OHCI1394_busReset)) return; - fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, + fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, evt & OHCI1394_selfIDComplete ? " selfID" : "", evt & OHCI1394_RQPkt ? " AR_req" : "", evt & OHCI1394_RSPkt ? " AR_resp" : "", @@ -302,6 +303,7 @@ static void log_irqs(u32 evt) evt & OHCI1394_isochTx ? " IT" : "", evt & OHCI1394_postedWriteErr ? " postedWriteErr" : "", evt & OHCI1394_cycleTooLong ? " cycleTooLong" : "", + evt & OHCI1394_cycle64Seconds ? " cycle64Seconds" : "", evt & OHCI1394_cycleInconsistent ? " cycleInconsistent" : "", evt & OHCI1394_regAccessFail ? " regAccessFail" : "", evt & OHCI1394_busReset ? " busReset" : "", @@ -309,7 +311,8 @@ static void log_irqs(u32 evt) OHCI1394_RSPkt | OHCI1394_reqTxComplete | OHCI1394_respTxComplete | OHCI1394_isochRx | OHCI1394_isochTx | OHCI1394_postedWriteErr | - OHCI1394_cycleTooLong | OHCI1394_cycleInconsistent | + OHCI1394_cycleTooLong | OHCI1394_cycle64Seconds | + OHCI1394_cycleInconsistent | OHCI1394_regAccessFail | OHCI1394_busReset) ? " ?" : ""); } @@ -1316,6 +1319,78 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet) } +static u32 cycle_timer_ticks(u32 cycle_timer) +{ + u32 ticks; + + ticks = cycle_timer & 0xfff; + ticks += 3072 * ((cycle_timer >> 12) & 0x1fff); + ticks += (3072 * 8000) * (cycle_timer >> 25); + + return ticks; +} + +/* + * Some controllers exhibit one or more of the following bugs when updating the + * iso cycle timer register: + * - When the lowest six bits are wrapping around to zero, a read that happens + * at the same time will return garbage in the lowest ten bits. + * - When the cycleOffset field wraps around to zero, the cycleCount field is + * not incremented for about 60 ns. + * - Occasionally, the entire register reads zero. + * + * To catch these, we read the register three times and ensure that the + * difference between each two consecutive reads is approximately the same, i.e. + * less than twice the other. Furthermore, any negative difference indicates an + * 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) +{ + u32 c0, c1, c2; + u32 t0, t1, t2; + s32 diff01, diff12; + int i; + + c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + + if (ohci->quirks & QUIRK_CYCLE_TIMER) { + i = 0; + c1 = c2; + c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + do { + c0 = c1; + c1 = c2; + c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + t0 = cycle_timer_ticks(c0); + t1 = cycle_timer_ticks(c1); + t2 = cycle_timer_ticks(c2); + diff01 = t1 - t0; + diff12 = t2 - t1; + } while ((diff01 <= 0 || diff12 <= 0 || + diff01 / diff12 >= 2 || diff12 / diff01 >= 2) + && i++ < 20); + } + + return c2; +} + +/* + * 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) +{ + u32 cycle_time_seconds = get_cycle_time(ohci) >> 25; + + if ((ohci->bus_time & 0x40) != (cycle_time_seconds & 0x40)) + ohci->bus_time += 0x40; + + return ohci->bus_time | cycle_time_seconds; +} + static void bus_reset_tasklet(unsigned long data) { struct fw_ohci *ohci = (struct fw_ohci *)data; @@ -1520,6 +1595,12 @@ static irqreturn_t irq_handler(int irq, void *data) fw_notify("isochronous cycle inconsistent\n"); } + if (event & OHCI1394_cycle64Seconds) { + spin_lock(&ohci->lock); + update_bus_time(ohci); + spin_unlock(&ohci->lock); + } + return IRQ_HANDLED; } @@ -1604,7 +1685,7 @@ static int ohci_enable(struct fw_card *card, { struct fw_ohci *ohci = fw_ohci(card); struct pci_dev *dev = to_pci_dev(card->device); - u32 lps, irqs; + u32 lps, seconds, irqs; int i, ret; if (software_reset(ohci)) { @@ -1652,6 +1733,10 @@ static int ohci_enable(struct fw_card *card, (OHCI1394_MAX_AT_RESP_RETRIES << 4) | (OHCI1394_MAX_PHYS_RESP_RETRIES << 8)); + seconds = lower_32_bits(get_seconds()); + reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); + ohci->bus_time = seconds & ~0x3f; + ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -1732,6 +1817,7 @@ static int ohci_enable(struct fw_card *card, OHCI1394_postedWriteErr | OHCI1394_selfIDComplete | OHCI1394_regAccessFail | + OHCI1394_cycle64Seconds | OHCI1394_cycleInconsistent | OHCI1394_cycleTooLong | OHCI1394_masterIntEnable; if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) @@ -1913,65 +1999,11 @@ static int ohci_enable_phys_dma(struct fw_card *card, #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */ } -static u32 cycle_timer_ticks(u32 cycle_timer) -{ - u32 ticks; - - ticks = cycle_timer & 0xfff; - ticks += 3072 * ((cycle_timer >> 12) & 0x1fff); - ticks += (3072 * 8000) * (cycle_timer >> 25); - - return ticks; -} - -/* - * Some controllers exhibit one or more of the following bugs when updating the - * iso cycle timer register: - * - When the lowest six bits are wrapping around to zero, a read that happens - * at the same time will return garbage in the lowest ten bits. - * - When the cycleOffset field wraps around to zero, the cycleCount field is - * not incremented for about 60 ns. - * - Occasionally, the entire register reads zero. - * - * To catch these, we read the register three times and ensure that the - * difference between each two consecutive reads is approximately the same, i.e. - * less than twice the other. Furthermore, any negative difference indicates an - * 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) -{ - u32 c0, c1, c2; - u32 t0, t1, t2; - s32 diff01, diff12; - int i; - - c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - - if (ohci->quirks & QUIRK_CYCLE_TIMER) { - i = 0; - c1 = c2; - c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - do { - c0 = c1; - c1 = c2; - c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - t0 = cycle_timer_ticks(c0); - t1 = cycle_timer_ticks(c1); - t2 = cycle_timer_ticks(c2); - diff01 = t1 - t0; - diff12 = t2 - t1; - } while ((diff01 <= 0 || diff12 <= 0 || - diff01 / diff12 >= 2 || diff12 / diff01 >= 2) - && i++ < 20); - } - - return c2; -} - static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) { struct fw_ohci *ohci = fw_ohci(card); + unsigned long flags; + u32 value; switch (csr_offset) { case CSR_NODE_IDS: @@ -1980,6 +2012,17 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) case CSR_CYCLE_TIME: return get_cycle_time(ohci); + case CSR_BUS_TIME: + /* + * We might be called just after the cycle timer has wrapped + * around but just before the cycle64Seconds handler, so we + * better check here, too, if the bus time needs to be updated. + */ + spin_lock_irqsave(&ohci->lock, flags); + value = update_bus_time(ohci); + spin_unlock_irqrestore(&ohci->lock, flags); + return value; + default: WARN_ON(1); return 0; @@ -1989,6 +2032,7 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) { struct fw_ohci *ohci = fw_ohci(card); + unsigned long flags; switch (csr_offset) { case CSR_NODE_IDS: @@ -2003,6 +2047,12 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) flush_writes(ohci); break; + case CSR_BUS_TIME: + spin_lock_irqsave(&ohci->lock, flags); + ohci->bus_time = (ohci->bus_time & 0x7f) | (value & ~0x7f); + spin_unlock_irqrestore(&ohci->lock, flags); + break; + default: WARN_ON(1); break; |