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