root/drivers/rtc/rtc-mc13xxx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mc13xxx_rtc_irq_enable_unlocked
  2. mc13xxx_rtc_alarm_irq_enable
  3. mc13xxx_rtc_read_time
  4. mc13xxx_rtc_set_time
  5. mc13xxx_rtc_read_alarm
  6. mc13xxx_rtc_set_alarm
  7. mc13xxx_rtc_alarm_handler
  8. mc13xxx_rtc_reset_handler
  9. mc13xxx_rtc_probe
  10. mc13xxx_rtc_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Real Time Clock driver for Freescale MC13XXX PMIC
   4  *
   5  * (C) 2009 Sascha Hauer, Pengutronix
   6  * (C) 2009 Uwe Kleine-Koenig, Pengutronix
   7  */
   8 
   9 #include <linux/mfd/mc13xxx.h>
  10 #include <linux/platform_device.h>
  11 #include <linux/kernel.h>
  12 #include <linux/module.h>
  13 #include <linux/mod_devicetable.h>
  14 #include <linux/slab.h>
  15 #include <linux/rtc.h>
  16 
  17 #define DRIVER_NAME "mc13xxx-rtc"
  18 
  19 #define MC13XXX_RTCTOD  20
  20 #define MC13XXX_RTCTODA 21
  21 #define MC13XXX_RTCDAY  22
  22 #define MC13XXX_RTCDAYA 23
  23 
  24 #define SEC_PER_DAY     (24 * 60 * 60)
  25 
  26 struct mc13xxx_rtc {
  27         struct rtc_device *rtc;
  28         struct mc13xxx *mc13xxx;
  29         int valid;
  30 };
  31 
  32 static int mc13xxx_rtc_irq_enable_unlocked(struct device *dev,
  33                 unsigned int enabled, int irq)
  34 {
  35         struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
  36         int (*func)(struct mc13xxx *mc13xxx, int irq);
  37 
  38         if (!priv->valid)
  39                 return -ENODATA;
  40 
  41         func = enabled ? mc13xxx_irq_unmask : mc13xxx_irq_mask;
  42         return func(priv->mc13xxx, irq);
  43 }
  44 
  45 static int mc13xxx_rtc_alarm_irq_enable(struct device *dev,
  46                                         unsigned int enabled)
  47 {
  48         struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
  49         int ret;
  50 
  51         mc13xxx_lock(priv->mc13xxx);
  52 
  53         ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, MC13XXX_IRQ_TODA);
  54 
  55         mc13xxx_unlock(priv->mc13xxx);
  56 
  57         return ret;
  58 }
  59 
  60 static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
  61 {
  62         struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
  63         unsigned int seconds, days1, days2;
  64 
  65         if (!priv->valid)
  66                 return -ENODATA;
  67 
  68         do {
  69                 int ret;
  70 
  71                 ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1);
  72                 if (ret)
  73                         return ret;
  74 
  75                 ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds);
  76                 if (ret)
  77                         return ret;
  78 
  79                 ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2);
  80                 if (ret)
  81                         return ret;
  82         } while (days1 != days2);
  83 
  84         rtc_time64_to_tm((time64_t)days1 * SEC_PER_DAY + seconds, tm);
  85 
  86         return 0;
  87 }
  88 
  89 static int mc13xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
  90 {
  91         struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
  92         unsigned int seconds, days;
  93         unsigned int alarmseconds;
  94         int ret;
  95 
  96         days = div_s64_rem(rtc_tm_to_time64(tm), SEC_PER_DAY, &seconds);
  97 
  98         mc13xxx_lock(priv->mc13xxx);
  99 
 100         /*
 101          * temporarily invalidate alarm to prevent triggering it when the day is
 102          * already updated while the time isn't yet.
 103          */
 104         ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &alarmseconds);
 105         if (unlikely(ret))
 106                 goto out;
 107 
 108         if (alarmseconds < SEC_PER_DAY) {
 109                 ret = mc13xxx_reg_write(priv->mc13xxx,
 110                                 MC13XXX_RTCTODA, 0x1ffff);
 111                 if (unlikely(ret))
 112                         goto out;
 113         }
 114 
 115         /*
 116          * write seconds=0 to prevent a day switch between writing days
 117          * and seconds below
 118          */
 119         ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, 0);
 120         if (unlikely(ret))
 121                 goto out;
 122 
 123         ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAY, days);
 124         if (unlikely(ret))
 125                 goto out;
 126 
 127         ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, seconds);
 128         if (unlikely(ret))
 129                 goto out;
 130 
 131         /* restore alarm */
 132         if (alarmseconds < SEC_PER_DAY) {
 133                 ret = mc13xxx_reg_write(priv->mc13xxx,
 134                                 MC13XXX_RTCTODA, alarmseconds);
 135                 if (unlikely(ret))
 136                         goto out;
 137         }
 138 
 139         if (!priv->valid) {
 140                 ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
 141                 if (unlikely(ret))
 142                         goto out;
 143 
 144                 ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
 145         }
 146 
 147 out:
 148         priv->valid = !ret;
 149 
 150         mc13xxx_unlock(priv->mc13xxx);
 151 
 152         return ret;
 153 }
 154 
 155 static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 156 {
 157         struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
 158         unsigned int seconds, days;
 159         time64_t s1970;
 160         int enabled, pending;
 161         int ret;
 162 
 163         mc13xxx_lock(priv->mc13xxx);
 164 
 165         ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &seconds);
 166         if (unlikely(ret))
 167                 goto out;
 168         if (seconds >= SEC_PER_DAY) {
 169                 ret = -ENODATA;
 170                 goto out;
 171         }
 172 
 173         ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days);
 174         if (unlikely(ret))
 175                 goto out;
 176 
 177         ret = mc13xxx_irq_status(priv->mc13xxx, MC13XXX_IRQ_TODA,
 178                         &enabled, &pending);
 179 
 180 out:
 181         mc13xxx_unlock(priv->mc13xxx);
 182 
 183         if (ret)
 184                 return ret;
 185 
 186         alarm->enabled = enabled;
 187         alarm->pending = pending;
 188 
 189         s1970 = (time64_t)days * SEC_PER_DAY + seconds;
 190 
 191         rtc_time64_to_tm(s1970, &alarm->time);
 192         dev_dbg(dev, "%s: %lld\n", __func__, (long long)s1970);
 193 
 194         return 0;
 195 }
 196 
 197 static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 198 {
 199         struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
 200         time64_t s1970;
 201         u32 seconds, days;
 202         int ret;
 203 
 204         mc13xxx_lock(priv->mc13xxx);
 205 
 206         /* disable alarm to prevent false triggering */
 207         ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, 0x1ffff);
 208         if (unlikely(ret))
 209                 goto out;
 210 
 211         ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TODA);
 212         if (unlikely(ret))
 213                 goto out;
 214 
 215         s1970 = rtc_tm_to_time64(&alarm->time);
 216 
 217         dev_dbg(dev, "%s: %s %lld\n", __func__, alarm->enabled ? "on" : "off",
 218                         (long long)s1970);
 219 
 220         ret = mc13xxx_rtc_irq_enable_unlocked(dev, alarm->enabled,
 221                         MC13XXX_IRQ_TODA);
 222         if (unlikely(ret))
 223                 goto out;
 224 
 225         days = div_s64_rem(s1970, SEC_PER_DAY, &seconds);
 226 
 227         ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days);
 228         if (unlikely(ret))
 229                 goto out;
 230 
 231         ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, seconds);
 232 
 233 out:
 234         mc13xxx_unlock(priv->mc13xxx);
 235 
 236         return ret;
 237 }
 238 
 239 static irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev)
 240 {
 241         struct mc13xxx_rtc *priv = dev;
 242         struct mc13xxx *mc13xxx = priv->mc13xxx;
 243 
 244         rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
 245 
 246         mc13xxx_irq_ack(mc13xxx, irq);
 247 
 248         return IRQ_HANDLED;
 249 }
 250 
 251 static const struct rtc_class_ops mc13xxx_rtc_ops = {
 252         .read_time = mc13xxx_rtc_read_time,
 253         .set_time = mc13xxx_rtc_set_time,
 254         .read_alarm = mc13xxx_rtc_read_alarm,
 255         .set_alarm = mc13xxx_rtc_set_alarm,
 256         .alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable,
 257 };
 258 
 259 static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev)
 260 {
 261         struct mc13xxx_rtc *priv = dev;
 262         struct mc13xxx *mc13xxx = priv->mc13xxx;
 263 
 264         priv->valid = 0;
 265 
 266         mc13xxx_irq_mask(mc13xxx, irq);
 267 
 268         return IRQ_HANDLED;
 269 }
 270 
 271 static int __init mc13xxx_rtc_probe(struct platform_device *pdev)
 272 {
 273         int ret;
 274         struct mc13xxx_rtc *priv;
 275         struct mc13xxx *mc13xxx;
 276 
 277         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 278         if (!priv)
 279                 return -ENOMEM;
 280 
 281         mc13xxx = dev_get_drvdata(pdev->dev.parent);
 282         priv->mc13xxx = mc13xxx;
 283         priv->valid = 1;
 284 
 285         priv->rtc = devm_rtc_allocate_device(&pdev->dev);
 286         if (IS_ERR(priv->rtc))
 287                 return PTR_ERR(priv->rtc);
 288         platform_set_drvdata(pdev, priv);
 289 
 290         priv->rtc->ops = &mc13xxx_rtc_ops;
 291         /* 15bit days + hours, minutes, seconds */
 292         priv->rtc->range_max = (timeu64_t)(1 << 15) * SEC_PER_DAY - 1;
 293 
 294         mc13xxx_lock(mc13xxx);
 295 
 296         mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_RTCRST);
 297 
 298         ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST,
 299                         mc13xxx_rtc_reset_handler, DRIVER_NAME, priv);
 300         if (ret)
 301                 goto err_irq_request;
 302 
 303         ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_TODA,
 304                         mc13xxx_rtc_alarm_handler, DRIVER_NAME, priv);
 305         if (ret)
 306                 goto err_irq_request;
 307 
 308         mc13xxx_unlock(mc13xxx);
 309 
 310         ret = rtc_register_device(priv->rtc);
 311         if (ret)
 312                 goto err_irq_request;
 313 
 314         return 0;
 315 
 316 err_irq_request:
 317         mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv);
 318         mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv);
 319 
 320         mc13xxx_unlock(mc13xxx);
 321 
 322         return ret;
 323 }
 324 
 325 static int mc13xxx_rtc_remove(struct platform_device *pdev)
 326 {
 327         struct mc13xxx_rtc *priv = platform_get_drvdata(pdev);
 328 
 329         mc13xxx_lock(priv->mc13xxx);
 330 
 331         mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TODA, priv);
 332         mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_RTCRST, priv);
 333 
 334         mc13xxx_unlock(priv->mc13xxx);
 335 
 336         return 0;
 337 }
 338 
 339 static const struct platform_device_id mc13xxx_rtc_idtable[] = {
 340         {
 341                 .name = "mc13783-rtc",
 342         }, {
 343                 .name = "mc13892-rtc",
 344         }, {
 345                 .name = "mc34708-rtc",
 346         },
 347         { /* sentinel */ }
 348 };
 349 MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable);
 350 
 351 static struct platform_driver mc13xxx_rtc_driver = {
 352         .id_table = mc13xxx_rtc_idtable,
 353         .remove = mc13xxx_rtc_remove,
 354         .driver = {
 355                 .name = DRIVER_NAME,
 356         },
 357 };
 358 
 359 module_platform_driver_probe(mc13xxx_rtc_driver, &mc13xxx_rtc_probe);
 360 
 361 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 362 MODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC");
 363 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */