blob: 62e586ba09fe99d11f39ecc31476e6192d8d3a4f [file] [log] [blame]
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +02001/*
2 * avr.c
3 *
4 * AVR functions
5 *
6 * Copyright (C) 2006 Mihai Georgian <u-boot@linuxnotincluded.org.uk>
7 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02008 * SPDX-License-Identifier: GPL-2.0+
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +02009 */
10#include <common.h>
11#include <ns16550.h>
Jean-Christophe PLAGNIOL-VILLARD2a7a0312009-05-16 12:14:54 +020012#include <stdio_dev.h>
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +020013
14/* Button codes from the AVR */
15#define PWRR 0x20 /* Power button release */
16#define PWRP 0x21 /* Power button push */
17#define RESR 0x22 /* Reset button release */
18#define RESP 0x23 /* Reset button push */
19#define AVRINIT 0x33 /* Init complete */
20#define AVRRESET 0x31 /* Reset request */
21
22/* LED commands */
23#define PWRBLINKSTRT '[' /* Blink power LED */
24#define PWRBLINKSTOP 'Z' /* Solid power LED */
Wolfgang Denka1be4762008-05-20 16:00:29 +020025#define HDDLEDON 'W' /* HDD LED on */
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +020026#define HDDLEDOFF 'V' /* HDD LED off */
27#define HDDBLINKSTRT 'Y' /* HDD LED start blink */
28#define HDDBLINKSTOP 'X' /* HDD LED stop blink */
29
30/* Timings for LEDs blinking to show choice */
31#define PULSETIME 250 /* msecs */
32#define LONGPAUSE (5 * PULSETIME)
33
34/* Button press times */
35#define PUSHHOLD 1000 /* msecs */
36#define NOBUTTON (6 * (LONGPAUSE+PULSETIME))
37
38/* Boot and console choices */
39#define MAX_BOOT_CHOICE 3
40
41static char *consoles[] = {
42 "serial",
43#if defined(CONFIG_NETCONSOLE)
44 "nc",
45#endif
46};
Wolfgang Denka1be4762008-05-20 16:00:29 +020047#define MAX_CONS_CHOICE (sizeof(consoles)/sizeof(char *))
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +020048
49#if !defined(CONFIG_NETCONSOLE)
50#define DEF_CONS_CHOICE 0
51#else
52#define DEF_CONS_CHOICE 1
53#endif
54
55#define perror(fmt, args...) printf("%s: " fmt, __FUNCTION__ , ##args)
56
57extern void miconCntl_SendCmd(unsigned char dat);
58extern void miconCntl_DisWDT(void);
59
60static int boot_stop;
61
62static int boot_choice = 1;
63static int cons_choice = DEF_CONS_CHOICE;
64
65static char envbuffer[16];
66
67void init_AVR_DUART (void)
68{
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020069 NS16550_t AVR_port = (NS16550_t) CONFIG_SYS_NS16550_COM2;
70 int clock_divisor = CONFIG_SYS_NS16550_CLK / 16 / 9600;
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +020071
72 /*
73 * AVR port init sequence taken from
74 * the original Linkstation init code
75 * Normal U-Boot serial reinit doesn't
76 * work because the AVR uses even parity
77 */
78 AVR_port->lcr = 0x00;
79 AVR_port->ier = 0x00;
Detlev Zundel166fb542009-04-03 11:53:01 +020080 AVR_port->lcr = UART_LCR_BKSE;
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +020081 AVR_port->dll = clock_divisor & 0xff;
82 AVR_port->dlm = (clock_divisor >> 8) & 0xff;
Detlev Zundel166fb542009-04-03 11:53:01 +020083 AVR_port->lcr = UART_LCR_WLS_8 | UART_LCR_PEN | UART_LCR_EPS;
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +020084 AVR_port->mcr = 0x00;
Detlev Zundel166fb542009-04-03 11:53:01 +020085 AVR_port->fcr = UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR;
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +020086
87 miconCntl_DisWDT();
88
89 boot_stop = 0;
90 miconCntl_SendCmd(PWRBLINKSTRT);
91}
92
93static inline int avr_tstc(void)
94{
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020095 return (NS16550_tstc((NS16550_t)CONFIG_SYS_NS16550_COM2));
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +020096}
97
98static inline char avr_getc(void)
99{
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200100 return (NS16550_getc((NS16550_t)CONFIG_SYS_NS16550_COM2));
Guennadi Liakhovetski40c26b22008-03-31 01:32:15 +0200101}
102
103static int push_timeout(char button_code)
104{
105 ulong push_start = get_timer(0);
106 while (get_timer(push_start) <= PUSHHOLD)
107 if (avr_tstc() && avr_getc() == button_code)
108 return 0;
109 return 1;
110}
111
112static void next_boot_choice(void)
113{
114 ulong return_start;
115 ulong pulse_start;
116 int on_times;
117 int button_on;
118 int led_state;
119 char c;
120
121 button_on = 0;
122 return_start = get_timer(0);
123
124 on_times = boot_choice;
125 led_state = 0;
126 miconCntl_SendCmd(HDDLEDOFF);
127 pulse_start = get_timer(0);
128
129 while (get_timer(return_start) <= NOBUTTON || button_on) {
130 if (avr_tstc()) {
131 c = avr_getc();
132 if (c == PWRP)
133 button_on = 1;
134 else if (c == PWRR) {
135 button_on = 0;
136 return_start = get_timer(0);
137 if (++boot_choice > MAX_BOOT_CHOICE)
138 boot_choice = 1;
139 sprintf(envbuffer, "bootcmd%d", boot_choice);
140 if (getenv(envbuffer)) {
141 sprintf(envbuffer, "run bootcmd%d", boot_choice);
142 setenv("bootcmd", envbuffer);
143 }
144 on_times = boot_choice;
145 led_state = 1;
146 miconCntl_SendCmd(HDDLEDON);
147 pulse_start = get_timer(0);
148 } else {
149 perror("Unexpected code: 0x%02X\n", c);
150 }
151 }
152 if (on_times && get_timer(pulse_start) > PULSETIME) {
153 if (led_state == 1) {
154 --on_times;
155 led_state = 0;
156 miconCntl_SendCmd(HDDLEDOFF);
157 } else {
158 led_state = 1;
159 miconCntl_SendCmd(HDDLEDON);
160 }
161 pulse_start = get_timer(0);
162 }
163 if (!on_times && get_timer(pulse_start) > LONGPAUSE) {
164 on_times = boot_choice;
165 led_state = 1;
166 miconCntl_SendCmd(HDDLEDON);
167 pulse_start = get_timer(0);
168 }
169 }
170 if (led_state)
171 miconCntl_SendCmd(HDDLEDOFF);
172}
173
174void next_cons_choice(int console)
175{
176 ulong return_start;
177 ulong pulse_start;
178 int on_times;
179 int button_on;
180 int led_state;
181 char c;
182
183 button_on = 0;
184 cons_choice = console;
185 return_start = get_timer(0);
186
187 on_times = cons_choice+1;
188 led_state = 1;
189 miconCntl_SendCmd(HDDLEDON);
190 pulse_start = get_timer(0);
191
192 while (get_timer(return_start) <= NOBUTTON || button_on) {
193 if (avr_tstc()) {
194 c = avr_getc();
195 if (c == RESP)
196 button_on = 1;
197 else if (c == RESR) {
198 button_on = 0;
199 return_start = get_timer(0);
200 cons_choice = (cons_choice + 1) % MAX_CONS_CHOICE;
201 console_assign(stdin, consoles[cons_choice]);
202 console_assign(stdout, consoles[cons_choice]);
203 console_assign(stderr, consoles[cons_choice]);
204 on_times = cons_choice+1;
205 led_state = 0;
206 miconCntl_SendCmd(HDDLEDOFF);
207 pulse_start = get_timer(0);
208 } else {
209 perror("Unexpected code: 0x%02X\n", c);
210 }
211 }
212 if (on_times && get_timer(pulse_start) > PULSETIME) {
213 if (led_state == 0) {
214 --on_times;
215 led_state = 1;
216 miconCntl_SendCmd(HDDLEDON);
217 } else {
218 led_state = 0;
219 miconCntl_SendCmd(HDDLEDOFF);
220 }
221 pulse_start = get_timer(0);
222 }
223 if (!on_times && get_timer(pulse_start) > LONGPAUSE) {
224 on_times = cons_choice+1;
225 led_state = 0;
226 miconCntl_SendCmd(HDDLEDOFF);
227 pulse_start = get_timer(0);
228 }
229 }
230 if (led_state);
231 miconCntl_SendCmd(HDDLEDOFF);
232}
233
234int avr_input(void)
235{
236 char avr_button;
237
238 if (!avr_tstc())
239 return 0;
240
241 avr_button = avr_getc();
242 switch (avr_button) {
243 case PWRP:
244 if (push_timeout(PWRR)) {
245 /* Timeout before power button release */
246 boot_stop = ~boot_stop;
247 if (boot_stop)
248 miconCntl_SendCmd(PWRBLINKSTOP);
249 else
250 miconCntl_SendCmd(PWRBLINKSTRT);
251 /* Wait for power button release */
252 while (avr_getc() != PWRR)
253 ;
254 } else
255 /* Power button released */
256 next_boot_choice();
257 break;
258 case RESP:
259 /* Wait for Reset button release */
260 while (avr_getc() != RESR)
261 ;
262 next_cons_choice(cons_choice);
263 break;
264 case AVRINIT:
265 return 0;
266 default:
267 perror("Unexpected code: 0x%02X\n", avr_button);
268 return 0;
269 }
270 if (boot_stop)
271 return (-3);
272 else
273 return (-2);
274}
275
276void avr_StopBoot(void)
277{
278 boot_stop = ~0;
279 miconCntl_SendCmd(PWRBLINKSTOP);
280}