]>
Commit | Line | Data |
---|---|---|
45fd8a0c ML |
1 | /* |
2 | * Au1xxx counter0 (aka Time-Of-Year counter) RTC interface driver. | |
3 | * | |
4 | * Copyright (C) 2008 Manuel Lauss <mano@roarinelk.homelinux.net> | |
5 | * | |
6 | * This file is subject to the terms and conditions of the GNU General Public | |
7 | * License. See the file "COPYING" in the main directory of this archive | |
8 | * for more details. | |
9 | */ | |
10 | ||
11 | /* All current Au1xxx SoCs have 2 counters fed by an external 32.768 kHz | |
12 | * crystal. Counter 0, which keeps counting during sleep/powerdown, is | |
13 | * used to count seconds since the beginning of the unix epoch. | |
14 | * | |
15 | * The counters must be configured and enabled by bootloader/board code; | |
16 | * no checks as to whether they really get a proper 32.768kHz clock are | |
17 | * made as this would take far too long. | |
18 | */ | |
19 | ||
20 | #include <linux/module.h> | |
21 | #include <linux/kernel.h> | |
22 | #include <linux/rtc.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/io.h> | |
26 | #include <asm/mach-au1x00/au1000.h> | |
27 | ||
28 | /* 32kHz clock enabled and detected */ | |
29 | #define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) | |
30 | ||
31 | static int au1xtoy_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
32 | { | |
33 | unsigned long t; | |
34 | ||
1d09de7d | 35 | t = alchemy_rdsys(AU1000_SYS_TOYREAD); |
45fd8a0c | 36 | |
0a22bd6f | 37 | rtc_time64_to_tm(t, tm); |
45fd8a0c | 38 | |
22652ba7 | 39 | return 0; |
45fd8a0c ML |
40 | } |
41 | ||
42 | static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
43 | { | |
44 | unsigned long t; | |
45 | ||
0a22bd6f | 46 | t = rtc_tm_to_time64(tm); |
45fd8a0c | 47 | |
1d09de7d | 48 | alchemy_wrsys(t, AU1000_SYS_TOYWRITE); |
45fd8a0c ML |
49 | |
50 | /* wait for the pending register write to succeed. This can | |
51 | * take up to 6 seconds... | |
52 | */ | |
1d09de7d | 53 | while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S) |
45fd8a0c ML |
54 | msleep(1); |
55 | ||
56 | return 0; | |
57 | } | |
58 | ||
8bc57e7f | 59 | static const struct rtc_class_ops au1xtoy_rtc_ops = { |
45fd8a0c ML |
60 | .read_time = au1xtoy_rtc_read_time, |
61 | .set_time = au1xtoy_rtc_set_time, | |
62 | }; | |
63 | ||
5a167f45 | 64 | static int au1xtoy_rtc_probe(struct platform_device *pdev) |
45fd8a0c ML |
65 | { |
66 | struct rtc_device *rtcdev; | |
67 | unsigned long t; | |
45fd8a0c | 68 | |
1d09de7d | 69 | t = alchemy_rdsys(AU1000_SYS_CNTRCTRL); |
45fd8a0c ML |
70 | if (!(t & CNTR_OK)) { |
71 | dev_err(&pdev->dev, "counters not working; aborting.\n"); | |
9cf71edb | 72 | return -ENODEV; |
45fd8a0c ML |
73 | } |
74 | ||
45fd8a0c | 75 | /* set counter0 tickrate to 1Hz if necessary */ |
1d09de7d | 76 | if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767) { |
45fd8a0c ML |
77 | /* wait until hardware gives access to TRIM register */ |
78 | t = 0x00100000; | |
1d09de7d | 79 | while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T0S) && --t) |
45fd8a0c ML |
80 | msleep(1); |
81 | ||
82 | if (!t) { | |
83 | /* timed out waiting for register access; assume | |
84 | * counters are unusable. | |
85 | */ | |
86 | dev_err(&pdev->dev, "timeout waiting for access\n"); | |
9cf71edb | 87 | return -ETIMEDOUT; |
45fd8a0c ML |
88 | } |
89 | ||
90 | /* set 1Hz TOY tick rate */ | |
1d09de7d | 91 | alchemy_wrsys(32767, AU1000_SYS_TOYTRIM); |
45fd8a0c ML |
92 | } |
93 | ||
94 | /* wait until the hardware allows writes to the counter reg */ | |
1d09de7d | 95 | while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S) |
45fd8a0c ML |
96 | msleep(1); |
97 | ||
7fc9790a AB |
98 | rtcdev = devm_rtc_allocate_device(&pdev->dev); |
99 | if (IS_ERR(rtcdev)) | |
100 | return PTR_ERR(rtcdev); | |
101 | ||
102 | rtcdev->ops = &au1xtoy_rtc_ops; | |
b1b686e4 | 103 | rtcdev->range_max = U32_MAX; |
45fd8a0c ML |
104 | |
105 | platform_set_drvdata(pdev, rtcdev); | |
106 | ||
fdcfd854 | 107 | return devm_rtc_register_device(rtcdev); |
45fd8a0c ML |
108 | } |
109 | ||
45fd8a0c ML |
110 | static struct platform_driver au1xrtc_driver = { |
111 | .driver = { | |
112 | .name = "rtc-au1xxx", | |
45fd8a0c | 113 | }, |
45fd8a0c ML |
114 | }; |
115 | ||
aaa83458 | 116 | module_platform_driver_probe(au1xrtc_driver, au1xtoy_rtc_probe); |
45fd8a0c ML |
117 | |
118 | MODULE_DESCRIPTION("Au1xxx TOY-counter-based RTC driver"); | |
119 | MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>"); | |
120 | MODULE_LICENSE("GPL"); | |
121 | MODULE_ALIAS("platform:rtc-au1xxx"); |