diff mbox

[1/4] clk: samsung: out: Add infrastructure to register CLKOUT

Message ID 1399640410-30957-2-git-send-email-tushar.behera@linaro.org
State New
Headers show

Commit Message

Tushar Behera May 9, 2014, 1 p.m. UTC
All SoC in Exynos-series have a clock with name XCLKOUT to provide
debug information about various clocks available in the SoC. The register
controlling the MUX and GATE of this clock is provided within PMU domain.
Since PMU domain can't be dedicatedly mapped by every driver, the register
needs to be handled through a regmap handle provided by PMU syscon
controller. Right now, CCF doesn't allow regmap based MUX and GATE clocks,
hence a dedicated clock provider for XCLKOUT is added here.

Signed-off-by: Tushar Behera <tushar.behera@linaro.org>
CC: Tomasz Figa <t.figa@samsung.com>
---
 drivers/clk/samsung/Makefile  |    2 +-
 drivers/clk/samsung/clk-out.c |  181 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/samsung/clk.h     |   33 ++++++++
 3 files changed, 215 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/samsung/clk-out.c

Comments

Pankaj Dubey May 10, 2014, 3:51 a.m. UTC | #1
On 05/09/2014 10:00 PM, Tushar Behera wrote:
> All SoC in Exynos-series have a clock with name XCLKOUT to provide
> debug information about various clocks available in the SoC. The register
> controlling the MUX and GATE of this clock is provided within PMU domain.
> Since PMU domain can't be dedicatedly mapped by every driver, the register
> needs to be handled through a regmap handle provided by PMU syscon
> controller. Right now, CCF doesn't allow regmap based MUX and GATE clocks,
> hence a dedicated clock provider for XCLKOUT is added here.
>
> Signed-off-by: Tushar Behera <tushar.behera@linaro.org>
> CC: Tomasz Figa <t.figa@samsung.com>
> ---
>   drivers/clk/samsung/Makefile  |    2 +-
>   drivers/clk/samsung/clk-out.c |  181 +++++++++++++++++++++++++++++++++++++++++
>   drivers/clk/samsung/clk.h     |   33 ++++++++
>   3 files changed, 215 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/clk/samsung/clk-out.c
>
> diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
> index 8eb4799..d23ad4f 100644
> --- a/drivers/clk/samsung/Makefile
> +++ b/drivers/clk/samsung/Makefile
> @@ -2,7 +2,7 @@
>   # Samsung Clock specific Makefile
>   #
>   
> -obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-pll.o
> +obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-pll.o clk-out.o
>   obj-$(CONFIG_ARCH_EXYNOS4)	+= clk-exynos4.o
>   obj-$(CONFIG_SOC_EXYNOS5250)	+= clk-exynos5250.o
>   obj-$(CONFIG_SOC_EXYNOS5420)	+= clk-exynos5420.o
> diff --git a/drivers/clk/samsung/clk-out.c b/drivers/clk/samsung/clk-out.c
> new file mode 100644
> index 0000000..76489b6
> --- /dev/null
> +++ b/drivers/clk/samsung/clk-out.c
> @@ -0,0 +1,181 @@
> +/*
> + * Copyright (c) 2014 Samsung Electronics Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This file contains the utility functions to register the clkout clocks.
> +*/
> +
> +/**
> + * All SoC in Exynos-series have a clock with name XCLKOUT to provide
> + * debug information about various clocks available in the SoC. The register
> + * controlling the MUX and GATE of this clock is provided within PMU domain.
> + * Since PMU domain can't be dedicatedly mapped every driver, the register
> + * needs to be handled through a regmap handle provided by PMU syscon
> + * controller. Right now, CCF doesn't allow regmap based MUX and GATE clocks,
> + * hence a dedicated clock provider for XCLKOUT is added here.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "clk.h"
> +
> +/**
> + * struct samsung_clkout_soc_data: SoC specific register details
> + * @reg: Offset of CLKOUT register from PMU base

how about naming this variable as "offset" instead of "reg".

> + * @mux_shift: Start-bit of MUX bit-field
> + * @mux_width: Width of MUX bit-field
> + * @enable_bit: The bit corresponding to gating of this clock
> + */
> +struct samsung_clkout_soc_data {
> +	unsigned int reg;
> +	u8 mux_shift;
> +	u8 mux_width;
> +	u8 enable_bit;
> +};
> +
> +/**
> + * struct samsung_clkout: Structure to store driver specific clock context
> + * @hw: Handle to CCF clock
> + * @soc_data: SoC specific register details
> + * @regmap: Regmap handle of the PMU
> + */
> +struct samsung_clkout {
> +	struct clk_hw hw;
> +	const struct samsung_clkout_soc_data *soc_data;
> +	struct regmap *regmap;
> +};
> +
> +#define to_clk_out(_hw) container_of(_hw, struct samsung_clkout, hw)
> +
> +int samsung_clkout_enable(struct clk_hw *hw)
> +{
> +	struct samsung_clkout *clkout = to_clk_out(hw);
> +	const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
> +	unsigned int enable_mask = BIT(soc_data->enable_bit);
> +
> +	/* clkout is enabled if enable bit is low */
> +	regmap_update_bits(clkout->regmap, soc_data->reg, enable_mask, 0);
> +
> +	return 0;
> +}
> +
> +void samsung_clkout_disable(struct clk_hw *hw)
> +{
> +	struct samsung_clkout *clkout = to_clk_out(hw);
> +	const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
> +	unsigned int enable_mask = BIT(soc_data->enable_bit);
> +
> +	/* clkout is gated if enable bit is high */
> +	regmap_update_bits(clkout->regmap, soc_data->reg,
> +			enable_mask, enable_mask);
> +
> +	return;
> +}
> +
> +int samsung_clkout_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct samsung_clkout *clkout = to_clk_out(hw);
> +	const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
> +	unsigned int parent_mask = BIT(soc_data->mux_width) - 1;
> +
> +	regmap_update_bits(clkout->regmap, soc_data->reg,
> +			parent_mask << soc_data->mux_shift,
> +			index << soc_data->mux_shift);
> +
> +	return 0;
> +}
> +
> +u8 samsung_clkout_get_parent(struct clk_hw *hw)
> +{
> +	struct samsung_clkout *clkout = to_clk_out(hw);
> +	const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
> +	unsigned int parent_mask = BIT(soc_data->mux_width) - 1;
> +	unsigned int val;
> +	int ret;
> +
> +	ret = regmap_read(clkout->regmap, soc_data->reg, &val);

Do we really need to keep return value in "ret" as I can't see you are 
using it anywhere?

> +
> +	return (val >> soc_data->mux_shift) & parent_mask;
> +}
> +
> +static const struct clk_ops samsung_clkout_clk_ops = {
> +	.enable = samsung_clkout_enable,
> +	.disable = samsung_clkout_disable,
> +	.set_parent = samsung_clkout_set_parent,
> +	.get_parent = samsung_clkout_get_parent,
> +};
> +
> +static void __init _samsung_clk_register_clkout(
> +		struct samsung_out_clock *out,
> +		const struct samsung_clkout_soc_data *soc_data,
> +		struct regmap *regmap)
> +{
> +	struct samsung_clkout *clkout;
> +	struct clk *clk;
> +	struct clk_init_data init;
> +	int ret;
> +
> +	clkout = kzalloc(sizeof(*clkout), GFP_KERNEL);
> +	if (!clkout) {
> +		pr_err("%s: could not allocate out clk %s\n",
> +			__func__, out->name);
> +		return;
> +	}
> +
> +	init.name = out->name;
> +	init.parent_names = out->parent_names;
> +	init.num_parents = out->num_parents;
> +	init.ops = &samsung_clkout_clk_ops;
> +
> +	clkout->hw.init = &init;
> +	clkout->regmap = regmap;
> +	clkout->soc_data = soc_data;
> +
> +	clk = clk_register(NULL, &clkout->hw);
> +	if (IS_ERR(clk)) {
> +		pr_err("%s: failed to register out clock %s : %ld\n",
> +			__func__, out->name, PTR_ERR(clk));
> +		kfree(clkout);
> +		return;
> +	}
> +
> +	samsung_clk_add_lookup(clk, out->id);
> +
> +	if (!out->alias)
> +		return;
> +
> +	ret = clk_register_clkdev(clk, out->alias, out->dev_name);
> +	if (ret)
> +		pr_err("%s: failed to register lookup for %s : %d",
> +			__func__, out->name, ret);
> +}
> +
> +/* All existing Exynos serial of SoCs have common values for this offsets. */
typo: serial/series/
> +static const struct samsung_clkout_soc_data exynos_clkout_soc_data = {
> +	.reg = 0xa00,
> +	.mux_shift = 8,
> +	.mux_width = 5,
> +	.enable_bit = 0,
> +};
> +
> +void __init samsung_clk_register_clkout(struct device_node *np,
> +		struct samsung_out_clock *out_clk_list, unsigned int nr_out_clk)
> +{
> +	int cnt;
> +	struct regmap *reg;
> +	const struct samsung_clkout_soc_data *priv = &exynos_clkout_soc_data;
> +
> +	reg = syscon_early_regmap_lookup_by_phandle(np, "samsung,pmu-syscon");
> +	if (IS_ERR(reg)) {
> +		pr_err("Failed to get pmu-syscon handle for clkout\n");
> +		return;
> +	}
> +
> +	for (cnt = 0; cnt < nr_out_clk; cnt++)
> +		_samsung_clk_register_clkout(&out_clk_list[cnt], priv, reg);
> +}
> diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
> index c7141ba..b4b2122 100644
> --- a/drivers/clk/samsung/clk.h
> +++ b/drivers/clk/samsung/clk.h
> @@ -312,6 +312,37 @@ struct samsung_pll_clock {
>   	__PLL(_typ, _id, NULL, _name, _pname, CLK_GET_RATE_NOCACHE,	\
>   		_lock, _con, _rtable, _alias)
>   
> +/**
> + * struct samsung_out_clock: information about CLKOUT clock
> + * @id: platform specific id of the clock.
> + * @dev_name: name of the device to which this clock belongs.
> + * @name: name of this mux clock.
> + * @parent_names: array of pointer to parent clock names.
> + * @num_parents: number of parents listed in @parent_names.
> + * @alias: optional clock alias name to be assigned to this clock.
> + */
> +struct samsung_out_clock {
> +	unsigned int		id;
> +	const char		*dev_name;
> +	const char		*name;
> +	const char		**parent_names;
> +	unsigned int		num_parents;
> +	const char		*alias;
> +};
> +
> +#define __CLKOUT(_id, dname, cname, pnames, a)		\
> +	{							\
> +		.id		= _id,				\
> +		.dev_name	= dname,			\
> +		.name		= cname,			\
> +		.parent_names	= pnames,			\
> +		.num_parents	= ARRAY_SIZE(pnames),		\
> +		.alias		= a,				\
> +	}
> +
> +#define CLKOUT(_id, cname, pnames) \
> +	__CLKOUT(_id, NULL, cname, pnames, NULL)
> +
>   extern void __init samsung_clk_init(struct device_node *np, void __iomem *base,
>   				    unsigned long nr_clks);
>   extern void __init samsung_clk_of_register_fixed_ext(
> @@ -335,6 +366,8 @@ extern void __init samsung_clk_register_gate(
>   		struct samsung_gate_clock *clk_list, unsigned int nr_clk);
>   extern void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
>   		unsigned int nr_clk, void __iomem *base);
> +extern void __init samsung_clk_register_clkout(struct device_node *np,
> +	struct samsung_out_clock *out_clk_list, unsigned int nr_out_clk);
>   
>   extern unsigned long _get_rate(const char *clk_name);
>
Tushar Behera May 12, 2014, 4:46 a.m. UTC | #2
On 05/10/2014 09:21 AM, Pankaj Dubey wrote:
> On 05/09/2014 10:00 PM, Tushar Behera wrote:
>> All SoC in Exynos-series have a clock with name XCLKOUT to provide
>> debug information about various clocks available in the SoC. The register
>> controlling the MUX and GATE of this clock is provided within PMU domain.
>> Since PMU domain can't be dedicatedly mapped by every driver, the
>> register
>> needs to be handled through a regmap handle provided by PMU syscon
>> controller. Right now, CCF doesn't allow regmap based MUX and GATE
>> clocks,
>> hence a dedicated clock provider for XCLKOUT is added here.
>>
>> Signed-off-by: Tushar Behera <tushar.behera@linaro.org>
>> CC: Tomasz Figa <t.figa@samsung.com>
>> ---
>>   drivers/clk/samsung/Makefile  |    2 +-
>>   drivers/clk/samsung/clk-out.c |  181
>> +++++++++++++++++++++++++++++++++++++++++
>>   drivers/clk/samsung/clk.h     |   33 ++++++++
>>   3 files changed, 215 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/clk/samsung/clk-out.c
>>

[ ... ]

>> +/**
>> + * struct samsung_clkout_soc_data: SoC specific register details
>> + * @reg: Offset of CLKOUT register from PMU base
> 
> how about naming this variable as "offset" instead of "reg".
> 

Okay, I will change that.

[ ... ]

>> +u8 samsung_clkout_get_parent(struct clk_hw *hw)
>> +{
>> +    struct samsung_clkout *clkout = to_clk_out(hw);
>> +    const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
>> +    unsigned int parent_mask = BIT(soc_data->mux_width) - 1;
>> +    unsigned int val;
>> +    int ret;
>> +
>> +    ret = regmap_read(clkout->regmap, soc_data->reg, &val);
> 
> Do we really need to keep return value in "ret" as I can't see you are
> using it anywhere?
> 

Right, we are not using that and can be removed.

>> +
>> +    return (val >> soc_data->mux_shift) & parent_mask;
>> +}
>> +

[ ... ]

>> +/* All existing Exynos serial of SoCs have common values for this
>> offsets. */
> typo: serial/series/

Sure. Thanks for your review.
Rahul Sharma May 15, 2014, 1:44 p.m. UTC | #3
Hi Tushar,

Basically you are adding a new clock-type for Clkout. IMO clkout
is not a special hardware. Existing clock types can be reused to
support clkout. I see 3 major problem here:

1) Clkout -> (Mux + Gate). You clubbed mux and gate together, and
exposing as a single clock which is something like a composite clock.
IMO this is not a recommended way in CCF.

2) New Clock Type: Since clkout is just a combination of a simple
mux and gate which are already supported, it is a unnecessary
duplication.

3) Clkout registered along with CMU: which is not correct. Clkout is in PMU
(Separate physical IP) and should be registered as a independent Clock
provider which provides 1 mux and 1 gate clock (As if now). It should also be
well connected with main CMU.

I understand the challenge in using regmap interface for a clock provider. But
we need to identify a clean solution. IMHO a independent clock provider with
iomap, is relatively cleaner approach till CCF is not ready with regmap based
reg access for clock registers.

Experts!! please comment.

Regards,
Rahul Sharma.

On 12 May 2014 10:16, Tushar Behera <tushar.behera@linaro.org> wrote:
> On 05/10/2014 09:21 AM, Pankaj Dubey wrote:
>> On 05/09/2014 10:00 PM, Tushar Behera wrote:
>>> All SoC in Exynos-series have a clock with name XCLKOUT to provide
>>> debug information about various clocks available in the SoC. The register
>>> controlling the MUX and GATE of this clock is provided within PMU domain.
>>> Since PMU domain can't be dedicatedly mapped by every driver, the
>>> register
>>> needs to be handled through a regmap handle provided by PMU syscon
>>> controller. Right now, CCF doesn't allow regmap based MUX and GATE
>>> clocks,
>>> hence a dedicated clock provider for XCLKOUT is added here.
>>>
>>> Signed-off-by: Tushar Behera <tushar.behera@linaro.org>
>>> CC: Tomasz Figa <t.figa@samsung.com>
>>> ---
>>>   drivers/clk/samsung/Makefile  |    2 +-
>>>   drivers/clk/samsung/clk-out.c |  181
>>> +++++++++++++++++++++++++++++++++++++++++
>>>   drivers/clk/samsung/clk.h     |   33 ++++++++
>>>   3 files changed, 215 insertions(+), 1 deletion(-)
>>>   create mode 100644 drivers/clk/samsung/clk-out.c
>>>
>
> [ ... ]
>
>>> +/**
>>> + * struct samsung_clkout_soc_data: SoC specific register details
>>> + * @reg: Offset of CLKOUT register from PMU base
>>
>> how about naming this variable as "offset" instead of "reg".
>>
>
> Okay, I will change that.
>
> [ ... ]
>
>>> +u8 samsung_clkout_get_parent(struct clk_hw *hw)
>>> +{
>>> +    struct samsung_clkout *clkout = to_clk_out(hw);
>>> +    const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
>>> +    unsigned int parent_mask = BIT(soc_data->mux_width) - 1;
>>> +    unsigned int val;
>>> +    int ret;
>>> +
>>> +    ret = regmap_read(clkout->regmap, soc_data->reg, &val);
>>
>> Do we really need to keep return value in "ret" as I can't see you are
>> using it anywhere?
>>
>
> Right, we are not using that and can be removed.
>
>>> +
>>> +    return (val >> soc_data->mux_shift) & parent_mask;
>>> +}
>>> +
>
> [ ... ]
>
>>> +/* All existing Exynos serial of SoCs have common values for this
>>> offsets. */
>> typo: serial/series/
>
> Sure. Thanks for your review.
>
> --
> Tushar Behera
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tomasz Figa May 15, 2014, 2:07 p.m. UTC | #4
Hi Rahul, Tushar,

On 15.05.2014 15:44, Rahul Sharma wrote:
> Hi Tushar,
> 
> Basically you are adding a new clock-type for Clkout. IMO clkout
> is not a special hardware. Existing clock types can be reused to
> support clkout. I see 3 major problem here:
> 
> 1) Clkout -> (Mux + Gate). You clubbed mux and gate together, and
> exposing as a single clock which is something like a composite clock.
> IMO this is not a recommended way in CCF.
> 
> 2) New Clock Type: Since clkout is just a combination of a simple
> mux and gate which are already supported, it is a unnecessary
> duplication.
> 
> 3) Clkout registered along with CMU: which is not correct. Clkout is in PMU
> (Separate physical IP) and should be registered as a independent Clock
> provider which provides 1 mux and 1 gate clock (As if now). It should also be
> well connected with main CMU.
> 
> I understand the challenge in using regmap interface for a clock provider. But
> we need to identify a clean solution. IMHO a independent clock provider with
> iomap, is relatively cleaner approach till CCF is not ready with regmap based
> reg access for clock registers.
> 
> Experts!! please comment.

It's quite unfortunate that Tushar has duplicated the effort to create a
clkout driver, considering the fact that we did have such driver
internally at SRPOL and it was quite nice and simple.

I will post a cleaned-up version today, that is about 2 times smaller in
terms of lines of added code and provides the same functionality,
without introducing custom clock types. In addition, it models the
clkout properly as a feature of PMU, not CMU (CMU only provides outputs
of particular sub-blocks that are fed into the PMU).

Best regards,
Tomasz
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tushar Behera May 19, 2014, 3:30 a.m. UTC | #5
On 15 May 2014 19:37, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Rahul, Tushar,
>
> On 15.05.2014 15:44, Rahul Sharma wrote:
>> Hi Tushar,
>>
>> Basically you are adding a new clock-type for Clkout. IMO clkout
>> is not a special hardware. Existing clock types can be reused to
>> support clkout. I see 3 major problem here:
>>
>> 1) Clkout -> (Mux + Gate). You clubbed mux and gate together, and
>> exposing as a single clock which is something like a composite clock.
>> IMO this is not a recommended way in CCF.
>>
>> 2) New Clock Type: Since clkout is just a combination of a simple
>> mux and gate which are already supported, it is a unnecessary
>> duplication.
>>
>> 3) Clkout registered along with CMU: which is not correct. Clkout is in PMU
>> (Separate physical IP) and should be registered as a independent Clock
>> provider which provides 1 mux and 1 gate clock (As if now). It should also be
>> well connected with main CMU.
>>
>> I understand the challenge in using regmap interface for a clock provider. But
>> we need to identify a clean solution. IMHO a independent clock provider with
>> iomap, is relatively cleaner approach till CCF is not ready with regmap based
>> reg access for clock registers.
>>
>> Experts!! please comment.
>
> It's quite unfortunate that Tushar has duplicated the effort to create a
> clkout driver, considering the fact that we did have such driver
> internally at SRPOL and it was quite nice and simple.
>

I had no idea that you had some solutions to this available to be posted :(

Now that the new series is posted, I will test that at my end and
update you later.

> I will post a cleaned-up version today, that is about 2 times smaller in
> terms of lines of added code and provides the same functionality,
> without introducing custom clock types. In addition, it models the
> clkout properly as a feature of PMU, not CMU (CMU only provides outputs
> of particular sub-blocks that are fed into the PMU).
>
> Best regards,
> Tomasz
Tomasz Figa May 19, 2014, 10:44 a.m. UTC | #6
On 19.05.2014 05:30, Tushar Behera wrote:
> On 15 May 2014 19:37, Tomasz Figa <t.figa@samsung.com> wrote:
>> Hi Rahul, Tushar,
>>
>> On 15.05.2014 15:44, Rahul Sharma wrote:
>>> Hi Tushar,
>>>
>>> Basically you are adding a new clock-type for Clkout. IMO clkout
>>> is not a special hardware. Existing clock types can be reused to
>>> support clkout. I see 3 major problem here:
>>>
>>> 1) Clkout -> (Mux + Gate). You clubbed mux and gate together, and
>>> exposing as a single clock which is something like a composite clock.
>>> IMO this is not a recommended way in CCF.
>>>
>>> 2) New Clock Type: Since clkout is just a combination of a simple
>>> mux and gate which are already supported, it is a unnecessary
>>> duplication.
>>>
>>> 3) Clkout registered along with CMU: which is not correct. Clkout is in PMU
>>> (Separate physical IP) and should be registered as a independent Clock
>>> provider which provides 1 mux and 1 gate clock (As if now). It should also be
>>> well connected with main CMU.
>>>
>>> I understand the challenge in using regmap interface for a clock provider. But
>>> we need to identify a clean solution. IMHO a independent clock provider with
>>> iomap, is relatively cleaner approach till CCF is not ready with regmap based
>>> reg access for clock registers.
>>>
>>> Experts!! please comment.
>>
>> It's quite unfortunate that Tushar has duplicated the effort to create a
>> clkout driver, considering the fact that we did have such driver
>> internally at SRPOL and it was quite nice and simple.
>>
> 
> I had no idea that you had some solutions to this available to be posted :(
> 
> Now that the new series is posted, I will test that at my end and
> update you later.

Again, sorry for this duplicated effort.

The biggest problem is that we (SRPOL and Linaro) don't have a way to
share our upstream TODO lists and say "hey, we have this working, if you
need it, we can upstream it \o/" or "we need it too, but don't have
resources right now to work on it :(".

Maybe we should set up some wiki or any other semi-public site for this
purpose? I need to talk with other people from my team and my manager to
see what we can do. Other guys (other Samsung teams, Chromium folks,
hobbyists) could also benefit from it.

Anyway, thanks for your understanding and testing.

Best regards,
Tomasz
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
diff mbox

Patch

diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
index 8eb4799..d23ad4f 100644
--- a/drivers/clk/samsung/Makefile
+++ b/drivers/clk/samsung/Makefile
@@ -2,7 +2,7 @@ 
 # Samsung Clock specific Makefile
 #
 
-obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-pll.o
+obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-pll.o clk-out.o
 obj-$(CONFIG_ARCH_EXYNOS4)	+= clk-exynos4.o
 obj-$(CONFIG_SOC_EXYNOS5250)	+= clk-exynos5250.o
 obj-$(CONFIG_SOC_EXYNOS5420)	+= clk-exynos5420.o
diff --git a/drivers/clk/samsung/clk-out.c b/drivers/clk/samsung/clk-out.c
new file mode 100644
index 0000000..76489b6
--- /dev/null
+++ b/drivers/clk/samsung/clk-out.c
@@ -0,0 +1,181 @@ 
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file contains the utility functions to register the clkout clocks.
+*/
+
+/**
+ * All SoC in Exynos-series have a clock with name XCLKOUT to provide
+ * debug information about various clocks available in the SoC. The register
+ * controlling the MUX and GATE of this clock is provided within PMU domain.
+ * Since PMU domain can't be dedicatedly mapped every driver, the register
+ * needs to be handled through a regmap handle provided by PMU syscon
+ * controller. Right now, CCF doesn't allow regmap based MUX and GATE clocks,
+ * hence a dedicated clock provider for XCLKOUT is added here.
+ */
+
+#include <linux/errno.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "clk.h"
+
+/**
+ * struct samsung_clkout_soc_data: SoC specific register details
+ * @reg: Offset of CLKOUT register from PMU base
+ * @mux_shift: Start-bit of MUX bit-field
+ * @mux_width: Width of MUX bit-field
+ * @enable_bit: The bit corresponding to gating of this clock
+ */
+struct samsung_clkout_soc_data {
+	unsigned int reg;
+	u8 mux_shift;
+	u8 mux_width;
+	u8 enable_bit;
+};
+
+/**
+ * struct samsung_clkout: Structure to store driver specific clock context
+ * @hw: Handle to CCF clock
+ * @soc_data: SoC specific register details
+ * @regmap: Regmap handle of the PMU
+ */
+struct samsung_clkout {
+	struct clk_hw hw;
+	const struct samsung_clkout_soc_data *soc_data;
+	struct regmap *regmap;
+};
+
+#define to_clk_out(_hw) container_of(_hw, struct samsung_clkout, hw)
+
+int samsung_clkout_enable(struct clk_hw *hw)
+{
+	struct samsung_clkout *clkout = to_clk_out(hw);
+	const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
+	unsigned int enable_mask = BIT(soc_data->enable_bit);
+
+	/* clkout is enabled if enable bit is low */
+	regmap_update_bits(clkout->regmap, soc_data->reg, enable_mask, 0);
+
+	return 0;
+}
+
+void samsung_clkout_disable(struct clk_hw *hw)
+{
+	struct samsung_clkout *clkout = to_clk_out(hw);
+	const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
+	unsigned int enable_mask = BIT(soc_data->enable_bit);
+
+	/* clkout is gated if enable bit is high */
+	regmap_update_bits(clkout->regmap, soc_data->reg,
+			enable_mask, enable_mask);
+
+	return;
+}
+
+int samsung_clkout_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct samsung_clkout *clkout = to_clk_out(hw);
+	const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
+	unsigned int parent_mask = BIT(soc_data->mux_width) - 1;
+
+	regmap_update_bits(clkout->regmap, soc_data->reg,
+			parent_mask << soc_data->mux_shift,
+			index << soc_data->mux_shift);
+
+	return 0;
+}
+
+u8 samsung_clkout_get_parent(struct clk_hw *hw)
+{
+	struct samsung_clkout *clkout = to_clk_out(hw);
+	const struct samsung_clkout_soc_data *soc_data = clkout->soc_data;
+	unsigned int parent_mask = BIT(soc_data->mux_width) - 1;
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(clkout->regmap, soc_data->reg, &val);
+
+	return (val >> soc_data->mux_shift) & parent_mask;
+}
+
+static const struct clk_ops samsung_clkout_clk_ops = {
+	.enable = samsung_clkout_enable,
+	.disable = samsung_clkout_disable,
+	.set_parent = samsung_clkout_set_parent,
+	.get_parent = samsung_clkout_get_parent,
+};
+
+static void __init _samsung_clk_register_clkout(
+		struct samsung_out_clock *out,
+		const struct samsung_clkout_soc_data *soc_data,
+		struct regmap *regmap)
+{
+	struct samsung_clkout *clkout;
+	struct clk *clk;
+	struct clk_init_data init;
+	int ret;
+
+	clkout = kzalloc(sizeof(*clkout), GFP_KERNEL);
+	if (!clkout) {
+		pr_err("%s: could not allocate out clk %s\n",
+			__func__, out->name);
+		return;
+	}
+
+	init.name = out->name;
+	init.parent_names = out->parent_names;
+	init.num_parents = out->num_parents;
+	init.ops = &samsung_clkout_clk_ops;
+
+	clkout->hw.init = &init;
+	clkout->regmap = regmap;
+	clkout->soc_data = soc_data;
+
+	clk = clk_register(NULL, &clkout->hw);
+	if (IS_ERR(clk)) {
+		pr_err("%s: failed to register out clock %s : %ld\n",
+			__func__, out->name, PTR_ERR(clk));
+		kfree(clkout);
+		return;
+	}
+
+	samsung_clk_add_lookup(clk, out->id);
+
+	if (!out->alias)
+		return;
+
+	ret = clk_register_clkdev(clk, out->alias, out->dev_name);
+	if (ret)
+		pr_err("%s: failed to register lookup for %s : %d",
+			__func__, out->name, ret);
+}
+
+/* All existing Exynos serial of SoCs have common values for this offsets. */
+static const struct samsung_clkout_soc_data exynos_clkout_soc_data = {
+	.reg = 0xa00,
+	.mux_shift = 8,
+	.mux_width = 5,
+	.enable_bit = 0,
+};
+
+void __init samsung_clk_register_clkout(struct device_node *np,
+		struct samsung_out_clock *out_clk_list, unsigned int nr_out_clk)
+{
+	int cnt;
+	struct regmap *reg;
+	const struct samsung_clkout_soc_data *priv = &exynos_clkout_soc_data;
+
+	reg = syscon_early_regmap_lookup_by_phandle(np, "samsung,pmu-syscon");
+	if (IS_ERR(reg)) {
+		pr_err("Failed to get pmu-syscon handle for clkout\n");
+		return;
+	}
+
+	for (cnt = 0; cnt < nr_out_clk; cnt++)
+		_samsung_clk_register_clkout(&out_clk_list[cnt], priv, reg);
+}
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index c7141ba..b4b2122 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -312,6 +312,37 @@  struct samsung_pll_clock {
 	__PLL(_typ, _id, NULL, _name, _pname, CLK_GET_RATE_NOCACHE,	\
 		_lock, _con, _rtable, _alias)
 
+/**
+ * struct samsung_out_clock: information about CLKOUT clock
+ * @id: platform specific id of the clock.
+ * @dev_name: name of the device to which this clock belongs.
+ * @name: name of this mux clock.
+ * @parent_names: array of pointer to parent clock names.
+ * @num_parents: number of parents listed in @parent_names.
+ * @alias: optional clock alias name to be assigned to this clock.
+ */
+struct samsung_out_clock {
+	unsigned int		id;
+	const char		*dev_name;
+	const char		*name;
+	const char		**parent_names;
+	unsigned int		num_parents;
+	const char		*alias;
+};
+
+#define __CLKOUT(_id, dname, cname, pnames, a)		\
+	{							\
+		.id		= _id,				\
+		.dev_name	= dname,			\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.alias		= a,				\
+	}
+
+#define CLKOUT(_id, cname, pnames) \
+	__CLKOUT(_id, NULL, cname, pnames, NULL)
+
 extern void __init samsung_clk_init(struct device_node *np, void __iomem *base,
 				    unsigned long nr_clks);
 extern void __init samsung_clk_of_register_fixed_ext(
@@ -335,6 +366,8 @@  extern void __init samsung_clk_register_gate(
 		struct samsung_gate_clock *clk_list, unsigned int nr_clk);
 extern void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
 		unsigned int nr_clk, void __iomem *base);
+extern void __init samsung_clk_register_clkout(struct device_node *np,
+	struct samsung_out_clock *out_clk_list, unsigned int nr_out_clk);
 
 extern unsigned long _get_rate(const char *clk_name);