diff options
Diffstat (limited to 'src/atsam/sam3_usb.c')
-rw-r--r-- | src/atsam/sam3_usb.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/atsam/sam3_usb.c b/src/atsam/sam3_usb.c new file mode 100644 index 00000000..d9101478 --- /dev/null +++ b/src/atsam/sam3_usb.c @@ -0,0 +1,229 @@ +// Hardware interface to USB on SAM3X +// +// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include <string.h> // NULL +#include "board/usb_cdc.h" // usb_notify_ep0 +#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN +#include "internal.h" // UOTGHS +#include "sched.h" // DECL_INIT + +#define EP_SIZE(s) ((s)==64 ? UOTGHS_DEVEPTCFG_EPSIZE_64_BYTE : \ + ((s)==32 ? UOTGHS_DEVEPTCFG_EPSIZE_32_BYTE : \ + ((s)==16 ? UOTGHS_DEVEPTCFG_EPSIZE_16_BYTE : \ + UOTGHS_DEVEPTCFG_EPSIZE_8_BYTE))) + +#define usb_fifo(ep) ((void*)UOTGHS_RAM_ADDR + ((ep) * 0x8000)) + +static void +usb_write_packet(uint32_t ep, const uint8_t *data, uint32_t len) +{ + uint8_t *dest = usb_fifo(ep); + while (len--) + *dest++ = *data++; +} + +static void +usb_read_packet(uint32_t ep, uint8_t *data, uint32_t len) +{ + uint8_t *src = usb_fifo(ep); + while (len--) + *data++ = *src++; +} + +int_fast8_t +usb_read_bulk_out(void *data, uint_fast8_t max_len) +{ + uint32_t eps = UOTGHS->UOTGHS_DEVEPTISR[USB_CDC_EP_BULK_OUT]; + if (!(eps & UOTGHS_DEVEPTISR_RXOUTI)) { + // No data ready + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << USB_CDC_EP_BULK_OUT; + return -1; + } + uint32_t len = (eps&UOTGHS_DEVEPTISR_BYCT_Msk) >> UOTGHS_DEVEPTISR_BYCT_Pos; + usb_read_packet(USB_CDC_EP_BULK_OUT, data, len > max_len ? max_len : len); + UOTGHS->UOTGHS_DEVEPTICR[USB_CDC_EP_BULK_OUT] = UOTGHS_DEVEPTICR_RXOUTIC; + UOTGHS->UOTGHS_DEVEPTIDR[USB_CDC_EP_BULK_OUT] = UOTGHS_DEVEPTIDR_FIFOCONC; + return len; +} + +int_fast8_t +usb_send_bulk_in(void *data, uint_fast8_t len) +{ + uint32_t eps = UOTGHS->UOTGHS_DEVEPTISR[USB_CDC_EP_BULK_IN]; + if (!(eps & UOTGHS_DEVEPTISR_TXINI)) { + // Buffer full + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << USB_CDC_EP_BULK_IN; + return -1; + } + usb_write_packet(USB_CDC_EP_BULK_IN, data, len); + UOTGHS->UOTGHS_DEVEPTICR[USB_CDC_EP_BULK_IN] = UOTGHS_DEVEPTICR_TXINIC; + UOTGHS->UOTGHS_DEVEPTIDR[USB_CDC_EP_BULK_IN] = UOTGHS_DEVEPTIDR_FIFOCONC; + return len; +} + +int_fast8_t +usb_read_ep0(void *data, uint_fast8_t max_len) +{ + uint32_t eps = UOTGHS->UOTGHS_DEVEPTISR[0]; + if (eps & UOTGHS_DEVEPTISR_RXSTPI) + return -2; + if (!(eps & UOTGHS_DEVEPTISR_RXOUTI)) { + // Not ready to receive data + UOTGHS->UOTGHS_DEVEPTIER[0] = UOTGHS_DEVEPTIER_RXOUTES; + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << 0; + return -1; + } + usb_read_packet(0, data, max_len); + if (UOTGHS->UOTGHS_DEVEPTISR[0] & UOTGHS_DEVEPTISR_RXSTPI) + return -2; + UOTGHS->UOTGHS_DEVEPTICR[0] = UOTGHS_DEVEPTICR_RXOUTIC; + return max_len; +} + +int_fast8_t +usb_read_ep0_setup(void *data, uint_fast8_t max_len) +{ + uint32_t eps = UOTGHS->UOTGHS_DEVEPTISR[0]; + if (!(eps & UOTGHS_DEVEPTISR_RXSTPI)) { + // No data ready to read + UOTGHS->UOTGHS_DEVEPTIDR[0] = (UOTGHS_DEVEPTIDR_TXINEC + | UOTGHS_DEVEPTIDR_RXOUTEC); + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << 0; + return -1; + } + usb_read_packet(0, data, max_len); + UOTGHS->UOTGHS_DEVEPTICR[0] = (UOTGHS_DEVEPTICR_RXSTPIC + | UOTGHS_DEVEPTICR_RXOUTIC); + return max_len; +} + +int_fast8_t +usb_send_ep0(const void *data, uint_fast8_t len) +{ + uint32_t eps = UOTGHS->UOTGHS_DEVEPTISR[0]; + if (eps & (UOTGHS_DEVEPTISR_RXSTPI | UOTGHS_DEVEPTISR_RXOUTI)) + return -2; + if (!(eps & UOTGHS_DEVEPTISR_TXINI)) { + // Not ready to send + UOTGHS->UOTGHS_DEVEPTIER[0] = (UOTGHS_DEVEPTIER_TXINES + | UOTGHS_DEVEPTIER_RXOUTES); + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << 0; + return -1; + } + usb_write_packet(0, data, len); + UOTGHS->UOTGHS_DEVEPTICR[0] = UOTGHS_DEVEPTICR_TXINIC; + return len; +} + +void +usb_stall_ep0(void) +{ + UOTGHS->UOTGHS_DEVEPTIER[0] = UOTGHS_DEVEPTIER_STALLRQS; + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << 0; +} + +static uint8_t set_address; + +void +usb_set_address(uint_fast8_t addr) +{ + set_address = addr | UOTGHS_DEVCTRL_ADDEN; + usb_send_ep0(NULL, 0); + UOTGHS->UOTGHS_DEVEPTIER[0] = UOTGHS_DEVEPTIER_TXINES; + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << 0; +} + +void +usb_set_configure(void) +{ + UOTGHS->UOTGHS_DEVEPT = ((UOTGHS_DEVEPT_EPEN0 << 0) + | (UOTGHS_DEVEPT_EPEN0 << USB_CDC_EP_BULK_OUT) + | (UOTGHS_DEVEPT_EPEN0 << USB_CDC_EP_BULK_IN) + | (UOTGHS_DEVEPT_EPEN0 << USB_CDC_EP_ACM)); + + UOTGHS->UOTGHS_DEVEPTCFG[USB_CDC_EP_BULK_OUT] = ( + EP_SIZE(USB_CDC_EP_BULK_OUT_SIZE) + | UOTGHS_DEVEPTCFG_EPTYPE_BLK | UOTGHS_DEVEPTCFG_EPBK_2_BANK + | UOTGHS_DEVEPTCFG_ALLOC); + UOTGHS->UOTGHS_DEVEPTIER[USB_CDC_EP_BULK_OUT] = UOTGHS_DEVEPTIER_RXOUTES; + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << USB_CDC_EP_BULK_OUT; + + UOTGHS->UOTGHS_DEVEPTCFG[USB_CDC_EP_BULK_IN] = ( + EP_SIZE(USB_CDC_EP_BULK_IN_SIZE) | UOTGHS_DEVEPTCFG_EPDIR_IN + | UOTGHS_DEVEPTCFG_EPTYPE_BLK | UOTGHS_DEVEPTCFG_EPBK_2_BANK + | UOTGHS_DEVEPTCFG_ALLOC); + UOTGHS->UOTGHS_DEVEPTIER[USB_CDC_EP_BULK_IN] = UOTGHS_DEVEPTIER_TXINES; + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << USB_CDC_EP_BULK_IN; + + UOTGHS->UOTGHS_DEVEPTCFG[USB_CDC_EP_ACM] = ( + EP_SIZE(USB_CDC_EP_ACM_SIZE) | UOTGHS_DEVEPTCFG_EPDIR_IN + | UOTGHS_DEVEPTCFG_EPTYPE_INTRPT | UOTGHS_DEVEPTCFG_EPBK_2_BANK + | UOTGHS_DEVEPTCFG_ALLOC); +} + +// Configure endpoint 0 after usb reset completes +static void +handle_end_reset(void) +{ + UOTGHS->UOTGHS_DEVEPT = UOTGHS_DEVEPT_EPEN0 << 0; + UOTGHS->UOTGHS_DEVEPTCFG[0] = ( + EP_SIZE(USB_CDC_EP0_SIZE) + | UOTGHS_DEVEPTCFG_EPTYPE_CTRL | UOTGHS_DEVEPTCFG_EPBK_1_BANK + | UOTGHS_DEVEPTCFG_ALLOC); + UOTGHS->UOTGHS_DEVEPTIER[0] = UOTGHS_DEVEPTIER_RXSTPES; + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_PEP_0 << 0; + + UOTGHS->UOTGHS_DEVICR = UOTGHS_DEVICR_EORSTC; +} + +void +usbserial_init(void) +{ + // Setup USB clock + enable_pclock(ID_UOTGHS); + PMC->CKGR_UCKR = CKGR_UCKR_UPLLCOUNT(3) | CKGR_UCKR_UPLLEN; + while (!(PMC->PMC_SR & PMC_SR_LOCKU)) + ; + PMC->PMC_USB = PMC_USB_USBS | PMC_USB_USBDIV(0); + PMC->PMC_SCER = PMC_SCER_UOTGCLK; + + // Enable USB + UOTGHS->UOTGHS_CTRL = (UOTGHS_CTRL_UIMOD | UOTGHS_CTRL_OTGPADE + | UOTGHS_CTRL_USBE); + UOTGHS->UOTGHS_DEVCTRL = UOTGHS_DEVCTRL_SPDCONF_FORCED_FS; + + // Enable interrupts + NVIC_SetPriority(UOTGHS_IRQn, 1); + NVIC_EnableIRQ(UOTGHS_IRQn); + UOTGHS->UOTGHS_DEVIER = UOTGHS_DEVIER_EORSTES; +} +DECL_INIT(usbserial_init); + +void __visible +UOTGHS_Handler(void) +{ + uint32_t s = UOTGHS->UOTGHS_DEVISR; + if (s & UOTGHS_DEVISR_EORST) { + handle_end_reset(); + return; + } + UOTGHS->UOTGHS_DEVIDR = s; + if (s & (UOTGHS_DEVISR_PEP_0 << 0)) { + usb_notify_ep0(); + + if (set_address + && (UOTGHS->UOTGHS_DEVEPTISR[0] & UOTGHS_DEVEPTISR_TXINI)) { + // Ack from set_address command sent - now update address + UOTGHS->UOTGHS_DEVCTRL = (set_address + | UOTGHS_DEVCTRL_SPDCONF_FORCED_FS); + set_address = 0; + } + } + if (s & (UOTGHS_DEVISR_PEP_0 << USB_CDC_EP_BULK_OUT)) + usb_notify_bulk_out(); + if (s & (UOTGHS_DEVISR_PEP_0 << USB_CDC_EP_BULK_IN)) + usb_notify_bulk_in(); +} |