[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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user