lkml.org 
[lkml]   [2005]   [May]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Subjectrs5c372 driver patch
From
Date
Hi,
If some one need this...
www.lannerinc.com and someone else
are producing NAS with rs5c372 RTC.
It is impossible run normal linux hw without rtc :)
I ll be glad for any responce....
--
Best regards,
Pavel S. Mironchik

--- lanner-2.6.10/drivers/char/Kconfig.orig 2005-05-25 19:38:00 +0400
+++ lanner-2.6.10/drivers/char/Kconfig 2005-05-25 19:38:56 +0400
@@ -809,6 +809,10 @@
tristate "ST M41ST85W Real Time Clock (Intel IOP Platform)"
depends on ARCH_IQ31244 && I2C

+config RS5C372_RTC
+ tristate "RS5C372 Real Time Clock"
+ depends on I2C
+
config PIC16F8X_PIC
tristate "Microchip PIC16F8X PIC Controller support (Intel IOP Platform)"
depends on ARCH_EP80219 && I2C
--- lanner-2.6.10/drivers/char/Makefile.orig 2005-05-25 19:36:44 +0400
+++ lanner-2.6.10/drivers/char/Makefile 2005-05-25 19:37:11 +0400
@@ -66,6 +66,7 @@
obj-$(CONFIG_DS1302) += ds1302.o
obj-$(CONFIG_S3C2410_RTC) += s3c2410-rtc.o
obj-$(CONFIG_M41ST85W_RTC) += m41st85w.o
+obj-$(CONFIG_RS5C372_RTC) += rs5c372.o
obj-$(CONFIG_PIC16F8X_PIC) += pic16f8x.o
ifeq ($(CONFIG_GENERIC_NVRAM),y)
obj-$(CONFIG_NVRAM) += generic_nvram.o
--- /dev/null 2005-05-25 17:30:46 +0400
+++ lanner-2.6.10/drivers/char/rs5c372.c 2005-05-25 20:02:10 +0400
@@ -0,0 +1,491 @@
+/*
+ * rs5c372.c
+ *
+ * Device driver for Real Time Controller's rs5c372 chips
+ *
+ * Copyright (C) 2005 Pavel Mironchik pmironchik@optifacio.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This driver is adapted from the m41st85w driver
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/string.h>
+#include <linux/miscdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+
+
+//define DEBUG 0
+
+#define PROC_RS5C372_NAME "driver/rs5c372_reset"
+#define RS5C372_I2C_SLAVE_ADDR 0x32
+#define RS5C372_RAM_ADDR_START 0x00
+#define RS5C372_RAM_ADDR_END 0x0F
+#define RS5C372_RAM_SIZE 0x10
+#define I2C_DRIVERID_RS5C372 0xF6
+
+#define RS5C372_GETDATETIME 0
+#define RS5C372_SETTIME 1
+#define RS5C372_SETDATETIME 2
+
+#define RTC_SECONDS 0
+#define RTC_MINUTES 1
+#define RTC_HOURS 2
+#define RTC_WEEKDAYS 3
+#define RTC_DAYS 4
+#define RTC_MONTHS 5
+#define RTC_YEARS 6
+#define RTC_TIMETRIMM 7
+
+// ALARM A registers
+#define RTC_AM 8
+#define RTC_AH 9
+#define RTC_AW 10
+
+// ALARM B registers
+#define RTC_BM 11
+#define RTC_BH 12
+#define RTC_BW 13
+
+// Control registers
+#define RTC_CR1 14
+#define RTC_CR2 15
+
+#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
+#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
+#define HOURS_24(n) BCD_TO_BIN((n)&0x3F)
+
+static unsigned short slave_address = RS5C372_I2C_SLAVE_ADDR;
+
+struct i2c_driver rs5c372_driver;
+struct i2c_client *rs5c372_i2c_client = NULL;
+extern int (*set_rtc) (void);
+
+static unsigned short ignore[] = { I2C_CLIENT_END };
+static unsigned short normal_addr[] = { RS5C372_I2C_SLAVE_ADDR, 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 int rs5c372_rtc_ioctl(struct inode *, struct file *, unsigned int,
+ unsigned long);
+static int rs5c372_rtc_open(struct inode *inode, struct file *file);
+static int rs5c372_rtc_release(struct inode *inode, struct file *file);
+static int rs5c372_k_set_rtc_time(void);
+
+static struct file_operations rtc_fops = {
+ owner:THIS_MODULE,
+ ioctl:rs5c372_rtc_ioctl,
+ open:rs5c372_rtc_open,
+ release:rs5c372_rtc_release,
+};
+
+
+static struct miscdevice rs5c372_rtc_miscdev = {
+ RTC_MINOR,
+ "rtc",
+ &rtc_fops
+};
+
+static int rs5c372_probe(struct i2c_adapter *adap);
+static int rs5c372_detach(struct i2c_client *client);
+static int rs5c372_command(struct i2c_client *client, unsigned int cmd,
+ void *arg);
+struct i2c_driver rs5c372_driver = {
+ name:"rs5c372",
+ id:I2C_DRIVERID_RS5C372,
+ flags:I2C_DF_NOTIFY,
+ attach_adapter:rs5c372_probe,
+ detach_client:rs5c372_detach,
+ command:rs5c372_command
+};
+
+extern int (*set_rtc) (void);
+
+static spinlock_t rs5c372_rtc_lock = SPIN_LOCK_UNLOCKED;
+
+static int rs5c372_read(char *buf, int len)
+{
+ unsigned long flags;
+ struct i2c_msg msgs[1] = {
+ {rs5c372_i2c_client->addr, I2C_M_RD, len, buf}
+ };
+ int ret;
+
+ spin_lock_irqsave(&rs5c372_rtc_lock, flags);
+ ret = i2c_transfer(rs5c372_i2c_client->adapter, msgs, 1);
+ spin_unlock_irqrestore(&rs5c372_rtc_lock, flags);
+ return ret;
+}
+
+static int rs5c372_write(char *buf, int len)
+{
+ // warning refer of the format of the first byte
+ // in rs5c372a-e datasheet
+
+ unsigned long flags;
+ struct i2c_msg msgs[1] = {
+ {rs5c372_i2c_client->addr, 0, len, buf}
+ };
+ int ret;
+
+ spin_lock_irqsave(&rs5c372_rtc_lock, flags);
+ ret = i2c_transfer(rs5c372_i2c_client->adapter, msgs, 1);
+ spin_unlock_irqrestore(&rs5c372_rtc_lock, flags);
+ return ret;
+}
+
+static void rs5c372_setdefaults(char *buf)
+{
+ buf[0]=0x00;
+ buf[4]=0x01;
+ buf[5]=0x01;
+ buf[6]=0x01;
+ buf[7]=0x01;
+
+ buf[0xF]=0x00;
+ buf[0x10]= 0x20; // 24 hour format ADJ=0
+}
+
+static void rs5c372_dump(void)
+{
+ unsigned char buf[RS5C372_RAM_SIZE+1];
+ int ret;
+ struct i2c_msg msgs[] = {
+ {rs5c372_i2c_client->addr, 1,RS5C372_RAM_SIZE+1, buf}
+ };
+
+ buf[0]=0x00;
+ ret = i2c_transfer(rs5c372_i2c_client->adapter, msgs, 1);
+ if (ret > 0) {
+ int i;
+ for(i=0;i<2*RS5C372_RAM_SIZE;i++){
+ printk("%02X ", buf[i]);
+ }
+ printk("RS5C372 RTclock\n");
+ printk("Seconds: %d ",BCD_TO_BIN(buf[1]));
+ printk("Minutes: %d ",BCD_TO_BIN(buf[2]));
+ printk("Hours: %d ",BCD_TO_BIN(buf[3]& 0x1F));
+ printk("Weekdays: %d ",BCD_TO_BIN(buf[4]));
+ printk("Days: %d ",BCD_TO_BIN(buf[5]));
+ printk("Months: %d ",BCD_TO_BIN(buf[6]));
+ printk("Years: %d\n",BCD_TO_BIN(buf[7]));
+ }
+}
+
+static int rs5c372_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ int ret = 0;
+ struct i2c_client *c;
+ struct rtc_time rtctm;
+
+ if (!(c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ memset(c, 0, sizeof(struct i2c_client));
+ strncpy(c->name, "rs5c372", I2C_NAME_SIZE);
+
+ c->id = rs5c372_driver.id;
+ c->flags = I2C_CLIENT_ALLOW_USE | I2C_DF_NOTIFY;
+ c->addr = addr;
+ c->adapter = adap;
+ c->driver = &rs5c372_driver;
+
+ ret = i2c_attach_client(c);
+ rs5c372_i2c_client = c;
+ rs5c372_command(rs5c372_i2c_client, RS5C372_GETDATETIME,
+ (void *)&rtctm);
+
+ xtime.tv_nsec = 0;
+ xtime.tv_sec = mktime(1900 + rtctm.tm_year, rtctm.tm_mon + 1,
+ rtctm.tm_mday, rtctm.tm_hour, rtctm.tm_min, rtctm.tm_sec);
+
+ /* Fix for Uptime */
+ wall_to_monotonic.tv_sec = -xtime.tv_sec;
+ wall_to_monotonic.tv_nsec = -xtime.tv_nsec;
+
+ set_rtc = rs5c372_k_set_rtc_time;
+
+ if (ret < 0) {
+ printk(KERN_ERR "rs5c372: Attach was not ok!\n");
+ kfree(c);
+ }
+ return ret;
+}
+
+static int rs5c372_probe(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, rs5c372_attach);
+}
+
+static int rs5c372_detach(struct i2c_client *client)
+{
+ if (client) {
+ i2c_detach_client(client);
+ kfree(client);
+ client = rs5c372_i2c_client = NULL;
+ }
+ return 0;
+}
+
+
+static void rs5c372_convert_to_time(struct rtc_time *dt, char *buf)
+{
+ dt->tm_sec = BCD_TO_BIN(buf[1] & 0x7f);
+ dt->tm_min = BCD_TO_BIN(buf[2] & 0x7f);
+
+ /* 24 hour mode only */
+ dt->tm_hour = HOURS_24(buf[3]);
+
+ dt->tm_wday = BCD_TO_BIN(buf[4] & 0x07);
+ dt->tm_mday = BCD_TO_BIN(buf[5] & 0x3f);
+
+ /* dt->tm_mon is zero-based */
+ dt->tm_mon = BCD_TO_BIN(buf[6] & 0x1f) - 1;
+ /* year is 1900 + dt->tm_year */
+ dt->tm_year = BCD_TO_BIN(buf[7])+100;
+
+#ifdef DEBUG
+ printk("rs5c372_get_datetime: year = %d\n", dt->tm_year);
+ printk("rs5c372_get_datetime: mon = %d\n", dt->tm_mon);
+ printk("rs5c372_get_datetime: mday = %d\n", dt->tm_mday);
+ printk("rs5c372_get_datetime: hour = %d\n", dt->tm_hour);
+ printk("rs5c372_get_datetime: min = %d\n", dt->tm_min);
+ printk("rs5c372_get_datetime: sec = %d\n", dt->tm_sec);
+#endif
+}
+
+static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *dt)
+{
+ unsigned char buf[RS5C372_RAM_SIZE+1];
+ int ret = -EIO;
+
+ ret = rs5c372_read(buf,RS5C372_RAM_SIZE+1);
+ if (ret >= 0) {
+ rs5c372_convert_to_time(dt, buf);
+ ret = 0;
+ } else
+ printk("rs5c372_get_datetime(), i2c_transfer() returned %d\n",
+ ret);
+ return ret;
+}
+
+
+static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *dt,int datetoo)
+{
+ unsigned char buf[RS5C372_RAM_SIZE+1];
+ int ret, len = 4;
+
+#ifdef DEBUG
+ printk("rs5c372_set_datetime: tm_year = %d\n", dt->tm_year);
+ printk("rs5c372_set_datetime: tm_mon = %d\n", dt->tm_mon);
+ printk("rs5c372_set_datetime: tm_mday = %d\n", dt->tm_mday);
+ printk("rs5c372_set_datetime: tm_hour = %d\n", dt->tm_hour);
+ printk("rs5c372_set_datetime: tm_min = %d\n", dt->tm_min);
+ printk("rs5c372_set_datetime: tm_sec = %d\n", dt->tm_sec);
+#endif
+
+ buf[0] = 0;
+ buf[1] = (BIN_TO_BCD(dt->tm_sec));
+ buf[2] = (BIN_TO_BCD(dt->tm_min));
+ buf[3] = (BIN_TO_BCD(dt->tm_hour));
+
+ if (datetoo) {
+ len = 8;
+ buf[4] = (BIN_TO_BCD(dt->tm_wday));
+ buf[5] = (BIN_TO_BCD(dt->tm_mday));
+ buf[6] = (BIN_TO_BCD(dt->tm_mon + 1));
+ buf[7] = (BIN_TO_BCD(dt->tm_year - 100));
+ }
+
+ ret = rs5c372_write(buf,len);
+ if (ret >= 0)
+ ret = 0;
+ else
+ printk
+ ("rs5c372_set_datetime(), i2c_master_send() returned %d\n",
+ ret);
+ return ret;
+}
+
+static int rs5c372_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case RS5C372_GETDATETIME:
+ return rs5c372_get_datetime(client, arg);
+ case RS5C372_SETTIME:
+ return rs5c372_set_datetime(client, arg, 0);
+ case RS5C372_SETDATETIME:
+ return rs5c372_set_datetime(client, arg, 1);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int rs5c372_rtc_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+
+static int rs5c372_rtc_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int rs5c372_rtc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned long flags;
+ struct rtc_time wtime;
+ int status = 0;
+
+ switch (cmd) {
+ default:
+ case RTC_UIE_ON:
+ case RTC_UIE_OFF:
+ case RTC_PIE_ON:
+ case RTC_PIE_OFF:
+ case RTC_AIE_ON:
+ case RTC_AIE_OFF:
+ case RTC_ALM_SET:
+ case RTC_ALM_READ:
+ case RTC_IRQP_READ:
+ case RTC_IRQP_SET:
+ case RTC_EPOCH_SET:
+ case RTC_WKALM_SET:
+ case RTC_WKALM_RD:
+ status = -EINVAL;
+ break;
+ case RTC_EPOCH_READ:
+ return put_user(1970, (unsigned long *)arg);
+ case RTC_RD_TIME:
+ spin_lock_irqsave(&rs5c372_rtc_lock, flags);
+ rs5c372_command(rs5c372_i2c_client, RS5C372_GETDATETIME,
+ &wtime);
+ spin_unlock_irqrestore(&rs5c372_rtc_lock, flags);
+ if (copy_to_user((void *)arg, &wtime, sizeof(struct rtc_time)))
+ status = -EFAULT;
+ break;
+
+ case RTC_SET_TIME:
+ if (!capable(CAP_SYS_TIME)) {
+ status = -EACCES;
+ break;
+ }
+
+ if (copy_from_user
+ (&wtime, (struct rtc_time *)arg, sizeof(struct rtc_time))) {
+ status = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&rs5c372_rtc_lock, flags);
+ rs5c372_command(rs5c372_i2c_client, RS5C372_SETDATETIME,
+ &wtime);
+ spin_unlock_irqrestore(&rs5c372_rtc_lock, flags);
+ break;
+ }
+
+ return status;
+}
+
+static int rs5c372_k_set_rtc_time(void)
+{
+ struct rtc_time new_rtctm, old_rtctm;
+ unsigned long nowtime = xtime.tv_sec;
+
+ if (rs5c372_command
+ (rs5c372_i2c_client, RS5C372_GETDATETIME, &old_rtctm))
+ return 0;
+
+ new_rtctm.tm_sec = nowtime % 60;
+ nowtime /= 60;
+ new_rtctm.tm_min = nowtime % 60;
+ nowtime /= 60;
+ new_rtctm.tm_hour = nowtime % 24;
+
+ if ((old_rtctm.tm_hour == 23 && old_rtctm.tm_min == 59) ||
+ (new_rtctm.tm_hour == 23 && new_rtctm.tm_min == 59))
+ return 1;
+
+ return rs5c372_command(rs5c372_i2c_client, RS5C372_SETTIME,
+ &new_rtctm);
+}
+
+
+static int rs5c372_rtc_write_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ // Perfoming reset to defaults
+ int len = 0;
+ unsigned char buf[RS5C372_RAM_SIZE+1];
+ memset(buf,0,RS5C372_RAM_SIZE+1);
+ rs5c372_setdefaults(buf);
+ rs5c372_write(buf,RS5C372_RAM_SIZE+1);
+ rs5c372_dump();
+ return len;
+}
+
+
+static __init int rs5c372_init(void)
+{
+ int retval = 0;
+ struct proc_dir_entry *res=NULL;
+
+
+ normal_addr[0] = slave_address;
+ retval = i2c_add_driver(&rs5c372_driver);
+ if (retval == 0) {
+ res=create_proc_entry(PROC_RS5C372_NAME, 0, 0);
+ if (res) {
+ res->write_proc=rs5c372_rtc_write_proc;
+ res->data=NULL;
+ }
+ misc_register(&rs5c372_rtc_miscdev);
+ printk("I2C: rs5c372 RTC driver successfully loaded\n");
+// rs5c372_dump();
+ }
+
+ return retval;
+}
+
+static __exit void rs5c372_exit(void)
+{
+ remove_proc_entry(PROC_RS5C372_NAME, NULL);
+ misc_deregister(&rs5c372_rtc_miscdev);
+ i2c_del_driver(&rs5c372_driver);
+
+}
+
+module_init(rs5c372_init);
+module_exit(rs5c372_exit);
+
+
+MODULE_PARM(slave_address, "i");
+MODULE_PARM_DESC(slave_address, "I2C slave address for RS5C372 RTC.");
+
+MODULE_AUTHOR("Pavel Mironchik pmironchik@optifacio.net");
+MODULE_LICENSE("GPL");
\
 
 \ /
  Last update: 2005-05-25 19:10    [W:0.404 / U:0.008 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site