mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 23:42:32 +00:00
hw: GICv3 implementation
* modern GICv3 implementation * distributor * redistributor * MMIO cpu interface Ref #3426
This commit is contained in:
parent
fa1aa33f83
commit
dd505edd19
62
repos/base-hw/src/bootstrap/spec/arm/gicv3.cc
Normal file
62
repos/base-hw/src/bootstrap/spec/arm/gicv3.cc
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* \brief GICv3 interrupt controller for core
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2019-07-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <platform.h>
|
||||||
|
|
||||||
|
Hw::Pic::Pic()
|
||||||
|
: _distr(Board::Cpu_mmio::IRQ_CONTROLLER_DISTR_BASE),
|
||||||
|
_redistr(Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_BASE),
|
||||||
|
_redistr_sgi(Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_BASE +
|
||||||
|
Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_SIZE / 2),
|
||||||
|
_max_irq(_distr.max_irq())
|
||||||
|
{
|
||||||
|
/* disable device */
|
||||||
|
_distr.write<Distributor::Ctlr>(0);
|
||||||
|
_distr.wait_for_rwp();
|
||||||
|
|
||||||
|
/* XXX: remove */
|
||||||
|
struct Affinity : Genode::Register<64>
|
||||||
|
{
|
||||||
|
struct Aff0 : Bitfield<0, 8> { };
|
||||||
|
struct Aff1 : Bitfield<8, 8> { };
|
||||||
|
struct Aff2 : Bitfield<16, 8> { };
|
||||||
|
struct Aff3 : Bitfield<32, 8> { };
|
||||||
|
};
|
||||||
|
Genode::uint64_t mpidr = 0;
|
||||||
|
asm volatile ("mrs %0, mpidr_el1" : "=r"(mpidr) : : "memory");
|
||||||
|
Affinity::access_t affinity = 0;
|
||||||
|
Affinity::Aff0::set(affinity, mpidr);
|
||||||
|
Affinity::Aff1::set(affinity, (mpidr >> 8) & 0xff);
|
||||||
|
Affinity::Aff2::set(affinity, (mpidr >> 16) & 0xff);
|
||||||
|
Affinity::Aff3::set(affinity, (mpidr >> 32) & 0xff);
|
||||||
|
|
||||||
|
/* configure every shared peripheral interrupt */
|
||||||
|
for (unsigned i = min_spi; i <= _max_irq; i++) {
|
||||||
|
_distr.write<Distributor::Icfgr::Edge_triggered>(0, i);
|
||||||
|
_distr.write<Distributor::Ipriorityr::Priority>(0xa0, i);
|
||||||
|
_distr.write<Distributor::Icenabler::Clear_enable>(1, i);
|
||||||
|
_distr.write<Distributor::Icpendr::Clear_pending>(1, i);
|
||||||
|
_distr.write<Distributor::Igroup0r::Group1>(1, i);
|
||||||
|
|
||||||
|
/* XXX remove: route all SPIs to this PE */
|
||||||
|
_distr.write<Distributor::Irouter>(affinity, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable device GRP1_NS with affinity */
|
||||||
|
Distributor::Ctlr::access_t ctlr = 0;
|
||||||
|
Distributor::Ctlr::Enable_grp1_a::set(ctlr, 1);
|
||||||
|
Distributor::Ctlr::Are_ns::set(ctlr, 1);
|
||||||
|
|
||||||
|
_distr.write<Distributor::Ctlr>(ctlr);
|
||||||
|
_distr.wait_for_rwp();
|
||||||
|
}
|
29
repos/base-hw/src/core/spec/arm/gicv3.cc
Normal file
29
repos/base-hw/src/core/spec/arm/gicv3.cc
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* \brief Generic Interrupt Controller version 3
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2019-06-27
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* core includes */
|
||||||
|
#include <board.h>
|
||||||
|
#include <platform.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
Hw::Pic::Pic()
|
||||||
|
: _distr(Platform::mmio_to_virt(Board::Cpu_mmio::IRQ_CONTROLLER_DISTR_BASE)),
|
||||||
|
_redistr(Platform::mmio_to_virt(Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_BASE)),
|
||||||
|
_redistr_sgi(Platform::mmio_to_virt(Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_BASE)
|
||||||
|
+ Board::Cpu_mmio::IRQ_CONTROLLER_REDIST_SIZE / 2),
|
||||||
|
_max_irq(_distr.max_irq())
|
||||||
|
{
|
||||||
|
_redistributor_init();
|
||||||
|
_cpui.init();
|
||||||
|
}
|
262
repos/base-hw/src/include/hw/spec/arm/gicv3.h
Normal file
262
repos/base-hw/src/include/hw/spec/arm/gicv3.h
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* \brief GICv3 interrupt controller for core
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \date 2019-07-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SRC__INCLUDE__HW__SPEC__ARM__GIC_V3_H_
|
||||||
|
#define _SRC__INCLUDE__HW__SPEC__ARM__GIC_V3_H_
|
||||||
|
|
||||||
|
#include <util/mmio.h>
|
||||||
|
|
||||||
|
namespace Hw { class Pic; }
|
||||||
|
|
||||||
|
#define SYSTEM_REGISTER(sz, name, reg, ...) \
|
||||||
|
struct name : Genode::Register<sz> \
|
||||||
|
{ \
|
||||||
|
static access_t read() \
|
||||||
|
{ \
|
||||||
|
access_t v; \
|
||||||
|
asm volatile ("mrs %0, " reg : "=r" (v)); \
|
||||||
|
return v; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static void write(access_t const v) { \
|
||||||
|
asm volatile ("msr " reg ", %0" :: "r" (v)); } \
|
||||||
|
\
|
||||||
|
__VA_ARGS__; \
|
||||||
|
};
|
||||||
|
|
||||||
|
class Hw::Pic
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
|
||||||
|
static constexpr unsigned min_spi = 32;
|
||||||
|
static constexpr unsigned spurious_id = 1023;
|
||||||
|
|
||||||
|
struct Distributor : Genode::Mmio
|
||||||
|
{
|
||||||
|
static constexpr unsigned nr_of_irq = 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control register (secure access)
|
||||||
|
*/
|
||||||
|
/* XXX: CAUTION this is different in EL3/EL2/EL1! */
|
||||||
|
struct Ctlr : Register<0x0, 32>
|
||||||
|
{
|
||||||
|
struct Enable_grp0 : Bitfield<0, 1> { };
|
||||||
|
struct Enable_grp1_a : Bitfield<1, 1> { };
|
||||||
|
struct Are_ns : Bitfield<5, 1> { };
|
||||||
|
struct Rwp : Bitfield<31, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Typer : Register<0x004, 32> {
|
||||||
|
struct It_lines_number : Bitfield<0,5> { }; };
|
||||||
|
|
||||||
|
struct Igroup0r : Register_array<0x80, 32, 32*32, 1> {
|
||||||
|
struct Group1 : Bitfield<0, 1> { }; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupt Set-Enable register
|
||||||
|
*/
|
||||||
|
struct Isenabler : Register_array<0x100, 32, 32*32, 1, true> {
|
||||||
|
struct Set_enable : Bitfield<0, 1> { }; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupt clear enable registers
|
||||||
|
*/
|
||||||
|
struct Icenabler : Register_array<0x180, 32, 32*32, 1, true> {
|
||||||
|
struct Clear_enable : Bitfield<0, 1> { }; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupt clear pending registers
|
||||||
|
*/
|
||||||
|
struct Icpendr : Register_array<0x280, 32, 32*32, 1, true> {
|
||||||
|
struct Clear_pending : Bitfield<0, 1> { }; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupt priority level registers
|
||||||
|
*/
|
||||||
|
struct Ipriorityr : Register_array<0x400, 32, 255*4, 8> {
|
||||||
|
struct Priority : Bitfield<0, 8> { }; };
|
||||||
|
|
||||||
|
|
||||||
|
struct Icfgr : Register_array<0xc00, 32, 64*16, 2> {
|
||||||
|
struct Edge_triggered : Bitfield<1, 1> { }; };
|
||||||
|
|
||||||
|
struct Irouter : Register_array<0x6000, 64, 1020, 64, true> { };
|
||||||
|
|
||||||
|
void wait_for_rwp()
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 1000; i++)
|
||||||
|
if (read<Ctlr::Rwp>() == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned max_irq() { return 32 * (read<Typer::It_lines_number>() + 1) - 1; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Distributor(Genode::addr_t const base) : Genode::Mmio(base)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Redistributor : Genode::Mmio
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Ctlr : Register<0x0, 32>
|
||||||
|
{
|
||||||
|
struct Uwp : Bitfield<31, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
Redistributor(Genode::addr_t const base) : Genode::Mmio(base)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/* wait for upstream writes */
|
||||||
|
void wait_for_uwp()
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 1000; i++)
|
||||||
|
if (read<Ctlr::Uwp>() == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Redistributor_sgi_ppi : Genode::Mmio
|
||||||
|
{
|
||||||
|
struct Igroupr0 : Register<0x80, 32> { };
|
||||||
|
|
||||||
|
struct Isenabler0 : Register_array<0x100, 32, 32, 1>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
struct Icenabler0 : Register_array<0x180, 32, 32, 1>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
struct Icactiver0 : Register<0x380, 32> { };
|
||||||
|
|
||||||
|
struct Ipriorityr : Register_array<0x400, 32, min_spi, 8> {
|
||||||
|
struct Priority : Bitfield<0, 8> { }; };
|
||||||
|
|
||||||
|
struct Icfgr1 : Register<0xc04, 32> { };
|
||||||
|
|
||||||
|
Redistributor_sgi_ppi(Genode::addr_t const base) : Genode::Mmio(base)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cpu_interface
|
||||||
|
{
|
||||||
|
SYSTEM_REGISTER(32, Icc_sre_el1, "S3_0_C12_C12_5",
|
||||||
|
struct Sre : Bitfield<0, 1> { };
|
||||||
|
);
|
||||||
|
|
||||||
|
SYSTEM_REGISTER(32, Icc_iar1_el1, "S3_0_C12_C12_0");
|
||||||
|
SYSTEM_REGISTER(32, Icc_br1_el1, "S3_0_C12_C12_3");
|
||||||
|
SYSTEM_REGISTER(32, Icc_pmr_el1, "S3_0_C4_C6_0");
|
||||||
|
SYSTEM_REGISTER(32, Icc_igrpen1_el1, "S3_0_C12_C12_7");
|
||||||
|
SYSTEM_REGISTER(32, Icc_eoir1_el1, "S3_0_C12_C12_1");
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
/* enable register access */
|
||||||
|
Icc_sre_el1::access_t sre = Icc_sre_el1::read();
|
||||||
|
Icc_sre_el1::Sre::set(sre, 1);
|
||||||
|
Icc_sre_el1::write(sre);
|
||||||
|
|
||||||
|
/* XXX: check if needed or move somewhere else */
|
||||||
|
asm volatile("isb sy" ::: "memory");
|
||||||
|
|
||||||
|
/* no priority grouping */
|
||||||
|
Icc_br1_el1::write(0);
|
||||||
|
|
||||||
|
/* allow all priorities */
|
||||||
|
Icc_pmr_el1::write(0xff);
|
||||||
|
|
||||||
|
/* enable GRP1 interrupts */
|
||||||
|
Icc_igrpen1_el1::write(1);
|
||||||
|
|
||||||
|
/* XXX: check if needed or move somewhere else */
|
||||||
|
asm volatile("isb sy" ::: "memory");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void _redistributor_init()
|
||||||
|
{
|
||||||
|
/* diactivate SGI/PPI */
|
||||||
|
_redistr_sgi.write<Redistributor_sgi_ppi::Icactiver0>(~0u);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < min_spi; i++) {
|
||||||
|
_redistr_sgi.write<Redistributor_sgi_ppi::Ipriorityr::Priority>(0xa0, i); }
|
||||||
|
|
||||||
|
/* set group 1 for all PPI/SGIs */
|
||||||
|
_redistr_sgi.write<Redistributor_sgi_ppi::Igroupr0>(~0);
|
||||||
|
|
||||||
|
/* disable SGI/PPI */
|
||||||
|
_redistr_sgi.write<Redistributor_sgi_ppi::Icenabler0>(~0);
|
||||||
|
|
||||||
|
/* set PPIs to level triggered */
|
||||||
|
_redistr_sgi.write<Redistributor_sgi_ppi::Icfgr1>(0);
|
||||||
|
|
||||||
|
_redistr.wait_for_uwp();
|
||||||
|
}
|
||||||
|
|
||||||
|
Distributor _distr;
|
||||||
|
Redistributor _redistr;
|
||||||
|
Redistributor_sgi_ppi _redistr_sgi;
|
||||||
|
Cpu_interface _cpui { };
|
||||||
|
|
||||||
|
unsigned const _max_irq;
|
||||||
|
|
||||||
|
Cpu_interface::Icc_iar1_el1::access_t _last_iar { spurious_id };
|
||||||
|
|
||||||
|
bool _valid(unsigned const irq_id) const { return irq_id <= _max_irq; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Pic();
|
||||||
|
|
||||||
|
enum { IPI = 0 };
|
||||||
|
enum { NR_OF_IRQ = Distributor::nr_of_irq };
|
||||||
|
|
||||||
|
bool take_request(unsigned &irq)
|
||||||
|
{
|
||||||
|
_last_iar = Cpu_interface::Icc_iar1_el1::read();
|
||||||
|
irq = _last_iar;
|
||||||
|
|
||||||
|
return _valid(irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void finish_request()
|
||||||
|
{
|
||||||
|
Cpu_interface::Icc_eoir1_el1::write(_last_iar);
|
||||||
|
_last_iar = spurious_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unmask(unsigned const irq_id, unsigned const /* cpu_id */)
|
||||||
|
{
|
||||||
|
if (irq_id < min_spi) {
|
||||||
|
_redistr_sgi.write<Redistributor_sgi_ppi::Isenabler0>(1, irq_id);
|
||||||
|
} else {
|
||||||
|
_distr.write<Distributor::Isenabler::Set_enable>(1, irq_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mask(unsigned const irq_id)
|
||||||
|
{
|
||||||
|
if (irq_id < min_spi) {
|
||||||
|
_redistr_sgi.write<Redistributor_sgi_ppi::Icenabler0>(1, irq_id);
|
||||||
|
} else {
|
||||||
|
_distr.write<Distributor::Icenabler::Clear_enable>(1, irq_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef SYSTEM_REGISTER
|
||||||
|
|
||||||
|
#endif /* _SRC__INCLUDE__HW__SPEC__ARM__GIC_V3_H_ */
|
Loading…
Reference in New Issue
Block a user