| /* | |
| * 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 <stdio_dev.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) CONFIG_SYS_NS16550_COM2; | |
| int clock_divisor = CONFIG_SYS_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 = UART_LCR_BKSE; | |
| AVR_port->dll = clock_divisor & 0xff; | |
| AVR_port->dlm = (clock_divisor >> 8) & 0xff; | |
| AVR_port->lcr = UART_LCR_WLS_8 | UART_LCR_PEN | UART_LCR_EPS; | |
| AVR_port->mcr = 0x00; | |
| AVR_port->fcr = UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR; | |
| miconCntl_DisWDT(); | |
| boot_stop = 0; | |
| miconCntl_SendCmd(PWRBLINKSTRT); | |
| } | |
| static inline int avr_tstc(void) | |
| { | |
| return (NS16550_tstc((NS16550_t)CONFIG_SYS_NS16550_COM2)); | |
| } | |
| static inline char avr_getc(void) | |
| { | |
| return (NS16550_getc((NS16550_t)CONFIG_SYS_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); | |
| } |