sd/mmc: enable 4-bit data transfers

It works, but performance is still unexpectedly low (getting about
50kB/s write speed) with almost no CPU load.

Change-Id: I7da3ee70c8b379c4e6c2250d67f880c78635874f
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3630
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
Adrien Destugues 2021-01-14 18:35:05 +01:00 committed by Adrien Destugues
parent d57b8f90e2
commit 34552f8e66
7 changed files with 93 additions and 7 deletions

View File

@ -81,6 +81,8 @@ typedef struct mmc_bus_interface {
// Execute a command that involves a data transfer.
void (*set_scan_semaphore)(void* controller, sem_id sem);
// Pass the semaphore used for device rescan to the bus controller
void (*set_bus_width)(void* controller, int width);
// Set the data bus width to 1, 4 or 8 bit mode.
} mmc_bus_interface;
@ -97,6 +99,8 @@ typedef struct mmc_device_interface {
status_t (*do_io)(device_node* controller, uint16_t rca,
uint8_t command, IOOperation* operation, bool offsetAsSectors);
// Execute a command that involves a data transfer.
void (*set_bus_width)(device_node* controller, int width);
// Set the data bus width to 1, 4 or 8 bit mode.
} mmc_device_interface;

View File

@ -106,6 +106,13 @@ MMCBus::SetClock(int frequency)
}
void
MMCBus::SetBusWidth(int width)
{
fController->set_bus_width(fCookie, width);
}
status_t
MMCBus::_ActivateDevice(uint16_t rca)
{
@ -151,6 +158,12 @@ MMCBus::_WorkerThread(void* cookie)
status_t result;
do {
bus->_AcquireScanSemaphore();
// In case we just got a "card inserted", wait for things to settle
// a bit before continuing (there can be glitches while the card is
// being inserted)
snooze(30000);
TRACE("Reset the bus...\n");
result = bus->ExecuteCommand(0, SD_GO_IDLE_STATE, 0, NULL);
TRACE("CMD0 result: %s\n", strerror(result));
@ -158,14 +171,16 @@ MMCBus::_WorkerThread(void* cookie)
// Need to wait at least 8 clock cycles after CMD0 before sending the next
// command. With the default 400kHz clock that would be 20 microseconds,
// but apparently we need more.
// but we need to wait at least 20ms here, otherwise the next command times
// out
snooze(20000);
while (bus->fStatus != B_SHUTTING_DOWN) {
TRACE("Scanning the bus\n");
// Use the low speed clock for scanning
// Use the low speed clock and 1bit bus width for scanning
bus->SetClock(400);
bus->SetBusWidth(1);
// Probe the voltage range
enum {

View File

@ -48,6 +48,7 @@ public:
bool offsetAsSectors);
void SetClock(int frequency);
void SetBusWidth(int width);
void AcquireBus() { acquire_sem(fLockSemaphore); }
void ReleaseBus() { release_sem(fLockSemaphore); }

View File

@ -124,6 +124,26 @@ mmc_bus_do_io(device_node* node, uint16_t rca, uint8_t command,
}
static void
mmc_bus_set_width(device_node* node, int width)
{
driver_module_info* mmc;
void* cookie;
// FIXME store the parent cookie in the bus cookie or something instead of
// getting/putting the parent each time.
device_node* parent = gDeviceManager->get_parent_node(node);
gDeviceManager->get_driver(parent, &mmc, &cookie);
gDeviceManager->put_node(parent);
MMCBus* bus = (MMCBus*)cookie;
bus->AcquireBus();
bus->SetBusWidth(width);
bus->ReleaseBus();
}
static status_t
std_ops(int32 op, ...)
{
@ -174,7 +194,8 @@ mmc_device_interface mmc_bus_controller_module = {
NULL
},
mmc_bus_execute_command,
mmc_bus_do_io
mmc_bus_do_io,
mmc_bus_set_width
};

View File

@ -56,6 +56,7 @@ class SdhciBus {
status_t DoIO(uint8_t command, IOOperation* operation,
bool offsetAsSectors);
void SetScanSemaphore(sem_id sem);
void SetBusWidth(int width);
private:
bool PowerOn();
@ -389,7 +390,7 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors)
off_t offset = operation->Offset();
generic_size_t length = operation->Length();
TRACE("%s %" B_PRIuSIZE " bytes at %" B_PRIdOFF "\n",
TRACE("%s %"B_PRIu64 " bytes at %" B_PRIdOFF "\n",
isWrite ? "Write" : "Read", length, offset);
// Check that the IO scheduler did its job in following our DMA restrictions
@ -489,6 +490,28 @@ SdhciBus::SetScanSemaphore(sem_id sem)
}
void
SdhciBus::SetBusWidth(int width)
{
uint8_t widthBits;
switch(width) {
case 1:
widthBits = HostControl::kDataTransfer1Bit;
break;
case 4:
widthBits = HostControl::kDataTransfer4Bit;
break;
case 8:
widthBits = HostControl::kDataTransfer8Bit;
break;
default:
panic("Incorrect bitwidth value");
return;
}
fRegisters->host_control.SetDataTransferWidth(widthBits);
}
bool
SdhciBus::PowerOn()
{
@ -962,6 +985,16 @@ set_scan_semaphore(void* controller, sem_id sem)
}
static void
set_bus_width(void* controller, int width)
{
CALLED();
SdhciBus* bus = (SdhciBus*)controller;
return bus->SetBusWidth(width);
}
module_dependency module_dependencies[] = {
{ MMC_BUS_MODULE_NAME, (module_info**)&gMMCBusController},
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
@ -990,7 +1023,8 @@ static mmc_bus_interface gSDHCIPCIDeviceModule = {
set_clock,
execute_command,
do_io,
set_scan_semaphore
set_scan_semaphore,
set_bus_width
};

View File

@ -224,10 +224,20 @@ class HostControl {
value = (value & ~kDmaMask) | dmaMode;
}
void SetDataTransferWidth(uint8_t width)
{
value = (value & ~kDataTransferWidthMask) | width;
}
static const uint8_t kDmaMask = 3 << 3;
static const uint8_t kSdma = 0 << 3;
static const uint8_t kAdma32 = 2 << 3;
static const uint8_t kAdma64 = 3 << 3;
static const uint8_t kDataTransferWidthMask = 0x22;
static const uint8_t kDataTransfer1Bit = 0;
static const uint8_t kDataTransfer4Bit = 2;
static const uint8_t kDataTransfer8Bit = 0x20;
private:
volatile uint8_t value;
} __attribute__((packed));

View File

@ -170,7 +170,6 @@ mmc_block_get_geometry(mmc_disk_driver_info* info, device_geometry* geometry)
geometry->read_only = false; // TODO check write protect switch?
geometry->write_once = false;
#if 0
// 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)
@ -180,7 +179,9 @@ mmc_block_get_geometry(mmc_disk_driver_info* info, device_geometry* geometry)
info->rca << 16, &cardStatus);
info->mmc->execute_command(info->parent, info->rca, SD_SET_BUS_WIDTH,
k4BitMode, &cardStatus);
#endif
// From now on we use 4 bit mode
info->mmc->set_bus_width(info->parent, 4);
return B_OK;
}