diff --git a/Kernel/Arch/aarch64/RPi/SDHostController.cpp b/Kernel/Arch/aarch64/RPi/SDHostController.cpp new file mode 100644 index 0000000000..cba0e9db52 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/SDHostController.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Kernel::RPi { + +Singleton s_sdhc; + +SDHostController& SDHostController::the() +{ + return *s_sdhc; +} + +SDHostController::SDHostController() + : ::SDHostController() +{ + auto& gpio = GPIO::the(); + gpio.set_pin_function(21, GPIO::PinFunction::Alternate3); // CD + gpio.set_pin_high_detect_enable(21, true); + + gpio.set_pin_function(22, GPIO::PinFunction::Alternate3); // SD1_CLK + gpio.set_pin_function(23, GPIO::PinFunction::Alternate3); // SD1_CMD + + gpio.set_pin_function(24, GPIO::PinFunction::Alternate3); // SD1_DAT0 + gpio.set_pin_function(25, GPIO::PinFunction::Alternate3); // SD1_DAT1 + gpio.set_pin_function(26, GPIO::PinFunction::Alternate3); // SD1_DAT2 + gpio.set_pin_function(27, GPIO::PinFunction::Alternate3); // SD1_DAT3 + + m_registers = MMIO::the().peripheral(0x30'0000); +} + +} diff --git a/Kernel/Arch/aarch64/RPi/SDHostController.h b/Kernel/Arch/aarch64/RPi/SDHostController.h new file mode 100644 index 0000000000..cdd6b9d3b5 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/SDHostController.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel::RPi { + +class SDHostController : public ::SDHostController { +public: + static SDHostController& the(); + SDHostController(); + virtual ~SDHostController() override = default; + +protected: + // ^SDHostController + virtual SD::HostControlRegisterMap volatile* get_register_map_base_address() override { return m_registers; } + +private: + SD::HostControlRegisterMap volatile* m_registers; +}; + +} diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 1a87157820..fea4290cae 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -110,6 +110,8 @@ set(KERNEL_SOURCES Storage/NVMe/NVMeQueue.cpp Storage/Ramdisk/Controller.cpp Storage/Ramdisk/Device.cpp + Storage/SD/SDHostController.cpp + Storage/SD/SDMemoryCard.cpp Storage/DiskPartition.cpp Storage/StorageController.cpp Storage/StorageDevice.cpp @@ -436,6 +438,7 @@ elseif("${SERENITY_ARCH}" STREQUAL "aarch64") Arch/aarch64/RPi/InterruptController.cpp Arch/aarch64/RPi/Mailbox.cpp Arch/aarch64/RPi/MMIO.cpp + Arch/aarch64/RPi/SDHostController.cpp Arch/aarch64/RPi/Timer.cpp Arch/aarch64/RPi/UART.cpp ) diff --git a/Kernel/Storage/SD/Commands.h b/Kernel/Storage/SD/Commands.h new file mode 100644 index 0000000000..aa00e860ad --- /dev/null +++ b/Kernel/Storage/SD/Commands.h @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Kernel::SD { + +// Relevant Specifications: +// * (SDHC): SD Host Controller Simplified Specification (https://www.sdcard.org/downloads/pls/) +// * (PLSS) Physical Layer Simplified Specification (https://www.sdcard.org/downloads/pls/) + +// PLSS 4.7.4: "Detailed Command Description" +enum class CommandIndex : u8 { + GoIdleState = 0, + AllSendCid = 2, + SendRelativeAddr = 3, + AppSetBusWidth = 6, + SelectCard = 7, + SendIfCond = 8, + SendCsd = 9, + SetBlockLen = 16, + ReadSingleBlock = 17, + ReadMultipleBlock = 18, + WriteSingleBlock = 24, + WriteMultipleBlock = 25, + AppSendOpCond = 41, + AppSendScr = 51, + AppCmd = 55, +}; + +enum class CommandType : u8 { + Normal, + Suspend, + Resume, + Abort +}; + +enum class ResponseType : u8 { + NoResponse, + ResponseOf136Bits, + ResponseOf48Bits, + ResponseOf48BitsWithBusy +}; + +enum class DataTransferDirection : u8 { + HostToCard, + CardToHost +}; + +enum class SendAutoCommand : u8 { + Disabled, + Command12, + Command23 +}; + +// SDHC 2.2.5 & 2.2.6: "Transfer Mode Register" & "Command Register" +union Command { + u32 raw; + + struct { + u32 reserved0 : 1; + u32 block_counter : 1; + SendAutoCommand auto_command : 2; + DataTransferDirection direction : 1; + u32 multiblock : 1; + u32 reserved1 : 10; + ResponseType response_type : 2; + u32 reserved2 : 1; + u32 crc_enable : 1; + u32 idx_enable : 1; + u32 is_data : 1; + CommandType type : 2; + CommandIndex index : 6; + u32 reserved3 : 2; + }; + + bool requires_dat_line() const + { + return is_data; + } + + bool uses_transfer_complete_interrupt() const + { + // FIXME: I don't know how to determine this. + return false; + } +}; +static_assert(AssertSize()); + +namespace Commands { + +constexpr Command go_idle_state = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::NoResponse, + .reserved2 = 0, + .crc_enable = 0, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::GoIdleState, + .reserved3 = 0 +}; + +constexpr Command all_send_cid = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf136Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::AllSendCid, + .reserved3 = 0 +}; + +constexpr Command send_relative_addr = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::SendRelativeAddr, + .reserved3 = 0 +}; + +constexpr Command app_set_bus_width = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::AppSetBusWidth, + .reserved3 = 0 +}; + +constexpr Command select_card = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48BitsWithBusy, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::SelectCard, + .reserved3 = 0 +}; + +constexpr Command send_if_cond = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::SendIfCond, + .reserved3 = 0 +}; + +constexpr Command send_csd = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf136Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::SendCsd, + .reserved3 = 0 +}; + +constexpr Command set_block_len = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::SetBlockLen, + .reserved3 = 0 +}; + +constexpr Command read_single_block = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::CardToHost, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 1, + .type = CommandType::Normal, + .index = CommandIndex::ReadSingleBlock, + .reserved3 = 0 +}; + +constexpr Command read_multiple_block = { + .reserved0 = 0, + .block_counter = 1, + .auto_command = SendAutoCommand::Command12, + .direction = DataTransferDirection::CardToHost, + .multiblock = 1, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 1, + .type = CommandType::Normal, + .index = CommandIndex::ReadMultipleBlock, + .reserved3 = 0 +}; + +constexpr Command write_single_block = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 1, + .type = CommandType::Normal, + .index = CommandIndex::WriteSingleBlock, + .reserved3 = 0 +}; + +constexpr Command write_multiple_block = { + .reserved0 = 0, + .block_counter = 1, + .auto_command = SendAutoCommand::Command12, + .direction = DataTransferDirection::HostToCard, + .multiblock = 1, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 1, + .type = CommandType::Normal, + .index = CommandIndex::WriteMultipleBlock, + .reserved3 = 0 +}; + +constexpr Command app_send_op_cond = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 0, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::AppSendOpCond, + .reserved3 = 0 +}; + +constexpr Command app_send_scr = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::CardToHost, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 0, + .idx_enable = 0, + .is_data = 1, + .type = CommandType::Normal, + .index = CommandIndex::AppSendScr, + .reserved3 = 0 +}; + +constexpr Command app_cmd = { + .reserved0 = 0, + .block_counter = 0, + .auto_command = SendAutoCommand::Disabled, + .direction = DataTransferDirection::HostToCard, + .multiblock = 0, + .reserved1 = 0, + .response_type = ResponseType::ResponseOf48Bits, + .reserved2 = 0, + .crc_enable = 1, + .idx_enable = 0, + .is_data = 0, + .type = CommandType::Normal, + .index = CommandIndex::AppCmd, + .reserved3 = 0 +}; + +} + +} diff --git a/Kernel/Storage/SD/Registers.h b/Kernel/Storage/SD/Registers.h new file mode 100644 index 0000000000..1809ee9616 --- /dev/null +++ b/Kernel/Storage/SD/Registers.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel::SD { + +// Relevant Specifications: +// * (SDHC): SD Host Controller Simplified Specification (https://www.sdcard.org/downloads/pls/) +// * (PLSS) Physical Layer Simplified Specification (https://www.sdcard.org/downloads/pls/) +// * (BCM2835) BCM2835 ARM Peripherals (https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf) + +enum class HostVersion : u8 { + Version1, + Version2, + Version3, + Unknown +}; + +// SDHC 2.1.1 "SD Host Control Register Map" +// NOTE: The registers must be 32 bits, because of a quirk in the RPI. +struct HostControlRegisterMap { + u32 argument_2; + u32 block_size_and_block_count; + u32 argument_1; + u32 transfer_mode_and_command; + u32 response_0; + u32 response_1; + u32 response_2; + u32 response_3; + u32 buffer_data_port; + u32 present_state; + u32 host_configuration_0; + u32 host_configuration_1; + u32 interrupt_status; + u32 interrupt_status_enable; + u32 interrupt_signal_enable; + u32 host_configuration_2; + u32 capabilities_0; + u32 capabilities_1; + u32 maximum_current_capabilities; + u32 maximum_current_capabilities_reserved; + u32 force_event_for_auto_cmd_error_status; + u32 adma_error_status; + u32 adma_system_address[2]; + u32 preset_value[4]; + u32 reserved_0[28]; + u32 shared_bus_control; + u32 reserved_1[6]; + struct [[gnu::packed]] { + u8 interrupt_signal_for_each_slot; + u8 : 8; + HostVersion specification_version_number; + u8 vendor_version_number; + } slot_interrupt_status_and_version; +}; +static_assert(AssertSize()); + +// PLSS 5.1: "OCR Register" +union OperatingConditionRegister { + u32 raw; + struct { + u32 : 15; + u32 vdd_voltage_window_27_28 : 1; + u32 vdd_voltage_window_28_29 : 1; + u32 vdd_voltage_window_29_30 : 1; + u32 vdd_voltage_window_30_31 : 1; + u32 vdd_voltage_window_31_32 : 1; + u32 vdd_voltage_window_32_33 : 1; + u32 vdd_voltage_window_33_34 : 1; + u32 vdd_voltage_window_34_35 : 1; + u32 vdd_voltage_window_35_36 : 1; + u32 switching_to_18v_accepted : 1; + u32 : 2; + u32 over_2tb_support_status : 1; + u32 : 1; + u32 uhs2_card_status : 1; + u32 card_capacity_status : 1; + u32 card_power_up_status : 1; + }; +}; +static_assert(AssertSize()); + +// PLSS 5.2: "CID Register" +union CardIdentificationRegister { + u32 raw[4]; + + struct [[gnu::packed]] { + u64 manufacturing_date : 12; + u64 : 4; + u64 product_serial_number : 32; + u64 product_revision : 8; + u64 product_name : 40; + u64 oem_id : 16; + u64 manufacturer_id : 8; + }; +}; +static_assert(AssertSize()); + +// PLSS 5.3.2: "CSD Register (CSD Version 1.0)" +union CardSpecificDataRegister { + u64 raw[2]; + + struct [[gnu::packed]] { + // Note that the physical layer spec says there are 7 bits of checksum and 1 reserved bit here, + // but they are removed + u32 : 1; + u32 write_protection_until_power_cycle : 1; + u32 file_format : 2; + u32 temporary_write_protection : 1; + u32 permanent_write_protection : 1; + u32 copy_flag : 1; + u32 file_format_group : 1; + u32 : 5; + u32 partial_blocks_for_write_allowed : 1; + u32 max_write_data_block_length : 4; + u32 write_speed_factor : 3; + u32 : 2; + u32 write_protect_group_enable : 1; + u32 write_protect_group_size : 7; + u32 erase_sector_size : 7; + u32 erase_single_block_enable : 1; + u32 device_size_multiplier : 3; + u32 max_write_current_at_vdd_max : 3; + u32 max_write_current_at_vdd_min : 3; + u32 max_read_current_at_vdd_max : 3; + u32 max_read_current_at_vdd_min : 3; + u32 device_size : 12; + u32 : 2; + u32 dsr_implemented : 1; + u32 read_block_misalignment : 1; + u32 write_block_misalignment : 1; + u32 partial_blocks_for_read_allowed : 1; + u32 max_read_data_block_length : 4; + u32 card_command_classes : 12; + u32 max_data_transfer_rate : 8; + u32 data_read_access_time2 : 8; + u32 data_read_access_time1 : 8; + u32 : 6; + u32 csd_structure : 2; + }; +}; +static_assert(AssertSize()); + +// PLSS 5.6: "SCR Register" +union SDConfigurationRegister { + u8 raw[8]; + + struct { + u32 scr_structure : 4; + u32 sd_specification : 4; + u32 data_status_after_erase : 1; + u32 sd_security : 3; + u32 sd_bus_widths : 4; + u32 sd_specification3 : 1; + u32 extended_security : 4; + u32 sd_specification4 : 1; + u32 sd_specification_x : 4; + u32 : 1; + u32 command_support : 5; + u32 : 32; + }; +}; +static_assert(AssertSize()); + +// PLSS 4.10.1: "Card Status" +union CardStatus { + u32 raw; + + struct { + u32 : 3; + u32 ake_seq_error : 1; + u32 : 1; + u32 app_cmd : 1; + u32 fx_event : 1; + u32 : 1; + u32 ready_for_data : 1; + u32 current_state : 4; + u32 erase_reset : 1; + u32 card_ecc_disabled : 1; + u32 wp_erase_skip : 1; + u32 csd_overwrite : 1; + u32 : 2; + u32 error : 1; + u32 cc_error : 1; + u32 card_ecc_failed : 1; + u32 illegal_command : 1; + u32 com_crc_error : 1; + u32 lock_unlock_failed : 1; + u32 card_is_locked : 1; + u32 wp_violation : 1; + u32 erase_param : 1; + u32 erase_seq_error : 1; + u32 block_len_error : 1; + u32 address_error : 1; + u32 out_of_range : 1; + }; +}; +static_assert(AssertSize()); + +} diff --git a/Kernel/Storage/SD/SDHostController.cpp b/Kernel/Storage/SD/SDHostController.cpp new file mode 100644 index 0000000000..8adc2e55bf --- /dev/null +++ b/Kernel/Storage/SD/SDHostController.cpp @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#if ARCH(AARCH64) +# include +#endif + +namespace Kernel { + +// Relevant Specifications: +// * (SDHC): SD Host Controller Simplified Specification (https://www.sdcard.org/downloads/pls/) +// * (PLSS) Physical Layer Simplified Specification (https://www.sdcard.org/downloads/pls/) +// * (BCM2835) BCM2835 ARM Peripherals (https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf) + +static void delay(i64 nanoseconds) +{ + auto start = TimeManagement::the().monotonic_time().to_nanoseconds(); + auto end = start + nanoseconds; + while (TimeManagement::the().monotonic_time().to_nanoseconds() < end) + Processor::pause(); +} + +constexpr u32 max_supported_sdsc_frequency = 25000000; + +// In "m_registers->host_configuration_1" +// In sub-register "Clock Control" +constexpr u32 internal_clock_enable = 1 << 0; +constexpr u32 internal_clock_stable = 1 << 1; +constexpr u32 sd_clock_enable = 1 << 2; + +// In sub-register "Software Reset" +constexpr u32 software_reset_for_all = 0x01000000; + +// In Interrupt Status Register +const u32 command_complete = 1 << 0; +const u32 transfer_complete = 1 << 1; +const u32 buffer_write_ready = 1 << 4; +const u32 buffer_read_ready = 1 << 5; + +// PLSS 5.1: all voltage windows +constexpr u32 acmd41_voltage = 0x00ff8000; +// PLSS 4.2.3.1: All voltage windows, XPC = 1, SDHC = 1 +constexpr u32 acmd41_arg = 0x50ff8000; + +constexpr u32 block_len = 512; + +SDHostController::SDHostController() + : StorageController(StorageManagement::generate_relative_sd_controller_id({})) +{ +} + +ErrorOr SDHostController::reset() { return ENOTIMPL; } + +ErrorOr SDHostController::shutdown() { return ENOTIMPL; } + +void SDHostController::complete_current_request(AsyncDeviceRequest::RequestResult) +{ + VERIFY_NOT_REACHED(); +} + +ErrorOr SDHostController::initialize() +{ + m_registers = get_register_map_base_address(); + if (!m_registers) + return EIO; + + if (host_version() != SD::HostVersion::Version3) + return ENOTSUP; + + TRY(reset_host_controller()); + + m_registers->interrupt_status_enable = 0xffffffff; + m_registers->interrupt_signal_enable = 0xffffffff; + + auto card_or_error = try_initialize_inserted_card(); + if (card_or_error.is_error() && card_or_error.error().code() != ENODEV) { + dmesgln("SDHostController: Failed to initialize inserted card: {}", card_or_error.error()); + } else if (!card_or_error.is_error()) { + m_card = card_or_error.release_value(); + } + + return {}; +} + +ErrorOr> SDHostController::try_initialize_inserted_card() +{ + if (!is_card_inserted()) + return ENODEV; + + // PLSS 4.2: "Card Identification Mode" + // "After power-on ...the cards are initialized with ... 400KHz clock frequency." + TRY(sd_clock_supply(400000)); + + // PLSS 4.2.3: "Card Initialization and Identification Process" + // Also see Figure 4-2 in the PLSS spec for a flowchart of the initialization process. + // Note that the steps correspond to the steps in the flowchart, although I made up the numbering and text + + // 1. Send CMD0 (GO_IDLE_STATE) to the card + TRY(issue_command(SD::Commands::go_idle_state, 0)); + TRY(wait_for_response()); + + // 2. Send CMD8 (SEND_IF_COND) to the card + // SD interface condition: 7:0 = check pattern, 11:8 = supply voltage + // 0x1aa: check pattern = 10101010, supply voltage = 1 => 2.7-3.6V + const u32 voltage_window = 0x1aa; + TRY(issue_command(SD::Commands::send_if_cond, voltage_window)); + auto interface_condition_response = wait_for_response(); + + // 3. If the card does not respond to CMD8 it means that (Ver2.00 or later + // SD Memory Card(voltage mismatch) or Ver1.X SD Memory Card or not SD + // Memory Card) + if (interface_condition_response.is_error()) { + // TODO: This is supposed to be the "No Response" branch of the + // flowchart in Figure 4-2 of the PLSS spec + return ENOTSUP; + } + + // 4. If the card responds to CMD8, but it's not a valid response then the + // card is not usable + if (interface_condition_response.value().response[0] != voltage_window) { + // FIXME: We should probably try again with a lower voltage window + return ENODEV; + } + + // 5. Send ACMD41 (SEND_OP_COND) with HCS=1 to the card, repeat this until the card is ready or timeout + SD::OperatingConditionRegister ocr = {}; + bool card_is_usable = true; + if (!retry_with_timeout([&]() { + if (issue_command(SD::Commands::app_cmd, 0).is_error() || wait_for_response().is_error()) + return false; + + if (issue_command(SD::Commands::app_send_op_cond, acmd41_arg).is_error()) + return false; + + if (auto acmd41_response = wait_for_response(); + !acmd41_response.is_error()) { + + // 20. Check if the card supports the voltage windows we requested and SDHC + u32 response = acmd41_response.value().response[0]; + if ((response & acmd41_voltage) != acmd41_voltage) { + card_is_usable = false; + return false; + } + + ocr.raw = acmd41_response.value().response[0]; + } + + return ocr.card_power_up_status == 1; + })) { + return card_is_usable ? EIO : ENODEV; + } + + // 6. If you requested to switch to 1.8V, and the card accepts, execute a voltage switch sequence + // (we didn't ask it) + + // 7. Send CMD2 (ALL_SEND_CID) to the card + TRY(issue_command(SD::Commands::all_send_cid, 0)); + auto all_send_cid_response = TRY(wait_for_response()); + auto cid = bit_cast(all_send_cid_response.response); + + // 8. Send CMD3 (SEND_RELATIVE_ADDR) to the card + TRY(issue_command(SD::Commands::send_relative_addr, 0)); + auto send_relative_addr_response = TRY(wait_for_response()); + u32 rca = send_relative_addr_response.response[0]; // FIXME: Might need to clear some bits here + + // Extra steps: + + TRY(issue_command(SD::Commands::send_csd, rca)); + auto send_csd_response = TRY(wait_for_response()); + auto csd = bit_cast(send_csd_response.response); + + u32 block_count = (csd.device_size + 1) * (1 << (csd.device_size_multiplier + 2)); + u32 block_size = (1 << csd.max_read_data_block_length); + u64 capacity = static_cast(block_count) * block_size; + u64 card_capacity_in_blocks = capacity / block_len; + + // TODO: Do high speed initialisation, if supported + TRY(sd_clock_frequency_change(max_supported_sdsc_frequency)); + + TRY(issue_command(SD::Commands::select_card, rca)); + TRY(wait_for_response()); + + // Set block length to 512 if the card is SDSC. + // All other models only support 512 byte blocks so they don't need to be explicitly told + if (!ocr.card_capacity_status) { + TRY(issue_command(SD::Commands::set_block_len, block_len)); + TRY(wait_for_response()); + } + + auto scr = TRY(retrieve_sd_configuration_register(rca)); + + TRY(issue_command(SD::Commands::app_cmd, rca)); + TRY(wait_for_response()); + TRY(issue_command(SD::Commands::app_set_bus_width, 0x2)); // 0b00=1 bit bus, 0b10=4 bit bus + TRY(wait_for_response()); + + return TRY(DeviceManagement::try_create_device( + *this, + StorageDevice::LUNAddress { controller_id(), 0, 0 }, + hardware_relative_controller_id(), block_len, + card_capacity_in_blocks, rca, ocr, cid, scr)); +} + +bool SDHostController::retry_with_timeout(Function f, i64 delay_between_tries) +{ + int timeout = 1000; + bool success = false; + while (!success && timeout > 0) { + success = f(); + if (!success) + delay(delay_between_tries); + timeout--; + } + return timeout > 0; +} + +ErrorOr SDHostController::issue_command(SD::Command const& cmd, u32 argument) +{ + // SDHC 3.7.1: "Transaction Control without Data Transfer Using DAT Line" + constexpr u32 command_inhibit = 1 << 1; + + // 1. Check Command Inhibit (CMD) in the Present State register. + // Repeat this step until Command Inhibit (CMD) is 0. + // That is, when Command Inhibit (CMD) is 1, the Host Driver + // shall not issue an SD Command. + if (!retry_with_timeout( + [&]() { return !(m_registers->present_state & command_inhibit); })) { + return EIO; + } + + // 2. If the Host Driver issues an SD Command using DAT lines + // including busy signal, go to step (3). + // If without using DAT lines including busy signal, go to step (5). + // 3. If the Host Driver is issuing an abort command, go to step (5). In the + // case of non-abort command, go to step (4). + if (cmd.requires_dat_line() && cmd.type != SD::CommandType::Abort) { + + // 4. Check Command Inhibit (DAT) in the Present State register. Repeat + // this step until Command Inhibit (DAT) is set to 0. + constexpr u32 data_inhibit = 1 << 2; + if (!retry_with_timeout([&]() { return !(m_registers->present_state & data_inhibit); })) { + return EIO; + } + } + + // 5. Set registers as described in Table 1-2 except Command register. + m_registers->argument_1 = argument; + + // 6. Set the Command register. + m_registers->transfer_mode_and_command = cmd.raw; + + // 7. Perform Command Completion Sequence in accordance with 3.7.1.2. + // Done in wait_for_response() + + return {}; +} + +ErrorOr SDHostController::wait_for_response() +{ + // SDHC 3.7.1.2 The Sequence to Finalize a Command + + // 1. Wait for the Command Complete Interrupt. If the Command Complete + // Interrupt has occurred, go to step (2). + if (!retry_with_timeout( + [&]() { return m_registers->interrupt_status & command_complete; })) { + return EIO; + } + + // 2. Write 1 to Command Complete in the Normal Interrupt Status register to clear this bit + m_registers->interrupt_status = command_complete; + + // 3. Read the Response register(s) to get the response. + // NOTE: We read fewer bits than ResponseType because the missing bits are only + // relevant for the physical layer, and the device filters them before they + // reach us + Response r = {}; + auto cmd = last_sent_command(); + switch (cmd.response_type) { + case SD::ResponseType::NoResponse: + break; + case SD::ResponseType::ResponseOf136Bits: + r.response[0] = m_registers->response_0; + r.response[1] = m_registers->response_1; + r.response[2] = m_registers->response_2; + r.response[3] = m_registers->response_3; + break; + case SD::ResponseType::ResponseOf48Bits: + r.response[0] = m_registers->response_0; + break; + case SD::ResponseType::ResponseOf48BitsWithBusy: + // FIXME: Idk what to do here + break; + } + + // 4. Judge whether the command uses the Transfer Complete Interrupt or not. + // If it uses Transfer Complete, go to step (5). If not, go to step (7). + if (last_sent_command().uses_transfer_complete_interrupt()) + TODO(); + + // 7. Check for errors in Response Data. If there is no error, go to step (8). If there is an error, go to step (9). + if (cmd.response_type != SD::ResponseType::ResponseOf136Bits) { + if (card_status_contains_errors(cmd, r.response[0])) { + return EIO; + } + } + + // NOTE: Steps 7, 8 and 9 consist of checking the response for errors, which + // are specific to each command therefore those steps are not fully implemented + // here. + + return { r }; +} + +ErrorOr SDHostController::sd_clock_supply(u32 frequency) +{ + // SDHC 3.2.1: "SD Clock Supply Sequence" + // The *Clock Control* register is in the lower 16 bits of *Host Configuration 1* + VERIFY((m_registers->host_configuration_1 & sd_clock_enable) == 0); + + // 1. Find out the divisor to determine the SD Clock Frequency + const u32 sd_clock_frequency = TRY(retrieve_sd_clock_frequency()); + + // FIXME: The way the SD Clock is to be calculated is different for other versions + if (host_version() != SD::HostVersion::Version3) + TODO(); + u32 divisor = AK::max(AK::ceil_div(sd_clock_frequency, frequency), 2); + + // 2. Set Internal Clock Enable and SDCLK Frequency Select in the Clock Control register + const u32 two_upper_bits_of_sdclk_frequency_select = (divisor >> 8 & 0x3) << 6; + const u32 eight_lower_bits_of_sdclk_frequency_select = (divisor & 0xff) << 8; + const u32 sdclk_frequency_select = two_upper_bits_of_sdclk_frequency_select | eight_lower_bits_of_sdclk_frequency_select; + m_registers->host_configuration_1 = m_registers->host_configuration_1 | internal_clock_enable | sdclk_frequency_select; + + // 3. Check Internal Clock Stable in the Clock Control register until it is 1 + if (!retry_with_timeout([&] { return m_registers->host_configuration_1 & internal_clock_stable; })) { + return EIO; + } + + // 4. Set SD Clock Enable in the Clock Control register to 1 + m_registers->host_configuration_1 = m_registers->host_configuration_1 | sd_clock_enable; + + return {}; +} + +void SDHostController::sd_clock_stop() +{ + // SDHC 3.2.2: "SD Clock Stop Sequence" + + // 1. Set SD Clock Enable in the Clock Control register to 0 + m_registers->host_configuration_1 = m_registers->host_configuration_1 & ~sd_clock_enable; +} + +ErrorOr SDHostController::sd_clock_frequency_change(u32 new_frequency) +{ + // SDHC 3.2.3: "SD Clock Frequency Change Sequence" + + // 1. Execute the SD Clock Stop Sequence + sd_clock_stop(); + + // 2. Execute the SD Clock Supply Sequence + return sd_clock_supply(new_frequency); +} + +ErrorOr SDHostController::reset_host_controller() +{ + m_registers->host_configuration_0 = 0; + m_registers->host_configuration_1 = m_registers->host_configuration_1 | software_reset_for_all; + if (!retry_with_timeout( + [&] { + return (m_registers->host_configuration_1 & software_reset_for_all) == 0; + })) { + return EIO; + } + + return {}; +} + +ErrorOr SDHostController::transaction_control_with_data_transfer_using_the_dat_line_without_dma( + SD::Command const& command, + u32 argument, + u32 block_count, + u32 block_size, + UserOrKernelBuffer buf, + DataTransferType data_transfer_type) +{ + // SDHC 3.7.2: "Transaction Control with Data Transfer Using DAT Line (without DMA)" + + // 1. Set the value corresponding to the executed data byte length of one block to Block Size register. + // 2. Set the value corresponding to the executed data block count to Block Count register in accordance with Table 2-8. + m_registers->block_size_and_block_count = (block_count << 16) | block_size; + + // 3. Set the argument value to Argument 1 register. + m_registers->argument_1 = argument; + + // 4. Set the value to the Transfer Mode register. The host driver + // determines Multi / Single Block + // Select, Block Count Enable, Data Transfer Direction, Auto CMD12 Enable + // and DMA Enable. Multi / Single Block Select and Block Count Enable are + // determined according to Table 2-8. (NOTE: We assume `cmd` already has + // the correct flags set) + // 5. Set the value to Command register. + m_registers->transfer_mode_and_command = command.raw; + + // 6. Then, wait for the Command Complete Interrupt. + if (!retry_with_timeout( + [&]() { return m_registers->interrupt_status & command_complete; })) { + return EIO; + } + + // 7. Write 1 to the Command Complete in the Normal Interrupt Status + // register for clearing this bit. + m_registers->interrupt_status = command_complete; + + // 8. Read Response register and get necessary information of the issued + // command + // (FIXME: Return the value for better error handling) + + // 9. In the case where this sequence is for write to a card, go to step + // (10). + // In case of read from a card, go to step (14). + if (data_transfer_type == DataTransferType::Write) { + + for (u32 i = 0; i < block_count; i++) { + // 10. Then wait for Buffer Write Ready Interrupt. + if (!retry_with_timeout( + [&]() { + return m_registers->interrupt_status & buffer_write_ready; + })) { + return EIO; + } + + // 11. Write 1 to the Buffer Write Ready in the Normal Interrupt Status register for clearing this bit. + m_registers->interrupt_status = buffer_write_ready; + + // 12. Write block data (in according to the number of bytes specified at the step (1)) to Buffer Data Port register. + u32 temp; + for (u32 j = 0; j < block_size / sizeof(u32); j++) { + TRY(buf.read(&temp, i * block_size + sizeof(u32) * j, sizeof(u32))); + m_registers->buffer_data_port = temp; + } + + // 13. Repeat until all blocks are sent and then go to step (18). + } + } else { + for (u32 i = 0; i < block_count; i++) { + // 14. Then wait for the Buffer Read Ready Interrupt. + if (!retry_with_timeout([&]() { return m_registers->interrupt_status & buffer_read_ready; })) { + return EIO; + } + + // 15. Write 1 to the Buffer Read Ready in the Normal Interrupt Status + // register for clearing this bit. + m_registers->interrupt_status = buffer_read_ready; + + // 16. Read block data (in according to the number of bytes specified at + // the step (1)) from the Buffer Data Port register + u32 temp; + for (u32 j = 0; j < block_size / sizeof(u32); j++) { + temp = m_registers->buffer_data_port; + TRY(buf.write(&temp, i * block_size + sizeof(u32) * j, sizeof(u32))); + } + + // 17. Repeat until all blocks are received and then go to step (18). + } + } + + // 18. If this sequence is for Single or Multiple Block Transfer, go to step + // (19). In case of Infinite Block Transfer, go to step (21) + + // 19. Wait for Transfer Complete Interrupt. + if (!retry_with_timeout( + [&]() { return m_registers->interrupt_status & transfer_complete; })) { + return EIO; + } + + // 20. Write 1 to the Transfer Complete in the Normal Interrupt Status + // register for clearing this bit + m_registers->interrupt_status = transfer_complete; + + return {}; +} + +ErrorOr SDHostController::read_block(Badge, u32 block_address, u32 block_count, UserOrKernelBuffer out) +{ + VERIFY(is_card_inserted()); + + if (block_count > 1) { + return transaction_control_with_data_transfer_using_the_dat_line_without_dma( + SD::Commands::read_multiple_block, + block_address, + block_count, + block_len, + out, + DataTransferType::Read); + } + + return transaction_control_with_data_transfer_using_the_dat_line_without_dma( + SD::Commands::read_single_block, + block_address, + block_count, + block_len, + out, + DataTransferType::Read); +} + +ErrorOr SDHostController::write_block(Badge, u32 block_address, u32 block_count, UserOrKernelBuffer in) +{ + VERIFY(is_card_inserted()); + + if (block_count > 1) { + return transaction_control_with_data_transfer_using_the_dat_line_without_dma( + SD::Commands::write_multiple_block, + block_address, + block_count, + block_len, + in, + DataTransferType::Write); + } + return transaction_control_with_data_transfer_using_the_dat_line_without_dma( + SD::Commands::write_single_block, + block_address, + block_count, + block_len, + in, + DataTransferType::Write); +} + +ErrorOr SDHostController::retrieve_sd_configuration_register(u32 relative_card_address) +{ + SD::SDConfigurationRegister scr; + + TRY(issue_command(SD::Commands::app_cmd, relative_card_address)); + TRY(wait_for_response()); + TRY(transaction_control_with_data_transfer_using_the_dat_line_without_dma( + SD::Commands::app_send_scr, + 0, 1, 8, + UserOrKernelBuffer::for_kernel_buffer(scr.raw), DataTransferType::Read)); + + return scr; +} + +ErrorOr SDHostController::retrieve_sd_clock_frequency() +{ + const i64 one_mhz = 1'000'000; + return { ((m_registers->capabilities_0 & 0xff00) >> 8) * one_mhz }; +} + +// PLSS Table 4-43 : Card Status Field/Command +bool SDHostController::card_status_contains_errors(SD::Command const& command, u32 resp) +{ + SD::CardStatus status; + // PLSS 4.9.5 R6 + if (command.index == SD::CommandIndex::SendRelativeAddr) { + status.raw = (resp & 0x1fff) | ((resp & 0x2000) << 6) | ((resp & 0x4000) << 8) | ((resp & 0x8000) << 8); + } else { + status.raw = resp; + } + + bool common_errors = status.error || status.cc_error || status.card_ecc_failed || status.illegal_command || status.com_crc_error || status.lock_unlock_failed || status.card_is_locked || status.wp_violation || status.erase_param || status.csd_overwrite; + + bool contains_errors = false; + switch (command.index) { + case SD::CommandIndex::SendRelativeAddr: + if (status.error || status.illegal_command || status.com_crc_error) { + contains_errors = true; + } + break; + case SD::CommandIndex::SelectCard: + if (common_errors) { + contains_errors = true; + } + break; + case SD::CommandIndex::SetBlockLen: + if (common_errors || status.block_len_error) { + contains_errors = true; + } + break; + case SD::CommandIndex::ReadSingleBlock: + case SD::CommandIndex::ReadMultipleBlock: + if (common_errors || status.address_error || status.out_of_range) { + contains_errors = true; + } + break; + case SD::CommandIndex::WriteSingleBlock: + case SD::CommandIndex::WriteMultipleBlock: + if (common_errors || status.block_len_error || status.address_error || status.out_of_range) { + contains_errors = true; + } + break; + case SD::CommandIndex::AppSendScr: + if (common_errors) { + contains_errors = true; + } + break; + case SD::CommandIndex::AppCmd: + if (common_errors) { + contains_errors = true; + } + break; + default: + break; + } + + return contains_errors; +} + +} diff --git a/Kernel/Storage/SD/SDHostController.h b/Kernel/Storage/SD/SDHostController.h new file mode 100644 index 0000000000..a50bb35c2a --- /dev/null +++ b/Kernel/Storage/SD/SDHostController.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class SDHostController : public StorageController { +public: + SDHostController(); + ErrorOr initialize(); + + virtual ~SDHostController() = default; + + virtual LockRefPtr device(u32 index) const override { return index == 0 ? m_card : nullptr; } + virtual ErrorOr reset() override; + virtual ErrorOr shutdown() override; + virtual size_t devices_count() const override { return m_card ? 1 : 0; } + virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override; + + ErrorOr read_block(Badge, u32 block_address, u32 block_count, UserOrKernelBuffer out); + ErrorOr write_block(Badge, u32 block_address, u32 block_count, UserOrKernelBuffer in); + +protected: + virtual SD::HostControlRegisterMap volatile* get_register_map_base_address() = 0; + +private: + ErrorOr> try_initialize_inserted_card(); + + bool is_card_inserted() const + { + constexpr u32 card_inserted = 1 << 16; + return m_registers->present_state & card_inserted; + } + + SD::HostVersion host_version() { return m_registers->slot_interrupt_status_and_version.specification_version_number; } + + ErrorOr reset_host_controller(); + + SD::Command last_sent_command() + { + SD::Command command {}; + command.raw = m_registers->transfer_mode_and_command; + return command; + } + bool currently_active_command_uses_transfer_complete_interrupt(); + + ErrorOr sd_clock_supply(u32 frequency); + void sd_clock_stop(); + ErrorOr sd_clock_frequency_change(u32 frequency); + ErrorOr retrieve_sd_clock_frequency(); + + struct Response { + u32 response[4]; + }; + ErrorOr issue_command(SD::Command const&, u32 argument); + ErrorOr wait_for_response(); + + bool card_status_contains_errors(SD::Command const&, u32); + + bool retry_with_timeout(Function, i64 delay_between_tries = 100); + + enum class DataTransferType { + Read, + Write + }; + ErrorOr transaction_control_with_data_transfer_using_the_dat_line_without_dma(SD::Command const&, u32 argument, u32 block_count, u32 block_size, UserOrKernelBuffer, DataTransferType data_transfer_type); + ErrorOr retrieve_sd_configuration_register(u32 relative_card_address); + + volatile SD::HostControlRegisterMap* m_registers; + LockRefPtr m_card { nullptr }; + + u32 m_hardware_relative_controller_id { 0 }; + Mutex m_lock { "SDHostController"sv }; +}; + +} diff --git a/Kernel/Storage/SD/SDMemoryCard.cpp b/Kernel/Storage/SD/SDMemoryCard.cpp new file mode 100644 index 0000000000..0a6b8872cd --- /dev/null +++ b/Kernel/Storage/SD/SDMemoryCard.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel { + +SDMemoryCard::SDMemoryCard(SDHostController& sdhc, StorageDevice::LUNAddress lun_address, u32 hardware_relative_controller_id, u32 block_len, u64 capacity_in_blocks, u32 relative_card_address, SD::OperatingConditionRegister ocr, SD::CardIdentificationRegister cid, SD::SDConfigurationRegister scr) + : StorageDevice(lun_address, hardware_relative_controller_id, block_len, + capacity_in_blocks) + , m_sdhc(sdhc) + , m_relative_card_address(relative_card_address) + , m_ocr(ocr) + , m_cid(cid) + , m_scr(scr) +{ +} + +void SDMemoryCard::start_request(AsyncBlockDeviceRequest& request) +{ + MutexLocker locker(m_lock); + + VERIFY(request.block_size() == block_size()); + + auto buffer = request.buffer(); + u32 block_address = request.block_index(); + if (card_addressing_mode() == CardAddressingMode::ByteAddressing) { + block_address *= block_size(); + } + + if (request.request_type() == AsyncBlockDeviceRequest::RequestType::Write) { + if (m_sdhc.write_block({}, block_address, request.block_count(), buffer).is_error()) { + request.complete(AsyncDeviceRequest::Failure); + return; + } + } else { + if (m_sdhc.read_block({}, block_address, request.block_count(), buffer).is_error()) { + request.complete(AsyncDeviceRequest::Failure); + return; + } + } + + request.complete(AsyncDeviceRequest::Success); +} + +} diff --git a/Kernel/Storage/SD/SDMemoryCard.h b/Kernel/Storage/SD/SDMemoryCard.h new file mode 100644 index 0000000000..08cf332b0a --- /dev/null +++ b/Kernel/Storage/SD/SDMemoryCard.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class SDHostController; + +class SDMemoryCard : public StorageDevice { +public: + SDMemoryCard(SDHostController& sdhc, StorageDevice::LUNAddress, u32 hardware_relative_controller_id, u32 block_len, u64 capacity_in_blocks, u32 relative_card_address, SD::OperatingConditionRegister ocr, SD::CardIdentificationRegister cid, SD::SDConfigurationRegister scr); + + // ^StorageDevice + virtual CommandSet command_set() const override { return CommandSet::SD; } + + // ^BlockDevice + virtual void start_request(AsyncBlockDeviceRequest&) override; + +private: + enum class CardAddressingMode { + ByteAddressing, + BlockAddressing + }; + CardAddressingMode card_addressing_mode() const + { + return m_ocr.card_capacity_status ? CardAddressingMode::BlockAddressing : CardAddressingMode::ByteAddressing; + } + + Mutex m_lock { "SDMemoryCard"sv }; + SDHostController& m_sdhc; + + u32 m_relative_card_address; + SD::OperatingConditionRegister m_ocr; + SD::CardIdentificationRegister m_cid; + SD::SDConfigurationRegister m_scr; +}; + +} diff --git a/Kernel/Storage/StorageDevice.cpp b/Kernel/Storage/StorageDevice.cpp index 091d354602..d61644e2ce 100644 --- a/Kernel/Storage/StorageDevice.cpp +++ b/Kernel/Storage/StorageDevice.cpp @@ -77,6 +77,8 @@ StringView StorageDevice::command_set_to_string_view() const return "ata"sv; case CommandSet::NVMe: return "nvme"sv; + case CommandSet::SD: + return "sd"sv; default: break; } diff --git a/Kernel/Storage/StorageDevice.h b/Kernel/Storage/StorageDevice.h index 08198f4792..736a42dcae 100644 --- a/Kernel/Storage/StorageDevice.h +++ b/Kernel/Storage/StorageDevice.h @@ -38,6 +38,7 @@ public: SCSI, ATA, NVMe, + SD, }; // Note: The most reliable way to address this device from userspace interfaces, diff --git a/Kernel/Storage/StorageManagement.cpp b/Kernel/Storage/StorageManagement.cpp index cd529ed0e1..eccb9d7242 100644 --- a/Kernel/Storage/StorageManagement.cpp +++ b/Kernel/Storage/StorageManagement.cpp @@ -13,6 +13,9 @@ # include # include #endif +#if ARCH(AARCH64) +# include +#endif #include #include #include @@ -26,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +44,7 @@ static Atomic s_controller_id; static Atomic s_relative_ata_controller_id; static Atomic s_relative_nvme_controller_id; +static Atomic s_relative_sd_controller_id; static constexpr StringView partition_uuid_prefix = "PARTUUID:"sv; @@ -61,6 +66,7 @@ u32 StorageManagement::generate_relative_nvme_controller_id(Badge) { auto controller_id = s_relative_ata_controller_id.load(); @@ -68,6 +74,13 @@ u32 StorageManagement::generate_relative_ata_controller_id(Badge) return controller_id; } +u32 StorageManagement::generate_relative_sd_controller_id(Badge) +{ + auto controller_id = s_relative_sd_controller_id.load(); + s_relative_sd_controller_id++; + return controller_id; +} + void StorageManagement::remove_device(StorageDevice& device) { m_storage_devices.remove(device); @@ -445,6 +458,16 @@ UNMAP_AFTER_INIT void StorageManagement::initialize(StringView root_device, bool } else { enumerate_pci_controllers(force_pio, poll); } + +#if ARCH(AARCH64) + auto& rpi_sdhc = RPi::SDHostController::the(); + if (auto maybe_error = rpi_sdhc.initialize(); maybe_error.is_error()) { + dmesgln("Unable to initialize RaspberryPi's SD Host Controller: {}", maybe_error.error()); + } else { + m_controllers.append(rpi_sdhc); + } +#endif + // Note: Whether PCI bus is present on the system or not, always try to attach // a given ramdisk. auto controller = RamdiskController::try_initialize(); diff --git a/Kernel/Storage/StorageManagement.h b/Kernel/Storage/StorageManagement.h index 7e21376c92..1e97eb595c 100644 --- a/Kernel/Storage/StorageManagement.h +++ b/Kernel/Storage/StorageManagement.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ public: static u32 generate_relative_nvme_controller_id(Badge); static u32 generate_relative_ata_controller_id(Badge); + static u32 generate_relative_sd_controller_id(Badge); void remove_device(StorageDevice&);