tiny4412 linux-4.2 移植(四)I2C总线

开发板信息

核心板:Tiny4412-1412
底板:Tiny4412SDK-1506
Schematic-PCB:Tiny4412-1412-Schematic.pdf
Tiny4412SDK-1506-Schematic.pdf
工具链:
Linux版本:Linux-4.2

简介

Exynos4412有8个i2c控制器,其中,i2c3挂载三轴加速度传感器,i2c0挂载EEPROM。4412的i2c控制器支持标准模式下100kbit/s的传输速度,也支持高速模式下400kbit/s的传输速度。
每个厂商都有实现自己i2c总线的platform_driver,集中在/drivers/i2c/busses/下。比如恩智浦的i2c-imx.c、展讯的i2c-sprd.c、三星的i2c-exynos5.c和i2c-s3c2410.c。我们的4412使用的是i2c-s3c2410.c,所以这里我们来简单地分析i2c-s3c2410.c。本文以i2c3控制器为例来分析samsung平台的i2c总线代码,为了更加直观的分析代码,我这里简化了代码。

DTS

先从Exynos 4412 SCP_Users Manual_Ver.0.10.00_Preliminary0.pdf 和 Tiny4412-1412-Schematic.pdf提取相关硬件信息。
引脚信息:
从引脚信息中可以看出i2c3使用的是GPA1_2 和GPA1_3,这两个引脚的Pin Name为XuCTSn2和XuRTSn2。要想把这两个引脚设置为i2c,需要设置寄存器把Function指定为第3个,也就是Function3,当然我们现在不需要配置寄存器,只需要配置设备树就行。比如:samsung,pin-function = <EXYNOS_PIN_FUNC_3>; 这一行dts就能把对应的引脚设置成Function3了。当然还需要指定引脚号samsung,pins = “gpa1-2”, “gpa1-3”;
在这里插入图片描述
中断:参考我之前的文章 《tiny4412 linux-4.2 移植 GIC中断》结合文档Exynos 4412 SCP_Users Manual_Ver.0.10.00_Preliminary0.pdf 可以知道中断号为61
在这里插入图片描述
/arch/arm/boot/dts/Exynos4.dtsi

        i2c_3: i2c@13890000 {
            #address-cells = <1>;
            #size-cells = <0>;
            compatible = "samsung,s3c2440-i2c";  /* 匹配到s3c24xx_i2c_match 中的 .compatible = "samsung,s3c2440-i2c"*/
            reg = <0x13890000 0x100>;		/* 寄存器基地址  寄存器大小 */
            interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;  /* 中断号为61 */
            clocks = <&clock CLK_I2C3>;
            clock-names = "i2c";
            pinctrl-names = "default";
            pinctrl-0 = <&i2c3_bus>;
            status = "disabled";
        };

上面的pinctrl-0引用了节点i2c3_bus,i2c3_bus在Exynos4412-pinctrl.dtsi中有定义。
/arch/arm/boot/dts/Exynos4412-pinctrl.dtsi

    i2c3_bus: i2c3-bus {    /* 加速度传感器是挂在i2c3上面 */
        samsung,pins = "gpa1-2", "gpa1-3";
        samsung,pin-function = <EXYNOS_PIN_FUNC_3>;  /* 定义引脚功能为 function 3*/
        samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;    /* 引脚内部上拉 */
        samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;    /* 引脚驱动能力 */
    };

可以看出i2c3_bus把i2c3的相关引脚"gpa1-2" "gpa1-3"设置成function 3(也就是i2c功能),并且把他们设置成内部上拉,设置它们的驱动能力。

I2c-s3c2410.c
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap){    /* 声明总线功能,在i2c设备驱动中用i2c_check_functionality可以获取到这个值 */
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
        I2C_FUNC_PROTOCOL_MANGLING;
}

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
    .master_xfer        = s3c24xx_i2c_xfer,    //传输函数,i2c_tansfer会调用到
    .functionality        = s3c24xx_i2c_func,
};
#ifdef CONFIG_OF    //这个宏定义跟设备树相关,有这个定义说明是用了设备树
static const struct of_device_id s3c24xx_i2c_match[] = {
    { .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },//从exynos4.dtsi文件可知exynos4xxxx大多数是用这个compatible
    {},
};
MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
#endif
static struct platform_driver s3c24xx_i2c_driver = {
    .probe        = s3c24xx_i2c_probe,
    .id_table    = s3c24xx_driver_ids,
    .driver        = {
        .name    = "s3c-i2c",        .of_match_table = of_match_ptr(s3c24xx_i2c_match),
    },
};
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
    struct s3c24xx_i2c *i2c;    //厂商定义的结构体,会挂在struct i2c_adapter的algo_data
    if (!pdev->dev.of_node) {    //没有存在这个节点,说明没有是用设备树
        pdata = dev_get_platdata(&pdev->dev);       //旧版本没有是用设备树,用这个去获取平台相关数据
    }
    /*下面简化代码,去掉非设备树的场景*/
    i2c->quirks = s3c24xx_get_device_quirks(pdev);  --->  //quirks 其实是自定义的flag,用于区分i2c总线类型
                --->     kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev){
                                match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node);
                                return (kernel_ulong_t)match->data;    //跟设备树匹配到这里是samsung,s3c2440-i2c,所以返回自定义的宏QUIRK_S3C2440
                            }
    s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);    --->
                ---->    s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c){
                                /* 读取这三个属性的值,这三个一般是在自己的平台dts文件中指定 */
                                of_property_read_u32(np, "samsung,i2c-sda-delay", &pdata->sda_delay);    
                                of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr);
                                of_property_read_u32(np, "samsung,i2c-max-bus-freq", (u32 *)&pdata->frequency);
                                /* 这个函数的后面这里是 exynos 5的逻辑,不用管它*/
                                ......
                            }
    /*  struct i2c_adapter    adap*/
    strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
    i2c->adap.owner = THIS_MODULE;
    i2c->adap.algo = &s3c24xx_i2c_algorithm;
    i2c->adap.retries = 2;
    i2c->adap.class = I2C_CLASS_DEPRECATED;
    i2c->tx_setup = 50;

    i2c->clk = devm_clk_get(&pdev->dev, "i2c");    //这个函数的最终操作是从dts中获取到clock

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    /* 获取到i2c_0的物理地址 */
    i2c->regs = devm_ioremap_resource(&pdev->dev, res);    /* 映射地址,这里返回映射后的起始地址 */
    
    i2c->adap.algo_data = i2c; //struct s3c24xx_i2c *i2c
    i2c->adap.dev.parent = &pdev->dev;
    i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);        --->  这个函数会去设置引脚状态
            ---->  devm_pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT)  ---->
                -----> devm_pinctrl_get_select(struct device *dev, const char *name){
                                struct pinctrl_state *s;
                                 ret = pinctrl_select_state(p, s);    //设置引脚状态
                            }
    ret = clk_prepare_enable(i2c->clk);    //使能时钟
    
    ret = s3c24xx_i2c_init(i2c);    ---->
            ---> static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c){
                        writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);    //数据:i2c从设备的地址   目标寄存器:i2c控制器的基地址加偏移
                        s3c24xx_i2c_clockrate(i2c, &freq)         //设置i2c 总线的频率,跟dts属性samsung,i2c-max-bus-freq相关
                    }
    if (!(i2c->quirks & QUIRK_POLL)) {
        i2c->irq = ret = platform_get_irq(pdev, 0);    //从platform_device中获取到中断
        ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0, dev_name(&pdev->dev), i2c);
    }
    i2c->adap.nr = i2c->pdata->bus_num;    //以前的版本是使用i2c_add_adapter去添加总线的,现在改用这种设置值得方式
    i2c->adap.dev.of_node = pdev->dev.of_node;    //把所属的设备树节点也传递过去
    platform_set_drvdata(pdev, i2c);    //把填充完毕的结构体注册到platform_device中
    ret = i2c_add_numbered_adapter(&i2c->adap);    //注册总线适配器
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值