Path: blob/main/sys/dev/clk/allwinner/aw_clk_nkmp.c
39536 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_nkmp.h>3334#include "clkdev_if.h"3536/*37* clknode for clocks matching the formula :38*39* clk = (clkin * n * k) / (m * p)40*41*/4243struct aw_clk_nkmp_sc {44uint32_t offset;4546struct aw_clk_factor n;47struct aw_clk_factor k;48struct aw_clk_factor m;49struct aw_clk_factor p;5051uint32_t mux_shift;52uint32_t mux_mask;53uint32_t gate_shift;54uint32_t lock_shift;55uint32_t lock_retries;56uint32_t update_shift;5758uint32_t flags;59};6061#define WRITE4(_clk, off, val) \62CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)63#define READ4(_clk, off, val) \64CLKDEV_READ_4(clknode_get_device(_clk), off, val)65#define MODIFY4(_clk, off, clr, set ) \66CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)67#define DEVICE_LOCK(_clk) \68CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))69#define DEVICE_UNLOCK(_clk) \70CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))7172static int73aw_clk_nkmp_init(struct clknode *clk, device_t dev)74{75struct aw_clk_nkmp_sc *sc;76uint32_t val, idx;7778sc = clknode_get_softc(clk);7980idx = 0;81if ((sc->flags & AW_CLK_HAS_MUX) != 0) {82DEVICE_LOCK(clk);83READ4(clk, sc->offset, &val);84DEVICE_UNLOCK(clk);8586idx = (val & sc->mux_mask) >> sc->mux_shift;87}8889clknode_init_parent_idx(clk, idx);90return (0);91}9293static int94aw_clk_nkmp_set_gate(struct clknode *clk, bool enable)95{96struct aw_clk_nkmp_sc *sc;97uint32_t val;9899sc = clknode_get_softc(clk);100101if ((sc->flags & AW_CLK_HAS_GATE) == 0)102return (0);103104DEVICE_LOCK(clk);105READ4(clk, sc->offset, &val);106if (enable)107val |= (1 << sc->gate_shift);108else109val &= ~(1 << sc->gate_shift);110WRITE4(clk, sc->offset, val);111DEVICE_UNLOCK(clk);112113return (0);114}115116static int117aw_clk_nkmp_set_mux(struct clknode *clk, int index)118{119struct aw_clk_nkmp_sc *sc;120uint32_t val;121122sc = clknode_get_softc(clk);123124if ((sc->flags & AW_CLK_HAS_MUX) == 0)125return (0);126127DEVICE_LOCK(clk);128READ4(clk, sc->offset, &val);129val &= ~sc->mux_mask;130val |= index << sc->mux_shift;131WRITE4(clk, sc->offset, val);132DEVICE_UNLOCK(clk);133134return (0);135}136137static uint64_t138aw_clk_nkmp_find_best(struct aw_clk_nkmp_sc *sc, uint64_t fparent, uint64_t *fout,139uint32_t *factor_n, uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_p)140{141uint64_t cur, best;142uint32_t n, k, m, p;143144best = 0;145*factor_n = 0;146*factor_k = 0;147*factor_m = 0;148*factor_p = 0;149150for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); ) {151for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); ) {152for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); ) {153for (p = aw_clk_factor_get_min(&sc->p); p <= aw_clk_factor_get_max(&sc->p); ) {154cur = (fparent * n * k) / (m * p);155if ((*fout - cur) < (*fout - best)) {156best = cur;157*factor_n = n;158*factor_k = k;159*factor_m = m;160*factor_p = p;161}162if (best == *fout)163return (best);164if ((sc->p.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)165p <<= 1;166else167p++;168}169if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)170m <<= 1;171else172m++;173}174if ((sc->k.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)175k <<= 1;176else177k++;178}179if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)180n <<= 1;181else182n++;183}184185return best;186}187188static void189aw_clk_nkmp_set_freq_scale(struct clknode *clk, struct aw_clk_nkmp_sc *sc,190uint32_t factor_n, uint32_t factor_k, uint32_t factor_m, uint32_t factor_p)191{192uint32_t val, m, p;193int retry;194195DEVICE_LOCK(clk);196READ4(clk, sc->offset, &val);197198m = aw_clk_get_factor(val, &sc->m);199p = aw_clk_get_factor(val, &sc->p);200201if (p < factor_p) {202val &= ~sc->p.mask;203val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;204WRITE4(clk, sc->offset, val);205DELAY(2000);206}207208if (m < factor_m) {209val &= ~sc->m.mask;210val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;211WRITE4(clk, sc->offset, val);212DELAY(2000);213}214215val &= ~sc->n.mask;216val &= ~sc->k.mask;217val |= aw_clk_factor_get_value(&sc->n, factor_n) << sc->n.shift;218val |= aw_clk_factor_get_value(&sc->k, factor_k) << sc->k.shift;219WRITE4(clk, sc->offset, val);220DELAY(2000);221222if (m > factor_m) {223val &= ~sc->m.mask;224val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;225WRITE4(clk, sc->offset, val);226DELAY(2000);227}228229if (p > factor_p) {230val &= ~sc->p.mask;231val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;232WRITE4(clk, sc->offset, val);233DELAY(2000);234}235236if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {237for (retry = 0; retry < sc->lock_retries; retry++) {238READ4(clk, sc->offset, &val);239if ((val & (1 << sc->lock_shift)) != 0)240break;241DELAY(1000);242}243}244245DEVICE_UNLOCK(clk);246}247248static int249aw_clk_nkmp_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,250int flags, int *stop)251{252struct aw_clk_nkmp_sc *sc;253uint64_t best;254uint32_t val, best_n, best_k, best_m, best_p;255int retry;256257sc = clknode_get_softc(clk);258259best = aw_clk_nkmp_find_best(sc, fparent, fout,260&best_n, &best_k, &best_m, &best_p);261if ((flags & CLK_SET_DRYRUN) != 0) {262*fout = best;263*stop = 1;264return (0);265}266267if ((best < *fout) &&268((flags & CLK_SET_ROUND_DOWN) != 0)) {269*stop = 1;270return (ERANGE);271}272if ((best > *fout) &&273((flags & CLK_SET_ROUND_UP) != 0)) {274*stop = 1;275return (ERANGE);276}277278if ((sc->flags & AW_CLK_SCALE_CHANGE) != 0)279aw_clk_nkmp_set_freq_scale(clk, sc,280best_n, best_k, best_m, best_p);281else {282DEVICE_LOCK(clk);283READ4(clk, sc->offset, &val);284val &= ~sc->n.mask;285val &= ~sc->k.mask;286val &= ~sc->m.mask;287val &= ~sc->p.mask;288val |= aw_clk_factor_get_value(&sc->n, best_n) << sc->n.shift;289val |= aw_clk_factor_get_value(&sc->k, best_k) << sc->k.shift;290val |= aw_clk_factor_get_value(&sc->m, best_m) << sc->m.shift;291val |= aw_clk_factor_get_value(&sc->p, best_p) << sc->p.shift;292WRITE4(clk, sc->offset, val);293DELAY(2000);294DEVICE_UNLOCK(clk);295296if ((sc->flags & AW_CLK_HAS_UPDATE) != 0) {297DEVICE_LOCK(clk);298READ4(clk, sc->offset, &val);299val |= 1 << sc->update_shift;300WRITE4(clk, sc->offset, val);301DELAY(2000);302DEVICE_UNLOCK(clk);303}304305if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {306for (retry = 0; retry < sc->lock_retries; retry++) {307READ4(clk, sc->offset, &val);308if ((val & (1 << sc->lock_shift)) != 0)309break;310DELAY(1000);311}312}313}314315*fout = best;316*stop = 1;317318return (0);319}320321static int322aw_clk_nkmp_recalc(struct clknode *clk, uint64_t *freq)323{324struct aw_clk_nkmp_sc *sc;325uint32_t val, m, n, k, p;326327sc = clknode_get_softc(clk);328329DEVICE_LOCK(clk);330READ4(clk, sc->offset, &val);331DEVICE_UNLOCK(clk);332333n = aw_clk_get_factor(val, &sc->n);334k = aw_clk_get_factor(val, &sc->k);335m = aw_clk_get_factor(val, &sc->m);336p = aw_clk_get_factor(val, &sc->p);337338*freq = (*freq * n * k) / (m * p);339340return (0);341}342343static clknode_method_t aw_nkmp_clknode_methods[] = {344/* Device interface */345CLKNODEMETHOD(clknode_init, aw_clk_nkmp_init),346CLKNODEMETHOD(clknode_set_gate, aw_clk_nkmp_set_gate),347CLKNODEMETHOD(clknode_set_mux, aw_clk_nkmp_set_mux),348CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nkmp_recalc),349CLKNODEMETHOD(clknode_set_freq, aw_clk_nkmp_set_freq),350CLKNODEMETHOD_END351};352353DEFINE_CLASS_1(aw_nkmp_clknode, aw_nkmp_clknode_class, aw_nkmp_clknode_methods,354sizeof(struct aw_clk_nkmp_sc), clknode_class);355356int357aw_clk_nkmp_register(struct clkdom *clkdom, struct aw_clk_nkmp_def *clkdef)358{359struct clknode *clk;360struct aw_clk_nkmp_sc *sc;361362clk = clknode_create(clkdom, &aw_nkmp_clknode_class, &clkdef->clkdef);363if (clk == NULL)364return (1);365366sc = clknode_get_softc(clk);367368sc->offset = clkdef->offset;369370sc->n.shift = clkdef->n.shift;371sc->n.width = clkdef->n.width;372sc->n.mask = ((1 << clkdef->n.width) - 1) << sc->n.shift;373sc->n.value = clkdef->n.value;374sc->n.flags = clkdef->n.flags;375376sc->k.shift = clkdef->k.shift;377sc->k.width = clkdef->k.width;378sc->k.mask = ((1 << clkdef->k.width) - 1) << sc->k.shift;379sc->k.value = clkdef->k.value;380sc->k.flags = clkdef->k.flags;381382sc->m.shift = clkdef->m.shift;383sc->m.width = clkdef->m.width;384sc->m.mask = ((1 << clkdef->m.width) - 1) << sc->m.shift;385sc->m.value = clkdef->m.value;386sc->m.flags = clkdef->m.flags;387388sc->p.shift = clkdef->p.shift;389sc->p.width = clkdef->p.width;390sc->p.mask = ((1 << clkdef->p.width) - 1) << sc->p.shift;391sc->p.value = clkdef->p.value;392sc->p.flags = clkdef->p.flags;393394sc->mux_shift = clkdef->mux_shift;395sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;396397sc->gate_shift = clkdef->gate_shift;398sc->lock_shift = clkdef->lock_shift;399sc->lock_retries = clkdef->lock_retries;400sc->update_shift = clkdef->update_shift;401sc->flags = clkdef->flags;402403clknode_register(clkdom, clk);404405return (0);406}407408409