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) == 0)
 197                alrm->enabled = 1;
 198        else
 199                alrm->enabled = 0;
 200        ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS);
 201        if (ret < 0)
 202                goto out;
 203        if (ret & ALARM0_STATUS)
 204                alrm->pending = 1;
 205        else
 206                alrm->pending = 0;
 207out:
 208        return ret;
 209}
 210
 211static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 212{
 213        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 214        unsigned char buf[TIME_NUM];
 215        int ret;
 216
 217        ret = data_calc(buf, &alrm->time, TIME_NUM);
 218        if (ret < 0)
 219                goto out;
 220        ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
 221        if (ret < 0)
 222                goto out;
 223        /* only enable alarm on year/month/day/hour/min/sec */
 224        ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
 225        if (ret < 0)
 226                goto out;
 227out:
 228        return ret;
 229}
 230
 231static const struct rtc_class_ops max8925_rtc_ops = {
 232        .read_time      = max8925_rtc_read_time,
 233        .set_time       = max8925_rtc_set_time,
 234        .read_alarm     = max8925_rtc_read_alarm,
 235        .set_alarm      = max8925_rtc_set_alarm,
 236};
 237
 238static int __devinit max8925_rtc_probe(struct platform_device *pdev)
 239{
 240        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 241        struct max8925_rtc_info *info;
 242        int irq, ret;
 243
 244        info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL);
 245        if (!info)
 246                return -ENOMEM;
 247        info->chip = chip;
 248        info->rtc = chip->rtc;
 249        info->dev = &pdev->dev;
 250        irq = chip->irq_base + MAX8925_IRQ_RTC_ALARM0;
 251
 252        ret = request_threaded_irq(irq, NULL, rtc_update_handler,
 253                                   IRQF_ONESHOT, "rtc-alarm0", info);
 254        if (ret < 0) {
 255                dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
 256                        irq, ret);
 257                goto out_irq;
 258        }
 259
 260        dev_set_drvdata(&pdev->dev, info);
 261        /* XXX - isn't this redundant? */
 262        platform_set_drvdata(pdev, info);
 263
 264        device_init_wakeup(&pdev->dev, 1);
 265
 266        info->rtc_dev = rtc_device_register("max8925-rtc", &pdev->dev,
 267                                        &max8925_rtc_ops, THIS_MODULE);
 268        ret = PTR_ERR(info->rtc_dev);
 269        if (IS_ERR(info->rtc_dev)) {
 270                dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
 271                goto out_rtc;
 272        }
 273
 274        return 0;
 275out_rtc:
 276        platform_set_drvdata(pdev, NULL);
 277        free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
 278out_irq:
 279        kfree(info);
 280        return ret;
 281}
 282
 283static int __devexit max8925_rtc_remove(struct platform_device *pdev)
 284{
 285        struct max8925_rtc_info *info = platform_get_drvdata(pdev);
 286
 287        if (info) {
 288                free_irq(info->chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
 289                rtc_device_unregister(info->rtc_dev);
 290                kfree(info);
 291        }
 292        return 0;
 293}
 294
 295#ifdef CONFIG_PM_SLEEP
 296static int max8925_rtc_suspend(struct device *dev)
 297{
 298        struct platform_device *pdev = to_platform_device(dev);
 299        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 300
 301        if (device_may_wakeup(dev))
 302                chip->wakeup_flag |= 1 << MAX8925_IRQ_RTC_ALARM0;
 303        return 0;
 304}
 305static int max8925_rtc_resume(struct device *dev)
 306{
 307        struct platform_device *pdev = to_platform_device(dev);
 308        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 309
 310        if (device_may_wakeup(dev))
 311                chip->wakeup_flag &= ~(1 << MAX8925_IRQ_RTC_ALARM0);
 312        return 0;
 313}
 314#endif
 315
 316static SIMPLE_DEV_PM_OPS(max8925_rtc_pm_ops, max8925_rtc_suspend, max8925_rtc_resume);
 317
 318static struct platform_driver max8925_rtc_driver = {
 319        .driver         = {
 320                .name   = "max8925-rtc",
 321                .owner  = THIS_MODULE,
 322                .pm     = &max8925_rtc_pm_ops,
 323        },
 324        .probe          = max8925_rtc_probe,
 325        .remove         = __devexit_p(max8925_rtc_remove),
 326};
 327
 328module_platform_driver(max8925_rtc_driver);
 329
 330MODULE_DESCRIPTION("Maxim MAX8925 RTC driver");
 331MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
 332MODULE_LICENSE("GPL");
 333
 334