sdhci_pci: support for inserting cards after boot.

Change-Id: Ic67ea38bb80b35528ebb1a150d1a916a56184e69
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3617
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
Adrien Destugues 2021-01-08 21:15:00 +01:00 committed by Adrien Destugues
parent b18298348a
commit 2413679304
6 changed files with 92 additions and 43 deletions

View File

@ -78,6 +78,8 @@ typedef struct mmc_bus_interface {
status_t (*do_io)(void* controller, uint8_t command,
IOOperation* operation, bool offsetAsSectors);
// 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
} mmc_bus_interface;

View File

@ -40,6 +40,8 @@ MMCBus::MMCBus(device_node* node)
fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller",
B_NORMAL_PRIORITY, this);
resume_thread(fWorkerThread);
fController->set_scan_semaphore(fCookie, fScanSemaphore);
}
@ -115,6 +117,14 @@ MMCBus::_ActivateDevice(uint16_t rca)
}
void MMCBus::_AcquireScanSemaphore()
{
release_sem(fLockSemaphore);
acquire_sem(fScanSemaphore);
acquire_sem(fLockSemaphore);
}
status_t
MMCBus::_WorkerThread(void* cookie)
{
@ -127,16 +137,22 @@ MMCBus::_WorkerThread(void* cookie)
// cards.
// Reset all cards on the bus
bus->ExecuteCommand(SD_GO_IDLE_STATE, 0, NULL);
// This does not work if the bus has not been powered on yet (the command
// will timeout), in that case we wait until asked to scan again when a
// card has been inserted and powered on.
status_t result;
do {
bus->_AcquireScanSemaphore();
TRACE("Reset the bus...\n");
result = bus->ExecuteCommand(SD_GO_IDLE_STATE, 0, NULL);
TRACE("CMD0 result: %s\n", strerror(result));
} while (result != B_OK);
// Need to wait at least 8 clock cycles after CMD0 before sending the next
// command
snooze(100000);
while (bus->fStatus != B_SHUTTING_DOWN) {
release_sem(bus->fLockSemaphore);
// wait for bus to signal a card is inserted
// 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");
// Probe the voltage range
@ -162,10 +178,12 @@ MMCBus::_WorkerThread(void* cookie)
} else if (response != probe) {
ERROR("Card does not support voltage range (expected %x, "
"reply %x)\n", probe, response);
// TODO what now?
// TODO we should power off the bus in this case.
}
// Probe OCR, waiting for card to become ready
// We keep repeating ACMD41 until the card replies that it is
// initialized.
uint32_t ocr;
do {
uint32_t cardStatus;
@ -259,6 +277,10 @@ MMCBus::_WorkerThread(void* cookie)
// FIXME we also need to unpublish devices that are gone. Probably need
// to "ping" all RCAs somehow? Or is there an interrupt we can look for
// to detect added/removed cards?
// Wait for the next scan request
// The thread will spend most of its time waiting here
bus->_AcquireScanSemaphore();
}
release_sem(bus->fLockSemaphore);

View File

@ -51,6 +51,7 @@ public:
void ReleaseBus() { release_sem(fLockSemaphore); }
private:
status_t _ActivateDevice(uint16_t rca);
void _AcquireScanSemaphore();
static status_t _WorkerThread(void*);
private:

View File

@ -49,9 +49,7 @@ mmc_bus_uninit(void* _device)
static status_t
mmc_bus_register_child(void* _device)
{
CALLED();
MMCBus* device = (MMCBus*)_device;
device->Rescan();
// Nothing to do, child devices are registered by the scanning thread
return B_OK;
}

View File

@ -46,6 +46,7 @@ class SdhciBus {
~SdhciBus();
void EnableInterrupts(uint32_t mask);
void DisableInterrupts();
status_t ExecuteCommand(uint8_t command, uint32_t argument,
uint32_t* response);
int32 HandleInterrupt();
@ -54,6 +55,7 @@ class SdhciBus {
void SetClock(int kilohertz);
status_t DoIO(uint8_t command, IOOperation* operation,
bool offsetAsSectors);
void SetScanSemaphore(sem_id sem);
private:
bool PowerOn();
@ -64,6 +66,7 @@ class SdhciBus {
uint32_t fCommandResult;
uint8_t fIrq;
sem_id fSemaphore;
sem_id fScanSemaphore;
status_t fStatus;
};
@ -117,16 +120,12 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq)
// Then we configure the clock to the frequency needed for initialization
SetClock(400);
// And we turn on the power supply to the card
// FIXME maybe this should only be done when a card is inserted?
if (!PowerOn()) {
ERROR("Failed to power on the card\n");
fStatus = B_NO_INIT;
return;
}
// Turn on the power supply to the card, if there is a card inserted
PowerOn();
EnableInterrupts(SDHCI_INT_CMD_CMP | SDHCI_INT_CARD_INS
| SDHCI_INT_CARD_REM | SDHCI_INT_TRANS_CMP);
// Finally, configure some useful interrupts
EnableInterrupts(SDHCI_INT_CMD_CMP | SDHCI_INT_CARD_REM
| SDHCI_INT_TRANS_CMP);
// 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
@ -139,7 +138,7 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq)
SdhciBus::~SdhciBus()
{
EnableInterrupts(0);
DisableInterrupts();
if (fSemaphore != 0)
delete_sem(fSemaphore);
@ -155,8 +154,16 @@ SdhciBus::~SdhciBus()
void
SdhciBus::EnableInterrupts(uint32_t mask)
{
fRegisters->interrupt_status_enable = mask;
fRegisters->interrupt_signal_enable = mask;
fRegisters->interrupt_status_enable |= mask;
fRegisters->interrupt_signal_enable |= mask;
}
void
SdhciBus::DisableInterrupts()
{
fRegisters->interrupt_status_enable = 0;
fRegisters->interrupt_signal_enable = 0;
}
@ -468,6 +475,21 @@ SdhciBus::DoIO(uint8_t command, IOOperation* operation, bool offsetAsSectors)
}
void
SdhciBus::SetScanSemaphore(sem_id sem)
{
fScanSemaphore = sem;
// If there is already a card in, start a scan immediately
if (fRegisters->present_state.IsCardInserted())
release_sem(fScanSemaphore);
// We can now enable the card insertion interrupt for next time a card
// is inserted
EnableInterrupts(SDHCI_INT_CARD_INS);
}
bool
SdhciBus::PowerOn()
{
@ -532,23 +554,21 @@ SdhciBus::HandleInterrupt()
TRACE("interrupt function called %x\n", intmask);
// handling card presence interrupt
if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) {
uint32_t card_present = ((intmask & SDHCI_INT_CARD_INS) != 0);
fRegisters->interrupt_status_enable &= ~(SDHCI_INT_CARD_INS
| SDHCI_INT_CARD_REM);
fRegisters->interrupt_signal_enable &= ~(SDHCI_INT_CARD_INS
| SDHCI_INT_CARD_REM);
if ((intmask & SDHCI_INT_CARD_INS) != 0) {
PowerOn();
fRegisters->interrupt_status_enable |= card_present
? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS;
fRegisters->interrupt_signal_enable |= card_present
? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS;
release_sem_etc(fScanSemaphore, 1, B_DO_NOT_RESCHEDULE);
fRegisters->interrupt_status |= (intmask &
(SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM));
fRegisters->interrupt_status |= SDHCI_INT_CARD_INS;
TRACE("Card presence interrupt handled\n");
}
if ((intmask & SDHCI_INT_CARD_REM) != 0) {
fRegisters->power_control.PowerOff();
fRegisters->interrupt_status |= SDHCI_INT_CARD_REM;
TRACE("Card removal interrupt handled\n");
}
// handling command interrupt
if (intmask & SDHCI_INT_CMD_MASK) {
fCommandResult = intmask;
@ -927,12 +947,22 @@ do_io(void* controller, uint8_t command, IOOperation* operation,
bool offsetAsSectors)
{
CALLED();
SdhciBus* bus = (SdhciBus*)controller;
return bus->DoIO(command, operation, offsetAsSectors);
}
static void
set_scan_semaphore(void* controller, sem_id sem)
{
CALLED();
SdhciBus* bus = (SdhciBus*)controller;
return bus->SetScanSemaphore(sem);
}
module_dependency module_dependencies[] = {
{ MMC_BUS_MODULE_NAME, (module_info**)&gMMCBusController},
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
@ -960,7 +990,8 @@ static mmc_bus_interface gSDHCIPCIDeviceModule = {
set_clock,
execute_command,
do_io
do_io,
set_scan_semaphore
};

View File

@ -453,12 +453,9 @@ mmc_block_io(void* cookie, io_request* request)
static status_t
mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
{
CALLED();
mmc_disk_handle* handle = (mmc_disk_handle*)cookie;
mmc_disk_driver_info* info = handle->info;
TRACE("ioctl(op = %" B_PRId32 ")\n", op);
switch (op) {
case B_GET_MEDIA_STATUS:
{
@ -466,8 +463,6 @@ mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
return B_BAD_VALUE;
*(status_t *)buffer = B_OK;
TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n",
*(status_t *)buffer);
return B_OK;
break;
}