mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 23:42:32 +00:00
hw_x86_64: Support for dynamic IRQ mode setting
Add a Platform::setup_irq_mode function which enables the IRQ session to update the trigger mode and polarity of the associated IRQ according to the session parameters. On ARM this function is a nop. This change enables the x86_64 platform to support devices which use arbitrary trigger modes and polarity settings, e.g. AHCI on QEMU and real hardware. Fixes #1528.
This commit is contained in:
parent
1592e78387
commit
965d85d52d
@ -121,6 +121,16 @@ namespace Genode {
|
|||||||
*/
|
*/
|
||||||
static long irq(long const user_irq);
|
static long irq(long const user_irq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup mode of an IRQ to specified trigger mode and polarity
|
||||||
|
*
|
||||||
|
* \param irq_number ID of targeted interrupt
|
||||||
|
* \param trigger new interrupt trigger mode
|
||||||
|
* \param polarity new interrupt polarity setting
|
||||||
|
*/
|
||||||
|
static void setup_irq_mode(unsigned irq_number, unsigned trigger,
|
||||||
|
unsigned polarity);
|
||||||
|
|
||||||
/********************************
|
/********************************
|
||||||
** Platform_generic interface **
|
** Platform_generic interface **
|
||||||
********************************/
|
********************************/
|
||||||
|
@ -36,6 +36,8 @@ namespace Genode
|
|||||||
* Programmable interrupt controller for core
|
* Programmable interrupt controller for core
|
||||||
*/
|
*/
|
||||||
class Pic;
|
class Pic;
|
||||||
|
|
||||||
|
enum { IRQ_COUNT = 256 };
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Genode::Irte : Register<64>
|
struct Genode::Irte : Register<64>
|
||||||
@ -60,17 +62,52 @@ class Genode::Ioapic : public Mmio
|
|||||||
/* Register selectors */
|
/* Register selectors */
|
||||||
IOAPICVER = 0x01,
|
IOAPICVER = 0x01,
|
||||||
IOREDTBL = 0x10,
|
IOREDTBL = 0x10,
|
||||||
|
|
||||||
|
/* IRQ modes */
|
||||||
|
TRIGGER_EDGE = 0,
|
||||||
|
TRIGGER_LEVEL = 1,
|
||||||
|
POLARITY_HIGH = 0,
|
||||||
|
POLARITY_LOW = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IRQ mode specifies trigger mode and polarity of an IRQ
|
||||||
|
*/
|
||||||
|
struct Irq_mode
|
||||||
|
{
|
||||||
|
unsigned trigger_mode;
|
||||||
|
unsigned polarity;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Irq_mode _irq_mode[IRQ_COUNT];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether 'irq' is an edge-triggered interrupt
|
* Return whether 'irq' is an edge-triggered interrupt
|
||||||
*/
|
*/
|
||||||
bool _edge_triggered(unsigned const irq)
|
bool _edge_triggered(unsigned const irq)
|
||||||
{
|
{
|
||||||
if ((irq >= 0 && irq <= 8) || (irq >= 12 && irq <= Board::ISA_IRQ_END))
|
return _irq_mode[irq].trigger_mode == TRIGGER_EDGE;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
return false;
|
/**
|
||||||
|
* Update IRT entry of given IRQ
|
||||||
|
*
|
||||||
|
* Note: The polarity and trigger flags are located in the lower
|
||||||
|
* 32 bits so only the necessary half of the IRT entry is
|
||||||
|
* updated.
|
||||||
|
*/
|
||||||
|
void _update_irt_entry(unsigned irq)
|
||||||
|
{
|
||||||
|
Irte::access_t irte;
|
||||||
|
|
||||||
|
write<Ioregsel>(IOREDTBL + 2 * irq);
|
||||||
|
irte = read<Iowin>();
|
||||||
|
|
||||||
|
Irte::Pol::set(irte, _irq_mode[irq].polarity);
|
||||||
|
Irte::Trg::set(irte, _irq_mode[irq].trigger_mode);
|
||||||
|
|
||||||
|
write<Ioregsel>(IOREDTBL + 2 * irq);
|
||||||
|
write<Iowin>(irte);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,11 +118,9 @@ class Genode::Ioapic : public Mmio
|
|||||||
Irte::access_t irte = REMAP_BASE + irq;
|
Irte::access_t irte = REMAP_BASE + irq;
|
||||||
Irte::Mask::set(irte, 1);
|
Irte::Mask::set(irte, 1);
|
||||||
|
|
||||||
/* Use level-triggered, low-active mode for non-legacy IRQs */
|
Irte::Pol::set(irte, _irq_mode[irq].polarity);
|
||||||
if (!_edge_triggered(irq)) {
|
Irte::Trg::set(irte, _irq_mode[irq].trigger_mode);
|
||||||
Irte::Pol::set(irte, 1);
|
|
||||||
Irte::Trg::set(irte, 1);
|
|
||||||
}
|
|
||||||
return irte;
|
return irte;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,17 +128,34 @@ class Genode::Ioapic : public Mmio
|
|||||||
|
|
||||||
Ioapic() : Mmio(Board::MMIO_IOAPIC_BASE)
|
Ioapic() : Mmio(Board::MMIO_IOAPIC_BASE)
|
||||||
{
|
{
|
||||||
/* Remap all supported IRQs */
|
for (unsigned i = 0; i < IRQ_COUNT; i++)
|
||||||
for (unsigned i = 0; i <= IRTE_COUNT; i++) {
|
{
|
||||||
Irte::access_t irte = _create_irt_entry(i);
|
/* set legacy/ISA IRQs to edge, high */
|
||||||
write<Ioregsel>(IOREDTBL + 2 * i + 1);
|
if (i <= Board::ISA_IRQ_END) {
|
||||||
write<Iowin>(irte >> Iowin::ACCESS_WIDTH);
|
_irq_mode[i].trigger_mode = TRIGGER_EDGE;
|
||||||
write<Ioregsel>(IOREDTBL + 2 * i);
|
_irq_mode[i].polarity = POLARITY_HIGH;
|
||||||
write<Iowin>(irte);
|
} else {
|
||||||
|
_irq_mode[i].trigger_mode = TRIGGER_LEVEL;
|
||||||
|
_irq_mode[i].polarity = POLARITY_LOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remap all IRQs managed by I/O APIC */
|
||||||
|
if (i <= IRTE_COUNT) {
|
||||||
|
Irte::access_t irte = _create_irt_entry(i);
|
||||||
|
write<Ioregsel>(IOREDTBL + 2 * i + 1);
|
||||||
|
write<Iowin>(irte >> Iowin::ACCESS_WIDTH);
|
||||||
|
write<Ioregsel>(IOREDTBL + 2 * i);
|
||||||
|
write<Iowin>(irte);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Set/unset mask bit of IRTE for given vector */
|
/**
|
||||||
|
* Set/unset mask bit of IRTE for given vector
|
||||||
|
*
|
||||||
|
* \param vector targeted vector
|
||||||
|
* \param set whether to set or to unset the mask bit
|
||||||
|
*/
|
||||||
void toggle_mask(unsigned const vector, bool const set)
|
void toggle_mask(unsigned const vector, bool const set)
|
||||||
{
|
{
|
||||||
const unsigned irq = vector - REMAP_BASE;
|
const unsigned irq = vector - REMAP_BASE;
|
||||||
@ -124,7 +176,20 @@ class Genode::Ioapic : public Mmio
|
|||||||
write<Iowin>(irte);
|
write<Iowin>(irte);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Registers */
|
/**
|
||||||
|
* Setup mode of an IRQ to specified trigger mode and polarity
|
||||||
|
*
|
||||||
|
* \param irq_number ID of targeted interrupt
|
||||||
|
* \param trigger new interrupt trigger mode
|
||||||
|
* \param polarity new interrupt polarity setting
|
||||||
|
*/
|
||||||
|
void setup_irq_mode(unsigned irq_number, unsigned trigger,
|
||||||
|
unsigned polarity);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Registers
|
||||||
|
*/
|
||||||
|
|
||||||
struct Ioregsel : Register<0x00, 32> { };
|
struct Ioregsel : Register<0x00, 32> { };
|
||||||
struct Iowin : Register<0x10, 32> { };
|
struct Iowin : Register<0x10, 32> { };
|
||||||
};
|
};
|
||||||
@ -133,7 +198,10 @@ class Genode::Pic : public Mmio
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/* Registers */
|
/*
|
||||||
|
* Registers
|
||||||
|
*/
|
||||||
|
|
||||||
struct EOI : Register<0x0b0, 32, true> { };
|
struct EOI : Register<0x0b0, 32, true> { };
|
||||||
struct Svr : Register<0x0f0, 32>
|
struct Svr : Register<0x0f0, 32>
|
||||||
{
|
{
|
||||||
@ -141,13 +209,12 @@ class Genode::Pic : public Mmio
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ISR register, see Intel SDM Vol. 3A, section 10.8.4. Each of the 8
|
* ISR register, see Intel SDM Vol. 3A, section 10.8.4.
|
||||||
* 32-bit ISR values is followed by 12 bytes of padding.
|
*
|
||||||
|
* Each of the 8 32-bit ISR values is followed by 12 bytes of padding.
|
||||||
*/
|
*/
|
||||||
struct Isr : Register_array<0x100, 32, 8 * 4, 32> { };
|
struct Isr : Register_array<0x100, 32, 8 * 4, 32> { };
|
||||||
|
|
||||||
Ioapic _ioapic;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine lowest pending interrupt in ISR register
|
* Determine lowest pending interrupt in ISR register
|
||||||
*
|
*
|
||||||
@ -177,7 +244,7 @@ class Genode::Pic : public Mmio
|
|||||||
* necessary
|
* necessary
|
||||||
*/
|
*/
|
||||||
IPI = 255,
|
IPI = 255,
|
||||||
NR_OF_IRQ = 256,
|
NR_OF_IRQ = IRQ_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,6 +252,8 @@ class Genode::Pic : public Mmio
|
|||||||
*/
|
*/
|
||||||
Pic();
|
Pic();
|
||||||
|
|
||||||
|
Ioapic ioapic;
|
||||||
|
|
||||||
bool take_request(unsigned &irq);
|
bool take_request(unsigned &irq);
|
||||||
|
|
||||||
void finish_request();
|
void finish_request();
|
||||||
|
@ -78,4 +78,46 @@ Irq_session_component::Irq_session_component(Range_allocator * const irq_alloc,
|
|||||||
PERR("unavailable interrupt requested");
|
PERR("unavailable interrupt requested");
|
||||||
throw Root::Invalid_args();
|
throw Root::Invalid_args();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long irq_trg = Arg_string::find_arg(args, "irq_trigger").long_value(-1);
|
||||||
|
long irq_pol = Arg_string::find_arg(args, "irq_polarity").long_value(-1);
|
||||||
|
|
||||||
|
Irq_session::Trigger irq_trigger;
|
||||||
|
Irq_session::Polarity irq_polarity;
|
||||||
|
|
||||||
|
switch(irq_trg) {
|
||||||
|
case -1:
|
||||||
|
case Irq_session::TRIGGER_UNCHANGED:
|
||||||
|
irq_trigger = Irq_session::TRIGGER_UNCHANGED;
|
||||||
|
break;
|
||||||
|
case Irq_session::TRIGGER_EDGE:
|
||||||
|
irq_trigger = Irq_session::TRIGGER_EDGE;
|
||||||
|
break;
|
||||||
|
case Irq_session::TRIGGER_LEVEL:
|
||||||
|
irq_trigger = Irq_session::TRIGGER_LEVEL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PERR("invalid trigger mode %ld specified for IRQ %u", irq_trg,
|
||||||
|
_irq_number);
|
||||||
|
throw Root::Unavailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(irq_pol) {
|
||||||
|
case -1:
|
||||||
|
case POLARITY_UNCHANGED:
|
||||||
|
irq_polarity = POLARITY_UNCHANGED;
|
||||||
|
break;
|
||||||
|
case POLARITY_HIGH:
|
||||||
|
irq_polarity = POLARITY_HIGH;
|
||||||
|
break;
|
||||||
|
case POLARITY_LOW:
|
||||||
|
irq_polarity = POLARITY_LOW;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PERR("invalid polarity %ld specified for IRQ %u", irq_pol,
|
||||||
|
_irq_number);
|
||||||
|
throw Root::Unavailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform::setup_irq_mode(_irq_number, irq_trigger, irq_polarity);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ using namespace Genode;
|
|||||||
|
|
||||||
void Platform::_init_io_port_alloc() { };
|
void Platform::_init_io_port_alloc() { };
|
||||||
|
|
||||||
|
void Platform::setup_irq_mode(unsigned, unsigned, unsigned) { }
|
||||||
|
|
||||||
Native_region * mmio_regions(unsigned);
|
Native_region * mmio_regions(unsigned);
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,8 +12,11 @@
|
|||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <port_io.h>
|
/* Genode includes */
|
||||||
|
#include <irq_session/irq_session.h>
|
||||||
|
|
||||||
|
/* core includes */
|
||||||
|
#include <port_io.h>
|
||||||
#include "pic.h"
|
#include "pic.h"
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
@ -72,10 +75,51 @@ void Pic::finish_request()
|
|||||||
|
|
||||||
void Pic::unmask(unsigned const i, unsigned)
|
void Pic::unmask(unsigned const i, unsigned)
|
||||||
{
|
{
|
||||||
_ioapic.toggle_mask(i, false);
|
ioapic.toggle_mask(i, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pic::mask(unsigned const i)
|
void Pic::mask(unsigned const i)
|
||||||
{
|
{
|
||||||
_ioapic.toggle_mask(i, true);
|
ioapic.toggle_mask(i, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ioapic::Irq_mode Ioapic::_irq_mode[IRQ_COUNT];
|
||||||
|
|
||||||
|
void Ioapic::setup_irq_mode(unsigned irq_number, unsigned trigger,
|
||||||
|
unsigned polarity)
|
||||||
|
{
|
||||||
|
const unsigned irq_nr = irq_number - REMAP_BASE;
|
||||||
|
bool needs_sync = false;
|
||||||
|
|
||||||
|
switch (trigger) {
|
||||||
|
case Irq_session::TRIGGER_EDGE:
|
||||||
|
_irq_mode[irq_nr].trigger_mode = TRIGGER_EDGE;
|
||||||
|
needs_sync = true;
|
||||||
|
break;
|
||||||
|
case Irq_session::TRIGGER_LEVEL:
|
||||||
|
_irq_mode[irq_nr].trigger_mode = TRIGGER_LEVEL;
|
||||||
|
needs_sync = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Do nothing */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (polarity) {
|
||||||
|
case Irq_session::POLARITY_HIGH:
|
||||||
|
_irq_mode[irq_nr].polarity = POLARITY_HIGH;
|
||||||
|
needs_sync = true;
|
||||||
|
break;
|
||||||
|
case Irq_session::POLARITY_LOW:
|
||||||
|
_irq_mode[irq_nr].polarity = POLARITY_LOW;
|
||||||
|
needs_sync = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Do nothing */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update IR table if IRQ mode changed */
|
||||||
|
if (needs_sync)
|
||||||
|
_update_irt_entry(irq_nr);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
#include <board.h>
|
#include <board.h>
|
||||||
#include <cpu.h>
|
#include <cpu.h>
|
||||||
|
#include <pic.h>
|
||||||
|
#include <kernel/kernel.h>
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
@ -75,3 +77,10 @@ long Platform::irq(long const user_irq)
|
|||||||
if (user_irq) return user_irq + Board::VECTOR_REMAP_BASE;
|
if (user_irq) return user_irq + Board::VECTOR_REMAP_BASE;
|
||||||
return Board::TIMER_VECTOR_USER;
|
return Board::TIMER_VECTOR_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Platform::setup_irq_mode(unsigned irq_number, unsigned trigger,
|
||||||
|
unsigned polarity)
|
||||||
|
{
|
||||||
|
Kernel::pic()->ioapic.setup_irq_mode(irq_number, trigger, polarity);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user