xref: /openbmc/linux/drivers/clk/at91/clk-system.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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