#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <net/rtl/rtl_alias.h>
#include <net/rtl/rtl865x_netif.h>

#if defined(CONFIG_RTL_8676HWNAT)
extern int vlan_passthru_enable;
extern int vlan_passthru_adslup;
extern int rtl865x_setDefACLForNetDecisionMiss(unsigned char start_ingressAclIdx, unsigned char end_ingressAclIdx,unsigned char start_egressAclIdx,unsigned char end_egressAclIdx);
#endif

#include "../xdsl_ctrl/xdsl_ctrl.h"

#ifdef CONFIG_REMOTE_ADSL_PHY
#include "xdsl_event.h"
#endif


// LX PLL SEL
#define REG_LX_PLL_SEL	0xB8000228
#define SHIFT_UTOPLL	12
#define MASK_UTOPLL	(0x0F<<SHIFT_UTOPLL)

extern __attribute__ ((mips16)) int GetLinkSpeed(char* pData);
unsigned long dslDataRate[2];
unsigned long dsl_tx_linkspeed=0;

#ifdef CONFIG_VDSL
extern unsigned int enable_mode, enable_phy_role;
#if defined(CONFIG_PTM_BONDING_MASTER) || defined(CONFIG_PTM_BONDING_SLAVE)
extern unsigned int BondStatus;
#endif
#if defined(CONFIG_PTM_BONDING_MASTER)
extern spinlock_t bonding_lock;
extern struct mutex bonding_mutex;
unsigned int linkup_linenum = 0;
#endif
#endif /*CONFIG_VDSL*/

#ifdef CONFIG_RTL8672_SAR
extern void AdslLinkUp_SAR(void);
extern void AdslLinkDown_SAR(void);
#endif /*CONFIG_RTL8672_SAR*/

#if defined(CONFIG_PTM_BONDING_MASTER)
extern void ptm_tpstc_reset(void);
extern void ptm_utopia_reset(char active_line);
extern void set_nonbonding_board(void);
extern void set_bonding_board(void);
extern void change_bonding_timeout(int);
extern void bonding_enable(int);
extern void bonding_disable(int);
extern void nonbonding_enable(void);
extern void nonbonding_disable(void);
#endif

#ifdef CONFIG_RTL8685_PTM_MII
extern void ptm_start_hw (void);
extern void ptm_stop_hw (void);
#if defined(CONFIG_DSL_ON_SLAVE)
extern void ptm_timer_add(void);
#endif
#endif /*CONFIG_RTL8685_PTM_MII*/

#ifdef CONFIG_RTL867X_NFBI_SLAVE
extern void mdio_set_dsllink_bit(int val);
#endif /*CONFIG_RTL867X_NFBI_SLAVE*/

#ifdef CONFIG_RTL867X_NFBI_MASTER
extern unsigned char slv_adslup;
#endif

#if defined(CONFIG_PTM_BONDING_MASTER)
unsigned char adslup=0;
#else
static unsigned char adslup=0;
#endif

unsigned char getADSLLinkStatus(void){
	return adslup;
}

#if defined(CONFIG_XDSL_CTRL_PHY_IS_DSL)
extern void (*xdsl_ctrl_event_notify_hook)(int event);
#endif

#if defined(CONFIG_RTL8685_PTM_MII)
#if defined(CONFIG_RTK_PTM_US_CTRL)
extern void rtk_ptm_us_ctrl_decision(int isLinkup);
#endif

void enable_ptm(void)
{
	struct net_device *ndev;
	#if defined(CONFIG_PTM_BONDING_MASTER)
	char linkup_line=0;

	mutex_lock(&bonding_mutex);
	#endif

	#if defined(CONFIG_RTL867X_NFBI_MASTER) && defined(CONFIG_PTM_BONDING_MASTER)
	if (adslup || slv_adslup) 
	#endif
	{
        #if defined(CONFIG_RTK_PTM_US_CTRL)
		rtk_ptm_us_ctrl_decision(1);
        #endif
		#if defined(CONFIG_PTM_BONDING_MASTER)
		if (BondStatus){
			linkup_linenum++;
			if (linkup_linenum==1){
				//default bonding timeout setting is for single line
				set_bonding_board();
			} else if (linkup_linenum == 2){
				change_bonding_timeout(2);
			}

			if (adslup){
				printk("enable bonding master\n");
				bonding_enable(0);
				//master status: active
				linkup_line |= (1<<0);
			}

			if (slv_adslup){
				printk("enable bonding slave\n");
				bonding_enable(1);
				//slave status: active
				linkup_line |= (1<<1);
			}
			ptm_tpstc_reset();

		} else {
			set_nonbonding_board();
			nonbonding_enable();
		}
		#endif
		ndev = dev_get_by_name(&init_net, ALIASNAME_PTM0);
		if (ndev) {
			netif_carrier_on(ndev);
			dev_put(ndev);
		}
		#ifdef CONFIG_RTL8685S
		// hack -- fixup UTOPIA clock to avoid potential ptm tx error (when enabling throughput enhancement)
		*(volatile u32 *)REG_LX_PLL_SEL = ((*(volatile u32 *)REG_LX_PLL_SEL)&(~(MASK_UTOPLL)))|(0x05<<SHIFT_UTOPLL);
		#endif
		#ifdef CONFIG_RTL8685_PTM_MII
		#if defined(CONFIG_DSL_ON_SLAVE)
		ptm_timer_add();
		#else
		#if defined(CONFIG_PTM_BONDING_MASTER)
		if ((BondStatus && linkup_linenum==1) || (BondStatus==0))
		#endif
		ptm_start_hw();
		#endif
		#endif /*CONFIG_RTL8685_PTM_MII*/

		#if defined(CONFIG_PTM_BONDING_MASTER)
		if (BondStatus){
			ptm_utopia_reset(linkup_line);
		}
		#endif
	}	
	#if defined(CONFIG_PTM_BONDING_MASTER)
	mutex_unlock(&bonding_mutex);
	#endif
}

void disable_ptm(void)
{
	struct net_device *ndev;
	#if defined(CONFIG_PTM_BONDING_MASTER)
	char linkup_line;

	mutex_lock(&bonding_mutex);
	#endif

	#if defined(CONFIG_RTL867X_NFBI_MASTER) && defined(CONFIG_PTM_BONDING_MASTER)
	if (BondStatus)
	{
		linkup_linenum--;
		//bit0: master, bit1: slave
		linkup_line = ((1<<0)|(1<<1));
		if (linkup_linenum==1){
			change_bonding_timeout(1);
		}

		if (adslup==0){
			printk("disable bonding master\n");
			bonding_disable(0);
			//master status: inactive
			linkup_line &= (~(1<<0));
		}
			
		if (slv_adslup==0){
			printk("disable bonding slave\n");
			bonding_disable(1);
			//slave status: inactive
			linkup_line &= (~(1<<1));
		}

		if (linkup_linenum==1){
			ptm_tpstc_reset();
			ptm_utopia_reset(linkup_line);
		}
	}
	else {
		nonbonding_disable();
	}
	#endif	

	#if defined(CONFIG_RTL867X_NFBI_MASTER) && defined(CONFIG_PTM_BONDING_MASTER)
	if ((BondStatus && adslup==0 && slv_adslup==0) || (BondStatus==0 && adslup==0))
	#endif
	{
		ndev = dev_get_by_name(&init_net, ALIASNAME_PTM0);
		if (ndev) {
			netif_carrier_off(ndev);
			dev_put(ndev);
		}

		ptm_stop_hw();
		#if defined(CONFIG_RTK_PTM_US_CTRL)
		rtk_ptm_us_ctrl_decision(0);
		#endif
	}

	#if defined(CONFIG_PTM_BONDING_MASTER)
	mutex_unlock(&bonding_mutex);
	#endif

}
#endif

void AdslLinkUp(void)
{
	adslup=1;

	GetLinkSpeed((char*) dslDataRate);
	dsl_tx_linkspeed = dslDataRate[0];
	// Kaohj -- update link state
	printk("ADSL Link up\n");
#ifdef CONFIG_VDSL
//	printk( "AdslLinkUp: enable_mode=%d, enable_phy_role=%d\n", enable_mode, enable_phy_role);
	if(enable_mode)
	{
		#if defined(CONFIG_RTL8685_PTM_MII)
		enable_ptm();
		#endif

		#ifdef CONFIG_REMOTE_ADSL_PHY
		remotePhy_evt_notify(SLAVE_PTM_LINK_UP, NULL);	
		#endif

		#if defined(CONFIG_XDSL_CTRL_PHY_IS_DSL)
		(*xdsl_ctrl_event_notify_hook)(2);
		#endif

	}else
#endif /*CONFIG_VDSL*/
	{
		#if defined(CONFIG_RTL_8676HWNAT)
		vlan_passthru_adslup = 1;
		rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU, RTL865X_ACLTBL_ALL_TO_CPU, RTL865X_ACLTBL_ALL_TO_CPU);
		#endif
		#ifdef CONFIG_RTL8672_SAR
		AdslLinkUp_SAR();
		#endif /*CONFIG_RTL8672_SAR*/

		#ifdef CONFIG_REMOTE_ADSL_PHY
		remotePhy_evt_notify(SLAVE_ATM_LINK_UP, NULL);	
		#endif
	}

#if defined(CONFIG_PTM_BONDING_SLAVE) && defined(CONFIG_RTL867X_NFBI_SLAVE)
	if (BondStatus){
		printk("bonding mode\n");
		mdio_set_dsllink_bit(1);
	} else {
		printk("non bonding mode\n");
	}
#endif /*CONFIG_RTL867X_NFBI_SLAVE*/
}

void AdslLinkDown(void)
{
	adslup=0;

	dsl_tx_linkspeed=0;
	// Kaohj -- update link state
	printk("ADSL Link down\n");
#ifdef CONFIG_VDSL
//	printk( "AdslLinkDown: enable_mode=%d, enable_phy_role=%d\n", enable_mode, enable_phy_role);
	if(enable_mode)
	{
		#if defined(CONFIG_RTL8685_PTM_MII)
		disable_ptm();
		#endif

		#ifdef CONFIG_REMOTE_ADSL_PHY
		remotePhy_evt_notify(SLAVE_PTM_LINK_DOWN, NULL);	
		#endif

		#if defined(CONFIG_XDSL_CTRL_PHY_IS_DSL)
		(*xdsl_ctrl_event_notify_hook)(3);
		#endif
	}
#endif /*CONFIG_VDSL*/

#ifdef CONFIG_RTL8672_SAR
	AdslLinkDown_SAR();
#endif /*CONFIG_RTL8672_SAR*/


#ifdef CONFIG_REMOTE_ADSL_PHY
		remotePhy_evt_notify(SLAVE_ATM_LINK_DOWN, NULL);	
#endif


	#if defined(CONFIG_RTL_8676HWNAT)
	vlan_passthru_adslup = 0;
	if(vlan_passthru_enable)
		rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL, RTL865X_ACLTBL_PERMIT_ALL, RTL865X_ACLTBL_PERMIT_ALL);
	else
		rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU);
	#endif
#if defined(CONFIG_PTM_BONDING_SLAVE) && defined(CONFIG_RTL867X_NFBI_SLAVE)
	if (BondStatus){
		mdio_set_dsllink_bit(0);
	} 
#endif /*CONFIG_RTL867X_NFBI_SLAVE*/
}


#ifndef CONFIG_RTL8672_SAR
void set_atm_data_mode(int mode)
{
	return;
}
#endif /*CONFIG_RTL8672_SAR*/

#if !defined(CONFIG_RTL_819X_SWCORE) && defined(CONFIG_RTL8685_PTM_MII) && !defined(CONFIG_PTM_BONDING_SLAVE)
/*
 *	Called by DSL driver to send ERB Ethernet packet in G.Vector mode.
 *	See rtl_nic.c for real definition.
 */
int re865x_send_ERB(char* ERB_data, int ERB_data_len
			,int line_id,int sync_symbol_count,int segment_code,unsigned char* VCE_macaddr)
{
	return 0;
}
#endif /*CONFIG_PTMWAN*/

#if defined(CONFIG_PTM_BONDING_MASTER)
// for bonding master dsl lib comiple
long sendcmd_to_master(int dev_num, unsigned int command, unsigned long inarg)
{
	return 0;
}

void RestartWan(void)
{

}
EXPORT_SYMBOL(sendcmd_to_master);
EXPORT_SYMBOL(RestartWan);
#endif
EXPORT_SYMBOL(enable_ptm);
EXPORT_SYMBOL(disable_ptm);