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:
parent
b18298348a
commit
2413679304
@ -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;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user