diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-19 22:45:37 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-19 22:45:37 +0000 |
commit | 6d403070f28cd44860fdb3a53be5da0275c65cf4 (patch) | |
tree | 0d0e7fe7b5fb7568d19e11d7d862b77a866ce081 /drivers/ieee1394 | |
parent | ecf1bf5f6c2e668d03b0a9fb026db7aa41e292e1 (diff) |
Merge with 2.4.0-test1-ac21 + pile of MIPS cleanups to make merging
possible. Chainsawed RM200 kernel to compile again. Jazz machine
status unknown.
Diffstat (limited to 'drivers/ieee1394')
-rw-r--r-- | drivers/ieee1394/Makefile | 8 | ||||
-rw-r--r-- | drivers/ieee1394/aic5800.c | 7 | ||||
-rw-r--r-- | drivers/ieee1394/guid.c | 226 | ||||
-rw-r--r-- | drivers/ieee1394/guid.h | 54 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_core.c | 87 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_syms.c | 6 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_transactions.c | 32 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_types.h | 17 | ||||
-rw-r--r-- | drivers/ieee1394/ohci1394.c | 1230 | ||||
-rw-r--r-- | drivers/ieee1394/ohci1394.h | 63 | ||||
-rw-r--r-- | drivers/ieee1394/pcilynx.c | 286 | ||||
-rw-r--r-- | drivers/ieee1394/pcilynx.h | 174 | ||||
-rw-r--r-- | drivers/ieee1394/raw1394.c | 13 | ||||
-rw-r--r-- | drivers/ieee1394/video1394.h | 50 |
14 files changed, 1780 insertions, 473 deletions
diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile index f2ce58897..c89374f45 100644 --- a/drivers/ieee1394/Makefile +++ b/drivers/ieee1394/Makefile @@ -25,15 +25,13 @@ O_OBJS := OX_OBJS := ifeq ($(CONFIG_IEEE1394),y) -L_OBJS += ieee1394.o hosts.o highlevel.o csr.o -O_TARGET = ieee1394.o -O_OBJS += ieee1394_core.o ieee1394_transactions.o -OX_OBJS += ieee1394_syms.o +L_OBJS += ieee1394_core.o ieee1394_transactions.o hosts.o highlevel.o csr.o guid.o +LX_OBJS += ieee1394_syms.o else ifeq ($(CONFIG_IEEE1394),m) M_OBJS += ieee1394.o O_TARGET = ieee1394.o - O_OBJS += ieee1394_core.o ieee1394_transactions.o hosts.o highlevel.o csr.o + O_OBJS += ieee1394_core.o ieee1394_transactions.o hosts.o highlevel.o csr.o guid.o OX_OBJS += ieee1394_syms.o endif endif diff --git a/drivers/ieee1394/aic5800.c b/drivers/ieee1394/aic5800.c index be6516684..6cf3779d5 100644 --- a/drivers/ieee1394/aic5800.c +++ b/drivers/ieee1394/aic5800.c @@ -723,15 +723,18 @@ inline static void * quadquadalign(void *buf) static int add_card(struct pci_dev *dev) { -#define FAIL(fmt, args...) \ +#define FAIL(fmt, args...) do {\ PRINT_G(KERN_ERR, fmt , ## args); \ num_of_cards--; \ remove_card(aic); \ - return 1; + return 1; } while (0) struct aic5800 *aic; /* shortcut to currently handled device */ unsigned long page; + if (pci_enable_device(dev)) + return 1; + if (num_of_cards == MAX_AIC5800_CARDS) { PRINT_G(KERN_WARNING, "cannot handle more than %d cards. " "Adjust MAX_AIC5800_CARDS in aic5800.h.", diff --git a/drivers/ieee1394/guid.c b/drivers/ieee1394/guid.c new file mode 100644 index 000000000..36ac332c5 --- /dev/null +++ b/drivers/ieee1394/guid.c @@ -0,0 +1,226 @@ +/* + * IEEE 1394 for Linux + * + * GUID collection and management + * + * Copyright (C) 2000 Andreas E. Bombe + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <asm/byteorder.h> +#include <asm/atomic.h> + +#include "ieee1394_types.h" +#include "ieee1394.h" +#include "hosts.h" +#include "ieee1394_transactions.h" +#include "highlevel.h" +#include "csr.h" + + +static atomic_t outstanding_requests; + +static LIST_HEAD(guid_list); +rwlock_t guid_lock = RW_LOCK_UNLOCKED; + +struct guid_entry { + struct list_head list; + atomic_t refcount; + + u64 guid; + + struct hpsb_host *host; + nodeid_t node_id; + + atomic_t generation; +}; + +struct guid_req { + struct hpsb_packet *pkt; + struct tq_struct tq; +}; + + +static struct guid_entry *create_guid_entry(void) +{ + struct guid_entry *ge; + unsigned long flags; + + ge = kmalloc(sizeof(struct guid_entry), SLAB_ATOMIC); + if (!ge) return NULL; + + INIT_LIST_HEAD(&ge->list); + atomic_set(&ge->refcount, 0); + ge->guid = (u64) -1; + ge->host = NULL; + ge->node_id = 0; + atomic_set(&ge->generation, -1); + + write_lock_irqsave(&guid_lock, flags); + list_add_tail(&ge->list, &guid_list); + write_unlock_irqrestore(&guid_lock, flags); + + return ge; +} + +static struct guid_entry *find_entry(u64 guid) +{ + struct list_head *lh; + struct guid_entry *ge; + + lh = guid_list.next; + while (lh != &guid_list) { + ge = list_entry(lh, struct guid_entry, list); + if (ge->guid == guid) return ge; + lh = lh->next; + } + + return NULL; +} + +static void associate_guid(struct hpsb_host *host, nodeid_t nodeid, u64 guid) +{ + struct guid_entry *ge; + unsigned long flags; + + HPSB_DEBUG("node %d on host 0x%p has GUID 0x%08x%08x", + nodeid & NODE_MASK, host, (unsigned int)(guid >> 32), + (unsigned int)(guid & 0xffffffff)); + + read_lock_irqsave(&guid_lock, flags); + ge = find_entry(guid); + read_unlock_irqrestore(&guid_lock, flags); + + if (!ge) ge = create_guid_entry(); + if (!ge) return; + + ge->host = host; + ge->node_id = nodeid; + ge->guid = guid; + + atomic_set(&ge->generation, get_hpsb_generation()); +} + +static void pkt_complete(struct guid_req *req) +{ + struct hpsb_packet *pkt = req->pkt; + int rcode = (pkt->header[1] >> 12) & 0xf; + + if (pkt->ack_code == ACK_PENDING && rcode == RCODE_COMPLETE) { + if (*(char *)pkt->data > 1) { + associate_guid(pkt->host, pkt->node_id, + ((u64)be32_to_cpu(pkt->data[3]) << 32) + | be32_to_cpu(pkt->data[4])); + } else { + HPSB_DEBUG("minimal ROM on node %d", + pkt->node_id & NODE_MASK); + } + } else { + HPSB_DEBUG("guid transaction error: ack %d, rcode %d", + pkt->ack_code, rcode); + } + + free_tlabel(pkt->host, pkt->node_id, pkt->tlabel); + free_hpsb_packet(pkt); + kfree(req); + + if (atomic_dec_and_test(&outstanding_requests)) { + /* FIXME: free unreferenced and inactive GUID entries. */ + } +} + + +static void host_reset(struct hpsb_host *host) +{ + struct guid_req *greq; + struct hpsb_packet *pkt; + struct selfid *sid = (struct selfid *)host->topology_map; + int nodecount = host->node_count; + nodeid_t nodeid = LOCAL_BUS; + + for (; nodecount; nodecount--, nodeid++, sid++) { + while (sid->extended) sid++; + if (!sid->link_active) continue; + if (nodeid == host->node_id) continue; + + greq = kmalloc(sizeof(struct guid_req), SLAB_ATOMIC); + if (!greq) { + HPSB_ERR("out of memory in GUID processing"); + return; + } + + pkt = hpsb_make_readbpacket(host, nodeid, + CSR_REGISTER_BASE + CSR_CONFIG_ROM, + 20); + if (!pkt) { + kfree(greq); + HPSB_ERR("out of memory in GUID processing"); + return; + } + + greq->tq.next = NULL; + greq->tq.sync = 0; + greq->tq.routine = (void (*)(void*))pkt_complete; + greq->tq.data = greq; + greq->pkt = pkt; + + queue_task(&greq->tq, &pkt->complete_tq); + + if (!hpsb_send_packet(pkt)) { + free_tlabel(pkt->host, pkt->node_id, pkt->tlabel); + free_hpsb_packet(pkt); + kfree(greq); + HPSB_NOTICE("failed to send packet in GUID processing"); + } + + HPSB_INFO("GUID request sent to node %d", nodeid & NODE_MASK); + atomic_inc(&outstanding_requests); + } +} + + +struct guid_entry *hpsb_guid_get_handle(u64 guid) +{ + unsigned long flags; + struct guid_entry *ge; + + read_lock_irqsave(&guid_lock, flags); + ge = find_entry(guid); + if (ge) atomic_inc(&ge->refcount); + read_unlock_irqrestore(&guid_lock, flags); + + return ge; +} + +struct hpsb_host *hpsb_guid_localhost(struct guid_entry *ge) +{ + if (atomic_read(&ge->generation) != get_hpsb_generation()) return NULL; + if (ge->node_id == ge->host->node_id) return ge->host; + return NULL; +} + +int hpsb_guid_fill_packet(struct guid_entry *ge, struct hpsb_packet *pkt) +{ + if (atomic_read(&ge->generation) != get_hpsb_generation()) return 0; + + pkt->host = ge->host; + pkt->node_id = ge->node_id; + pkt->generation = atomic_read(&ge->generation); + return 1; +} + + +static struct hpsb_highlevel_ops guid_ops = { + host_reset: host_reset, +}; + +void init_ieee1394_guid(void) +{ + atomic_set(&outstanding_requests, 0); + + if (!hpsb_register_highlevel("GUID manager", &guid_ops)) { + HPSB_ERR("out of memory during ieee1394 initialization"); + } +} diff --git a/drivers/ieee1394/guid.h b/drivers/ieee1394/guid.h new file mode 100644 index 000000000..e3a87dda9 --- /dev/null +++ b/drivers/ieee1394/guid.h @@ -0,0 +1,54 @@ + +#ifndef _IEEE1394_GUID_H +#define _IEEE1394_GUID_H + + +/* + * General information: Finding out which GUID belongs to which node is done by + * sending packets and therefore waiting for the answers. Wherever it is + * mentioned that a node is inaccessible this could just as well mean that we + * just don't know yet (usually, bus reset handlers can't rely on GUIDs being + * associated with current nodes). + */ + +struct guid_entry; +typedef struct guid_entry *hpsb_guid_t; + + +/* + * Returns a guid handle (which has its reference count incremented) or NULL if + * there is the GUID in question is not known of. Getting a valid handle does + * not mean that the node with this GUID is currently accessible (might not be + * plugged in or powered down). + */ +hpsb_guid_t hpsb_guid_get_handle(u64 guid); + +/* + * If the handle refers to a local host, this function will return the pointer + * to the hpsb_host structure. It will return NULL otherwise. Once you have + * established it is a local host, you can use that knowledge from then on (the + * GUID won't wander to an external node). + * + * Note that the local GUID currently isn't collected, so this will always + * return NULL. + */ +struct hpsb_host *hpsb_guid_localhost(hpsb_guid_t handle); + +/* + * This will fill in the given, pre-initialised hpsb_packet with the current + * information from the GUID handle (host, node ID, generation number). It will + * return false if the node owning the GUID is not accessible (and not modify the + * hpsb_packet) and return true otherwise. + * + * Note that packet sending may still fail in hpsb_send_packet if a bus reset + * happens while you are trying to set up the packet (due to obsolete generation + * number). It will at least reliably fail so that you don't accidentally and + * unknowingly send your packet to the wrong node. + */ +int hpsb_guid_fill_packet(hpsb_guid_t handle, struct hpsb_packet *pkt); + + +void init_ieee1394_guid(void); + + +#endif /* _IEEE1394_GUID_H */ diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index c7ce73144..ffcfc7f52 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -25,6 +25,7 @@ #include "highlevel.h" #include "ieee1394_transactions.h" #include "csr.h" +#include "guid.h" atomic_t hpsb_generation = ATOMIC_INIT(0); @@ -45,6 +46,26 @@ static void dump_packet(const char *text, quadlet_t *data, int size) } +/** + * alloc_hpsb_packet - allocate new packet structure + * @data_size: size of the data block to be allocated + * + * This function allocates, initializes and returns a new &struct hpsb_packet. + * It can be used in interrupt context. A header block is always included, its + * size is big enough to contain all possible 1394 headers. The data block is + * only allocated when @data_size is not zero. + * + * For packets for which responses will be received the @data_size has to be big + * enough to contain the response's data block since no further allocation + * occurs at response matching time. + * + * The packet's generation value will be set to the current generation number + * for ease of use. Remember to overwrite it with your own recorded generation + * number if you can not be sure that your code will not race with a bus reset. + * + * Return value: A pointer to a &struct hpsb_packet or NULL on allocation + * failure. + */ struct hpsb_packet *alloc_hpsb_packet(size_t data_size) { struct hpsb_packet *packet = NULL; @@ -83,6 +104,14 @@ struct hpsb_packet *alloc_hpsb_packet(size_t data_size) return packet; } + +/** + * free_hpsb_packet - free packet and data associated with it + * @packet: packet to free (is NULL safe) + * + * This function will free packet->data, packet->header and finally the packet + * itself. + */ void free_hpsb_packet(struct hpsb_packet *packet) { if (packet == NULL) { @@ -336,6 +365,20 @@ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, queue_task(&host->timeout_tq, &tq_timer); } +/** + * hpsb_send_packet - transmit a packet on the bus + * @packet: packet to send + * + * The packet is sent through the host specified in the packet->host field. + * Before sending, the packet's transmit speed is automatically determined using + * the local speed map. + * + * Possibilities for failure are that host is either not initialized, in bus + * reset, the packet's generation number doesn't match the current generation + * number or the host reports a transmit error. + * + * Return value: False (0) on failure, true (1) otherwise. + */ int hpsb_send_packet(struct hpsb_packet *packet) { struct hpsb_host *host = packet->host; @@ -721,47 +764,6 @@ void abort_timedouts(struct hpsb_host *host) } -#if 0 -int hpsb_host_thread(void *hostPointer) -{ - struct hpsb_host *host = (struct hpsb_host *)hostPointer; - - /* I don't understand why, but I just want to be on the safe side. */ - lock_kernel(); - - HPSB_INFO(__FUNCTION__ " starting for one %s adapter", - host->template->name); - - exit_mm(current); - exit_files(current); - exit_fs(current); - - strcpy(current->comm, "ieee1394 thread"); - - /* ... but then again, I think the following is safe. */ - unlock_kernel(); - - for (;;) { - siginfo_t info; - unsigned long signr; - - if (signal_pending(current)) { - spin_lock_irq(¤t->sigmask_lock); - signr = dequeue_signal(¤t->blocked, &info); - spin_unlock_irq(¤t->sigmask_lock); - - break; - } - - abort_timedouts(host); - } - - HPSB_INFO(__FUNCTION__ " exiting"); - return 0; -} -#endif - - #ifndef MODULE void __init ieee1394_init(void) @@ -769,6 +771,7 @@ void __init ieee1394_init(void) register_builtin_lowlevels(); init_hpsb_highlevel(); init_csr(); + init_ieee1394_guid(); } #else @@ -777,6 +780,8 @@ int init_module(void) { init_hpsb_highlevel(); init_csr(); + init_ieee1394_guid(); + return 0; } diff --git a/drivers/ieee1394/ieee1394_syms.c b/drivers/ieee1394/ieee1394_syms.c index 3a99b0923..c1ebadd10 100644 --- a/drivers/ieee1394/ieee1394_syms.c +++ b/drivers/ieee1394/ieee1394_syms.c @@ -8,6 +8,7 @@ #include <linux/types.h> #include <linux/module.h> +#include <linux/string.h> #include "ieee1394_types.h" #include "hosts.h" @@ -15,6 +16,7 @@ #include "ieee1394_transactions.h" /* #include "events.h" */ #include "highlevel.h" +#include "guid.h" EXPORT_SYMBOL(hpsb_register_lowlevel); EXPORT_SYMBOL(hpsb_unregister_lowlevel); @@ -52,3 +54,7 @@ EXPORT_SYMBOL(highlevel_read); EXPORT_SYMBOL(highlevel_write); EXPORT_SYMBOL(highlevel_lock); EXPORT_SYMBOL(highlevel_lock64); + +EXPORT_SYMBOL(hpsb_guid_get_handle); +EXPORT_SYMBOL(hpsb_guid_localhost); +EXPORT_SYMBOL(hpsb_guid_fill_packet); diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index 6f07a7f6a..c5c5cc4ad 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -132,6 +132,26 @@ void fill_iso_packet(struct hpsb_packet *packet, int length, int channel, } +/** + * get_tlabel - allocate a transaction label + * @host: host to be used for transmission + * @nodeid: the node ID of the transmission target + * @wait: whether to sleep if no tlabel is available + * + * Every asynchronous transaction on the 1394 bus needs a transaction label to + * match the response to the request. This label has to be different from any + * other transaction label in an outstanding request to the same node to make + * matching possible without ambiguity. + * + * There are 64 different tlabels, so an allocated tlabel has to be freed with + * free_tlabel() after the transaction is complete (unless it's reused again for + * the same target node). + * + * @wait must not be set to true if you are calling from interrupt context. + * + * Return value: The allocated transaction label or -1 if there was no free + * tlabel and @wait is false. + */ int get_tlabel(struct hpsb_host *host, nodeid_t nodeid, int wait) { unsigned long flags; @@ -166,6 +186,18 @@ int get_tlabel(struct hpsb_host *host, nodeid_t nodeid, int wait) } } +/** + * free_tlabel - free an allocated transaction label + * @host: host to be used for transmission + * @nodeid: the node ID of the transmission target + * @tlabel: the transaction label to free + * + * Frees the transaction label allocated with get_tlabel(). The tlabel has to + * be freed after the transaction is complete (i.e. response was received for a + * split transaction or packet was sent for a unified transaction). + * + * A tlabel must not be freed twice. + */ void free_tlabel(struct hpsb_host *host, nodeid_t nodeid, int tlabel) { unsigned long flags; diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h index d817e61c6..411b79b36 100644 --- a/drivers/ieee1394/ieee1394_types.h +++ b/drivers/ieee1394/ieee1394_types.h @@ -27,7 +27,19 @@ static __inline__ void list_add_tail(struct list_head *new, struct list_head *he #define __constant_cpu_to_be32(x) __constant_htonl((x)) -#endif +#define set_current_state(state_value) \ + do { current->state = (state_value); } while (0) + +#include <linux/pci.h> +inline static int pci_enable_device(struct pci_dev *dev) +{ + u16 cmd; + pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY); + return 0; +} + +#endif /* Linux version < 2.3 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,18) #include <asm/spinlock.h> @@ -39,6 +51,9 @@ static __inline__ void list_add_tail(struct list_head *new, struct list_head *he #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif typedef __u32 quadlet_t; typedef __u64 octlet_t; diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index c7db523b8..a0933c3e4 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -38,15 +38,25 @@ * . Self-id are sometimes not received properly * if card is initialized with no other nodes * on the bus + * . SONY CXD3222 chip is not working properly + * . Apple PowerBook detected but not working yet */ /* * Acknowledgments: * * Emilie Chung <emilie.chung@axis.com> - * .Tip on Async Request Filter + * . Tip on Async Request Filter * Pascal Drolet <pascal.drolet@informission.ca> - * .Various tips for optimization and functionnalities + * . Various tips for optimization and functionnalities + * Robert Ficklin <rficklin@westengineering.com> + * . Loop in irq_handler + * James Goodwin <jamesg@Filanet.com> + * . Various tips on initialization, self-id reception, etc. + * Albrecht Dress <ad@mpifr-bonn.mpg.de> + * . Apple PowerBook detection + * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> + * . Reset the board properly before leaving */ #include <linux/config.h> @@ -65,6 +75,7 @@ #include <asm/uaccess.h> #include <linux/proc_fs.h> #include <linux/tqueue.h> +#include <linux/delay.h> #include <asm/pgtable.h> #include <asm/page.h> @@ -118,6 +129,7 @@ int supported_chips[][2] = { { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72862 }, { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72870 }, { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72871 }, + { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_FW }, { -1, -1 } }; @@ -128,7 +140,619 @@ static int add_card(struct pci_dev *dev); static void remove_card(struct ti_ohci *ohci); static int init_driver(void); static void dma_trm_bh(void *data); +static void dma_rcv_bh(void *data); static void dma_trm_reset(struct dma_trm_ctx *d); +static void stop_context(struct ti_ohci *ohci, int reg, char *msg); + +#ifdef _VIDEO_1394_H + +/* Taken from bttv.c */ +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +/* [DaveM] I've recoded most of this so that: + * 1) It's easier to tell what is happening + * 2) It's more portable, especially for translating things + * out of vmalloc mapped areas in the kernel. + * 3) Less unnecessary translations happen. + * + * The code used to assume that the kernel vmalloc mappings + * existed in the page tables of every process, this is simply + * not guarenteed. We now use pgd_offset_k which is the + * defined way to get at the kernel page tables. + */ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if(pte_present(pte)) + ret = (pte_page(pte)|(adr&(PAGE_SIZE-1))); + } + } + MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr, page; + + mem=vmalloc(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr, page; + + if (mem) + { + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + +static int free_dma_fbuf_ctx(struct dma_fbuf_ctx **d) +{ + int i; + struct ti_ohci *ohci; + + if ((*d)==NULL) return -1; + + ohci = (struct ti_ohci *)(*d)->ohci; + + DBGMSG(ohci->id, "Freeing dma_fbuf_ctx %d", (*d)->ctx); + + stop_context(ohci, (*d)->ctrlClear, NULL); + + if ((*d)->buf) rvfree((void *)(*d)->buf, + (*d)->num_desc * (*d)->buf_size); + + if ((*d)->prg) { + for (i=0;i<(*d)->num_desc;i++) + if ((*d)->prg[i]) kfree((*d)->prg[i]); + kfree((*d)->prg); + } + + if ((*d)->buffer_status) + kfree((*d)->buffer_status); + + kfree(*d); + *d = NULL; + + return 0; +} + +static struct dma_fbuf_ctx * +alloc_dma_fbuf_ctx(struct ti_ohci *ohci, int ctx, int num_desc, + int buf_size, int channel) +{ + struct dma_fbuf_ctx *d=NULL; + int i; + + d = (struct dma_fbuf_ctx *)kmalloc(sizeof(struct dma_fbuf_ctx), + GFP_KERNEL); + + if (d==NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma_fbuf_ctx"); + return NULL; + } + + d->ohci = (void *)ohci; + d->ctx = ctx; + d->channel = channel; + d->num_desc = num_desc; + d->frame_size = buf_size; + if (buf_size%PAGE_SIZE) + d->buf_size = buf_size + PAGE_SIZE - (buf_size%PAGE_SIZE); + else + d->buf_size = buf_size; + d->ctrlSet = OHCI1394_IrRcvContextControlSet+32*d->ctx; + d->ctrlClear = OHCI1394_IrRcvContextControlClear+32*d->ctx; + d->cmdPtr = OHCI1394_IrRcvCommandPtr+32*d->ctx; + d->ctxMatch = OHCI1394_IrRcvContextMatch+32*d->ctx; + d->nb_cmd = d->buf_size / PAGE_SIZE + 1; + d->last_buffer = 0; + d->buf = NULL; + d->prg = NULL; + init_waitqueue_head(&d->waitq); + + d->buf = rvmalloc(d->num_desc * d->buf_size); + + if (d->buf == NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuffer"); + free_dma_fbuf_ctx(&d); + return NULL; + } + memset(d->buf, 0, d->num_desc * d->buf_size); + + d->prg = kmalloc(d->num_desc * sizeof(struct dma_cmd *), + GFP_KERNEL); + + if (d->prg == NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuf prg"); + free_dma_fbuf_ctx(&d); + return NULL; + } + memset(d->prg, 0, d->num_desc * sizeof(struct dma_cmd *)); + + for (i=0;i<d->num_desc;i++) { + d->prg[i] = kmalloc(d->nb_cmd * sizeof(struct dma_cmd), + GFP_KERNEL); + if (d->prg[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma fbuf prg"); + free_dma_fbuf_ctx(&d); + return NULL; + } + } + + d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int), + GFP_KERNEL); + + if (d->buffer_status == NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuf prg"); + free_dma_fbuf_ctx(&d); + return NULL; + } + memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int)); + + PRINT(KERN_INFO, ohci->id, "Iso DMA to User's Space: %d buffers " + "of size %d allocated for a frame size %d, each with %d prgs", + d->num_desc, d->buf_size, d->frame_size, d->nb_cmd); + + return d; +} + +static void initialize_dma_fbuf_prg(struct dma_cmd *prg, int n, + int frame_size, unsigned long buf) +{ + int i; + int leftsize = (frame_size%PAGE_SIZE) ? + frame_size%PAGE_SIZE : PAGE_SIZE; + + /* the first descriptor will sync and read only 4 bytes */ + prg[0].control = (0x280F << 16) | 4; + prg[0].address = kvirt_to_bus(buf); + prg[0].branchAddress = (virt_to_bus(&(prg[1].control)) + & 0xfffffff0) | 0x1; + prg[0].status = 0; + + /* the second descriptor will read PAGE_SIZE-4 bytes */ + prg[1].control = (0x280C << 16) | (PAGE_SIZE-4); + prg[1].address = kvirt_to_bus(buf+4); + prg[1].branchAddress = (virt_to_bus(&(prg[2].control)) + & 0xfffffff0) | 0x1; + prg[1].status = 0; + + for (i=2;i<n-1;i++) { + prg[i].control = (0x280C << 16) | PAGE_SIZE; + prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); + + prg[i].branchAddress = + (virt_to_bus(&(prg[i+1].control)) + & 0xfffffff0) | 0x1; + + prg[i].status = 0; + } + + /* the last descriptor will generate an interrupt */ + prg[i].control = (0x283C << 16) | leftsize; + prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); + prg[i].status = 0; +} + +static void initialize_dma_fbuf_ctx(struct dma_fbuf_ctx *d, int tag) +{ + struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; + int i; + + stop_context(ohci, d->ctrlClear, NULL); + + for (i=0;i<d->num_desc;i++) { + initialize_dma_fbuf_prg(d->prg[i], d->nb_cmd, d->frame_size, + (unsigned long)d->buf+i*d->buf_size); + } + + /* Set bufferFill, no header */ + reg_write(ohci, d->ctrlSet, 0x80000000); + + /* Set the context match register to match on all tags, + sync for sync tag, and listen to d->channel */ + reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); + + /* Set up isoRecvIntMask to generate interrupts */ + reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx); +} + +/* find which context is listening to this channel */ +int fbuf_ctx_listening(struct ti_ohci *ohci, int channel) +{ + int i; + for (i=0;i<ohci->nb_iso_ctx-1;i++) + if (ohci->fbuf_context[i]) { + if (ohci->fbuf_context[i]->channel==channel) + return i; + } + + PRINT(KERN_ERR, ohci->id, + "no iso context is listening to channel %d", + channel); + return -1; +} + +static int ohci_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; + + switch(cmd) + { + case VIDEO1394_LISTEN_CHANNEL: + { + struct video1394_mmap v; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) { + PRINT(KERN_ERR, ohci->id, + "iso channel %d out of bound", v.channel); + return -EFAULT; + } + if (test_and_set_bit(v.channel, &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is already taken", v.channel); + return -EFAULT; + } + + /* find a free iso context */ + for (i=0;i<ohci->nb_iso_ctx-1;i++) + if (ohci->fbuf_context[i]==NULL) break; + + if (i==(ohci->nb_iso_ctx-1)) { + PRINT(KERN_ERR, ohci->id, "no iso context available"); + return -EFAULT; + } + + if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { + PRINT(KERN_ERR, ohci->id, + "%d buffers of size %d bytes is too big", + v.nb_buffers, v.buf_size); + return -EFAULT; + } + + ohci->fbuf_context[i] = + alloc_dma_fbuf_ctx(ohci, i+1, v.nb_buffers, + v.buf_size, v.channel); + + if (ohci->fbuf_context[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "Couldn't allocate fbuf context"); + return -EFAULT; + } + initialize_dma_fbuf_ctx(ohci->fbuf_context[i], v.sync_tag); + + ohci->current_fbuf_ctx = ohci->fbuf_context[i]; + + v.buf_size = ohci->fbuf_context[i]->buf_size; + + PRINT(KERN_INFO, ohci->id, + "iso context %d listen on channel %d", i+1, + v.channel); + + if(copy_to_user((void *)arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDEO1394_UNLISTEN_CHANNEL: + { + int channel; + int i; + + if(copy_from_user(&channel, (void *)arg, sizeof(int))) + return -EFAULT; + + if (!test_and_clear_bit(channel, &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is not being used", channel); + return -EFAULT; + } + + i = fbuf_ctx_listening(ohci, channel); + if (i<0) return -EFAULT; + + free_dma_fbuf_ctx(&ohci->fbuf_context[i]); + + PRINT(KERN_INFO, ohci->id, + "iso context %d stop listening on channel %d", + i+1, channel); + + return 0; + } + case VIDEO1394_QUEUE_BUFFER: + { + struct video1394_wait v; + struct dma_fbuf_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = fbuf_ctx_listening(ohci, v.channel); + if (i<0) return -EFAULT; + d = ohci->fbuf_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { + PRINT(KERN_ERR, ohci->id, + "buffer %d is already used",v.buffer); + return -EFAULT; + } + + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; + + d->prg[d->last_buffer][d->nb_cmd-1].branchAddress = + (virt_to_bus(&(d->prg[v.buffer][0].control)) + & 0xfffffff0) | 0x1; + + d->last_buffer = v.buffer; + + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) + { + DBGMSG(ohci->id, "Starting iso DMA ctx=%d",d->ctx); + + /* Tell the controller where the first program is */ + reg_write(ohci, d->cmdPtr, + virt_to_bus(&(d->prg[v.buffer][0])) | 0x1 ); + + /* Run IR context */ + reg_write(ohci, d->ctrlSet, 0x8000); + } + else { + /* Wake up dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { + PRINT(KERN_INFO, ohci->id, + "Waking up iso dma ctx=%d", d->ctx); + reg_write(ohci, d->ctrlSet, 0x1000); + } + } + return 0; + + } + case VIDEO1394_WAIT_BUFFER: + { + struct video1394_wait v; + struct dma_fbuf_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = fbuf_ctx_listening(ohci, v.channel); + if (i<0) return -EFAULT; + d = ohci->fbuf_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + switch(d->buffer_status[v.buffer]) { + case VIDEO1394_BUFFER_READY: + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + case VIDEO1394_BUFFER_QUEUED: + while(d->buffer_status[v.buffer]!= + VIDEO1394_BUFFER_READY) { + interruptible_sleep_on(&d->waitq); + if(signal_pending(current)) return -EINTR; + } + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + default: + PRINT(KERN_ERR, ohci->id, + "buffer %d is not queued",v.buffer); + return -EFAULT; + } + } + default: + return -EINVAL; + } +} + +/* + * This maps the vmalloced and reserved fbuffer to user space. + * + * FIXME: + * - PAGE_READONLY should suffice!? + * - remap_page_range is kind of inefficient for page by page remapping. + * But e.g. pte_alloc() does not work in modules ... :-( + */ + +static int do_fbuf_mmap(struct ti_ohci *ohci, struct dma_fbuf_ctx *d, + const char *adr, unsigned long size) +{ + unsigned long start=(unsigned long) adr; + unsigned long page,pos; + + if (size>d->num_desc * d->buf_size) { + PRINT(KERN_ERR, ohci->id, + "fbuf context %d buf size is different from mmap size", + d->ctx); + return -EINVAL; + } + if (!d->buf) { + PRINT(KERN_ERR, ohci->id, + "fbuf context %d is not allocated", d->ctx); + return -EINVAL; + } + + pos=(unsigned long) d->buf; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start+=PAGE_SIZE; + pos+=PAGE_SIZE; + size-=PAGE_SIZE; + } + return 0; +} + +int ohci_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ti_ohci *ohci=&cards[MINOR(file->f_dentry->d_inode->i_rdev)]; + PRINT(KERN_INFO, ohci->id, "mmap"); + if (ohci->current_fbuf_ctx == NULL) { + PRINT(KERN_ERR, ohci->id, "current fbuf context not set"); + return -EINVAL; + } + + return do_fbuf_mmap(ohci, ohci->current_fbuf_ctx, + (char *)vma->vm_start, + (unsigned long)(vma->vm_end-vma->vm_start)); + return 0; +} + +static int ohci_open(struct inode *inode, struct file *file) +{ + struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; + PRINT(KERN_INFO, ohci->id, "open"); + return 0; +} + +static int ohci_release(struct inode *inode, struct file *file) +{ + struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; + int i; + + PRINT(KERN_INFO, ohci->id, "release"); + for (i=0;i<ohci->nb_iso_ctx-1;i++) + if (ohci->fbuf_context[i]) { + if (!test_and_clear_bit(ohci->fbuf_context[i]->channel, + &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is not being used", + ohci->fbuf_context[i]->channel); + } + PRINT(KERN_INFO, ohci->id, + "iso context %d stop listening on channel %d", + i+1, ohci->fbuf_context[i]->channel); + free_dma_fbuf_ctx(&ohci->fbuf_context[i]); + } + return 0; +} + +static struct file_operations ohci_fops= +{ + owner: THIS_MODULE, + ioctl: ohci_ioctl, + mmap: ohci_mmap, + open: ohci_open, + release: ohci_release +}; + +int wakeup_dma_fbuf_ctx(struct ti_ohci *ohci, struct dma_fbuf_ctx *d) +{ + int i; + + if (d==NULL) { + PRINT(KERN_ERR, ohci->id, "Iso receive event received but " + "context not allocated"); + return -EFAULT; + } + + for (i=0;i<d->num_desc;i++) { + if (d->prg[i][d->nb_cmd-1].status) { + d->prg[i][d->nb_cmd-1].status=0; + d->buffer_status[i] = VIDEO1394_BUFFER_READY; + } + } + if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); + return 0; +} + +#endif + + /*********************************** * IEEE-1394 functionality section * @@ -220,6 +844,22 @@ inline static int handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host, "Error in reception of self-id packets" "Self-id count: %08x q[0]: %08x", self_id_count, q[0]); + + /* + * Tip by James Goodwin <jamesg@Filanet.com>: + * We had an error, generate another bus reset in response. + * TODO. Actually read the current value in the phy before + * generating a bus reset (read modify write). This way + * we don't stomp any current gap count settings, etc. + */ + if (ohci->self_id_errors<OHCI1394_MAX_SELF_ID_ERRORS) { + reg_write(ohci, OHCI1394_PhyControl, 0x000041ff); + ohci->self_id_errors++; + } + else { + PRINT(KERN_ERR, ohci->id, + "Timeout on self-id error reception"); + } return -1; } @@ -411,15 +1051,34 @@ static int ohci_initialize(struct hpsb_host *host) spin_lock_init(&ohci->phy_reg_lock); + /* + * Tip by James Goodwin <jamesg@Filanet.com>: + * We need to add delays after the soft reset, setting LPS, and + * enabling our link. This might fixes the self-id reception + * problem at initialization. + */ + /* Soft reset */ if ((retval=ohci_soft_reset(ohci))<0) return retval; - - /* Set the bus number */ - reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0); + /* + *Delay aftger soft reset to make sure everything has settled + * down (sanity) + */ + mdelay(100); + /* Set Link Power Status (LPS) */ reg_write(ohci, OHCI1394_HCControlSet, 0x00080000); + /* + * Delay after setting LPS in order to make sure link/phy + * communication is established + */ + mdelay(100); + + /* Set the bus number */ + reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0); + /* Enable posted writes */ reg_write(ohci, OHCI1394_HCControlSet, 0x00040000); @@ -464,9 +1123,6 @@ static int ohci_initialize(struct hpsb_host *host) /* Don't accept phy packets into AR request context */ reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); - /* Enable link */ - reg_write(ohci, OHCI1394_HCControlSet, 0x00020000); - /* Initialize IR dma */ ohci->nb_iso_ctx = get_nb_iso_ctx(ohci); PRINT(KERN_INFO, ohci->id, "%d iso contexts available", @@ -477,7 +1133,18 @@ static int ohci_initialize(struct hpsb_host *host) reg_write(ohci, OHCI1394_IrRcvContextMatch+32*i, 0); reg_write(ohci, OHCI1394_IrRcvCommandPtr+32*i, 0); } - +#ifdef _VIDEO_1394_H + ohci->fbuf_context = (struct dma_fbuf_ctx **) + kmalloc((ohci->nb_iso_ctx-1)*sizeof(struct dma_fbuf_ctx *), + GFP_KERNEL); + if (ohci->fbuf_context) + memset(ohci->fbuf_context, 0, + (ohci->nb_iso_ctx-1)*sizeof(struct dma_fbuf_ctx *)); + else { + PRINT(KERN_ERR, ohci->id, "Cannot allocate fbuf_context"); + return -1; + } +#endif /* Set bufferFill, isochHeader, multichannel for IR context */ reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0xd0000000); @@ -495,16 +1162,6 @@ static int ohci_initialize(struct hpsb_host *host) (thanks to Michael Greger for seeing that I forgot this) */ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0x00000001); - initialize_dma_rcv_ctx(ohci->ir_context); - - /* Initialize AR dma */ - initialize_dma_rcv_ctx(ohci->ar_req_context); - initialize_dma_rcv_ctx(ohci->ar_resp_context); - - /* Initialize AT dma */ - initialize_dma_trm_ctx(ohci->at_req_context); - initialize_dma_trm_ctx(ohci->at_resp_context); - /* * Accept AT requests from all nodes. This probably * will have to be controlled from the subsystem @@ -539,6 +1196,20 @@ static int ohci_initialize(struct hpsb_host *host) OHCI1394_isochRx ); + /* Enable link */ + reg_write(ohci, OHCI1394_HCControlSet, 0x00020000); + + /* Initialize AR dma */ + initialize_dma_rcv_ctx(ohci->ar_req_context); + initialize_dma_rcv_ctx(ohci->ar_resp_context); + + /* Initialize AT dma */ + initialize_dma_trm_ctx(ohci->at_req_context); + initialize_dma_trm_ctx(ohci->at_resp_context); + + /* Initialize IR dma */ + initialize_dma_rcv_ctx(ohci->ir_context); + return 1; } @@ -619,7 +1290,7 @@ static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) struct ti_ohci *ohci = host->hostdata; struct dma_trm_ctx *d; unsigned char tcode; - int i=50; + int timeout=50; if (packet->data_size >= ohci->max_packet_size) { PRINT(KERN_ERR, ohci->id, @@ -642,8 +1313,9 @@ static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) } while (d->free_prgs<1) { spin_unlock(&d->lock); - schedule(); - if (i-- <0) { + interruptible_sleep_on(&d->waitq); + if(signal_pending(current)) return -EINTR; + if (timeout--<0) { stop_context(ohci, d->ctrlClear, "AT DMA runaway loop... bailing out"); return 0; @@ -687,7 +1359,7 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) switch (cmd) { case RESET_BUS: - host->attempt_root=1; + host->attempt_root=1; PRINT(KERN_INFO, ohci->id, "resetting bus on request%s", (host->attempt_root ? " and attempting to become root" : "")); @@ -743,6 +1415,11 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) spin_lock_irqsave(&ohci->IR_channel_lock, flags); +#if 0 + PRINT(KERN_INFO, ohci->id, "!!! try listen on channel %d !!!", + arg); +#endif + if (!test_and_set_bit(arg, &ohci->IR_channel_usage)) { PRINT(KERN_INFO, ohci->id, "listening enabled on channel %d", arg); @@ -845,137 +1522,179 @@ static void ohci_irq_handler(int irq, void *dev_id, struct ti_ohci *ohci = (struct ti_ohci *)dev_id; struct hpsb_host *host = ohci->host; int phyid = -1, isroot = 0; + int timeout = 255; - /* read the interrupt event register */ - event=reg_read(ohci, OHCI1394_IntEventSet); + do { + /* read the interrupt event register */ + event=reg_read(ohci, OHCI1394_IntEventClear); -#if 0 - /* - * clear the interrupt event register, except for the - * bus reset event interrupt (if any). This is an - * attempt to comply with ohci spec 7.2.3.2 - */ - reg_write(ohci, OHCI1394_IntEventClear, event & (~OHCI1394_busReset)); -#else - /* The above attempt doesn't work */ - reg_write(ohci, OHCI1394_IntEventClear, event); -#endif - if (event & OHCI1394_busReset) { - if (!host->in_bus_reset) { - PRINT(KERN_INFO, ohci->id, "Bus reset"); + DBGMSG(ohci->id, "IntEvent: %08x",event); - /* Wait for the AT fifo to be flushed */ - dma_trm_reset(ohci->at_req_context); - dma_trm_reset(ohci->at_resp_context); + if (!event) return; -#if 0 - /* clear the bus reset event */ - reg_write(ohci, OHCI1394_IntEventClear, - OHCI1394_busReset); -#endif - /* Subsystem call */ - hpsb_bus_reset(ohci->host); + /* clear the interrupt event register */ + reg_write(ohci, OHCI1394_IntEventClear, event); + + if (event & OHCI1394_busReset) { + if (!host->in_bus_reset) { + PRINT(KERN_INFO, ohci->id, "Bus reset"); + + /* Wait for the AT fifo to be flushed */ + dma_trm_reset(ohci->at_req_context); + dma_trm_reset(ohci->at_resp_context); - ohci->NumBusResets++; + /* Subsystem call */ + hpsb_bus_reset(ohci->host); + + ohci->NumBusResets++; + } } - } - /* - * Problem: How can I ensure that the AT bottom half will be - * executed before the AR bottom half (both events may have - * occured within a single irq event) - * Quick hack: just launch it within the IRQ handler - */ - if (event & OHCI1394_reqTxComplete) { - struct dma_trm_ctx *d = ohci->at_req_context; - DBGMSG(ohci->id, "Got reqTxComplete interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "reqTxComplete"); - else - dma_trm_bh((void *)d); - } - if (event & OHCI1394_respTxComplete) { - struct dma_trm_ctx *d = ohci->at_resp_context; - DBGMSG(ohci->id, "Got respTxComplete interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "respTxComplete"); - else - dma_trm_bh((void *)d); - } - if (event & OHCI1394_RQPkt) { - struct dma_rcv_ctx *d = ohci->ar_req_context; - DBGMSG(ohci->id, "Got RQPkt interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "RQPkt"); - else { - queue_task(&d->task, &tq_immediate); - mark_bh(IMMEDIATE_BH); + /* + * Problem: How can I ensure that the AT bottom half will be + * executed before the AR bottom half (both events may have + * occured within a single irq event) + * Quick hack: just launch it within the IRQ handler + */ + if (event & OHCI1394_reqTxComplete) { + struct dma_trm_ctx *d = ohci->at_req_context; + DBGMSG(ohci->id, "Got reqTxComplete interrupt " + "status=0x%08X", reg_read(ohci, d->ctrlSet)); + if (reg_read(ohci, d->ctrlSet) & 0x800) + stop_context(ohci, d->ctrlClear, + "reqTxComplete"); + else + dma_trm_bh((void *)d); } - } - if (event & OHCI1394_RSPkt) { - struct dma_rcv_ctx *d = ohci->ar_resp_context; - DBGMSG(ohci->id, "Got RSPkt interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "RSPkt"); - else { - queue_task(&d->task, &tq_immediate); - mark_bh(IMMEDIATE_BH); + if (event & OHCI1394_respTxComplete) { + struct dma_trm_ctx *d = ohci->at_resp_context; + DBGMSG(ohci->id, "Got respTxComplete interrupt " + "status=0x%08X", reg_read(ohci, d->ctrlSet)); + if (reg_read(ohci, d->ctrlSet) & 0x800) + stop_context(ohci, d->ctrlClear, + "respTxComplete"); + else + dma_trm_bh((void *)d); } - } - if (event & OHCI1394_isochRx) { - quadlet_t isoRecvIntEvent; - struct dma_rcv_ctx *d = ohci->ir_context; - isoRecvIntEvent = reg_read(ohci, OHCI1394_IsoRecvIntEventSet); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, - isoRecvIntEvent); - DBGMSG(ohci->id, "Got reqTxComplete interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "isochRx"); - else { - queue_task(&d->task, &tq_immediate); - mark_bh(IMMEDIATE_BH); + if (event & OHCI1394_RQPkt) { + struct dma_rcv_ctx *d = ohci->ar_req_context; + DBGMSG(ohci->id, "Got RQPkt interrupt status=0x%08X", + reg_read(ohci, d->ctrlSet)); + if (reg_read(ohci, d->ctrlSet) & 0x800) + stop_context(ohci, d->ctrlClear, "RQPkt"); + else { +#if 1 + queue_task(&d->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + dma_rcv_bh((void *)d); +#endif + } } - } - if (event & OHCI1394_selfIDComplete) { - if (host->in_bus_reset) { - node_id = reg_read(ohci, OHCI1394_NodeID); - if (node_id & 0x80000000) { /* NodeID valid */ - phyid = node_id & 0x0000003f; - isroot = (node_id & 0x40000000) != 0; - - PRINT(KERN_INFO, ohci->id, - "SelfID process finished (phyid %d, %s)", - phyid, (isroot ? "root" : "not root")); - - handle_selfid(ohci, host, phyid, isroot); + if (event & OHCI1394_RSPkt) { + struct dma_rcv_ctx *d = ohci->ar_resp_context; + DBGMSG(ohci->id, "Got RSPkt interrupt status=0x%08X", + reg_read(ohci, d->ctrlSet)); + if (reg_read(ohci, d->ctrlSet) & 0x800) + stop_context(ohci, d->ctrlClear, "RSPkt"); + else { +#if 1 + queue_task(&d->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + dma_rcv_bh((void *)d); +#endif } - else - PRINT(KERN_ERR, ohci->id, - "SelfID process finished but NodeID" - " not valid: %08X",node_id); - - /* Accept Physical requests from all nodes. */ - reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0xffffffff); - reg_write(ohci,OHCI1394_AsReqFilterLoSet, 0xffffffff); - } - else PRINT(KERN_INFO, ohci->id, - "phy reg received without reset\n"); - } - if (event & OHCI1394_phyRegRcvd) { -#if 0 - if (host->in_bus_reset) { - PRINT(KERN_INFO, ohci->id, "PhyControl: %08X", - reg_read(ohci, OHCI1394_PhyControl)); - } else - PRINT(KERN_ERR, ohci->id, - "phy reg received without reset"); + } + if (event & OHCI1394_isochRx) { + quadlet_t isoRecvIntEvent; + struct dma_rcv_ctx *d = ohci->ir_context; +#ifdef _VIDEO_1394_H + int i; #endif - } + isoRecvIntEvent = + reg_read(ohci, OHCI1394_IsoRecvIntEventSet); + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, + isoRecvIntEvent); + DBGMSG(ohci->id, "Got isochRx interrupt " + "status=0x%08X isoRecvIntEvent=%08x", + reg_read(ohci, d->ctrlSet), isoRecvIntEvent); + if (isoRecvIntEvent & 0x1) { + if (reg_read(ohci, d->ctrlSet) & 0x800) + stop_context(ohci, d->ctrlClear, + "isochRx"); + else { +#if 1 + queue_task(&d->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + dma_rcv_bh((void *)d); +#endif + } + } +#ifdef _VIDEO_1394_H + for (i=0;i<ohci->nb_iso_ctx-1;i++) + if (isoRecvIntEvent & (1<<(i+1))) + wakeup_dma_fbuf_ctx( + ohci,ohci->fbuf_context[i]); +#endif + } + if (event & OHCI1394_selfIDComplete) { + if (host->in_bus_reset) { + node_id = reg_read(ohci, OHCI1394_NodeID); + if (node_id & 0x80000000) { /* NodeID valid */ + phyid = node_id & 0x0000003f; + isroot = (node_id & 0x40000000) != 0; + + PRINT(KERN_INFO, ohci->id, + "SelfID process finished " + "(phyid %d, %s)", phyid, + (isroot ? "root" : "not root")); + + handle_selfid(ohci, host, + phyid, isroot); + } + else + PRINT(KERN_ERR, ohci->id, + "SelfID process finished but " + "NodeID not valid: %08X", + node_id); + + /* Accept Physical requests from all nodes. */ + reg_write(ohci,OHCI1394_AsReqFilterHiSet, + 0xffffffff); + reg_write(ohci,OHCI1394_AsReqFilterLoSet, + 0xffffffff); + /* + * Tip by James Goodwin <jamesg@Filanet.com> + * Turn on phys dma reception. We should + * probably manage the filtering somehow, + * instead of blindly turning it on. + */ + reg_write(ohci,OHCI1394_PhyReqFilterHiSet, + 0xffffffff); + reg_write(ohci,OHCI1394_PhyReqFilterLoSet, + 0xffffffff); + reg_write(ohci,OHCI1394_PhyUpperBound, + 0xffff0000); + } + else PRINT(KERN_ERR, ohci->id, + "self-id received outside of bus reset" + "sequence"); + } + if (event & OHCI1394_phyRegRcvd) { +#if 1 + if (host->in_bus_reset) { + PRINT(KERN_INFO, ohci->id, "PhyControl: %08X", + reg_read(ohci, OHCI1394_PhyControl)); + } + else PRINT(KERN_ERR, ohci->id, + "phy reg received outside of bus reset" + "sequence"); +#endif + } + } while (--timeout); + PRINT(KERN_ERR, ohci->id, "irq_handler timeout event=0x%08x", event); } /* Put the buffer back into the dma context */ @@ -1119,6 +1838,17 @@ static void dma_rcv_bh(void *data) buf_ptr += offset/4; } + /* + * Tip by James Goodwin <jamesg@Filanet.com> + * We need to handle write requests that are received + * to our middle address space (posted writes). + * In this case, the hardware generates an + * ack_complete... but, if we pass the packet up to + * the subsystem, it will try and send a response + * (which it shouldn't), because it assumes we + * returned ack_pending. + */ + /* * We get one phy packet for each bus reset. * we know that from now on the bus topology may @@ -1132,6 +1862,20 @@ static void dma_rcv_bh(void *data) (d->spb[length/4-1]>>16)&0x1f, (d->spb[length/4-1]>>21)&0x3, tcode, length, d->spb[3], d->ctx); + + /* + * Tip by James Goodwin <jamesg@Filanet.com> + * Handle case of posted writes. If we receive + * an ack_complete, we should not send a + * response. Fake out upper layers by turning + * the packet into a broadcast packet... we + * should really modify the core stack to + * accept an ack received argument and figure + * out whether to reply. + */ + if (((d->spb[length/4-1]>>16)&0x1f) == 0x11) { + d->spb[0] |= (ALL_NODES<<16); + } hpsb_packet_received(ohci->host, d->spb, length); } @@ -1154,6 +1898,20 @@ static void dma_rcv_bh(void *data) (buf_ptr[length/4-1]>>16)&0x1f, (buf_ptr[length/4-1]>>21)&0x3, tcode, length, buf_ptr[3], d->ctx); + + /* + * Tip by James Goodwin <jamesg@Filanet.com> + * Handle case of posted writes. If we receive + * an ack_complete, we should not send a + * response. Fake out upper layers by turning + * the packet into a broadcast packet... we + * should really modify the core stack to + * accept an ack received argument and figure + * out whether to reply. + */ + if (((d->spb[length/4-1]>>16)&0x1f) == 0x11) { + buf_ptr[0] |= (ALL_NODES<<16); + } hpsb_packet_received(ohci->host, buf_ptr, length); } @@ -1207,32 +1965,42 @@ static void dma_trm_bh(void *data) d->sent_ind = (d->sent_ind+1)%d->num_desc; d->free_prgs++; spin_unlock(&d->lock); - + + if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); + DBGMSG(ohci->id, "Packet sent to node %d ack=0x%X spd=%d ctx=%d", (packet->header[0]>>16)&0x3f, ack&0x1f, (ack>>5)&0x3, d->ctx); hpsb_packet_sent(ohci->host, packet, ack&0xf); } -static int free_dma_rcv_ctx(struct dma_rcv_ctx *d) +static int free_dma_rcv_ctx(struct dma_rcv_ctx **d) { int i; + struct ti_ohci *ohci; + + if (*d==NULL) return -1; + + ohci = (struct ti_ohci *)(*d)->ohci; - if (d==NULL) return -1; + DBGMSG(ohci->id, "Freeing dma_rcv_ctx %d",(*d)->ctx); + + stop_context(ohci, (*d)->ctrlClear, NULL); - if (d->buf) { - for (i=0; i<d->num_desc; i++) - if (d->buf[i]) kfree(d->buf[i]); - kfree(d->buf); + if ((*d)->buf) { + for (i=0; i<(*d)->num_desc; i++) + if ((*d)->buf[i]) kfree((*d)->buf[i]); + kfree((*d)->buf); } - if (d->prg) { - for (i=0; i<d->num_desc; i++) - if (d->prg[i]) kfree(d->prg[i]); - kfree(d->prg); + if ((*d)->prg) { + for (i=0; i<(*d)->num_desc; i++) + if ((*d)->prg[i]) kfree((*d)->prg[i]); + kfree((*d)->prg); } - if (d->spb) kfree(d->spb); - - kfree(d); + if ((*d)->spb) kfree((*d)->spb); + kfree(*d); + *d = NULL; + return 0; } @@ -1270,7 +2038,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, if (d->buf == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); - free_dma_rcv_ctx(d); + free_dma_rcv_ctx(&d); return NULL; } memset(d->buf, 0, d->num_desc * sizeof(quadlet_t*)); @@ -1279,7 +2047,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, if (d->prg == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg"); - free_dma_rcv_ctx(d); + free_dma_rcv_ctx(&d); return NULL; } memset(d->prg, 0, d->num_desc * sizeof(struct dma_cmd*)); @@ -1288,7 +2056,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, if (d->spb == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate split buffer"); - free_dma_rcv_ctx(d); + free_dma_rcv_ctx(&d); return NULL; } @@ -1300,7 +2068,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, } else { PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); - free_dma_rcv_ctx(d); + free_dma_rcv_ctx(&d); return NULL; } @@ -1311,7 +2079,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, } else { PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg"); - free_dma_rcv_ctx(d); + free_dma_rcv_ctx(&d); return NULL; } } @@ -1319,17 +2087,29 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, spin_lock_init(&d->lock); /* initialize bottom handler */ + d->task.sync = 0; + d->task.next = NULL; d->task.routine = dma_rcv_bh; d->task.data = (void*)d; return d; } -static int free_dma_trm_ctx(struct dma_trm_ctx *d) +static int free_dma_trm_ctx(struct dma_trm_ctx **d) { - if (d==NULL) return -1; - if (d->prg) kfree(d->prg); - kfree(d); + struct ti_ohci *ohci; + + if (*d==NULL) return -1; + + ohci = (struct ti_ohci *)(*d)->ohci; + + DBGMSG(ohci->id, "Freeing dma_trm_ctx %d",(*d)->ctx); + + stop_context(ohci, (*d)->ctrlClear, NULL); + + if ((*d)->prg) kfree((*d)->prg); + kfree(*d); + *d = NULL; return 0; } @@ -1359,7 +2139,7 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, if (d->prg == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate at dma prg"); - free_dma_trm_ctx(d); + free_dma_trm_ctx(&d); return NULL; } memset(d->prg, 0, d->num_desc * sizeof(struct at_dma_prg)); @@ -1370,6 +2150,8 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, d->task.routine = dma_trm_bh; d->task.data = (void*)d; + init_waitqueue_head(&d->waitq); + return d; } @@ -1385,10 +2167,12 @@ static int add_card(struct pci_dev *dev) return 1; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) - /* XXX check return value */ - pci_enable_device(dev); -#endif + if (pci_enable_device(dev)) { + PRINT_G(KERN_NOTICE, "failed to enable OHCI hardware %d", + num_of_cards); + return 1; + } + pci_set_master(dev); ohci = &cards[num_of_cards++]; @@ -1397,13 +2181,6 @@ static int add_card(struct pci_dev *dev) ohci->state = 0; - if (!request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ, - OHCI1394_DRIVER_NAME, ohci)) { - PRINT(KERN_INFO, ohci->id, "allocated interrupt %d", dev->irq); - } else { - FAIL("failed to allocate shared interrupt %d", dev->irq); - } - /* csr_config rom allocation */ ohci->csr_config_rom = kmalloc(1024, GFP_KERNEL); if (ohci->csr_config_rom == NULL) { @@ -1437,7 +2214,9 @@ static int add_card(struct pci_dev *dev) OHCI1394_AsRspRcvContextControlClear, OHCI1394_AsRspRcvCommandPtr); - if (ohci->ar_resp_context == NULL) return 1; + if (ohci->ar_resp_context == NULL) { + FAIL("failed to allocate AR Resp context"); + } ohci->at_req_context = alloc_dma_trm_ctx(ohci, 0, AT_REQ_NUM_DESC, @@ -1445,7 +2224,9 @@ static int add_card(struct pci_dev *dev) OHCI1394_AsReqTrContextControlClear, OHCI1394_AsReqTrCommandPtr); - if (ohci->at_req_context == NULL) return 1; + if (ohci->at_req_context == NULL) { + FAIL("failed to allocate AT Req context"); + } ohci->at_resp_context = alloc_dma_trm_ctx(ohci, 1, AT_RESP_NUM_DESC, @@ -1453,7 +2234,9 @@ static int add_card(struct pci_dev *dev) OHCI1394_AsRspTrContextControlClear, OHCI1394_AsRspTrCommandPtr); - if (ohci->at_resp_context == NULL) return 1; + if (ohci->at_resp_context == NULL) { + FAIL("failed to allocate AT Resp context"); + } ohci->ir_context = alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC, @@ -1462,7 +2245,9 @@ static int add_card(struct pci_dev *dev) OHCI1394_IrRcvContextControlClear, OHCI1394_IrRcvCommandPtr); - if (ohci->ir_context == NULL) return 1; + if (ohci->ir_context == NULL) { + FAIL("failed to allocate IR context"); + } ohci->IR_channel_usage= 0x0000000000000000; spin_lock_init(&ohci->IR_channel_lock); @@ -1482,6 +2267,13 @@ static int add_card(struct pci_dev *dev) PRINT(KERN_INFO, ohci->id, "remapped memory spaces reg 0x%p", ohci->registers); + if (!request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ, + OHCI1394_DRIVER_NAME, ohci)) { + PRINT(KERN_INFO, ohci->id, "allocated interrupt %d", dev->irq); + } else { + FAIL("failed to allocate shared interrupt %d", dev->irq); + } + return 0; #undef FAIL } @@ -1507,6 +2299,10 @@ int ohci_get_info(char *buf, char **start, off_t fpos, int i; struct dma_rcv_ctx *d=NULL; struct dma_trm_ctx *dt=NULL; +#ifdef _VIDEO_1394_H + int j; + struct dma_fbuf_ctx *f=ohci->fbuf_context[0]; +#endif p += sprintf(p,"IEEE-1394 OHCI Driver status report:\n"); p += sprintf(p," bus number: 0x%x Node ID: 0x%x\n", @@ -1535,6 +2331,27 @@ int ohci_get_info(char *buf, char **start, off_t fpos, host->is_busmgr ? "bus_mgr" : ""); p += sprintf(p,"\n---Iso Receive DMA---\n"); + +#ifdef _VIDEO_1394_H + +#if 0 + if (f!=NULL) { + for (i=0; i<f->num_desc; i++) { + for (j=0;j<f->nb_cmd;j++) { + p += sprintf(p, + "prg[%d][%d]: %p %08x %08x %08x %08x\n", + i,j,virt_to_bus(&(f->prg[i][j])), + f->prg[i][j].control, + f->prg[i][j].address, + f->prg[i][j].branchAddress, + f->prg[i][j].status); + } + } + } +#endif + +#else + d = ohci->ir_context; #if 0 for (i=0; i<d->num_desc; i++) { @@ -1621,7 +2438,8 @@ int ohci_get_info(char *buf, char **start, off_t fpos, dt->branchAddrPtr); p += sprintf(p, "AT resp queue: first: %p last: %p\n", dt->first, dt->last); - +#endif + /* ----- Register Dump ----- */ p += sprintf(p,"\n### HC Register dump ###\n"); SR("Version : %08x GUID_ROM : %08x ATRetries : %08x\n", @@ -1736,19 +2554,34 @@ struct proc_dir_entry ohci_proc_entry = static void remove_card(struct ti_ohci *ohci) { - if (ohci->registers) - iounmap(ohci->registers); +#ifdef _VIDEO_1394_H + int i; +#endif /* Free AR dma */ - free_dma_rcv_ctx(ohci->ar_req_context); - free_dma_rcv_ctx(ohci->ar_resp_context); + free_dma_rcv_ctx(&ohci->ar_req_context); + free_dma_rcv_ctx(&ohci->ar_resp_context); /* Free AT dma */ - free_dma_trm_ctx(ohci->at_req_context); - free_dma_trm_ctx(ohci->at_resp_context); + free_dma_trm_ctx(&ohci->at_req_context); + free_dma_trm_ctx(&ohci->at_resp_context); /* Free IR dma */ - free_dma_rcv_ctx(ohci->ir_context); + free_dma_rcv_ctx(&ohci->ir_context); + +#ifdef _VIDEO_1394_H + /* Free the frame buffer context */ + if (ohci->fbuf_context) + for (i=0;i<ohci->nb_iso_ctx-1;i++) { + free_dma_fbuf_ctx(&ohci->fbuf_context[i]); + } +#endif + + /* + * Reset the board properly before leaving + * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> + */ + ohci_soft_reset(ohci); /* Free self-id buffer */ if (ohci->self_id_buffer) @@ -1761,6 +2594,9 @@ static void remove_card(struct ti_ohci *ohci) /* Free the IRQ */ free_irq(ohci->dev->irq, ohci); + if (ohci->registers) + iounmap(ohci->registers); + ohci->state = 0; } @@ -1858,16 +2694,30 @@ void cleanup_module(void) proc_unregister(&proc_root, ohci_proc_entry.low_ino); #endif #endif + +#ifdef _VIDEO_1394_H + unregister_chrdev(OHCI1394_MAJOR, "ohci1394"); +#endif + PRINT_G(KERN_INFO, "removed " OHCI1394_DRIVER_NAME " module\n"); } int init_module(void) { - + memset(cards, 0, MAX_OHCI1394_CARDS * sizeof (struct ti_ohci)); + if (hpsb_register_lowlevel(get_ohci_template())) { PRINT_G(KERN_ERR, "registering failed\n"); return -ENXIO; } else { +#ifdef _VIDEO_1394_H + if (register_chrdev(OHCI1394_MAJOR, "ohci1394", &ohci_fops)) + { + printk("ohci1394: unable to get major %d\n", + OHCI1394_MAJOR); + return -EIO; + } +#endif return 0; } } diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index f0ec83d48..d778cbe7d 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -1,8 +1,29 @@ +/* + * ohci1394.h - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> + * Gord Peters <GordPeters@smarttech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ #ifndef _OHCI1394_H #define _OHCI1394_H #include "ieee1394_types.h" +/* include this for the video frame grabber */ +/* #include "video1394.h" */ #define OHCI1394_DRIVER_NAME "ohci1394" @@ -46,11 +67,16 @@ #define PCI_DEVICE_ID_NEC_UPD72871 0x00ce #endif +#ifndef PCI_DEVICE_ID_APPLE_UNI_N_FW +#define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018 +#endif + #define MAX_OHCI1394_CARDS 4 #define OHCI1394_MAX_AT_REQ_RETRIES 0x2 #define OHCI1394_MAX_AT_RESP_RETRIES 0x2 #define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 +#define OHCI1394_MAX_SELF_ID_ERRORS 16 #define AR_REQ_NUM_DESC 4 /* number of AR req descriptors */ #define AR_REQ_BUF_SIZE 4096 /* size of AR req buffers */ @@ -116,8 +142,34 @@ struct dma_trm_ctx { int ctrlClear; int ctrlSet; int cmdPtr; + wait_queue_head_t waitq; }; +#ifdef _VIDEO_1394_H + +#define OHCI1394_MAJOR 172 +#define ISO_CHANNELS 64 + +struct dma_fbuf_ctx { + void *ohci; + int ctx; + int channel; + int last_buffer; + unsigned int num_desc; + unsigned int buf_size; + unsigned int frame_size; + unsigned int nb_cmd; + unsigned char *buf; + struct dma_cmd **prg; + unsigned int *buffer_status; + int ctrlClear; + int ctrlSet; + int cmdPtr; + int ctxMatch; + wait_queue_head_t waitq; +}; +#endif + struct ti_ohci { int id; /* sequential card number */ @@ -147,6 +199,12 @@ struct ti_ohci { spinlock_t IR_channel_lock; int nb_iso_ctx; +#ifdef _VIDEO_1394_H + /* frame buffer context */ + struct dma_fbuf_ctx **fbuf_context; + struct dma_fbuf_ctx *current_fbuf_ctx; +#endif + /* IEEE-1394 part follows */ struct hpsb_host *host; @@ -154,6 +212,7 @@ struct ti_ohci { spinlock_t phy_reg_lock; + int self_id_errors; int NumBusResets; }; @@ -328,8 +387,8 @@ quadlet_t ohci_csr_rom[] = { #define OHCI1394_RSPkt 0x00000020 #define OHCI1394_isochTx 0x00000040 #define OHCI1394_isochRx 0x00000080 -#define OHCI1394_postedWriteErr 0x00001000 -#define OHCI1394_lockRespErr 0x00002000 +#define OHCI1394_postedWriteErr 0x00000100 +#define OHCI1394_lockRespErr 0x00000200 #define OHCI1394_selfIDComplete 0x00010000 #define OHCI1394_busReset 0x00020000 #define OHCI1394_phy 0x00080000 diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c index 773d68983..368c5137b 100644 --- a/drivers/ieee1394/pcilynx.c +++ b/drivers/ieee1394/pcilynx.c @@ -99,6 +99,8 @@ static pcl_t alloc_pcl(struct ti_lynx *lynx) return -1; } + +#if 0 static void free_pcl(struct ti_lynx *lynx, pcl_t pclid) { int off, bit; @@ -145,6 +147,7 @@ static void print_pcl(const struct ti_lynx *lynx, pcl_t pclid) get_pcl(lynx, pclid, &pcl); pretty_print_pcl(&pcl); } +#endif static int add_card(struct pci_dev *dev); @@ -486,7 +489,8 @@ static int lynx_initialize(struct hpsb_host *host) put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl); /* 85 bytes for each FIFO - FIXME - optimize or make configurable */ - reg_write(lynx, FIFO_SIZES, 0x00555555); + /* reg_write(lynx, FIFO_SIZES, 0x00555555); */ + reg_write(lynx, FIFO_SIZES, 0x002020c0); /* 20 byte threshold before triggering PCI transfer */ reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24); /* 69 byte threshold on both send FIFOs before transmitting */ @@ -597,15 +601,16 @@ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) } else { arg = 1 << 6; } - + + retval = get_phy_reg(lynx, 1); + arg |= (retval == -1 ? 63 : retval); + retval = 0; + PRINT(KERN_INFO, lynx->id, "resetting bus on request%s", (host->attempt_root ? " and attempting to become root" : "")); - spin_lock_irqsave(&lynx->phy_reg_lock, flags); - reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(1) - | LINK_PHY_WDATA(arg)); - spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); + set_phy_reg(lynx, 1, arg); break; case GET_CYCLE_COUNTER: @@ -705,6 +710,7 @@ static ssize_t mem_write(struct file*, const char*, size_t, loff_t*); static struct file_operations aux_ops = { + owner: THIS_MODULE, /* FIXME: should have custom llseek with bounds checking */ read: mem_read, write: mem_write, @@ -717,61 +723,10 @@ static struct file_operations aux_ops = { static void aux_setup_pcls(struct ti_lynx *lynx) { struct ti_pcl pcl; - unsigned long membufbus = virt_to_bus(lynx->mem_dma_buffer); - int i; - /* This pcl is used to start any aux transfers, the pointer to next - points to itself to avoid a dummy pcl (the PCL engine only executes - the next pcl on startup. The real chain is done by branch */ - pcl.next = pcl_bus(lynx, lynx->mem_pcl.start); - pcl.buffer[0].control = PCL_CMD_BRANCH | PCL_COND_DMARDY_SET; - pcl.buffer[0].pointer = pcl_bus(lynx, lynx->mem_pcl.max); - pcl.buffer[1].control = PCL_CMD_BRANCH | PCL_COND_DMARDY_CLEAR; - pcl.buffer[1].pointer = pcl_bus(lynx, lynx->mem_pcl.cmd); - put_pcl(lynx, lynx->mem_pcl.start, &pcl); - - /* let maxpcl transfer exactly 32kB */ pcl.next = PCL_NEXT_INVALID; - for (i=0; i<8; i++) { - pcl.buffer[i].control = 4000; - pcl.buffer[i].pointer = membufbus + i * 4000; - } - pcl.buffer[0].control |= PCL_CMD_LBUS_TO_PCI /*| PCL_GEN_INTR*/; - pcl.buffer[8].control = 768 | PCL_LAST_BUFF; - pcl.buffer[8].pointer = membufbus + 8 * 4000; - put_pcl(lynx, lynx->mem_pcl.max, &pcl); - - - /* magic stuff - self and modpcl modifying pcl */ - pcl.next = pcl_bus(lynx, lynx->mem_pcl.mod); - pcl.user_data = 4000; - pcl.buffer[0].control = PCL_CMD_LOAD; - pcl.buffer[0].pointer = pcl_bus(lynx, lynx->mem_pcl.cmd) - + pcloffs(user_data); - pcl.buffer[1].control = PCL_CMD_STOREQ; - pcl.buffer[1].pointer = pcl_bus(lynx, lynx->mem_pcl.mod) - + pcloffs(buffer[1].control); - pcl.buffer[2].control = PCL_CMD_LOAD; - pcl.buffer[2].pointer = membufbus; - pcl.buffer[3].control = PCL_CMD_STOREQ; - pcl.buffer[3].pointer = pcl_bus(lynx, lynx->mem_pcl.cmd) - + pcloffs(buffer[1].pointer); - pcl.buffer[4].control = PCL_CMD_STOREQ; - pcl.buffer[4].pointer = pcl_bus(lynx, lynx->mem_pcl.cmd) - + pcloffs(buffer[6].pointer); - pcl.buffer[5].control = PCL_CMD_LOAD; - pcl.buffer[5].pointer = membufbus + 4; - pcl.buffer[6].control = PCL_CMD_STOREQ | PCL_LAST_CMD; - put_pcl(lynx, lynx->mem_pcl.cmd, &pcl); - - /* modified by cmdpcl when actual transfer occurs */ - pcl.next = PCL_NEXT_INVALID; - pcl.buffer[0].control = PCL_CMD_LBUS_TO_PCI; /* null transfer */ - for (i=1; i<13; i++) { - pcl.buffer[i].control = 4000; - pcl.buffer[i].pointer = membufbus + (i-1) * 4000; - } - put_pcl(lynx, lynx->mem_pcl.mod, &pcl); + pcl.user_data = pcl_bus(lynx, lynx->dmem_pcl); + put_pcl(lynx, lynx->dmem_pcl, &pcl); } static int mem_open(struct inode *inode, struct file *file) @@ -779,24 +734,19 @@ static int mem_open(struct inode *inode, struct file *file) int cid = MINOR(inode->i_rdev); enum { rom, aux, ram } type; struct memdata *md; - - MOD_INC_USE_COUNT; if (cid < PCILYNX_MINOR_AUX_START) { /* just for completeness */ - MOD_DEC_USE_COUNT; return -ENXIO; } else if (cid < PCILYNX_MINOR_ROM_START) { cid -= PCILYNX_MINOR_AUX_START; if (cid >= num_of_cards || !cards[cid].aux_port) { - MOD_DEC_USE_COUNT; return -ENXIO; } type = aux; } else if (cid < PCILYNX_MINOR_RAM_START) { cid -= PCILYNX_MINOR_ROM_START; if (cid >= num_of_cards || !cards[cid].local_rom) { - MOD_DEC_USE_COUNT; return -ENXIO; } type = rom; @@ -805,7 +755,6 @@ static int mem_open(struct inode *inode, struct file *file) * It is currently used inside the driver! */ cid -= PCILYNX_MINOR_RAM_START; if (cid >= num_of_cards || !cards[cid].local_ram) { - MOD_DEC_USE_COUNT; return -ENXIO; } type = ram; @@ -813,7 +762,6 @@ static int mem_open(struct inode *inode, struct file *file) md = (struct memdata *)vmalloc(sizeof(struct memdata)); if (md == NULL) { - MOD_DEC_USE_COUNT; return -ENOMEM; } @@ -828,7 +776,8 @@ static int mem_open(struct inode *inode, struct file *file) md->type = ram; break; case aux: - md->aux_intr_last_seen = atomic_read(&cards[cid].aux_intr_seen); + atomic_set(&md->aux_intr_last_seen, + atomic_read(&cards[cid].aux_intr_seen)); md->type = aux; break; } @@ -844,7 +793,6 @@ static int mem_release(struct inode *inode, struct file *file) vfree(md); - MOD_DEC_USE_COUNT; return 0; } @@ -862,11 +810,9 @@ static unsigned int aux_poll(struct file *file, poll_table *pt) poll_wait(file, &cards[cid].aux_intr_wait, pt); intr_seen = atomic_read(&cards[cid].aux_intr_seen); - if (md->aux_intr_last_seen != intr_seen) { + if (atomic_read(&md->aux_intr_last_seen) != intr_seen) { mask |= POLLPRI; - /* md->aux_intr_last_seen = intr_seen; */ - md->aux_intr_last_seen++; /* don't miss interrupts */ - /* FIXME - make ioctl for configuring this */ + atomic_inc(&md->aux_intr_last_seen); } } @@ -882,38 +828,104 @@ static unsigned int aux_poll(struct file *file, poll_table *pt) short mem_mindma = 2400; MODULE_PARM(mem_mindma, "h"); +static ssize_t mem_dmaread(struct memdata *md, u32 physbuf, ssize_t count, + int offset) +{ + pcltmp_t pcltmp; + struct ti_pcl *pcl; + size_t retval; + int i; + DECLARE_WAITQUEUE(wait, current); + + //printk("buf 0x%08x %x count %d offset %d\n", physbuf, physbuf % 3, count, offset); + + count &= ~3; + count = MIN(count, 53196); + retval = count; + + if (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) + & DMA_CHAN_CTRL_BUSY) { + PRINT(KERN_WARNING, md->lynx->id, "DMA ALREADY ACTIVE!"); + } + + switch (md->type) { + case rom: + reg_write(md->lynx, LBUS_ADDR, LBUS_ADDR_SEL_ROM | offset); + break; + case ram: + reg_write(md->lynx, LBUS_ADDR, LBUS_ADDR_SEL_RAM | offset); + break; + case aux: + reg_write(md->lynx, LBUS_ADDR, LBUS_ADDR_SEL_AUX | offset); + break; + } + + pcl = edit_pcl(md->lynx, md->lynx->dmem_pcl, &pcltmp); + pcl->buffer[0].control = PCL_CMD_LBUS_TO_PCI | MIN(count, 4092); + pcl->buffer[0].pointer = physbuf; + count -= 4092; + + i = 0; + while (count > 0) { + i++; + pcl->buffer[i].control = MIN(count, 4092); + pcl->buffer[i].pointer = physbuf + i * 4092; + count -= 4092; + } + pcl->buffer[i].control |= PCL_LAST_BUFF; + commit_pcl(md->lynx, md->lynx->dmem_pcl, &pcltmp); + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&md->lynx->mem_dma_intr_wait, &wait); + run_sub_pcl(md->lynx, md->lynx->dmem_pcl, 2, CHANNEL_LOCALBUS); + + schedule(); + while (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) + & DMA_CHAN_CTRL_BUSY) { + if (signal_pending(current)) { + retval = -EINTR; + break; + } + schedule(); + } + + reg_write(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS), 0); + remove_wait_queue(&md->lynx->mem_dma_intr_wait, &wait); + + if (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) + & DMA_CHAN_CTRL_BUSY) { + PRINT(KERN_ERR, md->lynx->id, "DMA STILL ACTIVE!"); + } + + return retval; +} + static ssize_t mem_read(struct file *file, char *buffer, size_t count, loff_t *offset) { struct memdata *md = (struct memdata *)file->private_data; - size_t bcount; + ssize_t bcount; size_t alignfix; int off = (int)*offset; /* avoid useless 64bit-arithmetic */ + ssize_t retval; void *membase; - DECLARE_WAITQUEUE(wait, current); - - if ((off + count) > PCILYNX_MAX_MEMORY+1) { - count = PCILYNX_MAX_MEMORY+1 - off; + if ((off + count) > PCILYNX_MAX_MEMORY + 1) { + count = PCILYNX_MAX_MEMORY + 1 - off; } if (count <= 0) { return 0; } - down(&md->lynx->mem_dma_mutex); - switch (md->type) { case rom: - reg_write(md->lynx, LBUS_ADDR, LBUS_ADDR_SEL_ROM | off); membase = md->lynx->local_rom; break; case ram: - reg_write(md->lynx, LBUS_ADDR, LBUS_ADDR_SEL_RAM | off); membase = md->lynx->local_ram; break; case aux: - reg_write(md->lynx, LBUS_ADDR, LBUS_ADDR_SEL_AUX | off); membase = md->lynx->aux_port; break; default: @@ -921,89 +933,49 @@ static ssize_t mem_read(struct file *file, char *buffer, size_t count, md->lynx->id, md->type); } + down(&md->lynx->mem_dma_mutex); + if (count < mem_mindma) { memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, count); - copy_to_user(buffer, md->lynx->mem_dma_buffer, count); - bcount = 0; - goto done; + goto out; } - + bcount = count; alignfix = 4 - (off % 4); if (alignfix != 4) { if (bcount < alignfix) { alignfix = bcount; } - memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, alignfix); - copy_to_user(buffer, md->lynx->mem_dma_buffer, alignfix); + memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, + alignfix); if (bcount == alignfix) { - goto done; + goto out; } bcount -= alignfix; - buffer += alignfix; off += alignfix; } - if (reg_read(md->lynx, DMA0_CHAN_CTRL) & DMA_CHAN_CTRL_BUSY) { - PRINT(KERN_WARNING, md->lynx->id, "DMA ALREADY ACTIVE!"); - } - - add_wait_queue(&md->lynx->mem_dma_intr_wait, &wait); - - if (bcount > 32768) { - current->state = TASK_INTERRUPTIBLE; + while (bcount >= 4) { + retval = mem_dmaread(md, virt_to_phys(md->lynx->mem_dma_buffer) + + count - bcount, bcount, off); + if (retval < 0) return retval; - reg_write(md->lynx, DMA0_READY, 1); /* select maxpcl */ - run_pcl(md->lynx, md->lynx->mem_pcl.start, 0); - - while (reg_read(md->lynx, DMA0_CHAN_CTRL) - & DMA_CHAN_CTRL_BUSY) { - if (signal_pending(current)) { - reg_write(md->lynx, DMA0_CHAN_CTRL, 0); - goto rmwait_done; - } - schedule(); - } - - copy_to_user(buffer, md->lynx->mem_dma_buffer, 32768); - buffer += 32768; - bcount -= 32768; - } - - *(u32 *)(md->lynx->mem_dma_buffer) = - pcl_bus(md->lynx, md->lynx->mem_pcl.mod) - + pcloffs(buffer[bcount/4000+1].control); - *(u32 *)(md->lynx->mem_dma_buffer+4) = PCL_LAST_BUFF | (bcount % 4000); - - current->state = TASK_INTERRUPTIBLE; - - reg_write(md->lynx, DMA0_READY, 0); - run_pcl(md->lynx, md->lynx->mem_pcl.start, 0); - - while (reg_read(md->lynx, DMA0_CHAN_CTRL) & DMA_CHAN_CTRL_BUSY) { - if (signal_pending(current)) { - reg_write(md->lynx, DMA0_CHAN_CTRL, 0); - goto rmwait_done; - } - schedule(); + bcount -= retval; + off += retval; } - copy_to_user(buffer, md->lynx->mem_dma_buffer, bcount); - bcount = 0; - - if (reg_read(md->lynx, DMA0_CHAN_CTRL) & DMA_CHAN_CTRL_BUSY) { - PRINT(KERN_ERR, md->lynx->id, "DMA STILL ACTIVE!"); + if (bcount) { + memcpy_fromio(md->lynx->mem_dma_buffer + count - bcount, + membase+off, bcount); } - rmwait_done: - reg_write(md->lynx, DMA0_CHAN_CTRL, 0); - remove_wait_queue(&md->lynx->mem_dma_intr_wait, &wait); - done: + out: + retval = copy_to_user(buffer, md->lynx->mem_dma_buffer, count); up(&md->lynx->mem_dma_mutex); - count -= bcount; + if (retval < 0) return retval; *offset += count; - return (count ? count : -EINTR); + return count; } @@ -1200,6 +1172,12 @@ static void iso_rcv_bh(struct ti_lynx *lynx) data = lynx->iso_rcv.page[idx / ISORCV_PER_PAGE] + (idx % ISORCV_PER_PAGE) * MAX_ISORCV_SIZE; + if ((*data >> 16) + 4 != (lynx->iso_rcv.stat[idx] & 0x1fff)) { + PRINT(KERN_ERR, lynx->id, + "iso length mismatch 0x%08x/0x%08x", *data, + lynx->iso_rcv.stat[idx]); + } + if (lynx->iso_rcv.stat[idx] & (DMA_CHAN_STAT_PCIERR | DMA_CHAN_STAT_PKTERR)) { PRINT(KERN_INFO, lynx->id, @@ -1224,11 +1202,12 @@ static void iso_rcv_bh(struct ti_lynx *lynx) static int add_card(struct pci_dev *dev) { -#define FAIL(fmt, args...) \ +#define FAIL(fmt, args...) do { \ PRINT_G(KERN_ERR, fmt , ## args); \ num_of_cards--; \ remove_card(lynx); \ - return 1 + return 1; \ + } while (0) struct ti_lynx *lynx; /* shortcut to currently handled device */ unsigned long page; @@ -1246,6 +1225,9 @@ static int add_card(struct pci_dev *dev) lynx->id = num_of_cards-1; lynx->dev = dev; + if (pci_enable_device(dev)) { + FAIL("failed to enable PCILynx hardware %d", lynx->id); + } pci_set_master(dev); if (!request_irq(dev->irq, lynx_irq_handler, SA_SHIRQ, @@ -1271,7 +1253,7 @@ static int add_card(struct pci_dev *dev) } #endif - lynx->mem_dma_buffer = kmalloc(32768, GFP_KERNEL); + lynx->mem_dma_buffer = kmalloc(65536, GFP_KERNEL); if (lynx->mem_dma_buffer != NULL) { lynx->state = have_aux_buf; } else { @@ -1301,15 +1283,15 @@ static int add_card(struct pci_dev *dev) lynx->local_ram = ioremap(dev->base_address[1], PCILYNX_MAX_MEMORY); lynx->aux_port = ioremap(dev->base_address[2], PCILYNX_MAX_MEMORY); #else - lynx->registers = ioremap_nocache(dev->resource[0].start, + lynx->registers = ioremap_nocache(pci_resource_start(dev,0), PCILYNX_MAX_REGISTER); - lynx->local_ram = ioremap(dev->resource[1].start, PCILYNX_MAX_MEMORY); - lynx->aux_port = ioremap(dev->resource[2].start, PCILYNX_MAX_MEMORY); + lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY); + lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,15) lynx->local_rom = ioremap(dev->rom_address, PCILYNX_MAX_MEMORY); #else - lynx->local_rom = ioremap(dev->resource[PCI_ROM_RESOURCE].start, + lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE), PCILYNX_MAX_MEMORY); #endif lynx->state = have_iomappings; @@ -1328,12 +1310,8 @@ static int add_card(struct pci_dev *dev) /* alloc_pcl return values are not checked, it is expected that the * provided PCL space is sufficient for the initial allocations */ if (lynx->aux_port != NULL) { - lynx->mem_pcl.start = alloc_pcl(lynx); - lynx->mem_pcl.cmd = alloc_pcl(lynx); - lynx->mem_pcl.mod = alloc_pcl(lynx); - lynx->mem_pcl.max = alloc_pcl(lynx); + lynx->dmem_pcl = alloc_pcl(lynx); aux_setup_pcls(lynx); - sema_init(&lynx->mem_dma_mutex, 1); } lynx->rcv_pcl = alloc_pcl(lynx); diff --git a/drivers/ieee1394/pcilynx.h b/drivers/ieee1394/pcilynx.h index f8154bb42..cf45d8d0a 100644 --- a/drivers/ieee1394/pcilynx.h +++ b/drivers/ieee1394/pcilynx.h @@ -19,7 +19,7 @@ #define ISORCV_PER_PAGE (PAGE_SIZE / MAX_ISORCV_SIZE) #define ISORCV_PAGES (NUM_ISORCV_PCL / ISORCV_PER_PAGE) -/* only iso rcv uses these definitions so far */ +/* only iso rcv and localbus use these definitions so far */ #define CHANNEL_LOCALBUS 0 #define CHANNEL_ASYNC_RCV 1 #define CHANNEL_ISO_RCV 2 @@ -70,9 +70,7 @@ struct ti_lynx { #endif /* PCLs for local mem / aux transfers */ - struct { - pcl_t start, cmd, mod, max; - } mem_pcl; + pcl_t dmem_pcl; /* IEEE-1394 part follows */ struct hpsb_host *host; @@ -105,7 +103,7 @@ struct ti_lynx { struct memdata { struct ti_lynx *lynx; int cid; - int aux_intr_last_seen; + atomic_t aux_intr_last_seen; enum { rom, aux, ram } type; }; @@ -415,6 +413,38 @@ inline static u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) #endif /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */ +#if defined (CONFIG_IEEE1394_PCILYNX_LOCALRAM) || defined (__BIG_ENDIAN) +typedef struct ti_pcl pcltmp_t; + +inline static struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid, + pcltmp_t *tmp) +{ + get_pcl(lynx, pclid, tmp); + return tmp; +} + +inline static void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid, + pcltmp_t *tmp) +{ + put_pcl(lynx, pclid, tmp); +} + +#else +typedef int pcltmp_t; /* just a dummy */ + +inline static struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid, + pcltmp_t *tmp) +{ + return lynx->pcl_mem + pclid * sizeof(struct ti_pcl); +} + +inline static void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid, + pcltmp_t *tmp) +{ +} +#endif + + inline static void run_sub_pcl(const struct ti_lynx *lynx, pcl_t pclid, int idx, int dmachan) { @@ -464,73 +494,73 @@ inline static void run_pcl(const struct ti_lynx *lynx, pcl_t pclid, int dmachan) #define _(x) (__constant_cpu_to_be32(x)) -quadlet_t lynx_csr_rom[] = { - /* bus info block */ - _(0x04040000), /* info/CRC length, CRC */ - _(0x31333934), /* 1394 magic number */ - _(0xf064a000), /* misc. settings */ - _(0x08002850), /* vendor ID, chip ID high */ - _(0x0000ffff), /* chip ID low */ - /* root directory */ - _(0x00090000), /* CRC length, CRC */ - _(0x03080028), /* vendor ID (Texas Instr.) */ - _(0x81000009), /* offset to textual ID */ - _(0x0c000200), /* node capabilities */ - _(0x8d00000e), /* offset to unique ID */ - _(0xc7000010), /* offset to module independent info */ - _(0x04000000), /* module hardware version */ - _(0x81000026), /* offset to textual ID */ - _(0x09000000), /* node hardware version */ - _(0x81000026), /* offset to textual ID */ - /* module vendor ID textual */ - _(0x00080000), /* CRC length, CRC */ - _(0x00000000), - _(0x00000000), - _(0x54455841), /* "Texas Instruments" */ - _(0x5320494e), - _(0x53545255), - _(0x4d454e54), - _(0x53000000), - /* node unique ID leaf */ - _(0x00020000), /* CRC length, CRC */ - _(0x08002850), /* vendor ID, chip ID high */ - _(0x0000ffff), /* chip ID low */ - /* module dependent info */ - _(0x00060000), /* CRC length, CRC */ - _(0xb8000006), /* offset to module textual ID */ - _(0x81000004), /* ??? textual descriptor */ - _(0x39010000), /* SRAM size */ - _(0x3a010000), /* AUXRAM size */ - _(0x3b000000), /* AUX device */ - /* module textual ID */ - _(0x00050000), /* CRC length, CRC */ - _(0x00000000), - _(0x00000000), - _(0x54534231), /* "TSB12LV21" */ - _(0x324c5632), - _(0x31000000), - /* part number */ - _(0x00060000), /* CRC length, CRC */ - _(0x00000000), - _(0x00000000), - _(0x39383036), /* "9806000-0001" */ - _(0x3030342d), - _(0x30303431), - _(0x20000001), - /* module hardware version textual */ - _(0x00050000), /* CRC length, CRC */ - _(0x00000000), - _(0x00000000), - _(0x5453424b), /* "TSBKPCITST" */ - _(0x50434954), - _(0x53540000), - /* node hardware version textual */ - _(0x00050000), /* CRC length, CRC */ - _(0x00000000), - _(0x00000000), - _(0x54534232), /* "TSB21LV03" */ - _(0x313c5630), - _(0x33000000) +static quadlet_t lynx_csr_rom[] = { +/* bus info block offset (hex) */ + _(0x04040000), /* info/CRC length, CRC 400 */ + _(0x31333934), /* 1394 magic number 404 */ + _(0xf064a000), /* misc. settings 408 */ + _(0x08002850), /* vendor ID, chip ID high 40c */ + _(0x0000ffff), /* chip ID low 410 */ +/* root directory */ + _(0x00090000), /* directory length, CRC 414 */ + _(0x03080028), /* vendor ID (Texas Instr.) 418 */ + _(0x81000008), /* offset to textual ID 41c */ + _(0x0c000200), /* node capabilities 420 */ + _(0x8d00000e), /* offset to unique ID 424 */ + _(0xc7000010), /* offset to module independent info 428 */ + _(0x04000000), /* module hardware version 42c */ + _(0x81000014), /* offset to textual ID 430 */ + _(0x09000000), /* node hardware version 434 */ + _(0x81000018), /* offset to textual ID 438 */ + /* module vendor ID textual */ + _(0x00070000), /* CRC length, CRC 43c */ + _(0x00000000), /* 440 */ + _(0x00000000), /* 444 */ + _(0x54455841), /* "Texas Instruments" 448 */ + _(0x5320494e), /* 44c */ + _(0x53545255), /* 450 */ + _(0x4d454e54), /* 454 */ + _(0x53000000), /* 458 */ +/* node unique ID leaf */ + _(0x00020000), /* CRC length, CRC 45c */ + _(0x08002850), /* vendor ID, chip ID high 460 */ + _(0x0000ffff), /* chip ID low 464 */ +/* module dependent info */ + _(0x00050000), /* CRC length, CRC 468 */ + _(0x81000012), /* offset to module textual ID 46c */ + _(0x81000017), /* textual descriptor 470 */ + _(0x39010000), /* SRAM size 474 */ + _(0x3a010000), /* AUXRAM size 478 */ + _(0x3b000000), /* AUX device 47c */ +/* module textual ID */ + _(0x00050000), /* CRC length, CRC 480 */ + _(0x00000000), /* 484 */ + _(0x00000000), /* 488 */ + _(0x54534231), /* "TSB12LV21" 48c */ + _(0x324c5632), /* 490 */ + _(0x31000000), /* 494 */ +/* part number */ + _(0x00060000), /* CRC length, CRC 498 */ + _(0x00000000), /* 49c */ + _(0x00000000), /* 4a0 */ + _(0x39383036), /* "9806000-0001" 4a4 */ + _(0x3030302d), /* 4a8 */ + _(0x30303031), /* 4ac */ + _(0x20000001), /* 4b0 */ +/* module hardware version textual */ + _(0x00050000), /* CRC length, CRC 4b4 */ + _(0x00000000), /* 4b8 */ + _(0x00000000), /* 4bc */ + _(0x5453424b), /* "TSBKPCITST" 4c0 */ + _(0x50434954), /* 4c4 */ + _(0x53540000), /* 4c8 */ +/* node hardware version textual */ + _(0x00050000), /* CRC length, CRC 4d0 */ + _(0x00000000), /* 4d4 */ + _(0x00000000), /* 4d8 */ + _(0x54534232), /* "TSB21LV03" 4dc */ + _(0x314c5630), /* 4e0 */ + _(0x33000000) /* 4e4 */ }; #undef _ diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 4a994aba5..523f5d393 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -575,8 +575,8 @@ static int handle_local_request(struct file_info *fi, break; case RAW1394_REQ_LOCK: - if ((req->req.misc != EXTCODE_FETCH_ADD) - && (req->req.misc != EXTCODE_LITTLE_ADD)) { + if ((req->req.misc == EXTCODE_FETCH_ADD) + || (req->req.misc == EXTCODE_LITTLE_ADD)) { if (req->req.length != 4) { req->req.error = RAW1394_ERROR_INVALID_ARG; break; @@ -667,8 +667,8 @@ static int handle_remote_request(struct file_info *fi, break; case RAW1394_REQ_LOCK: - if ((req->req.misc != EXTCODE_FETCH_ADD) - && (req->req.misc != EXTCODE_LITTLE_ADD)) { + if ((req->req.misc == EXTCODE_FETCH_ADD) + || (req->req.misc == EXTCODE_LITTLE_ADD)) { if (req->req.length != 4) { req->req.error = RAW1394_ERROR_INVALID_ARG; break; @@ -690,6 +690,7 @@ static int handle_remote_request(struct file_info *fi, break; } + req->data = packet->data; req->req.length = 4; break; @@ -716,6 +717,7 @@ static int handle_remote_request(struct file_info *fi, if (!hpsb_send_packet(packet)) { req->req.error = RAW1394_ERROR_SEND_ERROR; req->req.length = 0; + free_tlabel(packet->host, packet->node_id, packet->tlabel); queue_complete_req(req); } return sizeof(struct raw1394_request); @@ -843,7 +845,6 @@ static int dev_open(struct inode *inode, struct file *file) file->private_data = fi; - MOD_INC_USE_COUNT; return 0; } @@ -897,7 +898,6 @@ static int dev_release(struct inode *inode, struct file *file) kfree(fi); - MOD_DEC_USE_COUNT; return 0; } @@ -910,6 +910,7 @@ static struct hpsb_highlevel_ops hl_ops = { }; static struct file_operations file_ops = { + owner: THIS_MODULE, read: dev_read, write: dev_write, poll: dev_poll, diff --git a/drivers/ieee1394/video1394.h b/drivers/ieee1394/video1394.h new file mode 100644 index 000000000..47b8e64a2 --- /dev/null +++ b/drivers/ieee1394/video1394.h @@ -0,0 +1,50 @@ +/* + * video1394.h - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _VIDEO_1394_H +#define _VIDEO_1394_H + +#define VIDEO1394_MAX_SIZE 0x400000 + +enum { + VIDEO1394_BUFFER_FREE = 0, + VIDEO1394_BUFFER_QUEUED, + VIDEO1394_BUFFER_READY +}; + +enum { + VIDEO1394_LISTEN_CHANNEL = 0, + VIDEO1394_UNLISTEN_CHANNEL, + VIDEO1394_QUEUE_BUFFER, + VIDEO1394_WAIT_BUFFER +}; + +struct video1394_mmap { + int channel; + int sync_tag; + int nb_buffers; + int buf_size; +}; + +struct video1394_wait { + int channel; + int buffer; +}; + +#endif |