lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20250501-deafening-obtain-ab689094972d@wendy>
Date: Thu, 1 May 2025 09:51:48 +0100
From: Conor Dooley <conor.dooley@...rochip.com>
To: Conor Dooley <conor@...nel.org>
CC: Stephen Boyd <sboyd@...nel.org>, Daire McNamara
	<daire.mcnamara@...rochip.com>, <pierre-henry.moussay@...rochip.com>,
	<valentina.fernandezalanis@...rochip.com>, Michael Turquette
	<mturquette@...libre.com>, Rob Herring <robh@...nel.org>, Krzysztof Kozlowski
	<krzk+dt@...nel.org>, Jassi Brar <jassisinghbrar@...il.com>, Lee Jones
	<lee@...nel.org>, Paul Walmsley <paul.walmsley@...ive.com>, Palmer Dabbelt
	<palmer@...belt.com>, Philipp Zabel <p.zabel@...gutronix.de>,
	<linux-riscv@...ts.infradead.org>, <linux-clk@...r.kernel.org>,
	<devicetree@...r.kernel.org>, <linux-kernel@...r.kernel.org>
Subject: Re: [RFC PATCH v2 8/9] clk: divider, gate: create regmap-backed
 copies of gate and divider clocks

Stephen,

On Fri, Mar 21, 2025 at 05:22:41PM +0000, Conor Dooley wrote:
> From: Conor Dooley <conor.dooley@...rochip.com>
> 
> Implement regmap-backed copies of gate and divider clocks by replacing
> the iomem pointer to the clock registers with a regmap and offset
> within.

Is this the sort of implementation you were looking for in your comments
on the previous version of this series?
(https://lore.kernel.org/linux-clk/20250220-iciness-mobilize-94a027ff52ce@spud/)

Cheers,
Conor.

> 
> Signed-off-by: Conor Dooley <conor.dooley@...rochip.com>
> ---
>  drivers/clk/Kconfig              |   8 +
>  drivers/clk/Makefile             |   2 +
>  drivers/clk/clk-divider-regmap.c | 270 +++++++++++++++++++++++++++++++
>  drivers/clk/clk-gate-regmap.c    | 253 +++++++++++++++++++++++++++++
>  drivers/clk/clk-gate.c           |   5 +-
>  include/linux/clk-provider.h     | 120 ++++++++++++++
>  6 files changed, 656 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/clk/clk-divider-regmap.c
>  create mode 100644 drivers/clk/clk-gate-regmap.c
> 
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 713573b6c86c..c700b6ef4f34 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -33,6 +33,14 @@ menuconfig COMMON_CLK
>  
>  if COMMON_CLK
>  
> +config COMMON_CLK_DIVIDER_REGMAP
> +	bool
> +	select REGMAP
> +
> +config COMMON_CLK_GATE_REGMAP
> +	bool
> +	select REGMAP
> +
>  config COMMON_CLK_WM831X
>  	tristate "Clock driver for WM831x/2x PMICs"
>  	depends on MFD_WM831X
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index bf4bd45adc3a..c57f72af5db9 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -20,11 +20,13 @@ clk-test-y			:= clk_test.o \
>  				   kunit_clk_assigned_rates_zero_consumer.dtbo.o \
>  				   kunit_clk_parent_data_test.dtbo.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-divider.o
> +obj-$(CONFIG_COMMON_CLK_DIVIDER_REGMAP)	+= clk-divider-regmap.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-factor.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o
>  obj-$(CONFIG_CLK_FIXED_RATE_KUNIT_TEST)	+= clk-fixed-rate-test.o
>  clk-fixed-rate-test-y		:= clk-fixed-rate_test.o kunit_clk_fixed_rate_test.dtbo.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o
> +obj-$(CONFIG_COMMON_CLK_GATE_REGMAP)	+= clk-gate-regmap.o
>  obj-$(CONFIG_CLK_GATE_KUNIT_TEST) += clk-gate_test.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-multiplier.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-mux.o
> diff --git a/drivers/clk/clk-divider-regmap.c b/drivers/clk/clk-divider-regmap.c
> new file mode 100644
> index 000000000000..2906130a2b47
> --- /dev/null
> +++ b/drivers/clk/clk-divider-regmap.c
> @@ -0,0 +1,270 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +
> +static inline u32 clk_div_regmap_readl(struct clk_divider_regmap *divider)
> +{
> +	u32 val;
> +
> +	regmap_read(divider->regmap, divider->map_offset, &val);
> +
> +	return val;
> +}
> +
> +static inline void clk_div_regmap_writel(struct clk_divider_regmap *divider, u32 val)
> +{
> +	regmap_write(divider->regmap, divider->map_offset, val);
> +
> +}
> +
> +static unsigned long clk_divider_regmap_recalc_rate(struct clk_hw *hw,
> +		unsigned long parent_rate)
> +{
> +	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
> +	unsigned int val;
> +
> +	val = clk_div_regmap_readl(divider) >> divider->shift;
> +	val &= clk_div_mask(divider->width);
> +
> +	return divider_recalc_rate(hw, parent_rate, val, divider->table,
> +				   divider->flags, divider->width);
> +}
> +
> +static long clk_divider_regmap_round_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long *prate)
> +{
> +	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
> +
> +	/* if read only, just return current value */
> +	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
> +		u32 val;
> +
> +		val = clk_div_regmap_readl(divider) >> divider->shift;
> +		val &= clk_div_mask(divider->width);
> +
> +		return divider_ro_round_rate(hw, rate, prate, divider->table,
> +					     divider->width, divider->flags,
> +					     val);
> +	}
> +
> +	return divider_round_rate(hw, rate, prate, divider->table,
> +				  divider->width, divider->flags);
> +}
> +
> +static int clk_divider_regmap_determine_rate(struct clk_hw *hw,
> +				      struct clk_rate_request *req)
> +{
> +	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
> +
> +	/* if read only, just return current value */
> +	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
> +		u32 val;
> +
> +		val = clk_div_regmap_readl(divider) >> divider->shift;
> +		val &= clk_div_mask(divider->width);
> +
> +		return divider_ro_determine_rate(hw, req, divider->table,
> +						 divider->width,
> +						 divider->flags, val);
> +	}
> +
> +	return divider_determine_rate(hw, req, divider->table, divider->width,
> +				      divider->flags);
> +}
> +
> +static int clk_divider_regmap_set_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long parent_rate)
> +{
> +	struct clk_divider_regmap *divider = to_clk_divider_regmap(hw);
> +	int value;
> +	unsigned long flags = 0;
> +	u32 val;
> +
> +	value = divider_get_val(rate, parent_rate, divider->table,
> +				divider->width, divider->flags);
> +	if (value < 0)
> +		return value;
> +
> +	if (divider->lock)
> +		spin_lock_irqsave(divider->lock, flags);
> +	else
> +		__acquire(divider->lock);
> +
> +	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
> +		val = clk_div_mask(divider->width) << (divider->shift + 16);
> +	} else {
> +		val = clk_div_regmap_readl(divider);
> +		val &= ~(clk_div_mask(divider->width) << divider->shift);
> +	}
> +	val |= (u32)value << divider->shift;
> +	clk_div_regmap_writel(divider, val);
> +
> +	if (divider->lock)
> +		spin_unlock_irqrestore(divider->lock, flags);
> +	else
> +		__release(divider->lock);
> +
> +	return 0;
> +}
> +
> +const struct clk_ops clk_divider_regmap_ops = {
> +	.recalc_rate = clk_divider_regmap_recalc_rate,
> +	.round_rate = clk_divider_regmap_round_rate,
> +	.determine_rate = clk_divider_regmap_determine_rate,
> +	.set_rate = clk_divider_regmap_set_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_divider_regmap_ops);
> +
> +const struct clk_ops clk_divider_regmap_ro_ops = {
> +	.recalc_rate = clk_divider_regmap_recalc_rate,
> +	.round_rate = clk_divider_regmap_round_rate,
> +	.determine_rate = clk_divider_regmap_determine_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_divider_regmap_ro_ops);
> +
> +struct clk_hw *__clk_hw_register_divider_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock)
> +{
> +	struct clk_divider_regmap *div;
> +	struct clk_hw *hw;
> +	struct clk_init_data init = {};
> +	int ret;
> +
> +	if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
> +		if (width + shift > 16) {
> +			pr_warn("divider value exceeds LOWORD field\n");
> +			return ERR_PTR(-EINVAL);
> +		}
> +	}
> +
> +	/* allocate the divider */
> +	div = kzalloc(sizeof(*div), GFP_KERNEL);
> +	if (!div)
> +		return ERR_PTR(-ENOMEM);
> +
> +	init.name = name;
> +	if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
> +		init.ops = &clk_divider_regmap_ro_ops;
> +	else
> +		init.ops = &clk_divider_regmap_ops;
> +	init.flags = flags;
> +	init.parent_names = parent_name ? &parent_name : NULL;
> +	init.parent_hws = parent_hw ? &parent_hw : NULL;
> +	init.parent_data = parent_data;
> +	if (parent_name || parent_hw || parent_data)
> +		init.num_parents = 1;
> +	else
> +		init.num_parents = 0;
> +
> +	/* struct clk_divider assignments */
> +	div->regmap = regmap;
> +	div->map_offset = map_offset;
> +	div->shift = shift;
> +	div->width = width;
> +	div->flags = clk_divider_flags;
> +	div->lock = lock;
> +	div->hw.init = &init;
> +	div->table = table;
> +
> +	/* register the clock */
> +	hw = &div->hw;
> +	ret = clk_hw_register(dev, hw);
> +	if (ret) {
> +		kfree(div);
> +		hw = ERR_PTR(ret);
> +	}
> +
> +	return hw;
> +}
> +EXPORT_SYMBOL_GPL(__clk_hw_register_divider_regmap);
> +
> +struct clk *clk_register_divider_regmap_table(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock)
> +{
> +	struct clk_hw *hw;
> +
> +	hw =  __clk_hw_register_divider_regmap(dev, NULL, name, parent_name, NULL,
> +					       NULL, flags, regmap, map_offset,
> +					       shift, width, clk_divider_flags,
> +					       table, lock);
> +	if (IS_ERR(hw))
> +		return ERR_CAST(hw);
> +	return hw->clk;
> +}
> +EXPORT_SYMBOL_GPL(clk_register_divider_regmap_table);
> +
> +void clk_unregister_divider_regmap(struct clk *clk)
> +{
> +	struct clk_divider_regmap *div;
> +	struct clk_hw *hw;
> +
> +	hw = __clk_get_hw(clk);
> +	if (!hw)
> +		return;
> +
> +	div = to_clk_divider_regmap(hw);
> +
> +	clk_unregister(clk);
> +	kfree(div);
> +}
> +EXPORT_SYMBOL_GPL(clk_unregister_divider_regmap);
> +
> +/**
> + * clk_hw_unregister_divider_regmap - unregister a clk divider
> + * @hw: hardware-specific clock data to unregister
> + */
> +void clk_hw_unregister_divider_regmap(struct clk_hw *hw)
> +{
> +	struct clk_divider_regmap *div;
> +
> +	div = to_clk_divider_regmap(hw);
> +
> +	clk_hw_unregister(hw);
> +	kfree(div);
> +}
> +EXPORT_SYMBOL_GPL(clk_hw_unregister_divider_regmap);
> +
> +static void devm_clk_hw_release_divider_regmap(struct device *dev, void *res)
> +{
> +	clk_hw_unregister_divider_regmap(*(struct clk_hw **)res);
> +}
> +
> +struct clk_hw *__devm_clk_hw_register_divider_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock)
> +{
> +	struct clk_hw **ptr, *hw;
> +
> +	ptr = devres_alloc(devm_clk_hw_release_divider_regmap, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	hw = __clk_hw_register_divider_regmap(dev, np, name, parent_name, parent_hw,
> +					      parent_data, flags, regmap, map_offset,
> +					      shift, width, clk_divider_flags, table,
> +					      lock);
> +
> +	if (!IS_ERR(hw)) {
> +		*ptr = hw;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return hw;
> +}
> +EXPORT_SYMBOL_GPL(__devm_clk_hw_register_divider_regmap);
> diff --git a/drivers/clk/clk-gate-regmap.c b/drivers/clk/clk-gate-regmap.c
> new file mode 100644
> index 000000000000..cf9e48407971
> --- /dev/null
> +++ b/drivers/clk/clk-gate-regmap.c
> @@ -0,0 +1,253 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +
> +/**
> + * DOC: basic gatable clock which can gate and ungate its output
> + *
> + * Traits of this clock:
> + * prepare - clk_(un)prepare only ensures parent is (un)prepared
> + * enable - clk_enable and clk_disable are functional & control gating
> + * rate - inherits rate from parent.  No clk_set_rate support
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +static inline u32 clk_gate_regmap_readl(struct clk_gate_regmap *gate)
> +{
> +	u32 val;
> +
> +	regmap_read(gate->map, gate->map_offset, &val);
> +
> +	return val;
> +}
> +
> +static inline void clk_gate_regmap_writel(struct clk_gate_regmap *gate, u32 val)
> +{
> +	regmap_write(gate->map, gate->map_offset, val);
> +
> +}
> +
> +/*
> + * It works on following logic:
> + *
> + * For enabling clock, enable = 1
> + *	set2dis = 1	-> clear bit	-> set = 0
> + *	set2dis = 0	-> set bit	-> set = 1
> + *
> + * For disabling clock, enable = 0
> + *	set2dis = 1	-> set bit	-> set = 1
> + *	set2dis = 0	-> clear bit	-> set = 0
> + *
> + * So, result is always: enable xor set2dis.
> + */
> +static void clk_gate_regmap_endisable(struct clk_hw *hw, int enable)
> +{
> +	struct clk_gate_regmap *gate = to_clk_gate_regmap(hw);
> +	int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
> +	unsigned long flags;
> +	u32 reg;
> +
> +	set ^= enable;
> +
> +	if (gate->lock)
> +		spin_lock_irqsave(gate->lock, flags);
> +	else
> +		__acquire(gate->lock);
> +
> +	if (gate->flags & CLK_GATE_HIWORD_MASK) {
> +		reg = BIT(gate->bit_idx + 16);
> +		if (set)
> +			reg |= BIT(gate->bit_idx);
> +	} else {
> +		reg = clk_gate_regmap_readl(gate);
> +
> +		if (set)
> +			reg |= BIT(gate->bit_idx);
> +		else
> +			reg &= ~BIT(gate->bit_idx);
> +	}
> +
> +	clk_gate_regmap_writel(gate, reg);
> +
> +	if (gate->lock)
> +		spin_unlock_irqrestore(gate->lock, flags);
> +	else
> +		__release(gate->lock);
> +}
> +
> +static int clk_gate_regmap_enable(struct clk_hw *hw)
> +{
> +	clk_gate_regmap_endisable(hw, 1);
> +
> +	return 0;
> +}
> +
> +static void clk_gate_regmap_disable(struct clk_hw *hw)
> +{
> +	clk_gate_regmap_endisable(hw, 0);
> +}
> +
> +int clk_gate_regmap_is_enabled(struct clk_hw *hw)
> +{
> +	u32 reg;
> +	struct clk_gate_regmap *gate = to_clk_gate_regmap(hw);
> +
> +	reg = clk_gate_regmap_readl(gate);
> +
> +	/* if a set bit disables this clk, flip it before masking */
> +	if (gate->flags & CLK_GATE_SET_TO_DISABLE)
> +		reg ^= BIT(gate->bit_idx);
> +
> +	reg &= BIT(gate->bit_idx);
> +
> +	return reg ? 1 : 0;
> +}
> +EXPORT_SYMBOL_GPL(clk_gate_regmap_is_enabled);
> +
> +const struct clk_ops clk_gate_regmap_ops = {
> +	.enable = clk_gate_regmap_enable,
> +	.disable = clk_gate_regmap_disable,
> +	.is_enabled = clk_gate_regmap_is_enabled,
> +};
> +EXPORT_SYMBOL_GPL(clk_gate_regmap_ops);
> +
> +struct clk_hw *__clk_hw_register_gate_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data,
> +		unsigned long flags,
> +		struct regmap *map, u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock)
> +{
> +	struct clk_gate_regmap *gate;
> +	struct clk_hw *hw;
> +	struct clk_init_data init = {};
> +	int ret = -EINVAL;
> +
> +	if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
> +		if (bit_idx > 15) {
> +			pr_err("gate bit exceeds LOWORD field\n");
> +			return ERR_PTR(-EINVAL);
> +		}
> +	}
> +
> +	/* allocate the gate */
> +	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +	if (!gate)
> +		return ERR_PTR(-ENOMEM);
> +
> +	init.name = name;
> +	init.ops = &clk_gate_regmap_ops;
> +	init.flags = flags;
> +	init.parent_names = parent_name ? &parent_name : NULL;
> +	init.parent_hws = parent_hw ? &parent_hw : NULL;
> +	init.parent_data = parent_data;
> +	if (parent_name || parent_hw || parent_data)
> +		init.num_parents = 1;
> +	else
> +		init.num_parents = 0;
> +
> +	/* struct clk_gate_regmap assignments */
> +	gate->map = map;
> +	gate->map_offset = map_offset;
> +	gate->bit_idx = bit_idx;
> +	gate->flags = clk_gate_flags;
> +	gate->lock = lock;
> +	gate->hw.init = &init;
> +
> +	hw = &gate->hw;
> +	if (dev || !np)
> +		ret = clk_hw_register(dev, hw);
> +	else if (np)
> +		ret = of_clk_hw_register(np, hw);
> +	if (ret) {
> +		kfree(gate);
> +		hw = ERR_PTR(ret);
> +	}
> +
> +	return hw;
> +
> +}
> +EXPORT_SYMBOL_GPL(__clk_hw_register_gate_regmap);
> +
> +struct clk *clk_register_gate_regmap(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags, struct regmap *map,
> +		u8 map_offset, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock)
> +{
> +	struct clk_hw *hw;
> +
> +	hw = __clk_hw_register_gate_regmap(dev, NULL, name, parent_name, NULL,
> +					   NULL, flags, map, map_offset, bit_idx,
> +					   clk_gate_flags, lock);
> +	if (IS_ERR(hw))
> +		return ERR_CAST(hw);
> +	return hw->clk;
> +}
> +EXPORT_SYMBOL_GPL(clk_register_gate_regmap);
> +
> +void clk_unregister_gate_regmap(struct clk *clk)
> +{
> +	struct clk_gate_regmap *gate;
> +	struct clk_hw *hw;
> +
> +	hw = __clk_get_hw(clk);
> +	if (!hw)
> +		return;
> +
> +	gate = to_clk_gate_regmap(hw);
> +
> +	clk_unregister(clk);
> +	kfree(gate);
> +}
> +EXPORT_SYMBOL_GPL(clk_unregister_gate_regmap);
> +
> +void clk_hw_unregister_gate_regmap(struct clk_hw *hw)
> +{
> +	struct clk_gate_regmap *gate;
> +
> +	gate = to_clk_gate_regmap(hw);
> +
> +	clk_hw_unregister(hw);
> +	kfree(gate);
> +}
> +EXPORT_SYMBOL_GPL(clk_hw_unregister_gate_regmap);
> +
> +static void devm_clk_hw_release_gate_regmap(struct device *dev, void *res)
> +{
> +	clk_hw_unregister_gate_regmap(*(struct clk_hw **)res);
> +}
> +
> +struct clk_hw *__devm_clk_hw_register_gate_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data,
> +		unsigned long flags, struct regmap *map,
> +		u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock)
> +{
> +	struct clk_hw **ptr, *hw;
> +
> +	ptr = devres_alloc(devm_clk_hw_release_gate_regmap, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	hw = __clk_hw_register_gate_regmap(dev, np, name, parent_name, parent_hw,
> +					   parent_data, flags, map, map_offset,
> +					   bit_idx, clk_gate_flags, lock);
> +
> +	if (!IS_ERR(hw)) {
> +		*ptr = hw;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return hw;
> +}
> +EXPORT_SYMBOL_GPL(__devm_clk_hw_register_gate_regmap);
> diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
> index 68e585a02fd9..1e3f21a249ca 100644
> --- a/drivers/clk/clk-gate.c
> +++ b/drivers/clk/clk-gate.c
> @@ -34,10 +34,11 @@ static inline u32 clk_gate_readl(struct clk_gate *gate)
>  
>  static inline void clk_gate_writel(struct clk_gate *gate, u32 val)
>  {
> -	if (gate->flags & CLK_GATE_BIG_ENDIAN)
> +	if (gate->flags & CLK_GATE_BIG_ENDIAN) {
>  		iowrite32be(val, gate->reg);
> -	else
> +	} else {
>  		writel(val, gate->reg);
> +	}
>  }
>  
>  /*
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 2e6e603b7493..6f5cf6670b48 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -8,6 +8,7 @@
>  
>  #include <linux/of.h>
>  #include <linux/of_clk.h>
> +#include <linux/regmap.h>
>  
>  /*
>   * flags used across common struct clk.  these flags should only affect the
> @@ -526,6 +527,7 @@ void of_fixed_clk_setup(struct device_node *np);
>  struct clk_gate {
>  	struct clk_hw hw;
>  	void __iomem	*reg;
> +	u8		map_offset;
>  	u8		bit_idx;
>  	u8		flags;
>  	spinlock_t	*lock;
> @@ -538,6 +540,37 @@ struct clk_gate {
>  #define CLK_GATE_BIG_ENDIAN		BIT(2)
>  
>  extern const struct clk_ops clk_gate_ops;
> +
> +#ifdef CONFIG_COMMON_CLK_GATE_REGMAP
> +/**
> + * struct clk_gate_regmap - gating clock via regmap
> + *
> + * @hw:		handle between common and hardware-specific interfaces
> + * @map:	regmap controlling gate
> + * @map_offset:	register offset within the regmap controlling gate
> + * @bit_idx:	single bit controlling gate
> + * @flags:	hardware-specific flags
> + * @lock:	register lock
> + *
> + * Clock which can gate its output.  Implements .enable & .disable
> + *
> + * Flags:
> + * See clk_gate
> + */
> +struct clk_gate_regmap {
> +	struct clk_hw hw;
> +	struct regmap	*map;
> +	u8		map_offset;
> +	u8		bit_idx;
> +	u8		flags;
> +	spinlock_t	*lock;
> +};
> +
> +#define to_clk_gate_regmap(_hw) container_of(_hw, struct clk_gate_regmap, hw)
> +
> +extern const struct clk_ops clk_gate_regmap_ops;
> +#endif
> +
>  struct clk_hw *__clk_hw_register_gate(struct device *dev,
>  		struct device_node *np, const char *name,
>  		const char *parent_name, const struct clk_hw *parent_hw,
> @@ -663,6 +696,31 @@ void clk_unregister_gate(struct clk *clk);
>  void clk_hw_unregister_gate(struct clk_hw *hw);
>  int clk_gate_is_enabled(struct clk_hw *hw);
>  
> +#ifdef CONFIG_COMMON_CLK_GATE_REGMAP
> +struct clk_hw *__clk_hw_register_gate_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data,
> +		unsigned long flags,
> +		struct regmap *map, u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock);
> +struct clk_hw *__devm_clk_hw_register_gate_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data,
> +		unsigned long flags,
> +		struct regmap *map, u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock);
> +struct clk *clk_register_gate_regmap(struct device *dev, const char *name,
> +		const char *parent_name, unsigned long flags,
> +		struct regmap *map, u8 map_offset, u8 bit_idx,
> +		u8 clk_gate_flags, spinlock_t *lock);
> +
> +void clk_unregister_gate_regmap(struct clk *clk);
> +void clk_hw_unregister_gate_regmap(struct clk_hw *hw);
> +int clk_gate_regmap_is_enabled(struct clk_hw *hw);
> +#endif
> +
>  struct clk_div_table {
>  	unsigned int	val;
>  	unsigned int	div;
> @@ -736,6 +794,41 @@ struct clk_divider {
>  extern const struct clk_ops clk_divider_ops;
>  extern const struct clk_ops clk_divider_ro_ops;
>  
> +#ifdef CONFIG_COMMON_CLK_DIVIDER_REGMAP
> +/**
> + * struct clk_divider_regmap - adjustable divider clock via regmap
> + *
> + * @hw:		handle between common and hardware-specific interfaces
> + * @map:	regmap containing the divider
> + * @map_offset:	register offset within the regmap containing the divider
> + * @shift:	shift to the divider bit field
> + * @width:	width of the divider bit field
> + * @table:	array of value/divider pairs, last entry should have div = 0
> + * @lock:	register lock
> + *
> + * Clock with an adjustable divider affecting its output frequency.  Implements
> + * .recalc_rate, .set_rate and .round_rate
> + *
> + * @flags:
> + * See clk_divider
> + */
> +struct clk_divider_regmap {
> +	struct clk_hw	hw;
> +	struct regmap	*regmap;
> +	u8		map_offset;
> +	u8		shift;
> +	u8		width;
> +	u8		flags;
> +	const struct clk_div_table	*table;
> +	spinlock_t	*lock;
> +};
> +
> +#define to_clk_divider_regmap(_hw) container_of(_hw, struct clk_divider_regmap, hw)
> +
> +extern const struct clk_ops clk_divider_regmap_ops;
> +extern const struct clk_ops clk_divider_regmap_ro_ops;
> +#endif
> +
>  unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
>  		unsigned int val, const struct clk_div_table *table,
>  		unsigned long flags, unsigned long width);
> @@ -972,6 +1065,33 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
>  void clk_unregister_divider(struct clk *clk);
>  void clk_hw_unregister_divider(struct clk_hw *hw);
>  
> +#ifdef CONFIG_COMMON_CLK_DIVIDER_REGMAP
> +struct clk_hw *__clk_hw_register_divider_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock);
> +
> +struct clk_hw *__devm_clk_hw_register_divider_regmap(struct device *dev,
> +		struct device_node *np, const char *name,
> +		const char *parent_name, const struct clk_hw *parent_hw,
> +		const struct clk_parent_data *parent_data, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock);
> +
> +struct clk *clk_register_divider_regmap_table(struct device *dev,
> +		const char *name, const char *parent_name, unsigned long flags,
> +		struct regmap *regmap, u8 map_offset, u8 shift, u8 width,
> +		u8 clk_divider_flags, const struct clk_div_table *table,
> +		spinlock_t *lock);
> +
> +void clk_unregister_divider_regmap(struct clk *clk);
> +void clk_hw_unregister_divider_regmap(struct clk_hw *hw);
> +#endif
> +
>  /**
>   * struct clk_mux - multiplexer clock
>   *
> -- 
> 2.45.2
> 

Download attachment "signature.asc" of type "application/pgp-signature" (229 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ