I2c驱动小结

I2c驱动小结

前段时间由于工作需要,使用atmeld20g18,cortex-m0,上面有I2C的控制器,atmel提供了SDK,开发速度很快,由于个人的好奇心,也顺便看了下linux下的I2C驱动框架,如下文所示,打个标记。

Linux下的I2C驱动主要的数据结构是围绕i2c_adapter,对于ARM上来说一个i2c控制器就对应一个adapter,如果一个控制器上有多个i2c设备则每个设备就是一个client,所以一个adapter可以对应多个client。

struct i2c_adapter {

……

         const struct i2c_algorithm*algo; /* the algorithm to access the bus,I2C总线算法 */

         struct device dev;        /* the adapter device */

……

};

struct i2c_algorithm {
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
               unsigned short flags, char read_write,
               u8 command, int size, union i2c_smbus_data *data);
};

struct i2c_client {
    unsigned short addr;        /* chip address - NOTE: 7bit    *///外部I2C设备的地址
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter;    /* the adapter we sit on    */
    struct device dev;        /* the device structure        */
};

 

一.字符设备驱动程序

1.1基本的数据结构

I2c属于字符设备,字符设备的操作函数如下:

staticconst struct file_operations i2cdev_fops = {

         .owner               =THIS_MODULE,

         .llseek                =no_llseek,

         .read                  =i2cdev_read,

         .write                 =i2cdev_write,

         .unlocked_ioctl         = i2cdev_ioctl,

         .open                 =i2cdev_open,

         .release   = i2cdev_release,

};

总线bus是i2c_bus_type,定义如下:

structbus_type i2c_bus_type = {

         .name                ="i2c",

         .match               =i2c_device_match,

         .probe                =i2c_device_probe,

         .remove            =i2c_device_remove,

         .shutdown        = i2c_device_shutdown,

};

字符设备的注册

int __init i2c_dev_init(void)

{

         res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");

         i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");

             /* Bind to already existing adapters right away */
         i2c_for_each_dev(NULL, i2cdev_attach_adapter);//遍历所有的adapter,即有多个I2C控制器

}

static int i2cdev_attach_adapter(struct device *dev, void *dummy)

{

      struct i2c_adapter *adap;
      struct i2c_dev *i2c_dev;

      adap = to_i2c_adapter(dev);

     i2c_dev = get_free_i2c_dev(adap);

    cdev_init(&i2c_dev->cdev, &i2cdev_fops);//注册字符设备,即字符设备的操作函数

    i2c_dev->cdev.owner = THIS_MODULE;
    res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);

//此处注意I2C设备的次设备号adap->nr, adap->nr的出处就是下文中提到的

//    i2c->adap.nr = i2c->pdata->bus_num; 然后该adapter->nr通过idr_alloc注册到radix tree 里面去

//   id = idr_alloc(&i2c_adapter_idr,adap, adap->nr, adap->nr + 1, GFP_KERNEL);

//i2cdev_open()函数根据次设备号找到adapter

    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                     MKDEV(I2C_MAJOR, adap->nr), NULL,
                     "i2c-%d", adap->nr);

}

 

1.2设备的打开

I2C设备的打开:

static int i2cdev_open(struct inode *inode, struct file *file)

//inode可以根据设备节点找到

{

         unsigned int minor = iminor(inode);

         struct i2c_client *client;

         struct i2c_adapter *adap;

 

         adap = i2c_get_adapter(minor);//根据次设备号找到内存中的i2c_adapter

         if (!adap)

                   return -ENODEV;

         client = kzalloc(sizeof(*client),GFP_KERNEL);//分配设备的i2c_client

         if (!client) {

                   i2c_put_adapter(adap);

                   return -ENOMEM;

         }

         snprintf(client->name,I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

 

         client->adapter = adap;//连接client和i2c_adapter

         file->private_data = client;//该值保存,被所有的file_operations共享。

         return 0;

}

 

structi2c_adapter *i2c_get_adapter(int nr)

{

         struct i2c_adapter *adapter;

 

         mutex_lock(&core_lock);

         adapter = idr_find(&i2c_adapter_idr,nr);//根据次设备号找到adapter, idr数据结构类似于一种map映射,根据索引找到adapter的值。

         if (!adapter)

                   goto exit;

 

         if (try_module_get(adapter->owner))

                   get_device(&adapter->dev);

         else

                   adapter = NULL;

 

 exit:

         mutex_unlock(&core_lock);

         return adapter;

}

1.3设备参数设置

staticlong i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

//该函数完成配置参数的作用,如连接的I2C设备的地址等。

{

         struct i2c_client *client =file->private_data;

         unsigned long funcs;

         dev_dbg(&client->adapter->dev,"ioctl, cmd=0x%02x, arg=0x%02lx\n",

                   cmd, arg);

 

         switch (cmd) {

         case I2C_SLAVE:

         case I2C_SLAVE_FORCE:

                   if ((arg > 0x3ff) ||

                       (((client->flags & I2C_M_TEN) == 0)&& arg > 0x7f))

                            return -EINVAL;

                   if (cmd == I2C_SLAVE&& i2cdev_check_addr(client->adapter, arg))

                            return -EBUSY;

                   client->addr = arg;  //设备地址的配置

                   return 0;

         case I2C_TENBIT:

                   if (arg)

                            client->flags |=I2C_M_TEN;

                   else

                            client->flags&= ~I2C_M_TEN;

                   return 0;

         case I2C_PEC:

                   if (arg)

                            client->flags |=I2C_CLIENT_PEC;

                   else

                            client->flags&= ~I2C_CLIENT_PEC;

                   return 0;

         case I2C_FUNCS:

                   funcs =i2c_get_functionality(client->adapter);

                   return put_user(funcs,(unsigned long __user *)arg);

 

         case I2C_RDWR:

                   returni2cdev_ioctl_rdwr(client, arg);

 

         case I2C_SMBUS:

                   return i2cdev_ioctl_smbus(client,arg);

 

         case I2C_RETRIES:

                   client->adapter->retries= arg;

                   break;

         case I2C_TIMEOUT:

                   client->adapter->timeout= msecs_to_jiffies(arg * 10);

                   break;

         default:

                   return -ENOTTY;

         }

         return 0;

}

1.4设备数据读取

staticssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,

                   loff_t *offset)

{

         char *tmp;

         int ret;

 

         struct i2c_client *client =file->private_data; //该值在i2cdev_open()函数中被分配。

 

         if (count > 8192)

                   count = 8192;

 

         tmp = kmalloc(count, GFP_KERNEL);

         if (tmp == NULL)

                   return -ENOMEM;

 

         pr_debug("i2c-dev: i2c-%d reading%zu bytes.\n",

                   iminor(file_inode(file)),count);

 

         ret = i2c_master_recv(client, tmp,count);//内部转换到adapter的i2c_algorithm *algo

         if (ret >= 0)

                   ret = copy_to_user(buf, tmp,count) ? -EFAULT : ret;

         kfree(tmp);

         return ret;

}

 

二.设备及驱动的注册

I2c控制器集成在SOC芯片内部,因此使用SOC内部I2C控制器,是作为平台设备注册的。

staticint __init i2c_init(void)

{

         int retval;

           …………

         retval =bus_register(&i2c_bus_type);//注册总线

         if (retval)

                   return retval;

…………

}

 

 

structplatform_device s3c_device_i2c = {

         .name                  = "s3c2410-i2c",

         .id                = -1,

         .num_resources         =ARRAY_SIZE(s3c_i2c_resource),

         .resource   = s3c_i2c_resource,

};

 

staticstruct platform_driver s3c24xx_i2c_driver = {

         .probe                =s3c24xx_i2c_probe,

         .remove            =s3c24xx_i2c_remove,

         .id_table  = s3c24xx_driver_ids,

         .driver                ={

                   .name       = "s3c-i2c",

                   .pm  = S3C24XX_DEV_PM_OPS,

                   .of_match_table =of_match_ptr(s3c24xx_i2c_match),

         },

};

 

 

staticint s3c24xx_i2c_probe(struct platform_device *pdev)

{

         struct s3c24xx_i2c *i2c;

         struct s3c2410_platform_i2c *pdata =NULL;

         struct resource *res;

         int ret;

 

         if (!pdev->dev.of_node) {

                   pdata =dev_get_platdata(&pdev->dev);

                   if (!pdata) {

                            dev_err(&pdev->dev,"no platform data\n");

                            return -EINVAL;

                   }

         }

 

         i2c = devm_kzalloc(&pdev->dev,sizeof(struct s3c24xx_i2c), GFP_KERNEL);

//分配s3c24xx_i2c,内部包含了i2c_adapter

         if (!i2c)

                   return -ENOMEM;

 

         i2c->pdata =devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);

         if (!i2c->pdata)

                   return -ENOMEM;

 

         i2c->quirks = s3c24xx_get_device_quirks(pdev);

         i2c->sysreg = ERR_PTR(-ENOENT);

         if (pdata)

                   memcpy(i2c->pdata, pdata,sizeof(*pdata));

         else

                   s3c24xx_i2c_parse_dt(pdev->dev.of_node,i2c);

 

         strlcpy(i2c->adap.name,"s3c2410-i2c", sizeof(i2c->adap.name));

         i2c->adap.owner = THIS_MODULE;

         i2c->adap.algo =&s3c24xx_i2c_algorithm;  //实际I2C设备的实际操作算法

         i2c->adap.retries = 2;

         i2c->adap.class =I2C_CLASS_DEPRECATED;

         i2c->tx_setup = 50;

 

         init_waitqueue_head(&i2c->wait);

 

         /* find the clock and enable it */

         i2c->dev = &pdev->dev;

         i2c->clk =devm_clk_get(&pdev->dev, "i2c");

         if (IS_ERR(i2c->clk)) {

                   dev_err(&pdev->dev,"cannot get clock\n");

                   return -ENOENT;

         }

 

         dev_dbg(&pdev->dev, "clocksource %p\n", i2c->clk);

 

         /* map the registers */

         res = platform_get_resource(pdev,IORESOURCE_MEM, 0);

         i2c->regs =devm_ioremap_resource(&pdev->dev, res);

 

         if (IS_ERR(i2c->regs))

                   return PTR_ERR(i2c->regs);

 

         dev_dbg(&pdev->dev,"registers %p (%p)\n",

                   i2c->regs, res);

 

         /* setup info block for the i2c core */

         i2c->adap.algo_data = i2c;

         i2c->adap.dev.parent =&pdev->dev;

         i2c->pctrl =devm_pinctrl_get_select_default(i2c->dev);

 

         /* inititalise the i2c gpio lines */

         if (i2c->pdata->cfg_gpio)

                   i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));

         else if (IS_ERR(i2c->pctrl)&& s3c24xx_i2c_parse_dt_gpio(i2c))

                   return -EINVAL;

 

         /* initialise the i2c controller */

         ret = clk_prepare_enable(i2c->clk);

         if (ret) {

                   dev_err(&pdev->dev,"I2C clock enable failed\n");

                   return ret;

         }

 

         ret = s3c24xx_i2c_init(i2c); //初始化设备

         clk_disable(i2c->clk);

         if (ret != 0) {

                   dev_err(&pdev->dev,"I2C controller init failed\n");

                   clk_unprepare(i2c->clk);

                   return ret;

         }

 

         if (!(i2c->quirks & QUIRK_POLL)){

                   i2c->irq = ret =platform_get_irq(pdev, 0);

                   if (ret <= 0) {

                            dev_err(&pdev->dev,"cannot find IRQ\n");

                            clk_unprepare(i2c->clk);

                            return ret;

                   }

 

                   ret =devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq,

                                            0, dev_name(&pdev->dev), i2c);

                   if (ret != 0) {

                            dev_err(&pdev->dev,"cannot claim IRQ %d\n", i2c->irq);

                            clk_unprepare(i2c->clk);

                            return ret;

                   }

         }

 

         ret =s3c24xx_i2c_register_cpufreq(i2c);

         if (ret < 0) {

                   dev_err(&pdev->dev,"failed to register cpufreq notifier\n");

                   clk_unprepare(i2c->clk);

                   return ret;

         }

 

         i2c->adap.nr =i2c->pdata->bus_num;

///这个bus_num,如果bus_num=-1, 会在下面的i2c_add_numbered_adapter()中动态分配,nr会通过idr_alloc注册到radix tree上

         i2c->adap.dev.of_node =pdev->dev.of_node;

 

         platform_set_drvdata(pdev, i2c);

 

         pm_runtime_enable(&pdev->dev);

 

         ret =i2c_add_numbered_adapter(&i2c->adap); 

         if (ret < 0) {

                   pm_runtime_disable(&pdev->dev);

                   s3c24xx_i2c_deregister_cpufreq(i2c);

                   clk_unprepare(i2c->clk);

                   return ret;

         }

 

         dev_info(&pdev->dev, "%s:S3C I2C adapter\n", dev_name(&i2c->adap.dev));

         return 0;

}

 

inti2c_add_numbered_adapter(struct i2c_adapter *adap)

{

         if (adap->nr == -1) /* -1 meansdynamically assign bus id *///此处即上文提到的动态分配adap->nr ,

                   return i2c_add_adapter(adap);

 

         return__i2c_add_numbered_adapter(adap);

}

 

staticint __i2c_add_numbered_adapter(struct i2c_adapter *adap)

{

         int id;

 

         mutex_lock(&core_lock);

         id = idr_alloc(&i2c_adapter_idr,adap, adap->nr, adap->nr + 1, GFP_KERNEL);

//前文i2c_get_adapter(intnr)中根据次设备号找到的adapter是从这边注册的, adap->nr会被注册为i2c设备的次设备号;

         mutex_unlock(&core_lock);

         if (WARN(id < 0, "couldn't getidr"))

                   return id == -ENOSPC ? -EBUSY: id;

 

         return i2c_register_adapter(adap);  //注册adapter

}

 

static int i2c_register_adapter(struct i2c_adapter *adap)

{

…………

dev_set_name(&adap->dev,"i2c-%d", adap->nr);

adap->dev.bus= &i2c_bus_type;                             

adap->dev.type= &i2c_adapter_type;

res =device_register(&adap->dev);   //注册设备

         if (res) {

                   pr_err("adapter '%s':can't register device (%d)\n", adap->name, res);

                   goto out_list;

         }

…………

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值