forked from pool/u-boot
Guillaume GARDET
c8ce9a56ae
- Remove ls1012afrdmqspi flavor since it has been removed upstream with commit b60274e6900ed1b08ad41f6d7fdebb2726ded108 - Update to 2024.01 OBS-URL: https://build.opensuse.org/request/show/1138155 OBS-URL: https://build.opensuse.org/package/show/hardware:boot/u-boot?expand=0&rev=203
241 lines
7.6 KiB
Diff
241 lines
7.6 KiB
Diff
From ceab0d2076af0777605701fd9bc8f93641f7accf Mon Sep 17 00:00:00 2001
|
|
From: "Ivan T. Ivanov" <iivanov@suse.de>
|
|
Date: Fri, 15 Dec 2023 09:43:45 +0100
|
|
Subject: [PATCH] mmc: bcmstb: Add support for bcm2712 SD controller
|
|
|
|
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" },
|
|
{ }
|