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 to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw)
24 struct regmap
*regmap
;
27 static unsigned long clk_plldiv_recalc_rate(struct clk_hw
*hw
,
28 unsigned long parent_rate
)
30 struct clk_plldiv
*plldiv
= to_clk_plldiv(hw
);
33 regmap_read(plldiv
->regmap
, AT91_PMC_MCKR
, &mckr
);
35 if (mckr
& AT91_PMC_PLLADIV2
)
36 return parent_rate
/ 2;
41 static long clk_plldiv_round_rate(struct clk_hw
*hw
, unsigned long rate
,
42 unsigned long *parent_rate
)
46 if (rate
> *parent_rate
)
48 div
= *parent_rate
/ 2;
52 if (rate
- div
< *parent_rate
- rate
)
58 static int clk_plldiv_set_rate(struct clk_hw
*hw
, unsigned long rate
,
59 unsigned long parent_rate
)
61 struct clk_plldiv
*plldiv
= to_clk_plldiv(hw
);
63 if ((parent_rate
!= rate
) && (parent_rate
/ 2 != rate
))
66 regmap_update_bits(plldiv
->regmap
, AT91_PMC_MCKR
, AT91_PMC_PLLADIV2
,
67 parent_rate
!= rate
? AT91_PMC_PLLADIV2
: 0);
72 static const struct clk_ops plldiv_ops
= {
73 .recalc_rate
= clk_plldiv_recalc_rate
,
74 .round_rate
= clk_plldiv_round_rate
,
75 .set_rate
= clk_plldiv_set_rate
,
78 static struct clk_hw
* __init
79 at91_clk_register_plldiv(struct regmap
*regmap
, const char *name
,
80 const char *parent_name
)
82 struct clk_plldiv
*plldiv
;
84 struct clk_init_data init
;
87 plldiv
= kzalloc(sizeof(*plldiv
), GFP_KERNEL
);
89 return ERR_PTR(-ENOMEM
);
92 init
.ops
= &plldiv_ops
;
93 init
.parent_names
= parent_name
? &parent_name
: NULL
;
94 init
.num_parents
= parent_name
? 1 : 0;
95 init
.flags
= CLK_SET_RATE_GATE
;
97 plldiv
->hw
.init
= &init
;
98 plldiv
->regmap
= regmap
;
101 ret
= clk_hw_register(NULL
, &plldiv
->hw
);
111 of_at91sam9x5_clk_plldiv_setup(struct device_node
*np
)
114 const char *parent_name
;
115 const char *name
= np
->name
;
116 struct regmap
*regmap
;
118 parent_name
= of_clk_get_parent_name(np
, 0);
120 of_property_read_string(np
, "clock-output-names", &name
);
122 regmap
= syscon_node_to_regmap(of_get_parent(np
));
126 hw
= at91_clk_register_plldiv(regmap
, name
, parent_name
);
130 of_clk_add_hw_provider(np
, of_clk_hw_simple_get
, hw
);
132 CLK_OF_DECLARE(at91sam9x5_clk_plldiv
, "atmel,at91sam9x5-clk-plldiv",
133 of_at91sam9x5_clk_plldiv_setup
);