1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * RTC driver for the Armada 38x Marvell SoCs
4 *
5 * Copyright (C) 2021 Marek Beh��n <kabel@kernel.org>
6 *
7 * Based on Linux' driver by Gregory Clement and Marvell
8 */
9
10#include <asm/io.h>
11#include <dm.h>
12#include <linux/delay.h>
13#include <rtc.h>
14
15#define RTC_STATUS			0x0
16#define RTC_TIME			0xC
17#define RTC_CONF_TEST			0x1C
18
19/* Armada38x SoC registers  */
20#define RTC_38X_BRIDGE_TIMING_CTL	0x0
21#define RTC_38X_PERIOD_OFFS		0
22#define RTC_38X_PERIOD_MASK		(0x3FF << RTC_38X_PERIOD_OFFS)
23#define RTC_38X_READ_DELAY_OFFS		26
24#define RTC_38X_READ_DELAY_MASK		(0x1F << RTC_38X_READ_DELAY_OFFS)
25
26#define SAMPLE_NR			100
27
28struct armada38x_rtc {
29	void __iomem *regs;
30	void __iomem *regs_soc;
31};
32
33/*
34 * According to Erratum RES-3124064 we have to do some configuration in MBUS.
35 * To read an RTC register we need to read it 100 times and return the most
36 * frequent value.
37 * To write an RTC register we need to write 2x zero into STATUS register,
38 * followed by the proper write. Linux adds an 5 us delay after this, so we do
39 * it here as well.
40 */
41static void update_38x_mbus_timing_params(struct armada38x_rtc *rtc)
42{
43	u32 reg;
44
45	reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
46	reg &= ~RTC_38X_PERIOD_MASK;
47	reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */
48	reg &= ~RTC_38X_READ_DELAY_MASK;
49	reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */
50	writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
51}
52
53static void armada38x_rtc_write(u32 val, struct armada38x_rtc *rtc, u8 reg)
54{
55	writel(0, rtc->regs + RTC_STATUS);
56	writel(0, rtc->regs + RTC_STATUS);
57	writel(val, rtc->regs + reg);
58	udelay(5);
59}
60
61static u32 armada38x_rtc_read(struct armada38x_rtc *rtc, u8 reg)
62{
63	u8 counts[SAMPLE_NR], max_idx;
64	u32 samples[SAMPLE_NR], max;
65	int i, j, last;
66
67	for (i = 0, last = 0; i < SAMPLE_NR; ++i) {
68		u32 sample = readl(rtc->regs + reg);
69
70		/* find if this value was already read */
71		for (j = 0; j < last; ++j) {
72			if (samples[j] == sample)
73				break;
74		}
75
76		if (j < last) {
77			/* if yes, increment count */
78			++counts[j];
79		} else {
80			/* if not, add */
81			samples[last] = sample;
82			counts[last] = 1;
83			++last;
84		}
85	}
86
87	/* finally find the sample that was read the most */
88	max = 0;
89	max_idx = 0;
90
91	for (i = 0; i < last; ++i) {
92		if (counts[i] > max) {
93			max = counts[i];
94			max_idx = i;
95		}
96	}
97
98	return samples[max_idx];
99}
100
101static int armada38x_rtc_get(struct udevice *dev, struct rtc_time *tm)
102{
103	struct armada38x_rtc *rtc = dev_get_priv(dev);
104	u32 time;
105
106	time = armada38x_rtc_read(rtc, RTC_TIME);
107
108	rtc_to_tm(time, tm);
109
110	return 0;
111}
112
113static int armada38x_rtc_reset(struct udevice *dev)
114{
115	struct armada38x_rtc *rtc = dev_get_priv(dev);
116	u32 reg;
117
118	reg = armada38x_rtc_read(rtc, RTC_CONF_TEST);
119
120	if (reg & 0xff) {
121		armada38x_rtc_write(0, rtc, RTC_CONF_TEST);
122		mdelay(500);
123		armada38x_rtc_write(0, rtc, RTC_TIME);
124		armada38x_rtc_write(BIT(0) | BIT(1), rtc, RTC_STATUS);
125	}
126
127	return 0;
128}
129
130static int armada38x_rtc_set(struct udevice *dev, const struct rtc_time *tm)
131{
132	struct armada38x_rtc *rtc = dev_get_priv(dev);
133	unsigned long time;
134
135	time = rtc_mktime(tm);
136
137	if (time > U32_MAX)
138		printf("%s: requested time to set will overflow\n", dev->name);
139
140	armada38x_rtc_reset(dev);
141	armada38x_rtc_write(time, rtc, RTC_TIME);
142
143	return 0;
144}
145
146static int armada38x_probe(struct udevice *dev)
147{
148	struct armada38x_rtc *rtc = dev_get_priv(dev);
149
150	rtc->regs = dev_remap_addr_name(dev, "rtc");
151	if (!rtc->regs)
152		goto err;
153
154	rtc->regs_soc = dev_remap_addr_name(dev, "rtc-soc");
155	if (!rtc->regs_soc)
156		goto err;
157
158	update_38x_mbus_timing_params(rtc);
159
160	return 0;
161err:
162	printf("%s: io address missing\n", dev->name);
163	return -ENODEV;
164}
165
166static const struct rtc_ops armada38x_rtc_ops = {
167	.get = armada38x_rtc_get,
168	.set = armada38x_rtc_set,
169	.reset = armada38x_rtc_reset,
170};
171
172static const struct udevice_id armada38x_rtc_ids[] = {
173	{ .compatible = "marvell,armada-380-rtc", .data = 0 },
174	{ }
175};
176
177U_BOOT_DRIVER(rtc_armada38x) = {
178	.name		= "rtc-armada38x",
179	.id		= UCLASS_RTC,
180	.of_match	= armada38x_rtc_ids,
181	.probe		= armada38x_probe,
182	.priv_auto	= sizeof(struct armada38x_rtc),
183	.ops		= &armada38x_rtc_ops,
184};
185