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 { +