diff --git a/headers/private/drivers/mmc.h b/headers/private/drivers/mmc.h index 98fd6ab83c..0bf832091f 100644 --- a/headers/private/drivers/mmc.h +++ b/headers/private/drivers/mmc.h @@ -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; diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp index 448304a155..45124d2893 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp @@ -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 { diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h index fe42c06b75..0adc3b9c49 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h @@ -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); } diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp index e09d4ccebd..a1bac8d808 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp @@ -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 }; diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp index 92d13329b4..18cb8ffe81 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -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 }; diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.h b/src/add-ons/kernel/busses/mmc/sdhci_pci.h index ec5d0ad0b2..381e503384 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.h +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.h @@ -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)); diff --git a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp index c80d7fc444..9d2c8d25c6 100644 --- a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp @@ -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; }