summaryrefslogtreecommitdiff
path: root/drivers/timer/imx-gpt-timer.c
blob: 72be2977547ab53b953b1a821cf16ea20532c058 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2021
 * Author(s): Giulio Benetti <giulio.benetti@benettiengineering.com>
 */

#include <common.h>
#include <clk.h>
#include <dm.h>
#include <fdtdec.h>
#include <timer.h>
#include <dm/device_compat.h>

#include <asm/io.h>

#define GPT_CR_EN			BIT(0)
#define GPT_CR_FRR			BIT(9)
#define GPT_CR_EN_24M			BIT(10)
#define GPT_CR_SWR			BIT(15)

#define GPT_PR_PRESCALER24M_MASK	0x0000F000
#define GPT_PR_PRESCALER24M_SHIFT	12
#define GPT_PR_PRESCALER24M_MAX	(GPT_PR_PRESCALER24M_MASK >> GPT_PR_PRESCALER24M_SHIFT)
#define GPT_PR_PRESCALER_MASK		0x00000FFF
#define GPT_PR_PRESCALER_SHIFT		0
#define GPT_PR_PRESCALER_MAX		(GPT_PR_PRESCALER_MASK >> GPT_PR_PRESCALER_SHIFT)

#define GPT_CLKSRC_IPG_CLK		(1 << 6)
#define GPT_CLKSRC_IPG_CLK_24M		(5 << 6)

/* If CONFIG_SYS_HZ_CLOCK not specified et's default to 3Mhz */
#ifndef CONFIG_SYS_HZ_CLOCK
#define CONFIG_SYS_HZ_CLOCK		3000000
#endif

struct imx_gpt_timer_regs {
	u32 cr;
	u32 pr;
	u32 sr;
	u32 ir;
	u32 ocr1;
	u32 ocr2;
	u32 ocr3;
	u32 icr1;
	u32 icr2;
	u32 cnt;
};

struct imx_gpt_timer_priv {
	struct imx_gpt_timer_regs *base;
};

static u64 imx_gpt_timer_get_count(struct udevice *dev)
{
	struct imx_gpt_timer_priv *priv = dev_get_priv(dev);
	struct imx_gpt_timer_regs *regs = priv->base;

	return timer_conv_64(readl(&regs->cnt));
}

static int imx_gpt_setup(struct imx_gpt_timer_regs *regs, u32 rate)
{
	u32 prescaler = (rate / CONFIG_SYS_HZ_CLOCK) - 1;

	/* Reset the timer */
	setbits_le32(&regs->cr, GPT_CR_SWR);

	/* Wait for timer to finish reset */
	while (readl(&regs->cr) & GPT_CR_SWR)
		;

	if (rate == 24000000UL) {
		/* Set timer frequency if using 24M clock source */
		if (prescaler > GPT_PR_PRESCALER24M_MAX)
			return -EINVAL;

		/* Set 24M prescaler */
		writel((prescaler << GPT_PR_PRESCALER24M_SHIFT), &regs->pr);
		/* Set Oscillator as clock source, enable 24M input and set gpt
		 * in free-running mode
		 */
		writel(GPT_CLKSRC_IPG_CLK_24M | GPT_CR_EN_24M | GPT_CR_FRR, &regs->cr);
	} else {
		if (prescaler > GPT_PR_PRESCALER_MAX)
			return -EINVAL;

		/* Set prescaler */
		writel((prescaler << GPT_PR_PRESCALER_SHIFT), &regs->pr);
		/* Set Peripheral as clock source and set gpt in free-running
		 * mode
		 */
		writel(GPT_CLKSRC_IPG_CLK | GPT_CR_FRR, &regs->cr);
	}

	/* Start timer */
	setbits_le32(&regs->cr, GPT_CR_EN);

	return 0;
}

static int imx_gpt_timer_probe(struct udevice *dev)
{
	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
	struct imx_gpt_timer_priv *priv = dev_get_priv(dev);
	struct imx_gpt_timer_regs *regs;
	struct clk clk;
	fdt_addr_t addr;
	u32 clk_rate;
	int ret;

	addr = dev_read_addr(dev);
	if (addr == FDT_ADDR_T_NONE)
		return -EINVAL;

	priv->base = (struct imx_gpt_timer_regs *)addr;
	regs = priv->base;

	ret = clk_get_by_index(dev, 0, &clk);
	if (ret < 0)
		return ret;

	ret = clk_enable(&clk);
	if (ret) {
		dev_err(dev, "Failed to enable clock\n");
		return ret;
	}

	/* Get timer clock rate */
	clk_rate = clk_get_rate(&clk);
	if (clk_rate <= 0) {
		dev_err(dev, "Could not get clock rate...\n");
		return -EINVAL;
	}

	ret = imx_gpt_setup(regs, clk_rate);
	if (ret) {
		dev_err(dev, "Could not setup timer\n");
		return ret;
	}

	uc_priv->clock_rate = CONFIG_SYS_HZ_CLOCK;

	return 0;
}

static const struct timer_ops imx_gpt_timer_ops = {
	.get_count = imx_gpt_timer_get_count,
};

static const struct udevice_id imx_gpt_timer_ids[] = {
	{ .compatible = "fsl,imxrt-gpt" },
	{}
};

U_BOOT_DRIVER(imx_gpt_timer) = {
	.name = "imx_gpt_timer",
	.id = UCLASS_TIMER,
	.of_match = imx_gpt_timer_ids,
	.priv_auto = sizeof(struct imx_gpt_timer_priv),
	.probe = imx_gpt_timer_probe,
	.ops = &imx_gpt_timer_ops,
};