diff options
Diffstat (limited to 'drivers/rtc/rtc-s3c.c')
-rw-r--r-- | drivers/rtc/rtc-s3c.c | 851 |
1 files changed, 531 insertions, 320 deletions
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 4958a363b2c7..a6b1252c9941 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c | |||
@@ -32,155 +32,150 @@ | |||
32 | #include <asm/irq.h> | 32 | #include <asm/irq.h> |
33 | #include "rtc-s3c.h" | 33 | #include "rtc-s3c.h" |
34 | 34 | ||
35 | enum s3c_cpu_type { | 35 | struct s3c_rtc { |
36 | TYPE_S3C2410, | 36 | struct device *dev; |
37 | TYPE_S3C2416, | 37 | struct rtc_device *rtc; |
38 | TYPE_S3C2443, | ||
39 | TYPE_S3C64XX, | ||
40 | }; | ||
41 | 38 | ||
42 | struct s3c_rtc_drv_data { | 39 | void __iomem *base; |
43 | int cpu_type; | 40 | struct clk *rtc_clk; |
44 | }; | 41 | struct clk *rtc_src_clk; |
42 | bool enabled; | ||
43 | |||
44 | struct s3c_rtc_data *data; | ||
45 | 45 | ||
46 | /* I have yet to find an S3C implementation with more than one | 46 | int irq_alarm; |
47 | * of these rtc blocks in */ | 47 | int irq_tick; |
48 | 48 | ||
49 | static struct clk *rtc_clk; | 49 | spinlock_t pie_lock; |
50 | static void __iomem *s3c_rtc_base; | 50 | spinlock_t alarm_clk_lock; |
51 | static int s3c_rtc_alarmno; | ||
52 | static int s3c_rtc_tickno; | ||
53 | static enum s3c_cpu_type s3c_rtc_cpu_type; | ||
54 | 51 | ||
55 | static DEFINE_SPINLOCK(s3c_rtc_pie_lock); | 52 | int ticnt_save, ticnt_en_save; |
53 | bool wake_en; | ||
54 | }; | ||
55 | |||
56 | struct s3c_rtc_data { | ||
57 | int max_user_freq; | ||
58 | bool needs_src_clk; | ||
59 | |||
60 | void (*irq_handler) (struct s3c_rtc *info, int mask); | ||
61 | void (*set_freq) (struct s3c_rtc *info, int freq); | ||
62 | void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq); | ||
63 | void (*select_tick_clk) (struct s3c_rtc *info); | ||
64 | void (*save_tick_cnt) (struct s3c_rtc *info); | ||
65 | void (*restore_tick_cnt) (struct s3c_rtc *info); | ||
66 | void (*enable) (struct s3c_rtc *info); | ||
67 | void (*disable) (struct s3c_rtc *info); | ||
68 | }; | ||
56 | 69 | ||
57 | static void s3c_rtc_alarm_clk_enable(bool enable) | 70 | static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) |
58 | { | 71 | { |
59 | static DEFINE_SPINLOCK(s3c_rtc_alarm_clk_lock); | ||
60 | static bool alarm_clk_enabled; | ||
61 | unsigned long irq_flags; | 72 | unsigned long irq_flags; |
62 | 73 | ||
63 | spin_lock_irqsave(&s3c_rtc_alarm_clk_lock, irq_flags); | 74 | spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); |
64 | if (enable) { | 75 | if (enable) { |
65 | if (!alarm_clk_enabled) { | 76 | if (!info->enabled) { |
66 | clk_enable(rtc_clk); | 77 | clk_enable(info->rtc_clk); |
67 | alarm_clk_enabled = true; | 78 | if (info->data->needs_src_clk) |
79 | clk_enable(info->rtc_src_clk); | ||
80 | info->enabled = true; | ||
68 | } | 81 | } |
69 | } else { | 82 | } else { |
70 | if (alarm_clk_enabled) { | 83 | if (info->enabled) { |
71 | clk_disable(rtc_clk); | 84 | if (info->data->needs_src_clk) |
72 | alarm_clk_enabled = false; | 85 | clk_disable(info->rtc_src_clk); |
86 | clk_disable(info->rtc_clk); | ||
87 | info->enabled = false; | ||
73 | } | 88 | } |
74 | } | 89 | } |
75 | spin_unlock_irqrestore(&s3c_rtc_alarm_clk_lock, irq_flags); | 90 | spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); |
76 | } | 91 | } |
77 | 92 | ||
78 | /* IRQ Handlers */ | 93 | /* IRQ Handlers */ |
79 | 94 | static irqreturn_t s3c_rtc_tickirq(int irq, void *id) | |
80 | static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) | ||
81 | { | 95 | { |
82 | struct rtc_device *rdev = id; | 96 | struct s3c_rtc *info = (struct s3c_rtc *)id; |
83 | |||
84 | clk_enable(rtc_clk); | ||
85 | rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); | ||
86 | |||
87 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) | ||
88 | writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP); | ||
89 | |||
90 | clk_disable(rtc_clk); | ||
91 | 97 | ||
92 | s3c_rtc_alarm_clk_enable(false); | 98 | if (info->data->irq_handler) |
99 | info->data->irq_handler(info, S3C2410_INTP_TIC); | ||
93 | 100 | ||
94 | return IRQ_HANDLED; | 101 | return IRQ_HANDLED; |
95 | } | 102 | } |
96 | 103 | ||
97 | static irqreturn_t s3c_rtc_tickirq(int irq, void *id) | 104 | static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) |
98 | { | 105 | { |
99 | struct rtc_device *rdev = id; | 106 | struct s3c_rtc *info = (struct s3c_rtc *)id; |
100 | 107 | ||
101 | clk_enable(rtc_clk); | 108 | if (info->data->irq_handler) |
102 | rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); | 109 | info->data->irq_handler(info, S3C2410_INTP_ALM); |
103 | 110 | ||
104 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) | ||
105 | writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP); | ||
106 | |||
107 | clk_disable(rtc_clk); | ||
108 | return IRQ_HANDLED; | 111 | return IRQ_HANDLED; |
109 | } | 112 | } |
110 | 113 | ||
111 | /* Update control registers */ | 114 | /* Update control registers */ |
112 | static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) | 115 | static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) |
113 | { | 116 | { |
117 | struct s3c_rtc *info = dev_get_drvdata(dev); | ||
114 | unsigned int tmp; | 118 | unsigned int tmp; |
115 | 119 | ||
116 | dev_dbg(dev, "%s: aie=%d\n", __func__, enabled); | 120 | dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); |
117 | 121 | ||
118 | clk_enable(rtc_clk); | 122 | clk_enable(info->rtc_clk); |
119 | tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; | 123 | if (info->data->needs_src_clk) |
124 | clk_enable(info->rtc_src_clk); | ||
125 | tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; | ||
120 | 126 | ||
121 | if (enabled) | 127 | if (enabled) |
122 | tmp |= S3C2410_RTCALM_ALMEN; | 128 | tmp |= S3C2410_RTCALM_ALMEN; |
123 | 129 | ||
124 | writeb(tmp, s3c_rtc_base + S3C2410_RTCALM); | 130 | writeb(tmp, info->base + S3C2410_RTCALM); |
125 | clk_disable(rtc_clk); | 131 | if (info->data->needs_src_clk) |
132 | clk_disable(info->rtc_src_clk); | ||
133 | clk_disable(info->rtc_clk); | ||
126 | 134 | ||
127 | s3c_rtc_alarm_clk_enable(enabled); | 135 | s3c_rtc_alarm_clk_enable(info, enabled); |
128 | 136 | ||
129 | return 0; | 137 | return 0; |
130 | } | 138 | } |
131 | 139 | ||
132 | static int s3c_rtc_setfreq(struct device *dev, int freq) | 140 | /* Set RTC frequency */ |
141 | static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
133 | { | 142 | { |
134 | struct platform_device *pdev = to_platform_device(dev); | ||
135 | struct rtc_device *rtc_dev = platform_get_drvdata(pdev); | ||
136 | unsigned int tmp = 0; | ||
137 | int val; | ||
138 | |||
139 | if (!is_power_of_2(freq)) | 143 | if (!is_power_of_2(freq)) |
140 | return -EINVAL; | 144 | return -EINVAL; |
141 | 145 | ||
142 | clk_enable(rtc_clk); | 146 | clk_enable(info->rtc_clk); |
143 | spin_lock_irq(&s3c_rtc_pie_lock); | 147 | if (info->data->needs_src_clk) |
148 | clk_enable(info->rtc_src_clk); | ||
149 | spin_lock_irq(&info->pie_lock); | ||
144 | 150 | ||
145 | if (s3c_rtc_cpu_type != TYPE_S3C64XX) { | 151 | if (info->data->set_freq) |
146 | tmp = readb(s3c_rtc_base + S3C2410_TICNT); | 152 | info->data->set_freq(info, freq); |
147 | tmp &= S3C2410_TICNT_ENABLE; | ||
148 | } | ||
149 | 153 | ||
150 | val = (rtc_dev->max_user_freq / freq) - 1; | 154 | spin_unlock_irq(&info->pie_lock); |
151 | 155 | if (info->data->needs_src_clk) | |
152 | if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) { | 156 | clk_disable(info->rtc_src_clk); |
153 | tmp |= S3C2443_TICNT_PART(val); | 157 | clk_disable(info->rtc_clk); |
154 | writel(S3C2443_TICNT1_PART(val), s3c_rtc_base + S3C2443_TICNT1); | ||
155 | |||
156 | if (s3c_rtc_cpu_type == TYPE_S3C2416) | ||
157 | writel(S3C2416_TICNT2_PART(val), s3c_rtc_base + S3C2416_TICNT2); | ||
158 | } else { | ||
159 | tmp |= val; | ||
160 | } | ||
161 | |||
162 | writel(tmp, s3c_rtc_base + S3C2410_TICNT); | ||
163 | spin_unlock_irq(&s3c_rtc_pie_lock); | ||
164 | clk_disable(rtc_clk); | ||
165 | 158 | ||
166 | return 0; | 159 | return 0; |
167 | } | 160 | } |
168 | 161 | ||
169 | /* Time read/write */ | 162 | /* Time read/write */ |
170 | |||
171 | static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | 163 | static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) |
172 | { | 164 | { |
165 | struct s3c_rtc *info = dev_get_drvdata(dev); | ||
173 | unsigned int have_retried = 0; | 166 | unsigned int have_retried = 0; |
174 | void __iomem *base = s3c_rtc_base; | ||
175 | 167 | ||
176 | clk_enable(rtc_clk); | 168 | clk_enable(info->rtc_clk); |
169 | if (info->data->needs_src_clk) | ||
170 | clk_enable(info->rtc_src_clk); | ||
171 | |||
177 | retry_get_time: | 172 | retry_get_time: |
178 | rtc_tm->tm_min = readb(base + S3C2410_RTCMIN); | 173 | rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN); |
179 | rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); | 174 | rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); |
180 | rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE); | 175 | rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE); |
181 | rtc_tm->tm_mon = readb(base + S3C2410_RTCMON); | 176 | rtc_tm->tm_mon = readb(info->base + S3C2410_RTCMON); |
182 | rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); | 177 | rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR); |
183 | rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC); | 178 | rtc_tm->tm_sec = readb(info->base + S3C2410_RTCSEC); |
184 | 179 | ||
185 | /* the only way to work out whether the system was mid-update | 180 | /* the only way to work out whether the system was mid-update |
186 | * when we read it is to check the second counter, and if it | 181 | * when we read it is to check the second counter, and if it |
@@ -207,13 +202,16 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | |||
207 | 202 | ||
208 | rtc_tm->tm_mon -= 1; | 203 | rtc_tm->tm_mon -= 1; |
209 | 204 | ||
210 | clk_disable(rtc_clk); | 205 | if (info->data->needs_src_clk) |
206 | clk_disable(info->rtc_src_clk); | ||
207 | clk_disable(info->rtc_clk); | ||
208 | |||
211 | return rtc_valid_tm(rtc_tm); | 209 | return rtc_valid_tm(rtc_tm); |
212 | } | 210 | } |
213 | 211 | ||
214 | static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) | 212 | static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) |
215 | { | 213 | { |
216 | void __iomem *base = s3c_rtc_base; | 214 | struct s3c_rtc *info = dev_get_drvdata(dev); |
217 | int year = tm->tm_year - 100; | 215 | int year = tm->tm_year - 100; |
218 | 216 | ||
219 | dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n", | 217 | dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n", |
@@ -227,33 +225,42 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) | |||
227 | return -EINVAL; | 225 | return -EINVAL; |
228 | } | 226 | } |
229 | 227 | ||
230 | clk_enable(rtc_clk); | 228 | clk_enable(info->rtc_clk); |
231 | writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC); | 229 | if (info->data->needs_src_clk) |
232 | writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN); | 230 | clk_enable(info->rtc_src_clk); |
233 | writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR); | 231 | |
234 | writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE); | 232 | writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC); |
235 | writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON); | 233 | writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); |
236 | writeb(bin2bcd(year), base + S3C2410_RTCYEAR); | 234 | writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR); |
237 | clk_disable(rtc_clk); | 235 | writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE); |
236 | writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON); | ||
237 | writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR); | ||
238 | |||
239 | if (info->data->needs_src_clk) | ||
240 | clk_disable(info->rtc_src_clk); | ||
241 | clk_disable(info->rtc_clk); | ||
238 | 242 | ||
239 | return 0; | 243 | return 0; |
240 | } | 244 | } |
241 | 245 | ||
242 | static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) | 246 | static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) |
243 | { | 247 | { |
248 | struct s3c_rtc *info = dev_get_drvdata(dev); | ||
244 | struct rtc_time *alm_tm = &alrm->time; | 249 | struct rtc_time *alm_tm = &alrm->time; |
245 | void __iomem *base = s3c_rtc_base; | ||
246 | unsigned int alm_en; | 250 | unsigned int alm_en; |
247 | 251 | ||
248 | clk_enable(rtc_clk); | 252 | clk_enable(info->rtc_clk); |
249 | alm_tm->tm_sec = readb(base + S3C2410_ALMSEC); | 253 | if (info->data->needs_src_clk) |
250 | alm_tm->tm_min = readb(base + S3C2410_ALMMIN); | 254 | clk_enable(info->rtc_src_clk); |
251 | alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR); | ||
252 | alm_tm->tm_mon = readb(base + S3C2410_ALMMON); | ||
253 | alm_tm->tm_mday = readb(base + S3C2410_ALMDATE); | ||
254 | alm_tm->tm_year = readb(base + S3C2410_ALMYEAR); | ||
255 | 255 | ||
256 | alm_en = readb(base + S3C2410_RTCALM); | 256 | alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC); |
257 | alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN); | ||
258 | alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR); | ||
259 | alm_tm->tm_mon = readb(info->base + S3C2410_ALMMON); | ||
260 | alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE); | ||
261 | alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR); | ||
262 | |||
263 | alm_en = readb(info->base + S3C2410_RTCALM); | ||
257 | 264 | ||
258 | alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; | 265 | alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; |
259 | 266 | ||
@@ -297,65 +304,74 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
297 | else | 304 | else |
298 | alm_tm->tm_year = -1; | 305 | alm_tm->tm_year = -1; |
299 | 306 | ||
300 | clk_disable(rtc_clk); | 307 | if (info->data->needs_src_clk) |
308 | clk_disable(info->rtc_src_clk); | ||
309 | clk_disable(info->rtc_clk); | ||
310 | |||
301 | return 0; | 311 | return 0; |
302 | } | 312 | } |
303 | 313 | ||
304 | static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | 314 | static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) |
305 | { | 315 | { |
316 | struct s3c_rtc *info = dev_get_drvdata(dev); | ||
306 | struct rtc_time *tm = &alrm->time; | 317 | struct rtc_time *tm = &alrm->time; |
307 | void __iomem *base = s3c_rtc_base; | ||
308 | unsigned int alrm_en; | 318 | unsigned int alrm_en; |
309 | 319 | ||
310 | clk_enable(rtc_clk); | 320 | clk_enable(info->rtc_clk); |
321 | if (info->data->needs_src_clk) | ||
322 | clk_enable(info->rtc_src_clk); | ||
323 | |||
311 | dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", | 324 | dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", |
312 | alrm->enabled, | 325 | alrm->enabled, |
313 | 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, | 326 | 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, |
314 | tm->tm_hour, tm->tm_min, tm->tm_sec); | 327 | tm->tm_hour, tm->tm_min, tm->tm_sec); |
315 | 328 | ||
316 | alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; | 329 | alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; |
317 | writeb(0x00, base + S3C2410_RTCALM); | 330 | writeb(0x00, info->base + S3C2410_RTCALM); |
318 | 331 | ||
319 | if (tm->tm_sec < 60 && tm->tm_sec >= 0) { | 332 | if (tm->tm_sec < 60 && tm->tm_sec >= 0) { |
320 | alrm_en |= S3C2410_RTCALM_SECEN; | 333 | alrm_en |= S3C2410_RTCALM_SECEN; |
321 | writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC); | 334 | writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC); |
322 | } | 335 | } |
323 | 336 | ||
324 | if (tm->tm_min < 60 && tm->tm_min >= 0) { | 337 | if (tm->tm_min < 60 && tm->tm_min >= 0) { |
325 | alrm_en |= S3C2410_RTCALM_MINEN; | 338 | alrm_en |= S3C2410_RTCALM_MINEN; |
326 | writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN); | 339 | writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN); |
327 | } | 340 | } |
328 | 341 | ||
329 | if (tm->tm_hour < 24 && tm->tm_hour >= 0) { | 342 | if (tm->tm_hour < 24 && tm->tm_hour >= 0) { |
330 | alrm_en |= S3C2410_RTCALM_HOUREN; | 343 | alrm_en |= S3C2410_RTCALM_HOUREN; |
331 | writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR); | 344 | writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR); |
332 | } | 345 | } |
333 | 346 | ||
334 | dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); | 347 | dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); |
335 | 348 | ||
336 | writeb(alrm_en, base + S3C2410_RTCALM); | 349 | writeb(alrm_en, info->base + S3C2410_RTCALM); |
337 | 350 | ||
338 | s3c_rtc_setaie(dev, alrm->enabled); | 351 | s3c_rtc_setaie(dev, alrm->enabled); |
339 | 352 | ||
340 | clk_disable(rtc_clk); | 353 | if (info->data->needs_src_clk) |
354 | clk_disable(info->rtc_src_clk); | ||
355 | clk_disable(info->rtc_clk); | ||
356 | |||
341 | return 0; | 357 | return 0; |
342 | } | 358 | } |
343 | 359 | ||
344 | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) | 360 | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) |
345 | { | 361 | { |
346 | unsigned int ticnt; | 362 | struct s3c_rtc *info = dev_get_drvdata(dev); |
347 | 363 | ||
348 | clk_enable(rtc_clk); | 364 | clk_enable(info->rtc_clk); |
349 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { | 365 | if (info->data->needs_src_clk) |
350 | ticnt = readw(s3c_rtc_base + S3C2410_RTCCON); | 366 | clk_enable(info->rtc_src_clk); |
351 | ticnt &= S3C64XX_RTCCON_TICEN; | 367 | |
352 | } else { | 368 | if (info->data->enable_tick) |
353 | ticnt = readb(s3c_rtc_base + S3C2410_TICNT); | 369 | info->data->enable_tick(info, seq); |
354 | ticnt &= S3C2410_TICNT_ENABLE; | 370 | |
355 | } | 371 | if (info->data->needs_src_clk) |
372 | clk_disable(info->rtc_src_clk); | ||
373 | clk_disable(info->rtc_clk); | ||
356 | 374 | ||
357 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); | ||
358 | clk_disable(rtc_clk); | ||
359 | return 0; | 375 | return 0; |
360 | } | 376 | } |
361 | 377 | ||
@@ -368,152 +384,199 @@ static const struct rtc_class_ops s3c_rtcops = { | |||
368 | .alarm_irq_enable = s3c_rtc_setaie, | 384 | .alarm_irq_enable = s3c_rtc_setaie, |
369 | }; | 385 | }; |
370 | 386 | ||
371 | static void s3c_rtc_enable(struct platform_device *pdev, int en) | 387 | static void s3c24xx_rtc_enable(struct s3c_rtc *info) |
372 | { | 388 | { |
373 | void __iomem *base = s3c_rtc_base; | 389 | unsigned int con, tmp; |
374 | unsigned int tmp; | ||
375 | 390 | ||
376 | if (s3c_rtc_base == NULL) | 391 | clk_enable(info->rtc_clk); |
377 | return; | 392 | if (info->data->needs_src_clk) |
378 | 393 | clk_enable(info->rtc_src_clk); | |
379 | clk_enable(rtc_clk); | ||
380 | if (!en) { | ||
381 | tmp = readw(base + S3C2410_RTCCON); | ||
382 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) | ||
383 | tmp &= ~S3C64XX_RTCCON_TICEN; | ||
384 | tmp &= ~S3C2410_RTCCON_RTCEN; | ||
385 | writew(tmp, base + S3C2410_RTCCON); | ||
386 | |||
387 | if (s3c_rtc_cpu_type != TYPE_S3C64XX) { | ||
388 | tmp = readb(base + S3C2410_TICNT); | ||
389 | tmp &= ~S3C2410_TICNT_ENABLE; | ||
390 | writeb(tmp, base + S3C2410_TICNT); | ||
391 | } | ||
392 | } else { | ||
393 | /* re-enable the device, and check it is ok */ | ||
394 | 394 | ||
395 | if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { | 395 | con = readw(info->base + S3C2410_RTCCON); |
396 | dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); | 396 | /* re-enable the device, and check it is ok */ |
397 | if ((con & S3C2410_RTCCON_RTCEN) == 0) { | ||
398 | dev_info(info->dev, "rtc disabled, re-enabling\n"); | ||
397 | 399 | ||
398 | tmp = readw(base + S3C2410_RTCCON); | 400 | tmp = readw(info->base + S3C2410_RTCCON); |
399 | writew(tmp | S3C2410_RTCCON_RTCEN, | 401 | writew(tmp | S3C2410_RTCCON_RTCEN, |
400 | base + S3C2410_RTCCON); | 402 | info->base + S3C2410_RTCCON); |
401 | } | 403 | } |
402 | 404 | ||
403 | if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) { | 405 | if (con & S3C2410_RTCCON_CNTSEL) { |
404 | dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n"); | 406 | dev_info(info->dev, "removing RTCCON_CNTSEL\n"); |
405 | 407 | ||
406 | tmp = readw(base + S3C2410_RTCCON); | 408 | tmp = readw(info->base + S3C2410_RTCCON); |
407 | writew(tmp & ~S3C2410_RTCCON_CNTSEL, | 409 | writew(tmp & ~S3C2410_RTCCON_CNTSEL, |
408 | base + S3C2410_RTCCON); | 410 | info->base + S3C2410_RTCCON); |
409 | } | 411 | } |
410 | 412 | ||
411 | if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) { | 413 | if (con & S3C2410_RTCCON_CLKRST) { |
412 | dev_info(&pdev->dev, "removing RTCCON_CLKRST\n"); | 414 | dev_info(info->dev, "removing RTCCON_CLKRST\n"); |
413 | 415 | ||
414 | tmp = readw(base + S3C2410_RTCCON); | 416 | tmp = readw(info->base + S3C2410_RTCCON); |
415 | writew(tmp & ~S3C2410_RTCCON_CLKRST, | 417 | writew(tmp & ~S3C2410_RTCCON_CLKRST, |
416 | base + S3C2410_RTCCON); | 418 | info->base + S3C2410_RTCCON); |
417 | } | ||
418 | } | 419 | } |
419 | clk_disable(rtc_clk); | 420 | |
421 | if (info->data->needs_src_clk) | ||
422 | clk_disable(info->rtc_src_clk); | ||
423 | clk_disable(info->rtc_clk); | ||
420 | } | 424 | } |
421 | 425 | ||
422 | static int s3c_rtc_remove(struct platform_device *dev) | 426 | static void s3c24xx_rtc_disable(struct s3c_rtc *info) |
423 | { | 427 | { |
424 | s3c_rtc_setaie(&dev->dev, 0); | 428 | unsigned int con; |
429 | |||
430 | clk_enable(info->rtc_clk); | ||
431 | if (info->data->needs_src_clk) | ||
432 | clk_enable(info->rtc_src_clk); | ||
433 | |||
434 | con = readw(info->base + S3C2410_RTCCON); | ||
435 | con &= ~S3C2410_RTCCON_RTCEN; | ||
436 | writew(con, info->base + S3C2410_RTCCON); | ||
425 | 437 | ||
426 | clk_unprepare(rtc_clk); | 438 | con = readb(info->base + S3C2410_TICNT); |
427 | rtc_clk = NULL; | 439 | con &= ~S3C2410_TICNT_ENABLE; |
440 | writeb(con, info->base + S3C2410_TICNT); | ||
441 | |||
442 | if (info->data->needs_src_clk) | ||
443 | clk_disable(info->rtc_src_clk); | ||
444 | clk_disable(info->rtc_clk); | ||
445 | } | ||
446 | |||
447 | static void s3c6410_rtc_disable(struct s3c_rtc *info) | ||
448 | { | ||
449 | unsigned int con; | ||
450 | |||
451 | clk_enable(info->rtc_clk); | ||
452 | if (info->data->needs_src_clk) | ||
453 | clk_enable(info->rtc_src_clk); | ||
454 | |||
455 | con = readw(info->base + S3C2410_RTCCON); | ||
456 | con &= ~S3C64XX_RTCCON_TICEN; | ||
457 | con &= ~S3C2410_RTCCON_RTCEN; | ||
458 | writew(con, info->base + S3C2410_RTCCON); | ||
459 | |||
460 | if (info->data->needs_src_clk) | ||
461 | clk_disable(info->rtc_src_clk); | ||
462 | clk_disable(info->rtc_clk); | ||
463 | } | ||
464 | |||
465 | static int s3c_rtc_remove(struct platform_device *pdev) | ||
466 | { | ||
467 | struct s3c_rtc *info = platform_get_drvdata(pdev); | ||
468 | |||
469 | s3c_rtc_setaie(info->dev, 0); | ||
470 | |||
471 | clk_unprepare(info->rtc_clk); | ||
472 | info->rtc_clk = NULL; | ||
428 | 473 | ||
429 | return 0; | 474 | return 0; |
430 | } | 475 | } |
431 | 476 | ||
432 | static const struct of_device_id s3c_rtc_dt_match[]; | 477 | static const struct of_device_id s3c_rtc_dt_match[]; |
433 | 478 | ||
434 | static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) | 479 | static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) |
435 | { | 480 | { |
436 | #ifdef CONFIG_OF | 481 | const struct of_device_id *match; |
437 | struct s3c_rtc_drv_data *data; | 482 | |
438 | if (pdev->dev.of_node) { | 483 | match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); |
439 | const struct of_device_id *match; | 484 | return (struct s3c_rtc_data *)match->data; |
440 | match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); | ||
441 | data = (struct s3c_rtc_drv_data *) match->data; | ||
442 | return data->cpu_type; | ||
443 | } | ||
444 | #endif | ||
445 | return platform_get_device_id(pdev)->driver_data; | ||
446 | } | 485 | } |
447 | 486 | ||
448 | static int s3c_rtc_probe(struct platform_device *pdev) | 487 | static int s3c_rtc_probe(struct platform_device *pdev) |
449 | { | 488 | { |
450 | struct rtc_device *rtc; | 489 | struct s3c_rtc *info = NULL; |
451 | struct rtc_time rtc_tm; | 490 | struct rtc_time rtc_tm; |
452 | struct resource *res; | 491 | struct resource *res; |
453 | int ret; | 492 | int ret; |
454 | int tmp; | ||
455 | 493 | ||
456 | dev_dbg(&pdev->dev, "%s: probe=%p\n", __func__, pdev); | 494 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
495 | if (!info) | ||
496 | return -ENOMEM; | ||
457 | 497 | ||
458 | /* find the IRQs */ | 498 | /* find the IRQs */ |
459 | 499 | info->irq_tick = platform_get_irq(pdev, 1); | |
460 | s3c_rtc_tickno = platform_get_irq(pdev, 1); | 500 | if (info->irq_tick < 0) { |
461 | if (s3c_rtc_tickno < 0) { | ||
462 | dev_err(&pdev->dev, "no irq for rtc tick\n"); | 501 | dev_err(&pdev->dev, "no irq for rtc tick\n"); |
463 | return s3c_rtc_tickno; | 502 | return info->irq_tick; |
503 | } | ||
504 | |||
505 | info->dev = &pdev->dev; | ||
506 | info->data = s3c_rtc_get_data(pdev); | ||
507 | if (!info->data) { | ||
508 | dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); | ||
509 | return -EINVAL; | ||
464 | } | 510 | } |
511 | spin_lock_init(&info->pie_lock); | ||
512 | spin_lock_init(&info->alarm_clk_lock); | ||
513 | |||
514 | platform_set_drvdata(pdev, info); | ||
465 | 515 | ||
466 | s3c_rtc_alarmno = platform_get_irq(pdev, 0); | 516 | info->irq_alarm = platform_get_irq(pdev, 0); |
467 | if (s3c_rtc_alarmno < 0) { | 517 | if (info->irq_alarm < 0) { |
468 | dev_err(&pdev->dev, "no irq for alarm\n"); | 518 | dev_err(&pdev->dev, "no irq for alarm\n"); |
469 | return s3c_rtc_alarmno; | 519 | return info->irq_alarm; |
470 | } | 520 | } |
471 | 521 | ||
472 | dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", | 522 | dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", |
473 | s3c_rtc_tickno, s3c_rtc_alarmno); | 523 | info->irq_tick, info->irq_alarm); |
474 | 524 | ||
475 | /* get the memory region */ | 525 | /* get the memory region */ |
476 | |||
477 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 526 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
478 | s3c_rtc_base = devm_ioremap_resource(&pdev->dev, res); | 527 | info->base = devm_ioremap_resource(&pdev->dev, res); |
479 | if (IS_ERR(s3c_rtc_base)) | 528 | if (IS_ERR(info->base)) |
480 | return PTR_ERR(s3c_rtc_base); | 529 | return PTR_ERR(info->base); |
481 | 530 | ||
482 | rtc_clk = devm_clk_get(&pdev->dev, "rtc"); | 531 | info->rtc_clk = devm_clk_get(&pdev->dev, "rtc"); |
483 | if (IS_ERR(rtc_clk)) { | 532 | if (IS_ERR(info->rtc_clk)) { |
484 | dev_err(&pdev->dev, "failed to find rtc clock source\n"); | 533 | dev_err(&pdev->dev, "failed to find rtc clock\n"); |
485 | ret = PTR_ERR(rtc_clk); | 534 | return PTR_ERR(info->rtc_clk); |
486 | rtc_clk = NULL; | ||
487 | return ret; | ||
488 | } | 535 | } |
536 | clk_prepare_enable(info->rtc_clk); | ||
489 | 537 | ||
490 | clk_prepare_enable(rtc_clk); | 538 | info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); |
539 | if (IS_ERR(info->rtc_src_clk)) { | ||
540 | dev_err(&pdev->dev, "failed to find rtc source clock\n"); | ||
541 | return PTR_ERR(info->rtc_src_clk); | ||
542 | } | ||
543 | clk_prepare_enable(info->rtc_src_clk); | ||
491 | 544 | ||
492 | /* check to see if everything is setup correctly */ | ||
493 | 545 | ||
494 | s3c_rtc_enable(pdev, 1); | 546 | /* check to see if everything is setup correctly */ |
547 | if (info->data->enable) | ||
548 | info->data->enable(info); | ||
495 | 549 | ||
496 | dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", | 550 | dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", |
497 | readw(s3c_rtc_base + S3C2410_RTCCON)); | 551 | readw(info->base + S3C2410_RTCCON)); |
498 | 552 | ||
499 | device_init_wakeup(&pdev->dev, 1); | 553 | device_init_wakeup(&pdev->dev, 1); |
500 | 554 | ||
501 | /* register RTC and exit */ | 555 | /* register RTC and exit */ |
502 | 556 | info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, | |
503 | rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, | ||
504 | THIS_MODULE); | 557 | THIS_MODULE); |
505 | 558 | if (IS_ERR(info->rtc)) { | |
506 | if (IS_ERR(rtc)) { | ||
507 | dev_err(&pdev->dev, "cannot attach rtc\n"); | 559 | dev_err(&pdev->dev, "cannot attach rtc\n"); |
508 | ret = PTR_ERR(rtc); | 560 | ret = PTR_ERR(info->rtc); |
509 | goto err_nortc; | 561 | goto err_nortc; |
510 | } | 562 | } |
511 | 563 | ||
512 | s3c_rtc_cpu_type = s3c_rtc_get_driver_data(pdev); | 564 | ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq, |
565 | 0, "s3c2410-rtc alarm", info); | ||
566 | if (ret) { | ||
567 | dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret); | ||
568 | goto err_nortc; | ||
569 | } | ||
513 | 570 | ||
514 | /* Check RTC Time */ | 571 | ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq, |
572 | 0, "s3c2410-rtc tick", info); | ||
573 | if (ret) { | ||
574 | dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret); | ||
575 | goto err_nortc; | ||
576 | } | ||
515 | 577 | ||
516 | s3c_rtc_gettime(NULL, &rtc_tm); | 578 | /* Check RTC Time */ |
579 | s3c_rtc_gettime(&pdev->dev, &rtc_tm); | ||
517 | 580 | ||
518 | if (rtc_valid_tm(&rtc_tm)) { | 581 | if (rtc_valid_tm(&rtc_tm)) { |
519 | rtc_tm.tm_year = 100; | 582 | rtc_tm.tm_year = 100; |
@@ -523,163 +586,312 @@ static int s3c_rtc_probe(struct platform_device *pdev) | |||
523 | rtc_tm.tm_min = 0; | 586 | rtc_tm.tm_min = 0; |
524 | rtc_tm.tm_sec = 0; | 587 | rtc_tm.tm_sec = 0; |
525 | 588 | ||
526 | s3c_rtc_settime(NULL, &rtc_tm); | 589 | s3c_rtc_settime(&pdev->dev, &rtc_tm); |
527 | 590 | ||
528 | dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); | 591 | dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); |
529 | } | 592 | } |
530 | 593 | ||
531 | if (s3c_rtc_cpu_type != TYPE_S3C2410) | 594 | if (info->data->select_tick_clk) |
532 | rtc->max_user_freq = 32768; | 595 | info->data->select_tick_clk(info); |
533 | else | ||
534 | rtc->max_user_freq = 128; | ||
535 | |||
536 | if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) { | ||
537 | tmp = readw(s3c_rtc_base + S3C2410_RTCCON); | ||
538 | tmp |= S3C2443_RTCCON_TICSEL; | ||
539 | writew(tmp, s3c_rtc_base + S3C2410_RTCCON); | ||
540 | } | ||
541 | 596 | ||
542 | platform_set_drvdata(pdev, rtc); | 597 | s3c_rtc_setfreq(info, 1); |
543 | 598 | ||
544 | s3c_rtc_setfreq(&pdev->dev, 1); | 599 | if (info->data->needs_src_clk) |
545 | 600 | clk_disable(info->rtc_src_clk); | |
546 | ret = devm_request_irq(&pdev->dev, s3c_rtc_alarmno, s3c_rtc_alarmirq, | 601 | clk_disable(info->rtc_clk); |
547 | 0, "s3c2410-rtc alarm", rtc); | ||
548 | if (ret) { | ||
549 | dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret); | ||
550 | goto err_nortc; | ||
551 | } | ||
552 | |||
553 | ret = devm_request_irq(&pdev->dev, s3c_rtc_tickno, s3c_rtc_tickirq, | ||
554 | 0, "s3c2410-rtc tick", rtc); | ||
555 | if (ret) { | ||
556 | dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret); | ||
557 | goto err_nortc; | ||
558 | } | ||
559 | |||
560 | clk_disable(rtc_clk); | ||
561 | 602 | ||
562 | return 0; | 603 | return 0; |
563 | 604 | ||
564 | err_nortc: | 605 | err_nortc: |
565 | s3c_rtc_enable(pdev, 0); | 606 | if (info->data->disable) |
566 | clk_disable_unprepare(rtc_clk); | 607 | info->data->disable(info); |
608 | clk_disable_unprepare(info->rtc_clk); | ||
567 | 609 | ||
568 | return ret; | 610 | return ret; |
569 | } | 611 | } |
570 | 612 | ||
571 | #ifdef CONFIG_PM_SLEEP | 613 | #ifdef CONFIG_PM_SLEEP |
572 | /* RTC Power management control */ | ||
573 | |||
574 | static int ticnt_save, ticnt_en_save; | ||
575 | static bool wake_en; | ||
576 | 614 | ||
577 | static int s3c_rtc_suspend(struct device *dev) | 615 | static int s3c_rtc_suspend(struct device *dev) |
578 | { | 616 | { |
579 | struct platform_device *pdev = to_platform_device(dev); | 617 | struct s3c_rtc *info = dev_get_drvdata(dev); |
618 | |||
619 | clk_enable(info->rtc_clk); | ||
620 | if (info->data->needs_src_clk) | ||
621 | clk_enable(info->rtc_src_clk); | ||
580 | 622 | ||
581 | clk_enable(rtc_clk); | ||
582 | /* save TICNT for anyone using periodic interrupts */ | 623 | /* save TICNT for anyone using periodic interrupts */ |
583 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { | 624 | if (info->data->save_tick_cnt) |
584 | ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON); | 625 | info->data->save_tick_cnt(info); |
585 | ticnt_en_save &= S3C64XX_RTCCON_TICEN; | 626 | |
586 | ticnt_save = readl(s3c_rtc_base + S3C2410_TICNT); | 627 | if (info->data->disable) |
587 | } else { | 628 | info->data->disable(info); |
588 | ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); | ||
589 | } | ||
590 | s3c_rtc_enable(pdev, 0); | ||
591 | 629 | ||
592 | if (device_may_wakeup(dev) && !wake_en) { | 630 | if (device_may_wakeup(dev) && !info->wake_en) { |
593 | if (enable_irq_wake(s3c_rtc_alarmno) == 0) | 631 | if (enable_irq_wake(info->irq_alarm) == 0) |
594 | wake_en = true; | 632 | info->wake_en = true; |
595 | else | 633 | else |
596 | dev_err(dev, "enable_irq_wake failed\n"); | 634 | dev_err(dev, "enable_irq_wake failed\n"); |
597 | } | 635 | } |
598 | clk_disable(rtc_clk); | 636 | |
637 | if (info->data->needs_src_clk) | ||
638 | clk_disable(info->rtc_src_clk); | ||
639 | clk_disable(info->rtc_clk); | ||
599 | 640 | ||
600 | return 0; | 641 | return 0; |
601 | } | 642 | } |
602 | 643 | ||
603 | static int s3c_rtc_resume(struct device *dev) | 644 | static int s3c_rtc_resume(struct device *dev) |
604 | { | 645 | { |
605 | struct platform_device *pdev = to_platform_device(dev); | 646 | struct s3c_rtc *info = dev_get_drvdata(dev); |
606 | unsigned int tmp; | ||
607 | 647 | ||
608 | clk_enable(rtc_clk); | 648 | clk_enable(info->rtc_clk); |
609 | s3c_rtc_enable(pdev, 1); | 649 | if (info->data->needs_src_clk) |
610 | if (s3c_rtc_cpu_type == TYPE_S3C64XX) { | 650 | clk_enable(info->rtc_src_clk); |
611 | writel(ticnt_save, s3c_rtc_base + S3C2410_TICNT); | ||
612 | if (ticnt_en_save) { | ||
613 | tmp = readw(s3c_rtc_base + S3C2410_RTCCON); | ||
614 | writew(tmp | ticnt_en_save, | ||
615 | s3c_rtc_base + S3C2410_RTCCON); | ||
616 | } | ||
617 | } else { | ||
618 | writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); | ||
619 | } | ||
620 | 651 | ||
621 | if (device_may_wakeup(dev) && wake_en) { | 652 | if (info->data->enable) |
622 | disable_irq_wake(s3c_rtc_alarmno); | 653 | info->data->enable(info); |
623 | wake_en = false; | 654 | |
655 | if (info->data->restore_tick_cnt) | ||
656 | info->data->restore_tick_cnt(info); | ||
657 | |||
658 | if (device_may_wakeup(dev) && info->wake_en) { | ||
659 | disable_irq_wake(info->irq_alarm); | ||
660 | info->wake_en = false; | ||
624 | } | 661 | } |
625 | clk_disable(rtc_clk); | 662 | |
663 | if (info->data->needs_src_clk) | ||
664 | clk_disable(info->rtc_src_clk); | ||
665 | clk_disable(info->rtc_clk); | ||
626 | 666 | ||
627 | return 0; | 667 | return 0; |
628 | } | 668 | } |
629 | #endif | 669 | #endif |
630 | |||
631 | static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); | 670 | static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); |
632 | 671 | ||
633 | #ifdef CONFIG_OF | 672 | static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) |
634 | static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = { | 673 | { |
635 | [TYPE_S3C2410] = { TYPE_S3C2410 }, | 674 | clk_enable(info->rtc_clk); |
636 | [TYPE_S3C2416] = { TYPE_S3C2416 }, | 675 | if (info->data->needs_src_clk) |
637 | [TYPE_S3C2443] = { TYPE_S3C2443 }, | 676 | clk_enable(info->rtc_src_clk); |
638 | [TYPE_S3C64XX] = { TYPE_S3C64XX }, | 677 | rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); |
678 | if (info->data->needs_src_clk) | ||
679 | clk_disable(info->rtc_src_clk); | ||
680 | clk_disable(info->rtc_clk); | ||
681 | |||
682 | s3c_rtc_alarm_clk_enable(info, false); | ||
683 | } | ||
684 | |||
685 | static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) | ||
686 | { | ||
687 | clk_enable(info->rtc_clk); | ||
688 | if (info->data->needs_src_clk) | ||
689 | clk_enable(info->rtc_src_clk); | ||
690 | rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); | ||
691 | writeb(mask, info->base + S3C2410_INTP); | ||
692 | if (info->data->needs_src_clk) | ||
693 | clk_disable(info->rtc_src_clk); | ||
694 | clk_disable(info->rtc_clk); | ||
695 | |||
696 | s3c_rtc_alarm_clk_enable(info, false); | ||
697 | } | ||
698 | |||
699 | static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
700 | { | ||
701 | unsigned int tmp = 0; | ||
702 | int val; | ||
703 | |||
704 | tmp = readb(info->base + S3C2410_TICNT); | ||
705 | tmp &= S3C2410_TICNT_ENABLE; | ||
706 | |||
707 | val = (info->rtc->max_user_freq / freq) - 1; | ||
708 | tmp |= val; | ||
709 | |||
710 | writel(tmp, info->base + S3C2410_TICNT); | ||
711 | } | ||
712 | |||
713 | static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
714 | { | ||
715 | unsigned int tmp = 0; | ||
716 | int val; | ||
717 | |||
718 | tmp = readb(info->base + S3C2410_TICNT); | ||
719 | tmp &= S3C2410_TICNT_ENABLE; | ||
720 | |||
721 | val = (info->rtc->max_user_freq / freq) - 1; | ||
722 | |||
723 | tmp |= S3C2443_TICNT_PART(val); | ||
724 | writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); | ||
725 | |||
726 | writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2); | ||
727 | |||
728 | writel(tmp, info->base + S3C2410_TICNT); | ||
729 | } | ||
730 | |||
731 | static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
732 | { | ||
733 | unsigned int tmp = 0; | ||
734 | int val; | ||
735 | |||
736 | tmp = readb(info->base + S3C2410_TICNT); | ||
737 | tmp &= S3C2410_TICNT_ENABLE; | ||
738 | |||
739 | val = (info->rtc->max_user_freq / freq) - 1; | ||
740 | |||
741 | tmp |= S3C2443_TICNT_PART(val); | ||
742 | writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); | ||
743 | |||
744 | writel(tmp, info->base + S3C2410_TICNT); | ||
745 | } | ||
746 | |||
747 | static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
748 | { | ||
749 | int val; | ||
750 | |||
751 | val = (info->rtc->max_user_freq / freq) - 1; | ||
752 | writel(val, info->base + S3C2410_TICNT); | ||
753 | } | ||
754 | |||
755 | static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) | ||
756 | { | ||
757 | unsigned int ticnt; | ||
758 | |||
759 | ticnt = readb(info->base + S3C2410_TICNT); | ||
760 | ticnt &= S3C2410_TICNT_ENABLE; | ||
761 | |||
762 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); | ||
763 | } | ||
764 | |||
765 | static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info) | ||
766 | { | ||
767 | unsigned int con; | ||
768 | |||
769 | con = readw(info->base + S3C2410_RTCCON); | ||
770 | con |= S3C2443_RTCCON_TICSEL; | ||
771 | writew(con, info->base + S3C2410_RTCCON); | ||
772 | } | ||
773 | |||
774 | static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) | ||
775 | { | ||
776 | unsigned int ticnt; | ||
777 | |||
778 | ticnt = readw(info->base + S3C2410_RTCCON); | ||
779 | ticnt &= S3C64XX_RTCCON_TICEN; | ||
780 | |||
781 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); | ||
782 | } | ||
783 | |||
784 | static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info) | ||
785 | { | ||
786 | info->ticnt_save = readb(info->base + S3C2410_TICNT); | ||
787 | } | ||
788 | |||
789 | static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info) | ||
790 | { | ||
791 | writeb(info->ticnt_save, info->base + S3C2410_TICNT); | ||
792 | } | ||
793 | |||
794 | static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info) | ||
795 | { | ||
796 | info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); | ||
797 | info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; | ||
798 | info->ticnt_save = readl(info->base + S3C2410_TICNT); | ||
799 | } | ||
800 | |||
801 | static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info) | ||
802 | { | ||
803 | unsigned int con; | ||
804 | |||
805 | writel(info->ticnt_save, info->base + S3C2410_TICNT); | ||
806 | if (info->ticnt_en_save) { | ||
807 | con = readw(info->base + S3C2410_RTCCON); | ||
808 | writew(con | info->ticnt_en_save, | ||
809 | info->base + S3C2410_RTCCON); | ||
810 | } | ||
811 | } | ||
812 | |||
813 | static struct s3c_rtc_data const s3c2410_rtc_data = { | ||
814 | .max_user_freq = 128, | ||
815 | .irq_handler = s3c24xx_rtc_irq, | ||
816 | .set_freq = s3c2410_rtc_setfreq, | ||
817 | .enable_tick = s3c24xx_rtc_enable_tick, | ||
818 | .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, | ||
819 | .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, | ||
820 | .enable = s3c24xx_rtc_enable, | ||
821 | .disable = s3c24xx_rtc_disable, | ||
822 | }; | ||
823 | |||
824 | static struct s3c_rtc_data const s3c2416_rtc_data = { | ||
825 | .max_user_freq = 32768, | ||
826 | .irq_handler = s3c24xx_rtc_irq, | ||
827 | .set_freq = s3c2416_rtc_setfreq, | ||
828 | .enable_tick = s3c24xx_rtc_enable_tick, | ||
829 | .select_tick_clk = s3c2416_rtc_select_tick_clk, | ||
830 | .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, | ||
831 | .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, | ||
832 | .enable = s3c24xx_rtc_enable, | ||
833 | .disable = s3c24xx_rtc_disable, | ||
834 | }; | ||
835 | |||
836 | static struct s3c_rtc_data const s3c2443_rtc_data = { | ||
837 | .max_user_freq = 32768, | ||
838 | .irq_handler = s3c24xx_rtc_irq, | ||
839 | .set_freq = s3c2443_rtc_setfreq, | ||
840 | .enable_tick = s3c24xx_rtc_enable_tick, | ||
841 | .select_tick_clk = s3c2416_rtc_select_tick_clk, | ||
842 | .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, | ||
843 | .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, | ||
844 | .enable = s3c24xx_rtc_enable, | ||
845 | .disable = s3c24xx_rtc_disable, | ||
846 | }; | ||
847 | |||
848 | static struct s3c_rtc_data const s3c6410_rtc_data = { | ||
849 | .max_user_freq = 32768, | ||
850 | .irq_handler = s3c6410_rtc_irq, | ||
851 | .set_freq = s3c6410_rtc_setfreq, | ||
852 | .enable_tick = s3c6410_rtc_enable_tick, | ||
853 | .save_tick_cnt = s3c6410_rtc_save_tick_cnt, | ||
854 | .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, | ||
855 | .enable = s3c24xx_rtc_enable, | ||
856 | .disable = s3c6410_rtc_disable, | ||
857 | }; | ||
858 | |||
859 | static struct s3c_rtc_data const exynos3250_rtc_data = { | ||
860 | .max_user_freq = 32768, | ||
861 | .needs_src_clk = true, | ||
862 | .irq_handler = s3c6410_rtc_irq, | ||
863 | .set_freq = s3c6410_rtc_setfreq, | ||
864 | .enable_tick = s3c6410_rtc_enable_tick, | ||
865 | .save_tick_cnt = s3c6410_rtc_save_tick_cnt, | ||
866 | .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, | ||
867 | .enable = s3c24xx_rtc_enable, | ||
868 | .disable = s3c6410_rtc_disable, | ||
639 | }; | 869 | }; |
640 | 870 | ||
641 | static const struct of_device_id s3c_rtc_dt_match[] = { | 871 | static const struct of_device_id s3c_rtc_dt_match[] = { |
642 | { | 872 | { |
643 | .compatible = "samsung,s3c2410-rtc", | 873 | .compatible = "samsung,s3c2410-rtc", |
644 | .data = &s3c_rtc_drv_data_array[TYPE_S3C2410], | 874 | .data = (void *)&s3c2410_rtc_data, |
645 | }, { | 875 | }, { |
646 | .compatible = "samsung,s3c2416-rtc", | 876 | .compatible = "samsung,s3c2416-rtc", |
647 | .data = &s3c_rtc_drv_data_array[TYPE_S3C2416], | 877 | .data = (void *)&s3c2416_rtc_data, |
648 | }, { | 878 | }, { |
649 | .compatible = "samsung,s3c2443-rtc", | 879 | .compatible = "samsung,s3c2443-rtc", |
650 | .data = &s3c_rtc_drv_data_array[TYPE_S3C2443], | 880 | .data = (void *)&s3c2443_rtc_data, |
651 | }, { | 881 | }, { |
652 | .compatible = "samsung,s3c6410-rtc", | 882 | .compatible = "samsung,s3c6410-rtc", |
653 | .data = &s3c_rtc_drv_data_array[TYPE_S3C64XX], | 883 | .data = (void *)&s3c6410_rtc_data, |
654 | }, | ||
655 | {}, | ||
656 | }; | ||
657 | MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); | ||
658 | #endif | ||
659 | |||
660 | static struct platform_device_id s3c_rtc_driver_ids[] = { | ||
661 | { | ||
662 | .name = "s3c2410-rtc", | ||
663 | .driver_data = TYPE_S3C2410, | ||
664 | }, { | ||
665 | .name = "s3c2416-rtc", | ||
666 | .driver_data = TYPE_S3C2416, | ||
667 | }, { | ||
668 | .name = "s3c2443-rtc", | ||
669 | .driver_data = TYPE_S3C2443, | ||
670 | }, { | 884 | }, { |
671 | .name = "s3c64xx-rtc", | 885 | .compatible = "samsung,exynos3250-rtc", |
672 | .driver_data = TYPE_S3C64XX, | 886 | .data = (void *)&exynos3250_rtc_data, |
673 | }, | 887 | }, |
674 | { } | 888 | { /* sentinel */ }, |
675 | }; | 889 | }; |
676 | 890 | MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); | |
677 | MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); | ||
678 | 891 | ||
679 | static struct platform_driver s3c_rtc_driver = { | 892 | static struct platform_driver s3c_rtc_driver = { |
680 | .probe = s3c_rtc_probe, | 893 | .probe = s3c_rtc_probe, |
681 | .remove = s3c_rtc_remove, | 894 | .remove = s3c_rtc_remove, |
682 | .id_table = s3c_rtc_driver_ids, | ||
683 | .driver = { | 895 | .driver = { |
684 | .name = "s3c-rtc", | 896 | .name = "s3c-rtc", |
685 | .owner = THIS_MODULE, | 897 | .owner = THIS_MODULE, |
@@ -687,7 +899,6 @@ static struct platform_driver s3c_rtc_driver = { | |||
687 | .of_match_table = of_match_ptr(s3c_rtc_dt_match), | 899 | .of_match_table = of_match_ptr(s3c_rtc_dt_match), |
688 | }, | 900 | }, |
689 | }; | 901 | }; |
690 | |||
691 | module_platform_driver(s3c_rtc_driver); | 902 | module_platform_driver(s3c_rtc_driver); |
692 | 903 | ||
693 | MODULE_DESCRIPTION("Samsung S3C RTC Driver"); | 904 | MODULE_DESCRIPTION("Samsung S3C RTC Driver"); |