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);
212 buf
[m41t00_chip
->year
] = year
;
214 if (i2c_master_send(save_client
, wbuf
, 9) < 0)
215 dev_err(&save_client
->dev
, "m41t00_set: Write error\n");
218 static ulong new_time
;
219 /* well, isn't this API just _lovely_? */
221 m41t00_barf(struct work_struct
*unusable
)
223 m41t00_set(&new_time
);
226 static struct workqueue_struct
*m41t00_wq
;
227 static DECLARE_WORK(m41t00_work
, m41t00_barf
);
230 m41t00_set_rtc_time(ulong nowtime
)
235 queue_work(m41t00_wq
, &m41t00_work
);
237 m41t00_set(&new_time
);
241 EXPORT_SYMBOL_GPL(m41t00_set_rtc_time
);
244 *****************************************************************************
246 * platform_data Driver Interface
248 *****************************************************************************
251 m41t00_platform_probe(struct platform_device
*pdev
)
253 struct m41t00_platform_data
*pdata
;
256 if (pdev
&& (pdata
= pdev
->dev
.platform_data
)) {
257 normal_addr
[0] = pdata
->i2c_addr
;
259 for (i
=0; i
<ARRAY_SIZE(m41t00_chip_info_tbl
); i
++)
260 if (m41t00_chip_info_tbl
[i
].type
== pdata
->type
) {
261 m41t00_chip
= &m41t00_chip_info_tbl
[i
];
262 m41t00_chip
->sqw_freq
= pdata
->sqw_freq
;
270 m41t00_platform_remove(struct platform_device
*pdev
)
275 static struct platform_driver m41t00_platform_driver
= {
276 .probe
= m41t00_platform_probe
,
277 .remove
= m41t00_platform_remove
,
279 .owner
= THIS_MODULE
,
280 .name
= M41T00_DRV_NAME
,
285 *****************************************************************************
289 *****************************************************************************
292 m41t00_probe(struct i2c_adapter
*adap
, int addr
, int kind
)
294 struct i2c_client
*client
;
297 if (!i2c_check_functionality(adap
, I2C_FUNC_I2C
298 | I2C_FUNC_SMBUS_BYTE_DATA
))
301 client
= kzalloc(sizeof(struct i2c_client
), GFP_KERNEL
);
305 strlcpy(client
->name
, m41t00_chip
->name
, I2C_NAME_SIZE
);
307 client
->adapter
= adap
;
308 client
->driver
= &m41t00_driver
;
310 if ((rc
= i2c_attach_client(client
)))
313 if (m41t00_chip
->type
!= M41T00_TYPE_M41T00
) {
314 /* If asked, disable SQW, set SQW frequency & re-enable */
315 if (m41t00_chip
->sqw_freq
)
316 if (((rc
= i2c_smbus_read_byte_data(client
,
317 m41t00_chip
->alarm_mon
)) < 0)
318 || ((rc
= i2c_smbus_write_byte_data(client
,
319 m41t00_chip
->alarm_mon
, rc
& ~0x40)) <0)
320 || ((rc
= i2c_smbus_write_byte_data(client
,
322 m41t00_chip
->sqw_freq
)) < 0)
323 || ((rc
= i2c_smbus_write_byte_data(client
,
324 m41t00_chip
->alarm_mon
, rc
| 0x40)) <0))
327 /* Make sure HT (Halt Update) bit is cleared */
328 if ((rc
= i2c_smbus_read_byte_data(client
,
329 m41t00_chip
->alarm_hour
)) < 0)
333 if ((rc
= i2c_smbus_write_byte_data(client
,
334 m41t00_chip
->alarm_hour
, rc
& ~0x40))<0)
338 /* Make sure ST (stop) bit is cleared */
339 if ((rc
= i2c_smbus_read_byte_data(client
, m41t00_chip
->sec
)) < 0)
343 if ((rc
= i2c_smbus_write_byte_data(client
, m41t00_chip
->sec
,
347 m41t00_wq
= create_singlethread_workqueue(m41t00_chip
->name
);
348 save_client
= client
;
352 dev_err(&client
->dev
, "m41t00_probe: Can't clear ST bit\n");
355 dev_err(&client
->dev
, "m41t00_probe: Can't clear HT bit\n");
358 dev_err(&client
->dev
, "m41t00_probe: Can't set SQW Frequency\n");
365 m41t00_attach(struct i2c_adapter
*adap
)
367 return i2c_probe(adap
, &addr_data
, m41t00_probe
);
371 m41t00_detach(struct i2c_client
*client
)
375 if ((rc
= i2c_detach_client(client
)) == 0) {
377 destroy_workqueue(m41t00_wq
);
382 static struct i2c_driver m41t00_driver
= {
384 .name
= M41T00_DRV_NAME
,
386 .id
= I2C_DRIVERID_STM41T00
,
387 .attach_adapter
= m41t00_attach
,
388 .detach_client
= m41t00_detach
,
396 if (!(rc
= platform_driver_register(&m41t00_platform_driver
)))
397 rc
= i2c_add_driver(&m41t00_driver
);
404 i2c_del_driver(&m41t00_driver
);
405 platform_driver_unregister(&m41t00_platform_driver
);
408 module_init(m41t00_init
);
409 module_exit(m41t00_exit
);
411 MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
412 MODULE_DESCRIPTION("ST Microelectronics M41T00 RTC I2C Client Driver");
413 MODULE_LICENSE("GPL");