From patchwork Wed Apr 6 23:36:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brad Larson X-Patchwork-Id: 558659 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AB2A0C4332F for ; Wed, 6 Apr 2022 23:37:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238107AbiDFXja (ORCPT ); Wed, 6 Apr 2022 19:39:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58202 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237927AbiDFXjV (ORCPT ); Wed, 6 Apr 2022 19:39:21 -0400 Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E1C291FD2D8 for ; Wed, 6 Apr 2022 16:37:20 -0700 (PDT) Received: by mail-pl1-x62b.google.com with SMTP id q12so298107pla.9 for ; Wed, 06 Apr 2022 16:37:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pensando.io; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=tU6Ru8s4t+4eV16HMXKCbSpdipgLefmp/0QR68TsaEQ=; b=YhOL2fkqmE38zy1mF16X9qP0BiA5aEjmS9z3dURZ61fpEa/QcV+2FAoeJHAU3tnGKq X3QQfcp+BqV9W0yNTzqvrNb45dYO4ypnwM9WLIiQl8bbgr2RAf8GI0FjH8foeHg/G7So OmojdhmkZZjhWDuuXiJGW4b1ZBJCoJpHEluiC8ZgtENqYlfCzRGT0wKZo+iqOMHaWgCn PztkyXe2XdsWDJBq0qEX05vqFiKnTq6HbMvyzZVzTwRhemvUxSRV8Dw0ffLrGwwqWxbC jUDcaYJvRqFYGrCO6It4at5xhQYKZObWtHzZ0tosFQ24MgM21JcUoSBn/xleo7Eflbj3 5nSw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=tU6Ru8s4t+4eV16HMXKCbSpdipgLefmp/0QR68TsaEQ=; b=zmS3TtsBIu9rxUvL78Uf3LDyShUmwu4l/E4aB9E8zB9889O1XZ2NXxI2TSAVlKdV7s OQvRgIYcu8ibKwMcbofyxs5ugYbv8bMl3f41wVOTZsReV0fuV6vomzk1sgIdiHPgupi6 mr3D9Zh9PI8jkE3qGF4Ry/pI9MRqMr83GTXoSzaxgxydZbCXK+3tIW54K/ktG3POf7Un SCnbCW3NSMNzvYBpT6aYJLxEybEyRrTPaq2sFgaxVl156MH04zG4K7YqBjhsUSZx3msu tiP+RiXXE+kHBIfcXjWsWIqri0hmzez1VAGUaHXe4kCmwjtORydvXyT227E+fVcAAylo W8Zg== X-Gm-Message-State: AOAM5300v87bpysshQy6AVuGLFvGwsnHrPhiA/XCQqLQRSmNw5TosQJJ gmmPJDTtUY8CkKlYfAOzmZ0AEQ== X-Google-Smtp-Source: ABdhPJxTqmOxM3EimZWHh4l/LL2h3fRQnUAschIQ/njW986P/Pp8Ke7k82Z+PQwCjc4guhIqFrDKGg== X-Received: by 2002:a17:902:e541:b0:156:9cdc:e6ca with SMTP id n1-20020a170902e54100b001569cdce6camr10988249plf.2.1649288240409; Wed, 06 Apr 2022 16:37:20 -0700 (PDT) Received: from platform-dev1.pensando.io ([12.226.153.42]) by smtp.gmail.com with ESMTPSA id m21-20020a17090a7f9500b001c97c6bcaf4sm6903667pjl.39.2022.04.06.16.37.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Apr 2022 16:37:20 -0700 (PDT) From: Brad Larson To: linux-arm-kernel@lists.infradead.org Cc: arnd@arndb.de, linus.walleij@linaro.org, bgolaszewski@baylibre.com, broonie@kernel.org, fancer.lancer@gmail.com, adrian.hunter@intel.com, ulf.hansson@linaro.org, olof@lixom.net, brad@pensando.io, dac2@pensando.io, linux-gpio@vger.kernel.org, linux-spi@vger.kernel.org, linux-mmc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 09/11] mmc: sdhci-cadence: Add Pensando Elba SoC support Date: Wed, 6 Apr 2022 16:36:46 -0700 Message-Id: <20220406233648.21644-10-brad@pensando.io> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220406233648.21644-1-brad@pensando.io> References: <20220406233648.21644-1-brad@pensando.io> Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org Add support for Pensando Elba SoC which explicitly controls byte-lane enables on writes. Add priv_write_l() which is used on Elba platforms for byte-lane control. Select MMC_SDHCI_IO_ACCESSORS for MMC_SDHCI_CADENCE which allows Elba SoC sdhci_elba_ops to overwrite the SDHCI IO memory accessors. Signed-off-by: Brad Larson --- Change from V3: - Change from elba-emmc to elba-sd4hc to match file convention drivers/mmc/host/Kconfig | 1 + drivers/mmc/host/sdhci-cadence.c | 148 ++++++++++++++++++++++++++++--- 2 files changed, 135 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index af6c3c329076..f3f4dc95f21e 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -243,6 +243,7 @@ config MMC_SDHCI_CADENCE tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller" depends on MMC_SDHCI_PLTFM depends on OF + select MMC_SDHCI_IO_ACCESSORS help This selects the Cadence SD/SDIO/eMMC driver. diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 6f2de54a5987..e9b7f80e8cf0 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -66,7 +66,11 @@ struct sdhci_cdns_phy_param { struct sdhci_cdns_priv { void __iomem *hrs_addr; + void __iomem *ctl_addr; /* write control */ + spinlock_t wrlock; /* write lock */ bool enhanced_strobe; + void (*priv_write_l)(struct sdhci_cdns_priv *priv, u32 val, + void __iomem *reg); unsigned int nr_phy_params; struct sdhci_cdns_phy_param phy_params[]; }; @@ -76,6 +80,11 @@ struct sdhci_cdns_phy_cfg { u8 addr; }; +struct sdhci_cdns_drv_data { + int (*init)(struct platform_device *pdev); + const struct sdhci_pltfm_data pltfm_data; +}; + static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = { { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, }, { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, }, @@ -90,6 +99,15 @@ static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = { { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, }, }; +static inline void sdhci_cdns_priv_writel(struct sdhci_cdns_priv *priv, + u32 val, void __iomem *reg) +{ + if (unlikely(priv->priv_write_l)) + priv->priv_write_l(priv, val, reg); + else + writel(val, reg); +} + static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, u8 addr, u8 data) { @@ -104,17 +122,17 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) | FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr); - writel(tmp, reg); + sdhci_cdns_priv_writel(priv, tmp, reg); tmp |= SDHCI_CDNS_HRS04_WR; - writel(tmp, reg); + sdhci_cdns_priv_writel(priv, tmp, reg); ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10); if (ret) return ret; tmp &= ~SDHCI_CDNS_HRS04_WR; - writel(tmp, reg); + sdhci_cdns_priv_writel(priv, tmp, reg); ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK), 0, 10); @@ -191,7 +209,7 @@ static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode) tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06); tmp &= ~SDHCI_CDNS_HRS06_MODE; tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode); - writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06); + sdhci_cdns_priv_writel(priv, tmp, priv->hrs_addr + SDHCI_CDNS_HRS06); } static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv) @@ -223,7 +241,7 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) */ for (i = 0; i < 2; i++) { tmp |= SDHCI_CDNS_HRS06_TUNE_UP; - writel(tmp, reg); + sdhci_cdns_priv_writel(priv, tmp, reg); ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), @@ -309,6 +327,88 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host, sdhci_set_uhs_signaling(host, timing); } +/* + * The Pensando Elba SoC explicitly controls byte-lane enables on writes + * which includes writes to the HRS registers. + */ +static void elba_priv_write_l(struct sdhci_cdns_priv *priv, u32 val, + void __iomem *reg) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->wrlock, flags); + writel(0x78, priv->ctl_addr); + writel(val, reg); + spin_unlock_irqrestore(&priv->wrlock, flags); +} + +static void elba_write_l(struct sdhci_host *host, u32 val, int reg) +{ + elba_priv_write_l(sdhci_cdns_priv(host), val, host->ioaddr + reg); +} + +static void elba_write_w(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + unsigned long flags; + u32 m = (reg & 0x3); + u32 msk = (0x3 << (m)); + + spin_lock_irqsave(&priv->wrlock, flags); + writel(msk << 3, priv->ctl_addr); + writew(val, host->ioaddr + reg); + spin_unlock_irqrestore(&priv->wrlock, flags); +} + +static void elba_write_b(struct sdhci_host *host, u8 val, int reg) +{ + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + unsigned long flags; + u32 m = (reg & 0x3); + u32 msk = (0x1 << (m)); + + spin_lock_irqsave(&priv->wrlock, flags); + writel(msk << 3, priv->ctl_addr); + writeb(val, host->ioaddr + reg); + spin_unlock_irqrestore(&priv->wrlock, flags); +} + +static const struct sdhci_ops sdhci_elba_ops = { + .write_l = elba_write_l, + .write_w = elba_write_w, + .write_b = elba_write_b, + .set_clock = sdhci_set_clock, + .get_timeout_clock = sdhci_cdns_get_timeout_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_cdns_set_uhs_signaling, +}; + +static int elba_drv_init(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + struct resource *iomem; + void __iomem *ioaddr; + + host->mmc->caps |= (MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA); + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!iomem) + return -ENOMEM; + + ioaddr = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(ioaddr)) + return PTR_ERR(ioaddr); + + priv->ctl_addr = ioaddr; + priv->priv_write_l = elba_priv_write_l; + spin_lock_init(&priv->wrlock); + writel(0x78, priv->ctl_addr); + + return 0; +} + static const struct sdhci_ops sdhci_cdns_ops = { .set_clock = sdhci_set_clock, .get_timeout_clock = sdhci_cdns_get_timeout_clock, @@ -318,13 +418,24 @@ static const struct sdhci_ops sdhci_cdns_ops = { .set_uhs_signaling = sdhci_cdns_set_uhs_signaling, }; -static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = { - .ops = &sdhci_cdns_ops, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +static const struct sdhci_cdns_drv_data sdhci_cdns_uniphier_drv_data = { + .pltfm_data = { + .ops = &sdhci_cdns_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, +}; + +static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = { + .init = elba_drv_init, + .pltfm_data = { + .ops = &sdhci_elba_ops, + }, }; -static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = { - .ops = &sdhci_cdns_ops, +static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = { + .pltfm_data = { + .ops = &sdhci_cdns_ops, + }, }; static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc, @@ -350,7 +461,7 @@ static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc, static int sdhci_cdns_probe(struct platform_device *pdev) { struct sdhci_host *host; - const struct sdhci_pltfm_data *data; + const struct sdhci_cdns_drv_data *data; struct sdhci_pltfm_host *pltfm_host; struct sdhci_cdns_priv *priv; struct clk *clk; @@ -369,10 +480,10 @@ static int sdhci_cdns_probe(struct platform_device *pdev) data = of_device_get_match_data(dev); if (!data) - data = &sdhci_cdns_pltfm_data; + data = &sdhci_cdns_drv_data; nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node); - host = sdhci_pltfm_init(pdev, data, + host = sdhci_pltfm_init(pdev, &data->pltfm_data, struct_size(priv, phy_params, nr_phy_params)); if (IS_ERR(host)) { ret = PTR_ERR(host); @@ -389,6 +500,11 @@ static int sdhci_cdns_probe(struct platform_device *pdev) host->ioaddr += SDHCI_CDNS_SRS_BASE; host->mmc_host_ops.hs400_enhanced_strobe = sdhci_cdns_hs400_enhanced_strobe; + if (data->init) { + ret = data->init(pdev); + if (ret) + goto free; + } sdhci_enable_v4_mode(host); __sdhci_read_caps(host, &version, NULL, NULL); @@ -453,7 +569,11 @@ static const struct dev_pm_ops sdhci_cdns_pm_ops = { static const struct of_device_id sdhci_cdns_match[] = { { .compatible = "socionext,uniphier-sd4hc", - .data = &sdhci_cdns_uniphier_pltfm_data, + .data = &sdhci_cdns_uniphier_drv_data, + }, + { + .compatible = "pensando,elba-sd4hc", + .data = &sdhci_elba_drv_data }, { .compatible = "cdns,sd4hc" }, { /* sentinel */ }