Skip to content

Commit

Permalink
mmc: sdhci-of-arasan: Add support for ZynqMP Platform Tap Delays Setup
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Manish Narani authored and Ulf Hansson committed Nov 20, 2019
1 parent 1297eac commit a5c8b2a
Showing 1 changed file with 204 additions and 2 deletions.
206 changes: 204 additions & 2 deletions drivers/mmc/host/sdhci-of-arasan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
};

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -724,13 +883,33 @@ 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
* function.
*/
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,
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit a5c8b2a

Please sign in to comment.