aboutsummaryrefslogtreecommitdiffstats
path: root/src/atsam/sam3_usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/atsam/sam3_usb.c')
-rw-r--r--src/atsam/sam3_usb.c229
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();
+}