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