Path: blob/main/sys/arm64/freescale/imx/clk/imx_clk_sscg_pll.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2020 Oleksandr Tymoshenko <[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_sscg_pll.h>3435#include "clkdev_if.h"3637struct imx_clk_sscg_pll_sc {38uint32_t offset;39};4041#define WRITE4(_clk, off, val) \42CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)43#define READ4(_clk, off, val) \44CLKDEV_READ_4(clknode_get_device(_clk), off, val)45#define DEVICE_LOCK(_clk) \46CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))47#define DEVICE_UNLOCK(_clk) \48CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))4950#define CFG0 0x0051#define CFG0_PLL_LOCK (1 << 31)52#define CFG0_PD (1 << 7)53#define CFG0_BYPASS2 (1 << 5)54#define CFG0_BYPASS1 (1 << 4)55#define CFG1 0x0456#define CFG2 0x0857#define CFG2_DIVR1_MASK (7 << 25)58#define CFG2_DIVR1_SHIFT 2559#define CFG2_DIVR2_MASK (0x3f << 19)60#define CFG2_DIVR2_SHIFT 1961#define CFG2_DIVF1_MASK (0x3f << 13)62#define CFG2_DIVF1_SHIFT 1363#define CFG2_DIVF2_MASK (0x3f << 7)64#define CFG2_DIVF2_SHIFT 765#define CFG2_DIV_MASK (0x3f << 1)66#define CFG2_DIV_SHIFT 16768#if 069#define dprintf(format, arg...) \70printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)71#else72#define dprintf(format, arg...)73#endif7475static int76imx_clk_sscg_pll_init(struct clknode *clk, device_t dev)77{78if (clknode_get_parents_num(clk) > 1) {79device_printf(clknode_get_device(clk),80"error: SSCG PLL does not support more than one parent yet\n");81return (EINVAL);82}83clknode_init_parent_idx(clk, 0);8485return (0);86}8788static int89imx_clk_sscg_pll_set_gate(struct clknode *clk, bool enable)90{91struct imx_clk_sscg_pll_sc *sc;92uint32_t cfg0;93int timeout;9495sc = clknode_get_softc(clk);9697DEVICE_LOCK(clk);98READ4(clk, sc->offset + CFG0, &cfg0);99if (enable)100cfg0 &= ~(CFG0_PD);101else102cfg0 |= CFG0_PD;103WRITE4(clk, sc->offset + CFG0, cfg0);104105/* Reading lock */106if (enable) {107for (timeout = 1000; timeout; timeout--) {108READ4(clk, sc->offset + CFG0, &cfg0);109if (cfg0 & CFG0_PLL_LOCK)110break;111DELAY(1);112}113}114115DEVICE_UNLOCK(clk);116117return (0);118}119120static int121imx_clk_sscg_pll_recalc(struct clknode *clk, uint64_t *freq)122{123struct imx_clk_sscg_pll_sc *sc;124uint32_t cfg0, cfg2;125int divr1, divr2, divf1, divf2, div;126127sc = clknode_get_softc(clk);128129DEVICE_LOCK(clk);130READ4(clk, sc->offset + CFG0, &cfg0);131READ4(clk, sc->offset + CFG2, &cfg2);132DEVICE_UNLOCK(clk);133134/* PLL is bypassed */135if (cfg0 & CFG0_BYPASS2)136return (0);137138divr1 = (cfg2 & CFG2_DIVR1_MASK) >> CFG2_DIVR1_SHIFT;139divr2 = (cfg2 & CFG2_DIVR2_MASK) >> CFG2_DIVR2_SHIFT;140divf1 = (cfg2 & CFG2_DIVF1_MASK) >> CFG2_DIVF1_SHIFT;141divf2 = (cfg2 & CFG2_DIVF2_MASK) >> CFG2_DIVF2_SHIFT;142div = (cfg2 & CFG2_DIV_MASK) >> CFG2_DIV_SHIFT;143144if (cfg0 & CFG0_BYPASS1) {145*freq = *freq / ((divr2 + 1) * (div + 1));146return (0);147}148149*freq *= 2 * (divf1 + 1) * (divf2 + 1);150*freq /= (divr1 + 1) * (divr2 + 1) * (div + 1);151152return (0);153}154155static clknode_method_t imx_clk_sscg_pll_clknode_methods[] = {156/* Device interface */157CLKNODEMETHOD(clknode_init, imx_clk_sscg_pll_init),158CLKNODEMETHOD(clknode_set_gate, imx_clk_sscg_pll_set_gate),159CLKNODEMETHOD(clknode_recalc_freq, imx_clk_sscg_pll_recalc),160CLKNODEMETHOD_END161};162163DEFINE_CLASS_1(imx_clk_sscg_pll_clknode, imx_clk_sscg_pll_clknode_class,164imx_clk_sscg_pll_clknode_methods, sizeof(struct imx_clk_sscg_pll_sc),165clknode_class);166167int168imx_clk_sscg_pll_register(struct clkdom *clkdom,169struct imx_clk_sscg_pll_def *clkdef)170{171struct clknode *clk;172struct imx_clk_sscg_pll_sc *sc;173174clk = clknode_create(clkdom, &imx_clk_sscg_pll_clknode_class,175&clkdef->clkdef);176if (clk == NULL)177return (1);178179sc = clknode_get_softc(clk);180181sc->offset = clkdef->offset;182183clknode_register(clkdom, clk);184185return (0);186}187188189