--- zzzz-none-000/linux-2.4.17/drivers/acorn/char/i2c.c 2001-03-03 02:38:37.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/drivers/acorn/char/i2c.c 2004-11-24 13:23:55.000000000 +0000 @@ -14,6 +14,9 @@ */ #include #include +#include +#include +#include #include #include @@ -21,15 +24,16 @@ #include #include #include +#include #include "pcf8583.h" -extern unsigned long -mktime(unsigned int year, unsigned int mon, unsigned int day, - unsigned int hour, unsigned int min, unsigned int sec); extern int (*set_rtc)(void); static struct i2c_client *rtc_client; +static const unsigned char days_in_mon[] = + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static unsigned int rtc_epoch = 1900; static inline int rtc_command(int cmd, void *data) { @@ -44,12 +48,10 @@ /* * Read the current RTC time and date, and update xtime. */ -static void get_rtc_time(void) +static void get_rtc_time(struct rtc_tm *rtctm, unsigned int *year) { - unsigned char ctrl; - unsigned char year; - struct rtc_tm rtctm; - struct mem rtcmem = { 0xc0, 1, &year }; + unsigned char ctrl, yr; + struct mem rtcmem = { 0xc0, 1, &yr }; /* * Ensure that the RTC is running. @@ -73,22 +75,50 @@ if (rtc_command(MEM_READ, &rtcmem)) return; - if (rtc_command(RTC_GETDATETIME, &rtctm)) + if (rtc_command(RTC_GETDATETIME, rtctm)) return; - if (year < 70) - year += 100; + if (yr < 70) + yr += 100; - xtime.tv_usec = rtctm.cs * 10000; - xtime.tv_sec = mktime(1900 + year, rtctm.mon, rtctm.mday, - rtctm.hours, rtctm.mins, rtctm.secs); + *year = yr + rtc_epoch; +} + +static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year) +{ + unsigned char yr, leap; + struct mem rtcmem = { 0xc0, 1, &yr }; + int ret; + + if (year < 1970) + return -EINVAL; + + leap = (!(year % 4) && (year % 100)) || !(year % 400); + + if (rtctm->mon > 12 || rtctm->mday == 0) + return -EINVAL; + + if (rtctm->mday > (days_in_mon[rtctm->mon] + (rtctm->mon == 2 && leap))) + return -EINVAL; + + if (rtctm->hours >= 24 || rtctm->mins >= 60 || rtctm->secs >= 60) + return -EINVAL; + + ret = rtc_command(RTC_SETDATETIME, rtctm); + if (ret == 0) { + yr = year - rtc_epoch; + if (yr > 100) + yr -= 100; + ret = rtc_command(MEM_WRITE, &rtcmem); + } + return ret; } /* * Set the RTC time only. Note that * we do not touch the date. */ -static int set_rtc_time(void) +static int k_set_rtc_time(void) { struct rtc_tm new_rtctm, old_rtctm; unsigned long nowtime = xtime.tv_sec; @@ -110,13 +140,70 @@ * [ rtc: 1/1/2000 23:58:00, real 2/1/2000 00:01:00, * rtc gets set to 1/1/2000 00:01:00 ] */ - if ((old_rtctm.hours == 23 && old_rtctm.mins == 59) || - (new_rtctm.hours == 23 && new_rtctm.mins == 59)) + if ((old_rtctm.hours == 23 && old_rtctm.mins == 59) || + (new_rtctm.hours == 23 && new_rtctm.mins == 59)) return 1; return rtc_command(RTC_SETTIME, &new_rtctm); } +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int year; + struct rtc_time rtctm; + struct rtc_tm rtc_raw; + + switch (cmd) { + case RTC_ALM_READ: + case RTC_ALM_SET: + break; + + case RTC_RD_TIME: + get_rtc_time(&rtc_raw, &year); + rtctm.tm_sec = rtc_raw.secs; + rtctm.tm_min = rtc_raw.mins; + rtctm.tm_hour = rtc_raw.hours; + rtctm.tm_mday = rtc_raw.mday; + rtctm.tm_mon = rtc_raw.mon - 1; /* month starts at 0 */ + rtctm.tm_year = year - 1900; /* starts at 1900 */ + return copy_to_user((void *)arg, &rtctm, sizeof(rtctm)) + ? -EFAULT : 0; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtctm, (void *)arg, sizeof(rtctm))) + return -EFAULT; + rtc_raw.secs = rtctm.tm_sec; + rtc_raw.mins = rtctm.tm_min; + rtc_raw.hours = rtctm.tm_hour; + rtc_raw.mday = rtctm.tm_mday; + rtc_raw.mon = rtctm.tm_mon + 1; + rtc_raw.year_off = 0; + year = rtctm.tm_year + 1900; + return set_rtc_time(&rtc_raw, year); + break; + + case RTC_EPOCH_READ: + return put_user(rtc_epoch, (unsigned long *)arg); + + } + return -EINVAL; +} + +static struct file_operations rtc_fops = { + ioctl: rtc_ioctl, +}; + +static struct miscdevice rtc_dev = { + minor: RTC_MINOR, + name: "rtc", + fops: &rtc_fops, +}; + +/* IOC / IOMD i2c driver */ #define FORCE_ONES 0xdc #define SCL 0x02 @@ -184,9 +271,16 @@ { if (client->id == I2C_DRIVERID_PCF8583 && client->addr == 0x50) { + struct rtc_tm rtctm; + unsigned int year; + rtc_client = client; - get_rtc_time(); - set_rtc = set_rtc_time; + get_rtc_time(&rtctm, &year); + + xtime.tv_usec = rtctm.cs * 10000; + xtime.tv_sec = mktime(year, rtctm.mon, rtctm.mday, + rtctm.hours, rtctm.mins, rtctm.secs); + set_rtc = k_set_rtc_time; } return 0; @@ -212,9 +306,16 @@ static int __init i2c_ioc_init(void) { + int ret; + force_ones = FORCE_ONES | SCL | SDA; - return i2c_bit_add_bus(&ioc_ops); + ret = i2c_bit_add_bus(&ioc_ops); + + if (ret >= 0) + misc_register(&rtc_dev); + + return ret; } __initcall(i2c_ioc_init);