Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / arch / x86_64 / kernel / rca / l0rca.c
diff --git a/kitten/arch/x86_64/kernel/rca/l0rca.c b/kitten/arch/x86_64/kernel/rca/l0rca.c
new file mode 100644 (file)
index 0000000..7bf61b1
--- /dev/null
@@ -0,0 +1,1258 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ */
+/*
+ *   RCA: Interface between CRAY RCA and linux kernel
+ *
+ * Copyright (c) 2003 Cray Inc.
+ *
+ *      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 code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+
+#include <lwk/kernel.h>
+#include <lwk/string.h>
+#include <lwk/errno.h>
+#include <lwk/delay.h>
+#include <lwk/version.h>
+#include <rca/rca_l0_linux.h>
+#include <rca/rca_l0.h>
+extern void    set_debug_traps(void) ;         /* GDB routine */
+
+#if defined(CONFIG_CRAY_RS_DEBUG)
+#define assert(expr) \
+if(!(expr)) {                                   \
+       printk( "Assertion failed! %s,%s,%s,line=%d\n", \
+              #expr,__FILE__,__FUNCTION__,__LINE__);          \
+}
+#else
+#define assert(expr)
+#endif /* CONFIG_CRAY_RS_DEBUG */
+
+/* States for up channel. Used for calling the tx_done callback only if reqd */
+#define        CHAN_TX_STATE_AVAIL     (0)
+#define        CHAN_TX_STATE_FULL      (1)
+#define GET_CHAN_STATE(x)      ((x)->state)
+#define SET_CHAN_STATE(x, s)   ((x)->state = (s))
+
+typedef struct l0rca_mapped_ch {
+       uint32_t num_obj;        /* number of objects    */
+       uint32_t intr_bit;
+       volatile uint32_t *ch_ridx;             /* Pointer to ridx */
+       volatile uint32_t *ch_widx;             /* Pointer to widx */
+       rs_event_t              *ch_buf_ptr;    /* Pointer to channel buffer */
+       l0rca_down_handle_t     down_handler;
+       l0rca_up_handle_t       up_handler;
+       uint32_t                reg_count;
+       int                     poll;           /* timeout */
+       int                     tshhld;         /* timeout */
+       int                     state;          /* outgoing chan full or not */
+} l0rca_mapped_ch_t;
+
+#define L0RCA_INITVAL   (0xAA55F00D)
+
+typedef struct l0rca_mapped_config {
+       uint32_t        version;        /* config version */
+       rs_node_t       proc_id;        /* node id */
+       int32_t         proc_num;       /* cpu number (0-3) */
+       l0rca_mapped_ch_t       ch_data[NUM_L0RCA_CHANNELS];
+       volatile uint32_t       *l0rca_l0_intr_ptr;     /* interrupt to L0 */
+        uint32_t        initialized;
+} l0rca_mapped_config_t;
+
+/* Our copy with virt addrs; so we can directly access the config area */
+l0rca_mapped_config_t l0rca_early_cfg = {0};
+
+/* Pointer to the actual config struct in Seastar RAM shared memory */
+l0rca_config_t *l0rca_cfg = NULL;
+
+/* Store the size of the event header without the msg body */
+uint32_t rs_ev_hdr_sz;
+
+
+void send_intr_to_l0(l0rca_mapped_ch_t *ch)
+{
+       volatile uint32_t *addr = &(((l0rca_intr_t *)(l0rca_early_cfg.l0rca_l0_intr_ptr))->l0r_intr_set);
+       SSPUT32(addr, (ch)->intr_bit);
+}
+
+/* 
+ * Function:   l0rca_event_data
+ * 
+ * Description: Return a pointer to the data portion and length of the
+ *             data portion of the event.
+ *             NB: Reflect any changes to l0rca_event_data in gdb_l0rca_event_data
+ * 
+ * Arguments: rs_event_t *evp IN: Event whose data is of interest
+ *           void **data OUT: Upon return will point to data portion of event
+ *           int32_t *len OUT: Upon return will have the length of the data
+ *                             portion of the event
+ *
+ * Returns: No Return Value.
+ * Note: The main purpose of this routine is to read in the data portion of the
+ * event from Seastar memory. 
+ */
+void l0rca_event_data(rs_event_t *evp, void **data, int32_t *len)
+{
+        /* Get length and data portion from the event */
+        *len = evp->ev_len;
+        *data = &evp->ev_data;
+} /* l0rca_event_data */
+
+/* 
+ * Function:   l0rca_get_proc_id
+ * 
+ * Description: Return the node/processor id.
+ * 
+ * Arguments: None
+ *
+ * Returns: The proc id.
+ */
+rs_node_t l0rca_get_proc_id(void)
+{
+       return l0rca_cfg->proc_id;
+}
+
+/* 
+ * Function:   l0rca_get_proc_num
+ * 
+ * Description: Returns this processor's CPU number, 0-3.
+ * 
+ * Arguments: None.
+ * 
+ * Returns: The CPU number of this node.
+ */
+int l0rca_get_proc_num(void)
+{
+    int tmp;
+    SSGET32(&(l0rca_cfg->proc_num),tmp);
+    return(tmp);
+}
+
+/*
+ * Function:   set_chan_tx_state
+ *
+ * Description:        Looks at the read and write indices to determine if the
+ *             channel is full or not. Sets state accordingly.
+ *             NB: Reflect any changes to set_chan_tx_state in gdb_set_chan_tx_state.
+ *
+ * Arguments: l0rca_mapped_ch_t *ch_ptr IN: Pointer to channel being set.
+ *
+ * Returns: None
+ */
+static inline void set_chan_tx_state(l0rca_mapped_ch_t *ch_ptr)
+{
+       volatile uint32_t wx, rx;
+       int not_full;
+       SSGET32(ch_ptr->ch_widx, wx);
+       SSGET32(ch_ptr->ch_ridx, rx);
+       not_full = (wx - rx) < ch_ptr->num_obj;
+       SET_CHAN_STATE(ch_ptr, not_full  ? CHAN_TX_STATE_AVAIL : CHAN_TX_STATE_FULL);
+}
+
+
+/*
+ * Function:   l0rca_init_config
+ *
+ * Description: Read L0 - RCA communication config structure and populate 
+ *              our personal copy. If there is any error, the OS panics
+ *              since not being able to communicate with L0 is a total disaster.
+ *              If already initialized then returns silently.
+ *
+ * Arguments: None.
+ *
+ * Returns: None
+ */
+
+void l0rca_init_config(void)
+{
+       rs_event_t *ev_buf;
+       int i;
+       volatile uint64_t tmp64;
+       volatile uint32_t tmp32;
+
+       /* 
+        * Check if already initialized; No locking is needed as this 
+        * is called during the boot process from within the kernel (as 
+        * opposed from a driver)
+        */
+       if (L0RCA_INITVAL == l0rca_early_cfg.initialized)
+               return;
+
+       l0rca_cfg = (l0rca_config_t *)rca_l0_comm_va(L0_SIC_RAM);
+
+       /* TODO - first order of business is to check the Version Number */
+       /* Also, should we panic if version mismatch? */
+#ifdef CONFIG_CRAY_RS_DEBUG
+       printk ("l0 config at virtual %p\n", l0rca_cfg);
+       printk ("Phys event bufs 0x%llx intr reg 0x%llx\n",
+               l0rca_cfg->l0rca_buf_addr,
+               l0rca_cfg->l0rca_l0_intr_addr);
+#endif /* CONFIG_CRAY_RS_DEBUG */
+
+       /* convert event buffer address from physical to virtual */
+       SSGET64(&(l0rca_cfg->l0rca_buf_addr),tmp64);
+       ev_buf = (rs_event_t *)rca_l0_comm_va(tmp64);
+
+       /* convert intr reg address from physical to virtual */
+       SSGET64(&(l0rca_cfg->l0rca_l0_intr_addr), tmp64);
+       l0rca_early_cfg.l0rca_l0_intr_ptr = (uint32_t *)rca_l0_comm_va(tmp64);
+
+#ifdef CONFIG_CRAY_RS_DEBUG
+        printk ("event bufs %p intr reg %p\n", ev_buf,
+                l0rca_early_cfg.l0rca_l0_intr_ptr);
+#endif /* CONFIG_CRAY_RS_DEBUG */
+
+       /* Now setup the channel buffers */
+       for (i = 0; i < NUM_L0RCA_CHANNELS; i++)
+       {
+               int num_obj; 
+               SSGET32(&(l0rca_cfg->chnl_data[i].num_obj), tmp32);
+               num_obj = tmp32;
+
+               /* Skip if channel is unused */
+               if (!num_obj)
+                       continue;
+
+               /* Ensure num_obj is a power of 2 */
+               if (num_obj & (num_obj - 1))
+               {
+#if 0
+                       panic ("l0rca_init_config: num_obj[%u] for channel %d is not power of 2\n",
+                              num_obj, i);
+#endif
+               }
+               l0rca_early_cfg.ch_data[i].num_obj = num_obj;
+               SSGET32(&(l0rca_cfg->chnl_data[i].l0_intr_bit), tmp32);
+               l0rca_early_cfg.ch_data[i].intr_bit = tmp32;
+
+               /* Point to the read/writew index */
+               l0rca_early_cfg.ch_data[i].ch_ridx = &l0rca_cfg->chnl_data[i].ridx;
+               l0rca_early_cfg.ch_data[i].ch_widx = &l0rca_cfg->chnl_data[i].widx;
+               l0rca_early_cfg.ch_data[i].ch_buf_ptr = ev_buf;
+
+               ev_buf += num_obj;
+
+#ifdef CONFIG_CRAY_RS_DEBUG
+               printk ("Buffer %p for channel %d rd %u wr %u\n", 
+                       l0rca_early_cfg.ch_data[i].ch_buf_ptr, i,
+                       l0rca_cfg->chnl_data[i].ridx,
+                       l0rca_cfg->chnl_data[i].widx);
+#endif /* CONFIG_CRAY_RS_DEBUG */
+       }       /* End of for */
+
+       /* Set the remaining fields */
+       SSGET32(&(l0rca_cfg->version), l0rca_early_cfg.version);
+       SSGET32(&(l0rca_cfg->proc_id), l0rca_early_cfg.proc_id);
+       SSGET32(&(l0rca_cfg->proc_num), l0rca_early_cfg.proc_num);
+
+       /* Assumes ev_data is the last element TODO: this should be
+         * defined via a macro in the rs_event_t structure definition */
+       rs_ev_hdr_sz = offsetof(rs_event_t, ev_data);
+
+       /* Indicate we have a initialized the mapped copy */
+       l0rca_early_cfg.initialized = L0RCA_INITVAL;
+
+        return;
+}
+
+/* 
+ * Function:   register_ch_up
+ * 
+ * Description: Register function for the upload channel. It is expected that
+ * there be at most one registered user for an upload channel. This user 
+ * provides a callback to be invoked when the buffer drains below tshhld
+ * (only if the buffer became full last time the tshhld was crossed)
+ * 
+ * Arguments: int ch_num IN: channel number to register on
+ *           l0rca_up_handle_t handler IN: callback routine
+ *            int tshhld IN: buffer to drain before invoking callback; ignored
+ *                          if poll is negative.
+ *           int poll IN: if > zero - duration in ms to check for buffer drain
+ *                        if = zero - tx done interrupt invokes callback (TODO)
+ *                        if < zero - do nothing. It is assumed that the user (TODO)
+ *                        has her own means to check for buffer drain
+ *
+ * Returns: -EBUSY - If another user is already registered.
+ *          -EINVAL - if ch_num is not in range.
+ *          zero (SUCCESS) otherwise.
+ *
+ * Note: It is expected that the buffer is empty upon call to this routine. 
+ *      This should be true on system startup and after an unregister call.
+ *
+ * Note: As of 12/1/04, the polling is forced, and is hard coded in 
+ *   l0rca_linux.c.  Thus, the tshhld and poll params have no meaning.
+ */
+int
+register_ch_up(int ch_num, l0rca_up_handle_t handler, int tshhld, int poll)
+{
+        volatile uint32_t wx, rx;
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+
+       if (NUM_L0RCA_CHANNELS <= ch_num)
+               return -EINVAL;
+
+       /* Allow only one user per channel */
+       if (ch_ptr->reg_count)
+               return -EBUSY;
+
+        SSGET32(ch_ptr->ch_widx, wx);
+        SSGET32(ch_ptr->ch_ridx, rx);
+       assert(wx == rx);
+
+       SET_CHAN_STATE(ch_ptr, CHAN_TX_STATE_AVAIL);
+       ch_ptr->down_handler = NULL;    /* Clear the down handler */
+       ch_ptr->reg_count++;
+       ch_ptr->poll = poll;
+       ch_ptr->up_handler = handler;
+       ch_ptr->tshhld = tshhld;
+
+       return 0;
+}
+
+/* 
+ * Function:   register_ch_down
+ * 
+ * Description: Register function for the download channel. It is expected that
+ * there be at most one registered user for a download channel. This user 
+ * provides a callback to be invoked when data from L0 arrives on the channel.
+ * 
+ * Arguments: int ch_num IN: channel number to register on
+ *           l0rca_down_handle_t handler IN: callback routine
+ *           int poll IN: if > zero - duration in ms to check for event
+ *                        if = zero - event arrival is interrupt driven.
+ *                        if < zero - do nothing. It is assumed that the user
+ *                        has her own means to check for event arrival.
+ *
+ * Returns: EBUSY - If another user is already registered.
+ *          zero (SUCCESS) otherwise.
+ * 
+ * Note: As of 12/1/04, the polling is forced, and is hard coded in 
+ *   l0rca_linux.c.  Thus, the poll parameter has no meaning.
+ */
+int register_ch_down(int ch_num, l0rca_down_handle_t handler, int poll)
+{
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+
+       if (NUM_L0RCA_CHANNELS <= ch_num)
+               return -EINVAL;
+
+       /* Allow only one user per channel */
+       if (ch_ptr->reg_count)
+               return -EBUSY;
+
+       ch_ptr->reg_count++;
+       ch_ptr->down_handler = handler;
+       ch_ptr->poll = poll;
+
+#if 0
+       /* Do any OS specific initialization e.g. set irq, timers etc. */
+       if (l0rca_os_init())
+       {
+               panic ("Unable to initialize OS service for L0RCA interface\n");
+       }
+#endif
+
+       return 0;
+}
+
+#ifdef CONFIG_CRAY_RS_DEBUG
+uint32_t l0rca_buf_full;
+#endif /* CONFIG_CRAY_RS_DEBUG */
+/* 
+ * Function:   ch_send_data
+ * 
+ * Description: Sends data towards the L0. 
+ * The data that buf points to is sent as the payload in an rs_event structure.
+ * The header is a separate parameter and the send routine directly copies
+ * the header and the data into the circular buffer, thus avoiding a copy.
+ *
+ * NB: Reflect any changes to ch_send_data in gdb_ch_send_data
+ * 
+ * Arguments: int ch_num IN: channel number on which to send data
+ *           rs_event_t *ev_hdr  IN: Header without len & timestamp
+ *           void* buf  IN: Buffer with data
+ *           int len    IN: length of data to transfer
+ *
+ * Returns: -EINVAL - if no user registered on the channel (Debug only)
+ *          -EFAULT - if buf or ev_hdr is NULL (Debug only)
+ *          -E2BIG -  if len exceeds max event payload (RS_MSG_LEN) (Debug only)
+ *          zero -    SUCCESS, all bytes sent.
+ *          > 0 -     Bytes not sent. Sender should retry.
+ *
+ * Notes: data in buf will be copied to the channel buffer, therfore, upon
+ *       return, user can free the buf. buf may not be NULL and the len must
+ *       not be zero. Use ch_send_event to send events with zero length data
+ *       portion.
+ */
+
+int ch_send_data(int ch_num, const rs_event_t *ev_hdr, 
+                void* buf, unsigned int len)
+{
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+       uint32_t ev_len = 0;
+       volatile uint32_t tmpwx, tmprx;
+
+#ifdef CONFIG_CRAY_RS_DEBUG
+       uint32_t wr, rd, no_events;
+       if ((NULL == ev_hdr) || (NULL == buf))
+       {
+               return -EFAULT;
+       }
+
+       if ((NUM_L0RCA_CHANNELS <= ch_num) || (0 == len) ||
+           (L0RCA_INITVAL != l0rca_early_cfg.initialized) ||
+           (0 == ch_ptr->reg_count))
+       {
+               return -EINVAL;
+       }
+
+       /* Normalize the write & read indexes */
+        SSGET32(ch_ptr->ch_widx, tmpwx);
+       wr = tmpwx & (ch_ptr->num_obj - 1);
+        SSGET32(ch_ptr->ch_ridx, tmprx);
+       rd = tmprx & (ch_ptr->num_obj - 1);
+
+
+       /* Calculate the number of events we will be sending */
+       no_events = (len + RS_MSG_LEN -1)/RS_MSG_LEN;
+       if ((wr + (no_events) - rd) < ch_ptr->num_obj)
+       {
+            l0rca_buf_full++;
+       }
+
+#endif /* CONFIG_CRAY_RS_DEBUG */
+
+       /* Optimize for buf not full and only one event needed to send */
+
+       /* 
+        * Masking of indexes is not needed for the check in while() below.
+        * Both widx & ridx are unsigned and increase monotonically such that
+        * ridx cannot lag behind widx by more than the circular buf size i.e.
+        * num_obj. widx will overflow before ridx.
+        * 'widx - ridx' will always yield the difference between the two 
+        * even if widx has overflowed and ridx has not yet overflowed.
+        */
+       SSGET32(ch_ptr->ch_widx, tmpwx);
+       SSGET32(ch_ptr->ch_ridx, tmprx);
+       while ((tmpwx - tmprx) < ch_ptr->num_obj)
+       {
+               rs_event_t *wr_ev_ptr = 
+                 &ch_ptr->ch_buf_ptr[tmpwx & (ch_ptr->num_obj - 1)];
+
+               /* Copy same header for each event */
+               SSMEMPUT(wr_ev_ptr, (uint32_t*) ev_hdr, rs_ev_hdr_sz);
+
+               /* Copy the data portion over */
+               ev_len = (RS_MSG_LEN > len) ? len : RS_MSG_LEN;
+               SSPUT32(&(wr_ev_ptr->ev_len), ev_len);
+               SSMEMPUT((char*)wr_ev_ptr + rs_ev_hdr_sz, buf, RS_MSG_LEN);
+
+               /* TODO: Set the timestamp in each event */
+#if 0
+               SSPUT32(wr_ev_ptr->_ev_stp, 0x0);
+#endif  /* 0 */
+               len -= ev_len;
+               /* 
+                * After updating the widx, DO NOT access any field in that 
+                * event. Though not desirable, the reader is free to alter 
+                * fields in the event.
+                */
+               SSGET32(ch_ptr->ch_widx, tmpwx);
+               SSPUT32(ch_ptr->ch_widx, tmpwx + 1);
+               set_chan_tx_state(ch_ptr);
+
+               /* Let L0 know an ev is available */
+                send_intr_to_l0(ch_ptr);
+
+               if (0 == len)
+                       break;
+
+               buf = (void *)(((char *)buf) + RS_MSG_LEN);
+
+               SSGET32(ch_ptr->ch_widx, tmpwx);
+               SSGET32(ch_ptr->ch_ridx, tmprx);
+       }       /* End of while */
+       return len;     /* bytes remaining, if any */
+}
+
+/* 
+ * Function:   ch_send_event
+ * 
+ * Description: Sends an event to L0. An event with zero length data portion
+ *             is supported.
+ * 
+ * Arguments: int ch_num IN: channel number on which to send the event
+ *           const rs_event_t *evp  IN: EVent to send
+ *
+ * Returns: -EINVAL - if no user registered on the channel (Debug only)
+ *          -EFAULT - if evp is NULL (Debug only)
+ *          zero -    SUCCESS, event sent.
+ *          +EBUSY -  Event not sent. Sender should retry.
+ *
+ * Notes: The event will be copied to the channel buffer, therfore, upon
+ *       return, user may free the space associated with the event
+ */
+int ch_send_event(int ch_num, const rs_event_t *evp)
+{
+       int ret = 0;
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+        volatile uint32_t tmpwx, tmprx;
+
+#ifdef CONFIG_CRAY_RS_DEBUG
+       if (NULL == evp)
+       {
+               return -EFAULT;
+       }
+
+       if ((NUM_L0RCA_CHANNELS <= ch_num) ||
+           (L0RCA_INITVAL != l0rca_early_cfg.initialized) ||
+           (0 == ch_ptr->reg_count))
+       {
+               return -EINVAL;
+       }
+#endif /* CONFIG_CRAY_RS_DEBUG */
+
+       /* Optimize for circular buffer not full */
+
+       /* 
+        * Masking of indexes is not needed for the check in while() below.
+        * Both widx & ridx are unsigned and increase monotonically such that
+        * ridx cannot lag behind widx by more than the circular buf size i.e.
+        * num_obj. widx will overflow before ridx.
+        * 'widx - ridx' will always yield the difference between the two 
+        * even if widx has overflowed and ridx has not yet overflowed.
+        */
+       SSGET32(ch_ptr->ch_widx,tmpwx);
+       SSGET32(ch_ptr->ch_ridx,tmprx);
+       if((tmpwx - tmprx) < ch_ptr->num_obj)
+       {
+               rs_event_t *wr_ev_ptr = 
+                 &ch_ptr->ch_buf_ptr[tmpwx & (ch_ptr->num_obj - 1)];
+
+               /* Copy header & data length for the event */
+               SSMEMPUT(wr_ev_ptr, (uint32_t*)evp, rs_ev_hdr_sz + evp->ev_len);
+
+               SSGET32(ch_ptr->ch_widx, tmpwx);
+               SSPUT32(ch_ptr->ch_widx, tmpwx + 1);
+               set_chan_tx_state(ch_ptr);
+
+               /* Let L0 know an ev is available */
+                send_intr_to_l0(ch_ptr);
+       } else
+       {
+               ret = EBUSY;
+       }
+
+       return ret;
+}
+
+/* 
+ * Function:   l0rca_ch_get_event
+ * 
+ * Description: Read an event from L0 (if any). If an event is availabe then 
+ *             the read pointer is advanced.
+ *             NB: Reflect any changes to l0rca_ch_get_event in gdb_l0rca_ch_get_event
+ * 
+ * Arguments: int ch_num IN: channel number from which to read the event
+ *           rs_event_t *evp  IN: Buffer to place the event
+ *
+ * Returns: -EINVAL - if no user registered on the channel (Debug only)
+ *          -EFAULT - if evp is NULL (Debug only)
+ *          zero -    no event to receive at this time
+ *          > 0 -     Event received
+ *
+ */
+int l0rca_ch_get_event(int ch_num, rs_event_t *evp)
+{
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+       int ret = 0;
+       uint32_t nbytes;
+       volatile uint32_t tmpwx, tmprx;
+
+#ifdef CONFIG_CRAY_RS_DEBUG
+       if (NULL == evp)
+               return -EFAULT;
+
+       if ((NUM_L0RCA_CHANNELS <= ch_num) ||
+           (L0RCA_INITVAL != l0rca_early_cfg.initialized) ||
+           (0 == ch_ptr->reg_count))
+               return -EINVAL;
+#endif /* CONFIG_CRAY_RS_DEBUG */
+
+       SSGET32(ch_ptr->ch_widx,tmpwx);
+       SSGET32(ch_ptr->ch_ridx,tmprx);
+       if(tmpwx != tmprx)
+       {
+               rs_event_t *wr_ev_ptr = 
+                 &ch_ptr->ch_buf_ptr[tmprx & (ch_ptr->num_obj - 1)];
+
+               /* Copy over the event */
+               SSGET32(&(wr_ev_ptr->ev_len), nbytes);
+               SSMEMGET(evp, (uint32_t*)wr_ev_ptr, nbytes + rs_ev_hdr_sz);
+
+               /* Update the rd index */
+               SSGET32(ch_ptr->ch_ridx, tmprx);
+               SSPUT32(ch_ptr->ch_ridx, tmprx + 1);
+
+               /* Let L0 know that the  event has been drained */
+               send_intr_to_l0(ch_ptr);
+
+               ret = 1;
+       }
+
+       return ret;
+}
+
+/* 
+ * Function:   unregister_ch_down
+ * 
+ * Description: Unregister function for the upload channel. Use to indicate
+ * that the channel is no longer to be used. The read & write pointers are 
+ * equalized to make the circ buffer empty.
+ * 
+ * Arguments: int ch_num IN: channel number to unregister
+ *
+ * Returns: -EINVAL - if ch_num is not correct or no user registered
+ *          zero (SUCCESS)
+ */
+int unregister_ch_down(int ch_num)
+{
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+       volatile uint32_t tmpwx;
+
+       if (NUM_L0RCA_CHANNELS <= ch_num)
+               return -EINVAL;
+
+       /* Check if user is using this channel */
+       if (!ch_ptr->reg_count)
+               return -EINVAL;
+
+       ch_ptr->down_handler = NULL;
+       ch_ptr->reg_count--;
+
+       /* Equalize the read & write pointers i.e. drain the circ buffer */
+       /* NOTE: We cannot really stop the L0 from sending data */
+       SSGET32(ch_ptr->ch_widx, tmpwx);
+       SSPUT32(ch_ptr->ch_ridx, tmpwx);
+
+       return 0;
+}
+
+/* 
+ * Function:   unregister_ch_up
+ * 
+ * Description: Unregister function for the download channel. Use to indicate
+ * that the channel is no longer to be used.
+ * 
+ * Arguments: int ch_num IN: channel number to unregister
+ *
+ * Returns: -EINVAL - if ch_num is not correct or no user registered
+ *          zero (SUCCESS)
+ */
+int unregister_ch_up(int ch_num)
+{
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+       volatile uint32_t tmpwx, tmprx;
+
+       if (NUM_L0RCA_CHANNELS <= ch_num)
+               return -EINVAL;
+
+       /* Check if user is using this channel */
+       if (!ch_ptr->reg_count)
+               return -EINVAL;
+
+       /* Wait for events to be drained by the L0 */
+       SSGET32(ch_ptr->ch_widx,tmpwx);
+       SSGET32(ch_ptr->ch_ridx,tmprx);
+       while(tmpwx != tmprx)
+       {
+               udelay(1000);
+               SSGET32(ch_ptr->ch_widx,tmpwx);
+               SSGET32(ch_ptr->ch_ridx,tmprx);
+       }
+
+       ch_ptr->up_handler = NULL;
+       ch_ptr->reg_count--;
+
+       return 0;
+}
+
+/* TODO: If we decide to use the PKT_MODE register to indicate the channels
+ * that need attention then use PKT_MODE instead of looking at each channel.
+ * TODO: Currently the rx_done callback is invoked for each event in the 
+ * incoming channel. Change this to be called with all the pending events.
+ * Note that since the channel is a circular buffer and the pending events
+ * wrap around, then the callback needs to be invoked twice - once with events
+ * upto the "end" of the circular buffer and then with events starting from the
+ * "start" of the circular buffer.
+ * TODO: To prevent thrashing of the tx_done callback in case the circular
+ * buffer is being operated under almost full condition, obey the threshold
+ * specified at the registration. The tx_done callback is only called once the
+ * circular buffer occupancy is below the specified threshold.
+ */
+/* 
+ * Function:   l0rca_poll_callback
+ * 
+ * Description: Scan the incoming channels and call the receive callback
+ *             (if any) in case an event is pending to be processed.
+ *             Update the read pointer. Next scan the outgoing channels
+ *             and if the channel was full, call the transmit done callback
+ *             so that events may be sent.
+ * 
+ * Arguments: None
+ *
+ * Returns: 0 if no events were processed, else 1.
+ *
+ * Note: It is possible that this routine is called from interrupt
+ *      context. The callbacks invoked *must* not block.
+ */
+
+int l0rca_poll_callback(void)
+{
+       int i;
+       int rval = 0;
+       rs_event_t ev, *wr_ev_ptr;
+       volatile uint32_t tmpwx, tmprx;
+
+       /* Loop through all channels with incoming events */
+       for (i = 1; i < NUM_L0RCA_CHANNELS; i+=2)
+       {
+               l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[i];
+
+               /* GDB handled separately.
+                */
+               if (i == L0RCA_CH_KGDB_DOWN) 
+                   continue;
+
+               if ((0 == ch_ptr->reg_count) || (NULL == ch_ptr->down_handler))
+                       continue;
+               
+               if ((ch_ptr->ch_ridx == 0) || (ch_ptr->ch_widx == 0)) {
+                   continue;
+               }
+
+               if (!ch_ptr->num_obj) {
+                   SSGET32(ch_ptr->ch_widx, tmpwx);
+                   SSGET32(ch_ptr->ch_ridx, tmprx);
+                   continue;
+               }
+
+               SSGET32(ch_ptr->ch_widx, tmpwx);
+               SSGET32(ch_ptr->ch_ridx, tmprx);
+               if (tmpwx != tmprx)
+               {
+                       wr_ev_ptr = (rs_event_t*)&ch_ptr->ch_buf_ptr[tmprx & (ch_ptr->num_obj - 1)];
+                       /* read the entire event */
+                       SSMEMGET((uint32_t*)&ev, (uint32_t*)wr_ev_ptr, rs_sizeof_event(RS_MSG_LEN));
+
+                       /* Call callback routine with one event */
+                       (ch_ptr->down_handler)(i, &ev, 1);
+
+                       /* We are done with this event */
+                       rval++;
+                       SSGET32(ch_ptr->ch_ridx, tmprx);
+                       SSPUT32(ch_ptr->ch_ridx, tmprx + 1);
+
+                       /* Let L0 know that the  event has been drained */
+                       send_intr_to_l0(ch_ptr);
+               } /* End of if */
+       } /* End of for incoming channels */
+
+       /* Loop through all channels with outgoing events */
+       for (i = 0; i < NUM_L0RCA_CHANNELS; i+=2)
+       {
+               l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[i];
+
+               /* GDB handled separately.
+                */
+               if (i == L0RCA_CH_KGDB_UP)
+                       continue;
+
+               if(NULL != ch_ptr->up_handler)
+               {
+                       /* Lock needed for mutex with tx routine */
+                       LOCK_CHANNEL(i);
+                       if (CHAN_TX_STATE_FULL == GET_CHAN_STATE(ch_ptr))
+                       {
+                               /* Call the callback if chan no longer full */
+                               assert(0 != ch_ptr->reg_count);
+                               SSGET32(ch_ptr->ch_widx, tmpwx);
+                               SSGET32(ch_ptr->ch_ridx, tmprx);
+                               if((tmpwx - tmprx) < ch_ptr->num_obj)
+                               {
+                                       rval++;
+                                       SET_CHAN_STATE(ch_ptr,CHAN_TX_STATE_AVAIL);
+                                       UNLOCK_CHANNEL(i);
+                                       (ch_ptr->up_handler)(i);
+                                       LOCK_CHANNEL(i);
+                               }
+                       } /* End of if */
+                       UNLOCK_CHANNEL(i);
+               }
+       } /* End of for */
+
+       return rval;
+}
+
+#if defined(CONFIG_CRAY_XT_KGDB) || defined(CONFIG_CRAY_KGDB)
+/* Kernel mode GDB via L0 interface routines.
+ *
+ * Note that this code was derived from gdbl0.c,
+ * which was derived from Linux gdbserial.c,
+ * which contains no copyright notice.  Parts
+ * of this may need to be rewritten if a GPL
+ * copyright notice was removed from gdbserial.c
+ *
+ */
+
+#define LRPRINTF  printk
+
+#undef PRNT                            /* define for debug printing */
+
+#define        GDB_BUF_SIZE    512             /* power of 2, please */
+
+static char    gdb_buf[GDB_BUF_SIZE] ;
+static int     gdb_buf_in_inx ;
+static int     gdb_buf_in_cnt ;
+static int     gdb_buf_out_inx ;
+
+static int initialized = -1;
+
+int gdb_store_overflow;
+int gdb_read_error;
+
+/* Preset this with constant fields in the event header */
+static rs_event_t l0rca_gdb_ev_template = {0};
+
+/*
+ * Function:    rcal0_gdb_template
+ *
+ * Description: Hand craft a event header to be sent with each outgoing event
+ *
+ * Arguments: None.
+ *
+ * Returns: None
+ *
+ * Note: The len & timestamp are not filled in.
+ */
+static void rcal0_gdb_template(void)
+{
+       l0rca_gdb_ev_template.ev_id = ec_kgdb_output;
+       l0rca_gdb_ev_template.ev_gen = RCA_MKSVC(RCA_INST_ANY,
+           RCA_SVCTYPE_TEST0, l0rca_get_proc_id());
+       l0rca_gdb_ev_template.ev_src = l0rca_gdb_ev_template.ev_gen;
+       l0rca_gdb_ev_template.ev_priority = RCA_LOG_DEBUG;
+       l0rca_gdb_ev_template.ev_flag = 0;      /* For Debugging */
+
+       /* Timestamp, len & data is filled at the time of sending event */
+}
+
+/*
+ * Function:    l0_gdb_init
+ *
+ * Description: Take steps to set things up for early_printk output
+ *
+ * Arguments: struct console *console IN: pointer to console struct
+ *            char *input IN: Not used
+ *
+ * Returns: zero (SUCCESS)
+ */
+int l0_gdb_init(void)
+{
+
+       int ret;
+
+       /* RCA already initialized on Q
+        */
+       /* Read the configuration information provided by L0 */
+       l0rca_init_config();
+
+       /* Setup the Event template to use for outgoing events */
+       rcal0_gdb_template();
+
+       /* Set up channel internal state by calling
+        * registration routines.
+        */
+
+       /* Register with the KGDB out channel to send gdb data */
+       ret = register_ch_up (L0RCA_CH_KGDB_UP, NULL, 0, 0);
+
+       if (!ret)
+       {
+               /* Register with the KGDB in channel to receive gdb commands */
+               ret = register_ch_down(L0RCA_CH_KGDB_DOWN, NULL, 0);
+       }
+       
+       return ret;
+}
+
+extern void breakpoint(void);
+
+
+int gdb_hook(void)
+{
+       int retval;
+
+       /*
+        * Call GDB routine to setup the exception vectors for the debugger.
+        */
+       
+       /* Setup both kgdb channels */
+       retval = l0_gdb_init();
+
+       /* TODO: on Linux the call to printk in this
+        * routine no longer generate output.
+        *
+        * Did something change in the setup of the
+        * console channel?
+        */
+       if (retval == 0)
+       {
+               initialized = 1;
+       } else
+       {
+               initialized = 0;
+               LRPRINTF("gdb_hook: l0_gdb_init() failed: %d\n", retval);
+               return (-1);
+       }
+       return 0;
+
+} /* gdb_hook */
+
+/*
+ * Function:   l0rca_kgdb_down_getc()
+ * 
+ * Description:        Checks for an event on the KGDB DOWN L0 channel
+ *             and returns the first character, other characters
+ *              are thrown away.  Used to detect control C for
+ *              breakpointing the kernel.  Returns 0 when no
+ *             input is available or on error where gdb_read_error
+ *             is incremented.
+ */
+int l0rca_kgdb_down_getc(void)
+{
+       char    *chp;
+       int ret, len;
+       rs_event_t ev = {0};
+
+       if ((ret = l0rca_ch_get_event(L0RCA_CH_KGDB_DOWN, &ev)) <= 0) {
+               if (ret < 0)
+                    gdb_read_error++;
+               return 0;
+       }
+       l0rca_event_data(&ev, (void *)&chp, &len);
+       if (len > 0)
+               return *chp;
+       return 0;
+} /* l0rca_kgdb_down_getc */
+
+/* Only routines used by the Kernel trap mode KGDB interface
+ * should follow this point.
+ *
+ * These functions should only call other `gdb_.*' functions.
+ *
+ * This is best done by examining the assembly file and
+ * ensuring that all assembly call statements only call
+ * routines that match `gdb_.*', in all of the routines
+ * that follow.
+ *
+ * NB: SETTING BREAKPOINTS IN THE FOLLOWING ROUTINES MAY
+ * BREAK THE KERNEL DEBUGGER.
+ */
+
+/* 
+ * Function:   gdb_l0rca_event_data
+ * 
+ * Description:        Clone of l0rca_event_data to only be called by kernel GDB.
+ *             data portion of the event.
+ * 
+ * Arguments: rs_event_t *evp IN: Event whose data is of interest
+ *           void **data OUT: Upon return will point to data portion of event
+ *           int32_t *len OUT: Upon return will have the length of the data
+ *                             portion of the event
+ *
+ * Returns: No Return Value.
+ */
+void gdb_l0rca_event_data(rs_event_t *evp, void **data, int32_t *len)
+{
+        /* Get length and data portion from the event */
+        *len = evp->ev_len;
+        *data = &evp->ev_data;
+} /* gdb_l0rca_event_data */
+
+/*
+ * Function:   gdb_set_chan_tx_state
+ *
+ * Description:        Clone of set_chan_tx_state to only be called by kernel GDB.
+ */
+static inline void gdb_set_chan_tx_state(l0rca_mapped_ch_t *ch_ptr)
+{
+
+       int not_full = (*ch_ptr->ch_widx - *ch_ptr->ch_ridx) < ch_ptr->num_obj;
+
+       SET_CHAN_STATE(ch_ptr, not_full  ? CHAN_TX_STATE_AVAIL : CHAN_TX_STATE_FULL);
+}
+
+static void gdb_memcpy(void *dest, const void *src, int cnt)
+{
+    int i;
+    char *pd = dest;
+    const char *ps = src;
+
+    for (i = 0; i < cnt; i++)
+       pd[i] = ps[i];
+}
+
+/* 
+ * Function:   gdb_ch_send_data
+ *
+ * Description:        Clone of ch_send_data to be only called by kernel GDB.
+ * 
+ */
+int gdb_ch_send_data(int ch_num, const rs_event_t *ev_hdr, 
+                void* buf, unsigned int len)
+{
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+
+       /* No registration checks needed.
+        */
+
+       /* Optimize for buf not full and only one event needed to send */
+
+       /* 
+        * Masking of indexes is not needed for the check in while() below.
+        * Both widx & ridx are unsigned and increase monotonically such that
+        * ridx cannot lag behind widx by more than the circular buf size i.e.
+        * num_obj. widx will overflow before ridx.
+        * 'widx - ridx' will always yield the difference between the two 
+        * even if widx has overflowed and ridx has not yet overflowed.
+        */
+       while((*ch_ptr->ch_widx - *ch_ptr->ch_ridx) < ch_ptr->num_obj)
+       {
+               rs_event_t *wr_ev_ptr = 
+                 &ch_ptr->ch_buf_ptr[*ch_ptr->ch_widx & (ch_ptr->num_obj - 1)];
+
+               /* Copy same header for each event */
+               gdb_memcpy(wr_ev_ptr, (void *) ev_hdr, rs_ev_hdr_sz);
+
+               /* Copy the data portion over */
+               wr_ev_ptr->ev_len = (RS_MSG_LEN > len) ? len : RS_MSG_LEN;
+               gdb_memcpy((char *)wr_ev_ptr + rs_ev_hdr_sz, buf,
+                      wr_ev_ptr->ev_len);
+
+               /* TODO: Set the timestamp in each event */
+#if 0
+               wr_ev_ptr->_ev_stp = 0x0;
+#endif /* 0 */
+               
+               len -= wr_ev_ptr->ev_len;
+               /* 
+                * After updating the widx, DO NOT access any field in that 
+                * event. Though not desirable, the reader is free to alter 
+                * fields in the event.
+                */
+               (*ch_ptr->ch_widx)++;
+               gdb_set_chan_tx_state(ch_ptr);
+
+               /* Let L0 know an ev is available */
+                send_intr_to_l0(ch_ptr);
+
+               if (0 == len)
+                       break;
+
+               buf += RS_MSG_LEN;
+       }       /* End of while */
+
+       return len;     /* bytes remaining, if any */
+}
+
+/* 
+ * Function:   gdb_l0rca_ch_get_event
+ * 
+ * Description:        Clone of l0rca_ch_get_event to only be called by kernel GDB.
+ *             the read pointer is advanced.
+ * 
+ */
+int gdb_l0rca_ch_get_event(int ch_num, rs_event_t *evp)
+{
+       l0rca_mapped_ch_t *ch_ptr = &l0rca_early_cfg.ch_data[ch_num];
+       int ret = 0;
+
+       /* No registration checks needed
+        */
+
+       if(*ch_ptr->ch_widx != *ch_ptr->ch_ridx)
+       {
+               rs_event_t *wr_ev_ptr = 
+                 &ch_ptr->ch_buf_ptr[*ch_ptr->ch_ridx & (ch_ptr->num_obj - 1)];
+
+               /* Copy over the event */
+               gdb_memcpy(evp, (void *)wr_ev_ptr, wr_ev_ptr->ev_len+rs_ev_hdr_sz);
+
+               /* Update the rd index */
+               (*ch_ptr->ch_ridx)++;
+
+               /* Let L0 know that the  event has been drained */
+               send_intr_to_l0(ch_ptr);
+
+               ret = 1;
+       }
+
+       return ret;
+}
+
+/*
+ * Function:    gdb_store_char_in_buf
+ *
+ * Description: Check for overflow and place the incoming character into 
+ *             the local buffer for later retreival.
+ *
+ * Arguments: int ch IN: Incoming character
+ *
+ * Returns: zero  - (SUCCESS)
+ *         -1    - Buffer Overflow
+ */
+static int gdb_store_char_in_buf(char ch)
+{
+       if (gdb_buf_in_cnt >= GDB_BUF_SIZE)
+       {       /* buffer overflow, clear it */
+               gdb_buf_in_inx = 0 ;
+               gdb_buf_in_cnt = 0 ;
+               gdb_buf_out_inx = 0 ;
+               return -1;
+       }
+
+       gdb_buf[gdb_buf_in_inx++] = ch;
+       gdb_buf_in_inx &= (GDB_BUF_SIZE - 1) ;
+       gdb_buf_in_cnt++;
+
+       return 0;
+}
+
+/*
+ * Wait until the interface can accept a char, then write it.
+ */
+static void gdb_write_char(char chr)
+{
+       int ret;
+
+       while ((ret = gdb_ch_send_data(L0RCA_CH_KGDB_UP, &l0rca_gdb_ev_template,
+              (void *)&chr, sizeof(char))) > 0)
+       {
+               /* Buffer full; keep trying.... */
+               ;
+       }       /* End of while */
+
+       return;
+} /* gdb_write_char */
+
+/*
+ * gdb_getc
+ *
+ * This is a GDB stub routine.  It waits for a character from the
+ * L0 interface and then returns it.
+ */
+int gdb_getc(int wait)
+{
+       char    *chp, *end_buf;
+       int ret, len;
+       rs_event_t ev = {0};
+
+#ifdef PRNT
+       LRPRINTF("gdb_getc:") ;
+#endif
+       /* First check if the receive callback has any chars pending */
+       if (gdb_buf_in_cnt == 0)
+       {
+               /* No chars from rx_callback; Loop until a char is available */
+               while ((ret = gdb_l0rca_ch_get_event(L0RCA_CH_KGDB_DOWN, &ev)) 
+                       <= 0)
+               {
+                       if (ret < 0)
+                       {
+                               /* Error!! This is death */
+                               gdb_read_error++;
+                               return -1;
+                       }
+                       if (! wait)
+                               return 0;
+               } /* End of while */
+
+               /* Get the data in the event */
+               gdb_l0rca_event_data(&ev, (void *)&chp, &len);
+
+               /* We have an event; fill the local buffer */
+               for (end_buf = chp+len; chp < end_buf; chp++)
+               {
+                       if (gdb_store_char_in_buf(*chp) < 0)
+                       {
+                               gdb_store_overflow++;
+                       }
+               } /* End of for */
+       } /* End of if */
+
+       /* There should be something for us in the local buffer now */
+       chp = &gdb_buf[gdb_buf_out_inx++] ;
+       gdb_buf_out_inx &= (GDB_BUF_SIZE - 1) ;
+       gdb_buf_in_cnt--;
+
+#ifdef PRNT
+       LRPRINTF("%c\n", *chp > ' ' && *chp < 0x7F ? *chp : ' ') ;
+#endif
+       return(*chp) ;
+
+} /* gdb_getc */
+
+/*
+ * gdb_putc
+ *
+ * This is a GDB stub routine.  It waits until the interface is ready
+ * to transmit a char and then sends it.  If there is no serial
+ * interface connection then it simply returns to its caller, having
+ * pretended to send the char.
+ */
+int gdb_putc(char chr)
+{
+#ifdef PRNT
+       LRPRINTF("gdb_putc: chr=%02x '%c'\n", chr,
+                chr > ' ' && chr < 0x7F ? chr : ' ') ;
+#endif
+
+       gdb_write_char(chr);    /* this routine will wait */
+
+       return 1;
+
+} /* gdb_putc */
+
+int    putDebugPacket(char *buf, int n)
+{
+       int ret = -1;
+
+       /* Loop sending the data */
+       while (n)
+       {
+               if ((ret = gdb_ch_send_data(L0RCA_CH_KGDB_UP, &l0rca_gdb_ev_template,
+                    (void *)buf, n)) <= 0) {
+                        /* Either error or we are done */
+                        break;
+                }
+
+                if (n > ret) {
+                        /* Some bytes were sent, point to the remaining data */
+                        buf += (n - ret);
+                        n = ret;
+                }
+       }       
+
+       return ret;
+}
+#endif /* CONFIG_CRAY_KGDB */