1
2
3
4
5
6
7
8
9
10#include <linux/dma-mapping.h>
11#include <linux/errno.h>
12#include <linux/firewire.h>
13#include <linux/firewire-constants.h>
14#include <linux/kernel.h>
15#include <linux/mm.h>
16#include <linux/slab.h>
17#include <linux/spinlock.h>
18#include <linux/vmalloc.h>
19#include <linux/export.h>
20
21#include <asm/byteorder.h>
22
23#include "core.h"
24
25
26
27
28
29int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count)
30{
31 int i;
32
33 buffer->page_count = 0;
34 buffer->page_count_mapped = 0;
35 buffer->pages = kmalloc_array(page_count, sizeof(buffer->pages[0]),
36 GFP_KERNEL);
37 if (buffer->pages == NULL)
38 return -ENOMEM;
39
40 for (i = 0; i < page_count; i++) {
41 buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
42 if (buffer->pages[i] == NULL)
43 break;
44 }
45 buffer->page_count = i;
46 if (i < page_count) {
47 fw_iso_buffer_destroy(buffer, NULL);
48 return -ENOMEM;
49 }
50
51 return 0;
52}
53
54int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
55 enum dma_data_direction direction)
56{
57 dma_addr_t address;
58 int i;
59
60 buffer->direction = direction;
61
62 for (i = 0; i < buffer->page_count; i++) {
63 address = dma_map_page(card->device, buffer->pages[i],
64 0, PAGE_SIZE, direction);
65 if (dma_mapping_error(card->device, address))
66 break;
67
68 set_page_private(buffer->pages[i], address);
69 }
70 buffer->page_count_mapped = i;
71 if (i < buffer->page_count)
72 return -ENOMEM;
73
74 return 0;
75}
76
77int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
78 int page_count, enum dma_data_direction direction)
79{
80 int ret;
81
82 ret = fw_iso_buffer_alloc(buffer, page_count);
83 if (ret < 0)
84 return ret;
85
86 ret = fw_iso_buffer_map_dma(buffer, card, direction);
87 if (ret < 0)
88 fw_iso_buffer_destroy(buffer, card);
89
90 return ret;
91}
92EXPORT_SYMBOL(fw_iso_buffer_init);
93
94void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
95 struct fw_card *card)
96{
97 int i;
98 dma_addr_t address;
99
100 for (i = 0; i < buffer->page_count_mapped; i++) {
101 address = page_private(buffer->pages[i]);
102 dma_unmap_page(card->device, address,
103 PAGE_SIZE, buffer->direction);
104 }
105 for (i = 0; i < buffer->page_count; i++)
106 __free_page(buffer->pages[i]);
107
108 kfree(buffer->pages);
109 buffer->pages = NULL;
110 buffer->page_count = 0;
111 buffer->page_count_mapped = 0;
112}
113EXPORT_SYMBOL(fw_iso_buffer_destroy);
114
115
116size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
117{
118 size_t i;
119 dma_addr_t address;
120 ssize_t offset;
121
122 for (i = 0; i < buffer->page_count; i++) {
123 address = page_private(buffer->pages[i]);
124 offset = (ssize_t)completed - (ssize_t)address;
125 if (offset > 0 && offset <= PAGE_SIZE)
126 return (i << PAGE_SHIFT) + offset;
127 }
128
129 return 0;
130}
131
132struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
133 int type, int channel, int speed, size_t header_size,
134 fw_iso_callback_t callback, void *callback_data)
135{
136 struct fw_iso_context *ctx;
137
138 ctx = card->driver->allocate_iso_context(card,
139 type, channel, header_size);
140 if (IS_ERR(ctx))
141 return ctx;
142
143 ctx->card = card;
144 ctx->type = type;
145 ctx->channel = channel;
146 ctx->speed = speed;
147 ctx->header_size = header_size;
148 ctx->callback.sc = callback;
149 ctx->callback_data = callback_data;
150
151 return ctx;
152}
153EXPORT_SYMBOL(fw_iso_context_create);
154
155void fw_iso_context_destroy(struct fw_iso_context *ctx)
156{
157 ctx->card->driver->free_iso_context(ctx);
158}
159EXPORT_SYMBOL(fw_iso_context_destroy);
160
161int fw_iso_context_start(struct fw_iso_context *ctx,
162 int cycle, int sync, int tags)
163{
164 return ctx->card->driver->start_iso(ctx, cycle, sync, tags);
165}
166EXPORT_SYMBOL(fw_iso_context_start);
167
168int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels)
169{
170 return ctx->card->driver->set_iso_channels(ctx, channels);
171}
172
173int fw_iso_context_queue(struct fw_iso_context *ctx,
174 struct fw_iso_packet *packet,
175 struct fw_iso_buffer *buffer,
176 unsigned long payload)
177{
178 return ctx->card->driver->queue_iso(ctx, packet, buffer, payload);
179}
180EXPORT_SYMBOL(fw_iso_context_queue);
181
182void fw_iso_context_queue_flush(struct fw_iso_context *ctx)
183{
184 ctx->card->driver->flush_queue_iso(ctx);
185}
186EXPORT_SYMBOL(fw_iso_context_queue_flush);
187
188int fw_iso_context_flush_completions(struct fw_iso_context *ctx)
189{
190 return ctx->card->driver->flush_iso_completions(ctx);
191}
192EXPORT_SYMBOL(fw_iso_context_flush_completions);
193
194int fw_iso_context_stop(struct fw_iso_context *ctx)
195{
196 return ctx->card->driver->stop_iso(ctx);
197}
198EXPORT_SYMBOL(fw_iso_context_stop);
199
200
201
202
203
204static int manage_bandwidth(struct fw_card *card, int irm_id, int generation,
205 int bandwidth, bool allocate)
206{
207 int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0;
208 __be32 data[2];
209
210
211
212
213
214
215 for (try = 0; try < 5; try++) {
216 new = allocate ? old - bandwidth : old + bandwidth;
217 if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL)
218 return -EBUSY;
219
220 data[0] = cpu_to_be32(old);
221 data[1] = cpu_to_be32(new);
222 switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
223 irm_id, generation, SCODE_100,
224 CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
225 data, 8)) {
226 case RCODE_GENERATION:
227
228 return allocate ? -EAGAIN : bandwidth;
229
230 case RCODE_COMPLETE:
231 if (be32_to_cpup(data) == old)
232 return bandwidth;
233
234 old = be32_to_cpup(data);
235
236 }
237 }
238
239 return -EIO;
240}
241
242static int manage_channel(struct fw_card *card, int irm_id, int generation,
243 u32 channels_mask, u64 offset, bool allocate)
244{
245 __be32 bit, all, old;
246 __be32 data[2];
247 int channel, ret = -EIO, retry = 5;
248
249 old = all = allocate ? cpu_to_be32(~0) : 0;
250
251 for (channel = 0; channel < 32; channel++) {
252 if (!(channels_mask & 1 << channel))
253 continue;
254
255 ret = -EBUSY;
256
257 bit = cpu_to_be32(1 << (31 - channel));
258 if ((old & bit) != (all & bit))
259 continue;
260
261 data[0] = old;
262 data[1] = old ^ bit;
263 switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
264 irm_id, generation, SCODE_100,
265 offset, data, 8)) {
266 case RCODE_GENERATION:
267
268 return allocate ? -EAGAIN : channel;
269
270 case RCODE_COMPLETE:
271 if (data[0] == old)
272 return channel;
273
274 old = data[0];
275
276
277 if ((data[0] & bit) == (data[1] & bit))
278 continue;
279
280 fallthrough;
281 default:
282 if (retry) {
283 retry--;
284 channel--;
285 } else {
286 ret = -EIO;
287 }
288 }
289 }
290
291 return ret;
292}
293
294static void deallocate_channel(struct fw_card *card, int irm_id,
295 int generation, int channel)
296{
297 u32 mask;
298 u64 offset;
299
300 mask = channel < 32 ? 1 << channel : 1 << (channel - 32);
301 offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI :
302 CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO;
303
304 manage_channel(card, irm_id, generation, mask, offset, false);
305}
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338void fw_iso_resource_manage(struct fw_card *card, int generation,
339 u64 channels_mask, int *channel, int *bandwidth,
340 bool allocate)
341{
342 u32 channels_hi = channels_mask;
343 u32 channels_lo = channels_mask >> 32;
344 int irm_id, ret, c = -EINVAL;
345
346 spin_lock_irq(&card->lock);
347 irm_id = card->irm_node->node_id;
348 spin_unlock_irq(&card->lock);
349
350 if (channels_hi)
351 c = manage_channel(card, irm_id, generation, channels_hi,
352 CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI,
353 allocate);
354 if (channels_lo && c < 0) {
355 c = manage_channel(card, irm_id, generation, channels_lo,
356 CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO,
357 allocate);
358 if (c >= 0)
359 c += 32;
360 }
361 *channel = c;
362
363 if (allocate && channels_mask != 0 && c < 0)
364 *bandwidth = 0;
365
366 if (*bandwidth == 0)
367 return;
368
369 ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate);
370 if (ret < 0)
371 *bandwidth = 0;
372
373 if (allocate && ret < 0) {
374 if (c >= 0)
375 deallocate_channel(card, irm_id, generation, c);
376 *channel = ret;
377 }
378}
379EXPORT_SYMBOL(fw_iso_resource_manage);
380