summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZixun LI <[email protected]>2026-07-19 22:20:09 +0200
committerGitHub <[email protected]>2026-07-19 22:20:09 +0200
commit2555891761f771bd87504574c48096eb2d07c985 (patch)
treec8d936281847d5fd9e7185fe898ea1538a6367bf /src
parent52e6ab7704ce2e57f41f70d6ba2bc5470c4b56c7 (diff)
parentb00b40da28285f67efd652e20cfe3877d42d2ff3 (diff)
Merge pull request #3694 from rhgndf/py32f0
Puya PY32F07x support
Diffstat (limited to 'src')
-rw-r--r--src/common/tusb_mcu.h11
-rw-r--r--src/portable/mentor/musb/dcd_musb.c63
-rw-r--r--src/portable/mentor/musb/musb_max32.h1
-rw-r--r--src/portable/mentor/musb/musb_py32.h79
-rw-r--r--src/portable/mentor/musb/musb_ti.h1
-rw-r--r--src/portable/mentor/musb/musb_type.h71
-rw-r--r--src/tusb_option.h7
7 files changed, 223 insertions, 10 deletions
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index 0959ac3a9..d599f89fb 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -703,6 +703,17 @@
#define TU_ATTR_FAST_FUNC __attribute__((section(".fast")))
+//--------------------------------------------------------------------+
+// Puya
+//--------------------------------------------------------------------+
+#elif TU_CHECK_MCU(OPT_MCU_PY32F0)
+ #define TUP_USBIP_MUSB
+ #define TUP_USBIP_MUSB_PY32
+ #define TUP_DCD_ENDPOINT_MAX 6
+ // PY32 shares the buffer between IN and OUT of the same endpoint number.
+ // Possible to share IN/OUT if only one direction is armed at any one time
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
+
#endif
// External USB controller
diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c
index 17993f23a..92c9fe92e 100644
--- a/src/portable/mentor/musb/dcd_musb.c
+++ b/src/portable/mentor/musb/dcd_musb.c
@@ -24,6 +24,8 @@
#include "musb_ti.h"
#elif defined(TUP_USBIP_MUSB_ADI)
#include "musb_max32.h"
+#elif defined(TUP_USBIP_MUSB_PY32)
+ #include "musb_py32.h"
#else
#error "Unsupported MCU"
#endif
@@ -45,6 +47,7 @@ typedef struct {
};
uint16_t length; /* the number of bytes in the buffer */
uint16_t remaining; /* the number of bytes remaining in the buffer */
+ uint16_t mps; /* maximum packet size */
bool armed; /* true while a transfer is posted */
bool use_fifo; /* true: buf is tu_fifo_t*; false: buf is plain byte pointer. */
} pipe_state_t;
@@ -159,6 +162,14 @@ TU_ATTR_ALWAYS_INLINE static inline pipe_state_t* pipe_get(uint8_t epnum, tusb_d
return &_dcd.pipe[idx];
}
+TU_ATTR_ALWAYS_INLINE static inline uint16_t musb_mps_to_maxp(uint16_t mps) {
+#if defined(TUP_USBIP_MUSB_PY32)
+ return (uint8_t) ((mps + 7u) / 8u);
+#else
+ return mps;
+#endif
+}
+
//--------------------------------------------------------------------
// HW FIFO Helper
// Note: Index register is already set by caller
@@ -223,7 +234,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsign
bool double_packet) {
(void) mps;
- #if defined(TUP_USBIP_MUSB_ADI)
+ #if defined(TUP_USBIP_MUSB_PY32)
+ (void) musb; (void) is_rx; (void) double_packet;
+ // Puya FIFO sizes: EP0 = 64 B, EP1 = 512 B, EP2..4 = 128 B, EP5 = 64 B, shared between IN and OUT.
+ static const uint16_t py32_fifo_size[] = { 64, 512, 128, 128, 128, 64 };
+ return epnum < TU_ARRAY_SIZE(py32_fifo_size) && mps <= py32_fifo_size[epnum];
+ #elif defined(TUP_USBIP_MUSB_ADI)
// AnalogDevice FIFO sizes: EP1..7 = 512 B, EP8..9 = 2048 B, EP10..11 = 4096 B.
// DPB requires FIFO >= 2 * MPS. For HS bulk (MPS=512) only EP >= 8 qualifies.
// Force single-buffered on EP < 8 even if the caller requested DPB.
@@ -266,7 +282,7 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_flush(musb_regs_t* musb, unsigne
// write to txfifo using pipe_state_t info
static void pipe_write(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) {
musb_ep_csr_t* ep_csr = &musb_regs->indexed_csr;
- const uint16_t mps = ep_csr->tx_maxp & MUSB_TXMAXP_PACKET_SIZE_M;
+ const uint16_t mps = pipe->mps;
const uint16_t xact_len = tu_min16(mps, pipe->remaining);
volatile void *hwfifo = &musb_regs->fifo[epnum];
if (xact_len) {
@@ -320,7 +336,7 @@ static void process_epin_isr(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epn
// release the FIFO slot by clearing RXRDY. return true if short packet
static bool pipe_read(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) {
musb_ep_csr_t* ep_csr = &musb_regs->indexed_csr; // index already set in process_epout_isr()
- const uint16_t mps = ep_csr->rx_maxp & MUSB_RXMAXP_PACKET_SIZE_M;
+ const uint16_t mps = pipe->mps;
const uint16_t rx_count = ep_csr->rx_count;
const uint16_t xact_len = tu_min16(tu_min16(pipe->remaining, mps), rx_count);
volatile void *hwfifo = &musb_regs->fifo[epnum];
@@ -632,7 +648,11 @@ static void process_bus_reset_isr(uint8_t rhport) {
hwfifo_reset(musb, i, 0);
hwfifo_reset(musb, i, 1);
}
+#if defined(TUP_USBIP_MUSB_PY32)
+ dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+#else
dcd_event_bus_reset(rhport, (musb->power & MUSB_POWER_HSMODE) ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL, true);
+#endif
}
/*------------------------------------------------------------------
@@ -641,6 +661,11 @@ static void process_bus_reset_isr(uint8_t rhport) {
#if CFG_TUSB_DEBUG >= MUSB_DEBUG
static void print_musb_info(musb_regs_t* musb_regs) {
+#if defined(TUP_USBIP_MUSB_PY32)
+ (void) musb_regs;
+ // musb discovery fields not present
+ TU_LOG1("musb py32 fixed full-speed configuration\r\n");
+#else
// print version, epinfo, raminfo, config_data0, fifo_size
TU_LOG1("musb version = %u.%u\r\n", musb_regs->hwvers_bit.major, musb_regs->hwvers_bit.minor);
TU_LOG1("Number of endpoints: %u TX, %u RX\r\n", musb_regs->epinfo_bit.tx_ep_num, musb_regs->epinfo_bit.rx_ep_num);
@@ -657,6 +682,7 @@ static void print_musb_info(musb_regs_t* musb_regs) {
TU_LOG1("FIFO %u Size: TX %u RX %u\r\n", i, musb_regs->indexed_csr.fifo_size_bit.tx, musb_regs->indexed_csr.fifo_size_bit.rx);
}
#endif
+#endif
}
#endif
@@ -712,6 +738,18 @@ void dcd_remote_wakeup(uint8_t rhport) {
musb_regs->power &= ~MUSB_POWER_RESUME;
}
+#if defined(TUP_USBIP_MUSB_PY32)
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+}
+#else
+
// Connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
@@ -727,6 +765,8 @@ void dcd_disconnect(uint8_t rhport)
musb_regs->power &= ~MUSB_POWER_SOFTCONN;
}
+#endif
+
void dcd_sof_enable(uint8_t rhport, bool en)
{
(void) rhport;
@@ -750,6 +790,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
pipe->buf = NULL;
pipe->length = 0;
pipe->remaining = 0;
+ pipe->mps = (uint16_t) mps;
pipe->armed = false;
musb_regs_t* musb = MUSB_REGS(rhport);
@@ -757,7 +798,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
const uint8_t is_rx = (1 - epdir);
musb_ep_maxp_csr_t* maxp_csr = &ep_csr->maxp_csr[is_rx];
- maxp_csr->maxp = mps;
+ maxp_csr->maxp = musb_mps_to_maxp((uint16_t) mps);
maxp_csr->csrh = 0;
#if MUSB_CFG_SHARED_FIFO
if (epdir) {
@@ -768,7 +809,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
hwfifo_flush(musb, epn, is_rx, true);
TU_ASSERT(hwfifo_config(musb, epn, is_rx, mps, ep_desc->bmAttributes.xfer == TUSB_XFER_BULK));
- musb->intren_ep[is_rx] |= TU_BIT(epn);
+ musb->intren_ep[is_rx ^ MUSB_INTR_EP_TX_RX_SWAP] |= TU_BIT(epn);
return true;
}
@@ -779,8 +820,11 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
musb_regs_t* musb = MUSB_REGS(rhport);
musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn);
const uint8_t is_rx = 1 - dir_in;
+ pipe_state_t *pipe = pipe_get(epn, dir_in);
+ pipe->mps = largest_packet_size;
ep_csr->maxp_csr[is_rx].csrh = 0;
- return hwfifo_config(musb, epn, is_rx, largest_packet_size, true);
+ TU_ASSERT(hwfifo_config(musb, epn, is_rx, largest_packet_size, true));
+ return true;
}
bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc ) {
@@ -796,6 +840,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc )
pipe->buf = NULL;
pipe->length = 0;
pipe->remaining = 0;
+ pipe->mps = (uint16_t) mps;
pipe->armed = false;
musb_regs_t* musb = MUSB_REGS(rhport);
@@ -803,7 +848,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc )
const uint8_t is_rx = 1 - dir_in;
musb_ep_maxp_csr_t* maxp_csr = &ep_csr->maxp_csr[is_rx];
- maxp_csr->maxp = mps;
+ maxp_csr->maxp = musb_mps_to_maxp((uint16_t) mps);
maxp_csr->csrh |= MUSB_CSRH_ISO;
#if MUSB_CFG_SHARED_FIFO
if (dir_in) {
@@ -818,7 +863,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc )
musb->fifo_size[is_rx] = hwfifo_byte2size(mps) | MUSB_FIFOSZ_DOUBLE_PACKET;
#endif
- musb->intren_ep[is_rx] |= TU_BIT(epn);
+ musb->intren_ep[is_rx ^ MUSB_INTR_EP_TX_RX_SWAP] |= TU_BIT(epn);
if (ie) musb_dcd_int_enable(rhport);
@@ -839,7 +884,7 @@ void dcd_edpt_close_all(uint8_t rhport)
musb_ep_maxp_csr_t* maxp_csr = &ep_csr->maxp_csr[d];
hwfifo_flush(musb, i, d, true);
hwfifo_reset(musb, i, d);
- maxp_csr->maxp = 0;
+ maxp_csr->maxp = musb_mps_to_maxp(0);
maxp_csr->csrh = 0;
}
}
diff --git a/src/portable/mentor/musb/musb_max32.h b/src/portable/mentor/musb/musb_max32.h
index 628454216..9cf99126e 100644
--- a/src/portable/mentor/musb/musb_max32.h
+++ b/src/portable/mentor/musb/musb_max32.h
@@ -28,6 +28,7 @@ extern "C" {
#define MUSB_CFG_SHARED_FIFO 1 // shared FIFO for TX and RX endpoints
#define MUSB_CFG_DYNAMIC_FIFO 0 // dynamic EP FIFO sizing
+#define MUSB_INTR_EP_TX_RX_SWAP 0
static const uintptr_t MUSB_BASES[] = { MXC_BASE_USBHS };
diff --git a/src/portable/mentor/musb/musb_py32.h b/src/portable/mentor/musb/musb_py32.h
new file mode 100644
index 000000000..68d58ccb8
--- /dev/null
+++ b/src/portable/mentor/musb/musb_py32.h
@@ -0,0 +1,79 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026, Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef TUSB_MUSB_PY32_H_
+#define TUSB_MUSB_PY32_H_
+
+#include "py32f0xx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// PY32 does not expose generic MUSB shared FIFO allocation registers; this
+// selects the existing TX_MODE path required for IN endpoints.
+#define MUSB_CFG_SHARED_FIFO 1
+#define MUSB_CFG_DYNAMIC_FIFO 0
+#define MUSB_INTR_EP_TX_RX_SWAP 1
+
+static const uintptr_t MUSB_BASES[] = { USBD_BASE };
+static const IRQn_Type musb_irqs[] = { USB_IRQn };
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_phy_init(uint8_t rhport) {
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+
+ musb_regs->index = 0;
+ musb_regs->faddr = 0;
+ musb_regs->intr_usben = MUSB_IE_RESET | MUSB_IE_RESUME | MUSB_IE_SUSPND;
+ musb_regs->intr_txen = TU_BIT(0);
+ musb_regs->intr_rxen = 0;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_enable(uint8_t rhport) {
+ NVIC_EnableIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_disable(uint8_t rhport) {
+ NVIC_DisableIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_clear(uint8_t rhport) {
+ NVIC_ClearPendingIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline unsigned musb_dcd_get_int_enable(uint8_t rhport) {
+ return NVIC_GetEnableIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_handler_enter(uint8_t rhport) {
+ (void) rhport;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/portable/mentor/musb/musb_ti.h b/src/portable/mentor/musb/musb_ti.h
index db2e237d2..1cf26b1cb 100644
--- a/src/portable/mentor/musb/musb_ti.h
+++ b/src/portable/mentor/musb/musb_ti.h
@@ -30,6 +30,7 @@
#define MUSB_CFG_SHARED_FIFO 0
#define MUSB_CFG_DYNAMIC_FIFO 1
#define MUSB_CFG_DYNAMIC_FIFO_SIZE 4096
+#define MUSB_INTR_EP_TX_RX_SWAP 0
static const uintptr_t MUSB_BASES[] = { USB0_BASE };
diff --git a/src/portable/mentor/musb/musb_type.h b/src/portable/mentor/musb/musb_type.h
index 3c079cb25..1e8a761c0 100644
--- a/src/portable/mentor/musb/musb_type.h
+++ b/src/portable/mentor/musb/musb_type.h
@@ -64,6 +64,75 @@
#define __R volatile const
#endif
+#if defined(TUP_USBIP_MUSB_PY32)
+
+typedef struct TU_ATTR_PACKED {
+ __IO uint8_t csrh; // 0x04, 0x08: CSRH
+ __IO uint8_t csrl; // 0x05, 0x09: CSRL
+ __IO uint8_t maxp; // 0x06, 0x0A: MAXP
+ __I uint8_t reserved;
+} musb_ep_maxp_csr_t;
+
+TU_VERIFY_STATIC(sizeof(musb_ep_maxp_csr_t) == 4, "size is not correct");
+
+typedef struct TU_ATTR_PACKED {
+ __IO uint8_t csr0l; // 0x00: CSR0
+ __IO uint8_t count0; // 0x01: COUNT0
+ __I uint8_t reserved_0x02[2];
+
+ union {
+ struct {
+ __IO uint8_t tx_csrh; // 0x04: TX CSRH
+ __IO uint8_t tx_csrl; // 0x05: TX CSRL
+ __IO uint8_t tx_maxp; // 0x06: TX MAXP
+ __I uint8_t reserved_0x07;
+
+ __IO uint8_t rx_csrh; // 0x08: RX CSRH
+ __IO uint8_t rx_csrl; // 0x09: RX CSRL
+ __IO uint8_t rx_maxp; // 0x0A: RX MAXP
+ __I uint8_t reserved_0x0b;
+ };
+
+ musb_ep_maxp_csr_t maxp_csr[2];
+ };
+
+ __IO uint16_t rx_count; // 0x0C: RX COUNT
+ __I uint8_t reserved_0x0e[2];
+} musb_ep_csr_t;
+
+TU_VERIFY_STATIC(sizeof(musb_ep_csr_t) == 16, "size is not correct");
+
+typedef struct TU_ATTR_PACKED {
+ __IO uint8_t faddr; // 0x00: FADDR
+ __IO uint8_t power; // 0x01: POWER
+ __I uint8_t reserved_0x02[2];
+
+ __IO uint8_t intr_usb; // 0x04: INTRUSB
+ __IO uint8_t intr_rx; // 0x05: INTRRX
+ __IO uint8_t intr_tx; // 0x06: INTRTX
+ __I uint8_t reserved_0x07;
+
+ __IO uint8_t intr_usben; // 0x08: INTRUSBEN
+ union {
+ struct {
+ __IO uint8_t intr_rxen; // 0x09: INTRRXEN
+ __IO uint8_t intr_txen; // 0x0A: INTRTXEN
+ };
+
+ __IO uint8_t intren_ep[2]; // 0x09-0x0A: RX, TX
+ };
+ __I uint8_t reserved_0x0b;
+
+ __IO uint16_t frame; // 0x0C: FRAME
+ __IO uint8_t index; // 0x0E: INDEX
+ __I uint8_t reserved_0x0f;
+
+ musb_ep_csr_t indexed_csr; // 0x10-0x1F: Indexed CSR
+ __IO uint32_t fifo[16]; // 0x20-0x5F: FIFO 0-15
+} musb_regs_t;
+
+#else
+
typedef struct TU_ATTR_PACKED {
__IO uint16_t maxp; // 0x00, 0x04: MAXP
__IO uint8_t csrl; // 0x02, 0x06: CSRL
@@ -277,6 +346,8 @@ typedef struct {
TU_VERIFY_STATIC(sizeof(musb_regs_t) == 0x350, "size is not correct");
+#endif
+
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 75c8ff93d..b5e910fca 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -204,6 +204,9 @@
// HPMicro
#define OPT_MCU_HPM 2600 ///< HPMicro
+// Puya
+#define OPT_MCU_PY32F0 2700 ///< Puya PY32F0
+
// Check if configured MCU is one of listed
// Apply TU_MCU_IS_EQUAL with || as separator to list of input
#define TU_MCU_IS_EQUAL(_m) (CFG_TUSB_MCU == (_m))
@@ -400,7 +403,9 @@
#if defined(TUP_USBIP_MUSB)
#define CFG_TUD_EDPT_DEDICATED_HWFIFO 1
#define CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE 4 // 32 bit data
- #define CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS // allow odd 16bit access
+ #if !defined(TUP_USBIP_MUSB_PY32)
+ #define CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS // allow odd 16bit access
+ #endif
#define CFG_TUSB_FIFO_HWFIFO_DATA_ODD_8BIT_ACCESS // allow odd 8bit access
#define CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE 0 // fixed hwfifo
#endif