From: Dmitry Osipenko <digetx@gmail.com>
To: Stephen Boyd <sboyd@kernel.org>,
Jonathan Hunter <jonathanh@nvidia.com>,
Joseph Lo <josephl@nvidia.com>,
Michael Turquette <mturquette@baylibre.com>,
Peter De Schrijver <pdeschrijver@nvidia.com>,
Prashant Gaikwad <pgaikwad@nvidia.com>,
Rob Herring <robh+dt@kernel.org>,
Thierry Reding <thierry.reding@gmail.com>
Cc: devicetree@vger.kernel.org, linux-clk@vger.kernel.org,
linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4 01/10] clk: tegra20/30: Add custom EMC clock implementation
Date: Wed, 19 Jun 2019 18:37:46 +0300 [thread overview]
Message-ID: <1b3a95c7-37e5-535a-ff8c-2d52596cef1b@gmail.com> (raw)
In-Reply-To: <20190619011401.9DE5C2085A@mail.kernel.org>
19.06.2019 4:14, Stephen Boyd пишет:
> Quoting Dmitry Osipenko (2019-06-16 16:35:42)
>> A proper External Memory Controller clock rounding and parent selection
>> functionality is required by the EMC drivers. It is not available using
>> the generic clock implementation, hence add a custom one.
>
> Why isn't it available? Please add this information to the commit text.
Ok! It's not available because only the EMC driver has information about available memory
timings and thus about the available rates (and parents consequently).
>> The clock rate
>> rounding shall be done by the EMC drivers because they have information
>> about available memory timings, so the drivers will have to register a
>> callback that will round the requested rate. EMC clock users won't be able
>> to request EMC clock by getting -EPROBE_DEFER until EMC driver is probed
>> and the callback is set up. The functionality is somewhat similar to the
>> clk-emc.c which serves Tegra124+ SoC's, the later HW generations support
>> more parent clock sources and the HW configuration and integration with
>> the EMC drivers differs a tad from the older gens, hence it's not really
>> worth to try to squash everything into a single source file.
>>
>> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> [...]
>> diff --git a/drivers/clk/tegra/clk-tegra20-emc.c b/drivers/clk/tegra/clk-tegra20-emc.c
>> new file mode 100644
>> index 000000000000..b7f64ad5c04c
>> --- /dev/null
>> +++ b/drivers/clk/tegra/clk-tegra20-emc.c
>> @@ -0,0 +1,305 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +#include <linux/bits.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/clk/tegra.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/slab.h>
>> +
>> +#include "clk.h"
>> +
>> +#define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK GENMASK(7, 0)
>> +#define CLK_SOURCE_EMC_2X_CLK_SRC_MASK GENMASK(31, 30)
>> +#define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT 30
>> +
>> +#define MC_EMC_SAME_FREQ BIT(16)
>> +#define USE_PLLM_UD BIT(29)
>> +
>> +#define EMC_SRC_PLL_M 0
>> +#define EMC_SRC_PLL_C 1
>> +#define EMC_SRC_PLL_P 2
>> +#define EMC_SRC_CLK_M 3
>> +
> [...]
>> +void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
>> + void *cb_arg)
>> +{
>> + struct clk *clk = __clk_lookup("emc");
>> + struct tegra_clk_emc *emc;
>> + struct clk_hw *hw;
>> +
>> + if (clk) {
>> + hw = __clk_get_hw(clk);
>> + emc = to_tegra_clk_emc(hw);
>> +
>> + emc->round_cb = round_cb;
>> + emc->cb_arg = cb_arg;
>> + }
>> +}
>> +
>> +bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
>> +{
>> + return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
>> +}
>> +
>> +struct clk *tegra20_clk_register_emc(void __iomem *ioaddr)
>
> Is this used outside this file?
Yes, it is. It is getting used even in this patch, you just snipped it off in the reply.
>> +{
>> + struct tegra_clk_emc *emc;
>> + struct clk_init_data init;
>> + struct clk *clk;
>> +
>> + emc = kzalloc(sizeof(*emc), GFP_KERNEL);
>> + if (!emc)
>> + return NULL;
>> +
>> + init.name = "emc";
>> + init.ops = &tegra_clk_emc_ops;
>> + init.flags = CLK_IS_CRITICAL;
>
> Can you please add a comment in the code why this clk is critical?
Okay!
>> + init.parent_names = emc_parent_clk_names;
>> + init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
>> +
>> + emc->reg = ioaddr;
>> + emc->hw.init = &init;
>> +
>> + clk = clk_register(NULL, &emc->hw);
>> + if (IS_ERR(clk)) {
>> + kfree(emc);
>> + return NULL;
>> + }
>> +
>> + return clk;
>> +}
>> +
>> +void tegra30_clk_set_emc_round_callback(tegra30_clk_emc_round_cb *round_cb,
>> + void *cb_arg)
>> +{
>> + tegra20_clk_set_emc_round_callback(round_cb, cb_arg);
>> +}
>> +
>> +bool tegra30_clk_emc_driver_available(struct clk_hw *emc_hw)
>> +{
>> + return tegra20_clk_emc_driver_available(emc_hw);
>> +}
>> +
>> +struct clk *tegra30_clk_register_emc(void __iomem *ioaddr)
>> +{
>> + struct tegra_clk_emc *emc;
>> + struct clk_hw *hw;
>> + struct clk *clk;
>> +
>> + clk = tegra20_clk_register_emc(ioaddr);
>> + if (!clk)
>> + return NULL;
>> +
>> + hw = __clk_get_hw(clk);
>
> It would be nicer to not use __clk_get_hw() and have the above function
> return the clk_hw pointer instead. Then some driver can return the clk
> pointer from there, if it's even needed for anything?
This is solely for internal use by the tegra-clk driver itself, this function isn't publicly
exposed. Again, you snipped off a lot in the reply, please take a look at the original patch.
Technically I could return clk_hw from here, but it doesn't make much sense in the context
of the tegra-clk driver. Please see how these functions are getting used in the original patch.
In short, tegra-clk driver operates with clk struct and not clk_hw. You already was asking
the same question in v3 and I replied that converting the whole driver for clk_hw will take
a lot of effort. I suppose it will be somewhat similar to what was done for the IMX driver
recently [1].
[1] https://lkml.org/lkml/2019/5/2/170
>> + emc = to_tegra_clk_emc(hw);
>> + emc->want_low_jitter = true;
>> +
>> + return clk;
>> +}
>> +
>> +int tegra30_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
>> +{
>> + struct tegra_clk_emc *emc;
>> + struct clk_hw *hw;
>> +
>> + if (emc_clk) {
>> + hw = __clk_get_hw(emc_clk);
>> + emc = to_tegra_clk_emc(hw);
>> + emc->mc_same_freq = same;
>> +
>> + return 0;
>> + }
>> +
>> + return -EINVAL;
>> +}
>> diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
>> index bcd871134f45..f937a0f35afb 100644
>> --- a/drivers/clk/tegra/clk-tegra20.c
>> +++ b/drivers/clk/tegra/clk-tegra20.c
>> @@ -1115,6 +1083,8 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
>> if (IS_ERR(clk))
>> return clk;
>>
>> + hw = __clk_get_hw(clk);
>> +
>> /*
>> * Tegra20 CDEV1 and CDEV2 clocks are a bit special case, their parent
>> * clock is created by the pinctrl driver. It is possible for clk user
>> @@ -1124,13 +1094,16 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
>> */
>> if (clkspec->args[0] == TEGRA20_CLK_CDEV1 ||
>> clkspec->args[0] == TEGRA20_CLK_CDEV2) {
>> - hw = __clk_get_hw(clk);
>> -
>> parent_hw = clk_hw_get_parent(hw);
>> if (!parent_hw)
>> return ERR_PTR(-EPROBE_DEFER);
>> }
>>
>> + if (clkspec->args[0] == TEGRA20_CLK_EMC) {
>> + if (!tegra20_clk_emc_driver_available(hw))
>> + return ERR_PTR(-EPROBE_DEFER);
>> + }
>> +
>> return clk;
>> }
>>
>> diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
>> index 7b4c6a488527..fab075808c20 100644
>> --- a/drivers/clk/tegra/clk-tegra30.c
>> +++ b/drivers/clk/tegra/clk-tegra30.c
>> @@ -1302,6 +1298,26 @@ static struct tegra_audio_clk_info tegra30_audio_plls[] = {
>> { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
>> };
>>
>> +static struct clk *tegra30_clk_src_onecell_get(struct of_phandle_args *clkspec,
>> + void *data)
>> +{
>> + struct clk_hw *hw;
>> + struct clk *clk;
>> +
>> + clk = of_clk_src_onecell_get(clkspec, data);
>> + if (IS_ERR(clk))
>> + return clk;
>> +
>> + hw = __clk_get_hw(clk);
>> +
>> + if (clkspec->args[0] == TEGRA30_CLK_EMC) {
>> + if (!tegra30_clk_emc_driver_available(hw))
>> + return ERR_PTR(-EPROBE_DEFER);
>> + }
>> +
>> + return clk;
>> +}
>
> This above function makes me uneasy because it looks like a clk_get() on
> top of a clk_get()?
Yes, this way we're intercepting clk_get() within the driver, which is a very nice feature.
We're already doing that in the clk-tegra20.c, can't see any problems with that.
next prev parent reply other threads:[~2019-06-19 15:37 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-06-16 23:35 [PATCH v4 00/10] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 01/10] clk: tegra20/30: Add custom EMC clock implementation Dmitry Osipenko
2019-06-17 9:35 ` Thierry Reding
2019-06-17 15:00 ` Dmitry Osipenko
2019-06-18 12:21 ` Thierry Reding
2019-06-19 1:14 ` Stephen Boyd
2019-06-19 1:14 ` Stephen Boyd
2019-06-19 15:37 ` Dmitry Osipenko [this message]
2019-06-16 23:35 ` [PATCH v4 02/10] memory: tegra20-emc: Drop setting EMC rate to max on probe Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 03/10] memory: tegra20-emc: Adapt for clock driver changes Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 04/10] memory: tegra20-emc: Include io.h instead of iopoll.h Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 05/10] memory: tegra20-emc: Replace clk_get_sys with devm_clk_get Dmitry Osipenko
2019-06-17 9:46 ` Thierry Reding
2019-06-17 15:01 ` Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 06/10] dt-bindings: memory: Add binding for NVIDIA Tegra30 External Memory Controller Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 07/10] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
2019-06-17 9:50 ` Thierry Reding
2019-06-17 15:03 ` Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 08/10] memory: tegra: Ensure timing control debug features are disabled Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 09/10] memory: tegra: Consolidate registers definition into one place Dmitry Osipenko
2019-06-16 23:35 ` [PATCH v4 10/10] ARM: dts: tegra30: Add External Memory Controller node Dmitry Osipenko
2019-06-17 8:21 ` [PATCH v4 00/10] memory: tegra: Introduce Tegra30 EMC driver Peter De Schrijver
2019-06-17 15:08 ` Dmitry Osipenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1b3a95c7-37e5-535a-ff8c-2d52596cef1b@gmail.com \
--to=digetx@gmail.com \
--cc=devicetree@vger.kernel.org \
--cc=jonathanh@nvidia.com \
--cc=josephl@nvidia.com \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=mturquette@baylibre.com \
--cc=pdeschrijver@nvidia.com \
--cc=pgaikwad@nvidia.com \
--cc=robh+dt@kernel.org \
--cc=sboyd@kernel.org \
--cc=thierry.reding@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).