Path: blob/main/sys/dev/clk/allwinner/aw_clk_np.c
107832 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2019 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 <dev/clk/allwinner/aw_clk.h>34#include <dev/clk/allwinner/aw_clk_np.h>3536#include "clkdev_if.h"3738/*39* clknode for clocks matching the formula :40*41* clk = clkin * n / p42*43*/4445struct aw_clk_np_sc {46uint32_t offset;4748struct aw_clk_factor n;49struct aw_clk_factor p;5051uint32_t gate_shift;52uint32_t lock_shift;53uint32_t lock_retries;5455uint32_t flags;56};5758#define WRITE4(_clk, off, val) \59CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)60#define READ4(_clk, off, val) \61CLKDEV_READ_4(clknode_get_device(_clk), off, val)62#define DEVICE_LOCK(_clk) \63CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))64#define DEVICE_UNLOCK(_clk) \65CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))6667static int68aw_clk_np_init(struct clknode *clk, device_t dev)69{7071clknode_init_parent_idx(clk, 0);72return (0);73}7475static int76aw_clk_np_set_gate(struct clknode *clk, bool enable)77{78struct aw_clk_np_sc *sc;79uint32_t val;8081sc = clknode_get_softc(clk);8283if ((sc->flags & AW_CLK_HAS_GATE) == 0)84return (0);8586DEVICE_LOCK(clk);87READ4(clk, sc->offset, &val);88if (enable)89val |= (1 << sc->gate_shift);90else91val &= ~(1 << sc->gate_shift);92WRITE4(clk, sc->offset, val);93DEVICE_UNLOCK(clk);9495return (0);96}9798static uint64_t99aw_clk_np_find_best(struct aw_clk_np_sc *sc, uint64_t fparent, uint64_t *fout,100uint32_t *factor_n, uint32_t *factor_p)101{102uint64_t cur, best;103uint32_t n, p, max_n, max_p, min_n, min_p;104105*factor_n = *factor_p = 0;106107max_n = aw_clk_factor_get_max(&sc->n);108max_p = aw_clk_factor_get_max(&sc->p);109min_n = aw_clk_factor_get_min(&sc->n);110min_p = aw_clk_factor_get_min(&sc->p);111112for (p = min_p; p <= max_p; ) {113for (n = min_n; n <= max_n; ) {114cur = fparent * n / p;115if (abs(*fout - cur) < abs(*fout - best)) {116best = cur;117*factor_n = n;118*factor_p = p;119}120121n++;122}123p++;124}125126return (best);127}128129static int130aw_clk_np_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,131int flags, int *stop)132{133struct aw_clk_np_sc *sc;134uint64_t cur, best;135uint32_t val, n, p, best_n, best_p;136int retry;137138sc = clknode_get_softc(clk);139140best = cur = 0;141142best = aw_clk_np_find_best(sc, fparent, fout,143&best_n, &best_p);144145if ((flags & CLK_SET_DRYRUN) != 0) {146*fout = best;147*stop = 1;148return (0);149}150151if ((best < *fout) &&152((flags & CLK_SET_ROUND_DOWN) == 0)) {153*stop = 1;154return (ERANGE);155}156if ((best > *fout) &&157((flags & CLK_SET_ROUND_UP) == 0)) {158*stop = 1;159return (ERANGE);160}161162DEVICE_LOCK(clk);163READ4(clk, sc->offset, &val);164165n = aw_clk_factor_get_value(&sc->n, best_n);166p = aw_clk_factor_get_value(&sc->p, best_p);167val &= ~sc->n.mask;168val &= ~sc->p.mask;169val |= n << sc->n.shift;170val |= p << sc->p.shift;171172WRITE4(clk, sc->offset, val);173DEVICE_UNLOCK(clk);174175if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {176for (retry = 0; retry < sc->lock_retries; retry++) {177READ4(clk, sc->offset, &val);178if ((val & (1 << sc->lock_shift)) != 0)179break;180DELAY(1000);181}182}183184*fout = best;185*stop = 1;186187return (0);188}189190static int191aw_clk_np_recalc(struct clknode *clk, uint64_t *freq)192{193struct aw_clk_np_sc *sc;194uint32_t val, n, p;195196sc = clknode_get_softc(clk);197198DEVICE_LOCK(clk);199READ4(clk, sc->offset, &val);200DEVICE_UNLOCK(clk);201202n = aw_clk_get_factor(val, &sc->n);203p = aw_clk_get_factor(val, &sc->p);204205*freq = *freq * n / p;206207return (0);208}209210static clknode_method_t aw_np_clknode_methods[] = {211/* Device interface */212CLKNODEMETHOD(clknode_init, aw_clk_np_init),213CLKNODEMETHOD(clknode_set_gate, aw_clk_np_set_gate),214CLKNODEMETHOD(clknode_recalc_freq, aw_clk_np_recalc),215CLKNODEMETHOD(clknode_set_freq, aw_clk_np_set_freq),216CLKNODEMETHOD_END217};218219DEFINE_CLASS_1(aw_np_clknode, aw_np_clknode_class, aw_np_clknode_methods,220sizeof(struct aw_clk_np_sc), clknode_class);221222int223aw_clk_np_register(struct clkdom *clkdom, struct aw_clk_np_def *clkdef)224{225struct clknode *clk;226struct aw_clk_np_sc *sc;227228clk = clknode_create(clkdom, &aw_np_clknode_class, &clkdef->clkdef);229if (clk == NULL)230return (1);231232sc = clknode_get_softc(clk);233234sc->offset = clkdef->offset;235236sc->n.shift = clkdef->n.shift;237sc->n.width = clkdef->n.width;238sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;239sc->n.value = clkdef->n.value;240sc->n.flags = clkdef->n.flags;241242sc->p.shift = clkdef->p.shift;243sc->p.width = clkdef->p.width;244sc->p.mask = ((1 << sc->p.width) - 1) << sc->p.shift;245sc->p.value = clkdef->p.value;246sc->p.flags = clkdef->p.flags;247248sc->gate_shift = clkdef->gate_shift;249250sc->lock_shift = clkdef->lock_shift;251sc->lock_retries = clkdef->lock_retries;252253sc->flags = clkdef->flags;254255clknode_register(clkdom, clk);256257return (0);258}259260261