Path: blob/main/sys/dev/clk/xilinx/zynqmp_clk_div.c
39537 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/cdefs.h>2829#include <sys/param.h>30#include <sys/systm.h>31#include <sys/bus.h>3233#include <dev/clk/clk.h>3435#include <dev/clk/xilinx/zynqmp_clk_div.h>3637#include "clkdev_if.h"38#include "zynqmp_firmware_if.h"3940#define DIV_ROUND_CLOSEST(n, d) (((n) + (d) / 2) / (d))4142struct zynqmp_clk_div_softc {43device_t firmware;44enum zynqmp_clk_div_type type;45uint32_t id;46};4748static int49zynqmp_clk_div_init(struct clknode *clk, device_t dev)50{5152clknode_init_parent_idx(clk, 0);53return (0);54}5556static int57zynqmp_clk_div_recalc(struct clknode *clk, uint64_t *freq)58{59struct zynqmp_clk_div_softc *sc;60uint32_t div;61int rv;6263sc = clknode_get_softc(clk);64rv = ZYNQMP_FIRMWARE_CLOCK_GETDIVIDER(sc->firmware, sc->id, &div);65if (rv != 0) {66printf("%s: Error while getting divider for %s\n",67__func__,68clknode_get_name(clk));69return (EINVAL);70}7172if (sc->type == CLK_DIV_TYPE_DIV0)73div &= 0xFFFF;74else75div = div >> 16;76*freq = howmany((unsigned long long)*freq, div + 1);77return (0);78}7980static int81zynqmp_clk_div_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,82int flags, int *stop)83{84struct zynqmp_clk_div_softc *sc;85uint32_t div;86int rv;8788sc = clknode_get_softc(clk);8990div = DIV_ROUND_CLOSEST(fparent, *fout);91if (sc->type == CLK_DIV_TYPE_DIV0) {92div &= 0xFFFF;93div |= 0xFFFF << 16;94} else {95div <<= 16;96div |= 0xFFFF;97}9899rv = ZYNQMP_FIRMWARE_CLOCK_SETDIVIDER(sc->firmware, sc->id, div);100if (rv != 0) {101printf("%s: Error while setting divider for %s\n",102__func__,103clknode_get_name(clk));104return (EINVAL);105}106107return (rv);108}109110static clknode_method_t zynqmp_clk_div_clknode_methods[] = {111/* Device interface */112CLKNODEMETHOD(clknode_init, zynqmp_clk_div_init),113CLKNODEMETHOD(clknode_recalc_freq, zynqmp_clk_div_recalc),114CLKNODEMETHOD(clknode_set_freq, zynqmp_clk_div_set_freq),115CLKNODEMETHOD_END116};117118DEFINE_CLASS_1(zynqmp_clk_div_clknode, zynqmp_clk_div_clknode_class,119zynqmp_clk_div_clknode_methods, sizeof(struct zynqmp_clk_div_softc), clknode_class);120121int122zynqmp_clk_div_register(struct clkdom *clkdom, device_t fw, struct clknode_init_def *clkdef, enum zynqmp_clk_div_type type)123{124struct clknode *clk;125struct zynqmp_clk_div_softc *sc;126uint32_t fw_clk_id;127128fw_clk_id = clkdef->id - 1;129clkdef->id = 0;130clk = clknode_create(clkdom, &zynqmp_clk_div_clknode_class, clkdef);131if (clk == NULL)132return (1);133sc = clknode_get_softc(clk);134sc->id = fw_clk_id;135sc->firmware = fw;136sc->type = type;137clknode_register(clkdom, clk);138return (0);139}140141142