Open FFBoard
Open source force feedback firmware
typec_stm32.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2023 Ha Thach (tinyusb.org)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include "tusb_option.h"
26#include "typec/tcd.h"
27
28#if CFG_TUC_ENABLED && defined(TUP_USBIP_TYPEC_STM32)
29
30#include "common/tusb_common.h"
31
32#if CFG_TUSB_MCU == OPT_MCU_STM32G4
33 #include "stm32g4xx.h"
34 #include "stm32g4xx_ll_dma.h" // for UCPD REQID
35#else
36 #error "Unsupported STM32 family"
37#endif
38
39//--------------------------------------------------------------------+
40//
41//--------------------------------------------------------------------+
42
43enum {
44 IMR_ATTACHED = UCPD_IMR_TXMSGDISCIE | UCPD_IMR_TXMSGSENTIE | UCPD_IMR_TXMSGABTIE | UCPD_IMR_TXUNDIE |
45 UCPD_IMR_RXHRSTDETIE | UCPD_IMR_RXOVRIE | UCPD_IMR_RXMSGENDIE | UCPD_IMR_RXORDDETIE |
46 UCPD_IMR_HRSTDISCIE | UCPD_IMR_HRSTSENTIE | UCPD_IMR_FRSEVTIE
47};
48
49#define PHY_SYNC1 0x18u
50#define PHY_SYNC2 0x11u
51#define PHY_SYNC3 0x06u
52#define PHY_RST1 0x07u
53#define PHY_RST2 0x19u
54#define PHY_EOP 0x0Du
55
56#define PHY_ORDERED_SET_SOP (PHY_SYNC1 | (PHY_SYNC1<<5u) | (PHY_SYNC1<<10u) | (PHY_SYNC2<<15u)) // SOP Ordered set coding
57#define PHY_ORDERED_SET_SOP_P (PHY_SYNC1 | (PHY_SYNC1<<5u) | (PHY_SYNC3<<10u) | (PHY_SYNC3<<15u)) // SOP' Ordered set coding
58#define PHY_ORDERED_SET_SOP_PP (PHY_SYNC1 | (PHY_SYNC3<<5u) | (PHY_SYNC1<<10u) | (PHY_SYNC3<<15u)) // SOP'' Ordered set coding
59#define PHY_ORDERED_SET_HARD_RESET (PHY_RST1 | (PHY_RST1<<5u) | (PHY_RST1<<10u) | (PHY_RST2<<15u )) // Hard Reset Ordered set coding
60#define PHY_ORDERED_SET_CABLE_RESET (PHY_RST1 | (PHY_SYNC1<<5u) | (PHY_RST1<<10u) | (PHY_SYNC3<<15u)) // Cable Reset Ordered set coding
61#define PHY_ORDERED_SET_SOP_P_DEBUG (PHY_SYNC1 | (PHY_RST2<<5u) | (PHY_RST2<<10u) | (PHY_SYNC3<<15u)) // SOP' Debug Ordered set coding
62#define PHY_ORDERED_SET_SOP_PP_DEBUG (PHY_SYNC1 | (PHY_RST2<<5u) | (PHY_SYNC3<<10u) | (PHY_SYNC2<<15u)) // SOP'' Debug Ordered set coding
63
64
65static uint8_t const* _rx_buf;
66static uint8_t const* _tx_pending_buf;
67static uint16_t _tx_pending_bytes;
68static uint16_t _tx_xferring_bytes;
69
72 .data_role = 0, // UFP
73 .specs_rev = PD_REV_20,
74 .power_role = 0, // Sink
75 .msg_id = 0,
76 .n_data_obj = 0,
77 .extended = 0
78};
79
80// address of DMA channel rx, tx for each port
81#define CFG_TUC_STM32_DMA { { DMA1_Channel1_BASE, DMA1_Channel2_BASE } }
82
83//--------------------------------------------------------------------+
84// DMA
85//--------------------------------------------------------------------+
86
87static const uint32_t _dma_addr_arr[TUP_TYPEC_RHPORTS_NUM][2] = CFG_TUC_STM32_DMA;
88
89TU_ATTR_ALWAYS_INLINE static inline uint32_t dma_get_addr(uint8_t rhport, bool is_rx) {
90 return _dma_addr_arr[rhport][is_rx ? 0 : 1];
91}
92
93static void dma_init(uint8_t rhport, bool is_rx) {
94 uint32_t dma_addr = dma_get_addr(rhport, is_rx);
95 DMA_Channel_TypeDef* dma_ch = (DMA_Channel_TypeDef*) dma_addr;
96 uint32_t req_id;
97
98 if (is_rx) {
99 // Peripheral -> Memory, Memory inc, 8-bit, High priority
100 dma_ch->CCR = DMA_CCR_MINC | DMA_CCR_PL_1;
101 dma_ch->CPAR = (uint32_t) &UCPD1->RXDR;
102
103 req_id = LL_DMAMUX_REQ_UCPD1_RX;
104 } else {
105 // Memory -> Peripheral, Memory inc, 8-bit, High priority
106 dma_ch->CCR = DMA_CCR_MINC | DMA_CCR_PL_1 | DMA_CCR_DIR;
107 dma_ch->CPAR = (uint32_t) &UCPD1->TXDR;
108
109 req_id = LL_DMAMUX_REQ_UCPD1_TX;
110 }
111
112 // find and set up mux channel TODO support mcu with multiple DMAMUXs
113 enum {
114 CH_DIFF = DMA1_Channel2_BASE - DMA1_Channel1_BASE
115 };
116 uint32_t mux_ch_num;
117
118 #ifdef DMA2_BASE
119 if (dma_addr > DMA2_BASE) {
120 mux_ch_num = 8 * ((dma_addr - DMA2_Channel1_BASE) / CH_DIFF);
121 } else
122 #endif
123 {
124 mux_ch_num = (dma_addr - DMA1_Channel1_BASE) / CH_DIFF;
125 }
126
127 DMAMUX_Channel_TypeDef* mux_ch = DMAMUX1_Channel0 + mux_ch_num;
128
129 uint32_t mux_ccr = mux_ch->CCR & ~(DMAMUX_CxCR_DMAREQ_ID);
130 mux_ccr |= req_id;
131 mux_ch->CCR = mux_ccr;
132}
133
134TU_ATTR_ALWAYS_INLINE static inline void dma_start(uint8_t rhport, bool is_rx, void const* buf, uint16_t len) {
135 DMA_Channel_TypeDef* dma_ch = (DMA_Channel_TypeDef*) dma_get_addr(rhport, is_rx);
136
137 dma_ch->CMAR = (uint32_t) buf;
138 dma_ch->CNDTR = len;
139 dma_ch->CCR |= DMA_CCR_EN;
140}
141
142TU_ATTR_ALWAYS_INLINE static inline void dma_stop(uint8_t rhport, bool is_rx) {
143 DMA_Channel_TypeDef* dma_ch = (DMA_Channel_TypeDef*) dma_get_addr(rhport, is_rx);
144 dma_ch->CCR &= ~DMA_CCR_EN;
145}
146
147TU_ATTR_ALWAYS_INLINE static inline bool dma_enabled(uint8_t rhport, bool is_rx) {
148 DMA_Channel_TypeDef* dma_ch = (DMA_Channel_TypeDef*) dma_get_addr(rhport, is_rx);
149 return dma_ch->CCR & DMA_CCR_EN;
150}
151
152TU_ATTR_ALWAYS_INLINE static inline void dma_tx_start(uint8_t rhport, void const* buf, uint16_t len) {
153 UCPD1->TX_ORDSET = PHY_ORDERED_SET_SOP;
154 UCPD1->TX_PAYSZ = len;
155 dma_start(rhport, false, buf, len);
156 UCPD1->CR |= UCPD_CR_TXSEND;
157}
158
159TU_ATTR_ALWAYS_INLINE static inline void dma_tx_stop(uint8_t rhport) {
160 dma_stop(rhport, false);
161}
162
163//--------------------------------------------------------------------+
164//
165//--------------------------------------------------------------------+
166
167bool tcd_init(uint8_t rhport, uint32_t port_type) {
168 (void) rhport;
169
170 // Init DMA for RX, TX
171 dma_init(rhport, true);
172 dma_init(rhport, false);
173
174 // Initialization phase: CFG1, detect all SOPs
175 UCPD1->CFG1 = (0x0d << UCPD_CFG1_HBITCLKDIV_Pos) | (0x10 << UCPD_CFG1_IFRGAP_Pos) | (0x07 << UCPD_CFG1_TRANSWIN_Pos) |
176 (0x01 << UCPD_CFG1_PSC_UCPDCLK_Pos) | (0x1f << UCPD_CFG1_RXORDSETEN_Pos);
177 UCPD1->CFG1 |= UCPD_CFG1_UCPDEN;
178
179 // General programming sequence (with UCPD configured then enabled)
180 if (port_type == TUSB_TYPEC_PORT_SNK) {
181 // Set analog mode enable both CC Phy
182 UCPD1->CR = (0x01 << UCPD_CR_ANAMODE_Pos) | (UCPD_CR_CCENABLE_0 | UCPD_CR_CCENABLE_1);
183
184 // Read Voltage State on CC1 & CC2 fore initial state
185 uint32_t v_cc[2];
186 (void) v_cc;
187 v_cc[0] = (UCPD1->SR >> UCPD_SR_TYPEC_VSTATE_CC1_Pos) & 0x03;
188 v_cc[1] = (UCPD1->SR >> UCPD_SR_TYPEC_VSTATE_CC2_Pos) & 0x03;
189 TU_LOG1("Initial VState CC1 = %lu, CC2 = %lu\r\n", v_cc[0], v_cc[1]);
190
191 // Enable CC1 & CC2 Interrupt
192 UCPD1->IMR = UCPD_IMR_TYPECEVT1IE | UCPD_IMR_TYPECEVT2IE;
193 }
194
195 // Disable dead battery in PWR's CR3
196 PWR->CR3 |= PWR_CR3_UCPD_DBDIS;
197
198 return true;
199}
200
201// Enable interrupt
202void tcd_int_enable (uint8_t rhport) {
203 (void) rhport;
204 NVIC_EnableIRQ(UCPD1_IRQn);
205}
206
207// Disable interrupt
208void tcd_int_disable(uint8_t rhport) {
209 (void) rhport;
210 NVIC_DisableIRQ(UCPD1_IRQn);
211}
212
213bool tcd_msg_receive(uint8_t rhport, uint8_t* buffer, uint16_t total_bytes) {
214 _rx_buf = buffer;
215 dma_start(rhport, true, buffer, total_bytes);
216 return true;
217}
218
219bool tcd_msg_send(uint8_t rhport, uint8_t const* buffer, uint16_t total_bytes) {
220 (void) rhport;
221
222 if (dma_enabled(rhport, false)) {
223 // DMA is busy, probably sending GoodCRC, save as pending TX
226 }else {
227 // DMA is free, start sending
228 _tx_pending_buf = NULL;
230
233 }
234
235 return true;
236}
237
238void tcd_int_handler(uint8_t rhport) {
239 (void) rhport;
240
241 uint32_t sr = UCPD1->SR;
242 sr &= UCPD1->IMR;
243
244 if (sr & (UCPD_SR_TYPECEVT1 | UCPD_SR_TYPECEVT2)) {
245 uint32_t v_cc[2];
246 v_cc[0] = (UCPD1->SR >> UCPD_SR_TYPEC_VSTATE_CC1_Pos) & 0x03;
247 v_cc[1] = (UCPD1->SR >> UCPD_SR_TYPEC_VSTATE_CC2_Pos) & 0x03;
248
249 TU_LOG3("VState CC1 = %lu, CC2 = %lu\r\n", v_cc[0], v_cc[1]);
250
251 uint32_t cr = UCPD1->CR;
252
253 // TODO only support SNK for now, required highest voltage for now
254 // Enable PHY on active CC and disable Rd on other CC
255 // FIXME somehow CC2 is vstate is not correct, always 1 even not attached.
256 // on DPOW1 board, it is connected to PA10 (USBPD_DBCC2), we probably miss something.
257 if ((sr & UCPD_SR_TYPECEVT1) && (v_cc[0] == 3)) {
258 TU_LOG3("Attach CC1\r\n");
259 cr &= ~(UCPD_CR_PHYCCSEL | UCPD_CR_CCENABLE);
260 cr |= UCPD_CR_PHYRXEN | UCPD_CR_CCENABLE_0;
261 } else if ((sr & UCPD_SR_TYPECEVT2) && (v_cc[1] == 3)) {
262 TU_LOG3("Attach CC2\r\n");
263 cr &= ~UCPD_CR_CCENABLE;
264 cr |= (UCPD_CR_PHYCCSEL | UCPD_CR_PHYRXEN | UCPD_CR_CCENABLE_1);
265 } else {
266 TU_LOG3("Detach\r\n");
267 cr &= ~UCPD_CR_PHYRXEN;
268 cr |= UCPD_CR_CCENABLE_0 | UCPD_CR_CCENABLE_1;
269 }
270
271 if (cr & UCPD_CR_PHYRXEN) {
272 // Attached
273 UCPD1->IMR |= IMR_ATTACHED;
274 UCPD1->CFG1 |= UCPD_CFG1_RXDMAEN | UCPD_CFG1_TXDMAEN;
275 }else {
276 // Detached
277 UCPD1->CFG1 &= ~(UCPD_CFG1_RXDMAEN | UCPD_CFG1_TXDMAEN);
278 UCPD1->IMR &= ~IMR_ATTACHED;
279 }
280
281 // notify stack
282 tcd_event_cc_changed(rhport, v_cc[0], v_cc[1], true);
283
284 UCPD1->CR = cr;
285
286 // ack
287 UCPD1->ICR = UCPD_ICR_TYPECEVT1CF | UCPD_ICR_TYPECEVT2CF;
288 }
289
290 //------------- RX -------------//
291 if (sr & UCPD_SR_RXORDDET) {
292 // SOP: Start of Packet.
293 TU_LOG3("SOP\r\n");
294 // UCPD1->RX_ORDSET & UCPD_RX_ORDSET_RXORDSET_Msk;
295
296 // ack
297 UCPD1->ICR = UCPD_ICR_RXORDDETCF;
298 }
299
300 // Received full message
301 if (sr & UCPD_SR_RXMSGEND) {
302 TU_LOG3("RX MSG END\r\n");
303
304 // stop TX
305 dma_stop(rhport, true);
306
307 uint8_t result;
308
309 if (!(sr & UCPD_SR_RXERR)) {
310 // response with good crc
311 // TODO move this to usbc stack
312 if (_rx_buf) {
313 _good_crc.msg_id = ((pd_header_t const *) _rx_buf)->msg_id;
314 dma_tx_start(rhport, &_good_crc, 2);
315 }
316
317 result = XFER_RESULT_SUCCESS;
318 }else {
319 // CRC failed
320 result = XFER_RESULT_FAILED;
321 }
322
323 // notify stack
324 tcd_event_rx_complete(rhport, UCPD1->RX_PAYSZ, result, true);
325
326 // ack
327 UCPD1->ICR = UCPD_ICR_RXMSGENDCF;
328 }
329
330 if (sr & UCPD_SR_RXOVR) {
331 TU_LOG3("RXOVR\r\n");
332 // ack
333 UCPD1->ICR = UCPD_ICR_RXOVRCF;
334 }
335
336 //------------- TX -------------//
337 // All tx events: complete and error
338 if (sr & (UCPD_SR_TXMSGSENT | (UCPD_SR_TXMSGDISC | UCPD_SR_TXMSGABT | UCPD_SR_TXUND))) {
339 // force TX stop
340 dma_tx_stop(rhport);
341
342 uint16_t const xferred_bytes = _tx_xferring_bytes - UCPD1->TX_PAYSZ;
343 uint8_t result;
344
345 if ( sr & UCPD_SR_TXMSGSENT ) {
346 TU_LOG3("TX MSG SENT\r\n");
347 result = XFER_RESULT_SUCCESS;
348 // ack
349 UCPD1->ICR = UCPD_ICR_TXMSGSENTCF;
350 }else {
351 TU_LOG3("TX Error\r\n");
352 result = XFER_RESULT_FAILED;
353 // ack
354 UCPD1->ICR = UCPD_SR_TXMSGDISC | UCPD_SR_TXMSGABT | UCPD_SR_TXUND;
355 }
356
357 // start pending TX if any
359 // Start the pending TX
361
362 // clear pending
363 _tx_pending_buf = NULL;
365 }
366
367 // notify stack
368 tcd_event_tx_complete(rhport, xferred_bytes, result, true);
369 }
370}
371
372#endif
uint16_t total_bytes
Definition: dcd_nuc505.c:113
uint8_t const * buffer
Definition: midi_device.h:100
@ PD_CTRL_GOOD_CRC
Definition: pd_types.h:54
@ PD_REV_20
Definition: pd_types.h:101
@ TUSB_TYPEC_PORT_SNK
Definition: pd_types.h:48
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint16_t msg_type
Definition: pd_types.h:116
uint16_t msg_id
Definition: pd_types.h:120
static TU_ATTR_ALWAYS_INLINE void tcd_event_tx_complete(uint8_t rhport, uint16_t xferred_bytes, uint8_t result, bool in_isr)
Definition: tcd.h:126
static TU_ATTR_ALWAYS_INLINE void tcd_event_cc_changed(uint8_t rhport, uint8_t cc1, uint8_t cc2, bool in_isr)
Definition: tcd.h:99
static TU_ATTR_ALWAYS_INLINE void tcd_event_rx_complete(uint8_t rhport, uint16_t xferred_bytes, uint8_t result, bool in_isr)
Definition: tcd.h:112
@ XFER_RESULT_FAILED
Definition: tusb_types.h:238
@ XFER_RESULT_SUCCESS
Definition: tusb_types.h:237
static TU_ATTR_ALWAYS_INLINE void dma_tx_stop(uint8_t rhport)
Definition: typec_stm32.c:159
void tcd_int_handler(uint8_t rhport)
Definition: typec_stm32.c:238
void tcd_int_disable(uint8_t rhport)
Definition: typec_stm32.c:208
static uint16_t _tx_pending_bytes
Definition: typec_stm32.c:67
static void dma_init(uint8_t rhport, bool is_rx)
Definition: typec_stm32.c:93
@ IMR_ATTACHED
Definition: typec_stm32.c:44
static TU_ATTR_ALWAYS_INLINE void dma_start(uint8_t rhport, bool is_rx, void const *buf, uint16_t len)
Definition: typec_stm32.c:134
static uint8_t const * _rx_buf
Definition: typec_stm32.c:65
static TU_ATTR_ALWAYS_INLINE bool dma_enabled(uint8_t rhport, bool is_rx)
Definition: typec_stm32.c:147
bool tcd_init(uint8_t rhport, uint32_t port_type)
Definition: typec_stm32.c:167
static uint16_t _tx_xferring_bytes
Definition: typec_stm32.c:68
static const uint32_t _dma_addr_arr[TUP_TYPEC_RHPORTS_NUM][2]
Definition: typec_stm32.c:87
bool tcd_msg_receive(uint8_t rhport, uint8_t *buffer, uint16_t total_bytes)
Definition: typec_stm32.c:213
static TU_ATTR_ALWAYS_INLINE uint32_t dma_get_addr(uint8_t rhport, bool is_rx)
Definition: typec_stm32.c:89
void tcd_int_enable(uint8_t rhport)
Definition: typec_stm32.c:202
static pd_header_t _good_crc
Definition: typec_stm32.c:70
static uint8_t const * _tx_pending_buf
Definition: typec_stm32.c:66
static TU_ATTR_ALWAYS_INLINE void dma_stop(uint8_t rhport, bool is_rx)
Definition: typec_stm32.c:142
bool tcd_msg_send(uint8_t rhport, uint8_t const *buffer, uint16_t total_bytes)
Definition: typec_stm32.c:219
static TU_ATTR_ALWAYS_INLINE void dma_tx_start(uint8_t rhport, void const *buf, uint16_t len)
Definition: typec_stm32.c:152