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_hw
* __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_init_data init
;
135 if (!name
|| !num_parents
|| !parent_names
)
136 return ERR_PTR(-EINVAL
);
138 master
= kzalloc(sizeof(*master
), GFP_KERNEL
);
140 return ERR_PTR(-ENOMEM
);
143 init
.ops
= &master_ops
;
144 init
.parent_names
= parent_names
;
145 init
.num_parents
= num_parents
;
148 master
->hw
.init
= &init
;
149 master
->layout
= layout
;
150 master
->characteristics
= characteristics
;
151 master
->regmap
= regmap
;
154 ret
= clk_hw_register(NULL
, &master
->hw
);
164 static const struct clk_master_layout at91rm9200_master_layout
= {
169 static const struct clk_master_layout at91sam9x5_master_layout
= {
175 static struct clk_master_characteristics
* __init
176 of_at91_clk_master_get_characteristics(struct device_node
*np
)
178 struct clk_master_characteristics
*characteristics
;
180 characteristics
= kzalloc(sizeof(*characteristics
), GFP_KERNEL
);
181 if (!characteristics
)
184 if (of_at91_get_clk_range(np
, "atmel,clk-output-range", &characteristics
->output
))
185 goto out_free_characteristics
;
187 of_property_read_u32_array(np
, "atmel,clk-divisors",
188 characteristics
->divisors
, 4);
190 characteristics
->have_div3_pres
=
191 of_property_read_bool(np
, "atmel,master-clk-have-div3-pres");
193 return characteristics
;
195 out_free_characteristics
:
196 kfree(characteristics
);
201 of_at91_clk_master_setup(struct device_node
*np
,
202 const struct clk_master_layout
*layout
)
205 unsigned int num_parents
;
206 const char *parent_names
[MASTER_SOURCE_MAX
];
207 const char *name
= np
->name
;
208 struct clk_master_characteristics
*characteristics
;
209 struct regmap
*regmap
;
211 num_parents
= of_clk_get_parent_count(np
);
212 if (num_parents
== 0 || num_parents
> MASTER_SOURCE_MAX
)
215 of_clk_parent_fill(np
, parent_names
, num_parents
);
217 of_property_read_string(np
, "clock-output-names", &name
);
219 characteristics
= of_at91_clk_master_get_characteristics(np
);
220 if (!characteristics
)
223 regmap
= syscon_node_to_regmap(of_get_parent(np
));
227 hw
= at91_clk_register_master(regmap
, name
, num_parents
,
228 parent_names
, layout
,
231 goto out_free_characteristics
;
233 of_clk_add_hw_provider(np
, of_clk_hw_simple_get
, hw
);
236 out_free_characteristics
:
237 kfree(characteristics
);
240 static void __init
of_at91rm9200_clk_master_setup(struct device_node
*np
)
242 of_at91_clk_master_setup(np
, &at91rm9200_master_layout
);
244 CLK_OF_DECLARE(at91rm9200_clk_master
, "atmel,at91rm9200-clk-master",
245 of_at91rm9200_clk_master_setup
);
247 static void __init
of_at91sam9x5_clk_master_setup(struct device_node
*np
)
249 of_at91_clk_master_setup(np
, &at91sam9x5_master_layout
);
251 CLK_OF_DECLARE(at91sam9x5_clk_master
, "atmel,at91sam9x5-clk-master",
252 of_at91sam9x5_clk_master_setup
);