diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firewire/core-cdev.c | 16 | ||||
-rw-r--r-- | drivers/firewire/core-device.c | 127 | ||||
-rw-r--r-- | drivers/firewire/core-transaction.c | 17 | ||||
-rw-r--r-- | drivers/firewire/core.h | 2 | ||||
-rw-r--r-- | drivers/firewire/ohci.c | 87 | ||||
-rw-r--r-- | drivers/firewire/sbp2.c | 5 | ||||
-rw-r--r-- | drivers/media/dvb/firewire/firedtv-fw.c | 39 |
7 files changed, 174 insertions, 119 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 4eeaed57e219..3c1ac0933d24 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -25,6 +25,7 @@ #include <linux/firewire.h> #include <linux/firewire-cdev.h> #include <linux/idr.h> +#include <linux/irqflags.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/kref.h> @@ -32,7 +33,6 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/poll.h> -#include <linux/preempt.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/string.h> @@ -1035,21 +1035,19 @@ static int ioctl_get_cycle_timer(struct client *client, void *buffer) { struct fw_cdev_get_cycle_timer *request = buffer; struct fw_card *card = client->device->card; - unsigned long long bus_time; struct timeval tv; - unsigned long flags; + u32 cycle_time; - preempt_disable(); - local_irq_save(flags); + local_irq_disable(); - bus_time = card->driver->get_bus_time(card); + cycle_time = card->driver->get_cycle_time(card); do_gettimeofday(&tv); - local_irq_restore(flags); - preempt_enable(); + local_irq_enable(); request->local_time = tv.tv_sec * 1000000ULL + tv.tv_usec; - request->cycle_timer = bus_time & 0xffffffff; + request->cycle_timer = cycle_time; + return 0; } diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 9d0dfcbe2c1c..eecd52dc8e98 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -43,7 +43,7 @@ #include "core.h" -void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 * p) +void fw_csr_iterator_init(struct fw_csr_iterator *ci, const u32 *p) { ci->p = p + 1; ci->end = ci->p + (p[0] >> 16); @@ -59,9 +59,76 @@ int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value) } EXPORT_SYMBOL(fw_csr_iterator_next); +static const u32 *search_leaf(const u32 *directory, int search_key) +{ + struct fw_csr_iterator ci; + int last_key = 0, key, value; + + fw_csr_iterator_init(&ci, directory); + while (fw_csr_iterator_next(&ci, &key, &value)) { + if (last_key == search_key && + key == (CSR_DESCRIPTOR | CSR_LEAF)) + return ci.p - 1 + value; + + last_key = key; + } + + return NULL; +} + +static int textual_leaf_to_string(const u32 *block, char *buf, size_t size) +{ + unsigned int quadlets, i; + char c; + + if (!size || !buf) + return -EINVAL; + + quadlets = min(block[0] >> 16, 256U); + if (quadlets < 2) + return -ENODATA; + + if (block[1] != 0 || block[2] != 0) + /* unknown language/character set */ + return -ENODATA; + + block += 3; + quadlets -= 2; + for (i = 0; i < quadlets * 4 && i < size - 1; i++) { + c = block[i / 4] >> (24 - 8 * (i % 4)); + if (c == '\0') + break; + buf[i] = c; + } + buf[i] = '\0'; + + return i; +} + +/** + * fw_csr_string - reads a string from the configuration ROM + * @directory: e.g. root directory or unit directory + * @key: the key of the preceding directory entry + * @buf: where to put the string + * @size: size of @buf, in bytes + * + * The string is taken from a minimal ASCII text descriptor leaf after + * the immediate entry with @key. The string is zero-terminated. + * Returns strlen(buf) or a negative error code. + */ +int fw_csr_string(const u32 *directory, int key, char *buf, size_t size) +{ + const u32 *leaf = search_leaf(directory, key); + if (!leaf) + return -ENOENT; + + return textual_leaf_to_string(leaf, buf, size); +} +EXPORT_SYMBOL(fw_csr_string); + static bool is_fw_unit(struct device *dev); -static int match_unit_directory(u32 *directory, u32 match_flags, +static int match_unit_directory(const u32 *directory, u32 match_flags, const struct ieee1394_device_id *id) { struct fw_csr_iterator ci; @@ -195,7 +262,7 @@ static ssize_t show_immediate(struct device *dev, struct config_rom_attribute *attr = container_of(dattr, struct config_rom_attribute, attr); struct fw_csr_iterator ci; - u32 *dir; + const u32 *dir; int key, value, ret = -ENOENT; down_read(&fw_device_rwsem); @@ -226,10 +293,10 @@ static ssize_t show_text_leaf(struct device *dev, { struct config_rom_attribute *attr = container_of(dattr, struct config_rom_attribute, attr); - struct fw_csr_iterator ci; - u32 *dir, *block = NULL, *p, *end; - int length, key, value, last_key = 0, ret = -ENOENT; - char *b; + const u32 *dir; + size_t bufsize; + char dummy_buf[2]; + int ret; down_read(&fw_device_rwsem); @@ -238,40 +305,23 @@ static ssize_t show_text_leaf(struct device *dev, else dir = fw_device(dev)->config_rom + 5; - fw_csr_iterator_init(&ci, dir); - while (fw_csr_iterator_next(&ci, &key, &value)) { - if (attr->key == last_key && - key == (CSR_DESCRIPTOR | CSR_LEAF)) - block = ci.p - 1 + value; - last_key = key; + if (buf) { + bufsize = PAGE_SIZE - 1; + } else { + buf = dummy_buf; + bufsize = 1; } - if (block == NULL) - goto out; - - length = min(block[0] >> 16, 256U); - if (length < 3) - goto out; + ret = fw_csr_string(dir, attr->key, buf, bufsize); - if (block[1] != 0 || block[2] != 0) - /* Unknown encoding. */ - goto out; - - if (buf == NULL) { - ret = length * 4; - goto out; + if (ret >= 0) { + /* Strip trailing whitespace and add newline. */ + while (ret > 0 && isspace(buf[ret - 1])) + ret--; + strcpy(buf + ret, "\n"); + ret++; } - b = buf; - end = &block[length + 1]; - for (p = &block[3]; p < end; p++, b += 4) - * (u32 *) b = (__force u32) __cpu_to_be32(*p); - - /* Strip trailing whitespace and add newline. */ - while (b--, (isspace(*b) || *b == '\0') && b > buf); - strcpy(b + 1, "\n"); - ret = b + 2 - buf; - out: up_read(&fw_device_rwsem); return ret; @@ -371,7 +421,7 @@ static ssize_t guid_show(struct device *dev, return ret; } -static int units_sprintf(char *buf, u32 *directory) +static int units_sprintf(char *buf, const u32 *directory) { struct fw_csr_iterator ci; int key, value; @@ -453,7 +503,8 @@ static int read_rom(struct fw_device *device, */ static int read_bus_info_block(struct fw_device *device, int generation) { - u32 *rom, *stack, *old_rom, *new_rom; + const u32 *old_rom, *new_rom; + u32 *rom, *stack; u32 sp, key; int i, end, length, ret = -1; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 495849eb13cc..673b03f8b4ec 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -921,23 +921,15 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, void *payload, size_t length, void *callback_data) { int reg = offset & ~CSR_REGISTER_BASE; - unsigned long long bus_time; __be32 *data = payload; int rcode = RCODE_COMPLETE; switch (reg) { case CSR_CYCLE_TIME: - case CSR_BUS_TIME: - if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) { - rcode = RCODE_TYPE_ERROR; - break; - } - - bus_time = card->driver->get_bus_time(card); - if (reg == CSR_CYCLE_TIME) - *data = cpu_to_be32(bus_time); + if (TCODE_IS_READ_REQUEST(tcode) && length == 4) + *data = cpu_to_be32(card->driver->get_cycle_time(card)); else - *data = cpu_to_be32(bus_time >> 25); + rcode = RCODE_TYPE_ERROR; break; case CSR_BROADCAST_CHANNEL: @@ -968,6 +960,9 @@ 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/core.h b/drivers/firewire/core.h index ed3b1a765c00..fb0321300cce 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -70,7 +70,7 @@ struct fw_card_driver { int (*enable_phys_dma)(struct fw_card *card, int node_id, int generation); - u64 (*get_bus_time)(struct fw_card *card); + u32 (*get_cycle_time)(struct fw_card *card); struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 43ebf337b131..0f7c4bb978e7 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -38,7 +38,6 @@ #include <linux/spinlock.h> #include <linux/string.h> -#include <asm/atomic.h> #include <asm/byteorder.h> #include <asm/page.h> #include <asm/system.h> @@ -187,11 +186,11 @@ struct fw_ohci { int node_id; int generation; int request_generation; /* for timestamping incoming requests */ - atomic_t bus_seconds; bool use_dualbuffer; bool old_uninorth; bool bus_reset_packet_quirk; + bool iso_cycle_timer_quirk; /* * Spinlock for accessing fw_ohci data. Never call out of @@ -275,7 +274,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%s\n", evt, + fw_notify("IRQ %08x%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" : "", @@ -285,7 +284,6 @@ 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" : "", @@ -293,8 +291,7 @@ static void log_irqs(u32 evt) OHCI1394_RSPkt | OHCI1394_reqTxComplete | OHCI1394_respTxComplete | OHCI1394_isochRx | OHCI1394_isochTx | OHCI1394_postedWriteErr | - OHCI1394_cycleTooLong | OHCI1394_cycle64Seconds | - OHCI1394_cycleInconsistent | + OHCI1394_cycleTooLong | OHCI1394_cycleInconsistent | OHCI1394_regAccessFail | OHCI1394_busReset) ? " ?" : ""); } @@ -1384,7 +1381,7 @@ static void bus_reset_tasklet(unsigned long data) static irqreturn_t irq_handler(int irq, void *data) { struct fw_ohci *ohci = data; - u32 event, iso_event, cycle_time; + u32 event, iso_event; int i; event = reg_read(ohci, OHCI1394_IntEventClear); @@ -1454,12 +1451,6 @@ static irqreturn_t irq_handler(int irq, void *data) fw_notify("isochronous cycle inconsistent\n"); } - if (event & OHCI1394_cycle64Seconds) { - cycle_time = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - if ((cycle_time & 0x80000000) == 0) - atomic_inc(&ohci->bus_seconds); - } - return IRQ_HANDLED; } @@ -1553,8 +1544,7 @@ static int ohci_enable(struct fw_card *card, OHCI1394_reqTxComplete | OHCI1394_respTxComplete | OHCI1394_isochRx | OHCI1394_isochTx | OHCI1394_postedWriteErr | OHCI1394_cycleTooLong | - OHCI1394_cycleInconsistent | - OHCI1394_cycle64Seconds | OHCI1394_regAccessFail | + OHCI1394_cycleInconsistent | OHCI1394_regAccessFail | OHCI1394_masterIntEnable); if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); @@ -1794,16 +1784,61 @@ static int ohci_enable_phys_dma(struct fw_card *card, #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */ } -static u64 ohci_get_bus_time(struct fw_card *card) +static u32 cycle_timer_ticks(u32 cycle_timer) { - struct fw_ohci *ohci = fw_ohci(card); - u32 cycle_time; - u64 bus_time; + u32 ticks; - cycle_time = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - bus_time = ((u64)atomic_read(&ohci->bus_seconds) << 32) | cycle_time; + ticks = cycle_timer & 0xfff; + ticks += 3072 * ((cycle_timer >> 12) & 0x1fff); + ticks += (3072 * 8000) * (cycle_timer >> 25); - return bus_time; + 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 ohci_get_cycle_time(struct fw_card *card) +{ + struct fw_ohci *ohci = fw_ohci(card); + u32 c0, c1, c2; + u32 t0, t1, t2; + s32 diff01, diff12; + int i; + + c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + + if (ohci->iso_cycle_timer_quirk) { + 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 void copy_iso_headers(struct iso_context *ctx, void *p) @@ -2383,7 +2418,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_bus_time = ohci_get_bus_time, + .get_cycle_time = ohci_get_cycle_time, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, @@ -2503,6 +2538,10 @@ static int __devinit pci_probe(struct pci_dev *dev, #endif ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI; + ohci->iso_cycle_timer_quirk = dev->vendor == PCI_VENDOR_ID_AL || + dev->vendor == PCI_VENDOR_ID_NEC || + dev->vendor == PCI_VENDOR_ID_VIA; + ar_context_init(&ohci->ar_request_ctx, ohci, OHCI1394_AsReqRcvContextControlSet); @@ -2662,7 +2701,7 @@ static int pci_resume(struct pci_dev *dev) } #endif -static struct pci_device_id pci_table[] = { +static const struct pci_device_id pci_table[] = { { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_FIREWIRE_OHCI, ~0) }, { } }; diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index d485cdd8cbac..7e33b0b1704c 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -1014,7 +1014,8 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) return 0; } -static int sbp2_scan_logical_unit_dir(struct sbp2_target *tgt, u32 *directory) +static int sbp2_scan_logical_unit_dir(struct sbp2_target *tgt, + const u32 *directory) { struct fw_csr_iterator ci; int key, value; @@ -1027,7 +1028,7 @@ static int sbp2_scan_logical_unit_dir(struct sbp2_target *tgt, u32 *directory) return 0; } -static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory, +static int sbp2_scan_unit_dir(struct sbp2_target *tgt, const u32 *directory, u32 *model, u32 *firmware_revision) { struct fw_csr_iterator ci; diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c index 6223bf01efe9..4253b7ab0097 100644 --- a/drivers/media/dvb/firewire/firedtv-fw.c +++ b/drivers/media/dvb/firewire/firedtv-fw.c @@ -239,47 +239,18 @@ static const struct fw_address_region fcp_region = { }; /* Adjust the template string if models with longer names appear. */ -#define MAX_MODEL_NAME_LEN ((int)DIV_ROUND_UP(sizeof("FireDTV ????"), 4)) - -static size_t model_name(u32 *directory, __be32 *buffer) -{ - struct fw_csr_iterator ci; - int i, length, key, value, last_key = 0; - u32 *block = NULL; - - fw_csr_iterator_init(&ci, directory); - while (fw_csr_iterator_next(&ci, &key, &value)) { - if (last_key == CSR_MODEL && - key == (CSR_DESCRIPTOR | CSR_LEAF)) - block = ci.p - 1 + value; - last_key = key; - } - - if (block == NULL) - return 0; - - length = min((int)(block[0] >> 16) - 2, MAX_MODEL_NAME_LEN); - if (length <= 0) - return 0; - - /* fast-forward to text string */ - block += 3; - - for (i = 0; i < length; i++) - buffer[i] = cpu_to_be32(block[i]); - - return length * 4; -} +#define MAX_MODEL_NAME_LEN sizeof("FireDTV ????") static int node_probe(struct device *dev) { struct firedtv *fdtv; - __be32 name[MAX_MODEL_NAME_LEN]; + char name[MAX_MODEL_NAME_LEN]; int name_len, err; - name_len = model_name(fw_unit(dev)->directory, name); + name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL, + name, sizeof(name)); - fdtv = fdtv_alloc(dev, &backend, (char *)name, name_len); + fdtv = fdtv_alloc(dev, &backend, name, name_len >= 0 ? name_len : 0); if (!fdtv) return -ENOMEM; |