| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2013 by Amaury Pouly |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| #include "regs-pinctrl.h" |
| #include "regs-power.h" |
| #include "regs-lradc.h" |
| #include "regs-digctl.h" |
| |
| typedef unsigned long uint32_t; |
| |
| // target specific boot decision |
| enum boot_t |
| { |
| BOOT_STOP, /* power down */ |
| BOOT_ROCK, /* boot to Rockbox */ |
| BOOT_OF, /* boot to OF */ |
| }; |
| |
| /** |
| * Helper functions |
| */ |
| |
| static inline int __attribute__((always_inline)) read_gpio(int bank, int pin) |
| { |
| return (HW_PINCTRL_DINn(bank) >> pin) & 1; |
| } |
| |
| static inline int __attribute__((always_inline)) read_pswitch(void) |
| { |
| #if IMX233_SUBTARGET >= 3700 |
| return BF_RD(POWER_STS, PSWITCH); |
| #else |
| return BF_RD(DIGCTL_STATUS, PSWITCH); |
| #endif |
| } |
| |
| /* only works for channels <=7, always divide by 2, never accumulates */ |
| static inline void __attribute__((always_inline)) setup_lradc(int src) |
| { |
| BF_CLR(LRADC_CTRL0, SFTRST); |
| BF_CLR(LRADC_CTRL0, CLKGATE); |
| /* don't bother changing the source, we are early enough at boot so that |
| * channel x is mapped to source x */ |
| HW_LRADC_CHn_CLR(src) = BM_OR2(LRADC_CHn, NUM_SAMPLES, ACCUMULATE); |
| BF_SETV(LRADC_CTRL2, DIVIDE_BY_TWO, 1 << src); |
| } |
| |
| #define BP_LRADC_CTRL1_LRADCx_IRQ(x) (x) |
| #define BM_LRADC_CTRL1_LRADCx_IRQ(x) (1 << (x)) |
| |
| static inline int __attribute__((always_inline)) read_lradc(int src) |
| { |
| BF_CLR(LRADC_CTRL1, LRADCx_IRQ(src)); |
| BF_SETV(LRADC_CTRL0, SCHEDULE, 1 << src); |
| while(!BF_RD(LRADC_CTRL1, LRADCx_IRQ(src))); |
| return BF_RDn(LRADC_CHn, src, VALUE); |
| } |
| |
| static inline void __attribute__((noreturn)) power_down() |
| { |
| /* power down */ |
| HW_POWER_RESET = BM_OR2(POWER_RESET, UNLOCK, PWD); |
| while(1); |
| } |
| |
| /** |
| * Boot decision functions |
| */ |
| |
| #if defined(CREATIVE_ZENMOZAIC) || defined(CREATIVE_ZEN) || defined(CREATIVE_ZENXFI) \ |
| || defined(CREATIVE_ZENV) |
| static enum boot_t boot_decision() |
| { |
| setup_lradc(0); // setup LRADC channel 0 to read keys |
| /* make a decision */ |
| /* read keys */ |
| int val = read_lradc(0); |
| /* if back is pressed, boot to OF |
| * otherwise boot to RB */ |
| if(val >= 2650 && val < 2750) // conveniently, all players use the same value |
| return BOOT_OF; |
| return BOOT_ROCK; |
| } |
| #else |
| #warning You should define a target specific boot decision function |
| static int boot_decision() |
| { |
| return BOOT_ROCK; |
| } |
| #endif |
| |
| static int main(uint32_t rb_addr, uint32_t of_addr) |
| { |
| switch(boot_decision()) |
| { |
| case BOOT_ROCK: |
| return rb_addr; |
| case BOOT_OF: |
| /* fix back the loading address |
| /* NOTE: see mkzenboot for more details */ |
| *(uint32_t *)0x20 = of_addr; |
| return 0; |
| case BOOT_STOP: |
| default: |
| power_down(); |
| } |
| } |
| |
| /** Glue for the linker mostly */ |
| |
| extern uint32_t of_vector; |
| extern uint32_t rb_vector; |
| extern uint32_t boot_arg; |
| |
| void __attribute__((section(".start"))) start() |
| { |
| uint32_t addr = main(rb_vector, of_vector); |
| ((void (*)(uint32_t))addr)(boot_arg); |
| } |