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_SOURCE_MAX 4
22 #define MASTER_PRES_MASK 0x7
23 #define MASTER_PRES_MAX MASTER_PRES_MASK
24 #define MASTER_DIV_SHIFT 8
25 #define MASTER_DIV_MASK 0x3
27 struct clk_master_characteristics
{
28 struct clk_range output
;
33 struct clk_master_layout
{
38 #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
42 struct regmap
*regmap
;
43 const struct clk_master_layout
*layout
;
44 const struct clk_master_characteristics
*characteristics
;
47 static inline bool clk_master_ready(struct regmap
*regmap
)
51 regmap_read(regmap
, AT91_PMC_SR
, &status
);
53 return status
& AT91_PMC_MCKRDY
? 1 : 0;
56 static int clk_master_prepare(struct clk_hw
*hw
)
58 struct clk_master
*master
= to_clk_master(hw
);
60 while (!clk_master_ready(master
->regmap
))
66 static int clk_master_is_prepared(struct clk_hw
*hw
)
68 struct clk_master
*master
= to_clk_master(hw
);
70 return clk_master_ready(master
->regmap
);
73 static unsigned long clk_master_recalc_rate(struct clk_hw
*hw
,
74 unsigned long parent_rate
)
78 unsigned long rate
= parent_rate
;
79 struct clk_master
*master
= to_clk_master(hw
);
80 const struct clk_master_layout
*layout
= master
->layout
;
81 const struct clk_master_characteristics
*characteristics
=
82 master
->characteristics
;
85 regmap_read(master
->regmap
, AT91_PMC_MCKR
, &mckr
);
88 pres
= (mckr
>> layout
->pres_shift
) & MASTER_PRES_MASK
;
89 div
= (mckr
>> MASTER_DIV_SHIFT
) & MASTER_DIV_MASK
;
91 if (characteristics
->have_div3_pres
&& pres
== MASTER_PRES_MAX
)
96 rate
/= characteristics
->divisors
[div
];
98 if (rate
< characteristics
->output
.min
)
99 pr_warn("master clk is underclocked");
100 else if (rate
> characteristics
->output
.max
)
101 pr_warn("master clk is overclocked");
106 static u8
clk_master_get_parent(struct clk_hw
*hw
)
108 struct clk_master
*master
= to_clk_master(hw
);
111 regmap_read(master
->regmap
, AT91_PMC_MCKR
, &mckr
);
113 return mckr
& AT91_PMC_CSS
;
116 static const struct clk_ops master_ops
= {
117 .prepare
= clk_master_prepare
,
118 .is_prepared
= clk_master_is_prepared
,
119 .recalc_rate
= clk_master_recalc_rate
,
120 .get_parent
= clk_master_get_parent
,
123 static struct clk
* __init
124 at91_clk_register_master(struct regmap
*regmap
,
125 const char *name
, int num_parents
,
126 const char **parent_names
,
127 const struct clk_master_layout
*layout
,
128 const struct clk_master_characteristics
*characteristics
)
130 struct clk_master
*master
;
131 struct clk
*clk
= NULL
;
132 struct clk_init_data init
;
134 if (!name
|| !num_parents
|| !parent_names
)
135 return ERR_PTR(-EINVAL
);
137 master
= kzalloc(sizeof(*master
), GFP_KERNEL
);
139 return ERR_PTR(-ENOMEM
);
142 init
.ops
= &master_ops
;
143 init
.parent_names
= parent_names
;
144 init
.num_parents
= num_parents
;
147 master
->hw
.init
= &init
;
148 master
->layout
= layout
;
149 master
->characteristics
= characteristics
;
150 master
->regmap
= regmap
;
152 clk
= clk_register(NULL
, &master
->hw
);
161 static const struct clk_master_layout at91rm9200_master_layout
= {
166 static const struct clk_master_layout at91sam9x5_master_layout
= {
172 static struct clk_master_characteristics
* __init
173 of_at91_clk_master_get_characteristics(struct device_node
*np
)
175 struct clk_master_characteristics
*characteristics
;
177 characteristics
= kzalloc(sizeof(*characteristics
), GFP_KERNEL
);
178 if (!characteristics
)
181 if (of_at91_get_clk_range(np
, "atmel,clk-output-range", &characteristics
->output
))
182 goto out_free_characteristics
;
184 of_property_read_u32_array(np
, "atmel,clk-divisors",
185 characteristics
->divisors
, 4);
187 characteristics
->have_div3_pres
=
188 of_property_read_bool(np
, "atmel,master-clk-have-div3-pres");
190 return characteristics
;
192 out_free_characteristics
:
193 kfree(characteristics
);
198 of_at91_clk_master_setup(struct device_node
*np
,
199 const struct clk_master_layout
*layout
)
202 unsigned int num_parents
;
203 const char *parent_names
[MASTER_SOURCE_MAX
];
204 const char *name
= np
->name
;
205 struct clk_master_characteristics
*characteristics
;
206 struct regmap
*regmap
;
208 num_parents
= of_clk_get_parent_count(np
);
209 if (num_parents
== 0 || num_parents
> MASTER_SOURCE_MAX
)
212 of_clk_parent_fill(np
, parent_names
, num_parents
);
214 of_property_read_string(np
, "clock-output-names", &name
);
216 characteristics
= of_at91_clk_master_get_characteristics(np
);
217 if (!characteristics
)
220 regmap
= syscon_node_to_regmap(of_get_parent(np
));
224 clk
= at91_clk_register_master(regmap
, name
, num_parents
,
225 parent_names
, layout
,
228 goto out_free_characteristics
;
230 of_clk_add_provider(np
, of_clk_src_simple_get
, clk
);
233 out_free_characteristics
:
234 kfree(characteristics
);
237 static void __init
of_at91rm9200_clk_master_setup(struct device_node
*np
)
239 of_at91_clk_master_setup(np
, &at91rm9200_master_layout
);
241 CLK_OF_DECLARE(at91rm9200_clk_master
, "atmel,at91rm9200-clk-master",
242 of_at91rm9200_clk_master_setup
);
244 static void __init
of_at91sam9x5_clk_master_setup(struct device_node
*np
)
246 of_at91_clk_master_setup(np
, &at91sam9x5_master_layout
);
248 CLK_OF_DECLARE(at91sam9x5_clk_master
, "atmel,at91sam9x5-clk-master",
249 of_at91sam9x5_clk_master_setup
);