Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/stm32/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ config HAVE_STM32_USBFS
config HAVE_STM32_USBOTG
bool
default y if MACH_STM32F2 || MACH_STM32F4 || MACH_STM32F7 || MACH_STM32H7
config STM32_USB_DOUBLE_BUFFER_TX
bool
default y
config HAVE_STM32_CANBUS
bool
default y if MACH_STM32F1 || MACH_STM32F2 || MACH_STM32F4x5 || MACH_STM32F446 || MACH_STM32F0x2
Expand Down
71 changes: 46 additions & 25 deletions src/stm32/usbfs.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Hardware interface to "fullspeed USB controller"
//
// Copyright (C) 2018-2023 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2018-2025 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

Expand All @@ -19,29 +19,27 @@
// Transfer memory is accessed with 32bits, but contains only 16bits of data
typedef volatile uint32_t epmword_t;
#define WSIZE 2
#define USBx_IRQn USB_LP_IRQn
#elif CONFIG_MACH_STM32F0 || CONFIG_MACH_STM32L4
#elif CONFIG_MACH_STM32F0 || CONFIG_MACH_STM32L4 || CONFIG_MACH_STM32G4
// Transfer memory is accessed with 16bits and contains 16bits of data
typedef volatile uint16_t epmword_t;
#define WSIZE 2
#define USBx_IRQn USB_IRQn
#elif CONFIG_MACH_STM32G4
// Transfer memory is accessed with 16bits and contains 16bits of data
typedef volatile uint16_t epmword_t;
#define WSIZE 2
#define USBx_IRQn USB_LP_IRQn
#elif CONFIG_MACH_STM32G0
// Transfer memory is accessed with 32bits and contains 32bits of data
typedef volatile uint32_t epmword_t;
#define WSIZE 4
#endif

// Different chips have different names for the USB irq
#if CONFIG_MACH_STM32F1 || CONFIG_MACH_STM32G4
#define USBx_IRQn USB_LP_IRQn
#elif CONFIG_MACH_STM32G0B1
#define USBx_IRQn USB_UCPD1_2_IRQn
#else
#define USBx_IRQn USB_IRQn
#endif

// The stm32g0 has slightly different register names
#if CONFIG_MACH_STM32G0
#if CONFIG_MACH_STM32G0B1
#define USB_IRQn USB_UCPD1_2_IRQn
#endif
#define USB USB_DRD_FS
#define USB_PMAADDR USB_DRD_PMAADDR
#define USB_EPADDR_FIELD USB_CHEP_ADDR
Expand All @@ -55,8 +53,8 @@

// Some chip variants do not define these fields
#ifndef USB_EP_DTOG_TX_Pos
#define USB_EP_DTOG_TX_Pos 6
#define USB_EP_DTOG_RX_Pos 14
#define USB_EP_DTOG_TX_Pos 6
#define USB_EP_DTOG_RX_Pos 14
#endif


Expand Down Expand Up @@ -244,8 +242,9 @@ usb_read_bulk_out(void *data, uint_fast8_t max_len)
static uint32_t bulk_in_push_pos, bulk_in_pop_flag;
#define BI_START 2

int_fast8_t
usb_send_bulk_in(void *data, uint_fast8_t len)
// Send bulk packet to host with double buffering optimization
static int_fast8_t
usb_send_bulk_in_double_buffer(void *data, uint_fast8_t len)
{
if (readl(&bulk_in_pop_flag))
// No buffer space available
Expand Down Expand Up @@ -278,6 +277,21 @@ usb_send_bulk_in(void *data, uint_fast8_t len)
return len;
}

// Send bulk usb packet to host
int_fast8_t
usb_send_bulk_in(void *data, uint_fast8_t len)
{
if (CONFIG_STM32_USB_DOUBLE_BUFFER_TX)
return usb_send_bulk_in_double_buffer(data, len);
uint32_t ep = USB_CDC_EP_BULK_IN, epr = USB_EPR[ep];
if ((epr & USB_EPTX_STAT) != USB_EP_TX_NAK)
// No buffer space available
return -1;
btable_write_packet(ep, BUFTX, data, len);
USB_EPR[ep] = calc_epr_bits(epr, USB_EPTX_STAT, USB_EP_TX_VALID);
return len;
}

int_fast8_t
usb_read_ep0(void *data, uint_fast8_t max_len)
{
Expand Down Expand Up @@ -336,9 +350,10 @@ usb_set_configure(void)
bulk_out_pop_count = 0;
USB_EPR[ep] = calc_epr_bits(USB_EPR[ep], USB_EPRX_STAT, USB_EP_RX_VALID);

ep = USB_CDC_EP_BULK_IN;
bulk_in_push_pos = BI_START;
writel(&bulk_in_pop_flag, 0);
if (CONFIG_STM32_USB_DOUBLE_BUFFER_TX) {
bulk_in_push_pos = BI_START;
writel(&bulk_in_pop_flag, 0);
}
}


Expand All @@ -363,9 +378,12 @@ usb_reset(void)
bulk_out_push_flag = USB_EP_DTOG_TX;

ep = USB_CDC_EP_BULK_IN;
USB_EPR[ep] = (USB_CDC_EP_BULK_IN | USB_EP_BULK | USB_EP_KIND
| USB_EP_TX_NAK);
bulk_in_pop_flag = USB_EP_DTOG_RX;
uint32_t bi_epr_flags = USB_CDC_EP_BULK_IN | USB_EP_BULK | USB_EP_TX_NAK;
if (CONFIG_STM32_USB_DOUBLE_BUFFER_TX) {
bi_epr_flags |= USB_EP_KIND;
bulk_in_pop_flag = USB_EP_DTOG_RX;
}
USB_EPR[ep] = bi_epr_flags;

USB->CNTR = USB_CNTR_CTRM | USB_CNTR_RESETM;
USB->DADDR = USB_DADDR_EF;
Expand All @@ -385,9 +403,12 @@ USB_IRQHandler(void)
bulk_out_push_flag = 0;
usb_notify_bulk_out();
} else if (ep == USB_CDC_EP_BULK_IN) {
USB_EPR[ep] = (calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0)
| bulk_in_pop_flag);
bulk_in_pop_flag = 0;
uint32_t ne = calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0);
if (CONFIG_STM32_USB_DOUBLE_BUFFER_TX) {
ne |= bulk_in_pop_flag;
bulk_in_pop_flag = 0;
}
USB_EPR[ep] = ne;
usb_notify_bulk_in();
} else if (ep == 0) {
USB_EPR[ep] = calc_epr_bits(epr, USB_EP_CTR_RX | USB_EP_CTR_TX, 0);
Expand Down
55 changes: 50 additions & 5 deletions src/stm32/usbotg.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Hardware interface to "USB OTG (on the go) controller" on stm32
//
// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2019-2025 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

Expand Down Expand Up @@ -119,6 +119,24 @@ fifo_write_packet(uint32_t ep, const uint8_t *src, uint32_t len)
return len;
}

// Write a packet to a tx fifo (optimized for already aligned data)
static int
fifo_write_packet_fast(uint32_t ep, const uint32_t *src, uint32_t len)
{
void *fifo = EPFIFO(ep);
USB_OTG_INEndpointTypeDef *epi = EPIN(ep);
uint32_t ctl = epi->DIEPCTL;
if (ctl & USB_OTG_DIEPCTL_EPENA)
return -1;
epi->DIEPINT = USB_OTG_DIEPINT_XFRC;
epi->DIEPTSIZ = len | (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos);
epi->DIEPCTL = ctl | USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;
uint32_t i;
for (i=0; i < DIV_ROUND_UP(len, sizeof(uint32_t)); i++)
writel(fifo, src[i]);
return 0;
}

// Read a packet from the rx queue
static int_fast8_t
fifo_read_packet(uint8_t *dest, uint_fast8_t max_len)
Expand Down Expand Up @@ -208,6 +226,12 @@ usb_read_bulk_out(void *data, uint_fast8_t max_len)
return ret;
}

// Storage for "bulk in" transmissions for a kind of manual "double buffering"
static struct {
uint32_t len;
uint32_t buf[USB_CDC_EP_BULK_IN_SIZE / sizeof(uint32_t)];
} TX_BUF;

int_fast8_t
usb_send_bulk_in(void *data, uint_fast8_t len)
{
Expand All @@ -218,11 +242,23 @@ usb_send_bulk_in(void *data, uint_fast8_t len)
usb_irq_enable();
return len;
}
if (ctl & USB_OTG_DIEPCTL_EPENA) {
// Wait for space to transmit
int dbuf_busy = CONFIG_STM32_USB_DOUBLE_BUFFER_TX && TX_BUF.len;
if (ctl & USB_OTG_DIEPCTL_EPENA || dbuf_busy) {
if (!CONFIG_STM32_USB_DOUBLE_BUFFER_TX || TX_BUF.len || !len) {
// Wait for space to transmit
OTGD->DAINTMSK |= 1 << USB_CDC_EP_BULK_IN;
usb_irq_enable();
return -1;
}
// Buffer next packet for transmission from irq handler
len = len > USB_CDC_EP_BULK_IN_SIZE ? USB_CDC_EP_BULK_IN_SIZE : len;
uint32_t blocks = DIV_ROUND_UP(len, sizeof(uint32_t));
TX_BUF.buf[blocks-1] = 0;
memcpy(TX_BUF.buf, data, len);
TX_BUF.len = len;
OTGD->DAINTMSK |= 1 << USB_CDC_EP_BULK_IN;
usb_irq_enable();
return -1;
return len;
}
int_fast8_t ret = fifo_write_packet(USB_CDC_EP_BULK_IN, data, len);
usb_irq_enable();
Expand Down Expand Up @@ -373,6 +409,8 @@ usb_set_configure(void)
| USB_OTG_GRSTCTL_TXFFLSH);
while (OTG->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH)
;
if (CONFIG_STM32_USB_DOUBLE_BUFFER_TX)
TX_BUF.len = 0;
usb_irq_enable();
}

Expand Down Expand Up @@ -401,8 +439,15 @@ OTG_FS_IRQHandler(void)
OTGD->DAINTMSK = msk & ~daint;
if (pend & (1 << 0))
usb_notify_ep0();
if (pend & (1 << USB_CDC_EP_BULK_IN))
if (pend & (1 << USB_CDC_EP_BULK_IN)) {
usb_notify_bulk_in();
if (CONFIG_STM32_USB_DOUBLE_BUFFER_TX && TX_BUF.len) {
int ret = fifo_write_packet_fast(USB_CDC_EP_BULK_IN
, TX_BUF.buf, TX_BUF.len);
if (!ret)
TX_BUF.len = 0;
}
}
}
}

Expand Down
Loading