aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-firmware.c
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2008-12-31 07:35:57 -0500
committerRusty Russell <rusty@rustcorp.com.au>2008-12-31 07:35:57 -0500
commit2ca1a615835d9f4990f42102ab1f2ef434e7e89c (patch)
tree726cf3d5f29a6c66c44e4bd68e7ebed2fd83d059 /drivers/media/video/cx18/cx18-firmware.c
parente12f0102ac81d660c9f801d0a0e10ccf4537a9de (diff)
parent6a94cb73064c952255336cc57731904174b2c58f (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: arch/x86/kernel/io_apic.c
Diffstat (limited to 'drivers/media/video/cx18/cx18-firmware.c')
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c229
1 files changed, 169 insertions, 60 deletions
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index 51534428cd00..1fa95da1575e 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -2,6 +2,7 @@
2 * cx18 firmware functions 2 * cx18 firmware functions
3 * 3 *
4 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> 4 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
5 * Copyright (C) 2008 Andy Walls <awalls@radix.net>
5 * 6 *
6 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 8 * it under the terms of the GNU General Public License as published by
@@ -25,6 +26,7 @@
25#include "cx18-irq.h" 26#include "cx18-irq.h"
26#include "cx18-firmware.h" 27#include "cx18-firmware.h"
27#include "cx18-cards.h" 28#include "cx18-cards.h"
29#include "cx18-av-core.h"
28#include <linux/firmware.h> 30#include <linux/firmware.h>
29 31
30#define CX18_PROC_SOFT_RESET 0xc70010 32#define CX18_PROC_SOFT_RESET 0xc70010
@@ -121,6 +123,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
121 if (cx18_raw_readl(cx, dst) != *src) { 123 if (cx18_raw_readl(cx, dst) != *src) {
122 CX18_ERR("Mismatch at offset %x\n", i); 124 CX18_ERR("Mismatch at offset %x\n", i);
123 release_firmware(fw); 125 release_firmware(fw);
126 cx18_setup_page(cx, 0);
124 return -EIO; 127 return -EIO;
125 } 128 }
126 dst++; 129 dst++;
@@ -131,10 +134,12 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
131 CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); 134 CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
132 size = fw->size; 135 size = fw->size;
133 release_firmware(fw); 136 release_firmware(fw);
137 cx18_setup_page(cx, SCB_OFFSET);
134 return size; 138 return size;
135} 139}
136 140
137static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) 141static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
142 u32 *entry_addr)
138{ 143{
139 const struct firmware *fw = NULL; 144 const struct firmware *fw = NULL;
140 int i, j; 145 int i, j;
@@ -149,9 +154,11 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
149 if (request_firmware(&fw, fn, &cx->dev->dev)) { 154 if (request_firmware(&fw, fn, &cx->dev->dev)) {
150 CX18_ERR("unable to open firmware %s\n", fn); 155 CX18_ERR("unable to open firmware %s\n", fn);
151 CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); 156 CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
157 cx18_setup_page(cx, 0);
152 return -ENOMEM; 158 return -ENOMEM;
153 } 159 }
154 160
161 *entry_addr = 0;
155 src = (const u32 *)fw->data; 162 src = (const u32 *)fw->data;
156 vers = fw->data + sizeof(seghdr); 163 vers = fw->data + sizeof(seghdr);
157 sz = fw->size; 164 sz = fw->size;
@@ -168,10 +175,12 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
168 } 175 }
169 CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, 176 CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
170 seghdr.addr + seghdr.size - 1); 177 seghdr.addr + seghdr.size - 1);
178 if (*entry_addr == 0)
179 *entry_addr = seghdr.addr;
171 if (offset + seghdr.size > sz) 180 if (offset + seghdr.size > sz)
172 break; 181 break;
173 for (i = 0; i < seghdr.size; i += 4096) { 182 for (i = 0; i < seghdr.size; i += 4096) {
174 cx18_setup_page(cx, offset + i); 183 cx18_setup_page(cx, seghdr.addr + i);
175 for (j = i; j < seghdr.size && j < i + 4096; j += 4) { 184 for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
176 /* no need for endianness conversion on the ppc */ 185 /* no need for endianness conversion on the ppc */
177 cx18_raw_writel(cx, src[(offset + j) / 4], 186 cx18_raw_writel(cx, src[(offset + j) / 4],
@@ -181,6 +190,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
181 CX18_ERR("Mismatch at offset %x\n", 190 CX18_ERR("Mismatch at offset %x\n",
182 offset + j); 191 offset + j);
183 release_firmware(fw); 192 release_firmware(fw);
193 cx18_setup_page(cx, 0);
184 return -EIO; 194 return -EIO;
185 } 195 }
186 } 196 }
@@ -192,16 +202,17 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
192 fn, apu_version, fw->size); 202 fn, apu_version, fw->size);
193 size = fw->size; 203 size = fw->size;
194 release_firmware(fw); 204 release_firmware(fw);
195 /* Clear bit0 for APU to start from 0 */ 205 cx18_setup_page(cx, 0);
196 cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
197 return size; 206 return size;
198} 207}
199 208
200void cx18_halt_firmware(struct cx18 *cx) 209void cx18_halt_firmware(struct cx18 *cx)
201{ 210{
202 CX18_DEBUG_INFO("Preparing for firmware halt.\n"); 211 CX18_DEBUG_INFO("Preparing for firmware halt.\n");
203 cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ 212 cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
204 cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL); 213 0x0000000F, 0x000F000F);
214 cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
215 0x00000002, 0x00020002);
205} 216}
206 217
207void cx18_init_power(struct cx18 *cx, int lowpwr) 218void cx18_init_power(struct cx18 *cx, int lowpwr)
@@ -211,9 +222,48 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
211 cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); 222 cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
212 223
213 /* ADEC out of sleep */ 224 /* ADEC out of sleep */
214 cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL); 225 cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
215 226 0x00000000, 0x00020002);
216 /* The fast clock is at 200/245 MHz */ 227
228 /*
229 * The PLL parameters are based on the external crystal frequency that
230 * would ideally be:
231 *
232 * NTSC Color subcarrier freq * 8 =
233 * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
234 *
235 * The accidents of history and rationale that explain from where this
236 * combination of magic numbers originate can be found in:
237 *
238 * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
239 * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
240 *
241 * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
242 * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
243 *
244 * As Mike Bradley has rightly pointed out, it's not the exact crystal
245 * frequency that matters, only that all parts of the driver and
246 * firmware are using the same value (close to the ideal value).
247 *
248 * Since I have a strong suspicion that, if the firmware ever assumes a
249 * crystal value at all, it will assume 28.636360 MHz, the crystal
250 * freq used in calculations in this driver will be:
251 *
252 * xtal_freq = 28.636360 MHz
253 *
254 * an error of less than 0.13 ppm which is way, way better than any off
255 * the shelf crystal will have for accuracy anyway.
256 *
257 * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors.
258 *
259 * Many thanks to Jeff Campbell and Mike Bradley for their extensive
260 * investigation, experimentation, testing, and suggested solutions of
261 * of audio/video sync problems with SVideo and CVBS captures.
262 */
263
264 /* the fast clock is at 200/245 MHz */
265 /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
266 /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
217 cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); 267 cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
218 cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, 268 cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
219 CX18_FAST_CLOCK_PLL_FRAC); 269 CX18_FAST_CLOCK_PLL_FRAC);
@@ -223,16 +273,36 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
223 cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); 273 cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
224 274
225 /* set slow clock to 125/120 MHz */ 275 /* set slow clock to 125/120 MHz */
226 cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); 276 /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
227 cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8, 277 /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
278 cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
279 cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
228 CX18_SLOW_CLOCK_PLL_FRAC); 280 CX18_SLOW_CLOCK_PLL_FRAC);
229 cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST); 281 cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
230 282
231 /* mpeg clock pll 54MHz */ 283 /* mpeg clock pll 54MHz */
284 /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
232 cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); 285 cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
233 cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); 286 cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
234 cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); 287 cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
235 288
289 /*
290 * VDCLK Integer = 0x0f, Post Divider = 0x04
291 * AIMCLK Integer = 0x0e, Post Divider = 0x16
292 */
293 cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
294
295 /* VDCLK Fraction = 0x2be2fe */
296 /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
297 cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
298
299 /* AIMCLK Fraction = 0x05227ad */
300 /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */
301 cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
302
303 /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
304 cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
305
236 /* Defaults */ 306 /* Defaults */
237 /* APU = SC or SC/2 = 125/62.5 */ 307 /* APU = SC or SC/2 = 125/62.5 */
238 /* EPU = SC = 125 */ 308 /* EPU = SC = 125 */
@@ -248,22 +318,34 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
248 /* VFC = disabled */ 318 /* VFC = disabled */
249 /* USB = disabled */ 319 /* USB = disabled */
250 320
251 cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004, 321 if (lowpwr) {
252 CX18_CLOCK_SELECT1); 322 cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
253 cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006, 323 0x00000020, 0xFFFFFFFF);
254 CX18_CLOCK_SELECT2); 324 cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
255 325 0x00000004, 0xFFFFFFFF);
256 cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1); 326 } else {
257 cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2); 327 /* This doesn't explicitly set every clock select */
328 cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
329 0x00000004, 0x00060006);
330 cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
331 0x00000006, 0x00060006);
332 }
258 333
259 cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1); 334 cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
260 cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2); 335 0x00000002, 0xFFFFFFFF);
336 cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
337 0x00000104, 0xFFFFFFFF);
338 cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
339 0x00009026, 0xFFFFFFFF);
340 cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
341 0x00003105, 0xFFFFFFFF);
261} 342}
262 343
263void cx18_init_memory(struct cx18 *cx) 344void cx18_init_memory(struct cx18 *cx)
264{ 345{
265 cx18_msleep_timeout(10, 0); 346 cx18_msleep_timeout(10, 0);
266 cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET); 347 cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
348 0x00000000, 0x00010001);
267 cx18_msleep_timeout(10, 0); 349 cx18_msleep_timeout(10, 0);
268 350
269 cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); 351 cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
@@ -282,13 +364,15 @@ void cx18_init_memory(struct cx18 *cx)
282 364
283 cx18_msleep_timeout(10, 0); 365 cx18_msleep_timeout(10, 0);
284 366
285 cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET); 367 cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
368 0x00000000, 0x00020002);
286 cx18_msleep_timeout(10, 0); 369 cx18_msleep_timeout(10, 0);
287 370
288 /* use power-down mode when idle */ 371 /* use power-down mode when idle */
289 cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); 372 cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
290 373
291 cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN); 374 cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
375 0x00000001, 0x00010001);
292 376
293 cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); 377 cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
294 cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); 378 cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
@@ -307,51 +391,76 @@ void cx18_init_memory(struct cx18 *cx)
307 391
308int cx18_firmware_init(struct cx18 *cx) 392int cx18_firmware_init(struct cx18 *cx)
309{ 393{
394 u32 fw_entry_addr;
395 int sz, retries;
396 u32 api_args[MAX_MB_ARGUMENTS];
397
310 /* Allow chip to control CLKRUN */ 398 /* Allow chip to control CLKRUN */
311 cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); 399 cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
312 400
313 cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ 401 /* Stop the firmware */
402 cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
403 0x0000000F, 0x000F000F);
314 404
315 cx18_msleep_timeout(1, 0); 405 cx18_msleep_timeout(1, 0);
316 406
407 /* If the CPU is still running */
408 if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
409 CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
410 return -EIO;
411 }
412
317 cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); 413 cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
318 cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); 414 cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
319 415
320 /* Only if the processor is not running */ 416 sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
321 if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { 417 if (sz <= 0)
322 int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", 418 return sz;
323 cx->enc_mem, cx); 419
324 420 /* The SCB & IPC area *must* be correct before starting the firmwares */
325 cx18_write_enc(cx, 0xE51FF004, 0); 421 cx18_init_scb(cx);
326 cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */ 422
327 /* Start APU */ 423 fw_entry_addr = 0;
328 cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET); 424 sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
329 cx18_msleep_timeout(500, 0); 425 &fw_entry_addr);
330 426 if (sz <= 0)
331 sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", 427 return sz;
332 cx->enc_mem, cx); 428
333 429 /* Start the CPU. The CPU will take care of the APU for us. */
334 if (sz > 0) { 430 cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
335 int retries = 0; 431 0x00000000, 0x00080008);
336 432
337 /* start the CPU */ 433 /* Wait up to 500 ms for the APU to come out of reset */
338 cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET); 434 for (retries = 0;
339 while (retries++ < 50) { /* Loop for max 500mS */ 435 retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
340 if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) 436 retries++)
341 & 1) == 0) 437 cx18_msleep_timeout(10, 0);
342 break; 438
343 cx18_msleep_timeout(10, 0); 439 cx18_msleep_timeout(200, 0);
344 } 440
345 cx18_msleep_timeout(200, 0); 441 if (retries == 50 &&
346 if (retries == 51) { 442 (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
347 CX18_ERR("Could not start the CPU\n"); 443 CX18_ERR("Could not start the CPU\n");
348 return -EIO; 444 return -EIO;
349 }
350 }
351 if (sz <= 0)
352 return -EIO;
353 } 445 }
446
447 /*
448 * The CPU had once before set up to receive an interrupt for it's
449 * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an
450 * interrupt when it sends us an ack, but by the time we process it,
451 * that flag in the SW2 status register has been cleared by the CPU
452 * firmware. We'll prevent that not so useful condition from happening
453 * by clearing the CPU's interrupt enables for Ack IRQ's we want to
454 * process.
455 */
456 cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
457
458 /* Try a benign command to see if the CPU is alive and well */
459 sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
460 if (sz < 0)
461 return sz;
462
354 /* initialize GPIO */ 463 /* initialize GPIO */
355 cx18_write_reg(cx, 0x14001400, 0xC78110); 464 cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
356 return 0; 465 return 0;
357} 466}