summaryrefslogtreecommitdiffstats
path: root/drivers/ieee1394
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-06-19 22:45:37 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-06-19 22:45:37 +0000
commit6d403070f28cd44860fdb3a53be5da0275c65cf4 (patch)
tree0d0e7fe7b5fb7568d19e11d7d862b77a866ce081 /drivers/ieee1394
parentecf1bf5f6c2e668d03b0a9fb026db7aa41e292e1 (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/Makefile8
-rw-r--r--drivers/ieee1394/aic5800.c7
-rw-r--r--drivers/ieee1394/guid.c226
-rw-r--r--drivers/ieee1394/guid.h54
-rw-r--r--drivers/ieee1394/ieee1394_core.c87
-rw-r--r--drivers/ieee1394/ieee1394_syms.c6
-rw-r--r--drivers/ieee1394/ieee1394_transactions.c32
-rw-r--r--drivers/ieee1394/ieee1394_types.h17
-rw-r--r--drivers/ieee1394/ohci1394.c1230
-rw-r--r--drivers/ieee1394/ohci1394.h63
-rw-r--r--drivers/ieee1394/pcilynx.c286
-rw-r--r--drivers/ieee1394/pcilynx.h174
-rw-r--r--drivers/ieee1394/raw1394.c13
-rw-r--r--drivers/ieee1394/video1394.h50
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(&current->sigmask_lock);
- signr = dequeue_signal(&current->blocked, &info);
- spin_unlock_irq(&current->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