Dear Toradex support,
I have a question related to the clock synchronization via PTP on IMX8MP with Mallow board V1.1. My goal is to synchronize audio clock via PTP with ethernet clock. So far, I was unable to synchronize clocks but I am gonna explain the processes I experimented with.
First of all, my goal is to have ethernet clock as master and audio clock as slave. The PTP synchronization could be done via software and it is already compiled within Yocto build (I am using tdx-reference-multimedia-image). This means I am able to use phc2sys, which is, from what I have understand, the software way to use PTP synchronization. However, I was not able to find a way to expose custom clock to the /dev/ so phc2sys does not see the clock. When I run:
phc2sys -s /dev/ptp0 -c /dev/custom_audio_clock -w
or other variants, I got the error: “valid destination clock must be selected.”
There is no /dev/custom_audio_clock even though when I run:
cat /sys/kernel/debug/clk/clk_summary
I can see the custom_audio_clock derived from audio_pll1:
I guess the problem is that my custom clock is handled by imx8mp clock driver and it does not expose it to /dev/. Here is a snippet of my clock configuration in device tree (imx8mp-verdin-dev.dtsi):
/ {
sound {
compatible = "simple-audio-card";
simple-audio-card,bitclock-master = <&cpu_dai>;
simple-audio-card,format = "msb";
simple-audio-card,frame-master = <&cpu_dai>;
simple-audio-card,name = "ak4621";
simple-audio-card,routing =
"Headphones", "LOUT1",
"Headphones", "ROUT1",
"LMIC", "Mic In",
"RMIC", "Mic In";
simple-audio-card,widgets =
"Headphones", "Headphones",
"Microphone", "Mic In";
codec_dai: simple-audio-card,codec {
sound-dai = <&ak4621>;
};
cpu_dai: simple-audio-card,cpu {
sound-dai = <&sai1>;
};
};
};
&clk {
custom_audio_clock: custom_audio_clock {
compatible = "fixed-factor-clock";
clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIO_BLK_CTRL_SAI1_MCLK1>;
clock-names = "sai1_mclk1_clk";
clock-div = <1>; /* need to achieve 12288000 Hz! */
clock-mult = <1>;
#clock-cells = <0>;
};
};
&fec {
phy-supply = <®_eth2phy>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fec>;
phy-mode = "rgmii-id";
phy-handle = <ðphy0>;
fsl,magic-packet;
fsl,ptp-clock-source = <1>; /* Use custom_clock as the PTP clock source */
fsl,ptp-timer-period = <81380>; /* 12288000 Hz = 81.38 ns period */
};
/* Verdin QSPI_1 */
&flexspi {
status = "okay";
ak4621: audio-codec@0 {
compatible = "asahi-kasei,ak4621";
reg = <0>;
clocks = <&custom_audio_clock>;
clock-names = "mclk";
#sound-dai-cells = <0>;
};
};
I tested the audio codec and it works fine. I am not sure about the “compatible” propery of the audio clock as I used “fixed-clock” before and it worked as well. But I suppose a clock for PTP synchronization should be dynamic and I am not sure what should I use. I tried to expose the clock to /dev/ using custom clock driver like this:
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/device.h>
static ssize_t custom_audio_clock_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "custom_audio_clock\n");
}
static DEVICE_ATTR(custom_audio_clock, 0444, custom_audio_clock_show, NULL);
static int custom_clock_probe(struct platform_device *pdev)
{
struct clk *clk;
struct device *dev = &pdev->dev;
struct clk *parent_clk;
dev_info(dev, "custom_audio_clock probe started\n");
/* Get the parent clock from the device tree */
parent_clk = devm_clk_get(dev, "sai1_mclk1_clk");
if (IS_ERR(parent_clk)) {
dev_err(dev, "Failed to get parent clock\n");
return PTR_ERR(parent_clk);
}
/* Create a fixed-factor clock */
clk = clk_register_fixed_factor(dev, "custom_audio_clock", __clk_get_name(parent_clk), 0, 1, 1);
if (IS_ERR(clk)) {
dev_err(dev, "Failed to register custom_audio_clock\n");
return PTR_ERR(clk);
}
/* Create a device node in /dev */
if (device_create_file(dev, &dev_attr_custom_audio_clock)) {
dev_err(dev, "Failed to create device node\n");
clk_unregister_fixed_factor(clk);
return -ENODEV;
}
dev_info(dev, "custom_audio_clock registered with name: %s\n", __clk_get_name(clk));
return 0;
}
static int custom_clock_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
/* Remove the device node */
device_remove_file(dev, &dev_attr_custom_audio_clock);
dev_info(dev, "custom_audio_clock unregistered\n");
return 0;
}
static const struct of_device_id custom_clock_of_match[] = {
{ .compatible = "custom-audio-clock", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, custom_clock_of_match);
static struct platform_driver custom_clock_driver = {
.probe = custom_clock_probe,
.remove = custom_clock_remove,
.driver = {
.name = "custom_audio_clock",
.of_match_table = custom_clock_of_match,
},
};
module_platform_driver(custom_clock_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Custom Audio Clock Driver");
MODULE_AUTHOR("xistva02");
It got compiled, I added it to Makefile and Kconfig:
FILESEXTRAPATHS:prepend := "${THISDIR}/linux-toradex:"
SRC_URI += " \
file://0001-add-ak4621-dt-support.patch \
file://0001-add-dynamic-clock.patch \
file://0001-add-custom-clk-config.patch \
file://clk-custom.c \
"
# Copy the custom clock driver to the source tree
do_configure:append() {
cp ${WORKDIR}/clk-custom.c ${S}/drivers/clk/
}
# Update PTP configuration after patches are applied
do_configure:append() {
echo "CONFIG_PTP_1588_CLOCK=y" >> ${B}/.config
echo "CONFIG_NETWORK_PHY_TIMESTAMPING=y" >> ${B}/.config
echo "CONFIG_CUSTOM_CLOCK=y" >> ${B}/.config
}
and it seemed okay. But even when I changed the “compatible” of the clock to “custom-audio-clock”, the module was not loaded and the clock was not visible even in clk_summary. The debug messages from probe was not printed in dmesg.
So my question is: how can I synchronize the custom audio clock derived from audio_pll1 with ethernet clock? Is there a way to expose it to /dev/ so the phc2sys can see it or should it be done any other way?
Thank you.
Best regards
Matej I.