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 SYSTEM_MAX_ID 31
17 #define SYSTEM_MAX_NAME_SZ 32
19 #define to_clk_system(hw) container_of(hw, struct clk_system, hw)
22 struct regmap
*regmap
;
23 struct at91_clk_pms pms
;
27 static inline int is_pck(int id
)
29 return (id
>= 8) && (id
<= 15);
32 static inline bool clk_system_ready(struct regmap
*regmap
, int id
)
36 regmap_read(regmap
, AT91_PMC_SR
, &status
);
38 return !!(status
& (1 << id
));
41 static int clk_system_prepare(struct clk_hw
*hw
)
43 struct clk_system
*sys
= to_clk_system(hw
);
45 regmap_write(sys
->regmap
, AT91_PMC_SCER
, 1 << sys
->id
);
50 while (!clk_system_ready(sys
->regmap
, sys
->id
))
56 static void clk_system_unprepare(struct clk_hw
*hw
)
58 struct clk_system
*sys
= to_clk_system(hw
);
60 regmap_write(sys
->regmap
, AT91_PMC_SCDR
, 1 << sys
->id
);
63 static int clk_system_is_prepared(struct clk_hw
*hw
)
65 struct clk_system
*sys
= to_clk_system(hw
);
68 regmap_read(sys
->regmap
, AT91_PMC_SCSR
, &status
);
70 if (!(status
& (1 << sys
->id
)))
76 regmap_read(sys
->regmap
, AT91_PMC_SR
, &status
);
78 return !!(status
& (1 << sys
->id
));
81 static int clk_system_save_context(struct clk_hw
*hw
)
83 struct clk_system
*sys
= to_clk_system(hw
);
85 sys
->pms
.status
= clk_system_is_prepared(hw
);
90 static void clk_system_restore_context(struct clk_hw
*hw
)
92 struct clk_system
*sys
= to_clk_system(hw
);
95 clk_system_prepare(&sys
->hw
);
98 static const struct clk_ops system_ops
= {
99 .prepare
= clk_system_prepare
,
100 .unprepare
= clk_system_unprepare
,
101 .is_prepared
= clk_system_is_prepared
,
102 .save_context
= clk_system_save_context
,
103 .restore_context
= clk_system_restore_context
,
106 struct clk_hw
* __init
107 at91_clk_register_system(struct regmap
*regmap
, const char *name
,
108 const char *parent_name
, struct clk_hw
*parent_hw
, u8 id
,
111 struct clk_system
*sys
;
113 struct clk_init_data init
= {};
116 if (!(parent_name
|| parent_hw
) || id
> SYSTEM_MAX_ID
)
117 return ERR_PTR(-EINVAL
);
119 sys
= kzalloc(sizeof(*sys
), GFP_KERNEL
);
121 return ERR_PTR(-ENOMEM
);
124 init
.ops
= &system_ops
;
126 init
.parent_hws
= (const struct clk_hw
**)&parent_hw
;
128 init
.parent_names
= &parent_name
;
129 init
.num_parents
= 1;
130 init
.flags
= CLK_SET_RATE_PARENT
| flags
;
133 sys
->hw
.init
= &init
;
134 sys
->regmap
= regmap
;
137 ret
= clk_hw_register(NULL
, &sys
->hw
);