1 // SPDX-License-Identifier: GPL-2.0-only
3 * Real time clocks driver for MStar/SigmaStar SSD202D SoCs.
5 * (C) 2021 Daniel Palmer
6 * (C) 2023 Romain Perier
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>
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
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)
44 #define ISO_CTRL_MASK GENMASK(2, 0)
47 struct rtc_device
*rtc_dev
;
51 static u8
read_iso_en(void __iomem
*base
)
53 return readb(base
+ REG_RTC_TEST
) & 0x1;
56 static u8
read_iso_ctrl_ack(void __iomem
*base
)
58 return (readb(base
+ REG_ISOACK
) & ISO_CTRL_ACK_MASK
) >> ISO_CTRL_ACK_SHIFT
;
61 static int ssd202d_rtc_isoctrl(struct ssd202d_rtc
*priv
)
63 static const unsigned int sequence
[] = { 0x0, 0x1, 0x3, 0x7, 0x5, 0x1, 0x0 };
65 struct device
*dev
= &priv
->rtc_dev
->dev
;
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
72 for (i
= 0; i
< ARRAY_SIZE(sequence
); i
++) {
73 writeb(sequence
[i
] & ISO_CTRL_MASK
, priv
->base
+ REG_ISO_CTRL
);
75 ret
= read_poll_timeout(read_iso_ctrl_ack
, val
, val
== (i
% 2), 100,
76 20 * 100, true, priv
->base
);
78 dev_dbg(dev
, "Timeout waiting for ack byte %i (%x) of sequence\n", i
,
85 * At this point iso_en should be raised for 1ms
87 ret
= read_poll_timeout(read_iso_en
, val
, val
, 100, 22 * 100, true, priv
->base
);
89 dev_dbg(dev
, "Timeout waiting for iso_en\n");
94 static void ssd202d_rtc_read_reg(struct ssd202d_rtc
*priv
, unsigned int reg
,
95 unsigned int field
, unsigned int *base
)
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
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
);
108 l
= readw(priv
->base
+ REG_RDDATA_L
);
109 h
= readw(priv
->base
+ REG_RDDATA_H
);
111 *base
= (h
<< 16) | l
;
114 static void ssd202d_rtc_write_reg(struct ssd202d_rtc
*priv
, unsigned int reg
,
115 unsigned int field
, u32 base
)
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
);
128 static int ssd202d_rtc_read_counter(struct ssd202d_rtc
*priv
, unsigned int *counter
)
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
);
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
);
142 l
= readw(priv
->base
+ REG_RDCNT_L
);
143 h
= readw(priv
->base
+ REG_RDCNT_H
);
145 *counter
= (h
<< 16) | l
;
150 static int ssd202d_rtc_read_time(struct device
*dev
, struct rtc_time
*tm
)
152 struct ssd202d_rtc
*priv
= dev_get_drvdata(dev
);
153 unsigned int sw0
, base
, counter
;
157 /* Check that RTC is enabled by SW */
158 ssd202d_rtc_read_reg(priv
, REG_CTRL
, SW0_RD_BIT
, &sw0
);
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
);
169 seconds
= base
+ counter
;
171 rtc_time64_to_tm(seconds
, tm
);
176 static int ssd202d_rtc_reset_counter(struct ssd202d_rtc
*priv
)
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
);
189 static int ssd202d_rtc_set_time(struct device
*dev
, struct rtc_time
*tm
)
191 struct ssd202d_rtc
*priv
= dev_get_drvdata(dev
);
192 unsigned long seconds
= rtc_tm_to_time64(tm
);
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);
201 static const struct rtc_class_ops ssd202d_rtc_ops
= {
202 .read_time
= ssd202d_rtc_read_time
,
203 .set_time
= ssd202d_rtc_set_time
,
206 static int ssd202d_rtc_probe(struct platform_device
*pdev
)
208 struct device
*dev
= &pdev
->dev
;
209 struct ssd202d_rtc
*priv
;
211 priv
= devm_kzalloc(&pdev
->dev
, sizeof(struct ssd202d_rtc
), GFP_KERNEL
);
215 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
216 if (IS_ERR(priv
->base
))
217 return PTR_ERR(priv
->base
);
219 priv
->rtc_dev
= devm_rtc_allocate_device(dev
);
220 if (IS_ERR(priv
->rtc_dev
))
221 return PTR_ERR(priv
->rtc_dev
);
223 priv
->rtc_dev
->ops
= &ssd202d_rtc_ops
;
224 priv
->rtc_dev
->range_max
= U32_MAX
;
226 platform_set_drvdata(pdev
, priv
);
228 return devm_rtc_register_device(priv
->rtc_dev
);
231 static const struct of_device_id ssd202d_rtc_of_match_table
[] = {
232 { .compatible
= "mstar,ssd202d-rtc" },
235 MODULE_DEVICE_TABLE(of
, ssd202d_rtc_of_match_table
);
237 static struct platform_driver ssd202d_rtc_driver
= {
238 .probe
= ssd202d_rtc_probe
,
240 .name
= "ssd202d-rtc",
241 .of_match_table
= ssd202d_rtc_of_match_table
,
244 module_platform_driver(ssd202d_rtc_driver
);
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");