This patch adds a new driver for the GRSPW SpaceWire AMBA interface family. The new driver does not implement a standard RTEMS driver, instead it provides only a library interface to the GRSPW devices. This driver can be used to implement a RTEMS I/O driver or used directly.
New features compared with old GRSPW driver: * zero-copy packet interface * possibility to send/receive mulitple packets per call * optional interrupt awaken work-task to process TX/RX queues * DMA channels support. Before only first DMA channel supported * Separate DMA and link control * Packet based error flags --- c/src/lib/libbsp/sparc/leon3/Makefile.am | 2 + c/src/lib/libbsp/sparc/leon3/preinstall.am | 4 + .../sparc/shared/include/drvmgr/ambapp_bus.h | 1 + c/src/lib/libbsp/sparc/shared/include/grspw_pkt.h | 598 +++++ c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c | 2642 ++++++++++++++++++++ cpukit/libdrvmgr/drvmgr_confdefs.h | 6 + 6 files changed, 3253 insertions(+), 0 deletions(-) create mode 100644 c/src/lib/libbsp/sparc/shared/include/grspw_pkt.h create mode 100644 c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c diff --git a/c/src/lib/libbsp/sparc/leon3/Makefile.am b/c/src/lib/libbsp/sparc/leon3/Makefile.am index 4517ee3..d50b210 100644 --- a/c/src/lib/libbsp/sparc/leon3/Makefile.am +++ b/c/src/lib/libbsp/sparc/leon3/Makefile.am @@ -151,8 +151,10 @@ libbsp_a_SOURCES += ../../sparc/shared/can/occan.c \ # SpaceWire include_HEADERS += ../../sparc/shared/include/grspw.h +include_HEADERS += ../../sparc/shared/include/grspw_pkt.h include_HEADERS += ../../sparc/shared/include/grspw_router.h libbsp_a_SOURCES += ../../sparc/shared/spw/grspw.c +libbsp_a_SOURCES += ../../sparc/shared/spw/grspw_pkt.c libbsp_a_SOURCES += ../../sparc/shared/spw/grspw_router.c # UART diff --git a/c/src/lib/libbsp/sparc/leon3/preinstall.am b/c/src/lib/libbsp/sparc/leon3/preinstall.am index 29fd095..e157862 100644 --- a/c/src/lib/libbsp/sparc/leon3/preinstall.am +++ b/c/src/lib/libbsp/sparc/leon3/preinstall.am @@ -197,6 +197,10 @@ $(PROJECT_INCLUDE)/grspw.h: ../../sparc/shared/include/grspw.h $(PROJECT_INCLUDE $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw.h +$(PROJECT_INCLUDE)/grspw_pkt.h: ../../sparc/shared/include/grspw_pkt.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw_pkt.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw_pkt.h + $(PROJECT_INCLUDE)/grspw_router.h: ../../sparc/shared/include/grspw_router.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw_router.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw_router.h diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h index 687bafd..e419862 100644 --- a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h @@ -45,6 +45,7 @@ extern "C" { #define DRIVER_AMBAPP_GAISLER_GRPWM_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRPWM) #define DRIVER_AMBAPP_GAISLER_GRPWRX_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_PW2APB) #define DRIVER_AMBAPP_GAISLER_GRSPW_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPW) +#define DRIVER_AMBAPP_GAISLER_GRSPW2_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPW2) #define DRIVER_AMBAPP_GAISLER_GRTC_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRTC) #define DRIVER_AMBAPP_GAISLER_GRTM_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRTM) #define DRIVER_AMBAPP_GAISLER_I2CMST_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_I2CMST) diff --git a/c/src/lib/libbsp/sparc/shared/include/grspw_pkt.h b/c/src/lib/libbsp/sparc/shared/include/grspw_pkt.h new file mode 100644 index 0000000..943dbe0 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grspw_pkt.h @@ -0,0 +1,598 @@ +/* + * GRSPW/GRSPW2 SpaceWire Kernel Library Interface + * + * COPYRIGHT (c) 2011 + * Cobham Gaisler AB + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifndef __GRSPW_PKT_H__ +#define __GRSPW_PKT_H__ + +struct grspw_pkt; + +/* Maximum number of GRSPW devices supported by driver */ +#define GRSPW_MAX 32 + +/* Weak overridable variable the user can use to define the worker-task + * priority (0..255) or to disable (-1) the creation of the worker-task + * and the message queue to save space */ +extern int grspw_work_task_priority; + +#ifndef GRSPW_PKT_FLAGS +#define GRSPW_PKT_FLAGS +/*** TX Packet flags ***/ + +/* Enable IRQ generation */ +#define TXPKT_FLAG_IE 0x0040 + +/* Enable Header CRC generation (if CRC is available in HW) + * Header CRC will be appended (one byte at end of header) + */ +#define TXPKT_FLAG_HCRC 0x0100 + +/* Enable Data CRC generation (if CRC is available in HW) + * Data CRC will be appended (one byte at end of packet) + */ +#define TXPKT_FLAG_DCRC 0x0200 + +/* Control how many bytes the beginning of the Header + * the CRC should not be calculated for */ +#define TXPKT_FLAG_NOCRC_MASK 0x0000000f +#define TXPKT_FLAG_NOCRC_LEN0 0x00000000 +#define TXPKT_FLAG_NOCRC_LEN1 0x00000001 +#define TXPKT_FLAG_NOCRC_LEN2 0x00000002 +#define TXPKT_FLAG_NOCRC_LEN3 0x00000003 +#define TXPKT_FLAG_NOCRC_LEN4 0x00000004 +#define TXPKT_FLAG_NOCRC_LEN5 0x00000005 +#define TXPKT_FLAG_NOCRC_LEN6 0x00000006 +#define TXPKT_FLAG_NOCRC_LEN7 0x00000007 +#define TXPKT_FLAG_NOCRC_LEN8 0x00000008 +#define TXPKT_FLAG_NOCRC_LEN9 0x00000009 +#define TXPKT_FLAG_NOCRC_LENa 0x0000000a +#define TXPKT_FLAG_NOCRC_LENb 0x0000000b +#define TXPKT_FLAG_NOCRC_LENc 0x0000000c +#define TXPKT_FLAG_NOCRC_LENd 0x0000000d +#define TXPKT_FLAG_NOCRC_LENe 0x0000000e +#define TXPKT_FLAG_NOCRC_LENf 0x0000000f + +/* Marks if packet was transmitted or not */ +#define TXPKT_FLAG_TX 0x8000 + +#define TXPKT_FLAG_INPUT_MASK (TXPKT_FLAG_NOCRC_MASK | TXPKT_FLAG_IE | \ + TXPKT_FLAG_HCRC | TXPKT_FLAG_DCRC) + +/* Link Error */ +#define TXPKT_FLAG_LINKERR 0x4000 + +#define TXPKT_FLAG_OUTPUT_MASK (TXPKT_FLAG_LINKERR) + +/*** RX Packet Flags ***/ + +/* Enable IRQ generation */ +#define RXPKT_FLAG_IE 0x0010 + +#define RXPKT_FLAG_INPUT_MASK (RXPKT_FLAG_IE) + +/* Packet was truncated */ +#define RXPKT_FLAG_TRUNK 0x0800 +/* Data CRC error (only valid if RMAP CRC is enabled) */ +#define RXPKT_FLAG_DCRC 0x0400 +/* Header CRC error (only valid if RMAP CRC is enabled) */ +#define RXPKT_FLAG_HCRC 0x0200 +/* Error in End-of-Packet */ +#define RXPKT_FLAG_EEOP 0x0100 +/* Marks if packet was recevied or not */ +#define RXPKT_FLAG_RX 0x8000 + +#define RXPKT_FLAG_OUTPUT_MASK (RXPKT_FLAG_TRUNK | RXPKT_FLAG_DCRC | \ + RXPKT_FLAG_HCRC | RXPKT_FLAG_EEOP) + +/*** General packet flag options ***/ + +/* Translate Hdr and/or Payload address */ +#define PKT_FLAG_TR_DATA 0x1000 +#define PKT_FLAG_TR_HDR 0x2000 +/* All General options */ +#define PKT_FLAG_MASK 0x3000 + +#endif +/* GRSPW RX/TX Packet structure. + * + * - For RX the 'hdr' and 'hlen' fields are not used, they are not written + * by driver. + * + * - The 'pkt_id' field is untouched by driver, it is intended for packet + * numbering or user-custom data. + * + * - The last packet in a list must have 'next' set to NULL. + * + * - data and hdr pointers are written without modification to hardware, + * this means that caller must do address translation to hardware + * address itself. + * + * - the 'flags' field are interpreted differently depending on transfer + * type (RX/TX). See XXPKT_FLAG_* options above. + */ +struct grspw_pkt { + struct grspw_pkt *next; + unsigned int pkt_id; /* User assigned ID */ + unsigned short flags; /* RX/TX Options */ + unsigned char reserved; /* Reserved, must be zero */ + unsigned char hlen; /* Length of Header Buffer */ + unsigned int dlen; /* Length of Data Buffer */ + void *data; /* 4-byte or byte aligned depends on HW */ + void *hdr; /* 4-byte or byte aligned depends on HW */ +}; + +/* GRSPW SpaceWire Packet List */ +struct grspw_list { + struct grspw_pkt *head; + struct grspw_pkt *tail; +}; + +/* SpaceWire Link State */ +typedef enum { + SPW_LS_ERRRST = 0, + SPW_LS_ERRWAIT = 1, + SPW_LS_READY = 2, + SPW_LS_CONNECTING = 3, + SPW_LS_STARTED = 4, + SPW_LS_RUN = 5 +} spw_link_state_t; + +/* Address Configuration */ +struct grspw_addr_config { + /* Ignore address field and put all received packets to first + * DMA channel. + */ + int promiscuous; + + /* Default Node Address and Mask */ + unsigned char def_addr; + unsigned char def_mask; + /* DMA Channel custom Node Address and Mask */ + struct { + char node_en; /* Enable Separate Addr */ + unsigned char node_addr; /* Node address */ + unsigned char node_mask; /* Node address mask */ + } dma_nacfg[4]; +}; + +/* Hardware Support in GRSPW Core */ +struct grspw_hw_sup { + char rmap; /* If RMAP in HW is available */ + char rmap_crc; /* If RMAP CRC is available */ + char rx_unalign; /* RX unaligned (byte boundary) access allowed*/ + char nports; /* Number of Ports (1 or 2) */ + char ndma_chans; /* Number of DMA Channels (1..4) */ + char strip_adr; /* Hardware can strip ADR from packet data */ + char strip_pid; /* Hardware can strip PID from packet data */ + int hw_version; /* GRSPW Hardware Version */ + char reserved[2]; +}; + +struct grspw_core_stats { + int irq_cnt; + int err_credit; + int err_eeop; + int err_addr; + int err_parity; + int err_escape; + int err_wsync; /* only in GRSPW1 */ +}; + +/* grspw_link_ctrl() options */ +#define LINKOPTS_ENABLE 0x0000 +#define LINKOPTS_DISABLE 0x0001 +#define LINKOPTS_START 0x0002 +#define LINKOPTS_AUTOSTART 0x0004 +#define LINKOPTS_DIS_ONERR 0x0008 +/*#define LINKOPTS_TICK_OUT_IRQ 0x0100*//* Enable Tick-out IRQ */ +#define LINKOPTS_IRQ 0x0200 /* Enable Error Link IRQ */ +#define LINKOPTS_MASK 0x020f /* All above options */ + +/* grspw_tc_ctrl() options */ +#define TCOPTS_EN_RXIRQ 0x0001 /* Tick-Out IRQ */ +#define TCOPTS_EN_TX 0x0004 +#define TCOPTS_EN_RX 0x0008 + +/* grspw_rmap_ctrl() options */ +#define RMAPOPTS_EN_RMAP 0x0001 +#define RMAPOPTS_EN_BUF 0x0002 + + +/* grspw_dma_config.flags options */ +#define DMAFLAG_NO_SPILL 0x0001 /* See HW doc DMA-CTRL NS bit */ +#define DMAFLAG_RESV1 0x0002 /* HAS NO EFFECT */ +#define DMAFLAG_STRIP_ADR 0x0004 /* See HW doc DMA-CTRL SA bit */ +#define DMAFLAG_STRIP_PID 0x0008 /* See HW doc DMA-CTRL SP bit */ +#define DMAFLAG_RESV2 0x0010 /* HAS NO EFFECT */ +#define DMAFLAG_MASK (DMAFLAG_NO_SPILL|DMAFLAG_STRIP_ADR|DMAFLAG_STRIP_PID) + +struct grspw_dma_config { + int flags; + + int rxmaxlen; /* RX Max Packet Length */ + int rx_irq_en_cnt; /* Enable RX IRQ every cnt descriptors */ + int tx_irq_en_cnt; /* Enable TX IRQ every cnt descriptors */ +}; + +/* Statistics per DMA channel */ +struct grspw_dma_stats { + /* IRQ Statistics */ + int irq_cnt; /* Number of DMA IRQs generated by channel */ + + /* Descriptor Statistics */ + int tx_pkts; /* Number of Transmitted packets */ + int tx_err_link; /* Number of Transmitted packets with Link Error*/ + int rx_pkts; /* Number of Received packets */ + int rx_err_trunk; /* Number of Received Truncated packets */ + int rx_err_endpkt; /* Number of Received packets with bad ending */ + + /* Diagnostics to help developers sizing their number buffers to avoid + * out-of-buffers or other phenomenons. + */ + int send_cnt_min; /* Minimum number of packets in TX SEND Q */ + int send_cnt_max; /* Maximum number of packets in TX SEND Q */ + int tx_sched_cnt_min; /* Minimum number of packets in TX SCHED Q */ + int tx_sched_cnt_max; /* Maximum number of packets in TX SCHED Q */ + int sent_cnt_max; /* Maximum number of packets in TX SENT Q */ + int tx_work_cnt; /* Times the work thread processed TX BDs */ + int tx_work_enabled; /* No. RX BDs enabled by work thread */ + + int ready_cnt_min; /* Minimum number of packets in RX READY Q */ + int ready_cnt_max; /* Maximum number of packets in RX READY Q */ + int rx_sched_cnt_min; /* Minimum number of packets in RX SCHED Q */ + int rx_sched_cnt_max; /* Maximum number of packets in RX SCHED Q */ + int recv_cnt_max; /* Maximum number of packets in RX RECV Q */ + int rx_work_cnt; /* Times the work thread processed RX BDs */ + int rx_work_enabled; /* No. RX BDs enabled by work thread */ +}; + +extern void grspw_initialize_user( + /* Callback every time a GRSPW device is found. Args: DeviceIndex */ + void *(*devfound)(int), + /* Callback every time a GRSPW device is removed. Args: + * int = DeviceIndex + * void* = Return Value from devfound() + */ + void (*devremove)(int,void*) + ); +extern int grspw_dev_count(void); +extern void *grspw_open(int dev_no); +extern void grspw_close(void *d); +extern void grspw_hw_support(void *d, struct grspw_hw_sup *hw); +extern void grspw_stats_read(void *d, struct grspw_core_stats *sts); +extern void grspw_stats_clr(void *d); + +/* Set and Read current node address configuration. The dma_nacfg[N] field + * represents the configuration for DMA Channel N. + * + * Set cfg->promiscous to -1 in order to only read current configuration. + */ +extern void grspw_addr_ctrl(void *d, struct grspw_addr_config *cfg); + +/*** Link Control interface ***/ +/* Read Link State */ +extern spw_link_state_t grspw_link_state(void *d); +/* options [in/out]: set to -1 to only read current config + * + * CLKDIV register contain: + * bits 7..0 : Clock Div RUN (only run-state) + * bits 15..8 : Clock Div During Startup (all link states except run-state) + */ +extern void grspw_link_ctrl(void *d, int *options, int *clkdiv); +/* Read the current value of the status register */ +extern unsigned int grspw_status(void *d); + +/*** Time Code Interface ***/ +/* Generate Tick-In (increment Time Counter, Send Time Code) */ +extern void grspw_tc_tx(void *d); +/* Control Timcode settings of core */ +extern void grspw_tc_ctrl(void *d, int *options); +/* Assign ISR Function to TimeCode RX IRQ */ +extern void grspw_tc_isr(void *d, void (*tcisr)(void *data, int tc), void *data); +/* Read/Write TCTRL and TIMECNT. Write if not -1, always read current value + * TCTRL = bits 7 and 6 + * TIMECNT = bits 5 to 0 + */ +extern void grspw_tc_time(void *d, int *time); + +/*** RMAP Control Interface ***/ +/* Set (not -1) and/or read RMAP options. */ +extern int grspw_rmap_ctrl(void *d, int *options, int *dstkey); +extern void grspw_rmap_support(void *d, char *rmap, char *rmap_crc); + +/*** SpW Port Control Interface ***/ + +/* Select port, if + * -1=The current selected port is returned + * 0=Port 0 + * 1=Port 1 + * Other positive values=Both Port0 and Port1 + */ +extern int grspw_port_ctrl(void *d, int *port); +/* Returns Number ports available in hardware */ +extern int grspw_port_count(void *d); +/* Returns the current active port */ +extern int grspw_port_active(void *d); + +/*** DMA Interface ***/ +extern void *grspw_dma_open(void *d, int chan_no); +extern void grspw_dma_close(void *c); + +extern int grspw_dma_start(void *c); +extern void grspw_dma_stop(void *c); + +/* Schedule List of packets for transmission at some point in + * future. + * + * 1. Move transmitted packets to SENT List (SCHED->SENT) + * 2. Add the requested packets to the SEND List (USER->SEND) + * 3. Schedule as many packets as possible for transmission (SEND->SCHED) + * + * Call this function with pkts=NULL to just do step 1 and 3. This may be + * required in Polling-mode. + * + * The above steps 1 and 3 may be skipped by setting 'opts': + * bit0 = 1: Skip Step 1. + * bit1 = 1: Skip Step 3. + * Skipping both step 1 and 3 may be usefull when IRQ is enabled, then + * the work queue will be totaly responsible for handling descriptors. + * + * The fastest solution in retreiving sent TX packets and sending new frames + * is to call: + * A. grspw_dma_tx_reclaim(opts=0) + * B. grspw_dma_tx_send(opts=1) + * + * NOTE: the TXPKT_FLAG_TX flag must not be set. + * + * Return Code + * -1 Error + * 0 Successfully added pkts to send/sched list + * 1 DMA stopped. No operation. + */ +extern int grspw_dma_tx_send(void *c, int opts, struct grspw_list *pkts, int count); + +/* Reclaim TX packet buffers that has previously been scheduled for transmission + * with grspw_dma_tx_send(). + * + * 1. Move transmitted packets to SENT List (SCHED->SENT) + * 2. Move all SENT List to pkts list (SENT->USER) + * 3. Schedule as many packets as possible for transmission (SEND->SCHED) + * + * The above steps 1 may be skipped by setting 'opts': + * bit0 = 1: Skip Step 1. + * bit1 = 1: Skip Step 3. + * + * The fastest solution in retreiving sent TX packets and sending new frames + * is to call: + * A. grspw_dma_tx_reclaim(opts=2) (Skip step 3) + * B. grspw_dma_tx_send(opts=1) (Skip step 1) + * + * Return Code + * -1 Error + * 0 Successful. pkts list filled with all packets from sent list + * 1 Same as 0, but indicates that DMA stopped + */ +extern int grspw_dma_tx_reclaim(void *c, int opts, struct grspw_list *pkts, int *count); + +/* Get current number of Packets in respective TX Queue. */ +extern void grspw_dma_tx_count(void *c, int *send, int *sched, int *sent); + +#define GRSPW_OP_AND 0 +#define GRSPW_OP_OR 1 +/* Block until ready_cnt or fewer packets are Queued in "Send and Scheduled" Q, + * op (AND or OR), sent_cnt or more packet "have been sent" (Sent Q) condition + * is met. + * If a link error occurs and the Stop on Link error is defined, this function + * will also return to caller. + * The timeout argument is used to return after timeout ticks, regardless of + * the other conditions. If timeout is zero, the function will wait forever + * until the condition is satisfied. + * + * NOTE: if IRQ of TX descriptors are not enabled conditions are never + * checked, this may hang infinitely unless a timeout has been specified + * + * Return Code + * -1 Error + * 0 Returing to caller because specified conditions are now fullfilled + * 1 DMA stopped + * 2 Timeout, conditions are not met + */ +extern int grspw_dma_tx_wait(void *c, int send_cnt, int op, int sent_cnt, int timeout); + +/* Get received RX packet buffers that has previously been scheduled for + * reception with grspw_dma_rx_prepare(). + * + * 1. Move Scheduled packets to RECV List (SCHED->RECV) + * 2. Move all RECV packet to the callers list (RECV->USER) + * 3. Schedule as many free packet buffers as possible (READY->SCHED) + * + * The above steps 1 may be skipped by setting 'opts': + * bit0 = 1: Skip Step 1. + * bit1 = 1: Skip Step 3. + * + * The fastest solution in retreiving received RX packets and preparing new + * packet buffers for future receive, is to call: + * A. grspw_dma_rx_recv(opts=2, &recvlist) (Skip step 3) + * B. grspw_dma_rx_prepare(opts=1, &freelist) (Skip step 1) + * + * Return Code + * -1 Error + * 0 Successfully filled pkts list with packets from recv list. + * 1 DMA stopped + */ +extern int grspw_dma_rx_recv(void *c, int opts, struct grspw_list *pkts, int *count); + +/* Add more RX packet buffers for future for reception. The received packets + * can later be read out with grspw_dma_rx_recv(). + * + * 1. Move Received packets to RECV List (SCHED->RECV) + * 2. Add the "free/ready" packet buffers to the READY List (USER->READY) + * 3. Schedule as many packets as possible (READY->SCHED) + * + * The above steps 1 may be skipped by setting 'opts': + * bit0 = 1: Skip Step 1. + * bit1 = 1: Skip Step 3. + * + * The fastest solution in retreiving received RX packets and preparing new + * packet buffers for future receive, is to call: + * A. grspw_dma_rx_recv(opts=2, &recvlist) (Skip step 3) + * B. grspw_dma_rx_prepare(opts=1, &freelist) (Skip step 1) + * + * Return Code + * -1 Error + * 0 Successfully added packet buffers from pkt list into the ready queue + * 1 DMA stopped + */ +extern int grspw_dma_rx_prepare(void *c, int opts, struct grspw_list *pkts, int count); + +/* Get current number of Packets in respective RX Queue. */ +extern void grspw_dma_rx_count(void *c, int *ready, int *sched, int *recv); + +/* Block until recv_cnt or more packets are Queued in RECV Q, op (AND or OR), + * ready_cnt or fewer packet buffers are available in the "READY and Scheduled" Q, + * condition is met. + * If a link error occurs and the Stop on Link error is defined, this function + * will also return to caller, however with an error. + * The timeout argument is used to return after timeout ticks, regardless of + * the other conditions. If timeout is zero, the function will wait forever + * until the condition is satisfied. + * + * NOTE: if IRQ of TX descriptors are not enabled conditions are never + * checked, this may hang infinitely unless a timeout has been specified + * + * Return Code + * -1 Error + * 0 Returing to caller because specified conditions are now fullfilled + * 1 DMA stopped + * 2 Timeout, conditions are not met + */ +extern int grspw_dma_rx_wait(void *c, int recv_cnt, int op, int ready_cnt, int timeout); + +extern int grspw_dma_config(void *c, struct grspw_dma_config *cfg); +extern void grspw_dma_config_read(void *c, struct grspw_dma_config *cfg); + +extern void grspw_dma_stats_read(void *c, struct grspw_dma_stats *sts); +extern void grspw_dma_stats_clr(void *c); + +/*** GRSPW SpaceWire Packet List Handling Routines ***/ + +static inline void grspw_list_clr(struct grspw_list *list) +{ + list->head = NULL; + list->tail = NULL; +} + +static inline int grspw_list_is_empty(struct grspw_list *list) +{ + return (list->head == NULL); +} + +/* Return Number of entries in list */ +static inline int grspw_list_cnt(struct grspw_list *list) +{ + struct grspw_pkt *lastpkt = NULL, *pkt = list->head; + int cnt = 0; + while ( pkt ) { + cnt++; + lastpkt = pkt; + pkt = pkt->next; + } + if ( lastpkt && (list->tail != lastpkt) ) + return -1; + return cnt; +} + +static inline void +grspw_list_append(struct grspw_list *list, struct grspw_pkt *pkt) +{ + pkt->next = NULL; + if ( list->tail == NULL ) { + list->head = pkt; + } else { + list->tail->next = pkt; + } + list->tail = pkt; +} + +static inline void +grspw_list_prepend(struct grspw_list *list, struct grspw_pkt *pkt) +{ + pkt->next = list->head; + if ( list->head == NULL ) { + list->tail = pkt; + } + list->head = pkt; +} + +static inline void +grspw_list_append_list(struct grspw_list *list, struct grspw_list *alist) +{ + alist->tail->next = NULL; + if ( list->tail == NULL ) { + list->head = alist->head; + } else { + list->tail->next = alist->head; + } + list->tail = alist->tail; +} + +static inline void +grspw_list_prepend_list(struct grspw_list *list, struct grspw_list *alist) +{ + if ( list->head == NULL ) { + list->tail = alist->tail; + alist->tail->next = NULL; + } else { + alist->tail->next = list->head; + } + list->head = alist->head; +} + +/* Remove dlist (delete-list) from head of list */ +static inline void +grspw_list_remove_head_list(struct grspw_list *list, struct grspw_list *dlist) +{ + list->head = dlist->tail->next; + if ( list->head == NULL ) { + list->tail = NULL; + } + dlist->tail->next = NULL; +} + +/* Take A number of entries from head of list 'list' and put the entires + * to rlist (result list). + */ +static inline int +grspw_list_take_head_list(struct grspw_list *list, struct grspw_list *rlist, int max) +{ + int cnt; + struct grspw_pkt *pkt, *last; + + pkt = list->head; + + if ( (max < 1) || (pkt == NULL) ) { + grspw_list_clr(rlist); + return 0; + } + + cnt = 0; + rlist->head = pkt; + last = pkt; + while ((cnt < max) && pkt) { + last = pkt; + pkt = pkt->next; + cnt++; + } + rlist->tail = last; + grspw_list_remove_head_list(list, rlist); + return cnt; +} + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c b/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c new file mode 100644 index 0000000..6b6820a --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/spw/grspw_pkt.c @@ -0,0 +1,2642 @@ +/* + * Cobham Gaisler GRSPW/GRSPW2 SpaceWire Kernel Library Interface for RTEMS. + * + * This driver can be used to implement a standard I/O system "char"-driver + * or used directly. NOTE SMP support has not been tested. + * + * COPYRIGHT (c) 2011 + * Cobham Gaisler AB + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <malloc.h> +#include <rtems/bspIo.h> + +#include <drvmgr/drvmgr.h> +#include <ambapp.h> +#include <drvmgr/ambapp_bus.h> +#include <grspw_pkt.h> + +/* This driver has been prepared for SMP operation however never tested + * on a SMP system - use on your own risk. + */ +#ifdef RTEMS_HAS_SMP + +#include <rtems/score/smplock.h> /* spin-lock */ + +/* SPIN_LOCK() and SPIN_UNLOCK() NOT_IMPLEMENTED_BY_RTEMS. Use _IRQ version + * to implement. + */ +#define SPIN_DECLARE(name) SMP_lock_spinlock_simple_Control name +#define SPIN_INIT(lock) _SMP_lock_spinlock_simple_Initialize(lock) +#define SPIN_LOCK(lock, level) SPIN_LOCK_IRQ(lock, level) +#define SPIN_LOCK_IRQ(lock, level) (level) = _SMP_lock_spinlock_simple_Obtain(lock) +#define SPIN_UNLOCK(lock, level) SPIN_UNLOCK_IRQ(lock, level) +#define SPIN_UNLOCK_IRQ(lock, level) _SMP_lock_spinlock_simple_Release(lock, level) +#define IRQFLAGS_TYPE ISR_Level + +#else + +#define SPIN_DECLARE(name) +#define SPIN_INIT(lock) +#define SPIN_LOCK(lock, level) +#define SPIN_LOCK_IRQ(lock, level) rtems_interrupt_disable(level) +#define SPIN_UNLOCK(lock, level) +#define SPIN_UNLOCK_IRQ(lock, level) rtems_interrupt_enable(level) +#define IRQFLAGS_TYPE rtems_interrupt_level + +#endif + +/*#define STATIC*/ +#define STATIC static + +/*#define GRSPW_DBG(args...) printk(args)*/ +#define GRSPW_DBG(args...) + +struct grspw_dma_regs { + volatile unsigned int ctrl; /* DMA Channel Control */ + volatile unsigned int rxmax; /* RX Max Packet Length */ + volatile unsigned int txdesc; /* TX Descriptor Base/Current */ + volatile unsigned int rxdesc; /* RX Descriptor Base/Current */ + volatile unsigned int addr; /* Address Register */ + volatile unsigned int resv[3]; +}; + +struct grspw_regs { + volatile unsigned int ctrl; + volatile unsigned int status; + volatile unsigned int nodeaddr; + volatile unsigned int clkdiv; + volatile unsigned int destkey; + volatile unsigned int time; + volatile unsigned int timer; /* Used only in GRSPW1 */ + volatile unsigned int resv1; + + /* DMA Registers, ctrl.NCH determines number of ports, + * up to 4 channels are supported + */ + struct grspw_dma_regs dma[4]; +}; + +/* GRSPW - Control Register - 0x00 */ +#define GRSPW_CTRL_RA_BIT 31 +#define GRSPW_CTRL_RX_BIT 30 +#define GRSPW_CTRL_RC_BIT 29 +#define GRSPW_CTRL_NCH_BIT 27 +#define GRSPW_CTRL_PO_BIT 26 +#define GRSPW_CTRL_PS_BIT 21 +#define GRSPW_CTRL_NP_BIT 20 +#define GRSPW_CTRL_RD_BIT 17 +#define GRSPW_CTRL_RE_BIT 16 +#define GRSPW_CTRL_TR_BIT 11 +#define GRSPW_CTRL_TT_BIT 10 +#define GRSPW_CTRL_LI_BIT 9 +#define GRSPW_CTRL_TQ_BIT 8 +#define GRSPW_CTRL_RS_BIT 6 +#define GRSPW_CTRL_PM_BIT 5 +#define GRSPW_CTRL_TI_BIT 4 +#define GRSPW_CTRL_IE_BIT 3 +#define GRSPW_CTRL_AS_BIT 2 +#define GRSPW_CTRL_LS_BIT 1 +#define GRSPW_CTRL_LD_BIT 0 + +#define GRSPW_CTRL_RA (1<<GRSPW_CTRL_RA_BIT) +#define GRSPW_CTRL_RX (1<<GRSPW_CTRL_RX_BIT) +#define GRSPW_CTRL_RC (1<<GRSPW_CTRL_RC_BIT) +#define GRSPW_CTRL_NCH (0x3<<GRSPW_CTRL_NCH_BIT) +#define GRSPW_CTRL_PO (1<<GRSPW_CTRL_PO_BIT) +#define GRSPW_CTRL_PS (1<<GRSPW_CTRL_PS_BIT) +#define GRSPW_CTRL_NP (1<<GRSPW_CTRL_NP_BIT) +#define GRSPW_CTRL_RD (1<<GRSPW_CTRL_RD_BIT) +#define GRSPW_CTRL_RE (1<<GRSPW_CTRL_RE_BIT) +#define GRSPW_CTRL_TR (1<<GRSPW_CTRL_TR_BIT) +#define GRSPW_CTRL_TT (1<<GRSPW_CTRL_TT_BIT) +#define GRSPW_CTRL_LI (1<<GRSPW_CTRL_LI_BIT) +#define GRSPW_CTRL_TQ (1<<GRSPW_CTRL_TQ_BIT) +#define GRSPW_CTRL_RS (1<<GRSPW_CTRL_RS_BIT) +#define GRSPW_CTRL_PM (1<<GRSPW_CTRL_PM_BIT) +#define GRSPW_CTRL_TI (1<<GRSPW_CTRL_TI_BIT) +#define GRSPW_CTRL_IE (1<<GRSPW_CTRL_IE_BIT) +#define GRSPW_CTRL_AS (1<<GRSPW_CTRL_AS_BIT) +#define GRSPW_CTRL_LS (1<<GRSPW_CTRL_LS_BIT) +#define GRSPW_CTRL_LD (1<<GRSPW_CTRL_LD_BIT) + +/* GRSPW - Status Register - 0x04 */ +#define GRSPW_STS_LS_BIT 21 +#define GRSPW_STS_AP_BIT 9 +#define GRSPW_STS_EE_BIT 8 +#define GRSPW_STS_IA_BIT 7 +#define GRSPW_STS_WE_BIT 6 +#define GRSPW_STS_PE_BIT 4 +#define GRSPW_STS_DE_BIT 3 +#define GRSPW_STS_ER_BIT 2 +#define GRSPW_STS_CE_BIT 1 +#define GRSPW_STS_TO_BIT 0 + +#define GRSPW_STS_LS (0x7<<GRSPW_STS_LS_BIT) +#define GRSPW_STS_AP (1<<GRSPW_STS_AP_BIT) +#define GRSPW_STS_EE (1<<GRSPW_STS_EE_BIT) +#define GRSPW_STS_IA (1<<GRSPW_STS_IA_BIT) +#define GRSPW_STS_WE (1<<GRSPW_STS_WE_BIT) +#define GRSPW_STS_PE (1<<GRSPW_STS_PE_BIT) +#define GRSPW_STS_DE (1<<GRSPW_STS_DE_BIT) +#define GRSPW_STS_ER (1<<GRSPW_STS_ER_BIT) +#define GRSPW_STS_CE (1<<GRSPW_STS_CE_BIT) +#define GRSPW_STS_TO (1<<GRSPW_STS_TO_BIT) + +/* GRSPW - Default Address Register - 0x08 */ +#define GRSPW_DEF_ADDR_BIT 0 +#define GRSPW_DEF_MASK_BIT 8 +#define GRSPW_DEF_ADDR (0xff<<GRSPW_DEF_ADDR_BIT) +#define GRSPW_DEF_MASK (0xff<<GRSPW_DEF_MASK_BIT) + +/* GRSPW - Clock Divisor Register - 0x0C */ +#define GRSPW_CLKDIV_START_BIT 8 +#define GRSPW_CLKDIV_RUN_BIT 0 +#define GRSPW_CLKDIV_START (0xff<<GRSPW_CLKDIV_START_BIT) +#define GRSPW_CLKDIV_RUN (0xff<<GRSPW_CLKDIV_RUN_BIT) +#define GRSPW_CLKDIV_MASK (GRSPW_CLKDIV_START|GRSPW_CLKDIV_RUN) + +/* GRSPW - Destination key Register - 0x10 */ +#define GRSPW_DK_DESTKEY_BIT 0 +#define GRSPW_DK_DESTKEY (0xff<<GRSPW_DK_DESTKEY_BIT) + +/* GRSPW - Time Register - 0x14 */ +#define GRSPW_TIME_CTRL_BIT 0 +#define GRSPW_TIME_CNT_BIT 6 +#define GRSPW_TIME_CTRL (0x3f<<GRSPW_TIME_CTRL_BIT) +#define GRSPW_TIME_TCNT (0x3<<GRSPW_TIME_CNT_BIT) + +/* GRSPW - DMA Control Register - 0x20*N */ +#define GRSPW_DMACTRL_LE_BIT 16 +#define GRSPW_DMACTRL_SP_BIT 15 +#define GRSPW_DMACTRL_SA_BIT 14 +#define GRSPW_DMACTRL_EN_BIT 13 +#define GRSPW_DMACTRL_NS_BIT 12 +#define GRSPW_DMACTRL_RD_BIT 11 +#define GRSPW_DMACTRL_RX_BIT 10 +#define GRSPW_DMACTRL_AT_BIT 9 +#define GRSPW_DMACTRL_RA_BIT 8 +#define GRSPW_DMACTRL_TA_BIT 7 +#define GRSPW_DMACTRL_PR_BIT 6 +#define GRSPW_DMACTRL_PS_BIT 5 +#define GRSPW_DMACTRL_AI_BIT 4 +#define GRSPW_DMACTRL_RI_BIT 3 +#define GRSPW_DMACTRL_TI_BIT 2 +#define GRSPW_DMACTRL_RE_BIT 1 +#define GRSPW_DMACTRL_TE_BIT 0 + +#define GRSPW_DMACTRL_LE (1<<GRSPW_DMACTRL_LE_BIT) +#define GRSPW_DMACTRL_SP (1<<GRSPW_DMACTRL_SP_BIT) +#define GRSPW_DMACTRL_SA (1<<GRSPW_DMACTRL_SA_BIT) +#define GRSPW_DMACTRL_EN (1<<GRSPW_DMACTRL_EN_BIT) +#define GRSPW_DMACTRL_NS (1<<GRSPW_DMACTRL_NS_BIT) +#define GRSPW_DMACTRL_RD (1<<GRSPW_DMACTRL_RD_BIT) +#define GRSPW_DMACTRL_RX (1<<GRSPW_DMACTRL_RX_BIT) +#define GRSPW_DMACTRL_AT (1<<GRSPW_DMACTRL_AT_BIT) +#define GRSPW_DMACTRL_RA (1<<GRSPW_DMACTRL_RA_BIT) +#define GRSPW_DMACTRL_TA (1<<GRSPW_DMACTRL_TA_BIT) +#define GRSPW_DMACTRL_PR (1<<GRSPW_DMACTRL_PR_BIT) +#define GRSPW_DMACTRL_PS (1<<GRSPW_DMACTRL_PS_BIT) +#define GRSPW_DMACTRL_AI (1<<GRSPW_DMACTRL_AI_BIT) +#define GRSPW_DMACTRL_RI (1<<GRSPW_DMACTRL_RI_BIT) +#define GRSPW_DMACTRL_TI (1<<GRSPW_DMACTRL_TI_BIT) +#define GRSPW_DMACTRL_RE (1<<GRSPW_DMACTRL_RE_BIT) +#define GRSPW_DMACTRL_TE (1<<GRSPW_DMACTRL_TE_BIT) + +/* GRSPW - DMA Channel Max Packet Length Register - (0x20*N + 0x04) */ +#define GRSPW_DMARXLEN_MAX_BIT 0 +#define GRSPW_DMARXLEN_MAX (0xffffff<<GRSPW_DMARXLEN_MAX_BIT) + +/* GRSPW - DMA Channel Address Register - (0x20*N + 0x10) */ +#define GRSPW_DMAADR_ADDR_BIT 0 +#define GRSPW_DMAADR_MASK_BIT 8 +#define GRSPW_DMAADR_ADDR (0xff<<GRSPW_DMAADR_ADDR_BIT) +#define GRSPW_DMAADR_MASK (0xff<<GRSPW_DMAADR_MASK_BIT) + +/* RX Buffer Descriptor */ +struct grspw_rxbd { + volatile unsigned int ctrl; + volatile unsigned int addr; +}; + +/* TX Buffer Descriptor */ +struct grspw_txbd { + volatile unsigned int ctrl; + volatile unsigned int haddr; + volatile unsigned int dlen; + volatile unsigned int daddr; +}; + +/* GRSPW - DMA RXBD Ctrl */ +#define GRSPW_RXBD_LEN_BIT 0 +#define GRSPW_RXBD_LEN (0x1ffffff<<GRSPW_RXBD_LEN_BIT) +#define GRSPW_RXBD_EN (1<<25) +#define GRSPW_RXBD_WR (1<<26) +#define GRSPW_RXBD_IE (1<<27) +#define GRSPW_RXBD_EP (1<<28) +#define GRSPW_RXBD_HC (1<<29) +#define GRSPW_RXBD_DC (1<<30) +#define GRSPW_RXBD_TR (1<<31) + +#define GRSPW_TXBD_HLEN (0xff<<0) +#define GRSPW_TXBD_NCL (0xf<<8) +#define GRSPW_TXBD_EN (1<<12) +#define GRSPW_TXBD_WR (1<<13) +#define GRSPW_TXBD_IE (1<<14) +#define GRSPW_TXBD_LE (1<<15) +#define GRSPW_TXBD_HC (1<<16) +#define GRSPW_TXBD_DC (1<<17) + +#define GRSPW_DMAADR_MASK_BIT 8 +#define GRSPW_DMAADR_ADDR (0xff<<GRSPW_DMAADR_ADDR_BIT) +#define GRSPW_DMAADR_MASK (0xff<<GRSPW_DMAADR_MASK_BIT) + + +/* GRSPW Error Condition */ +#define GRSPW_STAT_ERROR (GRSPW_STS_EE | GRSPW_STS_IA | GRSPW_STS_WE | GRSPW_STS_PE | GRSPW_STS_DE | GRSPW_STS_ER | GRSPW_STS_CE) +#define GRSPW_DMA_STATUS_ERROR (GRSPW_DMACTRL_RA | GRSPW_DMACTRL_TA) +/* GRSPW Link configuration options */ +#define GRSPW_LINK_CFG (GRSPW_CTRL_LI | GRSPW_CTRL_LD | GRSPW_CTRL_LS | GRSPW_CTRL_AS) +#define GRSPW_LINKSTATE(status) ((status & GRSPW_CTRL_LS) >> GRSPW_CTRL_LS_BIT) + +/* Software Defaults */ +#define DEFAULT_RXMAX 1024 /* 1 KBytes Max RX Packet Size */ + +/* GRSPW Constants */ +#define GRSPW_TXBD_NR 64 /* Maximum number of TX Descriptors */ +#define GRSPW_RXBD_NR 128 /* Maximum number of RX Descriptors */ +#define BDTAB_SIZE 0x400 /* BD Table Size (RX or TX) */ +#define BDTAB_ALIGN 0x400 /* BD Table Alignment Requirement */ + +/* Memory and HW Registers Access routines. All 32-bit access routines */ +#define BD_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (unsigned int)(val)) +/*#define BD_READ(addr) (*(volatile unsigned int *)(addr))*/ +#define BD_READ(addr) leon_r32_no_cache((unsigned long)(addr)) +#define REG_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (unsigned int)(val)) +#define REG_READ(addr) (*(volatile unsigned int *)(addr)) + +struct grspw_ring { + struct grspw_ring *next; /* Next Descriptor */ + union { + struct grspw_txbd *tx; /* Descriptor Address */ + struct grspw_rxbd *rx; /* Descriptor Address */ + } bd; + struct grspw_pkt *pkt; /* Packet description associated.NULL if none*/ +}; + +/* An entry in the TX descriptor Ring */ +struct grspw_txring { + struct grspw_txring *next; /* Next Descriptor */ + struct grspw_txbd *bd; /* Descriptor Address */ + struct grspw_pkt *pkt; /* Packet description associated.NULL if none*/ +}; + +/* An entry in the RX descriptor Ring */ +struct grspw_rxring { + struct grspw_rxring *next; /* Next Descriptor */ + struct grspw_rxbd *bd; /* Descriptor Address */ + struct grspw_pkt *pkt; /* Packet description associated.NULL if none*/ +}; + + +struct grspw_dma_priv { + struct grspw_priv *core; /* GRSPW Core */ + struct grspw_dma_regs *regs; /* DMA Channel Registers */ + int index; /* DMA Channel Index @ GRSPW core */ + int open; /* DMA Channel opened by user */ + int started; /* DMA Channel activity (start|stop) */ + rtems_id sem_dma; /* DMA Channel Semaphore */ + struct grspw_dma_stats stats; /* DMA Channel Statistics */ + struct grspw_dma_config cfg; /* DMA Channel Configuration */ + + /*** RX ***/ + + /* RX Descriptor Ring */ + struct grspw_rxbd *rx_bds; /* Descriptor Address */ + struct grspw_rxbd *rx_bds_hwa; /* Descriptor HW Address */ + struct grspw_rxring *rx_ring_base; + struct grspw_rxring *rx_ring_head; /* Next descriptor to enable */ + struct grspw_rxring *rx_ring_tail; /* Oldest enabled Descriptor */ + int rx_irq_en_cnt_curr; + struct { + int waiting; + int ready_cnt; + int op; + int recv_cnt; + rtems_id sem_wait; /* RX Semaphore used to implement RX blocking */ + } rx_wait; + + /* Queue of Packets READY to be scheduled */ + struct grspw_list ready; + int ready_cnt; + + /* Scheduled RX Packets Queue */ + struct grspw_list rx_sched; + int rx_sched_cnt; + + /* Queue of Packets that has been RECIEVED */ + struct grspw_list recv; + int recv_cnt; + + + /*** TX ***/ + + /* TX Descriptor Ring */ + struct grspw_txbd *tx_bds; /* Descriptor Address */ + struct grspw_txbd *tx_bds_hwa; /* Descriptor HW Address */ + struct grspw_txring *tx_ring_base; + struct grspw_txring *tx_ring_head; + struct grspw_txring *tx_ring_tail; + int tx_irq_en_cnt_curr; + struct { + int waiting; + int send_cnt; + int op; + int sent_cnt; + rtems_id sem_wait; /* TX Semaphore used to implement TX blocking */ + } tx_wait; + + /* Queue of Packets ready to be scheduled for transmission */ + struct grspw_list send; + int send_cnt; + + /* Scheduled TX Packets Queue */ + struct grspw_list tx_sched; + int tx_sched_cnt; + + /* Queue of Packets that has been SENT */ + struct grspw_list sent; + int sent_cnt; +}; + +struct grspw_priv { + char devname[8]; /* Device name "grspw%d" */ + struct drvmgr_dev *dev; /* Device */ + struct grspw_regs *regs; /* Virtual Address of APB Registers */ + int irq; /* AMBA IRQ number of core */ + int index; /* Index in order it was probed */ + int core_index; /* Core Bus Index */ + int open; /* If Device is alrady opened (=1) or not (=0) */ + void *data; /* User private Data for this device instance, set by grspw_initialize_user */ + + /* Features supported by Hardware */ + struct grspw_hw_sup hwsup; + + /* Pointer to an array of Maximally 4 DMA Channels */ + struct grspw_dma_priv *dma; + + /* Spin-lock ISR protection */ + SPIN_DECLARE(devlock); + + /* Descriptor Memory Area for TX & RX and all DMA channels */ + unsigned int bd_mem; + unsigned int bd_mem_alloced; + + /*** Time Code Handling ***/ + void (*tcisr)(void *data, int timecode); + void *tcisr_arg; + + /* Disable Link on SpW Link error */ + int dis_link_on_err; + + /* "Core Global" Statistics gathered, not dependent on DMA channel */ + struct grspw_core_stats stats; +}; + +int grspw_initialized = 0; +int grspw_count = 0; +struct workqueue_struct *grspw_workq = NULL; +rtems_id grspw_sem; +static struct grspw_priv *priv_tab[GRSPW_MAX]; + +/* callback to upper layer when devices are discovered/removed */ +void *(*grspw_dev_add)(int) = NULL; +void (*grspw_dev_del)(int,void*) = NULL; + +/* USER OVERRIDABLE - The work task priority. Set to -1 to disable creating + * the work-task and work-queue to save space. + */ +int grspw_work_task_priority __attribute__((weak)) = 100; +int grspw_task_stop = 0; +rtems_id grspw_work_task; +rtems_id grspw_work_queue = 0; +#define WORK_NONE 0 +#define WORK_SHUTDOWN 0x100 +#define WORK_DMA(channel) (0x1 << (channel)) +#define WORK_DMA_MASK 0xf /* max 4 channels */ +#define WORK_CORE_BIT 16 +#define WORK_CORE_MASK 0xffff +#define WORK_CORE(device) ((device) << WORK_CORE_BIT) + +STATIC void grspw_hw_stop(struct grspw_priv *priv); +STATIC void grspw_hw_dma_stop(struct grspw_dma_priv *dma); +STATIC void grspw_dma_reset(struct grspw_dma_priv *dma); +STATIC void grspw_dma_stop_locked(struct grspw_dma_priv *dma); +STATIC void grspw_isr(void *data); + +void *grspw_open(int dev_no) +{ + struct grspw_priv *priv; + unsigned int bdtabsize, hwa; + int i; + union drvmgr_key_value *value; + + if (grspw_initialized != 1 || (dev_no >= grspw_count)) + return NULL; + + priv = priv_tab[dev_no]; + + /* Take GRSPW lock - Wait until we get semaphore */ + if (rtems_semaphore_obtain(grspw_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return NULL; + + if (priv->open) { + priv = NULL; + goto out; + } + + /* Initialize Spin-lock for GRSPW Device. This is to protect + * CTRL and DMACTRL registers from ISR. + */ + SPIN_INIT(&priv->devlock); + + priv->tcisr = NULL; + priv->tcisr_arg = NULL; + + grspw_stats_clr(priv); + + /* Allocate TX & RX Descriptor memory area for all DMA + * channels. Max-size descriptor area is allocated (or user assigned): + * - 128 RX descriptors per DMA Channel + * - 64 TX descriptors per DMA Channel + */ + bdtabsize = 2 * BDTAB_SIZE * priv->hwsup.ndma_chans; + value = drvmgr_dev_key_get(priv->dev, "bdDmaArea", KEY_TYPE_INT); + if (value) { + priv->bd_mem = value->i; + priv->bd_mem_alloced = 0; + if (priv->bd_mem & (BDTAB_ALIGN-1)) { + GRSPW_DBG("GRSPW[%d]: user-def DMA-area not aligned", + priv->index); + priv = NULL; + goto out; + } + } else { + priv->bd_mem_alloced = (unsigned int)malloc(bdtabsize + BDTAB_ALIGN - 1); + if (priv->bd_mem_alloced == 0) { + priv = NULL; + goto out; + } + /* Align memory */ + priv->bd_mem = (priv->bd_mem_alloced + (BDTAB_ALIGN - 1)) & + ~(BDTAB_ALIGN-1); + } + + /* Translate into DMA address that HW can use to access DMA + * descriptors + */ + drvmgr_translate_check( + priv->dev, + CPUMEM_TO_DMA, + (void *)priv->bd_mem, + (void **)&hwa, + bdtabsize); + + GRSPW_DBG("GRSPW%d DMA descriptor table setup: (alloced:%p, bd_mem:%p, size: %d)\n", + priv->index, priv->bd_mem_alloced, priv->bd_mem, bdtabsize + BDTAB_ALIGN - 1); + for (i=0; i<priv->hwsup.ndma_chans; i++) { + /* Do DMA Channel Init, other variables etc. are inited + * when respective DMA channel is opened. + * + * index & core are initialized by probe function. + */ + priv->dma[i].open = 0; + priv->dma[i].rx_bds = (struct grspw_rxbd *) + (priv->bd_mem + i*BDTAB_SIZE*2); + priv->dma[i].rx_bds_hwa = (struct grspw_rxbd *) + (hwa + BDTAB_SIZE*(2*i)); + priv->dma[i].tx_bds = (struct grspw_txbd *) + (priv->bd_mem + BDTAB_SIZE*(2*i+1)); + priv->dma[i].tx_bds_hwa = (struct grspw_txbd *) + (hwa + BDTAB_SIZE*(2*i+1)); + GRSPW_DBG(" DMA[%i]: RX %p - %p (%p - %p) TX %p - %p (%p - %p)\n", + i, + priv->dma[i].rx_bds, (void *)priv->dma[i].rx_bds + BDTAB_SIZE - 1, + priv->dma[i].rx_bds_hwa, (void *)priv->dma[i].rx_bds_hwa + BDTAB_SIZE - 1, + priv->dma[i].tx_bds, (void *)priv->dma[i].tx_bds + BDTAB_SIZE - 1, + priv->dma[i].tx_bds_hwa, (void *)priv->dma[i].tx_bds_hwa + BDTAB_SIZE - 1); + } + + /* Basic initialization of hardware, clear some registers but + * keep Link/RMAP/Node-Address registers intact. + */ + grspw_hw_stop(priv); + + /* Register Interrupt handler and enable IRQ at IRQ ctrl */ + drvmgr_interrupt_register(priv->dev, 0, priv->devname, grspw_isr, priv); + + /* Take the device */ + priv->open = 1; +out: + rtems_semaphore_release(grspw_sem); + return priv; +} + +void grspw_close(void *d) +{ + struct grspw_priv *priv = d; + int i; + + /* Take GRSPW lock - Wait until we get semaphore */ + if (rtems_semaphore_obtain(grspw_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return; + + /* Stop Hardware from doing DMA, put HW into "startup-state", + * Stop hardware from generating IRQ. + */ + for (i=0; i<priv->hwsup.ndma_chans; i++) + grspw_dma_close(&priv->dma[i]); + grspw_hw_stop(priv); + + /* Mark not open */ + priv->open = 0; + + rtems_semaphore_release(grspw_sem); + + /* Check that all threads are out? */ +} + +void grspw_hw_support(void *d, struct grspw_hw_sup *hw) +{ + struct grspw_priv *priv = d; + + *hw = priv->hwsup; +} + +void grspw_addr_ctrl(void *d, struct grspw_addr_config *cfg) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl, nodeaddr; + IRQFLAGS_TYPE irqflags; + int i; + + if (!priv || !cfg) + return; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + + if (cfg->promiscuous != -1) { + /* Set Configuration */ + ctrl = REG_READ(®s->ctrl); + if (cfg->promiscuous) + ctrl |= GRSPW_CTRL_PM; + else + ctrl &= ~GRSPW_CTRL_PM; + REG_WRITE(®s->ctrl, ctrl); + REG_WRITE(®s->nodeaddr, (cfg->def_mask<<8) | cfg->def_addr); + + for (i=0; i<priv->hwsup.ndma_chans; i++) { + ctrl = REG_READ(®s->dma[i].ctrl); + ctrl &= ~(GRSPW_DMACTRL_PS|GRSPW_DMACTRL_PR|GRSPW_DMA_STATUS_ERROR); + if (cfg->dma_nacfg[i].node_en) { + ctrl |= GRSPW_DMACTRL_EN; + REG_WRITE(®s->dma[i].addr, + (cfg->dma_nacfg[i].node_addr & 0xff) | + ((cfg->dma_nacfg[i].node_mask & 0xff)<<8)); + } else { + ctrl &= ~GRSPW_DMACTRL_EN; + } + REG_WRITE(®s->dma[i].ctrl, ctrl); + } + } + + /* Read Current Configuration */ + cfg->promiscuous = REG_READ(®s->ctrl) & GRSPW_CTRL_PM; + nodeaddr = REG_READ(®s->nodeaddr); + cfg->def_addr = (nodeaddr & GRSPW_DEF_ADDR) >> GRSPW_DEF_ADDR_BIT; + cfg->def_mask = (nodeaddr & GRSPW_DEF_MASK) >> GRSPW_DEF_MASK_BIT; + for (i=0; i<priv->hwsup.ndma_chans; i++) { + cfg->dma_nacfg[i].node_en = REG_READ(®s->dma[i].ctrl) & + GRSPW_DMACTRL_EN; + ctrl = REG_READ(®s->dma[i].addr); + cfg->dma_nacfg[i].node_addr = (ctrl & GRSPW_DMAADR_ADDR) >> + GRSPW_DMAADR_ADDR_BIT; + cfg->dma_nacfg[i].node_mask = (ctrl & GRSPW_DMAADR_MASK) >> + GRSPW_DMAADR_MASK_BIT; + } + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + for (; i<4; i++) { + cfg->dma_nacfg[i].node_en = 0; + cfg->dma_nacfg[i].node_addr = 0; + cfg->dma_nacfg[i].node_mask = 0; + } +} + +/* Return Current Status Register */ +unsigned int grspw_link_status(void *d) +{ + struct grspw_priv *priv = d; + + return REG_READ(&priv->regs->status); +} + +/* Return Current Link State */ +spw_link_state_t grspw_link_state(void *d) +{ + struct grspw_priv *priv = d; + unsigned int status = REG_READ(&priv->regs->status); + + return (status & GRSPW_STS_LS) >> GRSPW_STS_LS_BIT; +} + +/* options and clkdiv [in/out]: set to -1 to only read current config */ +void grspw_link_ctrl(void *d, int *options, int *clkdiv) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + /* Write? */ + if (clkdiv) { + if (*clkdiv != -1) + REG_WRITE(®s->clkdiv, *clkdiv & GRSPW_CLKDIV_MASK); + *clkdiv = REG_READ(®s->clkdiv) & GRSPW_CLKDIV_MASK; + } + if (options) { + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(®s->ctrl); + if (*options != -1) { + ctrl = (ctrl & ~GRSPW_LINK_CFG) | + (*options & GRSPW_LINK_CFG); + + /* Enable Global IRQ only of LI or TQ is set */ + if (ctrl & (GRSPW_CTRL_LI|GRSPW_CTRL_TQ)) + ctrl |= GRSPW_CTRL_IE; + else + ctrl &= ~GRSPW_CTRL_IE; + + REG_WRITE(®s->ctrl, ctrl); + priv->dis_link_on_err = (*options & LINKOPTS_DIS_ONERR) >> 3; + } + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + *options = (ctrl & GRSPW_LINK_CFG)|(priv->dis_link_on_err << 3); + } +} + +/* Generate Tick-In (increment Time Counter, Send Time Code) */ +void grspw_tc_tx(void *d) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + IRQFLAGS_TYPE irqflags; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + REG_WRITE(®s->ctrl, REG_READ(®s->ctrl) | GRSPW_CTRL_TI); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); +} + +void grspw_tc_ctrl(void *d, int *options) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + if (options == NULL) + return; + + /* Write? */ + if (*options != -1) { + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(®s->ctrl); + ctrl &= ~(GRSPW_CTRL_TR|GRSPW_CTRL_TT|GRSPW_CTRL_TQ); + ctrl |= (*options & 0xd) << GRSPW_CTRL_TQ_BIT; + + /* Enable Global IRQ only of LI or TQ is set */ + if (ctrl & (GRSPW_CTRL_LI|GRSPW_CTRL_TQ)) + ctrl |= GRSPW_CTRL_IE; + else + ctrl &= ~GRSPW_CTRL_IE; + + REG_WRITE(®s->ctrl, ctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } else + ctrl = REG_READ(®s->ctrl); + *options = (ctrl >> GRSPW_CTRL_TQ_BIT) & 0xd; +} + +/* Assign ISR Function to TimeCode RX IRQ */ +void grspw_tc_isr(void *d, void (*tcisr)(void *data, int tc), void *data) +{ + struct grspw_priv *priv = d; + + priv->tcisr_arg = data; + priv->tcisr = tcisr; +} + +/* Read/Write TCTRL and TIMECNT. Write if not -1, always read current value + * TCTRL = bits 7 and 6 + * TIMECNT = bits 5 to 0 + */ +void grspw_tc_time(void *d, int *time) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + + if (time == NULL) + return; + if (*time != -1) + REG_WRITE(®s->time, *time & (GRSPW_TIME_TCNT | GRSPW_TIME_CTRL)); + *time = REG_READ(®s->time) & (GRSPW_TIME_TCNT | GRSPW_TIME_CTRL); +} + +/* Set (not -1) and/or read RMAP options. */ +int grspw_rmap_ctrl(void *d, int *options, int *dstkey) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + if (dstkey) { + if (*dstkey != -1) + REG_WRITE(®s->destkey, *dstkey & GRSPW_DK_DESTKEY); + *dstkey = REG_READ(®s->destkey) & GRSPW_DK_DESTKEY; + } + if (options) { + if (*options != -1) { + if ((*options & RMAPOPTS_EN_RMAP) && !priv->hwsup.rmap) + return -1; + + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(®s->ctrl); + ctrl &= ~(GRSPW_CTRL_RE|GRSPW_CTRL_RD); + ctrl |= (*options & 0x3) << GRSPW_CTRL_RE_BIT; + REG_WRITE(®s->ctrl, ctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } + *options = (REG_READ(®s->ctrl) >> GRSPW_CTRL_RE_BIT) & 0x3; + } + + return 0; +} + +void grspw_rmap_support(void *d, char *rmap, char *rmap_crc) +{ + struct grspw_priv *priv = d; + + if (rmap) + *rmap = priv->hwsup.rmap; + if (rmap_crc) + *rmap_crc = priv->hwsup.rmap_crc; +} + +/* Select port, if + * -1=The current selected port is returned + * 0=Port 0 + * 1=Port 1 + * Others=Both Port0 and Port1 + */ +int grspw_port_ctrl(void *d, int *port) +{ + struct grspw_priv *priv = d; + struct grspw_regs *regs = priv->regs; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + if (port == NULL) + return -1; + + if ((*port == 1) || (*port == 0)) { + /* Select port user selected */ + if ((*port == 1) && (priv->hwsup.nports < 2)) + return -1; /* Changing to Port 1, but only one port available */ + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(®s->ctrl); + ctrl &= ~(GRSPW_CTRL_NP | GRSPW_CTRL_PS); + ctrl |= (*port & 1) << GRSPW_CTRL_PS_BIT; + REG_WRITE(®s->ctrl, ctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } else if (*port > 1) { + /* Select both ports */ + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + REG_WRITE(®s->ctrl, REG_READ(®s->ctrl) | GRSPW_CTRL_NP); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + } + + /* Get current settings */ + ctrl = REG_READ(®s->ctrl); + if (ctrl & GRSPW_CTRL_NP) { + /* Any port, selected by hardware */ + if (priv->hwsup.nports > 1) + *port = 3; + else + *port = 0; /* Port0 the only port available */ + } else { + *port = (ctrl & GRSPW_CTRL_PS) >> GRSPW_CTRL_PS_BIT; + } + + return 0; +} + +/* Returns Number ports available in hardware */ +int grspw_port_count(void *d) +{ + struct grspw_priv *priv = d; + + return priv->hwsup.nports; +} + +/* Current active port: 0 or 1 */ +int grspw_port_active(void *d) +{ + struct grspw_priv *priv = d; + unsigned int status; + + status = REG_READ(&priv->regs->status); + + return (status & GRSPW_STS_AP) >> GRSPW_STS_AP_BIT; +} + +void grspw_stats_read(void *d, struct grspw_core_stats *sts) +{ + struct grspw_priv *priv = d; + + if (sts == NULL) + return; + memcpy(sts, &priv->stats, sizeof(priv->stats)); +} + +void grspw_stats_clr(void *d) +{ + struct grspw_priv *priv = d; + + /* Clear most of the statistics */ + memset(&priv->stats, 0, sizeof(priv->stats)); +} + +/*** DMA Interface ***/ + +/* Initialize the RX and TX Descriptor Ring, empty of packets */ +STATIC void grspw_bdrings_init(struct grspw_dma_priv *dma) +{ + struct grspw_ring *r; + int i; + + /* Empty BD rings */ + dma->rx_ring_head = dma->rx_ring_base; + dma->rx_ring_tail = dma->rx_ring_base; + dma->tx_ring_head = dma->tx_ring_base; + dma->tx_ring_tail = dma->tx_ring_base; + + /* Init RX Descriptors */ + r = (struct grspw_ring *)dma->rx_ring_base; + for (i=0; i<GRSPW_RXBD_NR; i++) { + + /* Init Ring Entry */ + r[i].next = &r[i+1]; + r[i].bd.rx = &dma->rx_bds[i]; + r[i].pkt = NULL; + + /* Init HW Descriptor */ + BD_WRITE(&r[i].bd.rx->ctrl, 0); + BD_WRITE(&r[i].bd.rx->addr, 0); + } + r[GRSPW_RXBD_NR-1].next = &r[0]; + + /* Init TX Descriptors */ + r = (struct grspw_ring *)dma->tx_ring_base; + for (i=0; i<GRSPW_TXBD_NR; i++) { + + /* Init Ring Entry */ + r[i].next = &r[i+1]; + r[i].bd.tx = &dma->tx_bds[i]; + r[i].pkt = NULL; + + /* Init HW Descriptor */ + BD_WRITE(&r[i].bd.tx->ctrl, 0); + BD_WRITE(&r[i].bd.tx->haddr, 0); + BD_WRITE(&r[i].bd.tx->dlen, 0); + BD_WRITE(&r[i].bd.tx->daddr, 0); + } + r[GRSPW_TXBD_NR-1].next = &r[0]; +} + +/* Try to populate descriptor ring with as many as possible READY unused packet + * buffers. The packets assigned with to a descriptor are put in the end of + * the scheduled list. + * + * The number of Packets scheduled is returned. + * + * - READY List -> RX-SCHED List + * - Descriptors are initialized and enabled for reception + */ +STATIC int grspw_rx_schedule_ready(struct grspw_dma_priv *dma) +{ + int cnt; + unsigned int ctrl, dmactrl; + void *hwaddr; + struct grspw_rxring *curr_bd; + struct grspw_pkt *curr_pkt, *last_pkt; + struct grspw_list lst; + IRQFLAGS_TYPE irqflags; + + /* Is Ready Q empty? */ + if (grspw_list_is_empty(&dma->ready)) + return 0; + + cnt = 0; + lst.head = curr_pkt = dma->ready.head; + curr_bd = dma->rx_ring_head; + while (!curr_bd->pkt) { + + /* Assign Packet to descriptor */ + curr_bd->pkt = curr_pkt; + + /* Prepare descriptor address. */ + hwaddr = curr_pkt->data; + if (curr_pkt->flags & PKT_FLAG_TR_DATA) { + drvmgr_translate(dma->core->dev, CPUMEM_TO_DMA, + hwaddr, &hwaddr); + if (curr_pkt->data == hwaddr) /* translation needed? */ + curr_pkt->flags &= ~PKT_FLAG_TR_DATA; + } + BD_WRITE(&curr_bd->bd->addr, hwaddr); + + ctrl = GRSPW_RXBD_EN; + if (curr_bd->next == dma->rx_ring_base) { + /* Wrap around (only needed when smaller descriptor + * table) + */ + ctrl |= GRSPW_RXBD_WR; + } + + /* Is this Packet going to be an interrupt Packet? */ + if ((--dma->rx_irq_en_cnt_curr) <= 0) { + if (dma->cfg.rx_irq_en_cnt == 0) { + /* IRQ is disabled. A big number to avoid + * equal to zero too often + */ + dma->rx_irq_en_cnt_curr = 0x3fffffff; + } else { + dma->rx_irq_en_cnt_curr = dma->cfg.rx_irq_en_cnt; + ctrl |= GRSPW_RXBD_IE; + } + } + + if (curr_pkt->flags & RXPKT_FLAG_IE) + ctrl |= GRSPW_RXBD_IE; + + /* Enable descriptor */ + BD_WRITE(&curr_bd->bd->ctrl, ctrl); + + last_pkt = curr_pkt; + curr_bd = curr_bd->next; + cnt++; + + /* Get Next Packet from Ready Queue */ + if (curr_pkt == dma->ready.tail) { + /* Handled all in ready queue. */ + curr_pkt = NULL; + break; + } + curr_pkt = curr_pkt->next; + } + + /* Has Packets been scheduled? */ + if (cnt > 0) { + /* Prepare list for insertion/deleation */ + lst.tail = last_pkt; + + /* Remove scheduled packets from ready queue */ + grspw_list_remove_head_list(&dma->ready, &lst); + dma->ready_cnt -= cnt; + if (dma->stats.ready_cnt_min > dma->ready_cnt) + dma->stats.ready_cnt_min = dma->ready_cnt; + + /* Insert scheduled packets into scheduled queue */ + grspw_list_append_list(&dma->rx_sched, &lst); + dma->rx_sched_cnt += cnt; + if (dma->stats.rx_sched_cnt_max < dma->rx_sched_cnt) + dma->stats.rx_sched_cnt_max = dma->rx_sched_cnt; + + /* Update TX ring posistion */ + dma->rx_ring_head = curr_bd; + + /* Make hardware aware of the newly enabled descriptors + * We must protect from ISR which writes RI|TI + */ + SPIN_LOCK_IRQ(&dma->core->devlock, irqflags); + dmactrl = REG_READ(&dma->regs->ctrl); + dmactrl &= ~(GRSPW_DMACTRL_PS|GRSPW_DMACTRL_PR|GRSPW_DMA_STATUS_ERROR); + dmactrl |= GRSPW_DMACTRL_RE | GRSPW_DMACTRL_RD; + REG_WRITE(&dma->regs->ctrl, dmactrl); + SPIN_UNLOCK_IRQ(&dma->core->devlock, irqflags); + } + + return cnt; +} + +/* Scans the RX desciptor table for scheduled Packet that has been received, + * and moves these Packet from the head of the scheduled queue to the + * tail of the recv queue. + * + * Also, for all packets the status is updated. + * + * - SCHED List -> SENT List + * + * Return Value + * Number of packets moved + */ +STATIC int grspw_rx_process_scheduled(struct grspw_dma_priv *dma) +{ + struct grspw_rxring *curr; + struct grspw_pkt *last_pkt; + int recv_pkt_cnt = 0; + unsigned int ctrl; + struct grspw_list lst; + + curr = dma->rx_ring_tail; + + /* Step into RX ring to find if packets have been scheduled for + * reception. + */ + if (!curr->pkt) + return 0; /* No scheduled packets, thus no received, abort */ + + /* There has been Packets scheduled ==> scheduled Packets may have been + * received and needs to be collected into RECV List. + * + * A temporary list "lst" with all received packets is created. + */ + lst.head = curr->pkt; + + /* Loop until first enabled "unrecveived" SpW Packet is found. + * An unused descriptor is indicated by an unassigned pkt field. + */ + while (curr->pkt && !((ctrl=BD_READ(&curr->bd->ctrl)) & GRSPW_RXBD_EN)) { + /* Handle one received Packet */ + + /* Remember last handled Packet so that insertion/removal from + * Packet lists go fast. + */ + last_pkt = curr->pkt; + + /* Get Length of Packet in bytes, and reception options */ + last_pkt->dlen = (ctrl & GRSPW_RXBD_LEN) >> GRSPW_RXBD_LEN_BIT; + + /* Set flags to indicate error(s) and CRC information, + * and Mark Received. + */ + last_pkt->flags = (last_pkt->flags & ~RXPKT_FLAG_OUTPUT_MASK) | + ((ctrl >> 20) & RXPKT_FLAG_OUTPUT_MASK) | + RXPKT_FLAG_RX; + + /* Packet was Truncated? */ + if (ctrl & GRSPW_RXBD_TR) + dma->stats.rx_err_trunk++; + + /* Error End-Of-Packet? */ + if (ctrl & GRSPW_RXBD_EP) + dma->stats.rx_err_endpkt++; + curr->pkt = NULL; /* Mark descriptor unused */ + + /* Increment */ + curr = curr->next; + recv_pkt_cnt++; + } + + /* 1. Remove all handled packets from scheduled queue + * 2. Put all handled packets into recv queue + */ + if (recv_pkt_cnt > 0) { + + /* Update Stats, Number of Received Packets */ + dma->stats.rx_pkts += recv_pkt_cnt; + + /* Save RX ring posistion */ + dma->rx_ring_tail = curr; + + /* Prepare list for insertion/deleation */ + lst.tail = last_pkt; + + /* Remove received Packets from RX-SCHED queue */ + grspw_list_remove_head_list(&dma->rx_sched, &lst); + dma->rx_sched_cnt -= recv_pkt_cnt; + if (dma->stats.rx_sched_cnt_min > dma->rx_sched_cnt) + dma->stats.rx_sched_cnt_min = dma->rx_sched_cnt; + + /* Insert received Packets into RECV queue */ + grspw_list_append_list(&dma->recv, &lst); + dma->recv_cnt += recv_pkt_cnt; + if (dma->stats.recv_cnt_max < dma->recv_cnt) + dma->stats.recv_cnt_max = dma->recv_cnt; + } + + return recv_pkt_cnt; +} + +/* Try to populate descriptor ring with as many SEND packets as possible. The + * packets assigned with to a descriptor are put in the end of + * the scheduled list. + * + * The number of Packets scheduled is returned. + * + * - SEND List -> TX-SCHED List + * - Descriptors are initialized and enabled for transmission + */ +STATIC int grspw_tx_schedule_send(struct grspw_dma_priv *dma) +{ + int cnt; + unsigned int ctrl, dmactrl; + void *hwaddr; + struct grspw_txring *curr_bd; + struct grspw_pkt *curr_pkt, *last_pkt; + struct grspw_list lst; + IRQFLAGS_TYPE irqflags; + + /* Is Ready Q empty? */ + if (grspw_list_is_empty(&dma->send)) + return 0; + + cnt = 0; + lst.head = curr_pkt = dma->send.head; + curr_bd = dma->tx_ring_head; + while (!curr_bd->pkt) { + + /* Assign Packet to descriptor */ + curr_bd->pkt = curr_pkt; + + /* Set up header transmission */ + if (curr_pkt->hdr && curr_pkt->hlen) { + hwaddr = curr_pkt->hdr; + if (curr_pkt->flags & PKT_FLAG_TR_HDR) { + drvmgr_translate(dma->core->dev, CPUMEM_TO_DMA, + hwaddr, &hwaddr); + /* translation needed? */ + if (curr_pkt->hdr == hwaddr) + curr_pkt->flags &= ~PKT_FLAG_TR_HDR; + } + BD_WRITE(&curr_bd->bd->haddr, hwaddr); + ctrl = GRSPW_TXBD_EN | curr_pkt->hlen; + } else { + ctrl = GRSPW_TXBD_EN; + } + /* Enable IRQ generation and CRC options as specified + * by user. + */ + ctrl |= (curr_pkt->flags & TXPKT_FLAG_INPUT_MASK) << 8; + + if (curr_bd->next == dma->tx_ring_base) { + /* Wrap around (only needed when smaller descriptor table) */ + ctrl |= GRSPW_TXBD_WR; + } + + /* Is this Packet going to be an interrupt Packet? */ + if ((--dma->tx_irq_en_cnt_curr) <= 0) { + if (dma->cfg.tx_irq_en_cnt == 0) { + /* IRQ is disabled. + * A big number to avoid equal to zero too often + */ + dma->tx_irq_en_cnt_curr = 0x3fffffff; + } else { + dma->tx_irq_en_cnt_curr = dma->cfg.tx_irq_en_cnt; + ctrl |= GRSPW_TXBD_IE; + } + } + + /* Prepare descriptor address. Parts of CTRL is written to + * DLEN for debug-only (CTRL is cleared by HW). + */ + if (curr_pkt->data && curr_pkt->dlen) { + hwaddr = curr_pkt->data; + if (curr_pkt->flags & PKT_FLAG_TR_DATA) { + drvmgr_translate(dma->core->dev, CPUMEM_TO_DMA, + hwaddr, &hwaddr); + /* translation needed? */ + if (curr_pkt->data == hwaddr) + curr_pkt->flags &= ~PKT_FLAG_TR_DATA; + } + BD_WRITE(&curr_bd->bd->daddr, hwaddr); + BD_WRITE(&curr_bd->bd->dlen, curr_pkt->dlen | + ((ctrl & 0x3f000) << 12)); + } else { + BD_WRITE(&curr_bd->bd->daddr, 0); + BD_WRITE(&curr_bd->bd->dlen, ((ctrl & 0x3f000) << 12)); + } + + /* Enable descriptor */ + BD_WRITE(&curr_bd->bd->ctrl, ctrl); + + last_pkt = curr_pkt; + curr_bd = curr_bd->next; + cnt++; + + /* Get Next Packet from Ready Queue */ + if (curr_pkt == dma->send.tail) { + /* Handled all in ready queue. */ + curr_pkt = NULL; + break; + } + curr_pkt = curr_pkt->next; + } + + /* Have Packets been scheduled? */ + if (cnt > 0) { + /* Prepare list for insertion/deleation */ + lst.tail = last_pkt; + + /* Remove scheduled packets from ready queue */ + grspw_list_remove_head_list(&dma->send, &lst); + dma->send_cnt -= cnt; + if (dma->stats.send_cnt_min > dma->send_cnt) + dma->stats.send_cnt_min = dma->send_cnt; + + /* Insert scheduled packets into scheduled queue */ + grspw_list_append_list(&dma->tx_sched, &lst); + dma->tx_sched_cnt += cnt; + if (dma->stats.tx_sched_cnt_max < dma->tx_sched_cnt) + dma->stats.tx_sched_cnt_max = dma->tx_sched_cnt; + + /* Update TX ring posistion */ + dma->tx_ring_head = curr_bd; + + /* Make hardware aware of the newly enabled descriptors */ + SPIN_LOCK_IRQ(&dma->core->devlock, irqflags); + dmactrl = REG_READ(&dma->regs->ctrl); + dmactrl &= ~(GRSPW_DMACTRL_PS|GRSPW_DMACTRL_PR|GRSPW_DMA_STATUS_ERROR); + dmactrl |= GRSPW_DMACTRL_TE; + REG_WRITE(&dma->regs->ctrl, dmactrl); + SPIN_UNLOCK_IRQ(&dma->core->devlock, irqflags); + } + return cnt; +} + +/* Scans the TX desciptor table for transmitted packets, and moves these + * packets from the head of the scheduled queue to the tail of the sent queue. + * + * Also, for all packets the status is updated. + * + * - SCHED List -> SENT List + * + * Return Value + * Number of packet moved + */ +STATIC int grspw_tx_process_scheduled(struct grspw_dma_priv *dma) +{ + struct grspw_txring *curr; + struct grspw_pkt *last_pkt; + int sent_pkt_cnt = 0; + unsigned int ctrl; + struct grspw_list lst; + + curr = dma->tx_ring_tail; + + /* Step into TX ring to find if packets have been scheduled for + * transmission. + */ + if (!curr->pkt) + return 0; /* No scheduled packets, thus no sent, abort */ + + /* There has been Packets scheduled ==> scheduled Packets may have been + * transmitted and needs to be collected into SENT List. + * + * A temporary list "lst" with all sent packets is created. + */ + lst.head = curr->pkt; + + /* Loop until first enabled "un-transmitted" SpW Packet is found. + * An unused descriptor is indicated by an unassigned pkt field. + */ + while (curr->pkt && !((ctrl=BD_READ(&curr->bd->ctrl)) & GRSPW_TXBD_EN)) { + /* Handle one sent Packet */ + + /* Remember last handled Packet so that insertion/removal from + * packet lists go fast. + */ + last_pkt = curr->pkt; + + /* Set flags to indicate error(s) and Mark Sent. + */ + last_pkt->flags = (last_pkt->flags & ~TXPKT_FLAG_OUTPUT_MASK) | + (ctrl & TXPKT_FLAG_LINKERR) | + TXPKT_FLAG_TX; + + /* Sent packet experienced link error? */ + if (ctrl & GRSPW_TXBD_LE) + dma->stats.tx_err_link++; + + curr->pkt = NULL; /* Mark descriptor unused */ + + /* Increment */ + curr = curr->next; + sent_pkt_cnt++; + } + + /* 1. Remove all handled packets from TX-SCHED queue + * 2. Put all handled packets into SENT queue + */ + if (sent_pkt_cnt > 0) { + /* Update Stats, Number of Transmitted Packets */ + dma->stats.tx_pkts += sent_pkt_cnt; + + /* Save TX ring posistion */ + dma->tx_ring_tail = curr; + + /* Prepare list for insertion/deleation */ + lst.tail = last_pkt; + + /* Remove sent packets from TX-SCHED queue */ + grspw_list_remove_head_list(&dma->tx_sched, &lst); + dma->tx_sched_cnt -= sent_pkt_cnt; + if (dma->stats.tx_sched_cnt_min > dma->tx_sched_cnt) + dma->stats.tx_sched_cnt_min = dma->tx_sched_cnt; + + /* Insert received packets into SENT queue */ + grspw_list_append_list(&dma->sent, &lst); + dma->sent_cnt += sent_pkt_cnt; + if (dma->stats.sent_cnt_max < dma->sent_cnt) + dma->stats.sent_cnt_max = dma->sent_cnt; + } + + return sent_pkt_cnt; +} + +void *grspw_dma_open(void *d, int chan_no) +{ + struct grspw_priv *priv = d; + struct grspw_dma_priv *dma; + int size; + + if ((chan_no < 0) && (priv->hwsup.ndma_chans <= chan_no)) + return NULL; + + dma = &priv->dma[chan_no]; + + /* Take GRSPW lock */ + if (rtems_semaphore_obtain(grspw_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return NULL; + + if (dma->open) { + dma = NULL; + goto out; + } + + dma->started = 0; + + /* Set Default Configuration: + * + * - MAX RX Packet Length = + * - Disable IRQ generation + * - + */ + dma->cfg.rxmaxlen = DEFAULT_RXMAX; + dma->cfg.rx_irq_en_cnt = 0; + dma->cfg.tx_irq_en_cnt = 0; + dma->cfg.flags = DMAFLAG_NO_SPILL; + + /* DMA Channel Semaphore created with count = 1 */ + if (rtems_semaphore_create( + rtems_build_name('S', 'D', '0' + priv->index, '0' + chan_no), 1, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ + RTEMS_NO_PRIORITY_CEILING, 0, &dma->sem_dma) != RTEMS_SUCCESSFUL) { + dma = NULL; + goto out; + } + + /* Allocate memory for the two descriptor rings */ + size = sizeof(struct grspw_ring) * (GRSPW_RXBD_NR + GRSPW_TXBD_NR); + dma->rx_ring_base = (struct grspw_rxring *)malloc(size); + dma->tx_ring_base = (struct grspw_txring *)&dma->rx_ring_base[GRSPW_RXBD_NR]; + if (dma->rx_ring_base == NULL) { + dma = NULL; + goto out; + } + + /* Create DMA RX and TX Channel sempahore with count = 0 */ + if (rtems_semaphore_create( + rtems_build_name('S', 'R', '0' + priv->index, '0' + chan_no), 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ + RTEMS_NO_PRIORITY_CEILING, 0, &dma->rx_wait.sem_wait) != RTEMS_SUCCESSFUL) { + dma = NULL; + goto out; + } + if (rtems_semaphore_create( + rtems_build_name('S', 'T', '0' + priv->index, '0' + chan_no), 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ + RTEMS_NO_PRIORITY_CEILING, 0, &dma->tx_wait.sem_wait) != RTEMS_SUCCESSFUL) { + dma = NULL; + goto out; + } + + /* Reset software structures */ + grspw_dma_reset(dma); + + /* Take the device */ + dma->open = 1; +out: + /* Return GRSPW Lock */ + rtems_semaphore_release(grspw_sem); + + return dma; +} + +/* Initialize Software Structures: + * - Clear all Queues + * - init BD ring + * - init IRQ counter + * - clear statistics counters + * - init wait structures and semaphores + */ +STATIC void grspw_dma_reset(struct grspw_dma_priv *dma) +{ + /* Empty RX and TX queues */ + grspw_list_clr(&dma->ready); + grspw_list_clr(&dma->rx_sched); + grspw_list_clr(&dma->recv); + grspw_list_clr(&dma->send); + grspw_list_clr(&dma->tx_sched); + grspw_list_clr(&dma->sent); + dma->ready_cnt = 0; + dma->rx_sched_cnt = 0; + dma->recv_cnt = 0; + dma->send_cnt = 0; + dma->tx_sched_cnt = 0; + dma->sent_cnt = 0; + + dma->rx_irq_en_cnt_curr = 0; + dma->tx_irq_en_cnt_curr = 0; + + grspw_bdrings_init(dma); + + dma->rx_wait.waiting = 0; + dma->tx_wait.waiting = 0; + + grspw_dma_stats_clr(dma); +} + +void grspw_dma_close(void *c) +{ + struct grspw_dma_priv *dma = c; + + if (!dma->open) + return; + + /* Take device lock - Wait until we get semaphore */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return; + + grspw_dma_stop_locked(dma); + + /* Free resources */ + rtems_semaphore_delete(dma->rx_wait.sem_wait); + rtems_semaphore_delete(dma->tx_wait.sem_wait); + rtems_semaphore_delete(dma->sem_dma); /* Release and delete lock */ + + /* Free memory */ + if (dma->rx_ring_base) + free(dma->rx_ring_base); + dma->rx_ring_base = NULL; + dma->tx_ring_base = NULL; + + dma->open = 0; +} + +/* Schedule List of packets for transmission at some point in + * future. + * + * 1. Move transmitted packets to SENT List (SCHED->SENT) + * 2. Add the requested packets to the SEND List (USER->SEND) + * 3. Schedule as many packets as possible (SEND->SCHED) + */ +int grspw_dma_tx_send(void *c, int opts, struct grspw_list *pkts, int count) +{ + struct grspw_dma_priv *dma = c; + int ret; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + if (dma->started == 0) { + ret = 1; /* signal DMA has been stopped */ + goto out; + } + ret = 0; + + /* 1. Move transmitted packets to SENT List (SCHED->SENT) */ + if ((opts & 1) == 0) + grspw_tx_process_scheduled(dma); + + /* 2. Add the requested packets to the SEND List (USER->SEND) */ + if (pkts) { + grspw_list_append_list(&dma->send, pkts); + dma->send_cnt += count; + if (dma->stats.send_cnt_max < dma->send_cnt) + dma->stats.send_cnt_max = dma->send_cnt; + } + + /* 3. Schedule as many packets as possible (SEND->SCHED) */ + if ((opts & 2) == 0) + grspw_tx_schedule_send(dma); + +out: + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return ret; +} + +int grspw_dma_tx_reclaim(void *c, int opts, struct grspw_list *pkts, int *count) +{ + struct grspw_dma_priv *dma = c; + struct grspw_pkt *pkt, *lastpkt; + int cnt, started; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + /* 1. Move transmitted packets to SENT List (SCHED->SENT) */ + started = dma->started; + if ((started > 0) && ((opts & 1) == 0)) + grspw_tx_process_scheduled(dma); + + /* Move all/count SENT packet to the callers list (SENT->USER) */ + if (pkts) { + if ((count == NULL) || (*count == -1) || + (*count >= dma->sent_cnt)) { + /* Move all SENT Packets */ + *pkts = dma->sent; + grspw_list_clr(&dma->sent); + if (count) + *count = dma->sent_cnt; + dma->sent_cnt = 0; + } else { + /* Move a number of SENT Packets */ + pkts->head = pkt = lastpkt = dma->sent.head; + cnt = 0; + while (cnt < *count) { + lastpkt = pkt; + pkt = pkt->next; + cnt++; + } + if (cnt > 0) { + pkts->tail = lastpkt; + grspw_list_remove_head_list(&dma->sent, pkts); + dma->sent_cnt -= cnt; + } else { + grspw_list_clr(pkts); + } + } + } else if (count) { + *count = 0; + } + + /* 3. Schedule as many packets as possible (SEND->SCHED) */ + if ((started > 0 ) && ((opts & 2) == 0)) + grspw_tx_schedule_send(dma); + + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return (~started) & 1; /* signal DMA has been stopped */ +} + +void grspw_dma_tx_count(void *c, int *send, int *sched, int *sent) +{ + struct grspw_dma_priv *dma = c; + + if (send) + *send = dma->send_cnt; + if (sched) + *sched = dma->tx_sched_cnt; + if (sent) + *sent = dma->sent_cnt; +} + +static inline int grspw_tx_wait_eval(struct grspw_dma_priv *dma) +{ + int send_val, sent_val; + + if (dma->tx_wait.send_cnt >= (dma->send_cnt + dma->tx_sched_cnt)) + send_val = 1; + else + send_val = 0; + + if (dma->tx_wait.sent_cnt <= dma->sent_cnt) + sent_val = 1; + else + sent_val = 0; + + /* AND or OR ? */ + if (dma->tx_wait.op == 0) + return send_val & sent_val; /* AND */ + else + return send_val | sent_val; /* OR */ +} + +/* Block until send_cnt or fewer packets are Queued in "Send and Scheduled" Q, + * op (AND or OR), sent_cnt or more packet "have been sent" (Sent Q) condition + * is met. + * If a link error occurs and the Stop on Link error is defined, this function + * will also return to caller. + */ +int grspw_dma_tx_wait(void *c, int send_cnt, int op, int sent_cnt, int timeout) +{ + struct grspw_dma_priv *dma = c; + int ret, rc; + + if (timeout == 0) + timeout = RTEMS_NO_TIMEOUT; + +check_condition: + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + /* Check so that no other thread is waiting, this driver only supports + * one waiter at a time. + */ + if (dma->tx_wait.waiting) { + ret = -1; + goto out; + } + + /* Stop if link error or similar, abort */ + if (dma->started == 0) { + ret = 1; + goto out; + } + + /* Set up Condition */ + dma->tx_wait.send_cnt = send_cnt; + dma->tx_wait.op = op; + dma->tx_wait.sent_cnt = sent_cnt; + + if (grspw_tx_wait_eval(dma) == 0) { + /* Prepare Wait */ + dma->tx_wait.waiting = 1; + + /* Release DMA channel lock */ + rtems_semaphore_release(dma->sem_dma); + + /* Try to take Wait lock, if this fail link may have gone down + * or user stopped this DMA channel + */ + rc = rtems_semaphore_obtain(dma->tx_wait.sem_wait, RTEMS_WAIT, + timeout); + if (rc == RTEMS_TIMEOUT) { + dma->tx_wait.waiting = 0; + return 2; + } else if (rc == RTEMS_UNSATISFIED || + rc == RTEMS_OBJECT_WAS_DELETED) { + dma->tx_wait.waiting = 0; + return 1; /* sem was flushed/deleted, means DMA stop */ + } else if (rc != RTEMS_SUCCESSFUL) + return -1; + + /* Check condition once more */ + goto check_condition; + } else { + /* No Wait needed */ + dma->tx_wait.waiting = 0; + } + + ret = 0; +out: + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return ret; +} + +int grspw_dma_rx_recv(void *c, int opts, struct grspw_list *pkts, int *count) +{ + struct grspw_dma_priv *dma = c; + struct grspw_pkt *pkt, *lastpkt; + int cnt, started; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + /* 1. Move Scheduled packets to RECV List (SCHED->RECV) */ + started = dma->started; + if (((opts & 1) == 0) && (started > 0)) + grspw_rx_process_scheduled(dma); + + /* Move all RECV packet to the callers list */ + if (pkts) { + if ((count == NULL) || (*count == -1) || + (*count >= dma->recv_cnt)) { + /* Move all Received packets */ + *pkts = dma->recv; + grspw_list_clr(&dma->recv); + if ( count ) + *count = dma->recv_cnt; + dma->recv_cnt = 0; + } else { + /* Move a number of RECV Packets */ + pkts->head = pkt = lastpkt = dma->recv.head; + cnt = 0; + while (cnt < *count) { + lastpkt = pkt; + pkt = pkt->next; + cnt++; + } + if (cnt > 0) { + pkts->tail = lastpkt; + grspw_list_remove_head_list(&dma->recv, pkts); + dma->recv_cnt -= cnt; + } else { + grspw_list_clr(pkts); + } + } + } else if (count) { + *count = 0; + } + + /* 3. Schedule as many free packet buffers as possible (READY->SCHED) */ + if (((opts & 2) == 0) && (started > 0)) + grspw_rx_schedule_ready(dma); + + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return (~started) & 1; +} + +int grspw_dma_rx_prepare(void *c, int opts, struct grspw_list *pkts, int count) +{ + struct grspw_dma_priv *dma = c; + int ret; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + if (dma->started == 0) { + ret = 1; + goto out; + } + + /* 1. Move Received packets to RECV List (SCHED->RECV) */ + if ((opts & 1) == 0) + grspw_rx_process_scheduled(dma); + + /* 2. Add the "free/ready" packet buffers to the READY List (USER->READY) */ + if (pkts && (count > 0)) { + grspw_list_append_list(&dma->ready, pkts); + dma->ready_cnt += count; + if (dma->stats.ready_cnt_max < dma->ready_cnt) + dma->stats.ready_cnt_max = dma->ready_cnt; + } + + /* 3. Schedule as many packets as possible (READY->SCHED) */ + if ((opts & 2) == 0) + grspw_rx_schedule_ready(dma); + + ret = 0; +out: + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return ret; +} + +void grspw_dma_rx_count(void *c, int *ready, int *sched, int *recv) +{ + struct grspw_dma_priv *dma = c; + + if (ready) + *ready = dma->ready_cnt; + if (sched) + *sched = dma->rx_sched_cnt; + if (recv) + *recv = dma->recv_cnt; +} + +static inline int grspw_rx_wait_eval(struct grspw_dma_priv *dma) +{ + int ready_val, recv_val; + + if (dma->rx_wait.ready_cnt >= (dma->ready_cnt + dma->rx_sched_cnt)) + ready_val = 1; + else + ready_val = 0; + + if (dma->rx_wait.recv_cnt <= dma->recv_cnt) + recv_val = 1; + else + recv_val = 0; + + /* AND or OR ? */ + if (dma->rx_wait.op == 0) + return ready_val & recv_val; /* AND */ + else + return ready_val | recv_val; /* OR */ +} + +/* Block until recv_cnt or more packets are Queued in RECV Q, op (AND or OR), + * ready_cnt or fewer packet buffers are available in the "READY and Scheduled" Q, + * condition is met. + * If a link error occurs and the Stop on Link error is defined, this function + * will also return to caller, however with an error. + */ +int grspw_dma_rx_wait(void *c, int recv_cnt, int op, int ready_cnt, int timeout) +{ + struct grspw_dma_priv *dma = c; + int ret, rc; + + if (timeout == 0) + timeout = RTEMS_NO_TIMEOUT; + +check_condition: + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return -1; + + /* Check so that no other thread is waiting, this driver only supports + * one waiter at a time. + */ + if (dma->rx_wait.waiting) { + ret = -1; + goto out; + } + + /* Stop if link error or similar (MDA stopped) */ + if (dma->started == 0) { + ret = 1; + goto out; + } + + /* Set up Condition */ + dma->rx_wait.recv_cnt = recv_cnt; + dma->rx_wait.op = op; + dma->rx_wait.ready_cnt = ready_cnt; + + if (grspw_rx_wait_eval(dma) == 0) { + /* Prepare Wait */ + dma->rx_wait.waiting = 1; + + /* Release channel lock */ + rtems_semaphore_release(dma->sem_dma); + + /* Try to take Wait lock, if this fail link may have gone down + * or user stopped this DMA channel + */ + rc = rtems_semaphore_obtain(dma->rx_wait.sem_wait, RTEMS_WAIT, + timeout); + if (rc == RTEMS_TIMEOUT) { + dma->rx_wait.waiting = 0; + return 2; + } else if (rc == RTEMS_UNSATISFIED || + rc == RTEMS_OBJECT_WAS_DELETED) { + dma->rx_wait.waiting = 0; + return 1; /* sem was flushed/deleted, means DMA stop */ + } else if (rc != RTEMS_SUCCESSFUL) + return -1; + + /* Check condition once more */ + goto check_condition; + } else { + /* No Wait needed */ + dma->rx_wait.waiting = 0; + } + ret = 0; + +out: + /* Unlock DMA channel */ + rtems_semaphore_release(dma->sem_dma); + + return ret; +} + +int grspw_dma_config(void *c, struct grspw_dma_config *cfg) +{ + struct grspw_dma_priv *dma = c; + + if (dma->started || !cfg) + return -1; + + if (cfg->flags & ~DMAFLAG_MASK) + return -1; + + /* Update Configuration */ + memcpy(&dma->cfg, cfg, sizeof(*cfg)); + + return 0; +} + +void grspw_dma_config_read(void *c, struct grspw_dma_config *cfg) +{ + struct grspw_dma_priv *dma = c; + + /* Copy Current Configuration */ + memcpy(cfg, &dma->cfg, sizeof(*cfg)); +} + +void grspw_dma_stats_read(void *c, struct grspw_dma_stats *sts) +{ + struct grspw_dma_priv *dma = c; + + memcpy(sts, &dma->stats, sizeof(dma->stats)); +} + +void grspw_dma_stats_clr(void *c) +{ + struct grspw_dma_priv *dma = c; + + /* Clear most of the statistics */ + memset(&dma->stats, 0, sizeof(dma->stats)); + + /* Init proper default values so that comparisons will work the + * first time. + */ + dma->stats.send_cnt_min = 0x3fffffff; + dma->stats.tx_sched_cnt_min = 0x3fffffff; + dma->stats.ready_cnt_min = 0x3fffffff; + dma->stats.rx_sched_cnt_min = 0x3fffffff; +} + +int grspw_dma_start(void *c) +{ + struct grspw_dma_priv *dma = c; + struct grspw_dma_regs *dregs = dma->regs; + unsigned int ctrl; + + if (dma->started) + return 0; + + /* Initialize Software Structures: + * - Clear all Queues + * - init BD ring + * - init IRQ counter + * - clear statistics counters + * - init wait structures and semaphores + */ + grspw_dma_reset(dma); + + /* RX&RD and TX is not enabled until user fills SEND and READY Queue + * with SpaceWire Packet buffers. So we do not have to worry about + * IRQs for this channel just yet. However other DMA channels + * may be active. + * + * Some functionality that is not changed during started mode is set up + * once and for all here: + * + * - RX MAX Packet length + * - TX Descriptor base address to first BD in TX ring (not enabled) + * - RX Descriptor base address to first BD in RX ring (not enabled) + * - IRQs (TX DMA, RX DMA, DMA ERROR) + * - Strip PID + * - Strip Address + * - No Spill + * - Receiver Enable + * - disable on link error (LE) + * + * Note that the address register and the address enable bit in DMACTRL + * register must be left untouched, they are configured on a GRSPW + * core level. + * + * Note that the receiver is enabled here, but since descriptors are + * not enabled the GRSPW core may stop/pause RX (if NS bit set) until + * descriptors are enabled or it may ignore RX packets (NS=0) until + * descriptors are enabled (writing RD bit). + */ + REG_WRITE(&dregs->txdesc, dma->tx_bds_hwa); + REG_WRITE(&dregs->rxdesc, dma->rx_bds_hwa); + + /* MAX Packet length */ + REG_WRITE(&dma->regs->rxmax, dma->cfg.rxmaxlen); + + ctrl = GRSPW_DMACTRL_AI | GRSPW_DMACTRL_PS | GRSPW_DMACTRL_PR | + GRSPW_DMACTRL_TA | GRSPW_DMACTRL_RA | GRSPW_DMACTRL_RE | + (dma->cfg.flags & DMAFLAG_MASK) << GRSPW_DMACTRL_NS_BIT; + if (dma->core->dis_link_on_err) + ctrl |= GRSPW_DMACTRL_LE; + if (dma->cfg.rx_irq_en_cnt != 0) + ctrl |= GRSPW_DMACTRL_RI; + if (dma->cfg.tx_irq_en_cnt != 0) + ctrl |= GRSPW_DMACTRL_TI; + REG_WRITE(&dregs->ctrl, ctrl); + + dma->started = 1; /* open up other DMA interfaces */ + + return 0; +} + +STATIC void grspw_dma_stop_locked(struct grspw_dma_priv *dma) +{ + IRQFLAGS_TYPE irqflags; + + if (dma->started == 0) + return; + dma->started = 0; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + grspw_hw_dma_stop(dma); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + + /* From here no more packets will be sent, however + * there may still exist scheduled packets that has been + * sent, and packets in the SEND Queue waiting for free + * descriptors. All packets are moved to the SENT Queue + * so that the user may get its buffers back, the user + * must look at the TXPKT_FLAG_TX in order to determine + * if the packet was sent or not. + */ + + /* Retreive scheduled all sent packets */ + grspw_tx_process_scheduled(dma); + + /* Move un-sent packets in SEND and SCHED queue to the + * SENT Queue. (never marked sent) + */ + if (!grspw_list_is_empty(&dma->tx_sched)) { + grspw_list_append_list(&dma->sent, &dma->tx_sched); + grspw_list_clr(&dma->tx_sched); + dma->sent_cnt += dma->tx_sched_cnt; + dma->tx_sched_cnt = 0; + } + if (!grspw_list_is_empty(&dma->send)) { + grspw_list_append_list(&dma->sent, &dma->send); + grspw_list_clr(&dma->send); + dma->sent_cnt += dma->send_cnt; + dma->send_cnt = 0; + } + + /* Similar for RX */ + grspw_rx_process_scheduled(dma); + if (!grspw_list_is_empty(&dma->rx_sched)) { + grspw_list_append_list(&dma->recv, &dma->rx_sched); + grspw_list_clr(&dma->rx_sched); + dma->recv_cnt += dma->rx_sched_cnt; + dma->rx_sched_cnt = 0; + } + if (!grspw_list_is_empty(&dma->ready)) { + grspw_list_append_list(&dma->recv, &dma->ready); + grspw_list_clr(&dma->ready); + dma->recv_cnt += dma->ready_cnt; + dma->ready_cnt = 0; + } + + /* Throw out blocked threads */ + rtems_semaphore_flush(dma->rx_wait.sem_wait); + rtems_semaphore_flush(dma->tx_wait.sem_wait); +} + +void grspw_dma_stop(void *c) +{ + struct grspw_dma_priv *dma = c; + + /* Take DMA Channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return; + + grspw_dma_stop_locked(dma); + + rtems_semaphore_release(dma->sem_dma); +} + +/* Do general work, invoked indirectly from ISR */ +static void grspw_work_shutdown_func(struct grspw_priv *priv) +{ + int i; + + /* Link is down for some reason, and the user has configured + * that we stop all DMA channels and throw out all blocked + * threads. + */ + for (i=0; i<priv->hwsup.ndma_chans; i++) + grspw_dma_stop(&priv->dma[i]); + grspw_hw_stop(priv); +} + +/* Do DMA work on one channel, invoked indirectly from ISR */ +static void grspw_work_dma_func(struct grspw_dma_priv *dma) +{ + int tx_cond_true, rx_cond_true; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + rx_cond_true = 0; + tx_cond_true = 0; + dma->stats.irq_cnt++; + + /* Take DMA channel lock */ + if (rtems_semaphore_obtain(dma->sem_dma, RTEMS_WAIT, RTEMS_NO_TIMEOUT) + != RTEMS_SUCCESSFUL) + return; + + /* Look at cause we were woken up and clear source */ + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + ctrl = REG_READ(&dma->regs->ctrl); + + /* Read/Write DMA error ? */ + if (ctrl & GRSPW_DMA_STATUS_ERROR) { + /* DMA error -> Stop DMA channel (both RX and TX) */ + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + grspw_dma_stop_locked(dma); + } else if (ctrl & (GRSPW_DMACTRL_PR | GRSPW_DMACTRL_PS)) { + /* DMA has finished a TX/RX packet */ + ctrl &= ~GRSPW_DMACTRL_AT; + if (dma->cfg.rx_irq_en_cnt != 0) + ctrl |= GRSPW_DMACTRL_RI; + if (dma->cfg.tx_irq_en_cnt != 0) + ctrl |= GRSPW_DMACTRL_TI; + REG_WRITE(&dma->regs->ctrl, ctrl); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + if (ctrl & GRSPW_DMACTRL_PR) { + /* Do RX Work */ + dma->stats.rx_work_cnt++; + grspw_rx_process_scheduled(dma); + dma->stats.rx_work_enabled += grspw_rx_schedule_ready(dma); + /* Check to see if condition for waking blocked USER + * task is fullfilled. + */ + if (dma->rx_wait.waiting) { + rx_cond_true = grspw_rx_wait_eval(dma); + if (rx_cond_true) + dma->rx_wait.waiting = 0; + } + } + if (ctrl & GRSPW_DMACTRL_PS) { + /* Do TX Work */ + dma->stats.tx_work_cnt++; + grspw_tx_process_scheduled(dma); + dma->stats.tx_work_enabled += grspw_tx_schedule_send(dma); + if (dma->tx_wait.waiting) { + tx_cond_true = grspw_tx_wait_eval(dma); + if (tx_cond_true) + dma->tx_wait.waiting = 0; + } + } + } else + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + + /* Release lock */ + rtems_semaphore_release(dma->sem_dma); + + if (rx_cond_true) + rtems_semaphore_release(dma->rx_wait.sem_wait); + + if (tx_cond_true) + rtems_semaphore_release(dma->tx_wait.sem_wait); +} + +/* Work task is receiving work for the work message queue posted from + * the ISR. + */ +void grspw_work_func(rtems_task_argument unused) +{ + rtems_status_code status; + unsigned int message; + size_t size; + struct grspw_priv *priv; + int i; + + while (grspw_task_stop == 0) { + /* Wait for ISR to schedule work */ + status = rtems_message_queue_receive( + grspw_work_queue, &message, + &size, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (status != RTEMS_SUCCESSFUL) + break; + + /* Handle work */ + priv = priv_tab[message >> WORK_CORE_BIT]; + if (message & WORK_SHUTDOWN) + grspw_work_shutdown_func(priv); + else if (message & WORK_DMA_MASK) { + for (i = 0; i < 4; i++) { + if (message & WORK_DMA(i)) + grspw_work_dma_func(&priv->dma[i]); + } + } + } + rtems_task_delete(RTEMS_SELF); +} + +STATIC void grspw_isr(void *data) +{ + struct grspw_priv *priv = data; + unsigned int dma_stat, stat, stat_clrmsk, ctrl, timecode; + int i, handled = 0, message = WORK_NONE; +#ifdef RTEMS_HAS_SMP + IRQFLAGS_TYPE irqflags; +#endif + + /* Get Status from Hardware */ + stat = REG_READ(&priv->regs->status); + stat_clrmsk = stat & (GRSPW_STS_TO | GRSPW_STAT_ERROR); + + /* Make sure to put the timecode handling first in order to get the + * smallest possible interrupt latency + */ + if ((stat & GRSPW_STS_TO) && (priv->tcisr != NULL)) { + /* Timecode received. Let custom function handle this */ + timecode = priv->regs->time; + (priv->tcisr)(priv->tcisr_arg, timecode); + } + + /* An Error occured? */ + if (stat & GRSPW_STAT_ERROR) { + /* Wake Global WorkQ */ + handled = 1; + + if (stat & GRSPW_STS_EE) + priv->stats.err_eeop++; + + if (stat & GRSPW_STS_IA) + priv->stats.err_addr++; + + if (stat & GRSPW_STS_PE) + priv->stats.err_parity++; + + if (stat & GRSPW_STS_ER) + priv->stats.err_escape++; + + if (stat & GRSPW_STS_CE) + priv->stats.err_credit++; + + if (stat & GRSPW_STS_WE) + priv->stats.err_wsync++; + + if (priv->dis_link_on_err) { + /* Disable the link, no more transfers are expected + * on any DMA channel. + */ + SPIN_LOCK(&priv->devlock, irqflags); + ctrl = REG_READ(&priv->regs->ctrl); + REG_WRITE(&priv->regs->ctrl, GRSPW_CTRL_LD | + (ctrl & ~(GRSPW_CTRL_IE|GRSPW_CTRL_LS))); + SPIN_UNLOCK(&priv->devlock, irqflags); + /* Signal to work-thread to stop DMA and clean up */ + message = WORK_SHUTDOWN; + } + } + + /* Clear Status Flags */ + if (stat_clrmsk) { + handled = 1; + REG_WRITE(&priv->regs->status, stat_clrmsk); + } + + /* A DMA transfer or Error occured? In that case disable more IRQs + * from the DMA channel, then invoke the workQ. + * + * Also the GI interrupt flag may not be available for older + * designs where (was added together with mutiple DMA channels). + */ + SPIN_LOCK(&priv->devlock, irqflags); + for (i=0; i<priv->hwsup.ndma_chans; i++) { + dma_stat = REG_READ(&priv->regs->dma[i].ctrl); + /* Check for Errors and if Packets been sent or received if + * respective IRQ are enabled + */ +#ifdef HW_WITH_GI + if ( dma_stat & (GRSPW_DMA_STATUS_ERROR | GRSPW_DMACTRL_GI) ) { +#else + if ( (((dma_stat << 3) & (GRSPW_DMACTRL_PR | GRSPW_DMACTRL_PS)) + | GRSPW_DMA_STATUS_ERROR) & dma_stat ) { +#endif + /* Disable Further IRQs (until enabled again) + * from this DMA channel. Let the status + * bit remain so that they can be handled by + * work function. + */ + REG_WRITE(&priv->regs->dma[i].ctrl, dma_stat & + ~(GRSPW_DMACTRL_RI|GRSPW_DMACTRL_TI| + GRSPW_DMACTRL_PR|GRSPW_DMACTRL_PS| + GRSPW_DMACTRL_RA|GRSPW_DMACTRL_TA| + GRSPW_DMACTRL_AT)); + message |= WORK_DMA(i); + handled = 1; + } + } + SPIN_UNLOCK(&priv->devlock, irqflags); + + if (handled != 0) + priv->stats.irq_cnt++; + + /* Schedule work by sending message to work thread */ + if ((message != WORK_NONE) && grspw_work_queue) { + message |= WORK_CORE(priv->index); + stat = rtems_message_queue_send(grspw_work_queue, &message, 4); + if (stat != RTEMS_SUCCESSFUL) + printk("grspw_isr(%d): message fail %d (0x%x)\n", + priv->index, stat, message); + } +} + +STATIC void grspw_hw_dma_stop(struct grspw_dma_priv *dma) +{ + unsigned int ctrl; + struct grspw_dma_regs *dregs = dma->regs; + + ctrl = REG_READ(&dregs->ctrl) & (GRSPW_DMACTRL_LE | GRSPW_DMACTRL_EN | + GRSPW_DMACTRL_SP | GRSPW_DMACTRL_SA | GRSPW_DMACTRL_NS); + ctrl |= GRSPW_DMACTRL_AT; + REG_WRITE(&dregs->ctrl, ctrl); +} + +STATIC void grspw_hw_dma_softreset(struct grspw_dma_priv *dma) +{ + unsigned int ctrl; + struct grspw_dma_regs *dregs = dma->regs; + + ctrl = REG_READ(&dregs->ctrl) & (GRSPW_DMACTRL_LE | GRSPW_DMACTRL_EN); + REG_WRITE(&dregs->ctrl, ctrl); + + REG_WRITE(&dregs->rxmax, DEFAULT_RXMAX); + REG_WRITE(&dregs->txdesc, 0); + REG_WRITE(&dregs->rxdesc, 0); +} + +/* Hardware Action: + * - stop DMA + * - do not bring down the link (RMAP may be active) + * - RMAP settings untouched (RMAP may be active) + * - port select untouched (RMAP may be active) + * - timecodes are disabled + * - IRQ generation disabled + * - status not cleared (let user analyze it if requested later on) + * - Node address / First DMA channels Node address + * is untouched (RMAP may be active) + */ +STATIC void grspw_hw_stop(struct grspw_priv *priv) +{ + int i; + unsigned int ctrl; + IRQFLAGS_TYPE irqflags; + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + + for (i=0; i<priv->hwsup.ndma_chans; i++) + grspw_hw_dma_stop(&priv->dma[i]); + + ctrl = REG_READ(&priv->regs->ctrl); + REG_WRITE(&priv->regs->ctrl, ctrl & ( + GRSPW_CTRL_LD | GRSPW_CTRL_LS | GRSPW_CTRL_AS | + GRSPW_CTRL_RE | GRSPW_CTRL_RD | + GRSPW_CTRL_NP | GRSPW_CTRL_PS)); + + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); +} + +/* Soft reset of GRSPW core registers */ +STATIC void grspw_hw_softreset(struct grspw_priv *priv) +{ + int i; + + for (i=0; i<priv->hwsup.ndma_chans; i++) + grspw_hw_dma_softreset(&priv->dma[i]); + + REG_WRITE(&priv->regs->status, 0xffffffff); + REG_WRITE(&priv->regs->time, 0); +} + +int grspw_dev_count(void) +{ + return grspw_count; +} + +void grspw_initialize_user(void *(*devfound)(int), void (*devremove)(int,void*)) +{ + int i; + struct grspw_priv *priv; + + /* Set new Device Found Handler */ + grspw_dev_add = devfound; + grspw_dev_del = devremove; + + if (grspw_initialized == 1 && grspw_dev_add) { + /* Call callback for every previously found device */ + for (i=0; i<grspw_count; i++) { + priv = priv_tab[i]; + if (priv) + priv->data = grspw_dev_add(i); + } + } +} + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +static int grspw_common_init(void); +static int grspw2_init3(struct drvmgr_dev *dev); + +static struct drvmgr_drv_ops grspw2_ops = +{ + .init = {NULL, NULL, grspw2_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +static struct amba_dev_id grspw2_ids[] = +{ + {VENDOR_GAISLER, GAISLER_SPW}, /* not yet supported */ + {VENDOR_GAISLER, GAISLER_SPW2}, + {VENDOR_GAISLER, GAISLER_SPW2_DMA}, + {0, 0} /* Mark end of table */ +}; + +static struct amba_drv_info grspw2_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRSPW2_ID,/* Driver ID */ + "GRSPW_PKT_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grspw2_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct grspw_priv), /* Let DrvMgr alloc priv */ + }, + &grspw2_ids[0] +}; + +void grspw2_register_drv (void) +{ + GRSPW_DBG("Registering GRSPW2 packet driver\n"); + drvmgr_drv_register(&grspw2_drv_info.general); +} + +static int grspw2_init3(struct drvmgr_dev *dev) +{ + struct grspw_priv *priv; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + int i, size; + unsigned int ctrl; + union drvmgr_key_value *value; + + GRSPW_DBG("GRSPW[%d] on bus %s\n", dev->minor_drv, + dev->parent->dev->name); + + if (grspw_count > GRSPW_MAX) + return DRVMGR_ENORES; + + priv = dev->priv; + if (priv == NULL) + return DRVMGR_NOMEM; + priv->dev = dev; + + /* If first device init common part of driver */ + if (grspw_common_init()) + return DRVMGR_FAIL; + + /*** Now we take care of device initialization ***/ + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if (ambadev == NULL) + return -1; + pnpinfo = &ambadev->info; + priv->irq = pnpinfo->irq; + priv->regs = (struct grspw_regs *)pnpinfo->apb_slv->start; + + /* Read Hardware Support from Control Register */ + ctrl = REG_READ(&priv->regs->ctrl); + priv->hwsup.rmap = (ctrl & GRSPW_CTRL_RA) >> GRSPW_CTRL_RA_BIT; + priv->hwsup.rmap_crc = (ctrl & GRSPW_CTRL_RC) >> GRSPW_CTRL_RC_BIT; + priv->hwsup.rx_unalign = (ctrl & GRSPW_CTRL_RX) >> GRSPW_CTRL_RX_BIT; + priv->hwsup.nports = 1 + ((ctrl & GRSPW_CTRL_PO) >> GRSPW_CTRL_PO_BIT); + priv->hwsup.ndma_chans = 1 + ((ctrl & GRSPW_CTRL_NCH) >> GRSPW_CTRL_NCH_BIT); + + /* Construct hardware version identification */ + priv->hwsup.hw_version = pnpinfo->device << 16 | pnpinfo->apb_slv->ver; + + if ((pnpinfo->device == GAISLER_SPW2) || + (pnpinfo->device == GAISLER_SPW2_DMA)) { + priv->hwsup.strip_adr = 1; /* All GRSPW2 can strip Address */ + priv->hwsup.strip_pid = 1; /* All GRSPW2 can strip PID */ + } else { + /* Autodetect GRSPW1 features? */ + priv->hwsup.strip_adr = 0; + priv->hwsup.strip_pid = 0; + } + + /* Let user limit the number of DMA channels on this core to save + * space. Only the first nDMA channels will be available. + */ + value = drvmgr_dev_key_get(priv->dev, "nDMA", KEY_TYPE_INT); + if (value && (value->i < priv->hwsup.ndma_chans)) + priv->hwsup.ndma_chans = value->i; + + /* Allocate and init Memory for all DMA channels */ + size = sizeof(struct grspw_dma_priv) * priv->hwsup.ndma_chans; + priv->dma = (struct grspw_dma_priv *) malloc(size); + if (priv->dma == NULL) + return DRVMGR_NOMEM; + memset(priv->dma, 0, size); + for (i=0; i<priv->hwsup.ndma_chans; i++) { + priv->dma[i].core = priv; + priv->dma[i].index = i; + priv->dma[i].regs = &priv->regs->dma[i]; + } + + /* Startup Action: + * - stop DMA + * - do not bring down the link (RMAP may be active) + * - RMAP settings untouched (RMAP may be active) + * - port select untouched (RMAP may be active) + * - timecodes are diabled + * - IRQ generation disabled + * - status cleared + * - Node address / First DMA channels Node address + * is untouched (RMAP may be active) + */ + grspw_hw_stop(priv); + grspw_hw_softreset(priv); + + /* Register character device in registered region */ + priv->index = grspw_count; + priv_tab[priv->index] = priv; + grspw_count++; + + /* Device name */ + sprintf(priv->devname, "grspw%d", priv->index); + + /* Tell above layer about new device */ + if (grspw_dev_add) + priv->data = grspw_dev_add(priv->index); + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +static int grspw_common_init(void) +{ + if (grspw_initialized == 1) + return 0; + if (grspw_initialized == -1) + return -1; + grspw_initialized = -1; + + /* Device Semaphore created with count = 1 */ + if (rtems_semaphore_create(rtems_build_name('S', 'G', 'L', 'S'), 1, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ + RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ + RTEMS_NO_PRIORITY_CEILING, 0, &grspw_sem) != RTEMS_SUCCESSFUL) + return -1; + + /* Work queue, Work thread. Not created if user disables it. + * user can disable it when interrupt is not used to save resources + */ + if (grspw_work_task_priority != -1) { + if (rtems_message_queue_create( + rtems_build_name('S', 'G', 'L', 'Q'), 32, 4, RTEMS_FIFO, + &grspw_work_queue) != RTEMS_SUCCESSFUL) + return -1; + + if (rtems_task_create(rtems_build_name('S', 'G', 'L', 'T'), + grspw_work_task_priority, RTEMS_MINIMUM_STACK_SIZE, + RTEMS_PREEMPT | RTEMS_NO_ASR, RTEMS_NO_FLOATING_POINT, + &grspw_work_task) != RTEMS_SUCCESSFUL) + return -1; + + if (rtems_task_start(grspw_work_task, grspw_work_func, 0) != + RTEMS_SUCCESSFUL) + return -1; + } + + grspw_initialized = 1; + return 0; +} diff --git a/cpukit/libdrvmgr/drvmgr_confdefs.h b/cpukit/libdrvmgr/drvmgr_confdefs.h index b2e4aba..957e4e0 100644 --- a/cpukit/libdrvmgr/drvmgr_confdefs.h +++ b/cpukit/libdrvmgr/drvmgr_confdefs.h @@ -42,6 +42,9 @@ extern void greth_register_drv(void); #define DRIVER_AMBAPP_GAISLER_GRSPW_REG {grspw_register_drv} extern void grspw_register_drv(void); +#define DRIVER_AMBAPP_GAISLER_GRSPW2_REG {grspw2_register_drv} +extern void grspw2_register_drv(void); + #define DRIVER_AMBAPP_GAISLER_GRCAN_REG {grcan_register_drv} extern void grcan_register_drv(void); @@ -155,6 +158,9 @@ struct drvmgr_drv_reg_func drvmgr_drivers[] = { #ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRSPW DRIVER_AMBAPP_GAISLER_GRSPW_REG, #endif +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRSPW2 + DRIVER_AMBAPP_GAISLER_GRSPW2_REG, +#endif #ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GRCAN DRIVER_AMBAPP_GAISLER_GRCAN_REG, #endif -- 1.7.0.4 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel