Newer
Older
mbed-os / targets / TARGET_ARM_SSG / TARGET_MUSCA_B1 / device / drivers / timer_gp_drv.c
@Harrison Mutai Harrison Mutai on 15 Oct 2020 6 KB Add SPDX license identifier to Arm files
/*
 * Copyright (c) 2018 Arm Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 timer_gp_drv.c
 *
 * \brief Generic driver for general purpose timer.
 */

#include "timer_gp_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))))

/**
 * \brief Timer register map structure
 *
 */
struct timer_gp_reg_map_t {
    volatile uint32_t reset;      /*!< Offset: 0x000 (R/W) Control Reset */
    volatile uint32_t irq_mask;   /*!< Offset: 0x004 (R/W) Masked Interrupt */
    volatile uint32_t irq_clear;  /*!< Offset: 0x008 (R/W) Interrupt Clear */
    volatile uint32_t reserved;   /*!< Offset: 0x00C Reserved */
    volatile uint32_t alarm0;     /*!< Offset: 0x010 (R/W) Alarm0 data value */
    volatile uint32_t alarm1;     /*!< Offset: 0x014 (R/W) Alarm1 data value */
    volatile uint32_t irq_status; /*!< Offset: 0x018 (R) Raw Interrupt status */
    volatile uint32_t counter;    /*!< Offset: 0x01C (R) Counter data value */
};

/**
 * \brief Reset control register bit identifiers
 *
 */
enum reset_reg_bits_t{
    RESET_REG_INDEX = 0
    /* 1-31: Reserved. Read as zero. Do No Modify (DNM). */
};

void timer_gp_init(const struct timer_gp_dev_t* dev)
{
    if (dev->data->is_initialized == false) {
        timer_gp_interrupt_disable(dev, TIMER_GP_ALARM_0);
        timer_gp_interrupt_clear(dev, TIMER_GP_ALARM_0);
        timer_gp_interrupt_disable(dev, TIMER_GP_ALARM_1);
        timer_gp_interrupt_clear(dev, TIMER_GP_ALARM_1);
        timer_gp_set_alarm_value(dev, TIMER_GP_ALARM_0, dev->data->alarm0_init);
        timer_gp_set_alarm_value(dev, TIMER_GP_ALARM_1, dev->data->alarm1_init);
        timer_gp_counter_reset(dev);
        dev->data->is_initialized = true;
    }
}

void timer_gp_counter_reset(const struct timer_gp_dev_t* dev)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;

    SET_BIT(register_map->reset, RESET_REG_INDEX);
    /* Reset bit is not self-clearing and some pulse width is required
     * for successful reset, so we have to check whether the
     * timer counter is set to reset value. Until this bit is asserted
     * the timer won't be started.
     * The timer is running only if the reset bit is cleared.*/
    while (timer_gp_get_counter(dev) != TIMER_GP_DEFAULT_RESET)
        ;
    CLR_BIT(register_map->reset, RESET_REG_INDEX);
}

uint32_t timer_gp_get_counter(const struct timer_gp_dev_t* dev)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;
    return register_map->counter;
}

void timer_gp_interrupt_enable(const struct timer_gp_dev_t* dev,
                               const enum timer_gp_alarm_identifier_t alarm)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;
    SET_BIT(register_map->irq_mask, alarm);
}

void timer_gp_interrupt_disable(const struct timer_gp_dev_t* dev,
                                const enum timer_gp_alarm_identifier_t alarm)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;
    CLR_BIT(register_map->irq_mask, alarm);
}

bool timer_gp_interrupt_is_enabled(const struct timer_gp_dev_t* dev,
        const enum timer_gp_alarm_identifier_t alarm)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;
    return GET_BIT(register_map->irq_mask, alarm);
}

bool timer_gp_interrupt_is_active(const struct timer_gp_dev_t* dev,
        const enum timer_gp_read_alarm_identifier_t alarm)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;
    return GET_BIT(register_map->irq_status, alarm);
}

void timer_gp_interrupt_clear(const struct timer_gp_dev_t* dev,
                              const enum timer_gp_alarm_identifier_t alarm)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;
    enum timer_gp_read_alarm_identifier_t read_alarm =
                    ((alarm == TIMER_GP_ALARM_0) ?
                    (TIMER_GP_READ_ALARM_0) :
                    (TIMER_GP_READ_ALARM_1));

    SET_BIT(register_map->irq_clear, alarm);

    /* Clear bit is not self-clearing and some pulse width is required
     * for successful interrupt clear, so we have to check whether the
     * interrupt is cleared. */
    while(timer_gp_interrupt_is_active(dev, read_alarm))
        ;
    CLR_BIT(register_map->irq_clear, alarm);
}

void timer_gp_set_alarm_value(const struct timer_gp_dev_t* dev,
                              const enum timer_gp_alarm_identifier_t alarm,
                              const uint32_t value)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;
    if (alarm == TIMER_GP_ALARM_0) {
        register_map->alarm0 = value;
    } else {
        register_map->alarm1 = value;
    }
}

uint32_t timer_gp_get_alarm_value(const struct timer_gp_dev_t* dev,
                                  const enum timer_gp_alarm_identifier_t alarm)
{
    struct timer_gp_reg_map_t* const register_map =
            (struct timer_gp_reg_map_t*)dev->cfg->base;
    if (alarm == TIMER_GP_ALARM_0) {
        return register_map->alarm0;
    } else {
        return register_map->alarm1;
    }
}