1 /* ----------------------------------------------------------------------------
2 * SAM Software Package License
3 * ----------------------------------------------------------------------------
4 * Copyright (c) 2011-2012, Atmel Corporation
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following condition is met:
10 *
11 * - Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the disclaimer below.
13 *
14 * Atmel's name may not be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
20 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 * ----------------------------------------------------------------------------
28 */
29
30 #include "chip.h"
31 #include <stdio.h>
32
33 #if SAM3XA_SERIES
34
35 //#define TRACE_UOTGHS_HOST(x) x
36 #define TRACE_UOTGHS_HOST(x)
37
38 extern void (*gpf_isr)(void);
39
40 // Handle UOTGHS Host driver state
41 static uhd_vbus_state_t uhd_state = UHD_STATE_NO_VBUS;
42
43 /**
44 * \brief Interrupt sub routine for USB Host state machine management.
45 */
UHD_ISR(void)46 static void UHD_ISR(void)
47 {
48 // Manage dis/connection event
49 if (Is_uhd_disconnection() && Is_uhd_disconnection_int_enabled()) {
50 TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : Disconnection INT\r\n");)
51 uhd_ack_disconnection();
52 uhd_disable_disconnection_int();
53 // Stop reset signal, in case of disconnection during reset
54 uhd_stop_reset();
55 // Disable wakeup/resumes interrupts,
56 // in case of disconnection during suspend mode
57 //UOTGHS->UOTGHS_HSTIDR = UOTGHS_HSTIDR_HWUPIEC
58 // | UOTGHS_HSTIDR_RSMEDIEC
59 // | UOTGHS_HSTIDR_RXRSMIEC;
60 uhd_ack_connection();
61 uhd_enable_connection_int();
62 uhd_state = UHD_STATE_DISCONNECTED;
63 return;
64 }
65 if (Is_uhd_connection() && Is_uhd_connection_int_enabled()) {
66 TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : Connection INT\r\n");)
67 uhd_ack_connection();
68 uhd_disable_connection_int();
69 uhd_ack_disconnection();
70 uhd_enable_disconnection_int();
71 //uhd_enable_sof();
72 uhd_state = UHD_STATE_CONNECTED;
73 return;
74 }
75
76 // Manage Vbus error
77 if (Is_uhd_vbus_error_interrupt())
78 {
79 TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS error INT\r\n");)
80 uhd_ack_vbus_error_interrupt();
81 uhd_state = UHD_STATE_DISCONNECTED; //UHD_STATE_ERROR;
82 return;
83 }
84
85 // Check USB clock ready after asynchronous interrupt
86 while (!Is_otg_clock_usable())
87 ;
88 otg_unfreeze_clock();
89
90 // Manage Vbus state change
91 if (Is_otg_vbus_transition())
92 {
93 otg_ack_vbus_transition();
94 if (Is_otg_vbus_high())
95 {
96 TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS transition INT : UHD_STATE_DISCONNECT\r\n");)
97 uhd_state = UHD_STATE_DISCONNECTED;
98 }
99 else
100 {
101 TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS transition INT : UHD_STATE_NO_VBUS\r\n");)
102 otg_freeze_clock();
103 uhd_state = UHD_STATE_NO_VBUS;
104 }
105 TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS transition INT : done.\r\n");)
106 return;
107 }
108
109 // Other errors
110 if (Is_uhd_errors_interrupt())
111 {
112 TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : Other error INT\r\n");)
113 uhd_ack_errors_interrupt();
114 return;
115 }
116 }
117
118 /**
119 * \brief Set the interrupt sub routines callback for USB interrupts.
120 *
121 * \param pf_isr the ISR address.
122 */
UHD_SetStack(void (* pf_isr)(void))123 void UHD_SetStack(void (*pf_isr)(void))
124 {
125 gpf_isr = pf_isr;
126 }
127
128 /**
129 * \brief Initialize the UOTGHS host driver.
130 */
UHD_Init(void)131 void UHD_Init(void)
132 {
133 irqflags_t flags;
134
135 // To avoid USB interrupt before end of initialization
136 flags = cpu_irq_save();
137
138 // Setup USB Host interrupt callback
139 UHD_SetStack(&UHD_ISR);
140
141 // Enables the USB Clock
142 pmc_enable_upll_clock();
143 pmc_switch_udpck_to_upllck(0); // div=0+1
144 pmc_enable_udpck();
145 pmc_enable_periph_clk(ID_UOTGHS);
146
147 // Always authorize asynchronous USB interrupts to exit of sleep mode
148 // For SAM3 USB wake up device except BACKUP mode
149 NVIC_SetPriority((IRQn_Type) ID_UOTGHS, 0);
150 NVIC_EnableIRQ((IRQn_Type) ID_UOTGHS);
151
152 // ID pin not used then force host mode
153 otg_disable_id_pin();
154 otg_force_host_mode();
155
156 // Signal is active low (because all SAM3X Pins are high after startup)
157 // Hence VBOF must be low after connection request to power up the remote device
158 // uhd_set_vbof_active_low();
159
160 // According to the Arduino Due circuit the VBOF must be active high to power up the remote device
161 uhd_set_vbof_active_high();
162
163 otg_enable_pad();
164 otg_enable();
165
166 otg_unfreeze_clock();
167
168 // Check USB clock
169 while (!Is_otg_clock_usable())
170 ;
171
172 // Clear all interrupts that may have been set by a previous host mode
173 UOTGHS->UOTGHS_HSTICR = UOTGHS_HSTICR_DCONNIC | UOTGHS_HSTICR_DDISCIC
174 | UOTGHS_HSTICR_HSOFIC | UOTGHS_HSTICR_HWUPIC
175 | UOTGHS_HSTICR_RSMEDIC | UOTGHS_HSTICR_RSTIC
176 | UOTGHS_HSTICR_RXRSMIC;
177
178 otg_ack_vbus_transition();
179
180 // Enable Vbus change and error interrupts
181 // Disable automatic Vbus control after Vbus error
182 Set_bits(UOTGHS->UOTGHS_CTRL,
183 UOTGHS_CTRL_VBUSHWC | UOTGHS_CTRL_VBUSTE | UOTGHS_CTRL_VBERRE);
184
185 uhd_enable_vbus();
186
187 // Force Vbus interrupt when Vbus is always high
188 // This is possible due to a short timing between a Host mode stop/start.
189 if (Is_otg_vbus_high())
190 {
191 otg_raise_vbus_transition();
192 }
193
194 // Enable main control interrupt
195 // Connection, SOF and reset
196 UOTGHS->UOTGHS_HSTIER = UOTGHS_HSTICR_DCONNIC;
197
198 otg_freeze_clock();
199
200 uhd_state = UHD_STATE_NO_VBUS;
201
202 cpu_irq_restore(flags);
203 }
204
205 /**
206 * \brief Trigger a USB bus reset.
207 */
UHD_BusReset(void)208 void UHD_BusReset(void)
209 {
210 uhd_start_reset();
211 }
212
213 /**
214 * \brief Get VBUS state.
215 *
216 * \return VBUS status.
217 */
UHD_GetVBUSState(void)218 uhd_vbus_state_t UHD_GetVBUSState(void)
219 {
220 return uhd_state;
221 }
222
223 /*uhd_speed_t uhd_get_speed(void)
224 {
225 switch (uhd_get_speed_mode())
226 {
227 case UOTGHS_SR_SPEED_HIGH_SPEED:
228 return UHD_SPEED_HIGH;
229
230 case UOTGHS_SR_SPEED_FULL_SPEED:
231 return UHD_SPEED_FULL;
232
233 case UOTGHS_SR_SPEED_LOW_SPEED:
234 return UHD_SPEED_LOW;
235
236 default:
237 return UHD_SPEED_LOW;
238 }
239 }*/
240
241 /**
242 * \brief Allocate FIFO for pipe 0.
243 *
244 * \param ul_add Address of remote device for pipe 0.
245 * \param ul_ep_size Actual size of the FIFO in bytes.
246 *
247 * \retval 0 success.
248 * \retval 1 error.
249 */
UHD_Pipe0_Alloc(uint32_t ul_add,uint32_t ul_ep_size)250 uint32_t UHD_Pipe0_Alloc(uint32_t ul_add, uint32_t ul_ep_size)
251 {
252 if (ul_ep_size < 8)
253 {
254 TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP0_Alloc : incorrect pipe size!\r\n");)
255 return 1;
256 }
257
258 if (Is_uhd_pipe_enabled(0))
259 {
260 // Pipe is already allocated
261 return 0;
262 }
263
264 uhd_enable_pipe(0);
265 uhd_configure_pipe(0, // Pipe 0
266 0, // No frequency
267 0, // Enpoint 0
268 UOTGHS_HSTPIPCFG_PTYPE_CTRL,
269 UOTGHS_HSTPIPCFG_PTOKEN_SETUP,
270 ul_ep_size,
271 UOTGHS_HSTPIPCFG_PBK_1_BANK, 0);
272
273 uhd_allocate_memory(0);
274
275 if (!Is_uhd_pipe_configured(0))
276 {
277 TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP0_Alloc : incorrect pipe settings!\r\n");)
278 uhd_disable_pipe(0);
279 return 1;
280 }
281
282 uhd_configure_address(0, ul_add);
283
284 return 0;
285 }
286
287 /**
288 * \brief Allocate a new pipe.
289 *
290 * \note UOTGHS maximum pipe number is limited to 10, meaning that only a limited
291 * amount of devices can be connected. Unfortunately, using only one pipe shared accross
292 * various endpoints and devices is not possible because the UOTGHS IP does not allow to
293 * change the data toggle value through register interface.
294 *
295 * \param ul_dev_addr Address of remote device.
296 * \param ul_dev_ep Targeted endpoint of remote device.
297 * \param ul_type Pipe type.
298 * \param ul_dir Pipe direction.
299 * \param ul_maxsize Pipe size.
300 * \param ul_interval Polling interval (if applicable to pipe type).
301 * \param ul_nb_bank Number of banks associated with this pipe.
302 *
303 * \return the newly allocated pipe number on success, 0 otherwise.
304 */
UHD_Pipe_Alloc(uint32_t ul_dev_addr,uint32_t ul_dev_ep,uint32_t ul_type,uint32_t ul_dir,uint32_t ul_maxsize,uint32_t ul_interval,uint32_t ul_nb_bank)305 uint32_t UHD_Pipe_Alloc(uint32_t ul_dev_addr, uint32_t ul_dev_ep, uint32_t ul_type, uint32_t ul_dir, uint32_t ul_maxsize, uint32_t ul_interval, uint32_t ul_nb_bank)
306 {
307 uint32_t ul_pipe = 1;
308
309 for (ul_pipe = 1; ul_pipe < UOTGHS_EPT_NUM; ++ul_pipe)
310 {
311 if (Is_uhd_pipe_enabled(ul_pipe))
312 {
313 continue;
314 }
315
316 uhd_enable_pipe(ul_pipe);
317
318 uhd_configure_pipe(ul_pipe, ul_interval, ul_dev_ep, ul_type, ul_dir,
319 ul_maxsize, ul_nb_bank, UOTGHS_HSTPIPCFG_AUTOSW);
320
321 uhd_allocate_memory(ul_pipe);
322
323 if (!Is_uhd_pipe_configured(ul_pipe))
324 {
325 uhd_disable_pipe(ul_pipe);
326 return 0;
327 }
328
329 uhd_configure_address(ul_pipe, ul_dev_addr);
330
331 // Pipe is configured and allocated successfully
332 return ul_pipe;
333 }
334
335 return 0;
336 }
337
338 /**
339 * \brief Free a pipe.
340 *
341 * \param ul_pipe Pipe number to free.
342 */
UHD_Pipe_Free(uint32_t ul_pipe)343 void UHD_Pipe_Free(uint32_t ul_pipe)
344 {
345 // Unalloc pipe
346 uhd_disable_pipe(ul_pipe);
347 uhd_unallocate_memory(ul_pipe);
348 uhd_reset_pipe(ul_pipe);
349 }
350
351 /**
352 * \brief Read from a pipe.
353 *
354 * \param ul_pipe Pipe number.
355 * \param ul_size Maximum number of data to read.
356 * \param data Buffer to store the data.
357 *
358 * \return number of data read.
359 */
UHD_Pipe_Read(uint32_t ul_pipe,uint32_t ul_size,uint8_t * data)360 uint32_t UHD_Pipe_Read(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data)
361 {
362 uint8_t *ptr_ep_data = 0;
363 uint8_t nb_byte_received = 0;
364 uint32_t ul_nb_trans = 0;
365
366 // Get information to read data
367 nb_byte_received = uhd_byte_count(ul_pipe);
368
369 ptr_ep_data = (uint8_t *) & uhd_get_pipe_fifo_access(ul_pipe, 8);
370
371 // Copy data from pipe to payload buffer
372 while (ul_size && nb_byte_received) {
373 *data++ = *ptr_ep_data++;
374 ul_nb_trans++;
375 ul_size--;
376 nb_byte_received--;
377 }
378
379 return ul_nb_trans;
380 }
381
382 /**
383 * \brief Write into a pipe.
384 *
385 * \param ul_pipe Pipe number.
386 * \param ul_size Maximum number of data to read.
387 * \param data Buffer containing data to write.
388 */
UHD_Pipe_Write(uint32_t ul_pipe,uint32_t ul_size,uint8_t * data)389 void UHD_Pipe_Write(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data)
390 {
391 volatile uint8_t *ptr_ep_data = 0;
392 uint32_t i = 0;
393
394 // Check pipe
395 if (!Is_uhd_pipe_enabled(ul_pipe))
396 {
397 // Endpoint not valid
398 TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe is not enabled!\r\n");)
399 return;
400 }
401
402 ptr_ep_data = (volatile uint8_t *)&uhd_get_pipe_fifo_access(ul_pipe, 8);
403 for (i = 0; i < ul_size; ++i)
404 *ptr_ep_data++ = *data++;
405 }
406
407 /**
408 * \brief Send a pipe content.
409 *
410 * \param ul_pipe Pipe number.
411 * \param ul_token_type Token type.
412 */
UHD_Pipe_Send(uint32_t ul_pipe,uint32_t ul_token_type)413 void UHD_Pipe_Send(uint32_t ul_pipe, uint32_t ul_token_type)
414 {
415 // Check pipe
416 if (!Is_uhd_pipe_enabled(ul_pipe))
417 {
418 // Endpoint not valid
419 TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe %lu is not enabled!\r\n", ul_pipe);)
420 return;
421 }
422
423 // Set token type for zero length packet
424 // When actually using the FIFO, pipe token MUST be configured first
425 uhd_configure_pipe_token(ul_pipe, ul_token_type);
426
427 // Clear interrupt flags
428 uhd_ack_setup_ready(ul_pipe);
429 uhd_ack_in_received(ul_pipe);
430 uhd_ack_out_ready(ul_pipe);
431 uhd_ack_short_packet(ul_pipe);
432 uhd_ack_nak_received(ul_pipe);
433
434 // Send actual packet
435 uhd_ack_fifocon(ul_pipe);
436 uhd_unfreeze_pipe(ul_pipe);
437 }
438
439 /**
440 * \brief Check for pipe transfer completion.
441 *
442 * \param ul_pipe Pipe number.
443 * \param ul_token_type Token type.
444 *
445 * \retval 0 transfer is not complete.
446 * \retval 1 transfer is complete.
447 */
UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe,uint32_t ul_token_type)448 uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type)
449 {
450 // Check for transfer completion depending on token type
451 switch (ul_token_type)
452 {
453 case UOTGHS_HSTPIPCFG_PTOKEN_SETUP:
454 if (Is_uhd_setup_ready(ul_pipe))
455 {
456 uhd_freeze_pipe(ul_pipe);
457 uhd_ack_setup_ready(ul_pipe);
458 return 1;
459 }
460
461 case UOTGHS_HSTPIPCFG_PTOKEN_IN:
462 if (Is_uhd_in_received(ul_pipe))
463 {
464 // In case of low USB speed and with a high CPU frequency,
465 // a ACK from host can be always running on USB line
466 // then wait end of ACK on IN pipe.
467 while(!Is_uhd_pipe_frozen(ul_pipe))
468 ;
469
470 // IN packet received
471 uhd_ack_in_received(ul_pipe);
472
473 return 1;
474 }
475
476 case UOTGHS_HSTPIPCFG_PTOKEN_OUT:
477 if (Is_uhd_out_ready(ul_pipe))
478 {
479 // OUT packet sent
480 uhd_freeze_pipe(ul_pipe);
481 uhd_ack_out_ready(ul_pipe);
482
483 return 1;
484 }
485 }
486
487 return 0;
488 }
489
490 #endif /* SAM3XA_SERIES */
491