summaryrefslogtreecommitdiffstats
path: root/rbutil/mkzenboot/dualboot/dualboot.c
blob: 0b32772afb57f3d1416c19788703388545b44bea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/***************************************************************************
 *             __________               __   ___.
 *   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);
}