Path: blob/main/sys/dev/clk/allwinner/aw_clk_mipi.c
39536 views
/*-1* Copyright (c) 2019 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_mipi.h>3334#include "clkdev_if.h"3536/* #define dprintf(format, arg...) printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */37#define dprintf(format, arg...)3839/*40* clknode for PLL_MIPI :41*42* clk = (pll_video0 * n * k) / m when vfb_sel=043* clk depend on sint_frac, sdiv2, s6p25_7p5, pll_feedback_div when vfb_sel=144*45*/4647struct aw_clk_mipi_sc {48uint32_t offset;4950struct aw_clk_factor k;51struct aw_clk_factor m;52struct aw_clk_factor n;5354uint64_t min_freq;55uint64_t max_freq;5657uint32_t gate_shift;58uint32_t lock_shift;59uint32_t lock_retries;6061uint32_t flags;62};6364#define WRITE4(_clk, off, val) \65CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)66#define READ4(_clk, off, val) \67CLKDEV_READ_4(clknode_get_device(_clk), off, val)68#define DEVICE_LOCK(_clk) \69CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))70#define DEVICE_UNLOCK(_clk) \71CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))7273#define LDO1_EN_SHIFT 2374#define LDO2_EN_SHIFT 2275#define VFB_SEL_SHIFT 167677static int78aw_clk_mipi_init(struct clknode *clk, device_t dev)79{8081clknode_init_parent_idx(clk, 0);82return (0);83}8485static int86aw_clk_mipi_set_gate(struct clknode *clk, bool enable)87{88struct aw_clk_mipi_sc *sc;89uint32_t val;9091sc = clknode_get_softc(clk);9293dprintf("%sabling gate\n", enable ? "En" : "Dis");94DEVICE_LOCK(clk);95READ4(clk, sc->offset, &val);96if (enable) {97val |= (1 << sc->gate_shift);98val |= (1 << LDO1_EN_SHIFT);99val |= (1 << LDO2_EN_SHIFT);100} else {101val &= ~(1 << sc->gate_shift);102val &= ~(1 << LDO1_EN_SHIFT);103val &= ~(1 << LDO2_EN_SHIFT);104}105WRITE4(clk, sc->offset, val);106DEVICE_UNLOCK(clk);107108return (0);109}110111static uint64_t112aw_clk_mipi_find_best(struct aw_clk_mipi_sc *sc, uint64_t fparent, uint64_t *fout,113uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_n)114{115uint64_t cur, best;116uint32_t n, k, m;117118best = 0;119*factor_n = 0;120*factor_k = 0;121*factor_m = 0;122123for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); n++) {124for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); k++) {125for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); m++) {126cur = (fparent * n * k) / m;127if ((*fout - cur) < (*fout - best)) {128best = cur;129*factor_n = n;130*factor_k = k;131*factor_m = m;132}133if (best == *fout)134return (best);135}136}137}138139return best;140}141142static int143aw_clk_mipi_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,144int flags, int *stop)145{146struct aw_clk_mipi_sc *sc;147uint64_t best = 0;148uint32_t best_k, best_m, best_n;149uint32_t k, m, n;150uint32_t val;151uint32_t retry;152153sc = clknode_get_softc(clk);154155best = aw_clk_mipi_find_best(sc, fparent, fout, &best_k, &best_m, &best_n);156157if (best < sc->min_freq ||158best > sc->max_freq) {159printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n",160__func__, best, clknode_get_name(clk),161sc->min_freq, sc->max_freq);162return (ERANGE);163}164if ((flags & CLK_SET_DRYRUN) != 0) {165*fout = best;166*stop = 1;167return (0);168}169170DEVICE_LOCK(clk);171READ4(clk, sc->offset, &val);172/* Disable clock during freq changes */173val &= ~(1 << sc->gate_shift);174WRITE4(clk, sc->offset, val);175176k = aw_clk_factor_get_value(&sc->k, best_k);177n = aw_clk_factor_get_value(&sc->n, best_n);178m = aw_clk_factor_get_value(&sc->m, best_m);179val &= ~sc->k.mask;180val &= ~sc->m.mask;181val &= ~sc->n.mask;182val |= k << sc->k.shift;183val |= m << sc->m.shift;184val |= n << sc->n.shift;185186/* Write the clock changes */187WRITE4(clk, sc->offset, val);188189/* Enable clock now that we've change it */190val |= 1 << sc->gate_shift;191WRITE4(clk, sc->offset, val);192DEVICE_UNLOCK(clk);193194for (retry = 0; retry < sc->lock_retries; retry++) {195READ4(clk, sc->offset, &val);196if ((val & (1 << sc->lock_shift)) != 0)197break;198DELAY(1000);199}200201*fout = best;202*stop = 1;203204return (0);205}206207static int208aw_clk_mipi_recalc(struct clknode *clk, uint64_t *freq)209{210struct aw_clk_mipi_sc *sc;211uint32_t val, m, n, k;212213sc = clknode_get_softc(clk);214215DEVICE_LOCK(clk);216READ4(clk, sc->offset, &val);217DEVICE_UNLOCK(clk);218219k = aw_clk_get_factor(val, &sc->k);220m = aw_clk_get_factor(val, &sc->m);221n = aw_clk_get_factor(val, &sc->n);222223*freq = (*freq * n * k) / m;224225return (0);226}227228static clknode_method_t aw_mipi_clknode_methods[] = {229/* Device interface */230CLKNODEMETHOD(clknode_init, aw_clk_mipi_init),231CLKNODEMETHOD(clknode_set_gate, aw_clk_mipi_set_gate),232CLKNODEMETHOD(clknode_recalc_freq, aw_clk_mipi_recalc),233CLKNODEMETHOD(clknode_set_freq, aw_clk_mipi_set_freq),234CLKNODEMETHOD_END235};236237DEFINE_CLASS_1(aw_mipi_clknode, aw_mipi_clknode_class, aw_mipi_clknode_methods,238sizeof(struct aw_clk_mipi_sc), clknode_class);239240int241aw_clk_mipi_register(struct clkdom *clkdom, struct aw_clk_mipi_def *clkdef)242{243struct clknode *clk;244struct aw_clk_mipi_sc *sc;245246clk = clknode_create(clkdom, &aw_mipi_clknode_class, &clkdef->clkdef);247if (clk == NULL)248return (1);249250sc = clknode_get_softc(clk);251252sc->offset = clkdef->offset;253254sc->k.shift = clkdef->k.shift;255sc->k.width = clkdef->k.width;256sc->k.mask = ((1 << sc->k.width) - 1) << sc->k.shift;257sc->k.value = clkdef->k.value;258sc->k.flags = clkdef->k.flags;259sc->k.min_value = clkdef->k.min_value;260261sc->m.shift = clkdef->m.shift;262sc->m.width = clkdef->m.width;263sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;264sc->m.value = clkdef->m.value;265sc->m.flags = clkdef->m.flags;266sc->m.min_value = clkdef->m.min_value;267268sc->n.shift = clkdef->n.shift;269sc->n.width = clkdef->n.width;270sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;271sc->n.value = clkdef->n.value;272sc->n.flags = clkdef->n.flags;273sc->n.min_value = clkdef->n.min_value;274275sc->min_freq = clkdef->min_freq;276sc->max_freq = clkdef->max_freq;277278sc->gate_shift = clkdef->gate_shift;279280sc->lock_shift = clkdef->lock_shift;281sc->lock_retries = clkdef->lock_retries;282283sc->flags = clkdef->flags;284285clknode_register(clkdom, clk);286287return (0);288}289290291