-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgicv2.zig
287 lines (251 loc) · 12.9 KB
/
gicv2.zig
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
const std = @import("std");
pub const gicdBase = 0x400000;
pub const giccBase = 0x400000 + 0x10000;
pub const Gic = struct {
// initialize gic controller
pub fn init() !void {
Gicc.init();
try Gicd.init();
}
pub const itargetsrPerReg = 4;
pub const isenablerPerReg = 32;
pub const isdisablerPerReg = 32;
pub const icpendrPerReg = 32;
pub const icfgrPerReg = 16;
pub const intPerReg = 32; // 32 interrupts per reg
pub const ipriorityPerReg = 4; // 4 priority per reg
pub const icenablerPerReg = 32;
pub const intMax: usize = 64;
pub const intNoPpi0: usize = 32;
pub const intNoSpi0: usize = 16;
pub const priShift: usize = 4;
// 8.8 The GIC Distributor register map
pub const GicdRegMap = struct {
// Enables interrupts and affinity routing
pub const ctlr = @intToPtr(*volatile u32, gicdBase + 0x0);
// Deactivates the corresponding interrupt. These registers are used when saving and restoring GIC state.
pub const intType = @intToPtr(*volatile u32, gicdBase + 0x004);
// distributor implementer identification register
pub const iidr = @intToPtr(*volatile u32, gicdBase + 0x008);
// Controls whether the corresponding interrupt is in Group 0 or Group 1.
pub const igroupr = @intToPtr(*volatile u32, gicdBase + 0x080);
// interrupt set-enable registers
pub const isenabler = @intToPtr(*volatile u32, gicdBase + 0x100);
// Disables forwarding of the corresponding interrupt to the CPU interfaces.
pub const icenabler = @intToPtr(*volatile u32, gicdBase + 0x180);
// interrupt set-pending registers
pub const ispendr = @intToPtr(*volatile u32, gicdBase + 0x200);
// Removes the pending state from the corresponding interrupt.
pub const icpendr = @intToPtr(*volatile u32, gicdBase + 0x280);
pub const isactiver = @intToPtr(*volatile u32, gicdBase + 0x300);
// Deactivates the corresponding interrupt. These registers are used when saving and restoring GIC state.
pub const icactiver = @intToPtr(*volatile u32, gicdBase + 0x380);
// interrupt priority registers
pub const ipriorityr = @intToPtr(*volatile u32, gicdBase + 0x400);
// interrupt processor targets registers
pub const itargetsr = @intToPtr(*volatile u32, gicdBase + 0x800);
// Determines whether the corresponding interrupt is edge-triggered or level-sensitive
pub const icfgr = @intToPtr(*volatile u32, gicdBase + 0xc00);
// software generated interrupt register
pub const nscar = @intToPtr(*volatile u32, gicdBase + 0xe00);
// sgi clear-pending registers
pub const cpendsgir = @intToPtr(*volatile u32, gicdBase + 0xf10);
// sgi set-pending registers
pub const spendsgir = @intToPtr(*volatile u32, gicdBase + 0xf20);
pub const sgir = @intToPtr(*volatile u32, 0xf00);
};
// 8.12 the gic cpu interface register map
pub const GiccRegMap = struct {
// cpu interface control register
pub const ctlr = @intToPtr(*volatile u32, giccBase + 0x000);
// interrupt priority mask register
pub const pmr = @intToPtr(*volatile u32, giccBase + 0x004);
// binary point register
pub const bpr = @intToPtr(*volatile u32, giccBase + 0x008);
// interrupt acknowledge register
pub const iar = @intToPtr(*volatile u32, giccBase + 0x00c);
// end of interrupt register
pub const eoir = @intToPtr(*volatile u32, giccBase + 0x010);
// running priority register
pub const rpr = @intToPtr(*volatile u32, giccBase + 0x014);
// highest pending interrupt register
pub const hpir = @intToPtr(*volatile u32, giccBase + 0x018);
// aliased binary point register
pub const abpr = @intToPtr(*volatile u32, giccBase + 0x01c);
// cpu interface identification register
pub const iidr = @intToPtr(*volatile u32, giccBase + 0x0fc);
};
pub const GicdRegValues = struct {
pub const itargetsrCore0TargetBmap = 0x01010101; // cpu interface 0
// 8.9.4 gicd_ctlr, distributor control register
pub const gicdCtlrEnable = 0x1; // enable gicd
pub const gicdCtlrDisable = 0; // disable gicd
// 8.9.7 gicd_icfgr<n>, interrupt configuration registers
pub const gicdIcfgrLevel = 0; // level-sensitive
pub const gicdIcfgrEdge = 0x2; // edge-triggered
};
pub const GiccRegValues = struct {
// gicc..
// 8.13.14 gicc_pmr, cpu interface priority mask register
pub const giccPmrPrioMin = 0xff; // the lowest level mask
pub const giccPmrPrioHigh = 0x0; // the highest level mask
// 8.13.7 gicc_ctlr, cpu interface control register
pub const giccCtlrEnable = 0x1; // enable gicc
pub const giccCtlrDisable = 0x0; // disable gicc
// 8.13.6 gicc_bpr, cpu interface binary point register
// in systems that support only one security state, when gicc_ctlr.cbpr == 0, this register determines only group 0 interrupt preemption.
pub const giccBprNoGroup = 0x0; // handle all interrupts
// 8.13.11 gicc_iar, cpu interface interrupt acknowledge register
pub const giccIarIntrIdmask = 0x3ff; // 0-9 bits means interrupt id
pub const giccIarSpuriousIntr = 0x3ff; // 1023 means spurious interrupt
};
pub const InterruptIds = enum(u32) {
non_secure_physical_timer = 30,
};
pub const Gicc = struct {
// initialize gic controller
fn init() void {
// disable cpu interface
GiccRegMap.ctlr.* = GiccRegValues.giccCtlrDisable;
// set the priority level as the lowest priority
// note: higher priority corresponds to a lower priority field value in the gic_pmr.
// in addition to this, writing 255 to the gicc_pmr always sets it to the largest supported priority field value.
GiccRegMap.pmr.* = GiccRegValues.giccPmrPrioMin;
// handle all of interrupts in a single group
GiccRegMap.bpr.* = GiccRegValues.giccBprNoGroup;
// clear all of the active interrupts
var pending_irq: u32 = 0;
while (pending_irq != GiccRegValues.giccIarSpuriousIntr) : (pending_irq = GiccRegMap.iar.* & GiccRegValues.giccIarSpuriousIntr) {
GiccRegMap.eoir.* = GiccRegMap.iar.*;
}
// enable cpu interface
GiccRegMap.ctlr.* = GiccRegValues.giccCtlrEnable;
}
// send end of interrupt to irq line for gic
// ctrlr irq controller Configrmation
// irq irq number
pub fn gicv2Eoi(irq: u32) void {
Gicd.gicdClearPending(irq);
}
// find pending irq
// irqp an irq number to be processed
pub fn gicv2FindPendingIrq() ![@bitSizeOf(usize)]u32 {
var ret_array = [_]u32{0} ** @bitSizeOf(usize);
var i: u32 = 0;
var i_found: u32 = 0;
while (@bitSizeOf(usize) > i) : (i += 1) {
if (try Gicd.gicdProbePending(i)) {
ret_array[i_found] = i;
i_found += 1;
}
}
return ret_array;
}
};
pub const Gicd = struct {
// init the gic distributor
fn init() !void {
var i: u32 = 0;
var irq_num: u32 = 0;
// diable distributor
GicdRegMap.ctlr.* = GiccRegValues.giccCtlrDisable;
// disable all irqs & clear pending
irq_num = try std.math.divExact(u32, @bitSizeOf(usize) + intPerReg, intPerReg);
while (irq_num > i) : (i += 1) {
(try calcReg(GicdRegMap.icenabler, i, icenablerPerReg)).* = ~@as(u32, 0);
(try calcReg(GicdRegMap.icpendr, i, icpendrPerReg)).* = ~@as(u32, 0);
}
i = 0;
// set all of interrupt priorities as the lowest priority
irq_num = try std.math.divExact(u32, @bitSizeOf(usize) + ipriorityPerReg, ipriorityPerReg);
while (irq_num > i) : (i += 1) {
(try calcReg(GicdRegMap.ipriorityr, i, ipriorityPerReg)).* = ~@as(u32, 0);
}
i = 0;
// set target of all of shared arm to processor 0
i = try std.math.divExact(u32, intNoSpi0, itargetsrPerReg);
while ((try std.math.divExact(u32, @bitSizeOf(usize) + itargetsrPerReg, itargetsrPerReg)) > i) : (i += 1) {
(try calcReg(GicdRegMap.itargetsr, i, itargetsrPerReg)).* = @as(u32, GicdRegValues.itargetsrCore0TargetBmap);
}
// set trigger type for all armeral interrupts level triggered
i = try std.math.divExact(u32, intNoPpi0, icfgrPerReg);
while ((try std.math.divExact(u32, @bitSizeOf(usize) + icfgrPerReg, icfgrPerReg)) > i) : (i += 1) {
(try calcReg(GicdRegMap.icfgr, i, icfgrPerReg)).* = GicdRegValues.gicdIcfgrLevel;
}
// enable distributor
GicdRegMap.ctlr.* = GicdRegValues.gicdCtlrEnable;
}
pub fn gicdEnableInt(irq_id: InterruptIds) !void {
const clear_ena_bit = try calcShift(@enumToInt(irq_id), isenablerPerReg);
const reg = try calcReg(GicdRegMap.isenabler, @enumToInt(irq_id), isenablerPerReg);
reg.* = reg.* | (@as(u32, 1) << clear_ena_bit);
}
pub fn gicdDisableInt(irq_id: InterruptIds) !void {
// irq_id mod 32
const clear_ena_bit = try calcShift(@enumToInt(irq_id), isdisablerPerReg);
const reg = try calcReg(GicdRegMap.icenabler, @enumToInt(irq_id), isdisablerPerReg);
reg.* = reg.* << clear_ena_bit;
}
pub fn gicdClearPending(irq_id: InterruptIds) !void {
// irq_id mod 32
const clear_ena_bit = try calcShift(@enumToInt(irq_id), icpendrPerReg);
const reg = try calcReg(GicdRegMap.icpendr, @enumToInt(irq_id), icpendrPerReg);
reg.* = reg.* << clear_ena_bit;
}
pub fn gicdProbePending(irq_id: u32) !bool {
// irq_id mod 32
const clear_ena_ind = try calcShift(irq_id, icpendrPerReg);
const cleared_ena_bit = @as(u32, 1) << clear_ena_ind;
const is_pending = (try calcReg(GicdRegMap.icpendr, irq_id, icpendrPerReg)).* & cleared_ena_bit;
return is_pending != 0;
}
// from the gicv2 docs:
// For interrupt ID m, when DIV and MOD are the integer division and modulo operations:
// • the corresponding GICD_ICENABLERn number, n, is given by m = n DIV 32 (32 ints per reg)
// • the offset of the required GICD_ICENABLERn is (0x180 + (4*n))
// • the bit number of the required Clear-enable bit in this register is m MOD 32 (calcShift...)
fn calcReg(addr_start: *volatile u32, irq_id: u32, n_per_reg: u32) !*volatile u32 {
return @intToPtr(*volatile u32, @ptrToInt(addr_start) + try std.math.divFloor(u32, irq_id, n_per_reg));
}
fn calcShift(irq_id: u32, n_per_reg: u32) !u5 {
return @truncate(u5, try std.math.mod(u32, irq_id, n_per_reg));
}
// set an interrupt target processor
// irq irq number
// p target processor mask
// 0x1 processor 0
// 0x2 processor 1
// 0x4 processor 2
// 0x8 processor 3
pub fn gicdSetTarget(irq_id: InterruptIds, p: u32) !void {
const reg = try calcReg(GicdRegMap.itargetsr, @enumToInt(irq_id), itargetsrPerReg);
const shift = try calcShift(@enumToInt(irq_id), itargetsrPerReg) * 8;
var update_val: u32 = reg.*;
update_val &= ~(@as(u32, 0xff) << shift);
update_val |= p << shift;
reg.* = update_val;
}
// set an interrupt priority
// irq irq number
// prio interrupt priority in arm specific expression
pub fn gicdSetPriority(irq_id: InterruptIds, prio: u8) !void {
const reg = try calcReg(GicdRegMap.ipriorityr, @enumToInt(irq_id), ipriorityPerReg);
const prio_shift = @truncate(u3, (try std.math.mod(u32, @enumToInt(irq_id), ipriorityPerReg)) * 8);
var update_val = reg.*;
update_val &= ~(@intCast(u32, 0xff) << prio_shift);
update_val |= prio << prio_shift;
reg.* = update_val;
}
// configure irq
// irq irq number
// config configuration value for gicd_icfgr
pub fn gicdConfig(irq_id: InterruptIds, config: u32) !void {
const reg = try calcReg(GicdRegMap.icfgr, @enumToInt(irq_id), icfgrPerReg);
var shift = try calcShift(@enumToInt(irq_id), icfgrPerReg) * 2; // gicd_icfgr has 16 fields, each field has 2bits.
var update_val = reg.*;
update_val &= ~((@as(u32, 0x03)) << shift); // clear the field
update_val |= ((@as(u32, config)) << shift); // set the value to the field correponding to irq
reg.* = update_val;
}
};
};