[WIP] sd/mmc: enable high speed transfers

- Switch to 25MHz clock
- Switch to 4bit transfers mode (the default is 1bit)

Reading and writing SD cards do not seem to work anymore with these
changes. I get invalid data on read, and on write, an interrupt is never
called in some cases.
This commit is contained in:
Adrien Destugues
2021-01-12 22:01:43 +01:00
parent cf15598562
commit 522c141d53
6 changed files with 68 additions and 35 deletions
+3 -2
View File
@@ -58,6 +58,7 @@ enum SD_COMMANDS {
enum SDHCI_APPLICATION_COMMANDS {
SD_SET_BUS_WIDTH = 6,
SD_SEND_OP_COND = 41,
};
@@ -90,8 +91,8 @@ typedef struct mmc_bus_interface {
// type of card.
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 (*execute_command)(device_node* node, uint16_t rca,
uint8_t command, uint32_t argument, uint32_t* result);
// Execute a command with no I/O phase
status_t (*do_io)(device_node* controller, uint16_t rca,
uint8_t command, IOOperation* operation, bool offsetAsSectors);
+27 -10
View File
@@ -78,9 +78,10 @@ MMCBus::Rescan()
status_t
MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
MMCBus::ExecuteCommand(uint16_t rca, uint8_t command, uint32_t argument,
uint32_t* response)
{
status_t status = _ActivateDevice(0);
status_t status = _ActivateDevice(rca);
if (status != B_OK)
return status;
return fController->execute_command(fCookie, command, argument, response);
@@ -98,6 +99,13 @@ MMCBus::DoIO(uint16_t rca, uint8_t command, IOOperation* operation,
}
void
MMCBus::SetClock(int frequency)
{
fController->set_clock(fCookie, frequency);
}
status_t
MMCBus::_ActivateDevice(uint16_t rca)
{
@@ -144,17 +152,21 @@ MMCBus::_WorkerThread(void* cookie)
do {
bus->_AcquireScanSemaphore();
TRACE("Reset the bus...\n");
result = bus->ExecuteCommand(SD_GO_IDLE_STATE, 0, NULL);
result = bus->ExecuteCommand(0, SD_GO_IDLE_STATE, 0, NULL);
TRACE("CMD0 result: %s\n", strerror(result));
} while (result != B_OK);
// Need to wait at least 8 clock cycles after CMD0 before sending the next
// command
snooze(100000);
// command. With the default 400kHz clock that would be 20 microseconds,
// but apparently we need more.
snooze(20000);
while (bus->fStatus != B_SHUTTING_DOWN) {
TRACE("Scanning the bus\n");
// Use the low speed clock for scanning
bus->SetClock(400);
// Probe the voltage range
enum {
// Table 4-40 in physical layer specification v8.00
@@ -171,7 +183,7 @@ MMCBus::_WorkerThread(void* cookie)
// If ACMD41 also does not work, it may be an SDIO card, too
uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern;
uint32_t hcs = 1 << 30;
if (bus->ExecuteCommand(SD_SEND_IF_COND, probe, &response) != B_OK) {
if (bus->ExecuteCommand(0, 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;
@@ -187,7 +199,7 @@ MMCBus::_WorkerThread(void* cookie)
uint32_t ocr;
do {
uint32_t cardStatus;
while (bus->ExecuteCommand(SD_APP_CMD, 0, &cardStatus)
while (bus->ExecuteCommand(0, SD_APP_CMD, 0, &cardStatus)
== B_BUSY) {
ERROR("Card locked after CMD8...\n");
snooze(1000000);
@@ -197,7 +209,7 @@ MMCBus::_WorkerThread(void* cookie)
if ((cardStatus & (1 << 5)) == 0)
ERROR("Card did not enter ACMD mode\n");
bus->ExecuteCommand(SD_SEND_OP_COND, hcs | 0xFF8000, &ocr);
bus->ExecuteCommand(0, SD_SEND_OP_COND, hcs | 0xFF8000, &ocr);
if ((ocr & (1 << 31)) == 0) {
TRACE("Card is busy\n");
@@ -228,8 +240,8 @@ MMCBus::_WorkerThread(void* cookie)
// (and a matching published device on our side).
uint32_t cid[4];
while (bus->ExecuteCommand(SD_ALL_SEND_CID, 0, cid) == B_OK) {
bus->ExecuteCommand(SD_SEND_RELATIVE_ADDR, 0, &response);
while (bus->ExecuteCommand(0, SD_ALL_SEND_CID, 0, cid) == B_OK) {
bus->ExecuteCommand(0, SD_SEND_RELATIVE_ADDR, 0, &response);
TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF);
@@ -274,6 +286,11 @@ MMCBus::_WorkerThread(void* cookie)
attrs, NULL, NULL);
}
// TODO if there is a single card active, check if it supports CMD6
// (spec version 1.10 or later in SCR). If it does, check if CMD6 can
// enable high speed mode, use that to go to 50MHz instead of 25.
bus->SetClock(25000);
// 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?
@@ -41,12 +41,14 @@ public:
status_t InitCheck();
void Rescan();
status_t ExecuteCommand(uint8_t command,
status_t ExecuteCommand(uint16_t rca, uint8_t command,
uint32_t argument, uint32_t* response);
status_t DoIO(uint16_t rca, uint8_t command,
IOOperation* operation,
bool offsetAsSectors);
void SetClock(int frequency);
void AcquireBus() { acquire_sem(fLockSemaphore); }
void ReleaseBus() { release_sem(fLockSemaphore); }
private:
@@ -78,8 +78,8 @@ mmc_bus_added_device(device_node* parent)
static status_t
mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument,
uint32_t* result)
mmc_bus_execute_command(device_node* node, uint16_t rca, uint8_t command,
uint32_t argument, uint32_t* result)
{
// FIXME store the parent cookie in the bus cookie or something instead of
// getting/putting the parent each time.
@@ -94,7 +94,7 @@ mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument,
MMCBus* bus = (MMCBus*)cookie;
bus->AcquireBus();
status_t error = bus->ExecuteCommand(command, argument, result);
status_t error = bus->ExecuteCommand(rca, command, argument, result);
bus->ReleaseBus();
return error;
}
+5 -6
View File
@@ -223,9 +223,10 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
replyType = Command::kR1Type | Command::kDataPresent;
break;
case SD_APP_CMD:
case SD_SET_BUS_WIDTH: // SD Application command
replyType = Command::kR1Type;
break;
case 41: // ACMD
case SD_SEND_OP_COND: // SD Application command
replyType = Command::kR3Type;
break;
default:
@@ -388,7 +389,8 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors)
off_t offset = operation->Offset();
generic_size_t length = operation->Length();
TRACE("%s %ld bytes at %ld\n", isWrite ? "Write" : "Read", length, offset);
TRACE("%s %" B_PRIuSIZE " bytes at %" B_PRIdOFF "\n",
isWrite ? "Write" : "Read", length, offset);
// Check that the IO scheduler did its job in following our DMA restrictions
// We can start a read only at a sector boundary
@@ -468,10 +470,7 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors)
offset += toCopy;
}
if (result != B_OK)
return result;
return B_OK;
return result;
}
@@ -147,7 +147,7 @@ mmc_block_get_geometry(mmc_disk_driver_info* info, device_geometry* geometry)
{
struct mmc_disk_csd csd;
TRACE("Get geometry\n");
status_t error = info->mmc->execute_command(info->parent, SD_SEND_CSD,
status_t error = info->mmc->execute_command(info->parent, 0, SD_SEND_CSD,
info->rca << 16, (uint32_t*)&csd);
if (error != B_OK) {
TRACE("Could not get CSD! %s\n", strerror(error));
@@ -156,20 +156,31 @@ mmc_block_get_geometry(mmc_disk_driver_info* info, device_geometry* geometry)
TRACE("CSD: %" PRIx64 " %" PRIx64 "\n", csd.bits[0], csd.bits[1]);
if (csd.structure_version() < 3) {
geometry->bytes_per_sector = 1 << csd.read_bl_len();
geometry->sectors_per_track = csd.c_size() + 1;
geometry->cylinder_count = 1 << (csd.c_size_mult() + 2);
geometry->head_count = 1;
geometry->device_type = B_DISK;
geometry->removable = true; // TODO detect eMMC which isn't
geometry->read_only = false; // TODO check write protect switch?
geometry->write_once = false;
return B_OK;
if (csd.structure_version() >= 3) {
TRACE("unknown CSD version %d\n", csd.structure_version());
return B_NOT_SUPPORTED;
}
TRACE("unknown CSD version %d\n", csd.structure_version());
return B_NOT_SUPPORTED;
geometry->bytes_per_sector = 1 << csd.read_bl_len();
geometry->sectors_per_track = csd.c_size() + 1;
geometry->cylinder_count = 1 << (csd.c_size_mult() + 2);
geometry->head_count = 1;
geometry->device_type = B_DISK;
geometry->removable = true; // TODO detect eMMC which isn't
geometry->read_only = false; // TODO check write protect switch?
geometry->write_once = false;
// This function will be called before all data transfers, so we use this
// opportunity to switch the card to 4-bit data transfers (instead of the
// default 1 bit mode)
uint32_t cardStatus;
const uint32 k4BitMode = 2;
info->mmc->execute_command(info->parent, info->rca, SD_APP_CMD,
info->rca << 16, &cardStatus);
info->mmc->execute_command(info->parent, info->rca, SD_SET_BUS_WIDTH,
k4BitMode, &cardStatus);
return B_OK;
}
@@ -308,6 +319,9 @@ mmc_block_init_device(void* _info, void** _cookie)
mmc_disk_driver_info* info = (mmc_disk_driver_info*)_info;
*_cookie = info;
// Note: it is not possible to execute commands here, because this is called
// with the mmc_bus locked for enumeration (and still using slow clock).
return B_OK;
}