1 // SPDX-License-Identifier: GPL-2.0-or-later
6 #include <linux/clk-provider.h>
7 #include <linux/clkdev.h>
8 #include <linux/clk/at91_pmc.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/regmap.h>
15 #define MASTER_PRES_MASK 0x7
16 #define MASTER_PRES_MAX MASTER_PRES_MASK
17 #define MASTER_DIV_SHIFT 8
18 #define MASTER_DIV_MASK 0x3
20 #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
24 struct regmap *regmap;
25 const struct clk_master_layout *layout;
26 const struct clk_master_characteristics *characteristics;
30 static inline bool clk_master_ready(struct regmap *regmap)
34 regmap_read(regmap, AT91_PMC_SR, &status);
36 return status & AT91_PMC_MCKRDY ? 1 : 0;
39 static int clk_master_prepare(struct clk_hw *hw)
41 struct clk_master *master = to_clk_master(hw);
43 while (!clk_master_ready(master->regmap))
49 static int clk_master_is_prepared(struct clk_hw *hw)
51 struct clk_master *master = to_clk_master(hw);
53 return clk_master_ready(master->regmap);
56 static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
57 unsigned long parent_rate)
61 unsigned long rate = parent_rate;
62 struct clk_master *master = to_clk_master(hw);
63 const struct clk_master_layout *layout = master->layout;
64 const struct clk_master_characteristics *characteristics =
65 master->characteristics;
68 regmap_read(master->regmap, master->layout->offset, &mckr);
71 pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
72 div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
74 if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
79 rate /= characteristics->divisors[div];
81 if (rate < characteristics->output.min)
82 pr_warn("master clk is underclocked");
83 else if (rate > characteristics->output.max)
84 pr_warn("master clk is overclocked");
89 static u8 clk_master_get_parent(struct clk_hw *hw)
91 struct clk_master *master = to_clk_master(hw);
94 regmap_read(master->regmap, master->layout->offset, &mckr);
96 return mckr & AT91_PMC_CSS;
99 static const struct clk_ops master_ops = {
100 .prepare = clk_master_prepare,
101 .is_prepared = clk_master_is_prepared,
102 .recalc_rate = clk_master_recalc_rate,
103 .get_parent = clk_master_get_parent,
106 struct clk_hw * __init
107 at91_clk_register_master(struct regmap *regmap,
108 const char *name, int num_parents,
109 const char **parent_names,
110 const struct clk_master_layout *layout,
111 const struct clk_master_characteristics *characteristics)
113 struct clk_master *master;
114 struct clk_init_data init;
118 if (!name || !num_parents || !parent_names)
119 return ERR_PTR(-EINVAL);
121 master = kzalloc(sizeof(*master), GFP_KERNEL);
123 return ERR_PTR(-ENOMEM);
126 init.ops = &master_ops;
127 init.parent_names = parent_names;
128 init.num_parents = num_parents;
131 master->hw.init = &init;
132 master->layout = layout;
133 master->characteristics = characteristics;
134 master->regmap = regmap;
137 ret = clk_hw_register(NULL, &master->hw);
146 const struct clk_master_layout at91rm9200_master_layout = {
149 .offset = AT91_PMC_MCKR,
152 const struct clk_master_layout at91sam9x5_master_layout = {
155 .offset = AT91_PMC_MCKR,