12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a9c0688fSBoris BREZILLON /*
3a9c0688fSBoris BREZILLON * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4a9c0688fSBoris BREZILLON */
5a9c0688fSBoris BREZILLON
6a9c0688fSBoris BREZILLON #include <linux/clk-provider.h>
7a9c0688fSBoris BREZILLON #include <linux/clkdev.h>
8a9c0688fSBoris BREZILLON #include <linux/clk/at91_pmc.h>
9a9c0688fSBoris BREZILLON #include <linux/of.h>
101bdf0232SBoris Brezillon #include <linux/mfd/syscon.h>
111bdf0232SBoris Brezillon #include <linux/regmap.h>
12a9c0688fSBoris BREZILLON
13a9c0688fSBoris BREZILLON #include "pmc.h"
14a9c0688fSBoris BREZILLON
15a9c0688fSBoris BREZILLON #define SMD_DIV_SHIFT 8
16a9c0688fSBoris BREZILLON #define SMD_MAX_DIV 0xf
17a9c0688fSBoris BREZILLON
18a9c0688fSBoris BREZILLON struct at91sam9x5_clk_smd {
19a9c0688fSBoris BREZILLON struct clk_hw hw;
201bdf0232SBoris Brezillon struct regmap *regmap;
21a9c0688fSBoris BREZILLON };
22a9c0688fSBoris BREZILLON
23a9c0688fSBoris BREZILLON #define to_at91sam9x5_clk_smd(hw) \
24a9c0688fSBoris BREZILLON container_of(hw, struct at91sam9x5_clk_smd, hw)
25a9c0688fSBoris BREZILLON
at91sam9x5_clk_smd_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)26a9c0688fSBoris BREZILLON static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw,
27a9c0688fSBoris BREZILLON unsigned long parent_rate)
28a9c0688fSBoris BREZILLON {
29a9c0688fSBoris BREZILLON struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
301bdf0232SBoris Brezillon unsigned int smdr;
311bdf0232SBoris Brezillon u8 smddiv;
32a9c0688fSBoris BREZILLON
331bdf0232SBoris Brezillon regmap_read(smd->regmap, AT91_PMC_SMD, &smdr);
341bdf0232SBoris Brezillon smddiv = (smdr & AT91_PMC_SMD_DIV) >> SMD_DIV_SHIFT;
351bdf0232SBoris Brezillon
36a9c0688fSBoris BREZILLON return parent_rate / (smddiv + 1);
37a9c0688fSBoris BREZILLON }
38a9c0688fSBoris BREZILLON
at91sam9x5_clk_smd_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)39*6a25bd4dSMaxime Ripard static int at91sam9x5_clk_smd_determine_rate(struct clk_hw *hw,
40*6a25bd4dSMaxime Ripard struct clk_rate_request *req)
41a9c0688fSBoris BREZILLON {
42a9c0688fSBoris BREZILLON unsigned long div;
43a9c0688fSBoris BREZILLON unsigned long bestrate;
44a9c0688fSBoris BREZILLON unsigned long tmp;
45a9c0688fSBoris BREZILLON
46*6a25bd4dSMaxime Ripard if (req->rate >= req->best_parent_rate) {
47*6a25bd4dSMaxime Ripard req->rate = req->best_parent_rate;
48*6a25bd4dSMaxime Ripard return 0;
49*6a25bd4dSMaxime Ripard }
50a9c0688fSBoris BREZILLON
51*6a25bd4dSMaxime Ripard div = req->best_parent_rate / req->rate;
52*6a25bd4dSMaxime Ripard if (div > SMD_MAX_DIV) {
53*6a25bd4dSMaxime Ripard req->rate = req->best_parent_rate / (SMD_MAX_DIV + 1);
54*6a25bd4dSMaxime Ripard return 0;
55*6a25bd4dSMaxime Ripard }
56a9c0688fSBoris BREZILLON
57*6a25bd4dSMaxime Ripard bestrate = req->best_parent_rate / div;
58*6a25bd4dSMaxime Ripard tmp = req->best_parent_rate / (div + 1);
59*6a25bd4dSMaxime Ripard if (bestrate - req->rate > req->rate - tmp)
60a9c0688fSBoris BREZILLON bestrate = tmp;
61a9c0688fSBoris BREZILLON
62*6a25bd4dSMaxime Ripard req->rate = bestrate;
63*6a25bd4dSMaxime Ripard return 0;
64a9c0688fSBoris BREZILLON }
65a9c0688fSBoris BREZILLON
at91sam9x5_clk_smd_set_parent(struct clk_hw * hw,u8 index)66a9c0688fSBoris BREZILLON static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index)
67a9c0688fSBoris BREZILLON {
68a9c0688fSBoris BREZILLON struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
69a9c0688fSBoris BREZILLON
70a9c0688fSBoris BREZILLON if (index > 1)
71a9c0688fSBoris BREZILLON return -EINVAL;
721bdf0232SBoris Brezillon
731bdf0232SBoris Brezillon regmap_update_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS,
741bdf0232SBoris Brezillon index ? AT91_PMC_SMDS : 0);
751bdf0232SBoris Brezillon
76a9c0688fSBoris BREZILLON return 0;
77a9c0688fSBoris BREZILLON }
78a9c0688fSBoris BREZILLON
at91sam9x5_clk_smd_get_parent(struct clk_hw * hw)79a9c0688fSBoris BREZILLON static u8 at91sam9x5_clk_smd_get_parent(struct clk_hw *hw)
80a9c0688fSBoris BREZILLON {
81a9c0688fSBoris BREZILLON struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
821bdf0232SBoris Brezillon unsigned int smdr;
83a9c0688fSBoris BREZILLON
841bdf0232SBoris Brezillon regmap_read(smd->regmap, AT91_PMC_SMD, &smdr);
851bdf0232SBoris Brezillon
861bdf0232SBoris Brezillon return smdr & AT91_PMC_SMDS;
87a9c0688fSBoris BREZILLON }
88a9c0688fSBoris BREZILLON
at91sam9x5_clk_smd_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)89a9c0688fSBoris BREZILLON static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate,
90a9c0688fSBoris BREZILLON unsigned long parent_rate)
91a9c0688fSBoris BREZILLON {
92a9c0688fSBoris BREZILLON struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
93a9c0688fSBoris BREZILLON unsigned long div = parent_rate / rate;
94a9c0688fSBoris BREZILLON
95a9c0688fSBoris BREZILLON if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1))
96a9c0688fSBoris BREZILLON return -EINVAL;
971bdf0232SBoris Brezillon
981bdf0232SBoris Brezillon regmap_update_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV,
991bdf0232SBoris Brezillon (div - 1) << SMD_DIV_SHIFT);
100a9c0688fSBoris BREZILLON
101a9c0688fSBoris BREZILLON return 0;
102a9c0688fSBoris BREZILLON }
103a9c0688fSBoris BREZILLON
104a9c0688fSBoris BREZILLON static const struct clk_ops at91sam9x5_smd_ops = {
105a9c0688fSBoris BREZILLON .recalc_rate = at91sam9x5_clk_smd_recalc_rate,
106*6a25bd4dSMaxime Ripard .determine_rate = at91sam9x5_clk_smd_determine_rate,
107a9c0688fSBoris BREZILLON .get_parent = at91sam9x5_clk_smd_get_parent,
108a9c0688fSBoris BREZILLON .set_parent = at91sam9x5_clk_smd_set_parent,
109a9c0688fSBoris BREZILLON .set_rate = at91sam9x5_clk_smd_set_rate,
110a9c0688fSBoris BREZILLON };
111a9c0688fSBoris BREZILLON
112b2e39dc0SAlexandre Belloni struct clk_hw * __init
at91sam9x5_clk_register_smd(struct regmap * regmap,const char * name,const char ** parent_names,u8 num_parents)1131bdf0232SBoris Brezillon at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
114a9c0688fSBoris BREZILLON const char **parent_names, u8 num_parents)
115a9c0688fSBoris BREZILLON {
116a9c0688fSBoris BREZILLON struct at91sam9x5_clk_smd *smd;
117f5644f10SStephen Boyd struct clk_hw *hw;
118a9c0688fSBoris BREZILLON struct clk_init_data init;
119f5644f10SStephen Boyd int ret;
120a9c0688fSBoris BREZILLON
121a9c0688fSBoris BREZILLON smd = kzalloc(sizeof(*smd), GFP_KERNEL);
122a9c0688fSBoris BREZILLON if (!smd)
123a9c0688fSBoris BREZILLON return ERR_PTR(-ENOMEM);
124a9c0688fSBoris BREZILLON
125a9c0688fSBoris BREZILLON init.name = name;
126a9c0688fSBoris BREZILLON init.ops = &at91sam9x5_smd_ops;
127a9c0688fSBoris BREZILLON init.parent_names = parent_names;
128a9c0688fSBoris BREZILLON init.num_parents = num_parents;
129a9c0688fSBoris BREZILLON init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
130a9c0688fSBoris BREZILLON
131a9c0688fSBoris BREZILLON smd->hw.init = &init;
1321bdf0232SBoris Brezillon smd->regmap = regmap;
133a9c0688fSBoris BREZILLON
134f5644f10SStephen Boyd hw = &smd->hw;
135f5644f10SStephen Boyd ret = clk_hw_register(NULL, &smd->hw);
136f5644f10SStephen Boyd if (ret) {
137a9c0688fSBoris BREZILLON kfree(smd);
138f5644f10SStephen Boyd hw = ERR_PTR(ret);
139f5644f10SStephen Boyd }
140a9c0688fSBoris BREZILLON
141f5644f10SStephen Boyd return hw;
142a9c0688fSBoris BREZILLON }
143