nxp_timer.c 3.52 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * Copyright 2021 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <lib/utils_def.h>
#include <nxp_timer.h>
#include <plat/common/platform.h>

static uintptr_t g_nxp_timer_addr;
static timer_ops_t ops;

uint64_t get_timer_val(uint64_t start)
{
	uint64_t cntpct;

	isb();
	cntpct = read_cntpct_el0();
	return (cntpct * 1000ULL / read_cntfrq_el0() - start);
}

static uint32_t timer_get_value(void)
{
	uint64_t cntpct;

	isb();
	cntpct = read_cntpct_el0();
#ifdef ERRATA_SOC_A008585
	uint8_t	max_fetch_count = 10U;
	/* This erratum number needs to be confirmed to match ARM document */
	uint64_t temp;

	isb();
	temp = read_cntpct_el0();

	while (temp != cntpct && max_fetch_count) {
		isb();
		cntpct = read_cntpct_el0();
		isb();
		temp = read_cntpct_el0();
		max_fetch_count--;
	}
#endif

	/*
	 * Generic delay timer implementation expects the timer to be a down
	 * counter. We apply bitwise NOT operator to the tick values returned
	 * by read_cntpct_el0() to simulate the down counter. The value is
	 * clipped from 64 to 32 bits.
	 */
	return (uint32_t)(~cntpct);
}

static void delay_timer_init_args(uint32_t mult, uint32_t div)
{
	ops.get_timer_value	= timer_get_value,
	ops.clk_mult		= mult;
	ops.clk_div		= div;

	timer_init(&ops);

	VERBOSE("Generic delay timer configured with mult=%u and div=%u\n",
		mult, div);
}

/*
 * Initialise the nxp on-chip free rolling usec counter as the delay
 * timer.
 */
void delay_timer_init(uintptr_t nxp_timer_addr)
{
	/* Value in ticks */
	unsigned int mult = MHZ_TICKS_PER_SEC;

	unsigned int div;

	unsigned int counter_base_frequency = plat_get_syscnt_freq2();

	g_nxp_timer_addr = nxp_timer_addr;
	/* Rounding off the Counter Frequency to MHZ_TICKS_PER_SEC */
	if (counter_base_frequency > MHZ_TICKS_PER_SEC) {
		counter_base_frequency = (counter_base_frequency
					/ MHZ_TICKS_PER_SEC)
					* MHZ_TICKS_PER_SEC;
	} else {
		counter_base_frequency = (counter_base_frequency
					/ KHZ_TICKS_PER_SEC)
					* KHZ_TICKS_PER_SEC;
	}

	/* Value in ticks per second (Hz) */
	div = counter_base_frequency;

	/* Reduce multiplier and divider by dividing them repeatedly by 10 */
	while ((mult % 10U == 0U) && (div % 10U == 0U)) {
		mult /= 10U;
		div /= 10U;
	}

	/* Enable and initialize the System level generic timer */
	mmio_write_32(g_nxp_timer_addr + CNTCR_OFF,
			CNTCR_FCREQ(0) | CNTCR_EN);

	delay_timer_init_args(mult, div);
}


#ifdef IMAGE_BL31
/*******************************************************************************
 * TBD: Configures access to the system counter timer module.
 ******************************************************************************/
void ls_configure_sys_timer(uintptr_t ls_sys_timctl_base,
			    uint8_t ls_config_cntacr,
			    uint8_t plat_ls_ns_timer_frame_id)
{
	unsigned int reg_val;

	if (ls_config_cntacr == 1U) {
		reg_val = (1U << CNTACR_RPCT_SHIFT) | (1U << CNTACR_RVCT_SHIFT);
		reg_val |= (1U << CNTACR_RFRQ_SHIFT) | (1U << CNTACR_RVOFF_SHIFT);
		reg_val |= (1U << CNTACR_RWVT_SHIFT) | (1U << CNTACR_RWPT_SHIFT);
		mmio_write_32(ls_sys_timctl_base +
		      CNTACR_BASE(plat_ls_ns_timer_frame_id), reg_val);
		mmio_write_32(ls_sys_timctl_base, plat_get_syscnt_freq2());
	}

	reg_val = (1U << CNTNSAR_NS_SHIFT(plat_ls_ns_timer_frame_id));
	mmio_write_32(ls_sys_timctl_base + CNTNSAR, reg_val);
}

void enable_init_timer(void)
{
	/* Enable and initialize the System level generic timer */
	mmio_write_32(g_nxp_timer_addr + CNTCR_OFF,
			CNTCR_FCREQ(0) | CNTCR_EN);
}
#endif