revert "dpt_i2o: convert to SCSI hotplug model"
[powerpc.git] / drivers / firewire / fw-ohci.c
index a333748..436a855 100644 (file)
@@ -437,6 +437,21 @@ static void ar_context_run(struct ar_context *ctx)
        flush_writes(ctx->ohci);
 }
 
+static struct descriptor *
+find_branch_descriptor(struct descriptor *d, int z)
+{
+       int b, key;
+
+       b   = (le16_to_cpu(d->control) & DESCRIPTOR_BRANCH_ALWAYS) >> 2;
+       key = (le16_to_cpu(d->control) & DESCRIPTOR_KEY_IMMEDIATE) >> 8;
+
+       /* figure out which descriptor the branch address goes in */
+       if (z == 2 && (b == 3 || key == 2))
+               return d;
+       else
+               return d + z - 1;
+}
+
 static void context_tasklet(unsigned long data)
 {
        struct context *ctx = (struct context *) data;
@@ -455,7 +470,7 @@ static void context_tasklet(unsigned long data)
                address = le32_to_cpu(last->branch_address);
                z = address & 0xf;
                d = ctx->buffer + (address - ctx->buffer_bus) / sizeof(*d);
-               last = (z == 2) ? d : d + z - 1;
+               last = find_branch_descriptor(d, z);
 
                if (!ctx->callback(ctx, d, last))
                        break;
@@ -566,7 +581,7 @@ static void context_append(struct context *ctx,
 
        ctx->head_descriptor = d + z + extra;
        ctx->prev_descriptor->branch_address = cpu_to_le32(d_bus | z);
-       ctx->prev_descriptor = z == 2 ? d : d + z - 1;
+       ctx->prev_descriptor = find_branch_descriptor(d, z);
 
        dma_sync_single_for_device(ctx->ohci->card.device, ctx->buffer_bus,
                                   ctx->buffer_size, DMA_TO_DEVICE);
@@ -606,7 +621,7 @@ static int
 at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
 {
        struct fw_ohci *ohci = ctx->ohci;
-       dma_addr_t d_bus, payload_bus;
+       dma_addr_t d_bus, uninitialized_var(payload_bus);
        struct driver_data *driver_data;
        struct descriptor *d, *last;
        __le32 *header;
@@ -655,7 +670,7 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
        driver_data = (struct driver_data *) &d[3];
        driver_data->packet = packet;
        packet->driver_data = driver_data;
-       
+
        if (packet->payload_length > 0) {
                payload_bus =
                        dma_map_single(ohci->card.device, packet->payload,
@@ -680,6 +695,9 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
 
        /* FIXME: Document how the locking works. */
        if (ohci->generation != packet->generation) {
+               if (packet->payload_length > 0)
+                       dma_unmap_single(ohci->card.device, payload_bus,
+                                        packet->payload_length, DMA_TO_DEVICE);
                packet->ack = RCODE_GENERATION;
                return -1;
        }
@@ -900,7 +918,7 @@ at_context_transmit(struct context *ctx, struct fw_packet *packet)
 
        if (retval < 0)
                packet->callback(packet, &ctx->ohci->card, packet->ack);
-       
+
 }
 
 static void bus_reset_tasklet(unsigned long data)
@@ -914,10 +932,15 @@ static void bus_reset_tasklet(unsigned long data)
 
        reg = reg_read(ohci, OHCI1394_NodeID);
        if (!(reg & OHCI1394_NodeID_idValid)) {
-               fw_error("node ID not valid, new bus reset in progress\n");
+               fw_notify("node ID not valid, new bus reset in progress\n");
+               return;
+       }
+       if ((reg & OHCI1394_NodeID_nodeNumber) == 63) {
+               fw_notify("malconfigured bus\n");
                return;
        }
-       ohci->node_id = reg & 0xffff;
+       ohci->node_id = reg & (OHCI1394_NodeID_busNumber |
+                              OHCI1394_NodeID_nodeNumber);
 
        /*
         * The count in the SelfIDCount register is the number of
@@ -976,8 +999,10 @@ static void bus_reset_tasklet(unsigned long data)
         */
 
        if (ohci->next_config_rom != NULL) {
-               free_rom     = ohci->config_rom;
-               free_rom_bus = ohci->config_rom_bus;
+               if (ohci->next_config_rom != ohci->config_rom) {
+                       free_rom      = ohci->config_rom;
+                       free_rom_bus  = ohci->config_rom_bus;
+               }
                ohci->config_rom      = ohci->next_config_rom;
                ohci->config_rom_bus  = ohci->next_config_rom_bus;
                ohci->next_config_rom = NULL;
@@ -1153,19 +1178,30 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
         * the right values in the bus reset tasklet.
         */
 
-       ohci->next_config_rom =
-               dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE,
-                                  &ohci->next_config_rom_bus, GFP_KERNEL);
-       if (ohci->next_config_rom == NULL)
-               return -ENOMEM;
+       if (config_rom) {
+               ohci->next_config_rom =
+                       dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+                                          &ohci->next_config_rom_bus,
+                                          GFP_KERNEL);
+               if (ohci->next_config_rom == NULL)
+                       return -ENOMEM;
 
-       memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE);
-       fw_memcpy_to_be32(ohci->next_config_rom, config_rom, length * 4);
+               memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE);
+               fw_memcpy_to_be32(ohci->next_config_rom, config_rom, length * 4);
+       } else {
+               /*
+                * In the suspend case, config_rom is NULL, which
+                * means that we just reuse the old config rom.
+                */
+               ohci->next_config_rom = ohci->config_rom;
+               ohci->next_config_rom_bus = ohci->config_rom_bus;
+       }
 
-       ohci->next_header = config_rom[0];
+       ohci->next_header = be32_to_cpu(ohci->next_config_rom[0]);
        ohci->next_config_rom[0] = 0;
        reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
-       reg_write(ohci, OHCI1394_BusOptions, config_rom[2]);
+       reg_write(ohci, OHCI1394_BusOptions,
+                 be32_to_cpu(ohci->next_config_rom[2]));
        reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus);
 
        reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000);
@@ -1410,6 +1446,57 @@ static int handle_ir_dualbuffer_packet(struct context *context,
        return 1;
 }
 
+static int handle_ir_packet_per_buffer(struct context *context,
+                                      struct descriptor *d,
+                                      struct descriptor *last)
+{
+       struct iso_context *ctx =
+               container_of(context, struct iso_context, context);
+       struct descriptor *pd = d + 1;
+       __le32 *ir_header;
+       size_t header_length;
+       void *p, *end;
+       int i, z;
+
+       if (pd->res_count == pd->req_count)
+               /* Descriptor(s) not done yet, stop iteration */
+               return 0;
+
+       header_length = le16_to_cpu(d->req_count);
+
+       i   = ctx->header_length;
+       z   = le32_to_cpu(pd->branch_address) & 0xf;
+       p   = d + z;
+       end = p + header_length;
+
+       while (p < end && i + ctx->base.header_size <= PAGE_SIZE) {
+               /*
+                * The iso header is byteswapped to little endian by
+                * the controller, but the remaining header quadlets
+                * are big endian.  We want to present all the headers
+                * as big endian, so we have to swap the first quadlet.
+                */
+               *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
+               memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
+               i += ctx->base.header_size;
+               p += ctx->base.header_size + 4;
+       }
+
+       ctx->header_length = i;
+
+       if (le16_to_cpu(pd->control) & DESCRIPTOR_IRQ_ALWAYS) {
+               ir_header = (__le32 *) (d + z);
+               ctx->base.callback(&ctx->base,
+                                  le32_to_cpu(ir_header[0]) & 0xffff,
+                                  ctx->header_length, ctx->header,
+                                  ctx->base.callback_data);
+               ctx->header_length = 0;
+       }
+
+
+       return 1;
+}
+
 static int handle_it_packet(struct context *context,
                            struct descriptor *d,
                            struct descriptor *last)
@@ -1445,14 +1532,12 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
        } else {
                mask = &ohci->ir_context_mask;
                list = ohci->ir_context_list;
-               callback = handle_ir_dualbuffer_packet;
+               if (ohci->version >= OHCI_VERSION_1_1)
+                       callback = handle_ir_dualbuffer_packet;
+               else
+                       callback = handle_ir_packet_per_buffer;
        }
 
-       /* FIXME: We need a fallback for pre 1.1 OHCI. */
-       if (callback == handle_ir_dualbuffer_packet &&
-           ohci->version < OHCI_VERSION_1_1)
-               return ERR_PTR(-EINVAL);
-
        spin_lock_irqsave(&ohci->lock, flags);
        index = ffs(*mask) - 1;
        if (index >= 0)
@@ -1511,7 +1596,9 @@ static int ohci_start_iso(struct fw_iso_context *base,
                context_run(&ctx->context, match);
        } else {
                index = ctx - ohci->ir_context_list;
-               control = IR_CONTEXT_DUAL_BUFFER_MODE | IR_CONTEXT_ISOCH_HEADER;
+               control = IR_CONTEXT_ISOCH_HEADER;
+               if (ohci->version >= OHCI_VERSION_1_1)
+                       control |= IR_CONTEXT_DUAL_BUFFER_MODE;
                match = (tags << 28) | (sync << 8) | ctx->base.channel;
                if (cycle >= 0) {
                        match |= (cycle & 0x07fff) << 12;
@@ -1717,7 +1804,6 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
        offset   = payload & ~PAGE_MASK;
        rest     = p->payload_length;
 
-       /* FIXME: OHCI 1.0 doesn't support dual buffer receive */
        /* FIXME: make packet-per-buffer/dual-buffer a context option */
        while (rest > 0) {
                d = context_get_descriptors(&ctx->context,
@@ -1755,6 +1841,81 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
        return 0;
 }
 
+static int
+ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
+                                        struct fw_iso_packet *packet,
+                                        struct fw_iso_buffer *buffer,
+                                        unsigned long payload)
+{
+       struct iso_context *ctx = container_of(base, struct iso_context, base);
+       struct descriptor *d = NULL, *pd = NULL;
+       struct fw_iso_packet *p;
+       dma_addr_t d_bus, page_bus;
+       u32 z, header_z, rest;
+       int i, page, offset, packet_count, header_size;
+
+       if (packet->skip) {
+               d = context_get_descriptors(&ctx->context, 1, &d_bus);
+               if (d == NULL)
+                       return -ENOMEM;
+
+               d->control = cpu_to_le16(DESCRIPTOR_STATUS |
+                                        DESCRIPTOR_INPUT_LAST |
+                                        DESCRIPTOR_BRANCH_ALWAYS |
+                                        DESCRIPTOR_WAIT);
+               context_append(&ctx->context, d, 1, 0);
+       }
+
+       /* one descriptor for header, one for payload */
+       /* FIXME: handle cases where we need multiple desc. for payload */
+       z = 2;
+       p = packet;
+
+       /*
+        * The OHCI controller puts the status word in the
+        * buffer too, so we need 4 extra bytes per packet.
+        */
+       packet_count = p->header_length / ctx->base.header_size;
+       header_size  = packet_count * (ctx->base.header_size + 4);
+
+       /* Get header size in number of descriptors. */
+       header_z = DIV_ROUND_UP(header_size, sizeof(*d));
+       page     = payload >> PAGE_SHIFT;
+       offset   = payload & ~PAGE_MASK;
+       rest     = p->payload_length;
+
+       for (i = 0; i < packet_count; i++) {
+               /* d points to the header descriptor */
+               d = context_get_descriptors(&ctx->context,
+                                           z + header_z, &d_bus);
+               if (d == NULL)
+                       return -ENOMEM;
+
+               d->control      = cpu_to_le16(DESCRIPTOR_INPUT_MORE);
+               d->req_count    = cpu_to_le16(header_size);
+               d->res_count    = d->req_count;
+               d->data_address = cpu_to_le32(d_bus + (z * sizeof(*d)));
+
+               /* pd points to the payload descriptor */
+               pd = d + 1;
+               pd->control = cpu_to_le16(DESCRIPTOR_STATUS |
+                                         DESCRIPTOR_INPUT_LAST |
+                                         DESCRIPTOR_BRANCH_ALWAYS);
+               if (p->interrupt)
+                       pd->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS);
+
+               pd->req_count = cpu_to_le16(rest);
+               pd->res_count = pd->req_count;
+
+               page_bus = page_private(buffer->pages[page]);
+               pd->data_address = cpu_to_le32(page_bus + offset);
+
+               context_append(&ctx->context, d, z, header_z);
+       }
+
+       return 0;
+}
+
 static int
 ohci_queue_iso(struct fw_iso_context *base,
               struct fw_iso_packet *packet,
@@ -1769,8 +1930,9 @@ ohci_queue_iso(struct fw_iso_context *base,
                return ohci_queue_iso_receive_dualbuffer(base, packet,
                                                         buffer, payload);
        else
-               /* FIXME: Implement fallback for OHCI 1.0 controllers. */
-               return -EINVAL;
+               return ohci_queue_iso_receive_packet_per_buffer(base, packet,
+                                                               buffer,
+                                                               payload);
 }
 
 static const struct fw_card_driver ohci_driver = {
@@ -1890,7 +2052,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
        ohci->version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
        fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n",
                  dev->dev.bus_id, ohci->version >> 16, ohci->version & 0xff);
-
        return 0;
 
  fail_self_id:
@@ -1971,7 +2132,7 @@ static int pci_resume(struct pci_dev *pdev)
                return err;
        }
 
-       return ohci_enable(&ohci->card, ohci->config_rom, CONFIG_ROM_SIZE);
+       return ohci_enable(&ohci->card, NULL, 0);
 }
 #endif