diff options
Diffstat (limited to 'drivers/rtc/rtc-sh.c')
| -rw-r--r-- | drivers/rtc/rtc-sh.c | 298 |
1 files changed, 188 insertions, 110 deletions
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 9e9caa5d7f5f..110699bb4787 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; |
| @@ -349,7 +361,7 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) | |||
| 349 | 361 | ||
| 350 | dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " | 362 | dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " |
| 351 | "mday=%d, mon=%d, year=%d, wday=%d\n", | 363 | "mday=%d, mon=%d, year=%d, wday=%d\n", |
| 352 | __FUNCTION__, | 364 | __func__, |
| 353 | tm->tm_sec, tm->tm_min, tm->tm_hour, | 365 | tm->tm_sec, tm->tm_min, tm->tm_hour, |
| 354 | tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); | 366 | tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); |
| 355 | 367 | ||
| @@ -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); |
