12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25fba62eaSBoris BREZILLON /*
35fba62eaSBoris BREZILLON * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
45fba62eaSBoris BREZILLON */
55fba62eaSBoris BREZILLON
65fba62eaSBoris BREZILLON #include <linux/clk-provider.h>
75fba62eaSBoris BREZILLON #include <linux/clkdev.h>
85fba62eaSBoris BREZILLON #include <linux/clk/at91_pmc.h>
95fba62eaSBoris BREZILLON #include <linux/of.h>
101bdf0232SBoris Brezillon #include <linux/mfd/syscon.h>
111bdf0232SBoris Brezillon #include <linux/regmap.h>
125fba62eaSBoris BREZILLON
135fba62eaSBoris BREZILLON #include "pmc.h"
145fba62eaSBoris BREZILLON
155fba62eaSBoris BREZILLON #define SYSTEM_MAX_ID 31
165fba62eaSBoris BREZILLON
175fba62eaSBoris BREZILLON #define SYSTEM_MAX_NAME_SZ 32
185fba62eaSBoris BREZILLON
195fba62eaSBoris BREZILLON #define to_clk_system(hw) container_of(hw, struct clk_system, hw)
205fba62eaSBoris BREZILLON struct clk_system {
215fba62eaSBoris BREZILLON struct clk_hw hw;
221bdf0232SBoris Brezillon struct regmap *regmap;
2336971566SClaudiu Beznea struct at91_clk_pms pms;
245fba62eaSBoris BREZILLON u8 id;
255fba62eaSBoris BREZILLON };
265fba62eaSBoris BREZILLON
is_pck(int id)27cce6db80SJean-Jacques Hiblot static inline int is_pck(int id)
28cce6db80SJean-Jacques Hiblot {
29cce6db80SJean-Jacques Hiblot return (id >= 8) && (id <= 15);
30cce6db80SJean-Jacques Hiblot }
31cce6db80SJean-Jacques Hiblot
clk_system_ready(struct regmap * regmap,int id)321bdf0232SBoris Brezillon static inline bool clk_system_ready(struct regmap *regmap, int id)
331bdf0232SBoris Brezillon {
341bdf0232SBoris Brezillon unsigned int status;
351bdf0232SBoris Brezillon
361bdf0232SBoris Brezillon regmap_read(regmap, AT91_PMC_SR, &status);
371bdf0232SBoris Brezillon
3842324d95SClaudiu Beznea return !!(status & (1 << id));
391bdf0232SBoris Brezillon }
401bdf0232SBoris Brezillon
clk_system_prepare(struct clk_hw * hw)41cce6db80SJean-Jacques Hiblot static int clk_system_prepare(struct clk_hw *hw)
425fba62eaSBoris BREZILLON {
435fba62eaSBoris BREZILLON struct clk_system *sys = to_clk_system(hw);
445fba62eaSBoris BREZILLON
451bdf0232SBoris Brezillon regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id);
46cce6db80SJean-Jacques Hiblot
47cce6db80SJean-Jacques Hiblot if (!is_pck(sys->id))
48cce6db80SJean-Jacques Hiblot return 0;
49cce6db80SJean-Jacques Hiblot
5099a81706SAlexandre Belloni while (!clk_system_ready(sys->regmap, sys->id))
51cce6db80SJean-Jacques Hiblot cpu_relax();
5299a81706SAlexandre Belloni
535fba62eaSBoris BREZILLON return 0;
545fba62eaSBoris BREZILLON }
555fba62eaSBoris BREZILLON
clk_system_unprepare(struct clk_hw * hw)56cce6db80SJean-Jacques Hiblot static void clk_system_unprepare(struct clk_hw *hw)
575fba62eaSBoris BREZILLON {
585fba62eaSBoris BREZILLON struct clk_system *sys = to_clk_system(hw);
595fba62eaSBoris BREZILLON
601bdf0232SBoris Brezillon regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id);
615fba62eaSBoris BREZILLON }
625fba62eaSBoris BREZILLON
clk_system_is_prepared(struct clk_hw * hw)63cce6db80SJean-Jacques Hiblot static int clk_system_is_prepared(struct clk_hw *hw)
645fba62eaSBoris BREZILLON {
655fba62eaSBoris BREZILLON struct clk_system *sys = to_clk_system(hw);
661bdf0232SBoris Brezillon unsigned int status;
675fba62eaSBoris BREZILLON
681bdf0232SBoris Brezillon regmap_read(sys->regmap, AT91_PMC_SCSR, &status);
691bdf0232SBoris Brezillon
701bdf0232SBoris Brezillon if (!(status & (1 << sys->id)))
71cce6db80SJean-Jacques Hiblot return 0;
72cce6db80SJean-Jacques Hiblot
73cce6db80SJean-Jacques Hiblot if (!is_pck(sys->id))
74cce6db80SJean-Jacques Hiblot return 1;
75cce6db80SJean-Jacques Hiblot
761bdf0232SBoris Brezillon regmap_read(sys->regmap, AT91_PMC_SR, &status);
771bdf0232SBoris Brezillon
7842324d95SClaudiu Beznea return !!(status & (1 << sys->id));
795fba62eaSBoris BREZILLON }
805fba62eaSBoris BREZILLON
clk_system_save_context(struct clk_hw * hw)8136971566SClaudiu Beznea static int clk_system_save_context(struct clk_hw *hw)
8236971566SClaudiu Beznea {
8336971566SClaudiu Beznea struct clk_system *sys = to_clk_system(hw);
8436971566SClaudiu Beznea
8536971566SClaudiu Beznea sys->pms.status = clk_system_is_prepared(hw);
8636971566SClaudiu Beznea
8736971566SClaudiu Beznea return 0;
8836971566SClaudiu Beznea }
8936971566SClaudiu Beznea
clk_system_restore_context(struct clk_hw * hw)9036971566SClaudiu Beznea static void clk_system_restore_context(struct clk_hw *hw)
9136971566SClaudiu Beznea {
9236971566SClaudiu Beznea struct clk_system *sys = to_clk_system(hw);
9336971566SClaudiu Beznea
9436971566SClaudiu Beznea if (sys->pms.status)
9536971566SClaudiu Beznea clk_system_prepare(&sys->hw);
9636971566SClaudiu Beznea }
9736971566SClaudiu Beznea
985fba62eaSBoris BREZILLON static const struct clk_ops system_ops = {
99cce6db80SJean-Jacques Hiblot .prepare = clk_system_prepare,
100cce6db80SJean-Jacques Hiblot .unprepare = clk_system_unprepare,
101cce6db80SJean-Jacques Hiblot .is_prepared = clk_system_is_prepared,
10236971566SClaudiu Beznea .save_context = clk_system_save_context,
10336971566SClaudiu Beznea .restore_context = clk_system_restore_context,
1045fba62eaSBoris BREZILLON };
1055fba62eaSBoris BREZILLON
106b2e39dc0SAlexandre Belloni struct clk_hw * __init
at91_clk_register_system(struct regmap * regmap,const char * name,const char * parent_name,struct clk_hw * parent_hw,u8 id,unsigned long flags)1071bdf0232SBoris Brezillon at91_clk_register_system(struct regmap *regmap, const char *name,
108*1a537f62SClaudiu Beznea const char *parent_name, struct clk_hw *parent_hw, u8 id,
109*1a537f62SClaudiu Beznea unsigned long flags)
1105fba62eaSBoris BREZILLON {
1115fba62eaSBoris BREZILLON struct clk_system *sys;
112f5644f10SStephen Boyd struct clk_hw *hw;
113*1a537f62SClaudiu Beznea struct clk_init_data init = {};
114f5644f10SStephen Boyd int ret;
1155fba62eaSBoris BREZILLON
116*1a537f62SClaudiu Beznea if (!(parent_name || parent_hw) || id > SYSTEM_MAX_ID)
1175fba62eaSBoris BREZILLON return ERR_PTR(-EINVAL);
1185fba62eaSBoris BREZILLON
1195fba62eaSBoris BREZILLON sys = kzalloc(sizeof(*sys), GFP_KERNEL);
1205fba62eaSBoris BREZILLON if (!sys)
1215fba62eaSBoris BREZILLON return ERR_PTR(-ENOMEM);
1225fba62eaSBoris BREZILLON
1235fba62eaSBoris BREZILLON init.name = name;
1245fba62eaSBoris BREZILLON init.ops = &system_ops;
125*1a537f62SClaudiu Beznea if (parent_hw)
126*1a537f62SClaudiu Beznea init.parent_hws = (const struct clk_hw **)&parent_hw;
127*1a537f62SClaudiu Beznea else
1285fba62eaSBoris BREZILLON init.parent_names = &parent_name;
1295fba62eaSBoris BREZILLON init.num_parents = 1;
13068b3b6f1SClaudiu Beznea init.flags = CLK_SET_RATE_PARENT | flags;
1315fba62eaSBoris BREZILLON
1325fba62eaSBoris BREZILLON sys->id = id;
1335fba62eaSBoris BREZILLON sys->hw.init = &init;
1341bdf0232SBoris Brezillon sys->regmap = regmap;
1355fba62eaSBoris BREZILLON
136f5644f10SStephen Boyd hw = &sys->hw;
137f5644f10SStephen Boyd ret = clk_hw_register(NULL, &sys->hw);
138f5644f10SStephen Boyd if (ret) {
1395fba62eaSBoris BREZILLON kfree(sys);
140f5644f10SStephen Boyd hw = ERR_PTR(ret);
141f5644f10SStephen Boyd }
1425fba62eaSBoris BREZILLON
143f5644f10SStephen Boyd return hw;
1445fba62eaSBoris BREZILLON }
145