2 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
11 #include <linux/clk-provider.h>
12 #include <linux/clkdev.h>
13 #include <linux/clk/at91_pmc.h>
15 #include <linux/mfd/syscon.h>
16 #include <linux/regmap.h>
20 #define MASTER_PRES_MASK 0x7
21 #define MASTER_PRES_MAX MASTER_PRES_MASK
22 #define MASTER_DIV_SHIFT 8
23 #define MASTER_DIV_MASK 0x3
25 #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
29 struct regmap *regmap;
30 const struct clk_master_layout *layout;
31 const struct clk_master_characteristics *characteristics;
34 static inline bool clk_master_ready(struct regmap *regmap)
38 regmap_read(regmap, AT91_PMC_SR, &status);
40 return status & AT91_PMC_MCKRDY ? 1 : 0;
43 static int clk_master_prepare(struct clk_hw *hw)
45 struct clk_master *master = to_clk_master(hw);
47 while (!clk_master_ready(master->regmap))
53 static int clk_master_is_prepared(struct clk_hw *hw)
55 struct clk_master *master = to_clk_master(hw);
57 return clk_master_ready(master->regmap);
60 static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
61 unsigned long parent_rate)
65 unsigned long rate = parent_rate;
66 struct clk_master *master = to_clk_master(hw);
67 const struct clk_master_layout *layout = master->layout;
68 const struct clk_master_characteristics *characteristics =
69 master->characteristics;
72 regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
75 pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
76 div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
78 if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
83 rate /= characteristics->divisors[div];
85 if (rate < characteristics->output.min)
86 pr_warn("master clk is underclocked");
87 else if (rate > characteristics->output.max)
88 pr_warn("master clk is overclocked");
93 static u8 clk_master_get_parent(struct clk_hw *hw)
95 struct clk_master *master = to_clk_master(hw);
98 regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
100 return mckr & AT91_PMC_CSS;
103 static const struct clk_ops master_ops = {
104 .prepare = clk_master_prepare,
105 .is_prepared = clk_master_is_prepared,
106 .recalc_rate = clk_master_recalc_rate,
107 .get_parent = clk_master_get_parent,
110 struct clk_hw * __init
111 at91_clk_register_master(struct regmap *regmap,
112 const char *name, int num_parents,
113 const char **parent_names,
114 const struct clk_master_layout *layout,
115 const struct clk_master_characteristics *characteristics)
117 struct clk_master *master;
118 struct clk_init_data init;
122 if (!name || !num_parents || !parent_names)
123 return ERR_PTR(-EINVAL);
125 master = kzalloc(sizeof(*master), GFP_KERNEL);
127 return ERR_PTR(-ENOMEM);
130 init.ops = &master_ops;
131 init.parent_names = parent_names;
132 init.num_parents = num_parents;
135 master->hw.init = &init;
136 master->layout = layout;
137 master->characteristics = characteristics;
138 master->regmap = regmap;
141 ret = clk_hw_register(NULL, &master->hw);
151 const struct clk_master_layout at91rm9200_master_layout = {
156 const struct clk_master_layout at91sam9x5_master_layout = {