radeonfb: suspend/resume for ATI Mobility Radeon RV350
[safe/jmp/linux-2.6] / drivers / firewire / fw-transaction.c
index 051be78..283dac6 100644 (file)
  */
 
 #include <linux/completion.h>
+#include <linux/idr.h>
 #include <linux/kernel.h>
+#include <linux/kref.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
@@ -29,7 +32,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"
 #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)
 
-static int
-close_transaction(struct fw_transaction *transaction,
-                 struct fw_card *card, int rcode,
-                 u32 *payload, size_t length)
+static int close_transaction(struct fw_transaction *transaction,
+                            struct fw_card *card, int rcode)
 {
        struct fw_transaction *t;
        unsigned long flags;
@@ -78,7 +81,7 @@ close_transaction(struct fw_transaction *transaction,
        spin_unlock_irqrestore(&card->lock, flags);
 
        if (&t->link != &card->transaction_list) {
-               t->callback(card, rcode, payload, length, t->callback_data);
+               t->callback(card, rcode, NULL, 0, t->callback_data);
                return 0;
        }
 
@@ -89,9 +92,8 @@ close_transaction(struct fw_transaction *transaction,
  * Only valid for transactions that are potentially pending (ie have
  * been sent).
  */
-int
-fw_cancel_transaction(struct fw_card *card,
-                     struct fw_transaction *transaction)
+int fw_cancel_transaction(struct fw_card *card,
+                         struct fw_transaction *transaction)
 {
        /*
         * Cancel the packet transmission if it's still queued.  That
@@ -107,20 +109,19 @@ fw_cancel_transaction(struct fw_card *card,
         * if the transaction is still pending and remove it in that case.
         */
 
-       return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0);
+       return close_transaction(transaction, card, RCODE_CANCELLED);
 }
 EXPORT_SYMBOL(fw_cancel_transaction);
 
-static void
-transmit_complete_callback(struct fw_packet *packet,
-                          struct fw_card *card, int status)
+static void transmit_complete_callback(struct fw_packet *packet,
+                                      struct fw_card *card, int status)
 {
        struct fw_transaction *t =
            container_of(packet, struct fw_transaction, packet);
 
        switch (status) {
        case ACK_COMPLETE:
-               close_transaction(t, card, RCODE_COMPLETE, NULL, 0);
+               close_transaction(t, card, RCODE_COMPLETE);
                break;
        case ACK_PENDING:
                t->timestamp = packet->timestamp;
@@ -128,31 +129,42 @@ transmit_complete_callback(struct fw_packet *packet,
        case ACK_BUSY_X:
        case ACK_BUSY_A:
        case ACK_BUSY_B:
-               close_transaction(t, card, RCODE_BUSY, NULL, 0);
+               close_transaction(t, card, RCODE_BUSY);
                break;
        case ACK_DATA_ERROR:
-               close_transaction(t, card, RCODE_DATA_ERROR, NULL, 0);
+               close_transaction(t, card, RCODE_DATA_ERROR);
                break;
        case ACK_TYPE_ERROR:
-               close_transaction(t, card, RCODE_TYPE_ERROR, NULL, 0);
+               close_transaction(t, card, RCODE_TYPE_ERROR);
                break;
        default:
                /*
                 * In this case the ack is really a juju specific
                 * rcode, so just forward that to the callback.
                 */
-               close_transaction(t, card, status, NULL, 0);
+               close_transaction(t, card, status);
                break;
        }
 }
 
-static void
-fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
-               int node_id, int source_id, int generation, int speed,
+static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
+               int destination_id, int source_id, int generation, int speed,
                unsigned long long offset, void *payload, size_t length)
 {
        int ext_tcode;
 
+       if (tcode == TCODE_STREAM_DATA) {
+               packet->header[0] =
+                       HEADER_DATA_LENGTH(length) |
+                       destination_id |
+                       HEADER_TCODE(TCODE_STREAM_DATA);
+               packet->header_length = 4;
+               packet->payload = payload;
+               packet->payload_length = length;
+
+               goto common;
+       }
+
        if (tcode > 0x10) {
                ext_tcode = tcode & ~0x10;
                tcode = TCODE_LOCK_REQUEST;
@@ -163,7 +175,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
                HEADER_RETRY(RETRY_X) |
                HEADER_TLABEL(tlabel) |
                HEADER_TCODE(tcode) |
-               HEADER_DESTINATION(node_id);
+               HEADER_DESTINATION(destination_id);
        packet->header[1] =
                HEADER_OFFSET_HIGH(offset >> 32) | HEADER_SOURCE(source_id);
        packet->header[2] =
@@ -199,10 +211,11 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
                packet->payload_length = 0;
                break;
        }
-
+ common:
        packet->speed = speed;
        packet->generation = generation;
        packet->ack = 0;
+       packet->payload_bus = 0;
 }
 
 /**
@@ -240,16 +253,17 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
  * @param callback function to be called when the transaction is completed
  * @param callback_data pointer to arbitrary data, which will be
  *   passed to the callback
+ *
+ * In case of asynchronous stream packets i.e. TCODE_STREAM_DATA, the caller
+ * needs to synthesize @destination_id with fw_stream_packet_destination_id().
  */
-void
-fw_send_request(struct fw_card *card, struct fw_transaction *t,
-               int tcode, int node_id, int generation, int speed,
-               unsigned long long offset,
-               void *payload, size_t length,
-               fw_transaction_callback_t callback, void *callback_data)
+void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
+                    int destination_id, int generation, int speed,
+                    unsigned long long offset, void *payload, size_t length,
+                    fw_transaction_callback_t callback, void *callback_data)
 {
        unsigned long flags;
-       int tlabel, source;
+       int tlabel;
 
        /*
         * Bump the flush timer up 100ms first of all so we
@@ -265,7 +279,6 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
 
        spin_lock_irqsave(&card->lock, flags);
 
-       source = card->node_id;
        tlabel = card->current_tlabel;
        if (card->tlabel_mask & (1 << tlabel)) {
                spin_unlock_irqrestore(&card->lock, flags);
@@ -276,59 +289,98 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
        card->current_tlabel = (card->current_tlabel + 1) & 0x1f;
        card->tlabel_mask |= (1 << tlabel);
 
-       list_add_tail(&t->link, &card->transaction_list);
-
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       /* Initialize rest of transaction, fill out packet and send it. */
-       t->node_id = node_id;
+       t->node_id = destination_id;
        t->tlabel = tlabel;
        t->callback = callback;
        t->callback_data = callback_data;
 
        fw_fill_request(&t->packet, tcode, t->tlabel,
-                       node_id, source, generation,
+                       destination_id, card->node_id, generation,
                        speed, offset, payload, length);
        t->packet.callback = transmit_complete_callback;
 
+       list_add_tail(&t->link, &card->transaction_list);
+
+       spin_unlock_irqrestore(&card->lock, flags);
+
        card->driver->send_request(card, &t->packet);
 }
 EXPORT_SYMBOL(fw_send_request);
 
-struct fw_phy_packet {
-       struct fw_packet packet;
+struct transaction_callback_data {
        struct completion done;
+       void *payload;
+       int rcode;
 };
 
-static void
-transmit_phy_packet_callback(struct fw_packet *packet,
-                            struct fw_card *card, int status)
+static void transaction_callback(struct fw_card *card, int rcode,
+                                void *payload, size_t length, void *data)
+{
+       struct transaction_callback_data *d = data;
+
+       if (rcode == RCODE_COMPLETE)
+               memcpy(d->payload, payload, length);
+       d->rcode = rcode;
+       complete(&d->done);
+}
+
+/**
+ * fw_run_transaction - send request and sleep until transaction is completed
+ *
+ * Returns the RCODE.
+ */
+int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+                      int generation, int speed, unsigned long long offset,
+                      void *payload, size_t length)
 {
-       struct fw_phy_packet *p =
-                       container_of(packet, struct fw_phy_packet, packet);
+       struct transaction_callback_data d;
+       struct fw_transaction t;
+
+       init_completion(&d.done);
+       d.payload = payload;
+       fw_send_request(card, &t, tcode, destination_id, generation, speed,
+                       offset, payload, length, transaction_callback, &d);
+       wait_for_completion(&d.done);
 
-       complete(&p->done);
+       return d.rcode;
 }
+EXPORT_SYMBOL(fw_run_transaction);
+
+static DEFINE_MUTEX(phy_config_mutex);
+static DECLARE_COMPLETION(phy_config_done);
+
+static void transmit_phy_packet_callback(struct fw_packet *packet,
+                                        struct fw_card *card, int status)
+{
+       complete(&phy_config_done);
+}
+
+static struct fw_packet phy_config_packet = {
+       .header_length  = 8,
+       .payload_length = 0,
+       .speed          = SCODE_100,
+       .callback       = transmit_phy_packet_callback,
+};
 
 void fw_send_phy_config(struct fw_card *card,
                        int node_id, int generation, int 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.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);
-
-       card->driver->send_request(card, &p.packet);
-       wait_for_completion(&p.done);
+       mutex_lock(&phy_config_mutex);
+
+       phy_config_packet.header[0] = data;
+       phy_config_packet.header[1] = ~data;
+       phy_config_packet.generation = generation;
+       INIT_COMPLETION(phy_config_done);
+
+       card->driver->send_request(card, &phy_config_packet);
+       wait_for_completion_timeout(&phy_config_done, timeout);
+
+       mutex_unlock(&phy_config_mutex);
 }
 
 void fw_flush_transactions(struct fw_card *card)
@@ -355,9 +407,8 @@ void fw_flush_transactions(struct fw_card *card)
        }
 }
 
-static struct fw_address_handler *
-lookup_overlapping_address_handler(struct list_head *list,
-                                  unsigned long long offset, size_t length)
+static struct fw_address_handler *lookup_overlapping_address_handler(
+       struct list_head *list, unsigned long long offset, size_t length)
 {
        struct fw_address_handler *handler;
 
@@ -370,9 +421,8 @@ lookup_overlapping_address_handler(struct list_head *list,
        return NULL;
 }
 
-static struct fw_address_handler *
-lookup_enclosing_address_handler(struct list_head *list,
-                                unsigned long long offset, size_t length)
+static struct fw_address_handler *lookup_enclosing_address_handler(
+       struct list_head *list, unsigned long long offset, size_t length)
 {
        struct fw_address_handler *handler;
 
@@ -388,10 +438,13 @@ 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 =
@@ -399,43 +452,47 @@ const struct fw_address_region fw_csr_region =
          .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
- * controller.  When a request is received that falls within the
- * specified address range, the specified callback is invoked.  The
- * parameters passed to the callback give the details of the
- * particular request.
+ * fw_core_add_address_handler - register for incoming requests
+ * @handler: callback
+ * @region: region in the IEEE 1212 node space address range
+ *
+ * region->start, ->end, and handler->length have to be quadlet-aligned.
+ *
+ * When a request is received that falls within the specified address range,
+ * the specified callback is invoked.  The parameters passed to the callback
+ * give the details of the particular request.
  *
  * Return value:  0 on success, non-zero otherwise.
  * The start offset of the handler's address region is determined by
  * fw_core_add_address_handler() and is returned in handler->offset.
- * The offset is quadlet-aligned.
  */
-int
-fw_core_add_address_handler(struct fw_address_handler *handler,
-                           const struct fw_address_region *region)
+int fw_core_add_address_handler(struct fw_address_handler *handler,
+                               const struct fw_address_region *region)
 {
        struct fw_address_handler *other;
        unsigned long flags;
        int ret = -EBUSY;
 
+       if (region->start & 0xffff000000000003ULL ||
+           region->end   & 0xffff000000000003ULL ||
+           region->start >= region->end ||
+           handler->length & 3 ||
+           handler->length == 0)
+               return -EINVAL;
+
        spin_lock_irqsave(&address_handler_lock, flags);
 
-       handler->offset = roundup(region->start, 4);
+       handler->offset = region->start;
        while (handler->offset + handler->length <= region->end) {
                other =
                    lookup_overlapping_address_handler(&address_handler_list,
                                                       handler->offset,
                                                       handler->length);
                if (other != NULL) {
-                       handler->offset =
-                           roundup(other->offset + other->length, 4);
+                       handler->offset += other->length;
                } else {
                        list_add_tail(&handler->link, &address_handler_list);
                        ret = 0;
@@ -450,12 +507,7 @@ fw_core_add_address_handler(struct fw_address_handler *handler,
 EXPORT_SYMBOL(fw_core_add_address_handler);
 
 /**
- * Deallocate a range of addresses allocated with fw_allocate.  This
- * will call the associated callback one last time with a the special
- * tcode TCODE_DEALLOCATE, to let the client destroy the registered
- * callback data.  For convenience, the callback parameters offset and
- * length are set to the start and the length respectively for the
- * deallocated region, payload is set to NULL.
+ * fw_core_remove_address_handler - unregister an address handler
  */
 void fw_core_remove_address_handler(struct fw_address_handler *handler)
 {
@@ -475,9 +527,8 @@ struct fw_request {
        u32 data[0];
 };
 
-static void
-free_response_callback(struct fw_packet *packet,
-                      struct fw_card *card, int status)
+static void free_response_callback(struct fw_packet *packet,
+                                  struct fw_card *card, int status)
 {
        struct fw_request *request;
 
@@ -485,9 +536,8 @@ free_response_callback(struct fw_packet *packet,
        kfree(request);
 }
 
-void
-fw_fill_response(struct fw_packet *response, u32 *request_header,
-                int rcode, void *payload, size_t length)
+void fw_fill_response(struct fw_packet *response, u32 *request_header,
+                     int rcode, void *payload, size_t length)
 {
        int tcode, tlabel, extended_tcode, source, destination;
 
@@ -540,11 +590,12 @@ fw_fill_response(struct fw_packet *response, u32 *request_header,
                BUG();
                return;
        }
+
+       response->payload_bus = 0;
 }
 EXPORT_SYMBOL(fw_fill_response);
 
-static struct fw_request *
-allocate_request(struct fw_packet *p)
+static struct fw_request *allocate_request(struct fw_packet *p)
 {
        struct fw_request *request;
        u32 *data, length;
@@ -574,7 +625,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;
        }
 
@@ -603,15 +655,12 @@ allocate_request(struct fw_packet *p)
        return request;
 }
 
-void
-fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
+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;
        }
@@ -627,8 +676,7 @@ fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
 }
 EXPORT_SYMBOL(fw_send_response);
 
-void
-fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
+void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
 {
        struct fw_address_handler *handler;
        struct fw_request *request;
@@ -676,8 +724,7 @@ fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
 }
 EXPORT_SYMBOL(fw_core_handle_request);
 
-void
-fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
+void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
 {
        struct fw_transaction *t;
        unsigned long flags;
@@ -750,12 +797,10 @@ static const struct fw_address_region topology_map_region =
        { .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,
-                   int tcode, int destination, int source,
-                   int generation, int speed,
-                   unsigned long long offset,
-                   void *payload, size_t length, void *callback_data)
+static void handle_topology_map(struct fw_card *card, struct fw_request *request,
+               int tcode, int destination, int source, int generation,
+               int speed, unsigned long long offset,
+               void *payload, size_t length, void *callback_data)
 {
        int i, start, end;
        __be32 *map;
@@ -789,22 +834,21 @@ static const struct fw_address_region registers_region =
        { .start = CSR_REGISTER_BASE,
          .end   = CSR_REGISTER_BASE | CSR_CONFIG_ROM, };
 
-static void
-handle_registers(struct fw_card *card, struct fw_request *request,
-                int tcode, int destination, int source,
-                int generation, int speed,
-                unsigned long long offset,
-                void *payload, size_t length, void *callback_data)
+static void handle_registers(struct fw_card *card, struct fw_request *request,
+               int tcode, int destination, int source, int generation,
+               int speed, unsigned long long offset,
+               void *payload, size_t length, void *callback_data)
 {
        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;
                }
 
@@ -813,7 +857,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:
@@ -832,10 +886,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 = {
@@ -882,11 +939,11 @@ static struct fw_descriptor model_id_descriptor = {
 
 static int __init fw_core_init(void)
 {
-       int retval;
+       int ret;
 
-       retval = bus_register(&fw_bus_type);
-       if (retval < 0)
-               return retval;
+       ret = bus_register(&fw_bus_type);
+       if (ret < 0)
+               return ret;
 
        fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
        if (fw_cdev_major < 0) {
@@ -894,19 +951,10 @@ static int __init fw_core_init(void)
                return fw_cdev_major;
        }
 
-       retval = fw_core_add_address_handler(&topology_map,
-                                            &topology_map_region);
-       BUG_ON(retval < 0);
-
-       retval = fw_core_add_address_handler(&registers,
-                                            &registers_region);
-       BUG_ON(retval < 0);
-
-       /* Add the vendor textual descriptor. */
-       retval = fw_core_add_descriptor(&vendor_id_descriptor);
-       BUG_ON(retval < 0);
-       retval = fw_core_add_descriptor(&model_id_descriptor);
-       BUG_ON(retval < 0);
+       fw_core_add_address_handler(&topology_map, &topology_map_region);
+       fw_core_add_address_handler(&registers, &registers_region);
+       fw_core_add_descriptor(&vendor_id_descriptor);
+       fw_core_add_descriptor(&model_id_descriptor);
 
        return 0;
 }
@@ -915,6 +963,7 @@ static void __exit fw_core_cleanup(void)
 {
        unregister_chrdev(fw_cdev_major, "firewire");
        bus_unregister(&fw_bus_type);
+       idr_destroy(&fw_device_idr);
 }
 
 module_init(fw_core_init);