📄 ds3231.c
字号:
/* * linux/drivers/i2c/chips/ds1337.c * * Copyright (C) 2005 James Chapman <jchapman@katalix.com> * * based on linux/drivers/acorn/char/pcf8583.c * Copyright (C) 2000 Russell King * * This program 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. * * Driver for Dallas Semiconductor DS1337 and DS1339 real time clock chip * * History: * 2007/8/11 Port by WenBinWu for PNX8550,add interface for user application; * 2007/9/12 add source comment by liuym for ds3231 chipset * 2008/4/24 add read temperature funtion for ds3231 chipset */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/string.h> #include <linux/rtc.h> /* get the user-level API */ #include <linux/bcd.h> #include <linux/list.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <asm/uaccess.h> #include <linux/ioctl.h> #include <linux/major.h> #include <linux/errno.h> #include "ds3231.h" /* Device registers */ #define DS3231_REG_SECOND 0x00 #define DS3231_REG_MINUTE 0x01 #define DS3231_REG_HOUR 0x02 /*x [12/24] 2 1 | 8 4 2 1 */ #define DS3231_REG_DAY 0x03 #define DS3231_REG_DATE 0x04 #define DS3231_REG_MONTH 0x05 #define ds3231_REG_YEAR 0x06 /* 8 4 2 1 | 8 4 2 1 */ #define ds3231_REG_AL1_SECOND 0x07 /* A1M1 4 2 1 | 8 4 2 1 */ #define ds3231_REG_AL1_MIN 0x08 /* A1M2 4 2 1 | 8 4 2 1 */ #define ds3231_REG_AL1_HR 0x09 /* A1M3 [12/24] 2 1 | 8 4 2 1 */ #define ds3231_REG_AL1_DAY 0x0a /* A1M4 [DY/DT] 2 1 | 8 4 2 1 */ #define ds3231_REG_AL2_MIN 0x0b /* A2M2 4 2 1 | 8 4 2 1 */ #define ds3231_REG_AL2_HR 0x0c /* A2M3 [12/24] 2 1 | 8 4 2 1 */ #define ds3231_REG_AL2_DAY 0x0d /* A2M4 [DY/DT] 2 1 | 8 4 2 1 */ #define DS3231_REG_CONTROL 0x0e /*ESOC x x RS2 RS1 INTCN A2IE A1IE*/ #define DS3231_REG_STATUS 0x0f /*OSF X X X X X A1F A2F*/ #define DS3231_REG_TEMP_HI 0x11 #define DS3231_REG_TEMP_LO 0x12 /* Control reg */ #define ds3231_CTRL_A1IE (1<<0) #define ds3231_CTRL_A2IE (1<<1) #define ds3231_CTRL_INTCN (1<<2) #define ds3231_CTRL_RS (1<<3) #define ds3231_CTRL_EOSC (1<<7) /* Status reg */ #define ds3231_STATUS_A1F (1<<0) #define ds3231_STATUS_A2F (1<<1) #define ds3231_STATUS_OSF (1<<7) /* CLKOUT frequencies , RS*/ #define ds3231_FD_32768HZ (0x3) #define ds3231_FD_1024HZ (0x2) #define ds3231_FD_32 (0x1) #define ds3231_FD_1HZ (0x0) #define I2C_SALVEID_DS3231 0xd0 #define I2C_DRIVERID_DS3231 0xf000 #define DEVNAME "rtc" /* * Generic i2c probe * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */static unsigned short normal_i2c[] = { I2C_SALVEID_DS3231 >> 1, I2C_CLIENT_END };static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END };static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END };static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END };static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END }; static struct i2c_client_address_data addr_data = { .normal_i2c = normal_i2c, .normal_i2c_range = normal_i2c_range, .probe = probe, .probe_range = probe_range, .ignore = ignore, .ignore_range = ignore_range, .force = force};//I2C_CLIENT_INSMOD_1(ds3231);static int ds3231_attach_adapter(struct i2c_adapter *adapter);static int ds3231_detect(struct i2c_adapter *adapter, int address, int kind);static void ds3231_init_client(struct i2c_client *client);static int ds3231_detach_client(struct i2c_client *client);static int ds3231_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int ds3231_get_tempture(struct i2c_client *client, int *value);/*2007/9/12, liuym, file operations for device file register though ds3231_MiscDev, which is defined below*/static struct file_operations ds3231_fops = { .owner = THIS_MODULE, .read = NULL, .open = NULL, .release = NULL, .ioctl = ds3231_ioctl,};/*2007/9/12, liuym, structure used in dynamically register and unregister device ,search for "misc_register" and "misc_deregister" */static struct miscdevice ds3231_MiscDev = { .minor = MISC_DYNAMIC_MINOR, .name = DEVNAME, .fops = &ds3231_fops}; /* * I2c Driver struct */static struct i2c_driver ds3231_driver = { .owner = THIS_MODULE, .id = I2C_DRIVERID_DS3231, .name = "ds3231", .flags = I2C_DF_NOTIFY, .attach_adapter = ds3231_attach_adapter, .detach_client = ds3231_detach_client, }; static int ds3231_attached = 0;static struct i2c_client *ds3231_client; /* * Client data (each client gets its own) */ struct ds3231_data { struct i2c_client client; struct list_head list; }; /* * Internal variables */ //static LIST_HEAD(ds3231_clients); /*DS3231读写函数*/ static inline int ds3231_read(struct i2c_client *client, u8 reg, u8 *value) { s32 tmp = i2c_smbus_read_byte_data(client, reg); if (tmp < 0) return -EIO; *value = tmp; return 0; } static inline int ds3231_write (struct i2c_client *client, u8 reg, u8 value){ return i2c_smbus_write_byte_data(client, reg, value);} /*读取DS3231内部温度传感器转换编码值*/static int ds3231_get_tempture(struct i2c_client *client, int *value){ int i; int err; u8 buf[2]; int flag; int t1; int t2; err = ds3231_read(client, DS3231_REG_TEMP_HI, &buf[0]); if (err) return err; err = ds3231_read(client, DS3231_REG_TEMP_LO, &buf[1]); if (err) return err; /*温度编码值占2个字节,第一个字节的第1位是符号位,第二字节的只有第1,2位是有效位。*/ flag=buf[0] & 0x80; t1=buf[0] & 0xef; t2=buf[1] & 0xc0; t1=(t1<<2) | (t2 >> 6); if (flag>0) t1 = t1 * (-1); *value = t1; return 0; } /* * DS3231时间获取函数 */ static int ds3231_get_datetime(struct i2c_client *client, struct rtc_time *dt) { u8 buf[7]; u8 val; int err = 0; if (!dt) { dev_dbg(&client->dev, "%s: EINVAL: dt=NULL\n", __FUNCTION__); return -EINVAL; } u8 i = 0; dev_dbg(&client->dev, "%s: RTC not running!\n", __FUNCTION__); /* 读取寄存器的值*/ for (i=0; i<7; i++) { err = ds3231_read(client, i, &buf[i]); if (err != 0) break; } dev_dbg(&client->dev, "%s: [%d] %02x %02x %02x %02x %02x %02x %02x\n", __FUNCTION__, err, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (err == 0) { dt->tm_sec = BCD2BIN(buf[0]); dt->tm_min = BCD2BIN(buf[1]); val = buf[2] & 0x3f; //RTC 模式是24小时制 dt->tm_hour = BCD2BIN(val); dt->tm_wday = BCD2BIN(buf[3]) - 1;//RTC星期表示为1-7;需转换成0-6 dt->tm_mday = BCD2BIN(buf[4]); val = buf[5] & 0x7f; dt->tm_mon = BCD2BIN(val) - 1;//RTC月份表示为1-12;需转换成0-11 dt->tm_year = BCD2BIN(buf[6]); if (buf[5] & 0x80) dt->tm_year += 100; dev_dbg(&client->dev, "%s: secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, dt->tm_sec, dt->tm_min, dt->tm_hour, dt->tm_mday, dt->tm_mon, dt->tm_year, dt->tm_wday); return 0; } dev_err(&client->dev, "error reading data! %d\n", err); return -EIO; } /* * DS3231时间设置函数 */ static int ds3231_set_datetime(struct i2c_client *client, struct rtc_time *dt) { u8 buf[7]; u8 val; u8 i = 0; if (!dt) { dev_dbg(&client->dev, "%s: EINVAL: dt=NULL\n", __FUNCTION__); return -EINVAL; } dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, dt->tm_sec, dt->tm_min, dt->tm_hour, dt->tm_mday, dt->tm_mon, dt->tm_year, dt->tm_wday); buf[0] = BIN2BCD(dt->tm_sec); buf[1] = BIN2BCD(dt->tm_min); buf[2] = BIN2BCD(dt->tm_hour); buf[3] = BIN2BCD(dt->tm_wday + 1); buf[4] = BIN2BCD(dt->tm_mday); buf[5] = BIN2BCD(dt->tm_mon + 1); val = dt->tm_year; if (val >= 100) { val -= 100; buf[5] |= (1 << 7); } buf[6] = BIN2BCD(val); for (i=0; i<7; i++) { ds3231_write(client, i, buf[i]); } return 0; }/* ds3231命令接口,应用层的ioctl调用将首先进入该函数*/ static int ds3231_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct rtc_time rtc_tm; int val; switch (cmd) { /*读取RTC时间*/ case RTC_RD_TIME: memset(&rtc_tm, 0, sizeof (struct rtc_time)); if (ds3231_get_datetime(ds3231_client, &rtc_tm)) return -EFAULT; if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time))) return -EFAULT; break; /*设置RTC时间*/ case RTC_SET_TIME: if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) return -EFAULT; if (ds3231_set_datetime(ds3231_client, &rtc_tm)) return -EFAULT; break; case RTC_TEMP_READ: if (ds3231_get_tempture(ds3231_client, &val)) return -EFAULT; if (copy_to_user((int*)arg, &val, sizeof(int))) return -EFAULT; break; case RTC_ALM_READ: case RTC_ALM_SET: break; default: return -EINVAL; } return 0;} static int ds3231_attach_adapter(struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, ds3231_detect); } /* * DS3231检测程序,如果测试成功,将会注册这个设备 */ static int ds3231_i2c_id = 0; static int ds3231_detect(struct i2c_adapter *adapter, int address, int kind) { int err = 0; if (ds3231_attached == 1) goto exit; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) goto exit; if (!(ds3231_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { err = -ENOMEM; goto exit; } memset(ds3231_client, 0, sizeof(struct i2c_client)); ds3231_client->addr = address; ds3231_client->adapter = adapter; ds3231_client->driver = &ds3231_driver; ds3231_client->flags = I2C_CLIENT_ALLOW_USE; ds3231_client->id = ds3231_i2c_id++; snprintf(ds3231_client->name, sizeof(ds3231_client->name) - 1, "ds3231-%d", ds3231_client->id); /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(ds3231_client))) goto exit_free; /* Initialize the DS3231 chip */ ds3231_init_client(ds3231_client); /* Register a misc device called "ds3231". */ if ((err = misc_register( &ds3231_MiscDev ))) goto exit_free; ds3231_attached = 1; /*only support one device*/ return 0; exit_free: kfree(ds3231_client); exit: return err; } /* ds3231芯片初始化函数,在探测到到芯片后,调用该函数 */ static void ds3231_init_client(struct i2c_client *client) { u8 status = 0, control = 0; u8 val; int err; err = ds3231_read(client, DS3231_REG_STATUS, &status); err = ds3231_read(client, DS3231_REG_CONTROL,&control); if ((status & 0x80) || (control & 0x80)) { /* RTC 没有运行,复位RTC */ u8 i = 0; printk("RTC not running!\n"); dev_dbg(&client->dev, "%s: RTC not running!\n", __FUNCTION__); /* 将 STATUS ,CONTROL 设置为0 */ for (i=14; i<16; i++) { ds3231_write(client, i, 0); } } else { /* DS3231运行中,要保证运行在24小时模式 */ printk("RTC running!\n"); err = ds3231_read(client, DS3231_REG_HOUR, &val); if ((val & (1 << 6))) ds3231_write(client, DS3231_REG_HOUR, val & 0x3f); } } static int ds3231_detach_client(struct i2c_client *client) { int err; if ((err = i2c_detach_client(client))) return err; if (misc_deregister(&ds3231_MiscDev)!=0) { printk(KERN_INFO "ds3231: could not misc_deregister the device\n"); } kfree(client); return 0; } /*加载模块(insmod)的入口*/ static int __init ds3231_init(void) { return i2c_add_driver(&ds3231_driver); } /*卸载模块(rmmod)的入口*/ static void __exit ds3231_exit(void) { i2c_del_driver(&ds3231_driver); } MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); MODULE_DESCRIPTION("DS3231 RTC driver"); MODULE_LICENSE("GPL"); module_init(ds3231_init); module_exit(ds3231_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -