First upload. Untested software

This commit is contained in:
2026-04-08 17:02:29 +02:00
commit 9b83763318
11 changed files with 698 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
# Makefile for Amiga Joystick and Mouse Emulation on Milk-V Duo
# Baremetal Implementation (Targets SRAM at 0x0E000000)
#/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
#* March 2026 - Anders Holck
#* This software has no license. If you choose to use, please include my name :)
#*/
CROSS_COMPILE = riscv64-unknown-elf-
CC = $(CROSS_COMPILE)gcc
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
# Baremetal flags
CFLAGS = -march=rv64gc -mabi=lp64d -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -ffreestanding -O2 -T linker.ld
LDFLAGS = -static -nostdlib -T linker.ld
TARGET = amiga_joystick_mouse.elf
BIN = amiga_joystick_mouse.bin
SRCS = startup.s main.c amiga_hw.c usb_dwc2.c
all: $(BIN)
$(TARGET): $(SRCS)
$(CC) $(CFLAGS) -o $@ $^
$(BIN): $(TARGET)
$(OBJCOPY) -O binary $< $@
clean:
rm -f *.o $(TARGET) $(BIN)
.PHONY: all clean
+5
View File
@@ -0,0 +1,5 @@
Amiga Mouse + Joystick to USB adapter.
Using a CVITEK CV1800B (milk-v duo 64MB) Risc-V microcomputer/controller.
A level shifter is required to convert 3.3V to 5V. Joystick buttons and mouse buttons can be skipped.
This software has no license. If you choose to use, please include my name :)
+77
View File
@@ -0,0 +1,77 @@
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
* March 2026 - Anders Holck
* This software has no license. If you choose to use, please include my name :)
*/
#include "amiga_hw.h"
#include "usb_dwc2.h"
#define GPIOA_EXT_PORTA 0x03020050
// GPIOA Bit Mappings (From PINS.md)
#define BIT_JOY1_FIRE 28
#define BIT_JOY1_UP 29
#define BIT_JOY1_DOWN 14
#define BIT_JOY1_LEFT 15
#define BIT_JOY1_RIGHT 16
#define BIT_JOY2_FIRE 17
#define BIT_JOY2_UP 18
#define BIT_JOY2_DOWN 19
#define BIT_JOY2_LEFT 20
#define BIT_JOY2_RIGHT 21
#define BIT_MOUSE_H_P 22
#define BIT_MOUSE_V_P 23
#define BIT_MOUSE_H_D 24
#define BIT_MOUSE_V_D 25
#define BIT_MOUSE_BT1 26
#define BIT_MOUSE_BT2 27
static uint8_t last_h_pulse = 0;
static uint8_t last_v_pulse = 0;
void amiga_hw_poll(amiga_input_t *input) {
uint32_t val = read32(GPIOA_EXT_PORTA);
// 1. Joystick 1 (Active Low - Amiga pulls to GND)
input->joy1 = 0;
if (!(val & (1 << BIT_JOY1_UP))) input->joy1 |= (1 << 0);
if (!(val & (1 << BIT_JOY1_DOWN))) input->joy1 |= (1 << 1);
if (!(val & (1 << BIT_JOY1_LEFT))) input->joy1 |= (1 << 2);
if (!(val & (1 << BIT_JOY1_RIGHT))) input->joy1 |= (1 << 3);
if (!(val & (1 << BIT_JOY1_FIRE))) input->joy1 |= (1 << 4);
// 2. Joystick 2
input->joy2 = 0;
if (!(val & (1 << BIT_JOY2_UP))) input->joy2 |= (1 << 0);
if (!(val & (1 << BIT_JOY2_DOWN))) input->joy2 |= (1 << 1);
if (!(val & (1 << BIT_JOY2_LEFT))) input->joy2 |= (1 << 2);
if (!(val & (1 << BIT_JOY2_RIGHT))) input->joy2 |= (1 << 3);
if (!(val & (1 << BIT_JOY2_FIRE))) input->joy2 |= (1 << 4);
// 3. Mouse Buttons
input->buttons = 0;
if (!(val & (1 << BIT_MOUSE_BT1))) input->buttons |= (1 << 0);
if (!(val & (1 << BIT_MOUSE_BT2))) input->buttons |= (1 << 1);
// 4. Mouse Quadrature Decoding (Simplified)
// Amiga Mouse uses 2 pulses per axis (Clock/Data or Phase A/B)
// Here we treat H-Pulse and V-Pulse as "clocks" and H-Dir/V-Dir as "direction"
uint8_t curr_h_pulse = (val >> BIT_MOUSE_H_P) & 1;
uint8_t curr_v_pulse = (val >> BIT_MOUSE_V_P) & 1;
input->mouse_x = 0;
if (curr_h_pulse != last_h_pulse) {
uint8_t h_dir = (val >> BIT_MOUSE_H_D) & 1;
input->mouse_x = (h_dir == curr_h_pulse) ? 1 : -1;
last_h_pulse = curr_h_pulse;
}
input->mouse_y = 0;
if (curr_v_pulse != last_v_pulse) {
uint8_t v_dir = (val >> BIT_MOUSE_V_D) & 1;
input->mouse_y = (v_dir == curr_v_pulse) ? 1 : -1;
last_v_pulse = curr_v_pulse;
}
}
+21
View File
@@ -0,0 +1,21 @@
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
* March 2026 - Anders Holck
* This software has no license. If you choose to use, please include my name :)
*/
#ifndef AMIGA_HW_H
#define AMIGA_HW_H
#include <stdint.h>
typedef struct {
int8_t mouse_x;
int8_t mouse_y;
uint8_t buttons; // Bit 0: Mouse Left, Bit 1: Mouse Right
uint8_t joy1; // Bits: 0:Up, 1:Down, 2:Left, 3:Right, 4:Fire
uint8_t joy2; // Bits: 0:Up, 1:Down, 2:Left, 3:Right, 4:Fire
} amiga_input_t;
void amiga_hw_poll(amiga_input_t *input);
#endif /* AMIGA_HW_H */
+53
View File
@@ -0,0 +1,53 @@
/* linker.ld - Linker script for Milk-V Duo Baremetal (SRAM) */
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
* March 2026 - Anders Holck
* This software has no license. If you choose to use, please include my name :)
*/
OUTPUT_ARCH("riscv")
ENTRY(_start)
MEMORY
{
/* Internal SRAM is 128KB or more, starting at 0x0E000000 */
ram (rwx) : ORIGIN = 0x0E000000, LENGTH = 128K
}
SECTIONS
{
/* Read-only code and constants */
.text :
{
*(.text.startup)
*(.text)
*(.text.*)
} > ram
.rodata :
{
*(.rodata)
*(.rodata.*)
} > ram
/* Read-write initialized data */
.data :
{
*(.data)
*(.data.*)
} > ram
/* Read-write zero-initialized data */
.bss :
{
. = ALIGN(8);
_bss_start = .;
*(.bss)
*(.bss.*)
*(COMMON)
. = ALIGN(8);
_bss_end = .;
} > ram
_end = .;
}
+80
View File
@@ -0,0 +1,80 @@
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
* March 2026 - Anders Holck
* This software has no license. If you choose to use, please include my name :)
*/
#include <stdint.h>
#include "usb_dwc2.h"
#include "pinmux.h"
#include "amiga_hw.h"
void delay(int count) {
for (volatile int i = 0; i < count; i++);
}
void usb_init_baremetal() {
// 1. Enable USB Clocks
uint32_t clk_en = read32(CLK_EN_1);
clk_en |= (1 << 13) | (1 << 14);
write32(CLK_EN_1, clk_en);
// 2. Power on PHY and release reset
write32(USB_PHY_CTRL, 0x07);
delay(10000);
// 3. Global Reset of DWC2 Core
write32(GRSTCTL, (1 << 0)); // Soft Reset
while (!(read32(GRSTCTL) & (1 << 29)));
delay(10000);
// 4. Force Device Mode and Basic Config
write32(GUSBCFG, (1 << 30) | (1 << 29)); // Force Device, PHY Interface
write32(DCFG, 0x0); // Full Speed
write32(GINTMSK, (1 << 12) | (1 << 13) | (1 << 4) | (1 << 18)); // Reset, Enum, RXFLVL, IEPInt
// 5. Connect
write32(DCTL, 0); // Clear Soft Disconnect
}
int main() {
pinmux_init_amiga();
usb_init_baremetal();
amiga_input_t input;
uint8_t hid_report[4] = {0}; // 2 bytes buttons, 2 bytes mouse
uint32_t report_timer = 0;
while (1) {
// 1. Poll Amiga Hardware
amiga_hw_poll(&input);
// 2. Poll USB State Machine
dwc2_poll();
// 3. Send HID Report every ~10ms (approximate)
if (++report_timer > 100) {
report_timer = 0;
// Format HID Report (Match hid_report_desc in usb_descriptors.h)
// Byte 0: Joy1 (5 bits) + Joy2 (3 bits)
// Byte 1: Joy2 (remaining 2 bits) + Padding
// Byte 2: Mouse X
// Byte 3: Mouse Y
hid_report[0] = input.joy1 | ((input.joy2 & 0x07) << 5);
hid_report[1] = (input.joy2 >> 3) & 0x03;
if (input.buttons & 0x01) hid_report[1] |= (1 << 2); // Mouse B1
if (input.buttons & 0x02) hid_report[1] |= (1 << 3); // Mouse B2
hid_report[2] = (uint8_t)input.mouse_x;
hid_report[3] = (uint8_t)input.mouse_y;
// Send via Endpoint 1 (Interrupt IN)
dwc2_send_packet(1, hid_report, 4);
}
delay(100);
}
return 0;
}
+58
View File
@@ -0,0 +1,58 @@
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
* March 2026 - Anders Holck
* This software has no license. If you choose to use, please include my name :)
*/
#ifndef PINMUX_H
#define PINMUX_H
#include "usb_dwc2.h"
#define PINMUX_BASE 0x03001000
/* Pinmux offsets for GPIOA 14-29 */
#define PINMUX_GPIOA14 0x050
#define PINMUX_GPIOA15 0x054
#define PINMUX_GPIOA16 0x058
#define PINMUX_GPIOA17 0x05C
#define PINMUX_GPIOA18 0x060
#define PINMUX_GPIOA19 0x064
#define PINMUX_GPIOA20 0x068
#define PINMUX_GPIOA21 0x06C
#define PINMUX_GPIOA22 0x070
#define PINMUX_GPIOA23 0x074
#define PINMUX_GPIOA24 0x078
#define PINMUX_GPIOA25 0x07C
#define PINMUX_GPIOA26 0x080
#define PINMUX_GPIOA27 0x084
#define PINMUX_GPIOA28 0x088
#define PINMUX_GPIOA29 0x08C
/* Function Mode for GPIO (usually Mode 0 or 3) */
#define MUX_MODE_GPIO 0
static inline void pinmux_config(uint32_t offset, uint32_t mode) {
write32(PINMUX_BASE + offset, mode);
}
static inline void pinmux_init_amiga() {
// Config GP0-GP15 as GPIO
pinmux_config(PINMUX_GPIOA28, MUX_MODE_GPIO); // GP0
pinmux_config(PINMUX_GPIOA29, MUX_MODE_GPIO); // GP1
pinmux_config(PINMUX_GPIOA14, MUX_MODE_GPIO); // GP2
pinmux_config(PINMUX_GPIOA15, MUX_MODE_GPIO); // GP3
pinmux_config(PINMUX_GPIOA16, MUX_MODE_GPIO); // GP4
pinmux_config(PINMUX_GPIOA17, MUX_MODE_GPIO); // GP5
pinmux_config(PINMUX_GPIOA18, MUX_MODE_GPIO); // GP6
pinmux_config(PINMUX_GPIOA19, MUX_MODE_GPIO); // GP7
pinmux_config(PINMUX_GPIOA20, MUX_MODE_GPIO); // GP8
pinmux_config(PINMUX_GPIOA21, MUX_MODE_GPIO); // GP9
pinmux_config(PINMUX_GPIOA22, MUX_MODE_GPIO); // GP10
pinmux_config(PINMUX_GPIOA23, MUX_MODE_GPIO); // GP11
pinmux_config(PINMUX_GPIOA24, MUX_MODE_GPIO); // GP12
pinmux_config(PINMUX_GPIOA25, MUX_MODE_GPIO); // GP13
pinmux_config(PINMUX_GPIOA26, MUX_MODE_GPIO); // GP14
pinmux_config(PINMUX_GPIOA27, MUX_MODE_GPIO); // GP15
}
#endif /* PINMUX_H */
+32
View File
@@ -0,0 +1,32 @@
# startup.s - Baremetal startup code for Milk-V Duo
#/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
#* March 2026 - Anders Holck
#* This software has no license. If you choose to use, please include my name :)
#*/
.section .text.startup
.global _start
_start:
# 1. Disable interrupts
csrw mie, zero
# 2. Set up stack pointer (end of SRAM)
li sp, 0x0E000000 + 128*1024
# 3. Clear BSS
la t0, _bss_start
la t1, _bss_end
bss_clear_loop:
bgeu t0, t1, bss_clear_done
sd zero, 0(t0)
addi t0, t0, 8
j bss_clear_loop
bss_clear_done:
j main
.section .text
# The main function will be in amiga_joystick_mouse.s
+125
View File
@@ -0,0 +1,125 @@
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
* March 2026 - Anders Holck
* This software has no license. If you choose to use, please include my name :)
*/
#ifndef USB_DESCRIPTORS_H
#define USB_DESCRIPTORS_H
#include <stdint.h>
// USB Standard Request Codes
#define USB_REQ_GET_STATUS 0x00
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_DESCRIPTOR 0x07
#define USB_REQ_GET_CONFIGURATION 0x08
#define USB_REQ_SET_CONFIGURATION 0x09
// Descriptor Types
#define USB_DESC_DEVICE 0x01
#define USB_DESC_CONFIGURATION 0x02
#define USB_DESC_STRING 0x03
#define USB_DESC_INTERFACE 0x04
#define USB_DESC_ENDPOINT 0x05
#define USB_DESC_HID 0x21
#define USB_DESC_REPORT 0x22
// Device Descriptor
const uint8_t device_desc[] = {
18, // bLength
USB_DESC_DEVICE, // bDescriptorType
0x00, 0x02, // bcdUSB (2.0)
0x00, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
64, // bMaxPacketSize0
0x6B, 0x1D, // idVendor (0x1D6B - Linux Foundation)
0x04, 0x01, // idProduct (0x0104)
0x00, 0x01, // bcdDevice
1, // iManufacturer
2, // iProduct
3, // iSerialNumber
1 // bNumConfigurations
};
// HID Report Descriptor (Combined Gamepad and Mouse)
const uint8_t hid_report_desc[] = {
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
// Joystick 1 & 2 Buttons (10 buttons total)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (Button 1)
0x29, 0x0A, // Usage Maximum (Button 10)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0A, // Report Count (10)
0x81, 0x02, // Input (Data, Var, Abs)
// Padding for buttons (6 bits)
0x75, 0x01, // Report Size (1)
0x95, 0x06, // Report Count (6)
0x81, 0x03, // Input (Cnst, Var, Abs)
// Mouse X/Y (Relative)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8)
0x95, 0x02, // Report Count (2)
0x81, 0x06, // Input (Data, Var, Rel)
0xC0, // End Collection
0xC0 // End Collection
};
// Configuration Descriptor
const uint8_t config_desc[] = {
9, // bLength
USB_DESC_CONFIGURATION,
34, 0, // wTotalLength (9+9+9+7)
1, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0x80, // bmAttributes (Bus Powered)
250, // bMaxPower (500mA)
// Interface Descriptor
9, // bLength
USB_DESC_INTERFACE,
0, // bInterfaceNumber
0, // bAlternateSetting
1, // bNumEndpoints
0x03, // bInterfaceClass (HID)
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0, // iInterface
// HID Descriptor
9, // bLength
USB_DESC_HID,
0x11, 0x01, // bcdHID (1.11)
0x00, // bCountryCode
1, // bNumDescriptors
USB_DESC_REPORT, // bDescriptorType
sizeof(hid_report_desc), 0, // wItemLength
// Endpoint Descriptor (IN 1)
7, // bLength
USB_DESC_ENDPOINT,
0x81, // bEndpointAddress (IN 1)
0x03, // bmAttributes (Interrupt)
8, 0, // wMaxPacketSize (8 bytes)
10 // bInterval (10ms)
};
#endif /* USB_DESCRIPTORS_H */
+150
View File
@@ -0,0 +1,150 @@
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
* March 2026 - Anders Holck
* This software has no license. If you choose to use, please include my name :)
*/
#include "usb_dwc2.h"
#include "usb_descriptors.h"
// Endpoint 0 State
#define EP0_IDLE 0
#define EP0_SETUP 1
#define EP0_DATA_IN 2
#define EP0_DATA_OUT 3
#define EP0_STATUS_IN 4
#define EP0_STATUS_OUT 5
static uint8_t ep0_state = EP0_IDLE;
static uint8_t usb_address = 0;
// Setup Packet Structure
typedef struct {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} usb_setup_t;
void dwc2_setup_fifo() {
// Total FIFO size is often 512 or 1024 words.
// Configure RX FIFO (Shared)
write32(GRXFSIZ, 128); // 128 words
// Configure TX FIFO 0 (EP0 IN)
// Offset = GRXFSIZ
write32(GNPTXFSIZ, (128 << 16) | 128); // Size 128, Start 128
// Configure TX FIFO 1 (EP1 IN - HID)
// Offset = GRXFSIZ + GNPTXFSIZ
write32(USB_DWC2_BASE + 0x104, (128 << 16) | 256); // Size 128, Start 256
}
void dwc2_send_packet(uint8_t ep_num, const uint8_t *data, uint32_t len) {
uint32_t fifo_addr = USB_DWC2_BASE + 0x1000 + (ep_num * 0x1000);
// 1. Set Transfer Size and Packet Count
uint32_t dieptsiz = (1 << 19) | len; // 1 packet, 'len' bytes
write32(USB_DWC2_BASE + 0x908 + (ep_num * 0x20), dieptsiz);
// 2. Enable Endpoint and Clear NAK
uint32_t diepctl = read32(USB_DWC2_BASE + 0x900 + (ep_num * 0x20));
diepctl |= (1 << 31) | (1 << 26); // EPEna, Cnak
write32(USB_DWC2_BASE + 0x900 + (ep_num * 0x20), diepctl);
// 3. Write data to FIFO (Word-aligned writes)
uint32_t words = (len + 3) / 4;
uint32_t *data32 = (uint32_t *)data;
for (uint32_t i = 0; i < words; i++) {
write32(fifo_addr, data32[i]);
}
}
void dwc2_handle_setup(usb_setup_t *setup) {
if ((setup->bmRequestType & 0x60) == 0) { // Standard Request
switch (setup->bRequest) {
case USB_REQ_GET_DESCRIPTOR: {
uint8_t type = setup->wValue >> 8;
const uint8_t *ptr = 0;
uint32_t len = 0;
if (type == USB_DESC_DEVICE) {
ptr = device_desc;
len = sizeof(device_desc);
} else if (type == USB_DESC_CONFIGURATION) {
ptr = config_desc;
len = sizeof(config_desc);
} else if (type == USB_DESC_REPORT) {
ptr = hid_report_desc;
len = sizeof(hid_report_desc);
}
if (ptr) {
if (len > setup->wLength) len = setup->wLength;
dwc2_send_packet(0, ptr, len);
ep0_state = EP0_DATA_IN;
}
break;
}
case USB_REQ_SET_ADDRESS:
usb_address = setup->wValue & 0x7F;
// Address is applied after the status phase
dwc2_send_packet(0, 0, 0); // ZLP Status
ep0_state = EP0_STATUS_IN;
break;
case USB_REQ_SET_CONFIGURATION:
dwc2_send_packet(0, 0, 0); // ZLP Status
ep0_state = EP0_STATUS_IN;
break;
}
}
}
void dwc2_poll() {
uint32_t gintsts = read32(GINTSTS);
// 1. USB Reset
if (gintsts & (1 << 12)) {
write32(GINTSTS, (1 << 12));
usb_address = 0;
ep0_state = EP0_IDLE;
dwc2_setup_fifo();
// Enable EP0 OUT
write32(DOEPCTL0, (1 << 31) | (1 << 26)); // EPEna, Cnak
}
// 2. Enumeration Done
if (gintsts & (1 << 13)) {
write32(GINTSTS, (1 << 13));
// High speed or Full speed detected...
}
// 3. Receive FIFO Level (Data available)
if (gintsts & (1 << 4)) {
uint32_t grxstsp = read32(GRXSTSP);
uint8_t ep_num = grxstsp & 0xF;
uint32_t bcnt = (grxstsp >> 4) & 0x7FF;
uint8_t pktsts = (grxstsp >> 17) & 0xF;
if (pktsts == 6) { // SETUP Packet Received
usb_setup_t setup;
uint32_t *setup_ptr = (uint32_t *)&setup;
uint32_t fifo_addr = USB_DWC2_BASE + 0x1000;
setup_ptr[0] = read32(fifo_addr);
setup_ptr[1] = read32(fifo_addr);
dwc2_handle_setup(&setup);
}
}
// 4. Update Address after Status Phase
if (ep0_state == EP0_STATUS_IN) {
uint32_t daint = read32(DAINT);
if (daint & (1 << 0)) { // EP0 IN Complete
uint32_t dcfg = read32(DCFG);
dcfg &= ~(0x7F << 4);
dcfg |= (usb_address << 4);
write32(DCFG, dcfg);
ep0_state = EP0_IDLE;
}
}
}
+63
View File
@@ -0,0 +1,63 @@
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
* March 2026 - Anders Holck
* This software has no license. If you choose to use, please include my name :)
*/
#ifndef USB_DWC2_H
#define USB_DWC2_H
#include <stdint.h>
/* System Control Registers */
#define SYS_CTRL_BASE 0x03000000
#define CLK_EN_1 (SYS_CTRL_BASE + 0x2004)
#define USB_PHY_CTRL (SYS_CTRL_BASE + 0x7000)
/* USB DWC2 Register Base */
#define USB_DWC2_BASE 0x04340000
/* Global Registers */
#define GOTGCTL (USB_DWC2_BASE + 0x000)
#define GOTGINT (USB_DWC2_BASE + 0x004)
#define GAHBCFG (USB_DWC2_BASE + 0x008)
#define GUSBCFG (USB_DWC2_BASE + 0x00C)
#define GRSTCTL (USB_DWC2_BASE + 0x010)
#define GINTSTS (USB_DWC2_BASE + 0x014)
#define GINTMSK (USB_DWC2_BASE + 0x018)
#define GRXSTSR (USB_DWC2_BASE + 0x01C)
#define GRXSTSP (USB_DWC2_BASE + 0x020)
#define GRXFSIZ (USB_DWC2_BASE + 0x024)
#define GNPTXFSIZ (USB_DWC2_BASE + 0x028)
#define GSNPSID (USB_DWC2_BASE + 0x040)
#define GHWCFG1 (USB_DWC2_BASE + 0x044)
#define GHWCFG2 (USB_DWC2_BASE + 0x048)
#define GHWCFG3 (USB_DWC2_BASE + 0x04C)
#define GHWCFG4 (USB_DWC2_BASE + 0x050)
/* Device Registers */
#define DCFG (USB_DWC2_BASE + 0x800)
#define DCTL (USB_DWC2_BASE + 0x804)
#define DSTS (USB_DWC2_BASE + 0x808)
#define DIEPMSK (USB_DWC2_BASE + 0x810)
#define DOEPMSK (USB_DWC2_BASE + 0x814)
#define DAINT (USB_DWC2_BASE + 0x818)
#define DAINTMSK (USB_DWC2_BASE + 0x81C)
/* Endpoints (EP0 is at 0x900/0xB00) */
#define DIEPCTL0 (USB_DWC2_BASE + 0x900)
#define DOEPCTL0 (USB_DWC2_BASE + 0xB00)
/* Register Helpers */
static inline void write32(uintptr_t addr, uint32_t val) {
*(volatile uint32_t *)addr = val;
}
static inline uint32_t read32(uintptr_t addr) {
return *(volatile uint32_t *)addr;
}
/* Driver Functions */
void dwc2_poll();
void dwc2_send_packet(uint8_t ep_num, const uint8_t *data, uint32_t len);
#endif /* USB_DWC2_H */