diff options
Diffstat (limited to 'drivers/staging/rt2860/pci_main_dev.c')
-rw-r--r-- | drivers/staging/rt2860/pci_main_dev.c | 1190 |
1 files changed, 1190 insertions, 0 deletions
diff --git a/drivers/staging/rt2860/pci_main_dev.c b/drivers/staging/rt2860/pci_main_dev.c new file mode 100644 index 00000000000..6af43041907 --- /dev/null +++ b/drivers/staging/rt2860/pci_main_dev.c @@ -0,0 +1,1190 @@ +/* + ************************************************************************* + * 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> + +/* 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 */ + if ((rv = pci_enable_device(pci_dev)) != 0) { + DBGPRINT(RT_DEBUG_ERROR, + ("Enable PCI device failed, errno=%d!\n", rv)); + return rv; + } + + print_name = (char *)pci_name(pci_dev); + + if ((rv = pci_request_regions(pci_dev, print_name)) != 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")); +} |