mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-22 09:21:57 -05:00
Kernel/xHCI: Support xHCI controllers defined in the devicetree
This commit is contained in:
parent
980bc4ddf0
commit
60bb8edd7b
9 changed files with 174 additions and 3 deletions
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
76
Kernel/Bus/USB/xHCI/DeviceTreexHCIController.cpp
Normal file
76
Kernel/Bus/USB/xHCI/DeviceTreexHCIController.cpp
Normal 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 {};
|
||||
}
|
||||
|
||||
}
|
35
Kernel/Bus/USB/xHCI/DeviceTreexHCIController.h
Normal file
35
Kernel/Bus/USB/xHCI/DeviceTreexHCIController.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue