firewire: Handle access to CSR resources on local node.
authorKristian Høgsberg <krh@redhat.com>
Fri, 26 Jan 2007 05:38:26 +0000 (00:38 -0500)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Fri, 9 Mar 2007 21:02:47 +0000 (22:02 +0100)
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
drivers/firewire/fw-ohci.c
drivers/firewire/fw-transaction.c
drivers/firewire/fw-transaction.h

index 5156329a8655a497d18a0f2f225b100bf24b84d3..ac6c018de0dce7f1092ea316a5c6f3dd3f9c166f 100644 (file)
@@ -540,38 +540,136 @@ at_context_init(struct at_context *ctx, struct fw_ohci *ohci, u32 control_set)
 }
 
 #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 local;
 
        spin_lock_irqsave(&ctx->ohci->lock, flags);
 
        if (header_get_destination(packet->header[0]) == ctx->ohci->node_id &&
            ctx->ohci->generation == packet->generation) {
-               local = 1;
-       } else {
-               list_add_tail(&packet->link, &ctx->list);
-               if (ctx->list.next == &packet->link)
-                       at_context_setup_packet(ctx, &list);
-               local = 0;
+               spin_unlock_irqrestore(&ctx->ohci->lock, flags);
+               handle_local_request(ctx, packet);
+               return;
        }
 
+       list_add_tail(&packet->link, &ctx->list);
+       if (ctx->list.next == &packet->link)
+               at_context_setup_packet(ctx, &list);
+
        spin_unlock_irqrestore(&ctx->ohci->lock, flags);
 
        do_packet_callbacks(ctx->ohci, &list);
-
-       if (local) {
-               packet->ack = ACK_PENDING;
-               packet->callback(packet, &ctx->ohci->card, packet->ack);
-               if (ctx == &ctx->ohci->at_request_ctx)
-                       fw_core_handle_request(&ctx->ohci->card, packet);
-               else
-                       fw_core_handle_response(&ctx->ohci->card, packet);
-       }
 }
 
 static void bus_reset_tasklet(unsigned long data)
index a116ffa9bf454b858d4d9f3e7a76c24197268464..780ed2b449831102659feceb83c400c5ffd3d277 100644 (file)
@@ -425,7 +425,7 @@ free_response_callback(struct fw_packet *packet,
        kfree(request);
 }
 
-static void
+void
 fw_fill_response(struct fw_packet *response, u32 *request_header,
                 int rcode, void *payload, size_t length)
 {
@@ -457,7 +457,10 @@ fw_fill_response(struct fw_packet *response, u32 *request_header,
        case TCODE_READ_QUADLET_REQUEST:
                response->header[0] |=
                        header_tcode(TCODE_READ_QUADLET_RESPONSE);
-               response->header[3] = *(u32 *)payload;
+               if (payload != NULL)
+                       response->header[3] = *(u32 *)payload;
+               else
+                       response->header[3] = 0;
                response->header_length = 16;
                response->payload_length = 0;
                break;
@@ -478,6 +481,7 @@ fw_fill_response(struct fw_packet *response, u32 *request_header,
                return;
        }
 }
+EXPORT_SYMBOL(fw_fill_response);
 
 static struct fw_request *
 allocate_request(struct fw_packet *p)
@@ -529,9 +533,9 @@ allocate_request(struct fw_packet *p)
        request->response.generation = p->generation;
        request->response.callback = free_response_callback;
        request->ack = p->ack;
-       request->length = p->payload_length;
+       request->length = length;
        if (data)
-               memcpy(request->data, p->payload, p->payload_length);
+               memcpy(request->data, p->payload, length);
 
        memcpy(request->request_header, p->header, sizeof p->header);
 
@@ -656,7 +660,7 @@ fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
 
        case TCODE_READ_BLOCK_RESPONSE:
        case TCODE_LOCK_RESPONSE:
-               data = &p->header[4];
+               data = p->payload;
                data_length = header_get_data_length(p->header[3]);
                break;
 
index 903235b142f28aee6fd9b4426a57957754169c78..fb46ef78a323773ebb88fabff3f05a4b0876325a 100644 (file)
@@ -40,6 +40,7 @@
 #define TCODE_STREAM_DATA              10
 #define TCODE_LOCK_RESPONSE            11
 
+#define TCODE_IS_READ_REQUEST(tcode)   (((tcode) & ~1) == 4)
 #define TCODE_IS_BLOCK_PACKET(tcode)   (((tcode) &  1) != 0)
 #define TCODE_IS_REQUEST(tcode)                (((tcode) &  2) == 0)
 #define TCODE_IS_RESPONSE(tcode)       (((tcode) &  2) != 0)
 #define PHY_PACKET_LINK_ON     0x1
 #define PHY_PACKET_SELF_ID     0x2
 
+#define CSR_REGISTER_BASE              0xfffff0000000ULL
+
+/* register offsets relative to CSR_REGISTER_BASE */
+#define CSR_STATE_CLEAR                        0x0
+#define CSR_STATE_SET                  0x4
+#define CSR_NODE_IDS                   0x8
+#define CSR_RESET_START                        0xc
+#define CSR_SPLIT_TIMEOUT_HI           0x18
+#define CSR_SPLIT_TIMEOUT_LO           0x1c
+#define CSR_CYCLE_TIME                 0x200
+#define CSR_BUS_TIME                   0x204
+#define CSR_BUSY_TIMEOUT               0x210
+#define CSR_BUS_MANAGER_ID             0x21c
+#define CSR_BANDWIDTH_AVAILABLE                0x220
+#define CSR_CHANNELS_AVAILABLE         0x224
+#define CSR_CHANNELS_AVAILABLE_HI      0x224
+#define CSR_CHANNELS_AVAILABLE_LO      0x228
+#define CSR_BROADCAST_CHANNEL          0x234
+#define CSR_CONFIG_ROM                 0x400
+#define CSR_CONFIG_ROM_END             0x800
+#define CSR_FCP_COMMAND                        0xB00
+#define CSR_FCP_RESPONSE               0xD00
+#define CSR_FCP_END                    0xF00
+#define CSR_TOPOLOGY_MAP               0x1000
+#define CSR_TOPOLOGY_MAP_END           0x1400
+#define CSR_SPEED_MAP                  0x2000
+#define CSR_SPEED_MAP_END              0x3000
+
 #define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args)
 #define fw_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
 #define fw_debug(s, args...) printk(KERN_DEBUG KBUILD_MODNAME ": " s, ## args)
@@ -227,6 +256,8 @@ extern const struct fw_address_region fw_unit_space_region;
 int fw_core_add_address_handler(struct fw_address_handler *handler,
                                const struct fw_address_region *region);
 void fw_core_remove_address_handler(struct fw_address_handler *handler);
+void fw_fill_response(struct fw_packet *response, u32 *request_header,
+                     int rcode, void *payload, size_t length);
 void fw_send_response(struct fw_card *card,
                      struct fw_request *request, int rcode);