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 <kallisti5@unixzen.com>
This commit is contained in:
parent
e21a1abec4
commit
d1fee57dee
@ -76,7 +76,7 @@ typedef struct mmc_bus_interface {
|
||||
uint32_t argument, uint32_t* result);
|
||||
// Execute a command with no I/O phase
|
||||
status_t (*do_io)(void* controller, uint8_t command,
|
||||
IOOperation* operation);
|
||||
IOOperation* operation, bool offsetAsSectors);
|
||||
// Execute a command that involves a data transfer.
|
||||
} mmc_bus_interface;
|
||||
|
||||
@ -92,7 +92,7 @@ typedef struct mmc_device_interface {
|
||||
uint32_t argument, uint32_t* result);
|
||||
// Execute a command with no I/O phase
|
||||
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.
|
||||
} mmc_device_interface;
|
||||
|
||||
|
@ -86,12 +86,13 @@ MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
|
||||
|
||||
|
||||
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);
|
||||
if (status != B_OK)
|
||||
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,
|
||||
uint32_t argument, uint32_t* response);
|
||||
status_t DoIO(uint16_t rca, uint8_t command,
|
||||
IOOperation* operation);
|
||||
IOOperation* operation,
|
||||
bool offsetAsSectors);
|
||||
|
||||
void AcquireBus() { acquire_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
|
||||
mmc_bus_do_io(device_node* node, uint16_t rca, uint8_t command,
|
||||
IOOperation* operation)
|
||||
IOOperation* operation, bool offsetAsSectors)
|
||||
{
|
||||
driver_module_info* mmc;
|
||||
void* cookie;
|
||||
@ -119,7 +119,7 @@ mmc_bus_do_io(device_node* node, uint16_t rca, uint8_t command,
|
||||
bus->AcquireBus();
|
||||
status_t result = B_OK;
|
||||
|
||||
result = bus->DoIO(rca, command, operation);
|
||||
result = bus->DoIO(rca, command, operation, offsetAsSectors);
|
||||
|
||||
bus->ReleaseBus();
|
||||
return result;
|
||||
|
@ -52,7 +52,8 @@ class SdhciBus {
|
||||
status_t InitCheck();
|
||||
void Reset();
|
||||
void SetClock(int kilohertz);
|
||||
status_t DoIO(uint8_t command, IOOperation* operation);
|
||||
status_t DoIO(uint8_t command, IOOperation* operation,
|
||||
bool offsetAsSectors);
|
||||
|
||||
private:
|
||||
bool PowerOn();
|
||||
@ -340,7 +341,7 @@ SdhciBus::SetClock(int kilohertz)
|
||||
|
||||
|
||||
status_t
|
||||
SdhciBus::DoIO(uint8_t command, IOOperation* operation)
|
||||
SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors)
|
||||
{
|
||||
bool isWrite = operation->IsWrite();
|
||||
|
||||
@ -385,8 +386,6 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation)
|
||||
while (length > 0) {
|
||||
size_t toCopy = std::min((generic_size_t)length,
|
||||
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 (toCopy == 0) {
|
||||
@ -413,7 +412,8 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation)
|
||||
| TransferMode::kBlockCountEnable | TransferMode::kDmaEnable;
|
||||
|
||||
uint32_t response;
|
||||
result = ExecuteCommand(command, offset, &response);
|
||||
result = ExecuteCommand(command,
|
||||
offset / (offsetAsSectors ? kBlockSize : 1), &response);
|
||||
if (result != B_OK)
|
||||
break;
|
||||
|
||||
@ -817,12 +817,13 @@ execute_command(void* controller, uint8_t command, uint32_t argument,
|
||||
|
||||
|
||||
static status_t
|
||||
do_io(void* controller, uint8_t command, IOOperation* operation)
|
||||
do_io(void* controller, uint8_t command, IOOperation* operation,
|
||||
bool offsetAsSectors)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
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 {
|
||||
// The content of this register is described in Physical Layer Simplified
|
||||
// Specification Version 8.00, section 5.3
|
||||
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; }
|
||||
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;
|
||||
else
|
||||
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) {
|
||||
info->scheduler->OperationCompleted(operation, error, 0);
|
||||
@ -185,6 +200,21 @@ mmc_disk_init_driver(device_node* node, void** cookie)
|
||||
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
|
||||
status_t error;
|
||||
|
||||
|
@ -20,12 +20,19 @@
|
||||
#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
|
||||
typedef struct {
|
||||
device_node* node;
|
||||
device_node* parent;
|
||||
mmc_device_interface* mmc;
|
||||
uint16_t rca;
|
||||
uint32_t flags;
|
||||
|
||||
device_geometry geometry;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user