pandora: add a boot menu and it's helpers
[pandora-u-boot.git] / board / pandora / menu.c
1 #include <common.h>
2 #include <asm/io.h>
3 #include <asm/arch/gpio.h>
4 #include <hush.h>
5 #include <malloc.h>
6 #include <lcd.h>
7 #include <twl4030.h>
8
9 /* game buttons as in GPIO bank 4 */
10 #define BTN_R           GPIO9
11 #define BTN_UP          GPIO14
12 #define BTN_DOWN        GPIO7
13 #define BTN_G2          GPIO15
14 #define BTN_G3          GPIO10
15
16 struct menu_item {
17         const char *name;
18         int (*handler)(struct menu_item *item);
19         char *cmd;
20 };
21
22 static struct menu_item *menu_items[24];
23 static int menu_item_count;
24
25 static int do_cmd(const char *fmt, ...)
26 {
27         char cmdbuff[256];
28         va_list args;
29
30         va_start(args, fmt);
31         vsprintf(cmdbuff, fmt, args);
32         va_end(args);
33
34         return !parse_string_outer(cmdbuff,
35                 FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
36 }
37
38 static u32 menu_wait_for_input(int down)
39 {
40         struct gpio *gpio4_base = (struct gpio *)OMAP34XX_GPIO4_BASE;
41         u32 btns;
42
43         while (1) {
44                 btns = ~readl(&gpio4_base->datain) &
45                         (BTN_UP|BTN_DOWN|BTN_G2|BTN_G3);
46                 if (!btns == !down)
47                         break;
48                 udelay(5000);
49         }
50
51         return btns;
52 }
53
54 static int menu_do_default(struct menu_item *item)
55 {
56         return 1;
57 }
58
59 static int menu_do_poweroff(struct menu_item *item)
60 {
61         u8 d;
62
63         printf("power off.\n");
64
65         twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, &d, TWL4030_PM_MASTER_P1_SW_EVENTS);
66         d |= TWL4030_PM_MASTER_SW_EVENTS_DEVOFF;
67         twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, d, TWL4030_PM_MASTER_P1_SW_EVENTS);
68
69         return 0;
70 }
71
72 static int menu_do_usb_serial(struct menu_item *item)
73 {
74         do_cmd("dcache off"); /* doesn't work with dcache */
75         do_cmd("usbinit");
76         printf("Switched to USB serial.\n");
77
78         setenv("stdout", "usbtty");
79         setenv("stdin", "usbtty");
80         setenv("stderr", "usbtty");
81         setenv("bootcmd", "");
82         return 1;
83 }
84
85 static int menu_do_serial(struct menu_item *item)
86 {
87         printf("Switched to serial.\n");
88
89         setenv("stdout", "serial");
90         setenv("bootcmd", "");
91         return 1;
92 }
93
94 static int menu_do_script_cmd(struct menu_item *item)
95 {
96         int failed = 0;
97
98         if (item->cmd == NULL || !do_cmd(item->cmd))
99                 failed = 1;
100
101         printf("script %s.\n", failed ? "failed" : "finished");
102         flush_dcache_all();
103         menu_wait_for_input(0);
104         menu_wait_for_input(1);
105         return 0;
106 }
107
108 static void add_menu_item(const char *name,
109         int (*handler)(struct menu_item *), const char *cmd)
110 {
111         struct menu_item *mitem;
112
113         mitem = malloc(sizeof(*mitem));
114         if (mitem == NULL)
115                 return;
116         mitem->name = strdup(name);
117         mitem->handler = handler;
118         mitem->cmd = strdup(cmd);
119
120         if (menu_item_count < ARRAY_SIZE(menu_items))
121                 menu_items[menu_item_count++] = mitem;
122 }
123
124 static char *bootmenu_next_ctl(char *p)
125 {
126         while (*p && *p != '|' && *p != '\r' && *p != '\n')
127                 p++;
128
129         return p;
130 }
131
132 static char *bootmenu_skip_blanks(char *p)
133 {
134         while (*p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
135                 p++;
136
137         return p;
138 }
139
140 static char *bootmenu_skip_line(char *p)
141 {
142         while (*p && *p != '\r' && *p != '\n')
143                 p++;
144
145         return p;
146 }
147
148 static void parse_bootmenu(char *buf)
149 {
150         char *p = buf, *name, *cmd;
151         int i;
152
153         for (i = 1; ; i++)
154         {
155                 p = bootmenu_skip_blanks(p);
156                 if (*p == 0)
157                         break;
158                 if (*p == '#') {
159                         p = bootmenu_skip_line(p);
160                         continue;
161                 }
162
163                 name = p;
164                 p = bootmenu_next_ctl(p);
165                 if (*p != '|') {
166                         printf("bootmenu.txt: invalid line ~%d\n", i);
167                         p = bootmenu_skip_line(p);
168                         continue;
169                 }
170                 *p++ = 0;
171
172                 cmd = p;
173                 p = bootmenu_skip_line(p);
174                 if (*p != 0)
175                         *p++ = 0;
176
177                 add_menu_item(name, menu_do_script_cmd, cmd);
178         }
179 }
180
181 static struct menu_item default_menu_items[] = {
182         { "default boot",       menu_do_default, },
183         { "power off",          menu_do_poweroff, },
184         { "USB serial prompt",  menu_do_usb_serial, },
185         { "serial prompt",      menu_do_serial, },
186 };
187
188 static void menu_init(void)
189 {
190         const char *check_format1 = "%sload mmc1 0:%d ${loadaddr} boot.scr 4";
191         const char *check_format2 = "%sload mmc1 0:%d ${loadaddr} boot.txt 4";
192         const char *run_format1 = "%sload mmc1 0:%d ${loadaddr} boot.scr;source ${loadaddr}";
193         const char *run_format2 = "mw.l ${loadaddr} 0 1024;%sload mmc1 0:%d ${loadaddr} boot.txt;"
194                                         "ssource ${loadaddr}";
195         disk_partition_t part_info;
196         block_dev_desc_t *dev_desc;
197         char tmp_name[32], tmp_cmd[128];
198         int i;
199
200         for (i = 0; i < 2; i++)
201                 menu_items[i] = &default_menu_items[i];
202         menu_item_count = i;
203
204         if (!do_cmd("mmc rescan"))
205                 goto no_mmc;
206
207         dev_desc = get_dev("mmc1", 0);
208         if (dev_desc == NULL) {
209                 printf("dev desc null\n");
210                 goto no_mmc;
211         }
212
213         /* kill stdout while we search for bootfiles */
214         setenv("stdout", "nulldev");
215
216         for (i = 1; menu_item_count < ARRAY_SIZE(menu_items); i++) {
217                 if (get_partition_info(dev_desc, i, &part_info))
218                         break;
219                 if (do_cmd("fatls mmc1 0:%d", i)) {
220                         if (do_cmd(check_format1, "fat", i)) {
221                                 sprintf(tmp_cmd, run_format1, "fat", i);
222                                 goto found;
223                         }
224                         if (do_cmd(check_format2, "fat", i)) {
225                                 sprintf(tmp_cmd, run_format2, "fat", i);
226                                 goto found;
227                         }
228                         continue;
229                 }
230                 if (do_cmd("ext2ls mmc1 0:%d", i)) {
231                         if (do_cmd(check_format1, "ext2", i)) {
232                                 sprintf(tmp_cmd, run_format1, "ext2", i);
233                                 goto found;
234                         }
235                         if (do_cmd(check_format2, "ext2", i)) {
236                                 sprintf(tmp_cmd, run_format2, "ext2", i);
237                                 goto found;
238                         }
239                         continue;
240                 }
241                 continue;
242
243 found:
244                 sprintf(tmp_name, "boot from SD1:%d", i);
245                 add_menu_item(tmp_name, menu_do_script_cmd, tmp_cmd);
246         }
247
248 no_mmc:
249         setenv("stdout", "serial");
250
251         if (do_cmd("ubi part boot && ubifsmount boot")) {
252                 ulong addr = getenv_ulong("loadaddr", 16, 0);
253                 if ((int)addr < (int)0x90000000) {
254                         if (do_cmd("ubifsload ${loadaddr} bootmenu.txt")) {
255                                 ulong size = getenv_ulong("filesize", 16, 0);
256                                 *(char *)(addr + size) = 0;
257                                 parse_bootmenu((char *)addr);
258                         }
259                 }
260         }
261
262         for (i = 2; i < ARRAY_SIZE(default_menu_items); i++) {
263                 if (menu_item_count >= ARRAY_SIZE(menu_items))
264                         break;
265                 menu_items[menu_item_count++] = &default_menu_items[i];
266         }
267
268         setenv("stdout", "lcd");
269 }
270
271 static int boot_menu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
272 {
273         int i, sel = 0, max_sel;
274         int tl_row;
275         u32 btns;
276
277         menu_init();
278
279         tl_row = panel_info.vl_row / 16 / 2 - (menu_item_count + 2) / 2;
280         max_sel = menu_item_count - 1;
281
282         console_col = 3;
283         console_row = tl_row;
284         lcd_printf("Boot menu");
285
286         while (1)
287         {
288                 for (i = 0; i < menu_item_count; i++) {
289                         console_col = 3;
290                         console_row = tl_row + 2 + i;
291                         lcd_printf(menu_items[i]->name);
292                 }
293
294                 for (i = 0; i < menu_item_count; i++) {
295                         console_col = 1;
296                         console_row = tl_row + 2 + i;
297                         lcd_printf(i == sel ? ">" : " ");
298                 }
299
300                 flush_dcache_all();
301                 menu_wait_for_input(0);
302                 btns = menu_wait_for_input(1);
303                 if (btns & BTN_UP) {
304                         sel--;
305                         if (sel < 0)
306                                 sel = max_sel;
307                 }
308                 else if (btns & BTN_DOWN) {
309                         sel++;
310                         if (sel > max_sel)
311                                 sel = 0;
312                 }
313                 else {
314                         do_cmd("cls");
315                         if (menu_items[sel]->handler(menu_items[sel]))
316                                 break;
317                         do_cmd("cls");
318                 }
319         }
320
321         return 0;
322 }
323
324 U_BOOT_CMD(
325         pmenu, 1, 1, boot_menu,
326         "show pandora's boot menu",
327         ""
328 );
329
330 /* helpers */
331 static int do_ssource(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
332 {
333         ulong addr;
334
335         if (argc < 2)
336                 return 1;
337
338         addr = simple_strtoul(argv[1], NULL, 16);
339
340         printf("## Executing plain script at %08lx\n", addr);
341         return parse_string_outer((char *)addr, FLAG_PARSE_SEMICOLON);
342 }
343
344 U_BOOT_CMD(
345         ssource, 2, 0, do_ssource,
346         "run script from memory (no header)",
347         "<addr>"
348 );
349
350 static int do_usbinit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
351 {
352         extern int drv_usbtty_init(void);
353         static int usbinit_done;
354         if (!usbinit_done) {
355                 usbinit_done = 1;
356                 return !drv_usbtty_init();
357         }
358         return 0;
359 }
360
361 U_BOOT_CMD(
362         usbinit, 1, 0, do_usbinit,
363         "initialize USB",
364         ""
365 );
366
367 static int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
368 {
369         menu_do_poweroff(NULL);
370         return 0;
371 }
372
373 U_BOOT_CMD(
374         poweroff, 1, 0, do_poweroff,
375         "power off this device",
376         ""
377 );