⛏️ index : haiku.git

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