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