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:
parent
fc87b4a284
commit
dedbe94e46
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user