summaryrefslogtreecommitdiff
path: root/src/portable/raspberrypi/rp2040/rp2040_usb.h
blob: f4e85764d5eb6992e50b02b98d050f79c63c8058 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/*
 * SPDX-FileCopyrightText: Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 * SPDX-FileCopyrightText: Copyright (c) 2020 Ha Thach (tinyusb.org)
 * SPDX-License-Identifier: MIT
 *
 * This file is part of the TinyUSB stack.
 */

#ifndef RP2040_COMMON_H_
#define RP2040_COMMON_H_

#include "pico.h"
#include "hardware/structs/usb.h"
#include "hardware/irq.h"
#include "hardware/resets.h"
#include "hardware/timer.h"

#include "pico/critical_section.h"

#include "common/tusb_common.h"
#include "osal/osal.h"
#include "common/tusb_fifo.h"

#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE)
  #error TinyUSB device and host mode not supported at the same time
#endif

#if defined(PICO_RP2040) && PICO_RP2040 == 1
  // RP2040-E2 USB device endpoint abort is not cleared.
  #define CFG_TUSB_RP2_ERRATA_E2 1

  // RP2040-E4: USB host writes to upper half of buffer status in single buffered mode.
  #define CFG_TUSB_RP2_ERRATA_E4 1

  // RP2040-E5: USB device fails to exit RESET state on busy USB bus.
  #if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX)
    #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
  #endif

  // RP2040-E15: USB Device controller will hang if certain bus errors occur during an IN transfer.
  #ifndef CFG_TUSB_RP2_ERRATA_E15
    #if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX)
      #define CFG_TUSB_RP2_ERRATA_E15 (CFG_TUD_ENABLED && PICO_RP2040_USB_DEVICE_UFRAME_FIX)
    #elif defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX)
      #define CFG_TUSB_RP2_ERRATA_E15 (CFG_TUD_ENABLED && TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX)
    #endif
  #endif
#endif

#ifndef CFG_TUSB_RP2_ERRATA_E2
  #define CFG_TUSB_RP2_ERRATA_E2 0
#endif

#ifndef CFG_TUSB_RP2_ERRATA_E4
  #define CFG_TUSB_RP2_ERRATA_E4 0
#endif

#ifndef TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
  #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX 0
#endif

#ifndef CFG_TUSB_RP2_ERRATA_E15
  #define CFG_TUSB_RP2_ERRATA_E15 0
#endif

#if CFG_TUSB_RP2_ERRATA_E15
  #undef PICO_RP2040_USB_FAST_IRQ
  #define PICO_RP2040_USB_FAST_IRQ 1
#endif

#ifndef PICO_RP2040_USB_FAST_IRQ
  #define PICO_RP2040_USB_FAST_IRQ 0
#endif

#if PICO_RP2040_USB_FAST_IRQ
  #define __tusb_irq_path_func(x) __no_inline_not_in_flash_func(x)
#else
  #define __tusb_irq_path_func(x) x
#endif

// Flags we set by default in sie_ctrl (we add other bits on top)
enum {
  SIE_CTRL_BASE      = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS,
  SIE_CTRL_BASE_MASK = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS | USB_SIE_CTRL_SOF_EN_BITS |
                       USB_SIE_CTRL_KEEP_ALIVE_EN_BITS
};

//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
#define usb_hw_set    ((usb_hw_t *) hw_set_alias_untyped(usb_hw))
#define usb_hw_clear  ((usb_hw_t *) hw_clear_alias_untyped(usb_hw))

#define pico_info(...)  TU_LOG(2, __VA_ARGS__)
#define pico_trace(...) TU_LOG(3, __VA_ARGS__)

enum {
  EPSTATE_IDLE = 0,
  EPSTATE_ACTIVE,
  EPSTATE_PENDING,
  EPSTATE_PENDING_SETUP
};

// Hardware information per endpoint
typedef struct hw_endpoint {
  uint8_t ep_addr;
  uint8_t next_pid;
  uint8_t state;

#if CFG_TUD_EDPT_DEDICATED_HWFIFO
  bool is_xfer_fifo; // transfer using fifo
#endif

#if CFG_TUD_ENABLED
  uint8_t future_bufid; // which buffer holds next data
  uint8_t future_len;   // next data len
#endif

#if CFG_TUSB_RP2_ERRATA_E15
  bool e15_bulk_in; // Errata15 device bulk in
#endif

#if CFG_TUH_ENABLED
  uint8_t dev_addr;
  uint8_t interrupt_num; // 1-15 for interrupt endpoints
  struct TU_ATTR_PACKED {
    uint8_t transfer_type : 2;
    uint8_t need_pre      : 1; // preamble for low-speed device behind full speed hub
  };
#endif

  uint16_t max_packet_size; // max packet size also indicates configured
  uint8_t *dpram_buf;       // Buffer pointer in usb dpram

  // transfer info
  union {
    uint8_t   *user_buf; // User buffer in main memory
    tu_fifo_t *user_fifo;
  };
  uint16_t remaining_len;
  uint16_t xferred_len;

} hw_endpoint_t;

#if CFG_TUSB_RP2_ERRATA_E15
extern volatile uint32_t e15_last_sof;
#endif

void rp2usb_init(void);

// if usb hardware is in host mode
TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) {
  return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false;
}

extern critical_section_t rp2usb_lock;

TU_ATTR_ALWAYS_INLINE static inline void rp2usb_critical_enter(void) {
  critical_section_enter_blocking(&rp2usb_lock);
}
TU_ATTR_ALWAYS_INLINE static inline void rp2usb_critical_exit(void) {
  critical_section_exit(&rp2usb_lock);
}

//--------------------------------------------------------------------+
// Hardware Endpoint
//--------------------------------------------------------------------+
void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff,
                       uint16_t total_len);
bool rp2usb_xfer_continue(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id, bool is_rx);
void rp2usb_buffer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, bool is_rx);
void rp2usb_reset_transfer(hw_endpoint_t *ep);


TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint *ep, __unused int delta) {
  // todo add critsec as necessary to prevent issues between worker and IRQ...
  //  note that this is perhaps as simple as disabling IRQs because it would make
  //  sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
}

//--------------------------------------------------------------------+
// Hardware Buffer
//--------------------------------------------------------------------+
void bufctrl_write32(io_rw_32 *buf_reg, uint32_t value);
void bufctrl_write16(io_rw_16 *buf_reg16, uint16_t value);
uint16_t bufctrl_prepare16(hw_endpoint_t *ep, uint8_t *dpram_buf, bool is_rx);

TU_ATTR_ALWAYS_INLINE static inline uintptr_t hw_data_offset(uint8_t *buf) {
  // Remove usb base from buffer pointer
  return (uintptr_t)buf ^ (uintptr_t)usb_dpram;
}

#endif