18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> 48c2ecf20Sopenharmony_ci <http://rt2x00.serialmonkey.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci Module: rt2x00pci 108c2ecf20Sopenharmony_ci Abstract: rt2x00 generic pci device routines. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/pci.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "rt2x00.h" 208c2ecf20Sopenharmony_ci#include "rt2x00pci.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * PCI driver handlers. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_cistatic void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci kfree(rt2x00dev->rf); 288c2ecf20Sopenharmony_ci rt2x00dev->rf = NULL; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci kfree(rt2x00dev->eeprom); 318c2ecf20Sopenharmony_ci rt2x00dev->eeprom = NULL; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (rt2x00dev->csr.base) { 348c2ecf20Sopenharmony_ci iounmap(rt2x00dev->csr.base); 358c2ecf20Sopenharmony_ci rt2x00dev->csr.base = NULL; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0); 448c2ecf20Sopenharmony_ci if (!rt2x00dev->csr.base) 458c2ecf20Sopenharmony_ci goto exit; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); 488c2ecf20Sopenharmony_ci if (!rt2x00dev->eeprom) 498c2ecf20Sopenharmony_ci goto exit; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); 528c2ecf20Sopenharmony_ci if (!rt2x00dev->rf) 538c2ecf20Sopenharmony_ci goto exit; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciexit: 588c2ecf20Sopenharmony_ci rt2x00_probe_err("Failed to allocate registers\n"); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci rt2x00pci_free_reg(rt2x00dev); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return -ENOMEM; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ciint rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct ieee80211_hw *hw; 688c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev; 698c2ecf20Sopenharmony_ci int retval; 708c2ecf20Sopenharmony_ci u16 chip; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci retval = pci_enable_device(pci_dev); 738c2ecf20Sopenharmony_ci if (retval) { 748c2ecf20Sopenharmony_ci rt2x00_probe_err("Enable device failed\n"); 758c2ecf20Sopenharmony_ci return retval; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci retval = pci_request_regions(pci_dev, pci_name(pci_dev)); 798c2ecf20Sopenharmony_ci if (retval) { 808c2ecf20Sopenharmony_ci rt2x00_probe_err("PCI request regions failed\n"); 818c2ecf20Sopenharmony_ci goto exit_disable_device; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci pci_set_master(pci_dev); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (pci_set_mwi(pci_dev)) 878c2ecf20Sopenharmony_ci rt2x00_probe_err("MWI not available\n"); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { 908c2ecf20Sopenharmony_ci rt2x00_probe_err("PCI DMA not supported\n"); 918c2ecf20Sopenharmony_ci retval = -EIO; 928c2ecf20Sopenharmony_ci goto exit_release_regions; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); 968c2ecf20Sopenharmony_ci if (!hw) { 978c2ecf20Sopenharmony_ci rt2x00_probe_err("Failed to allocate hardware\n"); 988c2ecf20Sopenharmony_ci retval = -ENOMEM; 998c2ecf20Sopenharmony_ci goto exit_release_regions; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci pci_set_drvdata(pci_dev, hw); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci rt2x00dev = hw->priv; 1058c2ecf20Sopenharmony_ci rt2x00dev->dev = &pci_dev->dev; 1068c2ecf20Sopenharmony_ci rt2x00dev->ops = ops; 1078c2ecf20Sopenharmony_ci rt2x00dev->hw = hw; 1088c2ecf20Sopenharmony_ci rt2x00dev->irq = pci_dev->irq; 1098c2ecf20Sopenharmony_ci rt2x00dev->name = ops->name; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (pci_is_pcie(pci_dev)) 1128c2ecf20Sopenharmony_ci rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); 1138c2ecf20Sopenharmony_ci else 1148c2ecf20Sopenharmony_ci rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci retval = rt2x00pci_alloc_reg(rt2x00dev); 1178c2ecf20Sopenharmony_ci if (retval) 1188c2ecf20Sopenharmony_ci goto exit_free_device; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* 1218c2ecf20Sopenharmony_ci * Because rt3290 chip use different efuse offset to read efuse data. 1228c2ecf20Sopenharmony_ci * So before read efuse it need to indicate it is the 1238c2ecf20Sopenharmony_ci * rt3290 or not. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip); 1268c2ecf20Sopenharmony_ci rt2x00dev->chip.rt = chip; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci retval = rt2x00lib_probe_dev(rt2x00dev); 1298c2ecf20Sopenharmony_ci if (retval) 1308c2ecf20Sopenharmony_ci goto exit_free_reg; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ciexit_free_reg: 1358c2ecf20Sopenharmony_ci rt2x00pci_free_reg(rt2x00dev); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciexit_free_device: 1388c2ecf20Sopenharmony_ci ieee80211_free_hw(hw); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciexit_release_regions: 1418c2ecf20Sopenharmony_ci pci_clear_mwi(pci_dev); 1428c2ecf20Sopenharmony_ci pci_release_regions(pci_dev); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ciexit_disable_device: 1458c2ecf20Sopenharmony_ci pci_disable_device(pci_dev); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return retval; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00pci_probe); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_civoid rt2x00pci_remove(struct pci_dev *pci_dev) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); 1548c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = hw->priv; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * Free all allocated data. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci rt2x00lib_remove_dev(rt2x00dev); 1608c2ecf20Sopenharmony_ci rt2x00pci_free_reg(rt2x00dev); 1618c2ecf20Sopenharmony_ci ieee80211_free_hw(hw); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * Free the PCI device data. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci pci_clear_mwi(pci_dev); 1678c2ecf20Sopenharmony_ci pci_disable_device(pci_dev); 1688c2ecf20Sopenharmony_ci pci_release_regions(pci_dev); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00pci_remove); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int __maybe_unused rt2x00pci_suspend(struct device *dev) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = dev_get_drvdata(dev); 1758c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = hw->priv; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return rt2x00lib_suspend(rt2x00dev); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int __maybe_unused rt2x00pci_resume(struct device *dev) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = dev_get_drvdata(dev); 1838c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = hw->priv; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return rt2x00lib_resume(rt2x00dev); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciSIMPLE_DEV_PM_OPS(rt2x00pci_pm_ops, rt2x00pci_suspend, rt2x00pci_resume); 1898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00pci_pm_ops); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * rt2x00pci module information. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRV_PROJECT); 1958c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 1968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("rt2x00 pci library"); 1978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 198