blob: 3e74ba56dc4caa4d9e0dacd67912ea5bc2c2dd54 [file]
/*
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <utils/util.h>
#include <platsupport/timer.h>
#include <platsupport/plat/timer.h>
/* The GPT status register is w1c (write 1 to clear), and there are 6 status bits in the iMX
status register, so writing the value 0b111111 = 0x3F will clear it. */
#define GPT_STATUS_REGISTER_CLEAR 0x3F
#define CLEANUP_FAIL_TEXT "Failed to cleanup the GPT after failing to initialise it"
/* GPT CONTROL REGISTER BITS */
typedef enum {
/*
* This bit enables the GPT.
*/
EN = 0,
/*
* When GPT is disabled (EN=0), then
* both Main Counter and Prescaler Counter freeze their count at
* current count values. The ENMOD bit determines the value of
* the GPT counter when Counter is enabled again (if the EN bit is set).
*
* If the ENMOD bit is 1, then the Main Counter and Prescaler Counter
* values are reset to 0 after GPT is enabled (EN=1).
*
* If the ENMOD bit is 0, then the Main Counter and Prescaler Counter
* restart counting from their frozen values after GPT is enabled (EN=1).
*
* If GPT is programmed to be disabled in a low power mode (STOP/WAIT), then
* the Main Counter and Prescaler Counter freeze at their current count
* values when the GPT enters low power mode.
*
* When GPT exits low power mode, the Main Counter and Prescaler Counter start
* counting from their frozen values, regardless of the ENMOD bit value.
*
* Setting the SWR bit will clear the Main Counter and Prescalar Counter values,
* regardless of the value of EN or ENMOD bits.
*
* A hardware reset resets the ENMOD bit.
* A software reset does not affect the ENMOD bit.
*/
ENMOD = 1,
/*
* This read/write control bit enables the operation of the GPT
* during debug mode
*/
DBGEN = 2,
/*
* This read/write control bit enables the operation of the GPT
* during wait mode
*/
WAITEN = 3,
/*
* This read/write control bit enables the operation of the GPT
* during doze mode
*/
DOZEN = 4,
/*
* This read/write control bit enables the operation of the GPT
* during stop mode
*/
STOPEN = 5,
/*
* bits 6-8 - These bits selects the clock source for the
* prescaler and subsequently be used to run the GPT counter.
* the following sources are available on i.MX 7 board:
* 000: no clock
* 001: peripheral clock
* 010: high frequency reference clock
* 011: external clock (CLKIN)
* 100: low frequency reference clock 32 kHZ
* 101: crystal oscillator as reference clock 24 MHz
* others: reserved
* by default the peripheral clock is used.
*
* For imx6 :
* 000: no clock
* 001: peripheral clock
* 010: high frequency reference clock
* 011: external clock (CLKIN)
* 100: low frequency reference clock
* 101: crystal oscillator divided by 8 as reference clock
* 111: crystal osscillator as reference clock
*/
CLKSRC = 6,
/*
* Freerun or Restart mode.
*
* 0 Restart mode
* 1 Freerun mode
*/
FRR = 9,
/* for i.MX7 only
* enable the 24 MHz clock input from crystal
* a hardware reset resets the EN_24M bit.
* a software reset dose not affect the EN_24M bit.
* 0: disabled
* 1: enabled
*/
EN_24M = 10,
/*
* Software reset.
*
* This bit is set when the module is in reset state and is cleared
* when the reset procedure is over. Writing a 1 to this bit
* produces a single wait state write cycle. Setting this bit
* resets all the registers to their default reset values except
* for the EN, ENMOD, STOPEN, DOZEN, WAITEN and DBGEN bits in this
* control register.
*/
SWR = 15,
/* Input capture channel operating modes */
IM1 = 16, IM2 = 18,
/* Output compare channel operating modes */
OM1 = 20, OM2 = 23, OM3 = 26,
/* Force output compare channel bits */
FO1 = 29, FO2 = 30, FO3 = 31
} gpt_control_reg;
/* bits in the interrupt/status regiser */
enum gpt_interrupt_register_bits {
/* Output compare interrupt enable bits */
OF1IE = 0, OF2IE = 1, OF3IE = 2,
/* Input capture interrupt enable bits */
IF1IE = 3, IF2IE = 4,
/* Rollover interrupt enabled */
ROV = 5,
};
/* Memory map for GPT. */
struct gpt_map {
/* gpt control register */
uint32_t gptcr;
/* gpt prescaler register */
uint32_t gptpr;
/* gpt status register */
uint32_t gptsr;
/* gpt interrupt register */
uint32_t gptir;
/* gpt output compare register 1 */
uint32_t gptcr1;
/* gpt output compare register 2 */
uint32_t gptcr2;
/* gpt output compare register 3 */
uint32_t gptcr3;
/* gpt input capture register 1 */
uint32_t gpticr1;
/* gpt input capture register 2 */
uint32_t gpticr2;
/* gpt counter register */
uint32_t gptcnt;
};
int gpt_start(gpt_t *gpt)
{
gpt->gpt_map->gptcr |= BIT(EN);
gpt->high_bits = 0;
return 0;
}
int gpt_stop(gpt_t *gpt)
{
/* Disable timer. */
gpt->gpt_map->gptcr &= ~(BIT(EN));
gpt->high_bits = 0;
return 0;
}
static void gpt_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data)
{
assert(data != NULL);
gpt_t *gpt = data;
/* we've only set the GPT to interrupt on overflow */
if (gpt->gpt_map->gptcr & BIT(FRR)) {
/* free-run mode, we should only enable the rollover interrupt */
if (gpt->gpt_map->gptsr & BIT(ROV)) {
gpt->high_bits++;
}
}
/* clear the interrupt status register */
gpt->gpt_map->gptsr = GPT_STATUS_REGISTER_CLEAR;
/* acknowledge the interrupt and call the user callback if any */
ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the interrupt from the GPT");
if (gpt->user_callback) {
gpt->user_callback(gpt->user_callback_token, LTIMER_OVERFLOW_EVENT);
}
}
uint64_t gpt_get_time(gpt_t *gpt)
{
uint32_t low_bits = gpt->gpt_map->gptcnt;
uint32_t high_bits = gpt->high_bits;
if (gpt->gpt_map->gptsr) {
/* irq has come in */
high_bits++;
}
uint64_t value = ((uint64_t) high_bits << 32llu) + low_bits;
/* convert to ns */
uint64_t ns = (value / (uint64_t)GPT_FREQ) * NS_IN_US * (gpt->prescaler + 1);
return ns;
}
static int allocate_register_callback(pmem_region_t pmem, unsigned curr_num, size_t num_regs, void *token)
{
assert(token != NULL);
/* Should only be called once. I.e. only one register field */
assert(curr_num == 0);
gpt_t *gpt = token;
gpt->gpt_map = (volatile struct gpt_map *) ps_pmem_map(&gpt->io_ops, pmem, false, PS_MEM_NORMAL);
if (!gpt->gpt_map) {
ZF_LOGE("Failed to map in registers for the GPT");
return EIO;
}
gpt->timer_pmem = pmem;
return 0;
}
static int allocate_irq_callback(ps_irq_t irq, unsigned curr_num, size_t num_irqs, void *token)
{
assert(token != NULL);
/* Should only be called once. I.e. only one interrupt field */
assert(curr_num == 0);
gpt_t *gpt = token;
gpt->irq_id = ps_irq_register(&gpt->io_ops.irq_ops, irq, gpt_handle_irq, gpt);
if (gpt->irq_id < 0) {
ZF_LOGE("Failed to register the GPT interrupt with the IRQ interface");
return EIO;
}
return 0;
}
int gpt_init(gpt_t *gpt, gpt_config_t config)
{
/* Initialise the structure */
gpt->io_ops = config.io_ops;
gpt->user_callback = config.user_callback;
gpt->user_callback_token = config.user_callback_token;
gpt->irq_id = PS_INVALID_IRQ_ID;
gpt->prescaler = config.prescaler;
/* Read the timer's path in the DTB */
ps_fdt_cookie_t *cookie = NULL;
int error = ps_fdt_read_path(&gpt->io_ops.io_fdt, &gpt->io_ops.malloc_ops, config.device_path, &cookie);
if (error) {
ZF_LOGF_IF(ps_fdt_cleanup_cookie(&gpt->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
ZF_LOGF_IF(gpt_destroy(gpt), CLEANUP_FAIL_TEXT);
return ENODEV;
}
/* Walk the registers and allocate them */
error = ps_fdt_walk_registers(&gpt->io_ops.io_fdt, cookie, allocate_register_callback, gpt);
if (error) {
ZF_LOGF_IF(ps_fdt_cleanup_cookie(&gpt->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
ZF_LOGF_IF(gpt_destroy(gpt), CLEANUP_FAIL_TEXT);
return ENODEV;
}
/* Walk the interrupts and allocate the first */
error = ps_fdt_walk_irqs(&gpt->io_ops.io_fdt, cookie, allocate_irq_callback, gpt);
if (error) {
ZF_LOGF_IF(ps_fdt_cleanup_cookie(&gpt->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
ZF_LOGF_IF(gpt_destroy(gpt), CLEANUP_FAIL_TEXT);
return ENODEV;
}
ZF_LOGF_IF(ps_fdt_cleanup_cookie(&gpt->io_ops.malloc_ops, cookie),
"Failed to cleanup the FDT cookie after initialising the GPT");
uint32_t gptcr = 0;
if (gpt == NULL) {
return EINVAL;
}
/* Disable GPT. */
gpt->gpt_map->gptcr = 0;
gpt->gpt_map->gptsr = GPT_STATUS_REGISTER_CLEAR;
/* Configure GPT. */
gpt->gpt_map->gptcr = 0 | BIT(SWR); /* Reset the GPT */
/* SWR will be 0 when the reset is done */
while (gpt->gpt_map->gptcr & BIT(SWR));
/* GPT can do more but for this just set it as free running so we can tell the time */
gptcr = BIT(FRR) | BIT(ENMOD);
#ifdef CONFIG_PLAT_IMX7
/* eanble the 24MHz source and select the oscillator as CLKSRC */
gptcr |= (BIT(EN_24M) | (5u << CLKSRC));
#else
gptcr |= BIT(CLKSRC);
#endif
gpt->gpt_map->gptcr = gptcr;
gpt->gpt_map->gptir = BIT(ROV); /* Interrupt when the timer overflows */
/* The prescaler register has two parts when the 24 MHz clocksource is used.
* The 24MHz crystal clock is devided by the (the top 15-12 bits + 1) before
* it is fed to the CLKSRC field.
* The clock selected by the CLKSRC is divided by the (the 11-0 bits + ) again.
* For unknown reason, when the prescaler for the 24MHz clock is set to zero, which
* is valid according to the manual, the GPTCNT register does not work. So we
* set the value at least to 1, using a 12MHz clocksource.
*/
#ifdef CONFIG_PLAT_IMX7
gpt->gpt_map->gptpr = config.prescaler | (1u << 12);
#else
gpt->gpt_map->gptpr = config.prescaler; /* Set the prescaler */
#endif
gpt->high_bits = 0;
return 0;
}
int gpt_destroy(gpt_t *gpt)
{
if (gpt->gpt_map) {
ZF_LOGF_IF(gpt_stop(gpt), "Failed to stop the GPT before de-allocating it");
ps_io_unmap(&gpt->io_ops.io_mapper, (void *) gpt->gpt_map, (size_t) gpt->timer_pmem.length);
}
if (gpt->irq_id != PS_INVALID_IRQ_ID) {
ZF_LOGF_IF(ps_irq_unregister(&gpt->io_ops.irq_ops, gpt->irq_id), "Failed to unregister IRQ");
}
return 0;
}
int gpt_set_timeout(gpt_t *gpt, uint64_t ns, bool periodic)
{
uint32_t gptcr = 0;
uint64_t counter_value = (uint64_t)(GPT_FREQ / (gpt->prescaler + 1)) * (ns / 1000ULL);
if (counter_value >= (1ULL << 32)) {
/* Counter too large to be stored in 32 bits. */
ZF_LOGW("ns too high %llu, going to be capping it\n", ns);
counter_value = UINT32_MAX;
}
gpt->gpt_map->gptcr = 0;
gpt->gpt_map->gptsr = GPT_STATUS_REGISTER_CLEAR;
gpt->gpt_map->gptcr = BIT(SWR);
while (gpt->gpt_map->gptcr & BIT(SWR));
gptcr = (periodic ? 0 : BIT(FRR));
#ifdef CONFIG_PLAT_IMX7
gptcr |= BIT(EN_24M) | (5u << CLKSRC);
#else
gptcr |= BIT(CLKSRC);
#endif
gpt->gpt_map->gptcr = gptcr;
gpt->gpt_map->gptcr1 = (uint32_t)counter_value;
while (gpt->gpt_map->gptcr1 != counter_value) {
gpt->gpt_map->gptcr1 = (uint32_t)counter_value;
}
#ifdef CONFIG_PLAT_IMX7
gpt->gpt_map->gptpr = gpt->prescaler | BIT(12);
#else
gpt->gpt_map->gptpr = gpt->prescaler; /* Set the prescaler */
#endif
gpt->gpt_map->gptir = 1;
gpt->gpt_map->gptcr |= BIT(EN);
return 0;
}