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

From: [email-masked] (Pankaj Dubey)
Date: Sat, 10 May 2014 12:51:31 +0900

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 <[email-masked]>
> CC: Tomasz Figa <[email-masked]>
> ---
>   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);
>   

-- 
Best Regards,
Pankaj Dubey