3834 lines
109 KiB
C
3834 lines
109 KiB
C
/***************************************************************************
|
||
* sdla_bri.c WANPIPE(tm)
|
||
* ISDN-BRI support module for "Cologne XHFC-2SU" chip.
|
||
*
|
||
* Author(s): David Rokhvarg <davidr@sangoma.com>
|
||
*
|
||
* Copyright: (c) 1984 - 2007 Sangoma Technologies Inc.
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License
|
||
* as published by the Free Software Foundation; either version
|
||
* 2 of the License, or (at your option) any later version.
|
||
*
|
||
* ============================================================================
|
||
* March 12, 2007 David Rokhvarg
|
||
* v1.0 Initial version.
|
||
*
|
||
* February 26, 2008 David Rokhvarg
|
||
* v1.1 Imrovements in SU State transition code.
|
||
* Re-implemented T3 and T4 timers.
|
||
*
|
||
* March 10, 2008 David Rokhvarg
|
||
* v1.2 Added 'loopback' commands.
|
||
* Added commands to switch between line recovery
|
||
* clock and internal oscillator clock.
|
||
*
|
||
* April 1, 2008 David Rokhvarg
|
||
* v1.3 In TE mode, when in F3 state, Do NOT
|
||
* indicate 'disconnect' right away, do it when
|
||
* T4 expires.
|
||
*
|
||
* July 21 2009 David Rokhvarg
|
||
* v1.4 Implemented T1 timer for NT.
|
||
*
|
||
* January 24 2011 David Rokhvarg
|
||
* v1.5 The T3 timer was broken by changes in AFT Core - fixed it
|
||
* by adding T3_TIMER_EXPIRED bit.
|
||
* The T3_TIMER_EXPIRED bit resolved "failed TE activation"
|
||
* issue on some BRI lines.
|
||
******************************************************************************
|
||
*/
|
||
|
||
/*******************************************************************************
|
||
** INCLUDE FILES
|
||
*******************************************************************************/
|
||
|
||
# include "wanpipe_includes.h"
|
||
# include "wanpipe_defines.h"
|
||
# include "wanpipe_debug.h"
|
||
# include "wanpipe_abstr.h"
|
||
# include "wanpipe_common.h"
|
||
# include "wanpipe_events.h"
|
||
# include "wanpipe.h"
|
||
# include "wanpipe_events.h"
|
||
# include "if_wanpipe_common.h" /* for 'wanpipe_common_t' used in 'aft_core.h'*/
|
||
# include "aft_core.h" /* for 'private_area_t' */
|
||
# include "sdla_bri.h"
|
||
|
||
|
||
/* DEBUG macro definitions */
|
||
#define DEBUG_HFC_INIT if(0)DEBUG_EVENT
|
||
#define DEBUG_HFC_MODE if(0)DEBUG_EVENT
|
||
#define DEBUG_HFC_IRQ if(0)DEBUG_EVENT
|
||
#define DEBUG_HFC_SU_IRQ if(0)DEBUG_EVENT
|
||
|
||
#define DEBUG_HFC_TX if(0)DEBUG_EVENT
|
||
#define DEBUG_TX_DATA if(0)DEBUG_EVENT
|
||
#define TX_EXTRA_DBG if(0)DEBUG_EVENT
|
||
#define TX_FAST_DBG if(0)DEBUG_EVENT
|
||
|
||
#define DEBUG_HFC_RX if(0)DEBUG_EVENT
|
||
#define RX_EXTRA_DBG if(0)DEBUG_EVENT
|
||
#define DEBUG_RX1 if(0)DEBUG_EVENT
|
||
#define DBG_RX_DATA if(0)DEBUG_EVENT
|
||
|
||
#define DEBUG_HFC_CLOCK if(0)DEBUG_EVENT
|
||
#define DBG_MODULE_TESTER if(0)DEBUG_EVENT
|
||
|
||
#define NT_STATE_FUNC() if(0)DEBUG_EVENT("%s(): line: %d\n", __FUNCTION__, __LINE__)
|
||
#define CLOCK_FUNC() if(0)DEBUG_EVENT("%s(): line: %d\n", __FUNCTION__, __LINE__)
|
||
|
||
#define DBG_SPI if(0)DEBUG_EVENT
|
||
|
||
#define DEBUG_FE_STATUS if(0)DEBUG_EVENT
|
||
|
||
#define DEBUG_LOOPB if(0)DEBUG_EVENT
|
||
|
||
#define DEBUG_TE_STATES if(0)DEBUG_EVENT
|
||
|
||
#define FIFO_THRESHOLD_INDEX 1
|
||
|
||
typedef enum _nt_states{
|
||
NT_STATE_RESET_G0 = 0,
|
||
NT_STATE_DEACTIVATED_G1,
|
||
NT_STATE_PENDING_ACTIVATION_G2,
|
||
NT_STATE_ACTIVE_G3,
|
||
NT_STATE_PENDING_DEACTIVATION_G4
|
||
}nt_states_t;
|
||
|
||
typedef enum _te_states{
|
||
TE_STATE_RESET_F0 = 0,
|
||
TE_STATE_SENSING_F2 = 2,
|
||
TE_STATE_DEACTIVATED_F3 = 3,
|
||
TE_STATE_AWAITING_SIGNAL_F4 = 4,
|
||
TE_STATE_IDENTIFYING_INPUT_F5 = 5,
|
||
TE_STATE_SYNCHRONIZED_F6 = 6,
|
||
TE_STATE_ACTIVATED_F7 = 7,
|
||
TE_STATE_LOST_FRAMING_F8 = 8
|
||
}te_states_t;
|
||
|
||
#define WP_DECODE_TE_STATE(te_state) \
|
||
(te_state == TE_STATE_RESET_F0) ? "TE_STATE_RESET_F0" : \
|
||
(te_state == TE_STATE_SENSING_F2) ? "TE_STATE_SENSING_F2" : \
|
||
(te_state == TE_STATE_DEACTIVATED_F3) ? "TE_STATE_DEACTIVATED_F3" : \
|
||
(te_state == TE_STATE_AWAITING_SIGNAL_F4) ? "TE_STATE_AWAITING_SIGNAL_F4" : \
|
||
(te_state == TE_STATE_IDENTIFYING_INPUT_F5) ? "TE_STATE_IDENTIFYING_INPUT_F5" : \
|
||
(te_state == TE_STATE_SYNCHRONIZED_F6) ? "TE_STATE_SYNCHRONIZED_F6" : \
|
||
(te_state == TE_STATE_ACTIVATED_F7) ? "TE_STATE_ACTIVATED_F7" : \
|
||
(te_state == TE_STATE_LOST_FRAMING_F8) ? "TE_STATE_LOST_FRAMING_F8" : \
|
||
"Invalid TE State"
|
||
|
||
|
||
#define CHECK_DATA 0
|
||
#if CHECK_DATA
|
||
static void dump_chip_SRAM(sdla_fe_t *fe, u8 mod_no, u8 port_no);
|
||
static void dump_data(u8 *data, int data_len);
|
||
static int check_data(u8 *data, int data_len);
|
||
#endif
|
||
|
||
typedef enum _wp_bri_critical_bits {
|
||
WP_BCB_BRI_CONFIG,
|
||
WP_BCB_BRI_POST_INIT
|
||
} wp_bri_critical_bits_t;
|
||
|
||
/*******************************************************************************
|
||
** DEFINES AND MACROS
|
||
*******************************************************************************/
|
||
#define REPORT_MOD_NO(mod_no) (mod_no+1)
|
||
|
||
static u8 validate_fe_line_no(sdla_fe_t *fe, const char *caller_name)
|
||
{
|
||
if ((int32_t)WAN_FE_LINENO(fe) < 0 || WAN_FE_LINENO(fe) > MAX_BRI_LINES){
|
||
DEBUG_EVENT("%s(): %s: ISDN BRI: Invalid FE line number %d (Min=1 Max=%d)\n",
|
||
caller_name, fe->name, WAN_FE_LINENO(fe)+1, MAX_BRI_LINES);
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int32_t validate_physical_mod_no(u32 mod_no, const char *caller_name)
|
||
{
|
||
if(mod_no % 2){
|
||
DEBUG_ERROR("%s(): Error: mod_no (%d) is not divisible by 2!!\n",
|
||
caller_name, mod_no);
|
||
return 1;
|
||
}
|
||
|
||
if(mod_no >= MAX_BRI_LINES){
|
||
DEBUG_ERROR("%s(): Error: mod_no (%d) is greate than maximum of %d!!\n",
|
||
caller_name, mod_no, MAX_BRI_LINES - 1);
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Translate FE_LINENO to physical module number divisible by BRI_MAX_PORTS_PER_CHIP. */
|
||
static u8 fe_line_no_to_physical_mod_no(sdla_fe_t *fe)
|
||
{
|
||
u8 mod_no;
|
||
|
||
mod_no = (u8)WAN_FE_LINENO(fe);
|
||
/* get quotient between 0 and 11 (including) */
|
||
mod_no = mod_no / BRI_MAX_PORTS_PER_CHIP;
|
||
/* here WAN_FE_LINENO(fe) is translated into an EVEN number between 0 and 22 (including). */
|
||
mod_no *= BRI_MAX_PORTS_PER_CHIP;
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return 0;
|
||
}
|
||
|
||
return mod_no;
|
||
}
|
||
|
||
/* Translate FE_LINENO to port number on the module. can be only 0 or 1. */
|
||
static u8 fe_line_no_to_port_no(sdla_fe_t *fe)
|
||
{
|
||
return WAN_FE_LINENO(fe) % BRI_MAX_PORTS_PER_CHIP;
|
||
}
|
||
|
||
|
||
/*******************************************************************************/
|
||
/* SPI access functions and dbg macros */
|
||
|
||
static
|
||
__u8 _read_xhfc(sdla_fe_t *fe, u32 mod_no, u8 reg, const char *caller_name, int32_t file_lineno);
|
||
|
||
static
|
||
int32_t _write_xhfc(sdla_fe_t *fe, u32 mod_no, u8 reg, u8 val,
|
||
const char *caller_name, int32_t file_lineno);
|
||
|
||
/* Read/Write to front-end register */
|
||
#define WRITE_REG(reg,val) _write_xhfc(fe, mod_no, reg, val, __FUNCTION__, __LINE__)
|
||
#define READ_REG(reg) _read_xhfc(fe, mod_no, reg, __FUNCTION__, __LINE__)
|
||
|
||
/*******************************************************************************/
|
||
|
||
|
||
/*******************************************************************************
|
||
** STRUCTURES AND TYPEDEFS
|
||
*******************************************************************************/
|
||
|
||
|
||
/*******************************************************************************
|
||
** GLOBAL VARIABLES
|
||
*******************************************************************************/
|
||
#if !defined(__WINDOWS__)
|
||
extern WAN_LIST_HEAD(, wan_tdmv_) wan_tdmv_head;
|
||
#endif
|
||
|
||
|
||
/*******************************************************************************
|
||
** FUNCTION PROTOTYPES
|
||
*******************************************************************************/
|
||
static int32_t bri_global_config(void* pfe);
|
||
static int32_t bri_global_unconfig(void* pfe);
|
||
|
||
static int32_t wp_bri_config(void *pfe);
|
||
static int32_t wp_bri_post_init(void *pfe);
|
||
|
||
static int32_t wp_bri_unconfig(void *pfe);
|
||
static int32_t wp_bri_post_unconfig(void* pfe);
|
||
|
||
static int32_t wp_bri_if_config(void *pfe, u32 mod_map, u8);
|
||
static int32_t wp_bri_if_unconfig(void *pfe, u32 mod_map, u8);
|
||
|
||
static int32_t wp_bri_disable_irq(sdla_fe_t *fe, u32 mod_no, u8 port_no);
|
||
static int wp_bri_disable_fe_irq(void *fe);
|
||
static void bri_enable_interrupts(sdla_fe_t *fe, u32 mod_no, u8 port_no);
|
||
static int32_t wp_bri_intr(sdla_fe_t *);
|
||
static int32_t wp_bri_check_intr(sdla_fe_t *);
|
||
static int32_t wp_bri_polling(sdla_fe_t*);
|
||
static int32_t wp_bri_udp(sdla_fe_t*, void*, u8*);
|
||
static u32 wp_bri_active_map(sdla_fe_t* fe, u8 line_no);
|
||
static u8 wp_bri_fe_media(sdla_fe_t *fe);
|
||
static int32_t wp_bri_set_dtmf(sdla_fe_t*, int32_t, u8);
|
||
|
||
static int wp_bri_intr_ctrl(sdla_fe_t *fe, int, u_int8_t, u_int8_t, unsigned int);
|
||
static int wp_bri_event_ctrl(sdla_fe_t*, wan_event_ctrl_t*);
|
||
|
||
static int32_t wp_bri_dchan_tx(sdla_fe_t *fe, void *src_data_buffer, u32 buffer_len);
|
||
|
||
static void *wp_bri_dchan_rx(sdla_fe_t *fe, u8 mod_no, u8 port_no, u32 * pending_data);
|
||
|
||
static int wp_bri_get_fe_status(sdla_fe_t *fe, unsigned char *status, int notused);
|
||
static int wp_bri_set_fe_status(sdla_fe_t *fe, unsigned char status);
|
||
|
||
static int wp_bri_control(sdla_fe_t *fe, u32 command);
|
||
|
||
/*******************************************************************************/
|
||
/* TE timer routines */
|
||
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||
static void l1_timer_expire_t3(void* pfe);
|
||
static void l1_timer_expire_t4(void* pfe);
|
||
#elif defined(__WINDOWS__)
|
||
static void l1_timer_expire_t3(IN PKDPC Dpc, void* pfe, void* arg2, void* arg3);
|
||
static void l1_timer_expire_t4(IN PKDPC Dpc, void* pfe, void* arg2, void* arg3);
|
||
#elif defined(KERN_TIMER_SETUP) && KERN_TIMER_SETUP > 0
|
||
static void l1_timer_expire_t3(struct timer_list *t);
|
||
static void l1_timer_expire_t4(struct timer_list *t);
|
||
#else
|
||
static void l1_timer_expire_t3(unsigned long pfe);
|
||
static void l1_timer_expire_t4(unsigned long pfe);
|
||
#endif
|
||
|
||
static void l1_timer_start_t3(void *pport);
|
||
static void l1_timer_stop_t3(void *pport);
|
||
static void l1_timer_start_t4(void *pport);
|
||
static void l1_timer_stop_t4(void *pport);
|
||
|
||
static void __l1_timer_expire_t3(sdla_fe_t *fe);
|
||
|
||
/* NT timer routines */
|
||
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||
static void l1_timer_expire_t1(void* pfe);
|
||
#elif defined(__WINDOWS__)
|
||
static void l1_timer_expire_t1(IN PKDPC Dpc, void* pfe, void* arg2, void* arg3);
|
||
#elif defined(KERN_TIMER_SETUP) && KERN_TIMER_SETUP > 0
|
||
static void l1_timer_expire_t1(struct timer_list *t);
|
||
#else
|
||
static void l1_timer_expire_t1(unsigned long pfe);
|
||
#endif
|
||
|
||
static void l1_timer_start_t1(void *pport);
|
||
static void l1_timer_stop_t1(void *pport);
|
||
static void __l1_timer_expire_t1(sdla_fe_t *fe);
|
||
|
||
/*******************************************************************************/
|
||
|
||
static int32_t wp_bri_spi_bus_reset(sdla_fe_t *fe);
|
||
static int32_t reset_chip(sdla_fe_t *fe, u32 mod_no);
|
||
static int32_t init_xfhc(sdla_fe_t *fe, u32 mod_no);
|
||
static void xhfc_select_fifo(sdla_fe_t *fe, u32 mod_no, u8 fifo);
|
||
static void xhfc_waitbusy(sdla_fe_t *fe, u32 mod_no);
|
||
static void xhfc_select_pcm_slot(sdla_fe_t *fe, u32 mod_no, u8 slot, u8 direction);
|
||
static void xhfc_increment_fifo(sdla_fe_t *fe, u32 mod_no);
|
||
|
||
static int32_t __config_clock_routing(sdla_fe_t *fe, u32 mod_no, u8 master_mode);
|
||
static int32_t config_clock_routing(sdla_fe_t *fe, u8 master_mode);
|
||
|
||
static void xhfc_ph_command(sdla_fe_t *fe, bri_xhfc_port_t *port, u_char command);
|
||
|
||
static u8 __su_new_state(sdla_fe_t *fe, u8 mod_no, u8 port_no);
|
||
static void sdla_bri_set_status(sdla_fe_t* fe, u8 mod_no, u8 port_no, u8 status);
|
||
|
||
/* for selecting PCM direction */
|
||
#define XHFC_DIRECTION_TX 0
|
||
#define XHFC_DIRECTION_RX 1
|
||
|
||
#if 0
|
||
static void xhfc_select_xhfc_channel(sdla_fe_t *fe, u32 mod_no,
|
||
u8 channel, u8 direction, u8 vrout_bitmap);
|
||
#endif
|
||
|
||
static int32_t check_f0cl_increment(sdla_fe_t *fe, u8 old_f0cl, u8 new_f0cl, int32_t *diff);
|
||
|
||
/*******************************************************************************
|
||
** FUNCTION DEFINITIONS
|
||
*******************************************************************************/
|
||
|
||
|
||
/*******************************************************************************/
|
||
|
||
static
|
||
u8 _read_xhfc(sdla_fe_t *fe, u32 mod_no, u8 reg, const char *caller_name, int32_t file_lineno)
|
||
{
|
||
u8 val = READ_BRI_REG(mod_no, reg);
|
||
return val;
|
||
}
|
||
|
||
static
|
||
int32_t _write_xhfc(sdla_fe_t *fe, u32 mod_no, u8 reg, u8 val, const char *caller_name, int32_t file_lineno)
|
||
{
|
||
return WRITE_BRI_REG(mod_no, reg, val);
|
||
}
|
||
|
||
/*******************************************************************************/
|
||
|
||
static void xhfc_waitbusy(sdla_fe_t *fe, u32 mod_no)
|
||
{
|
||
u32 wait_counter = 0;
|
||
#define MAX_XHFC_WAIT_COUNTER 200
|
||
|
||
do{
|
||
if(!(READ_REG(R_STATUS) & M_BUSY)){
|
||
break;
|
||
}
|
||
WP_DELAY(10);
|
||
}while(wait_counter++ < MAX_XHFC_WAIT_COUNTER);
|
||
|
||
if(wait_counter >= MAX_XHFC_WAIT_COUNTER){
|
||
DEBUG_EVENT("%s: %s() time out!\n", fe->name, __FUNCTION__);
|
||
}
|
||
}
|
||
|
||
static inline void xhfc_resetfifo_flag(sdla_fe_t *fe, u32 mod_no, u8 flag)
|
||
{
|
||
WRITE_REG(A_INC_RES_FIFO, flag);
|
||
xhfc_waitbusy(fe, mod_no);
|
||
}
|
||
|
||
static inline void xhfc_resetfifo(sdla_fe_t *fe, u32 mod_no)
|
||
{
|
||
return xhfc_resetfifo_flag(fe, mod_no,M_RES_FIFO | M_RES_FIFO_ERR);
|
||
}
|
||
|
||
static void xhfc_select_fifo(sdla_fe_t *fe, u32 mod_no, u8 fifo)
|
||
{
|
||
WRITE_REG(R_FIFO, fifo);
|
||
xhfc_waitbusy(fe, mod_no);
|
||
}
|
||
|
||
static void xhfc_select_pcm_slot(sdla_fe_t *fe, u32 mod_no, u8 slot, u8 direction)
|
||
{
|
||
reg_r_slot r_slot;
|
||
|
||
memset(&r_slot, 0, sizeof(reg_r_slot));
|
||
|
||
r_slot.bit.v_sl_dir = direction;
|
||
r_slot.bit.v_sl_num = slot;
|
||
|
||
WRITE_REG(R_SLOT, r_slot.reg);
|
||
xhfc_waitbusy(fe, mod_no);
|
||
}
|
||
|
||
#if 0
|
||
static void xhfc_select_xhfc_channel(sdla_fe_t *fe, u32 mod_no,
|
||
u8 channel, u8 direction, u8 vrout_bitmap)
|
||
{
|
||
reg_a_sl_cfg a_sl_cfg;
|
||
memset(&a_sl_cfg, 0, sizeof(reg_a_sl_cfg));
|
||
|
||
a_sl_cfg.bit.v_ch_sdir = direction; /* 1 bit */
|
||
a_sl_cfg.bit.v_ch_snum = channel; /* 5 bits */
|
||
a_sl_cfg.bit.v_rout = vrout_bitmap;
|
||
|
||
WRITE_REG(A_SL_CFG, a_sl_cfg.reg);
|
||
xhfc_waitbusy(fe, mod_no);
|
||
}
|
||
#endif
|
||
|
||
static void xhfc_increment_fifo(sdla_fe_t *fe, u32 mod_no)
|
||
{
|
||
WRITE_REG(A_INC_RES_FIFO, M_INC_F);
|
||
xhfc_waitbusy(fe, mod_no);
|
||
}
|
||
|
||
static u32 calculate_pcm_timeslot(u32 mod_no, u8 port_no, u8 bchan)
|
||
{
|
||
u32 pcm_slot;
|
||
#if 0
|
||
DEBUG_HFC_INIT("port_no: %d, bchan: %d\n", port_no, bchan);
|
||
#endif
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return 0;
|
||
}
|
||
|
||
pcm_slot = 2*port_no + bchan; /* 0, 1, 2, 3 */
|
||
pcm_slot *= 2; /* 0, 2, 4, 6 */
|
||
pcm_slot += (4*mod_no); /* mod_no is 0,2,4.... -->0+0, 0+8, 0+16 */
|
||
|
||
return pcm_slot;
|
||
}
|
||
|
||
static int32_t reset_chip(sdla_fe_t *fe, u32 mod_no)
|
||
{
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
int32_t err = 0, maximum_reset_wait_counter;
|
||
reg_r_fifo_thres r_fifo_thres;
|
||
|
||
DEBUG_HFC_INIT("%s(): mod_no: %d\n", __FUNCTION__, mod_no);
|
||
|
||
WAN_ASSERT(fe->write_fe_reg == NULL);
|
||
WAN_ASSERT(fe->read_fe_reg == NULL);
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return 1;
|
||
}
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
|
||
/* read ChipID from Read Only register R_CHIP_ID */
|
||
fe->fe_chip_id = READ_REG(R_CHIP_ID);
|
||
switch (fe->fe_chip_id)
|
||
{
|
||
case CHIP_ID_2SU:
|
||
DEBUG_EVENT("%s: Detected XHFC-2SU chip.\n", fe->name);
|
||
bri_module->num_ports = 2;
|
||
bri_module->max_fifo = 8;
|
||
bri_module->max_z = 0x7F;
|
||
DBG_MODULE_TESTER("configure FIFOs\n");
|
||
/* 01 = 8 FIFOs with 128 bytes for TX and RX each. page 125 */
|
||
WRITE_REG( R_FIFO_MD, M1_FIFO_MD * 1);
|
||
break;
|
||
default:
|
||
err = 1;
|
||
DEBUG_EVENT("%s: %s(): unknown Chip ID 0x%x!\n",
|
||
fe->name, __FUNCTION__, fe->fe_chip_id);
|
||
return err;
|
||
}
|
||
|
||
/* general soft chip reset */
|
||
WRITE_REG(R_CIRM, M_SRES);
|
||
WP_DELAY(50);
|
||
WRITE_REG(R_CIRM, 0);
|
||
WP_DELAY(2000);
|
||
|
||
/* wait for XHFC init seqeuence to be finished. should take ~40 microseconds (p.285). */
|
||
maximum_reset_wait_counter = 1000;
|
||
while ((READ_REG(R_STATUS) & (M_BUSY | M_PCM_INIT)) && (maximum_reset_wait_counter)){
|
||
WP_DELAY(10);/* 10 microsecond delay */
|
||
maximum_reset_wait_counter--;
|
||
}
|
||
|
||
if (!(maximum_reset_wait_counter)) {
|
||
DEBUG_ERROR("%s: %s(): Error: chip initialization sequence timeout!\n",
|
||
fe->name, __FUNCTION__);
|
||
return 1;
|
||
}
|
||
|
||
/* amplitude */
|
||
WRITE_REG(R_PWM_MD, 0x80);
|
||
WRITE_REG(R_PWM1, 0x18);
|
||
|
||
/* Set FIFO threshold. page 124.*/
|
||
r_fifo_thres.reg = 0;
|
||
r_fifo_thres.bit.v_thres_tx = r_fifo_thres.bit.v_thres_rx = FIFO_THRESHOLD_INDEX;
|
||
WRITE_REG(R_FIFO_THRES, r_fifo_thres.reg);
|
||
|
||
WP_DELAY(2000);
|
||
return 0;
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
* __config_clock_routing()
|
||
*
|
||
* Description : TE mode: if 'master_mode' is set to WANOPT_YES, clock
|
||
* recovered from the line will be routed to ALL other BRI
|
||
* modules on the card.
|
||
* NT mode: the only valid value for 'master_mode' is WANOPT_NO.
|
||
*
|
||
* Arguments : pfe - pointer to Front End structure.
|
||
* mod_no - module number.
|
||
* master_mode - Yes or No
|
||
*
|
||
* Returns : 0 - configred successfully, otherwise non-zero value.
|
||
*******************************************************************************/
|
||
static int32_t __config_clock_routing(sdla_fe_t *fe, u32 mod_no, u8 master_mode)
|
||
{
|
||
reg_r_pcm_md0 pcm_md0;
|
||
reg_r_pcm_md2 r_pcm_md2;
|
||
reg_r_gpio_sel r_gpio_sel;
|
||
reg_r_gpio_en0 r_gpio_en0;
|
||
|
||
DEBUG_HFC_CLOCK("%s(): mod_no: %d\n", __FUNCTION__, mod_no);
|
||
|
||
WAN_ASSERT(fe->write_fe_reg == NULL);
|
||
WAN_ASSERT(fe->read_fe_reg == NULL);
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return 1;
|
||
}
|
||
|
||
if (fe->bri_param.mod[mod_no].type != MOD_TYPE_TE && master_mode) {
|
||
DEBUG_ERROR("%s: Module %d: error configuring clock routing on NT\n",
|
||
fe->name, mod_no);
|
||
return 1;
|
||
}
|
||
|
||
DEBUG_EVENT("%s: %s Clock line recovery Module=%d Port=%d: %s\n",
|
||
fe->name, WP_BRI_DECODE_MOD_TYPE(fe->bri_param.mod[mod_no].type),
|
||
REPORT_MOD_NO(mod_no), fe_line_no_to_port_no(fe)+1,
|
||
(master_mode == WANOPT_YES ? "Enabled" : "Disabled"));
|
||
|
||
|
||
#if 0
|
||
/************************************************************************/
|
||
{
|
||
reg_r_su_sync r_su_sync;
|
||
r_su_sync.reg = 0;
|
||
if(master_mode == WANOPT_YES){
|
||
CLOCK_FUNC();
|
||
/*r_su_sync.bit.v_sync_sel = 0; //000 = source is line interface 0
|
||
r_su_sync.bit.v_man_sync = 1;
|
||
r_su_sync.bit.v_auto_synci = 1;*/
|
||
}else{
|
||
CLOCK_FUNC();
|
||
|
||
}
|
||
WRITE_REG(R_SU_SYNC, r_su_sync.reg);
|
||
}
|
||
#endif
|
||
/************************************************************************/
|
||
r_gpio_sel.reg = 0;
|
||
r_gpio_en0.reg = 0;
|
||
if(master_mode == WANOPT_YES){
|
||
CLOCK_FUNC();
|
||
/* select 1-st function of pin 16 - SYNC_O. page 315. */
|
||
r_gpio_sel.bit.v_gpio_sel6 = 0;
|
||
|
||
/* enable output on pin 16 - page 314.*/
|
||
r_gpio_en0.bit.v_gpio_en6 = 1;
|
||
}else{
|
||
CLOCK_FUNC();
|
||
/* if in NORMAL mode, do NOT provide output on 16,
|
||
so there is only one clock source - the master. */
|
||
|
||
/* select 2-nd function of pin 16 - GPIO6. page 315.*/
|
||
r_gpio_sel.bit.v_gpio_sel6 = 1;
|
||
|
||
/* DISABLE output on pin 16 - page 314. */
|
||
r_gpio_en0.bit.v_gpio_en6 = 0;
|
||
}
|
||
WRITE_REG(R_GPIO_SEL, r_gpio_sel.reg);
|
||
WRITE_REG(R_GPIO_EN0, r_gpio_en0.reg);
|
||
|
||
/************************************************************************/
|
||
pcm_md0.reg = 0;
|
||
pcm_md0.bit.v_pcm_idx = 0xA; /* get access to R_PCM_MD2 */
|
||
#if 0
|
||
/* PCM master mode test */
|
||
pcm_md0.bit.v_pcm_md = 0x1; /* PCM bus mode.
|
||
0 = slave (pins C4IO and F0IO are inputs)
|
||
1 = master (pins C4IO and F0IO are outputs)
|
||
If no external C4IO and F0IO signal is provided
|
||
this bit must be set for operation. */
|
||
#endif
|
||
|
||
WRITE_REG(R_PCM_MD0, pcm_md0.reg);
|
||
|
||
r_pcm_md2.reg = 0;
|
||
if(master_mode == WANOPT_YES){
|
||
CLOCK_FUNC();
|
||
|
||
if(fe->bri_param.use_512khz_recovery_clock == 1){
|
||
DEBUG_EVENT("%s: Module=%d Port=%d: using 512khz from PLL\n",
|
||
fe->name, REPORT_MOD_NO(mod_no), fe_line_no_to_port_no(fe)+1);
|
||
|
||
r_pcm_md2.bit.v_sync_out1 = 1;/* 1 = SYNC_O is either 512 kHz from the PLL or
|
||
the received multiframe / superframe
|
||
synchronization pulse. page 244. */
|
||
}else{
|
||
DEBUG_EVENT("%s: Module=%d Port=%d: SYNC_O -> SYNC_I / Sync Pulse.\n",
|
||
fe->name, REPORT_MOD_NO(mod_no), fe_line_no_to_port_no(fe)+1);
|
||
|
||
r_pcm_md2.bit.v_sync_out1 = 0;/* 0 = SYNC_O is either SYNC_I or the received
|
||
synchronization pulse. page 244. */
|
||
}
|
||
}
|
||
r_pcm_md2.bit.v_sync_out2 = 0;/* SYNC_O output selection
|
||
0 = ST/Up receive from the selected line interface
|
||
in TE mode (see R_SU_SYNC register for synchronization source selection)
|
||
1 = SYNC_I is connected to SYNC_O. page 244. */
|
||
#if 0
|
||
if(master_mode == WANOPT_NO){
|
||
r_pcm_md2.bit.v_sync_src = 1; /*V_SYNC_SRC PCM PLL synchronization source selection
|
||
0 = line interface (see R_SU_SYNC for further
|
||
synchronization configuration)
|
||
1 = SYNC_I input (8 kHz). page 244.
|
||
*/
|
||
}
|
||
#endif
|
||
|
||
WRITE_REG(R_PCM_MD2, r_pcm_md2.reg);
|
||
/************************************************************************/
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int config_clock_routing(sdla_fe_t *fe, u8 master_mode)
|
||
{
|
||
u8 mod_no;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
|
||
return __config_clock_routing(fe, mod_no, master_mode);
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
* init_xfhc()
|
||
*
|
||
* Description : Physical module global configuration. Should be done only one
|
||
* time per-module.
|
||
* Assuming chip was already reset.
|
||
*
|
||
* Arguments : pfe - pointer to Front End structure.
|
||
* mod_no - module number.
|
||
*
|
||
* Returns : 0 - configred successfully, otherwise non-zero value.
|
||
*******************************************************************************/
|
||
static int32_t init_xfhc(sdla_fe_t *fe, u32 mod_no)
|
||
{
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
int32_t err = 0, i;
|
||
|
||
u8 port_no, bchan;
|
||
reg_a_su_ctrl0 a_su_ctrl0;
|
||
reg_a_su_rd_sta a_su_rd_sta;
|
||
|
||
DEBUG_HFC_INIT("%s(): mod_no: %d\n", __FUNCTION__, mod_no);
|
||
|
||
WAN_ASSERT(fe->write_fe_reg == NULL);
|
||
WAN_ASSERT(fe->read_fe_reg == NULL);
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return 1;
|
||
}
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
|
||
DBG_MODULE_TESTER("read ChipID from Read Only register R_CHIP_ID: must be 0x61\n");
|
||
|
||
if((err = reset_chip(fe, mod_no))){
|
||
return err;
|
||
}
|
||
|
||
a_su_ctrl0.reg = 0;
|
||
a_su_rd_sta.reg = 0;
|
||
|
||
/* PCM: 1. Slave mode 2. PCM64 (4MBit/s data rate). */
|
||
/* slow PCM adjust speed */
|
||
bri_module->pcm_md1.bit.v_pll_adj = 3;
|
||
bri_module->pcm_md1.bit.v_pcm_dr = 1; /* dr stands for 'data rate' (64) */
|
||
|
||
WRITE_REG(R_PCM_MD0, bri_module->pcm_md0.reg + 0x90); /* get access to R_PCM_MD1 */
|
||
WRITE_REG(R_PCM_MD1, bri_module->pcm_md1.reg);
|
||
|
||
/* After chip reset SYNC_O is set for OUTPUT by default, make sure
|
||
SYNC_O is set for INPUT! Otherwise may cause clock conflict. */
|
||
__config_clock_routing(fe, mod_no, WANOPT_NO);
|
||
|
||
DEBUG_HFC_INIT("\n%s: configuring B-channels FIFOs...\n", fe->name);
|
||
/* configure B channel fifos for ST<->PCM data flow */
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {/* 2 ports */
|
||
DEBUG_HFC_INIT("=========== port_no: %d ========\n", port_no);
|
||
for (bchan = 0; bchan < 2; bchan++) { /* 2 B channels on each port_no */
|
||
DEBUG_HFC_INIT("port_no: %d, bchan: %d\n", port_no, bchan);
|
||
/* B chan - Tx of port_no */
|
||
xhfc_select_fifo(fe, mod_no, port_no*8 + bchan*2);
|
||
/* ST-->PCM, channel enabled */
|
||
WRITE_REG(A_CON_HDLC, 0xde);/* page 83: Tx, Transparent, ST/U-->PCM */
|
||
/* 64kbit/s */
|
||
WRITE_REG(A_SUBCH_CFG, 0);
|
||
/* no interrupts */
|
||
WRITE_REG(A_FIFO_CTRL, 0);
|
||
|
||
/* B chan - Rx of port_no */
|
||
xhfc_select_fifo(fe, mod_no, port_no*8 + bchan*2 + 1);
|
||
/* ST<--PCM, channel enabled */
|
||
WRITE_REG(A_CON_HDLC, 0xde);/* page 83: Rx, Transparent, ST/U<--PCM */
|
||
/* 64kbit/s */
|
||
WRITE_REG(A_SUBCH_CFG, 0);
|
||
/* no interrupts */
|
||
WRITE_REG(A_FIFO_CTRL, 0);
|
||
}
|
||
}
|
||
DEBUG_HFC_INIT("\nDone\n");
|
||
|
||
DEBUG_HFC_INIT("\nconfiguring D-channel FIFOs...\n");
|
||
/* configure D channel fifos */
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {
|
||
reg_a_con_hdlc a_con_hdlc;
|
||
|
||
a_con_hdlc.reg = 0;
|
||
|
||
a_con_hdlc.bit.v_iff = 1;/* InterFrameFill=ones */
|
||
a_con_hdlc.bit.v_fifo_irq = 1; /* an interrupt is generated */
|
||
|
||
DEBUG_HFC_INIT("=========== port_no: %d ========\n", port_no);
|
||
/* D - Tx of port_no */
|
||
xhfc_select_fifo(fe, mod_no, port_no*8 + 4);
|
||
/* FIFO-->ST, channel enabled, IFF=ones */
|
||
WRITE_REG(A_CON_HDLC, a_con_hdlc.reg);
|
||
/* 16kbit/s */
|
||
WRITE_REG(A_SUBCH_CFG, 2);
|
||
#if 1
|
||
/* interrupts at end of frame only */
|
||
WRITE_REG(A_FIFO_CTRL, 0x1);
|
||
#else
|
||
/* Interrupts at end of frame AND at fifo threshold.
|
||
Will work as 'transmit interrupt'. */
|
||
WRITE_REG(A_FIFO_CTRL, 0x5);
|
||
#endif
|
||
|
||
/* D - Rx of port_no */
|
||
xhfc_select_fifo(fe, mod_no, port_no*8 + 4 + 1);
|
||
/* ST--> FIFO, channel enabled */
|
||
WRITE_REG(A_CON_HDLC, a_con_hdlc.reg);
|
||
/* 16kbit/s */
|
||
WRITE_REG(A_SUBCH_CFG, 2);
|
||
|
||
#if 1
|
||
/* interrupts at end of frame only */
|
||
WRITE_REG(A_FIFO_CTRL, 0x1);
|
||
#else
|
||
/* interrupts at end of frame AND at fifo threshold */
|
||
WRITE_REG(A_FIFO_CTRL, 0x5);
|
||
#endif
|
||
}
|
||
DEBUG_HFC_INIT("\nDone\n");
|
||
|
||
DEBUG_HFC_INIT("Configure PCM time slots\n");
|
||
/* Configure PCM time slots.
|
||
B-chan data will use PCM64 bus. D-chan data will use SPI.
|
||
*/
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {
|
||
for (bchan = 0; bchan < 2; bchan++) {
|
||
|
||
u8 pcm_slot;
|
||
|
||
DEBUG_HFC_INIT("port_no: %d, bchan: %d\n", port_no, bchan);
|
||
|
||
if(mod_no >= MAX_BRI_MODULES){
|
||
/* adjust mod_no to be between 0 and 10 (including)*/
|
||
pcm_slot = (u8)calculate_pcm_timeslot(mod_no - MAX_BRI_MODULES, port_no, bchan);
|
||
/* AFT Line 1 will use odd PCM timeslots */
|
||
pcm_slot += 1;
|
||
}else{
|
||
/* AFT Line 0 will use even PCM timeslots */
|
||
pcm_slot = (u8)calculate_pcm_timeslot(mod_no, port_no, bchan);
|
||
}
|
||
|
||
DEBUG_HFC_INIT("selecting TX pcm_slot: %d\n", pcm_slot);
|
||
|
||
/*****************************************************************************************/
|
||
/* transmit slot - select direction TX */
|
||
xhfc_select_pcm_slot(fe, mod_no, pcm_slot, XHFC_DIRECTION_TX);
|
||
|
||
/* Connect time slot with channel and pin.
|
||
PCM data output on pin STIO2 (+0x40 swap pins)
|
||
0x80+0x40==0xC0==11000000 -> v_rout==0x3 -> "output buffer for STIO2 enabled" page 257.
|
||
0*8 + 0*2 = 0 --> HFC channel 0.
|
||
|
||
Assign HFC channel (from 0 to 15) to the selected PCM slot.
|
||
*/
|
||
WRITE_REG(A_SL_CFG,0x80+0x40+port_no*8+bchan*2); /* assign HFC channel (from 0 to 15)
|
||
to the selected PCM slot. */
|
||
|
||
/*****************************************************************************************/
|
||
/* receive slot - select direction RX */
|
||
DEBUG_HFC_INIT("selecting RX pcm_slot: %d\n", pcm_slot);
|
||
|
||
xhfc_select_pcm_slot(fe, mod_no, pcm_slot, XHFC_DIRECTION_RX);
|
||
|
||
/* Connect time slot with channel and pin.
|
||
PCM data input from pin STIO1 (+0x40 swap pins).
|
||
Assign HFC channel (from 0 to 15) to the selected PCM slot.
|
||
*/
|
||
|
||
WRITE_REG(A_SL_CFG,0x80+0x40+port_no*8+bchan*2+1);/* assign HFC channel (from 0 to 15)
|
||
to the selected PCM slot. */
|
||
}/* for (bchan = 0; bchan < 2; bchan++) */
|
||
}/* for (port_no = 0; port_no < bri_module->num_ports; port_no++) */
|
||
|
||
DBG_MODULE_TESTER("configure ST ports\n");
|
||
/* configure ST ports */
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {
|
||
u8 old_f0cl, new_f0cl;
|
||
int32_t f0cl_diff;
|
||
|
||
DEBUG_HFC_INIT("%s(): configurig ST port_no: %d\n", __FUNCTION__, port_no);
|
||
|
||
WRITE_REG(R_SU_SEL, port_no);
|
||
|
||
switch(bri_module->type)
|
||
{
|
||
case MOD_TYPE_TE:
|
||
WRITE_REG(A_SU_CLK_DLY, CLK_DLY_TE);
|
||
|
||
/* TE, ST B1+B2 tx enabled, end of pulse control enabled */
|
||
WRITE_REG(A_SU_CTRL0, 0x43);
|
||
break;
|
||
|
||
case MOD_TYPE_NT:
|
||
WRITE_REG(A_SU_CLK_DLY, CLK_DLY_NT);
|
||
|
||
/* NT, ST B1+B2 tx enabled, end of pulse control enabled */
|
||
a_su_ctrl0.bit.v_b1_tx_en = 1;
|
||
a_su_ctrl0.bit.v_b2_tx_en = 1;
|
||
a_su_ctrl0.bit.v_su_md = 1;
|
||
WRITE_REG(A_SU_CTRL0, a_su_ctrl0.reg);
|
||
break;
|
||
}
|
||
|
||
/* reset default. TE and NT */
|
||
WRITE_REG(A_SU_CTRL1, 0x0);
|
||
|
||
switch(bri_module->type)
|
||
{
|
||
case MOD_TYPE_NT:
|
||
/* enables automatic transition G2->G3 */
|
||
WRITE_REG(A_SU_CTRL1, M_G2_G3_EN);
|
||
break;
|
||
}
|
||
|
||
/* ST B1+B2 rx enabled */
|
||
WRITE_REG(A_SU_CTRL2, 0x3);
|
||
|
||
/* end of pulse control for layer 1 compliance */
|
||
WRITE_REG(A_ST_CTRL3, 0xf8);
|
||
|
||
/* WRITE_REG(R_SU_SEL, port_no); */
|
||
#if defined(BUILD_MOD_TESTER)
|
||
DBG_MODULE_TESTER("Activate ST port_no state machines (NT only!!)\n");
|
||
/* try to activate port_no */
|
||
switch(bri_module->type)
|
||
{
|
||
case MOD_TYPE_TE:
|
||
/*WRITE_REG(A_SU_WR_STA, STA_ACTIVATE);*/
|
||
break;
|
||
case MOD_TYPE_NT:
|
||
WRITE_REG(A_SU_WR_STA, STA_ACTIVATE | M_SU_SET_G2_G3);
|
||
break;
|
||
}
|
||
#endif
|
||
DBG_MODULE_TESTER("poll R_F0_CNTL to make sure PCM is connected\n");
|
||
|
||
/* poll R_F0_CNTL to make sure PCM is connected */
|
||
for (i = 0; i < 10; i++) {
|
||
|
||
DBG_MODULE_TESTER("get current R_F0_CNTL\n");
|
||
|
||
old_f0cl=READ_REG(R_F0_CNTL);
|
||
DBG_MODULE_TESTER("wait for 10ms - f0cl should be incremented by 80 (+- 10 is ok)\n");
|
||
/* wait for 10ms - f0cl should be incremented by 80 (+- 10 is ok) */
|
||
WP_MDELAY(10);
|
||
|
||
DBG_MODULE_TESTER("get the R_F0_CNTL after the wait\n");
|
||
new_f0cl=READ_REG(R_F0_CNTL);
|
||
|
||
if(check_f0cl_increment(fe, old_f0cl, new_f0cl, &f0cl_diff)){
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
DEBUG_EVENT("%s: Module: %d, PCM 125us pulse ok. (f0cl diff: %d)\n",
|
||
fe->name, REPORT_MOD_NO(mod_no) + port_no, f0cl_diff);
|
||
|
||
}/* for (port_no = 0; port_no < bri_module->num_ports; port_no++) */
|
||
|
||
/* init line interfaces state machines (in software only!) */
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {
|
||
|
||
bri_module->port[port_no].idx = port_no;
|
||
bri_module->port[port_no].hw = bri_module;
|
||
|
||
/*wan_set_bit(HFC_L1_ACTIVATING, &bri_module->port[port_no].l1_flags);*/
|
||
|
||
}/* for (port_no = 0; port_no < bri_module->num_ports; port_no++) */
|
||
|
||
#if defined(BUILD_MOD_TESTER)
|
||
/* for debugging only - read line status */
|
||
WP_MDELAY(300);
|
||
|
||
DBG_MODULE_TESTER("read line status - Connected/Disconnected\n");
|
||
/* read line status - Connected/Disconnected */
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {
|
||
|
||
DBG_MODULE_TESTER("select port_no %d\n", port_no);
|
||
WRITE_REG(R_SU_SEL, port_no);
|
||
|
||
/* poll line status register for around 5ms */
|
||
for (i = 0; i < 5; i++) {
|
||
u8 line_status;
|
||
|
||
DBG_MODULE_TESTER("read ST line status for port_no %d\n", port_no);
|
||
/* read ST line status*/
|
||
a_su_rd_sta.reg = line_status = READ_REG(A_SU_RD_STA);
|
||
|
||
if(bri_module->type == MOD_TYPE_TE){
|
||
DEBUG_HFC_S0_STATES("%d: TE: force F7 - pt:%d line status: 0x%02x, v_su_fr_sync: %d\n",
|
||
i, port_no, line_status, a_su_rd_sta.bit.v_su_fr_sync);
|
||
}else{
|
||
DEBUG_HFC_S0_STATES("%d: NT: force G3 - pt:%d line status: 0x%02x, v_su_fr_sync: %d\n",
|
||
i, port_no, line_status, a_su_rd_sta.bit.v_su_fr_sync);
|
||
}
|
||
|
||
DBG_MODULE_TESTER("a_su_rd_sta.bit.v_su_fr_sync: %d (0-Disconnected, 1-Connected)\n", a_su_rd_sta.bit.v_su_fr_sync);
|
||
|
||
WP_MDELAY(200);
|
||
}
|
||
}
|
||
#endif
|
||
DBG_MODULE_TESTER("%s(): finished\n", __FUNCTION__);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int32_t check_f0cl_increment(sdla_fe_t *fe, u8 old_f0cl, u8 new_f0cl, int32_t *diff)
|
||
{
|
||
*diff = new_f0cl - old_f0cl;
|
||
if(*diff < 0){
|
||
*diff = 255 - old_f0cl;
|
||
*diff += new_f0cl;
|
||
}
|
||
|
||
/* should be between 70 and 90 over 10ms time */
|
||
if(*diff == 0){
|
||
DEBUG_ERROR("%s: PCM ERROR: BRI Modlue NO CLOCK found! 125us pulse f0cl diff: %d\n",
|
||
fe->name, *diff);
|
||
return 1;
|
||
}
|
||
|
||
if(*diff > 150 || *diff < 70){
|
||
DEBUG_WARNING("%s: PCM Warning 125us pulse count f0cl diff: %d\n",
|
||
fe->name, *diff);
|
||
}
|
||
|
||
DBG_MODULE_TESTER("f0cl diff: %d\n", *diff);
|
||
return 0;
|
||
}
|
||
|
||
// these functions are used for making sure indexes are properly read
|
||
|
||
static u8 read_reg_volatile(sdla_fe_t *fe, u8 mod_no, u8 reg)
|
||
{
|
||
const u16 max_num = 128;
|
||
u16 cur_num = max_num+1;
|
||
|
||
u8 res = READ_REG(reg);
|
||
|
||
while (--cur_num)
|
||
{
|
||
const u8 chk = READ_REG(reg);
|
||
|
||
if (res == chk)
|
||
{
|
||
if (cur_num != max_num) {
|
||
DEBUG_RX1("%s: stable 0x%02X (0x%02X) after %u reads\n",
|
||
fe->name, reg, res, max_num - cur_num + 1);
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
res = chk;
|
||
}
|
||
|
||
DEBUG_WARNING("%s: unable to get stable read from 0x%02X (using 0x%02X)\n", fe->name, reg, res);
|
||
return res;
|
||
}
|
||
|
||
#define READ_REG_VOLATILE(reg) \
|
||
read_reg_volatile(fe, mod_no, reg)
|
||
|
||
|
||
typedef enum _DCHAN_RC{
|
||
DCHAN_STATUS_BUSY = 1,
|
||
DCHAN_STATUS_COMPLETE,
|
||
DCHAN_STATUS_INCOMPLETE,
|
||
DCHAN_STATUS_EMPTY
|
||
}DCHAN_RC;
|
||
|
||
#define TX_EMPTY_FIFO 1
|
||
|
||
static u8 xhfc_write_fifo_dchan(sdla_fe_t *fe, u8 mod_no,
|
||
wp_bri_module_t *bri_module, bri_xhfc_port_t *port,
|
||
u8 *free_space)
|
||
{
|
||
u8 *buf = NULL;
|
||
int *len = NULL,
|
||
*idx = NULL;
|
||
|
||
u8 fcnt, tcnt, i;
|
||
u8 f1, f2;
|
||
u8 z1, z2;
|
||
|
||
reg_a_fifo_sta fstat;
|
||
|
||
u8 *data;
|
||
u8 rc;
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
private_area_t *chan=NULL;
|
||
|
||
u8 fifo_free;
|
||
u8 fifo_usage;
|
||
|
||
buf = port->dtxbuf; /* data buffer */
|
||
len = &port->bytes2transmit; /* hdlc packet len */
|
||
idx = &port->dtx_indx; /* already transmitted */
|
||
|
||
DEBUG_HFC_TX("%s(): *len: %d\n", __FUNCTION__, *len);
|
||
|
||
/* select the D-channel TX fifo */
|
||
xhfc_select_fifo(fe, mod_no, (port->idx*8+4));
|
||
|
||
fstat.reg = READ_REG_VOLATILE(A_FIFO_STA);
|
||
|
||
if (fstat.reg & (M_FIFO_ERR|M_ABO_DONE)) {
|
||
xhfc_resetfifo_flag(fe, mod_no, M_RES_FIFO_ERR);
|
||
TX_FAST_DBG("%s(%d, %d) TX FIFO ERROR (%02X)\n", __FUNCTION__, mod_no, port->idx, fstat.reg);
|
||
}
|
||
|
||
z1 = READ_REG_VOLATILE( A_Z1);
|
||
z2 = READ_REG_VOLATILE( A_Z2);
|
||
|
||
fifo_usage = z1 >= z2 ? (z1 - z2) : (bri_module->max_z - (z2 - z1)) + 1;
|
||
fifo_free = (bri_module->max_z - fifo_usage);
|
||
|
||
*free_space = fifo_free;
|
||
|
||
TX_FAST_DBG("%s(): Line:%d: free: %d\n", __FUNCTION__, __LINE__, fifo_free);
|
||
|
||
tcnt = ((fifo_free >= (*len - *idx)) ? (*len - *idx) : fifo_free);
|
||
|
||
TX_EXTRA_DBG("%s(): tcnt: %d, *idx: %d, *len %d, fstat.reg: 0x%X, free %d, usage: %d\n",
|
||
__FUNCTION__, tcnt, *idx, *len, fstat.reg, fifo_free, fifo_usage);
|
||
|
||
if (tcnt) {
|
||
f1 = READ_REG_VOLATILE( A_F1);
|
||
f2 = READ_REG_VOLATILE( A_F2);
|
||
|
||
/******** free frame/byte count in FIFOs *********
|
||
* *
|
||
* condition buffer sections *
|
||
* 0 LAST *
|
||
* f1 >= f2 -> | (free) F2 (used) F1 (free) | *
|
||
* f1 < f2 -> | (busy) F1 (free) F2 (busy) | *
|
||
* *
|
||
**************************************************/
|
||
|
||
fcnt = (f1 >= f2 ? (0x07 - f1) + f2 : (f2 - f1) - 1);
|
||
|
||
TX_FAST_DBG("%s(): free: %d, fcnt: %d, tcnt: %d\n", __FUNCTION__, fifo_free, fcnt, tcnt);
|
||
|
||
TX_EXTRA_DBG("%s(): START: usage: 0x%X, z1: 0x%X z2: 0x%X f1: 0x%X: f2:0x%X f0c:0x%X\n",
|
||
__FUNCTION__, READ_REG( A_USAGE),READ_REG( A_Z1),READ_REG( A_Z2),f1,f2,READ_REG( R_F0_CNTL));
|
||
|
||
if (fifo_free && fcnt) {
|
||
data = buf + *idx;
|
||
*idx += tcnt;
|
||
|
||
/* write data to FIFO */
|
||
i=0;
|
||
while (i<tcnt) {
|
||
WRITE_REG(A_FIFO_DATA, *(data+i));
|
||
i++;
|
||
}
|
||
|
||
/* terminate frame */
|
||
if (*idx == *len) {
|
||
xhfc_increment_fifo(fe, mod_no);/* incrementing fifo sends the frame out */
|
||
#if !TX_EMPTY_FIFO
|
||
*len = 0;
|
||
*idx = 0;
|
||
#endif
|
||
DEBUG_HFC_TX("%s(%d, %d): finished frame transmission.\n", __FUNCTION__, mod_no, port->idx);
|
||
rc = DCHAN_STATUS_COMPLETE;
|
||
#if !TX_EMPTY_FIFO
|
||
chan=(private_area_t*)card->u.aft.dev_to_ch_map[BRI_DCHAN_LOGIC_CHAN];
|
||
if (chan) {
|
||
*len = 0;
|
||
*idx = 0;
|
||
TX_FAST_DBG("%s(): calling wanpipe_wake_stack()\n", __FUNCTION__);
|
||
wanpipe_wake_stack(chan);
|
||
}
|
||
#endif
|
||
|
||
}else{
|
||
xhfc_select_fifo(fe, mod_no, (port->idx*8+4));/* addition ?? */
|
||
|
||
DEBUG_HFC_TX(KERN_INFO "%s(%d, %d): transmitted part of a frame.\n", __FUNCTION__, mod_no, port->idx);
|
||
rc = DCHAN_STATUS_INCOMPLETE;
|
||
}
|
||
}else{
|
||
DEBUG_HFC_TX(KERN_INFO "%s(%d, %d): NO space (free=%u, usage=%u, fcnt=%u, tcnt=%u)!\n",
|
||
__FUNCTION__, mod_no, port->idx, fifo_free, fifo_usage, fcnt, tcnt);
|
||
|
||
rc = DCHAN_STATUS_BUSY; /* there is NO free space in tx fifo */
|
||
}
|
||
} else {
|
||
DEBUG_HFC_TX("%s(%d, %d): nothing to send (free=%u, usage=%u, fcnt=%u, tcnt=%u)\n",
|
||
__FUNCTION__, mod_no, port->idx, fifo_free, fifo_usage, fcnt, tcnt);
|
||
|
||
rc = DCHAN_STATUS_COMPLETE;
|
||
}
|
||
|
||
TX_EXTRA_DBG("%s(): END: eof usage: 0x%X, z1: 0x%X z2: 0x%X f1: 0x%X: f2:0x%X f0c:0x%X\n",
|
||
__FUNCTION__,
|
||
READ_REG(A_USAGE), READ_REG( A_Z1), READ_REG(A_Z2),f1,f2,READ_REG( R_F0_CNTL));
|
||
|
||
#if TX_EMPTY_FIFO
|
||
chan=(private_area_t*)card->u.aft.dev_to_ch_map[BRI_DCHAN_LOGIC_CHAN];
|
||
|
||
z1 = READ_REG_VOLATILE( A_Z1);
|
||
z2 = READ_REG_VOLATILE( A_Z2);
|
||
|
||
fifo_usage = z1 >= z2 ? (z1 - z2) : bri_module->max_z - (z2 - z1);
|
||
fifo_free = (bri_module->max_z - fifo_usage);
|
||
|
||
if (chan) {
|
||
if(fifo_usage < (bri_module->max_z >> 1)) {
|
||
/* FIFO has enough space */
|
||
/*WP_DELAY(10000);*/
|
||
*len = 0;
|
||
*idx = 0;
|
||
|
||
TX_FAST_DBG("%s(): calling wanpipe_wake_stack()\n", __FUNCTION__);
|
||
wanpipe_wake_stack(chan);
|
||
}
|
||
}
|
||
|
||
TX_EXTRA_DBG("%s(): tcnt: %d, *idx: %d, *len %d, fstat.reg: 0x%X, free %d, usage: %d\n",
|
||
__FUNCTION__, tcnt, *idx, *len, fstat.reg, fifo_free, fifo_usage);
|
||
#endif
|
||
|
||
DEBUG_HFC_TX("%s(): returning: %d.\n", __FUNCTION__, rc);
|
||
return rc;
|
||
}
|
||
|
||
/*
|
||
Transmit D channel frames.
|
||
Transmitting fifo data requires running PCM clocks with signal at C4IO and F0IO.
|
||
*/
|
||
static int32_t wp_bri_dchan_tx(sdla_fe_t *fe, void *src_data_buffer, u32 buffer_len)
|
||
{
|
||
u8 mod_no, port_no, rc;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
u8 free_space;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
DEBUG_TX_DATA("%lu: %s(): Module: %d, port_no: %d. fe->name: %s blen=%i\n",
|
||
jiffies, __FUNCTION__, mod_no, port_no, fe->name, buffer_len);
|
||
|
||
#if defined(BUILD_MOD_TESTER)
|
||
{
|
||
u32 i;
|
||
u8 *tmp = (u8*)src_data_buffer;
|
||
|
||
DEBUG_TX_DATA("TX data:\n");
|
||
|
||
for(i = 0; i < buffer_len; i++) {
|
||
_DEBUG_EVENT("%02x ", tmp[i]);
|
||
if(i && ((i % 20) == 0)){
|
||
DEBUG_EVENT("\n");
|
||
}
|
||
}
|
||
DEBUG_EVENT("\n");
|
||
}
|
||
#endif
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
if(src_data_buffer == NULL){
|
||
/* Caller is interested in tx buffer space,
|
||
return how many bytes is still left to transmit */
|
||
return port_ptr->bytes2transmit;
|
||
}
|
||
|
||
if(MAX_DFRAME_LEN_L1 <= buffer_len){
|
||
DEBUG_EVENT("%s: %s(): Tx data length %d exceeds maximum of %d bytes!\n",
|
||
fe->name, __FUNCTION__, buffer_len, MAX_DFRAME_LEN_L1);
|
||
return -EINVAL;
|
||
}
|
||
|
||
if(port_ptr->bytes2transmit){
|
||
TX_EXTRA_DBG("%s(): %s: transmit EBUSY (queued = %u)\n",
|
||
fe->name, __FUNCTION__, port_ptr->bytes2transmit);
|
||
/* still transmitting previous frame */
|
||
return -EBUSY;
|
||
}
|
||
/*
|
||
{
|
||
int ind;
|
||
u8 * u8_src_data_buffer = (u8*)src_data_buffer;
|
||
|
||
for(ind = 0; ind < buffer_len; ind++){
|
||
if(u8_src_data_buffer[ind] != ind){
|
||
DEBUG_RX1("tx: u8_src_data_buffer[0x%x] is: 0x%x != 0x%x!!\n",
|
||
ind, u8_src_data_buffer[ind], ind);
|
||
}
|
||
}
|
||
}
|
||
*/
|
||
|
||
memcpy(port_ptr->dtxbuf, src_data_buffer, buffer_len);
|
||
port_ptr->bytes2transmit = buffer_len;
|
||
|
||
rc = xhfc_write_fifo_dchan(fe, mod_no, bri_module, port_ptr, &free_space);
|
||
|
||
/* The frame was accepted for transmission.
|
||
* Return 0 - Successful tx but now queue is full.
|
||
* Note that the frame maybe actually transmitted in parts, depending on the length.
|
||
*/
|
||
return 0;
|
||
}
|
||
|
||
#if CHECK_DATA
|
||
static void dump_chip_SRAM(sdla_fe_t *fe, u8 mod_no, u8 port_no)
|
||
{
|
||
u8 db;
|
||
u16 a;
|
||
/*
|
||
XHFC dump of internal RAM
|
||
|
||
the dump shows content of internal array registers
|
||
Z and F counters and other internal variables
|
||
|
||
please select address range for D channel that shows the errors
|
||
|
||
128 bytes address range for receive buffer of channel 5, D rx port_no 0
|
||
*/
|
||
/* wrong offset
|
||
u16 start_addr = 0x580;
|
||
u16 end_addr = 0x600;
|
||
*/
|
||
u16 start_addr = 0x280;
|
||
u16 end_addr = 0x300;
|
||
|
||
/*
|
||
128 bytes address range for receive buffer of channel 13, D rx port_no 1
|
||
u16 start_addr = 0xd80;
|
||
u16 end_addr = 0xe00;
|
||
*/
|
||
for (a = start_addr; a < end_addr; a++)
|
||
{
|
||
WRITE_REG(R_RAM_ADDR, (a & 0xff));
|
||
WRITE_REG(R_RAM_CTRL, ((a >> 8) & 0xff));
|
||
db=READ_REG(R_RAM_DATA);
|
||
|
||
/* dummy read to add some delay */
|
||
/*db=READ_REG(R_INT_DATA);
|
||
db=READ_REG(R_INT_DATA);*/
|
||
|
||
if((a % 16) == 0){
|
||
_DEBUG_EVENT("\n %04x",a);
|
||
}
|
||
_DEBUG_EVENT(" %02x",db);
|
||
}
|
||
DBG_RX_DATA("\n");
|
||
}
|
||
|
||
static int check_data(u8 *data, int data_len)
|
||
{
|
||
int i, rc = 0;
|
||
|
||
/*DBG_RX_DATA("%s(): data_len: %d\n", __FUNCTION__, data_len);*/
|
||
|
||
for(i = 0; i < data_len; i++){
|
||
if(data[i] == 0xFF){
|
||
rc = 1;
|
||
break;
|
||
}
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
static void dump_data(u8 *data, int data_len)
|
||
{
|
||
int i;
|
||
|
||
DBG_RX_DATA("%s(): data_len: %d\n", __FUNCTION__, data_len);
|
||
|
||
for(i = 0; i < data_len; i++){
|
||
if((i%16) == 0){
|
||
_DEBUG_EVENT("\n %04X", i);
|
||
}
|
||
|
||
_DEBUG_EVENT(" %02X", data[i]);
|
||
|
||
if (i == (data_len - 4)){
|
||
printk (" -");
|
||
}
|
||
}
|
||
_DEBUG_EVENT("\n");
|
||
}
|
||
#endif/* CHECK_DATA */
|
||
|
||
static int xhfc_read_fifo_dchan(sdla_fe_t *fe, u8 mod_no,
|
||
wp_bri_module_t *bri_module, bri_xhfc_port_t *port, u32 *rx_data_len)
|
||
{
|
||
u8 *buf = port->drxbuf;
|
||
int *idx = &port->drx_indx;
|
||
u8 *data; /* pointer for new data */
|
||
u8 rcnt, fcnt, i;
|
||
u8 f1=0, f2=0, z1=0, z2=0;
|
||
|
||
reg_a_fifo_sta fstat;
|
||
|
||
/* select D-RX fifo */
|
||
xhfc_select_fifo(fe, mod_no, (port->idx * 8) + 5);
|
||
|
||
fstat.reg = READ_REG(A_FIFO_STA);
|
||
|
||
if (fstat.reg & M_FIFO_ERR) {
|
||
xhfc_resetfifo_flag(fe, mod_no, M_RES_FIFO_ERR);
|
||
RX_EXTRA_DBG("%s(%d, %d) RX FIFO ERROR (%u)\n", __FUNCTION__, mod_no, port->idx, fstat.reg);
|
||
}
|
||
|
||
/* hdlc rcnt */
|
||
f1 = READ_REG_VOLATILE( A_F1);
|
||
f2 = READ_REG_VOLATILE( A_F2);
|
||
|
||
z1 = READ_REG_VOLATILE( A_Z1);
|
||
z2 = READ_REG_VOLATILE( A_Z2);
|
||
|
||
/* used frame + index count in rx fifo *
|
||
* *
|
||
* same logic as tx, but reversed (free instead of used) *
|
||
* *
|
||
* when overlap happens we need to add one (+1) to the used byte/frame *
|
||
* count for considering the extra "empty" slot represented by f1==f2 */
|
||
|
||
fcnt = f1 >= f2 ? (f1 - f2) : (0x07 - f2) + f1 + 1;
|
||
rcnt = z1 >= z2 ? (z1 - z2) : (bri_module->max_z - z2) + z1 + 1;
|
||
|
||
RX_EXTRA_DBG("%s(%d, %d): fcnt: %d, rcnt: %d, *idx %d, z1 %d, z2 %d, f1 %d, f2 %d\n",
|
||
__FUNCTION__, mod_no, port->idx, fcnt, rcnt, *idx, z1, z2, f1, f2);
|
||
|
||
/* debug message of F and Z counters */
|
||
RX_EXTRA_DBG("%s(): START: usage: 0x%X, z1: 0x%X z2: 0x%X f1: 0x%X: f2:0x%X f0c:0x%X\n",
|
||
__FUNCTION__,
|
||
READ_REG(A_USAGE), READ_REG( A_Z1), READ_REG(A_Z2),f1,f2,READ_REG( R_F0_CNTL));
|
||
|
||
if (!fcnt)
|
||
{
|
||
RX_EXTRA_DBG("%s(): END: eof usage: 0x%X, z1: 0x%X z2: 0x%X f1: 0x%X: f2:0x%X f0c:0x%X\n",
|
||
__FUNCTION__,
|
||
READ_REG(A_USAGE), READ_REG( A_Z1), READ_REG(A_Z2),f1,f2,READ_REG( R_F0_CNTL));
|
||
|
||
*rx_data_len = rcnt;
|
||
|
||
if (rcnt != 0) {
|
||
RX_EXTRA_DBG("%s: no frame, %u bytes\n", fe->name, rcnt);
|
||
} else {
|
||
RX_EXTRA_DBG("%s(): RX FIFO is empty! (z1=%u,z2=%u,f1=%u,f2=%u)\n", __FUNCTION__, z1, z2, f1, f2);
|
||
}
|
||
return DCHAN_STATUS_EMPTY;
|
||
} else {
|
||
// extra byte after end of frame
|
||
++rcnt;
|
||
}
|
||
|
||
data = buf + *idx;
|
||
*idx += rcnt;
|
||
if(*idx >= MAX_DFRAME_LEN_L1){
|
||
if (0 && WAN_NET_RATELIMIT()) {
|
||
DEBUG_EVENT("%s: %s(): frame in mod_no %d, port_no %d > maximum size of %d bytes!\n",
|
||
fe->name, __FUNCTION__, mod_no, port->idx, MAX_DFRAME_LEN_L1);
|
||
}
|
||
*idx = 0;
|
||
*rx_data_len = 0;
|
||
return DCHAN_STATUS_EMPTY;
|
||
}
|
||
|
||
DEBUG_HFC_RX("rcnt: %d\n", rcnt);
|
||
/* read data from FIFO */
|
||
i=0;
|
||
while (i < rcnt) {
|
||
*(data+i) = READ_REG(A_FIFO_DATA);
|
||
i++;
|
||
}
|
||
|
||
/* hdlc frame termination */
|
||
if (fcnt) {
|
||
xhfc_increment_fifo(fe, mod_no);
|
||
/* check minimum frame size */
|
||
if (*idx < 4) {
|
||
if (0 && WAN_NET_RATELIMIT()) {
|
||
DEBUG_EVENT("%s: %s(): frame in mod_no %d, port_no %d < minimum size of 4 bytes (%u)!\n",
|
||
fe->name, __FUNCTION__, mod_no, port->idx, *idx);
|
||
}
|
||
*idx = 0;
|
||
*rx_data_len = 0;
|
||
return DCHAN_STATUS_EMPTY;
|
||
}
|
||
|
||
/* check crc */
|
||
if (buf[(*idx) - 1]) {
|
||
if (0 && WAN_NET_RATELIMIT()) {
|
||
DEBUG_EVENT("%s: %s(): CRC-error in frame in mod_no %d, port_no %d!\n",
|
||
fe->name, __FUNCTION__, mod_no, port->idx);
|
||
}
|
||
*idx = 0;
|
||
*rx_data_len = 0;
|
||
return DCHAN_STATUS_EMPTY;
|
||
}
|
||
|
||
#if 0
|
||
/* D-Channel debug to syslog */
|
||
DBG_RX_DATA("%lu:D-RX len(%02i):\n", jiffies, (*idx));
|
||
i = 0;
|
||
while(i < (*idx)){
|
||
printk("%02x ", buf[i++]);
|
||
if (i == (*idx - 3)){
|
||
printk ("- ");
|
||
}
|
||
}
|
||
printk("\n");
|
||
#endif
|
||
*rx_data_len = *idx - 3;/* discard CRC and STATUS - 3 bytes */
|
||
/* STATUS is the last byte of a frame in the fifo
|
||
which is used to check if CRC is correct or not.
|
||
|
||
After the ending flag of a frame the XHFC-2SU checks the HDLC CRC checksum.
|
||
If it is correct one byte with all <20>0<EFBFBD>s is inserted behind
|
||
the CRC data in the FIFO named STAT (see Fig. 4.2). This last byte of a frame in the FIFO is different
|
||
from all <20>0<EFBFBD>s if there is no correct CRC field at the end of the frame.
|
||
If the STAT value is 0xFF, the HDLC frame ended with at least 8 bits <20>1<EFBFBD>s. This is similar to an abort
|
||
HDLC frame condition.
|
||
The ending flag of a HDLC frame can also be the starting flag of the next frame.
|
||
page 122.
|
||
*/
|
||
#if CHECK_DATA
|
||
if(check_data(port->drxbuf, *rx_data_len)){
|
||
dump_chip_SRAM(fe, mod_no, port->idx);
|
||
dump_chip_SRAM(fe, mod_no, port->idx);
|
||
reset_chip(fe, mod_no);
|
||
dump_data(port->drxbuf, *idx);
|
||
}
|
||
#endif
|
||
*idx = 0;
|
||
|
||
RX_EXTRA_DBG("%s(): END: eof usage: 0x%X, z1: 0x%X z2: 0x%X f1: 0x%X: f2:0x%X f0c:0x%X\n",
|
||
__FUNCTION__,
|
||
READ_REG(A_USAGE), READ_REG( A_Z1), READ_REG(A_Z2),f1,f2,READ_REG( R_F0_CNTL));
|
||
|
||
DEBUG_HFC_RX("%s(): finished receiving a frame.\n", __FUNCTION__);
|
||
return DCHAN_STATUS_COMPLETE;
|
||
} else {
|
||
RX_EXTRA_DBG("%s(): END: eof usage: 0x%X, z1: 0x%X z2: 0x%X f1: 0x%X: f2:0x%X f0c:0x%X\n",
|
||
__FUNCTION__,
|
||
READ_REG(A_USAGE), READ_REG( A_Z1), READ_REG(A_Z2),f1,f2,READ_REG( R_F0_CNTL));
|
||
|
||
DEBUG_HFC_RX("%s(): received a part of frame.\n", __FUNCTION__);
|
||
return DCHAN_STATUS_INCOMPLETE;
|
||
}
|
||
}
|
||
|
||
|
||
static void *wp_bri_dchan_rx(sdla_fe_t *fe, u8 mod_no, u8 port_no, u32 * rx_data_len)
|
||
{
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
netskb_t *skb;
|
||
u8 *skb_data_area;
|
||
int rc;
|
||
|
||
/* Note: D channel is slow (less than 2 bytes / ms!!) */
|
||
DEBUG_HFC_RX("%s(): line: %d, mod_no: %d port_no: %d\n", __FUNCTION__, __LINE__, mod_no, port_no);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
*rx_data_len = 0;
|
||
|
||
rc = xhfc_read_fifo_dchan(fe, mod_no, bri_module, port_ptr, rx_data_len);
|
||
|
||
skb = NULL;
|
||
if((rc == DCHAN_STATUS_COMPLETE) && (*rx_data_len < MAX_DFRAME_LEN_L1) && (*rx_data_len > 0)){
|
||
/**************************/
|
||
skb = wan_skb_alloc(*rx_data_len);
|
||
if(skb == NULL){
|
||
return NULL;
|
||
}
|
||
|
||
skb_data_area = wan_skb_put(skb, *rx_data_len);
|
||
memcpy(skb_data_area, port_ptr->drxbuf, *rx_data_len);
|
||
|
||
#if 0
|
||
{
|
||
int ind;
|
||
for(ind = 0; ind < *rx_data_len; ind++){
|
||
if(skb_data_area[ind] != ind){
|
||
DEBUG_RX1("rx: skb_data_area[0x%x] is: 0x%x != 0x%x!!\n",
|
||
ind, skb_data_area[ind], ind);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
return skb;
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
** wp_bri_iface_init) -
|
||
**
|
||
** OK
|
||
*/
|
||
int32_t wp_bri_iface_init(void *pfe_iface)
|
||
{
|
||
sdla_fe_iface_t *fe_iface = (sdla_fe_iface_t*)pfe_iface;
|
||
|
||
BRI_FUNC();
|
||
fe_iface->global_config = &bri_global_config; /* not used in BRI */
|
||
fe_iface->global_unconfig = &bri_global_unconfig; /* not used in BRI */
|
||
|
||
fe_iface->config = &wp_bri_config;
|
||
fe_iface->unconfig = &wp_bri_unconfig;
|
||
|
||
fe_iface->post_unconfig = &wp_bri_post_unconfig;
|
||
|
||
fe_iface->post_init = &wp_bri_post_init;
|
||
|
||
fe_iface->if_config = &wp_bri_if_config;
|
||
fe_iface->if_unconfig = &wp_bri_if_unconfig;
|
||
|
||
fe_iface->active_map = &wp_bri_active_map;
|
||
|
||
fe_iface->set_fe_status = &wp_bri_set_fe_status;
|
||
fe_iface->get_fe_status = &wp_bri_get_fe_status;
|
||
|
||
fe_iface->isr = &wp_bri_intr;
|
||
|
||
fe_iface->disable_irq = &wp_bri_disable_fe_irq;
|
||
|
||
fe_iface->check_isr = &wp_bri_check_intr;
|
||
|
||
fe_iface->polling = &wp_bri_polling;
|
||
fe_iface->process_udp = &wp_bri_udp;
|
||
fe_iface->get_fe_media = &wp_bri_fe_media;
|
||
|
||
fe_iface->set_dtmf = &wp_bri_set_dtmf;
|
||
fe_iface->intr_ctrl = &wp_bri_intr_ctrl;
|
||
fe_iface->event_ctrl = &wp_bri_event_ctrl;
|
||
|
||
fe_iface->isdn_bri_dchan_tx = &wp_bri_dchan_tx;
|
||
|
||
fe_iface->clock_ctrl = &config_clock_routing;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Should be done only ONCE per card. */
|
||
static int32_t wp_bri_spi_bus_reset(sdla_fe_t *fe)
|
||
{
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
|
||
BRI_FUNC();
|
||
|
||
DEBUG_EVENT("%s: Executing SPI bus reset....\n", fe->name);
|
||
|
||
card->hw_iface.bus_write_4( card->hw,
|
||
SPI_INTERFACE_REG,
|
||
MOD_SPI_RESET);
|
||
WP_DELAY(1000);
|
||
card->hw_iface.bus_write_4( card->hw,
|
||
SPI_INTERFACE_REG,
|
||
0x00000000);
|
||
WP_DELAY(1000);
|
||
return 0;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* scan_modules()
|
||
*
|
||
* Description : Scan for installed modules.
|
||
*
|
||
* Arguments : pfe - pointer to Front End structure.
|
||
*
|
||
* Returns : number of discovered modules.
|
||
*******************************************************************************/
|
||
static u_int8_t scan_modules(sdla_fe_t *fe, u_int8_t rm_no)
|
||
{
|
||
u_int8_t mod_no = RM_BRI_STATUS_READ/*0x3*/;/* to read remora status register ALWAYS put 0x3 into mod_addr. */
|
||
u_int8_t value, ind, mod_counter = 0;
|
||
u_int8_t mod_no_index; /* index in the array of ALL modules (NOT lines) on ALL remoras. From 0 to 11 */
|
||
|
||
/* format of remora status register:
|
||
bit 0 == 1 - module 1 active (exist)
|
||
bit 1 - type of module 1 (0 - NT, 1 - TE)
|
||
|
||
bit 2 == 1 - module 2 active (exist)
|
||
bit 3 - type of module 1 (0 - NT, 1 - TE)
|
||
|
||
bit 4 == 1 - module 3 active (exist)
|
||
bit 5 - type of module 1 (0 - NT, 1 - TE)
|
||
|
||
bit 6,7 - has to be zeros for active remora. if non-zero, remora does not exist.
|
||
*/
|
||
|
||
value = fe->read_fe_reg(fe->card,
|
||
mod_no,
|
||
MOD_TYPE_NONE,
|
||
rm_no,
|
||
0);
|
||
|
||
#define MODULE1 0
|
||
#define MODULE2 2
|
||
#define MODULE3 4
|
||
|
||
DEBUG_BRI_INIT("remora number: %d, remora status register: 0x%02X\n", rm_no, value);
|
||
|
||
if((value >> 6) == 0x2){
|
||
DEBUG_EVENT("%s: Remora number %d: Found 512khz Recovery clock remora.\n", fe->name, rm_no);
|
||
fe->bri_param.use_512khz_recovery_clock = 1;
|
||
}else{
|
||
|
||
if(((value >> 7) & 0x01)){
|
||
DEBUG_EVENT("%s: Remora number %d does not exist.\n", fe->name, rm_no);
|
||
return 0;
|
||
}else{
|
||
DEBUG_EVENT("%s: Remora number %d exist.\n", fe->name, rm_no);
|
||
}
|
||
}
|
||
|
||
for(ind = 0; ind < 6; ind++){
|
||
|
||
switch(ind){
|
||
|
||
case MODULE1:
|
||
case MODULE2:
|
||
case MODULE3:
|
||
DEBUG_BRI_INIT("module Number on REMORA (0-2): %d\n", ind / 2);
|
||
|
||
/* 0-11, all (even and odd) numbers */
|
||
mod_no_index = rm_no * MAX_BRI_MODULES_PER_REMORA + ind / 2;
|
||
DEBUG_BRI_INIT("mod_no_index on CARD (should be 0-11): %d\n", mod_no_index);
|
||
|
||
/* 0-23, only even numbers */
|
||
mod_no_index = mod_no_index * 2;
|
||
DEBUG_BRI_INIT("mod_no_index (line number) on CARD (should be 0-23): %d\n", mod_no_index);
|
||
|
||
if(mod_no_index >= MAX_BRI_LINES){
|
||
DEBUG_ERROR("%s: Error: Module %d/%d exceeds maximum (%d)\n",
|
||
fe->name, mod_no_index, mod_no_index, MAX_BRI_LINES);
|
||
return 0;
|
||
}
|
||
|
||
if((value >> ind) & 0x01){
|
||
|
||
mod_counter++;
|
||
|
||
if((value >> (ind + 1)) & 0x01){
|
||
|
||
DEBUG_BRI_INIT("%s: Module %d type is: TE\n",
|
||
fe->name, mod_no_index);
|
||
|
||
fe->bri_param.mod[mod_no_index].type = MOD_TYPE_TE;
|
||
|
||
fe->bri_param.mod[mod_no_index].port[PORT_0].mode |= PORT_MODE_TE;
|
||
fe->bri_param.mod[mod_no_index].port[PORT_1].mode |= PORT_MODE_TE;
|
||
|
||
}else{
|
||
DEBUG_BRI_INIT("%s: Module %d type is: NT\n",
|
||
fe->name, mod_no_index);
|
||
|
||
fe->bri_param.mod[mod_no_index].type = MOD_TYPE_NT;
|
||
|
||
fe->bri_param.mod[mod_no_index].port[PORT_0].mode |= PORT_MODE_NT;
|
||
fe->bri_param.mod[mod_no_index].port[PORT_1].mode |= PORT_MODE_NT;
|
||
}
|
||
|
||
/*Copy information from (even numbered) 'mod_no_index' to
|
||
(odd numbered) 'mod_no_index + 1' because for each module there are two lines.*/
|
||
memcpy(&fe->bri_param.mod[mod_no_index + 1], &fe->bri_param.mod[mod_no_index],
|
||
sizeof(wp_bri_module_t));
|
||
}else{
|
||
DEBUG_BRI_INIT("%s: Module %d is not installed\n",
|
||
fe->name, mod_no_index);
|
||
}
|
||
DEBUG_BRI_INIT("=================================\n\n");
|
||
break;
|
||
}/* switch() */
|
||
}/* for() */
|
||
|
||
return mod_counter;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* scan_remoras_and_modules()
|
||
*
|
||
* Description : Scan for installed Remoras and modules.
|
||
*
|
||
* Arguments : pfe - pointer to Front End structure.
|
||
*
|
||
* Returns : 0 - configred successfully, otherwise non-zero value.
|
||
*******************************************************************************/
|
||
static int32_t scan_remoras_and_modules(void* pfe)
|
||
{
|
||
sdla_fe_t *fe = (sdla_fe_t*)pfe;
|
||
u8 rm_no, modules_counter = 0;
|
||
|
||
BRI_FUNC();
|
||
|
||
for(rm_no = 0; rm_no < MAX_BRI_REMORAS; rm_no++){
|
||
|
||
modules_counter += scan_modules(fe, rm_no);
|
||
}
|
||
|
||
if(modules_counter == 0){
|
||
DEBUG_ERROR("%s: Error: modules counter is zero!\n", fe->name);
|
||
return 1;
|
||
}else{
|
||
DEBUG_EVENT("%s: Total number of modules: %d.\n", fe->name, modules_counter);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/******************************************************************************
|
||
* bri_global_config()
|
||
*
|
||
* Description : Global configuration for Sangoma BRI board.
|
||
*
|
||
* Notes : 1. This routine runs only ONCE for a physical 'base' CARD,
|
||
* not for 'additional' cards.
|
||
* 2. reset card's SPI.
|
||
* 3. Scan for installed Remoras and modules.
|
||
*
|
||
* Arguments : pfe - pointer to Front End structure.
|
||
*
|
||
* Returns : 0 - configred successfully, otherwise non-zero value.
|
||
*******************************************************************************/
|
||
static int32_t bri_global_config(void* pfe)
|
||
{
|
||
sdla_fe_t *fe = (sdla_fe_t*)pfe;
|
||
|
||
BRI_FUNC();
|
||
|
||
DEBUG_EVENT("%s: %s Global Front End configuration\n",
|
||
fe->name, FE_MEDIA_DECODE(fe));
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* bri_global_unconfig()
|
||
*
|
||
* Description : Global un-configuration for Sangoma BRI board.
|
||
* Note: This routne runs only ONCE for a physical card.
|
||
*
|
||
* Arguments :
|
||
*
|
||
* Returns : 0
|
||
*******************************************************************************/
|
||
static int32_t bri_global_unconfig(void* pfe)
|
||
{
|
||
sdla_fe_t *fe = (sdla_fe_t*)pfe;
|
||
|
||
BRI_FUNC();
|
||
|
||
DEBUG_EVENT("%s: %s Global Front End unconfiguration!\n",
|
||
fe->name, FE_MEDIA_DECODE(fe));
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*
|
||
******************************************************************************
|
||
* wp_bri_post_unconfig()
|
||
*
|
||
* Description: Free resources used by 'fe'. This function is NOT locked and
|
||
* it must NOT be called more than one time.
|
||
* Arguments:
|
||
* Returns:
|
||
******************************************************************************
|
||
*/
|
||
static int wp_bri_post_unconfig(void* pfe)
|
||
{
|
||
sdla_fe_t *fe = (sdla_fe_t*)pfe;
|
||
wp_bri_module_t *bri_module;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
u8 mod_no, port_no;
|
||
bri_xhfc_port_t *port_ptr;
|
||
|
||
if (!wan_test_bit(WP_BCB_BRI_POST_INIT, &fe->bri_param.critical)) {
|
||
return 1;
|
||
}
|
||
|
||
if(validate_fe_line_no(fe, __FUNCTION__)){
|
||
return 1;
|
||
}
|
||
|
||
DEBUG_EVENT("%s: Running post-unconfig...\n", fe->name);
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
DEBUG_HFC_INIT("%s(): mod_no: %i, port_no: %i\n", __FUNCTION__, mod_no, port_no);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
wan_del_timer(&port_ptr->t3_timer);
|
||
wan_del_timer(&port_ptr->t4_timer);
|
||
wan_del_timer(&port_ptr->t1_timer);
|
||
|
||
}/* for (port_no = 0; port_no < bri_module->num_ports; port_no++) */
|
||
|
||
wan_clear_bit(WP_BCB_BRI_POST_INIT, &fe->bri_param.critical);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* wp_bri_config() - initialise the XHFC ISDN Chip.
|
||
*
|
||
* Description : Configure the PHYSICAL module ONE time.
|
||
* On each module 2 lines will be configured
|
||
* in exactly the same way.
|
||
*
|
||
* Arguments : pfe - pointer to Front End structure.
|
||
*
|
||
* Returns : 0 - configred successfully, otherwise non-zero value.
|
||
*******************************************************************************/
|
||
static int32_t wp_bri_config(void *pfe)
|
||
{
|
||
sdla_fe_t *fe = (sdla_fe_t*)pfe;
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
u8 mod_no, port_no, mod_cnt = 0;
|
||
/*sdlahw_t* hw = (sdlahw_t*)card->hw;*/
|
||
int32_t err = 0, aft_line_no = 0;
|
||
u16 physical_module_config_counter;
|
||
u32 physical_card_config_counter;
|
||
wp_bri_module_t *bri_module;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
|
||
BRI_FUNC();
|
||
|
||
if (wan_test_bit(WP_BCB_BRI_CONFIG, &fe->bri_param.critical)) {
|
||
DEBUG_ERROR("%s: %s: Error: Line already configred! Line number %i !\n",
|
||
fe->name, FE_MEDIA_DECODE(fe), WAN_FE_LINENO(fe)+1);
|
||
return 1;
|
||
}
|
||
|
||
fe->fe_status = FE_UNITIALIZED;
|
||
|
||
if(validate_fe_line_no(fe, __FUNCTION__)){
|
||
DEBUG_ERROR("%s: %s: Error: Invalid Front End Line number %i !\n",
|
||
fe->name, FE_MEDIA_DECODE(fe), WAN_FE_LINENO(fe)+1);
|
||
return 1;
|
||
}
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
|
||
DEBUG_EVENT("%s: %s: Front End configuration Line=%d Mod=%i\n",
|
||
fe->name, FE_MEDIA_DECODE(fe), WAN_FE_LINENO(fe) + 1, mod_no);
|
||
|
||
DEBUG_TEST("%s(): mod_no: %i, port_no: %i\n", __FUNCTION__, mod_no, port_no);
|
||
|
||
WAN_ASSERT(fe->write_fe_reg == NULL);
|
||
WAN_ASSERT(fe->read_fe_reg == NULL);
|
||
|
||
fe->bri_param.max_fe_channels = MAX_TIMESLOTS;
|
||
fe->bri_param.module_map[0] = fe->bri_param.module_map[1] = 0;
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
|
||
card->hw_iface.getcfg(card->hw, SDLA_HWTYPE_USEDCNT, &physical_card_config_counter);
|
||
|
||
if(physical_card_config_counter == 1){
|
||
/* Per-card initialization. Important to do only ONCE.*/
|
||
wp_bri_spi_bus_reset(fe);
|
||
}
|
||
|
||
fe->bri_param.use_512khz_recovery_clock = 0;
|
||
|
||
if(scan_remoras_and_modules(fe)){
|
||
return 1;
|
||
}
|
||
|
||
#if defined(BUILD_MOD_TESTER)
|
||
/* for Production Test nothing else should be done */
|
||
return 0;
|
||
#endif
|
||
|
||
/*Event lock */
|
||
WAN_LIST_INIT(&fe->event);
|
||
/* wan_spin_lock_init(&fe->lock, "wp_bri_lock"); FIXME: 'lock' is not there, but what is there? */
|
||
|
||
card->hw_iface.getcfg(card->hw, SDLA_HWLINEREG, &physical_module_config_counter);
|
||
|
||
bri_module->fe = fe;
|
||
|
||
switch(fe->bri_param.mod[mod_no].type)
|
||
{
|
||
case MOD_TYPE_TE:
|
||
case MOD_TYPE_NT:
|
||
if(physical_module_config_counter == 1){
|
||
|
||
if((err = init_xfhc(fe, mod_no))){
|
||
return err;
|
||
}
|
||
}else{
|
||
bri_module->num_ports = BRI_MAX_PORTS_PER_CHIP;
|
||
bri_module->max_fifo = 8;
|
||
bri_module->max_z = 0x7F;
|
||
|
||
/* init line interfaces state machines (in software only!) */
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {
|
||
|
||
bri_module->port[port_no].idx = port_no;
|
||
bri_module->port[port_no].hw = bri_module;
|
||
|
||
}/* for (port_no = 0; port_no < bri_module->num_ports; port_no++) */
|
||
}
|
||
break;
|
||
|
||
default:
|
||
DEBUG_ERROR("%s(): %s: Error: Module %d (AFT Line: %d): Not Installed!!\n",
|
||
__FUNCTION__, fe->name, REPORT_MOD_NO(mod_no), aft_line_no);
|
||
return 1;
|
||
}
|
||
|
||
/**************************************************************************/
|
||
/* Set active modules (channels) bitmap for all installed LOGICAL modules */
|
||
|
||
#define BCHAN_BITMAP 0x3 /* each BRI line has 2 b-channels --> 2 bits */
|
||
|
||
if(mod_no < (MAX_BRI_LINES / 2)){
|
||
/* 1-st aft line has timeslots 0-23 */
|
||
aft_line_no = 0;
|
||
}else{
|
||
/* 2-nd aft line has timeslots 24-47 */
|
||
aft_line_no = 1;
|
||
}
|
||
|
||
fe->bri_param.mod[mod_no].mod_no = mod_no;
|
||
|
||
switch(fe->bri_param.mod[mod_no].type)
|
||
{
|
||
case MOD_TYPE_TE:
|
||
case MOD_TYPE_NT:
|
||
fe->bri_param.module_map[aft_line_no] |= (BCHAN_BITMAP << (mod_no*2));
|
||
|
||
DEBUG_EVENT("%s: Module %d (AFT Line: %d): Installed -- %s. Timeslots map: 0x%08X\n",
|
||
fe->name, REPORT_MOD_NO(mod_no), aft_line_no,
|
||
WP_BRI_DECODE_MOD_TYPE(fe->bri_param.mod[mod_no].type),
|
||
(BCHAN_BITMAP << (mod_no*2)));
|
||
mod_cnt++;
|
||
break;
|
||
|
||
default:
|
||
DEBUG_WARNING("%s(): %s: Warning: Module %d (AFT Line: %d): Not Installed.\n",
|
||
__FUNCTION__, fe->name, REPORT_MOD_NO(mod_no), aft_line_no);
|
||
break;
|
||
}
|
||
|
||
/*******************************************************/
|
||
/* Enable interrupts on the installed PHYSICAL module. */
|
||
switch(fe->bri_param.mod[mod_no].type)
|
||
{
|
||
case MOD_TYPE_TE:
|
||
case MOD_TYPE_NT:
|
||
if(physical_module_config_counter == 1){
|
||
/* DAVIDR-consider moving call bri_enable_interrupts() to wp_bri_post_init() */
|
||
bri_enable_interrupts(fe, mod_no, port_no);
|
||
}else{
|
||
DEBUG_HFC_INIT("%s(): the other port_no is ALREADY running, interrupts are enabled.\n",
|
||
__FUNCTION__);
|
||
}
|
||
break;
|
||
default:
|
||
DEBUG_WARNING("%s(): %s: Warning: Module %d (AFT Line: %d): Not Installed.\n",
|
||
__FUNCTION__, fe->name, REPORT_MOD_NO(mod_no), aft_line_no);
|
||
break;
|
||
}
|
||
|
||
/******************************************************/
|
||
/*------------------ CLOCK RECOVERY ------------------*/
|
||
if(physical_card_config_counter == 1){
|
||
/* Per-card initialization. Important to do only ONCE.*/
|
||
u32 i;
|
||
|
||
/* ALL modules MUST have GPIO6 function switched
|
||
from the default 'output' to 'input'.
|
||
Otherwise there will be a clock conflict!*/
|
||
|
||
for(i = 0; i < MAX_BRI_LINES; i += BRI_MAX_PORTS_PER_CHIP){
|
||
|
||
switch(fe->bri_param.mod[i].type)
|
||
{
|
||
case MOD_TYPE_TE:
|
||
case MOD_TYPE_NT:
|
||
__config_clock_routing(fe, i, WANOPT_NO);
|
||
break;
|
||
default:
|
||
continue;
|
||
}/* switch() */
|
||
}/* for() */
|
||
}/* if() */
|
||
/*----------- END OF CLOCK RECOVERY ------------------*/
|
||
/******************************************************/
|
||
|
||
fe->fe_status = FE_DISCONNECTED;
|
||
|
||
wan_set_bit(WP_BCB_BRI_CONFIG, &fe->bri_param.critical);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/******************************************************************************
|
||
** wp_bri_unconfig() -
|
||
**
|
||
** OK
|
||
*/
|
||
static int32_t wp_bri_unconfig(void *pfe)
|
||
{
|
||
sdla_fe_t *fe = (sdla_fe_t*)pfe;
|
||
u8 mod_no, port_no;
|
||
u16 physical_module_config_counter;
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
|
||
if(validate_fe_line_no(fe, __FUNCTION__)){
|
||
return 1;
|
||
}
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
DEBUG_HFC_INIT("%s(): mod_no: %d, port_no: %d\n", __FUNCTION__, mod_no, port_no);
|
||
|
||
/* Check if port was configured. If not, return. */
|
||
if (!wan_test_bit(WP_BCB_BRI_CONFIG, &fe->bri_param.critical)) {
|
||
DEBUG_BRI("%s: %s(): Warning: Front End initialization was incomplete OR not a first call to UnConfig!\n",
|
||
fe->name, __FUNCTION__);
|
||
return 1;
|
||
}
|
||
|
||
DEBUG_EVENT("%s: Unconfiguring BRI Front End...\n", fe->name);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
card->hw_iface.getcfg(card->hw, SDLA_HWLINEREG, &physical_module_config_counter);
|
||
|
||
WAN_ASSERT(fe->write_fe_reg == NULL);
|
||
WAN_ASSERT(fe->read_fe_reg == NULL);
|
||
|
||
/******************************************************************/
|
||
switch(fe->bri_param.mod[mod_no].type)
|
||
{
|
||
case MOD_TYPE_TE:
|
||
case MOD_TYPE_NT:
|
||
|
||
if(physical_module_config_counter == 1){
|
||
wp_bri_disable_irq(fe, mod_no, port_no);
|
||
}else{
|
||
DEBUG_HFC_INIT("%s(): the other port_no is still running, leave interrupts enabled.\n",
|
||
__FUNCTION__);
|
||
}
|
||
break;
|
||
default:
|
||
/* for missing (not installed) modules - do nothing */
|
||
DEBUG_EVENT("%s(): %s: Warining: unknown module type!\n",
|
||
__FUNCTION__, fe->name);
|
||
break;
|
||
}
|
||
|
||
#if 1
|
||
{
|
||
reg_a_su_wr_sta a_su_wr_sta;
|
||
/******************************************************************/
|
||
/* When we stop a port (because not used anymore), force the
|
||
deactivation of the line interface by writing the deactivated state
|
||
into the A_SU_WR_STA register.
|
||
For a port that is configured in NT mode, write 0x11 to A_SU_WR_STA (force G1)
|
||
and for a TE port write 0x13 (force F3) to this register.
|
||
In these states the port will send only INFO0 (no signal) and no
|
||
line interface state change interrupts will be generated. */
|
||
WRITE_REG(R_SU_SEL, port_no);
|
||
|
||
DEBUG_HFC_INIT("%s(): port_no: %i, port_ptr: 0x%p, port_ptr->mode: %i\n", __FUNCTION__, port_no, port_ptr, port_ptr->mode);
|
||
|
||
a_su_wr_sta.reg = 0;
|
||
if (port_ptr->mode & PORT_MODE_TE) {
|
||
/* TE to F3 */
|
||
a_su_wr_sta.bit.v_su_set_sta = 0x3;
|
||
a_su_wr_sta.bit.v_su_ld_sta = 0x1;
|
||
}else{
|
||
/* NT to G1 */
|
||
a_su_wr_sta.bit.v_su_set_sta = 0x1;
|
||
a_su_wr_sta.bit.v_su_ld_sta = 0x1;
|
||
}
|
||
|
||
WRITE_REG(A_SU_WR_STA, a_su_wr_sta.reg);
|
||
/* Immediatly after that we get SU state interrupt - clear it! (for all ports) */
|
||
/*READ_REG(R_SU_IRQ);*/
|
||
}
|
||
#endif
|
||
/******************************************************************/
|
||
|
||
fe->fe_status = FE_UNITIALIZED;
|
||
|
||
wan_clear_bit(WP_BCB_BRI_CONFIG, &fe->bri_param.critical);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/******************************************************************************
|
||
** wp_bri_post_init() -
|
||
**
|
||
** OK
|
||
*/
|
||
static int32_t wp_bri_post_init(void *pfe)
|
||
{
|
||
sdla_fe_t *fe = (sdla_fe_t*)pfe;
|
||
wp_bri_module_t *bri_module;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
u8 mod_no, port_no;
|
||
bri_xhfc_port_t *port_ptr;
|
||
|
||
if (wan_test_bit(WP_BCB_BRI_POST_INIT, &fe->bri_param.critical)) {
|
||
return 1;
|
||
}
|
||
|
||
if(validate_fe_line_no(fe, __FUNCTION__)){
|
||
return 1;
|
||
}
|
||
|
||
DEBUG_EVENT("%s: Running post initialization...\n", fe->name);
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
DEBUG_HFC_INIT("%s(): mod_no: %i, port_no: %i\n", __FUNCTION__, mod_no, port_no);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
|
||
/* */
|
||
for (port_no = 0; port_no < bri_module->num_ports; port_no++) {
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
wan_init_timer(&port_ptr->t3_timer, l1_timer_expire_t3, (wan_timer_arg_t)port_ptr);
|
||
wan_init_timer(&port_ptr->t4_timer, l1_timer_expire_t4, (wan_timer_arg_t)port_ptr);
|
||
wan_init_timer(&port_ptr->t1_timer, l1_timer_expire_t1, (wan_timer_arg_t)port_ptr);
|
||
}/* for (port_no = 0; port_no < bri_module->num_ports; port_no++) */
|
||
|
||
#if 1
|
||
{
|
||
wan_smp_flag_t smp_flags1;
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
|
||
/* Try to activate the port_no - if cable is in, line will get activated.
|
||
If no cable, the application will have to call wp_bri_set_fe_status()
|
||
to get line activated. */
|
||
card->hw_iface.hw_lock(card->hw,&smp_flags1);
|
||
wp_bri_set_fe_status(fe, WAN_FE_CONNECTED);
|
||
card->hw_iface.hw_unlock(card->hw,&smp_flags1);
|
||
}
|
||
#endif
|
||
|
||
wan_set_bit(WP_BCB_BRI_POST_INIT, &fe->bri_param.critical);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* l1_timer_start_t3
|
||
*/
|
||
static void l1_timer_start_t3(void *pport)
|
||
{
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
wp_bri_module_t *bri_module;
|
||
sdla_fe_t *fe;
|
||
u8 mod_no;
|
||
|
||
WAN_ASSERT_VOID(port_ptr == NULL);
|
||
|
||
WAN_ASSERT_VOID(port_ptr->hw == NULL);
|
||
bri_module = port_ptr->hw;
|
||
|
||
WAN_ASSERT_VOID(bri_module->fe == NULL);
|
||
fe = (sdla_fe_t*)bri_module->fe;
|
||
|
||
mod_no = (u8)bri_module->mod_no;
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
if (!wan_test_bit(WP_BCB_BRI_CONFIG, &fe->bri_param.critical)) {
|
||
/* may get here during unload!! */
|
||
return;
|
||
}
|
||
|
||
if (!wan_test_bit(T3_TIMER_ACTIVE, &port_ptr->timer_flags) &&
|
||
!wan_test_bit(T3_TIMER_EXPIRED, &port_ptr->timer_flags)){
|
||
|
||
DEBUG_HFC_S0_STATES("Starting T3 timer...\n");
|
||
|
||
wan_set_bit(T3_TIMER_ACTIVE, &port_ptr->timer_flags);
|
||
|
||
wan_add_timer(&port_ptr->t3_timer, (XHFC_TIMER_T3 * HZ) / 1000);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* l1_timer_stop_t3
|
||
*/
|
||
static void l1_timer_stop_t3(void *pport)
|
||
{
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
wp_bri_module_t *bri_module = port_ptr->hw;
|
||
u8 mod_no = (u8)bri_module->mod_no;
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
if(wan_test_bit(T3_TIMER_ACTIVE, &port_ptr->timer_flags)){
|
||
|
||
wan_clear_bit(T3_TIMER_ACTIVE, &port_ptr->timer_flags);
|
||
wan_clear_bit(T3_TIMER_EXPIRED, &port_ptr->timer_flags);
|
||
|
||
wan_clear_bit(HFC_L1_ACTIVATING, &port_ptr->l1_flags);
|
||
wan_del_timer(&port_ptr->t3_timer);
|
||
}
|
||
}
|
||
|
||
/*
|
||
******************************************************************************
|
||
* l1_timer_expire_t3()
|
||
*
|
||
* Description: called when timer t3 expires.
|
||
* Activation failed, force clean L1 deactivation.
|
||
* Arguments:
|
||
* Returns:
|
||
******************************************************************************
|
||
*/
|
||
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||
static void l1_timer_expire_t3(void* pport)
|
||
#elif defined(__WINDOWS__)
|
||
static void l1_timer_expire_t3(IN PKDPC Dpc, void* pport, void* arg2, void* arg3)
|
||
#elif defined(KERN_TIMER_SETUP) && KERN_TIMER_SETUP > 0
|
||
static void l1_timer_expire_t3(struct timer_list *t)
|
||
#else
|
||
static void l1_timer_expire_t3(unsigned long pport)
|
||
#endif
|
||
{
|
||
#if defined(KERN_TIMER_SETUP) && KERN_TIMER_SETUP > 0
|
||
bri_xhfc_port_t *port_ptr = from_timer(port_ptr, t, t3_timer.timer_info);
|
||
#else
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
#endif
|
||
wp_bri_module_t *bri_module = port_ptr->hw;
|
||
sdla_fe_t *fe = (sdla_fe_t*)bri_module->fe;
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
wan_device_t *wandev = &card->wandev;
|
||
|
||
DEBUG_HFC_S0_STATES("%s()\n", __FUNCTION__);
|
||
|
||
if (wandev->fe_enable_timer){
|
||
|
||
wan_set_bit(T3_TIMER_EXPIRED, &port_ptr->timer_flags);
|
||
|
||
wandev->fe_enable_timer(fe->card);
|
||
}
|
||
}
|
||
|
||
static void __l1_timer_expire_t3(sdla_fe_t *fe)
|
||
{
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
u8 mod_no, port_no;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
wan_clear_bit(T3_TIMER_ACTIVE, &port_ptr->timer_flags);
|
||
wan_clear_bit(T3_TIMER_EXPIRED, &port_ptr->timer_flags);
|
||
|
||
wan_clear_bit(HFC_L1_ACTIVATING, &port_ptr->l1_flags);
|
||
xhfc_ph_command(fe, port_ptr, HFC_L1_FORCE_DEACTIVATE_TE);
|
||
}
|
||
|
||
|
||
/**
|
||
* l1_timer_start_t4
|
||
*/
|
||
static void l1_timer_start_t4(void *pport)
|
||
{
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
wp_bri_module_t *bri_module;
|
||
sdla_fe_t *fe;
|
||
u8 mod_no;
|
||
|
||
WAN_ASSERT_VOID(port_ptr == NULL);
|
||
|
||
WAN_ASSERT_VOID(port_ptr->hw == NULL);
|
||
bri_module = port_ptr->hw;
|
||
|
||
WAN_ASSERT_VOID(bri_module->fe == NULL);
|
||
fe = (sdla_fe_t*)bri_module->fe;
|
||
|
||
mod_no = (u8)bri_module->mod_no;
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
if (!wan_test_bit(WP_BCB_BRI_CONFIG, &fe->bri_param.critical)) {
|
||
/* may get here during unload!! */
|
||
return;
|
||
}
|
||
|
||
if(!wan_test_and_set_bit(T4_TIMER_ACTIVE, &port_ptr->timer_flags)){
|
||
DEBUG_HFC_S0_STATES("Starting T4 timer...\n");
|
||
wan_set_bit(HFC_L1_DEACTTIMER, &port_ptr->l1_flags);
|
||
wan_add_timer(&port_ptr->t4_timer, (XHFC_TIMER_T4 * HZ) / 1000);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* l1_timer_stop_t4
|
||
*/
|
||
static void l1_timer_stop_t4(void *pport)
|
||
{
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
wp_bri_module_t *bri_module = port_ptr->hw;
|
||
u8 mod_no = (u8)bri_module->mod_no;
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
if(wan_test_bit(T4_TIMER_ACTIVE, &port_ptr->timer_flags)){
|
||
wan_clear_bit(T4_TIMER_ACTIVE, &port_ptr->timer_flags);
|
||
wan_clear_bit(HFC_L1_DEACTTIMER, &port_ptr->l1_flags);
|
||
wan_del_timer(&port_ptr->t4_timer);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
******************************************************************************
|
||
* l1_timer_expire_t4()
|
||
*
|
||
* Description: l1_timer_expire_t4 - called when timer t4 expires.
|
||
* Send (PH_DEACTIVATE | INDICATION) to upper layer.
|
||
*
|
||
* Note that this function does NOT access the hardware so we
|
||
* don't have to use wandev->fe_enable_timer() as it is done
|
||
* for T1 and T3.
|
||
*
|
||
* Arguments:
|
||
* Returns:
|
||
******************************************************************************
|
||
*/
|
||
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||
static void l1_timer_expire_t4(void* pport)
|
||
#elif defined(__WINDOWS__)
|
||
static void l1_timer_expire_t4(IN PKDPC Dpc, void* pport, void* arg2, void* arg3)
|
||
#elif defined(KERN_TIMER_SETUP) && KERN_TIMER_SETUP > 0
|
||
static void l1_timer_expire_t4(struct timer_list *t)
|
||
#else
|
||
static void l1_timer_expire_t4(unsigned long pport)
|
||
#endif
|
||
{
|
||
#if defined(KERN_TIMER_SETUP) && KERN_TIMER_SETUP > 0
|
||
bri_xhfc_port_t *port_ptr = from_timer(port_ptr, t, t4_timer.timer_info);
|
||
#else
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
#endif
|
||
wp_bri_module_t *bri_module = port_ptr->hw;
|
||
sdla_fe_t *fe = bri_module->fe;
|
||
u8 mod_no, port_no;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
wan_clear_bit(T4_TIMER_ACTIVE, &port_ptr->timer_flags);
|
||
wan_clear_bit(HFC_L1_DEACTTIMER, &port_ptr->l1_flags);
|
||
sdla_bri_set_status(fe, mod_no, port_no, FE_DISCONNECTED);
|
||
}
|
||
|
||
|
||
/*
|
||
******************************************************************************
|
||
* l1_timer_expire_t1()
|
||
*
|
||
* Description: called when timer t1 expires.
|
||
* Activation failed, force clean L1 deactivation.
|
||
* Arguments:
|
||
* Returns:
|
||
******************************************************************************
|
||
*/
|
||
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||
static void l1_timer_expire_t1(void* pport)
|
||
#elif defined(__WINDOWS__)
|
||
static void l1_timer_expire_t1(IN PKDPC Dpc, void* pport, void* arg2, void* arg3)
|
||
#elif defined(KERN_TIMER_SETUP) && KERN_TIMER_SETUP > 0
|
||
static void l1_timer_expire_t1(struct timer_list *t)
|
||
#else
|
||
static void l1_timer_expire_t1(unsigned long pport)
|
||
#endif
|
||
{
|
||
#if defined(KERN_TIMER_SETUP) && KERN_TIMER_SETUP > 0
|
||
bri_xhfc_port_t *port_ptr = from_timer(port_ptr, t, t1_timer.timer_info);
|
||
#else
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
#endif
|
||
wp_bri_module_t *bri_module = port_ptr->hw;
|
||
sdla_fe_t *fe = (sdla_fe_t*)bri_module->fe;
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
wan_device_t *wandev = &card->wandev;
|
||
|
||
DEBUG_HFC_S0_STATES("%s()\n", __FUNCTION__);
|
||
|
||
if (wandev->fe_enable_timer){
|
||
|
||
wan_set_bit(T1_TIMER_EXPIRED, &port_ptr->timer_flags);
|
||
|
||
wandev->fe_enable_timer(fe->card);
|
||
}
|
||
}
|
||
|
||
static void __l1_timer_expire_t1(sdla_fe_t *fe)
|
||
{
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
u8 mod_no, port_no, connected = 0;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
wan_clear_bit(T1_TIMER_EXPIRED, &port_ptr->timer_flags);
|
||
wan_clear_bit(T1_TIMER_ACTIVE, &port_ptr->timer_flags);
|
||
|
||
if( (port_ptr->su_state.bit.v_su_info0) ||
|
||
(!port_ptr->su_state.bit.v_su_fr_sync)){
|
||
/* If receiving INFO0 or Lost Framing, after T1 expired, we must deactivate
|
||
* NT, because otherwise the user will be prevented from NT activation
|
||
* forever by the HFC_L1_ACTIVATED bit. */
|
||
connected = 0;
|
||
}else if(port_ptr->su_state.bit.v_su_fr_sync){
|
||
/* Got synchronized. No automatic state change expected. */
|
||
connected = 1;
|
||
}
|
||
|
||
if(!wan_test_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags)) {
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): (1) De-Activating NT...\n", __FUNCTION__);
|
||
xhfc_ph_command(fe, port_ptr, HFC_L1_DEACTIVATE_NT);
|
||
|
||
}else if(!connected){
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): (2) De-Activating NT...\n", __FUNCTION__);
|
||
xhfc_ph_command(fe, port_ptr, HFC_L1_DEACTIVATE_NT);
|
||
|
||
}else{
|
||
/* T1 expired AFTER line become active. */
|
||
DEBUG_HFC_S0_STATES("%s(): NT in Activated state. Doing nothing.\n", __FUNCTION__);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* l1_timer_start_t1
|
||
*/
|
||
static void l1_timer_start_t1(void *pport)
|
||
{
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
wp_bri_module_t *bri_module;
|
||
sdla_fe_t *fe;
|
||
u8 mod_no;
|
||
|
||
WAN_ASSERT_VOID(port_ptr == NULL);
|
||
|
||
WAN_ASSERT_VOID(port_ptr->hw == NULL);
|
||
bri_module = port_ptr->hw;
|
||
|
||
WAN_ASSERT_VOID(bri_module->fe == NULL);
|
||
fe = (sdla_fe_t*)bri_module->fe;
|
||
|
||
mod_no = (u8)bri_module->mod_no;
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
if (!wan_test_bit(WP_BCB_BRI_CONFIG, &fe->bri_param.critical)) {
|
||
/* may get here during unload!! */
|
||
return;
|
||
}
|
||
|
||
if (!wan_test_bit(T1_TIMER_ACTIVE, &port_ptr->timer_flags) &&
|
||
!wan_test_bit(T1_TIMER_EXPIRED, &port_ptr->timer_flags)){
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): Starting T1 timer...\n", __FUNCTION__);
|
||
|
||
wan_set_bit(T1_TIMER_ACTIVE, &port_ptr->timer_flags);
|
||
|
||
wan_add_timer(&port_ptr->t1_timer, (XHFC_TIMER_T1 * HZ) / 1000);
|
||
|
||
}else{
|
||
DEBUG_HFC_S0_STATES("%s(): the T1_TIMER_ACTIVE bit already set\n", __FUNCTION__);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* l1_timer_stop_t1
|
||
*/
|
||
static void l1_timer_stop_t1(void *pport)
|
||
{
|
||
bri_xhfc_port_t *port_ptr = (bri_xhfc_port_t*)pport;
|
||
wp_bri_module_t *bri_module = port_ptr->hw;
|
||
u8 mod_no = (u8)bri_module->mod_no;
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
if(wan_test_bit(T1_TIMER_ACTIVE, &port_ptr->timer_flags)){
|
||
|
||
wan_clear_bit(T1_TIMER_ACTIVE, &port_ptr->timer_flags);
|
||
wan_clear_bit(T1_TIMER_EXPIRED, &port_ptr->timer_flags);
|
||
|
||
wan_del_timer(&port_ptr->t1_timer);
|
||
}
|
||
}
|
||
|
||
/******************************************************************************
|
||
** wp_bri_if_config() -
|
||
**
|
||
** OK
|
||
*/
|
||
static int32_t wp_bri_if_config(void *pfe, u32 mod_map, u8 usedby)
|
||
{
|
||
BRI_FUNC();
|
||
return 0;
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
** wp_bri_if_unconfig() -
|
||
**
|
||
** OK
|
||
*/
|
||
static int32_t wp_bri_if_unconfig(void *pfe, u32 mod_map, u8 usedby)
|
||
{
|
||
BRI_FUNC();
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*******************************************************************************
|
||
* bri_enable_interrupts()
|
||
*
|
||
* Description: Enable BRI interrupts - start interrupt and set interrupt mask.
|
||
*
|
||
* Arguments:
|
||
*
|
||
* Returns:
|
||
*******************************************************************************/
|
||
static void bri_enable_interrupts(sdla_fe_t *fe, u32 mod_no, u8 port_no)
|
||
{
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
reg_r_ti_wd r_ti_wd;
|
||
reg_r_misc_irqmsk r_misc_irqmsk;
|
||
reg_r_irq_ctrl r_irq_ctrl;
|
||
reg_r_su_irqmsk r_su_irqmsk;
|
||
|
||
BRI_FUNC();
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return;
|
||
}
|
||
|
||
DEBUG_EVENT("%s: Module: %d: Enabling %s Interrupts \n",
|
||
fe->name, REPORT_MOD_NO(mod_no),
|
||
FE_MEDIA_DECODE(fe));
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
|
||
WRITE_REG(R_SU_IRQMSK, 0);
|
||
|
||
r_ti_wd.reg = 0x0;
|
||
/* Configure Timer Interrupt - every 1 sec (page 289, 297).
|
||
Used for SU port_no state monitoring + polling fifo IRQs. */
|
||
r_ti_wd.bit.v_ev_ts = 0xC;
|
||
/* Watch Dog interrupt not used */
|
||
r_ti_wd.bit.v_wd_ts = 0x0;
|
||
WRITE_REG(R_TI_WD, r_ti_wd.reg);
|
||
|
||
r_misc_irqmsk.reg = 0;
|
||
#if !defined(BUILD_MOD_TESTER)
|
||
r_misc_irqmsk.bit.v_ti_irqmsk = 1;/* Enable (one) / Disable (zero) timer interrupts */
|
||
#endif
|
||
WRITE_REG( R_MISC_IRQMSK, r_misc_irqmsk.reg);
|
||
|
||
/* clear all pending interrupts bits */
|
||
READ_REG( R_MISC_IRQ);
|
||
READ_REG( R_SU_IRQ);
|
||
READ_REG( R_FIFO_BL0_IRQ);
|
||
READ_REG( R_FIFO_BL1_IRQ);
|
||
READ_REG( R_FIFO_BL2_IRQ);
|
||
READ_REG( R_FIFO_BL3_IRQ);
|
||
|
||
/* unmask SU state interrupt for all ports */
|
||
r_su_irqmsk.reg = 0;
|
||
r_su_irqmsk.bit.v_su0_irqmsk = 1;
|
||
r_su_irqmsk.bit.v_su1_irqmsk = 1;
|
||
WRITE_REG( R_SU_IRQMSK, r_su_irqmsk.reg);
|
||
|
||
/* enable global (all) interrupts */
|
||
r_irq_ctrl.reg = 0;
|
||
r_irq_ctrl.bit.v_glob_irq_en = 1;
|
||
r_irq_ctrl.bit.v_fifo_irq_en = 1;
|
||
WRITE_REG( R_IRQ_CTRL, r_irq_ctrl.reg);
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
** wp_bri_disable_fe_irq() - disable all interrupts by disabling M_GLOB_IRQ_EN
|
||
**
|
||
** OK
|
||
*/
|
||
|
||
static int wp_bri_disable_fe_irq(void *pfe)
|
||
{
|
||
sdla_fe_t *fe = (sdla_fe_t*)pfe;
|
||
u32 mod_no;
|
||
u8 port_no;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
wp_bri_disable_irq(fe,mod_no,port_no);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/******************************************************************************
|
||
** wp_bri_disable_irq() - disable all interrupts by disabling M_GLOB_IRQ_EN
|
||
**
|
||
** OK
|
||
*/
|
||
static int32_t wp_bri_disable_irq(sdla_fe_t *fe, u32 mod_no, u8 port_no)
|
||
{
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
reg_r_su_irqmsk r_su_irqmsk;
|
||
reg_r_irq_ctrl r_irq_ctrl;
|
||
|
||
BRI_FUNC();
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return 1;
|
||
}
|
||
|
||
DEBUG_EVENT("%s: Module: %d: Disabling %s Interrupts \n",
|
||
fe->name, REPORT_MOD_NO(mod_no),
|
||
FE_MEDIA_DECODE(fe));
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
|
||
/* disable SU state interrupt for all ports */
|
||
r_su_irqmsk.reg = 0;
|
||
WRITE_REG( R_SU_IRQMSK, r_su_irqmsk.reg);
|
||
|
||
/*
|
||
if (!wan_test_bit(WP_RM_CONFIGURED,(void*)&fe->bri_param.critical)){
|
||
return -EINVAL;
|
||
}
|
||
*/
|
||
|
||
/* disable global (all) other interrupts */
|
||
r_irq_ctrl.reg = 0;
|
||
WRITE_REG( R_IRQ_CTRL, 0);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static u32 wp_bri_active_map(sdla_fe_t* fe, u8 line_no)
|
||
{
|
||
BRI_FUNC();
|
||
|
||
if(line_no >= 2){
|
||
DEBUG_ERROR("%s: %s(): Error: Line number %d is out of range!\n",
|
||
fe->name, __FUNCTION__, line_no);
|
||
return 0;
|
||
}
|
||
|
||
DEBUG_TEST("%s: ACTIVE MAP Port=%i Returning 0x%08X\n",
|
||
fe->name,
|
||
WAN_FE_LINENO(fe),
|
||
0x03 << (WAN_FE_LINENO(fe)%MAX_BRI_MODULES)*2);
|
||
|
||
return 0x03 << (WAN_FE_LINENO(fe)%MAX_BRI_MODULES)*2;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* wp_bri_fe_status()
|
||
*
|
||
* Description:
|
||
* Arguments:
|
||
* Returns:
|
||
*******************************************************************************/
|
||
static u8 wp_bri_fe_media(sdla_fe_t *fe)
|
||
{
|
||
BRI_FUNC();
|
||
return fe->fe_cfg.media;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* wp_bri_set_dtmf()
|
||
*
|
||
* Description:
|
||
* Arguments:
|
||
* Returns:
|
||
*******************************************************************************/
|
||
static int32_t wp_bri_set_dtmf(sdla_fe_t *fe, int32_t mod_no, u8 val)
|
||
{
|
||
BRI_FUNC();
|
||
#if 0
|
||
|
||
if (mod_no > MAX_REMORA_MODULES){
|
||
DEBUG_EVENT("%s: Module %d: Module number out of range!\n",
|
||
fe->name, mod_no);
|
||
return -EINVAL;
|
||
}
|
||
if (!wan_test_bit(mod_no-1, &fe->bri_param.module_map)){
|
||
DEBUG_EVENT("%s: Module %d: Not configures yet!\n",
|
||
fe->name, mod_no);
|
||
return -EINVAL;
|
||
}
|
||
|
||
#endif
|
||
return -EINVAL;
|
||
}
|
||
|
||
#if 0
|
||
|
||
/*******************************************************************************
|
||
* sdla_bri_timer()
|
||
*
|
||
* Description:
|
||
* Arguments:
|
||
* Returns:
|
||
*******************************************************************************/
|
||
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||
static void wp_bri_timer(void* pfe)
|
||
#elif defined(__WINDOWS__)
|
||
static void wp_bri_timer(IN PKDPC Dpc, void* pfe, void* arg2, void* arg3)
|
||
#else
|
||
static void wp_bri_timer(void *pfe)
|
||
#endif
|
||
{
|
||
BRI_FUNC();
|
||
return;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* wp_bri_enable_timer()
|
||
*
|
||
* Description: Enable software timer interrupt in delay ms.
|
||
* Arguments:
|
||
* Returns:
|
||
*******************************************************************************/
|
||
static void wp_bri_enable_timer(sdla_fe_t* fe, u8 mod_no, u8 cmd, u32 delay)
|
||
{
|
||
BRI_FUNC();
|
||
return;
|
||
}
|
||
|
||
static int32_t wp_bri_regdump(sdla_fe_t* fe, u8 *data)
|
||
{
|
||
BRI_FUNC();
|
||
return 0;
|
||
}
|
||
|
||
#endif /* if 0*/
|
||
|
||
static int32_t wp_bri_polling(sdla_fe_t* fe)
|
||
{
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
u8 mod_no, port_no;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
/* Note that aft_core.c calls card->wandev.fe_iface.polling()
|
||
* UNCONDITIONALLY, on each Front End interrupt.
|
||
* It is a problem for BRI timers because we need an additional flag,
|
||
* which will indicate that "T1 timer really expired".
|
||
* Only if the flag is set, we act as we should on timer expiration. */
|
||
|
||
if (port_ptr->mode & PORT_MODE_TE) {
|
||
/*************/
|
||
/* TE timers */
|
||
if(wan_test_bit(T3_TIMER_ACTIVE, &port_ptr->timer_flags)){
|
||
DEBUG_HFC_S0_STATES("%s(): TE T3_TIMER_ACTIVE\n", __FUNCTION__);
|
||
}
|
||
|
||
if(wan_test_bit(T3_TIMER_EXPIRED, &port_ptr->timer_flags)){
|
||
DEBUG_HFC_S0_STATES("%s(): TE T3_TIMER_EXPIRED\n", __FUNCTION__);
|
||
}
|
||
|
||
if (wan_test_bit(T3_TIMER_EXPIRED, &port_ptr->timer_flags) &&
|
||
wan_test_bit(T3_TIMER_ACTIVE, &port_ptr->timer_flags)){
|
||
|
||
__l1_timer_expire_t3(fe);
|
||
}
|
||
}else{
|
||
/*************/
|
||
/* NT timers */
|
||
if(wan_test_bit(T1_TIMER_ACTIVE, &port_ptr->timer_flags)){
|
||
DEBUG_HFC_S0_STATES("%s(): NT T1_TIMER_ACTIVE\n", __FUNCTION__);
|
||
}
|
||
|
||
if(wan_test_bit(T1_TIMER_EXPIRED, &port_ptr->timer_flags)){
|
||
DEBUG_HFC_S0_STATES("%s(): NT T1_TIMER_EXPIRED\n", __FUNCTION__);
|
||
}
|
||
|
||
if (wan_test_bit(T1_TIMER_EXPIRED, &port_ptr->timer_flags) &&
|
||
wan_test_bit(T1_TIMER_ACTIVE, &port_ptr->timer_flags)) {
|
||
|
||
__l1_timer_expire_t1(fe);
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* wp_bri_udp()
|
||
*
|
||
* Description:
|
||
* Arguments:
|
||
* Returns:
|
||
*******************************************************************************/
|
||
static int32_t wp_bri_udp(sdla_fe_t *fe, void* p_udp_cmd, u8* data)
|
||
{
|
||
wan_cmd_t *udp_cmd = (wan_cmd_t*)p_udp_cmd;
|
||
wan_femedia_t *fe_media;
|
||
sdla_fe_timer_event_t event;
|
||
|
||
BRI_FUNC();
|
||
memset(&event, 0, sizeof(sdla_fe_timer_event_t));
|
||
switch(udp_cmd->wan_cmd_command){
|
||
case WAN_GET_MEDIA_TYPE:
|
||
fe_media = (wan_femedia_t*)data;
|
||
memset(fe_media, 0, sizeof(wan_femedia_t));
|
||
fe_media->media = fe->fe_cfg.media;
|
||
fe_media->sub_media = fe->fe_cfg.sub_media;
|
||
fe_media->chip_id = 0x00;
|
||
fe_media->max_ports = 1;
|
||
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
||
udp_cmd->wan_cmd_data_len = sizeof(wan_femedia_t);
|
||
break;
|
||
default:
|
||
udp_cmd->wan_cmd_return_code = WAN_UDP_INVALID_CMD;
|
||
udp_cmd->wan_cmd_data_len = 0;
|
||
break;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/******************************************************************************
|
||
*wp_bri_get_fe_status()
|
||
*
|
||
* Description : Get current FE line state - is it Connected or Disconnected
|
||
*
|
||
* Arguments : fe - pointer to Front End structure.
|
||
* status - pointer to location where the FE line state will
|
||
* be stored.
|
||
* notused - ignored
|
||
*
|
||
* Returns : always zero.
|
||
*******************************************************************************/
|
||
static int wp_bri_get_fe_status(sdla_fe_t *fe, unsigned char *status, int notused)
|
||
{
|
||
*status = fe->fe_status;
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int bchan_loopback_control(sdla_fe_t *fe, u8 bchan_no, u8 loopback_enable)
|
||
{
|
||
u8 mod_no, port_no, pcm_slot;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
reg_a_sl_cfg a_sl_cfg;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
|
||
DEBUG_LOOPB("%s()\n", __FUNCTION__);
|
||
|
||
if (!((bchan_no == 0) || (bchan_no == 1))) {
|
||
DEBUG_LOOPB("%s %s(): port_no(%i) ERROR: bchan_no(%i) invalid!\n",
|
||
fe->name, __FUNCTION__, port_no, bchan_no);
|
||
return 1;
|
||
}
|
||
|
||
DEBUG_LOOPB("%s %s(): %s loopback, port_no(%i), bchan_no(%i)\n",
|
||
fe->name, __FUNCTION__,
|
||
(loopback_enable) ? ("enable") : ("disable"), port_no, bchan_no);
|
||
|
||
if(mod_no >= MAX_BRI_MODULES){
|
||
/* adjust mod_no to be between 0 and 10 (including)*/
|
||
pcm_slot = (u8)calculate_pcm_timeslot(mod_no - MAX_BRI_MODULES, port_no, bchan_no);
|
||
/* AFT Line 1 will use odd PCM timeslots */
|
||
pcm_slot += 1;
|
||
}else{
|
||
/* AFT Line 0 will use even PCM timeslots */
|
||
pcm_slot = (u8)calculate_pcm_timeslot(mod_no, port_no, bchan_no);
|
||
}
|
||
|
||
DEBUG_LOOPB("selecting pcm_slot: %i, HFC channel: %i\n", pcm_slot, port_no*4+bchan_no);
|
||
|
||
/*****************************************************************************************/
|
||
/* transmit slot - select direction TX */
|
||
xhfc_select_pcm_slot(fe, mod_no, pcm_slot, XHFC_DIRECTION_TX);
|
||
|
||
/* Connect time slot with channel and pin.
|
||
Assign HFC channel (from 0 to 15) to the selected PCM slot.*/
|
||
|
||
a_sl_cfg.reg = 0;
|
||
|
||
a_sl_cfg.bit.v_ch_sdir = 0;
|
||
a_sl_cfg.bit.v_ch_snum = port_no*4+bchan_no;/*page 75 */
|
||
a_sl_cfg.bit.v_rout = (loopback_enable ? 0x01:0x03);/* page 257 */
|
||
|
||
WRITE_REG(A_SL_CFG, a_sl_cfg.reg);
|
||
|
||
|
||
/*****************************************************************************************/
|
||
/* receive slot - select direction RX */
|
||
xhfc_select_pcm_slot(fe, mod_no, pcm_slot, XHFC_DIRECTION_RX);
|
||
|
||
/* Connect time slot with channel and pin.
|
||
Assign HFC channel (from 0 to 15) to the selected PCM slot. */
|
||
a_sl_cfg.reg = 0;
|
||
|
||
a_sl_cfg.bit.v_ch_sdir = 1;
|
||
a_sl_cfg.bit.v_ch_snum = port_no*4+bchan_no;/*page 75 */
|
||
a_sl_cfg.bit.v_rout = (loopback_enable ? 0x01:0x03);/* page 257 */
|
||
|
||
WRITE_REG(A_SL_CFG,a_sl_cfg.reg);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Commands from user. */
|
||
static int wp_bri_control(sdla_fe_t *fe, u32 command)
|
||
{
|
||
u8 mod_no, port_no;
|
||
int rc;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
DEBUG_LOOPB("%s(): Module: %d, port_no: %d. fe->name: %s, command: %i\n",
|
||
__FUNCTION__, mod_no, port_no, fe->name, command);
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
switch(command)
|
||
{
|
||
case HFC_L1_ENABLE_LOOP_B1:
|
||
DEBUG_LOOPB("HFC_L1_ENABLE_LOOP_B1\n");
|
||
rc = bchan_loopback_control(fe, 0, 1);
|
||
break;
|
||
|
||
case HFC_L1_ENABLE_LOOP_B2:
|
||
DEBUG_LOOPB("HFC_L1_ENABLE_LOOP_B2\n");
|
||
rc = bchan_loopback_control(fe, 1, 1);
|
||
break;
|
||
|
||
case HFC_L1_DISABLE_LOOP_B1:
|
||
DEBUG_LOOPB("HFC_L1_DISABLE_LOOP_B1\n");
|
||
rc = bchan_loopback_control(fe, 0, 0);
|
||
break;
|
||
|
||
case HFC_L1_DISABLE_LOOP_B2:
|
||
DEBUG_LOOPB("HFC_L1_DISABLE_LOOP_B2\n");
|
||
rc = bchan_loopback_control(fe, 1, 0);
|
||
break;
|
||
|
||
default:
|
||
DEBUG_ERROR("%s(): %s: Error: invalid command '%i'requested!\n",
|
||
__FUNCTION__, fe->name, command);
|
||
rc = 1;
|
||
break;
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
* wp_bri_set_fe_status()
|
||
*
|
||
* Description : Set FE line state to Connected or Disconnected.
|
||
* In BRI this means Activate or Deactivate the line.
|
||
*
|
||
* Arguments : fe - pointer to Front End structure.
|
||
* new_status - the new FE line state.
|
||
*
|
||
* Returns : 0 - success.
|
||
* 1 - failure.
|
||
*******************************************************************************/
|
||
static int wp_bri_set_fe_status(sdla_fe_t *fe, unsigned char new_status)
|
||
{
|
||
u8 mod_no, port_no;
|
||
int rc = 0;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
bri_xhfc_port_t *port_ptr;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
DEBUG_FE_STATUS("%s(): Module: %d, port_no: %d. fe->name: %s, new status: %d (%s)\n",
|
||
__FUNCTION__, mod_no, port_no, fe->name, new_status, FE_STATUS_DECODE(new_status));
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
#if defined (BUILD_MOD_TESTER)
|
||
sdla_bri_set_status(fe, mod_no, port_no, FE_CONNECTED);
|
||
return 0;
|
||
#endif
|
||
|
||
switch(new_status)
|
||
{
|
||
case WAN_FE_CONNECTED:
|
||
DEBUG_L2_TO_L1_ACTIVATION("%s: L2->L1 -- ACTIVATE REQUEST\n",
|
||
fe->name);
|
||
if (port_ptr->mode & PORT_MODE_TE) {
|
||
if (wan_test_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags)) {
|
||
/* The line is already in active state. Confirm to L2 that line is connected. */
|
||
DEBUG_L2_TO_L1_ACTIVATION("%s: TE: L1->L2 -- ACTIVATE CONFIRM (line already active)\n",
|
||
fe->name);
|
||
sdla_bri_set_status(fe, mod_no, port_no, FE_CONNECTED);
|
||
} else {
|
||
wan_test_and_set_bit(HFC_L1_ACTIVATING, &port_ptr->l1_flags);
|
||
xhfc_ph_command(fe, port_ptr, HFC_L1_ACTIVATE_TE);
|
||
l1_timer_start_t3(port_ptr);
|
||
}
|
||
} else {
|
||
if(wan_test_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags)) {
|
||
/* The line is already in active state. Confirm to L2 that line is connected. */
|
||
DEBUG_L2_TO_L1_ACTIVATION("%s: NT: L1->L2 -- ACTIVATE CONFIRM (line already active)\n",
|
||
fe->name);
|
||
sdla_bri_set_status(fe, mod_no, port_no, FE_CONNECTED);
|
||
} else {
|
||
xhfc_ph_command(fe, port_ptr, HFC_L1_ACTIVATE_NT);
|
||
/* After activation, state will automatically change to G2, where
|
||
* T1 will be started. Don't start T1 here as recommended by Table 5.4. */
|
||
}
|
||
}
|
||
break;
|
||
|
||
case WAN_FE_DISCONNECTED:
|
||
DEBUG_L2_TO_L1_ACTIVATION("%s: L2->L1 -- DEACTIVATE REQUEST\n",
|
||
fe->name);
|
||
if (port_ptr->mode & PORT_MODE_TE) {
|
||
/* no deact request in TE mode ! */
|
||
DEBUG_ERROR("%s(): %s: Error: 'deactivate' request is invalid for TE!\n",
|
||
__FUNCTION__, fe->name);
|
||
rc = 1;
|
||
} else {
|
||
xhfc_ph_command(fe, port_ptr, HFC_L1_DEACTIVATE_NT);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
DEBUG_ERROR("%s(): %s: Error: invalid new status '%d' (%s) requested!\n",
|
||
__FUNCTION__, fe->name, new_status, FE_STATUS_DECODE(new_status));
|
||
rc = 1;
|
||
break;
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
* wp_bri_event_ctrl()
|
||
*
|
||
* Description: Enable/Disable event types
|
||
* Arguments: mod_no - Module number (1,2,3,... MAX_REMORA_MODULES)
|
||
* Returns:
|
||
******************************************************************************/
|
||
static int wp_bri_event_ctrl(sdla_fe_t *fe, wan_event_ctrl_t *ectrl)
|
||
{
|
||
int err = 1;
|
||
|
||
BRI_FUNC();
|
||
|
||
WAN_ASSERT(ectrl == NULL);
|
||
|
||
DEBUG_LOOPB("%s: Event Type: %s, Mode: %s.\n",
|
||
fe->name, WAN_EVENT_TYPE_DECODE(ectrl->type),
|
||
WAN_EVENT_MODE_DECODE(ectrl->mode));
|
||
|
||
switch(ectrl->type)
|
||
{
|
||
case WAN_EVENT_BRI_CHAN_LOOPBACK:
|
||
switch(ectrl->channel)
|
||
{
|
||
case WAN_BRI_BCHAN1:
|
||
err = wp_bri_control(fe, (ectrl->mode == WAN_EVENT_ENABLE ? HFC_L1_ENABLE_LOOP_B1:HFC_L1_DISABLE_LOOP_B1));
|
||
break;
|
||
case WAN_BRI_BCHAN2:
|
||
err = wp_bri_control(fe, (ectrl->mode == WAN_EVENT_ENABLE ? HFC_L1_ENABLE_LOOP_B2:HFC_L1_DISABLE_LOOP_B2));
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* wp_bri_watchdog()
|
||
*
|
||
* Description:
|
||
* Arguments: mod_no - Module number (1,2,3,... MAX_REMORA_MODULES)
|
||
* Returns:
|
||
******************************************************************************/
|
||
#if 0
|
||
static int32_t wp_bri_watchdog(sdla_fe_t *fe)
|
||
{
|
||
BRI_FUNC();
|
||
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
/******************************************************************************
|
||
* wp_bri_intr_ctrl()
|
||
*
|
||
* Description: Enable/Disable extra interrupt types
|
||
* Arguments: mod_no - Module number (1,2,3,... MAX_REMORA_MODULES)
|
||
* Returns:
|
||
******************************************************************************/
|
||
static int wp_bri_intr_ctrl(sdla_fe_t *fe, int mod_no, u_int8_t type, u_int8_t mode, unsigned int ts_map)
|
||
{
|
||
int32_t err = 0;
|
||
|
||
BRI_FUNC();
|
||
|
||
return err;
|
||
|
||
}
|
||
|
||
/****************************************************/
|
||
/* Physical S/U commands to control Line Interface */
|
||
/****************************************************/
|
||
static void xhfc_ph_command(sdla_fe_t *fe, bri_xhfc_port_t *port, u_char command)
|
||
{
|
||
wp_bri_module_t *bri_module = port->hw;
|
||
u8 mod_no = (u8)bri_module->mod_no;
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): command: 0x%X\n", __FUNCTION__, command);
|
||
|
||
switch (command)
|
||
{
|
||
case HFC_L1_ACTIVATE_TE:
|
||
DEBUG_L2_TO_L1_ACTIVATION("HFC_L1_ACTIVATE_TE port(%i)\n", port->idx);
|
||
|
||
WRITE_REG(R_SU_SEL, port->idx);
|
||
WRITE_REG(A_SU_WR_STA, STA_ACTIVATE);
|
||
break;
|
||
|
||
case HFC_L1_FORCE_DEACTIVATE_TE:
|
||
DEBUG_L2_TO_L1_ACTIVATION("HFC_L1_FORCE_DEACTIVATE_TE port(%i)\n", port->idx);
|
||
|
||
WRITE_REG(R_SU_SEL, port->idx);
|
||
WRITE_REG(A_SU_WR_STA, STA_DEACTIVATE);
|
||
break;
|
||
|
||
case HFC_L1_ACTIVATE_NT:
|
||
DEBUG_L2_TO_L1_ACTIVATION("HFC_L1_ACTIVATE_NT port(%i)\n", port->idx);
|
||
|
||
WRITE_REG(R_SU_SEL, port->idx);
|
||
WRITE_REG(A_SU_WR_STA, STA_ACTIVATE | M_SU_SET_G2_G3);
|
||
break;
|
||
|
||
case HFC_L1_DEACTIVATE_NT:
|
||
DEBUG_L2_TO_L1_ACTIVATION("HFC_L1_DEACTIVATE_NT port(%i)\n", port->idx);
|
||
|
||
WRITE_REG(R_SU_SEL, port->idx);
|
||
WRITE_REG(A_SU_WR_STA, STA_DEACTIVATE);
|
||
break;
|
||
|
||
default:
|
||
DEBUG_L2_TO_L1_ACTIVATION("Invalid command: %i !\n", command);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/******************************************************************************
|
||
* sdla_bri_set_status()
|
||
*
|
||
* Description: set line status to 'connected' or 'disconnected' and indicate
|
||
* line state change to upper layer.
|
||
* Arguments: fe, mod_no, port_no, new line status
|
||
* Returns: nothing
|
||
******************************************************************************/
|
||
static void sdla_bri_set_status(sdla_fe_t* fe, u8 mod_no, u8 port_no, u8 new_status)
|
||
{
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
|
||
BRI_FUNC();
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): new_status: %i, old status: %i\n",
|
||
__FUNCTION__, new_status, fe->fe_status);
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return;
|
||
}
|
||
|
||
if (new_status == fe->fe_status){
|
||
return;
|
||
}
|
||
|
||
fe->fe_status = new_status;
|
||
|
||
if (new_status == FE_CONNECTED){
|
||
|
||
DEBUG_EVENT("%s: %s Module: %d connected!\n",
|
||
fe->name,
|
||
FE_MEDIA_DECODE(fe), REPORT_MOD_NO(mod_no) + port_no);
|
||
|
||
if (card->wandev.te_report_alarms){
|
||
card->wandev.te_report_alarms(card, 0);
|
||
}
|
||
}else{
|
||
|
||
DEBUG_EVENT("%s: %s Module: %d disconnected!\n",
|
||
fe->name,
|
||
FE_MEDIA_DECODE(fe), REPORT_MOD_NO(mod_no) + port_no);
|
||
|
||
if (card->wandev.te_report_alarms){
|
||
card->wandev.te_report_alarms(card, (1|WAN_TE_BIT_ALARM_RED));
|
||
}
|
||
}
|
||
|
||
if (card->wandev.te_link_state){
|
||
card->wandev.te_link_state(card);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
/******************************************************************************
|
||
* su_new_state()
|
||
*
|
||
* Description: handle SU port state interrupt on a physical module
|
||
*
|
||
* SU port state interrupt notes:
|
||
* 1. Chip automatically goes into inactive state if:
|
||
* 1.1 line is disconnected
|
||
* 1.2 line is deactivated
|
||
*
|
||
* 2. Because of (1) user application will have to activate the line,
|
||
* wait for the line to get 'connected' for about 1 second and if
|
||
* after 1 second line is not getting 'connected', it means line is
|
||
* actually disconnected and NOT simply deactivated.
|
||
*
|
||
* Arguments: fe, mod_no
|
||
*
|
||
* Returns: nothing
|
||
******************************************************************************/
|
||
static void su_new_state(sdla_fe_t *fe, u8 mod_no, u8 port_no)
|
||
{
|
||
bri_xhfc_port_t *port;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
u8 connected = 0;
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return;
|
||
}
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port = &bri_module->port[port_no];
|
||
|
||
connected = __su_new_state(fe, mod_no, port_no);
|
||
if(connected == 1){
|
||
sdla_bri_set_status(fe, mod_no, port_no, FE_CONNECTED);
|
||
}else{
|
||
sdla_bri_set_status(fe, mod_no, port_no, FE_DISCONNECTED);
|
||
}
|
||
}
|
||
|
||
|
||
static u8 __su_new_state(sdla_fe_t *fe, u8 mod_no, u8 port_no)
|
||
{
|
||
bri_xhfc_port_t *port_ptr;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
u8 connected = 0;
|
||
u8 current_fe_status = fe->fe_status;
|
||
|
||
|
||
BRI_FUNC();
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return connected;
|
||
}
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
port_ptr = &bri_module->port[port_no];
|
||
|
||
DEBUG_HFC_S0_STATES("%s(): mod_no: %i, port number: %i\n", __FUNCTION__, mod_no, port_ptr->idx);
|
||
|
||
if (port_ptr->mode & PORT_MODE_TE) {
|
||
DEBUG_HFC_S0_STATES("TE F%d\n", port_ptr->l1_state);
|
||
|
||
if ((port_ptr->l1_state <= 3) || (port_ptr->l1_state >= 7)){
|
||
l1_timer_stop_t3(port_ptr);
|
||
}
|
||
|
||
switch (port_ptr->l1_state)
|
||
{
|
||
case (3):
|
||
if (wan_test_and_clear_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags)){
|
||
/* Do NOT indicate 'disconnect' right away, do it when
|
||
T4 expires. */
|
||
if (fe->fe_status == FE_CONNECTED){
|
||
connected = 1; /* keep the old state */
|
||
}
|
||
l1_timer_start_t4(port_ptr);
|
||
}
|
||
return connected;
|
||
|
||
case (7):
|
||
l1_timer_stop_t4(port_ptr);
|
||
connected = 1;
|
||
|
||
if (wan_test_and_clear_bit(HFC_L1_ACTIVATING, &port_ptr->l1_flags)) {
|
||
DEBUG_HFC_S0_STATES("l1->l2 -- ACTIVATE CONFIRM\n");
|
||
|
||
wan_set_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags);
|
||
|
||
} else {
|
||
if (!(wan_test_and_set_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags))) {
|
||
DEBUG_HFC_S0_STATES("l1->l2 -- ACTIVATE INDICATION\n");
|
||
} else {
|
||
/* L1 was already activated (e.g. F8->F7) */
|
||
return connected;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case (8):/* framing is lost but not a disconnect yet */
|
||
l1_timer_stop_t4(port_ptr);
|
||
connected = 1;
|
||
return connected;
|
||
|
||
case (6):/* synchronized */
|
||
connected = 1;
|
||
return connected;
|
||
|
||
default:
|
||
return connected;
|
||
}
|
||
|
||
} else if (port_ptr->mode & PORT_MODE_NT) {
|
||
|
||
DEBUG_HFC_S0_STATES("NT G%d\n", port_ptr->l1_state);
|
||
|
||
/* S/T state transition based Cologne recomendations:
|
||
* T1 is a counter that can be reset. An other stop function is not needed.
|
||
* There are only 3 states of T1:
|
||
* 1. Reset permanent (Stop)
|
||
* 2. Running
|
||
* 3. Expire
|
||
*
|
||
* The easiest way to implement T1 is:
|
||
* state action for T1
|
||
* 0,1 T1 Reset
|
||
* 2 T1 Running
|
||
* 3,4 T1 Reset
|
||
*/
|
||
|
||
if(port_ptr->l1_state != NT_STATE_PENDING_ACTIVATION_G2){
|
||
l1_timer_stop_t1(port_ptr);
|
||
}
|
||
|
||
/* S/T state transitions based on Table 5.4 */
|
||
switch (port_ptr->l1_state)
|
||
{
|
||
case NT_STATE_RESET_G0:
|
||
case NT_STATE_DEACTIVATED_G1:
|
||
case NT_STATE_PENDING_DEACTIVATION_G4:
|
||
wan_clear_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags);
|
||
connected = 0;
|
||
break;
|
||
|
||
case NT_STATE_PENDING_ACTIVATION_G2:
|
||
/* Start 10 seconds software timer T1. If already started, function has no effect. */
|
||
l1_timer_start_t1(port_ptr);
|
||
|
||
if(!port_ptr->su_state.bit.v_su_info0){
|
||
/* We are NOT receiving INFO0 and very likely we are receiving INFO3.
|
||
* That means link is on the way UP. Next state should be G3.
|
||
*
|
||
* Automatic G2->G3 transition is allowed by init_xfhc() -
|
||
* V_G2_G3_EN was set in the register A_SU_CTRL1.*/
|
||
}else{
|
||
/* Link on the way DOWN, but no automatic state change.
|
||
* The state change will be triggered by T1 expiration,
|
||
* where the line is deactivated. */
|
||
wan_clear_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags);
|
||
}
|
||
|
||
/* In any case, do NOT change the line state. */
|
||
connected = (current_fe_status == FE_CONNECTED ? 1 : 0);
|
||
break;
|
||
|
||
case NT_STATE_ACTIVE_G3:
|
||
if( (port_ptr->su_state.bit.v_su_info0) ||
|
||
(!port_ptr->su_state.bit.v_su_fr_sync)){
|
||
/* If receiving INFO0 or Lost Framing, chip will automatically go into G2. */
|
||
/* Wait for G2 to go into disconnected state, so here stay 'connected'. */
|
||
wan_set_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags);
|
||
connected = 1;
|
||
}else if(port_ptr->su_state.bit.v_su_fr_sync){
|
||
/* Got synchronized. No automatic state change expected. */
|
||
wan_set_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags);
|
||
connected = 1;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
DEBUG_ERROR("%s: Error: invalid NT State: %d.\n", fe->name, port_ptr->l1_state);
|
||
break;
|
||
}
|
||
|
||
if(connected == 1){
|
||
DEBUG_HFC_S0_STATES("NT: l1->l2 -- ACTIVATE INDICATION\n");
|
||
}else{
|
||
DEBUG_HFC_S0_STATES("NT: l1->l2 (PH_DEACTIVATE | INDICATION)\n");
|
||
}
|
||
}
|
||
|
||
return connected;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* get_FE_ptr_for_port()
|
||
*
|
||
* Description : get pointer to FE structure belonging to a 'port_no' on
|
||
* module 'mod_no'.
|
||
* Allows to handle FE interrupt of ALL ports on the SAME
|
||
* physical module.
|
||
* Note: the returned pointer is NOT the same as the pointer
|
||
* in 'wp_bri_module_t->fe' because this function returns
|
||
* 'fe' related to the 'card'.
|
||
*
|
||
* Arguments : fe, mod_no, port_no
|
||
*
|
||
* Returns : fe pointer - found FE for the 'port_no'.
|
||
* NULL - FE for the 'port_no' not found (it means port_no
|
||
* is not used).
|
||
*
|
||
******************************************************************************/
|
||
sdla_fe_t *get_FE_ptr_for_port(sdla_fe_t *original_fe, u8 mod_no, u8 port_no)
|
||
{
|
||
sdla_t *card = (sdla_t*)original_fe->card;
|
||
sdla_t *tmp_card;
|
||
void **card_list;
|
||
|
||
BRI_FUNC();
|
||
|
||
if(!card || !card->hw){
|
||
DEBUG_HFC_IRQ("%s(): card: 0x%p!!\n", __FUNCTION__, card);
|
||
return NULL;
|
||
}
|
||
DEBUG_HFC_IRQ("%s(): mod: %d, port_no: %d (sum: %d)\n",
|
||
__FUNCTION__, mod_no, port_no, mod_no + port_no);
|
||
|
||
card_list=__sdla_get_ptr_isr_array(card->hw);
|
||
/*
|
||
{
|
||
int i;
|
||
for(i = 0; i < SDLA_MAX_PORTS; i++){
|
||
DEBUG_HFC_IRQ("card_list[%d]: 0x%p\n", i, card_list[i]);
|
||
}
|
||
}
|
||
*/
|
||
tmp_card=(sdla_t*)card_list[mod_no + port_no];
|
||
|
||
DEBUG_HFC_IRQ("%s(): card_list ptr: 0x%p\n", __FUNCTION__, card_list);
|
||
DEBUG_HFC_IRQ("%s(): card ptr: 0x%p, tmp_card ptr: 0x%p\n", __FUNCTION__, card, tmp_card);
|
||
|
||
if (!tmp_card){
|
||
return NULL;
|
||
}
|
||
|
||
return &tmp_card->fe;
|
||
}
|
||
|
||
/******************************************************************************
|
||
* xhfc_interrupt()
|
||
*
|
||
* Description : handle interrupt on a PHYSICAL module
|
||
*
|
||
* Arguments : fe, mod_no
|
||
*
|
||
* Returns : 1 - interrupt recognized and handled
|
||
* 0 - interrupt not recognized (not generated by this module)
|
||
*
|
||
******************************************************************************/
|
||
static int32_t xhfc_interrupt(sdla_fe_t *fe, u8 mod_no)
|
||
{
|
||
sdla_fe_t *new_fe;
|
||
uint32_t fifo_irq;
|
||
sdla_bri_param_t *bri = &fe->bri_param;
|
||
wp_bri_module_t *bri_module;
|
||
u8 port_no, i;
|
||
reg_a_su_rd_sta new_su_state;
|
||
reg_r_su_irq r_su_irq;
|
||
reg_r_misc_irq r_misc_irq;
|
||
uint32_t rx_chan_bit;
|
||
|
||
if(validate_physical_mod_no(mod_no, __FUNCTION__)){
|
||
return 0;
|
||
}
|
||
|
||
bri_module = &bri->mod[mod_no];
|
||
|
||
#if 0
|
||
DEBUG_HFC_SU_IRQ("%s(%lu): %s: mod_no: %d\n", __FUNCTION__, jiffies, fe->name, mod_no);
|
||
#endif
|
||
|
||
/* clear SU state interrupt (for all ports) */
|
||
r_su_irq.reg = READ_REG(R_SU_IRQ);
|
||
|
||
/* clear 'misc' interrupts such as timer interrupt */
|
||
r_misc_irq.reg = READ_REG(R_MISC_IRQ);
|
||
|
||
/***************************************************************************/
|
||
|
||
fifo_irq = 0;
|
||
for (i = 0; i < bri_module->num_ports; i++)
|
||
{
|
||
new_fe = get_FE_ptr_for_port(fe, mod_no, i);
|
||
|
||
if(new_fe == NULL){
|
||
/* 'port_no' is not used by any 'wanpipe' */
|
||
continue;
|
||
}
|
||
|
||
fe = new_fe;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
DBG_MODULE_TESTER("get fifo IRQ state for port_no %d\n", port_no);
|
||
|
||
/* get fifo IRQ states in bundle */
|
||
fifo_irq |= (READ_REG(R_FIFO_BL0_IRQ + port_no) << (port_no * 8));
|
||
|
||
DBG_MODULE_TESTER("fifo_irq: 0x%X\n", fifo_irq);
|
||
|
||
}
|
||
|
||
if (bri_module->prev_fifoirq != fifo_irq)
|
||
{
|
||
DEBUG_HFC_IRQ("%s():%s: fifo IRQ: now = 0x%04X, prev = 0x%04X (%d)\n",
|
||
__FUNCTION__, fe->name,
|
||
fifo_irq, bri_module->prev_fifoirq, bri_module->num_ports);
|
||
}
|
||
|
||
/***************************************************************************/
|
||
for (i = 0; i < bri_module->num_ports; i++) {
|
||
|
||
/****************************************************************/
|
||
new_fe = get_FE_ptr_for_port(fe, mod_no, i);
|
||
DEBUG_HFC_IRQ("%s(): fe ptr: 0x%p\n", __FUNCTION__, fe);
|
||
if(new_fe == NULL){
|
||
/* 'port_no' is not used by any 'wanpipe' */
|
||
continue;
|
||
}
|
||
fe = new_fe;
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
rx_chan_bit = 1 << (port_no*8+5);
|
||
|
||
/****************************************************************/
|
||
|
||
if(r_misc_irq.reg & M_TI_IRQ){
|
||
/*sdla_bri_param_t *su_bri = &fe->bri_param;
|
||
wp_bri_module_t *su_bri_module = &su_bri->mod[mod_no];
|
||
bri_xhfc_port_t *port_ptr = &su_bri_module->port[port_no];*/
|
||
|
||
/*DEBUG_HFC_SU_IRQ("Timer IRQ\n");*/
|
||
}
|
||
|
||
/****************************************************************/
|
||
|
||
/* select the port on the chip */
|
||
WRITE_REG(R_SU_SEL, port_no);
|
||
new_su_state.reg = READ_REG(A_SU_RD_STA);
|
||
|
||
DEBUG_HFC_SU_IRQ("%s(): SU State: sta:%d, v_su_fr_sync: %d, v_su_t2_exp: %d, v_su_info0: %d, v_g2_g3: %d\n",
|
||
__FUNCTION__,
|
||
new_su_state.bit.v_su_sta, new_su_state.bit.v_su_fr_sync, new_su_state.bit.v_su_t2_exp,
|
||
new_su_state.bit.v_su_info0, new_su_state.bit.v_g2_g3);
|
||
|
||
if((r_su_irq.reg & (1 << port_no)) || (r_misc_irq.reg & M_TI_IRQ)){
|
||
sdla_bri_param_t *su_bri = &fe->bri_param;
|
||
wp_bri_module_t *su_bri_module = &su_bri->mod[mod_no];
|
||
bri_xhfc_port_t *port_ptr = &su_bri_module->port[port_no];
|
||
|
||
if (new_su_state.bit.v_su_sta != port_ptr->l1_state) {
|
||
|
||
DEBUG_HFC_S0_STATES("%s():SU IRQ:%lu: %s: SU State: 0x%X, v_su_fr_sync: %d, v_su_info0: %d, v_g2_g3: %d\n",
|
||
__FUNCTION__, jiffies,
|
||
(port_ptr->mode & PORT_MODE_NT) ? "NT: G" : "TE: F",
|
||
new_su_state.bit.v_su_sta, new_su_state.bit.v_su_fr_sync,
|
||
new_su_state.bit.v_su_info0, new_su_state.bit.v_g2_g3);
|
||
|
||
#if 0
|
||
if (port_ptr->mode & PORT_MODE_TE) {
|
||
DEBUG_TE_STATES("v_su_sta: 0x%02X (%s), v_su_fr_sync: %d, v_su_info0: %d, v_g2_g3: %d\n",
|
||
new_su_state.bit.v_su_sta, WP_DECODE_TE_STATE(new_su_state.bit.v_su_sta),
|
||
new_su_state.bit.v_su_fr_sync,
|
||
new_su_state.bit.v_su_info0,
|
||
new_su_state.bit.v_g2_g3);
|
||
}
|
||
#endif
|
||
port_ptr->l1_state = new_su_state.bit.v_su_sta;
|
||
port_ptr->su_state = new_su_state;
|
||
/* Handle S/U state change. */
|
||
su_new_state(fe, mod_no, port_no);
|
||
}
|
||
|
||
}/* if(r_misc_irq.reg & M_TI_IRQ || (r_su_irq.reg & (1 << port_no))) */
|
||
|
||
|
||
/* receive D-Channel Data */
|
||
|
||
if (((fifo_irq | bri_module->prev_fifoirq) & rx_chan_bit) || (r_misc_irq.reg & M_TI_IRQ)) {
|
||
uint32_t rx_chan_pending = 0;
|
||
u8 rx_read_skb = 0;
|
||
|
||
for (;;) {
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
private_area_t *chan;
|
||
|
||
netskb_t *skb = wp_bri_dchan_rx(fe, mod_no, port_no, &rx_chan_pending);
|
||
|
||
if (!skb) break;
|
||
|
||
DEBUG_HFC_IRQ("%s(): Module: %d, port_no: %d.\n", __FUNCTION__, mod_no, port_no);
|
||
chan=(private_area_t*)card->u.aft.dev_to_ch_map[BRI_DCHAN_LOGIC_CHAN];
|
||
|
||
DEBUG_HFC_IRQ("%s(): chan ptr: 0x%p\n", __FUNCTION__, chan);
|
||
if (!chan){
|
||
DEBUG_ERROR("%s: Error: BRI D-Chan: No Device for Rx data.(logical ch=%d)\n",
|
||
card->devname, BRI_DCHAN_LOGIC_CHAN);
|
||
break;
|
||
}
|
||
wan_skb_queue_tail(&chan->wp_rx_bri_dchan_complete_list, skb);
|
||
|
||
if (skb || rx_chan_pending) {
|
||
rx_read_skb = 1;
|
||
}
|
||
}
|
||
|
||
if (rx_read_skb) {
|
||
sdla_t *card = (sdla_t*)fe->card;
|
||
private_area_t *chan=(private_area_t*)card->u.aft.dev_to_ch_map[BRI_DCHAN_LOGIC_CHAN];
|
||
|
||
DEBUG_HFC_IRQ("%s(): chan ptr: 0x%p\n", __FUNCTION__, chan);
|
||
if (!chan){
|
||
DEBUG_ERROR("%s: Error: BRI D-Chan: No Device for Rx data.(logical ch=%d)\n",
|
||
card->devname, BRI_DCHAN_LOGIC_CHAN);
|
||
break;
|
||
}
|
||
|
||
WAN_TASKLET_SCHEDULE((&chan->common.bh_task));
|
||
}
|
||
|
||
if (rx_read_skb || rx_chan_pending) {
|
||
bri_module->prev_fifoirq |= rx_chan_bit;
|
||
} else {
|
||
bri_module->prev_fifoirq &= ~rx_chan_bit;
|
||
}
|
||
}
|
||
|
||
if(bri_module->port[port_no].bytes2transmit){
|
||
u8 free_space;
|
||
|
||
TX_FAST_DBG("%s(): port_no: %d, bytes2transmit: %d, dtx_indx: %d\n",
|
||
__FUNCTION__, port_no, bri_module->port[port_no].bytes2transmit,
|
||
bri_module->port[port_no].dtx_indx);
|
||
|
||
xhfc_write_fifo_dchan(fe, mod_no, bri_module, &bri_module->port[port_no], &free_space);
|
||
}
|
||
}
|
||
/***************************************************************************/
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int32_t wp_bri_check_intr(sdla_fe_t *fe)
|
||
{
|
||
/* must return 1! */
|
||
return 1;
|
||
}
|
||
|
||
static int32_t wp_bri_intr(sdla_fe_t *fe)
|
||
{
|
||
u8 mod_no, port_no;
|
||
int32_t interrupt_serviced = 0;
|
||
|
||
BRI_FUNC();
|
||
|
||
if (!wan_test_bit(WP_BCB_BRI_CONFIG, &fe->bri_param.critical)) {
|
||
return 0;
|
||
}
|
||
|
||
mod_no = fe_line_no_to_physical_mod_no(fe);
|
||
port_no = fe_line_no_to_port_no(fe);
|
||
|
||
switch(fe->bri_param.mod[mod_no].type)
|
||
{
|
||
case MOD_TYPE_TE:
|
||
case MOD_TYPE_NT:
|
||
if(xhfc_interrupt(fe, mod_no)){
|
||
/* at least one module generated an interrupt */
|
||
interrupt_serviced = 1;
|
||
}
|
||
break;
|
||
default:
|
||
/* for missing (not installed) modules - do nothing */
|
||
break;
|
||
}
|
||
|
||
return interrupt_serviced;
|
||
}
|
||
|
||
|
||
|