mmc_disk: add SDHC support.

The main differences:
- The initialization sequence requires an additional command (this was
  already done)
- The layout of the CSD register and the way to compute the device
  geometry from it changes
- The read and write commands parameter is a sector number instead of a
  byte position

Change-Id: Ie729e333c9748f36b37acd70c970adfd425cf0b6
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3512
Reviewed-by: Alex von Gluck IV <[email protected]>
This commit is contained in:
Adrien Destugues
2021-01-05 02:06:44 +00:00
committed by waddlesplash
parent e21a1abec4
commit d1fee57dee
7 changed files with 59 additions and 19 deletions
+2 -2
View File
@@ -76,7 +76,7 @@ typedef struct mmc_bus_interface {
uint32_t argument, uint32_t* result); 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)(void* controller, uint8_t command, status_t (*do_io)(void* controller, uint8_t command,
IOOperation* operation); IOOperation* operation, bool offsetAsSectors);
// Execute a command that involves a data transfer. // Execute a command that involves a data transfer.
} mmc_bus_interface; } mmc_bus_interface;
@@ -92,7 +92,7 @@ typedef struct mmc_device_interface {
uint32_t argument, uint32_t* result); 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); uint8_t command, IOOperation* operation, bool offsetAsSectors);
// Execute a command that involves a data transfer. // Execute a command that involves a data transfer.
} mmc_device_interface; } mmc_device_interface;
@@ -86,12 +86,13 @@ MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
status_t status_t
MMCBus::DoIO(uint16_t rca, uint8_t command, IOOperation* operation) MMCBus::DoIO(uint16_t rca, uint8_t command, IOOperation* operation,
bool offsetAsSectors)
{ {
status_t status = _ActivateDevice(rca); status_t status = _ActivateDevice(rca);
if (status != B_OK) if (status != B_OK)
return status; return status;
return fController->do_io(fCookie, command, operation); return fController->do_io(fCookie, command, operation, offsetAsSectors);
} }
@@ -44,7 +44,8 @@ public:
status_t ExecuteCommand(uint8_t command, status_t ExecuteCommand(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);
void AcquireBus() { acquire_sem(fLockSemaphore); } void AcquireBus() { acquire_sem(fLockSemaphore); }
void ReleaseBus() { release_sem(fLockSemaphore); } void ReleaseBus() { release_sem(fLockSemaphore); }
@@ -104,7 +104,7 @@ mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument,
static status_t static status_t
mmc_bus_do_io(device_node* node, uint16_t rca, uint8_t command, mmc_bus_do_io(device_node* node, uint16_t rca, uint8_t command,
IOOperation* operation) IOOperation* operation, bool offsetAsSectors)
{ {
driver_module_info* mmc; driver_module_info* mmc;
void* cookie; void* cookie;
@@ -119,7 +119,7 @@ mmc_bus_do_io(device_node* node, uint16_t rca, uint8_t command,
bus->AcquireBus(); bus->AcquireBus();
status_t result = B_OK; status_t result = B_OK;
result = bus->DoIO(rca, command, operation); result = bus->DoIO(rca, command, operation, offsetAsSectors);
bus->ReleaseBus(); bus->ReleaseBus();
return result; return result;
+8 -7
View File
@@ -52,7 +52,8 @@ class SdhciBus {
status_t InitCheck(); status_t InitCheck();
void Reset(); void Reset();
void SetClock(int kilohertz); void SetClock(int kilohertz);
status_t DoIO(uint8_t command, IOOperation* operation); status_t DoIO(uint8_t command, IOOperation* operation,
bool offsetAsSectors);
private: private:
bool PowerOn(); bool PowerOn();
@@ -340,7 +341,7 @@ SdhciBus::SetClock(int kilohertz)
status_t status_t
SdhciBus::DoIO(uint8_t command, IOOperation* operation) SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors)
{ {
bool isWrite = operation->IsWrite(); bool isWrite = operation->IsWrite();
@@ -385,8 +386,6 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation)
while (length > 0) { while (length > 0) {
size_t toCopy = std::min((generic_size_t)length, size_t toCopy = std::min((generic_size_t)length,
vecs->length - vecOffset); vecs->length - vecOffset);
TRACE("Loop %ld bytes from position %ld to %p\n", toCopy, offset,
vecs->base + vecOffset);
// If the current vec is empty, we can move to the next // If the current vec is empty, we can move to the next
if (toCopy == 0) { if (toCopy == 0) {
@@ -413,7 +412,8 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation)
| TransferMode::kBlockCountEnable | TransferMode::kDmaEnable; | TransferMode::kBlockCountEnable | TransferMode::kDmaEnable;
uint32_t response; uint32_t response;
result = ExecuteCommand(command, offset, &response); result = ExecuteCommand(command,
offset / (offsetAsSectors ? kBlockSize : 1), &response);
if (result != B_OK) if (result != B_OK)
break; break;
@@ -817,12 +817,13 @@ execute_command(void* controller, uint8_t command, uint32_t argument,
static status_t static status_t
do_io(void* controller, uint8_t command, IOOperation* operation) do_io(void* controller, uint8_t command, IOOperation* operation,
bool offsetAsSectors)
{ {
CALLED(); CALLED();
SdhciBus* bus = (SdhciBus*)controller; SdhciBus* bus = (SdhciBus*)controller;
return bus->DoIO(command, operation); return bus->DoIO(command, operation, offsetAsSectors);
} }
@@ -43,15 +43,29 @@ static device_manager_info* sDeviceManager;
struct mmc_disk_csd { struct mmc_disk_csd {
// The content of this register is described in Physical Layer Simplified
// Specification Version 8.00, section 5.3
uint64 bits[2]; uint64 bits[2];
uint8 structure_version() { return bits[1] >> 60; } uint8 structure_version() { return bits[1] >> 54; }
uint8 read_bl_len() { return (bits[1] >> 8) & 0xF; } uint8 read_bl_len() { return (bits[1] >> 8) & 0xF; }
uint16 c_size() uint32 c_size()
{ {
if (structure_version() == 0)
return ((bits[0] >> 54) & 0x3FF) | ((bits[1] & 0x3) << 10); return ((bits[0] >> 54) & 0x3FF) | ((bits[1] & 0x3) << 10);
if (structure_version() == 1)
return (bits[0] >> 40) & 0x3FFFFF;
return ((bits[0] >> 40) & 0xFFFFFF) | ((bits[1] & 0xF) << 24);
}
uint8 c_size_mult()
{
if (structure_version() == 0)
return (bits[0] >> 39) & 0x7;
// In later versions this field is not present in the structure and a
// fixed value is used.
return 8;
} }
uint8 c_size_mult() { return (bits[0] >> 39) & 0x7; }
}; };
@@ -115,7 +129,8 @@ mmc_disk_execute_iorequest(void* data, IOOperation* operation)
command = SD_WRITE_MULTIPLE_BLOCKS; command = SD_WRITE_MULTIPLE_BLOCKS;
else else
command = SD_READ_MULTIPLE_BLOCKS; command = SD_READ_MULTIPLE_BLOCKS;
error = info->mmc->do_io(info->parent, info->rca, command, operation); error = info->mmc->do_io(info->parent, info->rca, command, operation,
(info->flags & kIoCommandOffsetAsSectors) != 0);
if (error != B_OK) { if (error != B_OK) {
info->scheduler->OperationCompleted(operation, error, 0); info->scheduler->OperationCompleted(operation, error, 0);
@@ -185,6 +200,21 @@ mmc_disk_init_driver(device_node* node, void** cookie)
return B_BAD_DATA; return B_BAD_DATA;
} }
uint8_t deviceType;
if (sDeviceManager->get_attr_uint8(info->parent, kMmcTypeAttribute,
&deviceType, true) != B_OK) {
ERROR("Could not get device type\n");
free(info);
return B_BAD_DATA;
}
// SD and MMC cards use byte offsets for IO commands, later ones (SDHC,
// SDXC, ...) use sectors.
if (deviceType == CARD_TYPE_SD || deviceType == CARD_TYPE_MMC)
info->flags = 0;
else
info->flags = kIoCommandOffsetAsSectors;
static const uint32 kBlockSize = 512; // FIXME get it from the CSD static const uint32 kBlockSize = 512; // FIXME get it from the CSD
status_t error; status_t error;
@@ -20,12 +20,19 @@
#include "IOSchedulerSimple.h" #include "IOSchedulerSimple.h"
enum MMCDiskFlags {
kIoCommandOffsetAsSectors = 1,
// IO commands use sector offsets instead of byte offsets
};
// This is the device info structure, allocated once per device // This is the device info structure, allocated once per device
typedef struct { typedef struct {
device_node* node; device_node* node;
device_node* parent; device_node* parent;
mmc_device_interface* mmc; mmc_device_interface* mmc;
uint16_t rca; uint16_t rca;
uint32_t flags;
device_geometry geometry; device_geometry geometry;