📄 ds1337.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 */#include <linux/module.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>/* Device registers */#define DS1337_REG_HOUR 2#define DS1337_REG_DAY 3#define DS1337_REG_DATE 4#define DS1337_REG_MONTH 5#define DS1337_REG_CONTROL 14#define DS1337_REG_STATUS 15/* FIXME - how do we export these interface constants? */#define DS1337_GET_DATE 0#define DS1337_SET_DATE 1/* * Functions declaration */static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END };I2C_CLIENT_INSMOD_1(ds1337);static int ds1337_attach_adapter(struct i2c_adapter *adapter);static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind);static void ds1337_init_client(struct i2c_client *client);static int ds1337_detach_client(struct i2c_client *client);static int ds1337_command(struct i2c_client *client, unsigned int cmd, void *arg);/* * Driver data (common to all clients) */static struct i2c_driver ds1337_driver = { .driver = { .name = "ds1337", }, .attach_adapter = ds1337_attach_adapter, .detach_client = ds1337_detach_client, .command = ds1337_command,};/* * Client data (each client gets its own) */struct ds1337_data { struct i2c_client client; struct list_head list;};/* * Internal variables */static LIST_HEAD(ds1337_clients);static inline int ds1337_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 ds1337_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 ds1337_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 ds1337_command(struct i2c_client *client, unsigned int cmd, void *arg){ dev_dbg(&client->dev, "%s: cmd=%d\n", __FUNCTION__, cmd); switch (cmd) { case DS1337_GET_DATE: return ds1337_get_datetime(client, arg); case DS1337_SET_DATE: return ds1337_set_datetime(client, arg); default: return -EINVAL; }}/* * Public API for access to specific device. Useful for low-level * RTC access from kernel code. */int ds1337_do_command(int bus, int cmd, void *arg){ struct list_head *walk; struct list_head *tmp; struct ds1337_data *data; list_for_each_safe(walk, tmp, &ds1337_clients) { data = list_entry(walk, struct ds1337_data, list); if (data->client.adapter->nr == bus) return ds1337_command(&data->client, cmd, arg); } return -ENODEV;}static int ds1337_attach_adapter(struct i2c_adapter *adapter){ return i2c_probe(adapter, &addr_data, ds1337_detect);}/* * The following function does more than just detection. If detection * succeeds, it also registers the new chip. */static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind){ struct i2c_client *new_client; struct ds1337_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 = kzalloc(sizeof(struct ds1337_data), GFP_KERNEL))) { err = -ENOMEM; goto exit; } INIT_LIST_HEAD(&data->list); /* The common I2C client data is placed right before the * DS1337-specific data. */ new_client = &data->client; i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &ds1337_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 DS1337 if forced */ if (kind == 0) kind = ds1337; if (kind < 0) { /* detection and identification */ u8 data; /* Check that status register bits 6-2 are zero */ if ((ds1337_read(new_client, DS1337_REG_STATUS, &data) < 0) || (data & 0x7c)) goto exit_free; /* Check for a valid day register value */ if ((ds1337_read(new_client, DS1337_REG_DAY, &data) < 0) || (data == 0) || (data & 0xf8)) goto exit_free; /* Check for a valid date register value */ if ((ds1337_read(new_client, DS1337_REG_DATE, &data) < 0) || (data == 0) || (data & 0xc0) || ((data & 0x0f) > 9) || (data >= 0x32)) goto exit_free; /* Check for a valid month register value */ if ((ds1337_read(new_client, DS1337_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 ((ds1337_read(new_client, DS1337_REG_CONTROL, &data) < 0) || (data & 0x60)) goto exit_free; kind = ds1337; } if (kind == ds1337) name = "ds1337"; /* 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 DS1337 chip */ ds1337_init_client(new_client); /* Add client to local list */ list_add(&data->list, &ds1337_clients); return 0;exit_free: kfree(data);exit: return err;}static void ds1337_init_client(struct i2c_client *client){ u8 status, control; /* On some boards, the RTC isn't configured by boot firmware. * Handle that case by starting/configuring the RTC now. */ status = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS); control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); if ((status & 0x80) || (control & 0x80)) { /* RTC not running */ u8 buf[16]; struct i2c_msg msg[1]; dev_dbg(&client->dev, "%s: RTC not running!\n", __FUNCTION__); /* Initialize all, including STATUS and CONTROL to zero */ memset(buf, 0, sizeof(buf)); msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = sizeof(buf); msg[0].buf = &buf[0]; i2c_transfer(client->adapter, msg, 1); } else { /* Running: ensure that device is set in 24-hour mode */ s32 val; val = i2c_smbus_read_byte_data(client, DS1337_REG_HOUR); if ((val >= 0) && (val & (1 << 6))) i2c_smbus_write_byte_data(client, DS1337_REG_HOUR, val & 0x3f); }}static int ds1337_detach_client(struct i2c_client *client){ int err; struct ds1337_data *data = i2c_get_clientdata(client); if ((err = i2c_detach_client(client))) return err; list_del(&data->list); kfree(data); return 0;}static int __init ds1337_init(void){ return i2c_add_driver(&ds1337_driver);}static void __exit ds1337_exit(void){ i2c_del_driver(&ds1337_driver);}MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");MODULE_DESCRIPTION("DS1337 RTC driver");MODULE_LICENSE("GPL");EXPORT_SYMBOL_GPL(ds1337_do_command);module_init(ds1337_init);module_exit(ds1337_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -