diff --git a/repos/dde_bsd/run/audio_in.run b/repos/dde_bsd/run/audio_in.run
index d4954c1d9c..681ee398a2 100644
--- a/repos/dde_bsd/run/audio_in.run
+++ b/repos/dde_bsd/run/audio_in.run
@@ -97,6 +97,7 @@ append config {
+
diff --git a/repos/dde_bsd/run/audio_out.run b/repos/dde_bsd/run/audio_out.run
index 3e8893add9..86bd1dc7ce 100644
--- a/repos/dde_bsd/run/audio_out.run
+++ b/repos/dde_bsd/run/audio_out.run
@@ -85,6 +85,7 @@ install_config {
+
diff --git a/repos/os/include/pci/config.h b/repos/os/include/pci/config.h
index ba2496ceac..6e2580257b 100644
--- a/repos/os/include/pci/config.h
+++ b/repos/os/include/pci/config.h
@@ -106,18 +106,23 @@ struct Pci::Config : Genode::Mmio
Bar_32bit::access_t _conf_value { 0 };
+ template
+ typename REG::access_t _get_and_set(typename REG::access_t value)
+ {
+ write(0xffffffff);
+ typename REG::access_t ret = read();
+ write(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();
- write(0xffffffff);
- _conf_value = read();
- write(v);
- }
+ if (!_conf_value)
+ _conf_value = _get_and_set(read());
return _conf_value;
}
@@ -152,6 +157,19 @@ struct Pci::Config : Genode::Mmio
else
return Bar_32bit::Io_base::masked(read());
}
+
+ void set(Genode::uint64_t v)
+ {
+ if (!valid() || v == addr())
+ return;
+
+ if (memory()) {
+ if (bit64())
+ _get_and_set((Upper_bits::access_t)(v >> 32));
+ _get_and_set(Bar_32bit::Memory_base::masked(v & ~0U));
+ } else
+ _get_and_set(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::D0)
+ return false;
+
+ write(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::D3) write(Reg::D3);
+ }
+
+
+ bool soft_reset()
+ {
+ return !read();
+ }
};
@@ -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(1);
write(1);
}
+
+ void reset(Delayer & delayer)
+ {
+ if (!read())
+ return;
+ write(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();
+ }
};
diff --git a/repos/os/run/nvme.run b/repos/os/run/nvme.run
index ade076d6e3..e0c70e5ef1 100644
--- a/repos/os/run/nvme.run
+++ b/repos/os/run/nvme.run
@@ -143,7 +143,7 @@ append config {
-
+
diff --git a/repos/os/src/drivers/platform/device.h b/repos/os/src/drivers/platform/device.h
index 9b6d415bcc..267c1d50f6 100644
--- a/repos/os/src/drivers/platform/device.h
+++ b/repos/os/src/drivers/platform/device.h
@@ -239,7 +239,7 @@ class Driver::Device : private List_model::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 void for_pci_config(FN const & fn) const
diff --git a/repos/os/src/drivers/platform/device_component.cc b/repos/os/src/drivers/platform/device_component.cc
index c6771a9442..67f8851638 100644
--- a/repos/os/src/drivers/platform/device_component.cc
+++ b/repos/os/src/drivers/platform/device_component.cc
@@ -190,7 +190,8 @@ Device_component::Device_component(Registry & 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;
diff --git a/repos/os/src/drivers/platform/pci.cc b/repos/os/src/drivers/platform/pci.cc
index 1c59af84de..0d4653f441 100644
--- a/repos/os/src/drivers/platform/pci.cc
+++ b/repos/os/src/drivers/platform/pci.cc
@@ -12,6 +12,7 @@
*/
#include
+#include
#include
#include
@@ -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();
/* 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(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(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(cmd);
diff --git a/repos/os/src/drivers/platform/pci_uhci.h b/repos/os/src/drivers/platform/pci_uhci.h
index 713575d52f..1a4ae82247 100644
--- a/repos/os/src/drivers/platform/pci_uhci.h
+++ b/repos/os/src/drivers/platform/pci_uhci.h
@@ -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);
diff --git a/repos/pc/run/intel_fb.run b/repos/pc/run/intel_fb.run
index 83a5098c12..4689dc1d61 100644
--- a/repos/pc/run/intel_fb.run
+++ b/repos/pc/run/intel_fb.run
@@ -120,6 +120,7 @@ append config {
+
diff --git a/repos/pc/run/wifi.run b/repos/pc/run/wifi.run
index ef5df2b717..939416b98d 100644
--- a/repos/pc/run/wifi.run
+++ b/repos/pc/run/wifi.run
@@ -160,6 +160,7 @@ append config {
+