wanpipe/patches/kdrivers/src/net/sdla_bri.c

3834 lines
109 KiB
C
Raw Blame History

/***************************************************************************
* 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;
}