diff options
Diffstat (limited to 'sys/dev/pci/if_san_xilinx.c')
-rw-r--r-- | sys/dev/pci/if_san_xilinx.c | 3535 |
1 files changed, 3535 insertions, 0 deletions
diff --git a/sys/dev/pci/if_san_xilinx.c b/sys/dev/pci/if_san_xilinx.c new file mode 100644 index 00000000000..05c036ec7fb --- /dev/null +++ b/sys/dev/pci/if_san_xilinx.c @@ -0,0 +1,3535 @@ +/*- + * Copyright (c) 2001-2004 Sangoma Technologies (SAN) + * All rights reserved. www.sangoma.com + * + * This code is written by Nenad Corbic <ncorbic@sangoma.com> and + * Alex Feldman <al.feldman@sangoma.com> for SAN. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of Sangoma Technologies nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY SANGOMA TECHNOLOGIES AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +# include </usr/include/bitstring.h> +# include <sys/types.h> +# include <sys/param.h> +# include <sys/systm.h> +# include <sys/syslog.h> +# include <sys/ioccom.h> +# include <sys/malloc.h> +# include <sys/errno.h> +# include <sys/mbuf.h> +# include <sys/socket.h> +# include <sys/kernel.h> +# include <sys/time.h> +# include <sys/timeout.h> + +# include <net/bpf.h> +# include <net/if.h> +# include <net/if_media.h> +# include <net/netisr.h> +# include <net/if_sppp.h> +# include <netinet/in_systm.h> +# include <netinet/in.h> +# include <netinet/in_var.h> +# include <netinet/udp.h> +# include <netinet/ip.h> + +# include <dev/pci/if_san_common.h> +# include <dev/pci/if_san_obsd.h> +# include <dev/pci/if_san_xilinx.h> + +/****** Defines & Macros ****************************************************/ + +/* Private critical flags */ +enum { + POLL_CRIT = PRIV_CRIT, + TX_BUSY, + RX_BUSY, + TASK_POLL, + CARD_DOWN +}; + +enum { + LINK_DOWN, + DEVICE_DOWN +}; + +#define MAX_IP_ERRORS 10 + +#define PORT(x) (x == 0 ? "PRIMARY" : "SECONDARY" ) +#define MAX_TX_BUF 10 +#define MAX_RX_BUF 10 + +#undef DEB_XILINX + +#if 1 +# define TRUE_FIFO_SIZE 1 +#else +# undef TRUE_FIFO_SIZE +# define HARD_FIFO_CODE 0x01 +#endif + +static int aft_rx_copyback=1000; + +/******Data Structures*****************************************************/ + +/* This structure is placed in the private data area of the device structure. + * The card structure used to occupy the private area but now the following + * structure will incorporate the card structure along with Protocol specific data + */ +typedef struct +{ + wanpipe_common_t common; + + struct ifqueue wp_tx_free_list; + struct ifqueue wp_tx_pending_list; + struct ifqueue wp_tx_complete_list; + struct ifqueue wp_rx_free_list; + struct ifqueue wp_rx_complete_list; + + struct mbuf *tx_dma_mbuf; + u_int8_t tx_dma_cnt; + + struct mbuf *rx_dma_mbuf; + + unsigned long time_slot_map; + unsigned char num_of_time_slots; + long logic_ch_num; + + unsigned char dma_status; + unsigned char ignore_modem; + struct ifqueue udp_queue; + + unsigned long router_start_time; + + unsigned long tick_counter; /* For 5s timeout counter */ + unsigned long router_up_time; + + unsigned char mc; /* Mulitcast support on/off */ + unsigned char udp_pkt_src; /* udp packet processing */ + unsigned short timer_int_enabled; + + unsigned char interface_down; + + u_int8_t gateway; + u_int8_t true_if_encoding; + + char if_name[IFNAMSIZ+1]; + + u_int8_t idle_flag; + u_int16_t max_idle_size; + u_int8_t idle_start; + + u_int8_t pkt_error; + u_int8_t rx_fifo_err_cnt; + + int first_time_slot; + + struct mbuf *tx_idle_mbuf; + unsigned long tx_dma_addr; + unsigned int tx_dma_len; + unsigned char rx_dma; + unsigned char pci_retry; + + unsigned char fifo_size_code; + unsigned char fifo_base_addr; + unsigned char fifo_size; + + int dma_mtu; + + void *prot_ch; + wan_trace_t trace_info; + +}xilinx_softc_t; +#define WAN_IFP_TO_SOFTC(ifp) (xilinx_softc_t*)((ifp)->if_softc) + +/* Route Status options */ +#define NO_ROUTE 0x00 +#define ADD_ROUTE 0x01 +#define ROUTE_ADDED 0x02 +#define REMOVE_ROUTE 0x03 + +#define WP_WAIT 0 +#define WP_NO_WAIT 1 + +/* variable for keeping track of enabling/disabling FT1 monitor status */ +/* static int rCount; */ + +extern void disable_irq(unsigned int); +extern void enable_irq(unsigned int); + +/**SECTOIN************************************************** + * + * Function Prototypes + * + ***********************************************************/ + +/* WAN link driver entry points. These are called by the WAN router module. */ +static int wan_xilinx_release(sdla_t*, struct ifnet*); + +/* Network device interface */ +static int wan_xilinx_up (struct ifnet*); +static int wan_xilinx_down(struct ifnet*); +static int wan_xilinx_ioctl(struct ifnet*, int cmd, struct ifreq*); +static int wan_xilinx_send (struct mbuf*, struct ifnet*); + +static void handle_front_end_state(void*); +static void enable_timer(void*); + +/* Miscellaneous Functions */ +static void port_set_state (sdla_t*, int); + +/* Interrupt handlers */ +static void wp_xilinx_isr (sdla_t*); + +/* Miscellaneous functions */ +static int process_udp_mgmt_pkt(sdla_t*, struct ifnet*, + xilinx_softc_t*, + int); +/* Bottom half handlers */ +static void xilinx_process_packet(xilinx_softc_t*); + +static int xilinx_chip_configure(sdla_t*); +static int xilinx_chip_unconfigure(sdla_t*); +static int xilinx_dev_configure(sdla_t*, xilinx_softc_t*); +static void xilinx_dev_unconfigure(sdla_t*, xilinx_softc_t*); +static int xilinx_dma_rx(sdla_t*, xilinx_softc_t*); +static void xilinx_dev_enable(sdla_t*, xilinx_softc_t*); +static void xilinx_dev_close(sdla_t*, xilinx_softc_t*); +static int xilinx_dma_tx (sdla_t*, xilinx_softc_t*); +static void xilinx_dma_tx_complete (sdla_t*, xilinx_softc_t*); +static void xilinx_dma_rx_complete (sdla_t*, xilinx_softc_t*); +static void xilinx_dma_max_logic_ch(sdla_t*); +static int xilinx_init_rx_dev_fifo(sdla_t*, xilinx_softc_t*, unsigned char); +static void xilinx_init_tx_dma_descr(sdla_t*, xilinx_softc_t*); +static int xilinx_init_tx_dev_fifo(sdla_t*, xilinx_softc_t*, unsigned char); +static void xilinx_tx_post_complete (sdla_t*, xilinx_softc_t*, struct mbuf*); +static void xilinx_rx_post_complete (sdla_t*, xilinx_softc_t*, + struct mbuf*, + struct mbuf**, + unsigned char*); + + +static char request_xilinx_logical_channel_num (sdla_t*, xilinx_softc_t*, long*); +static void free_xilinx_logical_channel_num (sdla_t*, int); + + +static unsigned char read_cpld(sdla_t*, unsigned short); +static unsigned char write_cpld(sdla_t*, unsigned short,unsigned char); + +static void front_end_interrupt(sdla_t*, unsigned long); +static void enable_data_error_intr(sdla_t*); +static void disable_data_error_intr(sdla_t*, unsigned char); + +static void xilinx_tx_fifo_under_recover (sdla_t*, xilinx_softc_t*); + +static int xilinx_write_ctrl_hdlc(sdla_t*, u_int32_t, u_int8_t, u_int32_t); + +static int set_chan_state(sdla_t*, struct ifnet*, int); + +static int fifo_error_interrupt(sdla_t*, unsigned long); +static int request_fifo_baddr_and_size(sdla_t*, xilinx_softc_t*); +static int map_fifo_baddr_and_size(sdla_t*, unsigned char, unsigned char*); +static int free_fifo_baddr_and_size (sdla_t*, xilinx_softc_t*); + +static void aft_red_led_ctrl(sdla_t*, int); +static void aft_led_timer(void*); + +static int aft_alloc_rx_dma_buff(sdla_t*, xilinx_softc_t*, int); +static int aft_init_requeue_free_m(xilinx_softc_t*, struct mbuf*); +static int aft_core_ready(sdla_t*); + +/* TE1 Control registers */ +static WRITE_FRONT_END_REG_T write_front_end_reg; +static READ_FRONT_END_REG_T read_front_end_reg; + +static void wan_ifmedia_sts(struct ifnet*, struct ifmediareq*); +static int wan_ifmedia_upd(struct ifnet*); + +static void xilinx_delay(int sec) +{ +#if 0 + unsigned long timeout=ticks; + while ((ticks-timeout)<(sec*hz)){ + schedule(); + } +#endif +} + +/**SECTION********************************************************* + * + * Public Functions + * + ******************************************************************/ + +/*============================================================================ + * wan_xilinx_init - + * + */ + +void* wan_xilinx_init (sdla_t* card) +{ + xilinx_softc_t *sc; + struct ifnet *ifp; + + /* Verify configuration ID */ + bit_clear((u_int8_t*)&card->critical, CARD_DOWN); + + card->u.xilinx.num_of_ch = 0; + card->u.xilinx.mru_trans = 1500; + card->u.xilinx.dma_per_ch = 10; + + /* TE1 Make special hardware initialization for T1/E1 board */ + + if (IS_TE1(&card->fe_te.te_cfg)){ + + card->write_front_end_reg = write_front_end_reg; + card->read_front_end_reg = read_front_end_reg; + card->te_enable_timer = enable_timer; + card->te_link_state = handle_front_end_state; + }else{ + card->front_end_status = FE_CONNECTED; + } + + /* WARNING: After this point the init function + * must return with 0. The following bind + * functions will cause problems if structures + * below are not initialized */ + + card->del_if = &wan_xilinx_release; + card->iface_up = &wan_xilinx_up; + card->iface_down = &wan_xilinx_down; + card->iface_send = &wan_xilinx_send; + card->iface_ioctl= &wan_xilinx_ioctl; + + write_cpld(card,LED_CONTROL_REG,0x0E); + + sdla_getcfg(card->hw, SDLA_BASEADDR, &card->u.xilinx.bar); + + xilinx_delay(1); + + timeout_set(&card->u.xilinx.led_timer, aft_led_timer, (void*)card); + + /* allocate and initialize private data */ + sc = malloc(sizeof(xilinx_softc_t), M_DEVBUF, M_NOWAIT); + if(sc == NULL){ + return NULL; + } + memset(sc, 0, sizeof(xilinx_softc_t)); + ifp = (struct ifnet*)&sc->common.ifp; + if (wanpipe_generic_register(card, ifp, card->devname)){ + free(sc, M_DEVBUF); + return NULL; + } + + + strncpy(sc->if_name, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + sc->common.card = card; + sc->first_time_slot=-1; + sc->time_slot_map = 0; + + IFQ_SET_MAXLEN(&sc->wp_tx_free_list, MAX_TX_BUF); + sc->wp_tx_free_list.ifq_len = 0; + IFQ_SET_MAXLEN(&sc->wp_tx_pending_list, MAX_TX_BUF); + sc->wp_tx_pending_list.ifq_len = 0; + IFQ_SET_MAXLEN(&sc->wp_tx_complete_list, MAX_TX_BUF); + sc->wp_tx_complete_list.ifq_len = 0; + IFQ_SET_MAXLEN(&sc->wp_rx_free_list, MAX_RX_BUF); + sc->wp_rx_free_list.ifq_len = 0; + IFQ_SET_MAXLEN(&sc->wp_rx_complete_list, MAX_RX_BUF); + sc->wp_rx_complete_list.ifq_len = 0; + + xilinx_delay(1); + + ifmedia_init(&sc->common.ifm, 0, wan_ifmedia_upd, wan_ifmedia_sts); + if (IS_TE1(&card->fe_te.te_cfg)){ + ifmedia_add(&sc->common.ifm, IFM_TDM|IFM_TDM_T1, 0, NULL); + ifmedia_add(&sc->common.ifm, IFM_TDM|IFM_TDM_T1_AMI, 0, NULL); + ifmedia_add(&sc->common.ifm, IFM_TDM|IFM_TDM_E1, 0, NULL); + ifmedia_add(&sc->common.ifm, IFM_TDM|IFM_TDM_E1_AMI, 0, NULL); + ifmedia_set(&sc->common.ifm, IFM_TDM|IFM_TDM_T1); + }else{ + /* Currently we not support ifmedia types for other + ** front end types. */ + } + return sc; +} + +/*============================================================================ + * wan_xilinx_release + * + * + */ +static int wan_xilinx_release (sdla_t* card, struct ifnet* ifp) +{ + xilinx_softc_t* sc = ifp->if_softc; + + IF_PURGE(&sc->wp_rx_free_list); + IF_PURGE(&sc->wp_rx_complete_list); + IF_PURGE(&sc->wp_tx_free_list); + IF_PURGE(&sc->wp_tx_pending_list); + + if (sc->tx_dma_addr && sc->tx_dma_len){ + sc->tx_dma_addr=0; + sc->tx_dma_len=0; + } + + if (sc->tx_dma_mbuf){ + log(LOG_INFO, "freeing tx dma mbuf\n"); + m_freem(sc->tx_dma_mbuf); + sc->tx_dma_mbuf=NULL; + } + + if (sc->tx_idle_mbuf){ + log(LOG_INFO, "freeing idle tx dma mbuf\n"); + m_freem(sc->tx_idle_mbuf); + sc->tx_idle_mbuf=NULL; + } + + if (sc->rx_dma_mbuf){ + m_freem(sc->rx_dma_mbuf); + sc->rx_dma_mbuf=NULL; + } + wanpipe_generic_unregister(ifp); + ifp->if_softc = NULL; + free(sc, M_DEVBUF); + return 0; +} + +static void wan_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmreq) +{ + wanpipe_common_t *common = (wanpipe_common_t*)ifp->if_softc; + struct ifmedia *ifm; + + WAN_ASSERT1(common == NULL); + ifm = &common->ifm; + ifmreq->ifm_active = ifm->ifm_cur->ifm_media; + return; +} + +static int wan_ifmedia_upd(struct ifnet *ifp) +{ + wanpipe_common_t *common = (wanpipe_common_t*)ifp->if_softc; + sdla_t *card; + + WAN_ASSERT(common == NULL); + WAN_ASSERT(common->card == NULL); + card = (sdla_t*)common->card; + if (IS_TE1(&card->fe_te.te_cfg)){ + return sdla_te_setcfg(card, &common->ifm); + } + return -EINVAL; +} + + +/**SECTION*********************************************************** + * + * KERNEL Device Entry Interfaces + * + ********************************************************************/ + + + +/*============================================================================ + * wan_xilinx_up + * + */ +static int wan_xilinx_up (struct ifnet* ifp) +{ + xilinx_softc_t *sc = ifp->if_softc; + sdla_t *card = NULL; + struct timeval tv; + int err = 0; + + WAN_ASSERT(sc == NULL); + WAN_ASSERT(sc->common.card == NULL); + card = (sdla_t*)sc->common.card; + if (card->state != WAN_DISCONNECTED){ + return 0; + } + sc->time_slot_map = card->fe_te.te_cfg.active_ch; + sc->dma_mtu = xilinx_valid_mtu(ifp->if_mtu+100); + if (!sc->dma_mtu){ + log(LOG_INFO, "%s:%s: Error invalid MTU %d\n", + card->devname, + sc->if_name, + ifp->if_mtu); + return -EINVAL; + } + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Allocating %d dma mbuf len=%d\n", + card->devname, + card->u.xilinx.dma_per_ch, + sc->dma_mtu); +#endif + err=aft_alloc_rx_dma_buff(card, sc, card->u.xilinx.dma_per_ch); + if (err){ + return -EINVAL; + } + + err=xilinx_chip_configure(card); + if (err){ + return -EINVAL; + } + + card->isr = &wp_xilinx_isr; + + err=xilinx_dev_configure(card, sc); + if (err){ + xilinx_chip_unconfigure(card); + return -EINVAL; + } + xilinx_delay(1); + + /* Initialize the router start time. + * Used by wanpipemon debugger to indicate + * how long has the interface been up */ + microtime(&tv); + sc->router_start_time = tv.tv_sec; + + xilinx_init_tx_dma_descr(card,sc); + xilinx_dev_enable(card,sc); + + sc->ignore_modem=0x0F; + bit_clear((u_int8_t*)&card->critical, CARD_DOWN); + port_set_state(card, WAN_CONNECTING); + return err; +} + +/*============================================================================ + * wan_xilinx_down + * + */ +static int wan_xilinx_down (struct ifnet* ifp) +{ + xilinx_softc_t *sc = ifp->if_softc; + sdla_t *card = (sdla_t*)sc->common.card; + struct mbuf *m; + int s; + + if (card->state == WAN_DISCONNECTED){ + return 0; + } + xilinx_dev_close(card,sc); + + /* Disable DMA ENGINE before we perform + * core reset. Otherwise, we will receive + * rx fifo errors on subsequent resetart. */ + disable_data_error_intr(card,DEVICE_DOWN); + + bit_set((u_int8_t*)&card->critical, CARD_DOWN); + + timeout_del(&card->u.xilinx.led_timer); + + /* TE1 - Unconfiging, only on shutdown */ + if (IS_TE1(&card->fe_te.te_cfg)) { + sdla_te_unconfig(card); + } + + s = splnet(); + + card->isr=NULL; + + if (sc->tx_dma_addr && sc->tx_dma_len){ + sc->tx_dma_addr=0; + sc->tx_dma_len=0; + } + + if (sc->tx_dma_mbuf){ + m_freem(sc->tx_dma_mbuf); + sc->tx_dma_mbuf=NULL; + } + + if (sc->tx_idle_mbuf){ + m_freem(sc->tx_idle_mbuf); + sc->tx_idle_mbuf=NULL; + } + + /* If there is something left in rx_dma_buf, then + ** move it to rx_free_list. */ + if (sc->rx_dma_mbuf){ + m=sc->rx_dma_mbuf; + aft_init_requeue_free_m(sc, m); + sc->rx_dma_mbuf=NULL; + } + /* If there is something in rx_complete_list, then + ** move evething to rx_free_list. */ + for(;;){ + IF_DEQUEUE(&sc->wp_rx_complete_list, m); + if (m == NULL) + break; + IF_ENQUEUE(&sc->wp_rx_free_list, m); + } + + splx(s); + + DELAY(10); + + xilinx_dev_unconfigure(card, sc); + xilinx_chip_unconfigure(card); + + port_set_state(card, WAN_DISCONNECTED); + sc->ignore_modem=0x00; + return 0; +} + +/*============================================================================ + * wan_xilinx_send + * + * + */ +static int wan_xilinx_send (struct mbuf* m, struct ifnet* ifp) +{ + + xilinx_softc_t *sc = ifp->if_softc; + sdla_t *card = (sdla_t*)sc->common.card; + + /* Mark interface as busy. The kernel will not + * attempt to send any more packets until we clear + * this condition */ + + if (m == NULL){ + /* This should never happen. Just a sanity check. + */ + return -EINVAL; + } + + if (card->state != WAN_CONNECTED){ + /* The card is still not ready to transmit... drop this packet! */ + m_freem(m); + return -EINVAL; + + }else { + + if (IF_QFULL(&sc->wp_tx_pending_list)){ + int err; +#if defined(DEBUG_TX) + log(LOG_INFO, "%s: Tx pending queue FULL\n", + ifp->if_xname); +#endif + /* TX pending queue is full. Try to send packet + ** from tx_pending queue (first) */ + err = xilinx_dma_tx(card,sc); + if (!err && !IF_QFULL(&sc->wp_tx_pending_list)){ + /* On success, we have place for the new + ** tx packet, try to send it now! */ + goto wan_xilinx_dma_tx_try; + } + /* Tx pedning queue is full. I can't accept new + ** tx packet, drop this packet and set interface + ** queue to OACTIVE */ + m_freem(m); + ifp->if_flags |= IFF_OACTIVE; + return -EBUSY; + }else{ +wan_xilinx_dma_tx_try: + IF_ENQUEUE(&sc->wp_tx_pending_list, m); + xilinx_dma_tx(card,sc); + } + } + + return 0; +} + + + +/*======================================================================== + * + * wan_xilinx_ioctl + * + * + */ +static int +wan_xilinx_ioctl(struct ifnet *ifp, int cmd, struct ifreq *ifr) +{ + xilinx_softc_t *sc= (xilinx_softc_t*)ifp->if_softc; + struct mbuf *m; + sdla_t *card; + wan_udp_pkt_t *wan_udp_pkt; + int err=0; + + if (!sc){ + return -ENODEV; + } + card=(sdla_t*)sc->common.card; + + switch(cmd) + { + case SIOC_WANPIPE_PIPEMON: + + if (IF_QFULL(&sc->udp_queue)){ + return -EBUSY; + } + + /* For performance reasons test the critical + * here before spin lock */ + if (bit_test((u_int8_t*)&card->in_isr, 0)){ + return -EBUSY; + } + + m = wan_mbuf_alloc(); + if (m == NULL){ + return -ENOMEM; + } + wan_udp_pkt = mtod(m, wan_udp_pkt_t*); + if (copyin(ifr->ifr_data, &wan_udp_pkt->wan_udp_hdr,sizeof(wan_udp_hdr_t))){ + m_freem(m); + return -EFAULT; + } + IF_ENQUEUE(&sc->udp_queue, m); + + process_udp_mgmt_pkt(card,ifp,sc,1); + + if (copyout(&wan_udp_pkt->wan_udp_hdr,ifr->ifr_data, sizeof(wan_udp_hdr_t))){ + m_freem(m); + return -EFAULT; + } + + IF_DEQUEUE(&sc->udp_queue, m); + m_freem(m); + return 0; + + default: + if (card->ioctl){ + err = card->ioctl(ifp, cmd, ifr); + } + break; + } + + return err; +} + +/*============================================================================= + * process_udp_mgmt_pkt + * + * Process all "wanpipemon" debugger commands. This function + * performs all debugging tasks: + * + * Line Tracing + * Line/Hardware Statistics + * Protocol Statistics + * + * "wanpipemon" utility is a user-space program that + * is used to debug the WANPIPE product. + * + */ +#if 1 +static int process_udp_mgmt_pkt(sdla_t* card, struct ifnet* ifp, + xilinx_softc_t* sc, int local_dev ) +{ + struct mbuf *m; + unsigned short buffer_length; + wan_udp_pkt_t *wan_udp_pkt; + wan_trace_t *trace_info=NULL; + struct timeval tv; + + IF_POLL(&sc->udp_queue, m); + if (m == NULL){ + return -EINVAL; + } + wan_udp_pkt = mtod(m, wan_udp_pkt_t *); + trace_info=&sc->trace_info; + + { + + struct mbuf *m0; + + wan_udp_pkt->wan_udp_opp_flag = 0; + + switch(wan_udp_pkt->wan_udp_command) { + + case READ_CONFIGURATION: + case READ_CODE_VERSION: + wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_data_len=0; + break; + + + case ENABLE_TRACING: + + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + wan_udp_pkt->wan_udp_data_len = 0; + + if (!bit_test((u_int8_t*)&trace_info->tracing_enabled, 0)){ + + trace_info->trace_timeout = ticks; + + IF_PURGE(&trace_info->ifq); + if (wan_udp_pkt->wan_udp_data[0] == 0){ + bit_clear((u_int8_t*)&trace_info->tracing_enabled, 1); + log(LOG_INFO, "%s: ADSL L3 trace enabled!\n", + card->devname); + }else if (wan_udp_pkt->wan_udp_data[0] == 1){ + bit_clear((u_int8_t*)&trace_info->tracing_enabled,2 ); + bit_set((u_int8_t*)&trace_info->tracing_enabled,1); + log(LOG_INFO, "%s: ADSL L2 trace enabled!\n", + card->devname); + }else{ + bit_clear((u_int8_t*)&trace_info->tracing_enabled, 1); + bit_set((u_int8_t*)&trace_info->tracing_enabled,2); + log(LOG_INFO, "%s: ADSL L1 trace enabled!\n", + card->devname); + } + bit_set ((u_int8_t*)&trace_info->tracing_enabled, 0); + + }else{ + log(LOG_INFO, "%s: Error: AFT trace running!\n", + card->devname); + wan_udp_pkt->wan_udp_return_code = 2; + } + + break; + + case DISABLE_TRACING: + + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + + if(bit_test((u_int8_t*)&trace_info->tracing_enabled, 0)) { + + bit_clear((u_int8_t*)&trace_info->tracing_enabled, 0); + bit_clear((u_int8_t*)&trace_info->tracing_enabled, 1); + bit_clear((u_int8_t*)&trace_info->tracing_enabled, 2); + IF_PURGE(&trace_info->ifq); + log(LOG_INFO, "%s: Disabling ADSL trace\n", + card->devname); + + }else{ + /* set return code to line trace already + disabled */ + wan_udp_pkt->wan_udp_return_code = 1; + } + + break; + + case GET_TRACE_INFO: + + if(bit_test((u_int8_t*)&trace_info->tracing_enabled, 0)){ + trace_info->trace_timeout = ticks; + }else{ + log(LOG_INFO, "%s: Error AFT trace not enabled\n", + card->devname); + /* set return code */ + wan_udp_pkt->wan_udp_return_code = 1; + break; + } + + buffer_length = 0; + wan_udp_pkt->wan_udp_aft_num_frames = 0; + wan_udp_pkt->wan_udp_aft_ismoredata = 0; + + while (!IF_IS_EMPTY(&trace_info->ifq)){ + IF_POLL(&trace_info->ifq, m0); + if (m0 == NULL){ + log(LOG_INFO, "%s: No more trace packets in trace queue!\n", + card->devname); + break; + } + if ((WAN_MAX_DATA_SIZE - buffer_length) < m0->m_pkthdr.len){ + /* indicate there are more frames on board & exit */ + wan_udp_pkt->wan_udp_aft_ismoredata = 0x01; + break; + } + + m_copydata(m0, + 0, + m0->m_pkthdr.len, + &wan_udp_pkt->wan_udp_data[buffer_length]); + buffer_length += m0->m_pkthdr.len; + IF_DEQUEUE(&trace_info->ifq, m0); + if (m0){ + m_freem(m0); + } + wan_udp_pkt->wan_udp_aft_num_frames++; + } + /* set the data length and return code */ + wan_udp_pkt->wan_udp_data_len = buffer_length; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + break; + + case ROUTER_UP_TIME: + microtime(&tv); + sc->router_up_time = tv.tv_sec; + sc->router_up_time -= sc->router_start_time; + *(unsigned long *)&wan_udp_pkt->wan_udp_data = + sc->router_up_time; + wan_udp_pkt->wan_udp_data_len = sizeof(unsigned long); + wan_udp_pkt->wan_udp_return_code = 0; + break; + + case WAN_GET_MEDIA_TYPE: + case WAN_FE_GET_STAT: + case WAN_FE_SET_LB_MODE: + case WAN_FE_FLUSH_PMON: + case WAN_FE_GET_CFG: + + if (IS_TE1(&card->fe_te.te_cfg)){ + sdla_te_udp(card, + &wan_udp_pkt->wan_udp_cmd, + &wan_udp_pkt->wan_udp_data[0]); + }else{ + if (wan_udp_pkt->wan_udp_command == WAN_GET_MEDIA_TYPE){ + wan_udp_pkt->wan_udp_data_len = sizeof(unsigned char); + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + }else{ + wan_udp_pkt->wan_udp_return_code = WAN_UDP_INVALID_CMD; + } + } + break; + + + + case WAN_GET_PROTOCOL: + wan_udp_pkt->wan_udp_aft_num_frames = WANCONFIG_AFT; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + wan_udp_pkt->wan_udp_data_len = 1; + break; + + case WAN_GET_PLATFORM: + wan_udp_pkt->wan_udp_data[0] = WAN_PLATFORM_ID; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + wan_udp_pkt->wan_udp_data_len = 1; + break; + + default: + wan_udp_pkt->wan_udp_data_len = 0; + wan_udp_pkt->wan_udp_return_code = 0xCD; + + log(LOG_INFO, "%s: Warning, Illegal UDP command attempted from network: %x\n", + card->devname,wan_udp_pkt->wan_udp_command); + break; + } /* end of switch */ + } /* end of else */ + + wan_udp_pkt->wan_udp_request_reply = UDPMGMT_REPLY; + return 1; + +} +#endif + +/**SECTION********************************************************** + * + * FIRMWARE Specific Interface Functions + * + *******************************************************************/ + + +/*============================================================================ + * xilinx_chip_configure + * + * + */ + +static int xilinx_chip_configure(sdla_t *card) +{ + u_int32_t reg,tmp; + int err=0; + u_int16_t adapter_type,adptr_security; + +#if defined(DEBUG_INIT) + log(LOG_DEBUG, "Xilinx Chip Configuration. -- \n"); +#endif + xilinx_delay(1); + + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); + + /* Configure for T1 or E1 front end */ + if (IS_T1(&card->fe_te.te_cfg)){ + card->u.xilinx.num_of_time_slots=NUM_OF_T1_CHANNELS; + bit_clear((u_int8_t*)®, INTERFACE_TYPE_T1_E1_BIT); + bit_set((u_int8_t*)®, FRONT_END_FRAME_FLAG_ENABLE_BIT); + }else if (IS_E1(&card->fe_te.te_cfg)){ + card->u.xilinx.num_of_time_slots=NUM_OF_E1_CHANNELS; + bit_set((u_int8_t*)®, INTERFACE_TYPE_T1_E1_BIT); + bit_set((u_int8_t*)®, FRONT_END_FRAME_FLAG_ENABLE_BIT); + }else{ + log(LOG_INFO, "%s: Error: Xilinx doesn't support non T1/E1 interface!\n", + card->devname); + return -EINVAL; + } + + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + + DELAY(10000); + + /* Reset PMC */ + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); + bit_clear((u_int8_t*)®, FRONT_END_RESET_BIT); + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + DELAY(1000); + + bit_set((u_int8_t*)®, FRONT_END_RESET_BIT); + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + DELAY(100); + +#if defined(DEBUG_INIT) + log(LOG_DEBUG, "--- Chip Reset. -- \n"); +#endif + + /* Reset Chip Core */ + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); + bit_set((u_int8_t*)®, CHIP_RESET_BIT); + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + + DELAY(100); + + /* Disable the chip/hdlc reset condition */ + bit_clear((u_int8_t*)®, CHIP_RESET_BIT); + + /* Disable ALL chip interrupts */ + bit_clear((u_int8_t*)®, GLOBAL_INTR_ENABLE_BIT); + bit_clear((u_int8_t*)®, ERROR_INTR_ENABLE_BIT); + bit_clear((u_int8_t*)®, FRONT_END_INTR_ENABLE_BIT); + + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + + xilinx_delay(1); + + sdla_getcfg(card->hw, SDLA_ADAPTERTYPE, &adapter_type); + DELAY(100); + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Hardware Adapter Type 0x%X\n", + card->devname,adapter_type); +#endif + + adptr_security = read_cpld(card,SECURITY_CPLD_REG); + adptr_security = adptr_security >> SECURITY_CPLD_SHIFT; + adptr_security = adptr_security & SECURITY_CPLD_MASK; + +#if defined(DEBUG_INIT) + switch(adptr_security){ + case SECURITY_1LINE_UNCH: + log(LOG_INFO, "%s: Security 1 Line UnCh\n", + card->devname); + break; + case SECURITY_1LINE_CH: + log(LOG_INFO, "%s: Security 1 Line Ch\n", + card->devname); + break; + case SECURITY_2LINE_UNCH: + log(LOG_INFO, "%s: Security 2 Line UnCh\n", + card->devname); + break; + case SECURITY_2LINE_CH: + log(LOG_INFO, "%s: Security 2 Line Ch\n", + card->devname); + break; + default: + log(LOG_INFO, "%s: Error Invalid Security ID=0x%X\n", + card->devname,adptr_security); + /*return -EINVAL;*/ + } +#endif + + /* Turn off Onboard RED LED */ + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); + bit_set((u_int8_t*)®, XILINX_RED_LED); + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + DELAY(10); + + err=aft_core_ready(card); + if (err != 0){ + log(LOG_INFO, "%s: WARNING: HDLC Core Not Ready: B4 TE CFG!\n", + card->devname); + + } + + log(LOG_INFO, "%s: Configuring A101 PMC T1/E1/J1 Front End\n", + card->devname); + + if (sdla_te_config(card)){ + log(LOG_INFO, "%s: Failed %s configuratoin!\n", + card->devname, + IS_T1(&card->fe_te.te_cfg)?"T1":"E1"); + return -EINVAL; + } + + xilinx_delay(1); + + err=aft_core_ready(card); + if (err != 0){ + log(LOG_INFO, "%s: Error: HDLC Core Not Ready!\n", + card->devname); + + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); + + /* Disable the chip/hdlc reset condition */ + bit_set((u_int8_t*)®, CHIP_RESET_BIT); + + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + return err; + } else{ +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: HDLC Core Ready 0x%08X\n", + card->devname,reg); +#endif + } + + xilinx_delay(1); + + /* Setup global DMA parameters */ + reg=0; + reg|=(XILINX_DMA_SIZE << DMA_SIZE_BIT_SHIFT); + reg|=(XILINX_DMA_FIFO_UP << DMA_FIFO_HI_MARK_BIT_SHIFT); + reg|=(XILINX_DMA_FIFO_LO << DMA_FIFO_LO_MARK_BIT_SHIFT); + + /* Enable global DMA engine and set to default + * number of active channels. Note: this value will + * change in dev configuration */ + reg|=(XILINX_DEFLT_ACTIVE_CH << DMA_ACTIVE_CHANNEL_BIT_SHIFT); + bit_set((u_int8_t*)®, DMA_ENGINE_ENABLE_BIT); + +#if defined(DEBUG_INIT) + log(LOG_INFO, "--- Setup DMA control Reg. -- \n"); +#endif + + sdla_bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); + +#if defined(DEBUG_INIT) + log(LOG_INFO, "--- Tx/Rx global enable. -- \n"); +#endif + + xilinx_delay(1); + + reg=0; + sdla_bus_write_4(card->hw,XILINX_TIMESLOT_HDLC_CHAN_REG,reg); + + /* Clear interrupt pending registers befor first interrupt enable */ + sdla_bus_read_4(card->hw, XILINX_DMA_RX_INTR_PENDING_REG, &tmp); + sdla_bus_read_4(card->hw, XILINX_DMA_TX_INTR_PENDING_REG, &tmp); + sdla_bus_read_4(card->hw,XILINX_HDLC_RX_INTR_PENDING_REG, &tmp); + sdla_bus_read_4(card->hw,XILINX_HDLC_TX_INTR_PENDING_REG, &tmp); + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG, (u_int32_t*)®); + if (bit_test((u_int8_t*)®, DMA_INTR_FLAG)){ + log(LOG_INFO, "%s: Error: Active DMA Interrupt Pending. !\n", + card->devname); + + reg = 0; + /* Disable the chip/hdlc reset condition */ + bit_set((u_int8_t*)®, CHIP_RESET_BIT); + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + return err; + } + if (bit_test((u_int8_t*)®, ERROR_INTR_FLAG)){ + log(LOG_INFO, "%s: Error: Active Error Interrupt Pending. !\n", + card->devname); + + reg = 0; + /* Disable the chip/hdlc reset condition */ + bit_set((u_int8_t*)®, CHIP_RESET_BIT); + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + return err; + } + + + /* Alawys disable global data and error + * interrupts */ + bit_clear((u_int8_t*)®, GLOBAL_INTR_ENABLE_BIT); + bit_clear((u_int8_t*)®, ERROR_INTR_ENABLE_BIT); + + /* Always enable the front end interrupt */ + bit_set((u_int8_t*)®, FRONT_END_INTR_ENABLE_BIT); + +#if defined(DEBUG_INIT) + log(LOG_DEBUG, "--- Set Global Interrupts (0x%X)-- \n",reg); +#endif + + xilinx_delay(1); + + + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + + return err; +} + +/*============================================================================ + * xilinx_chip_unconfigure + * + * + */ + +static int xilinx_chip_unconfigure(sdla_t *card) +{ + u_int32_t reg = 0; + + sdla_bus_write_4(card->hw,XILINX_TIMESLOT_HDLC_CHAN_REG,reg); + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); + /* Enable the chip/hdlc reset condition */ + reg=0; + bit_set((u_int8_t*)®, CHIP_RESET_BIT); + + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + return 0; +} + + +/*============================================================================ + * xilinx_dev_configure + * + * + */ + +static int xilinx_dev_configure(sdla_t *card, xilinx_softc_t *sc) +{ + u_int32_t reg; + long free_logic_ch,i; + + sc->logic_ch_num=-1; + + if (!IS_TE1(&card->fe_te.te_cfg)){ + return -EINVAL; + } + + if (IS_E1(&card->fe_te.te_cfg)){ + log(LOG_DEBUG, "%s: Time Slot Orig 0x%lX Shifted 0x%lX\n", + sc->if_name, + sc->time_slot_map, + sc->time_slot_map<<1); + sc->time_slot_map=sc->time_slot_map<<1; + bit_clear((u_int8_t*)&sc->time_slot_map, 0); + } + + /* Channel definition section. If not channels defined + * return error */ + if (sc->time_slot_map == 0){ + log(LOG_INFO, "%s: Invalid Channel Selection 0x%lX\n", + card->devname,sc->time_slot_map); + return -EINVAL; + } + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s:%s: Active channels = 0x%lX\n", + card->devname,sc->if_name,sc->time_slot_map); +#endif + xilinx_delay(1); + + /* Check that the time slot is not being used. If it is + * stop the interface setup. Notice, though we proceed + * to check for all timeslots before we start binding + * the channels in. This way, we don't have to go back + * and clean the time_slot_map */ + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (bit_test((u_int8_t*)&sc->time_slot_map, i)){ + + if (sc->first_time_slot == -1){ +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Setting first time slot to %ld\n", + card->devname,i); +#endif + sc->first_time_slot=i; + } + +#if defined(DEBUG_INIT) + log(LOG_DEBUG, "%s: Configuring %s for timeslot %ld\n", + card->devname, sc->if_name, + IS_E1(&card->fe_te.te_cfg)?i:i+1); +#endif + if (bit_test((u_int8_t*)&card->u.xilinx.time_slot_map, i)){ + log(LOG_INFO, "%s: Channel/Time Slot resource conflict!\n", + card->devname); + log(LOG_INFO, "%s: %s: Channel/Time Slot %ld, aready in use!\n", + card->devname,sc->if_name,(i+1)); + + return -EEXIST; + } + + /* Calculate the number of timeslots for this + * interface */ + ++sc->num_of_time_slots; + } + } + + xilinx_delay(1); + + sc->logic_ch_num=request_xilinx_logical_channel_num(card, sc, &free_logic_ch); + if (sc->logic_ch_num == -1){ + return -EBUSY; + } + + xilinx_delay(1); + + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (bit_test((u_int8_t*)&sc->time_slot_map, i)){ + + bit_set((u_int8_t*)&card->u.xilinx.time_slot_map, i); + + sdla_bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); + reg&=~TIMESLOT_BIT_MASK; + + /*FIXME do not hardcode !*/ + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + + + /* Select a Timeslot for configuration */ + sdla_bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(i<<TIMESLOT_BIT_SHIFT))); + + + reg=sc->logic_ch_num&CONTROL_RAM_DATA_MASK; + +#ifdef TRUE_FIFO_SIZE + reg|=(sc->fifo_size_code&HDLC_FIFO_SIZE_MASK)<<HDLC_FIFO_SIZE_SHIFT; +#else + + reg|=(HARD_FIFO_CODE&HDLC_FIFO_SIZE_MASK)<<HDLC_FIFO_SIZE_SHIFT; +#endif + + reg|=(sc->fifo_base_addr&HDLC_FIFO_BASE_ADDR_MASK)<< + HDLC_FIFO_BASE_ADDR_SHIFT; + +#if defined(DEBUG_INIT) + log(LOG_INFO, "Setting Timeslot %ld to logic ch %ld Reg=0x%X\n", + i, sc->logic_ch_num,reg); +#endif + + xilinx_write_ctrl_hdlc(card, + i, + XILINX_CONTROL_RAM_ACCESS_BUF, + reg); + + } + } + + if (free_logic_ch != -1){ + + char free_ch_used=0; + + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (!bit_test((u_int8_t*)&card->u.xilinx.time_slot_map, i)){ + + sdla_bus_read_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + ®); + + reg&=~TIMESLOT_BIT_MASK; + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + + /* Select a Timeslot for configuration */ + sdla_bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(i<<TIMESLOT_BIT_SHIFT))); + + + reg=free_logic_ch&CONTROL_RAM_DATA_MASK; + + /* For the rest of the unused logic channels + * bind them to timeslot 31 and set the fifo + * size to 32 byte = Code=0x00 */ + + reg|=(FIFO_32B&HDLC_FIFO_SIZE_MASK)<<HDLC_FIFO_SIZE_SHIFT; + + reg|=(free_logic_ch&HDLC_FIFO_BASE_ADDR_MASK)<< + HDLC_FIFO_BASE_ADDR_SHIFT; + +#if defined(DEBUG_INIT) + log(LOG_INFO, "Setting Timeslot %ld to free logic ch %ld Reg=0x%X\n", + i, free_logic_ch,reg); +#endif + xilinx_write_ctrl_hdlc(card, + i, + XILINX_CONTROL_RAM_ACCESS_BUF, + reg); + + free_ch_used=1; + } + } + + + /* We must check if the free logic has been bound + * to any timeslots */ + if (free_ch_used){ + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Setting Free CH %ld to idle\n", + sc->if_name,free_logic_ch); +#endif + xilinx_delay(1); + + /* Setup the free logic channel as IDLE */ + + sdla_bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); + + reg&=~HDLC_LOGIC_CH_BIT_MASK; + reg&=HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + + sdla_bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(free_logic_ch&HDLC_LOGIC_CH_BIT_MASK))); + + reg=0; + bit_clear((u_int8_t*)®, HDLC_RX_PROT_DISABLE_BIT); + bit_clear((u_int8_t*)®, HDLC_TX_PROT_DISABLE_BIT); + + bit_set((u_int8_t*)®, HDLC_RX_ADDR_RECOGN_DIS_BIT); + + xilinx_write_ctrl_hdlc(card, + sc->first_time_slot, + XILINX_HDLC_CONTROL_REG, + reg); + } + } + + /* Select an HDLC logic channel for configuration */ + sdla_bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); + + reg&=~HDLC_LOGIC_CH_BIT_MASK; + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + + sdla_bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(sc->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); + + reg=0; + + /* HDLC engine is enabled on the above logical channels */ + bit_clear((u_int8_t*)®, HDLC_RX_PROT_DISABLE_BIT); + bit_clear((u_int8_t*)®, HDLC_TX_PROT_DISABLE_BIT); + + bit_set((u_int8_t*)®, HDLC_TX_CHAN_ENABLE_BIT); + bit_set((u_int8_t*)®, HDLC_RX_ADDR_RECOGN_DIS_BIT); + + xilinx_write_ctrl_hdlc(card, + sc->first_time_slot, + XILINX_HDLC_CONTROL_REG, + reg); + + return 0; +} + + +static void xilinx_dev_unconfigure(sdla_t *card, xilinx_softc_t *sc) +{ + u_int32_t reg; + int i, s; + +#if defined(DEBUG_INIT) + log(LOG_DEBUG, "\n-- Unconfigure Xilinx. --\n"); +#endif + + /* Select an HDLC logic channel for configuration */ + if (sc->logic_ch_num != -1){ + + sdla_bus_read_4(card->hw,XILINX_TIMESLOT_HDLC_CHAN_REG, ®); + reg&=~HDLC_LOGIC_CH_BIT_MASK; + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + + sdla_bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(sc->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); + + + reg=0x00020000; + xilinx_write_ctrl_hdlc(card, + sc->first_time_slot, + XILINX_HDLC_CONTROL_REG, + reg); + + + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (bit_test((u_int8_t*)&sc->time_slot_map, i)){ + + sdla_bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); + reg&=~TIMESLOT_BIT_MASK; + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + + /* Select a Timeslot for configuration */ + sdla_bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(i<<TIMESLOT_BIT_SHIFT))); + + + reg=31&CONTROL_RAM_DATA_MASK; + + reg|=(FIFO_32B&HDLC_FIFO_SIZE_MASK)<<HDLC_FIFO_SIZE_SHIFT; + reg|=(31&HDLC_FIFO_BASE_ADDR_MASK)<< + HDLC_FIFO_BASE_ADDR_SHIFT; + +#if defined(DEBUG_INIT) + log(LOG_INFO, "Setting Timeslot %d to logic ch %d Reg=0x%X\n", + i, 31 ,reg); +#endif + xilinx_write_ctrl_hdlc(card, + i, + XILINX_CONTROL_RAM_ACCESS_BUF, + reg); + } + } + + /* Lock to protect the logic ch map to + * sc device array */ + s = splnet(); + free_xilinx_logical_channel_num(card,sc->logic_ch_num); + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (bit_test((u_int8_t*)&sc->time_slot_map, i)){ + --sc->num_of_time_slots; + } + } + free_fifo_baddr_and_size(card,sc); + splx(s); + + sc->logic_ch_num=-1; + + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (bit_test((u_int8_t*)&sc->time_slot_map, i)){ + bit_clear((u_int8_t*)&card->u.xilinx.time_slot_map, i); + } + } + } +} + +#define FIFO_RESET_TIMEOUT_CNT 1000 +#define FIFO_RESET_TIMEOUT_US 10 +static int xilinx_init_rx_dev_fifo(sdla_t *card, xilinx_softc_t *sc, unsigned char wait) +{ + + u_int32_t reg; + u_int32_t dma_descr; + u_int8_t timeout=1; + u_int16_t i; + + /* Clean RX DMA fifo */ + dma_descr=(unsigned long)(sc->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + reg=0; + bit_set((u_int8_t*)®, INIT_DMA_FIFO_CMD_BIT); + +#if defined(DEBUG_INIT) + log(LOG_DEBUG, "%s: Clearing RX Fifo DmaDescr=(0x%X) Reg=(0x%X) (%s)\n", + sc->if_name, + dma_descr,reg, __FUNCTION__); +#endif + + sdla_bus_write_4(card->hw,dma_descr,reg); + + if (wait == WP_WAIT){ + for(i=0;i<FIFO_RESET_TIMEOUT_CNT;i++){ + sdla_bus_read_4(card->hw,dma_descr,®); + if (bit_test((u_int8_t*)®, INIT_DMA_FIFO_CMD_BIT)){ + DELAY(FIFO_RESET_TIMEOUT_US); + continue; + } + timeout=0; + break; + } + +#if defined(DEBUG_INIT) + if (timeout){ + log(LOG_INFO, "%s:%s: Error: Rx fifo reset timedout %u us\n", + card->devname,sc->if_name,i*FIFO_RESET_TIMEOUT_US); + }else{ + log(LOG_INFO, "%s:%s: Rx Fifo reset successful %u us\n", + card->devname,sc->if_name,i*FIFO_RESET_TIMEOUT_US); + } +#endif + }else{ + timeout=0; + } + + return timeout; +} + +static int xilinx_init_tx_dev_fifo(sdla_t *card, xilinx_softc_t *sc, unsigned char wait) +{ + u_int32_t reg; + u_int32_t dma_descr; + u_int8_t timeout=1; + u_int16_t i; + + /* Clean TX DMA fifo */ + dma_descr=(unsigned long)(sc->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + reg=0; + bit_set((u_int8_t*)®, INIT_DMA_FIFO_CMD_BIT); + +#if defined(DEBUG_INIT) + log(LOG_DEBUG, "%s: Clearing TX Fifo DmaDescr=(0x%X) Reg=(0x%X) (%s)\n", + sc->if_name, + dma_descr,reg, __FUNCTION__); +#endif + sdla_bus_write_4(card->hw,dma_descr,reg); + + if (wait == WP_WAIT){ + for(i=0;i<FIFO_RESET_TIMEOUT_CNT;i++){ + sdla_bus_read_4(card->hw,dma_descr,®); + if (bit_test((u_int8_t*)®, INIT_DMA_FIFO_CMD_BIT)){ + DELAY(FIFO_RESET_TIMEOUT_US); + continue; + } + timeout=0; + break; + } + +#if defined(DEBUG_INIT) + if (timeout){ + log(LOG_INFO, "%s:%s: Error: Tx fifo reset timedout %u us\n", + card->devname,sc->if_name,i*FIFO_RESET_TIMEOUT_US); + }else{ + log(LOG_INFO, "%s:%s: Tx Fifo reset successful %u us\n", + card->devname,sc->if_name,i*FIFO_RESET_TIMEOUT_US); + } +#endif + }else{ + timeout=0; + } + + return timeout; +} + + +static void xilinx_dev_enable(sdla_t *card, xilinx_softc_t *sc) +{ + u_int32_t reg; + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Enabling Global Inter Mask !\n",sc->if_name); +#endif + /* Enable Logic Channel Interrupts for DMA and fifo */ + sdla_bus_read_4(card->hw, + XILINX_GLOBAL_INTER_MASK, ®); + bit_set((u_int8_t*)®, sc->logic_ch_num); + + sdla_bus_write_4(card->hw, + XILINX_GLOBAL_INTER_MASK, reg); + + bit_set((u_int8_t*)&card->u.xilinx.active_ch_map, sc->logic_ch_num); +} + +static void xilinx_dev_close(sdla_t *card, xilinx_softc_t *sc) +{ + u_int32_t reg; + unsigned long dma_descr; + int s; + +#if defined(DEBUG_INIT) + log(LOG_DEBUG, "-- Close Xilinx device. --\n"); +#endif + /* Disable Logic Channel Interrupts for DMA and fifo */ + sdla_bus_read_4(card->hw, XILINX_GLOBAL_INTER_MASK, ®); + + bit_clear((u_int8_t*)®, sc->logic_ch_num); + bit_clear((u_int8_t*)&card->u.xilinx.active_ch_map, sc->logic_ch_num); + + /* We are masking the sc interrupt. + * Lock to make sure that the interrupt is + * not running */ + s = splnet(); + sdla_bus_write_4(card->hw, XILINX_GLOBAL_INTER_MASK, reg); + splx(s); + + reg=0; + + /* Select an HDLC logic channel for configuration */ + sdla_bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); + + reg&=~HDLC_LOGIC_CH_BIT_MASK; + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + + sdla_bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(sc->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); + + + reg=0; + xilinx_write_ctrl_hdlc(card, + sc->first_time_slot, + XILINX_HDLC_CONTROL_REG, + reg); + + /* Clear descriptors */ + reg=0; + dma_descr=(sc->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + sdla_bus_write_4(card->hw,dma_descr,reg); + dma_descr=(sc->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + sdla_bus_write_4(card->hw,dma_descr,reg); + + /* FIXME: Cleanp up Tx and Rx buffers */ +} + +static int xilinx_dma_rx(sdla_t *card, xilinx_softc_t *sc) +{ + u_int32_t reg; + unsigned long dma_descr; + unsigned long bus_addr; + wp_rx_element_t *rx_el; + + /* sanity check: make sure that DMA is in ready state */ +#if 0 + dma_descr=(sc->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + sdla_bus_read_4(card->hw,dma_descr, ®); + + if (bit_test((u_int8_t*)®,RxDMA_HI_DMA_GO_READY_BIT)){ + log(LOG_INFO, "%s: Error: RxDMA GO Ready bit set on dma Rx\n", + card->devname); + return -EFAULT; + } +#endif + + if (sc->rx_dma_mbuf){ + log(LOG_INFO, "%s: Critial Error: Rx Dma Buf busy!\n", + sc->if_name); + return -EINVAL; + } + + IF_DEQUEUE(&sc->wp_rx_free_list, sc->rx_dma_mbuf); + + if (!sc->rx_dma_mbuf){ + log(LOG_INFO, "%s: Critical Error no rx dma buf Free=%d Comp=%d!\n", + sc->if_name, + sc->wp_rx_free_list.ifq_len, + sc->wp_rx_complete_list.ifq_len); + + return -ENOMEM; + } + + rx_el = mtod(sc->rx_dma_mbuf, wp_rx_element_t*); + sc->rx_dma_mbuf->m_len = sizeof(wp_rx_element_t); + sc->rx_dma_mbuf->m_pkthdr.len = sc->rx_dma_mbuf->m_len; + memset(rx_el,0,sizeof(wp_rx_element_t)); + + bus_addr = kvtop(mtod(sc->rx_dma_mbuf, caddr_t) + sc->rx_dma_mbuf->m_len); + + if (!bus_addr){ + log(LOG_INFO, "%s: %s Critical error pci_map_single() failed!\n", + sc->if_name,__FUNCTION__); + return -EINVAL; + } + + rx_el->dma_addr=bus_addr; + + /* Write the pointer of the data packet to the + * DMA address register */ + reg=bus_addr; + + /* Set the 32bit alignment of the data length. + * Since we are setting up for rx, set this value + * to Zero */ + reg&=~(RxDMA_LO_ALIGNMENT_BIT_MASK); + + dma_descr=(sc->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_LO; + +#if defined(DEBUG_RX) + log(LOG_INFO,"%s: RxDMA_LO = 0x%X, BusAddr=0x%lX DmaDescr=0x%lX (%s)\n", + card->devname,reg,bus_addr,dma_descr, __FUNCTION__); +#endif + sdla_bus_write_4(card->hw,dma_descr,reg); + + dma_descr=(unsigned long)(sc->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + + reg =0; + + reg|=(sc->dma_mtu>>2)&RxDMA_HI_DMA_DATA_LENGTH_MASK; + +#ifdef TRUE_FIFO_SIZE + reg|=(sc->fifo_size_code&DMA_FIFO_SIZE_MASK)<<DMA_FIFO_SIZE_SHIFT; +#else + + reg|=(HARD_FIFO_CODE&DMA_FIFO_SIZE_MASK)<<DMA_FIFO_SIZE_SHIFT; +#endif + reg|=(sc->fifo_base_addr&DMA_FIFO_BASE_ADDR_MASK)<< + DMA_FIFO_BASE_ADDR_SHIFT; + + bit_set((u_int8_t*)®, RxDMA_HI_DMA_GO_READY_BIT); + +#if defined(DEBUG_RX) + log(LOG_INFO, "%s: RXDMA_HI = 0x%X, BusAddr=0x%lX DmaDescr=0x%lX (%s)\n", + sc->if_name, reg,bus_addr,dma_descr, __FUNCTION__); +#endif + + sdla_bus_write_4(card->hw,dma_descr,reg); + + bit_set((u_int8_t*)&sc->rx_dma, 0); + + return 0; +} + + +static int xilinx_dma_tx (sdla_t *card, xilinx_softc_t *sc) +{ + u_int32_t reg=0; + struct mbuf *m; + unsigned long dma_descr; + unsigned char len_align=0; + int len=0; + +#if defined(DEBUG_TX) + log(LOG_INFO, "------ Setup Tx DMA descriptor. --\n"); +#endif + + if (bit_test((u_int8_t*)&sc->dma_status, TX_BUSY)){ + log(LOG_INFO, "%s: TX_BUSY set (%s:%d)!\n", + sc->if_name, __FUNCTION__,__LINE__); + return -EBUSY; + } + bit_set((u_int8_t*)&sc->dma_status, TX_BUSY); + + + /* Free the previously skb dma mapping. + * In this case the tx interrupt didn't finish + * and we must re-transmit.*/ + + if (sc->tx_dma_addr && sc->tx_dma_len){ + log(LOG_INFO, "%s: Unmaping tx_dma_addr in %s\n", + sc->if_name,__FUNCTION__); + + sc->tx_dma_addr=0; + sc->tx_dma_len=0; + } + + /* Free the previously sent tx packet. To + * minimize tx isr, the previously transmitted + * packet is deallocated here */ + if (sc->tx_dma_mbuf){ + log(LOG_INFO, "%s: Deallocating tx_dma_mbuf in %s\n", + sc->if_name,__FUNCTION__); + m_freem(sc->tx_dma_mbuf); + sc->tx_dma_mbuf=NULL; + } + + + /* check queue pointers befor start transmittion */ + + /* sanity check: make sure that DMA is in ready state */ + dma_descr=(sc->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + +#if defined(DEBUG_TX) + log(LOG_INFO, "%s: sc logic ch=%ld dma_descr=0x%lx set (%s:%d)!\n", + sc->if_name, sc->logic_ch_num,dma_descr, + __FUNCTION__,__LINE__); +#endif + + sdla_bus_read_4(card->hw,dma_descr, ®); + + if (bit_test((u_int8_t*)®, TxDMA_HI_DMA_GO_READY_BIT)){ + log(LOG_INFO, "%s: Error: TxDMA GO Ready bit set on dma Tx 0x%X\n", + card->devname,reg); + bit_clear((u_int8_t*)&sc->dma_status, TX_BUSY); + return -EFAULT; + } + + IF_DEQUEUE(&sc->wp_tx_pending_list, m); + + if (!m){ + bit_clear((u_int8_t*)&sc->dma_status, TX_BUSY); + return -ENOBUFS; + }else{ + + len = m->m_len; + if (len > MAX_XILINX_TX_DMA_SIZE){ + /* FIXME: We need to split this frame into + * multiple parts. For now thought + * just drop it :) */ + log(LOG_INFO, "%s: Tx len %d > %d (MAX TX DMA LEN) (%s:%d)!\n", + sc->if_name, len, MAX_XILINX_TX_DMA_SIZE, + __FUNCTION__,__LINE__); + m_freem(m); + bit_clear((u_int8_t*)&sc->dma_status, TX_BUSY); + return -EINVAL; + } + + sc->tx_dma_addr = kvtop(mtod(m, caddr_t)); + sc->tx_dma_len = len; + } + + if (sc->tx_dma_addr & 0x03){ + log(LOG_INFO, "%s: Error: Tx Ptr not aligned to 32bit boudary!\n", + card->devname); + + if (m){ + m_freem(m); + } + + bit_clear((u_int8_t*)&sc->dma_status, TX_BUSY); + return -EINVAL; + } + + sc->tx_dma_mbuf=m; + + /* WARNING: Do ont use the "skb" pointer from + * here on. The skb pointer might not exist if + * we are in transparent mode */ + + dma_descr=(sc->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_LO; + + /* Write the pointer of the data packet to the + * DMA address register */ + reg=sc->tx_dma_addr; + + /* Set the 32bit alignment of the data length. + * Used to pad the tx packet to the 32 bit + * boundary */ + reg&=~(TxDMA_LO_ALIGNMENT_BIT_MASK); + reg|=(len&0x03); + + if (len&0x03){ + len_align=1; + } + +#if defined(DEBUG_TX) + log(LOG_INFO, "%s: TXDMA_LO=0x%X PhyAddr=0x%lX DmaDescr=0x%lX (%s)\n", + sc->if_name, reg,sc->tx_dma_addr,dma_descr, __FUNCTION__); +#endif + + sdla_bus_write_4(card->hw,dma_descr,reg); + + dma_descr=(sc->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + + reg=0; + reg|=(((len>>2)+len_align)&TxDMA_HI_DMA_DATA_LENGTH_MASK); + +#ifdef TRUE_FIFO_SIZE + reg|=(sc->fifo_size_code&DMA_FIFO_SIZE_MASK)<<DMA_FIFO_SIZE_SHIFT; +#else + + reg|=(HARD_FIFO_CODE&DMA_FIFO_SIZE_MASK)<<DMA_FIFO_SIZE_SHIFT; +#endif + reg|=(sc->fifo_base_addr&DMA_FIFO_BASE_ADDR_MASK)<< + DMA_FIFO_BASE_ADDR_SHIFT; + + /* Only enable the Frame Start/Stop on + * non-transparent hdlc configuration */ + + bit_set((u_int8_t*)®, TxDMA_HI_DMA_FRAME_START_BIT); + bit_set((u_int8_t*)®, TxDMA_HI_DMA_FRAME_END_BIT); + + bit_set((u_int8_t*)®, TxDMA_HI_DMA_GO_READY_BIT); + +#if defined(DEBUG_TX) + log(LOG_INFO, "%s: TXDMA_HI=0x%X DmaDescr=0x%lX (%s)\n", + sc->if_name, reg,dma_descr, __FUNCTION__); +#endif + + sdla_bus_write_4(card->hw,dma_descr,reg); + + return 0; + +} + +static void xilinx_dma_tx_complete (sdla_t *card, xilinx_softc_t *sc) +{ + u_int32_t reg=0; + unsigned long dma_descr; + +#if defined(DEBUG_TX) + log(LOG_INFO, "%s: TX DMA complete\n", card->devname); +#endif + /* DEBUGTX */ +/* sdla_bus_read_4(card->hw,0x78, &tmp1); */ + + dma_descr=(sc->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + sdla_bus_read_4(card->hw,dma_descr, ®); + + if (!sc->tx_dma_mbuf){ + + log(LOG_INFO, "%s: Critical Error: Tx DMA intr: no tx mbuf !\n", + card->devname); + bit_clear((u_int8_t*)&sc->dma_status, TX_BUSY); + return; + + }else{ + sc->tx_dma_addr=0; + sc->tx_dma_len=0; + + + /* Do not free the packet here, + * copy the packet dma info into csum + * field and let the bh handler analyze + * the transmitted packet. + */ + + if (reg & TxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ + + log(LOG_INFO, "%s:%s: PCI Error: 'Retry' exceeds maximum (64k): Reg=0x%X!\n", + card->devname,sc->if_name,reg); + + if (++sc->pci_retry < 3){ + bit_set((u_int8_t*)®, TxDMA_HI_DMA_GO_READY_BIT); + + log(LOG_INFO, "%s: Retry: TXDMA_HI=0x%X DmaDescr=0x%lX (%s)\n", + sc->if_name, reg,dma_descr, __FUNCTION__); + + sdla_bus_write_4(card->hw,dma_descr,reg); + return; + } + } + + sc->pci_retry=0; + sc->tx_dma_mbuf->m_pkthdr.csum = reg; + IF_ENQUEUE(&sc->wp_tx_complete_list, sc->tx_dma_mbuf); + sc->tx_dma_mbuf=NULL; + + bit_clear((u_int8_t*)&sc->dma_status, TX_BUSY); + + xilinx_process_packet(sc); + } + +} + + +static void xilinx_tx_post_complete (sdla_t *card, xilinx_softc_t *sc, struct mbuf *m) +{ + struct ifnet *ifp; + unsigned long reg = m->m_pkthdr.csum; + + WAN_ASSERT1(sc == NULL); + ifp = (struct ifnet*)&sc->common.ifp; + if ((bit_test((u_int8_t*)®, TxDMA_HI_DMA_GO_READY_BIT)) || + (reg & TxDMA_HI_DMA_DATA_LENGTH_MASK) || + (reg & TxDMA_HI_DMA_PCI_ERROR_MASK)){ + +#if defined(DEBUG_TX) + log(LOG_INFO, "%s:%s: Tx DMA Descriptor=0x%lX\n", + card->devname,sc->if_name,reg); +#endif + + /* Checking Tx DMA Go bit. Has to be '0' */ + if (bit_test((u_int8_t*)®, TxDMA_HI_DMA_GO_READY_BIT)){ + log(LOG_INFO, "%s:%s: Error: TxDMA Intr: GO bit set on Tx intr\n", + card->devname,sc->if_name); + } + + if (reg & TxDMA_HI_DMA_DATA_LENGTH_MASK){ + log(LOG_INFO, "%s:%s: Error: TxDMA Length not equal 0 \n", + card->devname,sc->if_name); + } + + /* Checking Tx DMA PCI error status. Has to be '0's */ + if (reg&TxDMA_HI_DMA_PCI_ERROR_MASK){ + + if (reg & TxDMA_HI_DMA_PCI_ERROR_M_ABRT){ + log(LOG_INFO, "%s:%s: Tx Error: Abort from Master: pci fatal error!\n", + card->devname,sc->if_name); + } + if (reg & TxDMA_HI_DMA_PCI_ERROR_T_ABRT){ + log(LOG_INFO, "%s:%s: Tx Error: Abort from Target: pci fatal error!\n", + card->devname,sc->if_name); + } + if (reg & TxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ + log(LOG_INFO, "%s:%s: Tx Warning: PCI Latency Timeout!\n", + card->devname,sc->if_name); + goto tx_post_ok; + } + if (reg & TxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ + log(LOG_INFO, "%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", + card->devname,sc->if_name); + } + } + goto tx_post_exit; + } + +tx_post_ok: + + if (ifp){ + ifp->if_opackets++; + ifp->if_obytes += m->m_len; + } + + /* Indicate that the first tx frame went + * out on the transparent link */ + bit_set((u_int8_t*)&sc->idle_start, 0); + +tx_post_exit: + + if (!xilinx_dma_tx(card,sc)){ + /* If we was able to transmit and the interface is set to OACTIVE + ** remove this flag and let kernel try to transmit. */ + if (ifp->if_flags & IFF_OACTIVE){ + ifp->if_flags &= ~IFF_OACTIVE; + } + } + return; +} + +static void xilinx_dma_rx_complete (sdla_t *card, xilinx_softc_t *sc) +{ + unsigned long dma_descr; + struct mbuf *m; + wp_rx_element_t *rx_el; + + bit_clear((u_int8_t*)&sc->rx_dma, 0); + + if (!sc->rx_dma_mbuf){ + log(LOG_INFO, "%s: Critical Error: rx_dma_mbuf\n",sc->if_name); + return; + } + + rx_el= mtod(sc->rx_dma_mbuf, wp_rx_element_t *); + + /* Reading Rx DMA descriptor information */ + dma_descr=(sc->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_LO; + sdla_bus_read_4(card->hw,dma_descr, &rx_el->align); + rx_el->align&=RxDMA_LO_ALIGNMENT_BIT_MASK; + + dma_descr=(sc->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + sdla_bus_read_4(card->hw,dma_descr, &rx_el->reg); + + rx_el->pkt_error = sc->pkt_error; + sc->pkt_error=0; + +#if defined(DEBUG_RX) + log(LOG_INFO, "%s: RX HI=0x%X LO=0x%X DMA=0x%lX (%s:%d)\n", + sc->if_name,rx_el->reg,rx_el->align,rx_el->dma_addr, + __FUNCTION__,__LINE__); +#endif + + m=sc->rx_dma_mbuf; + sc->rx_dma_mbuf=NULL; + + xilinx_dma_rx(card,sc); + + IF_ENQUEUE(&sc->wp_rx_complete_list, m); + + xilinx_process_packet(sc); + +/* sdla_bus_read_4(card->hw,0x80, &rx_empty); */ +} + + +static void xilinx_rx_post_complete (sdla_t *card, xilinx_softc_t *sc, + struct mbuf *m, + struct mbuf **new_m, + unsigned char *pkt_error) +{ + struct ifnet *ifp; + unsigned int len,data_error = 0; + wp_rx_element_t *rx_el = mtod(m, wp_rx_element_t *); + + WAN_ASSERT1(sc == NULL); + ifp = (struct ifnet*)&sc->common.ifp; /*m->m_pkthdr.rcvif;*/ + +#if defined(DEBUG_RX) + log(LOG_INFO, "%s: RX HI=0x%X LO=0x%X DMA=0x%lX (%s:%d)\n", + sc->if_name, + rx_el->reg, + rx_el->align, + rx_el->dma_addr, + __FUNCTION__,__LINE__); +#endif + rx_el->align&=RxDMA_LO_ALIGNMENT_BIT_MASK; + *pkt_error=0; + *new_m=NULL; + + + /* Checking Rx DMA Go bit. Has to be '0' */ + if (bit_test((u_int8_t*)&rx_el->reg, RxDMA_HI_DMA_GO_READY_BIT)){ + log(LOG_INFO, "%s: Error: RxDMA Intr: GO bit set on Rx intr\n", + card->devname); + ifp->if_ierrors++; + goto rx_comp_error; + } + + /* Checking Rx DMA PCI error status. Has to be '0's */ + if (rx_el->reg&RxDMA_HI_DMA_PCI_ERROR_MASK){ + + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_M_ABRT){ +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: Rx Error: Abort from Master: pci fatal error!\n", + card->devname); +#endif + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_T_ABRT){ +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: Rx Error: Abort from Target: pci fatal error!\n", + card->devname); +#endif + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: Rx Error: No 'DeviceSelect' from target: pci fatal error!\n", + card->devname); +#endif + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", + card->devname); +#endif + } +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: RXDMA PCI ERROR = 0x%x\n", + card->devname,rx_el->reg); +#endif + if (ifp) ifp->if_ierrors++; + goto rx_comp_error; + } + + /* Checking Rx DMA Frame start bit. (information for api) */ + if (!bit_test((u_int8_t*)&rx_el->reg, RxDMA_HI_DMA_FRAME_START_BIT)){ +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: RxDMA Intr: Start flag missing: MTU Mismatch! Reg=0x%X\n", + card->devname,rx_el->reg); +#endif + if (ifp) ifp->if_ierrors++; + goto rx_comp_error; + } + + /* Checking Rx DMA Frame end bit. (information for api) */ + if (!bit_test((u_int8_t*)&rx_el->reg, RxDMA_HI_DMA_FRAME_END_BIT)){ +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: RxDMA Intr: End flag missing: MTU Mismatch! Reg=0x%X\n", + card->devname,rx_el->reg); +#endif + if (ifp) ifp->if_ierrors++; + goto rx_comp_error; + + } else { /* Check CRC error flag only if this is the end of Frame */ + + if (bit_test((u_int8_t*)&rx_el->reg, RxDMA_HI_DMA_CRC_ERROR_BIT)){ +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: RxDMA Intr: CRC Error! Reg=0x%X\n", + card->devname,rx_el->reg); +#endif + if (ifp) ifp->if_ierrors++; + bit_set((u_int8_t*)&rx_el->pkt_error, WP_CRC_ERROR_BIT); + data_error = 1; + } + + /* Check if this frame is an abort, if it is + * drop it and continue receiving */ + if (bit_test((u_int8_t*)&rx_el->reg, RxDMA_HI_DMA_FRAME_ABORT_BIT)){ +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: RxDMA Intr: Abort! Reg=0x%X\n", + card->devname,rx_el->reg); +#endif + if (ifp) ifp->if_ierrors++; + bit_set((u_int8_t*)&rx_el->pkt_error, WP_ABORT_ERROR_BIT); + data_error = 1; + } + + if (data_error){ + goto rx_comp_error; + } + } + + len=rx_el->reg&RxDMA_HI_DMA_DATA_LENGTH_MASK; + + /* In HDLC mode, calculate rx length based + * on alignment value, received from DMA */ + len=(((sc->dma_mtu>>2)-len)<<2) - (~(rx_el->align)&RxDMA_LO_ALIGNMENT_BIT_MASK); + + *pkt_error=rx_el->pkt_error; + + /* After a RX FIFO overflow, we must mark max 7 + * subsequent frames since firmware, cannot + * guarantee the contents of the fifo */ + + if (bit_test((u_int8_t*)&rx_el->pkt_error, WP_FIFO_ERROR_BIT)){ + if (++sc->rx_fifo_err_cnt >= WP_MAX_FIFO_FRAMES){ + sc->rx_fifo_err_cnt=0; + } + bit_set((u_int8_t*)pkt_error, WP_FIFO_ERROR_BIT); + }else{ + if (sc->rx_fifo_err_cnt){ + if (++sc->rx_fifo_err_cnt >= WP_MAX_FIFO_FRAMES){ + sc->rx_fifo_err_cnt=0; + } + bit_set((u_int8_t*)pkt_error, WP_FIFO_ERROR_BIT); + } + } + + if (len > aft_rx_copyback){ + /* The rx size is big enough, thus + * send this buffer up the stack + * and allocate another one */ +#if 0 + memset(&skb->cb[0],0,sizeof(wp_rx_element_t)); +#endif + memset(mtod(m, caddr_t),0,sizeof(wp_rx_element_t)); + m_adj(m, sizeof(wp_rx_element_t)); + m->m_len += len; + m->m_pkthdr.len = m->m_len; + *new_m=m; + + aft_alloc_rx_dma_buff(card,sc,1); + }else{ + struct mbuf *m0; + /* The rx packet is very + * small thus, allocate a new + * buffer and pass it up */ + m0 = wan_mbuf_alloc(); + if (m0 == NULL){ + log(LOG_INFO, "%s: Failed to allocate mbuf!\n", + sc->if_name); + if (ifp) ifp->if_ierrors++; + goto rx_comp_error; + } + + m0->m_len = m0->m_pkthdr.len = len; + memcpy(mtod(m0, caddr_t), mtod(m, caddr_t) + m->m_len, len); + *new_m = m0; + aft_init_requeue_free_m(sc, m); + } + + return; + +rx_comp_error: + + aft_init_requeue_free_m(sc, m); + return; +} + + +static char request_xilinx_logical_channel_num (sdla_t *card, xilinx_softc_t *sc, long *free_ch) +{ + char logic_ch=-1, free_logic_ch=-1; + int i,err; + + *free_ch=-1; + +#if defined(DEBUG_INIT) + log(LOG_INFO, "-- Request_Xilinx_logic_channel_num:--\n"); + log(LOG_INFO, "%s: Global Num Timeslots=%d Global Logic ch Map 0x%lX (%s:%d)\n", + sc->if_name, + card->u.xilinx.num_of_time_slots, + card->u.xilinx.logic_ch_map, + __FUNCTION__,__LINE__); +#endif + + err=request_fifo_baddr_and_size(card,sc); + if (err){ + return -1; + } + + + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (!bit_test((u_int8_t*)&card->u.xilinx.logic_ch_map, i)){ + bit_set((u_int8_t*)&card->u.xilinx.logic_ch_map, i); + logic_ch=i; + break; + } + } + + if (logic_ch == -1){ + return logic_ch; + } + + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (!bit_test((u_int8_t*)&card->u.xilinx.logic_ch_map, i)){ + free_logic_ch=HDLC_FREE_LOGIC_CH; + break; + } + } + + if (card->u.xilinx.dev_to_ch_map[(unsigned char)logic_ch]){ + log(LOG_INFO, "%s: Error, request logical ch=%d map busy\n", + card->devname,logic_ch); + return -1; + } + + *free_ch=free_logic_ch; + + card->u.xilinx.dev_to_ch_map[(unsigned char)logic_ch]=(void*)sc; + + if (logic_ch > card->u.xilinx.top_logic_ch){ + card->u.xilinx.top_logic_ch=logic_ch; + xilinx_dma_max_logic_ch(card); + } + + + return logic_ch; +} + +static void free_xilinx_logical_channel_num (sdla_t *card, int logic_ch) +{ + bit_clear ((u_int8_t*)&card->u.xilinx.logic_ch_map, logic_ch); + card->u.xilinx.dev_to_ch_map[logic_ch]=NULL; + + if (logic_ch >= card->u.xilinx.top_logic_ch){ + int i; + + card->u.xilinx.top_logic_ch=XILINX_DEFLT_ACTIVE_CH; + + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (card->u.xilinx.dev_to_ch_map[logic_ch]){ + card->u.xilinx.top_logic_ch=i; + } + } + + xilinx_dma_max_logic_ch(card); + } + +} + +static void xilinx_dma_max_logic_ch(sdla_t *card) +{ + u_int32_t reg; + +#if defined(DEBUG_INIT) + log(LOG_INFO, "-- Xilinx_dma_max_logic_ch :--\n"); +#endif + + sdla_bus_read_4(card->hw,XILINX_DMA_CONTROL_REG, ®); + + /* Set up the current highest active logic channel */ + + reg&=DMA_ACTIVE_CHANNEL_BIT_MASK; + reg|=(card->u.xilinx.top_logic_ch << DMA_ACTIVE_CHANNEL_BIT_SHIFT); + + sdla_bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); +} + +static int aft_init_requeue_free_m(xilinx_softc_t *sc, struct mbuf *m) +{ + int err; + + m->m_data = (m->m_flags & M_EXT) ? m->m_ext.ext_buf : m->m_pktdat; + m->m_pkthdr.len = m->m_len = 0; + + memset(mtod(m, caddr_t),0,sizeof(wp_rx_element_t)); + IF_ENQUEUE(&sc->wp_rx_free_list, m); + return err; +} + +static int aft_alloc_rx_dma_buff(sdla_t *card, xilinx_softc_t *sc, int num) +{ + int i; + struct mbuf *m; + + for (i=0;i<num;i++){ + m = wan_mbuf_alloc(); + if (m == NULL){ + log(LOG_INFO, "%s: %s no memory\n", + sc->if_name,__FUNCTION__); + return -ENOMEM; + } + IF_ENQUEUE(&sc->wp_rx_free_list, m); + } + + return 0; +} + + + + +/*============================================================================ + * Enable timer interrupt + */ +static void enable_timer (void* card_id) +{ + sdla_t *card = (sdla_t*)card_id; + int s; + + s = splnet(); + sdla_te_polling(card); + splx(s); + return; +} + +static void xilinx_process_packet(xilinx_softc_t *sc) +{ + struct ifnet *ifp; + struct mbuf *new_m, *m; + unsigned char pkt_error; + + WAN_ASSERT1(sc == NULL); + for(;;){ + IF_DEQUEUE(&sc->wp_rx_complete_list, m); + if (m == NULL) + break; + + new_m=NULL; + pkt_error=0; + + /* The post function will take care + * of the skb and new_skb buffer. + * If new_skb buffer exists, driver + * must pass it up the stack, or free it */ + xilinx_rx_post_complete (sc->common.card, sc, + m, + &new_m, + &pkt_error); + if (new_m){ + + ifp = (struct ifnet*)&sc->common.ifp; +#if defined(DEBUG_RX) + log(LOG_INFO, "%s: Receiving packet %d bytes!\n", + ifp->if_xname, new_m->m_len); +#endif + wanpipe_generic_input(ifp, new_m); + } + } + + for(;;){ + IF_DEQUEUE(&sc->wp_tx_complete_list, m); + if (m == NULL) + break; + xilinx_tx_post_complete (sc->common.card,sc,m); + m_freem(m); + } + + return; +} + +static int fifo_error_interrupt(sdla_t *card, unsigned long reg) +{ + u_int32_t rx_status, tx_status; + u_int32_t err=0; + u_int32_t i; + xilinx_softc_t *sc; + +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s: Fifo error interrupt!\n", + card->devname); +#endif + + /* Clear HDLC pending registers */ + sdla_bus_read_4(card->hw,XILINX_HDLC_TX_INTR_PENDING_REG,&tx_status); + sdla_bus_read_4(card->hw,XILINX_HDLC_RX_INTR_PENDING_REG,&rx_status); + + if (card->state != WAN_CONNECTED){ + log(LOG_INFO, "%s: Warning: Ignoring Error Intr: link disc!\n", + card->devname); + return 0; + } + + tx_status&=card->u.xilinx.active_ch_map; + rx_status&=card->u.xilinx.active_ch_map; + + if (tx_status != 0){ + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (bit_test((u_int8_t*)&tx_status, i) && bit_test((u_int8_t*)&card->u.xilinx.logic_ch_map, i)){ + struct ifnet *ifp; + + sc=(xilinx_softc_t*)card->u.xilinx.dev_to_ch_map[i]; + if (!sc){ + log(LOG_INFO, "Warning: ignoring tx error intr: no dev!\n"); + continue; + } + + ifp = (struct ifnet*)&sc->common.ifp; +#if 0 + if (!(ifp->if_flags & IFF_UP)){ + log(LOG_INFO, "%s: Warning: ignoring tx error intr: dev down 0x%X UP=0x%X!\n", + ifp->if_xname, + sc->common.state, + sc->ignore_modem); + continue; + } +#endif + + if (card->state != WAN_CONNECTED){ + log(LOG_INFO, "%s: Warning: ignoring tx error intr: dev disc!\n", + ifp->if_xname); + continue; + } + +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s:%s: Warning TX Fifo Error on LogicCh=%ld Slot=%d!\n", + card->devname,sc->if_name,sc->logic_ch_num,i); +#endif + xilinx_tx_fifo_under_recover(card,sc); + err=-EINVAL; + } + } + } + + + if (rx_status != 0){ + for (i=0;i<card->u.xilinx.num_of_time_slots;i++){ + if (bit_test((u_int8_t*)&rx_status, i) && bit_test((u_int8_t*)&card->u.xilinx.logic_ch_map, i)){ + struct ifnet *ifp; + sc=(xilinx_softc_t*)card->u.xilinx.dev_to_ch_map[i]; + if (!sc){ + continue; + } + + ifp = (struct ifnet*)&sc->common.ifp; +#if 0 + if (!(ifp->if_flags & IFF_UP)){ + log(LOG_INFO, "%s: Warning: ignoring rx error intr: dev down 0x%X UP=0x%X!\n", + ifp->if_xname, + sc->common.state, + sc->ignore_modem); + continue; + } +#endif + + if (card->state != WAN_CONNECTED){ + log(LOG_INFO, "%s: Warning: ignoring rx error intr: dev disc!\n", + ifp->if_xname); + continue; + } + +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s:%s: Warning RX Fifo Error on LCh=%ld Slot=%d RxCL=%d RxFL=%d RxDMA=%d\n", + card->devname, + sc->if_name, + sc->logic_ch_num, + i, + sc->wp_rx_complete_list.ifq_len, + sc->wp_rx_free_list.ifq_len, + sc->rx_dma); +#endif + +#if 0 +{ + unsigned long dma_descr; + unsigned int reg; + dma_descr=(sc->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + sdla_bus_read_4(card->hw, dma_descr, ®); + log(LOG_INFO, "%s: Hi Descriptor 0x%X\n",sc->if_name,reg); +} +#endif + + bit_set((u_int8_t*)&sc->pkt_error, WP_FIFO_ERROR_BIT); + + err=-EINVAL; + } + } + } + + return err; +} + + +static void front_end_interrupt(sdla_t *card, unsigned long reg) +{ + sdla_te_intr(card); + handle_front_end_state(card); + return; +} + +/**SECTION*************************************************************** + * + * HARDWARE Interrupt Handlers + * + ***********************************************************************/ + + +/*============================================================================ + * wan_xilinx_isr + * + * Main interrupt service routine. + * Determin the interrupt received and handle it. + * + */ +static void wp_xilinx_isr (sdla_t* card) +{ + int i; + u_int32_t reg; + u_int32_t dma_tx_reg,dma_rx_reg; + xilinx_softc_t *sc; + + if (bit_test((u_int8_t*)&card->critical, CARD_DOWN)){ + log(LOG_INFO, "%s: Card down, ignoring interrupt !!!!!!!!\n", + card->devname); + return; + } + + + bit_set((u_int8_t*)&card->in_isr, 0); + +/* write_cpld(card,LED_CONTROL_REG,0x0F);*/ + + /* -----------------2/6/2003 9:02AM------------------ + * Disable all chip Interrupts (offset 0x040) + * -- "Transmit/Receive DMA Engine" interrupt disable + * -- "FiFo/Line Abort Error" interrupt disable + * --------------------------------------------------*/ + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); + + if (bit_test((u_int8_t*)®, SECURITY_STATUS_FLAG)){ + log(LOG_INFO, "%s: Critical: Chip Security Compromised!\n", + card->devname); + log(LOG_INFO, "%s: Disabling Driver!\n", + card->devname); + log(LOG_INFO, "%s: Please call Sangoma Tech Suppor!\n", + card->devname); + log(LOG_INFO, "%s: (www.sangoma.com)!\n", + card->devname); + + port_set_state(card,WAN_DISCONNECTED); + disable_data_error_intr(card,CARD_DOWN); + goto isr_end; + } + + /* Note: If interrupts are received without pending + ** flags, it usually indicates that the interrupt + ** is being shared. (Check 'cat /proc/interrupts') */ + if (bit_test((u_int8_t*)®, FRONT_END_INTR_ENABLE_BIT)){ + if (bit_test((u_int8_t*)®, FRONT_END_INTR_FLAG)){ + front_end_interrupt(card,reg); + if (card->u.xilinx.state_change_exit_isr){ + card->u.xilinx.state_change_exit_isr=0; + /* The state change occured, skip all + ** other interrupts */ + goto isr_end; + } + } + } + + /* Test Fifo Error Interrupt, + ** If set shutdown all interfaces and + ** reconfigure */ + if (bit_test((u_int8_t*)®, ERROR_INTR_ENABLE_BIT)){ + if (bit_test((u_int8_t*)®, ERROR_INTR_FLAG)){ + fifo_error_interrupt(card,reg); + } + } + + /* -----------------2/6/2003 9:37AM------------------ + ** Checking for Interrupt source: + ** 1. Receive DMA Engine + ** 2. Transmit DMA Engine + ** 3. Error conditions. + ** --------------------------------------------------*/ + if (bit_test((u_int8_t*)®, GLOBAL_INTR_ENABLE_BIT) && + bit_test((u_int8_t*)®, DMA_INTR_FLAG)){ + + + /* Receive DMA Engine */ + sdla_bus_read_4(card->hw, + XILINX_DMA_RX_INTR_PENDING_REG, + &dma_rx_reg); + + dma_rx_reg&=card->u.xilinx.active_ch_map; + + if (dma_rx_reg == 0){ + goto isr_rx; + } + + for (i=0; i<card->u.xilinx.num_of_time_slots ;i++){ + if (bit_test((u_int8_t*)&dma_rx_reg, i) && + bit_test((u_int8_t*)&card->u.xilinx.logic_ch_map, i)){ + sc=(xilinx_softc_t*)card->u.xilinx.dev_to_ch_map[i]; + if (!sc){ + log(LOG_INFO, "%s: Error: No Dev for Rx logical ch=%d\n", + card->devname,i); + continue; + } + + xilinx_dma_rx_complete(card,sc); + } + } +isr_rx: + + /* Transmit DMA Engine */ + sdla_bus_read_4(card->hw, + XILINX_DMA_TX_INTR_PENDING_REG, + &dma_tx_reg); + + dma_tx_reg&=card->u.xilinx.active_ch_map; + + if (dma_tx_reg == 0){ + goto isr_tx; + } + + for (i=0; i<card->u.xilinx.num_of_time_slots ;i++){ + if (bit_test((u_int8_t*)&dma_tx_reg, i) && + bit_test((u_int8_t*)&card->u.xilinx.logic_ch_map, i)){ + sc=(xilinx_softc_t*)card->u.xilinx.dev_to_ch_map[i]; + if (!sc){ + log(LOG_INFO, "%s: Error: No Dev for Tx logical ch=%d\n", + card->devname,i); + continue; + } + + xilinx_dma_tx_complete(card,sc); + } + } + } + +isr_tx: + + /* -----------------2/6/2003 10:36AM----------------- + * Finish of the interupt handler + * --------------------------------------------------*/ +isr_end: + +/* write_cpld(card,LED_CONTROL_REG,0x0E); */ + + bit_clear((u_int8_t*)&card->in_isr, 0); + return; +} + + +/**SECTION************************************************************* + * + * TASK Functions and Triggers + * + **********************************************************************/ + + +/*============================================================================ + * port_set_state + * + * Set PORT state. + * + */ +static void port_set_state (sdla_t *card, int state) +{ + wanpipe_common_t *common; + + if (card->state != state){ + switch (state){ + case WAN_CONNECTED: + log(LOG_INFO, "%s: Link connected!\n", + card->devname); + aft_red_led_ctrl(card,AFT_LED_OFF); + aft_green_led_ctrl(card,AFT_LED_ON); + break; + + case WAN_CONNECTING: + log(LOG_INFO, "%s: Link connecting...\n", + card->devname); + aft_red_led_ctrl(card,AFT_LED_ON); + aft_green_led_ctrl(card,AFT_LED_OFF); + break; + + case WAN_DISCONNECTED: + log(LOG_INFO, "%s: Link disconnected!\n", + card->devname); + aft_red_led_ctrl(card,AFT_LED_ON); + aft_green_led_ctrl(card,AFT_LED_OFF); + break; + } + card->state = state; + LIST_FOREACH(common, &card->dev_head, next){ + struct ifnet* ifp = (struct ifnet*)&common->ifp; + if (ifp){ + set_chan_state(card, ifp, state); + } + } + } +} + + +/*============================================================ + * handle_front_end_state + * + * + */ + +static void handle_front_end_state(void *card_id) +{ + sdla_t* card = (sdla_t*)card_id; + + if (card->front_end_status == FE_CONNECTED){ + enable_data_error_intr(card); + port_set_state(card,WAN_CONNECTED); + card->u.xilinx.state_change_exit_isr=1; + }else{ + port_set_state(card,WAN_CONNECTING); + disable_data_error_intr(card,LINK_DOWN); + card->u.xilinx.state_change_exit_isr=1; + } +} + +static unsigned char read_cpld(sdla_t *card, unsigned short cpld_off) +{ + u_int16_t org_off; + u_int8_t tmp; + + cpld_off &= ~BIT_DEV_ADDR_CLEAR; + cpld_off |= BIT_DEV_ADDR_CPLD; + + /* Save the current address. */ + sdla_bus_read_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + &org_off); + + sdla_bus_write_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + cpld_off); + + sdla_bus_read_1(card->hw,XILINX_MCPU_INTERFACE, &tmp); + + /* Restore original address */ + sdla_bus_write_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + org_off); + return tmp; +} + +static unsigned char +write_cpld(sdla_t *card, unsigned short off,unsigned char data) +{ + u_int16_t org_off; + + off &= ~BIT_DEV_ADDR_CLEAR; + off |= BIT_DEV_ADDR_CPLD; + + /* Save the current original address */ + sdla_bus_read_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + &org_off); + + sdla_bus_write_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + off); + + /* This delay is required to avoid bridge optimization + * (combining two writes together)*/ + DELAY(5); + + sdla_bus_write_1(card->hw, + XILINX_MCPU_INTERFACE, + data); + + /* This delay is required to avoid bridge optimization + * (combining two writes together)*/ + DELAY(5); + + /* Restore the original address */ + sdla_bus_write_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + org_off); + return 0; +} + +static unsigned char +write_front_end_reg(void* card1, unsigned short off, unsigned char value) +{ + sdla_t* card = (sdla_t*)card1; + + off &= ~BIT_DEV_ADDR_CLEAR; + sdla_bus_write_2(card->hw,XILINX_MCPU_INTERFACE_ADDR, off); + /* AF: Sep 10, 2003 + * IMPORTANT + * This delays are required to avoid bridge optimization + * (combining two writes together) + */ + DELAY(5); + sdla_bus_write_1(card->hw,XILINX_MCPU_INTERFACE, value); + DELAY(5); + + return 0; +} + + +/*======================================================================= + * Read TE1/56K Front end registers + */ +static unsigned char read_front_end_reg (void* card1, unsigned short off) +{ + sdla_t* card = (sdla_t*)card1; + u_int8_t tmp; + + off &= ~BIT_DEV_ADDR_CLEAR; + sdla_bus_write_2(card->hw, XILINX_MCPU_INTERFACE_ADDR, off); + sdla_bus_read_1(card->hw,XILINX_MCPU_INTERFACE, &tmp); + DELAY(5); + + return tmp; +} + + +/*========================================= + * enable_data_error_intr + * + * Description: + * + * Run only after the front end comes + * up from down state. + * + * Clean the DMA Tx/Rx pending interrupts. + * (Ignore since we will reconfigure + * all dma descriptors. DMA controler + * was already disabled on link down) + * + * For all channels clean Tx/Rx Fifo + * + * Enable DMA controler + * (This starts the fifo cleaning + * process) + * + * For all channels reprogram Tx/Rx DMA + * descriptors. + * + * Clean the Tx/Rx Error pending interrupts. + * (Since dma fifo's are now empty) + * + * Enable global DMA and Error interrutps. + * + */ + +static void enable_data_error_intr(sdla_t *card) +{ + wanpipe_common_t *common; + struct ifnet *ifp; + u_int32_t reg; + + /* Clean Tx/Rx DMA interrupts */ + sdla_bus_read_4(card->hw, + XILINX_DMA_RX_INTR_PENDING_REG, ®); + sdla_bus_read_4(card->hw, + XILINX_DMA_TX_INTR_PENDING_REG, ®); + + + /* For all channels clean Tx/Rx fifos */ + LIST_FOREACH(common, &card->dev_head, next){ + xilinx_softc_t *sc; + + ifp = (struct ifnet*)&common->ifp; + if (!ifp || !ifp->if_softc) + continue; + sc = ifp->if_softc; +#if 0 + if (!(ifp->if_flags & IFF_UP)){ + continue; + } +#endif + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Init interface fifo no wait %s\n", + sc->if_name, __FUNCTION__); +#endif + xilinx_init_rx_dev_fifo(card, sc, WP_NO_WAIT); + xilinx_init_tx_dev_fifo(card, sc, WP_NO_WAIT); + } + + /* Enable DMA controler, in order to start the + ** fifo cleaning */ + sdla_bus_read_4(card->hw,XILINX_DMA_CONTROL_REG,®); + bit_set((u_int8_t*)®, DMA_ENGINE_ENABLE_BIT); + sdla_bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); + + /* For all channels clean Tx/Rx fifos */ + LIST_FOREACH(common, &card->dev_head, next){ + xilinx_softc_t *sc; + + ifp = (struct ifnet*)&common->ifp; + if (!ifp || ifp->if_softc == NULL) + continue; + sc = ifp->if_softc; +#if 0 + if (!(ifp->if_flags & IFF_UP)){ + continue; + } +#endif + + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Init interface fifo %s\n", + sc->if_name, __FUNCTION__); +#endif + + xilinx_init_rx_dev_fifo(card, sc, WP_WAIT); + xilinx_init_tx_dev_fifo(card, sc, WP_WAIT); + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Clearing Fifo and idle_flag %s\n", + card->devname,sc->if_name); +#endif + bit_clear((u_int8_t*)&sc->idle_start, 0); + } + + /* For all channels, reprogram Tx/Rx DMA descriptors. + * For Tx also make sure that the BUSY flag is clear + * and previoulsy Tx packet is deallocated */ + + LIST_FOREACH(common, &card->dev_head, next){ + xilinx_softc_t *sc; + + ifp = (struct ifnet*)&common->ifp; + if (!ifp || !ifp->if_softc) + continue; + sc = ifp->if_softc; +#if 0 + if (!(ifp->if_flags & IFF_UP)){ + continue; + } +#endif + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Init interface %s\n", + sc->if_name, __FUNCTION__); +#endif + + if (sc->rx_dma_mbuf){ + wp_rx_element_t *rx_el; + struct mbuf *m=sc->rx_dma_mbuf; + + sc->rx_dma_mbuf=NULL; + rx_el = mtod(m, wp_rx_element_t *); + aft_init_requeue_free_m(sc, m); + } + + xilinx_dma_rx(card,sc); + + if (sc->tx_dma_addr && sc->tx_dma_len){ + sc->tx_dma_addr=0; + sc->tx_dma_len=0; + } + + if (sc->tx_dma_mbuf){ + m_freem(sc->tx_dma_mbuf); + sc->tx_dma_mbuf=NULL; + } + + bit_clear((u_int8_t*)&sc->dma_status, TX_BUSY); + bit_clear((u_int8_t*)&sc->idle_start, 0); + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Clearing Fifo and idle_flag %s\n", + card->devname,sc->if_name); +#endif + } + + /* Clean Tx/Rx Error interrupts, since fifos are now + * empty, and Tx fifo may generate an underrun which + * we want to ignore :) */ + sdla_bus_read_4(card->hw, + XILINX_HDLC_RX_INTR_PENDING_REG, ®); + sdla_bus_read_4(card->hw, + XILINX_HDLC_TX_INTR_PENDING_REG, ®); + + /* Enable Global DMA and Error Interrupts */ + + reg=0; + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); + bit_set((u_int8_t*)®, GLOBAL_INTR_ENABLE_BIT); + bit_set((u_int8_t*)®, ERROR_INTR_ENABLE_BIT); + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + + return; +} + +static void disable_data_error_intr(sdla_t *card, unsigned char event) +{ + u_int32_t reg; + + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); + bit_clear((u_int8_t*)®, GLOBAL_INTR_ENABLE_BIT); + bit_clear((u_int8_t*)®, ERROR_INTR_ENABLE_BIT); + if (event==DEVICE_DOWN){ + bit_clear((u_int8_t*)®, FRONT_END_INTR_ENABLE_BIT); + } + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + + sdla_bus_read_4(card->hw,XILINX_DMA_CONTROL_REG,®); + bit_clear((u_int8_t*)®, DMA_ENGINE_ENABLE_BIT); + sdla_bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); + +} + +static void xilinx_init_tx_dma_descr(sdla_t *card, xilinx_softc_t *sc) +{ + unsigned long dma_descr; + unsigned long reg=0; + + dma_descr=(sc->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + sdla_bus_write_4(card->hw,dma_descr, reg); +} + + + +static void xilinx_tx_fifo_under_recover (sdla_t *card, xilinx_softc_t *sc) +{ + struct ifnet *ifp = (struct ifnet*)&sc->common.ifp; + u_int32_t reg=0; + unsigned long dma_descr; + +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s:%s: Tx Fifo Recovery \n", + card->devname,sc->if_name); +#endif + + /* Initialize Tx DMA descriptor: Stop DMA */ + dma_descr=(sc->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + sdla_bus_write_4(card->hw,dma_descr, reg); + + /* Clean the TX FIFO */ + xilinx_init_tx_dev_fifo(card, sc, WP_WAIT); + if (sc->tx_dma_addr && sc->tx_dma_len){ + sc->tx_dma_addr=0; + sc->tx_dma_len=0; + } + + /* Requeue the current tx packet, for + ** re-transmission */ + if (sc->tx_dma_mbuf){ + IF_PREPEND(&sc->wp_tx_pending_list, + (struct mbuf*)sc->tx_dma_mbuf); + sc->tx_dma_mbuf=NULL; + } + + /* Wake up the stack, because tx dma interrupt + ** failed */ + if (ifp) ifp->if_oerrors++; + +#if defined(DEBUG_ERR) + log(LOG_INFO, "%s:%s: Tx Fifo Recovery: Restarting Transmission \n", + card->devname,sc->if_name); +#endif + + /* Re-start transmission */ + bit_clear((u_int8_t*)&sc->dma_status, TX_BUSY); + if (!xilinx_dma_tx(card,sc)){ + /* If we was able to transmit and the interface is set + ** to OACTIVE remove this flag and let kernel try to + ** transmit. */ + if (ifp->if_flags & IFF_OACTIVE){ + ifp->if_flags &= ~IFF_OACTIVE; + } + } + return; +} + +static int +xilinx_write_ctrl_hdlc(sdla_t *card, u_int32_t timeslot, u_int8_t reg_off, u_int32_t data) +{ + u_int32_t reg; + u_int32_t ts_orig=timeslot; + unsigned long timeout=ticks; + + if (timeslot == 0){ + timeslot=card->u.xilinx.num_of_time_slots-2; + }else if (timeslot == 1){ + timeslot=card->u.xilinx.num_of_time_slots-1; + }else{ + timeslot-=2; + } + + timeslot=timeslot<<XILINX_CURRENT_TIMESLOT_SHIFT; + timeslot&=XILINX_CURRENT_TIMESLOT_MASK; + + for (;;){ + sdla_bus_read_4(card->hw,XILINX_TIMESLOT_HDLC_CHAN_REG,®); + reg&=XILINX_CURRENT_TIMESLOT_MASK; + + if (reg == timeslot){ + sdla_bus_write_4(card->hw,reg_off,data); + return 0; + } + + if ((ticks-timeout) > 1){ + log(LOG_INFO, + "%s: Error: Access to timeslot %d timed out!\n", + card->devname,ts_orig); + return -EIO; + } + } + + return -EIO; +} + +static int set_chan_state(sdla_t* card, struct ifnet* ifp, int state) +{ + xilinx_softc_t *sc = ifp->if_softc; + + if (sc == NULL){ + return 0; + } + if (state == WAN_CONNECTED){ +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Setting idle_start to 0\n", + sc->if_name); +#endif + bit_clear((u_int8_t*)&sc->idle_start, 0); + } + + return 0; +} + + +static char fifo_size_vector[] = {1, 2, 4, 8, 16, 32}; +static char fifo_code_vector[] = {0, 1, 3, 7,0xF,0x1F}; + +static int request_fifo_baddr_and_size(sdla_t *card, xilinx_softc_t *sc) +{ + unsigned char req_fifo_size,fifo_size; + int i; + + /* Calculate the optimal fifo size based + * on the number of time slots requested */ + + if (IS_T1(&card->fe_te.te_cfg)){ + + if (sc->num_of_time_slots == NUM_OF_T1_CHANNELS){ + req_fifo_size=32; + }else if (sc->num_of_time_slots == 1){ + req_fifo_size=1; + }else if (sc->num_of_time_slots == 2 || + sc->num_of_time_slots == 3){ + req_fifo_size=2; + }else if (sc->num_of_time_slots >= 4 && + sc->num_of_time_slots<= 7){ + req_fifo_size=4; + }else if (sc->num_of_time_slots >= 8 && + sc->num_of_time_slots<= 15){ + req_fifo_size=8; + }else if (sc->num_of_time_slots >= 16 && + sc->num_of_time_slots<= 23){ + req_fifo_size=16; + }else{ + log(LOG_INFO, "%s: Invalid number of timeslots %d\n", + card->devname, + sc->num_of_time_slots); + return -EINVAL; + } + }else{ + if (sc->num_of_time_slots == (NUM_OF_E1_CHANNELS-1)){ + req_fifo_size=32; + }else if (sc->num_of_time_slots == 1){ + req_fifo_size=1; + }else if (sc->num_of_time_slots == 2 || + sc->num_of_time_slots == 3){ + req_fifo_size=2; + }else if (sc->num_of_time_slots >= 4 && + sc->num_of_time_slots <= 7){ + req_fifo_size=4; + }else if (sc->num_of_time_slots >= 8 && + sc->num_of_time_slots <= 15){ + req_fifo_size=8; + }else if (sc->num_of_time_slots >= 16 && + sc->num_of_time_slots <= 31){ + req_fifo_size=16; + }else{ + log(LOG_INFO, + "%s:%s: Invalid number of timeslots %d\n", + card->devname, + sc->if_name, + sc->num_of_time_slots); + return -EINVAL; + } + } + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s:%s: Optimal Fifo Size =%d Timeslots=%d \n", + card->devname, + sc->if_name, + req_fifo_size, + sc->num_of_time_slots); +#endif + fifo_size=map_fifo_baddr_and_size(card, + req_fifo_size, + &sc->fifo_base_addr); + if (fifo_size == 0 || sc->fifo_base_addr == 31){ + log(LOG_INFO, + "%s:%s: Error: Failed to obtain fifo size %d or addr %d\n", + card->devname, + sc->if_name, + fifo_size, + sc->fifo_base_addr); + return -EINVAL; + } + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s:%s: Optimal Fifo Size =%d TS=%d New Fifo Size=%d\n", + card->devname, + sc->if_name, + req_fifo_size, + sc->num_of_time_slots, + fifo_size); +#endif + + for (i=0;i<sizeof(fifo_size_vector);i++){ + if (fifo_size_vector[i] == fifo_size){ + sc->fifo_size_code=fifo_code_vector[i]; + break; + } + } + + if (fifo_size != req_fifo_size){ + log(LOG_INFO, + "%s:%s: WARN: Failed to obtain the req fifo %d got %d\n", + card->devname, + sc->if_name, + req_fifo_size, + fifo_size); + } + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: %s:Fifo Size=%d TS=%d Fifo Code=%d Addr=%d\n", + card->devname,sc->if_name,fifo_size, + sc->num_of_time_slots,sc->fifo_size_code, + sc->fifo_base_addr); +#endif + sc->fifo_size = fifo_size; + + return 0; +} + + +static int +map_fifo_baddr_and_size(sdla_t *card, unsigned char fifo_size, unsigned char *addr) +{ + u_int32_t reg=0; + int i; + + for (i=0;i<fifo_size;i++){ + bit_set((u_int8_t*)®, i); + } + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Trying to MAP 0x%X to 0x%lX\n", + card->devname,reg,card->u.xilinx.fifo_addr_map); +#endif + for (i=0;i<32;i+=fifo_size){ + if (card->u.xilinx.fifo_addr_map & (reg<<i)){ + continue; + } + card->u.xilinx.fifo_addr_map |= reg<<i; + *addr=i; + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Card fifo Map 0x%lX Addr =%d\n", + card->devname,card->u.xilinx.fifo_addr_map,i); +#endif + return fifo_size; + } + + if (fifo_size == 1){ + return 0; + } + + fifo_size = fifo_size >> 1; + + return map_fifo_baddr_and_size(card,fifo_size,addr); +} + + +static int free_fifo_baddr_and_size (sdla_t *card, xilinx_softc_t *sc) +{ + u_int32_t reg=0; + int i; + + for (i=0;i<sc->fifo_size;i++){ + bit_set((u_int8_t*)®, i); + } + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: Unmapping 0x%X from 0x%lX\n", + card->devname, + reg<<sc->fifo_base_addr, + card->u.xilinx.fifo_addr_map); +#endif + card->u.xilinx.fifo_addr_map &= ~(reg<<sc->fifo_base_addr); + +#if defined(DEBUG_INIT) + log(LOG_INFO, "%s: New Map is 0x%lX\n", + card->devname, card->u.xilinx.fifo_addr_map); +#endif + + sc->fifo_size=0; + sc->fifo_base_addr=0; + + return 0; +} + +static void aft_red_led_ctrl(sdla_t *card, int mode) +{ + unsigned int led; + + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG, &led); + + if (mode == AFT_LED_ON){ + bit_clear((u_int8_t*)&led, XILINX_RED_LED); + }else if (mode == AFT_LED_OFF){ + bit_set((u_int8_t*)&led, XILINX_RED_LED); + }else{ + if (bit_test((u_int8_t*)&led, XILINX_RED_LED)){ + bit_clear((u_int8_t*)&led, XILINX_RED_LED); + }else{ + bit_set((u_int8_t*)&led, XILINX_RED_LED); + } + } + + sdla_bus_write_4(card->hw,XILINX_CHIP_CFG_REG, led); +} + +static void aft_led_timer(void *data) +{ + sdla_t *card=(sdla_t*)data; + unsigned int te_alarm; + + if (bit_test((u_int8_t*)&card->critical, CARD_DOWN)){ + return; + } + + if (IS_TE1(&card->fe_te.te_cfg)) { + int s = splnet(); + + te_alarm = sdla_te_alarm(card, 0); + te_alarm&=~(BIT_OOSMF_ALARM|BIT_OOCMF_ALARM); + + if (!te_alarm){ + if (card->state == WAN_CONNECTED){ + aft_red_led_ctrl(card, AFT_LED_OFF); + aft_green_led_ctrl(card, AFT_LED_ON); + }else{ + aft_red_led_ctrl(card, AFT_LED_OFF); + aft_green_led_ctrl(card, AFT_LED_TOGGLE); + } + + }else if (te_alarm & (BIT_RED_ALARM|BIT_LOS_ALARM)){ + /* Red or LOS Alarm solid RED */ + aft_red_led_ctrl(card, AFT_LED_ON); + aft_green_led_ctrl(card, AFT_LED_OFF); + }else if (te_alarm & BIT_OOF_ALARM){ + /* OOF Alarm flashing RED */ + aft_red_led_ctrl(card, AFT_LED_TOGGLE); + aft_green_led_ctrl(card, AFT_LED_OFF); + }else if (te_alarm & BIT_AIS_ALARM){ + /* AIS - Blue Alarm flasing RED and GREEN */ + aft_red_led_ctrl(card, AFT_LED_TOGGLE); + aft_green_led_ctrl(card, AFT_LED_TOGGLE); + }else if (te_alarm & BIT_YEL_ALARM){ + /* Yellow Alarm */ + aft_red_led_ctrl(card, AFT_LED_ON); + aft_green_led_ctrl(card, AFT_LED_ON); + }else{ + + /* Default case shouldn't happen */ + log(LOG_INFO, "%s: Unknown Alarm 0x%X\n", + card->devname,te_alarm); + aft_red_led_ctrl(card, AFT_LED_ON); + aft_green_led_ctrl(card, AFT_LED_ON); + } + + splx(s); + timeout_add(&card->u.xilinx.led_timer,hz); + } +} + + +int aft_core_ready(sdla_t *card) +{ + u_int32_t reg; + volatile unsigned char cnt=0; + + for (;;){ + sdla_bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); + if (!bit_test((u_int8_t*)®, HDLC_CORE_READY_FLAG_BIT)){ + /* The HDLC Core is not ready! we have + ** an error. */ + if (++cnt > 5){ + return -EINVAL; + }else{ + DELAY(500); + /* WARNING: we cannot do this while in + * critical area */ + } + }else{ + return 0; + } + } + + return -EINVAL; +} + + +/****** End ****************************************************************/ |