| /* |
| ************************************************************************* |
| * Ralink Tech Inc. |
| * 5F., No.36, Taiyuan St., Jhubei City, |
| * Hsinchu County 302, |
| * Taiwan, R.O.C. |
| * |
| * (c) Copyright 2002-2007, Ralink Technology, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify * |
| * it under the terms of the GNU General Public License as published by * |
| * the Free Software Foundation; either version 2 of the License, or * |
| * (at your option) any later version. * |
| * * |
| * 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., * |
| * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
| * * |
| ************************************************************************* |
| |
| Module Name: |
| pci_main_dev.c |
| |
| Abstract: |
| Create and register network interface for PCI based chipsets in Linux platform. |
| |
| Revision History: |
| Who When What |
| -------- ---------- ---------------------------------------------- |
| */ |
| |
| #include "rt_config.h" |
| #include <linux/pci.h> |
| #include <linux/slab.h> |
| |
| /* Following information will be show when you run 'modinfo' */ |
| /* *** If you have a solution for the bug in current version of driver, please mail to me. */ |
| /* Otherwise post to forum in ralinktech's web site(www.ralinktech.com) and let all users help you. *** */ |
| MODULE_AUTHOR("Jett Chen <jett_chen@ralinktech.com>"); |
| MODULE_DESCRIPTION("RT2860/RT3090 Wireless Lan Linux Driver"); |
| MODULE_LICENSE("GPL"); |
| MODULE_ALIAS("rt3090sta"); |
| |
| /* */ |
| /* Function declarations */ |
| /* */ |
| extern int rt28xx_close(IN struct net_device *net_dev); |
| extern int rt28xx_open(struct net_device *net_dev); |
| |
| static void __devexit rt2860_remove_one(struct pci_dev *pci_dev); |
| static int __devinit rt2860_probe(struct pci_dev *pci_dev, |
| const struct pci_device_id *ent); |
| static void __exit rt2860_cleanup_module(void); |
| static int __init rt2860_init_module(void); |
| |
| static void RTMPInitPCIeDevice(IN struct pci_dev *pci_dev, |
| struct rt_rtmp_adapter *pAd); |
| |
| #ifdef CONFIG_PM |
| static int rt2860_suspend(struct pci_dev *pci_dev, pm_message_t state); |
| static int rt2860_resume(struct pci_dev *pci_dev); |
| #endif /* CONFIG_PM // */ |
| |
| /* */ |
| /* Ralink PCI device table, include all supported chipsets */ |
| /* */ |
| static struct pci_device_id rt2860_pci_tbl[] __devinitdata = { |
| #ifdef RT2860 |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2860_PCI_DEVICE_ID)}, /*RT28602.4G */ |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2860_PCIe_DEVICE_ID)}, |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2760_PCI_DEVICE_ID)}, |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2790_PCIe_DEVICE_ID)}, |
| {PCI_DEVICE(VEN_AWT_PCI_VENDOR_ID, VEN_AWT_PCIe_DEVICE_ID)}, |
| {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7708)}, |
| {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7728)}, |
| {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7758)}, |
| {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7727)}, |
| {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7738)}, |
| {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7748)}, |
| {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7768)}, |
| #endif |
| #ifdef RT3090 |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC3090_PCIe_DEVICE_ID)}, |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC3091_PCIe_DEVICE_ID)}, |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC3092_PCIe_DEVICE_ID)}, |
| #endif /* RT3090 // */ |
| #ifdef RT3390 |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC3390_PCIe_DEVICE_ID)}, |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC3391_PCIe_DEVICE_ID)}, |
| {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC3392_PCIe_DEVICE_ID)}, |
| #endif /* RT3390 // */ |
| {0,} /* terminate list */ |
| }; |
| |
| MODULE_DEVICE_TABLE(pci, rt2860_pci_tbl); |
| #ifdef MODULE_VERSION |
| MODULE_VERSION(STA_DRIVER_VERSION); |
| #endif |
| |
| /* */ |
| /* Our PCI driver structure */ |
| /* */ |
| static struct pci_driver rt2860_driver = { |
| name: "rt2860", |
| id_table : rt2860_pci_tbl, |
| probe : rt2860_probe, |
| remove : __devexit_p(rt2860_remove_one), |
| #ifdef CONFIG_PM |
| suspend : rt2860_suspend, |
| resume : rt2860_resume, |
| #endif |
| }; |
| |
| /*************************************************************************** |
| * |
| * PCI device initialization related procedures. |
| * |
| ***************************************************************************/ |
| #ifdef CONFIG_PM |
| |
| void RT2860RejectPendingPackets(struct rt_rtmp_adapter *pAd) |
| { |
| /* clear PS packets */ |
| /* clear TxSw packets */ |
| } |
| |
| static int rt2860_suspend(struct pci_dev *pci_dev, pm_message_t state) |
| { |
| struct net_device *net_dev = pci_get_drvdata(pci_dev); |
| struct rt_rtmp_adapter *pAd = (struct rt_rtmp_adapter *)NULL; |
| int retval = 0; |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_suspend()\n")); |
| |
| if (net_dev == NULL) { |
| DBGPRINT(RT_DEBUG_ERROR, ("net_dev == NULL!\n")); |
| } else { |
| GET_PAD_FROM_NET_DEV(pAd, net_dev); |
| |
| /* we can not use IFF_UP because ra0 down but ra1 up */ |
| /* and 1 suspend/resume function for 1 module, not for each interface */ |
| /* so Linux will call suspend/resume function once */ |
| if (VIRTUAL_IF_NUM(pAd) > 0) { |
| /* avoid users do suspend after interface is down */ |
| |
| /* stop interface */ |
| netif_carrier_off(net_dev); |
| netif_stop_queue(net_dev); |
| |
| /* mark device as removed from system and therefore no longer available */ |
| netif_device_detach(net_dev); |
| |
| /* mark halt flag */ |
| RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS); |
| RTMP_SET_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF); |
| |
| /* take down the device */ |
| rt28xx_close((struct net_device *)net_dev); |
| |
| RT_MOD_DEC_USE_COUNT(); |
| } |
| } |
| |
| /* reference to http://vovo2000.com/type-lab/linux/kernel-api/linux-kernel-api.html */ |
| /* enable device to generate PME# when suspended */ |
| /* pci_choose_state(): Choose the power state of a PCI device to be suspended */ |
| retval = pci_enable_wake(pci_dev, pci_choose_state(pci_dev, state), 1); |
| /* save the PCI configuration space of a device before suspending */ |
| pci_save_state(pci_dev); |
| /* disable PCI device after use */ |
| pci_disable_device(pci_dev); |
| |
| retval = pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_suspend()\n")); |
| return retval; |
| } |
| |
| static int rt2860_resume(struct pci_dev *pci_dev) |
| { |
| struct net_device *net_dev = pci_get_drvdata(pci_dev); |
| struct rt_rtmp_adapter *pAd = (struct rt_rtmp_adapter *)NULL; |
| int retval; |
| |
| /* set the power state of a PCI device */ |
| /* PCI has 4 power states, DO (normal) ~ D3(less power) */ |
| /* in include/linux/pci.h, you can find that */ |
| /* #define PCI_D0 ((pci_power_t __force) 0) */ |
| /* #define PCI_D1 ((pci_power_t __force) 1) */ |
| /* #define PCI_D2 ((pci_power_t __force) 2) */ |
| /* #define PCI_D3hot ((pci_power_t __force) 3) */ |
| /* #define PCI_D3cold ((pci_power_t __force) 4) */ |
| /* #define PCI_UNKNOWN ((pci_power_t __force) 5) */ |
| /* #define PCI_POWER_ERROR ((pci_power_t __force) -1) */ |
| retval = pci_set_power_state(pci_dev, PCI_D0); |
| |
| /* restore the saved state of a PCI device */ |
| pci_restore_state(pci_dev); |
| |
| /* initialize device before it's used by a driver */ |
| if (pci_enable_device(pci_dev)) { |
| printk("pci enable fail!\n"); |
| return 0; |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_resume()\n")); |
| |
| if (net_dev == NULL) |
| DBGPRINT(RT_DEBUG_ERROR, ("net_dev == NULL!\n")); |
| else |
| GET_PAD_FROM_NET_DEV(pAd, net_dev); |
| |
| if (pAd != NULL) { |
| /* we can not use IFF_UP because ra0 down but ra1 up */ |
| /* and 1 suspend/resume function for 1 module, not for each interface */ |
| /* so Linux will call suspend/resume function once */ |
| if (VIRTUAL_IF_NUM(pAd) > 0) { |
| /* mark device as attached from system and restart if needed */ |
| netif_device_attach(net_dev); |
| |
| if (rt28xx_open((struct net_device *)net_dev) != 0) { |
| /* open fail */ |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("<=== rt2860_resume()\n")); |
| return 0; |
| } |
| /* increase MODULE use count */ |
| RT_MOD_INC_USE_COUNT(); |
| |
| RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_HALT_IN_PROGRESS); |
| RTMP_CLEAR_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF); |
| |
| netif_start_queue(net_dev); |
| netif_carrier_on(net_dev); |
| netif_wake_queue(net_dev); |
| } |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_resume()\n")); |
| return 0; |
| } |
| #endif /* CONFIG_PM // */ |
| |
| static int __init rt2860_init_module(void) |
| { |
| return pci_register_driver(&rt2860_driver); |
| } |
| |
| /* */ |
| /* Driver module unload function */ |
| /* */ |
| static void __exit rt2860_cleanup_module(void) |
| { |
| pci_unregister_driver(&rt2860_driver); |
| } |
| |
| module_init(rt2860_init_module); |
| module_exit(rt2860_cleanup_module); |
| |
| /* */ |
| /* PCI device probe & initialization function */ |
| /* */ |
| static int __devinit rt2860_probe(IN struct pci_dev *pci_dev, |
| IN const struct pci_device_id *pci_id) |
| { |
| struct rt_rtmp_adapter *pAd = (struct rt_rtmp_adapter *)NULL; |
| struct net_device *net_dev; |
| void *handle; |
| char *print_name; |
| unsigned long csr_addr; |
| int rv = 0; |
| struct rt_rtmp_os_netdev_op_hook netDevHook; |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_probe\n")); |
| |
| /*PCIDevInit============================================== */ |
| /* wake up and enable device */ |
| rv = pci_enable_device(pci_dev); |
| |
| if (rv != 0) { |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("Enable PCI device failed, errno=%d!\n", rv)); |
| return rv; |
| } |
| |
| print_name = (char *)pci_name(pci_dev); |
| |
| rv = pci_request_regions(pci_dev, print_name); |
| |
| if (rv != 0) { |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("Request PCI resource failed, errno=%d!\n", rv)); |
| goto err_out; |
| } |
| /* map physical address to virtual address for accessing register */ |
| csr_addr = |
| (unsigned long)ioremap(pci_resource_start(pci_dev, 0), |
| pci_resource_len(pci_dev, 0)); |
| if (!csr_addr) { |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("ioremap failed for device %s, region 0x%lX @ 0x%lX\n", |
| print_name, (unsigned long)pci_resource_len(pci_dev, 0), |
| (unsigned long)pci_resource_start(pci_dev, 0))); |
| goto err_out_free_res; |
| } else { |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("%s: at 0x%lx, VA 0x%lx, IRQ %d. \n", print_name, |
| (unsigned long)pci_resource_start(pci_dev, 0), |
| (unsigned long)csr_addr, pci_dev->irq)); |
| } |
| |
| /* Set DMA master */ |
| pci_set_master(pci_dev); |
| |
| /*RtmpDevInit============================================== */ |
| /* Allocate struct rt_rtmp_adapter adapter structure */ |
| handle = kmalloc(sizeof(struct os_cookie), GFP_KERNEL); |
| if (handle == NULL) { |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("%s(): Allocate memory for os handle failed!\n", |
| __func__)); |
| goto err_out_iounmap; |
| } |
| |
| ((struct os_cookie *)handle)->pci_dev = pci_dev; |
| |
| rv = RTMPAllocAdapterBlock(handle, &pAd); /*shiang: we may need the pci_dev for allocate structure of "struct rt_rtmp_adapter" */ |
| if (rv != NDIS_STATUS_SUCCESS) |
| goto err_out_iounmap; |
| /* Here are the struct rt_rtmp_adapter structure with pci-bus specific parameters. */ |
| pAd->CSRBaseAddress = (u8 *)csr_addr; |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("pAd->CSRBaseAddress =0x%lx, csr_addr=0x%lx!\n", |
| (unsigned long)pAd->CSRBaseAddress, csr_addr)); |
| RtmpRaDevCtrlInit(pAd, RTMP_DEV_INF_PCI); |
| |
| /*NetDevInit============================================== */ |
| net_dev = RtmpPhyNetDevInit(pAd, &netDevHook); |
| if (net_dev == NULL) |
| goto err_out_free_radev; |
| |
| /* Here are the net_device structure with pci-bus specific parameters. */ |
| net_dev->irq = pci_dev->irq; /* Interrupt IRQ number */ |
| net_dev->base_addr = csr_addr; /* Save CSR virtual address and irq to device structure */ |
| pci_set_drvdata(pci_dev, net_dev); /* Set driver data */ |
| |
| /* for supporting Network Manager */ |
| /* Set the sysfs physical device reference for the network logical device |
| * if set prior to registration will cause a symlink during initialization. |
| */ |
| SET_NETDEV_DEV(net_dev, &(pci_dev->dev)); |
| |
| /*All done, it's time to register the net device to linux kernel. */ |
| /* Register this device */ |
| rv = RtmpOSNetDevAttach(net_dev, &netDevHook); |
| if (rv) |
| goto err_out_free_netdev; |
| |
| pAd->StaCfg.OriDevType = net_dev->type; |
| RTMPInitPCIeDevice(pci_dev, pAd); |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_probe\n")); |
| |
| return 0; /* probe ok */ |
| |
| /* --------------------------- ERROR HANDLE --------------------------- */ |
| err_out_free_netdev: |
| RtmpOSNetDevFree(net_dev); |
| |
| err_out_free_radev: |
| /* free struct rt_rtmp_adapter strcuture and os_cookie */ |
| RTMPFreeAdapter(pAd); |
| |
| err_out_iounmap: |
| iounmap((void *)(csr_addr)); |
| release_mem_region(pci_resource_start(pci_dev, 0), |
| pci_resource_len(pci_dev, 0)); |
| |
| err_out_free_res: |
| pci_release_regions(pci_dev); |
| |
| err_out: |
| pci_disable_device(pci_dev); |
| |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("<=== rt2860_probe failed with rv = %d!\n", rv)); |
| |
| return -ENODEV; /* probe fail */ |
| } |
| |
| static void __devexit rt2860_remove_one(IN struct pci_dev *pci_dev) |
| { |
| struct net_device *net_dev = pci_get_drvdata(pci_dev); |
| struct rt_rtmp_adapter *pAd = NULL; |
| unsigned long csr_addr = net_dev->base_addr; /* pAd->CSRBaseAddress; */ |
| |
| GET_PAD_FROM_NET_DEV(pAd, net_dev); |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_remove_one\n")); |
| |
| if (pAd != NULL) { |
| /* Unregister/Free all allocated net_device. */ |
| RtmpPhyNetDevExit(pAd, net_dev); |
| |
| /* Unmap CSR base address */ |
| iounmap((char *)(csr_addr)); |
| |
| /* release memory region */ |
| release_mem_region(pci_resource_start(pci_dev, 0), |
| pci_resource_len(pci_dev, 0)); |
| |
| /* Free struct rt_rtmp_adapter related structures. */ |
| RtmpRaDevCtrlExit(pAd); |
| |
| } else { |
| /* Unregister network device */ |
| RtmpOSNetDevDetach(net_dev); |
| |
| /* Unmap CSR base address */ |
| iounmap((char *)(net_dev->base_addr)); |
| |
| /* release memory region */ |
| release_mem_region(pci_resource_start(pci_dev, 0), |
| pci_resource_len(pci_dev, 0)); |
| } |
| |
| /* Free the root net_device */ |
| RtmpOSNetDevFree(net_dev); |
| |
| } |
| |
| /* |
| ======================================================================== |
| Routine Description: |
| Check the chipset vendor/product ID. |
| |
| Arguments: |
| _dev_p Point to the PCI or USB device |
| |
| Return Value: |
| TRUE Check ok |
| FALSE Check fail |
| |
| Note: |
| ======================================================================== |
| */ |
| BOOLEAN RT28XXChipsetCheck(IN void *_dev_p) |
| { |
| /* always TRUE */ |
| return TRUE; |
| } |
| |
| /*************************************************************************** |
| * |
| * PCIe device initialization related procedures. |
| * |
| ***************************************************************************/ |
| static void RTMPInitPCIeDevice(struct pci_dev *pci_dev, struct rt_rtmp_adapter *pAd) |
| { |
| u16 device_id; |
| struct os_cookie *pObj; |
| |
| pObj = (struct os_cookie *)pAd->OS_Cookie; |
| pci_read_config_word(pci_dev, PCI_DEVICE_ID, &device_id); |
| device_id = le2cpu16(device_id); |
| pObj->DeviceID = device_id; |
| if ( |
| #ifdef RT2860 |
| (device_id == NIC2860_PCIe_DEVICE_ID) || |
| (device_id == NIC2790_PCIe_DEVICE_ID) || |
| (device_id == VEN_AWT_PCIe_DEVICE_ID) || |
| #endif |
| #ifdef RT3090 |
| (device_id == NIC3090_PCIe_DEVICE_ID) || |
| (device_id == NIC3091_PCIe_DEVICE_ID) || |
| (device_id == NIC3092_PCIe_DEVICE_ID) || |
| #endif /* RT3090 // */ |
| 0) { |
| u32 MacCsr0 = 0, Index = 0; |
| do { |
| RTMP_IO_READ32(pAd, MAC_CSR0, &MacCsr0); |
| |
| if ((MacCsr0 != 0x00) && (MacCsr0 != 0xFFFFFFFF)) |
| break; |
| |
| RTMPusecDelay(10); |
| } while (Index++ < 100); |
| |
| /* Support advanced power save after 2892/2790. */ |
| /* MAC version at offset 0x1000 is 0x2872XXXX/0x2870XXXX(PCIe, USB, SDIO). */ |
| if ((MacCsr0 & 0xffff0000) != 0x28600000) |
| OPSTATUS_SET_FLAG(pAd, fOP_STATUS_PCIE_DEVICE); |
| } |
| } |
| |
| void RTMPInitPCIeLinkCtrlValue(struct rt_rtmp_adapter *pAd) |
| { |
| int pos; |
| u16 reg16, data2, PCIePowerSaveLevel, Configuration; |
| u32 MacValue; |
| BOOLEAN bFindIntel = FALSE; |
| struct os_cookie *pObj; |
| |
| pObj = (struct os_cookie *)pAd->OS_Cookie; |
| |
| if (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_PCIE_DEVICE)) |
| return; |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("%s.===>\n", __func__)); |
| /* Init EEPROM, and save settings */ |
| if (!(IS_RT3090(pAd) || IS_RT3572(pAd) || IS_RT3390(pAd))) { |
| RT28xx_EEPROM_READ16(pAd, 0x22, PCIePowerSaveLevel); |
| pAd->PCIePowerSaveLevel = PCIePowerSaveLevel & 0xff; |
| |
| pAd->LnkCtrlBitMask = 0; |
| if ((PCIePowerSaveLevel & 0xff) == 0xff) { |
| OPSTATUS_CLEAR_FLAG(pAd, fOP_STATUS_PCIE_DEVICE); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("====> PCIePowerSaveLevel = 0x%x.\n", |
| PCIePowerSaveLevel)); |
| return; |
| } else { |
| PCIePowerSaveLevel &= 0x3; |
| RT28xx_EEPROM_READ16(pAd, 0x24, data2); |
| |
| if (! |
| (((data2 & 0xff00) == 0x9200) |
| && ((data2 & 0x80) != 0))) { |
| if (PCIePowerSaveLevel > 1) |
| PCIePowerSaveLevel = 1; |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("====> Write 0x83 = 0x%x.\n", |
| PCIePowerSaveLevel)); |
| AsicSendCommandToMcu(pAd, 0x83, 0xff, |
| (u8)PCIePowerSaveLevel, 0x00); |
| RT28xx_EEPROM_READ16(pAd, 0x22, PCIePowerSaveLevel); |
| PCIePowerSaveLevel &= 0xff; |
| PCIePowerSaveLevel = PCIePowerSaveLevel >> 6; |
| switch (PCIePowerSaveLevel) { |
| case 0: /* Only support L0 */ |
| pAd->LnkCtrlBitMask = 0; |
| break; |
| case 1: /* Only enable L0s */ |
| pAd->LnkCtrlBitMask = 1; |
| break; |
| case 2: /* enable L1, L0s */ |
| pAd->LnkCtrlBitMask = 3; |
| break; |
| case 3: /* sync with host clk and enable L1, L0s */ |
| pAd->LnkCtrlBitMask = 0x103; |
| break; |
| } |
| RT28xx_EEPROM_READ16(pAd, 0x24, data2); |
| if ((PCIePowerSaveLevel & 0xff) != 0xff) { |
| PCIePowerSaveLevel &= 0x3; |
| |
| if (! |
| (((data2 & 0xff00) == 0x9200) |
| && ((data2 & 0x80) != 0))) { |
| if (PCIePowerSaveLevel > 1) |
| PCIePowerSaveLevel = 1; |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("====> rt28xx Write 0x83 Command = 0x%x.\n", |
| PCIePowerSaveLevel)); |
| |
| AsicSendCommandToMcu(pAd, 0x83, 0xff, |
| (u8)PCIePowerSaveLevel, |
| 0x00); |
| } |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("====> LnkCtrlBitMask = 0x%x.\n", |
| pAd->LnkCtrlBitMask)); |
| } |
| } else if (IS_RT3090(pAd) || IS_RT3572(pAd) || IS_RT3390(pAd)) { |
| u8 LinkCtrlSetting = 0; |
| |
| /* Check 3090E special setting chip. */ |
| RT28xx_EEPROM_READ16(pAd, 0x24, data2); |
| if ((data2 == 0x9280) && ((pAd->MACVersion & 0xffff) == 0x0211)) { |
| pAd->b3090ESpecialChip = TRUE; |
| DBGPRINT_RAW(RT_DEBUG_ERROR, ("Special 3090E chip \n")); |
| } |
| |
| RTMP_IO_READ32(pAd, AUX_CTRL, &MacValue); |
| /*enable WAKE_PCIE function, which forces to enable PCIE clock when mpu interrupt asserting. */ |
| /*Force PCIE 125MHz CLK to toggle */ |
| MacValue |= 0x402; |
| RTMP_IO_WRITE32(pAd, AUX_CTRL, MacValue); |
| DBGPRINT_RAW(RT_DEBUG_ERROR, |
| (" AUX_CTRL = 0x%32x\n", MacValue)); |
| |
| /* for RT30xx F and after, PCIe infterface, and for power solution 3 */ |
| if ((IS_VERSION_AFTER_F(pAd)) |
| && (pAd->StaCfg.PSControl.field.rt30xxPowerMode >= 2) |
| && (pAd->StaCfg.PSControl.field.rt30xxPowerMode <= 3)) { |
| RTMP_IO_READ32(pAd, AUX_CTRL, &MacValue); |
| DBGPRINT_RAW(RT_DEBUG_ERROR, |
| (" Read AUX_CTRL = 0x%x\n", MacValue)); |
| /* turn on bit 12. */ |
| /*enable 32KHz clock mode for power saving */ |
| MacValue |= 0x1000; |
| if (MacValue != 0xffffffff) { |
| RTMP_IO_WRITE32(pAd, AUX_CTRL, MacValue); |
| DBGPRINT_RAW(RT_DEBUG_ERROR, |
| (" Write AUX_CTRL = 0x%x\n", |
| MacValue)); |
| /* 1. if use PCIePowerSetting is 2 or 3, need to program OSC_CTRL to 0x3ff11. */ |
| MacValue = 0x3ff11; |
| RTMP_IO_WRITE32(pAd, OSC_CTRL, MacValue); |
| DBGPRINT_RAW(RT_DEBUG_ERROR, |
| (" OSC_CTRL = 0x%x\n", MacValue)); |
| /* 2. Write PCI register Clk ref bit */ |
| RTMPrt3xSetPCIePowerLinkCtrl(pAd); |
| } else { |
| /* Error read Aux_Ctrl value. Force to use solution 1 */ |
| DBGPRINT(RT_DEBUG_ERROR, |
| (" Error Value in AUX_CTRL = 0x%x\n", |
| MacValue)); |
| pAd->StaCfg.PSControl.field.rt30xxPowerMode = 1; |
| DBGPRINT(RT_DEBUG_ERROR, |
| (" Force to use power solution1 \n")); |
| } |
| } |
| /* 1. read setting from inf file. */ |
| |
| PCIePowerSaveLevel = |
| (u16)pAd->StaCfg.PSControl.field.rt30xxPowerMode; |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("====> rt30xx Read PowerLevelMode = 0x%x.\n", |
| PCIePowerSaveLevel)); |
| /* 2. Check EnableNewPS. */ |
| if (pAd->StaCfg.PSControl.field.EnableNewPS == FALSE) |
| PCIePowerSaveLevel = 1; |
| |
| if (IS_VERSION_BEFORE_F(pAd) |
| && (pAd->b3090ESpecialChip == FALSE)) { |
| /* Chip Version E only allow 1, So force set 1. */ |
| PCIePowerSaveLevel &= 0x1; |
| pAd->PCIePowerSaveLevel = (u16)PCIePowerSaveLevel; |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("====> rt30xx E Write 0x83 Command = 0x%x.\n", |
| PCIePowerSaveLevel)); |
| |
| AsicSendCommandToMcu(pAd, 0x83, 0xff, |
| (u8)PCIePowerSaveLevel, 0x00); |
| } else { |
| /* Chip Version F and after only allow 1 or 2 or 3. This might be modified after new chip version come out. */ |
| if (! |
| ((PCIePowerSaveLevel == 1) |
| || (PCIePowerSaveLevel == 3))) |
| PCIePowerSaveLevel = 1; |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("====> rt30xx F Write 0x83 Command = 0x%x.\n", |
| PCIePowerSaveLevel)); |
| pAd->PCIePowerSaveLevel = (u16)PCIePowerSaveLevel; |
| /* for 3090F , we need to add high-byte arg for 0x83 command to indicate the link control setting in */ |
| /* PCI Configuration Space. Because firmware can't read PCI Configuration Space */ |
| if ((pAd->Rt3xxRalinkLinkCtrl & 0x2) |
| && (pAd->Rt3xxHostLinkCtrl & 0x2)) { |
| LinkCtrlSetting = 1; |
| } |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("====> rt30xxF LinkCtrlSetting = 0x%x.\n", |
| LinkCtrlSetting)); |
| AsicSendCommandToMcu(pAd, 0x83, 0xff, |
| (u8)PCIePowerSaveLevel, |
| LinkCtrlSetting); |
| } |
| } |
| /* Find Ralink PCIe Device's Express Capability Offset */ |
| pos = pci_find_capability(pObj->pci_dev, PCI_CAP_ID_EXP); |
| |
| if (pos != 0) { |
| /* Ralink PCIe Device's Link Control Register Offset */ |
| pAd->RLnkCtrlOffset = pos + PCI_EXP_LNKCTL; |
| pci_read_config_word(pObj->pci_dev, pAd->RLnkCtrlOffset, |
| ®16); |
| Configuration = le2cpu16(reg16); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Read (Ralink PCIe Link Control Register) offset 0x%x = 0x%x\n", |
| pAd->RLnkCtrlOffset, Configuration)); |
| pAd->RLnkCtrlConfiguration = (Configuration & 0x103); |
| Configuration &= 0xfefc; |
| Configuration |= (0x0); |
| #ifdef RT2860 |
| if ((pObj->DeviceID == NIC2860_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC2790_PCIe_DEVICE_ID)) { |
| reg16 = cpu2le16(Configuration); |
| pci_write_config_word(pObj->pci_dev, |
| pAd->RLnkCtrlOffset, reg16); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Write (Ralink PCIe Link Control Register) offset 0x%x = 0x%x\n", |
| pos + PCI_EXP_LNKCTL, Configuration)); |
| } |
| #endif /* RT2860 // */ |
| |
| RTMPFindHostPCIDev(pAd); |
| if (pObj->parent_pci_dev) { |
| u16 vendor_id; |
| |
| pci_read_config_word(pObj->parent_pci_dev, |
| PCI_VENDOR_ID, &vendor_id); |
| vendor_id = le2cpu16(vendor_id); |
| if (vendor_id == PCIBUS_INTEL_VENDOR) { |
| bFindIntel = TRUE; |
| RTMP_SET_PSFLAG(pAd, fRTMP_PS_TOGGLE_L1); |
| } |
| /* Find PCI-to-PCI Bridge Express Capability Offset */ |
| pos = |
| pci_find_capability(pObj->parent_pci_dev, |
| PCI_CAP_ID_EXP); |
| |
| if (pos != 0) { |
| BOOLEAN bChange = FALSE; |
| /* PCI-to-PCI Bridge Link Control Register Offset */ |
| pAd->HostLnkCtrlOffset = pos + PCI_EXP_LNKCTL; |
| pci_read_config_word(pObj->parent_pci_dev, |
| pAd->HostLnkCtrlOffset, |
| ®16); |
| Configuration = le2cpu16(reg16); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Read (Host PCI-to-PCI Bridge Link Control Register) offset 0x%x = 0x%x\n", |
| pAd->HostLnkCtrlOffset, |
| Configuration)); |
| pAd->HostLnkCtrlConfiguration = |
| (Configuration & 0x103); |
| Configuration &= 0xfefc; |
| Configuration |= (0x0); |
| |
| switch (pObj->DeviceID) { |
| #ifdef RT2860 |
| case NIC2860_PCIe_DEVICE_ID: |
| case NIC2790_PCIe_DEVICE_ID: |
| bChange = TRUE; |
| break; |
| #endif /* RT2860 // */ |
| #ifdef RT3090 |
| case NIC3090_PCIe_DEVICE_ID: |
| case NIC3091_PCIe_DEVICE_ID: |
| case NIC3092_PCIe_DEVICE_ID: |
| if (bFindIntel == FALSE) |
| bChange = TRUE; |
| break; |
| #endif /* RT3090 // */ |
| default: |
| break; |
| } |
| |
| if (bChange) { |
| reg16 = cpu2le16(Configuration); |
| pci_write_config_word(pObj-> |
| parent_pci_dev, |
| pAd-> |
| HostLnkCtrlOffset, |
| reg16); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Write (Host PCI-to-PCI Bridge Link Control Register) offset 0x%x = 0x%x\n", |
| pAd->HostLnkCtrlOffset, |
| Configuration)); |
| } |
| } else { |
| pAd->HostLnkCtrlOffset = 0; |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("%s: cannot find PCI-to-PCI Bridge PCI Express Capability!\n", |
| __func__)); |
| } |
| } |
| } else { |
| pAd->RLnkCtrlOffset = 0; |
| pAd->HostLnkCtrlOffset = 0; |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("%s: cannot find Ralink PCIe Device's PCI Express Capability!\n", |
| __func__)); |
| } |
| |
| if (bFindIntel == FALSE) { |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Doesn't find Intel PCI host controller. \n")); |
| /* Doesn't switch L0, L1, So set PCIePowerSaveLevel to 0xff */ |
| pAd->PCIePowerSaveLevel = 0xff; |
| if ((pAd->RLnkCtrlOffset != 0) |
| #ifdef RT3090 |
| && ((pObj->DeviceID == NIC3090_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC3091_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC3092_PCIe_DEVICE_ID)) |
| #endif /* RT3090 // */ |
| ) { |
| pci_read_config_word(pObj->pci_dev, pAd->RLnkCtrlOffset, |
| ®16); |
| Configuration = le2cpu16(reg16); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Read (Ralink 30xx PCIe Link Control Register) offset 0x%x = 0x%x\n", |
| pAd->RLnkCtrlOffset, Configuration)); |
| pAd->RLnkCtrlConfiguration = (Configuration & 0x103); |
| Configuration &= 0xfefc; |
| Configuration |= (0x0); |
| reg16 = cpu2le16(Configuration); |
| pci_write_config_word(pObj->pci_dev, |
| pAd->RLnkCtrlOffset, reg16); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Write (Ralink PCIe Link Control Register) offset 0x%x = 0x%x\n", |
| pos + PCI_EXP_LNKCTL, Configuration)); |
| } |
| } |
| } |
| |
| void RTMPFindHostPCIDev(struct rt_rtmp_adapter *pAd) |
| { |
| u16 reg16; |
| u8 reg8; |
| u32 DevFn; |
| struct pci_dev *pPci_dev; |
| struct os_cookie *pObj; |
| |
| pObj = (struct os_cookie *)pAd->OS_Cookie; |
| |
| if (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_PCIE_DEVICE)) |
| return; |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("%s.===>\n", __func__)); |
| |
| pObj->parent_pci_dev = NULL; |
| if (pObj->pci_dev->bus->parent) { |
| for (DevFn = 0; DevFn < 255; DevFn++) { |
| pPci_dev = |
| pci_get_slot(pObj->pci_dev->bus->parent, DevFn); |
| if (pPci_dev) { |
| pci_read_config_word(pPci_dev, PCI_CLASS_DEVICE, |
| ®16); |
| reg16 = le2cpu16(reg16); |
| pci_read_config_byte(pPci_dev, PCI_CB_CARD_BUS, |
| ®8); |
| if ((reg16 == PCI_CLASS_BRIDGE_PCI) |
| && (reg8 == pObj->pci_dev->bus->number)) { |
| pObj->parent_pci_dev = pPci_dev; |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| |
| Arguments: |
| Level = RESTORE_HALT : Restore PCI host and Ralink PCIe Link Control field to its default value. |
| Level = Other Value : Restore from dot11 power save or radio off status. And force PCI host Link Control fields to 0x1 |
| |
| ======================================================================== |
| */ |
| void RTMPPCIeLinkCtrlValueRestore(struct rt_rtmp_adapter *pAd, u8 Level) |
| { |
| u16 PCIePowerSaveLevel, reg16; |
| u16 Configuration; |
| struct os_cookie *pObj; |
| |
| pObj = (struct os_cookie *)pAd->OS_Cookie; |
| |
| if (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_PCIE_DEVICE)) |
| return; |
| |
| #ifdef RT2860 |
| if (!((pObj->DeviceID == NIC2860_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC2790_PCIe_DEVICE_ID))) |
| return; |
| #endif /* RT2860 // */ |
| /* Check PSControl Configuration */ |
| if (pAd->StaCfg.PSControl.field.EnableNewPS == FALSE) |
| return; |
| |
| /*3090 will not execute the following codes. */ |
| /* Check interface : If not PCIe interface, return. */ |
| |
| #ifdef RT3090 |
| if ((pObj->DeviceID == NIC3090_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC3091_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC3092_PCIe_DEVICE_ID)) |
| return; |
| #endif /* RT3090 // */ |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("%s.===>\n", __func__)); |
| PCIePowerSaveLevel = pAd->PCIePowerSaveLevel; |
| if ((PCIePowerSaveLevel & 0xff) == 0xff) { |
| DBGPRINT(RT_DEBUG_TRACE, ("return \n")); |
| return; |
| } |
| |
| if (pObj->parent_pci_dev && (pAd->HostLnkCtrlOffset != 0)) { |
| PCI_REG_READ_WORD(pObj->parent_pci_dev, pAd->HostLnkCtrlOffset, |
| Configuration); |
| if ((Configuration != 0) && (Configuration != 0xFFFF)) { |
| Configuration &= 0xfefc; |
| /* If call from interface down, restore to orginial setting. */ |
| if (Level == RESTORE_CLOSE) |
| Configuration |= pAd->HostLnkCtrlConfiguration; |
| else |
| Configuration |= 0x0; |
| PCI_REG_WIRTE_WORD(pObj->parent_pci_dev, |
| pAd->HostLnkCtrlOffset, |
| Configuration); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Restore PCI host : offset 0x%x = 0x%x\n", |
| pAd->HostLnkCtrlOffset, Configuration)); |
| } else |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("Restore PCI host : PCI_REG_READ_WORD failed (Configuration = 0x%x)\n", |
| Configuration)); |
| } |
| |
| if (pObj->pci_dev && (pAd->RLnkCtrlOffset != 0)) { |
| PCI_REG_READ_WORD(pObj->pci_dev, pAd->RLnkCtrlOffset, |
| Configuration); |
| if ((Configuration != 0) && (Configuration != 0xFFFF)) { |
| Configuration &= 0xfefc; |
| /* If call from interface down, restore to orginial setting. */ |
| if (Level == RESTORE_CLOSE) |
| Configuration |= pAd->RLnkCtrlConfiguration; |
| else |
| Configuration |= 0x0; |
| PCI_REG_WIRTE_WORD(pObj->pci_dev, pAd->RLnkCtrlOffset, |
| Configuration); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Restore Ralink : offset 0x%x = 0x%x\n", |
| pAd->RLnkCtrlOffset, Configuration)); |
| } else |
| DBGPRINT(RT_DEBUG_ERROR, |
| ("Restore Ralink : PCI_REG_READ_WORD failed (Configuration = 0x%x)\n", |
| Configuration)); |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("%s <===\n", __func__)); |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| |
| Arguments: |
| Max : limit Host PCI and Ralink PCIe device's LINK CONTROL field's value. |
| Because now frequently set our device to mode 1 or mode 3 will cause problem. |
| |
| ======================================================================== |
| */ |
| void RTMPPCIeLinkCtrlSetting(struct rt_rtmp_adapter *pAd, u16 Max) |
| { |
| u16 PCIePowerSaveLevel, reg16; |
| u16 Configuration; |
| struct os_cookie *pObj; |
| |
| pObj = (struct os_cookie *)pAd->OS_Cookie; |
| |
| if (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_PCIE_DEVICE)) |
| return; |
| |
| #ifdef RT2860 |
| if (!((pObj->DeviceID == NIC2860_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC2790_PCIe_DEVICE_ID))) |
| return; |
| #endif /* RT2860 // */ |
| /* Check PSControl Configuration */ |
| if (pAd->StaCfg.PSControl.field.EnableNewPS == FALSE) |
| return; |
| |
| /* Check interface : If not PCIe interface, return. */ |
| /*Block 3090 to enter the following function */ |
| |
| #ifdef RT3090 |
| if ((pObj->DeviceID == NIC3090_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC3091_PCIe_DEVICE_ID) |
| || (pObj->DeviceID == NIC3092_PCIe_DEVICE_ID)) |
| return; |
| #endif /* RT3090 // */ |
| if (!RTMP_TEST_PSFLAG(pAd, fRTMP_PS_CAN_GO_SLEEP)) { |
| DBGPRINT(RT_DEBUG_INFO, |
| ("RTMPPCIePowerLinkCtrl return on fRTMP_PS_CAN_GO_SLEEP flag\n")); |
| return; |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("%s===>\n", __func__)); |
| PCIePowerSaveLevel = pAd->PCIePowerSaveLevel; |
| if ((PCIePowerSaveLevel & 0xff) == 0xff) { |
| DBGPRINT(RT_DEBUG_TRACE, ("return \n")); |
| return; |
| } |
| PCIePowerSaveLevel = PCIePowerSaveLevel >> 6; |
| |
| /* Skip non-exist deice right away */ |
| if (pObj->parent_pci_dev && (pAd->HostLnkCtrlOffset != 0)) { |
| PCI_REG_READ_WORD(pObj->parent_pci_dev, pAd->HostLnkCtrlOffset, |
| Configuration); |
| switch (PCIePowerSaveLevel) { |
| case 0: |
| /* Set b0 and b1 of LinkControl (both 2892 and PCIe bridge) to 00 */ |
| Configuration &= 0xfefc; |
| break; |
| case 1: |
| /* Set b0 and b1 of LinkControl (both 2892 and PCIe bridge) to 01 */ |
| Configuration &= 0xfefc; |
| Configuration |= 0x1; |
| break; |
| case 2: |
| /* Set b0 and b1 of LinkControl (both 2892 and PCIe bridge) to 11 */ |
| Configuration &= 0xfefc; |
| Configuration |= 0x3; |
| break; |
| case 3: |
| /* Set b0 and b1 of LinkControl (both 2892 and PCIe bridge) to 11 and bit 8 of LinkControl of 2892 to 1 */ |
| Configuration &= 0xfefc; |
| Configuration |= 0x103; |
| break; |
| } |
| PCI_REG_WIRTE_WORD(pObj->parent_pci_dev, pAd->HostLnkCtrlOffset, |
| Configuration); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Write PCI host offset 0x%x = 0x%x\n", |
| pAd->HostLnkCtrlOffset, Configuration)); |
| } |
| |
| if (pObj->pci_dev && (pAd->RLnkCtrlOffset != 0)) { |
| /* first 2892 chip not allow to frequently set mode 3. will cause hang problem. */ |
| if (PCIePowerSaveLevel > Max) |
| PCIePowerSaveLevel = Max; |
| |
| PCI_REG_READ_WORD(pObj->pci_dev, pAd->RLnkCtrlOffset, |
| Configuration); |
| switch (PCIePowerSaveLevel) { |
| case 0: |
| /* No PCI power safe */ |
| /* Set b0 and b1 of LinkControl (both 2892 and PCIe bridge) to 00 . */ |
| Configuration &= 0xfefc; |
| break; |
| case 1: |
| /* L0 */ |
| /* Set b0 and b1 of LinkControl (both 2892 and PCIe bridge) to 01 . */ |
| Configuration &= 0xfefc; |
| Configuration |= 0x1; |
| break; |
| case 2: |
| /* L0 and L1 */ |
| /* Set b0 and b1 of LinkControl (both 2892 and PCIe bridge) to 11 */ |
| Configuration &= 0xfefc; |
| Configuration |= 0x3; |
| break; |
| case 3: |
| /* L0 , L1 and clock management. */ |
| /* Set b0 and b1 of LinkControl (both 2892 and PCIe bridge) to 11 and bit 8 of LinkControl of 2892 to 1 */ |
| Configuration &= 0xfefc; |
| Configuration |= 0x103; |
| pAd->bPCIclkOff = TRUE; |
| break; |
| } |
| PCI_REG_WIRTE_WORD(pObj->pci_dev, pAd->RLnkCtrlOffset, |
| Configuration); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Write Ralink device : offset 0x%x = 0x%x\n", |
| pAd->RLnkCtrlOffset, Configuration)); |
| } |
| |
| DBGPRINT(RT_DEBUG_TRACE, ("RTMPPCIePowerLinkCtrl <==============\n")); |
| } |
| |
| /* |
| ======================================================================== |
| |
| Routine Description: |
| 1. Write a PCI register for rt30xx power solution 3 |
| |
| ======================================================================== |
| */ |
| void RTMPrt3xSetPCIePowerLinkCtrl(struct rt_rtmp_adapter *pAd) |
| { |
| |
| unsigned long HostConfiguration = 0; |
| unsigned long Configuration; |
| struct os_cookie *pObj; |
| int pos; |
| u16 reg16; |
| |
| pObj = (struct os_cookie *)pAd->OS_Cookie; |
| |
| DBGPRINT(RT_DEBUG_INFO, |
| ("RTMPrt3xSetPCIePowerLinkCtrl.===> %lx\n", |
| pAd->StaCfg.PSControl.word)); |
| |
| /* Check PSControl Configuration */ |
| if (pAd->StaCfg.PSControl.field.EnableNewPS == FALSE) |
| return; |
| RTMPFindHostPCIDev(pAd); |
| if (pObj->parent_pci_dev) { |
| /* Find PCI-to-PCI Bridge Express Capability Offset */ |
| pos = pci_find_capability(pObj->parent_pci_dev, PCI_CAP_ID_EXP); |
| |
| if (pos != 0) |
| pAd->HostLnkCtrlOffset = pos + PCI_EXP_LNKCTL; |
| |
| /* If configurared to turn on L1. */ |
| HostConfiguration = 0; |
| if (pAd->StaCfg.PSControl.field.rt30xxForceASPMTest == 1) { |
| DBGPRINT(RT_DEBUG_TRACE, ("Enter,PSM : Force ASPM\n")); |
| |
| /* Skip non-exist deice right away */ |
| if ((pAd->HostLnkCtrlOffset != 0)) { |
| PCI_REG_READ_WORD(pObj->parent_pci_dev, |
| pAd->HostLnkCtrlOffset, |
| HostConfiguration); |
| /* Prepare Configuration to write to Host */ |
| HostConfiguration |= 0x3; |
| PCI_REG_WIRTE_WORD(pObj->parent_pci_dev, |
| pAd->HostLnkCtrlOffset, |
| HostConfiguration); |
| pAd->Rt3xxHostLinkCtrl = HostConfiguration; |
| /* Because in rt30xxForceASPMTest Mode, Force turn on L0s, L1. */ |
| /* Fix HostConfiguration bit0:1 = 0x3 for later use. */ |
| HostConfiguration = 0x3; |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("PSM : Force ASPM : " |
| "Host device L1/L0s Value = 0x%lx\n", |
| HostConfiguration)); |
| } |
| } else if (pAd->StaCfg.PSControl.field.rt30xxFollowHostASPM == |
| 1) { |
| |
| /* Skip non-exist deice right away */ |
| if ((pAd->HostLnkCtrlOffset != 0)) { |
| PCI_REG_READ_WORD(pObj->parent_pci_dev, |
| pAd->HostLnkCtrlOffset, |
| HostConfiguration); |
| pAd->Rt3xxHostLinkCtrl = HostConfiguration; |
| HostConfiguration &= 0x3; |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("PSM : Follow Host ASPM : " |
| "Host device L1/L0s Value = 0x%lx\n", |
| HostConfiguration)); |
| } |
| } |
| } |
| /* Prepare to write Ralink setting. */ |
| /* Find Ralink PCIe Device's Express Capability Offset */ |
| pos = pci_find_capability(pObj->pci_dev, PCI_CAP_ID_EXP); |
| |
| if (pos != 0) { |
| /* Ralink PCIe Device's Link Control Register Offset */ |
| pAd->RLnkCtrlOffset = pos + PCI_EXP_LNKCTL; |
| pci_read_config_word(pObj->pci_dev, pAd->RLnkCtrlOffset, |
| ®16); |
| Configuration = le2cpu16(reg16); |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("Read (Ralink PCIe Link Control Register) " |
| "offset 0x%x = 0x%lx\n", |
| pAd->RLnkCtrlOffset, Configuration)); |
| Configuration |= 0x100; |
| if ((pAd->StaCfg.PSControl.field.rt30xxFollowHostASPM == 1) |
| || (pAd->StaCfg.PSControl.field.rt30xxForceASPMTest == 1)) { |
| switch (HostConfiguration) { |
| case 0: |
| Configuration &= 0xffffffc; |
| break; |
| case 1: |
| Configuration &= 0xffffffc; |
| Configuration |= 0x1; |
| break; |
| case 2: |
| Configuration &= 0xffffffc; |
| Configuration |= 0x2; |
| break; |
| case 3: |
| Configuration |= 0x3; |
| break; |
| } |
| } |
| reg16 = cpu2le16(Configuration); |
| pci_write_config_word(pObj->pci_dev, pAd->RLnkCtrlOffset, |
| reg16); |
| pAd->Rt3xxRalinkLinkCtrl = Configuration; |
| DBGPRINT(RT_DEBUG_TRACE, |
| ("PSM :Write Ralink device L1/L0s Value = 0x%lx\n", |
| Configuration)); |
| } |
| DBGPRINT(RT_DEBUG_INFO, |
| ("PSM :RTMPrt3xSetPCIePowerLinkCtrl <==============\n")); |
| } |