summaryrefslogtreecommitdiffstats
path: root/board/linkstation/avr.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/linkstation/avr.c')
-rw-r--r--board/linkstation/avr.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/board/linkstation/avr.c b/board/linkstation/avr.c
new file mode 100644
index 0000000..68bc545
--- /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);
+}