]>
Commit | Line | Data |
---|---|---|
69a2bac8 GJ |
1 | /* |
2 | Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> | |
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 | |
a05b8c58 | 16 | along with this program; if not, see <http://www.gnu.org/licenses/>. |
69a2bac8 GJ |
17 | */ |
18 | ||
19 | /* | |
20 | Module: rt2x00mmio | |
21 | Abstract: rt2x00 generic mmio device routines. | |
22 | */ | |
23 | ||
24 | #include <linux/dma-mapping.h> | |
25 | #include <linux/kernel.h> | |
26 | #include <linux/module.h> | |
27 | #include <linux/slab.h> | |
28 | ||
29 | #include "rt2x00.h" | |
30 | #include "rt2x00mmio.h" | |
31 | ||
32 | /* | |
33 | * Register access. | |
34 | */ | |
58959bdc GJ |
35 | int rt2x00mmio_regbusy_read(struct rt2x00_dev *rt2x00dev, |
36 | const unsigned int offset, | |
37 | const struct rt2x00_field32 field, | |
38 | u32 *reg) | |
69a2bac8 GJ |
39 | { |
40 | unsigned int i; | |
41 | ||
42 | if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) | |
43 | return 0; | |
44 | ||
45 | for (i = 0; i < REGISTER_BUSY_COUNT; i++) { | |
3954b4e3 | 46 | *reg = rt2x00mmio_register_read(rt2x00dev, offset); |
69a2bac8 GJ |
47 | if (!rt2x00_get_field32(*reg, field)) |
48 | return 1; | |
49 | udelay(REGISTER_BUSY_DELAY); | |
50 | } | |
51 | ||
52 | printk_once(KERN_ERR "%s() Indirect register access failed: " | |
53 | "offset=0x%.08x, value=0x%.08x\n", __func__, offset, *reg); | |
54 | *reg = ~0; | |
55 | ||
56 | return 0; | |
57 | } | |
58959bdc | 58 | EXPORT_SYMBOL_GPL(rt2x00mmio_regbusy_read); |
69a2bac8 | 59 | |
58959bdc | 60 | bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev) |
69a2bac8 GJ |
61 | { |
62 | struct data_queue *queue = rt2x00dev->rx; | |
63 | struct queue_entry *entry; | |
58959bdc | 64 | struct queue_entry_priv_mmio *entry_priv; |
69a2bac8 GJ |
65 | struct skb_frame_desc *skbdesc; |
66 | int max_rx = 16; | |
67 | ||
68 | while (--max_rx) { | |
69 | entry = rt2x00queue_get_entry(queue, Q_INDEX); | |
70 | entry_priv = entry->priv_data; | |
71 | ||
72 | if (rt2x00dev->ops->lib->get_entry_state(entry)) | |
73 | break; | |
74 | ||
75 | /* | |
76 | * Fill in desc fields of the skb descriptor | |
77 | */ | |
78 | skbdesc = get_skb_frame_desc(entry->skb); | |
79 | skbdesc->desc = entry_priv->desc; | |
80 | skbdesc->desc_len = entry->queue->desc_size; | |
81 | ||
82 | /* | |
83 | * DMA is already done, notify rt2x00lib that | |
84 | * it finished successfully. | |
85 | */ | |
86 | rt2x00lib_dmastart(entry); | |
87 | rt2x00lib_dmadone(entry); | |
88 | ||
89 | /* | |
90 | * Send the frame to rt2x00lib for further processing. | |
91 | */ | |
92 | rt2x00lib_rxdone(entry, GFP_ATOMIC); | |
93 | } | |
94 | ||
95 | return !max_rx; | |
96 | } | |
58959bdc | 97 | EXPORT_SYMBOL_GPL(rt2x00mmio_rxdone); |
69a2bac8 | 98 | |
58959bdc | 99 | void rt2x00mmio_flush_queue(struct data_queue *queue, bool drop) |
69a2bac8 GJ |
100 | { |
101 | unsigned int i; | |
102 | ||
103 | for (i = 0; !rt2x00queue_empty(queue) && i < 10; i++) | |
cfe82fbd | 104 | msleep(50); |
69a2bac8 | 105 | } |
58959bdc | 106 | EXPORT_SYMBOL_GPL(rt2x00mmio_flush_queue); |
69a2bac8 GJ |
107 | |
108 | /* | |
109 | * Device initialization handlers. | |
110 | */ | |
58959bdc GJ |
111 | static int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev, |
112 | struct data_queue *queue) | |
69a2bac8 | 113 | { |
58959bdc | 114 | struct queue_entry_priv_mmio *entry_priv; |
69a2bac8 GJ |
115 | void *addr; |
116 | dma_addr_t dma; | |
117 | unsigned int i; | |
118 | ||
119 | /* | |
120 | * Allocate DMA memory for descriptor and buffer. | |
121 | */ | |
df6e6333 JP |
122 | addr = dma_zalloc_coherent(rt2x00dev->dev, |
123 | queue->limit * queue->desc_size, &dma, | |
124 | GFP_KERNEL); | |
69a2bac8 GJ |
125 | if (!addr) |
126 | return -ENOMEM; | |
127 | ||
69a2bac8 GJ |
128 | /* |
129 | * Initialize all queue entries to contain valid addresses. | |
130 | */ | |
131 | for (i = 0; i < queue->limit; i++) { | |
132 | entry_priv = queue->entries[i].priv_data; | |
133 | entry_priv->desc = addr + i * queue->desc_size; | |
134 | entry_priv->desc_dma = dma + i * queue->desc_size; | |
135 | } | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
58959bdc GJ |
140 | static void rt2x00mmio_free_queue_dma(struct rt2x00_dev *rt2x00dev, |
141 | struct data_queue *queue) | |
69a2bac8 | 142 | { |
58959bdc | 143 | struct queue_entry_priv_mmio *entry_priv = |
69a2bac8 GJ |
144 | queue->entries[0].priv_data; |
145 | ||
146 | if (entry_priv->desc) | |
147 | dma_free_coherent(rt2x00dev->dev, | |
148 | queue->limit * queue->desc_size, | |
149 | entry_priv->desc, entry_priv->desc_dma); | |
150 | entry_priv->desc = NULL; | |
151 | } | |
152 | ||
58959bdc | 153 | int rt2x00mmio_initialize(struct rt2x00_dev *rt2x00dev) |
69a2bac8 GJ |
154 | { |
155 | struct data_queue *queue; | |
156 | int status; | |
157 | ||
158 | /* | |
159 | * Allocate DMA | |
160 | */ | |
161 | queue_for_each(rt2x00dev, queue) { | |
58959bdc | 162 | status = rt2x00mmio_alloc_queue_dma(rt2x00dev, queue); |
69a2bac8 GJ |
163 | if (status) |
164 | goto exit; | |
165 | } | |
166 | ||
167 | /* | |
168 | * Register interrupt handler. | |
169 | */ | |
170 | status = request_irq(rt2x00dev->irq, | |
171 | rt2x00dev->ops->lib->irq_handler, | |
172 | IRQF_SHARED, rt2x00dev->name, rt2x00dev); | |
173 | if (status) { | |
ec9c4989 JP |
174 | rt2x00_err(rt2x00dev, "IRQ %d allocation failed (error %d)\n", |
175 | rt2x00dev->irq, status); | |
69a2bac8 GJ |
176 | goto exit; |
177 | } | |
178 | ||
179 | return 0; | |
180 | ||
181 | exit: | |
182 | queue_for_each(rt2x00dev, queue) | |
58959bdc | 183 | rt2x00mmio_free_queue_dma(rt2x00dev, queue); |
69a2bac8 GJ |
184 | |
185 | return status; | |
186 | } | |
58959bdc | 187 | EXPORT_SYMBOL_GPL(rt2x00mmio_initialize); |
69a2bac8 | 188 | |
58959bdc | 189 | void rt2x00mmio_uninitialize(struct rt2x00_dev *rt2x00dev) |
69a2bac8 GJ |
190 | { |
191 | struct data_queue *queue; | |
192 | ||
193 | /* | |
194 | * Free irq line. | |
195 | */ | |
196 | free_irq(rt2x00dev->irq, rt2x00dev); | |
197 | ||
198 | /* | |
199 | * Free DMA | |
200 | */ | |
201 | queue_for_each(rt2x00dev, queue) | |
58959bdc | 202 | rt2x00mmio_free_queue_dma(rt2x00dev, queue); |
69a2bac8 | 203 | } |
58959bdc | 204 | EXPORT_SYMBOL_GPL(rt2x00mmio_uninitialize); |
69a2bac8 GJ |
205 | |
206 | /* | |
207 | * rt2x00mmio module information. | |
208 | */ | |
209 | MODULE_AUTHOR(DRV_PROJECT); | |
210 | MODULE_VERSION(DRV_VERSION); | |
211 | MODULE_DESCRIPTION("rt2x00 mmio library"); | |
212 | MODULE_LICENSE("GPL"); |