1 /*
2  * Copyright (C) Maxime Coquelin 2015
3  * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
4  * License terms:  GNU General Public License (GPL), version 2
5  */
6 
7 #include <linux/kernel.h>
8 #include <linux/clocksource.h>
9 #include <linux/clockchips.h>
10 #include <linux/of.h>
11 #include <linux/of_address.h>
12 #include <linux/clk.h>
13 #include <linux/bitops.h>
14 
15 #define SYST_CSR	0x00
16 #define SYST_RVR	0x04
17 #define SYST_CVR	0x08
18 #define SYST_CALIB	0x0c
19 
20 #define SYST_CSR_ENABLE BIT(0)
21 
22 #define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
23 
24 static void __init system_timer_of_register(struct device_node *np)
25 {
26 	struct clk *clk = NULL;
27 	void __iomem *base;
28 	u32 rate;
29 	int ret;
30 
31 	base = of_iomap(np, 0);
32 	if (!base) {
33 		pr_warn("system-timer: invalid base address\n");
34 		return;
35 	}
36 
37 	ret = of_property_read_u32(np, "clock-frequency", &rate);
38 	if (ret) {
39 		clk = of_clk_get(np, 0);
40 		if (IS_ERR(clk))
41 			goto out_unmap;
42 
43 		ret = clk_prepare_enable(clk);
44 		if (ret)
45 			goto out_clk_put;
46 
47 		rate = clk_get_rate(clk);
48 		if (!rate)
49 			goto out_clk_disable;
50 	}
51 
52 	writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
53 	writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
54 
55 	ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
56 			200, 24, clocksource_mmio_readl_down);
57 	if (ret) {
58 		pr_err("failed to init clocksource (%d)\n", ret);
59 		if (clk)
60 			goto out_clk_disable;
61 		else
62 			goto out_unmap;
63 	}
64 
65 	pr_info("ARM System timer initialized as clocksource\n");
66 
67 	return;
68 
69 out_clk_disable:
70 	clk_disable_unprepare(clk);
71 out_clk_put:
72 	clk_put(clk);
73 out_unmap:
74 	iounmap(base);
75 	pr_warn("ARM System timer register failed (%d)\n", ret);
76 }
77 
78 CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
79 			system_timer_of_register);
80