1 修改bsp_以便支持probe
1.1 AT24C08地址的确定
原理图上将A2、A1、A0都接地了,所以地址是0x50。
注意到是7位(bit).
1.2 修改bsp
采用友善之臂的, 2.6.32.2内核
[root@localhost mach-s3c2440]# vim
/opt/FriendlyARM/mini2440/linux-2.6.32.2/arch/arm/mach-s3c2440/mach-mini2440.c
#include
static struct platform_device *mini2440_devices[] __initdata =
{
&s3c_device_usb,
&s3c_device_rtc,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0, //没有修改
&s3c_device_iis,
&mini2440_device_eth,
&s3c24xx_uda134x,
&s3c_device_nand,
&s3c_device_sdi,
&s3c_device_usbgadget,
}; //这里没有修改
static struct at24_platform_data at24c08 = {
.byte_len = SZ_8K / 8,
.page_size = 16,
}; //add
static struct i2c_board_info i2c_devices[] __initdata = {
{ I2C_BOARD_INFO("at24c08b", 0x50),
.platform_data = &at24c08, //不可少的
},
}; //add
#if 0
static struct i2c_board_info i2c_devices[] __initdata = {
{ I2C_BOARD_INFO("at24c08b", 0x50),
.irq=43, //不用.platform_data = &at24c08, 用这个也行
//从cat /proc/interrupt中可知
},
}; //TESTED BY awaken_ing#163
#endif
static void __init mini2440_machine_init(void)
{
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
//add
#if defined (LCD_WIDTH)
s3c24xx_fb_set_platdata(&mini2440_fb_info);
#endif
s3c_i2c0_set_platdata(NULL);
s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);
s3c_device_nand.dev.platform_data =
&friendly_arm_nand_info;
s3c_device_sdi.dev.platform_data =
&mini2440_mmc_cfg;
platform_add_devices(mini2440_devices,
ARRAY_SIZE(mini2440_devices));
s3c_pm_init();
}
然后make -j2 (传说的多任务, 这里是2个任务, 速度快点)进行编译内核
1.3 编译内核, 然后u-boot部分
#/opt/study_arm/u-boot-2009.11_ok_no_nand/tools/mkimage -A arm -O
linux -T kernel -C none -a 30008000 -e 30008040 -n linux_awaken -d
/opt/FriendlyARM/mini2440/linux-2.6.32.2/arch/arm/boot/zImage
/opt/study_arm/uImage_mini_2.img
#nfs 30008000
192.168.0.9:/opt/study_arm/uImage_mini_2.img
#setenv bootargs noinitrd root=/dev/nfs rw
nfsroot=192.168.0.9:/opt/FriendlyARM/mini2440/rootfs_qtopia_qt4/
ip=192.168.0.2:192.168.0.1::255.255.255.0 console=ttySAC0,115200
init=/linuxrc mem=64M
#bootm 0x30008000
1.4 启动linux后
[root@FriendlyARM /home]# ls /dev|grep i2
i2c
这是原先的
[root@FriendlyARM /home]# mknod /dev/at24c08b c 250 0
要mknod的, 不是insmod后产生的, (我傻傻地在这折腾了好久)
[root@FriendlyARM /home]# ls /dev|grep 24
at24c08b
tty24
2 驱动程序
#include #include
#include
#include
#include
#include
#include
#include
#include#include
#define AT24C08B_MAJOR 250
static int at24c08b_major = AT24C08B_MAJOR;struct at24c08b_dev
{
struct
i2c_client *client;
char
name[30];
unsigned
short current_pointer;
struct cdev
cdev;};
struct at24c08b_dev *at24c08b_devp;
static intat24c08b_open (struct inode *inode, struct file
*file)
{
file->private_data =
at24c08b_devp;
return
0;}
static ssize_tat24c08b_read (struct file *file, char *buf, size_t count, loff_t *
ppos)
{
int i =
0;
int
transferred = 0;
int ret,
my_buf[512];
struct
at24c08b_dev *dev = (struct at24c08b_dev *)
file->private_data;
dev->current_pointer =
*ppos;
if
(i2c_check_functionality
(dev->client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA))
{
while
(transferred <
count)
{
ret =i2c_smbus_read_byte_data
(dev->client,
dev->current_pointer +
i); my_buf[i++] = (unsigned short) ret;
transferred += 1;}
copy_to_user
(buf, (void *) my_buf,
transferred);
dev->current_pointer +=
transferred;
}
return
transferred;}
static ssize_tat24c08b_write (struct file *file, char *buf, size_t count, loff_t
* ppos)
{
int i =
0;
int
transferred = 0; int ret, my_buf[512];
struct at24c08b_dev *dev = (struct at24c08b_dev *)
file->private_data;dev->current_pointer =
*ppos;
if
(i2c_check_functionality (dev->client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA))
{
copy_from_user (my_buf, buf,
count);
while
(transferred <
count)
{
ret =i2c_smbus_write_byte_data
(dev->client,
dev->current_pointer +
i,
my_buf[i]);
i += 1;
transferred += 1;
}
dev->current_pointer +=
transferred;
}
return
transferred;}
static intat24c08b_ioctl (struct inode *inodep, struct file *file, unsigned
int cmd, unsigned long arg)
{
return
0;}
static intat24c08b_release (struct inode *inodep, struct file
*file)
{
file->private_data =
NULL;
return
0;}
static const struct file_operations at24c08b_fops = {.owner =
THIS_MODULE,
.open =
at24c08b_open,
.read =
at24c08b_read,
.write =
at24c08b_write,
.ioctl =
at24c08b_ioctl,
.release =
at24c08b_release,};
static voidat24c08b_setup_cdev (struct at24c08b_dev *dev, int
index)
{
int err,
devnum = MKDEV (at24c08b_major,
index);
cdev_init
(&dev->cdev,
&at24c08b_fops);
dev->cdev.owner =
THIS_MODULE;
err =
cdev_add (&dev->cdev, devnum,
1);
if
(err)
printk
(KERN_NOTICE "Error %d adding at24c08b %d", err,
index);}
static int __devinitat24c08b_probe (struct i2c_client *client, const struct
i2c_device_id *id)
{
int
ret;
printk
(KERN_NOTICE "at24c08b probe is
start\n"); //调试用,看是否执行了probe 函数 dev_t devnum = MKDEV (at24c08b_major, 0);
if (at24c08b_major)ret =
register_chrdev_region (devnum, 1,
"at24c08b");
else
{
ret =
alloc_chrdev_region (&devnum, 0, 1,
"at24c08b");
at24c08b_major = MAJOR
(devnum);
}
if (ret
< 0)
return
ret;
at24c08b_devp = kmalloc (sizeof (struct at24c08b_dev),
GFP_KERNEL);
if
(!at24c08b_devp)
{
ret =
-ENOMEM;
goto
fail_malloc;
} memset (at24c08b_devp, 0, sizeof (struct at24c08b_dev));
at24c08b_devp->client = client;
at24c08b_setup_cdev (at24c08b_devp, 0);
return 0;
fail_malloc: unregister_chrdev_region (devnum, 1);
return ret;}
static int __devexitat24c08b_remove (struct i2c_client
*client)
{
cdev_del
(&at24c08b_devp->cdev);
kfree
(at24c08b_devp); unregister_chrdev_region (MKDEV (at24c08b_major, 0), 1);
return 0;}
static const struct i2c_device_id at24c08b_id[] = {{"at24c08b",
0}, //这个0是不是有点奇怪啊, 呵呵
{}};
MODULE_DEVICE_TABLE (i2c, at24c08b_id);static struct i2c_driver at24c08b_driver =
{
.driver =
{
.name = "at24c08b",
.owner = THIS_MODULE,
},
.probe =
at24c08b_probe,
.remove =
__devexit_p
(at24c08b_remove),
.id_table =
at24c08b_id,};
static int __initat24c08b_init (void)
{
printk
(KERN_NOTICE "at24c08b is
insmod\n");
return
i2c_add_driver
(&at24c08b_driver);}
voidat24c08b_exit (void)
{
printk
(KERN_NOTICE "at24c08b is
rmmod\n");
i2c_del_driver
(&at24c08b_driver);}
MODULE_DESCRIPTION ("at24c08b eeprom driver");MODULE_LICENSE ("Dual
BSD/GPL");
MODULE_AUTHOR ("Weimeng Li "); //不是我, 我是awaken_inghttp://blog.163.com/awaken_ing/MODULE_VERSION ("V1.0");
module_param (at24c08b_major, int, S_IRUGO);
module_init (at24c08b_init);module_exit (at24c08b_exit);
3 用户程序
#include #include
#include
#include
#include#include
int main(int argc, char **argv){
int i;
unsigned int value[512];
value[0] = 0x12;
value[1] = 0x23;
value[2] = 0x34;
value[3] = 0x45;
value[4] = 0x56; value[5] = 0x67;
int fd;fd = open("/dev/at24c08b",
O_RDWR);
if(fd < 0)
{
printf("Open at24c08b Device
Faild!\n");
exit(1); }
write(fd, value, 6);for(i = 0; i < 6;
i++)
printf("write reg[%d] data: %x to at24c08\n", i,
value[i]);
printf("#########################################\n"); sleep(1);
read(fd, value, 6);for(i = 0; i < 6;
i++) printf("read reg[%d] data: %x to at24c08\n", i, value[i]);
close(fd);return
0;}
4 makefile
#Makefile#变量APP、DEV分别用于配置用户程序/驱动程序
*文件名*
APP=app_i2c
DEV=i2c_no_fops
ifneq ($(KERNELRELEASE),)
#call from kernel build
system
obj-m:=$(DEV).o
else
KERNELDIR
?=/opt/FriendlyARM/mini2440/linux-2.6.32.2
PWD :=$(shell pwd)
default:
$(MAKE) -C
$(KERNELDIR) M=$(PWD)
modules
cp $(DEV).ko
/opt/FriendlyARM/mini2440/rootfs_qtopia_qt4/home
#for
app,根据变量APP是否为空来处理
ifneq ($(APP),)
arm-linux-gcc -Wall $(APP).c -o
$(APP)
cp $(APP)
/opt/FriendlyARM/mini2440/rootfs_qtopia_qt4/home
endifendif
clean:rm -rf
*.ko
rm -rf
*.o rm -rf *.mod.*
#注意到$(APP).c会正确得到解析.
5 测试
[root@FriendlyARM /home]# mknod /dev/at24c08b c 250 0
[root@FriendlyARM /home]# insmod dev_i2c.ko
at24c08b is insmod
at24c08b probe is start
[root@FriendlyARM /home]# ./app_i2c
write reg[0] data: 12 to at24c08
write reg[1] data: 23 to at24c08
write reg[2] data: 34 to at24c08
write reg[3] data: 45 to at24c08
write reg[4] data: 56 to at24c08
write reg[5] data: 67 to at24c08
#########################################
read reg[0] data: 12 to at24c08
read reg[1] data: 23 to at24c08
read reg[2] data: 34 to at24c08
read reg[3] data: 45 to at24c08
read reg[4] data: 56 to at24c08
read reg[5] data: 67 to at24c08
参考文章
linux文档 Documentation/i2c/upgrading-clients