patch-2.1.23 linux/arch/sparc/ap1000/tnet.c

Next file: linux/arch/sparc/ap1000/util.c
Previous file: linux/arch/sparc/ap1000/timer.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.22/linux/arch/sparc/ap1000/tnet.c linux/arch/sparc/ap1000/tnet.c
@@ -0,0 +1,709 @@
+  /*
+   * 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 Tnet interface */
+
+#include <asm/ap1000/apreg.h>
+#include <asm/ap1000/aplib.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/pgtable.h>
+#include <asm/pgtsrmmu.h>
+#include <stdarg.h>
+#include <linux/skbuff.h>
+
+
+/* message types for system messages */
+#define TNET_IP         0
+#define TNET_IP_SMALL   1
+#define TNET_RPC        2
+
+static struct {
+	int errors;
+	int alloc_errors;
+	int bytes_received;
+	int bytes_sent;
+	int packets_received;
+	int packets_sent;
+	int small_packets_received;
+	int small_packets_sent;
+} tnet_stats;
+
+extern int cap_cid0;
+extern int cap_ncel0;
+static u_long xy_global_head;
+
+extern unsigned _ncel, _ncelx, _ncely, _cid, _cidx, _cidy;
+
+extern struct ringbuf_struct system_ringbuf;
+extern u_long system_read_ptr;
+
+u_long system_recv_flag = 0;
+static u_long system_recv_count = 0;
+
+int *tnet_rel_cid_table;
+
+static int dummy=1;
+
+#define TNET_IP_THRESHOLD 100
+
+void tnet_check_completion(void);
+static void reschedule(void);
+static u_long tnet_add_completion(void (*fn)(int a1,...), 
+				  int a1,int a2);
+static void tnet_info(void);
+
+static struct {
+  int shift;
+  void (*fn)(void);
+} iports[4] = {
+  {MC_INTP_0_SH,tnet_check_completion},
+  {MC_INTP_1_SH,reschedule},
+  {MC_INTP_2_SH,NULL},
+  {MC_INTP_3_SH,NULL}};
+
+static inline int rel_cid(unsigned dst)
+{  
+	unsigned dstx, dsty;
+	unsigned dx,dy;
+
+	if (dst == _cid) return 0;
+
+	dstx = dst % _ncelx;
+	dsty = dst / _ncelx;
+	if (dstx >= _cidx)
+		dx = dstx - _cidx;
+	else
+		dx = (_ncelx - _cidx) + dstx;
+	
+	if (dsty >= _cidy)
+		dy = dsty - _cidy;
+	else
+		dy = (_ncely - _cidy) + dsty;
+	
+	return (dx<<8) | dy;
+}
+
+#define SAVE_PID() \
+  unsigned long flags; \
+  int saved_pid; \
+  save_flags(flags); cli(); \
+  saved_pid = MSC_IN(MSC_PID); \
+  MSC_OUT(MSC_PID,SYSTEM_CONTEXT);
+
+#define RESTORE_PID() \
+  MSC_OUT(MSC_PID,saved_pid); \
+  restore_flags(flags);
+
+
+void ap_put(int dest_cell,u_long local_addr,int size,
+	    u_long remote_addr,u_long dest_flag,u_long local_flag)
+{
+  volatile u_long *entry;
+  SAVE_PID();
+    
+  entry = (volatile u_long *)MSC_PUT_QUEUE_S;
+
+  *entry = tnet_rel_cid_table[dest_cell];
+  *entry = ((size+3) >> 2); 
+  *entry = (u_long)remote_addr;
+  *entry = 0;
+  *entry = (u_long)dest_flag;
+  *entry = (u_long)local_flag;
+  *entry = (u_long)local_addr;
+  *entry = 0;
+  RESTORE_PID();
+}
+
+/* remote_addr is physical 
+   local address is virtual 
+   both flags are virtual */
+void ap_phys_put(int dest_cell,u_long local_addr,int size,
+		 u_long remote_addr,u_long dest_flag,u_long local_flag)
+{
+  volatile u_long *entry;
+  SAVE_PID();
+
+  entry = (volatile u_long *)MSC_CPUT_QUEUE_S;
+
+  *entry = tnet_rel_cid_table[dest_cell];
+  *entry = ((size+3) >> 2); 
+  *entry = (u_long)remote_addr;
+  *entry = 0;
+  *entry = (u_long)dest_flag;
+  *entry = (u_long)local_flag;
+  *entry = (u_long)local_addr;
+  *entry = 0;
+  RESTORE_PID();
+}
+
+
+/* broadcast put - yeah! */
+void ap_bput(u_long local_addr,int size,
+	     u_long remote_addr,u_long dest_flag,u_long local_flag)
+{
+  volatile u_long *entry = (volatile u_long *)MSC_XYG_QUEUE_S;
+  SAVE_PID();
+
+  *entry = xy_global_head;
+  *entry = ((size+3) >> 2);
+  *entry = (u_long)remote_addr;
+  *entry = 0;
+  *entry = (u_long)dest_flag;
+  *entry = (u_long)local_flag;
+  *entry = (u_long)local_addr;
+  *entry = 0;
+  RESTORE_PID();
+}
+
+
+/* remote_addr is physical */
+void ap_phys_bput(u_long local_addr,int size,
+		  u_long remote_addr,u_long dest_flag,u_long local_flag)
+{
+  volatile u_long *entry = (volatile u_long *)MSC_CXYG_QUEUE_S;
+  SAVE_PID();
+
+  *entry = xy_global_head;
+  *entry = ((size+3) >> 2);
+  *entry = (u_long)remote_addr;
+  *entry = 0;
+  *entry = (u_long)dest_flag;
+  *entry = (u_long)local_flag;
+  *entry = (u_long)local_addr;
+  *entry = 0;
+  RESTORE_PID();
+}
+
+
+
+void ap_get(int dest_cell,u_long local_addr,int size,
+	    u_long remote_addr,u_long local_flag,u_long dest_flag)
+{
+  volatile u_long *entry;
+  SAVE_PID();
+    
+  entry = (u_long *)MSC_GET_QUEUE_S;
+
+  *entry = tnet_rel_cid_table[dest_cell];
+  *entry = (size+3) >> 2;           /* byte --> word */
+  *entry = (u_long)local_addr;
+  *entry = 0;
+  *entry = (u_long)local_flag;
+  *entry = (u_long)dest_flag;
+  *entry = (u_long)remote_addr;
+  *entry = 0;    
+  RESTORE_PID();
+}
+
+
+/* local_addr is physical 
+   remote_addr is virtual
+   both flags are virtual
+*/
+void ap_phys_get(int dest_cell,u_long local_addr,int size,
+		 u_long remote_addr,u_long local_flag,u_long dest_flag)
+{
+  volatile u_long *entry;
+  SAVE_PID();
+    
+  entry = (u_long *)MSC_CGET_QUEUE_S;
+
+  *entry = tnet_rel_cid_table[dest_cell];
+  *entry = (size+3) >> 2;           /* byte --> word */
+  *entry = (u_long)local_addr;
+  *entry = 0;
+  *entry = (u_long)local_flag;
+  *entry = (u_long)dest_flag;
+  *entry = (u_long)remote_addr;
+  *entry = 0;    
+  RESTORE_PID();
+}
+
+
+/*
+ * copy a message from the ringbuffer - being careful of wraparound
+*/
+static inline void tnet_copyin(unsigned *dest,unsigned *src,int size)
+{
+	unsigned *limit = (unsigned *)system_ringbuf.ringbuf + 
+		(SYSTEM_RINGBUF_SIZE>>2);
+	int size1 = limit - src;
+
+	if (size < size1)
+		size1 = size;
+
+	size -= size1;
+	while (size1--) {
+		*dest++ = *src++;
+	}
+	src = system_ringbuf.ringbuf;
+	while (size--) {
+		*dest++ = *src++;
+	}
+}
+
+
+/*
+  put some data into a tasks ringbuffer. size is in words.
+  */
+static inline void memcpy_to_rbuf(unsigned tid,unsigned *msgp,unsigned size)
+{
+	struct aplib_struct *aplib;
+	unsigned octx, ctx;
+	struct task_struct *tsk;
+	unsigned room;
+
+	tsk = task[tid];
+	if (!tsk || !tsk->aplib)
+		return;
+
+	octx = srmmu_get_context();
+	ctx = MPP_TASK_TO_CTX(tid);
+	if (octx != ctx)
+		srmmu_set_context(ctx);
+	aplib = tsk->aplib;
+
+	if (aplib->write_pointer < aplib->read_pointer) 
+		room = aplib->read_pointer - (aplib->write_pointer+1);
+	else
+		room = aplib->ringbuf_size - 
+			((aplib->write_pointer+1)-aplib->read_pointer);
+
+	if (room < size) {
+		send_sig(SIGLOST,tsk,1);		
+		goto finished;
+	}
+
+	tnet_copyin(&aplib->ringbuf[aplib->write_pointer], msgp, size);
+
+	aplib->write_pointer += size;
+	if (aplib->write_pointer >= aplib->ringbuf_size)
+		aplib->write_pointer -= aplib->ringbuf_size;
+
+	aplib->rbuf_flag1++;
+
+finished:
+	if (octx != ctx)
+		srmmu_set_context(octx);
+}
+
+
+
+/* a aplib message has arrived on the system message queue - process
+   it immediately and return the number of bytes taken by the message in
+   the system ringbuffer 
+
+   Note that this function may be called from interrupt level
+   */
+static inline void aplib_system_recv(unsigned *msgp)
+{
+	unsigned tag = msgp[1]>>28;
+	unsigned size, tid;
+
+	if (tag == RBUF_BIGSEND) {
+		aplib_bigrecv(msgp);
+		return;
+	}
+
+	size = (msgp[0]&0xFFFFF);
+	tid = (msgp[1]&0x3FF);
+
+	memcpy_to_rbuf(tid,msgp,size+2);
+}
+
+
+void tnet_ip_complete(struct sk_buff *skb,int from)
+{  
+#if IP_DEBUG
+	char *data = skb->data;
+	int i;
+	printk("CID(%d) tnet ip complete from %d\n",_cid,from);
+
+	for (i=0;i<skb->len;i+=4)
+		printk("(%08x)%c",*(int *)(data+i),i==32?'\n':' ');
+	printk("\n");
+#endif
+	/* ap_phys_put(from,(u_long)&dummy,4,MC_INTP_0,0,0); */
+	bif_rx(skb);
+	tnet_stats.bytes_received += skb->len;
+	tnet_stats.packets_received++;
+}
+
+
+static void tnet_ip_recv(int cid,u_long *info)
+{
+	u_long flag;
+	u_long ipsize = info[1];
+	u_long remote_addr = info[0];
+	u_long remote_flag = info[2];
+	struct sk_buff *skb = dev_alloc_skb(ipsize+8);
+	char *p;
+
+	if (!skb) {
+		ap_put(cid,0,0,0,remote_flag,0);
+		ap_phys_put(cid,(u_long)&dummy,4,MC_INTP_0,0,0);
+		tnet_stats.alloc_errors++;
+		return;
+	}
+
+	skb_reserve(skb,8); /* align on 16 byte boundary */
+
+	flag = tnet_add_completion(tnet_ip_complete,(int)skb,(int)cid);
+
+	p = (char *)skb_put(skb,ipsize);
+#if 0
+{
+	static unsigned count=0;
+	if (count%500 == 0)
+		printk("CID(%d) fetching %d bytes from %x to %x\n",
+		       _cid,ipsize,remote_addr,p);
+	count++;
+}
+#endif
+	ap_get(cid,p,ipsize,remote_addr,flag,remote_flag);
+	ap_phys_get(cid,MC_INTP_0,4,(u_long)&dummy,0,0);
+#if IP_DEBUG
+	printk("CID(%d) ip packet of length %ld from %ld\n",_cid,ipsize,cid);
+#endif
+}
+
+
+static void tnet_ip_recv_small(u_long *data,int size)
+{
+	struct sk_buff *skb = dev_alloc_skb(size+8);
+	if (skb) {
+		skb_reserve(skb,8);
+		tnet_copyin((unsigned *)skb_put(skb,size),(unsigned *)data,(size+3)>>2);
+		bif_rx(skb);
+		tnet_stats.bytes_received += size;
+		tnet_stats.packets_received++;
+		tnet_stats.small_packets_received++;
+	} else {
+		tnet_stats.alloc_errors++;
+	}
+}
+
+
+/* we've got an RPC from a remote cell */
+static void tnet_rpc_recv(u_long *data,int size)
+{
+	struct fnp {
+		void (*fn)();
+	} fnp;
+	fnp = *(struct fnp *)data;
+	fnp.fn(data,size);
+}
+
+/*
+ * receive messages from the system ringbuffer. We don't bother with
+ * all the niceities that are done in user space, we just always
+ * process the messages in order 
+ */
+static inline void tnet_recv(void)
+{
+	unsigned flags;
+	u_long from,*data,fix,align,size1,size,type;
+
+	if (system_recv_flag == system_recv_count) 
+		return;
+
+	save_flags(flags); cli();
+	while (system_recv_flag != system_recv_count) {
+		u_long read_ptr = 
+			(system_read_ptr + 1) % (SYSTEM_RINGBUF_SIZE>>5);  
+		u_long *msgp = 
+			((u_long *)system_ringbuf.ringbuf) + (read_ptr<<3);
+		u_long tag = (msgp[1]>>28) & 0xF;
+		size1 = (msgp[0]&0xFFFFF)<<2;
+
+		/* move our read pointer past this message */
+		system_read_ptr = (system_read_ptr + 
+				   ((size1+8+31)>>5))%(SYSTEM_RINGBUF_SIZE>>5);
+		system_recv_count++;		
+
+
+		if (tag != RBUF_SYSTEM) {
+			aplib_system_recv(msgp);
+			continue;
+		} 
+
+		from = msgp[0] >> 22;
+		data = msgp+2;
+		fix = (msgp[0]>>20)&3;
+		align = (msgp[1]>>26)&3;
+		size = ((size1 - align) & ~3) | fix;
+		type = (msgp[1]&0xFF);
+
+		switch (type) {
+		case TNET_IP:
+			tnet_ip_recv(from,data);
+			break;
+			
+		case TNET_IP_SMALL:
+			tnet_ip_recv_small(data,size);
+			break;
+			
+		case TNET_RPC:
+			tnet_rpc_recv(data,size);
+			break;
+			
+		default:
+			tnet_stats.errors++;
+			printk("unknown Tnet type %ld\n",type);
+		}
+
+#if DEBUG
+		printk("CID(%d) recvd %d bytes of type %d read_ptr=%x\n",
+		       _cid,size,type,system_read_ptr);
+#endif
+	}
+	restore_flags(flags);
+}
+
+
+#define COMPLETION_LIST_LENGTH 256
+
+static unsigned completion_list_rp = 0;
+static unsigned completion_list_wp = 0;
+
+static volatile struct completion_struct {
+  u_long flag;
+  void (*fn)(int a1,...);
+  u_long args[2];
+} completion_list[COMPLETION_LIST_LENGTH];
+
+
+void tnet_check_completion(void)
+{
+	struct completion_struct *cs;
+	unsigned flags;
+
+	tnet_recv();  
+
+	if (completion_list[completion_list_rp].flag != 2)
+		return;
+
+	save_flags(flags); cli();
+  
+	while (completion_list[completion_list_rp].flag == 2) {
+		cs = &completion_list[completion_list_rp];
+		cs->flag = 0;
+		if (++completion_list_rp == COMPLETION_LIST_LENGTH)
+			completion_list_rp = 0;
+
+		restore_flags(flags);
+
+		cs->fn(cs->args[0],cs->args[1]);
+
+		if (completion_list[completion_list_rp].flag != 2)
+			return;
+
+		save_flags(flags); cli();
+	}
+	
+	restore_flags(flags);
+}
+
+
+static u_long tnet_add_completion(void (*fn)(int a1,...),int a1,int a2)
+{
+        unsigned flags;
+        struct completion_struct *cs;
+	
+	save_flags(flags); cli();
+
+	while (completion_list[completion_list_wp].flag != 0)
+		tnet_check_completion();
+
+	cs = &completion_list[completion_list_wp];
+
+	if (++completion_list_wp == COMPLETION_LIST_LENGTH)
+		completion_list_wp = 0;
+
+	restore_flags(flags);
+
+        cs->flag = 1;
+        cs->fn = fn;
+	cs->args[0] = a1;
+	cs->args[1] = a2;
+
+        return (u_long)&cs->flag;
+}
+
+
+/* 
+ * send a message to the tnet ringuffer on another cell. When the send has
+ * completed call fn with the args supplied 
+ */
+static void tnet_send(long cid,long type,char *src_addr,long byteSize,
+		      int immediate,u_long flag)
+{
+	int wordSize;
+	int byteAlign, byteFix;
+	u_long src;
+	u_long info1, info2;
+	volatile u_long *entry = (volatile u_long *)MSC_SEND_QUEUE_S;
+	SAVE_PID();
+
+	byteAlign = ((u_long)src_addr) & 0x3;
+	byteFix = byteSize & 0x3;
+	
+	src = (u_long)src_addr & ~3;
+	
+	wordSize = (byteSize + byteAlign + 3) >> 2;
+	
+	info1 = (_cid << 22) | (byteFix << 20) | wordSize;
+	info2 = (RBUF_SYSTEM<<28) | (byteAlign << 26) | type;
+	
+	*entry = tnet_rel_cid_table[cid];
+	*entry = wordSize;
+	*entry = (u_long)&system_recv_flag;
+	*entry = flag;
+	*entry = (u_long)src;
+	*entry = 0;
+	*entry = info1;
+	*entry = info2;
+	RESTORE_PID();
+	
+	ap_phys_put(cid,(u_long)&dummy,4,MC_INTP_0,0,0);
+	if (immediate && flag) 
+		ap_phys_put(_cid,(u_long)&dummy,4,MC_INTP_0,0,0);
+}
+
+
+static void free_skb(struct sk_buff *skb, int op)
+{
+	dev_kfree_skb(skb,op);
+}
+
+void tnet_send_ip(int cid,struct sk_buff *skb)
+{
+	char *data = skb->data + sizeof(struct cap_request);
+	int size = skb->len - sizeof(struct cap_request);
+	u_long flag;
+#if IP_DEBUG
+	int i;
+	for (i=0;i<size;i+=4)
+		printk("[%08x]%c",*(int *)(data+i),i==32?'\n':' ');
+	printk("\n");
+#endif
+	if (size > TNET_IP_THRESHOLD) {
+		int *info = (int *)skb->data; /* re-use the header */
+		info[0] = (int)data;
+		info[1] = size;
+		info[2] = tnet_add_completion(free_skb,(int)skb,(int)FREE_WRITE);
+		tnet_send(cid,TNET_IP,info,sizeof(int)*3,0,0);
+	} else {
+		flag = tnet_add_completion(free_skb,
+					   (int)skb,(int)FREE_WRITE);
+		tnet_send(cid,TNET_IP_SMALL,data,size,0,flag);
+		tnet_stats.small_packets_sent++;
+	}
+	tnet_stats.packets_sent++;
+	tnet_stats.bytes_sent += size;
+#if IP_DEBUG
+	printk("CID(%d) sent IP of size %d to %d\n",_cid,size,cid);
+#endif
+}
+
+static void reschedule(void)
+{
+	need_resched = 1;  
+	mark_bh(TQUEUE_BH);
+}
+
+
+/* make a remote procedure call 
+   If free is set then free the data after sending it 
+   The first element of data is presumed to be a function pointer
+*/
+int tnet_rpc(int cell,char *data,int size,int free)
+{
+	unsigned flag=0;
+
+	if (free) {
+		flag = tnet_add_completion(kfree,data,0);
+	}
+
+	tnet_send(cell,TNET_RPC,data,size,0,flag);
+	return 0;
+}
+
+
+static void iport_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int i;
+	u_long intr = MC_IN(MC_INTR_PORT);
+
+	for (i=0;i<4;i++) {
+		if (intr & (AP_INTR_REQ << iports[i].shift)) {
+			MC_OUT(MC_INTR_PORT,AP_CLR_INTR_REQ << iports[i].shift);
+			if (iports[i].fn) iports[i].fn();
+		}
+	}
+}
+
+
+void ap_tnet_init(void)
+{
+	int i;
+
+	bif_add_debug_key('T',tnet_info,"Tnet status");
+
+	memset(completion_list,0,sizeof(completion_list));
+	
+	request_irq(APIPORT_IRQ, iport_irq, SA_INTERRUPT, "iport", 0);
+
+	for (i=0;i<4;i++) {
+		MC_OUT(MC_INTR_PORT,AP_CLR_INTR_REQ << iports[i].shift);
+		MC_OUT(MC_INTR_PORT,AP_CLR_INTR_MASK << iports[i].shift);
+	}    
+
+
+	tnet_rel_cid_table = (int *)kmalloc(sizeof(int)*_ncel,GFP_ATOMIC);
+	for (i=0;i<_ncel;i++)
+		tnet_rel_cid_table[i] = rel_cid(i);
+
+	if(_cid == 0) {
+		xy_global_head  = (((_ncelx -1) << 8) & 0xff00) | 
+			((_ncely - 1) & 0xff);
+	}
+	else {
+		for(i = 1; i < _ncel; i *= 2){
+			if(i & _cid) {	
+				int	rcidx = (_cid-i)%_ncelx - _cid%_ncelx;
+				int	rcidy = (_cid-i)/_ncelx - _cid/_ncelx;
+				xy_global_head  = ((rcidx << 8) & 0xff00) | 
+					(rcidy & 0xff);
+				break;	
+			}
+		}
+	}
+}
+
+static void tnet_info(void)
+{
+	struct completion_struct *cs;
+	
+	printk(
+	       "errors=%d  alloc_errors=%d
+bytes_received=%d  bytes_sent=%d
+packets_received=%d  packets_sent=%d
+small_received=%d  small_sent=%d
+",
+	       tnet_stats.errors, tnet_stats.alloc_errors,
+	       tnet_stats.bytes_received,
+	       tnet_stats.bytes_sent, tnet_stats.packets_received,
+	       tnet_stats.packets_sent, tnet_stats.small_packets_received,
+	       tnet_stats.small_packets_sent);
+
+	printk("recv_flag=%d recv_count=%d read_ptr=%d\n",
+	       system_recv_flag,system_recv_count,system_read_ptr);
+	printk("completion_list_rp=%d  completion_list_wp=%d\n",
+	       completion_list_rp,completion_list_wp);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov