ULINK driver: Implement variable TCK frequency in OpenULINK firmware
[fw/openocd] / src / jtag / drivers / OpenULINK / src / protocol.c
1 /***************************************************************************
2  *   Copyright (C) 2011 by Martin Schmoelzer                               *
3  *   <martin.schmoelzer@student.tuwien.ac.at>                              *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include "protocol.h"
22 #include "jtag.h"
23 #include "delay.h"
24 #include "usb.h"
25 #include "io.h"
26 #include "msgtypes.h"
27
28 #include "reg_ezusb.h"
29
30 /**
31  * @file Implementation of the OpenULINK communication protocol.
32  *
33  * The OpenULINK protocol uses one OUT and one IN endpoint. These two endpoints
34  * are configured to use the maximum packet size for full-speed transfers,
35  * 64 bytes. Commands always start with a command ID (see msgtypes.h for
36  * command ID definitions) and contain zero or more payload data bytes in both
37  * transfer directions (IN and OUT). The payload 
38  *
39  * Almost all commands contain a fixed number of payload data bytes. The number
40  * of payload data bytes for the IN and OUT direction does not need to be the
41  * same.
42  * 
43  * Multiple commands may be sent in one EP2 Bulk-OUT packet. Because the
44  * OpenULINK firmware does not perform bounds checking for EP2 Bulk-IN packets,
45  * the host MUST ensure that the commands sent in the OUT packet require a
46  * maximum of 64 bytes of IN data.
47  */
48
49 /** Index in EP2 Bulk-OUT data buffer that contains the current command ID */
50 volatile u8 cmd_id_index;
51
52 /** Number of data bytes already in EP2 Bulk-IN buffer */
53 volatile u8 payload_index_in;
54
55 /**
56  * Execute a SET_LEDS command.
57  */
58 void execute_set_led_command(void)
59 {
60   u8 led_state = OUT2BUF[cmd_id_index + 1];
61
62   if (led_state & RUN_LED_ON) {
63     SET_RUN_LED();
64   }
65
66   if (led_state & COM_LED_ON) {
67     SET_COM_LED();
68   }
69
70   if (led_state & RUN_LED_OFF) {
71     CLEAR_RUN_LED();
72   }
73
74   if (led_state & COM_LED_OFF) {
75     CLEAR_COM_LED();
76   }
77 }
78
79 /**
80  * Executes one command and updates global command indexes.
81  *
82  * @param index the index of the Bulk EP2-OUT data buffer at which the
83  *  command ID is stored.
84  * @return true if this command was the last command.
85  * @return false if there are more commands within the current contents of the
86  *  Bulk EP2-OUT data buffer.
87  */
88 bool execute_command(void)
89 {
90   u8 usb_out_bytecount, usb_in_bytecount;
91   u16 signal_state;
92   u16 count;
93
94   /* Most commands do not transfer IN data. To save code space, we write 0 to
95    * usb_in_bytecount here, then modify it in the switch statement below where
96    * neccessary */
97   usb_in_bytecount = 0;
98
99   switch (OUT2BUF[cmd_id_index] /* Command ID */) {
100   case CMD_SCAN_IN:
101     usb_out_bytecount = 5;
102     usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
103     jtag_scan_in(cmd_id_index + 1, payload_index_in);
104     break;
105   case CMD_SCAN_OUT:
106     usb_out_bytecount = OUT2BUF[cmd_id_index + 1] + 5;
107     jtag_scan_out(cmd_id_index + 1);
108     break;
109   case CMD_SCAN_IO:
110     usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
111     usb_out_bytecount = usb_in_bytecount + 5;
112     jtag_scan_io(cmd_id_index + 1, payload_index_in);
113     break;
114   case CMD_CLOCK_TMS:
115     usb_out_bytecount = 2;
116     jtag_clock_tms(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
117     break;
118   case CMD_CLOCK_TCK:
119     usb_out_bytecount = 2;
120     count = (u16)OUT2BUF[cmd_id_index + 1];
121     count |= ((u16)OUT2BUF[cmd_id_index + 2]) << 8;
122     jtag_clock_tck(count);
123     break;
124   case CMD_SLOW_SCAN_IN:
125     usb_out_bytecount = 5;
126     usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
127     jtag_slow_scan_in(cmd_id_index + 1, payload_index_in);
128     break;
129   case CMD_SLOW_SCAN_OUT:
130     usb_out_bytecount = OUT2BUF[cmd_id_index + 1] + 5;
131     jtag_slow_scan_out(cmd_id_index + 1);
132     break;
133   case CMD_SLOW_SCAN_IO:
134     usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
135     usb_out_bytecount = usb_in_bytecount + 5;
136     jtag_slow_scan_io(cmd_id_index + 1, payload_index_in);
137     break;
138   case CMD_SLOW_CLOCK_TMS:
139     usb_out_bytecount = 2;
140     jtag_slow_clock_tms(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
141     break;
142   case CMD_SLOW_CLOCK_TCK:
143     usb_out_bytecount = 2;
144     count = (u16)OUT2BUF[cmd_id_index + 1];
145     count |= ((u16)OUT2BUF[cmd_id_index + 2]) << 8;
146     jtag_slow_clock_tck(count);
147     break;
148   case CMD_SLEEP_US:
149     usb_out_bytecount = 2;
150     count = (u16)OUT2BUF[cmd_id_index + 1];
151     count |= ((u16)OUT2BUF[cmd_id_index + 2]) << 8;
152     delay_us(count);
153     break;
154   case CMD_SLEEP_MS:
155     usb_out_bytecount = 2;
156     count = (u16)OUT2BUF[cmd_id_index + 1];
157     count |= ((u16)OUT2BUF[cmd_id_index + 2]) << 8;
158     delay_ms(count);
159     break;
160   case CMD_GET_SIGNALS:
161     usb_out_bytecount = 0;
162     usb_in_bytecount = 2;
163     signal_state = jtag_get_signals();
164     IN2BUF[payload_index_in] = (signal_state >> 8) & 0x00FF;
165     IN2BUF[payload_index_in + 1] = signal_state & 0x00FF;
166     break;
167   case CMD_SET_SIGNALS:
168     usb_out_bytecount = 2;
169     jtag_set_signals(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
170     break;
171   case CMD_CONFIGURE_TCK_FREQ:
172     usb_out_bytecount = 5;
173     jtag_configure_tck_delay(
174         OUT2BUF[cmd_id_index + 1],  /* scan_in */
175         OUT2BUF[cmd_id_index + 2],  /* scan_out */
176         OUT2BUF[cmd_id_index + 3],  /* scan_io */
177         OUT2BUF[cmd_id_index + 4],  /* clock_tck */
178         OUT2BUF[cmd_id_index + 5]); /* clock_tms */
179     break;
180   case CMD_SET_LEDS:
181     usb_out_bytecount = 1;
182     execute_set_led_command();
183     break;
184   case CMD_TEST:
185     usb_out_bytecount = 1;
186     /* Do nothing... This command is only used to test if the device is ready
187      * to accept new commands */
188     break;
189   default:
190     /* Should never be reached */
191     usb_out_bytecount = 0;
192     break;
193   }
194
195   /* Update EP2 Bulk-IN data byte count */
196   payload_index_in += usb_in_bytecount;
197
198   /* Determine if this was the last command */
199   if ((cmd_id_index + usb_out_bytecount + 1) >= OUT2BC) {
200     return true;
201   }
202   else {
203     /* Not the last command, update cmd_id_index */
204     cmd_id_index += (usb_out_bytecount + 1);
205     return false;
206   }
207 }
208
209 /**
210  * Forever wait for commands and execute them as they arrive.
211  */
212 void command_loop(void)
213 {
214   bool last_command;
215
216   while (1) {
217     cmd_id_index = 0;
218     payload_index_in = 0;
219
220     /* Wait until host sends EP2 Bulk-OUT packet */
221     while (!EP2_out);
222     EP2_out = 0;
223
224     /* Turn on COM LED to indicate command execution */
225     SET_COM_LED();
226
227     /* Execute the commands */
228     last_command = false;
229     while (last_command == false) {
230       last_command = execute_command();
231     }
232
233     CLEAR_COM_LED();
234     
235     /* Send back EP2 Bulk-IN packet if required */
236     if (payload_index_in > 0) {
237       IN2BC = payload_index_in;
238       while (!EP2_in);
239       EP2_in = 0;
240     }
241
242     /* Re-arm EP2-OUT after command execution */
243     OUT2BC = 0;
244   }
245 }