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

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

File Facts

System
Linux kernel
Corpus path
drivers/mmc/host/sdhci-of-at91.c
Extension
.c
Size
13802 bytes
Lines
468
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_at91_soc_data {
	const struct sdhci_pltfm_data *pdata;
	bool baseclk_is_generated_internally;
	unsigned int divider_for_baseclk;
};

struct sdhci_at91_priv {
	const struct sdhci_at91_soc_data *soc_data;
	struct clk *hclock;
	struct clk *gck;
	struct clk *mainck;
	bool restore_needed;
	bool cal_always_on;
};

static void sdhci_at91_set_force_card_detect(struct sdhci_host *host)
{
	u8 mc1r;

	mc1r = readb(host->ioaddr + SDMMC_MC1R);
	mc1r |= SDMMC_MC1R_FCD;
	writeb(mc1r, host->ioaddr + SDMMC_MC1R);
}

static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock)
{
	u16 clk;

	host->mmc->actual_clock = 0;

	/*
	 * There is no requirement to disable the internal clock before
	 * changing the SD clock configuration. Moreover, disabling the
	 * internal clock, changing the configuration and re-enabling the
	 * internal clock causes some bugs. It can prevent to get the internal
	 * clock stable flag ready and an unexpected switch to the base clock
	 * when using presets.
	 */
	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
	clk &= SDHCI_CLOCK_INT_EN;
	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);

	if (clock == 0)
		return;

	clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);

	clk |= SDHCI_CLOCK_INT_EN;
	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);

	/* Wait max 20 ms */
	if (read_poll_timeout(sdhci_readw, clk, (clk & SDHCI_CLOCK_INT_STABLE),
			      1000, 20000, false, host, SDHCI_CLOCK_CONTROL)) {
		pr_err("%s: Internal clock never stabilised.\n",
		       mmc_hostname(host->mmc));
		return;
	}

	clk |= SDHCI_CLOCK_CARD_EN;
	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
}

static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host,
					 unsigned int timing)
{
	u8 mc1r;

	if (timing == MMC_TIMING_MMC_DDR52) {
		mc1r = sdhci_readb(host, SDMMC_MC1R);
		mc1r |= SDMMC_MC1R_DDR;
		sdhci_writeb(host, mc1r, SDMMC_MC1R);
	}
	sdhci_set_uhs_signaling(host, timing);
}

static void sdhci_at91_reset(struct sdhci_host *host, u8 mask)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host);
	unsigned int tmp;

	sdhci_reset(host, mask);

	if ((host->mmc->caps & MMC_CAP_NONREMOVABLE)
	    || mmc_gpio_get_cd(host->mmc) >= 0)
		sdhci_at91_set_force_card_detect(host);

	if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) {
		u32 calcr = sdhci_readl(host, SDMMC_CALCR);

Annotation

Implementation Notes