aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/ports/KINETIS/LLD
diff options
context:
space:
mode:
authorFabio Utzig <utzig@utzig.org>2016-03-07 12:33:00 -0300
committerFabio Utzig <utzig@utzig.org>2016-03-07 12:33:00 -0300
commitb67ecdfeca2d1d5289e4c9b577a1940cf8702ab3 (patch)
tree9c78c9da3117a04f99d651ad8b774b6de18e4d83 /os/hal/ports/KINETIS/LLD
parent37ac6a4611ca3625343ef1a17943e66459f825af (diff)
downloadChibiOS-Contrib-b67ecdfeca2d1d5289e4c9b577a1940cf8702ab3.tar.gz
ChibiOS-Contrib-b67ecdfeca2d1d5289e4c9b577a1940cf8702ab3.tar.bz2
ChibiOS-Contrib-b67ecdfeca2d1d5289e4c9b577a1940cf8702ab3.zip
[KINETIS] Move from main repo to contrib
Diffstat (limited to 'os/hal/ports/KINETIS/LLD')
-rw-r--r--os/hal/ports/KINETIS/LLD/adc_lld.c258
-rw-r--r--os/hal/ports/KINETIS/LLD/adc_lld.h360
-rw-r--r--os/hal/ports/KINETIS/LLD/ext_lld.c361
-rw-r--r--os/hal/ports/KINETIS/LLD/ext_lld.h188
-rw-r--r--os/hal/ports/KINETIS/LLD/i2c_lld.c415
-rw-r--r--os/hal/ports/KINETIS/LLD/i2c_lld.h208
6 files changed, 1790 insertions, 0 deletions
diff --git a/os/hal/ports/KINETIS/LLD/adc_lld.c b/os/hal/ports/KINETIS/LLD/adc_lld.c
new file mode 100644
index 0000000..c0904c8
--- /dev/null
+++ b/os/hal/ports/KINETIS/LLD/adc_lld.c
@@ -0,0 +1,258 @@
+/*
+ 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) {
+ _adc_isr_full_code(&ADCD1);
+
+ adcp->current_index = 0;
+
+ /* We are never finished in circular mode */
+ more = ADCD1.grpp->circular;
+ }
+
+ 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/adc_lld.h b/os/hal/ports/KINETIS/LLD/adc_lld.h
new file mode 100644
index 0000000..22db2c0
--- /dev/null
+++ b/os/hal/ports/KINETIS/LLD/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 KINETIS/LLD/adc_lld.h
+ * @brief KINETIS ADC subsystem low level driver header.
+ *
+ * @addtogroup ADC
+ * @{
+ */
+
+#ifndef _ADC_LLD_H_
+#define _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 /* _ADC_LLD_H_ */
+
+/** @} */
diff --git a/os/hal/ports/KINETIS/LLD/ext_lld.c b/os/hal/ports/KINETIS/LLD/ext_lld.c
new file mode 100644
index 0000000..e85f032
--- /dev/null
+++ b/os/hal/ports/KINETIS/LLD/ext_lld.c
@@ -0,0 +1,361 @@
+/*
+ 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_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
+}
+
+/**
+ * @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_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
+}
+
+/*===========================================================================*/
+/* 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 */
+
+/**
+ * @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 */
+
+/*===========================================================================*/
+/* 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/ext_lld.h b/os/hal/ports/KINETIS/LLD/ext_lld.h
new file mode 100644
index 0000000..465bb89
--- /dev/null
+++ b/os/hal/ports/KINETIS/LLD/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 KINETIS/LLD/ext_lld.h
+ * @brief KINETIS EXT subsystem low level driver header.
+ *
+ * @addtogroup EXT
+ * @{
+ */
+
+#ifndef _EXT_LLD_H_
+#define _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 /* _EXT_LLD_H_ */
+
+/** @} */
diff --git a/os/hal/ports/KINETIS/LLD/i2c_lld.c b/os/hal/ports/KINETIS/LLD/i2c_lld.c
new file mode 100644
index 0000000..4e42d16
--- /dev/null
+++ b/os/hal/ports/KINETIS/LLD/i2c_lld.c
@@ -0,0 +1,415 @@
+/*
+ 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/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.
+ */
+ 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_SYSCLK_FREQUENCY / i2cp->config->clock;
+ else
+ divisor = KINETIS_SYSCLK_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;
+
+ if (i2c->S & I2Cx_S_ARBL) {
+
+ i2cp->errors |= I2C_ARBITRATION_LOST;
+ i2c->S |= I2Cx_S_ARBL;
+
+ } else if (state == STATE_SEND) {
+
+ if (i2c->S & I2Cx_S_RXAK)
+ i2cp->errors |= I2C_ACK_FAILURE;
+ else if (i2cp->txbuf != NULL && i2cp->txidx < i2cp->txbytes)
+ i2c->D = i2cp->txbuf[i2cp->txidx++];
+ else
+ i2cp->intstate = STATE_STOP;
+
+ } else if (state == STATE_DUMMY) {
+
+ if (i2c->S & I2Cx_S_RXAK)
+ i2cp->errors |= I2C_ACK_FAILURE;
+ else {
+ i2c->C1 &= ~I2Cx_C1_TX;
+
+ if (i2cp->rxbytes > 1)
+ i2c->C1 &= ~I2Cx_C1_TXAK;
+ else
+ i2c->C1 |= I2Cx_C1_TXAK;
+ (void) i2c->D;
+ i2cp->intstate = STATE_RECV;
+ }
+
+ } else if (state == STATE_RECV) {
+
+ if (i2cp->rxbytes > 1) {
+ if (i2cp->rxidx == (i2cp->rxbytes - 2))
+ i2c->C1 |= I2Cx_C1_TXAK;
+ else
+ i2c->C1 &= ~I2Cx_C1_TXAK;
+ }
+
+ if (i2cp->rxidx == i2cp->rxbytes - 1)
+ i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST);
+
+ i2cp->rxbuf[i2cp->rxidx++] = i2c->D;
+
+ if (i2cp->rxidx == i2cp->rxbytes)
+ i2cp->intstate = STATE_STOP;
+ }
+
+ /* 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__)
+
+/* FIXME: KL2x has I2C1 on Vector64; K2x don't have I2C1! */
+OSAL_IRQ_HANDLER(Vector64) {
+
+ 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;
+ i2cp->intstate = STATE_STOP;
+}
+
+/**
+ * @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) {
+
+ (void)timeout;
+ msg_t msg;
+
+ 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;
+
+ /* send START */
+ i2cp->i2c->C1 |= I2Cx_C1_MST;
+ i2cp->i2c->C1 |= I2Cx_C1_TX;
+
+ /* FIXME: should not use busy waiting! */
+ while (!(i2cp->i2c->S & I2Cx_S_BUSY));
+
+ i2cp->i2c->D = addr << 1 | op;
+
+ msg = osalThreadSuspendTimeoutS(&i2cp->thread, TIME_INFINITE);
+
+ /* FIXME */
+ //if (i2cp->i2c->S & I2Cx_S_RXAK)
+ // i2cp->errors |= I2C_ACK_FAILURE;
+
+ if (msg == MSG_OK && txbuf != NULL && rxbuf != NULL) {
+ i2cp->i2c->C1 |= I2Cx_C1_RSTA;
+ /* FIXME */
+ while (!(i2cp->i2c->S & I2Cx_S_BUSY));
+
+ i2cp->intstate = STATE_DUMMY;
+ i2cp->i2c->D = i2cp->addr << 1 | 1;
+
+ msg = osalThreadSuspendTimeoutS(&i2cp->thread, TIME_INFINITE);
+ }
+
+ 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. <b>After a
+ * timeout the driver must be stopped and restarted
+ * because the bus is in an uncertain state</b>.
+ *
+ * @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_DUMMY;
+ 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. <b>After a
+ * timeout the driver must be stopped and restarted
+ * because the bus is in an uncertain state</b>.
+ *
+ * @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/i2c_lld.h b/os/hal/ports/KINETIS/LLD/i2c_lld.h
new file mode 100644
index 0000000..11de3ae
--- /dev/null
+++ b/os/hal/ports/KINETIS/LLD/i2c_lld.h
@@ -0,0 +1,208 @@
+/*
+ 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/i2c_lld.h
+ * @brief KINETIS I2C subsystem low level driver header.
+ *
+ * @addtogroup I2C
+ * @{
+ */
+
+#ifndef _I2C_LLD_H_
+#define _I2C_LLD_H_
+
+#if HAL_USE_I2C || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+#define STATE_STOP 0x00
+#define STATE_SEND 0x01
+#define STATE_RECV 0x02
+#define STATE_DUMMY 0x03
+
+/*===========================================================================*/
+/* 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
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* 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__)
+#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__)
+ /**
+ * @brief Mutex protecting the bus.
+ */
+ mutex_t mutex;
+#elif CH_CFG_USE_SEMAPHORES
+ semaphore_t semaphore;
+#endif
+#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;
+};
+
+/*===========================================================================*/
+/* 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 /* _I2C_LLD_H_ */
+
+/** @} */