]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
clk: renesas: rcar-gen3: Switch to new SD clock handling
authorWolfram Sang <wsa+renesas@sang-engineering.com>
Wed, 10 Nov 2021 19:15:54 +0000 (20:15 +0100)
committerGeert Uytterhoeven <geert+renesas@glider.be>
Fri, 19 Nov 2021 10:32:39 +0000 (11:32 +0100)
The old SD handling code was huge and could not handle all the details
which showed up on R-Car Gen3 SoCs meanwhile. It is time to switch to
another design. Have SDnH a separate clock, use the existing divider
clocks and move the errata handling from the clock driver to the SDHI
driver where it belongs.

This patch removes the old SD handling code and switch to the new one.
This updates the SDHI driver at the same time. Because the SDHI driver
can only communicate with the clock driver via clk_set_rate(), I don't
see an alternative to this flag-day-approach, so we cross subsystems
here.

The patch sadly looks messy for the CPG lib, but it is basically a huge
chunk of code removed and smaller chunks added. It looks much better
when you just view the resulting source file.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Acked-by: Ulf Hansson <ulf.hansson@linaro.org> # For MMC
Link: https://lore.kernel.org/r/20211110191610.5664-6-wsa+renesas@sang-engineering.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
drivers/clk/renesas/r8a779a0-cpg-mssr.c
drivers/clk/renesas/rcar-cpg-lib.c
drivers/clk/renesas/rcar-cpg-lib.h
drivers/clk/renesas/rcar-gen3-cpg.c
drivers/mmc/host/renesas_sdhi.h
drivers/mmc/host/renesas_sdhi_core.c

index 935eaaf567cfdda659b36ce3cd1242607b680813..b89075f5fa89337fc5c4349954bc97123c2e6f9e 100644 (file)
@@ -479,10 +479,8 @@ static struct clk * __init rcar_r8a779a0_cpg_clk_register(struct device *dev,
                                           __clk_get_name(parent), notifiers);
 
        case CLK_TYPE_R8A779A0_SD:
-               return cpg_sd_clk_register(core->name, base, core->offset,
-                                          __clk_get_name(parent), notifiers,
-                                          false);
-               break;
+               return cpg_sd_clk_register(core->name, base + core->offset,
+                                          __clk_get_name(parent));
 
        case CLK_TYPE_R8A779A0_MDSEL:
                /*
index de8d21e4c6c55f0a82e908abc4b1e922b6edb2c4..e2e0447de1901d06a9e04ffa7cbbfcb0b7a6a1f2 100644 (file)
@@ -66,214 +66,48 @@ void cpg_simple_notifier_register(struct raw_notifier_head *notifiers,
  * SDn Clock
  */
 
-struct clk * __init cpg_sdh_clk_register(const char *name,
-       void __iomem *sdnckcr, const char *parent_name,
-       struct raw_notifier_head *notifiers)
-{
-       /* placeholder during transition */
-       return clk_register_fixed_factor(NULL, name, parent_name, 0, 1, 1);
-}
-
-#define CPG_SD_STP_HCK         BIT(9)
-#define CPG_SD_STP_CK          BIT(8)
-
-#define CPG_SD_STP_MASK                (CPG_SD_STP_HCK | CPG_SD_STP_CK)
-#define CPG_SD_FC_MASK         (0x7 << 2 | 0x3 << 0)
-
-#define CPG_SD_DIV_TABLE_DATA(stp_hck, sd_srcfc, sd_fc, sd_div) \
-{ \
-       .val = ((stp_hck) ? CPG_SD_STP_HCK : 0) | \
-              ((sd_srcfc) << 2) | \
-              ((sd_fc) << 0), \
-       .div = (sd_div), \
-}
-
-struct sd_div_table {
-       u32 val;
-       unsigned int div;
-};
+#define SDnSRCFC_SHIFT 2
+#define STPnHCK        BIT(9 - SDnSRCFC_SHIFT)
 
-struct sd_clock {
-       struct clk_hw hw;
-       const struct sd_div_table *div_table;
-       struct cpg_simple_notifier csn;
-       unsigned int div_num;
-       unsigned int cur_div_idx;
-};
-
-/* SDn divider
- *           sd_srcfc   sd_fc   div
- * stp_hck   (div)      (div)     = sd_srcfc x sd_fc
- *---------------------------------------------------------
- *  0         0 (1)      1 (4)      4 : SDR104 / HS200 / HS400 (8 TAP)
- *  0         1 (2)      1 (4)      8 : SDR50
- *  1         2 (4)      1 (4)     16 : HS / SDR25
- *  1         3 (8)      1 (4)     32 : NS / SDR12
- *  1         4 (16)     1 (4)     64
- *  0         0 (1)      0 (2)      2
- *  0         1 (2)      0 (2)      4 : SDR104 / HS200 / HS400 (4 TAP)
- *  1         2 (4)      0 (2)      8
- *  1         3 (8)      0 (2)     16
- *  1         4 (16)     0 (2)     32
- *
- *  NOTE: There is a quirk option to ignore the first row of the dividers
- *  table when searching for suitable settings. This is because HS400 on
- *  early ES versions of H3 and M3-W requires a specific setting to work.
- */
-static const struct sd_div_table cpg_sd_div_table[] = {
-/*     CPG_SD_DIV_TABLE_DATA(stp_hck,  sd_srcfc,   sd_fc,  sd_div) */
-       CPG_SD_DIV_TABLE_DATA(0,        0,          1,        4),
-       CPG_SD_DIV_TABLE_DATA(0,        1,          1,        8),
-       CPG_SD_DIV_TABLE_DATA(1,        2,          1,       16),
-       CPG_SD_DIV_TABLE_DATA(1,        3,          1,       32),
-       CPG_SD_DIV_TABLE_DATA(1,        4,          1,       64),
-       CPG_SD_DIV_TABLE_DATA(0,        0,          0,        2),
-       CPG_SD_DIV_TABLE_DATA(0,        1,          0,        4),
-       CPG_SD_DIV_TABLE_DATA(1,        2,          0,        8),
-       CPG_SD_DIV_TABLE_DATA(1,        3,          0,       16),
-       CPG_SD_DIV_TABLE_DATA(1,        4,          0,       32),
+static const struct clk_div_table cpg_sdh_div_table[] = {
+       { 0, 1 }, { 1, 2 }, { STPnHCK | 2, 4 }, { STPnHCK | 3, 8 },
+       { STPnHCK | 4, 16 }, { 0, 0 },
 };
 
-#define to_sd_clock(_hw) container_of(_hw, struct sd_clock, hw)
-
-static int cpg_sd_clock_enable(struct clk_hw *hw)
-{
-       struct sd_clock *clock = to_sd_clock(hw);
-
-       cpg_reg_modify(clock->csn.reg, CPG_SD_STP_MASK,
-                      clock->div_table[clock->cur_div_idx].val &
-                      CPG_SD_STP_MASK);
-
-       return 0;
-}
-
-static void cpg_sd_clock_disable(struct clk_hw *hw)
-{
-       struct sd_clock *clock = to_sd_clock(hw);
-
-       cpg_reg_modify(clock->csn.reg, 0, CPG_SD_STP_MASK);
-}
-
-static int cpg_sd_clock_is_enabled(struct clk_hw *hw)
+struct clk * __init cpg_sdh_clk_register(const char *name,
+       void __iomem *sdnckcr, const char *parent_name,
+       struct raw_notifier_head *notifiers)
 {
-       struct sd_clock *clock = to_sd_clock(hw);
-
-       return !(readl(clock->csn.reg) & CPG_SD_STP_MASK);
-}
+       struct cpg_simple_notifier *csn;
+       struct clk *clk;
 
-static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw,
-                                               unsigned long parent_rate)
-{
-       struct sd_clock *clock = to_sd_clock(hw);
+       csn = kzalloc(sizeof(*csn), GFP_KERNEL);
+       if (!csn)
+               return ERR_PTR(-ENOMEM);
 
-       return DIV_ROUND_CLOSEST(parent_rate,
-                                clock->div_table[clock->cur_div_idx].div);
-}
+       csn->reg = sdnckcr;
 
-static int cpg_sd_clock_determine_rate(struct clk_hw *hw,
-                                      struct clk_rate_request *req)
-{
-       unsigned long best_rate = ULONG_MAX, diff_min = ULONG_MAX;
-       struct sd_clock *clock = to_sd_clock(hw);
-       unsigned long calc_rate, diff;
-       unsigned int i;
-
-       for (i = 0; i < clock->div_num; i++) {
-               calc_rate = DIV_ROUND_CLOSEST(req->best_parent_rate,
-                                             clock->div_table[i].div);
-               if (calc_rate < req->min_rate || calc_rate > req->max_rate)
-                       continue;
-
-               diff = calc_rate > req->rate ? calc_rate - req->rate
-                                            : req->rate - calc_rate;
-               if (diff < diff_min) {
-                       best_rate = calc_rate;
-                       diff_min = diff;
-               }
+       clk = clk_register_divider_table(NULL, name, parent_name, 0, sdnckcr,
+                                        SDnSRCFC_SHIFT, 8, 0, cpg_sdh_div_table,
+                                        &cpg_lock);
+       if (IS_ERR(clk)) {
+               kfree(csn);
+               return clk;
        }
 
-       if (best_rate == ULONG_MAX)
-               return -EINVAL;
-
-       req->rate = best_rate;
-       return 0;
-}
-
-static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate,
-                                unsigned long parent_rate)
-{
-       struct sd_clock *clock = to_sd_clock(hw);
-       unsigned int i;
-
-       for (i = 0; i < clock->div_num; i++)
-               if (rate == DIV_ROUND_CLOSEST(parent_rate,
-                                             clock->div_table[i].div))
-                       break;
-
-       if (i >= clock->div_num)
-               return -EINVAL;
-
-       clock->cur_div_idx = i;
-
-       cpg_reg_modify(clock->csn.reg, CPG_SD_STP_MASK | CPG_SD_FC_MASK,
-                      clock->div_table[i].val &
-                      (CPG_SD_STP_MASK | CPG_SD_FC_MASK));
-
-       return 0;
+       cpg_simple_notifier_register(notifiers, csn);
+       return clk;
 }
 
-static const struct clk_ops cpg_sd_clock_ops = {
-       .enable = cpg_sd_clock_enable,
-       .disable = cpg_sd_clock_disable,
-       .is_enabled = cpg_sd_clock_is_enabled,
-       .recalc_rate = cpg_sd_clock_recalc_rate,
-       .determine_rate = cpg_sd_clock_determine_rate,
-       .set_rate = cpg_sd_clock_set_rate,
+static const struct clk_div_table cpg_sd_div_table[] = {
+       { 0, 2 }, { 1, 4 }, { 0, 0 },
 };
 
 struct clk * __init cpg_sd_clk_register(const char *name,
-       void __iomem *base, unsigned int offset, const char *parent_name,
-       struct raw_notifier_head *notifiers, bool skip_first)
+       void __iomem *sdnckcr, const char *parent_name)
 {
-       struct clk_init_data init = {};
-       struct sd_clock *clock;
-       struct clk *clk;
-       u32 val;
-
-       clock = kzalloc(sizeof(*clock), GFP_KERNEL);
-       if (!clock)
-               return ERR_PTR(-ENOMEM);
-
-       init.name = name;
-       init.ops = &cpg_sd_clock_ops;
-       init.flags = CLK_SET_RATE_PARENT;
-       init.parent_names = &parent_name;
-       init.num_parents = 1;
-
-       clock->csn.reg = base + offset;
-       clock->hw.init = &init;
-       clock->div_table = cpg_sd_div_table;
-       clock->div_num = ARRAY_SIZE(cpg_sd_div_table);
-
-       if (skip_first) {
-               clock->div_table++;
-               clock->div_num--;
-       }
-
-       val = readl(clock->csn.reg) & ~CPG_SD_FC_MASK;
-       val |= CPG_SD_STP_MASK | (clock->div_table[0].val & CPG_SD_FC_MASK);
-       writel(val, clock->csn.reg);
-
-       clk = clk_register(NULL, &clock->hw);
-       if (IS_ERR(clk))
-               goto free_clock;
-
-       cpg_simple_notifier_register(notifiers, &clock->csn);
-       return clk;
-
-free_clock:
-       kfree(clock);
-       return clk;
+       return clk_register_divider_table(NULL, name, parent_name, 0, sdnckcr,
+                                         0, 2, 0, cpg_sd_div_table, &cpg_lock);
 }
 
 struct rpc_clock {
index d001722ec13f33159b7938bff0fade70848dc28e..94627df1c94c49a400ad99fea772ea0dbb3e4b4b 100644 (file)
@@ -31,8 +31,7 @@ struct clk * __init cpg_sdh_clk_register(const char *name,
        struct raw_notifier_head *notifiers);
 
 struct clk * __init cpg_sd_clk_register(const char *name,
-       void __iomem *base, unsigned int offset, const char *parent_name,
-       struct raw_notifier_head *notifiers, bool skip_first);
+       void __iomem *sdnckcr, const char *parent_name);
 
 struct clk * __init cpg_rpc_clk_register(const char *name,
        void __iomem *rpcckcr, const char *parent_name,
index 0c815684dd47e3a56d4017d95dda330f07e186b3..941dadf07230a08a5b0a43000d4550dc4677310f 100644 (file)
@@ -406,9 +406,8 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
                                           __clk_get_name(parent), notifiers);
 
        case CLK_TYPE_GEN3_SD:
-               return cpg_sd_clk_register(core->name, base, core->offset,
-                                          __clk_get_name(parent), notifiers,
-                                          cpg_quirks & SD_SKIP_FIRST);
+               return cpg_sd_clk_register(core->name, base + core->offset,
+                                          __clk_get_name(parent));
 
        case CLK_TYPE_GEN3_R:
                if (cpg_quirks & RCKCR_CKSEL) {
index cd82420677ccd1cd1328f64244293fa730220e12..66d308e73e179774536ff09121d27b0988eddf61 100644 (file)
@@ -60,6 +60,7 @@ struct tmio_mmc_dma {
 
 struct renesas_sdhi {
        struct clk *clk;
+       struct clk *clkh;
        struct clk *clk_cd;
        struct tmio_mmc_data mmc_data;
        struct tmio_mmc_dma dma_priv;
index a4407f391f66a6f2f05bebad1334748895c9fe77..31e232bcb5e80c0fa6487e48722152834371cce6 100644 (file)
@@ -127,10 +127,12 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
 }
 
 static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
-                                           unsigned int new_clock)
+                                           unsigned int wanted_clock)
 {
        struct renesas_sdhi *priv = host_to_priv(host);
+       struct clk *ref_clk = priv->clk;
        unsigned int freq, diff, best_freq = 0, diff_min = ~0;
+       unsigned int new_clock, clkh_shift = 0;
        int i;
 
        /*
@@ -141,6 +143,16 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
        if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2) || mmc_doing_tune(host->mmc))
                return clk_get_rate(priv->clk);
 
+       if (priv->clkh) {
+               bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
+               bool need_slow_clkh = (host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) ||
+                                     (host->mmc->ios.timing == MMC_TIMING_MMC_HS400);
+               clkh_shift = use_4tap && need_slow_clkh ? 1 : 2;
+               ref_clk = priv->clkh;
+       }
+
+       new_clock = wanted_clock << clkh_shift;
+
        /*
         * We want the bus clock to be as close as possible to, but no
         * greater than, new_clock.  As we can divide by 1 << i for
@@ -148,11 +160,10 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
         * possible, but no greater than, new_clock << i.
         */
        for (i = min(9, ilog2(UINT_MAX / new_clock)); i >= 0; i--) {
-               freq = clk_round_rate(priv->clk, new_clock << i);
+               freq = clk_round_rate(ref_clk, new_clock << i);
                if (freq > (new_clock << i)) {
                        /* Too fast; look for a slightly slower option */
-                       freq = clk_round_rate(priv->clk,
-                                             (new_clock << i) / 4 * 3);
+                       freq = clk_round_rate(ref_clk, (new_clock << i) / 4 * 3);
                        if (freq > (new_clock << i))
                                continue;
                }
@@ -164,7 +175,10 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
                }
        }
 
-       clk_set_rate(priv->clk, best_freq);
+       clk_set_rate(ref_clk, best_freq);
+
+       if (priv->clkh)
+               clk_set_rate(priv->clk, best_freq >> clkh_shift);
 
        return clk_get_rate(priv->clk);
 }
@@ -947,6 +961,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
                mmc_data->max_segs = of_data->max_segs;
                dma_priv->dma_buswidth = of_data->dma_buswidth;
                host->bus_shift = of_data->bus_shift;
+               /* Fallback for old DTs */
+               if (of_data->sdhi_flags & SDHI_FLAG_NEED_CLKH_FALLBACK)
+                       priv->clkh = clk_get_parent(clk_get_parent(priv->clk));
+
        }
 
        host->write16_hook      = renesas_sdhi_write16_hook;