Merge branch 'master' of git://www.denx.de/git/u-boot
[platform/kernel/u-boot.git] / board / linkstation / avr.c
1 /*
2  * avr.c
3  *
4  * AVR functions
5  *
6  * Copyright (C) 2006 Mihai Georgian <u-boot@linuxnotincluded.org.uk>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23 #include <common.h>
24 #include <ns16550.h>
25 #include <console.h>
26
27 /* Button codes from the AVR */
28 #define PWRR                    0x20            /* Power button release */
29 #define PWRP                    0x21            /* Power button push    */
30 #define RESR                    0x22            /* Reset button release */
31 #define RESP                    0x23            /* Reset button push    */
32 #define AVRINIT                 0x33            /* Init complete        */
33 #define AVRRESET                0x31            /* Reset request        */
34
35 /* LED commands */
36 #define PWRBLINKSTRT            '['             /* Blink power LED      */
37 #define PWRBLINKSTOP            'Z'             /* Solid power LED      */
38 #define HDDLEDON                'W'             /* HDD LED on           */
39 #define HDDLEDOFF               'V'             /* HDD LED off          */
40 #define HDDBLINKSTRT            'Y'             /* HDD LED start blink  */
41 #define HDDBLINKSTOP            'X'             /* HDD LED stop blink   */
42
43 /* Timings for LEDs blinking to show choice */
44 #define PULSETIME               250             /* msecs                */
45 #define LONGPAUSE               (5 * PULSETIME)
46
47 /* Button press times */
48 #define PUSHHOLD                1000            /* msecs                */
49 #define NOBUTTON                (6 * (LONGPAUSE+PULSETIME))
50
51 /* Boot and console choices */
52 #define MAX_BOOT_CHOICE         3
53
54 static char *consoles[] = {
55         "serial",
56 #if defined(CONFIG_NETCONSOLE)
57         "nc",
58 #endif
59 };
60 #define MAX_CONS_CHOICE         (sizeof(consoles)/sizeof(char *))
61
62 #if !defined(CONFIG_NETCONSOLE)
63 #define DEF_CONS_CHOICE         0
64 #else
65 #define DEF_CONS_CHOICE         1
66 #endif
67
68 #define perror(fmt, args...) printf("%s: " fmt, __FUNCTION__ , ##args)
69
70 extern void miconCntl_SendCmd(unsigned char dat);
71 extern void miconCntl_DisWDT(void);
72
73 static int boot_stop;
74
75 static int boot_choice = 1;
76 static int cons_choice = DEF_CONS_CHOICE;
77
78 static char envbuffer[16];
79
80 void init_AVR_DUART (void)
81 {
82         NS16550_t AVR_port = (NS16550_t) CFG_NS16550_COM2;
83         int clock_divisor = CFG_NS16550_CLK / 16 / 9600;
84
85         /*
86          * AVR port init sequence taken from
87          * the original Linkstation init code
88          * Normal U-Boot serial reinit doesn't
89          * work because the AVR uses even parity
90          */
91         AVR_port->lcr = 0x00;
92         AVR_port->ier = 0x00;
93         AVR_port->lcr = LCR_BKSE;
94         AVR_port->dll = clock_divisor & 0xff;
95         AVR_port->dlm = (clock_divisor >> 8) & 0xff;
96         AVR_port->lcr = LCR_WLS_8 | LCR_PEN | LCR_EPS;
97         AVR_port->mcr = 0x00;
98         AVR_port->fcr = FCR_FIFO_EN | FCR_RXSR | FCR_TXSR;
99
100         miconCntl_DisWDT();
101
102         boot_stop = 0;
103         miconCntl_SendCmd(PWRBLINKSTRT);
104 }
105
106 static inline int avr_tstc(void)
107 {
108         return (NS16550_tstc((NS16550_t)CFG_NS16550_COM2));
109 }
110
111 static inline char avr_getc(void)
112 {
113         return (NS16550_getc((NS16550_t)CFG_NS16550_COM2));
114 }
115
116 static int push_timeout(char button_code)
117 {
118         ulong push_start = get_timer(0);
119         while (get_timer(push_start) <= PUSHHOLD)
120                 if (avr_tstc() && avr_getc() == button_code)
121                         return 0;
122         return 1;
123 }
124
125 static void next_boot_choice(void)
126 {
127         ulong return_start;
128         ulong pulse_start;
129         int on_times;
130         int button_on;
131         int led_state;
132         char c;
133
134         button_on = 0;
135         return_start = get_timer(0);
136
137         on_times = boot_choice;
138         led_state = 0;
139         miconCntl_SendCmd(HDDLEDOFF);
140         pulse_start = get_timer(0);
141
142         while (get_timer(return_start) <= NOBUTTON || button_on) {
143                 if (avr_tstc()) {
144                         c = avr_getc();
145                         if (c == PWRP)
146                                 button_on = 1;
147                         else if (c == PWRR) {
148                                 button_on = 0;
149                                 return_start = get_timer(0);
150                                 if (++boot_choice > MAX_BOOT_CHOICE)
151                                         boot_choice = 1;
152                                 sprintf(envbuffer, "bootcmd%d", boot_choice);
153                                 if (getenv(envbuffer)) {
154                                         sprintf(envbuffer, "run bootcmd%d", boot_choice);
155                                         setenv("bootcmd", envbuffer);
156                                 }
157                                 on_times = boot_choice;
158                                 led_state = 1;
159                                 miconCntl_SendCmd(HDDLEDON);
160                                 pulse_start = get_timer(0);
161                         } else {
162                                 perror("Unexpected code: 0x%02X\n", c);
163                         }
164                 }
165                 if (on_times && get_timer(pulse_start) > PULSETIME) {
166                         if (led_state == 1) {
167                                 --on_times;
168                                 led_state = 0;
169                                 miconCntl_SendCmd(HDDLEDOFF);
170                         } else {
171                                 led_state = 1;
172                                 miconCntl_SendCmd(HDDLEDON);
173                         }
174                         pulse_start = get_timer(0);
175                 }
176                 if (!on_times && get_timer(pulse_start) > LONGPAUSE) {
177                         on_times = boot_choice;
178                         led_state = 1;
179                         miconCntl_SendCmd(HDDLEDON);
180                         pulse_start = get_timer(0);
181                 }
182         }
183         if (led_state)
184                 miconCntl_SendCmd(HDDLEDOFF);
185 }
186
187 void next_cons_choice(int console)
188 {
189         ulong return_start;
190         ulong pulse_start;
191         int on_times;
192         int button_on;
193         int led_state;
194         char c;
195
196         button_on = 0;
197         cons_choice = console;
198         return_start = get_timer(0);
199
200         on_times = cons_choice+1;
201         led_state = 1;
202         miconCntl_SendCmd(HDDLEDON);
203         pulse_start = get_timer(0);
204
205         while (get_timer(return_start) <= NOBUTTON || button_on) {
206                 if (avr_tstc()) {
207                         c = avr_getc();
208                         if (c == RESP)
209                                 button_on = 1;
210                         else if (c == RESR) {
211                                 button_on = 0;
212                                 return_start = get_timer(0);
213                                 cons_choice = (cons_choice + 1) % MAX_CONS_CHOICE;
214                                 console_assign(stdin, consoles[cons_choice]);
215                                 console_assign(stdout, consoles[cons_choice]);
216                                 console_assign(stderr, consoles[cons_choice]);
217                                 on_times = cons_choice+1;
218                                 led_state = 0;
219                                 miconCntl_SendCmd(HDDLEDOFF);
220                                 pulse_start = get_timer(0);
221                         } else {
222                                 perror("Unexpected code: 0x%02X\n", c);
223                         }
224                 }
225                 if (on_times && get_timer(pulse_start) > PULSETIME) {
226                         if (led_state == 0) {
227                                 --on_times;
228                                 led_state = 1;
229                                 miconCntl_SendCmd(HDDLEDON);
230                         } else {
231                                 led_state = 0;
232                                 miconCntl_SendCmd(HDDLEDOFF);
233                         }
234                         pulse_start = get_timer(0);
235                 }
236                 if (!on_times && get_timer(pulse_start) > LONGPAUSE) {
237                         on_times = cons_choice+1;
238                         led_state = 0;
239                         miconCntl_SendCmd(HDDLEDOFF);
240                         pulse_start = get_timer(0);
241                 }
242         }
243         if (led_state);
244         miconCntl_SendCmd(HDDLEDOFF);
245 }
246
247 int avr_input(void)
248 {
249         char avr_button;
250
251         if (!avr_tstc())
252                 return 0;
253
254         avr_button = avr_getc();
255         switch (avr_button) {
256         case PWRP:
257                 if (push_timeout(PWRR)) {
258                         /* Timeout before power button release */
259                         boot_stop = ~boot_stop;
260                         if (boot_stop)
261                                 miconCntl_SendCmd(PWRBLINKSTOP);
262                         else
263                                 miconCntl_SendCmd(PWRBLINKSTRT);
264                         /* Wait for power button release */
265                         while (avr_getc() != PWRR)
266                                 ;
267                 } else
268                         /* Power button released */
269                         next_boot_choice();
270                 break;
271         case RESP:
272                 /* Wait for Reset button release */
273                 while (avr_getc() != RESR)
274                         ;
275                 next_cons_choice(cons_choice);
276                 break;
277         case AVRINIT:
278                 return 0;
279         default:
280                 perror("Unexpected code: 0x%02X\n", avr_button);
281                 return 0;
282         }
283         if (boot_stop)
284                 return (-3);
285         else
286                 return (-2);
287 }
288
289 void avr_StopBoot(void)
290 {
291         boot_stop = ~0;
292         miconCntl_SendCmd(PWRBLINKSTOP);
293 }