Blame view
drivers/rtc/rtc-max8998.c
7.94 KB
9b16c0a43 rtc: Add MAX8998 ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * RTC driver for Maxim MAX8998 * * Copyright (C) 2010 Samsung Electronics Co.Ltd * Author: Minkyu Kang <mk7.kang@samsung.com> * Author: Joonyoung Shim <jy0922.shim@samsung.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #include <linux/module.h> #include <linux/i2c.h> #include <linux/slab.h> #include <linux/bcd.h> #include <linux/rtc.h> #include <linux/platform_device.h> #include <linux/mfd/max8998.h> #include <linux/mfd/max8998-private.h> |
337ce5d1c mfd: Support LP39... |
23 |
#include <linux/delay.h> |
9b16c0a43 rtc: Add MAX8998 ... |
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
#define MAX8998_RTC_SEC 0x00 #define MAX8998_RTC_MIN 0x01 #define MAX8998_RTC_HOUR 0x02 #define MAX8998_RTC_WEEKDAY 0x03 #define MAX8998_RTC_DATE 0x04 #define MAX8998_RTC_MONTH 0x05 #define MAX8998_RTC_YEAR1 0x06 #define MAX8998_RTC_YEAR2 0x07 #define MAX8998_ALARM0_SEC 0x08 #define MAX8998_ALARM0_MIN 0x09 #define MAX8998_ALARM0_HOUR 0x0a #define MAX8998_ALARM0_WEEKDAY 0x0b #define MAX8998_ALARM0_DATE 0x0c #define MAX8998_ALARM0_MONTH 0x0d #define MAX8998_ALARM0_YEAR1 0x0e #define MAX8998_ALARM0_YEAR2 0x0f #define MAX8998_ALARM1_SEC 0x10 #define MAX8998_ALARM1_MIN 0x11 #define MAX8998_ALARM1_HOUR 0x12 #define MAX8998_ALARM1_WEEKDAY 0x13 #define MAX8998_ALARM1_DATE 0x14 #define MAX8998_ALARM1_MONTH 0x15 #define MAX8998_ALARM1_YEAR1 0x16 #define MAX8998_ALARM1_YEAR2 0x17 #define MAX8998_ALARM0_CONF 0x18 #define MAX8998_ALARM1_CONF 0x19 #define MAX8998_RTC_STATUS 0x1a #define MAX8998_WTSR_SMPL_CNTL 0x1b #define MAX8998_TEST 0x1f #define HOUR_12 (1 << 7) #define HOUR_PM (1 << 5) #define ALARM0_STATUS (1 << 1) #define ALARM1_STATUS (1 << 2) enum { RTC_SEC = 0, RTC_MIN, RTC_HOUR, RTC_WEEKDAY, RTC_DATE, RTC_MONTH, RTC_YEAR1, RTC_YEAR2, }; struct max8998_rtc_info { struct device *dev; struct max8998_dev *max8998; struct i2c_client *rtc; struct rtc_device *rtc_dev; int irq; |
337ce5d1c mfd: Support LP39... |
77 |
bool lp3974_bug_workaround; |
9b16c0a43 rtc: Add MAX8998 ... |
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
}; static void max8998_data_to_tm(u8 *data, struct rtc_time *tm) { tm->tm_sec = bcd2bin(data[RTC_SEC]); tm->tm_min = bcd2bin(data[RTC_MIN]); if (data[RTC_HOUR] & HOUR_12) { tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f); if (data[RTC_HOUR] & HOUR_PM) tm->tm_hour += 12; } else tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f); tm->tm_wday = data[RTC_WEEKDAY] & 0x07; tm->tm_mday = bcd2bin(data[RTC_DATE]); tm->tm_mon = bcd2bin(data[RTC_MONTH]); tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100; tm->tm_year -= 1900; } static void max8998_tm_to_data(struct rtc_time *tm, u8 *data) { data[RTC_SEC] = bin2bcd(tm->tm_sec); data[RTC_MIN] = bin2bcd(tm->tm_min); data[RTC_HOUR] = bin2bcd(tm->tm_hour); data[RTC_WEEKDAY] = tm->tm_wday; data[RTC_DATE] = bin2bcd(tm->tm_mday); data[RTC_MONTH] = bin2bcd(tm->tm_mon); data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100); data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100); } static int max8998_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct max8998_rtc_info *info = dev_get_drvdata(dev); u8 data[8]; int ret; ret = max8998_bulk_read(info->rtc, MAX8998_RTC_SEC, 8, data); if (ret < 0) return ret; max8998_data_to_tm(data, tm); return rtc_valid_tm(tm); } static int max8998_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct max8998_rtc_info *info = dev_get_drvdata(dev); u8 data[8]; |
337ce5d1c mfd: Support LP39... |
129 |
int ret; |
9b16c0a43 rtc: Add MAX8998 ... |
130 131 |
max8998_tm_to_data(tm, data); |
337ce5d1c mfd: Support LP39... |
132 133 134 135 136 137 |
ret = max8998_bulk_write(info->rtc, MAX8998_RTC_SEC, 8, data); if (info->lp3974_bug_workaround) msleep(2000); return ret; |
9b16c0a43 rtc: Add MAX8998 ... |
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
} static int max8998_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct max8998_rtc_info *info = dev_get_drvdata(dev); u8 data[8]; u8 val; int ret; ret = max8998_bulk_read(info->rtc, MAX8998_ALARM0_SEC, 8, data); if (ret < 0) return ret; max8998_data_to_tm(data, &alrm->time); ret = max8998_read_reg(info->rtc, MAX8998_ALARM0_CONF, &val); if (ret < 0) return ret; alrm->enabled = !!val; ret = max8998_read_reg(info->rtc, MAX8998_RTC_STATUS, &val); if (ret < 0) return ret; if (val & ALARM0_STATUS) alrm->pending = 1; else alrm->pending = 0; return 0; } static int max8998_rtc_stop_alarm(struct max8998_rtc_info *info) { |
337ce5d1c mfd: Support LP39... |
173 174 175 176 177 178 |
int ret = max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0); if (info->lp3974_bug_workaround) msleep(2000); return ret; |
9b16c0a43 rtc: Add MAX8998 ... |
179 180 181 182 |
} static int max8998_rtc_start_alarm(struct max8998_rtc_info *info) { |
337ce5d1c mfd: Support LP39... |
183 184 185 186 187 188 189 190 191 192 193 194 195 |
int ret; u8 alarm0_conf = 0x77; /* LP3974 with delay bug chips has rtc alarm bugs with "MONTH" field */ if (info->lp3974_bug_workaround) alarm0_conf = 0x57; ret = max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, alarm0_conf); if (info->lp3974_bug_workaround) msleep(2000); return ret; |
9b16c0a43 rtc: Add MAX8998 ... |
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
} static int max8998_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct max8998_rtc_info *info = dev_get_drvdata(dev); u8 data[8]; int ret; max8998_tm_to_data(&alrm->time, data); ret = max8998_rtc_stop_alarm(info); if (ret < 0) return ret; ret = max8998_bulk_write(info->rtc, MAX8998_ALARM0_SEC, 8, data); if (ret < 0) return ret; |
337ce5d1c mfd: Support LP39... |
213 214 |
if (info->lp3974_bug_workaround) msleep(2000); |
9b16c0a43 rtc: Add MAX8998 ... |
215 |
if (alrm->enabled) |
337ce5d1c mfd: Support LP39... |
216 |
ret = max8998_rtc_start_alarm(info); |
9b16c0a43 rtc: Add MAX8998 ... |
217 |
|
337ce5d1c mfd: Support LP39... |
218 |
return ret; |
9b16c0a43 rtc: Add MAX8998 ... |
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
} static int max8998_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct max8998_rtc_info *info = dev_get_drvdata(dev); if (enabled) return max8998_rtc_start_alarm(info); else return max8998_rtc_stop_alarm(info); } static irqreturn_t max8998_rtc_alarm_irq(int irq, void *data) { struct max8998_rtc_info *info = data; rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); return IRQ_HANDLED; } static const struct rtc_class_ops max8998_rtc_ops = { .read_time = max8998_rtc_read_time, .set_time = max8998_rtc_set_time, .read_alarm = max8998_rtc_read_alarm, .set_alarm = max8998_rtc_set_alarm, .alarm_irq_enable = max8998_rtc_alarm_irq_enable, }; static int __devinit max8998_rtc_probe(struct platform_device *pdev) { struct max8998_dev *max8998 = dev_get_drvdata(pdev->dev.parent); |
337ce5d1c mfd: Support LP39... |
252 |
struct max8998_platform_data *pdata = dev_get_platdata(max8998->dev); |
9b16c0a43 rtc: Add MAX8998 ... |
253 254 255 256 257 258 259 260 261 262 263 |
struct max8998_rtc_info *info; int ret; info = kzalloc(sizeof(struct max8998_rtc_info), GFP_KERNEL); if (!info) return -ENOMEM; info->dev = &pdev->dev; info->max8998 = max8998; info->rtc = max8998->rtc; info->irq = max8998->irq_base + MAX8998_IRQ_ALARM0; |
03cf7c477 rtc: max8998: Ini... |
264 |
platform_set_drvdata(pdev, info); |
9b16c0a43 rtc: Add MAX8998 ... |
265 266 267 268 269 270 271 272 273 |
info->rtc_dev = rtc_device_register("max8998-rtc", &pdev->dev, &max8998_rtc_ops, THIS_MODULE); if (IS_ERR(info->rtc_dev)) { ret = PTR_ERR(info->rtc_dev); dev_err(&pdev->dev, "Failed to register RTC device: %d ", ret); goto out_rtc; } |
9b16c0a43 rtc: Add MAX8998 ... |
274 275 |
ret = request_threaded_irq(info->irq, NULL, max8998_rtc_alarm_irq, 0, "rtc-alarm0", info); |
337ce5d1c mfd: Support LP39... |
276 |
|
9b16c0a43 rtc: Add MAX8998 ... |
277 278 279 280 |
if (ret < 0) dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d ", info->irq, ret); |
337ce5d1c mfd: Support LP39... |
281 282 283 284 285 286 287 288 |
dev_info(&pdev->dev, "RTC CHIP NAME: %s ", pdev->id_entry->name); if (pdata->rtc_delay) { info->lp3974_bug_workaround = true; dev_warn(&pdev->dev, "LP3974 with RTC REGERR option." " RTC updates will be extremely slow. "); } |
9b16c0a43 rtc: Add MAX8998 ... |
289 290 291 |
return 0; out_rtc: |
03cf7c477 rtc: max8998: Ini... |
292 |
platform_set_drvdata(pdev, NULL); |
9b16c0a43 rtc: Add MAX8998 ... |
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
kfree(info); return ret; } static int __devexit max8998_rtc_remove(struct platform_device *pdev) { struct max8998_rtc_info *info = platform_get_drvdata(pdev); if (info) { free_irq(info->irq, info); rtc_device_unregister(info->rtc_dev); kfree(info); } return 0; } |
337ce5d1c mfd: Support LP39... |
309 310 311 312 313 |
static const struct platform_device_id max8998_rtc_id[] = { { "max8998-rtc", TYPE_MAX8998 }, { "lp3974-rtc", TYPE_LP3974 }, { } }; |
9b16c0a43 rtc: Add MAX8998 ... |
314 315 316 317 318 319 320 |
static struct platform_driver max8998_rtc_driver = { .driver = { .name = "max8998-rtc", .owner = THIS_MODULE, }, .probe = max8998_rtc_probe, .remove = __devexit_p(max8998_rtc_remove), |
337ce5d1c mfd: Support LP39... |
321 |
.id_table = max8998_rtc_id, |
9b16c0a43 rtc: Add MAX8998 ... |
322 |
}; |
0c4eae665 rtc: convert driv... |
323 |
module_platform_driver(max8998_rtc_driver); |
9b16c0a43 rtc: Add MAX8998 ... |
324 325 326 327 328 |
MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); MODULE_DESCRIPTION("Maxim MAX8998 RTC driver"); MODULE_LICENSE("GPL"); |