1 修改bsp_以便支持probe
1.1 AT24C08地址的确定
注意到是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 <linux/i2c.h>
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 驱动程序
/*
dev_i2c.c
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/delay.h>
#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 int
at24c08b_open (struct inode *inode, struct file *file)
{
file->private_data = at24c08b_devp;
return 0;
}
static ssize_t
at24c08b_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_t
at24c08b_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 int
at24c08b_ioctl (struct inode *inodep, struct file *file, unsigned int cmd, unsigned long arg)
{
return 0;
}
static int
at24c08b_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 void
at24c08b_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 __devinit
at24c08b_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 __devexit
at24c08b_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 __init
at24c08b_init (void)
{
printk (KERN_NOTICE "at24c08b is insmod\n");
return i2c_add_driver (&at24c08b_driver);
}
void
at24c08b_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 <pursuitxh@163.com>"); //不是我, 我是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 用户程序
/*app_i2c.c*/
#include <stdio.h>
#include <linux/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
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
endif
endif
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
转载请注明出处 http://blog.163.com/awaken_ing/
参考文章
linux文档 Documentation/i2c/upgrading-clients
I2C in the 2.6.32 Linux Kernel http://www.embedded-bits.co.uk/2009/i2c-in-the-2632-linux-kernel/