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