mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
platform_drv: add EHCI PCI quirk, apply in order
* Add EHCI PCI quirk * Add UHCI reset to UHCI quirk * Apply all PCI quirks in order of the PCI bus numbering otherwise the machine might stall Ref genodelabs/genode#4578
This commit is contained in:
parent
a77ceb6871
commit
00c9ac363f
@ -205,4 +205,11 @@ void Driver::Device_model::generate(Xml_generator & xml) const
|
||||
void Driver::Device_model::update(Xml_node const & node)
|
||||
{
|
||||
_model.update_from_xml(*this, node);
|
||||
|
||||
/*
|
||||
* Iterate over all devices and apply PCI quirks if necessary
|
||||
*/
|
||||
for_each([&] (Device const & device) {
|
||||
pci_apply_quirks(_env, device);
|
||||
});
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <device_pd.h>
|
||||
#include <pci.h>
|
||||
#include <pci_uhci.h>
|
||||
#include <pci_ehci.h>
|
||||
#include <pci_intel_graphics.h>
|
||||
#include <pci_hd_audio.h>
|
||||
#include <pci_virtio.h>
|
||||
@ -29,16 +30,17 @@ using namespace Pci;
|
||||
|
||||
struct Config_helper
|
||||
{
|
||||
Env & _env;
|
||||
Driver::Device const & _dev;
|
||||
Driver::Device::Pci_config const & _cfg;
|
||||
|
||||
Attached_io_mem_dataspace _io_mem;
|
||||
Attached_io_mem_dataspace _io_mem { _env, _cfg.addr, 0x1000 };
|
||||
Config _config { (addr_t)_io_mem.local_addr<void>() };
|
||||
|
||||
Config_helper(Env & env,
|
||||
Driver::Device const & dev,
|
||||
Driver::Device::Pci_config const & cfg)
|
||||
: _dev(dev), _cfg(cfg), _io_mem(env, cfg.addr, 0x1000) { }
|
||||
: _env(env), _dev(dev), _cfg(cfg) { }
|
||||
|
||||
void enable(Driver::Device_pd & pd)
|
||||
{
|
||||
@ -62,10 +64,6 @@ struct Config_helper
|
||||
Config::Command::Io_space_enable::set(cmd, 1); });
|
||||
|
||||
_config.write<Config::Command>(cmd);
|
||||
|
||||
/* apply different PCI quirks, bios handover etc. */
|
||||
Driver::pci_uhci_quirks(_cfg, _config.base());
|
||||
Driver::pci_hd_audio_quirks(_cfg, _config);
|
||||
}
|
||||
|
||||
void disable()
|
||||
@ -78,6 +76,32 @@ struct Config_helper
|
||||
Config::Command::Interrupt_enable::set(cmd, 0);
|
||||
_config.write<Config::Command>(cmd);
|
||||
}
|
||||
|
||||
void apply_quirks()
|
||||
{
|
||||
Config::Command::access_t cmd =
|
||||
_config.read<Config::Command>();
|
||||
Config::Command::access_t cmd_old = cmd;
|
||||
|
||||
/* 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); });
|
||||
|
||||
/* 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); });
|
||||
|
||||
_config.write<Config::Command>(cmd);
|
||||
|
||||
/* apply different PCI quirks, bios handover etc. */
|
||||
Driver::pci_uhci_quirks(_env, _dev, _cfg, _config.base());
|
||||
Driver::pci_ehci_quirks(_env, _dev, _cfg, _config.base());
|
||||
Driver::pci_hd_audio_quirks(_cfg, _config);
|
||||
|
||||
_config.write<Config::Command>(cmd_old);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -95,6 +119,13 @@ void Driver::pci_disable(Env & env, Device const & dev)
|
||||
}
|
||||
|
||||
|
||||
void Driver::pci_apply_quirks(Env & env, Device const & dev)
|
||||
{
|
||||
dev.for_pci_config([&] (Device::Pci_config const & pc) {
|
||||
Config_helper(env, dev, pc).apply_quirks(); });
|
||||
}
|
||||
|
||||
|
||||
void Driver::pci_msi_enable(Env & env,
|
||||
addr_t cfg_space,
|
||||
Irq_session::Info const info)
|
||||
|
@ -24,6 +24,7 @@ namespace Driver {
|
||||
|
||||
void pci_enable(Genode::Env & env, Device_pd & pd, Device const & dev);
|
||||
void pci_disable(Genode::Env & env, Device const & dev);
|
||||
void pci_apply_quirks(Genode::Env & env, Device const & dev);
|
||||
void pci_msi_enable(Genode::Env & env, addr_t cfg_space,
|
||||
Genode::Irq_session::Info const info);
|
||||
bool pci_device_matches(Genode::Session_policy const & policy,
|
||||
|
105
repos/os/src/drivers/platform/pci_ehci.h
Normal file
105
repos/os/src/drivers/platform/pci_ehci.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* \brief Platform driver - PCI EHCI utilities
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2022-09-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <pci/config.h>
|
||||
#include <device.h>
|
||||
|
||||
namespace Driver {
|
||||
static void pci_ehci_quirks(Env &, Device const &,
|
||||
Device::Pci_config, addr_t);
|
||||
}
|
||||
|
||||
|
||||
void Driver::pci_ehci_quirks(Env & env,
|
||||
Device const & dev,
|
||||
Device::Pci_config cfg,
|
||||
addr_t base)
|
||||
{
|
||||
enum { EHCI_CLASS_CODE = 0xc0320 };
|
||||
|
||||
if (cfg.class_code != EHCI_CLASS_CODE)
|
||||
return;
|
||||
|
||||
/* EHCI host controller register definitions */
|
||||
struct Ehci : Mmio
|
||||
{
|
||||
struct Capability_parameters : Register<0x8, 32>
|
||||
{
|
||||
struct Extended_cap_pointer : Bitfield<8,8> {};
|
||||
};
|
||||
struct Configure_flag : Register<0x40, 32> {};
|
||||
|
||||
using Mmio::Mmio;
|
||||
};
|
||||
|
||||
struct Ehci_pci : Mmio
|
||||
{
|
||||
struct Port_wake : Register<0x62, 16> {};
|
||||
|
||||
using Mmio::Mmio;
|
||||
};
|
||||
|
||||
/* PCI extended capability for EHCI */
|
||||
struct Cap : Mmio
|
||||
{
|
||||
struct Pointer : Register<0x0, 16>
|
||||
{
|
||||
struct Id : Bitfield<0, 8> { enum { SYNC = 1 }; };
|
||||
struct Next : Bitfield<8, 8> { };
|
||||
};
|
||||
|
||||
struct Bios_semaphore : Register<0x2, 8> { };
|
||||
struct Os_semaphore : Register<0x3, 8> { };
|
||||
struct Usb_legacy_control_status : Register<0x4, 32> {};
|
||||
|
||||
using Mmio::Mmio;
|
||||
};
|
||||
|
||||
/* find ehci controller registers behind PCI bar zero */
|
||||
dev.for_each_io_mem([&] (unsigned, Device::Io_mem::Range range,
|
||||
Device::Pci_bar bar, bool)
|
||||
{
|
||||
if (!bar.valid() || bar.number != 0)
|
||||
return;
|
||||
|
||||
Ehci_pci pw(base);
|
||||
|
||||
Attached_io_mem_dataspace iomem(env, range.start, 0x1000);
|
||||
Ehci ehci((addr_t)iomem.local_addr<void>());
|
||||
addr_t offset =
|
||||
ehci.read<Ehci::Capability_parameters::Extended_cap_pointer>();
|
||||
|
||||
/* iterate over EHCI extended capabilities */
|
||||
while (offset) {
|
||||
Cap cap(base + offset);
|
||||
if (cap.read<Cap::Pointer::Id>() != Cap::Pointer::Id::SYNC)
|
||||
break;
|
||||
|
||||
bool bios_owned = cap.read<Cap::Bios_semaphore>();
|
||||
|
||||
if (bios_owned) cap.write<Cap::Os_semaphore>(1);
|
||||
|
||||
enum { MAX_ROUNDS = 1000000 };
|
||||
unsigned rounds = MAX_ROUNDS;
|
||||
while (cap.read<Cap::Bios_semaphore>() && --rounds) ;
|
||||
|
||||
if (!rounds) cap.write<Cap::Bios_semaphore>(0);
|
||||
|
||||
cap.write<Cap::Usb_legacy_control_status>(0);
|
||||
|
||||
if (bios_owned) ehci.write<Ehci::Configure_flag>(0);
|
||||
|
||||
offset = cap.read<Cap::Pointer::Next>();
|
||||
}
|
||||
});
|
||||
}
|
@ -11,15 +11,20 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <io_port_session/connection.h>
|
||||
#include <pci/config.h>
|
||||
#include <device.h>
|
||||
|
||||
namespace Driver {
|
||||
static void pci_uhci_quirks(Device::Pci_config cfg, addr_t base);
|
||||
static void pci_uhci_quirks(Env &, Device const &,
|
||||
Device::Pci_config, addr_t);
|
||||
}
|
||||
|
||||
|
||||
void Driver::pci_uhci_quirks(Device::Pci_config cfg, addr_t base)
|
||||
void Driver::pci_uhci_quirks(Env & env,
|
||||
Device const & dev,
|
||||
Device::Pci_config cfg,
|
||||
addr_t base)
|
||||
{
|
||||
enum { UHCI_CLASS_CODE = 0xc0300 };
|
||||
|
||||
@ -44,20 +49,70 @@ void Driver::pci_uhci_quirks(Device::Pci_config cfg, addr_t base)
|
||||
using Mmio::Mmio;
|
||||
};
|
||||
|
||||
/* 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) {
|
||||
if (!range.size) range = r; });
|
||||
|
||||
Io_port_connection io_ports(env, range.addr, range.size);
|
||||
Uhci config(base);
|
||||
|
||||
bool have_to_reset = false;
|
||||
uint16_t UHCI_CMD = range.addr;
|
||||
uint16_t UHCI_INTR = range.addr + 4;
|
||||
|
||||
struct Uhci_command : Register<16>
|
||||
{
|
||||
struct Enable : Bitfield<0,1> {};
|
||||
struct Reset : Bitfield<1,1> {};
|
||||
struct Global_suspend : Bitfield<3,1> {};
|
||||
struct Config : Bitfield<6,1> {};
|
||||
};
|
||||
|
||||
struct Uhci_irq_status : Register<16>
|
||||
{
|
||||
struct Resume : Bitfield<1,1> {};
|
||||
};
|
||||
|
||||
using Reg = Uhci::Usb_legacy_support;
|
||||
|
||||
/* BIOS handover */
|
||||
|
||||
/***************************
|
||||
** BIOS handover + reset **
|
||||
***************************/
|
||||
|
||||
Reg::access_t reg = 0;
|
||||
Reg::Trap_by_60h_read_status::set(reg,1);
|
||||
Reg::Trap_by_60h_write_status::set(reg,1);
|
||||
Reg::Trap_by_64h_read_status::set(reg,1);
|
||||
Reg::Trap_by_64h_write_status::set(reg,1);
|
||||
Reg::End_of_a20gate_pass_through_status::set(reg,1);
|
||||
Reg::Usb_pirq_enable::set(reg,1);
|
||||
if (config.read<Reg>() & ~reg)
|
||||
have_to_reset = true;
|
||||
|
||||
if (!have_to_reset) {
|
||||
Uhci_command::access_t cmd = io_ports.inw(UHCI_CMD);
|
||||
if (Uhci_command::Enable::get(cmd) ||
|
||||
!Uhci_command::Config::get(cmd) ||
|
||||
!Uhci_command::Global_suspend::get(cmd))
|
||||
have_to_reset = true;
|
||||
}
|
||||
|
||||
if (!have_to_reset) {
|
||||
Uhci_irq_status::access_t intr = io_ports.inw(UHCI_INTR);
|
||||
if (intr & ~Uhci_irq_status::Resume::mask())
|
||||
have_to_reset = true;
|
||||
}
|
||||
|
||||
if (!have_to_reset)
|
||||
return;
|
||||
|
||||
config.write<Reg>(reg);
|
||||
|
||||
io_ports.outw(UHCI_CMD, Uhci_command::Reset::bits(1));
|
||||
io_ports.outw(UHCI_INTR, 0);
|
||||
io_ports.outw(UHCI_CMD, 0);
|
||||
|
||||
if (cfg.vendor_id == 0x8086)
|
||||
config.write<Uhci::Usb_resume_intel>(0);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user