lx_kit: support enable/disable IRQ and pass IRQ number to handler

Fixes #3589
This commit is contained in:
Christian Prochaska 2019-12-13 13:51:35 +01:00 committed by Christian Helmuth
parent 18f90ca1e3
commit 0eaa1f7a08
9 changed files with 107 additions and 25 deletions

View File

@ -1094,7 +1094,7 @@ int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
{ {
struct drm_device * drm_dev = (struct drm_device*) dev; struct drm_device * drm_dev = (struct drm_device*) dev;
Lx::Pci_dev * pci_dev = (Lx::Pci_dev*) drm_dev->pdev->bus; Lx::Pci_dev * pci_dev = (Lx::Pci_dev*) drm_dev->pdev->bus;
Lx::Irq::irq().request_irq(pci_dev->client(), handler, dev); Lx::Irq::irq().request_irq(pci_dev->client(), irq, handler, dev);
return 0; return 0;
} }

View File

@ -586,7 +586,7 @@ int platform_get_irq(struct platform_device * d, unsigned int i)
int devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) int devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
{ {
Lx::Irq::irq().request_irq(Platform::Device::create(Lx_kit::env().env(), irq), handler, dev_id); Lx::Irq::irq().request_irq(Platform::Device::create(Lx_kit::env().env(), irq), irq, handler, dev_id);
return 0; return 0;
} }

View File

@ -45,7 +45,7 @@ void Lx::backend_free(Genode::Ram_dataspace_capability cap) {
extern "C" int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, extern "C" int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev) const char *name, void *dev)
{ {
Lx::Irq::irq().request_irq(Platform::Device::create(Lx_kit::env().env(), irq), handler, dev); Lx::Irq::irq().request_irq(Platform::Device::create(Lx_kit::env().env(), irq), irq, handler, dev);
return 0; return 0;
} }

View File

@ -187,7 +187,7 @@ int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
{ {
for (Lx::Pci_dev *pci_dev = Lx::pci_dev_registry()->first(); pci_dev; pci_dev = pci_dev->next()) for (Lx::Pci_dev *pci_dev = Lx::pci_dev_registry()->first(); pci_dev; pci_dev = pci_dev->next())
if (pci_dev->irq == irq) { if (pci_dev->irq == irq) {
Lx::Irq::irq().request_irq(pci_dev->client(), handler, dev); Lx::Irq::irq().request_irq(pci_dev->client(), irq, handler, dev);
return 0; return 0;
} }

View File

@ -48,7 +48,7 @@ void Lx::backend_free(Genode::Ram_dataspace_capability cap) {
extern "C" int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, extern "C" int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev) const char *name, void *dev)
{ {
Lx::Irq::irq().request_irq(Platform::Device::create(Lx_kit::env().env(), irq), handler, dev); Lx::Irq::irq().request_irq(Platform::Device::create(Lx_kit::env().env(), irq), irq, handler, dev);
return 0; return 0;
} }
@ -56,7 +56,7 @@ extern "C" int request_irq(unsigned int irq, irq_handler_t handler, unsigned lon
int devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) int devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
{ {
Lx::Irq::irq().request_irq(Platform::Device::create(Lx_kit::env().env(), irq), handler, dev_id); Lx::Irq::irq().request_irq(Platform::Device::create(Lx_kit::env().env(), irq), irq, handler, dev_id);
return 0; return 0;
} }

View File

@ -194,7 +194,7 @@ int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
if ((is.info().type != Genode::Irq_session::Info::MSI) if ((is.info().type != Genode::Irq_session::Info::MSI)
&& !flags) return 1; && !flags) return 1;
} }
Lx::Irq::irq().request_irq(pci_dev->client(), handler, dev); Lx::Irq::irq().request_irq(pci_dev->client(), irq, handler, dev);
return 0; return 0;
} }

View File

@ -32,10 +32,21 @@ class Lx::Irq
/** /**
* Request an IRQ * Request an IRQ
*/ */
virtual void request_irq(Platform::Device &dev, irq_handler_t handler, virtual void request_irq(Platform::Device &dev, unsigned int irq,
void *dev_id, irq_handler_t thread_fn = 0) = 0; irq_handler_t handler, void *dev_id,
irq_handler_t thread_fn = 0) = 0;
virtual void inject_irq(Platform::Device &dev) = 0; virtual void inject_irq(Platform::Device &dev) = 0;
/**
* Disable an IRQ
*/
virtual void disable_irq(unsigned int irq) = 0;
/**
* Enable an IRQ
*/
virtual void enable_irq(unsigned int irq) = 0;
}; };
#endif /* _LX_KIT__IRQ_H_ */ #endif /* _LX_KIT__IRQ_H_ */

View File

@ -1333,7 +1333,7 @@ int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev) unsigned long flags, const char *name, void *dev)
{ {
Lx::Pci_dev *pci_dev = Lx::pci_dev_registry()->first(); Lx::Pci_dev *pci_dev = Lx::pci_dev_registry()->first();
Lx::Irq::irq().request_irq(pci_dev->client(), handler, dev); Lx::Irq::irq().request_irq(pci_dev->client(), irq, handler, dev);
return 0; return 0;
} }
@ -1344,7 +1344,7 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
void *dev) void *dev)
{ {
Lx::Pci_dev *pci_dev = Lx::pci_dev_registry()->first(); Lx::Pci_dev *pci_dev = Lx::pci_dev_registry()->first();
Lx::Irq::irq().request_irq(pci_dev->client(), handler, dev, thread_fn); Lx::Irq::irq().request_irq(pci_dev->client(), irq, handler, dev, thread_fn);
return 0; return 0;
} }

View File

@ -58,20 +58,28 @@ class Lx_kit::Irq : public Lx::Irq
private: private:
void *_dev; /* Linux device */ void *_dev; /* Linux device */
unsigned int _irq; /* Linux IRQ number */
irq_handler_t _handler; /* Linux handler */ irq_handler_t _handler; /* Linux handler */
irq_handler_t _thread_fn; /* Linux thread function */ irq_handler_t _thread_fn; /* Linux thread function */
public: public:
Handler(void *dev, irq_handler_t handler, Handler(void *dev, unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn) irq_handler_t thread_fn)
: _dev(dev), _handler(handler), _thread_fn(thread_fn) { } : _dev(dev), _irq(irq), _handler(handler),
_thread_fn(thread_fn) { }
bool handle() bool handle()
{ {
switch (_handler(0, _dev)) { if (!_handler) {
/* on Linux, having no handler implies IRQ_WAKE_THREAD */
_thread_fn(_irq, _dev);
return true;
}
switch (_handler(_irq, _dev)) {
case IRQ_WAKE_THREAD: case IRQ_WAKE_THREAD:
_thread_fn(0, _dev); _thread_fn(_irq, _dev);
case IRQ_HANDLED: case IRQ_HANDLED:
return true; return true;
case IRQ_NONE: case IRQ_NONE:
@ -92,9 +100,12 @@ class Lx_kit::Irq : public Lx::Irq
Name_composer _name; Name_composer _name;
Platform::Device &_dev; Platform::Device &_dev;
unsigned int _irq;
Genode::Irq_session_client _irq_sess; Genode::Irq_session_client _irq_sess;
Lx_kit::List<Handler> _handler; Lx_kit::List<Handler> _handler;
Lx::Task _task; Lx::Task _task;
bool _irq_enabled;
bool _irq_ack_pending;
Genode::Signal_handler<Context> _dispatcher; Genode::Signal_handler<Context> _dispatcher;
@ -114,12 +125,16 @@ class Lx_kit::Irq : public Lx::Irq
* Constructor * Constructor
*/ */
Context(Genode::Entrypoint &ep, Context(Genode::Entrypoint &ep,
Platform::Device &dev) Platform::Device &dev,
unsigned int irq)
: :
_name(dev), _name(dev),
_dev(dev), _dev(dev),
_irq(irq),
_irq_sess(dev.irq(0)), _irq_sess(dev.irq(0)),
_task(_run_irq, this, _name.name, Lx::Task::PRIORITY_3, Lx::scheduler()), _task(_run_irq, this, _name.name, Lx::Task::PRIORITY_3, Lx::scheduler()),
_irq_enabled(true),
_irq_ack_pending(false),
_dispatcher(ep, *this, &Context::unblock) _dispatcher(ep, *this, &Context::unblock)
{ {
_irq_sess.sigh(_dispatcher); _irq_sess.sigh(_dispatcher);
@ -144,12 +159,24 @@ class Lx_kit::Irq : public Lx::Irq
*/ */
void handle_irq() void handle_irq()
{ {
/* report IRQ to all clients */ if (_irq_enabled) {
for (Handler *h = _handler.first(); h; h = h->next()) {
h->handle();
}
_irq_sess.ack_irq(); /* report IRQ to all clients */
for (Handler *h = _handler.first(); h; h = h->next())
h->handle();
_irq_sess.ack_irq();
} else {
/*
* IRQs are disabled by not acknowledging, so one IRQ
* can still occur in the 'disabled' state. It must be
* acknowledged later by 'enable_irq()'.
*/
_irq_ack_pending = true;
}
} }
/** /**
@ -159,6 +186,27 @@ class Lx_kit::Irq : public Lx::Irq
bool device(Platform::Device &dev) { bool device(Platform::Device &dev) {
return (&dev == &_dev); } return (&dev == &_dev); }
bool irq(unsigned int irq) {
return (irq == _irq); }
void disable_irq()
{
_irq_enabled = false;
}
void enable_irq()
{
if (_irq_enabled)
return;
if (_irq_ack_pending) {
_irq_sess.ack_irq();
_irq_ack_pending = false;
}
_irq_enabled = true;
}
}; };
private: private:
@ -181,6 +229,16 @@ class Lx_kit::Irq : public Lx::Irq
return nullptr; return nullptr;
} }
/**
* Find context for given IRQ number
*/
Context *_find_context(unsigned int irq)
{
for (Context *i = _list.first(); i; i = i->next())
if (i->irq(irq)) return i;
return nullptr;
}
Irq(Genode::Entrypoint &ep, Genode::Allocator &alloc) Irq(Genode::Entrypoint &ep, Genode::Allocator &alloc)
: _ep(ep), : _ep(ep),
_context_alloc(&alloc), _context_alloc(&alloc),
@ -198,20 +256,21 @@ class Lx_kit::Irq : public Lx::Irq
** Lx::Irq interface ** ** Lx::Irq interface **
***********************/ ***********************/
void request_irq(Platform::Device &dev, irq_handler_t handler, void request_irq(Platform::Device &dev, unsigned int irq,
void *dev_id, irq_handler_t thread_fn = 0) override irq_handler_t handler, void *dev_id,
irq_handler_t thread_fn = 0) override
{ {
Context *ctx = _find_context(dev); Context *ctx = _find_context(dev);
/* if this IRQ is not registered */ /* if this IRQ is not registered */
if (!ctx) { if (!ctx) {
ctx = new (&_context_alloc) Context(_ep, dev); ctx = new (&_context_alloc) Context(_ep, dev, irq);
_list.insert(ctx); _list.insert(ctx);
} }
/* register Linux handler */ /* register Linux handler */
Handler *h = new (&_handler_alloc) Handler *h = new (&_handler_alloc)
Handler(dev_id, handler, thread_fn); Handler(dev_id, irq, handler, thread_fn);
ctx->add_handler(h); ctx->add_handler(h);
} }
@ -220,6 +279,18 @@ class Lx_kit::Irq : public Lx::Irq
Context *ctx = _find_context(dev); Context *ctx = _find_context(dev);
if (ctx) ctx->unblock(); if (ctx) ctx->unblock();
} }
void disable_irq(unsigned int irq)
{
Context *ctx = _find_context(irq);
if (ctx) ctx->disable_irq();
}
void enable_irq(unsigned int irq)
{
Context *ctx = _find_context(irq);
if (ctx) ctx->enable_irq();
}
}; };