xref: /openbmc/linux/drivers/clk/at91/clk-master.c (revision e5be5370)
1 /*
2  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
3  *
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.
8  *
9  */
10 
11 #include <linux/clk-provider.h>
12 #include <linux/clkdev.h>
13 #include <linux/clk/at91_pmc.h>
14 #include <linux/of.h>
15 #include <linux/mfd/syscon.h>
16 #include <linux/regmap.h>
17 
18 #include "pmc.h"
19 
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
24 
25 #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
26 
27 struct clk_master {
28 	struct clk_hw hw;
29 	struct regmap *regmap;
30 	const struct clk_master_layout *layout;
31 	const struct clk_master_characteristics *characteristics;
32 	u32 mckr;
33 };
34 
35 static inline bool clk_master_ready(struct regmap *regmap)
36 {
37 	unsigned int status;
38 
39 	regmap_read(regmap, AT91_PMC_SR, &status);
40 
41 	return status & AT91_PMC_MCKRDY ? 1 : 0;
42 }
43 
44 static int clk_master_prepare(struct clk_hw *hw)
45 {
46 	struct clk_master *master = to_clk_master(hw);
47 
48 	while (!clk_master_ready(master->regmap))
49 		cpu_relax();
50 
51 	return 0;
52 }
53 
54 static int clk_master_is_prepared(struct clk_hw *hw)
55 {
56 	struct clk_master *master = to_clk_master(hw);
57 
58 	return clk_master_ready(master->regmap);
59 }
60 
61 static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
62 					    unsigned long parent_rate)
63 {
64 	u8 pres;
65 	u8 div;
66 	unsigned long rate = parent_rate;
67 	struct clk_master *master = to_clk_master(hw);
68 	const struct clk_master_layout *layout = master->layout;
69 	const struct clk_master_characteristics *characteristics =
70 						master->characteristics;
71 	unsigned int mckr;
72 
73 	regmap_read(master->regmap, master->layout->offset, &mckr);
74 	mckr &= layout->mask;
75 
76 	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
77 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
78 
79 	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
80 		rate /= 3;
81 	else
82 		rate >>= pres;
83 
84 	rate /= characteristics->divisors[div];
85 
86 	if (rate < characteristics->output.min)
87 		pr_warn("master clk is underclocked");
88 	else if (rate > characteristics->output.max)
89 		pr_warn("master clk is overclocked");
90 
91 	return rate;
92 }
93 
94 static u8 clk_master_get_parent(struct clk_hw *hw)
95 {
96 	struct clk_master *master = to_clk_master(hw);
97 	unsigned int mckr;
98 
99 	regmap_read(master->regmap, master->layout->offset, &mckr);
100 
101 	return mckr & AT91_PMC_CSS;
102 }
103 
104 static const struct clk_ops master_ops = {
105 	.prepare = clk_master_prepare,
106 	.is_prepared = clk_master_is_prepared,
107 	.recalc_rate = clk_master_recalc_rate,
108 	.get_parent = clk_master_get_parent,
109 };
110 
111 struct clk_hw * __init
112 at91_clk_register_master(struct regmap *regmap,
113 		const char *name, int num_parents,
114 		const char **parent_names,
115 		const struct clk_master_layout *layout,
116 		const struct clk_master_characteristics *characteristics)
117 {
118 	struct clk_master *master;
119 	struct clk_init_data init;
120 	struct clk_hw *hw;
121 	int ret;
122 
123 	if (!name || !num_parents || !parent_names)
124 		return ERR_PTR(-EINVAL);
125 
126 	master = kzalloc(sizeof(*master), GFP_KERNEL);
127 	if (!master)
128 		return ERR_PTR(-ENOMEM);
129 
130 	init.name = name;
131 	init.ops = &master_ops;
132 	init.parent_names = parent_names;
133 	init.num_parents = num_parents;
134 	init.flags = 0;
135 
136 	master->hw.init = &init;
137 	master->layout = layout;
138 	master->characteristics = characteristics;
139 	master->regmap = regmap;
140 
141 	hw = &master->hw;
142 	ret = clk_hw_register(NULL, &master->hw);
143 	if (ret) {
144 		kfree(master);
145 		hw = ERR_PTR(ret);
146 	}
147 
148 	return hw;
149 }
150 
151 const struct clk_master_layout at91rm9200_master_layout = {
152 	.mask = 0x31F,
153 	.pres_shift = 2,
154 	.offset = AT91_PMC_MCKR,
155 };
156 
157 const struct clk_master_layout at91sam9x5_master_layout = {
158 	.mask = 0x373,
159 	.pres_shift = 4,
160 	.offset = AT91_PMC_MCKR,
161 };
162