patch-2.3.40 linux/drivers/ieee1394/aic5800.c

Next file: linux/drivers/ieee1394/aic5800.h
Previous file: linux/drivers/ieee1394/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.39/linux/drivers/ieee1394/aic5800.c linux/drivers/ieee1394/aic5800.c
@@ -0,0 +1,897 @@
+/*
+ * aic5800.c - Adaptec AIC-5800 PCI-IEEE1394 chip driver
+ * Copyright (C)1999 Emanuel Pirker <epirker@edu.uni-klu.ac.at>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/delay.h>
+
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "ieee1394.h"
+#include "aic5800.h"
+
+
+
+/// print general (card independent) information 
+#define PRINT_G(level, fmt, args...) printk(level "aic5800: " fmt "\n" , ## args)
+/// print card specific information 
+#define PRINT(level, card, fmt, args...) printk(level "aic5800-%d: " fmt "\n" , card , ## args)
+
+/// card array 
+static struct aic5800 cards[MAX_AIC5800_CARDS];
+/// holds the number of installed aic5800 cards
+static int num_of_cards = 0;
+
+static int add_card(struct pci_dev *dev);
+static void remove_card(struct aic5800 *aic);
+static int init_driver(void);
+
+
+/*****************************************************************
+ * Auxiliary functions needed to read the EEPROM
+ * Daniel Minitti
+ *****************************************************************/
+#define SEEPDOUT        0x1
+#define SEEPDIN 0x02
+#define SEEPSK  0x04
+#define SEEPCS  0x08
+#define SEEPCYC 0x10
+#define SEEPBUSY        0x20
+
+#define CLOCK_PULSE() {\
+        int cnt=200;\
+        while(cnt-->0 && reg_read(aic, misc_SEEPCTL) & SEEPBUSY);\
+        if (reg_read(aic, misc_SEEPCTL) & SEEPBUSY) printk("BUSY ");\
+        }
+
+static inline unsigned short read_seeprom_word(struct aic5800 *aic,
+                                               int offset) 
+{
+        int i;
+        unsigned char temp;
+        unsigned char read_cmd[3] = {1,1,0};
+        unsigned short rd;
+
+        // send chip select for one clock cycle.
+        reg_write(aic, misc_SEEPCTL, SEEPSK|SEEPCS);
+        CLOCK_PULSE();
+
+        // write start bit (1) & READ op-code (10b)
+        for (i=0; i<sizeof(read_cmd); i++) {
+                temp = SEEPCS | SEEPCYC | read_cmd[i];
+                reg_write(aic, misc_SEEPCTL, temp);
+                CLOCK_PULSE();
+                temp = temp ^ SEEPSK;
+                reg_write(aic, misc_SEEPCTL, temp);
+                CLOCK_PULSE();
+        }
+        // write 8 bit address (MSB --> LSB)
+        for (i=7; i>=0; i--) {
+                temp = offset;
+                temp = (temp >> i) & 1;
+                temp = SEEPCS | SEEPCYC | temp;
+                reg_write(aic, misc_SEEPCTL, temp);
+                CLOCK_PULSE();
+                temp = temp ^ SEEPSK;
+                reg_write(aic, misc_SEEPCTL, temp);
+                CLOCK_PULSE();
+        }
+        // read 16 bit (MSB --> LSB)
+        rd = 0;
+        for (i=0; i<=16; i++) {
+                temp = SEEPCS | SEEPCYC;
+                reg_write(aic, misc_SEEPCTL, temp);
+                CLOCK_PULSE();
+                temp = temp ^ SEEPSK;
+                rd = (rd << 1) | (unsigned short)((reg_read(aic, misc_SEEPCTL)
+& SEEPDIN)>>1);
+                reg_write(aic, misc_SEEPCTL, temp);
+                CLOCK_PULSE();
+        }
+
+        // reset chip select for the next command cycle
+        reg_write(aic, misc_SEEPCTL, SEEPCYC);
+        CLOCK_PULSE();
+        reg_write(aic, misc_SEEPCTL, SEEPCYC | SEEPSK);
+        CLOCK_PULSE();
+        reg_write(aic, misc_SEEPCTL, SEEPCYC);
+        CLOCK_PULSE();
+
+        reg_write(aic, misc_SEEPCTL, 0);
+        CLOCK_PULSE();
+
+        return rd;
+}
+
+#undef DEBUG_SEEPROM
+
+/** Read 64-bit GUID (Global Unique ID) from SEEPROM
+ *
+ * It works well on AHA-8945.
+ * On AHA-8920 it works well only on first time, It returns ffff... on
+ * the other times.
+ *****************************************************************/
+static unsigned long long read_guid(struct aic5800 *aic) 
+{
+        int i;
+        unsigned long long guid;
+
+#ifdef DEBUG_SEEPROM
+        printk("\n");
+        printk("SEEPCTL value = 0x%x\n", reg_read(aic, misc_SEEPCTL));
+#endif
+
+        /* read GUID */
+        guid = 0;
+        for (i=0x10; i<0x14; i++)
+                guid = (guid << 16) | read_seeprom_word(aic,i);
+
+#ifdef DEBUG_SEEPROM
+        for (i=0; i<3; i++)
+                printk("%x ", (unsigned int) read_seeprom_word(aic,i));
+        printk("\nGUID = ");
+        for (i=3; i>=0; i--)
+                printk("%x ", (unsigned int)(guid>>(16*i))&0xffff);
+
+        printk("\nSEEPCTL value = 0x%x\n", reg_read(aic, misc_SEEPCTL));
+#endif
+        return guid;
+}
+
+#undef CLOCK_PULSE()
+
+static int aic_detect(struct hpsb_host_template *tmpl)
+{
+        struct hpsb_host *host;
+        int i;
+
+        init_driver();
+
+        for (i = 0; i < num_of_cards; i++) {
+                host = hpsb_get_host(tmpl, 0);
+                if (host == NULL) {
+                        /* simply don't init more after out of mem */
+                        return i;
+                }
+                host->hostdata = &cards[i];
+                cards[i].host = host;
+        }
+
+        return num_of_cards;
+}
+
+static int aic_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
+{
+    struct aic5800 *aic = host->hostdata;
+    int retval = 0;
+    unsigned long flags;
+    struct hpsb_packet *packet, *lastpacket;
+
+    switch (cmd) {
+    case RESET_BUS:
+	reg_write(aic, misc_PhyControl, 0x00004140 );
+	break;
+
+    case GET_CYCLE_COUNTER:
+	arg = reg_read(aic, misc_CycleTimer);
+	break;
+	
+    case SET_CYCLE_COUNTER:
+	reg_write(aic, misc_CycleTimer, arg);
+	break;
+	
+    case SET_BUS_ID:
+	reg_clear_bits(aic, misc_NodeID, 0xFFC0);
+	reg_set_bits(aic, misc_NodeID, (arg<<6));
+	break;
+	
+    case ACT_CYCLE_MASTER:
+	if (arg) {
+	    /* enable cycleMaster */
+	    reg_set_bits(aic, misc_Control, 0x20000);
+	} else {
+	    /* disable cycleMaster */
+	    reg_clear_bits(aic, misc_Control, 0x20000);
+	};
+	break;
+	
+    case CANCEL_REQUESTS:
+	spin_lock_irqsave(&aic->async_queue_lock, flags);
+	/* stop any chip activity */
+	reg_write( aic, AT_ChannelControl, 0x80000000);
+        packet = aic->async_queue;
+	aic->async_queue = NULL;                
+	spin_unlock_irqrestore(&aic->async_queue_lock, flags);
+
+        while (packet != NULL) {
+            lastpacket = packet;
+            packet = packet->xnext;
+            hpsb_packet_sent(host, lastpacket, ACKX_ABORTED);
+        }
+
+	break;
+
+    case MODIFY_USAGE:
+        if (arg) {
+            MOD_INC_USE_COUNT;
+        } else {
+            MOD_DEC_USE_COUNT;
+        }
+        break;
+
+#if 0
+   case DEBUG_DUMPINFO:
+      PRINT(KERN_INFO, aic->id, AIC5800_DRIVER_NAME);
+      PRINT(KERN_INFO, aic->id, "  Register MMIO base: 0x%p\n", 
+	    aic->registers); 
+      PRINT(KERN_INFO, aic->id, "  NodeID: 0x%x\n", 
+	    reg_read(aic, misc_NodeID) ); 
+      PRINT(KERN_INFO,aic->id, "  #Intr: %lu  BusResets: %lu\n",
+	      aic->NumInterrupts, aic->NumBusResets); 
+      PRINT(KERN_INFO, aic->id, "  TxPackets: %lu    RxPackets: %lu\n",
+	      aic->TxPackets, aic->RxPackets); 
+      PRINT(KERN_INFO,aic->id, "  TxRdy: %lu  ATErr: %lu HdrErr: %lu TcodeErr: %lu SendRej: %lu\n",
+	    aic->TxRdy, aic->ATError, aic->HdrErr, 
+	    aic->TCodeErr, aic->SendRej); 
+      break;
+#endif
+	
+    default:
+	PRINT(KERN_ERR, aic->id, "unknown devctl command %d", cmd);
+	retval = -1;
+    }
+    
+    return retval;
+    
+}
+
+/** Initialize the host adapter chip and corresponding data
+    structures. We reset the chip, enable transmitter, receiver,
+    the physical DMA units, cycle timer, cycle source, reception
+    of selfid packets and initialize several other registers. */
+static int aic_initialize(struct hpsb_host *host)
+{
+    int i;
+    struct aic5800 *aic = host->hostdata;
+
+    /* Reset data structures */
+    aic->async_queue = NULL;
+    spin_lock_init(&aic->async_queue_lock);
+
+    /* Reset the chip */
+    reg_write( aic, misc_Reset, 0x37);
+    udelay(10); // FIXME
+    reg_write( aic, misc_Reset, 0);
+   
+    /* Enable Transmitter/Receiver, enable physDMA,
+     * enable CycleTimer, cycleSource */
+    reg_write( aic, misc_Control, 0x82050003);
+
+    /* Enable reception of SelfID packets */
+    reg_set_bits(aic, misc_PacketControl, 0x20);
+
+    reg_write(aic, AT_InterruptSelect, 0x00F0001);
+    reg_write(aic, AT_BranchSelect, 0x0100010);
+    reg_write(aic, AT_WaitSelect, 0x00F0001);
+    reg_write(aic, misc_ATRetries, reg_read(aic, misc_ATRetries) | 0x7);
+
+    /* initialize AR DMA */
+
+    /* unset run bit */
+    reg_write( aic, AR_ChannelControl, 0x80000000);
+	    
+    /* here we should have 0 iterations because of the code
+       in the DmaAR handler. However, to be sure we do it */
+    i = 0;
+    while (reg_read(aic, AR_ChannelStatus) & 0x400) {
+	i++;
+	if (i>100000) {
+	    PRINT(KERN_ERR, aic->id, 
+		  "Huh! Can't set AR_ChannelControl... card can not receive!");
+	    break;
+	}
+    }
+    
+    (aic->AR_program)->control = ( DMA_CMD_INPUTLAST | DMA_KEY_STREAM0 
+				   | DMA_INTR_ALWAYS | DMA_BRANCH_ALWAYS) 
+	+ AIC5800_ARFIFO_SIZE;
+    (aic->AR_program)->address = virt_to_bus(aic->rcv_page);
+    (aic->AR_program)->branchAddress = virt_to_bus(aic->AR_program);
+    (aic->AR_program)->status = AIC5800_ARFIFO_SIZE;
+    
+    (aic->AR_program+1)->control = DMA_CMD_STOP;
+    (aic->AR_program+1)->address = 0;
+    (aic->AR_program+1)->branchAddress = 0;
+    (aic->AR_program+1)->status = 0;
+    
+    reg_write( aic, AR_CommandPtr, (u32) virt_to_bus(aic->AR_program)); 
+    reg_write( aic, AR_ChannelControl, 0x80008000);
+   
+    /* Enable Interrupts */
+    reg_write(aic, misc_InterruptClear, 0xFFFFFFFF);
+    reg_write(aic, misc_InterruptMask, 0xFFFFFFFF);
+    /*reg_write(aic, misc_InterruptMask, 0x00F1F03F);*/
+
+    return 1;
+}
+
+static void aic_release(struct hpsb_host *host)
+{
+    struct aic5800 *aic;
+        
+    if (host != NULL) {
+	aic = host->hostdata;
+	remove_card(aic);
+    } 
+}
+
+/* This must be called with the async_queue_lock held. */
+static void send_next_async(struct aic5800 *aic)
+{
+    int i;
+    struct hpsb_packet *packet = aic->async_queue;
+
+    /* stop the channel program if it's still running */
+    reg_write( aic, AT_ChannelControl, 0x80000000);
+    
+    /* re-format packet header for AIC-5800 chip */
+    packet->header[1] = (packet->header[1] & 0xFFFF) | 
+	(packet->header[0] & 0xFFFF0000);
+    packet->header[0] = (packet->header[0] & 0xFFFF);
+
+#ifndef __BIG_ENDIAN
+    /* Packet must be byte-swapped in non-big-endian environments, 
+     * see AIC-5800 specification...
+     */
+    { u32 i;
+    for ( i = 0 ; i < packet->header_size/sizeof(u32) ; i++ ) 
+	packet->header[i] = cpu_to_be32( packet->header[i] ); 
+    for ( i = 0 ; i < packet->data_size/sizeof(u32) ; i++ ) 
+	packet->data[i] = cpu_to_be32( packet->data[i] ); 
+    }
+    
+#endif
+    
+    /* typically we use only a few iterations here */
+    i = 0;
+    while (reg_read(aic, AT_ChannelStatus) & 0x400) {
+	i++;
+	if (i>5000) {
+	    PRINT(KERN_ERR, aic->id, 
+		  "runaway loop 1 in send_next_async() - bailing out...");
+	    break;
+	};
+    };
+    
+    /* set data buffer address and packet length */
+    memset(aic->AT_program, 0, MAX_AT_PROGRAM_SIZE * sizeof(struct dma_cmd));
+    
+    if (packet->data_size) {
+	aic->AT_program[0].control = ( DMA_CMD_OUTPUTMORE | DMA_KEY_STREAM0 ) + 
+	    packet -> header_size;
+	aic->AT_program[0].address = virt_to_bus( packet->header );
+	aic->AT_program[1].control = ( DMA_CMD_OUTPUTLAST | DMA_KEY_STREAM0 
+				       | DMA_INTR_ALWAYS ) 
+	    + packet -> data_size;
+	aic->AT_program[1].address = virt_to_bus( packet->data );
+	
+	aic->AT_program[2].control = DMA_CMD_STOP;
+	
+    } else {
+	aic->AT_program[0].control = ( DMA_CMD_OUTPUTLAST | DMA_INTR_ALWAYS | 
+				       DMA_KEY_STREAM0 ) + 
+	    packet -> header_size;
+	aic->AT_program[0].address = virt_to_bus( packet->header );
+	
+	aic->AT_program[1].control = DMA_CMD_STOP;
+    };
+    
+    /* set program start address */
+    reg_write(aic, AT_CommandPtr, (unsigned int) virt_to_bus(aic->AT_program));
+
+    /* typically we use only a few iterations here */
+    i = 0;
+    while (reg_read(aic, AT_CommandPtr) != (unsigned int) 
+	   virt_to_bus(aic->AT_program)) {
+		i++;
+	if (i>5000) {
+	    PRINT(KERN_ERR, aic->id, 
+		  "runaway loop 2 in send_next_async() - bailing out...");
+	    break;
+	};
+    };
+     
+    /* run program */
+    reg_write( aic, AT_ChannelControl, 0x80008000);
+}
+
+
+static int aic_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
+{
+    struct aic5800 *aic = host->hostdata;
+    struct hpsb_packet *p;
+    unsigned long flags;
+
+    if (packet->data_size >= 4096) {
+	PRINT(KERN_ERR, aic->id, "transmit packet data too big (%d)",
+	      packet->data_size);
+	return 0;
+    }
+
+    packet->xnext = NULL;
+    
+    spin_lock_irqsave(&aic->async_queue_lock, flags);
+
+    if (aic->async_queue == NULL) {
+	aic->async_queue = packet;
+	send_next_async(aic);
+    } else {
+	p = aic->async_queue;
+	while (p->xnext != NULL) {
+	    p = p->xnext;
+	}
+	
+	p->xnext = packet;
+    }
+    
+    spin_unlock_irqrestore(&aic->async_queue_lock, flags);
+
+    return 1;
+}
+
+static int get_phy_reg(struct aic5800 *aic, int addr)
+{
+        int retval;
+        int i = 0;
+
+	/* sanity check */
+        if (addr > 15) {
+                PRINT(KERN_ERR, aic->id, __FUNCTION__
+                      ": PHY register address %d out of range", addr);
+                return -1;
+        }
+
+	/* request data from PHY */
+        reg_write(aic, misc_PhyControl, LINK_PHY_READ | LINK_PHY_ADDR(addr));
+
+	/* read data from PhyControl register */
+	/* note that we have to wait until the register is updated */
+        do {
+                retval = reg_read(aic, misc_PhyControl);
+
+                if (i > 10000) {
+                        PRINT(KERN_ERR, aic->id, __FUNCTION__ 
+                              ": runaway loop, aborting");
+                        retval = -1;
+                        break;
+                }
+                i++;
+        } while ((retval & 0xf000000) != LINK_PHY_RADDR(addr));
+
+	/* we don't want a PhyInt interrupt */
+        reg_write(aic, misc_InterruptClear, INT_PhyInt);
+
+        if (retval != -1) {
+                return ((retval & 0xff0000)>>16);
+        } else {
+                return -1;
+        }
+}
+
+static quadlet_t generate_own_selfid(struct aic5800 *aic, int phyid)
+{
+    quadlet_t lsid;
+    char phyreg[7];
+    int i;
+
+    for (i = 1; i < 7; i++) {
+	phyreg[i] = get_phy_reg(aic, i);
+    }
+
+    /* Standard PHY register map */
+    lsid = 0x80400000 | (phyid << 24);
+    lsid |= (phyreg[1] & 0x3f) << 16; /* gap count */
+    lsid |= (phyreg[2] & 0xc0) << 8; /* max speed */
+    lsid |= (phyreg[6] & 0x01) << 11; /* contender (phy dep) */
+    lsid |= (phyreg[6] & 0x10) >> 3; /* initiated reset */
+
+    for (i = 0; i < (phyreg[2] & 0x1f); i++) { /* ports */
+	if (phyreg[3 + i] & 0x4) {
+	    lsid |= (((phyreg[3 + i] & 0x8) | 0x10) >> 3)
+						    << (6 - i*2);
+	} else {
+	    lsid |= 1 << (6 - i*2);
+	}
+    }
+
+    return lsid;
+};
+
+/* moved out to make interrupt routine more readable */
+inline static void handle_selfid(struct aic5800 *aic, struct hpsb_host *host,
+                          int phyid, int isroot, size_t size)
+{
+    quadlet_t *q = aic->rcv_page;
+    quadlet_t lsid;
+
+    /* we need our own self-id packet */
+    lsid = generate_own_selfid(aic, phyid);
+
+    /* unconnected state? only begin and end marker in rcv_page */
+    if (size==8) {
+	hpsb_selfid_received(host, lsid);
+    }
+
+    /* process buffer... AIC's FIFO often contains some strangenesses */
+    while (size > 0) {
+	if (q[0] == 0xe0) {
+	    /* marker */
+	    q += 1;
+	    size -= 4;
+	    continue;
+	};
+	if (q[0] == 0x1) {
+	    /* marker */
+	    q += 1;
+	    size -= 4;
+	    break;
+	};
+
+	if (q[0] == ~q[1]) {
+	    /* correct self-id */
+
+	    if ((q[0] & 0x3f800000) == ((phyid + 1) << 24)) {
+		/* its our turn now! */
+		//PRINT(KERN_INFO, 
+		//      aic->id, "selfid packet 0x%x included", lsid);
+		
+		hpsb_selfid_received(host, lsid);
+	    }
+
+	    //PRINT(KERN_INFO, aic->id, "selfid packet 0x%x rcvd", q[0]);
+	    hpsb_selfid_received(host, q[0]);
+	    q += 2;
+	    size -= 8;
+	    continue;
+	};
+    }
+
+    /* if we are root, our self-id packet is last */
+    if (isroot && phyid != 0) {
+	hpsb_selfid_received(host, lsid);
+    }
+
+    hpsb_selfid_complete(host, phyid, isroot);
+}
+
+static void aic_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct aic5800 *aic = (struct aic5800 *)dev_id;
+    struct hpsb_host *host = aic->host;
+    quadlet_t *q = aic->rcv_page;
+ 
+    int phyid = -1, isroot = 0;
+
+    u32 interruptEvent = reg_read(aic, misc_InterruptEvents);
+    reg_write(aic, misc_InterruptClear, interruptEvent);
+
+    //printk("InterruptEvent 0x%x\n", interruptEvent);
+    if ( (interruptEvent & 0x3f) == 0x3f ) {
+	PRINT(KERN_INFO, aic->id, "Dma Engine Error");
+    };
+
+    if ( interruptEvent & INT_DmaAT ) {
+	if (aic->AT_program[0].status & 0xFFFF) 
+	    PRINT(KERN_INFO, aic->id, "AT: could not transfer %d bytes", 
+		  aic->AT_program[0].status & 0xFFFF);
+    };
+
+    if ( interruptEvent & INT_PhyInt) {
+	PRINT(KERN_INFO, aic->id, "PhyInt");
+    };
+
+    if ( interruptEvent & INT_DmaAR ) {
+	int rcv_bytes;
+	int i;
+
+	/* we calculate the number of received bytes from the
+	   residual count field */
+	rcv_bytes = AIC5800_ARFIFO_SIZE - (aic->AR_program->status & 0xFFFF);
+
+	//PRINT(KERN_INFO, aic->id, "AR_status 0x%x, %d bytes read", aic->AR_program->status, rcv_bytes);
+
+	if ((aic->AR_program->status & 0x84000000) 
+	    && (aic->AR_program->status & 0xFFFF) >= 8 ) {
+
+#ifndef __BIG_ENDIAN 
+	    /* we have to do byte-swapping on non-bigendian architectures */
+	    for (i=0; i< (rcv_bytes / sizeof(quadlet_t)); i++) {
+		*q = be32_to_cpu(*q);
+		q++;
+	    };
+	    q = aic->rcv_page;
+#endif
+
+	    if (*q == 0xe0) {
+		phyid = reg_read(aic, misc_NodeID);
+		isroot = phyid & 0x800000;
+		phyid = phyid & 0x3F;
+		handle_selfid(aic, host, phyid, isroot, rcv_bytes);
+	    } else {
+		hpsb_packet_received(host, aic->rcv_page, rcv_bytes);
+	    };
+	} else {
+	    PRINT(KERN_ERR, aic->id, 
+		  "AR DMA program status value 0x%x is incorrect!",
+		  aic->AR_program->status);
+	};
+    }
+    if ( interruptEvent & INT_BusReset ) {
+	PRINT(KERN_INFO, aic->id, "bus reset occured");
+	if (!host->in_bus_reset) {
+	    hpsb_bus_reset(host);
+	}
+	reg_set_bits(aic, misc_Control, 0x1);
+	aic->NumBusResets++;
+    };
+
+    if (interruptEvent & INT_RcvData ) { 
+	aic->RxPackets++;
+    };
+
+    if (interruptEvent & INT_TxRdy) { 
+	/* async packet sent - transmitter ready */
+	u32 ack;
+	struct hpsb_packet *packet;
+
+	if (aic->async_queue) {
+
+	    spin_lock(&aic->async_queue_lock);
+
+	    
+	    ack = reg_read(aic, AT_ChannelStatus) & 0xF;
+	    
+	    packet = aic->async_queue;
+	    aic->async_queue = packet->xnext;
+	    
+	    if (aic->async_queue != NULL) {
+		send_next_async(aic);
+	    }
+	    spin_unlock(&aic->async_queue_lock);
+	    PRINT(KERN_INFO,aic->id,"packet sent with ack code %d",ack);
+	    hpsb_packet_sent(host, packet, ack);	    
+	} // else 
+	    //PRINT(KERN_INFO,aic->id,"packet sent without async_queue (self-id?)");
+
+	aic->TxRdy++;
+    };
+    if (interruptEvent & INT_ATError ) { 
+	PRINT(KERN_INFO,aic->id,"ATError");
+	aic->ATError++;
+    };
+    if (interruptEvent & INT_SendRej ) { 
+	aic->SendRej++;
+    };
+    if (interruptEvent & INT_HdrErr ) { 
+	aic->HdrErr++;
+    };
+    if (interruptEvent & INT_TCodeErr ) { 
+	PRINT(KERN_INFO,aic->id,"TCodeErr");
+	aic->TCodeErr++;
+    };
+
+    aic->NumInterrupts++;
+
+}
+
+inline static void * quadquadalign(void *buf)
+{
+  if ((unsigned int) buf % 0x10 != 0) {
+	return (void *)(((unsigned int)buf + 0x10) & 0xFFFFFFF0);
+	} else {
+	return buf;
+  };
+}
+
+static int add_card(struct pci_dev *dev)
+{
+#define FAIL(fmt, args...) \
+        PRINT_G(KERN_ERR, fmt , ## args); \
+        num_of_cards--; \
+        remove_card(aic); \
+        return 1;
+
+        struct aic5800 *aic; /* shortcut to currently handled device */
+        unsigned long page;
+
+        if (num_of_cards == MAX_AIC5800_CARDS) {
+                PRINT_G(KERN_WARNING, "cannot handle more than %d cards.  "
+                        "Adjust MAX_AIC5800_CARDS in aic5800.h.",
+                        MAX_AIC5800_CARDS);
+                return 1;
+        }
+
+        aic = &cards[num_of_cards++];
+
+        aic->id = num_of_cards-1;
+        aic->dev = dev;
+
+        if (!request_irq(dev->irq, aic_irq_handler, SA_SHIRQ,
+                         AIC5800_DRIVER_NAME, aic)) {
+                PRINT(KERN_INFO, aic->id, "allocated interrupt %d", dev->irq);
+        } else {
+                FAIL("failed to allocate shared interrupt %d", dev->irq);
+        }
+
+        page = get_free_page(GFP_KERNEL);
+        if (page != 0) {
+                aic->rcv_page = phys_to_virt(page);
+        } else {
+                FAIL("failed to allocate receive buffer");
+        }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+        aic->registers = ioremap_nocache(dev->base_address[0],
+                                          AIC5800_REGSPACE_SIZE);
+#else
+        aic->registers = ioremap_nocache(dev->resource[0].start,
+                                          AIC5800_REGSPACE_SIZE);
+#endif
+
+        if (aic->registers == NULL) {
+                FAIL("failed to remap registers - card not accessible");
+        }
+
+        PRINT(KERN_INFO, aic->id, "remapped memory space reg 0x%p", 
+	      aic->registers);
+
+        aic->pbuf = kmalloc(AIC5800_PBUF_SIZE, GFP_KERNEL);
+
+        if (!aic->pbuf) {
+                FAIL("failed to allocate program buffer");
+        }
+
+	aic->AT_program = quadquadalign(aic->pbuf);
+	aic->AT_program[2].control = DMA_CMD_STOP;
+
+	aic->AR_program = aic->AT_program + MAX_AT_PROGRAM_SIZE * 
+	    sizeof(struct dma_cmd);
+
+        return 0;
+#undef FAIL
+}
+
+static void remove_card(struct aic5800 *aic)
+{
+    /* Disable interrupts of this controller */
+    reg_write(aic, misc_InterruptMask, 0);
+    /* Free AR buffer */
+    free_page(virt_to_phys(aic->rcv_page));
+    /* Free channel program buffer */
+    kfree(aic->pbuf);
+    /* Free interrupt request */
+    free_irq(aic->dev->irq, aic);
+    /* Unmap register space */
+    iounmap(aic->registers);
+}
+
+static int init_driver()
+{
+        struct pci_dev *dev = NULL;
+        int success = 0;
+
+        if (num_of_cards) {
+                PRINT_G(KERN_DEBUG, __PRETTY_FUNCTION__ " called again");
+                return 0;
+        }
+
+        while ((dev = pci_find_device(PCI_VENDOR_ID_ADAPTEC,
+                                      PCI_DEVICE_ID_ADAPTEC_5800, dev)) 
+               != NULL) {
+                if (add_card(dev) == 0) {
+                        success = 1;
+                }
+        }
+
+        if (success == 0) {
+                PRINT_G(KERN_WARNING, "no operable AIC-5800 based cards found");
+                return -ENXIO;
+        }
+
+        return 0;
+}
+
+/** Prepare our local CSR ROM. This is done by using the software-stored
+    ROM and inserting the GUID read from the EEPROM */
+static size_t get_aic_rom(struct hpsb_host *host, const quadlet_t **ptr)
+{
+    struct aic5800 *aic = host -> hostdata;
+    u64 guid;
+
+    /* Read the GUID from the card's EEPROM and put it into the right
+       place in the CONFIG ROM. */
+    guid = read_guid(aic);
+    aic5800_csr_rom[15] = (u32) (guid >> 32);
+    aic5800_csr_rom[16] = (u32) (guid & 0xFFFF);
+
+    *ptr = aic5800_csr_rom;
+
+    return sizeof(aic5800_csr_rom);
+}
+
+struct hpsb_host_template *get_aic_template(void)
+{
+        static struct hpsb_host_template tmpl;
+        static int initialized = 0;
+
+        if (!initialized) {
+                /* Initialize by field names so that a template structure
+                 * reorganization does not influence this code. */
+                tmpl.name = "aic5800";
+                
+                tmpl.detect_hosts = aic_detect;
+                tmpl.initialize_host = aic_initialize;
+                tmpl.release_host = aic_release;
+                tmpl.get_rom = get_aic_rom;
+                tmpl.transmit_packet = aic_transmit;
+                tmpl.devctl = aic_devctl;
+
+                initialized = 1;
+        }
+
+        return &tmpl;
+}
+
+#ifdef MODULE
+
+/* EXPORT_NO_SYMBOLS; */
+
+MODULE_AUTHOR("Emanuel Pirker <epirker@edu.uni-klu.ac.at>");
+MODULE_DESCRIPTION("Adaptec AIC-5800 PCI-to-IEEE1394 controller driver");
+MODULE_SUPPORTED_DEVICE("aic5800");
+
+void cleanup_module(void)
+{
+        hpsb_unregister_lowlevel(get_aic_template());
+        PRINT_G(KERN_INFO, "removed " AIC5800_DRIVER_NAME " module");
+}
+
+int init_module(void)
+{
+        if (hpsb_register_lowlevel(get_aic_template())) {
+                PRINT_G(KERN_ERR, "registering failed");
+                return -ENXIO;
+        } else {
+                return 0;
+        }
+}
+
+#endif /* MODULE */

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