V4L/DVB (9895): cx18: Refine the firmware load and firmware startup process

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>
This commit is contained in:
Andy Walls 2008-12-14 21:26:25 -03:00 committed by Mauro Carvalho Chehab
parent 50b86bac6a
commit fd6b9c978d
5 changed files with 54 additions and 46 deletions

View 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,

View 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);
sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
if (sz <= 0)
return sz;
if (sz <= 0)
return sz;
/* The SCB & IPC area *must* be correct before starting the firmwares */
cx18_init_scb(cx);
/* Clear bit0 for APU to start from 0 */
cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
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;
cx18_write_enc(cx, 0xE51FF004, 0); /* ldr pc, [pc, #-4] */
cx18_write_enc(cx, fw_entry_addr, 4);
/* 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);
/* Start APU */
cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET,
0x00000000, 0x00010001);
cx18_msleep_timeout(500, 0);
/* 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);
sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
cx->enc_mem, cx);
cx18_msleep_timeout(200, 0);
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;
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;

View 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),
};

View 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);
}

View 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)
@ -71,6 +72,11 @@
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_... */