mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-29 15:44:02 +00:00
platform_drv: implement PCI powering and reset
Ref genodelabs/genode#4578
This commit is contained in:
parent
8f0a012345
commit
bc1e231775
@ -97,6 +97,7 @@ append config {
|
||||
</provides>
|
||||
<route>
|
||||
<service name="ROM" label="devices"> <child name="report_rom"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
|
@ -85,6 +85,7 @@ install_config {
|
||||
</provides>
|
||||
<route>
|
||||
<service name="ROM" label="devices"> <child name="report_rom"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
|
@ -106,18 +106,23 @@ struct Pci::Config : Genode::Mmio
|
||||
|
||||
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()
|
||||
{
|
||||
/*
|
||||
* Initialize _conf_value on demand only to prevent read-write
|
||||
* operations on BARs of invalid devices at construction time.
|
||||
*/
|
||||
if (!_conf_value) {
|
||||
Bar_32bit::access_t v = read<Bar_32bit>();
|
||||
write<Bar_32bit>(0xffffffff);
|
||||
_conf_value = read<Bar_32bit>();
|
||||
write<Bar_32bit>(v);
|
||||
}
|
||||
if (!_conf_value)
|
||||
_conf_value = _get_and_set<Bar_32bit>(read<Bar_32bit>());
|
||||
return _conf_value;
|
||||
}
|
||||
|
||||
@ -152,6 +157,19 @@ struct Pci::Config : Genode::Mmio
|
||||
else
|
||||
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 {
|
||||
@ -204,14 +222,53 @@ struct Pci::Config : Genode::Mmio
|
||||
|
||||
struct Power_management_capability : Pci_capability
|
||||
{
|
||||
struct Capabilities : Register<0x2, 16> {};
|
||||
struct Capabilities : Register<0x2, 16> {};
|
||||
|
||||
struct Control_status : Register<0x4, 16>
|
||||
{
|
||||
struct Pme_status : Bitfield<15,1> {};
|
||||
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 Data : Register<0x7, 8> {};
|
||||
|
||||
struct Data : Register<0x7, 8> {};
|
||||
|
||||
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>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -310,11 +367,19 @@ struct Pci::Config : Genode::Mmio
|
||||
|
||||
struct Pci_express_capability : Pci_capability
|
||||
{
|
||||
struct Capabilities : Register<0x2, 16> {};
|
||||
struct Device_capabilities : Register<0x4, 32> {};
|
||||
struct Device_control : Register<0x8, 16> {};
|
||||
struct Capabilities : Register<0x2, 16> {};
|
||||
|
||||
struct Device_status : Register<0xa, 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 Correctable_error : Bitfield<0, 1> {};
|
||||
struct Non_fatal_error : Bitfield<1, 1> {};
|
||||
@ -392,6 +457,17 @@ struct Pci::Config : Genode::Mmio
|
||||
write<Link_status::Lbm_status>(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);
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -143,7 +143,7 @@ append config {
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Timer"> <parent/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
</route>
|
||||
<config>
|
||||
<report devices="yes"/>
|
||||
|
@ -239,7 +239,7 @@ class Driver::Device : private List_model<Device>::Element
|
||||
{
|
||||
unsigned idx = 0;
|
||||
_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
|
||||
|
@ -190,7 +190,8 @@ Device_component::Device_component(Registry<Device_component> & registry,
|
||||
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});
|
||||
_ram_quota += Io_port_session::RAM_QUOTA;
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <base/attached_io_mem_dataspace.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <pci/config.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
@ -29,6 +30,20 @@ using namespace Genode;
|
||||
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
|
||||
{
|
||||
Env & _env;
|
||||
@ -41,28 +56,39 @@ struct Config_helper
|
||||
Config_helper(Env & env,
|
||||
Driver::Device const & dev,
|
||||
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)
|
||||
{
|
||||
pd.assign_pci(_io_mem.cap(),
|
||||
{ _cfg.bus_num, _cfg.dev_num, _cfg.func_num });
|
||||
|
||||
_config.power_on(delayer(_env));
|
||||
|
||||
Config::Command::access_t cmd =
|
||||
_config.read<Config::Command>();
|
||||
|
||||
/* always allow DMA operations */
|
||||
Config::Command::Bus_master_enable::set(cmd, 1);
|
||||
|
||||
/* enable memory space when I/O mem is defined */
|
||||
_dev.for_each_io_mem([&] (unsigned, Driver::Device::Io_mem::Range,
|
||||
Driver::Device::Pci_bar, bool) {
|
||||
Config::Command::Memory_space_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 i/o space when I/O ports are defined */
|
||||
_dev.for_each_io_port_range(
|
||||
[&] (unsigned, Driver::Device::Io_port_range::Range) {
|
||||
Config::Command::Io_space_enable::set(cmd, 1); });
|
||||
/* enable memory space when I/O mem is defined */
|
||||
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 */
|
||||
Config::Command::Io_space_enable::set(cmd, 1);
|
||||
});
|
||||
|
||||
_config.write<Config::Command>(cmd);
|
||||
}
|
||||
@ -76,6 +102,8 @@ struct Config_helper
|
||||
Config::Command::Bus_master_enable::set(cmd, 0);
|
||||
Config::Command::Interrupt_enable::set(cmd, 0);
|
||||
_config.write<Config::Command>(cmd);
|
||||
|
||||
_config.power_off();
|
||||
}
|
||||
|
||||
void apply_quirks()
|
||||
@ -91,7 +119,8 @@ struct Config_helper
|
||||
|
||||
/* enable i/o space when I/O ports are defined */
|
||||
_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.write<Config::Command>(cmd);
|
||||
|
@ -51,7 +51,8 @@ void Driver::pci_uhci_quirks(Env & env,
|
||||
|
||||
/* find uhci controller i/o ports */
|
||||
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; });
|
||||
|
||||
Io_port_connection io_ports(env, range.addr, range.size);
|
||||
|
@ -120,6 +120,7 @@ append config {
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
</route>
|
||||
<config>
|
||||
<policy label_prefix="intel_fb_drv" info="yes">
|
||||
|
@ -160,6 +160,7 @@ append config {
|
||||
<provides> <service name="Platform"/> </provides>
|
||||
<route>
|
||||
<service name="ROM" label="devices"> <child name="report_rom"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="IO_PORT"> <parent/> </service>
|
||||
|
Loading…
x
Reference in New Issue
Block a user