/* -*- 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 #include #include #include #include #include #include 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 */