public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
From: "Ivan T. Ivanov" <iivanov@suse.de>
To: Matthias Brugger <mbrugger@suse.com>,
	Peter Robinson <pbrobinson@gmail.com>
Cc: Dmitry Malkin <dmitry@bedrocksystems.com>,
	Thomas Fitzsimmons <fitzsim@fitzsim.org>,
	Peng Fan <peng.fan@nxp.com>,
	Jaehoon Chung <jh80.chung@samsung.com>,
	Anatolij Gustschin <agust@denx.de>,
	wahrenst@gmx.net, florian.fainelli@broadcom.com,
	u-boot@lists.denx.de, "Ivan T. Ivanov" <iivanov@suse.de>
Subject: [PATCH v4 5/6] mmc: bcmstb: Add support for bcm2712 SD controller
Date: Wed, 10 Jan 2024 14:29:07 +0200	[thread overview]
Message-ID: <20240110122908.31612-6-iivanov@suse.de> (raw)
In-Reply-To: <20240110122908.31612-1-iivanov@suse.de>

Borrow SD quirks from vendor Linux driver.

"BCM2712 unfortunately carries with it a perennial bug with the SD
controller register interface present on previous chips (2711/2709/2708).
Accesses must be dword-sized and a read-modify-write cycle to the 32-bit
registers containing the COMMAND, TRANSFER_MODE, BLOCK_SIZE and
BLOCK_COUNT registers tramples the upper/lower 16 bits of data written.
BCM2712 does not seem to need the extreme delay between each write as on
previous chips, just the serialisation of writes to these registers in a
single 32-bit operation."

Signed-off-by: Ivan T. Ivanov <iivanov@suse.de>
---
 drivers/mmc/bcmstb_sdhci.c | 173 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 172 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/bcmstb_sdhci.c b/drivers/mmc/bcmstb_sdhci.c
index dc96818cff..21489e66c0 100644
--- a/drivers/mmc/bcmstb_sdhci.c
+++ b/drivers/mmc/bcmstb_sdhci.c
@@ -38,6 +38,16 @@
  */
 #define BCMSTB_SDHCI_MINIMUM_CLOCK_FREQUENCY	400000
 
+#define SDIO_CFG_CTRL				0x0
+#define  SDIO_CFG_CTRL_SDCD_N_TEST_EN		BIT(31)
+#define  SDIO_CFG_CTRL_SDCD_N_TEST_LEV		BIT(30)
+
+#define SDIO_CFG_SD_PIN_SEL			0x44
+#define  SDIO_CFG_SD_PIN_SEL_MASK		0x3
+#define  SDIO_CFG_SD_PIN_SEL_CARD		BIT(1)
+
+#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
+
 /*
  * This driver has only been tested with eMMC devices; SD devices may
  * not work.
@@ -47,6 +57,53 @@ struct sdhci_bcmstb_plat {
 	struct mmc mmc;
 };
 
+struct sdhci_bcmstb_host {
+	struct sdhci_host host;
+	u32 shadow_cmd;
+	u32 shadow_blk;
+	bool is_cmd_shadowed;
+	bool is_blk_shadowed;
+};
+
+struct sdhci_brcmstb_dev_priv {
+	int (*init)(struct udevice *dev);
+	struct sdhci_ops *ops;
+};
+
+static inline struct sdhci_bcmstb_host *to_bcmstb_host(struct sdhci_host *host)
+{
+	return container_of(host, struct sdhci_bcmstb_host, host);
+}
+
+static int sdhci_brcmstb_init_2712(struct udevice *dev)
+{
+	struct sdhci_host *host = dev_get_priv(dev);
+	void *cfg_regs;
+	u32 reg;
+
+	/* Map in the non-standard CFG registers */
+	cfg_regs = dev_remap_addr_name(dev, "cfg");
+	if (!cfg_regs)
+		return -ENOENT;
+
+	if ((host->mmc->host_caps & MMC_CAP_NONREMOVABLE) ||
+	    (host->mmc->host_caps & MMC_CAP_NEEDS_POLL)) {
+		/* Force presence */
+		reg = readl(cfg_regs + SDIO_CFG_CTRL);
+		reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV;
+		reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN;
+		writel(reg, cfg_regs + SDIO_CFG_CTRL);
+	} else {
+		/* Enable card detection line */
+		reg = readl(cfg_regs + SDIO_CFG_SD_PIN_SEL);
+		reg &= ~SDIO_CFG_SD_PIN_SEL_MASK;
+		reg |= SDIO_CFG_SD_PIN_SEL_CARD;
+		writel(reg, cfg_regs + SDIO_CFG_SD_PIN_SEL);
+	}
+
+	return 0;
+}
+
 static int sdhci_bcmstb_bind(struct udevice *dev)
 {
 	struct sdhci_bcmstb_plat *plat = dev_get_plat(dev);
@@ -58,10 +115,14 @@ static int sdhci_bcmstb_probe(struct udevice *dev)
 {
 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 	struct sdhci_bcmstb_plat *plat = dev_get_plat(dev);
-	struct sdhci_host *host = dev_get_priv(dev);
+	struct sdhci_bcmstb_host *bcmstb = dev_get_priv(dev);
+	struct sdhci_host *host = &bcmstb->host;
+	struct sdhci_brcmstb_dev_priv *dev_priv;
 	fdt_addr_t base;
 	int ret;
 
+	dev_priv = (struct sdhci_brcmstb_dev_priv *)dev_get_driver_data(dev);
+
 	base = dev_read_addr(dev);
 	if (base == FDT_ADDR_T_NONE)
 		return -EINVAL;
@@ -75,6 +136,10 @@ static int sdhci_bcmstb_probe(struct udevice *dev)
 
 	host->mmc = &plat->mmc;
 	host->mmc->dev = dev;
+
+	if (dev_priv && dev_priv->ops)
+		host->ops = dev_priv->ops;
+
 	ret = sdhci_setup_cfg(&plat->cfg, host,
 			      BCMSTB_SDHCI_MAXIMUM_CLOCK_FREQUENCY,
 			      BCMSTB_SDHCI_MINIMUM_CLOCK_FREQUENCY);
@@ -84,10 +149,116 @@ static int sdhci_bcmstb_probe(struct udevice *dev)
 	upriv->mmc = &plat->mmc;
 	host->mmc->priv = host;
 
+	if (dev_priv && dev_priv->init) {
+		ret = dev_priv->init(dev);
+		if (ret)
+			return ret;
+	}
+
 	return sdhci_probe(dev);
 }
 
+static u16 sdhci_brcmstb_32bits_readw(struct sdhci_host *host, int reg)
+{
+	struct sdhci_bcmstb_host *bcmstb = to_bcmstb_host(host);
+	u16 word;
+	u32 val;
+
+	if (reg == SDHCI_TRANSFER_MODE && bcmstb->is_cmd_shadowed) {
+		/* Get the saved transfer mode */
+		val = bcmstb->shadow_cmd;
+	} else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+		   bcmstb->is_blk_shadowed) {
+		/* Get the saved block info */
+		val = bcmstb->shadow_blk;
+	} else {
+		val = readl(host->ioaddr + (reg & ~3));
+	}
+
+	word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
+	return word;
+}
+
+static u8 sdhci_brcmstb_32bits_readb(struct sdhci_host *host, int reg)
+{
+	u32 val = readl(host->ioaddr + (reg & ~3));
+	u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
+	return byte;
+}
+
+/*
+ * BCM2712 unfortunately carries with it a perennial bug with the SD
+ * controller register interface present on previous chips (2711/2709/2708).
+ * Accesses must be dword-sized and a read-modify-write cycle to the
+ * 32-bit registers containing the COMMAND, TRANSFER_MODE, BLOCK_SIZE and
+ * BLOCK_COUNT registers tramples the upper/lower 16 bits of data written.
+ * BCM2712 does not seem to need the extreme delay between each write as
+ * on previous chips, just the serialisation of writes to these registers
+ * in a single 32-bit operation.
+ */
+static void sdhci_brcmstb_32bits_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	struct sdhci_bcmstb_host *bcmstb = to_bcmstb_host(host);
+	u32 word_shift = REG_OFFSET_IN_BITS(reg);
+	u32 mask = 0xffff << word_shift;
+	u32 oldval, newval;
+
+	if (reg == SDHCI_COMMAND) {
+		/* Write the block now as we are issuing a command */
+		if (bcmstb->is_blk_shadowed) {
+			writel(bcmstb->shadow_blk, host->ioaddr + SDHCI_BLOCK_SIZE);
+			bcmstb->is_blk_shadowed = false;
+		}
+		oldval = bcmstb->shadow_cmd;
+		bcmstb->is_cmd_shadowed = false;
+	} else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+		   bcmstb->is_blk_shadowed) {
+		/* Block size and count are stored in shadow reg */
+		oldval = bcmstb->shadow_blk;
+	} else {
+		/* Read reg, all other registers are not shadowed */
+		oldval = readl(host->ioaddr + (reg & ~3));
+	}
+	newval = (oldval & ~mask) | (val << word_shift);
+
+	if (reg == SDHCI_TRANSFER_MODE) {
+		/* Save the transfer mode until the command is issued */
+		bcmstb->shadow_cmd = newval;
+		bcmstb->is_cmd_shadowed = true;
+	} else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+		/* Save the block info until the command is issued */
+		bcmstb->shadow_blk = newval;
+		bcmstb->is_blk_shadowed = true;
+	} else {
+		/* Command or other regular 32-bit write */
+		writel(newval, host->ioaddr + (reg & ~3));
+	}
+}
+
+static void sdhci_brcmstb_32bits_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	u32 oldval = readl(host->ioaddr + (reg & ~3));
+	u32 byte_shift = REG_OFFSET_IN_BITS(reg);
+	u32 mask = 0xff << byte_shift;
+	u32 newval = (oldval & ~mask) | (val << byte_shift);
+
+	writel(newval, host->ioaddr + (reg & ~3));
+}
+
+static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
+	.read_b    = sdhci_brcmstb_32bits_readb,
+	.read_w    = sdhci_brcmstb_32bits_readw,
+	.write_w   = sdhci_brcmstb_32bits_writew,
+	.write_b   = sdhci_brcmstb_32bits_writeb,
+};
+
+static const struct sdhci_brcmstb_dev_priv match_priv_2712 = {
+	.init = sdhci_brcmstb_init_2712,
+	.ops = &sdhci_brcmstb_ops_2712,
+};
+
 static const struct udevice_id sdhci_bcmstb_match[] = {
+	{ .compatible = "brcm,bcm2712-sdhci", .data = (ulong)&match_priv_2712 },
 	{ .compatible = "brcm,bcm7425-sdhci" },
 	{ .compatible = "brcm,sdhci-brcmstb" },
 	{ }
-- 
2.35.3


  parent reply	other threads:[~2024-01-10 12:36 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-10 12:29 [PATCH v4 0/6] rpi5: initial support Ivan T. Ivanov
2024-01-10 12:29 ` [PATCH v4 1/6] rpi5: add initial memory map for bcm2712 Ivan T. Ivanov
2024-01-10 17:57   ` Florian Fainelli
2024-01-10 12:29 ` [PATCH v4 2/6] rpi5: Use devicetree as alternative way to read IO base addresses Ivan T. Ivanov
2024-01-10 18:00   ` Florian Fainelli
2024-01-16  9:11     ` Ivan T. Ivanov
2024-01-10 12:29 ` [PATCH v4 3/6] rpi5: Use devicetree to retrieve board revision Ivan T. Ivanov
2024-01-10 12:29 ` [PATCH v4 4/6] bcm2835: Dynamically calculate bytes per pixel parameter Ivan T. Ivanov
2024-01-10 15:12   ` Matthias Brugger
2024-01-10 12:29 ` Ivan T. Ivanov [this message]
2024-01-11 22:07   ` [PATCH v4 5/6] mmc: bcmstb: Add support for bcm2712 SD controller Stefan Wahren
2024-01-12  7:44     ` Ivan T. Ivanov
2024-01-16  9:14       ` Ivan T. Ivanov
2024-01-10 12:29 ` [PATCH v4 6/6] configs: rpi_arm64: enable SDHCI BCMSTB driver Ivan T. Ivanov
2024-01-22 13:46 ` [PATCH v4 0/6] rpi5: initial support Matthias Brugger
2024-09-19  9:36 ` Stefan Agner
2024-09-19  9:52   ` Ivan T. Ivanov
2024-09-19 14:01     ` Stefan Agner
2024-09-19 14:22       ` Ivan T. Ivanov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240110122908.31612-6-iivanov@suse.de \
    --to=iivanov@suse.de \
    --cc=agust@denx.de \
    --cc=dmitry@bedrocksystems.com \
    --cc=fitzsim@fitzsim.org \
    --cc=florian.fainelli@broadcom.com \
    --cc=jh80.chung@samsung.com \
    --cc=mbrugger@suse.com \
    --cc=pbrobinson@gmail.com \
    --cc=peng.fan@nxp.com \
    --cc=u-boot@lists.denx.de \
    --cc=wahrenst@gmx.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox