blob: 3bbede3927fbd4254d87f0526a81655f70d05d86 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/lpc43xx/lpc43_rit.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* WARNING -- This code is currently *NOT* thread safe. No checking is done
* that one alarm call might not override an already enabled one.
* You might be able to handle this with some kind of cascading
* scheme where alarm receives the next value in a list of alarms
* all in the future.
*
* Brandon Warhurst
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <arch/board/board.h>
#include "arm_internal.h"
#include "hardware/lpc43_rit.h"
#include "lpc43_rit.h"
#ifdef CONFIG_LPC43_RIT
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Set the timer resolution at 1uS
*
* This is a battery waster, but I need this kind of resolution for 1-wire
* protocols... Until I can find a better way to implement them.
*
* This should probable be Kconfig material.
*/
#define RIT_TIMER_RESOLUTION CONFIG_LPC43_RIT_RES
/****************************************************************************
* Private Data
****************************************************************************/
static double sec_per_tick;
static uint64_t g_internal_timer;
static uint64_t g_alarm;
struct timespec g_ts;
/****************************************************************************
* Private Functions
****************************************************************************/
static int lpc43_rit_isr(int irq, void *context, void *arg)
{
irqstate_t flags;
flags = enter_critical_section();
putreg32(RIT_CTRL_INT, LPC43_RIT_CTRL);
g_internal_timer += (uint64_t)RIT_TIMER_RESOLUTION;
if (g_alarm > 0 && g_internal_timer >= g_alarm)
{
/* handle expired alarm */
g_ts.tv_sec = (uint32_t)(g_internal_timer / 1000000000);
g_ts.tv_nsec = (uint32_t)(g_internal_timer % 1000000000);
nxsched_alarm_expiration(&g_ts);
}
leave_critical_section(flags);
return OK;
}
static inline void lpc43_load_rit_timer(uint32_t value)
{
putreg32(value, LPC43_RIT_COUNTER);
}
static inline void lpc43_load_rit_compare(uint32_t value)
{
putreg32(value, LPC43_RIT_COMPVAL);
}
static inline void lpc43_set_rit_timer_mask(uint32_t value)
{
putreg32(value, LPC43_RIT_MASK);
}
static inline uint32_t lpc43_read_rit_timer(void)
{
return getreg32(LPC43_RIT_COUNTER);
}
static inline void lpc43_rit_timer_start(void)
{
uint32_t regval;
regval = getreg32(LPC43_RIT_CTRL);
/* Since a cycle timer is in use here, the timer clear is enabled. If
* using the timer directly, this likely should not be used. Also, clear
* the interrupt flag.
*/
regval |= RIT_CTRL_INT | RIT_CTRL_EN | RIT_CTRL_ENCLR;
putreg32(regval, LPC43_RIT_CTRL);
}
static inline void lpc43_rit_timer_stop(void)
{
uint32_t regval;
regval = getreg32(LPC43_RIT_CTRL);
regval &= ~RIT_CTRL_EN;
putreg32(regval, LPC43_RIT_CTRL);
}
/****************************************************************************
* Public Functions
****************************************************************************/
void up_timer_initialize(void)
{
uint32_t ticks_per_int;
uint32_t mask_bits = 0;
uint32_t mask_test = 0x80000000;
lpc43_rit_timer_stop();
lpc43_load_rit_timer(0);
g_internal_timer = 0;
/* Set up the IRQ here */
irq_attach(LPC43M4_IRQ_RITIMER, lpc43_rit_isr, NULL);
/* Compute how many seconds per tick we have on the main clock. If it is
* 204MHz for example, then there should be about 4.90ns per tick
*/
sec_per_tick = (double)1.0 / (double)LPC43_CCLK;
/* Given an RIT_TIMER_RESOLUTION, compute how many ticks it will take to
* reach that resolution. For example, if we wanted a 1/4uS timer
* resolution, that would be 250ns resolution. The timer is an integer
* value, although maybe this should change, but that means
* 250/1000000000*0.00000000490 = 51.02 ticks or 51 ticks, roughly.
* We round up by 1 tick.
*/
ticks_per_int = RIT_TIMER_RESOLUTION / (1000000000 * sec_per_tick) + 1;
/* Now we need to compute the mask that will let us set up to generate an
* interrupt every 1/4uS. This isn't "tickless" per-se, and probably
* should be implemented differently, however it allows me to create a
* 64 bit nanosecond timer than can "free-run" by being updated every
* RIT_TIMER_RESOLUTION cycles. I would have implemented the better
* approach, but I didn't have a good way to determine how to manage a
* 32 bit ns timer. Every 21 seconds the thing rolls over@ 204MHz, so
* you'd have to set up the compare interrupt to handle the roll over. It
* WOULD be fewer interrupts, but it seemed to make things more
* complicated. When I have a better idea, I'll change this.
*/
while (!((mask_test >> mask_bits) & ticks_per_int))
{
mask_bits++;
}
tmrinfo("mask_bits = %d, mask = %X, ticks_per_int = %d\n",
mask_bits, (0xffffffff << (32 - mask_bits)), ticks_per_int);
/* Set the mask and compare value so we get interrupts every
* RIT_TIMER_RESOLUTION cycles.
*/
lpc43_set_rit_timer_mask((0xffffffff << (32 - mask_bits)));
lpc43_load_rit_compare(ticks_per_int);
/* Turn on the IRQ */
up_enable_irq(LPC43M4_IRQ_RITIMER);
/* Start the timer */
lpc43_rit_timer_start();
}
int up_timer_gettime(struct timespec *ts)
{
ts->tv_sec = (uint32_t)(g_internal_timer / 1000000000);
ts->tv_nsec = (uint32_t)(g_internal_timer % 1000000000);
return OK;
}
int up_alarm_cancel(struct timespec *ts)
{
ts->tv_sec = (uint32_t)(g_internal_timer / 1000000000);
ts->tv_nsec = (uint32_t)(g_internal_timer % 1000000000);
g_alarm = 0;
return OK;
}
int up_alarm_start(const struct timespec *ts)
{
/* According to the docs, this version should expect to receive the time
* in the future when the alarm should expire. So that's the way it's
* coded.
*/
g_alarm = (uint64_t)ts->tv_sec * (uint64_t)1000000000 +
(uint64_t)ts->tv_nsec;
return OK;
}
int up_timer_cancel(struct timespec *ts)
{
/* Currently this is just an alarm and both are implemented. This is *NOT*
* how it is supposed to be and will be corrected, but for now, this is a
* simple way to implement both.
* FIXME
*/
return up_alarm_cancel(ts);
}
int up_timer_start(const struct timespec *ts)
{
/* According to the docs, this version should basically compute the time
* in the future when an alarm should go off. That is the way it could
* potentially be implemented, so that's the way I did it.
*/
g_alarm = g_internal_timer;
g_alarm += (uint64_t)ts->tv_sec * (uint64_t)1000000000 +
(uint64_t)ts->tv_nsec;
return OK;
}
#endif /* CONFIG_LPC43_RIT */