mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-30 14:14:31 +00:00
ahci: release device before suspend
- monitor system ROM changes - stop processing of new Jobs before suspend - destruct platform device before suspend, but keep platform DMA buffers - re-construct platform device and reinit resources (mmio, irq) on resume - re-start block job scheduling on resume Issue #5101
This commit is contained in:
parent
cde4d4aee0
commit
9d15735bc6
@ -113,6 +113,11 @@ struct Ahci::Hba
|
|||||||
void with_mmio (auto const &, auto const &) const;
|
void with_mmio (auto const &, auto const &) const;
|
||||||
|
|
||||||
Hba(Resources &resource) : _resource(resource)
|
Hba(Resources &resource) : _resource(resource)
|
||||||
|
{
|
||||||
|
reinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reinit()
|
||||||
{
|
{
|
||||||
with_mmio([&](Hba_mmio &mmio) {
|
with_mmio([&](Hba_mmio &mmio) {
|
||||||
|
|
||||||
@ -290,6 +295,27 @@ struct Ahci::Resources
|
|||||||
reinit();
|
reinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void release_device()
|
||||||
|
{
|
||||||
|
if (_device.constructed())
|
||||||
|
log("release AHCI device for suspend");
|
||||||
|
|
||||||
|
_mmio .destruct();
|
||||||
|
_irq .destruct();
|
||||||
|
_device.destruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
void acquire_device()
|
||||||
|
{
|
||||||
|
_device.construct(_platform);
|
||||||
|
_irq .construct(*_device);
|
||||||
|
_mmio .construct(*_device, _mmio_index(_platform));
|
||||||
|
|
||||||
|
_hba.reinit();
|
||||||
|
|
||||||
|
reinit();
|
||||||
|
}
|
||||||
|
|
||||||
void with_mmio_irq(auto const &fn, auto const &fn_error)
|
void with_mmio_irq(auto const &fn, auto const &fn_error)
|
||||||
{
|
{
|
||||||
if (_mmio.constructed() && _irq.constructed())
|
if (_mmio.constructed() && _irq.constructed())
|
||||||
@ -679,6 +705,7 @@ struct Ahci::Protocol : Interface
|
|||||||
virtual Block::Request completed(Port_mmio &) = 0;
|
virtual Block::Request completed(Port_mmio &) = 0;
|
||||||
virtual void handle_irq(Port &, Port_mmio &) = 0;
|
virtual void handle_irq(Port &, Port_mmio &) = 0;
|
||||||
virtual void writeable(bool rw) = 0;
|
virtual void writeable(bool rw) = 0;
|
||||||
|
virtual bool pending_requests() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -838,6 +865,8 @@ struct Ahci::Port : private Port_base
|
|||||||
Region_map &rm;
|
Region_map &rm;
|
||||||
unsigned cmd_slots = hba.command_slots();
|
unsigned cmd_slots = hba.command_slots();
|
||||||
|
|
||||||
|
bool stop_processing { };
|
||||||
|
|
||||||
Platform::Dma_buffer device_dma { plat, 0x1000, CACHED };
|
Platform::Dma_buffer device_dma { plat, 0x1000, CACHED };
|
||||||
Platform::Dma_buffer cmd_dma { plat,
|
Platform::Dma_buffer cmd_dma { plat,
|
||||||
align_addr(cmd_slots * Command_table::size(), 12), CACHED };
|
align_addr(cmd_slots * Command_table::size(), 12), CACHED };
|
||||||
@ -868,6 +897,11 @@ struct Ahci::Port : private Port_base
|
|||||||
:
|
:
|
||||||
Port_base(index, plat, hba, delayer),
|
Port_base(index, plat, hba, delayer),
|
||||||
protocol(protocol), rm(rm)
|
protocol(protocol), rm(rm)
|
||||||
|
{
|
||||||
|
reinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reinit()
|
||||||
{
|
{
|
||||||
_with_port_mmio([&](Port_mmio &mmio) {
|
_with_port_mmio([&](Port_mmio &mmio) {
|
||||||
reset(mmio);
|
reset(mmio);
|
||||||
@ -1190,7 +1224,10 @@ struct Ahci::Port : private Port_base
|
|||||||
request.success = true;
|
request.success = true;
|
||||||
fn(request);
|
fn(request);
|
||||||
|
|
||||||
}, [&](){ error("for_one_completed_request failed"); });
|
}, [&](){
|
||||||
|
if (protocol.pending_requests())
|
||||||
|
error("for_one_completed_request failed with pending requests");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeable(bool rw) { protocol.writeable(rw); }
|
void writeable(bool rw) { protocol.writeable(rw); }
|
||||||
|
@ -363,6 +363,8 @@ class Ata::Protocol : public Ahci::Protocol, Noncopyable
|
|||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool pending_requests() const override { return !!_slot_states; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _AHCI__ATA_PROTOCOL_H_ */
|
#endif /* _AHCI__ATA_PROTOCOL_H_ */
|
||||||
|
@ -188,6 +188,11 @@ class Atapi::Protocol : public Ahci::Protocol, Noncopyable
|
|||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool pending_requests() const override
|
||||||
|
{
|
||||||
|
return _pending.operation.valid();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _AHCI__ATAPI_PROTOCOL_H_ */
|
#endif /* _AHCI__ATAPI_PROTOCOL_H_ */
|
||||||
|
@ -62,11 +62,16 @@ class Ahci::Driver : Noncopyable
|
|||||||
Signal_handler<Driver> _handler { _env.ep(), *this, &Driver::handle_irq };
|
Signal_handler<Driver> _handler { _env.ep(), *this, &Driver::handle_irq };
|
||||||
Resources _resources { _env, _handler };
|
Resources _resources { _env, _handler };
|
||||||
|
|
||||||
|
Constructible<Attached_rom_dataspace> _system_rom { };
|
||||||
|
Signal_handler<Driver> _system_rom_sigh {
|
||||||
|
_env.ep(), *this, &Driver::_system_update };
|
||||||
|
|
||||||
Constructible<Ata::Protocol> _ata [MAX_PORTS];
|
Constructible<Ata::Protocol> _ata [MAX_PORTS];
|
||||||
Constructible<Atapi::Protocol> _atapi[MAX_PORTS];
|
Constructible<Atapi::Protocol> _atapi[MAX_PORTS];
|
||||||
Constructible<Port> _ports[MAX_PORTS];
|
Constructible<Port> _ports[MAX_PORTS];
|
||||||
|
|
||||||
bool _enable_atapi;
|
bool _enable_atapi;
|
||||||
|
bool _schedule_stop { };
|
||||||
|
|
||||||
unsigned _scan_ports(Region_map &rm, Platform::Connection &plat, Hba &hba)
|
unsigned _scan_ports(Region_map &rm, Platform::Connection &plat, Hba &hba)
|
||||||
{
|
{
|
||||||
@ -111,9 +116,50 @@ class Ahci::Driver : Noncopyable
|
|||||||
return port_count;
|
return port_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _system_update()
|
||||||
|
{
|
||||||
|
if (!_system_rom.constructed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_system_rom->update();
|
||||||
|
|
||||||
|
if (!_system_rom->valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto state = _system_rom->xml().attribute_value("state",
|
||||||
|
String<32>(""));
|
||||||
|
|
||||||
|
if (state == "driver_stop") {
|
||||||
|
_schedule_stop = true;
|
||||||
|
|
||||||
|
for_each_port([&](auto &port, auto, auto) {
|
||||||
|
port.stop_processing = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
device_release_if_stopped_and_idle();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == "driver_reinit") {
|
||||||
|
_resources.acquire_device();
|
||||||
|
|
||||||
|
_schedule_stop = false;
|
||||||
|
|
||||||
|
/* re-start request handling of client sessions */
|
||||||
|
for_each_port([&](auto &port, auto const index, auto) {
|
||||||
|
port.stop_processing = false;
|
||||||
|
port.reinit();
|
||||||
|
_dispatch.session(index);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Driver(Env &env, Dispatch &dispatch, bool support_atapi)
|
Driver(Env &env, Dispatch &dispatch, bool support_atapi, bool use_system_rom)
|
||||||
: _env(env), _dispatch(dispatch), _enable_atapi(support_atapi)
|
: _env(env), _dispatch(dispatch), _enable_atapi(support_atapi)
|
||||||
{
|
{
|
||||||
/* search for devices */
|
/* search for devices */
|
||||||
@ -126,6 +172,11 @@ class Ahci::Driver : Noncopyable
|
|||||||
Hex(hba.cap_np_value()), ",PI=", Hex(hba.pi_value()), ")");
|
Hex(hba.cap_np_value()), ",PI=", Hex(hba.pi_value()), ")");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (use_system_rom) {
|
||||||
|
_system_rom.construct(_env, "system");
|
||||||
|
_system_rom->sigh(_system_rom_sigh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,6 +193,28 @@ class Ahci::Driver : Noncopyable
|
|||||||
_dispatch.session(port);
|
_dispatch.session(port);
|
||||||
}, [&]() { error("hba handle_irq failed"); });
|
}, [&]() { error("hba handle_irq failed"); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
device_release_if_stopped_and_idle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void device_release_if_stopped_and_idle()
|
||||||
|
{
|
||||||
|
if (!_schedule_stop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* check for outstanding requests */
|
||||||
|
bool pending = false;
|
||||||
|
|
||||||
|
for_each_port([&](auto const &port, auto, auto) {
|
||||||
|
if (port.protocol.pending_requests())
|
||||||
|
pending = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* avoid disabling device if we have outstanding requests */
|
||||||
|
if (pending)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_resources.release_device();
|
||||||
}
|
}
|
||||||
|
|
||||||
Port &port(Session_label const &label, Session_policy const &policy)
|
Port &port(Session_label const &label, Session_policy const &policy)
|
||||||
@ -270,6 +343,10 @@ struct Ahci::Block_session_component : Rpc_object<Block::Session>,
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* all completed packets are handled, but no further processing */
|
||||||
|
if (port.stop_processing)
|
||||||
|
break;
|
||||||
|
|
||||||
with_requests([&] (Block::Request request) {
|
with_requests([&] (Block::Request request) {
|
||||||
|
|
||||||
Response response = Response::RETRY;
|
Response response = Response::RETRY;
|
||||||
@ -309,8 +386,9 @@ struct Ahci::Main : Rpc_object<Typed_root<Block::Session>>, Dispatch
|
|||||||
{
|
{
|
||||||
log("--- Starting AHCI driver ---");
|
log("--- Starting AHCI driver ---");
|
||||||
bool support_atapi = config.xml().attribute_value("atapi", false);
|
bool support_atapi = config.xml().attribute_value("atapi", false);
|
||||||
|
bool use_system_rom = config.xml().attribute_value("system", false);
|
||||||
try {
|
try {
|
||||||
driver.construct(env, *this, support_atapi);
|
driver.construct(env, *this, support_atapi, use_system_rom);
|
||||||
report_ports();
|
report_ports();
|
||||||
} catch (Ahci::Missing_controller) {
|
} catch (Ahci::Missing_controller) {
|
||||||
error("no AHCI controller found");
|
error("no AHCI controller found");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user