2 * I2C client/driver for the ST M41T00 family of i2c rtc chips.
4 * Author: Mark A. Greer <mgreer@mvista.com>
6 * 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under
7 * the terms of the GNU General Public License version 2. This program
8 * is licensed "as is" without any warranty of any kind, whether express
12 * This i2c client/driver wedges between the drivers/char/genrtc.c RTC
13 * interface and the SMBus interface of the i2c subsystem.
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/interrupt.h>
19 #include <linux/i2c.h>
20 #include <linux/rtc.h>
21 #include <linux/bcd.h>
22 #include <linux/workqueue.h>
23 #include <linux/platform_device.h>
24 #include <linux/m41t00.h>
28 static struct i2c_driver m41t00_driver
;
29 static struct i2c_client
*save_client
;
31 static unsigned short ignore
[] = { I2C_CLIENT_END
};
32 static unsigned short normal_addr
[] = { I2C_CLIENT_END
, I2C_CLIENT_END
};
34 static struct i2c_client_address_data addr_data
= {
35 .normal_i2c
= normal_addr
,
40 struct m41t00_chip_info
{
44 u8 sec
; /* Offsets for chip regs */
56 static struct m41t00_chip_info m41t00_chip_info_tbl
[] = {
58 .type
= M41T00_TYPE_M41T00
,
69 .type
= M41T00_TYPE_M41T81
,
83 .type
= M41T00_TYPE_M41T85
,
97 static struct m41t00_chip_info
*m41t00_chip
;
100 m41t00_get_rtc_time(void)
102 s32 sec
, min
, hour
, day
, mon
, year
;
103 s32 sec1
, min1
, hour1
, day1
, mon1
, year1
;
105 u8 buf
[8], msgbuf
[1] = { 0 }; /* offset into rtc's regs */
106 struct i2c_msg msgs
[] = {
108 .addr
= save_client
->addr
,
114 .addr
= save_client
->addr
,
121 sec
= min
= hour
= day
= mon
= year
= 0;
124 if (i2c_transfer(save_client
->adapter
, msgs
, 2) < 0)
134 sec
= buf
[m41t00_chip
->sec
] & 0x7f;
135 min
= buf
[m41t00_chip
->min
] & 0x7f;
136 hour
= buf
[m41t00_chip
->hour
] & 0x3f;
137 day
= buf
[m41t00_chip
->day
] & 0x3f;
138 mon
= buf
[m41t00_chip
->mon
] & 0x1f;
139 year
= buf
[m41t00_chip
->year
];
140 } while ((++reads
< m41t00_chip
->read_limit
) && ((sec
!= sec1
)
141 || (min
!= min1
) || (hour
!= hour1
) || (day
!= day1
)
142 || (mon
!= mon1
) || (year
!= year1
)));
144 if ((m41t00_chip
->read_limit
> 1) && ((sec
!= sec1
) || (min
!= min1
)
145 || (hour
!= hour1
) || (day
!= day1
) || (mon
!= mon1
)
151 hour
= BCD2BIN(hour
);
154 year
= BCD2BIN(year
);
160 return mktime(year
, mon
, day
, hour
, min
, sec
);
163 dev_err(&save_client
->dev
, "m41t00_get_rtc_time: Read error\n");
166 EXPORT_SYMBOL_GPL(m41t00_get_rtc_time
);
169 m41t00_set(void *arg
)
172 int nowtime
= *(int *)arg
;
173 s32 sec
, min
, hour
, day
, mon
, year
;
174 u8 wbuf
[9], *buf
= &wbuf
[1], msgbuf
[1] = { 0 };
175 struct i2c_msg msgs
[] = {
177 .addr
= save_client
->addr
,
183 .addr
= save_client
->addr
,
191 tm
.tm_year
= (tm
.tm_year
- 1900) % 100;
193 sec
= BIN2BCD(tm
.tm_sec
);
194 min
= BIN2BCD(tm
.tm_min
);
195 hour
= BIN2BCD(tm
.tm_hour
);
196 day
= BIN2BCD(tm
.tm_mday
);
197 mon
= BIN2BCD(tm
.tm_mon
);
198 year
= BIN2BCD(tm
.tm_year
);
200 /* Read reg values into buf[0..7]/wbuf[1..8] */
201 if (i2c_transfer(save_client
->adapter
, msgs
, 2) < 0) {
202 dev_err(&save_client
->dev
, "m41t00_set: Read error\n");
206 wbuf
[0] = 0; /* offset into rtc's regs */
207 buf
[m41t00_chip
->sec
] = (buf
[m41t00_chip
->sec
] & ~0x7f) | (sec
& 0x7f);
208 buf
[m41t00_chip
->min
] = (buf
[m41t00_chip
->min
] & ~0x7f) | (min
& 0x7f);
209 buf
[m41t00_chip
->hour
] = (buf
[m41t00_chip
->hour
] & ~0x3f) | (hour
& 0x3f);
210 buf
[m41t00_chip
->day
] = (buf
[m41t00_chip
->day
] & ~0x3f) | (day
& 0x3f);
211 buf
[m41t00_chip
->mon
] = (buf
[m41t00_chip
->mon
] & ~0x1f) | (mon
& 0x1f);
213 if (i2c_master_send(save_client
, wbuf
, 9) < 0)
214 dev_err(&save_client
->dev
, "m41t00_set: Write error\n");
217 static ulong new_time
;
218 static struct workqueue_struct
*m41t00_wq
;
219 static DECLARE_WORK(m41t00_work
, m41t00_set
, &new_time
);
222 m41t00_set_rtc_time(ulong nowtime
)
227 queue_work(m41t00_wq
, &m41t00_work
);
229 m41t00_set(&new_time
);
233 EXPORT_SYMBOL_GPL(m41t00_set_rtc_time
);
236 *****************************************************************************
238 * platform_data Driver Interface
240 *****************************************************************************
243 m41t00_platform_probe(struct platform_device
*pdev
)
245 struct m41t00_platform_data
*pdata
;
248 if (pdev
&& (pdata
= pdev
->dev
.platform_data
)) {
249 normal_addr
[0] = pdata
->i2c_addr
;
251 for (i
=0; i
<ARRAY_SIZE(m41t00_chip_info_tbl
); i
++)
252 if (m41t00_chip_info_tbl
[i
].type
== pdata
->type
) {
253 m41t00_chip
= &m41t00_chip_info_tbl
[i
];
254 m41t00_chip
->sqw_freq
= pdata
->sqw_freq
;
262 m41t00_platform_remove(struct platform_device
*pdev
)
267 static struct platform_driver m41t00_platform_driver
= {
268 .probe
= m41t00_platform_probe
,
269 .remove
= m41t00_platform_remove
,
271 .owner
= THIS_MODULE
,
272 .name
= M41T00_DRV_NAME
,
277 *****************************************************************************
281 *****************************************************************************
284 m41t00_probe(struct i2c_adapter
*adap
, int addr
, int kind
)
286 struct i2c_client
*client
;
289 if (!i2c_check_functionality(adap
, I2C_FUNC_I2C
290 | I2C_FUNC_SMBUS_BYTE_DATA
))
293 client
= kzalloc(sizeof(struct i2c_client
), GFP_KERNEL
);
297 strlcpy(client
->name
, m41t00_chip
->name
, I2C_NAME_SIZE
);
299 client
->adapter
= adap
;
300 client
->driver
= &m41t00_driver
;
302 if ((rc
= i2c_attach_client(client
)))
305 if (m41t00_chip
->type
!= M41T00_TYPE_M41T00
) {
306 /* If asked, disable SQW, set SQW frequency & re-enable */
307 if (m41t00_chip
->sqw_freq
)
308 if (((rc
= i2c_smbus_read_byte_data(client
,
309 m41t00_chip
->alarm_mon
)) < 0)
310 || ((rc
= i2c_smbus_write_byte_data(client
,
311 m41t00_chip
->alarm_mon
, rc
& ~0x40)) <0)
312 || ((rc
= i2c_smbus_write_byte_data(client
,
314 m41t00_chip
->sqw_freq
)) < 0)
315 || ((rc
= i2c_smbus_write_byte_data(client
,
316 m41t00_chip
->alarm_mon
, rc
| 0x40)) <0))
319 /* Make sure HT (Halt Update) bit is cleared */
320 if ((rc
= i2c_smbus_read_byte_data(client
,
321 m41t00_chip
->alarm_hour
)) < 0)
325 if ((rc
= i2c_smbus_write_byte_data(client
,
326 m41t00_chip
->alarm_hour
, rc
& ~0x40))<0)
330 /* Make sure ST (stop) bit is cleared */
331 if ((rc
= i2c_smbus_read_byte_data(client
, m41t00_chip
->sec
)) < 0)
335 if ((rc
= i2c_smbus_write_byte_data(client
, m41t00_chip
->sec
,
339 m41t00_wq
= create_singlethread_workqueue(m41t00_chip
->name
);
340 save_client
= client
;
344 dev_err(&client
->dev
, "m41t00_probe: Can't clear ST bit\n");
347 dev_err(&client
->dev
, "m41t00_probe: Can't clear HT bit\n");
350 dev_err(&client
->dev
, "m41t00_probe: Can't set SQW Frequency\n");
357 m41t00_attach(struct i2c_adapter
*adap
)
359 return i2c_probe(adap
, &addr_data
, m41t00_probe
);
363 m41t00_detach(struct i2c_client
*client
)
367 if ((rc
= i2c_detach_client(client
)) == 0) {
369 destroy_workqueue(m41t00_wq
);
374 static struct i2c_driver m41t00_driver
= {
376 .name
= M41T00_DRV_NAME
,
378 .id
= I2C_DRIVERID_STM41T00
,
379 .attach_adapter
= m41t00_attach
,
380 .detach_client
= m41t00_detach
,
388 if (!(rc
= platform_driver_register(&m41t00_platform_driver
)))
389 rc
= i2c_add_driver(&m41t00_driver
);
396 i2c_del_driver(&m41t00_driver
);
397 platform_driver_unregister(&m41t00_platform_driver
);
400 module_init(m41t00_init
);
401 module_exit(m41t00_exit
);
403 MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
404 MODULE_DESCRIPTION("ST Microelectronics M41T00 RTC I2C Client Driver");
405 MODULE_LICENSE("GPL");