|
|
|
/*
|
|
|
|
* avr.c
|
|
|
|
*
|
|
|
|
* AVR functions
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 Mihai Georgian <u-boot@linuxnotincluded.org.uk>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
|
|
*/
|
|
|
|
#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);
|
|
|
|
}
|