summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/firewire/fw-ohci.c360
-rw-r--r--drivers/firewire/fw-transaction.h3
2 files changed, 149 insertions, 214 deletions
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c
index 37467923e98b..9e8a8f909303 100644
--- a/drivers/firewire/fw-ohci.c
+++ b/drivers/firewire/fw-ohci.c
@@ -112,25 +112,6 @@ struct context {
struct tasklet_struct tasklet;
};
-struct at_context {
- struct fw_ohci *ohci;
- dma_addr_t descriptor_bus;
- dma_addr_t buffer_bus;
- struct fw_packet *current_packet;
-
- struct list_head list;
-
- struct {
- struct descriptor more;
- __le32 header[4];
- struct descriptor last;
- } d;
-
- u32 regs;
-
- struct tasklet_struct tasklet;
-};
-
#define it_header_sy(v) ((v) << 0)
#define it_header_tcode(v) ((v) << 4)
#define it_header_channel(v) ((v) << 8)
@@ -173,8 +154,8 @@ struct fw_ohci {
struct ar_context ar_request_ctx;
struct ar_context ar_response_ctx;
- struct at_context at_request_ctx;
- struct at_context at_response_ctx;
+ struct context at_request_ctx;
+ struct context at_response_ctx;
u32 it_context_mask;
struct iso_context *it_context_list;
@@ -210,6 +191,8 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card)
#define SELF_ID_BUF_SIZE 0x800
#define OHCI_TCODE_PHY_PACKET 0x0e
#define OHCI_VERSION_1_1 0x010010
+#define ISO_BUFFER_SIZE (64 * 1024)
+#define AT_BUFFER_SIZE 4096
static char ohci_driver_name[] = KBUILD_MODNAME;
@@ -587,210 +570,166 @@ static void context_stop(struct context *ctx)
}
}
-static void
-do_packet_callbacks(struct fw_ohci *ohci, struct list_head *list)
-{
- struct fw_packet *p, *next;
-
- list_for_each_entry_safe(p, next, list, link)
- p->callback(p, &ohci->card, p->ack);
-}
-
-static void
-complete_transmission(struct fw_packet *packet,
- int ack, struct list_head *list)
-{
- list_move_tail(&packet->link, list);
- packet->ack = ack;
-}
+struct driver_data {
+ struct fw_packet *packet;
+};
-/* This function prepares the first packet in the context queue for
- * transmission. Must always be called with the ochi->lock held to
- * ensure proper generation handling and locking around packet queue
- * manipulation. */
-static void
-at_context_setup_packet(struct at_context *ctx, struct list_head *list)
+/* This function apppends a packet to the DMA queue for transmission.
+ * Must always be called with the ochi->lock held to ensure proper
+ * generation handling and locking around packet queue manipulation. */
+static int
+at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
{
- struct fw_packet *packet;
struct fw_ohci *ohci = ctx->ohci;
+ dma_addr_t d_bus, payload_bus;
+ struct driver_data *driver_data;
+ struct descriptor *d, *last;
+ __le32 *header;
int z, tcode;
+ u32 reg;
- packet = fw_packet(ctx->list.next);
-
- memset(&ctx->d, 0, sizeof ctx->d);
- if (packet->payload_length > 0) {
- packet->payload_bus = dma_map_single(ohci->card.device,
- packet->payload,
- packet->payload_length,
- DMA_TO_DEVICE);
- if (dma_mapping_error(packet->payload_bus)) {
- complete_transmission(packet, RCODE_SEND_ERROR, list);
- return;
- }
-
- ctx->d.more.control =
- cpu_to_le16(descriptor_output_more |
- descriptor_key_immediate);
- ctx->d.more.req_count = cpu_to_le16(packet->header_length);
- ctx->d.more.res_count = cpu_to_le16(packet->timestamp);
- ctx->d.last.control =
- cpu_to_le16(descriptor_output_last |
- descriptor_irq_always |
- descriptor_branch_always);
- ctx->d.last.req_count = cpu_to_le16(packet->payload_length);
- ctx->d.last.data_address = cpu_to_le32(packet->payload_bus);
- z = 3;
- } else {
- ctx->d.more.control =
- cpu_to_le16(descriptor_output_last |
- descriptor_key_immediate |
- descriptor_irq_always |
- descriptor_branch_always);
- ctx->d.more.req_count = cpu_to_le16(packet->header_length);
- ctx->d.more.res_count = cpu_to_le16(packet->timestamp);
- z = 2;
+ d = context_get_descriptors(ctx, 4, &d_bus);
+ if (d == NULL) {
+ packet->ack = RCODE_SEND_ERROR;
+ return -1;
}
+ d[0].control = cpu_to_le16(descriptor_key_immediate);
+ d[0].res_count = cpu_to_le16(packet->timestamp);
+
/* The DMA format for asyncronous link packets is different
* from the IEEE1394 layout, so shift the fields around
* accordingly. If header_length is 8, it's a PHY packet, to
* which we need to prepend an extra quadlet. */
+
+ header = (__le32 *) &d[1];
if (packet->header_length > 8) {
- ctx->d.header[0] = cpu_to_le32((packet->header[0] & 0xffff) |
- (packet->speed << 16));
- ctx->d.header[1] = cpu_to_le32((packet->header[1] & 0xffff) |
- (packet->header[0] & 0xffff0000));
- ctx->d.header[2] = cpu_to_le32(packet->header[2]);
+ header[0] = cpu_to_le32((packet->header[0] & 0xffff) |
+ (packet->speed << 16));
+ header[1] = cpu_to_le32((packet->header[1] & 0xffff) |
+ (packet->header[0] & 0xffff0000));
+ header[2] = cpu_to_le32(packet->header[2]);
tcode = (packet->header[0] >> 4) & 0x0f;
if (TCODE_IS_BLOCK_PACKET(tcode))
- ctx->d.header[3] = cpu_to_le32(packet->header[3]);
+ header[3] = cpu_to_le32(packet->header[3]);
else
- ctx->d.header[3] = packet->header[3];
+ header[3] = (__force __le32) packet->header[3];
+
+ d[0].req_count = cpu_to_le16(packet->header_length);
} else {
- ctx->d.header[0] =
- cpu_to_le32((OHCI1394_phy_tcode << 4) |
- (packet->speed << 16));
- ctx->d.header[1] = cpu_to_le32(packet->header[0]);
- ctx->d.header[2] = cpu_to_le32(packet->header[1]);
- ctx->d.more.req_count = cpu_to_le16(12);
+ header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) |
+ (packet->speed << 16));
+ header[1] = cpu_to_le32(packet->header[0]);
+ header[2] = cpu_to_le32(packet->header[1]);
+ d[0].req_count = cpu_to_le16(12);
}
- /* FIXME: Document how the locking works. */
- if (ohci->generation == packet->generation) {
- reg_write(ctx->ohci, command_ptr(ctx->regs),
- ctx->descriptor_bus | z);
- reg_write(ctx->ohci, control_set(ctx->regs),
- CONTEXT_RUN | CONTEXT_WAKE);
- ctx->current_packet = packet;
+ driver_data = (struct driver_data *) &d[3];
+ driver_data->packet = packet;
+
+ if (packet->payload_length > 0) {
+ payload_bus =
+ dma_map_single(ohci->card.device, packet->payload,
+ packet->payload_length, DMA_TO_DEVICE);
+ if (dma_mapping_error(payload_bus)) {
+ packet->ack = RCODE_SEND_ERROR;
+ return -1;
+ }
+
+ d[2].req_count = cpu_to_le16(packet->payload_length);
+ d[2].data_address = cpu_to_le32(payload_bus);
+ last = &d[2];
+ z = 3;
} else {
- /* We dont return error codes from this function; all
- * transmission errors are reported through the
- * callback. */
- complete_transmission(packet, RCODE_GENERATION, list);
+ last = &d[0];
+ z = 2;
}
-}
-static void at_context_stop(struct at_context *ctx)
-{
- u32 reg;
+ last->control |= cpu_to_le16(descriptor_output_last |
+ descriptor_irq_always |
+ descriptor_branch_always);
- reg_write(ctx->ohci, control_clear(ctx->regs), CONTEXT_RUN);
+ /* FIXME: Document how the locking works. */
+ if (ohci->generation != packet->generation) {
+ packet->ack = RCODE_GENERATION;
+ return -1;
+ }
+
+ context_append(ctx, d, z, 4 - z);
+ /* If the context isn't already running, start it up. */
reg = reg_read(ctx->ohci, control_set(ctx->regs));
- if (reg & CONTEXT_ACTIVE)
- fw_notify("Tried to stop context, but it is still active "
- "(0x%08x).\n", reg);
+ if ((reg & CONTEXT_ACTIVE) == 0)
+ context_run(ctx, 0);
+
+ return 0;
}
-static void at_context_tasklet(unsigned long data)
+static int handle_at_packet(struct context *context,
+ struct descriptor *d,
+ struct descriptor *last)
{
- struct at_context *ctx = (struct at_context *)data;
- struct fw_ohci *ohci = ctx->ohci;
+ struct driver_data *driver_data;
struct fw_packet *packet;
- LIST_HEAD(list);
- unsigned long flags;
+ struct fw_ohci *ohci = context->ohci;
+ dma_addr_t payload_bus;
int evt;
- spin_lock_irqsave(&ohci->lock, flags);
-
- packet = fw_packet(ctx->list.next);
-
- at_context_stop(ctx);
+ if (last->transfer_status == 0)
+ /* This descriptor isn't done yet, stop iteration. */
+ return 0;
- /* If the head of the list isn't the packet that just got
- * transmitted, the packet got cancelled before we finished
- * transmitting it. */
- if (ctx->current_packet != packet)
- goto skip_to_next;
+ driver_data = (struct driver_data *) &d[3];
+ packet = driver_data->packet;
+ if (packet == NULL)
+ /* This packet was cancelled, just continue. */
+ return 1;
- if (packet->payload_length > 0) {
- dma_unmap_single(ohci->card.device, packet->payload_bus,
+ payload_bus = le32_to_cpu(last->data_address);
+ if (payload_bus != 0)
+ dma_unmap_single(ohci->card.device, payload_bus,
packet->payload_length, DMA_TO_DEVICE);
- evt = le16_to_cpu(ctx->d.last.transfer_status) & 0x1f;
- packet->timestamp = le16_to_cpu(ctx->d.last.res_count);
- }
- else {
- evt = le16_to_cpu(ctx->d.more.transfer_status) & 0x1f;
- packet->timestamp = le16_to_cpu(ctx->d.more.res_count);
- }
-
- if (evt < 16) {
- switch (evt) {
- case OHCI1394_evt_timeout:
- /* Async response transmit timed out. */
- complete_transmission(packet, RCODE_CANCELLED, &list);
- break;
-
- case OHCI1394_evt_flushed:
- /* The packet was flushed should give same
- * error as when we try to use a stale
- * generation count. */
- complete_transmission(packet,
- RCODE_GENERATION, &list);
- break;
-
- case OHCI1394_evt_missing_ack:
- /* Using a valid (current) generation count,
- * but the node is not on the bus or not
- * sending acks. */
- complete_transmission(packet, RCODE_NO_ACK, &list);
- break;
-
- default:
- complete_transmission(packet, RCODE_SEND_ERROR, &list);
- break;
- }
- } else
- complete_transmission(packet, evt - 16, &list);
- skip_to_next:
- /* If more packets are queued, set up the next one. */
- if (!list_empty(&ctx->list))
- at_context_setup_packet(ctx, &list);
+ evt = le16_to_cpu(last->transfer_status) & 0x1f;
+ packet->timestamp = le16_to_cpu(last->res_count);
- spin_unlock_irqrestore(&ohci->lock, flags);
+ switch (evt) {
+ case OHCI1394_evt_timeout:
+ /* Async response transmit timed out. */
+ packet->ack = RCODE_CANCELLED;
+ break;
- do_packet_callbacks(ohci, &list);
-}
+ case OHCI1394_evt_flushed:
+ /* The packet was flushed should give same error as
+ * when we try to use a stale generation count. */
+ packet->ack = RCODE_GENERATION;
+ break;
-static int
-at_context_init(struct at_context *ctx, struct fw_ohci *ohci, u32 regs)
-{
- INIT_LIST_HEAD(&ctx->list);
+ case OHCI1394_evt_missing_ack:
+ /* Using a valid (current) generation count, but the
+ * node is not on the bus or not sending acks. */
+ packet->ack = RCODE_NO_ACK;
+ break;
- ctx->descriptor_bus =
- dma_map_single(ohci->card.device, &ctx->d,
- sizeof ctx->d, DMA_TO_DEVICE);
- if (dma_mapping_error(ctx->descriptor_bus))
- return -ENOMEM;
+ case ACK_COMPLETE + 0x10:
+ case ACK_PENDING + 0x10:
+ case ACK_BUSY_X + 0x10:
+ case ACK_BUSY_A + 0x10:
+ case ACK_BUSY_B + 0x10:
+ case ACK_DATA_ERROR + 0x10:
+ case ACK_TYPE_ERROR + 0x10:
+ packet->ack = evt - 0x10;
+ break;
- ctx->regs = regs;
- ctx->ohci = ohci;
+ default:
+ packet->ack = RCODE_SEND_ERROR;
+ break;
+ }
- tasklet_init(&ctx->tasklet, at_context_tasklet, (unsigned long)ctx);
+ packet->callback(packet, &ohci->card, packet->ack);
- return 0;
+ return 1;
}
#define header_get_destination(q) (((q) >> 16) & 0xffff)
@@ -869,7 +808,7 @@ handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
}
static void
-handle_local_request(struct at_context *ctx, struct fw_packet *packet)
+handle_local_request(struct context *ctx, struct fw_packet *packet)
{
u64 offset;
u32 csr;
@@ -903,10 +842,10 @@ handle_local_request(struct at_context *ctx, struct fw_packet *packet)
}
static void
-at_context_transmit(struct at_context *ctx, struct fw_packet *packet)
+at_context_transmit(struct context *ctx, struct fw_packet *packet)
{
- LIST_HEAD(list);
unsigned long flags;
+ int retval;
spin_lock_irqsave(&ctx->ohci->lock, flags);
@@ -917,13 +856,12 @@ at_context_transmit(struct at_context *ctx, struct fw_packet *packet)
return;
}
- list_add_tail(&packet->link, &ctx->list);
- if (ctx->list.next == &packet->link)
- at_context_setup_packet(ctx, &list);
-
+ retval = at_context_queue_packet(ctx, packet);
spin_unlock_irqrestore(&ctx->ohci->lock, flags);
- do_packet_callbacks(ctx->ohci, &list);
+ if (retval < 0)
+ packet->callback(packet, &ctx->ohci->card, packet->ack);
+
}
static void bus_reset_tasklet(unsigned long data)
@@ -977,8 +915,8 @@ static void bus_reset_tasklet(unsigned long data)
spin_lock_irqsave(&ohci->lock, flags);
ohci->generation = generation;
- at_context_stop(&ohci->at_request_ctx);
- at_context_stop(&ohci->at_response_ctx);
+ context_stop(&ohci->at_request_ctx);
+ context_stop(&ohci->at_response_ctx);
reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
/* This next bit is unrelated to the AT context stuff but we
@@ -1216,24 +1154,24 @@ static void ohci_send_response(struct fw_card *card, struct fw_packet *packet)
static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
{
struct fw_ohci *ohci = fw_ohci(card);
- LIST_HEAD(list);
- unsigned long flags;
-
- spin_lock_irqsave(&ohci->lock, flags);
+ struct context *ctx = &ohci->at_request_ctx;
+ struct driver_data *driver_data = packet->driver_data;
+ int retval = -ENOENT;
- if (packet->ack == 0) {
- fw_notify("cancelling packet %p (header[0]=%08x)\n",
- packet, packet->header[0]);
+ tasklet_disable(&ctx->tasklet);
- complete_transmission(packet, RCODE_CANCELLED, &list);
- }
+ if (packet->ack != 0)
+ goto out;
- spin_unlock_irqrestore(&ohci->lock, flags);
+ driver_data->packet = NULL;
+ packet->ack = RCODE_CANCELLED;
+ packet->callback(packet, &ohci->card, packet->ack);
+ retval = 0;
- do_packet_callbacks(ohci, &list);
+ out:
+ tasklet_enable(&ctx->tasklet);
- /* Return success if we actually cancelled something. */
- return list_empty(&list) ? -ENOENT : 0;
+ return retval;
}
static int
@@ -1314,8 +1252,6 @@ static int handle_ir_dualbuffer_packet(struct context *context,
return 1;
}
-#define ISO_BUFFER_SIZE (64 * 1024)
-
static int handle_it_packet(struct context *context,
struct descriptor *d,
struct descriptor *last)
@@ -1872,11 +1808,11 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
ar_context_init(&ohci->ar_response_ctx, ohci,
OHCI1394_AsRspRcvContextControlSet);
- at_context_init(&ohci->at_request_ctx, ohci,
- OHCI1394_AsReqTrContextControlSet);
+ context_init(&ohci->at_request_ctx, ohci, AT_BUFFER_SIZE,
+ OHCI1394_AsReqTrContextControlSet, handle_at_packet);
- at_context_init(&ohci->at_response_ctx, ohci,
- OHCI1394_AsRspTrContextControlSet);
+ context_init(&ohci->at_response_ctx, ohci, AT_BUFFER_SIZE,
+ OHCI1394_AsRspTrContextControlSet, handle_at_packet);
reg_write(ohci, OHCI1394_ATRetries,
OHCI1394_MAX_AT_REQ_RETRIES |
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h
index b0d057533fb0..e7301b83f91e 100644
--- a/drivers/firewire/fw-transaction.h
+++ b/drivers/firewire/fw-transaction.h
@@ -203,8 +203,6 @@ struct fw_packet {
size_t payload_length;
u32 timestamp;
- dma_addr_t payload_bus;
-
/* This callback is called when the packet transmission has
* completed; for successful transmission, the status code is
* the ack received from the destination, otherwise it's a
@@ -215,6 +213,7 @@ struct fw_packet {
fw_packet_callback_t callback;
int ack;
struct list_head link;
+ void *driver_data;
};
struct fw_transaction {
OpenPOWER on IntegriCloud