返回信息流最近在做I2C总线的RTC芯片的嵌入式Linux下的驱动,在2.6.27内核中找到了一款相似的芯片PCF8563 ,驱动做的比较简洁明了,但是关于从设备地址一直搜索不到,按理应该是用 client->addr 做从地址访问器件的,但不知从何处赋值。下边是文件源代码,有做过的兄弟姐妹帮助给看下。谢谢
/*
* An I2C driver for the Philips PCF8563 RTC
* Copyright 2005-06 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
* Maintainers: http://www.nslu2-linux.org/
*
* based on the other drivers in this same directory.
*
* http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf
*
* 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.
*/
#include <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#define DRV_VERSION "0.4.3"
#define PCF8563_REG_ST1 0x00 /* status */
#define PCF8563_REG_ST2 0x01
#define PCF8563_REG_SC 0x02 /* datetime */
#define PCF8563_REG_MN 0x03
#define PCF8563_REG_HR 0x04
#define PCF8563_REG_DM 0x05
#define PCF8563_REG_DW 0x06
#define PCF8563_REG_MO 0x07
#define PCF8563_REG_YR 0x08
#define PCF8563_REG_AMN 0x09 /* alarm */
#define PCF8563_REG_AHR 0x0A
#define PCF8563_REG_ADM 0x0B
#define PCF8563_REG_ADW 0x0C
#define PCF8563_REG_CLKO 0x0D /* clock out */
#define PCF8563_REG_TMRC 0x0E /* timer control */
#define PCF8563_REG_TMR 0x0F /* timer */
#define PCF8563_SC_LV 0x80 /* low voltage */
#define PCF8563_MO_C 0x80 /* century */
static struct i2c_driver pcf8563_driver;
struct pcf8563 {
struct rtc_device *rtc;
/*
* The meaning of MO_C bit varies by the chip type.
* From PCF8563 datasheet: this bit is toggled when the years
* register overflows from 99 to 00
* 0 indicates the century is 20xx
* 1 indicates the century is 19xx
* From RTC8564 datasheet: this bit indicates change of
* century. When the year digit data overflows from 99 to 00,
* this bit is set. By presetting it to 0 while still in the
* 20th century, it will be set in year 2000, ...
* There seems no reliable way to know how the system use this
* bit. So let's do it heuristically, assuming we are live in
* 1970...2069.
*/
int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
};
/*
* In the routines that deal directly with the pcf8563 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
*/
//读取到tm结构体
static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
unsigned char buf[13] = { PCF8563_REG_ST1 };
struct i2c_msg msgs[] = {
{ client->addr, 0, 1, buf }, /* setup read ptr */
{ client->addr, I2C_M_RD, 13, buf }, /* read status + date */
};
/* read registers */
if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __func__);
return -EIO;
}
if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
dev_info(&client->dev,
"low voltage detected, date/time is not reliable.\n");
dev_dbg(&client->dev,
"%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
__func__,
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7],
buf[8]);
tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR]);
if (tm->tm_year < 70)
tm->tm_year += 100; /* assume we are in 1970...2069 */
/* detect the polarity heuristically. see note above. */
pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ?
(tm->tm_year >= 100) : (tm->tm_year < 100);
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__func__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
/* the clock can give out invalid datetime, but we cannot return
* -EINVAL otherwise hwclock will refuse to set the time on bootup.
*/
if (rtc_valid_tm(tm) < 0)
dev_err(&client->dev, "retrieved date/time is not valid.\n");
return 0;
}
static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
int i, err;
unsigned char buf[9];
dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__func__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
/* hours, minutes and seconds */
buf[PCF8563_REG_SC] = BIN2BCD(tm->tm_sec);
buf[PCF8563_REG_MN] = BIN2BCD(tm->tm_min);
buf[PCF8563_REG_HR] = BIN2BCD(tm->tm_hour);
buf[PCF8563_REG_DM] = BIN2BCD(tm->tm_mday);
/* month, 1 - 12 */
buf[PCF8563_REG_MO] = BIN2BCD(tm->tm_mon + 1);
/* year and century */
buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100);
if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
buf[PCF8563_REG_MO] |= PCF8563_MO_C;
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
/* write register's data */
for (i = 0; i < 7; i++) {
unsigned char data[2] = { PCF8563_REG_SC + i,
buf[PCF8563_REG_SC + i] };
err = i2c_master_send(client, data, sizeof(data));
if (err != sizeof(data)) {
dev_err(&client->dev,
"%s: err=%d addr=%02x, data=%02x\n",
__func__, err, data[0], data[1]);
return -EIO;
}
};
return 0;
}
struct pcf8563_limit
{
unsigned char reg;
unsigned char mask;
unsigned char min;
unsigned char max;
};
static int pcf8563_validate_client(struct i2c_client *client)
{
int i;
static const struct pcf8563_limit pattern[] = {
/* register, mask, min, max */
{ PCF8563_REG_SC, 0x7F, 0, 59 },
{ PCF8563_REG_MN, 0x7F, 0, 59 },
{ PCF8563_REG_HR, 0x3F, 0, 23 },
{ PCF8563_REG_DM, 0x3F, 0, 31 },
{ PCF8563_REG_MO, 0x1F, 0, 12 },
};
/* check limits (only registers with bcd values) */
for (i = 0; i < ARRAY_SIZE(pattern); i++) {
int xfer;
unsigned char value;
unsigned char buf = pattern[i].reg;//寄存器地址
//读一个寄存器数据
struct i2c_msg msgs[] = {
{ client->addr, 0, 1, &buf },
{ client->addr, I2C_M_RD, 1, &buf },
};
xfer = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (xfer != ARRAY_SIZE(msgs)) {
dev_err(&client->dev,
"%s: could not read register 0x%02X\n",
__func__, pattern[i].reg);
return -EIO;
}
value = BCD2BIN(buf & pattern[i].mask);//bcd 码转换成bin
//检查有效性
if (value > pattern[i].max ||
value < pattern[i].min) {
dev_info(&client->dev,
"%s: pattern=%d, reg=%x, mask=0x%02x, min=%d, "
"max=%d, value=%d, raw=0x%02X\n",
__func__, i, pattern[i].reg, pattern[i].mask,
pattern[i].min, pattern[i].max,
value, buf);
return -ENODEV;
}
}
return 0;
}
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
}
static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_set_datetime(to_i2c_client(dev), tm);
}
static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
};
//从地址是如何得到的???
static int pcf8563_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pcf8563 *pcf8563;
int err = 0;
dev_info(&client->dev, "%s\n", __func__);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
pcf8563 = kzalloc(sizeof(struct pcf8563), GFP_KERNEL);
if (!pcf8563)
return -ENOMEM;
//检查是否真正的8563
/* Verify the chip is really an PCF8563 */
if (pcf8563_validate_client(client) < 0) {
err = -ENODEV;
goto exit_kfree;
}
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
//注册设备
pcf8563->rtc = rtc_device_register(pcf8563_driver.driver.name,
&client->dev, &pcf8563_rtc_ops, THIS_MODULE);
if (IS_ERR(pcf8563->rtc)) {
err = PTR_ERR(pcf8563->rtc);
goto exit_kfree;
}
i2c_set_clientdata(client, pcf8563);
return 0;
exit_kfree:
kfree(pcf8563);
return err;
}
static int pcf8563_remove(struct i2c_client *client)
{
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
if (pcf8563->rtc)
rtc_device_unregister(pcf8563->rtc);
kfree(pcf8563);
return 0;
}
//支持两种芯片
static const struct i2c_device_id pcf8563_id[] = {
{ "pcf8563", 0 },
{ "rtc8564", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf8563_id);
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "rtc-pcf8563",
},
.probe = pcf8563_probe,
.remove = pcf8563_remove,
.id_table = pcf8563_id,
};
static int __init pcf8563_init(void)
{
return i2c_add_driver(&pcf8563_driver);
}
static void __exit pcf8563_exit(void)
{
i2c_del_driver(&pcf8563_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(pcf8563_init);
module_exit(pcf8563_exit);
另一个2.6.15内核的RTC驱动中就有相关的从地址定义
* linux/drivers/i2c/chips/rtc8564.c
*
* Copyright (C) 2002-2004 Stefan Eletzhofer
*
* based on linux/drivers/acron/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 system3's EPSON RTC 8564 chip
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/rtc.h> /* get the user-level API */
#include <linux/init.h>
#include "rtc8564.h"
#ifdef DEBUG
# define _DBG(x, fmt, args...) do{ if (debug>=x) printk(KERN_DEBUG"%s: " fmt "\n", __FUNCTION__, ##args); } while(0);
#else
# define _DBG(x, fmt, args...) do { } while(0);
#endif
#define _DBGRTCTM(x, rtctm) if (debug>=x) printk("%s: secs=%d, mins=%d, hours=%d, mday=%d, " \
"mon=%d, year=%d, wday=%d VL=%d\n", __FUNCTION__, \
(rtctm).secs, (rtctm).mins, (rtctm).hours, (rtctm).mday, \
(rtctm).mon, (rtctm).year, (rtctm).wday, (rtctm).vl);
struct rtc8564_data {
struct i2c_client client;
u16 ctrl;
};
static inline u8 _rtc8564_ctrl1(struct i2c_client *client)
{
struct rtc8564_data *data = i2c_get_clientdata(client);
return data->ctrl & 0xff;
}
static inline u8 _rtc8564_ctrl2(struct i2c_client *client)
{
struct rtc8564_data *data = i2c_get_clientdata(client);
return (data->ctrl & 0xff00) >> 8;
}
#define CTRL1(c) _rtc8564_ctrl1(c)
#define CTRL2(c) _rtc8564_ctrl2(c)
#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
static int debug;;
module_param(debug, int, S_IRUGO | S_IWUSR);
static struct i2c_driver rtc8564_driver;
static unsigned short ignore[] = { I2C_CLIENT_END };
//从器件地址0x51
static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.probe = ignore,
.ignore = ignore,
};
static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem);
static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem);
static int rtc8564_read(struct i2c_client *client, unsigned char adr,
unsigned char *buf, unsigned char len)
{
int ret = -EIO;
unsigned char addr[1] = { adr };
struct i2c_msg msgs[2] = {
{client->addr, 0, 1, addr},
{client->addr, I2C_M_RD, len, buf}
};
_DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, buf, len);
if (!buf) {
ret = -EINVAL;
goto done;
}
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret == 2) {
ret = 0;
}
done:
return ret;
}
static int rtc8564_write(struct i2c_client *client, unsigned char adr,
unsigned char *data, unsigned char len)
{
int ret = 0;
unsigned char _data[16];
struct i2c_msg wr;
int i;
if (!data || len > 15) {
ret = -EINVAL;
goto done;
}
_DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, data, len);
_data[0] = adr;
for (i = 0; i < len; i++) {
_data[i + 1] = data[i];
_DBG(5, "data[%d] = 0x%02x (%d)", i, data[i], data[i]);
}
wr.addr = client->addr;
wr.flags = 0;
wr.len = len + 1;
wr.buf = _data;
ret = i2c_transfer(client->adapter, &wr, 1);
if (ret == 1) {
ret = 0;
}
done:
return ret;
}
static int rtc8564_attach(struct i2c_adapter *adap, int addr, int kind)
{
int ret;
struct i2c_client *new_client;
struct rtc8564_data *d;
unsigned char data[10];
unsigned char ad[1] = { 0 };
struct i2c_msg ctrl_wr[1] = {
{addr, 0, 2, data}
};
struct i2c_msg ctrl_rd[2] = {
{addr, 0, 1, ad},
{addr, I2C_M_RD, 2, data}
};
d = kzalloc(sizeof(struct rtc8564_data), GFP_KERNEL);
if (!d) {
ret = -ENOMEM;
goto done;
}
new_client = &d->client;
strlcpy(new_client->name, "RTC8564", I2C_NAME_SIZE);
i2c_set_clientdata(new_client, d);
new_client->flags = I2C_CLIENT_ALLOW_USE;
new_client->addr = addr;
new_client->adapter = adap;
new_client->driver = &rtc8564_driver;
_DBG(1, "client=%p", new_client);
/* init ctrl1 reg */
data[0] = 0;
data[1] = 0;
ret = i2c_transfer(new_client->adapter, ctrl_wr, 1);
if (ret != 1) {
printk(KERN_INFO "rtc8564: cant init ctrl1\n");
ret = -ENODEV;
goto done;
}
/* read back ctrl1 and ctrl2 */
ret = i2c_transfer(new_client->adapter, ctrl_rd, 2);
if (ret != 2) {
printk(KERN_INFO "rtc8564: cant read ctrl\n");
ret = -ENODEV;
goto done;
}
d->ctrl = data[0] | (data[1] << 8);
_DBG(1, "RTC8564_REG_CTRL1=%02x, RTC8564_REG_CTRL2=%02x",
data[0], data[1]);
ret = i2c_attach_client(new_client);
done:
if (ret) {
kfree(d);
}
return ret;
}
static int rtc8564_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, rtc8564_attach);
}
static int rtc8564_detach(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
return 0;
}
static int rtc8564_get_datetime(struct i2c_client *client, struct rtc_tm *dt)
{
int ret = -EIO;
unsigned char buf[15];
_DBG(1, "client=%p, dt=%p", client, dt);
if (!dt)
return -EINVAL;
memset(buf, 0, sizeof(buf));
ret = rtc8564_read(client, 0, buf, 15);
if (ret)
return ret;
/* century stored in minute alarm reg */
dt->year = BCD_TO_BIN(buf[RTC8564_REG_YEAR]);
dt->year += 100 * BCD_TO_BIN(buf[RTC8564_REG_AL_MIN] & 0x3f);
dt->mday = BCD_TO_BIN(buf[RTC8564_REG_DAY] & 0x3f);
dt->wday = BCD_TO_BIN(buf[RTC8564_REG_WDAY] & 7);
dt->mon = BCD_TO_BIN(buf[RTC8564_REG_MON_CENT] & 0x1f);
dt->secs = BCD_TO_BIN(buf[RTC8564_REG_SEC] & 0x7f);
dt->vl = (buf[RTC8564_REG_SEC] & 0x80) == 0x80;
dt->mins = BCD_TO_BIN(buf[RTC8564_REG_MIN] & 0x7f);
dt->hours = BCD_TO_BIN(buf[RTC8564_REG_HR] & 0x3f);
_DBGRTCTM(2, *dt);
return 0;
}
static int
rtc8564_set_datetime(struct i2c_client *client, struct rtc_tm *dt, int datetoo)
{
int ret, len = 5;
unsigned char buf[15];
_DBG(1, "client=%p, dt=%p", client, dt);
if (!dt)
return -EINVAL;
_DBGRTCTM(2, *dt);
buf[RTC8564_REG_CTRL1] = CTRL1(client) | RTC8564_CTRL1_STOP;
buf[RTC8564_REG_CTRL2] = CTRL2(client);
buf[RTC8564_REG_SEC] = BIN_TO_BCD(dt->secs);
buf[RTC8564_REG_MIN] = BIN_TO_BCD(dt->mins);
buf[RTC8564_REG_HR] = BIN_TO_BCD(dt->hours);
if (datetoo) {
len += 5;
buf[RTC8564_REG_DAY] = BIN_TO_BCD(dt->mday);
buf[RTC8564_REG_WDAY] = BIN_TO_BCD(dt->wday);
buf[RTC8564_REG_MON_CENT] = BIN_TO_BCD(dt->mon) & 0x1f;
/* century stored in minute alarm reg */
buf[RTC8564_REG_YEAR] = BIN_TO_BCD(dt->year % 100);
buf[RTC8564_REG_AL_MIN] = BIN_TO_BCD(dt->year / 100);
}
ret = rtc8564_write(client, 0, buf, len);
if (ret) {
_DBG(1, "error writing data! %d", ret);
}
buf[RTC8564_REG_CTRL1] = CTRL1(client);
ret = rtc8564_write(client, 0, buf, 1);
if (ret) {
_DBG(1, "error writing data! %d", ret);
}
return ret;
}
static int rtc8564_get_ctrl(struct i2c_client *client, unsigned int *ctrl)
{
struct rtc8564_data *data = i2c_get_clientdata(client);
if (!ctrl)
return -1;
*ctrl = data->ctrl;
return 0;
}
static int rtc8564_set_ctrl(struct i2c_client *client, unsigned int *ctrl)
{
struct rtc8564_data *data = i2c_get_clientdata(client);
unsigned char buf[2];
if (!ctrl)
return -1;
buf[0] = *ctrl & 0xff;
buf[1] = (*ctrl & 0xff00) >> 8;
data->ctrl = *ctrl;
return rtc8564_write(client, 0, buf, 2);
}
static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem)
{
if (!mem)
return -EINVAL;
return rtc8564_read(client, mem->loc, mem->data, mem->nr);
}
static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem)
{
if (!mem)
return -EINVAL;
return rtc8564_write(client, mem->loc, mem->data, mem->nr);
}
static int
rtc8564_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
_DBG(1, "cmd=%d", cmd);
switch (cmd) {
case RTC_GETDATETIME:
return rtc8564_get_datetime(client, arg);
case RTC_SETTIME:
return rtc8564_set_datetime(client, arg, 0);
case RTC_SETDATETIME:
return rtc8564_set_datetime(client, arg, 1);
case RTC_GETCTRL:
return rtc8564_get_ctrl(client, arg);
case RTC_SETCTRL:
return rtc8564_set_ctrl(client, arg);
case MEM_READ:
return rtc8564_read_mem(client, arg);
case MEM_WRITE:
return rtc8564_write_mem(client, arg);
default:
return -EINVAL;
}
}
static struct i2c_driver rtc8564_driver = {
.owner = THIS_MODULE,
.name = "RTC8564",
.id = I2C_DRIVERID_RTC8564,
.flags = I2C_DF_NOTIFY,
.attach_adapter = rtc8564_probe,
.detach_client = rtc8564_detach,
.command = rtc8564_command
};
static __init int rtc8564_init(void)
{
return i2c_add_driver(&rtc8564_driver);
}
static __exit void rtc8564_exit(void)
{
i2c_del_driver(&rtc8564_driver);
}
MODULE_AUTHOR("Stefan Eletzhofer <Stefan.Eletzhofer@eletztrick.de>");
MODULE_DESCRIPTION("EPSON RTC8564 Driver");
MODULE_LICENSE("GPL");
module_init(rtc8564_init);
module_exit(rtc8564_exit);
这是一条镜像帖。来源:北邮人论坛 / embedded-system / #5076同步于 2009/6/11
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Embedded_System机器人发帖
关于I2C 芯片从地址的问题
jklbupt
2009/6/11镜像同步11 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
匆匆看了一眼,不是这个么?
//从器件地址0x51
static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.probe = ignore,
.ignore = ignore,
};
上边的程序分两部分,第一部分是2.6.27内核中的驱动,第二部分是2.6.15内核中的驱动(注解我添加的,这个没问题,为了说明问题来对照说明),而2.6.27中的I2C驱动结构有了较大更改,所以问题出在2.6.27驱动中的从地址.我一直怀疑2.6.27中的上边驱动不完善,现在也正在看.
这里??估计被包起来了吧,感觉2.6的内核里的驱动架构很好但包裹很严密
static const struct i2c_device_id pcf8563_id[] = {
{ "pcf8563", 0 },
{ "rtc8564", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf8563_id);
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "rtc-pcf8563",
},
.probe = pcf8563_probe,
.remove = pcf8563_remove,
.id_table = pcf8563_id,
};
in kernel 2.6.25 or later, you should find i2c addr in platform about file ,all the i2c chips and their addrs are registed together.and i2c is considered as a more independent bus type.
all the i2c chips and their addrs are registed together 是不是说设备驱动及从地址要一起注册给内核? 这句话从那儿引用来的?谢谢
下边是从网上找到的一个我用到的Rx8025的驱动,基本正确(但不符合最新I2C架构),有点小问题在调试中.通过addr_data,和normal_i2c注册给内核I2C从地址.
Index: linux/drivers/rtc/rtc-rx8025.c
===================================================================
--- linux/drivers/rtc/rtc-rx8025.c
+++ linux/drivers/rtc/rtc-rx8025.c
@@ -0,0 +1,560 @@
+/*
+ * drivers/rtc/rtc-rx8025.c
+ *
+ * Driver for Epson's RTC module RX-8025 SA/NB
+ *
+ * Copyright (C) 2005 by Digi International Inc.
+ * All rights reserved.
+ *
+ * Modify by fengjh at rising.com.cn
+ * 2006.11
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/rtc.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+
+/* registers */
+#define RX8025_REG_SEC 0x00
+#define RX8025_REG_MIN 0x01
+#define RX8025_REG_HOUR 0x02
+#define RX8025_REG_WDAY 0x03
+#define RX8025_REG_MDAY 0x04
+#define RX8025_REG_MONTH 0x05
+#define RX8025_REG_YEAR 0x06
+#define RX8025_REG_DIGOFF 0x07
+#define RX8025_REG_ALWMIN 0x08
+#define RX8025_REG_ALWHOUR 0x09
+#define RX8025_REG_ALWWDAY 0x0a
+#define RX8025_REG_ALDMIN 0x0b
+#define RX8025_REG_ALDHOUR 0x0c
+/* 0x0d is reserved */
+#define RX8025_REG_CTRL1 0x0e
+#define RX8025_REG_CTRL2 0x0f
+
+#define RX8025_BIT_CTRL1_1224 (1 << 5)
+#define RX8025_BIT_CTRL1_DALE (1 << 6)
+#define RX8025_BIT_CTRL1_WALE (1 << 7)
+
+#define RX8025_BIT_CTRL2_PON (1 << 4)
+#define RX8025_BIT_CTRL2_VDET (1 << 6)
+
+static unsigned short normal_i2c[] = { 0x32, I2C_CLIENT_END };
+I2C_CLIENT_INSMOD_1(rx8025);
+
+static int rx8025_attach_adapter(struct i2c_adapter *adapter);
+static int rx8025_detect(struct i2c_adapter *adapter, int address, int kind);
+static int rx8025_init_client(struct i2c_client *client);
+static int rx8025_detach_client(struct i2c_client *client);
+static struct i2c_driver rx8025_driver = {
+ .driver = {
+ .name = "rx8025",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = &rx8025_attach_adapter,
+ .detach_client = &rx8025_detach_client,
+};
+
+static struct i2c_client *rx8025_rtcclient = NULL;
+static int rx8025_get_rtctime(struct device *dev,struct rtc_time *dt);
+static int rx8025_set_rtctime(struct device *dev,struct rtc_time *dt);
+static struct rtc_class_ops rx8025_rtc_ops = {
+ .read_time = rx8025_get_rtctime,
+ .set_time = rx8025_set_rtctime,
+};
+struct rx8025_data {
+ struct i2c_client client;
+ struct list_head list;
+ struct rtc_device *rtc;
+};
+
+static LIST_HEAD(rx8025_clients);
+static DECLARE_MUTEX(rx8025_lock);
+
+static const unsigned char days_in_mo[] =
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static int rx8025_get_rtctime(struct device *dev,struct rtc_time *dt)
+{
+ u8 command = (RX8025_REG_CTRL1 << 4) | 0x08;
+ u8 result[9];
+ int i, err;
+
+ struct i2c_msg msg[] = {
+ {
+ .len = 1,
+ .buf = &command,
+ }, {
+ .flags = I2C_M_RD,
+ .len = ARRAY_SIZE(result),
+ .buf = result,
+ }
+ };
+
+ if (down_interruptible(&rx8025_lock))
+ return -ERESTARTSYS;
+
+ if (!rx8025_rtcclient) {
+ err = -EIO;
+ goto errout_unlock;
+ }
+
+ if (!dt) {
+ dev_err(&rx8025_rtcclient->dev,
+ "rx8025_get_rtctime: passed in dt == NULL\n");
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(msg); ++i)
+ msg[i].addr = rx8025_rtcclient->addr;
+
+ if ((err = i2c_transfer(rx8025_rtcclient->adapter, msg, 2)) < 2) {
+ err = err >= 0 ? -EIO : err;
+ goto errout_unlock;
+ }
+
+ up(&rx8025_lock);
+
+ dev_dbg(&rx8025_rtcclient->dev,
+ "rx8025_get_rtctime: read 0x%02x 0x%02x 0x%02x 0x%02x "
+ "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", result[0],
+ result[1], result[2], result[3], result[4], result[5],
+ result[6], result[7], result[8]);
+
+ dt->tm_sec = BCD2BIN(result[2 + RX8025_REG_SEC] & 0x7f);
+ dt->tm_min = BCD2BIN(result[2 + RX8025_REG_MIN] & 0x7f);
+ if (result[0] | RX8025_BIT_CTRL1_1224)
+ dt->tm_hour = BCD2BIN(result[2 + RX8025_REG_HOUR] & 0x3f);
+ else
+ dt->tm_hour = BCD2BIN(result[2 + RX8025_REG_HOUR] & 0x1f) % 12
+ + (result[2 + RX8025_REG_HOUR] & 0x20 ? 12 : 0);
+
+ dt->tm_wday = BCD2BIN(result[2 + RX8025_REG_WDAY] & 0x07);
+ dt->tm_mday = BCD2BIN(result[2 + RX8025_REG_MDAY] & 0x3f);
+ dt->tm_mon = BCD2BIN(result[2 + RX8025_REG_MONTH] & 0x1f) - 1;
+ dt->tm_year = BCD2BIN(result[2 + RX8025_REG_YEAR]);
+
+ if (dt->tm_year < 70)
+ dt->tm_year += 100;
+
+ dev_dbg(&rx8025_rtcclient->dev,
+ "rx8025_get_rtctime: "
+ "result: %ds %dm %dh %dwd %dmd %dm %dy\n",
+ dt->tm_sec, dt->tm_min, dt->tm_hour, dt->tm_wday,
+ dt->tm_mday, dt->tm_mon, dt->tm_year);
+
+ return 0;
+
+errout_unlock:
+ up(&rx8025_lock);
+ return err;
+}
+static int rx8025_set_rtctime(struct device *dev,struct rtc_time *dt)
+{
+ u8 command[8] = { RX8025_REG_SEC << 4, };
+ int err;
+ s32 ctrl1;
+
+ if (down_interruptible(&rx8025_lock))
+ return -ERESTARTSYS;
+
+ if (!rx8025_rtcclient) {
+ err = -EIO;
+ goto errout_unlock;
+ }
+
+ if (!dt) {
+ dev_err(&rx8025_rtcclient->dev,
+ "rx8025_set_rtctime: passed in dt == NULL\n");
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ if (dt->tm_sec < 0 || dt->tm_sec >= 60) {
+ dev_err(&rx8025_rtcclient->dev,
+ "rx8025_set_rtctime: seconds out of range: %d\n",
+ dt->tm_sec);
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ if (dt->tm_min < 0 || dt->tm_min >= 60) {
+ dev_err(&rx8025_rtcclient->dev,
+ "rx8025_set_rtctime: minutes out of range: %d\n",
+ dt->tm_min);
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ if (dt->tm_hour < 0 || dt->tm_hour >= 24) {
+ dev_err(&rx8025_rtcclient->dev,
+ "rx8025_set_rtctime: hours out of range: %d\n",
+ dt->tm_hour);
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ if (dt->tm_wday < 0 || dt->tm_wday >= 7) {
+ dev_err(&rx8025_rtcclient->dev,
+ "rx8025_set_rtctime: week day out of range: %d\n",
+ dt->tm_wday);
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ if (dt->tm_mon < 0 || dt->tm_mon >= 12) {
+ dev_err(&rx8025_rtcclient->dev,
+ "rx8025_set_rtctime: month out of range: %d\n",
+ dt->tm_mon);
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ if (dt->tm_year < 70 || dt->tm_year >= 170) {
+ dev_err(&rx8025_rtcclient->dev,
+ "rx8025_set_rtctime: year out of range: %d\n",
+ dt->tm_year);
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ /*
+ * BUG: The HW assumes every year that is a multiple of 4 to be a leap
+ * year. Next time this is wrong is 2100, which will not be a leap
+ * year.
+ */
+ if (dt->tm_mday > days_in_mo[dt->tm_mon] +
+ (!(dt->tm_year & 3) && (dt->tm_mon == 1))) {
+ dev_err(&rx8025_rtcclient->dev,
+ "%s: day out of range (with month=%d, year=%d): %d\n",
+ __FUNCTION__, dt->tm_mon, dt->tm_year, dt->tm_mday);
+ err = -EINVAL;
+ goto errout_unlock;
+ }
+
+ if ((ctrl1 = i2c_smbus_read_byte_data(rx8025_rtcclient,
+ RX8025_REG_CTRL1 << 4)) < 0) {
+ dev_err(&rx8025_rtcclient->dev,
+ "%s: failed to read out RX8025_REG_CTRL1\n",
+ __FUNCTION__);
+ err = -EIO;
+ goto errout_unlock;
+ }
+
+ dev_dbg(&rx8025_rtcclient->dev,
+ "%s: ctrl1=0x%x\n", __FUNCTION__, ctrl1);
+ /*
+ * Here the read-only bits are written as "0". I'm not sure if that
+ * is sound.
+ */
+ command[1 + RX8025_REG_SEC] = BIN2BCD(dt->tm_sec);
+ command[1 + RX8025_REG_MIN] = BIN2BCD(dt->tm_min);
+ if (ctrl1 & RX8025_BIT_CTRL1_1224)
+ command[1 + RX8025_REG_HOUR] = BIN2BCD(dt->tm_hour);
+ else
+ command[1 + RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0) |
+ BIN2BCD((dt->tm_hour + 11) % 12 + 1);
+
+ command[1 + RX8025_REG_WDAY] = BIN2BCD(dt->tm_wday);
+ command[1 + RX8025_REG_MDAY] = BIN2BCD(dt->tm_mday);
+ command[1 + RX8025_REG_MONTH] = BIN2BCD(dt->tm_mon + 1);
+ command[1 + RX8025_REG_YEAR] = BIN2BCD(dt->tm_year % 100);
+
+ dev_dbg(&rx8025_rtcclient->dev,
+ "%s: send 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
+ "0x%02x 0x%02x\n", __FUNCTION__, command[0],
+ command[1], command[2], command[3], command[4],
+ command[5], command[6], command[7]);
+
+ if ((err = i2c_master_send(rx8025_rtcclient, command, 8) < 8)) {
+ err = err >= 0 ? -EIO : err;
+ goto errout_unlock;
+ }
+
+ up(&rx8025_lock);
+
+ return 0;
+
+errout_unlock:
+ up(&rx8025_lock);
+ return err;
+}
+
+static int rx8025_attach_adapter(struct i2c_adapter *adapter)
+{
+ return i2c_probe(adapter, &addr_data, &rx8025_detect);
+}
+
+struct rx8025_limits {
+ u8 reg;
+ u8 mask;
+ u8 min;
+ u8 max;
+};
+
+static int rx8025_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ int err = 0;
+ struct rx8025_data *data;
+ struct i2c_client *client;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
+ dev_err(&adapter->dev, "doesn't support full I2C\n");
+ goto errout;
+ }
+
+ if (!(data = kzalloc(sizeof(*data), GFP_KERNEL))) {
+ dev_err(&adapter->dev, "failed to alloc memory\n");
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ client = &data->client;
+ i2c_set_clientdata(client, data);
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &rx8025_driver;
+
+ INIT_LIST_HEAD(&data->list);
+
+ if (kind == 0)
+ kind = rx8025;
+
+ if (kind < 0) {
+ u8 command = 0x08;
+ u8 res[RX8025_REG_YEAR + 1];
+ int i, err;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = address,
+ .len = 1,
+ .buf = &command,
+ }, {
+ .addr = address,
+ .flags = I2C_M_RD,
+ .len = ARRAY_SIZE(res),
+ .buf = res,
+ }
+ };
+
+ static const struct rx8025_limits limits[] = {
+ {
+ .reg = RX8025_REG_SEC,
+ .mask = 0x7f,
+ .min = 0,
+ .max = 59,
+ }, {
+ .reg = RX8025_REG_MIN,
+ .mask = 0x7f,
+ .min = 0,
+ .max = 59,
+ }, {
+ .reg = RX8025_REG_HOUR,
+ .mask = 0x3f,
+ .min = 0,
+ .max = 32,
+ }, {
+ .reg = RX8025_REG_WDAY,
+ .mask = 0x7f,
+ .min = 0,
+ .max = 6,
+ }, {
+ .reg = RX8025_REG_MDAY,
+ .mask = 0x3f,
+ .min = 1,
+ .max = 31,
+ }, {
+ .reg = RX8025_REG_MONTH,
+ .mask = 0x1f,
+ .min = 1,
+ .max = 12,
+ }, {
+ .reg = RX8025_REG_YEAR,
+ .mask = 0xff,
+ .min = 0,
+ .max = 99,
+ }
+ };
+
+ if ((err = i2c_transfer(adapter, msg, ARRAY_SIZE(msg)))
+ < ARRAY_SIZE(msg)) {
+ err = err >= 0 ? 0 : err;
+ goto errout_free;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(limits); ++i) {
+ u8 value;
+
+ if ((res[limits[i].reg] & ~limits[i].mask) ||
+ (limits[i].mask > 0x0f &&
+ (res[limits[i].reg] & 0x0f) > 9) ||
+ ((value = BCD2BIN(res[limits[i].reg]))
+ < limits[i].min) ||
+ value > limits[i].max) {
+ dev_dbg(&adapter->dev, "%s: register=0x%02x, "
+ "value=0x%02x\n", __FUNCTION__,
+ limits[i].reg,
+ res[limits[i].reg]);
+ err = -ENODEV;
+ goto errout_unlock;
+ }
+ }
+
+ kind = rx8025;
+ }
+
+ BUG_ON(kind != rx8025);
+
+ strlcpy(client->name, "rx8025", I2C_NAME_SIZE);
+
+ if (down_interruptible(&rx8025_lock)) {
+ err = -ERESTARTSYS;
+ goto errout_free;
+ }
+
+ if ((err = i2c_attach_client(client)))
+ goto errout_unlock;
+
+ list_add(&data->list, &rx8025_clients);
+
+ if ((err = rx8025_init_client(client)))
+ goto errout_detach;
+
+ if (!rx8025_rtcclient) {
+ rx8025_rtcclient = client;
+ data->rtc = rtc_device_register(client->name, &client->dev,
+ &rx8025_rtc_ops, THIS_MODULE);
+ if (IS_ERR(data->rtc)) {
+ rx8025_rtcclient = NULL;
+ dev_err(&client->dev,
+ "Failed to register rtc device\n");
+ }
+ }
+ up(&rx8025_lock);
+
+ dev_info(&client->dev, "chip found\n");
+
+ return 0;
+
+errout_detach:
+ rx8025_detach_client(client);
+
+errout_unlock:
+ up(&rx8025_lock);
+
+errout_free:
+ kfree(data);
+
+errout:
+ dev_err(&adapter->dev, "Failed to detect rx8025\n");
+ return err;
+}
+
+static int rx8025_init_client(struct i2c_client *client)
+{
+ u8 command[2] = { RX8025_REG_CTRL2 << 4 | 0x08, };
+ int err;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .len = 1,
+ .buf = command,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = ARRAY_SIZE(command) - 1,
+ .buf = command + 1,
+ }
+ };
+
+ if ((err = i2c_transfer(client->adapter, msg, 2)) < 2) {
+ err = err >= 0 ? -EIO : err;
+ goto errout;
+ }
+
+ if (command[1] & RX8025_BIT_CTRL2_PON)
+ dev_warn(&client->dev, "power-on reset was detected, "
+ "you may have to readjust the clock\n");
+
+ if (command[1] & RX8025_BIT_CTRL2_VDET)
+ dev_warn(&client->dev, "a power voltage drop was detected, "
+ "you may have to readjust the clock\n");
+
+ command[1] &= ~(RX8025_BIT_CTRL2_PON | RX8025_BIT_CTRL2_VDET);
+
+ command[0] = RX8025_REG_CTRL2 << 4;
+
+ if ((err = i2c_master_send(client, command, 2)) < 2) {
+ err = err >= 0 ? -EIO : err;
+ goto errout;
+ }
+
+ return 0;
+
+errout:
+ return err;
+}
+
+static int rx8025_detach_client(struct i2c_client *client)
+{
+ int err;
+ struct rx8025_data *data = i2c_get_clientdata(client);
+
+ if (down_interruptible(&rx8025_lock))
+ return -ERESTARTSYS;
+
+ BUG_ON(list_empty(&rx8025_clients));
+
+ if (rx8025_rtcclient == client) {
+ struct list_head *lh = rx8025_clients.next;
+
+ if (list_entry(lh, struct rx8025_data, list) == data)
+ lh = lh->next;
+
+ if (lh == &rx8025_clients) {
+ rtc_device_unregister(data->rtc);
+ rx8025_rtcclient = NULL;
+ } else
+ rx8025_rtcclient = &data->client;
+ }
+
+ if ((err = i2c_detach_client(client))) {
+ up(&rx8025_lock);
+ return err;
+ }
+
+ list_del(&data->list);
+
+ up(&rx8025_lock);
+ kfree(data);
+ return 0;
+}
+static int __init rx8025_init(void)
+{
+ return i2c_add_driver(&rx8025_driver);
+}
+
+static void __exit rx8025_exit(void)
+{
+ i2c_del_driver(&rx8025_driver);
+}
+
+MODULE_AUTHOR("Uwe Zeisberger <Uwe_Zeisberger <at> digi.com> and modify by fengjh at rising.com.cn");
+MODULE_DESCRIPTION("RX-8025 SA/NB RTC driver");
+MODULE_LICENSE("GPL");
+
+module_init(rx8025_init);
+module_exit(rx8025_exit);
这个是很早以前的版本了,在上上个月的时候,I2C就不需要自己写那么多detach之类的了,直接在probe中间可以获得client,然后就可以直接用了。你给的是用模块参数传I2C地址,现在的是在平台相关的文件中,有个i2c芯片信息的列表,你可以把东西填进去,然后内核会通过你注册的i2c的ID跟你的驱动匹配,匹配上了就BIND了。
上边最后边贴出的代码是针对2.6.17内核的,确实不符合最新架构,不过由于2.6.27内核具有向下兼容性也可以使用.
你所说的"现在的是在平台相关的文件中,有个i2c芯片信息的列表,你可以把东西填进去,然后内核会通过你注册的i2c的ID跟你的驱动匹配,匹配上了就BIND了。
" 平台相关的文件是否是目标板相关的文件?我也曾想过ID匹配问题,但在我的相关平台文件中都搜索过没有找到.能否帖出些具体源码以便交流分析,多谢!
【 在 zhangqingsup 的大作中提到: 】
: 这个是很早以前的版本了,在上上个月的时候,I2C就不需要自己写那么多detach之类的了,直接在probe中间可以获得client,然后就可以直接用了。你给的是用模块参数传I2C地址,现在的是在平台相关的文件中,有个i2c芯片信息的列表,你可以把东西填进去,然后内核会通过你注册的i2c的ID跟你的驱动匹配,匹配上了就BIND了。