Merge branch 'devel' into next
[pandora-kernel.git] / drivers / firewire / fw-transaction.c
index e6f1bda..40db807 100644 (file)
@@ -18,7 +18,9 @@
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#include <linux/completion.h>
 #include <linux/kernel.h>
+#include <linux/kref.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -28,7 +30,6 @@
 #include <linux/list.h>
 #include <linux/kthread.h>
 #include <asm/uaccess.h>
-#include <asm/semaphore.h>
 
 #include "fw-transaction.h"
 #include "fw-topology.h"
@@ -54,6 +55,9 @@
 #define HEADER_GET_DATA_LENGTH(q)      (((q) >> 16) & 0xffff)
 #define HEADER_GET_EXTENDED_TCODE(q)   (((q) >> 0) & 0xffff)
 
+#define HEADER_DESTINATION_IS_BROADCAST(q) \
+       (((q) & HEADER_DESTINATION(0x3f)) == HEADER_DESTINATION(0x3f))
+
 #define PHY_CONFIG_GAP_COUNT(gap_count)        (((gap_count) << 16) | (1 << 22))
 #define PHY_CONFIG_ROOT_ID(node_id)    ((((node_id) & 0x3f) << 24) | (1 << 23))
 #define PHY_IDENTIFIER(id)             ((id) << 30)
@@ -294,42 +298,58 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
 }
 EXPORT_SYMBOL(fw_send_request);
 
-static void
-transmit_phy_packet_callback(struct fw_packet *packet,
-                            struct fw_card *card, int status)
+struct fw_phy_packet {
+       struct fw_packet packet;
+       struct completion done;
+       struct kref kref;
+};
+
+static void phy_packet_release(struct kref *kref)
 {
-       kfree(packet);
+       struct fw_phy_packet *p =
+                       container_of(kref, struct fw_phy_packet, kref);
+       kfree(p);
 }
 
-static void send_phy_packet(struct fw_card *card, u32 data, int generation)
+static void transmit_phy_packet_callback(struct fw_packet *packet,
+                                        struct fw_card *card, int status)
 {
-       struct fw_packet *packet;
+       struct fw_phy_packet *p =
+                       container_of(packet, struct fw_phy_packet, packet);
 
-       packet = kzalloc(sizeof(*packet), GFP_ATOMIC);
-       if (packet == NULL)
-               return;
-
-       packet->header[0] = data;
-       packet->header[1] = ~data;
-       packet->header_length = 8;
-       packet->payload_length = 0;
-       packet->speed = SCODE_100;
-       packet->generation = generation;
-       packet->callback = transmit_phy_packet_callback;
-
-       card->driver->send_request(card, packet);
+       complete(&p->done);
+       kref_put(&p->kref, phy_packet_release);
 }
 
 void fw_send_phy_config(struct fw_card *card,
                        int node_id, int generation, int gap_count)
 {
-       u32 q;
-
-       q = PHY_IDENTIFIER(PHY_PACKET_CONFIG) |
-               PHY_CONFIG_ROOT_ID(node_id) |
-               PHY_CONFIG_GAP_COUNT(gap_count);
+       struct fw_phy_packet *p;
+       long timeout = DIV_ROUND_UP(HZ, 10);
+       u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) |
+                  PHY_CONFIG_ROOT_ID(node_id) |
+                  PHY_CONFIG_GAP_COUNT(gap_count);
+
+       p = kmalloc(sizeof(*p), GFP_KERNEL);
+       if (p == NULL)
+               return;
 
-       send_phy_packet(card, q, generation);
+       p->packet.header[0] = data;
+       p->packet.header[1] = ~data;
+       p->packet.header_length = 8;
+       p->packet.payload_length = 0;
+       p->packet.speed = SCODE_100;
+       p->packet.generation = generation;
+       p->packet.callback = transmit_phy_packet_callback;
+       init_completion(&p->done);
+       kref_set(&p->kref, 2);
+
+       card->driver->send_request(card, &p->packet);
+       timeout = wait_for_completion_timeout(&p->done, timeout);
+       kref_put(&p->kref, phy_packet_release);
+
+       /* will leak p if the callback is never executed */
+       WARN_ON(timeout == 0);
 }
 
 void fw_flush_transactions(struct fw_card *card)
@@ -389,21 +409,21 @@ lookup_enclosing_address_handler(struct list_head *list,
 static DEFINE_SPINLOCK(address_handler_lock);
 static LIST_HEAD(address_handler_list);
 
-const struct fw_address_region fw_low_memory_region =
-       { .start = 0x000000000000ULL, .end = 0x000100000000ULL,  };
 const struct fw_address_region fw_high_memory_region =
        { .start = 0x000100000000ULL, .end = 0xffffe0000000ULL,  };
+EXPORT_SYMBOL(fw_high_memory_region);
+
+#if 0
+const struct fw_address_region fw_low_memory_region =
+       { .start = 0x000000000000ULL, .end = 0x000100000000ULL,  };
 const struct fw_address_region fw_private_region =
        { .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL,  };
 const struct fw_address_region fw_csr_region =
-       { .start = 0xfffff0000000ULL, .end = 0xfffff0000800ULL,  };
+       { .start = CSR_REGISTER_BASE,
+         .end   = CSR_REGISTER_BASE | CSR_CONFIG_ROM_END,  };
 const struct fw_address_region fw_unit_space_region =
        { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, };
-EXPORT_SYMBOL(fw_low_memory_region);
-EXPORT_SYMBOL(fw_high_memory_region);
-EXPORT_SYMBOL(fw_private_region);
-EXPORT_SYMBOL(fw_csr_region);
-EXPORT_SYMBOL(fw_unit_space_region);
+#endif  /*  0  */
 
 /**
  * Allocate a range of addresses in the node space of the OHCI
@@ -574,7 +594,8 @@ allocate_request(struct fw_packet *p)
                break;
 
        default:
-               BUG();
+               fw_error("ERROR - corrupt request received - %08x %08x %08x\n",
+                        p->header[0], p->header[1], p->header[2]);
                return NULL;
        }
 
@@ -606,12 +627,9 @@ allocate_request(struct fw_packet *p)
 void
 fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
 {
-       /*
-        * Broadcast packets are reported as ACK_COMPLETE, so this
-        * check is sufficient to ensure we don't send response to
-        * broadcast packets or posted writes.
-        */
-       if (request->ack != ACK_PENDING) {
+       /* unified transaction or broadcast transaction: don't respond */
+       if (request->ack != ACK_PENDING ||
+           HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) {
                kfree(request);
                return;
        }
@@ -747,7 +765,8 @@ fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
 EXPORT_SYMBOL(fw_core_handle_response);
 
 static const struct fw_address_region topology_map_region =
-       { .start = 0xfffff0001000ull, .end = 0xfffff0001400ull, };
+       { .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP,
+         .end   = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, };
 
 static void
 handle_topology_map(struct fw_card *card, struct fw_request *request,
@@ -785,7 +804,8 @@ static struct fw_address_handler topology_map = {
 };
 
 static const struct fw_address_region registers_region =
-       { .start = 0xfffff0000000ull, .end = 0xfffff0000400ull, };
+       { .start = CSR_REGISTER_BASE,
+         .end   = CSR_REGISTER_BASE | CSR_CONFIG_ROM, };
 
 static void
 handle_registers(struct fw_card *card, struct fw_request *request,
@@ -794,15 +814,16 @@ handle_registers(struct fw_card *card, struct fw_request *request,
                 unsigned long long offset,
                 void *payload, size_t length, void *callback_data)
 {
-       int reg = offset CSR_REGISTER_BASE;
+       int reg = offset & ~CSR_REGISTER_BASE;
        unsigned long long bus_time;
        __be32 *data = payload;
+       int rcode = RCODE_COMPLETE;
 
        switch (reg) {
        case CSR_CYCLE_TIME:
        case CSR_BUS_TIME:
                if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) {
-                       fw_send_response(card, request, RCODE_TYPE_ERROR);
+                       rcode = RCODE_TYPE_ERROR;
                        break;
                }
 
@@ -811,7 +832,17 @@ handle_registers(struct fw_card *card, struct fw_request *request,
                        *data = cpu_to_be32(bus_time);
                else
                        *data = cpu_to_be32(bus_time >> 25);
-               fw_send_response(card, request, RCODE_COMPLETE);
+               break;
+
+       case CSR_BROADCAST_CHANNEL:
+               if (tcode == TCODE_READ_QUADLET_REQUEST)
+                       *data = cpu_to_be32(card->broadcast_channel);
+               else if (tcode == TCODE_WRITE_QUADLET_REQUEST)
+                       card->broadcast_channel =
+                           (be32_to_cpu(*data) & BROADCAST_CHANNEL_VALID) |
+                           BROADCAST_CHANNEL_INITIAL;
+               else
+                       rcode = RCODE_TYPE_ERROR;
                break;
 
        case CSR_BUS_MANAGER_ID:
@@ -830,10 +861,13 @@ handle_registers(struct fw_card *card, struct fw_request *request,
 
        case CSR_BUSY_TIMEOUT:
                /* FIXME: Implement this. */
+
        default:
-               fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+               rcode = RCODE_ADDRESS_ERROR;
                break;
        }
+
+       fw_send_response(card, request, rcode);
 }
 
 static struct fw_address_handler registers = {