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:
parent
d57b8f90e2
commit
34552f8e66
@ -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;
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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); }
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user