[PATCH v1 9/9] mmc: sdhci-cadence: add Altera Agilex5 SD6HC support
From: Tanmay Kathpalia
Date: Mon May 11 2026 - 16:24:53 EST
The Altera Agilex5 SoC integrates a Cadence SD6HC controller that needs
platform-specific configuration to operate correctly. Its SMMU exposes
DRAM within a 40-bit address space, requiring the DMA mask to be capped
at 40 bits to prevent address allocation beyond the controller's reach.
The silicon also has hardware limitations that require the
MULTIBLOCK_READ_ACMD12, PRESET_VALUE_BROKEN, and ACMD23_BROKEN quirks.
Support is registered under the "altr,agilex5-sd6hc" compatible string
with Agilex5-specific ops and platform data.
Signed-off-by: Tanmay Kathpalia <tanmay.kathpalia@xxxxxxxxxx>
---
drivers/mmc/host/sdhci-cadence4.c | 52 +++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/drivers/mmc/host/sdhci-cadence4.c b/drivers/mmc/host/sdhci-cadence4.c
index 283d22328248..b6d35c165673 100644
--- a/drivers/mmc/host/sdhci-cadence4.c
+++ b/drivers/mmc/host/sdhci-cadence4.c
@@ -7,6 +7,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/dma-mapping.h>
#include <linux/module.h>
#include "sdhci-cadence.h"
@@ -84,6 +85,7 @@ struct sdhci_cdns4_phy_cfg {
struct sdhci_cdns_drv_data {
int (*init)(struct platform_device *pdev);
const struct sdhci_pltfm_data pltfm_data;
+ u64 dma_mask;
};
static const struct sdhci_cdns4_phy_cfg sdhci_cdns4_phy_cfgs[] = {
@@ -193,6 +195,31 @@ static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
return host->max_clk;
}
+/**
+ * sdhci_cdns_set_dma_mask - Set platform-specific DMA mask
+ * @host: SDHCI host controller
+ *
+ * Configure DMA mask based on platform capabilities to avoid IOMMU
+ * address allocation beyond controller's reach or unnecessary bounce
+ * buffers.
+ */
+static int sdhci_cdns_set_dma_mask(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ struct device *dev = mmc_dev(mmc);
+ const struct sdhci_cdns_drv_data *data = of_device_get_match_data(dev);
+ int ret;
+
+ if (!data->dma_mask)
+ return 0;
+
+ ret = dma_set_mask_and_coherent(dev, data->dma_mask);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set DMA mask\n");
+
+ return 0;
+}
+
static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
{
u32 tmp;
@@ -479,6 +506,17 @@ static const struct sdhci_ops sdhci_cdns6_ops = {
.hw_reset = sdhci_cdns6_hw_reset,
};
+static const struct sdhci_ops sdhci_cdns6_agilex5_ops = {
+ .set_clock = sdhci_set_clock,
+ .get_timeout_clock = sdhci_cdns_get_timeout_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .platform_execute_tuning = sdhci_cdns_execute_tuning,
+ .set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
+ .hw_reset = sdhci_cdns6_hw_reset,
+ .set_dma_mask = sdhci_cdns_set_dma_mask,
+};
+
static const struct sdhci_cdns_drv_data sdhci_cdns_uniphier_drv_data = {
.pltfm_data = {
.ops = &sdhci_cdns4_ops,
@@ -506,6 +544,16 @@ static const struct sdhci_cdns_drv_data sdhci_cdns4_drv_data = {
},
};
+static const struct sdhci_cdns_drv_data sdhci_cdns6_agilex5_drv_data = {
+ .pltfm_data = {
+ .ops = &sdhci_cdns6_agilex5_ops,
+ .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_ACMD23_BROKEN,
+ },
+ .dma_mask = DMA_BIT_MASK(40),
+};
+
static const struct sdhci_cdns_drv_data sdhci_cdns6_drv_data = {
.pltfm_data = {
.ops = &sdhci_cdns6_ops,
@@ -691,6 +739,10 @@ static const struct of_device_id sdhci_cdns_match[] = {
.compatible = "cdns,sd4hc",
.data = &sdhci_cdns4_drv_data,
},
+ {
+ .compatible = "altr,agilex5-sd6hc",
+ .data = &sdhci_cdns6_agilex5_drv_data,
+ },
{
.compatible = "cdns,sd6hc",
.data = &sdhci_cdns6_drv_data,
--
2.43.7