/******************************************************************************
 *
 * Copyright(c) 2007 - 2017 Realtek Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 *****************************************************************************/
#define _HCI_INTF_C_

#include <drv_types.h>
#include <hal_data.h>

#include <linux/pci_regs.h>

#ifndef CONFIG_PCI_HCI

	#error "CONFIG_PCI_HCI shall be on!\n"

#endif


#ifdef CONFIG_80211N_HT
	extern int rtw_ht_enable;
	extern int rtw_bw_mode;
	extern int rtw_ampdu_enable;/* for enable tx_ampdu */
#endif

#ifdef CONFIG_GLOBAL_UI_PID
int ui_pid[3] = {0, 0, 0};
#endif

extern int pm_netdev_open(struct net_device *pnetdev, u8 bnormal);
int rtw_resume_process(_adapter *padapter);

#ifdef CONFIG_PM
	static int rtw_pci_suspend(struct pci_dev *pdev, pm_message_t state);
	static int rtw_pci_resume(struct pci_dev *pdev);
#endif

static int rtw_drv_init(struct pci_dev *pdev, const struct pci_device_id *pdid);
static void rtw_dev_remove(struct pci_dev *pdev);
static void rtw_dev_shutdown(struct pci_dev *pdev);

static struct specific_device_id specific_device_id_tbl[] = {
	{.idVendor = 0x0b05, .idProduct = 0x1791, .flags = SPEC_DEV_ID_DISABLE_HT},
	{.idVendor = 0x13D3, .idProduct = 0x3311, .flags = SPEC_DEV_ID_DISABLE_HT},
	{}
};

struct pci_device_id rtw_pci_id_tbl[] = {
#ifdef CONFIG_RTL8188E
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8179), .driver_data = RTL8188E},
#endif
#ifdef CONFIG_RTL8812A
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8812), .driver_data = RTL8812},
#endif
#ifdef CONFIG_RTL8821A
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8821), .driver_data = RTL8821},
#endif
#ifdef CONFIG_RTL8192E
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x818B), .driver_data = RTL8192E},
#endif
#ifdef CONFIG_RTL8192F
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0xf192), .driver_data = RTL8192F},
#endif
#ifdef CONFIG_RTL8723B
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0xb723), .driver_data = RTL8723B},
#endif
#ifdef CONFIG_RTL8723D
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0xd723), .driver_data = RTL8723D},
#endif
#ifdef CONFIG_RTL8814A
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8813), .driver_data = RTL8814A},
#endif
#ifdef CONFIG_RTL8822B
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0xB822), .driver_data = RTL8822B},
#endif
#ifdef CONFIG_RTL8821C
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0xC821), .driver_data = RTL8821C},
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0xC82A), .driver_data = RTL8821C},
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0xC82B), .driver_data = RTL8821C},
#endif
#ifdef CONFIG_RTL8822C
	{PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0xC822), .driver_data = RTL8822C},
#endif
	{},
};

struct pci_drv_priv {
	struct pci_driver rtw_pci_drv;
	int drv_registered;
};


static struct pci_drv_priv pci_drvpriv = {
	.rtw_pci_drv.name = (char *)DRV_NAME,
	.rtw_pci_drv.probe = rtw_drv_init,
	.rtw_pci_drv.remove = rtw_dev_remove,
	.rtw_pci_drv.shutdown = rtw_dev_shutdown,
	.rtw_pci_drv.id_table = rtw_pci_id_tbl,
#ifdef CONFIG_PM
	.rtw_pci_drv.suspend = rtw_pci_suspend,
	.rtw_pci_drv.resume = rtw_pci_resume,
#endif
};


MODULE_DEVICE_TABLE(pci, rtw_pci_id_tbl);


static u16 pcibridge_vendors[PCI_BRIDGE_VENDOR_MAX] = {
	INTEL_VENDOR_ID,
	ATI_VENDOR_ID,
	AMD_VENDOR_ID,
	SIS_VENDOR_ID
};

#define PCI_PM_CAP_ID		0x01	/* The Capability ID for PME function */
void	PlatformClearPciPMEStatus(PADAPTER Adapter)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(Adapter);
	struct pci_dev	*pdev = pdvobjpriv->ppcidev;
	BOOLEAN		PCIClkReq = _FALSE;
	u8	CapId = 0xff;
	u8	CapPointer = 0;
	/* u16	CapHdr; */
	RT_PCI_CAPABILITIES_HEADER CapHdr;
	u8	PMCSReg;
	int	result;

	/* Get the Capability pointer first, */
	/* the Capability Pointer is located at offset 0x34 from the Function Header */

	result = pci_read_config_byte(pdev, 0x34, &CapPointer);
	if (result != 0)
		RTW_INFO("%s() pci_read_config_byte 0x34 Failed!\n", __func__);
	else {
		RTW_INFO("PlatformClearPciPMEStatus(): PCI configration 0x34 = 0x%2x\n", CapPointer);
		do {
			/* end of pci capability */
			if (CapPointer == 0x00) {
				CapId = 0xff;
				break;
			}

			/* result = pci_read_config_word(pdev, CapPointer, &CapHdr); */
			result = pci_read_config_byte(pdev, CapPointer, &CapHdr.CapabilityID);
			if (result != 0) {
				RTW_INFO("%s() pci_read_config_byte %x Failed!\n", __func__, CapPointer);
				CapId = 0xff;
				break;
			}

			result = pci_read_config_byte(pdev, CapPointer + 1, &CapHdr.Next);
			if (result != 0) {
				RTW_INFO("%s() pci_read_config_byte %x Failed!\n", __func__, CapPointer);
				CapId = 0xff;
				break;
			}

			/* CapId = CapHdr & 0xFF; */
			CapId = CapHdr.CapabilityID;

			RTW_INFO("PlatformClearPciPMEStatus(): in pci configration1, CapPointer%x = %x\n", CapPointer, CapId);

			if (CapId == PCI_PM_CAP_ID)
				break;
			else {
				/* point to next Capability */
				/* CapPointer = (CapHdr >> 8) & 0xFF; */
				CapPointer = CapHdr.Next;
			}
		} while (_TRUE);

		if (CapId == PCI_PM_CAP_ID) {
			/* Get the PM CSR (Control/Status Register), */
			/* The PME_Status is located at PM Capatibility offset 5, bit 7 */
			result = pci_read_config_byte(pdev, CapPointer + 5, &PMCSReg);
			if (PMCSReg & BIT7) {
				/* PME event occured, clear the PM_Status by write 1 */
				PMCSReg = PMCSReg | BIT7;

				pci_write_config_byte(pdev, CapPointer + 5, PMCSReg);
				PCIClkReq = _TRUE;
				/* Read it back to check */
				pci_read_config_byte(pdev, CapPointer + 5, &PMCSReg);
				RTW_INFO("PlatformClearPciPMEStatus(): Clear PME status 0x%2x to 0x%2x\n", CapPointer + 5, PMCSReg);
			} else
				RTW_INFO("PlatformClearPciPMEStatus(): PME status(0x%2x) = 0x%2x\n", CapPointer + 5, PMCSReg);
		} else
			RTW_INFO("PlatformClearPciPMEStatus(): Cannot find PME Capability\n");
	}

	RTW_INFO("PME, value_offset = %x, PME EN = %x\n", CapPointer + 5, PCIClkReq);
}

void rtw_pci_aspm_config_clkreql0sl1(_adapter *padapter)
{
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);
	u8 tmp8 = 0;
	u16 tmp16 = 0;

	/* 0x70f Bit7 for L0s */
	tmp8 = rtw_hal_pci_dbi_read(padapter, 0x70f);

	if (pHalData->pci_backdoor_ctrl & PCI_BC_ASPM_L0s)
		tmp8 |= BIT7;
	else
		tmp8 &= (~BIT7);

	/* Default set L1 entrance latency to 16us */
	/* L0s: b[0-2], L1: b[3-5]*/
	if (pHalData->pci_backdoor_ctrl & PCI_BC_ASPM_L1) {
		tmp8 &= (~0x38);
		tmp8 |= 0x20;
#ifdef CONFIG_PCI_DYNAMIC_ASPM
		pHalData->bAspmL1LastIdle = 1;
#endif
	}

	rtw_hal_pci_dbi_write(padapter, 0x70f, tmp8);


	/* 0x719 Bit 3 for L1 ,  Bit4 for clock req */
	tmp8 = rtw_hal_pci_dbi_read(padapter, 0x719);

	if (pHalData->pci_backdoor_ctrl & PCI_BC_ASPM_L1)
		tmp8 |= BIT3;
	else
		tmp8 &= (~BIT3);

	if (pHalData->pci_backdoor_ctrl & PCI_BC_CLK_REQ)
		tmp8 |= BIT4;
	else
		tmp8 &= (~BIT4);

	rtw_hal_pci_dbi_write(padapter, 0x719, tmp8);

	if (pHalData->pci_backdoor_ctrl & PCI_BC_CLK_REQ) {
		tmp16 = rtw_hal_pci_mdio_read(padapter, 0x10);
		rtw_hal_pci_mdio_write(padapter, 0x10, (tmp16 | BIT2));
	}
}

void rtw_pci_aspm_config_l1off(_adapter *padapter)
{
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);

	u8 enable_l1off = _FALSE;

	if (pHalData->pci_backdoor_ctrl & PCI_BC_ASPM_L1Off)
		enable_l1off = rtw_hal_pci_l1off_nic_support(padapter);

	padapter->hal_func.hal_set_l1ssbackdoor_handler(padapter, enable_l1off);

}

void rtw_pci_aspm_config_l1off_general(_adapter *padapter, u8 enablel1off)
{

	u8 tmp8;
	u16 tmp16;

	if (enablel1off) {
		/* 0x718 Bit5 for L1SS */
		tmp8 = rtw_hal_pci_dbi_read(padapter, 0x718);
		rtw_hal_pci_dbi_write(padapter, 0x718, (tmp8 | BIT5));

		tmp16 = rtw_hal_pci_mdio_read(padapter, 0x1b);
		rtw_hal_pci_mdio_write(padapter, 0x1b, (tmp16 | BIT4));
	} else {
		tmp8 = rtw_hal_pci_dbi_read(padapter, 0x718);
		rtw_hal_pci_dbi_write(padapter, 0x718, (tmp8 & (~BIT5)));
	}

}

#ifdef CONFIG_PCI_DYNAMIC_ASPM
void rtw_pci_aspm_config_dynamic_l1_ilde_time(_adapter *padapter)
{
	BOOLEAN	 bCurrentIdle = 1;	/* Default idle 4us (0x70F = 0x17)*/
	HAL_DATA_TYPE *pHalData	= GET_HAL_DATA(padapter);
	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
	struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
	int current_tx_tp = pdvobjpriv->traffic_stat.cur_tx_tp;
	int current_rx_tp = pdvobjpriv->traffic_stat.cur_rx_tp;
	int current_tp = current_tx_tp + current_rx_tp;
	u8 tmp8 = 0;

	if (padapter->registrypriv.wifi_spec)
		return;

	if (!(pHalData->pci_backdoor_ctrl & PCI_BC_ASPM_L1))
		return;

#if 0
	RTW_INFO("current_tx_tp = %d\n", current_tx_tp);
	RTW_INFO("current_rx_tp = %d\n", current_rx_tp);
	RTW_INFO("current_tp = %d\n", current_tp);
#endif

	if ((rtw_linked_check(padapter) == _TRUE) && 
		((current_tx_tp >= 50)||
		(current_rx_tp >= 50)))
		/*(current_rx_tp >= 10))*/
		/*(current_tp >= 10))*/
	{
		bCurrentIdle = 0;
	}	
	else
	{
		bCurrentIdle = 1;
	}

	if(bCurrentIdle != pHalData->bAspmL1LastIdle)
	{
		pHalData->bAspmL1LastIdle = bCurrentIdle;

		tmp8 = rtw_hal_pci_dbi_read(padapter, 0x70F);
		tmp8 &= (~0x38);

		if(bCurrentIdle) {
			/*tmp8 |= 0x10; *//*L1 entrance latency: 4us*/
			/*tmp8 |= 0x18; *//*L1 entrance latency: 8us*/
			tmp8 |= 0x20; /*L1 entrance latency: 16us*/
			rtw_hal_pci_dbi_write(padapter, 0x70F, tmp8 );
		}
		else {
			tmp8 |= 0x28; /*L1 entrance latency: 32us*/
			rtw_hal_pci_dbi_write(padapter, 0x70F, tmp8 );
		}
	}

}
#endif

void rtw_pci_dump_aspm_info(_adapter *padapter)
{
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(padapter);
	struct pci_priv	*pcipriv = &(pdvobjpriv->pcipriv);
	u8 tmp8 = 0;
	u16 tmp16 = 0;
	u32 tmp32 = 0;
	u8 l1_idle = 0;


	RTW_INFO("***** ASPM Capability *****\n");

	pci_read_config_dword(pdvobjpriv->ppcidev, pcipriv->pciehdr_offset + PCI_EXP_LNKCAP, &tmp32);

	RTW_INFO("CLK REQ:	%s\n", (tmp32&PCI_EXP_LNKCAP_CLKPM) ? "Enable" : "Disable");

	RTW_INFO("ASPM L0s:	%s\n", (tmp32&BIT10) ? "Enable" : "Disable");
	RTW_INFO("ASPM L1:	%s\n", (tmp32&BIT11) ? "Enable" : "Disable");

	tmp8 = rtw_hal_pci_l1off_capability(padapter);
	RTW_INFO("ASPM L1OFF:%s\n", tmp8 ? "Enable" : "Disable");

	RTW_INFO("***** ASPM CTRL Reg *****\n");

	pci_read_config_word(pdvobjpriv->ppcidev, pcipriv->pciehdr_offset + PCI_EXP_LNKCTL, &tmp16);

	RTW_INFO("CLK REQ:	%s\n", (tmp16&PCI_EXP_LNKCTL_CLKREQ_EN) ? "Enable" : "Disable");
	RTW_INFO("ASPM L0s:	%s\n", (tmp16&BIT0) ? "Enable" : "Disable");
	RTW_INFO("ASPM L1:	%s\n", (tmp16&BIT1) ? "Enable" : "Disable");

	tmp8 = rtw_hal_pci_l1off_nic_support(padapter);
	RTW_INFO("ASPM L1OFF:%s\n", tmp8 ? "Enable" : "Disable");

	RTW_INFO("***** ASPM Backdoor *****\n");

	tmp8 = rtw_hal_pci_dbi_read(padapter, 0x719);
	RTW_INFO("CLK REQ:	%s\n", (tmp8 & BIT4) ? "Enable" : "Disable");

	tmp8 = rtw_hal_pci_dbi_read(padapter, 0x70f);
	l1_idle = tmp8 & 0x38;
	RTW_INFO("ASPM L0s:	%s\n", (tmp8&BIT7) ? "Enable" : "Disable");

	tmp8 = rtw_hal_pci_dbi_read(padapter, 0x719);
	RTW_INFO("ASPM L1:	%s\n", (tmp8 & BIT3) ? "Enable" : "Disable");

	tmp8 = rtw_hal_pci_dbi_read(padapter, 0x718);
	RTW_INFO("ASPM L1OFF:%s\n", (tmp8 & BIT5) ? "Enable" : "Disable");
	
	RTW_INFO("********* MISC **********\n");
	RTW_INFO("ASPM L1 Idel Time: 0x%x\n", l1_idle>>3);
	RTW_INFO("*************************\n");
}

void rtw_pci_aspm_config(_adapter *padapter)
{
	rtw_pci_aspm_config_clkreql0sl1(padapter);
	rtw_pci_aspm_config_l1off(padapter);
	rtw_pci_dump_aspm_info(padapter);
}

static u8 rtw_pci_platform_switch_device_pci_aspm(_adapter *padapter, u8 value)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(padapter);
	struct pci_priv	*pcipriv = &(pdvobjpriv->pcipriv);
	BOOLEAN		bResult = _FALSE;
	int	Result = 0;
	int	error;

	Result = pci_write_config_byte(pdvobjpriv->ppcidev, pcipriv->pciehdr_offset + 0x10, value);	/* enable I/O space */
	RTW_INFO("PlatformSwitchDevicePciASPM(0x%x) = 0x%x\n", pcipriv->pciehdr_offset + 0x10, value);
	if (Result != 0) {
		RTW_INFO("PlatformSwitchDevicePciASPM() Failed!\n");
		bResult = _FALSE;
	} else
		bResult = _TRUE;

	return bResult;
}

/*
 * When we set 0x01 to enable clk request. Set 0x0 to disable clk req.
 */
static u8 rtw_pci_switch_clk_req(_adapter *padapter, u8 value)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(padapter);
	u8	buffer, bResult = _FALSE;
	int	error;

	buffer = value;

	if (!rtw_is_hw_init_completed(padapter))
		return bResult;

	/* the clock request is located at offset 0x81, suppose the PCIE Capability register is located at offset 0x70 */
	/* the correct code should be: search the PCIE capability register first and then the clock request is located offset 0x11 */
	error = pci_write_config_byte(pdvobjpriv->ppcidev, 0x81, buffer);
	if (error != 0)
		RTW_INFO("rtw_pci_switch_clk_req error (%d)\n", error);
	else
		bResult = _TRUE;

	return bResult;
}

/*Disable RTL8192SE ASPM & Disable Pci Bridge ASPM*/
void rtw_pci_disable_aspm(_adapter *padapter)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(padapter);
	struct pwrctrl_priv	*pwrpriv = dvobj_to_pwrctl(pdvobjpriv);
	struct pci_dev	*pdev = pdvobjpriv->ppcidev;
	struct pci_dev	*bridge_pdev = pdev->bus->self;
	struct pci_priv	*pcipriv = &(pdvobjpriv->pcipriv);
	u8	linkctrl_reg;
	u8	pcibridge_linkctrlreg, aspmlevel = 0;


	/* We shall check RF Off Level for ASPM function instead of registry settings, revised by Roger, 2013.03.29. */
	if (!(pwrpriv->reg_rfps_level & (RT_RF_LPS_LEVEL_ASPM | RT_RF_PS_LEVEL_ALWAYS_ASPM)))
		return;

	if (!rtw_is_hw_init_completed(padapter))
		return;

	if (pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_UNKNOWN)
		return;

	linkctrl_reg = pcipriv->linkctrl_reg;
	pcibridge_linkctrlreg = pcipriv->pcibridge_linkctrlreg;

	/* Set corresponding value. */
	aspmlevel |= BIT(0) | BIT(1);
	linkctrl_reg &= ~aspmlevel;
	pcibridge_linkctrlreg &= ~aspmlevel;

	/*  */
	/* 09/08/21 MH From Sd1 suggestion. we need to adjust ASPM enable sequence */
	/* CLK_REQ ==> delay 50us ==> Device ==> Host ==> delay 50us */
	/*  */

	if (pwrpriv->reg_rfps_level & RT_RF_OFF_LEVL_CLK_REQ) {
		RT_CLEAR_PS_LEVEL(pwrpriv, RT_RF_OFF_LEVL_CLK_REQ);
		rtw_pci_switch_clk_req(padapter, 0x0);
	}

	{
		/*for promising device will in L0 state after an I/O.*/
		u8 tmp_u1b;

		pci_read_config_byte(pdev, (pcipriv->pciehdr_offset + 0x10), &tmp_u1b);
	}

	rtw_pci_platform_switch_device_pci_aspm(padapter, linkctrl_reg);

	rtw_udelay_os(50);

	/* When there exists anyone's BusNum, DevNum, and FuncNum that are set to 0xff, */
	/* we do not execute any action and return. Added by tynli. */
	if ((pcipriv->busnumber == 0xff && pcipriv->devnumber == 0xff && pcipriv->funcnumber == 0xff) ||
	    (pcipriv->pcibridge_busnum == 0xff && pcipriv->pcibridge_devnum == 0xff && pcipriv->pcibridge_funcnum == 0xff)) {
		/* Do Nothing!! */
	} else {
		/* 4  */ /* Disable Pci Bridge ASPM */
		pci_write_config_byte(bridge_pdev, (pcipriv->pcibridge_pciehdr_offset + 0x10), pcibridge_linkctrlreg);
		RTW_INFO("PlatformDisableASPM():PciBridge Write reg[%x] = %x\n",
			(pcipriv->pcibridge_pciehdr_offset + 0x10), pcibridge_linkctrlreg);
		rtw_udelay_os(50);
	}

}

/*Enable RTL8192SE ASPM & Enable Pci Bridge ASPM for
 * power saving We should follow the sequence to enable
 * RTL8192SE first then enable Pci Bridge ASPM
 * or the system will show bluescreen.
*/
void rtw_pci_enable_aspm(_adapter *padapter)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(padapter);
	struct pwrctrl_priv	*pwrpriv = dvobj_to_pwrctl(pdvobjpriv);
	struct pci_dev	*pdev = pdvobjpriv->ppcidev;
	struct pci_dev	*bridge_pdev = pdev->bus->self;
	struct pci_priv	*pcipriv = &(pdvobjpriv->pcipriv);
	u16	aspmlevel = 0;
	u8	u_pcibridge_aspmsetting = 0;
	u8	u_device_aspmsetting = 0;
	u32	u_device_aspmsupportsetting = 0;


	/* We shall check RF Off Level for ASPM function instead of registry settings, revised by Roger, 2013.03.29. */
	if (!(pwrpriv->reg_rfps_level & (RT_RF_LPS_LEVEL_ASPM | RT_RF_PS_LEVEL_ALWAYS_ASPM)))
		return;

	/* When there exists anyone's BusNum, DevNum, and FuncNum that are set to 0xff, */
	/* we do not execute any action and return. Added by tynli. */
	if ((pcipriv->busnumber == 0xff && pcipriv->devnumber == 0xff && pcipriv->funcnumber == 0xff) ||
	    (pcipriv->pcibridge_busnum == 0xff && pcipriv->pcibridge_devnum == 0xff && pcipriv->pcibridge_funcnum == 0xff)) {
		RTW_INFO("rtw_pci_enable_aspm(): Fail to enable ASPM. Cannot find the Bus of PCI(Bridge).\n");
		return;
	}

	/* Get Bridge ASPM Support
	 * not to enable bridge aspm if bridge does not support
	 * Added by sherry 20100803
	*/
	{
		/* Get the Link Capability, it ls located at offset 0x0c from the PCIE Capability */
		pci_read_config_dword(bridge_pdev, (pcipriv->pcibridge_pciehdr_offset + 0x0C), &u_device_aspmsupportsetting);

		RTW_INFO("rtw_pci_enable_aspm(): Bridge ASPM support %x\n", u_device_aspmsupportsetting);
		if (((u_device_aspmsupportsetting & BIT(11)) != BIT(11)) || ((u_device_aspmsupportsetting & BIT(10)) != BIT(10))) {
			if (pdvobjpriv->const_devicepci_aspm_setting == 3) {
				RTW_INFO("rtw_pci_enable_aspm(): Bridge not support L0S or L1\n");
				return;
			} else if (pdvobjpriv->const_devicepci_aspm_setting == 2) {
				if ((u_device_aspmsupportsetting & BIT(11)) != BIT(11)) {
					RTW_INFO("rtw_pci_enable_aspm(): Bridge not support L1\n");
					return;
				}
			} else if (pdvobjpriv->const_devicepci_aspm_setting == 1) {
				if ((u_device_aspmsupportsetting & BIT(10)) != BIT(10)) {
					RTW_INFO("rtw_pci_enable_aspm(): Bridge not support L0s\n");
					return;
				}

			}
		} else
			RTW_INFO("rtw_pci_enable_aspm(): Bridge support L0s and L1\n");
	}

	/*
	* Skip following settings if ASPM has already enabled, added by Roger, 2013.03.15.
	*/
	if ((pcipriv->pcibridge_linkctrlreg & (BIT0 | BIT1)) &&
	    (pcipriv->linkctrl_reg & (BIT0 | BIT1))) {
		/* BIT0: L0S, BIT1:L1 */

		RTW_INFO("PlatformEnableASPM(): ASPM is already enabled, skip incoming settings!!\n");
		return;
	}

	/* 4 Enable Pci Bridge ASPM */
	/* Write PCI bridge PCIE-capability Link Control Register */
	/* Justin: Can we change the ASPM Control register ? */
	/* The system BIOS should set this register with a correct value */
	/* If we change the force enable the ASPM L1/L0s, this may cause the system hang */
	u_pcibridge_aspmsetting = pcipriv->pcibridge_linkctrlreg;
	u_pcibridge_aspmsetting |= pdvobjpriv->const_hostpci_aspm_setting;

	if (pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_INTEL ||
	    pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_SIS)
		u_pcibridge_aspmsetting &= ~BIT(0); /* for intel host 42 device 43 */

	pci_write_config_byte(bridge_pdev, (pcipriv->pcibridge_pciehdr_offset + 0x10), u_pcibridge_aspmsetting);
	RTW_INFO("PlatformEnableASPM():PciBridge Write reg[%x] = %x\n",
		 (pcipriv->pcibridge_pciehdr_offset + 0x10),
		 u_pcibridge_aspmsetting);

	rtw_udelay_os(50);

	/*Get ASPM level (with/without Clock Req)*/
	aspmlevel |= pdvobjpriv->const_devicepci_aspm_setting;
	u_device_aspmsetting = pcipriv->linkctrl_reg;
	u_device_aspmsetting |= aspmlevel; /* device 43 */

	rtw_pci_platform_switch_device_pci_aspm(padapter, u_device_aspmsetting);

	if (pwrpriv->reg_rfps_level & RT_RF_OFF_LEVL_CLK_REQ) {
		rtw_pci_switch_clk_req(padapter, (pwrpriv->reg_rfps_level & RT_RF_OFF_LEVL_CLK_REQ) ? 1 : 0);
		RT_SET_PS_LEVEL(pwrpriv, RT_RF_OFF_LEVL_CLK_REQ);
	}

	rtw_udelay_os(50);
}

static u8 rtw_pci_get_amd_l1_patch(struct dvobj_priv *pdvobjpriv, struct pci_dev *pdev)
{
	u8	status = _FALSE;
	u8	offset_e0;
	u32	offset_e4;

	pci_write_config_byte(pdev, 0xE0, 0xA0);
	pci_read_config_byte(pdev, 0xE0, &offset_e0);

	if (offset_e0 == 0xA0) {
		pci_read_config_dword(pdev, 0xE4, &offset_e4);
		if (offset_e4 & BIT(23))
			status = _TRUE;
	}

	return status;
}

static s32	rtw_pci_get_linkcontrol_reg(struct pci_dev *pdev, u8 *LinkCtrlReg, u8 *HdrOffset)
{
	u8 CapabilityPointer;
	RT_PCI_CAPABILITIES_HEADER	CapabilityHdr;
	s32 status = _FAIL;

	/* get CapabilityOffset */
	pci_read_config_byte(pdev, 0x34, &CapabilityPointer);	/* the capability pointer is located offset 0x34 */

	/* Loop through the capabilities in search of the power management capability. */
	/* The list is NULL-terminated, so the last offset will always be zero. */

	while (CapabilityPointer != 0) {
		/* Read the header of the capability at  this offset. If the retrieved capability is not */
		/* the power management capability that we are looking for, follow the link to the  */
		/* next capability and continue looping. */

		/* 4 get CapabilityHdr */
		/* pci_read_config_word(pdev, CapabilityPointer, (u16 *)&CapabilityHdr); */
		pci_read_config_byte(pdev, CapabilityPointer, (u8 *)&CapabilityHdr.CapabilityID);
		pci_read_config_byte(pdev, CapabilityPointer + 1, (u8 *)&CapabilityHdr.Next);

		/* Found the PCI express capability */
		if (CapabilityHdr.CapabilityID == PCI_CAPABILITY_ID_PCI_EXPRESS)
			break;
		else {
			/* This is some other capability. Keep looking for the PCI express capability. */
			CapabilityPointer = CapabilityHdr.Next;
		}
	}

	/* Get the Link Control Register, it located at offset 0x10 from the Capability Header */
	if (CapabilityHdr.CapabilityID == PCI_CAPABILITY_ID_PCI_EXPRESS) {
		*HdrOffset = CapabilityPointer;
		pci_read_config_byte(pdev, CapabilityPointer + 0x10, LinkCtrlReg);

		status = _SUCCESS;
	} else {
		/* We didn't find a PCIe capability. */
		RTW_INFO("GetPciLinkCtrlReg(): Cannot Find PCIe Capability\n");
	}

	return status;
}

static s32	rtw_set_pci_cache_line_size(struct pci_dev *pdev, u8 CacheLineSizeToSet)
{
	u8	ucPciCacheLineSize;
	s32	Result;

	/* ucPciCacheLineSize  = pPciConfig->CacheLineSize; */
	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &ucPciCacheLineSize);

	if (ucPciCacheLineSize < 8 || ucPciCacheLineSize > 16) {
		RTW_INFO("Driver Sets default Cache Line Size...\n");

		ucPciCacheLineSize = CacheLineSizeToSet;

		Result = pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, ucPciCacheLineSize);

		if (Result != 0) {
			RTW_INFO("pci_write_config_byte (CacheLineSize) Result=%d\n", Result);
			goto _SET_CACHELINE_SIZE_FAIL;
		}

		Result = pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &ucPciCacheLineSize);
		if (Result != 0) {
			RTW_INFO("pci_read_config_byte (PciCacheLineSize) Result=%d\n", Result);
			goto _SET_CACHELINE_SIZE_FAIL;
		}

		if (ucPciCacheLineSize != CacheLineSizeToSet) {
			RTW_INFO("Failed to set Cache Line Size to 0x%x! ucPciCacheLineSize=%x\n", CacheLineSizeToSet, ucPciCacheLineSize);
			goto _SET_CACHELINE_SIZE_FAIL;
		}
	}

	return _SUCCESS;

_SET_CACHELINE_SIZE_FAIL:

	return _FAIL;
}


#define PCI_CMD_ENABLE_BUS_MASTER		BIT(2)
#define PCI_CMD_DISABLE_INTERRUPT		BIT(10)
#define CMD_BUS_MASTER				BIT(2)

static s32 rtw_pci_parse_configuration(struct pci_dev *pdev, struct dvobj_priv *pdvobjpriv)
{
	struct pci_priv	*pcipriv = &(pdvobjpriv->pcipriv);
	/* PPCI_COMMON_CONFIG pPciConfig = (PPCI_COMMON_CONFIG) pucBuffer; */
	/* u16	usPciCommand = pPciConfig->Command; */
	u16	usPciCommand = 0;
	int	Result, ret = _FAIL;
	u8	CapabilityOffset;
	RT_PCI_CAPABILITIES_HEADER CapabilityHdr;
	u8	PCIeCap;
	u8	LinkCtrlReg;
	u8	ClkReqReg;

	/* RTW_INFO("%s==>\n", __func__); */

	pci_read_config_word(pdev, PCI_COMMAND, &usPciCommand);

	do {
		/* 3 Enable bus matering if it isn't enabled by the BIOS */
		if (!(usPciCommand & PCI_CMD_ENABLE_BUS_MASTER)) {
			RTW_INFO("Bus master is not enabled by BIOS! usPciCommand=%x\n", usPciCommand);

			usPciCommand |= CMD_BUS_MASTER;

			Result = pci_write_config_word(pdev, PCI_COMMAND, usPciCommand);
			if (Result != 0) {
				RTW_INFO("pci_write_config_word (Command) Result=%d\n", Result);
				ret = _FAIL;
				break;
			}

			Result = pci_read_config_word(pdev, PCI_COMMAND, &usPciCommand);
			if (Result != 0) {
				RTW_INFO("pci_read_config_word (Command) Result=%d\n", Result);
				ret = _FAIL;
				break;
			}

			if (!(usPciCommand & PCI_CMD_ENABLE_BUS_MASTER)) {
				RTW_INFO("Failed to enable bus master! usPciCommand=%x\n", usPciCommand);
				ret = _FAIL;
				break;
			}
		}
		RTW_INFO("Bus master is enabled. usPciCommand=%x\n", usPciCommand);

		/* 3 Enable interrupt */
		if ((usPciCommand & PCI_CMD_DISABLE_INTERRUPT)) {
			RTW_INFO("INTDIS==1 usPciCommand=%x\n", usPciCommand);

			usPciCommand &= (~PCI_CMD_DISABLE_INTERRUPT);

			Result = pci_write_config_word(pdev, PCI_COMMAND, usPciCommand);
			if (Result != 0) {
				RTW_INFO("pci_write_config_word (Command) Result=%d\n", Result);
				ret = _FAIL;
				break;
			}

			Result = pci_read_config_word(pdev, PCI_COMMAND, &usPciCommand);
			if (Result != 0) {
				RTW_INFO("pci_read_config_word (Command) Result=%d\n", Result);
				ret = _FAIL;
				break;
			}

			if ((usPciCommand & PCI_CMD_DISABLE_INTERRUPT)) {
				RTW_INFO("Failed to set INTDIS to 0! usPciCommand=%x\n", usPciCommand);
				ret = _FAIL;
				break;
			}
		}

		/*  */
		/* Description: Find PCI express capability offset. Porting from 818xB by tynli 2008.12.19 */
		/*  */
		/* ------------------------------------------------------------- */

		/* 3 PCIeCap */
		/* The device supports capability lists. Find the capabilities. */

		/* CapabilityOffset = pPciConfig->u.type0.CapabilitiesPtr; */
		pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &CapabilityOffset);

		/* Loop through the capabilities in search of the power management capability. */
		/* The list is NULL-terminated, so the last offset will always be zero. */

		while (CapabilityOffset != 0) {
			/* Read the header of the capability at  this offset. If the retrieved capability is not */
			/* the power management capability that we are looking for, follow the link to the */
			/* next capability and continue looping. */

			/* Result = pci_read_config_word(pdev, CapabilityOffset, (u16 *)&CapabilityHdr); */
			Result = pci_read_config_byte(pdev, CapabilityOffset, (u8 *)&CapabilityHdr.CapabilityID);
			if (Result != 0)
				break;

			Result = pci_read_config_byte(pdev, CapabilityOffset + 1, (u8 *)&CapabilityHdr.Next);
			if (Result != 0)
				break;

			/* Found the PCI express capability */
			if (CapabilityHdr.CapabilityID == PCI_CAPABILITY_ID_PCI_EXPRESS)
				break;
			else {
				/* This is some other capability. Keep looking for the PCI express capability. */
				CapabilityOffset = CapabilityHdr.Next;
			}
		}

		if (Result != 0) {
			RTW_INFO("pci_read_config_word (RT_PCI_CAPABILITIES_HEADER) Result=%d\n", Result);
			break;
		}

		if (CapabilityHdr.CapabilityID == PCI_CAPABILITY_ID_PCI_EXPRESS) {
			pcipriv->pciehdr_offset = CapabilityOffset;
			RTW_INFO("PCIe Header Offset =%x\n", CapabilityOffset);

			/* Skip past the capabilities header and read the PCI express capability */
			/* Justin: The PCI-e capability size should be 2 bytes, why we just get 1 byte */
			/* Beside, this PCIeCap seems no one reference it in the driver code */
			Result = pci_read_config_byte(pdev, CapabilityOffset + 2, &PCIeCap);

			if (Result != 0) {
				RTW_INFO("pci_read_config_byte (PCIE Capability) Result=%d\n", Result);
				break;
			}

			pcipriv->pcie_cap = PCIeCap;
			RTW_INFO("PCIe Capability =%x\n", PCIeCap);

			/* 3 Link Control Register */
			/* Read "Link Control Register" Field (80h ~81h) */
			Result = pci_read_config_byte(pdev, CapabilityOffset + 0x10, &LinkCtrlReg);
			if (Result != 0) {
				RTW_INFO("pci_read_config_byte (Link Control Register) Result=%d\n", Result);
				break;
			}

			pcipriv->linkctrl_reg = LinkCtrlReg;
			RTW_INFO("Link Control Register =%x\n", LinkCtrlReg);

			/* 3 Get Capability of PCI Clock Request */
			/* The clock request setting is located at 0x81[0] */
			Result = pci_read_config_byte(pdev, CapabilityOffset + 0x11, &ClkReqReg);
			if (Result != 0) {
				pcipriv->pci_clk_req = _FALSE;
				RTW_INFO("pci_read_config_byte (Clock Request Register) Result=%d\n", Result);
				break;
			}
			if (ClkReqReg & BIT(0))
				pcipriv->pci_clk_req = _TRUE;
			else
				pcipriv->pci_clk_req = _FALSE;
			RTW_INFO("Clock Request =%x\n", pcipriv->pci_clk_req);
		} else {
			/* We didn't find a PCIe capability. */
			RTW_INFO("Didn't Find PCIe Capability\n");
			break;
		}

		/* 3 Fill Cacheline */
		ret = rtw_set_pci_cache_line_size(pdev, 8);
		if (ret != _SUCCESS) {
			RTW_INFO("rtw_set_pci_cache_line_size fail\n");
			break;
		}

		/* Include 92C suggested by SD1. Added by tynli. 2009.11.25.
		 * Enable the Backdoor
		 */
		{
			u8	tmp;

			Result = pci_read_config_byte(pdev, 0x98, &tmp);

			tmp |= BIT4;

			Result = pci_write_config_byte(pdev, 0x98, tmp);

		}
		ret = _SUCCESS;
	} while (_FALSE);

	return ret;
}

/*
 * Update PCI dependent default settings.
 *
 */
static void rtw_pci_update_default_setting(_adapter *padapter)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(padapter);
	struct pci_priv	*pcipriv = &(pdvobjpriv->pcipriv);
	struct pwrctrl_priv	*pwrpriv = dvobj_to_pwrctl(pdvobjpriv);
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);

	/* reset pPSC->reg_rfps_level & priv->b_support_aspm */
	pwrpriv->reg_rfps_level = 0;

	/* Update PCI ASPM setting */
	/* pwrpriv->const_amdpci_aspm = pdvobjpriv->const_amdpci_aspm; */
	switch (pdvobjpriv->const_pci_aspm) {
	case 0:		/* No ASPM */
		break;

	case 1:		/* ASPM dynamically enabled/disable. */
		pwrpriv->reg_rfps_level |= RT_RF_LPS_LEVEL_ASPM;
		break;

	case 2:		/* ASPM with Clock Req dynamically enabled/disable. */
		pwrpriv->reg_rfps_level |= (RT_RF_LPS_LEVEL_ASPM | RT_RF_OFF_LEVL_CLK_REQ);
		break;

	case 3:		/* Always enable ASPM and Clock Req from initialization to halt. */
		pwrpriv->reg_rfps_level &= ~(RT_RF_LPS_LEVEL_ASPM);
		pwrpriv->reg_rfps_level |= (RT_RF_PS_LEVEL_ALWAYS_ASPM | RT_RF_OFF_LEVL_CLK_REQ);
		break;

	case 4:		/* Always enable ASPM without Clock Req from initialization to halt. */
		pwrpriv->reg_rfps_level &= ~(RT_RF_LPS_LEVEL_ASPM | RT_RF_OFF_LEVL_CLK_REQ);
		pwrpriv->reg_rfps_level |= RT_RF_PS_LEVEL_ALWAYS_ASPM;
		break;

	case 5: /* Linux do not support ASPM OSC, added by Roger, 2013.03.27.	 */
		break;
	}

	pwrpriv->reg_rfps_level |= RT_RF_OFF_LEVL_HALT_NIC;

	/* Update Radio OFF setting */
	switch (pdvobjpriv->const_hwsw_rfoff_d3) {
	case 1:
		if (pwrpriv->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM)
			pwrpriv->reg_rfps_level |= RT_RF_OFF_LEVL_ASPM;
		break;

	case 2:
		if (pwrpriv->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM)
			pwrpriv->reg_rfps_level |= RT_RF_OFF_LEVL_ASPM;
		pwrpriv->reg_rfps_level |= RT_RF_OFF_LEVL_HALT_NIC;
		break;

	case 3:
		pwrpriv->reg_rfps_level |= RT_RF_OFF_LEVL_PCI_D3;
		break;
	}

	/* Update Rx 2R setting */
	/* pPSC->reg_rfps_level |= ((pDevice->RegLPS2RDisable) ? RT_RF_LPS_DISALBE_2R : 0); */

	/*  */
	/* Set HW definition to determine if it supports ASPM. */
	/*  */
	switch (pdvobjpriv->const_support_pciaspm) {
	case 1: {	/* Support ASPM. */
		u8	b_support_backdoor = _TRUE;
		u8	b_support_l1_on_amd = _FALSE;

		rtw_hal_get_def_var(padapter, HAL_DEF_PCI_AMD_L1_SUPPORT, &b_support_l1_on_amd);

		if (pHalData->CustomerID == RT_CID_TOSHIBA &&
		    pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_AMD &&
		    !pcipriv->amd_l1_patch && !b_support_l1_on_amd) {
			RTW_INFO("%s(): Disable L1 Backdoor!!\n", __func__);
			b_support_backdoor = _FALSE;
		}
		rtw_hal_set_def_var(padapter, HAL_DEF_PCI_SUUPORT_L1_BACKDOOR, &b_support_backdoor);
	}
	break;

	default:
		/* Do nothing. Set when finding the chipset. */
		break;
	}
}

static void rtw_pci_initialize_adapter_common(_adapter *padapter)
{
	struct pwrctrl_priv	*pwrpriv = adapter_to_pwrctl(padapter);

	rtw_pci_update_default_setting(padapter);

	if (pwrpriv->reg_rfps_level & RT_RF_PS_LEVEL_ALWAYS_ASPM) {
		/* Always enable ASPM & Clock Req. */
		rtw_pci_enable_aspm(padapter);
		RT_SET_PS_LEVEL(pwrpriv, RT_RF_PS_LEVEL_ALWAYS_ASPM);
	}

}

/*
 * 2009/10/28 MH Enable rtl8192ce DMA64 function. We need to enable 0x719 BIT5
 *   */
#ifdef CONFIG_64BIT_DMA
u8 PlatformEnableDMA64(PADAPTER Adapter)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(Adapter);
	struct pci_dev	*pdev = pdvobjpriv->ppcidev;
	u8	bResult = _TRUE;
	u8	value;

	pci_read_config_byte(pdev, 0x719, &value);

	/* 0x719 Bit5 is DMA64 bit fetch. */
	value |= (BIT5);

	pci_write_config_byte(pdev, 0x719, value);

	return bResult;
}
#endif

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) || (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18))
	#define rtw_pci_interrupt(x, y, z) rtw_pci_interrupt(x, y)
#endif

static irqreturn_t rtw_pci_interrupt(int irq, void *priv, struct pt_regs *regs)
{
	struct dvobj_priv *dvobj = (struct dvobj_priv *)priv;
	_adapter *adapter = dvobj_get_primary_adapter(dvobj);

	if (dvobj->irq_enabled == 0)
		return IRQ_HANDLED;

	if (rtw_hal_interrupt_handler(adapter) == _FAIL)
		return IRQ_HANDLED;
	/* return IRQ_NONE; */

	return IRQ_HANDLED;
}

#if defined(RTK_DMP_PLATFORM) || defined(CONFIG_PLATFORM_RTL8197D)
	#define pci_iounmap(x, y) iounmap(y)
#endif

int pci_alloc_irq(struct dvobj_priv *dvobj)
{
	int err;
	struct pci_dev *pdev = dvobj->ppcidev;
	int ret;

#ifndef CONFIG_RTW_PCI_MSI_DISABLE
	ret = pci_enable_msi(pdev);

	RTW_INFO("pci_enable_msi ret=%d\n", ret);
#endif

#if defined(IRQF_SHARED)
	err = request_irq(pdev->irq, &rtw_pci_interrupt, IRQF_SHARED, DRV_NAME, dvobj);
#else
	err = request_irq(pdev->irq, &rtw_pci_interrupt, SA_SHIRQ, DRV_NAME, dvobj);
#endif
	if (err)
		RTW_INFO("Error allocating IRQ %d", pdev->irq);
	else {
		dvobj->irq_alloc = 1;
		dvobj->irq = pdev->irq;
		RTW_INFO("Request_irq OK, IRQ %d\n", pdev->irq);
	}

	return err ? _FAIL : _SUCCESS;
}

static void rtw_decide_chip_type_by_pci_driver_data(struct dvobj_priv *pdvobj, const struct pci_device_id *pdid)
{
	pdvobj->chip_type = pdid->driver_data;

#ifdef CONFIG_RTL8188E
	if (pdvobj->chip_type == RTL8188E) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8188EE;
		RTW_INFO("CHIP TYPE: RTL8188E\n");
	}
#endif

#ifdef CONFIG_RTL8812A
	if (pdvobj->chip_type == RTL8812) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8812E;
		RTW_INFO("CHIP TYPE: RTL8812AE\n");
	}
#endif

#ifdef CONFIG_RTL8821A
	if (pdvobj->chip_type == RTL8821) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8821E;
		RTW_INFO("CHIP TYPE: RTL8821AE\n");
	}
#endif

#ifdef CONFIG_RTL8723B
	if (pdvobj->chip_type == RTL8723B) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8723BE;
		RTW_INFO("CHIP TYPE: RTL8723BE\n");
	}
#endif
#ifdef CONFIG_RTL8723D
	if (pdvobj->chip_type == RTL8723D) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8723DE;
		RTW_INFO("CHIP TYPE: RTL8723DE\n");
	}
#endif
#ifdef CONFIG_RTL8192E
	if (pdvobj->chip_type == RTL8192E) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8192EE;
		RTW_INFO("CHIP TYPE: RTL8192EE\n");
	}
#endif

#ifdef CONFIG_RTL8192F
	if (pdvobj->chip_type == RTL8192F) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8192FE;
		RTW_INFO("CHIP TYPE: RTL8192FE\n");
	}
#endif
#ifdef CONFIG_RTL8814A
	if (pdvobj->chip_type == RTL8814A) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8814AE;
		RTW_INFO("CHIP TYPE: RTL8814AE\n");
	}
#endif

#if defined(CONFIG_RTL8822B)
	if (pdvobj->chip_type == RTL8822B) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8822BE;
		RTW_INFO("CHIP TYPE: RTL8822BE\n");
	}
#endif

#if defined(CONFIG_RTL8821C)
	if (pdvobj->chip_type == RTL8821C) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8821CE;
		RTW_INFO("CHIP TYPE: RTL8821CE\n");
	}
#endif

#if defined(CONFIG_RTL8822C)
	if (pdvobj->chip_type == RTL8822C) {
		pdvobj->HardwareType = HARDWARE_TYPE_RTL8822CE;
		RTW_INFO("CHIP TYPE: RTL8822CE\n");
	}
#endif

}

static struct dvobj_priv	*pci_dvobj_init(struct pci_dev *pdev, const struct pci_device_id *pdid)
{
	int err;
	u32	status = _FAIL;
	struct dvobj_priv	*dvobj = NULL;
	struct pci_priv	*pcipriv = NULL;
	struct pci_dev	*bridge_pdev = pdev->bus->self;
	/* u32	pci_cfg_space[16]; */
	unsigned long pmem_start, pmem_len, pmem_flags;
	u8	tmp;
	u8	PciBgVIdIdx;
	int	i;


	dvobj = devobj_init();
	if (dvobj == NULL)
		goto exit;


	dvobj->ppcidev = pdev;
	pcipriv = &(dvobj->pcipriv);
	pci_set_drvdata(pdev, dvobj);


	err = pci_enable_device(pdev);
	if (err != 0) {
		RTW_ERR("%s : Cannot enable new PCI device\n", pci_name(pdev));
		goto free_dvobj;
	}

#ifdef CONFIG_64BIT_DMA
	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
		RTW_INFO("RTL819xCE: Using 64bit DMA\n");
		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
		if (err != 0) {
			RTW_ERR("Unable to obtain 64bit DMA for consistent allocations\n");
			goto disable_picdev;
		}
		dvobj->bdma64 = _TRUE;
	} else
#endif
	{
		if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
			err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
			if (err != 0) {
				RTW_ERR("Unable to obtain 32bit DMA for consistent allocations\n");
				goto disable_picdev;
			}
		}
	}

	pci_set_master(pdev);

	err = pci_request_regions(pdev, DRV_NAME);
	if (err != 0) {
		RTW_ERR("Can't obtain PCI resources\n");
		goto disable_picdev;
	}

#ifdef RTK_129X_PLATFORM
	if (pdev->bus->number == 0x00) {
		pmem_start = PCIE_SLOT1_MEM_START;
		pmem_len   = PCIE_SLOT1_MEM_LEN;
		pmem_flags = 0;
		RTW_PRINT("RTD129X: PCIE SLOT1\n");
	} else if (pdev->bus->number == 0x01) {
		pmem_start = PCIE_SLOT2_MEM_START;
		pmem_len   = PCIE_SLOT2_MEM_LEN;
		pmem_flags = 0;
		RTW_PRINT("RTD129X: PCIE SLOT2\n");
	} else {
		RTW_ERR(KERN_ERR "RTD129X: Wrong Slot Num\n");
		goto release_regions;
	}
#else
	/* Search for memory map resource (index 0~5) */
	for (i = 0 ; i < 6 ; i++) {
		pmem_start = pci_resource_start(pdev, i);
		pmem_len = pci_resource_len(pdev, i);
		pmem_flags = pci_resource_flags(pdev, i);

		if (pmem_flags & IORESOURCE_MEM)
			break;
	}

	if (i == 6) {
		RTW_ERR("%s: No MMIO resource found, abort!\n", __func__);
		goto release_regions;
	}
#endif /* RTK_DMP_PLATFORM */

#ifdef RTK_DMP_PLATFORM
	dvobj->pci_mem_start = (unsigned long)ioremap_nocache(pmem_start, pmem_len);
#elif defined(RTK_129X_PLATFORM)
	if (pdev->bus->number == 0x00)
		dvobj->ctrl_start =
			(unsigned long)ioremap(PCIE_SLOT1_CTRL_START, 0x200);
	else if (pdev->bus->number == 0x01)
		dvobj->ctrl_start =
			(unsigned long)ioremap(PCIE_SLOT2_CTRL_START, 0x200);

	if (dvobj->ctrl_start == 0) {
		RTW_ERR("RTD129X: Can't map CTRL mem\n");
		goto release_regions;
	}

	dvobj->mask_addr = dvobj->ctrl_start + PCIE_MASK_OFFSET;
	dvobj->tran_addr = dvobj->ctrl_start + PCIE_TRANSLATE_OFFSET;

	dvobj->pci_mem_start =
		(unsigned long)ioremap_nocache(pmem_start, pmem_len);
#else
	/* shared mem start */
	dvobj->pci_mem_start = (unsigned long)pci_iomap(pdev, i, pmem_len);
#endif
	if (dvobj->pci_mem_start == 0) {
		RTW_ERR("Can't map PCI mem\n");
		goto release_regions;
	}

	RTW_INFO("Memory mapped space start: 0x%08lx len:%08lx flags:%08lx, after map:0x%08lx\n",
		 pmem_start, pmem_len, pmem_flags, dvobj->pci_mem_start);

	/*find bus info*/
	pcipriv->busnumber = pdev->bus->number;
	pcipriv->devnumber = PCI_SLOT(pdev->devfn);
	pcipriv->funcnumber = PCI_FUNC(pdev->devfn);

	/*find bridge info*/
	if (bridge_pdev) {
		pcipriv->pcibridge_busnum = bridge_pdev->bus->number;
		pcipriv->pcibridge_devnum = PCI_SLOT(bridge_pdev->devfn);
		pcipriv->pcibridge_funcnum = PCI_FUNC(bridge_pdev->devfn);
		pcipriv->pcibridge_vendor = PCI_BRIDGE_VENDOR_UNKNOWN;
		pcipriv->pcibridge_vendorid = bridge_pdev->vendor;
		pcipriv->pcibridge_deviceid = bridge_pdev->device;
	}

#if 0
	/* Read PCI configuration Space Header */
	for (i = 0; i < 16; i++)
		pci_read_config_dword(pdev, (i << 2), &pci_cfg_space[i]);
#endif

	/*step 1-1., decide the chip_type via device info*/
	dvobj->interface_type = RTW_PCIE;
	rtw_decide_chip_type_by_pci_driver_data(dvobj, pdid);


	/* rtw_pci_parse_configuration(pdev, dvobj, (u8 *)&pci_cfg_space); */
	rtw_pci_parse_configuration(pdev, dvobj);

	for (PciBgVIdIdx = 0; PciBgVIdIdx < PCI_BRIDGE_VENDOR_MAX; PciBgVIdIdx++) {
		if (pcipriv->pcibridge_vendorid == pcibridge_vendors[PciBgVIdIdx]) {
			pcipriv->pcibridge_vendor = PciBgVIdIdx;
			RTW_INFO("Pci Bridge Vendor is found: VID=0x%x, VendorIdx=%d\n", pcipriv->pcibridge_vendorid, PciBgVIdIdx);
			break;
		}
	}

	if (pcipriv->pcibridge_vendor != PCI_BRIDGE_VENDOR_UNKNOWN) {
		rtw_pci_get_linkcontrol_reg(bridge_pdev, &pcipriv->pcibridge_linkctrlreg, &pcipriv->pcibridge_pciehdr_offset);

		if (pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_AMD)
			pcipriv->amd_l1_patch = rtw_pci_get_amd_l1_patch(dvobj, bridge_pdev);
	}

	status = _SUCCESS;

	if (status != _SUCCESS && dvobj->pci_mem_start != 0) {
#if 1/* def RTK_DMP_PLATFORM */
		pci_iounmap(pdev, (void *)dvobj->pci_mem_start);
#endif
		dvobj->pci_mem_start = 0;
	}

#ifdef RTK_129X_PLATFORM
	if (status != _SUCCESS && dvobj->ctrl_start != 0) {
		pci_iounmap(pdev, (void *)dvobj->ctrl_start);
		dvobj->ctrl_start = 0;
	}
#endif

release_regions:
	if (status != _SUCCESS)
		pci_release_regions(pdev);
disable_picdev:
	if (status != _SUCCESS)
		pci_disable_device(pdev);
free_dvobj:
	if (status != _SUCCESS && dvobj) {
		pci_set_drvdata(pdev, NULL);
		devobj_deinit(dvobj);
		dvobj = NULL;
	}
exit:
	return dvobj;
}


static void pci_dvobj_deinit(struct pci_dev *pdev)
{
	struct dvobj_priv *dvobj = pci_get_drvdata(pdev);

	pci_set_drvdata(pdev, NULL);
	if (dvobj) {
		if (dvobj->irq_alloc) {
			free_irq(pdev->irq, dvobj);
#ifndef CONFIG_RTW_PCI_MSI_DISABLE
			pci_disable_msi(pdev);
#endif
			dvobj->irq_alloc = 0;
		}

		if (dvobj->pci_mem_start != 0) {
#if 1/* def RTK_DMP_PLATFORM */
			pci_iounmap(pdev, (void *)dvobj->pci_mem_start);
#endif
			dvobj->pci_mem_start = 0;
		}

#ifdef RTK_129X_PLATFORM
		if (dvobj->ctrl_start != 0) {
			pci_iounmap(pdev, (void *)dvobj->ctrl_start);
			dvobj->ctrl_start = 0;
		}
#endif
		devobj_deinit(dvobj);
	}

	pci_release_regions(pdev);
	pci_disable_device(pdev);

}


u8 rtw_set_hal_ops(_adapter *padapter)
{
	/* alloc memory for HAL DATA */
	if (rtw_hal_data_init(padapter) == _FAIL)
		return _FAIL;

#ifdef CONFIG_RTL8188E
	if (rtw_get_chip_type(padapter) == RTL8188E)
		rtl8188ee_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8812A) || defined(CONFIG_RTL8821A)
	if ((rtw_get_chip_type(padapter) == RTL8812) || (rtw_get_chip_type(padapter) == RTL8821))
		rtl8812ae_set_hal_ops(padapter);
#endif

#ifdef CONFIG_RTL8723B
	if (rtw_get_chip_type(padapter) == RTL8723B)
		rtl8723be_set_hal_ops(padapter);
#endif

#ifdef CONFIG_RTL8723D
	if (rtw_get_chip_type(padapter) == RTL8723D)
		rtl8723de_set_hal_ops(padapter);
#endif

#ifdef CONFIG_RTL8192E
	if (rtw_get_chip_type(padapter) == RTL8192E)
		rtl8192ee_set_hal_ops(padapter);
#endif

#ifdef CONFIG_RTL8192F
	if (rtw_get_chip_type(padapter) == RTL8192F)
		rtl8192fe_set_hal_ops(padapter);
#endif

#ifdef CONFIG_RTL8814A
	if (rtw_get_chip_type(padapter) == RTL8814A)
		rtl8814ae_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8822B)
	if (rtw_get_chip_type(padapter) == RTL8822B)
		rtl8822be_set_hal_ops(padapter);
#endif
#if defined(CONFIG_RTL8821C)
	if (rtw_get_chip_type(padapter) == RTL8821C)
		rtl8821ce_set_hal_ops(padapter);
#endif

#if defined(CONFIG_RTL8822C)
	if (rtw_get_chip_type(padapter) == RTL8822C)
		rtl8822ce_set_hal_ops(padapter);
#endif

	if (rtw_hal_ops_check(padapter) == _FAIL)
		return _FAIL;

	if (hal_spec_init(padapter) == _FAIL)
		return _FAIL;

	return _SUCCESS;
}

void pci_set_intf_ops(_adapter *padapter, struct _io_ops *pops)
{
#ifdef CONFIG_RTL8188E
	if (rtw_get_chip_type(padapter) == RTL8188E)
		rtl8188ee_set_intf_ops(pops);
#endif

#if defined(CONFIG_RTL8812A) || defined(CONFIG_RTL8821A)
	if ((rtw_get_chip_type(padapter) == RTL8812) || (rtw_get_chip_type(padapter) == RTL8821))
		rtl8812ae_set_intf_ops(pops);
#endif

#ifdef CONFIG_RTL8723B
	if (rtw_get_chip_type(padapter) == RTL8723B)
		rtl8723be_set_intf_ops(pops);
#endif

#ifdef CONFIG_RTL8723D
	if (rtw_get_chip_type(padapter) == RTL8723D)
		rtl8723de_set_intf_ops(pops);
#endif

#ifdef CONFIG_RTL8192E
	if (rtw_get_chip_type(padapter) == RTL8192E)
		rtl8192ee_set_intf_ops(pops);
#endif

#ifdef CONFIG_RTL8192F
	if (rtw_get_chip_type(padapter) == RTL8192F)
		rtl8192fe_set_intf_ops(pops);
#endif

#ifdef CONFIG_RTL8814A
	if (rtw_get_chip_type(padapter) == RTL8814A)
		rtl8814ae_set_intf_ops(pops);
#endif

#if defined(CONFIG_RTL8822B)
	if (rtw_get_chip_type(padapter) == RTL8822B)
		rtl8822be_set_intf_ops(pops);
#endif

#if defined(CONFIG_RTL8821C)
	if (rtw_get_chip_type(padapter) == RTL8821C)
		rtl8821ce_set_intf_ops(pops);
#endif

#if defined(CONFIG_RTL8822C)
	if (rtw_get_chip_type(padapter) == RTL8822C)
		rtl8822ce_set_intf_ops(pops);
#endif

}

static void pci_intf_start(_adapter *padapter)
{
	u8 en_sw_bcn = _TRUE;

	RTW_INFO("+pci_intf_start\n");

	/* Enable hw interrupt */
	rtw_hal_enable_interrupt(padapter);
	rtw_hal_set_hwreg(padapter, HW_VAR_ENSWBCN, &en_sw_bcn);

#ifdef CONFIG_PCI_TX_POLLING
	rtw_tx_poll_init(padapter);
#endif

	RTW_INFO("-pci_intf_start\n");
}
static void rtw_mi_pci_tasklets_kill(_adapter *padapter)
{
	int i;
	_adapter *iface;
	struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);

	for (i = 0; i < dvobj->iface_nums; i++) {
		iface = dvobj->padapters[i];
		if ((iface) && rtw_is_adapter_up(iface)) {
			tasklet_kill(&(padapter->recvpriv.recv_tasklet));
			tasklet_kill(&(padapter->recvpriv.irq_prepare_beacon_tasklet));
			tasklet_kill(&(padapter->xmitpriv.xmit_tasklet));
		}
	}
}

static void pci_intf_stop(_adapter *padapter)
{


	/* Disable hw interrupt */
	if (!rtw_is_surprise_removed(padapter)) {
		/* device still exists, so driver can do i/o operation */
		rtw_hal_disable_interrupt(padapter);
		rtw_mi_pci_tasklets_kill(padapter);

		rtw_hal_set_hwreg(padapter, HW_VAR_PCIE_STOP_TX_DMA, 0);

		rtw_hal_irp_reset(padapter);

	} else {
		/* Clear irq_enabled to prevent handle interrupt function. */
		adapter_to_dvobj(padapter)->irq_enabled = 0;
	}

#ifdef CONFIG_PCI_TX_POLLING
	rtw_tx_poll_timer_cancel(padapter);
#endif

}

static void disable_ht_for_spec_devid(const struct pci_device_id *pdid)
{
#ifdef CONFIG_80211N_HT
	u16 vid, pid;
	u32 flags;
	int i;
	int num = sizeof(specific_device_id_tbl) / sizeof(struct specific_device_id);

	for (i = 0; i < num; i++) {
		vid = specific_device_id_tbl[i].idVendor;
		pid = specific_device_id_tbl[i].idProduct;
		flags = specific_device_id_tbl[i].flags;

		if ((pdid->vendor == vid) && (pdid->device == pid) && (flags & SPEC_DEV_ID_DISABLE_HT)) {
			rtw_ht_enable = 0;
			rtw_bw_mode = 0;
			rtw_ampdu_enable = 0;
		}

	}
#endif
}

#ifdef CONFIG_PM
static int rtw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
	int ret = 0;
	struct dvobj_priv *dvobj = pci_get_drvdata(pdev);
	_adapter *padapter = dvobj_get_primary_adapter(dvobj);

	ret = rtw_suspend_common(padapter);
	ret = pci_save_state(pdev);
	if (ret != 0) {
		RTW_INFO("%s Failed on pci_save_state (%d)\n", __func__, ret);
		goto exit;
	}

#ifdef CONFIG_WOWLAN
	device_set_wakeup_enable(&pdev->dev, true);
#endif
	pci_disable_device(pdev);

#ifdef CONFIG_WOWLAN
	ret = pci_enable_wake(pdev, pci_choose_state(pdev, state), true);
	if (ret != 0)
		RTW_INFO("%s Failed on pci_enable_wake (%d)\n", __func__, ret);
#endif
	ret = pci_set_power_state(pdev, pci_choose_state(pdev, state));
	if (ret != 0)
		RTW_INFO("%s Failed on pci_set_power_state (%d)\n", __func__, ret);

exit:
	return ret;

}

int rtw_resume_process(_adapter *padapter)
{
	return rtw_resume_common(padapter);
}

static int rtw_pci_resume(struct pci_dev *pdev)
{
	struct dvobj_priv *dvobj = pci_get_drvdata(pdev);
	_adapter *padapter = dvobj_get_primary_adapter(dvobj);
	struct net_device *pnetdev = padapter->pnetdev;
	struct pwrctrl_priv *pwrpriv = dvobj_to_pwrctl(dvobj);
	int	err = 0;

	err = pci_set_power_state(pdev, PCI_D0);
	if (err != 0) {
		RTW_INFO("%s Failed on pci_set_power_state (%d)\n", __func__, err);
		goto exit;
	}

	err = pci_enable_device(pdev);
	if (err != 0) {
		RTW_INFO("%s Failed on pci_enable_device (%d)\n", __func__, err);
		goto exit;
	}


#ifdef CONFIG_WOWLAN
	err =  pci_enable_wake(pdev, PCI_D0, 0);
	if (err != 0) {
		RTW_INFO("%s Failed on pci_enable_wake (%d)\n", __func__, err);
		goto exit;
	}
#endif
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 37))
	pci_restore_state(pdev);
#else
	err = pci_restore_state(pdev);
	if (err != 0) {
		RTW_INFO("%s Failed on pci_restore_state (%d)\n", __func__, err);
		goto exit;
	}
#endif

#ifdef CONFIG_WOWLAN
	device_set_wakeup_enable(&pdev->dev, false);
#endif

	if (pwrpriv->wowlan_mode || pwrpriv->wowlan_ap_mode) {
		rtw_resume_lock_suspend();
		err = rtw_resume_process(padapter);
		rtw_resume_unlock_suspend();
	} else {
#ifdef CONFIG_RESUME_IN_WORKQUEUE
		rtw_resume_in_workqueue(pwrpriv);
#else
		if (rtw_is_earlysuspend_registered(pwrpriv)) {
			/* jeff: bypass resume here, do in late_resume */
			rtw_set_do_late_resume(pwrpriv, _TRUE);
		} else {
			rtw_resume_lock_suspend();
			err = rtw_resume_process(padapter);
			rtw_resume_unlock_suspend();
		}
#endif
	}

exit:

	return err;
}
#endif/* CONFIG_PM */

_adapter *rtw_pci_primary_adapter_init(struct dvobj_priv *dvobj, struct pci_dev *pdev)
{
	_adapter *padapter = NULL;
	int status = _FAIL;

	padapter = (_adapter *)rtw_zvmalloc(sizeof(*padapter));
	if (padapter == NULL)
		goto exit;

	if (loadparam(padapter) != _SUCCESS)
		goto free_adapter;

	padapter->dvobj = dvobj;

	rtw_set_drv_stopped(padapter);/*init*/

	dvobj->padapters[dvobj->iface_nums++] = padapter;
	padapter->iface_id = IFACE_ID0;

	/* set adapter_type/iface type for primary padapter */
	padapter->isprimary = _TRUE;
	padapter->adapter_type = PRIMARY_ADAPTER;
#ifdef CONFIG_MI_WITH_MBSSID_CAM
	padapter->hw_port = HW_PORT0;
#else
	padapter->hw_port = HW_PORT0;
#endif

	if (rtw_init_io_priv(padapter, pci_set_intf_ops) == _FAIL)
		goto free_adapter;

	/* step 2.	hook HalFunc, allocate HalData */
	/* hal_set_hal_ops(padapter); */
	if (rtw_set_hal_ops(padapter) == _FAIL)
		goto free_hal_data;

	/* step 3. */
	padapter->intf_start = &pci_intf_start;
	padapter->intf_stop = &pci_intf_stop;

	/* .3 */
	rtw_hal_read_chip_version(padapter);

	/* .4 */
	rtw_hal_chip_configure(padapter);

#ifdef CONFIG_BT_COEXIST
	rtw_btcoex_Initialize(padapter);
#endif
	rtw_btcoex_wifionly_initialize(padapter);

	/* step 4. read efuse/eeprom data and get mac_addr */
	if (rtw_hal_read_chip_info(padapter) == _FAIL)
		goto free_hal_data;

	/* step 5. */
	if (rtw_init_drv_sw(padapter) == _FAIL)
		goto free_hal_data;

	if (rtw_hal_inirp_init(padapter) == _FAIL)
		goto free_hal_data;

	rtw_macaddr_cfg(adapter_mac_addr(padapter),  get_hal_mac_addr(padapter));

#ifdef CONFIG_MI_WITH_MBSSID_CAM
	rtw_mbid_camid_alloc(padapter, adapter_mac_addr(padapter));
#endif
#ifdef CONFIG_P2P
	rtw_init_wifidirect_addrs(padapter, adapter_mac_addr(padapter), adapter_mac_addr(padapter));
#endif /* CONFIG_P2P */

	rtw_hal_disable_interrupt(padapter);

	/* step 6. Init pci related configuration */
	rtw_pci_initialize_adapter_common(padapter);

	RTW_INFO("bDriverStopped:%s, bSurpriseRemoved:%s, bup:%d, hw_init_completed:%s\n"
		 , rtw_is_drv_stopped(padapter) ? "True" : "False"
		 , rtw_is_surprise_removed(padapter) ? "True" : "False"
		 , padapter->bup
		 , rtw_is_hw_init_completed(padapter) ? "True" : "False"
		);

	status = _SUCCESS;

free_hal_data:
	if (status != _SUCCESS && padapter->HalData)
		rtw_hal_free_data(padapter);

free_adapter:
	if (status != _SUCCESS && padapter) {
		#ifdef RTW_HALMAC
		rtw_halmac_deinit_adapter(dvobj);
		#endif
		rtw_vmfree((u8 *)padapter, sizeof(*padapter));
		padapter = NULL;
	}
exit:
	return padapter;
}

static void rtw_pci_primary_adapter_deinit(_adapter *padapter)
{
	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;

	/*	padapter->intf_stop(padapter); */

	if (check_fwstate(pmlmepriv, _FW_LINKED))
		rtw_disassoc_cmd(padapter, 0, RTW_CMDF_DIRECTLY);

#ifdef CONFIG_AP_MODE
	if (MLME_IS_AP(padapter) || MLME_IS_MESH(padapter)) {
		free_mlme_ap_info(padapter);
#ifdef CONFIG_HOSTAPD_MLME
		hostapd_mode_unload(padapter);
#endif
	}
#endif

	/*rtw_cancel_all_timer(padapte);*/
#ifdef CONFIG_WOWLAN
	adapter_to_pwrctl(padapter)->wowlan_mode = _FALSE;
#endif /* CONFIG_WOWLAN */
	rtw_dev_unload(padapter);

	RTW_INFO("%s, hw_init_completed=%s\n", __func__, rtw_is_hw_init_completed(padapter) ? "_TRUE" : "_FALSE");

	rtw_hal_inirp_deinit(padapter);
	rtw_free_drv_sw(padapter);

	/* TODO: use rtw_os_ndevs_deinit instead at the first stage of driver's dev deinit function */
	rtw_os_ndev_free(padapter);

#ifdef RTW_HALMAC
	rtw_halmac_deinit_adapter(adapter_to_dvobj(padapter));
#endif /* RTW_HALMAC */

	rtw_vmfree((u8 *)padapter, sizeof(_adapter));

#ifdef CONFIG_PLATFORM_RTD2880B
	RTW_INFO("wlan link down\n");
	rtd2885_wlan_netlink_sendMsg("linkdown", "8712");
#endif
}

/*
 * drv_init() - a device potentially for us
 *
 * notes: drv_init() is called when the bus driver has located a card for us to support.
 *        We accept the new device by returning 0.
*/
static int rtw_drv_init(struct pci_dev *pdev, const struct pci_device_id *pdid)
{
	int i, err = -ENODEV;

	int status = _FAIL;
	_adapter *padapter = NULL;
	struct dvobj_priv *dvobj;
	struct net_device *pnetdev;

	/* RTW_INFO("+rtw_drv_init\n"); */

	/* step 0. */
	disable_ht_for_spec_devid(pdid);

	/* Initialize dvobj_priv */
	dvobj = pci_dvobj_init(pdev, pdid);
	if (dvobj == NULL)
		goto exit;

	/* Initialize primary adapter */
	padapter = rtw_pci_primary_adapter_init(dvobj, pdev);
	if (padapter == NULL) {
		RTW_INFO("rtw_pci_primary_adapter_init Failed!\n");
		goto free_dvobj;
	}
	if (padapter->power_init_fail) {
		printk("power init fail\n");
		goto free_if_vir;
	}
	/* Initialize virtual interface */
#ifdef CONFIG_CONCURRENT_MODE
	if (padapter->registrypriv.virtual_iface_num > (CONFIG_IFACE_NUMBER - 1))
		padapter->registrypriv.virtual_iface_num = (CONFIG_IFACE_NUMBER - 1);

	for (i = 0; i < padapter->registrypriv.virtual_iface_num; i++) {
		if (rtw_drv_add_vir_if(padapter, pci_set_intf_ops) == NULL) {
			RTW_INFO("rtw_drv_add_iface failed! (%d)\n", i);
			goto free_if_vir;
		}
	}
#endif

#ifdef CONFIG_GLOBAL_UI_PID
	if (ui_pid[1] != 0) {
		RTW_INFO("ui_pid[1]:%d\n", ui_pid[1]);
		rtw_signal_process(ui_pid[1], SIGUSR2);
	}
#endif

	/* dev_alloc_name && register_netdev */
	if (rtw_os_ndevs_init(dvobj) != _SUCCESS)
		goto free_if_vir;

#ifdef CONFIG_HOSTAPD_MLME
	hostapd_mode_init(padapter);
#endif

#ifdef CONFIG_PLATFORM_RTD2880B
	RTW_INFO("wlan link up\n");
	rtd2885_wlan_netlink_sendMsg("linkup", "8712");
#endif

	/* alloc irq */
	if (pci_alloc_irq(dvobj) != _SUCCESS)
		goto os_ndevs_deinit;

	/* RTW_INFO("-871x_drv - drv_init, success!\n"); */

	status = _SUCCESS;

os_ndevs_deinit:
	if (status != _SUCCESS)
		rtw_os_ndevs_deinit(dvobj);
free_if_vir:
	if (status != _SUCCESS) {
#ifdef CONFIG_CONCURRENT_MODE
		rtw_drv_stop_vir_ifaces(dvobj);
		rtw_drv_free_vir_ifaces(dvobj);
#endif
	}

	if (status != _SUCCESS && padapter)
		rtw_pci_primary_adapter_deinit(padapter);

free_dvobj:
	if (status != _SUCCESS)
		pci_dvobj_deinit(pdev);
exit:
	return status == _SUCCESS ? 0 : -ENODEV;
}

/*
 * dev_remove() - our device is being removed
*/
/* rmmod module & unplug(SurpriseRemoved) will call r871xu_dev_remove() => how to recognize both */
static void rtw_dev_remove(struct pci_dev *pdev)
{
	struct dvobj_priv *pdvobjpriv = pci_get_drvdata(pdev);
	_adapter *padapter = dvobj_get_primary_adapter(pdvobjpriv);
	struct net_device *pnetdev = padapter->pnetdev;

	if (pdvobjpriv->processing_dev_remove == _TRUE) {
		RTW_WARN("%s-line%d: Warning! device has been removed!\n", __func__, __LINE__);
		return;
	}

	RTW_INFO("+rtw_dev_remove\n");

	pdvobjpriv->processing_dev_remove = _TRUE;

	if (unlikely(!padapter))
		return;

	/* TODO: use rtw_os_ndevs_deinit instead at the first stage of driver's dev deinit function */
	rtw_os_ndevs_unregister(pdvobjpriv);

#if 0
#ifdef RTK_DMP_PLATFORM
	rtw_clr_surprise_removed(padapter);	/* always trate as device exists*/
	/* this will let the driver to disable it's interrupt */
#else
	if (pci_drvpriv.drv_registered == _TRUE) {
		/* RTW_INFO("r871xu_dev_remove():padapter->bSurpriseRemoved == _TRUE\n"); */
		rtw_set_surprise_removed(padapter);
	}
	/*else
	{

		rtw_set_hw_init_completed(padapter, _FALSE);

	}*/
#endif
#endif

#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_ANDROID_POWER)
	rtw_unregister_early_suspend(dvobj_to_pwrctl(pdvobjpriv));
#endif

	if (GET_HAL_DATA(padapter)->bFWReady == _TRUE) {
		rtw_pm_set_ips(padapter, IPS_NONE);
		rtw_pm_set_lps(padapter, PS_MODE_ACTIVE);

		LeaveAllPowerSaveMode(padapter);
	}

	rtw_set_drv_stopped(padapter);	/*for stop thread*/
	rtw_stop_cmd_thread(padapter);
#ifdef CONFIG_CONCURRENT_MODE
	rtw_drv_stop_vir_ifaces(pdvobjpriv);
#endif

#ifdef CONFIG_BT_COEXIST
#ifdef CONFIG_BT_COEXIST_SOCKET_TRX
	if (GET_HAL_DATA(padapter)->EEPROMBluetoothCoexist)
		rtw_btcoex_close_socket(padapter);
#endif
	rtw_btcoex_HaltNotify(padapter);
#endif

	rtw_pci_primary_adapter_deinit(padapter);

#ifdef CONFIG_CONCURRENT_MODE
	rtw_drv_free_vir_ifaces(pdvobjpriv);
#endif

	pci_dvobj_deinit(pdev);

	RTW_INFO("-r871xu_dev_remove, done\n");

	return;
}

static void rtw_dev_shutdown(struct pci_dev *pdev)
{
	struct dvobj_priv *pdvobjpriv = pci_get_drvdata(pdev);
	_adapter *padapter = dvobj_get_primary_adapter(pdvobjpriv);
	struct net_device *pnetdev = padapter->pnetdev;

#ifdef CONFIG_RTL8723D
	if (IS_HARDWARE_TYPE_8723DE(padapter)) {
		u8 u1Tmp;

		u1Tmp = PlatformEFIORead1Byte(padapter, 0x75 /*REG_HCI_OPT_CTRL_8723D+1*/);
		PlatformEFIOWrite1Byte(padapter, 0x75 /*REG_HCI_OPT_CTRL_8723D+1*/, (u1Tmp|BIT0));/*Disable USB Suspend Signal*/
	}
#endif

	rtw_dev_remove(pdev);
}

static int __init rtw_drv_entry(void)
{
	int ret = 0;

	RTW_PRINT("module init start\n");
	dump_drv_version(RTW_DBGDUMP);
#ifdef BTCOEXVERSION
	RTW_PRINT(DRV_NAME" BT-Coex version = %s\n", BTCOEXVERSION);
#endif /* BTCOEXVERSION */

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24))
	/* console_suspend_enabled=0; */
#endif

	pci_drvpriv.drv_registered = _TRUE;
	rtw_suspend_lock_init();
	rtw_drv_proc_init();
	rtw_ndev_notifier_register();
	rtw_inetaddr_notifier_register();

	ret = pci_register_driver(&pci_drvpriv.rtw_pci_drv);

	if (ret != 0) {
		pci_drvpriv.drv_registered = _FALSE;
		rtw_suspend_lock_uninit();
		rtw_drv_proc_deinit();
		rtw_ndev_notifier_unregister();
		rtw_inetaddr_notifier_unregister();
		goto exit;
	}

exit:
	RTW_PRINT("module init ret=%d\n", ret);
	return ret;
}

static void __exit rtw_drv_halt(void)
{
	RTW_PRINT("module exit start\n");

	pci_drvpriv.drv_registered = _FALSE;

	pci_unregister_driver(&pci_drvpriv.rtw_pci_drv);

	rtw_suspend_lock_uninit();
	rtw_drv_proc_deinit();
	rtw_ndev_notifier_unregister();
	rtw_inetaddr_notifier_unregister();

	RTW_PRINT("module exit success\n");

	rtw_mstat_dump(RTW_DBGDUMP);
}


module_init(rtw_drv_entry);
module_exit(rtw_drv_halt);
