patch-2.1.23 linux/arch/sparc/ap1000/bnet.c
Next file: linux/arch/sparc/ap1000/dma.c
Previous file: linux/arch/sparc/ap1000/approm.c
Back to the patch index
Back to the overall index
- Lines: 1205
- Date:
Sun Jan 26 12:07:06 1997
- Orig file:
v2.1.22/linux/arch/sparc/ap1000/bnet.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.1.22/linux/arch/sparc/ap1000/bnet.c linux/arch/sparc/ap1000/bnet.c
@@ -0,0 +1,1204 @@
+ /*
+ * Copyright 1996 The Australian National University.
+ * Copyright 1996 Fujitsu Laboratories Limited
+ *
+ * This software may be distributed under the terms of the Gnu
+ * Public License version 2 or later
+ */
+/* routines to control the AP1000 bif interface. This is the interface
+ used to talk to the front end processor */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <asm/ap1000/apservice.h>
+#include <asm/ap1000/apreg.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/irq.h>
+#include <linux/skbuff.h>
+
+#define NET_DEBUG 0
+
+#define DUMMY_MSG_LEN 100
+#define DUMMY_MSG_WAIT 30
+
+#define MAX_CELLS 128
+
+#define HAVE_BIF() (BIF_IN(BIF_SDCSR) & BIF_SDCSR_BG)
+#define BIF_BUSY() (BIF_IN(BIF_SDCSR) & BIF_SDCSR_BB)
+
+#define SNET_ARBITRATION 0
+#define TOKEN_ARBITRATION 1
+
+#define DEBUG(x)
+
+#if TOKEN_ARBITRATION
+static int have_token = 0;
+#endif
+
+extern struct cap_init cap_init;
+
+static int interrupt_driven = 0;
+static int use_dma = 0;
+struct pt_regs *bif_pt_regs = NULL;
+enum dma_state {DMA_IDLE,DMA_INCOMING,DMA_OUTGOING};
+static enum dma_state dma_state = DMA_IDLE;
+
+static int net_started = 0;
+static int waiting_for_bif = 0;
+static int queue_length = 0;
+
+static int drop_ip_packets = 0;
+
+#define DMA_THRESHOLD 64
+
+static struct cap_request bread_req;
+
+int tnet_ip_enabled = 1;
+
+#define BIF_DATA_WAITING() (BIF_IN(BIF_SDCSR) & BIF_SDCSR_RB)
+
+#define ROUND4(x) (((x) + 3) & -4)
+
+static void bif_intr_receive(struct cap_request *req1);
+
+
+/* read some data from the bif */
+void read_bif(char *buf,int size)
+{
+ unsigned *ibuf = (unsigned *)buf;
+ unsigned avail;
+
+ DEBUG(("|read_bif %d\n",size));
+
+ if (dma_state != DMA_IDLE) ap_dma_wait(DMA_CH2);
+
+ size = (size+3) >> 2;
+
+ while (size > 4) {
+ while (!(avail=(BIF_IN(BIF_SDCSR) >> BIF_SDCSR_RB_SHIFT) & 7))
+ ;
+ if (avail & 4) {
+ ibuf[0] = BIF_IN(BIF_DATA);
+ ibuf[1] = BIF_IN(BIF_DATA);
+ ibuf[2] = BIF_IN(BIF_DATA);
+ ibuf[3] = BIF_IN(BIF_DATA);
+ size -= 4; ibuf += 4;
+ continue;
+ }
+
+ if (avail & 2) {
+ ibuf[0] = BIF_IN(BIF_DATA);
+ ibuf[1] = BIF_IN(BIF_DATA);
+ size -= 2; ibuf += 2;
+ continue;
+ }
+ *ibuf++ = BIF_IN(BIF_DATA);
+ size--;
+ }
+
+ while (size--) {
+ while (!(BIF_IN(BIF_SDCSR) & BIF_SDCSR_RB)) ;
+ *ibuf++ = BIF_IN(BIF_DATA);
+ }
+
+ DEBUG(("|read bif done\n"));
+}
+
+/* throw out some data from the bif. This is usually called when we
+ don't have the resources to handle it immediately */
+void bif_toss(int size)
+{
+ unsigned flags;
+ save_flags(flags); cli();
+
+ DEBUG(("|bif toss %d\n",size));
+
+ while (size>0) {
+ while (!BIF_DATA_WAITING());
+ BIF_IN(BIF_DATA);
+ size -= 4;
+ }
+
+ DEBUG(("|bif toss done\n"));
+
+ restore_flags(flags);
+}
+
+
+static void bif_reset_interrupts(void)
+{
+ BIF_OUT(BIF_INTR,AP_INTR_WENABLE << BIF_INTR_GET_SH);
+ BIF_OUT(BIF_INTR,AP_INTR_WENABLE << BIF_INTR_HEADER_SH);
+}
+
+static void bif_mask_interrupts(void)
+{
+ BIF_OUT(BIF_INTR,(AP_INTR_MASK|AP_INTR_WENABLE) << BIF_INTR_GET_SH);
+ BIF_OUT(BIF_INTR,(AP_INTR_MASK|AP_INTR_WENABLE) << BIF_INTR_HEADER_SH);
+}
+
+static void attn_enable(void)
+{
+ BIF_OUT(BIF_INTR,AP_INTR_WENABLE << BIF_INTR_ATTN_SH);
+}
+
+static void attn_mask(void)
+{
+ BIF_OUT(BIF_INTR,(AP_INTR_MASK|AP_INTR_WENABLE) << BIF_INTR_ATTN_SH);
+}
+
+
+void ap_bif_status(void)
+{
+ static int bif_sdcsr;
+ static int bif_intr;
+ static int bif_mhocr;
+ static int bif_x0sk;
+ static int bif_xsk;
+ static int bif_xsz;
+ static int bif_y0sk;
+ static int bif_ysk;
+ static int bif_ysz;
+ static int bif_cx0sk;
+ static int bif_cxsk;
+ static int bif_cxsz;
+ static int bif_cy0sk;
+ static int bif_cysk;
+ static int bif_cysz;
+ static int bif_ttl;
+ static int bif_cttl;
+ static int bif_header;
+
+ bif_sdcsr = BIF_IN(BIF_SDCSR);
+ bif_intr = BIF_IN(BIF_INTR);
+ bif_mhocr = BIF_IN(BIF_MHOCR);
+
+ bif_x0sk = BIF_IN(BIF_X0SK);
+ bif_xsk = BIF_IN(BIF_XSK);
+ bif_xsz = BIF_IN(BIF_XSZ);
+ bif_y0sk = BIF_IN(BIF_Y0SK);
+ bif_ysk = BIF_IN(BIF_YSK);
+ bif_ysz = BIF_IN(BIF_YSZ);
+
+ bif_cx0sk = BIF_IN(BIF_CX0SK);
+ bif_cxsk = BIF_IN(BIF_CXSK);
+ bif_cxsz = BIF_IN(BIF_CXSZ);
+ bif_cy0sk = BIF_IN(BIF_CY0SK);
+ bif_cysk = BIF_IN(BIF_CYSK);
+ bif_cysz = BIF_IN(BIF_CYSZ);
+
+ bif_ttl = BIF_IN(BIF_TTL);
+ bif_cttl = BIF_IN(BIF_CTTL);
+ bif_header = BIF_IN(BIF_HEADER);
+
+ printk("|\t***** BIF REG. *****\n");
+ printk("|\tBIF_SDCSR = %08x ", bif_sdcsr);
+ if(bif_sdcsr & BIF_SDCSR_CN) printk("|<BUS DISCONNECT>");
+ if(bif_sdcsr & BIF_SDCSR_FN) printk("|<SC/GA ENABLE>");
+ if(bif_sdcsr & BIF_SDCSR_DE) printk("|<DMA ENABLE>");
+ if(bif_sdcsr & BIF_SDCSR_DR) printk("|<GATHER>");
+ if(bif_sdcsr & BIF_SDCSR_BB) printk("|<BUS BSY>");
+ if(bif_sdcsr & BIF_SDCSR_BR) printk("|<BUS REQ>");
+ if(bif_sdcsr & BIF_SDCSR_BG) printk("|<BUS GET>");
+ if(bif_sdcsr & BIF_SDCSR_ER) printk("|<ERROR:");
+ if(bif_sdcsr & BIF_SDCSR_SP) printk("|SYNC PARITY:");
+ if(bif_sdcsr & BIF_SDCSR_LP) printk("|LBUS PARITY:");
+ if(bif_sdcsr & BIF_SDCSR_LR) printk("|READ EMPTY FIFO:");
+ if(bif_sdcsr & BIF_SDCSR_LW) printk("|WRITE FULL FIFO:");
+ if(bif_sdcsr & BIF_SDCSR_AL) printk("|READ ENDBIT:");
+ if(bif_sdcsr & BIF_SDCSR_SS) printk("|SET MASK SSTAT:");
+ if(bif_sdcsr & BIF_SDCSR_SC) printk("|CLR SSYNC ILLEGALLY:");
+ if(bif_sdcsr & BIF_SDCSR_SY) printk("|REQ SSYNC ILLEGALLY:");
+ if(bif_sdcsr & BIF_SDCSR_FS) printk("|SET MASK FSTAT:");
+ if(bif_sdcsr & BIF_SDCSR_FC) printk("|CLR FSYNC ILLEGALLY:");
+ if(bif_sdcsr & BIF_SDCSR_FY) printk("|REQ FSYNC ILLEGALLY:");
+ if(bif_sdcsr & BIF_SDCSR_CP) printk("|BNET PARITY:");
+ if(bif_sdcsr & BIF_SDCSR_FP) printk("|FE NOT SET WHEN SC/GA:");
+ if(bif_sdcsr & BIF_SDCSR_PS) printk("|RECV PACKET ILLEGALLY:");
+ if(bif_sdcsr & BIF_SDCSR_RA) printk("|CHANGE FE ILLEGALLY:");
+ if(bif_sdcsr & BIF_SDCSR_PA) printk("|SEND/RECV ILLEGALLY:");
+ if(bif_sdcsr & BIF_SDCSR_DL) printk("|DATA LOST:");
+ if(bif_sdcsr & BIF_SDCSR_ER) printk("|>");
+ if(bif_sdcsr & BIF_SDCSR_PE) printk("|<SYNC PARITY ENABLE>");
+ printk("|\n");
+ printk("|\tBIF_INTR = %08x\n", bif_intr);
+ printk("|\tBIF_MHOCR = %08x\n", bif_mhocr);
+
+ printk("|\tBIF_X0SK = %08x\n", bif_x0sk);
+ printk("|\tBIF_XSK = %08x\n", bif_xsk);
+ printk("|\tBIF_XSZ = %08x\n", bif_xsz);
+ printk("|\tBIF_Y0SK = %08x\n", bif_y0sk);
+ printk("|\tBIF_YSK = %08x\n", bif_ysk);
+ printk("|\tBIF_YSZ = %08x\n", bif_ysz);
+ printk("|\tBIF_CX0SK = %08x\n", bif_cx0sk);
+ printk("|\tBIF_CXSK = %08x\n", bif_cxsk);
+ printk("|\tBIF_CXSZ = %08x\n", bif_cxsz);
+ printk("|\tBIF_CY0SK = %08x\n", bif_cy0sk);
+ printk("|\tBIF_CYSK = %08x\n", bif_cysk);
+ printk("|\tBIF_CYSZ = %08x\n", bif_cysz);
+
+ printk("|\tBIF_TTL = %08x\n", bif_ttl);
+ printk("|\tBIF_CTTL = %08x\n", bif_cttl);
+ printk("|\tBIF_HEADER = %08x\n", bif_header);
+}
+
+
+void bif_led_status(void)
+{
+#if 1
+ static int i = 0;
+ unsigned char res = 0;
+
+ switch (i) {
+ case 0:
+ case 2:
+ res = 0xff;
+ break;
+ case 1:
+ case 3:
+ res = 0;
+ break;
+ default:
+ res = 0xFF & (BIF_IN(BIF_SDCSR) >> (((i-4)/4)*8));
+ }
+ i = (i+1) % 20;
+
+ ap_led(res);
+#endif
+}
+
+static void get_bif(void)
+{
+ if (HAVE_BIF())
+ return;
+
+ drop_ip_packets = 1;
+
+ DEBUG(("|get_bif started\n"));
+
+ if (dma_state != DMA_IDLE)
+ ap_dma_wait(DMA_CH2);
+
+#if SNET_ARBITRATION
+ /* wait till the host doesn't want the BIF anymore, tossing
+ any data that arrives */
+ while (BIF_IN(FSTT_CLR) & HOST_STATUS_BIT)
+ if (BIF_IN(BIF_SDCSR) & BIF_SDCSR_RB)
+ bif_intr_receive(NULL);
+ waiting_for_bif = 0;
+#endif
+
+#if TOKEN_ARBITRATION
+ BIF_OUT(FSTT_CLR,HOST_STATUS_BIT);
+#endif
+
+ /* request the BIF */
+ BIF_OUT(BIF_SDCSR,BIF_SDCSR_BR);
+
+ /* loop waiting for us to get the BIF, tossing any data */
+ while (!HAVE_BIF())
+ if (BIF_IN(BIF_SDCSR) & BIF_SDCSR_RB)
+ bif_intr_receive(NULL);
+
+ bif_reset_interrupts();
+ if (!interrupt_driven)
+ bif_mask_interrupts();
+
+ drop_ip_packets = 0;
+
+#if TOKEN_ARBITRATION
+ BIF_OUT(FSTT_SET,HOST_STATUS_BIT);
+#endif
+
+ DEBUG(("|get_bif done\n"));
+}
+
+
+/* write a message to the front end over the Bnet. This can be in
+ multiple parts, as long as the first part sets "start" and the last
+ part sets "end". The bus will be grabbed while this is going on
+ */
+static void write_bif(char *buf,int size,int start,int end)
+{
+ unsigned *ibuf;
+ unsigned avail;
+
+ DEBUG(("|write_bif %d %d %d\n",size,start,end));
+
+ if (start) {
+ /* a dma op may be in progress */
+ if (dma_state != DMA_IDLE) ap_dma_wait(DMA_CH2);
+ }
+
+ size = (size+3) >> 2;
+ ibuf = (unsigned *)buf;
+ if (end) size--;
+
+ while (size > 4) {
+ while (!(avail=(BIF_IN(BIF_SDCSR) >> BIF_SDCSR_TB_SHIFT) & 7))
+ ;
+ if (avail & 4) {
+ BIF_OUT(BIF_DATA,ibuf[0]);
+ BIF_OUT(BIF_DATA,ibuf[1]);
+ BIF_OUT(BIF_DATA,ibuf[2]);
+ BIF_OUT(BIF_DATA,ibuf[3]);
+ size -= 4; ibuf += 4;
+ continue;
+ }
+
+ if (avail & 2) {
+ BIF_OUT(BIF_DATA,ibuf[0]);
+ BIF_OUT(BIF_DATA,ibuf[1]);
+ size -= 2; ibuf += 2;
+ continue;
+ }
+ BIF_OUT(BIF_DATA,ibuf[0]);
+ ibuf++; size--;
+ }
+
+ while (size--) {
+ while (!(BIF_IN(BIF_SDCSR) & BIF_SDCSR_TB)) ;
+ BIF_OUT(BIF_DATA,ibuf[0]);
+ ibuf++;
+ }
+
+ if (end) {
+ while (!(BIF_IN(BIF_SDCSR) & BIF_SDCSR_TB)) ;
+ BIF_OUT(BIF_EDATA,*ibuf);
+ }
+
+ DEBUG(("|write bif done\n"));
+}
+
+#if TOKEN_ARBITRATION
+static void forward_token(void)
+{
+ struct cap_request req;
+ req.cid = mpp_cid();
+ req.type = REQ_BIF_TOKEN;
+ req.size = sizeof(req);
+ if (req.cid == cap_init.numcells - 1)
+ req.header = MAKE_HEADER(HOST_CID);
+ else
+ req.header = MAKE_HEADER(req.cid + 1);
+
+ write_bif((char *)&req,sizeof(req),1,1);
+ have_token = 0;
+}
+#endif
+
+static void release_bif(void)
+{
+ static int dummy[DUMMY_MSG_LEN];
+
+ waiting_for_bif = 0;
+
+#if SNET_ARBITRATION
+ /* mask the attention interrupt */
+ attn_mask();
+#endif
+
+ /* maybe we don't have it?? */
+ if (!HAVE_BIF())
+ return;
+
+ DEBUG(("|release bif started\n"));
+
+ if (dma_state != DMA_IDLE) ap_dma_wait(DMA_CH2);
+
+#if TOKEN_ARBITRATION
+ if (have_token)
+ forward_token();
+#endif
+
+#if 1
+ /* send a dummy message to ensure FIFO flushing
+ (suggestion from woods to overcome bif release
+ hardware bug) */
+ dummy[0] = 0xEEEE4000;
+ write_bif((char *)dummy,DUMMY_MSG_LEN,1,1);
+#endif
+ /* wait till the send FIFO is completely empty */
+ while (!((BIF_IN(BIF_SDCSR) & BIF_SDCSR_TB) == BIF_SDCSR_TB)) ;
+
+ /* wait another few us */
+ udelay(DUMMY_MSG_WAIT);
+
+ /* send release-data */
+ BIF_OUT(BIF_DATA,BIF_HEADER_RS);
+
+ /* wait until we don't have the bus */
+ while (HAVE_BIF()) ;
+
+ DEBUG(("|release bif done\n"));
+}
+
+
+/* wait for a particular request type - throwing away anything else! */
+void ap_wait_request(struct cap_request *req,int type)
+{
+ drop_ip_packets = 1;
+ do {
+ while (!BIF_DATA_WAITING())
+ if (HAVE_BIF()) release_bif();
+ read_bif((char *)req,sizeof(*req));
+ if (req->type != type) {
+ bif_intr_receive(req);
+ }
+ } while (req->type != type);
+ drop_ip_packets = 0;
+}
+
+
+void write_bif_polled(char *buf1,int len1,char *buf2,int len2)
+{
+ unsigned flags;
+ save_flags(flags); cli();
+
+ get_bif();
+ write_bif(buf1,len1,1,(buf2&&len2)?0:1);
+ if (buf2 && len2)
+ write_bif(buf2,len2,0,1);
+ release_bif();
+ restore_flags(flags);
+}
+
+static void want_bif(void)
+{
+ unsigned flags;
+
+ save_flags(flags); cli();
+
+ /* maybe we've already got it */
+ if (HAVE_BIF()) {
+ waiting_for_bif = 0;
+ restore_flags(flags);
+ return;
+ }
+
+#if SNET_ARBITRATION
+ if (interrupt_driven)
+ attn_enable();
+
+ /* check if the host wants it */
+ if (BIF_IN(FSTT_CLR) & HOST_STATUS_BIT) {
+ /* the host wants it - don't get it yet */
+ waiting_for_bif = 1;
+ } else {
+ /* the host doesn't want it - just set bus request */
+ waiting_for_bif = 0;
+ BIF_OUT(BIF_SDCSR,BIF_SDCSR_BR);
+ while (!HAVE_BIF() && !BIF_BUSY()) ;
+ DEBUG(("|set bif request\n"));
+ }
+ restore_flags(flags);
+ return;
+#endif
+
+#if TOKEN_ARBITRATION
+ if (net_started && !have_token) {
+ BIF_OUT(FSTT_CLR,HOST_STATUS_BIT);
+ restore_flags(flags);
+ return;
+ }
+ BIF_OUT(FSTT_SET,HOST_STATUS_BIT);
+#endif
+
+ BIF_OUT(BIF_SDCSR,BIF_SDCSR_BR);
+ restore_flags(flags);
+}
+
+#define BIF_NOCOPY (1<<0)
+
+/* a queue of requests that need to be sent over the bif. Needs to be
+modified sometime to allow the direct queueing of skb's */
+struct bif_queue {
+ volatile struct bif_queue *next;
+ struct cap_request req;
+ char *data;
+ int data_size;
+ int flags;
+};
+
+static volatile struct bif_queue *bif_queue_top = NULL;
+static volatile struct bif_queue *bif_queue_end = NULL;
+
+static struct sk_buff *skb_out = NULL;
+static struct sk_buff *skb_in = NULL;
+static char *bif_dma_data = NULL;
+static int bif_dma_out_size = 0;
+
+
+/* send waiting elements. Called mainly when we get a bif "bus get"
+ interrupt to say we now have the bus */
+static void bif_intr_runqueue(void)
+{
+ unsigned flags;
+
+ /* if I don't have the bus then return */
+ if (!HAVE_BIF())
+ return;
+
+ if (dma_state != DMA_IDLE) return;
+
+ save_flags(flags); cli();
+
+ while (bif_queue_top) {
+ volatile struct bif_queue *q = bif_queue_top;
+ bif_queue_top = q->next;
+
+ /* printk("|queue run (length=%d)\n",queue_length); */
+ queue_length--;
+
+ if (!q->data) {
+ /* use programmed IO for small requests */
+ write_bif((char *)&q->req,sizeof(q->req),1,1);
+ kfree_s((char *)q,sizeof(*q));
+ continue;
+ }
+
+ if (q->flags & BIF_NOCOPY) {
+ write_bif((char *)&q->req,sizeof(q->req),1,0);
+ }
+
+ if (use_dma && q->data_size > DMA_THRESHOLD) {
+ dma_state = DMA_OUTGOING;
+ if (q->req.type == REQ_IP) {
+ skb_out = (struct sk_buff *)q->data;
+ ap_dma_go(DMA_CH2,(unsigned)skb_out->data,
+ q->data_size,DMA_DCMD_TD_MD);
+ } else {
+ if (!(q->flags & BIF_NOCOPY)) {
+ bif_dma_data = q->data;
+ bif_dma_out_size = q->data_size;
+ }
+ ap_dma_go(DMA_CH2,(unsigned)q->data,
+ q->data_size,DMA_DCMD_TD_MD);
+ }
+ kfree_s((char *)q,sizeof(*q));
+ restore_flags(flags);
+ return; /* wait for DMA to complete */
+ }
+
+ if (q->req.type == REQ_IP) {
+ struct sk_buff *skb = (struct sk_buff *)q->data;
+ write_bif(skb->data,q->data_size,1,1);
+ dev_kfree_skb(skb, FREE_WRITE);
+ } else {
+ write_bif(q->data,q->data_size,1,1);
+ if (!(q->flags & BIF_NOCOPY))
+ kfree_s(q->data,q->data_size);
+ }
+ kfree_s((char *)q,sizeof(*q));
+ }
+
+ /* I don't want the bus now */
+ release_bif();
+ restore_flags(flags);
+}
+
+
+static void queue_attach(struct bif_queue *q)
+{
+ unsigned flags;
+ save_flags(flags); cli();
+
+ /* attach it to the end of the queue */
+ if (!bif_queue_top) {
+ bif_queue_top = q;
+ } else {
+ bif_queue_end->next = q;
+ }
+ bif_queue_end = q;
+
+ queue_length++;
+
+ /* printk("|queue add (length=%d)\n",queue_length); */
+
+ /* tell the bus we want access */
+ want_bif();
+
+ restore_flags(flags);
+}
+
+
+/* queue an element for sending over the bif. */
+int bif_queue(struct cap_request *req,char *buf,int bufsize)
+{
+ struct bif_queue *q;
+
+ if (req->header == 0)
+ req->header = MAKE_HEADER(HOST_CID);
+
+ /* if we aren't running interrupt driven then just send it
+ immediately */
+ if (!interrupt_driven) {
+ write_bif_polled((char *)req,sizeof(*req),buf,bufsize);
+ return(0);
+ }
+
+ /* allocate a queue element */
+ q = (struct bif_queue *)kmalloc(sizeof(*q), GFP_ATOMIC);
+ if (!q) {
+ /* yikes! */
+ return(-ENOMEM);
+ }
+
+ q->flags = 0;
+ q->data = NULL;
+ q->data_size = 0;
+
+ if (buf && bufsize>0) {
+ q->data_size = bufsize+sizeof(*req);
+ q->data = (char *)kmalloc(q->data_size,GFP_ATOMIC);
+ if (!q->data) {
+ kfree_s(q,sizeof(*q));
+ return(-ENOMEM);
+ }
+ }
+
+ q->req = *req;
+ if (buf&&bufsize) {
+ memcpy(q->data,(char *)req,sizeof(*req));
+ memcpy(q->data+sizeof(*req),buf,bufsize);
+ }
+ q->next = NULL;
+
+ queue_attach(q);
+
+ return(0);
+}
+
+
+/* queue an element for sending over the bif. */
+int bif_queue_nocopy(struct cap_request *req,char *buf,int bufsize)
+{
+ struct bif_queue *q;
+
+ if (req->header == 0)
+ req->header = MAKE_HEADER(HOST_CID);
+
+ /* allocate a queue element */
+ q = (struct bif_queue *)kmalloc(sizeof(*q), GFP_ATOMIC);
+ if (!q) {
+ return(-ENOMEM);
+ }
+
+ q->data = buf;
+ q->data_size = bufsize;
+ q->flags = BIF_NOCOPY;
+ q->req = *req;
+ q->next = NULL;
+
+ queue_attach(q);
+
+ return(0);
+}
+
+
+/* put an IP packet into the bif queue */
+int bif_send_ip(int cid, struct sk_buff *skb)
+{
+ struct cap_request *req = (struct cap_request *)skb->data;
+ struct bif_queue *q;
+ u_long destip;
+
+ destip = *(u_long *)(skb->data+sizeof(*req)+16);
+
+ if (cid != -1) {
+ req->header = MAKE_HEADER(cid);
+ } else if (destip == (cap_init.baseIP | ~cap_init.netmask)) {
+ req->header = BIF_HEADER_IN | BIF_HEADER_BR;
+ } else {
+ req->header = MAKE_HEADER(HOST_CID);
+ }
+
+ /* allocate a queue element */
+ q = (struct bif_queue *)kmalloc(sizeof(*q), GFP_ATOMIC);
+ if (!q) {
+ /* yikes! */
+ dev_kfree_skb(skb, FREE_WRITE);
+ return(-ENOMEM);
+ }
+
+ req->size = ROUND4(skb->len);
+ req->cid = mpp_cid();
+ req->type = REQ_IP;
+
+ q->data = (char *)skb;
+ q->data_size = req->size;
+ q->next = NULL;
+ q->req = *req;
+ q->flags = 0;
+
+ queue_attach(q);
+
+ return(0);
+}
+
+
+/* send an OPENNET request to tell the front end to open the apnet
+ network interface */
+void start_apnet(void)
+{
+ struct cap_request req;
+ req.cid = mpp_cid();
+ req.type = REQ_OPENNET;
+ req.size = sizeof(req);
+ req.header = MAKE_HEADER(HOST_CID);
+
+ bif_queue(&req,NULL,0);
+ printk("sent start_apnet request\n");
+}
+
+/* we have received an IP packet - pass it to the bif network
+ interface code */
+static void reply_ip(struct cap_request *req)
+{
+ if (drop_ip_packets ||
+ !(skb_in = dev_alloc_skb(req->size - sizeof(*req)))) {
+ bif_toss(req->size - sizeof(*req));
+ return;
+ }
+
+ if (use_dma && req->size > DMA_THRESHOLD) {
+ dma_state = DMA_INCOMING;
+ ap_dma_go(DMA_CH2,
+ (unsigned)skb_put(skb_in,req->size - sizeof(*req)),
+ req->size - sizeof(*req),DMA_DCMD_TD_DM);
+ } else {
+ read_bif(skb_put(skb_in,req->size - sizeof(*req)),
+ req->size - sizeof(*req));
+ bif_rx(skb_in);
+ skb_in = NULL;
+ }
+}
+
+
+/* we have received a bread block - DMA it in */
+static void reply_bread(struct cap_request *req)
+{
+ extern char *ap_buffer(struct cap_request *creq);
+ char *buffer;
+
+ buffer = ap_buffer(req);
+ bread_req = *req;
+
+ if (use_dma) {
+ dma_state = DMA_INCOMING;
+ ap_dma_go(DMA_CH2,
+ (unsigned)buffer,req->size - sizeof(*req),
+ DMA_DCMD_TD_DM);
+ } else {
+ read_bif(buffer,req->size - sizeof(*req));
+ ap_complete(&bread_req);
+ bread_req.type = -1;
+ }
+}
+
+
+static struct debug_key {
+ struct debug_key *next;
+ char key;
+ void (*fn)(void);
+ char *description;
+} *debug_keys = NULL;
+
+
+void show_debug_keys(void)
+{
+ struct debug_key *r;
+ for (r=debug_keys;r;r=r->next)
+ printk("%c: %s\n",r->key,r->description);
+}
+
+
+void bif_add_debug_key(char key,void (*fn)(void),char *description)
+{
+ struct debug_key *r,*r2;
+ r = (struct debug_key *)kmalloc(sizeof(*r),GFP_ATOMIC);
+ if (r) {
+ r->next = NULL;
+ r->key = key;
+ r->fn = fn;
+ r->description = description;
+ if (!debug_keys) {
+ debug_keys = r;
+ } else {
+ for (r2=debug_keys;
+ r2->next && r2->key != key;r2=r2->next) ;
+
+ if (r2->key == key) {
+ r2->fn = fn;
+ r2->description = description;
+ kfree_s(r,sizeof(*r));
+ } else {
+ r2->next = r;
+ }
+ }
+ }
+}
+
+/* these are very useful for debugging ! */
+static void reply_putchar(struct cap_request *req)
+{
+ struct debug_key *r;
+
+ char c = req->data[0];
+
+ ap_set_user(req->data[1]);
+
+ for (r=debug_keys;r;r=r->next)
+ if (r->key == c) {
+ r->fn();
+ break;
+ }
+ if (!r)
+ printk("cell %d got character %d [%c]\n",mpp_cid(),(int)c,c);
+
+ ap_set_user(-1);
+}
+
+
+/* send a signal to a task by name or pid */
+static void reply_kill(struct cap_request *req)
+{
+ int sig = req->data[0];
+ struct task_struct *p;
+ int len;
+ char name[32];
+
+ len = req->size - sizeof(*req);
+ if (len == 0) {
+ int pid = req->data[1];
+ for_each_task(p)
+ if (p->pid == pid) {
+ send_sig(sig,p,1);
+ return;
+ }
+ printk("cell %d: no task with pid %d\n",mpp_cid(),pid);
+ return;
+ }
+
+ if (len > sizeof(name)-1) {
+ bif_toss(len);
+ return;
+ }
+
+ read_bif(name,len);
+ name[len] = 0;
+
+ for_each_task(p)
+ if (strcmp(name,p->comm) == 0)
+ send_sig(sig,p,1);
+}
+
+
+static struct req_list {
+ struct req_list *next;
+ int type;
+ void (*fn)(struct cap_request *);
+} *reg_req_list = NULL;
+
+
+void bif_register_request(int type,void (*fn)(struct cap_request *))
+{
+ struct req_list *r,*r2;
+ r = (struct req_list *)kmalloc(sizeof(*r),GFP_ATOMIC);
+ if (r) {
+ r->next = NULL;
+ r->type = type;
+ r->fn = fn;
+ if (!reg_req_list) {
+ reg_req_list = r;
+ } else {
+ for (r2=reg_req_list;
+ r2->next && r2->type != type;r2=r2->next) ;
+
+ if (r2->type == type) {
+ r2->fn = fn;
+ kfree_s(r,sizeof(*r));
+ } else {
+ r2->next = r;
+ }
+ }
+ }
+}
+
+
+
+/* a request has come in on the bif - process it */
+static void bif_intr_receive(struct cap_request *req1)
+{
+ struct req_list *r;
+ extern void ap_open_reply(struct cap_request *creq);
+ struct cap_request req;
+
+ if (req1) {
+ req = *req1;
+ } else {
+ /* read the main cap request header */
+ read_bif((char *)&req,sizeof(req));
+ }
+
+ /* service it */
+ switch (req.type)
+ {
+ case REQ_PUTCHAR:
+ reply_putchar(&req);
+ break;
+ case REQ_KILL:
+ reply_kill(&req);
+ break;
+ case REQ_BREAK:
+ breakpoint();
+ break;
+ case REQ_IP:
+ reply_ip(&req);
+ break;
+#if TOKEN_ARBITRATION
+ case REQ_BIF_TOKEN:
+ have_token = 1;
+ want_bif();
+ break;
+#endif
+ case REQ_OPENNET:
+ net_started = 1;
+ break;
+ case REQ_BREAD:
+ reply_bread(&req);
+ break;
+ case REQ_BOPEN:
+ ap_open_reply(&req);
+ break;
+ case REQ_BWRITE:
+ ap_complete(&req);
+ break;
+ case REQ_SCHEDULE:
+ mpp_schedule(&req);
+ break;
+
+ default:
+ for (r=reg_req_list;r;r=r->next)
+ if (r->type == req.type) {
+ r->fn(&req);
+ return;
+ }
+ printk("Unknown request %d\n",req.type);
+ break;
+ }
+}
+
+
+static void bif_dma_complete(void)
+{
+ extern int bif_rx(struct sk_buff *skb);
+ enum dma_state old_state = dma_state;
+ unsigned a;
+
+ a = DMA_IN(DMA2_DMST);
+
+ if (a & DMA_DMST_AC) return;
+
+ DMA_OUT(DMA2_DMST,AP_CLR_INTR_REQ<<DMA_INTR_NORMAL_SH);
+ DMA_OUT(DMA2_DMST,AP_CLR_INTR_REQ<<DMA_INTR_ERROR_SH);
+
+ if (old_state == DMA_INCOMING) {
+ if (skb_in) bif_rx(skb_in);
+ skb_in = NULL;
+ }
+ if (bread_req.type != -1) {
+ ap_complete(&bread_req);
+ bread_req.type = -1;
+ }
+
+ if (bif_dma_data) {
+ kfree_s(bif_dma_data,bif_dma_out_size);
+ bif_dma_data = NULL;
+ }
+
+ if (skb_out) {
+ dev_kfree_skb(skb_out, FREE_WRITE);
+ skb_out = NULL;
+ }
+
+ dma_state = DMA_IDLE;
+
+ if (a & (AP_INTR_REQ<<DMA_INTR_ERROR_SH)) {
+ printk("BIF: got dma error\n");
+ }
+}
+
+
+/* handle bif related interrupts. Currently handles 3 interrupts, the
+ bif header interrupt, the bif get interrupt and the dma transfer
+ complete interrupt */
+static void bif_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned flags;
+
+ bif_pt_regs = regs;
+
+ save_flags(flags); cli();
+
+ mac_dma_complete();
+
+ if (dma_state != DMA_IDLE) {
+ bif_dma_complete();
+ }
+
+ bif_reset_interrupts();
+
+ while (dma_state == DMA_IDLE && BIF_DATA_WAITING()) {
+ bif_intr_receive(NULL);
+ }
+
+ if (dma_state != DMA_IDLE) {
+ bif_dma_complete();
+ }
+
+ if (dma_state == DMA_IDLE && bif_queue_top && !HAVE_BIF()) {
+ want_bif();
+ }
+
+ if (dma_state == DMA_IDLE && HAVE_BIF()) {
+ waiting_for_bif = 0;
+ bif_intr_runqueue();
+ }
+
+ if (dma_state == DMA_IDLE && HAVE_BIF()) {
+ release_bif();
+ }
+
+ restore_flags(flags);
+ bif_pt_regs = NULL;
+}
+
+
+/* handle the attention interrupt - used for BIF arbitration */
+static void attn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned flags;
+
+ save_flags(flags); cli();
+
+ attn_enable();
+
+#if SNET_ARBITRATION
+ attn_mask();
+ DEBUG(("|bif attn irq %d\n",irq));
+
+ if (waiting_for_bif)
+ want_bif();
+
+ DEBUG(("|bif attn irq %d done\n",irq));
+#endif
+
+ bif_interrupt(irq,dev_id,regs);
+ restore_flags(flags);
+}
+
+
+void bif_timer(void)
+{
+#if 1
+ if (interrupt_driven)
+ bif_interrupt(0,NULL,NULL);
+#endif
+}
+
+static void tnet_ip_enable(void)
+{
+ tnet_ip_enabled = 1;
+ printk("tnet_ip_enabled=%d\n",tnet_ip_enabled);
+}
+
+static void tnet_ip_disable(void)
+{
+ tnet_ip_enabled = 0;
+ printk("tnet_ip_enabled=%d\n",tnet_ip_enabled);
+}
+
+/* initialise the bif code */
+void ap_bif_init(void)
+{
+ int res;
+ unsigned long flags;
+ printk("doing ap_bif_init()\n");
+
+ bif_add_debug_key('+',tnet_ip_enable,"enable Tnet based IP");
+ bif_add_debug_key('-',tnet_ip_disable,"disable Tnet based IP");
+
+ save_flags(flags); cli();
+
+ /* register the BIF interrupt */
+ if ((res=request_irq(APBIF_IRQ, bif_interrupt,
+ SA_INTERRUPT, "apbif", NULL))) {
+ printk("Failed to install bif interrupt handler\n");
+ restore_flags(flags);
+ return;
+ }
+
+ /* and the bus get interrupt */
+ if ((res=request_irq(APBIFGET_IRQ, bif_interrupt,
+ SA_INTERRUPT, "apbifget", NULL))) {
+ printk("Failed to install bifget interrupt handler\n");
+ restore_flags(flags);
+ return;
+ }
+
+ /* dma complete interrupt */
+ if ((res=request_irq(APDMA_IRQ, bif_interrupt, SA_INTERRUPT,
+ "apdma", NULL))) {
+ printk("Failed to install bifdma interrupt handler\n");
+ restore_flags(flags);
+ return;
+ }
+
+ /* attention interrupt */
+ if ((res=request_irq(APATTN_IRQ, attn_interrupt, SA_INTERRUPT,
+ "apattn", NULL))) {
+ printk("Failed to install apattn interrupt handler\n");
+ restore_flags(flags);
+ return;
+ }
+
+ printk("Installed bif handlers\n");
+
+ /* enable dma-request */
+ BIF_OUT(BIF_SDCSR,BIF_SDCSR_DE);
+
+ DMA_OUT(DMA2_DCMD,DMA_DCMD_SA);
+ DMA_OUT(DMA2_DMST,DMA_DMST_RST);
+ DMA_OUT(DMA2_DMST,AP_CLR_INTR_REQ<<DMA_INTR_NORMAL_SH);
+ DMA_OUT(DMA2_DMST,AP_CLR_INTR_MASK<<DMA_INTR_NORMAL_SH);
+ DMA_OUT(DMA2_DMST,AP_CLR_INTR_REQ<<DMA_INTR_ERROR_SH);
+ DMA_OUT(DMA2_DMST,AP_CLR_INTR_MASK<<DMA_INTR_ERROR_SH);
+
+ /* enable the attention interrupt */
+ attn_enable();
+
+ DMA_OUT(DMA_BIF_BCMD,DMA_BCMD_SA);
+ DMA_OUT(DMA_BIF_BRST,DMA_DMST_RST);
+
+ /* from now on we are interrupt driven */
+ bread_req.type = -1;
+ dma_state = DMA_IDLE;
+ interrupt_driven = 1;
+ use_dma = 1;
+ bif_reset_interrupts();
+
+ /* if theres something in the queue then we also want the bus */
+ if (bif_queue_top)
+ want_bif();
+
+ /* tell the host that networking is now OK */
+ start_apnet();
+
+ printk("bif initialised\n");
+
+ restore_flags(flags);
+}
+
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov