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 29static const paddr_t* g_reg_frames; 30static const vaddr_t* g_reg_frames_virt; 31static uint g_reg_frame_count; 32 33void 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 74zx_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