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