BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / embedded-system / #5076同步于 2009/6/11
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Embedded_System机器人发帖

关于I2C 芯片从地址的问题

jklbupt
2009/6/11镜像同步11 回复
最近在做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);
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
lester98机器人#1 · 2009/6/12
匆匆看了一眼,不是这个么? //从器件地址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, };
jklbupt机器人#2 · 2009/6/12
上边的程序分两部分,第一部分是2.6.27内核中的驱动,第二部分是2.6.15内核中的驱动(注解我添加的,这个没问题,为了说明问题来对照说明),而2.6.27中的I2C驱动结构有了较大更改,所以问题出在2.6.27驱动中的从地址.我一直怀疑2.6.27中的上边驱动不完善,现在也正在看.
lester98机器人#3 · 2009/6/12
这里??估计被包起来了吧,感觉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, };
zhangqingsup机器人#4 · 2009/6/13
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.
afeionline机器人#5 · 2009/6/14
pcf8563的地址是0xA2才是,其他的我不懂,呵呵
jklbupt机器人#6 · 2009/6/14
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);
zhangqingsup机器人#7 · 2009/6/15
这个是很早以前的版本了,在上上个月的时候,I2C就不需要自己写那么多detach之类的了,直接在probe中间可以获得client,然后就可以直接用了。你给的是用模块参数传I2C地址,现在的是在平台相关的文件中,有个i2c芯片信息的列表,你可以把东西填进去,然后内核会通过你注册的i2c的ID跟你的驱动匹配,匹配上了就BIND了。
jklbupt机器人#8 · 2009/6/15
上边最后边贴出的代码是针对2.6.17内核的,确实不符合最新架构,不过由于2.6.27内核具有向下兼容性也可以使用. 你所说的"现在的是在平台相关的文件中,有个i2c芯片信息的列表,你可以把东西填进去,然后内核会通过你注册的i2c的ID跟你的驱动匹配,匹配上了就BIND了。 " 平台相关的文件是否是目标板相关的文件?我也曾想过ID匹配问题,但在我的相关平台文件中都搜索过没有找到.能否帖出些具体源码以便交流分析,多谢! 【 在 zhangqingsup 的大作中提到: 】 : 这个是很早以前的版本了,在上上个月的时候,I2C就不需要自己写那么多detach之类的了,直接在probe中间可以获得client,然后就可以直接用了。你给的是用模块参数传I2C地址,现在的是在平台相关的文件中,有个i2c芯片信息的列表,你可以把东西填进去,然后内核会通过你注册的i2c的ID跟你的驱动匹配,匹配上了就BIND了。
lvcha机器人#9 · 2009/6/15
这么长啊 特别的赞