root/src/add-ons/kernel/busses/mmc/sdhci.h
/*
 * 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; }

                // TODO response interrupt
                // TODO response check

                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;

                // For simplicity pre-define the standard response types from the SD
                // card specification
                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) {
                        // We need a value i so that:
                        //
                        // base (Hz)        1
                        // --------- < -----------
                        //  2 ^ i       delay (s)
                        //
                        // This will divide the timeout clock to a period longer than the delay needed by the
                        // SD card. Dividing each side of the equation by 1000 allows to use our input units
                        // directly (kHz on one side and ms on the other), and be less worried about overflows.
                        // In the code the equation is rearranged to not have to deal with divisions and lose
                        // precision.
                        //
                        // The allowed range for i is 13 to 27, which is then converted to a value between 0
                        // and 14 to write into the register.
                        //
                        // If we can't reach a sufficiently large delay, the loop will default to the maximum
                        // allowed value of 2^27.
                        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;
                        // wait up to 100ms
                        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));


// #pragma mark Interrupt registers 
#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)


// #pragma mark -
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;
                }

                // TODO add other bits for LED control, etc.

                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;

                // It's convenient to think of this as a single "bit width" setting,
                // but the bits for 4-bit and 8-bit modes were introduced at different
                // times and are not next to each other in the register.
                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));


// #pragma mark -
struct registers {
        // SD command generation
        volatile uint32_t system_address;
        BlockSize block_size;
        volatile uint16_t block_count;
        volatile uint32_t argument;
        volatile uint16_t transfer_mode;
        Command command;

        // Response
        volatile uint32_t response[4];

        // Buffer Data Port
        volatile uint32_t buffer_data_port;

        // Host control 1
        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;

        // Interrupt control
        volatile uint32_t interrupt_status;
        volatile uint32_t interrupt_status_enable;
        volatile uint32_t interrupt_signal_enable;
        volatile uint16_t auto_cmd12_error_status;

        // Host control 2
        volatile uint16_t host_control_2;

        // Capabilities
        Capabilities capabilities;
        volatile uint64_t max_current_capabilities;

        // Force event
        volatile uint16_t force_event_acmd_status;
        volatile uint16_t force_event_error_status;

        // ADMA2
        volatile uint8_t adma_error_status;
        volatile uint8_t padding[3];
        volatile uint64_t adma_system_address;

        // Preset values
        volatile uint64_t preset_value[2];
        volatile uint32_t padding1 :32;
        volatile uint16_t uhs2_preset_value;
        volatile uint16_t padding2 :16;

        // ADMA3
        volatile uint64_t adma3_id_address;

        // UHS-II
        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];

        // Pointers
        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];

        // Common area
        volatile uint16_t slot_interrupt_status;
        HostControllerVersion host_controller_version;
} __attribute__((packed));

typedef void* sdhci_mmc_bus;

struct sdhci_crs {
        uint8   irq;
//      uint8   irq_triggering;
//      uint8   irq_polarity;
//      uint8   irq_shareable;

        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 /*_SDHCI_H*/