mmc: sdhci-of-arasan: Add support for ZynqMP Platform Tap Delays Setup
Apart from taps set by auto tuning, ZynqMP platform has feature to set the tap values manually. Add support to set tap delay values in HW via ZynqMP SoC framework. Signed-off-by: Manish Narani <manish.narani@xilinx.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
1297eacfe7
commit
a5c8b2ae2e
|
@ -22,6 +22,7 @@
|
|||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/firmware/xlnx-zynqmp.h>
|
||||
|
||||
#include "cqhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
|
@ -32,6 +33,10 @@
|
|||
|
||||
#define PHY_CLK_TOO_SLOW_HZ 400000
|
||||
|
||||
/* Default settings for ZynqMP Clock Phases */
|
||||
#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
|
||||
#define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -80,6 +85,7 @@ struct sdhci_arasan_soc_ctl_map {
|
|||
* @clk_phase_in: Array of Input Clock Phase Delays for all speed modes
|
||||
* @clk_phase_out: Array of Output Clock Phase Delays for all speed modes
|
||||
* @set_clk_delays: Function pointer for setting Clock Delays
|
||||
* @clk_of_data: Platform specific runtime clock data storage pointer
|
||||
*/
|
||||
struct sdhci_arasan_clk_data {
|
||||
struct clk_hw sdcardclk_hw;
|
||||
|
@ -89,6 +95,11 @@ struct sdhci_arasan_clk_data {
|
|||
int clk_phase_in[MMC_TIMING_MMC_HS400 + 1];
|
||||
int clk_phase_out[MMC_TIMING_MMC_HS400 + 1];
|
||||
void (*set_clk_delays)(struct sdhci_host *host);
|
||||
void *clk_of_data;
|
||||
};
|
||||
|
||||
struct sdhci_arasan_zynqmp_clk_data {
|
||||
const struct zynqmp_eemi_ops *eemi_ops;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -540,6 +551,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);
|
||||
|
@ -598,6 +613,150 @@ static const struct clk_ops arasan_sampleclk_ops = {
|
|||
.recalc_rate = sdhci_arasan_sampleclk_recalc_rate,
|
||||
};
|
||||
|
||||
/**
|
||||
* sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
|
||||
*
|
||||
* Set the SD Output Clock Tap Delays for Output path
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @degrees The clock phase shift between 0 - 359.
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
|
||||
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
|
||||
struct sdhci_arasan_data *sdhci_arasan =
|
||||
container_of(clk_data, struct sdhci_arasan_data, clk_data);
|
||||
struct sdhci_host *host = sdhci_arasan->host;
|
||||
struct sdhci_arasan_zynqmp_clk_data *zynqmp_clk_data =
|
||||
clk_data->clk_of_data;
|
||||
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_clk_data->eemi_ops;
|
||||
const char *clk_name = clk_hw_get_name(hw);
|
||||
u32 node_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1;
|
||||
u8 tap_delay, tap_max = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* This is applicable for SDHCI_SPEC_300 and above
|
||||
* ZynqMP does not set phase for <=25MHz clock.
|
||||
* If degrees is zero, no need to do anything.
|
||||
*/
|
||||
if (host->version < SDHCI_SPEC_300 ||
|
||||
host->timing == MMC_TIMING_LEGACY ||
|
||||
host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
|
||||
return 0;
|
||||
|
||||
switch (host->timing) {
|
||||
case MMC_TIMING_MMC_HS:
|
||||
case MMC_TIMING_SD_HS:
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
/* For 50MHz clock, 30 Taps are available */
|
||||
tap_max = 30;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
/* For 100MHz clock, 15 Taps are available */
|
||||
tap_max = 15;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
/* For 200MHz clock, 8 Taps are available */
|
||||
tap_max = 8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tap_delay = (degrees * tap_max) / 360;
|
||||
|
||||
/* Set the Clock Phase */
|
||||
ret = eemi_ops->ioctl(node_id, IOCTL_SET_SD_TAPDELAY,
|
||||
PM_TAPDELAY_OUTPUT, tap_delay, NULL);
|
||||
if (ret)
|
||||
pr_err("Error setting Output Tap Delay\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct clk_ops zynqmp_sdcardclk_ops = {
|
||||
.recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
|
||||
.set_phase = sdhci_zynqmp_sdcardclk_set_phase,
|
||||
};
|
||||
|
||||
/**
|
||||
* sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
|
||||
*
|
||||
* Set the SD Input Clock Tap Delays for Input path
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @degrees The clock phase shift between 0 - 359.
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
|
||||
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
|
||||
struct sdhci_arasan_data *sdhci_arasan =
|
||||
container_of(clk_data, struct sdhci_arasan_data, clk_data);
|
||||
struct sdhci_host *host = sdhci_arasan->host;
|
||||
struct sdhci_arasan_zynqmp_clk_data *zynqmp_clk_data =
|
||||
clk_data->clk_of_data;
|
||||
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_clk_data->eemi_ops;
|
||||
const char *clk_name = clk_hw_get_name(hw);
|
||||
u32 node_id = !strcmp(clk_name, "clk_in_sd0") ? NODE_SD_0 : NODE_SD_1;
|
||||
u8 tap_delay, tap_max = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* This is applicable for SDHCI_SPEC_300 and above
|
||||
* ZynqMP does not set phase for <=25MHz clock.
|
||||
* If degrees is zero, no need to do anything.
|
||||
*/
|
||||
if (host->version < SDHCI_SPEC_300 ||
|
||||
host->timing == MMC_TIMING_LEGACY ||
|
||||
host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
|
||||
return 0;
|
||||
|
||||
switch (host->timing) {
|
||||
case MMC_TIMING_MMC_HS:
|
||||
case MMC_TIMING_SD_HS:
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
/* For 50MHz clock, 120 Taps are available */
|
||||
tap_max = 120;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
/* For 100MHz clock, 60 Taps are available */
|
||||
tap_max = 60;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
/* For 200MHz clock, 30 Taps are available */
|
||||
tap_max = 30;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tap_delay = (degrees * tap_max) / 360;
|
||||
|
||||
/* Set the Clock Phase */
|
||||
ret = eemi_ops->ioctl(node_id, IOCTL_SET_SD_TAPDELAY,
|
||||
PM_TAPDELAY_INPUT, tap_delay, NULL);
|
||||
if (ret)
|
||||
pr_err("Error setting Input Tap Delay\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct clk_ops zynqmp_sampleclk_ops = {
|
||||
.recalc_rate = sdhci_arasan_sampleclk_recalc_rate,
|
||||
.set_phase = sdhci_zynqmp_sampleclk_set_phase,
|
||||
};
|
||||
|
||||
/**
|
||||
* sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier
|
||||
*
|
||||
|
@ -724,6 +883,10 @@ static void arasan_dt_read_clk_phase(struct device *dev,
|
|||
static void arasan_dt_parse_clk_phases(struct device *dev,
|
||||
struct sdhci_arasan_clk_data *clk_data)
|
||||
{
|
||||
int *iclk_phase, *oclk_phase;
|
||||
u32 mio_bank = 0;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* This has been kept as a pointer and is assigned a function here.
|
||||
* So that different controller variants can assign their own handling
|
||||
|
@ -731,6 +894,22 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
|
|||
*/
|
||||
clk_data->set_clk_delays = sdhci_arasan_set_clk_delays;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "xlnx,zynqmp-8.9a")) {
|
||||
iclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) ZYNQMP_ICLK_PHASE;
|
||||
oclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) ZYNQMP_OCLK_PHASE;
|
||||
|
||||
of_property_read_u32(dev->of_node, "xlnx,mio-bank", &mio_bank);
|
||||
if (mio_bank == 2) {
|
||||
oclk_phase[MMC_TIMING_UHS_SDR104] = 90;
|
||||
oclk_phase[MMC_TIMING_MMC_HS200] = 90;
|
||||
}
|
||||
|
||||
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
|
||||
clk_data->clk_phase_in[i] = iclk_phase[i];
|
||||
clk_data->clk_phase_out[i] = oclk_phase[i];
|
||||
}
|
||||
}
|
||||
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
|
||||
"clk-phase-legacy");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
|
||||
|
@ -789,7 +968,10 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
|
|||
sdcardclk_init.parent_names = &parent_clk_name;
|
||||
sdcardclk_init.num_parents = 1;
|
||||
sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
|
||||
sdcardclk_init.ops = &arasan_sdcardclk_ops;
|
||||
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
|
||||
sdcardclk_init.ops = &zynqmp_sdcardclk_ops;
|
||||
else
|
||||
sdcardclk_init.ops = &arasan_sdcardclk_ops;
|
||||
|
||||
clk_data->sdcardclk_hw.init = &sdcardclk_init;
|
||||
clk_data->sdcardclk =
|
||||
|
@ -838,7 +1020,10 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
|
|||
sampleclk_init.parent_names = &parent_clk_name;
|
||||
sampleclk_init.num_parents = 1;
|
||||
sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
|
||||
sampleclk_init.ops = &arasan_sampleclk_ops;
|
||||
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
|
||||
sampleclk_init.ops = &zynqmp_sampleclk_ops;
|
||||
else
|
||||
sampleclk_init.ops = &arasan_sampleclk_ops;
|
||||
|
||||
clk_data->sampleclk_hw.init = &sampleclk_init;
|
||||
clk_data->sampleclk =
|
||||
|
@ -1047,6 +1232,23 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto clk_disable_all;
|
||||
|
||||
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) {
|
||||
struct sdhci_arasan_zynqmp_clk_data *zynqmp_clk_data;
|
||||
const struct zynqmp_eemi_ops *eemi_ops;
|
||||
|
||||
zynqmp_clk_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*zynqmp_clk_data),
|
||||
GFP_KERNEL);
|
||||
eemi_ops = zynqmp_pm_get_eemi_ops();
|
||||
if (IS_ERR(eemi_ops)) {
|
||||
ret = PTR_ERR(eemi_ops);
|
||||
goto unreg_clk;
|
||||
}
|
||||
|
||||
zynqmp_clk_data->eemi_ops = eemi_ops;
|
||||
sdhci_arasan->clk_data.clk_of_data = zynqmp_clk_data;
|
||||
}
|
||||
|
||||
arasan_dt_parse_clk_phases(&pdev->dev, &sdhci_arasan->clk_data);
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
|
|
Loading…
Reference in a new issue