From 74b60970787e901984a6bc0518cbe2d58b092589 Mon Sep 17 00:00:00 2001 From: Anarchos Date: Wed, 14 Oct 2020 15:10:01 +0200 Subject: [PATCH] sd/mmc: read, naive method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First implementation of reading sectors from an SD card. This is not the best performance for many reasons: - No DMA - Reads only one sector at a time - Cannot read more than 512 bytes per syscall Also there are major limitations: - Cannot read less than 512 bytes. The hardware of course works in full sectors. The mmc_disk driver should go through the io scheduler to make sure requests have a reasonable size and offset, and nothing tries to read just a few bytes in the middle of a sector. - SD cards only (no SDHC, no MMC) Architecture problems: I think too much of the implementation is done in sdhci_pci and should be moved to the upper layers. However it is difficult to say without having implemented DMA (which indeed will be at the low level of the sdhci controller). It doesn't help that the order of operations is a bit different depending on wether there is DMA or not. In DMA mode you first prepare the buffer, then run the command. In non-DMA mode you first send the command, then read the data into the buffer. We need an API at the mmc_bus level that doesn't care about that low-level detail. There are other things that the MMC bus should be doing however, such as switching to different clock speeds depending on which card is activated and how fast it can go. At least the following should be done: - The read method for mmc_bus and sdhci_pci should use a scatter-gather structure as a parameter instead of a single buffer - See if can be integrated into ExecuteCommand at sdhci level (it's essentially a command with an additional data phase) Change-Id: I688b6c694561074535c9c0c2545f06dc04b06e7d Reviewed-on: https://review.haiku-os.org/c/haiku/+/3466 Reviewed-by: Jérôme Duval --- headers/private/drivers/mmc.h | 36 +++- .../kernel/bus_managers/mmc/mmc_bus.cpp | 96 +++++++-- src/add-ons/kernel/bus_managers/mmc/mmc_bus.h | 14 +- .../kernel/bus_managers/mmc/mmc_module.cpp | 42 +++- src/add-ons/kernel/busses/mmc/sdhci_pci.cpp | 198 ++++++++++++++---- src/add-ons/kernel/busses/mmc/sdhci_pci.h | 65 ++++-- .../kernel/drivers/disk/mmc/mmc_disk.cpp | 13 +- 7 files changed, 378 insertions(+), 86 deletions(-) diff --git a/headers/private/drivers/mmc.h b/headers/private/drivers/mmc.h index af105f1e0f..43f3910486 100644 --- a/headers/private/drivers/mmc.h +++ b/headers/private/drivers/mmc.h @@ -25,6 +25,37 @@ enum { }; +// Commands for SD cards defined in SD Specifications Part 1: +// Physical Layer Simplified Specification Version 8.00 +// They are in the common .h file for the mmc stack because the SDHCI driver +// currently needs to map them to the corresponding expected response types. +enum SD_COMMANDS { + // Basic commands, class 0 + SD_GO_IDLE_STATE = 0, + SD_ALL_SEND_CID = 2, + SD_SEND_RELATIVE_ADDR = 3, + SD_SELECT_DESELECT_CARD = 7, + SD_SEND_IF_COND = 8, + SD_SEND_CSD = 9, + SD_STOP_TRANSMISSION = 12, + + // Block oriented read commands, class 2 + SD_READ_SINGLE_BLOCK = 17, + SD_READ_MULTIPLE_BLOCKS = 18, + + // Application specific commands, class 8 + SD_APP_CMD = 55, + + // I/O mode commands, class 9 + SD_IO_ABORT = 52, +}; + + +enum SDHCI_APPLICATION_COMMANDS { + SD_SEND_OP_COND = 41, +}; + + // Interface between mmc_bus and underlying implementation (sdhci_pci or any // other thing that can execute mmc commands) typedef struct mmc_bus_interface { @@ -33,15 +64,18 @@ typedef struct mmc_bus_interface { status_t (*set_clock)(void* controller, uint32_t kilohertz); status_t (*execute_command)(void* controller, uint8_t command, uint32_t argument, uint32_t* result); + status_t (*read_naive)(void* controller, off_t pos, + void* buffer, size_t* _length); } mmc_bus_interface; // Interface between mmc device driver (mmc_disk, sdio drivers, ...) and mmc_bus 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 (*read_naive)(device_node* controller, uint16_t rca, off_t pos, + void* buffer, size_t* _length); } mmc_device_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 339d156bff..de625f0e1e 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp @@ -18,7 +18,8 @@ MMCBus::MMCBus(device_node* node) fController(NULL), fCookie(NULL), fStatus(B_OK), - fWorkerThread(0) + fWorkerThread(-1), + fActiveDevice(0) { CALLED(); @@ -34,8 +35,9 @@ MMCBus::MMCBus(device_node* node) return; } - fSemaphore = create_sem(0, "MMC bus scan"); - fWorkerThread = spawn_kernel_thread(WorkerThread, "SD bus controller", + fScanSemaphore = create_sem(0, "MMC bus scan"); + fLockSemaphore = create_sem(1, "MMC bus lock"); + fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller", B_NORMAL_PRIORITY, this); resume_thread(fWorkerThread); } @@ -52,6 +54,9 @@ MMCBus::~MMCBus() if (fWorkerThread != 0) wait_for_thread(fWorkerThread, &result); // TODO power off cards, stop clock, etc if needed. + + delete_sem(fLockSemaphore); + delete_sem(fScanSemaphore); } @@ -66,41 +71,90 @@ void MMCBus::Rescan() { // Just wake up the thread for a scan - release_sem(fSemaphore); + release_sem(fScanSemaphore); } status_t MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) { + status_t status = _ActivateDevice(0); + if (status != B_OK) + return status; return fController->execute_command(fCookie, command, argument, response); } status_t -MMCBus::WorkerThread(void* cookie) +MMCBus::Read(uint16_t rca, off_t position, void* buffer, size_t* length) +{ + status_t status = _ActivateDevice(rca); + if (status != B_OK) + return status; + return fController->read_naive(fCookie, position, buffer, length); +} + + +status_t +MMCBus::_ActivateDevice(uint16_t rca) +{ + // Do nothing if the device is already activated + if (fActiveDevice == rca) + return B_OK; + + uint32_t response; + status_t result; + result = fController->execute_command(fCookie, SD_SELECT_DESELECT_CARD, + ((uint32)rca) << 16, &response); + + if (result == B_OK) + fActiveDevice = rca; + + return result; +} + + +status_t +MMCBus::_WorkerThread(void* cookie) { MMCBus* bus = (MMCBus*)cookie; uint32_t response; + acquire_sem(bus->fLockSemaphore); + // We assume the bus defaults to 400kHz clock and has already powered on // cards. - + // Reset all cards on the bus - bus->ExecuteCommand(0, 0, NULL); + bus->ExecuteCommand(SD_GO_IDLE_STATE, 0, NULL); while (bus->fStatus != B_SHUTTING_DOWN) { + release_sem(bus->fLockSemaphore); // wait for bus to signal a card is inserted - acquire_sem(bus->fSemaphore); + // 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 + enum { + // Table 4-40 in physical layer specification v8.00 + // All other values are currently reserved + HOST_27_36V = 1, //Host supplied voltage 2.7-3.6V + }; + + // An arbitrary value, we just need to check that the response + // containts the same. + static const uint8 kVoltageCheckPattern = 0xAA; + // FIXME MMC cards will not reply to this! They expect CMD1 instead // SD v1 cards will also not reply, but we can proceed to ACMD41 // If ACMD41 also does not work, it may be an SDIO card, too - uint32_t probe = (1 << 8) | 0xAA; + uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern; uint32_t hcs = 1 << 30; - if (bus->ExecuteCommand(8, probe, &response) != B_OK) { + if (bus->ExecuteCommand(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; @@ -114,7 +168,7 @@ MMCBus::WorkerThread(void* cookie) uint32_t ocr; do { uint32_t cardStatus; - while (bus->ExecuteCommand(55, 0, &cardStatus) + while (bus->ExecuteCommand(SD_APP_CMD, 0, &cardStatus) == B_BUSY) { ERROR("Card locked after CMD8...\n"); snooze(1000000); @@ -124,7 +178,7 @@ MMCBus::WorkerThread(void* cookie) if ((cardStatus & (1 << 5)) == 0) ERROR("Card did not enter ACMD mode\n"); - bus->ExecuteCommand(41, hcs | 0xFF8000, &ocr); + bus->ExecuteCommand(SD_SEND_OP_COND, hcs | 0xFF8000, &ocr); if ((ocr & (1 << 31)) == 0) { TRACE("Card is busy\n"); @@ -146,11 +200,17 @@ MMCBus::WorkerThread(void* cookie) // TODO send CMD11 to switch to low voltage mode if card supports it? - // iterate CMD2/CMD3 to assign an RCA to all cards and publish devices - // for each of them + // We use CMD2 (ALL_SEND_CID) and CMD3 (SEND_RELATIVE_ADDR) to assign + // an RCA to all cards. Initially all cards have an RCA of 0 and will + // all receive CMD2. But only ne of them will reply (they do collision + // detection while sending the CID in reply). We assign a new RCA to + // that first card, and repeat the process with the remaining ones + // until no one answers to CMD2. Then we know all cards have an RCA + // (and a matching published device on our side). uint32_t cid[4]; - while (bus->ExecuteCommand(2, 0, cid) == B_OK) { - bus->ExecuteCommand(3, 0, &response); + + while (bus->ExecuteCommand(SD_ALL_SEND_CID, 0, cid) == B_OK) { + bus->ExecuteCommand(SD_SEND_RELATIVE_ADDR, 0, &response); TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF); @@ -175,7 +235,7 @@ MMCBus::WorkerThread(void* cookie) uint8_t month = cid[0] & 0xF; uint16_t year = 2000 + ((cid[0] >> 4) & 0xFF); uint16_t rca = response >> 16; - + device_attr attrs[] = { { B_DEVICE_BUS, B_STRING_TYPE, {string: "mmc" }}, { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "mmc device" }}, @@ -200,6 +260,8 @@ MMCBus::WorkerThread(void* cookie) // to detect added/removed cards? } + release_sem(bus->fLockSemaphore); + TRACE("poller thread terminating"); return B_OK; } 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 cccae48e67..9692ef9489 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h @@ -41,10 +41,16 @@ public: status_t InitCheck(); void Rescan(); -private: status_t ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response); - static status_t WorkerThread(void*); + status_t Read(uint16_t rca, off_t position, void* buffer, + size_t* length); + + void AcquireBus() { acquire_sem(fLockSemaphore); } + void ReleaseBus() { release_sem(fLockSemaphore); } +private: + status_t _ActivateDevice(uint16_t rca); + static status_t _WorkerThread(void*); private: @@ -53,7 +59,9 @@ private: void* fCookie; status_t fStatus; thread_id fWorkerThread; - sem_id fSemaphore; + sem_id fScanSemaphore; + sem_id fLockSemaphore; + uint16 fActiveDevice; }; 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 5104f874ad..c0ce6e5c36 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp @@ -82,20 +82,43 @@ static status_t mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument, uint32_t* result) { - // FIXME store these in the bus cookie or something instead of - // getting/putting the parents each time. - mmc_bus_interface* sdhci; + // FIXME store the parent cookie in the bus cookie or something instead of + // getting/putting the parent each time. + driver_module_info* mmc; void* cookie; TRACE("In mmc_bus_execute_command\n"); device_node* parent = gDeviceManager->get_parent_node(node); - device_node* grandparent = gDeviceManager->get_parent_node(parent); - gDeviceManager->get_driver(grandparent, (driver_module_info**)&sdhci, - &cookie); - gDeviceManager->put_node(grandparent); + gDeviceManager->get_driver(parent, &mmc, &cookie); gDeviceManager->put_node(parent); - return sdhci->execute_command(cookie, command, argument, result); + MMCBus* bus = (MMCBus*)cookie; + + bus->AcquireBus(); + status_t error = bus->ExecuteCommand(command, argument, result); + bus->ReleaseBus(); + return error; +} + + +static status_t +mmc_bus_read_naive(device_node* node, uint16_t rca, off_t pos, void* buffer, + size_t* _length) +{ + // FIXME store the parent cookie in the bus cookie or something instead of + // getting/putting the parent each time. + driver_module_info* mmc; + void* cookie; + + device_node* parent = gDeviceManager->get_parent_node(node); + gDeviceManager->get_driver(parent, &mmc, &cookie); + gDeviceManager->put_node(parent); + + MMCBus* bus = (MMCBus*)cookie; + bus->AcquireBus(); + status_t result = bus->Read(rca, pos, buffer, _length); + bus->ReleaseBus(); + return result; } @@ -148,7 +171,8 @@ mmc_device_interface mmc_bus_controller_module = { NULL, NULL }, - mmc_bus_execute_command + mmc_bus_execute_command, + mmc_bus_read_naive }; diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp index fdefcdbcc8..3700c8c255 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -49,7 +49,8 @@ class SdhciBus { status_t InitCheck(); void Reset(); void SetClock(int kilohertz); - + status_t ReadNaive(off_t pos, void* buffer, size_t* _length); + private: bool PowerOn(); void RecoverError(); @@ -89,7 +90,7 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq) } fSemaphore = create_sem(0, "SDHCI interrupts"); - + fStatus = install_io_interrupt_handler(fIrq, sdhci_generic_interrupt, this, 0); @@ -113,23 +114,25 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq) return; } - // FIXME do we need all these? Wouldn't card insertion/removal and command - // completion be enough? EnableInterrupts(SDHCI_INT_CMD_CMP - | SDHCI_INT_TRANS_CMP | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM - | SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_INDEX - | SDHCI_INT_BUS_POWER | SDHCI_INT_END_BIT); + | SDHCI_INT_BUF_READ_READY | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM); - fRegisters->interrupt_status_enable |= SDHCI_INT_ERROR; + // 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 + // errors already) + fRegisters->interrupt_status_enable |= SDHCI_INT_ERROR + | SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_INDEX + | SDHCI_INT_BUS_POWER | SDHCI_INT_END_BIT; } SdhciBus::~SdhciBus() { + EnableInterrupts(0); + if (fSemaphore != 0) delete_sem(fSemaphore); - EnableInterrupts(0); if (fIrq != 0) remove_io_interrupt_handler(fIrq, sdhci_generic_interrupt, this); @@ -146,45 +149,83 @@ SdhciBus::EnableInterrupts(uint32_t mask) } +// #pragma mark - +/* +PartA2, SD Host Controller Simplified Specification, Version 4.20 +§3.7.1.1 The sequence to issue an SD Command +*/ status_t SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) { TRACE("ExecuteCommand(%d, %x)\n", command, argument); + + // Check if it's possible to send a command right now. + // It is not possible to send a command as long as the command line is busy. + // The spec says we should wait, but we can't do that on kernel side, since + // it leaves no chance for the upper layers to handle the problem. So we + // just say we're busy and the caller can retry later. + // Note that this should normally never happen: the command line is busy + // only during command execution, and we don't leave this function with ac + // command running. if (fRegisters->present_state.CommandInhibit()) { ERROR("Execution aborted, command inhibit\n"); return B_BUSY; } - fRegisters->argument = argument; - uint32_t replyType; switch(command) { - case 0: + case SD_GO_IDLE_STATE: replyType = Command::kNoReplyType; break; - case 55: - replyType = Command::kR1Type; - break; - case 2: - case 9: + case SD_ALL_SEND_CID: + case SD_SEND_CSD: replyType = Command::kR2Type; break; + case SD_SEND_RELATIVE_ADDR: + replyType = Command::kR6Type; + break; + case SD_SELECT_DESELECT_CARD: + replyType = Command::kR1bType; + break; + case SD_SEND_IF_COND: + replyType = Command::kR7Type; + break; + case SD_READ_SINGLE_BLOCK: + case SD_READ_MULTIPLE_BLOCKS: + replyType = Command::kR1Type | Command::kDataPresent; + break; + case SD_APP_CMD: + replyType = Command::kR1Type; + break; case 41: // ACMD replyType = Command::kR3Type; break; - case 3: - replyType = Command::kR6Type; - break; - case 8: - replyType = Command::kR7Type; - break; default: ERROR("Unknown command\n"); return B_BAD_DATA; } + // Check if DATA line is available (if needed) + if ((replyType & Command::k32BitResponseCheckBusy) != 0 + && command != SD_STOP_TRANSMISSION && command != SD_IO_ABORT) { + if (fRegisters->present_state.DataInhibit()) { + ERROR("Execution aborted, data inhibit\n"); + return B_BUSY; + } + } + + //FIXME : Assign only at this point, if needed : + // -32 bit block count/SDMA system address + // -block size + // -16-bit block count + // -transfer mode + + fRegisters->argument = argument; fRegisters->command.SendCommand(command, replyType); + + // Wait for command response to be available (either "command complete" or + // "buffer read ready" interrupt will happen, depending on the command) acquire_sem(fSemaphore); if (fCommandResult & SDHCI_INT_ERROR) { @@ -220,12 +261,13 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) response[2] = fRegisters->response[2]; response[3] = fRegisters->response[3]; break; + default: // No response break; } - ERROR("Command execution complete\n"); + ERROR("Command execution %d complete\n", command); return B_OK; } @@ -285,6 +327,62 @@ SdhciBus::SetClock(int kilohertz) } +status_t +SdhciBus::ReadNaive(off_t pos, void* buffer, size_t* _length) +{ + // TODO read multiple blocks at once (don't ignore _length) + fRegisters->block_size = 512; + fRegisters->block_count = 1; + fRegisters->transfer_mode = TransferMode::kSingle | TransferMode::kRead + | TransferMode::kAutoCmdDisabled | TransferMode::kNoDmaOrNoData; + + uint32_t response; + status_t result; + result = ExecuteCommand(SD_READ_SINGLE_BLOCK, pos, &response); + + if (result != B_OK) + return result; + + TRACE("Command response: %02x\n", response); + + if (fCommandResult & SDHCI_INT_BUF_READ_READY == 0) { + TRACE("No data!\n"); + return B_ERROR; + } + + // We don't know how to read more than 512 bytes (CMD18 would be needed) + if (*_length > 512) + *_length = 512; + + // read block data from Buffer Data Port register + // TODO use DMA instead + size_t to_read = *_length / sizeof(uint32_t); + size_t to_drop = 512 / sizeof(uint32_t) - to_read; + uint32_t* dest = (uint32_t*)buffer; + while(to_read > 0) { + *dest = fRegisters->buffer_data_port; + TRACE("read : 0x%x", *dest); + dest++; + to_read--; + } + + // We cannot read less than one sector, so we have to drop the extra data. + // This will be fixed when we use DMA and the IO scheduler (since it makes + // sure to only ask for complete sectors). + // Currently the IO scheduler does not support bounce buffers for non-DMA + // transfers. + while(to_drop > 0) { + (void*)fRegisters->buffer_data_port; + to_drop--; + } + + // wait for command complete interrupt + acquire_sem(fSemaphore); + + return B_OK; +} + + bool SdhciBus::PowerOn() { @@ -428,17 +526,29 @@ SdhciBus::RecoverError() int32 SdhciBus::HandleInterrupt() { + CALLED(); + +#if 0 + // We could use the slot register to quickly see for which slot the + // interrupt is. But since we have an interrupt handler call for each slot + // anyway, it's just as simple to let each of them scan its own interrupt + // status register. + if ( !(fRegisters->slot_interrupt_status & (1 << fSlot)) ) { + TRACE("interrupt not for me.\n"); + return B_UNHANDLED_INTERRUPT; + } +#endif + uint32_t intmask = fRegisters->interrupt_status; + // Shortcut: exit early if there is no interrupt or if the register is + // clearly invalid. if ((intmask == 0) || (intmask == 0xffffffff)) { return B_UNHANDLED_INTERRUPT; } TRACE("interrupt function called %x\n", intmask); - // FIXME use the global "slot interrupt" register to quickly decide if an - // interrupt is targetted to this slot - // handling card presence interrupt if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) { uint32_t card_present = ((intmask & SDHCI_INT_CARD_INS) != 0); @@ -455,37 +565,43 @@ SdhciBus::HandleInterrupt() fRegisters->interrupt_status |= (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)); TRACE("Card presence interrupt handled\n"); - - return B_HANDLED_INTERRUPT; } // handling command interrupt if (intmask & SDHCI_INT_CMD_MASK) { fCommandResult = intmask; - // Save the status before clearing so the thhread can handle it + // Save the status before clearing so the thread can handle it fRegisters->interrupt_status |= (intmask & SDHCI_INT_CMD_MASK); + // Notify the thread release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE); - TRACE("Command interrupt handled\n"); + TRACE("Command complete interrupt handled\n"); + } - return B_HANDLED_INTERRUPT; + // handling data transfer interrupt + if (intmask & SDHCI_INT_BUF_READ_READY) { + TRACE("buffer read ready interrupt raised"); + fRegisters->interrupt_status |= (intmask & SDHCI_INT_BUF_READ_READY); + + release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE); } // handling bus power interrupt if (intmask & SDHCI_INT_BUS_POWER) { fRegisters->interrupt_status |= SDHCI_INT_BUS_POWER; TRACE("card is consuming too much power\n"); - - return B_HANDLED_INTERRUPT; } + // Check that all interrupts have been cleared (we check all the ones we + // enabled, so that should always be the case) intmask = fRegisters->slot_interrupt_status; if (intmask != 0) { ERROR("Remaining interrupts at end of handler: %x\n", intmask); } - return B_UNHANDLED_INTERRUPT; + return B_HANDLED_INTERRUPT; } +// #pragma mark - static void @@ -639,6 +755,17 @@ execute_command(void* controller, uint8_t command, uint32_t argument, } +//Very naive read protocol : non DMA, 32 bits at a time (size of Buffer Data Port) +static status_t +read_naive(void* controller, off_t pos, void* buffer, size_t* _length) +{ + CALLED(); + + SdhciBus* bus = (SdhciBus*)controller; + return bus->ReadNaive(pos, buffer, _length); +} + + module_dependency module_dependencies[] = { { MMC_BUS_MODULE_NAME, (module_info**)&gMMCBusController}, { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, @@ -666,6 +793,7 @@ static mmc_bus_interface gSDHCIPCIDeviceModule = { set_clock, execute_command, + read_naive }; diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.h b/src/add-ons/kernel/busses/mmc/sdhci_pci.h index ad2fc36c4c..00dbc7a0fb 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.h +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.h @@ -19,6 +19,36 @@ #define SDHCI_BUS_TYPE_NAME "bus/sdhci/v1" +class TransferMode { + public: + uint16_t Bits() { return fBits; } + + // TODO response interrupt + // TODO response check + + static const uint8_t kR1 = 0 << 6; + static const uint8_t kR5 = 1 << 6; + + static const uint8_t kMulti = 1 << 5; + static const uint8_t kSingle = 0 << 5; + + static const uint8_t kRead = 1 << 4; + static const uint8_t kWrite = 0 << 4; + + static const uint8_t kAutoCmdDisabled = 0 << 2; + static const uint8_t kAutoCmd12Enable = 1 << 2; + static const uint8_t kAutoCmd23Enable = 2 << 2; + static const uint8_t kAutoCmdAutoSelect = + kAutoCmd23Enable | kAutoCmd12Enable; + + // TODO block count enable + + static const uint8_t kDmaEnable = 1; + static const uint8_t kNoDmaOrNoData = 0; + + private: + volatile uint16_t fBits; +} __attribute__((packed)); class Command { public: @@ -35,14 +65,17 @@ class Command { static const uint8_t kSubCommand = 0x4; static const uint8_t kReplySizeMask = 0x3; static const uint8_t k32BitResponse = 0x2; - static const uint8_t k128BitResponse = 0x1; + static const uint8_t k128BitResponse = 0x1; + static const uint8_t k32BitResponseCheckBusy = 0x3; // For simplicity pre-define the standard response types from the SD // card specification static const uint8_t kNoReplyType = 0; static const uint8_t kR1Type = kCheckIndex | kCRCEnable | k32BitResponse; - static const uint8_t kR2Type = kCRCEnable | k128BitResponse; + static const uint8_t kR1bType = (kCheckIndex | kCRCEnable + | k32BitResponseCheckBusy) & (~ kDataPresent); + static const uint8_t kR2Type = kCRCEnable | k128BitResponse; static const uint8_t kR3Type = k32BitResponse; static const uint8_t kR6Type = kCheckIndex | k32BitResponse; static const uint8_t kR7Type = kDataPresent | kCheckIndex | kCRCEnable @@ -62,6 +95,7 @@ class PresentState { bool IsCardInserted() { return fBits & (1 << 16); } bool CommandInhibit() { return fBits & (1 << 0); } + bool DataInhibit() { return fBits & (1 << 1); } private: volatile uint32_t fBits; @@ -134,24 +168,25 @@ class SoftwareReset { } __attribute__((packed)); -/* Interrupt registers */ -#define SDHCI_INT_CMD_CMP 0x00000001 // command complete enable -#define SDHCI_INT_TRANS_CMP 0x00000002 // transfer complete enable -#define SDHCI_INT_CARD_INS 0x00000040 // card insertion enable -#define SDHCI_INT_CARD_REM 0x00000080 // card removal enable -#define SDHCI_INT_ERROR 0x00008000 // error -#define SDHCI_INT_TIMEOUT 0x00010000 // Timeout error -#define SDHCI_INT_CRC 0x00020000 // CRC error -#define SDHCI_INT_END_BIT 0x00040000 // end bit error -#define SDHCI_INT_INDEX 0x00080000 // index error -#define SDHCI_INT_BUS_POWER 0x00800000 // power fail +// #pragma mark Interrupt registers +#define SDHCI_INT_CMD_CMP 0x00000001 // command complete enable +#define SDHCI_INT_TRANS_CMP 0x00000002 // transfer complete enable +#define SDHCI_INT_BUF_READ_READY 0x00000020 // buffer read ready enable +#define SDHCI_INT_CARD_INS 0x00000040 // card insertion enable +#define SDHCI_INT_CARD_REM 0x00000080 // card removal enable +#define SDHCI_INT_ERROR 0x00008000 // error +#define SDHCI_INT_TIMEOUT 0x00010000 // Timeout error +#define SDHCI_INT_CRC 0x00020000 // CRC error +#define SDHCI_INT_END_BIT 0x00040000 // end bit error +#define SDHCI_INT_INDEX 0x00080000 // index error +#define SDHCI_INT_BUS_POWER 0x00800000 // power fail #define SDHCI_INT_CMD_ERROR_MASK (SDHCI_INT_TIMEOUT | \ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) #define SDHCI_INT_CMD_MASK (SDHCI_INT_CMD_CMP | SDHCI_INT_CMD_ERROR_MASK) - +// #pragma mark - class Capabilities { public: @@ -175,7 +210,7 @@ class HostControllerVersion { const uint8_t vendorVersion; } __attribute__((packed)); - +// #pragma mark - struct registers { // SD command generation volatile uint32_t system_address; 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 8742668c8f..486a84bd32 100644 --- a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp @@ -1,5 +1,6 @@ /* * Copyright 2018-2020 Haiku, Inc. All rights reserved. + * Copyright 2020, Viveris Technologies. * Distributed under the terms of the MIT License. * * Authors: @@ -200,7 +201,7 @@ mmc_block_open(void* _info, const char* path, int openMode, void** _cookie) CALLED(); mmc_disk_driver_info* info = (mmc_disk_driver_info*)_info; - // TODO allocate cookie + // allocate cookie mmc_disk_handle* handle = new(std::nothrow) mmc_disk_handle; *_cookie = handle; if (handle == NULL) { @@ -233,13 +234,13 @@ mmc_block_free(void* cookie) } -static status_t -mmc_block_read(void* cookie, off_t position, void* buffer, size_t* length) +static status_t +mmc_block_read(void* cookie, off_t pos, void* buffer, size_t* _length) { CALLED(); mmc_disk_handle* handle = (mmc_disk_handle*)cookie; - - return B_NOT_SUPPORTED; + TRACE("Ready to execute %p\n", handle->info->mmc->read_naive); + return handle->info->mmc->read_naive(handle->info->parent, handle->info->rca, pos, buffer, _length); } @@ -269,7 +270,7 @@ mmc_block_get_geometry(mmc_disk_handle* handle, device_geometry* geometry) { struct mmc_disk_csd csd; TRACE("Ready to execute %p\n", handle->info->mmc->execute_command); - handle->info->mmc->execute_command(handle->info->parent, 9, + handle->info->mmc->execute_command(handle->info->parent, SD_SEND_CSD, handle->info->rca << 16, (uint32_t*)&csd); TRACE("CSD: %lx %lx\n", csd.bits[0], csd.bits[1]);