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 SMD_DIV_SHIFT 8
16 #define SMD_MAX_DIV 0xf
18 struct at91sam9x5_clk_smd
{
20 struct regmap
*regmap
;
23 #define to_at91sam9x5_clk_smd(hw) \
24 container_of(hw, struct at91sam9x5_clk_smd, hw)
26 static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw
*hw
,
27 unsigned long parent_rate
)
29 struct at91sam9x5_clk_smd
*smd
= to_at91sam9x5_clk_smd(hw
);
33 regmap_read(smd
->regmap
, AT91_PMC_SMD
, &smdr
);
34 smddiv
= (smdr
& AT91_PMC_SMD_DIV
) >> SMD_DIV_SHIFT
;
36 return parent_rate
/ (smddiv
+ 1);
39 static long at91sam9x5_clk_smd_round_rate(struct clk_hw
*hw
, unsigned long rate
,
40 unsigned long *parent_rate
)
43 unsigned long bestrate
;
46 if (rate
>= *parent_rate
)
49 div
= *parent_rate
/ rate
;
50 if (div
> SMD_MAX_DIV
)
51 return *parent_rate
/ (SMD_MAX_DIV
+ 1);
53 bestrate
= *parent_rate
/ div
;
54 tmp
= *parent_rate
/ (div
+ 1);
55 if (bestrate
- rate
> rate
- tmp
)
61 static int at91sam9x5_clk_smd_set_parent(struct clk_hw
*hw
, u8 index
)
63 struct at91sam9x5_clk_smd
*smd
= to_at91sam9x5_clk_smd(hw
);
68 regmap_update_bits(smd
->regmap
, AT91_PMC_SMD
, AT91_PMC_SMDS
,
69 index
? AT91_PMC_SMDS
: 0);
74 static u8
at91sam9x5_clk_smd_get_parent(struct clk_hw
*hw
)
76 struct at91sam9x5_clk_smd
*smd
= to_at91sam9x5_clk_smd(hw
);
79 regmap_read(smd
->regmap
, AT91_PMC_SMD
, &smdr
);
81 return smdr
& AT91_PMC_SMDS
;
84 static int at91sam9x5_clk_smd_set_rate(struct clk_hw
*hw
, unsigned long rate
,
85 unsigned long parent_rate
)
87 struct at91sam9x5_clk_smd
*smd
= to_at91sam9x5_clk_smd(hw
);
88 unsigned long div
= parent_rate
/ rate
;
90 if (parent_rate
% rate
|| div
< 1 || div
> (SMD_MAX_DIV
+ 1))
93 regmap_update_bits(smd
->regmap
, AT91_PMC_SMD
, AT91_PMC_SMD_DIV
,
94 (div
- 1) << SMD_DIV_SHIFT
);
99 static const struct clk_ops at91sam9x5_smd_ops
= {
100 .recalc_rate
= at91sam9x5_clk_smd_recalc_rate
,
101 .round_rate
= at91sam9x5_clk_smd_round_rate
,
102 .get_parent
= at91sam9x5_clk_smd_get_parent
,
103 .set_parent
= at91sam9x5_clk_smd_set_parent
,
104 .set_rate
= at91sam9x5_clk_smd_set_rate
,
107 struct clk_hw
* __init
108 at91sam9x5_clk_register_smd(struct regmap
*regmap
, const char *name
,
109 const char **parent_names
, u8 num_parents
)
111 struct at91sam9x5_clk_smd
*smd
;
113 struct clk_init_data init
;
116 smd
= kzalloc(sizeof(*smd
), GFP_KERNEL
);
118 return ERR_PTR(-ENOMEM
);
121 init
.ops
= &at91sam9x5_smd_ops
;
122 init
.parent_names
= parent_names
;
123 init
.num_parents
= num_parents
;
124 init
.flags
= CLK_SET_RATE_GATE
| CLK_SET_PARENT_GATE
;
126 smd
->hw
.init
= &init
;
127 smd
->regmap
= regmap
;
130 ret
= clk_hw_register(NULL
, &smd
->hw
);