* [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
@ 2025-05-28 8:54 Albert Yang
2025-05-28 9:06 ` Geert Uytterhoeven
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Albert Yang @ 2025-05-28 8:54 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, Ge Gordon
Cc: BST Linux Kernel Upstream Group, linux-mmc, linux-arm-kernel,
linux-kernel, Geert Uytterhoeven, Victor Shih, Shan-Chun Hung,
Arnd Bergmann, AngeloGioacchino Del Regno, Peter Robinson,
Ben Chuang, Albert Yang
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
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.");
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
2025-05-28 8:54 [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver Albert Yang
@ 2025-05-28 9:06 ` Geert Uytterhoeven
2025-05-28 22:56 ` kernel test robot
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Geert Uytterhoeven @ 2025-05-28 9:06 UTC (permalink / raw)
To: Albert Yang
Cc: Ulf Hansson, Adrian Hunter, Ge Gordon,
BST Linux Kernel Upstream Group, linux-mmc, linux-arm-kernel,
linux-kernel, Geert Uytterhoeven, Victor Shih, Shan-Chun Hung,
Arnd Bergmann, AngeloGioacchino Del Regno, Peter Robinson,
Ben Chuang
Hi Albert,
On Wed, 28 May 2025 at 10:55, Albert Yang <yangzh0906@thundersoft.com> wrote:
> 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>
Thanks for your patch!
> --- 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 ARCH_BST || COMPILE_TEST
> + 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.
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-of-bst-c1200.c
> +#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);
Why do you need this?
> +
> +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);
> + }
Please do not ioremap()/iounmap() for each register read...
> +
> + 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);
> + }
or write...
> +}
Aborting review.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
2025-05-28 8:54 [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver Albert Yang
2025-05-28 9:06 ` Geert Uytterhoeven
@ 2025-05-28 22:56 ` kernel test robot
2025-05-29 1:22 ` kernel test robot
2025-06-16 11:19 ` Adrian Hunter
3 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-05-28 22:56 UTC (permalink / raw)
To: Albert Yang, Ulf Hansson, Adrian Hunter, Ge Gordon
Cc: oe-kbuild-all, BST Linux Kernel Upstream Group, linux-mmc,
linux-arm-kernel, linux-kernel, Geert Uytterhoeven, Victor Shih,
Shan-Chun Hung, Arnd Bergmann, AngeloGioacchino Del Regno,
Peter Robinson, Ben Chuang, Albert Yang
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
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
2025-05-28 8:54 [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver Albert Yang
2025-05-28 9:06 ` Geert Uytterhoeven
2025-05-28 22:56 ` kernel test robot
@ 2025-05-29 1:22 ` kernel test robot
2025-06-16 11:19 ` Adrian Hunter
3 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-05-29 1:22 UTC (permalink / raw)
To: Albert Yang, Ulf Hansson, Adrian Hunter, Ge Gordon
Cc: llvm, oe-kbuild-all, BST Linux Kernel Upstream Group, linux-mmc,
linux-arm-kernel, linux-kernel, Geert Uytterhoeven, Victor Shih,
Shan-Chun Hung, Arnd Bergmann, AngeloGioacchino Del Regno,
Peter Robinson, Ben Chuang, Albert Yang
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
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
2025-05-28 8:54 [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver Albert Yang
` (2 preceding siblings ...)
2025-05-29 1:22 ` kernel test robot
@ 2025-06-16 11:19 ` Adrian Hunter
[not found] ` <202506271822530452465@thundersoft.com>
3 siblings, 1 reply; 8+ messages in thread
From: Adrian Hunter @ 2025-06-16 11:19 UTC (permalink / raw)
To: Albert Yang, Ulf Hansson, Ge Gordon
Cc: BST Linux Kernel Upstream Group, linux-mmc, linux-arm-kernel,
linux-kernel, Geert Uytterhoeven, Victor Shih, Shan-Chun Hung,
Arnd Bergmann, AngeloGioacchino Del Regno, Peter Robinson,
Ben Chuang
On 28/05/2025 11:54, Albert Yang wrote:
> 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
<SNIP>
> +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);
sdhci uses dma_sync_single_for_device() and dma_sync_single_for_cpu()
with this buffer. Does that really work?
> +
> + 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)
This is identical to sdhci_set_dma_mask() just just drop it.
> +{
> + 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)
It is not acceptable for the driver to have its own copy of
sdhci_setup().
Please describe what you need to customize and why.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
[not found] ` <202506271822530452465@thundersoft.com>
@ 2025-06-27 13:19 ` Adrian Hunter
2025-06-27 13:43 ` Arnd Bergmann
0 siblings, 1 reply; 8+ messages in thread
From: Adrian Hunter @ 2025-06-27 13:19 UTC (permalink / raw)
To: yangzh0906@thundersoft.com, ulf.hansson, gordon.ge
Cc: bst-upstream, linux-mmc, linux-arm-kernel, linux-kernel,
geert+renesas, victor.shih, shanchun1218, arnd,
angelogioacchino.delregno, pbrobinson, ben.chuang
On 27/06/2025 13:22, yangzh0906@thundersoft.com wrote:
> Dear Mr. Hunter,
>
> Our platform supports 64-bit physical addressing, but the eMMC controller's SRAM-based DMA engine is constrained to a 32-bit address space.
> When using the standard SDHCI interface, which allocates DDR-based DMA buffers with 64-bit addresses, thedma_map_single() operation fails
> because the DMA engine cannot handle addresses beyond 32 bits.
SDHCI controllers can use 32-bit DMA or 64-bit DMA, however even with
64-bit DMA it is possible to restrict the DMA addresses to 32-bits
by setting a 32-bit DMA mask.
If the host controller capabilities indicate support for 64-bit DMA
but you want the driver to use 32-bit DMA, set SDHCI_QUIRK2_BROKEN_64_BIT_DMA.
However, if you want to use 64-bit DMA with only 32-bit DMA addresses
you can instead implement sdhci host op ->set_dma_mask() and in that
function dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))
>
> To resolve this hardware limitation, we implement a bounce buffer allocated via dma_alloc_coherent() to satisfy DMA addressing constraints.
The bounce buffer should not be needed to satisfy DMA addressing
constraints. It is used when SDHCI ADMA (scatter/gather) is broken.
Also please be aware that "top-posting" is discouraged, refer:
https://www.kernel.org/doc/html/latest/process/submitting-patches.html
>
> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>>
>> Best regards,
>> Albert
>>
>
> *From:* Adrian Hunter <mailto:adrian.hunter@intel.com>
> *Date:* 2025-06-16 19:19
> *To:* Albert Yang <mailto:yangzh0906@thundersoft.com>; Ulf Hansson <mailto:ulf.hansson@linaro.org>; Ge Gordon <mailto:gordon.ge@bst.ai>
> *CC:* BST Linux Kernel Upstream Group <mailto:bst-upstream@bstai.top>; linux-mmc@vger.kernel.org <mailto:linux-mmc@vger.kernel.org>; linux-arm-kernel@lists.infradead.org <mailto:linux-arm-kernel@lists.infradead.org>; linux-kernel@vger.kernel.org <mailto:linux-kernel@vger.kernel.org>; Geert Uytterhoeven <mailto:geert+renesas@glider.be>; Victor Shih <mailto:victor.shih@genesyslogic.com.tw>; Shan-Chun Hung <mailto:shanchun1218@gmail.com>; Arnd Bergmann <mailto:arnd@arndb.de>; AngeloGioacchino Del Regno <mailto:angelogioacchino.delregno@collabora.com>; Peter Robinson <mailto:pbrobinson@gmail.com>; Ben Chuang <mailto:ben.chuang@genesyslogic.com.tw>
> *Subject:* Re: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
> On 28/05/2025 11:54, Albert Yang wrote:
> > 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
>
> <SNIP>
>
> > +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);
>
> sdhci uses dma_sync_single_for_device() and dma_sync_single_for_cpu()
> with this buffer. Does that really work?
>
> > +
> > + 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)
>
> This is identical to sdhci_set_dma_mask() just just drop it.
>
> > +{
> > + 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)
>
> It is not acceptable for the driver to have its own copy of
> sdhci_setup().
>
> Please describe what you need to customize and why.
>
>
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
2025-06-27 13:19 ` Adrian Hunter
@ 2025-06-27 13:43 ` Arnd Bergmann
[not found] ` <202507021657587315993@thundersoft.com>
0 siblings, 1 reply; 8+ messages in thread
From: Arnd Bergmann @ 2025-06-27 13:43 UTC (permalink / raw)
To: Adrian Hunter, yangzh0906@thundersoft.com, Ulf Hansson, gordon.ge
Cc: bst-upstream, linux-mmc @ vger . kernel . org, linux-arm-kernel,
linux-kernel, Geert Uytterhoeven, Victor Shih, Shan-Chun Hung,
AngeloGioacchino Del Regno, Peter Robinson, Ben Chuang
On Fri, Jun 27, 2025, at 15:19, Adrian Hunter wrote:
> On 27/06/2025 13:22, yangzh0906@thundersoft.com wrote:
>> Dear Mr. Hunter,
>>
>> Our platform supports 64-bit physical addressing, but the eMMC controller's SRAM-based DMA engine is constrained to a 32-bit address space.
>> When using the standard SDHCI interface, which allocates DDR-based DMA buffers with 64-bit addresses, thedma_map_single() operation fails
>> because the DMA engine cannot handle addresses beyond 32 bits.
dma_map_single() should always succeed on arm64 even if the buffer
is outside of the dma mask: On most modern SoCs there is an SMMU/IOMMU
that implements the actual mapping, and in the absence of that
there is a fallback to SWIOTLB, which is always built-in on arm64.
> SDHCI controllers can use 32-bit DMA or 64-bit DMA, however even with
> 64-bit DMA it is possible to restrict the DMA addresses to 32-bits
> by setting a 32-bit DMA mask.
>
> If the host controller capabilities indicate support for 64-bit DMA
> but you want the driver to use 32-bit DMA, set SDHCI_QUIRK2_BROKEN_64_BIT_DMA.
>
> However, if you want to use 64-bit DMA with only 32-bit DMA addresses
> you can instead implement sdhci host op ->set_dma_mask() and in that
> function dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))
I would not expect an standard SDHCI to be broken like this any more,
and if it is, I think the SDHCI_QUIRK2_BROKEN_64_BIT_DMA quirk is
more appropriate than overriding the dma_set_mask() operation.
What sometimes happens though is that the SoC integration itself is
broken, and a 64-bit capable DMA master like SDHCI is connected
to a 32-bit bus. In this case the DMA limitation should be
described in the device tree, using the "dma-ranges" property
of the broken bus node. The SDHCI code then still sets the correct
64-bit DMA mask according for the device, but the dma_map_single()
still uses an swiotlb bounce buffer or the IOMMU to work around
the bus restriction.
>> To resolve this hardware limitation, we implement a bounce buffer allocated via >> dma_alloc_coherent() to satisfy DMA addressing constraints.
>
> The bounce buffer should not be needed to satisfy DMA addressing
> constraints. It is used when SDHCI ADMA (scatter/gather) is broken.
I wonder if the actual problem here is not the addressing limit
but instead the coherency protocol. If the DMA master is cache
coherent but listed as non-coherent in DT, or vice versa, there will
be data corruption for all addresses, and using a dma_alloc_coherent()
bounce buffer may hide this.
Arnd
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver
[not found] ` <202507021657587315993@thundersoft.com>
@ 2025-07-02 9:51 ` Arnd Bergmann
0 siblings, 0 replies; 8+ messages in thread
From: Arnd Bergmann @ 2025-07-02 9:51 UTC (permalink / raw)
To: yangzh0906@thundersoft.com, Adrian Hunter, Ulf Hansson, gordon.ge
Cc: bst-upstream, linux-mmc @ vger . kernel . org, linux-arm-kernel,
linux-kernel, Geert Uytterhoeven, Victor Shih, Shan-Chun Hung,
AngeloGioacchino Del Regno, Peter Robinson, Ben Chuang
On Wed, Jul 2, 2025, at 10:57, yangzh0906@thundersoft.com wrote:
>>On Fri, Jun 27, 2025, at 15:19, Adrian Hunter wrote:
>>> On 27/06/2025 13:22, yangzh0906@thundersoft.com wrote:
>>
>>I would not expect an standard SDHCI to be broken like this any more,
>>and if it is, I think the SDHCI_QUIRK2_BROKEN_64_BIT_DMA quirk is
>>more appropriate than overriding the dma_set_mask() operation.
>
> I add SDHCI_QUIRK2_BROKEN_64_BIT_DMA in quirks2
> 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_64_BIT_DMA |
> SDHCI_QUIRK2_BROKEN_DDR50 |
> SDHCI_QUIRK2_TUNING_WORK_AROUND |
> SDHCI_QUIRK2_ACMD23_BROKEN,
> };
>>What sometimes happens though is that the SoC integration itself is
>>broken, and a 64-bit capable DMA master like SDHCI is connected
>>to a 32-bit bus. In this case the DMA limitation should be
>>described in the device tree, using the "dma-ranges" property
>>of the broken bus node. The SDHCI code then still sets the correct
>>64-bit DMA mask according for the device, but the dma_map_single()
>>still uses an swiotlb bounce buffer or the IOMMU to work around
>>the bus restriction.
>
> add the dma-ranges in dts node as below
> mmc0: mmc@22200000 {
> compatible = "bst,c1200-dwcmshc-sdhci";
> reg = <0x0 0x22200000 0x0 0x1000>,
> <0x0 0x23006000 0x0 0x1000>;
> interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>;
> clocks = <&clk_mmc>;
> clock-names = "core";
> max-frequency = <200000000>;
> bus-width = <8>;
> non-removable;
> dma-coherent;
> dma-ranges = <0x0 0x0 0x0 0x0 0x1 0x0>;
> };
The dma-ranges property makes no sense as part of the device.
It describes how DMA addresses are translated by a bus, so
it has to be part of the parent node.
As I tried to explain, you should only need to either set
the SDHCI device to 'SDHCI_QUIRK2_BROKEN_64_BIT_DMA' /or/
describe the bus translation, but not both.
Try to find out what the limitation is. If you have a datasheet
for the chip, it should either describe an erratum if the
sdhci device is known to be defective, or list the address
width of the internal link.
The 'dma-coherent' flag in the bst,c1200-dwcmshc-sdhci node is
suspicious as well: unless all DMA masters on this SoC are
cache-coherent, the mmc host is likely not the first thing
that the hardware designers used a coherent port for.
If you bounce all the transfers through an SRAM, it probably
still works correctly regardless of the dma-coherent flag,
but if you manage to configure ZONE_DMA32 RAM for SWIOTLB,
then the 'dma-coherent' flag must only be if the DMA master
is actually coherent with the CPU caches.
>>>> To resolve this hardware limitation, we implement a bounce buffer allocated via >> dma_alloc_coherent() to satisfy DMA addressing constraints.
>>>
>>> The bounce buffer should not be needed to satisfy DMA addressing
>>> constraints. It is used when SDHCI ADMA (scatter/gather) is broken.
>>
>>I wonder if the actual problem here is not the addressing limit
>>but instead the coherency protocol. If the DMA master is cache
>>coherent but listed as non-coherent in DT, or vice versa, there will
>>be data corruption for all addresses, and using a dma_alloc_coherent()
>>bounce buffer may hide this.
>
> Finally, the kernel will raise swiotlb_map error.
> I put the corresponding kernel log and driver files into it,
> https://drive.google.com/file/d/10HQE6lr3W-XyqoI7OUBjQzJxwPCaEkpz/view?usp=sharing,
> https://drive.google.com/file/d/1F8Hn2a7kPjZfSWiX525iSk634UHMo0Ph/view?usp=sharing
I see that there is no 32-bit addressable RAM in the system at
all, the lowest bit of system memory starts at 0x800254000 (32GB)
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000800254000-0x0000000800254fff]
[ 0.000000] node 0: [mem 0x0000000810000000-0x000000083fffffff]
[ 0.000000] node 0: [mem 0x00000008c0000000-0x00000009bfffffff]
[ 0.000000] node 0: [mem 0x0000000c00000000-0x0000000c3fffffff]
Maybe this is something that can be set up differently by the boot
loader, otherwise this won't be the only last problem with 32-bit
DMA you see on this chip.
Try to configure it so that there is at least 1GB of RAM in ZONE_DMA32,
or in the ideal case move all of the MMIO out of the way so all of
RAM can stay contiguous starting at a low address.
Arnd
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-07-02 9:52 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-28 8:54 [PATCH v1 5/9] mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver Albert Yang
2025-05-28 9:06 ` Geert Uytterhoeven
2025-05-28 22:56 ` kernel test robot
2025-05-29 1:22 ` kernel test robot
2025-06-16 11:19 ` Adrian Hunter
[not found] ` <202506271822530452465@thundersoft.com>
2025-06-27 13:19 ` Adrian Hunter
2025-06-27 13:43 ` Arnd Bergmann
[not found] ` <202507021657587315993@thundersoft.com>
2025-07-02 9:51 ` Arnd Bergmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).