From 148c7866c31d93f8c79366189075f5a26ad4556c Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 5 Jun 2010 11:46:49 +0200 Subject: firewire: ohci: do not enable interrupts without the handler On 26 Apr 2010, Clemens Ladisch wrote: > In theory, none of the interrupts should occur before the link is > enabled. In practice, I'd rather make sure to not set the master > interrupt enable bit until we have installed the interrupt handler. and proposed to move OHCI1394_masterIntEnable out of the present reg_write() into a new one before the HCControl.linkEnable reg_write(). Why not defer setting /all/ of the bits until right before linkEnable? Reviewed-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 07deac77bc13..9743a405e69c 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1594,7 +1594,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; + u32 lps, irqs; int i, ret; if (software_reset(ohci)) { @@ -1648,16 +1648,6 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000); reg_write(ohci, OHCI1394_IntEventClear, ~0); reg_write(ohci, OHCI1394_IntMaskClear, ~0); - reg_write(ohci, OHCI1394_IntMaskSet, - OHCI1394_selfIDComplete | - OHCI1394_RQPkt | OHCI1394_RSPkt | - OHCI1394_reqTxComplete | OHCI1394_respTxComplete | - OHCI1394_isochRx | OHCI1394_isochTx | - OHCI1394_postedWriteErr | OHCI1394_cycleTooLong | - OHCI1394_cycleInconsistent | OHCI1394_regAccessFail | - OHCI1394_masterIntEnable); - if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) - reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); ret = configure_1394a_enhancements(ohci); if (ret < 0) @@ -1723,6 +1713,18 @@ static int ohci_enable(struct fw_card *card, return -EIO; } + irqs = OHCI1394_reqTxComplete | OHCI1394_respTxComplete | + OHCI1394_RQPkt | OHCI1394_RSPkt | + OHCI1394_isochTx | OHCI1394_isochRx | + OHCI1394_postedWriteErr | + OHCI1394_selfIDComplete | + OHCI1394_regAccessFail | + OHCI1394_cycleInconsistent | OHCI1394_cycleTooLong | + OHCI1394_masterIntEnable; + if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) + irqs |= OHCI1394_busReset; + reg_write(ohci, OHCI1394_IntMaskSet, irqs); + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable | OHCI1394_HCControl_BIBimageValid); -- cgit v1.2.3 From 262444eecce40950af19ea4d75a3dc03b3c07283 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sat, 5 Jun 2010 12:31:25 +0200 Subject: firewire: ohci: add MSI support This patch adds support for message-signaled interrupts. Any native PCI-Express OHCI controller should support MSI, but most are just PCI cores behind a PCI-E/PCI bridge. The only chips that are known to claim to support MSI are the Lucent/Agere/LSI FW643 and the VIA VT6315, none of which I have been able to test. Due to the high level of trust I have in the competence of these and any future chip makers, I thought it a good idea to add a disable-MSI quirk. Signed-off-by: Clemens Ladisch Tested Agere FW643 rev 07 [11c1:5901] and JMicron JMB381 [197b:2380]. Added a quirks list entry for JMB38X since it kept its count of MSI events consistently at zero. Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 9743a405e69c..de5ff376231c 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -231,12 +231,14 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) static char ohci_driver_name[] = KBUILD_MODNAME; +#define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380 #define PCI_DEVICE_ID_TI_TSB12LV22 0x8009 #define QUIRK_CYCLE_TIMER 1 #define QUIRK_RESET_PACKET 2 #define QUIRK_BE_HEADERS 4 #define QUIRK_NO_1394A 8 +#define QUIRK_NO_MSI 16 /* In case of multiple matches in ohci_quirks[], only the first one is used. */ static const struct { @@ -247,6 +249,7 @@ static const struct { QUIRK_NO_1394A}, {PCI_VENDOR_ID_TI, PCI_ANY_ID, QUIRK_RESET_PACKET}, {PCI_VENDOR_ID_AL, PCI_ANY_ID, QUIRK_CYCLE_TIMER}, + {PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_FW, QUIRK_NO_MSI}, {PCI_VENDOR_ID_NEC, PCI_ANY_ID, QUIRK_CYCLE_TIMER}, {PCI_VENDOR_ID_VIA, PCI_ANY_ID, QUIRK_CYCLE_TIMER}, {PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_FW, QUIRK_BE_HEADERS}, @@ -260,6 +263,7 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0" ", reset packet generation = " __stringify(QUIRK_RESET_PACKET) ", AR/selfID endianess = " __stringify(QUIRK_BE_HEADERS) ", no 1394a enhancements = " __stringify(QUIRK_NO_1394A) + ", disable MSI = " __stringify(QUIRK_NO_MSI) ")"); #define OHCI_PARAM_DEBUG_AT_AR 1 @@ -1704,10 +1708,13 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); + if (!(ohci->quirks & QUIRK_NO_MSI)) + pci_enable_msi(dev); if (request_irq(dev->irq, irq_handler, - IRQF_SHARED, ohci_driver_name, ohci)) { - fw_error("Failed to allocate shared interrupt %d.\n", - dev->irq); + pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, + ohci_driver_name, ohci)) { + fw_error("Failed to allocate interrupt %d.\n", dev->irq); + pci_disable_msi(dev); dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, ohci->config_rom, ohci->config_rom_bus); return -EIO; @@ -2622,6 +2629,7 @@ static void pci_remove(struct pci_dev *dev) context_release(&ohci->at_response_ctx); kfree(ohci->it_context_list); kfree(ohci->ir_context_list); + pci_disable_msi(dev); pci_iounmap(dev, ohci->registers); pci_release_region(dev, 0); pci_disable_device(dev); @@ -2639,6 +2647,7 @@ static int pci_suspend(struct pci_dev *dev, pm_message_t state) software_reset(ohci); free_irq(dev->irq, ohci); + pci_disable_msi(dev); err = pci_save_state(dev); if (err) { fw_error("pci_save_state failed\n"); -- cgit v1.2.3 From 153e3979201b76dbd5788f032fb683e95121e159 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:22:07 +0200 Subject: firewire: ohci: speed up PHY register accesses Most PHY chips, when idle, can complete a register access in the time needed for two or three PCI read transactions; bigger delays occur only when data is currently being moved over the link/PHY interface. So if we busy-wait a few times when waiting for the register access to finish, it is likely that we can finish without having to sleep. Signed-off-by: Clemens Ladisch --- drivers/firewire/ohci.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index de5ff376231c..65b9bdb8541a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -474,12 +474,17 @@ static int read_phy_reg(struct fw_ohci *ohci, int addr) int i; reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Read(addr)); - for (i = 0; i < 10; i++) { + for (i = 0; i < 3 + 100; i++) { val = reg_read(ohci, OHCI1394_PhyControl); if (val & OHCI1394_PhyControl_ReadDone) return OHCI1394_PhyControl_ReadData(val); - msleep(1); + /* + * Try a few times without waiting. Sleeping is necessary + * only when the link/PHY interface is busy. + */ + if (i >= 3) + msleep(1); } fw_error("failed to read phy reg\n"); @@ -492,12 +497,13 @@ static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val) reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Write(addr, val)); - for (i = 0; i < 100; i++) { + for (i = 0; i < 3 + 100; i++) { val = reg_read(ohci, OHCI1394_PhyControl); if (!(val & OHCI1394_PhyControl_WritePending)) return 0; - msleep(1); + if (i >= 3) + msleep(1); } fw_error("failed to write phy reg\n"); -- cgit v1.2.3 From 60d32970c5a32e8c4f340a9e41993759ad658ef2 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:24:35 +0200 Subject: firewire: add read_csr_reg driver callback To prepare for the following additions of more OHCI-implemented CSR registers, replace the get_cycle_time driver callback with a generic CSR register callback. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-cdev.c | 2 +- drivers/firewire/core-transaction.c | 3 ++- drivers/firewire/core.h | 2 +- drivers/firewire/ohci.c | 19 ++++++++++++++++--- 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 50332b84f49a..2e62516a4b15 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1044,7 +1044,7 @@ static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) local_irq_disable(); - cycle_time = card->driver->get_cycle_time(card); + cycle_time = card->driver->read_csr_reg(card, CSR_CYCLE_TIME); switch (a->clk_id) { case CLOCK_REALTIME: getnstimeofday(&ts); break; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 6971400b6354..a4d8109edec2 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1025,7 +1025,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, case CSR_CYCLE_TIME: if (TCODE_IS_READ_REQUEST(tcode) && length == 4) - *data = cpu_to_be32(card->driver->get_cycle_time(card)); + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_CYCLE_TIME)); else rcode = RCODE_TYPE_ERROR; break; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 25a72e57a0cd..c19e9873e433 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -75,7 +75,7 @@ struct fw_card_driver { int (*enable_phys_dma)(struct fw_card *card, int node_id, int generation); - u32 (*get_cycle_time)(struct fw_card *card); + u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 65b9bdb8541a..a8093a9a3fc8 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1939,9 +1939,8 @@ static u32 cycle_timer_ticks(u32 cycle_timer) * 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 ohci_get_cycle_time(struct fw_card *card) +static u32 get_cycle_time(struct fw_ohci *ohci) { - struct fw_ohci *ohci = fw_ohci(card); u32 c0, c1, c2; u32 t0, t1, t2; s32 diff01, diff12; @@ -1970,6 +1969,20 @@ static u32 ohci_get_cycle_time(struct fw_card *card) return c2; } +static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) +{ + struct fw_ohci *ohci = fw_ohci(card); + + switch (csr_offset) { + case CSR_CYCLE_TIME: + return get_cycle_time(ohci); + + default: + WARN_ON(1); + return 0; + } +} + static void copy_iso_headers(struct iso_context *ctx, void *p) { int i = ctx->header_length; @@ -2407,7 +2420,7 @@ static const struct fw_card_driver ohci_driver = { .send_response = ohci_send_response, .cancel_packet = ohci_cancel_packet, .enable_phys_dma = ohci_enable_phys_dma, - .get_cycle_time = ohci_get_cycle_time, + .read_csr_reg = ohci_read_csr_reg, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, -- cgit v1.2.3 From 506f1a31932747f56a5029d5b3c14b1b68f41ccc Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:25:19 +0200 Subject: firewire: add CSR NODE_IDS support The NODE_IDS register, and especially its bus_id field, is quite useless because 1394.1 requires that the bus_id field always stays 0x3ff. However, the 1394 specification requires this register on all transaction capable nodes, and the Base 1394 Test Suite tests for it, so we better implement it. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 11 +++++++++++ drivers/firewire/core.h | 1 + drivers/firewire/ohci.c | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index a4d8109edec2..16ffa27d23b7 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1023,6 +1023,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, } break; + case CSR_NODE_IDS: + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_NODE_IDS)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_NODE_IDS, + be32_to_cpu(*data)); + else + rcode = RCODE_TYPE_ERROR; + break; + case CSR_CYCLE_TIME: if (TCODE_IS_READ_REQUEST(tcode) && length == 4) *data = cpu_to_be32(card->driver-> diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index c19e9873e433..efcdeb2e31e6 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -76,6 +76,7 @@ struct fw_card_driver { int node_id, int generation); u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); + void (*write_csr_reg)(struct fw_card *card, int csr_offset, u32 value); struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a8093a9a3fc8..a55fbbce9e79 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1974,6 +1974,9 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) struct fw_ohci *ohci = fw_ohci(card); switch (csr_offset) { + case CSR_NODE_IDS: + return reg_read(ohci, OHCI1394_NodeID) << 16; + case CSR_CYCLE_TIME: return get_cycle_time(ohci); @@ -1983,6 +1986,22 @@ 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); + + switch (csr_offset) { + case CSR_NODE_IDS: + reg_write(ohci, OHCI1394_NodeID, value >> 16); + flush_writes(ohci); + break; + + default: + WARN_ON(1); + break; + } +} + static void copy_iso_headers(struct iso_context *ctx, void *p) { int i = ctx->header_length; @@ -2421,6 +2440,7 @@ static const struct fw_card_driver ohci_driver = { .cancel_packet = ohci_cancel_packet, .enable_phys_dma = ohci_enable_phys_dma, .read_csr_reg = ohci_read_csr_reg, + .write_csr_reg = ohci_write_csr_reg, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, -- cgit v1.2.3 From 9ab5071cd4a16001e4ba790172a7da5e4172462b Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:26:48 +0200 Subject: firewire: add CSR CYCLE_TIME write support The specification requires that CYCLE_TIME is writable so that it can be initialized, so we better implement it. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 3 +++ drivers/firewire/ohci.c | 7 +++++++ 2 files changed, 10 insertions(+) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 9a7d3ec23f2b..e3925f67ec12 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1097,6 +1097,9 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, if (TCODE_IS_READ_REQUEST(tcode) && length == 4) *data = cpu_to_be32(card->driver-> read_csr_reg(card, CSR_CYCLE_TIME)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_CYCLE_TIME, + be32_to_cpu(*data)); else rcode = RCODE_TYPE_ERROR; break; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a55fbbce9e79..777811a736b2 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1996,6 +1996,13 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) flush_writes(ohci); break; + case CSR_CYCLE_TIME: + reg_write(ohci, OHCI1394_IsochronousCycleTimer, value); + reg_write(ohci, OHCI1394_IntEventSet, + OHCI1394_cycleInconsistent); + flush_writes(ohci); + break; + default: WARN_ON(1); break; -- cgit v1.2.3 From a48777e03ad53777ed119a5f86dd22a6c5a378ad Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:33:07 +0200 Subject: 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 --- drivers/firewire/core-transaction.c | 14 ++- drivers/firewire/ohci.c | 168 +++++++++++++++++++++++------------- 2 files changed, 120 insertions(+), 62 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index e3925f67ec12..2a390726fa76 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 777811a736b2..3d4badb7c79b 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; -- cgit v1.2.3 From 27a2329f8235d6ce637463f5d83e98d760ef006e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:34:13 +0200 Subject: firewire: add CSR BUSY_TIMEOUT support Implement the BUSY_TIMEOUT register, which is required for nodes that support retries. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 14 +++++++++++--- drivers/firewire/ohci.c | 14 +++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 2a390726fa76..8146133818dc 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1115,6 +1115,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + case CSR_BUSY_TIMEOUT: + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_BUSY_TIMEOUT)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_BUSY_TIMEOUT, + 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); @@ -1140,9 +1151,6 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, BUG(); break; - case CSR_BUSY_TIMEOUT: - /* FIXME: Implement this. */ - default: rcode = RCODE_ADDRESS_ERROR; break; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 3d4badb7c79b..9c588fd01250 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1731,7 +1731,8 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_ATRetries, OHCI1394_MAX_AT_REQ_RETRIES | (OHCI1394_MAX_AT_RESP_RETRIES << 4) | - (OHCI1394_MAX_PHYS_RESP_RETRIES << 8)); + (OHCI1394_MAX_PHYS_RESP_RETRIES << 8) | + (200 << 16)); seconds = lower_32_bits(get_seconds()); reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); @@ -2023,6 +2024,10 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) spin_unlock_irqrestore(&ohci->lock, flags); return value; + case CSR_BUSY_TIMEOUT: + value = reg_read(ohci, OHCI1394_ATRetries); + return (value >> 4) & 0x0ffff00f; + default: WARN_ON(1); return 0; @@ -2053,6 +2058,13 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) spin_unlock_irqrestore(&ohci->lock, flags); break; + case CSR_BUSY_TIMEOUT: + value = (value & 0xf) | ((value & 0xf) << 4) | + ((value & 0xf) << 8) | ((value & 0x0ffff000) << 4); + reg_write(ohci, OHCI1394_ATRetries, value); + flush_writes(ohci); + break; + default: WARN_ON(1); break; -- cgit v1.2.3 From a1a1132bd83d0aea51d4f19be4b4a58a064a0131 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:35:06 +0200 Subject: firewire: add CSR PRIORITY_BUDGET support If supported by the OHCI controller, implement the PRIORITY_BUDGET register, which is required for nodes that can use asynchronous priority arbitration. To allow the core to determine what features the lowlevel device supports, add a new card driver callback. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 14 ++++++++++++++ drivers/firewire/core.h | 4 ++++ drivers/firewire/ohci.c | 27 +++++++++++++++++++++++++++ include/linux/firewire.h | 1 + 4 files changed, 46 insertions(+) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 8146133818dc..a61eb3fb9573 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1126,6 +1126,20 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + case CSR_PRIORITY_BUDGET: + if (!(card->driver->get_features(card) & + FEATURE_PRIORITY_BUDGET)) + rcode = RCODE_ADDRESS_ERROR; + else if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_PRIORITY_BUDGET)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_PRIORITY_BUDGET, + 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); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index efcdeb2e31e6..3b8c0f042f49 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -38,6 +38,8 @@ struct fw_packet; #define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31) #define BROADCAST_CHANNEL_VALID (1 << 30) +#define FEATURE_PRIORITY_BUDGET 0x01 + struct fw_card_driver { /* * Enable the given card with the given initial config rom. @@ -78,6 +80,8 @@ struct fw_card_driver { u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); void (*write_csr_reg)(struct fw_card *card, int csr_offset, u32 value); + unsigned int (*get_features)(struct fw_card *card); + struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, int type, int channel, size_t header_size); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 9c588fd01250..0e5413531785 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; + unsigned int pri_req_max; u32 bus_time; /* @@ -1738,6 +1739,11 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); ohci->bus_time = seconds & ~0x3f; + /* 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); + ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -2028,6 +2034,10 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) value = reg_read(ohci, OHCI1394_ATRetries); return (value >> 4) & 0x0ffff00f; + case CSR_PRIORITY_BUDGET: + return (reg_read(ohci, OHCI1394_FairnessControl) & 0x3f) | + (ohci->pri_req_max << 8); + default: WARN_ON(1); return 0; @@ -2065,12 +2075,28 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) flush_writes(ohci); break; + case CSR_PRIORITY_BUDGET: + reg_write(ohci, OHCI1394_FairnessControl, value & 0x3f); + flush_writes(ohci); + break; + default: WARN_ON(1); break; } } +static unsigned int ohci_get_features(struct fw_card *card) +{ + struct fw_ohci *ohci = fw_ohci(card); + unsigned int features = 0; + + if (ohci->pri_req_max != 0) + features |= FEATURE_PRIORITY_BUDGET; + + return features; +} + static void copy_iso_headers(struct iso_context *ctx, void *p) { int i = ctx->header_length; @@ -2510,6 +2536,7 @@ static const struct fw_card_driver ohci_driver = { .enable_phys_dma = ohci_enable_phys_dma, .read_csr_reg = ohci_read_csr_reg, .write_csr_reg = ohci_write_csr_reg, + .get_features = ohci_get_features, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, diff --git a/include/linux/firewire.h b/include/linux/firewire.h index cdf8213c68ca..a50377d91254 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -32,6 +32,7 @@ #define CSR_CYCLE_TIME 0x200 #define CSR_BUS_TIME 0x204 #define CSR_BUSY_TIMEOUT 0x210 +#define CSR_PRIORITY_BUDGET 0x218 #define CSR_BUS_MANAGER_ID 0x21c #define CSR_BANDWIDTH_AVAILABLE 0x220 #define CSR_CHANNELS_AVAILABLE 0x224 -- cgit v1.2.3 From 4ffb7a6a066e4be4577976d1c08e237c7479770a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:36:37 +0200 Subject: firewire: add CSR cmstr support Implement the cmstr bit, which is required for cycle master capable nodes and tested for by the Base 1394 Test Suite. This bit allows the bus master to disable cycle start packets; there are bus master implementations that actually do this. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 12 ++++++++++-- drivers/firewire/core.h | 2 ++ drivers/firewire/ohci.c | 35 +++++++++++++++++++++++++++++++++++ drivers/firewire/ohci.h | 1 + 4 files changed, 48 insertions(+), 2 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index dd8ef650a7cb..e0c6cce894cf 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1003,7 +1003,12 @@ static u32 read_state_register(struct fw_card *card) * reset, but then cleared when the units are ready again, which * happens immediately for us. */ - return 0; + u32 value = 0x0000; + + /* Bit 8 (cmstr): */ + value |= card->driver->read_csr_reg(card, CSR_STATE_CLEAR); + + return value; } static void update_split_timeout(struct fw_card *card) @@ -1034,6 +1039,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, if (tcode == TCODE_READ_QUADLET_REQUEST) { *data = cpu_to_be32(read_state_register(card)); } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + card->driver->write_csr_reg(card, CSR_STATE_CLEAR, + be32_to_cpu(*data)); } else { rcode = RCODE_TYPE_ERROR; } @@ -1043,7 +1050,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, if (tcode == TCODE_READ_QUADLET_REQUEST) { *data = cpu_to_be32(read_state_register(card)); } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { - /* FIXME: implement cmstr */ + card->driver->write_csr_reg(card, CSR_STATE_SET, + be32_to_cpu(*data)); /* FIXME: implement abdicate */ } else { rcode = RCODE_TYPE_ERROR; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 3b8c0f042f49..aaecdd1c1767 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -40,6 +40,8 @@ struct fw_packet; #define FEATURE_PRIORITY_BUDGET 0x01 +#define CSR_STATE_BIT_CMSTR (1 << 8) + struct fw_card_driver { /* * Enable the given card with the given initial config rom. diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 0e5413531785..1dcc2e427eb1 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -172,6 +172,7 @@ struct fw_ohci { unsigned quirks; unsigned int pri_req_max; u32 bus_time; + bool is_root; /* * Spinlock for accessing fw_ohci data. Never call out of @@ -1400,6 +1401,7 @@ static void bus_reset_tasklet(unsigned long data) unsigned long flags; void *free_rom = NULL; dma_addr_t free_rom_bus = 0; + bool is_new_root; reg = reg_read(ohci, OHCI1394_NodeID); if (!(reg & OHCI1394_NodeID_idValid)) { @@ -1413,6 +1415,12 @@ static void bus_reset_tasklet(unsigned long data) ohci->node_id = reg & (OHCI1394_NodeID_busNumber | OHCI1394_NodeID_nodeNumber); + is_new_root = (reg & OHCI1394_NodeID_root) != 0; + if (!(ohci->is_root && is_new_root)) + reg_write(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_cycleMaster); + ohci->is_root = is_new_root; + reg = reg_read(ohci, OHCI1394_SelfIDCount); if (reg & OHCI1394_SelfIDCount_selfIDError) { fw_notify("inconsistent self IDs\n"); @@ -2013,6 +2021,16 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) u32 value; switch (csr_offset) { + case CSR_STATE_CLEAR: + case CSR_STATE_SET: + /* the controller driver handles only the cmstr bit */ + if (ohci->is_root && + (reg_read(ohci, OHCI1394_LinkControlSet) & + OHCI1394_LinkControl_cycleMaster)) + return CSR_STATE_BIT_CMSTR; + else + return 0; + case CSR_NODE_IDS: return reg_read(ohci, OHCI1394_NodeID) << 16; @@ -2050,6 +2068,23 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) unsigned long flags; switch (csr_offset) { + case CSR_STATE_CLEAR: + /* the controller driver handles only the cmstr bit */ + if ((value & CSR_STATE_BIT_CMSTR) && ohci->is_root) { + reg_write(ohci, OHCI1394_LinkControlClear, + OHCI1394_LinkControl_cycleMaster); + flush_writes(ohci); + } + 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); + } + break; + case CSR_NODE_IDS: reg_write(ohci, OHCI1394_NodeID, value >> 16); flush_writes(ohci); diff --git a/drivers/firewire/ohci.h b/drivers/firewire/ohci.h index 3bc9a5d744eb..0e6c5a466908 100644 --- a/drivers/firewire/ohci.h +++ b/drivers/firewire/ohci.h @@ -60,6 +60,7 @@ #define OHCI1394_LinkControl_cycleSource (1 << 22) #define OHCI1394_NodeID 0x0E8 #define OHCI1394_NodeID_idValid 0x80000000 +#define OHCI1394_NodeID_root 0x40000000 #define OHCI1394_NodeID_nodeNumber 0x0000003f #define OHCI1394_NodeID_busNumber 0x0000ffc0 #define OHCI1394_PhyControl 0x0EC -- cgit v1.2.3 From e91b2787d0a2e4719b016e8dec0afd2d5ab6c30f Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:40:49 +0200 Subject: firewire: allocate broadcast channel in hardware On OHCI 1.1 controllers, let the hardware allocate the broadcast channel automatically. This removes a theoretical race condition directly after a bus reset where it could be possible to read the channel allocation register with channel 31 still being unallocated. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 16 +++++++++++----- drivers/firewire/core-topology.c | 3 ++- drivers/firewire/core.h | 1 + drivers/firewire/ohci.c | 18 ++++++++++++------ 4 files changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 7c4cf6cfa746..faf2eee473b9 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -208,13 +208,19 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation) { int channel, bandwidth = 0; - fw_iso_resource_manage(card, generation, 1ULL << 31, &channel, - &bandwidth, true, card->bm_transaction_data); - if (channel == 31) { + if (!card->broadcast_channel_allocated) { + fw_iso_resource_manage(card, generation, 1ULL << 31, + &channel, &bandwidth, true, + card->bm_transaction_data); + if (channel != 31) { + fw_notify("failed to allocate broadcast channel\n"); + return; + } card->broadcast_channel_allocated = true; - device_for_each_child(card->device, (void *)(long)generation, - fw_device_set_broadcast_channel); } + + device_for_each_child(card->device, (void *)(long)generation, + fw_device_set_broadcast_channel); } static const char gap_count_table[] = { diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index ca3c65318165..00a556f3a585 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -543,7 +543,8 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, spin_lock_irqsave(&card->lock, flags); - card->broadcast_channel_allocated = false; + card->broadcast_channel_allocated = (card->driver->get_features(card) & + FEATURE_CHANNEL_31_ALLOCATED) != 0; card->node_id = node_id; /* * Update node_id before generation to prevent anybody from using diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index a9ace1f8dc3f..3f9e39b60bca 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -39,6 +39,7 @@ struct fw_packet; #define BROADCAST_CHANNEL_VALID (1 << 30) #define FEATURE_PRIORITY_BUDGET 0x01 +#define FEATURE_CHANNEL_31_ALLOCATED 0x02 #define CSR_STATE_BIT_CMSTR (1 << 8) #define CSR_STATE_BIT_ABDICATE (1 << 10) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 1dcc2e427eb1..51a55808d88a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -171,6 +171,7 @@ struct fw_ohci { int request_generation; /* for timestamping incoming requests */ unsigned quirks; unsigned int pri_req_max; + unsigned int features; u32 bus_time; bool is_root; @@ -1694,7 +1695,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, seconds, irqs; + u32 lps, seconds, version, irqs; int i, ret; if (software_reset(ohci)) { @@ -1747,10 +1748,19 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); ohci->bus_time = seconds & ~0x3f; + version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; + if (version >= OHCI_VERSION_1_1) { + reg_write(ohci, OHCI1394_InitialChannelsAvailableHi, + 0xfffffffe); + ohci->features |= FEATURE_CHANNEL_31_ALLOCATED; + } + /* 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); + if (ohci->pri_req_max != 0) + ohci->features |= FEATURE_PRIORITY_BUDGET; ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -2124,12 +2134,8 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) static unsigned int ohci_get_features(struct fw_card *card) { struct fw_ohci *ohci = fw_ohci(card); - unsigned int features = 0; - - if (ohci->pri_req_max != 0) - features |= FEATURE_PRIORITY_BUDGET; - return features; + return ohci->features; } static void copy_iso_headers(struct iso_context *ctx, void *p) -- cgit v1.2.3 From 65b2742ac002f554f6ca0c2eab84cc62400eafdf Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:26:51 +0200 Subject: firewire: 'add CSR_... support' addendum Add a comment on which of the conflicting NODE_IDS specifications we implement. Reduce a comment on rather irrelevant register bits that can all be looked up in the spec (or from now on in the code history). Directly include the required indirectly included bug.h. Signed-off-by: Stefan Richter --- drivers/firewire/core-transaction.c | 27 ++++++--------------------- drivers/firewire/ohci.c | 1 + 2 files changed, 7 insertions(+), 21 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 85a54da243e2..5069cfc75b50 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -984,29 +984,10 @@ static const struct fw_address_region registers_region = static u32 read_state_register(struct fw_card *card) { - /* - * Fixed bits (IEEE 1394-2008 8.3.2.2.1): - * Bits 0-1 (state) always read 00=running. - * Bits 2,3 (off, atn) are not implemented as per the spec. - * Bit 4 (elog) is not implemented because there is no error log. - * Bit 6 (dreq) cannot be set. It is intended to "disable requests - * from unreliable nodes"; however, IEEE 1212 states that devices - * may "clear their own dreq bit when it has been improperly set". - * Our implementation might be seen as an improperly extensive - * interpretation of "improperly", but the 1212-2001 revision - * dropped this bit altogether, so we're in the clear. :o) - * Bit 7 (lost) always reads 0 because a power reset has never occurred - * during normal operation. - * Bit 9 (linkoff) is not implemented because the PC is not powered - * from the FireWire cable. - * Bit 15 (gone) always reads 0. It must be set at a power/command/bus - * reset, but then cleared when the units are ready again, which - * happens immediately for us. - */ - u32 value = 0x0000; + u32 value; /* Bit 8 (cmstr): */ - value |= card->driver->read_csr_reg(card, CSR_STATE_CLEAR); + value = card->driver->read_csr_reg(card, CSR_STATE_CLEAR); /* Bit 10 (abdicate): */ if (card->csr_abdicate) @@ -1066,6 +1047,10 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, break; case CSR_NODE_IDS: + /* + * per IEEE 1394-2008 8.3.22.3, not IEEE 1394.1-2004 3.2.8 + * and 9.6, but interoperable with IEEE 1394.1-2004 bridges + */ if (tcode == TCODE_READ_QUADLET_REQUEST) *data = cpu_to_be32(card->driver-> read_csr_reg(card, CSR_NODE_IDS)); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 51a55808d88a..2abdb3268a10 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -18,6 +18,7 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include -- cgit v1.2.3 From db3c9cc105ee844f6cd7a1beb9926fb8e9a093ae Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:30:21 +0200 Subject: firewire: replace get_features card driver hook by feature variables in the fw_card struct. The hook appeared to be an unnecessary abstraction in the card driver interface. Cleaner would be to pass those feature flags as arguments to fw_card_initialize() or fw_card_add(), but the FairnessControl register is in the SCLK domain and may therefore not be accessible while Link Power Status is off, i.e. before the card->driver->enable call from fw_card_add(). Signed-off-by: Stefan Richter --- drivers/firewire/core-topology.c | 3 +-- drivers/firewire/core-transaction.c | 3 +-- drivers/firewire/core.h | 5 ----- drivers/firewire/ohci.c | 14 ++------------ include/linux/firewire.h | 3 +++ 5 files changed, 7 insertions(+), 21 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 00a556f3a585..3b9667c37b67 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -543,8 +543,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, spin_lock_irqsave(&card->lock, flags); - card->broadcast_channel_allocated = (card->driver->get_features(card) & - FEATURE_CHANNEL_31_ALLOCATED) != 0; + card->broadcast_channel_allocated = card->broadcast_channel_auto_allocated; card->node_id = node_id; /* * Update node_id before generation to prevent anybody from using diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 5069cfc75b50..62bf30560a3e 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1129,8 +1129,7 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, break; case CSR_PRIORITY_BUDGET: - if (!(card->driver->get_features(card) & - FEATURE_PRIORITY_BUDGET)) + if (!card->priority_budget_implemented) rcode = RCODE_ADDRESS_ERROR; else if (tcode == TCODE_READ_QUADLET_REQUEST) *data = cpu_to_be32(card->driver-> diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 3f9e39b60bca..8dc76d8711a5 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -38,9 +38,6 @@ struct fw_packet; #define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31) #define BROADCAST_CHANNEL_VALID (1 << 30) -#define FEATURE_PRIORITY_BUDGET 0x01 -#define FEATURE_CHANNEL_31_ALLOCATED 0x02 - #define CSR_STATE_BIT_CMSTR (1 << 8) #define CSR_STATE_BIT_ABDICATE (1 << 10) @@ -84,8 +81,6 @@ struct fw_card_driver { u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); void (*write_csr_reg)(struct fw_card *card, int csr_offset, u32 value); - unsigned int (*get_features)(struct fw_card *card); - struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, int type, int channel, size_t header_size); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 2abdb3268a10..09bba9315de9 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -172,7 +172,6 @@ struct fw_ohci { int request_generation; /* for timestamping incoming requests */ unsigned quirks; unsigned int pri_req_max; - unsigned int features; u32 bus_time; bool is_root; @@ -1753,15 +1752,14 @@ static int ohci_enable(struct fw_card *card, if (version >= OHCI_VERSION_1_1) { reg_write(ohci, OHCI1394_InitialChannelsAvailableHi, 0xfffffffe); - ohci->features |= FEATURE_CHANNEL_31_ALLOCATED; + card->broadcast_channel_auto_allocated = true; } /* 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); - if (ohci->pri_req_max != 0) - ohci->features |= FEATURE_PRIORITY_BUDGET; + card->priority_budget_implemented = ohci->pri_req_max != 0; ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -2132,13 +2130,6 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) } } -static unsigned int ohci_get_features(struct fw_card *card) -{ - struct fw_ohci *ohci = fw_ohci(card); - - return ohci->features; -} - static void copy_iso_headers(struct iso_context *ctx, void *p) { int i = ctx->header_length; @@ -2578,7 +2569,6 @@ static const struct fw_card_driver ohci_driver = { .enable_phys_dma = ohci_enable_phys_dma, .read_csr_reg = ohci_read_csr_reg, .write_csr_reg = ohci_write_csr_reg, - .get_features = ohci_get_features, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 4d22643215ef..5acb5fc19180 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -122,6 +122,9 @@ struct fw_card { bool bm_abdicate; /* value of csr_abdicate before last bus reset */ bool csr_abdicate; /* visible in CSR STATE_CLEAR/SET registers */ + bool priority_budget_implemented; /* controller feature */ + bool broadcast_channel_auto_allocated; /* controller feature */ + bool broadcast_channel_allocated; u32 broadcast_channel; __be32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4]; -- cgit v1.2.3 From c8a94ded57e9cc2498d401b2f5c856213a3e19fb Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:34:50 +0200 Subject: firewire: normalize STATE_CLEAR/SET CSR access interface Push the maintenance of STATE_CLEAR/SET.abdicate down into the card driver. This way, the read/write_csr_reg driver method works uniformly across all CSR offsets. Signed-off-by: Stefan Richter --- drivers/firewire/core-topology.c | 5 ++--- drivers/firewire/core-transaction.c | 41 +++++++++++-------------------------- drivers/firewire/core.h | 2 +- drivers/firewire/ohci.c | 19 ++++++++++++----- include/linux/firewire.h | 3 +-- 5 files changed, 30 insertions(+), 40 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 3b9667c37b67..56e908ba43f1 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -524,7 +524,7 @@ static void update_topology_map(struct fw_card *card, } void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, - int self_id_count, u32 *self_ids) + int self_id_count, u32 *self_ids, bool bm_abdicate) { struct fw_node *local_node; unsigned long flags; @@ -552,8 +552,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, smp_wmb(); card->generation = generation; card->reset_jiffies = jiffies; - card->bm_abdicate = card->csr_abdicate; - card->csr_abdicate = false; + card->bm_abdicate = bm_abdicate; fw_schedule_bm_work(card, 0); local_node = build_tree(card, self_ids, self_id_count); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 62bf30560a3e..87d69cddb231 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -982,20 +982,6 @@ static const struct fw_address_region registers_region = { .start = CSR_REGISTER_BASE, .end = CSR_REGISTER_BASE | CSR_CONFIG_ROM, }; -static u32 read_state_register(struct fw_card *card) -{ - u32 value; - - /* Bit 8 (cmstr): */ - value = card->driver->read_csr_reg(card, CSR_STATE_CLEAR); - - /* Bit 10 (abdicate): */ - if (card->csr_abdicate) - value |= CSR_STATE_BIT_ABDICATE; - - return value; -} - static void update_split_timeout(struct fw_card *card) { unsigned int cycles; @@ -1021,29 +1007,25 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, switch (reg) { case CSR_STATE_CLEAR: - if (tcode == TCODE_READ_QUADLET_REQUEST) { - *data = cpu_to_be32(read_state_register(card)); - } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_STATE_CLEAR)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) card->driver->write_csr_reg(card, CSR_STATE_CLEAR, be32_to_cpu(*data)); - if (*data & cpu_to_be32(CSR_STATE_BIT_ABDICATE)) - card->csr_abdicate = false; - } else { + else rcode = RCODE_TYPE_ERROR; - } break; case CSR_STATE_SET: - if (tcode == TCODE_READ_QUADLET_REQUEST) { - *data = cpu_to_be32(read_state_register(card)); - } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_STATE_SET)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) card->driver->write_csr_reg(card, CSR_STATE_SET, be32_to_cpu(*data)); - if (*data & cpu_to_be32(CSR_STATE_BIT_ABDICATE)) - card->csr_abdicate = true; - } else { + else rcode = RCODE_TYPE_ERROR; - } break; case CSR_NODE_IDS: @@ -1063,7 +1045,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, case CSR_RESET_START: if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->csr_abdicate = false; + card->driver->write_csr_reg(card, CSR_STATE_CLEAR, + CSR_STATE_BIT_ABDICATE); else rcode = RCODE_TYPE_ERROR; break; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 8dc76d8711a5..8280c625170b 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -196,7 +196,7 @@ static inline void fw_node_put(struct fw_node *node) } void fw_core_handle_bus_reset(struct fw_card *card, int node_id, - int generation, int self_id_count, u32 *self_ids); + int generation, int self_id_count, u32 *self_ids, bool bm_abdicate); void fw_destroy_nodes(struct fw_card *card); /* diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 09bba9315de9..a55cf0911b72 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -174,6 +174,7 @@ struct fw_ohci { unsigned int pri_req_max; u32 bus_time; bool is_root; + bool csr_state_setclear_abdicate; /* * Spinlock for accessing fw_ohci data. Never call out of @@ -1529,7 +1530,9 @@ static void bus_reset_tasklet(unsigned long data) self_id_count, ohci->self_id_buffer); fw_core_handle_bus_reset(&ohci->card, ohci->node_id, generation, - self_id_count, ohci->self_id_buffer); + self_id_count, ohci->self_id_buffer, + ohci->csr_state_setclear_abdicate); + ohci->csr_state_setclear_abdicate = false; } static irqreturn_t irq_handler(int irq, void *data) @@ -2032,13 +2035,16 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) switch (csr_offset) { case CSR_STATE_CLEAR: case CSR_STATE_SET: - /* the controller driver handles only the cmstr bit */ if (ohci->is_root && (reg_read(ohci, OHCI1394_LinkControlSet) & OHCI1394_LinkControl_cycleMaster)) - return CSR_STATE_BIT_CMSTR; + value = CSR_STATE_BIT_CMSTR; else - return 0; + value = 0; + if (ohci->csr_state_setclear_abdicate) + value |= CSR_STATE_BIT_ABDICATE; + + return value; case CSR_NODE_IDS: return reg_read(ohci, OHCI1394_NodeID) << 16; @@ -2078,12 +2084,13 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) switch (csr_offset) { case CSR_STATE_CLEAR: - /* the controller driver handles only the cmstr bit */ 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) + ohci->csr_state_setclear_abdicate = false; break; case CSR_STATE_SET: @@ -2092,6 +2099,8 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) OHCI1394_LinkControl_cycleMaster); flush_writes(ohci); } + if (value & CSR_STATE_BIT_ABDICATE) + ohci->csr_state_setclear_abdicate = true; break; case CSR_NODE_IDS: diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 5acb5fc19180..5553018d45d6 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -119,8 +119,7 @@ struct fw_card { int bm_retries; int bm_generation; __be32 bm_transaction_data[2]; - bool bm_abdicate; /* value of csr_abdicate before last bus reset */ - bool csr_abdicate; /* visible in CSR STATE_CLEAR/SET registers */ + bool bm_abdicate; bool priority_budget_implemented; /* controller feature */ bool broadcast_channel_auto_allocated; /* controller feature */ -- cgit v1.2.3 From 0fcff4e39323f466a47684d7c8ffa77e1be86c8a Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:35:52 +0200 Subject: firewire: rename CSR access driver methods Rather than "read a Control and Status Registers (CSR) Architecture register" I prefer to say "read a Control and Status Register". Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 2 +- drivers/firewire/core-transaction.c | 10 ++++------ drivers/firewire/core.h | 4 ++-- drivers/firewire/ohci.c | 8 ++++---- 4 files changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 2e62516a4b15..32a33da64991 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1044,7 +1044,7 @@ static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) local_irq_disable(); - cycle_time = card->driver->read_csr_reg(card, CSR_CYCLE_TIME); + cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME); switch (a->clk_id) { case CLOCK_REALTIME: getnstimeofday(&ts); break; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index b8f6db6a0494..182da69eae44 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1026,19 +1026,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, case CSR_BUS_TIME: case CSR_BUSY_TIMEOUT: if (tcode == TCODE_READ_QUADLET_REQUEST) - *data = cpu_to_be32(card->driver-> - read_csr_reg(card, reg)); + *data = cpu_to_be32(card->driver->read_csr(card, reg)); else if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr_reg(card, reg, - be32_to_cpu(*data)); + card->driver->write_csr(card, reg, be32_to_cpu(*data)); else rcode = RCODE_TYPE_ERROR; break; case CSR_RESET_START: if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr_reg(card, CSR_STATE_CLEAR, - CSR_STATE_BIT_ABDICATE); + card->driver->write_csr(card, CSR_STATE_CLEAR, + CSR_STATE_BIT_ABDICATE); else rcode = RCODE_TYPE_ERROR; break; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 8280c625170b..3000dd74acfd 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -78,8 +78,8 @@ struct fw_card_driver { int (*enable_phys_dma)(struct fw_card *card, int node_id, int generation); - u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); - void (*write_csr_reg)(struct fw_card *card, int csr_offset, u32 value); + u32 (*read_csr)(struct fw_card *card, int csr_offset); + void (*write_csr)(struct fw_card *card, int csr_offset, u32 value); struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a55cf0911b72..a4bbf3dadf58 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2026,7 +2026,7 @@ static int ohci_enable_phys_dma(struct fw_card *card, #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */ } -static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) +static u32 ohci_read_csr(struct fw_card *card, int csr_offset) { struct fw_ohci *ohci = fw_ohci(card); unsigned long flags; @@ -2077,7 +2077,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) +static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value) { struct fw_ohci *ohci = fw_ohci(card); unsigned long flags; @@ -2576,8 +2576,8 @@ static const struct fw_card_driver ohci_driver = { .send_response = ohci_send_response, .cancel_packet = ohci_cancel_packet, .enable_phys_dma = ohci_enable_phys_dma, - .read_csr_reg = ohci_read_csr_reg, - .write_csr_reg = ohci_write_csr_reg, + .read_csr = ohci_read_csr, + .write_csr = ohci_write_csr, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, -- cgit v1.2.3 From 02d37bed188c500ee7afb0a2dc6b65a80704c58e Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 8 Jul 2010 16:09:06 +0200 Subject: firewire: core: integrate software-forced bus resets with bus management Bus resets which are triggered - by the kernel drivers after updates of the local nodes' config ROM, - by userspace software via ioctl shall be deferred until after >=2 seconds after the last bus reset. If multiple modifications of the local nodes' config ROM happen in a row, only a single bus reset should happen after them. When the local node's link goes from inactive to active or vice versa, and at the two occasions of bus resets mentioned above --- and if the current gap count differs from 63 --- the bus reset should be preceded by a PHY configuration packet that reaffirms the gap count. Otherwise a bus manager would have to reset the bus again right after that. This is necessary to promote bus stability, e.g. leave grace periods for allocations and reallocations of isochronous channels and bandwidth, SBP-2 reconnections etc.; see IEEE 1394 clause 8.2.1. This change implements all of the above by moving bus reset initiation into a delayed work (except for bus resets which are triggered by the bus manager workqueue job and are performed there immediately). It comes with a necessary addition to the card driver methods that allows to get the current gap count from PHY registers. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 68 ++++++++++++++++++++++++++++--------- drivers/firewire/core-cdev.c | 3 +- drivers/firewire/core-transaction.c | 18 ++++++++-- drivers/firewire/core.h | 6 +++- drivers/firewire/ohci.c | 53 +++++++++++++++++++++-------- include/linux/firewire.h | 6 ++-- 6 files changed, 116 insertions(+), 38 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 6c316cfe70c4..2bb5c036e806 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); diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 7a690c466ce9..ee2e87353102 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -820,8 +820,9 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) static int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) { - return fw_core_initiate_bus_reset(client->device->card, + fw_schedule_bus_reset(client->device->card, true, arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET); + return 0; } static void release_descriptor(struct client *client, diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 7813da8a1293..5f5a7852f7ac 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -426,9 +426,21 @@ void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count) { long timeout = DIV_ROUND_UP(HZ, 10); - u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) | - PHY_CONFIG_ROOT_ID(node_id) | - PHY_CONFIG_GAP_COUNT(gap_count); + u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG); + + if (node_id != FW_PHY_CONFIG_NO_NODE_ID) + data |= PHY_CONFIG_ROOT_ID(node_id); + + if (gap_count == FW_PHY_CONFIG_CURRENT_GAP_COUNT) { + gap_count = card->driver->read_phy_reg(card, 1); + if (gap_count < 0) + return; + + gap_count &= 63; + if (gap_count == 63) + return; + } + data |= PHY_CONFIG_GAP_COUNT(gap_count); mutex_lock(&phy_config_mutex); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 3000dd74acfd..ff6c90922001 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -51,6 +51,7 @@ struct fw_card_driver { int (*enable)(struct fw_card *card, const __be32 *config_rom, size_t length); + int (*read_phy_reg)(struct fw_card *card, int address); int (*update_phy_reg)(struct fw_card *card, int address, int clear_bits, int set_bits); @@ -102,8 +103,8 @@ void fw_card_initialize(struct fw_card *card, int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid); void fw_core_remove_card(struct fw_card *card); -int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset); int fw_compute_block_crc(__be32 *block); +void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset); void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); static inline struct fw_card *fw_card_get(struct fw_card *card) @@ -225,6 +226,9 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); int fw_get_response_length(struct fw_request *request); void fw_fill_response(struct fw_packet *response, u32 *request_header, int rcode, void *payload, size_t length); + +#define FW_PHY_CONFIG_NO_NODE_ID -1 +#define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1 void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a4bbf3dadf58..bb6a92bc9e6a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -182,6 +183,8 @@ struct fw_ohci { */ spinlock_t lock; + struct mutex phy_reg_mutex; + struct ar_context ar_request_ctx; struct ar_context ar_response_ctx; struct context at_request_ctx; @@ -517,13 +520,10 @@ static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val) return -EBUSY; } -static int ohci_update_phy_reg(struct fw_card *card, int addr, - int clear_bits, int set_bits) +static int update_phy_reg(struct fw_ohci *ohci, int addr, + int clear_bits, int set_bits) { - struct fw_ohci *ohci = fw_ohci(card); - int ret; - - ret = read_phy_reg(ohci, addr); + int ret = read_phy_reg(ohci, addr); if (ret < 0) return ret; @@ -541,13 +541,38 @@ static int read_paged_phy_reg(struct fw_ohci *ohci, int page, int addr) { int ret; - ret = ohci_update_phy_reg(&ohci->card, 7, PHY_PAGE_SELECT, page << 5); + ret = update_phy_reg(ohci, 7, PHY_PAGE_SELECT, page << 5); if (ret < 0) return ret; return read_phy_reg(ohci, addr); } +static int ohci_read_phy_reg(struct fw_card *card, int addr) +{ + struct fw_ohci *ohci = fw_ohci(card); + int ret; + + mutex_lock(&ohci->phy_reg_mutex); + ret = read_phy_reg(ohci, addr); + mutex_unlock(&ohci->phy_reg_mutex); + + return ret; +} + +static int ohci_update_phy_reg(struct fw_card *card, int addr, + int clear_bits, int set_bits) +{ + struct fw_ohci *ohci = fw_ohci(card); + int ret; + + mutex_lock(&ohci->phy_reg_mutex); + ret = update_phy_reg(ohci, addr, clear_bits, set_bits); + mutex_unlock(&ohci->phy_reg_mutex); + + return ret; +} + static int ar_context_add_page(struct ar_context *ctx) { struct device *dev = ctx->ohci->card.device; @@ -1676,7 +1701,7 @@ static int configure_1394a_enhancements(struct fw_ohci *ohci) clear = PHY_ENABLE_ACCEL | PHY_ENABLE_MULTI; set = 0; } - ret = ohci_update_phy_reg(&ohci->card, 5, clear, set); + ret = update_phy_reg(ohci, 5, clear, set); if (ret < 0) return ret; @@ -1856,12 +1881,8 @@ static int ohci_enable(struct fw_card *card, OHCI1394_HCControl_BIBimageValid); flush_writes(ohci); - /* - * We are ready to go, initiate bus reset to finish the - * initialization. - */ - - fw_core_initiate_bus_reset(&ohci->card, 1); + /* We are ready to go, reset bus to finish initialization. */ + fw_schedule_bus_reset(&ohci->card, false, true); return 0; } @@ -1936,7 +1957,7 @@ static int ohci_set_config_rom(struct fw_card *card, * takes effect. */ if (ret == 0) - fw_core_initiate_bus_reset(&ohci->card, 1); + fw_schedule_bus_reset(&ohci->card, true, true); else dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, next_config_rom, next_config_rom_bus); @@ -2570,6 +2591,7 @@ static int ohci_queue_iso(struct fw_iso_context *base, static const struct fw_card_driver ohci_driver = { .enable = ohci_enable, + .read_phy_reg = ohci_read_phy_reg, .update_phy_reg = ohci_update_phy_reg, .set_config_rom = ohci_set_config_rom, .send_request = ohci_send_request, @@ -2645,6 +2667,7 @@ static int __devinit pci_probe(struct pci_dev *dev, pci_set_drvdata(dev, ohci); spin_lock_init(&ohci->lock); + mutex_init(&ohci->phy_reg_mutex); tasklet_init(&ohci->bus_reset_tasklet, bus_reset_tasklet, (unsigned long)ohci); diff --git a/include/linux/firewire.h b/include/linux/firewire.h index db30a752a87a..adc5b55e6e5f 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -114,8 +114,10 @@ struct fw_card { struct list_head link; - /* Work struct for BM duties. */ - struct delayed_work work; + struct delayed_work br_work; /* bus reset job */ + bool br_short; + + struct delayed_work bm_work; /* bus manager job */ int bm_retries; int bm_generation; __be32 bm_transaction_data[2]; -- cgit v1.2.3 From bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bc Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 16 Jul 2010 22:25:51 +0200 Subject: firewire: cdev: add PHY packet reception Add an FW_CDEV_IOC_RECEIVE_PHY_PACKETS ioctl() and FW_CDEV_EVENT_PHY_PACKET_RECEIVED poll()/read() event for /dev/fw*. This can be used to get information from remote PHYs by remote access PHY packets. This is also the 2nd half of the functionality (the receive part) to support a userspace implementation of a VersaPHY transaction layer. Safety considerations: - PHY packets are generally broadcasts, hence some kind of elevated privileges should be required of a process to be able to listen in on PHY packets. This implementation assumes that a process that is allowed to open the /dev/fw* of a local node does have this privilege. There was an inconclusive discussion about introducing POSIX capabilities as a means to check for user privileges for these kinds of operations. Other limitations: - PHY packet reception may be switched on by ioctl() but cannot be switched off again. It would be trivial to provide an off switch, but this is not worth the code. The client should simply close() the fd then, or just ignore further events. - For sake of simplicity of API and kernel-side implementation, no filter per packet content is provided. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 1 + drivers/firewire/core-cdev.c | 73 ++++++++++++++++++++++++++++++++++--- drivers/firewire/core-transaction.c | 5 +++ drivers/firewire/core.h | 2 + drivers/firewire/ohci.c | 3 +- include/linux/firewire-cdev.h | 39 ++++++++++++++++---- include/linux/firewire.h | 3 +- 7 files changed, 111 insertions(+), 15 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 0c312c4bb4bd..6d1cfae6aad4 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -500,6 +500,7 @@ void fw_card_initialize(struct fw_card *card, kref_init(&card->kref); init_completion(&card->done); INIT_LIST_HEAD(&card->transaction_list); + INIT_LIST_HEAD(&card->phy_receiver_list); spin_lock_init(&card->lock); card->local_node = NULL; diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f95719926487..0425dd5dfcd3 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -69,6 +69,9 @@ struct client { struct fw_iso_buffer buffer; unsigned long vm_start; + struct list_head phy_receiver_link; + u64 phy_receiver_closure; + struct list_head link; struct kref kref; }; @@ -201,6 +204,11 @@ struct outbound_phy_packet_event { struct fw_cdev_event_phy_packet phy_packet; }; +struct inbound_phy_packet_event { + struct event event; + struct fw_cdev_event_phy_packet phy_packet; +}; + static inline void __user *u64_to_uptr(__u64 value) { return (void __user *)(unsigned long)value; @@ -236,6 +244,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) idr_init(&client->resource_idr); INIT_LIST_HEAD(&client->event_list); init_waitqueue_head(&client->wait); + INIT_LIST_HEAD(&client->phy_receiver_link); kref_init(&client->kref); file->private_data = client; @@ -357,7 +366,7 @@ static void queue_bus_reset_event(struct client *client) e = kzalloc(sizeof(*e), GFP_KERNEL); if (e == NULL) { - fw_notify("Out of memory when allocating bus reset event\n"); + fw_notify("Out of memory when allocating event\n"); return; } @@ -404,6 +413,7 @@ union ioctl_arg { struct fw_cdev_send_stream_packet send_stream_packet; struct fw_cdev_get_cycle_timer2 get_cycle_timer2; struct fw_cdev_send_phy_packet send_phy_packet; + struct fw_cdev_receive_phy_packets receive_phy_packets; }; static int ioctl_get_info(struct client *client, union ioctl_arg *arg) @@ -671,9 +681,10 @@ static void handle_request(struct fw_card *card, struct fw_request *request, r = kmalloc(sizeof(*r), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); - if (r == NULL || e == NULL) + if (r == NULL || e == NULL) { + fw_notify("Out of memory when allocating event\n"); goto failed; - + } r->card = card; r->request = request; r->data = payload; @@ -902,9 +913,10 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, struct iso_interrupt_event *e; e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); - if (e == NULL) + if (e == NULL) { + fw_notify("Out of memory when allocating event\n"); return; - + } e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; e->interrupt.closure = client->iso_closure; e->interrupt.cycle = cycle; @@ -1447,6 +1459,52 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) return 0; } +static int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg) +{ + struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets; + struct fw_card *card = client->device->card; + + /* Access policy: Allow this ioctl only on local nodes' device files. */ + if (!client->device->is_local) + return -ENOSYS; + + spin_lock_irq(&card->lock); + + list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list); + client->phy_receiver_closure = a->closure; + + spin_unlock_irq(&card->lock); + + return 0; +} + +void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) +{ + struct client *client; + struct inbound_phy_packet_event *e; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { + e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); + if (e == NULL) { + fw_notify("Out of memory when allocating event\n"); + break; + } + e->phy_packet.closure = client->phy_receiver_closure; + e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; + e->phy_packet.rcode = RCODE_COMPLETE; + e->phy_packet.length = 8; + e->phy_packet.data[0] = p->header[1]; + e->phy_packet.data[1] = p->header[2]; + queue_event(client, &e->event, + &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0); + } + + spin_unlock_irqrestore(&card->lock, flags); +} + static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x00] = ioctl_get_info, [0x01] = ioctl_send_request, @@ -1470,6 +1528,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x13] = ioctl_send_stream_packet, [0x14] = ioctl_get_cycle_timer2, [0x15] = ioctl_send_phy_packet, + [0x16] = ioctl_receive_phy_packets, }; static int dispatch_ioctl(struct client *client, @@ -1577,6 +1636,10 @@ static int fw_device_op_release(struct inode *inode, struct file *file) struct client *client = file->private_data; struct event *event, *next_event; + spin_lock_irq(&client->device->card->lock); + list_del(&client->phy_receiver_link); + spin_unlock_irq(&client->device->card->lock); + mutex_lock(&client->device->client_list_mutex); list_del(&client->link); mutex_unlock(&client->device->client_list_mutex); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index e2e4dc624fb6..6f225cacbc3d 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -883,6 +883,11 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) return; + if (TCODE_IS_LINK_INTERNAL(HEADER_GET_TCODE(p->header[0]))) { + fw_cdev_handle_phy_packet(card, p); + return; + } + request = allocate_request(card, p); if (request == NULL) { /* FIXME: send statically allocated busy packet. */ diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index ff6c90922001..3102b6b63438 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -128,6 +128,7 @@ extern const struct file_operations fw_device_ops; void fw_device_cdev_update(struct fw_device *device); void fw_device_cdev_remove(struct fw_device *device); +void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p); /* -device */ @@ -214,6 +215,7 @@ static inline bool is_next_generation(int new_generation, int old_generation) #define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4) #define TCODE_IS_BLOCK_PACKET(tcode) (((tcode) & 1) != 0) +#define TCODE_IS_LINK_INTERNAL(tcode) ((tcode) == 0xe) #define TCODE_IS_REQUEST(tcode) (((tcode) & 2) == 0) #define TCODE_IS_RESPONSE(tcode) (((tcode) & 2) != 0) #define TCODE_HAS_REQUEST_DATA(tcode) (((tcode) & 12) != 4) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index bb6a92bc9e6a..08afccc66333 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1759,10 +1759,9 @@ static int ohci_enable(struct fw_card *card, OHCI1394_HCControl_noByteSwapData); reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); - reg_write(ohci, OHCI1394_LinkControlClear, - OHCI1394_LinkControl_rcvPhyPkt); reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_rcvSelfID | + OHCI1394_LinkControl_rcvPhyPkt | OHCI1394_LinkControl_cycleTimerEnable | OHCI1394_LinkControl_cycleMaster); diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 5bc051b9a013..b87409160794 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -35,6 +35,7 @@ /* available since kernel version 2.6.36 */ #define FW_CDEV_EVENT_REQUEST2 0x06 #define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 +#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08 /** * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types @@ -285,16 +286,24 @@ struct fw_cdev_event_iso_resource { }; /** - * struct fw_cdev_event_phy_packet - A PHY packet was transmitted - * @closure: See &fw_cdev_event_common; - * set by %FW_CDEV_IOC_SEND_PHY_PACKET ioctl - * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT + * struct fw_cdev_event_phy_packet - A PHY packet was transmitted or received + * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_SEND_PHY_PACKET + * or %FW_CDEV_IOC_RECEIVE_PHY_PACKETS ioctl + * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT or %..._RECEIVED * @rcode: %RCODE_..., indicates success or failure of transmission + * @length: Data length in bytes + * @data: Incoming data + * + * If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT, @length is 0 and @data empty. + * If @type is %FW_CDEV_EVENT_PHY_PACKET_RECEIVED, @length is 8 and @data + * consists of the two PHY packet quadlets, in host byte order. */ struct fw_cdev_event_phy_packet { __u64 closure; __u32 type; __u32 rcode; + __u32 length; + __u32 data[0]; }; /** @@ -308,7 +317,9 @@ struct fw_cdev_event_phy_packet { * @iso_resource: Valid if @common.type == * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED - * @phy_packet: Valid if @common.type == %FW_CDEV_EVENT_PHY_PACKET_SENT + * @phy_packet: Valid if @common.type == + * %FW_CDEV_EVENT_PHY_PACKET_SENT or + * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED * * Convenience union for userspace use. Events could be read(2) into an * appropriately aligned char buffer and then cast to this union for further @@ -360,6 +371,7 @@ union fw_cdev_event { /* available since kernel version 2.6.36 */ #define FW_CDEV_IOC_SEND_PHY_PACKET _IOWR('#', 0x15, struct fw_cdev_send_phy_packet) +#define FW_CDEV_IOC_RECEIVE_PHY_PACKETS _IOW('#', 0x16, struct fw_cdev_receive_phy_packets) /* * ABI version history @@ -376,9 +388,9 @@ union fw_cdev_event { * - shared use and auto-response for FCP registers * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 - * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_SENT + * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_* * - implemented &fw_cdev_event_bus_reset.bm_node_id - * - added %FW_CDEV_IOC_SEND_PHY_PACKET + * - added %FW_CDEV_IOC_SEND_PHY_PACKET, _RECEIVE_PHY_PACKETS */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ @@ -850,4 +862,17 @@ struct fw_cdev_send_phy_packet { __u32 generation; }; +/** + * struct fw_cdev_receive_phy_packets - start reception of PHY packets + * @closure: Passed back to userspace in phy packet events + * + * This ioctl activates issuing of %FW_CDEV_EVENT_PHY_PACKET_RECEIVED due to + * incoming PHY packets from any node on the same bus as the device. + * + * The ioctl is only permitted on device files which represent a local node. + */ +struct fw_cdev_receive_phy_packets { + __u64 closure; +}; + #endif /* _LINUX_FIREWIRE_CDEV_H */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 0c38b8e97722..d974aa4a24c9 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -111,9 +111,10 @@ struct fw_card { bool beta_repeaters_present; int index; - struct list_head link; + struct list_head phy_receiver_list; + struct delayed_work br_work; /* bus reset job */ bool br_short; -- cgit v1.2.3 From cc550216ae9a2993ef3973464714dc1a39ab1f86 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 18 Jul 2010 13:00:50 +0200 Subject: firewire: cdev: add PHY pinging This extends the FW_CDEV_IOC_SEND_PHY_PACKET ioctl() for /dev/fw* to be useful for ping time measurements. One application for it would be gap count optimization in userspace that is based on ping times rather than hop count. (The latter is implemented in firewire-core itself but is not applicable to beta PHYs that act as repeater.) Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 9 ++++++--- drivers/firewire/core.h | 5 +++++ drivers/firewire/ohci.c | 3 +++ include/linux/firewire-cdev.h | 5 ++++- 4 files changed, 18 insertions(+), 4 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 0425dd5dfcd3..31863cf8b6c4 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1423,9 +1423,10 @@ static void outbound_phy_packet_callback(struct fw_packet *packet, /* stale generation; cancelled; on certain controllers: no ack */ default: e->phy_packet.rcode = status; break; } + e->phy_packet.data[0] = packet->timestamp; - queue_event(e->client, &e->event, - &e->phy_packet, sizeof(e->phy_packet), NULL, 0); + queue_event(e->client, &e->event, &e->phy_packet, + sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0); client_put(e->client); } @@ -1439,7 +1440,7 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) if (!client->device->is_local) return -ENOSYS; - e = kzalloc(sizeof(*e), GFP_KERNEL); + e = kzalloc(sizeof(*e) + 4, GFP_KERNEL); if (e == NULL) return -ENOMEM; @@ -1453,6 +1454,8 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) e->p.callback = outbound_phy_packet_callback; e->phy_packet.closure = a->closure; e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; + if (is_ping_packet(a->data)) + e->phy_packet.length = 4; card->driver->send_request(card, &e->p); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 3102b6b63438..28621e44b111 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -234,4 +234,9 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count); +static inline bool is_ping_packet(u32 *data) +{ + return (data[0] & 0xc0ffffff) == 0 && ~data[0] == data[1]; +} + #endif /* _FIREWIRE_CORE_H */ diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 08afccc66333..5f6bb2c53808 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1068,6 +1068,9 @@ static int at_context_queue_packet(struct context *ctx, header[1] = cpu_to_le32(packet->header[0]); header[2] = cpu_to_le32(packet->header[1]); d[0].req_count = cpu_to_le16(12); + + if (is_ping_packet(packet->header)) + d[0].control |= cpu_to_le16(DESCRIPTOR_PING); break; case 4: diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index b87409160794..da0fec7e8dc0 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -294,7 +294,10 @@ struct fw_cdev_event_iso_resource { * @length: Data length in bytes * @data: Incoming data * - * If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT, @length is 0 and @data empty. + * If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT, @length is 0 and @data empty, + * except in case of a ping packet: Then, @length is 4, and @data[0] is the + * ping time in 49.152MHz clocks if @rcode is %RCODE_COMPLETE. + * * If @type is %FW_CDEV_EVENT_PHY_PACKET_RECEIVED, @length is 8 and @data * consists of the two PHY packet quadlets, in host byte order. */ -- cgit v1.2.3 From 071595ebdc66d70219e2d1ce746016f64b2b19e7 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 27 Jul 2010 13:20:33 +0200 Subject: firewire: ohci: use memory barriers to order descriptor updates When we append to a DMA program, we need to ensure that the order in which initialization of the new descriptors and update of the branch_address of the old tail descriptor, as seen by the PCI device, happen as intended. Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 5f6bb2c53808..3f59d1df6504 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -595,6 +595,7 @@ static int ar_context_add_page(struct ar_context *ctx) ab->descriptor.res_count = cpu_to_le16(PAGE_SIZE - offset); ab->descriptor.branch_address = 0; + wmb(); /* finish init of new descriptors before branch_address update */ ctx->last_buffer->descriptor.branch_address = cpu_to_le32(ab_bus | 1); ctx->last_buffer->next = ab; ctx->last_buffer = ab; @@ -982,6 +983,8 @@ static void context_append(struct context *ctx, d_bus = desc->buffer_bus + (d - desc->buffer) * sizeof(*d); desc->used += (z + extra) * sizeof(*d); + + wmb(); /* finish init of new descriptors before branch_address update */ ctx->prev->branch_address = cpu_to_le32(d_bus | z); ctx->prev = find_branch_descriptor(d, z); -- cgit v1.2.3 From e5b06c077c592e7e1623641520787a3da7b7c6bf Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 28 Jul 2010 15:50:00 +0200 Subject: firewire: ohci: release channel in error path firewire-ohci keeps book of which isochronous channels are occupied by IR DMA contexts, so that there cannot be more than one context listening to a certain channel. If IR context creation failed due to an out-of-memory condition, this bookkeeping leaked a channel. Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 3f59d1df6504..2e4b425847a7 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2309,6 +2309,7 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, free_page((unsigned long)ctx->header); out: spin_lock_irqsave(&ohci->lock, flags); + *channels |= 1ULL << channel; *mask |= 1 << index; spin_unlock_irqrestore(&ohci->lock, flags); -- cgit v1.2.3 From 872e330e38806d835bd6c311c93ab998e2fb9058 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 29 Jul 2010 18:19:22 +0200 Subject: firewire: add isochronous multichannel reception This adds the DMA context programming and userspace ABI for multichannel reception, i.e. for listening on multiple channel numbers by means of a single DMA context. The use case is reception of more streams than there are IR DMA units offered by the link layer. This is already implemented by the older ohci1394 + ieee1394 + raw1394 stack. And as discussed recently on linux1394-devel, this feature is occasionally used in practice. The big drawbacks of this mode are that buffer layout and interrupt generation necessarily differ from single-channel reception: Headers and trailers are not stripped from packets, packets are not aligned with buffer chunks, interrupts are per buffer chunk, not per packet. These drawbacks also cause a rather hefty code footprint to support this rarely used OHCI-1394 feature. (367 lines added, among them 94 lines of added userspace ABI documentation.) This implementation enforces that a multichannel reception context may only listen to channels to which no single-channel context on the same link layer is presently listening to. OHCI-1394 would allow to overlay single-channel contexts by the multi-channel context, but this would be a departure from the present first-come-first-served policy of IR context creation. The implementation is heavily based on an earlier one by Jay Fenlason. Thanks Jay. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 93 ++++++++++--- drivers/firewire/core-iso.c | 32 ++++- drivers/firewire/core.h | 2 + drivers/firewire/ohci.c | 316 +++++++++++++++++++++++++++++++++--------- include/linux/firewire-cdev.h | 281 +++++++++++++++++++++++++------------ include/linux/firewire.h | 29 ++-- 6 files changed, 560 insertions(+), 193 deletions(-) (limited to 'drivers/firewire/ohci.c') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index cf989e1635e1..ba23646bb108 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -193,6 +193,11 @@ struct iso_interrupt_event { struct fw_cdev_event_iso_interrupt interrupt; }; +struct iso_interrupt_mc_event { + struct event event; + struct fw_cdev_event_iso_interrupt_mc interrupt; +}; + struct iso_resource_event { struct event event; struct fw_cdev_event_iso_resource iso_resource; @@ -415,6 +420,7 @@ union ioctl_arg { struct fw_cdev_get_cycle_timer2 get_cycle_timer2; struct fw_cdev_send_phy_packet send_phy_packet; struct fw_cdev_receive_phy_packets receive_phy_packets; + struct fw_cdev_set_iso_channels set_iso_channels; }; static int ioctl_get_info(struct client *client, union ioctl_arg *arg) @@ -932,26 +938,54 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, sizeof(e->interrupt) + header_length, NULL, 0); } +static void iso_mc_callback(struct fw_iso_context *context, + dma_addr_t completed, void *data) +{ + struct client *client = data; + struct iso_interrupt_mc_event *e; + + e = kmalloc(sizeof(*e), GFP_ATOMIC); + if (e == NULL) { + fw_notify("Out of memory when allocating event\n"); + return; + } + e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL; + e->interrupt.closure = client->iso_closure; + e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer, + completed); + queue_event(client, &e->event, &e->interrupt, + sizeof(e->interrupt), NULL, 0); +} + static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) { struct fw_cdev_create_iso_context *a = &arg->create_iso_context; struct fw_iso_context *context; + fw_iso_callback_t cb; BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || - FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE); - - if (a->channel > 63) - return -EINVAL; + FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE || + FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL != + FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL); switch (a->type) { - case FW_ISO_CONTEXT_RECEIVE: - if (a->header_size < 4 || (a->header_size & 3)) + case FW_ISO_CONTEXT_TRANSMIT: + if (a->speed > SCODE_3200 || a->channel > 63) return -EINVAL; + + cb = iso_callback; break; - case FW_ISO_CONTEXT_TRANSMIT: - if (a->speed > SCODE_3200) + case FW_ISO_CONTEXT_RECEIVE: + if (a->header_size < 4 || (a->header_size & 3) || + a->channel > 63) return -EINVAL; + + cb = iso_callback; + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + cb = (fw_iso_callback_t)iso_mc_callback; break; default: @@ -959,8 +993,7 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) } context = fw_iso_context_create(client->device->card, a->type, - a->channel, a->speed, a->header_size, - iso_callback, client); + a->channel, a->speed, a->header_size, cb, client); if (IS_ERR(context)) return PTR_ERR(context); @@ -980,6 +1013,17 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) return 0; } +static int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg) +{ + struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels; + struct fw_iso_context *ctx = client->iso_context; + + if (ctx == NULL || a->handle != 0) + return -EINVAL; + + return fw_iso_context_set_channels(ctx, &a->channels); +} + /* Macros for decoding the iso packet control header. */ #define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) #define GET_INTERRUPT(v) (((v) >> 16) & 0x01) @@ -993,7 +1037,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) struct fw_cdev_queue_iso *a = &arg->queue_iso; struct fw_cdev_iso_packet __user *p, *end, *next; struct fw_iso_context *ctx = client->iso_context; - unsigned long payload, buffer_end, transmit_header_bytes; + unsigned long payload, buffer_end, transmit_header_bytes = 0; u32 control; int count; struct { @@ -1013,7 +1057,6 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) * use the indirect payload, the iso buffer need not be mapped * and the a->data pointer is ignored. */ - payload = (unsigned long)a->data - client->vm_start; buffer_end = client->buffer.page_count << PAGE_SHIFT; if (a->data == 0 || client->buffer.pages == NULL || @@ -1022,8 +1065,10 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) buffer_end = 0; } - p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); + if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3) + return -EINVAL; + p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); if (!access_ok(VERIFY_READ, p, a->size)) return -EFAULT; @@ -1039,19 +1084,24 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) u.packet.sy = GET_SY(control); u.packet.header_length = GET_HEADER_LENGTH(control); - if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { - if (u.packet.header_length % 4 != 0) + switch (ctx->type) { + case FW_ISO_CONTEXT_TRANSMIT: + if (u.packet.header_length & 3) return -EINVAL; transmit_header_bytes = u.packet.header_length; - } else { - /* - * We require that header_length is a multiple of - * the fixed header size, ctx->header_size. - */ + break; + + case FW_ISO_CONTEXT_RECEIVE: if (u.packet.header_length == 0 || u.packet.header_length % ctx->header_size != 0) return -EINVAL; - transmit_header_bytes = 0; + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (u.packet.payload_length == 0 || + u.packet.payload_length & 3) + return -EINVAL; + break; } next = (struct fw_cdev_iso_packet __user *) @@ -1534,6 +1584,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x14] = ioctl_get_cycle_timer2, [0x15] = ioctl_send_phy_packet, [0x16] = ioctl_receive_phy_packets, + [0x17] = ioctl_set_iso_channels, }; static int dispatch_ioctl(struct client *client, diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 4fe932e60fb8..0c8e662a5daf 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -117,6 +117,23 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, } EXPORT_SYMBOL(fw_iso_buffer_destroy); +/* Convert DMA address to offset into virtually contiguous buffer. */ +size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed) +{ + int i; + dma_addr_t address; + ssize_t offset; + + for (i = 0; i < buffer->page_count; i++) { + address = page_private(buffer->pages[i]); + offset = (ssize_t)completed - (ssize_t)address; + if (offset > 0 && offset <= PAGE_SIZE) + return (i << PAGE_SHIFT) + offset; + } + + return 0; +} + struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, int channel, int speed, size_t header_size, fw_iso_callback_t callback, void *callback_data) @@ -133,7 +150,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ctx->channel = channel; ctx->speed = speed; ctx->header_size = header_size; - ctx->callback = callback; + ctx->callback.sc = callback; ctx->callback_data = callback_data; return ctx; @@ -142,9 +159,7 @@ EXPORT_SYMBOL(fw_iso_context_create); void fw_iso_context_destroy(struct fw_iso_context *ctx) { - struct fw_card *card = ctx->card; - - card->driver->free_iso_context(ctx); + ctx->card->driver->free_iso_context(ctx); } EXPORT_SYMBOL(fw_iso_context_destroy); @@ -155,14 +170,17 @@ int fw_iso_context_start(struct fw_iso_context *ctx, } EXPORT_SYMBOL(fw_iso_context_start); +int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels) +{ + return ctx->card->driver->set_iso_channels(ctx, channels); +} + int fw_iso_context_queue(struct fw_iso_context *ctx, struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, unsigned long payload) { - struct fw_card *card = ctx->card; - - return card->driver->queue_iso(ctx, packet, buffer, payload); + return ctx->card->driver->queue_iso(ctx, packet, buffer, payload); } EXPORT_SYMBOL(fw_iso_context_queue); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 28621e44b111..e6239f971be6 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -90,6 +90,8 @@ struct fw_card_driver { int (*start_iso)(struct fw_iso_context *ctx, s32 cycle, u32 sync, u32 tags); + int (*set_iso_channels)(struct fw_iso_context *ctx, u64 *channels); + int (*queue_iso)(struct fw_iso_context *ctx, struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 2e4b425847a7..4bda1c1b74ba 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -190,11 +190,13 @@ struct fw_ohci { struct context at_request_ctx; struct context at_response_ctx; - u32 it_context_mask; + u32 it_context_mask; /* unoccupied IT contexts */ struct iso_context *it_context_list; - u64 ir_context_channels; - u32 ir_context_mask; + u64 ir_context_channels; /* unoccupied channels */ + u32 ir_context_mask; /* unoccupied IR contexts */ struct iso_context *ir_context_list; + u64 mc_channels; /* channels in use by the multichannel IR context */ + bool mc_allocated; __be32 *config_rom; dma_addr_t config_rom_bus; @@ -2197,10 +2199,9 @@ static int handle_ir_packet_per_buffer(struct context *context, __le32 *ir_header; void *p; - for (pd = d; pd <= last; pd++) { + for (pd = d; pd <= last; pd++) if (pd->transfer_status) break; - } if (pd > last) /* Descriptor(s) not done yet, stop iteration */ return 0; @@ -2210,16 +2211,38 @@ static int handle_ir_packet_per_buffer(struct context *context, if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { ir_header = (__le32 *) p; - ctx->base.callback(&ctx->base, - le32_to_cpu(ir_header[0]) & 0xffff, - ctx->header_length, ctx->header, - ctx->base.callback_data); + ctx->base.callback.sc(&ctx->base, + le32_to_cpu(ir_header[0]) & 0xffff, + ctx->header_length, ctx->header, + ctx->base.callback_data); ctx->header_length = 0; } return 1; } +/* d == last because each descriptor block is only a single descriptor. */ +static int handle_ir_buffer_fill(struct context *context, + struct descriptor *d, + struct descriptor *last) +{ + struct iso_context *ctx = + container_of(context, struct iso_context, context); + + if (!last->transfer_status) + /* Descriptor(s) not done yet, stop iteration */ + return 0; + + if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) + ctx->base.callback.mc(&ctx->base, + le32_to_cpu(last->data_address) + + le16_to_cpu(last->req_count) - + le16_to_cpu(last->res_count), + ctx->base.callback_data); + + return 1; +} + static int handle_it_packet(struct context *context, struct descriptor *d, struct descriptor *last) @@ -2245,72 +2268,118 @@ static int handle_it_packet(struct context *context, ctx->header_length += 4; } if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { - ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count), - ctx->header_length, ctx->header, - ctx->base.callback_data); + ctx->base.callback.sc(&ctx->base, le16_to_cpu(last->res_count), + ctx->header_length, ctx->header, + ctx->base.callback_data); ctx->header_length = 0; } return 1; } +static void set_multichannel_mask(struct fw_ohci *ohci, u64 channels) +{ + u32 hi = channels >> 32, lo = channels; + + 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; +} + static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, int type, int channel, size_t header_size) { struct fw_ohci *ohci = fw_ohci(card); - struct iso_context *ctx, *list; - descriptor_callback_t callback; - u64 *channels, dont_care = ~0ULL; - u32 *mask, regs; + struct iso_context *uninitialized_var(ctx); + descriptor_callback_t uninitialized_var(callback); + u64 *uninitialized_var(channels); + u32 *uninitialized_var(mask), uninitialized_var(regs); unsigned long flags; - int index, ret = -ENOMEM; + int index, ret = -EBUSY; - if (type == FW_ISO_CONTEXT_TRANSMIT) { - channels = &dont_care; - mask = &ohci->it_context_mask; - list = ohci->it_context_list; + spin_lock_irqsave(&ohci->lock, flags); + + switch (type) { + case FW_ISO_CONTEXT_TRANSMIT: + mask = &ohci->it_context_mask; callback = handle_it_packet; - } else { + index = ffs(*mask) - 1; + if (index >= 0) { + *mask &= ~(1 << index); + regs = OHCI1394_IsoXmitContextBase(index); + ctx = &ohci->it_context_list[index]; + } + break; + + case FW_ISO_CONTEXT_RECEIVE: channels = &ohci->ir_context_channels; - mask = &ohci->ir_context_mask; - list = ohci->ir_context_list; + mask = &ohci->ir_context_mask; callback = handle_ir_packet_per_buffer; - } + index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; + if (index >= 0) { + *channels &= ~(1ULL << channel); + *mask &= ~(1 << index); + regs = OHCI1394_IsoRcvContextBase(index); + ctx = &ohci->ir_context_list[index]; + } + break; - spin_lock_irqsave(&ohci->lock, flags); - index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; - if (index >= 0) { - *channels &= ~(1ULL << channel); - *mask &= ~(1 << index); + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + mask = &ohci->ir_context_mask; + callback = handle_ir_buffer_fill; + index = !ohci->mc_allocated ? ffs(*mask) - 1 : -1; + if (index >= 0) { + ohci->mc_allocated = true; + *mask &= ~(1 << index); + regs = OHCI1394_IsoRcvContextBase(index); + ctx = &ohci->ir_context_list[index]; + } + break; + + default: + index = -1; + ret = -ENOSYS; } + spin_unlock_irqrestore(&ohci->lock, flags); if (index < 0) - return ERR_PTR(-EBUSY); - - if (type == FW_ISO_CONTEXT_TRANSMIT) - regs = OHCI1394_IsoXmitContextBase(index); - else - regs = OHCI1394_IsoRcvContextBase(index); + return ERR_PTR(ret); - ctx = &list[index]; memset(ctx, 0, sizeof(*ctx)); ctx->header_length = 0; ctx->header = (void *) __get_free_page(GFP_KERNEL); - if (ctx->header == NULL) + if (ctx->header == NULL) { + ret = -ENOMEM; goto out; - + } ret = context_init(&ctx->context, ohci, regs, callback); if (ret < 0) goto out_with_header; + if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) + set_multichannel_mask(ohci, 0); + return &ctx->base; out_with_header: free_page((unsigned long)ctx->header); out: spin_lock_irqsave(&ohci->lock, flags); - *channels |= 1ULL << channel; + + switch (type) { + case FW_ISO_CONTEXT_RECEIVE: + *channels |= 1ULL << channel; + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + ohci->mc_allocated = false; + break; + } *mask |= 1 << index; + spin_unlock_irqrestore(&ohci->lock, flags); return ERR_PTR(ret); @@ -2321,10 +2390,11 @@ static int ohci_start_iso(struct fw_iso_context *base, { struct iso_context *ctx = container_of(base, struct iso_context, base); struct fw_ohci *ohci = ctx->context.ohci; - u32 control, match; + u32 control = IR_CONTEXT_ISOCH_HEADER, match; int index; - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + switch (ctx->base.type) { + case FW_ISO_CONTEXT_TRANSMIT: index = ctx - ohci->it_context_list; match = 0; if (cycle >= 0) @@ -2334,9 +2404,13 @@ static int ohci_start_iso(struct fw_iso_context *base, reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 1 << index); reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index); context_run(&ctx->context, match); - } else { + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + control |= IR_CONTEXT_BUFFER_FILL|IR_CONTEXT_MULTI_CHANNEL_MODE; + /* fall through */ + case FW_ISO_CONTEXT_RECEIVE: index = ctx - ohci->ir_context_list; - control = IR_CONTEXT_ISOCH_HEADER; match = (tags << 28) | (sync << 8) | ctx->base.channel; if (cycle >= 0) { match |= (cycle & 0x07fff) << 12; @@ -2347,6 +2421,7 @@ static int ohci_start_iso(struct fw_iso_context *base, reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << index); reg_write(ohci, CONTEXT_MATCH(ctx->context.regs), match); context_run(&ctx->context, control); + break; } return 0; @@ -2358,12 +2433,17 @@ static int ohci_stop_iso(struct fw_iso_context *base) struct iso_context *ctx = container_of(base, struct iso_context, base); int index; - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + switch (ctx->base.type) { + case FW_ISO_CONTEXT_TRANSMIT: index = ctx - ohci->it_context_list; reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 1 << index); - } else { + break; + + case FW_ISO_CONTEXT_RECEIVE: + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: index = ctx - ohci->ir_context_list; reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 1 << index); + break; } flush_writes(ohci); context_stop(&ctx->context); @@ -2384,24 +2464,65 @@ static void ohci_free_iso_context(struct fw_iso_context *base) spin_lock_irqsave(&ohci->lock, flags); - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: index = ctx - ohci->it_context_list; ohci->it_context_mask |= 1 << index; - } else { + break; + + case FW_ISO_CONTEXT_RECEIVE: index = ctx - ohci->ir_context_list; ohci->ir_context_mask |= 1 << index; ohci->ir_context_channels |= 1ULL << base->channel; + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + index = ctx - ohci->ir_context_list; + ohci->ir_context_mask |= 1 << index; + ohci->ir_context_channels |= ohci->mc_channels; + ohci->mc_channels = 0; + ohci->mc_allocated = false; + break; } spin_unlock_irqrestore(&ohci->lock, flags); } -static int ohci_queue_iso_transmit(struct fw_iso_context *base, - struct fw_iso_packet *packet, - struct fw_iso_buffer *buffer, - unsigned long payload) +static int ohci_set_iso_channels(struct fw_iso_context *base, u64 *channels) +{ + struct fw_ohci *ohci = fw_ohci(base->card); + unsigned long flags; + int ret; + + switch (base->type) { + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + + spin_lock_irqsave(&ohci->lock, flags); + + /* Don't allow multichannel to grab other contexts' channels. */ + if (~ohci->ir_context_channels & ~ohci->mc_channels & *channels) { + *channels = ohci->ir_context_channels; + ret = -EBUSY; + } else { + set_multichannel_mask(ohci, *channels); + ret = 0; + } + + spin_unlock_irqrestore(&ohci->lock, flags); + + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int queue_iso_transmit(struct iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) { - struct iso_context *ctx = container_of(base, struct iso_context, base); struct descriptor *d, *last, *pd; struct fw_iso_packet *p; __le32 *header; @@ -2497,14 +2618,12 @@ static int ohci_queue_iso_transmit(struct fw_iso_context *base, return 0; } -static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, - struct fw_iso_packet *packet, - struct fw_iso_buffer *buffer, - unsigned long payload) +static int queue_iso_packet_per_buffer(struct iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) { - struct iso_context *ctx = container_of(base, struct iso_context, base); struct descriptor *d, *pd; - struct fw_iso_packet *p = packet; dma_addr_t d_bus, page_bus; u32 z, header_z, rest; int i, j, length; @@ -2514,14 +2633,14 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, * The OHCI controller puts the isochronous header and trailer in the * buffer, so we need at least 8 bytes. */ - packet_count = p->header_length / ctx->base.header_size; + packet_count = packet->header_length / ctx->base.header_size; header_size = max(ctx->base.header_size, (size_t)8); /* Get header size in number of descriptors. */ header_z = DIV_ROUND_UP(header_size, sizeof(*d)); page = payload >> PAGE_SHIFT; offset = payload & ~PAGE_MASK; - payload_per_buffer = p->payload_length / packet_count; + payload_per_buffer = packet->payload_length / packet_count; for (i = 0; i < packet_count; i++) { /* d points to the header descriptor */ @@ -2533,7 +2652,7 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, d->control = cpu_to_le16(DESCRIPTOR_STATUS | DESCRIPTOR_INPUT_MORE); - if (p->skip && i == 0) + if (packet->skip && i == 0) d->control |= cpu_to_le16(DESCRIPTOR_WAIT); d->req_count = cpu_to_le16(header_size); d->res_count = d->req_count; @@ -2566,7 +2685,7 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, pd->control = cpu_to_le16(DESCRIPTOR_STATUS | DESCRIPTOR_INPUT_LAST | DESCRIPTOR_BRANCH_ALWAYS); - if (p->interrupt && i == packet_count - 1) + if (packet->interrupt && i == packet_count - 1) pd->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS); context_append(&ctx->context, d, z, header_z); @@ -2575,6 +2694,58 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, return 0; } +static int queue_iso_buffer_fill(struct iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) +{ + struct descriptor *d; + dma_addr_t d_bus, page_bus; + int page, offset, rest, z, i, length; + + page = payload >> PAGE_SHIFT; + offset = payload & ~PAGE_MASK; + rest = packet->payload_length; + + /* We need one descriptor for each page in the buffer. */ + z = DIV_ROUND_UP(offset + rest, PAGE_SIZE); + + if (WARN_ON(offset & 3 || rest & 3 || page + z > buffer->page_count)) + return -EFAULT; + + for (i = 0; i < z; i++) { + d = context_get_descriptors(&ctx->context, 1, &d_bus); + if (d == NULL) + return -ENOMEM; + + d->control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | + DESCRIPTOR_BRANCH_ALWAYS); + if (packet->skip && i == 0) + d->control |= cpu_to_le16(DESCRIPTOR_WAIT); + if (packet->interrupt && i == z - 1) + d->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS); + + if (offset + rest < PAGE_SIZE) + length = rest; + else + length = PAGE_SIZE - offset; + d->req_count = cpu_to_le16(length); + d->res_count = d->req_count; + d->transfer_status = 0; + + page_bus = page_private(buffer->pages[page]); + d->data_address = cpu_to_le32(page_bus + offset); + + rest -= length; + offset = 0; + page++; + + context_append(&ctx->context, d, 1, 0); + } + + return 0; +} + static int ohci_queue_iso(struct fw_iso_context *base, struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, @@ -2582,14 +2753,20 @@ static int ohci_queue_iso(struct fw_iso_context *base, { struct iso_context *ctx = container_of(base, struct iso_context, base); unsigned long flags; - int ret; + int ret = -ENOSYS; spin_lock_irqsave(&ctx->context.ohci->lock, flags); - if (base->type == FW_ISO_CONTEXT_TRANSMIT) - ret = ohci_queue_iso_transmit(base, packet, buffer, payload); - else - ret = ohci_queue_iso_receive_packet_per_buffer(base, packet, - buffer, payload); + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + ret = queue_iso_transmit(ctx, packet, buffer, payload); + break; + case FW_ISO_CONTEXT_RECEIVE: + ret = queue_iso_packet_per_buffer(ctx, packet, buffer, payload); + break; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + ret = queue_iso_buffer_fill(ctx, packet, buffer, payload); + break; + } spin_unlock_irqrestore(&ctx->context.ohci->lock, flags); return ret; @@ -2609,6 +2786,7 @@ static const struct fw_card_driver ohci_driver = { .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, + .set_iso_channels = ohci_set_iso_channels, .queue_iso = ohci_queue_iso, .start_iso = ohci_start_iso, .stop_iso = ohci_stop_iso, diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 14831119ff71..bc5c26fc1c64 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -25,17 +25,18 @@ #include #include -#define FW_CDEV_EVENT_BUS_RESET 0x00 -#define FW_CDEV_EVENT_RESPONSE 0x01 -#define FW_CDEV_EVENT_REQUEST 0x02 -#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 -#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 -#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 +#define FW_CDEV_EVENT_BUS_RESET 0x00 +#define FW_CDEV_EVENT_RESPONSE 0x01 +#define FW_CDEV_EVENT_REQUEST 0x02 +#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 +#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 +#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 /* available since kernel version 2.6.36 */ -#define FW_CDEV_EVENT_REQUEST2 0x06 -#define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 -#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08 +#define FW_CDEV_EVENT_REQUEST2 0x06 +#define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 +#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08 +#define FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL 0x09 /** * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types @@ -218,35 +219,41 @@ struct fw_cdev_event_request2 { * This event is sent when the controller has completed an &fw_cdev_iso_packet * with the %FW_CDEV_ISO_INTERRUPT bit set. * - * Isochronous transmit events: + * Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT): * - * In version 1 of the ABI, &header_length is 0. In version 3 and some - * implementations of version 2 of the ABI, &header_length is a multiple of 4 - * and &header contains timestamps of all packets up until the interrupt packet. - * The format of the timestamps is as described below for isochronous reception. + * In version 3 and some implementations of version 2 of the ABI, &header_length + * is a multiple of 4 and &header contains timestamps of all packets up until + * the interrupt packet. The format of the timestamps is as described below for + * isochronous reception. In version 1 of the ABI, &header_length was 0. * - * Isochronous receive events: + * Isochronous receive events (context type %FW_CDEV_ISO_CONTEXT_RECEIVE): * * The headers stripped of all packets up until and including the interrupt * packet are returned in the @header field. The amount of header data per * packet is as specified at iso context creation by * &fw_cdev_create_iso_context.header_size. * - * In version 1 of this ABI, header data consisted of the 1394 isochronous - * packet header, followed by quadlets from the packet payload if - * &fw_cdev_create_iso_context.header_size > 4. + * Hence, _interrupt.header_length / _context.header_size is the number of + * packets received in this interrupt event. The client can now iterate + * through the mmap()'ed DMA buffer according to this number of packets and + * to the buffer sizes as the client specified in &fw_cdev_queue_iso. * - * In version 2 of this ABI, header data consist of the 1394 isochronous - * packet header, followed by a timestamp quadlet if - * &fw_cdev_create_iso_context.header_size > 4, followed by quadlets from the - * packet payload if &fw_cdev_create_iso_context.header_size > 8. + * Since version 2 of this ABI, the portion for each packet in _interrupt.header + * consists of the 1394 isochronous packet header, followed by a timestamp + * quadlet if &fw_cdev_create_iso_context.header_size > 4, followed by quadlets + * from the packet payload if &fw_cdev_create_iso_context.header_size > 8. * - * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2. + * Format of 1394 iso packet header: 16 bits data_length, 2 bits tag, 6 bits + * channel, 4 bits tcode, 4 bits sy, in big endian byte order. + * data_length is the actual received size of the packet without the four + * 1394 iso packet header bytes. + * + * Format of timestamp: 16 bits invalid, 3 bits cycleSeconds, 13 bits + * cycleCount, in big endian byte order. * - * Format of 1394 iso packet header: 16 bits len, 2 bits tag, 6 bits channel, - * 4 bits tcode, 4 bits sy, in big endian byte order. Format of timestamp: - * 16 bits invalid, 3 bits cycleSeconds, 13 bits cycleCount, in big endian byte - * order. + * In version 1 of the ABI, no timestamp quadlet was inserted; instead, payload + * data followed directly after the 1394 is header if header_size > 4. + * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2. */ struct fw_cdev_event_iso_interrupt { __u64 closure; @@ -256,6 +263,43 @@ struct fw_cdev_event_iso_interrupt { __u32 header[0]; }; +/** + * struct fw_cdev_event_iso_interrupt_mc - An iso buffer chunk was completed + * @closure: See &fw_cdev_event_common; + * set by %FW_CDEV_CREATE_ISO_CONTEXT ioctl + * @type: %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL + * @completed: Offset into the receive buffer; data before this offest is valid + * + * This event is sent in multichannel contexts (context type + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL) for &fw_cdev_iso_packet buffer + * chunks that have the %FW_CDEV_ISO_INTERRUPT bit set. Whether this happens + * when a packet is completed and/or when a buffer chunk is completed depends + * on the hardware implementation. + * + * The buffer is continuously filled with the following data, per packet: + * - the 1394 iso packet header as described at &fw_cdev_event_iso_interrupt, + * but in little endian byte order, + * - packet payload (as many bytes as specified in the data_length field of + * the 1394 iso packet header) in big endian byte order, + * - 0...3 padding bytes as needed to align the following trailer quadlet, + * - trailer quadlet, containing the reception timestamp as described at + * &fw_cdev_event_iso_interrupt, but in little endian byte order. + * + * Hence the per-packet size is data_length (rounded up to a multiple of 4) + 8. + * When processing the data, stop before a packet that would cross the + * @completed offset. + * + * A packet near the end of a buffer chunk will typically spill over into the + * next queued buffer chunk. It is the responsibility of the client to check + * for this condition, assemble a broken-up packet from its parts, and not to + * re-queue any buffer chunks in which as yet unread packet parts reside. + */ +struct fw_cdev_event_iso_interrupt_mc { + __u64 closure; + __u32 type; + __u32 completed; +}; + /** * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed * @closure: See &fw_cdev_event_common; @@ -311,16 +355,18 @@ struct fw_cdev_event_phy_packet { /** * union fw_cdev_event - Convenience union of fw_cdev_event_ types - * @common: Valid for all types - * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET - * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE - * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST - * @request2: Valid if @common.type == %FW_CDEV_EVENT_REQUEST2 - * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT - * @iso_resource: Valid if @common.type == + * @common: Valid for all types + * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET + * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE + * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST + * @request2: Valid if @common.type == %FW_CDEV_EVENT_REQUEST2 + * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT + * @iso_interrupt_mc: Valid if @common.type == + * %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL + * @iso_resource: Valid if @common.type == * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED - * @phy_packet: Valid if @common.type == + * @phy_packet: Valid if @common.type == * %FW_CDEV_EVENT_PHY_PACKET_SENT or * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED * @@ -337,10 +383,11 @@ union fw_cdev_event { struct fw_cdev_event_bus_reset bus_reset; struct fw_cdev_event_response response; struct fw_cdev_event_request request; - struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ + struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ struct fw_cdev_event_iso_interrupt iso_interrupt; - struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ - struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ + struct fw_cdev_event_iso_interrupt_mc iso_interrupt_mc; /* added in 2.6.36 */ + struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ + struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ }; /* available since kernel version 2.6.22 */ @@ -375,6 +422,7 @@ union fw_cdev_event { /* available since kernel version 2.6.36 */ #define FW_CDEV_IOC_SEND_PHY_PACKET _IOWR('#', 0x15, struct fw_cdev_send_phy_packet) #define FW_CDEV_IOC_RECEIVE_PHY_PACKETS _IOW('#', 0x16, struct fw_cdev_receive_phy_packets) +#define FW_CDEV_IOC_SET_ISO_CHANNELS _IOW('#', 0x17, struct fw_cdev_set_iso_channels) /* * ABI version history @@ -391,10 +439,13 @@ union fw_cdev_event { * - shared use and auto-response for FCP registers * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 - * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_* + * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_*, + * and &fw_cdev_allocate.region_end * - implemented &fw_cdev_event_bus_reset.bm_node_id * - added %FW_CDEV_IOC_SEND_PHY_PACKET, _RECEIVE_PHY_PACKETS - * - added &fw_cdev_allocate.region_end + * - added %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL, + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL, and + * %FW_CDEV_IOC_SET_ISO_CHANNELS */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ @@ -597,34 +648,43 @@ struct fw_cdev_remove_descriptor { __u32 handle; }; -#define FW_CDEV_ISO_CONTEXT_TRANSMIT 0 -#define FW_CDEV_ISO_CONTEXT_RECEIVE 1 +#define FW_CDEV_ISO_CONTEXT_TRANSMIT 0 +#define FW_CDEV_ISO_CONTEXT_RECEIVE 1 +#define FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL 2 /* added in 2.6.36 */ /** - * struct fw_cdev_create_iso_context - Create a context for isochronous IO - * @type: %FW_CDEV_ISO_CONTEXT_TRANSMIT or %FW_CDEV_ISO_CONTEXT_RECEIVE - * @header_size: Header size to strip for receive contexts - * @channel: Channel to bind to - * @speed: Speed for transmit contexts - * @closure: To be returned in &fw_cdev_event_iso_interrupt + * struct fw_cdev_create_iso_context - Create a context for isochronous I/O + * @type: %FW_CDEV_ISO_CONTEXT_TRANSMIT or %FW_CDEV_ISO_CONTEXT_RECEIVE or + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL + * @header_size: Header size to strip in single-channel reception + * @channel: Channel to bind to in single-channel reception or transmission + * @speed: Transmission speed + * @closure: To be returned in &fw_cdev_event_iso_interrupt or + * &fw_cdev_event_iso_interrupt_multichannel * @handle: Handle to context, written back by kernel * * Prior to sending or receiving isochronous I/O, a context must be created. * The context records information about the transmit or receive configuration * and typically maps to an underlying hardware resource. A context is set up * for either sending or receiving. It is bound to a specific isochronous - * channel. + * @channel. * - * If a context was successfully created, the kernel writes back a handle to the - * context, which must be passed in for subsequent operations on that context. + * In case of multichannel reception, @header_size and @channel are ignored + * and the channels are selected by %FW_CDEV_IOC_SET_ISO_CHANNELS. + * + * For %FW_CDEV_ISO_CONTEXT_RECEIVE contexts, @header_size must be at least 4 + * and must be a multiple of 4. It is ignored in other context types. * - * For receive contexts, @header_size must be at least 4 and must be a multiple - * of 4. + * @speed is ignored in receive context types. * - * Note that the effect of a @header_size > 4 depends on - * &fw_cdev_get_info.version, as documented at &fw_cdev_event_iso_interrupt. + * If a context was successfully created, the kernel writes back a handle to the + * context, which must be passed in for subsequent operations on that context. * + * Limitations: * No more than one iso context can be created per fd. + * The total number of contexts that all userspace and kernelspace drivers can + * create on a card at a time is a hardware limit, typically 4 or 8 contexts per + * direction, and of them at most one multichannel receive context. */ struct fw_cdev_create_iso_context { __u32 type; @@ -635,6 +695,22 @@ struct fw_cdev_create_iso_context { __u32 handle; }; +/** + * struct fw_cdev_set_iso_channels - Select channels in multichannel reception + * @channels: Bitmask of channels to listen to + * @handle: Handle of the mutichannel receive context + * + * @channels is the bitwise or of 1ULL << n for each channel n to listen to. + * + * The ioctl fails with errno %EBUSY if there is already another receive context + * on a channel in @channels. In that case, the bitmask of all unoccupied + * channels is returned in @channels. + */ +struct fw_cdev_set_iso_channels { + __u64 channels; + __u32 handle; +}; + #define FW_CDEV_ISO_PAYLOAD_LENGTH(v) (v) #define FW_CDEV_ISO_INTERRUPT (1 << 16) #define FW_CDEV_ISO_SKIP (1 << 17) @@ -645,42 +721,72 @@ struct fw_cdev_create_iso_context { /** * struct fw_cdev_iso_packet - Isochronous packet - * @control: Contains the header length (8 uppermost bits), the sy field - * (4 bits), the tag field (2 bits), a sync flag (1 bit), - * a skip flag (1 bit), an interrupt flag (1 bit), and the + * @control: Contains the header length (8 uppermost bits), + * the sy field (4 bits), the tag field (2 bits), a sync flag + * or a skip flag (1 bit), an interrupt flag (1 bit), and the * payload length (16 lowermost bits) - * @header: Header and payload + * @header: Header and payload in case of a transmit context. * * &struct fw_cdev_iso_packet is used to describe isochronous packet queues. - * * Use the FW_CDEV_ISO_ macros to fill in @control. + * The @header array is empty in case of receive contexts. + * + * Context type %FW_CDEV_ISO_CONTEXT_TRANSMIT: + * + * @control.HEADER_LENGTH must be a multiple of 4. It specifies the numbers of + * bytes in @header that will be prepended to the packet's payload. These bytes + * are copied into the kernel and will not be accessed after the ioctl has + * returned. + * + * The @control.SY and TAG fields are copied to the iso packet header. These + * fields are specified by IEEE 1394a and IEC 61883-1. + * + * The @control.SKIP flag specifies that no packet is to be sent in a frame. + * When using this, all other fields except @control.INTERRUPT must be zero. + * + * When a packet with the @control.INTERRUPT flag set has been completed, an + * &fw_cdev_event_iso_interrupt event will be sent. + * + * Context type %FW_CDEV_ISO_CONTEXT_RECEIVE: * - * For transmit packets, the header length must be a multiple of 4 and specifies - * the numbers of bytes in @header that will be prepended to the packet's - * payload; these bytes are copied into the kernel and will not be accessed - * after the ioctl has returned. The sy and tag fields are copied to the iso - * packet header (these fields are specified by IEEE 1394a and IEC 61883-1). - * The skip flag specifies that no packet is to be sent in a frame; when using - * this, all other fields except the interrupt flag must be zero. - * - * For receive packets, the header length must be a multiple of the context's - * header size; if the header length is larger than the context's header size, - * multiple packets are queued for this entry. The sy and tag fields are - * ignored. If the sync flag is set, the context drops all packets until - * a packet with a matching sy field is received (the sync value to wait for is - * specified in the &fw_cdev_start_iso structure). The payload length defines - * how many payload bytes can be received for one packet (in addition to payload - * quadlets that have been defined as headers and are stripped and returned in - * the &fw_cdev_event_iso_interrupt structure). If more bytes are received, the - * additional bytes are dropped. If less bytes are received, the remaining - * bytes in this part of the payload buffer will not be written to, not even by - * the next packet, i.e., packets received in consecutive frames will not - * necessarily be consecutive in memory. If an entry has queued multiple - * packets, the payload length is divided equally among them. - * - * When a packet with the interrupt flag set has been completed, the + * @control.HEADER_LENGTH must be a multiple of the context's header_size. + * If the HEADER_LENGTH is larger than the context's header_size, multiple + * packets are queued for this entry. + * + * The @control.SY and TAG fields are ignored. + * + * If the @control.SYNC flag is set, the context drops all packets until a + * packet with a sy field is received which matches &fw_cdev_start_iso.sync. + * + * @control.PAYLOAD_LENGTH defines how many payload bytes can be received for + * one packet (in addition to payload quadlets that have been defined as headers + * and are stripped and returned in the &fw_cdev_event_iso_interrupt structure). + * If more bytes are received, the additional bytes are dropped. If less bytes + * are received, the remaining bytes in this part of the payload buffer will not + * be written to, not even by the next packet. I.e., packets received in + * consecutive frames will not necessarily be consecutive in memory. If an + * entry has queued multiple packets, the PAYLOAD_LENGTH is divided equally + * among them. + * + * When a packet with the @control.INTERRUPT flag set has been completed, an * &fw_cdev_event_iso_interrupt event will be sent. An entry that has queued * multiple receive packets is completed when its last packet is completed. + * + * Context type %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + * + * Here, &fw_cdev_iso_packet would be more aptly named _iso_buffer_chunk since + * it specifies a chunk of the mmap()'ed buffer, while the number and alignment + * of packets to be placed into the buffer chunk is not known beforehand. + * + * @control.PAYLOAD_LENGTH is the size of the buffer chunk and specifies room + * for header, payload, padding, and trailer bytes of one or more packets. + * It must be a multiple of 4. + * + * @control.HEADER_LENGTH, TAG and SY are ignored. SYNC is treated as described + * for single-channel reception. + * + * When a buffer chunk with the @control.INTERRUPT flag set has been filled + * entirely, an &fw_cdev_event_iso_interrupt_mc event will be sent. */ struct fw_cdev_iso_packet { __u32 control; @@ -689,9 +795,9 @@ struct fw_cdev_iso_packet { /** * struct fw_cdev_queue_iso - Queue isochronous packets for I/O - * @packets: Userspace pointer to packet data + * @packets: Userspace pointer to an array of &fw_cdev_iso_packet * @data: Pointer into mmap()'ed payload buffer - * @size: Size of packet data in bytes + * @size: Size of the @packets array, in bytes * @handle: Isochronous context handle * * Queue a number of isochronous packets for reception or transmission. @@ -704,6 +810,9 @@ struct fw_cdev_iso_packet { * The kernel may or may not queue all packets, but will write back updated * values of the @packets, @data and @size fields, so the ioctl can be * resubmitted easily. + * + * In case of a multichannel receive context, @data must be quadlet-aligned + * relative to the buffer start. */ struct fw_cdev_queue_iso { __u64 packets; diff --git a/include/linux/firewire.h b/include/linux/firewire.h index d974aa4a24c9..1cd637ef62d2 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -372,17 +372,19 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc); * scatter-gather streaming (e.g. assembling video frame automatically). */ struct fw_iso_packet { - u16 payload_length; /* Length of indirect payload. */ - u32 interrupt:1; /* Generate interrupt on this packet */ - u32 skip:1; /* Set to not send packet at all. */ - u32 tag:2; - u32 sy:4; - u32 header_length:8; /* Length of immediate header. */ - u32 header[0]; + u16 payload_length; /* Length of indirect payload */ + u32 interrupt:1; /* Generate interrupt on this packet */ + u32 skip:1; /* tx: Set to not send packet at all */ + /* rx: Sync bit, wait for matching sy */ + u32 tag:2; /* tx: Tag in packet header */ + u32 sy:4; /* tx: Sy in packet header */ + u32 header_length:8; /* Length of immediate header */ + u32 header[0]; /* tx: Top of 1394 isoch. data_block */ }; -#define FW_ISO_CONTEXT_TRANSMIT 0 -#define FW_ISO_CONTEXT_RECEIVE 1 +#define FW_ISO_CONTEXT_TRANSMIT 0 +#define FW_ISO_CONTEXT_RECEIVE 1 +#define FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL 2 #define FW_ISO_CONTEXT_MATCH_TAG0 1 #define FW_ISO_CONTEXT_MATCH_TAG1 2 @@ -406,24 +408,31 @@ struct fw_iso_buffer { int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, int page_count, enum dma_data_direction direction); void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); +size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed); struct fw_iso_context; typedef void (*fw_iso_callback_t)(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *data); +typedef void (*fw_iso_mc_callback_t)(struct fw_iso_context *context, + dma_addr_t completed, void *data); struct fw_iso_context { struct fw_card *card; int type; int channel; int speed; size_t header_size; - fw_iso_callback_t callback; + union { + fw_iso_callback_t sc; + fw_iso_mc_callback_t mc; + } callback; void *callback_data; }; struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, int channel, int speed, size_t header_size, fw_iso_callback_t callback, void *callback_data); +int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels); int fw_iso_context_queue(struct fw_iso_context *ctx, struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, -- cgit v1.2.3