aboutsummaryrefslogtreecommitdiffstats
path: root/src/lpc176x
diff options
context:
space:
mode:
Diffstat (limited to 'src/lpc176x')
-rw-r--r--src/lpc176x/Kconfig4
-rw-r--r--src/lpc176x/Makefile1
-rw-r--r--src/lpc176x/timer.c2
-rw-r--r--src/lpc176x/usbserial.c297
4 files changed, 303 insertions, 1 deletions
diff --git a/src/lpc176x/Kconfig b/src/lpc176x/Kconfig
index cdfad6cd..fa80c463 100644
--- a/src/lpc176x/Kconfig
+++ b/src/lpc176x/Kconfig
@@ -25,7 +25,11 @@ config CLOCK_FREQ
default 25000000 if MACH_LPC1768 # 100000000 / 4
default 30000000 if MACH_LPC1769 # 120000000 / 4
+config USBSERIAL
+ 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/lpc176x/Makefile b/src/lpc176x/Makefile
index 2ff7d174..94e6a268 100644
--- a/src/lpc176x/Makefile
+++ b/src/lpc176x/Makefile
@@ -17,6 +17,7 @@ src-y += lpc176x/main.c lpc176x/timer.c lpc176x/gpio.c
src-y += generic/crc16_ccitt.c generic/alloc.c
src-y += generic/armcm_irq.c generic/timer_irq.c
src-y += ../lib/lpc176x/device/system_LPC17xx.c
+src-$(CONFIG_USBSERIAL) += lpc176x/usbserial.c generic/usb_cdc.c
src-$(CONFIG_SERIAL) += lpc176x/serial.c generic/serial_irq.c
# Add the TOOLCHAIN_GCC_ARM files to the build
diff --git a/src/lpc176x/timer.c b/src/lpc176x/timer.c
index d6d6f9ed..d8196329 100644
--- a/src/lpc176x/timer.c
+++ b/src/lpc176x/timer.c
@@ -38,7 +38,7 @@ timer_init(void)
// Disable timer
LPC_TIM0->TCR = 0x02;
// Enable interrupts
- NVIC_SetPriority(TIMER0_IRQn, 1);
+ NVIC_SetPriority(TIMER0_IRQn, 2);
NVIC_EnableIRQ(TIMER0_IRQn);
LPC_TIM0->MCR = 0x01;
// Clear counter value
diff --git a/src/lpc176x/usbserial.c b/src/lpc176x/usbserial.c
new file mode 100644
index 00000000..28c0881f
--- /dev/null
+++ b/src/lpc176x/usbserial.c
@@ -0,0 +1,297 @@
+// Hardware interface to USB on lpc176x
+//
+// 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> // memcpy
+#include "LPC17xx.h" // LPC_SC
+#include "board/usb_cdc.h" // usb_notify_setup
+#include "byteorder.h" // cpu_to_le32
+#include "command.h" // output
+#include "internal.h" // gpio_peripheral
+#include "sched.h" // DECL_INIT
+
+// Internal endpoint addresses
+#define EP0OUT 0x00
+#define EP0IN 0x01
+#define EP1IN 0x03
+#define EP2OUT 0x04
+#define EP5IN 0x0b
+
+// USB device interupt status flags
+#define EP_SLOW (1<<2)
+#define DEV_STAT (1<<3)
+#define CCEMPTY (1<<4)
+#define CDFULL (1<<5)
+#define EP_RLZED (1<<8)
+
+#define RD_EN (1<<0)
+#define WR_EN (1<<1)
+
+static void
+usb_irq_disable(void)
+{
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+static void
+usb_irq_enable(void)
+{
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+static void
+usb_wait(uint32_t flag)
+{
+ while (!(LPC_USB->USBDevIntSt & flag))
+ ;
+ LPC_USB->USBDevIntClr = flag;
+}
+
+
+/****************************************************************
+ * Serial Interface Engine (SIE) functions
+ ****************************************************************/
+
+#define SIE_CMD_SELECT 0x00
+#define SIE_CMD_SET_ENDPOINT_STATUS 0x40
+#define SIE_CMD_SET_ADDRESS 0xD0
+#define SIE_CMD_CONFIGURE 0xD8
+#define SIE_CMD_SET_DEVICE_STATUS 0xFE
+#define SIE_CMD_CLEAR_BUFFER 0xF2
+#define SIE_CMD_VALIDATE_BUFFER 0xFA
+
+static void
+sie_cmd(uint32_t cmd)
+{
+ LPC_USB->USBDevIntClr = CDFULL | CCEMPTY;
+ LPC_USB->USBCmdCode = 0x00000500 | (cmd << 16);
+ usb_wait(CCEMPTY);
+}
+
+static void
+sie_cmd_write(uint32_t cmd, uint32_t data)
+{
+ sie_cmd(cmd);
+ LPC_USB->USBCmdCode = 0x00000100 | (data << 16);
+ usb_wait(CCEMPTY);
+}
+
+static uint32_t
+sie_cmd_read(uint32_t cmd)
+{
+ sie_cmd(cmd);
+ LPC_USB->USBCmdCode = 0x00000200 | (cmd << 16);
+ usb_wait(CDFULL);
+ return LPC_USB->USBCmdData;
+}
+
+static uint32_t
+sie_select_and_clear(uint32_t idx)
+{
+ LPC_USB->USBEpIntClr = 1<<idx;
+ usb_wait(CDFULL);
+ return LPC_USB->USBCmdData;
+}
+
+
+/****************************************************************
+ * Interface
+ ****************************************************************/
+
+static int_fast8_t
+usb_write_packet(uint32_t ep, const void *data, uint_fast8_t len)
+{
+ usb_irq_disable();
+ uint32_t sts = sie_cmd_read(SIE_CMD_SELECT | ep);
+ if (sts & 0x01) {
+ // Output buffers full
+ usb_irq_enable();
+ return -1;
+ }
+
+ LPC_USB->USBCtrl = WR_EN | ((ep/2) << 2);
+ LPC_USB->USBTxPLen = len;
+ if (!len)
+ LPC_USB->USBTxData = 0;
+ int i;
+ for (i = 0; i<DIV_ROUND_UP(len, 4); i++) {
+ uint32_t d;
+ memcpy(&d, data, sizeof(d));
+ data += sizeof(d);
+ LPC_USB->USBTxData = cpu_to_le32(d);
+ }
+ sie_cmd(SIE_CMD_VALIDATE_BUFFER);
+ usb_irq_enable();
+
+ return len;
+}
+
+static int_fast8_t
+usb_read_packet(uint32_t ep, void *data, uint_fast8_t max_len)
+{
+ usb_irq_disable();
+ uint32_t sts = sie_cmd_read(SIE_CMD_SELECT | ep);
+ if (!(sts & 0x01)) {
+ // No data available
+ usb_irq_enable();
+ return -1;
+ }
+
+ // Determine packet size
+ LPC_USB->USBCtrl = RD_EN | ((ep/2) << 2);
+ uint32_t plen = LPC_USB->USBRxPLen;
+ while (!(plen & (1<<11)))
+ plen = LPC_USB->USBRxPLen;
+ plen &= 0x3FF;
+ if (plen > max_len)
+ // XXX - return error code? must keep reading?
+ plen = max_len;
+ // Copy data
+ uint32_t xfer = plen;
+ for (;;) {
+ uint32_t d = le32_to_cpu(LPC_USB->USBRxData);
+ if (xfer <= sizeof(d)) {
+ memcpy(data, &d, xfer);
+ break;
+ }
+ memcpy(data, &d, sizeof(d));
+ data += sizeof(d);
+ xfer -= sizeof(d);
+ }
+ // Clear space for next packet
+ sts = sie_cmd_read(SIE_CMD_CLEAR_BUFFER);
+ usb_irq_enable();
+ if (sts & 0x01)
+ // Packet overwritten
+ return -1;
+
+ return plen;
+}
+
+int_fast8_t
+usb_read_bulk_out(void *data, uint_fast8_t max_len)
+{
+ return usb_read_packet(EP2OUT, data, max_len);
+}
+
+int_fast8_t
+usb_send_bulk_in(void *data, uint_fast8_t len)
+{
+ return usb_write_packet(EP5IN, data, len);
+}
+
+int_fast8_t
+usb_read_setup(void *data, uint_fast8_t max_len)
+{
+ return usb_read_packet(EP0OUT, data, max_len);
+}
+
+int_fast8_t
+usb_send_setup(const void *data, uint_fast8_t len)
+{
+ return usb_write_packet(EP0IN, data, len);
+}
+
+void
+usb_set_stall(void)
+{
+ usb_irq_disable();
+ sie_cmd_write(SIE_CMD_SET_ENDPOINT_STATUS | 0, (1<<7));
+ usb_irq_enable();
+}
+
+void
+usb_set_address(uint_fast8_t addr)
+{
+ usb_irq_disable();
+ sie_cmd_write(SIE_CMD_SET_ADDRESS, addr | (1<<7));
+ usb_irq_enable();
+ usb_send_setup(NULL, 0);
+}
+
+static void
+realize_endpoint(uint32_t idx, uint32_t packet_size)
+{
+ LPC_USB->USBDevIntClr = EP_RLZED;
+ LPC_USB->USBReEp |= 1<<idx;
+ LPC_USB->USBEpInd = idx;
+ LPC_USB->USBMaxPSize = packet_size;
+ usb_wait(EP_RLZED);
+ LPC_USB->USBEpIntEn |= 1<<idx;
+ sie_cmd_write(SIE_CMD_SET_ENDPOINT_STATUS | idx, 0);
+}
+
+void
+usb_set_configure(void)
+{
+ usb_irq_disable();
+ realize_endpoint(EP1IN, USB_CDC_EP_ACM_SIZE);
+ realize_endpoint(EP2OUT, USB_CDC_EP_BULK_OUT_SIZE);
+ realize_endpoint(EP5IN, USB_CDC_EP_BULK_IN_SIZE);
+ sie_cmd_write(SIE_CMD_CONFIGURE, 1);
+ usb_irq_enable();
+}
+
+void
+usbserial_init(void)
+{
+ usb_irq_disable();
+ // enable power
+ LPC_SC->PCONP |= (1<<31);
+ // enable clock
+ LPC_USB->USBClkCtrl = 0x12;
+ while (LPC_USB->USBClkSt != 0x12)
+ ;
+ // configure USBD+, USBD-, and USB Connect pins
+ gpio_peripheral(0, 29, 1, 0);
+ gpio_peripheral(0, 30, 1, 0);
+ gpio_peripheral(2, 9, 1, 0);
+ // setup endpoints
+ realize_endpoint(EP0OUT, USB_CDC_EP0_SIZE);
+ realize_endpoint(EP0IN, USB_CDC_EP0_SIZE);
+ sie_cmd_write(SIE_CMD_SET_DEVICE_STATUS, 1);
+ // enable irqs
+ LPC_USB->USBDevIntEn = DEV_STAT | EP_SLOW;
+ NVIC_SetPriority(USB_IRQn, 1);
+ usb_irq_enable();
+}
+DECL_INIT(usbserial_init);
+
+void
+usbserial_shutdown(void)
+{
+ usb_irq_enable();
+}
+DECL_SHUTDOWN(usbserial_shutdown);
+
+void __visible
+USB_IRQHandler(void)
+{
+ uint32_t udis = LPC_USB->USBDevIntSt;
+ if (udis & DEV_STAT) {
+ LPC_USB->USBDevIntClr = DEV_STAT;
+ // XXX - should handle reset and other states
+ }
+ if (udis & EP_SLOW) {
+ uint32_t ueis = LPC_USB->USBEpIntSt;
+ if (ueis & (1<<EP0OUT)) {
+ sie_select_and_clear(EP0OUT);
+ usb_notify_setup();
+ }
+ if (ueis & (1<<EP0IN)) {
+ sie_select_and_clear(EP0IN);
+ usb_notify_setup();
+ }
+ if (ueis & (1<<EP2OUT)) {
+ sie_select_and_clear(EP2OUT);
+ usb_notify_bulk_out();
+ }
+ if (ueis & (1<<EP5IN)) {
+ sie_select_and_clear(EP5IN);
+ usb_notify_bulk_in();
+ }
+ LPC_USB->USBDevIntClr = EP_SLOW;
+ }
+}