mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Ahmad Fatoum <ahmad@a3f.at>
To: barebox@lists.infradead.org
Cc: lst@pengutronix.de
Subject: [RFC PATCH 3/5] mci: add first-stage at91-sdhci driver
Date: Mon,  6 Jan 2020 18:35:38 +0100	[thread overview]
Message-ID: <20200106173540.20367-4-ahmad@a3f.at> (raw)
In-Reply-To: <20200106173540.20367-1-ahmad@a3f.at>

This commit imports the at91bootstrap v3.9.1-rc1 state of the
Atmel SDHCI variant driver.

Second stage use is not implemented and first stage use in general is
not possible, because SDRAM set up is not yet implemented.
However, it can already be used to show case the use of FAT from the
barebox PBL used in a follow up commit.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 drivers/mci/Kconfig              |   4 +
 drivers/mci/Makefile             |   1 +
 drivers/mci/atmel-sdhci-common.c | 279 ++++++++++++++++++++
 drivers/mci/atmel-sdhci-pbl.c    | 440 +++++++++++++++++++++++++++++++
 drivers/mci/atmel-sdhci.h        |  38 +++
 drivers/mci/sdhci.h              |  17 ++
 6 files changed, 779 insertions(+)
 create mode 100644 drivers/mci/atmel-sdhci-common.c
 create mode 100644 drivers/mci/atmel-sdhci-pbl.c
 create mode 100644 drivers/mci/atmel-sdhci.h

diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 80b3a26002b4..7485be5dcb6e 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -177,3 +177,7 @@ endif
 config MCI_IMX_ESDHC_PBL
 	bool
 	select MCI_SDHCI
+
+config MCI_ATMEL_SDHCI_PBL
+	bool
+	select MCI_SDHCI
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 54eb65978e5d..40ed772d3ff7 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_MCI_ATMEL)		+= atmel_mci.o
 obj-$(CONFIG_MCI_BCM283X)	+= mci-bcm2835.o
 obj-$(CONFIG_MCI_BCM283X_SDHOST)	+= bcm2835-sdhost.o
 obj-$(CONFIG_MCI_DOVE)		+= dove-sdhci.o
+pbl-$(CONFIG_MCI_ATMEL_SDHCI_PBL)	+= atmel-sdhci-pbl.o atmel-sdhci-common.o
 obj-$(CONFIG_MCI_IMX)		+= imx.o
 obj-$(CONFIG_MCI_IMX_ESDHC)	+= imx-esdhc.o imx-esdhc-common.o
 pbl-$(CONFIG_MCI_IMX_ESDHC_PBL)	+= imx-esdhc-pbl.o imx-esdhc-common.o
diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c
new file mode 100644
index 000000000000..499627e2e396
--- /dev/null
+++ b/drivers/mci/atmel-sdhci-common.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: 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 <io.h>
+#include <mci.h>
+#include <debug_ll.h>
+#include "atmel-sdhci.h"
+
+#define SDHCI_CARD_INSERTED		BIT(16)
+
+static u32 at91_sdhci_read32(struct sdhci *sdhci, int reg)
+{
+	struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci);
+	return readl(priv->base + reg);
+}
+
+static void at91_sdhci_write32(struct sdhci *sdhci, int reg, u32 value)
+{
+	struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci);
+	writel(value, priv->base + reg);
+}
+
+static u16 at91_sdhci_read16(struct sdhci *sdhci, int reg)
+{
+	struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci);
+	return readw(priv->base + reg);
+}
+
+static void at91_sdhci_write16(struct sdhci *sdhci, int reg, u16 value)
+{
+	struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci);
+	writew(value, priv->base + reg);
+}
+
+static u8 at91_sdhci_read8(struct sdhci *sdhci, int reg)
+{
+	struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci);
+	return readb(priv->base + reg);
+}
+
+static void at91_sdhci_write8(struct sdhci *sdhci, int reg, u8 value)
+{
+	struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci);
+	writeb(value, priv->base + reg);
+}
+
+static void at91_sdhci_software_reset_cmd(struct at91_sdhci *host)
+{
+	sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_CMD);
+
+	while (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_CMD)
+		;
+}
+
+void at91_sdhci_host_capability(struct at91_sdhci *host,
+				unsigned int *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)
+{
+	/*
+	 * 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 (50000µs). Let assume
+	 * the PCK (processor clock) frequency is 500MHz, hence 500 cycles/µs.
+	 * 500 * 50000 = 25000000 cycles.
+	 */
+	unsigned int timeout = 25000000;
+	bool is_inserted;
+	u32 status_mask;
+
+	/* Enable (unmask) the Interrupt Status 'card inserted' bit */
+	status_mask = sdhci_read32(&host->sdhci, SDHCI_INT_ENABLE);
+	status_mask |= SDHCI_INT_CARD_INSERT;
+	sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, status_mask);
+
+	is_inserted = !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_INSERTED);
+	if (is_inserted)
+		goto exit;
+
+	while (!(sdhci_read32(&host->sdhci, SDHCI_INT_STATUS) & SDHCI_INT_CARD_INSERT)
+	       && timeout--)
+		;
+
+	is_inserted = !!(sdhci_read32(&host->sdhci, SDHCI_INT_STATUS) &
+			 SDHCI_INT_CARD_INSERT);
+
+exit:
+	status_mask &= ~SDHCI_INT_CARD_INSERT;
+	sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, status_mask);
+
+	status_mask = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
+	status_mask |= SDHCI_INT_CARD_INSERT;
+	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, status_mask);
+
+	return is_inserted;
+}
+
+static void at91_sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
+				   struct mci_data *data, bool dma, u32 *command,
+				   u32 *xfer)
+{
+	*xfer = 0;
+
+	*command = SDHCI_CMD_INDEX(cmd->cmdidx);
+
+	if ((cmd->resp_type == SD_RESP_TYPE_R1)
+		|| (cmd->resp_type == SD_RESP_TYPE_R5)
+		|| (cmd->resp_type == SD_RESP_TYPE_R6)
+		|| (cmd->resp_type == SD_RESP_TYPE_R7))
+		*command |= SDHCI_RESP_TYPE_48
+			| SDHCI_CMD_CRC_CHECK_EN
+			| SDHCI_CMD_INDEX_CHECK_EN;
+	else if (cmd->resp_type == SD_RESP_TYPE_R1B)
+		*command |= SDHCI_RESP_TYPE_48_BUSY
+			| SDHCI_CMD_CRC_CHECK_EN
+			| SDHCI_CMD_INDEX_CHECK_EN;
+	else if (cmd->resp_type == SD_RESP_TYPE_R2)
+		*command |= SDHCI_RESP_TYPE_136
+			| SDHCI_CMD_CRC_CHECK_EN;
+	else if ((cmd->resp_type == SD_RESP_TYPE_R3)
+		|| (cmd->resp_type == SD_RESP_TYPE_R4))
+		*command |= SDHCI_RESP_TYPE_48;
+	else
+		*command |= SDHCI_RESP_NONE;
+
+	if (data) {
+		*command |= SDHCI_DATA_PRESENT;
+
+		*xfer |= SDHCI_BLOCK_COUNT_EN;
+
+		if (data->blocks > 1)
+			*xfer |= SDHCI_MULTIPLE_BLOCKS;
+
+		if (data->flags & MMC_DATA_READ)
+			*xfer |= SDHCI_DATA_TO_HOST;
+
+		if (dma)
+			*xfer |= SDHCI_DMA_EN;
+	}
+}
+
+int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *sd_cmd,
+			    struct mci_data *data)
+{
+	unsigned int normal_status, normal_status_mask;
+	unsigned int command, xfer;
+	unsigned int timeout;
+
+	timeout = 100000;
+	while ((--timeout) &&
+	       (sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) &
+		(SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA)))
+		;
+
+	if (!timeout)
+		pr_warn("SDHC: Timeout waiting for CMD and DAT Inhibit bits\n");
+
+	normal_status_mask = SDHCI_INT_CMD_COMPLETE;
+
+	at91_sdhci_set_cmd_xfer_mode(&host->sdhci, sd_cmd,
+			       data, false, &command,
+			       &xfer);
+
+	if (sd_cmd->resp_type == SD_RESP_TYPE_R1B)
+		normal_status_mask |= SDHCI_INT_XFER_COMPLETE;
+
+	if (data) {
+		sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+		sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, data->blocksize);
+		if (data->blocks > 1)
+			sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+
+		sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+	}
+
+	sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, sd_cmd->cmdarg);
+
+	sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+	timeout = 100000;
+	do {
+		normal_status = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS);
+	} while ((--timeout) &&
+		 ((normal_status & normal_status_mask) != normal_status_mask));
+
+	if (!timeout)
+		pr_debug("SDHC: Timeout waiting for command complete\n");
+
+	/* clear the status, except for read and write ready.
+	 * those will be cleared by the read/write data routine, which
+	 * bases itself on the fact that the hardware is ready to receive data
+	 * or has data ready to be read
+	 */
+	sdhci_write16(&host->sdhci, SDHCI_INT_NORMAL_STATUS,
+		    normal_status & ~(SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL));
+
+	if ((normal_status & normal_status_mask) == normal_status_mask) {
+		sdhci_read_response(&host->sdhci, sd_cmd);
+
+		/* if we have data but not using block transfer, we use PIO mode */
+		if (data)
+			sdhci_transfer_data(&host->sdhci, data);
+
+		return 0;
+	}
+
+	normal_status = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
+
+	at91_sdhci_software_reset_cmd(host);
+
+	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, normal_status);
+
+	if (normal_status & SDHCI_INT_TIMEOUT)
+		return -ETIMEDOUT;
+
+	return -EIO;
+}
+
+#define DEFAULT_SD_BLOCK_LEN		512
+
+int atmel_sdhci_init(struct at91_sdhci *host)
+{
+	unsigned int status_mask;
+
+	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(&host->sdhci, SDHCI_INT_ENABLE, status_mask);
+
+	sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0);
+
+	sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL,
+		     sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL)
+		     & ~(SDHCI_DATA_WIDTH_8BIT | SDHCI_DATA_WIDTH_4BIT));
+
+	return 0;
+}
+
+void atmel_sdhci_prep(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;
+}
diff --git a/drivers/mci/atmel-sdhci-pbl.c b/drivers/mci/atmel-sdhci-pbl.c
new file mode 100644
index 000000000000..52d15a7c0c76
--- /dev/null
+++ b/drivers/mci/atmel-sdhci-pbl.c
@@ -0,0 +1,440 @@
+// SPDX-License-Identifier: 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.
+ */
+
+#include <common.h>
+#include <pbl.h>
+#include <mci.h>
+#include <debug_ll.h>
+#include <mach/xload.h>
+#include "atmel-sdhci.h"
+
+#define DEFAULT_SD_BLOCK_LEN		512
+#define SUPPORT_MAX_BLOCKS		16U
+
+#define OCR_VOLTAGE_27_36_MASK		0xff8000
+#define CHECK_PATTERN			0xaa
+
+struct at91_sdhci_priv {
+	struct at91_sdhci host;
+
+	unsigned int voltages;
+	u32 rca;	/* Relative card address */
+	u32 ocr;	/* Operation condition register */
+	bool no_sd;
+	bool highcapacity_card;
+};
+
+/*
+ * Use time in us as a busy counter timeout value
+ */
+static inline void early_udelay(unsigned us)
+{
+	volatile unsigned i;
+
+	for (i = 0; i < us * 4; i++)
+		;
+}
+
+static int sd_cmd_send_status(struct at91_sdhci_priv *priv, unsigned int retries)
+{
+	unsigned int i;
+	int ret;
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_SEND_STATUS,
+		.resp_type = SD_RESP_TYPE_R1,
+		.cmdarg = priv->rca << 16,
+	};
+
+	for (i = 0; i < retries; i++) {
+		ret = at91_sdhci_send_command(&priv->host, &cmd, NULL);
+		if (ret)
+			return ret;
+
+		if ((cmd.response[0] >> 8) & 0x01)
+			break;
+
+		early_udelay(1000);
+	};
+
+	if (i == retries) {
+		pr_warn("Timeout, wait for card ready\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int sd_cmd_stop_transmission(struct at91_sdhci_priv *priv)
+{
+	unsigned int retries = 1000;
+	int ret;
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_STOP_TRANSMISSION,
+		.resp_type = SD_RESP_TYPE_R1B,
+	};
+
+	ret = at91_sdhci_send_command(&priv->host, &cmd, NULL);
+	if (ret)
+		return ret;
+
+	return sd_cmd_send_status(priv, retries);
+}
+
+static int sd_cmd_read_multiple_block(struct at91_sdhci_priv *priv,
+				      void *buf,
+				      unsigned int start,
+				      unsigned int block_count)
+{
+	u16 block_len = DEFAULT_SD_BLOCK_LEN;
+	struct mci_data data;
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK,
+		.resp_type = SD_RESP_TYPE_R1,
+		.cmdarg = start,
+	};
+
+	if (!priv->highcapacity_card)
+		cmd.cmdarg *= block_len;
+
+	data.dest = buf;
+	data.flags = MMC_DATA_READ;
+	data.blocksize = block_len;
+	data.blocks = block_count;
+
+	return at91_sdhci_send_command(&priv->host, &cmd, &data);
+}
+
+
+static int atmel_sdhci_bio_read(struct pbl_bio *bio, off_t start,
+				void *buf, unsigned int nblocks)
+{
+	struct at91_sdhci_priv *priv = bio->priv;
+	unsigned int blocks_done = 0;
+	unsigned int blocks;
+	unsigned int block_len = DEFAULT_SD_BLOCK_LEN;
+	unsigned int blocks_read;
+	int ret;
+
+	/*
+	 * Refer to the at91sam9g20 datasheet:
+	 * Figure 35-10. Read Function Flow Diagram
+	*/
+
+	while (blocks_done < nblocks) {
+		blocks = min(nblocks - blocks_done, SUPPORT_MAX_BLOCKS);
+
+		blocks_read = sd_cmd_read_multiple_block(priv, buf,
+							 start + blocks_done,
+							 blocks);
+
+		ret = sd_cmd_stop_transmission(priv);
+		if (ret)
+			return ret;
+
+		blocks_done += blocks_read;
+
+		if (blocks_read != blocks)
+			break;
+
+		buf += blocks * block_len;
+	}
+
+	return blocks_done;
+}
+
+static int sd_cmd_go_idle_state(struct at91_sdhci_priv *priv)
+{
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_GO_IDLE_STATE,
+		.resp_type = MMC_RSP_NONE,
+	};
+
+	return at91_sdhci_send_command(&priv->host, &cmd, NULL);
+}
+
+static int sd_cmd_send_if_cond(struct at91_sdhci_priv *priv)
+{
+	int ret;
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_SEND_EXT_CSD,
+		.resp_type = SD_RESP_TYPE_R1,
+		.cmdarg = CHECK_PATTERN,
+	};
+
+	if (priv->voltages & OCR_VOLTAGE_27_36_MASK)
+		cmd.cmdarg |= 0x01 << 8;
+
+	ret = at91_sdhci_send_command(&priv->host, &cmd, NULL);
+	if (ret)
+		return ret;
+
+	if (((cmd.response[0] & CHECK_PATTERN) != CHECK_PATTERN)
+		|| (((cmd.response[0] >> 8) & 0x0f) != 0x01))
+		return -EIO;
+
+	return 0;
+}
+
+static int sd_cmd_send_app_cmd(struct at91_sdhci_priv *priv)
+{
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_APP_CMD,
+		.resp_type = SD_RESP_TYPE_R1,
+		.cmdarg = priv->rca << 16,
+	};
+
+	return at91_sdhci_send_command(&priv->host, &cmd, NULL);
+}
+
+static int sd_cmd_app_sd_send_op_cmd(struct at91_sdhci_priv *priv,
+				unsigned int capacity_support,
+				unsigned int *reponse)
+{
+	int ret;
+	struct mci_cmd cmd = {
+		.cmdidx = SD_CMD_APP_SEND_OP_COND,
+		.resp_type = SD_RESP_TYPE_R3,
+		.cmdarg = priv->voltages & OCR_VOLTAGE_27_36_MASK,
+	};
+
+	if (capacity_support)
+		cmd.cmdarg |= OCR_HCS;
+
+	ret = at91_sdhci_send_command(&priv->host, &cmd, NULL);
+	if (ret)
+		return ret;
+
+	*reponse = cmd.response[0];
+
+	return 0;
+}
+
+static int sd_check_operational_condition(struct at91_sdhci_priv *priv,
+			unsigned int capacity_support)
+{
+	unsigned int response = 0;
+	unsigned int retries = 1000;
+	unsigned int i;
+	int ret;
+
+	/*
+	 * The host repeatedly issues ACMD41 for at least 1 second
+	 * or until the busy bit are set to 1.
+	 */
+	for (i = 0; i < retries; i++) {
+		ret = sd_cmd_send_app_cmd(priv);
+		if (ret)
+			return ret;
+
+		ret = sd_cmd_app_sd_send_op_cmd(priv,
+				capacity_support, &response);
+		if (ret)
+			return ret;
+
+		if (response & OCR_BUSY)
+			break;
+
+		early_udelay(1000);
+	};
+
+	if (i == retries)
+		return -EIO;
+
+	priv->ocr = response;
+
+	return 0;
+}
+
+static int sd_cmd_all_send_cid(struct at91_sdhci_priv *priv)
+{
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_ALL_SEND_CID,
+		.resp_type = SD_RESP_TYPE_R2,
+	};
+
+	return at91_sdhci_send_command(&priv->host, &cmd, NULL);
+}
+
+static int sd_cmd_send_relative_addr(struct at91_sdhci_priv *priv)
+{
+	int ret;
+	struct mci_cmd cmd = {
+		.cmdidx = SD_CMD_SEND_RELATIVE_ADDR,
+		.resp_type = SD_RESP_TYPE_R6,
+	};
+
+	priv->rca = 1;
+	cmd.cmdarg = priv->rca << 16,
+
+	ret = at91_sdhci_send_command(&priv->host, &cmd, NULL);
+	if (ret)
+		return ret;
+
+	if (!priv->no_sd)
+		priv->rca = (cmd.response[0] >> 16) & 0xffff;
+
+	return 0;
+}
+
+static int sd_cmd_select_card(struct at91_sdhci_priv *priv)
+{
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_SELECT_CARD,
+		.resp_type = SD_RESP_TYPE_R1,
+		.cmdarg = priv->rca << 16,
+	};
+
+	return at91_sdhci_send_command(&priv->host, &cmd, NULL);
+}
+
+#define OCR_VOLTAGE_WIN_27_36	0x00FF8000
+#define OCR_ACCESS_MODE		0x60000000
+
+#define OCR_ACCESS_MODE_BYTE	(0x00 << 30)
+#define OCR_ACCESS_MODE_SECTOR	(0x01 << 30)
+
+static int mmc_cmd_send_op_cond(struct at91_sdhci_priv *priv,
+				unsigned int *ocr)
+{
+	struct mci_cmd cmd = {
+		.cmdidx = MMC_CMD_SEND_OP_COND,
+		.resp_type = SD_RESP_TYPE_R3,
+		.cmdarg = *ocr & (OCR_VOLTAGE_WIN_27_36 | OCR_ACCESS_MODE),
+	};
+	int ret;
+
+	ret = at91_sdhci_send_command(&priv->host, &cmd, NULL);
+	if (ret)
+		return ret;
+
+	*ocr = cmd.response[0];
+
+	return 0;
+}
+
+static int mmc_verify_operating_condition(struct at91_sdhci_priv *priv)
+{
+	unsigned int ocr = 0;
+	unsigned int retries = 1000;
+	unsigned int i;
+	int ret;
+
+	/* Query the card and determine the voltage type of the card */
+	ret = mmc_cmd_send_op_cond(priv, &ocr);
+	if (ret)
+		return ret;
+
+	ocr |= OCR_ACCESS_MODE_SECTOR;
+
+	for (i = 0; i < retries; i++) {
+		ret = mmc_cmd_send_op_cond(priv, &ocr);
+		if (ret)
+			return ret;
+
+		if (ocr  & (0x01U << 31))
+			break;
+
+		early_udelay(1000);
+	};
+
+	if (i == retries)
+		return -EIO;
+
+	pr_debug("mmc_verify_operating_condition success OCR = %x\n", ocr);
+	return 0;
+}
+
+/*
+ * Refer to Physical Layer Specification Version 3.1
+ * Figure 4-1: SD Memory Card State Diagram (card identification mode)
+ * Figure 4-2: Card Initialization and Indentification Flow (SD mode)
+ */
+static int sdcard_identification(struct at91_sdhci_priv *priv)
+{
+	int ret;
+
+	early_udelay(3000);
+
+	ret = sd_cmd_go_idle_state(priv);
+	if (ret)
+		return ret;
+
+	early_udelay(2000);
+
+	ret = mmc_verify_operating_condition(priv);
+	if (ret == 0) {
+		priv->no_sd = true;
+	} else if (ret == -ETIMEDOUT) {
+		ret = sd_cmd_send_if_cond(priv);
+		if (ret == 0) /* Ver 2.00 or later SD Memory Card */
+			ret = sd_check_operational_condition(priv, 1);
+		else if (ret == -ETIMEDOUT)
+			ret = sd_check_operational_condition(priv, 0);
+	}
+
+	if (ret) {
+		pr_warn("Unusable Card\n");
+		return ret;
+	}
+
+	priv->highcapacity_card = priv->ocr & OCR_HCS ? 1 : 0;
+
+	/*
+	 * Card that is unidentified (which is in Ready State)
+	 * sends its CID number
+	 */
+	ret = sd_cmd_all_send_cid(priv);
+	if (ret) {
+		pr_warn("sd_cmd_all_send_cid failed\n");
+		return ret;
+	}
+
+	/* Asks the card to publish a new relative card address (RCA) */
+	ret = sd_cmd_send_relative_addr(priv);
+	if (ret) {
+		pr_warn("sd_cmd_send_relative_addr failed\n");
+		return ret;
+	}
+
+	pr_debug("sdcard_identification success\n");
+	return 0;
+}
+
+
+static struct at91_sdhci_priv atmel_sdcard;
+
+int atmel_sdhci_bio_init(struct pbl_bio *bio, void __iomem *base)
+{
+	struct at91_sdhci_priv *priv = &atmel_sdcard;
+	struct at91_sdhci *host = &priv->host;
+	int ret;
+
+	bio->priv = priv;
+	bio->read = atmel_sdhci_bio_read;
+
+	atmel_sdhci_prep(host, base);
+
+	at91_sdhci_host_capability(host, &priv->voltages);
+
+	if (!at91_sdhci_is_card_inserted(host)) {
+		pr_err("SDHC: No Card Inserted\n");
+		return -ENODEV;
+	}
+
+	ret = atmel_sdhci_init(host);
+	if (ret)
+		return ret;
+
+	/* Card Indentification Mode */
+	ret = sdcard_identification(priv);
+	if (ret)
+		return ret;
+
+	return sd_cmd_select_card(priv);
+}
diff --git a/drivers/mci/atmel-sdhci.h b/drivers/mci/atmel-sdhci.h
new file mode 100644
index 000000000000..fb4c0e346e7d
--- /dev/null
+++ b/drivers/mci/atmel-sdhci.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020 Ahmad Fatoum, Pengutronix
+
+#ifndef ATMEL_SDHCI_H_
+#define ATMEL_SDHCI_H_
+
+#include <linux/types.h>
+#include <mci.h>
+
+#include "sdhci.h"
+
+struct at91_sdhci {
+	struct sdhci	sdhci;
+	void __iomem	*base;
+};
+
+/*
+ * Response Types
+ */
+#define	SD_RESP_TYPE_NO_RESP	0x00
+#define	SD_RESP_TYPE_R1		0x10
+#define	SD_RESP_TYPE_R1B	0x11
+#define	SD_RESP_TYPE_R2		0x20
+#define	SD_RESP_TYPE_R3		0x30
+#define	SD_RESP_TYPE_R4		0x40
+#define	SD_RESP_TYPE_R5		0x50
+#define	SD_RESP_TYPE_R6		0x60
+#define	SD_RESP_TYPE_R7		0x70
+
+int atmel_sdhci_init(struct at91_sdhci *host);
+void atmel_sdhci_prep(struct at91_sdhci *host, void __iomem *base);
+int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *sd_cmd,
+			    struct mci_data *data);
+bool at91_sdhci_is_card_inserted(struct at91_sdhci *host);
+void at91_sdhci_host_capability(struct at91_sdhci *host,
+				unsigned int *voltages);
+
+#endif
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index a307dc97cd9a..48dc3f36530c 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -38,6 +38,7 @@
 #define SDHCI_RESPONSE_1					0x14
 #define SDHCI_RESPONSE_2					0x18
 #define SDHCI_RESPONSE_3					0x1c
+#define SDHCI_RESPONSE(i)					(SDHCI_RESPONSE_0 + 4 * (i))
 #define SDHCI_BUFFER						0x20
 #define SDHCI_PRESENT_STATE					0x24
 #define  SDHCI_WRITE_PROTECT			BIT(19)
@@ -53,6 +54,7 @@
 #define  SDHCI_CARD_DETECT_SIGNAL_SELECTION	BIT(7)
 #define  SDHCI_CARD_DETECT_TEST_LEVEL		BIT(6)
 #define  SDHCI_DATA_WIDTH_8BIT			BIT(5)
+#define  SDHCI_DMASEL_ADMA32			BIT(4)
 #define  SDHCI_HIGHSPEED_EN			BIT(2)
 #define  SDHCI_DATA_WIDTH_4BIT			BIT(1)
 #define SDHCI_POWER_CONTROL					0x29
@@ -68,8 +70,11 @@
 #define SDHCI_TIMEOUT_CONTROL					0x2e
 #define SDHCI_SOFTWARE_RESET					0x2f
 #define  SDHCI_RESET_ALL			BIT(0)
+#define  SDHCI_RESET_CMD			BIT(1)
+#define  SDHCI_RESET_DATA			BIT(2)
 #define SDHCI_INT_STATUS					0x30
 #define SDHCI_INT_NORMAL_STATUS					0x30
+#define  SDHCI_INT_ERROR_ADMA			BIT(25)
 #define  SDHCI_INT_DATA_END_BIT			BIT(22)
 #define  SDHCI_INT_DATA_CRC			BIT(21)
 #define  SDHCI_INT_DATA_TIMEOUT			BIT(20)
@@ -79,6 +84,7 @@
 #define  SDHCI_INT_TIMEOUT			BIT(16)
 #define  SDHCI_INT_ERROR			BIT(15)
 #define  SDHCI_INT_CARD_INT			BIT(8)
+#define  SDHCI_INT_CARD_INSERT			BIT(6)
 #define  SDHCI_INT_DATA_AVAIL			BIT(5)
 #define  SDHCI_INT_SPACE_AVAIL			BIT(4)
 #define  SDHCI_INT_DMA				BIT(3)
@@ -86,7 +92,11 @@
 #define  SDHCI_INT_CMD_COMPLETE			BIT(0)
 #define SDHCI_INT_ERROR_STATUS					0x32
 #define SDHCI_INT_ENABLE					0x34
+#define SDHCI_INT_NORMAL_ENABLE					0x34
+#define SDHCI_INT_ERROR_ENABLE					0x36
 #define SDHCI_SIGNAL_ENABLE					0x38
+#define SDHCI_SIGNAL_NORMAL_ENABLE				0x38
+#define SDHCI_SIGNAL_ERROR_ENABLE				0x3a
 #define SDHCI_ACMD12_ERR__HOST_CONTROL2				0x3C
 #define SDHCI_CAPABILITIES					0x40
 #define SDHCI_CAPABILITIES_1					0x42
@@ -96,6 +106,13 @@
 #define  SDHCI_HOSTCAP_HIGHSPEED		BIT(5)
 #define  SDHCI_HOSTCAP_8BIT			BIT(2)
 
+#define SDHCI_PRESET_FOR_SDR12					0x66
+#define SDHCI_PRESET_FOR_SDR25					0x68
+#define SDHCI_PRESET_FOR_SDR50					0x6A
+#define SDHCI_PRESET_FOR_SDR104					0x6C
+#define SDHCI_PRESET_FOR_DDR50					0x6E
+#define SDHCI_PRESET_FOR_HS400					0x74 /* Non-standard */
+
 #define SDHCI_SPEC_200_MAX_CLK_DIVIDER	256
 #define SDHCI_MMC_BOOT						0xC4
 
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  parent reply	other threads:[~2020-01-06 17:36 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-06 17:35 [RFC PATCH 0/5] fs: fat: extend for in-PBL support Ahmad Fatoum
2020-01-06 17:35 ` [RFC PATCH 1/5] pbl: add block I/O API Ahmad Fatoum
2020-01-06 17:35 ` [RFC PATCH 2/5] fs: fat: extend for in-PBL support Ahmad Fatoum
2020-01-08 11:14   ` Sascha Hauer
2020-01-06 17:35 ` Ahmad Fatoum [this message]
2020-01-08 11:23   ` [RFC PATCH 3/5] mci: add first-stage at91-sdhci driver Sascha Hauer
2020-01-06 17:35 ` [RFC PATCH 4/5] ARM: at91: add helpers for MCI barebox chain-loading Ahmad Fatoum
2020-01-06 17:35 ` [RFC PATCH 5/5] [WIP] ARM: at91: sama5d27-som1: add first stage entry point Ahmad Fatoum

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200106173540.20367-4-ahmad@a3f.at \
    --to=ahmad@a3f.at \
    --cc=barebox@lists.infradead.org \
    --cc=lst@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox