sd-as3525v1: set up bank selection data outside of the loop
[kugel-rb.git] / firmware / target / arm / as3525 / system-as3525.c
blob5eae1a35b3829f1218a24763e77635c0cb85dc0a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Rob Purchase
11 * Copyright © 2008 Rafaël Carré
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include "config.h"
24 #include "kernel.h"
25 #include "system.h"
26 #include "panic.h"
27 #include "ascodec-target.h"
28 #include "adc.h"
29 #include "dma-target.h"
30 #include "clock-target.h"
31 #include "fmradio_i2c.h"
32 #include "button-target.h"
33 #ifndef BOOTLOADER
34 #include "mmu-arm.h"
35 #endif
36 #include "backlight-target.h"
38 #define default_interrupt(name) \
39 extern __attribute__((weak,alias("UIRQ"))) void name (void)
41 static void UIRQ (void) __attribute__((interrupt ("IRQ")));
42 void irq_handler(void) __attribute__((interrupt ("IRQ")));
43 void fiq_handler(void) __attribute__((interrupt ("FIQ")));
45 default_interrupt(INT_WATCHDOG);
46 default_interrupt(INT_TIMER1);
47 default_interrupt(INT_TIMER2);
48 default_interrupt(INT_USB);
49 default_interrupt(INT_DMAC);
50 default_interrupt(INT_NAND);
51 default_interrupt(INT_IDE);
52 default_interrupt(INT_MCI0);
53 default_interrupt(INT_MCI1);
54 default_interrupt(INT_AUDIO);
55 default_interrupt(INT_SSP);
56 default_interrupt(INT_I2C_MS);
57 default_interrupt(INT_I2C_AUDIO);
58 default_interrupt(INT_I2SIN);
59 default_interrupt(INT_I2SOUT);
60 default_interrupt(INT_UART);
61 default_interrupt(INT_GPIOD);
62 default_interrupt(RESERVED1); /* Interrupt 17 : unused */
63 default_interrupt(INT_CGU);
64 default_interrupt(INT_MEMORY_STICK);
65 default_interrupt(INT_DBOP);
66 default_interrupt(RESERVED2); /* Interrupt 21 : unused */
67 default_interrupt(RESERVED3); /* Interrupt 22 : unused */
68 default_interrupt(RESERVED4); /* Interrupt 23 : unused */
69 default_interrupt(RESERVED5); /* Interrupt 24 : unused */
70 default_interrupt(RESERVED6); /* Interrupt 25 : unused */
71 default_interrupt(RESERVED7); /* Interrupt 26 : unused */
72 default_interrupt(RESERVED8); /* Interrupt 27 : unused */
73 default_interrupt(RESERVED9); /* Interrupt 28 : unused */
74 /* INT_GPIOA is declared in this file */
75 void INT_GPIOA(void);
76 default_interrupt(INT_GPIOB);
77 default_interrupt(INT_GPIOC);
79 static const char * const irqname[] =
81 "INT_WATCHDOG", "INT_TIMER1", "INT_TIMER2", "INT_USB", "INT_DMAC", "INT_NAND",
82 "INT_IDE", "INT_MCI0", "INT_MCI1", "INT_AUDIO", "INT_SSP", "INT_I2C_MS",
83 "INT_I2C_AUDIO", "INT_I2SIN", "INT_I2SOUT", "INT_UART", "INT_GPIOD", "RESERVED1",
84 "INT_CGU", "INT_MEMORY_STICK", "INT_DBOP", "RESERVED2", "RESERVED3", "RESERVED4",
85 "RESERVED5", "RESERVED6", "RESERVED7", "RESERVED8", "RESERVED9", "INT_GPIOA",
86 "INT_GPIOB", "INT_GPIOC"
89 static void UIRQ(void)
91 bool masked = false;
92 int status = VIC_IRQ_STATUS;
93 if(status == 0)
95 status = VIC_RAW_INTR; /* masked interrupts */
96 masked = true;
99 if(status == 0)
100 panicf("Unhandled IRQ (source unknown!)");
102 unsigned irq_no = 31 - __builtin_clz(status);
104 panicf("Unhandled %smasked IRQ %02X: %s (status 0x%8X)",
105 masked ? "" : "un", irq_no, irqname[irq_no], status);
108 struct vec_int_src
110 int source;
111 void (*isr) (void);
114 /* Vectored interrupts (16 available) */
115 struct vec_int_src vec_int_srcs[] =
117 /* Highest priority at the top of the list */
118 { INT_SRC_DMAC, INT_DMAC },
119 { INT_SRC_NAND, INT_NAND },
120 #if (defined HAVE_MULTIDRIVE && CONFIG_CPU == AS3525)
121 { INT_SRC_MCI0, INT_MCI0 },
122 #endif
123 { INT_SRC_USB, INT_USB, },
124 #ifdef HAVE_RECORDING
125 { INT_SRC_I2SIN, INT_I2SIN, },
126 #endif
127 { INT_SRC_TIMER1, INT_TIMER1 },
128 { INT_SRC_TIMER2, INT_TIMER2 },
129 { INT_SRC_I2C_AUDIO, INT_I2C_AUDIO },
130 { INT_SRC_AUDIO, INT_AUDIO },
131 #if defined(HAVE_HOTSWAP) || \
132 (defined(SANSA_FUZEV2) && !INCREASED_SCROLLWHEEL_POLLING)
133 { INT_SRC_GPIOA, INT_GPIOA, },
134 #endif
135 /* Lowest priority at the end of the list */
138 static void setup_vic(void)
140 const unsigned int n = sizeof(vec_int_srcs)/sizeof(vec_int_srcs[0]);
141 unsigned int i;
143 CGU_PERI |= CGU_VIC_CLOCK_ENABLE; /* enable VIC */
144 VIC_INT_EN_CLEAR = 0xffffffff; /* disable all interrupt lines */
145 VIC_INT_SELECT = 0; /* only IRQ, no FIQ */
147 *VIC_DEF_VECT_ADDR = UIRQ;
149 for(i = 0; i < n; i++)
151 VIC_VECT_ADDRS[i] = vec_int_srcs[i].isr;
152 VIC_VECT_CNTLS[i] = (1<<5) | vec_int_srcs[i].source;
156 void INT_GPIOA(void)
158 #ifdef HAVE_HOTSWAP
159 void sd_gpioa_isr(void);
160 sd_gpioa_isr();
161 #endif
162 #if defined(SANSA_FUZEV2) && !INCREASED_SCROLLWHEEL_POLLING
163 void button_gpioa_isr(void);
164 button_gpioa_isr();
165 #endif
168 void irq_handler(void)
170 (*VIC_VECT_ADDR)(); /* call the isr */
171 *VIC_VECT_ADDR = (void*)VIC_VECT_ADDR; /* any write will ack the irq */
174 void fiq_handler(void)
178 #if defined(SANSA_C200V2)
179 #include "dbop-as3525.h"
181 int c200v2_variant = 0;
183 static void check_model_variant(void)
185 unsigned int i;
186 unsigned int saved_dir = GPIOA_DIR;
188 /* Make A7 input */
189 GPIOA_DIR &= ~(1<<7);
190 /* wait a little to allow the pullup/pulldown resistor
191 * to charge the input capacitance */
192 for (i=0; i<1000; i++) asm volatile ("nop\n");
193 /* read the pullup/pulldown value on A7 to determine the variant */
194 if (GPIOA_PIN(7) == 0) {
196 * Backlight on A7.
198 c200v2_variant = 1;
199 } else {
201 * Backlight on A5.
203 c200v2_variant = 0;
205 GPIOA_DIR = saved_dir;
207 #else
208 static inline void check_model_variant(void)
211 #endif /* SANSA_C200V2*/
213 #if defined(BOOTLOADER)
214 static void sdram_delay(void)
216 int delay = 1024; /* arbitrary */
217 while (delay--) ;
220 /* Use the same initialization than OF */
221 static void sdram_init(void)
223 CGU_PERI |= (CGU_EXTMEM_CLOCK_ENABLE|CGU_EXTMEMIF_CLOCK_ENABLE);
225 MPMC_CONTROL = 0x1; /* enable MPMC */
227 MPMC_DYNAMIC_CONTROL = 0x183; /* SDRAM NOP, all clocks high */
228 sdram_delay();
230 MPMC_DYNAMIC_CONTROL = 0x103; /* SDRAM PALL, all clocks high */
231 sdram_delay();
233 MPMC_DYNAMIC_REFRESH = 0x138; /* 0x138 * 16 HCLK ticks between SDRAM refresh cycles */
235 MPMC_CONFIG = 0; /* little endian, HCLK:MPMCCLKOUT[3:0] ratio = 1:1 */
237 if(MPMC_PERIPH_ID2 & 0xf0)
238 MPMC_DYNAMIC_READ_CONFIG = 0x1; /* command delayed, clock out not delayed */
240 /* timings */
241 MPMC_DYNAMIC_tRP = 2;
242 MPMC_DYNAMIC_tRAS = 4;
243 MPMC_DYNAMIC_tSREX = 5;
244 MPMC_DYNAMIC_tAPR = 0;
245 MPMC_DYNAMIC_tDAL = 4;
246 MPMC_DYNAMIC_tWR = 2;
247 MPMC_DYNAMIC_tRC = 5;
248 MPMC_DYNAMIC_tRFC = 5;
249 MPMC_DYNAMIC_tXSR = 5;
250 MPMC_DYNAMIC_tRRD = 2;
251 MPMC_DYNAMIC_tMRD = 2;
253 #if defined(SANSA_CLIP) || defined(SANSA_M200V4) || defined(SANSA_C200V2)
254 /* 16 bits external bus, low power SDRAM, 16 Mbits = 2 Mbytes */
255 #define MEMORY_MODEL 0x21
257 #elif defined(SANSA_E200V2) || defined(SANSA_FUZE) || defined(SANSA_CLIPV2) \
258 || defined(SANSA_CLIPPLUS) || defined(SANSA_FUZEV2)
259 /* 16 bits external bus, high performance SDRAM, 64 Mbits = 8 Mbytes */
260 #define MEMORY_MODEL 0x5
262 #else
263 #error "The external memory in your player is unknown"
264 #endif
266 MPMC_DYNAMIC_RASCAS_0 = (2<<8)|2; /* CAS & RAS latency = 2 clock cycles */
267 MPMC_DYNAMIC_CONFIG_0 = (MEMORY_MODEL << 7);
269 MPMC_DYNAMIC_RASCAS_1 = MPMC_DYNAMIC_CONFIG_1 =
270 MPMC_DYNAMIC_RASCAS_2 = MPMC_DYNAMIC_CONFIG_2 =
271 MPMC_DYNAMIC_RASCAS_3 = MPMC_DYNAMIC_CONFIG_3 = 0;
273 MPMC_DYNAMIC_CONTROL = 0x82; /* SDRAM MODE, MPMCCLKOUT runs continuously */
275 /* program the SDRAM mode register */
276 /* FIXME: details the exact settings of mode register */
277 asm volatile(
278 "ldr r4, [%0]\n"
279 : : "p"(0x30000000+0x2300*MEM) : "r4");
281 /* SDRAM NORMAL, MPMCCLKOUT stopped when SDRAM is idle */
282 MPMC_DYNAMIC_CONTROL = 0x0;
284 MPMC_DYNAMIC_CONFIG_0 |= (1<<19); /* buffer enable */
286 #endif /* BOOTLOADER */
288 void system_init(void)
290 #if CONFIG_CPU == AS3525v2
291 CCU_SRC = 0x57D7BF0;
292 #else
293 CCU_SRC = 0x1fffff0
294 & ~CCU_SRC_IDE_EN; /* FIXME */
295 #endif
297 unsigned int reset_loops = 640;
298 while(reset_loops--)
299 CCU_SRL = CCU_SRL_MAGIC_NUMBER;
300 CCU_SRC = CCU_SRL = 0;
302 CCU_SCON = 1; /* AHB master's priority configuration :
303 TIC (Test Interface Controller) > DMA > USB > IDE > ARM */
305 CGU_PROC = 0; /* fclk 24 MHz */
306 #if CONFIG_CPU == AS3525v2
307 /* pclk is always based on PLLA, since we don't know the current PLLA speed,
308 * avoid having pclk too fast and hope it's not too low */
309 CGU_PERI |= 0xf << 2; /* pclk lowest */
310 #else
311 CGU_PERI &= ~0x7f; /* pclk 24 MHz */
312 #endif
314 /* bits 31:30 should be set to 0 in arm926-ejs */
315 asm volatile(
316 "mrc p15, 0, r0, c1, c0 \n" /* control register */
317 "bic r0, r0, #3<<30 \n" /* clears bus bits : sets fastbus */
318 "mcr p15, 0, r0, c1, c0 \n"
319 : : : "r0" );
321 CGU_COUNTA = CGU_LOCK_CNT;
322 CGU_PLLA = AS3525_PLLA_SETTING;
323 CGU_PLLASUP = 0; /* enable PLLA */
324 while(!(CGU_INTCTRL & CGU_PLLA_LOCK)); /* wait until PLLA is locked */
326 #if AS3525_MCLK_SEL == AS3525_CLK_PLLB
327 CGU_COUNTB = CGU_LOCK_CNT;
328 CGU_PLLB = AS3525_PLLB_SETTING;
329 CGU_PLLBSUP = 0; /* enable PLLB */
330 while(!(CGU_INTCTRL & CGU_PLLB_LOCK)); /* wait until PLLB is locked */
331 #endif
333 /* Set FCLK frequency */
334 CGU_PROC = ((AS3525_FCLK_POSTDIV << 4) |
335 (AS3525_FCLK_PREDIV << 2) |
336 AS3525_FCLK_SEL);
338 /* Set PCLK frequency */
339 CGU_PERI = ((CGU_PERI & ~0x7F) | /* reset divider & clksel bits */
340 (AS3525_PCLK_DIV0 << 2) |
341 #if CONFIG_CPU == AS3525
342 (AS3525_PCLK_DIV1 << 6) |
343 #endif
344 AS3525_PCLK_SEL);
346 #if defined(BOOTLOADER)
347 sdram_init();
348 #elif defined(SANSA_FUZE) || defined(SANSA_CLIP) || defined(SANSA_E200V2)
349 /* XXX: remove me when we have a new bootloader */
350 MPMC_DYNAMIC_CONTROL = 0x0; /* MPMCCLKOUT stops when all SDRAMs are idle */
351 #endif /* BOOTLOADER */
353 #if 0 /* the GPIO clock is already enabled by the dualboot function */
354 CGU_PERI |= CGU_GPIO_CLOCK_ENABLE;
355 #endif
357 /* enable timer interface for TIMER1 & TIMER2 */
358 CGU_PERI |= CGU_TIMERIF_CLOCK_ENABLE;
360 setup_vic();
362 dma_init();
364 ascodec_init();
366 #ifndef BOOTLOADER
367 /* setup isr for microsd monitoring and for fuzev2 scrollwheel irq */
368 #if defined(HAVE_HOTSWAP) || \
369 (defined(SANSA_FUZEV2) && !INCREASED_SCROLLWHEEL_POLLING)
370 VIC_INT_ENABLE = (INTERRUPT_GPIOA);
371 /* pin selection for irq happens in the drivers */
372 #endif
374 /* Initialize power management settings */
375 ascodec_write(AS3514_CVDD_DCDC3, AS314_CP_DCDC3_SETTING);
376 #if CONFIG_TUNER
377 fmradio_i2c_init();
378 #endif
379 #endif /* !BOOTLOADER */
380 check_model_variant();
383 void system_reboot(void)
385 _backlight_off();
386 /* use watchdog to reset */
387 CGU_PERI |= (CGU_WDOCNT_CLOCK_ENABLE | CGU_WDOIF_CLOCK_ENABLE);
388 WDT_LOAD = 1; /* set counter to 1 */
389 WDT_CONTROL = 3; /* enable watchdog counter & reset */
390 while(1);
393 void system_exception_wait(void)
395 /* wait until button release (if a button is pressed) */
396 while(button_read_device());
397 /* then wait until next button press */
398 while(!button_read_device());
401 int system_memory_guard(int newmode)
403 (void)newmode;
404 return 0;
407 #ifndef BOOTLOADER
408 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
410 #if CONFIG_CPU == AS3525
411 void set_cpu_frequency(long frequency)
413 if(frequency == CPUFREQ_MAX)
415 #ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE
416 /* Increasing frequency so boost voltage before change */
417 ascodec_write(AS3514_CVDD_DCDC3, (AS314_CP_DCDC3_SETTING | CVDD_1_20));
419 /* Some players run a bit low so use 1.175 volts instead of 1.20 */
420 /* Wait for voltage to be at least 1.175v before making fclk > 200 MHz */
421 while(adc_read(ADC_CVDD) < 470); /* 470 * .0025 = 1.175V */
422 #endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */
424 asm volatile(
425 "mrc p15, 0, r0, c1, c0 \n"
427 #ifdef ASYNCHRONOUS_BUS
428 "orr r0, r0, #3<<30 \n" /* asynchronous bus clocking */
429 #else
430 "bic r0, r0, #3<<30 \n" /* clear bus bits */
431 "orr r0, r0, #1<<30 \n" /* synchronous bus clocking */
432 #endif
434 "mcr p15, 0, r0, c1, c0 \n"
435 : : : "r0" );
437 cpu_frequency = CPUFREQ_MAX;
439 else
441 asm volatile(
442 "mrc p15, 0, r0, c1, c0 \n"
443 "bic r0, r0, #3<<30 \n" /* fastbus clocking */
444 "mcr p15, 0, r0, c1, c0 \n"
445 : : : "r0" );
448 #ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE
449 /* Decreasing frequency so reduce voltage after change */
450 ascodec_write(AS3514_CVDD_DCDC3, (AS314_CP_DCDC3_SETTING | CVDD_1_10));
451 #endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */
453 cpu_frequency = CPUFREQ_NORMAL;
456 #else /* as3525v2 */
457 /* FIXME : disabled for now, seems to cause buggy memory accesses
458 * Disabling MMU or putting the function in uncached memory seems to help? */
459 void set_cpu_frequency(long frequency)
461 int oldstatus = disable_irq_save();
463 /* We only have 2 settings */
464 cpu_frequency = (frequency == CPUFREQ_MAX) ? frequency : CPUFREQ_NORMAL;
466 if(frequency == CPUFREQ_MAX)
468 /* Change PCLK while FCLK is low, so it doesn't go too high */
469 CGU_PERI = (CGU_PERI & ~(0xF << 2)) | (AS3525_PCLK_DIV0 << 2);
471 CGU_PROC = ((AS3525_FCLK_POSTDIV << 4) |
472 (AS3525_FCLK_PREDIV << 2) |
473 AS3525_FCLK_SEL);
475 else
477 CGU_PROC = ((AS3525_FCLK_POSTDIV_UNBOOSTED << 4) |
478 (AS3525_FCLK_PREDIV << 2) |
479 AS3525_FCLK_SEL);
481 /* Change PCLK after FCLK is low, so it doesn't go too high */
482 CGU_PERI = (CGU_PERI & ~(0xF << 2)) | (AS3525_PCLK_DIV0_UNBOOSTED << 2);
485 restore_irq(oldstatus);
487 #endif
489 #endif /* HAVE_ADJUSTABLE_CPU_FREQ */
490 #endif /* !BOOTLOADER */