1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2016, Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7 
8 #include <assert.h>
9 #include <dev/interrupt/arm_gic_common.h>
10 #include <dev/interrupt/arm_gicv2_regs.h>
11 #include <dev/interrupt/arm_gicv2m.h>
12 #include <err.h>
13 #include <string.h>
14 #include <trace.h>
15 #include <zircon/types.h>
16 
17 #define LOCAL_TRACE 0
18 
19 // Section 9.7
20 #define MSI_TYPER_OFFSET (0x008)     // Type Register
21 #define MSI_SETSPI_NS_OFFSET (0x040) // Doorbell register (whack here for interrupt)
22 #define MSI_IIDR_OFFSET (0xFCC)      // Interface ID register
23 #define REG_RD(base, off) (((volatile uint32_t*)(base))[(off) >> 2])
24 
25 // Section 9.9.1
26 #define MIN_VALID_MSI_SPI (32)
27 #define MAX_VALID_MSI_SPI (1020)
28 
29 static const paddr_t* g_reg_frames;
30 static const vaddr_t* g_reg_frames_virt;
31 static uint g_reg_frame_count;
32 
arm_gicv2m_init(const paddr_t * reg_frames,const vaddr_t * reg_frames_virt,const uint reg_frame_count)33 void arm_gicv2m_init(const paddr_t* reg_frames, const vaddr_t* reg_frames_virt, const uint reg_frame_count) {
34     // Protect against double init.
35     DEBUG_ASSERT(!g_reg_frames);
36     DEBUG_ASSERT(!g_reg_frame_count);
37 
38     // If the user has no register frames, they should be using arm_gic, not
39     // arm_gicv2m
40     DEBUG_ASSERT(reg_frames);
41     DEBUG_ASSERT(reg_frame_count);
42 
43     // Stash the frame info
44     g_reg_frames = reg_frames;
45     g_reg_frames_virt = reg_frames_virt;
46     g_reg_frame_count = reg_frame_count;
47 
48     // Walk the list of regions, and make sure that all of the controlled SPIs
49     // are configured for edge triggered mode.
50     for (uint i = 0; i < g_reg_frame_count; ++i) {
51         uint32_t type_reg = REG_RD(g_reg_frames_virt[i], MSI_TYPER_OFFSET);
52         uint base_spi = (type_reg >> 16) & 0x3FF;
53         uint num_spi = type_reg & 0x3FF;
54 
55         dprintf(SPEW, "GICv2m %u: base spi %u count %u\n", i, base_spi, num_spi);
56 
57         for (uint i = 0; i < num_spi; ++i) {
58             uint spi_id = base_spi + i;
59             if ((spi_id < MIN_VALID_MSI_SPI) || (spi_id > MAX_VALID_MSI_SPI)) {
60                 TRACEF("Invalid SPI ID (%u) found in GICv2m register frame @%p\n",
61                        spi_id, (void*)g_reg_frames[i]);
62                 continue;
63             }
64 
65             uint reg_ndx = spi_id >> 4;
66             uint bit_shift = ((spi_id & 0xF) << 1) + 1;
67             uint32_t reg_val = GICREG(0, GICD_ICFGR(reg_ndx));
68             reg_val |= (0x1u << bit_shift);
69             GICREG(0, GICD_ICFGR(reg_ndx)) = reg_val;
70         }
71     }
72 }
73 
arm_gicv2m_get_frame_info(const uint frame_ndx,arm_gicv2m_frame_info_t * out_info)74 zx_status_t arm_gicv2m_get_frame_info(const uint frame_ndx, arm_gicv2m_frame_info_t* out_info) {
75     if (!out_info) {
76         return ZX_ERR_INVALID_ARGS;
77     }
78 
79     *out_info = {};
80 
81     if (!g_reg_frames || !g_reg_frame_count) {
82         return ZX_ERR_UNAVAILABLE;
83     }
84 
85     if (frame_ndx >= g_reg_frame_count) {
86         return ZX_ERR_NOT_FOUND;
87     }
88 
89     uint32_t type_reg = REG_RD(g_reg_frames_virt[frame_ndx], MSI_TYPER_OFFSET);
90     uint base_spi = (type_reg >> 16) & 0x3FF;
91     uint num_spi = type_reg & 0x3FF;
92     uint last_spi = base_spi + num_spi - 1;
93 
94     if (!num_spi ||
95         (base_spi < MIN_VALID_MSI_SPI) ||
96         (last_spi > MAX_VALID_MSI_SPI)) {
97         return ZX_ERR_BAD_STATE;
98     }
99 
100     out_info->start_spi_id = base_spi;
101     out_info->end_spi_id = last_spi;
102     out_info->doorbell = g_reg_frames[frame_ndx] + MSI_SETSPI_NS_OFFSET;
103     out_info->iid = REG_RD(g_reg_frames_virt[frame_ndx], MSI_IIDR_OFFSET);
104 
105     return ZX_OK;
106 }
107