clk-fixed-factor.c 6.06 KB
Newer Older
Sascha Hauer's avatar
Sascha Hauer committed
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * 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>
14
#include <linux/of.h>
15
#include <linux/platform_device.h>
Sascha Hauer's avatar
Sascha Hauer committed
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

/*
 * 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
 */

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);
31
	unsigned long long int rate;
Sascha Hauer's avatar
Sascha Hauer committed
32

33 34 35
	rate = (unsigned long long int)parent_rate * fix->mult;
	do_div(rate, fix->div);
	return (unsigned long)rate;
Sascha Hauer's avatar
Sascha Hauer committed
36 37 38 39 40 41 42
}

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);

43
	if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
Sascha Hauer's avatar
Sascha Hauer committed
44 45 46
		unsigned long best_parent;

		best_parent = (rate / fix->mult) * fix->div;
47
		*prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
Sascha Hauer's avatar
Sascha Hauer committed
48 49 50 51 52 53 54 55
	}

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

static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long parent_rate)
{
56 57 58 59 60 61
	/*
	 * We must report success but we can do so unconditionally because
	 * clk_factor_round_rate returns values that ensure this call is a
	 * nop.
	 */

Sascha Hauer's avatar
Sascha Hauer committed
62 63 64
	return 0;
}

65
const struct clk_ops clk_fixed_factor_ops = {
Sascha Hauer's avatar
Sascha Hauer committed
66 67 68 69 70 71
	.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);

72 73
struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
		const char *name, const char *parent_name, unsigned long flags,
Sascha Hauer's avatar
Sascha Hauer committed
74 75 76 77
		unsigned int mult, unsigned int div)
{
	struct clk_fixed_factor *fix;
	struct clk_init_data init;
78 79
	struct clk_hw *hw;
	int ret;
Sascha Hauer's avatar
Sascha Hauer committed
80 81

	fix = kmalloc(sizeof(*fix), GFP_KERNEL);
82
	if (!fix)
Sascha Hauer's avatar
Sascha Hauer committed
83 84 85 86 87 88 89 90 91
		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;
92
	init.flags = flags | CLK_IS_BASIC;
Sascha Hauer's avatar
Sascha Hauer committed
93 94 95
	init.parent_names = &parent_name;
	init.num_parents = 1;

96 97 98
	hw = &fix->hw;
	ret = clk_hw_register(dev, hw);
	if (ret) {
Sascha Hauer's avatar
Sascha Hauer committed
99
		kfree(fix);
100 101
		hw = ERR_PTR(ret);
	}
Sascha Hauer's avatar
Sascha Hauer committed
102

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
	return hw;
}
EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor);

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_hw *hw;

	hw = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
					  div);
	if (IS_ERR(hw))
		return ERR_CAST(hw);
	return hw->clk;
Sascha Hauer's avatar
Sascha Hauer committed
118
}
119
EXPORT_SYMBOL_GPL(clk_register_fixed_factor);
120 121 122 123 124 125 126 127 128 129 130 131 132

void clk_unregister_fixed_factor(struct clk *clk)
{
	struct clk_hw *hw;

	hw = __clk_get_hw(clk);
	if (!hw)
		return;

	clk_unregister(clk);
	kfree(to_clk_fixed_factor(hw));
}
EXPORT_SYMBOL_GPL(clk_unregister_fixed_factor);
133 134 135 136 137 138 139 140 141 142 143

void clk_hw_unregister_fixed_factor(struct clk_hw *hw)
{
	struct clk_fixed_factor *fix;

	fix = to_clk_fixed_factor(hw);

	clk_hw_unregister(hw);
	kfree(fix);
}
EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_factor);
144

145
#ifdef CONFIG_OF
146 147 148 149 150
static const struct of_device_id set_rate_parent_matches[] = {
	{ .compatible = "allwinner,sun4i-a10-pll3-2x-clk" },
	{ /* Sentinel */ },
};

151
static struct clk *_of_fixed_factor_clk_setup(struct device_node *node)
152 153 154 155
{
	struct clk *clk;
	const char *clk_name = node->name;
	const char *parent_name;
156
	unsigned long flags = 0;
157
	u32 div, mult;
158
	int ret;
159 160

	if (of_property_read_u32(node, "clock-div", &div)) {
161 162
		pr_err("%s Fixed factor clock <%pOFn> must have a clock-div property\n",
			__func__, node);
163
		return ERR_PTR(-EIO);
164 165 166
	}

	if (of_property_read_u32(node, "clock-mult", &mult)) {
167 168
		pr_err("%s Fixed factor clock <%pOFn> must have a clock-mult property\n",
			__func__, node);
169
		return ERR_PTR(-EIO);
170 171 172 173 174
	}

	of_property_read_string(node, "clock-output-names", &clk_name);
	parent_name = of_clk_get_parent_name(node, 0);

175 176 177 178
	if (of_match_node(set_rate_parent_matches, node))
		flags |= CLK_SET_RATE_PARENT;

	clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags,
179
					mult, div);
180 181 182 183 184 185 186
	if (IS_ERR(clk)) {
		/*
		 * If parent clock is not registered, registration would fail.
		 * Clear OF_POPULATED flag so that clock registration can be
		 * attempted again from probe function.
		 */
		of_node_clear_flag(node, OF_POPULATED);
187
		return clk;
188
	}
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

	ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
	if (ret) {
		clk_unregister(clk);
		return ERR_PTR(ret);
	}

	return clk;
}

/**
 * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
 */
void __init of_fixed_factor_clk_setup(struct device_node *node)
{
	_of_fixed_factor_clk_setup(node);
205 206 207
}
CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
		of_fixed_factor_clk_setup);
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249

static int of_fixed_factor_clk_remove(struct platform_device *pdev)
{
	struct clk *clk = platform_get_drvdata(pdev);

	clk_unregister_fixed_factor(clk);

	return 0;
}

static int of_fixed_factor_clk_probe(struct platform_device *pdev)
{
	struct clk *clk;

	/*
	 * This function is not executed when of_fixed_factor_clk_setup
	 * succeeded.
	 */
	clk = _of_fixed_factor_clk_setup(pdev->dev.of_node);
	if (IS_ERR(clk))
		return PTR_ERR(clk);

	platform_set_drvdata(pdev, clk);

	return 0;
}

static const struct of_device_id of_fixed_factor_clk_ids[] = {
	{ .compatible = "fixed-factor-clock" },
	{ }
};
MODULE_DEVICE_TABLE(of, of_fixed_factor_clk_ids);

static struct platform_driver of_fixed_factor_clk_driver = {
	.driver = {
		.name = "of_fixed_factor_clk",
		.of_match_table = of_fixed_factor_clk_ids,
	},
	.probe = of_fixed_factor_clk_probe,
	.remove = of_fixed_factor_clk_remove,
};
builtin_platform_driver(of_fixed_factor_clk_driver);
250
#endif