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 d7baf1b97cb8acb39e7b452bdb63813e35d1834e..f50cf2167adc1a6082789aef840915a53142f9f6 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 8eac84314d40c31fd626fd853915d8f2c2aaf957..e74f76d47df5264d028d5e9e79545a5ac5587d8b 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 d62351466e26ae31be7204cfd75b4eb65facf156..de5e723fdf445bbbbd66181c8ac26f5175f716e0 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 ac18bd9326d5a7e36f55b284c46e303193700e9f..34b4d03c55cda9692668ce05a0a12aa818aa22f7 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 668f968d7761846cb17ae5d046f9154ca68be08f..601f3a2ab7425d0873fec3d9ea0eb504d8d3496e 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_... */