* Copyright 2018-2025 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* B Krishnan Iyer, krishnaniyer97@gmail.com
* Adrien Destugues, pulkomandy@pulkomandy.tk
* Ron Ben Aroya, sed4906birdie@gmail.com
*/
#ifndef _SDHCI_H
#define _SDHCI_H
#include <condition_variable.h>
#include <device_manager.h>
#include <KernelExport.h>
class SdhciBus {
public:
SdhciBus(struct registers* registers, uint8_t irq, bool poll);
~SdhciBus();
void EnableInterrupts(uint32_t mask);
void DisableInterrupts();
status_t ExecuteCommand(uint8_t command, uint32_t argument,
uint32_t* response);
int32 HandleInterrupt();
status_t InitCheck();
void Reset();
void SetClock(int kilohertz, bool allowAuto);
status_t DoIO(uint8_t command, IOOperation* operation,
bool offsetAsSectors);
void SetScanSemaphore(sem_id sem);
void SetBusWidth(int width);
private:
bool PowerOn();
void RecoverError();
static status_t _WorkerThread(void*);
private:
struct registers* fRegisters;
uint32 fCommandResult;
uint8 fIrq;
ConditionVariable fInterruptNotifier;
sem_id fScanSemaphore;
status_t fStatus;
thread_id fWorkerThread;
};
class SdhciDevice {
public:
device_node* fNode;
uint8_t fRicohOriginalMode;
};
class TransferMode {
public:
uint16_t Bits() { return fBits; }
static const uint8_t kR1 = 0 << 6;
static const uint8_t kR5 = 1 << 6;
static const uint8_t kMulti = 1 << 5;
static const uint8_t kSingle = 0 << 5;
static const uint8_t kRead = 1 << 4;
static const uint8_t kWrite = 0 << 4;
static const uint8_t kAutoCmdDisabled = 0 << 2;
static const uint8_t kAutoCmd12Enable = 1 << 2;
static const uint8_t kAutoCmd23Enable = 2 << 2;
static const uint8_t kAutoCmdAutoSelect
= kAutoCmd23Enable | kAutoCmd12Enable;
static const uint8_t kBlockCountEnable = 1 << 1;
static const uint8_t kDmaEnable = 1;
static const uint8_t kNoDmaOrNoData = 0;
private:
volatile uint16_t fBits;
} __attribute__((packed));
class Command {
public:
uint16_t Bits() { return fBits; }
void SendCommand(uint8_t command, uint8_t type)
{
fBits = (command << 8) | type;
}
static const uint8_t kDataPresent = 0x20;
static const uint8_t kCheckIndex = 0x10;
static const uint8_t kCRCEnable = 0x8;
static const uint8_t kSubCommand = 0x4;
static const uint8_t kReplySizeMask = 0x3;
static const uint8_t k32BitResponse = 0x2;
static const uint8_t k128BitResponse = 0x1;
static const uint8_t k32BitResponseCheckBusy = 0x3;
static const uint8_t kNoReplyType = 0;
static const uint8_t kR1Type = kCheckIndex | kCRCEnable
| k32BitResponse;
static const uint8_t kR1bType = (kCheckIndex | kCRCEnable
| k32BitResponseCheckBusy) & (~ kDataPresent);
static const uint8_t kR2Type = kCRCEnable | k128BitResponse;
static const uint8_t kR3Type = k32BitResponse;
static const uint8_t kR6Type = kCheckIndex | k32BitResponse;
static const uint8_t kR7Type = kCheckIndex | kCRCEnable
| k32BitResponse;
private:
volatile uint16_t fBits;
} __attribute__((packed));
class PresentState {
public:
uint32_t Bits() { return fBits; }
bool IsCardInserted() { return fBits & (1 << 16); }
bool CommandInhibit() { return fBits & (1 << 0); }
bool DataInhibit() { return fBits & (1 << 1); }
private:
volatile uint32_t fBits;
} __attribute__((packed));
class PowerControl {
public:
uint8_t Bits() { return fBits; }
void SetVoltage(int voltage) {
fBits |= voltage | kBusPowerOn;
}
void PowerOff() { fBits &= ~kBusPowerOn; }
static const uint8_t k3v3 = 7 << 1;
static const uint8_t k3v0 = 6 << 1;
static const uint8_t k1v8 = 5 << 1;
private:
volatile uint8_t fBits;
static const uint8_t kBusPowerOn = 1;
} __attribute__((packed));
class ClockControl
{
public:
uint16_t Bits() { return fBits; }
uint16_t SetDivider(uint16_t divider) {
if (divider == 1)
divider = 0;
else
divider /= 2;
uint16_t bits = fBits & ~0xffc0;
bits |= divider << 8;
bits |= (divider >> 8) & 0xc0;
fBits = bits;
return divider == 0 ? 1 : divider * 2;
}
void EnableInternal() { fBits |= 1 << 0; }
bool InternalStable() { return fBits & (1 << 1); }
void EnableSD() { fBits |= 1 << 2; }
void DisableSD() { fBits &= ~(1 << 2); }
void EnablePLL() { fBits |= 1 << 3; }
private:
volatile uint16_t fBits;
} __attribute__((packed));
class TimeoutControl {
public:
void SetDivider(uint32_t base_khz, uint32_t delay_ms) {
uint32_t i;
for (i = 13; i < 27; i++) {
if ((base_khz * delay_ms) <= (1u << i))
break;
}
fBits = i - 13;
}
private:
volatile uint8_t fBits;
} __attribute__((packed));
class SoftwareReset {
public:
uint8_t Bits() { return fBits; }
bool ResetAll() {
fBits = 1;
int i = 0;
while ((fBits & 1) != 0 && i++ < 10)
snooze(10000);
return i < 10;
}
void ResetCommandLine() {
fBits |= 2;
while(fBits & 2);
}
private:
volatile uint8_t fBits;
} __attribute__((packed));
#define SDHCI_INT_CMD_CMP 0x00000001 // command complete enable
#define SDHCI_INT_TRANS_CMP 0x00000002 // transfer complete enable
#define SDHCI_INT_BLOCK_GAP 0x00000004
#define SDHCI_INT_DMA 0x00000008
#define SDHCI_INT_BUF_WRITE_READY 0x00000010
#define SDHCI_INT_BUF_READ_READY 0x00000020 // buffer read ready enable
#define SDHCI_INT_CARD_INS 0x00000040 // card insertion enable
#define SDHCI_INT_CARD_REM 0x00000080 // card removal enable
#define SDHCI_INT_CARD_STATUS 0x00000100
#define SDHCI_INT_A 0x00000200
#define SDHCI_INT_B 0x00000400
#define SDHCI_INT_C 0x00000800
#define SDHCI_INT_RETUNING 0x00001000
#define SDHCI_INT_ERROR 0x00008000 // error
#define SDHCI_INT_COMMAND_TIMEOUT 0x00010000 // Timeout error
#define SDHCI_INT_COMMAND_CRC 0x00020000 // CRC error
#define SDHCI_INT_COMMAND_END_BIT 0x00040000 // end bit error
#define SDHCI_INT_COMMAND_INDEX 0x00080000 // index error
#define SDHCI_INT_DATA_TIMEOUT 0x00100000
#define SDHCI_INT_DATA_CRC 0x00200000
#define SDHCI_INT_DATA_END 0x00400000
#define SDHCI_INT_BUS_POWER 0x00800000 // power fail
#define SDHCI_INT_AUTO_CMD_ERROR 0x01000000
#define SDHCI_INT_ADMA_ERROR 0x02000000
#define SDHCI_INT_TUNING_ERROR 0x04000000
#define SDHCI_INT_VENDOR_ERRORS 0xF0000000
#define SDHCI_INT_CMD_ERROR_MASK (SDHCI_INT_COMMAND_TIMEOUT \
| SDHCI_INT_COMMAND_CRC | SDHCI_INT_COMMAND_END_BIT | SDHCI_INT_COMMAND_INDEX)
#define SDHCI_INT_CMD_MASK (SDHCI_INT_CMD_CMP | SDHCI_INT_CMD_ERROR_MASK)
#define SDHCI_INT_ERROR_MASK (SDHCI_INT_VENDOR_ERRORS | SDHCI_INT_TUNING_ERROR \
| SDHCI_INT_ADMA_ERROR | SDHCI_INT_AUTO_CMD_ERROR | SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END \
| SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_CMD_ERROR_MASK)
#define SDHCI_INT_NORMAL_MASK (SDHCI_INT_CMD_CMP | SDHCI_INT_TRANS_CMP \
| SDHCI_INT_BLOCK_GAP | SDHCI_INT_DMA | SDHCI_INT_BUF_WRITE_READY | SDHCI_INT_BUF_READ_READY \
| SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM | SDHCI_INT_CARD_STATUS | SDHCI_INT_A | SDHCI_INT_B \
| SDHCI_INT_C | SDHCI_INT_RETUNING)
class Capabilities
{
public:
uint8_t ClockMultiplier() { return (fBits >> 48) & 0xFF; }
uint8_t RetuningModes() { return (fBits >> 46) & 3; }
bool UseTuningForSDR50() { return (fBits >> 45) & 1; }
uint8_t RetuningTimerCount() { return (fBits >> 40) & 7; }
bool TypeDSupport() { return (fBits >> 38) & 1; }
bool TypeCSupport() { return (fBits >> 37) & 1; }
bool TypeASupport() { return (fBits >> 36) & 1; }
bool DDR50Support() { return (fBits >> 34) & 1; }
bool SDR104Support() { return (fBits >> 33) & 1; }
bool SDR50Support() { return (fBits >> 32) & 1; }
uint8_t SlotType() { return (fBits >> 30) & 3; }
bool AsynchronousInterrupts() { return (fBits >> 29) & 1; }
bool SystemBus64Bits() { return (fBits >> 28) & 1; }
uint8_t SupportedVoltages() { return (fBits >> 24) & 7; }
bool SuspendResume() { return (fBits >> 23) & 1; }
bool SimpleDMA() { return (fBits >> 22) & 1; }
bool HighSpeed() { return (fBits >> 21) & 1; }
bool AdvancedDMA() { return (fBits >> 19) & 1; }
bool Embedded8Bit() { return (fBits >> 18) & 1; }
uint8_t MaxBlockLength() { return (fBits >> 16) & 3; }
uint8_t BaseClockFrequency() { return (fBits >> 8) & 0xFF; }
uint32_t TimeoutClockFrequency()
{
bool megahertz = (fBits >> 7) & 1;
uint32_t frequency = (fBits >> 0) & 0x1F;
if (megahertz)
frequency *= 1000;
return frequency;
}
static const uint8_t k3v3 = 1;
static const uint8_t k3v0 = 2;
static const uint8_t k1v8 = 4;
private:
uint64_t fBits;
} __attribute__((packed));
class HostControllerVersion {
public:
const uint8_t specVersion;
const uint8_t vendorVersion;
} __attribute__((packed));
class HostControl {
public:
void SetDMAMode(uint8_t dmaMode)
{
value = (value & ~kDmaMask) | dmaMode;
}
void SetDataTransferWidth(uint8_t width)
{
value = (value & ~kDataTransferWidthMask) | width;
}
uint8 Bits() { return value; }
static const uint8_t kDmaMask = 3 << 3;
static const uint8_t kSdma = 0 << 3;
static const uint8_t kAdma32 = 2 << 3;
static const uint8_t kAdma64 = 3 << 3;
static const uint8_t kDataTransfer1Bit = 0;
static const uint8_t kDataTransfer4Bit = 1 << 1;
static const uint8_t kDataTransfer8Bit = 1 << 5;
static const uint8_t kDataTransferWidthMask
= kDataTransfer4Bit | kDataTransfer8Bit;
private:
volatile uint8_t value;
} __attribute__((packed));
class BlockSize {
public:
void ConfigureTransfer(uint16_t transferBlockSize,
uint16_t dmaBoundary)
{
value = transferBlockSize | dmaBoundary << 12;
}
static const uint16_t kDmaBoundary4K = 0;
static const uint16_t kDmaBoundary8K = 1;
static const uint16_t kDmaBoundary16K = 2;
static const uint16_t kDmaBoundary32K = 3;
static const uint16_t kDmaBoundary64K = 4;
static const uint16_t kDmaBoundary128K = 5;
static const uint16_t kDmaBoundary256K = 6;
static const uint16_t kDmaBoundary512K = 7;
private:
volatile uint16_t value;
} __attribute__((packed));
struct registers {
volatile uint32_t system_address;
BlockSize block_size;
volatile uint16_t block_count;
volatile uint32_t argument;
volatile uint16_t transfer_mode;
Command command;
volatile uint32_t response[4];
volatile uint32_t buffer_data_port;
PresentState present_state;
HostControl host_control;
PowerControl power_control;
volatile uint8_t block_gap_control;
volatile uint8_t wakeup_control;
ClockControl clock_control;
TimeoutControl timeout_control;
SoftwareReset software_reset;
volatile uint32_t interrupt_status;
volatile uint32_t interrupt_status_enable;
volatile uint32_t interrupt_signal_enable;
volatile uint16_t auto_cmd12_error_status;
volatile uint16_t host_control_2;
Capabilities capabilities;
volatile uint64_t max_current_capabilities;
volatile uint16_t force_event_acmd_status;
volatile uint16_t force_event_error_status;
volatile uint8_t adma_error_status;
volatile uint8_t padding[3];
volatile uint64_t adma_system_address;
volatile uint64_t preset_value[2];
volatile uint32_t padding1 :32;
volatile uint16_t uhs2_preset_value;
volatile uint16_t padding2 :16;
volatile uint64_t adma3_id_address;
volatile uint16_t uhs2_block_size;
volatile uint16_t padding3 :16;
volatile uint32_t uhs2_block_count;
volatile uint8_t uhs2_command_packet[20];
volatile uint16_t uhs2_transfer_mode;
volatile uint16_t uhs2_command;
volatile uint8_t uhs2_response[20];
volatile uint8_t uhs2_msg_select;
volatile uint8_t padding4[3];
volatile uint32_t uhs2_msg;
volatile uint16_t uhs2_device_interrupt_status;
volatile uint8_t uhs2_device_select;
volatile uint8_t uhs2_device_int_code;
volatile uint16_t uhs2_software_reset;
volatile uint16_t uhs2_timer_control;
volatile uint32_t uhs2_error_interrupt_status;
volatile uint32_t uhs2_error_interrupt_status_enable;
volatile uint32_t uhs2_error_interrupt_signal_enable;
volatile uint8_t padding5[16];
volatile uint16_t uhs2_settings_pointer;
volatile uint16_t uhs2_host_capabilities_pointer;
volatile uint16_t uhs2_test_pointer;
volatile uint16_t embedded_control_pointer;
volatile uint16_t vendor_specific_pointer;
volatile uint16_t reserved_specific_pointer;
volatile uint8_t padding6[16];
volatile uint16_t slot_interrupt_status;
HostControllerVersion host_controller_version;
} __attribute__((packed));
typedef void* sdhci_mmc_bus;
struct sdhci_crs {
uint8 irq;
uint32 addr_bas;
uint32 addr_len;
};
extern float supports_device_acpi(device_node* parent);
extern float supports_device_pci(device_node* parent);
extern status_t register_child_devices_acpi(void* cookie);
extern status_t register_child_devices_pci(void* cookie);
extern status_t init_device_pci(device_node* node, SdhciDevice* context);
extern void uninit_device_pci(SdhciDevice* context, device_node* pciParent);
extern status_t init_bus_acpi(device_node* node, void** bus_cookie);
extern status_t init_bus_pci(device_node* node, void** bus_cookie);
extern void uninit_bus(void* bus_cookie);
extern void bus_removed(void* bus_cookie);
status_t set_clock(void* controller, uint32_t kilohertz);
status_t execute_command(void* controller, uint8_t command,
uint32_t argument, uint32_t* response);
status_t do_io(void* controller, uint8_t command,
IOOperation* operation, bool offsetAsSectors);
void set_scan_semaphore(void* controller, sem_id sem);
void set_bus_width(void* controller, int width);
extern mmc_bus_interface gSDHCIACPIDeviceModule;
extern mmc_bus_interface gSDHCIPCIDeviceModule;
extern device_manager_info* gDeviceManager;
#endif