]>
Commit | Line | Data |
---|---|---|
b789e4f2 JL |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (c) 2022 Nuvoton Technology Corp. | |
4 | */ | |
5 | ||
b789e4f2 JL |
6 | #include <clk.h> |
7 | #include <dm.h> | |
8 | #include <timer.h> | |
9 | #include <asm/io.h> | |
10 | ||
11 | #define NPCM_TIMER_CLOCK_RATE 1000000UL /* 1MHz timer */ | |
12 | #define NPCM_TIMER_INPUT_RATE 25000000UL /* Rate of input clock */ | |
13 | #define NPCM_TIMER_TDR_MASK GENMASK(23, 0) | |
14 | #define NPCM_TIMER_MAX_VAL NPCM_TIMER_TDR_MASK /* max counter value */ | |
15 | ||
16 | /* Register offsets */ | |
17 | #define TCR0 0x0 /* Timer Control and Status Register */ | |
18 | #define TICR0 0x8 /* Timer Initial Count Register */ | |
19 | #define TDR0 0x10 /* Timer Data Register */ | |
20 | ||
21 | /* TCR fields */ | |
22 | #define TCR_MODE_PERIODIC BIT(27) | |
23 | #define TCR_EN BIT(30) | |
24 | #define TCR_PRESCALE (NPCM_TIMER_INPUT_RATE / NPCM_TIMER_CLOCK_RATE - 1) | |
25 | ||
26 | enum input_clock_type { | |
27 | INPUT_CLOCK_FIXED, /* input clock rate is fixed */ | |
28 | INPUT_CLOCK_NON_FIXED | |
29 | }; | |
30 | ||
31 | /** | |
32 | * struct npcm_timer_priv - private data for npcm timer driver | |
33 | * npcm timer is a 24-bits down-counting timer. | |
34 | * | |
35 | * @last_count: last hw counter value | |
36 | * @counter: the value to be returned for get_count ops | |
37 | */ | |
38 | struct npcm_timer_priv { | |
39 | void __iomem *base; | |
40 | u32 last_count; | |
41 | u64 counter; | |
42 | }; | |
43 | ||
44 | static u64 npcm_timer_get_count(struct udevice *dev) | |
45 | { | |
46 | struct npcm_timer_priv *priv = dev_get_priv(dev); | |
47 | u32 val; | |
48 | ||
49 | /* The timer is counting down */ | |
50 | val = readl(priv->base + TDR0) & NPCM_TIMER_TDR_MASK; | |
51 | if (val <= priv->last_count) | |
52 | priv->counter += priv->last_count - val; | |
53 | else | |
54 | priv->counter += priv->last_count + (NPCM_TIMER_MAX_VAL + 1 - val); | |
55 | priv->last_count = val; | |
56 | ||
57 | return priv->counter; | |
58 | } | |
59 | ||
60 | static int npcm_timer_probe(struct udevice *dev) | |
61 | { | |
62 | struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); | |
63 | struct npcm_timer_priv *priv = dev_get_priv(dev); | |
64 | enum input_clock_type type = dev_get_driver_data(dev); | |
65 | struct clk clk; | |
66 | int ret; | |
67 | ||
68 | priv->base = dev_read_addr_ptr(dev); | |
69 | if (!priv->base) | |
70 | return -EINVAL; | |
71 | uc_priv->clock_rate = NPCM_TIMER_CLOCK_RATE; | |
72 | ||
73 | if (type == INPUT_CLOCK_NON_FIXED) { | |
74 | ret = clk_get_by_index(dev, 0, &clk); | |
75 | if (ret < 0) | |
76 | return ret; | |
77 | ||
78 | ret = clk_set_rate(&clk, NPCM_TIMER_INPUT_RATE); | |
79 | if (ret < 0) | |
80 | return ret; | |
81 | } | |
82 | ||
83 | /* | |
84 | * Configure timer and start | |
85 | * periodic mode | |
86 | * timer clock rate = input clock / prescale | |
87 | */ | |
88 | writel(0, priv->base + TCR0); | |
89 | writel(NPCM_TIMER_MAX_VAL, priv->base + TICR0); | |
90 | writel(TCR_EN | TCR_MODE_PERIODIC | TCR_PRESCALE, | |
91 | priv->base + TCR0); | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | static const struct timer_ops npcm_timer_ops = { | |
97 | .get_count = npcm_timer_get_count, | |
98 | }; | |
99 | ||
100 | static const struct udevice_id npcm_timer_ids[] = { | |
101 | { .compatible = "nuvoton,npcm845-timer", .data = INPUT_CLOCK_FIXED}, | |
102 | { .compatible = "nuvoton,npcm750-timer", .data = INPUT_CLOCK_NON_FIXED}, | |
103 | {} | |
104 | }; | |
105 | ||
106 | U_BOOT_DRIVER(npcm_timer) = { | |
107 | .name = "npcm_timer", | |
108 | .id = UCLASS_TIMER, | |
109 | .of_match = npcm_timer_ids, | |
110 | .priv_auto = sizeof(struct npcm_timer_priv), | |
111 | .probe = npcm_timer_probe, | |
112 | .ops = &npcm_timer_ops, | |
113 | .flags = DM_FLAG_PRE_RELOC, | |
114 | }; |