]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7f3923a1 CV |
2 | /* |
3 | * An SPI driver for the Philips PCF2123 RTC | |
4 | * Copyright 2009 Cyber Switching, Inc. | |
5 | * | |
6 | * Author: Chris Verges <chrisv@cyberswitching.com> | |
7 | * Maintainers: http://www.cyberswitching.com | |
8 | * | |
9 | * based on the RS5C348 driver in this same directory. | |
10 | * | |
11 | * Thanks to Christian Pellegrin <chripell@fsfe.org> for | |
12 | * the sysfs contributions to this driver. | |
13 | * | |
7f3923a1 CV |
14 | * Please note that the CS is active high, so platform data |
15 | * should look something like: | |
16 | * | |
17 | * static struct spi_board_info ek_spi_devices[] = { | |
369015fb SK |
18 | * ... |
19 | * { | |
20 | * .modalias = "rtc-pcf2123", | |
21 | * .chip_select = 1, | |
22 | * .controller_data = (void *)AT91_PIN_PA10, | |
7f3923a1 CV |
23 | * .max_speed_hz = 1000 * 1000, |
24 | * .mode = SPI_CS_HIGH, | |
25 | * .bus_num = 0, | |
26 | * }, | |
27 | * ... | |
28 | *}; | |
7f3923a1 CV |
29 | */ |
30 | ||
31 | #include <linux/bcd.h> | |
32 | #include <linux/delay.h> | |
33 | #include <linux/device.h> | |
34 | #include <linux/errno.h> | |
35 | #include <linux/init.h> | |
36 | #include <linux/kernel.h> | |
3fc70077 | 37 | #include <linux/of.h> |
7f3923a1 | 38 | #include <linux/string.h> |
5a0e3ad6 | 39 | #include <linux/slab.h> |
7f3923a1 CV |
40 | #include <linux/rtc.h> |
41 | #include <linux/spi/spi.h> | |
2113852b | 42 | #include <linux/module.h> |
790d0339 | 43 | #include <linux/regmap.h> |
7f3923a1 | 44 | |
245cb74b | 45 | /* REGISTERS */ |
7f3923a1 CV |
46 | #define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */ |
47 | #define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */ | |
48 | #define PCF2123_REG_SC (0x02) /* datetime */ | |
49 | #define PCF2123_REG_MN (0x03) | |
50 | #define PCF2123_REG_HR (0x04) | |
51 | #define PCF2123_REG_DM (0x05) | |
52 | #define PCF2123_REG_DW (0x06) | |
53 | #define PCF2123_REG_MO (0x07) | |
54 | #define PCF2123_REG_YR (0x08) | |
245cb74b JC |
55 | #define PCF2123_REG_ALRM_MN (0x09) /* Alarm Registers */ |
56 | #define PCF2123_REG_ALRM_HR (0x0a) | |
57 | #define PCF2123_REG_ALRM_DM (0x0b) | |
58 | #define PCF2123_REG_ALRM_DW (0x0c) | |
59 | #define PCF2123_REG_OFFSET (0x0d) /* Clock Rate Offset Register */ | |
60 | #define PCF2123_REG_TMR_CLKOUT (0x0e) /* Timer Registers */ | |
61 | #define PCF2123_REG_CTDWN_TMR (0x0f) | |
62 | ||
63 | /* PCF2123_REG_CTRL1 BITS */ | |
64 | #define CTRL1_CLEAR (0) /* Clear */ | |
65 | #define CTRL1_CORR_INT BIT(1) /* Correction irq enable */ | |
66 | #define CTRL1_12_HOUR BIT(2) /* 12 hour time */ | |
67 | #define CTRL1_SW_RESET (BIT(3) | BIT(4) | BIT(6)) /* Software reset */ | |
68 | #define CTRL1_STOP BIT(5) /* Stop the clock */ | |
69 | #define CTRL1_EXT_TEST BIT(7) /* External clock test mode */ | |
70 | ||
71 | /* PCF2123_REG_CTRL2 BITS */ | |
72 | #define CTRL2_TIE BIT(0) /* Countdown timer irq enable */ | |
73 | #define CTRL2_AIE BIT(1) /* Alarm irq enable */ | |
74 | #define CTRL2_TF BIT(2) /* Countdown timer flag */ | |
75 | #define CTRL2_AF BIT(3) /* Alarm flag */ | |
76 | #define CTRL2_TI_TP BIT(4) /* Irq pin generates pulse */ | |
77 | #define CTRL2_MSF BIT(5) /* Minute or second irq flag */ | |
78 | #define CTRL2_SI BIT(6) /* Second irq enable */ | |
79 | #define CTRL2_MI BIT(7) /* Minute irq enable */ | |
80 | ||
81 | /* PCF2123_REG_SC BITS */ | |
82 | #define OSC_HAS_STOPPED BIT(7) /* Clock has been stopped */ | |
83 | ||
84 | /* PCF2123_REG_ALRM_XX BITS */ | |
5bdf40da | 85 | #define ALRM_DISABLE BIT(7) /* MN, HR, DM, or DW alarm matching */ |
245cb74b JC |
86 | |
87 | /* PCF2123_REG_TMR_CLKOUT BITS */ | |
88 | #define CD_TMR_4096KHZ (0) /* 4096 KHz countdown timer */ | |
89 | #define CD_TMR_64HZ (1) /* 64 Hz countdown timer */ | |
90 | #define CD_TMR_1HZ (2) /* 1 Hz countdown timer */ | |
91 | #define CD_TMR_60th_HZ (3) /* 60th Hz countdown timer */ | |
92 | #define CD_TMR_TE BIT(3) /* Countdown timer enable */ | |
93 | ||
94 | /* PCF2123_REG_OFFSET BITS */ | |
82df3e04 | 95 | #define OFFSET_SIGN_BIT 6 /* 2's complement sign bit */ |
245cb74b | 96 | #define OFFSET_COARSE BIT(7) /* Coarse mode offset */ |
bae2f647 | 97 | #define OFFSET_STEP (2170) /* Offset step in parts per billion */ |
790d0339 | 98 | #define OFFSET_MASK GENMASK(6, 0) /* Offset value */ |
245cb74b JC |
99 | |
100 | /* READ/WRITE ADDRESS BITS */ | |
101 | #define PCF2123_WRITE BIT(4) | |
102 | #define PCF2123_READ (BIT(4) | BIT(7)) | |
7f3923a1 | 103 | |
7f3923a1 CV |
104 | |
105 | static struct spi_driver pcf2123_driver; | |
106 | ||
9126a2b1 | 107 | struct pcf2123_data { |
7f3923a1 | 108 | struct rtc_device *rtc; |
790d0339 | 109 | struct regmap *map; |
7f3923a1 CV |
110 | }; |
111 | ||
790d0339 DH |
112 | static const struct regmap_config pcf2123_regmap_config = { |
113 | .reg_bits = 8, | |
114 | .val_bits = 8, | |
115 | .read_flag_mask = PCF2123_READ, | |
116 | .write_flag_mask = PCF2123_WRITE, | |
117 | .max_register = PCF2123_REG_CTDWN_TMR, | |
118 | }; | |
7f3923a1 | 119 | |
bae2f647 | 120 | static int pcf2123_read_offset(struct device *dev, long *offset) |
83ab7dad | 121 | { |
9126a2b1 | 122 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
790d0339 DH |
123 | int ret, val; |
124 | unsigned int reg; | |
7f3923a1 | 125 | |
9126a2b1 | 126 | ret = regmap_read(pcf2123->map, PCF2123_REG_OFFSET, ®); |
4c5591c1 JH |
127 | if (ret) |
128 | return ret; | |
129 | ||
790d0339 | 130 | val = sign_extend32((reg & OFFSET_MASK), OFFSET_SIGN_BIT); |
bae2f647 JC |
131 | |
132 | if (reg & OFFSET_COARSE) | |
790d0339 | 133 | val *= 2; |
bae2f647 | 134 | |
790d0339 | 135 | *offset = ((long)val) * OFFSET_STEP; |
bae2f647 JC |
136 | |
137 | return 0; | |
138 | } | |
139 | ||
140 | /* | |
141 | * The offset register is a 7 bit signed value with a coarse bit in bit 7. | |
142 | * The main difference between the two is normal offset adjusts the first | |
143 | * second of n minutes every other hour, with 61, 62 and 63 being shoved | |
144 | * into the 60th minute. | |
145 | * The coarse adjustment does the same, but every hour. | |
146 | * the two overlap, with every even normal offset value corresponding | |
147 | * to a coarse offset. Based on this algorithm, it seems that despite the | |
148 | * name, coarse offset is a better fit for overlapping values. | |
149 | */ | |
150 | static int pcf2123_set_offset(struct device *dev, long offset) | |
151 | { | |
9126a2b1 | 152 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
bae2f647 JC |
153 | s8 reg; |
154 | ||
155 | if (offset > OFFSET_STEP * 127) | |
156 | reg = 127; | |
157 | else if (offset < OFFSET_STEP * -128) | |
158 | reg = -128; | |
159 | else | |
fedc459a | 160 | reg = DIV_ROUND_CLOSEST(offset, OFFSET_STEP); |
bae2f647 JC |
161 | |
162 | /* choose fine offset only for odd values in the normal range */ | |
163 | if (reg & 1 && reg <= 63 && reg >= -64) { | |
164 | /* Normal offset. Clear the coarse bit */ | |
165 | reg &= ~OFFSET_COARSE; | |
166 | } else { | |
167 | /* Coarse offset. Divide by 2 and set the coarse bit */ | |
168 | reg >>= 1; | |
169 | reg |= OFFSET_COARSE; | |
170 | } | |
171 | ||
9126a2b1 | 172 | return regmap_write(pcf2123->map, PCF2123_REG_OFFSET, (unsigned int)reg); |
bae2f647 JC |
173 | } |
174 | ||
7f3923a1 CV |
175 | static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) |
176 | { | |
9126a2b1 | 177 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
66c056d6 | 178 | u8 rxbuf[7]; |
7f3923a1 CV |
179 | int ret; |
180 | ||
9126a2b1 | 181 | ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_SC, rxbuf, |
790d0339 DH |
182 | sizeof(rxbuf)); |
183 | if (ret) | |
7f3923a1 | 184 | return ret; |
7f3923a1 | 185 | |
f07fa924 JC |
186 | if (rxbuf[0] & OSC_HAS_STOPPED) { |
187 | dev_info(dev, "clock was stopped. Time is not valid\n"); | |
188 | return -EINVAL; | |
189 | } | |
190 | ||
7f3923a1 CV |
191 | tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F); |
192 | tm->tm_min = bcd2bin(rxbuf[1] & 0x7F); | |
193 | tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */ | |
194 | tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F); | |
195 | tm->tm_wday = rxbuf[4] & 0x07; | |
196 | tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */ | |
d5b626e1 | 197 | tm->tm_year = bcd2bin(rxbuf[6]) + 100; |
7f3923a1 | 198 | |
c33850bb | 199 | dev_dbg(dev, "%s: tm is %ptR\n", __func__, tm); |
7f3923a1 | 200 | |
22652ba7 | 201 | return 0; |
7f3923a1 CV |
202 | } |
203 | ||
204 | static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
205 | { | |
9126a2b1 | 206 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
790d0339 | 207 | u8 txbuf[7]; |
7f3923a1 CV |
208 | int ret; |
209 | ||
c33850bb | 210 | dev_dbg(dev, "%s: tm is %ptR\n", __func__, tm); |
7f3923a1 CV |
211 | |
212 | /* Stop the counter first */ | |
9126a2b1 | 213 | ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP); |
790d0339 | 214 | if (ret) |
7f3923a1 | 215 | return ret; |
7f3923a1 CV |
216 | |
217 | /* Set the new time */ | |
790d0339 DH |
218 | txbuf[0] = bin2bcd(tm->tm_sec & 0x7F); |
219 | txbuf[1] = bin2bcd(tm->tm_min & 0x7F); | |
220 | txbuf[2] = bin2bcd(tm->tm_hour & 0x3F); | |
221 | txbuf[3] = bin2bcd(tm->tm_mday & 0x3F); | |
222 | txbuf[4] = tm->tm_wday & 0x07; | |
223 | txbuf[5] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */ | |
d5b626e1 | 224 | txbuf[6] = bin2bcd(tm->tm_year - 100); |
790d0339 | 225 | |
9126a2b1 | 226 | ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_SC, txbuf, |
790d0339 DH |
227 | sizeof(txbuf)); |
228 | if (ret) | |
7f3923a1 | 229 | return ret; |
7f3923a1 CV |
230 | |
231 | /* Start the counter */ | |
9126a2b1 | 232 | ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR); |
790d0339 | 233 | if (ret) |
7f3923a1 | 234 | return ret; |
7f3923a1 CV |
235 | |
236 | return 0; | |
237 | } | |
238 | ||
577f6482 AB |
239 | static int pcf2123_rtc_alarm_irq_enable(struct device *dev, unsigned int en) |
240 | { | |
9126a2b1 | 241 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
577f6482 | 242 | |
9126a2b1 | 243 | return regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE, |
577f6482 AB |
244 | en ? CTRL2_AIE : 0); |
245 | } | |
246 | ||
e32e60a2 DH |
247 | static int pcf2123_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) |
248 | { | |
9126a2b1 | 249 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
e32e60a2 DH |
250 | u8 rxbuf[4]; |
251 | int ret; | |
252 | unsigned int val = 0; | |
253 | ||
9126a2b1 | 254 | ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_ALRM_MN, rxbuf, |
e32e60a2 DH |
255 | sizeof(rxbuf)); |
256 | if (ret) | |
257 | return ret; | |
258 | ||
259 | alm->time.tm_min = bcd2bin(rxbuf[0] & 0x7F); | |
260 | alm->time.tm_hour = bcd2bin(rxbuf[1] & 0x3F); | |
261 | alm->time.tm_mday = bcd2bin(rxbuf[2] & 0x3F); | |
262 | alm->time.tm_wday = bcd2bin(rxbuf[3] & 0x07); | |
263 | ||
264 | dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time); | |
265 | ||
9126a2b1 | 266 | ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val); |
e32e60a2 DH |
267 | if (ret) |
268 | return ret; | |
269 | ||
270 | alm->enabled = !!(val & CTRL2_AIE); | |
271 | ||
272 | return 0; | |
273 | } | |
274 | ||
275 | static int pcf2123_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | |
276 | { | |
9126a2b1 | 277 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
e32e60a2 DH |
278 | u8 txbuf[4]; |
279 | int ret; | |
280 | ||
281 | dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time); | |
282 | ||
d0ce6ef7 | 283 | /* Disable alarm interrupt */ |
9126a2b1 | 284 | ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE, 0); |
e32e60a2 | 285 | if (ret) |
7f3923a1 | 286 | return ret; |
7f3923a1 | 287 | |
d0ce6ef7 | 288 | /* Ensure alarm flag is clear */ |
9126a2b1 | 289 | ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0); |
e32e60a2 DH |
290 | if (ret) |
291 | return ret; | |
292 | ||
293 | /* Set new alarm */ | |
294 | txbuf[0] = bin2bcd(alm->time.tm_min & 0x7F); | |
295 | txbuf[1] = bin2bcd(alm->time.tm_hour & 0x3F); | |
296 | txbuf[2] = bin2bcd(alm->time.tm_mday & 0x3F); | |
5bdf40da | 297 | txbuf[3] = ALRM_DISABLE; |
e32e60a2 | 298 | |
9126a2b1 | 299 | ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_ALRM_MN, txbuf, |
e32e60a2 DH |
300 | sizeof(txbuf)); |
301 | if (ret) | |
302 | return ret; | |
303 | ||
577f6482 | 304 | return pcf2123_rtc_alarm_irq_enable(dev, alm->enabled); |
7f3923a1 CV |
305 | } |
306 | ||
e32e60a2 DH |
307 | static irqreturn_t pcf2123_rtc_irq(int irq, void *dev) |
308 | { | |
9126a2b1 AB |
309 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
310 | struct mutex *lock = &pcf2123->rtc->ops_lock; | |
e32e60a2 DH |
311 | unsigned int val = 0; |
312 | int ret = IRQ_NONE; | |
313 | ||
314 | mutex_lock(lock); | |
9126a2b1 | 315 | regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val); |
e32e60a2 DH |
316 | |
317 | /* Alarm? */ | |
318 | if (val & CTRL2_AF) { | |
319 | ret = IRQ_HANDLED; | |
320 | ||
321 | /* Clear alarm flag */ | |
9126a2b1 | 322 | regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0); |
e32e60a2 | 323 | |
9126a2b1 | 324 | rtc_update_irq(pcf2123->rtc, 1, RTC_IRQF | RTC_AF); |
e32e60a2 DH |
325 | } |
326 | ||
327 | mutex_unlock(lock); | |
328 | ||
329 | return ret; | |
330 | } | |
331 | ||
1e094b94 JC |
332 | static int pcf2123_reset(struct device *dev) |
333 | { | |
9126a2b1 | 334 | struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); |
1e094b94 | 335 | int ret; |
790d0339 | 336 | unsigned int val = 0; |
1e094b94 | 337 | |
9126a2b1 | 338 | ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_SW_RESET); |
790d0339 | 339 | if (ret) |
1e094b94 JC |
340 | return ret; |
341 | ||
342 | /* Stop the counter */ | |
343 | dev_dbg(dev, "stopping RTC\n"); | |
9126a2b1 | 344 | ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP); |
790d0339 | 345 | if (ret) |
1e094b94 JC |
346 | return ret; |
347 | ||
348 | /* See if the counter was actually stopped */ | |
349 | dev_dbg(dev, "checking for presence of RTC\n"); | |
9126a2b1 | 350 | ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL1, &val); |
790d0339 | 351 | if (ret) |
1e094b94 JC |
352 | return ret; |
353 | ||
790d0339 DH |
354 | dev_dbg(dev, "received data from RTC (0x%08X)\n", val); |
355 | if (!(val & CTRL1_STOP)) | |
1e094b94 JC |
356 | return -ENODEV; |
357 | ||
358 | /* Start the counter */ | |
9126a2b1 | 359 | ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR); |
790d0339 | 360 | if (ret) |
1e094b94 JC |
361 | return ret; |
362 | ||
363 | return 0; | |
364 | } | |
365 | ||
7f3923a1 CV |
366 | static const struct rtc_class_ops pcf2123_rtc_ops = { |
367 | .read_time = pcf2123_rtc_read_time, | |
368 | .set_time = pcf2123_rtc_set_time, | |
bae2f647 JC |
369 | .read_offset = pcf2123_read_offset, |
370 | .set_offset = pcf2123_set_offset, | |
e32e60a2 DH |
371 | .read_alarm = pcf2123_rtc_read_alarm, |
372 | .set_alarm = pcf2123_rtc_set_alarm, | |
577f6482 | 373 | .alarm_irq_enable = pcf2123_rtc_alarm_irq_enable, |
7f3923a1 CV |
374 | }; |
375 | ||
5a167f45 | 376 | static int pcf2123_probe(struct spi_device *spi) |
7f3923a1 CV |
377 | { |
378 | struct rtc_device *rtc; | |
f07fa924 | 379 | struct rtc_time tm; |
9126a2b1 | 380 | struct pcf2123_data *pcf2123; |
e32e60a2 | 381 | int ret = 0; |
7f3923a1 | 382 | |
9126a2b1 | 383 | pcf2123 = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_data), |
dd48ccc4 | 384 | GFP_KERNEL); |
9126a2b1 | 385 | if (!pcf2123) |
7f3923a1 | 386 | return -ENOMEM; |
d3bad602 | 387 | |
9126a2b1 | 388 | dev_set_drvdata(&spi->dev, pcf2123); |
7f3923a1 | 389 | |
9126a2b1 | 390 | pcf2123->map = devm_regmap_init_spi(spi, &pcf2123_regmap_config); |
9126a2b1 | 391 | if (IS_ERR(pcf2123->map)) { |
790d0339 | 392 | dev_err(&spi->dev, "regmap init failed.\n"); |
9a5aeaad | 393 | return PTR_ERR(pcf2123->map); |
790d0339 DH |
394 | } |
395 | ||
f07fa924 | 396 | ret = pcf2123_rtc_read_time(&spi->dev, &tm); |
1e094b94 | 397 | if (ret < 0) { |
f07fa924 JC |
398 | ret = pcf2123_reset(&spi->dev); |
399 | if (ret < 0) { | |
400 | dev_err(&spi->dev, "chip not found\n"); | |
9a5aeaad | 401 | return ret; |
f07fa924 | 402 | } |
7f3923a1 CV |
403 | } |
404 | ||
7f3923a1 CV |
405 | dev_info(&spi->dev, "spiclk %u KHz.\n", |
406 | (spi->max_speed_hz + 500) / 1000); | |
407 | ||
7f3923a1 | 408 | /* Finalize the initialization */ |
935a7f45 AB |
409 | rtc = devm_rtc_allocate_device(&spi->dev); |
410 | if (IS_ERR(rtc)) | |
9a5aeaad | 411 | return PTR_ERR(rtc); |
7f3923a1 | 412 | |
9126a2b1 | 413 | pcf2123->rtc = rtc; |
7f3923a1 | 414 | |
e32e60a2 DH |
415 | /* Register alarm irq */ |
416 | if (spi->irq > 0) { | |
417 | ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, | |
418 | pcf2123_rtc_irq, | |
419 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | |
420 | pcf2123_driver.driver.name, &spi->dev); | |
421 | if (!ret) | |
422 | device_init_wakeup(&spi->dev, true); | |
423 | else | |
424 | dev_err(&spi->dev, "could not request irq.\n"); | |
7f3923a1 CV |
425 | } |
426 | ||
e32e60a2 DH |
427 | /* The PCF2123's alarm only has minute accuracy. Must add timer |
428 | * support to this driver to generate interrupts more than once | |
429 | * per minute. | |
430 | */ | |
935a7f45 AB |
431 | rtc->uie_unsupported = 1; |
432 | rtc->ops = &pcf2123_rtc_ops; | |
d5b626e1 AB |
433 | rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; |
434 | rtc->range_max = RTC_TIMESTAMP_END_2099; | |
435 | rtc->set_start_time = true; | |
935a7f45 | 436 | |
fdcfd854 | 437 | ret = devm_rtc_register_device(rtc); |
935a7f45 AB |
438 | if (ret) |
439 | return ret; | |
f3d2570a | 440 | |
7f3923a1 | 441 | return 0; |
7f3923a1 CV |
442 | } |
443 | ||
3fc70077 JC |
444 | #ifdef CONFIG_OF |
445 | static const struct of_device_id pcf2123_dt_ids[] = { | |
cb36cf80 | 446 | { .compatible = "nxp,pcf2123", }, |
3c3d7101 | 447 | { .compatible = "microcrystal,rv2123", }, |
cb36cf80 AB |
448 | /* Deprecated, do not use */ |
449 | { .compatible = "nxp,rtc-pcf2123", }, | |
3fc70077 JC |
450 | { /* sentinel */ } |
451 | }; | |
452 | MODULE_DEVICE_TABLE(of, pcf2123_dt_ids); | |
453 | #endif | |
454 | ||
7f3923a1 CV |
455 | static struct spi_driver pcf2123_driver = { |
456 | .driver = { | |
457 | .name = "rtc-pcf2123", | |
3fc70077 | 458 | .of_match_table = of_match_ptr(pcf2123_dt_ids), |
7f3923a1 CV |
459 | }, |
460 | .probe = pcf2123_probe, | |
7f3923a1 CV |
461 | }; |
462 | ||
109e9418 | 463 | module_spi_driver(pcf2123_driver); |
7f3923a1 CV |
464 | |
465 | MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>"); | |
466 | MODULE_DESCRIPTION("NXP PCF2123 RTC driver"); | |
467 | MODULE_LICENSE("GPL"); |