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 <[email protected]>
This commit is contained in:
committed by
Adrien Destugues
parent
b18298348a
commit
2413679304
@@ -78,6 +78,8 @@ typedef struct mmc_bus_interface {
|
|||||||
status_t (*do_io)(void* controller, uint8_t command,
|
status_t (*do_io)(void* controller, uint8_t command,
|
||||||
IOOperation* operation, bool offsetAsSectors);
|
IOOperation* operation, bool offsetAsSectors);
|
||||||
// Execute a command that involves a data transfer.
|
// 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;
|
} mmc_bus_interface;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ MMCBus::MMCBus(device_node* node)
|
|||||||
fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller",
|
fWorkerThread = spawn_kernel_thread(_WorkerThread, "SD bus controller",
|
||||||
B_NORMAL_PRIORITY, this);
|
B_NORMAL_PRIORITY, this);
|
||||||
resume_thread(fWorkerThread);
|
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
|
status_t
|
||||||
MMCBus::_WorkerThread(void* cookie)
|
MMCBus::_WorkerThread(void* cookie)
|
||||||
{
|
{
|
||||||
@@ -127,16 +137,22 @@ MMCBus::_WorkerThread(void* cookie)
|
|||||||
// cards.
|
// cards.
|
||||||
|
|
||||||
// Reset all cards on the bus
|
// 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) {
|
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");
|
TRACE("Scanning the bus\n");
|
||||||
|
|
||||||
// Probe the voltage range
|
// Probe the voltage range
|
||||||
@@ -162,10 +178,12 @@ MMCBus::_WorkerThread(void* cookie)
|
|||||||
} else if (response != probe) {
|
} else if (response != probe) {
|
||||||
ERROR("Card does not support voltage range (expected %x, "
|
ERROR("Card does not support voltage range (expected %x, "
|
||||||
"reply %x)\n", probe, response);
|
"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
|
// Probe OCR, waiting for card to become ready
|
||||||
|
// We keep repeating ACMD41 until the card replies that it is
|
||||||
|
// initialized.
|
||||||
uint32_t ocr;
|
uint32_t ocr;
|
||||||
do {
|
do {
|
||||||
uint32_t cardStatus;
|
uint32_t cardStatus;
|
||||||
@@ -259,6 +277,10 @@ MMCBus::_WorkerThread(void* cookie)
|
|||||||
// FIXME we also need to unpublish devices that are gone. Probably need
|
// 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 "ping" all RCAs somehow? Or is there an interrupt we can look for
|
||||||
// to detect added/removed cards?
|
// 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);
|
release_sem(bus->fLockSemaphore);
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public:
|
|||||||
void ReleaseBus() { release_sem(fLockSemaphore); }
|
void ReleaseBus() { release_sem(fLockSemaphore); }
|
||||||
private:
|
private:
|
||||||
status_t _ActivateDevice(uint16_t rca);
|
status_t _ActivateDevice(uint16_t rca);
|
||||||
|
void _AcquireScanSemaphore();
|
||||||
static status_t _WorkerThread(void*);
|
static status_t _WorkerThread(void*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ mmc_bus_uninit(void* _device)
|
|||||||
static status_t
|
static status_t
|
||||||
mmc_bus_register_child(void* _device)
|
mmc_bus_register_child(void* _device)
|
||||||
{
|
{
|
||||||
CALLED();
|
// Nothing to do, child devices are registered by the scanning thread
|
||||||
MMCBus* device = (MMCBus*)_device;
|
|
||||||
device->Rescan();
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class SdhciBus {
|
|||||||
~SdhciBus();
|
~SdhciBus();
|
||||||
|
|
||||||
void EnableInterrupts(uint32_t mask);
|
void EnableInterrupts(uint32_t mask);
|
||||||
|
void DisableInterrupts();
|
||||||
status_t ExecuteCommand(uint8_t command, uint32_t argument,
|
status_t ExecuteCommand(uint8_t command, uint32_t argument,
|
||||||
uint32_t* response);
|
uint32_t* response);
|
||||||
int32 HandleInterrupt();
|
int32 HandleInterrupt();
|
||||||
@@ -54,6 +55,7 @@ class SdhciBus {
|
|||||||
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);
|
bool offsetAsSectors);
|
||||||
|
void SetScanSemaphore(sem_id sem);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool PowerOn();
|
bool PowerOn();
|
||||||
@@ -64,6 +66,7 @@ class SdhciBus {
|
|||||||
uint32_t fCommandResult;
|
uint32_t fCommandResult;
|
||||||
uint8_t fIrq;
|
uint8_t fIrq;
|
||||||
sem_id fSemaphore;
|
sem_id fSemaphore;
|
||||||
|
sem_id fScanSemaphore;
|
||||||
status_t fStatus;
|
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
|
// Then we configure the clock to the frequency needed for initialization
|
||||||
SetClock(400);
|
SetClock(400);
|
||||||
|
|
||||||
// And we turn on the power supply to the card
|
// Turn on the power supply to the card, if there is a card inserted
|
||||||
// FIXME maybe this should only be done when a card is inserted?
|
PowerOn();
|
||||||
if (!PowerOn()) {
|
|
||||||
ERROR("Failed to power on the card\n");
|
|
||||||
fStatus = B_NO_INIT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnableInterrupts(SDHCI_INT_CMD_CMP | SDHCI_INT_CARD_INS
|
// Finally, configure some useful interrupts
|
||||||
| SDHCI_INT_CARD_REM | SDHCI_INT_TRANS_CMP);
|
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
|
// 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
|
// 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()
|
SdhciBus::~SdhciBus()
|
||||||
{
|
{
|
||||||
EnableInterrupts(0);
|
DisableInterrupts();
|
||||||
|
|
||||||
if (fSemaphore != 0)
|
if (fSemaphore != 0)
|
||||||
delete_sem(fSemaphore);
|
delete_sem(fSemaphore);
|
||||||
@@ -155,8 +154,16 @@ SdhciBus::~SdhciBus()
|
|||||||
void
|
void
|
||||||
SdhciBus::EnableInterrupts(uint32_t mask)
|
SdhciBus::EnableInterrupts(uint32_t mask)
|
||||||
{
|
{
|
||||||
fRegisters->interrupt_status_enable = mask;
|
fRegisters->interrupt_status_enable |= mask;
|
||||||
fRegisters->interrupt_signal_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
|
bool
|
||||||
SdhciBus::PowerOn()
|
SdhciBus::PowerOn()
|
||||||
{
|
{
|
||||||
@@ -532,23 +554,21 @@ SdhciBus::HandleInterrupt()
|
|||||||
TRACE("interrupt function called %x\n", intmask);
|
TRACE("interrupt function called %x\n", intmask);
|
||||||
|
|
||||||
// handling card presence interrupt
|
// handling card presence interrupt
|
||||||
if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) {
|
if ((intmask & SDHCI_INT_CARD_INS) != 0) {
|
||||||
uint32_t card_present = ((intmask & SDHCI_INT_CARD_INS) != 0);
|
PowerOn();
|
||||||
fRegisters->interrupt_status_enable &= ~(SDHCI_INT_CARD_INS
|
|
||||||
| SDHCI_INT_CARD_REM);
|
|
||||||
fRegisters->interrupt_signal_enable &= ~(SDHCI_INT_CARD_INS
|
|
||||||
| SDHCI_INT_CARD_REM);
|
|
||||||
|
|
||||||
fRegisters->interrupt_status_enable |= card_present
|
release_sem_etc(fScanSemaphore, 1, B_DO_NOT_RESCHEDULE);
|
||||||
? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS;
|
|
||||||
fRegisters->interrupt_signal_enable |= card_present
|
|
||||||
? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS;
|
|
||||||
|
|
||||||
fRegisters->interrupt_status |= (intmask &
|
fRegisters->interrupt_status |= SDHCI_INT_CARD_INS;
|
||||||
(SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM));
|
|
||||||
TRACE("Card presence interrupt handled\n");
|
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
|
// handling command interrupt
|
||||||
if (intmask & SDHCI_INT_CMD_MASK) {
|
if (intmask & SDHCI_INT_CMD_MASK) {
|
||||||
fCommandResult = intmask;
|
fCommandResult = intmask;
|
||||||
@@ -933,6 +953,16 @@ do_io(void* controller, uint8_t command, IOOperation* operation,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_scan_semaphore(void* controller, sem_id sem)
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
|
||||||
|
SdhciBus* bus = (SdhciBus*)controller;
|
||||||
|
return bus->SetScanSemaphore(sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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 },
|
||||||
@@ -960,7 +990,8 @@ static mmc_bus_interface gSDHCIPCIDeviceModule = {
|
|||||||
|
|
||||||
set_clock,
|
set_clock,
|
||||||
execute_command,
|
execute_command,
|
||||||
do_io
|
do_io,
|
||||||
|
set_scan_semaphore
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -453,12 +453,9 @@ mmc_block_io(void* cookie, io_request* request)
|
|||||||
static status_t
|
static status_t
|
||||||
mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
|
mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
CALLED();
|
|
||||||
mmc_disk_handle* handle = (mmc_disk_handle*)cookie;
|
mmc_disk_handle* handle = (mmc_disk_handle*)cookie;
|
||||||
mmc_disk_driver_info* info = handle->info;
|
mmc_disk_driver_info* info = handle->info;
|
||||||
|
|
||||||
TRACE("ioctl(op = %" B_PRId32 ")\n", op);
|
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case B_GET_MEDIA_STATUS:
|
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;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
*(status_t *)buffer = B_OK;
|
*(status_t *)buffer = B_OK;
|
||||||
TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n",
|
|
||||||
*(status_t *)buffer);
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user