1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
6 #include <linux/clk-provider.h>
7 #include <linux/clkdev.h>
8 #include <linux/clk/at91_pmc.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/regmap.h>
15 #define MASTER_PRES_MASK 0x7
16 #define MASTER_PRES_MAX MASTER_PRES_MASK
17 #define MASTER_DIV_SHIFT 8
18 #define MASTER_DIV_MASK 0x3
20 #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
24 struct regmap
*regmap
;
25 const struct clk_master_layout
*layout
;
26 const struct clk_master_characteristics
*characteristics
;
30 static inline bool clk_master_ready(struct regmap
*regmap
)
34 regmap_read(regmap
, AT91_PMC_SR
, &status
);
36 return status
& AT91_PMC_MCKRDY
? 1 : 0;
39 static int clk_master_prepare(struct clk_hw
*hw
)
41 struct clk_master
*master
= to_clk_master(hw
);
43 while (!clk_master_ready(master
->regmap
))
49 static int clk_master_is_prepared(struct clk_hw
*hw
)
51 struct clk_master
*master
= to_clk_master(hw
);
53 return clk_master_ready(master
->regmap
);
56 static unsigned long clk_master_recalc_rate(struct clk_hw
*hw
,
57 unsigned long parent_rate
)
61 unsigned long rate
= parent_rate
;
62 struct clk_master
*master
= to_clk_master(hw
);
63 const struct clk_master_layout
*layout
= master
->layout
;
64 const struct clk_master_characteristics
*characteristics
=
65 master
->characteristics
;
68 regmap_read(master
->regmap
, master
->layout
->offset
, &mckr
);
71 pres
= (mckr
>> layout
->pres_shift
) & MASTER_PRES_MASK
;
72 div
= (mckr
>> MASTER_DIV_SHIFT
) & MASTER_DIV_MASK
;
74 if (characteristics
->have_div3_pres
&& pres
== MASTER_PRES_MAX
)
79 rate
/= characteristics
->divisors
[div
];
81 if (rate
< characteristics
->output
.min
)
82 pr_warn("master clk is underclocked");
83 else if (rate
> characteristics
->output
.max
)
84 pr_warn("master clk is overclocked");
89 static u8
clk_master_get_parent(struct clk_hw
*hw
)
91 struct clk_master
*master
= to_clk_master(hw
);
94 regmap_read(master
->regmap
, master
->layout
->offset
, &mckr
);
96 return mckr
& AT91_PMC_CSS
;
99 static const struct clk_ops master_ops
= {
100 .prepare
= clk_master_prepare
,
101 .is_prepared
= clk_master_is_prepared
,
102 .recalc_rate
= clk_master_recalc_rate
,
103 .get_parent
= clk_master_get_parent
,
106 struct clk_hw
* __init
107 at91_clk_register_master(struct regmap
*regmap
,
108 const char *name
, int num_parents
,
109 const char **parent_names
,
110 const struct clk_master_layout
*layout
,
111 const struct clk_master_characteristics
*characteristics
)
113 struct clk_master
*master
;
114 struct clk_init_data init
;
118 if (!name
|| !num_parents
|| !parent_names
)
119 return ERR_PTR(-EINVAL
);
121 master
= kzalloc(sizeof(*master
), GFP_KERNEL
);
123 return ERR_PTR(-ENOMEM
);
126 init
.ops
= &master_ops
;
127 init
.parent_names
= parent_names
;
128 init
.num_parents
= num_parents
;
131 master
->hw
.init
= &init
;
132 master
->layout
= layout
;
133 master
->characteristics
= characteristics
;
134 master
->regmap
= regmap
;
137 ret
= clk_hw_register(NULL
, &master
->hw
);
146 const struct clk_master_layout at91rm9200_master_layout
= {
149 .offset
= AT91_PMC_MCKR
,
152 const struct clk_master_layout at91sam9x5_master_layout
= {
155 .offset
= AT91_PMC_MCKR
,