Newer
Older
barebox / drivers / mci / atmel-sdhci-common.c
// SPDX-License-Identifier: GPL-2.0-only AND BSD-1-Clause
/*
 * Copyright (c) 2015, Atmel Corporation
 * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 */

#define pr_fmt(fmt) "atmel-sdhci: " fmt

#include <common.h>
#include <mci.h>

#include <mach/early_udelay.h>

#ifdef __PBL__
#define udelay early_udelay
#undef  dev_err
#define dev_err(d, ...)		pr_err(__VA_ARGS__)
#undef  dev_warn
#define dev_warn(d, ...)	pr_warn(__VA_ARGS__)
#endif

#include "atmel-sdhci.h"

#define AT91_SDHCI_CA1R		0x44	/* Capabilities 1 Register */

#define AT91_SDHCI_MC1R		0x204
#define		AT91_SDHCI_MC1_FCD		BIT(7)
#define AT91_SDHCI_CALCR	0x240
#define		AT91_SDHCI_CALCR_EN		BIT(0)
#define		AT91_SDHCI_CALCR_ALWYSON	BIT(4)

static inline struct at91_sdhci *to_priv(struct sdhci *sdhci)
{
	return container_of(sdhci, struct at91_sdhci, sdhci);
}

void at91_sdhci_host_capability(struct at91_sdhci *host,
				unsigned *voltages)
{
	u16 caps;

	caps = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1);

	if (caps & SDHCI_HOSTCAP_VOLTAGE_330)
		*voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
	if (caps & SDHCI_HOSTCAP_VOLTAGE_300)
		*voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
	if (caps & SDHCI_HOSTCAP_VOLTAGE_180)
		*voltages |= MMC_VDD_165_195;
}

bool at91_sdhci_is_card_inserted(struct at91_sdhci *host)
{
	struct sdhci *sdhci = &host->sdhci;
	bool is_inserted = false;
	u32 status_mask, reg;
	int ret;

	/* Enable (unmask) the Interrupt Status 'card inserted' bit */
	status_mask = sdhci_read32(sdhci, SDHCI_INT_ENABLE);
	status_mask |= SDHCI_INT_CARD_INSERT;
	sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);

	reg = sdhci_read32(sdhci, SDHCI_PRESENT_STATE);
	if (reg & SDHCI_CARD_PRESENT) {
		is_inserted = true;
		goto exit;
	}

	/*
	 * Debouncing of the card detect pin is up to 13ms on sama5d2 rev B
	 * and later.  Try to be safe and wait for up to 50ms.
	 */
	ret = sdhci_read32_poll_timeout(sdhci, SDHCI_INT_STATUS, reg,
					reg & SDHCI_INT_CARD_INSERT,
					50 * USEC_PER_MSEC);
	if (ret == 0)
		is_inserted = true;
exit:
	status_mask &= ~SDHCI_INT_CARD_INSERT;
	sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);

	status_mask = sdhci_read32(sdhci, SDHCI_INT_STATUS);
	status_mask |= SDHCI_INT_CARD_INSERT;
	sdhci_write32(sdhci, SDHCI_INT_STATUS, status_mask);

	return is_inserted;
}

static int at91_sdhci_wait_for_done(struct at91_sdhci *host, u32 mask)
{
	struct sdhci *sdhci = &host->sdhci;
	u32 status;
	int ret;

	ret = sdhci_read32_poll_timeout(sdhci, SDHCI_INT_STATUS, status,
					(status & mask) == mask || (status & SDHCI_INT_ERROR),
					USEC_PER_SEC);

	if (ret < 0) {
		dev_err(host->dev, "SDHCI timeout while waiting for done\n");
		return ret;
	}

	if (status & SDHCI_INT_TIMEOUT)
		return -ETIMEDOUT;

	if (status & SDHCI_INT_ERROR) {
		dev_err(host->dev, "SDHCI_INT_STATUS: 0x%08x\n", status);
		return -EPERM;
	}

	return status & 0xFFFF;
}

int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
			    struct mci_data *data)
{
	unsigned command, xfer;
	struct sdhci *sdhci = &host->sdhci;
	u32 mask, state;
	int status;
	int ret;

	/* Wait for idle before next command */
	mask = SDHCI_CMD_INHIBIT_CMD;
	if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
		mask |= SDHCI_CMD_INHIBIT_DATA;

	ret = sdhci_read32_poll_timeout(sdhci, SDHCI_PRESENT_STATE, state,
					!(state & mask), 100 * USEC_PER_MSEC);
	if (ret) {
		dev_err(host->dev, "timeout while waiting for idle\n");
		return ret;
	}

	sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);

	mask = SDHCI_INT_CMD_COMPLETE;

	sdhci_set_cmd_xfer_mode(sdhci, cmd, data, false, &command, &xfer);

	if (data) {
		sdhci_write8(sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
		sdhci_write16(sdhci, SDHCI_BLOCK_SIZE,
			      SDHCI_DMA_BOUNDARY_512K
			      | SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
		sdhci_write16(sdhci, SDHCI_BLOCK_COUNT, data->blocks);
		sdhci_write16(sdhci, SDHCI_TRANSFER_MODE, xfer);
		if (cmd->resp_type & MMC_RSP_BUSY)
			mask |= SDHCI_INT_XFER_COMPLETE;
	} else if (cmd->resp_type & MMC_RSP_BUSY) {
		sdhci_write16(sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
	}

	sdhci_write32(sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
	sdhci_write16(sdhci, SDHCI_COMMAND, command);

	status = at91_sdhci_wait_for_done(host, mask);
	if (status < 0)
		goto error;

	sdhci_read_response(sdhci, cmd);
	sdhci_write32(sdhci, SDHCI_INT_STATUS, mask);

	if (data)
		sdhci_transfer_data(sdhci, data);

	udelay(1000);

	status = sdhci_read32(sdhci, SDHCI_INT_STATUS);
	sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);

	return 0;

error:
	sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);

	sdhci_reset(sdhci, SDHCI_RESET_CMD);
	sdhci_reset(sdhci, SDHCI_RESET_DATA);

	return status;
}

static void at91_sdhci_set_power(struct at91_sdhci *host, unsigned vdd)
{
	struct sdhci *sdhci = &host->sdhci;
	u8 pwr = 0;

	switch (vdd) {
	case MMC_VDD_165_195:
		pwr = SDHCI_POWER_180;
		break;
	case MMC_VDD_29_30:
	case MMC_VDD_30_31:
		pwr = SDHCI_POWER_300;
		break;
	case MMC_VDD_32_33:
	case MMC_VDD_33_34:
		pwr = SDHCI_POWER_330;
		break;
	}

	if (pwr == 0) {
		sdhci_write8(sdhci, SDHCI_POWER_CONTROL, 0);
		return;
	}

	pwr |= SDHCI_BUS_POWER_EN;

	sdhci_write8(sdhci, SDHCI_POWER_CONTROL, pwr);
}

static int at91_sdhci_set_clock(struct at91_sdhci *host, unsigned clock)
{

	struct sdhci *sdhci = &host->sdhci;
	unsigned clk = 0, clk_div;
	unsigned reg;
	u32 present_mask, caps, caps_clk_mult;
	int ret;

	present_mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
	ret = sdhci_read32_poll_timeout(sdhci, SDHCI_PRESENT_STATE, reg,
					!(reg & present_mask),
					100 * USEC_PER_MSEC);
	if (ret) {
		dev_warn(host->dev, "Timeout waiting for CMD and DAT Inhibit bits\n");
		return ret;
	}

	sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, 0);

	if (clock == 0)
		return 0;

	caps = sdhci_read32(sdhci, AT91_SDHCI_CA1R);

	caps_clk_mult = (caps & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT;

	if (caps_clk_mult) {
		for (clk_div = 1; clk_div <= 1024; clk_div++) {
			if (host->caps_max_clock / clk_div <= clock)
				break;
		}
		clk = SDHCI_PROG_CLOCK_MODE;
		clk_div -= 1;
	} else {
		/* Version 3.00 divisors must be a multiple of 2. */
		if (host->caps_max_clock <= clock) {
			clk_div = 1;
		} else {
			for (clk_div = 2; clk_div < 2048; clk_div += 2) {
				if (host->caps_max_clock / clk_div <= clock)
					break;
			}
		}
		clk_div >>= 1;
	}

	clk |= SDHCI_FREQ_SEL(clk_div);
	clk |= ((clk_div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
		<< SDHCI_DIVIDER_HI_SHIFT;
	clk |= SDHCI_INTCLOCK_EN;

	sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, clk);

	ret = sdhci_read32_poll_timeout(sdhci, SDHCI_CLOCK_CONTROL, clk,
					clk & SDHCI_INTCLOCK_STABLE,
					20 * USEC_PER_MSEC);
	if (ret) {
		dev_warn(host->dev, "Timeout waiting for clock stable\n");
		return ret;
	}

	clk |= SDHCI_SDCLOCK_EN;
	sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, clk);

	reg = sdhci_read8(sdhci, SDHCI_HOST_CONTROL);
	if (clock > 26000000)
		reg |= SDHCI_HIGHSPEED_EN;
	else
		reg &= ~SDHCI_HIGHSPEED_EN;

	sdhci_write8(sdhci, SDHCI_HOST_CONTROL, reg);

	return 0;
}

static int at91_sdhci_set_bus_width(struct at91_sdhci *host, unsigned bus_width)
{
	struct sdhci *sdhci = &host->sdhci;
	unsigned reg;

	reg = sdhci_read8(sdhci, SDHCI_HOST_CONTROL);

	switch(bus_width) {
	case MMC_BUS_WIDTH_8:
		reg |= SDHCI_DATA_WIDTH_8BIT;
		break;
	case MMC_BUS_WIDTH_4:
		reg &= ~SDHCI_DATA_WIDTH_8BIT;
		reg |= SDHCI_DATA_WIDTH_4BIT;
		break;
	default:
		reg &= ~SDHCI_DATA_WIDTH_8BIT;
		reg &= ~SDHCI_DATA_WIDTH_4BIT;
	}

	sdhci_write8(sdhci, SDHCI_HOST_CONTROL, reg);

	return 0;
}

int at91_sdhci_set_ios(struct at91_sdhci *host, struct mci_ios *ios)
{
	int ret;

	ret = at91_sdhci_set_clock(host, ios->clock);
	if (ret)
		return ret;

	return at91_sdhci_set_bus_width(host, ios->bus_width);
}

int at91_sdhci_init(struct at91_sdhci *host, u32 maxclk,
		    bool force_cd, bool cal_always_on)
{
	struct sdhci *sdhci = &host->sdhci;
	unsigned status_mask;

	host->caps_max_clock = maxclk;

	at91_sdhci_set_power(host, MMC_VDD_32_33);

	status_mask = SDHCI_INT_CMD_COMPLETE
		| SDHCI_INT_XFER_COMPLETE
		| SDHCI_INT_SPACE_AVAIL
		| SDHCI_INT_DATA_AVAIL;

	status_mask |= SDHCI_INT_TIMEOUT
		| SDHCI_INT_CRC
		| SDHCI_INT_END_BIT
		| SDHCI_INT_INDEX
		| SDHCI_INT_DATA_TIMEOUT
		| SDHCI_INT_DATA_CRC
		| SDHCI_INT_DATA_END_BIT;

	sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);

	sdhci_write32(sdhci, SDHCI_SIGNAL_ENABLE, 0);

	/*
	 * If the device attached to the mci bus is not removable, it is safer
	 * to set the Force Card Detect bit. People often don't connect the
	 * card detect signal and use this pin for another purpose. If the card
	 * detect pin is not muxed to SDHCI controller, a default value is
	 * used. This value can be different from a SoC revision to another
	 * one. Problems come when this default value is not card present. To
	 * avoid this case, if the device is non removable then the card
	 * detection procedure using the SDMCC_CD signal is bypassed.
	 * This bit is reset when a software reset for all command is performed
	 * so we need to implement our own reset function to set back this bit.
	 */
	if (force_cd) {
		u8 mc1r = sdhci_read8(sdhci, AT91_SDHCI_MC1R);
		mc1r |= AT91_SDHCI_MC1_FCD;
		sdhci_write8(sdhci, AT91_SDHCI_MC1R, mc1r);
	}

	if (cal_always_on) {
		sdhci_write32(sdhci, AT91_SDHCI_CALCR,
			     AT91_SDHCI_CALCR_ALWYSON | AT91_SDHCI_CALCR_EN);
	}

	return 0;
}

static u32 at91_sdhci_read32(struct sdhci *sdhci, int reg)
{
	return readl(to_priv(sdhci)->base + reg);
}

static void at91_sdhci_write32(struct sdhci *sdhci, int reg, u32 value)
{
	writel(value, to_priv(sdhci)->base + reg);
}

static u16 at91_sdhci_read16(struct sdhci *sdhci, int reg)
{
	return readw(to_priv(sdhci)->base + reg);
}

static void at91_sdhci_write16(struct sdhci *sdhci, int reg, u16 value)
{
	writew(value, to_priv(sdhci)->base + reg);
}

static u8 at91_sdhci_read8(struct sdhci *sdhci, int reg)
{
	return readb(to_priv(sdhci)->base + reg);
}

static void at91_sdhci_write8(struct sdhci *sdhci, int reg, u8 value)
{
	writeb(value, to_priv(sdhci)->base + reg);
}

void at91_sdhci_mmio_init(struct at91_sdhci *host, void __iomem *base)
{
	host->base = base;
	host->sdhci.read8 = at91_sdhci_read8;
	host->sdhci.read16 = at91_sdhci_read16;
	host->sdhci.read32 = at91_sdhci_read32;
	host->sdhci.write8 = at91_sdhci_write8;
	host->sdhci.write16 = at91_sdhci_write16;
	host->sdhci.write32 = at91_sdhci_write32;
}