aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2018-12-29 17:09:35 -0500
committerKevin O'Connor <kevin@koconnor.net>2019-01-07 19:34:31 -0500
commit02c558652f4f5a34655635d56e581ae03d9f42b7 (patch)
tree73fc26c7d45315da2b8aa72ed474b66ed3ec2ffc /src
parent75ad16ea1a3ffcb7026c69f6f1ea78197b055779 (diff)
downloadkutter-02c558652f4f5a34655635d56e581ae03d9f42b7.tar.gz
kutter-02c558652f4f5a34655635d56e581ae03d9f42b7.tar.xz
kutter-02c558652f4f5a34655635d56e581ae03d9f42b7.zip
sam3: Add USB support for the SAM4 "UDP" hardware
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src')
-rw-r--r--src/sam3/Kconfig5
-rw-r--r--src/sam3/Makefile1
-rw-r--r--src/sam3/sam4_usb.c238
-rw-r--r--src/sam3/sam4s_timer.c2
-rw-r--r--src/sam3/timer.c2
-rw-r--r--src/sam3/usb_cdc_ep.h10
6 files changed, 256 insertions, 2 deletions
diff --git a/src/sam3/Kconfig b/src/sam3/Kconfig
index e7085eaf..c2ebb200 100644
--- a/src/sam3/Kconfig
+++ b/src/sam3/Kconfig
@@ -37,7 +37,12 @@ config CLOCK_FREQ
default 15000000 if MACH_SAM4S8C # 120000000/8
default 60000000 if MACH_SAM4E8E # 120000000/2
+config USBSERIAL
+ depends on MACH_SAM4S8C || MACH_SAM4E8E
+ bool "Use USB for communication (instead of serial)"
+ default y
config SERIAL
+ depends on !USBSERIAL
bool
default y
config SERIAL_BAUD
diff --git a/src/sam3/Makefile b/src/sam3/Makefile
index 30361ed8..a9510717 100644
--- a/src/sam3/Makefile
+++ b/src/sam3/Makefile
@@ -28,6 +28,7 @@ CFLAGS_klipper.elf += $(eflags-y) --specs=nano.specs --specs=nosys.specs
src-y += sam3/main.c sam3/gpio.c sam3/i2c.c sam3/spi.c
src-y += generic/crc16_ccitt.c generic/alloc.c
src-y += generic/armcm_irq.c generic/timer_irq.c
+src-$(CONFIG_USBSERIAL) += sam3/sam4_usb.c generic/usb_cdc.c
src-$(CONFIG_SERIAL) += sam3/serial.c generic/serial_irq.c
src-$(CONFIG_MACH_SAM3X8E) += sam3/adc.c sam3/timer.c
src-$(CONFIG_MACH_SAM3X8E) += ../lib/sam3x/gcc/system_sam3xa.c
diff --git a/src/sam3/sam4_usb.c b/src/sam3/sam4_usb.c
new file mode 100644
index 00000000..ecf8e544
--- /dev/null
+++ b/src/sam3/sam4_usb.c
@@ -0,0 +1,238 @@
+// Hardware interface to SAM4 USB Device Port
+//
+// Copyright (C) 2018 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/irq.h" // irq_disable
+#include "board/usb_cdc.h" // usb_notify_ep0
+#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN
+#include "internal.h" // UDP
+#include "sched.h" // DECL_INIT
+
+#define CSR_EP0 (UDP_CSR_EPTYPE_CTRL | UDP_CSR_EPEDS)
+#define CSR_ACM (UDP_CSR_EPTYPE_INT_IN | UDP_CSR_EPEDS)
+#define CSR_BULK_OUT (UDP_CSR_EPTYPE_BULK_OUT | UDP_CSR_EPEDS)
+#define CSR_BULK_IN (UDP_CSR_EPTYPE_BULK_IN | UDP_CSR_EPEDS)
+
+static void
+usb_write_packet(uint32_t ep, const uint8_t *data, uint32_t len)
+{
+ while (len--)
+ UDP->UDP_FDR[ep] = *data++;
+}
+
+static uint32_t
+usb_read_packet(uint32_t ep, uint32_t csr, uint8_t *data, uint32_t max_len)
+{
+ uint32_t pk_len = (csr & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos;
+ uint32_t len = pk_len < max_len ? pk_len : max_len, orig_len = len;
+ while (len--)
+ *data++ = UDP->UDP_FDR[ep];
+ return orig_len;
+}
+
+int_fast8_t
+usb_read_bulk_out(void *data, uint_fast8_t max_len)
+{
+ static uint32_t next_bk = UDP_CSR_RX_DATA_BK0;
+ const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT
+ | UDP_CSR_TXCOMP);
+ uint32_t csr = UDP->UDP_CSR[USB_CDC_EP_BULK_OUT];
+ uint32_t bk = csr & (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1);
+ if (!bk) {
+ // Not ready to receive data
+ if (csr & other_irqs)
+ UDP->UDP_CSR[USB_CDC_EP_BULK_OUT] = (
+ CSR_BULK_OUT | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1);
+ UDP->UDP_IER = 1<<USB_CDC_EP_BULK_OUT;
+ return -1;
+ }
+ uint32_t len = usb_read_packet(USB_CDC_EP_BULK_OUT, csr, data, max_len);
+ if (bk == (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1))
+ bk = next_bk;
+ next_bk = bk ^ (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1);
+ UDP->UDP_CSR[USB_CDC_EP_BULK_OUT] = CSR_BULK_OUT | next_bk;
+ return len;
+}
+
+int_fast8_t
+usb_send_bulk_in(void *data, uint_fast8_t len)
+{
+ const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT
+ | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1);
+ uint32_t csr = UDP->UDP_CSR[USB_CDC_EP_BULK_IN];
+ if (csr & UDP_CSR_TXPKTRDY) {
+ // Not ready to send
+ if (csr & other_irqs)
+ UDP->UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN | UDP_CSR_TXCOMP;
+ UDP->UDP_IER = 1<<USB_CDC_EP_BULK_IN;
+ return -1;
+ }
+ usb_write_packet(USB_CDC_EP_BULK_IN, data, len);
+ UDP->UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN | UDP_CSR_TXPKTRDY;
+ return len;
+}
+
+int_fast8_t
+usb_read_ep0(void *data, uint_fast8_t max_len)
+{
+ const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT
+ | UDP_CSR_TXCOMP | UDP_CSR_RX_DATA_BK1);
+ uint32_t csr = UDP->UDP_CSR[0];
+ if (csr & other_irqs)
+ return -2;
+ if (!(csr & UDP_CSR_RX_DATA_BK0)) {
+ // Not ready to receive data
+ UDP->UDP_IER = 1<<0;
+ return -1;
+ }
+ uint32_t len = usb_read_packet(0, csr, data, max_len);
+ if (UDP->UDP_CSR[0] & other_irqs)
+ return -2;
+ UDP->UDP_CSR[0] = CSR_EP0 | other_irqs;
+ return len;
+}
+
+int_fast8_t
+usb_read_ep0_setup(void *data, uint_fast8_t max_len)
+{
+ const uint32_t other_irqs = (UDP_CSR_STALLSENT | UDP_CSR_TXCOMP
+ | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1);
+ uint32_t csr = UDP->UDP_CSR[0];
+ if (!(csr & UDP_CSR_RXSETUP)) {
+ // No data ready to read
+ if (csr & other_irqs)
+ UDP->UDP_CSR[0] = CSR_EP0 | UDP_CSR_RXSETUP;
+ UDP->UDP_IER = 1<<0;
+ return -1;
+ }
+ uint32_t len = usb_read_packet(0, csr, data, max_len);
+ uint32_t dir = *(uint8_t*)data & 0x80 ? UDP_CSR_DIR : 0;
+ UDP->UDP_CSR[0] = CSR_EP0 | dir;
+ return len;
+}
+
+int_fast8_t
+usb_send_ep0(const void *data, uint_fast8_t len)
+{
+ const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT
+ | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1);
+ uint32_t csr = UDP->UDP_CSR[0];
+ if (csr & other_irqs)
+ return -2;
+ if (csr & UDP_CSR_TXPKTRDY) {
+ // Not ready to send
+ UDP->UDP_IER = 1<<0;
+ return -1;
+ }
+ usb_write_packet(0, data, len);
+ uint32_t dir = csr & UDP_CSR_DIR;
+ UDP->UDP_CSR[0] = CSR_EP0 | dir | UDP_CSR_TXPKTRDY | other_irqs;
+ return len;
+}
+
+void
+usb_stall_ep0(void)
+{
+ UDP->UDP_CSR[0] = CSR_EP0 | UDP_CSR_FORCESTALL;
+ UDP->UDP_IER = 1<<0;
+}
+
+static uint32_t set_address;
+
+void
+usb_set_address(uint_fast8_t addr)
+{
+ set_address = addr | UDP_FADDR_FEN;
+ usb_send_ep0(NULL, 0);
+ UDP->UDP_IER = 1<<0;
+}
+
+void
+usb_set_configure(void)
+{
+ UDP->UDP_CSR[USB_CDC_EP_ACM] = CSR_ACM;
+ UDP->UDP_CSR[USB_CDC_EP_BULK_OUT] = CSR_BULK_OUT;
+ UDP->UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN;
+ UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG;
+}
+
+#if CONFIG_MACH_SAM4S8C
+#define EFC_HW EFC0
+#elif CONFIG_MACH_SAM4E8E
+#define EFC_HW EFC
+#endif
+
+void noinline __aligned(16) // align for predictable flash code access
+usb_request_bootloader(void)
+{
+ irq_disable();
+ // Request boot from ROM (instead of boot from flash)
+ while ((EFC_HW->EEFC_FSR & EEFC_FSR_FRDY) == 0)
+ ;
+ EFC_HW->EEFC_FCR = (EEFC_FCR_FCMD_CGPB | EEFC_FCR_FARG(1)
+ | EEFC_FCR_FKEY_PASSWD);
+ while ((EFC_HW->EEFC_FSR & EEFC_FSR_FRDY) == 0)
+ ;
+ // Reboot
+ RSTC->RSTC_CR = RSTC_CR_KEY(0xA5) | RSTC_CR_PROCRST | RSTC_CR_PERRST;
+ for (;;)
+ ;
+}
+
+void
+usbserial_init(void)
+{
+ // Enable clocks
+ enable_pclock(ID_UDP);
+ PMC->PMC_USB = PMC_USB_USBDIV(5 - 1); // PLLA=240Mhz; divide by 5 for 48Mhz
+ PMC->PMC_SCER = PMC_SCER_UDP;
+
+ // Enable USB pullup
+ UDP->UDP_TXVC = UDP_TXVC_PUON | UDP_TXVC_TXVDIS;
+
+ // Enable interrupts
+ UDP->UDP_ICR = 0xffffffff;
+ NVIC_SetPriority(UDP_IRQn, 1);
+ NVIC_EnableIRQ(UDP_IRQn);
+}
+DECL_INIT(usbserial_init);
+
+// Configure endpoint 0 after usb reset completes
+static void
+handle_end_reset(void)
+{
+ UDP->UDP_ICR = UDP_ISR_ENDBUSRES;
+
+ UDP->UDP_CSR[0] = CSR_EP0;
+ UDP->UDP_IER = 1<<0;
+
+ UDP->UDP_TXVC = UDP_TXVC_PUON;
+}
+
+void __visible
+UDP_Handler(void)
+{
+ uint32_t s = UDP->UDP_ISR;
+ UDP->UDP_IDR = s;
+ if (s & UDP_ISR_ENDBUSRES)
+ handle_end_reset();
+ if (s & UDP_ISR_RXRSM)
+ UDP->UDP_ICR = UDP_ISR_RXRSM;
+ if (s & (1<<0)) {
+ usb_notify_ep0();
+
+ if (set_address && UDP->UDP_CSR[0] & UDP_CSR_TXCOMP) {
+ // Ack from set_address command sent - now update address
+ UDP->UDP_FADDR = set_address;
+ UDP->UDP_GLB_STAT |= UDP_GLB_STAT_FADDEN;
+ set_address = 0;
+ }
+ }
+ if (s & (1<<USB_CDC_EP_BULK_OUT))
+ usb_notify_bulk_out();
+ if (s & (1<<USB_CDC_EP_BULK_IN))
+ usb_notify_bulk_in();
+}
diff --git a/src/sam3/sam4s_timer.c b/src/sam3/sam4s_timer.c
index a586dd64..a051f08c 100644
--- a/src/sam3/sam4s_timer.c
+++ b/src/sam3/sam4s_timer.c
@@ -93,7 +93,7 @@ timer_init(void)
enable_pclock(ID_TC0);
tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK2;
tc->TC_IER = TC_IER_CPAS;
- NVIC_SetPriority(TC0_IRQn, 1);
+ NVIC_SetPriority(TC0_IRQn, 2);
NVIC_EnableIRQ(TC0_IRQn);
timer_kick();
timer_reset();
diff --git a/src/sam3/timer.c b/src/sam3/timer.c
index 987a76e8..b2d600a5 100644
--- a/src/sam3/timer.c
+++ b/src/sam3/timer.c
@@ -44,7 +44,7 @@ timer_init(void)
enable_pclock(ID_TC0);
tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1;
tc->TC_IER = TC_IER_CPAS;
- NVIC_SetPriority(TC0_IRQn, 1);
+ NVIC_SetPriority(TC0_IRQn, 2);
NVIC_EnableIRQ(TC0_IRQn);
timer_kick();
tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
diff --git a/src/sam3/usb_cdc_ep.h b/src/sam3/usb_cdc_ep.h
new file mode 100644
index 00000000..bcf1d3e3
--- /dev/null
+++ b/src/sam3/usb_cdc_ep.h
@@ -0,0 +1,10 @@
+#ifndef __SAM3_USB_CDC_EP_H
+#define __SAM3_USB_CDC_EP_H
+
+enum {
+ USB_CDC_EP_ACM = 3,
+ USB_CDC_EP_BULK_OUT = 1,
+ USB_CDC_EP_BULK_IN = 2,
+};
+
+#endif // usb_cdc_ep.h