Path: blob/main/sys/dev/clk/allwinner/aw_clk_nm.c
107264 views
/*-1* Copyright (c) 2017 Emmanuel Vadot <[email protected]>2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6* 1. Redistributions of source code must retain the above copyright7* notice, this list of conditions and the following disclaimer.8* 2. Redistributions in binary form must reproduce the above copyright9* notice, this list of conditions and the following disclaimer in the10* documentation and/or other materials provided with the distribution.11*12* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR13* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES14* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.15* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,16* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,17* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;18* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED19* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,20* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY21* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF22* SUCH DAMAGE.23*/2425#include <sys/param.h>26#include <sys/systm.h>27#include <sys/bus.h>2829#include <dev/clk/clk.h>3031#include <dev/clk/allwinner/aw_clk.h>32#include <dev/clk/allwinner/aw_clk_nm.h>3334#include "clkdev_if.h"3536/*37* clknode for clocks matching the formula :38*39* clk = clkin / n / m40*41*/4243struct aw_clk_nm_sc {44uint32_t offset;4546struct aw_clk_factor m;47struct aw_clk_factor n;48struct aw_clk_factor prediv;4950uint32_t mux_shift;51uint32_t mux_mask;52uint32_t gate_shift;53uint32_t lock_shift;54uint32_t lock_retries;5556uint32_t flags;57};5859#define WRITE4(_clk, off, val) \60CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)61#define READ4(_clk, off, val) \62CLKDEV_READ_4(clknode_get_device(_clk), off, val)63#define DEVICE_LOCK(_clk) \64CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))65#define DEVICE_UNLOCK(_clk) \66CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))6768static int69aw_clk_nm_init(struct clknode *clk, device_t dev)70{71struct aw_clk_nm_sc *sc;72uint32_t val, idx;7374sc = clknode_get_softc(clk);7576idx = 0;77if ((sc->flags & AW_CLK_HAS_MUX) != 0) {78DEVICE_LOCK(clk);79READ4(clk, sc->offset, &val);80DEVICE_UNLOCK(clk);8182idx = (val & sc->mux_mask) >> sc->mux_shift;83}8485clknode_init_parent_idx(clk, idx);86return (0);87}8889static int90aw_clk_nm_set_gate(struct clknode *clk, bool enable)91{92struct aw_clk_nm_sc *sc;93uint32_t val;9495sc = clknode_get_softc(clk);9697if ((sc->flags & AW_CLK_HAS_GATE) == 0)98return (0);99100DEVICE_LOCK(clk);101READ4(clk, sc->offset, &val);102if (enable)103val |= (1 << sc->gate_shift);104else105val &= ~(1 << sc->gate_shift);106WRITE4(clk, sc->offset, val);107DEVICE_UNLOCK(clk);108109return (0);110}111112static int113aw_clk_nm_set_mux(struct clknode *clk, int index)114{115struct aw_clk_nm_sc *sc;116uint32_t val;117118sc = clknode_get_softc(clk);119120if ((sc->flags & AW_CLK_HAS_MUX) == 0)121return (0);122123DEVICE_LOCK(clk);124READ4(clk, sc->offset, &val);125val &= ~sc->mux_mask;126val |= index << sc->mux_shift;127WRITE4(clk, sc->offset, val);128DEVICE_UNLOCK(clk);129130return (0);131}132133static uint64_t134aw_clk_nm_find_best(struct aw_clk_nm_sc *sc, uint64_t fparent, uint64_t *fout,135uint32_t *factor_n, uint32_t *factor_m)136{137uint64_t cur, best = 0;138uint32_t m, n, max_m, max_n, min_m, min_n;139140*factor_n = *factor_m = 0;141142max_m = aw_clk_factor_get_max(&sc->m);143max_n = aw_clk_factor_get_max(&sc->n);144min_m = aw_clk_factor_get_min(&sc->m);145min_n = aw_clk_factor_get_min(&sc->n);146147for (m = min_m; m <= max_m; ) {148for (n = min_n; n <= max_n; ) {149cur = fparent / n / m;150if (clk_freq_diff(*fout, cur) <151clk_freq_diff(*fout, best)) {152best = cur;153*factor_n = n;154*factor_m = m;155}156157if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)158n <<= 1;159else160n++;161}162if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)163m <<= 1;164else165m++;166}167168return (best);169}170171static int172aw_clk_nm_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,173int flags, int *stop)174{175struct aw_clk_nm_sc *sc;176struct clknode *p_clk;177const char **p_names;178uint64_t cur, best;179uint32_t val, m, n, best_m, best_n;180int p_idx, best_parent, retry;181182sc = clknode_get_softc(clk);183184best = cur = 0;185best_parent = 0;186187if ((sc->flags & AW_CLK_REPARENT) != 0) {188p_names = clknode_get_parent_names(clk);189for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) {190p_clk = clknode_find_by_name(p_names[p_idx]);191clknode_get_freq(p_clk, &fparent);192193cur = aw_clk_nm_find_best(sc, fparent, fout, &n, &m);194if (clk_freq_diff(*fout, cur) <195clk_freq_diff(*fout, best)) {196best = cur;197best_parent = p_idx;198best_n = n;199best_m = m;200}201}202203p_idx = clknode_get_parent_idx(clk);204p_clk = clknode_get_parent(clk);205clknode_get_freq(p_clk, &fparent);206} else {207best = aw_clk_nm_find_best(sc, fparent, fout,208&best_n, &best_m);209}210211if ((flags & CLK_SET_DRYRUN) != 0) {212*fout = best;213*stop = 1;214return (0);215}216217if ((best < *fout) &&218((flags & CLK_SET_ROUND_DOWN) == 0)) {219*stop = 1;220printf("best freq (%ju) < requested freq(%ju)\n",221best, *fout);222return (ERANGE);223}224if ((best > *fout) &&225((flags & CLK_SET_ROUND_UP) == 0)) {226*stop = 1;227printf("best freq (%ju) > requested freq(%ju)\n",228best, *fout);229return (ERANGE);230}231232if ((sc->flags & AW_CLK_REPARENT) != 0 && p_idx != best_parent)233clknode_set_parent_by_idx(clk, best_parent);234235DEVICE_LOCK(clk);236READ4(clk, sc->offset, &val);237238n = aw_clk_factor_get_value(&sc->n, best_n);239m = aw_clk_factor_get_value(&sc->m, best_m);240val &= ~sc->n.mask;241val &= ~sc->m.mask;242val |= n << sc->n.shift;243val |= m << sc->m.shift;244245WRITE4(clk, sc->offset, val);246DEVICE_UNLOCK(clk);247248if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {249for (retry = 0; retry < sc->lock_retries; retry++) {250READ4(clk, sc->offset, &val);251if ((val & (1 << sc->lock_shift)) != 0)252break;253DELAY(1000);254}255}256257*fout = best;258*stop = 1;259260return (0);261}262263static int264aw_clk_nm_recalc(struct clknode *clk, uint64_t *freq)265{266struct aw_clk_nm_sc *sc;267uint32_t val, m, n, prediv;268269sc = clknode_get_softc(clk);270271DEVICE_LOCK(clk);272READ4(clk, sc->offset, &val);273DEVICE_UNLOCK(clk);274275m = aw_clk_get_factor(val, &sc->m);276n = aw_clk_get_factor(val, &sc->n);277if (sc->flags & AW_CLK_HAS_PREDIV)278prediv = aw_clk_get_factor(val, &sc->prediv);279else280prediv = 1;281282*freq = *freq / prediv / n / m;283284return (0);285}286287static clknode_method_t aw_nm_clknode_methods[] = {288/* Device interface */289CLKNODEMETHOD(clknode_init, aw_clk_nm_init),290CLKNODEMETHOD(clknode_set_gate, aw_clk_nm_set_gate),291CLKNODEMETHOD(clknode_set_mux, aw_clk_nm_set_mux),292CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nm_recalc),293CLKNODEMETHOD(clknode_set_freq, aw_clk_nm_set_freq),294CLKNODEMETHOD_END295};296297DEFINE_CLASS_1(aw_nm_clknode, aw_nm_clknode_class, aw_nm_clknode_methods,298sizeof(struct aw_clk_nm_sc), clknode_class);299300int301aw_clk_nm_register(struct clkdom *clkdom, struct aw_clk_nm_def *clkdef)302{303struct clknode *clk;304struct aw_clk_nm_sc *sc;305306clk = clknode_create(clkdom, &aw_nm_clknode_class, &clkdef->clkdef);307if (clk == NULL)308return (1);309310sc = clknode_get_softc(clk);311312sc->offset = clkdef->offset;313314sc->m.shift = clkdef->m.shift;315sc->m.width = clkdef->m.width;316sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;317sc->m.value = clkdef->m.value;318sc->m.flags = clkdef->m.flags;319320sc->n.shift = clkdef->n.shift;321sc->n.width = clkdef->n.width;322sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;323sc->n.value = clkdef->n.value;324sc->n.flags = clkdef->n.flags;325326sc->prediv.shift = clkdef->prediv.shift;327sc->prediv.width = clkdef->prediv.width;328sc->prediv.mask = ((1 << sc->prediv.width) - 1) << sc->prediv.shift;329sc->prediv.value = clkdef->prediv.value;330sc->prediv.flags = clkdef->prediv.flags;331sc->prediv.cond_shift = clkdef->prediv.cond_shift;332if (clkdef->prediv.cond_width != 0)333sc->prediv.cond_mask = ((1 << clkdef->prediv.cond_width) - 1) << sc->prediv.shift;334else335sc->prediv.cond_mask = clkdef->prediv.cond_mask;336sc->prediv.cond_value = clkdef->prediv.cond_value;337338sc->mux_shift = clkdef->mux_shift;339sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;340341sc->gate_shift = clkdef->gate_shift;342343sc->lock_shift = clkdef->lock_shift;344sc->lock_retries = clkdef->lock_retries;345346sc->flags = clkdef->flags;347348clknode_register(clkdom, clk);349350return (0);351}352353354