你好世界上树莓派3共用时钟架构驱动 [英] Hello World for common clock framework driver on Raspberry Pi 3

查看:881
本文介绍了你好世界上树莓派3共用时钟架构驱动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图写,我已经通过I2C连接到我的覆盆子PI 3时钟公共时钟架构驱动程序。注:我很新的Linux和内核编程。

更新:成功

下面的一个Hello World驱动程序,我不得不作出的设备树让我的司机载的唯一变化的作品中的code是添加了I2C1的子节点(在arch / ARM /启动/dts/bcm2708_common.dts):

  I2C1:I2C @ 7e804000 {
        兼容=BRCM,bcm2708-I2C
        章= LT; 0x7e804000为0x1000取代;
        中断= 2 21基;;
        时钟=&所述;&放大器; clk_core取代;
        #地址细胞=。1取代;
        #大小细胞=℃,取代;
        状态=已禁用;
        myclock:时钟发生器@ 6A {
                #时钟细胞=℃,取代;
                兼容=DBC,myclock
                章= LT;的0x6A取代;
                时钟频率=< 7500万取代;
        };
};

通过在地方,我现在看到的printk的消息我预计在dmesg可看到的。

什么是工作


  • 与时钟评估板通过串行总线挂接到PI。验证与i2cdetect / i2cdump。 I2C器件是@从机地址的0x6A。

  • 我有交叉编译我自己的版本的Ubuntu从树莓PI 3内核(4.4.16-V7)的(VirtualBox上运行),它成功地部署到PI。验证与-a的uname(检查外向性的信息,我加到Makefile文件)。

  • 我创造,我可以加载insmod的一个Hello World设备驱动程序。

  • 我创建了一个Hello World的设备驱动程序,我可以添加到设备树(以bcm2708_common.dtsi和bcm2710-RPI-3-b.dts)。我可以部署新设备树到PI。验证设备驱动程序正在使用的printk语句加载(查看用dmesg PI开机后),以及开机后检查lsmod的。

  • 我已经建立在司机一个Hello World公共时钟架构驱动器/ CLK(CLK-myclock.c)的初步尝试。此驱动程序将最终用于改变时钟速度,所以我采取了clk_ops结构recalc_rate,round_rate和set_rate。我添加此驱动程序驱动程序/ CLK / Makefile文件,并添加配置选项司机/ CLK /的Kconfig。我用menuconfig中启用该选项,并且我已经验证模块正在修建(CLK-myconfig.o通过构建创建)。

什么是不工作

我现在想我的Hello World CCF驱动程序添加到对树莓派设备树。我不明白设备树不够好,知道在哪里可以添加(或CCF甚至是否真正支持的PI)。

我已经尝试了两个主要的事情是:


  • 添加设备为下bcm2708_common.dtsi I2C0 I2C1和一个孩子。


  • 添加了时钟{}在bcm2708_common.dtsi部分设备,然后指我从I2C0 I2C1和的时钟财产新的时钟。


据我所知,我的司机是永远不会被加载或使用。这是基于这样的事实,我没有看到我的调试信息(从我的* _probe功能顶部的printk的调用),我没有看到启动后lsmod的加载我的模块。

纵观弓/ ARM /开机/ DTS / ZYNQ-zc702.dts文件,看来板有i2cswitch(兼容=恩智浦,pca9548)作为I2C0设备的孩子,下I2C0孩子即,再一个共同的时钟框架驱动器(SILABS,si570)下存在。我不知道相应的硬件架构上可能有树莓PI(或在哪里看明白这一点),以支持I2C链任意新的I2C设备。

问题


  1. 支撑在PI公共时钟框架?


  2. 你怎么任意新I2C设备添加到树莓PI设备树?


  3. 时,使用探头功能和lsmod的printk的检查,看看我的驱动程序,用于确定是否不是我的设备已在设备树中被发现,我的司机一直与它相关联装足够了吗?


code

CLK-myclock.c

 的#include< Linux的/ clk.h>
#包括LT&; Linux的/ CLK-provider.h>
#包括LT&; Linux的/ delay.h>
#包括LT&; Linux的/ - module.h中GT;
#包括LT&; Linux的/ i2c.h中>
#包括LT&; Linux的/ regmap.h>
#包括LT&; Linux的/ slab.h>#定义DRV_NAMEmyclock结构clk_myclock {
    结构clk_hw HW;
    结构regmap * regmap;
    无符号整型div_offset;
    U64 max_freq;
    U64值为fXTAL;
    无符号整型N1;
    无符号整型hs_div;
    U64 RFREQ;
    U64频率;
    结构i2c_client * i2c_client;
};
#定义to_clk_myclock(_hw)container_of(_hw,结构clk_myclock,HW)枚举clk_myclock_variant {
    myclock
};静态INT myclock_set_rate(结构clk_hw * HW,无符号长率,
        无符号长parent_rate)
{
    结构clk_myclock *数据= to_clk_myclock(HW);
    数据 - >频率=率;
    返回0;
}静态无符号长myclock_recalc_rate(结构clk_hw * HW,
        无符号长parent_rate)
{
    U64率;
    结构clk_myclock *数据= to_clk_myclock(HW);
    率=数据 - >值为fXTAL;
    收益率;
}静态长myclock_round_rate(结构clk_hw * HW,无符号长率,
        无符号长* parent_rate)
{
    如果(!速率)
        返回0;
    收益率;
}静态常量结构clk_ops myclock_clk_ops = {
    .recalc_rate = myclock_recalc_rate,
    .round_rate = myclock_round_rate,
    .set_rate = myclock_set_rate,
};静态布尔myclock_regmap_is_volatile(结构装置* dev的,无符号整型REG)
{
    返回false;
}静态布尔myclock_regmap_is_writeable(结构装置* dev的,无符号整型REG)
{
    返回true;
}静态常量结构regmap_config myclock_regmap_config = {
    .reg_bits = 8,
    .val_bits = 8,
    .cache_type = REGCACHE_RBTREE,
    .max_register = 0xFF的,
    .writeable_reg = myclock_regmap_is_writeable,
    .volatile_reg = myclock_regmap_is_volatile,
};
静态INT myclock_probe(结构i2c_client *客户端,
        常量结构i2c_device_id * ID)
{
    结构clk_myclock *数据;
    结构clk_init_data初始化;
    结构CLK * CLK;
    U32 initial_fout;
    INT犯错;    printk的(KERN_ALERTmyclock_probe \\ n);
    数据= devm_kzalloc(安培;客户 - >开发,sizeof的(*数据),GFP_KERNEL);
    如果(!数据)
        返回-ENOMEM;    init.ops =安培; myclock_clk_ops;
    init.flags = CLK_IS_ROOT;
    init.num_parents = 0;
    数据 - > hw.init =放大器;初始化;
    数据 - > i2c_client =客户端;    init.name =myclock;    DATA-> regmap = devm_regmap_init_i2c(客户端,与放大器; myclock_regmap_config);
    如果(IS_ERR(数据 - > regmap)){
        dev_err(安培;客户 - >开发,未能分配寄存器映射\\ n);
        返回PTR_ERR(数据 - > regmap);
    }    i2c_set_clientdata(客户端,数据);    CLK = devm_clk_register(安培;客户 - >开发,和放大器;数据 - > HW);
    如果(IS_ERR(CLK)){
        dev_err(安培;客户 - >开发,时钟注册失败\\ n);
        返回PTR_ERR(CLK);
    }
    ERR = of_clk_add_provider(客户端 - > dev.of_node,of_clk_src_simple_get,
            CLK);
    如果(ERR){
        dev_err(安培;客户 - >开发,无法添加CLK提供商\\ n);
        返回ERR;
    }    / *读取从设备树请求的初始输出频率* /
    如果(of_property_read_u32(客户端 - >!dev.of_node,时钟频率
                &放大器; initial_fout)){
        的dev_info(安培;客户 - >开发,初始输出频率:%U \\ N,initial_fout);
    }    / *显示一条消息,表明我们已经成功注册* /
    的dev_info(安培;客户 - >开发,注册,电流频率%LLU赫兹\\ n
            数据 - >频率);    返回0;
}静态INT myclock_remove(结构i2c_client *客户端)
{
   printk的(KERN_ALERTmyclock_remove \\ n);
   of_clk_del_provider(客户端 - > dev.of_node);
   返回0;
}静态常量结构i2c_device_id myclock_id [] = {
    {myclock,myclock},
    {}
};MODULE_DEVICE_TABLE(I2C,myclock_id);静态常量结构of_device_id myclock_of_match [] = {
        {.compatible =DBC,myclock},
        {},
};MODULE_DEVICE_TABLE(中,myclock_of_match);静态结构i2c_driver myclock_driver = {
    .driver = {
        。名称= DRV_NAME,
        .of_match_table = myclock_of_match,
    },
    .PROBE = myclock_probe,
    卸下摆臂= myclock_remove,
    .id_table = myclock_id,
};module_i2c_driver(myclock_driver);MODULE_DESCRIPTION(的Hello World公共时钟架构驱动程序);
MODULE_AUTHOR(大卫迎合);
MODULE_LICENSE(GPL);
MODULE_ALIAS(平台:DRV_NAME);

bcm2708_common.dtsi尝试#1:增加了时钟

  I2C0:I2C @ 7e205000 {
    兼容=BRCM,bcm2708-I2C
    章= LT; 0x7e205000为0x1000取代;
    中断= 2 21基;;
    时钟=&所述;&放大器; clk_core&放大器; clk_myclock取代;
    #地址细胞=。1取代;
    #大小细胞=℃,取代;
    状态=已禁用;
};
I2C1:I2C @ 7e804000 {
    兼容=BRCM,bcm2708-I2C
    章= LT; 0x7e804000为0x1000取代;
    中断= 2 21基;;
    时钟=&所述;&放大器; clk_core&放大器; clk_myclock取代;
    #地址细胞=。1取代;
    #大小细胞=℃,取代;
    状态=已禁用;
};
钟:钟表{
    clk_core:时钟@ 0 {
        兼容=固定时钟;
        章=℃,取代;
        #时钟细胞=℃,取代;
        时钟输出名称=核心;
        时钟频率=&所述; 2.5亿取代;
    };    ...
    clk_myclock:时钟@ 7 {
        #时钟细胞=℃,取代;
        章= LT;的0x6A取代;
        兼容=DBC,myclock
        时钟频率=< 7500万取代;
    };
};

bcm2708_common.dtsi尝试#2:将作为I2C的孩子

  I2C0:I2C @ 7e205000 {
    兼容=BRCM,bcm2708-I2C
    章= LT; 0x7e205000为0x1000取代;
    中断= 2 21基;;
    时钟=&所述;&放大器; clk_core&放大器; clk_myclock取代;
    #地址细胞=。1取代;
    #大小细胞=℃,取代;
    状态=已禁用;
    I2C @ 0 {
        #地址细胞=。1取代;
        #大小细胞=℃,取代;
        章=℃,取代;
        myclock:时钟发生器@ 6A {
            #时钟细胞=℃,取代;
            兼容=DBC,myclock
            章= LT;的0x6A取代;
            时钟频率=< 7500万取代;
        };
    };
};

更新:开机后设备树(#1)

这是从实时系统启动后设备树的一部分。这是从加入的时钟在DTS的时钟部分,然后在I2C0和I2C1时钟属性引用时钟。这是从运行 DTC -I FS的/ proc /设备树。 (整棵树超过该职位的限制)。

看起来I2C0被禁用,但I2C1被启用。

  / DTS-V1 /;/ {
    模型=树莓派3模型B版本1.2;
    兼容=BRCM,bcm2710,BRCM,bcm2709;
    memreserve =&下; 0x3b000000 0x4000000取代;
    #地址细胞= LT;为0x1取代;
    #大小细胞= LT;为0x1取代;
    中断父= LT;为0x1取代;    SOC {
        兼容=简单的总线
        范围= LT; 0x7e000000 0x3f000000 0x1000000为0x40000000 0x40000000之后0x40000取代;
        #地址细胞= LT;为0x1取代;
        phandle =&所述;的0x30取代;
        #大小细胞= LT;为0x1取代;
        ...        I2C @ 7e205000 {
            章= LT; 0x7e205000为0x1000取代;
            中断= LT; 0X2为0x15取代;
            pinctrl-0 =&所述;为0x10取代;
            兼容=BRCM,bcm2708-I2C
            时钟频率=&所述; 0x186a0取代;
            时钟= LT; 0x8中0xF的取代;
            状态=已禁用;
            #地址细胞= LT;为0x1取代;
            phandle =&所述; 0x28取代;
            #大小细胞= LT;为0x0取代;
            pinctrl-名称=默认;
        };
        I2C @ 7e804000 {
            章= LT; 0x7e804000为0x1000取代;
            中断= LT; 0X2为0x15取代;
            pinctrl-0 =&所述;为0x18取代;
            兼容=BRCM,bcm2708-I2C
            时钟频率=&所述; 0x186a0取代;
            时钟= LT; 0x8中0xF的取代;
            状态=没关系;
            #地址细胞= LT;为0x1取代;
            phandle =&所述; 0x29取代;
            #大小细胞= LT;为0x0取代;
            pinctrl-名称=默认;
        };
        I2C @ 7e805000 {
            章= LT; 0x7e805000为0x1000取代;
            中断= LT; 0X2为0x15取代;
            兼容=BRCM,bcm2708-I2C
            时钟频率=&所述; 0x186a0取代;
            时钟=&所述;位于0x8取代;
            状态=已禁用;
            #地址细胞= LT;为0x1取代;
            phandle =&所述;的0x19取代;
            #大小细胞= LT;为0x0取代;
        };
        GPIO @ 7e200000 {
            ...
            I2C0 {
                phandle =&所述;为0x10取代;
                BRCM,功能= LT;为0x4取代;
                BRCM,销=&所述;为0x0为0x1取代;
            };            I2C1 {
                phandle =&所述;为0x18取代;
                BRCM,功能= LT;为0x4取代;
                BRCM,销=&下; 0X2 0x3中取代;
            };
            ...
        };
    };
    ...
    时钟{
        兼容=简单的总线
        #地址细胞= LT;为0x1取代;
        phandle =&所述;×45取代;
        #大小细胞= LT;为0x0取代;        时钟@ 0 {
            章= LT;为0x0取代;
            #时钟细胞= LT;为0x0取代;
            兼容=固定时钟;
            时钟频率=&所述; 0x17d78400取代;
            时钟输出名称=核心;
            phandle =< 0x8中取代;
        };
        ...
        时钟@ 7 {
            章= LT;的0x6A取代;
            #时钟细胞= LT;为0x0取代;
            兼容=DBC,myclock
            时钟频率=&所述; 0x47868c0取代;
            phandle =&所述; 0xF的取代;
        };
    };    ...
    __symbols__ {
        ...
        I2C0 =/ SOC / I2C @ 7e205000
        I2C1 =/ SOC / I2C @ 7e804000
        I2C2 =/ SOC / I2C @ 7e805000
        ...
    };    别名{
        ...
        I2C0 =/ SOC / I2C @ 7e205000
        I2C1 =/ SOC / I2C @ 7e804000
        I2C2 =/ SOC / I2C @ 7e805000
        ...
        i2c_arm =/ SOC / I2C @ 7e804000
    };    __overrides__ {
        ...
        I2C0 =,,,(状态;
        I2C1 =,,,)的地位;
        i2c_arm =,,,)的地位;
        ...
    };
};

更新:在引导遇到错误

现在,我知道我处理I2C1,我移除了DTS所有多余的测试code。在这一点上,我只是想这样的:

  I2C1:I2C @ 7e804000 {
        兼容=BRCM,bcm2708-I2C
        章= LT; 0x7e804000为0x1000取代;
        中断= 2 21基;;
        时钟=&所述;&放大器; clk_core取代;
        #地址细胞=。1取代;
        #大小细胞=℃,取代;
        状态=已禁用;
    I2C @ 0 {
            #地址细胞=。1取代;
            #大小细胞=℃,取代;
            章=℃,取代;
            myclock:时钟发生器@ 6A {
                    #时钟细胞=℃,取代;
                    兼容=DBC,myclock
                    章= LT;的0x6A取代;
                    时钟频率=< 7500万取代;
            };
    };
};

现在我收到以下错误dmesg的:

  [5.071515] bcm2708_i2c_probe
[5.086179] I2C I2C-1:of_i2c:MODALIAS上/ SOC故障/ I2C @ 7e804000 / I2C @ 0
[5.086224] bcm2708_i2c 3f804000.i2c:BSC1控制器在0x3f804000(IRQ 83)(波特率100000)

我不知道如何跨preT一个MODALIAS失败。


解决方案

在原岗位工作的一个Hello World驱动程序中的C code和唯一的变化其实我不得不作出的设备树让我驱动程序加载是添加了I2C1的子节点(在arch / ARM /开机/ DTS / bcm2708_common.dts):

  I2C1:I2C @ 7e804000 {
        兼容=BRCM,bcm2708-I2C
        章= LT; 0x7e804000为0x1000取代;
        中断= 2 21基;;
        时钟=&所述;&放大器; clk_core取代;
        #地址细胞=。1取代;
        #大小细胞=℃,取代;
        状态=已禁用;
        myclock:时钟发生器@ 6A {
                #时钟细胞=℃,取代;
                兼容=DBC,myclock
                章= LT;的0x6A取代;
                时钟频率=< 7500万取代;
        };
};

通过在地方,我现在看到的printk的消息我预计在dmesg可看到的。

I am trying to write a common clock framework driver for a clock that I have attached to my Raspberry PI 3 via I2C. NOTE: I am very new to both Linux and kernel programming.

Update: SUCCESS!

The code below works for a Hello World driver, and the sole change I had to make to the device tree to get my driver to load was to add a child of the i2c1 node (in arch/arm/boot/dts/bcm2708_common.dts):

i2c1: i2c@7e804000 {
        compatible = "brcm,bcm2708-i2c";
        reg = <0x7e804000 0x1000>;
        interrupts = <2 21>;
        clocks = <&clk_core>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
        myclock: clock-generator@6a {
                #clock-cells = <0>;
                compatible = "dbc,myclock";
                reg = <0x6a>;
                clock-frequency = <75000000>;
        };
};

With that in place, I now see the printk messages I expected to see in dmesg.

What is working

  • The evaluation board with the clock is hooked up to the PI via the serial bus. Verified with i2cdetect/i2cdump. The I2C device is @ slave address 0x6a.
  • I have cross-compiled my own version of the Raspberry PI 3 kernel (4.4.16-v7) from Ubuntu (running on VirtualBox) and successfully deployed it to the PI. Verified with uname -a (checking EXTRAVERSION info I added to the Makefile).
  • I have created a Hello World device driver that I can load with insmod.
  • I have created a Hello World device driver that I can add to the device tree (in bcm2708_common.dtsi and bcm2710-rpi-3-b.dts). I can deploy the new device tree to the PI. Verified that the device driver is loading using printk statements (viewed using dmesg after PI boot) as well as checking lsmod after boot.
  • I have created an initial attempt at a Hello World common clock framework driver in drivers/clk (clk-myclock.c). This driver will ultimately be used to change the rate on the clock, so I am implementing recalc_rate, round_rate and set_rate in the clk_ops struct. I added this driver to drivers/clk/Makefile and added a config option to drivers/clk/Kconfig. I used menuconfig to enable the option, and I have verified that the module is being built (clk-myconfig.o is created by the build).

What is not working

I am now trying to add my Hello World ccf driver to the device tree on the Raspberry Pi. I don't understand the device tree well enough to know where to add it (or even whether the ccf is actually supported on the PI).

The two main things I have tried are:

  • Adding the device as a child under i2c0 and i2c1 in bcm2708_common.dtsi.

  • Adding the device in the clocks {} section in bcm2708_common.dtsi and then referring to my new clock from the clocks property of i2c0 and i2c1.

As far as I can tell, my driver is never being loaded or used. This is based on the fact that I don't see my debug message (from a printk call at the top of my *_probe function), and I don't see my module loaded in lsmod after booting.

Looking at the arch/arm/boot/dts/zynq-zc702.dts file, it appears that board has an i2cswitch (compatible="nxp,pca9548") as a child of the i2c0 device, and an i2c0 child under that, and then a common clock framework driver ("silabs,si570") under there. I have no idea what the corresponding hw architecture might be on the Raspberry PI (or where to look to figure that out) to support arbitrary new I2C devices in the I2C chain.

Questions

  1. Is the common clock framework supported on the PI?

  2. How do you add an arbitrary new I2C device to the Raspberry PI device tree?

  3. Is using printk in the probe function and lsmod to check to see if my driver is loaded sufficient for determining whether or not my device has been found in the device tree and my driver has been associated with it?

Code

clk-myclock.c

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>

#define DRV_NAME        "myclock"

struct clk_myclock {
    struct clk_hw hw;
    struct regmap *regmap;
    unsigned int div_offset;
    u64 max_freq;
    u64 fxtal;
    unsigned int n1;
    unsigned int hs_div;
    u64 rfreq;
    u64 frequency;
    struct i2c_client *i2c_client;
};
#define to_clk_myclock(_hw) container_of(_hw, struct clk_myclock, hw)

enum clk_myclock_variant {
    myclock
};

static int myclock_set_rate(struct clk_hw *hw, unsigned long rate,
        unsigned long parent_rate)
{
    struct clk_myclock *data = to_clk_myclock(hw);
    data->frequency = rate;
    return 0;
}

static unsigned long myclock_recalc_rate(struct clk_hw *hw,
        unsigned long parent_rate)
{
    u64 rate;
    struct clk_myclock *data = to_clk_myclock(hw);
    rate = data->fxtal;
    return rate;
}

static long myclock_round_rate(struct clk_hw *hw, unsigned long rate,
        unsigned long *parent_rate)
{
    if (!rate)
        return 0;
    return rate;
}

static const struct clk_ops myclock_clk_ops = {
    .recalc_rate = myclock_recalc_rate,
    .round_rate = myclock_round_rate,
    .set_rate = myclock_set_rate,
};

static bool myclock_regmap_is_volatile(struct device *dev, unsigned int reg)
{
    return false;
}

static bool myclock_regmap_is_writeable(struct device *dev, unsigned int reg)
{
    return true;
}

static const struct regmap_config myclock_regmap_config = {
    .reg_bits = 8,
    .val_bits = 8,
    .cache_type = REGCACHE_RBTREE,
    .max_register = 0xff,
    .writeable_reg = myclock_regmap_is_writeable,
    .volatile_reg = myclock_regmap_is_volatile,
};


static int myclock_probe(struct i2c_client *client,
        const struct i2c_device_id *id)
{
    struct clk_myclock *data;
    struct clk_init_data init;
    struct clk *clk;
    u32 initial_fout;
    int err;

    printk(KERN_ALERT "myclock_probe\n");
    data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    init.ops = &myclock_clk_ops;
    init.flags = CLK_IS_ROOT;
    init.num_parents = 0;
    data->hw.init = &init;
    data->i2c_client = client;

    init.name = "myclock";

    data->regmap = devm_regmap_init_i2c(client, &myclock_regmap_config);
    if (IS_ERR(data->regmap)) {
        dev_err(&client->dev, "failed to allocate register map\n");
        return PTR_ERR(data->regmap);
    }

    i2c_set_clientdata(client, data);

    clk = devm_clk_register(&client->dev, &data->hw);
    if (IS_ERR(clk)) {
        dev_err(&client->dev, "clock registration failed\n");
        return PTR_ERR(clk);
    }
    err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get,
            clk);
    if (err) {
        dev_err(&client->dev, "unable to add clk provider\n");
        return err;
    }

    /* Read the requested initial output frequency from device tree */
    if (!of_property_read_u32(client->dev.of_node, "clock-frequency",
                &initial_fout)) {
        dev_info(&client->dev, "initial output frequency: %u\n", initial_fout);
    }

    /* Display a message indicating that we've successfully registered */
    dev_info(&client->dev, "registered, current frequency %llu Hz\n",
            data->frequency);

    return 0;
}

static int myclock_remove(struct i2c_client *client)
{
   printk(KERN_ALERT "myclock_remove\n");
   of_clk_del_provider(client->dev.of_node);
   return 0;
}

static const struct i2c_device_id myclock_id[] = {
    { "myclock", myclock },
    { }
};

MODULE_DEVICE_TABLE(i2c, myclock_id);

static const struct of_device_id myclock_of_match[] = {
        { .compatible = "dbc,myclock" },
        {},
};

MODULE_DEVICE_TABLE(of, myclock_of_match);

static struct i2c_driver myclock_driver = {
    .driver = {
        .name = DRV_NAME,
        .of_match_table = myclock_of_match,
    },
    .probe      = myclock_probe,
    .remove     = myclock_remove,
    .id_table   = myclock_id,
};

module_i2c_driver(myclock_driver);

MODULE_DESCRIPTION("Hello World Common clock framework driver");
MODULE_AUTHOR("David Cater");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

bcm2708_common.dtsi Attempt #1: adding to clocks

i2c0: i2c@7e205000 {
    compatible = "brcm,bcm2708-i2c";
    reg = <0x7e205000 0x1000>;
    interrupts = <2 21>;
    clocks = <&clk_core &clk_myclock>;
    #address-cells = <1>;
    #size-cells = <0>;
    status = "disabled";
};
i2c1: i2c@7e804000 {
    compatible = "brcm,bcm2708-i2c";
    reg = <0x7e804000 0x1000>;
    interrupts = <2 21>;
    clocks = <&clk_core &clk_myclock>;
    #address-cells = <1>;
    #size-cells = <0>;
    status = "disabled";
};
clocks: clocks {
    clk_core: clock@0 {
        compatible = "fixed-clock";
        reg = <0>;
        #clock-cells = <0>;
        clock-output-names = "core";
        clock-frequency = <250000000>;
    };

    ...   
    clk_myclock: clock@7 {
        #clock-cells = <0>;
        reg = <0x6a>;
        compatible = "dbc,myclock";
        clock-frequency = <75000000>;
    };
};

bcm2708_common.dtsi Attempt #2: adding as child of i2c

i2c0: i2c@7e205000 {
    compatible = "brcm,bcm2708-i2c";
    reg = <0x7e205000 0x1000>;
    interrupts = <2 21>;
    clocks = <&clk_core &clk_myclock>;
    #address-cells = <1>;
    #size-cells = <0>;
    status = "disabled";
    i2c@0 {
        #address-cells = <1>;
        #size-cells = <0>;
        reg = <0>;
        myclock: clock-generator@6a {
            #clock-cells = <0>;
            compatible = "dbc,myclock";
            reg = <0x6a>;
            clock-frequency = <75000000>;
        };
    };
};

UPDATE: device tree after boot (from #1)

This is a portion of the device tree from the live system after boot. This is from adding the clocks to the clock section in the dts and then referencing the clock in the i2c0 and i2c1 clocks properties. This is from running dtc -I fs /proc/device-tree. (The whole tree exceeds the limits of the post).

It looks like i2c0 is disabled, but i2c1 is enabled.

/dts-v1/;

/ {
    model = "Raspberry Pi 3 Model B Rev 1.2";
    compatible = "brcm,bcm2710", "brcm,bcm2709";
    memreserve = <0x3b000000 0x4000000>;
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    interrupt-parent = <0x1>;

    soc {
        compatible = "simple-bus";
        ranges = <0x7e000000 0x3f000000 0x1000000 0x40000000 0x40000000 0x40000>;
        #address-cells = <0x1>;
        phandle = <0x30>;
        #size-cells = <0x1>;
        ...

        i2c@7e205000 {
            reg = <0x7e205000 0x1000>;
            interrupts = <0x2 0x15>;
            pinctrl-0 = <0x10>;
            compatible = "brcm,bcm2708-i2c";
            clock-frequency = <0x186a0>;
            clocks = <0x8 0xf>;
            status = "disabled";
            #address-cells = <0x1>;
            phandle = <0x28>;
            #size-cells = <0x0>;
            pinctrl-names = "default";
        };
        i2c@7e804000 {
            reg = <0x7e804000 0x1000>;
            interrupts = <0x2 0x15>;
            pinctrl-0 = <0x18>;
            compatible = "brcm,bcm2708-i2c";
            clock-frequency = <0x186a0>;
            clocks = <0x8 0xf>;
            status = "okay";
            #address-cells = <0x1>;
            phandle = <0x29>;
            #size-cells = <0x0>;
            pinctrl-names = "default";
        };
        i2c@7e805000 {
            reg = <0x7e805000 0x1000>;
            interrupts = <0x2 0x15>;
            compatible = "brcm,bcm2708-i2c";
            clock-frequency = <0x186a0>;
            clocks = <0x8>;
            status = "disabled";
            #address-cells = <0x1>;
            phandle = <0x19>;
            #size-cells = <0x0>;
        };


        gpio@7e200000 {
            ...
            i2c0 {
                phandle = <0x10>;
                brcm,function = <0x4>;
                brcm,pins = <0x0 0x1>;
            };

            i2c1 {
                phandle = <0x18>;
                brcm,function = <0x4>;
                brcm,pins = <0x2 0x3>;
            };
            ...
        };
    };
    ...
    clocks {
        compatible = "simple-bus";
        #address-cells = <0x1>;
        phandle = <0x45>;
        #size-cells = <0x0>;

        clock@0 {
            reg = <0x0>;
            #clock-cells = <0x0>;
            compatible = "fixed-clock";
            clock-frequency = <0x17d78400>;
            clock-output-names = "core";
            phandle = <0x8>;
        };
        ...
        clock@7 {
            reg = <0x6a>;
            #clock-cells = <0x0>;
            compatible = "dbc,myclock";
            clock-frequency = <0x47868c0>;
            phandle = <0xf>;
        };
    };

    ...
    __symbols__ {
        ...
        i2c0 = "/soc/i2c@7e205000";
        i2c1 = "/soc/i2c@7e804000";
        i2c2 = "/soc/i2c@7e805000";
        ...
    };

    aliases {
        ...
        i2c0 = "/soc/i2c@7e205000";
        i2c1 = "/soc/i2c@7e804000";
        i2c2 = "/soc/i2c@7e805000";
        ...
        i2c_arm = "/soc/i2c@7e804000";
    };

    __overrides__ {
        ...
        i2c0 = "", "", "", "(status";
        i2c1 = "", "", "", ")status";
        i2c_arm = "", "", "", ")status";
        ...
    };
};

UPDATE: Got error on boot

Now that I know I'm dealing with i2c1, I removed all extraneous test code from the dts. At this point I am just trying this:

i2c1: i2c@7e804000 {
        compatible = "brcm,bcm2708-i2c";
        reg = <0x7e804000 0x1000>;
        interrupts = <2 21>;
        clocks = <&clk_core>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    i2c@0 {
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <0>;
            myclock: clock-generator@6a {
                    #clock-cells = <0>;
                    compatible = "dbc,myclock";
                    reg = <0x6a>;
                    clock-frequency = <75000000>;
            };
    };
};

Now I am getting the following error in dmesg:

[    5.071515] bcm2708_i2c_probe
[    5.086179] i2c i2c-1: of_i2c: modalias failure on /soc/i2c@7e804000/i2c@0
[    5.086224] bcm2708_i2c 3f804000.i2c: BSC1 Controller at 0x3f804000 (irq 83) (baudrate 100000)

I'm not sure how to interpret a "modalias failure".

解决方案

The C code in the original post works for a Hello World driver, and the sole change I actually had to make to the device tree to get my driver to load was to add a child of the i2c1 node (in arch/arm/boot/dts/bcm2708_common.dts):

i2c1: i2c@7e804000 {
        compatible = "brcm,bcm2708-i2c";
        reg = <0x7e804000 0x1000>;
        interrupts = <2 21>;
        clocks = <&clk_core>;
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
        myclock: clock-generator@6a {
                #clock-cells = <0>;
                compatible = "dbc,myclock";
                reg = <0x6a>;
                clock-frequency = <75000000>;
        };
};

With that in place, I now see the printk messages I expected to see in dmesg.

这篇关于你好世界上树莓派3共用时钟架构驱动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆