diff --git a/arch/arm/src/lpc43xx/Make.defs b/arch/arm/src/lpc43xx/Make.defs index d6990c82449..b944c210b31 100644 --- a/arch/arm/src/lpc43xx/Make.defs +++ b/arch/arm/src/lpc43xx/Make.defs @@ -96,6 +96,8 @@ CHIP_CSRCS += lpc43_start.c lpc43_uart.c ifneq ($(CONFIG_SCHED_TICKLESS),y) CHIP_CSRCS += lpc43_timerisr.c +else +CHIP_CSRCS += lpc43_tickless_rit.c endif ifeq ($(CONFIG_BUILD_PROTECTED),y) diff --git a/arch/arm/src/lpc43xx/lpc43_ssp.c b/arch/arm/src/lpc43xx/lpc43_ssp.c index 8fbc2ad7923..146bf293691 100644 --- a/arch/arm/src/lpc43xx/lpc43_ssp.c +++ b/arch/arm/src/lpc43xx/lpc43_ssp.c @@ -162,8 +162,12 @@ static const struct spi_ops_s g_spi0ops = .cmddata = lpc43_ssp0cmddata, /* Provided externally */ #endif .send = ssp_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = ssp_exchange, +#else .sndblock = ssp_sndblock, .recvblock = ssp_recvblock, +#endif #ifdef CONFIG_SPI_CALLBACK .registercallback = lpc43_ssp0register, /* Provided externally */ #else @@ -197,8 +201,12 @@ static const struct spi_ops_s g_spi1ops = .cmddata = lpc43_ssp1cmddata, /* Provided externally */ #endif .send = ssp_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = ssp_exchange, +#else .sndblock = ssp_sndblock, .recvblock = ssp_recvblock, +#endif #ifdef CONFIG_SPI_CALLBACK .registercallback = lpc43_ssp1register, /* Provided externally */ #else @@ -526,6 +534,76 @@ static uint16_t ssp_send(FAR struct spi_dev_s *dev, uint16_t wd) return (uint16_t)regval; } +static void ssp_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) { + FAR struct lpc43_sspdev_s *priv = (FAR struct lpc43_sspdev_s *)dev; + union + { + FAR uint8_t *p8; + FAR uint16_t *p16; + FAR void *pv; + } tx; + union + { + FAR uint8_t *p8; + FAR uint16_t *p16; + FAR void *pv; + } rx; + uint32_t data; + uint32_t datadummy = (priv->nbits > 8)?0xffff:0xff; + uint32_t rxpending = 0; + + /* While there is remaining to be sent (and no synchronization error has occurred) */ + + sspdbg("nwords: %d\n", nwords); + + tx.pv = txbuffer; + rx.pv = rxbuffer; + + while (nwords || rxpending) + { + /* Write data to the data register while (1) the TX FIFO is + * not full, (2) we have not exceeded the depth of the TX FIFO, + * and (3) there are more bytes to be sent. + */ + + spivdbg("TX: rxpending: %d nwords: %d\n", rxpending, nwords); + while ((ssp_getreg(priv, LPC43_SSP_SR_OFFSET) & SSP_SR_TNF) && + (rxpending < LPC43_SSP_FIFOSZ) && nwords) + { + if (txbuffer && priv->nbits > 8) + { + data = (uint32_t)*tx.p16++; + } + else + { + data = (uint32_t)*tx.p8++; + } + + ssp_putreg(priv, LPC43_SSP_DR_OFFSET, txbuffer?data:datadummy); + nwords--; + rxpending++; + } + + /* Now, read the RX data from the RX FIFO while the RX FIFO is not empty */ + + spivdbg("RX: rxpending: %d\n", rxpending); + while (ssp_getreg(priv, LPC43_SSP_SR_OFFSET) & SSP_SR_RNE) + { + data = ssp_getreg(priv, LPC43_SSP_DR_OFFSET); + if (rxbuffer && priv->nbits > 8) + { + *rx.p16++ = (uint16_t)data; + } + else + { + *rx.p8++ = (uint8_t)data; + } + rxpending--; + } + } +} + /**************************************************************************** * Name: ssp_sndblock * @@ -547,72 +625,7 @@ static uint16_t ssp_send(FAR struct spi_dev_s *dev, uint16_t wd) static void ssp_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords) { - FAR struct lpc43_sspdev_s *priv = (FAR struct lpc43_sspdev_s *)dev; - union - { - FAR const uint8_t *p8; - FAR const uint16_t *p16; - FAR const void *pv; - } u; - uint32_t data; - uint32_t sr; - - /* Loop while thre are bytes remaining to be sent */ - - sspdbg("nwords: %d\n", nwords); - u.pv = buffer; - while (nwords > 0) - { - /* While the TX FIFO is not full and there are bytes left to send */ - - while ((ssp_getreg(priv, LPC43_SSP_SR_OFFSET) & SSP_SR_TNF) && nwords) - { - /* Fetch the data to send */ - - if (priv->nbits > 8) - { - data = (uint32_t)*u.p16++; - } - else - { - data = (uint32_t)*u.p8++; - } - - /* Send the data */ - - ssp_putreg(priv, LPC43_SSP_DR_OFFSET, data); - nwords--; - } - } - - /* Then discard all card responses until the RX & TX FIFOs are emptied. */ - - sspdbg("discarding\n"); - do - { - /* Is there anything in the RX fifo? */ - - sr = ssp_getreg(priv, LPC43_SSP_SR_OFFSET); - if ((sr & SSP_SR_RNE) != 0) - { - /* Yes.. Read and discard */ - - (void)ssp_getreg(priv, LPC43_SSP_DR_OFFSET); - } - - /* There is a race condition where TFE may go true just before - * RNE goes true and this loop terminates prematurely. The nasty little - * delay in the following solves that (it could probably be tuned - * to improve performance). - */ - - else if ((sr & SSP_SR_TFE) != 0) - { - up_udelay(100); - sr = ssp_getreg(priv, LPC43_SSP_SR_OFFSET); - } - } - while ((sr & SSP_SR_RNE) != 0 || (sr & SSP_SR_TFE) == 0); + return ssp_exchange(dev, buffer, NULL, nwords); } /**************************************************************************** @@ -636,54 +649,7 @@ static void ssp_sndblock(FAR struct spi_dev_s *dev, FAR const void *buffer, size static void ssp_recvblock(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords) { - FAR struct lpc43_sspdev_s *priv = (FAR struct lpc43_sspdev_s *)dev; - union - { - FAR uint8_t *p8; - FAR uint16_t *p16; - FAR void *pv; - } u; - uint32_t data; - uint32_t rxpending = 0; - - /* While there is remaining to be sent (and no synchronization error has occurred) */ - - sspdbg("nwords: %d\n", nwords); - u.pv = buffer; - while (nwords || rxpending) - { - /* Fill the transmit FIFO with 0xffff... - * Write 0xff to the data register while (1) the TX FIFO is - * not full, (2) we have not exceeded the depth of the TX FIFO, - * and (3) there are more bytes to be sent. - */ - - spivdbg("TX: rxpending: %d nwords: %d\n", rxpending, nwords); - while ((ssp_getreg(priv, LPC43_SSP_SR_OFFSET) & SSP_SR_TNF) && - (rxpending < LPC43_SSP_FIFOSZ) && nwords) - { - ssp_putreg(priv, LPC43_SSP_DR_OFFSET, 0xffff); - nwords--; - rxpending++; - } - - /* Now, read the RX data from the RX FIFO while the RX FIFO is not empty */ - - spivdbg("RX: rxpending: %d\n", rxpending); - while (ssp_getreg(priv, LPC43_SSP_SR_OFFSET) & SSP_SR_RNE) - { - data = (uint8_t)ssp_getreg(priv, LPC43_SSP_DR_OFFSET); - if (priv->nbits > 8) - { - *u.p16++ = (uint16_t)data; - } - else - { - *u.p8++ = (uint8_t)data; - } - rxpending--; - } - } + return ssp_exchange(dev, NULL, buffer, nwords); } /**************************************************************************** diff --git a/arch/arm/src/lpc43xx/lpc43_tickless_rit.c b/arch/arm/src/lpc43xx/lpc43_tickless_rit.c new file mode 100644 index 00000000000..e0f6cfb60bb --- /dev/null +++ b/arch/arm/src/lpc43xx/lpc43_tickless_rit.c @@ -0,0 +1,589 @@ +/**************************************************************************** + * arch/arm/src/lpc43/lpc43_rit.c + * + * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include "arch.h" +#include "time.h" +#include "chip/lpc43_rit.h" + +#ifdef CONFIG_SCHED_TICKLESS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define LPC43_TL_NANOS_IN_SEC 1000000000 + +#define LPC43_TL_NANOS_IN_TICK LPC43_TL_NANOS_IN_SEC/LPC43_CCLK + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static uint32_t to_reset = UINT32_MAX / 2; +static uint32_t to_reset_next = to_reset + UINT32_MAX / 4; +static uint32_t to_end = to_reset_next + UINT32_MAX / 8; + +static uint32_t reset_ticks = 1000; /* ticks to add to force a reset */ + +static uint64_t base = 0; /* time base */ + +static uint64_t alarm_time = 0; /* alarmTime to set on next interrupt, used if not already armed */ +static bool call = false; /* true if callback should be called on next interrupt */ +static bool armed = false; /* true if alarm is armed for next match */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* some timer HW functions */ + +static inline void lpc43_tl_set_counter (uint32_t value) +{ + putreg32 (value, LPC43_RIT_COUNTER); +} + +static inline uint32_t lpc43_tl_get_counter (void) +{ + return getreg32 (LPC43_RIT_COUNTER); +} + +static inline void lpc43_tl_set_compare (uint32_t value) +{ + putreg32 (value, LPC43_RIT_COMPVAL); +} + +static inline uint32_t lpc43_tl_get_compare (void) +{ + return getreg32 (LPC43_RIT_COMPVAL); +} + +static inline void lpc43_tl_set_mask (uint32_t value) +{ + putreg32 (value, LPC43_RIT_MASK); +} + +static inline uint32_t lpc43_tl_get_mask (void) +{ + return getreg32 (LPC43_RIT_MASK); +} + +static inline void lpc43_tl_set_ctrl_bit (uint32_t bit, bool value) +{ + uint32_t regval; + regval = getreg32 (LPC43_RIT_CTRL); + if (value) + { + regval |= bit; + } + else + { + regval &= ~bit; + } + putreg32 (regval, LPC43_RIT_CTRL); +} + +static inline bool lpc43_tl_get_ctrl_bit (uint32_t bit) +{ + uint32_t regval; + regval = getreg32 (LPC43_RIT_CTRL); + regval |= bit; + if (regval) + { + return true ; + } + else + { + return false ; + } +} + +static inline void lpc43_tl_set_reset_on_match (bool value) +{ + lpc43_tl_set_ctrl_bit (RIT_CTRL_ENCLR, value); +} + +static inline bool lpc43_tl_get_reset_on_match (void) +{ + return lpc43_tl_get_ctrl_bit (RIT_CTRL_ENCLR); +} + +static inline void lpc43_tl_set_enable (bool value) +{ + lpc43_tl_set_ctrl_bit (RIT_CTRL_EN, value); +} + +static inline bool lpc43_tl_get_enable (void) +{ + return lpc43_tl_get_ctrl_bit (RIT_CTRL_EN); +} + +static inline void lpc43_tl_clear_interrupt () +{ + lpc43_tl_set_ctrl_bit (RIT_CTRL_EN, true); +} + +static inline bool lpc43_tl_get_interrupt (void) +{ + return lpc43_tl_get_ctrl_bit (RIT_CTRL_INT); +} + +/* converters */ + +static inline uint64_t lpc43_tl_ts2tick ( FAR struct timespec *ts) +{ + return ((ts->tv_nsec + ts->tv_sec * LPC43_TL_NANOS_IN_SEC) / LPC43_TL_NANOS_IN_TICK); +} + +static inline void lpc43_tl_tick2ts (uint64_t tick, FAR struct timespec *ts) +{ + uint64_t nanos = tick * LPC43_TL_NANOS_IN_TICK; + + ts->tv_sec = nanos / LPC43_TL_NANOS_IN_SEC; + ts->tv_nsec = nanos % LPC43_TL_NANOS_IN_SEC; +} + +/* logic functions */ + +/* force interrupt, no reset possible */ + +static void lpc43_tl_force_int () +{ + lpc43_tl_set_reset_on_match (false); + lpc43_tl_set_mask (UINT32_MAX); +} + +/* init all vars */ + +static void lpc43_tl_init_timer_vars () +{ + alarm_time = 0; + call = false; + armed = false; +} + +/* calc reset_ticks and set compare to to_reset */ + +static void lpc43_tl_calibrate_init () +{ + uint32_t counter = lpc43_tl_get_counter (); + + uint32_t counter_after = lpc43_tl_get_counter (); + counter_after = to_reset + counter; + counter_after = counter_after - counter; + + /*shift to toReset*/ + + lpc43_tl_set_compare (counter_after); + + counter_after = lpc43_tl_get_counter (); + + reset_ticks = (counter_after - counter) * 2; + +} + +/* process current and set timer in default state */ + +static void lpc43_tl_save_timer () +{ + + /*process reset if any*/ + + uint32_t match = lpc43_tl_get_compare (); + + /*move to end, no resets during processing*/ + + lpc43_tl_set_compare (UINT32_MAX); + lpc43_tl_set_mask (0); + + if (lpc43_tl_get_interrupt ()) + { + if (lpc43_tl_get_reset_on_match ()) /*was reset ?*/ + { + base = base + match; + lpc43_tl_set_reset_on_match (false); + } + lpc43_tl_clear_interrupt (); + } +} + +/* assuming safe state, true if set, false - time is in the past */ + +static bool lpc43_tl_set_safe_compare (uint32_t time) +{ + if (time < to_reset) + { + lpc43_tl_set_reset_on_match (false); + } + else + { + lpc43_tl_set_reset_on_match (true); + } + + lpc43_tl_set_compare (time); + + //check if ok + bool reset = lpc43_tl_get_interrupt (); + uint32_t counter = lpc43_tl_get_counter (); + bool reset_after = lpc43_tl_get_interrupt (); + + if (reset != reset_after) + { + //was a reset get new counter + counter = lpc43_tl_get_counter (); + } + + if (reset_after || (!reset_after && time > counter)) + { + return true ; + } + else + { + return false ; + } + +} + +/* set_safe_compare in loop */ + +static void lpc43_tl_looped_forced_set_compare () +{ + uint32_t i = 1; + bool result = lpc43_tl_set_safe_compare (lpc43_tl_get_counter () + reset_ticks); /* like in calibrateInit */ + while (!result) + { + i++; + result = lpc43_tl_set_safe_compare (lpc43_tl_get_counter () + reset_ticks * i); + }; +} + +/* assuming safe state, true if set, false - time is in the past */ + +static bool lpc43_tl_set_calc_arm (uint32_t curr, uint32_t to_set, bool arm) +{ + + uint32_t calcTime; + + if (curr < to_reset_next) + { + calcTime = min (to_reset_next, to_set); + } + else + { + if (curr < to_end) + { + calcTime = min (lpc43_tl_get_counter () + reset_ticks, to_set); + } + else + { + lpc43_tl_looped_forced_set_compare (); + return true ; + } + } + + bool set = lpc43_tl_set_safe_compare (calcTime); + + if (arm && set && (calcTime == to_set)) + { + armed = true; + } + + return set; +} + +/* try to set compare for normal operation */ + +static void lpc43_tl_set_default_compare () +{ + bool result = lpc43_tl_set_calc_arm (lpc43_tl_get_counter (), UINT32_MAX, false); + if (!result) + { + result = lpc43_tl_set_calc_arm (lpc43_tl_get_counter (), UINT32_MAX, false); + if (!result) + { + lpc43_tl_looped_forced_set_compare (); + } + } + +} + +static inline void lpc43_tl_alarm() +{ + lpc43_tl_init_timer_vars (); + lpc43_tl_set_default_compare (); + + struct timespec ts; + + up_timer_gettime(&ts); + + sched_alarm_expiration(&ts); +} + +/* interrupt handler */ + +static int lpc43_tl_isr (int irq, FAR void *context) +{ + irqstate_t flags; + flags = irqsave (); + + if (call) + { + /* forced call case */ + lpc43_tl_set_mask (false); + + lpc43_tl_clear_interrupt (); + + lpc43_tl_alarm(); + + } + else + { + lpc43_tl_save_timer (); + + if (armed) + { + lpc43_tl_alarm(); + } + else + { + if (alarm_time > 0) + { + uint64_t toSet = alarm_time - base; + uint32_t curr = lpc43_tl_get_counter (); + + if (toSet > curr) + { + if (toSet > to_end) + { + lpc43_tl_set_default_compare (); + } + else + { + bool set = lpc43_tl_set_calc_arm (curr, toSet, true); + if (!set) + { + lpc43_tl_alarm(); + } + } + } + else + { + lpc43_tl_alarm(); + } + } + else + { + lpc43_tl_set_default_compare (); + } + } + } + + irqrestore (flags); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void up_timer_initialize (void) +{ + irqstate_t flags; + flags = irqsave (); + + lpc43_tl_set_enable (false); + + lpc43_tl_set_compare (UINT32_MAX); + lpc43_tl_set_counter (0); + lpc43_tl_set_mask (0); + + lpc43_tl_set_reset_on_match (false); + lpc43_tl_clear_interrupt (); + + irq_attach (LPC43M4_IRQ_RITIMER, lpc43_tl_isr); + up_enable_irq (LPC43M4_IRQ_RITIMER); + + lpc43_tl_init_timer_vars (); + + lpc43_tl_set_enable (true); + + lpc43_tl_calibrate_init (); + + irqrestore (flags); +} + +int up_timer_gettime (FAR struct timespec *ts) +{ + irqstate_t flags; + flags = irqsave (); + + uint32_t time; + /* no reg changes, only processing */ + + /* order of calls is important, reset can come during processing */ + + bool reset = lpc43_tl_get_interrupt (); + uint32_t count = lpc43_tl_get_counter (); + + /* not processed reset can exist */ + if (lpc43_tl_get_reset_on_match ()) + { + bool resetAfter = lpc43_tl_get_interrupt (); + + if (reset != resetAfter) /* was a reset get new counter */ + { + count = lpc43_tl_get_counter (); + } + + if (resetAfter) + { + time = base + count + lpc43_tl_get_compare (); + } + else + { + time = base + count; + } + } + else + { + time = base + count; + } + + lpc43_tl_tick2ts (time, ts); + + irqrestore (flags); + return OK; +} + +# ifdef CONFIG_SCHED_TICKLESS_ALARM +int up_alarm_cancel (FAR struct timespec *ts) +{ + irqstate_t flags; + flags = irqsave (); + + /*no reg changes, only variables logic*/ + + up_timer_gettime (ts); + + /* let default setup will be done in interrupt handler or setAlarm */ + lpc43_tl_init_timer_vars (); + + irqrestore (flags); + return OK; +} + +int up_alarm_start (FAR const struct timespec *ts) +{ + irqstate_t flags; + flags = irqsave (); + + lpc43_tl_init_timer_vars (); + + lpc43_tl_save_timer (); + + //----calc time to set + + uint64_t time = lpc43_tl_ts2tick (ts); + uint64_t toSet = time - base; + uint32_t curr = lpc43_tl_get_counter (); + + if (toSet > curr) + { + alarm_time = time; + + if (toSet > to_end) /* future set */ + { + lpc43_tl_set_default_compare (); + } + else + { + bool set = lpc43_tl_set_calc_arm (curr, toSet, true); + if (!set) + { + /* signal call, force interrupt handler */ + + call = true; + lpc43_tl_force_int (); + } + + } + + } + else + { + /* signal call, force interrupt handler */ + + call = true; + lpc43_tl_force_int (); + } + + irqrestore (flags); + + return OK; +} + +# else + +int up_timer_cancel(FAR struct timespec *ts) + { + /*TODO*/ + irqstate_t flags; + flags = irqsave (); + + irqrestore (flags); + return OK; + } + +int up_timer_start(FAR const struct timespec *ts) + { + /*TODO*/ + irqstate_t flags; + flags = irqsave (); + + irqrestore (flags); + return OK; + } + +# endif /* CONFIG_SCHED_TICKLESS_ALARM */ + +#endif /* CONFIG_SCHED_TICKLESS */