设备树学习之(八)eeprom

来源:互联网 发布:淘宝差价链接怎么做 编辑:程序博客网 时间:2024/06/05 19:04

开发板:tiny4412SDK + S702 + 4GB Flash
要移植的内核版本:Linux-4.4.0 (支持device tree)
u-boot版本:友善之臂自带的 U-Boot 2010.12
busybox版本:busybox 1.25

目标:
驱动 tiny4412 底板上的 i2c eeprom ,使用字符设备进行读写。

原理图:
这里写图片描述
设备地址为 0x50

设备树:

&i2c_0{    eeprom@50 {//它对应于driverid_table中的name        compatible = "tiny4412,eeprom";        reg = <0x50>;    };};

代码:

#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/i2c.h>#include <linux/err.h>#include <linux/regmap.h>#include <linux/slab.h>#include <linux/fs.h>#include <asm/uaccess.h>#include <linux/delay.h>#define uchar unsigned char#define mydebug() printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__)/*    24AA025E48    2K bit 只有低 1K bit可用    因此可以使用的范围 0 - 128 byte    一页 16bytes    支持写单字节,整页写操作    支持单字节读,连续读取*/static int major;static struct class *class;static struct i2c_client *at24cxx_client;/* 传入: buf[0] : addr * 输出: buf[1] : len */static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t count, loff_t *off){    int ret, i;    unsigned char addr, len, data[2];    unsigned char *readbuf;    struct i2c_msg msg[2];    if (count != 2)    {        printk("%s count invalid \n", __func__);        return -EINVAL;    }    ret = copy_from_user(data, buf, 2);    if (ret < 0)    {        printk("%s copy_from_user error\n", __func__);    }    addr = data[0];    len  = data[1];    readbuf = kzalloc(len, GFP_KERNEL);    if (addr + len - 1 >= 128)    {        printk("%s write addr len invalid \n", __func__);        return -EINVAL;    }    if (len == 0)    {        return 0;    }    else    {        readbuf[0] = i2c_smbus_read_byte_data(at24cxx_client, addr);        mdelay(20);        addr += 1;        /* 读AT24CXX时,要先把要读的存储空间的地址发给它 */        msg[0].addr  = at24cxx_client->addr;    /* 目的 */        msg[0].buf   = &addr;                   /* 源 */        msg[0].len   = 1;                       /* 地址=1 byte */        msg[0].flags = 0;                       /* 表示写 */        /* 然后启动读操作 */        msg[1].addr  = at24cxx_client->addr;    /* 源 */        msg[1].buf   = readbuf + 1;             /* 目的 */        msg[1].len   = len - 1;                 /* 数据=1 byte */        msg[1].flags = I2C_M_RD;                /* 表示读 */        ret = i2c_transfer(at24cxx_client->adapter, msg, 2);        if (ret != 2)        {            printk("%s i2c_transfer error \n", __func__);            return -EINVAL;        }    }    if (data < 0)    {        printk("%s data read  error\n", __func__);    }    ret = copy_to_user(buf + 2, readbuf, len);    if (ret < 0)    {        printk("%s copy_from_user error\n", __func__);    }    kfree(readbuf);    return count;}static void calHead(uchar align, uchar start, uchar len, uchar *hstart, uchar *hlen){    if (start % align + len <= align)    //长度很短,不跨段    {        *hlen   = len;        *hstart = start;        return;    }    if (start % align == 0) //没有零散头部    {        *hlen   = 0;        *hstart = start;    }    else                    //有零散头部    {        //(start % align)           范围 0 - align-1        //align - (start % align);  范围 1 - align,start:0 <-> align        *hlen   = align - (start % align);        *hstart = start;    }}static void calMiddle(uchar align, uchar start, uchar len, uchar *mstart, uchar *mlen, uchar *num){    uchar hstart, hlen;    calHead(align, start, len, &hstart, &hlen);    *mstart = hstart + hlen;    *num    = (len - hlen) / align;    *mlen   = ((len - hlen) / align) * align;}static void calEnd(uchar align, uchar start, uchar len, uchar *estart, uchar *elen){    uchar hstart, hlen, mstart, mlen, num;    calHead(align, start, len, &hstart, &hlen);    calMiddle(align, start, len, &mstart, &mlen, &num);    *estart = mstart + mlen;    *elen   = len - hlen - mlen;}/* buf[0] : addr_start * buf[1] : len * buf[n] : data */static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off){    int ret, i;    unsigned char addr, len;    unsigned char pagebuf[17];    unsigned char hstart, hlen, mstart, mlen, num, estart, elen;    unsigned char *data = kzalloc(count, GFP_KERNEL);    struct i2c_msg msg;    if (count < 3)    {        printk("%s count invalid \n", __func__);        return -EINVAL;    }    ret = copy_from_user(data, buf, count);    if (ret < 0)    {        printk("%s copy_from_user error\n", __func__);    }    addr = data[0];    len  = data[1];    printk("addr %d len %d\n", addr, len);    if (addr + len - 1 >= 128)    {        printk("%s write addr len invalid \n", __func__);        return -EINVAL;    }    calHead(  16, addr, len, &hstart, &hlen);    calMiddle(16, addr, len, &mstart, &mlen, &num);    calEnd(   16, addr, len, &estart, &elen);    for (i = hstart; i < hstart + hlen; i++)    {        if (i2c_smbus_write_byte_data(at24cxx_client, i, data[2 + i - addr]) < 0)        {            printk("%s i2c_smbus_write_byte_data %d \n", __func__, i);            return -EINVAL;        }        mdelay(5);    }    for (i = mstart; i < mstart + mlen; i += 16)    {        memset(pagebuf, i, 1);  //第一个字节为要写入的地址        memcpy(pagebuf + 1, data + 2 + i - addr, 16);        msg.addr  = at24cxx_client->addr;       /* 设备地址 */        msg.buf   = pagebuf;                    /* 源 */        msg.len   = 17;                         /* 地址+数据=17 byte */        msg.flags = 0;                          /* 表示写 */        ret = i2c_transfer(at24cxx_client->adapter, &msg, 1);        if (ret != 1)        {            printk("%s i2c_transfer error \n", __func__);            return -EINVAL;        }        mdelay(5);    }    for (i = estart; i < estart + elen; i++)    {        if (i2c_smbus_write_byte_data(at24cxx_client, i, data[2 + i - addr]) < 0)        {            printk("%s i2c_smbus_write_byte_data %d \n", __func__, i);            return -EINVAL;        }        mdelay(5);    }    kfree(data);    return count;}static struct file_operations at24cxx_fops ={    .owner = THIS_MODULE,    .read  = at24cxx_read,    .write = at24cxx_write,};static int at24cxx_probe(struct i2c_client *client,                         const struct i2c_device_id *id){    at24cxx_client = client;    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);    major = register_chrdev(0, "at24cxx", &at24cxx_fops);    class = class_create(THIS_MODULE, "at24cxx");    device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */    return 0;}static int at24cxx_remove(struct i2c_client *client){    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);    device_destroy(class, MKDEV(major, 0));    class_destroy(class);    unregister_chrdev(major, "at24cxx");    return 0;}static const struct i2c_device_id at24cxx_id_table[] ={    { "eeprom", 0 },    {}};/* 1. 分配/设置i2c_driver */static struct i2c_driver at24cxx_driver ={    .driver = {        .name   = "eeprom",        .owner  = THIS_MODULE,    },    .probe      = at24cxx_probe,    .remove     = at24cxx_remove,    .id_table   = at24cxx_id_table,};static int at24cxx_drv_init(void){    /* 2. 注册i2c_driver */    i2c_add_driver(&at24cxx_driver);    return 0;}static void at24cxx_drv_exit(void){    i2c_del_driver(&at24cxx_driver);}module_init(at24cxx_drv_init);module_exit(at24cxx_drv_exit);MODULE_LICENSE("GPL");
1 0
原创粉丝点击