linux/drivers/rtc/rtc-max8925.c
<<
>>
Prefs
   1/*
   2 * RTC driver for Maxim MAX8925
   3 *
   4 * Copyright (C) 2009-2010 Marvell International Ltd.
   5 *      Haojian Zhuang <haojian.zhuang@marvell.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/i2c.h>
  14#include <linux/slab.h>
  15#include <linux/rtc.h>
  16#include <linux/platform_device.h>
  17#include <linux/mfd/max8925.h>
  18
  19enum {
  20        RTC_SEC = 0,
  21        RTC_MIN,
  22        RTC_HOUR,
  23        RTC_WEEKDAY,
  24        RTC_DATE,
  25        RTC_MONTH,
  26        RTC_YEAR1,
  27        RTC_YEAR2,
  28};
  29
  30#define MAX8925_RTC_SEC                 0x00
  31#define MAX8925_RTC_MIN                 0x01
  32#define MAX8925_RTC_HOUR                0x02
  33#define MAX8925_RTC_WEEKDAY             0x03
  34#define MAX8925_RTC_DATE                0x04
  35#define MAX8925_RTC_MONTH               0x05
  36#define MAX8925_RTC_YEAR1               0x06
  37#define MAX8925_RTC_YEAR2               0x07
  38#define MAX8925_ALARM0_SEC              0x08
  39#define MAX8925_ALARM0_MIN              0x09
  40#define MAX8925_ALARM0_HOUR             0x0a
  41#define MAX8925_ALARM0_WEEKDAY          0x0b
  42#define MAX8925_ALARM0_DATE             0x0c
  43#define MAX8925_ALARM0_MON              0x0d
  44#define MAX8925_ALARM0_YEAR1            0x0e
  45#define MAX8925_ALARM0_YEAR2            0x0f
  46#define MAX8925_ALARM1_SEC              0x10
  47#define MAX8925_ALARM1_MIN              0x11
  48#define MAX8925_ALARM1_HOUR             0x12
  49#define MAX8925_ALARM1_WEEKDAY          0x13
  50#define MAX8925_ALARM1_DATE             0x14
  51#define MAX8925_ALARM1_MON              0x15
  52#define MAX8925_ALARM1_YEAR1            0x16
  53#define MAX8925_ALARM1_YEAR2            0x17
  54#define MAX8925_RTC_CNTL                0x1b
  55#define MAX8925_RTC_STATUS              0x20
  56
  57#define TIME_NUM                        8
  58#define ALARM_1SEC                      (1 << 7)
  59#define HOUR_12                         (1 << 7)
  60#define HOUR_AM_PM                      (1 << 5)
  61#define ALARM0_IRQ                      (1 << 3)
  62#define ALARM1_IRQ                      (1 << 2)
  63#define ALARM0_STATUS                   (1 << 2)
  64#define ALARM1_STATUS                   (1 << 1)
  65
  66
  67struct max8925_rtc_info {
  68        struct rtc_device       *rtc_dev;
  69        struct max8925_chip     *chip;
  70        struct i2c_client       *rtc;
  71        struct device           *dev;
  72};
  73
  74static irqreturn_t rtc_update_handler(int irq, void *data)
  75{
  76        struct max8925_rtc_info *info = (struct max8925_rtc_info *)data;
  77
  78        /* disable ALARM0 except for 1SEC alarm */
  79        max8925_set_bits(info->rtc, MAX8925_ALARM0_CNTL, 0x7f, 0);
  80        rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
  81        return IRQ_HANDLED;
  82}
  83
  84static int tm_calc(struct rtc_time *tm, unsigned char *buf, int len)
  85{
  86        if (len < TIME_NUM)
  87                return -EINVAL;
  88        tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000
  89                        + (buf[RTC_YEAR2] & 0xf) * 100
  90                        + (buf[RTC_YEAR1] >> 4) * 10
  91                        + (buf[RTC_YEAR1] & 0xf);
  92        tm->tm_year -= 1900;
  93        tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10
  94                        + (buf[RTC_MONTH] & 0x0f);
  95        tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10
  96                        + (buf[RTC_DATE] & 0x0f);
  97        tm->tm_wday = buf[RTC_WEEKDAY] & 0x07;
  98        if (buf[RTC_HOUR] & HOUR_12) {
  99                tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10
 100                                + (buf[RTC_HOUR] & 0x0f);
 101                if (buf[RTC_HOUR] & HOUR_AM_PM)
 102                        tm->tm_hour += 12;
 103        } else
 104                tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10
 105                                + (buf[RTC_HOUR] & 0x0f);
 106        tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10
 107                        + (buf[RTC_MIN] & 0x0f);
 108        tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10
 109                        + (buf[RTC_SEC] & 0x0f);
 110        return 0;
 111}
 112
 113static int data_calc(unsigned char *buf, struct rtc_time *tm, int len)
 114{
 115        unsigned char high, low;
 116
 117        if (len < TIME_NUM)
 118                return -EINVAL;
 119
 120        high = (tm->tm_year + 1900) / 1000;
 121        low = (tm->tm_year + 1900) / 100;
 122        low = low - high * 10;
 123        buf[RTC_YEAR2] = (high << 4) + low;
 124        high = (tm->tm_year + 1900) / 10;
 125        low = tm->tm_year + 1900;
 126        low = low - high * 10;
 127        high = high - (high / 10) * 10;
 128        buf[RTC_YEAR1] = (high << 4) + low;
 129        high = tm->tm_mon / 10;
 130        low = tm->tm_mon;
 131        low = low - high * 10;
 132        buf[RTC_MONTH] = (high << 4) + low;
 133        high = tm->tm_mday / 10;
 134        low = tm->tm_mday;
 135        low = low - high * 10;
 136        buf[RTC_DATE] = (high << 4) + low;
 137        buf[RTC_WEEKDAY] = tm->tm_wday;
 138        high = tm->tm_hour / 10;
 139        low = tm->tm_hour;
 140        low = low - high * 10;
 141        buf[RTC_HOUR] = (high << 4) + low;
 142        high = tm->tm_min / 10;
 143        low = tm->tm_min;
 144        low = low - high * 10;
 145        buf[RTC_MIN] = (high << 4) + low;
 146        high = tm->tm_sec / 10;
 147        low = tm->tm_sec;
 148        low = low - high * 10;
 149        buf[RTC_SEC] = (high << 4) + low;
 150        return 0;
 151}
 152
 153static int max8925_rtc_read_time(struct device *dev, struct rtc_time *tm)
 154{
 155        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 156        unsigned char buf[TIME_NUM];
 157        int ret;
 158
 159        ret = max8925_bulk_read(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
 160        if (ret < 0)
 161                goto out;
 162        ret = tm_calc(tm, buf, TIME_NUM);
 163out:
 164        return ret;
 165}
 166
 167static int max8925_rtc_set_time(struct device *dev, struct rtc_time *tm)
 168{
 169        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 170        unsigned char buf[TIME_NUM];
 171        int ret;
 172
 173        ret = data_calc(buf, tm, TIME_NUM);
 174        if (ret < 0)
 175                goto out;
 176        ret = max8925_bulk_write(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
 177out:
 178        return ret;
 179}
 180
 181static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 182{
 183        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 184        unsigned char buf[TIME_NUM];
 185        int ret;
 186
 187        ret = max8925_bulk_read(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
 188        if (ret < 0)
 189                goto out;
 190        ret = tm_calc(&alrm->time, buf, TIME_NUM);
 191        if (ret < 0)
 192                goto out;
 193        ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK);
 194        if (ret < 0)
 195                goto out;
 196        if (ret & ALARM0_IRQ) {
 197                alrm->enabled = 0;
 198        } else {
 199                ret = max8925_reg_read(info->rtc, MAX8925_ALARM0_CNTL);
 200                if (ret < 0)
 201                        goto out;
 202                if (!ret)
 203                        alrm->enabled = 0;
 204                else
 205                        alrm->enabled = 1;
 206        }
 207        ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS);
 208        if (ret < 0)
 209                goto out;
 210        if (ret & ALARM0_STATUS)
 211                alrm->pending = 1;
 212        else
 213                alrm->pending = 0;
 214        return 0;
 215out:
 216        return ret;
 217}
 218
 219static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 220{
 221        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 222        unsigned char buf[TIME_NUM];
 223        int ret;
 224
 225        ret = data_calc(buf, &alrm->time, TIME_NUM);
 226        if (ret < 0)
 227                goto out;
 228        ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
 229        if (ret < 0)
 230                goto out;
 231        if (alrm->enabled)
 232                /* only enable alarm on year/month/day/hour/min/sec */
 233                ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
 234        else
 235                ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0);
 236        if (ret < 0)
 237                goto out;
 238out:
 239        return ret;
 240}
 241
 242static const struct rtc_class_ops max8925_rtc_ops = {
 243        .read_time      = max8925_rtc_read_time,
 244        .set_time       = max8925_rtc_set_time,
 245        .read_alarm     = max8925_rtc_read_alarm,
 246        .set_alarm      = max8925_rtc_set_alarm,
 247};
 248
 249static int __devinit max8925_rtc_probe(struct platform_device *pdev)
 250{
 251        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 252        struct max8925_rtc_info *info;
 253        int irq, ret;
 254
 255        info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL);
 256        if (!info)
 257                return -ENOMEM;
 258        info->chip = chip;
 259        info->rtc = chip->rtc;
 260        info->dev = &pdev->dev;
 261        irq = chip->irq_base + MAX8925_IRQ_RTC_ALARM0;
 262
 263        ret = request_threaded_irq(irq, NULL, rtc_update_handler,
 264                                   IRQF_ONESHOT, "rtc-alarm0", info);
 265        if (ret < 0) {
 266                dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
 267                        irq, ret);
 268                goto out_irq;
 269        }
 270
 271        dev_set_drvdata(&pdev->dev, info);
 272        /* XXX - isn't this redundant? */
 273        platform_set_drvdata(pdev, info);
 274
 275        device_init_wakeup(&pdev->dev, 1);
 276
 277        info->rtc_dev = rtc_device_register("max8925-rtc", &pdev->dev,
 278                                        &max8925_rtc_ops, THIS_MODULE);
 279        ret = PTR_ERR(info->rtc_dev);
 280        if (IS_ERR(info->rtc_dev)) {
 281                dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
 282                goto out_rtc;
 283        }
 284
 285        return 0;
 286out_rtc:
 287        platform_set_drvdata(pdev, NULL);
 288        free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
 289out_irq:
 290        kfree(info);
 291        return ret;
 292}
 293
 294static int __devexit max8925_rtc_remove(struct platform_device *pdev)
 295{
 296        struct max8925_rtc_info *info = platform_get_drvdata(pdev);
 297
 298        if (info) {
 299                free_irq(info->chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
 300                rtc_device_unregister(info->rtc_dev);
 301                kfree(info);
 302        }
 303        return 0;
 304}
 305
 306#ifdef CONFIG_PM_SLEEP
 307static int max8925_rtc_suspend(struct device *dev)
 308{
 309        struct platform_device *pdev = to_platform_device(dev);
 310        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 311
 312        if (device_may_wakeup(dev))
 313                chip->wakeup_flag |= 1 << MAX8925_IRQ_RTC_ALARM0;
 314        return 0;
 315}
 316static int max8925_rtc_resume(struct device *dev)
 317{
 318        struct platform_device *pdev = to_platform_device(dev);
 319        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 320
 321        if (device_may_wakeup(dev))
 322                chip->wakeup_flag &= ~(1 << MAX8925_IRQ_RTC_ALARM0);
 323        return 0;
 324}
 325#endif
 326
 327static SIMPLE_DEV_PM_OPS(max8925_rtc_pm_ops, max8925_rtc_suspend, max8925_rtc_resume);
 328
 329static struct platform_driver max8925_rtc_driver = {
 330        .driver         = {
 331                .name   = "max8925-rtc",
 332                .owner  = THIS_MODULE,
 333                .pm     = &max8925_rtc_pm_ops,
 334        },
 335        .probe          = max8925_rtc_probe,
 336        .remove         = __devexit_p(max8925_rtc_remove),
 337};
 338
 339module_platform_driver(max8925_rtc_driver);
 340
 341MODULE_DESCRIPTION("Maxim MAX8925 RTC driver");
 342MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
 343MODULE_LICENSE("GPL");
 344
 345