diff --git a/headers/private/drivers/mmc.h b/headers/private/drivers/mmc.h new file mode 100644 index 0000000000..7057318a41 --- /dev/null +++ b/headers/private/drivers/mmc.h @@ -0,0 +1,28 @@ +/* + * Copyright 2019, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Adrien Destugues, pulkomandy@pulkomandy.tk + */ +#ifndef _MMC_H +#define _MMC_H + + +#include + + +#define MMC_BUS_MODULE_NAME "bus_managers/mmc_bus/driver_v1" + + +// Interface between mmc_bus and underlying implementation +typedef struct mmc_bus_interface { + driver_module_info info; + + status_t (*set_clock)(void* controller, uint32_t kilohertz); + status_t (*execute_command)(void* controller, uint8_t command, + uint32_t argument, uint32_t* result); +} mmc_bus_interface; + + +#endif /* _MMC_H */ diff --git a/src/add-ons/kernel/bus_managers/mmc/Jamfile b/src/add-ons/kernel/bus_managers/mmc/Jamfile index fbf3722826..59b8cc1b3e 100644 --- a/src/add-ons/kernel/bus_managers/mmc/Jamfile +++ b/src/add-ons/kernel/bus_managers/mmc/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers mmc ; UsePrivateKernelHeaders ; +UsePrivateHeaders drivers ; KernelAddon mmc_bus : mmc_module.cpp diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp index ffdc09cdb8..fbebd76a13 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.cpp @@ -7,6 +7,10 @@ */ #include "mmc_bus.h" +#include + +#include + MMCBus::MMCBus(device_node* node) : @@ -14,9 +18,11 @@ MMCBus::MMCBus(device_node* node) fController(NULL), fCookie(NULL), fStatus(B_OK), - fDriverCookie(NULL) + fWorkerThread(0) { CALLED(); + + // Get the parent info, it includes the API to send commands to the hardware device_node* parent = gDeviceManager->get_parent_node(node); fStatus = gDeviceManager->get_driver(parent, (driver_module_info**)&fController, &fCookie); @@ -27,12 +33,25 @@ MMCBus::MMCBus(device_node* node) strerror(fStatus)); return; } + + // TODO enumerate the bus (in a separate thread?) + fWorkerThread = spawn_kernel_thread(WorkerThread, "SD bus controller", + B_NORMAL_PRIORITY, this); + resume_thread(fWorkerThread); } MMCBus::~MMCBus() { CALLED(); + + // stop worker thread + fStatus = B_SHUTTING_DOWN; + + status_t result; + if (fWorkerThread != 0) + wait_for_thread(fWorkerThread, &result); + // TODO power off cards, stop clock, etc if needed. } @@ -41,3 +60,98 @@ MMCBus::InitCheck() { return fStatus; } + + +status_t +MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) +{ + return fController->execute_command(fCookie, command, argument, response); +} + + +status_t +MMCBus::WorkerThread(void* cookie) +{ + TRACE("worker thread spawned.\n"); + + MMCBus* bus = (MMCBus*)cookie; + uint32_t response; + + // FIXME wait for bus to signal a card is inserted + // We assume the bus defaults to 400kHz clock and has already powered on + // cards. + + // Reset all cards on the bus + bus->ExecuteCommand(0, 0, NULL); + + // Probe the voltage range + // 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 + // If ACMD41 also does not work, it may be an SDIO card, too + uint32_t probe = (1 << 8) | 0x55; + bus->ExecuteCommand(8, probe, &response); + if (response != probe) { + ERROR("Card does not support voltage range (expected %x, reply %x)\n", + probe, response); + // TODO what now? + } + + // Probe OCR, waiting for card to become ready + uint32_t ocr; + do { + uint32_t cardStatus; + while (bus->ExecuteCommand(55, 0, &cardStatus) + == B_BUSY) { + // FIXME we shouldn't get here if we handle CMD8 failure properly + ERROR("Card locked after CMD8...\n"); + snooze(1000000); + } + if ((cardStatus & 0xFFFF8000) != 0) + ERROR("SD card reports error\n"); + if ((cardStatus & (1 << 5)) == 0) + ERROR("Card did not enter ACMD mode"); + + bus->ExecuteCommand(41, (1 << 30) | 0xFF8000, &ocr); + + if ((ocr & (1 << 31)) == 0) { + TRACE("Card is busy\n"); + snooze(100000); + } + } while (((ocr & (1 << 31)) == 0)); + + if (ocr & (1 << 30)) + TRACE("Card is SDHC"); + if (ocr & (1 << 29)) + TRACE("Card supports UHS-II"); + if (ocr & (1 << 24)) + TRACE("Card supports 1.8v"); + TRACE("Voltage range: %x\n", ocr & 0xFFFFFF); + + // TODO send CMD11 to switch to low voltage mode if card supports it? + + uint32_t cid[4]; + bus->ExecuteCommand(2, 0, cid); + + TRACE("Manufacturer: %02x%c%c\n", cid[3] >> 16, cid[3] >> 8, cid[3]); + TRACE("Name: %c%c%c%c%c\n", cid[2] >> 24, cid[2] >> 16, cid[2] >> 8, + cid[2], cid[1] >> 24); + TRACE("Revision: %d.%d\n", (cid[1] >> 20) & 0xF, (cid[1] >> 16) & 0xF); + TRACE("Serial number: %x\n", (cid[1] << 16) | (cid[0] >> 16)); + TRACE("Date: %d/%d\n", cid[0] & 0xF, 2000 + ((cid[0] >> 4) & 0xFF)); + + bus->ExecuteCommand(3, 0, &response); + + TRACE("RCA: %x Status: %x\n", response >> 16, response & 0xFFFF); + + if ((response & 0xFF00) != 0x5000) + ERROR("Card did not enter data state\n"); + + // The card now has an RCA and it entered the data phase, which means our + // initializing job is over, we can pass it on to the mmc_disk driver. + + // TODO publish child device for the card + // TODO fill it with attributes from the CID + + // TODO iterate CMD2/CMD3 to assign an RCA to all cards (and publish devices + // for each of them) +} diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h index efdc97efec..c49c37f884 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_bus.h @@ -15,7 +15,7 @@ #include #include -#include "../../busses/mmc/sdhci_pci.h" +#include "mmc.h" #define MMCBUS_TRACE @@ -36,18 +36,23 @@ class MMCBus; class MMCBus { public: - MMCBus(device_node *node); - ~MMCBus(); - status_t InitCheck(); + MMCBus(device_node *node); + ~MMCBus(); + status_t InitCheck(); + +private: + status_t ExecuteCommand(uint8_t command, + uint32_t argument, uint32_t* response); + static status_t WorkerThread(void*); private: - device_node* fNode; - sdhci_mmc_bus_interface* fController; - void* fCookie; - status_t fStatus; - void* fDriverCookie; + device_node* fNode; + mmc_bus_interface* fController; + void* fCookie; + status_t fStatus; + thread_id fWorkerThread; }; -#endif /*MMC_BUS_H*/ \ No newline at end of file +#endif /*MMC_BUS_H*/ diff --git a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp index 7ada05ce64..f354d40a9f 100644 --- a/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp +++ b/src/add-ons/kernel/bus_managers/mmc/mmc_module.cpp @@ -8,6 +8,9 @@ #include "mmc_bus.h" +#define MMC_BUS_DEVICE_NAME "bus_managers/mmc_bus/device/v1" + + device_manager_info* gDeviceManager = NULL; @@ -53,23 +56,13 @@ status_t mmc_bus_added_device(device_node* parent) { CALLED(); - uint16 deviceType; - if (gDeviceManager->get_attr_uint16(parent, - SDHCI_DEVICE_TYPE_ITEM, &deviceType, true) != B_OK) { - TRACE("device is missing\n"); - return B_ERROR; - } - - TRACE("MMC bus device added\n"); device_attr attributes[] = { { B_DEVICE_BUS, B_STRING_TYPE, { string: "mmc bus"}}, - { SDHCI_DEVICE_TYPE_ITEM, B_UINT16_TYPE, - { ui16: deviceType }}, { NULL } }; - return gDeviceManager->register_node(parent, MMC_BUS_MODULE_NAME, + return gDeviceManager->register_node(parent, MMC_BUS_DEVICE_NAME, attributes, NULL, NULL); } @@ -93,7 +86,7 @@ std_ops(int32 op, ...) driver_module_info mmc_bus_device_module = { { - MMC_BUS_MODULE_NAME, + MMC_BUS_DEVICE_NAME, 0, std_ops }, @@ -111,7 +104,7 @@ driver_module_info mmc_bus_device_module = { driver_module_info mmc_bus_controller_module = { { - SDHCI_BUS_CONTROLLER_MODULE_NAME, + MMC_BUS_MODULE_NAME, 0, &std_ops }, diff --git a/src/add-ons/kernel/busses/mmc/Jamfile b/src/add-ons/kernel/busses/mmc/Jamfile index ef5298fb2f..5b6f27ba7c 100644 --- a/src/add-ons/kernel/busses/mmc/Jamfile +++ b/src/add-ons/kernel/busses/mmc/Jamfile @@ -2,6 +2,8 @@ SubDir HAIKU_TOP src add-ons kernel busses mmc ; SubDirC++Flags -fno-rtti ; +UsePrivateHeaders drivers ; + KernelAddon sdhci_pci : sdhci_pci.cpp ; diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp index 8a8d494ba9..4e045192d3 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -14,6 +14,7 @@ #include +#include "mmc.h" #include "sdhci_pci.h" @@ -35,75 +36,234 @@ #define SLOT_NUMBER "device/slot" #define BAR_INDEX "device/bar" -typedef struct { - struct registers* fRegisters; - uint8_t fIrq; -} sdhci_pci_mmc_bus_info; + +class SdhciBus { + public: + SdhciBus(struct registers* registers, uint8_t irq); + ~SdhciBus(); + + void EnableInterrupts(uint32_t mask); + status_t ExecuteCommand(uint8_t command, uint32_t argument, + uint32_t* response); + int32 HandleInterrupt(); + status_t InitCheck(); + void Reset(); + void SetClock(int kilohertz); + + private: + void DumpRegisters(uint8_t slot); + bool PowerOn(); + void RecoverError(); + + private: + struct registers* fRegisters; + uint8_t fIrq; + sem_id fSemaphore; + status_t fStatus; +}; device_manager_info* gDeviceManager; -device_module_info* gSDHCIDeviceController; +device_module_info* gMMCBusController; static pci_x86_module_info* sPCIx86Module; -static void -sdhci_register_dump(uint8_t slot, struct registers* regs) +static int32 +sdhci_generic_interrupt(void* data) +{ + SdhciBus* bus = (SdhciBus*)data; + return bus->HandleInterrupt(); +} + + +SdhciBus::SdhciBus(struct registers* registers, uint8_t irq) + : + fRegisters(registers), + fIrq(irq), + fSemaphore(0) +{ + if (irq == 0 || irq == 0xff) { + ERROR("PCI IRQ not assigned\n"); + fStatus = B_BAD_DATA; + return; + } + + fSemaphore = create_sem(0, "SDHCI command"); + + fStatus = install_io_interrupt_handler(fIrq, + sdhci_generic_interrupt, this, 0); + + if (fStatus != B_OK) { + ERROR("can't install interrupt handler\n"); + return; + } + + // First of all, we have to make sure we are in a sane state. The easiest + // way is to reset everything. + Reset(); + + // 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; + } + + // FIXME do we need all these? Wouldn't card insertion/removal and command + // completion be enough? + EnableInterrupts(SDHCI_INT_CMD_CMP + | SDHCI_INT_TRANS_CMP | 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); + +} + + +SdhciBus::~SdhciBus() +{ + if (fSemaphore != 0) + delete_sem(fSemaphore); + + EnableInterrupts(0); + if (fIrq != 0) + remove_io_interrupt_handler(fIrq, sdhci_generic_interrupt, this); + + area_id regs_area = area_for(fRegisters); + delete_area(regs_area); +} + + +void +SdhciBus::DumpRegisters(uint8_t slot) { #ifdef TRACE_SDHCI TRACE("Register values for slot %d:\n", slot); - TRACE("system_address: %d\n", regs->system_address); - TRACE("%d blocks of size %d\n", regs->block_count, regs->block_size); - TRACE("argument: %d\n", regs->argument); - TRACE("transfer_mode: %d\n", regs->transfer_mode); - TRACE("command: %d\n", regs->command.Bits()); + TRACE("system_address: %d\n", fRegisters->system_address); + TRACE("%d blocks of size %d\n", fRegisters->block_count, + fRegisters->block_size); + TRACE("argument: %x\n", fRegisters->argument); + TRACE("transfer_mode: %d\n", fRegisters->transfer_mode); + TRACE("command: %x\n", fRegisters->command.Bits()); TRACE("response:"); - for (int i = 0; i < 8; i++) - dprintf(" %d", regs->response[i]); + for (int i = 0; i < 4; i++) + dprintf(" %d", fRegisters->response[i]); dprintf("\n"); - TRACE("buffer_data_port: %d\n", regs->buffer_data_port); - TRACE("present_state: %x\n", regs->present_state.Bits()); - TRACE("power_control: %d\n", regs->power_control.Bits()); - TRACE("host_control: %d\n", regs->host_control); - TRACE("wakeup_control: %d\n", regs->wakeup_control); - TRACE("block_gap_control: %d\n", regs->block_gap_control); - TRACE("clock_control: %x\n", regs->clock_control.Bits()); - TRACE("software_reset: %d\n", regs->software_reset.Bits()); - TRACE("timeout_control: %d\n", regs->timeout_control); + TRACE("buffer_data_port: %d\n", fRegisters->buffer_data_port); + TRACE("present_state: %x\n", fRegisters->present_state.Bits()); + TRACE("power_control: %d\n", fRegisters->power_control.Bits()); + TRACE("host_control: %d\n", fRegisters->host_control); + TRACE("wakeup_control: %d\n", fRegisters->wakeup_control); + TRACE("block_gap_control: %d\n", fRegisters->block_gap_control); + TRACE("clock_control: %x\n", fRegisters->clock_control.Bits()); + TRACE("software_reset: %d\n", fRegisters->software_reset.Bits()); + TRACE("timeout_control: %d\n", fRegisters->timeout_control); TRACE("interrupt_status: %x enable: %x signal: %x\n", - regs->interrupt_status, regs->interrupt_status_enable, - regs->interrupt_signal_enable); - TRACE("auto_cmd12_error_status: %d\n", regs->auto_cmd12_error_status); - TRACE("capabilities: %lld\n", regs->capabilities.Bits()); + fRegisters->interrupt_status, fRegisters->interrupt_status_enable, + fRegisters->interrupt_signal_enable); + TRACE("auto_cmd12_error_status: %d\n", fRegisters->auto_cmd12_error_status); + TRACE("capabilities: %lld\n", fRegisters->capabilities.Bits()); TRACE("max_current_capabilities: %lld\n", - regs->max_current_capabilities); - TRACE("slot_interrupt_status: %d\n", regs->slot_interrupt_status); + fRegisters->max_current_capabilities); + TRACE("slot_interrupt_status: %d\n", fRegisters->slot_interrupt_status); TRACE("host_controller_version spec %x vendor %x\n", - regs->host_controller_version.specVersion, - regs->host_controller_version.vendorVersion); + fRegisters->host_controller_version.specVersion, + fRegisters->host_controller_version.vendorVersion); #endif } -static void -sdhci_reset(struct registers* regs) +void +SdhciBus::EnableInterrupts(uint32_t mask) { - // if card is not present then no point of reseting the registers - if (!regs->present_state.IsCardInserted()) - return; - - // enabling software reset all - regs->software_reset.ResetAll(); + fRegisters->interrupt_status_enable = mask; + fRegisters->interrupt_signal_enable = mask; } -static void -sdhci_set_clock(struct registers* regs) +status_t +SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) { - int base_clock = regs->capabilities.BaseClockFrequency(); - // Try to get as close to 400kHz as possible, but not faster - int divider = base_clock * 1000 / 400; + TRACE("ExecuteCommand(%d, %x)\n", command, argument); + if (fRegisters->present_state.CommandInhibit()) { + ERROR("Execution aborted, command inhibit\n"); + return B_BUSY; + } - if (regs->host_controller_version.specVersion <= 1) { + fRegisters->argument = argument; + + uint32_t replyType; + + switch(command) { + case 0: + replyType = Command::kNoReplyType; + break; + case 2: + replyType = Command::kR2Type; + break; + case 55: + replyType = Command::kR1Type; + break; + case 41: // ACMD + replyType = Command::kR3Type; + break; + case 3: + replyType = Command::kR6Type; + break; + case 8: + replyType = Command::kR7Type; + break; + default: + ERROR("Unknown command\n"); + return B_BAD_DATA; + } + + fRegisters->command.SendCommand(command, replyType); + acquire_sem(fSemaphore); + + if (replyType == Command::kNoReplyType) { + // No response + } else if (replyType == Command::kR2Type) { + // 128 bit response + response[0] = fRegisters->response[0]; + response[1] = fRegisters->response[1]; + response[2] = fRegisters->response[2]; + response[3] = fRegisters->response[3]; + } else { + // 32 bit response + *response = fRegisters->response[0]; + } + + ERROR("Command execution complete\n"); + return B_OK; +} + + +status_t +SdhciBus::InitCheck() +{ + return fStatus; +} + + +void +SdhciBus::Reset() +{ + fRegisters->software_reset.ResetAll(); +} + + +void +SdhciBus::SetClock(int kilohertz) +{ + int base_clock = fRegisters->capabilities.BaseClockFrequency(); + // Try to get as close to 400kHz as possible, but not faster + int divider = base_clock * 1000 / kilohertz; + + if (fRegisters->host_controller_version.specVersion <= 1) { // Old controller only support power of two dividers up to 256, // round to next power of two up to 256 if (divider > 256) @@ -116,7 +276,7 @@ sdhci_set_clock(struct registers* regs) divider++; } - divider = regs->clock_control.SetDivider(divider); + divider = fRegisters->clock_control.SetDivider(divider); // Log the value after possible rounding by SetDivider (only even values // are allowed). @@ -124,16 +284,16 @@ sdhci_set_clock(struct registers* regs) base_clock * 1000 / divider); // We have set the divider, now we can enable the internal clock. - regs->clock_control.EnableInternal(); + fRegisters->clock_control.EnableInternal(); // wait until internal clock is stabilized - while (!(regs->clock_control.InternalStable())); + while (!(fRegisters->clock_control.InternalStable())); - regs->clock_control.EnablePLL(); - while (!(regs->clock_control.InternalStable())); + fRegisters->clock_control.EnablePLL(); + while (!(fRegisters->clock_control.InternalStable())); // Finally, route the clock to the SD card - regs->clock_control.EnableSD(); + fRegisters->clock_control.EnableSD(); } @@ -144,31 +304,28 @@ sdhci_stop_clock(struct registers* regs) } -static void -sdhci_set_power(struct registers* _regs) +bool +SdhciBus::PowerOn() { - uint8_t supportedVoltages = _regs->capabilities.SupportedVoltages(); + if (!fRegisters->present_state.IsCardInserted()) { + TRACE("Card not inserted, not powering on for now\n"); + return false; + } + + uint8_t supportedVoltages = fRegisters->capabilities.SupportedVoltages(); if ((supportedVoltages & Capabilities::k3v3) != 0) - _regs->power_control.SetVoltage(PowerControl::k3v3); + fRegisters->power_control.SetVoltage(PowerControl::k3v3); else if ((supportedVoltages & Capabilities::k3v0) != 0) - _regs->power_control.SetVoltage(PowerControl::k3v0); + fRegisters->power_control.SetVoltage(PowerControl::k3v0); else if ((supportedVoltages & Capabilities::k1v8) != 0) - _regs->power_control.SetVoltage(PowerControl::k1v8); + fRegisters->power_control.SetVoltage(PowerControl::k1v8); else { - _regs->power_control.PowerOff(); + fRegisters->power_control.PowerOff(); ERROR("No voltage is supported\n"); - return; + return false; } - if (!_regs->present_state.IsCardInserted()) { - TRACE("Card not inserted\n"); - return; - } - - TRACE("Execute CMD0\n"); - _regs->command.SendCommand(0, false); - - DELAY(1000); + return true; } @@ -176,15 +333,8 @@ static status_t init_bus(device_node* node, void** bus_cookie) { CALLED(); - status_t status = B_OK; - area_id regs_area; - volatile uint32_t* regs; - uint8_t bar, slot; - - sdhci_pci_mmc_bus_info* bus = new(std::nothrow) sdhci_pci_mmc_bus_info; - if (bus == NULL) - return B_NO_MEMORY; + // Get the PCI driver and device pci_device_module_info* pci; pci_device* device; @@ -198,9 +348,12 @@ init_bus(device_node* node, void** bus_cookie) if (get_module(B_PCI_X86_MODULE_NAME, (module_info**)&sPCIx86Module) != B_OK) { sPCIx86Module = NULL; - TRACE("PCIx86Module not loaded\n"); + ERROR("PCIx86Module not loaded\n"); + // FIXME try probing FDT as well + return -1; } + uint8_t bar, slot; if (gDeviceManager->get_attr_uint8(node, SLOT_NUMBER, &slot, false) < B_OK || gDeviceManager->get_attr_uint8(node, BAR_INDEX, &bar, false) < B_OK) return -1; @@ -212,6 +365,7 @@ init_bus(device_node* node, void** bus_cookie) int msiCount = sPCIx86Module->get_msi_count(pciInfo.bus, pciInfo.device, pciInfo.function); TRACE("interrupts count: %d\n",msiCount); + // FIXME if available, use MSI rather than good old IRQ... // enable bus master and io uint16 pcicmd = pci->read_pci_config(device, PCI_command, 2); @@ -219,74 +373,49 @@ init_bus(device_node* node, void** bus_cookie) pcicmd |= PCI_command_master | PCI_command_memory; pci->write_pci_config(device, PCI_command, 2, pcicmd); - TRACE("init_bus() %p node %p pci %p device %p\n", bus, node, - pci, device); - - // mapping the registers by MMUIO method - int bar_size = pciInfo->u.h0.base_register_sizes[bar]; - + // map the slot registers + area_id regs_area; + struct registers* _regs; regs_area = map_physical_memory("sdhc_regs_map", pciInfo.u.h0.base_registers[bar], pciInfo.u.h0.base_register_sizes[bar], B_ANY_KERNEL_BLOCK_ADDRESS, - B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)®s); + B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&_regs); if (regs_area < B_OK) { - TRACE("mapping failed"); + ERROR("Could not map registers\n"); return B_BAD_VALUE; } - struct registers* _regs = (struct registers*)regs; - bus->fRegisters = _regs; - sdhci_reset(_regs); - // the interrupt is shared between all busses in an SDHC controller, but // they each register an handler. Not a problem, we will just test the // interrupt registers for all busses one after the other and find no // interrupts on the idle busses. - bus->fIrq = pciInfo.u.h0.interrupt_line; + uint8_t irq = pciInfo.u.h0.interrupt_line; + TRACE("irq interrupt line: %d\n", irq); - TRACE("irq interrupt line: %d\n", bus->fIrq); + SdhciBus* bus = new(std::nothrow) SdhciBus(_regs, irq); - if (bus->fIrq == 0 || bus->fIrq == 0xff) { - TRACE("PCI IRQ not assigned\n"); - if (sPCIx86Module != NULL) { - put_module(B_PCI_X86_MODULE_NAME); - sPCIx86Module = NULL; - } - delete_area(regs_area); - delete bus; - return B_ERROR; - } - - status = install_io_interrupt_handler(bus->fIrq, - sdhci_generic_interrupt, bus, 0); + status_t status = B_NO_MEMORY; + if (bus != NULL) + status = bus->InitCheck(); if (status != B_OK) { - TRACE("can't install interrupt handler\n"); if (sPCIx86Module != NULL) { put_module(B_PCI_X86_MODULE_NAME); sPCIx86Module = NULL; } - delete_area(regs_area); - delete bus; + + if (bus != NULL) + delete bus; + else + delete_area(regs_area); return status; } - TRACE("interrupt handler installed\n"); - - _regs->interrupt_status_enable = SDHCI_INT_CMD_CMP - | SDHCI_INT_TRANS_CMP | 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; - _regs->interrupt_signal_enable = SDHCI_INT_CMD_CMP - | SDHCI_INT_TRANS_CMP | 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; - - sdhci_register_dump(slot, _regs); - sdhci_set_clock(_regs); - sdhci_set_power(_regs); + // Store the created object as a cookie, allowing users of the bus to + // locate it. *bus_cookie = bus; + return status; } @@ -294,41 +423,31 @@ init_bus(device_node* node, void** bus_cookie) static void uninit_bus(void* bus_cookie) { - sdhci_pci_mmc_bus_info* bus = (sdhci_pci_mmc_bus_info*)bus_cookie; - - bus->fRegisters->interrupt_signal_enable = 0; - bus->fRegisters->interrupt_status_enable = 0; - - remove_io_interrupt_handler(bus->fIrq, sdhci_generic_interrupt, bus); - - area_id regs_area = area_for(bus->fRegisters); - delete_area(regs_area); + SdhciBus* bus = (SdhciBus*)bus_cookie; + delete bus; // FIXME do we need to put() the PCI module here? - - delete bus; } void -sdhci_error_interrupt_recovery(struct registers* _regs) +SdhciBus::RecoverError() { - _regs->interrupt_signal_enable &= ~(SDHCI_INT_CMD_CMP + fRegisters->interrupt_signal_enable &= ~(SDHCI_INT_CMD_CMP | SDHCI_INT_TRANS_CMP | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM); - if (_regs->interrupt_status & 7) - _regs->software_reset.ResetTransaction(); + if (fRegisters->interrupt_status & 7) + fRegisters->software_reset.ResetTransaction(); - int16_t erorr_status = _regs->interrupt_status; - _regs->interrupt_status &= ~(erorr_status); + int16_t error_status = fRegisters->interrupt_status; + fRegisters->interrupt_status &= ~(error_status); } int32 -sdhci_generic_interrupt(void* data) +SdhciBus::HandleInterrupt() { - sdhci_pci_mmc_bus_info* bus = (sdhci_pci_mmc_bus_info*)data; - uint32_t intmask = bus->fRegisters->slot_interrupt_status; + uint32_t intmask = fRegisters->slot_interrupt_status; if ((intmask == 0) || (intmask == 0xffffffff)) { return B_UNHANDLED_INTERRUPT; @@ -336,20 +455,23 @@ sdhci_generic_interrupt(void* data) TRACE("interrupt function called\n"); + // FIXME use the global "slot interrupt" register to quickly decide if an + // interrupt is targetted to this slot + // handling card presence interrupt if (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)) { uint32_t card_present = ((intmask & SDHCI_INT_CARD_INS) != 0); - bus->fRegisters->interrupt_status_enable &= ~(SDHCI_INT_CARD_INS + fRegisters->interrupt_status_enable &= ~(SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM); - bus->fRegisters->interrupt_signal_enable &= ~(SDHCI_INT_CARD_INS + fRegisters->interrupt_signal_enable &= ~(SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM); - bus->fRegisters->interrupt_status_enable |= card_present + fRegisters->interrupt_status_enable |= card_present ? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS; - bus->fRegisters->interrupt_signal_enable |= card_present + fRegisters->interrupt_signal_enable |= card_present ? SDHCI_INT_CARD_REM : SDHCI_INT_CARD_INS; - bus->fRegisters->interrupt_status |= (intmask & + fRegisters->interrupt_status |= (intmask & (SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM)); TRACE("Card presence interrupt handled\n"); @@ -358,8 +480,9 @@ sdhci_generic_interrupt(void* data) // handling command interrupt if (intmask & SDHCI_INT_CMD_MASK) { - bus->fRegisters->interrupt_status |= (intmask & SDHCI_INT_CMD_MASK); - // TODO do something with the interrupt + fRegisters->interrupt_status |= (intmask & SDHCI_INT_CMD_MASK); + // Notify the thread + release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE); TRACE("Command interrupt handled\n"); return B_HANDLED_INTERRUPT; @@ -367,17 +490,15 @@ sdhci_generic_interrupt(void* data) // handling bus power interrupt if (intmask & SDHCI_INT_BUS_POWER) { - bus->fRegisters->interrupt_status |= SDHCI_INT_BUS_POWER; + fRegisters->interrupt_status |= SDHCI_INT_BUS_POWER; TRACE("card is consuming too much power\n"); return B_HANDLED_INTERRUPT; } - intmask = bus->fRegisters->slot_interrupt_status; + intmask = fRegisters->slot_interrupt_status; if (intmask != 0) { ERROR("Remaining interrupts at end of handler: %x\n", intmask); - intmask &= ~(SDHCI_INT_BUS_POWER | SDHCI_INT_CARD_INS - | SDHCI_INT_CARD_REM | SDHCI_INT_CMD_MASK); } return B_UNHANDLED_INTERRUPT; @@ -403,7 +524,6 @@ register_child_devices(void* cookie) gDeviceManager->get_driver(parent, (driver_module_info**)&pci, (void**)&device); - uint16 pciSubDeviceId = pci->read_pci_config(device, PCI_subsystem_id, 2); slotsInfo = pci->read_pci_config(device, SDHCI_PCI_SLOT_INFO, 1); bar = SDHCI_PCI_SLOT_INFO_FIRST_BASE_INDEX(slotsInfo); slots_count = SDHCI_PCI_SLOTS(slotsInfo); @@ -411,28 +531,22 @@ register_child_devices(void* cookie) char prettyName[25]; if (slots_count > 6 || bar > 5) { - TRACE("Invalid slots count: %d or BAR count: %d \n", slots_count, bar); + ERROR("Invalid slots count: %d or BAR count: %d \n", slots_count, bar); return B_BAD_VALUE; } for (uint8_t slot = 0; slot <= slots_count; slot++) { bar = bar + slot; - sprintf(prettyName, "SDHC bus %" B_PRIu16 " slot %" - B_PRIu8, pciSubDeviceId, slot); + sprintf(prettyName, "SDHC bus %" B_PRIu8, slot); device_attr attrs[] = { // properties of this controller for SDHCI bus manager - { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, - { string: prettyName }}, + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: prettyName } }, { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, - {string: SDHCI_BUS_CONTROLLER_MODULE_NAME}}, - {SDHCI_DEVICE_TYPE_ITEM, B_UINT16_TYPE, - { ui16: pciSubDeviceId}}, - {B_DEVICE_BUS, B_STRING_TYPE,{string: "mmc"}}, - {SLOT_NUMBER, B_UINT8_TYPE, - { ui8: slot}}, - {BAR_INDEX, B_UINT8_TYPE, - { ui8: bar}}, + {string: MMC_BUS_MODULE_NAME} }, + { B_DEVICE_BUS, B_STRING_TYPE, {string: "mmc"} }, + { SLOT_NUMBER, B_UINT8_TYPE, { ui8: slot} }, + { BAR_INDEX, B_UINT8_TYPE, { ui8: bar} }, { NULL } }; if (gDeviceManager->register_node(node, SDHCI_PCI_MMC_BUS_MODULE_NAME, @@ -477,11 +591,14 @@ supports_device(device_node* parent) if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK || gDeviceManager->get_attr_uint16(parent, B_DEVICE_SUB_TYPE, &subType, false) < B_OK || gDeviceManager->get_attr_uint16(parent, - B_DEVICE_TYPE, &type, false) < B_OK) + B_DEVICE_TYPE, &type, false) < B_OK) { + ERROR("Could not find required attribute device/bus\n"); return -1; + } if (strcmp(bus, "pci") != 0) return 0.0f; + } if (type == PCI_base_peripheral) { if (subType != PCI_sd_host) @@ -501,14 +618,34 @@ supports_device(device_node* parent) } +static status_t +set_clock(void* controller, uint32_t kilohertz) +{ + SdhciBus* bus = (SdhciBus*)controller; + bus->SetClock(kilohertz); + return B_OK; +} + + +static status_t +execute_command(void* controller, uint8_t command, uint32_t argument, + uint32_t* response) +{ + SdhciBus* bus = (SdhciBus*)controller; + return bus->ExecuteCommand(command, argument, response); +} + + module_dependency module_dependencies[] = { - { SDHCI_BUS_CONTROLLER_MODULE_NAME, (module_info**)&gSDHCIDeviceController}, + { MMC_BUS_MODULE_NAME, (module_info**)&gMMCBusController}, { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, {} }; -static sdhci_mmc_bus_interface gSDHCIPCIDeviceModule = { +// Device node registered for each SD slot. It implements the MMC operations so +// the bus manager can use it to communicate with SD cards. +static mmc_bus_interface gSDHCIPCIDeviceModule = { { { SDHCI_PCI_MMC_BUS_MODULE_NAME, @@ -522,10 +659,15 @@ static sdhci_mmc_bus_interface gSDHCIPCIDeviceModule = { NULL, // register child devices NULL, // rescan bus_removed, - } + }, + + set_clock, + execute_command, }; +// Root device that binds to the PCI bus. It will register an mmc_bus_interface +// node for each SD slot in the device. static driver_module_info sSDHCIDevice = { { SDHCI_PCI_DEVICE_MODULE_NAME, diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.h b/src/add-ons/kernel/busses/mmc/sdhci_pci.h index 6035a1e2fa..7831c0c6da 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.h +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.h @@ -25,11 +25,18 @@ class Command { public: uint16_t Bits() { return fBits; } - void SendCommand(uint8_t command, bool data) + void SendCommand(uint8_t command, uint8_t type) { - fBits = (command << 8) | (data << 5); + fBits = (command << 8) | type; } + static const uint8_t kNoReplyType = 0; + static const uint8_t kR1Type = 0x1C; + static const uint8_t kR2Type = 0x09; + static const uint8_t kR3Type = 0x02; + static const uint8_t kR6Type = 0x1C; + static const uint8_t kR7Type = 0x3C; + private: volatile uint16_t fBits; } __attribute__((packed)); @@ -43,6 +50,7 @@ class PresentState { uint32_t Bits() { return fBits; } bool IsCardInserted() { return fBits & (1 << 16); } + bool CommandInhibit() { return fBits & (1 << 0); } private: volatile uint32_t fBits; @@ -166,7 +174,7 @@ struct registers { Command command; // Response - volatile uint16_t response[8]; + volatile uint32_t response[4]; // Buffer Data Port volatile uint32_t buffer_data_port; @@ -249,25 +257,6 @@ struct registers { typedef void* sdhci_mmc_bus; -#define DELAY(n) snooze(n) - -#define SDHCI_BUS_CONTROLLER_MODULE_NAME "bus_managers/mmc_bus/driver_v1" - -#define MMC_BUS_MODULE_NAME "bus_managers/mmc_bus/device/v1" - -static void sdhci_register_dump(uint8_t, struct registers*); -static void sdhci_reset(struct registers*); -static void sdhci_set_clock(struct registers*); -static void sdhci_set_power(struct registers*); -static void sdhci_stop_clock(struct registers*); -void sdhci_error_interrupt_recovery(struct registers*); - -status_t sdhci_generic_interrupt(void*); - -typedef struct { - driver_module_info info; - -} sdhci_mmc_bus_interface; #endif /*_SDHCI_PCI_H*/