summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-socfpga/arria10-xload-emmc.c
diff options
context:
space:
mode:
authorSteffen Trumtrar <s.trumtrar@pengutronix.de>2018-07-31 12:44:39 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2018-08-08 09:22:48 +0200
commit8a680e3c9b5dd8470b7437654877d5439e9a6407 (patch)
treed2ca86ad8029b3b70c5754cbdc43618740b5f5d9 /arch/arm/mach-socfpga/arria10-xload-emmc.c
parent1466d7d0e485fe43258aea423d2e4deba7d83c1e (diff)
downloadbarebox-8a680e3c9b5dd8470b7437654877d5439e9a6407.tar.gz
barebox-8a680e3c9b5dd8470b7437654877d5439e9a6407.tar.xz
ARM: socfpga: Arria10: support programming FPGA in PBL
Some Arria10 boards don't have the FPGA programmed externally. Instead barebox needs to do that. As the Arria10 has the SDRAM controller in the FPGA, the first thing we need to do is, configure the FPGA before the SDRAM can even be used. It works like this: 1. boot ROM fetches the PBL from MMC 2. read the MBR from MMC (this depends on the setup done by the boot ROM) 3. read the Bitstream from the MMC and program the FPGA 4. re-read the barebox image from MMC, this time with the full barebox that is appended to the PBL 5. jump into the full barebox Only supported boot device is eMMC. Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/arm/mach-socfpga/arria10-xload-emmc.c')
-rw-r--r--arch/arm/mach-socfpga/arria10-xload-emmc.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/arch/arm/mach-socfpga/arria10-xload-emmc.c b/arch/arm/mach-socfpga/arria10-xload-emmc.c
new file mode 100644
index 0000000000..dcc38cf4a2
--- /dev/null
+++ b/arch/arm/mach-socfpga/arria10-xload-emmc.c
@@ -0,0 +1,222 @@
+#include <common.h>
+#include <init.h>
+#include <linux/sizes.h>
+#include <mach/generic.h>
+#include <mach/arria10-regs.h>
+#include <mach/arria10-system-manager.h>
+#include <mach/arria10-xload.h>
+#include <mci.h>
+#include "../../../drivers/mci/sdhci.h"
+#include "../../../drivers/mci/dw_mmc.h"
+
+#define SECTOR_SIZE 512
+
+static int dwmci_wait_reset(uint32_t value)
+{
+ uint32_t ctrl;
+ int32_t timeout;
+
+ timeout = 10000;
+
+ writel(value, ARRIA10_SDMMC_ADDR + DWMCI_CTRL);
+
+ while (timeout-- > 0) {
+ ctrl = readl(ARRIA10_SDMMC_ADDR + DWMCI_CTRL);
+ if (!(ctrl & DWMCI_RESET_ALL))
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static int dwmci_prepare_data(struct mci_data *data)
+{
+ unsigned long ctrl;
+
+ dwmci_wait_reset(DWMCI_CTRL_FIFO_RESET);
+
+ writel(DWMCI_INTMSK_TXDR | DWMCI_INTMSK_RXDR,
+ ARRIA10_SDMMC_ADDR + DWMCI_RINTSTS);
+
+ ctrl = readl(ARRIA10_SDMMC_ADDR + DWMCI_INTMASK);
+ ctrl |= DWMCI_INTMSK_TXDR | DWMCI_INTMSK_RXDR;
+ writel(ctrl, ARRIA10_SDMMC_ADDR + DWMCI_INTMASK);
+
+ ctrl = readl(ARRIA10_SDMMC_ADDR + DWMCI_CTRL);
+ ctrl &= ~(DWMCI_IDMAC_EN | DWMCI_DMA_EN);
+ writel(ctrl, ARRIA10_SDMMC_ADDR + DWMCI_CTRL);
+
+ writel(0x1, ARRIA10_SDMMC_ADDR + DWMCI_FIFOTH);
+ writel(0xffffffff, ARRIA10_SDMMC_ADDR + DWMCI_TMOUT);
+ writel(0x0, ARRIA10_SDMMC_ADDR + DWMCI_IDINTEN);
+
+ return 0;
+}
+
+static int dwmci_read_data_pio(struct mci_data *data)
+{
+ u32 *pdata = (u32 *)data->dest;
+ u32 val, status, timeout;
+ u32 rcnt, rlen = 0;
+
+ for (rcnt = (data->blocksize * data->blocks)>>2; rcnt; rcnt--) {
+ timeout = 20000;
+ status = readl(ARRIA10_SDMMC_ADDR + DWMCI_STATUS);
+ while (--timeout > 0
+ && (status & DWMCI_STATUS_FIFO_EMPTY)) {
+ __udelay(200);
+ status = readl(ARRIA10_SDMMC_ADDR + DWMCI_STATUS);
+ }
+ if (!timeout)
+ break;
+
+ val = readl(ARRIA10_SDMMC_ADDR + DWMCI_DATA);
+ *pdata++ = val;
+ rlen += 4;
+ }
+ writel(DWMCI_INTMSK_RXDR, ARRIA10_SDMMC_ADDR + DWMCI_RINTSTS);
+
+ return rlen;
+}
+
+static int dwmci_cmd(struct mci_cmd *cmd, struct mci_data *data)
+{
+ int flags = 0;
+ uint32_t mask;
+ int timeout;
+
+ timeout = 100000;
+ while (readl(ARRIA10_SDMMC_ADDR + DWMCI_STATUS) & DWMCI_STATUS_BUSY) {
+ if (timeout-- <= 0)
+ return -ETIMEDOUT;
+
+ }
+
+ writel(DWMCI_INTMSK_ALL, ARRIA10_SDMMC_ADDR + DWMCI_RINTSTS);
+
+ if (data) {
+ writel(data->blocksize, ARRIA10_SDMMC_ADDR + DWMCI_BLKSIZ);
+ writel(data->blocksize * data->blocks, ARRIA10_SDMMC_ADDR +
+ DWMCI_BYTCNT);
+
+ dwmci_prepare_data(data);
+ }
+
+ writel(cmd->cmdarg, ARRIA10_SDMMC_ADDR + DWMCI_CMDARG);
+
+ if (data)
+ flags = DWMCI_CMD_DATA_EXP;
+
+ if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
+ return -EINVAL;
+
+ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+ flags |= DWMCI_CMD_ABORT_STOP;
+ else
+ flags |= DWMCI_CMD_PRV_DAT_WAIT;
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ flags |= DWMCI_CMD_RESP_EXP;
+ if (cmd->resp_type & MMC_RSP_136)
+ flags |= DWMCI_CMD_RESP_LENGTH;
+ }
+
+ if (cmd->resp_type & MMC_RSP_CRC)
+ flags |= DWMCI_CMD_CHECK_CRC;
+
+ flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG);
+
+ writel(flags, ARRIA10_SDMMC_ADDR + DWMCI_CMD);
+
+ for (timeout = 10000; timeout > 0; timeout--) {
+ mask = readl(ARRIA10_SDMMC_ADDR + DWMCI_RINTSTS);
+ if (mask & DWMCI_INTMSK_CDONE) {
+ if (!data)
+ writel(mask, ARRIA10_SDMMC_ADDR + DWMCI_RINTSTS);
+ break;
+ }
+ }
+
+ if (timeout <= 0)
+ return -ETIMEDOUT;
+
+ if (mask & DWMCI_INTMSK_RTO)
+ return -ETIMEDOUT;
+ else if (mask & DWMCI_INTMSK_RE)
+ return -EIO;
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (cmd->resp_type & MMC_RSP_136) {
+ cmd->response[0] = readl(ARRIA10_SDMMC_ADDR + DWMCI_RESP3);
+ cmd->response[1] = readl(ARRIA10_SDMMC_ADDR + DWMCI_RESP2);
+ cmd->response[2] = readl(ARRIA10_SDMMC_ADDR + DWMCI_RESP1);
+ cmd->response[3] = readl(ARRIA10_SDMMC_ADDR + DWMCI_RESP0);
+ } else {
+ cmd->response[0] = readl(ARRIA10_SDMMC_ADDR + DWMCI_RESP0);
+ }
+ }
+
+ if (data) {
+ do {
+ mask = readl(ARRIA10_SDMMC_ADDR + DWMCI_RINTSTS);
+ if (mask & (DWMCI_DATA_ERR))
+ return -EIO;
+
+ if (mask & DWMCI_INTMSK_RXDR) {
+ dwmci_read_data_pio(data);
+ mask = readl(ARRIA10_SDMMC_ADDR + DWMCI_RINTSTS);
+ }
+ } while (!(mask & DWMCI_INTMSK_DTO));
+
+ writel(mask, ARRIA10_SDMMC_ADDR + DWMCI_RINTSTS);
+ }
+
+ return 0;
+}
+
+int arria10_read_blocks(void *dst, int blocknum, size_t len)
+{
+ struct mci_cmd cmd;
+ struct mci_data data;
+ int ret;
+ int blocks;
+
+ blocks = len / SECTOR_SIZE;
+
+ if (blocks > 1)
+ cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+ else
+ cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
+
+ cmd.cmdarg = blocknum;
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.dest = dst;
+ data.blocks = blocks;
+ data.blocksize = SECTOR_SIZE;
+ data.flags = MMC_DATA_READ;
+
+ ret = dwmci_cmd(&cmd, &data);
+
+ if (ret || blocks > 1) {
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ dwmci_cmd(&cmd, NULL);
+ }
+
+ return ret;
+}
+
+void arria10_init_mmc(void)
+{
+ writel(ARRIA10_SYSMGR_SDMMC_DRVSEL(3) |
+ ARRIA10_SYSMGR_SDMMC_SMPLSEL(2),
+ ARRIA10_SYSMGR_SDMMC);
+
+ /* enable power to card */
+ writel(0x1, ARRIA10_SDMMC_ADDR + DWMCI_PWREN);
+
+ writel(DWMCI_CTYPE_1BIT, ARRIA10_SDMMC_ADDR + DWMCI_CTYPE);
+}