[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 {
|
enum SDHCI_APPLICATION_COMMANDS {
|
||||||
|
SD_SET_BUS_WIDTH = 6,
|
||||||
SD_SEND_OP_COND = 41,
|
SD_SEND_OP_COND = 41,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,8 +91,8 @@ typedef struct mmc_bus_interface {
|
|||||||
// type of card.
|
// type of card.
|
||||||
typedef struct mmc_device_interface {
|
typedef struct mmc_device_interface {
|
||||||
driver_module_info info;
|
driver_module_info info;
|
||||||
status_t (*execute_command)(device_node* node, uint8_t command,
|
status_t (*execute_command)(device_node* node, uint16_t rca,
|
||||||
uint32_t argument, uint32_t* result);
|
uint8_t command, uint32_t argument, uint32_t* result);
|
||||||
// Execute a command with no I/O phase
|
// Execute a command with no I/O phase
|
||||||
status_t (*do_io)(device_node* controller, uint16_t rca,
|
status_t (*do_io)(device_node* controller, uint16_t rca,
|
||||||
uint8_t command, IOOperation* operation, bool offsetAsSectors);
|
uint8_t command, IOOperation* operation, bool offsetAsSectors);
|
||||||
|
|||||||
@@ -78,9 +78,10 @@ MMCBus::Rescan()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
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)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
return fController->execute_command(fCookie, command, argument, response);
|
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
|
status_t
|
||||||
MMCBus::_ActivateDevice(uint16_t rca)
|
MMCBus::_ActivateDevice(uint16_t rca)
|
||||||
{
|
{
|
||||||
@@ -144,17 +152,21 @@ MMCBus::_WorkerThread(void* cookie)
|
|||||||
do {
|
do {
|
||||||
bus->_AcquireScanSemaphore();
|
bus->_AcquireScanSemaphore();
|
||||||
TRACE("Reset the bus...\n");
|
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));
|
TRACE("CMD0 result: %s\n", strerror(result));
|
||||||
} while (result != B_OK);
|
} while (result != B_OK);
|
||||||
|
|
||||||
// Need to wait at least 8 clock cycles after CMD0 before sending the next
|
// Need to wait at least 8 clock cycles after CMD0 before sending the next
|
||||||
// command
|
// command. With the default 400kHz clock that would be 20 microseconds,
|
||||||
snooze(100000);
|
// but apparently we need more.
|
||||||
|
snooze(20000);
|
||||||
|
|
||||||
while (bus->fStatus != B_SHUTTING_DOWN) {
|
while (bus->fStatus != B_SHUTTING_DOWN) {
|
||||||
TRACE("Scanning the bus\n");
|
TRACE("Scanning the bus\n");
|
||||||
|
|
||||||
|
// Use the low speed clock for scanning
|
||||||
|
bus->SetClock(400);
|
||||||
|
|
||||||
// Probe the voltage range
|
// Probe the voltage range
|
||||||
enum {
|
enum {
|
||||||
// Table 4-40 in physical layer specification v8.00
|
// 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
|
// If ACMD41 also does not work, it may be an SDIO card, too
|
||||||
uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern;
|
uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern;
|
||||||
uint32_t hcs = 1 << 30;
|
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");
|
TRACE("Card does not implement CMD8, may be a V1 SD card\n");
|
||||||
// Do not check for SDHC support in this case
|
// Do not check for SDHC support in this case
|
||||||
hcs = 0;
|
hcs = 0;
|
||||||
@@ -187,7 +199,7 @@ MMCBus::_WorkerThread(void* cookie)
|
|||||||
uint32_t ocr;
|
uint32_t ocr;
|
||||||
do {
|
do {
|
||||||
uint32_t cardStatus;
|
uint32_t cardStatus;
|
||||||
while (bus->ExecuteCommand(SD_APP_CMD, 0, &cardStatus)
|
while (bus->ExecuteCommand(0, SD_APP_CMD, 0, &cardStatus)
|
||||||
== B_BUSY) {
|
== B_BUSY) {
|
||||||
ERROR("Card locked after CMD8...\n");
|
ERROR("Card locked after CMD8...\n");
|
||||||
snooze(1000000);
|
snooze(1000000);
|
||||||
@@ -197,7 +209,7 @@ MMCBus::_WorkerThread(void* cookie)
|
|||||||
if ((cardStatus & (1 << 5)) == 0)
|
if ((cardStatus & (1 << 5)) == 0)
|
||||||
ERROR("Card did not enter ACMD mode\n");
|
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) {
|
if ((ocr & (1 << 31)) == 0) {
|
||||||
TRACE("Card is busy\n");
|
TRACE("Card is busy\n");
|
||||||
@@ -228,8 +240,8 @@ MMCBus::_WorkerThread(void* cookie)
|
|||||||
// (and a matching published device on our side).
|
// (and a matching published device on our side).
|
||||||
uint32_t cid[4];
|
uint32_t cid[4];
|
||||||
|
|
||||||
while (bus->ExecuteCommand(SD_ALL_SEND_CID, 0, cid) == B_OK) {
|
while (bus->ExecuteCommand(0, SD_ALL_SEND_CID, 0, cid) == B_OK) {
|
||||||
bus->ExecuteCommand(SD_SEND_RELATIVE_ADDR, 0, &response);
|
bus->ExecuteCommand(0, SD_SEND_RELATIVE_ADDR, 0, &response);
|
||||||
|
|
||||||
TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF);
|
TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF);
|
||||||
|
|
||||||
@@ -274,6 +286,11 @@ MMCBus::_WorkerThread(void* cookie)
|
|||||||
attrs, NULL, NULL);
|
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
|
// 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 "ping" all RCAs somehow? Or is there an interrupt we can look for
|
||||||
// to detect added/removed cards?
|
// to detect added/removed cards?
|
||||||
|
|||||||
@@ -41,12 +41,14 @@ public:
|
|||||||
status_t InitCheck();
|
status_t InitCheck();
|
||||||
void Rescan();
|
void Rescan();
|
||||||
|
|
||||||
status_t ExecuteCommand(uint8_t command,
|
status_t ExecuteCommand(uint16_t rca, uint8_t command,
|
||||||
uint32_t argument, uint32_t* response);
|
uint32_t argument, uint32_t* response);
|
||||||
status_t DoIO(uint16_t rca, uint8_t command,
|
status_t DoIO(uint16_t rca, uint8_t command,
|
||||||
IOOperation* operation,
|
IOOperation* operation,
|
||||||
bool offsetAsSectors);
|
bool offsetAsSectors);
|
||||||
|
|
||||||
|
void SetClock(int frequency);
|
||||||
|
|
||||||
void AcquireBus() { acquire_sem(fLockSemaphore); }
|
void AcquireBus() { acquire_sem(fLockSemaphore); }
|
||||||
void ReleaseBus() { release_sem(fLockSemaphore); }
|
void ReleaseBus() { release_sem(fLockSemaphore); }
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ mmc_bus_added_device(device_node* parent)
|
|||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument,
|
mmc_bus_execute_command(device_node* node, uint16_t rca, uint8_t command,
|
||||||
uint32_t* result)
|
uint32_t argument, uint32_t* result)
|
||||||
{
|
{
|
||||||
// FIXME store the parent cookie in the bus cookie or something instead of
|
// FIXME store the parent cookie in the bus cookie or something instead of
|
||||||
// getting/putting the parent each time.
|
// 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;
|
MMCBus* bus = (MMCBus*)cookie;
|
||||||
|
|
||||||
bus->AcquireBus();
|
bus->AcquireBus();
|
||||||
status_t error = bus->ExecuteCommand(command, argument, result);
|
status_t error = bus->ExecuteCommand(rca, command, argument, result);
|
||||||
bus->ReleaseBus();
|
bus->ReleaseBus();
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,9 +223,10 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
|
|||||||
replyType = Command::kR1Type | Command::kDataPresent;
|
replyType = Command::kR1Type | Command::kDataPresent;
|
||||||
break;
|
break;
|
||||||
case SD_APP_CMD:
|
case SD_APP_CMD:
|
||||||
|
case SD_SET_BUS_WIDTH: // SD Application command
|
||||||
replyType = Command::kR1Type;
|
replyType = Command::kR1Type;
|
||||||
break;
|
break;
|
||||||
case 41: // ACMD
|
case SD_SEND_OP_COND: // SD Application command
|
||||||
replyType = Command::kR3Type;
|
replyType = Command::kR3Type;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -388,7 +389,8 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors)
|
|||||||
off_t offset = operation->Offset();
|
off_t offset = operation->Offset();
|
||||||
generic_size_t length = operation->Length();
|
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
|
// Check that the IO scheduler did its job in following our DMA restrictions
|
||||||
// We can start a read only at a sector boundary
|
// 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;
|
offset += toCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != B_OK)
|
return result;
|
||||||
return result;
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ mmc_block_get_geometry(mmc_disk_driver_info* info, device_geometry* geometry)
|
|||||||
{
|
{
|
||||||
struct mmc_disk_csd csd;
|
struct mmc_disk_csd csd;
|
||||||
TRACE("Get geometry\n");
|
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);
|
info->rca << 16, (uint32_t*)&csd);
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
TRACE("Could not get CSD! %s\n", strerror(error));
|
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]);
|
TRACE("CSD: %" PRIx64 " %" PRIx64 "\n", csd.bits[0], csd.bits[1]);
|
||||||
|
|
||||||
if (csd.structure_version() < 3) {
|
if (csd.structure_version() >= 3) {
|
||||||
geometry->bytes_per_sector = 1 << csd.read_bl_len();
|
TRACE("unknown CSD version %d\n", csd.structure_version());
|
||||||
geometry->sectors_per_track = csd.c_size() + 1;
|
return B_NOT_SUPPORTED;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("unknown CSD version %d\n", csd.structure_version());
|
geometry->bytes_per_sector = 1 << csd.read_bl_len();
|
||||||
return B_NOT_SUPPORTED;
|
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;
|
mmc_disk_driver_info* info = (mmc_disk_driver_info*)_info;
|
||||||
*_cookie = 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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user