linux内核I2C总线驱动(一)

一般外面讲的i2c都是怎么去写驱动端和设备端,我们今天来详细说明一下,整个的框架,从调用soc内部的地址开始,告诉你是怎么来的,应该怎么修改,去适配自己的soc

开始吧!

 

主要学习的是分析代码的思路

 

 

直接看源码太麻烦了,我们先看一下soc手册怎么说的,以我手头的6818为例子

手册下载中。。。load。。。

我们按照这个总线地址去源码追一下看看

kernel-3.4.39\arch\arm\mach-s5p6818\include\mach\s5p6818.h

#define PHY_BASEADDR_I2C0           (0xC00A4000)

#define PHY_BASEADDR_I2C1           (0xC00A5000)

#define PHY_BASEADDR_I2C2           (0xC00A6000)

 

那么我们看看都有谁调用了这些宏

\kernel-3.4.39\arch\arm\mach-s5p6818\devices.c

static struct resource s3c_i2c0_resource[] = {

       [0] = DEFINE_RES_MEM(PHY_BASEADDR_I2C0, SZ_256),

       [1] = DEFINE_RES_IRQ(IRQ_PHY_I2C0),

};

\kernel-3.4.39\arch\arm\mach-s5p6818\devices.c

static struct resource s3c_i2c1_resource[] = {

       [0] = DEFINE_RES_MEM(PHY_BASEADDR_I2C1, SZ_4K),

       [1] = DEFINE_RES_IRQ(IRQ_PHY_I2C1),

};

这里呼啦呼啦一套宏,其实很简单目的就是给struct resource结构体赋值

struct resource {

       resource_size_t start;

       resource_size_t end;

       const char *name;

       unsigned long flags;

       struct resource *parent, *sibling, *child;

};

相当于在这个位置,记录了i2c总线的开始地址,结束地址,大小,和名字

但是struct resource *parent, *sibling, *child;这部分没有初始化还

这里的标志是

kernel-3.4.39\include\linux\ ioport.h

#define IORESOURCE_MEM       0x00000200

IORESOURCE_MEM 指的是属于外设或者用于和设备通讯的支持直接寻址的地址空间

 

我们已经拥有了设备信息,然后呢,是谁使用了这个

s3c_i2c1_resource

 

kernel-3.4.39\arch\arm\mach-s5p6818\ devices.c

static struct platform_device i2c_device_ch1 = {

       //.name  = DEV_NAME_I2C,

       .name = "s3c2440-i2c",

       .id           = 1,

       .num_resources  = ARRAY_SIZE(s3c_i2c1_resource),

       .resource   = s3c_i2c1_resource,

       .dev    = {

              .platform_data       = &i2c_data_ch1

       },

};

开始啦,开始啦,开始走平台总线模型啦

 

kernel-3.4.39\include\linux\ platform_device.h

struct platform_device {

       const char      * name;

       int          id;

       struct device  dev;

       u32         num_resources;

       struct resource      * resource;

 

       const struct platform_device_id   *id_entry;

 

       /* MFD cell pointer */

       struct mfd_cell *mfd_cell;

 

       /* arch specific additions */

       struct pdev_archdata    archdata;

};

 

这里把,名称初始化为 "s3c2440-i2c",id= 1,num_resources是地址大小,resource是总线地址信息,.dev的platform_data初始化为&i2c_data_ch1

没有信息的是

const struct platform_device_id   *id_entry;

struct mfd_cell *mfd_cell;

struct pdev_archdata    archdata;

 

 

\kernel-3.4.39\arch\arm\mach-s5p6818\ devices.c

void i2c_cfg_gpio1(struct platform_device *dev)

{

       nxp_soc_gpio_set_io_func( I2C1_SCL & 0xff, 1);

       nxp_soc_gpio_set_io_func( I2C1_SDA & 0xff, 1);

 

}  设置引脚复用

struct s3c2410_platform_i2c i2c_data_ch1 = {

       .bus_num      = 1,

    .flags      = 0,

    .slave_addr = 0x10,

    .frequency  = CFG_I2C1_CLK,

    .sda_delay  = 100,

       .cfg_gpio = i2c_cfg_gpio1,

};

 

其实这里信息已经比较齐全了,我们继续追i2c_device_ch1是被谁调用的

kernel-3.4.39\arch\arm\mach-s5p6818\ devices.c

static struct platform_device *i2c_devices[] = {

       #if   defined(CONFIG_I2C_NXP_PORT0)

       &i2c_device_ch0,

       #endif

       #if   defined(CONFIG_I2C_NXP_PORT1)

       &i2c_device_ch1,

       #endif

       #if   defined(CONFIG_I2C_NXP_PORT2)

       &i2c_device_ch2,

       #endif

};

绕来绕去封了好几层,其实就是为了整合,这里是确认哪些通道被打开了

我们这里出现了两个调用

kernel-3.4.39\arch\arm\plat-s5p6818\topeet\device.c

#if defined(CONFIG_I2C_NXP) || defined (CONFIG_I2C_SLSI)

    platform_add_devices(i2c_devices, ARRAY_SIZE(i2c_devices));

#endif

 

\kernel-3.4.39\arch\arm\mach-s5p6818\devices.c

#if defined(CONFIG_I2C_NXP) || defined(CONFIG_I2C_SLSI)

    printk("mach: add device i2c bus (array:%d) \n", ARRAY_SIZE(i2c_devices));

    platform_add_devices(i2c_devices, ARRAY_SIZE(i2c_devices));

      

这里一下子注册了三个平台总线

其实是循环调用platform_device_register实现的注册,名称很直白“平台设备注册”

这里平台部分其实就告一段落了,然后是匹配了提供接口呢

我们知道是靠这。Name去匹配的,那么我们去找找驱动端被

 

\kernel-3.4.39\drivers\i2c\busses\ i2c-s3c2410.c

static struct platform_device_id s3c24xx_driver_ids[] = {

       {

              .name            = "s3c2410-i2c",

              .driver_data    = TYPE_S3C2410,

       }, {

              .name            = "s3c2440-i2c",

              .driver_data    = TYPE_S3C2440,

       }, { },

};官方的描述为“平台总线位的设备驱动程序”

 

 

kernel-3.4.39\drivers\i2c\busses\ i2c-s3c2410.c

static struct platform_driver s3c24xx_i2c_driver = {

       .probe           = s3c24xx_i2c_probe,

       .remove         = s3c24xx_i2c_remove,

       .id_table  = s3c24xx_driver_ids,

       .driver            = {

              .owner    = THIS_MODULE,

              .name     = "s3c-i2c",

              .pm = S3C24XX_DEV_PM_OPS,

              .of_match_table = s3c24xx_i2c_match,

       },

};

明显看出是通过.id_tab去匹配的

static int s3c24xx_i2c_probe(struct platform_device *pdev)

当找到合适的设备时,由总线驱动程序调用,初始化I2C

static int s3c24xx_i2c_remove(struct platform_device *pdev)

当设备从总线中移除时调用,反初始化?

也有of_match_table的匹配方法,这里没有使用到。。。

 

 

kernel-3.4.39\drivers\i2c\busses\i2c-s3c2410.c

static int __init i2c_adap_s3c_init(void)

{

       int ret = 0;

       ret = platform_driver_register(&s3c24xx_i2c_driver);

       return ret;

}

在这个模块的初始化部分进行驱动注册,到这里其实驱动部分也已经结束了。。。。。。

是不是发现问题了?我们常见的master_xfer呢?不急不急,另外说一下,设备树的同学,在prob函数内可以解析设备树,并初始化喔,不会写设备树也可以看看这个函数,然后决定如何写设备树喔,嘻嘻。

 

 

在proc中:

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {

       .master_xfer          = s3c24xx_i2c_xfer,

       .functionality         = s3c24xx_i2c_func,

};

这个结构体也是非常的关键,这个结构体里面的函数s3c24xx_i2c_xfer是适配器算法的实现,这个函数实现了适配器与I2C CORE的连接。

 

s3c24xx_i2c_func是指该适配器所支持的功能。

s3c24xx_i2c_xfer这个函数实质是实现I2C数据的发送与接收的处理过程。不同的处理器实现的方法不同,主要表现在寄存器的设置与中断的处理方法上

 

依然在prob中

注册具有静态总线号的i2c适配器的函数,这样就能够使用我们后面的i2c_adapter等方法了啦。

idr_get_new_aboveidr机制整数id与指针建立关系

i2c_register_adapter注册i2c适配器

 

下周我们讲一下熟悉的I2C-core部分的细节吧,饿了吃饭去。。。。。。

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值