diff --git a/headers/private/drivers/mmc.h b/headers/private/drivers/mmc.h index 7057318a41..8638e5fe1e 100644 --- a/headers/private/drivers/mmc.h +++ b/headers/private/drivers/mmc.h @@ -15,6 +15,16 @@ #define MMC_BUS_MODULE_NAME "bus_managers/mmc_bus/driver_v1" +enum { + CARD_TYPE_MMC, + CARD_TYPE_SD, + CARD_TYPE_SDHC, + CARD_TYPE_UHS1, + CARD_TYPE_UHS2, + CARD_TYPE_SDIO +}; + + // Interface between mmc_bus and underlying implementation typedef struct mmc_bus_interface { driver_module_info info; 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 e4d9b6a946..cf289fdd30 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018 Haiku, Inc. All rights reserved. + * Copyright 2018-2020 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -34,7 +34,7 @@ MMCBus::MMCBus(device_node* node) return; } - // TODO enumerate the bus (in a separate thread?) + fSemaphore = create_sem(0, "MMC bus scan"); fWorkerThread = spawn_kernel_thread(WorkerThread, "SD bus controller", B_NORMAL_PRIORITY, this); resume_thread(fWorkerThread); @@ -62,6 +62,14 @@ MMCBus::InitCheck() } +void +MMCBus::Rescan() +{ + // Just wake up the thread for a scan + release_sem(fSemaphore); +} + + status_t MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) { @@ -72,90 +80,125 @@ MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) status_t MMCBus::WorkerThread(void* cookie) { - TRACE("worker thread spawned.\n"); - MMCBus* bus = (MMCBus*)cookie; uint32_t response; - // FIXME wait for bus to signal a card is inserted // 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); - // Probe the voltage range - // 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) | 0x55; - uint32_t hcs = 1 << 30; - if (bus->ExecuteCommand(8, 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; - } else if (response != probe) { - ERROR("Card does not support voltage range (expected %x, reply %x)\n", - probe, response); - // TODO what now? + while (bus->fStatus != B_SHUTTING_DOWN) { + // wait for bus to signal a card is inserted + acquire_sem(bus->fSemaphore); + TRACE("Scanning the bus\n"); + + // Probe the voltage range + // 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 hcs = 1 << 30; + if (bus->ExecuteCommand(8, 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; + } else if (response != probe) { + ERROR("Card does not support voltage range (expected %x, " + "reply %x)\n", probe, response); + // TODO what now? + } + + // Probe OCR, waiting for card to become ready + uint32_t ocr; + do { + uint32_t cardStatus; + while (bus->ExecuteCommand(55, 0, &cardStatus) + == B_BUSY) { + ERROR("Card locked after CMD8...\n"); + snooze(1000000); + } + if ((cardStatus & 0xFFFF8000) != 0) + ERROR("SD card reports error %x\n", cardStatus); + if ((cardStatus & (1 << 5)) == 0) + ERROR("Card did not enter ACMD mode\n"); + + bus->ExecuteCommand(41, hcs | 0xFF8000, &ocr); + + if ((ocr & (1 << 31)) == 0) { + TRACE("Card is busy\n"); + snooze(100000); + } + } while (((ocr & (1 << 31)) == 0)); + + // FIXME this should be asked to each card, when there are multiple + // ones. So ACMD41 should be moved inside the probing loop below? + uint8_t cardType = CARD_TYPE_SD; + + if (ocr & hcs != 0) + cardType = CARD_TYPE_SDHC; + if (ocr & (1 << 29) != 0) + cardType = CARD_TYPE_UHS2; + if (ocr & (1 << 24) != 0) + TRACE("Card supports 1.8v"); + TRACE("Voltage range: %x\n", ocr & 0xFFFFFF); + + // 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 + uint32_t cid[4]; + while (bus->ExecuteCommand(2, 0, cid) == B_OK) { + bus->ExecuteCommand(3, 0, &response); + + TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF); + + if ((response & 0xFF00) != 0x500) { + TRACE("Card did not enter data state\n"); + // This probably means there are no more cards to scan on the + // bus, so exit the loop. + break; + } + + // The card now has an RCA and it entered the data phase, which + // means our initializing job is over, we can pass it on to the + // mmc_disk driver. + + uint32_t vendor = cid[3] & 0xFFFFFF; + char name[6] = {cid[2] >> 24, cid[2] >> 16, cid[2] >> 8, cid[2], + cid[1] >> 24, 0}; + uint32_t serial = (cid[1] << 16) | (cid[0] >> 16); + uint16_t revision = (cid[1] >> 20) & 0xF; + revision *= 100; + revision += (cid[1] >> 16) & 0xF; + 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_VENDOR_ID, B_UINT32_TYPE, {ui32: vendor}}, + { B_DEVICE_ID, B_STRING_TYPE, {string: name}}, + { B_DEVICE_UNIQUE_ID, B_UINT32_TYPE, {ui32: serial}}, + { "mmc/revision", B_UINT16_TYPE, {ui16: revision}}, + { "mmc/month", B_UINT8_TYPE, {ui8: month}}, + { "mmc/year", B_UINT16_TYPE, {ui16: year}}, + { "mmc/rca", B_UINT16_TYPE, {ui16: rca}}, + { "mmc/type", B_UINT8_TYPE, {ui8: cardType}}, + {} + }; + + // publish child device for the card + gDeviceManager->register_node(bus->fNode, MMC_BUS_MODULE_NAME, + attrs, NULL, NULL); + } + + // 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? } - // Probe OCR, waiting for card to become ready - uint32_t ocr; - do { - uint32_t cardStatus; - while (bus->ExecuteCommand(55, 0, &cardStatus) - == B_BUSY) { - // FIXME we shouldn't get here if we handle CMD8 failure properly - ERROR("Card locked after CMD8...\n"); - snooze(1000000); - } - if ((cardStatus & 0xFFFF8000) != 0) - ERROR("SD card reports error\n"); - if ((cardStatus & (1 << 5)) == 0) - ERROR("Card did not enter ACMD mode"); - - bus->ExecuteCommand(41, hcs | 0xFF8000, &ocr); - - if ((ocr & (1 << 31)) == 0) { - TRACE("Card is busy\n"); - snooze(100000); - } - } while (((ocr & (1 << 31)) == 0)); - - if (ocr & hcs != 0) - TRACE("Card is SDHC"); - if (ocr & (1 << 29) != 0) - TRACE("Card supports UHS-II"); - if (ocr & (1 << 24) != 0) - TRACE("Card supports 1.8v"); - TRACE("Voltage range: %x\n", ocr & 0xFFFFFF); - - // TODO send CMD11 to switch to low voltage mode if card supports it? - - uint32_t cid[4]; - bus->ExecuteCommand(2, 0, cid); - - TRACE("Manufacturer: %02x%c%c\n", cid[3] >> 16, cid[3] >> 8, cid[3]); - TRACE("Name: %c%c%c%c%c\n", cid[2] >> 24, cid[2] >> 16, cid[2] >> 8, - cid[2], cid[1] >> 24); - TRACE("Revision: %d.%d\n", (cid[1] >> 20) & 0xF, (cid[1] >> 16) & 0xF); - TRACE("Serial number: %x\n", (cid[1] << 16) | (cid[0] >> 16)); - TRACE("Date: %d/%d\n", cid[0] & 0xF, 2000 + ((cid[0] >> 4) & 0xFF)); - - bus->ExecuteCommand(3, 0, &response); - - TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF); - - if ((response & 0xFF00) != 0x5000) - ERROR("Card did not enter data state\n"); - - // The card now has an RCA and it entered the data phase, which means our - // initializing job is over, we can pass it on to the mmc_disk driver. - - // TODO publish child device for the card - // TODO fill it with attributes from the CID - - // TODO iterate CMD2/CMD3 to assign an RCA to all cards (and publish devices - // for each of them) + 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 c49c37f884..cccae48e67 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h @@ -39,6 +39,7 @@ public: MMCBus(device_node *node); ~MMCBus(); status_t InitCheck(); + void Rescan(); private: status_t ExecuteCommand(uint8_t command, @@ -52,6 +53,7 @@ private: void* fCookie; status_t fStatus; thread_id fWorkerThread; + sem_id fSemaphore; }; 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 f354d40a9f..cb917f8d53 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp @@ -45,6 +45,16 @@ mmc_bus_uninit(void* _device) } +static status_t +mmc_bus_register_child(void* _device) +{ + CALLED(); + MMCBus* device = (MMCBus*)_device; + device->Rescan(); + return B_OK; +} + + static void mmc_bus_removed(void* _device) { @@ -58,7 +68,8 @@ mmc_bus_added_device(device_node* parent) CALLED(); device_attr attributes[] = { - { B_DEVICE_BUS, B_STRING_TYPE, { string: "mmc bus"}}, + { B_DEVICE_BUS, B_STRING_TYPE, { string: "mmc"}}, + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "MMC bus root"}}, { NULL } }; @@ -94,7 +105,7 @@ driver_module_info mmc_bus_device_module = { NULL, // register node mmc_bus_init, mmc_bus_uninit, - NULL, // register child devices + mmc_bus_register_child, NULL, // rescan mmc_bus_removed, NULL, // suspend diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp index 4cb933f5c7..83faa49f6c 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -88,7 +88,7 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq) return; } - fSemaphore = create_sem(0, "SDHCI command"); + fSemaphore = create_sem(0, "SDHCI interrupts"); fStatus = install_io_interrupt_handler(fIrq, sdhci_generic_interrupt, this, 0);