diff options
author | Angelo Castello <angelo.castello@st.com> | 2008-03-05 22:50:53 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2008-04-18 12:50:01 -0400 |
commit | b420b1a7a17ea88531d0e12b2f2679a0c8365803 (patch) | |
tree | d94200d33678cbc9866ce8a338c80edbcdca5fcb /drivers/rtc/rtc-sh.c | |
parent | 90fce7f4fb98c4256976aaa89b4453a544d74fd4 (diff) |
rtc: rtc-sh: Add support for periodic IRQs.
This adds support for periodic IRQs to the rtc-sh driver.
RTC_IRQP_READ/RTC_IRQP_SET are added, with a number of other fixes and
reordering across the rest of the code.
Signed-off-by: Angelo Castello <angelo.castello@st.com>
Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/rtc/rtc-sh.c')
-rw-r--r-- | drivers/rtc/rtc-sh.c | 296 |
1 files changed, 187 insertions, 109 deletions
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 9e9caa5d7f5f..c594b34c6767 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c | |||
@@ -1,8 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * SuperH On-Chip RTC Support | 2 | * SuperH On-Chip RTC Support |
3 | * | 3 | * |
4 | * Copyright (C) 2006, 2007 Paul Mundt | 4 | * Copyright (C) 2006, 2007, 2008 Paul Mundt |
5 | * Copyright (C) 2006 Jamie Lenehan | 5 | * Copyright (C) 2006 Jamie Lenehan |
6 | * Copyright (C) 2008 Angelo Castello | ||
6 | * | 7 | * |
7 | * Based on the old arch/sh/kernel/cpu/rtc.c by: | 8 | * Based on the old arch/sh/kernel/cpu/rtc.c by: |
8 | * | 9 | * |
@@ -26,7 +27,7 @@ | |||
26 | #include <asm/rtc.h> | 27 | #include <asm/rtc.h> |
27 | 28 | ||
28 | #define DRV_NAME "sh-rtc" | 29 | #define DRV_NAME "sh-rtc" |
29 | #define DRV_VERSION "0.1.6" | 30 | #define DRV_VERSION "0.2.0" |
30 | 31 | ||
31 | #define RTC_REG(r) ((r) * rtc_reg_size) | 32 | #define RTC_REG(r) ((r) * rtc_reg_size) |
32 | 33 | ||
@@ -63,6 +64,13 @@ | |||
63 | /* ALARM Bits - or with BCD encoded value */ | 64 | /* ALARM Bits - or with BCD encoded value */ |
64 | #define AR_ENB 0x80 /* Enable for alarm cmp */ | 65 | #define AR_ENB 0x80 /* Enable for alarm cmp */ |
65 | 66 | ||
67 | /* Period Bits */ | ||
68 | #define PF_HP 0x100 /* Enable Half Period to support 8,32,128Hz */ | ||
69 | #define PF_COUNT 0x200 /* Half periodic counter */ | ||
70 | #define PF_OXS 0x400 /* Periodic One x Second */ | ||
71 | #define PF_KOU 0x800 /* Kernel or User periodic request 1=kernel */ | ||
72 | #define PF_MASK 0xf00 | ||
73 | |||
66 | /* RCR1 Bits */ | 74 | /* RCR1 Bits */ |
67 | #define RCR1_CF 0x80 /* Carry Flag */ | 75 | #define RCR1_CF 0x80 /* Carry Flag */ |
68 | #define RCR1_CIE 0x10 /* Carry Interrupt Enable */ | 76 | #define RCR1_CIE 0x10 /* Carry Interrupt Enable */ |
@@ -84,33 +92,24 @@ struct sh_rtc { | |||
84 | unsigned int alarm_irq, periodic_irq, carry_irq; | 92 | unsigned int alarm_irq, periodic_irq, carry_irq; |
85 | struct rtc_device *rtc_dev; | 93 | struct rtc_device *rtc_dev; |
86 | spinlock_t lock; | 94 | spinlock_t lock; |
87 | int rearm_aie; | ||
88 | unsigned long capabilities; /* See asm-sh/rtc.h for cap bits */ | 95 | unsigned long capabilities; /* See asm-sh/rtc.h for cap bits */ |
96 | unsigned short periodic_freq; | ||
89 | }; | 97 | }; |
90 | 98 | ||
91 | static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) | 99 | static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) |
92 | { | 100 | { |
93 | struct platform_device *pdev = to_platform_device(dev_id); | 101 | struct sh_rtc *rtc = dev_id; |
94 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | 102 | unsigned int tmp; |
95 | unsigned int tmp, events = 0; | ||
96 | 103 | ||
97 | spin_lock(&rtc->lock); | 104 | spin_lock(&rtc->lock); |
98 | 105 | ||
99 | tmp = readb(rtc->regbase + RCR1); | 106 | tmp = readb(rtc->regbase + RCR1); |
100 | tmp &= ~RCR1_CF; | 107 | tmp &= ~RCR1_CF; |
101 | |||
102 | if (rtc->rearm_aie) { | ||
103 | if (tmp & RCR1_AF) | ||
104 | tmp &= ~RCR1_AF; /* try to clear AF again */ | ||
105 | else { | ||
106 | tmp |= RCR1_AIE; /* AF has cleared, rearm IRQ */ | ||
107 | rtc->rearm_aie = 0; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | writeb(tmp, rtc->regbase + RCR1); | 108 | writeb(tmp, rtc->regbase + RCR1); |
112 | 109 | ||
113 | rtc_update_irq(rtc->rtc_dev, 1, events); | 110 | /* Users have requested One x Second IRQ */ |
111 | if (rtc->periodic_freq & PF_OXS) | ||
112 | rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); | ||
114 | 113 | ||
115 | spin_unlock(&rtc->lock); | 114 | spin_unlock(&rtc->lock); |
116 | 115 | ||
@@ -119,47 +118,48 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) | |||
119 | 118 | ||
120 | static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) | 119 | static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) |
121 | { | 120 | { |
122 | struct platform_device *pdev = to_platform_device(dev_id); | 121 | struct sh_rtc *rtc = dev_id; |
123 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | 122 | unsigned int tmp; |
124 | unsigned int tmp, events = 0; | ||
125 | 123 | ||
126 | spin_lock(&rtc->lock); | 124 | spin_lock(&rtc->lock); |
127 | 125 | ||
128 | tmp = readb(rtc->regbase + RCR1); | 126 | tmp = readb(rtc->regbase + RCR1); |
129 | 127 | tmp &= ~(RCR1_AF | RCR1_AIE); | |
130 | /* | ||
131 | * If AF is set then the alarm has triggered. If we clear AF while | ||
132 | * the alarm time still matches the RTC time then AF will | ||
133 | * immediately be set again, and if AIE is enabled then the alarm | ||
134 | * interrupt will immediately be retrigger. So we clear AIE here | ||
135 | * and use rtc->rearm_aie so that the carry interrupt will keep | ||
136 | * trying to clear AF and once it stays cleared it'll re-enable | ||
137 | * AIE. | ||
138 | */ | ||
139 | if (tmp & RCR1_AF) { | ||
140 | events |= RTC_AF | RTC_IRQF; | ||
141 | |||
142 | tmp &= ~(RCR1_AF|RCR1_AIE); | ||
143 | |||
144 | writeb(tmp, rtc->regbase + RCR1); | 128 | writeb(tmp, rtc->regbase + RCR1); |
145 | 129 | ||
146 | rtc->rearm_aie = 1; | 130 | rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); |
147 | |||
148 | rtc_update_irq(rtc->rtc_dev, 1, events); | ||
149 | } | ||
150 | 131 | ||
151 | spin_unlock(&rtc->lock); | 132 | spin_unlock(&rtc->lock); |
133 | |||
152 | return IRQ_HANDLED; | 134 | return IRQ_HANDLED; |
153 | } | 135 | } |
154 | 136 | ||
155 | static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) | 137 | static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) |
156 | { | 138 | { |
157 | struct platform_device *pdev = to_platform_device(dev_id); | 139 | struct sh_rtc *rtc = dev_id; |
158 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | 140 | struct rtc_device *rtc_dev = rtc->rtc_dev; |
141 | unsigned int tmp; | ||
159 | 142 | ||
160 | spin_lock(&rtc->lock); | 143 | spin_lock(&rtc->lock); |
161 | 144 | ||
162 | rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); | 145 | tmp = readb(rtc->regbase + RCR2); |
146 | tmp &= ~RCR2_PEF; | ||
147 | writeb(tmp, rtc->regbase + RCR2); | ||
148 | |||
149 | /* Half period enabled than one skipped and the next notified */ | ||
150 | if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT)) | ||
151 | rtc->periodic_freq &= ~PF_COUNT; | ||
152 | else { | ||
153 | if (rtc->periodic_freq & PF_HP) | ||
154 | rtc->periodic_freq |= PF_COUNT; | ||
155 | if (rtc->periodic_freq & PF_KOU) { | ||
156 | spin_lock(&rtc_dev->irq_task_lock); | ||
157 | if (rtc_dev->irq_task) | ||
158 | rtc_dev->irq_task->func(rtc_dev->irq_task->private_data); | ||
159 | spin_unlock(&rtc_dev->irq_task_lock); | ||
160 | } else | ||
161 | rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); | ||
162 | } | ||
163 | 163 | ||
164 | spin_unlock(&rtc->lock); | 164 | spin_unlock(&rtc->lock); |
165 | 165 | ||
@@ -176,8 +176,8 @@ static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) | |||
176 | tmp = readb(rtc->regbase + RCR2); | 176 | tmp = readb(rtc->regbase + RCR2); |
177 | 177 | ||
178 | if (enable) { | 178 | if (enable) { |
179 | tmp &= ~RCR2_PESMASK; | 179 | tmp &= ~RCR2_PEF; /* Clear PES bit */ |
180 | tmp |= RCR2_PEF | (2 << 4); | 180 | tmp |= (rtc->periodic_freq & ~PF_HP); /* Set PES2-0 */ |
181 | } else | 181 | } else |
182 | tmp &= ~(RCR2_PESMASK | RCR2_PEF); | 182 | tmp &= ~(RCR2_PESMASK | RCR2_PEF); |
183 | 183 | ||
@@ -186,82 +186,81 @@ static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) | |||
186 | spin_unlock_irq(&rtc->lock); | 186 | spin_unlock_irq(&rtc->lock); |
187 | } | 187 | } |
188 | 188 | ||
189 | static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) | 189 | static inline int sh_rtc_setfreq(struct device *dev, unsigned int freq) |
190 | { | 190 | { |
191 | struct sh_rtc *rtc = dev_get_drvdata(dev); | 191 | struct sh_rtc *rtc = dev_get_drvdata(dev); |
192 | unsigned int tmp; | 192 | int tmp, ret = 0; |
193 | 193 | ||
194 | spin_lock_irq(&rtc->lock); | 194 | spin_lock_irq(&rtc->lock); |
195 | tmp = rtc->periodic_freq & PF_MASK; | ||
195 | 196 | ||
196 | tmp = readb(rtc->regbase + RCR1); | 197 | switch (freq) { |
197 | 198 | case 0: | |
198 | if (!enable) { | 199 | rtc->periodic_freq = 0x00; |
199 | tmp &= ~RCR1_AIE; | 200 | break; |
200 | rtc->rearm_aie = 0; | 201 | case 1: |
201 | } else if (rtc->rearm_aie == 0) | 202 | rtc->periodic_freq = 0x60; |
202 | tmp |= RCR1_AIE; | 203 | break; |
204 | case 2: | ||
205 | rtc->periodic_freq = 0x50; | ||
206 | break; | ||
207 | case 4: | ||
208 | rtc->periodic_freq = 0x40; | ||
209 | break; | ||
210 | case 8: | ||
211 | rtc->periodic_freq = 0x30 | PF_HP; | ||
212 | break; | ||
213 | case 16: | ||
214 | rtc->periodic_freq = 0x30; | ||
215 | break; | ||
216 | case 32: | ||
217 | rtc->periodic_freq = 0x20 | PF_HP; | ||
218 | break; | ||
219 | case 64: | ||
220 | rtc->periodic_freq = 0x20; | ||
221 | break; | ||
222 | case 128: | ||
223 | rtc->periodic_freq = 0x10 | PF_HP; | ||
224 | break; | ||
225 | case 256: | ||
226 | rtc->periodic_freq = 0x10; | ||
227 | break; | ||
228 | default: | ||
229 | ret = -ENOTSUPP; | ||
230 | } | ||
203 | 231 | ||
204 | writeb(tmp, rtc->regbase + RCR1); | 232 | if (ret == 0) { |
233 | rtc->periodic_freq |= tmp; | ||
234 | rtc->rtc_dev->irq_freq = freq; | ||
235 | } | ||
205 | 236 | ||
206 | spin_unlock_irq(&rtc->lock); | 237 | spin_unlock_irq(&rtc->lock); |
238 | return ret; | ||
207 | } | 239 | } |
208 | 240 | ||
209 | static int sh_rtc_open(struct device *dev) | 241 | static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) |
210 | { | 242 | { |
211 | struct sh_rtc *rtc = dev_get_drvdata(dev); | 243 | struct sh_rtc *rtc = dev_get_drvdata(dev); |
212 | unsigned int tmp; | 244 | unsigned int tmp; |
213 | int ret; | ||
214 | |||
215 | tmp = readb(rtc->regbase + RCR1); | ||
216 | tmp &= ~RCR1_CF; | ||
217 | tmp |= RCR1_CIE; | ||
218 | writeb(tmp, rtc->regbase + RCR1); | ||
219 | 245 | ||
220 | ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED, | 246 | spin_lock_irq(&rtc->lock); |
221 | "sh-rtc period", dev); | ||
222 | if (unlikely(ret)) { | ||
223 | dev_err(dev, "request period IRQ failed with %d, IRQ %d\n", | ||
224 | ret, rtc->periodic_irq); | ||
225 | return ret; | ||
226 | } | ||
227 | |||
228 | ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED, | ||
229 | "sh-rtc carry", dev); | ||
230 | if (unlikely(ret)) { | ||
231 | dev_err(dev, "request carry IRQ failed with %d, IRQ %d\n", | ||
232 | ret, rtc->carry_irq); | ||
233 | free_irq(rtc->periodic_irq, dev); | ||
234 | goto err_bad_carry; | ||
235 | } | ||
236 | 247 | ||
237 | ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, | 248 | tmp = readb(rtc->regbase + RCR1); |
238 | "sh-rtc alarm", dev); | ||
239 | if (unlikely(ret)) { | ||
240 | dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n", | ||
241 | ret, rtc->alarm_irq); | ||
242 | goto err_bad_alarm; | ||
243 | } | ||
244 | 249 | ||
245 | return 0; | 250 | if (!enable) |
251 | tmp &= ~RCR1_AIE; | ||
252 | else | ||
253 | tmp |= RCR1_AIE; | ||
246 | 254 | ||
247 | err_bad_alarm: | 255 | writeb(tmp, rtc->regbase + RCR1); |
248 | free_irq(rtc->carry_irq, dev); | ||
249 | err_bad_carry: | ||
250 | free_irq(rtc->periodic_irq, dev); | ||
251 | 256 | ||
252 | return ret; | 257 | spin_unlock_irq(&rtc->lock); |
253 | } | 258 | } |
254 | 259 | ||
255 | static void sh_rtc_release(struct device *dev) | 260 | static void sh_rtc_release(struct device *dev) |
256 | { | 261 | { |
257 | struct sh_rtc *rtc = dev_get_drvdata(dev); | ||
258 | |||
259 | sh_rtc_setpie(dev, 0); | 262 | sh_rtc_setpie(dev, 0); |
260 | sh_rtc_setaie(dev, 0); | 263 | sh_rtc_setaie(dev, 0); |
261 | |||
262 | free_irq(rtc->periodic_irq, dev); | ||
263 | free_irq(rtc->carry_irq, dev); | ||
264 | free_irq(rtc->alarm_irq, dev); | ||
265 | } | 264 | } |
266 | 265 | ||
267 | static int sh_rtc_proc(struct device *dev, struct seq_file *seq) | 266 | static int sh_rtc_proc(struct device *dev, struct seq_file *seq) |
@@ -270,31 +269,44 @@ static int sh_rtc_proc(struct device *dev, struct seq_file *seq) | |||
270 | unsigned int tmp; | 269 | unsigned int tmp; |
271 | 270 | ||
272 | tmp = readb(rtc->regbase + RCR1); | 271 | tmp = readb(rtc->regbase + RCR1); |
273 | seq_printf(seq, "carry_IRQ\t: %s\n", | 272 | seq_printf(seq, "carry_IRQ\t: %s\n", (tmp & RCR1_CIE) ? "yes" : "no"); |
274 | (tmp & RCR1_CIE) ? "yes" : "no"); | ||
275 | 273 | ||
276 | tmp = readb(rtc->regbase + RCR2); | 274 | tmp = readb(rtc->regbase + RCR2); |
277 | seq_printf(seq, "periodic_IRQ\t: %s\n", | 275 | seq_printf(seq, "periodic_IRQ\t: %s\n", |
278 | (tmp & RCR2_PEF) ? "yes" : "no"); | 276 | (tmp & RCR2_PESMASK) ? "yes" : "no"); |
279 | 277 | ||
280 | return 0; | 278 | return 0; |
281 | } | 279 | } |
282 | 280 | ||
283 | static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | 281 | static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) |
284 | { | 282 | { |
285 | unsigned int ret = -ENOIOCTLCMD; | 283 | struct sh_rtc *rtc = dev_get_drvdata(dev); |
284 | unsigned int ret = 0; | ||
286 | 285 | ||
287 | switch (cmd) { | 286 | switch (cmd) { |
288 | case RTC_PIE_OFF: | 287 | case RTC_PIE_OFF: |
289 | case RTC_PIE_ON: | 288 | case RTC_PIE_ON: |
290 | sh_rtc_setpie(dev, cmd == RTC_PIE_ON); | 289 | sh_rtc_setpie(dev, cmd == RTC_PIE_ON); |
291 | ret = 0; | ||
292 | break; | 290 | break; |
293 | case RTC_AIE_OFF: | 291 | case RTC_AIE_OFF: |
294 | case RTC_AIE_ON: | 292 | case RTC_AIE_ON: |
295 | sh_rtc_setaie(dev, cmd == RTC_AIE_ON); | 293 | sh_rtc_setaie(dev, cmd == RTC_AIE_ON); |
296 | ret = 0; | ||
297 | break; | 294 | break; |
295 | case RTC_UIE_OFF: | ||
296 | rtc->periodic_freq &= ~PF_OXS; | ||
297 | break; | ||
298 | case RTC_UIE_ON: | ||
299 | rtc->periodic_freq |= PF_OXS; | ||
300 | break; | ||
301 | case RTC_IRQP_READ: | ||
302 | ret = put_user(rtc->rtc_dev->irq_freq, | ||
303 | (unsigned long __user *)arg); | ||
304 | break; | ||
305 | case RTC_IRQP_SET: | ||
306 | ret = sh_rtc_setfreq(dev, arg); | ||
307 | break; | ||
308 | default: | ||
309 | ret = -ENOIOCTLCMD; | ||
298 | } | 310 | } |
299 | 311 | ||
300 | return ret; | 312 | return ret; |
@@ -421,7 +433,7 @@ static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) | |||
421 | { | 433 | { |
422 | struct platform_device *pdev = to_platform_device(dev); | 434 | struct platform_device *pdev = to_platform_device(dev); |
423 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | 435 | struct sh_rtc *rtc = platform_get_drvdata(pdev); |
424 | struct rtc_time* tm = &wkalrm->time; | 436 | struct rtc_time *tm = &wkalrm->time; |
425 | 437 | ||
426 | spin_lock_irq(&rtc->lock); | 438 | spin_lock_irq(&rtc->lock); |
427 | 439 | ||
@@ -452,7 +464,7 @@ static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc, | |||
452 | writeb(BIN2BCD(value) | AR_ENB, rtc->regbase + reg_off); | 464 | writeb(BIN2BCD(value) | AR_ENB, rtc->regbase + reg_off); |
453 | } | 465 | } |
454 | 466 | ||
455 | static int sh_rtc_check_alarm(struct rtc_time* tm) | 467 | static int sh_rtc_check_alarm(struct rtc_time *tm) |
456 | { | 468 | { |
457 | /* | 469 | /* |
458 | * The original rtc says anything > 0xc0 is "don't care" or "match | 470 | * The original rtc says anything > 0xc0 is "don't care" or "match |
@@ -503,11 +515,9 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) | |||
503 | 515 | ||
504 | /* disable alarm interrupt and clear the alarm flag */ | 516 | /* disable alarm interrupt and clear the alarm flag */ |
505 | rcr1 = readb(rtc->regbase + RCR1); | 517 | rcr1 = readb(rtc->regbase + RCR1); |
506 | rcr1 &= ~(RCR1_AF|RCR1_AIE); | 518 | rcr1 &= ~(RCR1_AF | RCR1_AIE); |
507 | writeb(rcr1, rtc->regbase + RCR1); | 519 | writeb(rcr1, rtc->regbase + RCR1); |
508 | 520 | ||
509 | rtc->rearm_aie = 0; | ||
510 | |||
511 | /* set alarm time */ | 521 | /* set alarm time */ |
512 | sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR); | 522 | sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR); |
513 | sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR); | 523 | sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR); |
@@ -529,14 +539,34 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) | |||
529 | return 0; | 539 | return 0; |
530 | } | 540 | } |
531 | 541 | ||
542 | static int sh_rtc_irq_set_state(struct device *dev, int enabled) | ||
543 | { | ||
544 | struct platform_device *pdev = to_platform_device(dev); | ||
545 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | ||
546 | |||
547 | if (enabled) { | ||
548 | rtc->periodic_freq |= PF_KOU; | ||
549 | return sh_rtc_ioctl(dev, RTC_PIE_ON, 0); | ||
550 | } else { | ||
551 | rtc->periodic_freq &= ~PF_KOU; | ||
552 | return sh_rtc_ioctl(dev, RTC_PIE_OFF, 0); | ||
553 | } | ||
554 | } | ||
555 | |||
556 | static int sh_rtc_irq_set_freq(struct device *dev, int freq) | ||
557 | { | ||
558 | return sh_rtc_ioctl(dev, RTC_IRQP_SET, freq); | ||
559 | } | ||
560 | |||
532 | static struct rtc_class_ops sh_rtc_ops = { | 561 | static struct rtc_class_ops sh_rtc_ops = { |
533 | .open = sh_rtc_open, | ||
534 | .release = sh_rtc_release, | 562 | .release = sh_rtc_release, |
535 | .ioctl = sh_rtc_ioctl, | 563 | .ioctl = sh_rtc_ioctl, |
536 | .read_time = sh_rtc_read_time, | 564 | .read_time = sh_rtc_read_time, |
537 | .set_time = sh_rtc_set_time, | 565 | .set_time = sh_rtc_set_time, |
538 | .read_alarm = sh_rtc_read_alarm, | 566 | .read_alarm = sh_rtc_read_alarm, |
539 | .set_alarm = sh_rtc_set_alarm, | 567 | .set_alarm = sh_rtc_set_alarm, |
568 | .irq_set_state = sh_rtc_irq_set_state, | ||
569 | .irq_set_freq = sh_rtc_irq_set_freq, | ||
540 | .proc = sh_rtc_proc, | 570 | .proc = sh_rtc_proc, |
541 | }; | 571 | }; |
542 | 572 | ||
@@ -544,6 +574,7 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) | |||
544 | { | 574 | { |
545 | struct sh_rtc *rtc; | 575 | struct sh_rtc *rtc; |
546 | struct resource *res; | 576 | struct resource *res; |
577 | unsigned int tmp; | ||
547 | int ret = -ENOENT; | 578 | int ret = -ENOENT; |
548 | 579 | ||
549 | rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); | 580 | rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); |
@@ -552,6 +583,7 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) | |||
552 | 583 | ||
553 | spin_lock_init(&rtc->lock); | 584 | spin_lock_init(&rtc->lock); |
554 | 585 | ||
586 | /* get periodic/carry/alarm irqs */ | ||
555 | rtc->periodic_irq = platform_get_irq(pdev, 0); | 587 | rtc->periodic_irq = platform_get_irq(pdev, 0); |
556 | if (unlikely(rtc->periodic_irq < 0)) { | 588 | if (unlikely(rtc->periodic_irq < 0)) { |
557 | dev_err(&pdev->dev, "No IRQ for period\n"); | 589 | dev_err(&pdev->dev, "No IRQ for period\n"); |
@@ -608,8 +640,48 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) | |||
608 | rtc->capabilities |= pinfo->capabilities; | 640 | rtc->capabilities |= pinfo->capabilities; |
609 | } | 641 | } |
610 | 642 | ||
643 | rtc->rtc_dev->max_user_freq = 256; | ||
644 | rtc->rtc_dev->irq_freq = 1; | ||
645 | rtc->periodic_freq = 0x60; | ||
646 | |||
611 | platform_set_drvdata(pdev, rtc); | 647 | platform_set_drvdata(pdev, rtc); |
612 | 648 | ||
649 | /* register periodic/carry/alarm irqs */ | ||
650 | ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED, | ||
651 | "sh-rtc period", rtc); | ||
652 | if (unlikely(ret)) { | ||
653 | dev_err(&pdev->dev, | ||
654 | "request period IRQ failed with %d, IRQ %d\n", ret, | ||
655 | rtc->periodic_irq); | ||
656 | goto err_badmap; | ||
657 | } | ||
658 | |||
659 | ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED, | ||
660 | "sh-rtc carry", rtc); | ||
661 | if (unlikely(ret)) { | ||
662 | dev_err(&pdev->dev, | ||
663 | "request carry IRQ failed with %d, IRQ %d\n", ret, | ||
664 | rtc->carry_irq); | ||
665 | free_irq(rtc->periodic_irq, rtc); | ||
666 | goto err_badmap; | ||
667 | } | ||
668 | |||
669 | ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, | ||
670 | "sh-rtc alarm", rtc); | ||
671 | if (unlikely(ret)) { | ||
672 | dev_err(&pdev->dev, | ||
673 | "request alarm IRQ failed with %d, IRQ %d\n", ret, | ||
674 | rtc->alarm_irq); | ||
675 | free_irq(rtc->carry_irq, rtc); | ||
676 | free_irq(rtc->periodic_irq, rtc); | ||
677 | goto err_badmap; | ||
678 | } | ||
679 | |||
680 | tmp = readb(rtc->regbase + RCR1); | ||
681 | tmp &= ~RCR1_CF; | ||
682 | tmp |= RCR1_CIE; | ||
683 | writeb(tmp, rtc->regbase + RCR1); | ||
684 | |||
613 | return 0; | 685 | return 0; |
614 | 686 | ||
615 | err_badmap: | 687 | err_badmap: |
@@ -630,6 +702,10 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev) | |||
630 | sh_rtc_setpie(&pdev->dev, 0); | 702 | sh_rtc_setpie(&pdev->dev, 0); |
631 | sh_rtc_setaie(&pdev->dev, 0); | 703 | sh_rtc_setaie(&pdev->dev, 0); |
632 | 704 | ||
705 | free_irq(rtc->carry_irq, rtc); | ||
706 | free_irq(rtc->periodic_irq, rtc); | ||
707 | free_irq(rtc->alarm_irq, rtc); | ||
708 | |||
633 | release_resource(rtc->res); | 709 | release_resource(rtc->res); |
634 | 710 | ||
635 | platform_set_drvdata(pdev, NULL); | 711 | platform_set_drvdata(pdev, NULL); |
@@ -662,6 +738,8 @@ module_exit(sh_rtc_exit); | |||
662 | 738 | ||
663 | MODULE_DESCRIPTION("SuperH on-chip RTC driver"); | 739 | MODULE_DESCRIPTION("SuperH on-chip RTC driver"); |
664 | MODULE_VERSION(DRV_VERSION); | 740 | MODULE_VERSION(DRV_VERSION); |
665 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, Jamie Lenehan <lenehan@twibble.org>"); | 741 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, " |
742 | "Jamie Lenehan <lenehan@twibble.org>, " | ||
743 | "Angelo Castello <angelo.castello@st.com>"); | ||
666 | MODULE_LICENSE("GPL"); | 744 | MODULE_LICENSE("GPL"); |
667 | MODULE_ALIAS("platform:" DRV_NAME); | 745 | MODULE_ALIAS("platform:" DRV_NAME); |