diff options
author | Kristian Høgsberg <krh@redhat.com> | 2007-02-16 17:34:40 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-03-09 16:02:58 -0500 |
commit | 295e3feb92e5073ec32a3c626302d4b92c4c8a95 (patch) | |
tree | 58c581080526c4b27401d3c2f4e52595d78cc4e8 /drivers/firewire/fw-ohci.c | |
parent | 30200739e612932739cc34baf588b39bacc2f427 (diff) |
firewire: Implement basic isochronous receive functionality.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/fw-ohci.c')
-rw-r--r-- | drivers/firewire/fw-ohci.c | 168 |
1 files changed, 149 insertions, 19 deletions
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 86fe55cff28a..90db5a4f3aee 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #define descriptor_irq_error (1 << 4) | 45 | #define descriptor_irq_error (1 << 4) |
46 | #define descriptor_irq_always (3 << 4) | 46 | #define descriptor_irq_always (3 << 4) |
47 | #define descriptor_branch_always (3 << 2) | 47 | #define descriptor_branch_always (3 << 2) |
48 | #define descriptor_wait (3 << 0) | ||
48 | 49 | ||
49 | struct descriptor { | 50 | struct descriptor { |
50 | __le16 req_count; | 51 | __le16 req_count; |
@@ -55,6 +56,20 @@ struct descriptor { | |||
55 | __le16 transfer_status; | 56 | __le16 transfer_status; |
56 | } __attribute__((aligned(16))); | 57 | } __attribute__((aligned(16))); |
57 | 58 | ||
59 | struct db_descriptor { | ||
60 | __le16 first_size; | ||
61 | __le16 control; | ||
62 | __le16 second_req_count; | ||
63 | __le16 first_req_count; | ||
64 | __le32 branch_address; | ||
65 | __le16 second_res_count; | ||
66 | __le16 first_res_count; | ||
67 | __le32 reserved0; | ||
68 | __le32 first_buffer; | ||
69 | __le32 second_buffer; | ||
70 | __le32 reserved1; | ||
71 | } __attribute__((aligned(16))); | ||
72 | |||
58 | #define control_set(regs) (regs) | 73 | #define control_set(regs) (regs) |
59 | #define control_clear(regs) ((regs) + 4) | 74 | #define control_clear(regs) ((regs) + 4) |
60 | #define command_ptr(regs) ((regs) + 12) | 75 | #define command_ptr(regs) ((regs) + 12) |
@@ -171,7 +186,12 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) | |||
171 | return container_of(card, struct fw_ohci, card); | 186 | return container_of(card, struct fw_ohci, card); |
172 | } | 187 | } |
173 | 188 | ||
174 | #define CONTEXT_CYCLE_MATCH_ENABLE 0x80000000 | 189 | #define IT_CONTEXT_CYCLE_MATCH_ENABLE 0x80000000 |
190 | #define IR_CONTEXT_BUFFER_FILL 0x80000000 | ||
191 | #define IR_CONTEXT_ISOCH_HEADER 0x40000000 | ||
192 | #define IR_CONTEXT_CYCLE_MATCH_ENABLE 0x20000000 | ||
193 | #define IR_CONTEXT_MULTI_CHANNEL_MODE 0x10000000 | ||
194 | #define IR_CONTEXT_DUAL_BUFFER_MODE 0x08000000 | ||
175 | 195 | ||
176 | #define CONTEXT_RUN 0x8000 | 196 | #define CONTEXT_RUN 0x8000 |
177 | #define CONTEXT_WAKE 0x1000 | 197 | #define CONTEXT_WAKE 0x1000 |
@@ -518,14 +538,14 @@ context_get_descriptors(struct context *ctx, int z, dma_addr_t *d_bus) | |||
518 | return d; | 538 | return d; |
519 | } | 539 | } |
520 | 540 | ||
521 | static void context_run(struct context *ctx, u32 cycle_match) | 541 | static void context_run(struct context *ctx, u32 extra) |
522 | { | 542 | { |
523 | struct fw_ohci *ohci = ctx->ohci; | 543 | struct fw_ohci *ohci = ctx->ohci; |
524 | 544 | ||
525 | reg_write(ohci, command_ptr(ctx->regs), | 545 | reg_write(ohci, command_ptr(ctx->regs), |
526 | le32_to_cpu(ctx->tail_descriptor_last->branch_address)); | 546 | le32_to_cpu(ctx->tail_descriptor_last->branch_address)); |
527 | reg_write(ohci, control_clear(ctx->regs), ~0); | 547 | reg_write(ohci, control_clear(ctx->regs), ~0); |
528 | reg_write(ohci, control_set(ctx->regs), CONTEXT_RUN | cycle_match); | 548 | reg_write(ohci, control_set(ctx->regs), CONTEXT_RUN | extra); |
529 | flush_writes(ohci); | 549 | flush_writes(ohci); |
530 | } | 550 | } |
531 | 551 | ||
@@ -1240,11 +1260,25 @@ ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation) | |||
1240 | return retval; | 1260 | return retval; |
1241 | } | 1261 | } |
1242 | 1262 | ||
1243 | static void ir_context_tasklet(unsigned long data) | 1263 | static int handle_ir_packet(struct context *context, |
1264 | struct descriptor *d, | ||
1265 | struct descriptor *last) | ||
1244 | { | 1266 | { |
1245 | struct iso_context *ctx = (struct iso_context *)data; | 1267 | struct iso_context *ctx = |
1268 | container_of(context, struct iso_context, context); | ||
1269 | struct db_descriptor *db = (struct db_descriptor *) d; | ||
1270 | |||
1271 | if (db->first_res_count > 0 && db->second_res_count > 0) | ||
1272 | /* This descriptor isn't done yet, stop iteration. */ | ||
1273 | return 0; | ||
1274 | |||
1275 | if (le16_to_cpu(db->control) & descriptor_irq_always) | ||
1276 | /* FIXME: we should pass payload address here. */ | ||
1277 | ctx->base.callback(&ctx->base, | ||
1278 | 0, 0, | ||
1279 | ctx->base.callback_data); | ||
1246 | 1280 | ||
1247 | (void)ctx; | 1281 | return 1; |
1248 | } | 1282 | } |
1249 | 1283 | ||
1250 | #define ISO_BUFFER_SIZE (64 * 1024) | 1284 | #define ISO_BUFFER_SIZE (64 * 1024) |
@@ -1274,7 +1308,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type) | |||
1274 | struct fw_ohci *ohci = fw_ohci(card); | 1308 | struct fw_ohci *ohci = fw_ohci(card); |
1275 | struct iso_context *ctx, *list; | 1309 | struct iso_context *ctx, *list; |
1276 | descriptor_callback_t callback; | 1310 | descriptor_callback_t callback; |
1277 | u32 *mask; | 1311 | u32 *mask, regs; |
1278 | unsigned long flags; | 1312 | unsigned long flags; |
1279 | int index, retval; | 1313 | int index, retval; |
1280 | 1314 | ||
@@ -1283,7 +1317,9 @@ ohci_allocate_iso_context(struct fw_card *card, int type) | |||
1283 | list = ohci->it_context_list; | 1317 | list = ohci->it_context_list; |
1284 | callback = handle_it_packet; | 1318 | callback = handle_it_packet; |
1285 | } else { | 1319 | } else { |
1286 | return ERR_PTR(-EINVAL); | 1320 | mask = &ohci->ir_context_mask; |
1321 | list = ohci->ir_context_list; | ||
1322 | callback = handle_ir_packet; | ||
1287 | } | 1323 | } |
1288 | 1324 | ||
1289 | spin_lock_irqsave(&ohci->lock, flags); | 1325 | spin_lock_irqsave(&ohci->lock, flags); |
@@ -1295,10 +1331,15 @@ ohci_allocate_iso_context(struct fw_card *card, int type) | |||
1295 | if (index < 0) | 1331 | if (index < 0) |
1296 | return ERR_PTR(-EBUSY); | 1332 | return ERR_PTR(-EBUSY); |
1297 | 1333 | ||
1334 | if (type == FW_ISO_CONTEXT_TRANSMIT) | ||
1335 | regs = OHCI1394_IsoXmitContextBase(index); | ||
1336 | else | ||
1337 | regs = OHCI1394_IsoRcvContextBase(index); | ||
1338 | |||
1298 | ctx = &list[index]; | 1339 | ctx = &list[index]; |
1299 | memset(ctx, 0, sizeof *ctx); | 1340 | memset(ctx, 0, sizeof *ctx); |
1300 | retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE, | 1341 | retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE, |
1301 | OHCI1394_IsoXmitContextBase(index), callback); | 1342 | regs, callback); |
1302 | if (retval < 0) { | 1343 | if (retval < 0) { |
1303 | spin_lock_irqsave(&ohci->lock, flags); | 1344 | spin_lock_irqsave(&ohci->lock, flags); |
1304 | *mask |= 1 << index; | 1345 | *mask |= 1 << index; |
@@ -1316,13 +1357,24 @@ static int ohci_send_iso(struct fw_iso_context *base, s32 cycle) | |||
1316 | u32 cycle_match = 0; | 1357 | u32 cycle_match = 0; |
1317 | int index; | 1358 | int index; |
1318 | 1359 | ||
1319 | index = ctx - ohci->it_context_list; | 1360 | if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { |
1320 | if (cycle > 0) | 1361 | index = ctx - ohci->it_context_list; |
1321 | cycle_match = CONTEXT_CYCLE_MATCH_ENABLE | | 1362 | if (cycle > 0) |
1322 | (cycle & 0x7fff) << 16; | 1363 | cycle_match = IT_CONTEXT_CYCLE_MATCH_ENABLE | |
1364 | (cycle & 0x7fff) << 16; | ||
1365 | |||
1366 | reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 1 << index); | ||
1367 | reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index); | ||
1368 | context_run(&ctx->context, cycle_match); | ||
1369 | } else { | ||
1370 | index = ctx - ohci->ir_context_list; | ||
1323 | 1371 | ||
1324 | reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index); | 1372 | reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 1 << index); |
1325 | context_run(&ctx->context, cycle_match); | 1373 | reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << index); |
1374 | reg_write(ohci, context_match(ctx->context.regs), | ||
1375 | 0xf0000000 | ctx->base.channel); | ||
1376 | context_run(&ctx->context, IR_CONTEXT_DUAL_BUFFER_MODE); | ||
1377 | } | ||
1326 | 1378 | ||
1327 | return 0; | 1379 | return 0; |
1328 | } | 1380 | } |
@@ -1355,10 +1407,10 @@ static void ohci_free_iso_context(struct fw_iso_context *base) | |||
1355 | } | 1407 | } |
1356 | 1408 | ||
1357 | static int | 1409 | static int |
1358 | ohci_queue_iso(struct fw_iso_context *base, | 1410 | ohci_queue_iso_transmit(struct fw_iso_context *base, |
1359 | struct fw_iso_packet *packet, | 1411 | struct fw_iso_packet *packet, |
1360 | struct fw_iso_buffer *buffer, | 1412 | struct fw_iso_buffer *buffer, |
1361 | unsigned long payload) | 1413 | unsigned long payload) |
1362 | { | 1414 | { |
1363 | struct iso_context *ctx = container_of(base, struct iso_context, base); | 1415 | struct iso_context *ctx = container_of(base, struct iso_context, base); |
1364 | struct descriptor *d, *last, *pd; | 1416 | struct descriptor *d, *last, *pd; |
@@ -1451,6 +1503,84 @@ ohci_queue_iso(struct fw_iso_context *base, | |||
1451 | return 0; | 1503 | return 0; |
1452 | } | 1504 | } |
1453 | 1505 | ||
1506 | static int | ||
1507 | ohci_queue_iso_receive(struct fw_iso_context *base, | ||
1508 | struct fw_iso_packet *packet, | ||
1509 | struct fw_iso_buffer *buffer, | ||
1510 | unsigned long payload) | ||
1511 | { | ||
1512 | struct iso_context *ctx = container_of(base, struct iso_context, base); | ||
1513 | struct db_descriptor *db = NULL; | ||
1514 | struct descriptor *d; | ||
1515 | struct fw_iso_packet *p; | ||
1516 | dma_addr_t d_bus, page_bus; | ||
1517 | u32 z, header_z, length, rest; | ||
1518 | int page, offset; | ||
1519 | |||
1520 | /* FIXME: Cycle lost behavior should be configurable: lose | ||
1521 | * packet, retransmit or terminate.. */ | ||
1522 | |||
1523 | p = packet; | ||
1524 | z = 2; | ||
1525 | |||
1526 | /* Get header size in number of descriptors. */ | ||
1527 | header_z = DIV_ROUND_UP(p->header_length, sizeof *d); | ||
1528 | page = payload >> PAGE_SHIFT; | ||
1529 | offset = payload & ~PAGE_MASK; | ||
1530 | rest = p->payload_length; | ||
1531 | |||
1532 | /* FIXME: OHCI 1.0 doesn't support dual buffer receive */ | ||
1533 | /* FIXME: handle descriptor_wait */ | ||
1534 | /* FIXME: make packet-per-buffer/dual-buffer a context option */ | ||
1535 | while (rest > 0) { | ||
1536 | d = context_get_descriptors(&ctx->context, | ||
1537 | z + header_z, &d_bus); | ||
1538 | if (d == NULL) | ||
1539 | return -ENOMEM; | ||
1540 | |||
1541 | db = (struct db_descriptor *) d; | ||
1542 | db->control = cpu_to_le16(descriptor_status | | ||
1543 | descriptor_branch_always); | ||
1544 | db->first_size = cpu_to_le16(ctx->base.header_size); | ||
1545 | db->first_req_count = cpu_to_le16(p->header_length); | ||
1546 | db->second_req_count = cpu_to_le16(p->payload_length); | ||
1547 | db->first_res_count = cpu_to_le16(db->first_req_count); | ||
1548 | db->second_res_count = cpu_to_le16(db->second_req_count); | ||
1549 | |||
1550 | db->first_buffer = cpu_to_le32(d_bus + sizeof *db); | ||
1551 | |||
1552 | if (offset + rest < PAGE_SIZE) | ||
1553 | length = rest; | ||
1554 | else | ||
1555 | length = PAGE_SIZE - offset; | ||
1556 | |||
1557 | page_bus = page_private(buffer->pages[page]); | ||
1558 | db->second_buffer = cpu_to_le32(page_bus + offset); | ||
1559 | |||
1560 | context_append(&ctx->context, d, z, header_z); | ||
1561 | offset = (offset + length) & ~PAGE_MASK; | ||
1562 | rest -= length; | ||
1563 | page++; | ||
1564 | } | ||
1565 | |||
1566 | if (p->interrupt) | ||
1567 | db->control |= cpu_to_le16(descriptor_irq_always); | ||
1568 | |||
1569 | return 0; | ||
1570 | } | ||
1571 | |||
1572 | static int | ||
1573 | ohci_queue_iso(struct fw_iso_context *base, | ||
1574 | struct fw_iso_packet *packet, | ||
1575 | struct fw_iso_buffer *buffer, | ||
1576 | unsigned long payload) | ||
1577 | { | ||
1578 | if (base->type == FW_ISO_CONTEXT_TRANSMIT) | ||
1579 | return ohci_queue_iso_transmit(base, packet, buffer, payload); | ||
1580 | else | ||
1581 | return ohci_queue_iso_receive(base, packet, buffer, payload); | ||
1582 | } | ||
1583 | |||
1454 | static const struct fw_card_driver ohci_driver = { | 1584 | static const struct fw_card_driver ohci_driver = { |
1455 | .name = ohci_driver_name, | 1585 | .name = ohci_driver_name, |
1456 | .enable = ohci_enable, | 1586 | .enable = ohci_enable, |