diff options
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/rtc-mc13783.c | 185 |
1 files changed, 172 insertions, 13 deletions
diff --git a/drivers/rtc/rtc-mc13783.c b/drivers/rtc/rtc-mc13783.c index 6a36201178a..d60c81b7b69 100644 --- a/drivers/rtc/rtc-mc13783.c +++ b/drivers/rtc/rtc-mc13783.c | |||
| @@ -28,6 +28,34 @@ struct mc13783_rtc { | |||
| 28 | int valid; | 28 | int valid; |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | static int mc13783_rtc_irq_enable_unlocked(struct device *dev, | ||
| 32 | unsigned int enabled, int irq) | ||
| 33 | { | ||
| 34 | struct mc13783_rtc *priv = dev_get_drvdata(dev); | ||
| 35 | int (*func)(struct mc13783 *mc13783, int irq); | ||
| 36 | |||
| 37 | if (!priv->valid) | ||
| 38 | return -ENODATA; | ||
| 39 | |||
| 40 | func = enabled ? mc13783_irq_unmask : mc13783_irq_mask; | ||
| 41 | return func(priv->mc13783, irq); | ||
| 42 | } | ||
| 43 | |||
| 44 | static int mc13783_rtc_irq_enable(struct device *dev, | ||
| 45 | unsigned int enabled, int irq) | ||
| 46 | { | ||
| 47 | struct mc13783_rtc *priv = dev_get_drvdata(dev); | ||
| 48 | int ret; | ||
| 49 | |||
| 50 | mc13783_lock(priv->mc13783); | ||
| 51 | |||
| 52 | ret = mc13783_rtc_irq_enable_unlocked(dev, enabled, irq); | ||
| 53 | |||
| 54 | mc13783_unlock(priv->mc13783); | ||
| 55 | |||
| 56 | return ret; | ||
| 57 | } | ||
| 58 | |||
| 31 | static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm) | 59 | static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm) |
| 32 | { | 60 | { |
| 33 | struct mc13783_rtc *priv = dev_get_drvdata(dev); | 61 | struct mc13783_rtc *priv = dev_get_drvdata(dev); |
| @@ -78,6 +106,7 @@ static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs) | |||
| 78 | { | 106 | { |
| 79 | struct mc13783_rtc *priv = dev_get_drvdata(dev); | 107 | struct mc13783_rtc *priv = dev_get_drvdata(dev); |
| 80 | unsigned int seconds, days; | 108 | unsigned int seconds, days; |
| 109 | unsigned int alarmseconds; | ||
| 81 | int ret; | 110 | int ret; |
| 82 | 111 | ||
| 83 | seconds = secs % 86400; | 112 | seconds = secs % 86400; |
| @@ -86,7 +115,22 @@ static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs) | |||
| 86 | mc13783_lock(priv->mc13783); | 115 | mc13783_lock(priv->mc13783); |
| 87 | 116 | ||
| 88 | /* | 117 | /* |
| 89 | * first write seconds=0 to prevent a day switch between writing days | 118 | * temporarily invalidate alarm to prevent triggering it when the day is |
| 119 | * already updated while the time isn't yet. | ||
| 120 | */ | ||
| 121 | ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTODA, &alarmseconds); | ||
| 122 | if (unlikely(ret)) | ||
| 123 | goto out; | ||
| 124 | |||
| 125 | if (alarmseconds < 86400) { | ||
| 126 | ret = mc13783_reg_write(priv->mc13783, | ||
| 127 | MC13783_RTCTODA, 0x1ffff); | ||
| 128 | if (unlikely(ret)) | ||
| 129 | goto out; | ||
| 130 | } | ||
| 131 | |||
| 132 | /* | ||
| 133 | * write seconds=0 to prevent a day switch between writing days | ||
| 90 | * and seconds below | 134 | * and seconds below |
| 91 | */ | 135 | */ |
| 92 | ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0); | 136 | ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0); |
| @@ -101,6 +145,14 @@ static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs) | |||
| 101 | if (unlikely(ret)) | 145 | if (unlikely(ret)) |
| 102 | goto out; | 146 | goto out; |
| 103 | 147 | ||
| 148 | /* restore alarm */ | ||
| 149 | if (alarmseconds < 86400) { | ||
| 150 | ret = mc13783_reg_write(priv->mc13783, | ||
| 151 | MC13783_RTCTODA, alarmseconds); | ||
| 152 | if (unlikely(ret)) | ||
| 153 | goto out; | ||
| 154 | } | ||
| 155 | |||
| 104 | ret = mc13783_irq_ack(priv->mc13783, MC13783_IRQ_RTCRST); | 156 | ret = mc13783_irq_ack(priv->mc13783, MC13783_IRQ_RTCRST); |
| 105 | if (unlikely(ret)) | 157 | if (unlikely(ret)) |
| 106 | goto out; | 158 | goto out; |
| @@ -114,6 +166,107 @@ out: | |||
| 114 | return ret; | 166 | return ret; |
| 115 | } | 167 | } |
| 116 | 168 | ||
| 169 | static int mc13783_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
| 170 | { | ||
| 171 | struct mc13783_rtc *priv = dev_get_drvdata(dev); | ||
| 172 | unsigned seconds, days; | ||
| 173 | unsigned long s1970; | ||
| 174 | int enabled, pending; | ||
| 175 | int ret; | ||
| 176 | |||
| 177 | mc13783_lock(priv->mc13783); | ||
| 178 | |||
| 179 | ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTODA, &seconds); | ||
| 180 | if (unlikely(ret)) | ||
| 181 | goto out; | ||
| 182 | if (seconds >= 86400) { | ||
| 183 | ret = -ENODATA; | ||
| 184 | goto out; | ||
| 185 | } | ||
| 186 | |||
| 187 | ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days); | ||
| 188 | if (unlikely(ret)) | ||
| 189 | goto out; | ||
| 190 | |||
| 191 | ret = mc13783_irq_status(priv->mc13783, MC13783_IRQ_TODA, | ||
| 192 | &enabled, &pending); | ||
| 193 | |||
| 194 | out: | ||
| 195 | mc13783_unlock(priv->mc13783); | ||
| 196 | |||
| 197 | if (ret) | ||
| 198 | return ret; | ||
| 199 | |||
| 200 | alarm->enabled = enabled; | ||
| 201 | alarm->pending = pending; | ||
| 202 | |||
| 203 | s1970 = days * 86400 + seconds; | ||
| 204 | |||
| 205 | rtc_time_to_tm(s1970, &alarm->time); | ||
| 206 | dev_dbg(dev, "%s: %lu\n", __func__, s1970); | ||
| 207 | |||
| 208 | return 0; | ||
| 209 | } | ||
| 210 | |||
| 211 | static int mc13783_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
| 212 | { | ||
| 213 | struct mc13783_rtc *priv = dev_get_drvdata(dev); | ||
| 214 | unsigned long s1970; | ||
| 215 | unsigned seconds, days; | ||
| 216 | int ret; | ||
| 217 | |||
| 218 | mc13783_lock(priv->mc13783); | ||
| 219 | |||
| 220 | /* disable alarm to prevent false triggering */ | ||
| 221 | ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTODA, 0x1ffff); | ||
| 222 | if (unlikely(ret)) | ||
| 223 | goto out; | ||
| 224 | |||
| 225 | ret = mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TODA); | ||
| 226 | if (unlikely(ret)) | ||
| 227 | goto out; | ||
| 228 | |||
| 229 | ret = rtc_tm_to_time(&alarm->time, &s1970); | ||
| 230 | if (unlikely(ret)) | ||
| 231 | goto out; | ||
| 232 | |||
| 233 | dev_dbg(dev, "%s: o%2.s %lu\n", __func__, alarm->enabled ? "n" : "ff", | ||
| 234 | s1970); | ||
| 235 | |||
| 236 | ret = mc13783_rtc_irq_enable_unlocked(dev, alarm->enabled, | ||
| 237 | MC13783_IRQ_TODA); | ||
| 238 | if (unlikely(ret)) | ||
| 239 | goto out; | ||
| 240 | |||
| 241 | seconds = s1970 % 86400; | ||
| 242 | days = s1970 / 86400; | ||
| 243 | |||
| 244 | ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAYA, days); | ||
| 245 | if (unlikely(ret)) | ||
| 246 | goto out; | ||
| 247 | |||
| 248 | ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTODA, seconds); | ||
| 249 | |||
| 250 | out: | ||
| 251 | mc13783_unlock(priv->mc13783); | ||
| 252 | |||
| 253 | return ret; | ||
| 254 | } | ||
| 255 | |||
| 256 | static irqreturn_t mc13783_rtc_alarm_handler(int irq, void *dev) | ||
| 257 | { | ||
| 258 | struct mc13783_rtc *priv = dev; | ||
| 259 | struct mc13783 *mc13783 = priv->mc13783; | ||
| 260 | |||
| 261 | dev_dbg(&priv->rtc->dev, "Alarm\n"); | ||
| 262 | |||
| 263 | rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF); | ||
| 264 | |||
| 265 | mc13783_irq_ack(mc13783, irq); | ||
| 266 | |||
| 267 | return IRQ_HANDLED; | ||
| 268 | } | ||
| 269 | |||
| 117 | static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev) | 270 | static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev) |
| 118 | { | 271 | { |
| 119 | struct mc13783_rtc *priv = dev; | 272 | struct mc13783_rtc *priv = dev; |
| @@ -131,24 +284,21 @@ static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev) | |||
| 131 | static int mc13783_rtc_update_irq_enable(struct device *dev, | 284 | static int mc13783_rtc_update_irq_enable(struct device *dev, |
| 132 | unsigned int enabled) | 285 | unsigned int enabled) |
| 133 | { | 286 | { |
| 134 | struct mc13783_rtc *priv = dev_get_drvdata(dev); | 287 | return mc13783_rtc_irq_enable(dev, enabled, MC13783_IRQ_1HZ); |
| 135 | int ret = -ENODATA; | 288 | } |
| 136 | |||
| 137 | mc13783_lock(priv->mc13783); | ||
| 138 | if (!priv->valid) | ||
| 139 | goto out; | ||
| 140 | |||
| 141 | ret = (enabled ? mc13783_irq_unmask : mc13783_irq_mask)(priv->mc13783, | ||
| 142 | MC13783_IRQ_1HZ); | ||
| 143 | out: | ||
| 144 | mc13783_unlock(priv->mc13783); | ||
| 145 | 289 | ||
| 146 | return ret; | 290 | static int mc13783_rtc_alarm_irq_enable(struct device *dev, |
| 291 | unsigned int enabled) | ||
| 292 | { | ||
| 293 | return mc13783_rtc_irq_enable(dev, enabled, MC13783_IRQ_TODA); | ||
| 147 | } | 294 | } |
| 148 | 295 | ||
| 149 | static const struct rtc_class_ops mc13783_rtc_ops = { | 296 | static const struct rtc_class_ops mc13783_rtc_ops = { |
| 150 | .read_time = mc13783_rtc_read_time, | 297 | .read_time = mc13783_rtc_read_time, |
| 151 | .set_mmss = mc13783_rtc_set_mmss, | 298 | .set_mmss = mc13783_rtc_set_mmss, |
| 299 | .read_alarm = mc13783_rtc_read_alarm, | ||
| 300 | .set_alarm = mc13783_rtc_set_alarm, | ||
| 301 | .alarm_irq_enable = mc13783_rtc_alarm_irq_enable, | ||
| 152 | .update_irq_enable = mc13783_rtc_update_irq_enable, | 302 | .update_irq_enable = mc13783_rtc_update_irq_enable, |
| 153 | }; | 303 | }; |
| 154 | 304 | ||
| @@ -197,11 +347,19 @@ static int __devinit mc13783_rtc_probe(struct platform_device *pdev) | |||
| 197 | if (ret) | 347 | if (ret) |
| 198 | goto err_update_irq_request; | 348 | goto err_update_irq_request; |
| 199 | 349 | ||
| 350 | ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_TODA, | ||
| 351 | mc13783_rtc_alarm_handler, DRIVER_NAME, priv); | ||
| 352 | if (ret) | ||
| 353 | goto err_alarm_irq_request; | ||
| 354 | |||
| 200 | priv->rtc = rtc_device_register(pdev->name, | 355 | priv->rtc = rtc_device_register(pdev->name, |
| 201 | &pdev->dev, &mc13783_rtc_ops, THIS_MODULE); | 356 | &pdev->dev, &mc13783_rtc_ops, THIS_MODULE); |
| 202 | if (IS_ERR(priv->rtc)) { | 357 | if (IS_ERR(priv->rtc)) { |
| 203 | ret = PTR_ERR(priv->rtc); | 358 | ret = PTR_ERR(priv->rtc); |
| 204 | 359 | ||
| 360 | mc13783_irq_free(priv->mc13783, MC13783_IRQ_TODA, priv); | ||
| 361 | err_alarm_irq_request: | ||
| 362 | |||
| 205 | mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv); | 363 | mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv); |
| 206 | err_update_irq_request: | 364 | err_update_irq_request: |
| 207 | 365 | ||
| @@ -227,6 +385,7 @@ static int __devexit mc13783_rtc_remove(struct platform_device *pdev) | |||
| 227 | 385 | ||
| 228 | rtc_device_unregister(priv->rtc); | 386 | rtc_device_unregister(priv->rtc); |
| 229 | 387 | ||
| 388 | mc13783_irq_free(priv->mc13783, MC13783_IRQ_TODA, priv); | ||
| 230 | mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv); | 389 | mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv); |
| 231 | mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv); | 390 | mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv); |
| 232 | 391 | ||
