From d5a880807b47188aa5d721f4aff491bc46c6f186 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 22 Mar 2016 17:51:46 +0000 Subject: [KINETIS] HAL cleanup/update. New entries in kinetis_registry.h to support new MCUs. Moved registers to ext/CMSIS, like it is done for STM32. Move the same drivers to LLD. Add USB driver. --- os/hal/ports/KINETIS/LLD/ext_lld.c | 73 +++ os/hal/ports/KINETIS/LLD/gpt_lld.c | 391 ++++++++++++++++ os/hal/ports/KINETIS/LLD/gpt_lld.h | 333 ++++++++++++++ os/hal/ports/KINETIS/LLD/i2c_lld.c | 5 +- os/hal/ports/KINETIS/LLD/i2c_lld.h | 30 +- os/hal/ports/KINETIS/LLD/pal_lld.c | 245 ++++++++++ os/hal/ports/KINETIS/LLD/pal_lld.h | 386 ++++++++++++++++ os/hal/ports/KINETIS/LLD/serial_lld.c | 583 ++++++++++++++++++++++++ os/hal/ports/KINETIS/LLD/serial_lld.h | 220 +++++++++ os/hal/ports/KINETIS/LLD/st_lld.c | 98 ++++ os/hal/ports/KINETIS/LLD/st_lld.h | 156 +++++++ os/hal/ports/KINETIS/LLD/usb_lld.c | 832 ++++++++++++++++++++++++++++++++++ os/hal/ports/KINETIS/LLD/usb_lld.h | 428 +++++++++++++++++ 13 files changed, 3776 insertions(+), 4 deletions(-) create mode 100644 os/hal/ports/KINETIS/LLD/gpt_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/gpt_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/pal_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/pal_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/serial_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/serial_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/st_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/st_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/usb_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/usb_lld.h (limited to 'os/hal/ports/KINETIS/LLD') diff --git a/os/hal/ports/KINETIS/LLD/ext_lld.c b/os/hal/ports/KINETIS/LLD/ext_lld.c index e85f032..21bb6e0 100644 --- a/os/hal/ports/KINETIS/LLD/ext_lld.c +++ b/os/hal/ports/KINETIS/LLD/ext_lld.c @@ -89,6 +89,19 @@ static void ext_lld_exti_irq_enable(void) { #if KINETIS_EXT_PORTA_WIDTH > 0 nvicEnableVector(PINA_IRQn, KINETIS_EXT_PORTA_IRQ_PRIORITY); #endif + +#if KINETIS_EXT_HAS_COMMON_BCDE_IRQ +#if (KINETIS_EXT_PORTB_WIDTH > 0) || (KINETIS_EXT_PORTC_WIDTH > 0) \ + || (KINETIS_EXT_PORTD_WIDTH > 0) || (KINETIS_EXT_PORTE_WIDTH > 0) + nvicEnableVector(PINBCDE_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY); +#endif + +#elif KINETIS_EXT_HAS_COMMON_CD_IRQ /* KINETIS_EXT_HAS_COMMON_BCDE_IRQ */ +#if (KINETIS_EXT_PORTC_WIDTH > 0) || (KINETIS_EXT_PORTD_WIDTH > 0) + nvicEnableVector(PINCD_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY); +#endif + +#else /* KINETIS_EXT_HAS_COMMON_CD_IRQ */ #if KINETIS_EXT_PORTB_WIDTH > 0 nvicEnableVector(PINB_IRQn, KINETIS_EXT_PORTB_IRQ_PRIORITY); #endif @@ -101,6 +114,7 @@ static void ext_lld_exti_irq_enable(void) { #if KINETIS_EXT_PORTE_WIDTH > 0 nvicEnableVector(PINE_IRQn, KINETIS_EXT_PORTE_IRQ_PRIORITY); #endif +#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ } /** @@ -113,6 +127,19 @@ static void ext_lld_exti_irq_disable(void) { #if KINETIS_EXT_PORTA_WIDTH > 0 nvicDisableVector(PINA_IRQn); #endif + +#if KINETIS_EXT_HAS_COMMON_BCDE_IRQ +#if (KINETIS_EXT_PORTB_WIDTH > 0) || (KINETIS_EXT_PORTC_WIDTH > 0) \ + || (KINETIS_EXT_PORTD_WIDTH > 0) || (KINETIS_EXT_PORTE_WIDTH > 0) + nvicDisableVector(PINBCDE_IRQn); +#endif + +#elif KINETIS_EXT_HAS_COMMON_CD_IRQ /* KINETIS_EXT_HAS_COMMON_BCDE_IRQ */ +#if (KINETIS_EXT_PORTC_WIDTH > 0) || (KINETIS_EXT_PORTD_WIDTH > 0) + nvicDisableVector(PINCD_IRQn); +#endif + +#else /* KINETIS_EXT_HAS_COMMON_CD_IRQ */ #if KINETIS_EXT_PORTB_WIDTH > 0 nvicDisableVector(PINB_IRQn); #endif @@ -125,6 +152,7 @@ static void ext_lld_exti_irq_disable(void) { #if KINETIS_EXT_PORTE_WIDTH > 0 nvicDisableVector(PINE_IRQn); #endif +#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ } /*===========================================================================*/ @@ -164,6 +192,49 @@ OSAL_IRQ_HANDLER(KINETIS_PORTA_IRQ_VECTOR) { } #endif /* KINETIS_EXT_PORTA_WIDTH > 0 */ +#if KINETIS_EXT_HAS_COMMON_BCDE_IRQ + +#if defined(KINETIS_PORTD_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + +#if (KINETIS_EXT_PORTB_WIDTH > 0) + irq_handler(PORTB, KINETIS_EXT_PORTB_WIDTH, portb_channel_map); +#endif +#if (KINETIS_EXT_PORTC_WIDTH > 0) + irq_handler(PORTC, KINETIS_EXT_PORTC_WIDTH, portc_channel_map); +#endif +#if (KINETIS_EXT_PORTD_WIDTH > 0) + irq_handler(PORTD, KINETIS_EXT_PORTD_WIDTH, portd_channel_map); +#endif +#if (KINETIS_EXT_PORTE_WIDTH > 0) + irq_handler(PORTE, KINETIS_EXT_PORTE_WIDTH, porte_channel_map); +#endif + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_PORTD_IRQ_VECTOR) */ + +#elif KINETIS_EXT_HAS_COMMON_CD_IRQ /* KINETIS_EXT_HAS_COMMON_BCDE_IRQ */ + +#if defined(KINETIS_PORTD_IRQ_VECTOR) +OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + +#if (KINETIS_EXT_PORTC_WIDTH > 0) + irq_handler(PORTC, KINETIS_EXT_PORTC_WIDTH, portc_channel_map); +#endif +#if (KINETIS_EXT_PORTD_WIDTH > 0) + irq_handler(PORTD, KINETIS_EXT_PORTD_WIDTH, portd_channel_map); +#endif + + OSAL_IRQ_EPILOGUE(); +} +#endif /* defined(KINETIS_PORTD_IRQ_VECTOR) */ + + +#else /* KINETIS_EXT_HAS_COMMON_CD_IRQ */ + /** * @brief PORTB interrupt handler. * @@ -224,6 +295,8 @@ OSAL_IRQ_HANDLER(KINETIS_PORTE_IRQ_VECTOR) { } #endif /* KINETIS_EXT_PORTE_WIDTH > 0 */ +#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ + /*===========================================================================*/ /* Driver exported functions. */ /*===========================================================================*/ diff --git a/os/hal/ports/KINETIS/LLD/gpt_lld.c b/os/hal/ports/KINETIS/LLD/gpt_lld.c new file mode 100644 index 0000000..6e88f88 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/gpt_lld.c @@ -0,0 +1,391 @@ +/* + ChibiOS - Copyright (C) 2014 Derek Mulcahy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KINETIS/gpt_lld.c + * @brief KINETIS GPT subsystem low level driver source. + * + * @addtogroup GPT + * @{ + */ + +#include "hal.h" + +#if HAL_USE_GPT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief GPTD1 driver identifier. + * @note The driver GPTD1 allocates the complex timer PIT0 when enabled. + */ +#if KINETIS_GPT_USE_PIT0 || defined(__DOXYGEN__) +GPTDriver GPTD1; +#endif + +/** + * @brief GPTD2 driver identifier. + * @note The driver GPTD2 allocates the timer PIT1 when enabled. + */ +#if KINETIS_GPT_USE_PIT1 || defined(__DOXYGEN__) +GPTDriver GPTD2; +#endif + +/** + * @brief GPTD3 driver identifier. + * @note The driver GPTD3 allocates the timer PIT2 when enabled. + */ +#if KINETIS_GPT_USE_PIT2 || defined(__DOXYGEN__) +GPTDriver GPTD3; +#endif + +/** + * @brief GPTD4 driver identifier. + * @note The driver GPTD4 allocates the timer PIT3 when enabled. + */ +#if KINETIS_GPT_USE_PIT3 || defined(__DOXYGEN__) +GPTDriver GPTD4; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#if KINETIS_HAS_PIT_COMMON_IRQ +static uint8_t active_channels = 0; +#endif /* KINETIS_HAS_PIT_COMMON_IRQ */ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Shared IRQ handler. + * + * @param[in] gptp pointer to a @p GPTDriver object + */ +static void gpt_lld_serve_interrupt(GPTDriver *gptp) { + + /* Clear the interrupt */ + gptp->channel->TFLG |= PIT_TFLGn_TIF; + + if (gptp->state == GPT_ONESHOT) { + gptp->state = GPT_READY; /* Back in GPT_READY state. */ + gpt_lld_stop_timer(gptp); /* Timer automatically stopped. */ + } + gptp->config->callback(gptp); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if !KINETIS_HAS_PIT_COMMON_IRQ + +#if KINETIS_GPT_USE_PIT0 +/** + * @brief PIT1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_PIT0_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + gpt_lld_serve_interrupt(&GPTD1); + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_GPT_USE_PIT0 */ + +#if KINETIS_GPT_USE_PIT1 +/** + * @brief PIT1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_PIT1_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + gpt_lld_serve_interrupt(&GPTD2); + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_GPT_USE_PIT1 */ + +#if KINETIS_GPT_USE_PIT2 +/** + * @brief PIT2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_PIT2_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + gpt_lld_serve_interrupt(&GPTD3); + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_GPT_USE_PIT2 */ + +#if KINETIS_GPT_USE_PIT3 +/** + * @brief PIT3 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_PIT3_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + gpt_lld_serve_interrupt(&GPTD4); + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_GPT_USE_PIT3 */ + +#else /* !KINETIS_HAS_PIT_COMMON_IRQ */ +/** + * @brief Common PIT interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_PIT_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); +#if KINETIS_GPT_USE_PIT0 + if(GPTD1.channel->TFLG & PIT_TFLGn_TIF) + gpt_lld_serve_interrupt(&GPTD1); +#endif /* KINETIS_GPT_USE_PIT0 */ +#if KINETIS_GPT_USE_PIT1 + if(GPTD2.channel->TFLG & PIT_TFLGn_TIF) + gpt_lld_serve_interrupt(&GPTD2); +#endif /* KINETIS_GPT_USE_PIT1 */ +#if KINETIS_GPT_USE_PIT2 + if(GPTD3.channel->TFLG & PIT_TFLGn_TIF) + gpt_lld_serve_interrupt(&GPTD3); +#endif /* KINETIS_GPT_USE_PIT2 */ +#if KINETIS_GPT_USE_PIT3 + if(GPTD4.channel->TFLG & PIT_TFLGn_TIF) + gpt_lld_serve_interrupt(&GPTD4); +#endif /* KINETIS_GPT_USE_PIT3 */ + OSAL_IRQ_EPILOGUE(); +} + +#endif /* !KINETIS_HAS_PIT_COMMON_IRQ */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level GPT driver initialization. + * + * @notapi + */ +void gpt_lld_init(void) { + +#if KINETIS_GPT_USE_PIT0 + /* Driver initialization.*/ + GPTD1.channel = &PIT->CHANNEL[0]; + gptObjectInit(&GPTD1); +#endif + +#if KINETIS_GPT_USE_PIT1 + /* Driver initialization.*/ + GPTD2.channel = &PIT->CHANNEL[1]; + gptObjectInit(&GPTD2); +#endif + +#if KINETIS_GPT_USE_PIT2 + /* Driver initialization.*/ + GPTD3.channel = &PIT->CHANNEL[2]; + gptObjectInit(&GPTD3); +#endif + +#if KINETIS_GPT_USE_PIT3 + /* Driver initialization.*/ + GPTD4.channel = &PIT->CHANNEL[3]; + gptObjectInit(&GPTD4); +#endif +} + +/** + * @brief Configures and activates the GPT peripheral. + * + * @param[in] gptp pointer to the @p GPTDriver object + * + * @notapi + */ +void gpt_lld_start(GPTDriver *gptp) { + uint16_t psc; + + if (gptp->state == GPT_STOP) { + /* Clock activation.*/ + SIM->SCGC6 |= SIM_SCGC6_PIT; + gptp->clock = KINETIS_SYSCLK_FREQUENCY; + +#if !KINETIS_HAS_PIT_COMMON_IRQ + +#if KINETIS_GPT_USE_PIT0 + if (&GPTD1 == gptp) { + nvicEnableVector(PITChannel0_IRQn, KINETIS_GPT_PIT0_IRQ_PRIORITY); + } +#endif +#if KINETIS_GPT_USE_PIT1 + if (&GPTD2 == gptp) { + nvicEnableVector(PITChannel1_IRQn, KINETIS_GPT_PIT1_IRQ_PRIORITY); + } +#endif +#if KINETIS_GPT_USE_PIT2 + if (&GPTD3 == gptp) { + nvicEnableVector(PITChannel2_IRQn, KINETIS_GPT_PIT2_IRQ_PRIORITY); + } +#endif +#if KINETIS_GPT_USE_PIT3 + if (&GPTD4 == gptp) { + nvicEnableVector(PITChannel3_IRQn, KINETIS_GPT_PIT3_IRQ_PRIORITY); + } +#endif + +#else /* !KINETIS_HAS_PIT_COMMON_IRQ */ + nvicEnableVector(PIT_IRQn, KINETIS_GPT_PIT_IRQ_PRIORITY); + active_channels++; +#endif /* !KINETIS_HAS_PIT_COMMON_IRQ */ + } + + /* Prescaler value calculation.*/ + psc = (uint16_t)((gptp->clock / gptp->config->frequency) - 1); + osalDbgAssert(((uint32_t)(psc + 1) * gptp->config->frequency) == gptp->clock, + "invalid frequency"); + + /* Enable the PIT */ + PIT->MCR = 0; +} + +/** + * @brief Deactivates the GPT peripheral. + * + * @param[in] gptp pointer to the @p GPTDriver object + * + * @notapi + */ +void gpt_lld_stop(GPTDriver *gptp) { + + if (gptp->state == GPT_READY) { + SIM->SCGC6 &= ~SIM_SCGC6_PIT; + + /* Disable the channel */ + gptp->channel->TCTRL = 0; + + /* Clear pending interrupts */ + gptp->channel->TFLG |= PIT_TFLGn_TIF; + +#if !KINETIS_HAS_PIT_COMMON_IRQ + +#if KINETIS_GPT_USE_PIT0 + if (&GPTD1 == gptp) { + nvicDisableVector(PITChannel0_IRQn); + } +#endif +#if KINETIS_GPT_USE_PIT1 + if (&GPTD2 == gptp) { + nvicDisableVector(PITChannel1_IRQn); + } +#endif +#if KINETIS_GPT_USE_PIT2 + if (&GPTD3 == gptp) { + nvicDisableVector(PITChannel2_IRQn); + } +#endif +#if KINETIS_GPT_USE_PIT3 + if (&GPTD4 == gptp) { + nvicDisableVector(PITChannel3_IRQn); + } +#endif + +#else /* !KINETIS_HAS_PIT_COMMON_IRQ */ + if(--active_channels == 0) + nvicDisableVector(PIT_IRQn); +#endif /* !KINETIS_HAS_PIT_COMMON_IRQ */ + } +} + +/** + * @brief Starts the timer in continuous mode. + * + * @param[in] gptp pointer to the @p GPTDriver object + * @param[in] interval period in ticks + * + * @notapi + */ +void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t interval) { + + /* Clear pending interrupts */ + gptp->channel->TFLG |= PIT_TFLGn_TIF; + + /* Set the interval */ + gpt_lld_change_interval(gptp, interval); + + /* Start the timer */ + gptp->channel->TCTRL |= PIT_TCTRLn_TIE | PIT_TCTRLn_TEN; +} + +/** + * @brief Stops the timer. + * + * @param[in] gptp pointer to the @p GPTDriver object + * + * @notapi + */ +void gpt_lld_stop_timer(GPTDriver *gptp) { + + /* Stop the timer */ + gptp->channel->TCTRL = 0; +} + +/** + * @brief Starts the timer in one shot mode and waits for completion. + * @details This function specifically polls the timer waiting for completion + * in order to not have extra delays caused by interrupt servicing, + * this function is only recommended for short delays. + * + * @param[in] gptp pointer to the @p GPTDriver object + * @param[in] interval time interval in ticks + * + * @notapi + */ +void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval) { + struct PIT_CHANNEL *channel = gptp->channel; + + /* Disable timer and disable interrupts */ + channel->TCTRL = 0; + + /* Clear the interrupt flag */ + channel->TFLG |= PIT_TFLGn_TIF; + + /* Set the interval */ + channel->LDVAL = (gptp->clock / gptp->config->frequency) * interval; + + /* Enable Timer but keep interrupts disabled */ + channel->TCTRL = PIT_TCTRLn_TEN; + + /* Wait for the interrupt flag to be set */ + while (!(channel->TFLG & PIT_TFLGn_TIF)) + ; + + /* Disable timer and disable interrupts */ + channel->TCTRL = 0; +} + +#endif /* HAL_USE_GPT */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/gpt_lld.h b/os/hal/ports/KINETIS/LLD/gpt_lld.h new file mode 100644 index 0000000..5c3e233 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/gpt_lld.h @@ -0,0 +1,333 @@ +/* + ChibiOS - Copyright (C) 2014 Derek Mulcahy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KINETIS/gpt_lld.h + * @brief KINETIS GPT subsystem low level driver header. + * + * @addtogroup GPT + * @{ + */ + +#ifndef _GPT_LLD_H_ +#define _GPT_LLD_H_ + +#if HAL_USE_GPT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief GPTD1 driver enable switch. + * @details If set to @p TRUE the support for GPTD1 is included. + * @note The default is @p TRUE. + */ +#if !defined(KINETIS_GPT_USE_PIT0) || defined(__DOXYGEN__) +#define KINETIS_GPT_USE_PIT0 FALSE +#endif + +/** + * @brief GPTD2 driver enable switch. + * @details If set to @p TRUE the support for GPTD2 is included. + * @note The default is @p TRUE. + */ +#if !defined(KINETIS_GPT_USE_PIT1) || defined(__DOXYGEN__) +#define KINETIS_GPT_USE_PIT1 FALSE +#endif + +/** + * @brief GPTD3 driver enable switch. + * @details If set to @p TRUE the support for GPTD3 is included. + * @note The default is @p TRUE. + */ +#if !defined(KINETIS_GPT_USE_PIT2) || defined(__DOXYGEN__) +#define KINETIS_GPT_USE_PIT2 FALSE +#endif + +/** + * @brief GPTD4 driver enable switch. + * @details If set to @p TRUE the support for GPTD4 is included. + * @note The default is @p TRUE. + */ +#if !defined(KINETIS_GPT_USE_PIT3) || defined(__DOXYGEN__) +#define KINETIS_GPT_USE_PIT3 FALSE +#endif + +/** + * @brief GPTD1 interrupt priority level setting. + */ +#if !defined(KINETIS_GPT_PIT0_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_GPT_PIT0_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD2 interrupt priority level setting. + */ +#if !defined(KINETIS_GPT_PIT1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_GPT_PIT1_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD3 interrupt priority level setting. + */ +#if !defined(KINETIS_GPT_PIT2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_GPT_PIT2_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD4 interrupt priority level setting. + */ +#if !defined(KINETIS_GPT_PIT3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_GPT_PIT3_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD* common interrupt priority level setting. + */ +#if (KINETIS_HAS_PIT_COMMON_IRQ && !defined(KINETIS_GPT_PIT_IRQ_PRIORITY)) \ + || defined(__DOXYGEN__) +#define KINETIS_GPT_PIT_IRQ_PRIORITY 2 +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if KINETIS_GPT_USE_PIT0 && !KINETIS_HAS_PIT0 +#error "PIT0 not present in the selected device" +#endif + +#if KINETIS_GPT_USE_PIT1 && !KINETIS_HAS_PIT1 +#error "PIT1 not present in the selected device" +#endif + +#if KINETIS_GPT_USE_PIT2 && !KINETIS_HAS_PIT2 +#error "PIT2 not present in the selected device" +#endif + +#if KINETIS_GPT_USE_PIT3 && !KINETIS_HAS_PIT3 +#error "PIT3 not present in the selected device" +#endif + +#if !KINETIS_GPT_USE_PIT0 && !KINETIS_GPT_USE_PIT1 && \ + !KINETIS_GPT_USE_PIT2 && !KINETIS_GPT_USE_PIT3 +#error "GPT driver activated but no PIT peripheral assigned" +#endif + +#if KINETIS_GPT_USE_PIT0 && !KINETIS_HAS_PIT_COMMON_IRQ && \ + !OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT0_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to PIT0" +#endif + +#if KINETIS_GPT_USE_PIT1 && !KINETIS_HAS_PIT_COMMON_IRQ && \ + !OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to PIT1" +#endif + +#if KINETIS_GPT_USE_PIT2 && !KINETIS_HAS_PIT_COMMON_IRQ && \ + !OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to PIT2" +#endif + +#if KINETIS_GPT_USE_PIT3 && !KINETIS_HAS_PIT_COMMON_IRQ && \ + !OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to PIT3" +#endif + +#if KINETIS_HAS_PIT_COMMON_IRQ && \ + !OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to PIT" +#endif + +#if KINETIS_GPT_USE_PIT0 && !defined(KINETIS_PIT0_IRQ_VECTOR) && \ + !KINETIS_HAS_PIT_COMMON_IRQ +#error "KINETIS_PIT0_IRQ_VECTOR not defined" +#endif + +#if KINETIS_GPT_USE_PIT1 && !defined(KINETIS_PIT1_IRQ_VECTOR) && \ + !KINETIS_HAS_PIT_COMMON_IRQ +#error "KINETIS_PIT1_IRQ_VECTOR not defined" +#endif + +#if KINETIS_GPT_USE_PIT2 && !defined(KINETIS_PIT2_IRQ_VECTOR) && \ + !KINETIS_HAS_PIT_COMMON_IRQ +#error "KINETIS_PIT2_IRQ_VECTOR not defined" +#endif + +#if KINETIS_GPT_USE_PIT3 && !defined(KINETIS_PIT3_IRQ_VECTOR) && \ + !KINETIS_HAS_PIT_COMMON_IRQ +#error "KINETIS_PIT3_IRQ_VECTOR not defined" +#endif + +#if KINETIS_HAS_PIT_COMMON_IRQ && !defined(KINETIS_PIT_IRQ_VECTOR) +#error "KINETIS_PIT_IRQ_VECTOR not defined" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief GPT frequency type. + */ +typedef uint32_t gptfreq_t; + +/** + * @brief GPT counter type. + */ +typedef uint32_t gptcnt_t; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Timer clock in Hz. + * @note The low level can use assertions in order to catch invalid + * frequency specifications. + */ + gptfreq_t frequency; + /** + * @brief Timer callback pointer. + * @note This callback is invoked on GPT counter events. + * @note This callback can be set to @p NULL but in that case the + * one-shot mode cannot be used. + */ + gptcallback_t callback; + /* End of the mandatory fields.*/ +} GPTConfig; + +/** + * @brief Structure representing a GPT driver. + */ +struct GPTDriver { + /** + * @brief Driver state. + */ + gptstate_t state; + /** + * @brief Current configuration data. + */ + const GPTConfig *config; +#if defined(GPT_DRIVER_EXT_FIELDS) + GPT_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Timer base clock. + */ + uint32_t clock; + /** + * @brief Channel structure in PIT registers block. + */ + struct PIT_CHANNEL *channel; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Changes the interval of GPT peripheral. + * @details This function changes the interval of a running GPT unit. + * @pre The GPT unit must be running in continuous mode. + * @post The GPT unit interval is changed to the new value. + * @note The function has effect at the next cycle start. + * + * @param[in] gptp pointer to a @p GPTDriver object + * @param[in] interval new cycle time in timer ticks + * + * @notapi + */ +#define gpt_lld_change_interval(gptp, interval) \ + ((gptp)->channel->LDVAL = (uint32_t)( \ + ( (gptp)->clock / (gptp)->config->frequency ) * \ + ( interval ) )) + +/** + * @brief Returns the interval of GPT peripheral. + * @pre The GPT unit must be running in continuous mode. + * + * @param[in] gptp pointer to a @p GPTDriver object + * @return The current interval. + * + * @notapi + */ +#define gpt_lld_get_interval(gptp) \ + ((uint32_t)( ( (uint64_t)(gptp)->channel->LDVAL * (gptp)->config->frequency ) / \ + ( (uint32_t)(gptp)->clock ) )) + +/** + * @brief Returns the counter value of GPT peripheral. + * @pre The GPT unit must be running in continuous mode. + * @note The nature of the counter is not defined, it may count upward + * or downward, it could be continuously running or not. + * + * @param[in] gptp pointer to a @p GPTDriver object + * @return The current counter value. + * + * @notapi + */ +#define gpt_lld_get_counter(gptp) ((gptcnt_t)(gptp)->pit->CHANNEL[gptp->channel].CVAL) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if KINETIS_GPT_USE_PIT0 && !defined(__DOXYGEN__) +extern GPTDriver GPTD1; +#endif + +#if KINETIS_GPT_USE_PIT1 && !defined(__DOXYGEN__) +extern GPTDriver GPTD2; +#endif + +#if KINETIS_GPT_USE_PIT2 && !defined(__DOXYGEN__) +extern GPTDriver GPTD3; +#endif + +#if KINETIS_GPT_USE_PIT3 && !defined(__DOXYGEN__) +extern GPTDriver GPTD4; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void gpt_lld_init(void); + void gpt_lld_start(GPTDriver *gptp); + void gpt_lld_stop(GPTDriver *gptp); + void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t period); + void gpt_lld_stop_timer(GPTDriver *gptp); + void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_GPT */ + +#endif /* _GPT_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/i2c_lld.c b/os/hal/ports/KINETIS/LLD/i2c_lld.c index 4e42d16..3659a93 100644 --- a/os/hal/ports/KINETIS/LLD/i2c_lld.c +++ b/os/hal/ports/KINETIS/LLD/i2c_lld.c @@ -15,7 +15,7 @@ */ /** - * @file KINETIS/i2c_lld.c + * @file KINETIS/LLD/i2c_lld.c * @brief KINETIS I2C subsystem low level driver source. * * @addtogroup I2C @@ -191,8 +191,7 @@ OSAL_IRQ_HANDLER(KINETIS_I2C0_IRQ_VECTOR) { #if KINETIS_I2C_USE_I2C1 || defined(__DOXYGEN__) -/* FIXME: KL2x has I2C1 on Vector64; K2x don't have I2C1! */ -OSAL_IRQ_HANDLER(Vector64) { +OSAL_IRQ_HANDLER(KINETIS_I2C1_IRQ_VECTOR) { OSAL_IRQ_PROLOGUE(); serve_interrupt(&I2CD2); diff --git a/os/hal/ports/KINETIS/LLD/i2c_lld.h b/os/hal/ports/KINETIS/LLD/i2c_lld.h index 11de3ae..5f1ed87 100644 --- a/os/hal/ports/KINETIS/LLD/i2c_lld.h +++ b/os/hal/ports/KINETIS/LLD/i2c_lld.h @@ -15,7 +15,7 @@ */ /** - * @file KINETIS/i2c_lld.h + * @file KINETIS/LLD/i2c_lld.h * @brief KINETIS I2C subsystem low level driver header. * * @addtogroup I2C @@ -63,10 +63,38 @@ #endif /** @} */ +/** + * @brief I2C0 interrupt priority level setting. + */ +#if !defined(KINETIS_I2C_I2C0_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_I2C_I2C0_PRIORITY 12 +#endif + +/** + * @brief I2C1 interrupt priority level setting. + */ +#if !defined(KINETIS_I2C_I2C1_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_I2C_I2C1_PRIORITY 12 +#endif + /*===========================================================================*/ /* Derived constants and error checks. */ /*===========================================================================*/ +/** @brief error checks */ +#if KINETIS_I2C_USE_I2C0 && !KINETIS_HAS_I2C0 +#error "I2C0 not present in the selected device" +#endif + +#if KINETIS_I2C_USE_I2C1 && !KINETIS_HAS_I2C1 +#error "I2C1 not present in the selected device" +#endif + + +#if !(KINETIS_I2C_USE_I2C0 || KINETIS_I2C_USE_I2C1) +#error "I2C driver activated but no I2C peripheral assigned" +#endif + /*===========================================================================*/ /* Driver data structures and types. */ /*===========================================================================*/ diff --git a/os/hal/ports/KINETIS/LLD/pal_lld.c b/os/hal/ports/KINETIS/LLD/pal_lld.c new file mode 100644 index 0000000..b307833 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/pal_lld.c @@ -0,0 +1,245 @@ +/* + ChibiOS - Copyright (C) 2014-2015 Fabio Utzig + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KINETIS/LLD/pal_lld.c + * @brief PAL subsystem low level driver. + * + * @addtogroup PAL + * @{ + */ + +#include "osal.h" +#include "hal.h" + +#if HAL_USE_PAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Reads a logical state from an I/O pad. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @return The logical state. + * @retval PAL_LOW low logical state. + * @retval PAL_HIGH high logical state. + * + * @notapi + */ +uint8_t _pal_lld_readpad(ioportid_t port, + uint8_t pad) { + + return (port->PDIR & ((uint32_t) 1 << pad)) ? PAL_HIGH : PAL_LOW; +} + +/** + * @brief Writes a logical state on an output pad. + * @note This function is not meant to be invoked directly by the + * application code. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] bit logical value, the value must be @p PAL_LOW or + * @p PAL_HIGH + * + * @notapi + */ +void _pal_lld_writepad(ioportid_t port, + uint8_t pad, + uint8_t bit) { + + if (bit == PAL_HIGH) + port->PDOR |= ((uint32_t) 1 << pad); + else + port->PDOR &= ~((uint32_t) 1 << pad); +} + +/** + * @brief Pad mode setup. + * @details This function programs a pad with the specified mode. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * @note Programming an unknown or unsupported mode is silently ignored. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] mode pad mode + * + * @notapi + */ +void _pal_lld_setpadmode(ioportid_t port, + uint8_t pad, + iomode_t mode) { + + PORT_TypeDef *portcfg = NULL; + + chDbgAssert(pad < PADS_PER_PORT, "pal_lld_setpadmode() #1, invalid pad"); + + if (mode == PAL_MODE_OUTPUT_PUSHPULL) + port->PDDR |= ((uint32_t) 1 << pad); + else + port->PDDR &= ~((uint32_t) 1 << pad); + + if (port == IOPORT1) + portcfg = PORTA; + else if (port == IOPORT2) + portcfg = PORTB; + else if (port == IOPORT3) + portcfg = PORTC; + else if (port == IOPORT4) + portcfg = PORTD; + else if (port == IOPORT5) + portcfg = PORTE; + + chDbgAssert(portcfg != NULL, "pal_lld_setpadmode() #2, invalid port"); + + switch (mode) { + case PAL_MODE_RESET: + case PAL_MODE_INPUT: + case PAL_MODE_OUTPUT_PUSHPULL: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1); + break; +#if KINETIS_GPIO_HAS_OPENDRAIN + case PAL_MODE_OUTPUT_OPENDRAIN: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1) | + PORTx_PCRn_ODE; + break; +#else +#undef PAL_MODE_OUTPUT_OPENDRAIN +#endif + case PAL_MODE_INPUT_PULLUP: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1) | + PORTx_PCRn_PE | + PORTx_PCRn_PS; + break; + case PAL_MODE_INPUT_PULLDOWN: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1) | + PORTx_PCRn_PE; + break; + case PAL_MODE_UNCONNECTED: + case PAL_MODE_INPUT_ANALOG: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(0); + break; + case PAL_MODE_ALTERNATIVE_1: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1); + break; + case PAL_MODE_ALTERNATIVE_2: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(2); + break; + case PAL_MODE_ALTERNATIVE_3: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(3); + break; + case PAL_MODE_ALTERNATIVE_4: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(4); + break; + case PAL_MODE_ALTERNATIVE_5: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(5); + break; + case PAL_MODE_ALTERNATIVE_6: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(6); + break; + case PAL_MODE_ALTERNATIVE_7: + portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(7); + break; + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Kinetis I/O ports configuration. + * @details Ports A-E clocks enabled. + * + * @param[in] config the Kinetis ports configuration + * + * @notapi + */ +void _pal_lld_init(const PALConfig *config) { + + int i, j; + + /* Enable clocking on all Ports */ + SIM->SCGC5 |= SIM_SCGC5_PORTA | + SIM_SCGC5_PORTB | + SIM_SCGC5_PORTC | + SIM_SCGC5_PORTD | + SIM_SCGC5_PORTE; + + /* Initial PORT and GPIO setup */ + for (i = 0; i < TOTAL_PORTS; i++) { + for (j = 0; j < PADS_PER_PORT; j++) { + pal_lld_setpadmode(config->ports[i].port, + j, + config->ports[i].pads[j]); + } + } +} + +/** + * @brief Pads mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @notapi + */ +void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode) { + + int i; + + (void)mask; + + for (i = 0; i < PADS_PER_PORT; i++) { + pal_lld_setpadmode(port, i, mode); + } +} + +#endif /* HAL_USE_PAL */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/pal_lld.h b/os/hal/ports/KINETIS/LLD/pal_lld.h new file mode 100644 index 0000000..2bd9872 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/pal_lld.h @@ -0,0 +1,386 @@ +/* + ChibiOS - Copyright (C) 2014-2015 Fabio Utzig + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KINETIS/LLD/pal_lld.h + * @brief PAL subsystem low level driver header. + * + * @addtogroup PAL + * @{ + */ + +#ifndef _PAL_LLD_H_ +#define _PAL_LLD_H_ + +#if HAL_USE_PAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Unsupported modes and specific modes */ +/*===========================================================================*/ + +#define PAL_MODE_ALTERNATIVE_1 0x10 +#define PAL_MODE_ALTERNATIVE_2 0x11 +#define PAL_MODE_ALTERNATIVE_3 0x12 +#define PAL_MODE_ALTERNATIVE_4 0x13 +#define PAL_MODE_ALTERNATIVE_5 0x14 +#define PAL_MODE_ALTERNATIVE_6 0x15 +#define PAL_MODE_ALTERNATIVE_7 0x16 + +#define PIN_MUX_ALTERNATIVE(x) PORTx_PCRn_MUX(x) + +/*===========================================================================*/ +/* I/O Ports Types and constants. */ +/*===========================================================================*/ + +#define TOTAL_PORTS 5 +#define PADS_PER_PORT 32 + +/** + * @brief Width, in bits, of an I/O port. + */ +#define PAL_IOPORTS_WIDTH 32 + +/** + * @brief Whole port mask. + * @brief This macro specifies all the valid bits into a port. + */ +#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFFFFF) + +/** + * @brief Digital I/O port sized unsigned type. + */ +typedef uint32_t ioportmask_t; + +/** + * @brief Digital I/O modes. + */ +typedef uint32_t iomode_t; + +/** + * @brief Port Identifier. + * @details This type can be a scalar or some kind of pointer, do not make + * any assumption about it, use the provided macros when populating + * variables of this type. + */ +typedef GPIO_TypeDef *ioportid_t; + +/** + * @brief Port Configuration. + * @details This structure stores the configuration parameters of all pads + * belonging to a port. + */ +typedef struct { + ioportid_t port; + iomode_t pads[PADS_PER_PORT]; +} PortConfig; + +/** + * @brief Generic I/O ports static initializer. + * @details An instance of this structure must be passed to @p palInit() at + * system startup time in order to initialized the digital I/O + * subsystem. This represents only the initial setup, specific pads + * or whole ports can be reprogrammed at later time. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ +typedef struct { + PortConfig ports[TOTAL_PORTS]; +} PALConfig; + +/*===========================================================================*/ +/* I/O Ports Identifiers. */ +/*===========================================================================*/ + +/** + * @brief GPIO port A identifier. + */ +#define IOPORT1 GPIOA + +/** + * @brief GPIO port B identifier. + */ +#define IOPORT2 GPIOB + +/** + * @brief GPIO port C identifier. + */ +#define IOPORT3 GPIOC + +/** + * @brief GPIO port D identifier. + */ +#define IOPORT4 GPIOD + +/** + * @brief GPIO port E identifier. + */ +#define IOPORT5 GPIOE + +/*===========================================================================*/ +/* Implementation, some of the following macros could be implemented as */ +/* functions, if so please put them in pal_lld.c. */ +/*===========================================================================*/ + +/** + * @brief Low level PAL subsystem initialization. + * + * @param[in] config architecture-dependent ports configuration + * + * @notapi + */ +#define pal_lld_init(config) _pal_lld_init(config) + +/** + * @brief Reads the physical I/O port states. + * + * @param[in] port port identifier + * @return The port bits. + * + * @notapi + */ +#define pal_lld_readport(port) \ + (port)->PDIR + +/** + * @brief Reads the output latch. + * @details The purpose of this function is to read back the latched output + * value. + * + * @param[in] port port identifier + * @return The latched logical states. + * + * @notapi + */ +#define pal_lld_readlatch(port) \ + (port)->PDOR + +/** + * @brief Writes a bits mask on a I/O port. + * + * @param[in] port port identifier + * @param[in] bits bits to be written on the specified port + * + * @notapi + */ +#define pal_lld_writeport(port, bits) \ + (port)->PDOR = (bits) + +/** + * @brief Sets a bits mask on a I/O port. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] bits bits to be ORed on the specified port + * + * @notapi + */ +#define pal_lld_setport(port, bits) \ + (port)->PSOR = (bits) + +/** + * @brief Clears a bits mask on a I/O port. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] bits bits to be cleared on the specified port + * + * @notapi + */ +#define pal_lld_clearport(port, bits) \ + (port)->PCOR = (bits) + +/** + * @brief Toggles a bits mask on a I/O port. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] bits bits to be toggled on the specified port + * + * @notapi + */ +#define pal_lld_toggleport(port, bits) \ + (port)->PTOR = (bits) + +/** + * @brief Reads a group of bits. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset group bit offset within the port + * @return The group logical states. + * + * @notapi + */ +#define pal_lld_readgroup(port, mask, offset) 0 + +/** + * @brief Writes a group of bits. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset group bit offset within the port + * @param[in] bits bits to be written. Values exceeding the group width + * are masked. + * + * @notapi + */ +#define pal_lld_writegroup(port, mask, offset, bits) (void)bits + +/** + * @brief Pads group mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * @note Programming an unknown or unsupported mode is silently ignored. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset group bit offset within the port + * @param[in] mode group mode + * + * @notapi + */ +#define pal_lld_setgroupmode(port, mask, offset, mode) \ + _pal_lld_setgroupmode(port, mask << offset, mode) + +/** + * @brief Reads a logical state from an I/O pad. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @return The logical state. + * @retval PAL_LOW low logical state. + * @retval PAL_HIGH high logical state. + * + * @notapi + */ +#define pal_lld_readpad(port, pad) _pal_lld_readpad(port, pad) + +/** + * @brief Writes a logical state on an output pad. + * @note This function is not meant to be invoked directly by the + * application code. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] bit logical value, the value must be @p PAL_LOW or + * @p PAL_HIGH + * + * @notapi + */ +#define pal_lld_writepad(port, pad, bit) _pal_lld_writepad(port, pad, bit) + +/** + * @brief Sets a pad logical state to @p PAL_HIGH. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_setpad(port, pad) (port)->PSOR = ((uint32_t) 1 << (pad)) + +/** + * @brief Clears a pad logical state to @p PAL_LOW. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_clearpad(port, pad) (port)->PCOR = ((uint32_t) 1 << (pad)) + +/** + * @brief Toggles a pad logical state. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_togglepad(port, pad) (port)->PTOR = ((uint32_t) 1 << (pad)) + +/** + * @brief Pad mode setup. + * @details This function programs a pad with the specified mode. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * @note Programming an unknown or unsupported mode is silently ignored. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] mode pad mode + * + * @notapi + */ +#define pal_lld_setpadmode(port, pad, mode) \ + _pal_lld_setpadmode(port, pad, mode) + +#if !defined(__DOXYGEN__) +extern const PALConfig pal_default_config; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void _pal_lld_init(const PALConfig *config); + void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode); + void _pal_lld_setpadmode(ioportid_t port, + uint8_t pad, + iomode_t mode); + uint8_t _pal_lld_readpad(ioportid_t port, + uint8_t pad); + void _pal_lld_writepad(ioportid_t port, + uint8_t pad, + uint8_t bit); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_PAL */ + +#endif /* _PAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/serial_lld.c b/os/hal/ports/KINETIS/LLD/serial_lld.c new file mode 100644 index 0000000..c80cf22 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/serial_lld.c @@ -0,0 +1,583 @@ +/* + ChibiOS - Copyright (C) 2013-2015 Fabio Utzig + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KL2x/serial_lld.c + * @brief Kinetis KL2x Serial Driver subsystem low level driver source. + * + * @addtogroup SERIAL + * @{ + */ + +#include "osal.h" +#include "hal.h" + +#if HAL_USE_SERIAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief SD1 driver identifier. + */ +#if KINETIS_SERIAL_USE_UART0 || defined(__DOXYGEN__) +SerialDriver SD1; +#endif + +#if KINETIS_SERIAL_USE_UART1 || defined(__DOXYGEN__) +SerialDriver SD2; +#endif + +#if KINETIS_SERIAL_USE_UART2 || defined(__DOXYGEN__) +SerialDriver SD3; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief Driver default configuration. + */ +static const SerialConfig default_config = { + 38400 +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ +/** + * @brief Error handling routine. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] isr UART s1 register value + */ +static void set_error(SerialDriver *sdp, uint8_t s1) { + eventflags_t sts = 0; + + if (s1 & UARTx_S1_OR) + sts |= SD_OVERRUN_ERROR; + if (s1 & UARTx_S1_PF) + sts |= SD_PARITY_ERROR; + if (s1 & UARTx_S1_FE) + sts |= SD_FRAMING_ERROR; + if (s1 & UARTx_S1_NF) + sts |= SD_NOISE_ERROR; + osalSysLockFromISR(); + chnAddFlagsI(sdp, sts); + osalSysUnlockFromISR(); +} + +/** + * @brief Common error IRQ handler. + * + * @param[in] sdp communication channel associated to the UART + */ +static void serve_error_interrupt(SerialDriver *sdp) { + UART_w_TypeDef *u = &(sdp->uart); + uint8_t s1 = *(u->s1_p); + + /* S1 bits are write-1-to-clear for UART0 on KL2x. */ + /* Clearing on K20x and KL2x/UART>0 is done by reading S1 and + * then reading D.*/ + +#if defined(KL2x) && KINETIS_SERIAL_USE_UART0 + if(sdp == &SD1) { + if(s1 & UARTx_S1_IDLE) { + *(u->s1_p) |= UARTx_S1_IDLE; + } + + if(s1 & (UARTx_S1_OR | UARTx_S1_NF | UARTx_S1_FE | UARTx_S1_PF)) { + set_error(sdp, s1); + *(u->s1_p) |= UARTx_S1_OR | UARTx_S1_NF | UARTx_S1_FE | UARTx_S1_PF; + } + return; + } +#endif /* KL2x && KINETIS_SERIAL_USE_UART0 */ + + if(s1 & UARTx_S1_IDLE) { + (void)*(u->d_p); + } + + if(s1 & (UARTx_S1_OR | UARTx_S1_NF | UARTx_S1_FE | UARTx_S1_PF)) { + set_error(sdp, s1); + (void)*(u->d_p); + } +} + +/** + * @brief Common IRQ handler. + * @note Tries hard to clear all the pending interrupt sources, we don't + * want to go through the whole ISR and have another interrupt soon + * after. + * + * @param[in] sdp communication channel associated to the UART + */ +static void serve_interrupt(SerialDriver *sdp) { + UART_w_TypeDef *u = &(sdp->uart); + uint8_t s1 = *(u->s1_p); + + if (s1 & UARTx_S1_RDRF) { + osalSysLockFromISR(); + if (iqIsEmptyI(&sdp->iqueue)) + chnAddFlagsI(sdp, CHN_INPUT_AVAILABLE); + if (iqPutI(&sdp->iqueue, *(u->d_p)) < Q_OK) + chnAddFlagsI(sdp, SD_OVERRUN_ERROR); + osalSysUnlockFromISR(); + } + + if (s1 & UARTx_S1_TDRE) { + msg_t b; + + osalSysLockFromISR(); + b = oqGetI(&sdp->oqueue); + osalSysUnlockFromISR(); + + if (b < Q_OK) { + osalSysLockFromISR(); + chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); + osalSysUnlockFromISR(); + *(u->c2_p) &= ~UARTx_C2_TIE; + } else { + *(u->d_p) = b; + } + } + + serve_error_interrupt(sdp); +} + +/** + * @brief Attempts a TX preload + */ +static void preload(SerialDriver *sdp) { + UART_w_TypeDef *u = &(sdp->uart); + + if (*(u->s1_p) & UARTx_S1_TDRE) { + msg_t b = oqGetI(&sdp->oqueue); + if (b < Q_OK) { + chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); + return; + } + *(u->d_p) = b; + *(u->c2_p) |= UARTx_C2_TIE; + } +} + +/** + * @brief Driver output notification. + */ +#if KINETIS_SERIAL_USE_UART0 || defined(__DOXYGEN__) +static void notify1(io_queue_t *qp) +{ + (void)qp; + preload(&SD1); +} +#endif + +#if KINETIS_SERIAL_USE_UART1 || defined(__DOXYGEN__) +static void notify2(io_queue_t *qp) +{ + (void)qp; + preload(&SD2); +} +#endif + +#if KINETIS_SERIAL_USE_UART2 || defined(__DOXYGEN__) +static void notify3(io_queue_t *qp) +{ + (void)qp; + preload(&SD3); +} +#endif + +/** + * @brief Common UART configuration. + * + */ +static void configure_uart(SerialDriver *sdp, const SerialConfig *config) { + + UART_w_TypeDef *uart = &(sdp->uart); + uint32_t divisor; + + /* Discard any incoming data. */ + while (*(uart->s1_p) & UARTx_S1_RDRF) { + (void)*(uart->d_p); + } + + /* Disable UART while configuring */ + *(uart->c2_p) &= ~(UARTx_C2_RE | UARTx_C2_TE); + + /* The clock sources for various UARTs can be different. */ + divisor=KINETIS_BUSCLK_FREQUENCY; + +#if defined(KL2x) + +#if KINETIS_SERIAL_USE_UART0 + if (sdp == &SD1) { + /* UART0 can be clocked from several sources on KL2x. */ + divisor = KINETIS_UART0_CLOCK_FREQ; + /* FIXME: change fixed OSR = 16 to dynamic value based on baud */ + /* Note: OSR only works on KL2x/UART0; further UARTs have fixed 16. */ + *(uart->c4_p) = UARTx_C4_OSR(16 - 1); + } +#endif /* KINETIS_SERIAL_USE_UART0 */ + +#elif defined(K20x) /* KL2x */ + + /* UARTs 0 and 1 are clocked from SYSCLK, others from BUSCLK on K20x. */ +#if KINETIS_SERIAL_USE_UART0 + if(sdp == &SD1) + divisor = KINETIS_SYSCLK_FREQUENCY; +#endif /* KINETIS_SERIAL_USE_UART0 */ +#if KINETIS_SERIAL_USE_UART1 + if(sdp == &SD2) + divisor = KINETIS_SYSCLK_FREQUENCY; +#endif /* KINETIS_SERIAL_USE_UART1 */ + +#else /* K20x */ +#error Baud rate selection not implemented for this MCU type +#endif /* K20x */ + + divisor = (divisor * 2 + 1) / config->sc_speed; + + *(uart->bdh_p) = UARTx_BDH_SBR(divisor >> 13) | (*(uart->bdh_p) & ~UARTx_BDH_SBR_MASK); + *(uart->bdl_p) = UARTx_BDL_SBR(divisor >> 5); +#if defined(K20x) + *(uart->c4_p) = UARTx_C4_BRFA(divisor) | (*(uart->c4_p) & ~UARTx_C4_BRFA_MASK); +#endif /* K20x */ + + /* Line settings. */ + *(uart->c1_p) = 0; + /* Enable error event interrupts (overrun, noise, framing, parity) */ + *(uart->c3_p) = UARTx_C3_ORIE | UARTx_C3_NEIE | UARTx_C3_FEIE | UARTx_C3_PEIE; + /* Enable the peripheral; including receive interrupts. */ + *(uart->c2_p) |= UARTx_C2_RE | UARTx_C2_RIE | UARTx_C2_TE; +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if KINETIS_SERIAL_USE_UART0 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL0_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&SD1); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_SERIAL_USE_UART1 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL1_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&SD2); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_SERIAL_USE_UART2 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL2_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&SD3); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_HAS_SERIAL_ERROR_IRQ + +#if KINETIS_SERIAL_USE_UART0 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL0_ERROR_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_error_interrupt(&SD1); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_SERIAL_USE_UART1 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL1_ERROR_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_error_interrupt(&SD2); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_SERIAL_USE_UART2 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL2_ERROR_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_error_interrupt(&SD3); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level serial driver initialization. + * + * @notapi + */ +void sd_lld_init(void) { + +#if KINETIS_SERIAL_USE_UART0 + /* Driver initialization.*/ + sdObjectInit(&SD1, NULL, notify1); +#if ! KINETIS_SERIAL0_IS_LPUART + SD1.uart.bdh_p = &(UART0->BDH); + SD1.uart.bdl_p = &(UART0->BDL); + SD1.uart.c1_p = &(UART0->C1); + SD1.uart.c2_p = &(UART0->C2); + SD1.uart.c3_p = &(UART0->C3); + SD1.uart.c4_p = &(UART0->C4); + SD1.uart.s1_p = (volatile uint8_t *)&(UART0->S1); + SD1.uart.s2_p = &(UART0->S2); + SD1.uart.d_p = &(UART0->D); +#else /* ! KINETIS_SERIAL0_IS_LPUART */ + /* little endian! */ + SD1.uart.bdh_p = ((uint8_t *)&(LPUART0->BAUD)) + 1; /* BDH: BAUD, byte 3 */ + SD1.uart.bdl_p = ((uint8_t *)&(LPUART0->BAUD)) + 0; /* BDL: BAUD, byte 4 */ + SD1.uart.c1_p = ((uint8_t *)&(LPUART0->CTRL)) + 0; /* C1: CTRL, byte 4 */ + SD1.uart.c2_p = ((uint8_t *)&(LPUART0->CTRL)) + 2; /* C2: CTRL, byte 2 */ + SD1.uart.c3_p = ((uint8_t *)&(LPUART0->CTRL)) + 3; /* C3: CTRL, byte 1 */ + SD1.uart.c4_p = ((uint8_t *)&(LPUART0->BAUD)) + 3; /* C4: BAUD, byte 1 */ + SD1.uart.s1_p = ((uint8_t *)&(LPUART0->STAT)) + 2; /* S1: STAT, byte 2 */ + SD1.uart.s2_p = ((uint8_t *)&(LPUART0->STAT)) + 3; /* S2: STAT, byte 1 */ + SD1.uart.d_p = ((uint8_t *)&(LPUART0->DATA)) + 0; /* D: DATA, byte 4 */ +#endif /* ! KINETIS_SERIAL0_IS_LPUART */ +#if KINETIS_SERIAL0_IS_UARTLP + SD1.uart.uartlp_p = UART0; + SD1.uart.uart_p = NULL; +#elif KINETIS_SERIAL0_IS_LPUART + SD1.uart.lpuart_p = LPUART0; + SD1.uart.uart_p = NULL; +#else /* KINETIS_SERIAL0_IS_LPUART */ + SD1.uart.uart_p = UART0; +#endif /* KINETIS_SERIAL0_IS_LPUART */ +#endif /* KINETIS_SERIAL_USE_UART0 */ + +#if KINETIS_SERIAL_USE_UART1 + /* Driver initialization.*/ + sdObjectInit(&SD2, NULL, notify2); +#if ! KINETIS_SERIAL1_IS_LPUART + SD2.uart.bdh_p = &(UART1->BDH); + SD2.uart.bdl_p = &(UART1->BDL); + SD2.uart.c1_p = &(UART1->C1); + SD2.uart.c2_p = &(UART1->C2); + SD2.uart.c3_p = &(UART1->C3); + SD2.uart.c4_p = &(UART1->C4); + SD2.uart.s1_p = (volatile uint8_t *)&(UART1->S1); + SD2.uart.s2_p = &(UART1->S2); + SD2.uart.d_p = &(UART1->D); + SD2.uart.uart_p = UART1; +#else /* ! KINETIS_SERIAL1_IS_LPUART */ + /* little endian! */ + SD2.uart.bdh_p = ((uint8_t *)&(LPUART1->BAUD)) + 1; /* BDH: BAUD, byte 3 */ + SD2.uart.bdl_p = ((uint8_t *)&(LPUART1->BAUD)) + 0; /* BDL: BAUD, byte 4 */ + SD2.uart.c1_p = ((uint8_t *)&(LPUART1->CTRL)) + 0; /* C1: CTRL, byte 4 */ + SD2.uart.c2_p = ((uint8_t *)&(LPUART1->CTRL)) + 2; /* C2: CTRL, byte 2 */ + SD2.uart.c3_p = ((uint8_t *)&(LPUART1->CTRL)) + 3; /* C3: CTRL, byte 1 */ + SD2.uart.c4_p = ((uint8_t *)&(LPUART1->BAUD)) + 3; /* C4: BAUD, byte 1 */ + SD2.uart.s1_p = ((uint8_t *)&(LPUART1->STAT)) + 2; /* S1: STAT, byte 2 */ + SD2.uart.s2_p = ((uint8_t *)&(LPUART1->STAT)) + 3; /* S2: STAT, byte 1 */ + SD2.uart.d_p = ((uint8_t *)&(LPUART1->DATA)) + 0; /* D: DATA, byte 4 */ + SD2.uart.lpuart_p = LPUART1; + SD2.uart.uart_p = NULL; +#endif /* ! KINETIS_SERIAL1_IS_LPUART */ +#endif /* KINETIS_SERIAL_USE_UART1 */ + +#if KINETIS_SERIAL_USE_UART2 + /* Driver initialization.*/ + sdObjectInit(&SD3, NULL, notify3); + SD3.uart.bdh_p = &(UART2->BDH); + SD3.uart.bdl_p = &(UART2->BDL); + SD3.uart.c1_p = &(UART2->C1); + SD3.uart.c2_p = &(UART2->C2); + SD3.uart.c3_p = &(UART2->C3); + SD3.uart.c4_p = &(UART2->C4); + SD3.uart.s1_p = (volatile uint8_t *)&(UART2->S1); + SD3.uart.s2_p = &(UART2->S2); + SD3.uart.d_p = &(UART2->D); + SD3.uart.uart_p = UART2; +#endif /* KINETIS_SERIAL_USE_UART2 */ +} + +/** + * @brief Low level serial driver configuration and (re)start. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] config the architecture-dependent serial driver configuration. + * If this parameter is set to @p NULL then a default + * configuration is used. + * + * @notapi + */ +void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) { + + if (config == NULL) + config = &default_config; + + if (sdp->state == SD_STOP) { + /* Enables the peripheral.*/ + +#if KINETIS_SERIAL_USE_UART0 + if (sdp == &SD1) { +#if KINETIS_SERIAL0_IS_LPUART + SIM->SCGC5 |= SIM_SCGC5_LPUART0; + SIM->SOPT2 = + (SIM->SOPT2 & ~SIM_SOPT2_LPUART0SRC_MASK) | + SIM_SOPT2_LPUART0SRC(KINETIS_UART0_CLOCK_SRC); +#else /* KINETIS_SERIAL0_IS_LPUART */ + SIM->SCGC4 |= SIM_SCGC4_UART0; +#endif /* KINETIS_SERIAL0_IS_LPUART */ +#if KINETIS_SERIAL0_IS_UARTLP + SIM->SOPT2 = + (SIM->SOPT2 & ~SIM_SOPT2_UART0SRC_MASK) | + SIM_SOPT2_UART0SRC(KINETIS_UART0_CLOCK_SRC); +#endif /* KINETIS_SERIAL0_IS_UARTLP */ + configure_uart(sdp, config); +#if KINETIS_HAS_SERIAL_ERROR_IRQ + nvicEnableVector(UART0Status_IRQn, KINETIS_SERIAL_UART0_PRIORITY); + nvicEnableVector(UART0Error_IRQn, KINETIS_SERIAL_UART0_PRIORITY); +#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */ +#if KINETIS_SERIAL0_IS_LPUART + nvicEnableVector(LPUART0_IRQn, KINETIS_SERIAL_UART0_PRIORITY); +#else /* KINETIS_SERIAL0_IS_LPUART */ + nvicEnableVector(UART0_IRQn, KINETIS_SERIAL_UART0_PRIORITY); +#endif /* KINETIS_SERIAL0_IS_LPUART */ +#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */ + } +#endif /* KINETIS_SERIAL_USE_UART0 */ + +#if KINETIS_SERIAL_USE_UART1 + if (sdp == &SD2) { +#if KINETIS_SERIAL1_IS_LPUART + SIM->SCGC5 |= SIM_SCGC5_LPUART1; + SIM->SOPT2 = + (SIM->SOPT2 & ~SIM_SOPT2_LPUART1SRC_MASK) | + SIM_SOPT2_LPUART1SRC(KINETIS_UART1_CLOCK_SRC); +#else /* KINETIS_SERIAL1_IS_LPUART */ + SIM->SCGC4 |= SIM_SCGC4_UART1; +#endif /* KINETIS_SERIAL1_IS_LPUART */ + configure_uart(sdp, config); +#if KINETIS_HAS_SERIAL_ERROR_IRQ + nvicEnableVector(UART1Status_IRQn, KINETIS_SERIAL_UART1_PRIORITY); + nvicEnableVector(UART1Error_IRQn, KINETIS_SERIAL_UART0_PRIORITY); +#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */ +#if KINETIS_SERIAL1_IS_LPUART + nvicEnableVector(LPUART1_IRQn, KINETIS_SERIAL_UART1_PRIORITY); +#else /* KINETIS_SERIAL1_IS_LPUART */ + nvicEnableVector(UART1_IRQn, KINETIS_SERIAL_UART1_PRIORITY); +#endif /* KINETIS_SERIAL1_IS_LPUART */ +#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */ + } +#endif /* KINETIS_SERIAL_USE_UART1 */ + +#if KINETIS_SERIAL_USE_UART2 + if (sdp == &SD3) { + SIM->SCGC4 |= SIM_SCGC4_UART2; + configure_uart(sdp, config); +#if KINETIS_HAS_SERIAL_ERROR_IRQ + nvicEnableVector(UART2Status_IRQn, KINETIS_SERIAL_UART2_PRIORITY); + nvicEnableVector(UART2Error_IRQn, KINETIS_SERIAL_UART0_PRIORITY); +#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */ + nvicEnableVector(UART2_IRQn, KINETIS_SERIAL_UART2_PRIORITY); +#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */ + } +#endif /* KINETIS_SERIAL_USE_UART2 */ + + } + /* Configures the peripheral.*/ + +} + +/** + * @brief Low level serial driver stop. + * @details De-initializes the USART, stops the associated clock, resets the + * interrupt vector. + * + * @param[in] sdp pointer to a @p SerialDriver object + * + * @notapi + */ +void sd_lld_stop(SerialDriver *sdp) { + + if (sdp->state == SD_READY) { + /* TODO: Resets the peripheral.*/ + +#if KINETIS_SERIAL_USE_UART0 + if (sdp == &SD1) { +#if KINETIS_HAS_SERIAL_ERROR_IRQ + nvicDisableVector(UART0Status_IRQn); + nvicDisableVector(UART0Error_IRQn); +#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */ +#if KINETIS_SERIAL0_IS_LPUART + nvicDisableVector(LPUART0_IRQn); +#else /* KINETIS_SERIAL0_IS_LPUART */ + nvicDisableVector(UART0_IRQn); +#endif /* KINETIS_SERIAL0_IS_LPUART */ +#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */ +#if KINETIS_SERIAL0_IS_LPUART + SIM->SCGC5 &= ~SIM_SCGC5_LPUART0; +#else /* KINETIS_SERIAL0_IS_LPUART */ + SIM->SCGC4 &= ~SIM_SCGC4_UART0; +#endif /* KINETIS_SERIAL0_IS_LPUART */ + } +#endif + +#if KINETIS_SERIAL_USE_UART1 + if (sdp == &SD2) { +#if KINETIS_HAS_SERIAL_ERROR_IRQ + nvicDisableVector(UART1Status_IRQn); + nvicDisableVector(UART1Error_IRQn); +#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */ +#if KINETIS_SERIAL1_IS_LPUART + nvicDisableVector(LPUART1_IRQn); +#else /* KINETIS_SERIAL1_IS_LPUART */ + nvicDisableVector(UART1_IRQn); +#endif /* KINETIS_SERIAL1_IS_LPUART */ +#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */ +#if KINETIS_SERIAL1_IS_LPUART + SIM->SCGC5 &= ~SIM_SCGC5_LPUART1; +#else /* KINETIS_SERIAL1_IS_LPUART */ + SIM->SCGC4 &= ~SIM_SCGC4_UART1; +#endif /* KINETIS_SERIAL1_IS_LPUART */ + } +#endif + +#if KINETIS_SERIAL_USE_UART2 + if (sdp == &SD3) { +#if KINETIS_HAS_SERIAL_ERROR_IRQ + nvicDisableVector(UART2Status_IRQn); + nvicDisableVector(UART2Error_IRQn); +#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */ + nvicDisableVector(UART2_IRQn); +#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */ + SIM->SCGC4 &= ~SIM_SCGC4_UART2; + } +#endif + } +} + +#endif /* HAL_USE_SERIAL */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/serial_lld.h b/os/hal/ports/KINETIS/LLD/serial_lld.h new file mode 100644 index 0000000..cc66eb3 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/serial_lld.h @@ -0,0 +1,220 @@ +/* + ChibiOS - Copyright (C) 2013-2015 Fabio Utzig + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KL2x/serial_lld.h + * @brief Kinetis KL2x Serial Driver subsystem low level driver header. + * + * @addtogroup SERIAL + * @{ + */ + +#ifndef _SERIAL_LLD_H_ +#define _SERIAL_LLD_H_ + +#if HAL_USE_SERIAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SD1 driver enable switch. + * @details If set to @p TRUE the support for SD1 is included. + */ +#if !defined(KINETIS_SERIAL_USE_UART0) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_USE_UART0 FALSE +#endif +/** + * @brief SD2 driver enable switch. + * @details If set to @p TRUE the support for SD2 is included. + */ +#if !defined(KINETIS_SERIAL_USE_UART1) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_USE_UART1 FALSE +#endif +/** + * @brief SD3 driver enable switch. + * @details If set to @p TRUE the support for SD3 is included. + */ +#if !defined(KINETIS_SERIAL_USE_UART2) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_USE_UART2 FALSE +#endif + +/** + * @brief UART0 interrupt priority level setting. + */ +#if !defined(KINETIS_SERIAL_UART0_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_UART0_PRIORITY 12 +#endif + +/** + * @brief UART1 interrupt priority level setting. + */ +#if !defined(KINETIS_SERIAL_UART1_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_UART1_PRIORITY 12 +#endif + +/** + * @brief UART2 interrupt priority level setting. + */ +#if !defined(KINETIS_SERIAL_UART2_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_UART2_PRIORITY 12 +#endif + +/** + * @brief UART0 clock source. + */ +#if !defined(KINETIS_UART0_CLOCK_SRC) || defined(__DOXYGEN__) +#define KINETIS_UART0_CLOCK_SRC 1 /* MCGFLLCLK clock, or MCGPLLCLK/2; or IRC48M */ +#endif + +/** + * @brief UART1 clock source. + */ +#if !defined(KINETIS_UART1_CLOCK_SRC) || defined(__DOXYGEN__) +#define KINETIS_UART1_CLOCK_SRC 1 /* IRC48M */ +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/** @brief error checks */ +#if KINETIS_SERIAL_USE_UART0 && !KINETIS_HAS_SERIAL0 +#error "UART0 not present in the selected device" +#endif + +#if KINETIS_SERIAL_USE_UART1 && !KINETIS_HAS_SERIAL1 +#error "UART1 not present in the selected device" +#endif + +#if KINETIS_SERIAL_USE_UART2 && !KINETIS_HAS_SERIAL2 +#error "UART2 not present in the selected device" +#endif + +#if !(KINETIS_SERIAL_USE_UART0 || KINETIS_SERIAL_USE_UART1 || \ + KINETIS_SERIAL_USE_UART2) +#error "Serial driver activated but no UART peripheral assigned" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Generic Serial Driver configuration structure. + * @details An instance of this structure must be passed to @p sdStart() + * in order to configure and start a serial driver operations. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ +typedef struct { + /** + * @brief Bit rate. + */ + uint32_t sc_speed; + /* End of the mandatory fields.*/ +} SerialConfig; + +/** + * @brief Generic UART register structure. + * @note Individual UART register blocks (even within the same chip) can differ. + */ + +typedef struct { + volatile uint8_t* bdh_p; + volatile uint8_t* bdl_p; + volatile uint8_t* c1_p; + volatile uint8_t* c2_p; + volatile uint8_t* c3_p; + volatile uint8_t* c4_p; + volatile uint8_t* s1_p; + volatile uint8_t* s2_p; + volatile uint8_t* d_p; + UART_TypeDef *uart_p; +#if KINETIS_SERIAL_USE_UART0 && KINETIS_SERIAL0_IS_UARTLP + UARTLP_TypeDef *uartlp_p; +#endif /* KINETIS_SERIAL_USE_UART0 && KINETIS_SERIAL0_IS_UARTLP */ +#if (KINETIS_SERIAL_USE_UART0 && KINETIS_SERIAL0_IS_LPUART) \ + || (KINETIS_SERIAL_USE_UART1 && KINETIS_SERIAL1_IS_LPUART) + LPUART_TypeDef *lpuart_p; +#endif /* KINETIS_SERIAL_USE_UART0 && KINETIS_SERIAL0_IS_LPUART */ +} UART_w_TypeDef; + +/** + * @brief @p SerialDriver specific data. + */ +#define _serial_driver_data \ + _base_asynchronous_channel_data \ + /* Driver state.*/ \ + sdstate_t state; \ + /* Input queue.*/ \ + input_queue_t iqueue; \ + /* Output queue.*/ \ + output_queue_t oqueue; \ + /* Input circular buffer.*/ \ + uint8_t ib[SERIAL_BUFFERS_SIZE]; \ + /* Output circular buffer.*/ \ + uint8_t ob[SERIAL_BUFFERS_SIZE]; \ + /* End of the mandatory fields.*/ \ + /* Pointer to the UART registers block.*/ \ + UART_w_TypeDef uart; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if KINETIS_SERIAL_USE_UART0 && !defined(__DOXYGEN__) +extern SerialDriver SD1; +#endif + +#if KINETIS_SERIAL_USE_UART1 && !defined(__DOXYGEN__) +extern SerialDriver SD2; +#endif + +#if KINETIS_SERIAL_USE_UART2 && !defined(__DOXYGEN__) +extern SerialDriver SD3; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void sd_lld_init(void); + void sd_lld_start(SerialDriver *sdp, const SerialConfig *config); + void sd_lld_stop(SerialDriver *sdp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SERIAL */ + +#endif /* _SERIAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/st_lld.c b/os/hal/ports/KINETIS/LLD/st_lld.c new file mode 100644 index 0000000..e6ed9e5 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/st_lld.c @@ -0,0 +1,98 @@ +/* + ChibiOS - Copyright (C) 2014-2015 Fabio Utzig + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KINETIS/LLD/st_lld.c + * @brief ST Driver subsystem low level driver code. + * + * @addtogroup ST + * @{ + */ + +#include "hal.h" + +#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if (OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC) || defined(__DOXYGEN__) +/** + * @brief System Timer vector. + * @details This interrupt is used for system tick in periodic mode. + * + * @isr + */ +OSAL_IRQ_HANDLER(SysTick_Handler) { + + OSAL_IRQ_PROLOGUE(); + + osalSysLockFromISR(); + osalOsTimerHandlerI(); + osalSysUnlockFromISR(); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ST driver initialization. + * + * @notapi + */ +void st_lld_init(void) { +#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC + /* Periodic systick mode, the Cortex-Mx internal systick timer is used + in this mode.*/ + SysTick->LOAD = (KINETIS_SYSCLK_FREQUENCY / OSAL_ST_FREQUENCY) - 1; + SysTick->VAL = 0; + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_ENABLE_Msk | + SysTick_CTRL_TICKINT_Msk; + + /* IRQ enabled.*/ + nvicSetSystemHandlerPriority(HANDLER_SYSTICK, KINETIS_ST_IRQ_PRIORITY); +#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ +} + +#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/st_lld.h b/os/hal/ports/KINETIS/LLD/st_lld.h new file mode 100644 index 0000000..c67a5d0 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/st_lld.h @@ -0,0 +1,156 @@ +/* + ChibiOS - Copyright (C) 2014-2015 Fabio Utzig + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KINETIS/LLD/st_lld.h + * @brief ST Driver subsystem low level driver header. + * @details This header is designed to be include-able without having to + * include other files from the HAL. + * + * @addtogroup ST + * @{ + */ + +#ifndef _ST_LLD_H_ +#define _ST_LLD_H_ + +#include "mcuconf.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SysTick timer IRQ priority. + */ +#if !defined(KINETIS_ST_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_ST_IRQ_PRIORITY 8 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void st_lld_init(void); +#ifdef __cplusplus +} +#endif + +/*===========================================================================*/ +/* Driver inline functions. */ +/*===========================================================================*/ + +/** + * @brief Returns the time counter value. + * + * @return The counter value. + * + * @notapi + */ +static inline systime_t st_lld_get_counter(void) { + + return (systime_t)0; +} + +/** + * @brief Starts the alarm. + * @note Makes sure that no spurious alarms are triggered after + * this call. + * + * @param[in] time the time to be set for the first alarm + * + * @notapi + */ +static inline void st_lld_start_alarm(systime_t time) { + + (void)time; +} + +/** + * @brief Stops the alarm interrupt. + * + * @notapi + */ +static inline void st_lld_stop_alarm(void) { + +} + +/** + * @brief Sets the alarm time. + * + * @param[in] time the time to be set for the next alarm + * + * @notapi + */ +static inline void st_lld_set_alarm(systime_t time) { + + (void)time; +} + +/** + * @brief Returns the current alarm time. + * + * @return The currently set alarm time. + * + * @notapi + */ +static inline systime_t st_lld_get_alarm(void) { + + return (systime_t)0; +} + +/** + * @brief Determines if the alarm is active. + * + * @return The alarm status. + * @retval false if the alarm is not active. + * @retval true is the alarm is active + * + * @notapi + */ +static inline bool st_lld_is_alarm_active(void) { + + return false; +} + +#endif /* _ST_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/usb_lld.c b/os/hal/ports/KINETIS/LLD/usb_lld.c new file mode 100644 index 0000000..159aef9 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/usb_lld.c @@ -0,0 +1,832 @@ +/* + ChibiOS - Copyright (C) 2015 RedoX https://github.com/RedoXyde/ + (C) 2015-2016 flabbergast + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KINETIS/LLD/usb_lld.c + * @brief KINETIS USB subsystem low level driver source. + * + * @addtogroup USB + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_USB || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USB0 driver identifier.*/ +#if KINETIS_USB_USE_USB0 || defined(__DOXYGEN__) +USBDriver USBD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief IN EP0 state. + */ +USBInEndpointState ep0in; + +/** + * @brief OUT EP0 state. + */ +USBOutEndpointState ep0out; + +/** + * @brief Buffer for the EP0 setup packets. + */ +static uint8_t ep0setup_buffer[8]; + +/** + * @brief EP0 initialization structure. + */ +static const USBEndpointConfig ep0config = { + USB_EP_MODE_TYPE_CTRL, + _usb_ep0setup, + _usb_ep0in, + _usb_ep0out, + 64, + 64, + &ep0in, + &ep0out, + 1, + ep0setup_buffer +}; + +/* + * Buffer Descriptor Table (BDT) + */ + +/* + * Buffer Descriptor (BD) + * */ +typedef struct { + uint32_t desc; + uint8_t* addr; +} bd_t; + +/* + * Buffer Descriptor fields - p.889 + */ +#define BDT_OWN 0x80 +#define BDT_DATA 0x40 +#define BDT_KEEP 0x20 +#define BDT_NINC 0x10 +#define BDT_DTS 0x08 +#define BDT_STALL 0x04 + +#define BDT_DESC(bc, data) (BDT_OWN | BDT_DTS | ((data&0x1)<<6) | ((bc) << 16)) + +/* + * BDT PID - p.891 + */ +#define BDT_PID_OUT 0x01 +#define BDT_PID_IN 0x09 +#define BDT_PID_SETUP 0x0D +#define BDT_TOK_PID(n) (((n)>>2)&0xF) + +/* + * BDT index fields + */ +#define DATA0 0 +#define DATA1 1 + +#define RX 0 +#define TX 1 + +#define EVEN 0 +#define ODD 1 + +#define BDT_INDEX(endpoint, tx, odd) (((endpoint)<<2) | ((tx)<<1) | (odd)) +/* + * Get RX-ed/TX-ed bytes count from BDT entry + */ +#define BDT_BC(n) (((n)>>16)&0x3FF) + +/* The USB-FS needs 2 BDT entry per endpoint direction + * that adds to: 2*2*16 BDT entries for 16 bi-directional EP + */ +static volatile bd_t _bdt[(KINETIS_USB_ENDPOINTS)*2*2] __attribute__((aligned(512))); + +/* FIXME later with dyn alloc + * 16 EP + * 2 directions per EP + * 2 buffer per direction + * => 64 buffers + */ +static uint8_t _usbb[KINETIS_USB_ENDPOINTS*4][64] __attribute__((aligned(4))); +static volatile uint8_t _usbbn=0; +uint8_t* usb_alloc(uint8_t size) +{ + (void)size; + if(_usbbn < (KINETIS_USB_ENDPOINTS)*4) + return _usbb[_usbbn++]; + while(1); /* Should not happen, ever */ +} +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/* Called from locked ISR. */ +void usb_packet_transmit(USBDriver *usbp, usbep_t ep, size_t n) +{ + const USBEndpointConfig *epc = usbp->epc[ep]; + 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; + + /* Copy from buf to _usbb[] */ + size_t i=0; + for(i=0;iaddr[i] = isp->txbuf[i]; + + /* Update the Buffer status */ + bd->desc = BDT_DESC(n, isp->data_bank); + /* Toggle the odd and data bits for next TX */ + isp->data_bank ^= DATA1; + isp->odd_even ^= ODD; +} + +/* Called from locked ISR. */ +void usb_packet_receive(USBDriver *usbp, usbep_t ep, size_t n) +{ + const USBEndpointConfig *epc = usbp->epc[ep]; + USBOutEndpointState *osp = epc->out_state; + + bd_t *bd = (bd_t *)&_bdt[BDT_INDEX(ep, RX, osp->odd_even)]; + + if (n > (size_t)epc->out_maxsize) + n = (size_t)epc->out_maxsize; + + /* Copy from _usbb[] to buf */ + size_t i=0; + for(i=0;irxbuf[i] = bd->addr[i]; + + /* Update the Buffer status + * Set current buffer to same DATA bank and then toggle. + * Since even/odd buffers are ping-pong and setup re-initialized them + * this should work correctly */ + bd->desc = BDT_DESC(epc->out_maxsize, osp->data_bank); + osp->data_bank ^= DATA1; + usb_lld_start_out(usbp, ep); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*============================================================================*/ + +#if KINETIS_USB_USE_USB0 || defined(__DOXYGEN__) +/** + * @brief USB interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_USB_IRQ_VECTOR) { + USBDriver *usbp = &USBD1; + uint8_t istat = USB0->ISTAT; + + OSAL_IRQ_PROLOGUE(); + + /* 04 - Bit2 - Start Of Frame token received */ + if(istat & USBx_ISTAT_SOFTOK) { + _usb_isr_invoke_sof_cb(usbp); + USB0->ISTAT = USBx_ISTAT_SOFTOK; + } + + /* 08 - Bit3 - Token processing completed */ + while(istat & USBx_ISTAT_TOKDNE) { + uint8_t stat = USB0->STAT; + uint8_t ep = stat >> 4; + if(ep > KINETIS_USB_ENDPOINTS) { + OSAL_IRQ_EPILOGUE(); + return; + } + const USBEndpointConfig *epc = usbp->epc[ep]; + + /* Get the correct BDT entry */ + uint8_t odd_even = (stat & USBx_STAT_ODD_MASK) >> USBx_STAT_ODD_SHIFT; + uint8_t tx_rx = (stat & USBx_STAT_TX_MASK) >> USBx_STAT_TX_SHIFT; + bd_t *bd = (bd_t*)&_bdt[BDT_INDEX(ep,tx_rx,odd_even)]; + + /* Update the ODD/EVEN state for RX */ + if(tx_rx == RX && epc->out_state != NULL) + epc->out_state->odd_even = odd_even; + + switch(BDT_TOK_PID(bd->desc)) + { + 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 */ + (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 */ + _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; + } break; + case BDT_PID_IN: // IN + { + if(epc->in_state == NULL) + break; + /* Special case for SetAddress for EP0 */ + if(ep == 0 && (((uint16_t)usbp->setup[0]<<8)|usbp->setup[1]) == 0x0500) + { + usbp->address = usbp->setup[2]; + usb_lld_set_address(usbp); + _usb_isr_invoke_event_cb(usbp, USB_EVENT_ADDRESS); + usbp->state = USB_SELECTED; + } + uint16_t txed = BDT_BC(bd->desc); + epc->in_state->txcnt += txed; + if(epc->in_state->txcnt < epc->in_state->txsize) + { + epc->in_state->txbuf += txed; + osalSysLockFromISR(); + usb_packet_transmit(usbp,ep,epc->in_state->txsize - epc->in_state->txcnt); + osalSysUnlockFromISR(); + } + else + { + if(epc->in_cb != NULL) + _usb_isr_invoke_in_cb(usbp,ep); + } + } break; + case BDT_PID_OUT: // OUT + { + if(epc->out_state == NULL) + break; + uint16_t rxed = BDT_BC(bd->desc); + + osalSysLockFromISR(); + usb_packet_receive(usbp,ep,rxed); + osalSysUnlockFromISR(); + if(rxed) + { + epc->out_state->rxbuf += rxed; + + /* Update transaction data */ + epc->out_state->rxcnt += rxed; + epc->out_state->rxsize -= rxed; + epc->out_state->rxpkts -= 1; + + /* The transaction is completed if the specified number of packets + has been received or the current packet is a short packet.*/ + if ((rxed < epc->out_maxsize) || (epc->out_state->rxpkts == 0)) + { + if(epc->out_cb != NULL) + _usb_isr_invoke_out_cb(usbp, ep); + } + } + } break; + default: + break; + } + USB0->ISTAT = USBx_ISTAT_TOKDNE; + istat = USB0->ISTAT; + } + + /* 01 - Bit0 - Valid USB Reset received */ + if(istat & USBx_ISTAT_USBRST) { + _usb_reset(usbp); + USB0->ISTAT = USBx_ISTAT_USBRST; + OSAL_IRQ_EPILOGUE(); + return; + } + + /* 80 - Bit7 - STALL handshake received */ + if(istat & USBx_ISTAT_STALL) { + USB0->ISTAT = USBx_ISTAT_STALL; + } + + /* 02 - Bit1 - ERRSTAT condition triggered */ + if(istat & USBx_ISTAT_ERROR) { + uint8_t err = USB0->ERRSTAT; + USB0->ERRSTAT = err; + USB0->ISTAT = USBx_ISTAT_ERROR; + } + + /* 10 - Bit4 - Constant IDLE on USB bus detected */ + if(istat & USBx_ISTAT_SLEEP) { + /* This seems to fire a few times before the device is + * configured - need to ignore those occurences somehow. */ + /* The other option would be to only activate INTEN_SLEEPEN + * on CONFIGURED event, but that would need to be done in + * user firmware. */ + if(usbp->state == USB_ACTIVE) { + _usb_suspend(usbp); + /* Enable interrupt on resume */ + USB0->INTEN |= USBx_INTEN_RESUMEEN; + } + + // low-power version (check!): + // enable wakeup interrupt on resume USB signaling + // (check that it was a wakeup int with USBx_USBTRC0_USB_RESUME_INT) + //? USB0->USBTRC0 |= USBx_USBTRC0_USBRESMEN + // suspend the USB module + //? USB0->USBCTRL |= USBx_USBCTRL_SUSP; + + USB0->ISTAT = USBx_ISTAT_SLEEP; + } + + /* 20 - Bit5 - Resume - Only allowed in sleep=suspend mode */ + if(istat & USBx_ISTAT_RESUME) { + /* Disable interrupt on resume (should be disabled + * during normal operation according to datasheet). */ + USB0->INTEN &= ~USBx_INTEN_RESUMEEN; + + // low power version (check!): + // desuspend the USB module + //? USB0->USBCTRL &= ~USBx_USBCTRL_SUSP; + // maybe also + //? USB0->CTL = USBx_CTL_USBENSOFEN; + _usb_wakeup(usbp); + USB0->ISTAT = USBx_ISTAT_RESUME; + } + + /* 40 - Bit6 - ATTACH - used */ + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_USB_USE_USB0 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level USB driver initialization. + * + * @notapi + */ +void usb_lld_init(void) { + /* Driver initialization.*/ + usbObjectInit(&USBD1); + +#if KINETIS_USB_USE_USB0 + + SIM->SOPT2 |= SIM_SOPT2_USBSRC; + +#if defined(K20x5) || defined(K20x7) + +#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); + +#elif KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE + + #define KINETIS_USBCLK_FREQUENCY 48000000UL + uint32_t i,j; + for(i=0; i < 2; i++) { + for(j=0; j < 8; j++) { + if((KINETIS_PLLCLK_FREQUENCY * (i+1)) == (KINETIS_USBCLK_FREQUENCY*(j+1))) { + SIM->CLKDIV2 = i | SIM_CLKDIV2_USBDIV(j); + goto usbfrac_match_found; + } + } + } + usbfrac_match_found: + chDbgAssert(i<2 && j <8,"USB Init error"); + +#else /* KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE */ +#error USB clock setting not implemented for this KINETIS_MCG_MODE +#endif /* KINETIS_MCG_MODE == ... */ + +#elif defined(KL25) || defined (KL26) || defined(KL27) + + /* No extra clock dividers for USB clock */ + +#else /* defined(KL25) || defined (KL26) || defined(KL27) */ +#error USB driver not implemented for your MCU type +#endif + +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Configures and activates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_start(USBDriver *usbp) { + if (usbp->state == USB_STOP) { +#if KINETIS_USB_USE_USB0 + if (&USBD1 == usbp) { + /* Clear BDT */ + uint8_t i; + for(i=0;iSCGC4 |= SIM_SCGC4_USBOTG; +#else /* KINETIS_USB0_IS_USBOTG */ + SIM->SCGC4 |= SIM_SCGC4_USBFS; +#endif /* KINETIS_USB0_IS_USBOTG */ + +#if KINETIS_HAS_USB_CLOCK_RECOVERY + USB0->CLK_RECOVER_IRC_EN |= USBx_CLK_RECOVER_IRC_EN_IRC_EN; + USB0->CLK_RECOVER_CTRL |= USBx_CLK_RECOVER_CTRL_CLOCK_RECOVER_EN; +#endif /* KINETIS_HAS_USB_CLOCK_RECOVERY */ + + /* Reset USB module, wait for completion */ + USB0->USBTRC0 |= USBx_USBTRC0_USBRESET; + while ((USB0->USBTRC0 & USBx_USBTRC0_USBRESET)); + + /* Set BDT Address */ + USB0->BDTPAGE1 = ((uint32_t)_bdt) >> 8; + USB0->BDTPAGE2 = ((uint32_t)_bdt) >> 16; + USB0->BDTPAGE3 = ((uint32_t)_bdt) >> 24; + + /* Clear all ISR flags */ + USB0->ISTAT = 0xFF; + USB0->ERRSTAT = 0xFF; +#if KINETIS_USB0_IS_USBOTG + USB0->OTGISTAT = 0xFF; +#endif /* KINETIS_USB0_IS_USBOTG */ + USB0->USBTRC0 |= 0x40; //a hint was given that this is an undocumented interrupt bit + + /* Enable USB */ + USB0->CTL = USBx_CTL_ODDRST | USBx_CTL_USBENSOFEN; + USB0->USBCTRL = 0; + + /* Enable reset interrupt */ + USB0->INTEN |= USBx_INTEN_USBRSTEN; + + /* Enable interrupt in NVIC */ +#if KINETIS_USB0_IS_USBOTG + nvicEnableVector(USB_OTG_IRQn, KINETIS_USB_USB0_IRQ_PRIORITY); +#else /* KINETIS_USB0_IS_USBOTG */ + nvicEnableVector(USB_IRQn, KINETIS_USB_USB0_IRQ_PRIORITY); +#endif /* KINETIS_USB0_IS_USBOTG */ + } +#endif /* KINETIS_USB_USE_USB0 */ + } +} + +/** + * @brief Deactivates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_stop(USBDriver *usbp) { + /* TODO: If in ready state then disables the USB clock.*/ + if (usbp->state == USB_STOP) { +#if KINETIS_USB_USE_USB0 + if (&USBD1 == usbp) { +#if KINETIS_USB0_IS_USBOTG + nvicDisableVector(USB_OTG_IRQn); +#else /* KINETIS_USB0_IS_USBOTG */ + nvicDisableVector(USB_IRQn); +#endif /* KINETIS_USB0_IS_USBOTG */ + } +#endif /* KINETIS_USB_USE_USB0 */ + } +} + +/** + * @brief USB low level reset routine. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_reset(USBDriver *usbp) { + // FIXME, dyn alloc + _usbbn = 0; + +#if KINETIS_USB_USE_USB0 + + /* Reset BDT ODD/EVEN bits */ + USB0->CTL = USBx_CTL_ODDRST; + + /* EP0 initialization.*/ + usbp->epc[0] = &ep0config; + usb_lld_init_endpoint(usbp, 0); + + /* Clear all pending interrupts */ + USB0->ERRSTAT = 0xFF; + USB0->ISTAT = 0xFF; + + /* Set the address to zero during enumeration */ + usbp->address = 0; + USB0->ADDR = 0; + + /* Enable other interrupts */ + USB0->ERREN = 0xFF; + USB0->INTEN = USBx_INTEN_TOKDNEEN | + USBx_INTEN_SOFTOKEN | + USBx_INTEN_STALLEN | + USBx_INTEN_ERROREN | + USBx_INTEN_USBRSTEN | + USBx_INTEN_SLEEPEN; + + /* "is this necessary?", Paul from PJRC */ + USB0->CTL = USBx_CTL_USBENSOFEN; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Sets the USB address. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_set_address(USBDriver *usbp) { + +#if KINETIS_USB_USE_USB0 + USB0->ADDR = usbp->address&0x7F; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Enables an endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { + + if(ep > KINETIS_USB_ENDPOINTS) + return; + + const USBEndpointConfig *epc = usbp->epc[ep]; + uint8_t mask=0; + + if(epc->out_state != NULL) + { + /* OUT Endpoint */ + epc->out_state->odd_even = EVEN; + epc->out_state->data_bank = DATA0; + /* RXe */ + _bdt[BDT_INDEX(ep, RX, EVEN)].desc = BDT_DESC(epc->out_maxsize, DATA0); + _bdt[BDT_INDEX(ep, RX, EVEN)].addr = usb_alloc(epc->out_maxsize); + /* RXo */ + _bdt[BDT_INDEX(ep, RX, ODD)].desc = BDT_DESC(epc->out_maxsize, DATA1); + _bdt[BDT_INDEX(ep, RX, ODD)].addr = usb_alloc(epc->out_maxsize); + /* Enable OUT direction */ + mask |= USBx_ENDPTn_EPRXEN; + } + if(epc->in_state != NULL) + { + /* IN Endpoint */ + epc->in_state->odd_even = EVEN; + epc->in_state->data_bank = DATA0; + /* TXe, not used yet */ + _bdt[BDT_INDEX(ep, TX, EVEN)].desc = 0; + _bdt[BDT_INDEX(ep, TX, EVEN)].addr = usb_alloc(epc->in_maxsize); + /* TXo, not used yet */ + _bdt[BDT_INDEX(ep, TX, ODD)].desc = 0; + _bdt[BDT_INDEX(ep, TX, ODD)].addr = usb_alloc(epc->in_maxsize); + /* Enable IN direction */ + mask |= USBx_ENDPTn_EPTXEN; + } + + /* EPHSHK should be set for CTRL, BULK, INTR not for ISOC*/ + if((epc->ep_mode & USB_EP_MODE_TYPE) != USB_EP_MODE_TYPE_ISOC) + mask |= USBx_ENDPTn_EPHSHK; + /* Endpoint is not a CTRL endpoint, disable SETUP transfers */ + if((epc->ep_mode & USB_EP_MODE_TYPE) != USB_EP_MODE_TYPE_CTRL) + mask |= USBx_ENDPTn_EPCTLDIS; + +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V = mask; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Disables all the active endpoints except the endpoint zero. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_disable_endpoints(USBDriver *usbp) { + (void)usbp; + uint8_t i; +#if KINETIS_USB_USE_USB0 + for(i=1;iENDPT[i].V = 0; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Returns the status of an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + if(ep > USB_MAX_ENDPOINTS) + return EP_STATUS_DISABLED; + if(!(USB0->ENDPT[ep].V & (USBx_ENDPTn_EPRXEN))) + return EP_STATUS_DISABLED; + else if(USB0->ENDPT[ep].V & USBx_ENDPTn_EPSTALL) + return EP_STATUS_STALLED; + return EP_STATUS_ACTIVE; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Returns the status of an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) { + (void)usbp; + if(ep > USB_MAX_ENDPOINTS) + return EP_STATUS_DISABLED; +#if KINETIS_USB_USE_USB0 + if(!(USB0->ENDPT[ep].V & (USBx_ENDPTn_EPTXEN))) + return EP_STATUS_DISABLED; + else if(USB0->ENDPT[ep].V & USBx_ENDPTn_EPSTALL) + return EP_STATUS_STALLED; + return EP_STATUS_ACTIVE; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Reads a setup packet from the dedicated packet buffer. + * @details This function must be invoked in the context of the @p setup_cb + * callback in order to read the received setup packet. + * @pre In order to use this function the endpoint must have been + * initialized as a control endpoint. + * @post The endpoint is ready to accept another packet. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @param[out] buf buffer where to copy the packet data + * + * @notapi + */ +void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { + /* Get the BDT entry */ + USBOutEndpointState *os = usbp->epc[ep]->out_state; + bd_t *bd = (bd_t*)&_bdt[BDT_INDEX(ep, RX, os->odd_even)]; + /* Copy the 8 bytes of data */ + uint8_t n; + for (n = 0; n < 8; n++) { + buf[n] = bd->addr[n]; + } + /* Release the buffer + * Setup packet is always DATA0 + * Initialize buffers so current expects DATA0 & opposite DATA1 */ + 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); + os->data_bank = DATA1; +} + +/** + * @brief Starts a receive operation on an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { + USBOutEndpointState *osp = usbp->epc[ep]->out_state; + /* Transfer initialization.*/ + if (osp->rxsize == 0) /* Special case for zero sized packets.*/ + osp->rxpkts = 1; + else + osp->rxpkts = (uint16_t)((osp->rxsize + usbp->epc[ep]->out_maxsize - 1) / + usbp->epc[ep]->out_maxsize); +} + +/** + * @brief Starts a transmit operation on an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @note Called from ISR and locked zone. + * @notapi + */ +void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { + (void)usbp; + (void)ep; + usb_packet_transmit(usbp,ep,usbp->epc[ep]->in_state->txsize); +} + +/** + * @brief Brings an OUT endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V |= USBx_ENDPTn_EPSTALL; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Brings an IN endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V |= USBx_ENDPTn_EPSTALL; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Brings an OUT endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V &= ~USBx_ENDPTn_EPSTALL; +#endif /* KINETIS_USB_USE_USB0 */ +} + +/** + * @brief Brings an IN endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) { + (void)usbp; +#if KINETIS_USB_USE_USB0 + USB0->ENDPT[ep].V &= ~USBx_ENDPTn_EPSTALL; +#endif /* KINETIS_USB_USE_USB0 */ +} + +#endif /* HAL_USE_USB */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/usb_lld.h b/os/hal/ports/KINETIS/LLD/usb_lld.h new file mode 100644 index 0000000..978e8a6 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/usb_lld.h @@ -0,0 +1,428 @@ +/* + ChibiOS - Copyright (C) 2015 RedoX https://github.com/RedoXyde/ + (C) 2015-2016 flabbergast + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file KINETIS/LLD/usb_lld.h + * @brief KINETIS USB subsystem low level driver header. + * + * @addtogroup USB + * @{ + */ + +#ifndef _USB_LLD_H_ +#define _USB_LLD_H_ + +#if HAL_USE_USB || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Maximum endpoint address. + */ +#define USB_MAX_ENDPOINTS 15 + +/** + * @brief Status stage handling method. + */ +#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW + +/** + * @brief Address ack handling + */ +#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW + +/** + * @brief This device requires the address change after the status packet. + */ +#define USB_SET_ADDRESS_MODE USB_LATE_SET_ADDRESS + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief USB1 driver enable switch. + * @details If set to @p TRUE the support for USB1 is included. + * @note The default is @p TRUE. + */ +#if !defined(KINETIS_USB_USE_USB0) || defined(__DOXYGEN__) +#define KINETIS_USB_USE_USB0 FALSE +#endif + +/** + * @brief USB1 interrupt priority level setting. + */ +#if !defined(KINETIS_USB_USB0_IRQ_PRIORITY)|| defined(__DOXYGEN__) +#define KINETIS_USB_USB0_IRQ_PRIORITY 5 +#endif + +#if !defined(KINETIS_USB_ENDPOINTS) || defined(__DOXYGEN__) + #define KINETIS_USB_ENDPOINTS USB_MAX_ENDPOINTS+1 +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if KINETIS_USB_USE_USB0 && !KINETIS_HAS_USB +#error "USB not present in the selected device" +#endif + +#if !KINETIS_USB_USE_USB0 +#error "USB driver activated but no USB peripheral assigned" +#endif + +#if KINETIS_USB_USE_USB0 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_USB_USB0_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to KINETIS_USB_USB0_IRQ_PRIORITY" +#endif + +#if !defined(KINETIS_USB_IRQ_VECTOR) +#error "KINETIS_USB_IRQ_VECTOR not defined" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of an IN endpoint state structure. + */ +typedef struct { + /** + * @brief Requested transmit transfer size. + */ + size_t txsize; + /** + * @brief Transmitted bytes so far. + */ + size_t txcnt; + /** + * @brief Pointer to the transmission linear buffer. + */ + const uint8_t *txbuf; +#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif + /* End of the mandatory fields.*/ + /* */ + bool odd_even; /* ODD / EVEN */ + /* */ + bool data_bank; /* DATA0 / DATA1 */ +} USBInEndpointState; + +/** + * @brief Type of an OUT endpoint state structure. + */ +typedef struct { + /** + * @brief Requested receive transfer size. + */ + size_t rxsize; + /** + * @brief Received bytes so far. + */ + size_t rxcnt; + /** + * @brief Pointer to the receive linear buffer. + */ + uint8_t *rxbuf; +#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Number of packets to receive. + */ + uint16_t rxpkts; + /* */ + bool odd_even; /* ODD / EVEN */ + /* */ + bool data_bank; /* DATA0 / DATA1 */ +} USBOutEndpointState; + +/** + * @brief Type of an USB endpoint configuration structure. + * @note Platform specific restrictions may apply to endpoints. + */ +typedef struct { + /** + * @brief Type and mode of the endpoint. + */ + uint32_t ep_mode; + /** + * @brief Setup packet notification callback. + * @details This callback is invoked when a setup packet has been + * received. + * @post The application must immediately call @p usbReadPacket() in + * order to access the received packet. + * @note This field is only valid for @p USB_EP_MODE_TYPE_CTRL + * endpoints, it should be set to @p NULL for other endpoint + * types. + */ + usbepcallback_t setup_cb; + /** + * @brief IN endpoint notification callback. + * @details This field must be set to @p NULL if callback is not required. + */ + usbepcallback_t in_cb; + /** + * @brief OUT endpoint notification callback. + * @details This field must be set to @p NULL if callback is not required. + */ + usbepcallback_t out_cb; + /** + * @brief IN endpoint maximum packet size. + * @details This field must be set to zero if the IN endpoint is not used. + */ + uint16_t in_maxsize; + /** + * @brief OUT endpoint maximum packet size. + * @details This field must be set to zero if the OUT endpoint is not used. + */ + uint16_t out_maxsize; + /** + * @brief @p USBEndpointState associated to the IN endpoint. + * @details This field must be set to @p NULL if the IN endpoint is not + * used. + */ + USBInEndpointState *in_state; + /** + * @brief @p USBEndpointState associated to the OUT endpoint. + * @details This field must be set to @p NULL if the OUT endpoint is not + * used. + */ + USBOutEndpointState *out_state; + /* End of the mandatory fields.*/ + /** + * @brief Reserved field, not currently used. + * @note Initialize this field to 1 in order to be forward compatible. + */ + uint16_t ep_buffers; + /** + * @brief Pointer to a buffer for setup packets. + * @details Setup packets require a dedicated 8-bytes buffer, set this + * field to @p NULL for non-control endpoints. + */ + uint8_t *setup_buf; +} USBEndpointConfig; + +/** + * @brief Type of an USB driver configuration structure. + */ +typedef struct { + /** + * @brief USB events callback. + * @details This callback is invoked when an USB driver event is registered. + */ + usbeventcb_t event_cb; + /** + * @brief Device GET_DESCRIPTOR request callback. + * @note This callback is mandatory and cannot be set to @p NULL. + */ + usbgetdescriptor_t get_descriptor_cb; + /** + * @brief Requests hook callback. + * @details This hook allows to be notified of standard requests or to + * handle non standard requests. + */ + usbreqhandler_t requests_hook_cb; + /** + * @brief Start Of Frame callback. + */ + usbcallback_t sof_cb; + /* End of the mandatory fields.*/ +} USBConfig; + +/** + * @brief Structure representing an USB driver. + */ +struct USBDriver { + /** + * @brief Driver state. + */ + usbstate_t state; + /** + * @brief Current configuration data. + */ + const USBConfig *config; + /** + * @brief Bit map of the transmitting IN endpoints. + */ + uint16_t transmitting; + /** + * @brief Bit map of the receiving OUT endpoints. + */ + uint16_t receiving; + /** + * @brief Active endpoints configurations. + */ + const USBEndpointConfig *epc[USB_MAX_ENDPOINTS + 1]; + /** + * @brief Fields available to user, it can be used to associate an + * application-defined handler to an IN endpoint. + * @note The base index is one, the endpoint zero does not have a + * reserved element in this array. + */ + void *in_params[USB_MAX_ENDPOINTS]; + /** + * @brief Fields available to user, it can be used to associate an + * application-defined handler to an OUT endpoint. + * @note The base index is one, the endpoint zero does not have a + * reserved element in this array. + */ + void *out_params[USB_MAX_ENDPOINTS]; + /** + * @brief Endpoint 0 state. + */ + usbep0state_t ep0state; + /** + * @brief Next position in the buffer to be transferred through endpoint 0. + */ + uint8_t *ep0next; + /** + * @brief Number of bytes yet to be transferred through endpoint 0. + */ + size_t ep0n; + /** + * @brief Endpoint 0 end transaction callback. + */ + usbcallback_t ep0endcb; + /** + * @brief Setup packet buffer. + */ + uint8_t setup[8]; + /** + * @brief Current USB device status. + */ + uint16_t status; + /** + * @brief Assigned USB address. + */ + uint8_t address; + /** + * @brief Current USB device configuration. + */ + uint8_t configuration; +#if defined(USB_DRIVER_EXT_FIELDS) + USB_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the next address in the packet memory. + */ + uint32_t pmnext; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Returns the current frame number. + * + * @param[in] usbp pointer to the @p USBDriver object + * @return The current frame number. + * + * @notapi + */ +#define usb_lld_get_frame_number(usbp) ((USB0->FRMNUMH<<8)|USB0->FRMNUML) + +/** + * @brief Returns the exact size of a receive transaction. + * @details The received size can be different from the size specified in + * @p usbStartReceiveI() because the last packet could have a size + * different from the expected one. + * @pre The OUT endpoint must have been configured in transaction mode + * in order to use this function. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return Received data size. + * + * @notapi + */ +#define usb_lld_get_transaction_size(usbp, ep) \ + ((usbp)->epc[ep]->out_state->rxcnt) + +/** + * @brief Connects the USB device. + * + * @api + */ +#if !defined(usb_lld_connect_bus) +#define usb_lld_connect_bus(usbp) (USB0->CONTROL |= USBx_CONTROL_DPPULLUPNONOTG) +#endif + +/** + * @brief Disconnect the USB device. + * + * @api + */ +#if !defined(usb_lld_disconnect_bus) +/* Writing to USB0->CONTROL causes an unhandled exception when USB module is not clocked. */ +#if KINETIS_USB0_IS_USBOTG +#define usb_lld_disconnect_bus(usbp) if(SIM->SCGC4 & SIM_SCGC4_USBOTG) {USB0->CONTROL &= ~USBx_CONTROL_DPPULLUPNONOTG;} else {} +#else /* KINETIS_USB0_IS_USBOTG */ +#define usb_lld_disconnect_bus(usbp) if(SIM->SCGC4 & SIM_SCGC4_USBFS) {USB0->CONTROL &= ~USBx_CONTROL_DPPULLUPNONOTG;} else {} +#endif /* KINETIS_USB0_IS_USBOTG */ +#endif + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if KINETIS_USB_USE_USB0 && !defined(__DOXYGEN__) +extern USBDriver USBD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void usb_lld_init(void); + void usb_lld_start(USBDriver *usbp); + void usb_lld_stop(USBDriver *usbp); + void usb_lld_reset(USBDriver *usbp); + void usb_lld_set_address(USBDriver *usbp); + void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep); + void usb_lld_disable_endpoints(USBDriver *usbp); + usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep); + usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep); + void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf); + void usb_lld_start_out(USBDriver *usbp, usbep_t ep); + void usb_lld_start_in(USBDriver *usbp, usbep_t ep); + void usb_lld_stall_out(USBDriver *usbp, usbep_t ep); + void usb_lld_stall_in(USBDriver *usbp, usbep_t ep); + void usb_lld_clear_out(USBDriver *usbp, usbep_t ep); + void usb_lld_clear_in(USBDriver *usbp, usbep_t ep); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_USB */ + +#endif /* _USB_LLD_H_ */ + +/** @} */ -- cgit v1.2.3