summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/misc/cros_ec_spi.c4
-rw-r--r--drivers/mmc/Makefile2
-rw-r--r--drivers/mmc/bcm2835_sdhci.c2
-rw-r--r--drivers/mmc/dw_mmc.c5
-rw-r--r--drivers/mmc/gen_atmel_mci.c63
-rw-r--r--drivers/mmc/mvebu_mmc.c361
-rw-r--r--drivers/mmc/s3c_sdi.c321
-rw-r--r--drivers/mtd/spi/sf_ops.c1
-rw-r--r--drivers/net/fm/memac_phy.c2
-rw-r--r--drivers/net/fm/tgec_phy.c2
-rw-r--r--drivers/spi/exynos_spi.c9
-rw-r--r--drivers/spi/mxc_spi.c17
-rw-r--r--drivers/spi/spi.c2
13 files changed, 764 insertions, 27 deletions
diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c
index 7df709cc71..015333f139 100644
--- a/drivers/misc/cros_ec_spi.c
+++ b/drivers/misc/cros_ec_spi.c
@@ -98,7 +98,7 @@ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,
}
out = dev->dout;
- out[0] = cmd_version;
+ out[0] = EC_CMD_VERSION0 + cmd_version;
out[1] = cmd;
out[2] = (uint8_t)dout_len;
memcpy(out + 3, dout, dout_len);
@@ -165,7 +165,7 @@ int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob)
*/
int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob)
{
- dev->spi = spi_setup_slave_fdt(blob, dev->parent_node, dev->node);
+ dev->spi = spi_setup_slave_fdt(blob, dev->node, dev->parent_node);
if (!dev->spi) {
debug("%s: Could not setup SPI slave\n", __func__);
return -1;
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 34febf52f0..464cee16d1 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
obj-$(CONFIG_SDHCI) += sdhci.o
obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o
obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o
+obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o
@@ -37,3 +38,4 @@ obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
obj-$(CONFIG_GENERIC_MMC) += mmc_write.o
endif
+obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o
diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c
index 54cfabfb91..82079d67cd 100644
--- a/drivers/mmc/bcm2835_sdhci.c
+++ b/drivers/mmc/bcm2835_sdhci.c
@@ -179,7 +179,7 @@ int bcm2835_sdhci_init(u32 regbase, u32 emmc_freq)
host->name = "bcm2835_sdhci";
host->ioaddr = (void *)regbase;
host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B |
- SDHCI_QUIRK_WAIT_SEND_CMD;
+ SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_NO_HISPD_BIT;
host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
host->ops = &bcm2835_ops;
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 5bf36a0309..0df30bc045 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -245,7 +245,10 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
return -EINVAL;
}
- div = DIV_ROUND_UP(sclk, 2 * freq);
+ if (sclk == freq)
+ div = 0; /* bypass mode */
+ else
+ div = DIV_ROUND_UP(sclk, 2 * freq);
dwmci_writel(host, DWMCI_CLKENA, 0);
dwmci_writel(host, DWMCI_CLKSRC, 0);
diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c
index a57a9b1faf..45bcffb6b2 100644
--- a/drivers/mmc/gen_atmel_mci.c
+++ b/drivers/mmc/gen_atmel_mci.c
@@ -58,30 +58,61 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen)
atmel_mci_t *mci = mmc->priv;
u32 bus_hz = get_mci_clk_rate();
u32 clkdiv = 255;
+ unsigned int version = atmel_mci_get_version(mci);
+ u32 clkodd = 0;
+ u32 mr;
debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n",
bus_hz, hz, blklen);
if (hz > 0) {
- /* find lowest clkdiv yielding a rate <= than requested */
- for (clkdiv=0; clkdiv<255; clkdiv++) {
- if ((bus_hz / (clkdiv+1) / 2) <= hz)
- break;
+ if (version >= 0x500) {
+ clkdiv = DIV_ROUND_UP(bus_hz, hz) - 2;
+ if (clkdiv > 511)
+ clkdiv = 511;
+
+ clkodd = clkdiv & 1;
+ clkdiv >>= 1;
+
+ printf("mci: setting clock %u Hz, block size %u\n",
+ bus_hz / (clkdiv * 2 + clkodd + 2), blklen);
+ } else {
+ /* find clkdiv yielding a rate <= than requested */
+ for (clkdiv = 0; clkdiv < 255; clkdiv++) {
+ if ((bus_hz / (clkdiv + 1) / 2) <= hz)
+ break;
+ }
+ printf("mci: setting clock %u Hz, block size %u\n",
+ (bus_hz / (clkdiv + 1)) / 2, blklen);
+
}
}
- printf("mci: setting clock %u Hz, block size %u\n",
- (bus_hz / (clkdiv+1)) / 2, blklen);
blklen &= 0xfffc;
- /* On some platforms RDPROOF and WRPROOF are ignored */
- writel((MMCI_BF(CLKDIV, clkdiv)
- | MMCI_BF(BLKLEN, blklen)
- | MMCI_BIT(RDPROOF)
- | MMCI_BIT(WRPROOF)), &mci->mr);
+
+ mr = MMCI_BF(CLKDIV, clkdiv);
+
+ /* MCI IP version >= 0x200 has R/WPROOF */
+ if (version >= 0x200)
+ mr |= MMCI_BIT(RDPROOF) | MMCI_BIT(WRPROOF);
+
/*
- * On some new platforms BLKLEN in mci->mr is ignored.
- * Should use the BLKLEN in the block register.
+ * MCI IP version >= 0x500 use bit 16 as clkodd.
+ * MCI IP version < 0x500 use upper 16 bits for blklen.
*/
- writel(MMCI_BF(BLKLEN, blklen), &mci->blkr);
+ if (version >= 0x500)
+ mr |= MMCI_BF(CLKODD, clkodd);
+ else
+ mr |= MMCI_BF(BLKLEN, blklen);
+
+ writel(mr, &mci->mr);
+
+ /* MCI IP version >= 0x200 has blkr */
+ if (version >= 0x200)
+ writel(MMCI_BF(BLKLEN, blklen), &mci->blkr);
+
+ if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS)
+ writel(MMCI_BIT(HSMODE), &mci->cfg);
+
initialized = 1;
}
@@ -376,8 +407,10 @@ int atmel_mci_init(void *regs)
/* need to be able to pass these in on a board by board basis */
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
version = atmel_mci_get_version(mci);
- if ((version & 0xf00) >= 0x300)
+ if ((version & 0xf00) >= 0x300) {
cfg->host_caps = MMC_MODE_8BIT;
+ cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
+ }
cfg->host_caps |= MMC_MODE_4BIT;
diff --git a/drivers/mmc/mvebu_mmc.c b/drivers/mmc/mvebu_mmc.c
new file mode 100644
index 0000000000..97591983d2
--- /dev/null
+++ b/drivers/mmc/mvebu_mmc.c
@@ -0,0 +1,361 @@
+/*
+ * Marvell MMC/SD/SDIO driver
+ *
+ * (C) Copyright 2012
+ * Marvell Semiconductor <www.marvell.com>
+ * Written-by: Maen Suleiman, Gerald Kerma
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <part.h>
+#include <mmc.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/kirkwood.h>
+#include <mvebu_mmc.h>
+
+#define DRIVER_NAME "MVEBU_MMC"
+
+static void mvebu_mmc_write(u32 offs, u32 val)
+{
+ writel(val, CONFIG_SYS_MMC_BASE + (offs));
+}
+
+static u32 mvebu_mmc_read(u32 offs)
+{
+ return readl(CONFIG_SYS_MMC_BASE + (offs));
+}
+
+static int mvebu_mmc_setup_data(struct mmc_data *data)
+{
+ u32 ctrl_reg;
+
+ debug("%s, data %s : blocks=%d blksz=%d\n", DRIVER_NAME,
+ (data->flags & MMC_DATA_READ) ? "read" : "write",
+ data->blocks, data->blocksize);
+
+ /* default to maximum timeout */
+ ctrl_reg = mvebu_mmc_read(SDIO_HOST_CTRL);
+ ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX);
+ mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg);
+
+ if (data->flags & MMC_DATA_READ) {
+ mvebu_mmc_write(SDIO_SYS_ADDR_LOW, (u32)data->dest & 0xffff);
+ mvebu_mmc_write(SDIO_SYS_ADDR_HI, (u32)data->dest >> 16);
+ } else {
+ mvebu_mmc_write(SDIO_SYS_ADDR_LOW, (u32)data->src & 0xffff);
+ mvebu_mmc_write(SDIO_SYS_ADDR_HI, (u32)data->src >> 16);
+ }
+
+ mvebu_mmc_write(SDIO_BLK_COUNT, data->blocks);
+ mvebu_mmc_write(SDIO_BLK_SIZE, data->blocksize);
+
+ return 0;
+}
+
+static int mvebu_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ int timeout = 10;
+ ushort waittype = 0;
+ ushort resptype = 0;
+ ushort xfertype = 0;
+ ushort resp_indx = 0;
+
+ debug("cmdidx [0x%x] resp_type[0x%x] cmdarg[0x%x]\n",
+ cmd->cmdidx, cmd->resp_type, cmd->cmdarg);
+
+ udelay(10*1000);
+
+ debug("%s: cmd %d (hw state 0x%04x)\n", DRIVER_NAME,
+ cmd->cmdidx, mvebu_mmc_read(SDIO_HW_STATE));
+
+ /* Checking if card is busy */
+ while ((mvebu_mmc_read(SDIO_HW_STATE) & CARD_BUSY)) {
+ if (timeout == 0) {
+ printf("%s: card busy!\n", DRIVER_NAME);
+ return -1;
+ }
+ timeout--;
+ udelay(1000);
+ }
+
+ /* Set up for a data transfer if we have one */
+ if (data) {
+ int err = mvebu_mmc_setup_data(data);
+
+ if (err)
+ return err;
+ }
+
+ resptype = SDIO_CMD_INDEX(cmd->cmdidx);
+
+ /* Analyzing resptype/xfertype/waittype for the command */
+ if (cmd->resp_type & MMC_RSP_BUSY)
+ resptype |= SDIO_CMD_RSP_48BUSY;
+ else if (cmd->resp_type & MMC_RSP_136)
+ resptype |= SDIO_CMD_RSP_136;
+ else if (cmd->resp_type & MMC_RSP_PRESENT)
+ resptype |= SDIO_CMD_RSP_48;
+ else
+ resptype |= SDIO_CMD_RSP_NONE;
+
+ if (cmd->resp_type & MMC_RSP_CRC)
+ resptype |= SDIO_CMD_CHECK_CMDCRC;
+
+ if (cmd->resp_type & MMC_RSP_OPCODE)
+ resptype |= SDIO_CMD_INDX_CHECK;
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ resptype |= SDIO_UNEXPECTED_RESP;
+ waittype |= SDIO_NOR_UNEXP_RSP;
+ }
+
+ if (data) {
+ resptype |= SDIO_CMD_DATA_PRESENT | SDIO_CMD_CHECK_DATACRC16;
+ xfertype |= SDIO_XFER_MODE_HW_WR_DATA_EN;
+ if (data->flags & MMC_DATA_READ) {
+ xfertype |= SDIO_XFER_MODE_TO_HOST;
+ waittype = SDIO_NOR_DMA_INI;
+ } else {
+ waittype |= SDIO_NOR_XFER_DONE;
+ }
+ } else {
+ waittype |= SDIO_NOR_CMD_DONE;
+ }
+
+ /* Setting cmd arguments */
+ mvebu_mmc_write(SDIO_ARG_LOW, cmd->cmdarg & 0xffff);
+ mvebu_mmc_write(SDIO_ARG_HI, cmd->cmdarg >> 16);
+
+ /* Setting Xfer mode */
+ mvebu_mmc_write(SDIO_XFER_MODE, xfertype);
+
+ mvebu_mmc_write(SDIO_NOR_INTR_STATUS, ~SDIO_NOR_CARD_INT);
+ mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
+
+ /* Sending command */
+ mvebu_mmc_write(SDIO_CMD, resptype);
+
+ mvebu_mmc_write(SDIO_NOR_INTR_EN, SDIO_POLL_MASK);
+ mvebu_mmc_write(SDIO_ERR_INTR_EN, SDIO_POLL_MASK);
+
+ /* Waiting for completion */
+ timeout = 1000000;
+
+ while (!((mvebu_mmc_read(SDIO_NOR_INTR_STATUS)) & waittype)) {
+ if (mvebu_mmc_read(SDIO_NOR_INTR_STATUS) & SDIO_NOR_ERROR) {
+ debug("%s: error! cmdidx : %d, err reg: %04x\n",
+ DRIVER_NAME, cmd->cmdidx,
+ mvebu_mmc_read(SDIO_ERR_INTR_STATUS));
+ if (mvebu_mmc_read(SDIO_ERR_INTR_STATUS) &
+ (SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT))
+ return TIMEOUT;
+ return COMM_ERR;
+ }
+
+ timeout--;
+ udelay(1);
+ if (timeout <= 0) {
+ printf("%s: command timed out\n", DRIVER_NAME);
+ return TIMEOUT;
+ }
+ }
+
+ /* Handling response */
+ if (cmd->resp_type & MMC_RSP_136) {
+ uint response[8];
+
+ for (resp_indx = 0; resp_indx < 8; resp_indx++)
+ response[resp_indx]
+ = mvebu_mmc_read(SDIO_RSP(resp_indx));
+
+ cmd->response[0] = ((response[0] & 0x03ff) << 22) |
+ ((response[1] & 0xffff) << 6) |
+ ((response[2] & 0xfc00) >> 10);
+ cmd->response[1] = ((response[2] & 0x03ff) << 22) |
+ ((response[3] & 0xffff) << 6) |
+ ((response[4] & 0xfc00) >> 10);
+ cmd->response[2] = ((response[4] & 0x03ff) << 22) |
+ ((response[5] & 0xffff) << 6) |
+ ((response[6] & 0xfc00) >> 10);
+ cmd->response[3] = ((response[6] & 0x03ff) << 22) |
+ ((response[7] & 0x3fff) << 8);
+ } else if (cmd->resp_type & MMC_RSP_PRESENT) {
+ uint response[3];
+
+ for (resp_indx = 0; resp_indx < 3; resp_indx++)
+ response[resp_indx]
+ = mvebu_mmc_read(SDIO_RSP(resp_indx));
+
+ cmd->response[0] = ((response[2] & 0x003f) << (8 - 8)) |
+ ((response[1] & 0xffff) << (14 - 8)) |
+ ((response[0] & 0x03ff) << (30 - 8));
+ cmd->response[1] = ((response[0] & 0xfc00) >> 10);
+ cmd->response[2] = 0;
+ cmd->response[3] = 0;
+ }
+
+ debug("%s: resp[0x%x] ", DRIVER_NAME, cmd->resp_type);
+ debug("[0x%x] ", cmd->response[0]);
+ debug("[0x%x] ", cmd->response[1]);
+ debug("[0x%x] ", cmd->response[2]);
+ debug("[0x%x] ", cmd->response[3]);
+ debug("\n");
+
+ return 0;
+}
+
+static void mvebu_mmc_power_up(void)
+{
+ debug("%s: power up\n", DRIVER_NAME);
+
+ /* disable interrupts */
+ mvebu_mmc_write(SDIO_NOR_INTR_EN, 0);
+ mvebu_mmc_write(SDIO_ERR_INTR_EN, 0);
+
+ /* SW reset */
+ mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
+
+ mvebu_mmc_write(SDIO_XFER_MODE, 0);
+
+ /* enable status */
+ mvebu_mmc_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK);
+ mvebu_mmc_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK);
+
+ /* enable interrupts status */
+ mvebu_mmc_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK);
+ mvebu_mmc_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
+}
+
+static void mvebu_mmc_set_clk(unsigned int clock)
+{
+ unsigned int m;
+
+ if (clock == 0) {
+ debug("%s: clock off\n", DRIVER_NAME);
+ mvebu_mmc_write(SDIO_XFER_MODE, SDIO_XFER_MODE_STOP_CLK);
+ mvebu_mmc_write(SDIO_CLK_DIV, MVEBU_MMC_BASE_DIV_MAX);
+ } else {
+ m = MVEBU_MMC_BASE_FAST_CLOCK/(2*clock) - 1;
+ if (m > MVEBU_MMC_BASE_DIV_MAX)
+ m = MVEBU_MMC_BASE_DIV_MAX;
+ mvebu_mmc_write(SDIO_CLK_DIV, m & MVEBU_MMC_BASE_DIV_MAX);
+ }
+
+ udelay(10*1000);
+}
+
+static void mvebu_mmc_set_bus(unsigned int bus)
+{
+ u32 ctrl_reg = 0;
+
+ ctrl_reg = mvebu_mmc_read(SDIO_HOST_CTRL);
+ ctrl_reg &= ~SDIO_HOST_CTRL_DATA_WIDTH_4_BITS;
+
+ switch (bus) {
+ case 4:
+ ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_4_BITS;
+ break;
+ case 1:
+ default:
+ ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_1_BIT;
+ }
+
+ /* default transfer mode */
+ ctrl_reg |= SDIO_HOST_CTRL_BIG_ENDIAN;
+ ctrl_reg &= ~SDIO_HOST_CTRL_LSB_FIRST;
+
+ /* default to maximum timeout */
+ ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX);
+
+ ctrl_reg |= SDIO_HOST_CTRL_PUSH_PULL_EN;
+
+ ctrl_reg |= SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY;
+
+ debug("%s: ctrl 0x%04x: %s %s %s\n", DRIVER_NAME, ctrl_reg,
+ (ctrl_reg & SDIO_HOST_CTRL_PUSH_PULL_EN) ?
+ "push-pull" : "open-drain",
+ (ctrl_reg & SDIO_HOST_CTRL_DATA_WIDTH_4_BITS) ?
+ "4bit-width" : "1bit-width",
+ (ctrl_reg & SDIO_HOST_CTRL_HI_SPEED_EN) ?
+ "high-speed" : "");
+
+ mvebu_mmc_write(SDIO_HOST_CTRL, ctrl_reg);
+ udelay(10*1000);
+}
+
+static void mvebu_mmc_set_ios(struct mmc *mmc)
+{
+ debug("%s: bus[%d] clock[%d]\n", DRIVER_NAME,
+ mmc->bus_width, mmc->clock);
+ mvebu_mmc_set_bus(mmc->bus_width);
+ mvebu_mmc_set_clk(mmc->clock);
+}
+
+static int mvebu_mmc_initialize(struct mmc *mmc)
+{
+ debug("%s: mvebu_mmc_initialize", DRIVER_NAME);
+
+ /*
+ * Setting host parameters
+ * Initial Host Ctrl : Timeout : max , Normal Speed mode,
+ * 4-bit data mode, Big Endian, SD memory Card, Push_pull CMD Line
+ */
+ mvebu_mmc_write(SDIO_HOST_CTRL,
+ SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX) |
+ SDIO_HOST_CTRL_DATA_WIDTH_4_BITS |
+ SDIO_HOST_CTRL_BIG_ENDIAN |
+ SDIO_HOST_CTRL_PUSH_PULL_EN |
+ SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY);
+
+ mvebu_mmc_write(SDIO_CLK_CTRL, 0);
+
+ /* enable status */
+ mvebu_mmc_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK);
+ mvebu_mmc_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK);
+
+ /* disable interrupts */
+ mvebu_mmc_write(SDIO_NOR_INTR_EN, 0);
+ mvebu_mmc_write(SDIO_ERR_INTR_EN, 0);
+
+ /* SW reset */
+ mvebu_mmc_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
+
+ udelay(10*1000);
+
+ return 0;
+}
+
+static const struct mmc_ops mvebu_mmc_ops = {
+ .send_cmd = mvebu_mmc_send_cmd,
+ .set_ios = mvebu_mmc_set_ios,
+ .init = mvebu_mmc_initialize,
+};
+
+static struct mmc_config mvebu_mmc_cfg = {
+ .name = DRIVER_NAME,
+ .ops = &mvebu_mmc_ops,
+ .f_min = MVEBU_MMC_BASE_FAST_CLOCK / MVEBU_MMC_BASE_DIV_MAX,
+ .f_max = MVEBU_MMC_CLOCKRATE_MAX,
+ .voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
+ .host_caps = MMC_MODE_4BIT | MMC_MODE_HS,
+ .part_type = PART_TYPE_DOS,
+ .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
+};
+
+int mvebu_mmc_init(bd_t *bis)
+{
+ struct mmc *mmc;
+
+ mvebu_mmc_power_up();
+
+ mmc = mmc_create(&mvebu_mmc_cfg, bis);
+ if (mmc == NULL)
+ return -1;
+
+ return 0;
+}
diff --git a/drivers/mmc/s3c_sdi.c b/drivers/mmc/s3c_sdi.c
new file mode 100644
index 0000000000..1b5b70512d
--- /dev/null
+++ b/drivers/mmc/s3c_sdi.c
@@ -0,0 +1,321 @@
+/*
+ * S3C24xx SD/MMC driver
+ *
+ * Based on OpenMoko S3C24xx driver by Harald Welte <laforge@openmoko.org>
+ *
+ * Copyright (C) 2014 Marek Vasut <marex@denx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <errno.h>
+#include <asm/arch/s3c24x0_cpu.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+
+#define S3C2440_SDICON_SDRESET (1 << 8)
+#define S3C2410_SDICON_FIFORESET (1 << 1)
+#define S3C2410_SDICON_CLOCKTYPE (1 << 0)
+
+#define S3C2410_SDICMDCON_LONGRSP (1 << 10)
+#define S3C2410_SDICMDCON_WAITRSP (1 << 9)
+#define S3C2410_SDICMDCON_CMDSTART (1 << 8)
+#define S3C2410_SDICMDCON_SENDERHOST (1 << 6)
+#define S3C2410_SDICMDCON_INDEX 0x3f
+
+#define S3C2410_SDICMDSTAT_CRCFAIL (1 << 12)
+#define S3C2410_SDICMDSTAT_CMDSENT (1 << 11)
+#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1 << 10)
+#define S3C2410_SDICMDSTAT_RSPFIN (1 << 9)
+
+#define S3C2440_SDIDCON_DS_WORD (2 << 22)
+#define S3C2410_SDIDCON_TXAFTERRESP (1 << 20)
+#define S3C2410_SDIDCON_RXAFTERCMD (1 << 19)
+#define S3C2410_SDIDCON_BLOCKMODE (1 << 17)
+#define S3C2410_SDIDCON_WIDEBUS (1 << 16)
+#define S3C2440_SDIDCON_DATSTART (1 << 14)
+#define S3C2410_SDIDCON_XFER_RXSTART (2 << 12)
+#define S3C2410_SDIDCON_XFER_TXSTART (3 << 12)
+#define S3C2410_SDIDCON_BLKNUM 0x7ff
+
+#define S3C2410_SDIDSTA_FIFOFAIL (1 << 8)
+#define S3C2410_SDIDSTA_CRCFAIL (1 << 7)
+#define S3C2410_SDIDSTA_RXCRCFAIL (1 << 6)
+#define S3C2410_SDIDSTA_DATATIMEOUT (1 << 5)
+#define S3C2410_SDIDSTA_XFERFINISH (1 << 4)
+
+#define S3C2410_SDIFSTA_TFHALF (1 << 11)
+#define S3C2410_SDIFSTA_COUNTMASK 0x7f
+
+/*
+ * WARNING: We only support one SD IP block.
+ * NOTE: It's not likely there will ever exist an S3C24xx with two,
+ * at least not in this universe all right.
+ */
+static int wide_bus;
+
+static int
+s3cmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+ struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
+ uint32_t sdiccon, sdicsta, sdidcon, sdidsta, sdidat, sdifsta;
+ uint32_t sdicsta_wait_bit = S3C2410_SDICMDSTAT_CMDSENT;
+ unsigned int timeout = 100000;
+ int ret = 0, xfer_len, data_offset = 0;
+ const uint32_t sdidsta_err_mask = S3C2410_SDIDSTA_FIFOFAIL |
+ S3C2410_SDIDSTA_CRCFAIL | S3C2410_SDIDSTA_RXCRCFAIL |
+ S3C2410_SDIDSTA_DATATIMEOUT;
+
+
+ writel(0xffffffff, &sdi_regs->sdicsta);
+ writel(0xffffffff, &sdi_regs->sdidsta);
+ writel(0xffffffff, &sdi_regs->sdifsta);
+
+ /* Set up data transfer (if applicable). */
+ if (data) {
+ writel(data->blocksize, &sdi_regs->sdibsize);
+
+ sdidcon = data->blocks & S3C2410_SDIDCON_BLKNUM;
+ sdidcon |= S3C2410_SDIDCON_BLOCKMODE;
+#if defined(CONFIG_S3C2440)
+ sdidcon |= S3C2440_SDIDCON_DS_WORD | S3C2440_SDIDCON_DATSTART;
+#endif
+ if (wide_bus)
+ sdidcon |= S3C2410_SDIDCON_WIDEBUS;
+
+ if (data->flags & MMC_DATA_READ) {
+ sdidcon |= S3C2410_SDIDCON_RXAFTERCMD;
+ sdidcon |= S3C2410_SDIDCON_XFER_RXSTART;
+ } else {
+ sdidcon |= S3C2410_SDIDCON_TXAFTERRESP;
+ sdidcon |= S3C2410_SDIDCON_XFER_TXSTART;
+ }
+
+ writel(sdidcon, &sdi_regs->sdidcon);
+ }
+
+ /* Write CMD arg. */
+ writel(cmd->cmdarg, &sdi_regs->sdicarg);
+
+ /* Write CMD index. */
+ sdiccon = cmd->cmdidx & S3C2410_SDICMDCON_INDEX;
+ sdiccon |= S3C2410_SDICMDCON_SENDERHOST;
+ sdiccon |= S3C2410_SDICMDCON_CMDSTART;
+
+ /* Command with short response. */
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ sdiccon |= S3C2410_SDICMDCON_WAITRSP;
+ sdicsta_wait_bit = S3C2410_SDICMDSTAT_RSPFIN;
+ }
+
+ /* Command with long response. */
+ if (cmd->resp_type & MMC_RSP_136)
+ sdiccon |= S3C2410_SDICMDCON_LONGRSP;
+
+ /* Start the command. */
+ writel(sdiccon, &sdi_regs->sdiccon);
+
+ /* Wait for the command to complete or for response. */
+ for (timeout = 100000; timeout; timeout--) {
+ sdicsta = readl(&sdi_regs->sdicsta);
+ if (sdicsta & sdicsta_wait_bit)
+ break;
+
+ if (sdicsta & S3C2410_SDICMDSTAT_CMDTIMEOUT)
+ timeout = 1;
+ }
+
+ /* Clean the status bits. */
+ setbits_le32(&sdi_regs->sdicsta, 0xf << 9);
+
+ if (!timeout) {
+ puts("S3C SDI: Command timed out!\n");
+ ret = TIMEOUT;
+ goto error;
+ }
+
+ /* Read out the response. */
+ if (cmd->resp_type & MMC_RSP_136) {
+ cmd->response[0] = readl(&sdi_regs->sdirsp0);
+ cmd->response[1] = readl(&sdi_regs->sdirsp1);
+ cmd->response[2] = readl(&sdi_regs->sdirsp2);
+ cmd->response[3] = readl(&sdi_regs->sdirsp3);
+ } else {
+ cmd->response[0] = readl(&sdi_regs->sdirsp0);
+ }
+
+ /* If there are no data, we're done. */
+ if (!data)
+ return 0;
+
+ xfer_len = data->blocksize * data->blocks;
+
+ while (xfer_len > 0) {
+ sdidsta = readl(&sdi_regs->sdidsta);
+ sdifsta = readl(&sdi_regs->sdifsta);
+
+ if (sdidsta & sdidsta_err_mask) {
+ printf("S3C SDI: Data error (sdta=0x%08x)\n", sdidsta);
+ ret = -EIO;
+ goto error;
+ }
+
+ if (data->flags & MMC_DATA_READ) {
+ if ((sdifsta & S3C2410_SDIFSTA_COUNTMASK) < 4)
+ continue;
+ sdidat = readl(&sdi_regs->sdidat);
+ put_unaligned_le32(sdidat, data->dest + data_offset);
+ } else { /* Write */
+ /* TX FIFO half full. */
+ if (!(sdifsta & S3C2410_SDIFSTA_TFHALF))
+ continue;
+
+ /* TX FIFO is below 32b full, write. */
+ sdidat = get_unaligned_le32(data->src + data_offset);
+ writel(sdidat, &sdi_regs->sdidat);
+ }
+ data_offset += 4;
+ xfer_len -= 4;
+ }
+
+ /* Wait for the command to complete or for response. */
+ for (timeout = 100000; timeout; timeout--) {
+ sdidsta = readl(&sdi_regs->sdidsta);
+ if (sdidsta & S3C2410_SDIDSTA_XFERFINISH)
+ break;
+
+ if (sdidsta & S3C2410_SDIDSTA_DATATIMEOUT)
+ timeout = 1;
+ }
+
+ /* Clear status bits. */
+ writel(0x6f8, &sdi_regs->sdidsta);
+
+ if (!timeout) {
+ puts("S3C SDI: Command timed out!\n");
+ ret = TIMEOUT;
+ goto error;
+ }
+
+ writel(0, &sdi_regs->sdidcon);
+
+ return 0;
+error:
+ return ret;
+}
+
+static void s3cmmc_set_ios(struct mmc *mmc)
+{
+ struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
+ uint32_t divider = 0;
+
+ wide_bus = (mmc->bus_width == 4);
+
+ if (!mmc->clock)
+ return;
+
+ divider = DIV_ROUND_UP(get_PCLK(), mmc->clock);
+ if (divider)
+ divider--;
+
+ writel(divider, &sdi_regs->sdipre);
+ mdelay(125);
+}
+
+static int s3cmmc_init(struct mmc *mmc)
+{
+ struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
+ struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi();
+
+ /* Start the clock. */
+ setbits_le32(&clk_power->clkcon, 1 << 9);
+
+#if defined(CONFIG_S3C2440)
+ writel(S3C2440_SDICON_SDRESET, &sdi_regs->sdicon);
+ mdelay(10);
+ writel(0x7fffff, &sdi_regs->sdidtimer);
+#else
+ writel(0xffff, &sdi_regs->sdidtimer);
+#endif
+ writel(MMC_MAX_BLOCK_LEN, &sdi_regs->sdibsize);
+ writel(0x0, &sdi_regs->sdiimsk);
+
+ writel(S3C2410_SDICON_FIFORESET | S3C2410_SDICON_CLOCKTYPE,
+ &sdi_regs->sdicon);
+
+ mdelay(125);
+
+ return 0;
+}
+
+struct s3cmmc_priv {
+ struct mmc_config cfg;
+ int (*getcd)(struct mmc *);
+ int (*getwp)(struct mmc *);
+};
+
+static int s3cmmc_getcd(struct mmc *mmc)
+{
+ struct s3cmmc_priv *priv = mmc->priv;
+ if (priv->getcd)
+ return priv->getcd(mmc);
+ else
+ return 0;
+}
+
+static int s3cmmc_getwp(struct mmc *mmc)
+{
+ struct s3cmmc_priv *priv = mmc->priv;
+ if (priv->getwp)
+ return priv->getwp(mmc);
+ else
+ return 0;
+}
+
+static const struct mmc_ops s3cmmc_ops = {
+ .send_cmd = s3cmmc_send_cmd,
+ .set_ios = s3cmmc_set_ios,
+ .init = s3cmmc_init,
+ .getcd = s3cmmc_getcd,
+ .getwp = s3cmmc_getwp,
+};
+
+int s3cmmc_initialize(bd_t *bis, int (*getcd)(struct mmc *),
+ int (*getwp)(struct mmc *))
+{
+ struct s3cmmc_priv *priv;
+ struct mmc *mmc;
+ struct mmc_config *cfg;
+
+ priv = calloc(1, sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+ cfg = &priv->cfg;
+
+ cfg->name = "S3C MMC";
+ cfg->ops = &s3cmmc_ops;
+ cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+ cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_HC | MMC_MODE_HS;
+ cfg->f_min = 400000;
+ cfg->f_max = get_PCLK() / 2;
+ cfg->b_max = 0x80;
+
+#if defined(CONFIG_S3C2410)
+ /*
+ * S3C2410 has some bug that prevents reliable
+ * operation at higher speed
+ */
+ cfg->f_max /= 2;
+#endif
+
+ mmc = mmc_create(cfg, priv);
+ if (!mmc) {
+ free(priv);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c
index ef91b924d7..85cf22d42e 100644
--- a/drivers/mtd/spi/sf_ops.c
+++ b/drivers/mtd/spi/sf_ops.c
@@ -421,6 +421,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
data += read_len;
}
+ free(cmd);
return ret;
}
diff --git a/drivers/net/fm/memac_phy.c b/drivers/net/fm/memac_phy.c
index de9c0e9cd2..5f910c2cdd 100644
--- a/drivers/net/fm/memac_phy.c
+++ b/drivers/net/fm/memac_phy.c
@@ -1,6 +1,6 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
- * Andy Fleming <afleming@freescale.com>
+ * Andy Fleming <afleming@gmail.com>
* Roy Zang <tie-fei.zang@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
diff --git a/drivers/net/fm/tgec_phy.c b/drivers/net/fm/tgec_phy.c
index faec317a0a..095f00cf97 100644
--- a/drivers/net/fm/tgec_phy.c
+++ b/drivers/net/fm/tgec_phy.c
@@ -1,6 +1,6 @@
/*
* Copyright 2009-2011 Freescale Semiconductor, Inc.
- * Andy Fleming <afleming@freescale.com>
+ * Andy Fleming <afleming@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
* Some part is taken from tsec.c
diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c
index c92276fdf7..2969184ee8 100644
--- a/drivers/spi/exynos_spi.c
+++ b/drivers/spi/exynos_spi.c
@@ -428,10 +428,6 @@ void spi_cs_activate(struct spi_slave *slave)
clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
debug("Activate CS, bus %d\n", spi_slave->slave.bus);
spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE;
-
- /* Remember time of this transaction so we can honour the bus delay */
- if (spi_slave->bus->deactivate_delay_us)
- spi_slave->last_transaction_us = timer_get_us();
}
/**
@@ -445,6 +441,11 @@ void spi_cs_deactivate(struct spi_slave *slave)
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
+
+ /* Remember time of this transaction so we can honour the bus delay */
+ if (spi_slave->bus->deactivate_delay_us)
+ spi_slave->last_transaction_us = timer_get_us();
+
debug("Deactivate CS, bus %d\n", spi_slave->slave.bus);
}
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index f3f029d634..2d5f3850da 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -30,6 +30,10 @@ static unsigned long spi_bases[] = {
#define reg_read readl
#define reg_write(a, v) writel(v, a)
+#if !defined(CONFIG_SYS_SPI_MXC_WAIT)
+#define CONFIG_SYS_SPI_MXC_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */
+#endif
+
struct mxc_spi_slave {
struct spi_slave slave;
unsigned long base;
@@ -212,6 +216,8 @@ int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen,
int nbytes = DIV_ROUND_UP(bitlen, 8);
u32 data, cnt, i;
struct cspi_regs *regs = (struct cspi_regs *)mxcs->base;
+ u32 ts;
+ int status;
debug("%s: bitlen %d dout 0x%x din 0x%x\n",
__func__, bitlen, (u32)dout, (u32)din);
@@ -272,9 +278,16 @@ int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen,
reg_write(&regs->ctrl, mxcs->ctrl_reg |
MXC_CSPICTRL_EN | MXC_CSPICTRL_XCH);
+ ts = get_timer(0);
+ status = reg_read(&regs->stat);
/* Wait until the TC (Transfer completed) bit is set */
- while ((reg_read(&regs->stat) & MXC_CSPICTRL_TC) == 0)
- ;
+ while ((status & MXC_CSPICTRL_TC) == 0) {
+ if (get_timer(ts) > CONFIG_SYS_SPI_MXC_WAIT) {
+ printf("spi_xchg_single: Timeout!\n");
+ return -1;
+ }
+ status = reg_read(&regs->stat);
+ }
/* Transfer completed, clear any pending request */
reg_write(&regs->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 7ddea9b026..7d81fbd7f8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -53,6 +53,8 @@ struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum,
mode |= SPI_CPHA;
if (fdtdec_get_bool(blob, node, "spi-cs-high"))
mode |= SPI_CS_HIGH;
+ if (fdtdec_get_bool(blob, node, "spi-half-duplex"))
+ mode |= SPI_PREAMBLE;
return spi_setup_slave(busnum, cs, max_hz, mode);
}
#endif