Re: [PATCH v3 11/13] clk: tegra: Add EMC clock driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Nov 6, 2014 at 8:41 PM, Mikko Perttunen
<mikko.perttunen@xxxxxxxx> wrote:
> On 11/06/2014 10:04 AM, Alexandre Courbot wrote:
>>
>> On 10/30/2014 01:22 AM, Tomeu Vizoso wrote:
>>>
>>> From: Mikko Perttunen <mperttunen@xxxxxxxxxx>
>>>
>>> The driver is currently only tested on Tegra124 Jetson TK1, but should
>>> work with other Tegra124 boards, provided that correct EMC tables are
>>> provided through the device tree. Older chip models have differing
>>> timing change sequences, so they are not currently supported.
>>>
>>> Signed-off-by: Mikko Perttunen <mperttunen@xxxxxxxxxx>
>>> Signed-off-by: Tomeu Vizoso <tomeu.vizoso@xxxxxxxxxxxxx>
>>>
>>> ---
>>>
>>> v3:    * Add some locking to protect the registers that are shared
>>> with the MC
>>>       clock
>>>
>>> v2:    * Make sure that the clock is properly registered
>>>     * Bail out early from attempts to set the same rate
>>> ---
>>>   drivers/clk/tegra/Makefile       |   2 +-
>>>   drivers/clk/tegra/clk-emc.c      | 470
>>> +++++++++++++++++++++++++++++++++++++++
>>>   drivers/clk/tegra/clk-tegra124.c |   4 +-
>>>   drivers/clk/tegra/clk.h          |   2 +
>>>   4 files changed, 476 insertions(+), 2 deletions(-)
>>>   create mode 100644 drivers/clk/tegra/clk-emc.c
>>>
>>> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
>>> index f7dfb72..240e5b4 100644
>>> --- a/drivers/clk/tegra/Makefile
>>> +++ b/drivers/clk/tegra/Makefile
>>> @@ -14,4 +14,4 @@ obj-y                    += clk-tegra-super-gen4.o
>>>   obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
>>>   obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
>>>   obj-$(CONFIG_ARCH_TEGRA_114_SOC)    += clk-tegra114.o
>>> -obj-$(CONFIG_ARCH_TEGRA_124_SOC)    += clk-tegra124.o
>>> +obj-$(CONFIG_ARCH_TEGRA_124_SOC)    += clk-tegra124.o clk-emc.o
>>> diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c
>>> new file mode 100644
>>> index 0000000..182a059
>>> --- /dev/null
>>> +++ b/drivers/clk/tegra/clk-emc.c
>>> @@ -0,0 +1,470 @@
>>> +/*
>>> + * drivers/clk/tegra/clk-emc.c
>>> + *
>>> + * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
>>> + *
>>> + * Author:
>>> + *    Mikko Perttunen <mperttunen@xxxxxxxxxx>
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + */
>>> +
>>> +#include <linux/clk-provider.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/clkdev.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/sort.h>
>>> +#include <linux/string.h>
>>> +
>>> +#include <soc/tegra/fuse.h>
>>> +#include <soc/tegra/memory.h>
>>> +
>>> +#define CLK_SOURCE_EMC 0x19c
>>> +
>>> +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT 0
>>> +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK 0xff
>>> +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(x) (((x) &
>>> CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK) << \
>>> +                          CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT)
>>> +
>>> +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT 29
>>> +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK 0x7
>>> +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC(x) (((x) &
>>> CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK) << \
>>> +                      CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT)
>>> +
>>> +const char *emc_parent_clk_names[] = {
>>> +    "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud",
>>> +    "pll_c2", "pll_c3", "pll_c_ud"
>>> +};
>>> +
>>> +/* List of clock sources for various parents the EMC clock can have.
>>> + * When we change the timing to a timing with a parent that has the same
>>> + * clock source as the current parent, we must first change to a backup
>>> + * timing that has a different clock source.
>>> + */
>>
>>
>> Nit: comment style throughout this file.
>>
>>> +
>>> +#define EMC_SRC_PLL_M 0
>>> +#define EMC_SRC_PLL_C 1
>>> +#define EMC_SRC_PLL_P 2
>>> +#define EMC_SRC_CLK_M 3
>>> +#define EMC_SRC_PLL_C2 4
>>> +#define EMC_SRC_PLL_C3 5
>>> +const char emc_parent_clk_sources[] = {
>>> +    EMC_SRC_PLL_M, EMC_SRC_PLL_C, EMC_SRC_PLL_P, EMC_SRC_CLK_M,
>>> +    EMC_SRC_PLL_M, EMC_SRC_PLL_C2, EMC_SRC_PLL_C3, EMC_SRC_PLL_C
>>> +};
>>> +
>>> +struct emc_timing {
>>> +    unsigned long rate, parent_rate;
>>> +    u8 parent_index;
>>> +    struct clk *parent;
>>> +    u32 ram_code;
>>> +};
>>> +
>>> +struct tegra_emc {
>>> +    struct clk_hw hw;
>>> +    void __iomem *clk_regs;
>>> +    struct clk *prev_parent;
>>> +    bool changing_timing;
>>> +
>>> +    int num_timings;
>>> +    struct emc_timing *timings;
>>> +    spinlock_t *lock;
>>> +};
>>> +
>>> +/* * * * * * * * * * * * * * * * * * * * * * * * * *
>>> + * Common clock framework callback implementations *
>>> + * * * * * * * * * * * * * * * * * * * * * * * * * */
>>> +
>>> +unsigned long emc_recalc_rate(struct clk_hw *hw, unsigned long
>>> parent_rate)
>>
>>
>> Shouldn't this function be static? Also applies to other functions in
>> this file.
>>
>>> +{
>>> +    struct tegra_emc *tegra = container_of(hw, struct tegra_emc, hw);
>>> +    u32 val, div;
>>> +
>>> +    /* CCF wrongly assumes that the parent won't change during set_rate,
>>> +     * so get the parent rate explicitly.
>>> +     */
>>> +    parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
>>> +
>>> +    val = readl(tegra->clk_regs + CLK_SOURCE_EMC);
>>> +    div = val & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK;
>>> +
>>> +    return parent_rate / (div + 2) * 2;
>>> +}
>>> +
>>> +/* Rounds up unless no higher rate exists, in which case down. This
>>> way is
>>> + * safer since things have EMC rate floors. Also don't touch parent_rate
>>> + * since we don't want the CCF to play with our parent clocks.
>>> + */
>>> +long emc_round_rate(struct clk_hw *hw, unsigned long rate,
>>> +            unsigned long *parent_rate)
>>> +{
>>> +    struct tegra_emc *tegra = container_of(hw, struct tegra_emc, hw);
>>> +    u8 ram_code = tegra_read_ram_code();
>>> +    struct emc_timing *timing;
>>> +    int i;
>>> +
>>> +    /* Returning the original rate will lead to a more sensible error
>>> +     * message when emc_set_rate fails.
>>> +     */
>>> +    if (tegra->num_timings == 0)
>>> +        return rate;
>>> +
>>> +    for (i = 0; i < tegra->num_timings; ++i) {
>>> +        timing = tegra->timings + i;
>>> +        if (timing->ram_code != ram_code)
>>> +            continue;
>>> +
>>> +        if (timing->rate >= rate)
>>> +            return timing->rate;
>>> +    }
>>> +
>>> +    return tegra->timings[tegra->num_timings - 1].rate;
>>> +}
>>> +
>>> +u8 emc_get_parent(struct clk_hw *hw)
>>> +{
>>> +    struct tegra_emc *tegra = container_of(hw, struct tegra_emc, hw);
>>> +    u32 val;
>>> +
>>> +    val = readl(tegra->clk_regs + CLK_SOURCE_EMC);
>>> +
>>> +    return (val >> CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT)
>>> +        & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK;
>>> +}
>>> +
>>> +static int emc_set_timing(struct tegra_emc *tegra, struct emc_timing
>>> *timing)
>>> +{
>>> +    int err;
>>> +    u8 div;
>>> +    u32 car_value;
>>> +    unsigned long flags = 0;
>>> +
>>> +    pr_debug("going to rate %ld prate %ld p %s\n",
>>> +        timing->rate, timing->parent_rate,
>>> +        __clk_get_name(timing->parent));
>>> +
>>> +    if (emc_get_parent(&tegra->hw) == timing->parent_index &&
>>> +        clk_get_rate(timing->parent) != timing->parent_rate) {
>>> +        BUG();
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    tegra->changing_timing = true;
>>> +
>>> +    err = clk_set_rate(timing->parent, timing->parent_rate);
>>> +    if (err) {
>>> +        pr_err("cannot change parent %s rate to %ld: %d\n",
>>> +               __clk_get_name(timing->parent),
>>> +               timing->parent_rate, err);
>>> +
>>> +        return err;
>>> +    }
>>> +
>>> +    err = clk_prepare_enable(timing->parent);
>>> +    if (err) {
>>> +        pr_err("cannot enable parent clock: %d\n", err);
>>> +        return err;
>>> +    }
>>> +
>>> +    div = timing->parent_rate / (timing->rate / 2) - 2;
>>> +
>>> +    tegra_emc_prepare_timing_change(timing->rate);
>>
>>
>> Since there is no locking whatsoever in the EMC driver, I wonder if this
>> function should be called after the spinlock is acquired to ensure it
>> cannot be called twice?
>
>
> This is the only place tegra_emc_{prepare,complete}_timing_change are
> called, and it is only called from emc_set_rate, so I would think CCF's
> locking should take care of that.

Ok, in that case I'm happy. Thanks for confirming.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux