sd/mmc: read, naive method

First implementation of reading sectors from an SD card.
This is not the best performance for many reasons:
- No DMA
- Reads only one sector at a time
- Cannot read more than 512 bytes per syscall

Also there are major limitations:
- Cannot read less than 512 bytes. The hardware of course works in full
  sectors. The mmc_disk driver should go through the io scheduler to
  make sure requests have a reasonable size and offset, and nothing
  tries to read just a few bytes in the middle of a sector.
- SD cards only (no SDHC, no MMC)

Architecture problems:
I think too much of the implementation is done in sdhci_pci and should
be moved to the upper layers. However it is difficult to say without
having implemented DMA (which indeed will be at the low level of the
sdhci controller). It doesn't help that the order of operations is a
bit different depending on wether there is DMA or not. In DMA mode you
first prepare the buffer, then run the command. In non-DMA mode you
first send the command, then read the data into the buffer. We need an
API at the mmc_bus level that doesn't care about that low-level detail.
There are other things that the MMC bus should be doing however, such
as switching to different clock speeds depending on which card is
activated and how fast it can go.

At least the following should be done:
- The read method for mmc_bus and sdhci_pci should use a scatter-gather
structure as a parameter instead of a single buffer
- See if can be integrated into ExecuteCommand at sdhci level (it's
essentially a command with an additional data phase)

Change-Id: I688b6c694561074535c9c0c2545f06dc04b06e7d
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3466
Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
Anarchos
2020-12-13 18:56:19 +00:00
committed by Adrien Destugues
parent f6d7f9f599
commit 74b6097078
7 changed files with 378 additions and 86 deletions
+35 -1
View File
@@ -25,6 +25,37 @@ enum {
}; };
// Commands for SD cards defined in SD Specifications Part 1:
// Physical Layer Simplified Specification Version 8.00
// They are in the common .h file for the mmc stack because the SDHCI driver
// currently needs to map them to the corresponding expected response types.
enum SD_COMMANDS {
// Basic commands, class 0
SD_GO_IDLE_STATE = 0,
SD_ALL_SEND_CID = 2,
SD_SEND_RELATIVE_ADDR = 3,
SD_SELECT_DESELECT_CARD = 7,
SD_SEND_IF_COND = 8,
SD_SEND_CSD = 9,
SD_STOP_TRANSMISSION = 12,
// Block oriented read commands, class 2
SD_READ_SINGLE_BLOCK = 17,
SD_READ_MULTIPLE_BLOCKS = 18,
// Application specific commands, class 8
SD_APP_CMD = 55,
// I/O mode commands, class 9
SD_IO_ABORT = 52,
};
enum SDHCI_APPLICATION_COMMANDS {
SD_SEND_OP_COND = 41,
};
// Interface between mmc_bus and underlying implementation (sdhci_pci or any // Interface between mmc_bus and underlying implementation (sdhci_pci or any
// other thing that can execute mmc commands) // other thing that can execute mmc commands)
typedef struct mmc_bus_interface { typedef struct mmc_bus_interface {
@@ -33,15 +64,18 @@ typedef struct mmc_bus_interface {
status_t (*set_clock)(void* controller, uint32_t kilohertz); status_t (*set_clock)(void* controller, uint32_t kilohertz);
status_t (*execute_command)(void* controller, uint8_t command, status_t (*execute_command)(void* controller, uint8_t command,
uint32_t argument, uint32_t* result); uint32_t argument, uint32_t* result);
status_t (*read_naive)(void* controller, off_t pos,
void* buffer, size_t* _length);
} mmc_bus_interface; } mmc_bus_interface;
// Interface between mmc device driver (mmc_disk, sdio drivers, ...) and mmc_bus // Interface between mmc device driver (mmc_disk, sdio drivers, ...) and mmc_bus
typedef struct mmc_device_interface { typedef struct mmc_device_interface {
driver_module_info info; driver_module_info info;
status_t (*execute_command)(device_node* node, uint8_t command, status_t (*execute_command)(device_node* node, uint8_t command,
uint32_t argument, uint32_t* result); uint32_t argument, uint32_t* result);
status_t (*read_naive)(device_node* controller, uint16_t rca, off_t pos,
void* buffer, size_t* _length);
} mmc_device_interface; } mmc_device_interface;
+79 -17
View File
@@ -18,7 +18,8 @@ MMCBus::MMCBus(device_node* node)
fController(NULL), fController(NULL),
fCookie(NULL), fCookie(NULL),
fStatus(B_OK), fStatus(B_OK),
fWorkerThread(0) fWorkerThread(-1),
fActiveDevice(0)
{ {
CALLED(); CALLED();
@@ -34,8 +35,9 @@ MMCBus::MMCBus(device_node* node)
return; return;
} }
fSemaphore = create_sem(0, "MMC bus scan"); fScanSemaphore = create_sem(0, "MMC bus scan");
fWorkerThread = spawn_kernel_thread(WorkerThread, "SD bus controller", fLockSemaphore = create_sem(1, "MMC bus lock");
fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller",
B_NORMAL_PRIORITY, this); B_NORMAL_PRIORITY, this);
resume_thread(fWorkerThread); resume_thread(fWorkerThread);
} }
@@ -52,6 +54,9 @@ MMCBus::~MMCBus()
if (fWorkerThread != 0) if (fWorkerThread != 0)
wait_for_thread(fWorkerThread, &result); wait_for_thread(fWorkerThread, &result);
// TODO power off cards, stop clock, etc if needed. // TODO power off cards, stop clock, etc if needed.
delete_sem(fLockSemaphore);
delete_sem(fScanSemaphore);
} }
@@ -66,41 +71,90 @@ void
MMCBus::Rescan() MMCBus::Rescan()
{ {
// Just wake up the thread for a scan // Just wake up the thread for a scan
release_sem(fSemaphore); release_sem(fScanSemaphore);
} }
status_t status_t
MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
{ {
status_t status = _ActivateDevice(0);
if (status != B_OK)
return status;
return fController->execute_command(fCookie, command, argument, response); return fController->execute_command(fCookie, command, argument, response);
} }
status_t status_t
MMCBus::WorkerThread(void* cookie) MMCBus::Read(uint16_t rca, off_t position, void* buffer, size_t* length)
{
status_t status = _ActivateDevice(rca);
if (status != B_OK)
return status;
return fController->read_naive(fCookie, position, buffer, length);
}
status_t
MMCBus::_ActivateDevice(uint16_t rca)
{
// Do nothing if the device is already activated
if (fActiveDevice == rca)
return B_OK;
uint32_t response;
status_t result;
result = fController->execute_command(fCookie, SD_SELECT_DESELECT_CARD,
((uint32)rca) << 16, &response);
if (result == B_OK)
fActiveDevice = rca;
return result;
}
status_t
MMCBus::_WorkerThread(void* cookie)
{ {
MMCBus* bus = (MMCBus*)cookie; MMCBus* bus = (MMCBus*)cookie;
uint32_t response; uint32_t response;
acquire_sem(bus->fLockSemaphore);
// We assume the bus defaults to 400kHz clock and has already powered on // We assume the bus defaults to 400kHz clock and has already powered on
// cards. // cards.
// Reset all cards on the bus // Reset all cards on the bus
bus->ExecuteCommand(0, 0, NULL); bus->ExecuteCommand(SD_GO_IDLE_STATE, 0, NULL);
while (bus->fStatus != B_SHUTTING_DOWN) { while (bus->fStatus != B_SHUTTING_DOWN) {
release_sem(bus->fLockSemaphore);
// wait for bus to signal a card is inserted // wait for bus to signal a card is inserted
acquire_sem(bus->fSemaphore); // Most of the time the thread will be waiting here, with
// fLockSemaphore released
acquire_sem(bus->fScanSemaphore);
acquire_sem(bus->fLockSemaphore);
TRACE("Scanning the bus\n"); TRACE("Scanning the bus\n");
// Probe the voltage range // Probe the voltage range
enum {
// Table 4-40 in physical layer specification v8.00
// All other values are currently reserved
HOST_27_36V = 1, //Host supplied voltage 2.7-3.6V
};
// An arbitrary value, we just need to check that the response
// containts the same.
static const uint8 kVoltageCheckPattern = 0xAA;
// FIXME MMC cards will not reply to this! They expect CMD1 instead // FIXME MMC cards will not reply to this! They expect CMD1 instead
// SD v1 cards will also not reply, but we can proceed to ACMD41 // SD v1 cards will also not reply, but we can proceed to ACMD41
// If ACMD41 also does not work, it may be an SDIO card, too // If ACMD41 also does not work, it may be an SDIO card, too
uint32_t probe = (1 << 8) | 0xAA; uint32_t probe = (HOST_27_36V << 8) | kVoltageCheckPattern;
uint32_t hcs = 1 << 30; uint32_t hcs = 1 << 30;
if (bus->ExecuteCommand(8, probe, &response) != B_OK) { if (bus->ExecuteCommand(SD_SEND_IF_COND, probe, &response) != B_OK) {
TRACE("Card does not implement CMD8, may be a V1 SD card\n"); TRACE("Card does not implement CMD8, may be a V1 SD card\n");
// Do not check for SDHC support in this case // Do not check for SDHC support in this case
hcs = 0; hcs = 0;
@@ -114,7 +168,7 @@ MMCBus::WorkerThread(void* cookie)
uint32_t ocr; uint32_t ocr;
do { do {
uint32_t cardStatus; uint32_t cardStatus;
while (bus->ExecuteCommand(55, 0, &cardStatus) while (bus->ExecuteCommand(SD_APP_CMD, 0, &cardStatus)
== B_BUSY) { == B_BUSY) {
ERROR("Card locked after CMD8...\n"); ERROR("Card locked after CMD8...\n");
snooze(1000000); snooze(1000000);
@@ -124,7 +178,7 @@ MMCBus::WorkerThread(void* cookie)
if ((cardStatus & (1 << 5)) == 0) if ((cardStatus & (1 << 5)) == 0)
ERROR("Card did not enter ACMD mode\n"); ERROR("Card did not enter ACMD mode\n");
bus->ExecuteCommand(41, hcs | 0xFF8000, &ocr); bus->ExecuteCommand(SD_SEND_OP_COND, hcs | 0xFF8000, &ocr);
if ((ocr & (1 << 31)) == 0) { if ((ocr & (1 << 31)) == 0) {
TRACE("Card is busy\n"); TRACE("Card is busy\n");
@@ -146,11 +200,17 @@ MMCBus::WorkerThread(void* cookie)
// TODO send CMD11 to switch to low voltage mode if card supports it? // TODO send CMD11 to switch to low voltage mode if card supports it?
// iterate CMD2/CMD3 to assign an RCA to all cards and publish devices // We use CMD2 (ALL_SEND_CID) and CMD3 (SEND_RELATIVE_ADDR) to assign
// for each of them // an RCA to all cards. Initially all cards have an RCA of 0 and will
// all receive CMD2. But only ne of them will reply (they do collision
// detection while sending the CID in reply). We assign a new RCA to
// that first card, and repeat the process with the remaining ones
// until no one answers to CMD2. Then we know all cards have an RCA
// (and a matching published device on our side).
uint32_t cid[4]; uint32_t cid[4];
while (bus->ExecuteCommand(2, 0, cid) == B_OK) {
bus->ExecuteCommand(3, 0, &response); while (bus->ExecuteCommand(SD_ALL_SEND_CID, 0, cid) == B_OK) {
bus->ExecuteCommand(SD_SEND_RELATIVE_ADDR, 0, &response);
TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF); TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF);
@@ -175,7 +235,7 @@ MMCBus::WorkerThread(void* cookie)
uint8_t month = cid[0] & 0xF; uint8_t month = cid[0] & 0xF;
uint16_t year = 2000 + ((cid[0] >> 4) & 0xFF); uint16_t year = 2000 + ((cid[0] >> 4) & 0xFF);
uint16_t rca = response >> 16; uint16_t rca = response >> 16;
device_attr attrs[] = { device_attr attrs[] = {
{ B_DEVICE_BUS, B_STRING_TYPE, {string: "mmc" }}, { B_DEVICE_BUS, B_STRING_TYPE, {string: "mmc" }},
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "mmc device" }}, { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "mmc device" }},
@@ -200,6 +260,8 @@ MMCBus::WorkerThread(void* cookie)
// to detect added/removed cards? // to detect added/removed cards?
} }
release_sem(bus->fLockSemaphore);
TRACE("poller thread terminating"); TRACE("poller thread terminating");
return B_OK; return B_OK;
} }
+11 -3
View File
@@ -41,10 +41,16 @@ public:
status_t InitCheck(); status_t InitCheck();
void Rescan(); void Rescan();
private:
status_t ExecuteCommand(uint8_t command, status_t ExecuteCommand(uint8_t command,
uint32_t argument, uint32_t* response); uint32_t argument, uint32_t* response);
static status_t WorkerThread(void*); status_t Read(uint16_t rca, off_t position, void* buffer,
size_t* length);
void AcquireBus() { acquire_sem(fLockSemaphore); }
void ReleaseBus() { release_sem(fLockSemaphore); }
private:
status_t _ActivateDevice(uint16_t rca);
static status_t _WorkerThread(void*);
private: private:
@@ -53,7 +59,9 @@ private:
void* fCookie; void* fCookie;
status_t fStatus; status_t fStatus;
thread_id fWorkerThread; thread_id fWorkerThread;
sem_id fSemaphore; sem_id fScanSemaphore;
sem_id fLockSemaphore;
uint16 fActiveDevice;
}; };
@@ -82,20 +82,43 @@ static status_t
mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument, mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument,
uint32_t* result) uint32_t* result)
{ {
// FIXME store these in the bus cookie or something instead of // FIXME store the parent cookie in the bus cookie or something instead of
// getting/putting the parents each time. // getting/putting the parent each time.
mmc_bus_interface* sdhci; driver_module_info* mmc;
void* cookie; void* cookie;
TRACE("In mmc_bus_execute_command\n"); TRACE("In mmc_bus_execute_command\n");
device_node* parent = gDeviceManager->get_parent_node(node); device_node* parent = gDeviceManager->get_parent_node(node);
device_node* grandparent = gDeviceManager->get_parent_node(parent); gDeviceManager->get_driver(parent, &mmc, &cookie);
gDeviceManager->get_driver(grandparent, (driver_module_info**)&sdhci,
&cookie);
gDeviceManager->put_node(grandparent);
gDeviceManager->put_node(parent); gDeviceManager->put_node(parent);
return sdhci->execute_command(cookie, command, argument, result); MMCBus* bus = (MMCBus*)cookie;
bus->AcquireBus();
status_t error = bus->ExecuteCommand(command, argument, result);
bus->ReleaseBus();
return error;
}
static status_t
mmc_bus_read_naive(device_node* node, uint16_t rca, off_t pos, void* buffer,
size_t* _length)
{
// FIXME store the parent cookie in the bus cookie or something instead of
// getting/putting the parent each time.
driver_module_info* mmc;
void* cookie;
device_node* parent = gDeviceManager->get_parent_node(node);
gDeviceManager->get_driver(parent, &mmc, &cookie);
gDeviceManager->put_node(parent);
MMCBus* bus = (MMCBus*)cookie;
bus->AcquireBus();
status_t result = bus->Read(rca, pos, buffer, _length);
bus->ReleaseBus();
return result;
} }
@@ -148,7 +171,8 @@ mmc_device_interface mmc_bus_controller_module = {
NULL, NULL,
NULL NULL
}, },
mmc_bus_execute_command mmc_bus_execute_command,
mmc_bus_read_naive
}; };
+163 -35
View File
@@ -49,7 +49,8 @@ class SdhciBus {
status_t InitCheck(); status_t InitCheck();
void Reset(); void Reset();
void SetClock(int kilohertz); void SetClock(int kilohertz);
status_t ReadNaive(off_t pos, void* buffer, size_t* _length);
private: private:
bool PowerOn(); bool PowerOn();
void RecoverError(); void RecoverError();
@@ -89,7 +90,7 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq)
} }
fSemaphore = create_sem(0, "SDHCI interrupts"); fSemaphore = create_sem(0, "SDHCI interrupts");
fStatus = install_io_interrupt_handler(fIrq, fStatus = install_io_interrupt_handler(fIrq,
sdhci_generic_interrupt, this, 0); sdhci_generic_interrupt, this, 0);
@@ -113,23 +114,25 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq)
return; return;
} }
// FIXME do we need all these? Wouldn't card insertion/removal and command
// completion be enough?
EnableInterrupts(SDHCI_INT_CMD_CMP EnableInterrupts(SDHCI_INT_CMD_CMP
| SDHCI_INT_TRANS_CMP | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM | SDHCI_INT_BUF_READ_READY | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM);
| SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_INDEX
| SDHCI_INT_BUS_POWER | SDHCI_INT_END_BIT);
fRegisters->interrupt_status_enable |= SDHCI_INT_ERROR; // We want to see the error bits in the status register, but not have an
// interrupt trigger on them (we get a "command complete" interrupt on
// errors already)
fRegisters->interrupt_status_enable |= SDHCI_INT_ERROR
| SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_INDEX
| SDHCI_INT_BUS_POWER | SDHCI_INT_END_BIT;
} }
SdhciBus::~SdhciBus() SdhciBus::~SdhciBus()
{ {
EnableInterrupts(0);
if (fSemaphore != 0) if (fSemaphore != 0)
delete_sem(fSemaphore); delete_sem(fSemaphore);
EnableInterrupts(0);
if (fIrq != 0) if (fIrq != 0)
remove_io_interrupt_handler(fIrq, sdhci_generic_interrupt, this); remove_io_interrupt_handler(fIrq, sdhci_generic_interrupt, this);
@@ -146,45 +149,83 @@ SdhciBus::EnableInterrupts(uint32_t mask)
} }
// #pragma mark -
/*
PartA2, SD Host Controller Simplified Specification, Version 4.20
§3.7.1.1 The sequence to issue an SD Command
*/
status_t status_t
SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
{ {
TRACE("ExecuteCommand(%d, %x)\n", command, argument); TRACE("ExecuteCommand(%d, %x)\n", command, argument);
// Check if it's possible to send a command right now.
// It is not possible to send a command as long as the command line is busy.
// The spec says we should wait, but we can't do that on kernel side, since
// it leaves no chance for the upper layers to handle the problem. So we
// just say we're busy and the caller can retry later.
// Note that this should normally never happen: the command line is busy
// only during command execution, and we don't leave this function with ac
// command running.
if (fRegisters->present_state.CommandInhibit()) { if (fRegisters->present_state.CommandInhibit()) {
ERROR("Execution aborted, command inhibit\n"); ERROR("Execution aborted, command inhibit\n");
return B_BUSY; return B_BUSY;
} }
fRegisters->argument = argument;
uint32_t replyType; uint32_t replyType;
switch(command) { switch(command) {
case 0: case SD_GO_IDLE_STATE:
replyType = Command::kNoReplyType; replyType = Command::kNoReplyType;
break; break;
case 55: case SD_ALL_SEND_CID:
replyType = Command::kR1Type; case SD_SEND_CSD:
break;
case 2:
case 9:
replyType = Command::kR2Type; replyType = Command::kR2Type;
break; break;
case SD_SEND_RELATIVE_ADDR:
replyType = Command::kR6Type;
break;
case SD_SELECT_DESELECT_CARD:
replyType = Command::kR1bType;
break;
case SD_SEND_IF_COND:
replyType = Command::kR7Type;
break;
case SD_READ_SINGLE_BLOCK:
case SD_READ_MULTIPLE_BLOCKS:
replyType = Command::kR1Type | Command::kDataPresent;
break;
case SD_APP_CMD:
replyType = Command::kR1Type;
break;
case 41: // ACMD case 41: // ACMD
replyType = Command::kR3Type; replyType = Command::kR3Type;
break; break;
case 3:
replyType = Command::kR6Type;
break;
case 8:
replyType = Command::kR7Type;
break;
default: default:
ERROR("Unknown command\n"); ERROR("Unknown command\n");
return B_BAD_DATA; return B_BAD_DATA;
} }
// Check if DATA line is available (if needed)
if ((replyType & Command::k32BitResponseCheckBusy) != 0
&& command != SD_STOP_TRANSMISSION && command != SD_IO_ABORT) {
if (fRegisters->present_state.DataInhibit()) {
ERROR("Execution aborted, data inhibit\n");
return B_BUSY;
}
}
//FIXME : Assign only at this point, if needed :
// -32 bit block count/SDMA system address
// -block size
// -16-bit block count
// -transfer mode
fRegisters->argument = argument;
fRegisters->command.SendCommand(command, replyType); fRegisters->command.SendCommand(command, replyType);
// Wait for command response to be available (either "command complete" or
// "buffer read ready" interrupt will happen, depending on the command)
acquire_sem(fSemaphore); acquire_sem(fSemaphore);
if (fCommandResult & SDHCI_INT_ERROR) { if (fCommandResult & SDHCI_INT_ERROR) {
@@ -220,12 +261,13 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
response[2] = fRegisters->response[2]; response[2] = fRegisters->response[2];
response[3] = fRegisters->response[3]; response[3] = fRegisters->response[3];
break; break;
default: default:
// No response // No response
break; break;
} }
ERROR("Command execution complete\n"); ERROR("Command execution %d complete\n", command);
return B_OK; return B_OK;
} }
@@ -285,6 +327,62 @@ SdhciBus::SetClock(int kilohertz)
} }
status_t
SdhciBus::ReadNaive(off_t pos, void* buffer, size_t* _length)
{
// TODO read multiple blocks at once (don't ignore _length)
fRegisters->block_size = 512;
fRegisters->block_count = 1;
fRegisters->transfer_mode = TransferMode::kSingle | TransferMode::kRead
| TransferMode::kAutoCmdDisabled | TransferMode::kNoDmaOrNoData;
uint32_t response;
status_t result;
result = ExecuteCommand(SD_READ_SINGLE_BLOCK, pos, &response);
if (result != B_OK)
return result;
TRACE("Command response: %02x\n", response);
if (fCommandResult & SDHCI_INT_BUF_READ_READY == 0) {
TRACE("No data!\n");
return B_ERROR;
}
// We don't know how to read more than 512 bytes (CMD18 would be needed)
if (*_length > 512)
*_length = 512;
// read block data from Buffer Data Port register
// TODO use DMA instead
size_t to_read = *_length / sizeof(uint32_t);
size_t to_drop = 512 / sizeof(uint32_t) - to_read;
uint32_t* dest = (uint32_t*)buffer;
while(to_read > 0) {
*dest = fRegisters->buffer_data_port;
TRACE("read : 0x%x", *dest);
dest++;
to_read--;
}
// We cannot read less than one sector, so we have to drop the extra data.
// This will be fixed when we use DMA and the IO scheduler (since it makes
// sure to only ask for complete sectors).
// Currently the IO scheduler does not support bounce buffers for non-DMA
// transfers.
while(to_drop > 0) {
(void*)fRegisters->buffer_data_port;
to_drop--;
}
// wait for command complete interrupt
acquire_sem(fSemaphore);
return B_OK;
}
bool bool
SdhciBus::PowerOn() SdhciBus::PowerOn()
{ {
@@ -428,17 +526,29 @@ SdhciBus::RecoverError()
int32 int32
SdhciBus::HandleInterrupt() SdhciBus::HandleInterrupt()
{ {
CALLED();
#if 0
// We could use the slot register to quickly see for which slot the
// interrupt is. But since we have an interrupt handler call for each slot
// anyway, it's just as simple to let each of them scan its own interrupt
// status register.
if ( !(fRegisters->slot_interrupt_status & (1 << fSlot)) ) {
TRACE("interrupt not for me.\n");
return B_UNHANDLED_INTERRUPT;
}
#endif
uint32_t intmask = fRegisters->interrupt_status; uint32_t intmask = fRegisters->interrupt_status;
// Shortcut: exit early if there is no interrupt or if the register is
// clearly invalid.
if ((intmask == 0) || (intmask == 0xffffffff)) { if ((intmask == 0) || (intmask == 0xffffffff)) {
return B_UNHANDLED_INTERRUPT; return B_UNHANDLED_INTERRUPT;
} }
TRACE("interrupt function called %x\n", intmask); TRACE("interrupt function called %x\n", intmask);
// FIXME use the global "slot interrupt" register to quickly decide if an
// interrupt is targetted to this slot
// handling card presence interrupt // handling card presence interrupt
if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) { if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) {
uint32_t card_present = ((intmask & SDHCI_INT_CARD_INS) != 0); uint32_t card_present = ((intmask & SDHCI_INT_CARD_INS) != 0);
@@ -455,37 +565,43 @@ SdhciBus::HandleInterrupt()
fRegisters->interrupt_status |= (intmask & fRegisters->interrupt_status |= (intmask &
(SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)); (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM));
TRACE("Card presence interrupt handled\n"); TRACE("Card presence interrupt handled\n");
return B_HANDLED_INTERRUPT;
} }
// handling command interrupt // handling command interrupt
if (intmask & SDHCI_INT_CMD_MASK) { if (intmask & SDHCI_INT_CMD_MASK) {
fCommandResult = intmask; fCommandResult = intmask;
// Save the status before clearing so the thhread can handle it // Save the status before clearing so the thread can handle it
fRegisters->interrupt_status |= (intmask & SDHCI_INT_CMD_MASK); fRegisters->interrupt_status |= (intmask & SDHCI_INT_CMD_MASK);
// Notify the thread // Notify the thread
release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE); release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE);
TRACE("Command interrupt handled\n"); TRACE("Command complete interrupt handled\n");
}
return B_HANDLED_INTERRUPT; // handling data transfer interrupt
if (intmask & SDHCI_INT_BUF_READ_READY) {
TRACE("buffer read ready interrupt raised");
fRegisters->interrupt_status |= (intmask & SDHCI_INT_BUF_READ_READY);
release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE);
} }
// handling bus power interrupt // handling bus power interrupt
if (intmask & SDHCI_INT_BUS_POWER) { if (intmask & SDHCI_INT_BUS_POWER) {
fRegisters->interrupt_status |= SDHCI_INT_BUS_POWER; fRegisters->interrupt_status |= SDHCI_INT_BUS_POWER;
TRACE("card is consuming too much power\n"); TRACE("card is consuming too much power\n");
return B_HANDLED_INTERRUPT;
} }
// Check that all interrupts have been cleared (we check all the ones we
// enabled, so that should always be the case)
intmask = fRegisters->slot_interrupt_status; intmask = fRegisters->slot_interrupt_status;
if (intmask != 0) { if (intmask != 0) {
ERROR("Remaining interrupts at end of handler: %x\n", intmask); ERROR("Remaining interrupts at end of handler: %x\n", intmask);
} }
return B_UNHANDLED_INTERRUPT; return B_HANDLED_INTERRUPT;
} }
// #pragma mark -
static void static void
@@ -639,6 +755,17 @@ execute_command(void* controller, uint8_t command, uint32_t argument,
} }
//Very naive read protocol : non DMA, 32 bits at a time (size of Buffer Data Port)
static status_t
read_naive(void* controller, off_t pos, void* buffer, size_t* _length)
{
CALLED();
SdhciBus* bus = (SdhciBus*)controller;
return bus->ReadNaive(pos, buffer, _length);
}
module_dependency module_dependencies[] = { module_dependency module_dependencies[] = {
{ MMC_BUS_MODULE_NAME, (module_info**)&gMMCBusController}, { MMC_BUS_MODULE_NAME, (module_info**)&gMMCBusController},
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
@@ -666,6 +793,7 @@ static mmc_bus_interface gSDHCIPCIDeviceModule = {
set_clock, set_clock,
execute_command, execute_command,
read_naive
}; };
+50 -15
View File
@@ -19,6 +19,36 @@
#define SDHCI_BUS_TYPE_NAME "bus/sdhci/v1" #define SDHCI_BUS_TYPE_NAME "bus/sdhci/v1"
class TransferMode {
public:
uint16_t Bits() { return fBits; }
// TODO response interrupt
// TODO response check
static const uint8_t kR1 = 0 << 6;
static const uint8_t kR5 = 1 << 6;
static const uint8_t kMulti = 1 << 5;
static const uint8_t kSingle = 0 << 5;
static const uint8_t kRead = 1 << 4;
static const uint8_t kWrite = 0 << 4;
static const uint8_t kAutoCmdDisabled = 0 << 2;
static const uint8_t kAutoCmd12Enable = 1 << 2;
static const uint8_t kAutoCmd23Enable = 2 << 2;
static const uint8_t kAutoCmdAutoSelect =
kAutoCmd23Enable | kAutoCmd12Enable;
// TODO block count enable
static const uint8_t kDmaEnable = 1;
static const uint8_t kNoDmaOrNoData = 0;
private:
volatile uint16_t fBits;
} __attribute__((packed));
class Command { class Command {
public: public:
@@ -35,14 +65,17 @@ class Command {
static const uint8_t kSubCommand = 0x4; static const uint8_t kSubCommand = 0x4;
static const uint8_t kReplySizeMask = 0x3; static const uint8_t kReplySizeMask = 0x3;
static const uint8_t k32BitResponse = 0x2; static const uint8_t k32BitResponse = 0x2;
static const uint8_t k128BitResponse = 0x1; static const uint8_t k128BitResponse = 0x1;
static const uint8_t k32BitResponseCheckBusy = 0x3;
// For simplicity pre-define the standard response types from the SD // For simplicity pre-define the standard response types from the SD
// card specification // card specification
static const uint8_t kNoReplyType = 0; static const uint8_t kNoReplyType = 0;
static const uint8_t kR1Type = kCheckIndex | kCRCEnable static const uint8_t kR1Type = kCheckIndex | kCRCEnable
| k32BitResponse; | k32BitResponse;
static const uint8_t kR2Type = kCRCEnable | k128BitResponse; static const uint8_t kR1bType = (kCheckIndex | kCRCEnable
| k32BitResponseCheckBusy) & (~ kDataPresent);
static const uint8_t kR2Type = kCRCEnable | k128BitResponse;
static const uint8_t kR3Type = k32BitResponse; static const uint8_t kR3Type = k32BitResponse;
static const uint8_t kR6Type = kCheckIndex | k32BitResponse; static const uint8_t kR6Type = kCheckIndex | k32BitResponse;
static const uint8_t kR7Type = kDataPresent | kCheckIndex | kCRCEnable static const uint8_t kR7Type = kDataPresent | kCheckIndex | kCRCEnable
@@ -62,6 +95,7 @@ class PresentState {
bool IsCardInserted() { return fBits & (1 << 16); } bool IsCardInserted() { return fBits & (1 << 16); }
bool CommandInhibit() { return fBits & (1 << 0); } bool CommandInhibit() { return fBits & (1 << 0); }
bool DataInhibit() { return fBits & (1 << 1); }
private: private:
volatile uint32_t fBits; volatile uint32_t fBits;
@@ -134,24 +168,25 @@ class SoftwareReset {
} __attribute__((packed)); } __attribute__((packed));
/* Interrupt registers */ // #pragma mark Interrupt registers
#define SDHCI_INT_CMD_CMP 0x00000001 // command complete enable #define SDHCI_INT_CMD_CMP 0x00000001 // command complete enable
#define SDHCI_INT_TRANS_CMP 0x00000002 // transfer complete enable #define SDHCI_INT_TRANS_CMP 0x00000002 // transfer complete enable
#define SDHCI_INT_CARD_INS 0x00000040 // card insertion enable #define SDHCI_INT_BUF_READ_READY 0x00000020 // buffer read ready enable
#define SDHCI_INT_CARD_REM 0x00000080 // card removal enable #define SDHCI_INT_CARD_INS 0x00000040 // card insertion enable
#define SDHCI_INT_ERROR 0x00008000 // error #define SDHCI_INT_CARD_REM 0x00000080 // card removal enable
#define SDHCI_INT_TIMEOUT 0x00010000 // Timeout error #define SDHCI_INT_ERROR 0x00008000 // error
#define SDHCI_INT_CRC 0x00020000 // CRC error #define SDHCI_INT_TIMEOUT 0x00010000 // Timeout error
#define SDHCI_INT_END_BIT 0x00040000 // end bit error #define SDHCI_INT_CRC 0x00020000 // CRC error
#define SDHCI_INT_INDEX 0x00080000 // index error #define SDHCI_INT_END_BIT 0x00040000 // end bit error
#define SDHCI_INT_BUS_POWER 0x00800000 // power fail #define SDHCI_INT_INDEX 0x00080000 // index error
#define SDHCI_INT_BUS_POWER 0x00800000 // power fail
#define SDHCI_INT_CMD_ERROR_MASK (SDHCI_INT_TIMEOUT | \ #define SDHCI_INT_CMD_ERROR_MASK (SDHCI_INT_TIMEOUT | \
SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
#define SDHCI_INT_CMD_MASK (SDHCI_INT_CMD_CMP | SDHCI_INT_CMD_ERROR_MASK) #define SDHCI_INT_CMD_MASK (SDHCI_INT_CMD_CMP | SDHCI_INT_CMD_ERROR_MASK)
// #pragma mark -
class Capabilities class Capabilities
{ {
public: public:
@@ -175,7 +210,7 @@ class HostControllerVersion {
const uint8_t vendorVersion; const uint8_t vendorVersion;
} __attribute__((packed)); } __attribute__((packed));
// #pragma mark -
struct registers { struct registers {
// SD command generation // SD command generation
volatile uint32_t system_address; volatile uint32_t system_address;
@@ -1,5 +1,6 @@
/* /*
* Copyright 2018-2020 Haiku, Inc. All rights reserved. * Copyright 2018-2020 Haiku, Inc. All rights reserved.
* Copyright 2020, Viveris Technologies.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -200,7 +201,7 @@ mmc_block_open(void* _info, const char* path, int openMode, void** _cookie)
CALLED(); CALLED();
mmc_disk_driver_info* info = (mmc_disk_driver_info*)_info; mmc_disk_driver_info* info = (mmc_disk_driver_info*)_info;
// TODO allocate cookie // allocate cookie
mmc_disk_handle* handle = new(std::nothrow) mmc_disk_handle; mmc_disk_handle* handle = new(std::nothrow) mmc_disk_handle;
*_cookie = handle; *_cookie = handle;
if (handle == NULL) { if (handle == NULL) {
@@ -233,13 +234,13 @@ mmc_block_free(void* cookie)
} }
static status_t static status_t
mmc_block_read(void* cookie, off_t position, void* buffer, size_t* length) mmc_block_read(void* cookie, off_t pos, void* buffer, size_t* _length)
{ {
CALLED(); CALLED();
mmc_disk_handle* handle = (mmc_disk_handle*)cookie; mmc_disk_handle* handle = (mmc_disk_handle*)cookie;
TRACE("Ready to execute %p\n", handle->info->mmc->read_naive);
return B_NOT_SUPPORTED; return handle->info->mmc->read_naive(handle->info->parent, handle->info->rca, pos, buffer, _length);
} }
@@ -269,7 +270,7 @@ mmc_block_get_geometry(mmc_disk_handle* handle, device_geometry* geometry)
{ {
struct mmc_disk_csd csd; struct mmc_disk_csd csd;
TRACE("Ready to execute %p\n", handle->info->mmc->execute_command); TRACE("Ready to execute %p\n", handle->info->mmc->execute_command);
handle->info->mmc->execute_command(handle->info->parent, 9, handle->info->mmc->execute_command(handle->info->parent, SD_SEND_CSD,
handle->info->rca << 16, (uint32_t*)&csd); handle->info->rca << 16, (uint32_t*)&csd);
TRACE("CSD: %lx %lx\n", csd.bits[0], csd.bits[1]); TRACE("CSD: %lx %lx\n", csd.bits[0], csd.bits[1]);