diff options
Diffstat (limited to 'drivers/net/wireless/rtlwifi/rtl8723as/os_dep/linux/pci_intf.c')
-rwxr-xr-x | drivers/net/wireless/rtlwifi/rtl8723as/os_dep/linux/pci_intf.c | 2012 |
1 files changed, 2012 insertions, 0 deletions
diff --git a/drivers/net/wireless/rtlwifi/rtl8723as/os_dep/linux/pci_intf.c b/drivers/net/wireless/rtlwifi/rtl8723as/os_dep/linux/pci_intf.c new file mode 100755 index 000000000000..a406301082b2 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723as/os_dep/linux/pci_intf.c @@ -0,0 +1,2012 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ +#define _HCI_INTF_C_ + +#include <drv_conf.h> +#include <osdep_service.h> +#include <drv_types.h> +#include <recv_osdep.h> +#include <xmit_osdep.h> +#include <hal_intf.h> +#include <rtw_version.h> + +#ifndef CONFIG_PCI_HCI + +#error "CONFIG_PCI_HCI shall be on!\n" + +#endif + +#include <pci_ops.h> +#include <pci_osintf.h> +#include <pci_hal.h> + +#if defined (PLATFORM_LINUX) && defined (PLATFORM_WINDOWS) + +#error "Shall be Linux or Windows, but not both!\n" + +#endif + +#ifdef CONFIG_80211N_HT +extern int rtw_ht_enable; +extern int rtw_cbw40_enable; +extern int rtw_ampdu_enable;//for enable tx_ampdu +#endif + +#ifdef CONFIG_PM +extern int pm_netdev_open(struct net_device *pnetdev); +static int rtw_suspend(struct pci_dev *pdev, pm_message_t state); +static int rtw_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 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)}, +#endif +#ifdef CONFIG_RTL8192C + {PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8191)}, + {PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8178)}, + {PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8177)}, + {PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8176)}, +#endif +#ifdef CONFIG_RTL8192D + {PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x8193)}, + {PCI_DEVICE(PCI_VENDER_ID_REALTEK, 0x002B)}, +#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.id_table = rtw_pci_id_tbl, +#ifdef CONFIG_PM + .rtw_pci_drv.suspend = rtw_suspend, + .rtw_pci_drv.resume = rtw_resume, +#else + .rtw_pci_drv.suspend = NULL, + .rtw_pci_drv.resume = NULL, +#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 +}; + +static u8 rtw_pci_platform_switch_device_pci_aspm(_adapter *padapter, u8 value) +{ + struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); + u8 bresult = _SUCCESS; + int error; + + value |= 0x40; + + error = pci_write_config_byte(pdvobjpriv->ppcidev, 0x80, value); + + if(error != 0) + { + bresult = _FALSE; + DBG_871X("rtw_pci_platform_switch_device_pci_aspm error (%d)\n",error); + } + + 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 = _SUCCESS; + int error; + + buffer = value; + + if(!padapter->hw_init_completed) + return bresult; + + error = pci_write_config_byte(pdvobjpriv->ppcidev, 0x81, value); + + if(error != 0) + { + bresult = _FALSE; + DBG_871X("rtw_pci_switch_clk_req error (%d)\n",error); + } + + return bresult; +} + +#if 0 +//Description: +//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_priv *pcipriv = &(pdvobjpriv->pcipriv); + u32 pcicfg_addrport = 0; + u8 num4bytes; + u8 linkctrl_reg; + u16 pcibridge_linkctrlreg, aspmlevel = 0; + + // When there exists anyone's busnum, devnum, and funcnum that are set to 0xff, + // we do not execute any action and return. + // if it is not intel bus then don't enable ASPM. + if ((pcipriv->busnumber == 0xff + && pcipriv->devnumber == 0xff + && pcipriv->funcnumber == 0xff) + || (pcipriv->pcibridge_busnum == 0xff + && pcipriv->pcibridge_devnum == 0xff + && pcipriv->pcibridge_funcnum == 0xff)) + { + DBG_871X("PlatformEnableASPM(): Fail to enable ASPM. Cannot find the Bus of PCI(Bridge).\n"); + return; + } + + if (pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_UNKNOWN) { + DBG_871X("%s(): Disable ASPM. Recognize the Bus of PCI(Bridge) as UNKNOWN.\n", __func__); + } + + 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); + } + + { + // Suggested by SD1 for promising device will in L0 state after an I/O. + u8 tmp_u1b; + + pci_read_config_byte(pdvobjpriv->ppcidev, 0x80, &tmp_u1b); + } + + // Retrieve original configuration settings. + linkctrl_reg = pcipriv->linkctrl_reg; + pcibridge_linkctrlreg = pcipriv->pcibridge_linkctrlreg; + + // Set corresponding value. + aspmlevel |= BIT(0) | BIT(1); + linkctrl_reg &= ~aspmlevel; + pcibridge_linkctrlreg &= ~(BIT(0) | BIT(1)); + + 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. + 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 + pcicfg_addrport = (pcipriv->pcibridge_busnum << 16) | + (pcipriv->pcibridge_devnum << 11) | + (pcipriv->pcibridge_funcnum << 8) | (1 << 31); + num4bytes = (pcipriv->pcibridge_pciehdr_offset + 0x10) / 4; + + // set up address port at 0xCF8 offset field= 0 (dev|vend) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (num4bytes << 2)); + + // now grab data port with device|vendor 4 byte dword + NdisRawWritePortUchar(PCI_CONF_DATA, pcibridge_linkctrlreg); + + DBG_871X("rtw_pci_disable_aspm():PciBridge busnumber[%x], DevNumbe[%x], funcnumber[%x], Write reg[%x] = %x\n", + pcipriv->pcibridge_busnum, pcipriv->pcibridge_devnum, + pcipriv->pcibridge_funcnum, + (pcipriv->pcibridge_pciehdr_offset+0x10), pcibridge_linkctrlreg); + + rtw_udelay_os(50); + } +} + +//[ASPM] +//Description: +// 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_priv *pcipriv = &(pdvobjpriv->pcipriv); + u16 aspmlevel = 0; + u32 pcicfg_addrport = 0; + u8 num4bytes; + u8 u_pcibridge_aspmsetting = 0; + u8 u_device_aspmsetting = 0; + + // When there exists anyone's busnum, devnum, and funcnum that are set to 0xff, + // we do not execute any action and return. + // if it is not intel bus then don't enable ASPM. + + if ((pcipriv->busnumber == 0xff + && pcipriv->devnumber == 0xff + && pcipriv->funcnumber == 0xff) + || (pcipriv->pcibridge_busnum == 0xff + && pcipriv->pcibridge_devnum == 0xff + && pcipriv->pcibridge_funcnum == 0xff)) + { + DBG_871X("PlatformEnableASPM(): Fail to enable ASPM. Cannot find the Bus of PCI(Bridge).\n"); + return; + } + + //4 Enable Pci Bridge ASPM + pcicfg_addrport = (pcipriv->pcibridge_busnum << 16) + | (pcipriv->pcibridge_devnum << 11) + | (pcipriv->pcibridge_funcnum << 8) | (1 << 31); + num4bytes = (pcipriv->pcibridge_pciehdr_offset + 0x10) / 4; + // set up address port at 0xCF8 offset field= 0 (dev|vend) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (num4bytes << 2)); + // now grab data port with device|vendor 4 byte dword + + u_pcibridge_aspmsetting = pcipriv->pcibridge_linkctrlreg | 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); + + NdisRawWritePortUchar(PCI_CONF_DATA, u_pcibridge_aspmsetting); + + DBG_871X("PlatformEnableASPM():PciBridge busnumber[%x], DevNumbe[%x], funcnumber[%x], Write reg[%x] = %x\n", + pcipriv->pcibridge_busnum, + pcipriv->pcibridge_devnum, + pcipriv->pcibridge_funcnum, + (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; + + rtw_pci_platform_switch_device_pci_aspm(padapter, u_device_aspmsetting); //(priv->linkctrl_reg | ASPMLevel)); + + 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); +} + +// +//Description: +//To get link control field by searching from PCIe capability lists. +// +static u8 +rtw_get_link_control_field(_adapter *padapter, u8 busnum, u8 devnum, + u8 funcnum) +{ + struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); + struct pci_priv *pcipriv = &(pdvobjpriv->pcipriv); + struct rt_pci_capabilities_header capability_hdr; + u8 capability_offset, num4bytes; + u32 pcicfg_addrport = 0; + u8 linkctrl_reg; + u8 status = _FALSE; + + //If busnum, devnum, funcnum are set to 0xff. + if (busnum == 0xff && devnum == 0xff && funcnum == 0xff) { + DBG_871X("GetLinkControlField(): Fail to find PCIe Capability\n"); + return _FALSE; + } + + pcicfg_addrport = (busnum << 16) | (devnum << 11) | (funcnum << 8) | (1 << 31); + + //2PCIeCap + + // The device supports capability lists. Find the capabilities. + num4bytes = 0x34 / 4; + //get capability_offset + // set up address port at 0xCF8 offset field= 0 (dev|vend) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (num4bytes << 2)); + // now grab data port with device|vendor 4 byte dword + NdisRawReadPortUchar(PCI_CONF_DATA, &capability_offset); + + // 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 (capability_offset != 0) { + // First find the number of 4 Byte. + num4bytes = capability_offset / 4; + + // 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 capability_hdr + // set up address port at 0xCF8 offset field= 0 (dev|vend) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (num4bytes << 2)); + // now grab data port with device|vendor 4 byte dword + NdisRawReadPortUshort(PCI_CONF_DATA, (u16 *) & capability_hdr); + + // Found the PCI express capability + if (capability_hdr.capability_id == PCI_CAPABILITY_ID_PCI_EXPRESS) + { + break; + } + else + { + // This is some other capability. Keep looking for the PCI express capability. + capability_offset = capability_hdr.next; + } + } + + if (capability_hdr.capability_id == PCI_CAPABILITY_ID_PCI_EXPRESS) // + { + num4bytes = (capability_offset + 0x10) / 4; + + //4 Read Link Control Register + // set up address port at 0xCF8 offset field= 0 (dev|vend) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (num4bytes << 2)); + // now grab data port with device|vendor 4 byte dword + NdisRawReadPortUchar(PCI_CONF_DATA, &linkctrl_reg); + + pcipriv->pcibridge_pciehdr_offset = capability_offset; + pcipriv->pcibridge_linkctrlreg = linkctrl_reg; + + status = _TRUE; + } + else + { + // We didn't find a PCIe capability. + DBG_871X("GetLinkControlField(): Cannot Find PCIe Capability\n"); + } + + return status; +} + +// +//Description: +//To get PCI bus infomation and return busnum, devnum, and funcnum about +//the bus(bridge) which the device binds. +// +static u8 +rtw_get_pci_bus_info(_adapter *padapter, + u16 vendorid, + u16 deviceid, + u8 irql, u8 basecode, u8 subclass, u8 filed19val, + u8 * busnum, u8 * devnum, u8 * funcnum) +{ + struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); + struct pci_dev *pdev = pdvobjpriv->ppcidev; + u8 busnum_idx, devicenum_idx, functionnum_idx; + u32 pcicfg_addrport = 0; + u32 dev_venid = 0, classcode, field19, headertype; + u16 venId, devId; + u8 basec, subc, irqline; + u16 regoffset; + u8 b_singlefunc = _FALSE; + u8 b_bridgechk = _FALSE; + + *busnum = 0xFF; + *devnum = 0xFF; + *funcnum = 0xFF; + + //DBG_871X("==============>vendorid:%x,deviceid:%x,irql:%x\n", vendorid,deviceid,irql); + if ((basecode == PCI_CLASS_BRIDGE_DEV) && + (subclass == PCI_SUBCLASS_BR_PCI_TO_PCI) + && (filed19val == U1DONTCARE)) + b_bridgechk = _TRUE; + + // perform a complete pci bus scan operation + for (busnum_idx = 0; busnum_idx < PCI_MAX_BRIDGE_NUMBER; busnum_idx++) //255 + { + for (devicenum_idx = 0; devicenum_idx < PCI_MAX_DEVICES; devicenum_idx++) //32 + { + b_singlefunc = _FALSE; + for (functionnum_idx = 0; functionnum_idx < PCI_MAX_FUNCTION; functionnum_idx++) //8 + { + // + // <Roger_Notes> We have to skip redundant Bus scan to prevent unexpected system hang + // if single function is present in this device. + // 2009.02.26. + // + if (functionnum_idx == 0) { + //4 get header type (DWORD #3) + pcicfg_addrport = (busnum_idx << 16) | (devicenum_idx << 11) | (functionnum_idx << 8) | (1 << 31); + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (3 << 2)); + NdisRawReadPortUlong(PCI_CONF_DATA, &headertype); + headertype = ((headertype >> 16) & 0x0080) >> 7; // address 0x0e[7]. + if (headertype == 0) //Single function + b_singlefunc = _TRUE; + } + else + {//By pass the following scan process. + if (b_singlefunc == _TRUE) + break; + } + + // Set access enable control. + pcicfg_addrport = (busnum_idx << 16) | (devicenum_idx << 11) | (functionnum_idx << 8) | (1 << 31); + + //4 // Get vendorid/ deviceid + // set up address port at 0xCF8 offset field= 0 (dev|vend) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport); + // now grab data port with device|vendor 4 byte dword + NdisRawReadPortUlong(PCI_CONF_DATA, &dev_venid); + + // if data port is full of 1s, no device is present + // some broken boards return 0 if a slot is empty: + if (dev_venid == 0xFFFFFFFF || dev_venid == 0) + continue; //PCI_INVALID_VENDORID + + // 4 // Get irql + regoffset = 0x3C; + pcicfg_addrport = (busnum_idx << 16) | (devicenum_idx << 11) | (functionnum_idx << 8) | (1 << 31) | (regoffset & 0xFFFFFFFC); + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport); + NdisRawReadPortUchar((PCI_CONF_DATA +(regoffset & 0x3)), &irqline); + + venId = (u16) (dev_venid >> 0) & 0xFFFF; + devId = (u16) (dev_venid >> 16) & 0xFFFF; + + // Check Vendor ID + if (!b_bridgechk && (venId != vendorid) && (vendorid != U2DONTCARE)) + continue; + + // Check Device ID + if (!b_bridgechk && (devId != deviceid) && (deviceid != U2DONTCARE)) + continue; + + // Check irql + if (!b_bridgechk && (irqline != irql) && (irql != U1DONTCARE)) + continue; + + //4 get Class Code + pcicfg_addrport = (busnum_idx << 16) | (devicenum_idx << 11) | (functionnum_idx << 8) | (1 << 31); + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (2 << 2)); + NdisRawReadPortUlong(PCI_CONF_DATA, &classcode); + classcode = classcode >> 8; + + basec = (u8) (classcode >> 16) & 0xFF; + subc = (u8) (classcode >> 8) & 0xFF; + if (b_bridgechk && (venId != vendorid) && (basec == basecode) && (subc == subclass)) + return _TRUE; + + // Check Vendor ID + if (b_bridgechk && (venId != vendorid) && (vendorid != U2DONTCARE)) + continue; + + // Check Device ID + if (b_bridgechk && (devId != deviceid) && (deviceid != U2DONTCARE)) + continue; + + // Check irql + if (b_bridgechk && (irqline != irql) && (irql != U1DONTCARE)) + continue; + + //4 get field 0x19 value (DWORD #6) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (6 << 2)); + NdisRawReadPortUlong(PCI_CONF_DATA, &field19); + field19 = (field19 >> 8) & 0xFF; + + //4 Matching Class Code and filed19. + if ((basec == basecode) && (subc == subclass) && ((field19 == filed19val) || (filed19val == U1DONTCARE))) { + *busnum = busnum_idx; + *devnum = devicenum_idx; + *funcnum = functionnum_idx; + + DBG_871X("GetPciBusInfo(): Find Device(%X:%X) bus=%d dev=%d, func=%d\n", + vendorid, deviceid, busnum_idx, devicenum_idx, functionnum_idx); + return _TRUE; + } + } + } + } + + DBG_871X("GetPciBusInfo(): Cannot Find Device(%X:%X:%X)\n", vendorid, deviceid, dev_venid); + + return _FALSE; +} + +static u8 +rtw_get_pci_brideg_info(_adapter *padapter, + u8 basecode, + u8 subclass, + u8 filed19val, u8 * busnum, u8 * devnum, + u8 * funcnum, u16 * vendorid, u16 * deviceid) +{ + u8 busnum_idx, devicenum_idx, functionnum_idx; + u32 pcicfg_addrport = 0; + u32 dev_venid, classcode, field19, headertype; + u16 venId, devId; + u8 basec, subc, irqline; + u16 regoffset; + u8 b_singlefunc = _FALSE; + + *busnum = 0xFF; + *devnum = 0xFF; + *funcnum = 0xFF; + + // perform a complete pci bus scan operation + for (busnum_idx = 0; busnum_idx < PCI_MAX_BRIDGE_NUMBER; busnum_idx++) //255 + { + for (devicenum_idx = 0; devicenum_idx < PCI_MAX_DEVICES; devicenum_idx++) //32 + { + b_singlefunc = _FALSE; + for (functionnum_idx = 0; functionnum_idx < PCI_MAX_FUNCTION; functionnum_idx++) //8 + { + // + // <Roger_Notes> We have to skip redundant Bus scan to prevent unexpected system hang + // if single function is present in this device. + // 2009.02.26. + // + if (functionnum_idx == 0) + { + //4 get header type (DWORD #3) + pcicfg_addrport = (busnum_idx << 16) | (devicenum_idx << 11) | (functionnum_idx << 8) | (1 << 31); + //NdisRawWritePortUlong((ULONG_PTR)PCI_CONF_ADDRESS , pcicfg_addrport + (3 << 2)); + //NdisRawReadPortUlong((ULONG_PTR)PCI_CONF_DATA, &headertype); + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (3 << 2)); + NdisRawReadPortUlong(PCI_CONF_DATA, &headertype); + headertype = ((headertype >> 16) & 0x0080) >> 7; // address 0x0e[7]. + if (headertype == 0) //Single function + b_singlefunc = _TRUE; + } + else + {//By pass the following scan process. + if (b_singlefunc == _TRUE) + break; + } + + pcicfg_addrport = (busnum_idx << 16) | (devicenum_idx << 11) | (functionnum_idx << 8) | (1 << 31); + + //4 // Get vendorid/ deviceid + // set up address port at 0xCF8 offset field= 0 (dev|vend) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport); + // now grab data port with device|vendor 4 byte dword + NdisRawReadPortUlong(PCI_CONF_DATA, &dev_venid); + + //4 Get irql + regoffset = 0x3C; + pcicfg_addrport = (busnum_idx << 16) | (devicenum_idx << 11) | (functionnum_idx << 8) | (1 << 31) | (regoffset & 0xFFFFFFFC); + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport); + NdisRawReadPortUchar((PCI_CONF_DATA + (regoffset & 0x3)), &irqline); + + venId = (u16) (dev_venid >> 0) & 0xFFFF; + devId = (u16) (dev_venid >> 16) & 0xFFFF; + + //4 get Class Code + pcicfg_addrport = (busnum_idx << 16) | (devicenum_idx << 11) | (functionnum_idx << 8) | (1 << 31); + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (2 << 2)); + NdisRawReadPortUlong(PCI_CONF_DATA, &classcode); + classcode = classcode >> 8; + + basec = (u8) (classcode >> 16) & 0xFF; + subc = (u8) (classcode >> 8) & 0xFF; + + //4 get field 0x19 value (DWORD #6) + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (6 << 2)); + NdisRawReadPortUlong(PCI_CONF_DATA, &field19); + field19 = (field19 >> 8) & 0xFF; + + //4 Matching Class Code and filed19. + if ((basec == basecode) && (subc == subclass) && ((field19 == filed19val) || (filed19val == U1DONTCARE))) { + *busnum = busnum_idx; + *devnum = devicenum_idx; + *funcnum = functionnum_idx; + *vendorid = venId; + *deviceid = devId; + + DBG_871X("GetPciBridegInfo : Find Device(%X:%X) bus=%d dev=%d, func=%d\n", + venId, devId, busnum_idx, devicenum_idx, functionnum_idx); + + return _TRUE; + } + } + } + } + + DBG_871X("GetPciBridegInfo(): Cannot Find PciBridge for Device\n"); + + return _FALSE; +} // end of GetPciBridegInfo + +// +//Description: +//To find specific bridge information. +// +static void rtw_find_bridge_info(_adapter *padapter) +{ + struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); + struct pci_priv *pcipriv = &(pdvobjpriv->pcipriv); + u8 pcibridge_busnum = 0xff; + u8 pcibridge_devnum = 0xff; + u8 pcibridge_funcnum = 0xff; + u16 pcibridge_vendorid = 0xff; + u16 pcibridge_deviceid = 0xff; + u8 tmp = 0; + + rtw_get_pci_brideg_info(padapter, + PCI_CLASS_BRIDGE_DEV, + PCI_SUBCLASS_BR_PCI_TO_PCI, + pcipriv->busnumber, + &pcibridge_busnum, + &pcibridge_devnum, &pcibridge_funcnum, + &pcibridge_vendorid, &pcibridge_deviceid); + + // match the array of vendor id and regonize which chipset is used. + pcipriv->pcibridge_vendor = PCI_BRIDGE_VENDOR_UNKNOWN; + + for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) { + if (pcibridge_vendorid == pcibridge_vendors[tmp]) { + pcipriv->pcibridge_vendor = tmp; + DBG_871X("Pci Bridge Vendor is found index: %d\n", tmp); + break; + } + } + DBG_871X("Pci Bridge Vendor is %x\n", pcibridge_vendors[tmp]); + + // Update corresponding PCI bus info. + pcipriv->pcibridge_busnum = pcibridge_busnum; + pcipriv->pcibridge_devnum = pcibridge_devnum; + pcipriv->pcibridge_funcnum = pcibridge_funcnum; + pcipriv->pcibridge_vendorid = pcibridge_vendorid; + pcipriv->pcibridge_deviceid = pcibridge_deviceid; + +} + +static u8 +rtw_get_amd_l1_patch(_adapter *padapter, u8 busnum, u8 devnum, + u8 funcnum) +{ + u8 status = _FALSE; + u8 offset_e0; + unsigned offset_e4; + u32 pcicfg_addrport = 0; + + pcicfg_addrport = (busnum << 16) | (devnum << 11) | (funcnum << 8) | (1 << 31); + + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + 0xE0); + NdisRawWritePortUchar(PCI_CONF_DATA, 0xA0); + + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + 0xE0); + NdisRawReadPortUchar(PCI_CONF_DATA, &offset_e0); + + if (offset_e0 == 0xA0) + { + NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + 0xE4); + NdisRawReadPortUlong(PCI_CONF_DATA, &offset_e4); + //DbgPrint("Offset E4 %x\n", offset_e4); + if (offset_e4 & BIT(23)) + status = _TRUE; + } + + return status; +} +#else +/*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; + u16 pcibridge_linkctrlreg; + u16 aspmlevel = 0; + + // We do not diable/enable ASPM by driver, in the future, the BIOS will enable host and NIC ASPM. + // Advertised by SD1 victorh. Added by tynli. 2009.11.23. + if(pdvobjpriv->const_pci_aspm == 0) + return; + + if(!padapter->hw_init_completed) + return; + + if (pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_UNKNOWN) { + RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("%s(): PCI(Bridge) UNKNOWN.\n", __FUNCTION__)); + 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; + + 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, 0x80, &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 + { + /*Disable Pci Bridge ASPM*/ + //NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + (num4bytes << 2)); + //NdisRawWritePortUchar(PCI_CONF_DATA, pcibridge_linkctrlreg); + pci_write_config_byte(bridge_pdev, pcipriv->pcibridge_pciehdr_offset + 0x10, pcibridge_linkctrlreg); + + DBG_871X("rtw_pci_disable_aspm():PciBridge busnumber[%x], DevNumbe[%x], funcnumber[%x], Write reg[%x] = %x\n", + pcipriv->pcibridge_busnum, pcipriv->pcibridge_devnum, + pcipriv->pcibridge_funcnum, + (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 do not diable/enable ASPM by driver, in the future, the BIOS will enable host and NIC ASPM. + // Advertised by SD1 victorh. Added by tynli. 2009.11.23. + if(pdvobjpriv->const_pci_aspm == 0) + 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) ) + { + DBG_871X("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 + if (IS_HARDWARE_TYPE_8192DE(padapter)) + { + //PciCfgAddrPort = (pcipriv->pcibridge_busnum << 16)|(pcipriv->pcibridge_devnum<< 11)|(pcipriv->pcibridge_funcnum << 8)|(1 << 31); + //Num4Bytes = (pcipriv->pcibridge_pciehdr_offset+0x0C)/4; + //NdisRawWritePortUlong((ULONG_PTR)PCI_CONF_ADDRESS , PciCfgAddrPort+(Num4Bytes << 2)); + //NdisRawReadPortUlong((ULONG_PTR)PCI_CONF_DATA,&uDeviceASPMSupportSetting); + pci_read_config_dword(bridge_pdev, (pcipriv->pcibridge_pciehdr_offset+0x0C), &u_device_aspmsupportsetting); + DBG_871X("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) + { + DBG_871X("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)) + { + DBG_871X("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)) + { + DBG_871X("rtw_pci_enable_aspm(): Bridge not support L0s \n"); + return; + } + + } + } + else + { + DBG_871X("rtw_pci_enable_aspm(): Bridge support L0s and L1 \n"); + } + } + + + /*Enable Pci Bridge ASPM*/ + //PciCfgAddrPort = (pcipriv->pcibridge_busnum << 16)|(pcipriv->pcibridge_devnum<< 11) |(pcipriv->pcibridge_funcnum << 8)|(1 << 31); + //Num4Bytes = (pcipriv->pcibridge_pciehdr_offset+0x10)/4; + // set up address port at 0xCF8 offset field= 0 (dev|vend) + //NdisRawWritePortUlong(PCI_CONF_ADDRESS, PciCfgAddrPort + (Num4Bytes << 2)); + // now grab data port with device|vendor 4 byte dword + + 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 + + //NdisRawWritePortUchar(PCI_CONF_DATA, u_pcibridge_aspmsetting); + pci_write_config_byte(bridge_pdev, (pcipriv->pcibridge_pciehdr_offset+0x10), u_pcibridge_aspmsetting); + + DBG_871X("PlatformEnableASPM():PciBridge busnumber[%x], DevNumbe[%x], funcnumber[%x], Write reg[%x] = %x\n", + pcipriv->pcibridge_busnum, pcipriv->pcibridge_devnum, pcipriv->pcibridge_funcnum, + (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 = pdvobjpriv->ppcidev; + struct pci_dev *bridge_pdev = pdev->bus->self; + u8 status = _FALSE; + u8 offset_e0; + u32 offset_e4; + + //NdisRawWritePortUlong(PCI_CONF_ADDRESS,pcicfg_addrport + 0xE0); + //NdisRawWritePortUchar(PCI_CONF_DATA, 0xA0); + pci_write_config_byte(bridge_pdev, 0xE0, 0xA0); + + //NdisRawWritePortUlong(PCI_CONF_ADDRESS,pcicfg_addrport + 0xE0); + //NdisRawReadPortUchar(PCI_CONF_DATA, &offset_e0); + pci_read_config_byte(bridge_pdev, 0xE0, &offset_e0); + + if (offset_e0 == 0xA0) { + //NdisRawWritePortUlong(PCI_CONF_ADDRESS, pcicfg_addrport + 0xE4); + //NdisRawReadPortUlong(PCI_CONF_DATA, &offset_e4); + pci_read_config_dword(bridge_pdev, 0xE4, &offset_e4); + if (offset_e4 & BIT(23)) + status = _TRUE; + } + + return status; +} + +static void rtw_pci_get_linkcontrol_field(struct dvobj_priv *pdvobjpriv) +{ + struct pci_priv *pcipriv = &(pdvobjpriv->pcipriv); + struct pci_dev *pdev = pdvobjpriv->ppcidev; + struct pci_dev *bridge_pdev = pdev->bus->self; + u8 capabilityoffset = pcipriv->pcibridge_pciehdr_offset; + u8 linkctrl_reg; + + /*Read Link Control Register*/ + pci_read_config_byte(bridge_pdev, capabilityoffset + PCI_EXP_LNKCTL, &linkctrl_reg); + + pcipriv->pcibridge_linkctrlreg = linkctrl_reg; +} +#endif + +static void rtw_pci_parse_configuration(struct pci_dev *pdev, struct dvobj_priv *pdvobjpriv) +{ + struct pci_priv *pcipriv = &(pdvobjpriv->pcipriv); + u8 tmp; + int pos; + u8 linkctrl_reg; + + //Link Control Register + pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); + pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &linkctrl_reg); + pcipriv->linkctrl_reg = linkctrl_reg; + + //DBG_871X("Link Control Register = %x\n", pcipriv->linkctrl_reg); + + pci_read_config_byte(pdev, 0x98, &tmp); + tmp |= BIT(4); + pci_write_config_byte(pdev, 0x98, tmp); + + //tmp = 0x17; + //pci_write_config_byte(pdev, 0x70f, tmp); +} + +// +// 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); + + //reset pPSC->reg_rfps_level & priv->b_support_aspm + pwrpriv->reg_rfps_level = 0; + pwrpriv->b_support_aspm = 0; + + // Dynamic Mechanism, + //rtw_hal_set_def_var(pAdapter, HAL_DEF_INIT_GAIN, &(pDevice->InitGainState)); + + // 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; + } + + 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 0: // Not support ASPM. + { + u8 b_support_aspm = _FALSE; + pwrpriv->b_support_aspm = b_support_aspm; + } + break; + + case 1: // Support ASPM. + { + u8 b_support_aspm = _TRUE; + u8 b_support_backdoor = _TRUE; + + pwrpriv->b_support_aspm = b_support_aspm; + + /*if(pAdapter->MgntInfo.CustomerID == RT_CID_TOSHIBA && + pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_AMD && + !pcipriv->amd_l1_patch) + b_support_backdoor = _FALSE;*/ + + pwrpriv->b_support_backdoor = b_support_backdoor; + } + break; + + case 2: // Set by Chipset. + // ASPM value set by chipset. + if (pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_INTEL) { + u8 b_support_aspm = _TRUE; + pwrpriv->b_support_aspm = b_support_aspm; + } + 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); + } + +} + +#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->if1; + + if (dvobj->irq_enabled == 0) { + return IRQ_HANDLED; + } + + if(rtw_hal_interrupt_handler(adapter) == _FAIL) + return IRQ_HANDLED; + //return IRQ_NONE; + + return IRQ_HANDLED; +} + +#ifdef RTK_DMP_PLATFORM +#define pci_iounmap(x,y) iounmap(y) +#endif + +int pci_alloc_irq(struct dvobj_priv *dvobj) +{ + int err; + struct pci_dev *pdev = dvobj->ppcidev; + +#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) { + DBG_871X("Error allocating IRQ %d",pdev->irq); + } else { + dvobj->irq_alloc = 1; + DBG_871X("Request_irq OK, IRQ %d\n",pdev->irq); + } + + return err?_FAIL:_SUCCESS; +} + +static struct dvobj_priv *pci_dvobj_init(struct pci_dev *pdev) +{ + int err; + u32 status = _FAIL; + struct dvobj_priv *dvobj = NULL; + struct pci_priv *pcipriv = NULL; + struct pci_dev *bridge_pdev = pdev->bus->self; + unsigned long pmem_start, pmem_len, pmem_flags; + u8 tmp; + +_func_enter_; + + if ((dvobj = (struct dvobj_priv*)rtw_zmalloc(sizeof(*dvobj))) == NULL) { + goto exit; + } + dvobj->ppcidev = pdev; + pcipriv = &(dvobj->pcipriv); + pci_set_drvdata(pdev, dvobj); + + _rtw_mutex_init(&dvobj->hw_init_mutex); + _rtw_mutex_init(&dvobj->h2c_fwcmd_mutex); + _rtw_mutex_init(&dvobj->setch_mutex); + _rtw_mutex_init(&dvobj->setbw_mutex); + _rtw_spinlock_init(&dvobj->lock); + dvobj->macid[1] = _TRUE; //macid=1 for bc/mc stainfo + + dvobj->processing_dev_remove = _FALSE; + if ( (err = pci_enable_device(pdev)) != 0) { + DBG_871X(KERN_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))) { + DBG_871X("RTL819xCE: Using 64bit DMA\n"); + if ((err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) != 0) { + DBG_871X(KERN_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))) { + if ((err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) != 0) { + DBG_871X(KERN_ERR "Unable to obtain 32bit DMA for consistent allocations\n"); + goto disable_picdev; + } + } + } + + pci_set_master(pdev); + + if ((err = pci_request_regions(pdev, DRV_NAME)) != 0) { + DBG_871X(KERN_ERR "Can't obtain PCI resources\n"); + goto disable_picdev; + } + //MEM map + pmem_start = pci_resource_start(pdev, 2); + pmem_len = pci_resource_len(pdev, 2); + pmem_flags = pci_resource_flags(pdev, 2); + +#ifdef RTK_DMP_PLATFORM + dvobj->pci_mem_start = (unsigned long)ioremap_nocache(pmem_start, pmem_len); +#else + dvobj->pci_mem_start = (unsigned long)pci_iomap(pdev, 2, pmem_len); /* shared mem start */ +#endif + if (dvobj->pci_mem_start == 0) { + DBG_871X(KERN_ERR "Can't map PCI mem\n"); + goto release_regions; + } + + DBG_871X("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); + + // Disable Clk Request */ + pci_write_config_byte(pdev, 0x81, 0); + // leave D3 mode */ + pci_write_config_byte(pdev, 0x44, 0); + pci_write_config_byte(pdev, 0x04, 0x06); + pci_write_config_byte(pdev, 0x04, 0x07); + +#if 1 + /*find bus info*/ + pcipriv->busnumber = pdev->bus->number; + pcipriv->devnumber = PCI_SLOT(pdev->devfn); + pcipriv->funcnumber = PCI_FUNC(pdev->devfn); + + /*find bridge info*/ + pcipriv->pcibridge_vendor = PCI_BRIDGE_VENDOR_UNKNOWN; + if(bridge_pdev){ + pcipriv->pcibridge_vendorid = bridge_pdev->vendor; + for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) { + if (bridge_pdev->vendor == pcibridge_vendors[tmp]) { + pcipriv->pcibridge_vendor = tmp; + DBG_871X("Pci Bridge Vendor is found index: %d, %x\n", tmp, pcibridge_vendors[tmp]); + break; + } + } + } + + //if (pcipriv->pcibridge_vendor != PCI_BRIDGE_VENDOR_UNKNOWN) { + 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); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) + pcipriv->pcibridge_pciehdr_offset = pci_find_capability(bridge_pdev, PCI_CAP_ID_EXP); +#else + pcipriv->pcibridge_pciehdr_offset = bridge_pdev->pcie_cap; +#endif + + rtw_pci_get_linkcontrol_field(dvobj); + + if (pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_AMD) { + pcipriv->amd_l1_patch = rtw_pci_get_amd_l1_patch(dvobj); + } + } +#else + // + // Find bridge related info. + // + rtw_get_pci_bus_info(padapter, + pdev->vendor, + pdev->device, + (u8) pdvobjpriv->irqline, + 0x02, 0x80, U1DONTCARE, + &pcipriv->busnumber, + &pcipriv->devnumber, + &pcipriv->funcnumber); + + rtw_find_bridge_info(padapter); + + if (pcipriv->pcibridge_vendor != PCI_BRIDGE_VENDOR_UNKNOWN) { + rtw_get_link_control_field(padapter, + pcipriv->pcibridge_busnum, + pcipriv->pcibridge_devnum, + pcipriv->pcibridge_funcnum); + + if (pcipriv->pcibridge_vendor == PCI_BRIDGE_VENDOR_AMD) { + pcipriv->amd_l1_patch = + rtw_get_amd_l1_patch(padapter, + pcipriv->pcibridge_busnum, + pcipriv->pcibridge_devnum, + pcipriv->pcibridge_funcnum); + } + } +#endif + + // + // Allow the hardware to look at PCI config information. + // + rtw_pci_parse_configuration(pdev, dvobj); + + DBG_871X("pcidev busnumber:devnumber:funcnumber:" + "vendor:link_ctl %d:%d:%d:%x:%x\n", + pcipriv->busnumber, + pcipriv->devnumber, + pcipriv->funcnumber, + pdev->vendor, + pcipriv->linkctrl_reg); + + DBG_871X("pci_bridge busnumber:devnumber:funcnumber:vendor:" + "pcie_cap:link_ctl_reg: %d:%d:%d:%x:%x:%x:%x\n", + pcipriv->pcibridge_busnum, + pcipriv->pcibridge_devnum, + pcipriv->pcibridge_funcnum, + pcibridge_vendors[pcipriv->pcibridge_vendor], + pcipriv->pcibridge_pciehdr_offset, + pcipriv->pcibridge_linkctrlreg, + pcipriv->amd_l1_patch); + + status = _SUCCESS; + +iounmap: + if (status != _SUCCESS && dvobj->pci_mem_start != 0) { + pci_iounmap(pdev, (void *)dvobj->pci_mem_start); + dvobj->pci_mem_start = 0; + } +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); + _rtw_spinlock_free(&dvobj->lock); + _rtw_mutex_free(&dvobj->hw_init_mutex); + _rtw_mutex_free(&dvobj->h2c_fwcmd_mutex); + _rtw_mutex_free(&dvobj->setch_mutex); + _rtw_mutex_free(&dvobj->setbw_mutex); + rtw_mfree((u8*)dvobj, sizeof(*dvobj)); + dvobj = NULL; + } +exit: +_func_exit_; + return dvobj; +} + + +static void pci_dvobj_deinit(struct pci_dev *pdev) +{ + struct dvobj_priv *dvobj = pci_get_drvdata(pdev); +_func_enter_; + + pci_set_drvdata(pdev, NULL); + if (dvobj) { + if (dvobj->irq_alloc) { + free_irq(pdev->irq, dvobj); + dvobj->irq_alloc = 0; + } + + if (dvobj->pci_mem_start != 0) { + pci_iounmap(pdev, (void *)dvobj->pci_mem_start); + dvobj->pci_mem_start = 0; + } + + _rtw_spinlock_free(&dvobj->lock); + _rtw_mutex_free(&dvobj->hw_init_mutex); + _rtw_mutex_free(&dvobj->h2c_fwcmd_mutex); + _rtw_mutex_free(&dvobj->setch_mutex); + _rtw_mutex_free(&dvobj->setbw_mutex); + + rtw_mfree((u8*)dvobj, sizeof(*dvobj)); + } + + pci_release_regions(pdev); + pci_disable_device(pdev); + +_func_exit_; +} + + +static void decide_chip_type_by_pci_device_id(_adapter *padapter, struct pci_dev *pdev) +{ + u16 venderid, deviceid, irqline; + u8 revisionid; + struct dvobj_priv *pdvobjpriv=adapter_to_dvobj(padapter); + + + venderid = pdev->vendor; + deviceid = pdev->device; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) + pci_read_config_byte(pdev, PCI_REVISION_ID, &revisionid); // PCI_REVISION_ID 0x08 +#else + revisionid = pdev->revision; +#endif + pci_read_config_word(pdev, PCI_INTERRUPT_LINE, &irqline); // PCI_INTERRUPT_LINE 0x3c + pdvobjpriv->irqline = irqline; + + + // + // Decide hardware type here. + // + if( deviceid == HAL_HW_PCI_8185_DEVICE_ID || + deviceid == HAL_HW_PCI_8188_DEVICE_ID || + deviceid == HAL_HW_PCI_8198_DEVICE_ID) + { + DBG_871X("Adapter (8185/8185B) is found- VendorID/DeviceID=%x/%x\n", venderid, deviceid); + padapter->HardwareType=HARDWARE_TYPE_RTL8185; + } + else if (deviceid == HAL_HW_PCI_8190_DEVICE_ID || + deviceid == HAL_HW_PCI_0045_DEVICE_ID || + deviceid == HAL_HW_PCI_0046_DEVICE_ID || + deviceid == HAL_HW_PCI_DLINK_DEVICE_ID) + { + DBG_871X("Adapter(8190 PCI) is found - vendorid/deviceid=%x/%x\n", venderid, deviceid); + padapter->HardwareType = HARDWARE_TYPE_RTL8190P; + } + else if (deviceid == HAL_HW_PCI_8192_DEVICE_ID || + deviceid == HAL_HW_PCI_0044_DEVICE_ID || + deviceid == HAL_HW_PCI_0047_DEVICE_ID || + deviceid == HAL_HW_PCI_8192SE_DEVICE_ID || + deviceid == HAL_HW_PCI_8174_DEVICE_ID || + deviceid == HAL_HW_PCI_8173_DEVICE_ID || + deviceid == HAL_HW_PCI_8172_DEVICE_ID || + deviceid == HAL_HW_PCI_8171_DEVICE_ID) + { + // 8192e and and 8192se may have the same device ID 8192. However, their Revision + // ID is different + // Added for 92DE. We deferentiate it from SVID,SDID. + if( pdev->subsystem_vendor == 0x10EC && pdev->subsystem_device == 0xE020){ + padapter->HardwareType = HARDWARE_TYPE_RTL8192DE; + DBG_871X("Adapter(8192DE) is found - VendorID/DeviceID/RID=%X/%X/%X\n", venderid, deviceid, revisionid); + }else{ + switch (revisionid) { + case HAL_HW_PCI_REVISION_ID_8192PCIE: + DBG_871X("Adapter(8192 PCI-E) is found - vendorid/deviceid=%x/%x\n", venderid, deviceid); + padapter->HardwareType = HARDWARE_TYPE_RTL8192E; + break; + case HAL_HW_PCI_REVISION_ID_8192SE: + DBG_871X("Adapter(8192SE) is found - vendorid/deviceid=%x/%x\n", venderid, deviceid); + padapter->HardwareType = HARDWARE_TYPE_RTL8192SE; + break; + default: + DBG_871X("Err: Unknown device - vendorid/deviceid=%x/%x\n", venderid, deviceid); + padapter->HardwareType = HARDWARE_TYPE_RTL8192SE; + break; + } + } + } + else if(deviceid==HAL_HW_PCI_8723E_DEVICE_ID ) + {//RTL8723E may have the same device ID with RTL8192CET + padapter->HardwareType = HARDWARE_TYPE_RTL8723AE; + DBG_871X("Adapter(8723 PCI-E) is found - VendorID/DeviceID=%x/%x\n", venderid, deviceid); + } + else if (deviceid == HAL_HW_PCI_8192CET_DEVICE_ID || + deviceid == HAL_HW_PCI_8192CE_DEVICE_ID || + deviceid == HAL_HW_PCI_8191CE_DEVICE_ID || + deviceid == HAL_HW_PCI_8188CE_DEVICE_ID) + { + DBG_871X("Adapter(8192C PCI-E) is found - vendorid/deviceid=%x/%x\n", venderid, deviceid); + padapter->HardwareType = HARDWARE_TYPE_RTL8192CE; + } + else if (deviceid == HAL_HW_PCI_8192DE_DEVICE_ID || + deviceid == HAL_HW_PCI_002B_DEVICE_ID ){ + padapter->HardwareType = HARDWARE_TYPE_RTL8192DE; + DBG_871X("Adapter(8192DE) is found - VendorID/DeviceID/RID=%X/%X/%X\n", venderid, deviceid, revisionid); + } + else if (deviceid == HAL_HW_PCI_8188EE_DEVICE_ID){ + padapter->HardwareType = HARDWARE_TYPE_RTL8188EE; + padapter->chip_type = RTL8188E; + DBG_871X("Adapter(8188EE) is found - VendorID/DeviceID/RID=%X/%X/%X\n", venderid, deviceid, revisionid); + } + + else + { + DBG_871X("Err: Unknown device - vendorid/deviceid=%x/%x\n", venderid, deviceid); + //padapter->HardwareType = HAL_DEFAULT_HARDWARE_TYPE; + } + + + padapter->chip_type = NULL_CHIP_TYPE; + + //TODO: +#ifdef CONFIG_RTL8192C + padapter->chip_type = RTL8188C_8192C; + padapter->HardwareType = HARDWARE_TYPE_RTL8192CE; +#endif +#ifdef CONFIG_RTL8192D + pdvobjpriv->InterfaceNumber = revisionid; + + padapter->chip_type = RTL8192D; + padapter->HardwareType = HARDWARE_TYPE_RTL8192DE; +#endif + +} + +static void pci_intf_start(_adapter *padapter) +{ + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("+pci_intf_start\n")); + DBG_871X("+pci_intf_start\n"); + + //Enable hw interrupt + rtw_hal_enable_interrupt(padapter); + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("-pci_intf_start\n")); + DBG_871X("-pci_intf_start\n"); +} + +static void pci_intf_stop(_adapter *padapter) +{ + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("+pci_intf_stop\n")); + + //Disable hw interrupt + if(padapter->bSurpriseRemoved == _FALSE) + { + //device still exists, so driver can do i/o operation + rtw_hal_disable_interrupt(padapter); + tasklet_kill(&(padapter->recvpriv.recv_tasklet)); + tasklet_kill(&(padapter->recvpriv.irq_prepare_beacon_tasklet)); + tasklet_kill(&(padapter->xmitpriv.xmit_tasklet)); + +#ifdef CONFIG_CONCURRENT_MODE + /* This function only be called at driver removing. disable buddy_adapter too + don't disable interrupt of buddy_adapter because it is same as primary. + */ + if (padapter->pbuddy_adapter){ + tasklet_kill(&(padapter->pbuddy_adapter->recvpriv.recv_tasklet)); + tasklet_kill(&(padapter->pbuddy_adapter->recvpriv.irq_prepare_beacon_tasklet)); + tasklet_kill(&(padapter->pbuddy_adapter->xmitpriv.xmit_tasklet)); + } +#endif + + rtw_hal_set_hwreg(padapter, HW_VAR_PCIE_STOP_TX_DMA, 0); + + rtw_hal_irp_reset(padapter); + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("pci_intf_stop: SurpriseRemoved==_FALSE\n")); + } + else + { + // Clear irq_enabled to prevent handle interrupt function. + adapter_to_dvobj(padapter)->irq_enabled = 0; + } + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("-pci_intf_stop\n")); + +} + + +void rtw_dev_unload(_adapter *padapter) +{ + struct net_device *pnetdev= (struct net_device*)padapter->pnetdev; + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("+rtw_dev_unload\n")); + + if(padapter->bup == _TRUE) + { + DBG_871X("+rtw_dev_unload\n"); + + padapter->bDriverStopped = _TRUE; + #ifdef CONFIG_XMIT_ACK + if (padapter->xmitpriv.ack_tx) + rtw_ack_tx_done(&padapter->xmitpriv, RTW_SCTX_DONE_DRV_STOP); + #endif + + //s3. + if(padapter->intf_stop) + { + padapter->intf_stop(padapter); + } + + //s4. + rtw_stop_drv_threads(padapter); + + + //s5. + if(padapter->bSurpriseRemoved == _FALSE) + { + DBG_871X("r871x_dev_unload()->rtl871x_hal_deinit()\n"); + rtw_hal_deinit(padapter); + + padapter->bSurpriseRemoved = _TRUE; + } + + padapter->bup = _FALSE; + + } + else + { + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("r871x_dev_unload():padapter->bup == _FALSE\n" )); + } + + DBG_871X("-rtw_dev_unload\n"); + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("-rtw_dev_unload\n")); + +} + +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_cbw40_enable = 0; + rtw_ampdu_enable = 0; + } + + } +#endif +} + +#ifdef CONFIG_PM +static int rtw_suspend(struct pci_dev *pdev, pm_message_t state) +{ + _func_enter_; + + + _func_exit_; + return 0; +} + +static int rtw_resume(struct pci_dev *pdev) +{ + _func_enter_; + + + _func_exit_; + + return 0; +} +#endif + +_adapter *rtw_pci_if1_init(struct dvobj_priv * dvobj, struct pci_dev *pdev, const struct pci_device_id *pdid) +{ + _adapter *padapter = NULL; + struct net_device *pnetdev = NULL; + int status = _FAIL; + + if ((padapter = (_adapter *)rtw_zvmalloc(sizeof(*padapter))) == NULL) { + goto exit; + } + padapter->dvobj = dvobj; + dvobj->if1 = padapter; + + padapter->bDriverStopped=_TRUE; + + dvobj->padapters[dvobj->iface_nums++] = padapter; + padapter->iface_id = IFACE_ID0; + +#if defined(CONFIG_CONCURRENT_MODE) || defined(CONFIG_DUALMAC_CONCURRENT) + //set adapter_type/iface type for primary padapter + padapter->isprimary = _TRUE; + padapter->adapter_type = PRIMARY_ADAPTER; + #ifndef CONFIG_HWPORT_SWAP + padapter->iface_type = IFACE_PORT0; + #else + padapter->iface_type = IFACE_PORT1; + #endif +#endif + + #ifndef RTW_DVOBJ_CHIP_HW_TYPE + //step 1-1., decide the chip_type via vid/pid + padapter->interface_type = RTW_PCIE; + decide_chip_type_by_pci_device_id(padapter, pdev); + #endif + + if((pnetdev = rtw_init_netdev(padapter)) == NULL) { + goto free_adapter; + } + + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + SET_NETDEV_DEV(pnetdev, dvobj_to_dev(dvobj)); + #endif + if (dvobj->bdma64) + pnetdev->features |= NETIF_F_HIGHDMA; + pnetdev->irq = pdev->irq; + + padapter = rtw_netdev_priv(pnetdev); + +#ifdef CONFIG_IOCTL_CFG80211 + if(rtw_wdev_alloc(padapter, dvobj_to_dev(dvobj)) != 0) { + goto free_adapter; + } +#endif //CONFIG_IOCTL_CFG80211 + + + //step 2. hook HalFunc, allocate HalData + hal_set_hal_ops(padapter); + + + //step 3. + padapter->intf_start=&pci_intf_start; + padapter->intf_stop=&pci_intf_stop; + + + //.2 + rtw_init_io_priv(padapter, pci_set_intf_ops); + + //.3 + rtw_hal_read_chip_version(padapter); + + //.4 + rtw_hal_chip_configure(padapter); + + + //step 4. read efuse/eeprom data and get mac_addr + rtw_hal_read_chip_info(padapter); + + //step 5. + if(rtw_init_drv_sw(padapter) ==_FAIL) { + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("Initialize driver software resource Failed!\n")); + goto free_hal_data; + } + + if(rtw_hal_inirp_init(padapter) ==_FAIL) { + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("Initialize PCI desc ring Failed!\n")); + goto free_hal_data; + } + rtw_macaddr_cfg(padapter->eeprompriv.mac_addr); + rtw_init_wifidirect_addrs(padapter, padapter->eeprompriv.mac_addr, padapter->eeprompriv.mac_addr); + + rtw_hal_disable_interrupt(padapter); + + //step 6. Init pci related configuration + rtw_pci_initialize_adapter_common(padapter); + + DBG_871X("bDriverStopped:%d, bSurpriseRemoved:%d, bup:%d, hw_init_completed:%d\n" + ,padapter->bDriverStopped + ,padapter->bSurpriseRemoved + ,padapter->bup + ,padapter->hw_init_completed + ); + + status = _SUCCESS; + +free_hal_data: + if(status != _SUCCESS && padapter->HalData) + rtw_mfree(padapter->HalData, sizeof(*(padapter->HalData))); + +free_wdev: + if(status != _SUCCESS) { + #ifdef CONFIG_IOCTL_CFG80211 + rtw_wdev_unregister(padapter->rtw_wdev); + rtw_wdev_free(padapter->rtw_wdev); + #endif + } + +free_adapter: + if (status != _SUCCESS) { + if (pnetdev) + rtw_free_netdev(pnetdev); + else if (padapter) + rtw_vmfree((u8*)padapter, sizeof(*padapter)); + padapter = NULL; + } +exit: + return padapter; +} + +static void rtw_pci_if1_deinit(_adapter *if1) +{ + struct net_device *pnetdev = if1->pnetdev; + struct mlme_priv *pmlmepriv= &if1->mlmepriv; + + // padapter->intf_stop(padapter); + + if(check_fwstate(pmlmepriv, _FW_LINKED)) + rtw_disassoc_cmd(if1, 0, _FALSE); + +#ifdef CONFIG_AP_MODE + free_mlme_ap_info(if1); + #ifdef CONFIG_HOSTAPD_MLME + hostapd_mode_unload(if1); + #endif +#endif + +/* + if (if1->DriverState != DRIVER_DISAPPEAR) { + if(pnetdev) { + unregister_netdev(pnetdev); //will call netdev_close() + rtw_proc_remove_one(pnetdev); + } + } +*/ + + rtw_cancel_all_timer(if1); +#ifdef CONFIG_WOWLAN + adapter_to_pwrctl(if1)->wowlan_mode=_FALSE; +#endif //CONFIG_WOWLAN + rtw_dev_unload(if1); + + DBG_871X("%s, hw_init_completed=%d\n", __func__, if1->hw_init_completed); + + //s6. + rtw_handle_dualmac(if1, 0); + +#ifdef CONFIG_IOCTL_CFG80211 + if(if1->rtw_wdev) + { + //rtw_wdev_unregister(if1->rtw_wdev); + rtw_wdev_free(if1->rtw_wdev); + } +#endif //CONFIG_IOCTL_CFG80211 + + rtw_hal_inirp_deinit(if1); + rtw_free_drv_sw(if1); + + if(pnetdev) + rtw_free_netdev(pnetdev); + +#ifdef CONFIG_PLATFORM_RTD2880B + DBG_871X("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; + _adapter *if1 = NULL, *if2 = NULL; + struct dvobj_priv *dvobj; + + RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+rtw_drv_init\n")); + //DBG_871X("+rtw_drv_init\n"); + + //step 0. + disable_ht_for_spec_devid(pdid); + + /* Initialize dvobj_priv */ + if ((dvobj = pci_dvobj_init(pdev)) == NULL) { + RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("initialize device object priv Failed!\n")); + goto exit; + } + + /* Initialize if1 */ + if ((if1 = rtw_pci_if1_init(dvobj, pdev, pdid)) == NULL) { + DBG_871X("rtw_pci_if1_init Failed!\n"); + goto free_dvobj; + } + + /* Initialize if2 */ +#ifdef CONFIG_CONCURRENT_MODE + if((if2 = rtw_drv_if2_init(if1, pci_set_intf_ops)) == NULL) { + goto free_if1; + } +#endif + + //dev_alloc_name && register_netdev + if((status = rtw_drv_register_netdev(if1)) != _SUCCESS) { + goto free_if2; + } + +#ifdef CONFIG_HOSTAPD_MLME + hostapd_mode_init(if1); +#endif + +#ifdef CONFIG_PLATFORM_RTD2880B + DBG_871X("wlan link up\n"); + rtd2885_wlan_netlink_sendMsg("linkup", "8712"); +#endif + +#ifdef RTK_DMP_PLATFORM + rtw_proc_init_one(if1->pnetdev); +#endif + + /* alloc irq */ + if (pci_alloc_irq(dvobj) != _SUCCESS) + goto free_if2; + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("-871x_drv - drv_init, success!\n")); + //DBG_871X("-871x_drv - drv_init, success!\n"); + + status = _SUCCESS; + +free_if2: + if(status != _SUCCESS && if2) { + #ifdef CONFIG_CONCURRENT_MODE + rtw_drv_if2_stop(if2); + rtw_drv_if2_free(if2); + #endif + } +free_if1: + if (status != _SUCCESS && if1) { + rtw_pci_if1_deinit(if1); + } +free_dvobj: + if (status != _SUCCESS) + pci_dvobj_deinit(pdev); +exit: + return status == _SUCCESS?0:-ENODEV; +} + +extern void rtw_unregister_netdevs(struct dvobj_priv *dvobj); +/* + * 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 = pdvobjpriv->if1; + struct net_device *pnetdev = padapter->pnetdev; + +_func_exit_; + + DBG_871X("+rtw_dev_remove\n"); + + pdvobjpriv->processing_dev_remove = _TRUE; + rtw_unregister_netdevs(pdvobjpriv); + + if (unlikely(!padapter)) { + return; + } + + #if 0 +#ifdef RTK_DMP_PLATFORM + padapter->bSurpriseRemoved = _FALSE; // always trate as device exists + // this will let the driver to disable it's interrupt +#else + if(pci_drvpriv.drv_registered == _TRUE) + { + //DBG_871X("r871xu_dev_remove():padapter->bSurpriseRemoved == _TRUE\n"); + padapter->bSurpriseRemoved = _TRUE; + } + /*else + { + //DBG_871X("r871xu_dev_remove():module removed\n"); + padapter->hw_init_completed = _FALSE; + }*/ +#endif + #endif + +#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_ANDROID_POWER) + rtw_unregister_early_suspend(dvobj_to_pwrctl(pdvobjpriv)); +#endif + + rtw_pm_set_ips(padapter, IPS_NONE); + rtw_pm_set_lps(padapter, PS_MODE_ACTIVE); + + LeaveAllPowerSaveMode(padapter); + +#ifdef CONFIG_CONCURRENT_MODE + rtw_drv_if2_stop(pdvobjpriv->if2); +#endif + + rtw_pci_if1_deinit(padapter); + +#ifdef CONFIG_CONCURRENT_MODE + rtw_drv_if2_free(pdvobjpriv->if2); +#endif + + pci_dvobj_deinit(pdev); + + DBG_871X("-r871xu_dev_remove, done\n"); + +_func_exit_; + return; +} + + +static int __init rtw_drv_entry(void) +{ + int ret = 0; + + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("+rtw_drv_entry\n")); + DBG_871X("rtw driver version=%s\n", DRIVERVERSION); + DBG_871X("Build at: %s %s\n", __DATE__, __TIME__); + pci_drvpriv.drv_registered = _TRUE; + + rtw_suspend_lock_init(); + + ret = pci_register_driver(&pci_drvpriv.rtw_pci_drv); + if (ret) { + RT_TRACE(_module_hci_intfs_c_, _drv_err_, (": No device found\n")); + } + + return ret; +} + +static void __exit rtw_drv_halt(void) +{ + RT_TRACE(_module_hci_intfs_c_,_drv_err_,("+rtw_drv_halt\n")); + DBG_871X("+rtw_drv_halt\n"); + + pci_drvpriv.drv_registered = _FALSE; + + pci_unregister_driver(&pci_drvpriv.rtw_pci_drv); + + rtw_suspend_lock_uninit(); + + DBG_871X("-rtw_drv_halt\n"); + + rtw_mstat_dump(); +} + + +module_init(rtw_drv_entry); +module_exit(rtw_drv_halt); + |