diff options
Diffstat (limited to 'board/linkstation/avr.c')
-rw-r--r-- | board/linkstation/avr.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/board/linkstation/avr.c b/board/linkstation/avr.c new file mode 100644 index 0000000..68bc545 --- /dev/null +++ b/board/linkstation/avr.c @@ -0,0 +1,293 @@ +/* + * avr.c + * + * AVR functions + * + * Copyright (C) 2006 Mihai Georgian <u-boot@linuxnotincluded.org.uk> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <ns16550.h> +#include <console.h> + +/* Button codes from the AVR */ +#define PWRR 0x20 /* Power button release */ +#define PWRP 0x21 /* Power button push */ +#define RESR 0x22 /* Reset button release */ +#define RESP 0x23 /* Reset button push */ +#define AVRINIT 0x33 /* Init complete */ +#define AVRRESET 0x31 /* Reset request */ + +/* LED commands */ +#define PWRBLINKSTRT '[' /* Blink power LED */ +#define PWRBLINKSTOP 'Z' /* Solid power LED */ +#define HDDLEDON 'W' /* HDD LED on */ +#define HDDLEDOFF 'V' /* HDD LED off */ +#define HDDBLINKSTRT 'Y' /* HDD LED start blink */ +#define HDDBLINKSTOP 'X' /* HDD LED stop blink */ + +/* Timings for LEDs blinking to show choice */ +#define PULSETIME 250 /* msecs */ +#define LONGPAUSE (5 * PULSETIME) + +/* Button press times */ +#define PUSHHOLD 1000 /* msecs */ +#define NOBUTTON (6 * (LONGPAUSE+PULSETIME)) + +/* Boot and console choices */ +#define MAX_BOOT_CHOICE 3 + +static char *consoles[] = { + "serial", +#if defined(CONFIG_NETCONSOLE) + "nc", +#endif +}; +#define MAX_CONS_CHOICE (sizeof(consoles)/sizeof(char *)) + +#if !defined(CONFIG_NETCONSOLE) +#define DEF_CONS_CHOICE 0 +#else +#define DEF_CONS_CHOICE 1 +#endif + +#define perror(fmt, args...) printf("%s: " fmt, __FUNCTION__ , ##args) + +extern void miconCntl_SendCmd(unsigned char dat); +extern void miconCntl_DisWDT(void); + +static int boot_stop; + +static int boot_choice = 1; +static int cons_choice = DEF_CONS_CHOICE; + +static char envbuffer[16]; + +void init_AVR_DUART (void) +{ + NS16550_t AVR_port = (NS16550_t) CFG_NS16550_COM2; + int clock_divisor = CFG_NS16550_CLK / 16 / 9600; + + /* + * AVR port init sequence taken from + * the original Linkstation init code + * Normal U-Boot serial reinit doesn't + * work because the AVR uses even parity + */ + AVR_port->lcr = 0x00; + AVR_port->ier = 0x00; + AVR_port->lcr = LCR_BKSE; + AVR_port->dll = clock_divisor & 0xff; + AVR_port->dlm = (clock_divisor >> 8) & 0xff; + AVR_port->lcr = LCR_WLS_8 | LCR_PEN | LCR_EPS; + AVR_port->mcr = 0x00; + AVR_port->fcr = FCR_FIFO_EN | FCR_RXSR | FCR_TXSR; + + miconCntl_DisWDT(); + + boot_stop = 0; + miconCntl_SendCmd(PWRBLINKSTRT); +} + +static inline int avr_tstc(void) +{ + return (NS16550_tstc((NS16550_t)CFG_NS16550_COM2)); +} + +static inline char avr_getc(void) +{ + return (NS16550_getc((NS16550_t)CFG_NS16550_COM2)); +} + +static int push_timeout(char button_code) +{ + ulong push_start = get_timer(0); + while (get_timer(push_start) <= PUSHHOLD) + if (avr_tstc() && avr_getc() == button_code) + return 0; + return 1; +} + +static void next_boot_choice(void) +{ + ulong return_start; + ulong pulse_start; + int on_times; + int button_on; + int led_state; + char c; + + button_on = 0; + return_start = get_timer(0); + + on_times = boot_choice; + led_state = 0; + miconCntl_SendCmd(HDDLEDOFF); + pulse_start = get_timer(0); + + while (get_timer(return_start) <= NOBUTTON || button_on) { + if (avr_tstc()) { + c = avr_getc(); + if (c == PWRP) + button_on = 1; + else if (c == PWRR) { + button_on = 0; + return_start = get_timer(0); + if (++boot_choice > MAX_BOOT_CHOICE) + boot_choice = 1; + sprintf(envbuffer, "bootcmd%d", boot_choice); + if (getenv(envbuffer)) { + sprintf(envbuffer, "run bootcmd%d", boot_choice); + setenv("bootcmd", envbuffer); + } + on_times = boot_choice; + led_state = 1; + miconCntl_SendCmd(HDDLEDON); + pulse_start = get_timer(0); + } else { + perror("Unexpected code: 0x%02X\n", c); + } + } + if (on_times && get_timer(pulse_start) > PULSETIME) { + if (led_state == 1) { + --on_times; + led_state = 0; + miconCntl_SendCmd(HDDLEDOFF); + } else { + led_state = 1; + miconCntl_SendCmd(HDDLEDON); + } + pulse_start = get_timer(0); + } + if (!on_times && get_timer(pulse_start) > LONGPAUSE) { + on_times = boot_choice; + led_state = 1; + miconCntl_SendCmd(HDDLEDON); + pulse_start = get_timer(0); + } + } + if (led_state) + miconCntl_SendCmd(HDDLEDOFF); +} + +void next_cons_choice(int console) +{ + ulong return_start; + ulong pulse_start; + int on_times; + int button_on; + int led_state; + char c; + + button_on = 0; + cons_choice = console; + return_start = get_timer(0); + + on_times = cons_choice+1; + led_state = 1; + miconCntl_SendCmd(HDDLEDON); + pulse_start = get_timer(0); + + while (get_timer(return_start) <= NOBUTTON || button_on) { + if (avr_tstc()) { + c = avr_getc(); + if (c == RESP) + button_on = 1; + else if (c == RESR) { + button_on = 0; + return_start = get_timer(0); + cons_choice = (cons_choice + 1) % MAX_CONS_CHOICE; + console_assign(stdin, consoles[cons_choice]); + console_assign(stdout, consoles[cons_choice]); + console_assign(stderr, consoles[cons_choice]); + on_times = cons_choice+1; + led_state = 0; + miconCntl_SendCmd(HDDLEDOFF); + pulse_start = get_timer(0); + } else { + perror("Unexpected code: 0x%02X\n", c); + } + } + if (on_times && get_timer(pulse_start) > PULSETIME) { + if (led_state == 0) { + --on_times; + led_state = 1; + miconCntl_SendCmd(HDDLEDON); + } else { + led_state = 0; + miconCntl_SendCmd(HDDLEDOFF); + } + pulse_start = get_timer(0); + } + if (!on_times && get_timer(pulse_start) > LONGPAUSE) { + on_times = cons_choice+1; + led_state = 0; + miconCntl_SendCmd(HDDLEDOFF); + pulse_start = get_timer(0); + } + } + if (led_state); + miconCntl_SendCmd(HDDLEDOFF); +} + +int avr_input(void) +{ + char avr_button; + + if (!avr_tstc()) + return 0; + + avr_button = avr_getc(); + switch (avr_button) { + case PWRP: + if (push_timeout(PWRR)) { + /* Timeout before power button release */ + boot_stop = ~boot_stop; + if (boot_stop) + miconCntl_SendCmd(PWRBLINKSTOP); + else + miconCntl_SendCmd(PWRBLINKSTRT); + /* Wait for power button release */ + while (avr_getc() != PWRR) + ; + } else + /* Power button released */ + next_boot_choice(); + break; + case RESP: + /* Wait for Reset button release */ + while (avr_getc() != RESR) + ; + next_cons_choice(cons_choice); + break; + case AVRINIT: + return 0; + default: + perror("Unexpected code: 0x%02X\n", avr_button); + return 0; + } + if (boot_stop) + return (-3); + else + return (-2); +} + +void avr_StopBoot(void) +{ + boot_stop = ~0; + miconCntl_SendCmd(PWRBLINKSTOP); +} |