platform_drv: implement PCI powering and reset

Ref genodelabs/genode#4578
This commit is contained in:
Stefan Kalkowski
2022-10-10 15:17:55 +02:00
committed by Christian Helmuth
parent 8f0a012345
commit bc1e231775
10 changed files with 161 additions and 27 deletions

View File

@@ -97,6 +97,7 @@ append config {
</provides> </provides>
<route> <route>
<service name="ROM" label="devices"> <child name="report_rom"/> </service> <service name="ROM" label="devices"> <child name="report_rom"/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="IRQ"> <parent/> </service> <service name="IRQ"> <parent/> </service>
<service name="IO_MEM"> <parent/> </service> <service name="IO_MEM"> <parent/> </service>
<service name="ROM"> <parent/> </service> <service name="ROM"> <parent/> </service>

View File

@@ -85,6 +85,7 @@ install_config {
</provides> </provides>
<route> <route>
<service name="ROM" label="devices"> <child name="report_rom"/> </service> <service name="ROM" label="devices"> <child name="report_rom"/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="IRQ"> <parent/> </service> <service name="IRQ"> <parent/> </service>
<service name="IO_MEM"> <parent/> </service> <service name="IO_MEM"> <parent/> </service>
<service name="ROM"> <parent/> </service> <service name="ROM"> <parent/> </service>

View File

@@ -106,18 +106,23 @@ struct Pci::Config : Genode::Mmio
Bar_32bit::access_t _conf_value { 0 }; Bar_32bit::access_t _conf_value { 0 };
template <typename REG>
typename REG::access_t _get_and_set(typename REG::access_t value)
{
write<REG>(0xffffffff);
typename REG::access_t ret = read<REG>();
write<REG>(value);
return ret;
}
Bar_32bit::access_t _conf() Bar_32bit::access_t _conf()
{ {
/* /*
* Initialize _conf_value on demand only to prevent read-write * Initialize _conf_value on demand only to prevent read-write
* operations on BARs of invalid devices at construction time. * operations on BARs of invalid devices at construction time.
*/ */
if (!_conf_value) { if (!_conf_value)
Bar_32bit::access_t v = read<Bar_32bit>(); _conf_value = _get_and_set<Bar_32bit>(read<Bar_32bit>());
write<Bar_32bit>(0xffffffff);
_conf_value = read<Bar_32bit>();
write<Bar_32bit>(v);
}
return _conf_value; return _conf_value;
} }
@@ -152,6 +157,19 @@ struct Pci::Config : Genode::Mmio
else else
return Bar_32bit::Io_base::masked(read<Bar_32bit>()); return Bar_32bit::Io_base::masked(read<Bar_32bit>());
} }
void set(Genode::uint64_t v)
{
if (!valid() || v == addr())
return;
if (memory()) {
if (bit64())
_get_and_set<Upper_bits>((Upper_bits::access_t)(v >> 32));
_get_and_set<Bar_32bit>(Bar_32bit::Memory_base::masked(v & ~0U));
} else
_get_and_set<Bar_32bit>(Bar_32bit::Io_base::masked(v & ~0U));
}
}; };
enum Base_addresses { enum Base_addresses {
@@ -205,13 +223,52 @@ struct Pci::Config : Genode::Mmio
struct Power_management_capability : Pci_capability struct Power_management_capability : Pci_capability
{ {
struct Capabilities : Register<0x2, 16> {}; struct Capabilities : Register<0x2, 16> {};
struct Control_status : Register<0x4, 16> struct Control_status : Register<0x4, 16>
{ {
struct Power_state : Bitfield<0, 2>
{
enum { D0, D1, D2, D3 };
};
struct No_soft_reset : Bitfield<3, 1> {};
struct Pme_status : Bitfield<15,1> {}; struct Pme_status : Bitfield<15,1> {};
}; };
struct Data : Register<0x7, 8> {}; struct Data : Register<0x7, 8> {};
using Pci_capability::Pci_capability; using Pci_capability::Pci_capability;
bool power_on(Delayer & delayer)
{
using Reg = Control_status::Power_state;
if (read<Reg>() == Reg::D0)
return false;
write<Reg>(Reg::D0);
/*
* PCI Express 4.3 - 5.3.1.4. D3 State
*
* "Unless Readiness Notifications mechanisms are used ..."
* "a minimum recovery time following a D3 hot → D0 transition of"
* "at least 10 ms ..."
*/
delayer.usleep(10'000);
return true;
}
void power_off()
{
using Reg = Control_status::Power_state;
if (read<Reg>() != Reg::D3) write<Reg>(Reg::D3);
}
bool soft_reset()
{
return !read<Control_status::No_soft_reset>();
}
}; };
@@ -311,8 +368,16 @@ struct Pci::Config : Genode::Mmio
struct Pci_express_capability : Pci_capability struct Pci_express_capability : Pci_capability
{ {
struct Capabilities : Register<0x2, 16> {}; struct Capabilities : Register<0x2, 16> {};
struct Device_capabilities : Register<0x4, 32> {};
struct Device_control : Register<0x8, 16> {}; struct Device_capabilities : Register<0x4, 32>
{
struct Function_level_reset : Bitfield<28,1> {};
};
struct Device_control : Register<0x8, 16>
{
struct Function_level_reset : Bitfield<15,1> {};
};
struct Device_status : Register<0xa, 16> struct Device_status : Register<0xa, 16>
{ {
@@ -392,6 +457,17 @@ struct Pci::Config : Genode::Mmio
write<Link_status::Lbm_status>(1); write<Link_status::Lbm_status>(1);
write<Link_control::Lbm_irq_enable>(1); write<Link_control::Lbm_irq_enable>(1);
} }
void reset(Delayer & delayer)
{
if (!read<Device_capabilities::Function_level_reset>())
return;
write<Device_control::Function_level_reset>(1);
try {
wait_for(Attempts(100), Microseconds(10000), delayer,
Device_status::Transactions_pending::Equal(0));
} catch(Polling_timeout) { }
}
}; };
@@ -545,6 +621,29 @@ struct Pci::Config : Genode::Mmio
io(reg0.addr(), reg0.size(), i); io(reg0.addr(), reg0.size(), i);
} }
}; };
void set_bar_address(unsigned idx, Genode::uint64_t addr)
{
if (idx > 5 || (idx > 1 && bridge()))
return;
Base_address bar { base() + BASE_ADDRESS_0 + idx*0x4 };
bar.set(addr);
}
void power_on(Delayer & delayer)
{
if (!power_cap.constructed() || !power_cap->power_on(delayer))
return;
if (power_cap->soft_reset() && pci_e_cap.constructed())
pci_e_cap->reset(delayer);
}
void power_off()
{
if (power_cap.constructed()) power_cap->power_off();
}
}; };

View File

@@ -143,7 +143,7 @@ append config {
<service name="PD"> <parent/> </service> <service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service> <service name="CPU"> <parent/> </service>
<service name="LOG"> <parent/> </service> <service name="LOG"> <parent/> </service>
<service name="Timer"> <parent/> </service> <service name="Timer"> <child name="timer"/> </service>
</route> </route>
<config> <config>
<report devices="yes"/> <report devices="yes"/>

View File

@@ -239,7 +239,7 @@ class Driver::Device : private List_model<Device>::Element
{ {
unsigned idx = 0; unsigned idx = 0;
_io_port_range_list.for_each([&] (Io_port_range const & ipr) { _io_port_range_list.for_each([&] (Io_port_range const & ipr) {
fn(idx++, ipr.range); }); fn(idx++, ipr.range, ipr.bar); });
} }
template <typename FN> void for_pci_config(FN const & fn) const template <typename FN> void for_pci_config(FN const & fn) const

View File

@@ -190,7 +190,8 @@ Device_component::Device_component(Registry<Device_component> & registry,
new (session.heap()) Io_mem(_io_mem_registry, bar, idx, range, pf); new (session.heap()) Io_mem(_io_mem_registry, bar, idx, range, pf);
}); });
device.for_each_io_port_range([&] (unsigned idx, Io_port_range::Range range) device.for_each_io_port_range([&] (unsigned idx, Io_port_range::Range range,
Device::Pci_bar)
{ {
session.ram_quota_guard().withdraw(Ram_quota{Io_port_session::RAM_QUOTA}); session.ram_quota_guard().withdraw(Ram_quota{Io_port_session::RAM_QUOTA});
_ram_quota += Io_port_session::RAM_QUOTA; _ram_quota += Io_port_session::RAM_QUOTA;

View File

@@ -12,6 +12,7 @@
*/ */
#include <base/attached_io_mem_dataspace.h> #include <base/attached_io_mem_dataspace.h>
#include <timer_session/connection.h>
#include <pci/config.h> #include <pci/config.h>
#include <util/reconstructible.h> #include <util/reconstructible.h>
@@ -29,6 +30,20 @@ using namespace Genode;
using namespace Pci; using namespace Pci;
static Config::Delayer & delayer(Env & env)
{
struct Delayer : Config::Delayer, Timer::Connection
{
using Timer::Connection::Connection;
void usleep(uint64_t us) override {
return Timer::Connection::usleep(us); }
};
static Delayer delayer(env);
return delayer;
};
struct Config_helper struct Config_helper
{ {
Env & _env; Env & _env;
@@ -41,28 +56,39 @@ struct Config_helper
Config_helper(Env & env, Config_helper(Env & env,
Driver::Device const & dev, Driver::Device const & dev,
Driver::Device::Pci_config const & cfg) Driver::Device::Pci_config const & cfg)
: _env(env), _dev(dev), _cfg(cfg) { } : _env(env), _dev(dev), _cfg(cfg) { _config.scan(); }
void enable(Driver::Device_pd & pd) void enable(Driver::Device_pd & pd)
{ {
pd.assign_pci(_io_mem.cap(), pd.assign_pci(_io_mem.cap(),
{ _cfg.bus_num, _cfg.dev_num, _cfg.func_num }); { _cfg.bus_num, _cfg.dev_num, _cfg.func_num });
_config.power_on(delayer(_env));
Config::Command::access_t cmd = Config::Command::access_t cmd =
_config.read<Config::Command>(); _config.read<Config::Command>();
/* always allow DMA operations */ /* always allow DMA operations */
Config::Command::Bus_master_enable::set(cmd, 1); Config::Command::Bus_master_enable::set(cmd, 1);
_dev.for_each_io_mem([&] (unsigned, Driver::Device::Io_mem::Range r,
Driver::Device::Pci_bar b, bool)
{
_config.set_bar_address(b.number, r.start);
/* enable memory space when I/O mem is defined */ /* enable memory space when I/O mem is defined */
_dev.for_each_io_mem([&] (unsigned, Driver::Device::Io_mem::Range, Config::Command::Memory_space_enable::set(cmd, 1);
Driver::Device::Pci_bar, bool) { });
Config::Command::Memory_space_enable::set(cmd, 1); });
_dev.for_each_io_port_range([&] (unsigned,
Driver::Device::Io_port_range::Range r,
Driver::Device::Pci_bar b)
{
_config.set_bar_address(b.number, r.addr);
/* enable i/o space when I/O ports are defined */ /* enable i/o space when I/O ports are defined */
_dev.for_each_io_port_range( Config::Command::Io_space_enable::set(cmd, 1);
[&] (unsigned, Driver::Device::Io_port_range::Range) { });
Config::Command::Io_space_enable::set(cmd, 1); });
_config.write<Config::Command>(cmd); _config.write<Config::Command>(cmd);
} }
@@ -76,6 +102,8 @@ struct Config_helper
Config::Command::Bus_master_enable::set(cmd, 0); Config::Command::Bus_master_enable::set(cmd, 0);
Config::Command::Interrupt_enable::set(cmd, 0); Config::Command::Interrupt_enable::set(cmd, 0);
_config.write<Config::Command>(cmd); _config.write<Config::Command>(cmd);
_config.power_off();
} }
void apply_quirks() void apply_quirks()
@@ -91,7 +119,8 @@ struct Config_helper
/* enable i/o space when I/O ports are defined */ /* enable i/o space when I/O ports are defined */
_dev.for_each_io_port_range( _dev.for_each_io_port_range(
[&] (unsigned, Driver::Device::Io_port_range::Range) { [&] (unsigned, Driver::Device::Io_port_range::Range,
Driver::Device::Pci_bar) {
Config::Command::Io_space_enable::set(cmd, 1); }); Config::Command::Io_space_enable::set(cmd, 1); });
_config.write<Config::Command>(cmd); _config.write<Config::Command>(cmd);

View File

@@ -51,7 +51,8 @@ void Driver::pci_uhci_quirks(Env & env,
/* find uhci controller i/o ports */ /* find uhci controller i/o ports */
Device::Io_port_range::Range range { 0, 0 }; Device::Io_port_range::Range range { 0, 0 };
dev.for_each_io_port_range([&] (unsigned, Device::Io_port_range::Range r) { dev.for_each_io_port_range([&] (unsigned, Device::Io_port_range::Range r,
Device::Pci_bar) {
if (!range.size) range = r; }); if (!range.size) range = r; });
Io_port_connection io_ports(env, range.addr, range.size); Io_port_connection io_ports(env, range.addr, range.size);

View File

@@ -120,6 +120,7 @@ append config {
<service name="PD"> <parent/> </service> <service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service> <service name="CPU"> <parent/> </service>
<service name="LOG"> <parent/> </service> <service name="LOG"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
</route> </route>
<config> <config>
<policy label_prefix="intel_fb_drv" info="yes"> <policy label_prefix="intel_fb_drv" info="yes">

View File

@@ -160,6 +160,7 @@ append config {
<provides> <service name="Platform"/> </provides> <provides> <service name="Platform"/> </provides>
<route> <route>
<service name="ROM" label="devices"> <child name="report_rom"/> </service> <service name="ROM" label="devices"> <child name="report_rom"/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="IRQ"> <parent/> </service> <service name="IRQ"> <parent/> </service>
<service name="IO_MEM"> <parent/> </service> <service name="IO_MEM"> <parent/> </service>
<service name="IO_PORT"> <parent/> </service> <service name="IO_PORT"> <parent/> </service>