Signed-off-by: Sascha Hauer <[email protected]>
---
 drivers/mci/Makefile      |   1 +
 drivers/mci/am654-sdhci.c | 680 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 681 insertions(+)
 create mode 100644 drivers/mci/am654-sdhci.c

diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index e3dc5ad8ae..b7fdddeaed 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_MCI_DW)          += dw_mmc.o
 obj-$(CONFIG_MCI_MMCI)         += mmci.o
 obj-$(CONFIG_MCI_STM32_SDMMC2) += stm32_sdmmc2.o
 obj-pbl-$(CONFIG_MCI_SDHCI)    += sdhci.o
+obj-y += am654-sdhci.o
diff --git a/drivers/mci/am654-sdhci.c b/drivers/mci/am654-sdhci.c
new file mode 100644
index 0000000000..1d94834c1a
--- /dev/null
+++ b/drivers/mci/am654-sdhci.c
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Texas Instruments' K3 SD Host Controller Interface
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <mci.h>
+#include <clock.h>
+#include <errno.h>
+#include <io.h>
+#include <regmap.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include "sdhci.h"
+
+/* CTL_CFG Registers */
+#define CTL_CFG_2              0x14
+
+#define SLOTTYPE_MASK          GENMASK(31, 30)
+#define SLOTTYPE_EMBEDDED      BIT(30)
+
+/* PHY Registers */
+#define PHY_CTRL1      0x100
+#define PHY_CTRL2      0x104
+#define PHY_CTRL3      0x108
+#define PHY_CTRL4      0x10C
+#define PHY_CTRL5      0x110
+#define PHY_CTRL6      0x114
+#define PHY_STAT1      0x130
+#define PHY_STAT2      0x134
+
+#define IOMUX_ENABLE_SHIFT     31
+#define IOMUX_ENABLE_MASK      BIT(IOMUX_ENABLE_SHIFT)
+#define OTAPDLYENA_SHIFT       20
+#define OTAPDLYENA_MASK                BIT(OTAPDLYENA_SHIFT)
+#define OTAPDLYSEL_SHIFT       12
+#define OTAPDLYSEL_MASK                GENMASK(15, 12)
+#define STRBSEL_SHIFT          24
+#define STRBSEL_4BIT_MASK      GENMASK(27, 24)
+#define STRBSEL_8BIT_MASK      GENMASK(31, 24)
+#define SEL50_SHIFT            8
+#define SEL50_MASK             BIT(SEL50_SHIFT)
+#define SEL100_SHIFT           9
+#define SEL100_MASK            BIT(SEL100_SHIFT)
+#define FREQSEL_SHIFT          8
+#define FREQSEL_MASK           GENMASK(10, 8)
+#define CLKBUFSEL_SHIFT                0
+#define CLKBUFSEL_MASK         GENMASK(2, 0)
+#define DLL_TRIM_ICP_SHIFT     4
+#define DLL_TRIM_ICP_MASK      GENMASK(7, 4)
+#define DR_TY_SHIFT            20
+#define DR_TY_MASK             GENMASK(22, 20)
+#define ENDLL_SHIFT            1
+#define ENDLL_MASK             BIT(ENDLL_SHIFT)
+#define DLLRDY_SHIFT           0
+#define DLLRDY_MASK            BIT(DLLRDY_SHIFT)
+#define PDB_SHIFT              0
+#define PDB_MASK               BIT(PDB_SHIFT)
+#define CALDONE_SHIFT          1
+#define CALDONE_MASK           BIT(CALDONE_SHIFT)
+#define RETRIM_SHIFT           17
+#define RETRIM_MASK            BIT(RETRIM_SHIFT)
+#define SELDLYTXCLK_SHIFT      17
+#define SELDLYTXCLK_MASK       BIT(SELDLYTXCLK_SHIFT)
+#define SELDLYRXCLK_SHIFT      16
+#define SELDLYRXCLK_MASK       BIT(SELDLYRXCLK_SHIFT)
+#define ITAPDLYSEL_SHIFT       0
+#define ITAPDLYSEL_MASK                GENMASK(4, 0)
+#define ITAPDLYENA_SHIFT       8
+#define ITAPDLYENA_MASK                BIT(ITAPDLYENA_SHIFT)
+#define ITAPCHGWIN_SHIFT       9
+#define ITAPCHGWIN_MASK                BIT(ITAPCHGWIN_SHIFT)
+
+#define DRIVER_STRENGTH_50_OHM 0x0
+#define DRIVER_STRENGTH_33_OHM 0x1
+#define DRIVER_STRENGTH_66_OHM 0x2
+#define DRIVER_STRENGTH_100_OHM        0x3
+#define DRIVER_STRENGTH_40_OHM 0x4
+
+#define AM654_SDHCI_MIN_FREQ   400000
+#define CLOCK_TOO_SLOW_HZ      50000000
+
+#define MMC_CAP2_HS200 0
+#define MMC_CAP2_HS400 0
+#define MMC_CAP_UHS_SDR104 0
+#define MMC_CAP_UHS_SDR12 0
+#define MMC_CAP_UHS_DDR50 0
+#define MMC_CAP_UHS_SDR25 0
+#define MMC_CAP_UHS_SDR50 0
+
+struct timing_data {
+       const char *otap_binding;
+       const char *itap_binding;
+       u32 capability;
+};
+
+static const struct timing_data td[] = {
+       [MMC_TIMING_LEGACY]     = {
+               "ti,otap-del-sel-legacy",
+               "ti,itap-del-sel-legacy",
+               0
+       },
+       [MMC_TIMING_MMC_HS]     = {
+               "ti,otap-del-sel-mmc-hs",
+               "ti,itap-del-sel-mms-hs",
+               MMC_CAP_MMC_HIGHSPEED
+       },
+       [MMC_TIMING_SD_HS]      = {
+               "ti,otap-del-sel-sd-hs",
+               "ti,itap-del-sel-sd-hs",
+               MMC_CAP_SD_HIGHSPEED
+       },
+       [MMC_TIMING_UHS_SDR12]  = {
+               "ti,otap-del-sel-sdr12",
+               "ti,itap-del-sel-sdr12",
+               MMC_CAP_UHS_SDR12
+       },
+       [MMC_TIMING_UHS_SDR25]  = {
+               "ti,otap-del-sel-sdr25",
+               "ti,itap-del-sel-sdr25",
+               MMC_CAP_UHS_SDR25
+       },
+       [MMC_TIMING_UHS_SDR50]  = {
+               "ti,otap-del-sel-sdr50",
+               NULL,
+               MMC_CAP_UHS_SDR50
+       },
+       [MMC_TIMING_UHS_SDR104] = {
+               "ti,otap-del-sel-sdr104",
+               NULL,
+               MMC_CAP_UHS_SDR104
+       },
+       [MMC_TIMING_UHS_DDR50]  = {
+               "ti,otap-del-sel-ddr50",
+               NULL,
+               MMC_CAP_UHS_DDR50
+       },
+       [MMC_TIMING_MMC_DDR52]  = {
+               "ti,otap-del-sel-ddr52",
+               "ti,itap-del-sel-ddr52",
+               MMC_CAP_DDR
+       },
+       [MMC_TIMING_MMC_HS200]  = {
+               "ti,otap-del-sel-hs200",
+               NULL,
+               MMC_CAP2_HS200
+       },
+       [MMC_TIMING_MMC_HS400]  = {
+               "ti,otap-del-sel-hs400",
+               NULL,
+               MMC_CAP2_HS400
+       },
+};
+
+struct am654_sdhci_plat {
+       struct sdhci sdhci;
+       struct mci_host mci;
+       struct clk *clk;
+       struct clk *clk_ahb;
+       const struct am654_driver_data *soc_data;
+       bool fails_without_test_cd;
+
+       struct regmap *base;
+       bool non_removable;
+       u32 otap_del_sel[ARRAY_SIZE(td)];
+       u32 itap_del_sel[ARRAY_SIZE(td)];
+       u32 trm_icp;
+       u32 drv_strength;
+       u32 strb_sel;
+       u32 clkbuf_sel;
+#define DLL_PRESENT    BIT(0)
+#define IOMUX_PRESENT  BIT(1)
+#define FREQSEL_2_BIT  BIT(2)
+#define STRBSEL_4_BIT  BIT(3)
+#define DLL_CALIB      BIT(4)
+};
+
+struct am654_driver_data {
+       int (*set_ios_post)(struct am654_sdhci_plat *plat, struct mci_ios *ios);
+       u32 flags;
+};
+
+static int am654_sdhci_setup_dll(struct am654_sdhci_plat *plat,
+                                unsigned int speed)
+{
+       int sel50, sel100, freqsel;
+       u32 mask, val;
+       int ret;
+
+       /* Disable delay chain mode */
+       regmap_update_bits(plat->base, PHY_CTRL5,
+                          SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
+
+       if (plat->soc_data->flags & FREQSEL_2_BIT) {
+               switch (speed) {
+               case 200000000:
+                       sel50 = 0;
+                       sel100 = 0;
+                       break;
+               case 100000000:
+                       sel50 = 0;
+                       sel100 = 1;
+                       break;
+               default:
+                       sel50 = 1;
+                       sel100 = 0;
+               }
+
+               /* Configure PHY DLL frequency */
+               mask = SEL50_MASK | SEL100_MASK;
+               val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
+               regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+       } else {
+               switch (speed) {
+               case 200000000:
+                       freqsel = 0x0;
+                       break;
+               default:
+                       freqsel = 0x4;
+               }
+               regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
+                                  freqsel << FREQSEL_SHIFT);
+       }
+
+       /* Configure DLL TRIM */
+       mask = DLL_TRIM_ICP_MASK;
+       val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
+
+       /* Configure DLL driver strength */
+       mask |= DR_TY_MASK;
+       val |= plat->drv_strength << DR_TY_SHIFT;
+       regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
+
+       /* Enable DLL */
+       regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
+                          0x1 << ENDLL_SHIFT);
+       /*
+        * Poll for DLL ready. Use a one second timeout.
+        * Works in all experiments done so far
+        */
+       ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
+                                      val & DLLRDY_MASK, 1000000);
+
+       return ret;
+}
+
+static void am654_sdhci_write_itapdly(struct am654_sdhci_plat *plat,
+                                     u32 itapdly)
+{
+       /* Set ITAPCHGWIN before writing to ITAPDLY */
+       regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK,
+                          1 << ITAPCHGWIN_SHIFT);
+       regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYSEL_MASK,
+                          itapdly << ITAPDLYSEL_SHIFT);
+       regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
+}
+
+static void am654_sdhci_setup_delay_chain(struct am654_sdhci_plat *plat,
+                                         int mode)
+{
+       u32 mask, val;
+
+       val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
+       mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
+       regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+
+       am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode]);
+}
+
+static int am654_sdhci_set_ios_post(struct am654_sdhci_plat *plat, struct 
mci_ios *ios)
+{
+       unsigned int speed = ios->clock;
+       int mode = ios->timing;
+       u32 otap_del_sel;
+       u32 mask, val;
+       int ret;
+
+       /* Reset SD Clock Enable */
+       val = sdhci_read16(&plat->sdhci, SDHCI_CLOCK_CONTROL);
+       val &= ~SDHCI_CLOCK_CARD_EN;
+       sdhci_write16(&plat->sdhci, SDHCI_CLOCK_CONTROL, val);
+
+       regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK, 0);
+
+       /* restart clock */
+       sdhci_set_clock(&plat->sdhci, speed, clk_get_rate(plat->clk));
+
+       /* switch phy back on */
+       otap_del_sel = plat->otap_del_sel[mode];
+       mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+       val = (1 << OTAPDLYENA_SHIFT) |
+             (otap_del_sel << OTAPDLYSEL_SHIFT);
+
+       /* Write to STRBSEL for HS400 speed mode */
+       if (mode == MMC_TIMING_MMC_HS400) {
+               if (plat->soc_data->flags & STRBSEL_4_BIT)
+                       mask |= STRBSEL_4BIT_MASK;
+               else
+                       mask |= STRBSEL_8BIT_MASK;
+
+               val |= plat->strb_sel << STRBSEL_SHIFT;
+       }
+
+       regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+       if (mode > MMC_TIMING_UHS_SDR25 && speed >= CLOCK_TOO_SLOW_HZ) {
+               ret = am654_sdhci_setup_dll(plat, speed);
+               if (ret)
+                       return ret;
+       } else {
+               am654_sdhci_setup_delay_chain(plat, mode);
+       }
+
+       regmap_update_bits(plat->base, PHY_CTRL5, CLKBUFSEL_MASK,
+                          plat->clkbuf_sel);
+
+       return 0;
+}
+
+static int am654_sdhci_init(struct mci_host *mci, struct device *dev)
+{
+       struct am654_sdhci_plat *plat = container_of(mci, struct 
am654_sdhci_plat, mci);
+       u32 ctl_cfg_2 = 0;
+       u32 mask, val;
+       int ret;
+
+       ret = sdhci_reset(&plat->sdhci, SDHCI_RESET_ALL);
+       if (ret)
+               return ret;
+
+       if (plat->fails_without_test_cd) {
+               val = sdhci_read8(&plat->sdhci, SDHCI_HOST_CONTROL);
+               val |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN;
+               sdhci_write8(&plat->sdhci, SDHCI_HOST_CONTROL, val);
+       }
+
+       sdhci_write8(&plat->sdhci, SDHCI_POWER_CONTROL,
+                               SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
+       udelay(400);
+
+       sdhci_write32(&plat->sdhci, SDHCI_INT_ENABLE, ~0);
+       sdhci_write32(&plat->sdhci, SDHCI_SIGNAL_ENABLE, 0x00);
+
+       /* Reset OTAP to default value */
+       mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+       regmap_update_bits(plat->base, PHY_CTRL4, mask, 0x0);
+
+       if (plat->soc_data->flags & DLL_CALIB) {
+               regmap_read(plat->base, PHY_STAT1, &val);
+               if (~val & CALDONE_MASK) {
+                       /* Calibrate IO lines */
+                       regmap_update_bits(plat->base, PHY_CTRL1, PDB_MASK,
+                                          PDB_MASK);
+                       ret = regmap_read_poll_timeout(plat->base, PHY_STAT1,
+                                                      val, val & CALDONE_MASK,
+                                                      20);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       /* Enable pins by setting IO mux to 0 */
+       if (plat->soc_data->flags & IOMUX_PRESENT)
+               regmap_update_bits(plat->base, PHY_CTRL1, IOMUX_ENABLE_MASK, 0);
+
+       /* Set slot type based on SD or eMMC */
+       if (plat->non_removable)
+               ctl_cfg_2 = SLOTTYPE_EMBEDDED;
+
+       regmap_update_bits(plat->base, CTL_CFG_2, SLOTTYPE_MASK, ctl_cfg_2);
+
+       return 0;
+}
+
+const struct am654_driver_data am654_drv_data = {
+       .flags = DLL_PRESENT | IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT,
+};
+
+const struct am654_driver_data am654_sr1_drv_data = {
+       .flags = IOMUX_PRESENT | FREQSEL_2_BIT | DLL_PRESENT | DLL_CALIB |
+                STRBSEL_4_BIT,
+};
+
+const struct am654_driver_data j721e_8bit_drv_data = {
+       .flags = DLL_PRESENT | DLL_CALIB,
+};
+
+static int j721e_4bit_sdhci_set_ios_post(struct am654_sdhci_plat *plat, struct 
mci_ios *ios)
+{
+       u32 otap_del_sel, mask, val;
+
+       otap_del_sel = plat->otap_del_sel[ios->timing];
+       mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+       val = (1 << OTAPDLYENA_SHIFT) | (otap_del_sel << OTAPDLYSEL_SHIFT);
+       regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+       regmap_update_bits(plat->base, PHY_CTRL5, CLKBUFSEL_MASK,
+                          plat->clkbuf_sel);
+
+       return 0;
+}
+
+const struct am654_driver_data j721e_4bit_drv_data = {
+       .set_ios_post           = &j721e_4bit_sdhci_set_ios_post,
+       .flags = IOMUX_PRESENT,
+};
+
+static const struct am654_driver_data sdhci_am64_8bit_drvdata = {
+       .set_ios_post           = &am654_sdhci_set_ios_post,
+       .flags = DLL_PRESENT | DLL_CALIB,
+};
+
+static const struct am654_driver_data sdhci_am64_4bit_drvdata = {
+       .set_ios_post           = &j721e_4bit_sdhci_set_ios_post,
+       .flags = IOMUX_PRESENT,
+};
+
+static int sdhci_am654_get_otap_delay(struct am654_sdhci_plat *plat)
+{
+       struct device *dev = plat->mci.hw_dev;
+       struct device_node *np = dev->of_node;
+       int ret;
+       int i;
+
+       /* ti,otap-del-sel-legacy is mandatory */
+       ret = of_property_read_u32(np, "ti,otap-del-sel-legacy",
+                          &plat->otap_del_sel[0]);
+       if (ret)
+               return ret;
+       /*
+        * Remove the corresponding capability if an otap-del-sel
+        * value is not found
+        */
+       for (i = MMC_TIMING_MMC_HS; i <= MMC_TIMING_MMC_HS400; i++) {
+               ret = of_property_read_u32(np, td[i].otap_binding,
+                                  &plat->otap_del_sel[i]);
+               if (ret) {
+                       dev_dbg(dev, "Couldn't find %s\n", td[i].otap_binding);
+                       /*
+                        * Remove the corresponding capability
+                        * if an otap-del-sel value is not found
+                        */
+                       plat->mci.host_caps &= ~td[i].capability;
+               }
+
+               if (td[i].itap_binding)
+                       of_property_read_u32(np, td[i].itap_binding,
+                                    &plat->itap_del_sel[i]);
+       }
+
+       return 0;
+}
+
+static void print_error(struct am654_sdhci_plat *host, int cmdidx)
+{
+       dev_dbg(host->mci.hw_dev,
+               "error while transfering data for command %d\n", cmdidx);
+       dev_dbg(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
+               sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
+               sdhci_read32(&host->sdhci, SDHCI_INT_NORMAL_STATUS));
+}
+
+static int am654_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+                               struct mci_data *data)
+{
+       struct am654_sdhci_plat *host = container_of(mci, struct 
am654_sdhci_plat, mci);
+       u32 command, xfer;
+       int ret;
+       dma_addr_t dma;
+
+       ret = sdhci_wait_idle(&host->sdhci, cmd);
+       if (ret)
+               return ret;
+
+       sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+       sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+
+       sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+       sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+                               dma == SDHCI_NO_DMA ? false : true,
+                               &command, &xfer);
+
+       sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+       sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+       sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+       ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
+       if (ret)
+               goto error;
+
+       sdhci_read_response(&host->sdhci, cmd);
+       sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
+
+       ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+
+error:
+       if (ret) {
+               print_error(host, cmd->cmdidx);
+               sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
+               sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
+       }
+
+       sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+       return ret;
+}
+
+static void am654_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+       struct am654_sdhci_plat *plat = container_of(mci, struct 
am654_sdhci_plat, mci);
+       u32 val;
+
+       if (ios->clock)
+               sdhci_set_clock(&plat->sdhci, ios->clock, plat->sdhci.max_clk);
+
+       sdhci_set_bus_width(&plat->sdhci, ios->bus_width);
+
+       val = sdhci_read8(&plat->sdhci, SDHCI_HOST_CONTROL);
+
+       if (ios->clock > 26000000)
+               val |= SDHCI_CTRL_HISPD;
+       else
+               val &= ~SDHCI_CTRL_HISPD;
+
+       sdhci_write8(&plat->sdhci, SDHCI_HOST_CONTROL, val);
+
+       plat->soc_data->set_ios_post(plat, ios);
+}
+
+static const struct regmap_config regmap_config = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+       .max_register   = 0x400,
+};
+
+static int am654_sdhci_probe(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct am654_sdhci_plat *plat;
+       struct mci_host *mci;
+       struct resource *iores;
+       u32 drv_strength;
+       int ret;
+
+       plat = xzalloc(sizeof(*plat));
+
+       ret = dev_get_drvdata(dev, (const void **)&plat->soc_data);
+       if (ret)
+               return ret;
+
+       iores = dev_request_mem_resource(dev, 0);
+       if (IS_ERR(iores))
+               return PTR_ERR(iores);
+
+       plat->sdhci.base = IOMEM(iores->start);
+
+       iores = dev_request_mem_resource(dev, 1);
+       if (IS_ERR(iores))
+               return PTR_ERR(iores);
+
+       plat->base = regmap_init_mmio(dev, IOMEM(iores->start), &regmap_config);
+        if (IS_ERR(plat->base)) {
+                dev_err(dev, "failed to init regmap: %ld\n",
+                                PTR_ERR(plat->base));
+                return PTR_ERR(plat->base);
+        }
+
+       plat->clk = clk_get(dev, "clk_xin");
+       if (IS_ERR(plat->clk)) {
+               dev_err(dev, "failed to get clock\n");
+               return ret;
+       }
+
+       clk_enable(plat->clk);
+
+       plat->clk_ahb = clk_get(dev, "clk_ahb");
+       if (IS_ERR(plat->clk_ahb)) {
+               dev_err(dev, "failed to get ahb clock\n");
+               return ret;
+       }
+
+       clk_enable(plat->clk_ahb);
+
+       mci = &plat->mci;
+       mci->f_max = clk_get_rate(plat->clk);
+       mci->f_min = 50000000 / 256;
+
+       if (plat->soc_data->flags & DLL_PRESENT) {
+               ret = of_property_read_u32(np, "ti,trm-icp", &plat->trm_icp);
+               if (ret)
+                       return ret;
+
+               ret = of_property_read_u32(np, "ti,driver-strength-ohm",
+                                  &drv_strength);
+               if (ret)
+                       return ret;
+
+               switch (drv_strength) {
+               case 50:
+                       plat->drv_strength = DRIVER_STRENGTH_50_OHM;
+                       break;
+               case 33:
+                       plat->drv_strength = DRIVER_STRENGTH_33_OHM;
+                       break;
+               case 66:
+                       plat->drv_strength = DRIVER_STRENGTH_66_OHM;
+                       break;
+               case 100:
+                       plat->drv_strength = DRIVER_STRENGTH_100_OHM;
+                       break;
+               case 40:
+                       plat->drv_strength = DRIVER_STRENGTH_40_OHM;
+                       break;
+               default:
+                       dev_err(dev, "Invalid driver strength\n");
+                       return -EINVAL;
+               }
+       }
+
+       mci->send_cmd = am654_sdhci_send_cmd;
+       mci->set_ios = am654_sdhci_set_ios;
+       mci->init = am654_sdhci_init;
+       mci->hw_dev = dev;
+
+       of_property_read_u32(np, "ti,strobe-sel", &plat->strb_sel);
+       of_property_read_u32(np, "ti,clkbuf-sel", &plat->clkbuf_sel);
+
+       plat->fails_without_test_cd = of_property_read_bool(np, 
"ti,fails-without-test-cd");
+
+       mci_of_parse(&plat->mci);
+
+       ret = sdhci_am654_get_otap_delay(plat);
+       if (ret)
+               return ret;
+
+       plat->sdhci.mci = mci;
+       sdhci_setup_host(&plat->sdhci);
+
+       dev->priv = plat;
+
+       return mci_register(&plat->mci);
+}
+
+static const struct of_device_id am654_sdhci_ids[] = {
+       {
+               .compatible = "ti,am654-sdhci-5.1",
+               .data = &am654_drv_data,
+       }, {
+               .compatible = "ti,j721e-sdhci-8bit",
+               .data = &j721e_8bit_drv_data,
+       }, {
+               .compatible = "ti,j721e-sdhci-4bit",
+               .data = &j721e_4bit_drv_data,
+       }, {
+               .compatible = "ti,am64-sdhci-8bit",
+               .data = &sdhci_am64_8bit_drvdata,
+       }, {
+               .compatible = "ti,am64-sdhci-4bit",
+               .data = &sdhci_am64_4bit_drvdata,
+       }, {
+               .compatible = "ti,am62-sdhci",
+               .data = &sdhci_am64_4bit_drvdata,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, am654_sdhci_ids);
+
+static struct driver am654_sdhc_driver = {
+        .name = "am654-sdhci",
+        .probe = am654_sdhci_probe,
+        .of_compatible = DRV_OF_COMPAT(am654_sdhci_ids),
+};
+device_platform_driver(am654_sdhc_driver);
-- 
2.39.2


Reply via email to