openwrt/target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch
Marty Jones 2e715fb4fc bcm27xx: update 6.1 patches to latest version
Add support for BCM2712 (Raspberry Pi 5).
3bb5880ab3
Patches were generated from the diff between linux kernel branch linux-6.1.y
and rpi-6.1.y from raspberry pi kernel source:
- git format-patch linux-6.1.y...rpi-6.1.y

Build system: x86_64
Build-tested: bcm2708, bcm2709, bcm2710, bcm2711
Run-tested: bcm2710/RPi3B, bcm2711/RPi4B

Signed-off-by: Marty Jones <mj8263788@gmail.com>
[Remove applied and reverted patches, squash patches and config commits]
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2024-01-25 17:46:45 +01:00

499 lines
16 KiB
Diff

From b627647c4500d39cb026924b608841fdf4d4d7e9 Mon Sep 17 00:00:00 2001
From: Ulf Hansson <ulf.hansson@linaro.org>
Date: Thu, 29 Oct 2020 09:57:16 +0800
Subject: [PATCH] mmc: brcmstb: add support for BCM2712
BCM2712 has an SD Express capable SDHCI implementation and uses
the SDIO CFG register block present on other STB chips.
Add plumbing for SD Express handover and BCM2712-specific functions.
Due to the common bus infrastructure between BCM2711 and BCM2712,
the driver also needs to implement 32-bit IO accessors.
mmc: brcmstb: override card presence if broken-cd is set
Not just if the card is declared as nonremovable.
sdhci: brcmstb: align SD express switchover with SD spec v8.00
Part 1 of the Physical specification, figure 3-24, details the switch
sequence for cards initially probed as SD. Add a missing check for DAT2
level after switching VDD2 on.
sdhci: brcmstb: clean up SD Express probe and error handling
Refactor to avoid spurious error messages in dmesg if the requisite SD
Express DT nodes aren't present.
Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
mmc: sdhci-brcmstb: only use the delay line PHY for tuneable speeds
The MMC core has a 200MHz core clock which allows the use of DDR50 and
below without incremental phase tuning. SDR50/SDR104 and the EMMC HS200
speeds require tuning.
Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
---
drivers/mmc/host/Kconfig | 2 +
drivers/mmc/host/sdhci-brcmstb.c | 356 +++++++++++++++++++++++++++++++
2 files changed, 358 insertions(+)
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -1082,7 +1082,9 @@ config MMC_SDHCI_BRCMSTB
tristate "Broadcom SDIO/SD/MMC support"
depends on ARCH_BRCMSTB || BMIPS_GENERIC
depends on MMC_SDHCI_PLTFM
+ select MMC_SDHCI_IO_ACCESSORS
select MMC_CQHCI
+ select OF_DYNAMIC
default y
help
This selects support for the SDIO/SD/MMC Host Controller on
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -11,6 +11,8 @@
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/delay.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
#include "sdhci-cqhci.h"
#include "sdhci-pltfm.h"
@@ -26,18 +28,43 @@
#define BRCMSTB_PRIV_FLAGS_HAS_CQE BIT(0)
#define BRCMSTB_PRIV_FLAGS_GATE_CLOCK BIT(1)
+#define BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS BIT(2)
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
+#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 SDIO_CFG_MAX_50MHZ_MODE 0x1ac
+#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
+#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
+
struct sdhci_brcmstb_priv {
void __iomem *cfg_regs;
unsigned int flags;
struct clk *base_clk;
u32 base_freq_hz;
+ u32 shadow_cmd;
+ u32 shadow_blk;
+ bool is_cmd_shadowed;
+ bool is_blk_shadowed;
+ struct regulator *sde_1v8;
+ struct device_node *sde_pcie;
+ void *__iomem sde_ioaddr;
+ void *__iomem sde_ioaddr2;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default;
+ struct pinctrl_state *pins_sdex;
};
struct brcmstb_match_priv {
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
+ void (*cfginit)(struct sdhci_host *host);
struct sdhci_ops *ops;
const unsigned int flags;
};
@@ -94,6 +121,124 @@ static void sdhci_brcmstb_set_clock(stru
sdhci_enable_clk(host, clk);
}
+#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
+
+static inline u32 sdhci_brcmstb_32only_readl(struct sdhci_host *host, int reg)
+{
+ u32 val = readl(host->ioaddr + reg);
+
+ pr_debug("%s: readl [0x%02x] 0x%08x\n",
+ mmc_hostname(host->mmc), reg, val);
+ return val;
+}
+
+static u16 sdhci_brcmstb_32only_readw(struct sdhci_host *host, int reg)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 val;
+ u16 word;
+
+ if ((reg == SDHCI_TRANSFER_MODE) && brcmstb_priv->is_cmd_shadowed) {
+ /* Get the saved transfer mode */
+ val = brcmstb_priv->shadow_cmd;
+ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+ brcmstb_priv->is_blk_shadowed) {
+ /* Get the saved block info */
+ val = brcmstb_priv->shadow_blk;
+ } else {
+ val = sdhci_brcmstb_32only_readl(host, (reg & ~3));
+ }
+ word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
+ return word;
+}
+
+static u8 sdhci_brcmstb_32only_readb(struct sdhci_host *host, int reg)
+{
+ u32 val = sdhci_brcmstb_32only_readl(host, (reg & ~3));
+ u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
+ return byte;
+}
+
+static inline void sdhci_brcmstb_32only_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ pr_debug("%s: writel [0x%02x] 0x%08x\n",
+ mmc_hostname(host->mmc), reg, val);
+
+ writel(val, host->ioaddr + reg);
+}
+
+/*
+ * 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_32only_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_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 (brcmstb_priv->is_blk_shadowed) {
+ sdhci_brcmstb_32only_writel(host, brcmstb_priv->shadow_blk,
+ SDHCI_BLOCK_SIZE);
+ brcmstb_priv->is_blk_shadowed = false;
+ }
+ oldval = brcmstb_priv->shadow_cmd;
+ brcmstb_priv->is_cmd_shadowed = false;
+ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
+ brcmstb_priv->is_blk_shadowed) {
+ /* Block size and count are stored in shadow reg */
+ oldval = brcmstb_priv->shadow_blk;
+ } else {
+ /* Read reg, all other registers are not shadowed */
+ oldval = sdhci_brcmstb_32only_readl(host, (reg & ~3));
+ }
+ newval = (oldval & ~mask) | (val << word_shift);
+
+ if (reg == SDHCI_TRANSFER_MODE) {
+ /* Save the transfer mode until the command is issued */
+ brcmstb_priv->shadow_cmd = newval;
+ brcmstb_priv->is_cmd_shadowed = true;
+ } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+ /* Save the block info until the command is issued */
+ brcmstb_priv->shadow_blk = newval;
+ brcmstb_priv->is_blk_shadowed = true;
+ } else {
+ /* Command or other regular 32-bit write */
+ sdhci_brcmstb_32only_writel(host, newval, reg & ~3);
+ }
+}
+
+static void sdhci_brcmstb_32only_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ u32 oldval = sdhci_brcmstb_32only_readl(host, (reg & ~3));
+ u32 byte_shift = REG_OFFSET_IN_BITS(reg);
+ u32 mask = 0xff << byte_shift;
+ u32 newval = (oldval & ~mask) | (val << byte_shift);
+
+ sdhci_brcmstb_32only_writel(host, newval, reg & ~3);
+}
+
+static void sdhci_brcmstb_set_power(struct sdhci_host *host, unsigned char mode,
+ unsigned short vdd)
+{
+ if (!IS_ERR(host->mmc->supply.vmmc)) {
+ struct mmc_host *mmc = host->mmc;
+
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+ }
+ sdhci_set_power_noreg(host, mode, vdd);
+}
+
static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host,
unsigned int timing)
{
@@ -123,6 +268,146 @@ static void sdhci_brcmstb_set_uhs_signal
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
+static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
+ bool want_dll = false;
+ u32 uhs_mask = (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104);
+ u32 hsemmc_mask = (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR |
+ MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V);
+ u32 reg;
+
+ if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)) {
+ if((host->mmc->caps & uhs_mask) || (host->mmc->caps2 & hsemmc_mask))
+ want_dll = true;
+ }
+
+ /*
+ * If we want a speed that requires tuning,
+ * then select the delay line PHY as the clock source.
+ */
+ if (want_dll) {
+ reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
+ reg &= ~SDIO_CFG_MAX_50MHZ_MODE_ENABLE;
+ reg |= SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE;
+ writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
+ }
+
+ if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+ (host->mmc->caps & MMC_CAP_NEEDS_POLL)) {
+ /* Force presence */
+ reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
+ reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV;
+ reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN;
+ writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
+ } else {
+ /* Enable card detection line */
+ reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_SD_PIN_SEL);
+ reg &= ~SDIO_CFG_SD_PIN_SEL_MASK;
+ reg |= SDIO_CFG_SD_PIN_SEL_CARD;
+ writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_SD_PIN_SEL);
+ }
+}
+
+static int bcm2712_init_sd_express(struct sdhci_host *host, struct mmc_ios *ios)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
+ struct device *dev = host->mmc->parent;
+ u32 ctrl_val;
+ u32 present_state;
+ int ret;
+
+ if (!brcmstb_priv->sde_ioaddr || !brcmstb_priv->sde_ioaddr2)
+ return -EINVAL;
+
+ if (!brcmstb_priv->pinctrl)
+ return -EINVAL;
+
+ /* Turn off the SD clock first */
+ sdhci_set_clock(host, 0);
+
+ /* Disable SD DAT0-3 pulls */
+ pinctrl_select_state(brcmstb_priv->pinctrl, brcmstb_priv->pins_sdex);
+
+ ctrl_val = readl(brcmstb_priv->sde_ioaddr);
+ dev_dbg(dev, "ctrl_val 1 %08x\n", ctrl_val);
+
+ /* Tri-state the SD pins */
+ ctrl_val |= 0x1ff8;
+ writel(ctrl_val, brcmstb_priv->sde_ioaddr);
+ dev_dbg(dev, "ctrl_val 1->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr));
+ /* Let voltages settle */
+ udelay(100);
+
+ /* Enable the PCIe sideband pins */
+ ctrl_val &= ~0x6000;
+ writel(ctrl_val, brcmstb_priv->sde_ioaddr);
+ dev_dbg(dev, "ctrl_val 1->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr));
+ /* Let voltages settle */
+ udelay(100);
+
+ /* Turn on the 1v8 VDD2 regulator */
+ ret = regulator_enable(brcmstb_priv->sde_1v8);
+ if (ret)
+ return ret;
+
+ /* Wait for Tpvcrl */
+ msleep(1);
+
+ /* Sample DAT2 (CLKREQ#) - if low, card is in PCIe mode */
+ present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ present_state = (present_state & SDHCI_DATA_LVL_MASK) >> SDHCI_DATA_LVL_SHIFT;
+ dev_dbg(dev, "state = 0x%08x\n", present_state);
+
+ if (present_state & BIT(2)) {
+ dev_err(dev, "DAT2 still high, abandoning SDex switch\n");
+ return -ENODEV;
+ }
+
+ /* Turn on the LCPLL PTEST mux */
+ ctrl_val = readl(brcmstb_priv->sde_ioaddr2 + 20); // misc5
+ ctrl_val &= ~(0x7 << 7);
+ ctrl_val |= 3 << 7;
+ writel(ctrl_val, brcmstb_priv->sde_ioaddr2 + 20);
+ dev_dbg(dev, "misc 5->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr2 + 20));
+
+ /* PTEST diff driver enable */
+ ctrl_val = readl(brcmstb_priv->sde_ioaddr2);
+ ctrl_val |= BIT(21);
+ writel(ctrl_val, brcmstb_priv->sde_ioaddr2);
+
+ dev_dbg(dev, "misc 0->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr2));
+
+ /* Wait for more than the minimum Tpvpgl time */
+ msleep(100);
+
+ if (brcmstb_priv->sde_pcie) {
+ struct of_changeset changeset;
+ static struct property okay_property = {
+ .name = "status",
+ .value = "okay",
+ .length = 5,
+ };
+
+ /* Enable the pcie controller */
+ of_changeset_init(&changeset);
+ ret = of_changeset_update_property(&changeset,
+ brcmstb_priv->sde_pcie,
+ &okay_property);
+ if (ret) {
+ dev_err(dev, "%s: failed to update property - %d\n", __func__,
+ ret);
+ return -ENODEV;
+ }
+ ret = of_changeset_apply(&changeset);
+ }
+
+ dev_dbg(dev, "%s -> %d\n", __func__, ret);
+ return ret;
+}
+
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
{
sdhci_dumpregs(mmc_priv(mmc));
@@ -155,6 +440,21 @@ static struct sdhci_ops sdhci_brcmstb_op
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
+static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
+ .read_l = sdhci_brcmstb_32only_readl,
+ .read_w = sdhci_brcmstb_32only_readw,
+ .read_b = sdhci_brcmstb_32only_readb,
+ .write_l = sdhci_brcmstb_32only_writel,
+ .write_w = sdhci_brcmstb_32only_writew,
+ .write_b = sdhci_brcmstb_32only_writeb,
+ .set_clock = sdhci_set_clock,
+ .set_power = sdhci_brcmstb_set_power,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .init_sd_express = bcm2712_init_sd_express,
+};
+
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
.set_clock = sdhci_brcmstb_set_clock,
.set_bus_width = sdhci_set_bus_width,
@@ -179,10 +479,16 @@ static const struct brcmstb_match_priv m
.ops = &sdhci_brcmstb_ops_7216,
};
+static const struct brcmstb_match_priv match_priv_2712 = {
+ .cfginit = sdhci_brcmstb_cfginit_2712,
+ .ops = &sdhci_brcmstb_ops_2712,
+};
+
static const struct of_device_id sdhci_brcm_of_match[] = {
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
+ { .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
{},
};
@@ -256,6 +562,7 @@ static int sdhci_brcmstb_probe(struct pl
u32 actual_clock_mhz;
struct sdhci_host *host;
struct resource *iomem;
+ bool no_pinctrl = false;
struct clk *clk;
struct clk *base_clk = NULL;
int res;
@@ -290,6 +597,11 @@ static int sdhci_brcmstb_probe(struct pl
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
}
+ priv->sde_pcie = of_parse_phandle(pdev->dev.of_node,
+ "sde-pcie", 0);
+ if (priv->sde_pcie)
+ priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
+
/* Map in the non-standard CFG registers */
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem);
@@ -303,6 +615,43 @@ static int sdhci_brcmstb_probe(struct pl
if (res)
goto err;
+ priv->sde_1v8 = devm_regulator_get_optional(&pdev->dev, "sde-1v8");
+ if (IS_ERR(priv->sde_1v8))
+ priv->flags &= ~BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (iomem) {
+ priv->sde_ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
+ if (IS_ERR(priv->sde_ioaddr))
+ priv->sde_ioaddr = NULL;
+ }
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ if (iomem) {
+ priv->sde_ioaddr2 = devm_ioremap_resource(&pdev->dev, iomem);
+ if (IS_ERR(priv->sde_ioaddr2))
+ priv->sde_ioaddr = NULL;
+ }
+
+ priv->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(priv->pinctrl)) {
+ no_pinctrl = true;
+ }
+ priv->pins_default = pinctrl_lookup_state(priv->pinctrl, "default");
+ if (IS_ERR(priv->pins_default)) {
+ dev_dbg(&pdev->dev, "No pinctrl default state\n");
+ no_pinctrl = true;
+ }
+ priv->pins_sdex = pinctrl_lookup_state(priv->pinctrl, "sd-express");
+ if (IS_ERR(priv->pins_sdex)) {
+ dev_dbg(&pdev->dev, "No pinctrl sd-express state\n");
+ no_pinctrl = true;
+ }
+ if (no_pinctrl || !priv->sde_ioaddr || !priv->sde_ioaddr2) {
+ priv->pinctrl = NULL;
+ priv->flags &= ~BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
+ }
+
/*
* Automatic clock gating does not work for SD cards that may
* voltage switch so only enable it for non-removable devices.
@@ -319,6 +668,13 @@ static int sdhci_brcmstb_probe(struct pl
(host->mmc->caps2 & MMC_CAP2_HS400_ES))
host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es;
+ if (host->ops->init_sd_express &&
+ (priv->flags & BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS))
+ host->mmc->caps2 |= MMC_CAP2_SD_EXP;
+
+ if(match_priv->cfginit)
+ match_priv->cfginit(host);
+
/*
* Supply the existing CAPS, but clear the UHS modes. This
* will allow these modes to be specified by device tree