patch-2.1.23 linux/arch/sparc/ap1000/msc.c
Next file: linux/arch/sparc/ap1000/sync.c
Previous file: linux/arch/sparc/ap1000/mpp.c
Back to the patch index
Back to the overall index
- Lines: 1264
- Date:
Sun Jan 26 12:07:06 1997
- Orig file:
v2.1.22/linux/arch/sparc/ap1000/msc.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.1.22/linux/arch/sparc/ap1000/msc.c linux/arch/sparc/ap1000/msc.c
@@ -0,0 +1,1263 @@
+ /*
+ * 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+ Message Controller (MSC+)
+ * and Memory Controller (MC+).
+ *
+ */
+#include <linux/config.h>
+#define _APLIB_
+#include <asm/ap1000/apreg.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/ap1000/pgtapmmu.h>
+#include <linux/tasks.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+static void msc_interrupt_9(int irq, void *dev_id, struct pt_regs *regs);
+static void msc_interrupt_11(int irq, void *dev_id, struct pt_regs *regs);
+static void msc_set_ringbuf(int context);
+static void msc_update_read_ptr(int context,int overflow);
+static void fail_write(int context,int intr,unsigned vaddr);
+static void fail_read(int context,int intr,unsigned vaddr);
+static void msc_switch_from_check(struct task_struct *tsk);
+static void msc_status(void);
+
+#define DEBUG 0
+
+/*
+ * This describes how the 5 queues for outgoing requests
+ * are mapped into the 256 words of send queue RAM in the MSC+.
+ */
+#define NSENDQUEUES 5
+
+static struct send_queues {
+ int base; /* must be a multiple of size */
+ int size; /* must be 32 or 64 */
+} send_queues[NSENDQUEUES] = {
+ {0, 64}, /* System put/send requests */
+ {192, 32}, /* Remote read/write requests */
+ {64, 64}, /* User put/send requests */
+ {224, 32}, /* Remote read replies */
+ {128, 64}, /* Get replies */
+};
+
+#define NR_RBUFS MSC_NR_RBUFS
+
+static struct {
+ unsigned rbmbwp;
+ unsigned rbmmode;
+ unsigned rbmrp;
+} ringbufs[MSC_NR_RBUFS] = {
+ {MSC_RBMBWP0, MSC_RBMMODE0, MSC_RBMRP0},
+ {MSC_RBMBWP1, MSC_RBMMODE1, MSC_RBMRP1},
+ {MSC_RBMBWP2, MSC_RBMMODE2, MSC_RBMRP2},
+};
+
+#define CTX_MASK 0xfff
+#define NULL_CONTEXT CTX_MASK
+
+#define QOF_ORDER 3 /* 32kB queue overflow buffer */
+#define QOF_SIZE ((1<<QOF_ORDER)*PAGE_SIZE)
+#define QOF_ELT_SIZE 8 /* size of each queue element */
+#define QOF_REDZONE_SZ 8192 /* 8kB redzone, imposed by hardware */
+#define QOF_NELT (QOF_SIZE / QOF_ELT_SIZE)
+#define QOF_RED_NELT (QOF_REDZONE_SZ / QOF_ELT_SIZE)
+#define QOF_GREEN_NELT ((QOF_SIZE - QOF_REDZONE_SZ) / QOF_ELT_SIZE)
+
+#define MAKE_QBMPTR(qof, size) \
+ (MKFIELD((qof) >> 19, MSC_QBMP_BP) \
+ + MKFIELD((qof) >> 3, MSC_QBMP_WP) \
+ + MKFIELD(((qof) + (size) - 1) >> 13, MSC_QBMP_LIM))
+
+#define QBM_UPDATE_WP(wp) \
+ MSC_OUT(MSC_QBMPTR, INSFIELD(MSC_IN(MSC_QBMPTR), (unsigned)(wp) >> 3, \
+ MSC_QBMP_WP))
+
+/* Send queue overflow buffer structure */
+struct qof_elt {
+ unsigned info;
+ unsigned data;
+};
+
+/* Fields in qof_elt.info */
+#define QOF_QUEUE_SH 24 /* queue bits start at bit 24 */
+#define QOF_QUEUE_M 0x1f /* 5 bits wide */
+#define QOF_ENDBIT 1 /* end bit in bit 0 */
+
+static struct qof_elt *qof_base=NULL; /* start of overflow buffer */
+static unsigned long qof_phys; /* physical start adrs of overflow buffer */
+static struct qof_elt *qof_rp; /* read pointer for refills */
+static struct qof_elt *qof_new; /* first element we haven't yet looked at */
+static int qof_present[NSENDQUEUES];/* # elts for each q in [qof_rp,qof_new) */
+
+/* this is used to flag when the msc is blocked, so we can't send
+ messages on it without the possability of deadlock */
+int msc_blocked = 0;
+int block_parallel_tasks = 0;
+
+static int qbm_full_counter = 0;
+
+#define INTR_LIMIT 10000
+static int intr_counter = 0;
+static unsigned intr_mask;
+
+#define DUMMY_RINGBUF_ORDER 5
+#define DUMMY_RINGBUF_SIZE ((1<<DUMMY_RINGBUF_ORDER)*PAGE_SIZE)
+
+/*
+ * The system ring buffer, used for inter-kernel comms
+ */
+struct ringbuf_struct system_ringbuf = {NULL,NULL,SYSTEM_RINGBUF_ORDER,0,0,0,0};
+struct ringbuf_struct dummy_ringbuf = {NULL,NULL,DUMMY_RINGBUF_ORDER,0,0,0,0};
+unsigned system_read_ptr = (SYSTEM_RINGBUF_SIZE>>5)-1;
+unsigned dummy_read_ptr = (DUMMY_RINGBUF_SIZE>>5)-1;
+
+#define SQ_NEW_MODE(mode) do { \
+ MSC_OUT(MSC_SQCTRL, ((MSC_IN(MSC_SQCTRL) & ~MSC_SQC_RMODE) \
+ | MSC_SQC_RMODE_ ## mode)); \
+ while ((MSC_IN(MSC_SQCTRL) & MSC_SQC_MODE) != MSC_SQC_MODE_ ## mode) \
+ /* hang */ ; \
+} while (0)
+
+/* Repack the queue overflow buffer if >= this many already-used entries */
+#define REPACK_THRESH 64
+
+
+static void refill_sq(void);
+static void repack_qof(void);
+static void shuffle_qof(void);
+static void async_callback(int, unsigned long, int, int);
+
+
+static void mask_all_interrupts(void)
+{
+ /* disable all MSC+ interrupts */
+ MSC_OUT(MSC_INTR,
+ (AP_SET_INTR_MASK << MSC_INTR_QBMFUL_SH) |
+ (AP_SET_INTR_MASK << MSC_INTR_SQFILL_SH) |
+ (AP_SET_INTR_MASK << MSC_INTR_RBMISS_SH) |
+ (AP_SET_INTR_MASK << MSC_INTR_RBFULL_SH) |
+ (AP_SET_INTR_MASK << MSC_INTR_RMASF_SH) |
+ (AP_SET_INTR_MASK << MSC_INTR_RMASE_SH) |
+ (AP_SET_INTR_MASK << MSC_INTR_SMASF_SH) |
+ (AP_SET_INTR_MASK << MSC_INTR_SMASE_SH));
+}
+
+static inline int valid_task(struct task_struct *tsk)
+{
+ return(tsk &&
+ !((tsk)->flags & PF_EXITING) &&
+ tsk->mm &&
+ tsk->mm->context != NO_CONTEXT);
+}
+
+static inline unsigned long apmmu_get_raw_ctable_ptr(void)
+{
+ unsigned int retval;
+
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (retval) :
+ "r" (APMMU_CTXTBL_PTR),
+ "i" (ASI_M_MMUREGS));
+ return (retval);
+}
+
+static void mc_tlb_map(unsigned phys_page,unsigned vpage,int context)
+{
+ unsigned long long *tlb4k;
+ unsigned long long new_entry;
+ unsigned long *new_entryp = (unsigned long *)&new_entry;
+ tlb4k = ((unsigned long long *)MC_MMU_TLB4K) + (vpage & 0xFF);
+ new_entryp[0] = (phys_page&~7) >> 3;
+ new_entryp[1] = ((phys_page & 7) << 29) | (((vpage>>8)&0xFFF) << 17) |
+ (context << 5) | 0x13;
+ tlb4k[0] = new_entry;
+#if DEBUG
+ printk("mc_tlb_map(%x,%x,%x) %x %x at %x\n",
+ phys_page,vpage,context,new_entryp[0],new_entryp[1],tlb4k);
+#endif
+}
+
+static void mc_tlb_unmap(unsigned vpage)
+{
+ unsigned long long *tlb4k = (unsigned long long *)MC_MMU_TLB4K;
+ tlb4k = ((unsigned long long *)MC_MMU_TLB4K) + (vpage & 0xFF);
+ tlb4k[0] = 0;
+}
+
+void mc_tlb_init(void)
+{
+ unsigned long long *tlb256k, *tlb4k;
+ int i;
+
+ tlb4k = (unsigned long long *)MC_MMU_TLB4K;
+ for (i = MC_MMU_TLB4K_SIZE; i > 0; --i)
+ *tlb4k++ = 0;
+ tlb256k = (unsigned long long *)MC_MMU_TLB256K;
+ for (i = MC_MMU_TLB256K_SIZE; i > 0; --i)
+ *tlb256k++ = 0;
+}
+
+void ap_msc_init(void)
+{
+ int i, flags, res;
+ unsigned int qp;
+
+ bif_add_debug_key('M',msc_status,"MSC+ status");
+
+#if DEBUG
+ printk("MSC+ version %x\n", MSC_IN(MSC_VERSION));
+ printk("MC+ version %x\n", MC_IN(MC_VERSION));
+#endif
+
+ mc_tlb_init();
+
+ /* Set the MC's copy of the context table pointer */
+ MC_OUT(MC_CTP, apmmu_get_raw_ctable_ptr());
+
+ /* Initialize the send queue pointers */
+ qp = MSC_SQPTR0;
+ for (i = 0; i < 5; ++i) {
+ MSC_OUT(qp, ((send_queues[i].size == 64? MSC_SQP_MODE: 0)
+ + ((send_queues[i].base >> 5) << MSC_SQP_BP_SH)));
+ qp += (MSC_SQPTR1 - MSC_SQPTR0);
+ }
+
+ /* Initialize the send queue RAM */
+ for (i = 0; i < 256; ++i)
+ MSC_OUT(MSC_SQRAM + i * 8, -1);
+
+ if (!qof_base) {
+ qof_base = (struct qof_elt *) __get_free_pages(GFP_ATOMIC, QOF_ORDER, 0);
+ for (i = MAP_NR(qof_base); i <= MAP_NR(((char*)qof_base)+QOF_SIZE-1);++i)
+ set_bit(PG_reserved, &mem_map[i].flags);
+ }
+
+ qof_phys = mmu_v2p((unsigned long) qof_base);
+ MSC_OUT(MSC_QBMPTR, MAKE_QBMPTR((unsigned long)qof_base, QOF_SIZE));
+ qof_rp = qof_base;
+ qof_new = qof_base;
+ for (i = 0; i < NSENDQUEUES; ++i)
+ qof_present[i] = 0;
+
+ SQ_NEW_MODE(NORMAL); /* Set the send queue to normal mode */
+
+ /* Register interrupt handler for MSC+ */
+ save_flags(flags); cli();
+ res = request_irq(APMSC_IRQ, msc_interrupt_11, SA_INTERRUPT,
+ "apmsc", NULL);
+ if (res != 0)
+ printk("couldn't register MSC interrupt 11: error=%d\n", res);
+ res = request_irq(APMAS_IRQ, msc_interrupt_9, SA_INTERRUPT,
+ "apmas", NULL);
+ if (res != 0)
+ printk("couldn't register MSC interrupt 9: error=%d\n", res);
+ restore_flags(flags);
+
+ MSC_OUT(MSC_MASCTRL, 0);
+
+ /* Enable all MSC+ interrupts (for now) */
+ MSC_OUT(MSC_INTR,
+ (AP_CLR_INTR_MASK << MSC_INTR_QBMFUL_SH) |
+ (AP_CLR_INTR_MASK << MSC_INTR_SQFILL_SH) |
+ (AP_CLR_INTR_MASK << MSC_INTR_RBMISS_SH) |
+ (AP_CLR_INTR_MASK << MSC_INTR_RBFULL_SH) |
+ (AP_CLR_INTR_MASK << MSC_INTR_RMASF_SH) |
+ (AP_CLR_INTR_MASK << MSC_INTR_RMASE_SH) |
+ (AP_CLR_INTR_MASK << MSC_INTR_SMASF_SH) |
+ (AP_CLR_INTR_MASK << MSC_INTR_SMASE_SH));
+
+ /* setup invalid contexts */
+ for (i=0; i<MSC_NR_RBUFS; i++)
+ MSC_OUT(ringbufs[i].rbmmode, NULL_CONTEXT);
+
+ MSC_OUT(MSC_SMASREG,0);
+ MSC_OUT(MSC_RMASREG,0);
+
+ if (!system_ringbuf.ringbuf) {
+ system_ringbuf.ringbuf =
+ (void *)__get_free_pages(GFP_ATOMIC,SYSTEM_RINGBUF_ORDER,0);
+ for (i=MAP_NR(system_ringbuf.ringbuf);
+ i<=MAP_NR(system_ringbuf.ringbuf+SYSTEM_RINGBUF_SIZE-1);i++)
+ set_bit(PG_reserved, &mem_map[i].flags);
+ system_ringbuf.write_ptr = mmu_v2p((unsigned)system_ringbuf.ringbuf)<<1;
+ }
+
+ if (!dummy_ringbuf.ringbuf) {
+ dummy_ringbuf.ringbuf =
+ (void *)__get_free_pages(GFP_ATOMIC,DUMMY_RINGBUF_ORDER,0);
+ for (i=MAP_NR(dummy_ringbuf.ringbuf);
+ i<=MAP_NR(dummy_ringbuf.ringbuf+DUMMY_RINGBUF_SIZE-1);i++)
+ set_bit(PG_reserved, &mem_map[i].flags);
+ dummy_ringbuf.write_ptr = mmu_v2p((unsigned)dummy_ringbuf.ringbuf)<<1;
+ }
+}
+
+
+static inline void qbmfill_interrupt(void)
+{
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_MASK << MSC_INTR_QBMFUL_SH);
+ intr_mask &= ~(AP_INTR_REQ << MSC_INTR_QBMFUL_SH);
+
+ SQ_NEW_MODE(THRU); /* set send queue ctrlr to through mode */
+ refill_sq(); /* refill the send queues */
+ SQ_NEW_MODE(NORMAL); /* set send queue ctrlr back to normal mode */
+ /* dismiss the interrupt */
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << MSC_INTR_SQFILL_SH);
+}
+
+static inline void qbmful_interrupt(void)
+{
+ int nvalid, ntot, q;
+
+ qbm_full_counter++;
+
+#if DEBUG
+ printk("qbm full interrupt\n");
+#endif
+
+ SQ_NEW_MODE(THRU); /* set send queue ctrlr to through mode */
+ /* stuff as much as we can into the send queue RAM */
+ refill_sq();
+ /* count how many valid words are left in the qof buffer */
+ nvalid = 0;
+ for (q = 0; q < NSENDQUEUES; ++q)
+ nvalid += qof_present[q];
+ if (nvalid >= QOF_GREEN_NELT) {
+#if DEBUG
+ printk("send queue overflow buffer overflow\n");
+#endif
+ MSC_OUT(MSC_INTR, AP_SET_INTR_MASK << MSC_INTR_QBMFUL_SH);
+ intr_mask |= (AP_INTR_REQ << MSC_INTR_QBMFUL_SH);
+ need_resched = 1;
+ block_parallel_tasks = 1;
+ mark_bh(TQUEUE_BH);
+ }
+ ntot = qof_new - qof_rp; /* total # words of qof buf used */
+ if (ntot - nvalid >= REPACK_THRESH || ntot >= QOF_GREEN_NELT
+ || (ntot > nvalid && nvalid >= QOF_GREEN_NELT - REPACK_THRESH)) {
+ repack_qof();
+ if (qof_new - qof_rp != nvalid) {
+ printk("MSC: qof_present wrong\n");
+ }
+ } else if (nvalid > 0) {
+ shuffle_qof();
+ }
+ /* N.B. if nvalid == 0, msc_refill_sq has already reset the QBM's WP */
+ SQ_NEW_MODE(NORMAL);
+
+ /* dismiss the interrupt */
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << MSC_INTR_QBMFUL_SH);
+}
+
+
+static void msc_interrupt_11(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned intr;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+
+ if (intr_counter++ == INTR_LIMIT) {
+ mask_all_interrupts();
+ printk("too many MSC interrupts\n");
+ restore_flags(flags);
+ return;
+ }
+
+ intr = MSC_IN(MSC_INTR);
+
+#if DEBUG
+ printk("CID(%d) msc_interrupt_11: intr = %x\n", mpp_cid(), intr);
+#endif
+
+ if (intr & (AP_INTR_REQ << MSC_INTR_RBMISS_SH)) {
+ int context;
+ context = MSC_IN(MSC_RMASREG) >> 20;
+
+ msc_set_ringbuf(context);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << MSC_INTR_RBMISS_SH);
+ }
+
+ if (intr & (AP_INTR_REQ << MSC_INTR_RBFULL_SH)) {
+ int context = MSC_IN(MSC_RMASREG) >> 20;
+ msc_update_read_ptr(context,1);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << MSC_INTR_RBFULL_SH);
+ }
+
+ if (intr & (AP_INTR_REQ << MSC_INTR_SQFILL_SH)) {
+ qbmfill_interrupt();
+ }
+
+ if (intr & (AP_INTR_REQ << MSC_INTR_QBMFUL_SH)) {
+ qbmful_interrupt();
+ }
+
+ restore_flags(flags);
+}
+
+
+void msc_timer(void)
+{
+ /* unmask all the interrupts that are supposed to be unmasked */
+ intr_counter = 0;
+}
+
+/* assumes NSENDQUEUES == 5 */
+static int log2tbl[32] = {
+ -1, 0, 1, -1, 2, -1, -1, -1,
+ 3, -1, -1, -1, -1, -1, -1, -1,
+ 4, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static unsigned long direct_queues[NSENDQUEUES][2] = {
+ { MSC_SYSTEM_DIRECT, MSC_SYSTEM_DIRECT_END },
+ { MSC_REMOTE_DIRECT, MSC_REMOTE_DIRECT_END },
+ { MSC_USER_DIRECT, MSC_USER_DIRECT_END },
+ { MSC_REMREPLY_DIRECT, MSC_REMREPLY_DIRECT_END },
+ { MSC_REPLY_DIRECT, MSC_REPLY_DIRECT_END }
+};
+
+/*
+ * Copy entries from the queue overflow buffer back to the send queue.
+ * This must be called with the send queue controller in THRU mode.
+ */
+static void refill_sq(void)
+{
+ int notfull, use_old;
+ int q, kept_some;
+ int sqp, sqc;
+ struct qof_elt *rp, *qof_wp;
+ int freew[NSENDQUEUES]; /* # free words in each queue */
+
+ /* give parallel tasks another chance */
+ block_parallel_tasks = 0;
+
+ /* get the qbm's write pointer */
+ qof_wp = qof_base + (EXTFIELD(MSC_IN(MSC_QBMPTR), MSC_QBMP_WP)
+ & ((QOF_SIZE - 1) >> 3));
+#if 0
+ printk("refill_sq: rp=%p new=%p wp=%p pres=[",
+ qof_rp, qof_new, qof_wp);
+ for (q = 0; q < NSENDQUEUES; ++q)
+ printk("%d ", qof_present[q]);
+ printk("]\n");
+#endif
+
+ /* work out which send queues and aren't full */
+ notfull = 0;
+ use_old = 0;
+ for (q = 0; q < NSENDQUEUES; ++q) {
+ sqp = MSC_IN(MSC_SQPTR0 + q * 8);
+ freew[q] = (EXTFIELD(sqp, MSC_SQP_RP) - EXTFIELD(sqp, MSC_SQP_WP) - 1)
+ & ((sqp & MSC_SQP_MODE)? 0x3f: 0x1f);
+ if (freew[q] > 0)
+ notfull |= 1 << (q + QOF_QUEUE_SH);
+ use_old += (freew[q] < qof_present[q])? freew[q]: qof_present[q];
+ }
+
+ /*
+ * If there are useful entries in the old part of the overflow
+ * queue, process them.
+ */
+ kept_some = 0;
+ for (rp = qof_rp; rp < qof_new && use_old > 0; ++rp) {
+ if (rp->info & notfull) {
+ /* Here's one we can stuff back into the send queue */
+ q = log2tbl[EXTFIELD(rp->info, QOF_QUEUE)];
+ if (q < 0) {
+ printk("bad queue bits in qof info (%x) at %p\n",
+ rp->info, rp);
+ /* XXX just ignore this entry - should never happen */
+ rp->info = 0;
+ continue;
+ }
+ MSC_OUT(direct_queues[q][rp->info & QOF_ENDBIT],rp->data);
+ if (--freew[q] == 0)
+ notfull &= ~(1 << (q + QOF_QUEUE_SH));
+ --qof_present[q];
+ --use_old;
+ rp->info = 0;
+ } else if (!kept_some && rp->info != 0) {
+ qof_rp = rp;
+ kept_some = 1;
+ }
+ }
+
+ /* Trim off any further already-used items. */
+ if (!kept_some) {
+ for (; rp < qof_new; ++rp) {
+ if (rp->info) {
+ qof_rp = rp;
+ kept_some = 1;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Now process everything that's arrived since we last updated qof_new.
+ */
+ for (rp = qof_new; rp < qof_wp; ++rp) {
+ if (rp->info == 0)
+ continue;
+ q = log2tbl[EXTFIELD(rp->info, QOF_QUEUE)];
+ if (q < 0) {
+ printk("bad queue bits in qof info (%x) at %p\n", rp->info, rp);
+ /* XXX just ignore this entry - should never happen */
+ rp->info = 0;
+ continue;
+ }
+ if (rp->info & notfull) {
+ /* Another one to stuff back into the send queue. */
+ MSC_OUT(direct_queues[q][rp->info & QOF_ENDBIT],rp->data);
+ if (--freew[q] == 0)
+ notfull &= ~(1 << (q + QOF_QUEUE_SH));
+ rp->info = 0;
+ } else {
+ ++qof_present[q];
+ if (!kept_some) {
+ qof_rp = rp;
+ kept_some = 1;
+ }
+ }
+ }
+
+ /* Update state and the MSC queue-spill flags. */
+ if (!kept_some) {
+ /* queue is empty; avoid unnecessary overflow interrupt later */
+ qof_rp = qof_new = qof_base;
+ QBM_UPDATE_WP(mmu_v2p((unsigned long)qof_base));
+ } else {
+ qof_new = qof_wp;
+ }
+
+ sqc = MSC_IN(MSC_SQCTRL);
+ for (q = 0; q < NSENDQUEUES; ++q)
+ if (qof_present[q] == 0 && freew[q] > 0)
+ sqc &= ~(1 << (q + MSC_SQC_SPLF_SH));
+ MSC_OUT(MSC_SQCTRL, sqc);
+}
+
+/*
+ * Copy the valid entries from their current position
+ * in the queue overflow buffer to the beginning.
+ * This must be called with the send queue controller in THRU or BLOCKING mode.
+ */
+static void repack_qof(void)
+{
+ struct qof_elt *rp, *wp;
+
+ wp = qof_base;
+ for (rp = qof_rp; rp < qof_new; ++rp) {
+ if (rp->info) {
+ if (rp > wp)
+ *wp = *rp;
+ ++wp;
+ }
+ }
+ qof_rp = qof_base;
+ qof_new = wp;
+ QBM_UPDATE_WP(wp);
+}
+
+/*
+ * Copy all entries from their current position
+ * in the queue overflow buffer to the beginning.
+ * This must be called with the send queue controller in THRU or BLOCKING mode.
+ */
+static void shuffle_qof(void)
+{
+ int n;
+
+ n = qof_new - qof_rp;
+ memmove(qof_base, qof_rp, n * sizeof(struct qof_elt));
+ qof_rp = qof_base;
+ qof_new = qof_base + n;
+ QBM_UPDATE_WP(qof_new);
+}
+
+static inline void handle_signal(int context,unsigned vaddr)
+{
+ int signum = (vaddr - MSC_REM_SIGNAL) >> PAGE_SHIFT;
+ int taskid = MPP_CTX_TO_TASK(context);
+ if (MPP_IS_PAR_TASK(taskid) && valid_task(task[taskid])) {
+ send_sig(signum,task[taskid],1);
+#if DEBUG
+ printk("CID(%d) sent signal %d to task %d\n",mpp_cid(),signum,taskid);
+#endif
+ }
+}
+
+
+/*
+ * fail a msc write operation. We use Pauls dirty tlb trick to avoide
+ * the msc hardware bugs
+ */
+static void fail_write(int context,int intr,unsigned vaddr)
+{
+ int tsk = MPP_CTX_TO_TASK(context);
+ int vpage = vaddr >> 12;
+#if DEBUG
+ printk("fail write tsk=%d intr=%x vaddr=%x RMASREG=%x errproc=%x\n",
+ tsk,intr,vaddr,MSC_IN(MSC_RMASREG),MSC_IN(MSC_RHDERRPROC));
+#endif
+
+ mc_tlb_map(0x800000 | (mmu_v2p((unsigned)dummy_ringbuf.ringbuf)>>12),
+ vpage,context);
+ MSC_OUT(MSC_MASCTRL, MSC_IN(MSC_MASCTRL) & ~MSC_MASC_RFEXIT);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << intr);
+
+ mc_tlb_unmap(vpage);
+
+ if (MPP_IS_PAR_CTX(context) && valid_task(task[tsk])) {
+ if (vaddr - MSC_REM_SIGNAL < _NSIG*PAGE_SIZE) {
+ handle_signal(context,vaddr);
+ } else {
+ task[tsk]->tss.sig_address = vaddr;
+ task[tsk]->tss.sig_desc = SUBSIG_NOMAPPING;
+ send_sig(SIGSEGV, task[tsk], 1);
+ }
+ }
+}
+
+/*
+ * fail a msc read operation using the tlb trick */
+static void fail_read(int context,int intr,unsigned vaddr)
+{
+ int tsk = MPP_CTX_TO_TASK(context);
+#if DEBUG
+ printk("fail read tsk=%d intr=%x\n",tsk,intr);
+#endif
+
+ mc_tlb_map(0x800000 | (mmu_v2p((unsigned)dummy_ringbuf.ringbuf)>>12),
+ vaddr>>12,context);
+ MSC_OUT(MSC_MASCTRL, MSC_IN(MSC_MASCTRL) & ~MSC_MASC_SFEXIT);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << intr);
+
+ mc_tlb_unmap(vaddr>>12);
+
+ if (MPP_IS_PAR_CTX(context) && valid_task(task[tsk])) {
+ if (vaddr - MSC_REM_SIGNAL < _NSIG*PAGE_SIZE) {
+ handle_signal(context,vaddr);
+ } else {
+ task[tsk]->tss.sig_address = vaddr;
+ task[tsk]->tss.sig_desc = SUBSIG_NOMAPPING;
+ send_sig(SIGSEGV, task[tsk], 1);
+ }
+ }
+}
+
+static void async_callback(int tsk,unsigned long vaddr,int write,int ret)
+{
+ unsigned flags;
+ save_flags(flags); cli();
+
+ msc_blocked--;
+ if (write) {
+ intr_mask &= ~(AP_INTR_REQ << MSC_INTR_RMASF_SH);
+ if (ret) {
+ fail_write(MPP_TASK_TO_CTX(tsk),MSC_INTR_RMASF_SH,vaddr);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_MASK << MSC_INTR_RMASF_SH);
+ restore_flags(flags);
+ return;
+ }
+ MSC_OUT(MSC_MASCTRL, MSC_IN(MSC_MASCTRL) & ~MSC_MASC_RFEXIT);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << MSC_INTR_RMASF_SH);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_MASK << MSC_INTR_RMASF_SH);
+ } else {
+ intr_mask &= ~(AP_INTR_REQ << MSC_INTR_SMASF_SH);
+ if (ret) {
+ fail_read(MPP_TASK_TO_CTX(tsk),MSC_INTR_SMASF_SH,vaddr);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_MASK << MSC_INTR_SMASF_SH);
+ restore_flags(flags);
+ return;
+ }
+ MSC_OUT(MSC_MASCTRL, MSC_IN(MSC_MASCTRL) & ~MSC_MASC_SFEXIT);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << MSC_INTR_SMASF_SH);
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_MASK << MSC_INTR_SMASF_SH);
+ }
+ restore_flags(flags);
+}
+
+
+
+static inline void msc_write_fault(void)
+{
+ unsigned context = MSC_IN(MSC_RMASREG) >> 20;
+ unsigned vaddr = MSC_IN(MSC_RMASTWP)<<12;
+
+ if (context == SYSTEM_CONTEXT) {
+ fail_write(context,MSC_INTR_RMASF_SH,vaddr);
+ show_mapping_ctx(0,context,vaddr);
+ printk("ERROR: system write fault at %x\n",vaddr);
+ return;
+ }
+
+ if (vaddr - MSC_REM_SIGNAL < _NSIG*PAGE_SIZE) {
+ fail_write(context,MSC_INTR_RMASF_SH,vaddr);
+ return;
+ }
+
+ if (MPP_IS_PAR_CTX(context)) {
+ int tsk = MPP_CTX_TO_TASK(context);
+ if (valid_task(task[tsk]) && task[tsk]->ringbuf) {
+ MSC_OUT(MSC_INTR,
+ AP_SET_INTR_MASK << MSC_INTR_RMASF_SH);
+ intr_mask |= (AP_INTR_REQ << MSC_INTR_RMASF_SH);
+#if DEBUG
+ show_mapping_ctx(0,context,vaddr);
+#endif
+ msc_blocked++;
+ async_fault(vaddr,1,tsk,async_callback);
+ return;
+ }
+ }
+
+#if DEBUG
+ printk("CID(%d) mas write fault context=%x vaddr=%x\n",
+ mpp_cid(),context,vaddr);
+#endif
+
+ fail_write(context,MSC_INTR_RMASF_SH,vaddr);
+}
+
+
+static inline void msc_read_fault(void)
+{
+ unsigned context = MSC_IN(MSC_SMASREG) >> 20;
+ unsigned vaddr = MSC_IN(MSC_SMASTWP)<<12;
+
+ if (context == SYSTEM_CONTEXT) {
+ fail_read(context,MSC_INTR_SMASF_SH,vaddr);
+ show_mapping_ctx(0,context,vaddr);
+ printk("ERROR: system read fault at %x\n",vaddr);
+ return;
+ }
+
+ if (MPP_IS_PAR_CTX(context)) {
+ int tsk = MPP_CTX_TO_TASK(context);
+
+ if (vaddr - MSC_REM_SIGNAL < _NSIG*PAGE_SIZE) {
+ fail_read(context,MSC_INTR_SMASF_SH,vaddr);
+ return;
+ }
+
+ if (valid_task(task[tsk]) && task[tsk]->ringbuf) {
+ MSC_OUT(MSC_INTR, AP_SET_INTR_MASK << MSC_INTR_SMASF_SH);
+ intr_mask |= (AP_INTR_REQ << MSC_INTR_SMASF_SH);
+ msc_blocked++;
+ async_fault(vaddr,0,tsk,async_callback);
+ return;
+ }
+ }
+
+#if DEBUG
+ printk("CID(%d) mas read fault context=%x vaddr=%x\n",
+ mpp_cid(),context,vaddr);
+#endif
+
+ fail_read(context,MSC_INTR_SMASF_SH,vaddr);
+}
+
+
+
+static void msc_interrupt_9(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ unsigned intr, cnt, r;
+
+ save_flags(flags); cli();
+
+ if (intr_counter++ == INTR_LIMIT) {
+ mask_all_interrupts();
+ printk("too many MSC interrupts\n");
+ restore_flags(flags);
+ return;
+ }
+
+ intr = MSC_IN(MSC_INTR) & ~intr_mask;
+
+#if DEBUG
+ printk("CID(%d) msc_interrupt_9: intr = %x\n", mpp_cid(), intr);
+#endif
+
+ if (intr & (AP_INTR_REQ << MSC_INTR_RMASF_SH)) {
+ msc_write_fault();
+ }
+
+ if (intr & (AP_INTR_REQ << MSC_INTR_SMASF_SH)) {
+ msc_read_fault();
+ }
+
+ if (intr & (AP_INTR_REQ << MSC_INTR_RMASE_SH)) {
+ printk("recv mas error interrupt (write)\n");
+ printk("masctrl = %x\n", MSC_IN(MSC_MASCTRL));
+ printk("rmasadr = %x %x\n", MSC_IN(MSC_RMASADR),
+ MSC_IN(MSC_RMASADR + 4));
+ printk("rmastwp = %x\n", MSC_IN(MSC_RMASTWP));
+ printk("rmasreg = %x\n", MSC_IN(MSC_RMASREG));
+ r = MSC_IN(MSC_RMASREG);
+ if ((r & MSC_MASR_AVIO) || (r & MSC_MASR_CMD) != MSC_MASR_CMD_XFER)
+ /* throw away the rest of the incoming data */
+ MSC_OUT(MSC_RHDERRPROC, 0);
+ /* clear the interrupt */
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << MSC_INTR_RMASE_SH);
+ }
+
+ if (intr & (AP_INTR_REQ << MSC_INTR_SMASE_SH)) {
+ printk("send mas error interrupt (read)\n");
+ printk("masctrl = %x\n", MSC_IN(MSC_MASCTRL));
+ printk("smasadr = %x %x\n", MSC_IN(MSC_SMASADR),
+ MSC_IN(MSC_SMASADR + 4));
+ printk("smascnt = %x\n", MSC_IN(MSC_SMASCNT));
+ printk("smastwp = %x\n", MSC_IN(MSC_SMASTWP));
+ printk("smasreg = %x\n", MSC_IN(MSC_SMASREG));
+ /* supply dummy data */
+ cnt = MSC_IN(MSC_SMASCNT);
+ switch (MSC_IN(MSC_SMASREG) & MSC_MASR_CMD) {
+ case MSC_MASR_CMD_XFER:
+ MSC_OUT(MSC_HDGERRPROC, (EXTFIELD(cnt, MSC_SMCT_MCNT)
+ + EXTFIELD(cnt, MSC_SMCT_ICNT)));
+ break;
+ /* case remote read: */
+ case MSC_MASR_CMD_FOP:
+ case MSC_MASR_CMD_CSI:
+ MSC_OUT(MSC_HDGERRPROC, 1);
+ break;
+ }
+ /* clear interrupt */
+ MSC_OUT(MSC_INTR, AP_CLR_INTR_REQ << MSC_INTR_SMASF_SH);
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * remove access to a tasks ring buffer
+ */
+void msc_unset_ringbuf(int i)
+{
+ int ctx = MSC_IN(ringbufs[i].rbmmode) & CTX_MASK;
+ int tsk = MPP_CTX_TO_TASK(ctx);
+ struct ringbuf_struct *rbuf;
+
+#if DEBUG
+ printk("msc_unset_ringbuf(%d) %x\n",i,ctx);
+#endif
+
+ MSC_OUT(ringbufs[i].rbmmode,NULL_CONTEXT);
+ if (ctx == SYSTEM_CONTEXT) {
+ rbuf = &system_ringbuf;
+ } else if (ctx != NULL_CONTEXT && MPP_IS_PAR_CTX(ctx) &&
+ valid_task(task[tsk]) && task[tsk]->ringbuf) {
+ rbuf = task[tsk]->ringbuf;
+ } else if (ctx != NULL_CONTEXT && MPP_IS_PAR_CTX(ctx) &&
+ valid_task(task[tsk]) && task[tsk]->aplib) {
+ rbuf = &system_ringbuf;
+ } else {
+ rbuf = &dummy_ringbuf;
+ }
+
+ rbuf->write_ptr = MSC_IN(ringbufs[i].rbmbwp);
+}
+
+static void msc_update_read_ptr(int context,int overflow)
+{
+ int i;
+ unsigned new_read_ptr;
+
+ for (i=0;i<NR_RBUFS;i++) {
+ if ((MSC_IN(ringbufs[i].rbmmode)&CTX_MASK) == context) break;
+ }
+ if (i == NR_RBUFS) {
+ printk("didn't find context %d in msc_update_read_ptr()\n",
+ context);
+ return;
+ }
+
+ if (context == SYSTEM_CONTEXT) {
+ tnet_check_completion();
+ if (overflow && MSC_IN(ringbufs[i].rbmrp) == system_read_ptr)
+ printk("system ringbuffer overflow\n");
+ new_read_ptr = system_read_ptr;
+ } else if (MPP_IS_PAR_CTX(context) &&
+ valid_task(task[MPP_CTX_TO_TASK(context)]) &&
+ task[MPP_CTX_TO_TASK(context)]->ringbuf) {
+ struct task_struct *tsk = task[MPP_CTX_TO_TASK(context)];
+ struct _kernel_cap_shared *_kernel;
+ unsigned soft_read_ptr;
+ unsigned octx;
+
+ octx = apmmu_get_context();
+ if (octx != context)
+ apmmu_set_context(context);
+ _kernel = (struct _kernel_cap_shared *)(RBUF_VBASE + RBUF_SHARED_PAGE_OFF);
+ soft_read_ptr = _kernel->rbuf_read_ptr;
+ if (octx != context)
+ apmmu_set_context(octx);
+
+ if (overflow && MSC_IN(ringbufs[i].rbmrp) == soft_read_ptr) {
+ /* send them a SIGLOST and wipe their ring buffer */
+ printk("ring buffer overflow for %s ctx=%x\n",
+ tsk->comm,context);
+ send_sig(SIGLOST,tsk,1);
+ soft_read_ptr--;
+ }
+ new_read_ptr = soft_read_ptr;
+ } else if (MPP_IS_PAR_CTX(context) &&
+ valid_task(task[MPP_CTX_TO_TASK(context)]) &&
+ task[MPP_CTX_TO_TASK(context)]->aplib) {
+ tnet_check_completion();
+ if (overflow && MSC_IN(ringbufs[i].rbmrp) == system_read_ptr)
+ printk("system ringbuffer overflow\n");
+ new_read_ptr = system_read_ptr;
+ } else {
+ dummy_read_ptr = MSC_IN(ringbufs[i].rbmrp) - 1;
+ new_read_ptr = dummy_read_ptr - 1;
+#if DEBUG
+ if (overflow)
+ printk("reset dummy ring buffer for context %x\n",
+ context);
+#endif
+ }
+
+
+ MSC_OUT(ringbufs[i].rbmrp,new_read_ptr);
+}
+
+/*
+ * give a task one of the system ring buffers
+ * this is called on a context miss interrupt, so we can assume that
+ * the tasks context is not currently set in one of the ringbufs
+ */
+static void msc_set_ringbuf(int context)
+{
+ int i;
+ int ctx;
+ int mode;
+ unsigned write_ptr;
+ static unsigned next_ctx = 0;
+ struct ringbuf_struct *rbuf;
+
+ if (context == SYSTEM_CONTEXT) {
+ rbuf = &system_ringbuf;
+ } else if (MPP_IS_PAR_CTX(context) &&
+ valid_task(task[MPP_CTX_TO_TASK(context)]) &&
+ task[MPP_CTX_TO_TASK(context)]->ringbuf) {
+ struct task_struct *tsk = task[MPP_CTX_TO_TASK(context)];
+ rbuf = tsk->ringbuf;
+ } else if (MPP_IS_PAR_CTX(context) &&
+ valid_task(task[MPP_CTX_TO_TASK(context)]) &&
+ task[MPP_CTX_TO_TASK(context)]->aplib) {
+ rbuf = &system_ringbuf;
+ } else {
+ /* use the dummy ring buffer */
+ rbuf = &dummy_ringbuf;
+ }
+
+ for (i=0;i<NR_RBUFS; i++) {
+ ctx = MSC_IN(ringbufs[i].rbmmode)&CTX_MASK;
+ if (ctx == NULL_CONTEXT) break;
+ }
+ if (i == NR_RBUFS) {
+ i = next_ctx;
+ next_ctx = (i+1)%NR_RBUFS;
+ }
+
+ ctx = MSC_IN(ringbufs[i].rbmmode)&CTX_MASK;
+ if (ctx != NULL_CONTEXT) msc_unset_ringbuf(i);
+
+ write_ptr = rbuf->write_ptr;
+ mode = (rbuf->order - 5) >> 1;
+
+ MSC_OUT(ringbufs[i].rbmmode,context | (mode << 12));
+ MSC_OUT(ringbufs[i].rbmbwp,write_ptr);
+
+ if (rbuf == &system_ringbuf) {
+ MSC_OUT(ringbufs[i].rbmrp,system_read_ptr);
+ } else {
+ msc_update_read_ptr(context,0);
+ }
+
+#if DEBUG
+ printk("CID(%d) mapped ringbuf for context %d in slot %d\n",
+ mpp_cid(),context,i);
+#endif
+}
+
+
+/*
+ * this is called when a task exits
+*/
+void exit_msc(struct task_struct *tsk)
+{
+ int i;
+
+ if (!MPP_IS_PAR_TASK(tsk->taskid))
+ return;
+
+#if DEBUG
+ printk("exit_msc(%d) ctx=%d\n",tsk->taskid,tsk->mm->context);
+#endif
+
+ for (i=0;i<NR_RBUFS; i++) {
+ int ctx = MSC_IN(ringbufs[i].rbmmode)&CTX_MASK;
+ if (ctx == MPP_TASK_TO_CTX(tsk->taskid))
+ msc_unset_ringbuf(i);
+ }
+ msc_switch_from_check(tsk);
+
+ /* stop it receiving new-style messages */
+ tsk->aplib = NULL;
+
+ exit_ringbuf(tsk);
+}
+
+
+static void msc_sq_pause(void)
+{
+ MSC_OUT(MSC_SQCTRL,MSC_IN(MSC_SQCTRL) | MSC_SQC_PAUSE);
+ while (!(MSC_IN(MSC_SQCTRL) & MSC_SQC_STABLE)) /* wait for stable bit */ ;
+}
+
+static void msc_sq_resume(void)
+{
+ MSC_OUT(MSC_SQCTRL,MSC_IN(MSC_SQCTRL) & ~MSC_SQC_PAUSE);
+}
+
+static void msc_switch_from_check(struct task_struct *tsk)
+{
+ int user_count;
+ unsigned flags;
+ struct ringbuf_struct *rbuf = NULL;
+ int octx, ctx;
+
+ if (valid_task(tsk) && tsk->ringbuf)
+ rbuf = tsk->ringbuf;
+
+ /* it doesn't seem obvious why this field should contain count+1,
+ but it does */
+ user_count = EXTFIELD(MSC_IN(MSC_QWORDCNT),MSC_QWDC_USRCNT) - 1;
+
+ /* check if the user queue count is != 0 */
+ if (user_count == 0) return;
+
+ if (!rbuf)
+ printk("switching from dead task\n");
+
+#if 1
+ printk("saving %d words MSC_QWORDCNT=%x\n",
+ user_count,MSC_IN(MSC_QWORDCNT));
+#endif
+
+ /* bugger - we have to do some messy work */
+ save_flags(flags); cli();
+
+ ctx = MPP_TASK_TO_CTX(tsk->taskid);
+ octx = apmmu_get_context();
+ if (octx != ctx)
+ apmmu_set_context(ctx);
+
+ msc_sq_pause();
+
+ /* remember the expected length of the command - usually (always?) 8 */
+ if (rbuf)
+ rbuf->frag_len = EXTFIELD(MSC_IN(MSC_QWORDCNT),MSC_QWDC_USRLEN);
+
+ /* pull words from the overflow first */
+ if (MSC_IN(MSC_SQCTRL) & MSC_SQC_USERF) {
+ /* we have overflowed */
+ struct qof_elt *qof_wp = qof_base +
+ (EXTFIELD(MSC_IN(MSC_QBMPTR), MSC_QBMP_WP) & ((QOF_SIZE - 1) >> 3));
+ while (qof_wp != qof_rp && user_count) {
+ qof_wp--;
+ /* only grab elements in the user queue */
+ if (qof_wp->info && log2tbl[EXTFIELD(qof_wp->info, QOF_QUEUE)] == 2) {
+ if (qof_wp->info & 1) {
+ printk("MSC: end bit set - yikes!\n");
+ }
+ qof_wp->info = 0;
+ if (rbuf) {
+ rbuf->sq_fragment[--user_count] = qof_wp->data;
+ rbuf->frag_count++;
+ }
+ if (qof_wp < qof_new)
+ qof_present[2]--;
+ }
+ }
+#if DEBUG
+ if (rbuf)
+ printk("pulled %d elements from overflow (%d left)\n",
+ rbuf->frag_count,user_count);
+#endif
+ }
+
+ /* then pull words direct from the msc ram */
+ if (user_count) {
+ int wp = EXTFIELD(MSC_IN(MSC_SQPTR2),MSC_SQP_WP);
+ int i;
+ wp -= user_count;
+ if (wp < 0) wp += send_queues[2].size;
+
+ for (i=0;i<user_count;i++) {
+ int wp2 = (wp + i)%send_queues[2].size;
+ if (rbuf)
+ rbuf->sq_fragment[i + rbuf->frag_count] =
+ MSC_IN(MSC_SQRAM + (send_queues[2].base + wp2)*8);
+ }
+
+ if (rbuf)
+ rbuf->frag_count += user_count;
+
+ MSC_OUT(MSC_SQPTR2,INSFIELD(MSC_IN(MSC_SQPTR2),wp,MSC_SQP_WP));
+#if DEBUG
+ printk("saved %d words from msc ram\n",rbuf->frag_count);
+#endif
+ }
+
+ /* reset the user count to 1 */
+ MSC_OUT(MSC_QWORDCNT,INSFIELD(MSC_IN(MSC_QWORDCNT),1,MSC_QWDC_USRCNT));
+
+ msc_sq_resume();
+
+ if (octx != ctx)
+ apmmu_set_context(octx);
+
+ restore_flags(flags);
+}
+
+static void msc_switch_to_check(struct task_struct *tsk)
+{
+ int i;
+ unsigned flags;
+ int octx, ctx;
+
+ if (!valid_task(tsk) || !tsk->ringbuf)
+ return;
+
+ save_flags(flags); cli();
+
+
+ ctx = MPP_TASK_TO_CTX(tsk->taskid);
+ octx = apmmu_get_context();
+ if (octx != ctx)
+ apmmu_set_context(ctx);
+
+ /* if the task we are switching to has no saved words then
+ we're finished */
+ if (tsk->ringbuf->frag_count == 0) {
+ if (octx != ctx)
+ apmmu_set_context(octx);
+ restore_flags(flags);
+ return;
+ }
+
+
+#if 1
+ printk("frag fill MSC_QWORDCNT=%x frag_count=%d\n",
+ MSC_IN(MSC_QWORDCNT),tsk->ringbuf->frag_count);
+#endif
+
+ /* reset the user length */
+ MSC_OUT(MSC_QWORDCNT,INSFIELD(MSC_IN(MSC_QWORDCNT),
+ tsk->ringbuf->frag_len,
+ MSC_QWDC_USRLEN));
+
+ /* push the words into the direct queue */
+ for (i=0;i<tsk->ringbuf->frag_count;i++)
+ MSC_OUT(MSC_USER_DIRECT,tsk->ringbuf->sq_fragment[i]);
+
+ /* reset the user count */
+ MSC_OUT(MSC_QWORDCNT,INSFIELD(MSC_IN(MSC_QWORDCNT),
+ 1+tsk->ringbuf->frag_count,
+ MSC_QWDC_USRCNT));
+
+#if DEBUG
+ printk("frag fill done MSC_QWORDCNT=%x\n",
+ MSC_IN(MSC_QWORDCNT));
+#endif
+
+ tsk->ringbuf->frag_count = 0;
+ tsk->ringbuf->frag_len = 0;
+ if (octx != ctx)
+ apmmu_set_context(octx);
+ restore_flags(flags);
+}
+
+
+
+void msc_switch_check(struct task_struct *tsk)
+{
+ static int last_task = 0;
+
+ if (last_task == tsk->taskid) return;
+
+ if (MPP_IS_PAR_TASK(last_task))
+ msc_switch_from_check(task[last_task]);
+
+ msc_switch_to_check(tsk);
+
+ last_task = tsk->taskid;
+}
+
+/* we want to try to avoid task switching while there are partial commands
+ in the send queues */
+int msc_switch_ok(void)
+{
+ if ((EXTFIELD(MSC_IN(MSC_QWORDCNT),MSC_QWDC_USRCNT) - 1))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * print out the state of the msc
+*/
+static void msc_status(void)
+{
+ int i;
+
+ printk("MSC_SQCTRL=%x\n",MSC_IN(MSC_SQCTRL));
+
+ for (i=0;i<5;i++)
+ printk("MSC_SQPTR%d=%x\n",i,MSC_IN(MSC_SQPTR0 + 8*i));
+ printk("MSC_OPTADR=%x\n",MSC_IN(MSC_OPTADR));
+ printk("MSC_MASCTRL=%x\n", MSC_IN(MSC_MASCTRL));
+ printk("MSC_SMASADR=%x_%x\n", MSC_IN(MSC_SMASADR),MSC_IN(MSC_SMASADR + 4));
+ printk("MSC_RMASADR=%x_%x\n", MSC_IN(MSC_RMASADR),MSC_IN(MSC_RMASADR + 4));
+ printk("MSC_PID=%x\n",MSC_IN(MSC_PID));
+
+ printk("MSC_QWORDCNT=%x\n",MSC_IN(MSC_QWORDCNT));
+
+ printk("MSC_INTR=%x\n",MSC_IN(MSC_INTR));
+ printk("MSC_CIDRANGE=%x\n",MSC_IN(MSC_CIDRANGE));
+ printk("MSC_QBMPTR=%x\n",MSC_IN(MSC_QBMPTR));
+ printk("MSC_SMASTWP=%x\n", MSC_IN(MSC_SMASTWP));
+ printk("MSC_RMASTWP=%x\n", MSC_IN(MSC_RMASTWP));
+ printk("MSC_SMASREG=%x\n", MSC_IN(MSC_SMASREG));
+ printk("MSC_RMASREG=%x\n", MSC_IN(MSC_RMASREG));
+ printk("MSC_SMASCNT=%x\n", MSC_IN(MSC_SMASCNT));
+ printk("MSC_IRL=%x\n", MSC_IN(MSC_IRL));
+ printk("MSC_SIMMCHK=%x\n", MSC_IN(MSC_SIMMCHK));
+
+ for (i=0;i<3;i++) {
+ printk("RBMBWP%d=%x\n",i,MSC_IN(ringbufs[i].rbmbwp));
+ printk("RBMMODE%d=%x\n",i,MSC_IN(ringbufs[i].rbmmode));
+ printk("RBMRP%d=%x\n",i,MSC_IN(ringbufs[i].rbmrp));
+ }
+
+ printk("DMA_GEN=%x\n",MSC_IN(DMA_GEN));
+
+ printk("qbm_full_counter=%d\n",qbm_full_counter);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov