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        int                     irq;
  73};
  74
  75static irqreturn_t rtc_update_handler(int irq, void *data)
  76{
  77        struct max8925_rtc_info *info = (struct max8925_rtc_info *)data;
  78
  79        /* disable ALARM0 except for 1SEC alarm */
  80        max8925_set_bits(info->rtc, MAX8925_ALARM0_CNTL, 0x7f, 0);
  81        rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
  82        return IRQ_HANDLED;
  83}
  84
  85static int tm_calc(struct rtc_time *tm, unsigned char *buf, int len)
  86{
  87        if (len < TIME_NUM)
  88                return -EINVAL;
  89        tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000
  90                        + (buf[RTC_YEAR2] & 0xf) * 100
  91                        + (buf[RTC_YEAR1] >> 4) * 10
  92                        + (buf[RTC_YEAR1] & 0xf);
  93        tm->tm_year -= 1900;
  94        tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10
  95                        + (buf[RTC_MONTH] & 0x0f);
  96        tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10
  97                        + (buf[RTC_DATE] & 0x0f);
  98        tm->tm_wday = buf[RTC_WEEKDAY] & 0x07;
  99        if (buf[RTC_HOUR] & HOUR_12) {
 100                tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10
 101                                + (buf[RTC_HOUR] & 0x0f);
 102                if (buf[RTC_HOUR] & HOUR_AM_PM)
 103                        tm->tm_hour += 12;
 104        } else
 105                tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10
 106                                + (buf[RTC_HOUR] & 0x0f);
 107        tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10
 108                        + (buf[RTC_MIN] & 0x0f);
 109        tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10
 110                        + (buf[RTC_SEC] & 0x0f);
 111        return 0;
 112}
 113
 114static int data_calc(unsigned char *buf, struct rtc_time *tm, int len)
 115{
 116        unsigned char high, low;
 117
 118        if (len < TIME_NUM)
 119                return -EINVAL;
 120
 121        high = (tm->tm_year + 1900) / 1000;
 122        low = (tm->tm_year + 1900) / 100;
 123        low = low - high * 10;
 124        buf[RTC_YEAR2] = (high << 4) + low;
 125        high = (tm->tm_year + 1900) / 10;
 126        low = tm->tm_year + 1900;
 127        low = low - high * 10;
 128        high = high - (high / 10) * 10;
 129        buf[RTC_YEAR1] = (high << 4) + low;
 130        high = tm->tm_mon / 10;
 131        low = tm->tm_mon;
 132        low = low - high * 10;
 133        buf[RTC_MONTH] = (high << 4) + low;
 134        high = tm->tm_mday / 10;
 135        low = tm->tm_mday;
 136        low = low - high * 10;
 137        buf[RTC_DATE] = (high << 4) + low;
 138        buf[RTC_WEEKDAY] = tm->tm_wday;
 139        high = tm->tm_hour / 10;
 140        low = tm->tm_hour;
 141        low = low - high * 10;
 142        buf[RTC_HOUR] = (high << 4) + low;
 143        high = tm->tm_min / 10;
 144        low = tm->tm_min;
 145        low = low - high * 10;
 146        buf[RTC_MIN] = (high << 4) + low;
 147        high = tm->tm_sec / 10;
 148        low = tm->tm_sec;
 149        low = low - high * 10;
 150        buf[RTC_SEC] = (high << 4) + low;
 151        return 0;
 152}
 153
 154static int max8925_rtc_read_time(struct device *dev, struct rtc_time *tm)
 155{
 156        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 157        unsigned char buf[TIME_NUM];
 158        int ret;
 159
 160        ret = max8925_bulk_read(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
 161        if (ret < 0)
 162                goto out;
 163        ret = tm_calc(tm, buf, TIME_NUM);
 164out:
 165        return ret;
 166}
 167
 168static int max8925_rtc_set_time(struct device *dev, struct rtc_time *tm)
 169{
 170        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 171        unsigned char buf[TIME_NUM];
 172        int ret;
 173
 174        ret = data_calc(buf, tm, TIME_NUM);
 175        if (ret < 0)
 176                goto out;
 177        ret = max8925_bulk_write(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
 178out:
 179        return ret;
 180}
 181
 182static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 183{
 184        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 185        unsigned char buf[TIME_NUM];
 186        int ret;
 187
 188        ret = max8925_bulk_read(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
 189        if (ret < 0)
 190                goto out;
 191        ret = tm_calc(&alrm->time, buf, TIME_NUM);
 192        if (ret < 0)
 193                goto out;
 194        ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK);
 195        if (ret < 0)
 196                goto out;
 197        if (ret & ALARM0_IRQ) {
 198                alrm->enabled = 0;
 199        } else {
 200                ret = max8925_reg_read(info->rtc, MAX8925_ALARM0_CNTL);
 201                if (ret < 0)
 202                        goto out;
 203                if (!ret)
 204                        alrm->enabled = 0;
 205                else
 206                        alrm->enabled = 1;
 207        }
 208        ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS);
 209        if (ret < 0)
 210                goto out;
 211        if (ret & ALARM0_STATUS)
 212                alrm->pending = 1;
 213        else
 214                alrm->pending = 0;
 215        return 0;
 216out:
 217        return ret;
 218}
 219
 220static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 221{
 222        struct max8925_rtc_info *info = dev_get_drvdata(dev);
 223        unsigned char buf[TIME_NUM];
 224        int ret;
 225
 226        ret = data_calc(buf, &alrm->time, TIME_NUM);
 227        if (ret < 0)
 228                goto out;
 229        ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
 230        if (ret < 0)
 231                goto out;
 232        if (alrm->enabled)
 233                /* only enable alarm on year/month/day/hour/min/sec */
 234                ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
 235        else
 236                ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0);
 237        if (ret < 0)
 238                goto out;
 239out:
 240        return ret;
 241}
 242
 243static const struct rtc_class_ops max8925_rtc_ops = {
 244        .read_time      = max8925_rtc_read_time,
 245        .set_time       = max8925_rtc_set_time,
 246        .read_alarm     = max8925_rtc_read_alarm,
 247        .set_alarm      = max8925_rtc_set_alarm,
 248};
 249
 250static int max8925_rtc_probe(struct platform_device *pdev)
 251{
 252        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 253        struct max8925_rtc_info *info;
 254        int ret;
 255
 256        info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL);
 257        if (!info)
 258                return -ENOMEM;
 259        info->chip = chip;
 260        info->rtc = chip->rtc;
 261        info->dev = &pdev->dev;
 262        info->irq = platform_get_irq(pdev, 0);
 263
 264        ret = request_threaded_irq(info->irq, NULL, rtc_update_handler,
 265                                   IRQF_ONESHOT, "rtc-alarm0", info);
 266        if (ret < 0) {
 267                dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
 268                        info->irq, ret);
 269                goto out_irq;
 270        }
 271
 272        dev_set_drvdata(&pdev->dev, info);
 273        /* XXX - isn't this redundant? */
 274        platform_set_drvdata(pdev, info);
 275
 276        device_init_wakeup(&pdev->dev, 1);
 277
 278        info->rtc_dev = rtc_device_register("max8925-rtc", &pdev->dev,
 279                                        &max8925_rtc_ops, THIS_MODULE);
 280        ret = PTR_ERR(info->rtc_dev);
 281        if (IS_ERR(info->rtc_dev)) {
 282                dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
 283                goto out_rtc;
 284        }
 285
 286        return 0;
 287out_rtc:
 288        platform_set_drvdata(pdev, NULL);
 289        free_irq(info->irq, info);
 290out_irq:
 291        kfree(info);
 292        return ret;
 293}
 294
 295static int max8925_rtc_remove(struct platform_device *pdev)
 296{
 297        struct max8925_rtc_info *info = platform_get_drvdata(pdev);
 298
 299        if (info) {
 300                free_irq(info->irq, info);
 301                rtc_device_unregister(info->rtc_dev);
 302                kfree(info);
 303        }
 304        return 0;
 305}
 306
 307#ifdef CONFIG_PM_SLEEP
 308static int max8925_rtc_suspend(struct device *dev)
 309{
 310        struct platform_device *pdev = to_platform_device(dev);
 311        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 312
 313        if (device_may_wakeup(dev))
 314                chip->wakeup_flag |= 1 << MAX8925_IRQ_RTC_ALARM0;
 315        return 0;
 316}
 317static int max8925_rtc_resume(struct device *dev)
 318{
 319        struct platform_device *pdev = to_platform_device(dev);
 320        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 321
 322        if (device_may_wakeup(dev))
 323                chip->wakeup_flag &= ~(1 << MAX8925_IRQ_RTC_ALARM0);
 324        return 0;
 325}
 326#endif
 327
 328static SIMPLE_DEV_PM_OPS(max8925_rtc_pm_ops, max8925_rtc_suspend, max8925_rtc_resume);
 329
 330static struct platform_driver max8925_rtc_driver = {
 331        .driver         = {
 332                .name   = "max8925-rtc",
 333                .owner  = THIS_MODULE,
 334                .pm     = &max8925_rtc_pm_ops,
 335        },
 336        .probe          = max8925_rtc_probe,
 337        .remove         = max8925_rtc_remove,
 338};
 339
 340module_platform_driver(max8925_rtc_driver);
 341
 342MODULE_DESCRIPTION("Maxim MAX8925 RTC driver");
 343MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
 344MODULE_LICENSE("GPL");
 345
 346
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.