1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * MediaTek timer driver
4  *
5  * Copyright (C) 2018 MediaTek Inc.
6  * Author: Ryder Lee <ryder.lee@mediatek.com>
7  */
8 
9 #include <clk.h>
10 #include <dm.h>
11 #include <timer.h>
12 #include <asm/io.h>
13 #include <linux/bitops.h>
14 
15 #define MTK_GPT4_OFFSET_V1	0x40
16 #define MTK_GPT4_OFFSET_V2	0x80
17 
18 #define MTK_GPT_CON		0x0
19 #define MTK_GPT_V1_CLK		0x4
20 #define MTK_GPT_CNT		0x8
21 
22 #define GPT_ENABLE		BIT(0)
23 #define GPT_CLEAR		BIT(1)
24 #define GPT_V1_FREERUN		GENMASK(5, 4)
25 #define GPT_V2_FREERUN		GENMASK(6, 5)
26 
27 enum mtk_gpt_ver {
28 	MTK_GPT_V1,
29 	MTK_GPT_V2
30 };
31 
32 struct mtk_timer_priv {
33 	void __iomem *base;
34 	unsigned int gpt4_offset;
35 };
36 
mtk_timer_get_count(struct udevice * dev)37 static u64 mtk_timer_get_count(struct udevice *dev)
38 {
39 	struct mtk_timer_priv *priv = dev_get_priv(dev);
40 	u32 val = readl(priv->base + priv->gpt4_offset + MTK_GPT_CNT);
41 
42 	return timer_conv_64(val);
43 }
44 
mtk_timer_probe(struct udevice * dev)45 static int mtk_timer_probe(struct udevice *dev)
46 {
47 	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
48 	struct mtk_timer_priv *priv = dev_get_priv(dev);
49 	struct clk clk, parent;
50 	int ret, gpt_ver;
51 
52 	priv->base = dev_read_addr_ptr(dev);
53 	gpt_ver = dev_get_driver_data(dev);
54 
55 	if (!priv->base)
56 		return -ENOENT;
57 
58 	if (gpt_ver == MTK_GPT_V2) {
59 		priv->gpt4_offset = MTK_GPT4_OFFSET_V2;
60 
61 		writel(GPT_V2_FREERUN | GPT_CLEAR | GPT_ENABLE,
62 		       priv->base + priv->gpt4_offset + MTK_GPT_CON);
63 	} else {
64 		priv->gpt4_offset = MTK_GPT4_OFFSET_V1;
65 
66 		writel(GPT_V1_FREERUN | GPT_CLEAR | GPT_ENABLE,
67 		       priv->base + priv->gpt4_offset + MTK_GPT_CON);
68 		writel(0, priv->base + priv->gpt4_offset + MTK_GPT_V1_CLK);
69 	}
70 
71 	ret = clk_get_by_index(dev, 0, &clk);
72 	if (ret)
73 		return ret;
74 
75 	ret = clk_get_by_index(dev, 1, &parent);
76 	/* Skip setting the parent with dummy fixed-clock */
77 	if (!ret && parent.dev->driver != DM_DRIVER_GET(fixed_clock)) {
78 		ret = clk_set_parent(&clk, &parent);
79 		if (ret)
80 			return ret;
81 	}
82 
83 	uc_priv->clock_rate = clk_get_rate(&clk);
84 	if (!uc_priv->clock_rate)
85 		return -EINVAL;
86 
87 	return 0;
88 }
89 
90 static const struct timer_ops mtk_timer_ops = {
91 	.get_count = mtk_timer_get_count,
92 };
93 
94 static const struct udevice_id mtk_timer_ids[] = {
95 	{ .compatible = "mediatek,timer", .data = MTK_GPT_V1 },
96 	{ .compatible = "mediatek,mt6577-timer", .data = MTK_GPT_V1 },
97 	{ .compatible = "mediatek,mt7981-timer", .data = MTK_GPT_V2 },
98 	{ .compatible = "mediatek,mt7986-timer", .data = MTK_GPT_V2 },
99 	{ }
100 };
101 
102 U_BOOT_DRIVER(mtk_timer) = {
103 	.name = "mtk_timer",
104 	.id = UCLASS_TIMER,
105 	.of_match = mtk_timer_ids,
106 	.priv_auto	= sizeof(struct mtk_timer_priv),
107 	.probe = mtk_timer_probe,
108 	.ops = &mtk_timer_ops,
109 	.flags = DM_FLAG_PRE_RELOC,
110 };
111