The driver supports UHS-I bus speed modes: SDR25, DDR50, and SDR104. By default, the controller operates in SDR25 mode. Higher-speed modes can be enabled via the Device Tree by adding the following properties to the SDIO node: - sd-uhs-ddr50 - sd-uhs-sdr104 When these properties are present, the driver negotiates the highest supported UHS mode with the card and host. Signed-off-by: Swee Aun Khor <sakhor@efinixinc.com>
386 lines
14 KiB
C
386 lines
14 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Efinix SDIO Host Controller Driver Header with DMA Support
|
|
*
|
|
* Copyright (C) 2026 Efinix, Inc.
|
|
* Author: Khor Swee Aun <sakhor@efinixinc.com>
|
|
*/
|
|
|
|
#ifndef __EFX_SDIO_H__
|
|
#define __EFX_SDIO_H__
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/mmc/host.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/dma-mapping.h>
|
|
|
|
/* Compatibility macros */
|
|
#ifndef min3
|
|
#define min3(x, y, z) min(min(x, y), z)
|
|
#endif
|
|
|
|
/* ADMA descriptor definitions */
|
|
#define EFX_ADMA_DESC_VALID BIT(0)
|
|
#define EFX_ADMA_DESC_END BIT(1)
|
|
#define EFX_ADMA_DESC_INT BIT(2)
|
|
#define EFX_ADMA_DESC_NOP (0 << 4)
|
|
#define EFX_ADMA_DESC_TRAN (2 << 4)
|
|
#define EFX_ADMA_DESC_LINK (3 << 4)
|
|
|
|
#define EFX_ADMA_MAX_LEN 65536
|
|
#define EFX_ADMA_DESC_ALIGN 8
|
|
#define EFX_ADMA_TABLE_SZ (512 * 8) /* Support up to 512 descriptors */
|
|
|
|
/* DMA boundary sizes */
|
|
#define EFX_DMA_BOUNDARY_4K 0
|
|
#define EFX_DMA_BOUNDARY_8K 1
|
|
#define EFX_DMA_BOUNDARY_16K 2
|
|
#define EFX_DMA_BOUNDARY_32K 3
|
|
#define EFX_DMA_BOUNDARY_64K 4
|
|
#define EFX_DMA_BOUNDARY_128K 5
|
|
#define EFX_DMA_BOUNDARY_256K 6
|
|
#define EFX_DMA_BOUNDARY_512K 7
|
|
|
|
/* SDIO IP Register Offsets - Efinix SDIO Controller */
|
|
#define EFX_SDIO_VERSION 0x000
|
|
#define EFX_SDIO_BASE_REG0 0x004
|
|
#define EFX_SDIO_BASE_STATUS_REG0 0x008
|
|
#define EFX_SDIO_BASE_REG1 0x00C
|
|
#define EFX_SDIO_ARG2 0x100
|
|
#define EFX_SDIO_BLOCK_SIZE 0x104
|
|
#define EFX_SDIO_ARG1 0x108
|
|
#define EFX_SDIO_TRANSFER_MODE 0x10C
|
|
#define EFX_SDIO_RESPONSE0 0x110
|
|
#define EFX_SDIO_RESPONSE1 0x114
|
|
#define EFX_SDIO_RESPONSE2 0x118
|
|
#define EFX_SDIO_RESPONSE3 0x11C
|
|
#define EFX_SDIO_BUFFER_DATA_PORT 0x120
|
|
#define EFX_SDIO_PRESENT_STATE 0x124
|
|
#define EFX_SDIO_HOST_CONTROL 0x128
|
|
#define EFX_SDIO_INT_STATUS 0x130
|
|
#define EFX_SDIO_INT_STATUS_EN 0x134
|
|
#define EFX_SDIO_INT_SIGNAL_EN 0x138
|
|
#define EFX_SDIO_HOST_CAPABILITIES 0x140
|
|
#define EFX_SDIO_HOST_ADJUSTMENT 0x144
|
|
#define EFX_SDIO_ADMA_SYS_ADDR_LOW 0x158
|
|
#define EFX_SDIO_ADMA_SYS_ADDR_HIGH 0x15C
|
|
|
|
/* System Register Offsets */
|
|
#define EFX_SYS_DATE_REG 0x000
|
|
#define EFX_SYS_TEST_REG 0x004
|
|
#define EFX_SYS_RESET_REG 0x008
|
|
|
|
/* Base Register 0 (0x004) */
|
|
#define EFX_SDIO_BASE_REG0_CLK_EN BIT(16)
|
|
#define EFX_SDIO_BASE_REG0_CLK_DIV_MASK 0xFFFF
|
|
|
|
/* Base Status Register 0 (0x008) */
|
|
#define EFX_SDIO_BASE_STATUS_DAT_BUSY BIT(1)
|
|
#define EFX_SDIO_BASE_STATUS_CMD_BUSY BIT(0)
|
|
|
|
/* Base Register 1 (0x00C) */
|
|
#define EFX_SDIO_BASE_REG1_SAMPLE_CNT_SHIFT 16
|
|
#define EFX_SDIO_BASE_REG1_SAMPLE_CNT_MASK (0xFFFF << 16)
|
|
#define EFX_SDIO_BASE_REG1_PHASE_SHIFT 6
|
|
#define EFX_SDIO_BASE_REG1_PHASE_MASK (0x7 << 6)
|
|
#define EFX_SDIO_BASE_REG1_PHASE_PULSE BIT(0)
|
|
|
|
/* Block Size Register (0x104) */
|
|
#define EFX_SDIO_BLOCK_COUNT_SHIFT 16
|
|
#define EFX_SDIO_BLOCK_COUNT_MASK (0xFFFF << 16)
|
|
#define EFX_SDIO_BLOCK_SIZE_MASK 0xFFF
|
|
#define EFX_SDIO_DMA_BOUNDARY_SHIFT 12
|
|
#define EFX_SDIO_DMA_BOUNDARY_MASK (0x7 << 12)
|
|
|
|
/* Transfer Mode Register (0x10C) */
|
|
#define EFX_SDIO_CMD_INDEX_SHIFT 24
|
|
#define EFX_SDIO_CMD_INDEX_MASK (0x3F << 24)
|
|
#define EFX_SDIO_DATA_PRESENT BIT(21)
|
|
#define EFX_SDIO_CMD_INDEX_CHECK_EN BIT(20)
|
|
#define EFX_SDIO_CMD_CRC_CHECK_EN BIT(19)
|
|
#define EFX_SDIO_RESP_TYPE_SHIFT 16
|
|
#define EFX_SDIO_RESP_TYPE_MASK (0x3 << 16)
|
|
#define EFX_SDIO_RESP_TYPE_NONE 0
|
|
#define EFX_SDIO_RESP_TYPE_136 1
|
|
#define EFX_SDIO_RESP_TYPE_48 2
|
|
#define EFX_SDIO_RESP_TYPE_48_BUSY 3
|
|
#define EFX_SDIO_MULTI_BLOCK_SEL BIT(5)
|
|
#define EFX_SDIO_DATA_XFER_DIR BIT(4)
|
|
#define EFX_SDIO_AUTO_CMD_EN_SHIFT 2
|
|
#define EFX_SDIO_AUTO_CMD_EN_MASK (0x3 << 2)
|
|
#define EFX_SDIO_BLOCK_COUNT_EN BIT(1)
|
|
#define EFX_SDIO_DMA_EN BIT(0)
|
|
|
|
/* Present State Register (0x124) */
|
|
#define EFX_SDIO_DAT_0_SIG_LVL BIT(20)
|
|
#define EFX_SDIO_BUFFER_READ_EN BIT(11)
|
|
#define EFX_SDIO_BUFFER_WRITE_EN BIT(10)
|
|
#define EFX_SDIO_READ_XFER_ACTIVE BIT(9)
|
|
#define EFX_SDIO_WRITE_XFER_ACTIVE BIT(8)
|
|
#define EFX_SDIO_DAT_LINE_ACTIVE BIT(2)
|
|
#define EFX_SDIO_CMD_INHIBIT_DAT BIT(1)
|
|
#define EFX_SDIO_CMD_INHIBIT_CMD BIT(0)
|
|
|
|
/* Host Control Register (0x128) */
|
|
#define EFX_SDIO_DATA_SAMPLING_MODE BIT(3)
|
|
#define EFX_SDIO_DATA_WIDTH_SHIFT 1
|
|
#define EFX_SDIO_DATA_WIDTH_MASK (0x3 << 1)
|
|
#define EFX_SDIO_DATA_WIDTH_1BIT 0
|
|
#define EFX_SDIO_DATA_WIDTH_4BIT 1
|
|
#define EFX_SDIO_DATA_WIDTH_8BIT 2
|
|
|
|
/* Interrupt Status Register bits */
|
|
#define EFX_SDIO_INT_ADMA_ERROR BIT(25)
|
|
#define EFX_SDIO_INT_DATA_TIMEOUT_ERR BIT(22)
|
|
#define EFX_SDIO_INT_DATA_CRC_ERR BIT(21)
|
|
#define EFX_SDIO_INT_DATA_END_BIT_ERR BIT(20)
|
|
#define EFX_SDIO_INT_CMD_INDEX_ERR BIT(19)
|
|
#define EFX_SDIO_INT_CMD_END_BIT_ERR BIT(18)
|
|
#define EFX_SDIO_INT_CMD_CRC_ERR BIT(17)
|
|
#define EFX_SDIO_INT_CMD_TIMEOUT_ERR BIT(16)
|
|
#define EFX_SDIO_INT_CARD BIT(8)
|
|
#define EFX_SDIO_INT_BUFFER_READ_RDY BIT(5)
|
|
#define EFX_SDIO_INT_BUFFER_WRITE_RDY BIT(4)
|
|
#define EFX_SDIO_INT_DMA_INTERRUPT BIT(3)
|
|
#define EFX_SDIO_INT_BLOCK_GAP_EVENT BIT(2)
|
|
#define EFX_SDIO_INT_XFER_COMPLETE BIT(1)
|
|
#define EFX_SDIO_INT_CMD_COMPLETE BIT(0)
|
|
|
|
#define EFX_SDIO_INT_ERROR_MASK (EFX_SDIO_INT_ADMA_ERROR | \
|
|
EFX_SDIO_INT_DATA_TIMEOUT_ERR | \
|
|
EFX_SDIO_INT_DATA_CRC_ERR | \
|
|
EFX_SDIO_INT_DATA_END_BIT_ERR | \
|
|
EFX_SDIO_INT_CMD_INDEX_ERR | \
|
|
EFX_SDIO_INT_CMD_END_BIT_ERR | \
|
|
EFX_SDIO_INT_CMD_CRC_ERR | \
|
|
EFX_SDIO_INT_CMD_TIMEOUT_ERR)
|
|
|
|
/* SDIO Ccard interrupt not included in ALL_MASK as it is handled separately
|
|
* by mmc core during SDIO IO driver initialization
|
|
*/
|
|
#define EFX_SDIO_INT_ALL_MASK (EFX_SDIO_INT_ERROR_MASK | \
|
|
EFX_SDIO_INT_BUFFER_READ_RDY | \
|
|
EFX_SDIO_INT_BUFFER_WRITE_RDY | \
|
|
EFX_SDIO_INT_DMA_INTERRUPT | \
|
|
EFX_SDIO_INT_BLOCK_GAP_EVENT | \
|
|
EFX_SDIO_INT_XFER_COMPLETE | \
|
|
EFX_SDIO_INT_CMD_COMPLETE)
|
|
|
|
/* System Reset Register bits */
|
|
#define EFX_SYS_RESET_SDIO_DEV BIT(3)
|
|
#define EFX_SYS_RESET_SDIO_IP BIT(2)
|
|
|
|
/* Host capabilities */
|
|
#define EFX_SDIO_BASE_CLK_FREQ_MHZ 200
|
|
#define EFX_SDIO_MAX_BLOCK_LENGTH 512
|
|
#define EFX_SDIO_TIMEOUT_CLK_FREQ 200000000
|
|
#define EFX_SDIO_IO_VOLTAGE_1_8V 0
|
|
#define EFX_SDIO_IO_VOLTAGE_3_3V 1
|
|
|
|
/* Driver constants */
|
|
#define EFX_SDIO_MIN_FREQ 400000 /* 400 KHz */
|
|
#define EFX_SDIO_MAX_FREQ 200000000 /* 200 MHz */
|
|
#define EFX_SDIO_PIO_TIMEOUT_MS 1000
|
|
|
|
/* Hardware specific constants from documentation */
|
|
#define EFX_SDIO_CLOCK_STABILIZE_DELAY 1000 /* 1ms + 74 clock cycles */
|
|
#define EFX_SDIO_RESET_PULSE_WIDTH 1 /* 1us minimum */
|
|
#define EFX_SDIO_POST_RESET_DELAY 200 /* 200us minimum */
|
|
#define EFX_SDIO_CMD_RETRY_COUNT 3 /* Command retry attempts */
|
|
|
|
/* Tuning algorithm constants */
|
|
#define EFX_SDIO_MAX_PLL_SHIFT 8 /* 8 phase positions (45°) */
|
|
#define EFX_SDIO_PLL_SETTLING_TIME 50 /* 50ms PLL settling time */
|
|
#define EFX_SDIO_TUNING_TIMEOUT_MS 50 /* CMD19 timeout */
|
|
#define EFX_SDIO_TUNING_POLL_INTERVAL 200 /* 200us polling interval */
|
|
#define EFX_SDIO_TUNING_BLOCK_SIZE_4BIT 64 /* 4-bit bus tuning block */
|
|
#define EFX_SDIO_TUNING_BLOCK_SIZE_8BIT 128 /* 8-bit bus tuning block */
|
|
#define EFX_SDIO_MIN_TIMING_MARGIN 1 /* Min consecutive valid */
|
|
|
|
/**
|
|
* struct efx_adma_desc - ADMA descriptor structure
|
|
* @attr: Descriptor attributes (valid, end, interrupt, type)
|
|
* @len: Data length for this descriptor
|
|
* @addr: 32-bit DMA address
|
|
*
|
|
* Hardware ADMA descriptor structure, must be 8-byte aligned
|
|
*/
|
|
struct efx_adma_desc {
|
|
u16 attr;
|
|
u16 len;
|
|
u32 addr;
|
|
} __packed __aligned(8);
|
|
|
|
/**
|
|
* struct efx_sdio_host - Efinix SDIO host controller instance
|
|
* @mmc: MMC host structure
|
|
* @ioaddr: Base address for SDIO registers
|
|
* @sys_ioaddr: Base address for system registers
|
|
* @clk: Controller clock
|
|
* @irq: Interrupt number
|
|
* @mrq: Current MMC request
|
|
* @cmd: Current MMC command
|
|
* @data: Current MMC data transfer
|
|
* @base_clk: Base clock frequency
|
|
* @current_clk: Current configured clock frequency
|
|
* @bytes_to_transfer: Remaining bytes for PIO transfer
|
|
* @blocks_done: Number of completed blocks
|
|
* @sg_offset: Current offset in scatter-gather list
|
|
* @adma_desc: ADMA descriptor table
|
|
* @adma_desc_dma: DMA address of descriptor table
|
|
* @adma_desc_sz: Size of descriptor table
|
|
* @bounce_buffer: Bounce buffer for unaligned transfers
|
|
* @bounce_dma: DMA address of bounce buffer
|
|
* @bounce_buffer_size: Size of bounce buffer
|
|
* @use_dma: Flag indicating DMA mode is active
|
|
* @dma_64bit: Flag indicating 64-bit DMA support
|
|
* @clk_div: Current clock divider value
|
|
* @optimal_sample_count: Optimal sample count from tuning
|
|
* @optimal_pll_shift: Optimal PLL shift from tuning
|
|
* @optimal_margin: Timing margin from tuning
|
|
* @tuning_done: Flag indicating tuning completion
|
|
* @tuning_in_progress: Flag indicating active tuning
|
|
* @prev_timing: Previous timing mode
|
|
* @hs400_retune_pending: Flag indicating HS400 retune needed
|
|
* @hs400_retune_work: Delayed work for HS400 retuning
|
|
* @tuned_timing_modes: Bitmap of successfully tuned timing modes
|
|
* @lock: Spinlock for protecting shared data
|
|
* @pdev: Platform device
|
|
*/
|
|
struct efx_sdio_host {
|
|
struct mmc_host *mmc;
|
|
void __iomem *ioaddr;
|
|
void __iomem *sys_ioaddr;
|
|
struct clk *clk;
|
|
int irq;
|
|
u32 io_voltage;
|
|
|
|
struct mmc_request *mrq;
|
|
struct mmc_command *cmd;
|
|
struct mmc_data *data;
|
|
|
|
u32 base_clk;
|
|
u32 current_clk;
|
|
|
|
/* Transfer state tracking */
|
|
unsigned int bytes_to_transfer;
|
|
unsigned int blocks_done;
|
|
unsigned int sg_offset;
|
|
|
|
/* DMA related fields */
|
|
struct efx_adma_desc *adma_desc;
|
|
dma_addr_t adma_desc_dma;
|
|
size_t adma_desc_sz;
|
|
|
|
void *bounce_buffer;
|
|
dma_addr_t bounce_dma;
|
|
unsigned int bounce_buffer_size;
|
|
bool use_bounce;
|
|
|
|
bool use_dma;
|
|
bool dma_64bit;
|
|
|
|
/* Tuning related fields */
|
|
u32 clk_div;
|
|
u32 optimal_sample_count;
|
|
u32 optimal_pll_shift;
|
|
u32 optimal_margin;
|
|
bool tuning_done;
|
|
bool tuning_in_progress;
|
|
unsigned int prev_timing;
|
|
bool hs400_retune_pending;
|
|
bool tuning_crc_error;
|
|
struct delayed_work hs400_retune_work;
|
|
|
|
/* Tuning state bitmap - tracks which timing modes have been tuned */
|
|
unsigned long tuned_timing_modes;
|
|
|
|
spinlock_t lock;
|
|
|
|
struct platform_device *pdev;
|
|
};
|
|
|
|
/* Debug macros */
|
|
#define efx_sdio_dbg_irq(host, fmt, ...) \
|
|
dev_dbg(&(host)->pdev->dev, fmt, ##__VA_ARGS__)
|
|
#define efx_sdio_dbg_pio(host, fmt, ...) \
|
|
dev_dbg(&(host)->pdev->dev, fmt, ##__VA_ARGS__)
|
|
#define efx_sdio_dbg_cmd(host, fmt, ...) \
|
|
dev_dbg(&(host)->pdev->dev, fmt, ##__VA_ARGS__)
|
|
|
|
/* Platform driver function prototypes */
|
|
int efx_sdio_probe(struct platform_device *pdev);
|
|
int efx_sdio_remove(struct platform_device *pdev);
|
|
int efx_sdio_init_hw(struct efx_sdio_host *host);
|
|
void efx_sdio_reset_hw(struct efx_sdio_host *host);
|
|
void efx_sdio_hs400_retune_work(struct work_struct *work);
|
|
|
|
/* Core MMC host operation prototypes */
|
|
void efx_sdio_request(struct mmc_host *mmc, struct mmc_request *mrq);
|
|
void efx_sdio_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
|
|
int efx_sdio_get_cd(struct mmc_host *mmc);
|
|
int efx_sdio_card_busy_wrapper(struct mmc_host *mmc);
|
|
int efx_sdio_get_ro(struct mmc_host *mmc);
|
|
irqreturn_t efx_sdio_irq(int irq, void *dev_id);
|
|
void efx_sdio_enable_sdio_irq(struct mmc_host *host, int enable);
|
|
void efx_sdio_ack_sdio_irq(struct mmc_host *host);
|
|
int efx_sdio_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios);
|
|
|
|
/* Core helper function prototypes */
|
|
void efx_sdio_send_command(struct efx_sdio_host *host, struct mmc_command *cmd);
|
|
void efx_sdio_finish_request(struct efx_sdio_host *host,
|
|
struct mmc_request *mrq);
|
|
void efx_sdio_finish_command(struct efx_sdio_host *host);
|
|
void efx_sdio_finish_data(struct efx_sdio_host *host);
|
|
void efx_sdio_transfer_pio(struct efx_sdio_host *host);
|
|
void efx_sdio_set_clock(struct efx_sdio_host *host, unsigned int clock);
|
|
void efx_sdio_set_bus_width(struct efx_sdio_host *host, int width);
|
|
void efx_sdio_set_timing(struct efx_sdio_host *host, unsigned int timing);
|
|
bool efx_sdio_card_busy(struct efx_sdio_host *host);
|
|
|
|
/* DMA function prototypes */
|
|
int efx_sdio_adma_table_pre(struct efx_sdio_host *host,
|
|
struct mmc_data *data);
|
|
void efx_sdio_adma_table_post(struct efx_sdio_host *host,
|
|
struct mmc_data *data);
|
|
void efx_sdio_prepare_dma(struct efx_sdio_host *host, struct mmc_data *data);
|
|
void efx_sdio_cleanup_dma(struct efx_sdio_host *host, struct mmc_data *data);
|
|
void efx_sdio_set_adma_addr(struct efx_sdio_host *host, dma_addr_t addr);
|
|
|
|
/* Tuning function prototypes */
|
|
int efx_sdio_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
|
int efx_sdio_execute_tuning_command(struct efx_sdio_host *host,
|
|
int bus_width);
|
|
int efx_sdio_execute_custom_tuning_command(struct efx_sdio_host *host,
|
|
int bus_width);
|
|
void efx_sdio_set_timing_config(struct efx_sdio_host *host,
|
|
u32 sample_count, u32 pll_shift);
|
|
int efx_sdio_find_optimal_timing(struct efx_sdio_host *host,
|
|
u8 result_map[][EFX_SDIO_MAX_PLL_SHIFT],
|
|
u32 max_sample_count);
|
|
|
|
/* Register access helpers */
|
|
static inline u32 efx_sdio_readl(struct efx_sdio_host *host, u32 reg)
|
|
{
|
|
return readl(host->ioaddr + reg);
|
|
}
|
|
|
|
static inline void efx_sdio_writel(struct efx_sdio_host *host, u32 val, u32 reg)
|
|
{
|
|
writel(val, host->ioaddr + reg);
|
|
}
|
|
|
|
static inline u32 efx_sdio_sys_readl(struct efx_sdio_host *host, u32 reg)
|
|
{
|
|
return readl(host->sys_ioaddr + reg);
|
|
}
|
|
|
|
static inline void efx_sdio_sys_writel(struct efx_sdio_host *host, u32 val,
|
|
u32 reg)
|
|
{
|
|
writel(val, host->sys_ioaddr + reg);
|
|
}
|
|
|
|
#endif /* __EFX_SDIO_H__ */ |