162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
462306a36Sopenharmony_ci	<http://rt2x00.serialmonkey.com>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci	Module: rt2x00pci
1062306a36Sopenharmony_ci	Abstract: rt2x00 generic pci device routines.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "rt2x00.h"
2062306a36Sopenharmony_ci#include "rt2x00pci.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * PCI driver handlers.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistatic void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	kfree(rt2x00dev->rf);
2862306a36Sopenharmony_ci	rt2x00dev->rf = NULL;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	kfree(rt2x00dev->eeprom);
3162306a36Sopenharmony_ci	rt2x00dev->eeprom = NULL;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (rt2x00dev->csr.base) {
3462306a36Sopenharmony_ci		iounmap(rt2x00dev->csr.base);
3562306a36Sopenharmony_ci		rt2x00dev->csr.base = NULL;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0);
4462306a36Sopenharmony_ci	if (!rt2x00dev->csr.base)
4562306a36Sopenharmony_ci		goto exit;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL);
4862306a36Sopenharmony_ci	if (!rt2x00dev->eeprom)
4962306a36Sopenharmony_ci		goto exit;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL);
5262306a36Sopenharmony_ci	if (!rt2x00dev->rf)
5362306a36Sopenharmony_ci		goto exit;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ciexit:
5862306a36Sopenharmony_ci	rt2x00_probe_err("Failed to allocate registers\n");
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	rt2x00pci_free_reg(rt2x00dev);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return -ENOMEM;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciint rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct ieee80211_hw *hw;
6862306a36Sopenharmony_ci	struct rt2x00_dev *rt2x00dev;
6962306a36Sopenharmony_ci	int retval;
7062306a36Sopenharmony_ci	u16 chip;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	retval = pci_enable_device(pci_dev);
7362306a36Sopenharmony_ci	if (retval) {
7462306a36Sopenharmony_ci		rt2x00_probe_err("Enable device failed\n");
7562306a36Sopenharmony_ci		return retval;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	retval = pci_request_regions(pci_dev, pci_name(pci_dev));
7962306a36Sopenharmony_ci	if (retval) {
8062306a36Sopenharmony_ci		rt2x00_probe_err("PCI request regions failed\n");
8162306a36Sopenharmony_ci		goto exit_disable_device;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	pci_set_master(pci_dev);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (pci_set_mwi(pci_dev))
8762306a36Sopenharmony_ci		rt2x00_probe_err("MWI not available\n");
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) {
9062306a36Sopenharmony_ci		rt2x00_probe_err("PCI DMA not supported\n");
9162306a36Sopenharmony_ci		retval = -EIO;
9262306a36Sopenharmony_ci		goto exit_release_regions;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
9662306a36Sopenharmony_ci	if (!hw) {
9762306a36Sopenharmony_ci		rt2x00_probe_err("Failed to allocate hardware\n");
9862306a36Sopenharmony_ci		retval = -ENOMEM;
9962306a36Sopenharmony_ci		goto exit_release_regions;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	pci_set_drvdata(pci_dev, hw);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	rt2x00dev = hw->priv;
10562306a36Sopenharmony_ci	rt2x00dev->dev = &pci_dev->dev;
10662306a36Sopenharmony_ci	rt2x00dev->ops = ops;
10762306a36Sopenharmony_ci	rt2x00dev->hw = hw;
10862306a36Sopenharmony_ci	rt2x00dev->irq = pci_dev->irq;
10962306a36Sopenharmony_ci	rt2x00dev->name = ops->name;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (pci_is_pcie(pci_dev))
11262306a36Sopenharmony_ci		rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE);
11362306a36Sopenharmony_ci	else
11462306a36Sopenharmony_ci		rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	retval = rt2x00pci_alloc_reg(rt2x00dev);
11762306a36Sopenharmony_ci	if (retval)
11862306a36Sopenharmony_ci		goto exit_free_device;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/*
12162306a36Sopenharmony_ci	 * Because rt3290 chip use different efuse offset to read efuse data.
12262306a36Sopenharmony_ci	 * So before read efuse it need to indicate it is the
12362306a36Sopenharmony_ci	 * rt3290 or not.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip);
12662306a36Sopenharmony_ci	rt2x00dev->chip.rt = chip;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	retval = rt2x00lib_probe_dev(rt2x00dev);
12962306a36Sopenharmony_ci	if (retval)
13062306a36Sopenharmony_ci		goto exit_free_reg;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciexit_free_reg:
13562306a36Sopenharmony_ci	rt2x00pci_free_reg(rt2x00dev);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciexit_free_device:
13862306a36Sopenharmony_ci	ieee80211_free_hw(hw);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciexit_release_regions:
14162306a36Sopenharmony_ci	pci_clear_mwi(pci_dev);
14262306a36Sopenharmony_ci	pci_release_regions(pci_dev);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ciexit_disable_device:
14562306a36Sopenharmony_ci	pci_disable_device(pci_dev);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return retval;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00pci_probe);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_civoid rt2x00pci_remove(struct pci_dev *pci_dev)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct ieee80211_hw *hw = pci_get_drvdata(pci_dev);
15462306a36Sopenharmony_ci	struct rt2x00_dev *rt2x00dev = hw->priv;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * Free all allocated data.
15862306a36Sopenharmony_ci	 */
15962306a36Sopenharmony_ci	rt2x00lib_remove_dev(rt2x00dev);
16062306a36Sopenharmony_ci	rt2x00pci_free_reg(rt2x00dev);
16162306a36Sopenharmony_ci	ieee80211_free_hw(hw);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/*
16462306a36Sopenharmony_ci	 * Free the PCI device data.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	pci_clear_mwi(pci_dev);
16762306a36Sopenharmony_ci	pci_disable_device(pci_dev);
16862306a36Sopenharmony_ci	pci_release_regions(pci_dev);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00pci_remove);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int __maybe_unused rt2x00pci_suspend(struct device *dev)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct ieee80211_hw *hw = dev_get_drvdata(dev);
17562306a36Sopenharmony_ci	struct rt2x00_dev *rt2x00dev = hw->priv;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return rt2x00lib_suspend(rt2x00dev);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int __maybe_unused rt2x00pci_resume(struct device *dev)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct ieee80211_hw *hw = dev_get_drvdata(dev);
18362306a36Sopenharmony_ci	struct rt2x00_dev *rt2x00dev = hw->priv;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return rt2x00lib_resume(rt2x00dev);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ciSIMPLE_DEV_PM_OPS(rt2x00pci_pm_ops, rt2x00pci_suspend, rt2x00pci_resume);
18962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00pci_pm_ops);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci/*
19262306a36Sopenharmony_ci * rt2x00pci module information.
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_ciMODULE_AUTHOR(DRV_PROJECT);
19562306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
19662306a36Sopenharmony_ciMODULE_DESCRIPTION("rt2x00 pci library");
19762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
198