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.
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.
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.
Is the common clock framework supported on the PI?
How do you add an arbitrary new I2C device to the Raspberry PI device tree?
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?
#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);
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>;
};
};
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>;
};
};
};
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";
...
};
};
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With