From d200007a2985ed585a674bccb683b1bc953b2e36 Mon Sep 17 00:00:00 2001 From: Konstantin Oblaukhov Date: Sun, 7 Oct 2018 15:04:33 +0700 Subject: Reorganize KINETIS driver files --- os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.c | 259 ++++++ os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.h | 360 ++++++++ os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c | 245 ++++++ os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.h | 423 ++++++++++ os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.c | 589 ++++++++++++++ os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.h | 247 ++++++ os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.c | 391 +++++++++ os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.h | 333 ++++++++ os/hal/ports/KINETIS/LLD/PITv1/hal_st_lld.c | 98 +++ os/hal/ports/KINETIS/LLD/PITv1/hal_st_lld.h | 156 ++++ os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.c | 434 ++++++++++ os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.h | 188 +++++ os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.c | 993 +++++++++++++++++++++++ os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.h | 202 +++++ os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.c | 692 ++++++++++++++++ os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.h | 287 +++++++ os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c | 873 ++++++++++++++++++++ os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h | 455 +++++++++++ os/hal/ports/KINETIS/LLD/hal_adc_lld.c | 259 ------ os/hal/ports/KINETIS/LLD/hal_adc_lld.h | 360 -------- os/hal/ports/KINETIS/LLD/hal_ext_lld.c | 434 ---------- os/hal/ports/KINETIS/LLD/hal_ext_lld.h | 188 ----- os/hal/ports/KINETIS/LLD/hal_gpt_lld.c | 391 --------- os/hal/ports/KINETIS/LLD/hal_gpt_lld.h | 333 -------- os/hal/ports/KINETIS/LLD/hal_i2c_lld.c | 589 -------------- os/hal/ports/KINETIS/LLD/hal_i2c_lld.h | 247 ------ os/hal/ports/KINETIS/LLD/hal_pal_lld.c | 245 ------ os/hal/ports/KINETIS/LLD/hal_pal_lld.h | 423 ---------- os/hal/ports/KINETIS/LLD/hal_sdc_lld.c | 993 ----------------------- os/hal/ports/KINETIS/LLD/hal_sdc_lld.h | 202 ----- os/hal/ports/KINETIS/LLD/hal_serial_lld.c | 692 ---------------- os/hal/ports/KINETIS/LLD/hal_serial_lld.h | 287 ------- os/hal/ports/KINETIS/LLD/hal_st_lld.c | 98 --- os/hal/ports/KINETIS/LLD/hal_st_lld.h | 156 ---- os/hal/ports/KINETIS/LLD/hal_usb_lld.c | 873 -------------------- os/hal/ports/KINETIS/LLD/hal_usb_lld.h | 455 ----------- 36 files changed, 7225 insertions(+), 7225 deletions(-) create mode 100644 os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/PITv1/hal_st_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/PITv1/hal_st_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_adc_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_adc_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_ext_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_ext_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_gpt_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_gpt_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_i2c_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_i2c_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_pal_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_pal_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_sdc_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_sdc_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_serial_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_serial_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_st_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_st_lld.h delete mode 100644 os/hal/ports/KINETIS/LLD/hal_usb_lld.c delete mode 100644 os/hal/ports/KINETIS/LLD/hal_usb_lld.h (limited to 'os/hal') diff --git a/os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.c b/os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.c new file mode 100644 index 0000000..847f424 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.c @@ -0,0 +1,259 @@ +/* + 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 ADCv1/hal_adc_lld.c + * @brief KINETIS ADC subsystem low level driver source. + * + * @addtogroup ADC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define ADC_CHANNEL_MASK 0x1f + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief ADC1 driver identifier.*/ +#if KINETIS_ADC_USE_ADC0 || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void calibrate(ADCDriver *adcp) { + + /* Clock Divide by 8, Use Bus Clock Div 2 */ + /* At 48MHz this results in ADCCLK of 48/8/2 == 3MHz */ + adcp->adc->CFG1 = ADCx_CFG1_ADIV(ADCx_CFG1_ADIV_DIV_8) | + ADCx_CFG1_ADICLK(ADCx_CFG1_ADIVCLK_BUS_CLOCK_DIV_2); + + /* Use software trigger and disable DMA etc. */ + adcp->adc->SC2 = 0; + + /* Enable Hardware Average, Average 32 Samples, Calibrate */ + adcp->adc->SC3 = ADCx_SC3_AVGE | + ADCx_SC3_AVGS(ADCx_SC3_AVGS_AVERAGE_32_SAMPLES) | + ADCx_SC3_CAL; + + /* FIXME: May take several ms. Use an interrupt instead of busy wait */ + /* Wait for calibration completion */ + while (!(adcp->adc->SC1A & ADCx_SC1n_COCO)) + ; + + uint16_t gain = ((adcp->adc->CLP0 + adcp->adc->CLP1 + adcp->adc->CLP2 + + adcp->adc->CLP3 + adcp->adc->CLP4 + adcp->adc->CLPS) / 2) | 0x8000; + adcp->adc->PG = gain; + + gain = ((adcp->adc->CLM0 + adcp->adc->CLM1 + adcp->adc->CLM2 + + adcp->adc->CLM3 + adcp->adc->CLM4 + adcp->adc->CLMS) / 2) | 0x8000; + adcp->adc->MG = gain; + +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if KINETIS_ADC_USE_ADC0 || defined(__DOXYGEN__) +/** + * @brief ADC interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_ADC0_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + ADCDriver *adcp = &ADCD1; + + /* Disable Interrupt, Disable Channel */ + adcp->adc->SC1A = ADCx_SC1n_ADCH(ADCx_SC1n_ADCH_DISABLED); + + /* Read the sample into the buffer */ + adcp->samples[adcp->current_index++] = adcp->adc->RA; + + bool more = true; + + /* At the end of the buffer then we may be finished */ + if (adcp->current_index == adcp->number_of_samples) { + /* We are never finished in circular mode */ + more = ADCD1.grpp->circular; + + _adc_isr_full_code(&ADCD1); + + adcp->current_index = 0; + + } + + if (more) { + + /* Signal half completion in circular mode. */ + if (ADCD1.grpp->circular && + (adcp->current_index == (adcp->number_of_samples / 2))) { + + _adc_isr_half_code(&ADCD1); + } + + /* Skip to the next channel */ + do { + adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; + } while (((1 << adcp->current_channel) & adcp->grpp->channel_mask) == 0); + + /* Enable Interrupt, Select the Channel */ + adcp->adc->SC1A = ADCx_SC1n_AIEN | ADCx_SC1n_ADCH(adcp->current_channel); + } + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) { + +#if KINETIS_ADC_USE_ADC0 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); +#endif + + /* The shared vector is initialized on driver initialization and never + disabled.*/ + nvicEnableVector(ADC0_IRQn, KINETIS_ADC_IRQ_PRIORITY); +} + +/** + * @brief Configures and activates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start(ADCDriver *adcp) { + + /* If in stopped state then enables the ADC clock.*/ + if (adcp->state == ADC_STOP) { + SIM->SCGC6 |= SIM_SCGC6_ADC0; + +#if KINETIS_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + adcp->adc = ADC0; + if (adcp->config->calibrate) { + calibrate(adcp); + } + } +#endif /* KINETIS_ADC_USE_ADC0 */ + } +} + +/** + * @brief Deactivates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop(ADCDriver *adcp) { + + /* If in ready state then disables the ADC clock.*/ + if (adcp->state == ADC_READY) { + SIM->SCGC6 &= ~SIM_SCGC6_ADC0; + +#if KINETIS_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + /* Disable Interrupt, Disable Channel */ + adcp->adc->SC1A = ADCx_SC1n_ADCH(ADCx_SC1n_ADCH_DISABLED); + } +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + const ADCConversionGroup *grpp = adcp->grpp; + + /* Enable the Bandgap Buffer if channel mask includes BANDGAP */ + if (grpp->channel_mask & ADC_BANDGAP) { + PMC->REGSC |= PMC_REGSC_BGBE; + } + + adcp->number_of_samples = adcp->depth * grpp->num_channels; + adcp->current_index = 0; + + /* Skip to the next channel */ + adcp->current_channel = 0; + while (((1 << adcp->current_channel) & grpp->channel_mask) == 0) { + adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; + } + + /* Set clock speed and conversion size */ + adcp->adc->CFG1 = grpp->cfg1; + + /* Set averaging */ + adcp->adc->SC3 = grpp->sc3; + + /* Enable Interrupt, Select Channel */ + adcp->adc->SC1A = ADCx_SC1n_AIEN | ADCx_SC1n_ADCH(adcp->current_channel); +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + const ADCConversionGroup *grpp = adcp->grpp; + + /* Disable the Bandgap buffer if channel mask includes BANDGAP */ + if (grpp->channel_mask & ADC_BANDGAP) { + /* Clear BGBE, ACKISO is w1c, avoid setting */ + PMC->REGSC &= ~(PMC_REGSC_BGBE | PMC_REGSC_ACKISO); + } + +} + +#endif /* HAL_USE_ADC */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.h b/os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.h new file mode 100644 index 0000000..2d11a7d --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/ADCv1/hal_adc_lld.h @@ -0,0 +1,360 @@ +/* + 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 ADCv1/hal_adc_lld.h + * @brief KINETIS ADC subsystem low level driver header. + * + * @addtogroup ADC + * @{ + */ + +#ifndef HAL_ADC_LLD_H_ +#define HAL_ADC_LLD_H_ + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Absolute Maximum Ratings + * @{ + */ +/** + * @brief Minimum ADC clock frequency. + */ +#define KINETIS_ADCCLK_MIN 600000 + +/** + * @brief Maximum ADC clock frequency. + */ +#define KINETIS_ADCCLK_MAX 36000000 + +#define ADCx_SC3_AVGS_AVERAGE_4_SAMPLES 0 +#define ADCx_SC3_AVGS_AVERAGE_8_SAMPLES 1 +#define ADCx_SC3_AVGS_AVERAGE_16_SAMPLES 2 +#define ADCx_SC3_AVGS_AVERAGE_32_SAMPLES 3 + +#define ADCx_CFG1_ADIV_DIV_1 0 +#define ADCx_CFG1_ADIV_DIV_2 1 +#define ADCx_CFG1_ADIV_DIV_4 2 +#define ADCx_CFG1_ADIV_DIV_8 3 + +#define ADCx_CFG1_ADIVCLK_BUS_CLOCK 0 +#define ADCx_CFG1_ADIVCLK_BUS_CLOCK_DIV_2 1 +#define ADCx_CFG1_ADIVCLK_BUS_ALTCLK 2 +#define ADCx_CFG1_ADIVCLK_BUS_ADACK 3 + +#define ADCx_CFG1_MODE_8_OR_9_BITS 0 +#define ADCx_CFG1_MODE_12_OR_13_BITS 1 +#define ADCx_CFG1_MODE_10_OR_11_BITS 2 +#define ADCx_CFG1_MODE_16_BITS 3 + +#define ADCx_SC1n_ADCH_DAD0 0 +#define ADCx_SC1n_ADCH_DAD1 1 +#define ADCx_SC1n_ADCH_DAD2 2 +#define ADCx_SC1n_ADCH_DAD3 3 +#define ADCx_SC1n_ADCH_DADP0 0 +#define ADCx_SC1n_ADCH_DADP1 1 +#define ADCx_SC1n_ADCH_DADP2 2 +#define ADCx_SC1n_ADCH_DADP3 3 +#define ADCx_SC1n_ADCH_AD4 4 +#define ADCx_SC1n_ADCH_AD5 5 +#define ADCx_SC1n_ADCH_AD6 6 +#define ADCx_SC1n_ADCH_AD7 7 +#define ADCx_SC1n_ADCH_AD8 8 +#define ADCx_SC1n_ADCH_AD9 9 +#define ADCx_SC1n_ADCH_AD10 10 +#define ADCx_SC1n_ADCH_AD11 11 +#define ADCx_SC1n_ADCH_AD12 12 +#define ADCx_SC1n_ADCH_AD13 13 +#define ADCx_SC1n_ADCH_AD14 14 +#define ADCx_SC1n_ADCH_AD15 15 +#define ADCx_SC1n_ADCH_AD16 16 +#define ADCx_SC1n_ADCH_AD17 17 +#define ADCx_SC1n_ADCH_AD18 18 +#define ADCx_SC1n_ADCH_AD19 19 +#define ADCx_SC1n_ADCH_AD20 20 +#define ADCx_SC1n_ADCH_AD21 21 +#define ADCx_SC1n_ADCH_AD22 22 +#define ADCx_SC1n_ADCH_AD23 23 +#define ADCx_SC1n_ADCH_TEMP_SENSOR 26 +#define ADCx_SC1n_ADCH_BANDGAP 27 +#define ADCx_SC1n_ADCH_VREFSH 29 +#define ADCx_SC1n_ADCH_VREFSL 30 +#define ADCx_SC1n_ADCH_DISABLED 31 + +#define ADC_DAD0 (1 << ADCx_SC1n_ADCH_DAD0) +#define ADC_DAD1 (1 << ADCx_SC1n_ADCH_DAD1) +#define ADC_DAD2 (1 << ADCx_SC1n_ADCH_DAD2) +#define ADC_DAD3 (1 << ADCx_SC1n_ADCH_DAD3) +#define ADC_DADP0 (1 << ADCx_SC1n_ADCH_DADP0) +#define ADC_DADP1 (1 << ADCx_SC1n_ADCH_DADP1) +#define ADC_DADP2 (1 << ADCx_SC1n_ADCH_DADP2) +#define ADC_DADP3 (1 << ADCx_SC1n_ADCH_DADP3) +#define ADC_AD4 (1 << ADCx_SC1n_ADCH_AD4) +#define ADC_AD5 (1 << ADCx_SC1n_ADCH_AD5) +#define ADC_AD6 (1 << ADCx_SC1n_ADCH_AD6) +#define ADC_AD7 (1 << ADCx_SC1n_ADCH_AD7) +#define ADC_AD8 (1 << ADCx_SC1n_ADCH_AD8) +#define ADC_AD9 (1 << ADCx_SC1n_ADCH_AD9) +#define ADC_AD10 (1 << ADCx_SC1n_ADCH_AD10) +#define ADC_AD11 (1 << ADCx_SC1n_ADCH_AD11) +#define ADC_AD12 (1 << ADCx_SC1n_ADCH_AD12) +#define ADC_AD13 (1 << ADCx_SC1n_ADCH_AD13) +#define ADC_AD14 (1 << ADCx_SC1n_ADCH_AD14) +#define ADC_AD15 (1 << ADCx_SC1n_ADCH_AD15) +#define ADC_AD16 (1 << ADCx_SC1n_ADCH_AD16) +#define ADC_AD17 (1 << ADCx_SC1n_ADCH_AD17) +#define ADC_AD18 (1 << ADCx_SC1n_ADCH_AD18) +#define ADC_AD19 (1 << ADCx_SC1n_ADCH_AD19) +#define ADC_AD20 (1 << ADCx_SC1n_ADCH_AD20) +#define ADC_AD21 (1 << ADCx_SC1n_ADCH_AD21) +#define ADC_AD22 (1 << ADCx_SC1n_ADCH_AD22) +#define ADC_AD23 (1 << ADCx_SC1n_ADCH_AD23) +#define ADC_TEMP_SENSOR (1 << ADCx_SC1n_ADCH_TEMP_SENSOR) +#define ADC_BANDGAP (1 << ADCx_SC1n_ADCH_BANDGAP) +#define ADC_VREFSH (1 << ADCx_SC1n_ADCH_VREFSH) +#define ADC_VREFSL (1 << ADCx_SC1n_ADCH_VREFSL) +#define ADC_DISABLED (1 << ADCx_SC1n_ADCH_DISABLED) + +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ + +/** + * @brief ADC1 driver enable switch. + * @details If set to @p TRUE the support for ADC1 is included. + * @note The default is @p TRUE. + */ +#if !defined(KINETIS_ADC_USE_ADC0) || defined(__DOXYGEN__) +#define KINETIS_ADC_USE_ADC0 FALSE +#endif + +/** + * @brief ADC interrupt priority level setting. + */ +#if !defined(KINETIS_ADC_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_ADC_IRQ_PRIORITY 5 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if KINETIS_ADC_USE_ADC0 && !KINETIS_HAS_ADC0 +#error "ADC1 not present in the selected device" +#endif + +#if !KINETIS_ADC_USE_ADC0 +#error "ADC driver activated but no ADC peripheral assigned" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ADC sample data type. + */ +typedef uint16_t adcsample_t; + +/** + * @brief Channels number in a conversion group. + */ +typedef uint16_t adc_channels_num_t; + +/** + * @brief Possible ADC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + ADC_ERR_OVERFLOW = 1 /**< ADC overflow condition. */ +} adcerror_t; + +/** + * @brief Type of a structure representing an ADC driver. + */ +typedef struct ADCDriver ADCDriver; + +/** + * @brief ADC notification callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] buffer pointer to the most recent samples data + * @param[in] n number of buffer rows available starting from @p buffer + */ +typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); + +/** + * @brief ADC error callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] err ADC error code + */ +typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); + +/** + * @brief Conversion group configuration structure. + * @details This implementation-dependent structure describes a conversion + * operation. + */ +typedef struct { + /** + * @brief Enables the circular buffer mode for the group. + */ + bool circular; + /** + * @brief Number of the analog channels belonging to the conversion group. + */ + adc_channels_num_t num_channels; + /** + * @brief Callback function associated to the group or @p NULL. + */ + adccallback_t end_cb; + /** + * @brief Error callback or @p NULL. + */ + adcerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + /** + * @brief Bitmask of channels for ADC conversion. + */ + uint32_t channel_mask; + /** + * @brief ADC CFG1 register initialization data. + * @note All the required bits must be defined into this field. + */ + uint32_t cfg1; + /** + * @brief ADC SC3 register initialization data. + * @note All the required bits must be defined into this field. + */ + uint32_t sc3; +} ADCConversionGroup; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /* Perform first time calibration */ + bool calibrate; +} ADCConfig; + +/** + * @brief Structure representing an ADC driver. + */ +struct ADCDriver { + /** + * @brief Driver state. + */ + adcstate_t state; + /** + * @brief Current configuration data. + */ + const ADCConfig *config; + /** + * @brief Current samples buffer pointer or @p NULL. + */ + adcsample_t *samples; + /** + * @brief Current samples buffer depth or @p 0. + */ + size_t depth; + /** + * @brief Current conversion group pointer or @p NULL. + */ + const ADCConversionGroup *grpp; +#if ADC_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif +#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* ADC_USE_MUTUAL_EXCLUSION */ +#if defined(ADC_DRIVER_EXT_FIELDS) + ADC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the ADCx registers block. + */ + ADC_TypeDef *adc; + /** + * @brief Number of samples expected. + */ + size_t number_of_samples; + /** + * @brief Current position in the buffer. + */ + size_t current_index; + /** + * @brief Current channel index into group channel_mask. + */ + size_t current_channel; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if KINETIS_ADC_USE_ADC0 && !defined(__DOXYGEN__) +extern ADCDriver ADCD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void adc_lld_init(void); + void adc_lld_start(ADCDriver *adcp); + void adc_lld_stop(ADCDriver *adcp); + void adc_lld_start_conversion(ADCDriver *adcp); + void adc_lld_stop_conversion(ADCDriver *adcp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ADC */ + +#endif /* HAL_ADC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c b/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c new file mode 100644 index 0000000..3cba308 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/GPIOv1/hal_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 GPIOv1/hal_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; + + osalDbgAssert(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; + + osalDbgAssert(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/GPIOv1/hal_pal_lld.h b/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.h new file mode 100644 index 0000000..17125b1 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.h @@ -0,0 +1,423 @@ +/* + 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 GPIOv1/hal_pal_lld.h + * @brief PAL subsystem low level driver header. + * + * @addtogroup PAL + * @{ + */ + +#ifndef HAL_PAL_LLD_H_ +#define HAL_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 Type of an I/O line. + */ +typedef uint32_t ioline_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 + +/** + * @name Line handling macros + * @{ + */ +/** + * @brief Forms a line identifier. + * @details A port/pad pair are encoded into an @p ioline_t type. The encoding + * of this type is platform-dependent. + * @note In this driver the pad number is encoded in the byte of the GPIO + * address that's zero on all Kinetis devices. + */ +#define PAL_LINE(port, pad) \ + ((ioline_t)((uint32_t)(port) | ((uint32_t)(pad)<<20))) + +/** + * @brief Decodes a port identifier from a line identifier. + */ +#define PAL_PORT(line) \ + ((GPIO_TypeDef *)(((uint32_t)(line)) & 0xF00FFFFFU)) + +/** + * @brief Decodes a pad identifier from a line identifier. + */ +#define PAL_PAD(line) \ + ((uint32_t)((uint32_t)(line) & 0x0FF00000U)>>20) + +/** + * @brief Value identifying an invalid line. + */ +#define PAL_NOLINE 0U +/** @} */ + +/*===========================================================================*/ +/* 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 /* HAL_PAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.c b/os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.c new file mode 100644 index 0000000..aa47aa3 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.c @@ -0,0 +1,589 @@ +/* + 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 I2Cv1/hal_i2c_lld.c + * @brief KINETIS I2C subsystem low level driver source. + * + * @addtogroup I2C + * @{ + */ + +#include "osal.h" +#include "hal.h" + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief I2C0 driver identifier. + */ +#if KINETIS_I2C_USE_I2C0 || defined(__DOXYGEN__) +I2CDriver I2CD1; +#endif + +/** + * @brief I2C1 driver identifier. + */ +#if KINETIS_I2C_USE_I2C1 || defined(__DOXYGEN__) +I2CDriver I2CD2; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +void config_frequency(I2CDriver *i2cp) { + + /* Each index in the table corresponds to a a frequency + * divider used to generate the SCL clock from the main + * system clock. + */ + const uint16_t icr_table[] = { + /* 0x00 - 0x0F */ + 20,22,24,26,28,30,34,40,28,32,36,40,44,48,56,68, + /* 0x10 - 0x1F */ + 48,56,64,72,80,88,104,128,80,96,112,128,144,160,192,240, + /* 0x20 - 0x2F */ + 160,192,224,256,288,320,384,480,320,384,448,512,576,640,768,960, + /* 0x30 - 0x3F */ + 640,768,896,1024,1152,1280,1536,1920,1280,1536,1792,2048,2304,2560,3072,3840, + }; + + int length = sizeof(icr_table) / sizeof(icr_table[0]); + uint16_t divisor; + uint8_t i = 0, index = 0; + uint16_t best, diff; + + if (i2cp->config != NULL) + divisor = KINETIS_BUSCLK_FREQUENCY / i2cp->config->clock; + else + divisor = KINETIS_BUSCLK_FREQUENCY / 100000; + + best = ~0; + index = 0; + /* Tries to find the SCL clock which is the closest + * approximation to the clock passed in config. To + * stay on the safe side, only values that generate + * lower frequency are used. + */ + for (i = 0; i < length; i++) { + if (icr_table[i] >= divisor) { + diff = icr_table[i] - divisor; + if (diff < best) { + best = diff; + index = i; + } + } + } + + i2cp->i2c->F = index; +} + +/** + * @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] i2cp pointer to an I2CDriver + */ +static void serve_interrupt(I2CDriver *i2cp) { + + I2C_TypeDef *i2c = i2cp->i2c; + intstate_t state = i2cp->intstate; + + /* check if we're master or slave */ + if (i2c->C1 & I2Cx_C1_MST) { + /* master */ + + if (i2c->S & I2Cx_S_ARBL) { + /* check if we lost arbitration */ + i2cp->errors |= I2C_ARBITRATION_LOST; + i2c->S |= I2Cx_S_ARBL; + /* TODO: may need to do more here, reset bus? */ + /* Perhaps clear MST? */ + } + +#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ + else if ((i2cp->rsta_workaround == RSTA_WORKAROUND_ON) && (i2cp->i2c->FLT & I2Cx_FLT_STARTF)) { + i2cp->rsta_workaround = RSTA_WORKAROUND_OFF; + /* clear+disable STARTF/STOPF interrupts and wake up the thread */ + i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; + i2cp->i2c->FLT &= ~I2Cx_FLT_SSIE; + i2c->S |= I2Cx_S_IICIF; + _i2c_wakeup_isr(i2cp); + } +#endif /* KL27Z RST workaround */ + + else if (i2c->S & I2Cx_S_TCF) { + /* just completed byte transfer */ + if (i2c->C1 & I2Cx_C1_TX) { + /* the byte was transmitted */ + + if (state == STATE_SEND) { + /* currently sending stuff */ + + if (i2c->S & I2Cx_S_RXAK) { + /* slave did not ACK */ + i2cp->errors |= I2C_ACK_FAILURE; + /* the thread will be woken up at the end of ISR and release the bus */ + + } else if (i2cp->txbuf != NULL && i2cp->txidx < i2cp->txbytes) { + /* slave ACK'd and we want to send more */ + i2c->D = i2cp->txbuf[i2cp->txidx++]; + } else { + /* slave ACK'd and we are done sending */ + i2cp->intstate = STATE_STOP; + /* this wakes up the waiting thread at the end of ISR */ + } + + } else if (state == STATE_RECV) { + /* should be receiving stuff, so we've just sent the address */ + + if (i2c->S & I2Cx_S_RXAK) { + /* slave did not ACK */ + i2cp->errors |= I2C_ACK_FAILURE; + /* the thread will be woken up and release the bus */ + + } else { + /* slave ACK'd, we should be receiving next */ + i2c->C1 &= ~I2Cx_C1_TX; + + if (i2cp->rxbytes > 1) { + /* multi-byte read, send ACK after next transfer */ + i2c->C1 &= ~I2Cx_C1_TXAK; + } else { + /* only 1 byte remaining, send NAK */ + i2c->C1 |= I2Cx_C1_TXAK; + } + + (void) i2c->D; /* dummy read; triggers next receive */ + } + + } /* possibly check other states here - should not happen! */ + + } else { + /* the byte was received */ + + if (state == STATE_RECV) { + /* currently receiving stuff */ + /* the received byte is now in D */ + + if (i2cp->rxbytes > 1) { + /* expecting at least one byte after this one */ + if (i2cp->rxidx == (i2cp->rxbytes - 2)) { + /* expecting exactly one byte after this one, NAK that one */ + i2c->C1 |= I2Cx_C1_TXAK; + } else { + /* expecting more than one after this one, respond with ACK */ + i2c->C1 &= ~I2Cx_C1_TXAK; + } + } + + if (i2cp->rxidx == i2cp->rxbytes - 1) { + /* D is the last byte we're expecting */ + /* release bus: switch to RX mode, send STOP */ + /* need to do it now otherwise the I2C module will wait for another byte */ + // delayMicroseconds(1); + i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); + i2cp->intstate = STATE_STOP; + /* this wakes up the waiting thread at the end of ISR */ + } + + /* get the data from D; this triggers the next receive */ + i2cp->rxbuf[i2cp->rxidx++] = i2c->D; + + // if (i2cp->rxidx == i2cp->rxbytes) { + /* done receiving */ + // } + } /* possibly check other states here - should not happen! */ + } + + } /* possibly check other interrupt flags here */ + } else { + /* slave */ + + /* Not implemented yet */ + } + + /* Reset other interrupt sources */ +#if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */ + i2cp->i2c->FLT |= I2Cx_FLT_STOPF; +#endif +#if defined(I2Cx_FLT_STARTF) /* extra flags on KL27Z */ + i2cp->i2c->FLT |= I2Cx_FLT_STARTF; +#endif + /* Reset interrupt flag */ + i2c->S |= I2Cx_S_IICIF; + + if (i2cp->errors != I2C_NO_ERROR) + _i2c_wakeup_error_isr(i2cp); + + if (i2cp->intstate == STATE_STOP) + _i2c_wakeup_isr(i2cp); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if KINETIS_I2C_USE_I2C0 || defined(__DOXYGEN__) + +OSAL_IRQ_HANDLER(KINETIS_I2C0_IRQ_VECTOR) { + + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&I2CD1); + OSAL_IRQ_EPILOGUE(); +} + +#endif + +#if KINETIS_I2C_USE_I2C1 || defined(__DOXYGEN__) + +OSAL_IRQ_HANDLER(KINETIS_I2C1_IRQ_VECTOR) { + + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&I2CD2); + OSAL_IRQ_EPILOGUE(); +} + +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2C driver initialization. + * + * @notapi + */ +void i2c_lld_init(void) { + +#if KINETIS_I2C_USE_I2C0 + i2cObjectInit(&I2CD1); + I2CD1.thread = NULL; + I2CD1.i2c = I2C0; +#endif + +#if KINETIS_I2C_USE_I2C1 + i2cObjectInit(&I2CD2); + I2CD2.thread = NULL; + I2CD2.i2c = I2C1; +#endif + +} + +/** + * @brief Configures and activates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_start(I2CDriver *i2cp) { + + if (i2cp->state == I2C_STOP) { + + /* TODO: + * The PORT must be enabled somewhere. The PIN multiplexer + * will map the I2C functionality to some PORT which must + * than be enabled. The easier way is enabling all PORTs at + * startup, which is currently being done in __early_init. + */ + +#if KINETIS_I2C_USE_I2C0 + if (&I2CD1 == i2cp) { + SIM->SCGC4 |= SIM_SCGC4_I2C0; + nvicEnableVector(I2C0_IRQn, KINETIS_I2C_I2C0_PRIORITY); + } +#endif + +#if KINETIS_I2C_USE_I2C1 + if (&I2CD2 == i2cp) { + SIM->SCGC4 |= SIM_SCGC4_I2C1; + nvicEnableVector(I2C1_IRQn, KINETIS_I2C_I2C1_PRIORITY); + } +#endif + + } + + config_frequency(i2cp); + i2cp->i2c->C1 = I2Cx_C1_IICEN | I2Cx_C1_IICIE; // reset I2C, enable interrupts + i2cp->i2c->S = I2Cx_S_IICIF | I2Cx_S_ARBL; // clear status flags just in case + i2cp->intstate = STATE_STOP; // internal state +} + +/** + * @brief Deactivates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_stop(I2CDriver *i2cp) { + + if (i2cp->state != I2C_STOP) { + + i2cp->i2c->C1 &= ~(I2Cx_C1_IICEN | I2Cx_C1_IICIE); + +#if KINETIS_I2C_USE_I2C0 + if (&I2CD1 == i2cp) { + SIM->SCGC4 &= ~SIM_SCGC4_I2C0; + nvicDisableVector(I2C0_IRQn); + } +#endif + +#if KINETIS_I2C_USE_I2C1 + if (&I2CD2 == i2cp) { + SIM->SCGC4 &= ~SIM_SCGC4_I2C1; + nvicDisableVector(I2C1_IRQn); + } +#endif + + } +} + +static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + + msg_t msg; + systime_t start, end; + + uint8_t op = (i2cp->intstate == STATE_SEND) ? 0 : 1; + + i2cp->errors = I2C_NO_ERROR; + i2cp->addr = addr; + + i2cp->txbuf = txbuf; + i2cp->txbytes = txbytes; + i2cp->txidx = 0; + + i2cp->rxbuf = rxbuf; + i2cp->rxbytes = rxbytes; + i2cp->rxidx = 0; + +#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ + i2cp->rsta_workaround = RSTA_WORKAROUND_OFF; +#endif /* KL27Z RST workaround */ + + /* clear status flags */ +#if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */ + i2cp->i2c->FLT |= I2Cx_FLT_STOPF; +#endif +#if defined(I2Cx_FLT_STARTF) /* extra flags on KL27Z */ + i2cp->i2c->FLT |= I2Cx_FLT_STARTF; +#endif + i2cp->i2c->S = I2Cx_S_IICIF|I2Cx_S_ARBL; + + /* acquire the bus */ + /* check to see if we already have the bus */ + if(i2cp->i2c->C1 & I2Cx_C1_MST) { + +#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ + /* need to wait for STARTF interrupt after issuing repeated start, + * otherwise the double buffering mechanism sends the last sent byte + * instead of the slave address. + * https://community.freescale.com/thread/377611 + */ + i2cp->rsta_workaround = RSTA_WORKAROUND_ON; + /* clear any interrupt bits and enable STARTF/STOPF interrupts */ + i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; + i2cp->i2c->S |= I2Cx_S_IICIF|I2Cx_S_ARBL; + i2cp->i2c->FLT |= I2Cx_FLT_SSIE; +#endif /* KL27Z RST workaround */ + + /* send repeated start */ + i2cp->i2c->C1 |= I2Cx_C1_RSTA | I2Cx_C1_TX; + +#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ + /* wait for the STARTF interrupt */ + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + /* abort if this didn't go well (timed out) */ + if (msg != MSG_OK) { + /* release bus - RX mode, send STOP */ + i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); + return msg; + } +#endif /* KL27Z RST workaround */ + + } else { + /* unlock during the wait, so that tasks with + * higher priority can get attention */ + osalSysUnlock(); + + /* wait until the bus is released */ + /* Calculating the time window for the timeout on the busy bus condition.*/ + start = osalOsGetSystemTimeX(); +#if defined(OSAL_TIME_MS2I) + end = start + OSAL_TIME_MS2I(KINETIS_I2C_BUSY_TIMEOUT); +#elif defined(OSAL_TIME_MS2ST) + end = start + OSAL_TIME_MS2ST(KINETIS_I2C_BUSY_TIMEOUT); +#else + end = start + OSAL_MS2ST(KINETIS_I2C_BUSY_TIMEOUT); +#endif + + while(true) { + osalSysLock(); + /* If the bus is not busy then the operation can continue, note, the + loop is exited in the locked state.*/ + if(!(i2cp->i2c->S & I2Cx_S_BUSY)) + break; + /* If the system time went outside the allowed window then a timeout + condition is returned.*/ + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) { + return MSG_TIMEOUT; + } + osalSysUnlock(); + } + + /* send START */ + i2cp->i2c->C1 |= I2Cx_C1_MST|I2Cx_C1_TX; + } + + /* send slave address */ + i2cp->i2c->D = addr << 1 | op; + + /* wait for the ISR to signal that the transmission (or receive if no transmission) phase is complete */ + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + + /* FIXME */ + //if (i2cp->i2c->S & I2Cx_S_RXAK) + // i2cp->errors |= I2C_ACK_FAILURE; + + /* the transmitting (or receiving if no transmission) phase has finished, + * do we expect to receive something? */ + if (msg == MSG_OK && rxbuf != NULL && rxbytes > 0 && i2cp->rxidx < rxbytes) { + +#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ + /* the same KL27Z RST workaround as above */ + i2cp->rsta_workaround = RSTA_WORKAROUND_ON; + /* clear any interrupt bits and enable STARTF/STOPF interrupts */ + i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; + i2cp->i2c->S |= I2Cx_S_IICIF|I2Cx_S_ARBL; + i2cp->i2c->FLT |= I2Cx_FLT_SSIE; +#endif /* KL27Z RST workaround */ + + /* send repeated start */ + i2cp->i2c->C1 |= I2Cx_C1_RSTA; + +#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ + /* wait for the STARTF interrupt */ + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + /* abort if this didn't go well (timed out) */ + if (msg != MSG_OK) { + /* release bus - RX mode, send STOP */ + i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); + return msg; + } +#endif /* KL27Z RST workaround */ + + /* FIXME */ + // while (!(i2cp->i2c->S & I2Cx_S_BUSY)); + + i2cp->intstate = STATE_RECV; + i2cp->i2c->D = i2cp->addr << 1 | 1; + + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + } + + /* release bus - RX mode, send STOP */ + // other kinetis I2C drivers wait here for 1us. is this needed? + i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); + /* FIXME */ + // while (i2cp->i2c->S & I2Cx_S_BUSY); + + return msg; +} + +/** + * @brief Receives data via the I2C bus as master. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + + i2cp->intstate = STATE_RECV; + return _i2c_txrx_timeout(i2cp, addr, NULL, 0, rxbuf, rxbytes, timeout); +} + +/** + * @brief Transmits data via the I2C bus as master. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[in] txbuf pointer to the transmit buffer + * @param[in] txbytes number of bytes to be transmitted + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + + i2cp->intstate = STATE_SEND; + return _i2c_txrx_timeout(i2cp, addr, txbuf, txbytes, rxbuf, rxbytes, timeout); +} + +#endif /* HAL_USE_I2C */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.h b/os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.h new file mode 100644 index 0000000..e5c9974 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/I2Cv1/hal_i2c_lld.h @@ -0,0 +1,247 @@ +/* + 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 I2Cv1/hal_i2c_lld.h + * @brief KINETIS I2C subsystem low level driver header. + * + * @addtogroup I2C + * @{ + */ + +#ifndef HAL_I2C_LLD_H_ +#define HAL_I2C_LLD_H_ + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define STATE_STOP 0x00 +#define STATE_SEND 0x01 +#define STATE_RECV 0x02 + +#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ +#define RSTA_WORKAROUND_OFF 0x00 +#define RSTA_WORKAROUND_ON 0x01 +#endif /* KL27Z RST workaround */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief I2C0 driver enable switch. + * @details If set to @p TRUE the support for I2C0 is included. + * @note The default is @p FALSE. + */ +#if !defined(KINETIS_I2C_USE_I2C0) || defined(__DOXYGEN__) +#define KINETIS_I2C_USE_I2C0 FALSE +#endif + +/** + * @brief I2C1 driver enable switch. + * @details If set to @p TRUE the support for I2C1 is included. + * @note The default is @p FALSE. + */ +#if !defined(KINETIS_I2C_USE_I2C1) || defined(__DOXYGEN__) +#define KINETIS_I2C_USE_I2C1 FALSE +#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 + +/** + * @brief Timeout for external clearing BUSY bus (in ms). + */ +#if !defined(KINETIS_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) +#define KINETIS_I2C_BUSY_TIMEOUT 50 +#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. */ +/*===========================================================================*/ + +/* @brief Type representing I2C address. */ +typedef uint8_t i2caddr_t; + +/* @brief Type of I2C Driver condition flags. */ +typedef uint32_t i2cflags_t; + +/* @brief Type used to control the ISR state machine. */ +typedef uint8_t intstate_t; + +/** + * @brief Driver configuration structure. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ + +/** + * @brief Driver configuration structure. + */ +typedef struct { + + /* @brief Clock to be used for the I2C bus. */ + uint32_t clock; + +} I2CConfig; + +/** + * @brief Type of a structure representing an I2C driver. + */ +typedef struct I2CDriver I2CDriver; + +/** + * @brief Structure representing an I2C driver. + */ +struct I2CDriver { + /** + * @brief Driver state. + */ + i2cstate_t state; + /** + * @brief Current configuration data. + */ + const I2CConfig *config; + /** + * @brief Error flags. + */ + i2cflags_t errors; +#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#endif /* I2C_USE_MUTUAL_EXCLUSION */ +#if defined(I2C_DRIVER_EXT_FIELDS) + I2C_DRIVER_EXT_FIELDS +#endif + /* @brief Thread waiting for I/O completion. */ + thread_reference_t thread; + /* @brief Current slave address without R/W bit. */ + i2caddr_t addr; + + /* End of the mandatory fields.*/ + + /* @brief Pointer to the buffer with data to send. */ + const uint8_t *txbuf; + /* @brief Number of bytes of data to send. */ + size_t txbytes; + /* @brief Current index in buffer when sending data. */ + size_t txidx; + /* @brief Pointer to the buffer to put received data. */ + uint8_t *rxbuf; + /* @brief Number of bytes of data to receive. */ + size_t rxbytes; + /* @brief Current index in buffer when receiving data. */ + size_t rxidx; + /* @brief Tracks current ISR state. */ + intstate_t intstate; + /* @brief Low-level register access. */ + I2C_TypeDef *i2c; +#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ + /* @brief Auxiliary variable for KL27Z repeated start workaround. */ + intstate_t rsta_workaround; +#endif /* KL27Z RST workaround */ +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Get errors from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) + +#if KINETIS_I2C_USE_I2C0 +extern I2CDriver I2CD1; +#endif + +#if KINETIS_I2C_USE_I2C1 +extern I2CDriver I2CD2; +#endif + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void i2c_lld_init(void); + void i2c_lld_start(I2CDriver *i2cp); + void i2c_lld_stop(I2CDriver *i2cp); + msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2C */ + +#endif /* HAL_I2C_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.c b/os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.c new file mode 100644 index 0000000..3669937 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/PITv1/hal_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 PITv1/hal_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/PITv1/hal_gpt_lld.h b/os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.h new file mode 100644 index 0000000..96c4c70 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/PITv1/hal_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 PITv1/hal_gpt_lld.h + * @brief KINETIS GPT subsystem low level driver header. + * + * @addtogroup GPT + * @{ + */ + +#ifndef HAL_GPT_LLD_H_ +#define HAL_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 /* HAL_GPT_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/PITv1/hal_st_lld.c b/os/hal/ports/KINETIS/LLD/PITv1/hal_st_lld.c new file mode 100644 index 0000000..5930b76 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/PITv1/hal_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 PITv1/hal_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/PITv1/hal_st_lld.h b/os/hal/ports/KINETIS/LLD/PITv1/hal_st_lld.h new file mode 100644 index 0000000..0d75a54 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/PITv1/hal_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 PITv1/hal_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 HAL_ST_LLD_H_ +#define HAL_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 /* HAL_ST_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.c b/os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.c new file mode 100644 index 0000000..c678d6a --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.c @@ -0,0 +1,434 @@ +/* + 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 PORTv1/hal_ext_lld.c + * @brief KINETIS EXT subsystem low level driver source. + * + * @addtogroup EXT + * @{ + */ + +#include "hal.h" + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define PCR_IRQC_DISABLED 0x0 +#define PCR_IRQC_DMA_RISING_EDGE 0x1 +#define PCR_IRQC_DMA_FALLING_EDGE 0x2 +#define PCR_IRQC_DMA_EITHER_EDGE 0x3 + +#define PCR_IRQC_LOGIC_ZERO 0x8 +#define PCR_IRQC_RISING_EDGE 0x9 +#define PCR_IRQC_FALLING_EDGE 0xA +#define PCR_IRQC_EITHER_EDGE 0xB +#define PCR_IRQC_LOGIC_ONE 0xC + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief EXTD1 driver identifier. + */ +EXTDriver EXTD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/* A channel map for each channel. + * + * The index is the pin number. + * The result is the channel for that pin. + */ +#if KINETIS_EXT_PORTA_WIDTH > 0 +uint8_t porta_channel_map[KINETIS_EXT_PORTA_WIDTH]; +#endif +#if KINETIS_EXT_PORTB_WIDTH > 0 +uint8_t portb_channel_map[KINETIS_EXT_PORTB_WIDTH]; +#endif +#if KINETIS_EXT_PORTC_WIDTH > 0 +uint8_t portc_channel_map[KINETIS_EXT_PORTC_WIDTH]; +#endif +#if KINETIS_EXT_PORTD_WIDTH > 0 +uint8_t portd_channel_map[KINETIS_EXT_PORTD_WIDTH]; +#endif +#if KINETIS_EXT_PORTE_WIDTH > 0 +uint8_t porte_channel_map[KINETIS_EXT_PORTE_WIDTH]; +#endif + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Enables EXTI IRQ sources. + * + * @notapi + */ +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 +#if KINETIS_EXT_PORTC_WIDTH > 0 + nvicEnableVector(PINC_IRQn, KINETIS_EXT_PORTC_IRQ_PRIORITY); +#endif +#if KINETIS_EXT_PORTD_WIDTH > 0 + nvicEnableVector(PIND_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY); +#endif +#if KINETIS_EXT_PORTE_WIDTH > 0 + nvicEnableVector(PINE_IRQn, KINETIS_EXT_PORTE_IRQ_PRIORITY); +#endif +#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ +} + +/** + * @brief Disables EXTI IRQ sources. + * + * @notapi + */ +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 +#if KINETIS_EXT_PORTC_WIDTH > 0 + nvicDisableVector(PINC_IRQn); +#endif +#if KINETIS_EXT_PORTD_WIDTH > 0 + nvicDisableVector(PIND_IRQn); +#endif +#if KINETIS_EXT_PORTE_WIDTH > 0 + nvicDisableVector(PINE_IRQn); +#endif +#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/* + * Generic interrupt handler. + */ +static inline void irq_handler(PORT_TypeDef * const port, const unsigned port_width, const uint8_t *channel_map) { + unsigned pin; + uint32_t isfr = port->ISFR; + + /* Clear all pending interrupts on this port. */ + port->ISFR = 0xFFFFFFFF; + + for (pin = 0; pin < port_width; pin++) { + if (isfr & (1 << pin)) { + expchannel_t channel = channel_map[pin]; + EXTD1.config->channels[channel].cb(&EXTD1, channel); + } + } +} + +/** + * @brief PORTA interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTA_IRQ_VECTOR) && KINETIS_EXT_PORTA_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTA_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTA, KINETIS_EXT_PORTA_WIDTH, porta_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#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. + * + * @isr + */ +#if defined(KINETIS_PORTB_IRQ_VECTOR) && KINETIS_EXT_PORTB_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTB_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTB, KINETIS_EXT_PORTB_WIDTH, portb_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTB_WIDTH > 0 */ + +/** + * @brief PORTC interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTC_IRQ_VECTOR) && KINETIS_EXT_PORTC_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTC_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTC, KINETIS_EXT_PORTC_WIDTH, portc_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTC_WIDTH > 0 */ + +/** + * @brief PORTD interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTD_IRQ_VECTOR) && KINETIS_EXT_PORTD_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTD, KINETIS_EXT_PORTD_WIDTH, portd_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTD_WIDTH > 0 */ + +/** + * @brief PORTE interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTE_IRQ_VECTOR) && KINETIS_EXT_PORTE_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTE_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTE, KINETIS_EXT_PORTE_WIDTH, porte_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTE_WIDTH > 0 */ + +#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level EXT driver initialization. + * + * @notapi + */ +void ext_lld_init(void) { + + /* Driver initialization.*/ + extObjectInit(&EXTD1); +} + +/** + * @brief Configures and activates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_start(EXTDriver *extp) { + expchannel_t channel; + + if (extp->state == EXT_STOP) + ext_lld_exti_irq_enable(); + + /* Configuration of automatic channels.*/ + for (channel = 0; channel < EXT_MAX_CHANNELS; channel++) { + + uint32_t mode = extp->config->channels[channel].mode; + PORT_TypeDef *port = extp->config->channels[channel].port; + uint32_t pin = extp->config->channels[channel].pin; + + /* Initialize the channel map */ +#if KINETIS_EXT_PORTA_WIDTH > 0 + if (port == PORTA) + porta_channel_map[pin] = channel; + else +#endif +#if KINETIS_EXT_PORTB_WIDTH > 0 + if (port == PORTB) + portb_channel_map[pin] = channel; + else +#endif +#if KINETIS_EXT_PORTC_WIDTH > 0 + if (port == PORTC) + portc_channel_map[pin] = channel; + else +#endif +#if KINETIS_EXT_PORTD_WIDTH > 0 + if (port == PORTD) + portd_channel_map[pin] = channel; + else +#endif +#if KINETIS_EXT_PORTE_WIDTH > 0 + if (port == PORTE) + porte_channel_map[pin] = channel; + else +#endif + {} + + if (mode & EXT_CH_MODE_AUTOSTART) + ext_lld_channel_enable(extp, channel); + else if (port != NULL) + ext_lld_channel_disable(extp, channel); + } +} + +/** + * @brief Deactivates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_stop(EXTDriver *extp) { + + if (extp->state == EXT_ACTIVE) + ext_lld_exti_irq_disable(); +} + +/** + * @brief Enables an EXT channel. + * + * @param[in] extp pointer to the @p EXTDriver object + * @param[in] channel channel to be enabled + * + * @notapi + */ +void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { + + uint32_t irqc; + uint32_t mode = extp->config->channels[channel].mode; + if (mode & EXT_CH_MODE_RISING_EDGE) + irqc = PCR_IRQC_RISING_EDGE; + else if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE) + irqc = PCR_IRQC_FALLING_EDGE; + else if (extp->config->channels[channel].mode & EXT_CH_MODE_BOTH_EDGES) + irqc = PCR_IRQC_EITHER_EDGE; + else + irqc = PCR_IRQC_DISABLED; + + PORT_TypeDef *port = extp->config->channels[channel].port; + uint32_t pin = extp->config->channels[channel].pin; + + uint32_t pcr = port->PCR[pin]; + + /* Clear all the IRQC bits */ + pcr &= ~PORTx_PCRn_IRQC_MASK; + /* Set the required IRQC bits */ + pcr |= PORTx_PCRn_IRQC(irqc); + + port->PCR[pin] = pcr; +} + +/** + * @brief Disables an EXT channel. + * + * @param[in] extp pointer to the @p EXTDriver object + * @param[in] channel channel to be disabled + * + * @notapi + */ +void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) { + + PORT_TypeDef *port = extp->config->channels[channel].port; + uint32_t pin = extp->config->channels[channel].pin; + port->PCR[pin] |= PORTx_PCRn_IRQC(PCR_IRQC_DISABLED); +} + +#endif /* HAL_USE_EXT */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.h b/os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.h new file mode 100644 index 0000000..0b500a4 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/PORTv1/hal_ext_lld.h @@ -0,0 +1,188 @@ +/* + 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 PORTv1/hal_ext_lld.h + * @brief KINETIS EXT subsystem low level driver header. + * + * @addtogroup EXT + * @{ + */ + +#ifndef HAL_EXT_LLD_H_ +#define HAL_EXT_LLD_H_ + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Number of EXT channels required. + */ +#define EXT_MAX_CHANNELS KINETIS_EXTI_NUM_CHANNELS + +/** + * @name KINETIS-specific EXT channel modes + * @{ + */ +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief PORTA interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTA_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTB interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTB_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTB_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTC interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTC_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTC_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTD interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTD_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTD_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTE interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTE_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTE_IRQ_PRIORITY 3 +#endif +/** @} */ +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief EXT channel identifier. + */ +typedef uint32_t expchannel_t; + +/** + * @brief Type of an EXT generic notification callback. + * + * @param[in] extp pointer to the @p EXPDriver object triggering the + * callback + */ +typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel); + +/** + * @brief Channel configuration structure. + */ +typedef struct { + /** + * @brief Channel mode. + */ + uint32_t mode; + /** + * @brief Channel callback. + */ + extcallback_t cb; + + /** + * @brief Port. + */ + PORT_TypeDef *port; + + /** + * @brief Pin. + */ + uint32_t pin; +} EXTChannelConfig; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Channel configurations. + */ + EXTChannelConfig channels[EXT_MAX_CHANNELS]; + /* End of the mandatory fields.*/ +} EXTConfig; + +/** + * @brief Structure representing an EXT driver. + */ +struct EXTDriver { + /** + * @brief Driver state. + */ + extstate_t state; + /** + * @brief Current configuration data. + */ + const EXTConfig *config; + /* End of the mandatory fields.*/ +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern EXTDriver EXTD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void ext_lld_init(void); + void ext_lld_start(EXTDriver *extp); + void ext_lld_stop(EXTDriver *extp); + void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel); + void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_EXT */ + +#endif /* HAL_EXT_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.c b/os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.c new file mode 100644 index 0000000..6d236eb --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.c @@ -0,0 +1,993 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + Copyright (C) 2017..2018 Wim Lewis + + 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 SDHCv1/hal_sdc_lld.h + * @brief Kinetis SDC subsystem low level driver. + * + * This driver provides a single SDC driver based on the Kinetis + * "Secured Digital Host Controller (SDHC)" peripheral. + * + * In order to use this driver, other peripherals must also be configured: + * + * The MPU must either be disabled (CESR=0), or it must be configured + * to allow the SDHC peripheral DMA access to any data buffers (read + * or write). + * + * The SDHC signals must be routed to the desired pins, and pullups/pulldowns + * configured. + * + * @addtogroup SDC + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_SDC == TRUE) || defined(__DOXYGEN__) + +#include "hal_mmcsd.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#if defined(MK66F18) +/* Configure SDHC block to use the IRC48M clock */ +#define KINETIS_SDHC_PERIPHERAL_FREQUENCY 48000000UL +#else +/* We configure the SDHC block to use the system clock */ +#define KINETIS_SDHC_PERIPHERAL_FREQUENCY KINETIS_SYSCLK_FREQUENCY +#endif + +#ifndef KINETIS_SDHC_PRIORITY +#define KINETIS_SDHC_PRIORITY 12 /* TODO? Default IRQ priority for SDHC */ +#endif + +/* The DTOC value (data timeout counter) controls how long the SDHC + will wait for a data transfer before indicating a timeout to + us. The card can tell us how long that should be, but various SDHC + documentation suggests that we should always allow around 500 msec + even if the card says it will finish sooner. This only comes into + play if there's a malfunction or something, so it's not critical to + get it exactly right. + + It controls the ratio between the SDCLK frequency and the + timeout, so we have a different DTOCV for each bus clock + frequency. +*/ +#define DTOCV_300ms_400kHz 4 /* 4 -> 2^17 -> 328 msec */ +#define DTOCV_700ms_25MHz 11 /* 11 -> 2^24 -> 671 msec */ +#define DTOCV_700ms_50MHz 12 /* 12 -> 2^25 -> 671 msec */ + +#if 0 +#define TRACE(t, val) chDbgWriteTrace ((void *)t, (void *)(uintptr_t)(val)) +#define TRACEI(t, val) chDbgWriteTraceI((void *)t, (void *)(uintptr_t)(val)) +#else +#define TRACE(t, val) +#define TRACEI(t, val) +#endif + +#define DIV_RND_UP(a, b) ( ((a)+(b)-1) / (b) ) + +/* Error bits from the SD / MMC Card Status response word. */ +/* TODO: These really belong in a HLD, not here. */ +#define MMC_ERR_OUT_OF_RANGE (1U << 31) +#define MMC_ERR_ADDRESS (1U << 30) +#define MMC_ERR_BLOCK_LEN (1U << 29) +#define MMC_ERR_ERASE_SEQ (1U << 28) +#define MMC_ERR_ERASE_PARAM (1U << 27) +#define MMC_ERR_WP (1U << 26) +#define MMC_ERR_CARD_IS_LOCKED (1U << 25) +#define MMC_ERR_LOCK_UNLOCK_FAILED (1U << 24) +#define MMC_ERR_COM_CRC_ERROR (1U << 23) +#define MMC_ERR_ILLEGAL_COMMAND (1U << 22) +#define MMC_ERR_CARD_ECC_FAILED (1U << 21) +#define MMC_ERR_CARD_CONTROLLER (1U << 20) +#define MMC_ERR_ERROR (1U << 19) +#define MMC_ERR_CSD_OVERWRITE (1U << 16) +#define MMC_ERR_AKE_SEQ (1U << 3) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief SDCD1 driver identifier. + */ +#if (PLATFORM_SDC_USE_SDC1 == TRUE) || defined(__DOXYGEN__) +SDCDriver SDCD1; +#else +#error HAL_USE_SDC is true but PLATFORM_SDC_USE_SDC1 is false +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void recover_after_botched_transfer(SDCDriver *); +static msg_t wait_interrupt(SDCDriver *, uint32_t); +static bool sdc_lld_transfer(SDCDriver *, uint32_t, uintptr_t, uint32_t, uint32_t); + +/** + * Compute the SDCLKFS and DVS values for a given SDCLK divisor. + * + * Note that in the current code, this function is always called with + * a constant argument (there are only a handful of valid SDCLK + * frequencies), and so GCC computes the results at compile time and + * does not actually emit this function into the output at all unless + * you're compiling with optimizations turned off. + * + * However if someone compiles with a KINETIS_SDHC_PERIPHERAL_FREQUENCY + * that is not a compile-time constant, this function would get emitted. + */ +static uint32_t divisor_settings(unsigned divisor) +{ + /* First, handle all the special cases */ + if (divisor <= 1) { + /* Pass through */ + return SDHC_SYSCTL_SDCLKFS(0) | SDHC_SYSCTL_DVS(0); + } + if (divisor <= 16 && (divisor & 0x01)) { + /* Disable the prescaler, just use the divider. */ + return SDHC_SYSCTL_SDCLKFS(0) | SDHC_SYSCTL_DVS(divisor - 1); + } + if (divisor <= 32 && !(divisor & 0x01)) { + /* Prescale by 2, but do the rest with the divider */ + return SDHC_SYSCTL_SDCLKFS(0x01) | SDHC_SYSCTL_DVS((divisor >> 1) - 1); + } + if (divisor >= 0x1000) { + /* It's not possible to divide by more than 2^12. If we're asked to, + just do the best we can. */ + return SDHC_SYSCTL_SDCLKFS(0x80) | SDHC_SYSCTL_DVS(0xF); + } + + /* The bit position in SDCLKFS provides a power-of-two prescale + factor, and the four bits in DVS allow division by up to 16 + (division by DVS+1). We want to approximate `divisor` using these + terms, but we want to round up --- it's OK to run the card a + little bit too slow, but not OK to run it a little bit too + fast. */ + + unsigned shift = (8 * sizeof(unsigned int) - 4) - __builtin_clz(divisor); + + /* Shift the divisor value right so that it only occupies the four + lowest bits. Subtract one because that's how the DVS circuit + works. Add one if we shifted any 1-bits off the bottom, so that + we always round up. */ + unsigned dvs = (divisor >> shift) - ((divisor & ((1 << shift)-1))? 0 : 1); + + return SDHC_SYSCTL_SDCLKFS(1 << (shift-1)) | SDHC_SYSCTL_DVS(dvs); +} + +/** + * @brief Enable the SDHC clock when stable. + * + * Waits for the clock divider in the SDHC block to stabilize, then + * enables the SD clock. + */ +static void enable_clock_when_stable(uint32_t new_sysctl) +{ + SDHC->SYSCTL = new_sysctl; + + /* Wait for clock divider to stabilize */ + while(!(SDHC->PRSSTAT & SDHC_PRSSTAT_SDSTB)) { + osalThreadSleepMilliseconds(1); + } + + /* Restart the clock */ + SDHC->SYSCTL = new_sysctl | SDHC_SYSCTL_SDCLKEN; + + /* Wait for clock to stabilize again */ + while(!(SDHC->PRSSTAT & SDHC_PRSSTAT_SDSTB)) { + osalThreadSleepMilliseconds(1); + } +} + +/** + * Translate error bits from a CMD transaction to the HAL's error flag set. + */ +static sdcflags_t translate_cmd_error(uint32_t status) { + /* Translate the failure into the flags understood by the top half */ + + sdcflags_t errors = 0; + + if (status & SDHC_IRQSTAT_CTOE || !(status & SDHC_IRQSTAT_CC)) { + errors |= SDC_COMMAND_TIMEOUT; + } + if (status & SDHC_IRQSTAT_CCE) { + errors |= SDC_CMD_CRC_ERROR; + } + + /* If CTOE and CCE are both set, this indicates that the Kinetis + SDHC peripheral has detected a CMD line conflict in a + multi-master scenario. There's no specific code for that, so just + pass it through as a combined timeout+CRC failure. */ + + /* Translate any other framing and protocol errors into CRC errors. */ + if (status & ~(SDHC_IRQSTAT_CCE|SDHC_IRQSTAT_CTOE|SDHC_IRQSTAT_CC)) { + errors |= SDC_CMD_CRC_ERROR; + } + + return errors; +} + +/** + * Translate error bits from a card's R1 response word into the HAL's + * error flag set. + * + * This function should probably be in the HLD, not here. + */ +static sdcflags_t translate_mmcsd_error(uint32_t cardstatus) { + sdcflags_t errors = 0; + + cardstatus &= MMCSD_R1_ERROR_MASK; + + if (cardstatus & MMC_ERR_COM_CRC_ERROR) + errors |= SDC_CMD_CRC_ERROR; + + if (cardstatus & MMC_ERR_CARD_ECC_FAILED) + errors |= SDC_DATA_CRC_ERROR; + + /* TODO: Extend the HLD error codes at least enough to distinguish + between invalid command/parameter errors (card is OK, but + retrying w/o change won't help) and other errors */ + if (cardstatus & ~(MMC_ERR_COM_CRC_ERROR|MMC_ERR_CARD_ECC_FAILED)) + errors |= SDC_UNHANDLED_ERROR; + + return errors; +} + +/** + * @brief Perform one CMD transaction on the SD bus. + */ +static bool send_and_wait_cmd(SDCDriver *sdcp, uint32_t cmd) { + /* SDCLKEN (CMD clock enabled) should be true; + * SDSTB (clock stable) should be true; + * CIHB (command inhibit / busy) should be false */ + osalDbgAssert((SDHC->PRSSTAT & (SDHC_PRSSTAT_SDSTB|SDHC_PRSSTAT_CIHB)) == SDHC_PRSSTAT_SDSTB, "Not in expected state"); + osalDbgAssert(SDHC->SYSCTL & SDHC_SYSCTL_SDCLKEN, "Clock disabled"); + osalDbgCheck((cmd & SDHC_XFERTYP_DPSEL) == 0); + osalDbgCheck((SDHC->IRQSTAT & (SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | SDHC_IRQSTAT_CCE | + SDHC_IRQSTAT_CTOE | SDHC_IRQSTAT_CC)) == 0); + + /* This initiates the CMD transaction */ + TRACE(1, cmd); + SDHC->XFERTYP = cmd; + + uint32_t events = + SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | SDHC_IRQSTAT_CCE | + SDHC_IRQSTAT_CTOE | /* SDHC_IRQSTAT_CRM | */ SDHC_IRQSTAT_CC; + wait_interrupt(sdcp, SDHC_IRQSTAT_CTOE | SDHC_IRQSTAT_CC); + uint32_t status = SDHC->IRQSTAT & events; + + /* These bits are write-1-to-clear (w1c) */ + SDHC->IRQSTAT = status; + + /* In the normal case, the CC (command complete) bit is set but none + of the others are */ + if (status == SDHC_IRQSTAT_CC) + return HAL_SUCCESS; + + /* Translate the failure into the flags understood by the top half */ + sdcp->errors |= translate_cmd_error(status); + + TRACE(9, SDHC->PRSSTAT); + + /* Issue a reset to the CMD portion of the SDHC peripheral to clear the + error bits and enable subsequent commands */ + SDHC->SYSCTL |= SDHC_SYSCTL_RSTC; + + return HAL_FAILED; +} + +/** + * @brief Perform one data transaction on the SD bus. + */ +static bool send_and_wait_transfer(SDCDriver *sdcp, uint32_t cmd) { + + osalDbgCheck(cmd & SDHC_XFERTYP_DPSEL); + osalDbgCheck(cmd & SDHC_XFERTYP_DMAEN); + + const uint32_t cmd_end_bits = + SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | SDHC_IRQSTAT_CCE | + SDHC_IRQSTAT_CTOE | /* SDHC_IRQSTAT_CRM | */ SDHC_IRQSTAT_CC; + + const uint32_t transfer_end_bits = + SDHC_IRQSTAT_DMAE | SDHC_IRQSTAT_AC12E | SDHC_IRQSTAT_DEBE | + SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE | SDHC_IRQSTAT_TC; + + TRACE(3, cmd); + + osalSysLock(); + osalDbgCheck(sdcp->thread == NULL); + + /* Clear anything pending from an earlier transfer */ + SDHC->IRQSTAT = cmd_end_bits | transfer_end_bits | SDHC_IRQSTAT_DINT; + + /* Enable interrupts on completions or failures */ + uint32_t old_staten = SDHC->IRQSTATEN; + SDHC->IRQSTATEN = (old_staten & ~(SDHC_IRQSTAT_BRR|SDHC_IRQSTAT_BWR)) | (cmd_end_bits | transfer_end_bits | SDHC_IRQSTAT_DINT); + SDHC->IRQSIGEN = SDHC_IRQSTAT_CTOE | SDHC_IRQSTAT_CC; + + /* Start the transfer */ + SDHC->XFERTYP = cmd; + + /* Await an interrupt */ + osalThreadSuspendS(&sdcp->thread); + osalSysUnlock(); + + /* Retrieve the flags and clear them */ + uint32_t cmdstat = SDHC->IRQSTAT & cmd_end_bits; + SDHC->IRQSTAT = cmdstat; + TRACE(2, cmdstat); + + /* If the command failed, the transfer won't happen */ + if (cmdstat != SDHC_IRQSTAT_CC) { + /* The command couldn't be sent, or wasn't acknowledged */ + sdcp->errors |= translate_cmd_error(cmdstat); + + /* Clear the error status */ + SDHC->SYSCTL |= SDHC_SYSCTL_RSTC; + + if (cmdstat == (SDHC_IRQSTAT_CCE|SDHC_IRQSTAT_CTOE)) { + /* A CMD-line conflict is unlikely, but doesn't require further recovery */ + } else { + /* For most error situations, we don't know whether the command + failed to send or we got line noise while receiving. Make sure + we're in a sane state by resetting the connection. */ + recover_after_botched_transfer(sdcp); + } + + return HAL_FAILED; + } + + uint32_t cmdresp = SDHC->CMDRSP[0]; + TRACE(11, cmdresp); + if (cmdresp & MMCSD_R1_ERROR_MASK) { + /* The command was sent, and the card responded with an error indication */ + sdcp->errors |= translate_mmcsd_error(cmdresp); + return HAL_FAILED; + } + + /* Check for end of data transfer phase */ + uint32_t datastat; + for (;;) { + datastat = SDHC->IRQSTAT & (transfer_end_bits | SDHC_IRQSTAT_DINT); + if (datastat & transfer_end_bits) + break; + wait_interrupt(sdcp, transfer_end_bits); + } + TRACE(6, datastat); + SDHC->IRQSTAT = datastat; + + /* Handle data transfer errors */ + if ((datastat & ~(SDHC_IRQSTAT_DINT)) != SDHC_IRQSTAT_TC) { + bool should_cancel = false; + + /* Data phase errors */ + if (datastat & (SDHC_IRQSTAT_DCE|SDHC_IRQSTAT_DEBE)) { + sdcp->errors |= SDC_DATA_CRC_ERROR; + should_cancel = true; + } + if (datastat & SDHC_IRQSTAT_DTOE) { + sdcp->errors |= SDC_DATA_TIMEOUT; + should_cancel = true; + } + + /* Internal DMA error */ + if (datastat & SDHC_IRQSTAT_DMAE) { + sdcp->errors |= SDC_UNHANDLED_ERROR; + if (!(datastat & SDHC_IRQSTAT_TC)) + should_cancel = true; + } + + if (datastat & SDHC_IRQSTAT_AC12E) { + uint32_t cmd12error = SDHC->AC12ERR; + + /* We don't know if CMD12 was successfully executed */ + should_cancel = true; + + if (cmd12error & SDHC_AC12ERR_AC12NE) { + sdcp->errors |= SDC_UNHANDLED_ERROR; + } else { + if (cmd12error & SDHC_AC12ERR_AC12TOE) + sdcp->errors |= SDC_COMMAND_TIMEOUT; + if (cmd12error & (SDHC_AC12ERR_AC12CE|SDHC_AC12ERR_AC12EBE)) + sdcp->errors |= SDC_CMD_CRC_ERROR; + } + } + + if (should_cancel) { + recover_after_botched_transfer(sdcp); + } + + return HAL_FAILED; + } + + /* For a read transfer, make sure the DMA has finished transferring + * to host memory. (For a write transfer, the DMA necessarily finishes + * before the transfer does, so we don't need to wait for it + * specially.) */ + if (!(datastat & SDHC_IRQSTAT_DINT)) { + for(;;) { + datastat = SDHC->IRQSTAT & (SDHC_IRQSTAT_DINT|SDHC_IRQSTAT_DMAE); + if (datastat) { + SDHC->IRQSTAT = datastat; + TRACE(7, datastat); + break; + } + /* ...?? */ + } + } + + SDHC->IRQSTATEN = old_staten; + + return HAL_SUCCESS; +} + +/** + * @brief Wait for an interrupt from the SDHC peripheral. + * + * @param[in] mask Bits to enable in IRQSIGEN. + * + * @return MSG_OK + */ +static msg_t wait_interrupt(SDCDriver *sdcp, uint32_t mask) { + osalSysLock(); + SDHC->IRQSIGEN = mask; + msg_t wakeup = osalThreadSuspendS(&sdcp->thread); + osalSysUnlock(); + return wakeup; +} + +static void recover_after_botched_transfer(SDCDriver *sdcp) { + + /* Query the card state */ + uint32_t cardstatus; + if (sdc_lld_send_cmd_short_crc(sdcp, + MMCSD_CMD_SEND_STATUS, + sdcp->rca, &cardstatus) == HAL_SUCCESS) { + sdcp->errors |= translate_mmcsd_error(cardstatus); + uint32_t state = MMCSD_R1_STS(cardstatus); + if (state == MMCSD_STS_DATA) { + + /* Send a CMD12 to make sure the card isn't still transferring anything */ + SDHC->CMDARG = 0; + send_and_wait_cmd(sdcp, + SDHC_XFERTYP_CMDINX(MMCSD_CMD_STOP_TRANSMISSION) | + SDHC_XFERTYP_CMDTYP_ABORT | + /* TODO: Should we set CICEN and CCCEN here? */ + SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | + SDHC_XFERTYP_RSPTYP_48b); + } + } + + /* And reset the data block of the SDHC peripheral */ + SDHC->SYSCTL |= SDHC_SYSCTL_RSTD; +} + +/** + * @brief Perform one data transfer command + * + * Sends a command to the card and waits for the corresponding data transfer + * (either a read or write) to complete. + */ +static bool sdc_lld_transfer(SDCDriver *sdcp, uint32_t startblk, + uintptr_t buf, uint32_t n, + uint32_t cmdx) { + + osalDbgCheck(n > 0); + osalDbgCheck((buf & 0x03) == 0); /* Must be 32-bit aligned */ + + osalDbgAssert((SDHC->PRSSTAT & (SDHC_PRSSTAT_DLA|SDHC_PRSSTAT_CDIHB|SDHC_PRSSTAT_CIHB)) == 0, + "SDHC interface not ready"); + + /* We always operate in terms of 512-byte blocks; the upper-layer + driver doesn't change the block size. The SDHC spec suggests that + only low-capacity cards support block sizes other than 512 bytes + anyway (SDHC "Physical Layer Simplified Specification" ver 6.0) */ + + if (sdcp->cardmode & SDC_MODE_HIGH_CAPACITY) { + SDHC->CMDARG = startblk; + } else { + SDHC->CMDARG = startblk * MMCSD_BLOCK_SIZE; + } + + /* Store the DMA start address */ + SDHC->DSADDR = buf; + + uint32_t xfer; + /* For data transfers, we need to set some extra bits in XFERTYP according to the + transfer we're starting: + DPSEL -> enable data transfer + DTDSEL -> 1 for a read (card-to-host) transfer + MSBSEL, BCEN -> multiple block transfer using BLKATTR_BLKCNT + AC12EN -> Automatically issue MMCSD_CMD_STOP_TRANSMISSION at end of transfer + + Setting BLKCOUNT to 1 seems to be necessary even if MSBSEL+BCEN + is not set, despite the datasheet suggesting otherwise. I'm not + sure if this is a silicon bug or if I'm misunderstanding the + datasheet. + */ + SDHC->BLKATTR = + SDHC_BLKATTR_BLKCNT(n) | + SDHC_BLKATTR_BLKSIZE(MMCSD_BLOCK_SIZE); + if (n == 1) { + xfer = + cmdx | + SDHC_XFERTYP_CMDTYP_NORMAL | + SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | + SDHC_XFERTYP_RSPTYP_48b | + SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; + } else { + xfer = + cmdx | + SDHC_XFERTYP_CMDTYP_NORMAL | + SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | + SDHC_XFERTYP_RSPTYP_48b | + SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_BCEN | SDHC_XFERTYP_AC12EN | + SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; + } + + return send_and_wait_transfer(sdcp, xfer); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if (PLATFORM_SDC_USE_SDC1 == TRUE) || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SDHC_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + osalSysLockFromISR(); + + TRACEI(4, SDHC->IRQSTAT); + + /* We disable the interrupts, and wake up the usermode task to read + * the flags from IRQSTAT. + */ + SDHC->IRQSIGEN = 0; + + osalThreadResumeI(&SDCD1.thread, MSG_OK); + + osalSysUnlockFromISR(); + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level SDC driver initialization. + * + * @notapi + */ +void sdc_lld_init(void) { +#if PLATFORM_SDC_USE_SDC1 == TRUE + sdcObjectInit(&SDCD1); +#endif +} + + +/** + * @brief Configures and activates the SDC peripheral. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_start(SDCDriver *sdcp) { + + if (sdcp->state == BLK_STOP) { +#if defined(MK66F18) + /* Use IRC48M clock for SDHC */ + SIM->SOPT2 |= SIM_SOPT2_SDHCSRC(1); + SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3); +#else + SIM->SOPT2 = + (SIM->SOPT2 & ~SIM_SOPT2_SDHCSRC_MASK) | + SIM_SOPT2_SDHCSRC(0); /* SDHC clock source 0: Core/system clock. */ +#endif + SIM->SCGC3 |= SIM_SCGC3_SDHC; /* Enable clock to SDHC peripheral */ + + /* Reset the SDHC block */ + SDHC->SYSCTL |= SDHC_SYSCTL_RSTA; + while(SDHC->SYSCTL & SDHC_SYSCTL_RSTA) { + osalThreadSleepMilliseconds(1); + } + + SDHC->IRQSIGEN = 0; + nvicEnableVector(SDHC_IRQn, KINETIS_SDHC_PRIORITY); + } +} + +/** + * @brief Deactivates the SDC peripheral. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_stop(SDCDriver *sdcp) { + + if (sdcp->state != BLK_STOP) { + /* TODO: Should we perform a reset (RSTA) before putting the + peripheral to sleep? */ + + /* Disable the card clock */ + SDHC->SYSCTL &= ~( SDHC_SYSCTL_SDCLKEN ); + + /* Turn off interrupts */ + nvicDisableVector(SDHC_IRQn); + SDHC->IRQSIGEN = 0; + SDHC->IRQSTATEN &= ~( SDHC_IRQSTATEN_CINTSEN | + SDHC_IRQSTATEN_CINSEN | + SDHC_IRQSTATEN_CRMSEN ); + + /* Disable the clock to the SDHC peripheral block */ + SIM->SCGC3 &= ~( SIM_SCGC3_SDHC ); + } +} + +/** + * @brief Starts the SDIO clock and sets it to init mode (400kHz or less). + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_start_clk(SDCDriver *sdcp) { + + (void)sdcp; + + /* Stop the card clock (it should already be stopped) */ + SDHC->SYSCTL &= ~( SDHC_SYSCTL_SDCLKEN ); + + /* Change the divisor and DTOCV for a 400kHz card closk */ + uint32_t sysctl = + SDHC_SYSCTL_DTOCV(DTOCV_300ms_400kHz) | + divisor_settings(DIV_RND_UP(KINETIS_SDHC_PERIPHERAL_FREQUENCY, 400000)); + + /* Restart the clock */ + enable_clock_when_stable(sysctl); + + /* Reset any protocol machinery; this also runs the clock for 80 + cycles without any data bits to help initalize the card's state + (the Kinetis peripheral docs say that this is required after card + insertion or power-on, but the abridged SDHC specifications I + have don't seem to mention it) */ + SDHC->SYSCTL |= SDHC_SYSCTL_INITA; + + TRACE(9, SDHC->PRSSTAT); +} + +/** + * @brief Sets the SDIO clock to data mode (25MHz or less). + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] clk the clock mode + * + * @notapi + */ +void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk) { + + (void)sdcp; + + /* Stop the card clock */ + SDHC->SYSCTL &= ~( SDHC_SYSCTL_SDCLKEN ); + + /* Change the divisor */ + uint32_t ctl; + switch (clk) { + default: + case SDC_CLK_25MHz: + ctl = + SDHC_SYSCTL_DTOCV(DTOCV_700ms_25MHz) | + divisor_settings(DIV_RND_UP(KINETIS_SDHC_PERIPHERAL_FREQUENCY, 25000000)); + break; + case SDC_CLK_50MHz: + ctl = + SDHC_SYSCTL_DTOCV(DTOCV_700ms_50MHz) | + divisor_settings(DIV_RND_UP(KINETIS_SDHC_PERIPHERAL_FREQUENCY, 50000000)); + break; + } + + /* Restart the clock */ + enable_clock_when_stable(ctl); +} + +/** + * @brief Stops the SDIO clock. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_stop_clk(SDCDriver *sdcp) { + (void)sdcp; + SDHC->SYSCTL &= ~( SDHC_SYSCTL_SDCLKEN ); +} + +/** + * @brief Switches the bus to 4 bit or 8 bit mode. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] mode bus mode + * + * @notapi + */ +void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) { + (void)sdcp; + uint32_t proctl = SDHC->PROCTL & ~( SDHC_PROCTL_DTW_MASK ); + + switch (mode) { + case SDC_MODE_1BIT: + proctl |= SDHC_PROCTL_DTW_1BIT; + break; + case SDC_MODE_4BIT: + proctl |= SDHC_PROCTL_DTW_4BIT; + break; + case SDC_MODE_8BIT: + proctl |= SDHC_PROCTL_DTW_8BIT; + break; + default: + osalDbgAssert(false, "invalid bus mode"); + break; + } + + SDHC->PROCTL = proctl; +} + +/** + * @brief Sends an SDIO command with no response expected. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * + * @notapi + */ +void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) { + SDHC->CMDARG = arg; + uint32_t xfer = + SDHC_XFERTYP_CMDINX(cmd) | + SDHC_XFERTYP_CMDTYP_NORMAL | + /* DPSEL=0, CICEN=0, CCCEN=0 */ + SDHC_XFERTYP_RSPTYP_NONE; + + send_and_wait_cmd(sdcp, xfer); +} + +/** + * @brief Sends an SDIO command with a short response expected. + * @note The CRC is not verified. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (one word) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + SDHC->CMDARG = arg; + uint32_t xfer = + SDHC_XFERTYP_CMDINX(cmd) | + SDHC_XFERTYP_CMDTYP_NORMAL | + /* DPSEL=0, CICEN=0, CCCEN=0 */ + SDHC_XFERTYP_RSPTYP_48; + + bool waited = send_and_wait_cmd(sdcp, xfer); + + *resp = SDHC->CMDRSP[0]; + + return waited; +} + +/** + * @brief Sends an SDIO command with a short response expected and CRC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (one word) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + SDHC->CMDARG = arg; + uint32_t xfer = + SDHC_XFERTYP_CMDINX(cmd) | + SDHC_XFERTYP_CMDTYP_NORMAL | + /* DPSEL=0, CICEN=1, CCCEN=1 */ + SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | + SDHC_XFERTYP_RSPTYP_48; + + bool waited = send_and_wait_cmd(sdcp, xfer); + + *resp = SDHC->CMDRSP[0]; + TRACE(11, *resp); + + return waited; +} + +/** + * @brief Sends an SDIO command with a long response expected and CRC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (four words) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + + /* In response format R2 (the 136-bit or "long" response) the CRC7 + field is valid, but the command index field is set to all 1s, so + we need to disable the command index check function (CICEN=0). */ + + SDHC->CMDARG = arg; + uint32_t xfer = + SDHC_XFERTYP_CMDINX(cmd) | + SDHC_XFERTYP_CMDTYP_NORMAL | + /* DPSEL=0, CICEN=0, CCCEN=1 */ + SDHC_XFERTYP_CCCEN | + SDHC_XFERTYP_RSPTYP_136; + + bool waited = send_and_wait_cmd(sdcp, xfer); + + resp[0] = SDHC->CMDRSP[0]; + resp[1] = SDHC->CMDRSP[1]; + resp[2] = SDHC->CMDRSP[2]; + resp[3] = SDHC->CMDRSP[3]; + + return waited; +} + +/** + * @brief Reads one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[out] buf pointer to the read buffer + * @param[in] n number of blocks to read + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ + +bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t n) { + uint32_t cmdx = (n == 1)? + SDHC_XFERTYP_CMDINX(MMCSD_CMD_READ_SINGLE_BLOCK) : + SDHC_XFERTYP_CMDINX(MMCSD_CMD_READ_MULTIPLE_BLOCK); + cmdx |= SDHC_XFERTYP_DTDSEL; + + return sdc_lld_transfer(sdcp, startblk, (uintptr_t)buf, n, cmdx); +} + +/** + * @brief Writes one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to write + * @param[out] buf pointer to the write buffer + * @param[in] n number of blocks to write + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t n) { + uint32_t cmdx = (n == 1)? + SDHC_XFERTYP_CMDINX(MMCSD_CMD_WRITE_BLOCK) : + SDHC_XFERTYP_CMDINX(MMCSD_CMD_WRITE_MULTIPLE_BLOCK); + + return sdc_lld_transfer(sdcp, startblk, (uintptr_t)buf, n, cmdx); +} + +/** + * @brief Waits for card idle condition. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval HAL_SUCCESS the operation succeeded. + * @retval HAL_FAILED the operation failed. + * + * @api + */ +bool sdc_lld_sync(SDCDriver *sdcp) { + + (void)sdcp; + + return HAL_SUCCESS; +} + +bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, + uint8_t cmd, uint32_t argument) { + uintptr_t bufaddr = (uintptr_t)buf; + + osalDbgCheck((bufaddr & 0x03) == 0); /* Must be 32-bit aligned */ + osalDbgCheck(bytes > 0); + osalDbgCheck(bytes < 4096); + + osalDbgAssert((SDHC->PRSSTAT & (SDHC_PRSSTAT_DLA|SDHC_PRSSTAT_CDIHB|SDHC_PRSSTAT_CIHB)) == 0, + "SDHC interface not ready"); + + TRACE(5, argument); + + /* Store the cmd argument and DMA start address */ + SDHC->CMDARG = argument; + SDHC->DSADDR = bufaddr; + + /* We're reading one block, of a (possibly) nonstandard size */ + SDHC->BLKATTR = SDHC_BLKATTR_BLKSIZE(bytes); + + uint32_t xfer = + SDHC_XFERTYP_CMDINX(cmd) | /* the command */ + SDHC_XFERTYP_DTDSEL | /* read transfer (card -> host) */ + SDHC_XFERTYP_CMDTYP_NORMAL | + SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | + SDHC_XFERTYP_RSPTYP_48 | + SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; /* DMA-assisted data transfer */ + + return send_and_wait_transfer(sdcp, xfer); +} + +bool sdc_lld_is_card_inserted(SDCDriver *sdcp) { + (void)sdcp; + + return ( SDHC->PRSSTAT & SDHC_PRSSTAT_CLSL )? true : false; +} + +bool sdc_lld_is_write_protected(SDCDriver *sdcp) { + (void)sdcp; + return false; +} + +#endif /* HAL_USE_SDC == TRUE */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.h b/os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.h new file mode 100644 index 0000000..cfa23cb --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/SDHCv1/hal_sdc_lld.h @@ -0,0 +1,202 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + Copyright (C) 2017 Wim Lewis + + 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 SDHCv1/hal_sdc_lld.h + * @brief PLATFORM SDC subsystem low level driver header. + * + * @addtogroup SDC + * @{ + */ + +#ifndef HAL_SDC_LLD_H +#define HAL_SDC_LLD_H + +#if (HAL_USE_SDC == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define SDHC_XFERTYP_CMDTYP_NORMAL SDHC_XFERTYP_CMDTYP(0) +#define SDHC_XFERTYP_CMDTYP_SUSPEND SDHC_XFERTYP_CMDTYP(1) +#define SDHC_XFERTYP_CMDTYP_RESUME SDHC_XFERTYP_CMDTYP(2) +#define SDHC_XFERTYP_CMDTYP_ABORT SDHC_XFERTYP_CMDTYP(3) + +#define SDHC_XFERTYP_RSPTYP_NONE SDHC_XFERTYP_RSPTYP(0) /* no response */ +#define SDHC_XFERTYP_RSPTYP_136 SDHC_XFERTYP_RSPTYP(1) /* 136-bit response */ +#define SDHC_XFERTYP_RSPTYP_48 SDHC_XFERTYP_RSPTYP(2) /* 48-bit response */ +#define SDHC_XFERTYP_RSPTYP_48b SDHC_XFERTYP_RSPTYP(3) /* 48-bit plus busy */ + +#define SDHC_PROCTL_DTW_1BIT SDHC_PROCTL_DTW(0) +#define SDHC_PROCTL_DTW_4BIT SDHC_PROCTL_DTW(1) +#define SDHC_PROCTL_DTW_8BIT SDHC_PROCTL_DTW(2) + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name PLATFORM configuration options + * @{ + */ +/** + * @brief SDC1 driver enable switch. + * @details If set to @p TRUE the support for SDC1 is included. + * @note The default is @p TRUE if HAL_USE_SDC is set. + */ +#if !defined(PLATFORM_SDC_USE_SDC1) || defined(__DOXYGEN__) +#define PLATFORM_SDC_USE_SDC1 TRUE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of card flags. + */ +typedef uint32_t sdcmode_t; + +/** + * @brief SDC Driver condition flags type. + */ +typedef uint32_t sdcflags_t; + +/** + * @brief Type of a structure representing an SDC driver. + */ +typedef struct SDCDriver SDCDriver; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Working area for memory consuming operations. + * @note It is mandatory for detecting MMC cards bigger than 2GB else it + * can be @p NULL. + * @note Memory pointed by this buffer is only used by @p sdcConnect(), + * afterward it can be reused for other purposes. + */ + uint8_t *scratchpad; + /** + * @brief Bus width. + */ + sdcbusmode_t bus_width; + /* End of the mandatory fields.*/ +} SDCConfig; + +/** + * @brief @p SDCDriver specific methods. + */ +#define _sdc_driver_methods \ + _mmcsd_block_device_methods + +/** + * @extends MMCSDBlockDeviceVMT + * + * @brief @p SDCDriver virtual methods table. + */ +struct SDCDriverVMT { + _sdc_driver_methods +}; + +/** + * @brief Structure representing an SDC driver. + */ +struct SDCDriver { + /** + * @brief Virtual Methods Table. + */ + const struct SDCDriverVMT *vmt; + _mmcsd_block_device_data + /** + * @brief Current configuration data. + */ + const SDCConfig *config; + /** + * @brief Various flags regarding the mounted card. + */ + sdcmode_t cardmode; + /** + * @brief Errors flags. + */ + sdcflags_t errors; + /** + * @brief Card RCA. + */ + uint32_t rca; + /* End of the mandatory fields.*/ + + /* Platform specific fields */ + thread_reference_t thread; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if (PLATFORM_SDC_USE_SDC1 == TRUE) && !defined(__DOXYGEN__) +extern SDCDriver SDCD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void sdc_lld_init(void); + void sdc_lld_start(SDCDriver *sdcp); + void sdc_lld_stop(SDCDriver *sdcp); + void sdc_lld_start_clk(SDCDriver *sdcp); + void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk); + void sdc_lld_stop_clk(SDCDriver *sdcp); + void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode); + void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg); + bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, + uint8_t cmd, uint32_t argument); + bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t n); + bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t n); + bool sdc_lld_sync(SDCDriver *sdcp); + bool sdc_lld_is_card_inserted(SDCDriver *sdcp); + bool sdc_lld_is_write_protected(SDCDriver *sdcp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SDC == TRUE */ + +#endif /* HAL_SDC_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.c b/os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.c new file mode 100644 index 0000000..443ec5f --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.c @@ -0,0 +1,692 @@ +/* + ChibiOS - Copyright (C) 2013-2015 Fabio Utzig + Copyright (C) 2017 Wim Lewis + + 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 UARTv1/hal_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 + +#if KINETIS_SERIAL_USE_UART3 || defined(__DOXYGEN__) +SerialDriver SD4; +#endif + +#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__) +SerialDriver SD5; +#endif + +#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__) +SerialDriver SD6; +#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); + + /* Clearing on K20x, K60x, and KL2x/UART>0 is done by reading S1 and + * then reading D.*/ + + 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); + } +} + +#if defined(KL2x) && KINETIS_SERIAL_USE_UART0 +static void serve_error_interrupt_uart0(void) { + SerialDriver *sdp = &SD1; + UART_w_TypeDef *u = &(sdp->uart); + uint8_t s1 = *(u->s1_p); + + /* S1 bits are write-1-to-clear for UART0 on KL2x. */ + + 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; + } +} +#endif /* KL2x && KINETIS_SERIAL_USE_UART0 */ + +/** + * @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; + } + } + +#if defined(KL2x) && KINETIS_SERIAL_USE_UART0 + if (sdp == &SD1) { + serve_error_interrupt_uart0(); + return; + } +#endif + 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. + */ +static void notify(io_queue_t *qp) +{ + preload(qp->q_link); +} + +/** + * @brief Common driver initialization, except LP. + */ +static void sd_lld_init_driver(SerialDriver *SDn, UART_TypeDef *UARTn) { + /* Driver initialization.*/ + sdObjectInit(SDn, NULL, notify); + SDn->uart.bdh_p = &(UARTn->BDH); + SDn->uart.bdl_p = &(UARTn->BDL); + SDn->uart.c1_p = &(UARTn->C1); + SDn->uart.c2_p = &(UARTn->C2); + SDn->uart.c3_p = &(UARTn->C3); + SDn->uart.c4_p = &(UARTn->C4); + SDn->uart.s1_p = (volatile uint8_t *)&(UARTn->S1); + SDn->uart.s2_p = &(UARTn->S2); + SDn->uart.d_p = &(UARTn->D); + SDn->uart.uart_p = UARTn; +} + +/** + * @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) || defined(K60x) || defined(MK66F18) /* KL2x */ + + /* UARTs 0 and 1 are clocked from SYSCLK, others from BUSCLK on K20x and K60x. */ +#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) || defined(K60x) + *(uart->c4_p) = UARTx_C4_BRFA(divisor) | (*(uart->c4_p) & ~UARTx_C4_BRFA_MASK); +#endif /* K20x, K60x */ + + /* 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_SERIAL_USE_UART3 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL3_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&SD4); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL4_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&SD5); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL5_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&SD6); + 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(); +#if defined(KL2x) + serve_error_interrupt_uart0(); +#else + serve_error_interrupt(&SD1); +#endif + 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 + +#if KINETIS_SERIAL_USE_UART3 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL3_ERROR_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_error_interrupt(&SD4); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL4_ERROR_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_error_interrupt(&SD5); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(KINETIS_SERIAL5_ERROR_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + serve_error_interrupt(&SD6); + 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.*/ +#if ! KINETIS_SERIAL0_IS_LPUART + sd_lld_init_driver(&SD1, UART0); +#else /* ! KINETIS_SERIAL0_IS_LPUART */ + /* little endian! */ + sdObjectInit(&SD1, NULL, notify); + 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.*/ +#if ! KINETIS_SERIAL1_IS_LPUART + sd_lld_init_driver(&SD2, UART1); +#else /* ! KINETIS_SERIAL1_IS_LPUART */ + /* little endian! */ + sdObjectInit(&SD2, NULL, notify); + 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 + sd_lld_init_driver(&SD3, UART2); +#endif /* KINETIS_SERIAL_USE_UART2 */ + +#if KINETIS_SERIAL_USE_UART3 + sd_lld_init_driver(&SD4, UART3); +#endif /* KINETIS_SERIAL_USE_UART3 */ + +#if KINETIS_SERIAL_USE_UART4 + sd_lld_init_driver(&SD5, UART4); +#endif /* KINETIS_SERIAL_USE_UART4 */ + +#if KINETIS_SERIAL_USE_UART5 + sd_lld_init_driver(&SD6, UART5); +#endif /* KINETIS_SERIAL_USE_UART5 */ +} + +/** + * @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 */ + +#if KINETIS_SERIAL_USE_UART3 + if (sdp == &SD4) { + SIM->SCGC4 |= SIM_SCGC4_UART3; + configure_uart(sdp, config); + nvicEnableVector(UART3Status_IRQn, KINETIS_SERIAL_UART3_PRIORITY); + nvicEnableVector(UART3Error_IRQn, KINETIS_SERIAL_UART3_PRIORITY); + } +#endif /* KINETIS_SERIAL_USE_UART3 */ + +#if KINETIS_SERIAL_USE_UART4 + if (sdp == &SD5) { + SIM->SCGC1 |= SIM_SCGC1_UART4; + configure_uart(sdp, config); + nvicEnableVector(UART4Status_IRQn, KINETIS_SERIAL_UART4_PRIORITY); + nvicEnableVector(UART4Error_IRQn, KINETIS_SERIAL_UART4_PRIORITY); + } +#endif /* KINETIS_SERIAL_USE_UART4 */ + +#if KINETIS_SERIAL_USE_UART5 + if (sdp == &SD6) { + SIM->SCGC1 |= SIM_SCGC1_UART5; + configure_uart(sdp, config); + nvicEnableVector(UART5Status_IRQn, KINETIS_SERIAL_UART5_PRIORITY); + nvicEnableVector(UART5Error_IRQn, KINETIS_SERIAL_UART5_PRIORITY); + } +#endif /* KINETIS_SERIAL_USE_UART5 */ + + } + /* 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 + +#if KINETIS_SERIAL_USE_UART3 + if (sdp == &SD4) { + nvicDisableVector(UART3Status_IRQn); + nvicDisableVector(UART3Error_IRQn); + SIM->SCGC4 &= ~SIM_SCGC4_UART3; + } +#endif + +#if KINETIS_SERIAL_USE_UART4 + if (sdp == &SD5) { + nvicDisableVector(UART4Status_IRQn); + nvicDisableVector(UART4Error_IRQn); + SIM->SCGC1 &= ~SIM_SCGC1_UART4; + } +#endif + +#if KINETIS_SERIAL_USE_UART5 + if (sdp == &SD6) { + nvicDisableVector(UART5Status_IRQn); + nvicDisableVector(UART5Error_IRQn); + SIM->SCGC1 &= ~SIM_SCGC1_UART5; + } +#endif + } +} + +#endif /* HAL_USE_SERIAL */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.h b/os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.h new file mode 100644 index 0000000..df7045b --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.h @@ -0,0 +1,287 @@ +/* + 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 UARTv1/hal_serial_lld.h + * @brief Kinetis KL2x Serial Driver subsystem low level driver header. + * + * @addtogroup SERIAL + * @{ + */ + +#ifndef HAL_SERIAL_LLD_H_ +#define HAL_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 SD4 driver enable switch. + * @details If set to @p TRUE the support for SD4 is included. + */ +#if !defined(KINETIS_SERIAL_USE_UART3) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_USE_UART3 FALSE +#endif +/** + * @brief SD5 driver enable switch. + * @details If set to @p TRUE the support for SD5 is included. + */ +#if !defined(KINETIS_SERIAL_USE_UART4) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_USE_UART4 FALSE +#endif +/** + * @brief SD6 driver enable switch. + * @details If set to @p TRUE the support for SD6 is included. + */ +#if !defined(KINETIS_SERIAL_USE_UART5) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_USE_UART5 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 UART3 interrupt priority level setting. + */ +#if !defined(KINETIS_SERIAL_UART3_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_UART3_PRIORITY 12 +#endif + +/** + * @brief UART4 interrupt priority level setting. + */ +#if !defined(KINETIS_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_UART4_PRIORITY 12 +#endif + +/** + * @brief UART5 interrupt priority level setting. + */ +#if !defined(KINETIS_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_SERIAL_UART5_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_UART3 && !KINETIS_HAS_SERIAL3 +#error "UART3 not present in the selected device" +#endif + +#if KINETIS_SERIAL_USE_UART4 && !KINETIS_HAS_SERIAL4 +#error "UART4 not present in the selected device" +#endif + +#if KINETIS_SERIAL_USE_UART5 && !KINETIS_HAS_SERIAL5 +#error "UART5 not present in the selected device" +#endif + +#if !(KINETIS_SERIAL_USE_UART0 || KINETIS_SERIAL_USE_UART1 || \ + KINETIS_SERIAL_USE_UART2 || KINETIS_SERIAL_USE_UART3 || \ + KINETIS_SERIAL_USE_UART4 || KINETIS_SERIAL_USE_UART5) +#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 + +#if KINETIS_SERIAL_USE_UART3 && !defined(__DOXYGEN__) +extern SerialDriver SD4; +#endif + +#if KINETIS_SERIAL_USE_UART4 && !defined(__DOXYGEN__) +extern SerialDriver SD5; +#endif + +#if KINETIS_SERIAL_USE_UART5 && !defined(__DOXYGEN__) +extern SerialDriver SD6; +#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 /* HAL_SERIAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c new file mode 100644 index 0000000..dc1fca9 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c @@ -0,0 +1,873 @@ +/* + 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 USBHSv1/hal_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 receiving in the chibios state machine */ + (usbp)->receiving &= ~1; + /* Call SETUP function (ChibiOS core), which prepares + * for send or receive and releases the buffer + */ + _usb_isr_invoke_setup_cb(usbp, ep); + /* When a setup packet is received, tx is suspended, + * so it needs to be resumed here. + */ + USB0->CTL &= ~USBx_CTL_TXSUSPENDTOKENBUSY; + } break; + case BDT_PID_IN: // IN + { + 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 + + /* Set USB clock source to MCGPLLCLK, MCGFLLCLK, USB1 PFD, or IRC48M */ + SIM->SOPT2 |= SIM_SOPT2_USBSRC; + +#if defined(K20x5) || defined(K20x7) || defined(MK66F18) + +#if KINETIS_MCG_MODE == KINETIS_MCG_MODE_FEI + + /* MCGOUTCLK is the SYSCLK frequency, so don't divide for USB clock */ + SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); + +#elif KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE + +#if !defined(MK66F18) + /* Note: We don't need this for MK66F18, we can use IRC48M clock for USB */ + #define KINETIS_USBCLK_FREQUENCY 48000000UL + uint32_t i,j; + for(i=0; i < 2; i++) { + 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: + osalDbgAssert(i<2 && j <8,"USB Init error"); +#endif + +#else /* KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE */ +#error USB clock setting not implemented for this KINETIS_MCG_MODE +#endif /* KINETIS_MCG_MODE == ... */ + +#if defined(MK66F18) + /* Switch from default MCGPLLCLK to IRC48M for USB */ + SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); + SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3); +#endif + +#elif defined(KL25) || defined (KL26) || defined(KL27) + + /* No extra clock dividers for USB clock */ + +#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;iUSBPHYCTL |= SIM_USBPHYCTL_USBDISILIM; +#endif + + /* Enable Clock */ +#if KINETIS_USB0_IS_USBOTG + SIM->SCGC4 |= SIM_SCGC4_USBOTG; + +#else /* KINETIS_USB0_IS_USBOTG */ + SIM->SCGC4 |= SIM_SCGC4_USBFS; +#endif /* KINETIS_USB0_IS_USBOTG */ + +#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 + * Release the current DATA0 buffer + */ + bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0); + /* If DATA1 was expected, then the states are out of sync. + * So reset the other buffer too, and set it as DATA1. + * This should not happen in normal cases, but is possible in + * error situations. NOTE: it's possible that this is too late + * and the next packet has already been received and dropped, but + * there's nothing that we can do about that anymore at this point. + */ + if (os->data_bank == DATA1) + { + bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, os->odd_even^ODD)]; + bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); + } + /* After a SETUP, both in and out are always DATA1 */ + usbp->epc[ep]->in_state->data_bank = DATA1; + os->data_bank = DATA1; +} + +/** + * @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) { + if (ep == 0 && usbp->ep0state == USB_EP0_IN_SENDING_STS) { + /* When a status packet is about to be sent on endpoint 0 the + * next packet will be a setup packet, which means that the + * buffer we expect after this should be DATA0, and the following + * DATA1. Since no out packets should be in flight at this time + * it's safe to initialize the buffers according to the expectations + * here. + */ + const USBEndpointConfig* epc = usbp->epc[ep]; + bd_t * bd = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even)]; + bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even^ODD)]; + + bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); + bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0); + epc->out_state->data_bank = DATA0; + } + usb_packet_transmit(usbp,ep,usbp->epc[ep]->in_state->txsize); +} + +/** + * @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/USBHSv1/hal_usb_lld.h b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h new file mode 100644 index 0000000..05dd9c8 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.h @@ -0,0 +1,455 @@ +/* + 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 USBHSv1/hal_usb_lld.h + * @brief KINETIS USB subsystem low level driver header. + * + * @addtogroup USB + * @{ + */ + +#ifndef HAL_USB_LLD_H_ +#define HAL_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 + +/** + * @brief Host wake-up procedure duration. + */ +#if !defined(USB_HOST_WAKEUP_DURATION) || defined(__DOXYGEN__) +#define USB_HOST_WAKEUP_DURATION 2 +#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 + +#if (USB_HOST_WAKEUP_DURATION < 2) || (USB_HOST_WAKEUP_DURATION > 15) +#error "invalid USB_HOST_WAKEUP_DURATION setting, it must be between 2 and 15" +#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; + /** + * @brief State of the driver when a suspend happened. + */ + usbstate_t saved_state; +#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 + +/** + * @brief Start of host wake-up procedure. + * + * @notapi + */ +#define usb_lld_wakeup_host(usbp) \ + do{ \ + USB0->CTL |= USBx_CTL_RESUME; \ + osalThreadSleepMilliseconds(USB_HOST_WAKEUP_DURATION); \ + USB0->CTL &= ~USBx_CTL_RESUME; \ + } while (false) + +/*===========================================================================*/ +/* 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 /* HAL_USB_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_adc_lld.c b/os/hal/ports/KINETIS/LLD/hal_adc_lld.c deleted file mode 100644 index 56ae4c3..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_adc_lld.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - 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/LLD/adc_lld.c - * @brief KINETIS ADC subsystem low level driver source. - * - * @addtogroup ADC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define ADC_CHANNEL_MASK 0x1f - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief ADC1 driver identifier.*/ -#if KINETIS_ADC_USE_ADC0 || defined(__DOXYGEN__) -ADCDriver ADCD1; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static void calibrate(ADCDriver *adcp) { - - /* Clock Divide by 8, Use Bus Clock Div 2 */ - /* At 48MHz this results in ADCCLK of 48/8/2 == 3MHz */ - adcp->adc->CFG1 = ADCx_CFG1_ADIV(ADCx_CFG1_ADIV_DIV_8) | - ADCx_CFG1_ADICLK(ADCx_CFG1_ADIVCLK_BUS_CLOCK_DIV_2); - - /* Use software trigger and disable DMA etc. */ - adcp->adc->SC2 = 0; - - /* Enable Hardware Average, Average 32 Samples, Calibrate */ - adcp->adc->SC3 = ADCx_SC3_AVGE | - ADCx_SC3_AVGS(ADCx_SC3_AVGS_AVERAGE_32_SAMPLES) | - ADCx_SC3_CAL; - - /* FIXME: May take several ms. Use an interrupt instead of busy wait */ - /* Wait for calibration completion */ - while (!(adcp->adc->SC1A & ADCx_SC1n_COCO)) - ; - - uint16_t gain = ((adcp->adc->CLP0 + adcp->adc->CLP1 + adcp->adc->CLP2 + - adcp->adc->CLP3 + adcp->adc->CLP4 + adcp->adc->CLPS) / 2) | 0x8000; - adcp->adc->PG = gain; - - gain = ((adcp->adc->CLM0 + adcp->adc->CLM1 + adcp->adc->CLM2 + - adcp->adc->CLM3 + adcp->adc->CLM4 + adcp->adc->CLMS) / 2) | 0x8000; - adcp->adc->MG = gain; - -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if KINETIS_ADC_USE_ADC0 || defined(__DOXYGEN__) -/** - * @brief ADC interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(KINETIS_ADC0_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - - ADCDriver *adcp = &ADCD1; - - /* Disable Interrupt, Disable Channel */ - adcp->adc->SC1A = ADCx_SC1n_ADCH(ADCx_SC1n_ADCH_DISABLED); - - /* Read the sample into the buffer */ - adcp->samples[adcp->current_index++] = adcp->adc->RA; - - bool more = true; - - /* At the end of the buffer then we may be finished */ - if (adcp->current_index == adcp->number_of_samples) { - /* We are never finished in circular mode */ - more = ADCD1.grpp->circular; - - _adc_isr_full_code(&ADCD1); - - adcp->current_index = 0; - - } - - if (more) { - - /* Signal half completion in circular mode. */ - if (ADCD1.grpp->circular && - (adcp->current_index == (adcp->number_of_samples / 2))) { - - _adc_isr_half_code(&ADCD1); - } - - /* Skip to the next channel */ - do { - adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; - } while (((1 << adcp->current_channel) & adcp->grpp->channel_mask) == 0); - - /* Enable Interrupt, Select the Channel */ - adcp->adc->SC1A = ADCx_SC1n_AIEN | ADCx_SC1n_ADCH(adcp->current_channel); - } - - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level ADC driver initialization. - * - * @notapi - */ -void adc_lld_init(void) { - -#if KINETIS_ADC_USE_ADC0 - /* Driver initialization.*/ - adcObjectInit(&ADCD1); -#endif - - /* The shared vector is initialized on driver initialization and never - disabled.*/ - nvicEnableVector(ADC0_IRQn, KINETIS_ADC_IRQ_PRIORITY); -} - -/** - * @brief Configures and activates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start(ADCDriver *adcp) { - - /* If in stopped state then enables the ADC clock.*/ - if (adcp->state == ADC_STOP) { - SIM->SCGC6 |= SIM_SCGC6_ADC0; - -#if KINETIS_ADC_USE_ADC0 - if (&ADCD1 == adcp) { - adcp->adc = ADC0; - if (adcp->config->calibrate) { - calibrate(adcp); - } - } -#endif /* KINETIS_ADC_USE_ADC0 */ - } -} - -/** - * @brief Deactivates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop(ADCDriver *adcp) { - - /* If in ready state then disables the ADC clock.*/ - if (adcp->state == ADC_READY) { - SIM->SCGC6 &= ~SIM_SCGC6_ADC0; - -#if KINETIS_ADC_USE_ADC0 - if (&ADCD1 == adcp) { - /* Disable Interrupt, Disable Channel */ - adcp->adc->SC1A = ADCx_SC1n_ADCH(ADCx_SC1n_ADCH_DISABLED); - } -#endif - } -} - -/** - * @brief Starts an ADC conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start_conversion(ADCDriver *adcp) { - const ADCConversionGroup *grpp = adcp->grpp; - - /* Enable the Bandgap Buffer if channel mask includes BANDGAP */ - if (grpp->channel_mask & ADC_BANDGAP) { - PMC->REGSC |= PMC_REGSC_BGBE; - } - - adcp->number_of_samples = adcp->depth * grpp->num_channels; - adcp->current_index = 0; - - /* Skip to the next channel */ - adcp->current_channel = 0; - while (((1 << adcp->current_channel) & grpp->channel_mask) == 0) { - adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; - } - - /* Set clock speed and conversion size */ - adcp->adc->CFG1 = grpp->cfg1; - - /* Set averaging */ - adcp->adc->SC3 = grpp->sc3; - - /* Enable Interrupt, Select Channel */ - adcp->adc->SC1A = ADCx_SC1n_AIEN | ADCx_SC1n_ADCH(adcp->current_channel); -} - -/** - * @brief Stops an ongoing conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop_conversion(ADCDriver *adcp) { - const ADCConversionGroup *grpp = adcp->grpp; - - /* Disable the Bandgap buffer if channel mask includes BANDGAP */ - if (grpp->channel_mask & ADC_BANDGAP) { - /* Clear BGBE, ACKISO is w1c, avoid setting */ - PMC->REGSC &= ~(PMC_REGSC_BGBE | PMC_REGSC_ACKISO); - } - -} - -#endif /* HAL_USE_ADC */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_adc_lld.h b/os/hal/ports/KINETIS/LLD/hal_adc_lld.h deleted file mode 100644 index c4edbd6..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_adc_lld.h +++ /dev/null @@ -1,360 +0,0 @@ -/* - 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/LLD/adc_lld.h - * @brief KINETIS ADC subsystem low level driver header. - * - * @addtogroup ADC - * @{ - */ - -#ifndef HAL_ADC_LLD_H_ -#define HAL_ADC_LLD_H_ - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name Absolute Maximum Ratings - * @{ - */ -/** - * @brief Minimum ADC clock frequency. - */ -#define KINETIS_ADCCLK_MIN 600000 - -/** - * @brief Maximum ADC clock frequency. - */ -#define KINETIS_ADCCLK_MAX 36000000 - -#define ADCx_SC3_AVGS_AVERAGE_4_SAMPLES 0 -#define ADCx_SC3_AVGS_AVERAGE_8_SAMPLES 1 -#define ADCx_SC3_AVGS_AVERAGE_16_SAMPLES 2 -#define ADCx_SC3_AVGS_AVERAGE_32_SAMPLES 3 - -#define ADCx_CFG1_ADIV_DIV_1 0 -#define ADCx_CFG1_ADIV_DIV_2 1 -#define ADCx_CFG1_ADIV_DIV_4 2 -#define ADCx_CFG1_ADIV_DIV_8 3 - -#define ADCx_CFG1_ADIVCLK_BUS_CLOCK 0 -#define ADCx_CFG1_ADIVCLK_BUS_CLOCK_DIV_2 1 -#define ADCx_CFG1_ADIVCLK_BUS_ALTCLK 2 -#define ADCx_CFG1_ADIVCLK_BUS_ADACK 3 - -#define ADCx_CFG1_MODE_8_OR_9_BITS 0 -#define ADCx_CFG1_MODE_12_OR_13_BITS 1 -#define ADCx_CFG1_MODE_10_OR_11_BITS 2 -#define ADCx_CFG1_MODE_16_BITS 3 - -#define ADCx_SC1n_ADCH_DAD0 0 -#define ADCx_SC1n_ADCH_DAD1 1 -#define ADCx_SC1n_ADCH_DAD2 2 -#define ADCx_SC1n_ADCH_DAD3 3 -#define ADCx_SC1n_ADCH_DADP0 0 -#define ADCx_SC1n_ADCH_DADP1 1 -#define ADCx_SC1n_ADCH_DADP2 2 -#define ADCx_SC1n_ADCH_DADP3 3 -#define ADCx_SC1n_ADCH_AD4 4 -#define ADCx_SC1n_ADCH_AD5 5 -#define ADCx_SC1n_ADCH_AD6 6 -#define ADCx_SC1n_ADCH_AD7 7 -#define ADCx_SC1n_ADCH_AD8 8 -#define ADCx_SC1n_ADCH_AD9 9 -#define ADCx_SC1n_ADCH_AD10 10 -#define ADCx_SC1n_ADCH_AD11 11 -#define ADCx_SC1n_ADCH_AD12 12 -#define ADCx_SC1n_ADCH_AD13 13 -#define ADCx_SC1n_ADCH_AD14 14 -#define ADCx_SC1n_ADCH_AD15 15 -#define ADCx_SC1n_ADCH_AD16 16 -#define ADCx_SC1n_ADCH_AD17 17 -#define ADCx_SC1n_ADCH_AD18 18 -#define ADCx_SC1n_ADCH_AD19 19 -#define ADCx_SC1n_ADCH_AD20 20 -#define ADCx_SC1n_ADCH_AD21 21 -#define ADCx_SC1n_ADCH_AD22 22 -#define ADCx_SC1n_ADCH_AD23 23 -#define ADCx_SC1n_ADCH_TEMP_SENSOR 26 -#define ADCx_SC1n_ADCH_BANDGAP 27 -#define ADCx_SC1n_ADCH_VREFSH 29 -#define ADCx_SC1n_ADCH_VREFSL 30 -#define ADCx_SC1n_ADCH_DISABLED 31 - -#define ADC_DAD0 (1 << ADCx_SC1n_ADCH_DAD0) -#define ADC_DAD1 (1 << ADCx_SC1n_ADCH_DAD1) -#define ADC_DAD2 (1 << ADCx_SC1n_ADCH_DAD2) -#define ADC_DAD3 (1 << ADCx_SC1n_ADCH_DAD3) -#define ADC_DADP0 (1 << ADCx_SC1n_ADCH_DADP0) -#define ADC_DADP1 (1 << ADCx_SC1n_ADCH_DADP1) -#define ADC_DADP2 (1 << ADCx_SC1n_ADCH_DADP2) -#define ADC_DADP3 (1 << ADCx_SC1n_ADCH_DADP3) -#define ADC_AD4 (1 << ADCx_SC1n_ADCH_AD4) -#define ADC_AD5 (1 << ADCx_SC1n_ADCH_AD5) -#define ADC_AD6 (1 << ADCx_SC1n_ADCH_AD6) -#define ADC_AD7 (1 << ADCx_SC1n_ADCH_AD7) -#define ADC_AD8 (1 << ADCx_SC1n_ADCH_AD8) -#define ADC_AD9 (1 << ADCx_SC1n_ADCH_AD9) -#define ADC_AD10 (1 << ADCx_SC1n_ADCH_AD10) -#define ADC_AD11 (1 << ADCx_SC1n_ADCH_AD11) -#define ADC_AD12 (1 << ADCx_SC1n_ADCH_AD12) -#define ADC_AD13 (1 << ADCx_SC1n_ADCH_AD13) -#define ADC_AD14 (1 << ADCx_SC1n_ADCH_AD14) -#define ADC_AD15 (1 << ADCx_SC1n_ADCH_AD15) -#define ADC_AD16 (1 << ADCx_SC1n_ADCH_AD16) -#define ADC_AD17 (1 << ADCx_SC1n_ADCH_AD17) -#define ADC_AD18 (1 << ADCx_SC1n_ADCH_AD18) -#define ADC_AD19 (1 << ADCx_SC1n_ADCH_AD19) -#define ADC_AD20 (1 << ADCx_SC1n_ADCH_AD20) -#define ADC_AD21 (1 << ADCx_SC1n_ADCH_AD21) -#define ADC_AD22 (1 << ADCx_SC1n_ADCH_AD22) -#define ADC_AD23 (1 << ADCx_SC1n_ADCH_AD23) -#define ADC_TEMP_SENSOR (1 << ADCx_SC1n_ADCH_TEMP_SENSOR) -#define ADC_BANDGAP (1 << ADCx_SC1n_ADCH_BANDGAP) -#define ADC_VREFSH (1 << ADCx_SC1n_ADCH_VREFSH) -#define ADC_VREFSL (1 << ADCx_SC1n_ADCH_VREFSL) -#define ADC_DISABLED (1 << ADCx_SC1n_ADCH_DISABLED) - -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ - -/** - * @brief ADC1 driver enable switch. - * @details If set to @p TRUE the support for ADC1 is included. - * @note The default is @p TRUE. - */ -#if !defined(KINETIS_ADC_USE_ADC0) || defined(__DOXYGEN__) -#define KINETIS_ADC_USE_ADC0 FALSE -#endif - -/** - * @brief ADC interrupt priority level setting. - */ -#if !defined(KINETIS_ADC_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_ADC_IRQ_PRIORITY 5 -#endif - -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if KINETIS_ADC_USE_ADC0 && !KINETIS_HAS_ADC0 -#error "ADC1 not present in the selected device" -#endif - -#if !KINETIS_ADC_USE_ADC0 -#error "ADC driver activated but no ADC peripheral assigned" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief ADC sample data type. - */ -typedef uint16_t adcsample_t; - -/** - * @brief Channels number in a conversion group. - */ -typedef uint16_t adc_channels_num_t; - -/** - * @brief Possible ADC failure causes. - * @note Error codes are architecture dependent and should not relied - * upon. - */ -typedef enum { - ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ - ADC_ERR_OVERFLOW = 1 /**< ADC overflow condition. */ -} adcerror_t; - -/** - * @brief Type of a structure representing an ADC driver. - */ -typedef struct ADCDriver ADCDriver; - -/** - * @brief ADC notification callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] buffer pointer to the most recent samples data - * @param[in] n number of buffer rows available starting from @p buffer - */ -typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); - -/** - * @brief ADC error callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] err ADC error code - */ -typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); - -/** - * @brief Conversion group configuration structure. - * @details This implementation-dependent structure describes a conversion - * operation. - */ -typedef struct { - /** - * @brief Enables the circular buffer mode for the group. - */ - bool circular; - /** - * @brief Number of the analog channels belonging to the conversion group. - */ - adc_channels_num_t num_channels; - /** - * @brief Callback function associated to the group or @p NULL. - */ - adccallback_t end_cb; - /** - * @brief Error callback or @p NULL. - */ - adcerrorcallback_t error_cb; - /* End of the mandatory fields.*/ - /** - * @brief Bitmask of channels for ADC conversion. - */ - uint32_t channel_mask; - /** - * @brief ADC CFG1 register initialization data. - * @note All the required bits must be defined into this field. - */ - uint32_t cfg1; - /** - * @brief ADC SC3 register initialization data. - * @note All the required bits must be defined into this field. - */ - uint32_t sc3; -} ADCConversionGroup; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /* Perform first time calibration */ - bool calibrate; -} ADCConfig; - -/** - * @brief Structure representing an ADC driver. - */ -struct ADCDriver { - /** - * @brief Driver state. - */ - adcstate_t state; - /** - * @brief Current configuration data. - */ - const ADCConfig *config; - /** - * @brief Current samples buffer pointer or @p NULL. - */ - adcsample_t *samples; - /** - * @brief Current samples buffer depth or @p 0. - */ - size_t depth; - /** - * @brief Current conversion group pointer or @p NULL. - */ - const ADCConversionGroup *grpp; -#if ADC_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif -#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* ADC_USE_MUTUAL_EXCLUSION */ -#if defined(ADC_DRIVER_EXT_FIELDS) - ADC_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the ADCx registers block. - */ - ADC_TypeDef *adc; - /** - * @brief Number of samples expected. - */ - size_t number_of_samples; - /** - * @brief Current position in the buffer. - */ - size_t current_index; - /** - * @brief Current channel index into group channel_mask. - */ - size_t current_channel; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if KINETIS_ADC_USE_ADC0 && !defined(__DOXYGEN__) -extern ADCDriver ADCD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void adc_lld_init(void); - void adc_lld_start(ADCDriver *adcp); - void adc_lld_stop(ADCDriver *adcp); - void adc_lld_start_conversion(ADCDriver *adcp); - void adc_lld_stop_conversion(ADCDriver *adcp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_ADC */ - -#endif /* HAL_ADC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_ext_lld.c b/os/hal/ports/KINETIS/LLD/hal_ext_lld.c deleted file mode 100644 index 21bb6e0..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_ext_lld.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - 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/LLD/ext_lld.c - * @brief KINETIS EXT subsystem low level driver source. - * - * @addtogroup EXT - * @{ - */ - -#include "hal.h" - -#if HAL_USE_EXT || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define PCR_IRQC_DISABLED 0x0 -#define PCR_IRQC_DMA_RISING_EDGE 0x1 -#define PCR_IRQC_DMA_FALLING_EDGE 0x2 -#define PCR_IRQC_DMA_EITHER_EDGE 0x3 - -#define PCR_IRQC_LOGIC_ZERO 0x8 -#define PCR_IRQC_RISING_EDGE 0x9 -#define PCR_IRQC_FALLING_EDGE 0xA -#define PCR_IRQC_EITHER_EDGE 0xB -#define PCR_IRQC_LOGIC_ONE 0xC - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief EXTD1 driver identifier. - */ -EXTDriver EXTD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/* A channel map for each channel. - * - * The index is the pin number. - * The result is the channel for that pin. - */ -#if KINETIS_EXT_PORTA_WIDTH > 0 -uint8_t porta_channel_map[KINETIS_EXT_PORTA_WIDTH]; -#endif -#if KINETIS_EXT_PORTB_WIDTH > 0 -uint8_t portb_channel_map[KINETIS_EXT_PORTB_WIDTH]; -#endif -#if KINETIS_EXT_PORTC_WIDTH > 0 -uint8_t portc_channel_map[KINETIS_EXT_PORTC_WIDTH]; -#endif -#if KINETIS_EXT_PORTD_WIDTH > 0 -uint8_t portd_channel_map[KINETIS_EXT_PORTD_WIDTH]; -#endif -#if KINETIS_EXT_PORTE_WIDTH > 0 -uint8_t porte_channel_map[KINETIS_EXT_PORTE_WIDTH]; -#endif - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Enables EXTI IRQ sources. - * - * @notapi - */ -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 -#if KINETIS_EXT_PORTC_WIDTH > 0 - nvicEnableVector(PINC_IRQn, KINETIS_EXT_PORTC_IRQ_PRIORITY); -#endif -#if KINETIS_EXT_PORTD_WIDTH > 0 - nvicEnableVector(PIND_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY); -#endif -#if KINETIS_EXT_PORTE_WIDTH > 0 - nvicEnableVector(PINE_IRQn, KINETIS_EXT_PORTE_IRQ_PRIORITY); -#endif -#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ -} - -/** - * @brief Disables EXTI IRQ sources. - * - * @notapi - */ -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 -#if KINETIS_EXT_PORTC_WIDTH > 0 - nvicDisableVector(PINC_IRQn); -#endif -#if KINETIS_EXT_PORTD_WIDTH > 0 - nvicDisableVector(PIND_IRQn); -#endif -#if KINETIS_EXT_PORTE_WIDTH > 0 - nvicDisableVector(PINE_IRQn); -#endif -#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/* - * Generic interrupt handler. - */ -static inline void irq_handler(PORT_TypeDef * const port, const unsigned port_width, const uint8_t *channel_map) { - unsigned pin; - uint32_t isfr = port->ISFR; - - /* Clear all pending interrupts on this port. */ - port->ISFR = 0xFFFFFFFF; - - for (pin = 0; pin < port_width; pin++) { - if (isfr & (1 << pin)) { - expchannel_t channel = channel_map[pin]; - EXTD1.config->channels[channel].cb(&EXTD1, channel); - } - } -} - -/** - * @brief PORTA interrupt handler. - * - * @isr - */ -#if defined(KINETIS_PORTA_IRQ_VECTOR) && KINETIS_EXT_PORTA_WIDTH > 0 -OSAL_IRQ_HANDLER(KINETIS_PORTA_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - - irq_handler(PORTA, KINETIS_EXT_PORTA_WIDTH, porta_channel_map); - - OSAL_IRQ_EPILOGUE(); -} -#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. - * - * @isr - */ -#if defined(KINETIS_PORTB_IRQ_VECTOR) && KINETIS_EXT_PORTB_WIDTH > 0 -OSAL_IRQ_HANDLER(KINETIS_PORTB_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - - irq_handler(PORTB, KINETIS_EXT_PORTB_WIDTH, portb_channel_map); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* KINETIS_EXT_PORTB_WIDTH > 0 */ - -/** - * @brief PORTC interrupt handler. - * - * @isr - */ -#if defined(KINETIS_PORTC_IRQ_VECTOR) && KINETIS_EXT_PORTC_WIDTH > 0 -OSAL_IRQ_HANDLER(KINETIS_PORTC_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - - irq_handler(PORTC, KINETIS_EXT_PORTC_WIDTH, portc_channel_map); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* KINETIS_EXT_PORTC_WIDTH > 0 */ - -/** - * @brief PORTD interrupt handler. - * - * @isr - */ -#if defined(KINETIS_PORTD_IRQ_VECTOR) && KINETIS_EXT_PORTD_WIDTH > 0 -OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - - irq_handler(PORTD, KINETIS_EXT_PORTD_WIDTH, portd_channel_map); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* KINETIS_EXT_PORTD_WIDTH > 0 */ - -/** - * @brief PORTE interrupt handler. - * - * @isr - */ -#if defined(KINETIS_PORTE_IRQ_VECTOR) && KINETIS_EXT_PORTE_WIDTH > 0 -OSAL_IRQ_HANDLER(KINETIS_PORTE_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - - irq_handler(PORTE, KINETIS_EXT_PORTE_WIDTH, porte_channel_map); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* KINETIS_EXT_PORTE_WIDTH > 0 */ - -#endif /* !KINETIS_EXT_HAS_COMMON_CD_IRQ */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level EXT driver initialization. - * - * @notapi - */ -void ext_lld_init(void) { - - /* Driver initialization.*/ - extObjectInit(&EXTD1); -} - -/** - * @brief Configures and activates the EXT peripheral. - * - * @param[in] extp pointer to the @p EXTDriver object - * - * @notapi - */ -void ext_lld_start(EXTDriver *extp) { - expchannel_t channel; - - if (extp->state == EXT_STOP) - ext_lld_exti_irq_enable(); - - /* Configuration of automatic channels.*/ - for (channel = 0; channel < EXT_MAX_CHANNELS; channel++) { - - uint32_t mode = extp->config->channels[channel].mode; - PORT_TypeDef *port = extp->config->channels[channel].port; - uint32_t pin = extp->config->channels[channel].pin; - - /* Initialize the channel map */ -#if KINETIS_EXT_PORTA_WIDTH > 0 - if (port == PORTA) - porta_channel_map[pin] = channel; - else -#endif -#if KINETIS_EXT_PORTB_WIDTH > 0 - if (port == PORTB) - portb_channel_map[pin] = channel; - else -#endif -#if KINETIS_EXT_PORTC_WIDTH > 0 - if (port == PORTC) - portc_channel_map[pin] = channel; - else -#endif -#if KINETIS_EXT_PORTD_WIDTH > 0 - if (port == PORTD) - portd_channel_map[pin] = channel; - else -#endif -#if KINETIS_EXT_PORTE_WIDTH > 0 - if (port == PORTE) - porte_channel_map[pin] = channel; - else -#endif - {} - - if (mode & EXT_CH_MODE_AUTOSTART) - ext_lld_channel_enable(extp, channel); - else if (port != NULL) - ext_lld_channel_disable(extp, channel); - } -} - -/** - * @brief Deactivates the EXT peripheral. - * - * @param[in] extp pointer to the @p EXTDriver object - * - * @notapi - */ -void ext_lld_stop(EXTDriver *extp) { - - if (extp->state == EXT_ACTIVE) - ext_lld_exti_irq_disable(); -} - -/** - * @brief Enables an EXT channel. - * - * @param[in] extp pointer to the @p EXTDriver object - * @param[in] channel channel to be enabled - * - * @notapi - */ -void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { - - uint32_t irqc; - uint32_t mode = extp->config->channels[channel].mode; - if (mode & EXT_CH_MODE_RISING_EDGE) - irqc = PCR_IRQC_RISING_EDGE; - else if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE) - irqc = PCR_IRQC_FALLING_EDGE; - else if (extp->config->channels[channel].mode & EXT_CH_MODE_BOTH_EDGES) - irqc = PCR_IRQC_EITHER_EDGE; - else - irqc = PCR_IRQC_DISABLED; - - PORT_TypeDef *port = extp->config->channels[channel].port; - uint32_t pin = extp->config->channels[channel].pin; - - uint32_t pcr = port->PCR[pin]; - - /* Clear all the IRQC bits */ - pcr &= ~PORTx_PCRn_IRQC_MASK; - /* Set the required IRQC bits */ - pcr |= PORTx_PCRn_IRQC(irqc); - - port->PCR[pin] = pcr; -} - -/** - * @brief Disables an EXT channel. - * - * @param[in] extp pointer to the @p EXTDriver object - * @param[in] channel channel to be disabled - * - * @notapi - */ -void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) { - - PORT_TypeDef *port = extp->config->channels[channel].port; - uint32_t pin = extp->config->channels[channel].pin; - port->PCR[pin] |= PORTx_PCRn_IRQC(PCR_IRQC_DISABLED); -} - -#endif /* HAL_USE_EXT */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_ext_lld.h b/os/hal/ports/KINETIS/LLD/hal_ext_lld.h deleted file mode 100644 index bcd9cb0..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_ext_lld.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - 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/LLD/ext_lld.h - * @brief KINETIS EXT subsystem low level driver header. - * - * @addtogroup EXT - * @{ - */ - -#ifndef HAL_EXT_LLD_H_ -#define HAL_EXT_LLD_H_ - -#if HAL_USE_EXT || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Number of EXT channels required. - */ -#define EXT_MAX_CHANNELS KINETIS_EXTI_NUM_CHANNELS - -/** - * @name KINETIS-specific EXT channel modes - * @{ - */ -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief PORTA interrupt priority level setting. - */ -#if !defined(KINETIS_EXT_PORTA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_EXT_PORTA_IRQ_PRIORITY 3 -#endif - -/** - * @brief PORTB interrupt priority level setting. - */ -#if !defined(KINETIS_EXT_PORTB_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_EXT_PORTB_IRQ_PRIORITY 3 -#endif - -/** - * @brief PORTC interrupt priority level setting. - */ -#if !defined(KINETIS_EXT_PORTC_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_EXT_PORTC_IRQ_PRIORITY 3 -#endif - -/** - * @brief PORTD interrupt priority level setting. - */ -#if !defined(KINETIS_EXT_PORTD_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_EXT_PORTD_IRQ_PRIORITY 3 -#endif - -/** - * @brief PORTE interrupt priority level setting. - */ -#if !defined(KINETIS_EXT_PORTE_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_EXT_PORTE_IRQ_PRIORITY 3 -#endif -/** @} */ -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief EXT channel identifier. - */ -typedef uint32_t expchannel_t; - -/** - * @brief Type of an EXT generic notification callback. - * - * @param[in] extp pointer to the @p EXPDriver object triggering the - * callback - */ -typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel); - -/** - * @brief Channel configuration structure. - */ -typedef struct { - /** - * @brief Channel mode. - */ - uint32_t mode; - /** - * @brief Channel callback. - */ - extcallback_t cb; - - /** - * @brief Port. - */ - PORT_TypeDef *port; - - /** - * @brief Pin. - */ - uint32_t pin; -} EXTChannelConfig; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Channel configurations. - */ - EXTChannelConfig channels[EXT_MAX_CHANNELS]; - /* End of the mandatory fields.*/ -} EXTConfig; - -/** - * @brief Structure representing an EXT driver. - */ -struct EXTDriver { - /** - * @brief Driver state. - */ - extstate_t state; - /** - * @brief Current configuration data. - */ - const EXTConfig *config; - /* End of the mandatory fields.*/ -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -extern EXTDriver EXTD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void ext_lld_init(void); - void ext_lld_start(EXTDriver *extp); - void ext_lld_stop(EXTDriver *extp); - void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel); - void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_EXT */ - -#endif /* HAL_EXT_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_gpt_lld.c b/os/hal/ports/KINETIS/LLD/hal_gpt_lld.c deleted file mode 100644 index 6e88f88..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_gpt_lld.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - 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/hal_gpt_lld.h b/os/hal/ports/KINETIS/LLD/hal_gpt_lld.h deleted file mode 100644 index 1b9e5ef..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_gpt_lld.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - 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 HAL_GPT_LLD_H_ -#define HAL_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 /* HAL_GPT_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c deleted file mode 100644 index f615dd5..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c +++ /dev/null @@ -1,589 +0,0 @@ -/* - 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/i2c_lld.c - * @brief KINETIS I2C subsystem low level driver source. - * - * @addtogroup I2C - * @{ - */ - -#include "osal.h" -#include "hal.h" - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief I2C0 driver identifier. - */ -#if KINETIS_I2C_USE_I2C0 || defined(__DOXYGEN__) -I2CDriver I2CD1; -#endif - -/** - * @brief I2C1 driver identifier. - */ -#if KINETIS_I2C_USE_I2C1 || defined(__DOXYGEN__) -I2CDriver I2CD2; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -void config_frequency(I2CDriver *i2cp) { - - /* Each index in the table corresponds to a a frequency - * divider used to generate the SCL clock from the main - * system clock. - */ - const uint16_t icr_table[] = { - /* 0x00 - 0x0F */ - 20,22,24,26,28,30,34,40,28,32,36,40,44,48,56,68, - /* 0x10 - 0x1F */ - 48,56,64,72,80,88,104,128,80,96,112,128,144,160,192,240, - /* 0x20 - 0x2F */ - 160,192,224,256,288,320,384,480,320,384,448,512,576,640,768,960, - /* 0x30 - 0x3F */ - 640,768,896,1024,1152,1280,1536,1920,1280,1536,1792,2048,2304,2560,3072,3840, - }; - - int length = sizeof(icr_table) / sizeof(icr_table[0]); - uint16_t divisor; - uint8_t i = 0, index = 0; - uint16_t best, diff; - - if (i2cp->config != NULL) - divisor = KINETIS_BUSCLK_FREQUENCY / i2cp->config->clock; - else - divisor = KINETIS_BUSCLK_FREQUENCY / 100000; - - best = ~0; - index = 0; - /* Tries to find the SCL clock which is the closest - * approximation to the clock passed in config. To - * stay on the safe side, only values that generate - * lower frequency are used. - */ - for (i = 0; i < length; i++) { - if (icr_table[i] >= divisor) { - diff = icr_table[i] - divisor; - if (diff < best) { - best = diff; - index = i; - } - } - } - - i2cp->i2c->F = index; -} - -/** - * @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] i2cp pointer to an I2CDriver - */ -static void serve_interrupt(I2CDriver *i2cp) { - - I2C_TypeDef *i2c = i2cp->i2c; - intstate_t state = i2cp->intstate; - - /* check if we're master or slave */ - if (i2c->C1 & I2Cx_C1_MST) { - /* master */ - - if (i2c->S & I2Cx_S_ARBL) { - /* check if we lost arbitration */ - i2cp->errors |= I2C_ARBITRATION_LOST; - i2c->S |= I2Cx_S_ARBL; - /* TODO: may need to do more here, reset bus? */ - /* Perhaps clear MST? */ - } - -#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ - else if ((i2cp->rsta_workaround == RSTA_WORKAROUND_ON) && (i2cp->i2c->FLT & I2Cx_FLT_STARTF)) { - i2cp->rsta_workaround = RSTA_WORKAROUND_OFF; - /* clear+disable STARTF/STOPF interrupts and wake up the thread */ - i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; - i2cp->i2c->FLT &= ~I2Cx_FLT_SSIE; - i2c->S |= I2Cx_S_IICIF; - _i2c_wakeup_isr(i2cp); - } -#endif /* KL27Z RST workaround */ - - else if (i2c->S & I2Cx_S_TCF) { - /* just completed byte transfer */ - if (i2c->C1 & I2Cx_C1_TX) { - /* the byte was transmitted */ - - if (state == STATE_SEND) { - /* currently sending stuff */ - - if (i2c->S & I2Cx_S_RXAK) { - /* slave did not ACK */ - i2cp->errors |= I2C_ACK_FAILURE; - /* the thread will be woken up at the end of ISR and release the bus */ - - } else if (i2cp->txbuf != NULL && i2cp->txidx < i2cp->txbytes) { - /* slave ACK'd and we want to send more */ - i2c->D = i2cp->txbuf[i2cp->txidx++]; - } else { - /* slave ACK'd and we are done sending */ - i2cp->intstate = STATE_STOP; - /* this wakes up the waiting thread at the end of ISR */ - } - - } else if (state == STATE_RECV) { - /* should be receiving stuff, so we've just sent the address */ - - if (i2c->S & I2Cx_S_RXAK) { - /* slave did not ACK */ - i2cp->errors |= I2C_ACK_FAILURE; - /* the thread will be woken up and release the bus */ - - } else { - /* slave ACK'd, we should be receiving next */ - i2c->C1 &= ~I2Cx_C1_TX; - - if (i2cp->rxbytes > 1) { - /* multi-byte read, send ACK after next transfer */ - i2c->C1 &= ~I2Cx_C1_TXAK; - } else { - /* only 1 byte remaining, send NAK */ - i2c->C1 |= I2Cx_C1_TXAK; - } - - (void) i2c->D; /* dummy read; triggers next receive */ - } - - } /* possibly check other states here - should not happen! */ - - } else { - /* the byte was received */ - - if (state == STATE_RECV) { - /* currently receiving stuff */ - /* the received byte is now in D */ - - if (i2cp->rxbytes > 1) { - /* expecting at least one byte after this one */ - if (i2cp->rxidx == (i2cp->rxbytes - 2)) { - /* expecting exactly one byte after this one, NAK that one */ - i2c->C1 |= I2Cx_C1_TXAK; - } else { - /* expecting more than one after this one, respond with ACK */ - i2c->C1 &= ~I2Cx_C1_TXAK; - } - } - - if (i2cp->rxidx == i2cp->rxbytes - 1) { - /* D is the last byte we're expecting */ - /* release bus: switch to RX mode, send STOP */ - /* need to do it now otherwise the I2C module will wait for another byte */ - // delayMicroseconds(1); - i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); - i2cp->intstate = STATE_STOP; - /* this wakes up the waiting thread at the end of ISR */ - } - - /* get the data from D; this triggers the next receive */ - i2cp->rxbuf[i2cp->rxidx++] = i2c->D; - - // if (i2cp->rxidx == i2cp->rxbytes) { - /* done receiving */ - // } - } /* possibly check other states here - should not happen! */ - } - - } /* possibly check other interrupt flags here */ - } else { - /* slave */ - - /* Not implemented yet */ - } - - /* Reset other interrupt sources */ -#if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */ - i2cp->i2c->FLT |= I2Cx_FLT_STOPF; -#endif -#if defined(I2Cx_FLT_STARTF) /* extra flags on KL27Z */ - i2cp->i2c->FLT |= I2Cx_FLT_STARTF; -#endif - /* Reset interrupt flag */ - i2c->S |= I2Cx_S_IICIF; - - if (i2cp->errors != I2C_NO_ERROR) - _i2c_wakeup_error_isr(i2cp); - - if (i2cp->intstate == STATE_STOP) - _i2c_wakeup_isr(i2cp); -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if KINETIS_I2C_USE_I2C0 || defined(__DOXYGEN__) - -OSAL_IRQ_HANDLER(KINETIS_I2C0_IRQ_VECTOR) { - - OSAL_IRQ_PROLOGUE(); - serve_interrupt(&I2CD1); - OSAL_IRQ_EPILOGUE(); -} - -#endif - -#if KINETIS_I2C_USE_I2C1 || defined(__DOXYGEN__) - -OSAL_IRQ_HANDLER(KINETIS_I2C1_IRQ_VECTOR) { - - OSAL_IRQ_PROLOGUE(); - serve_interrupt(&I2CD2); - OSAL_IRQ_EPILOGUE(); -} - -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level I2C driver initialization. - * - * @notapi - */ -void i2c_lld_init(void) { - -#if KINETIS_I2C_USE_I2C0 - i2cObjectInit(&I2CD1); - I2CD1.thread = NULL; - I2CD1.i2c = I2C0; -#endif - -#if KINETIS_I2C_USE_I2C1 - i2cObjectInit(&I2CD2); - I2CD2.thread = NULL; - I2CD2.i2c = I2C1; -#endif - -} - -/** - * @brief Configures and activates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_start(I2CDriver *i2cp) { - - if (i2cp->state == I2C_STOP) { - - /* TODO: - * The PORT must be enabled somewhere. The PIN multiplexer - * will map the I2C functionality to some PORT which must - * than be enabled. The easier way is enabling all PORTs at - * startup, which is currently being done in __early_init. - */ - -#if KINETIS_I2C_USE_I2C0 - if (&I2CD1 == i2cp) { - SIM->SCGC4 |= SIM_SCGC4_I2C0; - nvicEnableVector(I2C0_IRQn, KINETIS_I2C_I2C0_PRIORITY); - } -#endif - -#if KINETIS_I2C_USE_I2C1 - if (&I2CD2 == i2cp) { - SIM->SCGC4 |= SIM_SCGC4_I2C1; - nvicEnableVector(I2C1_IRQn, KINETIS_I2C_I2C1_PRIORITY); - } -#endif - - } - - config_frequency(i2cp); - i2cp->i2c->C1 = I2Cx_C1_IICEN | I2Cx_C1_IICIE; // reset I2C, enable interrupts - i2cp->i2c->S = I2Cx_S_IICIF | I2Cx_S_ARBL; // clear status flags just in case - i2cp->intstate = STATE_STOP; // internal state -} - -/** - * @brief Deactivates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_stop(I2CDriver *i2cp) { - - if (i2cp->state != I2C_STOP) { - - i2cp->i2c->C1 &= ~(I2Cx_C1_IICEN | I2Cx_C1_IICIE); - -#if KINETIS_I2C_USE_I2C0 - if (&I2CD1 == i2cp) { - SIM->SCGC4 &= ~SIM_SCGC4_I2C0; - nvicDisableVector(I2C0_IRQn); - } -#endif - -#if KINETIS_I2C_USE_I2C1 - if (&I2CD2 == i2cp) { - SIM->SCGC4 &= ~SIM_SCGC4_I2C1; - nvicDisableVector(I2C1_IRQn); - } -#endif - - } -} - -static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - - msg_t msg; - systime_t start, end; - - uint8_t op = (i2cp->intstate == STATE_SEND) ? 0 : 1; - - i2cp->errors = I2C_NO_ERROR; - i2cp->addr = addr; - - i2cp->txbuf = txbuf; - i2cp->txbytes = txbytes; - i2cp->txidx = 0; - - i2cp->rxbuf = rxbuf; - i2cp->rxbytes = rxbytes; - i2cp->rxidx = 0; - -#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ - i2cp->rsta_workaround = RSTA_WORKAROUND_OFF; -#endif /* KL27Z RST workaround */ - - /* clear status flags */ -#if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */ - i2cp->i2c->FLT |= I2Cx_FLT_STOPF; -#endif -#if defined(I2Cx_FLT_STARTF) /* extra flags on KL27Z */ - i2cp->i2c->FLT |= I2Cx_FLT_STARTF; -#endif - i2cp->i2c->S = I2Cx_S_IICIF|I2Cx_S_ARBL; - - /* acquire the bus */ - /* check to see if we already have the bus */ - if(i2cp->i2c->C1 & I2Cx_C1_MST) { - -#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ - /* need to wait for STARTF interrupt after issuing repeated start, - * otherwise the double buffering mechanism sends the last sent byte - * instead of the slave address. - * https://community.freescale.com/thread/377611 - */ - i2cp->rsta_workaround = RSTA_WORKAROUND_ON; - /* clear any interrupt bits and enable STARTF/STOPF interrupts */ - i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; - i2cp->i2c->S |= I2Cx_S_IICIF|I2Cx_S_ARBL; - i2cp->i2c->FLT |= I2Cx_FLT_SSIE; -#endif /* KL27Z RST workaround */ - - /* send repeated start */ - i2cp->i2c->C1 |= I2Cx_C1_RSTA | I2Cx_C1_TX; - -#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ - /* wait for the STARTF interrupt */ - msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); - /* abort if this didn't go well (timed out) */ - if (msg != MSG_OK) { - /* release bus - RX mode, send STOP */ - i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); - return msg; - } -#endif /* KL27Z RST workaround */ - - } else { - /* unlock during the wait, so that tasks with - * higher priority can get attention */ - osalSysUnlock(); - - /* wait until the bus is released */ - /* Calculating the time window for the timeout on the busy bus condition.*/ - start = osalOsGetSystemTimeX(); -#if defined(OSAL_TIME_MS2I) - end = start + OSAL_TIME_MS2I(KINETIS_I2C_BUSY_TIMEOUT); -#elif defined(OSAL_TIME_MS2ST) - end = start + OSAL_TIME_MS2ST(KINETIS_I2C_BUSY_TIMEOUT); -#else - end = start + OSAL_MS2ST(KINETIS_I2C_BUSY_TIMEOUT); -#endif - - while(true) { - osalSysLock(); - /* If the bus is not busy then the operation can continue, note, the - loop is exited in the locked state.*/ - if(!(i2cp->i2c->S & I2Cx_S_BUSY)) - break; - /* If the system time went outside the allowed window then a timeout - condition is returned.*/ - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) { - return MSG_TIMEOUT; - } - osalSysUnlock(); - } - - /* send START */ - i2cp->i2c->C1 |= I2Cx_C1_MST|I2Cx_C1_TX; - } - - /* send slave address */ - i2cp->i2c->D = addr << 1 | op; - - /* wait for the ISR to signal that the transmission (or receive if no transmission) phase is complete */ - msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); - - /* FIXME */ - //if (i2cp->i2c->S & I2Cx_S_RXAK) - // i2cp->errors |= I2C_ACK_FAILURE; - - /* the transmitting (or receiving if no transmission) phase has finished, - * do we expect to receive something? */ - if (msg == MSG_OK && rxbuf != NULL && rxbytes > 0 && i2cp->rxidx < rxbytes) { - -#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ - /* the same KL27Z RST workaround as above */ - i2cp->rsta_workaround = RSTA_WORKAROUND_ON; - /* clear any interrupt bits and enable STARTF/STOPF interrupts */ - i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; - i2cp->i2c->S |= I2Cx_S_IICIF|I2Cx_S_ARBL; - i2cp->i2c->FLT |= I2Cx_FLT_SSIE; -#endif /* KL27Z RST workaround */ - - /* send repeated start */ - i2cp->i2c->C1 |= I2Cx_C1_RSTA; - -#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ - /* wait for the STARTF interrupt */ - msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); - /* abort if this didn't go well (timed out) */ - if (msg != MSG_OK) { - /* release bus - RX mode, send STOP */ - i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); - return msg; - } -#endif /* KL27Z RST workaround */ - - /* FIXME */ - // while (!(i2cp->i2c->S & I2Cx_S_BUSY)); - - i2cp->intstate = STATE_RECV; - i2cp->i2c->D = i2cp->addr << 1 | 1; - - msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); - } - - /* release bus - RX mode, send STOP */ - // other kinetis I2C drivers wait here for 1us. is this needed? - i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); - /* FIXME */ - // while (i2cp->i2c->S & I2Cx_S_BUSY); - - return msg; -} - -/** - * @brief Receives data via the I2C bus as master. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @notapi - */ -msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - - i2cp->intstate = STATE_RECV; - return _i2c_txrx_timeout(i2cp, addr, NULL, 0, rxbuf, rxbytes, timeout); -} - -/** - * @brief Transmits data via the I2C bus as master. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[in] txbuf pointer to the transmit buffer - * @param[in] txbytes number of bytes to be transmitted - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @notapi - */ -msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - - i2cp->intstate = STATE_SEND; - return _i2c_txrx_timeout(i2cp, addr, txbuf, txbytes, rxbuf, rxbytes, timeout); -} - -#endif /* HAL_USE_I2C */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h deleted file mode 100644 index bfc5008..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - 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/i2c_lld.h - * @brief KINETIS I2C subsystem low level driver header. - * - * @addtogroup I2C - * @{ - */ - -#ifndef HAL_I2C_LLD_H_ -#define HAL_I2C_LLD_H_ - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -#define STATE_STOP 0x00 -#define STATE_SEND 0x01 -#define STATE_RECV 0x02 - -#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ -#define RSTA_WORKAROUND_OFF 0x00 -#define RSTA_WORKAROUND_ON 0x01 -#endif /* KL27Z RST workaround */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief I2C0 driver enable switch. - * @details If set to @p TRUE the support for I2C0 is included. - * @note The default is @p FALSE. - */ -#if !defined(KINETIS_I2C_USE_I2C0) || defined(__DOXYGEN__) -#define KINETIS_I2C_USE_I2C0 FALSE -#endif - -/** - * @brief I2C1 driver enable switch. - * @details If set to @p TRUE the support for I2C1 is included. - * @note The default is @p FALSE. - */ -#if !defined(KINETIS_I2C_USE_I2C1) || defined(__DOXYGEN__) -#define KINETIS_I2C_USE_I2C1 FALSE -#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 - -/** - * @brief Timeout for external clearing BUSY bus (in ms). - */ -#if !defined(KINETIS_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) -#define KINETIS_I2C_BUSY_TIMEOUT 50 -#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. */ -/*===========================================================================*/ - -/* @brief Type representing I2C address. */ -typedef uint8_t i2caddr_t; - -/* @brief Type of I2C Driver condition flags. */ -typedef uint32_t i2cflags_t; - -/* @brief Type used to control the ISR state machine. */ -typedef uint8_t intstate_t; - -/** - * @brief Driver configuration structure. - * @note Implementations may extend this structure to contain more, - * architecture dependent, fields. - */ - -/** - * @brief Driver configuration structure. - */ -typedef struct { - - /* @brief Clock to be used for the I2C bus. */ - uint32_t clock; - -} I2CConfig; - -/** - * @brief Type of a structure representing an I2C driver. - */ -typedef struct I2CDriver I2CDriver; - -/** - * @brief Structure representing an I2C driver. - */ -struct I2CDriver { - /** - * @brief Driver state. - */ - i2cstate_t state; - /** - * @brief Current configuration data. - */ - const I2CConfig *config; - /** - * @brief Error flags. - */ - i2cflags_t errors; -#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the bus. - */ - mutex_t mutex; -#endif /* I2C_USE_MUTUAL_EXCLUSION */ -#if defined(I2C_DRIVER_EXT_FIELDS) - I2C_DRIVER_EXT_FIELDS -#endif - /* @brief Thread waiting for I/O completion. */ - thread_reference_t thread; - /* @brief Current slave address without R/W bit. */ - i2caddr_t addr; - - /* End of the mandatory fields.*/ - - /* @brief Pointer to the buffer with data to send. */ - const uint8_t *txbuf; - /* @brief Number of bytes of data to send. */ - size_t txbytes; - /* @brief Current index in buffer when sending data. */ - size_t txidx; - /* @brief Pointer to the buffer to put received data. */ - uint8_t *rxbuf; - /* @brief Number of bytes of data to receive. */ - size_t rxbytes; - /* @brief Current index in buffer when receiving data. */ - size_t rxidx; - /* @brief Tracks current ISR state. */ - intstate_t intstate; - /* @brief Low-level register access. */ - I2C_TypeDef *i2c; -#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ - /* @brief Auxiliary variable for KL27Z repeated start workaround. */ - intstate_t rsta_workaround; -#endif /* KL27Z RST workaround */ -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Get errors from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) - -#if KINETIS_I2C_USE_I2C0 -extern I2CDriver I2CD1; -#endif - -#if KINETIS_I2C_USE_I2C1 -extern I2CDriver I2CD2; -#endif - -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void i2c_lld_init(void); - void i2c_lld_start(I2CDriver *i2cp); - void i2c_lld_stop(I2CDriver *i2cp); - msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2C */ - -#endif /* HAL_I2C_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_pal_lld.c b/os/hal/ports/KINETIS/LLD/hal_pal_lld.c deleted file mode 100644 index 51f8a2e..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_pal_lld.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - 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; - - osalDbgAssert(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; - - osalDbgAssert(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/hal_pal_lld.h b/os/hal/ports/KINETIS/LLD/hal_pal_lld.h deleted file mode 100644 index 833d95e..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_pal_lld.h +++ /dev/null @@ -1,423 +0,0 @@ -/* - 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 HAL_PAL_LLD_H_ -#define HAL_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 Type of an I/O line. - */ -typedef uint32_t ioline_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 - -/** - * @name Line handling macros - * @{ - */ -/** - * @brief Forms a line identifier. - * @details A port/pad pair are encoded into an @p ioline_t type. The encoding - * of this type is platform-dependent. - * @note In this driver the pad number is encoded in the byte of the GPIO - * address that's zero on all Kinetis devices. - */ -#define PAL_LINE(port, pad) \ - ((ioline_t)((uint32_t)(port) | ((uint32_t)(pad)<<20))) - -/** - * @brief Decodes a port identifier from a line identifier. - */ -#define PAL_PORT(line) \ - ((GPIO_TypeDef *)(((uint32_t)(line)) & 0xF00FFFFFU)) - -/** - * @brief Decodes a pad identifier from a line identifier. - */ -#define PAL_PAD(line) \ - ((uint32_t)((uint32_t)(line) & 0x0FF00000U)>>20) - -/** - * @brief Value identifying an invalid line. - */ -#define PAL_NOLINE 0U -/** @} */ - -/*===========================================================================*/ -/* 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 /* HAL_PAL_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_sdc_lld.c b/os/hal/ports/KINETIS/LLD/hal_sdc_lld.c deleted file mode 100644 index 6ba932e..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_sdc_lld.c +++ /dev/null @@ -1,993 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - Copyright (C) 2017..2018 Wim Lewis - - 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 hal_sdc_lld.c - * @brief Kinetis SDC subsystem low level driver. - * - * This driver provides a single SDC driver based on the Kinetis - * "Secured Digital Host Controller (SDHC)" peripheral. - * - * In order to use this driver, other peripherals must also be configured: - * - * The MPU must either be disabled (CESR=0), or it must be configured - * to allow the SDHC peripheral DMA access to any data buffers (read - * or write). - * - * The SDHC signals must be routed to the desired pins, and pullups/pulldowns - * configured. - * - * @addtogroup SDC - * @{ - */ - -#include "hal.h" - -#if (HAL_USE_SDC == TRUE) || defined(__DOXYGEN__) - -#include "hal_mmcsd.h" - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#if defined(MK66F18) -/* Configure SDHC block to use the IRC48M clock */ -#define KINETIS_SDHC_PERIPHERAL_FREQUENCY 48000000UL -#else -/* We configure the SDHC block to use the system clock */ -#define KINETIS_SDHC_PERIPHERAL_FREQUENCY KINETIS_SYSCLK_FREQUENCY -#endif - -#ifndef KINETIS_SDHC_PRIORITY -#define KINETIS_SDHC_PRIORITY 12 /* TODO? Default IRQ priority for SDHC */ -#endif - -/* The DTOC value (data timeout counter) controls how long the SDHC - will wait for a data transfer before indicating a timeout to - us. The card can tell us how long that should be, but various SDHC - documentation suggests that we should always allow around 500 msec - even if the card says it will finish sooner. This only comes into - play if there's a malfunction or something, so it's not critical to - get it exactly right. - - It controls the ratio between the SDCLK frequency and the - timeout, so we have a different DTOCV for each bus clock - frequency. -*/ -#define DTOCV_300ms_400kHz 4 /* 4 -> 2^17 -> 328 msec */ -#define DTOCV_700ms_25MHz 11 /* 11 -> 2^24 -> 671 msec */ -#define DTOCV_700ms_50MHz 12 /* 12 -> 2^25 -> 671 msec */ - -#if 0 -#define TRACE(t, val) chDbgWriteTrace ((void *)t, (void *)(uintptr_t)(val)) -#define TRACEI(t, val) chDbgWriteTraceI((void *)t, (void *)(uintptr_t)(val)) -#else -#define TRACE(t, val) -#define TRACEI(t, val) -#endif - -#define DIV_RND_UP(a, b) ( ((a)+(b)-1) / (b) ) - -/* Error bits from the SD / MMC Card Status response word. */ -/* TODO: These really belong in a HLD, not here. */ -#define MMC_ERR_OUT_OF_RANGE (1U << 31) -#define MMC_ERR_ADDRESS (1U << 30) -#define MMC_ERR_BLOCK_LEN (1U << 29) -#define MMC_ERR_ERASE_SEQ (1U << 28) -#define MMC_ERR_ERASE_PARAM (1U << 27) -#define MMC_ERR_WP (1U << 26) -#define MMC_ERR_CARD_IS_LOCKED (1U << 25) -#define MMC_ERR_LOCK_UNLOCK_FAILED (1U << 24) -#define MMC_ERR_COM_CRC_ERROR (1U << 23) -#define MMC_ERR_ILLEGAL_COMMAND (1U << 22) -#define MMC_ERR_CARD_ECC_FAILED (1U << 21) -#define MMC_ERR_CARD_CONTROLLER (1U << 20) -#define MMC_ERR_ERROR (1U << 19) -#define MMC_ERR_CSD_OVERWRITE (1U << 16) -#define MMC_ERR_AKE_SEQ (1U << 3) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief SDCD1 driver identifier. - */ -#if (PLATFORM_SDC_USE_SDC1 == TRUE) || defined(__DOXYGEN__) -SDCDriver SDCD1; -#else -#error HAL_USE_SDC is true but PLATFORM_SDC_USE_SDC1 is false -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static void recover_after_botched_transfer(SDCDriver *); -static msg_t wait_interrupt(SDCDriver *, uint32_t); -static bool sdc_lld_transfer(SDCDriver *, uint32_t, uintptr_t, uint32_t, uint32_t); - -/** - * Compute the SDCLKFS and DVS values for a given SDCLK divisor. - * - * Note that in the current code, this function is always called with - * a constant argument (there are only a handful of valid SDCLK - * frequencies), and so GCC computes the results at compile time and - * does not actually emit this function into the output at all unless - * you're compiling with optimizations turned off. - * - * However if someone compiles with a KINETIS_SDHC_PERIPHERAL_FREQUENCY - * that is not a compile-time constant, this function would get emitted. - */ -static uint32_t divisor_settings(unsigned divisor) -{ - /* First, handle all the special cases */ - if (divisor <= 1) { - /* Pass through */ - return SDHC_SYSCTL_SDCLKFS(0) | SDHC_SYSCTL_DVS(0); - } - if (divisor <= 16 && (divisor & 0x01)) { - /* Disable the prescaler, just use the divider. */ - return SDHC_SYSCTL_SDCLKFS(0) | SDHC_SYSCTL_DVS(divisor - 1); - } - if (divisor <= 32 && !(divisor & 0x01)) { - /* Prescale by 2, but do the rest with the divider */ - return SDHC_SYSCTL_SDCLKFS(0x01) | SDHC_SYSCTL_DVS((divisor >> 1) - 1); - } - if (divisor >= 0x1000) { - /* It's not possible to divide by more than 2^12. If we're asked to, - just do the best we can. */ - return SDHC_SYSCTL_SDCLKFS(0x80) | SDHC_SYSCTL_DVS(0xF); - } - - /* The bit position in SDCLKFS provides a power-of-two prescale - factor, and the four bits in DVS allow division by up to 16 - (division by DVS+1). We want to approximate `divisor` using these - terms, but we want to round up --- it's OK to run the card a - little bit too slow, but not OK to run it a little bit too - fast. */ - - unsigned shift = (8 * sizeof(unsigned int) - 4) - __builtin_clz(divisor); - - /* Shift the divisor value right so that it only occupies the four - lowest bits. Subtract one because that's how the DVS circuit - works. Add one if we shifted any 1-bits off the bottom, so that - we always round up. */ - unsigned dvs = (divisor >> shift) - ((divisor & ((1 << shift)-1))? 0 : 1); - - return SDHC_SYSCTL_SDCLKFS(1 << (shift-1)) | SDHC_SYSCTL_DVS(dvs); -} - -/** - * @brief Enable the SDHC clock when stable. - * - * Waits for the clock divider in the SDHC block to stabilize, then - * enables the SD clock. - */ -static void enable_clock_when_stable(uint32_t new_sysctl) -{ - SDHC->SYSCTL = new_sysctl; - - /* Wait for clock divider to stabilize */ - while(!(SDHC->PRSSTAT & SDHC_PRSSTAT_SDSTB)) { - osalThreadSleepMilliseconds(1); - } - - /* Restart the clock */ - SDHC->SYSCTL = new_sysctl | SDHC_SYSCTL_SDCLKEN; - - /* Wait for clock to stabilize again */ - while(!(SDHC->PRSSTAT & SDHC_PRSSTAT_SDSTB)) { - osalThreadSleepMilliseconds(1); - } -} - -/** - * Translate error bits from a CMD transaction to the HAL's error flag set. - */ -static sdcflags_t translate_cmd_error(uint32_t status) { - /* Translate the failure into the flags understood by the top half */ - - sdcflags_t errors = 0; - - if (status & SDHC_IRQSTAT_CTOE || !(status & SDHC_IRQSTAT_CC)) { - errors |= SDC_COMMAND_TIMEOUT; - } - if (status & SDHC_IRQSTAT_CCE) { - errors |= SDC_CMD_CRC_ERROR; - } - - /* If CTOE and CCE are both set, this indicates that the Kinetis - SDHC peripheral has detected a CMD line conflict in a - multi-master scenario. There's no specific code for that, so just - pass it through as a combined timeout+CRC failure. */ - - /* Translate any other framing and protocol errors into CRC errors. */ - if (status & ~(SDHC_IRQSTAT_CCE|SDHC_IRQSTAT_CTOE|SDHC_IRQSTAT_CC)) { - errors |= SDC_CMD_CRC_ERROR; - } - - return errors; -} - -/** - * Translate error bits from a card's R1 response word into the HAL's - * error flag set. - * - * This function should probably be in the HLD, not here. - */ -static sdcflags_t translate_mmcsd_error(uint32_t cardstatus) { - sdcflags_t errors = 0; - - cardstatus &= MMCSD_R1_ERROR_MASK; - - if (cardstatus & MMC_ERR_COM_CRC_ERROR) - errors |= SDC_CMD_CRC_ERROR; - - if (cardstatus & MMC_ERR_CARD_ECC_FAILED) - errors |= SDC_DATA_CRC_ERROR; - - /* TODO: Extend the HLD error codes at least enough to distinguish - between invalid command/parameter errors (card is OK, but - retrying w/o change won't help) and other errors */ - if (cardstatus & ~(MMC_ERR_COM_CRC_ERROR|MMC_ERR_CARD_ECC_FAILED)) - errors |= SDC_UNHANDLED_ERROR; - - return errors; -} - -/** - * @brief Perform one CMD transaction on the SD bus. - */ -static bool send_and_wait_cmd(SDCDriver *sdcp, uint32_t cmd) { - /* SDCLKEN (CMD clock enabled) should be true; - * SDSTB (clock stable) should be true; - * CIHB (command inhibit / busy) should be false */ - osalDbgAssert((SDHC->PRSSTAT & (SDHC_PRSSTAT_SDSTB|SDHC_PRSSTAT_CIHB)) == SDHC_PRSSTAT_SDSTB, "Not in expected state"); - osalDbgAssert(SDHC->SYSCTL & SDHC_SYSCTL_SDCLKEN, "Clock disabled"); - osalDbgCheck((cmd & SDHC_XFERTYP_DPSEL) == 0); - osalDbgCheck((SDHC->IRQSTAT & (SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | SDHC_IRQSTAT_CCE | - SDHC_IRQSTAT_CTOE | SDHC_IRQSTAT_CC)) == 0); - - /* This initiates the CMD transaction */ - TRACE(1, cmd); - SDHC->XFERTYP = cmd; - - uint32_t events = - SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | SDHC_IRQSTAT_CCE | - SDHC_IRQSTAT_CTOE | /* SDHC_IRQSTAT_CRM | */ SDHC_IRQSTAT_CC; - wait_interrupt(sdcp, SDHC_IRQSTAT_CTOE | SDHC_IRQSTAT_CC); - uint32_t status = SDHC->IRQSTAT & events; - - /* These bits are write-1-to-clear (w1c) */ - SDHC->IRQSTAT = status; - - /* In the normal case, the CC (command complete) bit is set but none - of the others are */ - if (status == SDHC_IRQSTAT_CC) - return HAL_SUCCESS; - - /* Translate the failure into the flags understood by the top half */ - sdcp->errors |= translate_cmd_error(status); - - TRACE(9, SDHC->PRSSTAT); - - /* Issue a reset to the CMD portion of the SDHC peripheral to clear the - error bits and enable subsequent commands */ - SDHC->SYSCTL |= SDHC_SYSCTL_RSTC; - - return HAL_FAILED; -} - -/** - * @brief Perform one data transaction on the SD bus. - */ -static bool send_and_wait_transfer(SDCDriver *sdcp, uint32_t cmd) { - - osalDbgCheck(cmd & SDHC_XFERTYP_DPSEL); - osalDbgCheck(cmd & SDHC_XFERTYP_DMAEN); - - const uint32_t cmd_end_bits = - SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | SDHC_IRQSTAT_CCE | - SDHC_IRQSTAT_CTOE | /* SDHC_IRQSTAT_CRM | */ SDHC_IRQSTAT_CC; - - const uint32_t transfer_end_bits = - SDHC_IRQSTAT_DMAE | SDHC_IRQSTAT_AC12E | SDHC_IRQSTAT_DEBE | - SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE | SDHC_IRQSTAT_TC; - - TRACE(3, cmd); - - osalSysLock(); - osalDbgCheck(sdcp->thread == NULL); - - /* Clear anything pending from an earlier transfer */ - SDHC->IRQSTAT = cmd_end_bits | transfer_end_bits | SDHC_IRQSTAT_DINT; - - /* Enable interrupts on completions or failures */ - uint32_t old_staten = SDHC->IRQSTATEN; - SDHC->IRQSTATEN = (old_staten & ~(SDHC_IRQSTAT_BRR|SDHC_IRQSTAT_BWR)) | (cmd_end_bits | transfer_end_bits | SDHC_IRQSTAT_DINT); - SDHC->IRQSIGEN = SDHC_IRQSTAT_CTOE | SDHC_IRQSTAT_CC; - - /* Start the transfer */ - SDHC->XFERTYP = cmd; - - /* Await an interrupt */ - osalThreadSuspendS(&sdcp->thread); - osalSysUnlock(); - - /* Retrieve the flags and clear them */ - uint32_t cmdstat = SDHC->IRQSTAT & cmd_end_bits; - SDHC->IRQSTAT = cmdstat; - TRACE(2, cmdstat); - - /* If the command failed, the transfer won't happen */ - if (cmdstat != SDHC_IRQSTAT_CC) { - /* The command couldn't be sent, or wasn't acknowledged */ - sdcp->errors |= translate_cmd_error(cmdstat); - - /* Clear the error status */ - SDHC->SYSCTL |= SDHC_SYSCTL_RSTC; - - if (cmdstat == (SDHC_IRQSTAT_CCE|SDHC_IRQSTAT_CTOE)) { - /* A CMD-line conflict is unlikely, but doesn't require further recovery */ - } else { - /* For most error situations, we don't know whether the command - failed to send or we got line noise while receiving. Make sure - we're in a sane state by resetting the connection. */ - recover_after_botched_transfer(sdcp); - } - - return HAL_FAILED; - } - - uint32_t cmdresp = SDHC->CMDRSP[0]; - TRACE(11, cmdresp); - if (cmdresp & MMCSD_R1_ERROR_MASK) { - /* The command was sent, and the card responded with an error indication */ - sdcp->errors |= translate_mmcsd_error(cmdresp); - return HAL_FAILED; - } - - /* Check for end of data transfer phase */ - uint32_t datastat; - for (;;) { - datastat = SDHC->IRQSTAT & (transfer_end_bits | SDHC_IRQSTAT_DINT); - if (datastat & transfer_end_bits) - break; - wait_interrupt(sdcp, transfer_end_bits); - } - TRACE(6, datastat); - SDHC->IRQSTAT = datastat; - - /* Handle data transfer errors */ - if ((datastat & ~(SDHC_IRQSTAT_DINT)) != SDHC_IRQSTAT_TC) { - bool should_cancel = false; - - /* Data phase errors */ - if (datastat & (SDHC_IRQSTAT_DCE|SDHC_IRQSTAT_DEBE)) { - sdcp->errors |= SDC_DATA_CRC_ERROR; - should_cancel = true; - } - if (datastat & SDHC_IRQSTAT_DTOE) { - sdcp->errors |= SDC_DATA_TIMEOUT; - should_cancel = true; - } - - /* Internal DMA error */ - if (datastat & SDHC_IRQSTAT_DMAE) { - sdcp->errors |= SDC_UNHANDLED_ERROR; - if (!(datastat & SDHC_IRQSTAT_TC)) - should_cancel = true; - } - - if (datastat & SDHC_IRQSTAT_AC12E) { - uint32_t cmd12error = SDHC->AC12ERR; - - /* We don't know if CMD12 was successfully executed */ - should_cancel = true; - - if (cmd12error & SDHC_AC12ERR_AC12NE) { - sdcp->errors |= SDC_UNHANDLED_ERROR; - } else { - if (cmd12error & SDHC_AC12ERR_AC12TOE) - sdcp->errors |= SDC_COMMAND_TIMEOUT; - if (cmd12error & (SDHC_AC12ERR_AC12CE|SDHC_AC12ERR_AC12EBE)) - sdcp->errors |= SDC_CMD_CRC_ERROR; - } - } - - if (should_cancel) { - recover_after_botched_transfer(sdcp); - } - - return HAL_FAILED; - } - - /* For a read transfer, make sure the DMA has finished transferring - * to host memory. (For a write transfer, the DMA necessarily finishes - * before the transfer does, so we don't need to wait for it - * specially.) */ - if (!(datastat & SDHC_IRQSTAT_DINT)) { - for(;;) { - datastat = SDHC->IRQSTAT & (SDHC_IRQSTAT_DINT|SDHC_IRQSTAT_DMAE); - if (datastat) { - SDHC->IRQSTAT = datastat; - TRACE(7, datastat); - break; - } - /* ...?? */ - } - } - - SDHC->IRQSTATEN = old_staten; - - return HAL_SUCCESS; -} - -/** - * @brief Wait for an interrupt from the SDHC peripheral. - * - * @param[in] mask Bits to enable in IRQSIGEN. - * - * @return MSG_OK - */ -static msg_t wait_interrupt(SDCDriver *sdcp, uint32_t mask) { - osalSysLock(); - SDHC->IRQSIGEN = mask; - msg_t wakeup = osalThreadSuspendS(&sdcp->thread); - osalSysUnlock(); - return wakeup; -} - -static void recover_after_botched_transfer(SDCDriver *sdcp) { - - /* Query the card state */ - uint32_t cardstatus; - if (sdc_lld_send_cmd_short_crc(sdcp, - MMCSD_CMD_SEND_STATUS, - sdcp->rca, &cardstatus) == HAL_SUCCESS) { - sdcp->errors |= translate_mmcsd_error(cardstatus); - uint32_t state = MMCSD_R1_STS(cardstatus); - if (state == MMCSD_STS_DATA) { - - /* Send a CMD12 to make sure the card isn't still transferring anything */ - SDHC->CMDARG = 0; - send_and_wait_cmd(sdcp, - SDHC_XFERTYP_CMDINX(MMCSD_CMD_STOP_TRANSMISSION) | - SDHC_XFERTYP_CMDTYP_ABORT | - /* TODO: Should we set CICEN and CCCEN here? */ - SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | - SDHC_XFERTYP_RSPTYP_48b); - } - } - - /* And reset the data block of the SDHC peripheral */ - SDHC->SYSCTL |= SDHC_SYSCTL_RSTD; -} - -/** - * @brief Perform one data transfer command - * - * Sends a command to the card and waits for the corresponding data transfer - * (either a read or write) to complete. - */ -static bool sdc_lld_transfer(SDCDriver *sdcp, uint32_t startblk, - uintptr_t buf, uint32_t n, - uint32_t cmdx) { - - osalDbgCheck(n > 0); - osalDbgCheck((buf & 0x03) == 0); /* Must be 32-bit aligned */ - - osalDbgAssert((SDHC->PRSSTAT & (SDHC_PRSSTAT_DLA|SDHC_PRSSTAT_CDIHB|SDHC_PRSSTAT_CIHB)) == 0, - "SDHC interface not ready"); - - /* We always operate in terms of 512-byte blocks; the upper-layer - driver doesn't change the block size. The SDHC spec suggests that - only low-capacity cards support block sizes other than 512 bytes - anyway (SDHC "Physical Layer Simplified Specification" ver 6.0) */ - - if (sdcp->cardmode & SDC_MODE_HIGH_CAPACITY) { - SDHC->CMDARG = startblk; - } else { - SDHC->CMDARG = startblk * MMCSD_BLOCK_SIZE; - } - - /* Store the DMA start address */ - SDHC->DSADDR = buf; - - uint32_t xfer; - /* For data transfers, we need to set some extra bits in XFERTYP according to the - transfer we're starting: - DPSEL -> enable data transfer - DTDSEL -> 1 for a read (card-to-host) transfer - MSBSEL, BCEN -> multiple block transfer using BLKATTR_BLKCNT - AC12EN -> Automatically issue MMCSD_CMD_STOP_TRANSMISSION at end of transfer - - Setting BLKCOUNT to 1 seems to be necessary even if MSBSEL+BCEN - is not set, despite the datasheet suggesting otherwise. I'm not - sure if this is a silicon bug or if I'm misunderstanding the - datasheet. - */ - SDHC->BLKATTR = - SDHC_BLKATTR_BLKCNT(n) | - SDHC_BLKATTR_BLKSIZE(MMCSD_BLOCK_SIZE); - if (n == 1) { - xfer = - cmdx | - SDHC_XFERTYP_CMDTYP_NORMAL | - SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | - SDHC_XFERTYP_RSPTYP_48b | - SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; - } else { - xfer = - cmdx | - SDHC_XFERTYP_CMDTYP_NORMAL | - SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | - SDHC_XFERTYP_RSPTYP_48b | - SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_BCEN | SDHC_XFERTYP_AC12EN | - SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; - } - - return send_and_wait_transfer(sdcp, xfer); -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if (PLATFORM_SDC_USE_SDC1 == TRUE) || defined(__DOXYGEN__) -OSAL_IRQ_HANDLER(KINETIS_SDHC_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - osalSysLockFromISR(); - - TRACEI(4, SDHC->IRQSTAT); - - /* We disable the interrupts, and wake up the usermode task to read - * the flags from IRQSTAT. - */ - SDHC->IRQSIGEN = 0; - - osalThreadResumeI(&SDCD1.thread, MSG_OK); - - osalSysUnlockFromISR(); - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level SDC driver initialization. - * - * @notapi - */ -void sdc_lld_init(void) { -#if PLATFORM_SDC_USE_SDC1 == TRUE - sdcObjectInit(&SDCD1); -#endif -} - - -/** - * @brief Configures and activates the SDC peripheral. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_start(SDCDriver *sdcp) { - - if (sdcp->state == BLK_STOP) { -#if defined(MK66F18) - /* Use IRC48M clock for SDHC */ - SIM->SOPT2 |= SIM_SOPT2_SDHCSRC(1); - SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3); -#else - SIM->SOPT2 = - (SIM->SOPT2 & ~SIM_SOPT2_SDHCSRC_MASK) | - SIM_SOPT2_SDHCSRC(0); /* SDHC clock source 0: Core/system clock. */ -#endif - SIM->SCGC3 |= SIM_SCGC3_SDHC; /* Enable clock to SDHC peripheral */ - - /* Reset the SDHC block */ - SDHC->SYSCTL |= SDHC_SYSCTL_RSTA; - while(SDHC->SYSCTL & SDHC_SYSCTL_RSTA) { - osalThreadSleepMilliseconds(1); - } - - SDHC->IRQSIGEN = 0; - nvicEnableVector(SDHC_IRQn, KINETIS_SDHC_PRIORITY); - } -} - -/** - * @brief Deactivates the SDC peripheral. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_stop(SDCDriver *sdcp) { - - if (sdcp->state != BLK_STOP) { - /* TODO: Should we perform a reset (RSTA) before putting the - peripheral to sleep? */ - - /* Disable the card clock */ - SDHC->SYSCTL &= ~( SDHC_SYSCTL_SDCLKEN ); - - /* Turn off interrupts */ - nvicDisableVector(SDHC_IRQn); - SDHC->IRQSIGEN = 0; - SDHC->IRQSTATEN &= ~( SDHC_IRQSTATEN_CINTSEN | - SDHC_IRQSTATEN_CINSEN | - SDHC_IRQSTATEN_CRMSEN ); - - /* Disable the clock to the SDHC peripheral block */ - SIM->SCGC3 &= ~( SIM_SCGC3_SDHC ); - } -} - -/** - * @brief Starts the SDIO clock and sets it to init mode (400kHz or less). - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_start_clk(SDCDriver *sdcp) { - - (void)sdcp; - - /* Stop the card clock (it should already be stopped) */ - SDHC->SYSCTL &= ~( SDHC_SYSCTL_SDCLKEN ); - - /* Change the divisor and DTOCV for a 400kHz card closk */ - uint32_t sysctl = - SDHC_SYSCTL_DTOCV(DTOCV_300ms_400kHz) | - divisor_settings(DIV_RND_UP(KINETIS_SDHC_PERIPHERAL_FREQUENCY, 400000)); - - /* Restart the clock */ - enable_clock_when_stable(sysctl); - - /* Reset any protocol machinery; this also runs the clock for 80 - cycles without any data bits to help initalize the card's state - (the Kinetis peripheral docs say that this is required after card - insertion or power-on, but the abridged SDHC specifications I - have don't seem to mention it) */ - SDHC->SYSCTL |= SDHC_SYSCTL_INITA; - - TRACE(9, SDHC->PRSSTAT); -} - -/** - * @brief Sets the SDIO clock to data mode (25MHz or less). - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] clk the clock mode - * - * @notapi - */ -void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk) { - - (void)sdcp; - - /* Stop the card clock */ - SDHC->SYSCTL &= ~( SDHC_SYSCTL_SDCLKEN ); - - /* Change the divisor */ - uint32_t ctl; - switch (clk) { - default: - case SDC_CLK_25MHz: - ctl = - SDHC_SYSCTL_DTOCV(DTOCV_700ms_25MHz) | - divisor_settings(DIV_RND_UP(KINETIS_SDHC_PERIPHERAL_FREQUENCY, 25000000)); - break; - case SDC_CLK_50MHz: - ctl = - SDHC_SYSCTL_DTOCV(DTOCV_700ms_50MHz) | - divisor_settings(DIV_RND_UP(KINETIS_SDHC_PERIPHERAL_FREQUENCY, 50000000)); - break; - } - - /* Restart the clock */ - enable_clock_when_stable(ctl); -} - -/** - * @brief Stops the SDIO clock. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_stop_clk(SDCDriver *sdcp) { - (void)sdcp; - SDHC->SYSCTL &= ~( SDHC_SYSCTL_SDCLKEN ); -} - -/** - * @brief Switches the bus to 4 bit or 8 bit mode. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] mode bus mode - * - * @notapi - */ -void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) { - (void)sdcp; - uint32_t proctl = SDHC->PROCTL & ~( SDHC_PROCTL_DTW_MASK ); - - switch (mode) { - case SDC_MODE_1BIT: - proctl |= SDHC_PROCTL_DTW_1BIT; - break; - case SDC_MODE_4BIT: - proctl |= SDHC_PROCTL_DTW_4BIT; - break; - case SDC_MODE_8BIT: - proctl |= SDHC_PROCTL_DTW_8BIT; - break; - default: - osalDbgAssert(false, "invalid bus mode"); - break; - } - - SDHC->PROCTL = proctl; -} - -/** - * @brief Sends an SDIO command with no response expected. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * - * @notapi - */ -void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) { - SDHC->CMDARG = arg; - uint32_t xfer = - SDHC_XFERTYP_CMDINX(cmd) | - SDHC_XFERTYP_CMDTYP_NORMAL | - /* DPSEL=0, CICEN=0, CCCEN=0 */ - SDHC_XFERTYP_RSPTYP_NONE; - - send_and_wait_cmd(sdcp, xfer); -} - -/** - * @brief Sends an SDIO command with a short response expected. - * @note The CRC is not verified. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (one word) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - SDHC->CMDARG = arg; - uint32_t xfer = - SDHC_XFERTYP_CMDINX(cmd) | - SDHC_XFERTYP_CMDTYP_NORMAL | - /* DPSEL=0, CICEN=0, CCCEN=0 */ - SDHC_XFERTYP_RSPTYP_48; - - bool waited = send_and_wait_cmd(sdcp, xfer); - - *resp = SDHC->CMDRSP[0]; - - return waited; -} - -/** - * @brief Sends an SDIO command with a short response expected and CRC. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (one word) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - SDHC->CMDARG = arg; - uint32_t xfer = - SDHC_XFERTYP_CMDINX(cmd) | - SDHC_XFERTYP_CMDTYP_NORMAL | - /* DPSEL=0, CICEN=1, CCCEN=1 */ - SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | - SDHC_XFERTYP_RSPTYP_48; - - bool waited = send_and_wait_cmd(sdcp, xfer); - - *resp = SDHC->CMDRSP[0]; - TRACE(11, *resp); - - return waited; -} - -/** - * @brief Sends an SDIO command with a long response expected and CRC. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (four words) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - - /* In response format R2 (the 136-bit or "long" response) the CRC7 - field is valid, but the command index field is set to all 1s, so - we need to disable the command index check function (CICEN=0). */ - - SDHC->CMDARG = arg; - uint32_t xfer = - SDHC_XFERTYP_CMDINX(cmd) | - SDHC_XFERTYP_CMDTYP_NORMAL | - /* DPSEL=0, CICEN=0, CCCEN=1 */ - SDHC_XFERTYP_CCCEN | - SDHC_XFERTYP_RSPTYP_136; - - bool waited = send_and_wait_cmd(sdcp, xfer); - - resp[0] = SDHC->CMDRSP[0]; - resp[1] = SDHC->CMDRSP[1]; - resp[2] = SDHC->CMDRSP[2]; - resp[3] = SDHC->CMDRSP[3]; - - return waited; -} - -/** - * @brief Reads one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[out] buf pointer to the read buffer - * @param[in] n number of blocks to read - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ - -bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n) { - uint32_t cmdx = (n == 1)? - SDHC_XFERTYP_CMDINX(MMCSD_CMD_READ_SINGLE_BLOCK) : - SDHC_XFERTYP_CMDINX(MMCSD_CMD_READ_MULTIPLE_BLOCK); - cmdx |= SDHC_XFERTYP_DTDSEL; - - return sdc_lld_transfer(sdcp, startblk, (uintptr_t)buf, n, cmdx); -} - -/** - * @brief Writes one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to write - * @param[out] buf pointer to the write buffer - * @param[in] n number of blocks to write - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n) { - uint32_t cmdx = (n == 1)? - SDHC_XFERTYP_CMDINX(MMCSD_CMD_WRITE_BLOCK) : - SDHC_XFERTYP_CMDINX(MMCSD_CMD_WRITE_MULTIPLE_BLOCK); - - return sdc_lld_transfer(sdcp, startblk, (uintptr_t)buf, n, cmdx); -} - -/** - * @brief Waits for card idle condition. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @return The operation status. - * @retval HAL_SUCCESS the operation succeeded. - * @retval HAL_FAILED the operation failed. - * - * @api - */ -bool sdc_lld_sync(SDCDriver *sdcp) { - - (void)sdcp; - - return HAL_SUCCESS; -} - -bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, - uint8_t cmd, uint32_t argument) { - uintptr_t bufaddr = (uintptr_t)buf; - - osalDbgCheck((bufaddr & 0x03) == 0); /* Must be 32-bit aligned */ - osalDbgCheck(bytes > 0); - osalDbgCheck(bytes < 4096); - - osalDbgAssert((SDHC->PRSSTAT & (SDHC_PRSSTAT_DLA|SDHC_PRSSTAT_CDIHB|SDHC_PRSSTAT_CIHB)) == 0, - "SDHC interface not ready"); - - TRACE(5, argument); - - /* Store the cmd argument and DMA start address */ - SDHC->CMDARG = argument; - SDHC->DSADDR = bufaddr; - - /* We're reading one block, of a (possibly) nonstandard size */ - SDHC->BLKATTR = SDHC_BLKATTR_BLKSIZE(bytes); - - uint32_t xfer = - SDHC_XFERTYP_CMDINX(cmd) | /* the command */ - SDHC_XFERTYP_DTDSEL | /* read transfer (card -> host) */ - SDHC_XFERTYP_CMDTYP_NORMAL | - SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | - SDHC_XFERTYP_RSPTYP_48 | - SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; /* DMA-assisted data transfer */ - - return send_and_wait_transfer(sdcp, xfer); -} - -bool sdc_lld_is_card_inserted(SDCDriver *sdcp) { - (void)sdcp; - - return ( SDHC->PRSSTAT & SDHC_PRSSTAT_CLSL )? true : false; -} - -bool sdc_lld_is_write_protected(SDCDriver *sdcp) { - (void)sdcp; - return false; -} - -#endif /* HAL_USE_SDC == TRUE */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_sdc_lld.h b/os/hal/ports/KINETIS/LLD/hal_sdc_lld.h deleted file mode 100644 index 9f77bf6..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_sdc_lld.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - Copyright (C) 2017 Wim Lewis - - 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 hal_sdc_lld.h - * @brief PLATFORM SDC subsystem low level driver header. - * - * @addtogroup SDC - * @{ - */ - -#ifndef HAL_SDC_LLD_H -#define HAL_SDC_LLD_H - -#if (HAL_USE_SDC == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -#define SDHC_XFERTYP_CMDTYP_NORMAL SDHC_XFERTYP_CMDTYP(0) -#define SDHC_XFERTYP_CMDTYP_SUSPEND SDHC_XFERTYP_CMDTYP(1) -#define SDHC_XFERTYP_CMDTYP_RESUME SDHC_XFERTYP_CMDTYP(2) -#define SDHC_XFERTYP_CMDTYP_ABORT SDHC_XFERTYP_CMDTYP(3) - -#define SDHC_XFERTYP_RSPTYP_NONE SDHC_XFERTYP_RSPTYP(0) /* no response */ -#define SDHC_XFERTYP_RSPTYP_136 SDHC_XFERTYP_RSPTYP(1) /* 136-bit response */ -#define SDHC_XFERTYP_RSPTYP_48 SDHC_XFERTYP_RSPTYP(2) /* 48-bit response */ -#define SDHC_XFERTYP_RSPTYP_48b SDHC_XFERTYP_RSPTYP(3) /* 48-bit plus busy */ - -#define SDHC_PROCTL_DTW_1BIT SDHC_PROCTL_DTW(0) -#define SDHC_PROCTL_DTW_4BIT SDHC_PROCTL_DTW(1) -#define SDHC_PROCTL_DTW_8BIT SDHC_PROCTL_DTW(2) - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name PLATFORM configuration options - * @{ - */ -/** - * @brief SDC1 driver enable switch. - * @details If set to @p TRUE the support for SDC1 is included. - * @note The default is @p TRUE if HAL_USE_SDC is set. - */ -#if !defined(PLATFORM_SDC_USE_SDC1) || defined(__DOXYGEN__) -#define PLATFORM_SDC_USE_SDC1 TRUE -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of card flags. - */ -typedef uint32_t sdcmode_t; - -/** - * @brief SDC Driver condition flags type. - */ -typedef uint32_t sdcflags_t; - -/** - * @brief Type of a structure representing an SDC driver. - */ -typedef struct SDCDriver SDCDriver; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Working area for memory consuming operations. - * @note It is mandatory for detecting MMC cards bigger than 2GB else it - * can be @p NULL. - * @note Memory pointed by this buffer is only used by @p sdcConnect(), - * afterward it can be reused for other purposes. - */ - uint8_t *scratchpad; - /** - * @brief Bus width. - */ - sdcbusmode_t bus_width; - /* End of the mandatory fields.*/ -} SDCConfig; - -/** - * @brief @p SDCDriver specific methods. - */ -#define _sdc_driver_methods \ - _mmcsd_block_device_methods - -/** - * @extends MMCSDBlockDeviceVMT - * - * @brief @p SDCDriver virtual methods table. - */ -struct SDCDriverVMT { - _sdc_driver_methods -}; - -/** - * @brief Structure representing an SDC driver. - */ -struct SDCDriver { - /** - * @brief Virtual Methods Table. - */ - const struct SDCDriverVMT *vmt; - _mmcsd_block_device_data - /** - * @brief Current configuration data. - */ - const SDCConfig *config; - /** - * @brief Various flags regarding the mounted card. - */ - sdcmode_t cardmode; - /** - * @brief Errors flags. - */ - sdcflags_t errors; - /** - * @brief Card RCA. - */ - uint32_t rca; - /* End of the mandatory fields.*/ - - /* Platform specific fields */ - thread_reference_t thread; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if (PLATFORM_SDC_USE_SDC1 == TRUE) && !defined(__DOXYGEN__) -extern SDCDriver SDCD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void sdc_lld_init(void); - void sdc_lld_start(SDCDriver *sdcp); - void sdc_lld_stop(SDCDriver *sdcp); - void sdc_lld_start_clk(SDCDriver *sdcp); - void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk); - void sdc_lld_stop_clk(SDCDriver *sdcp); - void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode); - void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg); - bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, - uint8_t cmd, uint32_t argument); - bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t n); - bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t n); - bool sdc_lld_sync(SDCDriver *sdcp); - bool sdc_lld_is_card_inserted(SDCDriver *sdcp); - bool sdc_lld_is_write_protected(SDCDriver *sdcp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SDC == TRUE */ - -#endif /* HAL_SDC_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_serial_lld.c b/os/hal/ports/KINETIS/LLD/hal_serial_lld.c deleted file mode 100644 index a1b6632..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_serial_lld.c +++ /dev/null @@ -1,692 +0,0 @@ -/* - ChibiOS - Copyright (C) 2013-2015 Fabio Utzig - Copyright (C) 2017 Wim Lewis - - 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 - -#if KINETIS_SERIAL_USE_UART3 || defined(__DOXYGEN__) -SerialDriver SD4; -#endif - -#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__) -SerialDriver SD5; -#endif - -#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__) -SerialDriver SD6; -#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); - - /* Clearing on K20x, K60x, and KL2x/UART>0 is done by reading S1 and - * then reading D.*/ - - 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); - } -} - -#if defined(KL2x) && KINETIS_SERIAL_USE_UART0 -static void serve_error_interrupt_uart0(void) { - SerialDriver *sdp = &SD1; - UART_w_TypeDef *u = &(sdp->uart); - uint8_t s1 = *(u->s1_p); - - /* S1 bits are write-1-to-clear for UART0 on KL2x. */ - - 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; - } -} -#endif /* KL2x && KINETIS_SERIAL_USE_UART0 */ - -/** - * @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; - } - } - -#if defined(KL2x) && KINETIS_SERIAL_USE_UART0 - if (sdp == &SD1) { - serve_error_interrupt_uart0(); - return; - } -#endif - 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. - */ -static void notify(io_queue_t *qp) -{ - preload(qp->q_link); -} - -/** - * @brief Common driver initialization, except LP. - */ -static void sd_lld_init_driver(SerialDriver *SDn, UART_TypeDef *UARTn) { - /* Driver initialization.*/ - sdObjectInit(SDn, NULL, notify); - SDn->uart.bdh_p = &(UARTn->BDH); - SDn->uart.bdl_p = &(UARTn->BDL); - SDn->uart.c1_p = &(UARTn->C1); - SDn->uart.c2_p = &(UARTn->C2); - SDn->uart.c3_p = &(UARTn->C3); - SDn->uart.c4_p = &(UARTn->C4); - SDn->uart.s1_p = (volatile uint8_t *)&(UARTn->S1); - SDn->uart.s2_p = &(UARTn->S2); - SDn->uart.d_p = &(UARTn->D); - SDn->uart.uart_p = UARTn; -} - -/** - * @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) || defined(K60x) || defined(MK66F18) /* KL2x */ - - /* UARTs 0 and 1 are clocked from SYSCLK, others from BUSCLK on K20x and K60x. */ -#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) || defined(K60x) - *(uart->c4_p) = UARTx_C4_BRFA(divisor) | (*(uart->c4_p) & ~UARTx_C4_BRFA_MASK); -#endif /* K20x, K60x */ - - /* 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_SERIAL_USE_UART3 || defined(__DOXYGEN__) -OSAL_IRQ_HANDLER(KINETIS_SERIAL3_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - serve_interrupt(&SD4); - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__) -OSAL_IRQ_HANDLER(KINETIS_SERIAL4_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - serve_interrupt(&SD5); - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__) -OSAL_IRQ_HANDLER(KINETIS_SERIAL5_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - serve_interrupt(&SD6); - 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(); -#if defined(KL2x) - serve_error_interrupt_uart0(); -#else - serve_error_interrupt(&SD1); -#endif - 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 - -#if KINETIS_SERIAL_USE_UART3 || defined(__DOXYGEN__) -OSAL_IRQ_HANDLER(KINETIS_SERIAL3_ERROR_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - serve_error_interrupt(&SD4); - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__) -OSAL_IRQ_HANDLER(KINETIS_SERIAL4_ERROR_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - serve_error_interrupt(&SD5); - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__) -OSAL_IRQ_HANDLER(KINETIS_SERIAL5_ERROR_IRQ_VECTOR) { - OSAL_IRQ_PROLOGUE(); - serve_error_interrupt(&SD6); - 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.*/ -#if ! KINETIS_SERIAL0_IS_LPUART - sd_lld_init_driver(&SD1, UART0); -#else /* ! KINETIS_SERIAL0_IS_LPUART */ - /* little endian! */ - sdObjectInit(&SD1, NULL, notify); - 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.*/ -#if ! KINETIS_SERIAL1_IS_LPUART - sd_lld_init_driver(&SD2, UART1); -#else /* ! KINETIS_SERIAL1_IS_LPUART */ - /* little endian! */ - sdObjectInit(&SD2, NULL, notify); - 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 - sd_lld_init_driver(&SD3, UART2); -#endif /* KINETIS_SERIAL_USE_UART2 */ - -#if KINETIS_SERIAL_USE_UART3 - sd_lld_init_driver(&SD4, UART3); -#endif /* KINETIS_SERIAL_USE_UART3 */ - -#if KINETIS_SERIAL_USE_UART4 - sd_lld_init_driver(&SD5, UART4); -#endif /* KINETIS_SERIAL_USE_UART4 */ - -#if KINETIS_SERIAL_USE_UART5 - sd_lld_init_driver(&SD6, UART5); -#endif /* KINETIS_SERIAL_USE_UART5 */ -} - -/** - * @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 */ - -#if KINETIS_SERIAL_USE_UART3 - if (sdp == &SD4) { - SIM->SCGC4 |= SIM_SCGC4_UART3; - configure_uart(sdp, config); - nvicEnableVector(UART3Status_IRQn, KINETIS_SERIAL_UART3_PRIORITY); - nvicEnableVector(UART3Error_IRQn, KINETIS_SERIAL_UART3_PRIORITY); - } -#endif /* KINETIS_SERIAL_USE_UART3 */ - -#if KINETIS_SERIAL_USE_UART4 - if (sdp == &SD5) { - SIM->SCGC1 |= SIM_SCGC1_UART4; - configure_uart(sdp, config); - nvicEnableVector(UART4Status_IRQn, KINETIS_SERIAL_UART4_PRIORITY); - nvicEnableVector(UART4Error_IRQn, KINETIS_SERIAL_UART4_PRIORITY); - } -#endif /* KINETIS_SERIAL_USE_UART4 */ - -#if KINETIS_SERIAL_USE_UART5 - if (sdp == &SD6) { - SIM->SCGC1 |= SIM_SCGC1_UART5; - configure_uart(sdp, config); - nvicEnableVector(UART5Status_IRQn, KINETIS_SERIAL_UART5_PRIORITY); - nvicEnableVector(UART5Error_IRQn, KINETIS_SERIAL_UART5_PRIORITY); - } -#endif /* KINETIS_SERIAL_USE_UART5 */ - - } - /* 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 - -#if KINETIS_SERIAL_USE_UART3 - if (sdp == &SD4) { - nvicDisableVector(UART3Status_IRQn); - nvicDisableVector(UART3Error_IRQn); - SIM->SCGC4 &= ~SIM_SCGC4_UART3; - } -#endif - -#if KINETIS_SERIAL_USE_UART4 - if (sdp == &SD5) { - nvicDisableVector(UART4Status_IRQn); - nvicDisableVector(UART4Error_IRQn); - SIM->SCGC1 &= ~SIM_SCGC1_UART4; - } -#endif - -#if KINETIS_SERIAL_USE_UART5 - if (sdp == &SD6) { - nvicDisableVector(UART5Status_IRQn); - nvicDisableVector(UART5Error_IRQn); - SIM->SCGC1 &= ~SIM_SCGC1_UART5; - } -#endif - } -} - -#endif /* HAL_USE_SERIAL */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_serial_lld.h b/os/hal/ports/KINETIS/LLD/hal_serial_lld.h deleted file mode 100644 index 3cb6d2b..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_serial_lld.h +++ /dev/null @@ -1,287 +0,0 @@ -/* - 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 HAL_SERIAL_LLD_H_ -#define HAL_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 SD4 driver enable switch. - * @details If set to @p TRUE the support for SD4 is included. - */ -#if !defined(KINETIS_SERIAL_USE_UART3) || defined(__DOXYGEN__) -#define KINETIS_SERIAL_USE_UART3 FALSE -#endif -/** - * @brief SD5 driver enable switch. - * @details If set to @p TRUE the support for SD5 is included. - */ -#if !defined(KINETIS_SERIAL_USE_UART4) || defined(__DOXYGEN__) -#define KINETIS_SERIAL_USE_UART4 FALSE -#endif -/** - * @brief SD6 driver enable switch. - * @details If set to @p TRUE the support for SD6 is included. - */ -#if !defined(KINETIS_SERIAL_USE_UART5) || defined(__DOXYGEN__) -#define KINETIS_SERIAL_USE_UART5 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 UART3 interrupt priority level setting. - */ -#if !defined(KINETIS_SERIAL_UART3_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_SERIAL_UART3_PRIORITY 12 -#endif - -/** - * @brief UART4 interrupt priority level setting. - */ -#if !defined(KINETIS_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_SERIAL_UART4_PRIORITY 12 -#endif - -/** - * @brief UART5 interrupt priority level setting. - */ -#if !defined(KINETIS_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__) -#define KINETIS_SERIAL_UART5_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_UART3 && !KINETIS_HAS_SERIAL3 -#error "UART3 not present in the selected device" -#endif - -#if KINETIS_SERIAL_USE_UART4 && !KINETIS_HAS_SERIAL4 -#error "UART4 not present in the selected device" -#endif - -#if KINETIS_SERIAL_USE_UART5 && !KINETIS_HAS_SERIAL5 -#error "UART5 not present in the selected device" -#endif - -#if !(KINETIS_SERIAL_USE_UART0 || KINETIS_SERIAL_USE_UART1 || \ - KINETIS_SERIAL_USE_UART2 || KINETIS_SERIAL_USE_UART3 || \ - KINETIS_SERIAL_USE_UART4 || KINETIS_SERIAL_USE_UART5) -#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 - -#if KINETIS_SERIAL_USE_UART3 && !defined(__DOXYGEN__) -extern SerialDriver SD4; -#endif - -#if KINETIS_SERIAL_USE_UART4 && !defined(__DOXYGEN__) -extern SerialDriver SD5; -#endif - -#if KINETIS_SERIAL_USE_UART5 && !defined(__DOXYGEN__) -extern SerialDriver SD6; -#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 /* HAL_SERIAL_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_st_lld.c b/os/hal/ports/KINETIS/LLD/hal_st_lld.c deleted file mode 100644 index e6ed9e5..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_st_lld.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - 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/hal_st_lld.h b/os/hal/ports/KINETIS/LLD/hal_st_lld.h deleted file mode 100644 index 29c7035..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_st_lld.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - 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 HAL_ST_LLD_H_ -#define HAL_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 /* HAL_ST_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c b/os/hal/ports/KINETIS/LLD/hal_usb_lld.c deleted file mode 100644 index a01b92e..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_usb_lld.c +++ /dev/null @@ -1,873 +0,0 @@ -/* - 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 receiving in the chibios state machine */ - (usbp)->receiving &= ~1; - /* Call SETUP function (ChibiOS core), which prepares - * for send or receive and releases the buffer - */ - _usb_isr_invoke_setup_cb(usbp, ep); - /* When a setup packet is received, tx is suspended, - * so it needs to be resumed here. - */ - USB0->CTL &= ~USBx_CTL_TXSUSPENDTOKENBUSY; - } break; - case BDT_PID_IN: // IN - { - 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 - - /* Set USB clock source to MCGPLLCLK, MCGFLLCLK, USB1 PFD, or IRC48M */ - SIM->SOPT2 |= SIM_SOPT2_USBSRC; - -#if defined(K20x5) || defined(K20x7) || defined(MK66F18) - -#if KINETIS_MCG_MODE == KINETIS_MCG_MODE_FEI - - /* MCGOUTCLK is the SYSCLK frequency, so don't divide for USB clock */ - SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); - -#elif KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE - -#if !defined(MK66F18) - /* Note: We don't need this for MK66F18, we can use IRC48M clock for USB */ - #define KINETIS_USBCLK_FREQUENCY 48000000UL - uint32_t i,j; - for(i=0; i < 2; i++) { - 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: - osalDbgAssert(i<2 && j <8,"USB Init error"); -#endif - -#else /* KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE */ -#error USB clock setting not implemented for this KINETIS_MCG_MODE -#endif /* KINETIS_MCG_MODE == ... */ - -#if defined(MK66F18) - /* Switch from default MCGPLLCLK to IRC48M for USB */ - SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0); - SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3); -#endif - -#elif defined(KL25) || defined (KL26) || defined(KL27) - - /* No extra clock dividers for USB clock */ - -#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;iUSBPHYCTL |= SIM_USBPHYCTL_USBDISILIM; -#endif - - /* Enable Clock */ -#if KINETIS_USB0_IS_USBOTG - SIM->SCGC4 |= SIM_SCGC4_USBOTG; - -#else /* KINETIS_USB0_IS_USBOTG */ - SIM->SCGC4 |= SIM_SCGC4_USBFS; -#endif /* KINETIS_USB0_IS_USBOTG */ - -#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 - * Release the current DATA0 buffer - */ - bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0); - /* If DATA1 was expected, then the states are out of sync. - * So reset the other buffer too, and set it as DATA1. - * This should not happen in normal cases, but is possible in - * error situations. NOTE: it's possible that this is too late - * and the next packet has already been received and dropped, but - * there's nothing that we can do about that anymore at this point. - */ - if (os->data_bank == DATA1) - { - bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, os->odd_even^ODD)]; - bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); - } - /* After a SETUP, both in and out are always DATA1 */ - usbp->epc[ep]->in_state->data_bank = DATA1; - os->data_bank = DATA1; -} - -/** - * @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) { - if (ep == 0 && usbp->ep0state == USB_EP0_IN_SENDING_STS) { - /* When a status packet is about to be sent on endpoint 0 the - * next packet will be a setup packet, which means that the - * buffer we expect after this should be DATA0, and the following - * DATA1. Since no out packets should be in flight at this time - * it's safe to initialize the buffers according to the expectations - * here. - */ - const USBEndpointConfig* epc = usbp->epc[ep]; - bd_t * bd = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even)]; - bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even^ODD)]; - - bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1); - bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0); - epc->out_state->data_bank = DATA0; - } - usb_packet_transmit(usbp,ep,usbp->epc[ep]->in_state->txsize); -} - -/** - * @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/hal_usb_lld.h b/os/hal/ports/KINETIS/LLD/hal_usb_lld.h deleted file mode 100644 index bd4eb39..0000000 --- a/os/hal/ports/KINETIS/LLD/hal_usb_lld.h +++ /dev/null @@ -1,455 +0,0 @@ -/* - 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 HAL_USB_LLD_H_ -#define HAL_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 - -/** - * @brief Host wake-up procedure duration. - */ -#if !defined(USB_HOST_WAKEUP_DURATION) || defined(__DOXYGEN__) -#define USB_HOST_WAKEUP_DURATION 2 -#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 - -#if (USB_HOST_WAKEUP_DURATION < 2) || (USB_HOST_WAKEUP_DURATION > 15) -#error "invalid USB_HOST_WAKEUP_DURATION setting, it must be between 2 and 15" -#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; - /** - * @brief State of the driver when a suspend happened. - */ - usbstate_t saved_state; -#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 - -/** - * @brief Start of host wake-up procedure. - * - * @notapi - */ -#define usb_lld_wakeup_host(usbp) \ - do{ \ - USB0->CTL |= USBx_CTL_RESUME; \ - osalThreadSleepMilliseconds(USB_HOST_WAKEUP_DURATION); \ - USB0->CTL &= ~USBx_CTL_RESUME; \ - } while (false) - -/*===========================================================================*/ -/* 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 /* HAL_USB_LLD_H_ */ - -/** @} */ -- cgit v1.2.3