1 // SPDX-License-Identifier: GPL-2.0-only
4 * Copyright (C) 2013 ARM Limited
7 #include <linux/amba/sp810.h>
8 #include <linux/slab.h>
10 #include <linux/clk-provider.h>
11 #include <linux/err.h>
14 #include <linux/of_address.h>
16 #define to_clk_sp810_timerclken(_hw) \
17 container_of(_hw, struct clk_sp810_timerclken, hw)
21 struct clk_sp810_timerclken
{
24 struct clk_sp810
*sp810
;
29 struct device_node
*node
;
32 struct clk_sp810_timerclken timerclken
[4];
35 static u8
clk_sp810_timerclken_get_parent(struct clk_hw
*hw
)
37 struct clk_sp810_timerclken
*timerclken
= to_clk_sp810_timerclken(hw
);
38 u32 val
= readl(timerclken
->sp810
->base
+ SCCTRL
);
40 return !!(val
& (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken
->channel
)));
43 static int clk_sp810_timerclken_set_parent(struct clk_hw
*hw
, u8 index
)
45 struct clk_sp810_timerclken
*timerclken
= to_clk_sp810_timerclken(hw
);
46 struct clk_sp810
*sp810
= timerclken
->sp810
;
47 u32 val
, shift
= SCCTRL_TIMERENnSEL_SHIFT(timerclken
->channel
);
48 unsigned long flags
= 0;
50 if (WARN_ON(index
> 1))
53 spin_lock_irqsave(&sp810
->lock
, flags
);
55 val
= readl(sp810
->base
+ SCCTRL
);
57 val
|= index
<< shift
;
58 writel(val
, sp810
->base
+ SCCTRL
);
60 spin_unlock_irqrestore(&sp810
->lock
, flags
);
65 static const struct clk_ops clk_sp810_timerclken_ops
= {
66 .get_parent
= clk_sp810_timerclken_get_parent
,
67 .set_parent
= clk_sp810_timerclken_set_parent
,
70 static struct clk
*clk_sp810_timerclken_of_get(struct of_phandle_args
*clkspec
,
73 struct clk_sp810
*sp810
= data
;
75 if (WARN_ON(clkspec
->args_count
!= 1 ||
76 clkspec
->args
[0] >= ARRAY_SIZE(sp810
->timerclken
)))
79 return sp810
->timerclken
[clkspec
->args
[0]].clk
;
82 static void __init
clk_sp810_of_setup(struct device_node
*node
)
84 struct clk_sp810
*sp810
= kzalloc(sizeof(*sp810
), GFP_KERNEL
);
85 const char *parent_names
[2];
86 int num
= ARRAY_SIZE(parent_names
);
88 struct clk_init_data init
;
96 if (of_clk_parent_fill(node
, parent_names
, num
) != num
) {
97 pr_warn("Failed to obtain parent clocks for SP810!\n");
103 sp810
->base
= of_iomap(node
, 0);
104 spin_lock_init(&sp810
->lock
);
107 init
.ops
= &clk_sp810_timerclken_ops
;
109 init
.parent_names
= parent_names
;
110 init
.num_parents
= num
;
112 deprecated
= !of_find_property(node
, "assigned-clock-parents", NULL
);
114 for (i
= 0; i
< ARRAY_SIZE(sp810
->timerclken
); i
++) {
115 snprintf(name
, sizeof(name
), "sp810_%d_%d", instance
, i
);
117 sp810
->timerclken
[i
].sp810
= sp810
;
118 sp810
->timerclken
[i
].channel
= i
;
119 sp810
->timerclken
[i
].hw
.init
= &init
;
122 * If DT isn't setting the parent, force it to be
123 * the 1 MHz clock without going through the framework.
124 * We do this before clk_register() so that it can determine
125 * the parent and setup the tree properly.
128 init
.ops
->set_parent(&sp810
->timerclken
[i
].hw
, 1);
130 sp810
->timerclken
[i
].clk
= clk_register(NULL
,
131 &sp810
->timerclken
[i
].hw
);
132 WARN_ON(IS_ERR(sp810
->timerclken
[i
].clk
));
135 of_clk_add_provider(node
, clk_sp810_timerclken_of_get
, sp810
);
138 CLK_OF_DECLARE(sp810
, "arm,sp810", clk_sp810_of_setup
);