blob: 0155bdc0d883575dedd5c0fcd63880db0b3d48e2 [file] [log] [blame]
/*
* Copyright (c) 2019 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 syscounter_armv8-m_cntrl_drv.c
*
* \brief Driver for Armv8-M System Counter Control, covering CNTControlBase
* Frame
*
* This System Counter is a 64-bit up-counter, generating the physical
* count for System Timer.
*
* Main features:
* - Enabled/disable and Set/Get the 64-bit upcounter
* - 2 scaling register for the 2 clock sources
* - These registers are used to pre-program the scaling values so
* that when hardware based clock switching is implemented there is no
* need to program the scaling increment value each time when clock is
* switched.
* - When counter scaling is enabled, ScaleVal is the amount added to the
* Counter Count Value for every period of the counter as determined
* by 1/Frequency from the current operating frequency of the system
* counter (the “counter tick”).
* - ScaleVal is expressed as an unsigned fixed-point number with
* a 8 bit integer value and a 24-bit fractional value
* - Interrupt for error detection
* There are 2 possible reasons for error notification generation from
* the Counter:
* 1. Security attribute mismatch between register access and security
* attribute of the CONTROL frame
* 2. Address decode error within a given frame
*
*/
#include "syscounter_armv8-m_cntrl_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))))
/** Clear-and-Set bit manipulation macro */
#define ASSIGN_BIT(WORD, BIT_INDEX, VALUE) (WORD = ((WORD & ~(1U << (BIT_INDEX))) | (VALUE << (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 CNTControlBase Register map structure
*/
struct cnt_control_base_reg_map_t
{
volatile uint32_t cntcr;
/*!< Offset: 0x000 (R/W) Counter Control Register */
volatile const uint32_t cntsr;
/*!< Offset: 0x004 (RO) Counter Status Register */
volatile uint32_t cntcv_low;
/*!< Offset: 0x008 (R/W) Counter Count Value [31:0] Register */
volatile uint32_t cntcv_high;
/*!< Offset: 0x00C (R/W) Counter Count Value [63:32] Register */
volatile uint32_t cntscr;
/*!< Offset: 0x010 (R/W) Counter Scale Register
* Aliased with CNTSCR0, meaning that either addresses of CNTSCR and
* CNTSCR0 will physically access a single register
*/
volatile const uint32_t reserved0[2];
/*!< Offset: 0x014-0x018 Reserved (RAZWI) */
volatile const uint32_t cntid;
/*!< Offset: 0x01C (RO) Counter ID Register */
volatile const uint32_t reserved1[40];
/*!< Offset: 0x020-0x0BC Reserved (RAZWI) */
volatile const uint32_t reserved2[4];
/*!< Offset: 0x0C0-0x0CC Reserved (RAZWI) */
volatile uint32_t cntscr0;
/*!< Offset: 0x0D0 (R/W) Counter Scale Register 0 */
volatile uint32_t cntscr1;
/*!< Offset: 0x0D4 (R/W) Counter Scale Register 1 */
volatile const uint32_t reserved3[958];
/*!< Offset: 0x0D8-0xFCC Reserved (RAZWI) */
volatile const uint32_t cntpidr4;
/*!< Offset: 0xFD0 (RO) Peripheral ID Register */
volatile const uint32_t reserved4[3];
/*!< Offset: 0xFD4-0xFDC Reserved (RAZWI) */
volatile const uint32_t cntpidr0;
/*!< Offset: 0xFE0 (RO) Peripheral ID Register */
volatile const uint32_t cntpidr1;
/*!< Offset: 0xFE4 (RO) Peripheral ID Register */
volatile const uint32_t cntpidr2;
/*!< Offset: 0xFE8 (RO) Peripheral ID Register */
volatile const uint32_t cntpidr3;
/*!< Offset: 0xFEC (RO) Peripheral ID Register */
volatile const uint32_t cntcidr0;
/*!< Offset: 0xFF0 (RO) Component ID Register */
volatile const uint32_t cntcidr1;
/*!< Offset: 0xFF4 (RO) Component ID Register */
volatile const uint32_t cntcidr2;
/*!< Offset: 0xFF8 (RO) Component ID Register */
volatile const uint32_t cntcidr3;
/*!< Offset: 0xFFC (RO) Component ID Register */
};
/**
* \brief Counter Control Register bit fields
*/
#define SYSCOUNTER_ARMV8M_CNTCR_EN_OFF 0u
/*!< Counter Control Register Enable Counter bit field offset */
#define SYSCOUNTER_ARMV8M_CNTCR_HDBG_OFF 1u
/*!< Counter Control Register Halt On Debug bit field offset */
#define SYSCOUNTER_ARMV8M_CNTCR_SCEN_OFF 2u
/*!< Counter Control Register Scale enable bit field offset */
#define SYSCOUNTER_ARMV8M_CNTCR_INTRMASK_OFF 3u
/*!< Counter Control Register Interrupt mask bit field offset */
#define SYSCOUNTER_ARMV8M_CNTCR_PSLVERRDIS_OFF 4u
/*!< Counter Control Register PSLVERR disable bit field offset */
#define SYSCOUNTER_ARMV8M_CNTCR_INTRCLR_OFF 5u
/*!< Counter Control Register Interrupt Clear bit field offset */
/**
* \brief Counter Status Register bit fields
*/
#define SYSCOUNTER_ARMV8M_CNTSR_DBGH_OFF 1u
/*!< Counter Status Register Halt-on-Debug bit field offset */
/**
* \brief Counter ID Register bit fields
*/
#define SYSCOUNTER_ARMV8M_CNTID_CNTSC_OFF 0u
/*!< Counter ID Register Counter Scaling is implemented bit field offset */
#define SYSCOUNTER_ARMV8M_CNTID_CNTCS_OFF 16u
/*!< Counter ID Register Clock switching is implemented bit field offset */
/*! Counter ID Register Clock source */
#define SYSCOUNTER_ARMV8M_CNTID_CNTSELCLK_OFF 17u
#define SYSCOUNTER_ARMV8M_CNTID_CNTSELCLK_WIDTH 2u
#define SYSCOUNTER_ARMV8M_CNTID_CNTSELCLK_MASK \
(BITMASK(SYSCOUNTER_ARMV8M_CNTID_CNTSELCLK_WIDTH) << SYSCOUNTER_ARMV8M_CNTID_CNTSELCLK_OFF)
#define SYSCOUNTER_ARMV8M_CNTID_CNTSCR_OVR_OFF 19u
/*!< Counter ID Register Override counter enable condition for
* writing to CNTSCR registers bit offset
*/
enum syscounter_armv8_m_cntrl_error_t syscounter_armv8_m_cntrl_init(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
enum syscounter_armv8_m_cntrl_error_t result = SYSCOUNTER_ARMV8_M_ERR_NONE;
if (dev->data->is_initialized == false)
{
syscounter_armv8_m_cntrl_disable_counter(dev);
if (syscounter_armv8_m_cntrl_is_counter_scaling_implemented(dev))
{
result = syscounter_armv8_m_cntrl_set_counter_scale_value(dev, SYSCOUNTER_ARMV8_M_SCALE_NR_0, dev->cfg->scale0);
if (result != SYSCOUNTER_ARMV8_M_ERR_NONE)
{
return result;
}
result = syscounter_armv8_m_cntrl_set_counter_scale_value(dev, SYSCOUNTER_ARMV8_M_SCALE_NR_1, dev->cfg->scale1);
if (result != SYSCOUNTER_ARMV8_M_ERR_NONE)
{
return result;
}
}
syscounter_armv8_m_cntrl_set_counter_value(dev, SYSCOUNTER_ARMV8_M_DEFAULT_INIT_CNT_VAL);
syscounter_armv8_m_cntrl_disable_interrupt(dev);
syscounter_armv8_m_cntrl_disable_scale(dev);
syscounter_armv8_m_cntrl_enable_counter(dev);
dev->data->is_initialized = true;
}
return SYSCOUNTER_ARMV8_M_ERR_NONE;
}
void syscounter_armv8_m_cntrl_uninit(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
if (dev->data->is_initialized == true)
{
syscounter_armv8_m_cntrl_disable_counter(dev);
syscounter_armv8_m_cntrl_disable_interrupt(dev);
syscounter_armv8_m_cntrl_set_counter_value(dev, SYSCOUNTER_ARMV8_M_DEFAULT_INIT_CNT_VAL);
dev->data->is_initialized = false;
}
}
void syscounter_armv8_m_cntrl_enable_counter(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
SET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_EN_OFF);
}
void syscounter_armv8_m_cntrl_disable_counter(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_EN_OFF);
}
bool syscounter_armv8_m_cntrl_is_counter_enabled(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_EN_OFF);
}
void syscounter_armv8_m_cntrl_enable_halt_on_debug(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
SET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_HDBG_OFF);
}
void syscounter_armv8_m_cntrl_disable_halt_on_debug(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_HDBG_OFF);
}
bool syscounter_armv8_m_cntrl_is_halt_on_debug_enabled(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_HDBG_OFF);
}
void syscounter_armv8_m_cntrl_enable_scale(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
SET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_SCEN_OFF);
}
void syscounter_armv8_m_cntrl_disable_scale(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_SCEN_OFF);
}
bool syscounter_armv8_m_cntrl_is_scale_enabled(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_SCEN_OFF);
}
void syscounter_armv8_m_cntrl_enable_interrupt(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
SET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_INTRMASK_OFF);
}
void syscounter_armv8_m_cntrl_disable_interrupt(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_INTRMASK_OFF);
}
bool syscounter_armv8_m_cntrl_is_interrupt_enabled(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_INTRMASK_OFF);
}
void syscounter_armv8_m_cntrl_enable_pslverr(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
SET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_PSLVERRDIS_OFF);
}
void syscounter_armv8_m_cntrl_disable_pslverr(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_PSLVERRDIS_OFF);
}
bool syscounter_armv8_m_cntrl_is_pslverr_enabled(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_PSLVERRDIS_OFF);
}
void syscounter_armv8_m_cntrl_clear_interrupt(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
CLR_BIT(p_cnt->cntcr, SYSCOUNTER_ARMV8M_CNTCR_INTRCLR_OFF);
}
bool syscounter_armv8_m_cntrl_is_counter_halted_on_debug(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntsr, SYSCOUNTER_ARMV8M_CNTSR_DBGH_OFF);
}
uint64_t syscounter_armv8_m_cntrl_get_counter_value(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_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->cntcv_high;
do
{
high_prev = high;
low = p_cnt->cntcv_low;
high = p_cnt->cntcv_high;
} while (high != high_prev);
value = low | (((uint64_t) high) << SYSCOUNTER_ARMV8_M_CNTRL_REGISTER_BIT_WIDTH);
return value;
}
void syscounter_armv8_m_cntrl_set_counter_value(struct syscounter_armv8_m_cntrl_dev_t * dev, uint64_t value)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
p_cnt->cntcv_low = value & UINT32_MAX;
p_cnt->cntcv_high = value >> SYSCOUNTER_ARMV8_M_CNTRL_REGISTER_BIT_WIDTH;
}
bool syscounter_armv8_m_cntrl_is_counter_scaling_implemented(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntid, SYSCOUNTER_ARMV8M_CNTID_CNTSC_OFF);
}
bool syscounter_armv8_m_cntrl_is_clock_switching_implemented(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return GET_BIT(p_cnt->cntid, SYSCOUNTER_ARMV8M_CNTID_CNTCS_OFF);
}
enum syscounter_armv8_m_cntrl_selclk_t syscounter_armv8_m_cntrl_get_clock_source(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return (enum syscounter_armv8_m_cntrl_selclk_t) GET_BIT_FIELD(p_cnt->cntid, SYSCOUNTER_ARMV8M_CNTID_CNTSELCLK_MASK,
SYSCOUNTER_ARMV8M_CNTID_CNTSELCLK_OFF);
}
enum syscounter_armv8_m_cntrl_cntscr_ovr_t syscounter_armv8_m_cntrl_get_override_cntscr(struct syscounter_armv8_m_cntrl_dev_t * dev)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
return (enum syscounter_armv8_m_cntrl_cntscr_ovr_t) GET_BIT(p_cnt->cntid, SYSCOUNTER_ARMV8M_CNTID_CNTSCR_OVR_OFF);
}
enum syscounter_armv8_m_cntrl_error_t
syscounter_armv8_m_cntrl_get_counter_scale_value(struct syscounter_armv8_m_cntrl_dev_t * dev,
enum syscounter_armv8_m_cntrl_scale_nr_t nr,
struct syscounter_armv8_m_cntrl_scale_val_t * val)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
switch (nr)
{
case SYSCOUNTER_ARMV8_M_SCALE_NR_0:
val->integer = p_cnt->cntscr0 >> SYSCOUNTER_ARMV8_M_SCALE_VAL_INT_OFF;
val->fixed_point_fraction = p_cnt->cntscr0 & SYSCOUNTER_ARMV8_M_SCALE_VAL_FRACT_MAX;
break;
case SYSCOUNTER_ARMV8_M_SCALE_NR_1:
val->integer = p_cnt->cntscr1 >> SYSCOUNTER_ARMV8_M_SCALE_VAL_INT_OFF;
val->fixed_point_fraction = p_cnt->cntscr1 & SYSCOUNTER_ARMV8_M_SCALE_VAL_FRACT_MAX;
break;
default:
val->integer = 0;
val->fixed_point_fraction = 0;
return SYSCOUNTER_ARMV8_M_ERR_INVALID_ARG;
}
return SYSCOUNTER_ARMV8_M_ERR_NONE;
}
enum syscounter_armv8_m_cntrl_error_t
syscounter_armv8_m_cntrl_set_counter_scale_value(struct syscounter_armv8_m_cntrl_dev_t * dev,
enum syscounter_armv8_m_cntrl_scale_nr_t nr,
struct syscounter_armv8_m_cntrl_scale_val_t val)
{
struct cnt_control_base_reg_map_t * p_cnt = (struct cnt_control_base_reg_map_t *) dev->cfg->base;
uint32_t reg_val = 0;
if ((syscounter_armv8_m_cntrl_get_override_cntscr(dev) == SYSCOUNTER_ARMV8_M_CNTSCR_IF_DISABLED) &&
syscounter_armv8_m_cntrl_is_counter_enabled(dev))
{
return SYSCOUNTER_ARMV8_M_ERR_INVALID;
}
if (val.integer > SYSCOUNTER_ARMV8_M_SCALE_VAL_INT_MAX || val.fixed_point_fraction > SYSCOUNTER_ARMV8_M_SCALE_VAL_FRACT_MAX)
{
return SYSCOUNTER_ARMV8_M_ERR_INVALID_ARG;
}
reg_val = val.integer << SYSCOUNTER_ARMV8_M_SCALE_VAL_INT_OFF;
reg_val |= (val.fixed_point_fraction & SYSCOUNTER_ARMV8_M_SCALE_VAL_FRACT_MAX);
switch (nr)
{
case SYSCOUNTER_ARMV8_M_SCALE_NR_0:
p_cnt->cntscr0 = reg_val;
break;
case SYSCOUNTER_ARMV8_M_SCALE_NR_1:
p_cnt->cntscr1 = reg_val;
break;
default:
return SYSCOUNTER_ARMV8_M_ERR_INVALID_ARG;
}
return SYSCOUNTER_ARMV8_M_ERR_NONE;
}