summaryrefslogtreecommitdiff
path: root/drivers/rtc/pl031.c
blob: a1d376611d6578e2be63ebb376bcacec62f920de (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * (C) Copyright 2008
 * Gururaja Hebbar gururajakr@sanyo.co.in
 *
 * reference linux-2.6.20.6/drivers/rtc/rtc-pl031.c
 */

#include <common.h>
#include <command.h>
#include <dm.h>
#include <errno.h>
#include <log.h>
#include <rtc.h>
#include <asm/io.h>
#include <asm/types.h>

/*
 * Register definitions
 */
#define	RTC_DR		0x00	/* Data read register */
#define	RTC_MR		0x04	/* Match register */
#define	RTC_LR		0x08	/* Data load register */
#define	RTC_CR		0x0c	/* Control register */
#define	RTC_IMSC	0x10	/* Interrupt mask and set register */
#define	RTC_RIS		0x14	/* Raw interrupt status register */
#define	RTC_MIS		0x18	/* Masked interrupt status register */
#define	RTC_ICR		0x1c	/* Interrupt clear register */

#define RTC_CR_START	(1 << 0)

struct pl031_plat {
	phys_addr_t base;
};

static inline u32 pl031_read_reg(struct udevice *dev, int reg)
{
	struct pl031_plat *pdata = dev_get_plat(dev);

	return readl(pdata->base + reg);
}

static inline u32 pl031_write_reg(struct udevice *dev, int reg, u32 value)
{
	struct pl031_plat *pdata = dev_get_plat(dev);

	return writel(value, pdata->base + reg);
}

/*
 * Probe RTC device
 */
static int pl031_probe(struct udevice *dev)
{
	/* Enable RTC Start in Control register*/
	pl031_write_reg(dev, RTC_CR, RTC_CR_START);

	return 0;
}

/*
 * Get the current time from the RTC
 */
static int pl031_get(struct udevice *dev, struct rtc_time *tm)
{
	unsigned long tim;

	if (!tm)
		return -EINVAL;

	tim = pl031_read_reg(dev, RTC_DR);

	rtc_to_tm(tim, tm);

	debug("Get DATE: %4d-%02d-%02d (wday=%d)  TIME: %2d:%02d:%02d\n",
	      tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
	      tm->tm_hour, tm->tm_min, tm->tm_sec);

	return 0;
}

/*
 * Set the RTC
 */
static int pl031_set(struct udevice *dev, const struct rtc_time *tm)
{
	unsigned long tim;

	if (!tm)
		return -EINVAL;

	debug("Set DATE: %4d-%02d-%02d (wday=%d)  TIME: %2d:%02d:%02d\n",
	      tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
	      tm->tm_hour, tm->tm_min, tm->tm_sec);

	/* Calculate number of seconds this incoming time represents */
	tim = rtc_mktime(tm);

	pl031_write_reg(dev, RTC_LR, tim);

	return 0;
}

/*
 * Reset the RTC. We set the date back to 1970-01-01.
 */
static int pl031_reset(struct udevice *dev)
{
	pl031_write_reg(dev, RTC_LR, 0);

	return 0;
}

static const struct rtc_ops pl031_ops = {
	.get = pl031_get,
	.set = pl031_set,
	.reset = pl031_reset,
};

static const struct udevice_id pl031_ids[] = {
	{ .compatible = "arm,pl031" },
	{ }
};

static int pl031_of_to_plat(struct udevice *dev)
{
	struct pl031_plat *pdata = dev_get_plat(dev);

	pdata->base = dev_read_addr(dev);

	return 0;
}

U_BOOT_DRIVER(rtc_pl031) = {
	.name	= "rtc-pl031",
	.id	= UCLASS_RTC,
	.of_match = pl031_ids,
	.probe	= pl031_probe,
	.of_to_plat = pl031_of_to_plat,
	.plat_auto	= sizeof(struct pl031_plat),
	.ops	= &pl031_ops,
};