diff mbox series

[v1,5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver

Message ID 20250528085453.481320-1-yangzh0906@thundersoft.com
State New
Headers show
Series None | expand

Commit Message

Albert Yang May 28, 2025, 8:54 a.m. UTC
Add a driver for the DesignWare Mobile Storage Host Controller (DWCMSHC)
SDHCI controller found in Black Sesame Technologies C1200 SoCs.

The driver provides specialized clock configuration, tuning, voltage
switching, and power management for the BST DWCMSHC controller. It also
includes support for eMMC boot and memory-mapped I/O for CRM registers.

Signed-off-by: Ge Gordon <gordon.ge@bst.ai>
Signed-off-by: Albert Yang <yangzh0906@thundersoft.com>
---
 drivers/mmc/host/Kconfig              |  11 +
 drivers/mmc/host/Makefile             |   1 +
 drivers/mmc/host/sdhci-of-bst-c1200.c | 920 ++++++++++++++++++++++++++
 3 files changed, 932 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-of-bst-c1200.c

Comments

kernel test robot May 28, 2025, 10:56 p.m. UTC | #1
Hi Albert,

kernel test robot noticed the following build warnings:

[auto build test WARNING on robh/for-next]
[also build test WARNING on arm64/for-next/core soc/for-next krzk/for-next krzk-dt/for-next krzk-mem-ctrl/for-next linus/master v6.15 next-20250528]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Albert-Yang/dt-bindings-vendor-prefixes-Add-Black-Sesame-Technologies-Co-Ltd/20250528-190614
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250528085453.481320-1-yangzh0906%40thundersoft.com
patch subject: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
config: i386-randconfig-002-20250529 (https://download.01.org/0day-ci/archive/20250529/202505290615.GZzN5rNL-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250529/202505290615.GZzN5rNL-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505290615.GZzN5rNL-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/mmc/host/sdhci-of-bst-c1200.c:64:6: warning: no previous prototype for 'sdhci_bst_print_vendor' [-Wmissing-prototypes]
      64 | void sdhci_bst_print_vendor(struct sdhci_host *host)
         |      ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/mmc/host/sdhci-of-bst-c1200.c:153:6: warning: no previous prototype for 'sdhci_enable_bst_clk' [-Wmissing-prototypes]
     153 | void sdhci_enable_bst_clk(struct sdhci_host *host, unsigned int clk)
         |      ^~~~~~~~~~~~~~~~~~~~
>> drivers/mmc/host/sdhci-of-bst-c1200.c:245:6: warning: no previous prototype for 'sdhci_set_bst_clock' [-Wmissing-prototypes]
     245 | void sdhci_set_bst_clock(struct sdhci_host *host, unsigned int clock)
         |      ^~~~~~~~~~~~~~~~~~~
   drivers/mmc/host/sdhci-of-bst-c1200.c: In function 'bst_sdhci_execute_tuning':
>> drivers/mmc/host/sdhci-of-bst-c1200.c:323:13: warning: unused variable 'val' [-Wunused-variable]
     323 |         u32 val;
         |             ^~~
   drivers/mmc/host/sdhci-of-bst-c1200.c: At top level:
>> drivers/mmc/host/sdhci-of-bst-c1200.c:507:5: warning: no previous prototype for 'bst_sdhci_setup_host' [-Wmissing-prototypes]
     507 | int bst_sdhci_setup_host(struct sdhci_host *host)
         |     ^~~~~~~~~~~~~~~~~~~~
   drivers/mmc/host/sdhci-of-bst-c1200.c: In function 'bst_sdhci_setup_host':
>> drivers/mmc/host/sdhci-of-bst-c1200.c:515:14: warning: variable 'enable_vqmmc' set but not used [-Wunused-but-set-variable]
     515 |         bool enable_vqmmc = false;
         |              ^~~~~~~~~~~~
>> drivers/mmc/host/sdhci-of-bst-c1200.c:513:13: warning: variable 'max_clk' set but not used [-Wunused-but-set-variable]
     513 |         u32 max_clk;
         |             ^~~~~~~


vim +/sdhci_bst_print_vendor +64 drivers/mmc/host/sdhci-of-bst-c1200.c

    63	
  > 64	void sdhci_bst_print_vendor(struct sdhci_host *host)
    65	{
    66		SDHCI_DUMP_BST("============ SDHCI VENDOR REGISTER DUMP ===========\n");
    67	
    68		SDHCI_DUMP_BST("VER_ID:  0x%08x | VER_TPYE:  0x%08x\n",
    69			       sdhci_readl(host, SDHC_MHSC_VER_ID_R),
    70			       sdhci_readl(host, SDHC_MHSC_VER_TPYE_R));
    71		SDHCI_DUMP_BST("MHSC_CTRL:  0x%08x | MBIU_CTRL:  0x%08x\n",
    72			       sdhci_readw(host, SDHC_MHSC_CTRL_R),
    73			       sdhci_readw(host, SDHC_MBIU_CTRL_R));
    74		SDHCI_DUMP_BST("EMMC_CTRL:  0x%08x | BOOT_CTRL: 0x%08x\n",
    75			       sdhci_readl(host, SDHC_EMMC_CTRL_R),
    76			       sdhci_readw(host, SDHC_BOOT_CTRL_R));
    77		SDHCI_DUMP_BST("GP_IN:   0x%08x | GP_OUT: 0x%08x\n",
    78			       sdhci_readl(host, SDHC_GP_IN_R),
    79			       sdhci_readb(host, SDHC_GP_OUT_R));
    80		SDHCI_DUMP_BST("AT_CTRL:     0x%08x | AT_STAT:  0x%08x\n",
    81			       sdhci_readb(host, SDHC_AT_CTRL_R),
    82			       sdhci_readb(host, SDHC_AT_STAT_R));
    83	}
    84	EXPORT_SYMBOL_GPL(sdhci_bst_print_vendor);
    85	
    86	static u32 bst_read_phys_bst(u32 phys_addr)
    87	{
    88		u32 phys_addr_page = phys_addr & 0xFFFFE000;
    89		u32 phys_offset = phys_addr & 0x00001FFF;
    90		u32 map_size = phys_offset + sizeof(u32);
    91		u32 ret = 0xDEADBEEF;
    92		void *mem_mapped = ioremap(phys_addr_page, map_size);
    93	
    94		if (mem_mapped) {
    95			ret = (u32)ioread32(((u8 *)mem_mapped) + phys_offset);
    96			iounmap(mem_mapped);
    97		}
    98	
    99		return ret;
   100	}
   101	
   102	static void bst_write_phys_bst(u32 phys_addr, u32 value)
   103	{
   104		u32 phys_addr_page = phys_addr & 0xFFFFE000;
   105		u32 phys_offset = phys_addr & 0x00001FFF;
   106		u32 map_size = phys_offset + sizeof(u32);
   107		void *mem_mapped = ioremap(phys_addr_page, map_size);
   108	
   109		if (mem_mapped) {
   110			iowrite32(value, ((u8 *)mem_mapped) + phys_offset);
   111			iounmap(mem_mapped);
   112		}
   113	}
   114	
   115	static unsigned int bst_get_max_clock(struct sdhci_host *host)
   116	{
   117		return host->mmc->f_max;
   118	}
   119	
   120	static unsigned int bst_get_min_clock(struct sdhci_host *host)
   121	{
   122		return host->mmc->f_min;
   123	}
   124	
   125	struct rx_ctrl {
   126		struct {
   127			u32 rx_revert:1;
   128			u32 rx_clk_sel_sec:1;
   129			u32 rx_clk_div:4;
   130			u32 rx_clk_phase_inner:2;
   131			u32 rx_clk_sel_first:1;
   132			u32 rx_clk_phase_out:2;
   133			u32 rx_clk_en:1;
   134			u32 res0:20;
   135		} bit;
   136		u32 reg;
   137	};
   138	
   139	struct sdmmc_iocfg {
   140		struct {
   141			u32 res0:16;
   142			u32 SC_SDMMC0_PVDD18POCSD0:2;
   143			u32 SC_SDMMC0_PVDD18POCSD1:2;
   144			u32 SC_SDMMC0_PVDD18POCSD2:2;
   145			u32 SC_SDMMC1_PVDD18POCSD0:2;
   146			u32 SC_SDMMC1_PVDD18POCSD1:2;
   147			u32 SC_SDMMC1_PVDD18POCSD2:2;
   148			u32 res1:4;
   149		} bit;
   150		u32 reg;
   151	};
   152	
 > 153	void sdhci_enable_bst_clk(struct sdhci_host *host, unsigned int clk)
   154	{
   155		struct sdhci_pltfm_host *pltfm_host;
   156		struct dwcmshc_priv *priv;
   157		unsigned int div;
   158		u32 val;
   159		struct rx_ctrl rx_reg;
   160	
   161		pltfm_host = sdhci_priv(host);
   162		priv = sdhci_pltfm_priv(pltfm_host);
   163		if (clk == 0) {
   164			div = clk;
   165		} else if (clk > default_max_freq) {
   166			div = clk / 1000;
   167			div = default_max_freq / div;
   168		} else if (clk < 1500) {
   169			div = clk;
   170		} else {
   171			div = default_max_freq * 100;
   172			div = div / clk;
   173			div /= 100;
   174		}
   175	
   176		clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
   177		clk &= ~SDHCI_CLOCK_CARD_EN;
   178		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
   179	
   180		clk &= ~SDHCI_CLOCK_PLL_EN;
   181		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
   182	
   183		val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL);
   184		val &= ~(1 << 8);
   185		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL, val);
   186	
   187		val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL);
   188		val &= ~(0xff);
   189		val |= 0x20;
   190		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL, val);
   191	
   192		val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL);
   193		val |= 1 << 8;
   194		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL, val);
   195	
   196		val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL);
   197		val &= ~(1 << 11);
   198		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL, val);
   199	
   200		rx_reg.reg = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL);
   201	
   202		rx_reg.bit.rx_revert = 0;
   203		rx_reg.bit.rx_clk_sel_sec = 1;
   204		rx_reg.bit.rx_clk_div = 4;
   205		rx_reg.bit.rx_clk_phase_inner = 2;
   206		rx_reg.bit.rx_clk_sel_first = 0;
   207		rx_reg.bit.rx_clk_phase_out = 2;
   208	
   209		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL, rx_reg.reg);
   210	
   211		val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL);
   212		val |= 1 << 11;
   213		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL, val);
   214	
   215		/* Disable clock first */
   216		val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL);
   217		val &= ~0x0400;
   218		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL, val);
   219	
   220		/* Setup clock divider */
   221		val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL);
   222		val &= ~0x03ff;
   223		val |= div;
   224		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL, val);
   225	
   226		/* Enable clock */
   227		val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL);
   228		val |= 0x0400;
   229		bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL, val);
   230	
   231		sdhci_writew(host, (div & 0xff) << 8, SDHCI_CLOCK_CONTROL);
   232	
   233		sdhci_writew(host, (div & 0xff) << 8, SDHCI_CLOCK_CONTROL);
   234		clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
   235		clk |= SDHCI_CLOCK_PLL_EN;
   236		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
   237	
   238		clk |= SDHCI_CLOCK_CARD_EN;
   239		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
   240	
   241		clk |= SDHCI_CLOCK_INT_EN;
   242		sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
   243	}
   244	
 > 245	void sdhci_set_bst_clock(struct sdhci_host *host, unsigned int clock)
   246	{
   247		if (clock == 0)
   248			return;
   249		sdhci_enable_bst_clk(host, clock);
   250	}
   251
kernel test robot May 29, 2025, 1:22 a.m. UTC | #2
Hi Albert,

kernel test robot noticed the following build errors:

[auto build test ERROR on robh/for-next]
[also build test ERROR on arm64/for-next/core soc/for-next krzk/for-next krzk-dt/for-next krzk-mem-ctrl/for-next linus/master v6.15 next-20250528]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Albert-Yang/dt-bindings-vendor-prefixes-Add-Black-Sesame-Technologies-Co-Ltd/20250528-190614
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250528085453.481320-1-yangzh0906%40thundersoft.com
patch subject: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20250529/202505290935.IfNyJVFA-lkp@intel.com/config)
compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250529/202505290935.IfNyJVFA-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505290935.IfNyJVFA-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/mmc/host/sdhci-of-bst-c1200.c:64:6: warning: no previous prototype for function 'sdhci_bst_print_vendor' [-Wmissing-prototypes]
      64 | void sdhci_bst_print_vendor(struct sdhci_host *host)
         |      ^
   drivers/mmc/host/sdhci-of-bst-c1200.c:64:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
      64 | void sdhci_bst_print_vendor(struct sdhci_host *host)
         | ^
         | static 
   drivers/mmc/host/sdhci-of-bst-c1200.c:153:6: warning: no previous prototype for function 'sdhci_enable_bst_clk' [-Wmissing-prototypes]
     153 | void sdhci_enable_bst_clk(struct sdhci_host *host, unsigned int clk)
         |      ^
   drivers/mmc/host/sdhci-of-bst-c1200.c:153:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     153 | void sdhci_enable_bst_clk(struct sdhci_host *host, unsigned int clk)
         | ^
         | static 
   drivers/mmc/host/sdhci-of-bst-c1200.c:245:6: warning: no previous prototype for function 'sdhci_set_bst_clock' [-Wmissing-prototypes]
     245 | void sdhci_set_bst_clock(struct sdhci_host *host, unsigned int clock)
         |      ^
   drivers/mmc/host/sdhci-of-bst-c1200.c:245:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     245 | void sdhci_set_bst_clock(struct sdhci_host *host, unsigned int clock)
         | ^
         | static 
   drivers/mmc/host/sdhci-of-bst-c1200.c:323:6: warning: unused variable 'val' [-Wunused-variable]
     323 |         u32 val;
         |             ^~~
>> drivers/mmc/host/sdhci-of-bst-c1200.c:607:18: error: call to undeclared function 'FIELD_GET'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     607 |         host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps);
         |                         ^
   drivers/mmc/host/sdhci-of-bst-c1200.c:513:6: warning: variable 'max_clk' set but not used [-Wunused-but-set-variable]
     513 |         u32 max_clk;
         |             ^
   drivers/mmc/host/sdhci-of-bst-c1200.c:515:7: warning: variable 'enable_vqmmc' set but not used [-Wunused-but-set-variable]
     515 |         bool enable_vqmmc = false;
         |              ^
   drivers/mmc/host/sdhci-of-bst-c1200.c:507:5: warning: no previous prototype for function 'bst_sdhci_setup_host' [-Wmissing-prototypes]
     507 | int bst_sdhci_setup_host(struct sdhci_host *host)
         |     ^
   drivers/mmc/host/sdhci-of-bst-c1200.c:507:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     507 | int bst_sdhci_setup_host(struct sdhci_host *host)
         | ^
         | static 
   7 warnings and 1 error generated.


vim +/FIELD_GET +607 drivers/mmc/host/sdhci-of-bst-c1200.c

   506	
   507	int bst_sdhci_setup_host(struct sdhci_host *host)
   508	{
   509		struct mmc_host *mmc;
   510		u32 max_current_caps;
   511		unsigned int ocr_avail;
   512		unsigned int override_timeout_clk;
   513		u32 max_clk;
   514		int ret = 0;
   515		bool enable_vqmmc = false;
   516	
   517		WARN_ON(!host);
   518		if (!host)
   519			return -EINVAL;
   520	
   521		mmc = host->mmc;
   522	
   523		/*
   524		 * If there are external regulators, get them. Note this must be done
   525		 * early before resetting the host and reading the capabilities so that
   526		 * the host can take the appropriate action if regulators are not
   527		 * available.
   528		 */
   529		if (!mmc->supply.vqmmc) {
   530			ret = mmc_regulator_get_supply(mmc);
   531			if (ret)
   532				return ret;
   533			enable_vqmmc  = true;
   534		}
   535	
   536		pr_info("Version:   0x%08x | Present:  0x%08x\n",
   537			sdhci_readw(host, SDHCI_HOST_VERSION),
   538			sdhci_readl(host, SDHCI_PRESENT_STATE));
   539		pr_info("Caps:      0x%08x | Caps_1:   0x%08x\n",
   540			sdhci_readl(host, SDHCI_CAPABILITIES),
   541			sdhci_readl(host, SDHCI_CAPABILITIES_1));
   542	
   543		sdhci_read_caps(host);
   544	
   545		override_timeout_clk = host->timeout_clk;
   546	
   547		host->flags |= SDHCI_USE_SDMA;
   548	
   549		if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
   550			if (host->ops->set_dma_mask)
   551				ret = host->ops->set_dma_mask(host);
   552			else
   553				ret = bst_sdhci_set_dma_mask(host);
   554	
   555			if (!ret && host->ops->enable_dma)
   556				ret = host->ops->enable_dma(host);
   557	
   558			if (ret) {
   559				pr_warn("%s: No suitable DMA available - falling back to PIO\n",
   560					mmc_hostname(mmc));
   561				host->flags &= ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA);
   562	
   563				ret = 0;
   564			}
   565		}
   566	
   567		if (host->flags & SDHCI_USE_ADMA) {
   568			dma_addr_t dma;
   569			void *buf;
   570	
   571			if (!(host->flags & SDHCI_USE_64_BIT_DMA))
   572				host->alloc_desc_sz = SDHCI_ADMA2_32_DESC_SZ;
   573			else if (!host->alloc_desc_sz)
   574				host->alloc_desc_sz = SDHCI_ADMA2_64_DESC_SZ(host);
   575	
   576			host->desc_sz = host->alloc_desc_sz;
   577			host->adma_table_sz = host->adma_table_cnt * host->desc_sz;
   578	
   579			host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
   580			/*
   581			 * Use zalloc to zero the reserved high 32-bits of 128-bit
   582			 * descriptors so that they never need to be written.
   583			 */
   584			buf = dma_alloc_coherent(mmc_dev(mmc),
   585						 host->align_buffer_sz + host->adma_table_sz,
   586						 &dma, GFP_KERNEL);
   587			if (!buf) {
   588				pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
   589					mmc_hostname(mmc));
   590				host->flags &= ~SDHCI_USE_ADMA;
   591			} else if ((dma + host->align_buffer_sz) &
   592				   (SDHCI_ADMA2_DESC_ALIGN - 1)) {
   593				pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
   594					mmc_hostname(mmc));
   595				host->flags &= ~SDHCI_USE_ADMA;
   596				dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
   597						  host->adma_table_sz, buf, dma);
   598			} else {
   599				host->align_buffer = buf;
   600				host->align_addr = dma;
   601	
   602				host->adma_table = buf + host->align_buffer_sz;
   603				host->adma_addr = dma + host->align_buffer_sz;
   604			}
   605		}
   606	
 > 607		host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps);
   608	
   609		host->max_clk *= 1000000;
   610		if (host->max_clk == 0 || host->quirks &
   611				SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
   612			if (!host->ops->get_max_clock) {
   613				pr_err("%s: Hardware doesn't specify base clock frequency.\n",
   614				       mmc_hostname(mmc));
   615				ret = -ENODEV;
   616				goto undma;
   617			}
   618			host->max_clk = host->ops->get_max_clock(host);
   619		}
   620	
   621		/*
   622		 * Set host parameters.
   623		 */
   624		max_clk = host->max_clk;
   625	
   626		if (host->ops->get_min_clock)
   627			mmc->f_min = host->ops->get_min_clock(host);
   628	
   629		if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
   630			host->timeout_clk = FIELD_GET(SDHCI_TIMEOUT_CLK_MASK, host->caps);
   631	
   632			if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
   633				host->timeout_clk *= 1000;
   634	
   635			if (host->timeout_clk == 0) {
   636				if (!host->ops->get_timeout_clock) {
   637					pr_err("%s: Hardware doesn't specify timeout clock frequency.\n",
   638					       mmc_hostname(mmc));
   639					ret = -ENODEV;
   640					goto undma;
   641				}
   642	
   643				host->timeout_clk =
   644					DIV_ROUND_UP(host->ops->get_timeout_clock(host),
   645						     1000);
   646			}
   647	
   648			if (override_timeout_clk)
   649				host->timeout_clk = override_timeout_clk;
   650	
   651			mmc->max_busy_timeout = host->ops->get_max_timeout_count ?
   652				host->ops->get_max_timeout_count(host) : 1 << 27;
   653			mmc->max_busy_timeout /= host->timeout_clk;
   654		}
   655	
   656		mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23;
   657		mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
   658	
   659		if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
   660			host->flags |= SDHCI_AUTO_CMD12;
   661	
   662		/*
   663		 * A controller may support 8-bit width, but the board itself
   664		 * might not have the pins brought out.  Boards that support
   665		 * 8-bit width must set "mmc->caps |= MMC_CAP_8_BIT_DATA;" in
   666		 * their platform code before calling sdhci_add_host(), and we
   667		 * won't assume 8-bit width for hosts without that CAP.
   668		 */
   669		if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
   670			mmc->caps |= MMC_CAP_4_BIT_DATA;
   671	
   672		if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
   673			mmc->caps &= ~MMC_CAP_CMD23;
   674	
   675		if (host->caps & SDHCI_CAN_DO_HISPD)
   676			mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
   677	
   678		/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
   679		if (host->caps1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
   680				   SDHCI_SUPPORT_DDR50))
   681			mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
   682	
   683		/* SDR104 supports also implies SDR50 support */
   684		if (host->caps1 & SDHCI_SUPPORT_SDR104) {
   685			mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
   686			/* SD3.0: SDR104 is supported so (for eMMC) the caps2
   687			 * field can be promoted to support HS200.
   688			 */
   689			if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
   690				mmc->caps2 |= MMC_CAP2_HS200;
   691		}
   692	
   693		if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
   694		    (IS_ERR(mmc->supply.vqmmc) ||
   695		     !regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000,
   696						     1300000)))
   697			mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
   698	
   699		/* Does the host need tuning for SDR50? */
   700		if (host->caps1 & SDHCI_USE_SDR50_TUNING)
   701			host->flags |= SDHCI_SDR50_NEEDS_TUNING;
   702	
   703		/* Driver Type(s) (A, C, D) supported by the host */
   704		if (host->caps1 & SDHCI_DRIVER_TYPE_A)
   705			mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
   706		if (host->caps1 & SDHCI_DRIVER_TYPE_C)
   707			mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
   708		if (host->caps1 & SDHCI_DRIVER_TYPE_D)
   709			mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
   710	
   711		/* Initial value for re-tuning timer count */
   712		host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK,
   713					       host->caps1);
   714	
   715		/*
   716		 * In case Re-tuning Timer is not disabled, the actual value of
   717		 * re-tuning timer will be 2 ^ (n - 1).
   718		 */
   719		if (host->tuning_count)
   720			host->tuning_count = 1 << (host->tuning_count - 1);
   721	
   722		/* Re-tuning mode supported by the Host Controller */
   723		host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1);
   724	
   725		ocr_avail = 0;
   726	
   727		if (host->caps & SDHCI_CAN_VDD_330) {
   728			ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
   729	
   730			mmc->max_current_330 = FIELD_GET(SDHCI_MAX_CURRENT_330_MASK,
   731							 max_current_caps) *
   732							SDHCI_MAX_CURRENT_MULTIPLIER;
   733		}
   734		if (host->caps & SDHCI_CAN_VDD_300) {
   735			ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
   736	
   737			mmc->max_current_300 = FIELD_GET(SDHCI_MAX_CURRENT_300_MASK,
   738							 max_current_caps) *
   739							SDHCI_MAX_CURRENT_MULTIPLIER;
   740		}
   741		if (host->caps & SDHCI_CAN_VDD_180) {
   742			ocr_avail |= MMC_VDD_165_195;
   743	
   744			mmc->max_current_180 = FIELD_GET(SDHCI_MAX_CURRENT_180_MASK,
   745							 max_current_caps) *
   746							SDHCI_MAX_CURRENT_MULTIPLIER;
   747		}
   748	
   749		/* If OCR set by host, use it instead. */
   750		if (host->ocr_mask)
   751			ocr_avail = host->ocr_mask;
   752	
   753		/* If OCR set by external regulators, give it highest prio. */
   754		if (mmc->ocr_avail)
   755			ocr_avail = mmc->ocr_avail;
   756	
   757		mmc->ocr_avail = ocr_avail;
   758		mmc->ocr_avail_sdio = ocr_avail;
   759		if (host->ocr_avail_sdio)
   760			mmc->ocr_avail_sdio &= host->ocr_avail_sdio;
   761		mmc->ocr_avail_sd = ocr_avail;
   762		if (host->ocr_avail_sd)
   763			mmc->ocr_avail_sd &= host->ocr_avail_sd;
   764		else /* normal SD controllers don't support 1.8V */
   765			mmc->ocr_avail_sd &= ~MMC_VDD_165_195;
   766		mmc->ocr_avail_mmc = ocr_avail;
   767		if (host->ocr_avail_mmc)
   768			mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
   769	
   770		if ((mmc->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
   771				  MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
   772				  MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR)) ||
   773		    (mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)))
   774			host->flags |= SDHCI_SIGNALING_180;
   775	
   776		spin_lock_init(&host->lock);
   777	
   778		/*
   779		 * Maximum number of sectors in one transfer. Limited by SDMA boundary
   780		 * size (512KiB). Note some tuning modes impose a 4MiB limit, but this
   781		 * is less anyway.
   782		 */
   783		mmc->max_req_size = 524288;
   784		/*
   785		 * Maximum number of segments. Depends on if the hardware
   786		 * can do scatter/gather or not.
   787		 */
   788		mmc->max_segs = 1;
   789		mmc->max_req_size = min_t(size_t, mmc->max_req_size,
   790					  dma_max_mapping_size(mmc_dev(mmc)));
   791	
   792		mmc->max_seg_size = mmc->max_req_size;
   793	
   794		mmc->max_blk_size = (host->caps & SDHCI_MAX_BLOCK_MASK) >>
   795				SDHCI_MAX_BLOCK_SHIFT;
   796	
   797		mmc->max_blk_size = 512 << mmc->max_blk_size;
   798	
   799		/*
   800		 * Maximum block count.
   801		 */
   802		mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
   803	
   804		if (mmc->max_segs == 1)
   805			/* This may alter mmc->*_blk_* parameters */
   806			// bst sdhci must reallocate bounce buffer
   807			bst_sdhci_allocate_bounce_buffer(host);
   808	
   809		return 0;
   810	
   811	undma:
   812		if (host->align_buffer)
   813			dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
   814					  host->adma_table_sz, host->align_buffer,
   815					  host->align_addr);
   816		host->adma_table = NULL;
   817		host->align_buffer = NULL;
   818	
   819		return ret;
   820	}
   821
diff mbox series

Patch

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 264e11fa58ea..a5b3c0beeb78 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -1112,3 +1112,14 @@  config MMC_LITEX
 	  module will be called litex_mmc.
 
 	  If unsure, say N.
+
+config MMC_SDHCI_BST
+	tristate "SDHCI OF support for the BST DWC MSHC"
+	depends on MMC_SDHCI_PLTFM
+	depends on OF
+	depends on COMMON_CLK
+	help
+	  This selects Synopsys DesignWare Cores Mobile Storage Controller
+	  support.
+	  If you have a controller with this interface, say Y or M here.
+	  If unsure, say N.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 5147467ec825..08a281009d1d 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -13,6 +13,7 @@  obj-$(CONFIG_MMC_MXS)		+= mxs-mmc.o
 obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o
 obj-$(CONFIG_MMC_SDHCI_UHS2)	+= sdhci-uhs2.o
 obj-$(CONFIG_MMC_SDHCI_PCI)	+= sdhci-pci.o
+obj-$(CONFIG_MMC_SDHCI_BST)	        += sdhci-of-bst-c1200.o
 sdhci-pci-y			+= sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \
 				   sdhci-pci-dwc-mshc.o sdhci-pci-gli.o
 obj-$(CONFIG_MMC_SDHCI_ACPI)	+= sdhci-acpi.o
diff --git a/drivers/mmc/host/sdhci-of-bst-c1200.c b/drivers/mmc/host/sdhci-of-bst-c1200.c
new file mode 100644
index 000000000000..c6d2e6273e96
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-bst-c1200.c
@@ -0,0 +1,920 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Black Sesame Technologies SDHCI driver
+ *
+ * Copyright (C) 2024 Black Sesame Technologies. All Rights Reserved.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/regulator/consumer.h>
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+struct dwcmshc_priv {
+	u32 phy_crm_reg_base;
+	u32 phy_crm_reg_size;
+};
+
+#define SDHCI_CLOCK_PLL_EN		0x0008
+#define SDHCI_TUNING_COUNT		0x20
+#define SDHCI_VENDOR_PTR_R		0xE8
+#define MBIU_CTRL			0x510
+#define BURST_INCR16_EN			BIT(3)
+#define BURST_INCR8_EN			BIT(2)
+#define BURST_INCR4_EN			BIT(1)
+#define BURST_EN			(BURST_INCR16_EN | BURST_INCR8_EN | BURST_INCR4_EN)
+
+/* Synopsys vendor specific registers */
+#define reg_offset_addr_vendor		(sdhci_readw(host, SDHCI_VENDOR_PTR_R))
+#define SDHC_MHSC_VER_ID_R		(reg_offset_addr_vendor)
+#define SDHC_MHSC_VER_TPYE_R		(reg_offset_addr_vendor + 0x4)
+#define SDHC_MHSC_CTRL_R		(reg_offset_addr_vendor + 0x8)
+#define SDHC_MBIU_CTRL_R		(reg_offset_addr_vendor + 0x10)
+#define SDHC_EMMC_CTRL_R		(reg_offset_addr_vendor + 0x2C)
+#define SDHC_BOOT_CTRL_R		(reg_offset_addr_vendor + 0x2E)
+#define SDHC_GP_IN_R			(reg_offset_addr_vendor + 0x30)
+#define SDHC_GP_OUT_R			(reg_offset_addr_vendor + 0x34)
+#define SDHC_AT_CTRL_R			(reg_offset_addr_vendor + 0x40)
+#define SDHC_AT_STAT_R			(reg_offset_addr_vendor + 0x44)
+
+#define SDEMMC_CRM_BCLK_DIV_CTRL	0x08
+#define SDEMMC_CRM_RX_CLK_CTRL		0x14
+#define SDEMMC_CRM_TIMER_DIV_CTRL	0x0C
+#define SDEMMC_CRM_VOL_CTRL		0x1C
+#define DRIVER_NAME			"sdhci_bst"
+#define BST_VOL_STABLE_ON		(0x1 << 7)
+#define SDHCI_DUMP_BST(f, x...)		\
+	pr_info("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ##x)
+#define SD_3_3V				0
+#define SD_1_8V				1
+#define default_max_freq		200000ul
+
+void sdhci_bst_print_vendor(struct sdhci_host *host)
+{
+	SDHCI_DUMP_BST("============ SDHCI VENDOR REGISTER DUMP ===========\n");
+
+	SDHCI_DUMP_BST("VER_ID:  0x%08x | VER_TPYE:  0x%08x\n",
+		       sdhci_readl(host, SDHC_MHSC_VER_ID_R),
+		       sdhci_readl(host, SDHC_MHSC_VER_TPYE_R));
+	SDHCI_DUMP_BST("MHSC_CTRL:  0x%08x | MBIU_CTRL:  0x%08x\n",
+		       sdhci_readw(host, SDHC_MHSC_CTRL_R),
+		       sdhci_readw(host, SDHC_MBIU_CTRL_R));
+	SDHCI_DUMP_BST("EMMC_CTRL:  0x%08x | BOOT_CTRL: 0x%08x\n",
+		       sdhci_readl(host, SDHC_EMMC_CTRL_R),
+		       sdhci_readw(host, SDHC_BOOT_CTRL_R));
+	SDHCI_DUMP_BST("GP_IN:   0x%08x | GP_OUT: 0x%08x\n",
+		       sdhci_readl(host, SDHC_GP_IN_R),
+		       sdhci_readb(host, SDHC_GP_OUT_R));
+	SDHCI_DUMP_BST("AT_CTRL:     0x%08x | AT_STAT:  0x%08x\n",
+		       sdhci_readb(host, SDHC_AT_CTRL_R),
+		       sdhci_readb(host, SDHC_AT_STAT_R));
+}
+EXPORT_SYMBOL_GPL(sdhci_bst_print_vendor);
+
+static u32 bst_read_phys_bst(u32 phys_addr)
+{
+	u32 phys_addr_page = phys_addr & 0xFFFFE000;
+	u32 phys_offset = phys_addr & 0x00001FFF;
+	u32 map_size = phys_offset + sizeof(u32);
+	u32 ret = 0xDEADBEEF;
+	void *mem_mapped = ioremap(phys_addr_page, map_size);
+
+	if (mem_mapped) {
+		ret = (u32)ioread32(((u8 *)mem_mapped) + phys_offset);
+		iounmap(mem_mapped);
+	}
+
+	return ret;
+}
+
+static void bst_write_phys_bst(u32 phys_addr, u32 value)
+{
+	u32 phys_addr_page = phys_addr & 0xFFFFE000;
+	u32 phys_offset = phys_addr & 0x00001FFF;
+	u32 map_size = phys_offset + sizeof(u32);
+	void *mem_mapped = ioremap(phys_addr_page, map_size);
+
+	if (mem_mapped) {
+		iowrite32(value, ((u8 *)mem_mapped) + phys_offset);
+		iounmap(mem_mapped);
+	}
+}
+
+static unsigned int bst_get_max_clock(struct sdhci_host *host)
+{
+	return host->mmc->f_max;
+}
+
+static unsigned int bst_get_min_clock(struct sdhci_host *host)
+{
+	return host->mmc->f_min;
+}
+
+struct rx_ctrl {
+	struct {
+		u32 rx_revert:1;
+		u32 rx_clk_sel_sec:1;
+		u32 rx_clk_div:4;
+		u32 rx_clk_phase_inner:2;
+		u32 rx_clk_sel_first:1;
+		u32 rx_clk_phase_out:2;
+		u32 rx_clk_en:1;
+		u32 res0:20;
+	} bit;
+	u32 reg;
+};
+
+struct sdmmc_iocfg {
+	struct {
+		u32 res0:16;
+		u32 SC_SDMMC0_PVDD18POCSD0:2;
+		u32 SC_SDMMC0_PVDD18POCSD1:2;
+		u32 SC_SDMMC0_PVDD18POCSD2:2;
+		u32 SC_SDMMC1_PVDD18POCSD0:2;
+		u32 SC_SDMMC1_PVDD18POCSD1:2;
+		u32 SC_SDMMC1_PVDD18POCSD2:2;
+		u32 res1:4;
+	} bit;
+	u32 reg;
+};
+
+void sdhci_enable_bst_clk(struct sdhci_host *host, unsigned int clk)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct dwcmshc_priv *priv;
+	unsigned int div;
+	u32 val;
+	struct rx_ctrl rx_reg;
+
+	pltfm_host = sdhci_priv(host);
+	priv = sdhci_pltfm_priv(pltfm_host);
+	if (clk == 0) {
+		div = clk;
+	} else if (clk > default_max_freq) {
+		div = clk / 1000;
+		div = default_max_freq / div;
+	} else if (clk < 1500) {
+		div = clk;
+	} else {
+		div = default_max_freq * 100;
+		div = div / clk;
+		div /= 100;
+	}
+
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	clk &= ~SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	clk &= ~SDHCI_CLOCK_PLL_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL);
+	val &= ~(1 << 8);
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL, val);
+
+	val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL);
+	val &= ~(0xff);
+	val |= 0x20;
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL, val);
+
+	val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL);
+	val |= 1 << 8;
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_TIMER_DIV_CTRL, val);
+
+	val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL);
+	val &= ~(1 << 11);
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL, val);
+
+	rx_reg.reg = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL);
+
+	rx_reg.bit.rx_revert = 0;
+	rx_reg.bit.rx_clk_sel_sec = 1;
+	rx_reg.bit.rx_clk_div = 4;
+	rx_reg.bit.rx_clk_phase_inner = 2;
+	rx_reg.bit.rx_clk_sel_first = 0;
+	rx_reg.bit.rx_clk_phase_out = 2;
+
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL, rx_reg.reg);
+
+	val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL);
+	val |= 1 << 11;
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_RX_CLK_CTRL, val);
+
+	/* Disable clock first */
+	val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL);
+	val &= ~0x0400;
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL, val);
+
+	/* Setup clock divider */
+	val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL);
+	val &= ~0x03ff;
+	val |= div;
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL, val);
+
+	/* Enable clock */
+	val = bst_read_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL);
+	val |= 0x0400;
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_BCLK_DIV_CTRL, val);
+
+	sdhci_writew(host, (div & 0xff) << 8, SDHCI_CLOCK_CONTROL);
+
+	sdhci_writew(host, (div & 0xff) << 8, SDHCI_CLOCK_CONTROL);
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	clk |= SDHCI_CLOCK_PLL_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	clk |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	clk |= SDHCI_CLOCK_INT_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+void sdhci_set_bst_clock(struct sdhci_host *host, unsigned int clock)
+{
+	if (clock == 0)
+		return;
+	sdhci_enable_bst_clk(host, clock);
+}
+
+/**
+ * sdhci_bst_reset - Reset the SDHCI host controller
+ * @host: SDHCI host controller
+ * @mask: Reset mask
+ *
+ * Performs a reset of the SDHCI host controller with special handling for eMMC.
+ */
+static void sdhci_bst_reset(struct sdhci_host *host, u8 mask)
+{
+	if (host->mmc->caps2 & MMC_CAP2_NO_SD) {
+		sdhci_writew(host,
+			     sdhci_readw(host, SDHC_EMMC_CTRL_R) & (~BIT(2)),
+			     SDHC_EMMC_CTRL_R);
+		sdhci_reset(host, mask);
+		usleep_range(10, 20);
+		sdhci_writew(host,
+			     sdhci_readw(host, SDHC_EMMC_CTRL_R) | BIT(2),
+			     SDHC_EMMC_CTRL_R);
+	} else {
+		sdhci_reset(host, mask);
+	}
+}
+
+/**
+ * sdhci_bst_timeout - Set timeout value for commands
+ * @host: SDHCI host controller
+ * @cmd: MMC command
+ *
+ * Sets the timeout control register to maximum value (0xE).
+ */
+static void sdhci_bst_timeout(struct sdhci_host *host, struct mmc_command *cmd)
+{
+	sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL);
+}
+
+/**
+ * sdhci_bst_set_power - Set power mode and voltage
+ * @host: SDHCI host controller
+ * @mode: Power mode to set
+ * @vdd: Voltage to set
+ *
+ * Sets power mode and voltage, also configures MBIU control register.
+ */
+static void sdhci_bst_set_power(struct sdhci_host *host, unsigned char mode,
+				unsigned short vdd)
+{
+	sdhci_set_power(host, mode, vdd);
+	sdhci_writeb(host, 0xF, SDHCI_POWER_CONTROL);
+	sdhci_writew(host,
+		     (sdhci_readw(host, MBIU_CTRL) & (~0xf)) | BURST_EN,
+		     MBIU_CTRL);
+}
+
+/**
+ * bst_sdhci_execute_tuning - Execute tuning procedure
+ * @host: SDHCI host controller
+ * @opcode: Opcode to use for tuning
+ *
+ * Performs tuning procedure by trying different values and selecting the best one.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int bst_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct dwcmshc_priv *priv;
+	unsigned int clk = 0, timeout;
+	int ret = 0, error;
+	int start0 = -1, end0 = -1, best = 0;
+	int start1 = -1, end1 = -1, flag = 0;
+	int i;
+	u32 val;
+
+	pltfm_host = sdhci_priv(host);
+	priv = sdhci_pltfm_priv(pltfm_host);
+
+	for (i = 0; i < SDHCI_TUNING_COUNT; i++) {
+		/* Protected write */
+		bst_write_phys_bst(priv->phy_crm_reg_base + 0x88, 0x1234abcd);
+		/* Write tuning value */
+		bst_write_phys_bst(priv->phy_crm_reg_base + 0x94,
+				   (1ul << i) - 1);
+
+		timeout = 20;
+		while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) &
+			SDHCI_CLOCK_INT_STABLE)) {
+			if (timeout == 0) {
+				pr_err("%s: Internal clock never stabilised.\n",
+				       __func__);
+				return -EBUSY;
+			}
+			timeout--;
+			usleep_range(1000, 1100);
+		}
+
+		ret = mmc_send_tuning(host->mmc, opcode, &error);
+		if (ret != 0) {
+			flag = 1;
+		} else {
+			if (flag == 0) {
+				if (start0 == -1)
+					start0 = i;
+				end0 = i;
+			} else {
+				if (start1 == -1)
+					start1 = i;
+				end1 = i;
+			}
+		}
+	}
+
+	/* Calculate best tuning value */
+	if (end0 - start0 >= end1 - start1)
+		best = ((end0 - start0) >> 1) + start0;
+	else
+		best = ((end1 - start1) >> 1) + start1;
+
+	if (best < 0)
+		best = 0;
+
+	bst_write_phys_bst(priv->phy_crm_reg_base + 0x94, (1ul << best) - 1);
+	timeout = 20;
+
+	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) &
+		SDHCI_CLOCK_INT_STABLE)) {
+		if (timeout == 0) {
+			pr_err("%s: Internal clock never stabilised.\n",
+			       __func__);
+			return -EBUSY;
+		}
+		timeout--;
+		usleep_range(1000, 1100);
+	}
+
+	return 0;
+}
+
+/**
+ * sdhci_bst_voltage_switch - Perform voltage switch
+ * @host: SDHCI host controller
+ *
+ * Enables voltage stable power.
+ */
+static void sdhci_bst_voltage_switch(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	/* vol stable power on */
+	bst_write_phys_bst(priv->phy_crm_reg_base + SDEMMC_CRM_VOL_CTRL,
+			   BST_VOL_STABLE_ON);
+}
+
+static const struct sdhci_ops sdhci_dwcmshc_ops = {
+	.set_clock		= sdhci_set_bst_clock,
+	.set_bus_width		= sdhci_set_bus_width,
+	.set_uhs_signaling	= sdhci_set_uhs_signaling,
+	.get_min_clock		= bst_get_min_clock,
+	.get_max_clock		= bst_get_max_clock,
+	.reset			= sdhci_bst_reset,
+	.set_power		= sdhci_bst_set_power,
+	.set_timeout		= sdhci_bst_timeout,
+	.platform_execute_tuning = bst_sdhci_execute_tuning,
+	.voltage_switch		= sdhci_bst_voltage_switch,
+};
+
+static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
+	.ops = &sdhci_dwcmshc_ops,
+	.quirks = SDHCI_QUIRK_DELAY_AFTER_POWER |
+		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+		  SDHCI_QUIRK_INVERTED_WRITE_PROTECT,
+	.quirks2 = SDHCI_QUIRK2_BROKEN_DDR50 |
+		   SDHCI_QUIRK2_TUNING_WORK_AROUND |
+		   SDHCI_QUIRK2_ACMD23_BROKEN,
+};
+
+static void bst_sdhci_allocate_bounce_buffer(struct sdhci_host *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	unsigned int max_blocks;
+	unsigned int bounce_size;
+	int ret;
+
+	/*
+	 * Cap the bounce buffer at 64KB. Using a bigger bounce buffer
+	 * has diminishing returns, this is probably because SD/MMC
+	 * cards are usually optimized to handle this size of requests.
+	 */
+	bounce_size = SZ_32K;
+	/*
+	 * Adjust downwards to maximum request size if this is less
+	 * than our segment size, else hammer down the maximum
+	 * request size to the maximum buffer size.
+	 */
+	if (mmc->max_req_size < bounce_size)
+		bounce_size = mmc->max_req_size;
+	max_blocks = bounce_size / 512;
+
+	ret = of_reserved_mem_device_init_by_idx(mmc_dev(mmc), mmc_dev(mmc)->of_node, 0);
+	if (ret) {
+		dev_err(mmc_dev(mmc), "of_reserved_mem_device_init error\n");
+		return;
+	}
+	host->bounce_buffer = dma_alloc_coherent(mmc_dev(mmc), bounce_size,
+						 &host->bounce_addr, GFP_KERNEL);
+
+	ret = dma_mapping_error(mmc_dev(mmc), host->bounce_addr);
+	if (ret) {
+		devm_kfree(mmc_dev(mmc), host->bounce_buffer);
+		host->bounce_buffer = NULL;
+		/* Again fall back to max_segs == 1 */
+		return;
+	}
+
+	host->bounce_buffer_size = bounce_size;
+
+	/* Lie about this since we're bouncing */
+	mmc->max_segs = max_blocks;
+	mmc->max_seg_size = bounce_size;
+	mmc->max_req_size = bounce_size;
+
+	pr_info("BST reallocate %s bounce up to %u segments into one, max segment size %u bytes\n",
+		mmc_hostname(mmc), max_blocks, bounce_size);
+}
+
+static int bst_sdhci_set_dma_mask(struct sdhci_host *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct device *dev = mmc_dev(mmc);
+	int ret = -EINVAL;
+
+	if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA)
+		host->flags &= ~SDHCI_USE_64_BIT_DMA;
+
+	/* Try 64-bit mask if hardware is capable  of it */
+	if (host->flags & SDHCI_USE_64_BIT_DMA) {
+		ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+		if (ret) {
+			pr_warn("%s: Failed to set 64-bit DMA mask.\n",
+				mmc_hostname(mmc));
+			host->flags &= ~SDHCI_USE_64_BIT_DMA;
+		}
+	}
+
+	/* 32-bit mask as default & fallback */
+	if (ret) {
+		ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+		if (ret)
+			pr_warn("%s: Failed to set 32-bit DMA mask.\n",
+				mmc_hostname(mmc));
+	}
+
+	return ret;
+}
+
+int bst_sdhci_setup_host(struct sdhci_host *host)
+{
+	struct mmc_host *mmc;
+	u32 max_current_caps;
+	unsigned int ocr_avail;
+	unsigned int override_timeout_clk;
+	u32 max_clk;
+	int ret = 0;
+	bool enable_vqmmc = false;
+
+	WARN_ON(!host);
+	if (!host)
+		return -EINVAL;
+
+	mmc = host->mmc;
+
+	/*
+	 * If there are external regulators, get them. Note this must be done
+	 * early before resetting the host and reading the capabilities so that
+	 * the host can take the appropriate action if regulators are not
+	 * available.
+	 */
+	if (!mmc->supply.vqmmc) {
+		ret = mmc_regulator_get_supply(mmc);
+		if (ret)
+			return ret;
+		enable_vqmmc  = true;
+	}
+
+	pr_info("Version:   0x%08x | Present:  0x%08x\n",
+		sdhci_readw(host, SDHCI_HOST_VERSION),
+		sdhci_readl(host, SDHCI_PRESENT_STATE));
+	pr_info("Caps:      0x%08x | Caps_1:   0x%08x\n",
+		sdhci_readl(host, SDHCI_CAPABILITIES),
+		sdhci_readl(host, SDHCI_CAPABILITIES_1));
+
+	sdhci_read_caps(host);
+
+	override_timeout_clk = host->timeout_clk;
+
+	host->flags |= SDHCI_USE_SDMA;
+
+	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+		if (host->ops->set_dma_mask)
+			ret = host->ops->set_dma_mask(host);
+		else
+			ret = bst_sdhci_set_dma_mask(host);
+
+		if (!ret && host->ops->enable_dma)
+			ret = host->ops->enable_dma(host);
+
+		if (ret) {
+			pr_warn("%s: No suitable DMA available - falling back to PIO\n",
+				mmc_hostname(mmc));
+			host->flags &= ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA);
+
+			ret = 0;
+		}
+	}
+
+	if (host->flags & SDHCI_USE_ADMA) {
+		dma_addr_t dma;
+		void *buf;
+
+		if (!(host->flags & SDHCI_USE_64_BIT_DMA))
+			host->alloc_desc_sz = SDHCI_ADMA2_32_DESC_SZ;
+		else if (!host->alloc_desc_sz)
+			host->alloc_desc_sz = SDHCI_ADMA2_64_DESC_SZ(host);
+
+		host->desc_sz = host->alloc_desc_sz;
+		host->adma_table_sz = host->adma_table_cnt * host->desc_sz;
+
+		host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
+		/*
+		 * Use zalloc to zero the reserved high 32-bits of 128-bit
+		 * descriptors so that they never need to be written.
+		 */
+		buf = dma_alloc_coherent(mmc_dev(mmc),
+					 host->align_buffer_sz + host->adma_table_sz,
+					 &dma, GFP_KERNEL);
+		if (!buf) {
+			pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
+				mmc_hostname(mmc));
+			host->flags &= ~SDHCI_USE_ADMA;
+		} else if ((dma + host->align_buffer_sz) &
+			   (SDHCI_ADMA2_DESC_ALIGN - 1)) {
+			pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
+				mmc_hostname(mmc));
+			host->flags &= ~SDHCI_USE_ADMA;
+			dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
+					  host->adma_table_sz, buf, dma);
+		} else {
+			host->align_buffer = buf;
+			host->align_addr = dma;
+
+			host->adma_table = buf + host->align_buffer_sz;
+			host->adma_addr = dma + host->align_buffer_sz;
+		}
+	}
+
+	host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps);
+
+	host->max_clk *= 1000000;
+	if (host->max_clk == 0 || host->quirks &
+			SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
+		if (!host->ops->get_max_clock) {
+			pr_err("%s: Hardware doesn't specify base clock frequency.\n",
+			       mmc_hostname(mmc));
+			ret = -ENODEV;
+			goto undma;
+		}
+		host->max_clk = host->ops->get_max_clock(host);
+	}
+
+	/*
+	 * Set host parameters.
+	 */
+	max_clk = host->max_clk;
+
+	if (host->ops->get_min_clock)
+		mmc->f_min = host->ops->get_min_clock(host);
+
+	if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
+		host->timeout_clk = FIELD_GET(SDHCI_TIMEOUT_CLK_MASK, host->caps);
+
+		if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
+			host->timeout_clk *= 1000;
+
+		if (host->timeout_clk == 0) {
+			if (!host->ops->get_timeout_clock) {
+				pr_err("%s: Hardware doesn't specify timeout clock frequency.\n",
+				       mmc_hostname(mmc));
+				ret = -ENODEV;
+				goto undma;
+			}
+
+			host->timeout_clk =
+				DIV_ROUND_UP(host->ops->get_timeout_clock(host),
+					     1000);
+		}
+
+		if (override_timeout_clk)
+			host->timeout_clk = override_timeout_clk;
+
+		mmc->max_busy_timeout = host->ops->get_max_timeout_count ?
+			host->ops->get_max_timeout_count(host) : 1 << 27;
+		mmc->max_busy_timeout /= host->timeout_clk;
+	}
+
+	mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23;
+	mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+
+	if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
+		host->flags |= SDHCI_AUTO_CMD12;
+
+	/*
+	 * A controller may support 8-bit width, but the board itself
+	 * might not have the pins brought out.  Boards that support
+	 * 8-bit width must set "mmc->caps |= MMC_CAP_8_BIT_DATA;" in
+	 * their platform code before calling sdhci_add_host(), and we
+	 * won't assume 8-bit width for hosts without that CAP.
+	 */
+	if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
+		mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
+		mmc->caps &= ~MMC_CAP_CMD23;
+
+	if (host->caps & SDHCI_CAN_DO_HISPD)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+
+	/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
+	if (host->caps1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
+			   SDHCI_SUPPORT_DDR50))
+		mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+
+	/* SDR104 supports also implies SDR50 support */
+	if (host->caps1 & SDHCI_SUPPORT_SDR104) {
+		mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
+		/* SD3.0: SDR104 is supported so (for eMMC) the caps2
+		 * field can be promoted to support HS200.
+		 */
+		if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
+			mmc->caps2 |= MMC_CAP2_HS200;
+	}
+
+	if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
+	    (IS_ERR(mmc->supply.vqmmc) ||
+	     !regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000,
+					     1300000)))
+		mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
+
+	/* Does the host need tuning for SDR50? */
+	if (host->caps1 & SDHCI_USE_SDR50_TUNING)
+		host->flags |= SDHCI_SDR50_NEEDS_TUNING;
+
+	/* Driver Type(s) (A, C, D) supported by the host */
+	if (host->caps1 & SDHCI_DRIVER_TYPE_A)
+		mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
+	if (host->caps1 & SDHCI_DRIVER_TYPE_C)
+		mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
+	if (host->caps1 & SDHCI_DRIVER_TYPE_D)
+		mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
+
+	/* Initial value for re-tuning timer count */
+	host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK,
+				       host->caps1);
+
+	/*
+	 * In case Re-tuning Timer is not disabled, the actual value of
+	 * re-tuning timer will be 2 ^ (n - 1).
+	 */
+	if (host->tuning_count)
+		host->tuning_count = 1 << (host->tuning_count - 1);
+
+	/* Re-tuning mode supported by the Host Controller */
+	host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1);
+
+	ocr_avail = 0;
+
+	if (host->caps & SDHCI_CAN_VDD_330) {
+		ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
+
+		mmc->max_current_330 = FIELD_GET(SDHCI_MAX_CURRENT_330_MASK,
+						 max_current_caps) *
+						SDHCI_MAX_CURRENT_MULTIPLIER;
+	}
+	if (host->caps & SDHCI_CAN_VDD_300) {
+		ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
+
+		mmc->max_current_300 = FIELD_GET(SDHCI_MAX_CURRENT_300_MASK,
+						 max_current_caps) *
+						SDHCI_MAX_CURRENT_MULTIPLIER;
+	}
+	if (host->caps & SDHCI_CAN_VDD_180) {
+		ocr_avail |= MMC_VDD_165_195;
+
+		mmc->max_current_180 = FIELD_GET(SDHCI_MAX_CURRENT_180_MASK,
+						 max_current_caps) *
+						SDHCI_MAX_CURRENT_MULTIPLIER;
+	}
+
+	/* If OCR set by host, use it instead. */
+	if (host->ocr_mask)
+		ocr_avail = host->ocr_mask;
+
+	/* If OCR set by external regulators, give it highest prio. */
+	if (mmc->ocr_avail)
+		ocr_avail = mmc->ocr_avail;
+
+	mmc->ocr_avail = ocr_avail;
+	mmc->ocr_avail_sdio = ocr_avail;
+	if (host->ocr_avail_sdio)
+		mmc->ocr_avail_sdio &= host->ocr_avail_sdio;
+	mmc->ocr_avail_sd = ocr_avail;
+	if (host->ocr_avail_sd)
+		mmc->ocr_avail_sd &= host->ocr_avail_sd;
+	else /* normal SD controllers don't support 1.8V */
+		mmc->ocr_avail_sd &= ~MMC_VDD_165_195;
+	mmc->ocr_avail_mmc = ocr_avail;
+	if (host->ocr_avail_mmc)
+		mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
+
+	if ((mmc->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+			  MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+			  MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR)) ||
+	    (mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)))
+		host->flags |= SDHCI_SIGNALING_180;
+
+	spin_lock_init(&host->lock);
+
+	/*
+	 * Maximum number of sectors in one transfer. Limited by SDMA boundary
+	 * size (512KiB). Note some tuning modes impose a 4MiB limit, but this
+	 * is less anyway.
+	 */
+	mmc->max_req_size = 524288;
+	/*
+	 * Maximum number of segments. Depends on if the hardware
+	 * can do scatter/gather or not.
+	 */
+	mmc->max_segs = 1;
+	mmc->max_req_size = min_t(size_t, mmc->max_req_size,
+				  dma_max_mapping_size(mmc_dev(mmc)));
+
+	mmc->max_seg_size = mmc->max_req_size;
+
+	mmc->max_blk_size = (host->caps & SDHCI_MAX_BLOCK_MASK) >>
+			SDHCI_MAX_BLOCK_SHIFT;
+
+	mmc->max_blk_size = 512 << mmc->max_blk_size;
+
+	/*
+	 * Maximum block count.
+	 */
+	mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
+
+	if (mmc->max_segs == 1)
+		/* This may alter mmc->*_blk_* parameters */
+		// bst sdhci must reallocate bounce buffer
+		bst_sdhci_allocate_bounce_buffer(host);
+
+	return 0;
+
+undma:
+	if (host->align_buffer)
+		dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
+				  host->adma_table_sz, host->align_buffer,
+				  host->align_addr);
+	host->adma_table = NULL;
+	host->align_buffer = NULL;
+
+	return ret;
+}
+
+static int bst_sdhci_add_host(struct sdhci_host *host)
+{
+	int ret;
+
+	ret = bst_sdhci_setup_host(host);
+	if (ret)
+		return ret;
+
+	ret = __sdhci_add_host(host);
+	if (ret)
+		goto cleanup;
+
+	return 0;
+
+cleanup:
+	sdhci_cleanup_host(host);
+
+	return ret;
+}
+
+/**
+ * dwcmshc_probe - Platform driver probe
+ * @pdev: Platform device
+ *
+ * Initializes the SDHCI host controller and registers it.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int dwcmshc_probe(struct platform_device *pdev)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct sdhci_host *host;
+	struct dwcmshc_priv *priv;
+	int err;
+
+	host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata,
+				sizeof(struct dwcmshc_priv));
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	pltfm_host = sdhci_priv(host);
+	priv = sdhci_pltfm_priv(pltfm_host);
+
+	err = mmc_of_parse(host->mmc);
+	if (err)
+		goto err_clk;
+
+	sdhci_get_of_property(pdev);
+	device_property_read_u32(&pdev->dev, "mmc_crm_base",
+				 &priv->phy_crm_reg_base);
+	device_property_read_u32(&pdev->dev, "mmc_crm_size",
+				 &priv->phy_crm_reg_size);
+
+	err = bst_sdhci_add_host(host);
+	if (err)
+		goto err_clk;
+
+	return 0;
+
+err_clk:
+	sdhci_pltfm_free(pdev);
+	return err;
+}
+
+/**
+ * dwcmshc_remove - Platform driver remove
+ * @pdev: Platform device
+ *
+ * Removes the SDHCI host controller.
+ *
+ * Return: 0 on success
+ */
+static void dwcmshc_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+
+	sdhci_remove_host(host, 0);
+	sdhci_pltfm_free(pdev);
+}
+
+static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
+	{ .compatible = "bst,dwcmshc-sdhci" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
+
+static struct platform_driver sdhci_dwcmshc_driver = {
+	.driver = {
+		.name = "sdhci-dwcmshc",
+		.of_match_table = sdhci_dwcmshc_dt_ids,
+	},
+	.probe = dwcmshc_probe,
+	.remove = dwcmshc_remove,
+};
+module_platform_driver(sdhci_dwcmshc_driver);
+
+MODULE_DESCRIPTION("SDHCI platform driver for BST DWC MSHC");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("BST Ltd.");