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/of_address.h>
16 #include <linux/of_irq.h>
18 #include <linux/wait.h>
19 #include <linux/sched.h>
20 #include <linux/interrupt.h>
21 #include <linux/irq.h>
25 #define MASTER_SOURCE_MAX 4
27 #define MASTER_PRES_MASK 0x7
28 #define MASTER_PRES_MAX MASTER_PRES_MASK
29 #define MASTER_DIV_SHIFT 8
30 #define MASTER_DIV_MASK 0x3
32 struct clk_master_characteristics
{
33 struct clk_range output
;
38 struct clk_master_layout
{
43 #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
49 wait_queue_head_t wait
;
50 const struct clk_master_layout
*layout
;
51 const struct clk_master_characteristics
*characteristics
;
54 static irqreturn_t
clk_master_irq_handler(int irq
, void *dev_id
)
56 struct clk_master
*master
= (struct clk_master
*)dev_id
;
58 wake_up(&master
->wait
);
59 disable_irq_nosync(master
->irq
);
63 static int clk_master_prepare(struct clk_hw
*hw
)
65 struct clk_master
*master
= to_clk_master(hw
);
66 struct at91_pmc
*pmc
= master
->pmc
;
68 while (!(pmc_read(pmc
, AT91_PMC_SR
) & AT91_PMC_MCKRDY
)) {
69 enable_irq(master
->irq
);
70 wait_event(master
->wait
,
71 pmc_read(pmc
, AT91_PMC_SR
) & AT91_PMC_MCKRDY
);
77 static int clk_master_is_prepared(struct clk_hw
*hw
)
79 struct clk_master
*master
= to_clk_master(hw
);
81 return !!(pmc_read(master
->pmc
, AT91_PMC_SR
) & AT91_PMC_MCKRDY
);
84 static unsigned long clk_master_recalc_rate(struct clk_hw
*hw
,
85 unsigned long parent_rate
)
89 unsigned long rate
= parent_rate
;
90 struct clk_master
*master
= to_clk_master(hw
);
91 struct at91_pmc
*pmc
= master
->pmc
;
92 const struct clk_master_layout
*layout
= master
->layout
;
93 const struct clk_master_characteristics
*characteristics
=
94 master
->characteristics
;
98 tmp
= pmc_read(pmc
, AT91_PMC_MCKR
) & layout
->mask
;
101 pres
= (tmp
>> layout
->pres_shift
) & MASTER_PRES_MASK
;
102 div
= (tmp
>> MASTER_DIV_SHIFT
) & MASTER_DIV_MASK
;
104 if (characteristics
->have_div3_pres
&& pres
== MASTER_PRES_MAX
)
109 rate
/= characteristics
->divisors
[div
];
111 if (rate
< characteristics
->output
.min
)
112 pr_warn("master clk is underclocked");
113 else if (rate
> characteristics
->output
.max
)
114 pr_warn("master clk is overclocked");
119 static u8
clk_master_get_parent(struct clk_hw
*hw
)
121 struct clk_master
*master
= to_clk_master(hw
);
122 struct at91_pmc
*pmc
= master
->pmc
;
124 return pmc_read(pmc
, AT91_PMC_MCKR
) & AT91_PMC_CSS
;
127 static const struct clk_ops master_ops
= {
128 .prepare
= clk_master_prepare
,
129 .is_prepared
= clk_master_is_prepared
,
130 .recalc_rate
= clk_master_recalc_rate
,
131 .get_parent
= clk_master_get_parent
,
134 static struct clk
* __init
135 at91_clk_register_master(struct at91_pmc
*pmc
, unsigned int irq
,
136 const char *name
, int num_parents
,
137 const char **parent_names
,
138 const struct clk_master_layout
*layout
,
139 const struct clk_master_characteristics
*characteristics
)
142 struct clk_master
*master
;
143 struct clk
*clk
= NULL
;
144 struct clk_init_data init
;
146 if (!pmc
|| !irq
|| !name
|| !num_parents
|| !parent_names
)
147 return ERR_PTR(-EINVAL
);
149 master
= kzalloc(sizeof(*master
), GFP_KERNEL
);
151 return ERR_PTR(-ENOMEM
);
154 init
.ops
= &master_ops
;
155 init
.parent_names
= parent_names
;
156 init
.num_parents
= num_parents
;
159 master
->hw
.init
= &init
;
160 master
->layout
= layout
;
161 master
->characteristics
= characteristics
;
164 init_waitqueue_head(&master
->wait
);
165 irq_set_status_flags(master
->irq
, IRQ_NOAUTOEN
);
166 ret
= request_irq(master
->irq
, clk_master_irq_handler
,
167 IRQF_TRIGGER_HIGH
, "clk-master", master
);
173 clk
= clk_register(NULL
, &master
->hw
);
175 free_irq(master
->irq
, master
);
183 static const struct clk_master_layout at91rm9200_master_layout
= {
188 static const struct clk_master_layout at91sam9x5_master_layout
= {
194 static struct clk_master_characteristics
* __init
195 of_at91_clk_master_get_characteristics(struct device_node
*np
)
197 struct clk_master_characteristics
*characteristics
;
199 characteristics
= kzalloc(sizeof(*characteristics
), GFP_KERNEL
);
200 if (!characteristics
)
203 if (of_at91_get_clk_range(np
, "atmel,clk-output-range", &characteristics
->output
))
204 goto out_free_characteristics
;
206 of_property_read_u32_array(np
, "atmel,clk-divisors",
207 characteristics
->divisors
, 4);
209 characteristics
->have_div3_pres
=
210 of_property_read_bool(np
, "atmel,master-clk-have-div3-pres");
212 return characteristics
;
214 out_free_characteristics
:
215 kfree(characteristics
);
220 of_at91_clk_master_setup(struct device_node
*np
, struct at91_pmc
*pmc
,
221 const struct clk_master_layout
*layout
)
226 const char *parent_names
[MASTER_SOURCE_MAX
];
227 const char *name
= np
->name
;
228 struct clk_master_characteristics
*characteristics
;
230 num_parents
= of_clk_get_parent_count(np
);
231 if (num_parents
<= 0 || num_parents
> MASTER_SOURCE_MAX
)
234 of_clk_parent_fill(np
, parent_names
, num_parents
);
236 of_property_read_string(np
, "clock-output-names", &name
);
238 characteristics
= of_at91_clk_master_get_characteristics(np
);
239 if (!characteristics
)
242 irq
= irq_of_parse_and_map(np
, 0);
244 goto out_free_characteristics
;
246 clk
= at91_clk_register_master(pmc
, irq
, name
, num_parents
,
247 parent_names
, layout
,
250 goto out_free_characteristics
;
252 of_clk_add_provider(np
, of_clk_src_simple_get
, clk
);
255 out_free_characteristics
:
256 kfree(characteristics
);
259 void __init
of_at91rm9200_clk_master_setup(struct device_node
*np
,
260 struct at91_pmc
*pmc
)
262 of_at91_clk_master_setup(np
, pmc
, &at91rm9200_master_layout
);
265 void __init
of_at91sam9x5_clk_master_setup(struct device_node
*np
,
266 struct at91_pmc
*pmc
)
268 of_at91_clk_master_setup(np
, pmc
, &at91sam9x5_master_layout
);