[PATCH 1/4] clk: samsung: out: Add infrastructure to register CLKOUT
Pankaj Dubey
pankaj.dubey at samsung.com
Fri May 9 20:51:31 PDT 2014
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 at linaro.org>
> CC: Tomasz Figa <t.figa at 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);
>
--
Best Regards,
Pankaj Dubey
More information about the linux-arm-kernel
mailing list