Controlling Zynq PL Clocks in Linux Userspace

The Xilinx Zynq Ultrascale devices seem to have this covered, but I struggled to find much info on how to do this with the Zynq 7000 parts.  Here’s my notes on both platforms.

With a 4.19 kernel, the Xilinx PL clock enabler (XILINX_FCLK) is the driver you need.  This will expose any compatible = “xlnx,fclk” device-tree nodes to userspace through sysfs.  On Zynq this is something like

# echo 150000000 > /sys/devices/soc0/fclk0/set_rate
# cat /sys/devices/soc0/fclk0/set_rate
142857142  # obviously some PLL rounding to deal with

on ZynqMP

# echo 150000000 > /sys/devices/platform/fclk0/set_rate
# cat /sys/devices/platform/fclk0/set_rate
133333332  # obviously some PLL rounding to deal with

The ZynqMP dtsi’s already have fclk nodes supplied from zynqmp-clk-ccf.dtsi.  My Zynq dts didn’t (probably because it was branched many years ago…) but they can be added like:

fclk0: fclk0 {
    status = "okay";
    compatible = "xlnx,fclk";
    clocks = <&clkc 15>;
};

The PL clocks on the Zynq are <&clkc 15>, <&clkc 16>, <&clkc 17> and <&clkc 18>.  On the ZynqMP they are <&zynqmp_clk PL0_REF> etc. (if you #include <dt-bindings/clock/xlnx-zynqmp-clk.h>).

If you don’t want to set clock frequencies from userspace, you can use ‘assigned-clocks’ in any device tree node that seems relevant.

&custom-thing {
    assigned-clocks = <&clkc 15>, <&clkc 16>;
    assigned-clock-rates = <250000000>,
                           <100000000>;
};

Hope this saves someone else some time.