Kernel/xHCI: Support xHCI controllers defined in the devicetree

This commit is contained in:
Sönke Holz 2025-01-10 20:10:42 +01:00
parent 980bc4ddf0
commit 60bb8edd7b
9 changed files with 174 additions and 3 deletions

View file

@ -362,9 +362,7 @@ void init_stage2(void*)
auto boot_profiling = kernel_command_line().is_boot_profiling_enabled();
if (!PCI::Access::is_disabled()) {
USB::USBManagement::initialize();
}
USB::USBManagement::initialize();
SysFSFirmwareDirectory::initialize();
if (!PCI::Access::is_disabled()) {

View file

@ -9,18 +9,21 @@
#include <AK/Singleton.h>
#include <Kernel/Boot/CommandLine.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/Access.h>
#include <Kernel/Bus/PCI/Definitions.h>
#include <Kernel/Bus/USB/EHCI/EHCIController.h>
#include <Kernel/Bus/USB/UHCI/UHCIController.h>
#include <Kernel/Bus/USB/USBManagement.h>
#include <Kernel/Bus/USB/xHCI/PCIxHCIController.h>
#include <Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.h>
#include <Kernel/Firmware/DeviceTree/DeviceTree.h>
#include <Kernel/Sections.h>
namespace Kernel::USB {
static NeverDestroyed<Vector<NonnullLockRefPtr<Driver>>> s_available_drivers;
static Singleton<USBManagement> s_the;
static NeverDestroyed<Vector<DeviceTree::DeviceRecipe<NonnullLockRefPtr<USBController>>>> s_recipes;
READONLY_AFTER_INIT bool s_initialized_sys_fs_directory = false;
@ -34,6 +37,19 @@ UNMAP_AFTER_INIT void USBManagement::enumerate_controllers()
if (kernel_command_line().disable_usb())
return;
for (auto& recipe : *s_recipes) {
auto device_or_error = recipe.create_device();
if (device_or_error.is_error()) {
dmesgln("USBManagement: Failed to create USB controller for device \"{}\" with driver {}: {}", recipe.node_name, recipe.driver_name, device_or_error.release_error());
continue;
}
m_controllers.append(device_or_error.release_value());
}
if (PCI::Access::is_disabled())
return;
MUST(PCI::enumerate([this](PCI::DeviceIdentifier const& device_identifier) {
if (device_identifier.class_code() != PCI::ClassID::SerialBus
|| device_identifier.subclass_code() != PCI::SerialBus::SubclassID::USB)
@ -117,6 +133,11 @@ USBManagement& USBManagement::the()
return *s_the;
}
void USBManagement::add_recipe(DeviceTree::DeviceRecipe<NonnullLockRefPtr<USBController>> recipe)
{
s_recipes->append(move(recipe));
}
Vector<NonnullLockRefPtr<Driver>>& USBManagement::available_drivers()
{
return *s_available_drivers;

View file

@ -25,6 +25,8 @@ public:
static LockRefPtr<Driver> get_driver_by_name(StringView name);
static void unregister_driver(NonnullLockRefPtr<Driver> driver);
static void add_recipe(DeviceTree::DeviceRecipe<NonnullLockRefPtr<USBController>>);
static Vector<NonnullLockRefPtr<Driver>>& available_drivers();
private:

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2025, Sönke Holz <sholz8530@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Bus/USB/USBManagement.h>
#include <Kernel/Bus/USB/xHCI/DeviceTreexHCIController.h>
#include <Kernel/Bus/USB/xHCI/xHCIInterrupter.h>
#include <Kernel/Firmware/DeviceTree/DeviceTree.h>
#include <Kernel/Firmware/DeviceTree/Driver.h>
#include <Kernel/Firmware/DeviceTree/Management.h>
namespace Kernel::USB {
ErrorOr<NonnullLockRefPtr<DeviceTreexHCIController>> DeviceTreexHCIController::try_to_initialize(DeviceTree::Device::Resource registers_resource, StringView node_name, size_t interrupt_number)
{
auto registers_mapping = TRY(Memory::map_typed_writable<u8>(registers_resource.paddr));
auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) DeviceTreexHCIController(move(registers_mapping), node_name, interrupt_number)));
TRY(controller->initialize());
return controller;
}
UNMAP_AFTER_INIT DeviceTreexHCIController::DeviceTreexHCIController(Memory::TypedMapping<u8> registers_mapping, StringView node_name, size_t interrupt_number)
: xHCIController(move(registers_mapping))
, m_node_name(node_name)
, m_interrupt_number(interrupt_number)
{
}
ErrorOr<NonnullOwnPtr<GenericInterruptHandler>> DeviceTreexHCIController::create_interrupter(u16 interrupter_id)
{
return TRY(xHCIDeviceTreeInterrupter::create(*this, m_interrupt_number, interrupter_id));
}
static constinit Array const compatibles_array = {
"generic-xhci"sv,
};
DEVICETREE_DRIVER(DeviceTreexHCIControllerDriver, compatibles_array);
// https://www.kernel.org/doc/Documentation/devicetree/bindings/usb/generic-xhci.yaml
ErrorOr<void> DeviceTreexHCIControllerDriver::probe(DeviceTree::Device const& device, StringView) const
{
auto registers_resource = TRY(device.get_resource(0));
auto interrupt = TRY(device.node().interrupts(DeviceTree::get()))[0];
// FIXME: Don't depend on a specific interrupt descriptor format and implement proper devicetree interrupt mapping/translation.
if (!interrupt.domain_root->is_compatible_with("arm,gic-400"sv) && !interrupt.domain_root->is_compatible_with("arm,cortex-a15-gic"sv))
return ENOTSUP;
if (interrupt.interrupt_identifier.size() != 3 * sizeof(BigEndian<u32>))
return ENOTSUP;
// The interrupt type is in the first cell. It should be 0 for SPIs.
if (reinterpret_cast<BigEndian<u32> const*>(interrupt.interrupt_identifier.data())[0] != 0)
return ENOTSUP;
// The interrupt number is in the second cell.
// GIC interrupts 32-1019 are for SPIs, so add 32 to get the GIC interrupt ID.
auto interrupt_number = (reinterpret_cast<BigEndian<u32> const*>(interrupt.interrupt_identifier.data())[1]) + 32;
DeviceTree::DeviceRecipe<NonnullLockRefPtr<USBController>> recipe {
name(),
device.node_name(),
[registers_resource, node_name = device.node_name(), interrupt_number] {
return DeviceTreexHCIController::try_to_initialize(registers_resource, node_name, interrupt_number);
},
};
USBManagement::add_recipe(move(recipe));
return {};
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2025, Sönke Holz <sholz8530@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Bus/USB/xHCI/xHCIController.h>
#include <Kernel/Firmware/DeviceTree/Device.h>
namespace Kernel::USB {
class DeviceTreexHCIController final : public xHCIController {
public:
static ErrorOr<NonnullLockRefPtr<DeviceTreexHCIController>> try_to_initialize(DeviceTree::Device::Resource, StringView node_name, size_t interrupt_number);
private:
DeviceTreexHCIController(Memory::TypedMapping<u8> registers_mapping, StringView node_name, size_t interrupt_number);
// ^xHCIController
virtual bool using_message_signalled_interrupts() const override { return m_using_message_signalled_interrupts; }
virtual ErrorOr<NonnullOwnPtr<GenericInterruptHandler>> create_interrupter(u16 interrupter_id) override;
virtual ErrorOr<void> write_dmesgln_prefix(StringBuilder& builder) const override
{
TRY(builder.try_appendff("xHCI: {}: "sv, m_node_name));
return {};
}
StringView m_node_name;
size_t m_interrupt_number { 0 };
bool m_using_message_signalled_interrupts { false };
};
}

View file

@ -16,10 +16,12 @@
namespace Kernel::USB {
class xHCIPCIInterrupter;
class xHCIDeviceTreeInterrupter;
class xHCIController
: public USBController {
friend class xHCIPCIInterrupter;
friend class xHCIDeviceTreeInterrupter;
public:
virtual ~xHCIController() override;

View file

@ -28,4 +28,23 @@ bool xHCIPCIInterrupter::handle_irq()
return true;
}
ErrorOr<NonnullOwnPtr<xHCIDeviceTreeInterrupter>> xHCIDeviceTreeInterrupter::create(DeviceTreexHCIController& controller, size_t irq, u16 interrupter_id)
{
return TRY(adopt_nonnull_own_or_enomem(new (nothrow) xHCIDeviceTreeInterrupter(controller, interrupter_id, irq)));
}
xHCIDeviceTreeInterrupter::xHCIDeviceTreeInterrupter(DeviceTreexHCIController& controller, u16 interrupter_id, size_t irq)
: IRQHandler(irq)
, m_controller(controller)
, m_interrupter_id(interrupter_id)
{
enable_irq();
}
bool xHCIDeviceTreeInterrupter::handle_irq()
{
m_controller.handle_interrupt(m_interrupter_id);
return true;
}
}

View file

@ -8,7 +8,9 @@
#include <AK/StdLibExtras.h>
#include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Bus/USB/xHCI/DeviceTreexHCIController.h>
#include <Kernel/Bus/USB/xHCI/PCIxHCIController.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Interrupts/PCIIRQHandler.h>
namespace Kernel::USB {
@ -28,4 +30,19 @@ private:
u16 m_interrupter_id { 0 };
};
class xHCIDeviceTreeInterrupter final : public IRQHandler {
public:
static ErrorOr<NonnullOwnPtr<xHCIDeviceTreeInterrupter>> create(DeviceTreexHCIController&, size_t irq, u16 interrupter_id);
virtual StringView purpose() const override { return "xHCI Interrupter"sv; }
private:
xHCIDeviceTreeInterrupter(DeviceTreexHCIController& controller, u16 interrupter_id, size_t irq);
virtual bool handle_irq() override;
DeviceTreexHCIController& m_controller;
u16 m_interrupter_id { 0 };
};
}

View file

@ -34,6 +34,7 @@ set(KERNEL_SOURCES
Bus/USB/EHCI/EHCIController.cpp
Bus/USB/UHCI/UHCIController.cpp
Bus/USB/UHCI/UHCIRootHub.cpp
Bus/USB/xHCI/DeviceTreexHCIController.cpp
Bus/USB/xHCI/PCIxHCIController.cpp
Bus/USB/xHCI/xHCIController.cpp
Bus/USB/xHCI/xHCIInterrupter.cpp