X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Ffirewire%2Ffw-ohci.c;h=29285f209dcf818d12b4fdc612c910cbc0a31c68;hb=641f8791f031d6133e5c3e9ce036b3e942416e9d;hp=d6f0644b05d46223243e4cf16f6b8c1de22cd201;hpb=907293d78872ee492ce6a114258dd853ec5082ae;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index d6f0644..29285f2 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -121,6 +121,7 @@ struct fw_ohci { dma_addr_t self_id_bus; __le32 *self_id_cpu; struct tasklet_struct bus_reset_tasklet; + int node_id; int generation; int request_generation; @@ -169,6 +170,8 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) #define OHCI1394_PCI_HCI_Control 0x40 #define SELF_ID_BUF_SIZE 0x800 +#define MAX_STOP_CONTEXT_LOOPS 1000 + static char ohci_driver_name[] = KBUILD_MODNAME; static inline void reg_write(const struct fw_ohci *ohci, int offset, u32 data) @@ -221,24 +224,55 @@ static void ar_context_tasklet(unsigned long data) { struct ar_context *ctx = (struct ar_context *)data; struct fw_ohci *ohci = ctx->ohci; - u32 status; - int length, speed, ack, timestamp, tcode; + struct fw_packet p; + u32 status, length, tcode; + int i; + + /* FIXME: We stop and restart the ar context here, what if we + * stop while a receive is in progress? Maybe we could just + * loop the context back to itself and use it in buffer fill + * mode as intended... */ + reg_write(ctx->ohci, ctx->control_clear, CONTEXT_RUN); /* FIXME: What to do about evt_* errors? */ length = le16_to_cpu(ctx->descriptor.req_count) - le16_to_cpu(ctx->descriptor.res_count) - 4; status = le32_to_cpu(ctx->buffer[length / 4]); - ack = ((status >> 16) & 0x1f) - 16; - speed = (status >> 21) & 0x7; - timestamp = status & 0xffff; - ctx->buffer[0] = le32_to_cpu(ctx->buffer[0]); - ctx->buffer[1] = le32_to_cpu(ctx->buffer[1]); - ctx->buffer[2] = le32_to_cpu(ctx->buffer[2]); + p.ack = ((status >> 16) & 0x1f) - 16; + p.speed = (status >> 21) & 0x7; + p.timestamp = status & 0xffff; + p.generation = ohci->request_generation; + + p.header[0] = le32_to_cpu(ctx->buffer[0]); + p.header[1] = le32_to_cpu(ctx->buffer[1]); + p.header[2] = le32_to_cpu(ctx->buffer[2]); + + tcode = (p.header[0] >> 4) & 0x0f; + switch (tcode) { + case TCODE_WRITE_QUADLET_REQUEST: + case TCODE_READ_QUADLET_RESPONSE: + p.header[3] = ctx->buffer[3]; + p.header_length = 16; + break; + + case TCODE_WRITE_BLOCK_REQUEST: + case TCODE_READ_BLOCK_REQUEST : + case TCODE_READ_BLOCK_RESPONSE: + case TCODE_LOCK_REQUEST: + case TCODE_LOCK_RESPONSE: + p.header[3] = le32_to_cpu(ctx->buffer[3]); + p.header_length = 16; + break; + + case TCODE_WRITE_RESPONSE: + case TCODE_READ_QUADLET_REQUEST: + p.header_length = 12; + break; + } - tcode = (ctx->buffer[0] >> 4) & 0x0f; - if (TCODE_IS_BLOCK_PACKET(tcode)) - ctx->buffer[3] = le32_to_cpu(ctx->buffer[3]); + p.payload = (void *) ctx->buffer + p.header_length; + p.payload_length = length - p.header_length; /* The OHCI bus reset handler synthesizes a phy packet with * the new generation number when a bus reset happens (see @@ -248,15 +282,12 @@ static void ar_context_tasklet(unsigned long data) * we use the unique tlabel for finding the matching * request. */ - if (ack + 16 == 0x09) + if (p.ack + 16 == 0x09) ohci->request_generation = (ctx->buffer[2] >> 16) & 0xff; else if (ctx == &ohci->ar_request_ctx) - fw_core_handle_request(&ohci->card, speed, ack, timestamp, - ohci->request_generation, - length, ctx->buffer); + fw_core_handle_request(&ohci->card, &p); else - fw_core_handle_response(&ohci->card, speed, ack, timestamp, - length, ctx->buffer); + fw_core_handle_response(&ohci->card, &p); ctx->descriptor.data_address = cpu_to_le32(ctx->buffer_bus); ctx->descriptor.req_count = cpu_to_le16(sizeof ctx->buffer); @@ -265,12 +296,14 @@ static void ar_context_tasklet(unsigned long data) dma_sync_single_for_device(ohci->card.device, ctx->descriptor_bus, sizeof ctx->descriptor_bus, DMA_TO_DEVICE); - /* FIXME: We stop and restart the ar context here, what if we - * stop while a receive is in progress? Maybe we could just - * loop the context back to itself and use it in buffer fill - * mode as intended... */ + /* Make sure the active bit is 0 before we reprogram the DMA. */ + for (i = 0; i < MAX_STOP_CONTEXT_LOOPS; i++) + if (!(reg_read(ctx->ohci, + ctx->control_clear) & CONTEXT_ACTIVE)) + break; + if (i == MAX_STOP_CONTEXT_LOOPS) + fw_error("Failed to stop ar context\n"); - reg_write(ctx->ohci, ctx->control_clear, CONTEXT_RUN); ar_context_run(ctx); } @@ -323,15 +356,15 @@ 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->status); + p->callback(p, &ohci->card, p->ack); } static void complete_transmission(struct fw_packet *packet, - int status, struct list_head *list) + int ack, struct list_head *list) { list_move_tail(&packet->link, list); - packet->status = status; + packet->ack = ack; } /* This function prepares the first packet in the context queue for @@ -354,7 +387,7 @@ at_context_setup_packet(struct at_context *ctx, struct list_head *list) packet->payload_length, DMA_TO_DEVICE); if (packet->payload_bus == 0) { - complete_transmission(packet, -ENOMEM, list); + complete_transmission(packet, RCODE_SEND_ERROR, list); return; } @@ -416,7 +449,7 @@ at_context_setup_packet(struct at_context *ctx, struct list_head *list) /* We dont return error codes from this function; all * transmission errors are reported through the * callback. */ - complete_transmission(packet, -ESTALE, list); + complete_transmission(packet, RCODE_GENERATION, list); } } @@ -462,26 +495,26 @@ static void at_context_tasklet(unsigned long data) switch (evt) { case OHCI1394_evt_timeout: /* Async response transmit timed out. */ - complete_transmission(packet, -ETIMEDOUT, &list); + 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, -ESTALE, &list); + complete_transmission(packet, + RCODE_GENERATION, &list); break; case OHCI1394_evt_missing_ack: - /* This would be a higher level software - * error, it is using a valid (current) - * generation count, but the node is not on - * the bus. */ - complete_transmission(packet, -ENODEV, &list); + /* 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, -EIO, &list); + complete_transmission(packet, RCODE_SEND_ERROR, &list); break; } } else @@ -517,18 +550,132 @@ at_context_init(struct at_context *ctx, struct fw_ohci *ohci, u32 control_set) return 0; } +#define header_get_destination(q) (((q) >> 16) & 0xffff) +#define header_get_tcode(q) (((q) >> 4) & 0x0f) +#define header_get_offset_high(q) (((q) >> 0) & 0xffff) +#define header_get_data_length(q) (((q) >> 16) & 0xffff) +#define header_get_extended_tcode(q) (((q) >> 0) & 0xffff) + +static void +handle_local_rom(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr) +{ + struct fw_packet response; + int tcode, length, i; + + tcode = header_get_tcode(packet->header[0]); + if (TCODE_IS_BLOCK_PACKET(tcode)) + length = header_get_data_length(packet->header[3]); + else + length = 4; + + i = csr - CSR_CONFIG_ROM; + if (i + length > CONFIG_ROM_SIZE) { + fw_fill_response(&response, packet->header, + RCODE_ADDRESS_ERROR, NULL, 0); + } else if (!TCODE_IS_READ_REQUEST(tcode)) { + fw_fill_response(&response, packet->header, + RCODE_TYPE_ERROR, NULL, 0); + } else { + fw_fill_response(&response, packet->header, RCODE_COMPLETE, + (void *) ohci->config_rom + i, length); + } + + fw_core_handle_response(&ohci->card, &response); +} + +static void +handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr) +{ + struct fw_packet response; + int tcode, length, ext_tcode, sel; + __be32 *payload, lock_old; + u32 lock_arg, lock_data; + + tcode = header_get_tcode(packet->header[0]); + length = header_get_data_length(packet->header[3]); + payload = packet->payload; + ext_tcode = header_get_extended_tcode(packet->header[3]); + + if (tcode == TCODE_LOCK_REQUEST && + ext_tcode == EXTCODE_COMPARE_SWAP && length == 8) { + lock_arg = be32_to_cpu(payload[0]); + lock_data = be32_to_cpu(payload[1]); + } else if (tcode == TCODE_READ_QUADLET_REQUEST) { + lock_arg = 0; + lock_data = 0; + } else { + fw_fill_response(&response, packet->header, + RCODE_TYPE_ERROR, NULL, 0); + goto out; + } + + sel = (csr - CSR_BUS_MANAGER_ID) / 4; + reg_write(ohci, OHCI1394_CSRData, lock_data); + reg_write(ohci, OHCI1394_CSRCompareData, lock_arg); + reg_write(ohci, OHCI1394_CSRControl, sel); + + if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000) + lock_old = cpu_to_be32(reg_read(ohci, OHCI1394_CSRData)); + else + fw_notify("swap not done yet\n"); + + fw_fill_response(&response, packet->header, + RCODE_COMPLETE, &lock_old, sizeof lock_old); + out: + fw_core_handle_response(&ohci->card, &response); +} + +static void +handle_local_request(struct at_context *ctx, struct fw_packet *packet) +{ + u64 offset; + u32 csr; + + packet->ack = ACK_PENDING; + packet->callback(packet, &ctx->ohci->card, packet->ack); + + offset = + ((unsigned long long) + header_get_offset_high(packet->header[1]) << 32) | + packet->header[2]; + csr = offset - CSR_REGISTER_BASE; + + /* Handle config rom reads. */ + if (csr >= CSR_CONFIG_ROM && csr < CSR_CONFIG_ROM_END) + handle_local_rom(ctx->ohci, packet, csr); + else switch (csr) { + case CSR_BUS_MANAGER_ID: + case CSR_BANDWIDTH_AVAILABLE: + case CSR_CHANNELS_AVAILABLE_HI: + case CSR_CHANNELS_AVAILABLE_LO: + handle_local_lock(ctx->ohci, packet, csr); + break; + default: + if (ctx == &ctx->ohci->at_request_ctx) + fw_core_handle_request(&ctx->ohci->card, packet); + else + fw_core_handle_response(&ctx->ohci->card, packet); + break; + } +} + static void at_context_transmit(struct at_context *ctx, struct fw_packet *packet) { LIST_HEAD(list); unsigned long flags; - int was_empty; spin_lock_irqsave(&ctx->ohci->lock, flags); - was_empty = list_empty(&ctx->list); + if (header_get_destination(packet->header[0]) == ctx->ohci->node_id && + ctx->ohci->generation == packet->generation) { + spin_unlock_irqrestore(&ctx->ohci->lock, flags); + handle_local_request(ctx, packet); + return; + } + list_add_tail(&packet->link, &ctx->list); - if (was_empty) + if (ctx->list.next == &packet->link) at_context_setup_packet(ctx, &list); spin_unlock_irqrestore(&ctx->ohci->lock, flags); @@ -539,7 +686,7 @@ at_context_transmit(struct at_context *ctx, struct fw_packet *packet) static void bus_reset_tasklet(unsigned long data) { struct fw_ohci *ohci = (struct fw_ohci *)data; - int self_id_count, i, j, reg, node_id; + int self_id_count, i, j, reg; int generation, new_generation; unsigned long flags; @@ -548,7 +695,7 @@ static void bus_reset_tasklet(unsigned long data) fw_error("node ID not valid, new bus reset in progress\n"); return; } - node_id = reg & 0xffff; + ohci->node_id = reg & 0xffff; /* The count in the SelfIDCount register is the number of * bytes in the self ID receive buffer. Since we also receive @@ -617,7 +764,7 @@ static void bus_reset_tasklet(unsigned long data) spin_unlock_irqrestore(&ohci->lock, flags); - fw_core_handle_bus_reset(&ohci->card, node_id, generation, + fw_core_handle_bus_reset(&ohci->card, ohci->node_id, generation, self_id_count, ohci->self_id_buffer); }