MOXA linux-2.6.x / linux-2.6.19-uc1 from UC-7110-LX-BOOTLOADER-1.9_VERSION-4.2.tgz
[linux-2.6.19-moxart.git] / drivers / i2c / chips / m41t11.c
blob538a97a1eed1705447d0c48e11fe40e547d2c913
1 /*
2 * drivers/i2c/chips/m41t11.c
4 * I2C client driver for the ST M41T11 Real Time Clock chip.
6 * (C) Copyright (C) 2006, Greg Ungerer <gerg@snapgear.com>
7 */
9 /*
10 * This driver is very much a hybrid RTC and I2C driver. It has interfaces
11 * into both sub-systems (well the RTC is really a misc device). Ultimately
12 * I want to be able to use hwclock "as is" on the RTC. But the hardware is
13 * a true I2c device...
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/miscdevice.h>
19 #include <linux/fs.h>
20 #include <linux/i2c.h>
21 #include <linux/bcd.h>
22 #include <linux/rtc.h>
23 #include <asm/uaccess.h>
25 #define M41T11_DRV_NAME "m41t11"
28 * Size of RTC region. 64 bytes total, the first 10 are the RTC.
30 #define M41T11_MSIZE 0x3f
33 * M41T11 register offsets.
35 #define M41T11_SEC 0x00 /* Address of second register */
36 #define M41T11_MIN 0x01 /* Address of minute register */
37 #define M41T11_HOUR 0x02 /* Address of hour register */
38 #define M41T11_WDAY 0x03 /* Address of day of week register */
39 #define M41T11_MDAY 0x04 /* Address of day of month register */
40 #define M41T11_MON 0x05 /* Address of month register */
41 #define M41T11_YEAR 0x06 /* Address of year register */
42 #define M41T11_FTOUT 0x07 /* Address of control register */
44 static DECLARE_MUTEX(m41t11_mutex);
47 * We keep a copy of the client device found, since we are really only
48 * need one device for the real misc device interface.
50 static struct i2c_client *client;
52 #define m41t11_readbyte(a) i2c_smbus_read_byte_data(client, a)
53 #define m41t11_writebyte(a,v) i2c_smbus_write_byte_data(client, a, v);
56 *****************************************************************************
58 * RTC Driver Interface
60 *****************************************************************************
63 static ssize_t m41t11_read(struct file *fp, char __user *buf, size_t count, loff_t *ptr)
65 int total;
67 if (fp->f_pos >= M41T11_MSIZE)
68 return 0;
70 if (count > (M41T11_MSIZE - fp->f_pos))
71 count = M41T11_MSIZE - fp->f_pos;
73 down(&m41t11_mutex);
74 for (total = 0; (total < count); total++)
75 put_user(m41t11_readbyte(fp->f_pos + total), buf++);
76 up(&m41t11_mutex);
78 fp->f_pos += total;
79 return total;
82 static ssize_t m41t11_write(struct file *fp, const char __user *buf, size_t count, loff_t *ptr)
84 int total;
85 char val;
87 if (fp->f_pos >= M41T11_MSIZE)
88 return 0;
90 if (count > (M41T11_MSIZE - fp->f_pos))
91 count = M41T11_MSIZE - fp->f_pos;
93 down(&m41t11_mutex);
94 for (total = 0; (total < count); total++, buf++) {
95 get_user(val,buf);
96 m41t11_writebyte((fp->f_pos + total), val);
98 up(&m41t11_mutex);
100 fp->f_pos += total;
101 return total;
105 * Do some consistency checks on the time. On first power up the
106 * RTC may contain completely bogus junk, this will clean it up.
107 * Just for good measure we do this when writing to the RTC as well.
109 static void m41t11_validatetime(struct rtc_time *rtime)
111 if ((rtime->tm_year < 70) || (rtime->tm_year >= 200))
112 rtime->tm_year = 70;
113 if ((rtime->tm_mon < 0) || (rtime->tm_mon >= 12))
114 rtime->tm_mon = 0;
115 if ((rtime->tm_mday < 1) || (rtime->tm_mday > 31))
116 rtime->tm_mday = 1;
117 if ((rtime->tm_wday < 0) || (rtime->tm_wday >= 7))
118 rtime->tm_wday = 0;
119 if ((rtime->tm_hour < 0) || (rtime->tm_hour >= 24))
120 rtime->tm_hour = 0;
121 if ((rtime->tm_min < 0) || (rtime->tm_min >= 60))
122 rtime->tm_min = 0;
123 if ((rtime->tm_sec < 0) || (rtime->tm_sec >= 60))
124 rtime->tm_sec = 0;
127 static void m41t11_readtime(struct rtc_time *rtime)
129 down(&m41t11_mutex);
130 memset(rtime, 0, sizeof(*rtime));
131 rtime->tm_year = BCD2BIN(m41t11_readbyte(M41T11_YEAR)) +
132 ((m41t11_readbyte(M41T11_HOUR) & 0x40) ? 100 : 0);
133 rtime->tm_mon = BCD2BIN(m41t11_readbyte(M41T11_MON & 0x1f)) - 1;
134 rtime->tm_mday = BCD2BIN(m41t11_readbyte(M41T11_MDAY & 0x3f));
135 rtime->tm_wday = BCD2BIN(m41t11_readbyte(M41T11_WDAY) & 0x7) - 1;
136 rtime->tm_hour = BCD2BIN(m41t11_readbyte(M41T11_HOUR) & 0x3f);
137 rtime->tm_min = BCD2BIN(m41t11_readbyte(M41T11_MIN) & 0x7f);
138 rtime->tm_sec = BCD2BIN(m41t11_readbyte(M41T11_SEC) & 0x7f);
139 up(&m41t11_mutex);
142 static void m41t11_settime(struct rtc_time *rtime)
144 down(&m41t11_mutex);
145 m41t11_writebyte(M41T11_YEAR, BIN2BCD(rtime->tm_year));
146 m41t11_writebyte(M41T11_MON, BIN2BCD(rtime->tm_mon+1));
147 m41t11_writebyte(M41T11_MDAY, BIN2BCD(rtime->tm_mday));
148 m41t11_writebyte(M41T11_WDAY, BIN2BCD(rtime->tm_wday+1));
149 m41t11_writebyte(M41T11_HOUR, BIN2BCD(rtime->tm_hour) |
150 ((rtime->tm_year > 99) ? 0xc0 : 0x80));
151 m41t11_writebyte(M41T11_MIN, BIN2BCD(rtime->tm_min));
152 m41t11_writebyte(M41T11_SEC, BIN2BCD(rtime->tm_sec));
153 m41t11_writebyte(M41T11_FTOUT, 0x90);
154 up(&m41t11_mutex);
157 static int m41t11_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
159 struct rtc_time rtime;
161 switch (cmd) {
163 case RTC_RD_TIME:
164 m41t11_readtime(&rtime);
165 m41t11_validatetime(&rtime);
166 if (copy_to_user((void __user *) arg, &rtime, sizeof(rtime)))
167 return -EFAULT;
168 break;
170 case RTC_SET_TIME:
171 if (!capable(CAP_SYS_TIME))
172 return -EACCES;
173 m41t11_validatetime(&rtime);
174 if (copy_from_user(&rtime, (void __user *) arg, sizeof(rtime)))
175 return -EFAULT;
176 m41t11_settime(&rtime);
177 break;
179 default:
180 return -EINVAL;
183 return 0;
187 *****************************************************************************
189 * I2C Driver Interface
191 *****************************************************************************
194 static unsigned short ignore[] = { I2C_CLIENT_END };
195 static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END };
197 static struct i2c_client_address_data addr_data = {
198 .normal_i2c = normal_addr,
199 .probe = ignore,
200 .ignore = ignore,
203 static struct i2c_driver m41t11_i2cdrv;
205 static int m41t11_probe(struct i2c_adapter *adap, int addr, int kind)
207 struct i2c_client *c;
208 int rc;
209 int val;
211 c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
212 if (!c)
213 return -ENOMEM;
215 strncpy(c->name, M41T11_DRV_NAME, I2C_NAME_SIZE);
216 c->addr = addr;
217 c->adapter = adap;
218 c->driver = &m41t11_i2cdrv;
220 if ((rc = i2c_attach_client(c)) != 0) {
221 kfree(c);
222 return rc;
225 client = c;
227 /* Start the oscillator if needed */
228 val = m41t11_readbyte(M41T11_SEC);
229 if (val & 0x80)
230 m41t11_writebyte(M41T11_SEC, val & 0x7f);
232 return 0;
235 static int m41t11_attach(struct i2c_adapter *adap)
237 return i2c_probe(adap, &addr_data, m41t11_probe);
240 static int m41t11_detach(struct i2c_client *c)
242 int rc;
244 if ((rc = i2c_detach_client(c)) < 0)
245 return rc;
246 kfree(c);
247 return 0;
251 *****************************************************************************
253 * Driver Interface
255 *****************************************************************************
258 static struct i2c_driver m41t11_i2cdrv = {
259 .driver = {
260 .name = M41T11_DRV_NAME,
262 .id = I2C_DRIVERID_STM41T00,
263 .attach_adapter = m41t11_attach,
264 .detach_client = m41t11_detach,
267 static struct file_operations m41t11_fops = {
268 .owner = THIS_MODULE,
269 .read = m41t11_read,
270 .write = m41t11_write,
271 .ioctl = m41t11_ioctl,
274 static struct miscdevice m41t11_miscdrv = {
275 .minor = RTC_MINOR,
276 .name = "rtc",
277 .fops = &m41t11_fops,
280 static int __init m41t11_init(void)
282 int rc;
284 if ((rc = i2c_add_driver(&m41t11_i2cdrv)) < 0)
285 return rc;
286 if ((rc = misc_register(&m41t11_miscdrv)) < 0) {
287 i2c_del_driver(&m41t11_i2cdrv);
288 return rc;
291 printk("M41T11: RTC I2C driver registered\n");
292 return 0;
295 static void __exit m41t11_exit(void)
297 misc_deregister(&m41t11_miscdrv);
298 i2c_del_driver(&m41t11_i2cdrv);
299 return;
302 module_init(m41t11_init);
303 module_exit(m41t11_exit);
305 MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
306 MODULE_DESCRIPTION("ST Microelectronics M41T11 RTC I2C Client Driver");
307 MODULE_LICENSE("GPL");