MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / char / moxa_rtc.c
blob7e6831ae0418f3b80acf2d65ce86cac36ba26260
1 /*
2 * A simple generic Real Time Clock interface for Linux.
4 * History:
5 * Date Author Comment
6 * 12-27-2005 Victor Yu. Create it.
7 */
9 #define RTC_VERSION "1.0"
11 #include <linux/config.h>
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/types.h>
15 #include <linux/miscdevice.h>
16 #include <linux/fcntl.h>
17 #include <linux/init.h>
18 #include <linux/poll.h>
19 #include <linux/proc_fs.h>
20 #include <linux/spinlock.h>
21 #include <linux/delay.h>
22 #include <linux/rtc.h>
23 #include <linux/interrupt.h>
25 #include <asm/io.h>
26 #include <asm/uaccess.h>
27 #include <asm/system.h>
28 #include <asm/arch/cpe/cpe.h>
29 #include <asm/arch/gpio.h>
31 #if (defined CONFIG_ARCH_IA241_32128)||(defined CONFIG_ARCH_IA241_16128) // add by Victor Yu. 05-22-2007
32 #define CONFIG_ARCH_IA241
33 #endif
35 #if (defined CONFIG_ARCH_UC_7112_LX_PLUS_LITON)
36 #define CONFIG_ARCH_UC_7112_LX_PLUS
37 #endif
39 #if ( defined CONFIG_ARCH_W341 ) || ( defined CONFIG_ARCH_W345 ) || ( defined CONFIG_ARCH_W345_IMP1 ) || ( defined CONFIG_ARCH_UC_7112_LX_PLUS ) || ( defined CONFIG_ARCH_W321 ) || ( defined CONFIG_ARCH_W311 )|| ( defined CONFIG_ARCH_W325 ) || (defined CONFIG_ARCH_W315)
40 #define GPIO_RTC_RESET (1<<7)
41 #define GPIO_RTC_SCLK (1<<5)
42 #define GPIO_RTC_DATA (1<<6)
43 #elif ( defined CONFIG_ARCH_IA240 ) || ( defined CONFIG_ARCH_IA241 )
44 #define GPIO_RTC_RESET (1<<8)
45 #define GPIO_RTC_SCLK (1<<28)
46 #define GPIO_RTC_DATA (1<<9)
48 #elif ( defined CONFIG_ARCH_W311 )
49 #define GPIO_RTC_RESET (1<<26)
50 #define GPIO_RTC_SCLK (1<<24)
51 #define GPIO_RTC_DATA (1<<25)
53 #endif
55 #define RTC_PROTECT_W 0x8E
56 #define RTC_PROTECT_R 0x8F
57 #define RTC_YEAR_W 0x8C
58 #define RTC_YEAR_R 0x8D
59 #define RTC_DAY_W 0x8A
60 #define RTC_DAY_R 0x8B
61 #define RTC_MONTH_W 0x88
62 #define RTC_MONTH_R 0x89
63 #define RTC_DATE_W 0x86
64 #define RTC_DATE_R 0x87
65 #define RTC_HOURS_W 0x84
66 #define RTC_HOURS_R 0x85
67 #define RTC_MINUTES_W 0x82
68 #define RTC_MINUTES_R 0x83
69 #define RTC_SECONDS_W 0x80
70 #define RTC_SECONDS_R 0x81
72 #define RTC_DELAY_TIME 8 // 8 usecond
74 static unsigned long rtc_status = 0; /* bitmapped status byte. */
76 static int rtc_read_proc(char *page, char **start, off_t off,
77 int count, int *eof, void *data);
80 #define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
82 static spinlock_t rtc_lock;
83 static unsigned long epoch = 2000; /* year corresponding to 0x00 */
85 static const unsigned char days_in_mo[] =
86 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
88 static u8 RTCReadRegister(u8 Cmd)
90 u8 data;
91 int i, v;
92 unsigned long flags;
94 save_flags(flags);
95 cli();
96 mcpu_gpio_inout(GPIO_RTC_DATA, MCPU_GPIO_OUTPUT);
97 mcpu_gpio_set(GPIO_RTC_RESET, MCPU_GPIO_HIGH);
98 //udelay(RTC_DELAY_TIME);
99 /* write command byte */
100 for ( i=0; i<8; i++, Cmd>>=1 ){
101 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_LOW);
102 if ( Cmd & 1 )
103 mcpu_gpio_set(GPIO_RTC_DATA, MCPU_GPIO_HIGH);
104 else
105 mcpu_gpio_set(GPIO_RTC_DATA, MCPU_GPIO_LOW);
106 //udelay(RTC_DELAY_TIME);
107 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_HIGH);
108 //udelay(RTC_DELAY_TIME);
111 mcpu_gpio_inout(GPIO_RTC_DATA, MCPU_GPIO_INPUT);
112 /* read data byte */
113 //udelay(RTC_DELAY_TIME);
114 for ( i=0,data=0; i<8; i++ ){
115 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_LOW);
116 //udelay(RTC_DELAY_TIME);
117 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_HIGH);
118 v = mcpu_gpio_get(GPIO_RTC_DATA);
119 if ( v )
120 data |= (1<<i);
121 //udelay(RTC_DELAY_TIME);
123 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_LOW);
124 mcpu_gpio_set(GPIO_RTC_RESET, MCPU_GPIO_LOW);
125 //udelay(RTC_DELAY_TIME);
126 restore_flags(flags);
128 return data;
131 static void RTCWriteRegister(u8 Cmd, u8 Data)
133 int i;
134 unsigned long flags;
136 save_flags(flags);
137 cli();
138 mcpu_gpio_inout(GPIO_RTC_DATA, MCPU_GPIO_OUTPUT);
139 mcpu_gpio_set(GPIO_RTC_RESET, MCPU_GPIO_HIGH);
140 //udelay(RTC_DELAY_TIME);
141 /* write command byte */
142 for ( i=0; i<8; i++,Cmd>>=1 ) {
143 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_LOW);
144 if ( Cmd & 1 )
145 mcpu_gpio_set(GPIO_RTC_DATA, MCPU_GPIO_HIGH);
146 else
147 mcpu_gpio_set(GPIO_RTC_DATA, MCPU_GPIO_LOW);
148 //udelay(RTC_DELAY_TIME);
149 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_HIGH);
150 //udelay(RTC_DELAY_TIME);
153 /* write data byte */
154 mcpu_gpio_inout(GPIO_RTC_DATA, MCPU_GPIO_OUTPUT);
155 for ( i=0; i<8; i++,Data>>=1 ){
156 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_LOW);
157 if ( Data & 1 )
158 mcpu_gpio_set(GPIO_RTC_DATA, MCPU_GPIO_HIGH);
159 else
160 mcpu_gpio_set(GPIO_RTC_DATA, MCPU_GPIO_LOW);
161 //udelay(RTC_DELAY_TIME);
162 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_HIGH);
163 //udelay(RTC_DELAY_TIME);
165 mcpu_gpio_set(GPIO_RTC_SCLK, MCPU_GPIO_LOW);
166 mcpu_gpio_set(GPIO_RTC_RESET, MCPU_GPIO_LOW);
167 //udelay(RTC_DELAY_TIME);
168 mcpu_gpio_inout(GPIO_RTC_DATA, MCPU_GPIO_INPUT);
169 restore_flags(flags);
172 #if 1 // add by Victor Yu. 01-10-2005
173 static int day_of_year[12]={0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
174 #endif
175 static void get_rtc_time(struct rtc_time *rtc_tm)
177 unsigned char v;
179 spin_lock_irq(&rtc_lock);
180 v = RTCReadRegister(RTC_SECONDS_R);
181 rtc_tm->tm_sec = (((v & 0x70) >> 4) * 10) + (v & 0x0F);
182 v = RTCReadRegister(RTC_MINUTES_R);
183 rtc_tm->tm_min = (((v & 0x70) >> 4) * 10) + (v & 0x0F);
184 v = RTCReadRegister(RTC_HOURS_R);
185 if ( v & 0x80 ) { // 12-hour mode
186 rtc_tm->tm_hour = (((v & 0x10) >> 4) * 10) + (v & 0x0F);
187 if ( v & 0x20 ) { // PM mode
188 rtc_tm->tm_hour += 12;
189 if ( rtc_tm->tm_hour >= 24 )
190 rtc_tm->tm_hour = 0;
192 } else { // 24-hour mode
193 rtc_tm->tm_hour = (((v & 0x30) >> 4) * 10) + (v & 0x0F);
195 v = RTCReadRegister(RTC_DATE_R);
196 rtc_tm->tm_mday = (((v & 0x30) >> 4) * 10) + (v & 0x0F);
197 v = RTCReadRegister(RTC_MONTH_R);
198 rtc_tm->tm_mon = (((v & 0x10) >> 4) * 10) + (v & 0x0F);
199 rtc_tm->tm_mon--;
200 v = RTCReadRegister(RTC_YEAR_R);
201 rtc_tm->tm_year = (((v & 0xF0) >> 4) * 10) + (v & 0x0F);
202 if ((rtc_tm->tm_year += (epoch - 1900)) <= 69)
203 rtc_tm->tm_year += 100;
204 #if 1 // add by Victor Yu. 01-10-2005
205 v = RTCReadRegister(RTC_DAY_R);
206 rtc_tm->tm_wday = (v & 0x0f) - 1;
207 rtc_tm->tm_yday = day_of_year[rtc_tm->tm_mon];
208 rtc_tm->tm_yday += (rtc_tm->tm_mday-1);
209 if ( rtc_tm->tm_mon >= 2 ) {
210 if ( !(rtc_tm->tm_year % 4) && (rtc_tm->tm_year % 100) )
211 rtc_tm->tm_yday++;
213 rtc_tm->tm_isdst = 0;
214 #endif
215 spin_unlock_irq(&rtc_lock);
218 static int
219 rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
220 unsigned long arg)
222 struct rtc_time rtc_tm;
223 unsigned char v;
225 switch (cmd) {
226 case RTC_RD_TIME: /* Read the time/date from RTC */
227 get_rtc_time(&rtc_tm);
228 return copy_to_user((void *) arg, &rtc_tm, sizeof(rtc_tm)) ?
229 -EFAULT : 0;
230 case RTC_SET_TIME: /* Set the RTC */
232 unsigned char mon, day, hrs, min, sec, leap_yr;
233 unsigned int yrs;
235 if (!capable(CAP_SYS_TIME))
236 return -EACCES;
238 if (copy_from_user(&rtc_tm,
239 (struct rtc_time *) arg,
240 sizeof(struct rtc_time)))
241 return -EFAULT;
243 yrs = rtc_tm.tm_year + 1900;
244 mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
245 day = rtc_tm.tm_mday;
246 hrs = rtc_tm.tm_hour;
247 min = rtc_tm.tm_min;
248 sec = rtc_tm.tm_sec;
250 if (yrs < 1970)
251 return -EINVAL;
253 leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
255 if ((mon > 12) || (day == 0))
256 return -EINVAL;
258 if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
259 return -EINVAL;
261 if ((hrs >= 24) || (min >= 60) || (sec >= 60))
262 return -EINVAL;
264 if ((yrs -= epoch) > 255) /* They are unsigned */
265 return -EINVAL;
266 spin_lock_irq(&rtc_lock);
267 /* These limits and adjustments are independant of
268 * whether the chip is in binary mode or not.
270 if (yrs > 169) {
271 spin_unlock_irq(&rtc_lock);
272 return -EINVAL;
274 if (yrs >= 100)
275 yrs -= 100;
277 RTCWriteRegister(RTC_PROTECT_W, 0);
278 v = ((hrs / 10) << 4) | (hrs % 10);
279 RTCWriteRegister(RTC_HOURS_W, v);
280 v = ((min / 10) << 4) | (min % 10);
281 RTCWriteRegister(RTC_MINUTES_W, v);
282 v = ((sec / 10) << 4) | (sec % 10);
283 RTCWriteRegister(RTC_SECONDS_W, v);
284 v = ((yrs / 10) << 4) | (yrs % 10);
285 RTCWriteRegister(RTC_YEAR_W, v);
286 v = ((mon / 10) << 4) | (mon % 10);
287 RTCWriteRegister(RTC_MONTH_W, v);
288 v = ((day / 10) << 4) | (day % 10);
289 RTCWriteRegister(RTC_DATE_W, v);
290 RTCWriteRegister(RTC_PROTECT_W, 0x80);
292 spin_unlock_irq(&rtc_lock);
293 return 0;
295 default:
296 return -EINVAL;
300 /* We use rtc_lock to protect against concurrent opens. So the BKL is not
301 * needed here. Or anywhere else in this driver. */
302 static int rtc_open(struct inode *inode, struct file *file)
304 spin_lock_irq(&rtc_lock);
306 if (rtc_status & RTC_IS_OPEN) {
307 spin_unlock_irq(&rtc_lock);
308 return -EBUSY;
311 rtc_status |= RTC_IS_OPEN;
313 spin_unlock_irq(&rtc_lock);
314 return 0;
317 static int rtc_release(struct inode *inode, struct file *file)
319 spin_lock_irq(&rtc_lock);
320 rtc_status &= ~RTC_IS_OPEN;
321 spin_unlock_irq(&rtc_lock);
322 return 0;
326 * The various file operations we support.
329 static struct file_operations rtc_fops = {
330 owner:THIS_MODULE,
331 llseek:no_llseek,
332 ioctl:rtc_ioctl,
333 open:rtc_open,
334 release:rtc_release,
337 static struct miscdevice rtc_dev = {
338 RTC_MINOR,
339 "rtc",
340 &rtc_fops
343 static int __init rtc_init(void)
345 #if 1 // add by Victor Yu. 04-21-2005, to avoid the RTS stop
346 struct rtc_time rtc_tm;
348 // set the CPU for GPIO
349 mcpu_gpio_mp_set(GPIO_RTC_RESET|GPIO_RTC_SCLK|GPIO_RTC_DATA);
352 // default set all RTC GPIO to output on the Moxa CPU
353 mcpu_gpio_inout(GPIO_RTC_RESET|GPIO_RTC_SCLK|GPIO_RTC_DATA, MCPU_GPIO_OUTPUT);
355 get_rtc_time(&rtc_tm);
356 //printk("YYYY-MON-DAY-HH-MM-SS=%d-%d-%d-%d-%d-%d\n", rtc_tm.tm_year, rtc_tm.tm_mon, rtc_tm.tm_mday, rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
357 if ( rtc_tm.tm_sec == 0 && rtc_tm.tm_min == 0 && rtc_tm.tm_min == 0 && rtc_tm.tm_hour == 0 &&
358 rtc_tm.tm_year == 100 && rtc_tm.tm_mon == 0 && rtc_tm.tm_mday == 1 ) {
359 printk("The RTC has stoped. Now reenable it.\n");
360 RTCWriteRegister(RTC_PROTECT_W,0);/* Disable Write Protect */
361 RTCWriteRegister(RTC_SECONDS_W,0);/* Enable OSC */
362 RTCWriteRegister(RTC_PROTECT_W,0x80);/* Enable Write Protect */
364 #endif
365 misc_register(&rtc_dev);
366 create_proc_read_entry("driver/rtc", 0, 0, rtc_read_proc, NULL);
368 printk(KERN_INFO "Generic Moxa RC7000 RTC Driver v" RTC_VERSION "\n");
370 return 0;
373 static void __exit rtc_exit(void)
375 remove_proc_entry("driver/rtc", NULL);
376 misc_deregister(&rtc_dev);
380 module_init(rtc_init);
381 module_exit(rtc_exit);
384 * Info exported via "/proc/driver/rtc".
387 static int rtc_proc_output(char *buf)
389 char *p;
390 struct rtc_time tm;
392 get_rtc_time(&tm);
394 p = buf;
397 * There is no way to tell if the luser has the RTC set for local
398 * time or for Universal Standard Time (GMT). Probably local though.
400 p += sprintf(p,
401 "rtc_time\t: %02d:%02d:%02d\n"
402 "rtc_date\t: %04d-%02d-%02d\n"
403 "rtc_epoch\t: %04lu\n",
404 tm.tm_hour, tm.tm_min, tm.tm_sec,
405 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
407 return p - buf;
410 static int rtc_read_proc(char *page, char **start, off_t off,
411 int count, int *eof, void *data)
413 int len = rtc_proc_output(page);
414 if (len <= off + count)
415 *eof = 1;
416 *start = page + off;
417 len -= off;
418 if (len > count)
419 len = count;
420 if (len < 0)
421 len = 0;
422 return len;
425 MODULE_AUTHOR("Victor Yu");
426 MODULE_LICENSE("GPL");