📄 ds12885.c
字号:
case RTC_EPOCH_SET: /* Set the epoch. */ { /* * There were no RTC clocks before 1900. */ if (arg < 1900) return -EINVAL; if (!capable(CAP_SYS_TIME)) return -EACCES; epoch = arg; return 0; } default: return -ENOTTY; } return copy_to_user((void __user *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;}static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ return rtc_do_ioctl(cmd, arg, 0);}/* * We enforce only one user at a time here with the open/close. * Also clear the previous interrupt data on an open, and clean * up things on a close. *//* We use rtc_lock to protect against concurrent opens. So the BKL is not * needed here. Or anywhere else in this driver. */static int rtc_open(struct inode *inode, struct file *file){ spin_lock_irq (&rtc_lock); if(rtc_status & RTC_IS_OPEN) goto out_busy; rtc_status |= RTC_IS_OPEN; rtc_irq_data = 0; spin_unlock_irq (&rtc_lock); return 0;out_busy: spin_unlock_irq (&rtc_lock); return -EBUSY;}static int rtc_release(struct inode *inode, struct file *file){ spin_lock_irq (&rtc_lock); rtc_irq_data = 0; rtc_status &= ~RTC_IS_OPEN; spin_unlock_irq (&rtc_lock); return 0;}/* * The various file operations we support. */static struct file_operations rtc12885_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = rtc_read, .ioctl = rtc_ioctl, .open = rtc_open, .release = rtc_release,};#if 0static void rtc_dropped_irq(unsigned long data){ unsigned long freq; spin_lock_irq (&rtc_lock); /* Just in case someone disabled the timer from behind our back... */ if (rtc_status & RTC_TIMER_ON)// mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); rtc_irq_data += ((rtc_freq/HZ)<<8); rtc_irq_data &= ~0xff; rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */ freq = rtc_freq; spin_unlock_irq(&rtc_lock); printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", freq); /* Now we have new data */ wake_up_interruptible(&rtc_wait); kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);}#endif/* * Info exported via "/proc/driver/rtc". */static int rtc_proc_output (char *buf){#define YN(bit) ((ctrl & bit) ? "yes" : "no")#define NY(bit) ((ctrl & bit) ? "no" : "yes") char *p; struct rtc_time tm; unsigned char batt, ctrl; unsigned long freq; spin_lock_irq(&rtc_lock); batt = CMOS_READ(RTC_VALID) & RTC_VRT; ctrl = CMOS_READ(RTC_CONTROL); freq = rtc_freq; spin_unlock_irq(&rtc_lock); p = buf; rtc_get_rtc_time(&tm); /* * There is no way to tell if the luser has the RTC set for local * time or for Universal Standard Time (GMT). Probably local though. */ p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" "rtc_date\t: %04d-%02d-%02d\n" "rtc_epoch\t: %04lu\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); get_rtc_alm_time(&tm); /* * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will * match any value for that particular field. Values that are * greater than a valid time, but less than 0xc0 shouldn't appear. */ p += sprintf(p, "alarm\t\t: "); if (tm.tm_hour <= 24) p += sprintf(p, "%02d:", tm.tm_hour); else p += sprintf(p, "**:"); if (tm.tm_min <= 59) p += sprintf(p, "%02d:", tm.tm_min); else p += sprintf(p, "**:"); if (tm.tm_sec <= 59) p += sprintf(p, "%02d\n", tm.tm_sec); else p += sprintf(p, "**\n"); p += sprintf(p, "DST_enable\t: %s\n" "BCD\t\t: %s\n" "24hr\t\t: %s\n" "square_wave\t: %s\n" "alarm_IRQ\t: %s\n" "update_IRQ\t: %s\n" "periodic_IRQ\t: %s\n" "periodic_freq\t: %ld\n" "batt_status\t: %s\n", YN(RTC_DST_EN), NY(RTC_DM_BINARY), YN(RTC_24H), YN(RTC_SQWE), YN(RTC_AIE), YN(RTC_UIE), YN(RTC_PIE), freq, batt ? "okay" : "dead"); return p - buf;#undef YN#undef NY}static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ int len = rtc_proc_output (page); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len>count) len = count; if (len<0) len = 0; return len;}void rtc_get_rtc_time(struct rtc_time *rtc_tm){ unsigned long uip_watchdog = jiffies; unsigned char ctrl; /* * read RTC once any update in progress is done. The update * can take just over 2ms. We wait 10 to 20ms. There is no need to * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. * If you need to know *exactly* when a second has started, enable * periodic update complete interrupts, (via ioctl) and then * immediately read /dev/rtc which will block until you get the IRQ. * Once the read clears, read the RTC time (again via ioctl). Easy. */ if (rtc_is_updating() != 0) while (jiffies - uip_watchdog < 2*HZ/100) { barrier(); cpu_relax(); } /* * Only the values that we read from the RTC are set. We leave * tm_wday, tm_yday and tm_isdst untouched. Even though the * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated * by the RTC when initially set to a non-zero value. */ spin_lock_irq(&rtc_lock); rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); rtc_tm->tm_year = CMOS_READ(RTC_YEAR); ctrl = CMOS_READ(RTC_CONTROL); spin_unlock_irq(&rtc_lock); if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BCD_TO_BIN(rtc_tm->tm_sec); BCD_TO_BIN(rtc_tm->tm_min); BCD_TO_BIN(rtc_tm->tm_hour); BCD_TO_BIN(rtc_tm->tm_mday); BCD_TO_BIN(rtc_tm->tm_mon); BCD_TO_BIN(rtc_tm->tm_year); } /* * Account for differences between how the RTC uses the values * and how they are defined in a struct rtc_time; */ if ((rtc_tm->tm_year += (epoch - 1900)) <= 69) rtc_tm->tm_year += 100; rtc_tm->tm_mon--;}static void get_rtc_alm_time(struct rtc_time *alm_tm){ unsigned char ctrl; /* * Only the values that we read from the RTC are set. That * means only tm_hour, tm_min, and tm_sec. */ spin_lock_irq(&rtc_lock); alm_tm->tm_sec = CMOS_READ(RTC_SECONDS_ALARM); alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM); alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM); ctrl = CMOS_READ(RTC_CONTROL); spin_unlock_irq(&rtc_lock); if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BCD_TO_BIN(alm_tm->tm_sec); BCD_TO_BIN(alm_tm->tm_min); BCD_TO_BIN(alm_tm->tm_hour); }}static void mask_rtc_irq_bit(unsigned char bit){ unsigned char val; spin_lock_irq(&rtc_lock); val = CMOS_READ(RTC_CONTROL); val &= ~bit; CMOS_WRITE(val, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); rtc_irq_data = 0; spin_unlock_irq(&rtc_lock);}static void set_rtc_irq_bit(unsigned char bit){ unsigned char val; spin_lock_irq(&rtc_lock); val = CMOS_READ(RTC_CONTROL); val |= bit; CMOS_WRITE(val, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); rtc_irq_data = 0; spin_unlock_irq(&rtc_lock);}static int __init test_set_ds12885(void){#define RTC_INIT 0xA5 unsigned char rtc_state,temp=0; spin_lock_irq(&rtc_lock); CMOS_WRITE(0xAA, 0xE); CMOS_WRITE(0x55, 0x31); temp=CMOS_READ(0xE); if(temp!=0xAA){ printk(KERN_ALERT "Not found DS12885!\n"); spin_unlock_irq(&rtc_lock); return 1; } temp=CMOS_READ(0x31); if(temp!=0x55){ printk(KERN_ALERT "Not found DS12885!\n"); spin_unlock_irq(&rtc_lock); return 1; } if(!(CMOS_READ(RTC_VALID) & RTC_VRT)){ printk(KERN_ALERT "DS12885's battery error!\n"); printk(KERN_INFO "Ds12885 init...\n"); goto init_rtc; } rtc_state=CMOS_READ(0xF); if(rtc_state!=RTC_INIT ){ printk(KERN_INFO "Ds12885 init...\n"); CMOS_WRITE(RTC_INIT, 0xF); goto init_rtc; } printk(KERN_INFO "Test DS12885 OK!\n"); spin_unlock_irq(&rtc_lock); return 0;init_rtc: CMOS_WRITE(0x1A,RTC_CONTROL); CMOS_WRITE(0x2F,RTC_FREQ_SELECT); spin_unlock_irq(&rtc_lock); return 0;#undef RTC_INIT}int __initds12885_init(void){ dev_t dev_Num = 0; int result=0; /* *Initialize periodic freq. to CMOS reset default, which is 2Hz.and test chip. */ if (test_set_ds12885()){ printk(KERN_INFO "test ds12885 failed!\n"); goto error_test; } dev_Num = MKDEV(DS12885_MAJOR, DS12885_MINOR); result = register_chrdev_region(dev_Num, 1, "DS12885-RTC"); if (result < 0) { printk(KERN_WARNING "ds12885 can't get major %d\n", DS12885_MAJOR); goto error_region; } if (!request_mem_region(RTC_PORT(RTC_DATA), 2, "ds12885")) { printk(KERN_ERR "ds12885: I/O port %d is not free.\n", RTC_PORT (RTC_DATA)); goto error_mem; } cdev_init(&rtc12885_dev.cdev, &rtc12885_fops); rtc12885_dev.cdev.owner = THIS_MODULE; rtc12885_dev.cdev.ops = &rtc12885_fops; result = cdev_add (&rtc12885_dev.cdev, dev_Num, 1); if (result) { printk(KERN_NOTICE "ds12885 Can not add cdev!\n"); goto error_dev; } if (!create_proc_read_entry ("driver/rtc-ds12885", 0, NULL, rtc_read_proc, NULL)) goto error_proc; sysctl_header = register_sysctl_table(dev_root, 0); outl((inl(GPIO_AINTEN) & (~DS12885_IRQ_POART)), GPIO_AINTEN); //disable EGPIO 0 as an interrupt pin outl((inl(GPIO_PADDR) & (~DS12885_IRQ_POART)), GPIO_PADDR); //set EGPIO 0 as an input pin outl((inl(GPIO_AINTTYPE1) | DS12885_IRQ_POART), GPIO_AINTTYPE1); //set EGPIO 0 to edge sensitive outl((inl(GPIO_AINTTYPE2) & (~DS12885_IRQ_POART)), GPIO_AINTTYPE2); //set EGPIO 0 interrupt is falling edge outl((inl(GPIO_ADB) | DS12885_IRQ_POART), GPIO_ADB); //set EGPIO 0 debounce register if (request_irq(ds12885_irq, ds12885_interrupt, SA_INTERRUPT|SA_SHIRQ, "ds12885", &rtc12885_dev)) { printk(KERN_ERR "ds12885: cannot register IRQ %d\n", ds12885_irq); goto error_irq; } else{ outl((inl(GPIO_AINTEN) | DS12885_IRQ_POART), GPIO_AINTEN); }#ifdef CONFIG_DEVFS_FS devfs_mk_cdev(dev_Num, S_IFCHR|S_IRUGO|S_IWUGO, "rtc");#endif //#ifdef CONFIG_DEVFS_FS printk(KERN_INFO "ds12885 Real Time Clock Driver init OK!\n"); return 0; error_irq: remove_proc_entry ("driver/rtc-ds12885", NULL); error_proc: cdev_del(&rtc12885_dev.cdev);// result=-ENOMEM;error_dev: // result= release_mem_region(RTC_PORT(RTC_DATA), 2); error_mem: unregister_chrdev_region(dev_Num, 1);// result = -EIO error_region: goto init_failed;error_test: // result= init_failed: return -EIO;} static void __exitds12885_exit(void){#ifdef CONFIG_DEVFS_FS devfs_remove("rtc");#endif //#ifdef CONFIG_DEVFS_FS outl((inl(GPIO_AINTEN) & (~DS12885_IRQ_POART)), GPIO_AINTEN); free_irq (ds12885_irq, &rtc12885_dev); cdev_del(&rtc12885_dev.cdev); unregister_chrdev_region(MKDEV(DS12885_MAJOR, DS12885_MINOR), 1); release_mem_region (RTC_PORT (RTC_DATA), 2); cleanup_sysctl();// unregister_sysctl_table(sysctl_header); remove_proc_entry ("driver/rtc-ds12885", NULL);}module_init(ds12885_init);module_exit(ds12885_exit);MODULE_AUTHOR("LiTao");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("DS12885 RTC Driver");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -