305 lines
10 KiB
C
305 lines
10 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Efinix eMMC Host Controller Driver Header with DMA Support
|
|
*
|
|
* Copyright (C) 2025 Efinix, Inc.
|
|
* Author: Teoh Choon Zone <czteoh@efinixinc.com>
|
|
*/
|
|
|
|
|
|
#ifndef __EFX_EMMC_H__
|
|
#define __EFX_EMMC_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>
|
|
|
|
#ifndef min3
|
|
#define min3(x, y, z) min(min(x, y), z)
|
|
#endif
|
|
|
|
#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)
|
|
|
|
#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
|
|
|
|
#define EFX_EMMC_VERSION 0x000
|
|
#define EFX_EMMC_BASE_REG0 0x004
|
|
#define EFX_EMMC_BASE_STATUS_REG0 0x008
|
|
#define EFX_EMMC_BASE_REG1 0x00C
|
|
#define EFX_EMMC_ARG2 0x100
|
|
#define EFX_EMMC_BLOCK_SIZE 0x104
|
|
#define EFX_EMMC_ARG1 0x108
|
|
#define EFX_EMMC_TRANSFER_MODE 0x10C
|
|
#define EFX_EMMC_RESPONSE0 0x110
|
|
#define EFX_EMMC_RESPONSE1 0x114
|
|
#define EFX_EMMC_RESPONSE2 0x118
|
|
#define EFX_EMMC_RESPONSE3 0x11C
|
|
#define EFX_EMMC_BUFFER_DATA_PORT 0x120
|
|
#define EFX_EMMC_PRESENT_STATE 0x124
|
|
#define EFX_EMMC_HOST_CONTROL 0x128
|
|
#define EFX_EMMC_INT_STATUS 0x130
|
|
#define EFX_EMMC_INT_STATUS_EN 0x134
|
|
#define EFX_EMMC_INT_SIGNAL_EN 0x138
|
|
#define EFX_EMMC_HOST_CAPABILITIES 0x140
|
|
#define EFX_EMMC_ADMA_SYS_ADDR_LOW 0x158
|
|
#define EFX_EMMC_ADMA_SYS_ADDR_HIGH 0x15C
|
|
|
|
#define EFX_SYS_DATE_REG 0x000
|
|
#define EFX_SYS_TEST_REG 0x004
|
|
#define EFX_SYS_RESET_REG 0x008
|
|
|
|
#define EFX_EMMC_BASE_REG0_CLK_EN BIT(16)
|
|
#define EFX_EMMC_BASE_REG0_CLK_DIV_MASK 0xFFFF
|
|
|
|
#define EFX_EMMC_BASE_STATUS_DAT_BUSY BIT(1)
|
|
#define EFX_EMMC_BASE_STATUS_CMD_BUSY BIT(0)
|
|
|
|
#define EFX_EMMC_BASE_REG1_SAMPLE_CNT_SHIFT 16
|
|
#define EFX_EMMC_BASE_REG1_SAMPLE_CNT_MASK (0xFFFF << 16)
|
|
#define EFX_EMMC_BASE_REG1_PHASE_SHIFT 6
|
|
#define EFX_EMMC_BASE_REG1_PHASE_MASK (0x7 << 6)
|
|
#define EFX_EMMC_BASE_REG1_PHASE_PULSE BIT(0)
|
|
|
|
#define EFX_EMMC_BLOCK_COUNT_SHIFT 16
|
|
#define EFX_EMMC_BLOCK_COUNT_MASK (0xFFFF << 16)
|
|
#define EFX_EMMC_BLOCK_SIZE_MASK 0xFFF
|
|
#define EFX_EMMC_DMA_BOUNDARY_SHIFT 12
|
|
#define EFX_EMMC_DMA_BOUNDARY_MASK (0x7 << 12)
|
|
|
|
#define EFX_EMMC_CMD_INDEX_SHIFT 24
|
|
#define EFX_EMMC_CMD_INDEX_MASK (0x3F << 24)
|
|
#define EFX_EMMC_DATA_PRESENT BIT(21)
|
|
#define EFX_EMMC_CMD_INDEX_CHECK_EN BIT(20)
|
|
#define EFX_EMMC_CMD_CRC_CHECK_EN BIT(19)
|
|
#define EFX_EMMC_RESP_TYPE_SHIFT 16
|
|
#define EFX_EMMC_RESP_TYPE_MASK (0x3 << 16)
|
|
#define EFX_EMMC_RESP_TYPE_NONE 0
|
|
#define EFX_EMMC_RESP_TYPE_136 1
|
|
#define EFX_EMMC_RESP_TYPE_48 2
|
|
#define EFX_EMMC_RESP_TYPE_48_BUSY 3
|
|
#define EFX_EMMC_MULTI_BLOCK_SEL BIT(5)
|
|
#define EFX_EMMC_DATA_XFER_DIR BIT(4)
|
|
#define EFX_EMMC_AUTO_CMD_EN_SHIFT 2
|
|
#define EFX_EMMC_AUTO_CMD_EN_MASK (0x3 << 2)
|
|
#define EFX_EMMC_BLOCK_COUNT_EN BIT(1)
|
|
#define EFX_EMMC_DMA_EN BIT(0)
|
|
|
|
#define EFX_EMMC_BUFFER_READ_EN BIT(11)
|
|
#define EFX_EMMC_BUFFER_WRITE_EN BIT(10)
|
|
#define EFX_EMMC_READ_XFER_ACTIVE BIT(9)
|
|
#define EFX_EMMC_WRITE_XFER_ACTIVE BIT(8)
|
|
#define EFX_EMMC_DAT_LINE_ACTIVE BIT(2)
|
|
#define EFX_EMMC_CMD_INHIBIT_DAT BIT(1)
|
|
#define EFX_EMMC_CMD_INHIBIT_CMD BIT(0)
|
|
|
|
#define EFX_EMMC_DATA_SAMPLING_MODE BIT(3)
|
|
#define EFX_EMMC_DATA_WIDTH_SHIFT 1
|
|
#define EFX_EMMC_DATA_WIDTH_MASK (0x3 << 1)
|
|
#define EFX_EMMC_DATA_WIDTH_1BIT 0
|
|
#define EFX_EMMC_DATA_WIDTH_4BIT 1
|
|
#define EFX_EMMC_DATA_WIDTH_8BIT 2
|
|
|
|
#define EFX_EMMC_INT_ADMA_ERROR BIT(25)
|
|
#define EFX_EMMC_INT_DATA_TIMEOUT_ERR BIT(22)
|
|
#define EFX_EMMC_INT_DATA_CRC_ERR BIT(21)
|
|
#define EFX_EMMC_INT_DATA_END_BIT_ERR BIT(20)
|
|
#define EFX_EMMC_INT_CMD_INDEX_ERR BIT(19)
|
|
#define EFX_EMMC_INT_CMD_END_BIT_ERR BIT(18)
|
|
#define EFX_EMMC_INT_CMD_CRC_ERR BIT(17)
|
|
#define EFX_EMMC_INT_CMD_TIMEOUT_ERR BIT(16)
|
|
#define EFX_EMMC_INT_BUFFER_READ_RDY BIT(5)
|
|
#define EFX_EMMC_INT_BUFFER_WRITE_RDY BIT(4)
|
|
#define EFX_EMMC_INT_BLOCK_GAP_EVENT BIT(2)
|
|
#define EFX_EMMC_INT_XFER_COMPLETE BIT(1)
|
|
#define EFX_EMMC_INT_CMD_COMPLETE BIT(0)
|
|
|
|
#define EFX_EMMC_INT_ERROR_MASK (EFX_EMMC_INT_ADMA_ERROR | \
|
|
EFX_EMMC_INT_DATA_TIMEOUT_ERR | \
|
|
EFX_EMMC_INT_DATA_CRC_ERR | \
|
|
EFX_EMMC_INT_DATA_END_BIT_ERR | \
|
|
EFX_EMMC_INT_CMD_INDEX_ERR | \
|
|
EFX_EMMC_INT_CMD_END_BIT_ERR | \
|
|
EFX_EMMC_INT_CMD_CRC_ERR | \
|
|
EFX_EMMC_INT_CMD_TIMEOUT_ERR)
|
|
|
|
#define EFX_EMMC_INT_ALL_MASK (EFX_EMMC_INT_ERROR_MASK | \
|
|
EFX_EMMC_INT_BUFFER_READ_RDY | \
|
|
EFX_EMMC_INT_BUFFER_WRITE_RDY | \
|
|
EFX_EMMC_INT_BLOCK_GAP_EVENT | \
|
|
EFX_EMMC_INT_XFER_COMPLETE | \
|
|
EFX_EMMC_INT_CMD_COMPLETE)
|
|
|
|
#define EFX_SYS_RESET_EMMC_DEV BIT(1)
|
|
#define EFX_SYS_RESET_EMMC_IP BIT(0)
|
|
|
|
#define EFX_EMMC_BASE_CLK_FREQ_MHZ 200
|
|
#define EFX_EMMC_MAX_BLOCK_LENGTH 512
|
|
#define EFX_EMMC_TIMEOUT_CLK_FREQ 200000000
|
|
|
|
#define EFX_EMMC_MIN_FREQ 400000
|
|
#define EFX_EMMC_MAX_FREQ 200000000
|
|
#define EFX_EMMC_PIO_TIMEOUT_MS 1000
|
|
|
|
#define EFX_EMMC_CLOCK_STABILIZE_DELAY 1000
|
|
#define EFX_EMMC_RESET_PULSE_WIDTH 1
|
|
#define EFX_EMMC_POST_RESET_DELAY 200
|
|
#define EFX_EMMC_CMD_RETRY_COUNT 3
|
|
|
|
#define EFX_EMMC_MAX_PLL_SHIFT 8
|
|
#define EFX_EMMC_PLL_SETTLING_TIME 50
|
|
#define EFX_EMMC_TUNING_TIMEOUT_MS 50
|
|
#define EFX_EMMC_TUNING_POLL_INTERVAL 200
|
|
#define EFX_EMMC_TUNING_BLOCK_SIZE_4BIT 64
|
|
#define EFX_EMMC_TUNING_BLOCK_SIZE_8BIT 128
|
|
#define EFX_EMMC_MIN_TIMING_MARGIN 1
|
|
|
|
struct efx_adma_desc {
|
|
u16 attr;
|
|
u16 len;
|
|
u32 addr;
|
|
} __packed __aligned(8);
|
|
|
|
struct efx_emmc_host {
|
|
struct mmc_host *mmc;
|
|
void __iomem *ioaddr;
|
|
void __iomem *sys_ioaddr;
|
|
struct clk *clk;
|
|
int irq;
|
|
|
|
struct mmc_request *mrq;
|
|
struct mmc_request *mrq_done;
|
|
struct mmc_command *cmd;
|
|
struct mmc_data *data;
|
|
|
|
u32 base_clk;
|
|
u32 current_clk;
|
|
|
|
int bytes_to_transfer;
|
|
int blocks_done;
|
|
unsigned int sg_offset;
|
|
|
|
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;
|
|
unsigned int bounce_used;
|
|
bool bounce_active;
|
|
|
|
bool use_dma;
|
|
bool dma_64bit;
|
|
|
|
u32 clk_div;
|
|
u32 hs200_sample_count;
|
|
u32 hs200_pll_shift;
|
|
u32 hs200_margin;
|
|
u32 hs400_sample_count;
|
|
u32 hs400_pll_shift;
|
|
u32 hs400_margin;
|
|
u32 hs400_default_sample;
|
|
u32 hs400_default_pll;
|
|
bool tuning_done;
|
|
bool tuning_in_progress;
|
|
unsigned int prev_timing;
|
|
bool hs400_retune_pending;
|
|
struct delayed_work hs400_retune_work;
|
|
|
|
unsigned long tuned_timing_modes;
|
|
|
|
spinlock_t lock;
|
|
|
|
struct platform_device *pdev;
|
|
};
|
|
|
|
#define efx_emmc_dbg_irq(host, fmt, ...) \
|
|
dev_dbg(&(host)->pdev->dev, fmt, ##__VA_ARGS__)
|
|
#define efx_emmc_dbg_pio(host, fmt, ...) \
|
|
dev_dbg(&(host)->pdev->dev, fmt, ##__VA_ARGS__)
|
|
#define efx_emmc_dbg_cmd(host, fmt, ...) \
|
|
dev_dbg(&(host)->pdev->dev, fmt, ##__VA_ARGS__)
|
|
|
|
int efx_emmc_probe(struct platform_device *pdev);
|
|
int efx_emmc_remove(struct platform_device *pdev);
|
|
int efx_emmc_init_hw(struct efx_emmc_host *host);
|
|
void efx_emmc_reset_hw(struct efx_emmc_host *host);
|
|
void efx_emmc_hs400_retune_work(struct work_struct *work);
|
|
|
|
void efx_emmc_request(struct mmc_host *mmc, struct mmc_request *mrq);
|
|
void efx_emmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
|
|
int efx_emmc_get_cd(struct mmc_host *mmc);
|
|
int efx_emmc_card_busy_wrapper(struct mmc_host *mmc);
|
|
int efx_emmc_get_ro(struct mmc_host *mmc);
|
|
irqreturn_t efx_emmc_irq(int irq, void *dev_id);
|
|
|
|
void efx_emmc_send_command(struct efx_emmc_host *host, struct mmc_command *cmd);
|
|
void efx_emmc_finish_request(struct efx_emmc_host *host,
|
|
struct mmc_request *mrq);
|
|
void efx_emmc_finish_command(struct efx_emmc_host *host);
|
|
void efx_emmc_finish_data(struct efx_emmc_host *host);
|
|
void efx_emmc_transfer_pio(struct efx_emmc_host *host);
|
|
void efx_emmc_set_clock(struct efx_emmc_host *host, unsigned int clock);
|
|
void efx_emmc_set_bus_width(struct efx_emmc_host *host, int width);
|
|
void efx_emmc_set_timing(struct efx_emmc_host *host, unsigned int timing);
|
|
bool efx_emmc_card_busy(struct efx_emmc_host *host);
|
|
|
|
int efx_emmc_adma_table_pre(struct efx_emmc_host *host,
|
|
struct mmc_data *data);
|
|
void efx_emmc_adma_table_post(struct efx_emmc_host *host,
|
|
struct mmc_data *data);
|
|
void efx_emmc_prepare_dma(struct efx_emmc_host *host, struct mmc_data *data);
|
|
void efx_emmc_cleanup_dma(struct efx_emmc_host *host, struct mmc_data *data);
|
|
void efx_emmc_set_adma_addr(struct efx_emmc_host *host, dma_addr_t addr);
|
|
|
|
int efx_emmc_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
|
int efx_emmc_execute_tuning_command(struct efx_emmc_host *host,
|
|
int bus_width);
|
|
void efx_emmc_set_timing_config(struct efx_emmc_host *host,
|
|
u32 sample_count, u32 pll_shift);
|
|
int efx_emmc_find_optimal_timing(struct efx_emmc_host *host,
|
|
u8 result_map[][EFX_EMMC_MAX_PLL_SHIFT],
|
|
u32 max_sample_count);
|
|
|
|
static inline u32 efx_emmc_readl(struct efx_emmc_host *host, u32 reg)
|
|
{
|
|
return readl(host->ioaddr + reg);
|
|
}
|
|
|
|
static inline void efx_emmc_writel(struct efx_emmc_host *host, u32 val, u32 reg)
|
|
{
|
|
writel(val, host->ioaddr + reg);
|
|
}
|
|
|
|
static inline u32 efx_emmc_sys_readl(struct efx_emmc_host *host, u32 reg)
|
|
{
|
|
return readl(host->sys_ioaddr + reg);
|
|
}
|
|
|
|
static inline void efx_emmc_sys_writel(struct efx_emmc_host *host, u32 val,
|
|
u32 reg)
|
|
{
|
|
writel(val, host->sys_ioaddr + reg);
|
|
}
|
|
|
|
#endif
|