From 522c141d53875e569ca12262c7b3b5f5151d3e70 Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Tue, 12 Jan 2021 21:58:59 +0100 Subject: [PATCH] [WIP] sd/mmc: enable high speed transfers - Switch to 25MHz clock - Switch to 4bit transfers mode (the default is 1bit) Reading and writing SD cards do not seem to work anymore with these changes. I get invalid data on read, and on write, an interrupt is never called in some cases. --- headers/private/drivers/mmc.h | 5 ++- .../kernel/bus_managers/mmc/mmc_bus.cpp | 37 ++++++++++++----- src/add-ons/kernel/bus_managers/mmc/mmc_bus.h | 4 +- .../kernel/bus_managers/mmc/mmc_module.cpp | 6 +-- src/add-ons/kernel/busses/mmc/sdhci_pci.cpp | 11 +++-- .../kernel/drivers/disk/mmc/mmc_disk.cpp | 40 +++++++++++++------ 6 files changed, 68 insertions(+), 35 deletions(-) diff --git a/headers/private/drivers/mmc.h b/headers/private/drivers/mmc.h index 8719bdfdbe..98fd6ab83c 100644 --- a/headers/private/drivers/mmc.h +++ b/headers/private/drivers/mmc.h @@ -58,6 +58,7 @@ enum SD_COMMANDS { enum SDHCI_APPLICATION_COMMANDS { + SD_SET_BUS_WIDTH = 6, SD_SEND_OP_COND = 41, }; @@ -90,8 +91,8 @@ typedef struct mmc_bus_interface { // type of card. typedef struct mmc_device_interface { driver_module_info info; - status_t (*execute_command)(device_node* node, uint8_t command, - uint32_t argument, uint32_t* result); + status_t (*execute_command)(device_node* node, uint16_t rca, + uint8_t command, uint32_t argument, uint32_t* result); // Execute a command with no I/O phase status_t (*do_io)(device_node* controller, uint16_t rca, uint8_t command, IOOperation* operation, bool offsetAsSectors); diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp index ddcdbe2b9a..448304a155 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp @@ -78,9 +78,10 @@ MMCBus::Rescan() status_t -MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) +MMCBus::ExecuteCommand(uint16_t rca, uint8_t command, uint32_t argument, + uint32_t* response) { - status_t status = _ActivateDevice(0); + status_t status = _ActivateDevice(rca); if (status != B_OK) return status; return fController->execute_command(fCookie, command, argument, response); @@ -98,6 +99,13 @@ MMCBus::DoIO(uint16_t rca, uint8_t command, IOOperation* operation, } +void +MMCBus::SetClock(int frequency) +{ + fController->set_clock(fCookie, frequency); +} + + status_t MMCBus::_ActivateDevice(uint16_t rca) { @@ -144,17 +152,21 @@ MMCBus::_WorkerThread(void* cookie) do { bus->_AcquireScanSemaphore(); TRACE("Reset the bus...\n"); - result = bus->ExecuteCommand(SD_GO_IDLE_STATE, 0, NULL); + result = bus->ExecuteCommand(0, SD_GO_IDLE_STATE, 0, NULL); TRACE("CMD0 result: %s\n", strerror(result)); } while (result != B_OK); // Need to wait at least 8 clock cycles after CMD0 before sending the next - // command - snooze(100000); + // command. With the default 400kHz clock that would be 20 microseconds, + // but apparently we need more. + snooze(20000); while (bus->fStatus != B_SHUTTING_DOWN) { TRACE("Scanning the bus\n"); + // Use the low speed clock for scanning + bus->SetClock(400); + // Probe the voltage range enum { // Table 4-40 in physical layer specification v8.00 @@ -171,7 +183,7 @@ MMCBus::_WorkerThread(void* cookie) // If ACMD41 also does not work, it may be an SDIO card, too uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern; uint32_t hcs = 1 << 30; - if (bus->ExecuteCommand(SD_SEND_IF_COND, probe, &response) != B_OK) { + if (bus->ExecuteCommand(0, SD_SEND_IF_COND, probe, &response) != B_OK) { TRACE("Card does not implement CMD8, may be a V1 SD card\n"); // Do not check for SDHC support in this case hcs = 0; @@ -187,7 +199,7 @@ MMCBus::_WorkerThread(void* cookie) uint32_t ocr; do { uint32_t cardStatus; - while (bus->ExecuteCommand(SD_APP_CMD, 0, &cardStatus) + while (bus->ExecuteCommand(0, SD_APP_CMD, 0, &cardStatus) == B_BUSY) { ERROR("Card locked after CMD8...\n"); snooze(1000000); @@ -197,7 +209,7 @@ MMCBus::_WorkerThread(void* cookie) if ((cardStatus & (1 << 5)) == 0) ERROR("Card did not enter ACMD mode\n"); - bus->ExecuteCommand(SD_SEND_OP_COND, hcs | 0xFF8000, &ocr); + bus->ExecuteCommand(0, SD_SEND_OP_COND, hcs | 0xFF8000, &ocr); if ((ocr & (1 << 31)) == 0) { TRACE("Card is busy\n"); @@ -228,8 +240,8 @@ MMCBus::_WorkerThread(void* cookie) // (and a matching published device on our side). uint32_t cid[4]; - while (bus->ExecuteCommand(SD_ALL_SEND_CID, 0, cid) == B_OK) { - bus->ExecuteCommand(SD_SEND_RELATIVE_ADDR, 0, &response); + while (bus->ExecuteCommand(0, SD_ALL_SEND_CID, 0, cid) == B_OK) { + bus->ExecuteCommand(0, SD_SEND_RELATIVE_ADDR, 0, &response); TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF); @@ -274,6 +286,11 @@ MMCBus::_WorkerThread(void* cookie) attrs, NULL, NULL); } + // TODO if there is a single card active, check if it supports CMD6 + // (spec version 1.10 or later in SCR). If it does, check if CMD6 can + // enable high speed mode, use that to go to 50MHz instead of 25. + bus->SetClock(25000); + // FIXME we also need to unpublish devices that are gone. Probably need // to "ping" all RCAs somehow? Or is there an interrupt we can look for // to detect added/removed cards? diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h index deb89003d9..fe42c06b75 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h @@ -41,12 +41,14 @@ public: status_t InitCheck(); void Rescan(); - status_t ExecuteCommand(uint8_t command, + status_t ExecuteCommand(uint16_t rca, uint8_t command, uint32_t argument, uint32_t* response); status_t DoIO(uint16_t rca, uint8_t command, IOOperation* operation, bool offsetAsSectors); + void SetClock(int frequency); + void AcquireBus() { acquire_sem(fLockSemaphore); } void ReleaseBus() { release_sem(fLockSemaphore); } private: diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp index c8ca102eca..e09d4ccebd 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp @@ -78,8 +78,8 @@ mmc_bus_added_device(device_node* parent) static status_t -mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument, - uint32_t* result) +mmc_bus_execute_command(device_node* node, uint16_t rca, uint8_t command, + uint32_t argument, uint32_t* result) { // FIXME store the parent cookie in the bus cookie or something instead of // getting/putting the parent each time. @@ -94,7 +94,7 @@ mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument, MMCBus* bus = (MMCBus*)cookie; bus->AcquireBus(); - status_t error = bus->ExecuteCommand(command, argument, result); + status_t error = bus->ExecuteCommand(rca, command, argument, result); bus->ReleaseBus(); return error; } diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp index 713a0ae6a3..92d13329b4 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -223,9 +223,10 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) replyType = Command::kR1Type | Command::kDataPresent; break; case SD_APP_CMD: + case SD_SET_BUS_WIDTH: // SD Application command replyType = Command::kR1Type; break; - case 41: // ACMD + case SD_SEND_OP_COND: // SD Application command replyType = Command::kR3Type; break; default: @@ -388,7 +389,8 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors) off_t offset = operation->Offset(); generic_size_t length = operation->Length(); - TRACE("%s %ld bytes at %ld\n", isWrite ? "Write" : "Read", length, offset); + TRACE("%s %" B_PRIuSIZE " bytes at %" B_PRIdOFF "\n", + isWrite ? "Write" : "Read", length, offset); // Check that the IO scheduler did its job in following our DMA restrictions // We can start a read only at a sector boundary @@ -468,10 +470,7 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors) offset += toCopy; } - if (result != B_OK) - return result; - - return B_OK; + return result; } diff --git a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp index ce8498536d..8fc2ca49f0 100644 --- a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp @@ -147,7 +147,7 @@ mmc_block_get_geometry(mmc_disk_driver_info* info, device_geometry* geometry) { struct mmc_disk_csd csd; TRACE("Get geometry\n"); - status_t error = info->mmc->execute_command(info->parent, SD_SEND_CSD, + status_t error = info->mmc->execute_command(info->parent, 0, SD_SEND_CSD, info->rca << 16, (uint32_t*)&csd); if (error != B_OK) { TRACE("Could not get CSD! %s\n", strerror(error)); @@ -156,20 +156,31 @@ mmc_block_get_geometry(mmc_disk_driver_info* info, device_geometry* geometry) TRACE("CSD: %" PRIx64 " %" PRIx64 "\n", csd.bits[0], csd.bits[1]); - if (csd.structure_version() < 3) { - geometry->bytes_per_sector = 1 << csd.read_bl_len(); - geometry->sectors_per_track = csd.c_size() + 1; - geometry->cylinder_count = 1 << (csd.c_size_mult() + 2); - geometry->head_count = 1; - geometry->device_type = B_DISK; - geometry->removable = true; // TODO detect eMMC which isn't - geometry->read_only = false; // TODO check write protect switch? - geometry->write_once = false; - return B_OK; + if (csd.structure_version() >= 3) { + TRACE("unknown CSD version %d\n", csd.structure_version()); + return B_NOT_SUPPORTED; } - TRACE("unknown CSD version %d\n", csd.structure_version()); - return B_NOT_SUPPORTED; + geometry->bytes_per_sector = 1 << csd.read_bl_len(); + geometry->sectors_per_track = csd.c_size() + 1; + geometry->cylinder_count = 1 << (csd.c_size_mult() + 2); + geometry->head_count = 1; + geometry->device_type = B_DISK; + geometry->removable = true; // TODO detect eMMC which isn't + geometry->read_only = false; // TODO check write protect switch? + geometry->write_once = false; + + // This function will be called before all data transfers, so we use this + // opportunity to switch the card to 4-bit data transfers (instead of the + // default 1 bit mode) + uint32_t cardStatus; + const uint32 k4BitMode = 2; + info->mmc->execute_command(info->parent, info->rca, SD_APP_CMD, + info->rca << 16, &cardStatus); + info->mmc->execute_command(info->parent, info->rca, SD_SET_BUS_WIDTH, + k4BitMode, &cardStatus); + + return B_OK; } @@ -308,6 +319,9 @@ mmc_block_init_device(void* _info, void** _cookie) mmc_disk_driver_info* info = (mmc_disk_driver_info*)_info; *_cookie = info; + // Note: it is not possible to execute commands here, because this is called + // with the mmc_bus locked for enumeration (and still using slow clock). + return B_OK; }