patch-2.4.20 linux-2.4.20/drivers/ieee1394/ohci1394.c

Next file: linux-2.4.20/drivers/ieee1394/ohci1394.h
Previous file: linux-2.4.20/drivers/ieee1394/nodemgr.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/drivers/ieee1394/ohci1394.c linux-2.4.20/drivers/ieee1394/ohci1394.c
@@ -154,7 +154,7 @@
 printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
 
 static char version[] __devinitdata =
-	"$Rev: 530 $ Ben Collins <bcollins@debian.org>";
+	"$Rev: 578 $ Ben Collins <bcollins@debian.org>";
 
 /* Module Parameters */
 MODULE_PARM(attempt_root,"i");
@@ -170,10 +170,6 @@
 
 static void ohci1394_pci_remove(struct pci_dev *pdev);
 
-static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci,
-					  quadlet_t isoRecvEvent, 
-					  quadlet_t isoXmitEvent);
-
 #ifndef __LITTLE_ENDIAN
 /* Swap a series of quads inplace. */
 static __inline__ void block_swab32(quadlet_t *data, size_t size) {
@@ -443,10 +439,8 @@
 	d->sent_ind = 0;
 	d->free_prgs = d->num_desc;
         d->branchAddrPtr = NULL;
-	d->fifo_first = NULL;
-	d->fifo_last = NULL;
-	d->pending_first = NULL;
-	d->pending_last = NULL;
+	INIT_LIST_HEAD(&d->fifo_list);
+	INIT_LIST_HEAD(&d->pending_list);
 
 	DBGMSG(ohci->id, "Transmit DMA ctx=%d initialized", d->ctx);
 }
@@ -477,34 +471,6 @@
 {
 	quadlet_t buf;
 
-	/* Start off with a soft reset, to clear everything to a sane
-	 * state. */
-	ohci_soft_reset(ohci);
-
-	/* Now enable LPS, which we need in order to start accessing
-	 * most of the registers.  In fact, on some cards (ALI M5251),
-	 * accessing registers in the SClk domain without LPS enabled
-	 * will lock up the machine.  Wait 50msec to make sure we have
-	 * full link enabled.  */
-	reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
-	mdelay(50);
-
-	/* Determine the number of available IR and IT contexts. */
-	ohci->nb_iso_rcv_ctx =
-		get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
-	DBGMSG(ohci->id, "%d iso receive contexts available",
-	       ohci->nb_iso_rcv_ctx);
-
-	ohci->nb_iso_xmit_ctx = 
-		get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
-	DBGMSG(ohci->id, "%d iso transmit contexts available",
-	       ohci->nb_iso_xmit_ctx);
-
-	/* Set the usage bits for non-existent contexts so they can't
-	 * be allocated */
-	ohci->ir_ctx_usage |= ~0 << ohci->nb_iso_rcv_ctx;
-	ohci->it_ctx_usage |= ~0 << ohci->nb_iso_xmit_ctx;
-	
 	spin_lock_init(&ohci->phy_reg_lock);
 	spin_lock_init(&ohci->event_lock);
   
@@ -571,18 +537,18 @@
 	reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
 
 	/* Initialize AR dma */
-	initialize_dma_rcv_ctx(ohci->ar_req_context, 0);
-	initialize_dma_rcv_ctx(ohci->ar_resp_context, 0);
+	initialize_dma_rcv_ctx(&ohci->ar_req_context, 0);
+	initialize_dma_rcv_ctx(&ohci->ar_resp_context, 0);
 
 	/* Initialize AT dma */
-	initialize_dma_trm_ctx(ohci->at_req_context);
-	initialize_dma_trm_ctx(ohci->at_resp_context);
+	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, 1);
+	initialize_dma_rcv_ctx(&ohci->ir_context, 1);
 
         /* Initialize IT dma */
-        initialize_dma_trm_ctx(ohci->it_context);
+        initialize_dma_trm_ctx(&ohci->it_context);
 
 	/* Set up isoRecvIntMask to generate interrupts for context 0
 	   (thanks to Michael Greger for seeing that I forgot this) */
@@ -629,7 +595,7 @@
 	      ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
 	      ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
 	      pci_resource_start(ohci->dev, 0),
-	      pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE,
+	      pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1,
 	      ohci->max_packet_size);
 }
 
@@ -789,13 +755,7 @@
 	d->free_prgs--;
 
 	/* queue the packet in the appropriate context queue */
-	if (d->fifo_last) {
-		d->fifo_last->xnext = packet;
-		d->fifo_last = packet;
-	} else {
-		d->fifo_first = packet;
-		d->fifo_last = packet;
-	}
+	list_add_tail(&packet->driver_list, &d->fifo_list);
 	d->prg_ind = (d->prg_ind+1)%d->num_desc;
 }
 
@@ -807,22 +767,24 @@
  */ 
 static int dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d)
 {
+	struct hpsb_packet *p;
 	int idx,z;
 
-	if (d->pending_first == NULL || d->free_prgs == 0) 
+	if (list_empty(&d->pending_list) || d->free_prgs == 0)
 		return 0;
 
+	p = driver_packet(d->pending_list.next);
 	idx = d->prg_ind;
-	z = (d->pending_first->data_size) ? 3 : 2;
+	z = (p->data_size) ? 3 : 2;
 
 	/* insert the packets into the at dma fifo */
-	while (d->free_prgs>0 && d->pending_first) {
-		insert_packet(ohci, d, d->pending_first);
-		d->pending_first = d->pending_first->xnext;
+	while (d->free_prgs > 0 && !list_empty(&d->pending_list)) {
+		struct hpsb_packet *p = driver_packet(d->pending_list.next);
+		list_del(&p->driver_list);
+		insert_packet(ohci, d, p);
 	}
-	if (d->pending_first == NULL) 
-		d->pending_last = NULL;
-	else
+
+	if (d->free_prgs == 0)
 		PRINT(KERN_INFO, ohci->id, 
 		      "Transmit DMA FIFO ctx=%d is full... waiting",d->ctx);
 
@@ -848,7 +810,6 @@
 {
 	struct ti_ohci *ohci = host->hostdata;
 	struct dma_trm_ctx *d;
-	unsigned char tcode;
 	unsigned long flags;
 
 	if (packet->data_size > ohci->max_packet_size) {
@@ -857,25 +818,20 @@
 		      packet->data_size);
 		return 0;
 	}
-	packet->xnext = NULL;
 
 	/* Decide wether we have an iso, a request, or a response packet */
-	tcode = (packet->header[0]>>4)&0xf;
-	if (tcode == TCODE_ISO_DATA) d = ohci->it_context;
-	else if (tcode & 0x02) d = ohci->at_resp_context;
-	else d = ohci->at_req_context;
+	if (packet->type == hpsb_raw)
+		d = &ohci->at_req_context;
+	else if (packet->tcode == TCODE_ISO_DATA)
+		d = &ohci->it_context;
+	else if (packet->tcode & 0x02)
+		d = &ohci->at_resp_context;
+	else 
+		d = &ohci->at_req_context;
 
 	spin_lock_irqsave(&d->lock,flags);
 
-	/* queue the packet for later insertion into the dma fifo */
-	if (d->pending_last) {
-		d->pending_last->xnext = packet;
-		d->pending_last = packet;
-	}
-	else {
-		d->pending_first = packet;
-		d->pending_last = packet;
-	}
+	list_add_tail(&packet->driver_list, &d->pending_list);
 	
 	dma_trm_flush(ohci, d);
 
@@ -929,8 +885,8 @@
 
 	case CANCEL_REQUESTS:
 		DBGMSG(ohci->id, "Cancel request received");
-		dma_trm_reset(ohci->at_req_context);
-		dma_trm_reset(ohci->at_resp_context);
+		dma_trm_reset(&ohci->at_req_context);
+		dma_trm_reset(&ohci->at_resp_context);
 		break;
 
 	case MODIFY_USAGE:
@@ -1033,46 +989,62 @@
 
 static void dma_trm_reset(struct dma_trm_ctx *d)
 {
-	struct ti_ohci *ohci;
 	unsigned long flags;
-        struct hpsb_packet *nextpacket;
+	LIST_HEAD(packet_list);
 
-	if (d==NULL) {
-		PRINT_G(KERN_ERR, "dma_trm_reset called with NULL arg");
-		return;
-	}
-	ohci = (struct ti_ohci *)(d->ohci);
-	ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+	ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
 
-	spin_lock_irqsave(&d->lock,flags);
+	/* Lock the context, reset it and release it. Move the packets
+	 * that were pending in the context to packet_list and free
+	 * them after releasing the lock. */
 
-	/* Is there still any packet pending in the fifo ? */
-	while(d->fifo_first) {
-		PRINT(KERN_INFO, ohci->id, 
-		      "AT dma reset ctx=%d, aborting transmission", 
-		      d->ctx);
-                nextpacket = d->fifo_first->xnext;
-		hpsb_packet_sent(ohci->host, d->fifo_first, ACKX_ABORTED);
-		d->fifo_first = nextpacket;
-	}
-	d->fifo_first = d->fifo_last = NULL;
+	spin_lock_irqsave(&d->lock, flags);
 
-	/* is there still any packet pending ? */
-	while(d->pending_first) {
-		PRINT(KERN_INFO, ohci->id, 
-		      "AT dma reset ctx=%d, aborting transmission", 
-		      d->ctx);
-                nextpacket = d->pending_first->xnext;
-		hpsb_packet_sent(ohci->host, d->pending_first, 
-				 ACKX_ABORTED);
-		d->pending_first = nextpacket;
-	}
-	d->pending_first = d->pending_last = NULL;
-	
-	d->branchAddrPtr=NULL;
+	list_splice(&d->fifo_list, &packet_list);
+	list_splice(&d->pending_list, &packet_list);
+	INIT_LIST_HEAD(&d->fifo_list);
+	INIT_LIST_HEAD(&d->pending_list);
+
+	d->branchAddrPtr = NULL;
 	d->sent_ind = d->prg_ind;
 	d->free_prgs = d->num_desc;
-	spin_unlock_irqrestore(&d->lock,flags);
+
+	spin_unlock_irqrestore(&d->lock, flags);
+
+	/* Now process subsystem callbacks for the packets from the
+	 * context. */
+
+	while (!list_empty(&packet_list)) {
+		struct hpsb_packet *p = driver_packet(packet_list.next);
+		PRINT(KERN_INFO, d->ohci->id, 
+		      "AT dma reset ctx=%d, aborting transmission", d->ctx);
+		list_del(&p->driver_list);
+		hpsb_packet_sent(d->ohci->host, p, ACKX_ABORTED);
+	}
+}
+
+static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci, 
+				       quadlet_t rx_event,
+				       quadlet_t tx_event)
+{
+	struct list_head *lh;
+	struct ohci1394_iso_tasklet *t;
+	unsigned long mask;
+
+	spin_lock(&ohci->iso_tasklet_list_lock);
+
+	list_for_each(lh, &ohci->iso_tasklet_list) {
+		t = list_entry(lh, struct ohci1394_iso_tasklet, link);
+		mask = 1 << t->context;
+
+		if (t->type == OHCI_ISO_TRANSMIT && tx_event & mask)
+			tasklet_schedule(&t->tasklet);
+		if (t->type == OHCI_ISO_RECEIVE && rx_event & mask)
+			tasklet_schedule(&t->tasklet);
+	}
+
+	spin_unlock(&ohci->iso_tasklet_list_lock);
+
 }
 
 static void ohci_irq_handler(int irq, void *dev_id,
@@ -1143,7 +1115,7 @@
 	 * we get sent acks before response packets. This sucks mainly
 	 * because it halts the interrupt handler.  */
 	if (event & OHCI1394_reqTxComplete) {
-		struct dma_trm_ctx *d = ohci->at_req_context;
+		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)
@@ -1154,7 +1126,7 @@
 		event &= ~OHCI1394_reqTxComplete;
 	}
 	if (event & OHCI1394_respTxComplete) {
-		struct dma_trm_ctx *d = ohci->at_resp_context;
+		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)
@@ -1165,7 +1137,7 @@
 		event &= ~OHCI1394_respTxComplete;
 	}
 	if (event & OHCI1394_RQPkt) {
-		struct dma_rcv_ctx *d = ohci->ar_req_context;
+		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)
@@ -1175,7 +1147,7 @@
 		event &= ~OHCI1394_RQPkt;
 	}
 	if (event & OHCI1394_RSPkt) {
-		struct dma_rcv_ctx *d = ohci->ar_resp_context;
+		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)
@@ -1185,46 +1157,19 @@
 		event &= ~OHCI1394_RSPkt;
 	}
 	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 isochRx interrupt "
-		       "status=0x%08X isoRecvIntEvent=%08x", 
-		       reg_read(ohci, d->ctrlSet), isoRecvIntEvent);
-		if (isoRecvIntEvent & 0x1) {
-			if (reg_read(ohci, d->ctrlSet) & 0x800)
-				ohci1394_stop_context(ohci, d->ctrlClear, 
-					     "isochRx");
-			else
-				tasklet_schedule(&d->task);
-		}
-
-		ohci1394_run_irq_hooks(ohci, isoRecvIntEvent, 0);
+		quadlet_t rx_event;
 
+		rx_event = reg_read(ohci, OHCI1394_IsoRecvIntEventSet);
+		reg_write(ohci, OHCI1394_IsoRecvIntEventClear, rx_event);
+		ohci_schedule_iso_tasklets(ohci, rx_event, 0);
 		event &= ~OHCI1394_isochRx;
 	}
 	if (event & OHCI1394_isochTx) {
-		quadlet_t isoXmitIntEvent;
-		struct dma_trm_ctx *d = ohci->it_context;
-		isoXmitIntEvent = 
-			reg_read(ohci, OHCI1394_IsoXmitIntEventSet);
-		reg_write(ohci, OHCI1394_IsoXmitIntEventClear,
-			  isoXmitIntEvent);
-                       DBGMSG(ohci->id, "Got isochTx interrupt "
-                               "status=0x%08x isoXmitIntEvent=%08x",
-                              reg_read(ohci, d->ctrlSet), isoXmitIntEvent);
+		quadlet_t tx_event;		
 
-		ohci1394_run_irq_hooks(ohci, 0, isoXmitIntEvent);
-		
-		if (isoXmitIntEvent & 0x1) {
-			if (reg_read(ohci, d->ctrlSet) & 0x800)
-				ohci1394_stop_context(ohci, d->ctrlClear, "isochTx");
-			else
-				tasklet_schedule(&d->task);
-		}
+		tx_event = reg_read(ohci, OHCI1394_IsoXmitIntEventSet);
+		reg_write(ohci, OHCI1394_IsoXmitIntEventClear, tx_event);
+		ohci_schedule_iso_tasklets(ohci, 0, tx_event);
 		event &= ~OHCI1394_isochTx;
 	}
 	if (event & OHCI1394_selfIDComplete) {
@@ -1507,25 +1452,16 @@
 {
 	struct dma_trm_ctx *d = (struct dma_trm_ctx*)data;
 	struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
-	struct hpsb_packet *packet, *nextpacket;
+	struct hpsb_packet *packet;
 	unsigned long flags;
 	u32 ack;
         size_t datasize;
 
 	spin_lock_irqsave(&d->lock, flags);
 
-	if (d->fifo_first == NULL) {
-#if 0
-		ohci1394_stop_context(ohci, d->ctrlClear, 
-			     "Packet sent ack received but queue is empty");
-#endif
-		spin_unlock_irqrestore(&d->lock, flags);
-		return;
-	}
-
-	while (d->fifo_first) {
-		packet = d->fifo_first;
-                datasize = d->fifo_first->data_size;
+	while (!list_empty(&d->fifo_list)) {
+		packet = driver_packet(d->fifo_list.next);
+                datasize = packet->data_size;
 		if (datasize && packet->type != hpsb_raw)
 			ack = le32_to_cpu(
 				d->prg_cpu[d->sent_ind]->end.status) >> 16;
@@ -1576,7 +1512,7 @@
                                 d->ctx);
 #endif		
 
-                nextpacket = packet->xnext;
+                list_del(&packet->driver_list);
 		hpsb_packet_sent(ohci->host, packet, ack & 0xf);
 
 		if (datasize) {
@@ -1588,90 +1524,64 @@
 
 		d->sent_ind = (d->sent_ind+1)%d->num_desc;
 		d->free_prgs++;
-		d->fifo_first = nextpacket;
 	}
-	if (d->fifo_first == NULL)
-		d->fifo_last = NULL;
 
 	dma_trm_flush(ohci, d);
 
 	spin_unlock_irqrestore(&d->lock, flags);
 }
 
-static int free_dma_rcv_ctx(struct dma_rcv_ctx **d)
+static void 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->ohci == NULL)
+		return;
 
-	DBGMSG(ohci->id, "Freeing dma_rcv_ctx %d",(*d)->ctx);
+	DBGMSG(d->ohci->id, "Freeing dma_rcv_ctx %d", d->ctx);
 	
-	ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL);
+	ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
 
-	tasklet_kill(&(*d)->task);
+	if (d->type == DMA_CTX_ISO)
+		ohci1394_unregister_iso_tasklet(d->ohci, &d->ohci->ir_tasklet);
+	else
+		tasklet_kill(&d->task);
 
-	if ((*d)->buf_cpu) {
-		for (i=0; i<(*d)->num_desc; i++)
-			if ((*d)->buf_cpu[i] && (*d)->buf_bus[i]) {
+	if (d->buf_cpu) {
+		for (i=0; i<d->num_desc; i++)
+			if (d->buf_cpu[i] && d->buf_bus[i]) {
 				pci_free_consistent(
-					ohci->dev, (*d)->buf_size, 
-					(*d)->buf_cpu[i], (*d)->buf_bus[i]);
+					d->ohci->dev, d->buf_size, 
+					d->buf_cpu[i], d->buf_bus[i]);
 				OHCI_DMA_FREE("consistent dma_rcv buf[%d]", i);
 			}
-		kfree((*d)->buf_cpu);
-		kfree((*d)->buf_bus);
+		kfree(d->buf_cpu);
+		kfree(d->buf_bus);
 	}
-	if ((*d)->prg_cpu) {
-		for (i=0; i<(*d)->num_desc; i++) 
-			if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) {
+	if (d->prg_cpu) {
+		for (i=0; i<d->num_desc; i++) 
+			if (d->prg_cpu[i] && d->prg_bus[i]) {
 				pci_free_consistent(
-					ohci->dev, sizeof(struct dma_cmd), 
-					(*d)->prg_cpu[i], (*d)->prg_bus[i]);
+					d->ohci->dev, sizeof(struct dma_cmd), 
+					d->prg_cpu[i], d->prg_bus[i]);
 				OHCI_DMA_FREE("consistent dma_rcv prg[%d]", i);
 			}
-		kfree((*d)->prg_cpu);
-		kfree((*d)->prg_bus);
+		kfree(d->prg_cpu);
+		kfree(d->prg_bus);
 	}
-	if ((*d)->spb) kfree((*d)->spb);
+	if (d->spb) kfree(d->spb);
 
-	/* clear ISO context usage bit */
-	if ((*d)->type == DMA_CTX_ISO) {
-		clear_bit((*d)->ctx, &ohci->ir_ctx_usage);
-	}
-	       
-	kfree(*d);
-	*d = NULL;
-
-	return 0;
+	/* Mark this context as freed. */
+	d->ohci = NULL;
 }
 
-static struct dma_rcv_ctx *
-alloc_dma_rcv_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc,
+static int
+alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
+		  enum context_type type, int ctx, int num_desc,
 		  int buf_size, int split_buf_size, int context_base)
 {
-	struct dma_rcv_ctx *d;
 	int i;
 
-	if (type == DMA_CTX_ISO) {
-		/* try to claim the ISO context usage bit */
-		if (test_and_set_bit(ctx, &ohci->ir_ctx_usage)) {
-			PRINT(KERN_ERR, ohci->id, "IR DMA context %d is not available", ctx);
-			return NULL;
-		}
-	}
-	
-	d = kmalloc(sizeof(struct dma_rcv_ctx), GFP_KERNEL);
-
-	if (d == NULL) {
-		PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_rcv_ctx");
-		return NULL;
-	}
-
-	memset (d, 0, sizeof (struct dma_rcv_ctx));
-
 	d->ohci = ohci;
 	d->type = type;
 	d->ctx = ctx;
@@ -1689,8 +1599,8 @@
 
 	if (d->buf_cpu == NULL || d->buf_bus == NULL) {
 		PRINT(KERN_ERR, ohci->id, "Failed to allocate dma buffer");
-		free_dma_rcv_ctx(&d);
-		return NULL;
+		free_dma_rcv_ctx(d);
+		return -ENOMEM;
 	}
 	memset(d->buf_cpu, 0, d->num_desc * sizeof(quadlet_t*));
 	memset(d->buf_bus, 0, d->num_desc * sizeof(dma_addr_t));
@@ -1701,8 +1611,8 @@
 
 	if (d->prg_cpu == NULL || d->prg_bus == NULL) {
 		PRINT(KERN_ERR, ohci->id, "Failed to allocate dma prg");
-		free_dma_rcv_ctx(&d);
-		return NULL;
+		free_dma_rcv_ctx(d);
+		return -ENOMEM;
 	}
 	memset(d->prg_cpu, 0, d->num_desc * sizeof(struct dma_cmd*));
 	memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t));
@@ -1711,8 +1621,8 @@
 
 	if (d->spb == NULL) {
 		PRINT(KERN_ERR, ohci->id, "Failed to allocate split buffer");
-		free_dma_rcv_ctx(&d);
-		return NULL;
+		free_dma_rcv_ctx(d);
+		return -ENOMEM;
 	}
 
 	for (i=0; i<d->num_desc; i++) {
@@ -1726,8 +1636,8 @@
 		} else {
 			PRINT(KERN_ERR, ohci->id, 
 			      "Failed to allocate dma buffer");
-			free_dma_rcv_ctx(&d);
-			return NULL;
+			free_dma_rcv_ctx(d);
+			return -ENOMEM;
 		}
 
 		
@@ -1741,80 +1651,68 @@
 		} else {
 			PRINT(KERN_ERR, ohci->id, 
 			      "Failed to allocate dma prg");
-			free_dma_rcv_ctx(&d);
-			return NULL;
+			free_dma_rcv_ctx(d);
+			return -ENOMEM;
 		}
 	}
 
         spin_lock_init(&d->lock);
 
-	/* initialize tasklet */
-	tasklet_init (&d->task, dma_rcv_tasklet, (unsigned long)d);
+	if (type == DMA_CTX_ISO) {
+		ohci1394_init_iso_tasklet(&ohci->ir_tasklet, OHCI_ISO_RECEIVE,
+					  dma_rcv_tasklet, (unsigned long) d);
+		if (ohci1394_register_iso_tasklet(ohci,
+						  &ohci->ir_tasklet) < 0) {
+			PRINT(KERN_ERR, ohci->id, "No IR DMA context available");
+			free_dma_rcv_ctx(d);
+			return -EBUSY;
+		}
+	}
+	else
+		tasklet_init (&d->task, dma_rcv_tasklet, (unsigned long) d);
 
-	return d;
+	return 0;
 }
 
-static int free_dma_trm_ctx(struct dma_trm_ctx **d)
+static void free_dma_trm_ctx(struct dma_trm_ctx *d)
 {
-	struct ti_ohci *ohci;
 	int i;
 
-	if (*d==NULL) return -1;
-
-	ohci = (struct ti_ohci *)(*d)->ohci;
+	if (d->ohci == NULL)
+		return;
 
-	DBGMSG(ohci->id, "Freeing dma_trm_ctx %d",(*d)->ctx);
+	DBGMSG(d->ohci->id, "Freeing dma_trm_ctx %d", d->ctx);
 
-	ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL);
+	ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
 
-	tasklet_kill(&(*d)->task);
+	if (d->type == DMA_CTX_ISO)
+		ohci1394_unregister_iso_tasklet(d->ohci, &d->ohci->it_tasklet);
+	else
+		tasklet_kill(&d->task);
 
-	if ((*d)->prg_cpu) {
-		for (i=0; i<(*d)->num_desc; i++) 
-			if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) {
+	if (d->prg_cpu) {
+		for (i=0; i<d->num_desc; i++) 
+			if (d->prg_cpu[i] && d->prg_bus[i]) {
 				pci_free_consistent(
-					ohci->dev, sizeof(struct at_dma_prg), 
-					(*d)->prg_cpu[i], (*d)->prg_bus[i]);
+					d->ohci->dev, sizeof(struct at_dma_prg), 
+					d->prg_cpu[i], d->prg_bus[i]);
 				OHCI_DMA_FREE("consistent dma_trm prg[%d]", i);
 			}
-		kfree((*d)->prg_cpu);
-		kfree((*d)->prg_bus);
+		kfree(d->prg_cpu);
+		kfree(d->prg_bus);
 	}
 
-	/* clear the ISO context usage bit */
-	if ((*d)->type == DMA_CTX_ISO) {
-		clear_bit((*d)->ctx, &ohci->it_ctx_usage);
-	}
-
-	kfree(*d);
-	*d = NULL;
-	return 0;
+	/* Mark this context as freed. */
+	d->ohci = NULL;
 }
 
-static struct dma_trm_ctx *
-alloc_dma_trm_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc,
+static int
+alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
+		  enum context_type type, int ctx, int num_desc,
 		  int context_base)
 {
-	struct dma_trm_ctx *d;
 	int i;
 
-	if (type == DMA_CTX_ISO) {
-		/* try to claim the ISO context usage bit */
-		if (test_and_set_bit(ctx, &ohci->it_ctx_usage)) {
-			PRINT(KERN_ERR, ohci->id, "IT DMA context %d is not available", ctx);
-			return NULL;
-		}
-	}
-	
-	d = kmalloc(sizeof(struct dma_trm_ctx), GFP_KERNEL);
-
-	if (d == NULL) {
-		PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_trm_ctx");
-		return NULL;
-	}
-
-	memset (d, 0, sizeof (struct dma_trm_ctx));
-
 	d->ohci = ohci;
 	d->type = type;
 	d->ctx = ctx;
@@ -1829,8 +1727,8 @@
 
 	if (d->prg_cpu == NULL || d->prg_bus == NULL) {
 		PRINT(KERN_ERR, ohci->id, "Failed to allocate at dma prg");
-		free_dma_trm_ctx(&d);
-		return NULL;
+		free_dma_trm_ctx(d);
+		return -ENOMEM;
 	}
 	memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*));
 	memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t));
@@ -1846,17 +1744,28 @@
 		} else {
 			PRINT(KERN_ERR, ohci->id, 
 			      "Failed to allocate at dma prg");
-			free_dma_trm_ctx(&d);
-			return NULL;
+			free_dma_trm_ctx(d);
+			return -ENOMEM;
 		}
 	}
 
         spin_lock_init(&d->lock);
 
-        /* initialize bottom handler */
-	tasklet_init (&d->task, dma_trm_tasklet, (unsigned long)d);
+	/* initialize tasklet */
+	if (type == DMA_CTX_ISO) {
+		ohci1394_init_iso_tasklet(&ohci->it_tasklet, OHCI_ISO_TRANSMIT,
+					  dma_rcv_tasklet, (unsigned long) d);
+		if (ohci1394_register_iso_tasklet(ohci,
+						  &ohci->it_tasklet) < 0) {
+			PRINT(KERN_ERR, ohci->id, "No IT DMA context available");
+			free_dma_trm_ctx(d);
+			return -EBUSY;
+		}
+	}
+	else
+		tasklet_init (&d->task, dma_trm_tasklet, (unsigned long)d);
 
-	return d;
+	return 0;
 }
 
 static u16 ohci_crc16 (u32 *ptr, int length)
@@ -2028,15 +1937,14 @@
 	return reg_read(ohci, OHCI1394_CSRData);
 }
 
-static struct hpsb_host_operations ohci1394_ops = {
+static struct hpsb_host_driver ohci1394_driver = {
+	.name =			OHCI1394_DRIVER_NAME,
 	.get_rom =		ohci_get_rom,
 	.transmit_packet =	ohci_transmit,
 	.devctl =		ohci_devctl,
 	.hw_csr_reg =		ohci_hw_csr_reg,
 };
 
-static struct hpsb_host_driver *ohci1394_driver;
-
 
 
 /***********************************
@@ -2059,7 +1967,6 @@
 	struct hpsb_host *host;
 	struct ti_ohci *ohci;	/* shortcut to currently handled device */
 	unsigned long ohci_base;
-	int i;
 	
 	if (version_printed++ == 0)
 		PRINT_G(KERN_INFO, "%s", version);
@@ -2069,7 +1976,7 @@
 		        card_id_counter++);
         pci_set_master(dev);
 
-	host = hpsb_alloc_host(ohci1394_driver, sizeof(struct ti_ohci));
+	host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci));
 	if (!host) FAIL(-ENOMEM, "Failed to allocate host structure");
 
 	ohci = host->hostdata;
@@ -2151,68 +2058,78 @@
 
 	ohci->init_state = OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE;
 	/* AR DMA request context allocation */
-	ohci->ar_req_context = 
-		alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC,
-				  AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
-				  OHCI1394_AsReqRcvContextBase);
-
-	if (ohci->ar_req_context == NULL)
+	if (alloc_dma_rcv_ctx(ohci, &ohci->ar_req_context,
+			      DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC,
+			      AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
+			      OHCI1394_AsReqRcvContextBase) < 0)
 		FAIL(-ENOMEM, "Failed to allocate AR Req context");
 
 	/* AR DMA response context allocation */
-	ohci->ar_resp_context = 
-		alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC,
-				  AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
-				  OHCI1394_AsRspRcvContextBase);
-	
-	if (ohci->ar_resp_context == NULL)
+	if (alloc_dma_rcv_ctx(ohci, &ohci->ar_resp_context,
+			      DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC,
+			      AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
+			      OHCI1394_AsRspRcvContextBase) < 0)
 		FAIL(-ENOMEM, "Failed to allocate AR Resp context");
 
 	/* AT DMA request context */
-	ohci->at_req_context = 
-		alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC,
-				  OHCI1394_AsReqTrContextBase);
-	
-	if (ohci->at_req_context == NULL)
+	if (alloc_dma_trm_ctx(ohci, &ohci->at_req_context,
+			      DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC,
+			      OHCI1394_AsReqTrContextBase) < 0)
 		FAIL(-ENOMEM, "Failed to allocate AT Req context");
 
 	/* AT DMA response context */
-	ohci->at_resp_context = 
-		alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC,
-				  OHCI1394_AsRspTrContextBase);
-	
-	if (ohci->at_resp_context == NULL)
+	if (alloc_dma_trm_ctx(ohci, &ohci->at_resp_context,
+			      DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC,
+			      OHCI1394_AsRspTrContextBase) < 0)
 		FAIL(-ENOMEM, "Failed to allocate AT Resp context");
 
-	ohci->ir_ctx_usage = 0;
-	ohci->it_ctx_usage = 0;
-	
-	/* IR DMA context */
-	ohci->ir_context =
-		alloc_dma_rcv_ctx(ohci, DMA_CTX_ISO, 0, IR_NUM_DESC,
-				  IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
-				  OHCI1394_IsoRcvContextBase);
+	/* Start off with a soft reset, to clear everything to a sane
+	 * state. */
+	ohci_soft_reset(ohci);
 
-	if (ohci->ir_context == NULL)
-		FAIL(-ENOMEM, "Failed to allocate IR context");
+	/* Now enable LPS, which we need in order to start accessing
+	 * most of the registers.  In fact, on some cards (ALI M5251),
+	 * accessing registers in the SClk domain without LPS enabled
+	 * will lock up the machine.  Wait 50msec to make sure we have
+	 * full link enabled.  */
+	reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
+	mdelay(50);
 
-	
-	/* IT DMA context allocation */
-	ohci->it_context =
-		alloc_dma_trm_ctx(ohci, DMA_CTX_ISO, 0, IT_NUM_DESC,
-				  OHCI1394_IsoXmitContextBase);
+	/* Determine the number of available IR and IT contexts. */
+	ohci->nb_iso_rcv_ctx =
+		get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
+	DBGMSG(ohci->id, "%d iso receive contexts available",
+	       ohci->nb_iso_rcv_ctx);
 
-	if (ohci->it_context == NULL)
-		FAIL(-ENOMEM, "Failed to allocate IT context");
+	ohci->nb_iso_xmit_ctx =
+		get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
+	DBGMSG(ohci->id, "%d iso transmit contexts available",
+	       ohci->nb_iso_xmit_ctx);
+
+	/* Set the usage bits for non-existent contexts so they can't
+	 * be allocated */
+	ohci->ir_ctx_usage = ~0 << ohci->nb_iso_rcv_ctx;
+	ohci->it_ctx_usage = ~0 << ohci->nb_iso_xmit_ctx;
 
+	INIT_LIST_HEAD(&ohci->iso_tasklet_list);
+	spin_lock_init(&ohci->iso_tasklet_list_lock);
 	ohci->ISO_channel_usage = 0;
         spin_lock_init(&ohci->IR_channel_lock);
 
-	for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
-		ohci->irq_hooks[i].irq_handler = NULL;
-		ohci->irq_hooks[i].data = NULL;
-	}
+	/* IR DMA context */
+	if (alloc_dma_rcv_ctx(ohci, &ohci->ir_context,
+			      DMA_CTX_ISO, 0, IR_NUM_DESC,
+			      IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
+			      OHCI1394_IsoRcvContextBase) < 0)
+		FAIL(-ENOMEM, "Failed to allocate IR context");
+
 	
+	/* IT DMA context allocation */
+	if (alloc_dma_trm_ctx(ohci, &ohci->it_context,
+			      DMA_CTX_ISO, 0, IT_NUM_DESC,
+			      OHCI1394_IsoXmitContextBase) < 0)
+		FAIL(-ENOMEM, "Failed to allocate IT context");
+
 	if (request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ,
 			 OHCI1394_DRIVER_NAME, ohci))
 		FAIL(-ENOMEM, "Failed to allocate shared interrupt %d", dev->irq);
@@ -2355,65 +2272,69 @@
 	if (msg) PRINT(KERN_ERR, ohci->id, "%s: dma prg stopped", msg);
 }
 
-static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci,
-					  quadlet_t isoRecvEvent, 
-					  quadlet_t isoXmitEvent)
+void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, int type,
+			       void (*func)(unsigned long), unsigned long data)
 {
-	int i;
-	for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
-		if(ohci->irq_hooks[i].irq_handler != NULL) {
-			ohci->irq_hooks[i].irq_handler(ohci->id, isoRecvEvent, isoXmitEvent,
-						       ohci->irq_hooks[i].data);
-		}
-	}
+	tasklet_init(&tasklet->tasklet, func, data);
+	tasklet->type = type;
+	/* We init the tasklet->link field, so we can list_del() it
+	 * without worrying wether it was added to the list or not. */
+	INIT_LIST_HEAD(&tasklet->link);
 }
 
-int ohci1394_hook_irq(struct ti_ohci *ohci,
-		      void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
-		      void *data)
+int ohci1394_register_iso_tasklet(struct ti_ohci *ohci,
+				  struct ohci1394_iso_tasklet *tasklet)
 {
-	int i;
-	
-	/* find a free slot */
-	for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
-		if(ohci->irq_hooks[i].irq_handler == NULL)
-			break;
+	unsigned long flags, *usage;
+	int n, i, r = -EBUSY;
+
+	if (tasklet->type == OHCI_ISO_TRANSMIT) {
+		n = ohci->nb_iso_xmit_ctx;
+		usage = &ohci->it_ctx_usage;
+	}
+	else {
+		n = ohci->nb_iso_rcv_ctx;
+		usage = &ohci->ir_ctx_usage;
 	}
 
-	if(i >= OHCI1394_MAX_IRQ_HOOKS)
-		return -EBUSY;
+	spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
 
-	ohci->irq_hooks[i].irq_handler = irq_handler;
-	ohci->irq_hooks[i].data = data;
+	for (i = 0; i < n; i++)
+		if (!test_and_set_bit(i, usage)) {
+			tasklet->context = i;
+			list_add_tail(&tasklet->link, &ohci->iso_tasklet_list);
+			r = 0;
+			break;
+		}
 
-	/* ohci1394 will never be unloaded while an IRQ hook is
-	   in use, because the user must reference this symbol */
+	spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
 	
-	return 0;
+	return r;
 }
 
-void ohci1394_unhook_irq(struct ti_ohci *ohci,
-			 void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
-			 void *data)
+void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
+				     struct ohci1394_iso_tasklet *tasklet)
 {
-	int i;
-	
-	for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
-		if( (ohci->irq_hooks[i].irq_handler == irq_handler) &&
-		    (ohci->irq_hooks[i].data == data) )
-			break;
-	}
-	
-	if(i < OHCI1394_MAX_IRQ_HOOKS) {
-		ohci->irq_hooks[i].irq_handler = NULL;
-		ohci->irq_hooks[i].data = NULL;
-	}
+	unsigned long flags;
+
+	tasklet_kill(&tasklet->tasklet);
+
+	spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
+
+	if (tasklet->type == OHCI_ISO_TRANSMIT)
+		clear_bit(tasklet->context, &ohci->it_ctx_usage);
+	else
+		clear_bit(tasklet->context, &ohci->ir_ctx_usage);
+
+	list_del(&tasklet->link);
+
+	spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
 }
 
 EXPORT_SYMBOL(ohci1394_stop_context);
-EXPORT_SYMBOL(ohci1394_hook_irq);
-EXPORT_SYMBOL(ohci1394_unhook_irq);
-
+EXPORT_SYMBOL(ohci1394_init_iso_tasklet);
+EXPORT_SYMBOL(ohci1394_register_iso_tasklet);
+EXPORT_SYMBOL(ohci1394_unregister_iso_tasklet);
 
 
 /***********************************
@@ -2427,27 +2348,11 @@
 static void __exit ohci1394_cleanup (void)
 {
 	pci_unregister_driver(&ohci1394_pci_driver);
-	hpsb_unregister_lowlevel(ohci1394_driver);
 }
 
 static int __init ohci1394_init(void)
 {
-	int ret;
-
-	ohci1394_driver = hpsb_register_lowlevel(&ohci1394_ops,
-						 OHCI1394_DRIVER_NAME);
-	if (!ohci1394_driver) {
-		PRINT_G(KERN_ERR, "hpsb_register_lowlevel failed");
-		return -ENOMEM;
-	}
-
-	ret = pci_module_init(&ohci1394_pci_driver);
-	if (ret < 0) {
-		PRINT_G(KERN_ERR, "pci_module_init failed");
-		hpsb_unregister_lowlevel(ohci1394_driver);
-		return ret;
-	}
-	return ret;
+	return pci_module_init(&ohci1394_pci_driver);
 }
 
 module_init(ohci1394_init);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)