sam3: Add USB support for the SAM4 "UDP" hardware

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2018-12-29 17:09:35 -05:00
parent 75ad16ea1a
commit 02c558652f
6 changed files with 256 additions and 2 deletions

View File

@ -37,7 +37,12 @@ config CLOCK_FREQ
default 15000000 if MACH_SAM4S8C # 120000000/8 default 15000000 if MACH_SAM4S8C # 120000000/8
default 60000000 if MACH_SAM4E8E # 120000000/2 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 config SERIAL
depends on !USBSERIAL
bool bool
default y default y
config SERIAL_BAUD config SERIAL_BAUD

View File

@ -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 += sam3/main.c sam3/gpio.c sam3/i2c.c sam3/spi.c
src-y += generic/crc16_ccitt.c generic/alloc.c src-y += generic/crc16_ccitt.c generic/alloc.c
src-y += generic/armcm_irq.c generic/timer_irq.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_SERIAL) += sam3/serial.c generic/serial_irq.c
src-$(CONFIG_MACH_SAM3X8E) += sam3/adc.c sam3/timer.c src-$(CONFIG_MACH_SAM3X8E) += sam3/adc.c sam3/timer.c
src-$(CONFIG_MACH_SAM3X8E) += ../lib/sam3x/gcc/system_sam3xa.c src-$(CONFIG_MACH_SAM3X8E) += ../lib/sam3x/gcc/system_sam3xa.c

238
src/sam3/sam4_usb.c Normal file
View File

@ -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();
}

View File

@ -93,7 +93,7 @@ timer_init(void)
enable_pclock(ID_TC0); enable_pclock(ID_TC0);
tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK2; tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK2;
tc->TC_IER = TC_IER_CPAS; tc->TC_IER = TC_IER_CPAS;
NVIC_SetPriority(TC0_IRQn, 1); NVIC_SetPriority(TC0_IRQn, 2);
NVIC_EnableIRQ(TC0_IRQn); NVIC_EnableIRQ(TC0_IRQn);
timer_kick(); timer_kick();
timer_reset(); timer_reset();

View File

@ -44,7 +44,7 @@ timer_init(void)
enable_pclock(ID_TC0); enable_pclock(ID_TC0);
tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1; tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1;
tc->TC_IER = TC_IER_CPAS; tc->TC_IER = TC_IER_CPAS;
NVIC_SetPriority(TC0_IRQn, 1); NVIC_SetPriority(TC0_IRQn, 2);
NVIC_EnableIRQ(TC0_IRQn); NVIC_EnableIRQ(TC0_IRQn);
timer_kick(); timer_kick();
tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;

10
src/sam3/usb_cdc_ep.h Normal file
View File

@ -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