+++ /dev/null
-/* -*- 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 */