#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h> 
#include <linux/semaphore.h> 
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h> 
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include "bspchip.h"
#include "ptm_regs.h"
#include "ptm_rtl8685.h"


#define PTM_RESET 1 

#ifdef PTM_RESET
#define PTM_TX_RESET 1

#if defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB)
#define SACHEM_TX_RESET
#endif /* CONFIG_RTL8685S */

#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/sched.h>
#endif


/* Prototype */
static int ptm_open( struct inode *inode, struct file *filp );
static int ptm_close( struct inode *inode, struct file *filp );
static long ptm_ioctl(struct file *flip,
    unsigned int command, unsigned long inarg );
static void rtl8685_set_ptm_counter(void);
static void ptm_init_hw (void);
void ptm_start_hw (void);
void ptm_stop_hw (void);

#if defined(CONFIG_PTM_BONDING_MASTER)
#define SLAVE_TPSTC_RX_POLLING 		1
extern void RestartWan(void);
extern int mdio_exeception(char flag);
extern int bonding_rx_fifo_check(int line);
extern void bonding_rx_fifo_reset_done(void);
extern long ptm1_ioctl(struct file *flip, unsigned int command, unsigned long inarg);
static int master_rx_fifo_full = 0;
static int slave_rx_fifo_full = 0;
static int rx_fifo_reset=1;
extern char adslup, slv_adslup;
extern int CheckTPScount_Flag;
static char tpstc_reset=0, utopia_reset=0, bonding_rx_reset=0;
static int line0rx_check=0, line1rx_check=0;
#endif

#if defined(CONFIG_PTM_BONDING_SLAVE)
#define RX_FIFO_FULL_EXECEPTION 	1
#define RX_FIFO_CHECK_EXECEPTION 	2
extern void bonding_rx_fifo_reset_done(void);
extern int nfbi_exeception(char flag);
static int rx_fifo_reset=1;
extern int CheckTPScount_Flag;
#endif

/* Definitions */
#define PTMBASE			0xB8400000  
#define SARCHEM_BASE	0xB8A80000 
#define SYSTEM_BASE		0xB8000000

#define RTL_W32(reg, value)			(*(volatile u32*)(PTMBASE+reg)) = (u32)value
#define RTL_W16(reg, value)			(*(volatile u16*)(PTMBASE+reg)) = (u16)value
#define RTL_W8(reg, value)				(*(volatile u8*)(PTMBASE+reg)) = (u8)value
#define RTL_R32(reg)					(*(volatile u32*)(PTMBASE+reg))
#define RTL_R16(reg)					(*(volatile u16*)(PTMBASE+reg))
#define RTL_R8(reg)					(*(volatile u8*)(PTMBASE+reg))

	
/* ioctl command called by protocols (system-independent) */
#define PTM_MAGIC			(('R'+'T'+'L'+'P'+'T'+'M') << 8)  
#define PTM_SET_DEFAULT_TABLE	(PTM_MAGIC+1)
#define PTM_GET_TABLE			(PTM_MAGIC+2)
#define PTM_READ_DATA			(PTM_MAGIC+3)
#define PTM_WRITE_DATA			(PTM_MAGIC+4)
#define PTM_SET_HW				(PTM_MAGIC+5)
#define PTM_GET_QMAP			(PTM_MAGIC+6)
#define PTM_STOP_HW				(PTM_MAGIC+7)
#define PTM_START_HW			(PTM_MAGIC+8)
#define PTM_CLEAN_WANIF			(PTM_MAGIC+9)
#define PTM_CLEAN_QMAP			(PTM_MAGIC+10)
#define PTM_SET_WANIF			(PTM_MAGIC+11)
#define PTM_SET_QMAP			(PTM_MAGIC+12)
#define PTM_SET_STAGTYPE		(PTM_MAGIC+13)
#define PTM_SET_DUMMYVID		(PTM_MAGIC+14)
#define PTM_SET_QMAPATSTAG		(PTM_MAGIC+15)
#define PTM_SET_MODE			(PTM_MAGIC+16)
#define PTM_SET_SYSTEM			(PTM_MAGIC+17)
#define PTM_ENABLE_TPSTC		(PTM_MAGIC+18)
#define PTM_DISABLE_TPSTC		(PTM_MAGIC+19)
#define PTM_RESET_TPSTC			(PTM_MAGIC+20)
#define PTM_GET_TPSTC_TX_CNT		(PTM_MAGIC+21)
#define PTM_GET_UTOPIA_TX_CNT		(PTM_MAGIC+22)


#ifndef SUCCESS
#define SUCCESS 	0
#endif
#ifndef FAILED
#define FAILED -1
#endif

/* Globals. */
#define PTM_DEV_MAJOR	120
#define PTM_DEV_NAME	"ptm"

static struct class *ptm_class=NULL;

static struct file_operations ptm_fops =
{
    unlocked_ioctl:      ptm_ioctl,
    open:       ptm_open,
    release:    ptm_close,
};

/* IOCTL structure */
struct ptm_arg{
	unsigned char cmd;
	union{
		struct{
			unsigned int cmd2;
			unsigned int cmd3;
			unsigned int cmd4;
		};
		struct{
			unsigned short a1;
			unsigned short a2;
			unsigned char a3[8];
		};
		unsigned char arg[12];
	};
};



/* Default table */
rtl8685_WANtbl_t default_tbl[]={	
	{{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, 0x020, 0x125, 0x1, "ptm0",0,0,0,0},
	{{0xab, 0xcd, 0xed, 0x12, 0x32, 0x57}, 0x344, 0x126, 0x1, "ptm1",0,0,0,0},
	{{0x00, 0x01, 0x02, 0x03, 0x04, 0x06}, 0x020, 0x127, 0x1, "ptm2",0,0,0,0},
	{{0xab, 0xcd, 0xed, 0x12, 0x32, 0x58}, 0x344, 0x128, 0x1, "ptm3",0,0,0,0},
	{{0x00, 0x01, 0x02, 0x03, 0x04, 0x07}, 0x020, 0x129, 0x1, "ptm4",0,0,0,0},
	{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x000, 0x000, 0x1, "ptm5",0,0,0,0},
	{{0xab, 0xcd, 0xed, 0x12, 0x32, 0x05}, 0x344, 0x130, 0x0, "ptm6",0,0,0,0},
	{{0x00, 0x01, 0x02, 0x03, 0x04, 0x09}, 0x020, 0x131, 0x0, "ptm7",0,0,0,0}
};

/* Current table */
rtl8685_WANtbl_t *current_tbl=NULL; 

/*Queue mapping table */
unsigned int *qmap_tbl=NULL;

/*Bonding mode selection 
	0: non-bonding mode
	1: bonding mode
*/
unsigned int ptm_bonding = 0, ptm_bonding_line = 0; 

#if defined(CONFIG_PTM_BONDING_MASTER)
spinlock_t bonding_lock;
struct mutex bonding_mutex;
#endif
/* 0x1064 bit9*/
#define ptm_table_lock(void) \
	do { \
		unsigned int p; \
		p=RTL_R32(Debug_Sel); \
		p=p|Update_en_tx; \
		RTL_W32(Debug_Sel, p); \
	}while(0)

#define ptm_table_unlock(void) \
		do { \
			unsigned int p; \
			p=RTL_R32(Debug_Sel); \
			p=p&(~Update_en_tx); \
			RTL_W32(Debug_Sel, p); \
		}while(0)

void Dumpreg(unsigned long Buffer, unsigned int size){

	int	k;	
	if(size%4)	size=size+4-(size%4);

	
	if ((Buffer&0xF0000000)==0x80000000) Buffer |= 0xA0000000;
	printk("Address  : Data");
	for(k=0;k<(size/4);k++){
		if ((k%4)==0) {
			printk ("\n");
			printk("%08X : ",(unsigned int)Buffer+k*4);
		}
		//mdelay(100);
		printk("%08X  ", (*(volatile unsigned int *)((unsigned int)((Buffer)+k*4))));
	}
	printk("\n");
}

int rtl8685_getWanTable(u32 idx, rtl8685_WANtbl_t *WANp)
{
	int ptm_intf_count = PTM_MAX_INTF;

	if (idx >= ptm_intf_count || WANp == NULL)
		return FAILED;

	/* Accumulate counter */
	current_tbl[idx].tx_pkt_cnt = RTL_R32(Entry_tx_pkt_cnt + idx*4);
	current_tbl[idx].rx_pkt_cnt = RTL_R32(Entry_rx_pkt_cnt + idx*4);
	current_tbl[idx].tx_pkt_byte = RTL_R32(Entry_tx_byte_cnt + idx*4);
	current_tbl[idx].rx_pkt_byte = RTL_R32(Entry_rx_byte_cnt + idx*4);

	memcpy(WANp, &current_tbl[idx], sizeof(rtl8685_WANtbl_t));

	return SUCCESS;
}

int rtl8685_setWanTable(u32 idx, rtl8685_WANtbl_t *WANp)
{
	int ptm_intf_count = PTM_MAX_INTF;
	int i;

	printk("idx=%u, %u, %s, %u, %u, %02x%02x%02x%02x%02x%02x\n", 
				idx, WANp->Valid, WANp->ifname, 
				WANp->SVlanID, WANp->CVlanID,
				WANp->MAC[0],WANp->MAC[1],WANp->MAC[2],
				WANp->MAC[3],WANp->MAC[4],WANp->MAC[5] );
		
	if (idx >= ptm_intf_count || WANp == NULL)
		return FAILED;

	memset( &current_tbl[idx], 0x0, sizeof(rtl8685_WANtbl_t)) ; 

	memcpy( &current_tbl[idx], WANp, sizeof(rtl8685_WANtbl_t));


	ptm_table_lock();
	/* Valid */
	RTL_R8( EntryValid_ADDR) &= ~(1 << idx);
	RTL_R8( EntryValid_ADDR) |= ( current_tbl[idx].Valid << idx);
	
	/* Set MAC address */
	for(i=0; i<6; i++){
		RTL_R8( EntryMAC_ADDR + idx*6 + i) = current_tbl[idx].MAC[i];
	}

	/* C-tag */
	RTL_R16( CTag_ADDR + idx*2) = current_tbl[idx].CVlanID;
	
	/* S-tag */
	RTL_R16( STag_ADDR + idx*2) = current_tbl[idx].SVlanID;

	/* Reset counter, do WriteClear */
	RTL_R32( Entry_tx_byte_cnt + idx*4) = 0x0;
	RTL_R32( Entry_rx_byte_cnt + idx*4) = 0x0;
	RTL_R32( Entry_tx_pkt_cnt + idx*4) = 0x0;
	RTL_R32( Entry_rx_pkt_cnt + idx*4) = 0x0;
	ptm_table_unlock();


	return SUCCESS;
}

void rtl8685_dumpWanTable(void)
{
	rtl8685_WANtbl_t  INTF_CTL;
	int i;

	printk("Intf	Valid	SVID	CVID	 MAC	\n");
	for(i=0; i<PTM_MAX_INTF; i++){
		rtl8685_getWanTable(i,&INTF_CTL);
		printk("%s	%d	0x%03X	0x%03X	%02X-%02X-%02X-%02X-%02X-%02X\n",
			INTF_CTL.ifname, INTF_CTL.Valid, INTF_CTL.SVlanID, INTF_CTL.CVlanID, 
			INTF_CTL.MAC[0], INTF_CTL.MAC[1], INTF_CTL.MAC[2], INTF_CTL.MAC[3], INTF_CTL.MAC[4], INTF_CTL.MAC[5]);
	}
	printk("\nIntf	Tx_pkt	Rx_pkt	Tx_byte	Rx_byte\n");
	for(i=0;i<PTM_MAX_INTF;i++){
		rtl8685_getWanTable(i,&INTF_CTL);
		printk("%s	0x%08X	0x%08X	0x%08X	0x%08X\n",
			INTF_CTL.ifname, INTF_CTL.tx_pkt_cnt, INTF_CTL.rx_pkt_cnt, INTF_CTL.tx_pkt_byte, INTF_CTL.rx_pkt_byte);
	}
}

int rtl8685_set_QMap(u32 idx, u8 pri, u8 value)
{
	int ptm_intf_count = PTM_MAX_INTF;
	unsigned int QMap_val = 0;

	//printk("idx=%u, pri=%u, value=0x%02x ", idx, pri, value );
	
	if (idx >= ptm_intf_count )
		return FAILED;

	QMap_val = (*(qmap_tbl + idx ));

	QMap_val &= ~( (0x7) << (pri*3));
	QMap_val |=  (value & 0x7) << (pri*3);

	//printk(", QMap_val = 0x%X\n", QMap_val);

	(*(qmap_tbl + idx )) = QMap_val;

	ptm_table_lock();
	RTL_R32( QMap_ADDR + idx*4) = QMap_val;
	ptm_table_unlock();

	return SUCCESS;
}

void rtl8685_dump_QMap(void)
{
	unsigned int qval32=0;
	int i,j;

	printk("Intf	PRI0	PRI1	PRI2	PRI3	PRI4	PRI5	PRI6	PRI7\n");
	for(i=0;i<PTM_MAX_INTF;i++){
		qval32 = RTL_R32(QMap_ADDR + i*4);
		printk("%d",i);
		
		for(j=0; j<PTM_PRI_NUM;j++){
			printk("	%u", (qval32>>(j*3))&0x7);
		}
		printk("\n");
	}
}

static void ptm_restore_wanitf(void)
{
	int i;
	for(i=0; i<PTM_MAX_INTF; i++)
	{
		rtl8685_WANtbl_t tmpW;
		unsigned int tmpQ;

		rtl8685_getWanTable(i, &tmpW);
		rtl8685_setWanTable(i, &tmpW);

		tmpQ=qmap_tbl[i];
		ptm_table_lock();
		RTL_R32( QMap_ADDR + i*4) = tmpQ;
		ptm_table_unlock();	
	}
}

#ifdef PTM_RESET
#define PTM_DELAY_TIME	(5*HZ)
static struct timer_list ptm_timer;
static unsigned int ptm_timer_dbg=0;
static unsigned int rx_tpkt_ptm0;
static unsigned int rx_tpkt_ptm1;
static unsigned int rx_tpkt_ptm2;
static unsigned int rx_tpkt_ptm3;
#ifdef PTM_TX_RESET
static unsigned int tx_total_cf_p0;
static unsigned int tx_total_cf_p1;
#endif /*PTM_TX_RESET*/

#ifdef SACHEM_TX_RESET
extern unsigned long gUStpsDataAccCnt0;
unsigned long sachem_tx_data_total;
static unsigned int sachem_tx_reset_try;
static unsigned int sachem_tx_reset_count;
static unsigned int sachem_tx_reset_done;
static unsigned int sachem_tx_reset_count_overall;
static unsigned long tx_data_cf_p0;
int sachem_tx_reset_step=25;
int sachem_tx_reset_times=6;
#endif /* SACHEM_TX_RESET */

#define SARCHEM_W32(reg, value)		(*(volatile u32*)(SARCHEM_BASE+reg)) = (u32)value
#define SARCHEM_R32(reg)			(*(volatile u32*)(SARCHEM_BASE+reg))
#define SYSTEM_W32(reg, value)		(*(volatile u32*)(SYSTEM_BASE+reg)) = (u32)value
#define SYSTEM_R32(reg)				(*(volatile u32*)(SYSTEM_BASE+reg))	

#define PTM_WAIT_SWITCH_TIME ((HZ)/5) //wait max.=200ms
#define P5_DCR0				(0xbb806160)
#define P5_DCR1				(P5_DCR0+4)
#define P5_DCR2				(P5_DCR0+8)
#define PCRP5				(0xbb804118)
#define ForceLink			(1<<23)

static void ptm_event_init(void)
{
	rx_tpkt_ptm0 = RTL_R32(RX_TPKT_PTM0);
	rx_tpkt_ptm1 = RTL_R32(RX_TPKT_PTM1);
	rx_tpkt_ptm2 = RTL_R32(RX_TPKT_PTM2);
	rx_tpkt_ptm3 = RTL_R32(RX_TPKT_PTM3);
#ifdef PTM_TX_RESET
	tx_total_cf_p0 = RTL_R32(TX_TOTAL_CF_P0);
	tx_total_cf_p1 = RTL_R32(TX_TOTAL_CF_P1);
#endif /*PTM_TX_RESET*/
#ifdef SACHEM_TX_RESET 
	sachem_tx_data_total = gUStpsDataAccCnt0;
	sachem_tx_reset_try = 0;
	sachem_tx_reset_count = 0;
	(IS_RL6405A==1)?(sachem_tx_reset_done = 0):(sachem_tx_reset_done = 1);
	tx_data_cf_p0 = RTL_R32(TX_DATA_CF_P0);
#endif /*SACHEM_TX_RESET*/
}

static int ptm_event_detect(void)
{
#ifdef SACHEM_TX_RESET
	unsigned long sachem_tx_data_0_new;
	unsigned int tx_data_cf_p0_new;
	unsigned int IS_PTM_TRAFFIC = 0;
	int ret_sachem_tx=0;
#endif /* SACHEM_TX_RESET */
#ifdef PTM_TX_RESET
	unsigned int tx_total_cf_p0_new;
	unsigned int tx_total_cf_p1_new;
	int ret_tx=0;
#endif /*PTM_TX_RESET*/
	unsigned int rx_tpkt_ptm0_new;
	unsigned int rx_tpkt_ptm1_new;
	unsigned int rx_tpkt_ptm2_new;
	unsigned int rx_tpkt_ptm3_new;
	int ret=0;

#ifdef SACHEM_TX_RESET
	if(sachem_tx_reset_done)
#endif /* SACHEM_TX_RESET */
	{
		rx_tpkt_ptm0_new = RTL_R32(RX_TPKT_PTM0);
		rx_tpkt_ptm1_new = RTL_R32(RX_TPKT_PTM1);
		rx_tpkt_ptm2_new = RTL_R32(RX_TPKT_PTM2);
		rx_tpkt_ptm3_new = RTL_R32(RX_TPKT_PTM3);

		if( (rx_tpkt_ptm0_new==rx_tpkt_ptm0) &&
			(rx_tpkt_ptm1_new==rx_tpkt_ptm1) &&
			(rx_tpkt_ptm2_new==rx_tpkt_ptm2) &&
			(rx_tpkt_ptm3_new==rx_tpkt_ptm3) ){
			printk("[%s]: Trigger PTM Rx reset event\n",__func__);
			ret=1;
		}

		rx_tpkt_ptm0 = rx_tpkt_ptm0_new;
		rx_tpkt_ptm1 = rx_tpkt_ptm1_new;
		rx_tpkt_ptm2 = rx_tpkt_ptm2_new;
		rx_tpkt_ptm3 = rx_tpkt_ptm3_new;

		#ifdef PTM_TX_RESET
		tx_total_cf_p0_new = RTL_R32(TX_TOTAL_CF_P0);
		tx_total_cf_p1_new = RTL_R32(TX_TOTAL_CF_P1);

		if( (tx_total_cf_p0_new==tx_total_cf_p0) &&
			(tx_total_cf_p1_new==tx_total_cf_p1) ){
			printk("[%s]: Trigger PTM Tx reset event\n",__func__);	
			ret_tx=1;
		}
			
		tx_total_cf_p0 = tx_total_cf_p0_new;
		tx_total_cf_p1 = tx_total_cf_p1_new;

		if(ret || ret_tx)
		{
			printk( "%s: reset_by: rx=%d, tx=%d\n", __func__, ret, ret_tx );
			ret=1;
		}
		#endif /*PTM_TX_RESET*/
	}


#ifdef SACHEM_TX_RESET
	if(IS_RL6405A){
		if(!sachem_tx_reset_done)
		{
			sachem_tx_data_0_new = gUStpsDataAccCnt0;
			sachem_tx_reset_try++;
			
			if((sachem_tx_data_0_new)>=(sachem_tx_reset_step+sachem_tx_data_total)){
				ret_sachem_tx = 1;
				sachem_tx_reset_count++;	
				sachem_tx_reset_count_overall++;
				printk("[%s]: Trigger Sachem Tx reset event\n",__func__);	
			}
			
			printk("%s, [after=%lu, before=%lu]\n",__func__,sachem_tx_data_0_new,sachem_tx_data_total);
			printk("%s, [TryCounter=%u, DoCounter=%u, OverallCounter=%u]\n",__func__
				,sachem_tx_reset_try,sachem_tx_reset_count,sachem_tx_reset_count_overall);
			
			if(sachem_tx_reset_try>=(sachem_tx_reset_times+sachem_tx_reset_count)){
				sachem_tx_reset_done = 1;
				
				/* Force SwCore port 5 link up */
				REG32(PCRP5) |= (ForceLink);
			}				
			sachem_tx_data_total = sachem_tx_data_0_new;
			
			if(ret_sachem_tx)
			{
				printk( "%s: rx=%d, tx=%d, sachem tx=%d\n", __func__, ret, ret_tx, ret_sachem_tx);
				ret=1;
			}
		}else{
		
			sachem_tx_data_0_new = gUStpsDataAccCnt0;
			tx_data_cf_p0_new = RTL_R32(TX_DATA_CF_P0);
			
			/* Real traffic : IS_PTM_TRAFFIC = 1*/
			IS_PTM_TRAFFIC = ((tx_data_cf_p0_new-tx_data_cf_p0)>10)?1:0;
			
			if(!IS_PTM_TRAFFIC){
			
				if( (sachem_tx_data_0_new-sachem_tx_data_total)>=((tx_data_cf_p0_new-tx_data_cf_p0)+1000)){
					ret_sachem_tx=1;
					printk("[%s,%d]: Trigger Sachem Tx reset event\n",__func__,__LINE__);
					printk("[%s,%d]: Sachem up counter = %d, PTM TX counter = %d\n",__func__,__LINE__,
						(sachem_tx_data_0_new-sachem_tx_data_total), (tx_data_cf_p0_new-tx_data_cf_p0));
				}		
			}
			
			sachem_tx_data_total = sachem_tx_data_0_new;
			tx_data_cf_p0 = tx_data_cf_p0_new;
			
			if(ret || ret_tx || ret_sachem_tx)
			{
				printk( "%s: rx=%d, tx=%d, sachem tx in showtime=%d\n", __func__, ret, ret_tx, ret_sachem_tx);
				ret=1;
			}		
		}
	}
#endif /* SACHEM_TX_RESET */

	if(ptm_timer_dbg) printk("%s: return %d\n",__func__, ret);
	return ret;
}


#if defined(CONFIG_PTM_BONDING)
int bonding_tpstc_rx_receive(void)
{
	#if defined(CONFIG_PTM_BONDING_MASTER)
	unsigned long flags;

	printk("bonding master rx fifo checking..\n");

	bonding_rx_fifo_check(0);
	#endif

	#if defined(CONFIG_PTM_BONDING_SLAVE)
	
	printk("bonding slave rx fifo checking..\n");
	nfbi_exeception(RX_FIFO_CHECK_EXECEPTION);
	#endif
}
#endif


static void ptm_SwitchPHYIfEnable(int en)
{
	if(en)
	{
		unsigned long cur_jiffies=jiffies;
		unsigned long to_jiffies;

		to_jiffies = cur_jiffies + PTM_WAIT_SWITCH_TIME;
		if(ptm_timer_dbg) printk( "%s: start cur_jiffies=%08lx, to_jiffies=%08lx\n", __func__, cur_jiffies, to_jiffies );
		while( cur_jiffies < to_jiffies )
		{
			unsigned int p5_dcr0,p5_dcr1,p5_dcr2;
			p5_dcr0=REG32(P5_DCR0);
			p5_dcr1=REG32(P5_DCR1);
			p5_dcr2=REG32(P5_DCR2);
			if( (p5_dcr0==0) && (p5_dcr1==0) && (p5_dcr2==0) )
				break;
			
			if(ptm_timer_dbg) printk( "%s: p5_dcr= %08x %08x %08x\n", __func__, p5_dcr0, p5_dcr1, p5_dcr2 );
			cur_jiffies=jiffies;
		}
#ifdef SACHEM_TX_RESET	
		if(sachem_tx_reset_done)
#endif
		{
			REG32(PCRP5) = REG32(PCRP5)|(ForceLink);
		}
	
		if(ptm_timer_dbg) printk( "%s: end cur_jiffies=%08lx, to_jiffies=%08lx\n", __func__, cur_jiffies, to_jiffies );
		if(cur_jiffies>=to_jiffies)
			printk( "%s: wait p5_dcr timeout!\n", __func__ );
	}else{
		REG32(PCRP5) = REG32(PCRP5)&~(ForceLink);
	}

	printk( "%s: PCRP5=0x%08x\n",__func__, REG32(PCRP5) );
}


extern void ShowtimeTpsReset(void);
extern void ShowtimeTpsSet(void);

static int ptm_event_process(void)
{
	printk("%s: ptm reset\n",__func__);
	ptm_SwitchPHYIfEnable(0);

	SARCHEM_W32(0x5040, 0x1);
	printk("0xb8a85040 :  %x \n",SARCHEM_R32(0x5040));

	ptm_stop_hw();
	REG32(BSP_IP_SEL) &= (~BSP_EN_PTM);
	printk( "PTM: disable IP %08x\n", REG32(BSP_IP_SEL) );

	ShowtimeTpsReset();

	REG32(BSP_IP_SEL) |= BSP_EN_PTM;
	printk( "PTM: enable IP %08x\n", REG32(BSP_IP_SEL) );
	ptm_init_hw();
	ptm_restore_wanitf();

	ShowtimeTpsSet();

	ptm_start_hw();
	
	if(IS_RL6405A){
		SARCHEM_W32(0x5040, 0x0);
	}

	ptm_SwitchPHYIfEnable(1);
	printk("%s: ptm reset done\n",__func__);
	return 0;
}


#if defined(CONFIG_PTM_BONDING_MASTER)
void ptm_reset(void)
{
	unsigned long flags;

	spin_lock_irqsave(&bonding_lock, flags);
	ptm1_ioctl(NULL, PTM_DISABLE_TPSTC, 0);

	SARCHEM_W32(0x5040, 0x1);

	ptm_stop_hw();

	ShowtimeTpsReset();

	ptm1_ioctl(NULL, PTM_RESET_TPSTC, 0);
	ShowtimeTpsSet();

	ptm_start_hw();

	SARCHEM_W32(0x5040, 0x0);
	ptm1_ioctl(NULL, PTM_ENABLE_TPSTC, 0);
	spin_unlock_irqrestore(&bonding_lock, flags);

}

int bonding_rx_fifo_reset(void)
{
	int master_ptm_rx, slave_ptm_rx;
	int master_ptm_rx2, slave_ptm_rx2;
	int master_rx_cnt, slave_rx_cnt;

	if (rx_fifo_reset==0)
		return 0;

	master_ptm_rx = RTL_R32(RX_TPKT_PTM0);
	slave_ptm_rx = RTL_R32(RX_TPKT_PTM4);

	mdelay(1000);	

	master_ptm_rx2 = RTL_R32(RX_TPKT_PTM0);
	slave_ptm_rx2 = RTL_R32(RX_TPKT_PTM4);

	master_rx_cnt = master_ptm_rx - master_ptm_rx2;
	slave_rx_cnt = slave_ptm_rx - slave_ptm_rx2;

	if ((master_rx_fifo_full && (master_rx_cnt == 0)) || (slave_rx_fifo_full && slave_rx_cnt==0)){
		REG32(PCRP5) = REG32(PCRP5)&~(ForceLink);
		ptm_reset();
		REG32(PCRP5) = REG32(PCRP5)|(ForceLink);
		RestartWan();
	}
}

int bonding_rx_fifo_check(int line)
{
	unsigned int line0_rx, line1_rx;
	int tpstc_rxpolling = 0;

	if ((utopia_reset==1 || tpstc_reset==1 || bonding_rx_reset==1) && 
	    (adslup && slv_adslup)){

		if ((line==0 && line1rx_check) || (line==1 && line0rx_check)){
			line0_rx = RTL_R32(RX_TPKT_PTM0);
			line1_rx = RTL_R32(RX_TPKT_PTM4);
			if ((line0_rx > 0) && (line1_rx>0)){
				printk("check line0 & line1 ptm ok\n");
				line0rx_check = 1;
				line1rx_check = 1;
			} else {
				printk("check line0 & line1 rx fifo error, do ptm reset\n");
				REG32(PCRP5) = REG32(PCRP5)&~(ForceLink);
				ptm_reset();	
				REG32(PCRP5) = REG32(PCRP5)|(ForceLink);
				RestartWan();
				tpstc_rxpolling = 1;
				bonding_rx_reset = 1;
				line0rx_check = 0;
				line1rx_check = 0;
			}
		} else if (line==0){
			line0rx_check = 1;
		} else if (line==1){
			line1rx_check = 1;
		}

	} else {
		if (line==0){
			line0_rx = RTL_R32(RX_TPKT_PTM0);
			printk("line0 ptm rx current value: %x\n", line0_rx);

			if (line0_rx > 0){
				printk("check line0 ptm ok\n");
			} else {
				printk("check line0 rx fifo error, do ptm reset\n");
				REG32(PCRP5) = REG32(PCRP5)&~(ForceLink);
				ptm_reset();	
				REG32(PCRP5) = REG32(PCRP5)|(ForceLink);
				RestartWan();
				tpstc_rxpolling = 1;
				bonding_rx_reset = 1;
			}	
		}
		else if (line==1){
			line1_rx = RTL_R32(RX_TPKT_PTM4);
			printk("line1 ptm rx current value: %x\n", line1_rx);
	
			if (line1_rx > 0){
				printk("check line1 ptm ok\n");
			} else {
				printk("check line1 rx fifo error, do ptm reset\n");
				REG32(PCRP5) = REG32(PCRP5)&~(ForceLink);
				ptm_reset();	
				REG32(PCRP5) = REG32(PCRP5)|(ForceLink);
				RestartWan();
				tpstc_rxpolling = 1;
				bonding_rx_reset = 1;
			}
		}
		else {
			printk("bonding_rx_fifo_check: invalid line number\n");
			return -1;
		}
	}

	if (tpstc_rxpolling){
		if (adslup){
			CheckTPScount_Flag = 1;
		}

		if (slv_adslup){
			mdio_exeception(SLAVE_TPSTC_RX_POLLING);
		}
	} 

	return 0;
}
#endif

int bonding_rx_fifo_full(void)
{
#if defined(CONFIG_PTM_BONDING_MASTER)
	unsigned long flags;

	master_rx_fifo_full = 1;

	bonding_rx_fifo_reset();

	master_rx_fifo_full = 0;
	bonding_rx_fifo_reset_done();

	printk("bonding master rx fifo reset done\n");

#elif defined(CONFIG_PTM_BONDING_SLAVE)
	nfbi_exeception(RX_FIFO_FULL_EXECEPTION);
	mdelay(1500);	
	bonding_rx_fifo_reset_done();
	printk("bonding slave rx fifo reset done\n");
#endif

	return 0;
}

#if defined(CONFIG_PTM_BONDING_SLAVE)
void slave_tpstc_rx_polling(void)
{	
	CheckTPScount_Flag = 1;
}
#endif

#if defined(CONFIG_PTM_BONDING_MASTER)
void slave_bonding_rx_fifo_check(void)
{
	unsigned long flags;

	printk("bonding slave rx fifo checking..\n");

	bonding_rx_fifo_check(1);
}

void slave_bonding_rx_fifo_full(void)
{
	unsigned long flags;

	slave_rx_fifo_full = 1;

	bonding_rx_fifo_reset();

	slave_rx_fifo_full = 0;
	printk("bonding slave rx fifo reset done\n");
}

void ptm_tpstc_reset(void)
{
	int val=0, retry, master_tpstc_txcnt, slave_tpstc_txcnt;

	//SwitchPHYPort5 Disable
	REG32(PCRP5) = REG32(PCRP5)&~(ForceLink);

	master_tpstc_txcnt = (*(volatile u32*)(0xb8af0040));
	slave_tpstc_txcnt = ptm1_ioctl(NULL, PTM_GET_TPSTC_TX_CNT, 0);

	mdelay(100);

	for (retry=0; retry<5; retry++){
		master_tpstc_txcnt = (*(volatile u32*)(0xb8af0040));
		slave_tpstc_txcnt = ptm1_ioctl(NULL, PTM_GET_TPSTC_TX_CNT, 0);

		printk("check master tpstc tx cnt=%x\n", master_tpstc_txcnt);
		printk("check slave tpstc tx cnt=%x\n", slave_tpstc_txcnt);
			
		if ((master_tpstc_txcnt > 0x100) || (slave_tpstc_txcnt > 0x100)){
			printk("%s: ptm reset\n",__func__);
			ptm_reset();
			tpstc_reset = 1;
			printk("%s: ptm reset done\n",__func__);
		} else {
			printk("check master/slave tpstc status ok\n");
			break;
		}
		mdelay(1000);
		printk("retry: %d\n", retry+1);
	}

	//SwitchPHYPort5 Enable
	REG32(PCRP5) = REG32(PCRP5)|(ForceLink);
}

#define MASTER	0
#define SLAVE	1
void ptm_utopia_reset(char active_line)
{
	int val=0, retry;
	int master_utopia_txcnt, slave_utopia_txcnt;

	master_utopia_txcnt = (*(volatile u32*)(0xb8af0028));
	slave_utopia_txcnt = ptm1_ioctl(NULL, PTM_GET_UTOPIA_TX_CNT, 0);

	mdelay(100);

	for (retry=0; retry<5; retry++){

		printk("check utopia status\n");
			
		if (active_line & (1<<MASTER)){
			master_utopia_txcnt = (*(volatile u32*)(0xb8af0028));
			printk("master linkup: check master utopia tx cnt=%x\n", master_utopia_txcnt);
		}

		if (active_line & (1<<SLAVE)){
			slave_utopia_txcnt = ptm1_ioctl(NULL, PTM_GET_UTOPIA_TX_CNT, 0);
			printk("slave linkup: check slave utopia tx cnt=%x\n", slave_utopia_txcnt);
		}

		if (((active_line & (1<<MASTER)) && master_utopia_txcnt==0) ||
		    ((active_line & (1<<SLAVE)) && slave_utopia_txcnt==0)) {
			printk("%s: utopia reset\n",__func__);
			//SwitchPHYPort5 Disable
			REG32(PCRP5) = REG32(PCRP5)&~(ForceLink);
			ptm_reset();
			REG32(PCRP5) = REG32(PCRP5)|(ForceLink);
			RestartWan();
			utopia_reset = 1;
			printk("%s: utopia reset done\n",__func__);
		} 
		else {
			printk("check utopia status ok\n");
			break;
		}

		mdelay(1000);
		printk("retry: %d\n", retry+1);
	}
	
}
#endif

static void ptm_timer_handle(unsigned long arg)
{
	if(ptm_timer_dbg) printk("%s\n",__func__);

#ifdef SACHEM_TX_RESET
	if(!sachem_tx_reset_done){
		mod_timer(&ptm_timer, jiffies+(PTM_DELAY_TIME/10));
	}else
#endif /* SACHEM_TX_RESET */
	{
		mod_timer(&ptm_timer, jiffies+PTM_DELAY_TIME);
	}

	if( ptm_event_detect() )
		ptm_event_process();

	return;
}

static void ptm_timer_init(void)
{
	printk("%s\n",__func__);

	init_timer(&ptm_timer);
	ptm_timer.function = &ptm_timer_handle;
	
#ifdef SACHEM_TX_RESET
	if(IS_RL6405A)
		ptm_timer.expires = jiffies+(PTM_DELAY_TIME/10);
	else
#endif /* SACHEM_TX_RESET */
	{
		ptm_timer.expires = jiffies+PTM_DELAY_TIME;
	}
}

#if defined(CONFIG_DSL_ON_SLAVE)
void ptm_timer_add(void)
#else
static void ptm_timer_add(void)
#endif
{
	printk("%s\n",__func__);

	ptm_event_init();
#ifdef SACHEM_TX_RESET
	if(!sachem_tx_reset_done){
		mod_timer(&ptm_timer, jiffies+(PTM_DELAY_TIME/10));
	}else
#endif /* SACHEM_TX_RESET */
	{
		mod_timer(&ptm_timer, jiffies+PTM_DELAY_TIME);
	}
}

static void ptm_timer_exit(void)
{
	printk("%s\n",__func__);

	del_timer(&ptm_timer);
}
#endif


/***************************************************************************
 * Function Name: ptm_stop_hw
 * Description  :Disable TX and RX function
 * Returns      : None.
 ***************************************************************************/
void ptm_stop_hw (void)
{
	u8 val;
	
	val = RTL_R8( WCR);
	val &= ~(RXRESB | TXRESB);

	RTL_R8( WCR) = val;

#ifdef SACHEM_TX_RESET
	/* Force SwCore port 5 link down */
	if(IS_RL6405A){
		REG32(PCRP5) &= ~(ForceLink);
	}
#endif /* SACHEM_TX_RESET */
	
#ifdef PTM_RESET
	ptm_timer_exit();
#endif

	//udelay(10);
}

typedef struct MSGTODSL {
int message;
int* intVal;
} msgToDsl;
extern __attribute__ ((mips16)) void UserGetDslData(msgToDsl *pData);
extern __attribute__ ((mips16)) int GetLinkSpeed(char* pData);
extern int rtl8651_setAsicQueueRate( int port, int queueid, unsigned int pprTime, unsigned int aprBurstSize, unsigned int apr );
int  ratelimit_ratio =96;
int  ratelimit_ratio_not30a = 91;
int  ratelimit_state=1;
static void set_port5_engress_ratelimit(void)
{
#ifdef CONFIG_RTL8685
	int i=0;
	unsigned long dslNetDataRate[2];
	unsigned long dsl_tx_rate;

	msgToDsl msgToDsl0;
	unsigned int profileName=0;
	unsigned int dsl_device;

  if(!IS_RTL8685C())
  {
	if(ratelimit_state==-1)//stop
	{
		return;
	}else if(ratelimit_state==0)//disable
	{
		for(i=0;i<8;i++)
			rtl8651_setAsicQueueRate(5, i, 7, 0xff, 0x3fff);
		return;
	}//else enable

	msgToDsl0.intVal= kmalloc( sizeof(int), GFP_KERNEL);
	if(msgToDsl0.intVal==NULL)
	{
		kfree(msgToDsl0.intVal);
		return;	
	}
	memset(msgToDsl0.intVal, 0, sizeof(int));

	msgToDsl0.message=1;//GetVdslProfile=1
	UserGetDslData(&msgToDsl0);
	profileName=msgToDsl0.intVal[0]; 

	msgToDsl0.message=53;//GetDeviceType=53, 0:VTUO, 1:VTUR
	msgToDsl0.intVal[0]=1;//default VTUR if failed
	UserGetDslData(&msgToDsl0);
	dsl_device=msgToDsl0.intVal[0]; 
	
	kfree(msgToDsl0.intVal);
	GetLinkSpeed((char*) dslNetDataRate);
	if(dsl_device) //VTUR
		dsl_tx_rate=dslNetDataRate[0];//upstream rate
	else //VTUO
		dsl_tx_rate=dslNetDataRate[1];//downstream rate
	//printk( "%s: got dsl_tx_rate=%lu\n", __FUNCTION__, dsl_tx_rate );
	
	if(profileName==0x80)//30a
	{
		for(i=0;i<8;i++)
			rtl8651_setAsicQueueRate(5, i, 0, 1, 	(unsigned int)(dsl_tx_rate*ratelimit_ratio/25600));
	}
	else
	{
		for(i=0;i<8;i++)
			rtl8651_setAsicQueueRate(5, i, 1, 1, 	(unsigned int)(dsl_tx_rate*ratelimit_ratio_not30a/25600));
	}
  }
#endif /* CONFIG_RTL8685 */
	
}


/***************************************************************************
 * Function Name: ptm_start_hw
 * Description  :Enable TX and RX function
 * Returns      : None.
 ***************************************************************************/
void ptm_start_hw (void)
{
	u8 val;

	val = RTL_R8( WCR);

	/* Set as stand alone mode */
	val |= (RXRESB|TXRESB); 
	RTL_R8( WCR) = val; 

#ifdef SACHEM_TX_RESET
	/* Force SwCore port 5 link down in default */
	if(IS_RL6405A)
	{
		u32 val_port5;
		
		val_port5 = REG32(PCRP5);
		val_port5 &= ~(ForceLink);
		REG32(PCRP5) = val_port5;
	}	
#endif /* SACHEM_TX_RESET */	
	
	set_port5_engress_ratelimit();
	
	/* Clear PTM HW counters */
	rtl8685_set_ptm_counter();

#ifdef PTM_RESET
	if(!ptm_bonding)
		ptm_timer_add();
#endif
}


void ptm_set_mode (u8 mode)
{
	u8 c;

	c=RTL_R8(WCR);
	c &= ~(Mode_Sel_Mask);
	c = c | mode;
	RTL_R8(WCR)=c;
}
/***************************************************************************
 * Function Name: ptm_init_hw
 * Description  :Set HW registers procedures
 * Returns      : None.
 ***************************************************************************/
static void ptm_init_hw (void)
{
	int i;
	
	/* Stop PTM HW first */
	ptm_stop_hw();
	
	/* Reset PTM module */
	RTL_W8(SRestReg, srstn);

	/* Set rate limit */ // no rx rate limit
	RTL_W16( TTRL, 0xff03);
	//RTL_W16( TTRL, 0x6400);

	/* Set S-tag type*/
	RTL_W16(Stag_type_val, 0x88a8);

	/* Set dummy VID*/
	RTL_W16(Dummy_vid, 0xfff);
	
	/* Set PCR to zero before using */
	RTL_W32(PCR, 0x0);
	
	/* Bonding setting */
	if(ptm_bonding==0){

		/* Non-bonding mode */
		RTL_W32(BD_SLV_NUM, (1<<1));
		RTL_W32(PCR, (TY_SYNC_F0 |TY_SYNC_S0 ));

		/* Cell avaliable in PTM */
		if(IS_RL6405 || IS_RLE0797){
			RTL_W32( PCR, (RTL_R32(PCR)|(rxutp_clav_en)) /*| (txutp_clav_en)*/);	
		}
		else if(IS_RLE0822 || IS_6518()){
			#if !defined(CONFIG_PTM_BONDING_MASTER)
			RTL_W32( PCR, (RTL_R32(PCR) | (rxutp_clav_en) | (txutp_clav_en) | (rxutp_clav_ext_en) | (txutp_clav_ext_en)) );	
			#endif
		}
		
	}else{
		/* UTP QoS */
		RTL_W32( UTP_APR32_F,  0x000e000e);
		RTL_W32( UTP_APR10_F,  0x7ffc7ffc);
		RTL_W32( UTP_APR32_S,  0x000e000e);
		RTL_W32( UTP_APR10_S,  0x7ffc7ffc);
		RTL_W32( UTP_APR_ALL,  0x7ffc7ffc);
		RTL_W32( UTP_WEIGHT_F, 0x01010101);
		RTL_W32( UTP_WEIGHT_S, 0x01010101);
		RTL_W32( UTP_QOS_MIS,  0x00983300);

		/* Bonding mode */
		RTL_W32(BD_SLV_NUM, (ptm_bonding_line << 1)); 		/* line number */
		RTL_W32(BOND_FRAG_LF, 0x000001f8);
		RTL_W32(BD_TIMEOUT, 0x00000100);

		for(i=0;i<ptm_bonding_line;i++){
			RTL_W32( PCR, RTL_R32(PCR)|(TY_SYNC_F0 <<i)| (TY_SYNC_S0 <<i));
		}	
		/* Move ptm tx/rx utp avaiable bit setting to AdslLinkUp() */	
	}
	
#ifdef CONFIG_PTM_HDLC
	RTL_W32( PCR, RTL_R32(PCR)|TC_SYNC_LC);
#else
	/*8681: 64/65 mode turns on short packet mode! BUG!!! */
	//RTL_W32( PCR, RTL_R32(PCR)|TC_SYNC_LC|EFM64BSP);
	//8685: don't turn on short packets by default
	RTL_W32( PCR, RTL_R32(PCR)|TC_SYNC_LC);
#endif

	/* Clear PTM HW counters */
	rtl8685_set_ptm_counter();

	ptm_set_mode(Mode2_Sel);
	
	if (!IS_RLE0822 && !IS_6518()) {
		RTL_W32( Debug_Sel, RTL_R32(Debug_Sel)| (Enable_LS_flow|Enable_LF_flow) );
	}
	/* Move ptm_start_hw() to AdslLinkUp()*/
	
}

static int hw_reg_read(struct seq_file *seq, void *offset)
{
	unsigned char c;

	seq_printf(seq, "PTMBASE         = 0x%08X\n", PTMBASE);

	c=RTL_R8(WCR);
	seq_printf(seq, "WCR             = 0x%02X\n", c);
	seq_printf(seq, "                    mode:%u\n", (c&Mode_Sel_Mask)?1:0 );
	seq_printf(seq, "                    qmap_at_stag: tx=%u, rx=%u\n", (c&Qmap_at_stag_tx)?1:0, (c&Qmap_at_stag_rx)?1:0 );
	seq_printf(seq, "                    enable: tx=%u, rx=%u\n", (c&TXRESB)?1:0, (c&RXRESB)?1:0 );
	
	seq_printf(seq, "TMII Rate Limit	= 0x%04X\n", RTL_R16(TTRL));
	seq_printf(seq, "S-tag type      = 0x%04X\n", RTL_R16(Stag_type_val));
	seq_printf(seq, "Dummy vid       = 0x%03X\n", RTL_R16(Dummy_vid));
	seq_printf(seq, "PCR             = 0x%08X\n", RTL_R32(PCR));
	seq_printf(seq, "Debug_Sel       = 0x%08X\n", RTL_R32(Debug_Sel));
	
	
	//if(ptm_bonding==1)
	{
		seq_printf(seq, "\nPTM bonding registers:\n");
		seq_printf(seq, "BOND_FRAG_LF	= 0x%08X\n",RTL_R32(BOND_FRAG_LF));
		seq_printf(seq, "BD_SLV_NUM      = 0x%08X\n",RTL_R32(BD_SLV_NUM));
		seq_printf(seq, "BD_TIMEOUT      = 0x%08X\n",RTL_R32(BD_TIMEOUT));
	}

	return 0;
}

static int hw_qos_read(struct seq_file *seq, void *data)
{
	seq_printf(seq, "\nUTP QoS registers:\n");
	seq_printf(seq, "UTP_APR32_F = 0x%08x            UTP_APR32_S = 0x%08x\n",RTL_R32(UTP_APR32_F),RTL_R32(UTP_APR32_S));
	seq_printf(seq, "UTP_APR10_F = 0x%08x            UTP_APR10_S = 0x%08x\n",RTL_R32(UTP_APR10_F),RTL_R32(UTP_APR10_S));
	seq_printf(seq, "UTP_APR_ALL = 0x%08x\n",RTL_R32(UTP_APR_ALL));
	seq_printf(seq, "UTP_WEIGHT_F= 0x%08x            UTP_WEIGHT_S= 0x%08x\n",RTL_R32(UTP_WEIGHT_F),RTL_R32(UTP_WEIGHT_S));
	seq_printf(seq, "UTP_QOS_MIS = 0x%08x\n",RTL_R32(UTP_QOS_MIS));
	seq_printf(seq, "\n");

	seq_printf(seq, "UTP_F0_ACC_INTERVAL = 0x%08x    UTP_S0_ACC_INTERVAL = 0x%08x\n",RTL_R32(UTP_F0_ACC_INTERVAL),RTL_R32(UTP_S0_ACC_INTERVAL));
	seq_printf(seq, "UTP_F1_ACC_INTERVAL = 0x%08x    UTP_S1_ACC_INTERVAL = 0x%08x\n",RTL_R32(UTP_F1_ACC_INTERVAL),RTL_R32(UTP_S1_ACC_INTERVAL));
	seq_printf(seq, "UTP_F2_ACC_INTERVAL = 0x%08x    UTP_S2_ACC_INTERVAL = 0x%08x\n",RTL_R32(UTP_F2_ACC_INTERVAL),RTL_R32(UTP_S2_ACC_INTERVAL));
	seq_printf(seq, "UTP_F3_ACC_INTERVAL = 0x%08x    UTP_S3_ACC_INTERVAL = 0x%08x\n",RTL_R32(UTP_F3_ACC_INTERVAL),RTL_R32(UTP_S3_ACC_INTERVAL));
	seq_printf(seq, "\n");

	seq_printf(seq, "UTP_F0_ACC = 0x%08x	            UTP_S0_ACC = 0x%08x\n",RTL_R32(UTP_F0_ACC),RTL_R32(UTP_S0_ACC));
	seq_printf(seq, "UTP_F1_ACC = 0x%08x	            UTP_S1_ACC = 0x%08x\n",RTL_R32(UTP_F1_ACC),RTL_R32(UTP_S1_ACC));
	seq_printf(seq, "UTP_F2_ACC = 0x%08x	            UTP_S2_ACC = 0x%08x\n",RTL_R32(UTP_F2_ACC),RTL_R32(UTP_S2_ACC));
	seq_printf(seq, "UTP_F3_ACC = 0x%08x	            UTP_S3_ACC = 0x%08x\n",RTL_R32(UTP_F3_ACC),RTL_R32(UTP_S3_ACC));
	seq_printf(seq, "\n");

	return 0;
}

static int hw_wanif_read(struct seq_file *seq, void *offset)
{
	int len = 0;

	rtl8685_dumpWanTable();

	return len;
}

static int hw_qmap_read(struct seq_file *seq, void *offset)
{
	int len = 0;

	rtl8685_dump_QMap();

	return len;
}

static int hw_counter_read(struct seq_file *seq, void *offset)
{
	int len = 0;

	seq_printf(seq, "TC counter (offset 0x01CC-01DC)\n");
	seq_printf(seq, "TCTX_PKT	=0x%08x		TCRX_PKT	=0x%08x\n",RTL_R32(TCTX_PKT),RTL_R32(TCRX_PKT));
	seq_printf(seq, "TCRX_GPKT	=0x%08x		TCRX_BPKT	=0x%08x\n",RTL_R32(TCRX_GPKT),RTL_R32(TCRX_BPKT));
	seq_printf(seq, "TCRX_IPKT	=0x%08x		\n",RTL_R32(TCRX_IPKT));


	seq_printf(seq, "\nTX  PTMx Codeword/Frame counter (offset 0x0220-023C)\n");
	seq_printf(seq, "TX_PTM0	=0x%08x		TX_PTM1	=0x%08x\n",RTL_R32(TX_TOTAL_CF_P0),RTL_R32(TX_TOTAL_CF_P1));
	seq_printf(seq, "TX_PTM2	=0x%08x		TX_PTM3	=0x%08x\n",RTL_R32(TX_TOTAL_CF_P2),RTL_R32(TX_TOTAL_CF_P3));
	seq_printf(seq, "TX_PTM4	=0x%08x		TX_PTM5	=0x%08x\n",RTL_R32(TX_TOTAL_CF_P4),RTL_R32(TX_TOTAL_CF_P5));
	seq_printf(seq, "TX_PTM6	=0x%08x		TX_PTM7	=0x%08x\n",RTL_R32(TX_TOTAL_CF_P6),RTL_R32(TX_TOTAL_CF_P7));

	seq_printf(seq, "\nTX  PTMx Data Codeword/Frame counter (offset 0x0240-025C)\n");
	seq_printf(seq, "TX_DATA_PTM0	=0x%08x		TX_DATA_PTM1	=0x%08x\n",RTL_R32(TX_DATA_CF_P0),RTL_R32(TX_DATA_CF_P1));
	seq_printf(seq, "TX_DATA_PTM2	=0x%08x		TX_DATA_PTM3	=0x%08x\n",RTL_R32(TX_DATA_CF_P2),RTL_R32(TX_DATA_CF_P3));
	seq_printf(seq, "TX_DATA_PTM4	=0x%08x		TX_DATA_PTM5	=0x%08x\n",RTL_R32(TX_DATA_CF_P4),RTL_R32(TX_DATA_CF_P5));
	seq_printf(seq, "TX_DATA_PTM6	=0x%08x		TX_DATA_PTM7	=0x%08x\n",RTL_R32(TX_DATA_CF_P6),RTL_R32(TX_DATA_CF_P7));

	seq_printf(seq, "\nRX non-short packet counter (offset 0x0260-026c)\n");
	seq_printf(seq, "H_F	=0x%08x		H_S	=0x%08x\n",RTL_R32(RX_HF_TPKT),RTL_R32(RX_HS_TPKT));
	seq_printf(seq, "L_F	=0x%08x		L_S	=0x%08x\n",RTL_R32(RX_LF_TPKT),RTL_R32(RX_LS_TPKT));

	seq_printf(seq, "\nRX short packet counter (offset 0x0270-027c)\n");
	seq_printf(seq, "H_F	=0x%08x		H_S	=0x%08x\n",RTL_R32(RX_HF_TPKT_SHT),RTL_R32(RX_HS_TPKT_SHT));
	seq_printf(seq, "L_F	=0x%08x		L_S	=0x%08x\n",RTL_R32(RX_LF_TPKT_SHT),RTL_R32(RX_LS_TPKT_SHT));

	seq_printf(seq, "\nRX CRC16 error packet counter (offset 0x0280-028c)\n");
	seq_printf(seq, "H_F	=0x%08x		H_S	=0x%08x\n",RTL_R32(RX_CRC_ERR_HF),RTL_R32(RX_CRC_ERR_HS));
	seq_printf(seq, "L_F	=0x%08x		L_S	=0x%08x\n",RTL_R32(RX_CRC_ERR_LF),RTL_R32(RX_CRC_ERR_LS));

	seq_printf(seq, "\nRX CRC16 error short packet counter (offset 0x0290-029c)\n");
	seq_printf(seq, "H_F	=0x%08x		H_S	=0x%08x\n",RTL_R32(RX_CRC_ERR_HF_SHT),RTL_R32(RX_CRC_ERR_HS_SHT));
	seq_printf(seq, "L_F	=0x%08x		L_S	=0x%08x\n",RTL_R32(RX_CRC_ERR_LF_SHT),RTL_R32(RX_CRC_ERR_LS_SHT));

	seq_printf(seq, "\nRX HDLC invlaid frame counter (offset 0x02a0-02a4)\n");
	seq_printf(seq, "Fast	=0x%08x		Slow	=0x%08x\n",RTL_R32(HDLC_INVLD_F),RTL_R32(HDLC_INVLD_S));

	seq_printf(seq, "\nRX coding err counter (offset 0x02a8-02ac)\n");
	seq_printf(seq, "Fast	=0x%08x		Slow	=0x%08x\n",RTL_R32(TC_CODING_ERR_F),RTL_R32(TC_CODING_ERR_S));

	seq_printf(seq, "\nRX data codeword counter (offset 0x02b0-02b4)\n");
	seq_printf(seq, "Fast	=0x%08x		Slow	=0x%08x\n",RTL_R32(RX_DPKT_PTM0),RTL_R32(RX_DPKT_PTM2));

	seq_printf(seq, "\nRX line0 codeword counter (offset 0x02b8-02c4)\n");
	seq_printf(seq, "FL	=0x%08x		FH	=0x%08x\n",RTL_R32(RX_TPKT_PTM0),RTL_R32(RX_TPKT_PTM1));
	seq_printf(seq, "SL	=0x%08x		SH	=0x%08x\n",RTL_R32(RX_TPKT_PTM2),RTL_R32(RX_TPKT_PTM3));
	
	if(ptm_bonding){
		seq_printf(seq, "\nRX line1 codeword counter (offset 0x02c8-02d4)\n");
		seq_printf(seq, "FL	=0x%08x		FH	=0x%08x\n",RTL_R32(RX_TPKT_PTM4),RTL_R32(RX_TPKT_PTM5));
		seq_printf(seq, "SL	=0x%08x		SH	=0x%08x\n",RTL_R32(RX_TPKT_PTM6),RTL_R32(RX_TPKT_PTM7));

		seq_printf(seq, "\nRX line2 codeword counter (offset 0x02d8-02e4)\n");
		seq_printf(seq, "FL	=0x%08x		FH	=0x%08x\n",RTL_R32(RX_TPKT_PTM8),RTL_R32(RX_TPKT_PTM9));
		seq_printf(seq, "SL	=0x%08x		SH	=0x%08x\n",RTL_R32(RX_TPKT_PTM10),RTL_R32(RX_TPKT_PTM11));

		seq_printf(seq, "\nRX line3 codeword counter (offset 0x02e8-02f4)\n");
		seq_printf(seq, "FL	=0x%08x		FH	=0x%08x\n",RTL_R32(RX_TPKT_PTM12),RTL_R32(RX_TPKT_PTM13));
		seq_printf(seq, "SL	=0x%08x		SH	=0x%08x\n",RTL_R32(RX_TPKT_PTM14),RTL_R32(RX_TPKT_PTM15));
	}
	return len;
}

static void rtl8685_set_ptm_counter(void)
{
	int i;

	/* Set pkt counter */
	for(i=0; i<5; i++){
		RTL_W32(TCTX_PKT+i*4, 0);
	}



	/* TX  PTMx Codeword/Frame */
	for(i=0;i<8;i++){
		RTL_W32(TX_TOTAL_CF_P0+i*4,0);
	}

	/* TX  PTMx Data Codeword/Frame */
	for(i=0;i<8;i++){
		RTL_W32(TX_DATA_CF_P0+i*4,0);
	}

	for(i=0;i<4;i++){
	/* RX non-short packet counter */
		RTL_W32(RX_HF_TPKT+i*4,0);

	/* RX short packet counter */
		RTL_W32(RX_HF_TPKT_SHT+i*4,0);
	
	/* RX CRC16 error packet counter */
		RTL_W32(RX_CRC_ERR_HF+i*4,0);

	/* RX CRC16 error short packet counter */
		RTL_W32(RX_CRC_ERR_HF_SHT+i*4,0);

		/* RX line 0*/
		RTL_W32(RX_TPKT_PTM0+i*4,0);

		/* RX line 1*/
		RTL_W32(RX_TPKT_PTM4+i*4,0);

		/* RX line 2*/
		RTL_W32(RX_TPKT_PTM8+i*4,0);

		/* RX line 3*/
		RTL_W32(RX_TPKT_PTM12+i*4,0);
	}


	for(i=0;i<2;i++){	
	/* RX HDLC invlaid frame counter */
		RTL_W32(HDLC_INVLD_F+i*4,0);

	/* RX coding err counter */
		RTL_W32(TC_CODING_ERR_F+i*4,0);

	/* RX data codeword counter */
		RTL_W32(RX_DPKT_PTM0+i*4,0);
	}


	/* RX codeword counter */
	for(i=0;i<16;i++){
		RTL_W32(RX_TPKT_PTM0+i*4,0);
	}

	/* WAN Interface counter */
	for(i=0;i<8;i++){
		RTL_W32(Entry_tx_pkt_cnt + i*4, 0);
		RTL_W32(Entry_rx_pkt_cnt + i*4, 0);
		RTL_W32(Entry_tx_byte_cnt + i*4, 0);
		RTL_W32(Entry_rx_byte_cnt + i*4, 0);
	}

	return;

}

#ifdef SACHEM_TX_RESET
static int hw_sachem_threshold_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	int len = 0;

	printk("IC Version: %s\n",(IS_RL6405)?"RL6405":"Not RL6405");
	if(IS_RL6405){
		printk("Sub Version: %s\n",(IS_RL6405A)?"A":"B");
	}
	printk("Sachem reset status: %s\n",(sachem_tx_reset_done)?"Done":"Undergoing");	
	printk("[Sachem TX Threshold] Step = %d\n",sachem_tx_reset_step);
	printk("[Sachem TX Threshold] Times = %d\n",sachem_tx_reset_times);
	
	return len;
}

static int hw_sachem_threshold_wirte( struct file *filp, const char *buff,unsigned long len, void *data )
{
	char 		tmpbuf[512];
	char		*strptr;
	char		*cmdptr;

	if (buff && !copy_from_user(tmpbuf, buff, len))
	{
		tmpbuf[len]=0;
		strptr=tmpbuf;
		if(strlen(strptr)==0) goto errout;
		cmdptr = strsep(&strptr," ");
		if(cmdptr==NULL) goto errout;

		if(strncmp(cmdptr, "step",4) == 0)
		{
			if(strptr==NULL) goto errout;
			cmdptr = strsep(&strptr," ");
			if(cmdptr==NULL) goto errout;
			sachem_tx_reset_step=simple_strtol(cmdptr, NULL, 0);
			
			printk("[%s] sachem_tx_reset_step = %d\n",__func__,sachem_tx_reset_step);
		}
		else if(strncmp(cmdptr, "times",4) == 0)
		{
			if(strptr==NULL) goto errout;
			cmdptr = strsep(&strptr," ");
			if(cmdptr==NULL) goto errout;
			sachem_tx_reset_times=simple_strtol(cmdptr, NULL, 0);
			
			printk("[%s] sachem_tx_reset_times = %d\n",__func__,sachem_tx_reset_times);
		}
	}	
	return len;

errout:
	printk("error input\n");
	return len;
}
#endif /* SACHEM_TX_RESET */

static int hw_port5_tx_limitrate_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	int len = 0;
	printk("ratelimite scale : %d\n",ratelimit_ratio);
	printk("ratelimite scale not30a : %d\n",ratelimit_ratio_not30a);
	printk("ratelimite state : ");
	if(ratelimit_state==-1) printk("stop\n");
	else if(ratelimit_state==0) printk("disable\n");
	else printk("enable\n");
#ifdef CONFIG_RTL8685
	printk("RTL8685C chip = %d\n", IS_RTL8685C() );
#endif /* CONFIG_RTL8685 */
	return len;
}
static int hw_port5_tx_limitrate_wirte( struct file *filp, const char *buff,unsigned long len, void *data )
{
	char 		tmpbuf[512];
	char		*strptr;
	char		*cmdptr;

	if (buff && !copy_from_user(tmpbuf, buff, len))
	{
		tmpbuf[len]=0;
		strptr=tmpbuf;
		if(strlen(strptr)==0) goto errout;
		cmdptr = strsep(&strptr," ");
		if(cmdptr==NULL) goto errout;

		if(strncmp(cmdptr, "disable",7) == 0)
		{
			ratelimit_state=0;
		}else if(strncmp(cmdptr, "enable",6) == 0)
		{
			ratelimit_state=1;
		}else if(strncmp(cmdptr, "stop",4) == 0)
		{
			ratelimit_state=-1;
		}else if(strncmp(cmdptr, "not30a",6) == 0)
		{
			if(strptr==NULL) goto errout;
			cmdptr = strsep(&strptr," ");
			if(cmdptr==NULL) goto errout;
			ratelimit_ratio_not30a=simple_strtol(cmdptr, NULL, 0);
		}else{
			ratelimit_ratio=simple_strtol(cmdptr, NULL, 0);
		}

		set_port5_engress_ratelimit();
	}	
	return len;

errout:
	printk("error input\n");
	return len;
}

static int hw_counter_write( struct file *filp, const char *buff,unsigned long len, void *data )
{
	char 		tmpbuf[512];
	char		*strptr;
	char		*cmdptr;

	if (buff && !copy_from_user(tmpbuf, buff, len))
	{
		tmpbuf[len] = '\0';
		
		strptr=tmpbuf;

		if(strlen(strptr)==0)
		{
			goto errout;
		}
		
		cmdptr = strsep(&strptr," ");
		if (cmdptr==NULL)
		{
			goto errout;
		}
		
#ifdef PTM_RESET
		if(strncmp(cmdptr, "1",1) == 0)
			ptm_event_process();
		if(strncmp(cmdptr, "2",1) == 0)
			ptm_timer_dbg=0;
		if(strncmp(cmdptr, "3",1) == 0)
			ptm_timer_dbg=1;
		#if defined(CONFIG_PTM_BONDING_MASTER)
		if(strncmp(cmdptr, "4",1) == 0){
			rx_fifo_reset=0;
			printk("bonding rx fifo reset function disable\n");
		}
		if(strncmp(cmdptr, "5",1) == 0){
			rx_fifo_reset=1;
			printk("bonding rx fifo reset function enable\n");
		}
		#endif
#endif
	
		/*parse command*/
		if(strncmp(cmdptr, "clearall",8) == 0)
		{
			printk("Reset PTM counter to zero!!\n");
			rtl8685_set_ptm_counter();
			
		}
	}

	return len;

errout:

	printk("error input\n");
	return len;

}

static int read_proc_open_hwreg(struct inode *inode, struct file *file) {
	return(single_open(file, hw_reg_read, NULL));
}

static struct file_operations fops_read_proc_hwreg = {
	.open     = read_proc_open_hwreg,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
};

static int read_proc_open_hwqos(struct inode *inode, struct file *file) {
	return(single_open(file, hw_qos_read, NULL));
}

static struct file_operations fops_read_proc_hwqos = {
	.open     = read_proc_open_hwqos,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
};

static int read_proc_open_hwwanif(struct inode *inode, struct file *file) {
	return(single_open(file, hw_qmap_read, NULL));
}

static struct file_operations fops_read_proc_hwwanif = {
	.open     = read_proc_open_hwwanif,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
};

static int read_proc_open_hwqmap(struct inode *inode, struct file *file) {
	return(single_open(file, hw_wanif_read, NULL));
}

static struct file_operations fops_read_proc_hwqmap = {
	.open     = read_proc_open_hwqmap,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
};

static ssize_t write_proc_hwcounter(struct file *file, const char __user * userbuf, size_t count, loff_t * off) {	
	return hw_counter_write(file, userbuf, count, NULL);
}
	
static int read_proc_open_hwcounter(struct inode *inode, struct file *file) {
	return(single_open(file, hw_counter_read, NULL));
}

static struct file_operations fops_proc_hwcounter = {
	.open     = read_proc_open_hwcounter,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
	.write    = write_proc_hwcounter,
};

static ssize_t write_proc_hw_port5_tx_limitrate(struct file *file, const char __user * userbuf, size_t count, loff_t * off) {	
	return hw_port5_tx_limitrate_wirte(file, userbuf, count, NULL);
}
	
static int read_proc_open_hw_port5_tx_limitrate(struct inode *inode, struct file *file) {
	return(single_open(file, hw_port5_tx_limitrate_read, NULL));
}

static struct file_operations fops_proc_hw_port5_tx_limitrate = {
	.open     = read_proc_open_hw_port5_tx_limitrate,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
	.write    = write_proc_hw_port5_tx_limitrate,
};

#ifdef SACHEM_TX_RESET
static ssize_t hw_sachem_threshold_write_proc(struct file *file, const char __user * userbuf, size_t count, loff_t * off) {	
	return hw_sachem_threshold_wirte(file, userbuf, count, NULL);
}
	
static int hw_sachem_threshold_open(struct inode *inode, struct file *file) {
	return(single_open(file, hw_sachem_threshold_read, NULL));
}
static struct file_operations fops_proc_hw_sachem_threshold = {
	.open     = hw_sachem_threshold_open,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
	.write    = hw_sachem_threshold_write_proc,
};
#endif /* SACHEM_TX_RESET */

#define PTM_PROC_DIR_NAME "ptm"
struct proc_dir_entry *ptm_proc_dir=NULL;
static struct proc_dir_entry *hw_counter, *hw_reg ,*hw_port5_tx_limitrate;
static struct proc_dir_entry *hw_qos, *hw_wanif, *hw_qmap;
#ifdef SACHEM_TX_RESET
static struct proc_dir_entry *hw_sachem_threshold;
#endif /* SACHEM_TX_RESET */
/***************************************************************************
 * Function Name: ptm_debug_proc_init
 * Description  :Show PTM debug message
 * Returns      : None.
 ***************************************************************************/
static void ptm_debug_proc_init(void){

	if(ptm_proc_dir==NULL)
		ptm_proc_dir = proc_mkdir(PTM_PROC_DIR_NAME,NULL);
	
	if(ptm_proc_dir)
	{
		hw_reg = proc_create_data("hw_reg", 0444, ptm_proc_dir, &fops_read_proc_hwreg, NULL);
		if(hw_reg == NULL) {
			printk("can't create proc entry for hw_reg\n");
		}

		hw_qos = proc_create_data("hw_qos", 0444, ptm_proc_dir, &fops_read_proc_hwqos, NULL);
		if(hw_qos == NULL) {
			printk("can't create proc entry for hw_qos\n");
		}

		hw_wanif = proc_create_data("hw_wanif", 0444, ptm_proc_dir, &fops_read_proc_hwwanif, NULL);
		if(hw_wanif == NULL) {
			printk("can't create proc entry for hw_wanif\n");
		}

		hw_qmap = proc_create_data("hw_qmap", 0444, ptm_proc_dir, &fops_read_proc_hwqmap, NULL);
		if(hw_qmap == NULL) {
			printk("can't create proc entry for hw_qmap\n");
		}

		hw_counter = proc_create_data("hw_counter", 0644, ptm_proc_dir, &fops_proc_hwcounter, NULL);
		if(hw_counter == NULL) {
			printk("can't create proc entry for hw_counter\n");
		}
		
		hw_port5_tx_limitrate = proc_create_data("hw_port5_tx_limitrate", 0644, ptm_proc_dir, &fops_proc_hw_port5_tx_limitrate, NULL);
		if(hw_port5_tx_limitrate == NULL) {			
			printk("can't create proc entry for hw_port5_tx_limitrate\n");
		}

#ifdef SACHEM_TX_RESET		
		hw_sachem_threshold = proc_create_data("hw_sachem_threshold", 0644, ptm_proc_dir, &fops_proc_hw_sachem_threshold, NULL);
		if(hw_sachem_threshold == NULL) {
			printk("can't create proc entry for hw_sachem_threshold\n");
		}
#endif /* SACHEM_TX_RESET */
	}
}

#if defined(CONFIG_DSL_ON_SLAVE)
int ipc_ptmdev_init(void)
#else
static int __init rtl8685_ptm_init(void)
#endif
{
	int err;
	struct device *dp;
	
	printk( "PTM: rtl8685_ptm_init entry\n" );

	/* Enable PTM module after PLL clock has provided*/
	REG32(BSP_IP_SEL) &= (~BSP_EN_PTM);
	REG32(BSP_IP_SEL) |= BSP_EN_PTM;
	printk( "PTM: disable => enable IP, %08x\n", REG32(BSP_IP_SEL) );

	/* Allocate PTM current tables */
	current_tbl = kzalloc(PTM_MAX_INTF*sizeof(rtl8685_WANtbl_t), GFP_KERNEL);
	if(current_tbl==NULL){
		printk("Out of memory !!\n");
		goto Err_MEM;
	}

	/* Allocate Q-Map tables */
	qmap_tbl = kzalloc(PTM_MAX_INTF*sizeof(unsigned int), GFP_KERNEL);
	if(qmap_tbl==NULL){
		printk("Out of memory !!\n");
		goto Err_MEM;
	}


	//init char device, /dev/ptm
	err=register_chrdev( PTM_DEV_MAJOR, PTM_DEV_NAME, &ptm_fops );
	if (err) {
		printk(KERN_ERR "failed to register ptm device (%d)\n", err);
		goto out_reg;
	}

	ptm_class = class_create(THIS_MODULE, PTM_DEV_NAME);
	if (IS_ERR(ptm_class)) {
		err = PTR_ERR(ptm_class);
		goto out_class;
	}
	dp = device_create(ptm_class, NULL, MKDEV(PTM_DEV_MAJOR, 0), NULL, PTM_DEV_NAME);

    if (IS_ERR(dp))
        printk( "ptm: create device failed\n" );
    else
        printk( "ptm: create device successed\n" );

	ptm_debug_proc_init();	

#ifdef PTM_RESET
	ptm_timer_init();
#endif

#if defined(CONFIG_PTM_BONDING_MASTER)
	spin_lock_init(&bonding_lock);
	mutex_init(&bonding_mutex);
#endif

	return 0;


out_class:
	unregister_chrdev(PTM_DEV_MAJOR, PTM_DEV_NAME);
out_reg:	
Err_MEM:
	if(qmap_tbl) kfree(qmap_tbl);
	if(current_tbl)kfree(current_tbl); 
	return FAILED;
}

/***************************************************************************
 * Function Name: rtl8685_ptm_exit
 * Description  : Final function that is called when the module is unloaded.
 * Returns      : None.
 ***************************************************************************/
static void __exit rtl8685_ptm_exit( void )
{
   	printk( "PTM: rtl8685_ptm_exit entry\n" );

	/* Free memory */
	if(current_tbl!=NULL){
		kfree(current_tbl);
	}

	if(qmap_tbl!=NULL){
		kfree(qmap_tbl);
	}

	/* Remove related proc */
	remove_proc_entry("hw_reg", ptm_proc_dir);
	remove_proc_entry("hw_qos", ptm_proc_dir);
	remove_proc_entry("hw_wanif", ptm_proc_dir);
	remove_proc_entry("hw_qmap", ptm_proc_dir);
	remove_proc_entry("hw_counter", ptm_proc_dir);
	remove_proc_entry("hw_port5_tx_limitrate", ptm_proc_dir);	
	remove_proc_entry(PTM_PROC_DIR_NAME, NULL);

	//cleanup char device, /dev/ptm
	unregister_chrdev(PTM_DEV_MAJOR, PTM_DEV_NAME);
	device_destroy(ptm_class, MKDEV(PTM_DEV_MAJOR, 0));
	class_destroy(ptm_class);

#ifdef PTM_RESET
	ptm_timer_exit();
#endif
} /* rtl8685_ptm_exit */

/***************************************************************************
 * Function Name: ptm_open
 * Description  : Called when an application opens this device.
 * Returns      : 0 - success
 ***************************************************************************/
static int ptm_open( struct inode *inode, struct file *filp )
{
	//printk("PTM: %s\n",__func__);

	return( 0 );
} /* ptm_open */

/***************************************************************************
 * Function Name: ptm_close
 * Description  : Called when an application stops this device.
 * Returns      : 0 - success
 ***************************************************************************/
static int ptm_close( struct inode *inode, struct file *filp )
{
	//printk("PTM: %s\n",__func__);

	return( 0 );
} /* ptm_close */

void set_nonbonding_board(void)
{
	int val = 0;

	/* Set PCR to zero before using */
	RTL_W32(PCR, 0x0);

	if (IS_RLE0822 || IS_6518()){
		val = SYSTEM_R32(0x0100);
		val &= (~(1<<24));
		val &= (~(1<<25));
		SYSTEM_W32(0x0100, val);

		val = SYSTEM_R32(0x010c);
		val &= (~(1<<8));
		SYSTEM_W32(0x010c, val);

		val = SYSTEM_R32(0x0114);
		val &= (~(1<<13));
		SYSTEM_W32(0x0114, val);
	}

	/* Non-bonding mode */
	RTL_W32(BD_SLV_NUM, (1<<1));
	RTL_W32(PCR, (TY_SYNC_F0 |TY_SYNC_S0 ));

	/* Cell avaliable in PTM */
	if(IS_RL6405 || IS_RLE0797){
		RTL_W32( PCR, (RTL_R32(PCR)|(rxutp_clav_en)) /*| (txutp_clav_en)*/);	
	}
	else if(IS_RLE0822 || IS_6518()){
		RTL_W32( PCR, (RTL_R32(PCR) | (rxutp_clav_en) | (txutp_clav_en) | (rxutp_clav_ext_en) | (txutp_clav_ext_en)) );	
	}
	
	RTL_W32( PCR, RTL_R32(PCR)|TC_SYNC_LC);
}

void bonding_enable(int line_num)
{
	//master enable
	if (line_num == 0)
		RTL_W32( PCR, (RTL_R32(PCR) | (rxutp_clav_en) | (txutp_clav_en)) );

	//slave enable
	if (line_num == 1)
		RTL_W32( PCR, (RTL_R32(PCR) | (rxutp_clav_ext_en) | (txutp_clav_ext_en)) );	
}

void bonding_disable(int line_num)
{
	//master disable
	if (line_num == 0)
		RTL_W32( PCR, (RTL_R32(PCR) & (~(rxutp_clav_en)) & (~(txutp_clav_en))) );

	//slave disable
	if (line_num == 1)
		RTL_W32( PCR, (RTL_R32(PCR) & (~(rxutp_clav_ext_en)) & (~(txutp_clav_ext_en))) );
}

void nonbonding_enable(void)
{
	RTL_W32( PCR, (RTL_R32(PCR) | (rxutp_clav_en) | (txutp_clav_en) | (rxutp_clav_ext_en) | (txutp_clav_ext_en)) );	
}

void nonbonding_disable(void)
{
	RTL_W32( PCR, (RTL_R32(PCR) & (~(rxutp_clav_en)) & (~(txutp_clav_en)) & (~(rxutp_clav_ext_en)) & (~(txutp_clav_en)) ) );
}

void change_bonding_timeout(int linkup_num)
{
	if (linkup_num == 1)
		RTL_W32(BD_TIMEOUT, 0x00000100);
	else if (linkup_num == 2)
		RTL_W32(BD_TIMEOUT, 0x0002ee20);
	
	return;
}

void set_bonding_board(void)
{
	int i, val = 0;
	ptm_bonding_line = 2;
	
	/* Set PCR to zero before using */
	RTL_W32(PCR, 0x0);

	if (IS_RLE0822 || IS_6518()){
		#if defined(CONFIG_PTM_BONDING_MASTER)
		// enable bonding master		
		val = SYSTEM_R32(0x0100);
		val |= (1<<25);
		val &= (~(1<<24));
		SYSTEM_W32(0x0100, val);
		#elif defined(CONFIG_PTM_BONDING_SLAVE)
		// enable bonding slave
		val = SYSTEM_R32(0x0100);
		val &= (~(1<<25));
		val |= (1<<24);
		SYSTEM_W32(0x0100, val);
		#endif

		// enable phyid_from_tpstc
		// the setting is needed on qa board 
//		val = SYSTEM_R32(0x010c);
//		val |= (1<<8);
//		SYSTEM_W32(0x010c, val);

		// set utopia driving for bonding function
		val = SYSTEM_R32(0x0114);
		val &= (~(1<<0));
		val &= (~(1<<1));
		val &= (~(1<<13));
		SYSTEM_W32(0x0114, val);

		/* UTP QoS */
		RTL_W32( UTP_APR32_F,  0x000e000e);
		RTL_W32( UTP_APR10_F,  0x7ffc7ffc);
		RTL_W32( UTP_APR32_S,  0x000e000e);
		RTL_W32( UTP_APR10_S,  0x7ffc7ffc);
		RTL_W32( UTP_APR_ALL,  0x7ffc7ffc);
		RTL_W32( UTP_WEIGHT_F, 0x01010101);
		RTL_W32( UTP_WEIGHT_S, 0x01010101);
		RTL_W32( UTP_QOS_MIS,  0x00983300);

		/* Bonding mode */
		RTL_W32(BD_SLV_NUM, (ptm_bonding_line << 1)); 		/* line number */
		RTL_W32(BOND_FRAG_LF, 0x000001f8);
		RTL_W32(BD_TIMEOUT, 0x00000100);

		for(i=0;i<ptm_bonding_line;i++){
			RTL_W32( PCR, RTL_R32(PCR)|(TY_SYNC_F0 <<i)| (TY_SYNC_S0 <<i));
		}
		
		RTL_W32( PCR, RTL_R32(PCR) | 0x5a000000 );
	}
	RTL_W32( PCR, RTL_R32(PCR)|TC_SYNC_LC);
}


/***************************************************************************
 * Function Name: ptm_ioctl
 * Description  : Main entry point for an application send issue ATM API
 *                requests.
 * Returns      : 0 - success or error
 ***************************************************************************/
static long ptm_ioctl(struct file *flip,
    unsigned int command, unsigned long inarg )
{
	int retVal = 0, i=0, j=0, val=0;
	char *data =(char *)inarg;
	unsigned char c;
	struct ptm_arg outarg;
	rtl8685_WANtbl_t  INTF_CTL;

	copy_from_user(&outarg, data, sizeof(struct ptm_arg));	
	//printk("Test command, pp_cmd = %d, command = %d\n", pp_cmd, command);
	switch(command){

		case PTM_SET_DEFAULT_TABLE:
			for(i=0; i<PTM_MAX_INTF;i++){
				memcpy(&INTF_CTL, &default_tbl[i], sizeof(rtl8685_WANtbl_t));	
				rtl8685_setWanTable(i,&INTF_CTL);
			}
			break;
			
		case PTM_GET_TABLE:
			rtl8685_dumpWanTable();
			break;
			
		case PTM_READ_DATA:			
			printk("Read Address 0x%08x with length 0x%08x\n", (unsigned int)outarg.cmd2, outarg.cmd3);
			Dumpreg((unsigned long)outarg.cmd2, (unsigned int)outarg.cmd3);
			break;
			
		case PTM_WRITE_DATA:
			printk("Write Address 0x%08x Value 0x%08x\n", (unsigned int)outarg.cmd2, outarg.cmd3);
			*(volatile u32*)(outarg.cmd2) = outarg.cmd3;
			break;

		case PTM_SET_SYSTEM:
			printk("Set System for PTM Bonding Board\n");
			set_bonding_board();
			break;

		case PTM_SET_HW:

			if(outarg.cmd2==1)
				ptm_bonding=1;
			else
				ptm_bonding=0;

			ptm_bonding_line = outarg.cmd3;

			if(ptm_bonding==0){
				printk("Set PTM as non-bonding mode\n");
			}else{
				printk("Set PTM as bonding mode with line %d\n",ptm_bonding_line);
			}
			
			ptm_init_hw();
			break;
			
		case PTM_GET_QMAP:
			rtl8685_dump_QMap();
			break;
		case PTM_STOP_HW:
			printk( "stop PTM HW\n" );
			ptm_stop_hw();
			break;
		case PTM_START_HW:
			printk( "start PTM HW\n" );
			ptm_start_hw();
			break;
		case PTM_CLEAN_WANIF:
			printk( "clean all wan interface\n" );
			memset( &INTF_CTL, 0, sizeof(INTF_CTL) );
			for(i=0; i<PTM_MAX_INTF;i++)
			{
				rtl8685_setWanTable(i,&INTF_CTL);
			}
			break;
		case PTM_CLEAN_QMAP:
			printk( "clean all queue mapping\n" );
			for(i=0; i<PTM_MAX_INTF;i++)
			{
				for(j=0; j<PTM_PRI_NUM;j++)
				{
					rtl8685_set_QMap(i,j,0);
				}
			}
			break;
		case PTM_SET_WANIF:
			printk( "set wanif, %u %u %u %02x%02x%02x%02x%02x%02x\n",
				outarg.cmd, outarg.a1, outarg.a2, outarg.a3[0],outarg.a3[1],
				outarg.a3[2],outarg.a3[3],outarg.a3[4],outarg.a3[5]);
			memset( &INTF_CTL, 0, sizeof(INTF_CTL) );
			INTF_CTL.Valid=1;
			i=outarg.cmd&0x7;
			sprintf(INTF_CTL.ifname, "wan_%u", i );
			INTF_CTL.SVlanID=outarg.a1 & 0x0fff;
			INTF_CTL.CVlanID=outarg.a2 & 0x0fff;
			memcpy( INTF_CTL.MAC, outarg.a3, 6);
			rtl8685_setWanTable(i,&INTF_CTL);
			break;
		case PTM_SET_QMAP:
			printk( "set qmap %u %u%u%u%u%u%u%u%u\n", outarg.cmd,
				outarg.arg[0], outarg.arg[1], outarg.arg[2], outarg.arg[3],
				outarg.arg[4], outarg.arg[5], outarg.arg[6], outarg.arg[7] );
			i=outarg.cmd&0x7;
			for(j=0; j<PTM_PRI_NUM;j++)
			{
				rtl8685_set_QMap(i,j,outarg.arg[j]&0x7);
			}
			break;
		case PTM_SET_STAGTYPE:
			printk( "set stagtype=0x%x\n", outarg.a1);
			RTL_R16(Stag_type_val)=outarg.a1;
			break;
		case PTM_SET_DUMMYVID:
			printk( "set dummyvid=0x%x\n", outarg.a1&0xfff );
			RTL_R16(Dummy_vid)=outarg.a1&0xfff;
			break;
		case PTM_SET_QMAPATSTAG:
			printk( "set qmapatstag tx=%u rx=%u\n", outarg.a1, outarg.a2 );
			c=RTL_R8(WCR);
			c &= ~(Qmap_at_stag_tx|Qmap_at_stag_rx);
			if(outarg.a1) c = c | Qmap_at_stag_tx;
			if(outarg.a2) c = c | Qmap_at_stag_rx;
			RTL_R8(WCR)=c;
			break;
		case PTM_SET_MODE:
			printk( "set mode %d\n", outarg.cmd);

			if(outarg.cmd) 
				ptm_set_mode(Mode2_Sel);
			else
				ptm_set_mode(Mode1_Sel);

			break;

		case PTM_DISABLE_TPSTC:
			printk("TPSTC DISABLE\n");
			SARCHEM_W32(0x5040, 0x1);
			break;

		case PTM_ENABLE_TPSTC:
			printk("TPSTC Enable\n");
			SARCHEM_W32(0x5040, 0x0);
			break;

		case PTM_RESET_TPSTC:
			printk("TPSTC RESET\n");
			ShowtimeTpsReset();
			ShowtimeTpsSet();
			break;

		case PTM_GET_TPSTC_TX_CNT:
			val =  (*(volatile u32*)(0xb8af0040));
			printk("Read Address: 0xb8af0040: %x\n", val);
			retVal = val;
			break;

		case PTM_GET_UTOPIA_TX_CNT:
			val =  (*(volatile u32*)(0xb8af0028));
			printk("Read Address: 0xb8af0028: %x\n", val);
			retVal = val;
			break;

		default:
			;//printk("error command\n");	
	}

	return retVal;
}


#if !defined(CONFIG_DSL_ON_SLAVE)
module_init(rtl8685_ptm_init);
module_exit(rtl8685_ptm_exit);
#endif