mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-04 08:30:54 +00:00
platform_drv: implement shared interrupt support
Ref genodelabs/genode#4578
This commit is contained in:
parent
fa124dd340
commit
2ccdbf1050
@ -11,6 +11,8 @@
|
|||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <util/bit_array.h>
|
||||||
|
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
#include <pci.h>
|
#include <pci.h>
|
||||||
#include <device_component.h>
|
#include <device_component.h>
|
||||||
@ -137,6 +139,7 @@ void Driver::Device::generate(Xml_generator & xml, bool info) const
|
|||||||
if (!info)
|
if (!info)
|
||||||
return;
|
return;
|
||||||
xml.attribute("number", irq.number);
|
xml.attribute("number", irq.number);
|
||||||
|
if (irq.shared) xml.attribute("shared", true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
_io_port_range_list.for_each([&] (Io_port_range const & iop) {
|
_io_port_range_list.for_each([&] (Io_port_range const & iop) {
|
||||||
@ -206,6 +209,52 @@ void Driver::Device_model::update(Xml_node const & node)
|
|||||||
{
|
{
|
||||||
_model.update_from_xml(*this, node);
|
_model.update_from_xml(*this, node);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detect all shared interrupts
|
||||||
|
*/
|
||||||
|
enum { MAX_IRQ = 1024 };
|
||||||
|
Bit_array<MAX_IRQ> detected_irqs, shared_irqs;
|
||||||
|
for_each([&] (Device const & device) {
|
||||||
|
device._irq_list.for_each([&] (Device::Irq const & irq) {
|
||||||
|
|
||||||
|
if (irq.type != Device::Irq::LEGACY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (detected_irqs.get(irq.number, 1)) {
|
||||||
|
if (!shared_irqs.get(irq.number, 1))
|
||||||
|
shared_irqs.set(irq.number, 1);
|
||||||
|
} else
|
||||||
|
detected_irqs.set(irq.number, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark all shared interrupts in the devices
|
||||||
|
*/
|
||||||
|
for_each([&] (Device & device) {
|
||||||
|
device._irq_list.for_each([&] (Device::Irq & irq) {
|
||||||
|
|
||||||
|
if (irq.type != Device::Irq::LEGACY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (shared_irqs.get(irq.number, 1))
|
||||||
|
irq.shared = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create shared interrupt objects
|
||||||
|
*/
|
||||||
|
for (unsigned i = 0; i < MAX_IRQ; i++) {
|
||||||
|
if (!shared_irqs.get(i, 1))
|
||||||
|
continue;
|
||||||
|
bool found = false;
|
||||||
|
_shared_irqs.for_each([&] (Shared_interrupt & sirq) {
|
||||||
|
if (sirq.number() == i) found = true; });
|
||||||
|
if (!found)
|
||||||
|
new (_heap) Shared_interrupt(_shared_irqs, _env, i);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iterate over all devices and apply PCI quirks if necessary
|
* Iterate over all devices and apply PCI quirks if necessary
|
||||||
*/
|
*/
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <util/reconstructible.h>
|
#include <util/reconstructible.h>
|
||||||
#include <util/xml_generator.h>
|
#include <util/xml_generator.h>
|
||||||
|
|
||||||
|
#include <shared_irq.h>
|
||||||
#include <clock.h>
|
#include <clock.h>
|
||||||
#include <reset.h>
|
#include <reset.h>
|
||||||
#include <power.h>
|
#include <power.h>
|
||||||
@ -98,6 +99,7 @@ class Driver::Device : private List_model<Device>::Element
|
|||||||
Type type { LEGACY };
|
Type type { LEGACY };
|
||||||
Irq_session::Polarity polarity { Irq_session::POLARITY_UNCHANGED };
|
Irq_session::Polarity polarity { Irq_session::POLARITY_UNCHANGED };
|
||||||
Irq_session::Trigger mode { Irq_session::TRIGGER_UNCHANGED };
|
Irq_session::Trigger mode { Irq_session::TRIGGER_UNCHANGED };
|
||||||
|
bool shared { false };
|
||||||
|
|
||||||
Irq(unsigned number) : number(number) {}
|
Irq(unsigned number) : number(number) {}
|
||||||
};
|
};
|
||||||
@ -222,7 +224,8 @@ class Driver::Device : private List_model<Device>::Element
|
|||||||
{
|
{
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
_irq_list.for_each([&] (Irq const & irq) {
|
_irq_list.for_each([&] (Irq const & irq) {
|
||||||
fn(idx++, irq.number, irq.type, irq.polarity, irq.mode); });
|
fn(idx++, irq.number, irq.type, irq.polarity,
|
||||||
|
irq.mode, irq.shared); });
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FN> void for_each_io_mem(FN const & fn) const
|
template <typename FN> void for_each_io_mem(FN const & fn) const
|
||||||
@ -308,13 +311,14 @@ class Driver::Device_model :
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Env & _env;
|
Env & _env;
|
||||||
Heap & _heap;
|
Heap & _heap;
|
||||||
Device_reporter & _reporter;
|
Device_reporter & _reporter;
|
||||||
List_model<Device> _model { };
|
List_model<Device> _model { };
|
||||||
Clocks _clocks { };
|
Registry<Shared_interrupt> _shared_irqs { };
|
||||||
Resets _resets { };
|
Clocks _clocks { };
|
||||||
Powers _powers { };
|
Resets _resets { };
|
||||||
|
Powers _powers { };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -336,6 +340,13 @@ class Driver::Device_model :
|
|||||||
template <typename FN>
|
template <typename FN>
|
||||||
void for_each(FN const & fn) const { _model.for_each(fn); }
|
void for_each(FN const & fn) const { _model.for_each(fn); }
|
||||||
|
|
||||||
|
template <typename FN>
|
||||||
|
void with_shared_irq(unsigned number, FN const & fn)
|
||||||
|
{
|
||||||
|
_shared_irqs.for_each([&] (Shared_interrupt & sirq) {
|
||||||
|
if (sirq.number() == number) fn(sirq); });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***********************
|
/***********************
|
||||||
** Update_policy API **
|
** Update_policy API **
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <device_component.h>
|
#include <device_component.h>
|
||||||
#include <pci.h>
|
#include <pci.h>
|
||||||
#include <session_component.h>
|
#include <session_component.h>
|
||||||
|
#include <shared_irq.h>
|
||||||
|
|
||||||
using Driver::Device_component;
|
using Driver::Device_component;
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ Genode::Irq_session_capability Device_component::irq(unsigned idx)
|
|||||||
if (irq.idx != idx)
|
if (irq.idx != idx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!irq.irq.constructed()) {
|
if (!irq.shared && !irq.irq.constructed()) {
|
||||||
addr_t pci_cfg_addr = 0;
|
addr_t pci_cfg_addr = 0;
|
||||||
if (irq.type != Device::Irq::LEGACY) {
|
if (irq.type != Device::Irq::LEGACY) {
|
||||||
if (_pci_config.constructed()) pci_cfg_addr = _pci_config->addr;
|
if (_pci_config.constructed()) pci_cfg_addr = _pci_config->addr;
|
||||||
@ -94,7 +95,14 @@ Genode::Irq_session_capability Device_component::irq(unsigned idx)
|
|||||||
pci_msi_enable(_env, pci_cfg_addr, info);
|
pci_msi_enable(_env, pci_cfg_addr, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
cap = irq.irq->cap();
|
if (irq.shared && !irq.sirq.constructed())
|
||||||
|
_device_model.with_shared_irq(irq.number,
|
||||||
|
[&] (Shared_interrupt & sirq) {
|
||||||
|
irq.sirq.construct(sirq, irq.mode, irq.polarity);
|
||||||
|
_env.ep().rpc_ep().manage(&*irq.sirq);
|
||||||
|
});
|
||||||
|
|
||||||
|
cap = irq.shared ? irq.sirq->cap() : irq.irq->cap();
|
||||||
});
|
});
|
||||||
|
|
||||||
return cap;
|
return cap;
|
||||||
@ -124,10 +132,12 @@ Genode::Io_port_session_capability Device_component::io_port_range(unsigned idx)
|
|||||||
Device_component::Device_component(Registry<Device_component> & registry,
|
Device_component::Device_component(Registry<Device_component> & registry,
|
||||||
Env & env,
|
Env & env,
|
||||||
Driver::Session_component & session,
|
Driver::Session_component & session,
|
||||||
|
Driver::Device_model & model,
|
||||||
Driver::Device & device)
|
Driver::Device & device)
|
||||||
:
|
:
|
||||||
_env(env),
|
_env(env),
|
||||||
_session(session),
|
_session(session),
|
||||||
|
_device_model(model),
|
||||||
_device(device.name()),
|
_device(device.name()),
|
||||||
_reg_elem(registry, *this)
|
_reg_elem(registry, *this)
|
||||||
{
|
{
|
||||||
@ -150,13 +160,15 @@ Device_component::Device_component(Registry<Device_component> & registry,
|
|||||||
unsigned nr,
|
unsigned nr,
|
||||||
Device::Irq::Type type,
|
Device::Irq::Type type,
|
||||||
Irq_session::Polarity polarity,
|
Irq_session::Polarity polarity,
|
||||||
Irq_session::Trigger mode)
|
Irq_session::Trigger mode,
|
||||||
|
bool shared)
|
||||||
{
|
{
|
||||||
session.ram_quota_guard().withdraw(Ram_quota{Irq_session::RAM_QUOTA});
|
session.ram_quota_guard().withdraw(Ram_quota{Irq_session::RAM_QUOTA});
|
||||||
_ram_quota += Irq_session::RAM_QUOTA;
|
_ram_quota += Irq_session::RAM_QUOTA;
|
||||||
session.cap_quota_guard().withdraw(Cap_quota{Irq_session::CAP_QUOTA});
|
session.cap_quota_guard().withdraw(Cap_quota{Irq_session::CAP_QUOTA});
|
||||||
_cap_quota += Irq_session::CAP_QUOTA;
|
_cap_quota += Irq_session::CAP_QUOTA;
|
||||||
new (session.heap()) Irq(_irq_registry, idx, nr, type, polarity, mode);
|
new (session.heap()) Irq(_irq_registry, idx, nr, type, polarity,
|
||||||
|
mode, shared);
|
||||||
});
|
});
|
||||||
|
|
||||||
device.for_each_io_mem([&] (unsigned idx, Range range,
|
device.for_each_io_mem([&] (unsigned idx, Range range,
|
||||||
|
@ -38,23 +38,26 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
|
|||||||
|
|
||||||
struct Irq : Registry<Irq>::Element
|
struct Irq : Registry<Irq>::Element
|
||||||
{
|
{
|
||||||
unsigned idx;
|
unsigned idx;
|
||||||
unsigned number;
|
unsigned number;
|
||||||
Device::Irq::Type type;
|
Device::Irq::Type type;
|
||||||
Irq_session::Polarity polarity;
|
Irq_session::Polarity polarity;
|
||||||
Irq_session::Trigger mode;
|
Irq_session::Trigger mode;
|
||||||
Constructible<Irq_connection> irq {};
|
bool shared;
|
||||||
|
Constructible<Irq_connection> irq {};
|
||||||
|
Constructible<Shared_interrupt_session> sirq {};
|
||||||
|
|
||||||
Irq(Registry<Irq> & registry,
|
Irq(Registry<Irq> & registry,
|
||||||
unsigned idx,
|
unsigned idx,
|
||||||
unsigned number,
|
unsigned number,
|
||||||
Device::Irq::Type type,
|
Device::Irq::Type type,
|
||||||
Irq_session::Polarity polarity,
|
Irq_session::Polarity polarity,
|
||||||
Irq_session::Trigger mode)
|
Irq_session::Trigger mode,
|
||||||
|
bool shared)
|
||||||
:
|
:
|
||||||
Registry<Irq>::Element(registry, *this),
|
Registry<Irq>::Element(registry, *this),
|
||||||
idx(idx), number(number), type(type),
|
idx(idx), number(number), type(type),
|
||||||
polarity(polarity), mode(mode) {}
|
polarity(polarity), mode(mode), shared(shared) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Io_mem : Registry<Io_mem>::Element
|
struct Io_mem : Registry<Io_mem>::Element
|
||||||
@ -99,6 +102,7 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
|
|||||||
Device_component(Registry<Device_component> & registry,
|
Device_component(Registry<Device_component> & registry,
|
||||||
Env & env,
|
Env & env,
|
||||||
Session_component & session,
|
Session_component & session,
|
||||||
|
Device_model & model,
|
||||||
Driver::Device & device);
|
Driver::Device & device);
|
||||||
~Device_component();
|
~Device_component();
|
||||||
|
|
||||||
@ -118,6 +122,7 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
|
|||||||
|
|
||||||
Env & _env;
|
Env & _env;
|
||||||
Session_component & _session;
|
Session_component & _session;
|
||||||
|
Device_model & _device_model;
|
||||||
Driver::Device::Name const _device;
|
Driver::Device::Name const _device;
|
||||||
size_t _cap_quota { 0 };
|
size_t _cap_quota { 0 };
|
||||||
size_t _ram_quota { 0 };
|
size_t _ram_quota { 0 };
|
||||||
|
@ -24,7 +24,7 @@ Genode::Capability<Platform::Device_interface>
|
|||||||
Session_component::_acquire(Device & device)
|
Session_component::_acquire(Device & device)
|
||||||
{
|
{
|
||||||
Device_component * dc = new (heap())
|
Device_component * dc = new (heap())
|
||||||
Device_component(_device_registry, _env, *this, device);
|
Device_component(_device_registry, _env, *this, _devices, device);
|
||||||
device.acquire(*this);
|
device.acquire(*this);
|
||||||
return _env.ep().rpc_ep().manage(dc);
|
return _env.ep().rpc_ep().manage(dc);
|
||||||
};
|
};
|
||||||
|
65
repos/os/src/drivers/platform/shared_irq.cc
Normal file
65
repos/os/src/drivers/platform/shared_irq.cc
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* \brief Platform driver - shared interrupts
|
||||||
|
* \author Stefan Kalkowski
|
||||||
|
* \date 2022-09-27
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 <shared_irq.h>
|
||||||
|
|
||||||
|
using Driver::Shared_interrupt;
|
||||||
|
using Driver::Shared_interrupt_session;
|
||||||
|
|
||||||
|
|
||||||
|
void Shared_interrupt::_handle()
|
||||||
|
{
|
||||||
|
_sessions.for_each([&] (Shared_interrupt_session & session) {
|
||||||
|
session.signal(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Shared_interrupt::enable(Irq_session::Trigger mode,
|
||||||
|
Irq_session::Polarity polarity)
|
||||||
|
{
|
||||||
|
if (!_irq.constructed()) {
|
||||||
|
_irq.construct(_env, _number, mode, polarity);
|
||||||
|
_irq->sigh(_handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Shared_interrupt::disable()
|
||||||
|
{
|
||||||
|
unsigned session_count = 0;
|
||||||
|
_sessions.for_each([&] (Shared_interrupt_session &) {
|
||||||
|
session_count++; });
|
||||||
|
|
||||||
|
/* if it is the last session, close the upstream connection */
|
||||||
|
if (session_count <= 1)
|
||||||
|
_irq.destruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Shared_interrupt::ack()
|
||||||
|
{
|
||||||
|
unsigned outstanding = 0;
|
||||||
|
_sessions.for_each([&] (Shared_interrupt_session & session) {
|
||||||
|
if (session.outstanding()) outstanding++; });
|
||||||
|
if (!outstanding) _irq->ack_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Shared_interrupt_session::signal()
|
||||||
|
{
|
||||||
|
if (!_cap.valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_outstanding = true;
|
||||||
|
Signal_transmitter(_cap).submit(1);
|
||||||
|
}
|
110
repos/os/src/drivers/platform/shared_irq.h
Normal file
110
repos/os/src/drivers/platform/shared_irq.h
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* \brief Platform driver - shared interrupts
|
||||||
|
* \author Stefan Kalkowski
|
||||||
|
* \date 2022-09-27
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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__DRIVERS__PLATFORM__SHARED_IRQ_H_
|
||||||
|
#define _SRC__DRIVERS__PLATFORM__SHARED_IRQ_H_
|
||||||
|
|
||||||
|
#include <base/registry.h>
|
||||||
|
#include <base/rpc_server.h>
|
||||||
|
#include <irq_session/connection.h>
|
||||||
|
|
||||||
|
namespace Driver {
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
class Shared_interrupt;
|
||||||
|
class Shared_interrupt_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Driver::Shared_interrupt :
|
||||||
|
private Registry<Shared_interrupt>::Element
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Env & _env;
|
||||||
|
unsigned _number;
|
||||||
|
Io_signal_handler<Shared_interrupt> _handler;
|
||||||
|
Constructible<Irq_connection> _irq {};
|
||||||
|
Registry<Shared_interrupt_session> _sessions {};
|
||||||
|
|
||||||
|
void _handle();
|
||||||
|
|
||||||
|
friend class Shared_interrupt_session;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Shared_interrupt(Registry<Shared_interrupt> & registry,
|
||||||
|
Env & env,
|
||||||
|
unsigned number)
|
||||||
|
:
|
||||||
|
Registry<Shared_interrupt>::Element(registry, *this),
|
||||||
|
_env(env),
|
||||||
|
_number(number),
|
||||||
|
_handler(env.ep(), *this, &Shared_interrupt::_handle) {}
|
||||||
|
|
||||||
|
unsigned number() const { return _number; }
|
||||||
|
|
||||||
|
void enable(Irq_session::Trigger mode,
|
||||||
|
Irq_session::Polarity polarity);
|
||||||
|
void disable();
|
||||||
|
void ack();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Driver::Shared_interrupt_session :
|
||||||
|
public Rpc_object<Irq_session, Shared_interrupt_session>,
|
||||||
|
private Registry<Shared_interrupt_session>::Element
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Shared_interrupt & _sirq;
|
||||||
|
Signal_context_capability _cap {};
|
||||||
|
bool _outstanding { true };
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Shared_interrupt_session(Shared_interrupt & sirq,
|
||||||
|
Irq_session::Trigger mode,
|
||||||
|
Irq_session::Polarity polarity)
|
||||||
|
:
|
||||||
|
Registry<Shared_interrupt_session>::Element(sirq._sessions, *this),
|
||||||
|
_sirq(sirq)
|
||||||
|
{
|
||||||
|
sirq.enable(mode, polarity);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Shared_interrupt_session() { _sirq.disable(); }
|
||||||
|
|
||||||
|
bool outstanding() { return _outstanding; }
|
||||||
|
void signal();
|
||||||
|
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
** Irq session interface **
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
void ack_irq() override
|
||||||
|
{
|
||||||
|
_outstanding = false;
|
||||||
|
_sirq.ack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sigh(Signal_context_capability cap) override { _cap = cap; }
|
||||||
|
|
||||||
|
Info info() override
|
||||||
|
{
|
||||||
|
return { .type = Info::Type::INVALID, .address = 0, .value = 0 };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _SRC__DRIVERS__PLATFORM__SHARED_IRQ_H_ */
|
@ -6,6 +6,7 @@ SRC_CC += main.cc
|
|||||||
SRC_CC += pci.cc
|
SRC_CC += pci.cc
|
||||||
SRC_CC += root.cc
|
SRC_CC += root.cc
|
||||||
SRC_CC += session_component.cc
|
SRC_CC += session_component.cc
|
||||||
|
SRC_CC += shared_irq.cc
|
||||||
|
|
||||||
GENERIC_DIR := $(dir $(call select_from_repositories,src/drivers/platform/target.inc))
|
GENERIC_DIR := $(dir $(call select_from_repositories,src/drivers/platform/target.inc))
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user