Commit | Line | Data |
---|---|---|
95ea3627 | 1 | /* |
9c9a0d14 | 2 | Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> |
95ea3627 ID |
3 | <http://rt2x00.serialmonkey.com> |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the | |
17 | Free Software Foundation, Inc., | |
18 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
19 | */ | |
20 | ||
21 | /* | |
22 | Module: rt2x00pci | |
23 | Abstract: rt2x00 generic pci device routines. | |
24 | */ | |
25 | ||
95ea3627 ID |
26 | #include <linux/dma-mapping.h> |
27 | #include <linux/kernel.h> | |
28 | #include <linux/module.h> | |
29 | #include <linux/pci.h> | |
5a0e3ad6 | 30 | #include <linux/slab.h> |
95ea3627 ID |
31 | |
32 | #include "rt2x00.h" | |
33 | #include "rt2x00pci.h" | |
34 | ||
95ea3627 ID |
35 | /* |
36 | * PCI driver handlers. | |
37 | */ | |
38 | static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) | |
39 | { | |
40 | kfree(rt2x00dev->rf); | |
41 | rt2x00dev->rf = NULL; | |
42 | ||
43 | kfree(rt2x00dev->eeprom); | |
44 | rt2x00dev->eeprom = NULL; | |
45 | ||
21795094 ID |
46 | if (rt2x00dev->csr.base) { |
47 | iounmap(rt2x00dev->csr.base); | |
48 | rt2x00dev->csr.base = NULL; | |
95ea3627 ID |
49 | } |
50 | } | |
51 | ||
52 | static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) | |
53 | { | |
14a3bf89 | 54 | struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); |
95ea3627 | 55 | |
275f165f | 56 | rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0); |
21795094 | 57 | if (!rt2x00dev->csr.base) |
95ea3627 ID |
58 | goto exit; |
59 | ||
60 | rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); | |
61 | if (!rt2x00dev->eeprom) | |
62 | goto exit; | |
63 | ||
64 | rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); | |
65 | if (!rt2x00dev->rf) | |
66 | goto exit; | |
67 | ||
68 | return 0; | |
69 | ||
70 | exit: | |
ec9c4989 | 71 | rt2x00_probe_err("Failed to allocate registers\n"); |
95ea3627 ID |
72 | |
73 | rt2x00pci_free_reg(rt2x00dev); | |
74 | ||
75 | return -ENOMEM; | |
76 | } | |
77 | ||
e01ae27f | 78 | int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) |
95ea3627 | 79 | { |
95ea3627 ID |
80 | struct ieee80211_hw *hw; |
81 | struct rt2x00_dev *rt2x00dev; | |
82 | int retval; | |
a89534ed | 83 | u16 chip; |
95ea3627 | 84 | |
47cb905d | 85 | retval = pci_enable_device(pci_dev); |
95ea3627 | 86 | if (retval) { |
ec9c4989 | 87 | rt2x00_probe_err("Enable device failed\n"); |
95ea3627 ID |
88 | return retval; |
89 | } | |
90 | ||
47cb905d | 91 | retval = pci_request_regions(pci_dev, pci_name(pci_dev)); |
95ea3627 | 92 | if (retval) { |
ec9c4989 | 93 | rt2x00_probe_err("PCI request regions failed\n"); |
47cb905d | 94 | goto exit_disable_device; |
95ea3627 ID |
95 | } |
96 | ||
97 | pci_set_master(pci_dev); | |
98 | ||
99 | if (pci_set_mwi(pci_dev)) | |
ec9c4989 | 100 | rt2x00_probe_err("MWI not available\n"); |
95ea3627 | 101 | |
284901a9 | 102 | if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { |
ec9c4989 | 103 | rt2x00_probe_err("PCI DMA not supported\n"); |
95ea3627 | 104 | retval = -EIO; |
47cb905d | 105 | goto exit_release_regions; |
95ea3627 ID |
106 | } |
107 | ||
108 | hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); | |
109 | if (!hw) { | |
ec9c4989 | 110 | rt2x00_probe_err("Failed to allocate hardware\n"); |
95ea3627 | 111 | retval = -ENOMEM; |
dfb6b7c1 | 112 | goto exit_release_regions; |
95ea3627 ID |
113 | } |
114 | ||
115 | pci_set_drvdata(pci_dev, hw); | |
116 | ||
117 | rt2x00dev = hw->priv; | |
14a3bf89 | 118 | rt2x00dev->dev = &pci_dev->dev; |
95ea3627 ID |
119 | rt2x00dev->ops = ops; |
120 | rt2x00dev->hw = hw; | |
440ddada | 121 | rt2x00dev->irq = pci_dev->irq; |
21cf502a | 122 | rt2x00dev->name = ops->name; |
440ddada | 123 | |
2cdb9a42 | 124 | if (pci_is_pcie(pci_dev)) |
6e1fdd11 GW |
125 | rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); |
126 | else | |
127 | rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); | |
2015d192 | 128 | |
95ea3627 ID |
129 | retval = rt2x00pci_alloc_reg(rt2x00dev); |
130 | if (retval) | |
131 | goto exit_free_device; | |
132 | ||
a89534ed WH |
133 | /* |
134 | * Because rt3290 chip use different efuse offset to read efuse data. | |
135 | * So before read efuse it need to indicate it is the | |
136 | * rt3290 or not. | |
137 | */ | |
138 | pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip); | |
139 | rt2x00dev->chip.rt = chip; | |
140 | ||
95ea3627 ID |
141 | retval = rt2x00lib_probe_dev(rt2x00dev); |
142 | if (retval) | |
143 | goto exit_free_reg; | |
144 | ||
145 | return 0; | |
146 | ||
147 | exit_free_reg: | |
148 | rt2x00pci_free_reg(rt2x00dev); | |
149 | ||
150 | exit_free_device: | |
151 | ieee80211_free_hw(hw); | |
152 | ||
95ea3627 ID |
153 | exit_release_regions: |
154 | pci_release_regions(pci_dev); | |
155 | ||
47cb905d KV |
156 | exit_disable_device: |
157 | pci_disable_device(pci_dev); | |
158 | ||
95ea3627 ID |
159 | pci_set_drvdata(pci_dev, NULL); |
160 | ||
161 | return retval; | |
162 | } | |
163 | EXPORT_SYMBOL_GPL(rt2x00pci_probe); | |
164 | ||
165 | void rt2x00pci_remove(struct pci_dev *pci_dev) | |
166 | { | |
167 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | |
168 | struct rt2x00_dev *rt2x00dev = hw->priv; | |
169 | ||
170 | /* | |
171 | * Free all allocated data. | |
172 | */ | |
173 | rt2x00lib_remove_dev(rt2x00dev); | |
174 | rt2x00pci_free_reg(rt2x00dev); | |
175 | ieee80211_free_hw(hw); | |
176 | ||
177 | /* | |
178 | * Free the PCI device data. | |
179 | */ | |
180 | pci_set_drvdata(pci_dev, NULL); | |
181 | pci_disable_device(pci_dev); | |
182 | pci_release_regions(pci_dev); | |
183 | } | |
184 | EXPORT_SYMBOL_GPL(rt2x00pci_remove); | |
185 | ||
186 | #ifdef CONFIG_PM | |
187 | int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state) | |
188 | { | |
189 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | |
190 | struct rt2x00_dev *rt2x00dev = hw->priv; | |
191 | int retval; | |
192 | ||
193 | retval = rt2x00lib_suspend(rt2x00dev, state); | |
194 | if (retval) | |
195 | return retval; | |
196 | ||
95ea3627 ID |
197 | pci_save_state(pci_dev); |
198 | pci_disable_device(pci_dev); | |
199 | return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); | |
200 | } | |
201 | EXPORT_SYMBOL_GPL(rt2x00pci_suspend); | |
202 | ||
203 | int rt2x00pci_resume(struct pci_dev *pci_dev) | |
204 | { | |
205 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | |
206 | struct rt2x00_dev *rt2x00dev = hw->priv; | |
95ea3627 ID |
207 | |
208 | if (pci_set_power_state(pci_dev, PCI_D0) || | |
1d3c16a8 | 209 | pci_enable_device(pci_dev)) { |
ec9c4989 | 210 | rt2x00_err(rt2x00dev, "Failed to resume device\n"); |
95ea3627 ID |
211 | return -EIO; |
212 | } | |
213 | ||
1d3c16a8 | 214 | pci_restore_state(pci_dev); |
499a214c | 215 | return rt2x00lib_resume(rt2x00dev); |
95ea3627 ID |
216 | } |
217 | EXPORT_SYMBOL_GPL(rt2x00pci_resume); | |
218 | #endif /* CONFIG_PM */ | |
219 | ||
220 | /* | |
221 | * rt2x00pci module information. | |
222 | */ | |
223 | MODULE_AUTHOR(DRV_PROJECT); | |
224 | MODULE_VERSION(DRV_VERSION); | |
181d6902 | 225 | MODULE_DESCRIPTION("rt2x00 pci library"); |
95ea3627 | 226 | MODULE_LICENSE("GPL"); |