diff options
author | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2021-12-15 21:04:28 +0100 |
---|---|---|
committer | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2021-12-24 18:05:53 +0100 |
commit | c876d3bbefe0dc00c27ca0c12d29da5874946962 (patch) | |
tree | 69f468a185a369b01998314bc3ecc19b70f4fcaa /utils/mkimxboot/dualboot/dualboot.c | |
parent | 6c6f0757d7a902feb293be165d1490c42bc8e7ad (diff) | |
download | rockbox-c876d3bbefe0dc00c27ca0c12d29da5874946962.tar.gz rockbox-c876d3bbefe0dc00c27ca0c12d29da5874946962.zip |
rbutil: Merge rbutil with utils folder.
rbutil uses several components from the utils folder, and can be
considered part of utils too. Having it in a separate folder is an
arbitrary split that doesn't help anymore these days, so merge them.
This also allows other utils to easily use libtools.make without the
need to navigate to a different folder.
Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21
Diffstat (limited to 'utils/mkimxboot/dualboot/dualboot.c')
-rw-r--r-- | utils/mkimxboot/dualboot/dualboot.c | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/utils/mkimxboot/dualboot/dualboot.c b/utils/mkimxboot/dualboot/dualboot.c new file mode 100644 index 0000000000..77b816bf76 --- /dev/null +++ b/utils/mkimxboot/dualboot/dualboot.c | |||
@@ -0,0 +1,323 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2013 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 | #include "regs/pinctrl.h" | ||
22 | #include "regs/power.h" | ||
23 | #include "regs/lradc.h" | ||
24 | #include "regs/digctl.h" | ||
25 | #include "regs/clkctrl.h" | ||
26 | |||
27 | #define BOOT_ROM_CONTINUE 0 /* continue boot */ | ||
28 | #define BOOT_ROM_SECTION 1 /* switch to new section *result_id */ | ||
29 | |||
30 | #define BOOT_ARG_CHARGE ('c' | 'h' << 8 | 'r' << 16 | 'g' << 24) | ||
31 | /** additional defines */ | ||
32 | #define BP_LRADC_CTRL4_LRADCxSELECT(x) (4 * (x)) | ||
33 | #define BM_LRADC_CTRL4_LRADCxSELECT(x) (0xf << (4 * (x))) | ||
34 | |||
35 | typedef unsigned long uint32_t; | ||
36 | |||
37 | /* we include the dualboot rtc code directly */ | ||
38 | #include "dualboot-imx233.h" | ||
39 | #include "dualboot-imx233.c" | ||
40 | |||
41 | // target specific boot context | ||
42 | enum context_t | ||
43 | { | ||
44 | CONTEXT_NORMAL, /* normal boot */ | ||
45 | CONTEXT_USB, /* USB plugged boot */ | ||
46 | CONTEXT_RTC, /* RTC wake up boot */ | ||
47 | }; | ||
48 | // target specific boot decision | ||
49 | enum boot_t | ||
50 | { | ||
51 | BOOT_STOP, /* power down */ | ||
52 | BOOT_ROCK, /* boot to Rockbox */ | ||
53 | BOOT_OF, /* boot to OF */ | ||
54 | }; | ||
55 | |||
56 | /** | ||
57 | * Helper functions | ||
58 | */ | ||
59 | |||
60 | static inline int __attribute__((always_inline)) read_gpio(int bank, int pin) | ||
61 | { | ||
62 | return (HW_PINCTRL_DINn(bank) >> pin) & 1; | ||
63 | } | ||
64 | |||
65 | static inline int __attribute__((always_inline)) read_pswitch(void) | ||
66 | { | ||
67 | #if IMX233_SUBTARGET >= 3700 | ||
68 | return BF_RD(POWER_STS, PSWITCH); | ||
69 | #else | ||
70 | return BF_RD(DIGCTL_STATUS, PSWITCH); | ||
71 | #endif | ||
72 | } | ||
73 | |||
74 | /* only works for channels <=7, always divide by 2, never accumulates */ | ||
75 | static inline void __attribute__((always_inline)) setup_lradc(int src) | ||
76 | { | ||
77 | BF_CLR(LRADC_CTRL0, SFTRST); | ||
78 | BF_CLR(LRADC_CTRL0, CLKGATE); | ||
79 | #if IMX233_SUBTARGET >= 3700 | ||
80 | HW_LRADC_CTRL4_CLR = BM_LRADC_CTRL4_LRADCxSELECT(src); | ||
81 | HW_LRADC_CTRL4_SET = src << BP_LRADC_CTRL4_LRADCxSELECT(src); | ||
82 | #endif | ||
83 | HW_LRADC_CHn_CLR(src) = BM_OR(LRADC_CHn, NUM_SAMPLES, ACCUMULATE); | ||
84 | BF_WR(LRADC_CTRL2_SET, DIVIDE_BY_TWO(1 << src)); | ||
85 | } | ||
86 | |||
87 | #define BP_LRADC_CTRL1_LRADCx_IRQ(x) (x) | ||
88 | #define BM_LRADC_CTRL1_LRADCx_IRQ(x) (1 << (x)) | ||
89 | |||
90 | static inline int __attribute__((always_inline)) read_lradc(int src) | ||
91 | { | ||
92 | BF_CLR(LRADC_CTRL1, LRADCx_IRQ(src)); | ||
93 | BF_WR(LRADC_CTRL0_SET, SCHEDULE(1 << src)); | ||
94 | while(!BF_RD(LRADC_CTRL1, LRADCx_IRQ(src))); | ||
95 | return BF_RD(LRADC_CHn(src), VALUE); | ||
96 | } | ||
97 | |||
98 | static inline void __attribute__((noreturn)) power_down() | ||
99 | { | ||
100 | #ifdef SANSA_FUZEPLUS | ||
101 | /* B0P09: this pin seems to be important to shutdown the hardware properly */ | ||
102 | HW_PINCTRL_MUXSELn_SET(0) = 3 << 18; | ||
103 | HW_PINCTRL_DOEn(0) = 1 << 9; | ||
104 | HW_PINCTRL_DOUTn(0) = 1 << 9; | ||
105 | #endif | ||
106 | /* power down */ | ||
107 | HW_POWER_RESET = BM_OR(POWER_RESET, UNLOCK, PWD); | ||
108 | while(1); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Boot decision functions | ||
113 | */ | ||
114 | |||
115 | #if defined(SANSA_FUZEPLUS) | ||
116 | static enum boot_t boot_decision(enum context_t context) | ||
117 | { | ||
118 | /* if volume down is hold, boot to OF */ | ||
119 | if(!read_gpio(1, 30)) | ||
120 | return BOOT_OF; | ||
121 | /* on normal boot, make sure power button is hold long enough */ | ||
122 | if(context == CONTEXT_NORMAL) | ||
123 | { | ||
124 | // monitor PSWITCH | ||
125 | int count = 0; | ||
126 | for(int i = 0; i < 550000; i++) | ||
127 | if(read_pswitch() == 1) | ||
128 | count++; | ||
129 | if(count < 400000) | ||
130 | return BOOT_STOP; | ||
131 | } | ||
132 | return BOOT_ROCK; | ||
133 | } | ||
134 | #elif defined(CREATIVE_ZENXFI2) | ||
135 | static int boot_decision(int context) | ||
136 | { | ||
137 | /* We are lacking buttons on the Zen X-Fi2 because on USB, the select button | ||
138 | * enters recovery mode ! So we can only use power but power is used to power up | ||
139 | * on normal boots and then select is free ! Thus use a non-uniform scheme: | ||
140 | * - normal boot/RTC: | ||
141 | * - no key: Rockbox | ||
142 | * - select: OF | ||
143 | * - USB boot: | ||
144 | * - no key: Rockbox | ||
145 | * - power: OF | ||
146 | */ | ||
147 | if(context == CONTEXT_USB) | ||
148 | return read_pswitch() == 1 ? BOOT_OF : BOOT_ROCK; | ||
149 | else | ||
150 | return !read_gpio(0, 14) ? BOOT_OF : BOOT_ROCK; | ||
151 | } | ||
152 | #elif defined(CREATIVE_ZENXFI3) | ||
153 | static int boot_decision(int context) | ||
154 | { | ||
155 | /* if volume down is hold, boot to OF */ | ||
156 | return !read_gpio(2, 7) ? BOOT_OF : BOOT_ROCK; | ||
157 | } | ||
158 | #elif defined(SONY_NWZE360) || defined(SONY_NWZE370) | ||
159 | static int local_decision(void) | ||
160 | { | ||
161 | /* read keys and pswitch */ | ||
162 | int val = read_lradc(0); | ||
163 | /* if hold is on, power off | ||
164 | * if back is pressed, boot to OF | ||
165 | * if play is pressed, boot RB | ||
166 | * otherwise power off */ | ||
167 | #ifdef SONY_NWZE360 | ||
168 | if(read_gpio(0, 9) == 0) | ||
169 | return BOOT_STOP; | ||
170 | #endif | ||
171 | if(val >= 1050 && val < 1150) | ||
172 | return BOOT_OF; | ||
173 | if(val >= 1420 && val < 1520) | ||
174 | return BOOT_ROCK; | ||
175 | return BOOT_STOP; | ||
176 | } | ||
177 | |||
178 | static int boot_decision(int context) | ||
179 | { | ||
180 | setup_lradc(0); // setup LRADC channel 0 to read keys | ||
181 | #ifdef SONY_NWZE360 | ||
182 | HW_PINCTRL_PULLn_SET(0) = 1 << 9; // enable pullup on hold key (B0P09) | ||
183 | #endif | ||
184 | /* make a decision */ | ||
185 | int decision = local_decision(); | ||
186 | /* in USB or alarm context, stick to it */ | ||
187 | if(context == CONTEXT_USB || context == CONTEXT_RTC) | ||
188 | { | ||
189 | /* never power down so replace power off decision by rockbox */ | ||
190 | return decision == BOOT_STOP ? BOOT_ROCK : decision; | ||
191 | } | ||
192 | /* otherwise start a 1 second timeout. Any decision change | ||
193 | * will result in power down */ | ||
194 | uint32_t tmo = HW_DIGCTL_MICROSECONDS + 1000000; | ||
195 | while(HW_DIGCTL_MICROSECONDS < tmo) | ||
196 | { | ||
197 | int new_dec = local_decision(); | ||
198 | if(new_dec != decision) | ||
199 | return BOOT_STOP; | ||
200 | } | ||
201 | return decision; | ||
202 | } | ||
203 | #elif defined(CREATIVE_ZENXFISTYLE) | ||
204 | static int boot_decision(int context) | ||
205 | { | ||
206 | setup_lradc(2); // setup LRADC channel 2 to read keys | ||
207 | /* make a decision */ | ||
208 | int val = read_lradc(2); | ||
209 | /* boot to OF if left is hold | ||
210 | * NOTE: VDDIO is set to 3.1V initially and the resistor ladder is wired to | ||
211 | * VDDIO so these values are not the same as in the main binary which is | ||
212 | * calibrated for VDDIO=3.3V */ | ||
213 | if(val >= 815 && val < 915) | ||
214 | return BOOT_OF; | ||
215 | return BOOT_ROCK; | ||
216 | } | ||
217 | #else | ||
218 | #warning You should define a target specific boot decision function | ||
219 | static int boot_decision(int context) | ||
220 | { | ||
221 | return BOOT_ROCK; | ||
222 | } | ||
223 | #endif | ||
224 | |||
225 | /** | ||
226 | * Context functions | ||
227 | */ | ||
228 | static inline enum context_t get_context(void) | ||
229 | { | ||
230 | #if IMX233_SUBTARGET >= 3780 | ||
231 | /* On the imx233 it's easy because we know the power up source */ | ||
232 | unsigned pwrup_src = BF_RD(POWER_STS, PWRUP_SOURCE); | ||
233 | if(pwrup_src & (1 << 5)) | ||
234 | return CONTEXT_USB; | ||
235 | else if(pwrup_src & (1 << 4)) | ||
236 | return CONTEXT_RTC; | ||
237 | else | ||
238 | return CONTEXT_NORMAL; | ||
239 | #else | ||
240 | /* On the other targets, we need to poke a few more registers */ | ||
241 | #endif | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * Charging function | ||
246 | */ | ||
247 | static inline void do_charge(void) | ||
248 | { | ||
249 | BF_CLR(LRADC_CTRL0, SFTRST); | ||
250 | BF_CLR(LRADC_CTRL0, CLKGATE); | ||
251 | BF_WR(LRADC_DELAYn(0), TRIGGER_LRADCS(0x80)); | ||
252 | BF_WR(LRADC_DELAYn(0), TRIGGER_DELAYS(0x1)); | ||
253 | BF_WR(LRADC_DELAYn(0), DELAY(200)); | ||
254 | BF_SET(LRADC_DELAYn(0), KICK); | ||
255 | BF_SET(LRADC_CONVERSION, AUTOMATIC); | ||
256 | BF_WR(LRADC_CONVERSION, SCALE_FACTOR_V(LI_ION)); | ||
257 | BF_WR(POWER_CHARGE, STOP_ILIMIT(1)); | ||
258 | BF_WR(POWER_CHARGE, BATTCHRG_I(0x10)); | ||
259 | BF_CLR(POWER_CHARGE, PWD_BATTCHRG); | ||
260 | #if IMX233_SUBTARGET >= 3780 | ||
261 | BF_WR(POWER_DCDC4P2, ENABLE_4P2(1)); | ||
262 | BF_CLR(POWER_5VCTRL, PWD_CHARGE_4P2); | ||
263 | BF_WR(POWER_5VCTRL, CHARGE_4P2_ILIMIT(0x10)); | ||
264 | #endif | ||
265 | while(1) | ||
266 | { | ||
267 | BF_WR(CLKCTRL_CPU, INTERRUPT_WAIT(1)); | ||
268 | asm volatile ( | ||
269 | "mcr p15, 0, %0, c7, c0, 4 \n" /* Wait for interrupt */ | ||
270 | "nop\n" /* Datasheet unclear: "The lr sent to handler points here after RTI"*/ | ||
271 | "nop\n" | ||
272 | : : "r"(0) | ||
273 | ); | ||
274 | } | ||
275 | } | ||
276 | |||
277 | static void set_updater_bits(void) | ||
278 | { | ||
279 | /* The OF will continue to updater if we clear 18 of PERSISTENT1. | ||
280 | * See dualboot-imx233.c in firmware/ for more explanation */ | ||
281 | HW_RTC_PERSISTENT1_CLR = 1 << 18; | ||
282 | } | ||
283 | |||
284 | int main(uint32_t arg, uint32_t *result_id) | ||
285 | { | ||
286 | if(arg == BOOT_ARG_CHARGE) | ||
287 | do_charge(); | ||
288 | /* tell rockbox that we can handle boot mode */ | ||
289 | imx233_dualboot_set_field(DUALBOOT_CAP_BOOT, 1); | ||
290 | /* if we were asked to boot in a special mode, do so */ | ||
291 | unsigned boot_mode = imx233_dualboot_get_field(DUALBOOT_BOOT); | ||
292 | /* clear boot mode to avoid any loop */ | ||
293 | imx233_dualboot_set_field(DUALBOOT_BOOT, IMX233_BOOT_NORMAL); | ||
294 | switch(boot_mode) | ||
295 | { | ||
296 | case IMX233_BOOT_UPDATER: | ||
297 | set_updater_bits(); | ||
298 | /* fallthrough */ | ||
299 | case IMX233_BOOT_OF: | ||
300 | /* continue booting */ | ||
301 | return BOOT_ROM_CONTINUE; | ||
302 | case IMX233_BOOT_NORMAL: | ||
303 | default: | ||
304 | break; | ||
305 | } | ||
306 | /* normal boot */ | ||
307 | switch(boot_decision(get_context())) | ||
308 | { | ||
309 | case BOOT_ROCK: | ||
310 | *result_id = arg; | ||
311 | return BOOT_ROM_SECTION; | ||
312 | case BOOT_OF: | ||
313 | return BOOT_ROM_CONTINUE; | ||
314 | case BOOT_STOP: | ||
315 | default: | ||
316 | power_down(); | ||
317 | } | ||
318 | } | ||
319 | |||
320 | int __attribute__((section(".start"))) start(uint32_t arg, uint32_t *result_id) | ||
321 | { | ||
322 | return main(arg, result_id); | ||
323 | } | ||