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