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/slab.h>
16 #include <linux/clk.h>
17 #include <linux/clk-provider.h>
18 #include <linux/err.h>
20 #include <linux/of_address.h>
22 #define to_clk_sp810_timerclken(_hw) \
23 container_of(_hw, struct clk_sp810_timerclken, hw)
27 struct clk_sp810_timerclken
{
30 struct clk_sp810
*sp810
;
35 struct device_node
*node
;
38 struct clk_sp810_timerclken timerclken
[4];
41 static u8
clk_sp810_timerclken_get_parent(struct clk_hw
*hw
)
43 struct clk_sp810_timerclken
*timerclken
= to_clk_sp810_timerclken(hw
);
44 u32 val
= readl(timerclken
->sp810
->base
+ SCCTRL
);
46 return !!(val
& (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken
->channel
)));
49 static int clk_sp810_timerclken_set_parent(struct clk_hw
*hw
, u8 index
)
51 struct clk_sp810_timerclken
*timerclken
= to_clk_sp810_timerclken(hw
);
52 struct clk_sp810
*sp810
= timerclken
->sp810
;
53 u32 val
, shift
= SCCTRL_TIMERENnSEL_SHIFT(timerclken
->channel
);
54 unsigned long flags
= 0;
56 if (WARN_ON(index
> 1))
59 spin_lock_irqsave(&sp810
->lock
, flags
);
61 val
= readl(sp810
->base
+ SCCTRL
);
63 val
|= index
<< shift
;
64 writel(val
, sp810
->base
+ SCCTRL
);
66 spin_unlock_irqrestore(&sp810
->lock
, flags
);
71 static const struct clk_ops clk_sp810_timerclken_ops
= {
72 .get_parent
= clk_sp810_timerclken_get_parent
,
73 .set_parent
= clk_sp810_timerclken_set_parent
,
76 static struct clk
*clk_sp810_timerclken_of_get(struct of_phandle_args
*clkspec
,
79 struct clk_sp810
*sp810
= data
;
81 if (WARN_ON(clkspec
->args_count
!= 1 ||
82 clkspec
->args
[0] >= ARRAY_SIZE(sp810
->timerclken
)))
85 return sp810
->timerclken
[clkspec
->args
[0]].clk
;
88 static void __init
clk_sp810_of_setup(struct device_node
*node
)
90 struct clk_sp810
*sp810
= kzalloc(sizeof(*sp810
), GFP_KERNEL
);
91 const char *parent_names
[2];
92 int num
= ARRAY_SIZE(parent_names
);
94 struct clk_init_data init
;
102 if (of_clk_parent_fill(node
, parent_names
, num
) != num
) {
103 pr_warn("Failed to obtain parent clocks for SP810!\n");
109 sp810
->base
= of_iomap(node
, 0);
110 spin_lock_init(&sp810
->lock
);
113 init
.ops
= &clk_sp810_timerclken_ops
;
114 init
.flags
= CLK_IS_BASIC
;
115 init
.parent_names
= parent_names
;
116 init
.num_parents
= num
;
118 deprecated
= !of_find_property(node
, "assigned-clock-parents", NULL
);
120 for (i
= 0; i
< ARRAY_SIZE(sp810
->timerclken
); i
++) {
121 snprintf(name
, sizeof(name
), "sp810_%d_%d", instance
, i
);
123 sp810
->timerclken
[i
].sp810
= sp810
;
124 sp810
->timerclken
[i
].channel
= i
;
125 sp810
->timerclken
[i
].hw
.init
= &init
;
128 * If DT isn't setting the parent, force it to be
129 * the 1 MHz clock without going through the framework.
130 * We do this before clk_register() so that it can determine
131 * the parent and setup the tree properly.
134 init
.ops
->set_parent(&sp810
->timerclken
[i
].hw
, 1);
136 sp810
->timerclken
[i
].clk
= clk_register(NULL
,
137 &sp810
->timerclken
[i
].hw
);
138 WARN_ON(IS_ERR(sp810
->timerclken
[i
].clk
));
141 of_clk_add_provider(node
, clk_sp810_timerclken_of_get
, sp810
);
144 CLK_OF_DECLARE(sp810
, "arm,sp810", clk_sp810_of_setup
);