firewire: use atomic type for fw_device.state
[safe/jmp/linux-2.6] / drivers / firewire / fw-ohci.c
index 5392a2b..29285f2 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/poll.h>
+#include <linux/dma-mapping.h>
+
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
 
@@ -119,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;
 
@@ -145,7 +148,7 @@ struct fw_ohci {
        struct iso_context *ir_context_list;
 };
 
-extern inline struct fw_ohci *fw_ohci(struct fw_card *card)
+static inline struct fw_ohci *fw_ohci(struct fw_card *card)
 {
        return container_of(card, struct fw_ohci, card);
 }
@@ -167,22 +170,21 @@ extern inline struct fw_ohci *fw_ohci(struct fw_card *card)
 #define OHCI1394_PCI_HCI_Control       0x40
 #define SELF_ID_BUF_SIZE               0x800
 
-/* FIXME: Move this to linux/pci_ids.h */
-#define PCI_CLASS_SERIAL_FIREWIRE_OHCI 0x0c0010
+#define MAX_STOP_CONTEXT_LOOPS         1000
 
 static char ohci_driver_name[] = KBUILD_MODNAME;
 
-extern inline void reg_write(const struct fw_ohci *ohci, int offset, u32 data)
+static inline void reg_write(const struct fw_ohci *ohci, int offset, u32 data)
 {
        writel(data, ohci->registers + offset);
 }
 
-extern inline u32 reg_read(const struct fw_ohci *ohci, int offset)
+static inline u32 reg_read(const struct fw_ohci *ohci, int offset)
 {
        return readl(ohci->registers + offset);
 }
 
-extern inline void flush_writes(const struct fw_ohci *ohci)
+static inline void flush_writes(const struct fw_ohci *ohci)
 {
        /* Do a dummy read to flush writes. */
        reg_read(ohci, OHCI1394_Version);
@@ -222,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
@@ -249,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);
@@ -266,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);
 }
 
@@ -285,8 +317,8 @@ ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 control_set)
                return -ENOMEM;
 
        if (ctx->descriptor_bus & 0xf)
-               fw_notify("descriptor not 16-byte aligned: 0x%08x\n",
-                         ctx->descriptor_bus);
+               fw_notify("descriptor not 16-byte aligned: 0x%08lx\n",
+                         (unsigned long)ctx->descriptor_bus);
 
        ctx->buffer_bus =
                dma_map_single(ohci->card.device, ctx->buffer,
@@ -324,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
@@ -355,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;
                }
 
@@ -417,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);
        }
 }
 
@@ -463,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
@@ -518,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);
@@ -540,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;
 
@@ -549,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
@@ -618,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);
 }
 
@@ -829,10 +975,10 @@ ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
 {
        struct fw_ohci *ohci = fw_ohci(card);
        unsigned long flags;
-       int retval = 0;
+       int n, retval = 0;
 
-       /* FIXME: make sure this bitmask is cleared when we clear the
-        * busReset interrupt bit. */
+       /* FIXME:  Make sure this bitmask is cleared when we clear the busReset
+        * interrupt bit.  Clear physReqResourceAllBuses on bus reset. */
 
        spin_lock_irqsave(&ohci->lock, flags);
 
@@ -841,17 +987,18 @@ ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
                goto out;
        }
 
-       if (node_id < 32) {
-               reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 1 << node_id);
-       } else {
-               reg_write(ohci, OHCI1394_PhyReqFilterHiSet,
-                         1 << (node_id - 32));
-       }
-       flush_writes(ohci);
+       /* NOTE, if the node ID contains a non-local bus ID, physical DMA is
+        * enabled for _all_ nodes on remote buses. */
 
-       spin_unlock_irqrestore(&ohci->lock, flags);
+       n = (node_id & 0xffc0) == LOCAL_BUS ? node_id & 0x3f : 63;
+       if (n < 32)
+               reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 1 << n);
+       else
+               reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 1 << (n - 32));
 
+       flush_writes(ohci);
  out:
+       spin_unlock_irqrestore(&ohci->lock, flags);
        return retval;
 }
 
@@ -961,9 +1108,8 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
         * element so that head == tail means buffer full. */
 
        memset(ctx->head_descriptor, 0, sizeof *ctx->head_descriptor);
-       ctx->head_descriptor->control          =
-               cpu_to_le16(descriptor_output_last);
-       ctx->head_descriptor->transfer_status  = cpu_to_le16(0x8011);
+       ctx->head_descriptor->control = cpu_to_le16(descriptor_output_last);
+       ctx->head_descriptor->transfer_status = cpu_to_le16(0x8011);
        ctx->head_descriptor++;
 
        return &ctx->base;
@@ -1144,7 +1290,7 @@ ohci_queue_iso(struct fw_iso_context *base,
        return 0;
 }
 
-static struct fw_card_driver ohci_driver = {
+static const struct fw_card_driver ohci_driver = {
        .name                   = ohci_driver_name,
        .enable                 = ohci_enable,
        .update_phy_reg         = ohci_update_phy_reg,
@@ -1156,7 +1302,7 @@ static struct fw_card_driver ohci_driver = {
        .allocate_iso_context   = ohci_allocate_iso_context,
        .free_iso_context       = ohci_free_iso_context,
        .queue_iso              = ohci_queue_iso,
-       .send_iso               = ohci_send_iso
+       .send_iso               = ohci_send_iso,
 };
 
 static int software_reset(struct fw_ohci *ohci)