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:
committed by
waddlesplash
parent
e21a1abec4
commit
d1fee57dee
@@ -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;
|
||||||
|
|||||||
@@ -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()
|
||||||
{
|
{
|
||||||
return ((bits[0] >> 54) & 0x3FF) | ((bits[1] & 0x3) << 10);
|
if (structure_version() == 0)
|
||||||
|
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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user