summaryrefslogtreecommitdiff
path: root/src/soc/cavium/cn81xx/timer.c
blob: bd67d8a888320fe7c0d22a64058d8de08941340d (plain)
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
 * This file is part of the coreboot project.
 *
 * Copyright 2018       Facebook, Inc.
 * Copyright 2003-2017  Cavium Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
 */

#include <delay.h>
#include <device/mmio.h>
#include <arch/lib_helpers.h>
#include <console/console.h>
#include <soc/clock.h>
#include <soc/timer.h>
#include <stdint.h>
#include <timer.h>
#include <soc/addressmap.h>
#include <assert.h>

/* Global System Timers Unit (GTI) registers */
struct cn81xx_timer {
	u32 cc_cntcr;
	u32 cc_cntsr;
	u64 cc_cntcv;
	u8 rsvd[0x10];
	u32 cc_cntfid0;
	u32 cc_cntfid1;
	u8 rsvd2[0x98];
	u32 cc_cntrate;
	u32 cc_cntracc;
	u64 cc_cntadd;
	u64 cc_cntmb;
	u64 cc_cntmbts;
	u64 cc_cntmb_int;
	u64 cc_cntmb_int_set;
	u64 cc_cntmb_int_ena_clr;
	u64 cc_cntmb_int_ena_set;
	u64 cc_imp_ctl;
	u8 skip[0x1fef8];
	u32 ctl_cntfrq;
	u32 ctl_cntnsar;
	u32 ctl_cnttidr;
	u8 rsvd3[0x34];
	u32 ctl_cntacr0;
	u8 skip2[0x1ffb8];
	u64 cwd_wdog[48]; /* Offset 0x40000 */
	u8 skip3[0xfe80];
	u64 cwd_poke[48]; /* Offset 0x50000 */
};

check_member(cn81xx_timer, cc_imp_ctl, 0x100);
check_member(cn81xx_timer, ctl_cntacr0, 0x20040);
check_member(cn81xx_timer, cwd_wdog[0], 0x40000);
check_member(cn81xx_timer, cwd_poke[0], 0x50000);


#define GTI_CC_CNTCR_EN			(1 << 0)
#define GTI_CC_CNTCR_HDBG		(1 << 1)
#define GTI_CC_CNTCR_FCREQ		(1 << 8)

#define GTI_CC_CNTSR_DBGH		(1 << 1)
#define GTI_CC_CNTSR_FCACK		(1 << 8)

#define GTI_CWD_WDOG_MODE_SHIFT		0
#define GTI_CWD_WDOG_MODE_MASK		0x3
#define GTI_CWD_WDOG_STATE_SHIFT	2
#define GTI_CWD_WDOG_STATE_MASK		0x3
#define GTI_CWD_WDOG_LEN_SHIFT		4
#define GTI_CWD_WDOG_LEN_MASK		0xffff
#define GTI_CWD_WDOG_CNT_SHIFT		20
#define GTI_CWD_WDOG_CNT_MASK		0xffffff
#define GTI_CWD_WDOC_DSTOP		(1 << 44)
#define GTI_CWD_WDOC_GSTOP		(1 << 45)

static uint64_t timer_raw_value(void)
{
	struct cn81xx_timer *timer = (void *)GTI_PF_BAR0;

	return read64(&timer->cc_cntcv);
}

/**
 * Get GTI counter value.
 * @param mt      Structure to fill
 */
void timer_monotonic_get(struct mono_time *mt)
{
	mono_time_set_usecs(mt, timer_raw_value());
}

/* Setup counter to operate at 1MHz */
static const size_t tickrate = 1000000;

/**
 * Init Global System Timers Unit (GTI).
 * Configure timer to run at 1MHz tick-rate.
 */
void init_timer(void)
{
	struct cn81xx_timer *gti = (struct cn81xx_timer *)GTI_PF_BAR0;

	/* Check if the counter was already setup */
	if (gti->cc_cntcr & GTI_CC_CNTCR_EN)
		return;

	u64 sclk = thunderx_get_io_clock();

	/* Use coprocessor clock source */
	write32(&gti->cc_imp_ctl, 0);

	write32(&gti->cc_cntfid0, tickrate);
	write32(&gti->ctl_cntfrq, tickrate);
	write32(&gti->cc_cntrate, ((1ULL << 32) * tickrate) / sclk);

	/* Enable the counter */
	setbits_le32(&gti->cc_cntcr, GTI_CC_CNTCR_EN);

	//u32 u = (CNTPS_CTL_EL1_IMASK | CNTPS_CTL_EL1_EN);
	//BDK_MSR(CNTPS_CTL_EL1, u);
}

void soc_timer_init(void)
{
	raw_write_cntfrq_el0(tickrate);
}

/**
 * Setup the watchdog to expire in timeout_ms milliseconds. When the watchdog
 * expires, the chip three things happen:
 * 1) Expire 1: interrupt that is ignored by the BDK
 * 2) Expire 2: DEL3T interrupt, which is disabled and ignored
 * 3) Expire 3: Soft reset of the chip
 *
 * Since we want a soft reset, we actually program the watchdog to expire at
 * the timeout / 3.
 *
 * @param index      Index of watchdog to configure
 * @param timeout_ms Timeout in milliseconds.
 */
void watchdog_set(const size_t index, unsigned int timeout_ms)
{
	uint64_t sclk = thunderx_get_io_clock();
	uint64_t timeout_sclk = sclk * timeout_ms / 1000;
	struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;

	assert(index < ARRAY_SIZE(timer->cwd_wdog));
	if (index >= ARRAY_SIZE(timer->cwd_wdog))
		return;

	/*
	 * Per comment above, we want the watchdog to expire at 3x the rate
	 * specified
	 */
	timeout_sclk /= 3;
	/* Watchdog counts in 1024 cycle steps */
	uint64_t timeout_wdog = timeout_sclk >> 10;
	/* We can only specify the upper 16 bits of a 24 bit value. Round up */
	timeout_wdog = (timeout_wdog + 0xff) >> 8;
	/* If the timeout overflows the hardware limit, set max */
	if (timeout_wdog >= 0x10000)
		timeout_wdog = 0xffff;

	printk(BIOS_DEBUG, "Watchdog: Set to expire %llu SCLK cycles\n",
	       timeout_wdog << 18);
	clrsetbits_le64(&timer->cwd_wdog[index],
			(GTI_CWD_WDOG_LEN_MASK << GTI_CWD_WDOG_LEN_SHIFT) |
			(GTI_CWD_WDOG_MODE_MASK << GTI_CWD_WDOG_MODE_SHIFT),
			(timeout_wdog << GTI_CWD_WDOG_LEN_SHIFT) |
			(3 << GTI_CWD_WDOG_MODE_SHIFT));
}

/**
 * Signal the watchdog that we are still running.
 *
 * @param index      Index of watchdog to configure.
 */
void watchdog_poke(const size_t index)
{
	struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;

	assert(index < ARRAY_SIZE(timer->cwd_poke));
	if (index >= ARRAY_SIZE(timer->cwd_poke))
		return;

	write64(&timer->cwd_poke[0], 0);
}

/**
 * Disable the hardware watchdog
 *
 * @param index      Index of watchdog to configure.
 */
void watchdog_disable(const size_t index)
{
	struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;

	assert(index < ARRAY_SIZE(timer->cwd_wdog));
	if (index >= ARRAY_SIZE(timer->cwd_wdog))
		return;

	write64(&timer->cwd_wdog[index], 0);
	printk(BIOS_DEBUG, "Watchdog: Disabled\n");
}

/**
 * Return true if the watchdog is configured and running
 *
 * @param index      Index of watchdog to configure.
 *
 * @return           Non-zero if watchdog is running.
 */
int watchdog_is_running(const size_t index)
{
	struct cn81xx_timer *timer = (struct cn81xx_timer *)GTI_PF_BAR0;

	assert(index < ARRAY_SIZE(timer->cwd_wdog));
	if (index >= ARRAY_SIZE(timer->cwd_wdog))
		return 0;

	uint64_t val = read64(&timer->cwd_wdog[index]);

	return !!(val & (GTI_CWD_WDOG_MODE_MASK << GTI_CWD_WDOG_MODE_SHIFT));
}