Merge branch 'x86-apic-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / firewire / ohci.c
index 23d1468..bcf792f 100644 (file)
@@ -253,7 +253,6 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card)
 #define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8
 
 #define OHCI1394_REGISTER_SIZE         0x800
-#define OHCI_LOOP_COUNT                        500
 #define OHCI1394_PCI_HCI_Control       0x40
 #define SELF_ID_BUF_SIZE               0x800
 #define OHCI_TCODE_PHY_PACKET          0x0e
@@ -264,6 +263,7 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
 #define PCI_DEVICE_ID_AGERE_FW643      0x5901
 #define PCI_DEVICE_ID_JMICRON_JMB38X_FW        0x2380
 #define PCI_DEVICE_ID_TI_TSB12LV22     0x8009
+#define PCI_VENDOR_ID_PINNACLE_SYSTEMS 0x11bd
 
 #define QUIRK_CYCLE_TIMER              1
 #define QUIRK_RESET_PACKET             2
@@ -513,6 +513,12 @@ static inline void flush_writes(const struct fw_ohci *ohci)
        reg_read(ohci, OHCI1394_Version);
 }
 
+/*
+ * Beware!  read_phy_reg(), write_phy_reg(), update_phy_reg(), and
+ * read_paged_phy_reg() require the caller to hold ohci->phy_reg_mutex.
+ * In other words, only use ohci_read_phy_reg() and ohci_update_phy_reg()
+ * directly.  Exceptions are intrinsically serialized contexts like pci_probe.
+ */
 static int read_phy_reg(struct fw_ohci *ohci, int addr)
 {
        u32 val;
@@ -521,6 +527,9 @@ static int read_phy_reg(struct fw_ohci *ohci, int addr)
        reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Read(addr));
        for (i = 0; i < 3 + 100; i++) {
                val = reg_read(ohci, OHCI1394_PhyControl);
+               if (!~val)
+                       return -ENODEV; /* Card was ejected. */
+
                if (val & OHCI1394_PhyControl_ReadDone)
                        return OHCI1394_PhyControl_ReadData(val);
 
@@ -544,6 +553,9 @@ static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val)
                  OHCI1394_PhyControl_Write(addr, val));
        for (i = 0; i < 3 + 100; i++) {
                val = reg_read(ohci, OHCI1394_PhyControl);
+               if (!~val)
+                       return -ENODEV; /* Card was ejected. */
+
                if (!(val & OHCI1394_PhyControl_WritePending))
                        return 0;
 
@@ -629,7 +641,6 @@ static void ar_context_link_page(struct ar_context *ctx, unsigned int index)
        ctx->last_buffer_index = index;
 
        reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
-       flush_writes(ctx->ohci);
 }
 
 static void ar_context_release(struct ar_context *ctx)
@@ -1001,18 +1012,16 @@ static void ar_context_run(struct ar_context *ctx)
 
        reg_write(ctx->ohci, COMMAND_PTR(ctx->regs), ctx->descriptors_bus | 1);
        reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN);
-       flush_writes(ctx->ohci);
 }
 
 static struct descriptor *find_branch_descriptor(struct descriptor *d, int z)
 {
-       int b, key;
+       __le16 branch;
 
-       b   = (le16_to_cpu(d->control) & DESCRIPTOR_BRANCH_ALWAYS) >> 2;
-       key = (le16_to_cpu(d->control) & DESCRIPTOR_KEY_IMMEDIATE) >> 8;
+       branch = d->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS);
 
        /* figure out which descriptor the branch address goes in */
-       if (z == 2 && (b == 3 || key == 2))
+       if (z == 2 && branch == cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))
                return d;
        else
                return d + z - 1;
@@ -1193,9 +1202,6 @@ static void context_append(struct context *ctx,
        wmb(); /* finish init of new descriptors before branch_address update */
        ctx->prev->branch_address = cpu_to_le32(d_bus | z);
        ctx->prev = find_branch_descriptor(d, z);
-
-       reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
-       flush_writes(ctx->ohci);
 }
 
 static void context_stop(struct context *ctx)
@@ -1205,19 +1211,20 @@ static void context_stop(struct context *ctx)
 
        reg_write(ctx->ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN);
        ctx->running = false;
-       flush_writes(ctx->ohci);
 
-       for (i = 0; i < 10; i++) {
+       for (i = 0; i < 1000; i++) {
                reg = reg_read(ctx->ohci, CONTROL_SET(ctx->regs));
                if ((reg & CONTEXT_ACTIVE) == 0)
                        return;
 
-               mdelay(1);
+               if (i)
+                       udelay(10);
        }
        fw_error("Error: DMA context still active (0x%08x)\n", reg);
 }
 
 struct driver_data {
+       u8 inline_data[8];
        struct fw_packet *packet;
 };
 
@@ -1301,20 +1308,28 @@ static int at_context_queue_packet(struct context *ctx,
                return -1;
        }
 
+       BUILD_BUG_ON(sizeof(struct driver_data) > sizeof(struct descriptor));
        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,
-                                      packet->payload_length, DMA_TO_DEVICE);
-               if (dma_mapping_error(ohci->card.device, payload_bus)) {
-                       packet->ack = RCODE_SEND_ERROR;
-                       return -1;
+               if (packet->payload_length > sizeof(driver_data->inline_data)) {
+                       payload_bus = dma_map_single(ohci->card.device,
+                                                    packet->payload,
+                                                    packet->payload_length,
+                                                    DMA_TO_DEVICE);
+                       if (dma_mapping_error(ohci->card.device, payload_bus)) {
+                               packet->ack = RCODE_SEND_ERROR;
+                               return -1;
+                       }
+                       packet->payload_bus     = payload_bus;
+                       packet->payload_mapped  = true;
+               } else {
+                       memcpy(driver_data->inline_data, packet->payload,
+                              packet->payload_length);
+                       payload_bus = d_bus + 3 * sizeof(*d);
                }
-               packet->payload_bus     = payload_bus;
-               packet->payload_mapped  = true;
 
                d[2].req_count    = cpu_to_le16(packet->payload_length);
                d[2].data_address = cpu_to_le32(payload_bus);
@@ -1340,7 +1355,9 @@ static int at_context_queue_packet(struct context *ctx,
 
        context_append(ctx, d, z, 4 - z);
 
-       if (!ctx->running)
+       if (ctx->running)
+               reg_write(ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
+       else
                context_run(ctx, 0);
 
        return 0;
@@ -1950,14 +1967,18 @@ static irqreturn_t irq_handler(int irq, void *data)
 
 static int software_reset(struct fw_ohci *ohci)
 {
+       u32 val;
        int i;
 
        reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
+       for (i = 0; i < 500; i++) {
+               val = reg_read(ohci, OHCI1394_HCControlSet);
+               if (!~val)
+                       return -ENODEV; /* Card was ejected. */
 
-       for (i = 0; i < OHCI_LOOP_COUNT; i++) {
-               if ((reg_read(ohci, OHCI1394_HCControlSet) &
-                    OHCI1394_HCControl_softReset) == 0)
+               if (!(val & OHCI1394_HCControl_softReset))
                        return 0;
+
                msleep(1);
        }
 
@@ -2066,8 +2087,6 @@ static int ohci_enable(struct fw_card *card,
 
        reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus);
        reg_write(ohci, OHCI1394_LinkControlSet,
-                 OHCI1394_LinkControl_rcvSelfID |
-                 OHCI1394_LinkControl_rcvPhyPkt |
                  OHCI1394_LinkControl_cycleTimerEnable |
                  OHCI1394_LinkControl_cycleMaster);
 
@@ -2094,9 +2113,6 @@ static int ohci_enable(struct fw_card *card,
        reg_write(ohci, OHCI1394_FairnessControl, 0);
        card->priority_budget_implemented = ohci->pri_req_max != 0;
 
-       ar_context_run(&ohci->ar_request_ctx);
-       ar_context_run(&ohci->ar_response_ctx);
-
        reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000);
        reg_write(ohci, OHCI1394_IntEventClear, ~0);
        reg_write(ohci, OHCI1394_IntMaskClear, ~0);
@@ -2186,6 +2202,14 @@ static int ohci_enable(struct fw_card *card,
        reg_write(ohci, OHCI1394_HCControlSet,
                  OHCI1394_HCControl_linkEnable |
                  OHCI1394_HCControl_BIBimageValid);
+
+       reg_write(ohci, OHCI1394_LinkControlSet,
+                 OHCI1394_LinkControl_rcvSelfID |
+                 OHCI1394_LinkControl_rcvPhyPkt);
+
+       ar_context_run(&ohci->ar_request_ctx);
+       ar_context_run(&ohci->ar_response_ctx);
+
        flush_writes(ohci);
 
        /* We are ready to go, reset bus to finish initialization. */
@@ -3112,6 +3136,14 @@ static int ohci_queue_iso(struct fw_iso_context *base,
        return ret;
 }
 
+static void ohci_flush_queue_iso(struct fw_iso_context *base)
+{
+       struct context *ctx =
+                       &container_of(base, struct iso_context, base)->context;
+
+       reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
+}
+
 static const struct fw_card_driver ohci_driver = {
        .enable                 = ohci_enable,
        .read_phy_reg           = ohci_read_phy_reg,
@@ -3128,6 +3160,7 @@ static const struct fw_card_driver ohci_driver = {
        .free_iso_context       = ohci_free_iso_context,
        .set_iso_channels       = ohci_set_iso_channels,
        .queue_iso              = ohci_queue_iso,
+       .flush_queue_iso        = ohci_flush_queue_iso,
        .start_iso              = ohci_start_iso,
        .stop_iso               = ohci_stop_iso,
 };
@@ -3170,6 +3203,11 @@ static int __devinit pci_probe(struct pci_dev *dev,
        int i, err;
        size_t size;
 
+       if (dev->vendor == PCI_VENDOR_ID_PINNACLE_SYSTEMS) {
+               dev_err(&dev->dev, "Pinnacle MovieBoard is not yet supported\n");
+               return -ENOSYS;
+       }
+
        ohci = kzalloc(sizeof(*ohci), GFP_KERNEL);
        if (ohci == NULL) {
                err = -ENOMEM;