summaryrefslogtreecommitdiffstats
path: root/drivers/mci/sdhci.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2019-11-18 12:13:36 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2019-11-19 11:07:22 +0100
commit124721b28dd6f559cb1324ae11b532394af7fe54 (patch)
treeb9c6ca46d3cc73d2be8c6b5f939d75eab00cb741 /drivers/mci/sdhci.c
parent4da3ec317ad7b284a2634da5a9e3e49baa58f2f5 (diff)
downloadbarebox-124721b28dd6f559cb1324ae11b532394af7fe54.tar.gz
barebox-124721b28dd6f559cb1324ae11b532394af7fe54.tar.xz
mci: Add sdhci helper
We have several SDHCI compatible drivers in the tree. This starts with a set of generic helper functions for these drivers to share some common functionality. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/mci/sdhci.c')
-rw-r--r--drivers/mci/sdhci.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
new file mode 100644
index 0000000000..1ab1c0f236
--- /dev/null
+++ b/drivers/mci/sdhci.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <driver.h>
+#include <mci.h>
+#include <io.h>
+
+#include "sdhci.h"
+
+void sdhci_read_response(struct sdhci *sdhci, struct mci_cmd *cmd)
+{
+ if (cmd->resp_type & MMC_RSP_136) {
+ u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;
+
+ cmdrsp3 = sdhci_read32(sdhci, SDHCI_RESPONSE_3);
+ cmdrsp2 = sdhci_read32(sdhci, SDHCI_RESPONSE_2);
+ cmdrsp1 = sdhci_read32(sdhci, SDHCI_RESPONSE_1);
+ cmdrsp0 = sdhci_read32(sdhci, SDHCI_RESPONSE_0);
+ cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24);
+ cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24);
+ cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24);
+ cmd->response[3] = (cmdrsp0 << 8);
+ } else {
+ cmd->response[0] = sdhci_read32(sdhci, SDHCI_RESPONSE_0);
+ }
+}
+
+void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
+ struct mci_data *data, bool dma, u32 *command,
+ u32 *xfer)
+{
+ *command = 0;
+ *xfer = 0;
+
+ if (!(cmd->resp_type & MMC_RSP_PRESENT))
+ *command |= SDHCI_RESP_NONE;
+ else if (cmd->resp_type & MMC_RSP_136)
+ *command |= SDHCI_RESP_TYPE_136;
+ else if (cmd->resp_type & MMC_RSP_BUSY)
+ *command |= SDHCI_RESP_TYPE_48_BUSY;
+ else
+ *command |= SDHCI_RESP_TYPE_48;
+
+ if (cmd->resp_type & MMC_RSP_CRC)
+ *command |= SDHCI_CMD_CRC_CHECK_EN;
+ if (cmd->resp_type & MMC_RSP_OPCODE)
+ *command |= SDHCI_CMD_INDEX_CHECK_EN;
+
+ *command |= SDHCI_CMD_INDEX(cmd->cmdidx);
+
+ 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;
+ }
+}
+
+static void sdhci_rx_pio(struct sdhci *sdhci, struct mci_data *data,
+ unsigned int block)
+{
+ u32 *buf = (u32 *)data->dest;
+ int i;
+
+ buf += block * data->blocksize / sizeof(u32);
+
+ for (i = 0; i < data->blocksize / sizeof(u32); i++)
+ buf[i] = sdhci_read32(sdhci, SDHCI_BUFFER);
+}
+
+static void sdhci_tx_pio(struct sdhci *sdhci, struct mci_data *data,
+ unsigned int block)
+{
+ const u32 *buf = (const u32 *)data->src;
+ int i;
+
+ buf += block * data->blocksize / sizeof(u32);
+
+ for (i = 0; i < data->blocksize / sizeof(u32); i++)
+ sdhci_write32(sdhci, SDHCI_BUFFER, buf[i]);
+}
+
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data)
+{
+ unsigned int block = 0;
+ u32 stat, prs;
+ uint64_t start = get_time_ns();
+
+ do {
+ stat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+ if (stat & SDHCI_INT_ERROR)
+ return -EIO;
+
+ if (block >= data->blocks)
+ continue;
+
+ prs = sdhci_read32(sdhci, SDHCI_PRESENT_STATE);
+
+ if (prs & SDHCI_BUFFER_READ_ENABLE &&
+ data->flags & MMC_DATA_READ) {
+ sdhci_rx_pio(sdhci, data, block);
+ block++;
+ start = get_time_ns();
+ }
+
+ if (prs & SDHCI_BUFFER_WRITE_ENABLE &&
+ !(data->flags & MMC_DATA_READ)) {
+ sdhci_tx_pio(sdhci, data, block);
+ block++;
+ start = get_time_ns();
+ }
+
+ if (is_timeout(start, 10 * SECOND))
+ return -ETIMEDOUT;
+
+ } while (!(stat & SDHCI_INT_XFER_COMPLETE));
+
+ return 0;
+}