aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-of-arasan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci-of-arasan.c')
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c458
1 files changed, 428 insertions, 30 deletions
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index dd10f7abf5a7..0ae40595d925 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -18,20 +18,53 @@
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
+#include <linux/delay.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/phy/phy.h>
+#include <linux/mmc/mmc.h>
+#include <linux/soc/xilinx/zynqmp/tap_delays.h>
+#include <linux/soc/xilinx/zynqmp/fw.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/regmap.h>
#include <linux/of.h>
+#include <linux/slab.h>
#include "cqhci.h"
+#include "sdhci-cqhci.h"
#include "sdhci-pltfm.h"
#define SDHCI_ARASAN_VENDOR_REGISTER 0x78
+#define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8
+#define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC
+
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
#define VENDOR_ENHANCED_STROBE BIT(0)
+#define CLK_CTRL_TIMEOUT_SHIFT 16
+#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
+#define CLK_CTRL_TIMEOUT_MIN_EXP 13
+#define SD_CLK_25_MHZ 25000000
+#define SD_CLK_19_MHZ 19000000
+#define MAX_TUNING_LOOP 40
#define PHY_CLK_TOO_SLOW_HZ 400000
+#define SDHCI_ITAPDLY_CHGWIN 0x200
+#define SDHCI_ITAPDLY_ENABLE 0x100
+#define SDHCI_OTAPDLY_ENABLE 0x40
+
+static u32 zynqmp_itap_delays[MMC_TIMING_MMC_HS400+1] = {0x0, 0x15, 0x15, 0x0,
+ 0x15, 0x0, 0x0, 0x3D, 0x12, 0x0, 0x0};
+static u32 zynqmp_otap_delays[MMC_TIMING_MMC_HS400+1] = {0x0, 0x5, 0x6, 0x0,
+ 0x5, 0x3, 0x3, 0x4, 0x6, 0x3, 0x0};
+
+static u32 versal_itap_delays[MMC_TIMING_MMC_HS400 + 1] = {0x0, 0x2C, 0x2C,
+ 0x0, 0x2C, 0x0, 0x0, 0x36, 0x1E, 0x0, 0x0};
+static u32 versal_otap_delays[MMC_TIMING_MMC_HS400 + 1] = {0x0, 0x5, 0x4,
+ 0x0, 0x4, 0x3, 0x2, 0x3, 0x5, 0x2, 0x0};
+
+#define MMC_BANK2 0x2
+
/*
* On some SoCs the syscon area has a feature where the upper 16-bits of
* each 32-bit register act as a write mask for the lower 16-bits. This allows
@@ -57,13 +90,13 @@ struct sdhci_arasan_soc_ctl_field {
/**
* struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers
*
- * It's up to the licensee of the Arsan IP block to make these available
- * somewhere if needed. Presumably these will be scattered somewhere that's
- * accessible via the syscon API.
- *
* @baseclkfreq: Where to find corecfg_baseclkfreq
* @clockmultiplier: Where to find corecfg_clockmultiplier
* @hiword_update: If true, use HIWORD_UPDATE to access the syscon
+ *
+ * It's up to the licensee of the Arsan IP block to make these available
+ * somewhere if needed. Presumably these will be scattered somewhere that's
+ * accessible via the syscon API.
*/
struct sdhci_arasan_soc_ctl_map {
struct sdhci_arasan_soc_ctl_field baseclkfreq;
@@ -72,20 +105,33 @@ struct sdhci_arasan_soc_ctl_map {
};
/**
- * struct sdhci_arasan_data
+ * struct sdhci_arasan_data - Structure for Arasan Controller Data
+ *
* @host: Pointer to the main SDHCI host structure.
* @clk_ahb: Pointer to the AHB clock
* @phy: Pointer to the generic phy
+ * @mio_bank: MIO Bank Number for ZynqMP SD Controller pins.
+ * @device_id: ZynqMP SD controller ID.
+ * @itapdly: Input Clock Delay for SD card clock.
+ * @otapdly: Output Clock Delay for SD card clock.
* @is_phy_on: True if the PHY is on; false if not.
+ * @has_cqe: Flag indicating support for Command Queueing.
* @sdcardclk_hw: Struct for the clock we might provide to a PHY.
* @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
* @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers.
+ * @pinctrl: Pointer to pin control structure.
+ * @pins_default: Pointer to pinctrl state for a device.
* @soc_ctl_map: Map to get offsets into soc_ctl registers.
+ * @quirks: Arasan deviations from spec.
*/
struct sdhci_arasan_data {
struct sdhci_host *host;
struct clk *clk_ahb;
struct phy *phy;
+ u32 mio_bank;
+ u32 device_id;
+ u32 itapdly[MMC_TIMING_MMC_HS400 + 1];
+ u32 otapdly[MMC_TIMING_MMC_HS400 + 1];
bool is_phy_on;
bool has_cqe;
@@ -93,6 +139,8 @@ struct sdhci_arasan_data {
struct clk *sdcardclk;
struct regmap *soc_ctl_base;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default;
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
unsigned int quirks; /* Arasan deviations from spec */
@@ -101,6 +149,8 @@ struct sdhci_arasan_data {
/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the
* internal clock even when the clock isn't stable */
#define SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE BIT(1)
+/* Controller has tap delay setting registers in it's local reg space */
+#define SDHCI_ARASAN_TAPDELAY_REG_LOCAL BIT(2)
};
struct sdhci_arasan_of_data {
@@ -123,14 +173,16 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_emmc_soc_ctl_map = {
/**
* sdhci_arasan_syscon_write - Write to a field in soc_ctl registers
*
+ * @host: The sdhci_host
+ * @fld: The field to write to
+ * @val: The value to write
+ *
* This function allows writing to fields in sdhci_arasan_soc_ctl_map.
* Note that if a field is specified as not available (shift < 0) then
* this function will silently return an error code. It will be noisy
* and print errors for any other (unexpected) errors.
*
- * @host: The sdhci_host
- * @fld: The field to write to
- * @val: The value to write
+ * Return: 0 on success and error value on error
*/
static int sdhci_arasan_syscon_write(struct sdhci_host *host,
const struct sdhci_arasan_soc_ctl_field *fld,
@@ -170,6 +222,100 @@ static int sdhci_arasan_syscon_write(struct sdhci_host *host,
return ret;
}
+static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u8 deviceid)
+{
+ u16 clk;
+ unsigned long timeout;
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Issue DLL Reset */
+ zynqmp_dll_reset(deviceid);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 20 ms */
+ timeout = 20;
+ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ dev_err(mmc_dev(host->mmc),
+ ": Internal clock never stabilised.\n");
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ int err;
+
+ arasan_zynqmp_dll_reset(host, sdhci_arasan->device_id);
+
+ err = sdhci_execute_tuning(mmc, opcode);
+ if (err)
+ return err;
+
+ arasan_zynqmp_dll_reset(host, sdhci_arasan->device_id);
+
+ return 0;
+}
+
+static void __arasan_set_tap_delay(struct sdhci_host *host, u8 itap_delay,
+ u8 otap_delay)
+{
+ u32 regval;
+
+ if (itap_delay) {
+ regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= SDHCI_ITAPDLY_CHGWIN;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= SDHCI_ITAPDLY_ENABLE;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= itap_delay;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval &= ~SDHCI_ITAPDLY_CHGWIN;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ }
+
+ if (otap_delay) {
+ regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ regval |= SDHCI_OTAPDLY_ENABLE;
+ sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ regval |= otap_delay;
+ sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ }
+}
+
+static void arasan_set_tap_delay(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ u8 itap_delay;
+ u8 otap_delay;
+
+ itap_delay = sdhci_arasan->itapdly[host->timing];
+ otap_delay = sdhci_arasan->otapdly[host->timing];
+
+ if (sdhci_arasan->quirks & SDHCI_ARASAN_TAPDELAY_REG_LOCAL)
+ __arasan_set_tap_delay(host, itap_delay, otap_delay);
+ else
+ arasan_zynqmp_set_tap_delay(sdhci_arasan->device_id,
+ itap_delay, otap_delay);
+}
+
static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -215,6 +361,17 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
}
}
+ /* Set the Input and Output Tap Delays */
+ if ((host->quirks2 & SDHCI_QUIRK2_CLOCK_STANDARD_25_BROKEN) &&
+ (host->version >= SDHCI_SPEC_300)) {
+ if (clock == SD_CLK_25_MHZ)
+ clock = SD_CLK_19_MHZ;
+ if ((host->timing != MMC_TIMING_LEGACY) &&
+ (host->timing != MMC_TIMING_UHS_SDR12)) {
+ arasan_set_tap_delay(host);
+ }
+ }
+
if (ctrl_phy && sdhci_arasan->is_phy_on) {
phy_power_off(sdhci_arasan->phy);
sdhci_arasan->is_phy_on = false;
@@ -264,7 +421,7 @@ static void sdhci_arasan_reset(struct sdhci_host *host, u8 mask)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
- sdhci_reset(host, mask);
+ sdhci_and_cqhci_reset(host, mask);
if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) {
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
@@ -306,7 +463,7 @@ static void sdhci_arasan_set_power(struct sdhci_host *host, unsigned char mode,
sdhci_set_power_noreg(host, mode, vdd);
}
-static const struct sdhci_ops sdhci_arasan_ops = {
+static struct sdhci_ops sdhci_arasan_ops = {
.set_clock = sdhci_arasan_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
@@ -318,7 +475,6 @@ static const struct sdhci_ops sdhci_arasan_ops = {
static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
.ops = &sdhci_arasan_ops,
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
@@ -394,13 +550,82 @@ static struct sdhci_arasan_of_data intel_lgm_emmc_data = {
.pdata = &sdhci_arasan_cqe_pdata,
};
+#ifdef CONFIG_PM
+/**
+ * sdhci_arasan_runtime_suspend - Suspend method for the driver
+ * @dev: Address of the device structure
+ * Put the device in a low power state.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int sdhci_arasan_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ int ret;
+
+ ret = sdhci_runtime_suspend_host(host);
+ if (ret)
+ return ret;
+
+ if (host->tuning_mode != SDHCI_TUNING_MODE_3)
+ mmc_retune_needed(host->mmc);
+
+ clk_disable(pltfm_host->clk);
+ clk_disable(sdhci_arasan->clk_ahb);
+
+ return 0;
+}
+
+/**
+ * sdhci_arasan_runtime_resume - Resume method for the driver
+ * @dev: Address of the device structure
+ * Resume operation after suspend
+ *
+ * Return: 0 on success and error value on error
+ */
+static int sdhci_arasan_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ int ret;
+
+ ret = clk_enable(sdhci_arasan->clk_ahb);
+ if (ret) {
+ dev_err(dev, "Cannot enable AHB clock.\n");
+ return ret;
+ }
+
+ ret = clk_enable(pltfm_host->clk);
+ if (ret) {
+ dev_err(dev, "Cannot enable SD clock.\n");
+ return ret;
+ }
+
+ ret = sdhci_runtime_resume_host(host, 0);
+ if (ret)
+ goto out;
+
+ return 0;
+out:
+ clk_disable(pltfm_host->clk);
+ clk_disable(sdhci_arasan->clk_ahb);
+
+ return ret;
+}
+#endif /* ! CONFIG_PM */
+
#ifdef CONFIG_PM_SLEEP
/**
* sdhci_arasan_suspend - Suspend method for the driver
* @dev: Address of the device structure
- * Returns 0 on success and error value on error
- *
* Put the device in a low power state.
+ *
+ * Return: 0 on success and error value on error
*/
static int sdhci_arasan_suspend(struct device *dev)
{
@@ -443,9 +668,9 @@ static int sdhci_arasan_suspend(struct device *dev)
/**
* sdhci_arasan_resume - Resume method for the driver
* @dev: Address of the device structure
- * Returns 0 on success and error value on error
- *
* Resume operation after suspend
+ *
+ * Return: 0 on success and error value on error
*/
static int sdhci_arasan_resume(struct device *dev)
{
@@ -488,8 +713,11 @@ static int sdhci_arasan_resume(struct device *dev)
}
#endif /* ! CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
- sdhci_arasan_resume);
+static const struct dev_pm_ops sdhci_arasan_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sdhci_arasan_suspend, sdhci_arasan_resume)
+ SET_RUNTIME_PM_OPS(sdhci_arasan_runtime_suspend,
+ sdhci_arasan_runtime_resume, NULL)
+};
static const struct of_device_id sdhci_arasan_of_match[] = {
/* SoC-specific compatible strings w/ soc_ctl_map */
@@ -514,6 +742,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
.compatible = "arasan,sdhci-4.9a",
.data = &sdhci_arasan_data,
},
+ {
+ .compatible = "xlnx,zynqmp-8.9a",
+ .data = &sdhci_arasan_data,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
@@ -521,12 +753,13 @@ MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
/**
* sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
*
+ * @hw: Pointer to the hardware clock structure.
+ * @parent_rate: The parent rate (should be rate of clk_xin).
+ *
* Return the current actual rate of the SD card clock. This can be used
* to communicate with out PHY.
*
- * @hw: Pointer to the hardware clock structure.
- * @parent_rate The parent rate (should be rate of clk_xin).
- * Returns the card clock rate.
+ * Return: the card clock rate.
*/
static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
@@ -546,6 +779,9 @@ static const struct clk_ops arasan_sdcardclk_ops = {
/**
* sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier
*
+ * @host: The sdhci_host
+ * @value: The value to write to syscon registers
+ *
* The corecfg_clockmultiplier is supposed to contain clock multiplier
* value of programmable clock generator.
*
@@ -557,8 +793,6 @@ static const struct clk_ops arasan_sdcardclk_ops = {
* - The value of corecfg_clockmultiplier should sync with that of corresponding
* value reading from sdhci_capability_register. So this function is called
* once at probe time and never called again.
- *
- * @host: The sdhci_host
*/
static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
u32 value)
@@ -585,6 +819,8 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
/**
* sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
*
+ * @host: The sdhci_host
+ *
* The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This
* function can be used to make that happen.
*
@@ -596,8 +832,6 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
* - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider
* to achieve lower clock rates. That means that this function is called once
* at probe time and never called again.
- *
- * @host: The sdhci_host
*/
static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host)
{
@@ -624,6 +858,10 @@ static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host)
/**
* sdhci_arasan_register_sdclk - Register the sdclk for a PHY to use
*
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
* Some PHY devices need to know what the actual card clock is. In order for
* them to find out, we'll provide a clock through the common clock framework
* for them to query.
@@ -636,10 +874,7 @@ static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host)
* to create nice clean device tree bindings and later (if needed) we can try
* re-architecting SDHCI if we see some benefit to it.
*
- * @sdhci_arasan: Our private data structure.
- * @clk_xin: Pointer to the functional clock
- * @dev: Pointer to our struct device.
- * Returns 0 on success and error value on error
+ * Return: 0 on success and error value on error
*/
static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
struct clk *clk_xin,
@@ -683,10 +918,10 @@ static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
/**
* sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk()
*
+ * @dev: Pointer to our struct device.
+ *
* Should be called any time we're exiting and sdhci_arasan_register_sdclk()
* returned success.
- *
- * @dev: Pointer to our struct device.
*/
static void sdhci_arasan_unregister_sdclk(struct device *dev)
{
@@ -741,6 +976,88 @@ cleanup:
return ret;
}
+static void arasan_dt_read_tap_delay(struct device *dev, u32 *tapdly,
+ u8 mode, const char *prop)
+{
+ struct device_node *np = dev->of_node;
+ /*
+ * Read Tap Delay values from DT, if the DT does not contain the
+ * Tap Values then use the pre-defined values
+ */
+ if (of_property_read_u32(np, prop, &tapdly[mode])) {
+ dev_dbg(dev, "Using predefined tapdly for %s = %d\n",
+ prop, tapdly[mode]);
+ }
+}
+
+/**
+ * arasan_dt_parse_tap_delays - Read Tap Delay values from DT
+ *
+ * @dev: Pointer to our struct device.
+ *
+ * Called at initialization to parse the values of Tap Delays.
+ */
+static void arasan_dt_parse_tap_delays(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ u32 *itapdly;
+ u32 *otapdly;
+ int i;
+
+ if (of_device_is_compatible(pdev->dev.of_node, "xlnx,zynqmp-8.9a")) {
+ itapdly = zynqmp_itap_delays;
+ otapdly = zynqmp_otap_delays;
+ if (sdhci_arasan->mio_bank == MMC_BANK2) {
+ otapdly[MMC_TIMING_UHS_SDR104] = 0x2;
+ otapdly[MMC_TIMING_MMC_HS200] = 0x2;
+ }
+ } else {
+ itapdly = versal_itap_delays;
+ otapdly = versal_otap_delays;
+ }
+
+ arasan_dt_read_tap_delay(dev, itapdly, MMC_TIMING_SD_HS,
+ "xlnx,itap-delay-sd-hsd");
+ arasan_dt_read_tap_delay(dev, itapdly, MMC_TIMING_UHS_SDR25,
+ "xlnx,itap-delay-sdr25");
+ arasan_dt_read_tap_delay(dev, itapdly, MMC_TIMING_UHS_SDR50,
+ "xlnx,itap-delay-sdr50");
+ arasan_dt_read_tap_delay(dev, itapdly, MMC_TIMING_UHS_SDR104,
+ "xlnx,itap-delay-sdr104");
+ arasan_dt_read_tap_delay(dev, itapdly, MMC_TIMING_UHS_DDR50,
+ "xlnx,itap-delay-sd-ddr50");
+ arasan_dt_read_tap_delay(dev, itapdly, MMC_TIMING_MMC_HS,
+ "xlnx,itap-delay-mmc-hsd");
+ arasan_dt_read_tap_delay(dev, itapdly, MMC_TIMING_MMC_DDR52,
+ "xlnx,itap-delay-mmc-ddr52");
+ arasan_dt_read_tap_delay(dev, itapdly, MMC_TIMING_MMC_HS200,
+ "xlnx,itap-delay-mmc-hs200");
+ arasan_dt_read_tap_delay(dev, otapdly, MMC_TIMING_SD_HS,
+ "xlnx,otap-delay-sd-hsd");
+ arasan_dt_read_tap_delay(dev, otapdly, MMC_TIMING_UHS_SDR25,
+ "xlnx,otap-delay-sdr25");
+ arasan_dt_read_tap_delay(dev, otapdly, MMC_TIMING_UHS_SDR50,
+ "xlnx,otap-delay-sdr50");
+ arasan_dt_read_tap_delay(dev, otapdly, MMC_TIMING_UHS_SDR104,
+ "xlnx,otap-delay-sdr104");
+ arasan_dt_read_tap_delay(dev, otapdly, MMC_TIMING_UHS_DDR50,
+ "xlnx,otap-delay-sd-ddr50");
+ arasan_dt_read_tap_delay(dev, otapdly, MMC_TIMING_MMC_HS,
+ "xlnx,otap-delay-mmc-hsd");
+ arasan_dt_read_tap_delay(dev, otapdly, MMC_TIMING_MMC_DDR52,
+ "xlnx,otap-delay-mmc-ddr52");
+ arasan_dt_read_tap_delay(dev, otapdly, MMC_TIMING_MMC_HS200,
+ "xlnx,otap-delay-mmc-hs200");
+
+ for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
+ sdhci_arasan->itapdly[i] = itapdly[i];
+ sdhci_arasan->otapdly[i] = otapdly[i];
+ }
+}
+
static int sdhci_arasan_probe(struct platform_device *pdev)
{
int ret;
@@ -751,10 +1068,35 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_arasan_data *sdhci_arasan;
struct device_node *np = pdev->dev.of_node;
+ unsigned int host_quirks2 = 0;
const struct sdhci_arasan_of_data *data;
match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
data = match->data;
+
+ if (of_device_is_compatible(pdev->dev.of_node, "xlnx,zynqmp-8.9a")) {
+ char *soc_rev;
+
+ /* read Silicon version using nvmem driver */
+ soc_rev = zynqmp_nvmem_get_silicon_version(&pdev->dev,
+ "soc_revision");
+ if (PTR_ERR(soc_rev) == -EPROBE_DEFER)
+ /* Do a deferred probe */
+ return -EPROBE_DEFER;
+ else if (IS_ERR(soc_rev))
+ dev_dbg(&pdev->dev, "Error getting silicon version\n");
+
+ /* Set host quirk if the silicon version is v1.0 */
+ if (!IS_ERR(soc_rev) && (*soc_rev == ZYNQMP_SILICON_V1))
+ host_quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+
+ /* Clean soc_rev if got a valid pointer from nvmem driver
+ * else we may end up in kernel panic
+ */
+ if (!IS_ERR(soc_rev))
+ kfree(soc_rev);
+ }
+
host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan));
if (IS_ERR(host))
@@ -766,6 +1108,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
+ host->quirks2 |= host_quirks2;
+
node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
if (node) {
sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node);
@@ -814,6 +1158,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (of_property_read_bool(np, "xlnx,int-clock-stable-broken"))
sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE;
+ if (of_device_is_compatible(pdev->dev.of_node, "xlnx,versal-8.9a"))
+ sdhci_arasan->quirks |= SDHCI_ARASAN_TAPDELAY_REG_LOCAL;
+
pltfm_host->clk = clk_xin;
if (of_device_is_compatible(pdev->dev.of_node,
@@ -833,6 +1180,50 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
goto unreg_clk;
}
+ if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-8.9a")) {
+ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+ host->quirks2 |= SDHCI_QUIRK2_CLOCK_STANDARD_25_BROKEN;
+ }
+
+ if (of_device_is_compatible(pdev->dev.of_node, "xlnx,zynqmp-8.9a") ||
+ of_device_is_compatible(pdev->dev.of_node, "xlnx,versal-8.9a")) {
+ arasan_dt_parse_tap_delays(&pdev->dev);
+ }
+
+ if (of_device_is_compatible(pdev->dev.of_node, "xlnx,zynqmp-8.9a")) {
+ ret = of_property_read_u32(pdev->dev.of_node, "xlnx,mio_bank",
+ &sdhci_arasan->mio_bank);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "\"xlnx,mio_bank \" property is missing.\n");
+ goto clk_disable_all;
+ }
+ ret = of_property_read_u32(pdev->dev.of_node, "xlnx,device_id",
+ &sdhci_arasan->device_id);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "\"xlnx,device_id \" property is missing.\n");
+ goto clk_disable_all;
+ }
+
+ host->mmc_host_ops.execute_tuning =
+ arasan_zynqmp_execute_tuning;
+ }
+
+ sdhci_arasan->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (!IS_ERR(sdhci_arasan->pinctrl)) {
+ sdhci_arasan->pins_default = pinctrl_lookup_state(
+ sdhci_arasan->pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(sdhci_arasan->pins_default)) {
+ dev_err(&pdev->dev, "Missing default pinctrl config\n");
+ return IS_ERR(sdhci_arasan->pins_default);
+ }
+
+ pinctrl_select_state(sdhci_arasan->pinctrl,
+ sdhci_arasan->pins_default);
+ }
+
sdhci_arasan->phy = ERR_PTR(-ENODEV);
if (of_device_is_compatible(pdev->dev.of_node,
"arasan,sdhci-5.1")) {
@@ -865,6 +1256,13 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (ret)
goto err_add_host;
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_forbid(&pdev->dev);
+
return 0;
err_add_host: