-
Notifications
You must be signed in to change notification settings - Fork 537
/
Copy pathfdt_timer_mtimer.c
168 lines (148 loc) · 4.39 KB
/
fdt_timer_mtimer.c
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
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <[email protected]>
*/
#include <libfdt.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_heap.h>
#include <sbi/sbi_list.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/timer/fdt_timer.h>
#include <sbi_utils/timer/aclint_mtimer.h>
struct timer_mtimer_quirks {
bool is_clint;
unsigned int clint_mtime_offset;
bool clint_without_mtime;
bool has_64bit_mmio;
};
struct timer_mtimer_node {
struct sbi_dlist head;
struct aclint_mtimer_data data;
};
static SBI_LIST_HEAD(mtn_list);
static struct aclint_mtimer_data *mt_reference = NULL;
static int timer_mtimer_cold_init(const void *fdt, int nodeoff,
const struct fdt_match *match)
{
int rc;
unsigned long addr[2], size[2];
struct timer_mtimer_node *mtn, *n;
struct aclint_mtimer_data *mt;
const struct timer_mtimer_quirks *quirks = match->data;
bool is_clint = quirks && quirks->is_clint;
mtn = sbi_zalloc(sizeof(*mtn));
if (!mtn)
return SBI_ENOMEM;
mt = &mtn->data;
rc = fdt_parse_aclint_node(fdt, nodeoff, true, !is_clint,
&addr[0], &size[0], &addr[1], &size[1],
&mt->first_hartid, &mt->hart_count);
if (rc) {
sbi_free(mtn);
return rc;
}
mt->has_64bit_mmio = true;
mt->has_shared_mtime = false;
rc = fdt_parse_timebase_frequency(fdt, &mt->mtime_freq);
if (rc) {
sbi_free(mtn);
return rc;
}
if (is_clint) { /* SiFive CLINT */
/* Set CLINT addresses */
mt->mtimecmp_addr = addr[0] + ACLINT_DEFAULT_MTIMECMP_OFFSET;
mt->mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE;
if (!quirks->clint_without_mtime) {
mt->mtime_addr = addr[0] + ACLINT_DEFAULT_MTIME_OFFSET;
mt->mtime_size = size[0] - mt->mtimecmp_size;
/* Adjust MTIMER address and size for CLINT device */
mt->mtime_addr += quirks->clint_mtime_offset;
mt->mtime_size -= quirks->clint_mtime_offset;
} else {
mt->mtime_addr = mt->mtime_size = 0;
}
mt->mtimecmp_addr += quirks->clint_mtime_offset;
} else { /* RISC-V ACLINT MTIMER */
/* Set ACLINT MTIMER addresses */
mt->mtime_addr = addr[0];
mt->mtime_size = size[0];
mt->mtimecmp_addr = addr[1];
mt->mtimecmp_size = size[1];
}
/* Apply additional quirks */
if (quirks) {
mt->has_64bit_mmio = quirks->has_64bit_mmio;
}
/* Check if MTIMER device has shared MTIME address */
if (mt->mtime_size) {
mt->has_shared_mtime = false;
sbi_list_for_each_entry(n, &mtn_list, head) {
if (n->data.mtime_addr == mt->mtime_addr) {
mt->has_shared_mtime = true;
break;
}
}
} else {
/* Assume shared time CSR */
mt->has_shared_mtime = true;
}
/* Initialize the MTIMER device */
rc = aclint_mtimer_cold_init(mt, mt_reference);
if (rc) {
sbi_free(mtn);
return rc;
}
/*
* Select first MTIMER device with no associated HARTs as our
* reference MTIMER device. This is only a temporary strategy
* of selecting reference MTIMER device. In future, we might
* define an optional DT property or some other mechanism to
* help us select the reference MTIMER device.
*/
if (!mt->hart_count && !mt_reference) {
mt_reference = mt;
/*
* Set reference for already propbed MTIMER devices
* with non-shared MTIME
*/
sbi_list_for_each_entry(n, &mtn_list, head) {
if (!n->data.has_shared_mtime)
aclint_mtimer_set_reference(&n->data, mt);
}
}
/* Explicitly sync-up MTIMER devices not associated with any HARTs */
if (!mt->hart_count)
aclint_mtimer_sync(mt);
sbi_list_add_tail(&mtn->head, &mtn_list);
return 0;
}
static const struct timer_mtimer_quirks sifive_clint_quirks = {
.is_clint = true,
.clint_mtime_offset = CLINT_MTIMER_OFFSET,
.has_64bit_mmio = true,
};
static const struct timer_mtimer_quirks thead_clint_quirks = {
.is_clint = true,
.clint_mtime_offset = CLINT_MTIMER_OFFSET,
.clint_without_mtime = true,
};
static const struct timer_mtimer_quirks thead_aclint_quirks = {
.has_64bit_mmio = false,
};
static const struct fdt_match timer_mtimer_match[] = {
{ .compatible = "riscv,clint0", .data = &sifive_clint_quirks },
{ .compatible = "sifive,clint0", .data = &sifive_clint_quirks },
{ .compatible = "thead,c900-clint", .data = &thead_clint_quirks },
{ .compatible = "thead,c900-aclint-mtimer",
.data = &thead_aclint_quirks },
{ .compatible = "riscv,aclint-mtimer" },
{ },
};
const struct fdt_driver fdt_timer_mtimer = {
.match_table = timer_mtimer_match,
.init = timer_mtimer_cold_init,
};