Skip to content

Commit 639d566

Browse files
Linus Walleijbebarino
authored andcommitted
clk: ux500: Implement the missing CLKOUT clocks
This implements the two missing CLKOUT clocks for the ux500 (well really U8500/DB8500) SoC. The clocks are initialized using a specific parent and divider and these are specified in the device tree, see the separate binding patch. The implementation is a bit different in that it will only create the clock in the clock framework if a user appears in the device tree, rather than it being registered upfront like most of the other clocks. This is because the clock needs parameters for source and divider from the consumer phandle for the clock to be set up properly when the clock is registered. There could be more than one user of a CLKOUT clock, but we have not seen this in practice. If this happens the framework prints and info and returns the previously registered clock. Using the clocks requires also muxing the CLKOUT1 or CLKOUT2 to the appropriate pad. In practice this is achived in a pinctrl handle in the DTS node for the device using the CLKOUT clock, so this muxing is done separately from the clock itself. Example: haptic@49 { compatible = "immersion,isa1200"; reg = <0x49>; (...) /* clkout1 from ACLK divided by 8 */ clocks = <&clkout_clk DB8500_CLKOUT_1 DB8500_CLKOUT_SRC_ACLK 8>; pinctrl-names = "default"; pinctrl-0 = <&isa1200_janice_default>; }; isa1200_janice_default: isa1200_janice { /* Bring out clkout1 on pin GPIO227 pin AH7 */ janice_mux { function = "clkout"; groups = "clkout1_a_1"; }; janice_cfg1 { pins = "GPIO227_AH7"; ste,config = <&out_lo>; }; (...) This was tested successfully with the Immersion ISA1200 haptic feedback unit on the Samsung Galaxy S Advance GT-I9070 (Janice) mobile phone. As the CLKOUT clocks need some undefined fixed rate parent clocks that are currently missing from the PRCMU clock implementation, the three simplest are added in this patch: clk38m_to_clkgen, aclk and sysclk. The only parent not yet available in the implementation is clk009, which is a kind of special muxed and divided clock which isn't even implemented in the vendor clock driver. Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Link: https://lore.kernel.org/r/20220414221751.323525-6-linus.walleij@linaro.org Signed-off-by: Stephen Boyd <sboyd@kernel.org>
1 parent a8173c5 commit 639d566

3 files changed

Lines changed: 199 additions & 7 deletions

File tree

drivers/clk/ux500/clk-prcmu.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,21 @@
1414
#include "clk.h"
1515

1616
#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw)
17+
#define to_clk_prcmu_clkout(_hw) container_of(_hw, struct clk_prcmu_clkout, hw)
1718

1819
struct clk_prcmu {
1920
struct clk_hw hw;
2021
u8 cg_sel;
2122
int opp_requested;
2223
};
2324

25+
struct clk_prcmu_clkout {
26+
struct clk_hw hw;
27+
u8 clkout_id;
28+
u8 source;
29+
u8 divider;
30+
};
31+
2432
/* PRCMU clock operations. */
2533

2634
static int clk_prcmu_prepare(struct clk_hw *hw)
@@ -284,3 +292,109 @@ struct clk_hw *clk_reg_prcmu_opp_volt_scalable(const char *name,
284292
return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
285293
&clk_prcmu_opp_volt_scalable_ops);
286294
}
295+
296+
/* The clkout (external) clock is special and need special ops */
297+
298+
static int clk_prcmu_clkout_prepare(struct clk_hw *hw)
299+
{
300+
struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
301+
302+
return prcmu_config_clkout(clk->clkout_id, clk->source, clk->divider);
303+
}
304+
305+
static void clk_prcmu_clkout_unprepare(struct clk_hw *hw)
306+
{
307+
struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
308+
int ret;
309+
310+
/* The clkout clock is disabled by dividing by 0 */
311+
ret = prcmu_config_clkout(clk->clkout_id, clk->source, 0);
312+
if (ret)
313+
pr_err("clk_prcmu: %s failed to disable %s\n", __func__,
314+
clk_hw_get_name(hw));
315+
}
316+
317+
static unsigned long clk_prcmu_clkout_recalc_rate(struct clk_hw *hw,
318+
unsigned long parent_rate)
319+
{
320+
struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
321+
322+
return (parent_rate / clk->divider);
323+
}
324+
325+
static u8 clk_prcmu_clkout_get_parent(struct clk_hw *hw)
326+
{
327+
struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
328+
329+
return clk->source;
330+
}
331+
332+
static int clk_prcmu_clkout_set_parent(struct clk_hw *hw, u8 index)
333+
{
334+
struct clk_prcmu_clkout *clk = to_clk_prcmu_clkout(hw);
335+
336+
clk->source = index;
337+
/* Make sure the change reaches the hardware immediately */
338+
if (clk_hw_is_prepared(hw))
339+
return clk_prcmu_clkout_prepare(hw);
340+
return 0;
341+
}
342+
343+
static const struct clk_ops clk_prcmu_clkout_ops = {
344+
.prepare = clk_prcmu_clkout_prepare,
345+
.unprepare = clk_prcmu_clkout_unprepare,
346+
.recalc_rate = clk_prcmu_clkout_recalc_rate,
347+
.get_parent = clk_prcmu_clkout_get_parent,
348+
.set_parent = clk_prcmu_clkout_set_parent,
349+
};
350+
351+
struct clk_hw *clk_reg_prcmu_clkout(const char *name,
352+
const char * const *parent_names,
353+
int num_parents,
354+
u8 source, u8 divider)
355+
356+
{
357+
struct clk_prcmu_clkout *clk;
358+
struct clk_init_data clk_prcmu_clkout_init;
359+
u8 clkout_id;
360+
int ret;
361+
362+
if (!name) {
363+
pr_err("clk_prcmu_clkout: %s invalid arguments passed\n", __func__);
364+
return ERR_PTR(-EINVAL);
365+
}
366+
367+
if (!strcmp(name, "clkout1"))
368+
clkout_id = 0;
369+
else if (!strcmp(name, "clkout2"))
370+
clkout_id = 1;
371+
else {
372+
pr_err("clk_prcmu_clkout: %s bad clock name\n", __func__);
373+
return ERR_PTR(-EINVAL);
374+
}
375+
376+
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
377+
if (!clk)
378+
return ERR_PTR(-ENOMEM);
379+
380+
clk->clkout_id = clkout_id;
381+
clk->source = source;
382+
clk->divider = divider;
383+
384+
clk_prcmu_clkout_init.name = name;
385+
clk_prcmu_clkout_init.ops = &clk_prcmu_clkout_ops;
386+
clk_prcmu_clkout_init.flags = CLK_GET_RATE_NOCACHE;
387+
clk_prcmu_clkout_init.parent_names = parent_names;
388+
clk_prcmu_clkout_init.num_parents = num_parents;
389+
clk->hw.init = &clk_prcmu_clkout_init;
390+
391+
ret = clk_hw_register(NULL, &clk->hw);
392+
if (ret)
393+
goto free_clkout;
394+
395+
return &clk->hw;
396+
free_clkout:
397+
kfree(clk);
398+
pr_err("clk_prcmu_clkout: %s failed to register clk\n", __func__);
399+
return ERR_PTR(-ENOMEM);
400+
}

drivers/clk/ux500/clk.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ struct clk_hw *clk_reg_prcmu_opp_volt_scalable(const char *name,
6060
unsigned long rate,
6161
unsigned long flags);
6262

63+
struct clk_hw *clk_reg_prcmu_clkout(const char *name,
64+
const char * const *parent_names,
65+
int num_parents,
66+
u8 source, u8 divider);
67+
6368
struct clk *clk_reg_sysctrl_gate(struct device *dev,
6469
const char *name,
6570
const char *parent_name,

drivers/clk/ux500/u8500_of_clk.c

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
static struct clk *prcc_pclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_CLUSTER];
1919
static struct clk *prcc_kclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_CLUSTER];
20+
static struct clk_hw *clkout_clk[2];
2021

2122
#define PRCC_SHOW(clk, base, bit) \
2223
clk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit]
@@ -56,6 +57,71 @@ static struct clk_hw_onecell_data u8500_prcmu_hw_clks = {
5657
.num = PRCMU_NUM_CLKS,
5758
};
5859

60+
/* Essentially names for the first PRCMU_CLKSRC_* defines */
61+
static const char * const u8500_clkout_parents[] = {
62+
"clk38m_to_clkgen",
63+
"aclk",
64+
/* Just called "sysclk" in documentation */
65+
"ab8500_sysclk",
66+
"lcdclk",
67+
"sdmmcclk",
68+
"tvclk",
69+
"timclk",
70+
/* CLK009 is not implemented, add it if you need it */
71+
"clk009",
72+
};
73+
74+
static struct clk_hw *ux500_clkout_get(struct of_phandle_args *clkspec,
75+
void *data)
76+
{
77+
u32 id, source, divider;
78+
struct clk_hw *clkout;
79+
80+
if (clkspec->args_count != 3)
81+
return ERR_PTR(-EINVAL);
82+
83+
id = clkspec->args[0];
84+
source = clkspec->args[1];
85+
divider = clkspec->args[2];
86+
87+
if (id > 1) {
88+
pr_err("%s: invalid clkout ID %d\n", __func__, id);
89+
return ERR_PTR(-EINVAL);
90+
}
91+
92+
if (clkout_clk[id]) {
93+
pr_info("%s: clkout%d already registered, not reconfiguring\n",
94+
__func__, id + 1);
95+
return clkout_clk[id];
96+
}
97+
98+
if (source > 7) {
99+
pr_err("%s: invalid source ID %d\n", __func__, source);
100+
return ERR_PTR(-EINVAL);
101+
}
102+
103+
if (divider == 0 || divider > 63) {
104+
pr_err("%s: invalid divider %d\n", __func__, divider);
105+
return ERR_PTR(-EINVAL);
106+
}
107+
108+
pr_debug("registering clkout%d with source %d and divider %d\n",
109+
id + 1, source, divider);
110+
111+
clkout = clk_reg_prcmu_clkout(id ? "clkout2" : "clkout1",
112+
u8500_clkout_parents,
113+
ARRAY_SIZE(u8500_clkout_parents),
114+
source, divider);
115+
if (IS_ERR(clkout)) {
116+
pr_err("failed to register clkout%d\n", id + 1);
117+
return ERR_CAST(clkout);
118+
}
119+
120+
clkout_clk[id] = clkout;
121+
122+
return clkout;
123+
}
124+
59125
static void u8500_clk_init(struct device_node *np)
60126
{
61127
struct prcmu_fw_version *fw_version;
@@ -99,7 +165,17 @@ static void u8500_clk_init(struct device_node *np)
99165
clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR,
100166
CLK_IGNORE_UNUSED);
101167

102-
/* FIXME: Add sys, ulp and int clocks here. */
168+
/*
169+
* Read-only clocks that only return their current rate, only used
170+
* as parents to other clocks and not visible in the device tree.
171+
* clk38m_to_clkgen is the same as the SYSCLK, i.e. the root clock.
172+
*/
173+
clk_reg_prcmu_rate("clk38m_to_clkgen", NULL, PRCMU_SYSCLK,
174+
CLK_IGNORE_UNUSED);
175+
clk_reg_prcmu_rate("aclk", NULL, PRCMU_ACLK,
176+
CLK_IGNORE_UNUSED);
177+
178+
/* TODO: add CLK009 if needed */
103179

104180
rtc_clk = clk_register_fixed_rate(NULL, "rtc32k", "NULL",
105181
CLK_IGNORE_UNUSED,
@@ -223,12 +299,6 @@ static void u8500_clk_init(struct device_node *np)
223299
twd_clk = clk_register_fixed_factor(NULL, "smp_twd", "armss",
224300
CLK_IGNORE_UNUSED, 1, 2);
225301

226-
/*
227-
* FIXME: Add special handled PRCMU clocks here:
228-
* 1. clkout0yuv, use PRCMU as parent + need regulator + pinctrl.
229-
* 2. ab9540_clkout1yuv, see clkout0yuv
230-
*/
231-
232302
/* PRCC P-clocks */
233303
clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", bases[CLKRST1_INDEX],
234304
BIT(0), 0);
@@ -526,6 +596,9 @@ static void u8500_clk_init(struct device_node *np)
526596
of_clk_add_hw_provider(child, of_clk_hw_onecell_get,
527597
&u8500_prcmu_hw_clks);
528598

599+
if (of_node_name_eq(child, "clkout-clock"))
600+
of_clk_add_hw_provider(child, ux500_clkout_get, NULL);
601+
529602
if (of_node_name_eq(child, "prcc-periph-clock"))
530603
of_clk_add_provider(child, ux500_twocell_get, prcc_pclk);
531604

0 commit comments

Comments
 (0)