mmc: register devices for detected cards
Change-Id: I90891ead9a425e0e8bd25c2190fe3d430d49411b Reviewed-on: https://review.haiku-os.org/c/haiku/+/1067 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
fc87b4a284
commit
dedbe94e46
@@ -15,6 +15,16 @@
|
|||||||
#define MMC_BUS_MODULE_NAME "bus_managers/mmc_bus/driver_v1"
|
#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
|
// Interface between mmc_bus and underlying implementation
|
||||||
typedef struct mmc_bus_interface {
|
typedef struct mmc_bus_interface {
|
||||||
driver_module_info info;
|
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.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -34,7 +34,7 @@ MMCBus::MMCBus(device_node* node)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO enumerate the bus (in a separate thread?)
|
fSemaphore = create_sem(0, "MMC bus scan");
|
||||||
fWorkerThread = spawn_kernel_thread(WorkerThread, "SD bus controller",
|
fWorkerThread = spawn_kernel_thread(WorkerThread, "SD bus controller",
|
||||||
B_NORMAL_PRIORITY, this);
|
B_NORMAL_PRIORITY, this);
|
||||||
resume_thread(fWorkerThread);
|
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
|
status_t
|
||||||
MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
|
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
|
status_t
|
||||||
MMCBus::WorkerThread(void* cookie)
|
MMCBus::WorkerThread(void* cookie)
|
||||||
{
|
{
|
||||||
TRACE("worker thread spawned.\n");
|
|
||||||
|
|
||||||
MMCBus* bus = (MMCBus*)cookie;
|
MMCBus* bus = (MMCBus*)cookie;
|
||||||
uint32_t response;
|
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
|
// We assume the bus defaults to 400kHz clock and has already powered on
|
||||||
// cards.
|
// cards.
|
||||||
|
|
||||||
// Reset all cards on the bus
|
// Reset all cards on the bus
|
||||||
bus->ExecuteCommand(0, 0, NULL);
|
bus->ExecuteCommand(0, 0, NULL);
|
||||||
|
|
||||||
// Probe the voltage range
|
while (bus->fStatus != B_SHUTTING_DOWN) {
|
||||||
// FIXME MMC cards will not reply to this! They expect CMD1 instead
|
// wait for bus to signal a card is inserted
|
||||||
// SD v1 cards will also not reply, but we can proceed to ACMD41
|
acquire_sem(bus->fSemaphore);
|
||||||
// If ACMD41 also does not work, it may be an SDIO card, too
|
TRACE("Scanning the bus\n");
|
||||||
uint32_t probe = (1 << 8) | 0x55;
|
|
||||||
uint32_t hcs = 1 << 30;
|
// Probe the voltage range
|
||||||
if (bus->ExecuteCommand(8, probe, &response) != B_OK) {
|
// FIXME MMC cards will not reply to this! They expect CMD1 instead
|
||||||
TRACE("Card does not implement CMD8, may be a V1 SD card\n");
|
// SD v1 cards will also not reply, but we can proceed to ACMD41
|
||||||
// Do not check for SDHC support in this case
|
// If ACMD41 also does not work, it may be an SDIO card, too
|
||||||
hcs = 0;
|
uint32_t probe = (1 << 8) | 0xAA;
|
||||||
} else if (response != probe) {
|
uint32_t hcs = 1 << 30;
|
||||||
ERROR("Card does not support voltage range (expected %x, reply %x)\n",
|
if (bus->ExecuteCommand(8, probe, &response) != B_OK) {
|
||||||
probe, response);
|
TRACE("Card does not implement CMD8, may be a V1 SD card\n");
|
||||||
// TODO what now?
|
// 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
|
TRACE("poller thread terminating");
|
||||||
uint32_t ocr;
|
return B_OK;
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public:
|
|||||||
MMCBus(device_node *node);
|
MMCBus(device_node *node);
|
||||||
~MMCBus();
|
~MMCBus();
|
||||||
status_t InitCheck();
|
status_t InitCheck();
|
||||||
|
void Rescan();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t ExecuteCommand(uint8_t command,
|
status_t ExecuteCommand(uint8_t command,
|
||||||
@@ -52,6 +53,7 @@ private:
|
|||||||
void* fCookie;
|
void* fCookie;
|
||||||
status_t fStatus;
|
status_t fStatus;
|
||||||
thread_id fWorkerThread;
|
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
|
static void
|
||||||
mmc_bus_removed(void* _device)
|
mmc_bus_removed(void* _device)
|
||||||
{
|
{
|
||||||
@@ -58,7 +68,8 @@ mmc_bus_added_device(device_node* parent)
|
|||||||
CALLED();
|
CALLED();
|
||||||
|
|
||||||
device_attr attributes[] = {
|
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 }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -94,7 +105,7 @@ driver_module_info mmc_bus_device_module = {
|
|||||||
NULL, // register node
|
NULL, // register node
|
||||||
mmc_bus_init,
|
mmc_bus_init,
|
||||||
mmc_bus_uninit,
|
mmc_bus_uninit,
|
||||||
NULL, // register child devices
|
mmc_bus_register_child,
|
||||||
NULL, // rescan
|
NULL, // rescan
|
||||||
mmc_bus_removed,
|
mmc_bus_removed,
|
||||||
NULL, // suspend
|
NULL, // suspend
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fSemaphore = create_sem(0, "SDHCI command");
|
fSemaphore = create_sem(0, "SDHCI interrupts");
|
||||||
|
|
||||||
fStatus = install_io_interrupt_handler(fIrq,
|
fStatus = install_io_interrupt_handler(fIrq,
|
||||||
sdhci_generic_interrupt, this, 0);
|
sdhci_generic_interrupt, this, 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user