1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // Copyright 2022 NXP. |
4 | |
5 | #include <linux/init.h> |
6 | #include <linux/io.h> |
7 | #include <linux/kernel.h> |
8 | #include <linux/mfd/syscon.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/pm_wakeirq.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/rtc.h> |
15 | |
16 | #define BBNSM_CTRL 0x8 |
17 | #define BBNSM_INT_EN 0x10 |
18 | #define BBNSM_EVENTS 0x14 |
19 | #define BBNSM_RTC_LS 0x40 |
20 | #define BBNSM_RTC_MS 0x44 |
21 | #define BBNSM_TA 0x50 |
22 | |
23 | #define RTC_EN 0x2 |
24 | #define RTC_EN_MSK 0x3 |
25 | #define TA_EN (0x2 << 2) |
26 | #define TA_DIS (0x1 << 2) |
27 | #define TA_EN_MSK (0x3 << 2) |
28 | #define RTC_INT_EN 0x2 |
29 | #define TA_INT_EN (0x2 << 2) |
30 | |
31 | #define BBNSM_EVENT_TA (0x2 << 2) |
32 | |
33 | #define CNTR_TO_SECS_SH 15 |
34 | |
35 | struct bbnsm_rtc { |
36 | struct rtc_device *rtc; |
37 | struct regmap *regmap; |
38 | int irq; |
39 | struct clk *clk; |
40 | }; |
41 | |
42 | static u32 bbnsm_read_counter(struct bbnsm_rtc *bbnsm) |
43 | { |
44 | u32 rtc_msb, rtc_lsb; |
45 | unsigned int timeout = 100; |
46 | u32 time; |
47 | u32 tmp = 0; |
48 | |
49 | do { |
50 | time = tmp; |
51 | /* read the msb */ |
52 | regmap_read(map: bbnsm->regmap, BBNSM_RTC_MS, val: &rtc_msb); |
53 | /* read the lsb */ |
54 | regmap_read(map: bbnsm->regmap, BBNSM_RTC_LS, val: &rtc_lsb); |
55 | /* convert to seconds */ |
56 | tmp = (rtc_msb << 17) | (rtc_lsb >> 15); |
57 | } while (tmp != time && --timeout); |
58 | |
59 | return time; |
60 | } |
61 | |
62 | static int bbnsm_rtc_read_time(struct device *dev, struct rtc_time *tm) |
63 | { |
64 | struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); |
65 | unsigned long time; |
66 | u32 val; |
67 | |
68 | regmap_read(map: bbnsm->regmap, BBNSM_CTRL, val: &val); |
69 | if ((val & RTC_EN_MSK) != RTC_EN) |
70 | return -EINVAL; |
71 | |
72 | time = bbnsm_read_counter(bbnsm); |
73 | rtc_time64_to_tm(time, tm); |
74 | |
75 | return 0; |
76 | } |
77 | |
78 | static int bbnsm_rtc_set_time(struct device *dev, struct rtc_time *tm) |
79 | { |
80 | struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); |
81 | unsigned long time = rtc_tm_to_time64(tm); |
82 | |
83 | /* disable the RTC first */ |
84 | regmap_update_bits(map: bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, val: 0); |
85 | |
86 | /* write the 32bit sec time to 47 bit timer counter, leaving 15 LSBs blank */ |
87 | regmap_write(map: bbnsm->regmap, BBNSM_RTC_LS, val: time << CNTR_TO_SECS_SH); |
88 | regmap_write(map: bbnsm->regmap, BBNSM_RTC_MS, val: time >> (32 - CNTR_TO_SECS_SH)); |
89 | |
90 | /* Enable the RTC again */ |
91 | regmap_update_bits(map: bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, RTC_EN); |
92 | |
93 | return 0; |
94 | } |
95 | |
96 | static int bbnsm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
97 | { |
98 | struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); |
99 | u32 bbnsm_events, bbnsm_ta; |
100 | |
101 | regmap_read(map: bbnsm->regmap, BBNSM_TA, val: &bbnsm_ta); |
102 | rtc_time64_to_tm(time: bbnsm_ta, tm: &alrm->time); |
103 | |
104 | regmap_read(map: bbnsm->regmap, BBNSM_EVENTS, val: &bbnsm_events); |
105 | alrm->pending = (bbnsm_events & BBNSM_EVENT_TA) ? 1 : 0; |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static int bbnsm_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) |
111 | { |
112 | struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); |
113 | |
114 | /* enable the alarm event */ |
115 | regmap_update_bits(map: bbnsm->regmap, BBNSM_CTRL, TA_EN_MSK, val: enable ? TA_EN : TA_DIS); |
116 | /* enable the alarm interrupt */ |
117 | regmap_update_bits(map: bbnsm->regmap, BBNSM_INT_EN, TA_EN_MSK, val: enable ? TA_EN : TA_DIS); |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | static int bbnsm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
123 | { |
124 | struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); |
125 | unsigned long time = rtc_tm_to_time64(tm: &alrm->time); |
126 | |
127 | /* disable the alarm */ |
128 | regmap_update_bits(map: bbnsm->regmap, BBNSM_CTRL, TA_EN, TA_EN); |
129 | |
130 | /* write the seconds to TA */ |
131 | regmap_write(map: bbnsm->regmap, BBNSM_TA, val: time); |
132 | |
133 | return bbnsm_rtc_alarm_irq_enable(dev, enable: alrm->enabled); |
134 | } |
135 | |
136 | static const struct rtc_class_ops bbnsm_rtc_ops = { |
137 | .read_time = bbnsm_rtc_read_time, |
138 | .set_time = bbnsm_rtc_set_time, |
139 | .read_alarm = bbnsm_rtc_read_alarm, |
140 | .set_alarm = bbnsm_rtc_set_alarm, |
141 | .alarm_irq_enable = bbnsm_rtc_alarm_irq_enable, |
142 | }; |
143 | |
144 | static irqreturn_t bbnsm_rtc_irq_handler(int irq, void *dev_id) |
145 | { |
146 | struct device *dev = dev_id; |
147 | struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); |
148 | u32 val; |
149 | |
150 | regmap_read(map: bbnsm->regmap, BBNSM_EVENTS, val: &val); |
151 | if (val & BBNSM_EVENT_TA) { |
152 | bbnsm_rtc_alarm_irq_enable(dev, enable: false); |
153 | /* clear the alarm event */ |
154 | regmap_write_bits(map: bbnsm->regmap, BBNSM_EVENTS, TA_EN_MSK, BBNSM_EVENT_TA); |
155 | rtc_update_irq(rtc: bbnsm->rtc, num: 1, RTC_AF | RTC_IRQF); |
156 | |
157 | return IRQ_HANDLED; |
158 | } |
159 | |
160 | return IRQ_NONE; |
161 | } |
162 | |
163 | static int bbnsm_rtc_probe(struct platform_device *pdev) |
164 | { |
165 | struct device_node *np = pdev->dev.of_node; |
166 | struct bbnsm_rtc *bbnsm; |
167 | int ret; |
168 | |
169 | bbnsm = devm_kzalloc(dev: &pdev->dev, size: sizeof(*bbnsm), GFP_KERNEL); |
170 | if (!bbnsm) |
171 | return -ENOMEM; |
172 | |
173 | bbnsm->rtc = devm_rtc_allocate_device(dev: &pdev->dev); |
174 | if (IS_ERR(ptr: bbnsm->rtc)) |
175 | return PTR_ERR(ptr: bbnsm->rtc); |
176 | |
177 | bbnsm->regmap = syscon_node_to_regmap(np: np->parent); |
178 | if (IS_ERR(ptr: bbnsm->regmap)) { |
179 | dev_dbg(&pdev->dev, "bbnsm get regmap failed\n" ); |
180 | return PTR_ERR(ptr: bbnsm->regmap); |
181 | } |
182 | |
183 | bbnsm->irq = platform_get_irq(pdev, 0); |
184 | if (bbnsm->irq < 0) |
185 | return bbnsm->irq; |
186 | |
187 | platform_set_drvdata(pdev, data: bbnsm); |
188 | |
189 | /* clear all the pending events */ |
190 | regmap_write(map: bbnsm->regmap, BBNSM_EVENTS, val: 0x7A); |
191 | |
192 | device_init_wakeup(dev: &pdev->dev, enable: true); |
193 | dev_pm_set_wake_irq(dev: &pdev->dev, irq: bbnsm->irq); |
194 | |
195 | ret = devm_request_irq(dev: &pdev->dev, irq: bbnsm->irq, handler: bbnsm_rtc_irq_handler, |
196 | IRQF_SHARED, devname: "rtc alarm" , dev_id: &pdev->dev); |
197 | if (ret) { |
198 | dev_err(&pdev->dev, "failed to request irq %d: %d\n" , |
199 | bbnsm->irq, ret); |
200 | return ret; |
201 | } |
202 | |
203 | bbnsm->rtc->ops = &bbnsm_rtc_ops; |
204 | bbnsm->rtc->range_max = U32_MAX; |
205 | |
206 | return devm_rtc_register_device(bbnsm->rtc); |
207 | } |
208 | |
209 | static const struct of_device_id bbnsm_dt_ids[] = { |
210 | { .compatible = "nxp,imx93-bbnsm-rtc" }, |
211 | { /* sentinel */ }, |
212 | }; |
213 | MODULE_DEVICE_TABLE(of, bbnsm_dt_ids); |
214 | |
215 | static struct platform_driver bbnsm_rtc_driver = { |
216 | .driver = { |
217 | .name = "bbnsm_rtc" , |
218 | .of_match_table = bbnsm_dt_ids, |
219 | }, |
220 | .probe = bbnsm_rtc_probe, |
221 | }; |
222 | module_platform_driver(bbnsm_rtc_driver); |
223 | |
224 | MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>" ); |
225 | MODULE_DESCRIPTION("NXP BBNSM RTC Driver" ); |
226 | MODULE_LICENSE("GPL" ); |
227 | |