1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> |
4 | <http://rt2x00.serialmonkey.com> |
5 | |
6 | */ |
7 | |
8 | /* |
9 | Module: rt2x00pci |
10 | Abstract: rt2x00 generic pci device routines. |
11 | */ |
12 | |
13 | #include <linux/dma-mapping.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/pci.h> |
17 | #include <linux/slab.h> |
18 | |
19 | #include "rt2x00.h" |
20 | #include "rt2x00pci.h" |
21 | |
22 | /* |
23 | * PCI driver handlers. |
24 | */ |
25 | static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) |
26 | { |
27 | kfree(objp: rt2x00dev->rf); |
28 | rt2x00dev->rf = NULL; |
29 | |
30 | kfree(objp: rt2x00dev->eeprom); |
31 | rt2x00dev->eeprom = NULL; |
32 | |
33 | if (rt2x00dev->csr.base) { |
34 | iounmap(addr: rt2x00dev->csr.base); |
35 | rt2x00dev->csr.base = NULL; |
36 | } |
37 | } |
38 | |
39 | static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) |
40 | { |
41 | struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); |
42 | |
43 | rt2x00dev->csr.base = pci_ioremap_bar(pdev: pci_dev, bar: 0); |
44 | if (!rt2x00dev->csr.base) |
45 | goto exit; |
46 | |
47 | rt2x00dev->eeprom = kzalloc(size: rt2x00dev->ops->eeprom_size, GFP_KERNEL); |
48 | if (!rt2x00dev->eeprom) |
49 | goto exit; |
50 | |
51 | rt2x00dev->rf = kzalloc(size: rt2x00dev->ops->rf_size, GFP_KERNEL); |
52 | if (!rt2x00dev->rf) |
53 | goto exit; |
54 | |
55 | return 0; |
56 | |
57 | exit: |
58 | rt2x00_probe_err("Failed to allocate registers\n" ); |
59 | |
60 | rt2x00pci_free_reg(rt2x00dev); |
61 | |
62 | return -ENOMEM; |
63 | } |
64 | |
65 | int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) |
66 | { |
67 | struct ieee80211_hw *hw; |
68 | struct rt2x00_dev *rt2x00dev; |
69 | int retval; |
70 | u16 chip; |
71 | |
72 | retval = pci_enable_device(dev: pci_dev); |
73 | if (retval) { |
74 | rt2x00_probe_err("Enable device failed\n" ); |
75 | return retval; |
76 | } |
77 | |
78 | retval = pci_request_regions(pci_dev, pci_name(pdev: pci_dev)); |
79 | if (retval) { |
80 | rt2x00_probe_err("PCI request regions failed\n" ); |
81 | goto exit_disable_device; |
82 | } |
83 | |
84 | pci_set_master(dev: pci_dev); |
85 | |
86 | if (pci_set_mwi(dev: pci_dev)) |
87 | rt2x00_probe_err("MWI not available\n" ); |
88 | |
89 | if (dma_set_mask(dev: &pci_dev->dev, DMA_BIT_MASK(32))) { |
90 | rt2x00_probe_err("PCI DMA not supported\n" ); |
91 | retval = -EIO; |
92 | goto exit_release_regions; |
93 | } |
94 | |
95 | hw = ieee80211_alloc_hw(priv_data_len: sizeof(struct rt2x00_dev), ops: ops->hw); |
96 | if (!hw) { |
97 | rt2x00_probe_err("Failed to allocate hardware\n" ); |
98 | retval = -ENOMEM; |
99 | goto exit_release_regions; |
100 | } |
101 | |
102 | pci_set_drvdata(pdev: pci_dev, data: hw); |
103 | |
104 | rt2x00dev = hw->priv; |
105 | rt2x00dev->dev = &pci_dev->dev; |
106 | rt2x00dev->ops = ops; |
107 | rt2x00dev->hw = hw; |
108 | rt2x00dev->irq = pci_dev->irq; |
109 | rt2x00dev->name = ops->name; |
110 | |
111 | if (pci_is_pcie(dev: pci_dev)) |
112 | rt2x00_set_chip_intf(rt2x00dev, intf: RT2X00_CHIP_INTF_PCIE); |
113 | else |
114 | rt2x00_set_chip_intf(rt2x00dev, intf: RT2X00_CHIP_INTF_PCI); |
115 | |
116 | retval = rt2x00pci_alloc_reg(rt2x00dev); |
117 | if (retval) |
118 | goto exit_free_device; |
119 | |
120 | /* |
121 | * Because rt3290 chip use different efuse offset to read efuse data. |
122 | * So before read efuse it need to indicate it is the |
123 | * rt3290 or not. |
124 | */ |
125 | pci_read_config_word(dev: pci_dev, PCI_DEVICE_ID, val: &chip); |
126 | rt2x00dev->chip.rt = chip; |
127 | |
128 | retval = rt2x00lib_probe_dev(rt2x00dev); |
129 | if (retval) |
130 | goto exit_free_reg; |
131 | |
132 | return 0; |
133 | |
134 | exit_free_reg: |
135 | rt2x00pci_free_reg(rt2x00dev); |
136 | |
137 | exit_free_device: |
138 | ieee80211_free_hw(hw); |
139 | |
140 | exit_release_regions: |
141 | pci_clear_mwi(dev: pci_dev); |
142 | pci_release_regions(pci_dev); |
143 | |
144 | exit_disable_device: |
145 | pci_disable_device(dev: pci_dev); |
146 | |
147 | return retval; |
148 | } |
149 | EXPORT_SYMBOL_GPL(rt2x00pci_probe); |
150 | |
151 | void rt2x00pci_remove(struct pci_dev *pci_dev) |
152 | { |
153 | struct ieee80211_hw *hw = pci_get_drvdata(pdev: pci_dev); |
154 | struct rt2x00_dev *rt2x00dev = hw->priv; |
155 | |
156 | /* |
157 | * Free all allocated data. |
158 | */ |
159 | rt2x00lib_remove_dev(rt2x00dev); |
160 | rt2x00pci_free_reg(rt2x00dev); |
161 | ieee80211_free_hw(hw); |
162 | |
163 | /* |
164 | * Free the PCI device data. |
165 | */ |
166 | pci_clear_mwi(dev: pci_dev); |
167 | pci_disable_device(dev: pci_dev); |
168 | pci_release_regions(pci_dev); |
169 | } |
170 | EXPORT_SYMBOL_GPL(rt2x00pci_remove); |
171 | |
172 | static int __maybe_unused rt2x00pci_suspend(struct device *dev) |
173 | { |
174 | struct ieee80211_hw *hw = dev_get_drvdata(dev); |
175 | struct rt2x00_dev *rt2x00dev = hw->priv; |
176 | |
177 | return rt2x00lib_suspend(rt2x00dev); |
178 | } |
179 | |
180 | static int __maybe_unused rt2x00pci_resume(struct device *dev) |
181 | { |
182 | struct ieee80211_hw *hw = dev_get_drvdata(dev); |
183 | struct rt2x00_dev *rt2x00dev = hw->priv; |
184 | |
185 | return rt2x00lib_resume(rt2x00dev); |
186 | } |
187 | |
188 | SIMPLE_DEV_PM_OPS(rt2x00pci_pm_ops, rt2x00pci_suspend, rt2x00pci_resume); |
189 | EXPORT_SYMBOL_GPL(rt2x00pci_pm_ops); |
190 | |
191 | /* |
192 | * rt2x00pci module information. |
193 | */ |
194 | MODULE_AUTHOR(DRV_PROJECT); |
195 | MODULE_VERSION(DRV_VERSION); |
196 | MODULE_DESCRIPTION("rt2x00 pci library" ); |
197 | MODULE_LICENSE("GPL" ); |
198 | |