diff options
author | Magnus Damm <magnus.damm@gmail.com> | 2008-02-06 04:38:53 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-06 13:41:14 -0500 |
commit | 2805b9698445e898ca6e5ffdc132e80d94664a0f (patch) | |
tree | 7341a04b8666df38be04fde6b28dbdbfa81e1069 /drivers/rtc/rtc-r9701.c | |
parent | 102f4a02de5c7217a04ccbbc24f35224b98bb183 (diff) |
rtc: add support for Epson RTC-9701JE V2
Add support for the Epson RTC-9701JE SPI RTC device.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Acked-by: Alessandro Zummo <a.zummo@towertech.it>
Acked-by: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc/rtc-r9701.c')
-rw-r--r-- | drivers/rtc/rtc-r9701.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c new file mode 100644 index 000000000000..926e8fda83c0 --- /dev/null +++ b/drivers/rtc/rtc-r9701.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* drivers/rtc/rtc-r9701.c | ||
2 | * | ||
3 | * Driver for Epson RTC-9701JE | ||
4 | * | ||
5 | * Copyright (C) 2008 Magnus Damm | ||
6 | * | ||
7 | * Based on drivers/rtc/rtc-max6902.c | ||
8 | * | ||
9 | * Copyright (C) 2006 8D Technologies inc. | ||
10 | * Copyright (C) 2004 Compulab Ltd. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/version.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/rtc.h> | ||
24 | #include <linux/spi/spi.h> | ||
25 | #include <linux/bcd.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/bitops.h> | ||
28 | |||
29 | #define RSECCNT 0x00 /* Second Counter */ | ||
30 | #define RMINCNT 0x01 /* Minute Counter */ | ||
31 | #define RHRCNT 0x02 /* Hour Counter */ | ||
32 | #define RWKCNT 0x03 /* Week Counter */ | ||
33 | #define RDAYCNT 0x04 /* Day Counter */ | ||
34 | #define RMONCNT 0x05 /* Month Counter */ | ||
35 | #define RYRCNT 0x06 /* Year Counter */ | ||
36 | #define R100CNT 0x07 /* Y100 Counter */ | ||
37 | #define RMINAR 0x08 /* Minute Alarm */ | ||
38 | #define RHRAR 0x09 /* Hour Alarm */ | ||
39 | #define RWKAR 0x0a /* Week/Day Alarm */ | ||
40 | #define RTIMCNT 0x0c /* Interval Timer */ | ||
41 | #define REXT 0x0d /* Extension Register */ | ||
42 | #define RFLAG 0x0e /* RTC Flag Register */ | ||
43 | #define RCR 0x0f /* RTC Control Register */ | ||
44 | |||
45 | static int write_reg(struct device *dev, int address, unsigned char data) | ||
46 | { | ||
47 | struct spi_device *spi = to_spi_device(dev); | ||
48 | unsigned char buf[2]; | ||
49 | |||
50 | buf[0] = address & 0x7f; | ||
51 | buf[1] = data; | ||
52 | |||
53 | return spi_write(spi, buf, ARRAY_SIZE(buf)); | ||
54 | } | ||
55 | |||
56 | static int read_regs(struct device *dev, unsigned char *regs, int no_regs) | ||
57 | { | ||
58 | struct spi_device *spi = to_spi_device(dev); | ||
59 | u8 txbuf[1], rxbuf[1]; | ||
60 | int k, ret; | ||
61 | |||
62 | ret = 0; | ||
63 | |||
64 | for (k = 0; ret == 0 && k < no_regs; k++) { | ||
65 | txbuf[0] = 0x80 | regs[k]; | ||
66 | ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1); | ||
67 | regs[k] = rxbuf[0]; | ||
68 | } | ||
69 | |||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | static int r9701_get_datetime(struct device *dev, struct rtc_time *dt) | ||
74 | { | ||
75 | struct spi_device *spi = to_spi_device(dev); | ||
76 | unsigned long time; | ||
77 | int ret; | ||
78 | unsigned char buf[] = { RSECCNT, RMINCNT, RHRCNT, | ||
79 | RDAYCNT, RMONCNT, RYRCNT }; | ||
80 | |||
81 | ret = read_regs(&spi->dev, buf, ARRAY_SIZE(buf)); | ||
82 | if (ret) | ||
83 | return ret; | ||
84 | |||
85 | memset(dt, 0, sizeof(*dt)); | ||
86 | |||
87 | dt->tm_sec = BCD2BIN(buf[0]); /* RSECCNT */ | ||
88 | dt->tm_min = BCD2BIN(buf[1]); /* RMINCNT */ | ||
89 | dt->tm_hour = BCD2BIN(buf[2]); /* RHRCNT */ | ||
90 | |||
91 | dt->tm_mday = BCD2BIN(buf[3]); /* RDAYCNT */ | ||
92 | dt->tm_mon = BCD2BIN(buf[4]) - 1; /* RMONCNT */ | ||
93 | dt->tm_year = BCD2BIN(buf[5]) + 100; /* RYRCNT */ | ||
94 | |||
95 | /* the rtc device may contain illegal values on power up | ||
96 | * according to the data sheet. make sure they are valid. | ||
97 | */ | ||
98 | |||
99 | ret = rtc_valid_tm(dt); | ||
100 | if (ret) | ||
101 | return ret; | ||
102 | |||
103 | /* don't bother with yday, wday and isdst. | ||
104 | * let the rtc core calculate them for us. | ||
105 | */ | ||
106 | |||
107 | rtc_tm_to_time(dt, &time); | ||
108 | rtc_time_to_tm(time, dt); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int r9701_set_datetime(struct device *dev, struct rtc_time *dt) | ||
113 | { | ||
114 | int ret, year; | ||
115 | |||
116 | year = dt->tm_year + 1900; | ||
117 | if (year >= 2100 || year < 2000) | ||
118 | return -EINVAL; | ||
119 | |||
120 | ret = write_reg(dev, RHRCNT, BIN2BCD(dt->tm_hour)); | ||
121 | ret |= write_reg(dev, RMINCNT, BIN2BCD(dt->tm_min)); | ||
122 | ret |= write_reg(dev, RSECCNT, BIN2BCD(dt->tm_sec)); | ||
123 | ret |= write_reg(dev, RDAYCNT, BIN2BCD(dt->tm_mday)); | ||
124 | ret |= write_reg(dev, RMONCNT, BIN2BCD(dt->tm_mon + 1)); | ||
125 | ret |= write_reg(dev, RYRCNT, BIN2BCD(dt->tm_year - 100)); | ||
126 | ret |= write_reg(dev, RWKCNT, 1 << dt->tm_wday); | ||
127 | |||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | static const struct rtc_class_ops r9701_rtc_ops = { | ||
132 | .read_time = r9701_get_datetime, | ||
133 | .set_time = r9701_set_datetime, | ||
134 | }; | ||
135 | |||
136 | static int __devinit r9701_probe(struct spi_device *spi) | ||
137 | { | ||
138 | struct rtc_device *rtc; | ||
139 | unsigned char tmp; | ||
140 | int res; | ||
141 | |||
142 | rtc = rtc_device_register("r9701", | ||
143 | &spi->dev, &r9701_rtc_ops, THIS_MODULE); | ||
144 | if (IS_ERR(rtc)) | ||
145 | return PTR_ERR(rtc); | ||
146 | |||
147 | dev_set_drvdata(&spi->dev, rtc); | ||
148 | |||
149 | spi->mode = SPI_MODE_3; | ||
150 | spi->bits_per_word = 8; | ||
151 | spi_setup(spi); | ||
152 | |||
153 | tmp = R100CNT; | ||
154 | res = read_regs(&spi->dev, &tmp, 1); | ||
155 | if (res || tmp != 0x20) { | ||
156 | rtc_device_unregister(rtc); | ||
157 | return res; | ||
158 | } | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int __devexit r9701_remove(struct spi_device *spi) | ||
164 | { | ||
165 | struct rtc_device *rtc = dev_get_drvdata(&spi->dev); | ||
166 | |||
167 | rtc_device_unregister(rtc); | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static struct spi_driver r9701_driver = { | ||
172 | .driver = { | ||
173 | .name = "rtc-r9701", | ||
174 | .bus = &spi_bus_type, | ||
175 | .owner = THIS_MODULE, | ||
176 | }, | ||
177 | .probe = r9701_probe, | ||
178 | .remove = __devexit_p(r9701_remove), | ||
179 | }; | ||
180 | |||
181 | static __init int r9701_init(void) | ||
182 | { | ||
183 | return spi_register_driver(&r9701_driver); | ||
184 | } | ||
185 | module_init(r9701_init); | ||
186 | |||
187 | static __exit void r9701_exit(void) | ||
188 | { | ||
189 | spi_unregister_driver(&r9701_driver); | ||
190 | } | ||
191 | module_exit(r9701_exit); | ||
192 | |||
193 | MODULE_DESCRIPTION("r9701 spi RTC driver"); | ||
194 | MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); | ||
195 | MODULE_LICENSE("GPL"); | ||