GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / rtc / rtc-ssd202d.c
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
46 struct ssd202d_rtc {
47         struct rtc_device *rtc_dev;
48         void __iomem *base;
49 };
50
51 static u8 read_iso_en(void __iomem *base)
52 {
53         return readb(base + REG_RTC_TEST) & 0x1;
54 }
55
56 static u8 read_iso_ctrl_ack(void __iomem *base)
57 {
58         return (readb(base + REG_ISOACK) & ISO_CTRL_ACK_MASK) >> ISO_CTRL_ACK_SHIFT;
59 }
60
61 static 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(sequence[i] & ISO_CTRL_MASK, 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
94 static 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(priv->base + reg);
104         writew(val | field, priv->base + reg);
105         ssd202d_rtc_isoctrl(priv);
106         writew(val & ~field, priv->base + reg);
107
108         l = readw(priv->base + REG_RDDATA_L);
109         h = readw(priv->base + REG_RDDATA_H);
110
111         *base = (h << 16) | l;
112 }
113
114 static 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(priv->base + reg);
121         writew(val | field, priv->base + reg);
122         writew(base, priv->base + REG_WRDATA_L);
123         writew(base >> 16, priv->base + REG_WRDATA_H);
124         ssd202d_rtc_isoctrl(priv);
125         writew(val & ~field, priv->base + reg);
126 }
127
128 static 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(priv->base + REG_CTRL1);
134         writew(val | CNT_RD_BIT, priv->base + REG_CTRL1);
135         ssd202d_rtc_isoctrl(priv);
136         writew(val & ~CNT_RD_BIT, priv->base + REG_CTRL1);
137
138         val = readw(priv->base + REG_CTRL1);
139         writew(val | CNT_RD_TRIG_BIT, priv->base + REG_CNT_TRIG);
140         writew(val & ~CNT_RD_TRIG_BIT, priv->base + REG_CNT_TRIG);
141
142         l = readw(priv->base + REG_RDCNT_L);
143         h = readw(priv->base + REG_RDCNT_H);
144
145         *counter = (h << 16) | l;
146
147         return 0;
148 }
149
150 static 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, &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);
164         /* Get RTC counter value from RDDATA */
165         ret = ssd202d_rtc_read_counter(priv, &counter);
166         if (ret)
167                 return ret;
168
169         seconds = base + counter;
170
171         rtc_time64_to_tm(seconds, tm);
172
173         return 0;
174 }
175
176 static int ssd202d_rtc_reset_counter(struct ssd202d_rtc *priv)
177 {
178         u16 val;
179
180         val = readw(priv->base + REG_CTRL);
181         writew(val | CNT_RST_BIT, priv->base + REG_CTRL);
182         ssd202d_rtc_isoctrl(priv);
183         writew(val & ~CNT_RST_BIT, priv->base + REG_CTRL);
184         ssd202d_rtc_isoctrl(priv);
185
186         return 0;
187 }
188
189 static 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, seconds);
195         ssd202d_rtc_reset_counter(priv);
196         ssd202d_rtc_write_reg(priv, REG_CTRL, SW0_WR_BIT, 1);
197
198         return 0;
199 }
200
201 static const struct rtc_class_ops ssd202d_rtc_ops = {
202         .read_time = ssd202d_rtc_read_time,
203         .set_time = ssd202d_rtc_set_time,
204 };
205
206 static 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(&pdev->dev, sizeof(struct ssd202d_rtc), GFP_KERNEL);
212         if (!priv)
213                 return -ENOMEM;
214
215         priv->base = devm_platform_ioremap_resource(pdev, 0);
216         if (IS_ERR(priv->base))
217                 return PTR_ERR(priv->base);
218
219         priv->rtc_dev = devm_rtc_allocate_device(dev);
220         if (IS_ERR(priv->rtc_dev))
221                 return PTR_ERR(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, priv);
227
228         return devm_rtc_register_device(priv->rtc_dev);
229 }
230
231 static const struct of_device_id ssd202d_rtc_of_match_table[] = {
232         { .compatible = "mstar,ssd202d-rtc" },
233         { }
234 };
235 MODULE_DEVICE_TABLE(of, ssd202d_rtc_of_match_table);
236
237 static 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 };
244 module_platform_driver(ssd202d_rtc_driver);
245
246 MODULE_AUTHOR("Daniel Palmer <daniel@thingy.jp>");
247 MODULE_AUTHOR("Romain Perier <romain.perier@gmail.com>");
248 MODULE_DESCRIPTION("MStar SSD202D RTC Driver");
249 MODULE_LICENSE("GPL");