1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2022 Nuvoton Technology Corp.
4  */
5 
6 #include <dm.h>
7 #include <timer.h>
8 #include <asm/io.h>
9 
10 #define NPCM_TIMER_CLOCK_RATE	25000000UL	/* 25MHz */
11 
12 /* Register offsets */
13 #define SECCNT	0x0	/* Seconds Counter Register */
14 #define CNTR25M	0x4	/* 25MHz Counter Register */
15 
16 struct npcm_timer_priv {
17 	void __iomem *base;
18 };
19 
npcm_timer_get_count(struct udevice * dev)20 static u64 npcm_timer_get_count(struct udevice *dev)
21 {
22 	struct npcm_timer_priv *priv = dev_get_priv(dev);
23 	u64 reg_sec, reg_25m;
24 	u64 counter;
25 
26 	reg_sec = readl(priv->base + SECCNT);
27 	reg_25m = readl(priv->base + CNTR25M);
28 	/*
29 	 * When CNTR25M reaches 25M, it goes to 0 and SECCNT is increased by 1.
30 	 * When CNTR25M is zero, wait for CNTR25M to become non-zero in case
31 	 * SECCNT is not updated yet.
32 	 */
33 	if (reg_25m == 0) {
34 		while (reg_25m == 0)
35 			reg_25m = readl(priv->base + CNTR25M);
36 		reg_sec = readl(priv->base + SECCNT);
37 	}
38 	counter = reg_sec * NPCM_TIMER_CLOCK_RATE + reg_25m;
39 
40 	return counter;
41 }
42 
npcm_timer_probe(struct udevice * dev)43 static int npcm_timer_probe(struct udevice *dev)
44 {
45 	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
46 	struct npcm_timer_priv *priv = dev_get_priv(dev);
47 
48 	priv->base = dev_read_addr_ptr(dev);
49 	if (!priv->base)
50 		return -EINVAL;
51 	uc_priv->clock_rate = NPCM_TIMER_CLOCK_RATE;
52 
53 	return 0;
54 }
55 
56 static const struct timer_ops npcm_timer_ops = {
57 	.get_count = npcm_timer_get_count,
58 };
59 
60 static const struct udevice_id npcm_timer_ids[] = {
61 	{ .compatible = "nuvoton,npcm845-timer"},
62 	{ .compatible = "nuvoton,npcm750-timer"},
63 	{}
64 };
65 
66 U_BOOT_DRIVER(npcm_timer) = {
67 	.name	= "npcm_timer",
68 	.id	= UCLASS_TIMER,
69 	.of_match = npcm_timer_ids,
70 	.priv_auto = sizeof(struct npcm_timer_priv),
71 	.probe = npcm_timer_probe,
72 	.ops	= &npcm_timer_ops,
73 	.flags = DM_FLAG_PRE_RELOC,
74 };
75