Skip to content

Commit a39069f

Browse files
Haojian ZhuangSamuel Ortiz
authored andcommitted
rtc: Enable rtc in max8925
MAX8925 is a PMIC that contains RTC component. Signed-off-by: Haojian Zhuang <haojian.zhuang@marvell.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
1 parent 1f1cf8f commit a39069f

File tree

3 files changed

+325
-0
lines changed

3 files changed

+325
-0
lines changed

drivers/rtc/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,16 @@ config RTC_DRV_MAX6900
175175
This driver can also be built as a module. If so, the module
176176
will be called rtc-max6900.
177177

178+
config RTC_DRV_MAX8925
179+
tristate "Maxim MAX8925"
180+
depends on MFD_MAX8925
181+
help
182+
If you say yes here you will get support for the
183+
RTC of Maxim MAX8925 PMIC.
184+
185+
This driver can also be built as a module. If so, the module
186+
will be called rtc-max8925.
187+
178188
config RTC_DRV_RS5C372
179189
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
180190
help

drivers/rtc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
5252
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
5353
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
5454
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
55+
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
5556
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
5657
obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
5758
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o

drivers/rtc/rtc-max8925.c

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

0 commit comments

Comments
 (0)