Read and register the sampleclk and sdcardclk. They are needed later for
HS200 tuning support.

Signed-off-by: Steffen Trumtrar <s.trumt...@pengutronix.de>
Reviewed-by: Ahmad Fatoum <a.fat...@pengutronix.de>
---
 drivers/mci/arasan-sdhci.c | 316 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+)

diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index 0e4c1287ae..4111b3321a 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -6,6 +6,7 @@
 #include <init.h>
 #include <linux/clk.h>
 #include <mci.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
 
 #include "sdhci.h"
 
@@ -40,12 +41,20 @@
 /**
  * struct sdhci_arasan_clk_data - Arasan Controller Clock Data.
  *
+ * @sdcardclk_hw:      Struct for the clock we might provide to a PHY.
+ * @sdcardclk:         Pointer to normal 'struct clock' for sdcardclk_hw.
+ * @sampleclk_hw:      Struct for the clock we might provide to a PHY.
+ * @sampleclk:         Pointer to normal 'struct clock' for sampleclk_hw.
  * @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;
+       struct clk      *sdcardclk;
+       struct clk_hw   sampleclk_hw;
+       struct clk      *sampleclk;
        int             clk_phase_in[ZYNQMP_CLK_PHASES + 1];
        int             clk_phase_out[ZYNQMP_CLK_PHASES + 1];
        void            (*set_clk_delays)(struct sdhci *host);
@@ -154,6 +163,11 @@ static void arasan_sdhci_set_clock(struct mci_host *mci, 
unsigned int clock)
                        clock = (25000000 * 19) / 25;
        }
 
+       clk_set_phase(clk_data->sampleclk,
+                     clk_data->clk_phase_in[mci->mci->host->timing]);
+       clk_set_phase(clk_data->sdcardclk,
+                     clk_data->clk_phase_out[mci->mci->host->timing]);
+
        sdhci_set_clock(&host->sdhci, clock, host->sdhci.max_clk);
 }
 
@@ -251,6 +265,10 @@ static void sdhci_arasan_set_clk_delays(struct sdhci *host)
        struct mci_host *mci = &arasan_sdhci->mci;
        struct sdhci_arasan_clk_data *clk_data = &arasan_sdhci->clk_data;
 
+       clk_set_phase(clk_data->sampleclk,
+                     clk_data->clk_phase_in[mci->timing]);
+       clk_set_phase(clk_data->sdcardclk,
+                     clk_data->clk_phase_out[mci->timing]);
 }
 
 static void arasan_dt_read_clk_phase(struct device *dev,
@@ -279,6 +297,302 @@ static void arasan_dt_read_clk_phase(struct device *dev,
        clk_data->clk_phase_out[timing] = clk_phase[1];
 }
 
+/**
+ * sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
+ *
+ * @hw:                        Pointer to the hardware clock structure.
+ * @degrees:           The clock phase shift between 0 - 359.
+ *
+ * Set the SD Input Clock Tap Delays for Input path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_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 arasan_sdhci_host *sdhci_arasan =
+               container_of(clk_data, struct arasan_sdhci_host, clk_data);
+       struct mci_host *host = &sdhci_arasan->mci;
+       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 */
+       if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
+               return 0;
+
+       /* Assert DLL Reset */
+       zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_ASSERT);
+
+       switch (host->timing) {
+       case MMC_TIMING_MMC_HS:
+       case MMC_TIMING_SD_HS:
+       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;
+               break;
+       default:
+               break;
+       }
+
+       tap_delay = (degrees * tap_max) / 360;
+
+       /* Set the Clock Phase */
+       ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_INPUT, tap_delay);
+       if (ret)
+               pr_err("Error setting Input Tap Delay\n");
+
+       return ret;
+}
+
+static unsigned long arasan_zynqmp_sampleclk_recalc_rate(struct clk_hw *hw,
+                                                        unsigned long 
parent_rate)
+{
+       struct sdhci_arasan_clk_data *clk_data =
+               container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+       struct arasan_sdhci_host *sdhci_arasan =
+               container_of(clk_data, struct arasan_sdhci_host, clk_data);
+       struct mci_host *host = &sdhci_arasan->mci;
+
+       return host->actual_clock;
+};
+
+/**
+ * sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
+ *
+ * @hw:                        Pointer to the hardware clock structure.
+ * @degrees:           The clock phase shift between 0 - 359.
+ *
+ * Set the SD Output Clock Tap Delays for Output path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_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 arasan_sdhci_host *sdhci_arasan =
+               container_of(clk_data, struct arasan_sdhci_host, clk_data);
+       struct mci_host *host = &sdhci_arasan->mci;
+       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 */
+       if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
+               return 0;
+
+       switch (host->timing) {
+       case MMC_TIMING_MMC_HS:
+       case MMC_TIMING_SD_HS:
+       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;
+               break;
+       default:
+               break;
+       }
+
+       tap_delay = (degrees * tap_max) / 360;
+
+       /* Set the Clock Phase */
+       ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_OUTPUT, tap_delay);
+       if (ret)
+               pr_err("Error setting Output Tap Delay\n");
+
+       /* Release DLL Reset */
+       zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_RELEASE);
+
+       return ret;
+}
+
+static unsigned long arasan_zynqmp_sdcardclk_recalc_rate(struct clk_hw *hw,
+                                                        unsigned long 
parent_rate)
+{
+       struct sdhci_arasan_clk_data *clk_data =
+               container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+       struct arasan_sdhci_host *sdhci_arasan =
+               container_of(clk_data, struct arasan_sdhci_host, clk_data);
+       struct mci_host *host = &sdhci_arasan->mci;
+
+       return host->actual_clock;
+};
+
+static const struct clk_ops clk_sampleclk_ops = {
+       .recalc_rate = arasan_zynqmp_sampleclk_recalc_rate,
+       .set_phase = arasan_zynqmp_sampleclk_set_phase,
+};
+
+static const struct clk_ops clk_sdcardclk_ops = {
+       .recalc_rate = arasan_zynqmp_sdcardclk_recalc_rate,
+       .set_phase = arasan_zynqmp_sdcardclk_set_phase,
+};
+
+/**
+ * arasan_sdhci_register_sampleclk - Register the sampleclk 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.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+arasan_sdhci_register_sampleclk(struct sdhci_arasan_clk_data *clk_data,
+                               struct clk *clk_xin,
+                               struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct clk_init_data sampleclk_init = {};
+       const char *clk_name;
+       int ret;
+
+       ret = of_property_read_string_index(np, "clock-output-names", 1,
+                                           &sampleclk_init.name);
+       if (ret) {
+               dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+               return ret;
+       }
+
+       clk_name = __clk_get_name(clk_xin);
+       sampleclk_init.parent_names = &clk_name;
+       sampleclk_init.num_parents = 1;
+       sampleclk_init.ops = &clk_sampleclk_ops;
+
+       clk_data->sampleclk_hw.init = &sampleclk_init;
+       clk_data->sampleclk = clk_register(dev, &clk_data->sampleclk_hw);
+       if (IS_ERR(clk_data->sampleclk))
+               return PTR_ERR(clk_data->sampleclk);
+       clk_data->sampleclk_hw.init = NULL;
+
+       ret = of_clk_add_provider(np, of_clk_src_simple_get,
+                                 clk_data->sampleclk);
+       if (ret)
+               dev_err(dev, "Failed to add sample clock provider\n");
+
+       return ret;
+}
+
+/**
+ * sdhci_arasan_register_sdcardclk - Register the sdcardclk 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.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+arasan_sdhci_register_sdcardclk(struct sdhci_arasan_clk_data *clk_data,
+                               struct clk *clk_xin,
+                               struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct clk_init_data sdcardclk_init = {};
+       const char *clk_name;
+       int ret;
+
+       ret = of_property_read_string_index(np, "clock-output-names", 0,
+                                           &sdcardclk_init.name);
+       if (ret) {
+               dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+               return ret;
+       }
+
+       clk_name = __clk_get_name(clk_xin);
+       sdcardclk_init.parent_names = &clk_name;
+       sdcardclk_init.ops = &clk_sdcardclk_ops;
+       sdcardclk_init.num_parents = 1;
+
+       clk_data->sdcardclk_hw.init = &sdcardclk_init;
+       clk_data->sdcardclk = clk_register(dev, &clk_data->sdcardclk_hw);
+       if (IS_ERR(clk_data->sdcardclk))
+               return PTR_ERR(clk_data->sdcardclk);
+       clk_data->sdcardclk_hw.init = NULL;
+
+       ret = of_clk_add_provider(np, of_clk_src_simple_get,
+                                 clk_data->sdcardclk);
+       if (ret)
+               dev_err(dev, "Failed to add sdcard clock provider\n");
+
+       return ret;
+}
+
+/**
+ * arasan_sdhci_register_sdclk - Register the sdcardclk 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.
+ *
+ * Note: without seriously re-architecting SDHCI's clock code and testing on
+ * all platforms, there's no way to create a totally beautiful clock here
+ * with all clock ops implemented.  Instead, we'll just create a clock that can
+ * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock
+ * framework that we're doing things behind its back.  This should be 
sufficient
+ * to create nice clean device tree bindings and later (if needed) we can try
+ * re-architecting SDHCI if we see some benefit to it.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_sdhci_register_sdclk(struct sdhci_arasan_clk_data 
*sdhci_arasan,
+                                      struct clk *clk_xin,
+                                      struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       u32 num_clks = 0;
+       int ret;
+
+       /* Providing a clock to the PHY is optional; no error if missing */
+       if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
+               return 0;
+
+       ret = arasan_sdhci_register_sdcardclk(sdhci_arasan, clk_xin, dev);
+       if (ret)
+               return ret;
+
+       if (num_clks)
+               return arasan_sdhci_register_sampleclk(sdhci_arasan, clk_xin,
+                                                      dev);
+
+       return 0;
+}
+
 /**
  * arasan_dt_parse_clk_phases - Read Clock Delay values from DT
  *
@@ -402,6 +716,8 @@ static int arasan_sdhci_probe(struct device *dev)
        mci->f_max = clk_get_rate(clk_xin);
        mci->f_min = 50000000 / 256;
 
+       arasan_sdhci_register_sdclk(&arasan_sdhci->clk_data, clk_xin, dev);
+
        arasan_dt_parse_clk_phases(dev, &arasan_sdhci->clk_data);
 
        /* parse board supported bus width capabilities */

-- 
2.40.1


Reply via email to