drivers/mmc/host/sdhci-of-bst.c

Source file repositories/reference/linux-study-clean/drivers/mmc/host/sdhci-of-bst.c

File Facts

System
Linux kernel
Corpus path
drivers/mmc/host/sdhci-of-bst.c
Extension
.c
Size
15159 bytes
Lines
524
Domain
Driver Families
Bucket
drivers/mmc
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct sdhci_bst_priv {
	void __iomem *crm_reg_base;
};

union sdhci_bst_rx_ctrl {
	struct {
		u32 rx_revert:1,
		    rx_clk_sel_sec:1,
		    rx_clk_div:4,
		    rx_clk_phase_inner:2,
		    rx_clk_sel_first:1,
		    rx_clk_phase_out:2,
		    rx_clk_en:1,
		    res0:20;
	};
	u32 reg;
};

static u32 sdhci_bst_crm_read(struct sdhci_pltfm_host *pltfm_host, u32 offset)
{
	struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host);

	return readl(priv->crm_reg_base + offset);
}

static void sdhci_bst_crm_write(struct sdhci_pltfm_host *pltfm_host, u32 offset, u32 value)
{
	struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host);

	writel(value, priv->crm_reg_base + offset);
}

static int sdhci_bst_wait_int_clk(struct sdhci_host *host)
{
	u16 clk;

	if (read_poll_timeout(sdhci_readw, clk, (clk & SDHCI_CLOCK_INT_STABLE),
			      BST_CLK_STABLE_POLL_US, BST_CLK_STABLE_TIMEOUT_US, false,
			      host, SDHCI_CLOCK_CONTROL))
		return -EBUSY;
	return 0;
}

static unsigned int sdhci_bst_get_max_clock(struct sdhci_host *host)
{
	return BST_DEFAULT_MAX_FREQ;
}

static unsigned int sdhci_bst_get_min_clock(struct sdhci_host *host)
{
	return BST_DEFAULT_MIN_FREQ;
}

static void sdhci_bst_enable_clk(struct sdhci_host *host, unsigned int clk)
{
	struct sdhci_pltfm_host *pltfm_host;
	unsigned int div;
	u32 val;
	union sdhci_bst_rx_ctrl rx_reg;

	pltfm_host = sdhci_priv(host);

	/* Calculate clock divider based on target frequency */
	if (clk == 0) {
		div = 0;
	} else if (clk < BST_DEFAULT_MIN_FREQ) {
		/* Below minimum: use max divider to get closest to min freq */
		div = BST_DEFAULT_MAX_FREQ / BST_DEFAULT_MIN_FREQ;
	} else if (clk <= BST_DEFAULT_MAX_FREQ) {
		/* Normal range: calculate divider directly */
		div = BST_DEFAULT_MAX_FREQ / clk;
	} else {
		/* Above maximum: no division needed */
		div = 1;
	}

	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
	clk &= ~SDHCI_CLOCK_CARD_EN;
	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);

	clk &= ~SDHCI_CLOCK_PLL_EN;
	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);

	val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL);
	val &= ~BST_TIMER_LOAD_BIT;
	sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val);

	val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL);
	val &= ~BST_TIMER_DIV_MASK;
	val |= BST_TIMER_DIV_VAL;

Annotation

Implementation Notes