diff --git a/arch/arm/src/kinetis/Make.defs b/arch/arm/src/kinetis/Make.defs index 9d4ea5083ab..29eec9505af 100644 --- a/arch/arm/src/kinetis/Make.defs +++ b/arch/arm/src/kinetis/Make.defs @@ -131,6 +131,14 @@ ifeq ($(CONFIG_PWM),y) CHIP_CSRCS += kinetis_pwm.c endif +ifeq ($(CONFIG_I2C),y) +CHIP_CSRCS += kinetis_i2c.c +endif + +ifeq ($(CONFIG_RTC),y) +CHIP_CSRCS += kinetis_rtc.c +endif + ifeq ($(CONFIG_NET),y) ifeq ($(CONFIG_KINETIS_ENET),y) CHIP_CSRCS += kinetis_enet.c diff --git a/arch/arm/src/kinetis/chip/kinetis_rtc.h b/arch/arm/src/kinetis/chip/kinetis_rtc.h index d00c02a6974..948c6ce8771 100644 --- a/arch/arm/src/kinetis/chip/kinetis_rtc.h +++ b/arch/arm/src/kinetis/chip/kinetis_rtc.h @@ -59,7 +59,7 @@ #define KINETIS_RTC_CR_OFFSET 0x0010 /* RTC Control Register */ #define KINETIS_RTC_SR_OFFSET 0x0014 /* RTC Status Register */ #define KINETIS_RTC_LR_OFFSET 0x0018 /* RTC Lock Register */ -#if defined(KINETIS_K40) || defined(KINETIS_K64) +#if defined(KINETIS_K20) || defined(KINETIS_K40) || defined(KINETIS_K64) # define KINETIS_RTC_IER_OFFSET 0x001c /* RTC Interrupt Enable Register (K40) */ #endif #ifdef KINETIS_K60 @@ -77,7 +77,7 @@ #define KINETIS_RTC_CR (KINETIS_RTC_BASE+KINETIS_RTC_CR_OFFSET) #define KINETIS_RTC_SR (KINETIS_RTC_BASE+KINETIS_RTC_SR_OFFSET) #define KINETIS_RTC_LR (KINETIS_RTC_BASE+KINETIS_RTC_LR_OFFSET) -#if defined(KINETIS_K40) || defined(KINETIS_K64) +#if defined(KINETIS_K20) || defined(KINETIS_K40) || defined(KINETIS_K64) # define KINETIS_RTC_IER (KINETIS_RTC_BASE+KINETIS_RTC_IER_OFFSET) #endif #ifdef KINETIS_K60 @@ -135,13 +135,13 @@ #define RTC_LR_TCL (1 << 3) /* Bit 3: Time Compensation Lock */ #define RTC_LR_CRL (1 << 4) /* Bit 4: Control Register Lock */ #define RTC_LR_SRL (1 << 5) /* Bit 5: Status Register Lock */ -#ifdef KINETIS_K40 +#if defined(KINETIS_K20) || defined(KINETIS_K40) # define RTC_LR_LRL (1 << 6) /* Bit 6: Lock Register Lock (K40) */ #endif /* Bits 7-31: Reserved */ /* RTC Interrupt Enable Register (32-bits, K40) */ -#if defined(KINETIS_K40) || defined(KINETIS_K64) +#if defined(KINETIS_K20) || defined(KINETIS_K40) || defined(KINETIS_K64) # define RTC_IER_TIIE (1 << 0) /* Bit 0: Time Invalid Interrupt Enable */ # define RTC_IER_TOIE (1 << 1) /* Bit 1: Time Overflow Interrupt Enable */ # define RTC_IER_TAIE (1 << 2) /* Bit 2: Time Alarm Interrupt Enable */ diff --git a/arch/arm/src/kinetis/kinetis_alarm.h b/arch/arm/src/kinetis/kinetis_alarm.h new file mode 100644 index 00000000000..ee135a5eb2f --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_alarm.h @@ -0,0 +1,78 @@ +#ifndef __ARCH_ARM_SRC_KINETIS_ALARM_H +#define __ARCH_ARM_SRC_KINETIS_ALARM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +#ifdef CONFIG_RTC_ALARM + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/* The form of an alarm callback */ + +typedef CODE void (*alarmcb_t)(void); + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: kinetis_rtc_setalarm + * + * Description: + * Set up an alarm. + * + * Input Parameters: + * tp - the time to set the alarm + * callback - the function to call when the alarm expires. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +struct timespec; +int kinetis_rtc_setalarm(FAR const struct timespec *tp, alarmcb_t callback); + +/**************************************************************************** + * Name: kinetis_rtc_cancelalarm + * + * Description: + * Cancel a pending alarm alarm + * + * Input Parameters: + * none + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ****************************************************************************/ + +int kinetis_rtc_cancelalarm(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_RTC_ALARM */ +#endif /* __ARCH_ARM_SRC_KINETIS_ALARM_H */ diff --git a/arch/arm/src/kinetis/kinetis_i2c.c b/arch/arm/src/kinetis/kinetis_i2c.c new file mode 100644 index 00000000000..7ae099a6857 --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_i2c.c @@ -0,0 +1,687 @@ +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "chip.h" +#include "up_arch.h" +#include "up_internal.h" + +#include "kinetis_config.h" +#include "chip.h" +#include "chip/kinetis_i2c.h" +#include "chip/kinetis_sim.h" +#include "chip/kinetis_pinmux.h" +#include "kinetis.h" +#include "kinetis_i2c.h" + + +#if defined(CONFIG_KINETIS_I2C0) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define I2C_TIMEOUT (20*1000/CONFIG_USEC_PER_TICK) /* 20 mS */ + +#define I2C_DEFAULT_FREQUENCY 400000 + +#define STATE_OK 0 +#define STATE_ARBITRATION_ERROR 1 +#define STATE_TIMEOUT 2 +#define STATE_NAK 3 + +/* + * TODO: + * - revisar tamanio de todos los registros (getreg/putreg) + */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct kinetis_i2cdev_s +{ + struct i2c_master_s dev; /* Generic I2C device */ + unsigned int base; /* Base address of registers */ + uint16_t irqid; /* IRQ for this device */ + uint32_t baseFreq; /* branch frequency */ + + sem_t mutex; /* Only one thread can access at a time */ + sem_t wait; /* Place to wait for state machine completion */ + volatile uint8_t state; /* State of state machine */ + WDOG_ID timeout; /* watchdog to timeout when bus hung */ + uint32_t frequency; /* Current I2C frequency */ + + struct i2c_msg_s *msgs; /* remaining transfers - first one is in progress */ + unsigned int nmsg; /* number of transfer remaining */ + + uint16_t wrcnt; /* number of bytes sent to tx fifo */ + uint16_t rdcnt; /* number of bytes read from rx fifo */ +}; + +static struct kinetis_i2cdev_s g_i2c_dev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int kinetis_i2c_start(struct kinetis_i2cdev_s *priv); +static void kinetis_i2c_stop(struct kinetis_i2cdev_s *priv); +static int kinetis_i2c_interrupt(int irq, FAR void *context); +static void kinetis_i2c_timeout(int argc, uint32_t arg, ...); +static void kinetis_i2c_setfrequency(struct kinetis_i2cdev_s *priv, + uint32_t frequency); +static int kinetis_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int kinetis_i2c_reset(FAR struct i2c_master_s * dev); +#endif + +/**************************************************************************** + * I2C device operations + ****************************************************************************/ + +struct i2c_ops_s kinetis_i2c_ops = +{ + .transfer = kinetis_i2c_transfer +#ifdef CONFIG_I2C_RESET + , .reset = kinetis_i2c_reset +#endif +}; + +/**************************************************************************** + * Name: kinetis_i2c_setfrequency + * + * Description: + * Set the frequency for the next transfer + * + ****************************************************************************/ + +static void kinetis_i2c_setfrequency(struct kinetis_i2cdev_s *priv, + uint32_t frequency) +{ + if (frequency == priv->frequency) return; + + /* TODO: use apropriate definitions */ + + #if BOARD_BUS_FREQ == 120000000 + if (frequency < 400000) { + I2C0_F = I2C_F_DIV1152; // 104 kHz + } else if (frequency < 1000000) { + I2C0_F = I2C_F_DIV288; // 416 kHz + } else { + I2C0_F = I2C_F_DIV128; // 0.94 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 108000000 + if (frequency < 400000) { + I2C0_F = I2C_F_DIV1024; // 105 kHz + } else if (frequency < 1000000) { + I2C0_F = I2C_F_DIV256; // 422 kHz + } else { + I2C0_F = I2C_F_DIV112; // 0.96 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 96000000 + if (frequency < 400000) { + I2C0_F = I2C_F_DIV960; // 100 kHz + } else if (frequency < 1000000) { + I2C0_F = I2C_F_DIV240; // 400 kHz + } else { + I2C0_F = I2C_F_DIV96; // 1.0 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 90000000 + if (frequency < 400000) { + I2C0_F = I2C_F_DIV896; // 100 kHz + } else if (frequency < 1000000) { + I2C0_F = I2C_F_DIV224; // 402 kHz + } else { + I2C0_F = I2C_F_DIV88; // 1.02 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 80000000 + if (frequency < 400000) { + I2C0_F = I2C_F_DIV768; // 104 kHz + } else if (frequency < 1000000) { + I2C0_F = I2C_F_DIV192; // 416 kHz + } else { + I2C0_F = I2C_F_DIV80; // 1.0 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 72000000 + if (frequency < 400000) { + I2C0_F = I2C_F_DIV640; // 112 kHz + } else if (frequency < 1000000) { + I2C0_F = I2C_F_DIV192; // 375 kHz + } else { + I2C0_F = I2C_F_DIV72; // 1.0 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 64000000 + if (frequency < 400000) { + I2C0_F = I2C_F_DIV640; // 100 kHz + } else if (frequency < 1000000) { + I2C0_F = I2C_F_DIV160; // 400 kHz + } else { + I2C0_F = I2C_F_DIV64; // 1.0 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 60000000 + if (frequency < 400000) { + I2C0_F = 0x2C; // 104 kHz + } else if (frequency < 1000000) { + I2C0_F = 0x1C; // 416 kHz + } else { + I2C0_F = 0x12; // 938 kHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 56000000 + if (frequency < 400000) { + I2C0_F = 0x2B; // 109 kHz + } else if (frequency < 1000000) { + I2C0_F = 0x1C; // 389 kHz + } else { + I2C0_F = 0x0E; // 1 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 54000000 + if (frequency < 400000) { + I2C0_F = I2C_F_DIV512; // 105 kHz + } else if (frequency < 1000000) { + I2C0_F = I2C_F_DIV128; // 422 kHz + } else { + I2C0_F = I2C_F_DIV56; // 0.96 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 48000000 + if (frequency < 400000) { + I2C0_F = 0x27; // 100 kHz + } else if (frequency < 1000000) { + I2C0_F = 0x1A; // 400 kHz + } else { + I2C0_F = 0x0D; // 1 MHz + } + I2C0_FLT = 4; + #elif BOARD_BUS_FREQ == 40000000 + if (frequency < 400000) { + I2C0_F = 0x29; // 104 kHz + } else if (frequency < 1000000) { + I2C0_F = 0x19; // 416 kHz + } else { + I2C0_F = 0x0B; // 1 MHz + } + I2C0_FLT = 3; + #elif BOARD_BUS_FREQ == 36000000 + if (frequency < 400000) { + putreg8(0x28, KINETIS_I2C0_F); // 113 kHz + } else if (frequency < 1000000) { + putreg8(0x19, KINETIS_I2C0_F); // 375 kHz + } else { + putreg8(0x0A, KINETIS_I2C0_F); // 1 MHz + } + putreg8(3, KINETIS_I2C0_FLT); + #elif BOARD_BUS_FREQ == 24000000 + if (frequency < 400000) { + I2C0_F = 0x1F; // 100 kHz + } else if (frequency < 1000000) { + I2C0_F = 0x12; // 375 kHz + } else { + I2C0_F = 0x02; // 1 MHz + } + I2C0_FLT = 2; + #elif BOARD_BUS_FREQ == 16000000 + if (frequency < 400000) { + I2C0_F = 0x20; // 100 kHz + } else if (frequency < 1000000) { + I2C0_F = 0x07; // 400 kHz + } else { + I2C0_F = 0x00; // 800 MHz + } + I2C0_FLT = 1; + #elif BOARD_BUS_FREQ == 8000000 + if (frequency < 400000) { + I2C0_F = 0x14; // 100 kHz + } else { + I2C0_F = 0x00; // 400 kHz + } + I2C0_FLT = 1; + #elif BOARD_BUS_FREQ == 4000000 + if (frequency < 400000) { + I2C0_F = 0x07; // 100 kHz + } else { + I2C0_F = 0x00; // 200 kHz + } + I2C0_FLT = 1; + #elif BOARD_BUS_FREQ == 2000000 + I2C0_F = 0x00; // 100 kHz + I2C0_FLT = 1; + #else + #error "F_BUS must be 120, 108, 96, 9, 80, 72, 64, 60, 56, 54, 48, 40, 36, 24, 16, 8, 4 or 2 MHz" + #endif + + priv->frequency = frequency; +} + +/**************************************************************************** + * Name: kinetis_i2c_start + * + * Description: + * Initiate I2C transfer (START/RSTART + address) + * + ****************************************************************************/ + +static int kinetis_i2c_start(struct kinetis_i2cdev_s *priv) +{ + struct i2c_msg_s *msg; + + msg = priv->msgs; + + /* now take control of the bus */ + if (getreg8(KINETIS_I2C0_C1) & I2C_C1_MST) + { + /* we are already the bus master, so send a repeated start */ + putreg8(I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_RSTA | I2C_C1_TX, KINETIS_I2C0_C1); + } + else + { + /* we are not currently the bus master, so wait for bus ready */ + while (getreg8(KINETIS_I2C0_S) & I2C_S_BUSY); + + /* become the bus master in transmit mode (send start) */ + putreg8(I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX, KINETIS_I2C0_C1); + } + + if (I2C_M_READ & msg->flags) /* DEBUG: should happen always */ + { + /* wait until start condition establishes control of the bus */ + while (1) { + if (getreg8(KINETIS_I2C0_S) & I2C_S_BUSY) break; + } + } + + /* initiate actual transfer (send address) */ + putreg8((I2C_M_READ & msg->flags) == I2C_M_READ ? + I2C_READADDR8(msg->addr) : + I2C_WRITEADDR8(msg->addr), KINETIS_I2C0_D); + + return OK; +} + +/**************************************************************************** + * Name: kinetis_i2c_stop + * + * Description: + * Perform a I2C transfer stop + * + ****************************************************************************/ + +static void kinetis_i2c_stop(struct kinetis_i2cdev_s *priv) +{ + putreg8(I2C_C1_IICEN | I2C_C1_IICIE, KINETIS_I2C0_C1); + sem_post(&priv->wait); +} + +/**************************************************************************** + * Name: kinetis_i2c_timeout + * + * Description: + * Watchdog timer for timeout of I2C operation + * + ****************************************************************************/ + +static void kinetis_i2c_timeout(int argc, uint32_t arg, ...) +{ + struct kinetis_i2cdev_s *priv = (struct kinetis_i2cdev_s *)arg; + + irqstate_t flags = enter_critical_section(); + priv->state = STATE_TIMEOUT; + sem_post(&priv->wait); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: kinetis_i2c_nextmsg + * + * Description: + * Setup for the next message. + * + ****************************************************************************/ + +void kinetis_i2c_nextmsg(struct kinetis_i2cdev_s *priv) +{ + priv->nmsg--; + + if (priv->nmsg > 0) + { + priv->msgs++; + sem_post(&priv->wait); + } + else + { + kinetis_i2c_stop(priv); + } +} + +/**************************************************************************** + * Name: kinetis_i2c_interrupt + * + * Description: + * The I2C Interrupt Handler + * + ****************************************************************************/ + +static int kinetis_i2c_interrupt(int irq, FAR void *context) +{ + struct kinetis_i2cdev_s *priv; + struct i2c_msg_s *msg; + uint32_t state; + int regval, dummy; + UNUSED(dummy); + + if (irq == KINETIS_IRQ_I2C0) + { + priv = &g_i2c_dev; + } + else + { + PANIC(); + } + + /* get current state */ + state = getreg8(KINETIS_I2C0_S); + msg = priv->msgs; + + /* arbitration lost */ + if (state & I2C_S_ARBL) + { + putreg8(I2C_S_IICIF | I2C_S_ARBL, KINETIS_I2C0_S); + priv->state = STATE_ARBITRATION_ERROR; + kinetis_i2c_stop(priv); + } + else + { + /* clear interrupt */ + putreg8(I2C_S_IICIF, KINETIS_I2C0_S); + + regval = getreg8(KINETIS_I2C0_C1); + + /* TX mode */ + if (regval & I2C_C1_TX) + { + /* last write was not acknowledged */ + if (state & I2C_S_RXAK) + { + priv->state = STATE_NAK; /* set error flag */ + kinetis_i2c_stop(priv); /* send STOP */ + } + else + { + /* actually intending to write */ + if ((I2C_M_READ & msg->flags) == 0) + { + /* wrote everything */ + if (priv->wrcnt == msg->length) + { + kinetis_i2c_nextmsg(priv); /* continue with next message */ + } + else + { + putreg8(msg->buffer[priv->wrcnt], KINETIS_I2C0_D); /* Put next byte */ + priv->wrcnt++; + } + } + /* actually intending to read (address was just sent) */ + else + { + if (msg->length == 1) /* go to RX mode, do not send ACK */ + putreg8(I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TXAK, KINETIS_I2C0_C1); + else /* go to RX mode */ + putreg8(I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST, KINETIS_I2C0_C1); + + /* TODO: handle zero-length reads */ + + dummy = getreg8(KINETIS_I2C0_D); /* dummy read to initiate reception */ + } + } + } + /* RX: mode */ + else + { + /* if last receiving byte */ + if (priv->rdcnt == (msg->length - 1)) + { + /* go to TX mode before last read, otherwise a new read is triggered */ + putreg8(I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX, KINETIS_I2C0_C1); /* go to TX mode */ + + msg->buffer[priv->rdcnt] = getreg8(KINETIS_I2C0_D); + priv->rdcnt++; + + kinetis_i2c_nextmsg(priv); + } + /* second to last receiving byte */ + else if (priv->rdcnt == (msg->length - 2)) + { + /* Do not ACK any more */ + putreg8(I2C_C1_IICEN | I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TXAK, KINETIS_I2C0_C1); + + msg->buffer[priv->rdcnt] = getreg8(KINETIS_I2C0_D); + priv->rdcnt++; + } + else + { + msg->buffer[priv->rdcnt] = getreg8(KINETIS_I2C0_D); + priv->rdcnt++; + } + } + } + + return OK; +} + +/**************************************************************************** + * Name: kinetis_i2c_transfer + * + * Description: + * Perform a sequence of I2C transfers + * + ****************************************************************************/ + +static int kinetis_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) +{ + struct kinetis_i2cdev_s *priv = (struct kinetis_i2cdev_s *)dev; + + DEBUGASSERT(dev != NULL); + + /* Get exclusive access to the I2C bus */ + + sem_wait(&priv->mutex); + + /* Set up for the transfer */ + priv->msgs = msgs; + priv->nmsg = count; + priv->state = STATE_OK; + + /* Configure the I2C frequency. + * REVISIT: Note that the frequency is set only on the first message. + * This could be extended to support different transfer frequencies for + * each message segment. + */ + + kinetis_i2c_setfrequency(priv, msgs->frequency); + + /* clear the status flags */ + putreg8(I2C_S_IICIF | I2C_S_ARBL, KINETIS_I2C0_S); + + /* Process every message */ + while (priv->nmsg && priv->state == STATE_OK) + { + priv->wrcnt = 0; + priv->rdcnt = 0; + + /* Initiate the transfer */ + kinetis_i2c_start(priv); + + /* wait for transfer complete */ + wd_start(priv->timeout, I2C_TIMEOUT, kinetis_i2c_timeout, 1, (uint32_t)priv); + sem_wait(&priv->wait); + + wd_cancel(priv->timeout); + } + + /* disable interrupts */ + putreg8(I2C_C1_IICEN, KINETIS_I2C0_C1); + + /* release access to I2C bus */ + sem_post(&priv->mutex); + + if (priv->state != STATE_OK) + return -1; + else + return 0; /* TODO: correct? */ +} + +/************************************************************************************ + * Name: kinetis_i2c_reset + * + * Description: + * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ************************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int kinetis_i2c_reset(FAR struct i2c_master_s * dev) +{ + return OK; +} +#endif /* CONFIG_I2C_RESET */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: kinetis_i2cbus_initialize + * + * Description: + * Initialise an I2C device + * + ****************************************************************************/ + +struct i2c_master_s *kinetis_i2cbus_initialize(int port) +{ + struct kinetis_i2cdev_s *priv; + + if (port > 1) + { + i2cerr("ERROR: Kinetis I2C Only suppors ports 0 and 1\n"); + return NULL; + } + + irqstate_t flags; + uint32_t regval; + + flags = enter_critical_section(); + + if (port == 0) + { + priv = &g_i2c_dev; + priv->base = KINETIS_I2C0_BASE; + priv->irqid = KINETIS_IRQ_I2C0; + priv->baseFreq = BOARD_BUS_FREQ; + + /* Enable clock */ + regval = getreg32(KINETIS_SIM_SCGC4); + regval |= SIM_SCGC4_I2C0; + putreg32(regval, KINETIS_SIM_SCGC4); + + kinetis_i2c_setfrequency(priv, I2C_DEFAULT_FREQUENCY); + + /* Disable while configuring */ + putreg8(0, KINETIS_I2C0_C1); + + /* Configure pins */ + kinetis_pinconfig(PIN_I2C0_SCL); + kinetis_pinconfig(PIN_I2C0_SDA); + + /* Enable */ + putreg8(I2C_C1_IICEN, KINETIS_I2C0_C1); + + /* High-drive select (TODO: why)? */ + regval = getreg8(KINETIS_I2C0_C2); + regval |= I2C_C2_HDRS; + putreg8(regval, KINETIS_I2C0_C2); + } + else + { + return NULL; + } + + leave_critical_section(flags); + + sem_init(&priv->mutex, 0, 1); + sem_init(&priv->wait, 0, 0); + + /* Allocate a watchdog timer */ + + priv->timeout = wd_create(); + DEBUGASSERT(priv->timeout != 0); + + /* Attach Interrupt Handler */ + + irq_attach(priv->irqid, kinetis_i2c_interrupt); + + /* Enable Interrupt Handler */ + + up_enable_irq(priv->irqid); + + /* Install our operations */ + + priv->dev.ops = &kinetis_i2c_ops; + return &priv->dev; +} + +/**************************************************************************** + * Name: kinetis_i2cbus_uninitialize + * + * Description: + * Uninitialise an I2C device + * + ****************************************************************************/ + +int kinetis_i2cbus_uninitialize(FAR struct i2c_master_s * dev) +{ + struct kinetis_i2cdev_s *priv = (struct kinetis_i2cdev_s *) dev; + + putreg8(0, KINETIS_I2C0_C1); + + up_disable_irq(priv->irqid); + irq_detach(priv->irqid); + return OK; +} + +#endif /* CONFIG_KINETIS_I2C0 */ diff --git a/arch/arm/src/kinetis/kinetis_i2c.h b/arch/arm/src/kinetis/kinetis_i2c.h new file mode 100644 index 00000000000..b42e248882a --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_i2c.h @@ -0,0 +1,52 @@ +#ifndef __ARCH_ARM_SRC_KINETIS_KINETIS_I2C_H +#define __ARCH_ARM_SRC_KINETIS_KINETIS_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include "chip/kinetis_i2c.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: kinetis_i2cbus_initialize + * + * Description: + * Initialize the selected I2C port. And return a unique instance of struct + * struct i2c_master_s. This function may be called to obtain multiple + * instances of the interface, each of which may be set up with a + * different frequency and slave address. + * + * Input Parameter: + * Port number (for hardware that has multiple I2C interfaces) + * + * Returned Value: + * Valid I2C device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +FAR struct i2c_master_s *kinetis_i2cbus_initialize(int port); + +/**************************************************************************** + * Name: kinetis_i2cbus_uninitialize + * + * Description: + * De-initialize the selected I2C port, and power down the device. + * + * Input Parameter: + * Device structure as returned by the lpc43_i2cbus_initialize() + * + * Returned Value: + * OK on success, ERROR when internal reference count mismatch or dev + * points to invalid hardware device. + * + ****************************************************************************/ + +int kinetis_i2cbus_uninitialize(FAR struct i2c_master_s *dev); + +#endif /* __ARCH_ARM_SRC_KINETIS_KINETIS_I2C_H */ diff --git a/arch/arm/src/kinetis/kinetis_rtc.c b/arch/arm/src/kinetis/kinetis_rtc.c new file mode 100644 index 00000000000..3d0af29c60d --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_rtc.c @@ -0,0 +1,323 @@ +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "up_arch.h" + +#include "kinetis_config.h" +#include "chip.h" +#include "chip/kinetis_rtc.h" +#include "chip/kinetis_sim.h" +#include "kinetis.h" +#include "kinetis_alarm.h" + +#if defined(CONFIG_RTC) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +static alarmcb_t g_alarmcb; +#endif + +/************************************************************************************ + * Private Declarations + ************************************************************************************/ + +static int kinetis_rtc_interrupt(int irq, void *context); + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +volatile bool g_rtc_enabled = false; + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: up_rtc_initialize + * + * Description: + * Initialize the hardware RTC per the selected configuration. This function is + * called once during the OS initialization sequence + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int up_rtc_initialize(void) +{ + int regval; + + /* enable RTC module */ + regval = getreg32(KINETIS_SIM_SCGC6); + regval |= SIM_SCGC6_RTC; + putreg32(regval, KINETIS_SIM_SCGC6); + + /* disable counters (just in case) */ + putreg32(0, KINETIS_RTC_SR); + + /* enable oscilator */ + putreg32(RTC_CR_SC16P | RTC_CR_SC4P | RTC_CR_OSCE, KINETIS_RTC_CR); /* capacitance values from teensyduino */ + /* TODO: delay some time (1024 cycles? would be 30ms) */ + + /* disable interrupts */ + putreg32(0, KINETIS_RTC_IER); + + /* reset flags requires writing the seconds register, the following line avoids altering any stored time value */ + putreg32(getreg32(KINETIS_RTC_TSR), KINETIS_RTC_TSR); + +#if defined(CONFIG_RTC_ALARM) + /* enable alarm interrupts */ + irq_attach(KINETIS_IRQ_RTC, kinetis_rtc_interrupt); + up_enable_irq(KINETIS_IRQ_RTC); +#endif + + /* enable counters */ + putreg32(RTC_SR_TCE, KINETIS_RTC_SR); + + /* mark RTC enabled */ + g_rtc_enabled = true; + + return OK; +} + +/************************************************************************************ + * Name: up_rtc_time + * + * Description: + * Get the current time in seconds. This is similar to the standard time() + * function. This interface is only required if the low-resolution RTC/counter + * hardware implementation selected. It is only used by the RTOS during + * initialization to set up the system time when CONFIG_RTC is set but neither + * CONFIG_RTC_HIRES nor CONFIG_RTC_DATETIME are set. + * + * Input Parameters: + * None + * + * Returned Value: + * The current time in seconds + * + ************************************************************************************/ + +#ifndef CONFIG_RTC_HIRES +time_t up_rtc_time(void) +{ + return getreg32(KINETIS_RTC_TSR); +} +#endif + +/************************************************************************************ + * Name: up_rtc_gettime + * + * Description: + * Get the current time from the high resolution RTC clock/counter. This interface + * is only supported by the high-resolution RTC/counter hardware implementation. + * It is used to replace the system timer. + * + * Input Parameters: + * tp - The location to return the high resolution time value. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_HIRES +int up_rtc_gettime(FAR struct timespec *tp) +{ + irqstate_t flags; + uint32_t seconds, prescaler, prescaler2; + + /* + * get prescaler and seconds register. this is in a loop which + * ensures that registers will be re-read if during the reads the + * prescaler has wrapped-around + */ + + flags = enter_critical_section(); + do + { + prescaler = getreg32(KINETIS_RTC_TPR); + seconds = getreg32(KINETIS_RTC_TSR); + prescaler2 = getreg32(KINETIS_RTC_TPR); + } + while (prescaler > prescaler2); + leave_critical_section(flags); + + /* build seconds + nanoseconds from seconds and prescaler register */ + tp->tv_sec = seconds; + tp->tv_nsec = prescaler * (1000000000 / CONFIG_RTC_FREQUENCY); + return OK; +} +#endif + +/************************************************************************************ + * Name: up_rtc_settime + * + * Description: + * Set the RTC to the provided time. All RTC implementations must be able to + * set their time based on a standard timespec. + * + * Input Parameters: + * tp - the time to use + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +int up_rtc_settime(FAR const struct timespec *tp) +{ + irqstate_t flags; + uint32_t seconds, prescaler; + + seconds = tp->tv_sec; + prescaler = tp->tv_nsec * (CONFIG_RTC_FREQUENCY / 1000000000); + + flags = enter_critical_section(); + + putreg32(0, KINETIS_RTC_SR); /* disable counter */ + + putreg32(prescaler, KINETIS_RTC_TPR); /* always write prescaler first */ + putreg32(seconds, KINETIS_RTC_TSR); + + putreg32(RTC_SR_TCE, KINETIS_RTC_SR); /* re-enable counter */ + + leave_critical_section(flags); + + return OK; +} + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: kinetis_rtc_setalarm + * + * Description: + * Set up an alarm. + * + * Input Parameters: + * tp - the time to set the alarm + * callback - the function to call when the alarm expires. + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int kinetis_rtc_setalarm(FAR const struct timespec *tp, alarmcb_t callback) +{ + /* Is there already something waiting on the ALARM? */ + if (g_alarmcb == NULL) + { + /* No.. Save the callback function pointer */ + + g_alarmcb = callback; + + /* Enable and set RTC alarm */ + + putreg32(tp->tv_sec, KINETIS_RTC_TAR); /* set alarm (also resets flags) */ + putreg32(RTC_IER_TAIE, KINETIS_RTC_IER); /* enable alarm interrupt */ + + return OK; + } + else + return -EBUSY; +} +#endif + +/************************************************************************************ + * Name: kinetis_rtc_cancelalarm + * + * Description: + * Cancel a pending alarm alarm + * + * Input Parameters: + * none + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure + * + ************************************************************************************/ + +#ifdef CONFIG_RTC_ALARM +int kinetis_rtc_cancelalarm(void) +{ + if (g_alarmcb != NULL) + { + /* Cancel the global callback function */ + + g_alarmcb = NULL; + + /* Unset the alarm */ + + putreg32(0, KINETIS_RTC_IER); /* disable alarm interrupt */ + + return OK; + } + else + return -ENODATA; +} +#endif + +/************************************************************************************ + * Name: kinetis_rtc_interrupt + * + * Description: + * RTC interrupt service routine + * + * Input Parameters: + * irq - The IRQ number that generated the interrupt + * context - Architecture specific register save information. + * + * Returned Value: + * Zero (OK) on success; A negated errno value on failure. + * + ************************************************************************************/ + +#if defined(CONFIG_RTC_ALARM) +static int kinetis_rtc_interrupt(int irq, void *context) +{ + if (g_alarmcb != NULL) + { + /* Alarm callback */ + g_alarmcb(); + g_alarmcb = NULL; + } + + /* Clear pending flags, disable alarm */ + putreg32(0, KINETIS_RTC_TAR); /* unset alarm (resets flags) */ + putreg32(0, KINETIS_RTC_IER); /* disable alarm interrupt */ + + return 0; +} +#endif + +#endif // KINETIS_RTC diff --git a/configs/teensy-3.x/include/board.h b/configs/teensy-3.x/include/board.h index e7342683519..b95734a04af 100644 --- a/configs/teensy-3.x/include/board.h +++ b/configs/teensy-3.x/include/board.h @@ -190,7 +190,7 @@ #define LED_STACKCREATED 1 /* STATUS LED=ON */ #define LED_INIRQ 2 /* STATUS LED=no change */ #define LED_SIGNAL 2 /* STATUS LED=no change */ -#define LED_ASSERTION 2 /* STATUS LED=no change */ +#define LED_ASSERTION 3 /* STATUS LED=no change */ #define LED_PANIC 3 /* STATUS LED=flashing */ /* Button definitions ***************************************************************/ @@ -232,6 +232,20 @@ # define PIN_UART0_TX PIN_UART1_TX_1 #endif +#ifdef CONFIG_KINETIS_I2C0 +#ifdef CONFIG_TEENSY_3X_I2C_ALT_PINS +# define PIN_I2C0_SCL (PIN_I2C0_SCL_1 | PIN_ALT2_OPENDRAIN | PIN_ALT2_SLOW) +# define PIN_I2C0_SDA (PIN_I2C0_SDA_1 | PIN_ALT2_OPENDRAIN | PIN_ALT2_SLOW) +#else +# define PIN_I2C0_SCL (PIN_I2C0_SCL_2 | PIN_ALT2_OPENDRAIN | PIN_ALT2_SLOW) +# define PIN_I2C0_SDA (PIN_I2C0_SDA_2 | PIN_ALT2_OPENDRAIN | PIN_ALT2_SLOW) +#endif +#endif + +#ifdef CONFIG_KINETIS_I2C1 +#error I2C1 not currently supported +#endif + /************************************************************************************ * Public Data ************************************************************************************/ diff --git a/configs/teensy-3.x/src/Makefile b/configs/teensy-3.x/src/Makefile index 3483e6deb2a..fdb261c551e 100644 --- a/configs/teensy-3.x/src/Makefile +++ b/configs/teensy-3.x/src/Makefile @@ -36,7 +36,7 @@ -include $(TOPDIR)/Make.defs ASRCS = -CSRCS = k20_boot.c k20_spi.c +CSRCS = k20_boot.c k20_spi.c k20_i2c.c ifeq ($(CONFIG_ARCH_LEDS),y) CSRCS += k20_autoleds.c diff --git a/configs/teensy-3.x/src/k20_boot.c b/configs/teensy-3.x/src/k20_boot.c index 6bd0282ddf4..cfb62f9b260 100644 --- a/configs/teensy-3.x/src/k20_boot.c +++ b/configs/teensy-3.x/src/k20_boot.c @@ -88,3 +88,13 @@ void kinetis_boardinitialize(void) board_autoled_initialize(); #endif } + +#if defined(CONFIG_BOARD_INITIALIZE) +void board_initialize(void) +{ +#if defined(CONFIG_KINETIS_I2C0) || defined(CONFIG_KINETIS_I2C1) + //if (kinetis_i2cdev_initialize) + kinetis_i2cdev_initialize(); +#endif +} +#endif diff --git a/configs/teensy-3.x/src/k20_i2c.c b/configs/teensy-3.x/src/k20_i2c.c new file mode 100644 index 00000000000..b6393b11d1f --- /dev/null +++ b/configs/teensy-3.x/src/k20_i2c.c @@ -0,0 +1,54 @@ +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "up_arch.h" +#include "chip.h" +#include "kinetis.h" +#include "kinetis_i2c.h" +#include "teensy-3x.h" + +#if defined(CONFIG_KINETIS_I2C0) || defined(CONFIG_KINETIS_I2C1) + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: kinetis_i2cdev_initialize + * + * Description: + * Called to configure I2C + * + ************************************************************************************/ + +void kinetis_i2cdev_initialize(void) +{ + i2c_dev = NULL; + +#if defined(CONFIG_KINETIS_I2C0) + i2c_dev = kinetis_i2cbus_initialize(0); +#if defined(CONFIG_I2C_DRIVER) + i2c_register(i2c_dev, 0); +#endif +#endif + +#if defined(CONFIG_KINETIS_I2C1) +#error Not yet supported in kinetis driver + i2c_dev = kinetis_i2cbus_initialize(1); +#if defined(CONFIG_I2C_DRIVER) + i2c_register(i2c_dev, 1); +#endif +#endif +} + +#endif /* CONFIG_KINETIS_I2C0 || CONFIG_KINETIS_I2C1 */ diff --git a/configs/teensy-3.x/src/teensy-3x.h b/configs/teensy-3.x/src/teensy-3x.h index 0e0b16b2599..030ac3d2cd6 100644 --- a/configs/teensy-3.x/src/teensy-3x.h +++ b/configs/teensy-3.x/src/teensy-3x.h @@ -44,6 +44,7 @@ #include #include #include +#include /************************************************************************************ * Pre-processor Definitions @@ -76,6 +77,8 @@ * Public data ************************************************************************************/ +FAR struct i2c_master_s* i2c_dev; + #ifndef __ASSEMBLY__ /************************************************************************************ @@ -92,6 +95,16 @@ extern void weak_function kinetis_spidev_initialize(void); +/************************************************************************************ + * Name: kinetis_i2cdev_initialize + * + * Description: + * Called to configure I2C + * + ************************************************************************************/ + +void kinetis_i2cdev_initialize(void); + /************************************************************************************ * Name: kinetis_usbinitialize *