Software APIs
dif_usbdev.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
6
7#include <assert.h>
8
11
12#include "usbdev_regs.h" // Generated.
13
14/**
15 * Definition in the header file (and probably other places) must be updated if
16 * there is a hardware change.
17 */
18static_assert(USBDEV_NUM_ENDPOINTS == USBDEV_PARAM_N_ENDPOINTS,
19 "Mismatch in number of endpoints");
20
21/**
22 * Max packet size is equal to the size of device buffers.
23 */
24#define USBDEV_BUFFER_ENTRY_SIZE_BYTES USBDEV_MAX_PACKET_SIZE
25
26/**
27 * Constants used to indicate that a buffer pool is full or empty.
28 */
29#define BUFFER_POOL_FULL (USBDEV_NUM_BUFFERS - 1)
30#define BUFFER_POOL_EMPTY -1
31
32/**
33 * Hardware information for endpoints.
34 */
35typedef struct endpoint_hw_info {
36 uint32_t config_in_reg_offset;
37 uint8_t bit_index;
38} endpoint_hw_info_t;
39
40/**
41 * Helper macro to define an `endpoint_hw_info_t` entry for endpoint N.
42 *
43 * Note: This uses the bit indices of `USBDEV_IN_SENT` register for the sake
44 * of conciseness because other endpoint registers use the same layout.
45 */
46#define ENDPOINT_HW_INFO_ENTRY(N) \
47 [N] = {.config_in_reg_offset = USBDEV_CONFIGIN_##N##_REG_OFFSET, \
48 .bit_index = USBDEV_IN_SENT_SENT_##N##_BIT}
49
50static const endpoint_hw_info_t kEndpointHwInfos[USBDEV_NUM_ENDPOINTS] = {
51 ENDPOINT_HW_INFO_ENTRY(0), ENDPOINT_HW_INFO_ENTRY(1),
52 ENDPOINT_HW_INFO_ENTRY(2), ENDPOINT_HW_INFO_ENTRY(3),
53 ENDPOINT_HW_INFO_ENTRY(4), ENDPOINT_HW_INFO_ENTRY(5),
54 ENDPOINT_HW_INFO_ENTRY(6), ENDPOINT_HW_INFO_ENTRY(7),
55 ENDPOINT_HW_INFO_ENTRY(8), ENDPOINT_HW_INFO_ENTRY(9),
56 ENDPOINT_HW_INFO_ENTRY(10), ENDPOINT_HW_INFO_ENTRY(11),
57};
58
59#undef ENDPOINT_HW_INFO_ENTRY
60
61/**
62 * Static functions for the free buffer pool.
63 */
64
65/**
66 * Checks if a buffer pool is full.
67 *
68 * A buffer pool is full if it contains `USBDEV_NUM_BUFFERS` buffers.
69 *
70 * @param pool A buffer pool.
71 * @return `true` if the buffer pool if full, `false` otherwise.
72 */
74static bool buffer_pool_is_full(dif_usbdev_buffer_pool_t *pool) {
75 return pool->top == BUFFER_POOL_FULL;
76}
77
78/**
79 * Checks if a buffer pool is empty.
80 *
81 * @param pool A buffer pool.
82 * @return `true` if the buffer pool is empty, `false` otherwise.
83 */
85static bool buffer_pool_is_empty(dif_usbdev_buffer_pool_t *pool) {
86 return pool->top == BUFFER_POOL_EMPTY;
87}
88
89/**
90 * Checks if a buffer id is valid.
91 *
92 * A buffer id is valid if it is less than `USBDEV_NUM_BUFFERS`.
93 *
94 * @param buffer_id A buffer id.
95 * @return `true` if `buffer_id` is valid, `false` otherwise.
96 */
98static bool buffer_pool_is_valid_buffer_id(uint8_t buffer_id) {
99 return buffer_id < USBDEV_NUM_BUFFERS;
100}
101
102/**
103 * Adds a buffer to a buffer pool.
104 *
105 * @param pool A buffer pool.
106 * @param buffer_id A buffer id.
107 * @return `true` if the operation was successful, `false` otherwise.
108 */
110static bool buffer_pool_add(dif_usbdev_buffer_pool_t *pool, uint8_t buffer_id) {
111 if (buffer_pool_is_full(pool) || !buffer_pool_is_valid_buffer_id(buffer_id)) {
112 return false;
113 }
114
115 ++pool->top;
116 pool->buffers[pool->top] = buffer_id;
117
118 return true;
119}
120
121/**
122 * Removes a buffer from a buffer pool.
123 *
124 * @param pool A buffer pool.
125 * @param buffer_id A buffer id.
126 * @return `true` if the operation was successful, `false` otherwise.
127 */
129static bool buffer_pool_remove(dif_usbdev_buffer_pool_t *pool,
130 uint8_t *buffer_id) {
131 if (buffer_pool_is_empty(pool) || buffer_id == NULL) {
132 return false;
133 }
134
135 *buffer_id = pool->buffers[pool->top];
136 --pool->top;
137
138 return true;
139}
140
141/**
142 * Initializes the buffer pool.
143 *
144 * At the end of this operation, the buffer pool contains `USBDEV_NUM_BUFFERS`
145 * buffers.
146 *
147 * @param pool A buffer pool.
148 * @return `true` if the operation was successful, `false` otherwise.
149 */
151static bool buffer_pool_init(dif_usbdev_buffer_pool_t *pool) {
152 // Start with an empty pool
153 pool->top = -1;
154
155 // Add all buffers
156 for (uint8_t i = 0; i < USBDEV_NUM_BUFFERS; ++i) {
157 if (!buffer_pool_add(pool, i)) {
158 return false;
159 }
160 }
161
162 return true;
163}
164
165/**
166 * Utility functions
167 */
168
169/**
170 * Checks if the given value is a valid endpoint number.
171 */
173static bool is_valid_endpoint(uint8_t endpoint_number) {
174 return endpoint_number < USBDEV_NUM_ENDPOINTS;
175}
176
177/**
178 * Enables/disables the functionality controlled by the register at `reg_offset`
179 * for an endpoint.
180 */
182static dif_result_t endpoint_functionality_enable(const dif_usbdev_t *usbdev,
183 uint32_t reg_offset,
184 uint8_t endpoint,
185 dif_toggle_t new_state) {
186 if (usbdev == NULL || !is_valid_endpoint(endpoint) ||
187 !dif_is_valid_toggle(new_state)) {
188 return kDifBadArg;
189 }
190
191 uint32_t reg_val =
192 mmio_region_read32(usbdev->base_addr, (ptrdiff_t)reg_offset);
193 reg_val = bitfield_bit32_write(reg_val, kEndpointHwInfos[endpoint].bit_index,
194 dif_toggle_to_bool(new_state));
195 mmio_region_write32(usbdev->base_addr, (ptrdiff_t)reg_offset, reg_val);
196 return kDifOk;
197}
198
199/**
200 * Returns the address that corresponds to the given buffer and offset
201 * into that buffer.
202 */
204static uint32_t get_buffer_addr(uint8_t buffer_id, size_t offset) {
205 return USBDEV_BUFFER_REG_OFFSET +
206 (buffer_id * USBDEV_BUFFER_ENTRY_SIZE_BYTES) + offset;
207}
208
209/**
210 * USBDEV DIF library functions.
211 */
212
213dif_result_t dif_usbdev_configure(const dif_usbdev_t *usbdev,
214 dif_usbdev_buffer_pool_t *buffer_pool,
215 dif_usbdev_config_t config) {
216 if (usbdev == NULL || buffer_pool == NULL) {
217 return kDifBadArg;
218 }
219
220 // Configure the free buffer pool.
221 if (!buffer_pool_init(buffer_pool)) {
222 return kDifError;
223 }
224
225 // Check enum fields.
226 if (!dif_is_valid_toggle(config.have_differential_receiver) ||
227 !dif_is_valid_toggle(config.use_tx_d_se0) ||
228 !dif_is_valid_toggle(config.single_bit_eop) ||
229 !dif_is_valid_toggle(config.pin_flip) ||
230 !dif_is_valid_toggle(config.clock_sync_signals)) {
231 return kDifBadArg;
232 }
233
234 // Determine the value of the PHY_CONFIG register.
235 uint32_t phy_config_val = 0;
236 phy_config_val = bitfield_bit32_write(
237 phy_config_val, USBDEV_PHY_CONFIG_USE_DIFF_RCVR_BIT,
238 dif_toggle_to_bool(config.have_differential_receiver));
239 phy_config_val =
240 bitfield_bit32_write(phy_config_val, USBDEV_PHY_CONFIG_TX_USE_D_SE0_BIT,
241 dif_toggle_to_bool(config.use_tx_d_se0));
242 phy_config_val =
243 bitfield_bit32_write(phy_config_val, USBDEV_PHY_CONFIG_EOP_SINGLE_BIT_BIT,
244 dif_toggle_to_bool(config.single_bit_eop));
245 phy_config_val =
246 bitfield_bit32_write(phy_config_val, USBDEV_PHY_CONFIG_PINFLIP_BIT,
247 dif_toggle_to_bool(config.pin_flip));
248 phy_config_val = bitfield_bit32_write(
249 phy_config_val, USBDEV_PHY_CONFIG_USB_REF_DISABLE_BIT,
250 !dif_toggle_to_bool(config.clock_sync_signals));
251
252 // Write configuration to PHY_CONFIG register
253 mmio_region_write32(usbdev->base_addr, USBDEV_PHY_CONFIG_REG_OFFSET,
254 phy_config_val);
255
256 return kDifOk;
257}
258
259dif_result_t dif_usbdev_fill_available_fifos(
260 const dif_usbdev_t *usbdev, dif_usbdev_buffer_pool_t *buffer_pool) {
261 if (usbdev == NULL || buffer_pool == NULL) {
262 return kDifBadArg;
263 }
264
265 // Remove buffers from the pool and write as many as possible into the FIFOs
266 while (!buffer_pool_is_empty(buffer_pool)) {
267 uint32_t status =
268 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
269 // Prioritize available SETUP buffers
270 uint32_t av_setup_depth =
271 bitfield_field32_read(status, USBDEV_USBSTAT_AV_SETUP_DEPTH_FIELD);
272 if (av_setup_depth >= 2) {
273 // Available SETUP Buffer FIFO is okay, what about the OUT buffers?
274 bool av_out_full =
275 bitfield_bit32_read(status, USBDEV_USBSTAT_AV_OUT_FULL_BIT);
276 if (av_out_full) {
277 break;
278 }
279 }
280 uint8_t buffer_id;
281 if (!buffer_pool_remove(buffer_pool, &buffer_id)) {
282 return kDifError;
283 }
284 if (av_setup_depth >= 2) {
285 // Supply Available OUT Buffer
286 uint32_t reg_val =
287 bitfield_field32_write(0, USBDEV_AVOUTBUFFER_BUFFER_FIELD, buffer_id);
288 mmio_region_write32(usbdev->base_addr, USBDEV_AVOUTBUFFER_REG_OFFSET,
289 reg_val);
290 } else {
291 // Supply Available SETUP Buffer
292 uint32_t reg_val = bitfield_field32_write(
293 0, USBDEV_AVSETUPBUFFER_BUFFER_FIELD, buffer_id);
294 mmio_region_write32(usbdev->base_addr, USBDEV_AVSETUPBUFFER_REG_OFFSET,
295 reg_val);
296 }
297 }
298
299 return kDifOk;
300}
301
302dif_result_t dif_usbdev_endpoint_setup_enable(const dif_usbdev_t *usbdev,
303 uint8_t endpoint,
304 dif_toggle_t new_state) {
305 return endpoint_functionality_enable(usbdev, USBDEV_RXENABLE_SETUP_REG_OFFSET,
306 endpoint, new_state);
307}
308
309dif_result_t dif_usbdev_endpoint_out_enable(const dif_usbdev_t *usbdev,
310 uint8_t endpoint,
311 dif_toggle_t new_state) {
312 return endpoint_functionality_enable(usbdev, USBDEV_RXENABLE_OUT_REG_OFFSET,
313 endpoint, new_state);
314}
315
316dif_result_t dif_usbdev_endpoint_set_nak_out_enable(const dif_usbdev_t *usbdev,
317 uint8_t endpoint,
318 dif_toggle_t new_state) {
319 return endpoint_functionality_enable(usbdev, USBDEV_SET_NAK_OUT_REG_OFFSET,
320 endpoint, new_state);
321}
322
323dif_result_t dif_usbdev_endpoint_stall_enable(const dif_usbdev_t *usbdev,
324 dif_usbdev_endpoint_id_t endpoint,
325 dif_toggle_t new_state) {
326 if (endpoint.direction == USBDEV_ENDPOINT_DIR_IN) {
327 return endpoint_functionality_enable(usbdev, USBDEV_IN_STALL_REG_OFFSET,
328 endpoint.number, new_state);
329 } else {
330 return endpoint_functionality_enable(usbdev, USBDEV_OUT_STALL_REG_OFFSET,
331 endpoint.number, new_state);
332 }
333}
334
335dif_result_t dif_usbdev_endpoint_stall_get(const dif_usbdev_t *usbdev,
336 dif_usbdev_endpoint_id_t endpoint,
337 bool *state) {
338 if (usbdev == NULL || state == NULL || !is_valid_endpoint(endpoint.number)) {
339 return kDifBadArg;
340 }
341
342 ptrdiff_t reg_offset = endpoint.direction == USBDEV_ENDPOINT_DIR_IN
343 ? USBDEV_IN_STALL_REG_OFFSET
344 : USBDEV_OUT_STALL_REG_OFFSET;
345 uint32_t reg_val = mmio_region_read32(usbdev->base_addr, reg_offset);
346 *state =
347 bitfield_bit32_read(reg_val, kEndpointHwInfos[endpoint.number].bit_index);
348
349 return kDifOk;
350}
351
352dif_result_t dif_usbdev_endpoint_iso_enable(const dif_usbdev_t *usbdev,
353 dif_usbdev_endpoint_id_t endpoint,
354 dif_toggle_t new_state) {
355 if (endpoint.direction == USBDEV_ENDPOINT_DIR_IN) {
356 return endpoint_functionality_enable(usbdev, USBDEV_IN_ISO_REG_OFFSET,
357 endpoint.number, new_state);
358 } else {
359 return endpoint_functionality_enable(usbdev, USBDEV_OUT_ISO_REG_OFFSET,
360 endpoint.number, new_state);
361 }
362}
363
364dif_result_t dif_usbdev_endpoint_enable(const dif_usbdev_t *usbdev,
365 dif_usbdev_endpoint_id_t endpoint,
366 dif_toggle_t new_state) {
367 if (endpoint.direction == USBDEV_ENDPOINT_DIR_IN) {
368 return endpoint_functionality_enable(usbdev, USBDEV_EP_IN_ENABLE_REG_OFFSET,
369 endpoint.number, new_state);
370 } else {
371 return endpoint_functionality_enable(
372 usbdev, USBDEV_EP_OUT_ENABLE_REG_OFFSET, endpoint.number, new_state);
373 }
374 return kDifOk;
375}
376
377dif_result_t dif_usbdev_interface_enable(const dif_usbdev_t *usbdev,
378 dif_toggle_t new_state) {
379 if (usbdev == NULL || !dif_is_valid_toggle(new_state)) {
380 return kDifBadArg;
381 }
382
383 uint32_t reg_val =
384 mmio_region_read32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET);
385 reg_val = bitfield_bit32_write(reg_val, USBDEV_USBCTRL_ENABLE_BIT,
386 dif_toggle_to_bool(new_state));
387 mmio_region_write32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET, reg_val);
388
389 return kDifOk;
390}
391
392dif_result_t dif_usbdev_recv(const dif_usbdev_t *usbdev,
394 dif_usbdev_buffer_t *buffer) {
395 if (usbdev == NULL || info == NULL || buffer == NULL) {
396 return kDifBadArg;
397 }
398
399 // Check if the RX FIFO is empty
400 uint32_t fifo_status =
401 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
402 if (bitfield_bit32_read(fifo_status, USBDEV_USBSTAT_RX_EMPTY_BIT)) {
403 return kDifUnavailable;
404 }
405
406 // Read fifo entry
407 const uint32_t fifo_entry =
408 mmio_region_read32(usbdev->base_addr, USBDEV_RXFIFO_REG_OFFSET);
409 // Init packet info
411 .endpoint =
412 (uint8_t)bitfield_field32_read(fifo_entry, USBDEV_RXFIFO_EP_FIELD),
413 .is_setup = bitfield_bit32_read(fifo_entry, USBDEV_RXFIFO_SETUP_BIT),
414 .length =
415 (uint8_t)bitfield_field32_read(fifo_entry, USBDEV_RXFIFO_SIZE_FIELD),
416 };
417 // Init buffer struct
418 *buffer = (dif_usbdev_buffer_t){
419 .id = (uint8_t)bitfield_field32_read(fifo_entry,
420 USBDEV_RXFIFO_BUFFER_FIELD),
421 .offset = 0,
422 .remaining_bytes = info->length,
424 };
425
426 return kDifOk;
427}
428
429dif_result_t dif_usbdev_buffer_request(const dif_usbdev_t *usbdev,
430 dif_usbdev_buffer_pool_t *buffer_pool,
431 dif_usbdev_buffer_t *buffer) {
432 if (usbdev == NULL || buffer_pool == NULL || buffer == NULL) {
433 return kDifBadArg;
434 }
435
436 if (buffer_pool_is_empty(buffer_pool)) {
437 return kDifUnavailable;
438 }
439
440 uint8_t buffer_id;
441 if (!buffer_pool_remove(buffer_pool, &buffer_id)) {
442 return kDifError;
443 }
444
445 *buffer = (dif_usbdev_buffer_t){
446 .id = buffer_id,
447 .offset = 0,
448 .remaining_bytes = USBDEV_BUFFER_ENTRY_SIZE_BYTES,
450 };
451
452 return kDifOk;
453}
454
455dif_result_t dif_usbdev_buffer_return(const dif_usbdev_t *usbdev,
456 dif_usbdev_buffer_pool_t *buffer_pool,
457 dif_usbdev_buffer_t *buffer) {
458 if (usbdev == NULL || buffer_pool == NULL || buffer == NULL) {
459 return kDifBadArg;
460 }
461
462 switch (buffer->type) {
465 // Return the buffer to the free buffer pool
466 if (!buffer_pool_add(buffer_pool, buffer->id)) {
467 return kDifError;
468 }
469 // Mark the buffer as stale
471 return kDifOk;
472 default:
473 return kDifBadArg;
474 }
475}
476
477dif_result_t dif_usbdev_buffer_read(const dif_usbdev_t *usbdev,
478 dif_usbdev_buffer_pool_t *buffer_pool,
479 dif_usbdev_buffer_t *buffer, uint8_t *dst,
480 size_t dst_len, size_t *bytes_written) {
481 if (usbdev == NULL || buffer_pool == NULL || buffer == NULL ||
482 buffer->type != kDifUsbdevBufferTypeRead || dst == NULL) {
483 return kDifBadArg;
484 }
485
486 // bytes_to_copy is the minimum of remaining_bytes and dst_len
487 size_t bytes_to_copy = buffer->remaining_bytes;
488 if (bytes_to_copy > dst_len) {
489 bytes_to_copy = dst_len;
490 }
491 // Copy from buffer to dst
492 const uint32_t buffer_addr = get_buffer_addr(buffer->id, buffer->offset);
493 mmio_region_memcpy_from_mmio32(usbdev->base_addr, buffer_addr, dst,
494 bytes_to_copy);
495 // Update buffer state
496 buffer->offset += bytes_to_copy;
497 buffer->remaining_bytes -= bytes_to_copy;
498
499 if (bytes_written != NULL) {
500 *bytes_written = bytes_to_copy;
501 }
502
503 // Check if there are any remaining bytes
504 if (buffer->remaining_bytes > 0) {
505 return kDifOk;
506 }
507
508 // Return the buffer to the free buffer pool
509 if (!buffer_pool_add(buffer_pool, buffer->id)) {
510 return kDifError;
511 }
512
513 // Mark the buffer as stale
515 return kDifOk;
516}
517
518dif_result_t dif_usbdev_buffer_write(const dif_usbdev_t *usbdev,
519 dif_usbdev_buffer_t *buffer,
520 const uint8_t *src, size_t src_len,
521 size_t *bytes_written) {
522 if (usbdev == NULL || buffer == NULL ||
523 buffer->type != kDifUsbdevBufferTypeWrite || src == NULL) {
524 return kDifBadArg;
525 }
526
527 // bytes_to_copy is the minimum of remaining_bytes and src_len.
528 size_t bytes_to_copy = buffer->remaining_bytes;
529 if (bytes_to_copy > src_len) {
530 bytes_to_copy = src_len;
531 }
532
533 // Write bytes to the buffer
534 uint32_t buffer_addr = get_buffer_addr(buffer->id, buffer->offset);
535 mmio_region_memcpy_to_mmio32(usbdev->base_addr, buffer_addr, src,
536 bytes_to_copy);
537
538 buffer->offset += bytes_to_copy;
539 buffer->remaining_bytes -= bytes_to_copy;
540
541 if (bytes_written) {
542 *bytes_written = bytes_to_copy;
543 }
544
545 if (buffer->remaining_bytes == 0 && bytes_to_copy < src_len) {
546 return kDifError;
547 }
548
549 return kDifOk;
550}
551
552dif_result_t dif_usbdev_send(const dif_usbdev_t *usbdev, uint8_t endpoint,
553 dif_usbdev_buffer_t *buffer) {
554 if (usbdev == NULL || !is_valid_endpoint(endpoint) || buffer == NULL ||
555 buffer->type != kDifUsbdevBufferTypeWrite) {
556 return kDifBadArg;
557 }
558
559 // Get the configin register offset of the endpoint.
560 const uint32_t config_in_reg_offset =
561 kEndpointHwInfos[endpoint].config_in_reg_offset;
562
563 // Configure USBDEV_CONFIGINX register.
564 // Note: Using mask and offset values for the USBDEV_CONFIGIN0 register
565 // for all endpoints because all USBDEV_CONFIGINX registers have the same
566 // layout.
567 uint32_t config_in_val = 0;
568 config_in_val = bitfield_field32_write(
569 config_in_val, USBDEV_CONFIGIN_0_BUFFER_0_FIELD, buffer->id);
570 config_in_val = bitfield_field32_write(
571 config_in_val, USBDEV_CONFIGIN_0_SIZE_0_FIELD, buffer->offset);
572 mmio_region_write32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset,
573 config_in_val);
574
575 // Mark the packet as ready for transmission
576 config_in_val =
577 bitfield_bit32_write(config_in_val, USBDEV_CONFIGIN_0_RDY_0_BIT, true);
578 mmio_region_write32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset,
579 config_in_val);
580
581 // Mark the buffer as stale. It will be returned to the free buffer pool
582 // in dif_usbdev_get_tx_status once transmission is complete.
584
585 return kDifOk;
586}
587
588dif_result_t dif_usbdev_get_tx_sent(const dif_usbdev_t *usbdev,
589 uint16_t *sent) {
590 if (usbdev == NULL || sent == NULL) {
591 return kDifBadArg;
592 }
593 *sent = (uint16_t)mmio_region_read32(usbdev->base_addr,
594 USBDEV_IN_SENT_REG_OFFSET);
595 return kDifOk;
596}
597
598dif_result_t dif_usbdev_clear_tx_status(const dif_usbdev_t *usbdev,
599 dif_usbdev_buffer_pool_t *buffer_pool,
600 uint8_t endpoint) {
601 if (usbdev == NULL || buffer_pool == NULL || !is_valid_endpoint(endpoint)) {
602 return kDifBadArg;
603 }
604 // Get the configin register offset and bit index of the endpoint.
605 uint32_t config_in_reg_offset =
606 kEndpointHwInfos[endpoint].config_in_reg_offset;
607 uint32_t config_in_reg_val =
608 mmio_region_read32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset);
609 uint8_t buffer = (uint8_t)bitfield_field32_read(
610 config_in_reg_val, USBDEV_CONFIGIN_0_BUFFER_0_FIELD);
611
612 mmio_region_write32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset,
613 1u << USBDEV_CONFIGIN_0_PEND_0_BIT);
614 // Clear IN_SENT bit (rw1c).
615 mmio_region_write32(usbdev->base_addr, USBDEV_IN_SENT_REG_OFFSET,
616 1u << endpoint);
617 // Return the buffer back to the free buffer pool.
618 if (!buffer_pool_add(buffer_pool, buffer)) {
619 return kDifError;
620 }
621 return kDifOk;
622}
623
624dif_result_t dif_usbdev_get_tx_status(const dif_usbdev_t *usbdev,
625 uint8_t endpoint,
627 if (usbdev == NULL || status == NULL || !is_valid_endpoint(endpoint)) {
628 return kDifBadArg;
629 }
630
631 // Get the configin register offset and bit index of the endpoint.
632 uint32_t config_in_reg_offset =
633 kEndpointHwInfos[endpoint].config_in_reg_offset;
634 uint8_t endpoint_bit_index = kEndpointHwInfos[endpoint].bit_index;
635
636 // Read the configin register.
637 uint32_t config_in_val =
638 mmio_region_read32(usbdev->base_addr, (ptrdiff_t)config_in_reg_offset);
639
640 // Check the status of the packet.
641 if (bitfield_bit32_read(config_in_val, USBDEV_CONFIGIN_0_RDY_0_BIT)) {
642 // Packet is marked as ready to be sent and pending transmission.
644 } else if (bitfield_bit32_read(mmio_region_read32(usbdev->base_addr,
645 USBDEV_IN_SENT_REG_OFFSET),
646 endpoint_bit_index)) {
647 // Packet was sent successfully.
649 } else if (bitfield_bit32_read(config_in_val, USBDEV_CONFIGIN_0_PEND_0_BIT)) {
650 // Canceled due to an IN SETUP packet or link reset.
652 } else {
653 // No packet has been queued for this endpoint.
655 }
656
657 return kDifOk;
658}
659
660dif_result_t dif_usbdev_address_set(const dif_usbdev_t *usbdev, uint8_t addr) {
661 if (usbdev == NULL) {
662 return kDifBadArg;
663 }
664
665 uint32_t reg_val =
666 mmio_region_read32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET);
667 reg_val = bitfield_field32_write(reg_val, USBDEV_USBCTRL_DEVICE_ADDRESS_FIELD,
668 addr);
669 mmio_region_write32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET, reg_val);
670
671 return kDifOk;
672}
673
674dif_result_t dif_usbdev_address_get(const dif_usbdev_t *usbdev, uint8_t *addr) {
675 if (usbdev == NULL || addr == NULL) {
676 return kDifBadArg;
677 }
678
679 uint32_t reg_val =
680 mmio_region_read32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET);
681 // Note: Size of address is 7 bits.
682 *addr = (uint8_t)bitfield_field32_read(reg_val,
683 USBDEV_USBCTRL_DEVICE_ADDRESS_FIELD);
684
685 return kDifOk;
686}
687
688dif_result_t dif_usbdev_data_toggle_out_read(const dif_usbdev_t *usbdev,
689 uint16_t *toggles) {
690 if (usbdev == NULL || toggles == NULL) {
691 return kDifBadArg;
692 }
693
694 uint32_t reg_val =
695 mmio_region_read32(usbdev->base_addr, USBDEV_OUT_DATA_TOGGLE_REG_OFFSET);
696 // Note: only 12 OUT endpoints defined.
697 *toggles = (uint16_t)reg_val;
698
699 return kDifOk;
700}
701
702dif_result_t dif_usbdev_data_toggle_in_read(const dif_usbdev_t *usbdev,
703 uint16_t *toggles) {
704 if (usbdev == NULL || toggles == NULL) {
705 return kDifBadArg;
706 }
707
708 uint32_t reg_val =
709 mmio_region_read32(usbdev->base_addr, USBDEV_IN_DATA_TOGGLE_REG_OFFSET);
710 // Note: only 12 OUT endpoints defined.
711 *toggles = (uint16_t)reg_val;
712
713 return kDifOk;
714}
715
716dif_result_t dif_usbdev_data_toggle_out_write(const dif_usbdev_t *usbdev,
717 uint16_t mask, uint16_t state) {
718 if (usbdev == NULL) {
719 return kDifBadArg;
720 }
721
722 // Note: only 12 OUT endpoints defined.
723 mmio_region_write32(usbdev->base_addr, USBDEV_OUT_DATA_TOGGLE_REG_OFFSET,
724 ((uint32_t)mask << 16) | state);
725
726 return kDifOk;
727}
728
729dif_result_t dif_usbdev_data_toggle_in_write(const dif_usbdev_t *usbdev,
730 uint16_t mask, uint16_t state) {
731 if (usbdev == NULL) {
732 return kDifBadArg;
733 }
734
735 // Note: only 12 OUT endpoints defined.
736 mmio_region_write32(usbdev->base_addr, USBDEV_IN_DATA_TOGGLE_REG_OFFSET,
737 ((uint32_t)mask << 16) | state);
738
739 return kDifOk;
740}
741
742dif_result_t dif_usbdev_clear_data_toggle(const dif_usbdev_t *usbdev,
743 uint8_t endpoint) {
744 if (usbdev == NULL) {
745 return kDifBadArg;
746 }
747
748 uint32_t reg_val = (uint32_t)1u << (endpoint + 16u);
749 mmio_region_write32(usbdev->base_addr, USBDEV_OUT_DATA_TOGGLE_REG_OFFSET,
750 reg_val);
751 mmio_region_write32(usbdev->base_addr, USBDEV_IN_DATA_TOGGLE_REG_OFFSET,
752 reg_val);
753
754 return kDifOk;
755}
756
757dif_result_t dif_usbdev_status_get_frame(const dif_usbdev_t *usbdev,
758 uint16_t *frame_index) {
759 if (usbdev == NULL || frame_index == NULL) {
760 return kDifBadArg;
761 }
762
763 uint32_t reg_val =
764 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
765 // Note: size of frame index is 11 bits.
766 *frame_index =
767 (uint8_t)bitfield_field32_read(reg_val, USBDEV_USBSTAT_FRAME_FIELD);
768
769 return kDifOk;
770}
771
772dif_result_t dif_usbdev_status_get_host_lost(const dif_usbdev_t *usbdev,
773 bool *host_lost) {
774 if (usbdev == NULL || host_lost == NULL) {
775 return kDifBadArg;
776 }
777
778 *host_lost =
779 mmio_region_get_bit32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET,
780 USBDEV_USBSTAT_HOST_LOST_BIT);
781
782 return kDifOk;
783}
784
785dif_result_t dif_usbdev_status_get_link_state(
786 const dif_usbdev_t *usbdev, dif_usbdev_link_state_t *link_state) {
787 if (usbdev == NULL || link_state == NULL) {
788 return kDifBadArg;
789 }
790
791 uint32_t val =
792 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
793 val = bitfield_field32_read(val, USBDEV_USBSTAT_LINK_STATE_FIELD);
794
795 switch (val) {
796 case USBDEV_USBSTAT_LINK_STATE_VALUE_DISCONNECTED:
797 *link_state = kDifUsbdevLinkStateDisconnected;
798 break;
799 case USBDEV_USBSTAT_LINK_STATE_VALUE_POWERED:
800 *link_state = kDifUsbdevLinkStatePowered;
801 break;
802 case USBDEV_USBSTAT_LINK_STATE_VALUE_POWERED_SUSPENDED:
803 *link_state = kDifUsbdevLinkStatePoweredSuspended;
804 break;
805 case USBDEV_USBSTAT_LINK_STATE_VALUE_ACTIVE:
806 *link_state = kDifUsbdevLinkStateActive;
807 break;
808 case USBDEV_USBSTAT_LINK_STATE_VALUE_SUSPENDED:
809 *link_state = kDifUsbdevLinkStateSuspended;
810 break;
811 case USBDEV_USBSTAT_LINK_STATE_VALUE_ACTIVE_NOSOF:
812 *link_state = kDifUsbdevLinkStateActiveNoSof;
813 break;
814 case USBDEV_USBSTAT_LINK_STATE_VALUE_RESUMING:
815 *link_state = kDifUsbdevLinkStateResuming;
816 break;
817 default:
818 return kDifError;
819 }
820
821 return kDifOk;
822}
823
824dif_result_t dif_usbdev_status_get_sense(const dif_usbdev_t *usbdev,
825 bool *sense) {
826 if (usbdev == NULL || sense == NULL) {
827 return kDifBadArg;
828 }
829
830 *sense = mmio_region_get_bit32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET,
831 USBDEV_USBSTAT_SENSE_BIT);
832
833 return kDifOk;
834}
835
836dif_result_t dif_usbdev_status_get_available_fifo_depths(
837 const dif_usbdev_t *usbdev, uint8_t *setup_depth, uint8_t *out_depth) {
838 if (usbdev == NULL || setup_depth == NULL || out_depth == NULL) {
839 return kDifBadArg;
840 }
841
842 uint32_t reg_val =
843 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
844 // Note: Size of Available SETUP FIFO depth is 3 bits.
845 *setup_depth = (uint8_t)bitfield_field32_read(
846 reg_val, USBDEV_USBSTAT_AV_SETUP_DEPTH_FIELD);
847 // Note: Size of Available OUT FIFO depth is 4 bits.
848 *out_depth = (uint8_t)bitfield_field32_read(
849 reg_val, USBDEV_USBSTAT_AV_OUT_DEPTH_FIELD);
850
851 return kDifOk;
852}
853
854dif_result_t dif_usbdev_status_get_available_fifo_full(
855 const dif_usbdev_t *usbdev, bool *setup_is_full, bool *out_is_full) {
856 if (usbdev == NULL || setup_is_full == NULL || out_is_full == NULL) {
857 return kDifBadArg;
858 }
859
860 uint32_t reg_val =
861 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
862 *setup_is_full =
863 bitfield_bit32_read(reg_val, USBDEV_USBSTAT_AV_SETUP_FULL_BIT);
864 *out_is_full = bitfield_bit32_read(reg_val, USBDEV_USBSTAT_AV_OUT_FULL_BIT);
865
866 return kDifOk;
867}
868
869dif_result_t dif_usbdev_status_get_rx_fifo_depth(const dif_usbdev_t *usbdev,
870 uint8_t *depth) {
871 if (usbdev == NULL || depth == NULL) {
872 return kDifBadArg;
873 }
874
875 uint32_t reg_val =
876 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
877 // Note: Size of RX FIFO depth is 4 bits.
878 *depth =
879 (uint8_t)bitfield_field32_read(reg_val, USBDEV_USBSTAT_RX_DEPTH_FIELD);
880
881 return kDifOk;
882}
883
884dif_result_t dif_usbdev_status_get_rx_fifo_empty(const dif_usbdev_t *usbdev,
885 bool *is_empty) {
886 if (usbdev == NULL || is_empty == NULL) {
887 return kDifBadArg;
888 }
889
890 uint32_t reg_val =
891 mmio_region_read32(usbdev->base_addr, USBDEV_USBSTAT_REG_OFFSET);
892 *is_empty = bitfield_bit32_read(reg_val, USBDEV_USBSTAT_RX_EMPTY_BIT);
893
894 return kDifOk;
895}
896
897dif_result_t dif_usbdev_set_osc_test_mode(const dif_usbdev_t *usbdev,
898 dif_toggle_t enable) {
899 if (usbdev == NULL || !dif_is_valid_toggle(enable)) {
900 return kDifBadArg;
901 }
902 bool set_tx_osc_mode = dif_toggle_to_bool(enable);
903 uint32_t reg_val =
904 mmio_region_read32(usbdev->base_addr, USBDEV_PHY_CONFIG_REG_OFFSET);
905 reg_val = bitfield_bit32_write(
906 reg_val, USBDEV_PHY_CONFIG_TX_OSC_TEST_MODE_BIT, set_tx_osc_mode);
907 mmio_region_write32(usbdev->base_addr, USBDEV_PHY_CONFIG_REG_OFFSET, reg_val);
908 return kDifOk;
909}
910
911dif_result_t dif_usbdev_set_wake_enable(const dif_usbdev_t *usbdev,
912 dif_toggle_t enable) {
913 if (usbdev == NULL || !dif_is_valid_toggle(enable)) {
914 return kDifBadArg;
915 }
916 uint32_t reg_val;
917 if (dif_toggle_to_bool(enable)) {
918 reg_val =
919 bitfield_bit32_write(0, USBDEV_WAKE_CONTROL_SUSPEND_REQ_BIT, true);
920 } else {
921 reg_val = bitfield_bit32_write(0, USBDEV_WAKE_CONTROL_WAKE_ACK_BIT, true);
922 }
923 mmio_region_write32(usbdev->base_addr, USBDEV_WAKE_CONTROL_REG_OFFSET,
924 reg_val);
925 return kDifOk;
926}
927
928dif_result_t dif_usbdev_get_wake_status(const dif_usbdev_t *usbdev,
929 dif_usbdev_wake_status_t *status) {
930 if (usbdev == NULL || status == NULL) {
931 return kDifBadArg;
932 }
933 uint32_t reg_val =
934 mmio_region_read32(usbdev->base_addr, USBDEV_WAKE_EVENTS_REG_OFFSET);
935 status->active =
936 bitfield_bit32_read(reg_val, USBDEV_WAKE_EVENTS_MODULE_ACTIVE_BIT);
937 status->disconnected =
938 bitfield_bit32_read(reg_val, USBDEV_WAKE_EVENTS_DISCONNECTED_BIT);
939 status->bus_reset =
940 bitfield_bit32_read(reg_val, USBDEV_WAKE_EVENTS_BUS_RESET_BIT);
941 status->bus_not_idle =
942 bitfield_bit32_read(reg_val, USBDEV_WAKE_EVENTS_BUS_NOT_IDLE_BIT);
943 return kDifOk;
944}
945
946dif_result_t dif_usbdev_resume_link_to_active(const dif_usbdev_t *usbdev) {
947 if (usbdev == NULL) {
948 return kDifBadArg;
949 }
950 uint32_t reg_val =
951 mmio_region_read32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET);
952 reg_val = bitfield_bit32_write(reg_val, USBDEV_USBCTRL_RESUME_LINK_ACTIVE_BIT,
953 true);
954 mmio_region_write32(usbdev->base_addr, USBDEV_USBCTRL_REG_OFFSET, reg_val);
955 return kDifOk;
956}
957
958dif_result_t dif_usbdev_get_phy_pins_status(
959 const dif_usbdev_t *usbdev, dif_usbdev_phy_pins_sense_t *status) {
960 if (usbdev == NULL || status == NULL) {
961 return kDifBadArg;
962 }
963 uint32_t reg_val =
964 mmio_region_read32(usbdev->base_addr, USBDEV_PHY_PINS_SENSE_REG_OFFSET);
965 status->rx_dp =
966 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_RX_DP_I_BIT);
967 status->rx_dn =
968 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_RX_DN_I_BIT);
969 status->rx_d = bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_RX_D_I_BIT);
970 status->tx_dp =
971 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_DP_O_BIT);
972 status->tx_dn =
973 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_DN_O_BIT);
974 status->tx_d = bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_D_O_BIT);
975 status->tx_se0 =
976 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_SE0_O_BIT);
977 status->output_enable =
978 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_TX_OE_O_BIT);
979 status->vbus_sense =
980 bitfield_bit32_read(reg_val, USBDEV_PHY_PINS_SENSE_PWR_SENSE_BIT);
981 return kDifOk;
982}
983
984dif_result_t dif_usbdev_set_phy_pins_state(
985 const dif_usbdev_t *usbdev, dif_toggle_t override_enable,
986 dif_usbdev_phy_pins_drive_t overrides) {
987 if (usbdev == NULL || !dif_is_valid_toggle(override_enable)) {
988 return kDifBadArg;
989 }
990 bool drive_en = dif_toggle_to_bool(override_enable);
991 uint32_t reg_val =
992 bitfield_bit32_write(0, USBDEV_PHY_PINS_DRIVE_EN_BIT, drive_en);
993 if (drive_en) {
994 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_DP_O_BIT,
995 overrides.dp);
996 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_DN_O_BIT,
997 overrides.dn);
998 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_D_O_BIT,
999 overrides.data);
1000 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_SE0_O_BIT,
1001 overrides.se0);
1002 reg_val = bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_OE_O_BIT,
1003 overrides.output_enable);
1004 reg_val =
1005 bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_RX_ENABLE_O_BIT,
1006 overrides.diff_receiver_enable);
1007 reg_val =
1008 bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_DP_PULLUP_EN_O_BIT,
1009 overrides.dp_pullup_en);
1010 reg_val =
1011 bitfield_bit32_write(reg_val, USBDEV_PHY_PINS_DRIVE_DN_PULLUP_EN_O_BIT,
1012 overrides.dn_pullup_en);
1013 }
1014 mmio_region_write32(usbdev->base_addr, USBDEV_PHY_PINS_DRIVE_REG_OFFSET,
1015 reg_val);
1016 return kDifOk;
1017}
1018
1019dif_result_t dif_usbdev_buffer_raw_write(const dif_usbdev_t *usbdev, uint8_t id,
1020 const uint8_t *src, size_t src_len) {
1021 if (usbdev == NULL || src == NULL || misalignment32_of((uintptr_t)src) ||
1022 src_len > USBDEV_BUFFER_ENTRY_SIZE_BYTES) {
1023 return kDifBadArg;
1024 }
1025
1026 // We're writing to the start of the buffer.
1027 ptrdiff_t buffer_offset = (ptrdiff_t)get_buffer_addr(id, 0U);
1028 const uint32_t *restrict ews = (uint32_t *)(src + (src_len & ~15u));
1029 const uint32_t *restrict ws = (uint32_t *)src;
1030
1031 // Transfer blocks of 4 x 32-bit words at a time; use the mmio_ routines for
1032 // compliance and to operate correctly with the DIF mocks, although this
1033 // results in transfers taking 50% longer because of the additional addressing
1034 // arithmetic and increased loop overheads.
1035 while (ws < ews) {
1036 mmio_region_write32(usbdev->base_addr, buffer_offset, ws[0]);
1037 mmio_region_write32(usbdev->base_addr, buffer_offset + 4, ws[1]);
1038 mmio_region_write32(usbdev->base_addr, buffer_offset + 8, ws[2]);
1039 mmio_region_write32(usbdev->base_addr, buffer_offset + 12, ws[3]);
1040 buffer_offset += 16;
1041 ws += 4;
1042 }
1043 src_len &= 15u;
1044
1045 if (src_len) {
1046 // Remaining whole words
1047 ews = ws + (src_len >> 2);
1048 while (ws < ews) {
1049 mmio_region_write32(usbdev->base_addr, buffer_offset, *ws++);
1050 buffer_offset += 4;
1051 }
1052 src_len &= 3u;
1053 if (src_len) {
1054 // Remaining individual bytes
1055 const uint8_t *restrict bs = (uint8_t *)ws;
1056 uint32_t d = bs[0];
1057 if (src_len > 1) {
1058 d |= ((uint32_t)bs[1] << 8);
1059 if (src_len > 2) {
1060 d |= ((uint32_t)bs[2] << 16);
1061 }
1062 }
1063 // Note: we can only perform full 32-bit writes to the packet buffer but
1064 // any additional byte(s) will be ignored. Attempting byte-level writes
1065 // would raise exceptions.
1066 mmio_region_write32(usbdev->base_addr, buffer_offset, d);
1067 }
1068 }
1069
1070 return kDifOk;
1071}
1072
1073dif_result_t dif_usbdev_buffer_raw_read(const dif_usbdev_t *usbdev, uint8_t id,
1074 uint8_t *dst, size_t dst_len) {
1075 if (usbdev == NULL || dst == NULL || misalignment32_of((uintptr_t)dst) ||
1076 dst_len > USBDEV_BUFFER_ENTRY_SIZE_BYTES) {
1077 return kDifBadArg;
1078 }
1079
1080 // We're reading from the start of the packet buffer.
1081 ptrdiff_t buffer_offset = (ptrdiff_t)get_buffer_addr(id, 0U);
1082 const uint32_t *restrict ewd = (uint32_t *)(dst + (dst_len & ~15u));
1083 uint32_t *restrict wd = (uint32_t *)dst;
1084
1085 // Transfer blocks of 4 x 32-bit words at a time; use the mmio_ routines for
1086 // compliance and to operate correctly with the DIF mocks, although this
1087 // results in transfers taking 50% longer because of the additional addressing
1088 // arithmetic and increased loop overheads.
1089 while (wd < ewd) {
1090 wd[0] = mmio_region_read32(usbdev->base_addr, buffer_offset);
1091 wd[1] = mmio_region_read32(usbdev->base_addr, buffer_offset + 4);
1092 wd[2] = mmio_region_read32(usbdev->base_addr, buffer_offset + 8);
1093 wd[3] = mmio_region_read32(usbdev->base_addr, buffer_offset + 12);
1094 buffer_offset += 16;
1095 wd += 4;
1096 }
1097 dst_len &= 15u;
1098
1099 if (dst_len) {
1100 // Remaining whole words
1101 ewd = wd + (dst_len >> 2);
1102 while (wd < ewd) {
1103 *wd++ = mmio_region_read32(usbdev->base_addr, buffer_offset);
1104 buffer_offset += 4;
1105 }
1106 dst_len &= 3u;
1107 if (dst_len) {
1108 // Remaining individual bytes
1109 // Note: we can only perform full 32-bit reads from the packet buffer.
1110 uint8_t *restrict bd = (uint8_t *)wd;
1111 uint32_t d = mmio_region_read32(usbdev->base_addr, buffer_offset);
1112 bd[0] = (uint8_t)d;
1113 if (dst_len > 1) {
1114 bd[1] = (uint8_t)(d >> 8);
1115 if (dst_len > 2) {
1116 bd[2] = (uint8_t)(d >> 16);
1117 }
1118 }
1119 }
1120 }
1121
1122 return kDifOk;
1123}
OSZAR »