2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * Copyright (C) 2013 ARM Limited
14 #include <linux/amba/sp810.h>
15 #include <linux/clkdev.h>
16 #include <linux/clk-provider.h>
17 #include <linux/err.h>
19 #include <linux/of_address.h>
21 #define to_clk_sp810_timerclken(_hw) \
22 container_of(_hw, struct clk_sp810_timerclken, hw)
26 struct clk_sp810_timerclken
{
29 struct clk_sp810
*sp810
;
34 struct device_node
*node
;
35 int refclk_index
, timclk_index
;
38 struct clk_sp810_timerclken timerclken
[4];
43 static u8
clk_sp810_timerclken_get_parent(struct clk_hw
*hw
)
45 struct clk_sp810_timerclken
*timerclken
= to_clk_sp810_timerclken(hw
);
46 u32 val
= readl(timerclken
->sp810
->base
+ SCCTRL
);
48 return !!(val
& (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken
->channel
)));
51 static int clk_sp810_timerclken_set_parent(struct clk_hw
*hw
, u8 index
)
53 struct clk_sp810_timerclken
*timerclken
= to_clk_sp810_timerclken(hw
);
54 struct clk_sp810
*sp810
= timerclken
->sp810
;
55 u32 val
, shift
= SCCTRL_TIMERENnSEL_SHIFT(timerclken
->channel
);
56 unsigned long flags
= 0;
58 if (WARN_ON(index
> 1))
61 spin_lock_irqsave(&sp810
->lock
, flags
);
63 val
= readl(sp810
->base
+ SCCTRL
);
65 val
|= index
<< shift
;
66 writel(val
, sp810
->base
+ SCCTRL
);
68 spin_unlock_irqrestore(&sp810
->lock
, flags
);
74 * FIXME - setting the parent every time .prepare is invoked is inefficient.
75 * This is better handled by a dedicated clock tree configuration mechanism at
76 * init-time. Revisit this later when such a mechanism exists
78 static int clk_sp810_timerclken_prepare(struct clk_hw
*hw
)
80 struct clk_sp810_timerclken
*timerclken
= to_clk_sp810_timerclken(hw
);
81 struct clk_sp810
*sp810
= timerclken
->sp810
;
82 struct clk
*old_parent
= __clk_get_parent(hw
->clk
);
83 struct clk
*new_parent
;
86 sp810
->refclk
= of_clk_get(sp810
->node
, sp810
->refclk_index
);
89 sp810
->timclk
= of_clk_get(sp810
->node
, sp810
->timclk_index
);
91 if (WARN_ON(IS_ERR(sp810
->refclk
) || IS_ERR(sp810
->timclk
)))
94 /* Select fastest parent */
95 if (clk_get_rate(sp810
->refclk
) > clk_get_rate(sp810
->timclk
))
96 new_parent
= sp810
->refclk
;
98 new_parent
= sp810
->timclk
;
100 /* Switch the parent if necessary */
101 if (old_parent
!= new_parent
) {
102 clk_prepare(new_parent
);
103 clk_set_parent(hw
->clk
, new_parent
);
104 clk_unprepare(old_parent
);
110 static void clk_sp810_timerclken_unprepare(struct clk_hw
*hw
)
112 struct clk_sp810_timerclken
*timerclken
= to_clk_sp810_timerclken(hw
);
113 struct clk_sp810
*sp810
= timerclken
->sp810
;
115 clk_put(sp810
->timclk
);
116 clk_put(sp810
->refclk
);
119 static const struct clk_ops clk_sp810_timerclken_ops
= {
120 .prepare
= clk_sp810_timerclken_prepare
,
121 .unprepare
= clk_sp810_timerclken_unprepare
,
122 .get_parent
= clk_sp810_timerclken_get_parent
,
123 .set_parent
= clk_sp810_timerclken_set_parent
,
126 struct clk
*clk_sp810_timerclken_of_get(struct of_phandle_args
*clkspec
,
129 struct clk_sp810
*sp810
= data
;
131 if (WARN_ON(clkspec
->args_count
!= 1 || clkspec
->args
[0] >
132 ARRAY_SIZE(sp810
->timerclken
)))
135 return sp810
->timerclken
[clkspec
->args
[0]].clk
;
138 void __init
clk_sp810_of_setup(struct device_node
*node
)
140 struct clk_sp810
*sp810
= kzalloc(sizeof(*sp810
), GFP_KERNEL
);
141 const char *parent_names
[2];
143 struct clk_init_data init
;
147 pr_err("Failed to allocate memory for SP810!\n");
151 sp810
->refclk_index
= of_property_match_string(node
, "clock-names",
153 parent_names
[0] = of_clk_get_parent_name(node
, sp810
->refclk_index
);
155 sp810
->timclk_index
= of_property_match_string(node
, "clock-names",
157 parent_names
[1] = of_clk_get_parent_name(node
, sp810
->timclk_index
);
159 if (parent_names
[0] <= 0 || parent_names
[1] <= 0) {
160 pr_warn("Failed to obtain parent clocks for SP810!\n");
165 sp810
->base
= of_iomap(node
, 0);
166 spin_lock_init(&sp810
->lock
);
169 init
.ops
= &clk_sp810_timerclken_ops
;
170 init
.flags
= CLK_IS_BASIC
;
171 init
.parent_names
= parent_names
;
172 init
.num_parents
= ARRAY_SIZE(parent_names
);
174 for (i
= 0; i
< ARRAY_SIZE(sp810
->timerclken
); i
++) {
175 snprintf(name
, ARRAY_SIZE(name
), "timerclken%d", i
);
177 sp810
->timerclken
[i
].sp810
= sp810
;
178 sp810
->timerclken
[i
].channel
= i
;
179 sp810
->timerclken
[i
].hw
.init
= &init
;
181 sp810
->timerclken
[i
].clk
= clk_register(NULL
,
182 &sp810
->timerclken
[i
].hw
);
183 WARN_ON(IS_ERR(sp810
->timerclken
[i
].clk
));
186 of_clk_add_provider(node
, clk_sp810_timerclken_of_get
, sp810
);
188 CLK_OF_DECLARE(sp810
, "arm,sp810", clk_sp810_of_setup
);