diff options
Diffstat (limited to 'firmware/target/arm/imx233/dualboot-imx233.c')
-rw-r--r-- | firmware/target/arm/imx233/dualboot-imx233.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/firmware/target/arm/imx233/dualboot-imx233.c b/firmware/target/arm/imx233/dualboot-imx233.c new file mode 100644 index 0000000000..39d70f17ea --- /dev/null +++ b/firmware/target/arm/imx233/dualboot-imx233.c | |||
@@ -0,0 +1,221 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright © 2011 by Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | /* IMPORTANT NOTE This file is used by both Rockbox (firmware and bootloader) | ||
23 | * and the dualboot stub. The stub #include this file directly, so make sure | ||
24 | * this file is independent and does not requires anything from the firmware */ | ||
25 | #include "dualboot-imx233.h" | ||
26 | |||
27 | #ifdef HAVE_DUALBOOT_STUB | ||
28 | /** Persistent registers usage by the OF based on the Firmware SDK | ||
29 | * and support for firmware upgrades in Rockbox | ||
30 | * (this includes the Fuze+, ZEN X-Fi3, NWZ-E360/E370/E380) | ||
31 | * | ||
32 | * The following are used: | ||
33 | * - PERSISTENT0: mostly standard stuff described in the datasheet | ||
34 | * - PERSISTENT1: mostly proprietary stuff + some bits described in the datasheet | ||
35 | * - PERSISTENT2: used to keep track of time (see below) | ||
36 | * - PERSISTENT3: proprietary stuff | ||
37 | * - PERSISTENT4: unused | ||
38 | * - PERSISTENT5: used by Rockbox to tell the dualboot stub what to do | ||
39 | * | ||
40 | * In particular, the following bits are involved in the firmware upgrade process | ||
41 | * and thus worth mentioning (Px means PERSISTENTx). Some of this information | ||
42 | * might not be entirely accurate: | ||
43 | * - P1[18]: when 0, indicates to the freescale boot stub to start the updater | ||
44 | * rather than the main firmware (play) or the usb firmware (host) | ||
45 | * - P1[22]: when 0, indicates that the OF database/store should be rebuilt | ||
46 | * - P3[10]: when 0, indicates that the firmware has been upgraded | ||
47 | * - P3[11]: when 1, indicates to the freescale boot stub to boot without | ||
48 | * requiring the user to hold the power button for a small delay | ||
49 | * - P3[12]: when 1, indicates that the internal drive or micro-sd card was | ||
50 | * modified in USB mode | ||
51 | * - P3[16]: when 1, indicates that a firmware upgrade was attempted but aborted | ||
52 | * due to a too low battery | ||
53 | * | ||
54 | * To understand how all this works out together, recall that the boot sequence | ||
55 | * usually looks as follows (fslx = freescale boot stub stage x, in section 0 | ||
56 | * of the firmware; rb = rockbox dualboot stub), where arrows indicate boot flow | ||
57 | * (since every stage can choose to continue in the same section or jump to another): | ||
58 | * | ||
59 | * +---> host (usb) | ||
60 | * | | ||
61 | * fsl0 -> fsl1 -> fsl2 -> rb -> fsl3 -> fsl4 (updater) | ||
62 | * | | | ||
63 | * | +---> play (firmware) | ||
64 | * | | ||
65 | * +-----------> rock (bootloader) (-> loads rockbox) | ||
66 | * | ||
67 | * Note that the exact number of fsl stages is device-dependent, there 5 on the | ||
68 | * fuze+, 3 on the NWZs for example. | ||
69 | * | ||
70 | * The fsl3 decides which stage to boot based on the following logic (order is | ||
71 | * important): | ||
72 | * - if P1[18] is 0, it goes to fsl4, to perform a firmware upgrade | ||
73 | * - if usb is plugged, it goes to host, the OF USB mode | ||
74 | * - if P1[22] is 1, it requires the user to hold the power button for small | ||
75 | * delay and aborts boot if this is not the case | ||
76 | * - it goes to play, the OF normal firmware | ||
77 | * | ||
78 | * The fsl4 (updater) performs the following action: | ||
79 | * - it clears P1[18] so that next boot will be a normal boot (ie NOT updater) | ||
80 | * - if firmware.sb does not exist or is invalid, it reboots | ||
81 | * - if the battery is too low for an upgrade, it sets P3[16] | ||
82 | * otherwise, it performs a firmware upgrade and clear P1[22] | ||
83 | * - it shutdowns | ||
84 | * | ||
85 | * The play (firmware) performs the following actions: | ||
86 | * - if P1[22] is 0 or P3[12] is 1, it rebuilds the store (the 'loading' screen) | ||
87 | * and set P1[22] to 1 and P3[12] to 0 | ||
88 | * - if P3[16] is 1, it displays a 'battery was too low to upgrade' message | ||
89 | * and clears P3[16] | ||
90 | * - if P3[10] is 0, it displays a 'firmware was successfully upgraded' message | ||
91 | * and sets P3[10] to 1 | ||
92 | * - it performs its usual (crappy) functions | ||
93 | * | ||
94 | * The host (USB) performs the following actions: | ||
95 | * - it clears P1[18] so that the next boot will run the updater | ||
96 | * - it sets P3[11] to 1 so that the device will reboot without user intervention | ||
97 | * at the end | ||
98 | * - if the host modifies the internal drive or micro-SD card, it sets P3[12] | ||
99 | * to 1 and clears P1[22] | ||
100 | * - after USB is unplugged, it reboots | ||
101 | * | ||
102 | * Thus a typical firmware upgrade sequence will look like this: | ||
103 | * - initially, the main firmware is running and flags are in the following state: | ||
104 | * P1[18] = 1 (normal boot) | ||
105 | * P1[22] = 1 (store is clean) | ||
106 | * P3[10] = 1 (firmware has not been upgraded) | ||
107 | * P3[11] = 0 (user needs to hold power button to boot) | ||
108 | * P3[12] = 0 (drive is clean) | ||
109 | * - the user plugs the USB cable, play reboots, fsl3 boots to host because | ||
110 | * P1[18] = 1, the users put firmware.sb on the drive, thus modifying its | ||
111 | * content and then unplugs the drive; the device reboots with the following | ||
112 | * flags: | ||
113 | * P1[18] = 0 (updater boot) | ||
114 | * P1[22] = 0 (store is dirty) | ||
115 | * P3[10] = 1 (firmware has not been upgraded) | ||
116 | * P3[11] = 1 (user does not needs to hold power button to boot) | ||
117 | * P3[12] = 1 (drive is dirty) | ||
118 | * - fsl3 boots to the updater because P1[18] = 0, the updater sees firmware.sb | ||
119 | * and performs a firmware upgrade; the device then shutdowns with the following | ||
120 | * flags: | ||
121 | * P1[18] = 1 (normal boot) | ||
122 | * P1[22] = 0 (store is dirty) | ||
123 | * P3[10] = 0 (firmware has been upgraded) | ||
124 | * P3[11] = 1 (user does not needs to hold power button to boot) | ||
125 | * P3[12] = 1 (drive is dirty) | ||
126 | * - the user presses the power button, fsl3 boots to play (firmware) because | ||
127 | * P1[18] = 1, it rebuilds the store because P1[22] is clear, it then display | ||
128 | * a message to the user saying that the firmware has been upgraded because | ||
129 | * P3[10] is 0, and it resets the flags to same state as initially | ||
130 | * | ||
131 | * Note that the OF is lazy: it reboots to updater after USB mode in all cases | ||
132 | * (even if firmware.sb was not present). In this case, the updater simply clears | ||
133 | * the update flags and reboot immediately, thus it looks like a normal boot. | ||
134 | * | ||
135 | * | ||
136 | * To support firmware upgrades in Rockbox, we need to two things: | ||
137 | * - a way to tell rb (rockbox dual stub) to continue to fsl3 instead of booting | ||
138 | * rock (our bootloader) | ||
139 | * - a way to setup the persistent bits so that fsl3 will boot to fsl4 (updater) | ||
140 | * instead of booting host (usb) or play (firmware) | ||
141 | * | ||
142 | * The approach taken is to use PERSISTENT5 to tell the dualboot stub what we want | ||
143 | * to do. Since previous dualboot stubs did not support this, and that other actions | ||
144 | * may be added in the future, the registers stores both the capabilities of the | ||
145 | * dualboot stub (so that Rockbox can read them) and the actions that the dualboot | ||
146 | * stub must perform (so that Rockbox can write them). The register is encoded | ||
147 | * so that older/random values will be detected as garbage by newer Rockbox and | ||
148 | * dualboot stub, and that a value of 0 for a field always behaves as when it did | ||
149 | * not exist. More precisely, the bottom 16-bit must be 'RB' and | ||
150 | * the top 16-bit store the actual data. The following fields are currently defined: | ||
151 | * - CAP_BOOT(1 bit): supports booting to OF and UPDATER using the BOOT field | ||
152 | * - BOOT(2 bits): sets boot mode | ||
153 | * | ||
154 | * At the moment, BOOT supports three values: | ||
155 | * - IMX233_BOOT_NORMAL: the dualboot will do a normal boot (booting to Rockbox | ||
156 | * unless the user presses the magic button that boots to the OF) | ||
157 | * - IMX233_BOOT_OF: the dualboot stub will continue booting with fsl3 instead | ||
158 | * of Rockbox, but it will not touch any of OF persistent bits (this is useful | ||
159 | * to simply reboot to the OF for example) | ||
160 | * - IMX233_BOOT_UPDATER: the dualboot will setup OF persistents bits and | ||
161 | * continue so that fsl3 enters fsl4 (updater) | ||
162 | * In this scheme, Rockbox does not have to care about how exactly those actions | ||
163 | * are achieved, only the dualboot stub has to deal with the persistent bits. | ||
164 | * When the dualboot stubs see either OF or UPDATER, it clears BOOT back | ||
165 | * to NORMAL before continuing, so as to avoid any boot loop. | ||
166 | * | ||
167 | */ | ||
168 | |||
169 | #include "regs/rtc.h" | ||
170 | |||
171 | /* the persistent register we use */ | ||
172 | #define REG_DUALBOOT HW_RTC_PERSISTENT5 | ||
173 | /* the bottom 16-bit are a magic value to indicate that the content is valid */ | ||
174 | #define MAGIC_MASK 0xffff | ||
175 | #define MAGIC_VALUE ('R' | 'B' << 8) | ||
176 | /* CAP_BOOT: 1-bit (16) */ | ||
177 | #define CAP_BOOT_POS 16 | ||
178 | #define CAP_BOOT_MASK (1 << 16) | ||
179 | /* BOOT field: 2-bits (18-17) */ | ||
180 | #define BOOT_POS 17 | ||
181 | #define BOOT_MASK (3 << 17) | ||
182 | |||
183 | unsigned imx233_dualboot_get_field(enum imx233_dualboot_field_t field) | ||
184 | { | ||
185 | unsigned val = HW_RTC_PERSISTENT5; | ||
186 | /* if signature doesn't match, assume everything is 0 */ | ||
187 | if((val & MAGIC_MASK) != MAGIC_VALUE) | ||
188 | return 0; | ||
189 | #define match(field) \ | ||
190 | case DUALBOOT_##field: return ((val & field##_MASK) >> field##_POS); | ||
191 | switch(field) | ||
192 | { | ||
193 | match(CAP_BOOT) | ||
194 | match(BOOT) | ||
195 | default: return 0; /* unknown */ | ||
196 | } | ||
197 | #undef match | ||
198 | } | ||
199 | |||
200 | void imx233_dualboot_set_field(enum imx233_dualboot_field_t field, unsigned fval) | ||
201 | { | ||
202 | unsigned val = HW_RTC_PERSISTENT5; | ||
203 | /* if signature doesn't match, create an empty register */ | ||
204 | if((val & MAGIC_MASK) != MAGIC_VALUE) | ||
205 | val = MAGIC_VALUE; /* all field are 0 */ | ||
206 | #define match(field) \ | ||
207 | case DUALBOOT_##field: \ | ||
208 | val &= ~field##_MASK; \ | ||
209 | val |= (fval << field##_POS) & field##_MASK; \ | ||
210 | break; | ||
211 | switch(field) | ||
212 | { | ||
213 | match(CAP_BOOT) | ||
214 | match(BOOT) | ||
215 | default: break; | ||
216 | } | ||
217 | HW_RTC_PERSISTENT5 = val; | ||
218 | #undef match | ||
219 | } | ||
220 | |||
221 | #endif /* HAVE_DUALBOOT_STUB */ | ||