summaryrefslogtreecommitdiff
path: root/usb
diff options
context:
space:
mode:
Diffstat (limited to 'usb')
-rw-r--r--usb/usb.h5
-rw-r--r--usb/usb_nrf.h187
-rw-r--r--usb/usb_nrf_def.h97
3 files changed, 289 insertions, 0 deletions
diff --git a/usb/usb.h b/usb/usb.h
index e57867c..260af9f 100644
--- a/usb/usb.h
+++ b/usb/usb.h
@@ -22,6 +22,11 @@ static L0_USB_t USB(0x40005c00, 0x40006000);
static L0_USB_t USB(0x40006800, 0x40006c00);
+#elif defined(NRF52840)
+#include "usb_nrf.h"
+
+static USB_NRF_t USBD(0x40027000);
+
#endif
#endif
diff --git a/usb/usb_nrf.h b/usb/usb_nrf.h
new file mode 100644
index 0000000..16e40a1
--- /dev/null
+++ b/usb/usb_nrf.h
@@ -0,0 +1,187 @@
+#ifndef LAKS_USB_USB_NRF_H
+#define LAKS_USB_USB_NRF_H
+
+#include "generic.h"
+#include "usb_nrf_def.h"
+
+#include <string.h>
+
+class USB_NRF : public USB_generic {
+ private:
+ USB_NRF_t& usb;
+
+ uint32_t ep_in_busy;
+
+ uint8_t buf_in[8][64];
+ uint8_t buf_out[8][64];
+
+ protected:
+ virtual void hw_set_address(uint8_t addr) {
+ usb_rblog.log("SetAddress: %d", addr);
+
+ // Do nothing, handled in hardware.
+ }
+
+ virtual void hw_conf_ep(uint8_t ep, EPType type, uint32_t size) {
+ usb_rblog.log("Configuring EP%02x: size=%d", ep, size);
+
+ uint8_t in = ep & 0x80;
+ ep &= 0x7f;
+
+ if(in || ep == 0) {
+ usb.reg_in[ep].PTR = (uint32_t)&buf_in[ep];
+ usb.reg.EPINEN |= 1 << ep;
+ }
+
+ if(!in) {
+ usb.reg_out[ep].PTR = (uint32_t)&buf_out[ep];
+
+ usb.reg.SIZE_EPOUT[ep] = 0;
+ usb.reg.EPOUTEN |= 1 << ep;
+ }
+ }
+
+ virtual void hw_set_stall(uint8_t ep) {
+ if(ep == 0) {
+ usb.tasks.EP0STALL = 1;
+ }
+ }
+
+ public:
+ USB_NRF(USB_NRF_t& usb_periph, desc_t dev, desc_t conf) : USB_generic(dev, conf), usb(usb_periph) {}
+
+ void init() {
+ *(volatile uint32_t*)0x4006ec00 = 0x00009375;
+ *(volatile uint32_t*)0x4006ed14 = 0x00000003;
+ *(volatile uint32_t*)0x4006ec00 = 0x00009375;
+
+ usb.reg.ENABLE = 1;
+
+ while(!(usb.reg.EVENTCAUSE & (1 << 11)));
+
+ usb.reg.EVENTCAUSE = 1 << 11;
+
+ *(volatile uint32_t*)0x4006ec00 = 0x00009375;
+ *(volatile uint32_t*)0x4006ed14 = 0x00000000;
+ *(volatile uint32_t*)0x4006ec00 = 0x00009375;
+
+ usb.reg.USBPULLUP = 1;
+ }
+
+ void process() {
+ if(usb.events.USBRESET) {
+ usb.events.USBRESET = 0;
+
+ usb_rblog.log("USB Reset");
+
+ handle_reset();
+ return;
+ }
+
+ if(usb.events.EP0SETUP) {
+ usb.events.EP0SETUP = 0;
+
+ uint8_t setupbuf[8] = {
+ (uint8_t)usb.reg.BMREQUESTTYPE,
+ (uint8_t)usb.reg.BREQUEST,
+ (uint8_t)usb.reg.WVALUEL,
+ (uint8_t)usb.reg.WVALUEH,
+ (uint8_t)usb.reg.WINDEXL,
+ (uint8_t)usb.reg.WINDEXH,
+ (uint8_t)usb.reg.WLENGTHL,
+ (uint8_t)usb.reg.WLENGTHH,
+ };
+
+ handle_setup((uint32_t*)&setupbuf);
+ }
+
+ if(usb.events.EP0DATADONE) {
+ // TODO: Support multi-packet data stages.
+ usb.events.EP0DATADONE = 0;
+
+ usb_rblog.log("Control data IN done, ACKing status stage.");
+
+ usb.tasks.EP0STATUS = 1;
+ }
+
+ /*
+ for(uint32_t ep = 0; ep <= 7; ep++) {
+ if(usb.events.ENDEPIN[ep]) {
+ usb.events.ENDEPIN[ep] = 0;
+
+ ep_in_busy &= ~(1 << ep);
+ }
+ }
+ */
+
+ if(usb.reg.EPDATASTATUS) {
+ for(uint32_t ep = 1; ep <= 7; ep++) {
+ if((usb.reg.EPDATASTATUS & ((1 << 16) << ep)) == 0) {
+ continue;
+ }
+
+ usb_rblog.log("EPDATA, starting DMA on ep %d", ep);
+
+ usb.reg.EPDATASTATUS = (1 << 16) << ep;
+
+ usb.reg_out[ep].MAXCNT = usb.reg.SIZE_EPOUT[ep];
+ usb.tasks.STARTEPOUT[ep] = 1;
+ }
+ }
+
+ for(uint32_t ep = 0; ep <= 7; ep++) {
+ if(usb.events.ENDEPOUT[ep]) {
+ usb.events.ENDEPOUT[ep] = 0;
+
+ handle_out(ep, usb.reg_out[ep].AMOUNT);
+ }
+ }
+ }
+
+ virtual bool ep_ready(uint32_t ep) {
+ //if(usb.events.ENDEPIN[ep]) {
+ // usb.events.ENDEPIN[ep] = 0;
+ // ep_in_busy &= ~(1 << ep);
+ //}
+
+ if(usb.reg.EPDATASTATUS & (1 << ep)) {
+ usb.reg.EPDATASTATUS = 1 << ep;
+
+ ep_in_busy &= ~(1 << ep);
+ }
+
+ return (ep_in_busy & (1 << ep)) == 0;
+ }
+
+ virtual void write(uint32_t ep, uint32_t* bufp, uint32_t len) {
+ usb_rblog.log("Writing, ep=%d, len=%d", ep, len);
+
+ if(ep == 0 && len == 0 && (usb.reg.BMREQUESTTYPE & 0x80) == 0) {
+ usb_rblog.log("EP0 status ACK");
+ usb.tasks.EP0STATUS = 1;
+ return;
+ }
+
+ memcpy(&buf_in[ep], bufp, len);
+
+ usb.reg_in[ep].MAXCNT = len;
+
+ usb.tasks.STARTEPIN[ep] = 1;
+
+ ep_in_busy |= 1 << ep;
+ }
+
+ virtual uint32_t read(uint32_t ep, uint32_t* bufp, uint32_t len) {
+ usb_rblog.log("Reading, ep=%d, len=%d", ep, len);
+
+ if(len > usb.reg_out[ep].AMOUNT) {
+ len = usb.reg_out[ep].AMOUNT;
+ }
+
+ memcpy(bufp, &buf_out[ep], len);
+
+ return len;
+ }
+};
+
+#endif
diff --git a/usb/usb_nrf_def.h b/usb/usb_nrf_def.h
new file mode 100644
index 0000000..0d56807
--- /dev/null
+++ b/usb/usb_nrf_def.h
@@ -0,0 +1,97 @@
+#ifndef LAKS_USB_USB_NRF_DEF_H
+#define LAKS_USB_USB_NRF_DEF_H
+
+#include <stdint.h>
+
+class USB_NRF_t {
+ private:
+ struct tasks_t {
+ uint32_t _reserved;
+ volatile uint32_t STARTEPIN[8];
+ volatile uint32_t STARTISOIN;
+ volatile uint32_t STARTEPOUT[8];
+ volatile uint32_t STARTISOOUT;
+ volatile uint32_t EP0RCVOUT;
+ volatile uint32_t EP0STATUS;
+ volatile uint32_t EP0STALL;
+ volatile uint32_t DPDMDRIVE;
+ volatile uint32_t DPDMNODRIVE;
+ };
+
+ struct events_t {
+ volatile uint32_t USBRESET;
+ volatile uint32_t STARTED;
+ volatile uint32_t ENDEPIN[8];
+ volatile uint32_t EP0DATADONE;
+ volatile uint32_t ENDISOIN;
+ volatile uint32_t ENDEPOUT[8];
+ volatile uint32_t ENDISOOUT;
+ volatile uint32_t SOF;
+ volatile uint32_t USBEVENT;
+ volatile uint32_t EP0SETUP;
+ volatile uint32_t EPDATA;
+ };
+
+ struct reg_t {
+ volatile uint32_t SHORTS;
+ uint32_t _reserved[63];
+ volatile uint32_t INTEN;
+ volatile uint32_t INTENSET;
+ volatile uint32_t INTENCLR;
+ uint32_t _reserved_1[61];
+ volatile uint32_t EVENTCAUSE;
+ uint32_t _reserved_2[7];
+ volatile uint32_t HALTED_EPIN[9];
+ volatile uint32_t HALTED_EPOUT[9];
+ volatile uint32_t EPSTATUS;
+ volatile uint32_t EPDATASTATUS;
+ volatile uint32_t USBADDR;
+ uint32_t _reserved_3[3];
+ volatile uint32_t BMREQUESTTYPE;
+ volatile uint32_t BREQUEST;
+ volatile uint32_t WVALUEL;
+ volatile uint32_t WVALUEH;
+ volatile uint32_t WINDEXL;
+ volatile uint32_t WINDEXH;
+ volatile uint32_t WLENGTHL;
+ volatile uint32_t WLENGTHH;
+ volatile uint32_t SIZE_EPOUT[8];
+ volatile uint32_t SIZE_ISOOUT;
+ uint32_t _reserved_4[15];
+ volatile uint32_t ENABLE;
+ volatile uint32_t USBPULLUP;
+ volatile uint32_t DPDMVALUE;
+ volatile uint32_t DTOGGLE;
+ volatile uint32_t EPINEN;
+ volatile uint32_t EPOUTEN;
+ volatile uint32_t EPSTALL;
+ volatile uint32_t ISOSPLIT;
+ volatile uint32_t FRAMECNTR;
+ uint32_t _reserved_5[2];
+ volatile uint32_t LOWPOWER;
+ volatile uint32_t ISOINCONFIG;
+ };
+
+ struct ep_reg_t {
+ volatile uint32_t PTR;
+ volatile uint32_t MAXCNT;
+ volatile uint32_t AMOUNT;
+ uint32_t _reserved[2];
+ };
+
+ public:
+ tasks_t& tasks;
+ events_t& events;
+ reg_t& reg;
+ ep_reg_t* reg_in;
+ ep_reg_t* reg_out;
+
+ USB_NRF_t(uint32_t reg_addr) :
+ tasks(*(tasks_t*)(reg_addr + 0x0)),
+ events(*(events_t*)(reg_addr + 0x100)),
+ reg(*(reg_t*)(reg_addr + 0x200)),
+ reg_in((ep_reg_t*)(reg_addr + 0x600)),
+ reg_out((ep_reg_t*)(reg_addr + 0x700)) {}
+};
+
+#endif