aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/rtc/Kconfig9
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-msm6242.c269
3 files changed, 279 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 3c20dae43ce2..16c8268da33c 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -509,6 +509,15 @@ config RTC_DRV_M48T59
509 This driver can also be built as a module, if so, the module 509 This driver can also be built as a module, if so, the module
510 will be called "rtc-m48t59". 510 will be called "rtc-m48t59".
511 511
512config RTC_DRV_MSM6242
513 tristate "Oki MSM6242"
514 help
515 If you say yes here you get support for the Oki MSM6242
516 timekeeping chip. It is used in some Amiga models (e.g. A2000).
517
518 This driver can also be built as a module. If so, the module
519 will be called rtc-msm6242.
520
512config RTC_MXC 521config RTC_MXC
513 tristate "Freescale MXC Real Time Clock" 522 tristate "Freescale MXC Real Time Clock"
514 depends on ARCH_MXC 523 depends on ARCH_MXC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index aa3fbd5517a1..7e704f67199b 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
52obj-$(CONFIG_RTC_MXC) += rtc-mxc.o 52obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
53obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o 53obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
54obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o 54obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
55obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
55obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o 56obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
56obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o 57obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
57obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o 58obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c
new file mode 100644
index 000000000000..5f5968a48925
--- /dev/null
+++ b/drivers/rtc/rtc-msm6242.c
@@ -0,0 +1,269 @@
1/*
2 * Oki MSM6242 RTC Driver
3 *
4 * Copyright 2009 Geert Uytterhoeven
5 *
6 * Based on the A2000 TOD code in arch/m68k/amiga/config.c
7 * Copyright (C) 1993 Hamish Macdonald
8 */
9
10#include <linux/delay.h>
11#include <linux/io.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/rtc.h>
16
17
18enum {
19 MSM6242_SECOND1 = 0x0, /* 1-second digit register */
20 MSM6242_SECOND10 = 0x1, /* 10-second digit register */
21 MSM6242_MINUTE1 = 0x2, /* 1-minute digit register */
22 MSM6242_MINUTE10 = 0x3, /* 10-minute digit register */
23 MSM6242_HOUR1 = 0x4, /* 1-hour digit register */
24 MSM6242_HOUR10 = 0x5, /* PM/AM, 10-hour digit register */
25 MSM6242_DAY1 = 0x6, /* 1-day digit register */
26 MSM6242_DAY10 = 0x7, /* 10-day digit register */
27 MSM6242_MONTH1 = 0x8, /* 1-month digit register */
28 MSM6242_MONTH10 = 0x9, /* 10-month digit register */
29 MSM6242_YEAR1 = 0xa, /* 1-year digit register */
30 MSM6242_YEAR10 = 0xb, /* 10-year digit register */
31 MSM6242_WEEK = 0xc, /* Week register */
32 MSM6242_CD = 0xd, /* Control Register D */
33 MSM6242_CE = 0xe, /* Control Register E */
34 MSM6242_CF = 0xf, /* Control Register F */
35};
36
37#define MSM6242_HOUR10_AM (0 << 2)
38#define MSM6242_HOUR10_PM (1 << 2)
39#define MSM6242_HOUR10_HR_MASK (3 << 0)
40
41#define MSM6242_WEEK_SUNDAY 0
42#define MSM6242_WEEK_MONDAY 1
43#define MSM6242_WEEK_TUESDAY 2
44#define MSM6242_WEEK_WEDNESDAY 3
45#define MSM6242_WEEK_THURSDAY 4
46#define MSM6242_WEEK_FRIDAY 5
47#define MSM6242_WEEK_SATURDAY 6
48
49#define MSM6242_CD_30_S_ADJ (1 << 3) /* 30-second adjustment */
50#define MSM6242_CD_IRQ_FLAG (1 << 2)
51#define MSM6242_CD_BUSY (1 << 1)
52#define MSM6242_CD_HOLD (1 << 0)
53
54#define MSM6242_CE_T_MASK (3 << 2)
55#define MSM6242_CE_T_64HZ (0 << 2) /* period 1/64 second */
56#define MSM6242_CE_T_1HZ (1 << 2) /* period 1 second */
57#define MSM6242_CE_T_1MINUTE (2 << 2) /* period 1 minute */
58#define MSM6242_CE_T_1HOUR (3 << 2) /* period 1 hour */
59
60#define MSM6242_CE_ITRPT_STND (1 << 1)
61#define MSM6242_CE_MASK (1 << 0) /* STD.P output control */
62
63#define MSM6242_CF_TEST (1 << 3)
64#define MSM6242_CF_12H (0 << 2)
65#define MSM6242_CF_24H (1 << 2)
66#define MSM6242_CF_STOP (1 << 1)
67#define MSM6242_CF_REST (1 << 0) /* reset */
68
69
70struct msm6242_priv {
71 u32 __iomem *regs;
72 struct rtc_device *rtc;
73};
74
75static inline unsigned int msm6242_read(struct msm6242_priv *priv,
76 unsigned int reg)
77{
78 return __raw_readl(&priv->regs[reg]) & 0xf;
79}
80
81static inline void msm6242_write(struct msm6242_priv *priv, unsigned int val,
82 unsigned int reg)
83{
84 return __raw_writel(val, &priv->regs[reg]);
85}
86
87static inline void msm6242_set(struct msm6242_priv *priv, unsigned int val,
88 unsigned int reg)
89{
90 msm6242_write(priv, msm6242_read(priv, reg) | val, reg);
91}
92
93static inline void msm6242_clear(struct msm6242_priv *priv, unsigned int val,
94 unsigned int reg)
95{
96 msm6242_write(priv, msm6242_read(priv, reg) & ~val, reg);
97}
98
99static void msm6242_lock(struct msm6242_priv *priv)
100{
101 int cnt = 5;
102
103 msm6242_set(priv, MSM6242_CD_HOLD, MSM6242_CD);
104
105 while ((msm6242_read(priv, MSM6242_CD) & MSM6242_CD_BUSY) && cnt) {
106 msm6242_clear(priv, MSM6242_CD_HOLD, MSM6242_CD);
107 udelay(70);
108 msm6242_set(priv, MSM6242_CD_HOLD, MSM6242_CD);
109 cnt--;
110 }
111
112 if (!cnt)
113 pr_warning("msm6242: timed out waiting for RTC (0x%x)\n",
114 msm6242_read(priv, MSM6242_CD));
115}
116
117static void msm6242_unlock(struct msm6242_priv *priv)
118{
119 msm6242_clear(priv, MSM6242_CD_HOLD, MSM6242_CD);
120}
121
122static int msm6242_read_time(struct device *dev, struct rtc_time *tm)
123{
124 struct msm6242_priv *priv = dev_get_drvdata(dev);
125
126 msm6242_lock(priv);
127
128 tm->tm_sec = msm6242_read(priv, MSM6242_SECOND10) * 10 +
129 msm6242_read(priv, MSM6242_SECOND1);
130 tm->tm_min = msm6242_read(priv, MSM6242_MINUTE10) * 10 +
131 msm6242_read(priv, MSM6242_MINUTE1);
132 tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10 & 3)) * 10 +
133 msm6242_read(priv, MSM6242_HOUR1);
134 tm->tm_mday = msm6242_read(priv, MSM6242_DAY10) * 10 +
135 msm6242_read(priv, MSM6242_DAY1);
136 tm->tm_wday = msm6242_read(priv, MSM6242_WEEK);
137 tm->tm_mon = msm6242_read(priv, MSM6242_MONTH10) * 10 +
138 msm6242_read(priv, MSM6242_MONTH1) - 1;
139 tm->tm_year = msm6242_read(priv, MSM6242_YEAR10) * 10 +
140 msm6242_read(priv, MSM6242_YEAR1);
141 if (tm->tm_year <= 69)
142 tm->tm_year += 100;
143
144 if (!(msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)) {
145 unsigned int pm = msm6242_read(priv, MSM6242_HOUR10) &
146 MSM6242_HOUR10_PM;
147 if (!pm && tm->tm_hour == 12)
148 tm->tm_hour = 0;
149 else if (pm && tm->tm_hour != 12)
150 tm->tm_hour += 12;
151 }
152
153 msm6242_unlock(priv);
154
155 return rtc_valid_tm(tm);
156}
157
158static int msm6242_set_time(struct device *dev, struct rtc_time *tm)
159{
160 struct msm6242_priv *priv = dev_get_drvdata(dev);
161
162 msm6242_lock(priv);
163
164 msm6242_write(priv, tm->tm_sec / 10, MSM6242_SECOND10);
165 msm6242_write(priv, tm->tm_sec % 10, MSM6242_SECOND1);
166 msm6242_write(priv, tm->tm_min / 10, MSM6242_MINUTE10);
167 msm6242_write(priv, tm->tm_min % 10, MSM6242_MINUTE1);
168 if (msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)
169 msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);
170 else if (tm->tm_hour >= 12)
171 msm6242_write(priv, MSM6242_HOUR10_PM + (tm->tm_hour - 12) / 10,
172 MSM6242_HOUR10);
173 else
174 msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);
175 msm6242_write(priv, tm->tm_hour % 10, MSM6242_HOUR1);
176 msm6242_write(priv, tm->tm_mday / 10, MSM6242_DAY10);
177 msm6242_write(priv, tm->tm_mday % 10, MSM6242_DAY1);
178 if (tm->tm_wday != -1)
179 msm6242_write(priv, tm->tm_wday, MSM6242_WEEK);
180 msm6242_write(priv, (tm->tm_mon + 1) / 10, MSM6242_MONTH10);
181 msm6242_write(priv, (tm->tm_mon + 1) % 10, MSM6242_MONTH1);
182 if (tm->tm_year >= 100)
183 tm->tm_year -= 100;
184 msm6242_write(priv, tm->tm_year / 10, MSM6242_YEAR10);
185 msm6242_write(priv, tm->tm_year % 10, MSM6242_YEAR1);
186
187 msm6242_unlock(priv);
188 return 0;
189}
190
191static const struct rtc_class_ops msm6242_rtc_ops = {
192 .read_time = msm6242_read_time,
193 .set_time = msm6242_set_time,
194};
195
196static int __init msm6242_rtc_probe(struct platform_device *dev)
197{
198 struct resource *res;
199 struct msm6242_priv *priv;
200 struct rtc_device *rtc;
201 int error;
202
203 res = platform_get_resource(dev, IORESOURCE_MEM, 0);
204 if (!res)
205 return -ENODEV;
206
207 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
208 if (!priv)
209 return -ENOMEM;
210
211 priv->regs = ioremap(res->start, resource_size(res));
212 if (!priv->regs) {
213 error = -ENOMEM;
214 goto out_free_priv;
215 }
216
217 rtc = rtc_device_register("rtc-msm6242", &dev->dev, &msm6242_rtc_ops,
218 THIS_MODULE);
219 if (IS_ERR(rtc)) {
220 error = PTR_ERR(rtc);
221 goto out_unmap;
222 }
223
224 priv->rtc = rtc;
225 platform_set_drvdata(dev, priv);
226 return 0;
227
228out_unmap:
229 iounmap(priv->regs);
230out_free_priv:
231 kfree(priv);
232 return error;
233}
234
235static int __exit msm6242_rtc_remove(struct platform_device *dev)
236{
237 struct msm6242_priv *priv = platform_get_drvdata(dev);
238
239 rtc_device_unregister(priv->rtc);
240 iounmap(priv->regs);
241 kfree(priv);
242 return 0;
243}
244
245static struct platform_driver msm6242_rtc_driver = {
246 .driver = {
247 .name = "rtc-msm6242",
248 .owner = THIS_MODULE,
249 },
250 .remove = __exit_p(msm6242_rtc_remove),
251};
252
253static int __init msm6242_rtc_init(void)
254{
255 return platform_driver_probe(&msm6242_rtc_driver, msm6242_rtc_probe);
256}
257
258static void __exit msm6242_rtc_fini(void)
259{
260 platform_driver_unregister(&msm6242_rtc_driver);
261}
262
263module_init(msm6242_rtc_init);
264module_exit(msm6242_rtc_fini);
265
266MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
267MODULE_LICENSE("GPL");
268MODULE_DESCRIPTION("Oki MSM6242 RTC driver");
269MODULE_ALIAS("platform:rtc-msm6242");