Path: blob/main/sys/arm64/freescale/imx/clk/imx_clk_composite.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2018 Emmanuel Vadot <[email protected]>4*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 ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,19* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;20* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED21* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,22* 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/param.h>28#include <sys/systm.h>29#include <sys/bus.h>3031#include <dev/clk/clk.h>3233#include <arm64/freescale/imx/clk/imx_clk_composite.h>3435#include "clkdev_if.h"3637#define TARGET_ROOT_ENABLE (1 << 28)38#define TARGET_ROOT_MUX(n) ((n) << 24)39#define TARGET_ROOT_MUX_MASK (7 << 24)40#define TARGET_ROOT_MUX_SHIFT 2441#define TARGET_ROOT_PRE_PODF(n) ((((n) - 1) & 0x7) << 16)42#define TARGET_ROOT_PRE_PODF_MASK (0x7 << 16)43#define TARGET_ROOT_PRE_PODF_SHIFT 1644#define TARGET_ROOT_PRE_PODF_MAX 745#define TARGET_ROOT_POST_PODF(n) ((((n) - 1) & 0x3f) << 0)46#define TARGET_ROOT_POST_PODF_MASK (0x3f << 0)47#define TARGET_ROOT_POST_PODF_SHIFT 048#define TARGET_ROOT_POST_PODF_MAX 0x3f4950struct imx_clk_composite_sc {51uint32_t offset;52uint32_t flags;53};5455#define WRITE4(_clk, off, val) \56CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)57#define READ4(_clk, off, val) \58CLKDEV_READ_4(clknode_get_device(_clk), off, val)59#define DEVICE_LOCK(_clk) \60CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))61#define DEVICE_UNLOCK(_clk) \62CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))6364#define IMX_CLK_COMPOSITE_MASK_SHIFT 166566#if 067#define dprintf(format, arg...) \68printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)69#else70#define dprintf(format, arg...)71#endif7273static int74imx_clk_composite_init(struct clknode *clk, device_t dev)75{76struct imx_clk_composite_sc *sc;77uint32_t val, idx;7879sc = clknode_get_softc(clk);8081DEVICE_LOCK(clk);82READ4(clk, sc->offset, &val);83DEVICE_UNLOCK(clk);84idx = (val & TARGET_ROOT_MUX_MASK) >> TARGET_ROOT_MUX_SHIFT;8586clknode_init_parent_idx(clk, idx);8788return (0);89}9091static int92imx_clk_composite_set_gate(struct clknode *clk, bool enable)93{94struct imx_clk_composite_sc *sc;95uint32_t val = 0;9697sc = clknode_get_softc(clk);9899dprintf("%sabling gate\n", enable ? "En" : "Dis");100DEVICE_LOCK(clk);101READ4(clk, sc->offset, &val);102if (enable)103val |= TARGET_ROOT_ENABLE;104else105val &= ~(TARGET_ROOT_ENABLE);106WRITE4(clk, sc->offset, val);107DEVICE_UNLOCK(clk);108109return (0);110}111112static int113imx_clk_composite_set_mux(struct clknode *clk, int index)114{115struct imx_clk_composite_sc *sc;116uint32_t val = 0;117118sc = clknode_get_softc(clk);119120dprintf("Set mux to %d\n", index);121DEVICE_LOCK(clk);122READ4(clk, sc->offset, &val);123val &= ~(TARGET_ROOT_MUX_MASK);124val |= TARGET_ROOT_MUX(index);125WRITE4(clk, sc->offset, val);126DEVICE_UNLOCK(clk);127128return (0);129}130131static int132imx_clk_composite_recalc(struct clknode *clk, uint64_t *freq)133{134struct imx_clk_composite_sc *sc;135uint32_t reg, pre_div, post_div;136137sc = clknode_get_softc(clk);138139DEVICE_LOCK(clk);140READ4(clk, sc->offset, ®);141DEVICE_UNLOCK(clk);142143pre_div = ((reg & TARGET_ROOT_PRE_PODF_MASK)144>> TARGET_ROOT_PRE_PODF_SHIFT) + 1;145post_div = ((reg & TARGET_ROOT_POST_PODF_MASK)146>> TARGET_ROOT_POST_PODF_SHIFT) + 1;147148dprintf("parent_freq=%ju, div=%u\n", *freq, div);149*freq = *freq / pre_div / post_div;150dprintf("Final freq=%ju\n", *freq);151return (0);152}153154static int155imx_clk_composite_find_best(uint64_t fparent, uint64_t ftarget,156uint32_t *pre_div, uint32_t *post_div, int flags)157{158uint32_t prediv, postdiv, best_prediv, best_postdiv;159int64_t diff, best_diff;160uint64_t cur;161162best_diff = INT64_MAX;163for (prediv = 1; prediv <= TARGET_ROOT_PRE_PODF_MAX + 1; prediv++) {164for (postdiv = 1; postdiv <= TARGET_ROOT_POST_PODF_MAX + 1; postdiv++) {165cur= fparent / prediv / postdiv;166diff = (int64_t)ftarget - (int64_t)cur;167if (flags & CLK_SET_ROUND_DOWN) {168if (diff >= 0 && diff < best_diff) {169best_diff = diff;170best_prediv = prediv;171best_postdiv = postdiv;172}173}174else if (flags & CLK_SET_ROUND_UP) {175if (diff <= 0 && abs(diff) < best_diff) {176best_diff = diff;177best_prediv = prediv;178best_postdiv = postdiv;179}180}181else {182if (abs(diff) < best_diff) {183best_diff = abs(diff);184best_prediv = prediv;185best_postdiv = postdiv;186}187}188}189}190191if (best_diff == INT64_MAX)192return (ERANGE);193194*pre_div = best_prediv;195*post_div = best_postdiv;196197return (0);198}199200static int201imx_clk_composite_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,202int flags, int *stop)203{204struct imx_clk_composite_sc *sc;205struct clknode *p_clk;206const char **p_names;207int p_idx, best_parent;208int64_t best_diff, diff;209int32_t best_pre_div __unused, best_post_div __unused;210int32_t pre_div, post_div;211uint64_t cur, best;212uint32_t val;213214sc = clknode_get_softc(clk);215dprintf("Finding best parent/div for target freq of %ju\n", *fout);216p_names = clknode_get_parent_names(clk);217218best_diff = 0;219best_parent = -1;220221for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) {222p_clk = clknode_find_by_name(p_names[p_idx]);223clknode_get_freq(p_clk, &fparent);224dprintf("Testing with parent %s (%d) at freq %ju\n",225clknode_get_name(p_clk), p_idx, fparent);226227if (!imx_clk_composite_find_best(fparent, *fout, &pre_div, &post_div, sc->flags))228continue;229cur = fparent / pre_div / post_div;230diff = abs((int64_t)*fout - (int64_t)cur);231if (diff < best_diff) {232best = cur;233best_diff = diff;234best_pre_div = pre_div;235best_post_div = post_div;236best_parent = p_idx;237dprintf("Best parent so far %s (%d) with best freq at "238"%ju\n", clknode_get_name(p_clk), p_idx, best);239}240}241242*stop = 1;243if (best_diff == INT64_MAX)244return (ERANGE);245246/* If we didn't find a new best_parent just return */247if (best_parent == -1)248return (0);249250if ((flags & CLK_SET_DRYRUN) != 0) {251*fout = best;252return (0);253}254255p_idx = clknode_get_parent_idx(clk);256if (p_idx != best_parent) {257dprintf("Switching parent index from %d to %d\n", p_idx,258best_parent);259clknode_set_parent_by_idx(clk, best_parent);260}261262dprintf("Setting dividers to pre=%d, post=%d\n", best_pre_div, best_post_div);263264DEVICE_LOCK(clk);265READ4(clk, sc->offset, &val);266val &= ~(TARGET_ROOT_PRE_PODF_MASK | TARGET_ROOT_POST_PODF_MASK);267val |= TARGET_ROOT_PRE_PODF(pre_div);268val |= TARGET_ROOT_POST_PODF(post_div);269DEVICE_UNLOCK(clk);270271*fout = best;272return (0);273}274275static clknode_method_t imx_clk_composite_clknode_methods[] = {276/* Device interface */277CLKNODEMETHOD(clknode_init, imx_clk_composite_init),278CLKNODEMETHOD(clknode_set_gate, imx_clk_composite_set_gate),279CLKNODEMETHOD(clknode_set_mux, imx_clk_composite_set_mux),280CLKNODEMETHOD(clknode_recalc_freq, imx_clk_composite_recalc),281CLKNODEMETHOD(clknode_set_freq, imx_clk_composite_set_freq),282CLKNODEMETHOD_END283};284285DEFINE_CLASS_1(imx_clk_composite_clknode, imx_clk_composite_clknode_class,286imx_clk_composite_clknode_methods, sizeof(struct imx_clk_composite_sc),287clknode_class);288289int290imx_clk_composite_register(struct clkdom *clkdom,291struct imx_clk_composite_def *clkdef)292{293struct clknode *clk;294struct imx_clk_composite_sc *sc;295296clk = clknode_create(clkdom, &imx_clk_composite_clknode_class,297&clkdef->clkdef);298if (clk == NULL)299return (1);300301sc = clknode_get_softc(clk);302303sc->offset = clkdef->offset;304sc->flags = clkdef->flags;305306clknode_register(clkdom, clk);307308return (0);309}310311312