Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0948f59 authored by Sascha Hauer's avatar Sascha Hauer Committed by Mike Turquette
Browse files

clk: add a fixed factor clock



Having fixed factors/dividers in hardware is a common pattern, so
add a basic clock type doing this. It basically describes a fixed
factor clock using a nominator and a denominator.

Signed-off-by: default avatarSascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: default avatarViresh Kumar <viresh.kumar@st.com>
Tested-by: default avatarShawn Guo <shawn.guo@linaro.org>
[mturquette@linaro.org: constify parent_names in static init macro]
[mturquette@linaro.org: copy/paste bug from mux in static init macro]
[mturquette@linaro.org: fix error handling in clk_register_fixed_factor]
[mturquette@linaro.org: improve division accuracy; thanks to Saravana]
Signed-off-by: default avatarMike Turquette <mturquette@linaro.org>
parent 31df9db9
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line

obj-$(CONFIG_CLKDEV_LOOKUP)	+= clkdev.o
obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-fixed-rate.o clk-gate.o \
				   clk-mux.o clk-divider.o
				   clk-mux.o clk-divider.o clk-fixed-factor.o
+95 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
 *
 * 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.
 *
 * Standard functionality for the common clock API.
 */
#include <linux/module.h>
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include <linux/err.h>

/*
 * DOC: basic fixed multiplier and divider 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 fixed.  clk->rate = parent->rate / div * mult
 * parent - fixed parent.  No clk_set_parent support
 */

#define to_clk_fixed_factor(_hw) container_of(_hw, struct clk_fixed_factor, hw)

static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
		unsigned long parent_rate)
{
	struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);

	return parent_rate * fix->mult / fix->div;
}

static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long *prate)
{
	struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);

	if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) {
		unsigned long best_parent;

		best_parent = (rate / fix->mult) * fix->div;
		*prate = __clk_round_rate(__clk_get_parent(hw->clk),
				best_parent);
	}

	return (*prate / fix->div) * fix->mult;
}

static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long parent_rate)
{
	return 0;
}

struct clk_ops clk_fixed_factor_ops = {
	.round_rate = clk_factor_round_rate,
	.set_rate = clk_factor_set_rate,
	.recalc_rate = clk_factor_recalc_rate,
};
EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);

struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		unsigned int mult, unsigned int div)
{
	struct clk_fixed_factor *fix;
	struct clk_init_data init;
	struct clk *clk;

	fix = kmalloc(sizeof(*fix), GFP_KERNEL);
	if (!fix) {
		pr_err("%s: could not allocate fixed factor clk\n", __func__);
		return ERR_PTR(-ENOMEM);
	}

	/* struct clk_fixed_factor assignments */
	fix->mult = mult;
	fix->div = div;
	fix->hw.init = &init;

	init.name = name;
	init.ops = &clk_fixed_factor_ops;
	init.flags = flags;
	init.parent_names = &parent_name;
	init.num_parents = 1;

	clk = clk_register(dev, &fix->hw);

	if (IS_ERR(clk))
		kfree(fix);

	return clk;
}
+20 −0
Original line number Diff line number Diff line
@@ -143,6 +143,26 @@ struct clk {
	DEFINE_CLK(_name, clk_mux_ops, _flags, _parent_names,	\
			_parents);

#define DEFINE_CLK_FIXED_FACTOR(_name, _parent_name,		\
				_parent_ptr, _flags,		\
				_mult, _div)			\
	static struct clk _name;				\
	static const char *_name##_parent_names[] = {		\
		_parent_name,					\
	};							\
	static struct clk *_name##_parents[] = {		\
		_parent_ptr,					\
	};							\
	static struct clk_fixed_factor _name##_hw = {		\
		.hw = {						\
			.clk = &_name,				\
		},						\
		.mult = _mult,					\
		.div = _div,					\
	};							\
	DEFINE_CLK(_name, clk_fixed_factor_ops, _flags,		\
			_name##_parent_names, _name##_parents);

/**
 * __clk_init - initialize the data structures in a struct clk
 * @dev:	device initializing this clk, placeholder for now
+23 −0
Original line number Diff line number Diff line
@@ -276,6 +276,29 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
		void __iomem *reg, u8 shift, u8 width,
		u8 clk_mux_flags, spinlock_t *lock);

/**
 * struct clk_fixed_factor - fixed multiplier and divider clock
 *
 * @hw:		handle between common and hardware-specific interfaces
 * @mult:	multiplier
 * @div:	divider
 *
 * Clock with a fixed multiplier and divider. The output frequency is the
 * parent clock rate divided by div and multiplied by mult.
 * Implements .recalc_rate, .set_rate and .round_rate
 */

struct clk_fixed_factor {
	struct clk_hw	hw;
	unsigned int	mult;
	unsigned int	div;
};

extern struct clk_ops clk_fixed_factor_ops;
struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		unsigned int mult, unsigned int div);

/**
 * clk_register - allocate a new clock, register it and return an opaque cookie
 * @dev: device that is registering this clock