From 2413679304fcb529e27d1aff39aeae8ef57fc32c Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Fri, 8 Jan 2021 21:15:00 +0100 Subject: [PATCH] sdhci_pci: support for inserting cards after boot. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ic67ea38bb80b35528ebb1a150d1a916a56184e69 Reviewed-on: https://review.haiku-os.org/c/haiku/+/3617 Reviewed-by: Jérôme Duval --- headers/private/drivers/mmc.h | 2 + .../kernel/bus_managers/mmc/mmc_bus.cpp | 40 +++++++-- src/add-ons/kernel/bus_managers/mmc/mmc_bus.h | 1 + .../kernel/bus_managers/mmc/mmc_module.cpp | 4 +- src/add-ons/kernel/busses/mmc/sdhci_pci.cpp | 83 +++++++++++++------ .../kernel/drivers/disk/mmc/mmc_disk.cpp | 5 -- 6 files changed, 92 insertions(+), 43 deletions(-) diff --git a/headers/private/drivers/mmc.h b/headers/private/drivers/mmc.h index 1fdfbbd0a5..8719bdfdbe 100644 --- a/headers/private/drivers/mmc.h +++ b/headers/private/drivers/mmc.h @@ -78,6 +78,8 @@ typedef struct mmc_bus_interface { status_t (*do_io)(void* controller, uint8_t command, IOOperation* operation, bool offsetAsSectors); // Execute a command that involves a data transfer. + void (*set_scan_semaphore)(void* controller, sem_id sem); + // Pass the semaphore used for device rescan to the bus controller } mmc_bus_interface; 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 3b34c93daa..ddcdbe2b9a 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp @@ -40,6 +40,8 @@ MMCBus::MMCBus(device_node* node) fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller", B_NORMAL_PRIORITY, this); resume_thread(fWorkerThread); + + fController->set_scan_semaphore(fCookie, fScanSemaphore); } @@ -115,6 +117,14 @@ MMCBus::_ActivateDevice(uint16_t rca) } +void MMCBus::_AcquireScanSemaphore() +{ + release_sem(fLockSemaphore); + acquire_sem(fScanSemaphore); + acquire_sem(fLockSemaphore); +} + + status_t MMCBus::_WorkerThread(void* cookie) { @@ -127,16 +137,22 @@ MMCBus::_WorkerThread(void* cookie) // cards. // Reset all cards on the bus - bus->ExecuteCommand(SD_GO_IDLE_STATE, 0, NULL); + // This does not work if the bus has not been powered on yet (the command + // will timeout), in that case we wait until asked to scan again when a + // card has been inserted and powered on. + status_t result; + do { + bus->_AcquireScanSemaphore(); + TRACE("Reset the bus...\n"); + result = bus->ExecuteCommand(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); while (bus->fStatus != B_SHUTTING_DOWN) { - release_sem(bus->fLockSemaphore); - // wait for bus to signal a card is inserted - // Most of the time the thread will be waiting here, with - // fLockSemaphore released - acquire_sem(bus->fScanSemaphore); - acquire_sem(bus->fLockSemaphore); - TRACE("Scanning the bus\n"); // Probe the voltage range @@ -162,10 +178,12 @@ MMCBus::_WorkerThread(void* cookie) } else if (response != probe) { ERROR("Card does not support voltage range (expected %x, " "reply %x)\n", probe, response); - // TODO what now? + // TODO we should power off the bus in this case. } // Probe OCR, waiting for card to become ready + // We keep repeating ACMD41 until the card replies that it is + // initialized. uint32_t ocr; do { uint32_t cardStatus; @@ -259,6 +277,10 @@ MMCBus::_WorkerThread(void* cookie) // 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? + + // Wait for the next scan request + // The thread will spend most of its time waiting here + bus->_AcquireScanSemaphore(); } release_sem(bus->fLockSemaphore); 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 f3809181a8..deb89003d9 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h @@ -51,6 +51,7 @@ public: void ReleaseBus() { release_sem(fLockSemaphore); } private: status_t _ActivateDevice(uint16_t rca); + void _AcquireScanSemaphore(); static status_t _WorkerThread(void*); 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 8434a5dfb9..c8ca102eca 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp @@ -49,9 +49,7 @@ mmc_bus_uninit(void* _device) static status_t mmc_bus_register_child(void* _device) { - CALLED(); - MMCBus* device = (MMCBus*)_device; - device->Rescan(); + // Nothing to do, child devices are registered by the scanning thread return B_OK; } diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp index 78e391c133..6a4ceffc1f 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -46,6 +46,7 @@ class SdhciBus { ~SdhciBus(); void EnableInterrupts(uint32_t mask); + void DisableInterrupts(); status_t ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response); int32 HandleInterrupt(); @@ -54,6 +55,7 @@ class SdhciBus { void SetClock(int kilohertz); status_t DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors); + void SetScanSemaphore(sem_id sem); private: bool PowerOn(); @@ -64,6 +66,7 @@ class SdhciBus { uint32_t fCommandResult; uint8_t fIrq; sem_id fSemaphore; + sem_id fScanSemaphore; status_t fStatus; }; @@ -117,16 +120,12 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq) // Then we configure the clock to the frequency needed for initialization SetClock(400); - // And we turn on the power supply to the card - // FIXME maybe this should only be done when a card is inserted? - if (!PowerOn()) { - ERROR("Failed to power on the card\n"); - fStatus = B_NO_INIT; - return; - } + // Turn on the power supply to the card, if there is a card inserted + PowerOn(); - EnableInterrupts(SDHCI_INT_CMD_CMP | SDHCI_INT_CARD_INS - | SDHCI_INT_CARD_REM | SDHCI_INT_TRANS_CMP); + // Finally, configure some useful interrupts + EnableInterrupts(SDHCI_INT_CMD_CMP | SDHCI_INT_CARD_REM + | SDHCI_INT_TRANS_CMP); // We want to see the error bits in the status register, but not have an // interrupt trigger on them (we get a "command complete" interrupt on @@ -139,7 +138,7 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq) SdhciBus::~SdhciBus() { - EnableInterrupts(0); + DisableInterrupts(); if (fSemaphore != 0) delete_sem(fSemaphore); @@ -155,8 +154,16 @@ SdhciBus::~SdhciBus() void SdhciBus::EnableInterrupts(uint32_t mask) { - fRegisters->interrupt_status_enable = mask; - fRegisters->interrupt_signal_enable = mask; + fRegisters->interrupt_status_enable |= mask; + fRegisters->interrupt_signal_enable |= mask; +} + + +void +SdhciBus::DisableInterrupts() +{ + fRegisters->interrupt_status_enable = 0; + fRegisters->interrupt_signal_enable = 0; } @@ -468,6 +475,21 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors) } +void +SdhciBus::SetScanSemaphore(sem_id sem) +{ + fScanSemaphore = sem; + + // If there is already a card in, start a scan immediately + if (fRegisters->present_state.IsCardInserted()) + release_sem(fScanSemaphore); + + // We can now enable the card insertion interrupt for next time a card + // is inserted + EnableInterrupts(SDHCI_INT_CARD_INS); +} + + bool SdhciBus::PowerOn() { @@ -532,23 +554,21 @@ SdhciBus::HandleInterrupt() TRACE("interrupt function called %x\n", intmask); // handling card presence interrupt - if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) { - uint32_t card_present = ((intmask & SDHCI_INT_CARD_INS) != 0); - fRegisters->interrupt_status_enable &= ~(SDHCI_INT_CARD_INS - | SDHCI_INT_CARD_REM); - fRegisters->interrupt_signal_enable &= ~(SDHCI_INT_CARD_INS - | SDHCI_INT_CARD_REM); + if ((intmask & SDHCI_INT_CARD_INS) != 0) { + PowerOn(); - fRegisters->interrupt_status_enable |= card_present - ? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS; - fRegisters->interrupt_signal_enable |= card_present - ? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS; + release_sem_etc(fScanSemaphore, 1, B_DO_NOT_RESCHEDULE); - fRegisters->interrupt_status |= (intmask & - (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)); + fRegisters->interrupt_status |= SDHCI_INT_CARD_INS; TRACE("Card presence interrupt handled\n"); } + if ((intmask & SDHCI_INT_CARD_REM) != 0) { + fRegisters->power_control.PowerOff(); + fRegisters->interrupt_status |= SDHCI_INT_CARD_REM; + TRACE("Card removal interrupt handled\n"); + } + // handling command interrupt if (intmask & SDHCI_INT_CMD_MASK) { fCommandResult = intmask; @@ -927,12 +947,22 @@ do_io(void* controller, uint8_t command, IOOperation* operation, bool offsetAsSectors) { CALLED(); - + SdhciBus* bus = (SdhciBus*)controller; return bus->DoIO(command, operation, offsetAsSectors); } +static void +set_scan_semaphore(void* controller, sem_id sem) +{ + CALLED(); + + SdhciBus* bus = (SdhciBus*)controller; + return bus->SetScanSemaphore(sem); +} + + module_dependency module_dependencies[] = { { MMC_BUS_MODULE_NAME, (module_info**)&gMMCBusController}, { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, @@ -960,7 +990,8 @@ static mmc_bus_interface gSDHCIPCIDeviceModule = { set_clock, execute_command, - do_io + do_io, + set_scan_semaphore }; 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 6b0511193e..ce8498536d 100644 --- a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp @@ -453,12 +453,9 @@ mmc_block_io(void* cookie, io_request* request) static status_t mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length) { - CALLED(); mmc_disk_handle* handle = (mmc_disk_handle*)cookie; mmc_disk_driver_info* info = handle->info; - TRACE("ioctl(op = %" B_PRId32 ")\n", op); - switch (op) { case B_GET_MEDIA_STATUS: { @@ -466,8 +463,6 @@ mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length) return B_BAD_VALUE; *(status_t *)buffer = B_OK; - TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n", - *(status_t *)buffer); return B_OK; break; }