linux/drivers/rtc/rtc-da9052.c
<<
>>
Prefs
   1/*
   2 * Real time clock driver for DA9052
   3 *
   4 * Copyright(c) 2012 Dialog Semiconductor Ltd.
   5 *
   6 * Author: Dajun Dajun Chen <dajun.chen@diasemi.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/platform_device.h>
  17#include <linux/rtc.h>
  18
  19#include <linux/mfd/da9052/da9052.h>
  20#include <linux/mfd/da9052/reg.h>
  21
  22#define rtc_err(da9052, fmt, ...) \
  23                dev_err(da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
  24
  25struct da9052_rtc {
  26        struct rtc_device *rtc;
  27        struct da9052 *da9052;
  28        int irq;
  29};
  30
  31static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable)
  32{
  33        int ret;
  34        if (enable) {
  35                ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
  36                                        DA9052_ALARM_Y_ALARM_ON,
  37                                        DA9052_ALARM_Y_ALARM_ON);
  38                if (ret != 0)
  39                        rtc_err(da9052, "Failed to enable ALM: %d\n", ret);
  40        } else {
  41                ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
  42                                        DA9052_ALARM_Y_ALARM_ON, 0);
  43                if (ret != 0)
  44                        rtc_err(da9052, "Write error: %d\n", ret);
  45        }
  46        return ret;
  47}
  48
  49static irqreturn_t da9052_rtc_irq(int irq, void *data)
  50{
  51        struct da9052_rtc *rtc = data;
  52        int ret;
  53
  54        ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_MI_REG);
  55        if (ret < 0) {
  56                rtc_err(rtc->da9052, "Read error: %d\n", ret);
  57                return IRQ_NONE;
  58        }
  59
  60        if (ret & DA9052_ALARMMI_ALARMTYPE) {
  61                da9052_rtc_enable_alarm(rtc->da9052, 0);
  62                rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
  63        } else
  64                rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_PF);
  65
  66        return IRQ_HANDLED;
  67}
  68
  69static int da9052_read_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm)
  70{
  71        int ret;
  72        uint8_t v[5];
  73
  74        ret = da9052_group_read(da9052, DA9052_ALARM_MI_REG, 5, v);
  75        if (ret != 0) {
  76                rtc_err(da9052, "Failed to group read ALM: %d\n", ret);
  77                return ret;
  78        }
  79
  80        rtc_tm->tm_year = (v[4] & DA9052_RTC_YEAR) + 100;
  81        rtc_tm->tm_mon  = (v[3] & DA9052_RTC_MONTH) - 1;
  82        rtc_tm->tm_mday = v[2] & DA9052_RTC_DAY;
  83        rtc_tm->tm_hour = v[1] & DA9052_RTC_HOUR;
  84        rtc_tm->tm_min  = v[0] & DA9052_RTC_MIN;
  85
  86        ret = rtc_valid_tm(rtc_tm);
  87        if (ret != 0)
  88                return ret;
  89        return ret;
  90}
  91
  92static int da9052_set_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm)
  93{
  94        int ret;
  95        uint8_t v[3];
  96
  97        rtc_tm->tm_year -= 100;
  98        rtc_tm->tm_mon += 1;
  99
 100        ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG,
 101                                DA9052_RTC_MIN, rtc_tm->tm_min);
 102        if (ret != 0) {
 103                rtc_err(da9052, "Failed to write ALRM MIN: %d\n", ret);
 104                return ret;
 105        }
 106
 107        v[0] = rtc_tm->tm_hour;
 108        v[1] = rtc_tm->tm_mday;
 109        v[2] = rtc_tm->tm_mon;
 110
 111        ret = da9052_group_write(da9052, DA9052_ALARM_H_REG, 3, v);
 112        if (ret < 0)
 113                return ret;
 114
 115        ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
 116                                DA9052_RTC_YEAR, rtc_tm->tm_year);
 117        if (ret != 0)
 118                rtc_err(da9052, "Failed to write ALRM YEAR: %d\n", ret);
 119
 120        return ret;
 121}
 122
 123static int da9052_rtc_get_alarm_status(struct da9052 *da9052)
 124{
 125        int ret;
 126
 127        ret = da9052_reg_read(da9052, DA9052_ALARM_Y_REG);
 128        if (ret < 0) {
 129                rtc_err(da9052, "Failed to read ALM: %d\n", ret);
 130                return ret;
 131        }
 132        ret &= DA9052_ALARM_Y_ALARM_ON;
 133        return (ret > 0) ? 1 : 0;
 134}
 135
 136static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
 137{
 138        struct da9052_rtc *rtc = dev_get_drvdata(dev);
 139        uint8_t v[6];
 140        int ret;
 141
 142        ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, v);
 143        if (ret < 0) {
 144                rtc_err(rtc->da9052, "Failed to read RTC time : %d\n", ret);
 145                return ret;
 146        }
 147
 148        rtc_tm->tm_year = (v[5] & DA9052_RTC_YEAR) + 100;
 149        rtc_tm->tm_mon  = (v[4] & DA9052_RTC_MONTH) - 1;
 150        rtc_tm->tm_mday = v[3] & DA9052_RTC_DAY;
 151        rtc_tm->tm_hour = v[2] & DA9052_RTC_HOUR;
 152        rtc_tm->tm_min  = v[1] & DA9052_RTC_MIN;
 153        rtc_tm->tm_sec  = v[0] & DA9052_RTC_SEC;
 154
 155        ret = rtc_valid_tm(rtc_tm);
 156        if (ret != 0) {
 157                rtc_err(rtc->da9052, "rtc_valid_tm failed: %d\n", ret);
 158                return ret;
 159        }
 160
 161        return 0;
 162}
 163
 164static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm)
 165{
 166        struct da9052_rtc *rtc;
 167        uint8_t v[6];
 168
 169        rtc = dev_get_drvdata(dev);
 170
 171        v[0] = tm->tm_sec;
 172        v[1] = tm->tm_min;
 173        v[2] = tm->tm_hour;
 174        v[3] = tm->tm_mday;
 175        v[4] = tm->tm_mon + 1;
 176        v[5] = tm->tm_year - 100;
 177
 178        return da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v);
 179}
 180
 181static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 182{
 183        int ret;
 184        struct rtc_time *tm = &alrm->time;
 185        struct da9052_rtc *rtc = dev_get_drvdata(dev);
 186
 187        ret = da9052_read_alarm(rtc->da9052, tm);
 188
 189        if (ret)
 190                return ret;
 191
 192        alrm->enabled = da9052_rtc_get_alarm_status(rtc->da9052);
 193
 194        return 0;
 195}
 196
 197static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 198{
 199        int ret;
 200        struct rtc_time *tm = &alrm->time;
 201        struct da9052_rtc *rtc = dev_get_drvdata(dev);
 202
 203        ret = da9052_rtc_enable_alarm(rtc->da9052, 0);
 204        if (ret < 0)
 205                return ret;
 206
 207        ret = da9052_set_alarm(rtc->da9052, tm);
 208        if (ret)
 209                return ret;
 210
 211        ret = da9052_rtc_enable_alarm(rtc->da9052, 1);
 212
 213        return ret;
 214}
 215
 216static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
 217{
 218        struct da9052_rtc *rtc = dev_get_drvdata(dev);
 219
 220        return da9052_rtc_enable_alarm(rtc->da9052, enabled);
 221}
 222
 223static const struct rtc_class_ops da9052_rtc_ops = {
 224        .read_time      = da9052_rtc_read_time,
 225        .set_time       = da9052_rtc_set_time,
 226        .read_alarm     = da9052_rtc_read_alarm,
 227        .set_alarm      = da9052_rtc_set_alarm,
 228        .alarm_irq_enable = da9052_rtc_alarm_irq_enable,
 229};
 230
 231static int da9052_rtc_probe(struct platform_device *pdev)
 232{
 233        struct da9052_rtc *rtc;
 234        int ret;
 235
 236        rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9052_rtc), GFP_KERNEL);
 237        if (!rtc)
 238                return -ENOMEM;
 239
 240        rtc->da9052 = dev_get_drvdata(pdev->dev.parent);
 241        platform_set_drvdata(pdev, rtc);
 242        rtc->irq = platform_get_irq_byname(pdev, "ALM");
 243        ret = request_threaded_irq(rtc->irq, NULL, da9052_rtc_irq,
 244                                   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
 245                                   "ALM", rtc);
 246        if (ret != 0) {
 247                rtc_err(rtc->da9052, "irq registration failed: %d\n", ret);
 248                return ret;
 249        }
 250
 251        rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
 252                                       &da9052_rtc_ops, THIS_MODULE);
 253        if (IS_ERR(rtc->rtc)) {
 254                ret = PTR_ERR(rtc->rtc);
 255                goto err_free_irq;
 256        }
 257
 258        return 0;
 259
 260err_free_irq:
 261        free_irq(rtc->irq, rtc);
 262        return ret;
 263}
 264
 265static int da9052_rtc_remove(struct platform_device *pdev)
 266{
 267        struct da9052_rtc *rtc = pdev->dev.platform_data;
 268
 269        rtc_device_unregister(rtc->rtc);
 270        free_irq(rtc->irq, rtc);
 271        platform_set_drvdata(pdev, NULL);
 272
 273        return 0;
 274}
 275
 276static struct platform_driver da9052_rtc_driver = {
 277        .probe  = da9052_rtc_probe,
 278        .remove = da9052_rtc_remove,
 279        .driver = {
 280                .name   = "da9052-rtc",
 281                .owner  = THIS_MODULE,
 282        },
 283};
 284
 285module_platform_driver(da9052_rtc_driver);
 286
 287MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
 288MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC");
 289MODULE_LICENSE("GPL");
 290MODULE_ALIAS("platform:da9052-rtc");
 291