|  | /* | 
|  | * 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); | 
|  | } |