V4L/DVB (9895): cx18: Refine the firmware load and firmware startup process
authorAndy Walls <awalls@radix.net>
Mon, 15 Dec 2008 00:26:25 +0000 (21:26 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 30 Dec 2008 11:39:09 +0000 (09:39 -0200)
Refine the firmware load and firmware startup process.  Significant changes
are to ensure the SCB and IPC area are correct before starting up the firmware,
and letting the CPU firmware start up the APU firmware for us.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/cx18/cx18-driver.c
drivers/media/video/cx18/cx18-firmware.c
drivers/media/video/cx18/cx18-mailbox.c
drivers/media/video/cx18/cx18-scb.c
drivers/media/video/cx18/cx23418.h

index d7baf1b..f50cf21 100644 (file)
@@ -878,6 +878,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
                goto free_i2c;
        }
        cx18_init_memory(cx);
+       cx18_init_scb(cx);
 
        /* Register IRQ */
        retval = request_irq(cx->dev->irq, cx18_irq_handler,
index 8eac843..e74f76d 100644 (file)
@@ -332,6 +332,10 @@ void cx18_init_memory(struct cx18 *cx)
 
 int cx18_firmware_init(struct cx18 *cx)
 {
+       u32 fw_entry_addr;
+       int sz, retries;
+       u32 api_args[MAX_MB_ARGUMENTS];
+
        /* Allow chip to control CLKRUN */
        cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
 
@@ -341,65 +345,62 @@ int cx18_firmware_init(struct cx18 *cx)
 
        cx18_msleep_timeout(1, 0);
 
+       /* If the CPU is still running */
+       if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+               CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+               return -EIO;
+       }
+
        cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
        cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
 
-       /* Only if the processor is not running */
-       if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) {
-               u32 fw_entry_addr = 0;
-               int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
-                              cx->enc_mem, cx, &fw_entry_addr);
-
-               if (sz <= 0)
-                       return sz;
-
-               /* Clear bit0 for APU to start from 0 */
-               cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
-
-               cx18_write_enc(cx, 0xE51FF004, 0);    /* ldr pc, [pc, #-4] */
-               cx18_write_enc(cx, fw_entry_addr, 4);
-
-               /* Start APU */
-               cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET,
-                                         0x00000000, 0x00010001);
-               cx18_msleep_timeout(500, 0);
-
-               sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
-                                       cx->enc_mem, cx);
-
-               if (sz > 0) {
-                       int retries = 0;
-
-                       /* start the CPU */
-                       cx18_write_reg_expect(cx,
-                                             0x00080000, CX18_PROC_SOFT_RESET,
-                                             0x00000000, 0x00080008);
-                       while (retries++ < 50) { /* Loop for max 500mS */
-                               if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET)
-                                    & 1) == 0)
-                                       break;
-                               cx18_msleep_timeout(10, 0);
-                       }
-                       cx18_msleep_timeout(200, 0);
-                       if (retries == 51) {
-                               CX18_ERR("Could not start the CPU\n");
-                               return -EIO;
-                       }
-               }
-               if (sz <= 0)
-                       return -EIO;
+       sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
+       if (sz <= 0)
+               return sz;
+
+       /* The SCB & IPC area *must* be correct before starting the firmwares */
+       cx18_init_scb(cx);
+
+       fw_entry_addr = 0;
+       sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
+                               &fw_entry_addr);
+       if (sz <= 0)
+               return sz;
+
+       /* Start the CPU. The CPU will take care of the APU for us. */
+       cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+                                 0x00000000, 0x00080008);
+
+       /* Wait up to 500 ms for the APU to come out of reset */
+       for (retries = 0;
+            retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+            retries++)
+               cx18_msleep_timeout(10, 0);
+
+       cx18_msleep_timeout(200, 0);
+
+       if (retries == 50 &&
+           (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+               CX18_ERR("Could not start the CPU\n");
+               return -EIO;
        }
 
        /*
-        * The CPU firmware apparently sets up to receive an interrupt for it's
-        * outgoing IRQ_CPU_TO_EPU_ACK to us (*boggle*).  We get an interrupt
-        * when it sends us an ack, but by the time we process it, that flag in
-        * the SW2 status register has been cleared by the CPU firmware.
-        * We'll prevent that not so useful behavior by clearing the CPU's
-        * interrupt enables for Ack IRQ's we want to process.
+        * The CPU had once before set up to receive an interrupt for it's
+        * outgoing IRQ_CPU_TO_EPU_ACK to us.  If it ever does this, we get an
+        * interrupt when it sends us an ack, but by the time we process it,
+        * that flag in the SW2 status register has been cleared by the CPU
+        * firmware.  We'll prevent that not so useful condition from happening
+        * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+        * process.
         */
        cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
 
+       /* Try a benign command to see if the CPU is alive and well */
+       sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+       if (sz < 0)
+               return sz;
+
        /* initialize GPIO */
        cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
        return 0;
index d623514..de5e723 100644 (file)
@@ -82,8 +82,9 @@ static const struct cx18_api_info api_info[] = {
        API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS,                    0),
        API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK,                 0),
        API_ENTRY(CPU, CX18_CPU_DE_SET_MDL,                     API_FAST),
-       API_ENTRY(CPU, CX18_APU_RESETAI,                        API_FAST),
        API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL,                 API_SLOW),
+       API_ENTRY(APU, CX18_APU_RESETAI,                        0),
+       API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32,                   0),
        API_ENTRY(0, 0,                                         0),
 };
 
index ac18bd9..34b4d03 100644 (file)
@@ -118,6 +118,5 @@ void cx18_init_scb(struct cx18 *cx)
        cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
                        &cx->scb->ipc_offset);
 
-       cx18_writel(cx, 1, &cx->scb->hpu_state);
        cx18_writel(cx, 1, &cx->scb->epu_state);
 }
index 668f968..601f3a2 100644 (file)
@@ -44,6 +44,7 @@
 
 /* All commands for CPU have the following mask set */
 #define CPU_CMD_MASK                           0x20000000
+#define CPU_CMD_MASK_DEBUG                     (CPU_CMD_MASK | 0x00000000)
 #define CPU_CMD_MASK_ACK                       (CPU_CMD_MASK | 0x80000000)
 #define CPU_CMD_MASK_CAPTURE                   (CPU_CMD_MASK | 0x00020000)
 #define CPU_CMD_MASK_TS                        (CPU_CMD_MASK | 0x00040000)
           0/zero/NULL means "I have nothing to say" */
 #define CX18_EPU_DEBUG                                 (EPU_CMD_MASK_DEBUG | 0x0003)
 
+/* Reads memory/registers (32-bit)
+   IN[0] - Address
+   OUT[1] - Value */
+#define CX18_CPU_DEBUG_PEEK32                  (CPU_CMD_MASK_DEBUG | 0x0003)
+
 /* Description: This command starts streaming with the set channel type
    IN[0] - Task handle. Handle of the task to start
    ReturnCode - One of the ERR_CAPTURE_... */