sdhci and mmc implementation

sdhci:
- Add semaphore for interrupt management
- Add basic operations (setting clock, executing a command)
- Add early initialization (clocks and power up)
- Wrap the bus in a C++ class to ease usage
- Expose API to MMC bus manager
- TODO: manage card insertion and removal interrupts
- TODO: use MSI when available

mmc_bus:
- Implements SD card management independant of the way we access the bus
  (later on different drivers can provide the same API as SDHCI)
- Worker thread to do the initialization
- Implement card initialization process up until getting an RCA from the
  card. This is the generic part to assign an ID to the card, after this
  point commands can be targetted at the specific card so it can be
  handed over to the mmc_disk driver.
- TODO: initialization for non-SDHC cards which do not reply to CMD8.

Change-Id: I71950ca3ce206378a68fa7f97c19f638183d6cdd
Reviewed-on: https://review.haiku-os.org/c/1032
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Adrien Destugues
2019-02-19 18:33:25 +00:00
committed by waddlesplash
parent 1f93ed9936
commit ff76d2df8e
8 changed files with 490 additions and 216 deletions
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright 2019, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Adrien Destugues, [email protected]
*/
#ifndef _MMC_H
#define _MMC_H
#include <device_manager.h>
#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 */
@@ -1,6 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel bus_managers mmc ;
UsePrivateKernelHeaders ;
UsePrivateHeaders drivers ;
KernelAddon mmc_bus :
mmc_module.cpp
+115 -1
View File
@@ -7,6 +7,10 @@
*/
#include "mmc_bus.h"
#include <Errors.h>
#include <stdint.h>
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)
}
+15 -10
View File
@@ -15,7 +15,7 @@
#include <lock.h>
#include <util/AutoLock.h>
#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*/
#endif /*MMC_BUS_H*/
@@ -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
},
+2
View File
@@ -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
;
+312 -170
View File
@@ -14,6 +14,7 @@
#include <KernelExport.h>
#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**)&regs);
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,
+11 -22
View File
@@ -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*/