123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- /*
- * hw_dwc_otg.c
- *
- * USB handling for DWC-OTG USB 2.0 controller.
- *
- * Written & released by Keir Fraser <keir.xen@gmail.com>
- *
- * This is free and unencumbered software released into the public domain.
- * See the file COPYING for more details, or visit <http://unlicense.org>.
- */
- #include "hw_dwc_otg.h"
- static int conf_iface;
- static bool_t is_hs;
- static struct rx_buf {
- uint32_t data[USB_HS_MPS / 4];
- uint32_t count;
- } rx_buf0[1], rx_bufn[32] section_ext_ram;
- #define RX_MASK(_ep, _idx) (((_ep)->_idx) & ((_ep)->rx_nr - 1))
- static struct ep {
- struct rx_buf *rx;
- uint16_t rxc, rxp, rx_nr;
- bool_t rx_active, tx_ready;
- } eps[conf_nr_ep];
- bool_t hw_has_highspeed(void)
- {
- return conf_iface == IFACE_HS_EMBEDDED;
- }
- bool_t usb_is_highspeed(void)
- {
- return is_hs;
- }
- static void core_reset(void)
- {
- do { delay_us(1); } while (!(otg->grstctl & OTG_GRSTCTL_AHBIDL));
- otg->grstctl |= OTG_GRSTCTL_CSRST;
- do { delay_us(1); } while (otg->grstctl & OTG_GRSTCTL_CSRST);
- }
- static void hsphyc_init(void)
- {
- /* Enable the LDO and wait for it to be ready. */
- hsphyc->ldo |= HSPHYC_LDO_ENABLE;
- do { delay_us(1); } while (!(hsphyc->ldo & HSPHYC_LDO_STATUS));
- /* This is correct only for HSE = 16Mhz. */
- hsphyc->pll1 |= HSPHYC_PLL1_SEL(3);
- /* Magic values from the LL driver. We can probably discard them. */
- hsphyc->tune |= (HSPHYC_TUNE_HSDRVCHKITRIM(7) |
- HSPHYC_TUNE_HSDRVRFRED |
- HSPHYC_TUNE_HSDRVDCCUR |
- HSPHYC_TUNE_INCURRINT |
- HSPHYC_TUNE_INCURREN);
- /* Enable the PLL and wait to stabilise. */
- hsphyc->pll1 |= HSPHYC_PLL1_EN;
- delay_ms(2);
- }
- static void flush_tx_fifo(int num)
- {
- otg->grstctl = OTG_GRSTCTL_TXFFLSH | OTG_GRSTCTL_TXFNUM(num);
- do { delay_us(1); } while (otg->grstctl & OTG_GRSTCTL_TXFFLSH);
- }
- static void flush_rx_fifo(void)
- {
- otg->grstctl = OTG_GRSTCTL_RXFFLSH;
- do { delay_us(1); } while (otg->grstctl & OTG_GRSTCTL_RXFFLSH);
- }
- static void ep0_out_start(void)
- {
- otg_doep[0].tsiz = (OTG_DOEPTSZ_STUPCNT |
- OTG_DOEPTSZ_PKTCNT(1)) |
- OTG_DOEPTSZ_XFERSIZ(3*8);
- }
- static bool_t is_high_speed(void)
- {
- /* 0 == DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ */
- return ((otgd->dsts >> 1) & 3) == 0;
- }
- static void prepare_rx(uint8_t epnr)
- {
- struct ep *ep = &eps[epnr];
- OTG_DOEP doep = &otg_doep[epnr];
- uint16_t mps, nr;
- uint32_t tsiz;
- if (ep->rx_active)
- return;
- nr = ep->rxp - ep->rxc;
- nr = ep->rx_nr - nr;
- if (nr <= ep->rx_nr/2)
- return;
- mps = (epnr == 0) ? EP0_MPS : (doep->ctl & 0x7ff);
- tsiz = doep->tsiz & 0xe0000000;
- tsiz |= OTG_DOEPTSZ_PKTCNT(nr);
- tsiz |= OTG_DOEPTSZ_XFERSIZ(mps * nr);
- doep->tsiz = tsiz;
- doep->ctl |= OTG_DOEPCTL_CNAK | OTG_DOEPCTL_EPENA;
- ep->rx_active = TRUE;
- }
- static void read_packet(void *p, int len)
- {
- uint32_t *_p = p;
- unsigned int n = (len + 3) / 4;
- while (n--)
- *_p++ = otg_dfifo[0].x[0];
- }
- static void write_packet(const void *p, uint8_t epnr, int len)
- {
- const uint32_t *_p = p;
- unsigned int n = (len + 3) / 4;
- while (n--)
- otg_dfifo[epnr].x[0] = *_p++;
- }
- static void fifos_init(void)
- {
- unsigned int i, base, tx_sz, rx_sz, fifo_sz;
- /* F7 OTG: FS 1.25k FIFO RAM, HS 4k FIFO RAM. */
- fifo_sz = ((conf_port == PORT_FS) ? 0x500 : 0x1000) >> 2;
- rx_sz = fifo_sz / 2;
- tx_sz = fifo_sz / (2 * conf_nr_ep);
- otg->grxfsiz = rx_sz;
- base = rx_sz;
- otg->dieptxf0 = (tx_sz << 16) | base;
- for (i = 1; i < conf_nr_ep; i++) {
- base += tx_sz;
- otg->dieptxf[i-1] = (tx_sz << 16) | base;
- }
- }
- void hw_usb_init(void)
- {
- int i;
- /* Determine which PHY we use based on hardware submodel ID. */
- switch (gw_info.hw_submodel) {
- case F7SM_lightning:
- conf_iface = IFACE_HS_EMBEDDED;
- break;
- default:
- conf_iface = IFACE_FS;
- break;
- }
- /*
- * HAL_PCD_MspInit
- */
- switch (conf_port) {
- case PORT_FS:
- ASSERT(conf_iface == IFACE_FS);
- gpio_set_af(gpioa, 11, 10);
- gpio_set_af(gpioa, 12, 10);
- gpio_configure_pin(gpioa, 11, AFO_pushpull(IOSPD_HIGH));
- gpio_configure_pin(gpioa, 12, AFO_pushpull(IOSPD_HIGH));
- rcc->ahb2enr |= RCC_AHB2ENR_OTGFSEN;
- break;
- case PORT_HS:
- ASSERT((conf_iface == IFACE_FS) || (conf_iface == IFACE_HS_EMBEDDED));
- if (conf_iface == IFACE_FS) {
- gpio_set_af(gpiob, 14, 12);
- gpio_set_af(gpiob, 15, 12);
- gpio_configure_pin(gpiob, 14, AFO_pushpull(IOSPD_HIGH));
- gpio_configure_pin(gpiob, 15, AFO_pushpull(IOSPD_HIGH));
- rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
- } else {
- rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
- rcc->ahb1enr |= RCC_AHB1ENR_OTGHSULPIEN;
- rcc->apb2enr |= RCC_APB2ENR_OTGPHYCEN;
- }
- break;
- default:
- ASSERT(0);
- }
- peripheral_clock_delay();
- /*
- * USB_CoreInit
- */
- if (conf_iface == IFACE_FS) {
- /* Select Full Speed PHY. */
- otg->gusbcfg |= OTG_GUSBCFG_PHYSEL;
- /* PHY selection must be followed by a core reset. */
- core_reset();
- /* Activate FS transceiver. */
- otg->gccfg |= OTG_GCCFG_PWRDWN;
- } else {
- /* Disable the FS transceiver, enable the HS transceiver. */
- otg->gccfg &= ~OTG_GCCFG_PWRDWN;
- otg->gccfg |= OTG_GCCFG_PHYHSEN;
- hsphyc_init();
- core_reset();
- }
- /*
- * USB_SetCurrentMode
- */
- /* Mode select followed by required 25ms delay. */
- otg->gusbcfg |= OTG_GUSBCFG_FDMOD;
- delay_ms(25);
- /*
- * USB_DevInit
- */
- for (i = 0; i < ARRAY_SIZE(otg->dieptxf); i++)
- otg->dieptxf[i] = 0;
- /* Override Vbus sense */
- otg->gccfg &= ~OTG_GCCFG_VBDEN;
- otg->gotgctl |= (OTG_GOTGCTL_BVALOVAL | OTG_GOTGCTL_BVALOEN);
- /* Restart PHY clock. */
- otg_pcgcctl->pcgcctl = 0;
- /* USB_SetDevSpeed */
- if (conf_iface == IFACE_FS) {
- otgd->dcfg = OTG_DCFG_DSPD(DSPD_FULL);
- } else {
- otgd->dcfg = OTG_DCFG_DSPD(DSPD_HIGH);
- }
- flush_tx_fifo(0x10);
- flush_rx_fifo();
- /* Clear endpoints state. */
- otgd->diepmsk = otgd->doepmsk = otgd->daintmsk = 0;
- for (i = 0; i < conf_nr_ep; i++) {
- bool_t ena = !!(otg_diep[i].ctl & OTG_DIEPCTL_EPENA);
- otg_diep[i].ctl =
- (!ena ? 0 : ((i == 0)
- ? OTG_DIEPCTL_SNAK
- : OTG_DIEPCTL_SNAK | OTG_DIEPCTL_EPDIS));
- otg_diep[i].tsiz = 0;
- otg_diep[i].intsts = 0xffff;
- }
- for (i = 0; i < conf_nr_ep; i++) {
- bool_t ena = !!(otg_doep[i].ctl & OTG_DOEPCTL_EPENA);
- otg_doep[i].ctl =
- (!ena ? 0 : ((i == 0)
- ? OTG_DOEPCTL_SNAK
- : OTG_DOEPCTL_SNAK | OTG_DOEPCTL_EPDIS));
- otg_doep[i].tsiz = 0;
- otg_doep[i].intsts = 0xffff;
- }
- otg->gintsts = ~0;
- otg->gintmsk = (OTG_GINT_USBRST |
- OTG_GINT_ENUMDNE |
- OTG_GINT_IEPINT |
- OTG_GINT_OEPINT |
- OTG_GINT_RXFLVL);
- fifos_init();
- /* HAL_PCD_Start, USB_DevConnect */
- otgd->dctl &= ~OTG_DCTL_SDIS;
- delay_ms(3);
- }
- void hw_usb_deinit(void)
- {
- switch (conf_port) {
- case PORT_FS:
- gpio_configure_pin(gpioa, 11, GPI_floating);
- gpio_configure_pin(gpioa, 12, GPI_floating);
- rcc->ahb2enr &= ~RCC_AHB2ENR_OTGFSEN;
- break;
- case PORT_HS:
- gpio_configure_pin(gpiob, 14, GPI_floating);
- gpio_configure_pin(gpiob, 15, GPI_floating);
- rcc->ahb1enr &= ~RCC_AHB1ENR_OTGHSEN;
- break;
- default:
- ASSERT(0);
- }
- }
- int ep_rx_ready(uint8_t epnr)
- {
- struct ep *ep = &eps[epnr];
- return (ep->rxc != ep->rxp) ? ep->rx[RX_MASK(ep, rxc)].count : -1;
- }
- bool_t ep_tx_ready(uint8_t epnr)
- {
- return eps[epnr].tx_ready;
- }
- void usb_read(uint8_t epnr, void *buf, uint32_t len)
- {
- struct ep *ep = &eps[epnr];
- memcpy(buf, ep->rx[RX_MASK(ep, rxc++)].data, len);
- prepare_rx(epnr);
- }
- void usb_write(uint8_t epnr, const void *buf, uint32_t len)
- {
- OTG_DIEP diep = &otg_diep[epnr];
- diep->tsiz = OTG_DIEPTSIZ_PKTCNT(1) | len;
- // if (len != 0)
- // otgd->diepempmsk |= 1u << epnr;
- diep->ctl |= OTG_DIEPCTL_CNAK | OTG_DIEPCTL_EPENA;
- write_packet(buf, epnr, len);
- eps[epnr].tx_ready = FALSE;
- }
- void usb_stall(uint8_t epnr)
- {
- otg_diep[epnr].ctl |= OTG_DIEPCTL_STALL;
- otg_doep[epnr].ctl |= OTG_DOEPCTL_STALL;
- }
- void usb_configure_ep(uint8_t epnr, uint8_t type, uint32_t size)
- {
- int i;
- struct ep *ep;
- bool_t in = !!(epnr & 0x80);
- epnr &= 0x7f;
- ep = &eps[epnr];
- if (type == EPT_DBLBUF)
- type = EPT_BULK;
- if (in || (epnr == 0)) {
- otgd->daintmsk |= 1u << epnr;
- if (!(otg_diep[epnr].ctl & OTG_DIEPCTL_USBAEP)) {
- otg_diep[epnr].ctl |=
- OTG_DIEPCTL_MPSIZ(size) |
- OTG_DIEPCTL_EPTYP(type) |
- OTG_DIEPCTL_TXFNUM(epnr) |
- OTG_DIEPCTL_SD0PID |
- OTG_DIEPCTL_USBAEP;
- }
- ep->tx_ready = TRUE;
- }
- if (!in) {
- otgd->daintmsk |= 1u << (epnr + 16);
- if (!(otg_doep[epnr].ctl & OTG_DOEPCTL_USBAEP)) {
- otg_doep[epnr].ctl |=
- OTG_DOEPCTL_MPSIZ(size) |
- OTG_DOEPCTL_EPTYP(type) |
- OTG_DIEPCTL_SD0PID |
- OTG_DOEPCTL_USBAEP;
- }
- ep->rxc = ep->rxp = 0;
- if (epnr == 0) {
- ep->rx = rx_buf0;
- ep->rx_nr = ARRAY_SIZE(rx_buf0);
- } else {
- /* We have one statically-allocated multi-packet buffer.
- * Check we aren't trying to map it to multiple endpoints. */
- ep->rx = NULL;
- for (i = 0; i < conf_nr_ep; i++)
- ASSERT(ep->rx != rx_bufn);
- ep->rx = rx_bufn;
- ep->rx_nr = ARRAY_SIZE(rx_bufn);
- }
- ep->rxc = ep->rxp = 0;
- ep->rx_active = FALSE;
- prepare_rx(epnr);
- }
- }
- void usb_setaddr(uint8_t addr)
- {
- otgd->dcfg = (otgd->dcfg & ~OTG_DCFG_DAD(0x7f)) | OTG_DCFG_DAD(addr);
- }
- static void handle_reset(void)
- {
- int i;
- /* Initialise core. */
- otgd->dctl &= ~OTG_DCTL_RWUSIG;
- flush_tx_fifo(0x10);
- for (i = 0; i < conf_nr_ep; i++) {
- otg_diep[i].ctl &= ~(OTG_DIEPCTL_STALL |
- OTG_DIEPCTL_USBAEP |
- OTG_DIEPCTL_MPSIZ(0x7ff) |
- OTG_DIEPCTL_TXFNUM(0xf) |
- OTG_DIEPCTL_SD0PID |
- OTG_DIEPCTL_EPTYP(3));
- otg_doep[i].ctl &= ~(OTG_DOEPCTL_STALL |
- OTG_DOEPCTL_USBAEP |
- OTG_DOEPCTL_MPSIZ(0x7ff) |
- OTG_DOEPCTL_SD0PID |
- OTG_DOEPCTL_EPTYP(3));
- }
- otgd->daintmsk = 0x10001u;
- otgd->doepmsk |= (OTG_DOEPMSK_STUPM |
- OTG_DOEPMSK_XFRCM |
- OTG_DOEPMSK_EPDM |
- OTG_DOEPMSK_STSPHSRXM |
- OTG_DOEPMSK_NAKM);
- otgd->diepmsk |= (OTG_DIEPMSK_TOM |
- OTG_DIEPMSK_XFRCM |
- OTG_DIEPMSK_EPDM);
- /* Set address 0. */
- otgd->dcfg &= ~OTG_DCFG_DAD(0x7f);
- ep0_out_start();
- /* Reinitialise class-specific subsystem. */
- usb_cdc_acm_ops.reset();
- /* Clear endpoint soft state. */
- memset(eps, 0, sizeof(eps));
- /* Clear any in-progress Control Transfer. */
- ep0.data_len = -1;
- ep0.tx.todo = -1;
- }
- static void handle_rx_transfer(void)
- {
- uint32_t grxsts = otg->grxstsp;
- unsigned int bcnt = OTG_RXSTS_BCNT(grxsts);
- unsigned int epnr = OTG_RXSTS_CHNUM(grxsts);
- unsigned int rxp;
- struct ep *ep = &eps[epnr];
- switch (OTG_RXSTS_PKTSTS(grxsts)) {
- case STS_SETUP_UPDT:
- bcnt = 8;
- case STS_DATA_UPDT:
- ASSERT(ep->rx_active);
- ASSERT((uint16_t)(ep->rxp - ep->rxc) < ep->rx_nr);
- rxp = RX_MASK(ep, rxp++);
- read_packet(ep->rx[rxp].data, bcnt);
- ep->rx[rxp].count = bcnt;
- break;
- default:
- break;
- }
- }
- static void handle_oepint(uint8_t epnr)
- {
- uint32_t oepint = otg_doep[epnr].intsts & otgd->doepmsk;
- struct ep *ep = &eps[epnr];
- otg_doep[epnr].intsts = oepint;
- if (oepint & OTG_DOEPMSK_XFRCM) {
- ASSERT(ep->rx_active);
- ep->rx_active = FALSE;
- if (epnr == 0)
- handle_rx_ep0(FALSE);
- }
- if (oepint & OTG_DOEPMSK_STUPM) {
- ASSERT(ep->rx_active);
- ep->rx_active = FALSE;
- if (epnr == 0)
- handle_rx_ep0(TRUE);
- }
- prepare_rx(epnr);
- }
- static void handle_iepint(uint8_t epnr)
- {
- uint32_t iepint = otg_diep[epnr].intsts, iepmsk;
- iepmsk = otgd->diepmsk | (((otgd->diepempmsk >> epnr) & 1) << 7);
- iepint = otg_diep[epnr].intsts & iepmsk;
- otg_diep[epnr].intsts = iepint;
- if (iepint & OTG_DIEPINT_XFRC) {
- otgd->diepempmsk &= ~(1 << epnr);
- eps[epnr].tx_ready = TRUE;
- if (epnr == 0)
- handle_tx_ep0();
- }
- if (iepint & OTG_DIEPINT_TXFE) {
- ASSERT(0);
- }
- }
- void usb_process(void)
- {
- uint32_t gintsts = otg->gintsts & otg->gintmsk;
- if (gintsts & OTG_GINT_OEPINT) {
- uint16_t mask = (otgd->daint & otgd->daintmsk) >> 16;
- int epnr;
- for (epnr = 0; mask != 0; mask >>= 1, epnr++) {
- if (mask & 1)
- handle_oepint(epnr);
- }
- }
- if (gintsts & OTG_GINT_IEPINT) {
- uint16_t mask = otgd->daint & otgd->daintmsk;
- int epnr;
- for (epnr = 0; mask != 0; mask >>= 1, epnr++) {
- if (mask & 1)
- handle_iepint(epnr);
- }
- }
- if (gintsts & OTG_GINT_ENUMDNE) {
- /* USB_ActivateSetup */
- otg_diep[0].ctl &= ~OTG_DIEPCTL_MPSIZ(0x7ff);
- otgd->dctl |= OTG_DCTL_CGINAK;
- /* USB_SetTurnaroundTime */
- is_hs = is_high_speed();
- usb_bulk_mps = is_hs ? USB_HS_MPS : USB_FS_MPS;
- /* Ref. Table 232, FS Mode */
- otg->gusbcfg |= OTG_GUSBCFG_TRDT(is_hs ? 9 : 6);
- usb_configure_ep(0, EPT_CONTROL, EP0_MPS);
- otg->gintsts = OTG_GINT_ENUMDNE;
- printk("[ENUMDNE: %cS]\n", is_hs ? 'H' : 'F');
- }
- if (gintsts & OTG_GINT_USBRST) {
- printk("[USBRST]\n");
- handle_reset();
- otg->gintsts = OTG_GINT_USBRST;
- }
- if (gintsts & OTG_GINT_RXFLVL) {
- handle_rx_transfer();
- }
- }
- /*
- * Local variables:
- * mode: C
- * c-file-style: "Linux"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
|