Commit | Line | Data |
---|---|---|
95ea3627 | 1 | /* |
811aa9ca | 2 | Copyright (C) 2004 - 2008 rt2x00 SourceForge Project |
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> | |
30 | ||
31 | #include "rt2x00.h" | |
32 | #include "rt2x00pci.h" | |
33 | ||
95ea3627 ID |
34 | /* |
35 | * TX data handlers. | |
36 | */ | |
6db3786a | 37 | int rt2x00pci_write_tx_data(struct queue_entry *entry) |
95ea3627 | 38 | { |
b8be63ff | 39 | struct queue_entry_priv_pci *entry_priv = entry->priv_data; |
181d6902 | 40 | struct skb_frame_desc *skbdesc; |
95ea3627 ID |
41 | u32 word; |
42 | ||
b8be63ff | 43 | rt2x00_desc_read(entry_priv->desc, 0, &word); |
95ea3627 | 44 | |
6db3786a ID |
45 | /* |
46 | * This should not happen, we already checked the entry | |
47 | * was ours. When the hardware disagrees there has been | |
48 | * a queue corruption! | |
49 | */ | |
50 | if (unlikely(rt2x00_get_field32(word, TXD_ENTRY_OWNER_NIC) || | |
51 | rt2x00_get_field32(word, TXD_ENTRY_VALID))) { | |
52 | ERROR(entry->queue->rt2x00dev, | |
53 | "Corrupt queue %d, accessing entry which is not ours.\n" | |
95ea3627 | 54 | "Please file bug report to %s.\n", |
e58c6aca | 55 | entry->queue->qid, DRV_PROJECT); |
95ea3627 ID |
56 | return -EINVAL; |
57 | } | |
58 | ||
08992f7f ID |
59 | /* |
60 | * Fill in skb descriptor | |
61 | */ | |
6db3786a | 62 | skbdesc = get_skb_frame_desc(entry->skb); |
e039fa4a | 63 | memset(skbdesc, 0, sizeof(*skbdesc)); |
b8be63ff | 64 | skbdesc->desc = entry_priv->desc; |
6db3786a | 65 | skbdesc->desc_len = entry->queue->desc_size; |
181d6902 ID |
66 | skbdesc->entry = entry; |
67 | ||
6db3786a | 68 | memcpy(entry_priv->data, entry->skb->data, entry->skb->len); |
95ea3627 | 69 | |
95ea3627 ID |
70 | return 0; |
71 | } | |
72 | EXPORT_SYMBOL_GPL(rt2x00pci_write_tx_data); | |
73 | ||
74 | /* | |
3957ccb5 | 75 | * TX/RX data handlers. |
95ea3627 ID |
76 | */ |
77 | void rt2x00pci_rxdone(struct rt2x00_dev *rt2x00dev) | |
78 | { | |
181d6902 ID |
79 | struct data_queue *queue = rt2x00dev->rx; |
80 | struct queue_entry *entry; | |
b8be63ff | 81 | struct queue_entry_priv_pci *entry_priv; |
181d6902 ID |
82 | struct skb_frame_desc *skbdesc; |
83 | struct rxdone_entry_desc rxdesc; | |
4150c572 | 84 | u32 word; |
95ea3627 ID |
85 | |
86 | while (1) { | |
181d6902 | 87 | entry = rt2x00queue_get_entry(queue, Q_INDEX); |
b8be63ff ID |
88 | entry_priv = entry->priv_data; |
89 | rt2x00_desc_read(entry_priv->desc, 0, &word); | |
95ea3627 | 90 | |
4150c572 | 91 | if (rt2x00_get_field32(word, RXD_ENTRY_OWNER_NIC)) |
95ea3627 ID |
92 | break; |
93 | ||
181d6902 ID |
94 | memset(&rxdesc, 0, sizeof(rxdesc)); |
95 | rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc); | |
95ea3627 ID |
96 | |
97 | /* | |
239c249d | 98 | * Allocate the sk_buffer and copy all data into it. |
95ea3627 | 99 | */ |
239c249d | 100 | entry->skb = rt2x00queue_alloc_rxskb(queue); |
181d6902 | 101 | if (!entry->skb) |
95ea3627 ID |
102 | return; |
103 | ||
239c249d GW |
104 | memcpy(entry->skb->data, entry_priv->data, rxdesc.size); |
105 | skb_trim(entry->skb, rxdesc.size); | |
95ea3627 | 106 | |
08992f7f ID |
107 | /* |
108 | * Fill in skb descriptor | |
109 | */ | |
181d6902 ID |
110 | skbdesc = get_skb_frame_desc(entry->skb); |
111 | memset(skbdesc, 0, sizeof(*skbdesc)); | |
b8be63ff | 112 | skbdesc->desc = entry_priv->desc; |
181d6902 | 113 | skbdesc->desc_len = queue->desc_size; |
08992f7f ID |
114 | skbdesc->entry = entry; |
115 | ||
95ea3627 ID |
116 | /* |
117 | * Send the frame to rt2x00lib for further processing. | |
118 | */ | |
181d6902 | 119 | rt2x00lib_rxdone(entry, &rxdesc); |
95ea3627 | 120 | |
181d6902 | 121 | if (test_bit(DEVICE_ENABLED_RADIO, &queue->rt2x00dev->flags)) { |
4150c572 | 122 | rt2x00_set_field32(&word, RXD_ENTRY_OWNER_NIC, 1); |
b8be63ff | 123 | rt2x00_desc_write(entry_priv->desc, 0, word); |
95ea3627 ID |
124 | } |
125 | ||
181d6902 | 126 | rt2x00queue_index_inc(queue, Q_INDEX); |
95ea3627 ID |
127 | } |
128 | } | |
129 | EXPORT_SYMBOL_GPL(rt2x00pci_rxdone); | |
130 | ||
181d6902 ID |
131 | void rt2x00pci_txdone(struct rt2x00_dev *rt2x00dev, struct queue_entry *entry, |
132 | struct txdone_entry_desc *txdesc) | |
3957ccb5 | 133 | { |
b8be63ff | 134 | struct queue_entry_priv_pci *entry_priv = entry->priv_data; |
e039fa4a | 135 | enum data_queue_qid qid = skb_get_queue_mapping(entry->skb); |
3957ccb5 ID |
136 | u32 word; |
137 | ||
181d6902 | 138 | rt2x00lib_txdone(entry, txdesc); |
3957ccb5 ID |
139 | |
140 | /* | |
141 | * Make this entry available for reuse. | |
142 | */ | |
143 | entry->flags = 0; | |
144 | ||
b8be63ff | 145 | rt2x00_desc_read(entry_priv->desc, 0, &word); |
3957ccb5 ID |
146 | rt2x00_set_field32(&word, TXD_ENTRY_OWNER_NIC, 0); |
147 | rt2x00_set_field32(&word, TXD_ENTRY_VALID, 0); | |
b8be63ff | 148 | rt2x00_desc_write(entry_priv->desc, 0, word); |
3957ccb5 | 149 | |
6db3786a | 150 | __clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); |
181d6902 | 151 | rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE); |
3957ccb5 ID |
152 | |
153 | /* | |
b869767b ID |
154 | * If the data queue was below the threshold before the txdone |
155 | * handler we must make sure the packet queue in the mac80211 stack | |
3957ccb5 ID |
156 | * is reenabled when the txdone handler has finished. |
157 | */ | |
b869767b | 158 | if (!rt2x00queue_threshold(entry->queue)) |
e039fa4a | 159 | ieee80211_wake_queue(rt2x00dev->hw, qid); |
3957ccb5 ID |
160 | |
161 | } | |
162 | EXPORT_SYMBOL_GPL(rt2x00pci_txdone); | |
163 | ||
95ea3627 ID |
164 | /* |
165 | * Device initialization handlers. | |
166 | */ | |
9c9dd2c9 ID |
167 | #define desc_size(__queue) \ |
168 | ({ \ | |
169 | ((__queue)->limit * (__queue)->desc_size);\ | |
95ea3627 ID |
170 | }) |
171 | ||
9c9dd2c9 ID |
172 | #define data_size(__queue) \ |
173 | ({ \ | |
174 | ((__queue)->limit * (__queue)->data_size);\ | |
95ea3627 ID |
175 | }) |
176 | ||
9c9dd2c9 ID |
177 | #define dma_size(__queue) \ |
178 | ({ \ | |
179 | data_size(__queue) + desc_size(__queue);\ | |
95ea3627 ID |
180 | }) |
181 | ||
9c9dd2c9 ID |
182 | #define desc_offset(__queue, __base, __i) \ |
183 | ({ \ | |
184 | (__base) + data_size(__queue) + \ | |
185 | ((__i) * (__queue)->desc_size); \ | |
186 | }) | |
187 | ||
188 | #define data_offset(__queue, __base, __i) \ | |
189 | ({ \ | |
190 | (__base) + \ | |
191 | ((__i) * (__queue)->data_size); \ | |
181d6902 ID |
192 | }) |
193 | ||
194 | static int rt2x00pci_alloc_queue_dma(struct rt2x00_dev *rt2x00dev, | |
195 | struct data_queue *queue) | |
95ea3627 | 196 | { |
b8be63ff | 197 | struct queue_entry_priv_pci *entry_priv; |
30b3a23c ID |
198 | void *addr; |
199 | dma_addr_t dma; | |
95ea3627 ID |
200 | unsigned int i; |
201 | ||
202 | /* | |
203 | * Allocate DMA memory for descriptor and buffer. | |
204 | */ | |
14a3bf89 GW |
205 | addr = dma_alloc_coherent(rt2x00dev->dev, dma_size(queue), &dma, |
206 | GFP_KERNEL | GFP_DMA); | |
30b3a23c | 207 | if (!addr) |
95ea3627 ID |
208 | return -ENOMEM; |
209 | ||
30b3a23c | 210 | memset(addr, 0, dma_size(queue)); |
9c9dd2c9 | 211 | |
95ea3627 | 212 | /* |
181d6902 | 213 | * Initialize all queue entries to contain valid addresses. |
95ea3627 | 214 | */ |
181d6902 | 215 | for (i = 0; i < queue->limit; i++) { |
b8be63ff ID |
216 | entry_priv = queue->entries[i].priv_data; |
217 | entry_priv->desc = desc_offset(queue, addr, i); | |
218 | entry_priv->desc_dma = desc_offset(queue, dma, i); | |
219 | entry_priv->data = data_offset(queue, addr, i); | |
220 | entry_priv->data_dma = data_offset(queue, dma, i); | |
95ea3627 ID |
221 | } |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
181d6902 ID |
226 | static void rt2x00pci_free_queue_dma(struct rt2x00_dev *rt2x00dev, |
227 | struct data_queue *queue) | |
95ea3627 | 228 | { |
b8be63ff ID |
229 | struct queue_entry_priv_pci *entry_priv = |
230 | queue->entries[0].priv_data; | |
181d6902 | 231 | |
b8be63ff | 232 | if (entry_priv->data) |
14a3bf89 GW |
233 | dma_free_coherent(rt2x00dev->dev, dma_size(queue), |
234 | entry_priv->data, entry_priv->data_dma); | |
b8be63ff | 235 | entry_priv->data = NULL; |
95ea3627 ID |
236 | } |
237 | ||
238 | int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev) | |
239 | { | |
14a3bf89 | 240 | struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); |
181d6902 | 241 | struct data_queue *queue; |
95ea3627 ID |
242 | int status; |
243 | ||
244 | /* | |
245 | * Allocate DMA | |
246 | */ | |
181d6902 ID |
247 | queue_for_each(rt2x00dev, queue) { |
248 | status = rt2x00pci_alloc_queue_dma(rt2x00dev, queue); | |
95ea3627 ID |
249 | if (status) |
250 | goto exit; | |
251 | } | |
252 | ||
253 | /* | |
254 | * Register interrupt handler. | |
255 | */ | |
256 | status = request_irq(pci_dev->irq, rt2x00dev->ops->lib->irq_handler, | |
257 | IRQF_SHARED, pci_name(pci_dev), rt2x00dev); | |
258 | if (status) { | |
259 | ERROR(rt2x00dev, "IRQ %d allocation failed (error %d).\n", | |
260 | pci_dev->irq, status); | |
b30cdfc5 | 261 | goto exit; |
95ea3627 ID |
262 | } |
263 | ||
264 | return 0; | |
265 | ||
266 | exit: | |
b30cdfc5 ID |
267 | queue_for_each(rt2x00dev, queue) |
268 | rt2x00pci_free_queue_dma(rt2x00dev, queue); | |
95ea3627 ID |
269 | |
270 | return status; | |
271 | } | |
272 | EXPORT_SYMBOL_GPL(rt2x00pci_initialize); | |
273 | ||
274 | void rt2x00pci_uninitialize(struct rt2x00_dev *rt2x00dev) | |
275 | { | |
181d6902 | 276 | struct data_queue *queue; |
95ea3627 ID |
277 | |
278 | /* | |
279 | * Free irq line. | |
280 | */ | |
14a3bf89 | 281 | free_irq(to_pci_dev(rt2x00dev->dev)->irq, rt2x00dev); |
95ea3627 ID |
282 | |
283 | /* | |
284 | * Free DMA | |
285 | */ | |
181d6902 ID |
286 | queue_for_each(rt2x00dev, queue) |
287 | rt2x00pci_free_queue_dma(rt2x00dev, queue); | |
95ea3627 ID |
288 | } |
289 | EXPORT_SYMBOL_GPL(rt2x00pci_uninitialize); | |
290 | ||
291 | /* | |
292 | * PCI driver handlers. | |
293 | */ | |
294 | static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) | |
295 | { | |
296 | kfree(rt2x00dev->rf); | |
297 | rt2x00dev->rf = NULL; | |
298 | ||
299 | kfree(rt2x00dev->eeprom); | |
300 | rt2x00dev->eeprom = NULL; | |
301 | ||
21795094 ID |
302 | if (rt2x00dev->csr.base) { |
303 | iounmap(rt2x00dev->csr.base); | |
304 | rt2x00dev->csr.base = NULL; | |
95ea3627 ID |
305 | } |
306 | } | |
307 | ||
308 | static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) | |
309 | { | |
14a3bf89 | 310 | struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); |
95ea3627 | 311 | |
21795094 | 312 | rt2x00dev->csr.base = ioremap(pci_resource_start(pci_dev, 0), |
95ea3627 | 313 | pci_resource_len(pci_dev, 0)); |
21795094 | 314 | if (!rt2x00dev->csr.base) |
95ea3627 ID |
315 | goto exit; |
316 | ||
317 | rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); | |
318 | if (!rt2x00dev->eeprom) | |
319 | goto exit; | |
320 | ||
321 | rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); | |
322 | if (!rt2x00dev->rf) | |
323 | goto exit; | |
324 | ||
325 | return 0; | |
326 | ||
327 | exit: | |
328 | ERROR_PROBE("Failed to allocate registers.\n"); | |
329 | ||
330 | rt2x00pci_free_reg(rt2x00dev); | |
331 | ||
332 | return -ENOMEM; | |
333 | } | |
334 | ||
335 | int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) | |
336 | { | |
337 | struct rt2x00_ops *ops = (struct rt2x00_ops *)id->driver_data; | |
338 | struct ieee80211_hw *hw; | |
339 | struct rt2x00_dev *rt2x00dev; | |
340 | int retval; | |
341 | ||
342 | retval = pci_request_regions(pci_dev, pci_name(pci_dev)); | |
343 | if (retval) { | |
344 | ERROR_PROBE("PCI request regions failed.\n"); | |
345 | return retval; | |
346 | } | |
347 | ||
348 | retval = pci_enable_device(pci_dev); | |
349 | if (retval) { | |
350 | ERROR_PROBE("Enable device failed.\n"); | |
351 | goto exit_release_regions; | |
352 | } | |
353 | ||
354 | pci_set_master(pci_dev); | |
355 | ||
356 | if (pci_set_mwi(pci_dev)) | |
357 | ERROR_PROBE("MWI not available.\n"); | |
358 | ||
14a3bf89 | 359 | if (dma_set_mask(&pci_dev->dev, DMA_32BIT_MASK)) { |
95ea3627 ID |
360 | ERROR_PROBE("PCI DMA not supported.\n"); |
361 | retval = -EIO; | |
362 | goto exit_disable_device; | |
363 | } | |
364 | ||
365 | hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); | |
366 | if (!hw) { | |
367 | ERROR_PROBE("Failed to allocate hardware.\n"); | |
368 | retval = -ENOMEM; | |
369 | goto exit_disable_device; | |
370 | } | |
371 | ||
372 | pci_set_drvdata(pci_dev, hw); | |
373 | ||
374 | rt2x00dev = hw->priv; | |
14a3bf89 | 375 | rt2x00dev->dev = &pci_dev->dev; |
95ea3627 ID |
376 | rt2x00dev->ops = ops; |
377 | rt2x00dev->hw = hw; | |
378 | ||
379 | retval = rt2x00pci_alloc_reg(rt2x00dev); | |
380 | if (retval) | |
381 | goto exit_free_device; | |
382 | ||
383 | retval = rt2x00lib_probe_dev(rt2x00dev); | |
384 | if (retval) | |
385 | goto exit_free_reg; | |
386 | ||
387 | return 0; | |
388 | ||
389 | exit_free_reg: | |
390 | rt2x00pci_free_reg(rt2x00dev); | |
391 | ||
392 | exit_free_device: | |
393 | ieee80211_free_hw(hw); | |
394 | ||
395 | exit_disable_device: | |
396 | if (retval != -EBUSY) | |
397 | pci_disable_device(pci_dev); | |
398 | ||
399 | exit_release_regions: | |
400 | pci_release_regions(pci_dev); | |
401 | ||
402 | pci_set_drvdata(pci_dev, NULL); | |
403 | ||
404 | return retval; | |
405 | } | |
406 | EXPORT_SYMBOL_GPL(rt2x00pci_probe); | |
407 | ||
408 | void rt2x00pci_remove(struct pci_dev *pci_dev) | |
409 | { | |
410 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | |
411 | struct rt2x00_dev *rt2x00dev = hw->priv; | |
412 | ||
413 | /* | |
414 | * Free all allocated data. | |
415 | */ | |
416 | rt2x00lib_remove_dev(rt2x00dev); | |
417 | rt2x00pci_free_reg(rt2x00dev); | |
418 | ieee80211_free_hw(hw); | |
419 | ||
420 | /* | |
421 | * Free the PCI device data. | |
422 | */ | |
423 | pci_set_drvdata(pci_dev, NULL); | |
424 | pci_disable_device(pci_dev); | |
425 | pci_release_regions(pci_dev); | |
426 | } | |
427 | EXPORT_SYMBOL_GPL(rt2x00pci_remove); | |
428 | ||
429 | #ifdef CONFIG_PM | |
430 | int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state) | |
431 | { | |
432 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | |
433 | struct rt2x00_dev *rt2x00dev = hw->priv; | |
434 | int retval; | |
435 | ||
436 | retval = rt2x00lib_suspend(rt2x00dev, state); | |
437 | if (retval) | |
438 | return retval; | |
439 | ||
440 | rt2x00pci_free_reg(rt2x00dev); | |
441 | ||
442 | pci_save_state(pci_dev); | |
443 | pci_disable_device(pci_dev); | |
444 | return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); | |
445 | } | |
446 | EXPORT_SYMBOL_GPL(rt2x00pci_suspend); | |
447 | ||
448 | int rt2x00pci_resume(struct pci_dev *pci_dev) | |
449 | { | |
450 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | |
451 | struct rt2x00_dev *rt2x00dev = hw->priv; | |
452 | int retval; | |
453 | ||
454 | if (pci_set_power_state(pci_dev, PCI_D0) || | |
455 | pci_enable_device(pci_dev) || | |
456 | pci_restore_state(pci_dev)) { | |
457 | ERROR(rt2x00dev, "Failed to resume device.\n"); | |
458 | return -EIO; | |
459 | } | |
460 | ||
461 | retval = rt2x00pci_alloc_reg(rt2x00dev); | |
462 | if (retval) | |
463 | return retval; | |
464 | ||
465 | retval = rt2x00lib_resume(rt2x00dev); | |
466 | if (retval) | |
467 | goto exit_free_reg; | |
468 | ||
469 | return 0; | |
470 | ||
471 | exit_free_reg: | |
472 | rt2x00pci_free_reg(rt2x00dev); | |
473 | ||
474 | return retval; | |
475 | } | |
476 | EXPORT_SYMBOL_GPL(rt2x00pci_resume); | |
477 | #endif /* CONFIG_PM */ | |
478 | ||
479 | /* | |
480 | * rt2x00pci module information. | |
481 | */ | |
482 | MODULE_AUTHOR(DRV_PROJECT); | |
483 | MODULE_VERSION(DRV_VERSION); | |
181d6902 | 484 | MODULE_DESCRIPTION("rt2x00 pci library"); |
95ea3627 | 485 | MODULE_LICENSE("GPL"); |