1be7d9c91SDaniel Palmer // SPDX-License-Identifier: GPL-2.0-only
2be7d9c91SDaniel Palmer /*
3be7d9c91SDaniel Palmer * Real time clocks driver for MStar/SigmaStar ARMv7 SoCs.
4be7d9c91SDaniel Palmer * Based on "Real Time Clock driver for msb252x." that was contained
5be7d9c91SDaniel Palmer * in various MStar kernels.
6be7d9c91SDaniel Palmer *
7be7d9c91SDaniel Palmer * (C) 2019 Daniel Palmer
8be7d9c91SDaniel Palmer * (C) 2021 Romain Perier
9be7d9c91SDaniel Palmer */
10be7d9c91SDaniel Palmer
11be7d9c91SDaniel Palmer #include <linux/clk.h>
12be7d9c91SDaniel Palmer #include <linux/delay.h>
1327ff63ebSAlexandre Belloni #include <linux/io.h>
14be7d9c91SDaniel Palmer #include <linux/module.h>
15be7d9c91SDaniel Palmer #include <linux/mod_devicetable.h>
16be7d9c91SDaniel Palmer #include <linux/platform_device.h>
17be7d9c91SDaniel Palmer #include <linux/rtc.h>
18be7d9c91SDaniel Palmer
19be7d9c91SDaniel Palmer /* Registers */
20be7d9c91SDaniel Palmer #define REG_RTC_CTRL 0x00
21be7d9c91SDaniel Palmer #define REG_RTC_FREQ_CW_L 0x04
22be7d9c91SDaniel Palmer #define REG_RTC_FREQ_CW_H 0x08
23be7d9c91SDaniel Palmer #define REG_RTC_LOAD_VAL_L 0x0C
24be7d9c91SDaniel Palmer #define REG_RTC_LOAD_VAL_H 0x10
25be7d9c91SDaniel Palmer #define REG_RTC_MATCH_VAL_L 0x14
26be7d9c91SDaniel Palmer #define REG_RTC_MATCH_VAL_H 0x18
27be7d9c91SDaniel Palmer #define REG_RTC_STATUS_INT 0x1C
28be7d9c91SDaniel Palmer #define REG_RTC_CNT_VAL_L 0x20
29be7d9c91SDaniel Palmer #define REG_RTC_CNT_VAL_H 0x24
30be7d9c91SDaniel Palmer
31be7d9c91SDaniel Palmer /* Control bits for REG_RTC_CTRL */
32be7d9c91SDaniel Palmer #define SOFT_RSTZ_BIT BIT(0)
33be7d9c91SDaniel Palmer #define CNT_EN_BIT BIT(1)
34be7d9c91SDaniel Palmer #define WRAP_EN_BIT BIT(2)
35be7d9c91SDaniel Palmer #define LOAD_EN_BIT BIT(3)
36be7d9c91SDaniel Palmer #define READ_EN_BIT BIT(4)
37be7d9c91SDaniel Palmer #define INT_MASK_BIT BIT(5)
38be7d9c91SDaniel Palmer #define INT_FORCE_BIT BIT(6)
39be7d9c91SDaniel Palmer #define INT_CLEAR_BIT BIT(7)
40be7d9c91SDaniel Palmer
41be7d9c91SDaniel Palmer /* Control bits for REG_RTC_STATUS_INT */
42be7d9c91SDaniel Palmer #define RAW_INT_BIT BIT(0)
43be7d9c91SDaniel Palmer #define ALM_INT_BIT BIT(1)
44be7d9c91SDaniel Palmer
45be7d9c91SDaniel Palmer struct msc313_rtc {
46be7d9c91SDaniel Palmer struct rtc_device *rtc_dev;
47be7d9c91SDaniel Palmer void __iomem *rtc_base;
48be7d9c91SDaniel Palmer };
49be7d9c91SDaniel Palmer
msc313_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alarm)50be7d9c91SDaniel Palmer static int msc313_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
51be7d9c91SDaniel Palmer {
52be7d9c91SDaniel Palmer struct msc313_rtc *priv = dev_get_drvdata(dev);
53be7d9c91SDaniel Palmer unsigned long seconds;
54be7d9c91SDaniel Palmer
55be7d9c91SDaniel Palmer seconds = readw(priv->rtc_base + REG_RTC_MATCH_VAL_L)
56f3606687SColin Ian King | ((unsigned long)readw(priv->rtc_base + REG_RTC_MATCH_VAL_H) << 16);
57be7d9c91SDaniel Palmer
58be7d9c91SDaniel Palmer rtc_time64_to_tm(seconds, &alarm->time);
59be7d9c91SDaniel Palmer
60be7d9c91SDaniel Palmer if (!(readw(priv->rtc_base + REG_RTC_CTRL) & INT_MASK_BIT))
61be7d9c91SDaniel Palmer alarm->enabled = 1;
62be7d9c91SDaniel Palmer
63be7d9c91SDaniel Palmer return 0;
64be7d9c91SDaniel Palmer }
65be7d9c91SDaniel Palmer
msc313_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)66be7d9c91SDaniel Palmer static int msc313_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
67be7d9c91SDaniel Palmer {
68be7d9c91SDaniel Palmer struct msc313_rtc *priv = dev_get_drvdata(dev);
69be7d9c91SDaniel Palmer u16 reg;
70be7d9c91SDaniel Palmer
71be7d9c91SDaniel Palmer reg = readw(priv->rtc_base + REG_RTC_CTRL);
72be7d9c91SDaniel Palmer if (enabled)
73be7d9c91SDaniel Palmer reg &= ~INT_MASK_BIT;
74be7d9c91SDaniel Palmer else
75be7d9c91SDaniel Palmer reg |= INT_MASK_BIT;
76be7d9c91SDaniel Palmer writew(reg, priv->rtc_base + REG_RTC_CTRL);
77be7d9c91SDaniel Palmer return 0;
78be7d9c91SDaniel Palmer }
79be7d9c91SDaniel Palmer
msc313_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alarm)80be7d9c91SDaniel Palmer static int msc313_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
81be7d9c91SDaniel Palmer {
82be7d9c91SDaniel Palmer struct msc313_rtc *priv = dev_get_drvdata(dev);
83be7d9c91SDaniel Palmer unsigned long seconds;
84be7d9c91SDaniel Palmer
85be7d9c91SDaniel Palmer seconds = rtc_tm_to_time64(&alarm->time);
86be7d9c91SDaniel Palmer writew((seconds & 0xFFFF), priv->rtc_base + REG_RTC_MATCH_VAL_L);
87be7d9c91SDaniel Palmer writew((seconds >> 16) & 0xFFFF, priv->rtc_base + REG_RTC_MATCH_VAL_H);
88be7d9c91SDaniel Palmer
89be7d9c91SDaniel Palmer msc313_rtc_alarm_irq_enable(dev, alarm->enabled);
90be7d9c91SDaniel Palmer
91be7d9c91SDaniel Palmer return 0;
92be7d9c91SDaniel Palmer }
93be7d9c91SDaniel Palmer
msc313_rtc_get_enabled(struct msc313_rtc * priv)94be7d9c91SDaniel Palmer static bool msc313_rtc_get_enabled(struct msc313_rtc *priv)
95be7d9c91SDaniel Palmer {
96be7d9c91SDaniel Palmer return readw(priv->rtc_base + REG_RTC_CTRL) & CNT_EN_BIT;
97be7d9c91SDaniel Palmer }
98be7d9c91SDaniel Palmer
msc313_rtc_set_enabled(struct msc313_rtc * priv)99be7d9c91SDaniel Palmer static void msc313_rtc_set_enabled(struct msc313_rtc *priv)
100be7d9c91SDaniel Palmer {
101be7d9c91SDaniel Palmer u16 reg;
102be7d9c91SDaniel Palmer
103be7d9c91SDaniel Palmer reg = readw(priv->rtc_base + REG_RTC_CTRL);
104be7d9c91SDaniel Palmer reg |= CNT_EN_BIT;
105be7d9c91SDaniel Palmer writew(reg, priv->rtc_base + REG_RTC_CTRL);
106be7d9c91SDaniel Palmer }
107be7d9c91SDaniel Palmer
msc313_rtc_read_time(struct device * dev,struct rtc_time * tm)108be7d9c91SDaniel Palmer static int msc313_rtc_read_time(struct device *dev, struct rtc_time *tm)
109be7d9c91SDaniel Palmer {
110be7d9c91SDaniel Palmer struct msc313_rtc *priv = dev_get_drvdata(dev);
111be7d9c91SDaniel Palmer u32 seconds;
112be7d9c91SDaniel Palmer u16 reg;
113be7d9c91SDaniel Palmer
114be7d9c91SDaniel Palmer if (!msc313_rtc_get_enabled(priv))
115be7d9c91SDaniel Palmer return -EINVAL;
116be7d9c91SDaniel Palmer
117be7d9c91SDaniel Palmer reg = readw(priv->rtc_base + REG_RTC_CTRL);
118be7d9c91SDaniel Palmer writew(reg | READ_EN_BIT, priv->rtc_base + REG_RTC_CTRL);
119be7d9c91SDaniel Palmer
120be7d9c91SDaniel Palmer /* Wait for HW latch done */
121be7d9c91SDaniel Palmer while (readw(priv->rtc_base + REG_RTC_CTRL) & READ_EN_BIT)
122be7d9c91SDaniel Palmer udelay(1);
123be7d9c91SDaniel Palmer
124be7d9c91SDaniel Palmer seconds = readw(priv->rtc_base + REG_RTC_CNT_VAL_L)
125f3606687SColin Ian King | ((unsigned long)readw(priv->rtc_base + REG_RTC_CNT_VAL_H) << 16);
126be7d9c91SDaniel Palmer
127be7d9c91SDaniel Palmer rtc_time64_to_tm(seconds, tm);
128be7d9c91SDaniel Palmer
129be7d9c91SDaniel Palmer return 0;
130be7d9c91SDaniel Palmer }
131be7d9c91SDaniel Palmer
msc313_rtc_set_time(struct device * dev,struct rtc_time * tm)132be7d9c91SDaniel Palmer static int msc313_rtc_set_time(struct device *dev, struct rtc_time *tm)
133be7d9c91SDaniel Palmer {
134be7d9c91SDaniel Palmer struct msc313_rtc *priv = dev_get_drvdata(dev);
135be7d9c91SDaniel Palmer unsigned long seconds;
136be7d9c91SDaniel Palmer u16 reg;
137be7d9c91SDaniel Palmer
138be7d9c91SDaniel Palmer seconds = rtc_tm_to_time64(tm);
139be7d9c91SDaniel Palmer writew(seconds & 0xFFFF, priv->rtc_base + REG_RTC_LOAD_VAL_L);
140be7d9c91SDaniel Palmer writew((seconds >> 16) & 0xFFFF, priv->rtc_base + REG_RTC_LOAD_VAL_H);
141be7d9c91SDaniel Palmer
142be7d9c91SDaniel Palmer /* Enable load for loading value into internal RTC counter */
143be7d9c91SDaniel Palmer reg = readw(priv->rtc_base + REG_RTC_CTRL);
144be7d9c91SDaniel Palmer writew(reg | LOAD_EN_BIT, priv->rtc_base + REG_RTC_CTRL);
145be7d9c91SDaniel Palmer
146be7d9c91SDaniel Palmer /* Wait for HW latch done */
147be7d9c91SDaniel Palmer while (readw(priv->rtc_base + REG_RTC_CTRL) & LOAD_EN_BIT)
148be7d9c91SDaniel Palmer udelay(1);
149be7d9c91SDaniel Palmer msc313_rtc_set_enabled(priv);
150be7d9c91SDaniel Palmer return 0;
151be7d9c91SDaniel Palmer }
152be7d9c91SDaniel Palmer
153be7d9c91SDaniel Palmer static const struct rtc_class_ops msc313_rtc_ops = {
154be7d9c91SDaniel Palmer .read_time = msc313_rtc_read_time,
155be7d9c91SDaniel Palmer .set_time = msc313_rtc_set_time,
156be7d9c91SDaniel Palmer .read_alarm = msc313_rtc_read_alarm,
157be7d9c91SDaniel Palmer .set_alarm = msc313_rtc_set_alarm,
158be7d9c91SDaniel Palmer .alarm_irq_enable = msc313_rtc_alarm_irq_enable,
159be7d9c91SDaniel Palmer };
160be7d9c91SDaniel Palmer
msc313_rtc_interrupt(s32 irq,void * dev_id)161be7d9c91SDaniel Palmer static irqreturn_t msc313_rtc_interrupt(s32 irq, void *dev_id)
162be7d9c91SDaniel Palmer {
163be7d9c91SDaniel Palmer struct msc313_rtc *priv = dev_get_drvdata(dev_id);
164be7d9c91SDaniel Palmer u16 reg;
165be7d9c91SDaniel Palmer
166be7d9c91SDaniel Palmer reg = readw(priv->rtc_base + REG_RTC_STATUS_INT);
167be7d9c91SDaniel Palmer if (!(reg & ALM_INT_BIT))
168be7d9c91SDaniel Palmer return IRQ_NONE;
169be7d9c91SDaniel Palmer
170be7d9c91SDaniel Palmer reg = readw(priv->rtc_base + REG_RTC_CTRL);
171be7d9c91SDaniel Palmer reg |= INT_CLEAR_BIT;
172be7d9c91SDaniel Palmer reg &= ~INT_FORCE_BIT;
173be7d9c91SDaniel Palmer writew(reg, priv->rtc_base + REG_RTC_CTRL);
174be7d9c91SDaniel Palmer
175be7d9c91SDaniel Palmer rtc_update_irq(priv->rtc_dev, 1, RTC_IRQF | RTC_AF);
176be7d9c91SDaniel Palmer
177be7d9c91SDaniel Palmer return IRQ_HANDLED;
178be7d9c91SDaniel Palmer }
179be7d9c91SDaniel Palmer
msc313_rtc_probe(struct platform_device * pdev)180be7d9c91SDaniel Palmer static int msc313_rtc_probe(struct platform_device *pdev)
181be7d9c91SDaniel Palmer {
182be7d9c91SDaniel Palmer struct device *dev = &pdev->dev;
183be7d9c91SDaniel Palmer struct msc313_rtc *priv;
184be7d9c91SDaniel Palmer unsigned long rate;
185be7d9c91SDaniel Palmer struct clk *clk;
186be7d9c91SDaniel Palmer int ret;
187be7d9c91SDaniel Palmer int irq;
188be7d9c91SDaniel Palmer
189be7d9c91SDaniel Palmer priv = devm_kzalloc(&pdev->dev, sizeof(struct msc313_rtc), GFP_KERNEL);
190be7d9c91SDaniel Palmer if (!priv)
191be7d9c91SDaniel Palmer return -ENOMEM;
192be7d9c91SDaniel Palmer
193be7d9c91SDaniel Palmer priv->rtc_base = devm_platform_ioremap_resource(pdev, 0);
194be7d9c91SDaniel Palmer if (IS_ERR(priv->rtc_base))
195be7d9c91SDaniel Palmer return PTR_ERR(priv->rtc_base);
196be7d9c91SDaniel Palmer
197be7d9c91SDaniel Palmer irq = platform_get_irq(pdev, 0);
198be7d9c91SDaniel Palmer if (irq < 0)
199be7d9c91SDaniel Palmer return -EINVAL;
200be7d9c91SDaniel Palmer
201be7d9c91SDaniel Palmer priv->rtc_dev = devm_rtc_allocate_device(dev);
202be7d9c91SDaniel Palmer if (IS_ERR(priv->rtc_dev))
203be7d9c91SDaniel Palmer return PTR_ERR(priv->rtc_dev);
204be7d9c91SDaniel Palmer
205be7d9c91SDaniel Palmer priv->rtc_dev->ops = &msc313_rtc_ops;
206be7d9c91SDaniel Palmer priv->rtc_dev->range_max = U32_MAX;
207be7d9c91SDaniel Palmer
208be7d9c91SDaniel Palmer ret = devm_request_irq(dev, irq, msc313_rtc_interrupt, IRQF_SHARED,
209be7d9c91SDaniel Palmer dev_name(&pdev->dev), &pdev->dev);
210be7d9c91SDaniel Palmer if (ret) {
211be7d9c91SDaniel Palmer dev_err(dev, "Could not request IRQ\n");
212be7d9c91SDaniel Palmer return ret;
213be7d9c91SDaniel Palmer }
214be7d9c91SDaniel Palmer
215*21b8a1ddSKees Cook clk = devm_clk_get_enabled(dev, NULL);
216be7d9c91SDaniel Palmer if (IS_ERR(clk)) {
217be7d9c91SDaniel Palmer dev_err(dev, "No input reference clock\n");
218be7d9c91SDaniel Palmer return PTR_ERR(clk);
219be7d9c91SDaniel Palmer }
220be7d9c91SDaniel Palmer
221be7d9c91SDaniel Palmer rate = clk_get_rate(clk);
222be7d9c91SDaniel Palmer writew(rate & 0xFFFF, priv->rtc_base + REG_RTC_FREQ_CW_L);
223be7d9c91SDaniel Palmer writew((rate >> 16) & 0xFFFF, priv->rtc_base + REG_RTC_FREQ_CW_H);
224be7d9c91SDaniel Palmer
225be7d9c91SDaniel Palmer platform_set_drvdata(pdev, priv);
226be7d9c91SDaniel Palmer
227be7d9c91SDaniel Palmer return devm_rtc_register_device(priv->rtc_dev);
228be7d9c91SDaniel Palmer }
229be7d9c91SDaniel Palmer
230be7d9c91SDaniel Palmer static const struct of_device_id msc313_rtc_of_match_table[] = {
231be7d9c91SDaniel Palmer { .compatible = "mstar,msc313-rtc" },
232be7d9c91SDaniel Palmer { }
233be7d9c91SDaniel Palmer };
234be7d9c91SDaniel Palmer MODULE_DEVICE_TABLE(of, msc313_rtc_of_match_table);
235be7d9c91SDaniel Palmer
236be7d9c91SDaniel Palmer static struct platform_driver msc313_rtc_driver = {
237be7d9c91SDaniel Palmer .probe = msc313_rtc_probe,
238be7d9c91SDaniel Palmer .driver = {
239be7d9c91SDaniel Palmer .name = "msc313-rtc",
240be7d9c91SDaniel Palmer .of_match_table = msc313_rtc_of_match_table,
241be7d9c91SDaniel Palmer },
242be7d9c91SDaniel Palmer };
243be7d9c91SDaniel Palmer
244be7d9c91SDaniel Palmer module_platform_driver(msc313_rtc_driver);
245be7d9c91SDaniel Palmer
246be7d9c91SDaniel Palmer MODULE_AUTHOR("Daniel Palmer <daniel@thingy.jp>");
247be7d9c91SDaniel Palmer MODULE_AUTHOR("Romain Perier <romain.perier@gmail.com>");
248be7d9c91SDaniel Palmer MODULE_DESCRIPTION("MStar RTC Driver");
249be7d9c91SDaniel Palmer MODULE_LICENSE("GPL v2");
250