/* A driver for the DS1339 RTC connected to the S3C2140 (ARM9 core) from Rolf Freitag 2004. Version 0.1 */ /* * Real Time Clock interface for SolidCard III * * Copyright (C) 2003 Juergen Beisert (jbeisert@netscape.net) * * Based in parts of: * drivers/char/rtc.c * Copyright (C) 1996 Paul Gortmaker * u-boot project, rtc/ds1337.c * (C) Copyright 2001, 2002 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * Keith Outwater, keith_outwater@mvis.com` * * This driver allows use of the real time clock from user space. * It exports the /dev/rtc interface supporting various ioctl() * and also the /proc/driver/rtc pseudo-file for status information. * * 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. * * 0.1 Juergen Beisert, Oct. 2003 */ #define RTC_VERSION 0.1 #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE 1 #endif #define CONFIG_S3C2410_FM /* Externe Deklarationen. */ #include #include #include /* Fuer z.B. __init */ #include /* Fuer z.B. printk */ #include /* Fuer z.B. kmalloc, kfree */ #include /* Fuer z.B. GFP_xxx-Makros */ #include /* Fuer z.B. struct file_operations */ #include /* Fuer z.B. EINVAL */ #include /* Fuer GMC_BUTTON_MAJOR */ #include /* Fuer z.B. free_irq */ #include /* Fuer z.B. SUCCESS */ #include /* Fuer z.B. udelay */ #include /* Fuer z.B. outb */ //#include #include #include #include #include #include #include #include #include #include #include #include #define YEAR_OFFSET 2000 #define RTC_GETDATETIME 0 #define RTC_SETTIME 1 #define RTC_SETDATETIME 2 #define RTC_GETCTRL 3 #define RTC_SETCTRL 4 #define MEM_READ 5 #define MEM_WRITE 6 /* Lokale Funtionen */ static loff_t ds1339_rtc_llseek (struct file *filp, loff_t ppos, int origin); static ssize_t ds1339_rtc_read (struct file *filp, char *buf, size_t count, loff_t * ppos); static ssize_t ds1339_rtc_write (struct file *filp, const char *buf, size_t count, loff_t * ppos); static int ds1339_rtc_ioctl (struct inode *inode, struct file *filp, unsigned int iocmd, unsigned long ioarg); static int ds1339_rtc_open (struct inode *inode, struct file *filp); static int ds1339_rtc_release (struct inode *inode, struct file *filp); static int ds1339_command (struct i2c_client *client, unsigned int cmd, void *arg); static int ds1339_probe (struct i2c_adapter *); static int ds1339_command (struct i2c_client *, unsigned int, void *); static int ds1339_detach (struct i2c_client *); static int register_rtc_driver (void); static int deregister_rtc_driver (void); static int rtc_read_proc (char *, char **, off_t, int, int *, void *); #define DAT(x) ((unsigned int)(x->data)) /* ------------------------------------------------------------ */ static int needsRegistering = 1; static unsigned short ignore[] = { I2C_CLIENT_END }; static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END }; static struct i2c_client_address_data addr_data = { normal_i2c:normal_addr, normal_i2c_range:ignore, probe:ignore, probe_range:ignore, ignore:ignore, ignore_range:ignore, force:ignore, }; /* ------------------------------------------------------------ */ static struct i2c_client *realTimeClock; /* ------------------- I2C stuff ------------------------------ */ /* Lokale Variable */ static struct file_operations ds1339_rtc_fops = { owner:THIS_MODULE, llseek:ds1339_rtc_llseek, /* Gibt nur einen Fehler zurueck. */ read:ds1339_rtc_read, /* Lese-Routine fuer Polling. */ write:ds1339_rtc_write, /* Gibt nur einen Fehler zurueck. */ ioctl:ds1339_rtc_ioctl, /* Gibt nur einen Fehler zurueck. */ open:ds1339_rtc_open, /* Datenstrom vom Treiber oeffnen. */ release:ds1339_rtc_release, /* Datenstrom vom Treiber schliessen. */ }; static struct i2c_driver ds1339_driver = { name:"DS1339", id:I2C_DRIVERID_PCF8583, flags:I2C_DF_NOTIFY, attach_adapter:ds1339_probe, detach_client:ds1339_detach, command:ds1339_command }; int ds1339RtcOpen = 0; /************************************************************************ * 2.Teil - Entry Points - Externer Teil des Treibers * ************************************************************************/ /*********************************************************************** * ds1339_rtc_init() Global * * Wird von Linux ueber mem.c beim Booten aufgerufen. * * initialisiert die IO-Ports, die zur Tataturabfrage benoetigt * * werdem, traegt den Treiber ein und gibt eine Meldung aus. * * Parameter: keiner * * Return: -errno - Fehlercode * * 0 - SUCCESS, fuer OK. * ***********************************************************************/ int __init ds1339_rtc_init (void) { int rerr; /* Fehlercode von register_chrdev. */ printk ("*** RTC Support by Rolf Freitag\n"); rerr = register_chrdev (RTC_BUTTON_MAJOR, RTC_BUTTON_DEV_NAME, &ds1339_rtc_fops); if (rerr) /* Fehler bei der Registrierung des Treibers? */ { printk (" driver not installed!\n"); if (rerr == -EINVAL) /* Device-Nummer nicht zulaessig. */ printk (" major device number >= MAX_CHRDEV!\n"); else if (rerr == -EBUSY) /* Device-Nummer ist schon belegt. */ printk (" major device number already used!\n"); else /* Anderer Fehler bei register_... */ printk (" watch register_chrdev errno= %6d!\n", rerr); } else /* Treiber eingetragen: */ { printk (" driver version %f loaded\n", RTC_VERSION); ds1339RtcOpen = 0; /* set opencount to 0 */ printk ("** ds1339 rtc loaded\n"); // Initialize } return SUCCESS; } /*********************************************************************** * ds1339_rtc_lseek() Entry-Lokal * * Wird ueber Zeiger in ds1339_rtc_fops aufgerufen. * * Position des Offsets im Treiber aendern. * * llseek macht bei diesem Treiber keinen Sinn, filp->f_pos hat * * hier keine Bedeutung. Daher gibt diese Funktin nur einen * * Fehlercode zurueck. * * Parameter: filp - Zeiger auf die file-Struktur des Geraets. * * ppos - ??? * * origin - ??? * * Return: -EINVAL - Unerlaubter Parameter. * ***********************************************************************/ static loff_t ds1339_rtc_llseek (struct file *filp, loff_t ppos, int origin) { return (0); /* Fehler -> zurueck. */ } /* ..end of ds1339_rtc_lseek. */ #define BCD2VAL(bcd) (10*((bcd) >> 4) + ((bcd) & 0x0F)) #define VAL2BCD(val) ((((val) / 10) << 4) + ((val) % 10)) /*********************************************************************** * ds1339_rtc_read() Entry-Lokal * * Wird ueber Zeiger in ds1339_rtc_fops aufgerufen. * * Lesen vom Treiber. * * Diese Funktion wird immer aufgerufen, wenn ein Prozess vom * * Geraet Daten lesen moechte. * * Parameter: filp - Zeiger auf die file-Struktur des Geraets. * * buf - Zeiger auf zu beschreibenden User-Speicher. * * count - Anzahl der Bytes, die erwartet werden. * * ppos - Zeiger auf ??? * * Return: -errno - Fehlercode, * * 0 - SUCCESS, fuer OK. * ***********************************************************************/ static ssize_t ds1339_rtc_read (struct file *filp, char *buf, size_t count, loff_t * ppos) { if (count >= 6) { rRTCCON = 0x01; ds1339_command (&ds1339_driver, RTC_GETDATETIME, (void *) buf); // buf[5] = BCD2VAL(rBCDYEAR); // buf[4] = BCD2VAL(rBCDMON); // buf[3] = BCD2VAL(rBCDDATE); // buf[2] = BCD2VAL(rBCDHOUR); // buf[1] = BCD2VAL(rBCDMIN); // buf[0] = BCD2VAL(rBCDSEC); // reread values if rBCDSEC == 0! (according to spec); // if (buf[0] == 0) // { // buf[5] = BCD2VAL(rBCDYEAR); // buf[4] = BCD2VAL(rBCDMON); // buf[3] = BCD2VAL(rBCDDATE); // buf[2] = BCD2VAL(rBCDHOUR); // buf[1] = BCD2VAL(rBCDMIN); // buf[0] = BCD2VAL(rBCDSEC); // } rRTCCON = 0x00; return 6; } else { return (-EINVAL); } } /* ..end of ds1339_rtc_read. */ /*********************************************************************** * ds1339_rtc_write() Entry-Lokal * * Wird ueber Zeiger in ds1339_rtc_fops aufgerufen. * * Auf den Treiber schreiben. * * Diese Funktin wird immer aufgerufen, wenn ein Prozess auf das * * Geraet Daten schreiben moechte. * * Parameter: filp - Zeiger auf die file-Struktur des Geraets. * * buf - Zeiger auf zu lesenden User-Speicher. * * count - Anzahl der Bytes, die zu lesen sind. * * ppos - Zeiger auf ??? * * Return: -errno - Fehlercode, * ***********************************************************************/ static ssize_t ds1339_rtc_write (struct file *filp, const char *buf, size_t count, loff_t * ppos) { if (count >= 6) { rRTCCON = 0x01; ds1339_command (&ds1339_driver, RTC_SETDATETIME, (void *) buf); // rBCDYEAR = VAL2BCD(buf[5]); // rBCDMON = VAL2BCD(buf[4]); // rBCDDATE = VAL2BCD(buf[3]); // rBCDHOUR = VAL2BCD(buf[2]); // rBCDMIN = VAL2BCD(buf[1]); // rBCDSEC = VAL2BCD(buf[0]); rRTCCON = 0x00; printk ("rtc set to %02X.%02X.20%02X %02X:%02X:%02X\n", buf[3], buf[4], buf[5], buf[2], buf[1], buf[0]); return 6; } else { return (-EINVAL); } } /* ..end of ds1339_rtc_write. */ /*********************************************************************** * ds1339_rtc_ioctl() Entry-Lokal * * Wird ueber einen Zeiger in ds1339_rtc_fops aufgerufen. * * Verschiedene Treiberabhaengige Dienste, da hier nichts davon * * benoetigt wird, wird generell ein Fehler zurueckgegeben. * * Wird vom Programmierer ueber ioctl(int fd, uint cmd, long arg) * * aufgerufen. * * Parameter: inode - Zeiger auf die inode-Struktur des Geraets. * * filp - Zeiger auf die file-Struktur des Geraets. * * iocmd - Auszufuehrendes Kommando. * * ioarg - Zeiger auf weitere Daten im User-Speicher. * * Return: -errno - Fehlercode, * ***********************************************************************/ static int ds1339_rtc_ioctl (struct inode *inode, struct file *filp, unsigned int iocmd, unsigned long ioarg) { return (-EINVAL); /* Fehler -> zurueck. */ } /* ..end of ds1339_rtc_ioctl. */ /*********************************************************************** * ds1339_rtc_open() Entry-Lokal * * Wird ueber Zeiger in ds1339_rtc_fops aufgerufen. * * Diese Funktin wird immer dann aufgerufen, wenn ein Prozess ein * * open auf dem Treiber durchfuehrt. * * Parameter: inode - Zeiger auf die inode-Struktur des Geraets. * * file - Zeiger auf die file-Struktur des Geraets. * * Return: -errno - Fehlercode, * * 0 - SUCCESS, fuer OK. * ***********************************************************************/ static int ds1339_rtc_open (struct inode *inode, struct file *filp) { int minor; minor = MINOR (inode->i_rdev); if ((minor >= 0) && (minor <= RTC_MAX_MINOR)) { if (ds1339RtcOpen == 0) /* noch nicht benutzt? */ { ds1339RtcOpen++; } else { return (-EBUSY); } } else { return (-EINVAL); } return (SUCCESS); } /* ..end of ds1339_rtc_open. */ /*********************************************************************** * ds1339_rtc_release() Entry-lokal * * Treiber ausloesen (kein Prozess hat ihn jetzt noch geoeffnet). * * Wird erst aufgerufen, nachdem zu jedem open auch ein close * * aufgerufen wurde, also erst beim letzten close. * * Parameter: inode - Zeiger auf die inode-Struktur des Geraets. * * file - Zeiger auf die file-Struktur des Geraets. * * Return: 0 - SUCCESS, fuer OK. * ***********************************************************************/ static int ds1339_rtc_release (struct inode *inode, struct file *filp) { int minor; minor = MINOR (inode->i_rdev); ds1339RtcOpen--; return (SUCCESS); } /* ..end of ds1339_rtc_open. */ /** * ds1339_attach - * @adap: * @addr: * @flags: * @kind: * */ static int ds1339_attach (struct i2c_adapter *adap, int addr, unsigned short flags, int kind) { unsigned char buf[1], ad[1] = { 0 }; struct i2c_msg msgs[2] = { {addr, 0, 1, ad}, {addr, I2C_M_RD, 1, buf} }; realTimeClock = kmalloc (sizeof (struct i2c_client), GFP_KERNEL); if (!realTimeClock) return -ENOMEM; strcpy (realTimeClock->name, "DS1339"); realTimeClock->id = ds1339_driver.id; realTimeClock->flags = 0; realTimeClock->addr = addr; realTimeClock->adapter = adap; realTimeClock->driver = &ds1339_driver; realTimeClock->data = NULL; if (i2c_transfer (realTimeClock->adapter, msgs, 2) == 2) DAT (realTimeClock) = buf[0]; return i2c_attach_client (realTimeClock); } /** * ds1339_probe - ???????????????? * @adap: * */ static int ds1339_probe (struct i2c_adapter *adap) { return i2c_probe (adap, &addr_data, ds1339_attach); } /** * ds1339_detach - ???????????????? * @client: * * Frees claimed recources */ static int ds1339_detach (struct i2c_client *client) { i2c_detach_client (client); kfree (client); return (0); } /** * ds1339_command - * @client: * @cmd: * @arg: * */ static int ds1339_command (struct i2c_client *client, unsigned int cmd, void *arg) { switch (cmd) { case RTC_GETDATETIME: return ds1339_get_datetime (client, arg); case RTC_SETTIME: return ds1339_set_datetime (client, arg, 0); case RTC_SETDATETIME: return ds1339_set_datetime (client, arg, 1); // case RTC_GETCTRL: // return pcf8583_get_ctrl(client, arg); // case RTC_SETCTRL: // return pcf8583_set_ctrl(client, arg); default: return -EINVAL; } } /* ------------------------------------------------------------ */ /** * get_rtc_time - Read the time and converts the chip register content into a readable format * @rtcTime: Pointer to the world format * */ static void get_rtc_time (struct rtc_time *rtcTime) { unsigned char buffer[8] = { 0, }; unsigned char addr[1] = { 0 }; /* read from offset 0 */ int ret = -EIO; struct i2c_msg msgs[2] = { { flags:0, len:1, buf:addr}, { flags:I2C_M_RD, len:7, /* read 7 bytes. Than we have the whole information we need */ buf:buffer} }; if (needsRegistering) register_rtc_driver (); msgs[0].addr = realTimeClock->addr; msgs[1].addr = realTimeClock->addr; ret = i2c_transfer (realTimeClock->adapter, msgs, 2); /* * Format in the chip: * Register 0: [7] always 0 * [6..4] 10 seconds * [3..0] seconds * Register 1: [7] always 0 * [6..4] 10 minutes * [3..0] minutes * Register 2: [7] always 0 * [6] 1= 24, 0=12 * [5] 10 hours (if [6]=1), AM/PM if [6]=0) * [4] 10 hours * [3..0] hours * Register 3: [7..3] always 0 * [2..0] day of week * Register 4: [7..6] always 0 * [5..4] 10 day of month * [3..0] day of month * Register 5: [7] century * [6..5] always 0 * [4] 10 month * [3..0] month * Register 6: [7..4] 10 year * [3..0] year */ rtcTime->tm_sec = buffer[0] & 0x7F; BCD_TO_BIN (rtcTime->tm_sec); rtcTime->tm_min = buffer[1] & 0x7F; BCD_TO_BIN (rtcTime->tm_min); rtcTime->tm_hour = buffer[2] & 0x3F; /* FIXME: AM/PM flag? */ BCD_TO_BIN (rtcTime->tm_hour); rtcTime->tm_mday = buffer[4] & 0x3F; BCD_TO_BIN (rtcTime->tm_mday); rtcTime->tm_mon = buffer[5] & 0x1F; BCD_TO_BIN (rtcTime->tm_mon); rtcTime->tm_wday = buffer[3] & 0x07; rtcTime->tm_year = buffer[6]; BCD_TO_BIN (rtcTime->tm_year); /* year without century! */ rtcTime->tm_year += buffer[5] & 0x80 ? 2000 : 1900; } /* * Bits in rtc_status. (6 bits of room for future expansion) */ #define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ #define RTC_TIMER_ON 0x02 /* missed irq timer active */ /* * Now all the various file operations that we export. */ /* -------------- file operations ----------------------------- */ /** * rtc_read - Read ... (what?) * @file: * @buf: * @count: * @ppos: * */ static ssize_t rtc_read (struct file *file, char *buf, size_t count, loff_t * ppos) { #if 0 # if !RTC_IRQ return -EIO; # else DECLARE_WAITQUEUE (wait, current); unsigned long data; ssize_t retval; if (rtc_has_irq == 0) return -EIO; if (count < sizeof (unsigned long)) return -EINVAL; add_wait_queue (&rtc_wait, &wait); current->state = TASK_INTERRUPTIBLE; do { /* First make it right. Then make it fast. Putting this whole * block within the parentheses of a while would be too * confusing. And no, xchg() is not the answer. */ spin_lock_irq (&rtc_lock); data = rtc_irq_data; rtc_irq_data = 0; spin_unlock_irq (&rtc_lock); if (data != 0) break; if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; goto out; } if (signal_pending (current)) { retval = -ERESTARTSYS; goto out; } schedule (); } while (1); retval = put_user (data, (unsigned long *) buf); if (!retval) retval = sizeof (unsigned long); out: current->state = TASK_RUNNING; remove_wait_queue (&rtc_wait, &wait); return retval; # endif #else return -EIO; #endif } /** * rtc_ioctl - do special IOCTL on th RTC * @inode: * @file: * @cmd: * @arg: * */ static int rtc_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct rtc_time wtime; if (needsRegistering) register_rtc_driver (); switch (cmd) { case RTC_ALM_READ: /* Read the present alarm time */ /* * This returns a struct rtc_time. Reading >= 0xc0 * means "don't care" or "match all". Only the tm_hour, * tm_min, and tm_sec values are filled in. */ printk ("RTC_ALARM_READ not implemented yet!\n"); /* get_rtc_alm_time(&wtime); */ return -ENOTTY; case RTC_ALM_SET: /* Store a time into the alarm */ /* * This expects a struct rtc_time. Writing 0xff means * "don't care" or "match all". Only the tm_hour, * tm_min and tm_sec are used. */ printk ("RTC_ALARM_SET not implemented yet!\n"); return -ENOTTY; #if 0 { unsigned char hrs, min, sec; struct rtc_time alm_tm; if (copy_from_user (&alm_tm, (struct rtc_time *) arg, sizeof (struct rtc_time))) return -EFAULT; hrs = alm_tm.tm_hour; min = alm_tm.tm_min; sec = alm_tm.tm_sec; spin_lock_irq (&rtc_lock); if (!(CMOS_READ (RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { if (sec < 60) BIN_TO_BCD (sec); else sec = 0xff; if (min < 60) BIN_TO_BCD (min); else min = 0xff; if (hrs < 24) BIN_TO_BCD (hrs); else hrs = 0xff; } CMOS_WRITE (hrs, RTC_HOURS_ALARM); CMOS_WRITE (min, RTC_MINUTES_ALARM); CMOS_WRITE (sec, RTC_SECONDS_ALARM); spin_unlock_irq (&rtc_lock); return 0; } #endif case RTC_RD_TIME: /* Read the time/date from RTC */ get_rtc_time (&wtime); break; case RTC_SET_TIME: /* Set the RTC */ printk ("RTC_RD_TIME not implemented yet!\n"); /* set_rtc_time(&wtime); */ return -ENOTTY; #if 0 { struct rtc_time rtc_tm; unsigned char mon, day, hrs, min, sec, leap_yr; unsigned char save_control, save_freq_select; unsigned int yrs; # ifdef CONFIG_DECSTATION unsigned int real_yrs; # endif if (!capable (CAP_SYS_TIME)) return -EACCES; if (copy_from_user (&rtc_tm, (struct rtc_time *) arg, sizeof (struct rtc_time))) return -EFAULT; yrs = rtc_tm.tm_year + 1900; mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ day = rtc_tm.tm_mday; hrs = rtc_tm.tm_hour; min = rtc_tm.tm_min; sec = rtc_tm.tm_sec; if (yrs < 1970) return -EINVAL; leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); if ((mon > 12) || (day == 0)) return -EINVAL; if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) return -EINVAL; if ((hrs >= 24) || (min >= 60) || (sec >= 60)) return -EINVAL; if ((yrs -= epoch) > 255) /* They are unsigned */ return -EINVAL; spin_lock_irq (&rtc_lock); # ifdef CONFIG_DECSTATION real_yrs = yrs; yrs = 72; /* * We want to keep the year set to 73 until March * for non-leap years, so that Feb, 29th is handled * correctly. */ if (!leap_yr && mon < 3) { real_yrs--; yrs = 73; } # endif /* These limits and adjustments are independant of * whether the chip is in binary mode or not. */ if (yrs > 169) { spin_unlock_irq (&rtc_lock); return -EINVAL; } if (yrs >= 100) yrs -= 100; if (!(CMOS_READ (RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BIN_TO_BCD (sec); BIN_TO_BCD (min); BIN_TO_BCD (hrs); BIN_TO_BCD (day); BIN_TO_BCD (mon); BIN_TO_BCD (yrs); } save_control = CMOS_READ (RTC_CONTROL); CMOS_WRITE ((save_control | RTC_SET), RTC_CONTROL); save_freq_select = CMOS_READ (RTC_FREQ_SELECT); CMOS_WRITE ((save_freq_select | RTC_DIV_RESET2), RTC_FREQ_SELECT); # ifdef CONFIG_DECSTATION CMOS_WRITE (real_yrs, RTC_DEC_YEAR); # endif CMOS_WRITE (yrs, RTC_YEAR); CMOS_WRITE (mon, RTC_MONTH); CMOS_WRITE (day, RTC_DAY_OF_MONTH); CMOS_WRITE (hrs, RTC_HOURS); CMOS_WRITE (min, RTC_MINUTES); CMOS_WRITE (sec, RTC_SECONDS); CMOS_WRITE (save_control, RTC_CONTROL); CMOS_WRITE (save_freq_select, RTC_FREQ_SELECT); spin_unlock_irq (&rtc_lock); return 0; } #endif case RTC_EPOCH_READ: /* Read the epoch. */ printk ("RTC_EPOCH_READ not implemented yet!\n"); /* return put_user (epoch, (unsigned long *)arg); */ return -ENOTTY; case RTC_EPOCH_SET: /* Set the epoch. */ printk ("RTC_EPOCH_SET not implemented yet!\n"); /* return put_user (epoch, (unsigned long *)arg); */ return -ENOTTY; default: return -ENOTTY; } return copy_to_user ((void *) arg, &wtime, sizeof wtime) ? -EFAULT : 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) { #if 0 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); #endif return -EBUSY; } /** * rtc_release - * @inode: * @file: * */ static int rtc_release (struct inode *inode, struct file *file) { #if 0 spin_lock_irq (&rtc_lock); rtc_irq_data = 0; spin_unlock_irq (&rtc_lock); /* No need for locking -- nobody else can do anything until this rmw is * committed, and no timer is running. */ rtc_status &= ~RTC_IS_OPEN; #endif return 0; } /* * The various file operations we support. */ static struct file_operations rtc_fops = { owner:THIS_MODULE, llseek:no_llseek, read:rtc_read, ioctl:rtc_ioctl, open:rtc_open, release:rtc_release, }; static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops }; /** * register_rtc_driver - registers the driver at the I2C subsystem * */ static int register_rtc_driver (void) { i2c_add_driver (&ds1339_driver); needsRegistering = 0; return (0); } /** * deregister_rtc_driver - registers the driver at the I2C subsystem * */ static int deregister_rtc_driver (void) { i2c_del_driver (&ds1339_driver); needsRegistering = 1; return (0); } /** * solidcard3_rtc_init - inits the software environment. * * It does not init the chip, because the I2C interface isn't running yet */ static int __init solidcard3_rtc_init (void) { printk (KERN_INFO "DS1339 I2C Real Time Clock Driver version %f\n", RTC_VERSION); needsRegistering = 1; misc_register (&rtc_dev); create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); return 0; } /** * solidcard3_rtc_exit - clean up all used resources * */ static void __exit solidcard3_rtc_exit (void) { deregister_rtc_driver (); remove_proc_entry ("driver/rtc", NULL); misc_deregister (&rtc_dev); } EXPORT_NO_SYMBOLS; /* * Info exported via "/proc/driver/rtc". */ static int rtc_proc_output (char *buf) { struct rtc_time rtcTime; int ret; get_rtc_time (&rtcTime); ret = sprintf (buf, "%02d%02d%02d%02d%04d.%02d", rtcTime.tm_mon, /* month */ rtcTime.tm_mday, /* day of month */ rtcTime.tm_hour, /* hour of day */ rtcTime.tm_min, /* minutes of hour */ rtcTime.tm_year, /* century and year */ rtcTime.tm_sec); return (ret); #if 0 # 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; 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 #endif } /** * rtc_read_proc - * @page: * @char: * @start: * @off: * @count: * @eof: * @data: * */ 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; } #if 0 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); } } #endif MODULE_AUTHOR ("Juergen Beisert"); MODULE_LICENSE ("GPL"); /* end of rtc_ds1339.c */ /************************************************************************ * 3.Teil - routines - Interner Teil des Treibers * ************************************************************************/ void cleanup_module (void) { solidcard3_rtc_exit (); // set output EINT19 and EINT20 low and switch off pullup LINE_LOW (LINE_EINT19); LINE_LOW (LINE_EINT20); PULLUP_OFF (LINE_EINT19); PULLUP_OFF (LINE_EINT20); // unregister char device unregister_chrdev (RTC_BUTTON_MAJOR, RTC_BUTTON_DEV_NAME); printk ("*** RTC Support by Rolf Freitag\n"); printk (" driver version %f unloaded\n", RTC_VERSION); return; } // init static int __init int init_module (void) { solidcard3_rtc_init (); return ds1339_rtc_init (); }