From b7f761313c11d94b89b605f81a89ced34a57c603 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Thu, 8 Feb 2018 08:34:51 +0200 Subject: Fix data usb data toggle sync problem USB control transfers are structured as the following. For incoming transfers Setup (Data0 out) Data (Data1/Data0 in) - starting with data 1 Status (Data1 out) For outgoing transfers Setup (Data0 out) Data (Data1/Data0 out) - starting with data 1 Status (Data1 in) The in buffers (device to host) are always correctly synchronized, since they can always be reset to Data1 each setup packet without any synchronization problems. The problem occured for outgoing transfers (host to device). For incoming transfers the data banks always alternates, and will automatically stay in sync. Outgoing transfers also stays in sync when there's an odd number of data packets. However when the number is even, including zero, then the last packet received by the device will be data0 and the next setup packet also has to be data0, so there's a synchronization problem. This itself is not a problem since data toggle synchronization(DTS) is ignored for setup packets, however if the follwoing packet after that is also an out packet, then the data bank will be wrong and the packet dropped. In this case the USB spec don't allow sending a nack, so it will only recover after a timeout, when the host tries to send a new setup packet. The old code tried to take care of this situation by reinitializing both data banks when a setup packet is received. The problem is that the next packet might already have been received or is in progress of being received at this point, so the fixup comes to late. The new code does the fixup when a status packet is about to be sent from the device to avoid this problem. --- os/hal/ports/KINETIS/LLD/hal_usb_lld.c | 57 ++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 16 deletions(-) (limited to 'os/hal/ports/KINETIS/LLD/hal_usb_lld.c') diff --git a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c index e8d9778..ba91d24 100644 --- a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c +++ b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c @@ -158,7 +158,7 @@ void usb_packet_transmit(USBDriver *usbp, usbep_t ep, size_t n) USBInEndpointState *isp = epc->in_state; bd_t *bd = (bd_t *)&_bdt[BDT_INDEX(ep, TX, isp->odd_even)]; - + if (n > (size_t)epc->in_maxsize) n = (size_t)epc->in_maxsize; @@ -244,19 +244,16 @@ OSAL_IRQ_HANDLER(KINETIS_USB_IRQ_VECTOR) { { case BDT_PID_SETUP: // SETUP { - /* Clear any pending IN stuff */ - _bdt[BDT_INDEX(ep, TX, EVEN)].desc = 0; - _bdt[BDT_INDEX(ep, TX, ODD)].desc = 0; - /* Also in the chibios state machine */ + /* Clear receiving in the chibios state machine */ (usbp)->receiving &= ~1; - /* After a SETUP, IN is always DATA1 */ - usbp->epc[ep]->in_state->data_bank = DATA1; - - /* Call SETUP function (ChibiOS core), which sends back stuff */ + /* Call SETUP function (ChibiOS core), which prepares + * for send or receive and releases the buffer + */ _usb_isr_invoke_setup_cb(usbp, ep); - /* Buffer is released by the above callback. */ - /* from Paul: "unfreeze the USB, now that we're ready" */ - USB0->CTL = USBx_CTL_USBENSOFEN; + /* When a setup packet is received, tx is suspended, + * so it needs to be resumed here. + */ + USB0->CTL &= ~USBx_CTL_TXSUSPENDTOKENBUSY; } break; case BDT_PID_IN: // IN { @@ -728,9 +725,23 @@ void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { } /* Release the buffer * Setup packet is always DATA0 - * Initialize buffers so current expects DATA0 & opposite DATA1 */ + * Release the current DATA0 buffer + */ bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0); - _bdt[BDT_INDEX(ep, RX, os->odd_even^ODD)].desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); + /* If DATA1 was expected, then the states are out of sync. + * So reset the other buffer too, and set it as DATA1. + * This should not happen in normal cases, but is possible in + * error situations. NOTE: it's possible that this is too late + * and the next packet has already been received and dropped, but + * there's nothing that we can do about that anymore at this point. + */ + if (os->data_bank == DATA1) + { + bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, os->odd_even^ODD)]; + bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); + } + /* After a SETUP, both in and out are always DATA1 */ + usbp->epc[ep]->in_state->data_bank = DATA1; os->data_bank = DATA1; } @@ -762,8 +773,22 @@ void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { * @notapi */ void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { - (void)usbp; - (void)ep; + if (ep == 0 && usbp->ep0state == USB_EP0_IN_SENDING_STS) { + /* When a status packet is about to be sent on endpoint 0 the + * next packet will be a setup packet, which means that the + * buffer we expect after this should be DATA0, and the following + * DATA1. Since no out packets should be in flight at this time + * it's safe to initialize the buffers according to the expectations + * here. + */ + const USBEndpointConfig* epc = usbp->epc[ep]; + bd_t * bd = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even)]; + bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even^ODD)]; + + bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); + bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0); + epc->out_state->data_bank = DATA0; + } usb_packet_transmit(usbp,ep,usbp->epc[ep]->in_state->txsize); } -- cgit v1.2.3 From 7506c2a2c158513ab3db9d382495e089725d79f3 Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Tue, 1 May 2018 00:46:42 -0700 Subject: support Teensy 3.6 --- os/hal/ports/KINETIS/LLD/hal_usb_lld.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'os/hal/ports/KINETIS/LLD/hal_usb_lld.c') diff --git a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c index e8d9778..77421c8 100644 --- a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c +++ b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c @@ -398,15 +398,21 @@ void usb_lld_init(void) { #if KINETIS_USB_USE_USB0 + /* Set USB clock source to MCGPLLCLK, MCGFLLCLK, USB1 PFD, or IRC48M */ SIM->SOPT2 |= SIM_SOPT2_USBSRC; -#if defined(K20x5) || defined(K20x7) +#if defined(K20x5) || defined(K20x7) || defined(MK66F18) #if KINETIS_MCG_MODE == KINETIS_MCG_MODE_FEI /* MCGOUTCLK is the SYSCLK frequency, so don't divide for USB clock */ SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); +#if defined(MK66F18) + /* Switch from default MCGPLLCLK to IRC48M for USB */ + SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3); +#endif + #elif KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE #define KINETIS_USBCLK_FREQUENCY 48000000UL -- cgit v1.2.3 From 0fbddccd80d3f7f27d2d9286b5e280b53a9e8bd0 Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Tue, 1 May 2018 03:26:28 -0700 Subject: disable voltage inrush regulator --- os/hal/ports/KINETIS/LLD/hal_usb_lld.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'os/hal/ports/KINETIS/LLD/hal_usb_lld.c') diff --git a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c index 77421c8..7b7aa0e 100644 --- a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c +++ b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c @@ -461,9 +461,15 @@ void usb_lld_start(USBDriver *usbp) { _bdt[i].addr=0; } +#if defined(MK66F18) + /* Disable the USB current limiter */ + SIM->USBPHYCTL |= SIM_USBPHYCTL_USBDISILIM; +#endif + /* Enable Clock */ #if KINETIS_USB0_IS_USBOTG SIM->SCGC4 |= SIM_SCGC4_USBOTG; + #else /* KINETIS_USB0_IS_USBOTG */ SIM->SCGC4 |= SIM_SCGC4_USBFS; #endif /* KINETIS_USB0_IS_USBOTG */ -- cgit v1.2.3 From 5bec0d7abc33169ef468e194297853a4d7f6ba79 Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Wed, 2 May 2018 06:49:29 -0700 Subject: Always use IRC48M clock for USB on MK66F18 --- os/hal/ports/KINETIS/LLD/hal_usb_lld.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'os/hal/ports/KINETIS/LLD/hal_usb_lld.c') diff --git a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c index fee91c5..694f5e2 100644 --- a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c +++ b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c @@ -405,11 +405,6 @@ void usb_lld_init(void) { /* MCGOUTCLK is the SYSCLK frequency, so don't divide for USB clock */ SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); -#if defined(MK66F18) - /* Switch from default MCGPLLCLK to IRC48M for USB */ - SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3); -#endif - #elif KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE #define KINETIS_USBCLK_FREQUENCY 48000000UL @@ -429,6 +424,12 @@ void usb_lld_init(void) { #error USB clock setting not implemented for this KINETIS_MCG_MODE #endif /* KINETIS_MCG_MODE == ... */ +#if defined(MK66F18) + /* Switch from default MCGPLLCLK to IRC48M for USB */ + SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); + SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3); +#endif + #elif defined(KL25) || defined (KL26) || defined(KL27) /* No extra clock dividers for USB clock */ -- cgit v1.2.3 From 8f7e1d18b943d1e2ba808f9a8e1e79cc78b6044e Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Wed, 2 May 2018 07:53:01 -0700 Subject: Always use IRC48M clock for USB on MK66F18 --- os/hal/ports/KINETIS/LLD/hal_usb_lld.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'os/hal/ports/KINETIS/LLD/hal_usb_lld.c') diff --git a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c index 694f5e2..a01b92e 100644 --- a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c +++ b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c @@ -407,6 +407,8 @@ void usb_lld_init(void) { #elif KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE +#if !defined(MK66F18) + /* Note: We don't need this for MK66F18, we can use IRC48M clock for USB */ #define KINETIS_USBCLK_FREQUENCY 48000000UL uint32_t i,j; for(i=0; i < 2; i++) { @@ -419,6 +421,7 @@ void usb_lld_init(void) { } usbfrac_match_found: osalDbgAssert(i<2 && j <8,"USB Init error"); +#endif #else /* KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE */ #error USB clock setting not implemented for this KINETIS_MCG_MODE -- cgit v1.2.3