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 000000000..68bc5450a --- /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); +} |