Real-time clock driver for Dallas Semiconductor DS1307 chip. This just

provides and API for NVRAM and RTC drivers to sit on top of this. An example
of drivers that uses this API are the Frodo RTC and NVRAM drivers.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1433 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Abraham van der Merwe committed 2002-07-09 12:45:08 +00:00
1 parent 73af6c49fc
commit 97d966fb75
2 files changed
+426

No files matched your search

+367
View File
@@ -0,0 +1,367 @@
/*
* linux/drivers/i2c/ds1307.c
*
* Author: Abraham van der Merwe <abraham@2d3d.co.za>
*
* Linux support for the Dallas Semiconductor DS1307 Serial Real-Time
* Clock.
*
* Based on code from the lm-sensors project which is available
* at http://www.lm-sensors.nu/ and Russell King's PCF8583 Real-Time
* Clock driver (linux/drivers/acorn/char/pcf8583.c).
*
* This source code 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.
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <asm/semaphore.h>
#include "ds1307.h"
#define BCD_TO_BIN(x) (((x) & 15) + ((x) >> 4) * 10)
#define BIN_TO_BCD(x) ((((x) / 10) << 4) + (x) % 10)
static struct i2c_driver ds1307;
static DECLARE_MUTEX (mutex);
/*
* The DS1307 Real-Time Clock wants the address in a different
* message, so we can't use the normal i2c_master_recv() routine
* for receiving data.
*/
static int ds1307_i2c_recv (struct i2c_client *client,char *buf,char addr,int count)
{
struct i2c_msg msg[] = {
{ addr: client->addr, flags: 0, len: 1, buf: &addr },
{ addr: client->addr, flags: I2C_M_RD, len: count, buf: buf }
};
int result = 0;
if (down_interruptible (&mutex))
return (-ERESTARTSYS);
if (i2c_transfer (client->adapter,msg,2) != 2)
result = -EIO;
up (&mutex);
return (result);
}
/*
* Would've been nice to specify the address to this as well, but then we
* would need to copy the buffer twice - not worth it...
*/
static int ds1307_i2c_send (struct i2c_client *client,const char *buf,int count)
{
int result = 0;
if (down_interruptible (&mutex))
return (-ERESTARTSYS);
if (i2c_master_send (client,(const char *) buf,count) != count)
result = -EIO;
up (&mutex);
return (result);
}
static int ds1307_attach (struct i2c_adapter *adapter,int addr,unsigned short flags,int kind)
{
struct i2c_client *client;
int result;
if ((client = (struct i2c_client *) kmalloc (sizeof (struct i2c_client),GFP_KERNEL)) == NULL)
return (-ENOMEM);
strcpy (client->name,ds1307.name);
client->id = ds1307.id;
client->flags = I2C_CLIENT_ALLOW_USE | I2C_CLIENT_ALLOW_MULTIPLE_USE;
client->addr = addr;
client->adapter = adapter;
client->driver = &ds1307;
client->data = NULL;
if ((result = i2c_attach_client (client))) {
kfree (client);
return (result);
}
return (0);
}
static int ds1307_attach_adapter (struct i2c_adapter *adapter)
{
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short addr[] = { 0x68, I2C_CLIENT_END };
static struct i2c_client_address_data ds1307_addr_data = {
normal_i2c: addr,
normal_i2c_range: ignore,
probe: ignore,
probe_range: ignore,
ignore: ignore,
ignore_range: ignore,
force: ignore
};
return (i2c_probe (adapter,&ds1307_addr_data,ds1307_attach));
}
static int ds1307_detach_client (struct i2c_client *client)
{
int result;
if ((result = i2c_detach_client (client)))
return (result);
kfree (client);
return (0);
}
static int ds1307_getdate (struct i2c_client *client,void *arg)
{
struct ds1307_date *date = (struct ds1307_date *) arg;
u8 buf[7];
/* this also enables the oscillator */
memset (buf,0,7);
/* enable 24-hour mode */
buf[2] = 0x40;
if (ds1307_i2c_recv (client,(char *) buf,0,7) < 0)
return (-EIO);
date->tm_sec = BCD_TO_BIN (buf[0] & ~0x80);
date->tm_min = BCD_TO_BIN (buf[1]);
date->tm_hour = BCD_TO_BIN (buf[2] & 0x3f);
date->tm_wday = BCD_TO_BIN (buf[3]) - 1;
date->tm_mday = BCD_TO_BIN (buf[4]);
date->tm_mon = BCD_TO_BIN (buf[5]) - 1;
date->tm_year = BCD_TO_BIN (buf[6]) + 100;
return (0);
}
static int ds1307_setdate (struct i2c_client *client,void *arg)
{
struct ds1307_date *date = (struct ds1307_date *) arg;
u8 buf[8];
/* select address 0 */
buf[0] = 0;
buf[1] = BIN_TO_BCD (date->tm_sec);
buf[2] = BIN_TO_BCD (date->tm_min);
buf[3] = BIN_TO_BCD (date->tm_hour) | 0x40;
buf[4] = BIN_TO_BCD (date->tm_wday + 1);
buf[5] = BIN_TO_BCD (date->tm_mday);
buf[6] = BIN_TO_BCD (date->tm_mon + 1);
buf[7] = BIN_TO_BCD (date->tm_year - 100);
return (ds1307_i2c_send (client,(const char *) buf,8));
}
static int ds1307_enable (struct i2c_client *client,void *arg)
{
u8 buf[2];
if (ds1307_i2c_recv (client,(char *) buf + 1,0,1) < 0)
return (-EIO);
if ((buf[1] & 0x80)) {
buf[0] = 0, buf[1] &= ~0x80;
return (ds1307_i2c_send (client,(const char *) buf,2));
}
return (0);
}
static int ds1307_irqon (struct i2c_client *client,void *arg)
{
u8 buf[2];
if (ds1307_i2c_recv (client,(char *) buf + 1,7,1) < 0)
return (-EIO);
buf[0] = 7;
buf[1] |= 0x10;
return (ds1307_i2c_send (client,(const char *) buf,2));
}
static int ds1307_irqoff (struct i2c_client *client,void *arg)
{
u8 buf[2];
if (ds1307_i2c_recv (client,(char *) buf + 1,7,1) < 0)
return (-EIO);
buf[0] = 7;
buf[1] &= ~0x10;
return (ds1307_i2c_send (client,(const char *) buf,2));
}
static int ds1307_getfreq (struct i2c_client *client,void *arg)
{
u16 *freq = (u16 *) arg;
u8 buf;
static const u16 table[] = {
DS1307_FREQ_1HZ,
DS1307_FREQ_4KHZ,
DS1307_FREQ_8KHZ,
DS1307_FREQ_32KHZ
};
if (ds1307_i2c_recv (client,(char *) &buf,7,1) < 0)
return (-EIO);
*freq = table[buf & 3];
return (0);
}
static int ds1307_setfreq (struct i2c_client *client,void *arg)
{
u16 *freq = (u16 *) arg;
u8 buf[2];
/* select address 7 */
buf[0] = 7;
/* default to 1HZ */
buf[1] = 0;
switch (*freq) {
case DS1307_FREQ_32KHZ: buf[1]++;
case DS1307_FREQ_8KHZ: buf[1]++;
case DS1307_FREQ_4KHZ: buf[1]++;
case DS1307_FREQ_1HZ: break;
default:
return (-EINVAL);
}
return (ds1307_i2c_send (client,(const char *) buf,2));
}
static int ds1307_read (struct i2c_client *client,void *arg)
{
struct ds1307_memory *mem = (struct ds1307_memory *) arg;
u8 buf[DS1307_SIZE];
if (mem->offset >= DS1307_SIZE || mem->offset + mem->length > DS1307_SIZE)
return (-EINVAL);
if (ds1307_i2c_recv (client,(char *) buf,mem->offset + 8,mem->length) < 0)
return (-EIO);
memcpy (mem->buf,buf,mem->length);
return (0);
}
static int ds1307_write (struct i2c_client *client,void *arg)
{
struct ds1307_memory *mem = (struct ds1307_memory *) arg;
u8 buf[DS1307_SIZE + 1];
if (mem->offset >= DS1307_SIZE || mem->offset + mem->length > DS1307_SIZE)
return (-EINVAL);
buf[0] = mem->offset + 8;
memcpy (buf + 1,mem->buf,mem->length);
return (ds1307_i2c_send (client,(const char *) buf,mem->length + 1));
}
static int ds1307_command (struct i2c_client *client,unsigned int cmd,void *arg)
{
static const struct {
int cmd;
int (*function) (struct i2c_client *,void *arg);
} ioctl[] = {
{ DS1307_ENABLE, ds1307_enable },
{ DS1307_GET_DATE, ds1307_getdate },
{ DS1307_SET_DATE, ds1307_setdate },
{ DS1307_IRQ_ON, ds1307_irqon },
{ DS1307_IRQ_OFF, ds1307_irqoff },
{ DS1307_GET_FREQ, ds1307_getfreq },
{ DS1307_SET_FREQ, ds1307_setfreq },
{ DS1307_READ, ds1307_read },
{ DS1307_WRITE, ds1307_write }
};
int i;
for (i = 0; i < sizeof (ioctl) / sizeof (ioctl[0]); i++)
if (ioctl[i].cmd == cmd)
return (ioctl[i].function (client,arg));
return (-EINVAL);
}
static void ds1307_inc_use (struct i2c_client *client)
{
MOD_INC_USE_COUNT;
}
static void ds1307_dec_use (struct i2c_client *client)
{
MOD_DEC_USE_COUNT;
}
static struct i2c_driver ds1307 = {
name: "ds1307",
id: I2C_DRIVERID_DS1307,
flags: I2C_DF_NOTIFY,
attach_adapter: ds1307_attach_adapter,
detach_client: ds1307_detach_client,
command: ds1307_command,
inc_use: ds1307_inc_use,
dec_use: ds1307_dec_use
};
static int __init ds1307_init (void)
{
int result;
if ((result = i2c_add_driver (&ds1307)))
return (result);
printk ("Dallas Semiconductor DS1307 Real-Time Clock driver (V0.03)\n");
return (0);
}
static void __exit ds1307_exit (void)
{
i2c_del_driver (&ds1307);
}
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR ("Abraham van der Merwe <abraham@2d3d.co.za>");
MODULE_DESCRIPTION ("Linux support for DS1307 Real-Time Clock");
#ifdef MODULE_LICENSE
MODULE_LICENSE ("GPL");
#endif /* #ifdef MODULE_LICENSE */
module_init (ds1307_init);
module_exit (ds1307_exit);
+59
View File
@@ -0,0 +1,59 @@
#ifndef DS1307_H
#define DS1307_H
/*
* linux/include/linux/ds1307.h
*
* Author: Abraham van der Merwe <abraham@2d3d.co.za>
*
* Linux support for the Dallas Semiconductor DS1307 Serial Real-Time
* Clock.
*
* Based on code from the lm-sensors project which is available
* at http://www.lm-sensors.nu/ and Russell King's PCF8583 Real-Time
* Clock driver (linux/drivers/acorn/char/pcf8583.c).
*
* This source code 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.
*/
#include <linux/types.h>
/* practically the same as struct rtc_time, but without tm_yday and tm_isdst */
struct ds1307_date {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
};
struct ds1307_memory {
u8 offset; /* 0 - 55 */
u8 buf[56]; /* data */
u8 length; /* offset + length <= 50 */
};
/* size of the rtc non-volatile ram */
#define DS1307_SIZE 56
/* the following frequencies are supported */
#define DS1307_FREQ_1HZ 1
#define DS1307_FREQ_4KHZ 4096
#define DS1307_FREQ_8KHZ 8192
#define DS1307_FREQ_32KHZ 32768
#define DS1307_GET_DATE _IOR ('d',0,struct ds1307_date *)
#define DS1307_SET_DATE _IOW ('d',1,struct ds1307_date *)
#define DS1307_IRQ_ON _IO ('d',2)
#define DS1307_IRQ_OFF _IO ('d',3)
#define DS1307_GET_FREQ _IOR ('d',4,u16 *)
#define DS1307_SET_FREQ _IOW ('d',5,u16 *)
#define DS1307_READ _IOR ('d',6,struct ds1307_memory *)
#define DS1307_WRITE _IOW ('d',7,struct ds1307_memory *)
#define DS1307_ENABLE _IO ('d',8)
#endif /* #ifdef DS1307_H */