blob: 685de81937110cc3820882958836c83febff917e [file] [log] [blame]
/*
* Copyright (c) 2019-2020 Arm Limited
*
* 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 systimer_armv8-m_drv.c
*
* \brief Driver for Armv8-M System Timer
*
*/
#include "systimer_armv8-m_drv.h"
/** Setter bit manipulation macro */
#define SET_BIT(WORD, BIT_INDEX) ((WORD) |= (1u << (BIT_INDEX)))
/** Clearing bit manipulation macro */
#define CLR_BIT(WORD, BIT_INDEX) ((WORD) &= ~(1u << (BIT_INDEX)))
/** Getter bit manipulation macro */
#define GET_BIT(WORD, BIT_INDEX) (bool) (((WORD) & (1u << (BIT_INDEX))))
/** Getter bit-field manipulation macro */
#define GET_BIT_FIELD(WORD, BIT_MASK, BIT_OFFSET) ((WORD & BIT_MASK) >> BIT_OFFSET)
/** Bit mask for given width bit-field manipulation macro */
#define BITMASK(width) ((1u << (width)) - 1)
/**
* \brief CNTBase Register map structure
*/
struct cnt_base_reg_map_t
{
volatile const uint32_t cntpct_low;
/*!< Offset: 0x000 (RO) Current Physical Counter Value [31:0] */
volatile const uint32_t cntpct_high;
/*!< Offset: 0x004 (RO) Current Physical Counter Value [63:32] */
volatile const uint32_t reserved0[2];
/*!< Offset: 0x008-0x0C Reserved */
volatile uint32_t cntfrq;
/*!< Offset: 0x010 (R/W) Counter Frequency register in Hz */
volatile const uint32_t reserved1[3];
/*!< Offset: 0x014-0x01C Reserved */
volatile uint32_t cntp_cval_low;
/*!< Offset: 0x020 (R/W) Timer Compare Value register [31:0] */
volatile uint32_t cntp_cval_high;
/*!< Offset: 0x024 (R/W) Timer Compare Value register [63:32] */
volatile uint32_t cntp_tval;
/*!< Offset: 0x028 (R/W) Timer Value register */
volatile uint32_t cntp_ctl;
/*!< Offset: 0x02C (R/W) Timer Control register */
volatile const uint32_t reserved2[4];
/*!< Offset: 0x030-0x03C Reserved */
volatile const uint32_t cntp_aival_low;
/*!< Offset: 0x040 (RO) Auto Increment Value register [31:0]*/
volatile const uint32_t cntp_aival_high;
/*!< Offset: 0x044 (RO) Auto Increment Value register [63:32]*/
volatile uint32_t cntp_aival_reload;
/*!< Offset: 0x048 (R/W) Auto Increment Value Reload register [63:32]*/
volatile uint32_t cntp_aival_ctl;
/*!< Offset: 0x04C (R/W) Auto Increment Control register */
volatile const uint32_t cntp_cfg;
/*!< Offset: 0x050 (RO) Timer Configuration register */
volatile const uint32_t reserved3[991];
/*!< Offset: 0x054-0xFCC Reserved */
volatile const uint32_t cntp_pid4;
/*!< Offset: 0xFD0 (RO) Peripheral ID Register */
volatile const uint32_t reserved4[3];
/*!< Offset: 0xFD4-0xFDC Reserved (RAZWI) */
volatile const uint32_t cntp_pid0;
/*!< Offset: 0xFE0 (RO) Peripheral ID Register */
volatile const uint32_t cntp_pid1;
/*!< Offset: 0xFE4 (RO) Peripheral ID Register */
volatile const uint32_t cntp_pid2;
/*!< Offset: 0xFE8 (RO) Peripheral ID Register */
volatile const uint32_t cntp_pid3;
/*!< Offset: 0xFEC (RO) Peripheral ID Register */
volatile const uint32_t cntp_cid0;
/*!< Offset: 0xFF0 (RO) Component ID Register */
volatile const uint32_t cntp_cid1;
/*!< Offset: 0xFF4 (RO) Component ID Register */
volatile const uint32_t cntp_cid2;
/*!< Offset: 0xFF8 (RO) Component ID Register */
volatile const uint32_t cntp_cid3;
/*!< Offset: 0xFFC (RO) Component ID Register */
};
/**
* \brief Timer Control Register bit fields
*/
#define SYSCTIMER_ARMV8M_CNTP_CTL_EN_OFF 0u
/*!< Timer Control Register Enable Counter bit field offset */
#define SYSCTIMER_ARMV8M_CNTP_CTL_IMASK_OFF 1u
/*!< Timer Control Register Interrupt Mask bit field offset */
#define SYSCTIMER_ARMV8M_CNTP_CTL_ISTATUS_OFF 2u
/*!< Timer Control Register Interrupt Status bit field offset */
/**
* \brief Timer AutoInc Control Register bit fields
*/
#define SYSCTIMER_ARMV8M_CNTP_AIVAL_CTL_EN_OFF 0u
/*!< Timer Control Register Enable Counter bit field offset */
#define SYSCTIMER_ARMV8M_CNTP_AIVAL_CTL_IRQ_CLR_OFF 1u
/*!< Timer Control Register Interrupt clear bit field offset */
/**
* \brief Timer AutoInc Config Register bit fields
*/
#define SYSCTIMER_ARMV8M_CNTP_CFG_CTL_AUTOINC_OFF 0u
/*!< Timer Control Register AutoInc is implemented bit field offset */
void systimer_armv8_m_init(struct systimer_armv8_m_dev_t * dev)
{
if (dev->data->is_initialized == false)
{
systimer_armv8_m_disable_interrupt(dev);
systimer_armv8_m_disable_autoinc(dev);
systimer_armv8_m_set_counter_freq(dev, dev->cfg->default_freq_hz);
systimer_armv8_m_enable_timer(dev);
dev->data->is_initialized = true;
}
}
void systimer_armv8_m_uninit(struct systimer_armv8_m_dev_t * dev)
{
if (dev->data->is_initialized == true)
{
systimer_armv8_m_disable_interrupt(dev);
systimer_armv8_m_disable_autoinc(dev);
systimer_armv8_m_disable_timer(dev);
dev->data->is_initialized = false;
}
}
uint64_t systimer_armv8_m_get_counter_value(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
uint32_t high = 0;
uint32_t low = 0;
uint32_t high_prev = 0;
uint64_t value = 0;
/* Make sure the 64-bit read will be atomic to avoid overflow between
* the low and high registers read
*/
high = p_cnt->cntpct_high;
do
{
high_prev = high;
low = p_cnt->cntpct_low;
high = p_cnt->cntpct_high;
} while (high != high_prev);
value = low | (((uint64_t) high) << SYSTIMER_ARMV8_M_REGISTER_BIT_WIDTH);
return value;
}
void systimer_armv8_m_set_compare_value(struct systimer_armv8_m_dev_t * dev, uint64_t value)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
p_cnt->cntp_cval_low = value & UINT32_MAX;
p_cnt->cntp_cval_high = value >> SYSTIMER_ARMV8_M_REGISTER_BIT_WIDTH;
}
uint64_t systimer_armv8_m_get_compare_value(struct systimer_armv8_m_dev_t * dev)
{
uint64_t value = 0;
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
value = p_cnt->cntp_cval_low | (((uint64_t) p_cnt->cntp_cval_high) << SYSTIMER_ARMV8_M_REGISTER_BIT_WIDTH);
return value;
}
void systimer_armv8_m_set_counter_freq(struct systimer_armv8_m_dev_t * dev, uint32_t value)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
p_cnt->cntfrq = value;
}
uint32_t systimer_armv8_m_get_counter_freq(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
return p_cnt->cntfrq;
}
void systimer_armv8_m_set_timer_value(struct systimer_armv8_m_dev_t * dev, uint32_t value)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
p_cnt->cntp_tval = value;
}
uint32_t systimer_armv8_m_get_timer_value(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
return p_cnt->cntp_tval;
}
void systimer_armv8_m_enable_timer(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
SET_BIT(p_cnt->cntp_ctl, SYSCTIMER_ARMV8M_CNTP_CTL_EN_OFF);
}
void systimer_armv8_m_disable_timer(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntp_ctl, SYSCTIMER_ARMV8M_CNTP_CTL_EN_OFF);
}
bool systimer_armv8_m_is_timer_enabled(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntp_ctl, SYSCTIMER_ARMV8M_CNTP_CTL_EN_OFF);
}
void systimer_armv8_m_enable_interrupt(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
/* The bit is masking interrupt, so it should be inverted. */
CLR_BIT(p_cnt->cntp_ctl, SYSCTIMER_ARMV8M_CNTP_CTL_IMASK_OFF);
}
void systimer_armv8_m_disable_interrupt(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
/* The bit is masking interrupt, so it should be inverted. */
SET_BIT(p_cnt->cntp_ctl, SYSCTIMER_ARMV8M_CNTP_CTL_IMASK_OFF);
}
bool systimer_armv8_m_is_interrupt_enabled(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
/* The bit is masking interrupt, so it should be inverted. */
return !GET_BIT(p_cnt->cntp_ctl, SYSCTIMER_ARMV8M_CNTP_CTL_IMASK_OFF);
}
bool systimer_armv8_m_is_interrupt_asserted(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntp_ctl, SYSCTIMER_ARMV8M_CNTP_CTL_ISTATUS_OFF);
}
uint64_t systimer_armv8_m_get_autoinc_value(struct systimer_armv8_m_dev_t * dev)
{
uint64_t value = 0;
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
value = p_cnt->cntp_aival_low | (((uint64_t) p_cnt->cntp_aival_high) << SYSTIMER_ARMV8_M_REGISTER_BIT_WIDTH);
return value;
}
void systimer_armv8_m_set_autoinc_reload(struct systimer_armv8_m_dev_t * dev, uint32_t value)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
p_cnt->cntp_aival_reload = value;
}
uint32_t systimer_armv8_m_get_autoinc_reload(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
return p_cnt->cntp_aival_reload;
}
void systimer_armv8_m_enable_autoinc(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
SET_BIT(p_cnt->cntp_aival_ctl, SYSCTIMER_ARMV8M_CNTP_AIVAL_CTL_EN_OFF);
}
void systimer_armv8_m_disable_autoinc(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntp_aival_ctl, SYSCTIMER_ARMV8M_CNTP_AIVAL_CTL_EN_OFF);
}
bool systimer_armv8_m_is_autoinc_enabled(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntp_aival_ctl, SYSCTIMER_ARMV8M_CNTP_AIVAL_CTL_EN_OFF);
}
void systimer_armv8_m_clear_autoinc_interrupt(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntp_aival_ctl, SYSCTIMER_ARMV8M_CNTP_AIVAL_CTL_IRQ_CLR_OFF);
}
bool systimer_armv8_m_is_autoinc_implemented(struct systimer_armv8_m_dev_t * dev)
{
struct cnt_base_reg_map_t * p_cnt = (struct cnt_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntp_cfg, SYSCTIMER_ARMV8M_CNTP_CFG_CTL_AUTOINC_OFF);
}