+// SPDX-License-Identifier: GPL-2.0+
/*
* Davinci MMC Controller Driver
*
* Copyright (C) 2010 Texas Instruments Incorporated
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include <common.h>
-#include <command.h>
+#include <dm.h>
+#include <errno.h>
#include <mmc.h>
+#include <command.h>
#include <part.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/arch/sdmmc_defs.h>
+#include <asm-generic/gpio.h>
+#include <linux/delay.h>
#define DAVINCI_MAX_BLOCKS (32)
#define WATCHDOG_COUNT (100000)
#define set_bit(addr, val) set_val((addr), (get_val(addr) | (val)))
#define clear_bit(addr, val) set_val((addr), (get_val(addr) & ~(val)))
+#ifdef CONFIG_DM_MMC
+/* Davinci MMC board definitions */
+struct davinci_mmc_priv {
+ struct davinci_mmc_regs *reg_base; /* Register base address */
+ uint input_clk; /* Input clock to MMC controller */
+ struct gpio_desc cd_gpio; /* Card Detect GPIO */
+ struct gpio_desc wp_gpio; /* Write Protect GPIO */
+};
+
+struct davinci_mmc_plat
+{
+ struct mmc_config cfg;
+ struct mmc mmc;
+};
+#endif
+
/* Set davinci clock prescalar value based on the required clock in HZ */
+#if !CONFIG_IS_ENABLED(DM_MMC)
static void dmmc_set_clock(struct mmc *mmc, uint clock)
{
struct davinci_mmc *host = mmc->priv;
+#else
+
+static void davinci_mmc_set_clock(struct udevice *dev, uint clock)
+{
+ struct davinci_mmc_priv *host = dev_get_priv(dev);
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+#endif
struct davinci_mmc_regs *regs = host->reg_base;
uint clkrt, sysclk2, act_clock;
- if (clock < mmc->f_min)
- clock = mmc->f_min;
- if (clock > mmc->f_max)
- clock = mmc->f_max;
+ if (clock < mmc->cfg->f_min)
+ clock = mmc->cfg->f_min;
+ if (clock > mmc->cfg->f_max)
+ clock = mmc->cfg->f_max;
set_val(®s->mmcclk, 0);
sysclk2 = host->input_clk;
udelay(100);
if (wdog == 0)
- return COMM_ERR;
+ return -ECOMM;
return 0;
}
udelay(10);
if (wdog == 0)
- return COMM_ERR;
+ return -ECOMM;
return 0;
}
return 0;
} else if (mmcstatus & st_error) {
if (mmcstatus & MMCST0_TOUTRS)
- return TIMEOUT;
+ return -ETIMEDOUT;
printf("[ ST0 ERROR %x]\n", mmcstatus);
/*
* Ignore CRC errors as some MMC cards fail to
*/
if (mmcstatus & MMCST0_CRCRS)
return 0;
- return COMM_ERR;
+ return -ECOMM;
}
udelay(10);
printf("Status %x Timeout ST0:%x ST1:%x\n", st_ready, mmcstatus,
get_val(®s->mmcst1));
- return COMM_ERR;
+ return -ECOMM;
}
/*
- * Sends a command out on the bus. Takes the mmc pointer,
+ * Sends a command out on the bus. Takes the device pointer,
* a command pointer, and an optional data pointer.
*/
-static int
-dmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+#if !CONFIG_IS_ENABLED(DM_MMC)
+static int dmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
struct davinci_mmc *host = mmc->priv;
+#else
+static int
+davinci_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+ struct davinci_mmc_priv *host = dev_get_priv(dev);
+#endif
volatile struct davinci_mmc_regs *regs = host->reg_base;
uint mmcstatus, status_rdy, status_err;
uint i, cmddata, bytes_left = 0;
/* Clear status registers */
mmcstatus = get_val(®s->mmcst0);
- fifo_words = (host->version == MMC_CTLR_VERSION_2) ? 16 : 8;
+ fifo_words = 16;
fifo_bytes = fifo_words << 2;
/* Wait for any previous busy signal to be cleared */
set_val(®s->mmcfifoctl,
(MMCFIFOCTL_FIFOLEV | MMCFIFOCTL_FIFORST));
- if (host->version == MMC_CTLR_VERSION_2)
- cmddata |= MMCCMD_DMATRIG;
+ cmddata |= MMCCMD_DMATRIG;
cmddata |= MMCCMD_WDATX;
if (data->flags == MMC_DATA_READ) {
}
/* Initialize Davinci MMC controller */
+#if !CONFIG_IS_ENABLED(DM_MMC)
static int dmmc_init(struct mmc *mmc)
{
struct davinci_mmc *host = mmc->priv;
+#else
+static int davinci_dm_mmc_init(struct udevice *dev)
+{
+ struct davinci_mmc_priv *host = dev_get_priv(dev);
+#endif
struct davinci_mmc_regs *regs = host->reg_base;
/* Clear status registers explicitly - soft reset doesn't clear it
return 0;
}
-/* Set buswidth or clock as indicated by the GENERIC_MMC framework */
-static void dmmc_set_ios(struct mmc *mmc)
+/* Set buswidth or clock as indicated by the MMC framework */
+#if !CONFIG_IS_ENABLED(DM_MMC)
+static int dmmc_set_ios(struct mmc *mmc)
{
struct davinci_mmc *host = mmc->priv;
struct davinci_mmc_regs *regs = host->reg_base;
+#else
+static int davinci_mmc_set_ios(struct udevice *dev)
+{
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+ struct davinci_mmc_priv *host = dev_get_priv(dev);
+ struct davinci_mmc_regs *regs = host->reg_base;
+#endif
/* Set the bus width */
if (mmc->bus_width == 4)
set_bit(®s->mmcctl, MMCCTL_WIDTH_4_BIT);
clear_bit(®s->mmcctl, MMCCTL_WIDTH_4_BIT);
/* Set clock speed */
- if (mmc->clock)
+ if (mmc->clock) {
+#if !CONFIG_IS_ENABLED(DM_MMC)
dmmc_set_clock(mmc, mmc->clock);
+#else
+ davinci_mmc_set_clock(dev, mmc->clock);
+#endif
+ }
+ return 0;
}
+#if !CONFIG_IS_ENABLED(DM_MMC)
+static const struct mmc_ops dmmc_ops = {
+ .send_cmd = dmmc_send_cmd,
+ .set_ios = dmmc_set_ios,
+ .init = dmmc_init,
+};
+#else
+
+static int davinci_mmc_getcd(struct udevice *dev)
+{
+ int value = -1;
+#if CONFIG_IS_ENABLED(DM_GPIO)
+ struct davinci_mmc_priv *priv = dev_get_priv(dev);
+ value = dm_gpio_get_value(&priv->cd_gpio);
+#endif
+ /* if no CD return as 1 */
+ if (value < 0)
+ return 1;
+
+ return value;
+}
+
+static int davinci_mmc_getwp(struct udevice *dev)
+{
+ int value = -1;
+#if CONFIG_IS_ENABLED(DM_GPIO)
+ struct davinci_mmc_priv *priv = dev_get_priv(dev);
+
+ value = dm_gpio_get_value(&priv->wp_gpio);
+#endif
+ /* if no WP return as 0 */
+ if (value < 0)
+ return 0;
+
+ return value;
+}
+
+static const struct dm_mmc_ops davinci_mmc_ops = {
+ .send_cmd = davinci_mmc_send_cmd,
+ .set_ios = davinci_mmc_set_ios,
+ .get_cd = davinci_mmc_getcd,
+ .get_wp = davinci_mmc_getwp,
+};
+#endif
+
+#if !CONFIG_IS_ENABLED(DM_MMC)
/* Called from board_mmc_init during startup. Can be called multiple times
- * depending on the number of slots available on board and controller
- */
+* depending on the number of slots available on board and controller
+*/
int davinci_mmc_init(bd_t *bis, struct davinci_mmc *host)
{
- struct mmc *mmc;
+ host->cfg.name = "davinci";
+ host->cfg.ops = &dmmc_ops;
+ host->cfg.f_min = 200000;
+ host->cfg.f_max = 25000000;
+ host->cfg.voltages = host->voltages;
+ host->cfg.host_caps = host->host_caps;
- mmc = malloc(sizeof(struct mmc));
- memset(mmc, 0, sizeof(struct mmc));
+ host->cfg.b_max = DAVINCI_MAX_BLOCKS;
- sprintf(mmc->name, "davinci");
- mmc->priv = host;
- mmc->send_cmd = dmmc_send_cmd;
- mmc->set_ios = dmmc_set_ios;
- mmc->init = dmmc_init;
- mmc->getcd = NULL;
- mmc->getwp = NULL;
+ mmc_create(&host->cfg, host);
- mmc->f_min = 200000;
- mmc->f_max = 25000000;
- mmc->voltages = host->voltages;
- mmc->host_caps = host->host_caps;
+ return 0;
+}
+#else
- mmc->b_max = DAVINCI_MAX_BLOCKS;
- mmc_register(mmc);
+static int davinci_mmc_probe(struct udevice *dev)
+{
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct davinci_mmc_plat *plat = dev_get_platdata(dev);
+ struct davinci_mmc_priv *priv = dev_get_priv(dev);
+ struct mmc_config *cfg = &plat->cfg;
+#ifdef CONFIG_SPL_BUILD
+ int ret;
+#endif
+
+ cfg->f_min = 200000;
+ cfg->f_max = 25000000;
+ cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
+ cfg->host_caps = MMC_MODE_4BIT, /* DA850 supports only 4-bit SD/MMC */
+ cfg->b_max = DAVINCI_MAX_BLOCKS;
+ cfg->name = "da830-mmc";
+
+ priv->reg_base = (struct davinci_mmc_regs *)dev_read_addr(dev);
+ priv->input_clk = clk_get(DAVINCI_MMCSD_CLKID);
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+ /* These GPIOs are optional */
+ gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
+ gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN);
+#endif
+
+ upriv->mmc = &plat->mmc;
+
+#ifdef CONFIG_SPL_BUILD
+ /*
+ * FIXME This is a temporary workaround to enable the driver model in
+ * SPL on omapl138-lcdk. For some reason the bind() callback is not
+ * being called in SPL for MMC which breaks the mmc boot - the hack
+ * is to call mmc_bind() from probe(). We also don't have full DT
+ * support in SPL, hence the hard-coded base register address.
+ */
+ priv->reg_base = (struct davinci_mmc_regs *)DAVINCI_MMC_SD0_BASE;
+ ret = mmc_bind(dev, &plat->mmc, &plat->cfg);
+ if (ret)
+ return ret;
+#endif
- return 0;
+ return davinci_dm_mmc_init(dev);
}
+
+static int davinci_mmc_bind(struct udevice *dev)
+{
+ struct davinci_mmc_plat *plat = dev_get_platdata(dev);
+
+ return mmc_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+static const struct udevice_id davinci_mmc_ids[] = {
+ { .compatible = "ti,da830-mmc" },
+ {},
+};
+
+U_BOOT_DRIVER(davinci_mmc_drv) = {
+ .name = "davinci_mmc",
+ .id = UCLASS_MMC,
+ .of_match = davinci_mmc_ids,
+#if CONFIG_BLK
+ .bind = davinci_mmc_bind,
+#endif
+ .probe = davinci_mmc_probe,
+ .ops = &davinci_mmc_ops,
+ .platdata_auto_alloc_size = sizeof(struct davinci_mmc_plat),
+ .priv_auto_alloc_size = sizeof(struct davinci_mmc_priv),
+};
+#endif