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