1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Real time clocks driver for MStar/SigmaStar SSD202D SoCs.
4 *
5 * (C) 2021 Daniel Palmer
6 * (C) 2023 Romain Perier
7 */
8
9#include <linux/clk.h>
10#include <linux/delay.h>
11#include <linux/module.h>
12#include <linux/mod_devicetable.h>
13#include <linux/platform_device.h>
14#include <linux/rtc.h>
15#include <linux/regmap.h>
16#include <linux/pm.h>
17
18#define REG_CTRL 0x0
19#define REG_CTRL1 0x4
20#define REG_ISO_CTRL 0xc
21#define REG_WRDATA_L 0x10
22#define REG_WRDATA_H 0x14
23#define REG_ISOACK 0x20
24#define REG_RDDATA_L 0x24
25#define REG_RDDATA_H 0x28
26#define REG_RDCNT_L 0x30
27#define REG_RDCNT_H 0x34
28#define REG_CNT_TRIG 0x38
29#define REG_PWRCTRL 0x3c
30#define REG_RTC_TEST 0x54
31
32#define CNT_RD_TRIG_BIT BIT(0)
33#define CNT_RD_BIT BIT(0)
34#define BASE_WR_BIT BIT(1)
35#define BASE_RD_BIT BIT(2)
36#define CNT_RST_BIT BIT(3)
37#define ISO_CTRL_ACK_MASK BIT(3)
38#define ISO_CTRL_ACK_SHIFT 3
39#define SW0_WR_BIT BIT(5)
40#define SW1_WR_BIT BIT(6)
41#define SW0_RD_BIT BIT(7)
42#define SW1_RD_BIT BIT(8)
43
44#define ISO_CTRL_MASK GENMASK(2, 0)
45
46struct ssd202d_rtc {
47 struct rtc_device *rtc_dev;
48 void __iomem *base;
49};
50
51static u8 read_iso_en(void __iomem *base)
52{
53 return readb(addr: base + REG_RTC_TEST) & 0x1;
54}
55
56static u8 read_iso_ctrl_ack(void __iomem *base)
57{
58 return (readb(addr: base + REG_ISOACK) & ISO_CTRL_ACK_MASK) >> ISO_CTRL_ACK_SHIFT;
59}
60
61static int ssd202d_rtc_isoctrl(struct ssd202d_rtc *priv)
62{
63 static const unsigned int sequence[] = { 0x0, 0x1, 0x3, 0x7, 0x5, 0x1, 0x0 };
64 unsigned int val;
65 struct device *dev = &priv->rtc_dev->dev;
66 int i, ret;
67
68 /*
69 * This gates iso_en by writing a special sequence of bytes to iso_ctrl
70 * and ensuring that it has been correctly applied by reading iso_ctrl_ack
71 */
72 for (i = 0; i < ARRAY_SIZE(sequence); i++) {
73 writeb(val: sequence[i] & ISO_CTRL_MASK, addr: priv->base + REG_ISO_CTRL);
74
75 ret = read_poll_timeout(read_iso_ctrl_ack, val, val == (i % 2), 100,
76 20 * 100, true, priv->base);
77 if (ret) {
78 dev_dbg(dev, "Timeout waiting for ack byte %i (%x) of sequence\n", i,
79 sequence[i]);
80 return ret;
81 }
82 }
83
84 /*
85 * At this point iso_en should be raised for 1ms
86 */
87 ret = read_poll_timeout(read_iso_en, val, val, 100, 22 * 100, true, priv->base);
88 if (ret)
89 dev_dbg(dev, "Timeout waiting for iso_en\n");
90 mdelay(2);
91 return 0;
92}
93
94static void ssd202d_rtc_read_reg(struct ssd202d_rtc *priv, unsigned int reg,
95 unsigned int field, unsigned int *base)
96{
97 unsigned int l, h;
98 u16 val;
99
100 /* Ask for the content of an RTC value into RDDATA by gating iso_en,
101 * then iso_en is gated and the content of RDDATA can be read
102 */
103 val = readw(addr: priv->base + reg);
104 writew(val: val | field, addr: priv->base + reg);
105 ssd202d_rtc_isoctrl(priv);
106 writew(val: val & ~field, addr: priv->base + reg);
107
108 l = readw(addr: priv->base + REG_RDDATA_L);
109 h = readw(addr: priv->base + REG_RDDATA_H);
110
111 *base = (h << 16) | l;
112}
113
114static void ssd202d_rtc_write_reg(struct ssd202d_rtc *priv, unsigned int reg,
115 unsigned int field, u32 base)
116{
117 u16 val;
118
119 /* Set the content of an RTC value from WRDATA by gating iso_en */
120 val = readw(addr: priv->base + reg);
121 writew(val: val | field, addr: priv->base + reg);
122 writew(val: base, addr: priv->base + REG_WRDATA_L);
123 writew(val: base >> 16, addr: priv->base + REG_WRDATA_H);
124 ssd202d_rtc_isoctrl(priv);
125 writew(val: val & ~field, addr: priv->base + reg);
126}
127
128static int ssd202d_rtc_read_counter(struct ssd202d_rtc *priv, unsigned int *counter)
129{
130 unsigned int l, h;
131 u16 val;
132
133 val = readw(addr: priv->base + REG_CTRL1);
134 writew(val: val | CNT_RD_BIT, addr: priv->base + REG_CTRL1);
135 ssd202d_rtc_isoctrl(priv);
136 writew(val: val & ~CNT_RD_BIT, addr: priv->base + REG_CTRL1);
137
138 val = readw(addr: priv->base + REG_CTRL1);
139 writew(val: val | CNT_RD_TRIG_BIT, addr: priv->base + REG_CNT_TRIG);
140 writew(val: val & ~CNT_RD_TRIG_BIT, addr: priv->base + REG_CNT_TRIG);
141
142 l = readw(addr: priv->base + REG_RDCNT_L);
143 h = readw(addr: priv->base + REG_RDCNT_H);
144
145 *counter = (h << 16) | l;
146
147 return 0;
148}
149
150static int ssd202d_rtc_read_time(struct device *dev, struct rtc_time *tm)
151{
152 struct ssd202d_rtc *priv = dev_get_drvdata(dev);
153 unsigned int sw0, base, counter;
154 u32 seconds;
155 int ret;
156
157 /* Check that RTC is enabled by SW */
158 ssd202d_rtc_read_reg(priv, REG_CTRL, SW0_RD_BIT, base: &sw0);
159 if (sw0 != 1)
160 return -EINVAL;
161
162 /* Get RTC base value from RDDATA */
163 ssd202d_rtc_read_reg(priv, REG_CTRL, BASE_RD_BIT, base: &base);
164 /* Get RTC counter value from RDDATA */
165 ret = ssd202d_rtc_read_counter(priv, counter: &counter);
166 if (ret)
167 return ret;
168
169 seconds = base + counter;
170
171 rtc_time64_to_tm(time: seconds, tm);
172
173 return 0;
174}
175
176static int ssd202d_rtc_reset_counter(struct ssd202d_rtc *priv)
177{
178 u16 val;
179
180 val = readw(addr: priv->base + REG_CTRL);
181 writew(val: val | CNT_RST_BIT, addr: priv->base + REG_CTRL);
182 ssd202d_rtc_isoctrl(priv);
183 writew(val: val & ~CNT_RST_BIT, addr: priv->base + REG_CTRL);
184 ssd202d_rtc_isoctrl(priv);
185
186 return 0;
187}
188
189static int ssd202d_rtc_set_time(struct device *dev, struct rtc_time *tm)
190{
191 struct ssd202d_rtc *priv = dev_get_drvdata(dev);
192 unsigned long seconds = rtc_tm_to_time64(tm);
193
194 ssd202d_rtc_write_reg(priv, REG_CTRL, BASE_WR_BIT, base: seconds);
195 ssd202d_rtc_reset_counter(priv);
196 ssd202d_rtc_write_reg(priv, REG_CTRL, SW0_WR_BIT, base: 1);
197
198 return 0;
199}
200
201static const struct rtc_class_ops ssd202d_rtc_ops = {
202 .read_time = ssd202d_rtc_read_time,
203 .set_time = ssd202d_rtc_set_time,
204};
205
206static int ssd202d_rtc_probe(struct platform_device *pdev)
207{
208 struct device *dev = &pdev->dev;
209 struct ssd202d_rtc *priv;
210
211 priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct ssd202d_rtc), GFP_KERNEL);
212 if (!priv)
213 return -ENOMEM;
214
215 priv->base = devm_platform_ioremap_resource(pdev, index: 0);
216 if (IS_ERR(ptr: priv->base))
217 return PTR_ERR(ptr: priv->base);
218
219 priv->rtc_dev = devm_rtc_allocate_device(dev);
220 if (IS_ERR(ptr: priv->rtc_dev))
221 return PTR_ERR(ptr: priv->rtc_dev);
222
223 priv->rtc_dev->ops = &ssd202d_rtc_ops;
224 priv->rtc_dev->range_max = U32_MAX;
225
226 platform_set_drvdata(pdev, data: priv);
227
228 return devm_rtc_register_device(priv->rtc_dev);
229}
230
231static const struct of_device_id ssd202d_rtc_of_match_table[] = {
232 { .compatible = "mstar,ssd202d-rtc" },
233 { }
234};
235MODULE_DEVICE_TABLE(of, ssd202d_rtc_of_match_table);
236
237static struct platform_driver ssd202d_rtc_driver = {
238 .probe = ssd202d_rtc_probe,
239 .driver = {
240 .name = "ssd202d-rtc",
241 .of_match_table = ssd202d_rtc_of_match_table,
242 },
243};
244module_platform_driver(ssd202d_rtc_driver);
245
246MODULE_AUTHOR("Daniel Palmer <daniel@thingy.jp>");
247MODULE_AUTHOR("Romain Perier <romain.perier@gmail.com>");
248MODULE_DESCRIPTION("MStar SSD202D RTC Driver");
249MODULE_LICENSE("GPL");
250

source code of linux/drivers/rtc/rtc-ssd202d.c