diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -700,3 +700,10 @@ dev/usb/controller/xlnx_dwc3.c optional xhci soc_xilinx_zynq fdt dev/firmware/xilinx/zynqmp_firmware.c optional fdt soc_xilinx_zynq dev/firmware/xilinx/zynqmp_firmware_if.m optional fdt soc_xilinx_zynq +dev/clk/xilinx/zynqmp_clock.c optional fdt soc_xilinx_zynq +dev/clk/xilinx/zynqmp_clk_div.c optional fdt soc_xilinx_zynq +dev/clk/xilinx/zynqmp_clk_fixed.c optional fdt soc_xilinx_zynq +dev/clk/xilinx/zynqmp_clk_gate.c optional fdt soc_xilinx_zynq +dev/clk/xilinx/zynqmp_clk_mux.c optional fdt soc_xilinx_zynq +dev/clk/xilinx/zynqmp_clk_pll.c optional fdt soc_xilinx_zynq +dev/clk/xilinx/zynqmp_reset.c optional fdt soc_xilinx_zynq diff --git a/sys/dev/clk/xilinx/zynqmp_clk_div.h b/sys/dev/clk/xilinx/zynqmp_clk_div.h new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_div.h @@ -0,0 +1,38 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _ZYNQMP_CLK_DIV_H_ +#define _ZYNQMP_CLK_DIV_H_ + +enum zynqmp_clk_div_type { + CLK_DIV_TYPE_DIV0 = 0, + CLK_DIV_TYPE_DIV1 +}; + +int zynqmp_clk_div_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef, enum zynqmp_clk_div_type type); + +#endif /* _ZYNQMP_CLK_DIV_H_ */ diff --git a/sys/dev/clk/xilinx/zynqmp_clk_div.c b/sys/dev/clk/xilinx/zynqmp_clk_div.c new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_div.c @@ -0,0 +1,140 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" +#include "zynqmp_firmware_if.h" + +#define DIV_ROUND_CLOSEST(n, d) (((n) + (d) / 2) / (d)) + +struct zynqmp_clk_div_softc { + device_t firmware; + enum zynqmp_clk_div_type type; + uint32_t id; +}; + +static int +zynqmp_clk_div_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +zynqmp_clk_div_recalc(struct clknode *clk, uint64_t *freq) +{ + struct zynqmp_clk_div_softc *sc; + uint32_t div; + int rv; + + sc = clknode_get_softc(clk); + rv = ZYNQMP_FIRMWARE_CLOCK_GETDIVIDER(sc->firmware, sc->id, &div); + if (rv != 0) { + printf("%s: Error while getting divider for %s\n", + __func__, + clknode_get_name(clk)); + return (EINVAL); + } + + if (sc->type == CLK_DIV_TYPE_DIV0) + div &= 0xFFFF; + else + div = div >> 16; + *freq = howmany((unsigned long long)*freq, div + 1); + return (0); +} + +static int +zynqmp_clk_div_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + struct zynqmp_clk_div_softc *sc; + uint32_t div; + int rv; + + sc = clknode_get_softc(clk); + + div = DIV_ROUND_CLOSEST(fparent, *fout); + if (sc->type == CLK_DIV_TYPE_DIV0) { + div &= 0xFFFF; + div |= 0xFFFF << 16; + } else { + div <<= 16; + div |= 0xFFFF; + } + + rv = ZYNQMP_FIRMWARE_CLOCK_SETDIVIDER(sc->firmware, sc->id, div); + if (rv != 0) { + printf("%s: Error while setting divider for %s\n", + __func__, + clknode_get_name(clk)); + return (EINVAL); + } + + return (rv); +} + +static clknode_method_t zynqmp_clk_div_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, zynqmp_clk_div_init), + CLKNODEMETHOD(clknode_recalc_freq, zynqmp_clk_div_recalc), + CLKNODEMETHOD(clknode_set_freq, zynqmp_clk_div_set_freq), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(zynqmp_clk_div_clknode, zynqmp_clk_div_clknode_class, + zynqmp_clk_div_clknode_methods, sizeof(struct zynqmp_clk_div_softc), clknode_class); + +int +zynqmp_clk_div_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef, enum zynqmp_clk_div_type type) +{ + struct clknode *clk; + struct zynqmp_clk_div_softc *sc; + uint32_t fw_clk_id; + + fw_clk_id = clkdef->id - 1; + clkdef->id = 0; + clk = clknode_create(clkdom, &zynqmp_clk_div_clknode_class, clkdef); + if (clk == NULL) + return (1); + sc = clknode_get_softc(clk); + sc->id = fw_clk_id; + sc->firmware = fw; + sc->type = type; + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/dev/clk/xilinx/zynqmp_clk_fixed.h b/sys/dev/clk/xilinx/zynqmp_clk_fixed.h new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_fixed.h @@ -0,0 +1,33 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _ZYNQMP_CLK_FIXED_H_ +#define _ZYNQMP_CLK_FIXED_H_ + +int zynqmp_clk_fixed_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef); + +#endif /* _ZYNQMP_CLK_FIXED_H_ */ diff --git a/sys/dev/clk/xilinx/zynqmp_clk_fixed.c b/sys/dev/clk/xilinx/zynqmp_clk_fixed.c new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_fixed.c @@ -0,0 +1,101 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" +#include "zynqmp_firmware_if.h" + +struct zynqmp_clk_fixed_softc { + device_t firmware; + uint32_t id; +}; + +static int +zynqmp_clk_fixed_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +zynqmp_clk_fixed_recalc(struct clknode *clk, uint64_t *freq) +{ + struct zynqmp_clk_fixed_softc *sc; + uint32_t mult, div; + int rv; + + sc = clknode_get_softc(clk); + rv = ZYNQMP_FIRMWARE_CLOCK_GET_FIXEDFACTOR(sc->firmware, sc->id, &mult, &div); + if (rv != 0) { + printf("%s: Error while getting fixed factor for %s\n", + __func__, + clknode_get_name(clk)); + return (EINVAL); + } + + *freq = (*freq * mult) / div; + return (0); +} + +static clknode_method_t zynqmp_clk_fixed_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, zynqmp_clk_fixed_init), + CLKNODEMETHOD(clknode_recalc_freq, zynqmp_clk_fixed_recalc), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(zynqmp_clk_fixed_clknode, zynqmp_clk_fixed_clknode_class, + zynqmp_clk_fixed_clknode_methods, sizeof(struct zynqmp_clk_fixed_softc), clknode_class); + +int +zynqmp_clk_fixed_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef) +{ + struct clknode *clk; + struct zynqmp_clk_fixed_softc *sc; + uint32_t fw_clk_id; + + fw_clk_id = clkdef->id - 1; + clkdef->id = 0; + clk = clknode_create(clkdom, &zynqmp_clk_fixed_clknode_class, clkdef); + if (clk == NULL) + return (1); + sc = clknode_get_softc(clk); + sc->id = fw_clk_id; + sc->firmware = fw; + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/dev/clk/xilinx/zynqmp_clk_gate.h b/sys/dev/clk/xilinx/zynqmp_clk_gate.h new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_gate.h @@ -0,0 +1,33 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _ZYNQMP_CLK_GATE_H_ +#define _ZYNQMP_CLK_GATE_H_ + +int zynqmp_clk_gate_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef); + +#endif /* _ZYNQMP_CLK_GATE_H_ */ diff --git a/sys/dev/clk/xilinx/zynqmp_clk_gate.c b/sys/dev/clk/xilinx/zynqmp_clk_gate.c new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_gate.c @@ -0,0 +1,102 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" +#include "zynqmp_firmware_if.h" + +struct zynqmp_clk_gate_softc { + device_t firmware; + uint32_t id; +}; + +static int +zynqmp_clk_gate_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +zynqmp_clk_set_gate(struct clknode *clk, bool enable) +{ + struct zynqmp_clk_gate_softc *sc; + int rv; + + sc = clknode_get_softc(clk); + if (enable) + rv = ZYNQMP_FIRMWARE_CLOCK_ENABLE(sc->firmware, sc->id); + else + rv = ZYNQMP_FIRMWARE_CLOCK_DISABLE(sc->firmware, sc->id); + if (rv != 0) { + printf("%s: Error %sbling %s\n", + __func__, + enable == true ? "ena" : "disa", + clknode_get_name(clk)); + return (EINVAL); + } + return (0); +} + +static clknode_method_t zynqmp_clk_gate_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, zynqmp_clk_gate_init), + CLKNODEMETHOD(clknode_set_gate, zynqmp_clk_set_gate), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(zynqmp_clk_gate_clknode, zynqmp_clk_gate_clknode_class, + zynqmp_clk_gate_clknode_methods, sizeof(struct zynqmp_clk_gate_softc), clknode_class); + +int +zynqmp_clk_gate_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef) +{ + struct clknode *clk; + struct zynqmp_clk_gate_softc *sc; + uint32_t fw_clk_id; + + fw_clk_id = clkdef->id - 1; + clkdef->id = 0; + clk = clknode_create(clkdom, &zynqmp_clk_gate_clknode_class, clkdef); + if (clk == NULL) + return (1); + sc = clknode_get_softc(clk); + sc->id = fw_clk_id; + sc->firmware = fw; + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/dev/clk/xilinx/zynqmp_clk_mux.h b/sys/dev/clk/xilinx/zynqmp_clk_mux.h new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_mux.h @@ -0,0 +1,33 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _ZYNQMP_CLK_MUX_H_ +#define _ZYNQMP_CLK_MUX_H_ + +int zynqmp_clk_mux_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef); + +#endif /* _ZYNQMP_CLK_MUX_H_ */ diff --git a/sys/dev/clk/xilinx/zynqmp_clk_mux.c b/sys/dev/clk/xilinx/zynqmp_clk_mux.c new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_mux.c @@ -0,0 +1,89 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" +#include "zynqmp_firmware_if.h" + +struct zynqmp_clk_mux_softc { + device_t firmware; + uint32_t id; +}; + +static int +zynqmp_clk_mux_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +zynqmp_clk_mux_set_mux(struct clknode *clk, int idx) +{ + + printf("%s: called for %s\n", __func__, clknode_get_name(clk)); + return (0); +} + +static clknode_method_t zynqmp_clk_mux_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, zynqmp_clk_mux_init), + CLKNODEMETHOD(clknode_set_mux, zynqmp_clk_mux_set_mux), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(zynqmp_clk_mux_clknode, zynqmp_clk_mux_clknode_class, + zynqmp_clk_mux_clknode_methods, sizeof(struct zynqmp_clk_mux_softc), clknode_class); + +int +zynqmp_clk_mux_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef) +{ + struct clknode *clk; + struct zynqmp_clk_mux_softc *sc; + uint32_t fw_clk_id; + + fw_clk_id = clkdef->id - 1; + clkdef->id = 0; + clk = clknode_create(clkdom, &zynqmp_clk_mux_clknode_class, clkdef); + if (clk == NULL) + return (1); + sc = clknode_get_softc(clk); + sc->id = fw_clk_id; + sc->firmware = fw; + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/dev/clk/xilinx/zynqmp_clk_pll.h b/sys/dev/clk/xilinx/zynqmp_clk_pll.h new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_pll.h @@ -0,0 +1,33 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _ZYNQMP_CLK_PLL_H_ +#define _ZYNQMP_CLK_PLL_H_ + +int zynqmp_clk_pll_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef); + +#endif /* _ZYNQMP_CLK_PLL_H_ */ diff --git a/sys/dev/clk/xilinx/zynqmp_clk_pll.c b/sys/dev/clk/xilinx/zynqmp_clk_pll.c new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clk_pll.c @@ -0,0 +1,132 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" +#include "zynqmp_firmware_if.h" + +struct zynqmp_clk_pll_softc { + device_t firmware; + uint32_t id; +}; + +enum pll_mode { + PLL_MODE_INT = 0, + PLL_MODE_FRAC, + PLL_MODE_ERROR, +}; + +static int +zynqmp_clk_pll_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +zynqmp_clk_pll_recalc(struct clknode *clk, uint64_t *freq) +{ + struct zynqmp_clk_pll_softc *sc; + uint64_t pll_freq, pll_frac; + uint32_t div, mode, frac; + int rv; + + sc = clknode_get_softc(clk); + rv = ZYNQMP_FIRMWARE_CLOCK_GETDIVIDER(sc->firmware, sc->id, &div); + if (rv != 0) { + printf("%s: Error while getting divider for %s\n", + __func__, + clknode_get_name(clk)); + } + rv = ZYNQMP_FIRMWARE_PLL_GET_MODE(sc->firmware, sc->id, &mode); + if (rv != 0) { + printf("%s: Error while getting mode for %s\n", + __func__, + clknode_get_name(clk)); + } + if (mode == PLL_MODE_ERROR) + return (0); + + pll_freq = *freq * div; + if (mode == PLL_MODE_FRAC) { + ZYNQMP_FIRMWARE_PLL_GET_FRAC_DATA(sc->firmware, sc->id, &frac); + pll_frac = (*freq * frac) / (1 << 16); + pll_freq += pll_frac; + } + + *freq = pll_freq; + return (0); +} + +static int +zynqmp_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + + /* TODO probably at one point */ + return (ENOTSUP); +} + +static clknode_method_t zynqmp_clk_pll_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, zynqmp_clk_pll_init), + CLKNODEMETHOD(clknode_recalc_freq, zynqmp_clk_pll_recalc), + CLKNODEMETHOD(clknode_set_freq, zynqmp_clk_pll_set_freq), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(zynqmp_clk_pll_clknode, zynqmp_clk_pll_clknode_class, + zynqmp_clk_pll_clknode_methods, sizeof(struct zynqmp_clk_pll_softc), clknode_class); + +int +zynqmp_clk_pll_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef) +{ + struct clknode *clk; + struct zynqmp_clk_pll_softc *sc; + uint32_t fw_clk_id; + + fw_clk_id = clkdef->id - 1; + clkdef->id = 0; + clk = clknode_create(clkdom, &zynqmp_clk_pll_clknode_class, clkdef); + if (clk == NULL) + return (1); + sc = clknode_get_softc(clk); + sc->id = fw_clk_id; + sc->firmware = fw; + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/dev/clk/xilinx/zynqmp_clock.c b/sys/dev/clk/xilinx/zynqmp_clock.c new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_clock.c @@ -0,0 +1,562 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "clkdev_if.h" +#include "zynqmp_firmware_if.h" + +#define ZYNQMP_MAX_NAME_LEN 16 +#define ZYNQMP_MAX_NODES 6 +#define ZYNQMP_MAX_PARENTS 100 + +#define ZYNQMP_CLK_IS_VALID (1 << 0) +#define ZYNQMP_CLK_IS_EXT (1 << 2) + +#define ZYNQMP_GET_NODE_TYPE(x) (x & 0x7) +#define ZYNQMP_GET_NODE_CLKFLAGS(x) ((x >> 8) & 0xFF) +#define ZYNQMP_GET_NODE_TYPEFLAGS(x) ((x >> 24) & 0xF) + +enum ZYNQMP_NODE_TYPE { + CLK_NODE_TYPE_NULL = 0, + CLK_NODE_TYPE_MUX, + CLK_NODE_TYPE_PLL, + CLK_NODE_TYPE_FIXED, + CLK_NODE_TYPE_DIV0, + CLK_NODE_TYPE_DIV1, + CLK_NODE_TYPE_GATE, +}; + +/* + * Clock IDs in the firmware starts at 0 but + * exported clocks (and so clock exposed by the clock framework) + * starts at 1 + */ +#define ZYNQMP_ID_TO_CLK(x) ((x) + 1) +#define CLK_ID_TO_ZYNQMP(x) ((x) - 1) + +struct zynqmp_clk { + TAILQ_ENTRY(zynqmp_clk) next; + struct clknode_init_def clkdef; + uint32_t id; + uint32_t parentids[ZYNQMP_MAX_PARENTS]; + uint32_t topology[ZYNQMP_MAX_NODES]; + uint32_t attributes; +}; + +struct zynqmp_clock_softc { + device_t dev; + device_t parent; + phandle_t node; + clk_t clk_pss_ref; + clk_t clk_video; + clk_t clk_pss_alt_ref; + clk_t clk_aux_ref; + clk_t clk_gt_crx_ref; + struct clkdom *clkdom; +}; + +struct name_resp { + char name[16]; +}; + +struct zynqmp_clk_softc { + struct zynqmp_clk *clk; + device_t firmware; + uint32_t id; +}; + +static int +zynqmp_clk_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static clknode_method_t zynqmp_clk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, zynqmp_clk_init), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(zynqmp_clk_clknode, zynqmp_clk_clknode_class, + zynqmp_clk_clknode_methods, sizeof(struct zynqmp_clk_softc), clknode_class); + +static int +zynqmp_clk_register(struct clkdom *clkdom, device_t fw, struct zynqmp_clk *clkdef) +{ + struct clknode *clknode; + struct zynqmp_clk_softc *sc; + char *prev_clock_name = NULL; + char *clkname, *parent_name; + struct clknode_init_def *zynqclk; + int i; + + for (i = 0; i < ZYNQMP_MAX_NODES; i++) { + /* Bail early if we have no node */ + if (ZYNQMP_GET_NODE_TYPE(clkdef->topology[i]) == CLK_NODE_TYPE_NULL) + break; + zynqclk = malloc(sizeof(*zynqclk), M_DEVBUF, M_WAITOK | M_ZERO); + zynqclk->id = clkdef->clkdef.id; + /* For the first node in the topology we use the main clock parents */ + if (i == 0) { + zynqclk->parent_cnt = clkdef->clkdef.parent_cnt; + zynqclk->parent_names = clkdef->clkdef.parent_names; + } else { + zynqclk->parent_cnt = 1; + zynqclk->parent_names = malloc(sizeof(char *) * zynqclk->parent_cnt, M_DEVBUF, M_ZERO | M_WAITOK); + parent_name = strdup(prev_clock_name, M_DEVBUF); + zynqclk->parent_names[0] = (const char *)parent_name; + } + /* Register the clock node based on the topology type */ + switch (ZYNQMP_GET_NODE_TYPE(clkdef->topology[i])) { + case CLK_NODE_TYPE_MUX: + asprintf(&clkname, M_DEVBUF, "%s_mux", clkdef->clkdef.name); + zynqclk->name = (const char *)clkname; + zynqmp_clk_mux_register(clkdom, fw, zynqclk); + break; + case CLK_NODE_TYPE_PLL: + asprintf(&clkname, M_DEVBUF, "%s_pll", clkdef->clkdef.name); + zynqclk->name = (const char *)clkname; + zynqmp_clk_pll_register(clkdom, fw, zynqclk); + break; + case CLK_NODE_TYPE_FIXED: + asprintf(&clkname, M_DEVBUF, "%s_fixed", clkdef->clkdef.name); + zynqclk->name = (const char *)clkname; + zynqmp_clk_fixed_register(clkdom, fw, zynqclk); + break; + case CLK_NODE_TYPE_DIV0: + asprintf(&clkname, M_DEVBUF, "%s_div0", clkdef->clkdef.name); + zynqclk->name = (const char *)clkname; + zynqmp_clk_div_register(clkdom, fw, zynqclk, CLK_DIV_TYPE_DIV0); + break; + case CLK_NODE_TYPE_DIV1: + asprintf(&clkname, M_DEVBUF, "%s_div1", clkdef->clkdef.name); + zynqclk->name = (const char *)clkname; + zynqmp_clk_div_register(clkdom, fw, zynqclk, CLK_DIV_TYPE_DIV1); + break; + case CLK_NODE_TYPE_GATE: + asprintf(&clkname, M_DEVBUF, "%s_gate", clkdef->clkdef.name); + zynqclk->name = (const char *)clkname; + zynqmp_clk_gate_register(clkdom, fw, zynqclk); + break; + case CLK_NODE_TYPE_NULL: + default: + clkname = NULL; + break; + } + if (i != 0) { + free(parent_name, M_DEVBUF); + free(zynqclk->parent_names, M_DEVBUF); + } + if (clkname != NULL) + prev_clock_name = strdup(clkname, M_DEVBUF); + free(clkname, M_DEVBUF); + free(zynqclk, M_DEVBUF); + } + + /* Register main clock */ + clkdef->clkdef.name = clkdef->clkdef.name; + clkdef->clkdef.parent_cnt = 1; + clkdef->clkdef.parent_names = malloc(sizeof(char *) * clkdef->clkdef.parent_cnt, M_DEVBUF, M_ZERO | M_WAITOK); + clkdef->clkdef.parent_names[0] = strdup(prev_clock_name, M_DEVBUF); + clknode = clknode_create(clkdom, &zynqmp_clk_clknode_class, &clkdef->clkdef); + if (clknode == NULL) + return (1); + sc = clknode_get_softc(clknode); + sc->id = clkdef->clkdef.id - 1; + sc->firmware = fw; + sc->clk = clkdef; + clknode_register(clkdom, clknode); + return (0); +} + +static int +zynqmp_fw_clk_get_name(struct zynqmp_clock_softc *sc, struct zynqmp_clk *clk, uint32_t id) +{ + char *clkname; + uint32_t query_data[4]; + int rv; + + rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_NAME, id, 0, 0, query_data); + if (rv != 0) + return (rv); + if (query_data[0] == '\0') + return (EINVAL); + clkname = malloc(ZYNQMP_MAX_NAME_LEN, M_DEVBUF, M_ZERO | M_WAITOK); + memcpy(clkname, query_data, ZYNQMP_MAX_NAME_LEN); + clk->clkdef.name = clkname; + return (0); +} + +static int +zynqmp_fw_clk_get_attributes(struct zynqmp_clock_softc *sc, struct zynqmp_clk *clk, uint32_t id) +{ + uint32_t query_data[4]; + int rv; + + rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_ATTRIBUTES, id, 0, 0, query_data); + if (rv != 0) + return (rv); + clk->attributes = query_data[1]; + return (0); +} + +static int +zynqmp_fw_clk_get_parents(struct zynqmp_clock_softc *sc, struct zynqmp_clk *clk, uint32_t id) +{ + int rv, i; + uint32_t query_data[4]; + + for (i = 0; i < ZYNQMP_MAX_PARENTS; i += 3) { + clk->parentids[i] = -1; + clk->parentids[i + 1] = -1; + clk->parentids[i + 2] = -1; + rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_PARENTS, id, i, 0, query_data); + clk->parentids[i] = query_data[1] & 0xFFFF; + clk->parentids[i + 1] = query_data[2] & 0xFFFF; + clk->parentids[i + 2] = query_data[3] & 0xFFFF; + if ((int32_t)query_data[1] == -1) { + clk->parentids[i] = -1; + break; + } + clk->parentids[i] += 1; + clk->clkdef.parent_cnt++; + if ((int32_t)query_data[2] == -1) { + clk->parentids[i + 1] = -1; + break; + } + clk->parentids[i + 1] += 1; + clk->clkdef.parent_cnt++; + if ((int32_t)query_data[3] == -1) { + clk->parentids[i + 2] = -1; + break; + } + clk->parentids[i + 2] += 1; + clk->clkdef.parent_cnt++; + if ((int32_t)query_data[1] == -2) + clk->parentids[i] = -2; + if ((int32_t)query_data[2] == -2) + clk->parentids[i + 1] = -2; + if ((int32_t)query_data[3] == -2) + clk->parentids[i + 2] = -2; + if (rv != 0) + break; + } + return (0); +} + +static int +zynqmp_fw_clk_get_topology(struct zynqmp_clock_softc *sc, struct zynqmp_clk *clk, uint32_t id) +{ + uint32_t query_data[4]; + int rv; + + rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_TOPOLOGY, id, 0, 0, query_data); + if (rv != 0) + return (rv); + clk->topology[0] = query_data[1]; + clk->topology[1] = query_data[2]; + clk->topology[2] = query_data[3]; + if (query_data[3] == '\0') + goto out; + rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_TOPOLOGY, id, 3, 0, query_data); + if (rv != 0) + return (rv); + clk->topology[3] = query_data[1]; + clk->topology[4] = query_data[2]; + clk->topology[5] = query_data[3]; + +out: + return (0); +} + +static int +zynqmp_clock_ofw_map(struct clkdom *clkdom, uint32_t ncells, + phandle_t *cells, struct clknode **clk) +{ + + if (ncells != 1) + return (ERANGE); + *clk = clknode_find_by_id(clkdom, ZYNQMP_ID_TO_CLK(cells[0])); + if (*clk == NULL) + return (ENXIO); + return (0); +} + +static int +zynqmp_fw_clk_get_all(struct zynqmp_clock_softc *sc) +{ + TAILQ_HEAD(tailhead, zynqmp_clk) clk_list; + struct zynqmp_clk *clk, *tmp, *tmp2; + char *clkname; + int rv, i; + uint32_t query_data[4], num_clock; + + TAILQ_INIT(&clk_list); + rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, + PM_QID_CLOCK_GET_NUM_CLOCKS, + 0, + 0, + 0, + query_data); + if (rv != 0) { + device_printf(sc->dev, "Cannot get clock details from the firmware\n"); + return (ENXIO); + } + + num_clock = query_data[1]; + for (i = 0; i < num_clock; i++) { + clk = malloc(sizeof(*clk), M_DEVBUF, M_WAITOK | M_ZERO); + clk->clkdef.id = ZYNQMP_ID_TO_CLK(i); + zynqmp_fw_clk_get_name(sc, clk, i); + zynqmp_fw_clk_get_attributes(sc, clk, i); + if ((clk->attributes & ZYNQMP_CLK_IS_VALID) == 0) { + free(clk, M_DEVBUF); + continue; + } + if (clk->attributes & ZYNQMP_CLK_IS_EXT) + goto skip_ext; + /* Get parents id */ + rv = zynqmp_fw_clk_get_parents(sc, clk, i); + if (rv != 0) { + device_printf(sc->dev, "Cannot get parent for %s\n", clk->clkdef.name); + free(clk, M_DEVBUF); + continue; + } + /* Get topology */ + rv = zynqmp_fw_clk_get_topology(sc, clk, i); + if (rv != 0) { + device_printf(sc->dev, "Cannot get topology for %s\n", clk->clkdef.name); + free(clk, M_DEVBUF); + continue; + } + skip_ext: + TAILQ_INSERT_TAIL(&clk_list, clk, next); + } + + /* Add a dummy clock */ + clk = malloc(sizeof(*clk), M_DEVBUF, M_WAITOK | M_ZERO); + clkname = strdup("dummy", M_DEVBUF); + clk->clkdef.name = (const char *)clkname; + clk->clkdef.id = i; + clk->attributes = ZYNQMP_CLK_IS_EXT; + TAILQ_INSERT_TAIL(&clk_list, clk, next); + + /* Map parents id to name */ + TAILQ_FOREACH_SAFE(clk, &clk_list, next, tmp) { + if (clk->attributes & ZYNQMP_CLK_IS_EXT) + continue; + clk->clkdef.parent_names = malloc(sizeof(char *) * clk->clkdef.parent_cnt, M_DEVBUF, M_ZERO | M_WAITOK); + for (i = 0; i < ZYNQMP_MAX_PARENTS; i++) { + if (clk->parentids[i] == -1) + break; + if (clk->parentids[i] == -2) { + clk->clkdef.parent_names[i] = strdup("dummy", M_DEVBUF); + continue; + } + TAILQ_FOREACH(tmp2, &clk_list, next) { + if (tmp2->clkdef.id == clk->parentids[i]) { + if (tmp2->attributes & ZYNQMP_CLK_IS_EXT) { + int idx; + + if (ofw_bus_find_string_index( sc->node, + "clock-names", tmp2->clkdef.name, &idx) == ENOENT) + clk->clkdef.parent_names[i] = strdup("dummy", M_DEVBUF); + else + clk->clkdef.parent_names[i] = strdup(tmp2->clkdef.name, M_DEVBUF); + } + else + clk->clkdef.parent_names[i] = strdup(tmp2->clkdef.name, M_DEVBUF); + break; + } + } + } + } + + sc->clkdom = clkdom_create(sc->dev); + if (sc->clkdom == NULL) + panic("Cannot create clkdom\n"); + clkdom_set_ofw_mapper(sc->clkdom, zynqmp_clock_ofw_map); + + /* Register the clocks */ + TAILQ_FOREACH_SAFE(clk, &clk_list, next, tmp) { + if (clk->attributes & ZYNQMP_CLK_IS_EXT) { + if (strcmp(clk->clkdef.name, "dummy") == 0) { + struct clk_fixed_def dummy; + + bzero(&dummy, sizeof(dummy)); + dummy.clkdef.id = clk->clkdef.id; + dummy.clkdef.name = strdup("dummy", M_DEVBUF); + clknode_fixed_register(sc->clkdom, &dummy); + free(__DECONST(char *, dummy.clkdef.name), M_DEVBUF); + } + } else + zynqmp_clk_register(sc->clkdom, sc->parent, clk); + + TAILQ_REMOVE(&clk_list, clk, next); + for (i = 0; i < clk->clkdef.parent_cnt; i++) + free(__DECONST(char *, clk->clkdef.parent_names[i]), M_DEVBUF); + free(clk->clkdef.parent_names, M_DEVBUF); + free(__DECONST(char *, clk->clkdef.name), M_DEVBUF); + free(clk, M_DEVBUF); + } + + if (clkdom_finit(sc->clkdom) != 0) + panic("cannot finalize clkdom initialization\n"); + + if (bootverbose) + clkdom_dump(sc->clkdom); + + return (0); +} + +static int +zynqmp_clock_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "xlnx,zynqmp-clk")) + return (ENXIO); + device_set_desc(dev, "ZynqMP Clock Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +zynqmp_clock_attach(device_t dev) +{ + struct zynqmp_clock_softc *sc; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->parent = device_get_parent(dev); + sc->node = ofw_bus_get_node(dev); + + /* Enable all clocks */ + if (clk_get_by_ofw_name(dev, 0, "pss_ref_clk", &sc->clk_pss_ref) != 0) { + device_printf(dev, "Cannot get pss_ref_clk clock\n"); + return (ENXIO); + } + rv = clk_enable(sc->clk_pss_ref); + if (rv != 0) { + device_printf(dev, "Could not enable clock pss_ref_clk\n"); + return (ENXIO); + } + if (clk_get_by_ofw_name(dev, 0, "video_clk", &sc->clk_video) != 0) { + device_printf(dev, "Cannot get video_clk clock\n"); + return (ENXIO); + } + rv = clk_enable(sc->clk_video); + if (rv != 0) { + device_printf(dev, "Could not enable clock video_clk\n"); + return (ENXIO); + } + if (clk_get_by_ofw_name(dev, 0, "pss_alt_ref_clk", &sc->clk_pss_alt_ref) != 0) { + device_printf(dev, "Cannot get pss_alt_ref_clk clock\n"); + return (ENXIO); + } + rv = clk_enable(sc->clk_pss_alt_ref); + if (rv != 0) { + device_printf(dev, "Could not enable clock pss_alt_ref_clk\n"); + return (ENXIO); + } + if (clk_get_by_ofw_name(dev, 0, "aux_ref_clk", &sc->clk_aux_ref) != 0) { + device_printf(dev, "Cannot get pss_aux_clk clock\n"); + return (ENXIO); + } + rv = clk_enable(sc->clk_aux_ref); + if (rv != 0) { + device_printf(dev, "Could not enable clock pss_aux_clk\n"); + return (ENXIO); + } + if (clk_get_by_ofw_name(dev, 0, "gt_crx_ref_clk", &sc->clk_gt_crx_ref) != 0) { + device_printf(dev, "Cannot get gt_crx_ref_clk clock\n"); + return (ENXIO); + } + rv = clk_enable(sc->clk_gt_crx_ref); + if (rv != 0) { + device_printf(dev, "Could not enable clock gt_crx_ref_clk\n"); + return (ENXIO); + } + + rv = zynqmp_fw_clk_get_all(sc); + if (rv != 0) { + clk_disable(sc->clk_gt_crx_ref); + clk_disable(sc->clk_aux_ref); + clk_disable(sc->clk_pss_alt_ref); + clk_disable(sc->clk_video); + clk_disable(sc->clk_pss_ref); + return (rv); + } + return (0); +} + +static device_method_t zynqmp_clock_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, zynqmp_clock_probe), + DEVMETHOD(device_attach, zynqmp_clock_attach), + + DEVMETHOD_END +}; + +static driver_t zynqmp_clock_driver = { + "zynqmp_clock", + zynqmp_clock_methods, + sizeof(struct zynqmp_clock_softc), +}; + +EARLY_DRIVER_MODULE(zynqmp_clock, simplebus, zynqmp_clock_driver, 0, 0, + BUS_PASS_BUS + BUS_PASS_ORDER_LAST); diff --git a/sys/dev/clk/xilinx/zynqmp_reset.c b/sys/dev/clk/xilinx/zynqmp_reset.c new file mode 100644 --- /dev/null +++ b/sys/dev/clk/xilinx/zynqmp_reset.c @@ -0,0 +1,253 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "hwreset_if.h" +#include "zynqmp_firmware_if.h" + +#define ZYNQMP_RESET_PCIE_CFG 0 +#define ZYNQMP_RESET_PCIE_BRIDGE 1 +#define ZYNQMP_RESET_PCIE_CTRL 2 +#define ZYNQMP_RESET_DP 3 +#define ZYNQMP_RESET_SWDT_CRF 4 +#define ZYNQMP_RESET_AFI_FM5 5 +#define ZYNQMP_RESET_AFI_FM4 6 +#define ZYNQMP_RESET_AFI_FM3 7 +#define ZYNQMP_RESET_AFI_FM2 8 +#define ZYNQMP_RESET_AFI_FM1 9 +#define ZYNQMP_RESET_AFI_FM0 10 +#define ZYNQMP_RESET_GDMA 11 +#define ZYNQMP_RESET_GPU_PP1 12 +#define ZYNQMP_RESET_GPU_PP0 13 +#define ZYNQMP_RESET_GPU 14 +#define ZYNQMP_RESET_GT 15 +#define ZYNQMP_RESET_SATA 16 +#define ZYNQMP_RESET_ACPU3_PWRON 17 +#define ZYNQMP_RESET_ACPU2_PWRON 18 +#define ZYNQMP_RESET_ACPU1_PWRON 19 +#define ZYNQMP_RESET_ACPU0_PWRON 20 +#define ZYNQMP_RESET_APU_L2 21 +#define ZYNQMP_RESET_ACPU3 22 +#define ZYNQMP_RESET_ACPU2 23 +#define ZYNQMP_RESET_ACPU1 24 +#define ZYNQMP_RESET_ACPU0 25 +#define ZYNQMP_RESET_DDR 26 +#define ZYNQMP_RESET_APM_FPD 27 +#define ZYNQMP_RESET_SOFT 28 +#define ZYNQMP_RESET_GEM0 29 +#define ZYNQMP_RESET_GEM1 30 +#define ZYNQMP_RESET_GEM2 31 +#define ZYNQMP_RESET_GEM3 32 +#define ZYNQMP_RESET_QSPI 33 +#define ZYNQMP_RESET_UART0 34 +#define ZYNQMP_RESET_UART1 35 +#define ZYNQMP_RESET_SPI0 36 +#define ZYNQMP_RESET_SPI1 37 +#define ZYNQMP_RESET_SDIO0 38 +#define ZYNQMP_RESET_SDIO1 39 +#define ZYNQMP_RESET_CAN0 40 +#define ZYNQMP_RESET_CAN1 41 +#define ZYNQMP_RESET_I2C0 42 +#define ZYNQMP_RESET_I2C1 43 +#define ZYNQMP_RESET_TTC0 44 +#define ZYNQMP_RESET_TTC1 45 +#define ZYNQMP_RESET_TTC2 46 +#define ZYNQMP_RESET_TTC3 47 +#define ZYNQMP_RESET_SWDT_CRL 48 +#define ZYNQMP_RESET_NAND 49 +#define ZYNQMP_RESET_ADMA 50 +#define ZYNQMP_RESET_GPIO 51 +#define ZYNQMP_RESET_IOU_CC 52 +#define ZYNQMP_RESET_TIMESTAMP 53 +#define ZYNQMP_RESET_RPU_R50 54 +#define ZYNQMP_RESET_RPU_R51 55 +#define ZYNQMP_RESET_RPU_AMBA 56 +#define ZYNQMP_RESET_OCM 57 +#define ZYNQMP_RESET_RPU_PGE 58 +#define ZYNQMP_RESET_USB0_CORERESET 59 +#define ZYNQMP_RESET_USB1_CORERESET 60 +#define ZYNQMP_RESET_USB0_HIBERRESET 61 +#define ZYNQMP_RESET_USB1_HIBERRESET 62 +#define ZYNQMP_RESET_USB0_APB 63 +#define ZYNQMP_RESET_USB1_APB 64 +#define ZYNQMP_RESET_IPI 65 +#define ZYNQMP_RESET_APM_LPD 66 +#define ZYNQMP_RESET_RTC 67 +#define ZYNQMP_RESET_SYSMON 68 +#define ZYNQMP_RESET_AFI_FM6 69 +#define ZYNQMP_RESET_LPD_SWDT 70 +#define ZYNQMP_RESET_FPD 71 +#define ZYNQMP_RESET_RPU_DBG1 72 +#define ZYNQMP_RESET_RPU_DBG0 73 +#define ZYNQMP_RESET_DBG_LPD 74 +#define ZYNQMP_RESET_DBG_FPD 75 +#define ZYNQMP_RESET_APLL 76 +#define ZYNQMP_RESET_DPLL 77 +#define ZYNQMP_RESET_VPLL 78 +#define ZYNQMP_RESET_IOPLL 79 +#define ZYNQMP_RESET_RPLL 80 +#define ZYNQMP_RESET_GPO3_PL_0 81 +#define ZYNQMP_RESET_GPO3_PL_1 82 +#define ZYNQMP_RESET_GPO3_PL_2 83 +#define ZYNQMP_RESET_GPO3_PL_3 84 +#define ZYNQMP_RESET_GPO3_PL_4 85 +#define ZYNQMP_RESET_GPO3_PL_5 86 +#define ZYNQMP_RESET_GPO3_PL_6 87 +#define ZYNQMP_RESET_GPO3_PL_7 88 +#define ZYNQMP_RESET_GPO3_PL_8 89 +#define ZYNQMP_RESET_GPO3_PL_9 90 +#define ZYNQMP_RESET_GPO3_PL_10 91 +#define ZYNQMP_RESET_GPO3_PL_11 92 +#define ZYNQMP_RESET_GPO3_PL_12 93 +#define ZYNQMP_RESET_GPO3_PL_13 94 +#define ZYNQMP_RESET_GPO3_PL_14 95 +#define ZYNQMP_RESET_GPO3_PL_15 96 +#define ZYNQMP_RESET_GPO3_PL_16 97 +#define ZYNQMP_RESET_GPO3_PL_17 98 +#define ZYNQMP_RESET_GPO3_PL_18 99 +#define ZYNQMP_RESET_GPO3_PL_19 100 +#define ZYNQMP_RESET_GPO3_PL_20 101 +#define ZYNQMP_RESET_GPO3_PL_21 102 +#define ZYNQMP_RESET_GPO3_PL_22 103 +#define ZYNQMP_RESET_GPO3_PL_23 104 +#define ZYNQMP_RESET_GPO3_PL_24 105 +#define ZYNQMP_RESET_GPO3_PL_25 106 +#define ZYNQMP_RESET_GPO3_PL_26 107 +#define ZYNQMP_RESET_GPO3_PL_27 108 +#define ZYNQMP_RESET_GPO3_PL_28 109 +#define ZYNQMP_RESET_GPO3_PL_29 110 +#define ZYNQMP_RESET_GPO3_PL_30 111 +#define ZYNQMP_RESET_GPO3_PL_31 112 +#define ZYNQMP_RESET_RPU_LS 113 +#define ZYNQMP_RESET_PS_ONLY 114 +#define ZYNQMP_RESET_PL 115 +#define ZYNQMP_RESET_PS_PL0 116 +#define ZYNQMP_RESET_PS_PL1 117 +#define ZYNQMP_RESET_PS_PL2 118 +#define ZYNQMP_RESET_PS_PL3 119 +#define ZYNQMP_RESET_MAX ZYNQMP_RESET_PS_PL3 + +struct zynqmp_reset_softc { + device_t dev; + device_t parent; +}; + +static int +zynqmp_reset_assert(device_t dev, intptr_t id, bool reset) +{ + struct zynqmp_reset_softc *sc; + int rv; + + if (id > ZYNQMP_RESET_MAX) + return (EINVAL); + sc = device_get_softc(dev); + device_printf(dev, "%s called for id = %ld, reset =%d\n", __func__, id, reset); + rv = ZYNQMP_FIRMWARE_RESET_ASSERT(sc->parent, id, reset); + return (rv); +} + +static int +zynqmp_reset_is_asserted(device_t dev, intptr_t id, bool *reset) +{ + struct zynqmp_reset_softc *sc; + int rv; + + if (id > ZYNQMP_RESET_MAX) + return (EINVAL); + sc = device_get_softc(dev); + device_printf(dev, "%s called for id = %ld\n", __func__, id); + rv = ZYNQMP_FIRMWARE_RESET_GET_STATUS(sc->parent, id, reset); + + return (rv); +} + +static int +zynqmp_reset_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "xlnx,zynqmp-reset")) + return (ENXIO); + device_set_desc(dev, "ZynqMP Reset Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +zynqmp_reset_attach(device_t dev) +{ + struct zynqmp_reset_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->parent = device_get_parent(dev); + + /* register our self as a reset provider */ + hwreset_register_ofw_provider(dev); + + return (0); +} + +static device_method_t zynqmp_reset_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, zynqmp_reset_probe), + DEVMETHOD(device_attach, zynqmp_reset_attach), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, zynqmp_reset_assert), + DEVMETHOD(hwreset_is_asserted, zynqmp_reset_is_asserted), + + DEVMETHOD_END +}; + +static driver_t zynqmp_reset_driver = { + "zynqmp_reset", + zynqmp_reset_methods, + sizeof(struct zynqmp_reset_softc), +}; + +EARLY_DRIVER_MODULE(zynqmp_reset, simplebus, zynqmp_reset_driver, 0, 0, + BUS_PASS_BUS + BUS_PASS_ORDER_LAST);