All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] clk: multiplier: introduce configurable clock multiplier
@ 2015-05-22 20:53 Jim Quinlan
  2015-07-23 19:17 ` Michael Turquette
  0 siblings, 1 reply; 2+ messages in thread
From: Jim Quinlan @ 2015-05-22 20:53 UTC (permalink / raw)
  To: mturquette, linux-clk; +Cc: bcm-kernel-feedback-list, Jim Quinlan

Broadcom STB SoCs come with a handful of configurable clock multipliers.
Although care must be taken to change them, they are configurable
nonetheless.  This commit introduces a configurable multiplier clock,
and it is essentially the mirror of clk-divider.c.

Signed-off-by: Jim Quinlan <jim2101024@gmail.com>
---
 drivers/clk/Makefile         |   1 +
 drivers/clk/clk-multiplier.c | 440 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk-provider.h |  81 ++++++++
 3 files changed, 522 insertions(+)
 create mode 100644 drivers/clk/clk-multiplier.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 3d00c25..29682e4 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_HAVE_CLK)		+= clk-devres.o
 obj-$(CONFIG_CLKDEV_LOOKUP)	+= clkdev.o
 obj-$(CONFIG_COMMON_CLK)	+= clk.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-divider.o
+obj-$(CONFIG_COMMON_CLK)	+= clk-multiplier.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-factor.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o
diff --git a/drivers/clk/clk-multiplier.c b/drivers/clk/clk-multiplier.c
new file mode 100644
index 0000000..de2d594
--- /dev/null
+++ b/drivers/clk/clk-multiplier.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2015 Jim Quinlan, Broadcom <jim2101024@gmail.com>
+ *
+ * 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.
+ *
+ * Adjustable multiplier clock implementation.  This is essentially
+ * the mirror of clk-divider.c.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/log2.h>
+
+/*
+ * DOC: basic adjustable multiplier clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable.  clk->rate = (parent->rate * multiplier)
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define to_clk_multiplier(_hw) container_of(_hw, struct clk_multiplier, hw)
+
+#define mult_mask(width)	((1 << (width)) - 1)
+
+static unsigned int _get_table_maxmult(const struct clk_mult_table *table)
+{
+	unsigned int maxmult = 0;
+	const struct clk_mult_table *clkt;
+
+	for (clkt = table; clkt->mult; clkt++)
+		if (clkt->mult > maxmult)
+			maxmult = clkt->mult;
+	return maxmult;
+}
+
+static unsigned int _get_table_minmult(const struct clk_mult_table *table)
+{
+	unsigned int minmult = UINT_MAX;
+	const struct clk_mult_table *clkt;
+
+	for (clkt = table; clkt->mult; clkt++)
+		if (clkt->mult < minmult)
+			minmult = clkt->mult;
+	return minmult;
+}
+
+static unsigned int _get_maxmult(const struct clk_mult_table *table, u8 width,
+				 unsigned long flags)
+{
+	if (flags & CLK_MULTIPLIER_ONE_BASED)
+		return mult_mask(width);
+	if (flags & CLK_MULTIPLIER_POWER_OF_TWO)
+		return 1 << mult_mask(width);
+	if (table)
+		return _get_table_maxmult(table);
+	return mult_mask(width) + 1;
+}
+
+static unsigned int _get_minmult(const struct clk_mult_table *table)
+{
+	if (table)
+		return _get_table_minmult(table);
+	return 1;
+}
+
+static unsigned int _get_table_mult(const struct clk_mult_table *table,
+				    unsigned int val)
+{
+	const struct clk_mult_table *clkt;
+
+	for (clkt = table; clkt->mult; clkt++)
+		if (clkt->val == val)
+			return clkt->mult;
+	return 0;
+}
+
+static unsigned int _get_mult(const struct clk_mult_table *table,
+			      u8 width, unsigned int val, unsigned long flags)
+{
+	if (flags & CLK_MULTIPLIER_ONE_BASED)
+		return val;
+	if (flags & CLK_MULTIPLIER_POWER_OF_TWO)
+		return 1 << val;
+	if (flags & CLK_MULTIPLIER_MAX_MULT_AT_ZERO)
+		return val ? val : mult_mask(width) + 1;
+	if (table)
+		return _get_table_mult(table, val);
+	return val + 1;
+}
+
+static unsigned int _get_table_val(const struct clk_mult_table *table,
+				   unsigned int mult)
+{
+	const struct clk_mult_table *clkt;
+
+	for (clkt = table; clkt->mult; clkt++)
+		if (clkt->mult == mult)
+			return clkt->val;
+	return 0;
+}
+
+static unsigned int _get_val(const struct clk_mult_table *table,
+			     u8 width, unsigned int mult, unsigned long flags)
+{
+	if (flags & CLK_MULTIPLIER_ONE_BASED)
+		return mult;
+	if (flags & CLK_MULTIPLIER_POWER_OF_TWO)
+		return __ffs(mult);
+	if (flags & CLK_MULTIPLIER_MAX_MULT_AT_ZERO)
+		return (mult == mult_mask(width) + 1)
+			? 0 : mult;
+	if (table)
+		return  _get_table_val(table, mult);
+	return mult - 1;
+}
+
+unsigned long multiplier_recalc_rate(struct clk_hw *hw,
+				     unsigned long parent_rate,
+				     unsigned int val,
+				     const struct clk_mult_table *table,
+				     unsigned long flags)
+{
+	struct clk_multiplier *multiplier = to_clk_multiplier(hw);
+	unsigned int mult;
+
+	mult = _get_mult(table, multiplier->width, val, flags);
+	if (!mult) {
+		WARN(!(flags & CLK_MULTIPLIER_ALLOW_ZERO),
+			"%s: Zero multiplier and CLK_MULTIPLIER_ALLOW_ZERO not set\n",
+			__clk_get_name(hw->clk));
+		return parent_rate;
+	}
+
+	return parent_rate * mult;
+}
+EXPORT_SYMBOL_GPL(multiplier_recalc_rate);
+
+static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct clk_multiplier *multiplier = to_clk_multiplier(hw);
+	unsigned int val;
+
+	val = clk_readl(multiplier->reg) >> multiplier->shift;
+	val &= mult_mask(multiplier->width);
+
+	return multiplier_recalc_rate(hw, parent_rate, val, multiplier->table,
+				   multiplier->flags);
+}
+
+static bool _is_valid_table_mult(const struct clk_mult_table *table,
+							 unsigned int mult)
+{
+	const struct clk_mult_table *clkt;
+
+	for (clkt = table; clkt->mult; clkt++)
+		if (clkt->mult == mult)
+			return true;
+	return false;
+}
+
+static bool _is_valid_mult(const struct clk_mult_table *table,
+			   unsigned int mult, unsigned long flags)
+{
+	if (flags & CLK_MULTIPLIER_POWER_OF_TWO)
+		return is_power_of_2(mult);
+	if (table)
+		return _is_valid_table_mult(table, mult);
+	return true;
+}
+
+static int clk_multiplier_bestmult(struct clk_hw *hw, unsigned long rate,
+			       unsigned long *best_parent_rate,
+			       const struct clk_mult_table *table, u8 width,
+			       unsigned long flags)
+{
+	int i, bestmult = 0;
+	unsigned long parent_rate, best = 0, now, maxmult, minmult;
+	unsigned long parent_rate_saved = *best_parent_rate;
+
+	if (!rate)
+		rate = 1;
+
+	minmult = _get_minmult(table);
+	maxmult = _get_maxmult(table, width, flags);
+
+	if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
+		parent_rate = *best_parent_rate;
+		bestmult = rate / parent_rate;
+		bestmult = bestmult == 0 ? minmult : bestmult;
+		bestmult = bestmult > maxmult ? maxmult : bestmult;
+		return bestmult;
+	}
+
+	/*
+	 * The maximum multiplier we can use without overflowing
+	 * unsigned long in rate * i below
+	 */
+	maxmult = min(ULONG_MAX / parent_rate_saved, maxmult);
+
+	for (i = 1; i <= maxmult; i++) {
+		if (!_is_valid_mult(table, i, flags))
+			continue;
+		if (rate == parent_rate_saved * i) {
+			/*
+			 * It's the most ideal case if the requested rate can be
+			 * multiplied from parent clock without needing to
+			 * change the parent rate, so return the multiplier
+			 * immediately.
+			 */
+			*best_parent_rate = parent_rate_saved;
+			return i;
+		}
+		parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
+					       rate / i);
+		now = parent_rate * i;
+		if (now <= rate && now > best) {
+			bestmult = i;
+			best = now;
+			*best_parent_rate = parent_rate;
+		}
+	}
+
+	if (!bestmult) {
+		bestmult = _get_minmult(table);
+		*best_parent_rate
+			= __clk_round_rate(__clk_get_parent(hw->clk), 1);
+	}
+
+	return bestmult;
+}
+
+long multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long *prate,
+			   const struct clk_mult_table *table,
+			   u8 width, unsigned long flags)
+{
+	int mult;
+
+	mult = clk_multiplier_bestmult(hw, rate, prate, table, width, flags);
+
+	return *prate * mult;
+}
+EXPORT_SYMBOL_GPL(multiplier_round_rate);
+
+static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
+{
+	struct clk_multiplier *multiplier = to_clk_multiplier(hw);
+	int bestmult;
+
+	/* if read only, just return current value */
+	if (multiplier->flags & CLK_MULTIPLIER_READ_ONLY) {
+		bestmult = readl(multiplier->reg) >> multiplier->shift;
+		bestmult &= mult_mask(multiplier->width);
+		bestmult = _get_mult(multiplier->table, multiplier->width,
+				     bestmult, multiplier->flags);
+		if ((__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT))
+			*prate = __clk_round_rate(__clk_get_parent(hw->clk),
+						  rate);
+		return *prate * bestmult;
+	}
+
+	return multiplier_round_rate(hw, rate, prate, multiplier->table,
+				  multiplier->width, multiplier->flags);
+}
+
+int multiplier_get_val(unsigned long rate, unsigned long parent_rate,
+		    const struct clk_mult_table *table, u8 width,
+		    unsigned long flags)
+{
+	unsigned int mult, value;
+
+	mult = rate / parent_rate;
+	value = _get_val(table, width, mult, flags);
+	return min_t(unsigned int, value, mult_mask(width));
+}
+EXPORT_SYMBOL_GPL(multiplier_get_val);
+
+static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	struct clk_multiplier *multiplier = to_clk_multiplier(hw);
+	unsigned int value;
+	unsigned long flags = 0;
+	u32 val;
+
+	value = multiplier_get_val(rate, parent_rate, multiplier->table,
+				multiplier->width, multiplier->flags);
+
+	if (multiplier->lock)
+		spin_lock_irqsave(multiplier->lock, flags);
+
+	if (multiplier->flags & CLK_MULTIPLIER_HIWORD_MASK) {
+		val = mult_mask(multiplier->width) << (multiplier->shift + 16);
+	} else {
+		val = clk_readl(multiplier->reg);
+		val &= ~(mult_mask(multiplier->width) << multiplier->shift);
+	}
+	val |= value << multiplier->shift;
+	clk_writel(val, multiplier->reg);
+
+	if (multiplier->lock)
+		spin_unlock_irqrestore(multiplier->lock, flags);
+
+	return 0;
+}
+
+const struct clk_ops clk_multiplier_ops = {
+	.recalc_rate = clk_multiplier_recalc_rate,
+	.round_rate = clk_multiplier_round_rate,
+	.set_rate = clk_multiplier_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_multiplier_ops);
+
+const struct clk_ops clk_multiplier_ro_ops = {
+	.recalc_rate = clk_multiplier_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_multiplier_ro_ops);
+
+static struct clk *_register_multiplier(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_multiplier_flags, const struct clk_mult_table *table,
+		spinlock_t *lock)
+{
+	struct clk_multiplier *mult;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	if (clk_multiplier_flags & CLK_MULTIPLIER_HIWORD_MASK) {
+		if (width + shift > 16) {
+			pr_warn("multiplier value exceeds LOWORD field\n");
+			return ERR_PTR(-EINVAL);
+		}
+	}
+
+	/* allocate the multiplier */
+	mult = kzalloc(sizeof(struct clk_multiplier), GFP_KERNEL);
+	if (!mult)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	if (clk_multiplier_flags & CLK_MULTIPLIER_READ_ONLY)
+		init.ops = &clk_multiplier_ro_ops;
+	else
+		init.ops = &clk_multiplier_ops;
+	init.flags = flags | CLK_IS_BASIC;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	/* struct clk_multiplier assignments */
+	mult->reg = reg;
+	mult->shift = shift;
+	mult->width = width;
+	mult->flags = clk_multiplier_flags;
+	mult->lock = lock;
+	mult->hw.init = &init;
+	mult->table = table;
+
+	/* register the clock */
+	clk = clk_register(dev, &mult->hw);
+
+	if (IS_ERR(clk))
+		kfree(mult);
+
+	return clk;
+}
+
+/**
+ * clk_register_multiplier - register a multiplier clock with the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust multiplier
+ * @shift: number of bits to shift the bitfield
+ * @width: width of the bitfield
+ * @clk_multiplier_flags: multiplier-specific flags for this clock
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_multiplier(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_multiplier_flags, spinlock_t *lock)
+{
+	return _register_multiplier(dev, name, parent_name, flags, reg, shift,
+			width, clk_multiplier_flags, NULL, lock);
+}
+EXPORT_SYMBOL_GPL(clk_register_multiplier);
+
+/**
+ * clk_register_multiplier_table - register a table based multiplier clock with
+ * the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust multiplier
+ * @shift: number of bits to shift the bitfield
+ * @width: width of the bitfield
+ * @clk_multiplier_flags: multiplier-specific flags for this clock
+ * @table: array of multiplier/value pairs ending with a mult set to 0
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_multiplier_table(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_multiplier_flags, const struct clk_mult_table *table,
+		spinlock_t *lock)
+{
+	return _register_multiplier(dev, name, parent_name, flags, reg, shift,
+			width, clk_multiplier_flags, table, lock);
+}
+EXPORT_SYMBOL_GPL(clk_register_multiplier_table);
+
+void clk_unregister_multiplier(struct clk *clk)
+{
+	struct clk_multiplier *mult;
+	struct clk_hw *hw;
+
+	hw = __clk_get_hw(clk);
+	if (!hw)
+		return;
+
+	mult = to_clk_multiplier(hw);
+
+	clk_unregister(clk);
+	kfree(mult);
+}
+EXPORT_SYMBOL_GPL(clk_unregister_multiplier);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index df69531..e35f6d7 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -383,6 +383,87 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
 		spinlock_t *lock);
 void clk_unregister_divider(struct clk *clk);
 
+
+struct clk_mult_table {
+	unsigned int	val;
+	unsigned int	mult;
+};
+
+/**
+ * struct clk_multiplier - adjustable multiplier clock
+ *
+ * @hw:		handle between common and hardware-specific interfaces
+ * @reg:	register containing the multiplier
+ * @shift:	shift to the multiplier bit field
+ * @width:	width of the multiplier bit field
+ * @table:	array of value/multiplier pairs, last entry should have mult = 0
+ * @lock:	register lock
+ *
+ * Clock with an adjustable multiplier affecting its output frequency.
+ * Implements .recalc_rate, .set_rate and .round_rate
+ *
+ * Flags:
+ * CLK_MULTIPLIER_ONE_BASED - by default the muliplier is the value read from
+ *	the register plus one.  If CLK_MULTIPLIER_ONE_BASED is set then the
+ *	multiplier is the raw value read from the register, with the value of
+ *	zero considered invalid, unless CLK_MULTIPLIER_ALLOW_ZERO is set.
+ * CLK_MULTIPLIER_POWER_OF_TWO - clock muliplier is 2 raised to the value
+ *	read from the hardware register
+ * CLK_MULTIPLIER_ALLOW_ZERO - Allow zero mulipliers.  For multipliers which
+ *	have CLK_MULTIPLIER_ONE_BASED set, it is possible to end up with a zero
+ *	muliplier.  Some hardware implementations gracefully handle this case
+ *	and allow a zero muliplier by not modifying their input clock
+ *	(multiply by one / bypass).
+ * CLK_MULTIPLIER_HIWORD_MASK - The multiplier settings are only in lower 16-bit
+ *	of this register, and mask of multiplier bits are in higher 16-bit of
+ *	this register.  While setting the multiplier bits, higher 16-bit should
+ *	also be updated to indicate changing multiplier bits.
+ * CLK_MULTIPLIER_READ_ONLY - The multiplier settings are preconfigured and
+ *	should not be changed by the clock framework.
+ * CLK_MULTIPLIER_MAX_AT_ZERO - For multipliers which are like
+ *	CLK_MULTIPLIER_ONE_BASED except when the value read from the register
+ *	is zero, the multiplier is 2^width of the field.
+ */
+struct clk_multiplier {
+	struct clk_hw	hw;
+	void __iomem	*reg;
+	u8		shift;
+	u8		width;
+	u8		flags;
+	const struct clk_mult_table	*table;
+	spinlock_t	*lock;
+};
+
+#define CLK_MULTIPLIER_ONE_BASED	BIT(0)
+#define CLK_MULTIPLIER_POWER_OF_TWO	BIT(1)
+#define CLK_MULTIPLIER_ALLOW_ZERO	BIT(2)
+#define CLK_MULTIPLIER_HIWORD_MASK	BIT(3)
+#define CLK_MULTIPLIER_READ_ONLY	BIT(4)
+#define CLK_MULTIPLIER_MAX_MULT_AT_ZERO	BIT(5)
+
+extern const struct clk_ops clk_multiplier_ops;
+
+unsigned long multiplier_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate, unsigned int val,
+		const struct clk_mult_table *table, unsigned long flags);
+long multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long *prate, const struct clk_mult_table *table,
+		u8 width, unsigned long flags);
+int multiplier_get_val(unsigned long rate, unsigned long parent_rate,
+		const struct clk_mult_table *table, u8 width,
+		unsigned long flags);
+
+struct clk *clk_register_multiplier(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_multiplier_flags, spinlock_t *lock);
+struct clk *clk_register_multiplier_table(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_multiplier_flags, const struct clk_mult_table *table,
+		spinlock_t *lock);
+void clk_unregister_multiplier(struct clk *clk);
+
 /**
  * struct clk_mux - multiplexer clock
  *
-- 
1.9.0.138.g2de3478

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] clk: multiplier: introduce configurable clock multiplier
  2015-05-22 20:53 [PATCH] clk: multiplier: introduce configurable clock multiplier Jim Quinlan
@ 2015-07-23 19:17 ` Michael Turquette
  0 siblings, 0 replies; 2+ messages in thread
From: Michael Turquette @ 2015-07-23 19:17 UTC (permalink / raw)
  To: Jim Quinlan, linux-clk; +Cc: bcm-kernel-feedback-list, Jim Quinlan

Quoting Jim Quinlan (2015-05-22 13:53:16)
> Broadcom STB SoCs come with a handful of configurable clock multipliers.
> Although care must be taken to change them, they are configurable
> nonetheless.  This commit introduces a configurable multiplier clock,
> and it is essentially the mirror of clk-divider.c.
> =

> Signed-off-by: Jim Quinlan <jim2101024@gmail.com>

Hi Jim,

Thanks for the patch. Stephen and I are still figuring out what to do
with these basic clock types. They represent a lot problems from a
maintainability point of view.

For now I'd like to hold off on merging this (nothing wrong with it
technically) until we figure out how to handle these common building
blocks a bit better.

Thanks,
Mike

> ---
>  drivers/clk/Makefile         |   1 +
>  drivers/clk/clk-multiplier.c | 440 +++++++++++++++++++++++++++++++++++++=
++++++
>  include/linux/clk-provider.h |  81 ++++++++
>  3 files changed, 522 insertions(+)
>  create mode 100644 drivers/clk/clk-multiplier.c
> =

> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 3d00c25..29682e4 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_HAVE_CLK)          +=3D clk-devres.o
>  obj-$(CONFIG_CLKDEV_LOOKUP)    +=3D clkdev.o
>  obj-$(CONFIG_COMMON_CLK)       +=3D clk.o
>  obj-$(CONFIG_COMMON_CLK)       +=3D clk-divider.o
> +obj-$(CONFIG_COMMON_CLK)       +=3D clk-multiplier.o
>  obj-$(CONFIG_COMMON_CLK)       +=3D clk-fixed-factor.o
>  obj-$(CONFIG_COMMON_CLK)       +=3D clk-fixed-rate.o
>  obj-$(CONFIG_COMMON_CLK)       +=3D clk-gate.o
> diff --git a/drivers/clk/clk-multiplier.c b/drivers/clk/clk-multiplier.c
> new file mode 100644
> index 0000000..de2d594
> --- /dev/null
> +++ b/drivers/clk/clk-multiplier.c
> @@ -0,0 +1,440 @@
> +/*
> + * Copyright (C) 2015 Jim Quinlan, Broadcom <jim2101024@gmail.com>
> + *
> + * 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.
> + *
> + * Adjustable multiplier clock implementation.  This is essentially
> + * the mirror of clk-divider.c.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +#include <linux/log2.h>
> +
> +/*
> + * DOC: basic adjustable multiplier clock that cannot gate
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare only ensures that parents are prepared
> + * enable - clk_enable only ensures that parents are enabled
> + * rate - rate is adjustable.  clk->rate =3D (parent->rate * multiplier)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define to_clk_multiplier(_hw) container_of(_hw, struct clk_multiplier, =
hw)
> +
> +#define mult_mask(width)       ((1 << (width)) - 1)
> +
> +static unsigned int _get_table_maxmult(const struct clk_mult_table *tabl=
e)
> +{
> +       unsigned int maxmult =3D 0;
> +       const struct clk_mult_table *clkt;
> +
> +       for (clkt =3D table; clkt->mult; clkt++)
> +               if (clkt->mult > maxmult)
> +                       maxmult =3D clkt->mult;
> +       return maxmult;
> +}
> +
> +static unsigned int _get_table_minmult(const struct clk_mult_table *tabl=
e)
> +{
> +       unsigned int minmult =3D UINT_MAX;
> +       const struct clk_mult_table *clkt;
> +
> +       for (clkt =3D table; clkt->mult; clkt++)
> +               if (clkt->mult < minmult)
> +                       minmult =3D clkt->mult;
> +       return minmult;
> +}
> +
> +static unsigned int _get_maxmult(const struct clk_mult_table *table, u8 =
width,
> +                                unsigned long flags)
> +{
> +       if (flags & CLK_MULTIPLIER_ONE_BASED)
> +               return mult_mask(width);
> +       if (flags & CLK_MULTIPLIER_POWER_OF_TWO)
> +               return 1 << mult_mask(width);
> +       if (table)
> +               return _get_table_maxmult(table);
> +       return mult_mask(width) + 1;
> +}
> +
> +static unsigned int _get_minmult(const struct clk_mult_table *table)
> +{
> +       if (table)
> +               return _get_table_minmult(table);
> +       return 1;
> +}
> +
> +static unsigned int _get_table_mult(const struct clk_mult_table *table,
> +                                   unsigned int val)
> +{
> +       const struct clk_mult_table *clkt;
> +
> +       for (clkt =3D table; clkt->mult; clkt++)
> +               if (clkt->val =3D=3D val)
> +                       return clkt->mult;
> +       return 0;
> +}
> +
> +static unsigned int _get_mult(const struct clk_mult_table *table,
> +                             u8 width, unsigned int val, unsigned long f=
lags)
> +{
> +       if (flags & CLK_MULTIPLIER_ONE_BASED)
> +               return val;
> +       if (flags & CLK_MULTIPLIER_POWER_OF_TWO)
> +               return 1 << val;
> +       if (flags & CLK_MULTIPLIER_MAX_MULT_AT_ZERO)
> +               return val ? val : mult_mask(width) + 1;
> +       if (table)
> +               return _get_table_mult(table, val);
> +       return val + 1;
> +}
> +
> +static unsigned int _get_table_val(const struct clk_mult_table *table,
> +                                  unsigned int mult)
> +{
> +       const struct clk_mult_table *clkt;
> +
> +       for (clkt =3D table; clkt->mult; clkt++)
> +               if (clkt->mult =3D=3D mult)
> +                       return clkt->val;
> +       return 0;
> +}
> +
> +static unsigned int _get_val(const struct clk_mult_table *table,
> +                            u8 width, unsigned int mult, unsigned long f=
lags)
> +{
> +       if (flags & CLK_MULTIPLIER_ONE_BASED)
> +               return mult;
> +       if (flags & CLK_MULTIPLIER_POWER_OF_TWO)
> +               return __ffs(mult);
> +       if (flags & CLK_MULTIPLIER_MAX_MULT_AT_ZERO)
> +               return (mult =3D=3D mult_mask(width) + 1)
> +                       ? 0 : mult;
> +       if (table)
> +               return  _get_table_val(table, mult);
> +       return mult - 1;
> +}
> +
> +unsigned long multiplier_recalc_rate(struct clk_hw *hw,
> +                                    unsigned long parent_rate,
> +                                    unsigned int val,
> +                                    const struct clk_mult_table *table,
> +                                    unsigned long flags)
> +{
> +       struct clk_multiplier *multiplier =3D to_clk_multiplier(hw);
> +       unsigned int mult;
> +
> +       mult =3D _get_mult(table, multiplier->width, val, flags);
> +       if (!mult) {
> +               WARN(!(flags & CLK_MULTIPLIER_ALLOW_ZERO),
> +                       "%s: Zero multiplier and CLK_MULTIPLIER_ALLOW_ZER=
O not set\n",
> +                       __clk_get_name(hw->clk));
> +               return parent_rate;
> +       }
> +
> +       return parent_rate * mult;
> +}
> +EXPORT_SYMBOL_GPL(multiplier_recalc_rate);
> +
> +static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw,
> +               unsigned long parent_rate)
> +{
> +       struct clk_multiplier *multiplier =3D to_clk_multiplier(hw);
> +       unsigned int val;
> +
> +       val =3D clk_readl(multiplier->reg) >> multiplier->shift;
> +       val &=3D mult_mask(multiplier->width);
> +
> +       return multiplier_recalc_rate(hw, parent_rate, val, multiplier->t=
able,
> +                                  multiplier->flags);
> +}
> +
> +static bool _is_valid_table_mult(const struct clk_mult_table *table,
> +                                                        unsigned int mul=
t)
> +{
> +       const struct clk_mult_table *clkt;
> +
> +       for (clkt =3D table; clkt->mult; clkt++)
> +               if (clkt->mult =3D=3D mult)
> +                       return true;
> +       return false;
> +}
> +
> +static bool _is_valid_mult(const struct clk_mult_table *table,
> +                          unsigned int mult, unsigned long flags)
> +{
> +       if (flags & CLK_MULTIPLIER_POWER_OF_TWO)
> +               return is_power_of_2(mult);
> +       if (table)
> +               return _is_valid_table_mult(table, mult);
> +       return true;
> +}
> +
> +static int clk_multiplier_bestmult(struct clk_hw *hw, unsigned long rate,
> +                              unsigned long *best_parent_rate,
> +                              const struct clk_mult_table *table, u8 wid=
th,
> +                              unsigned long flags)
> +{
> +       int i, bestmult =3D 0;
> +       unsigned long parent_rate, best =3D 0, now, maxmult, minmult;
> +       unsigned long parent_rate_saved =3D *best_parent_rate;
> +
> +       if (!rate)
> +               rate =3D 1;
> +
> +       minmult =3D _get_minmult(table);
> +       maxmult =3D _get_maxmult(table, width, flags);
> +
> +       if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
> +               parent_rate =3D *best_parent_rate;
> +               bestmult =3D rate / parent_rate;
> +               bestmult =3D bestmult =3D=3D 0 ? minmult : bestmult;
> +               bestmult =3D bestmult > maxmult ? maxmult : bestmult;
> +               return bestmult;
> +       }
> +
> +       /*
> +        * The maximum multiplier we can use without overflowing
> +        * unsigned long in rate * i below
> +        */
> +       maxmult =3D min(ULONG_MAX / parent_rate_saved, maxmult);
> +
> +       for (i =3D 1; i <=3D maxmult; i++) {
> +               if (!_is_valid_mult(table, i, flags))
> +                       continue;
> +               if (rate =3D=3D parent_rate_saved * i) {
> +                       /*
> +                        * It's the most ideal case if the requested rate=
 can be
> +                        * multiplied from parent clock without needing to
> +                        * change the parent rate, so return the multipli=
er
> +                        * immediately.
> +                        */
> +                       *best_parent_rate =3D parent_rate_saved;
> +                       return i;
> +               }
> +               parent_rate =3D __clk_round_rate(__clk_get_parent(hw->clk=
),
> +                                              rate / i);
> +               now =3D parent_rate * i;
> +               if (now <=3D rate && now > best) {
> +                       bestmult =3D i;
> +                       best =3D now;
> +                       *best_parent_rate =3D parent_rate;
> +               }
> +       }
> +
> +       if (!bestmult) {
> +               bestmult =3D _get_minmult(table);
> +               *best_parent_rate
> +                       =3D __clk_round_rate(__clk_get_parent(hw->clk), 1=
);
> +       }
> +
> +       return bestmult;
> +}
> +
> +long multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
> +                          unsigned long *prate,
> +                          const struct clk_mult_table *table,
> +                          u8 width, unsigned long flags)
> +{
> +       int mult;
> +
> +       mult =3D clk_multiplier_bestmult(hw, rate, prate, table, width, f=
lags);
> +
> +       return *prate * mult;
> +}
> +EXPORT_SYMBOL_GPL(multiplier_round_rate);
> +
> +static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long r=
ate,
> +                               unsigned long *prate)
> +{
> +       struct clk_multiplier *multiplier =3D to_clk_multiplier(hw);
> +       int bestmult;
> +
> +       /* if read only, just return current value */
> +       if (multiplier->flags & CLK_MULTIPLIER_READ_ONLY) {
> +               bestmult =3D readl(multiplier->reg) >> multiplier->shift;
> +               bestmult &=3D mult_mask(multiplier->width);
> +               bestmult =3D _get_mult(multiplier->table, multiplier->wid=
th,
> +                                    bestmult, multiplier->flags);
> +               if ((__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT))
> +                       *prate =3D __clk_round_rate(__clk_get_parent(hw->=
clk),
> +                                                 rate);
> +               return *prate * bestmult;
> +       }
> +
> +       return multiplier_round_rate(hw, rate, prate, multiplier->table,
> +                                 multiplier->width, multiplier->flags);
> +}
> +
> +int multiplier_get_val(unsigned long rate, unsigned long parent_rate,
> +                   const struct clk_mult_table *table, u8 width,
> +                   unsigned long flags)
> +{
> +       unsigned int mult, value;
> +
> +       mult =3D rate / parent_rate;
> +       value =3D _get_val(table, width, mult, flags);
> +       return min_t(unsigned int, value, mult_mask(width));
> +}
> +EXPORT_SYMBOL_GPL(multiplier_get_val);
> +
> +static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
> +                               unsigned long parent_rate)
> +{
> +       struct clk_multiplier *multiplier =3D to_clk_multiplier(hw);
> +       unsigned int value;
> +       unsigned long flags =3D 0;
> +       u32 val;
> +
> +       value =3D multiplier_get_val(rate, parent_rate, multiplier->table,
> +                               multiplier->width, multiplier->flags);
> +
> +       if (multiplier->lock)
> +               spin_lock_irqsave(multiplier->lock, flags);
> +
> +       if (multiplier->flags & CLK_MULTIPLIER_HIWORD_MASK) {
> +               val =3D mult_mask(multiplier->width) << (multiplier->shif=
t + 16);
> +       } else {
> +               val =3D clk_readl(multiplier->reg);
> +               val &=3D ~(mult_mask(multiplier->width) << multiplier->sh=
ift);
> +       }
> +       val |=3D value << multiplier->shift;
> +       clk_writel(val, multiplier->reg);
> +
> +       if (multiplier->lock)
> +               spin_unlock_irqrestore(multiplier->lock, flags);
> +
> +       return 0;
> +}
> +
> +const struct clk_ops clk_multiplier_ops =3D {
> +       .recalc_rate =3D clk_multiplier_recalc_rate,
> +       .round_rate =3D clk_multiplier_round_rate,
> +       .set_rate =3D clk_multiplier_set_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_multiplier_ops);
> +
> +const struct clk_ops clk_multiplier_ro_ops =3D {
> +       .recalc_rate =3D clk_multiplier_recalc_rate,
> +};
> +EXPORT_SYMBOL_GPL(clk_multiplier_ro_ops);
> +
> +static struct clk *_register_multiplier(struct device *dev, const char *=
name,
> +               const char *parent_name, unsigned long flags,
> +               void __iomem *reg, u8 shift, u8 width,
> +               u8 clk_multiplier_flags, const struct clk_mult_table *tab=
le,
> +               spinlock_t *lock)
> +{
> +       struct clk_multiplier *mult;
> +       struct clk *clk;
> +       struct clk_init_data init;
> +
> +       if (clk_multiplier_flags & CLK_MULTIPLIER_HIWORD_MASK) {
> +               if (width + shift > 16) {
> +                       pr_warn("multiplier value exceeds LOWORD field\n"=
);
> +                       return ERR_PTR(-EINVAL);
> +               }
> +       }
> +
> +       /* allocate the multiplier */
> +       mult =3D kzalloc(sizeof(struct clk_multiplier), GFP_KERNEL);
> +       if (!mult)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name =3D name;
> +       if (clk_multiplier_flags & CLK_MULTIPLIER_READ_ONLY)
> +               init.ops =3D &clk_multiplier_ro_ops;
> +       else
> +               init.ops =3D &clk_multiplier_ops;
> +       init.flags =3D flags | CLK_IS_BASIC;
> +       init.parent_names =3D (parent_name ? &parent_name : NULL);
> +       init.num_parents =3D (parent_name ? 1 : 0);
> +
> +       /* struct clk_multiplier assignments */
> +       mult->reg =3D reg;
> +       mult->shift =3D shift;
> +       mult->width =3D width;
> +       mult->flags =3D clk_multiplier_flags;
> +       mult->lock =3D lock;
> +       mult->hw.init =3D &init;
> +       mult->table =3D table;
> +
> +       /* register the clock */
> +       clk =3D clk_register(dev, &mult->hw);
> +
> +       if (IS_ERR(clk))
> +               kfree(mult);
> +
> +       return clk;
> +}
> +
> +/**
> + * clk_register_multiplier - register a multiplier clock with the clock =
framework
> + * @dev: device registering this clock
> + * @name: name of this clock
> + * @parent_name: name of clock's parent
> + * @flags: framework-specific flags
> + * @reg: register address to adjust multiplier
> + * @shift: number of bits to shift the bitfield
> + * @width: width of the bitfield
> + * @clk_multiplier_flags: multiplier-specific flags for this clock
> + * @lock: shared register lock for this clock
> + */
> +struct clk *clk_register_multiplier(struct device *dev, const char *name,
> +               const char *parent_name, unsigned long flags,
> +               void __iomem *reg, u8 shift, u8 width,
> +               u8 clk_multiplier_flags, spinlock_t *lock)
> +{
> +       return _register_multiplier(dev, name, parent_name, flags, reg, s=
hift,
> +                       width, clk_multiplier_flags, NULL, lock);
> +}
> +EXPORT_SYMBOL_GPL(clk_register_multiplier);
> +
> +/**
> + * clk_register_multiplier_table - register a table based multiplier clo=
ck with
> + * the clock framework
> + * @dev: device registering this clock
> + * @name: name of this clock
> + * @parent_name: name of clock's parent
> + * @flags: framework-specific flags
> + * @reg: register address to adjust multiplier
> + * @shift: number of bits to shift the bitfield
> + * @width: width of the bitfield
> + * @clk_multiplier_flags: multiplier-specific flags for this clock
> + * @table: array of multiplier/value pairs ending with a mult set to 0
> + * @lock: shared register lock for this clock
> + */
> +struct clk *clk_register_multiplier_table(struct device *dev, const char=
 *name,
> +               const char *parent_name, unsigned long flags,
> +               void __iomem *reg, u8 shift, u8 width,
> +               u8 clk_multiplier_flags, const struct clk_mult_table *tab=
le,
> +               spinlock_t *lock)
> +{
> +       return _register_multiplier(dev, name, parent_name, flags, reg, s=
hift,
> +                       width, clk_multiplier_flags, table, lock);
> +}
> +EXPORT_SYMBOL_GPL(clk_register_multiplier_table);
> +
> +void clk_unregister_multiplier(struct clk *clk)
> +{
> +       struct clk_multiplier *mult;
> +       struct clk_hw *hw;
> +
> +       hw =3D __clk_get_hw(clk);
> +       if (!hw)
> +               return;
> +
> +       mult =3D to_clk_multiplier(hw);
> +
> +       clk_unregister(clk);
> +       kfree(mult);
> +}
> +EXPORT_SYMBOL_GPL(clk_unregister_multiplier);
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index df69531..e35f6d7 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -383,6 +383,87 @@ struct clk *clk_register_divider_table(struct device=
 *dev, const char *name,
>                 spinlock_t *lock);
>  void clk_unregister_divider(struct clk *clk);
>  =

> +
> +struct clk_mult_table {
> +       unsigned int    val;
> +       unsigned int    mult;
> +};
> +
> +/**
> + * struct clk_multiplier - adjustable multiplier clock
> + *
> + * @hw:                handle between common and hardware-specific inter=
faces
> + * @reg:       register containing the multiplier
> + * @shift:     shift to the multiplier bit field
> + * @width:     width of the multiplier bit field
> + * @table:     array of value/multiplier pairs, last entry should have m=
ult =3D 0
> + * @lock:      register lock
> + *
> + * Clock with an adjustable multiplier affecting its output frequency.
> + * Implements .recalc_rate, .set_rate and .round_rate
> + *
> + * Flags:
> + * CLK_MULTIPLIER_ONE_BASED - by default the muliplier is the value read=
 from
> + *     the register plus one.  If CLK_MULTIPLIER_ONE_BASED is set then t=
he
> + *     multiplier is the raw value read from the register, with the valu=
e of
> + *     zero considered invalid, unless CLK_MULTIPLIER_ALLOW_ZERO is set.
> + * CLK_MULTIPLIER_POWER_OF_TWO - clock muliplier is 2 raised to the value
> + *     read from the hardware register
> + * CLK_MULTIPLIER_ALLOW_ZERO - Allow zero mulipliers.  For multipliers w=
hich
> + *     have CLK_MULTIPLIER_ONE_BASED set, it is possible to end up with =
a zero
> + *     muliplier.  Some hardware implementations gracefully handle this =
case
> + *     and allow a zero muliplier by not modifying their input clock
> + *     (multiply by one / bypass).
> + * CLK_MULTIPLIER_HIWORD_MASK - The multiplier settings are only in lowe=
r 16-bit
> + *     of this register, and mask of multiplier bits are in higher 16-bi=
t of
> + *     this register.  While setting the multiplier bits, higher 16-bit =
should
> + *     also be updated to indicate changing multiplier bits.
> + * CLK_MULTIPLIER_READ_ONLY - The multiplier settings are preconfigured =
and
> + *     should not be changed by the clock framework.
> + * CLK_MULTIPLIER_MAX_AT_ZERO - For multipliers which are like
> + *     CLK_MULTIPLIER_ONE_BASED except when the value read from the regi=
ster
> + *     is zero, the multiplier is 2^width of the field.
> + */
> +struct clk_multiplier {
> +       struct clk_hw   hw;
> +       void __iomem    *reg;
> +       u8              shift;
> +       u8              width;
> +       u8              flags;
> +       const struct clk_mult_table     *table;
> +       spinlock_t      *lock;
> +};
> +
> +#define CLK_MULTIPLIER_ONE_BASED       BIT(0)
> +#define CLK_MULTIPLIER_POWER_OF_TWO    BIT(1)
> +#define CLK_MULTIPLIER_ALLOW_ZERO      BIT(2)
> +#define CLK_MULTIPLIER_HIWORD_MASK     BIT(3)
> +#define CLK_MULTIPLIER_READ_ONLY       BIT(4)
> +#define CLK_MULTIPLIER_MAX_MULT_AT_ZERO        BIT(5)
> +
> +extern const struct clk_ops clk_multiplier_ops;
> +
> +unsigned long multiplier_recalc_rate(struct clk_hw *hw,
> +               unsigned long parent_rate, unsigned int val,
> +               const struct clk_mult_table *table, unsigned long flags);
> +long multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
> +               unsigned long *prate, const struct clk_mult_table *table,
> +               u8 width, unsigned long flags);
> +int multiplier_get_val(unsigned long rate, unsigned long parent_rate,
> +               const struct clk_mult_table *table, u8 width,
> +               unsigned long flags);
> +
> +struct clk *clk_register_multiplier(struct device *dev, const char *name,
> +               const char *parent_name, unsigned long flags,
> +               void __iomem *reg, u8 shift, u8 width,
> +               u8 clk_multiplier_flags, spinlock_t *lock);
> +struct clk *clk_register_multiplier_table(struct device *dev, const char=
 *name,
> +               const char *parent_name, unsigned long flags,
> +               void __iomem *reg, u8 shift, u8 width,
> +               u8 clk_multiplier_flags, const struct clk_mult_table *tab=
le,
> +               spinlock_t *lock);
> +void clk_unregister_multiplier(struct clk *clk);
> +
>  /**
>   * struct clk_mux - multiplexer clock
>   *
> -- =

> 1.9.0.138.g2de3478
>=20

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2015-07-23 19:17 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-22 20:53 [PATCH] clk: multiplier: introduce configurable clock multiplier Jim Quinlan
2015-07-23 19:17 ` Michael Turquette

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.