diff options
author | Vegard Storheil Eriksen <zyp@jvnv.net> | 2012-08-19 22:33:35 +0200 |
---|---|---|
committer | Vegard Storheil Eriksen <zyp@jvnv.net> | 2012-08-19 22:33:35 +0200 |
commit | c09be56d30e5a99ab0451a53d0fc0df0aa0dee8f (patch) | |
tree | c8f5ef6c1e54f3825deeee95169fbd10fce1baea /usb/usb.h | |
parent | e586c178073b9a0fee90d5fc8e795d266ebd7b7d (diff) |
Moved usb driver from suzumebachi project.
Diffstat (limited to 'usb/usb.h')
-rw-r--r-- | usb/usb.h | 215 |
1 files changed, 215 insertions, 0 deletions
@@ -90,4 +90,219 @@ static USB_t OTG_FS(0x50000000); static USB_t OTG_HS(0x40040000); #endif +static uint8_t dev_desc[] = { + 0x12, 0x01, 0x00, 0x02, 0xff, 0x00, 0x00, 0x40, 0x34, 0x12, 0x78, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +}; + +static uint8_t conf_desc[] = { + 0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x00, 0xc0, 0x00, + 0x09, 0x04, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, +}; + +static uint32_t buf[16]; + +class USB_generic { + public: + virtual void write(uint32_t ep, uint32_t* bufp, uint32_t len) = 0; + virtual void hw_set_address(uint8_t addr) = 0; + virtual void hw_conf_ep(uint8_t ep, uint32_t conf) = 0; + virtual void hw_set_stall(uint8_t ep) = 0; + + protected: + bool get_descriptor(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + switch(wValue) { + case 0x100: + write(0, (uint32_t*)dev_desc, wLength > sizeof(dev_desc) ? sizeof(dev_desc) : wLength); + return true; + case 0x200: + write(0, (uint32_t*)conf_desc, wLength > sizeof(conf_desc) ? sizeof(conf_desc) : wLength); + return true; + default: + return false; + } + } + + bool set_address(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + hw_set_address(wValue); + write(0, 0, 0); + return true; + } + + bool set_configuration(uint16_t wValue, uint16_t wIndex, uint16_t wLength) { + switch(wValue) { + case 0: + hw_conf_ep(1, 0); + break; + + case 1: + hw_conf_ep(1, (1 << 22) | (2 << 18) | (1 << 15) | 64); + break; + + default: + return false; + } + + write(0, 0, 0); + return true; + } + + + void handle_setup(const uint32_t* bufp) { + uint8_t bmRequestType = bufp[0] & 0xff; + uint8_t bRequest = (bufp[0] >> 8) & 0xff; + uint16_t wValue = (bufp[0] >> 16) & 0xffff; + uint16_t wIndex = bufp[1] & 0xffff; + uint16_t wLength = (bufp[1] >> 16) & 0xffff; + + // GET_DESCRIPTOR + if(bmRequestType == 0x80 && bRequest == 0x06) { + if(get_descriptor(wValue, wIndex, wLength)) { + return; + } + } + + // SET_ADDRESS + if(bmRequestType == 0x00 && bRequest == 0x05) { + if(set_address(wValue, wIndex, wLength)) { + return; + } + } + + // SET_CONFIGURATION + if(bmRequestType == 0x00 && bRequest == 0x09) { + if(set_configuration(wValue, wIndex, wLength)) { + return; + } + } + + // I2C_READ + //if(bmRequestType == 0xc0 && bRequest == 0xf0) { + // if(i2c_read(wValue, wIndex, wLength)) { + // return; + // } + //} + + // JTAG_SHIFT + //if(bmRequestType == 0xc0 && bRequest == 0xff) { + // if(jtag_shift(wValue, wIndex, wLength)) { + // return; + // } + //} + + hw_set_stall(0); + } + +}; + +class USB_otg : public USB_generic { + private: + USB_t& otg; + + protected: + virtual void hw_set_address(uint8_t addr) { + otg.dev_reg.DCFG |= addr << 4; + } + + virtual void hw_conf_ep(uint8_t ep, uint32_t conf) { + otg.dev_iep_reg[ep].DIEPCTL = conf; + } + + virtual void hw_set_stall(uint8_t ep) { + otg.dev_iep_reg[ep].DIEPCTL |= (1 << 21); + } + + public: + USB_otg(USB_t& otg_periph) : otg(otg_periph) {} + + void init() { + // Set PHYSEL. + otg.reg.GUSBCFG |= (1 << 6); + + Time::sleep(10); + + while(!(otg.reg.GRSTCTL & (1 << 31))); + otg.reg.GRSTCTL |= 1; + while(otg.reg.GRSTCTL & 1); + + otg.reg.GAHBCFG = 0; + + // USB configuration + otg.reg.GUSBCFG = (1 << 30) | (0xf << 10) | (0 << 9) | (0 << 8) | (1 << 6); + // FDMOD TRDT HNPCAP SRPCAP PHYSEL + + // interrupt mask + otg.reg.GINTMSK = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 4); + // ENUMDNEM USBRST USBSUSPM ESUSPM SOFM OTGINT MMISM + + // device configuration + otg.dev_reg.DCFG = (1 << 2) | 3; + // NZLSOHSK DSPD + + // core configuration + otg.reg.GCCFG = (1 << 19) | (1 << 16); + // VBUSBSEN PWRDWN + + } + + void process() { + // USB reset. + if(otg.reg.GINTSTS & (1 << 12)) { + otg.dev_oep_reg[0].DOEPCTL = (1 << 27); + otg.dev_reg.DAINTMSK = (1 << 16) | 1; + otg.dev_reg.DOEPMSK = (1 << 3) | 1; + otg.dev_reg.DIEPEMPMSK = (1 << 3) | 1; + otg.reg.GRXFSIZ = 256; + otg.reg.DIEPTXF0 = (64 << 16) | 256; + otg.reg.DIEPTXF1 = (64 << 16) | 320; + otg.dev_oep_reg[0].DOEPTSIZ = (3 << 29); + } + + // OTG interrupt. + if(otg.reg.GINTSTS & (1 << 2)) { + otg.reg.GOTGINT = (1 << 2); // SEDET + } + + // RxFIFO non-empty. + if(otg.reg.GINTSTS & (1 << 4)) { + handle_rxfifo(); + } + + otg.reg.GINTSTS = 0xffffffff; + } + + bool ep_ready(uint32_t ep) { + return (otg.dev_iep_reg[ep].DIEPCTL & 0x80008000) == 0x8000; + } + + virtual void write(uint32_t ep, uint32_t* bufp, uint32_t len) { + otg.dev_iep_reg[ep].DIEPTSIZ = (1 << 19) | len; + // PKTCNT + otg.dev_iep_reg[ep].DIEPCTL |= (1 << 31) | (1 << 26); + // EPENA CNAK + + len = (len + 3) >> 2; + + while(len--) { + otg.fifo[ep].reg = *bufp++; + } + } + + void handle_rxfifo() { + uint32_t status = otg.reg.GRXSTSP; + + uint32_t len = (status & 0x7ff0) >> 6; + + for(uint32_t i = 0; i < len; i++) { + buf[i] = otg.fifo[0].reg; + } + + //if(status == 0x000c0080) { + if((status & (0xf << 17)) == (0x4 << 17)) { + handle_setup(buf); + otg.dev_oep_reg[0].DOEPCTL |= (1 << 26); + } + } +}; + #endif |