mmc: register devices for detected cards

Change-Id: I90891ead9a425e0e8bd25c2190fe3d430d49411b
Reviewed-on: https://review.haiku-os.org/c/haiku/+/1067
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
Adrien Destugues 2019-02-17 11:54:12 +01:00 committed by waddlesplash
parent fc87b4a284
commit dedbe94e46
5 changed files with 146 additions and 80 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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

View File

@ -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);