📄 ds1337.c
字号:
/* * linux/drivers/i2c/chips/ds1307.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 DS1307 and DS1339 real time clock chip */#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/i2c-sensor.h>#include <linux/string.h>#include <linux/rtc.h> /* get the user-level API */#include <linux/bcd.h>#include <linux/list.h>/* Device registers */#define DS1307_REG_HOUR 2#define DS1307_REG_DAY 3#define DS1307_REG_DATE 4#define DS1307_REG_MONTH 5#define DS1307_REG_CONTROL 14#define DS1307_REG_STATUS 15/* FIXME - how do we export these interface constants? */#define DS1307_GET_DATE 0#define DS1307_SET_DATE 1/* * Functions declaration */static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END };static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };SENSORS_INSMOD_1(ds1307);static int ds1307_attach_adapter(struct i2c_adapter *adapter);static int ds1307_detect(struct i2c_adapter *adapter, int address, int kind);static void ds1307_init_client(struct i2c_client *client);static int ds1307_detach_client(struct i2c_client *client);static int ds1307_command(struct i2c_client *client, unsigned int cmd, void *arg);/* * Driver data (common to all clients) */static struct i2c_driver ds1307_driver = { .owner = THIS_MODULE, .name = "ds1307", .flags = I2C_DF_NOTIFY, .attach_adapter = ds1307_attach_adapter, .detach_client = ds1307_detach_client, .command = ds1307_command,};/* * Client data (each client gets its own) */struct ds1307_data { struct i2c_client client; struct list_head list;};/* * Internal variables */static LIST_HEAD(ds1307_clients);static inline int ds1307_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;}/* * Chip access functions */static int ds1307_get_datetime(struct i2c_client *client, struct rtc_time *dt){ int result; u8 buf[7]; u8 val; struct i2c_msg msg[2]; u8 offs = 0; if (!dt) { dev_dbg(&client->dev, "%s: EINVAL: dt=NULL\n", __FUNCTION__); return -EINVAL; } msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 1; msg[0].buf = &offs; msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].len = sizeof(buf); msg[1].buf = &buf[0]; result = i2c_transfer(client->adapter, msg, 2); dev_dbg(&client->dev, "%s: [%d] %02x %02x %02x %02x %02x %02x %02x\n", __FUNCTION__, result, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); if (result == 2) { dt->tm_sec = BCD2BIN(buf[0]); dt->tm_min = BCD2BIN(buf[1]); val = buf[2] & 0x3f; dt->tm_hour = BCD2BIN(val); dt->tm_wday = BCD2BIN(buf[3]) - 1; dt->tm_mday = BCD2BIN(buf[4]); val = buf[5] & 0x7f; dt->tm_mon = BCD2BIN(val) - 1; 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", result); return -EIO;}static int ds1307_set_datetime(struct i2c_client *client, struct rtc_time *dt){ int result; u8 buf[8]; u8 val; struct i2c_msg msg[1]; 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] = 0; /* reg offset */ buf[1] = BIN2BCD(dt->tm_sec); buf[2] = BIN2BCD(dt->tm_min); buf[3] = BIN2BCD(dt->tm_hour); buf[4] = BIN2BCD(dt->tm_wday) + 1; buf[5] = BIN2BCD(dt->tm_mday); buf[6] = BIN2BCD(dt->tm_mon) + 1; val = dt->tm_year; if (val >= 100) { val -= 100; buf[6] |= (1 << 7); } buf[7] = BIN2BCD(val); msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = sizeof(buf); msg[0].buf = &buf[0]; result = i2c_transfer(client->adapter, msg, 1); if (result == 1) return 0; dev_err(&client->dev, "error writing data! %d\n", result); return -EIO;}static int ds1307_command(struct i2c_client *client, unsigned int cmd, void *arg){ dev_dbg(&client->dev, "%s: cmd=%d\n", __FUNCTION__, cmd); switch (cmd) { case DS1307_GET_DATE: return ds1307_get_datetime(client, arg); case DS1307_SET_DATE: return ds1307_set_datetime(client, arg); default: return -EINVAL; }}/* * Public API for access to specific device. Useful for low-level * RTC access from kernel code. */int ds1307_do_command(int bus, int cmd, void *arg){ struct list_head *walk; struct list_head *tmp; struct ds1307_data *data; list_for_each_safe(walk, tmp, &ds1307_clients) { data = list_entry(walk, struct ds1307_data, list); if (data->client.adapter->nr == bus) return ds1307_command(&data->client, cmd, arg); } return -ENODEV;}static int ds1307_attach_adapter(struct i2c_adapter *adapter){ return i2c_detect(adapter, &addr_data, ds1307_detect);}/* * The following function does more than just detection. If detection * succeeds, it also registers the new chip. */static int ds1307_detect(struct i2c_adapter *adapter, int address, int kind){ struct i2c_client *new_client; struct ds1307_data *data; int err = 0; const char *name = ""; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_I2C)) goto exit; if (!(data = kmalloc(sizeof(struct ds1307_data), GFP_KERNEL))) { err = -ENOMEM; goto exit; } memset(data, 0, sizeof(struct ds1307_data)); INIT_LIST_HEAD(&data->list); /* The common I2C client data is placed right before the * DS1307-specific data. */ new_client = &data->client; i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &ds1307_driver; new_client->flags = 0; /* * Now we do the remaining detection. A negative kind means that * the driver was loaded with no force parameter (default), so we * must both detect and identify the chip. A zero kind means that * the driver was loaded with the force parameter, the detection * step shall be skipped. A positive kind means that the driver * was loaded with the force parameter and a given kind of chip is * requested, so both the detection and the identification steps * are skipped. * * For detection, we read registers that are most likely to cause * detection failure, i.e. those that have more bits with fixed * or reserved values. */ /* Default to an DS1307 if forced */ if (kind == 0) kind = ds1307; if (kind < 0) { /* detection and identification */ u8 data; /* Check that status register bits 6-2 are zero */ if ((ds1307_read(new_client, DS1307_REG_STATUS, &data) < 0) || (data & 0x7c)) goto exit_free; /* Check for a valid day register value */ if ((ds1307_read(new_client, DS1307_REG_DAY, &data) < 0) || (data == 0) || (data & 0xf8)) goto exit_free; /* Check for a valid date register value */ if ((ds1307_read(new_client, DS1307_REG_DATE, &data) < 0) || (data == 0) || (data & 0xc0) || ((data & 0x0f) > 9) || (data >= 0x32)) goto exit_free; /* Check for a valid month register value */ if ((ds1307_read(new_client, DS1307_REG_MONTH, &data) < 0) || (data == 0) || (data & 0x60) || ((data & 0x0f) > 9) || ((data >= 0x13) && (data <= 0x19))) goto exit_free; /* Check that control register bits 6-5 are zero */ if ((ds1307_read(new_client, DS1307_REG_CONTROL, &data) < 0) || (data & 0x60)) goto exit_free; kind = ds1307; } if (kind == ds1307) name = "ds1307"; /* We can fill in the remaining client fields */ strlcpy(new_client->name, name, I2C_NAME_SIZE); /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(new_client))) goto exit_free; /* Initialize the DS1307 chip */ ds1307_init_client(new_client); /* Add client to local list */ list_add(&data->list, &ds1307_clients); return 0;exit_free: kfree(data);exit: return err;}static void ds1307_init_client(struct i2c_client *client){ s32 val; /* Ensure that device is set in 24-hour mode */ val = i2c_smbus_read_byte_data(client, DS1307_REG_HOUR); if ((val >= 0) && (val & (1 << 6))) i2c_smbus_write_byte_data(client, DS1307_REG_HOUR, val & 0x3f);}static int ds1307_detach_client(struct i2c_client *client){ int err; struct ds1307_data *data = i2c_get_clientdata(client); if ((err = i2c_detach_client(client))) { dev_err(&client->dev, "Client deregistration failed, " "client not detached.\n"); return err; } list_del(&data->list); kfree(data); return 0;}static int __init ds1307_init(void){ return i2c_add_driver(&ds1307_driver);}static void __exit ds1307_exit(void){ i2c_del_driver(&ds1307_driver);}MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");MODULE_DESCRIPTION("DS1307 RTC driver");MODULE_LICENSE("GPL");EXPORT_SYMBOL_GPL(ds1307_do_command);module_init(ds1307_init);module_exit(ds1307_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -