From patchwork Mon Dec 31 16:59:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jagan Teki X-Patchwork-Id: 130 Return-Path: X-Original-To: linux-amarula@patchwork.amarulasolutions.com Delivered-To: linux-amarula@patchwork.amarulasolutions.com Received: from mail-pf1-f200.google.com (cartago.priv [10.11.12.1]) by cassiopea.amarulasolutions.com (Postfix) with ESMTPS id C32472E002E for ; Mon, 31 Dec 2018 18:01:49 +0100 (CET) Received: by mail-pf1-f200.google.com with SMTP id 75sf29326509pfq.8 for ; Mon, 31 Dec 2018 09:01:49 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1546275708; cv=pass; d=google.com; s=arc-20160816; b=dDdw3wCkC9E1AquIidguajbfJaRlg43kp9QWGl5lbZpidJRyp06XuOnuenR/p85i4w dCQ+pvwowICp2C3m+VExHM/0nWGrnoV2L/wgRS1fGSXMLDseh23pSqh4F/jZk1Wr7PrW 7epEgX3AFxPoiuJ/E7eoAw5aMASljQOb85Bnk6kx8rGV26unINI2R7/AhPxt+KTk8XBe DpHtgJje0Uf11KgieluYLOKsoS1LOXOh85etQWeBlUEo2QqDmBD8gsLlpntWA4bGdKIY /ho5OpX/x7VFMMz26PARoB8WfV7dCKYq/QEBI5PBVzfrD2WToLmhFGYHdzaeVe6heZ9k JwKw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-archive:list-help:list-post:list-id :mailing-list:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=pFO9lBbO5k+0sd9APrNyOGgo2aQlu3EXXOOXAsArhEk=; b=yZt/YWrBMXH0wegfs17ZJDzOWn3M+KU4nUoe/FSn4PmpGHw2T+pW1u89ZSvuZniaH2 4j5OGk5rkp5BeiopJSr6hMN+ZCv6DymCW4gpSILEUkFxm4NWqzBfvZR3dqZk+uz1gNQm WkMqFFW33D8LdNwd+V7F0E3vdfO95/1wDEZ/7PeKglTWKnqTO/hKn8JJEBGv9F4P2Pgs nyJHV7tpRGP8UYIX+FLNNLoI/VGDQ20q3Y/ocD+L7VEvxrsswwauGUMTFYYFeUAWGnZe hnXTlMBcL6sI/WeJ0H1P2pdcxSx5uFbeq2ptNi0mTUj5vZRh4LIaxJIiUXUtZ9zp3wXI AIUw== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b="cn42T+/A"; spf=pass (google.com: domain of jagan@amarulasolutions.com designates 209.85.220.65 as permitted sender) smtp.mailfrom=jagan@amarulasolutions.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amarulasolutions.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=pFO9lBbO5k+0sd9APrNyOGgo2aQlu3EXXOOXAsArhEk=; b=OVY25pp9Rvjo7OWJC8uga8wpqAHuOzMcj9XNjkaj4NzSat4nni7mtt6mztX+mRuoL6 tK6LgOfjSFq2GEdrptuTsEqceWNAfA8cu3Lt9j4mXBj2XnnbSuMVfzue35KY61Tn1MZT DKo9mvjTDmNYhRU3AITIlZuieVfRBfdiserJ8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-spam-checked-in-group:list-post:list-help:list-archive :list-unsubscribe; bh=pFO9lBbO5k+0sd9APrNyOGgo2aQlu3EXXOOXAsArhEk=; b=os+quwTWV3TwJgKYzLzL9eYHAOv9p0hLd5Q3i9vzzpZ6LVAc6PIsVxFafXmHtkZc2d qWOQ0EuxACfOXrdKZ8zVPlYOyrtO2JD4MJXb9NVk50UtS3fs85MURdDoxecL6w/z825C RvCArM/8aO+88rySjmigF3sYlAe2MMhdK65lP4ADRec6d4UGGPQ+I7Jtx9qU+qnOizME 1Wb74l7hAkYpratSV1Qv8D0B4xIzB/bPpCmcV87rsd2ZffWe8c2W6iqgpvpJ9cZvi5y8 iS4nNrcwgZ3Qax8mbonC7ByVQJNqYgCQ9Gu3ex0T57vzjUOgEx9+0w2SSNtXm44txJF7 L3mQ== X-Gm-Message-State: AA+aEWbvK/PBly787P9mC9aSWXD+KImI6mu/PiLn2jkH9wAa6KHv5E3/ F2bDiium7+E8tIrth4mYm1zLswst X-Google-Smtp-Source: AFSGD/V5BbB9uZpAkSjk5pWWWMcVT164ycwVX3CdWqWcOtQKs74n/fxnkZ1E+ocUJu6kHhhLB7Fk2A== X-Received: by 2002:aa7:83d0:: with SMTP id j16mr17195707pfn.92.1546275708380; Mon, 31 Dec 2018 09:01:48 -0800 (PST) X-BeenThere: linux-amarula@amarulasolutions.com Received: by 2002:a17:902:30f:: with SMTP id 15ls13414680pld.8.gmail; Mon, 31 Dec 2018 09:01:48 -0800 (PST) X-Received: by 2002:a17:902:8f97:: with SMTP id z23mr38205370plo.283.1546275707983; Mon, 31 Dec 2018 09:01:47 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1546275707; cv=none; d=google.com; s=arc-20160816; b=J2Jhm6Qanl+DuthNHaNl/i37GMvYOYlRNXLWjMF0RvqoaQdE4wx7KIMBI8OQ5zd6K0 eQwFIZQKvcYaM04JVIbp7QavDlcUxw5Ha3ksFyUthpmh0n1N1rz7U++bF/ifBpDA8CP5 /YLQaBywucQtrA6xuX26eYzwP6LUhHcLBGbzfAhTSvW05JlkCc39/hgEjJlUBRwRIFu2 g+qp4p9O95pgFd5Wy0SgsP7/CS1pP7Q9RHJMCjWCuMRAA1Nsij7bxav8zrKui7MN1reh iy/kdCF3sw1c/WgK1D1Yn+3ia3kM6SCMTFo+s3QaqKS49Z+biJ2uHyMMd2NT2sEkr+Gn U8Ng== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=pFO9lBbO5k+0sd9APrNyOGgo2aQlu3EXXOOXAsArhEk=; b=YFi3s7Ps2d5X9BdNaXYpelrPqQVr1fizj8sBGGjRK2K+wFsdh/bgnywnSrmT1pmHIa XaKCUInDMKsQeuLtTrEmbzQN9t+kJD5j4NLHZwSwCI0vv1Ev0K2+CwWiBuJsTDCf16LT Il+B0xF2LIqxCoSoOlWynBr9CaL1mGkNvSaNCbtd9gt2DP+jbOjHqGocPqDyWtcltI6c O4VqSYY4udcy+S3lb90NyD/whcRAa7mcLJP+cloJX+FhtEveLLBoFq+9aMK7voWROl+J MjjMnzwPRq1YxLzCEWAuA9/zt4rBrLHoENABP6KOi1dBxWHZI8Y/8fFVQimMdLyqf4vM I4KA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b="cn42T+/A"; spf=pass (google.com: domain of jagan@amarulasolutions.com designates 209.85.220.65 as permitted sender) smtp.mailfrom=jagan@amarulasolutions.com Received: from mail-sor-f65.google.com (mail-sor-f65.google.com. [209.85.220.65]) by mx.google.com with SMTPS id b35sor13240207plb.6.2018.12.31.09.01.47 for (Google Transport Security); Mon, 31 Dec 2018 09:01:47 -0800 (PST) Received-SPF: pass (google.com: domain of jagan@amarulasolutions.com designates 209.85.220.65 as permitted sender) client-ip=209.85.220.65; X-Received: by 2002:a17:902:9691:: with SMTP id n17mr14878624plp.9.1546275707488; Mon, 31 Dec 2018 09:01:47 -0800 (PST) Received: from localhost.localdomain ([115.97.184.237]) by smtp.gmail.com with ESMTPSA id p7sm90692925pfj.72.2018.12.31.09.01.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 31 Dec 2018 09:01:47 -0800 (PST) From: Jagan Teki To: Maxime Ripard , Andre Przywara Cc: Chen-Yu Tsai , Simon Glass , Tom Rini , u-boot@lists.denx.de, linux-sunxi@googlegroups.com, Michael Trimarchi , linux-amarula@amarulasolutions.com, Jagan Teki Subject: [PATCH v5 15/26] clk: sunxi: Add ccu clock tree support Date: Mon, 31 Dec 2018 22:29:16 +0530 Message-Id: <20181231165927.13803-16-jagan@amarulasolutions.com> X-Mailer: git-send-email 2.18.0.321.gffc6fa0e3 In-Reply-To: <20181231165927.13803-1-jagan@amarulasolutions.com> References: <20181231165927.13803-1-jagan@amarulasolutions.com> MIME-Version: 1.0 X-Original-Sender: jagan@amarulasolutions.com X-Original-Authentication-Results: mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b="cn42T+/A"; spf=pass (google.com: domain of jagan@amarulasolutions.com designates 209.85.220.65 as permitted sender) smtp.mailfrom=jagan@amarulasolutions.com Precedence: list Mailing-list: list linux-amarula@amarulasolutions.com; contact linux-amarula+owners@amarulasolutions.com List-ID: X-Spam-Checked-In-Group: linux-amarula@amarulasolutions.com X-Google-Group-Id: 476853432473 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Clock control unit comprises of parent clocks, gates, multiplexers, dividers, multipliers, pre/post dividers and flags etc. So, the U-Boot implementation of ccu has divided into gates and tree. gates are generic clock configuration of enable/disable bit management which can be handle via ccu_clock_gate. Tree clocks are parent clock type, fixed clocks, mp, nk, nkm, nkmp, pre/post div, flags etc. which were managed via ccu_clock_tree. This patch add support for MP, NK, MISC, FIXED clock types as part of ccu clock tree with get_rate functionality this eventually used by uart driver. and rest of the infrastructure will try to add while CLK is being used on respective peripherals. Note that few of the tree type clock would require to enable gates on their specific clock, in that case we need to add the gate details via ccu_clock_gate, example: MP with gate so the gate offset, bit value should add as part of ccu_clock_gate. Signed-off-by: Jagan Teki --- arch/arm/include/asm/arch-sunxi/ccu.h | 192 +++++++++++++++++++++++++- drivers/clk/sunxi/clk_a64.c | 40 ++++++ drivers/clk/sunxi/clk_sunxi.c | 182 ++++++++++++++++++++++++ 3 files changed, 413 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/arch-sunxi/ccu.h b/arch/arm/include/asm/arch-sunxi/ccu.h index 3fdc26978d..61b8c36b3b 100644 --- a/arch/arm/include/asm/arch-sunxi/ccu.h +++ b/arch/arm/include/asm/arch-sunxi/ccu.h @@ -7,15 +7,204 @@ #ifndef _ASM_ARCH_CCU_H #define _ASM_ARCH_CCU_H +#define OSC_32K_ULL 32000ULL +#define OSC_24M_ULL 24000000ULL + +/** + * enum ccu_clk_type - ccu clock types + * + * @CCU_CLK_TYPE_MISC: misc clock type + * @CCU_CLK_TYPE_FIXED: fixed clock type + * @CCU_CLK_TYPE_MP: mp clock type + * @CCU_CLK_TYPE_NK: nk clock type + */ +enum ccu_clk_type { + CCU_CLK_TYPE_MISC = 0, + CCU_CLK_TYPE_FIXED = 1, + CCU_CLK_TYPE_MP = 2, + CCU_CLK_TYPE_NK = 3, +}; + /** * enum ccu_clk_flags - ccu clock flags * - * @CCU_CLK_F_INIT_DONE: clock gate init done check + * @CCU_CLK_F_INIT_DONE: clock tree/gate init done check + * @CCU_CLK_F_POSTDIV: clock post divider */ enum ccu_clk_flags { CCU_CLK_F_INIT_DONE = BIT(0), + CCU_CLK_F_POSTDIV = BIT(1), }; +/** + * struct ccu_mult - ccu clock multiplier + * + * @shift: multiplier shift value + * @width: multiplier width value + * @offset: multiplier offset + * @min: minimum multiplier + * @max: maximum multiplier + */ +struct ccu_mult { + u8 shift; + u8 width; + u8 offset; + u8 min; + u8 max; +}; + +#define _CCU_MULT_OFF_MIN_MAX(_shift, _width, _offset, \ + _min, _max) { \ + .shift = _shift, \ + .width = _width, \ + .offset = _offset, \ + .min = _min, \ + .max = _max, \ +} + +#define _CCU_MULT_MIN(_shift, _width, _min) \ + _CCU_MULT_OFF_MIN_MAX(_shift, _width, 1, _min, 0) + +#define _CCU_MULT(_shift, _width) \ + _CCU_MULT_OFF_MIN_MAX(_shift, _width, 1, 1, 0) + +/** + * struct ccu_mux - ccu clock multiplexer + * + * @shift: multiplexer shift value + * @width: multiplexer width value + */ +struct ccu_mux { + u8 shift; + u8 width; +}; + +#define _CCU_MUX(_shift, _width) { \ + .shift = _shift, \ + .width = _width, \ +} + +/** + * struct ccu_div - ccu clock divider + * + * @shift: divider shift value + * @width: divider width value + * @offset: divider offset + * @max: maximum divider value + */ +struct ccu_div { + u8 shift; + u8 width; + u32 offset; + u32 max; +}; + +#define _CCU_DIV(_shift, _width) { \ + .shift = _shift, \ + .width = _width, \ + .offset = 1, \ + .max = 0, \ +} + +/** + * struct ccu_clk_tree - ccu clock tree + * + * @parent: parent clock tree + * @type: clock type + * @off: clock tree offset + * @m: divider m + * @p: divider p + * @mux: multiplexer mux + * @post: post divider value + * @n: multiplier n + * @k: multiplier k + * @fixed_rate: fixed rate + * @flags: clock tree flags + */ +struct ccu_clk_tree { + const unsigned long *parent; + enum ccu_clk_type type; + u16 off; + + struct ccu_div m; + struct ccu_div p; + struct ccu_mux mux; + unsigned int postdiv; + + struct ccu_mult n; + struct ccu_mult k; + + ulong fixed_rate; + enum ccu_clk_flags flags; +}; + +#define TREE(_parent, _type, _off, \ + _m, _p, \ + _mux, \ + _postdiv, \ + _n, _k, \ + _fixed_rate, \ + _flags) { \ + .parent = _parent, \ + .type = _type, \ + .off = _off, \ + .m = _m, \ + .p = _p, \ + .mux = _mux, \ + .postdiv = _postdiv, \ + .n = _n, \ + .k = _k, \ + .fixed_rate = _fixed_rate, \ + .flags = _flags, \ +} + +#define MISC(_parent) \ + TREE(_parent, CCU_CLK_TYPE_MISC, 0, \ + {0}, {0}, \ + {0}, \ + 0, \ + {0}, {0}, \ + 0, \ + CCU_CLK_F_INIT_DONE) + +#define FIXED(_fixed_rate) \ + TREE(NULL, CCU_CLK_TYPE_FIXED, 0, \ + {0}, {0}, \ + {0}, \ + 0, \ + {0}, {0}, \ + _fixed_rate, \ + CCU_CLK_F_INIT_DONE) + +#define NK(_parent, _off, \ + _nshift, _nwidth, \ + _kshift, _kwidth, _kmin, \ + _postdiv, \ + _flags) \ + TREE(_parent, CCU_CLK_TYPE_NK, _off, \ + {0}, {0}, \ + {0}, \ + _postdiv, \ + _CCU_MULT(_nshift, _nwidth), \ + _CCU_MULT_MIN(_kshift, _kwidth, _kmin), \ + 0, \ + CCU_CLK_F_INIT_DONE | _flags) + +#define MP(_parent, _off, \ + _mshift, _mwidth, \ + _pshift, _pwidth, \ + _muxshift, _muxwidth, \ + _postdiv, \ + _flags) \ + TREE(_parent, CCU_CLK_TYPE_MP, _off, \ + _CCU_DIV(_mshift, _mwidth), \ + _CCU_DIV(_pshift, _pwidth), \ + _CCU_MUX(_muxshift, _muxwidth), \ + _postdiv, \ + {0}, {0}, \ + 0, \ + CCU_CLK_F_INIT_DONE | _flags) + /** * struct ccu_clk_gate - ccu clock gate * @off: gate offset @@ -59,6 +248,7 @@ struct ccu_reset { * @resets: reset unit */ struct ccu_desc { + const struct ccu_clk_tree *tree; const struct ccu_clk_gate *gates; const struct ccu_reset *resets; }; diff --git a/drivers/clk/sunxi/clk_a64.c b/drivers/clk/sunxi/clk_a64.c index 162ec769d6..1d0cd98183 100644 --- a/drivers/clk/sunxi/clk_a64.c +++ b/drivers/clk/sunxi/clk_a64.c @@ -12,6 +12,45 @@ #include #include +#define CLK_APB2 26 +#define CLK_OSC_32K (CLK_GPU + 1) +#define CLK_OSC_24M (CLK_OSC_32K + 1) + +static const unsigned long periph0_parents[] = { + CLK_OSC_24M, +}; + +static const unsigned long apb2_parents[] = { + CLK_OSC_32K, + CLK_OSC_24M, + CLK_PLL_PERIPH0, + CLK_PLL_PERIPH0, +}; + +static const unsigned long uart_parents[] = { + CLK_APB2, +}; + +static const struct ccu_clk_tree a64_tree[] = { + [CLK_OSC_32K] = FIXED(OSC_32K_ULL), + [CLK_OSC_24M] = FIXED(OSC_24M_ULL), + + [CLK_PLL_PERIPH0] = NK(periph0_parents, 0x028, + 8, 5, /* N */ + 4, 2, 2, /* K */ + 2, /* post-div */ + CCU_CLK_F_POSTDIV), + + [CLK_APB2] = MP(apb2_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0, + 0), + + [CLK_BUS_UART0] = MISC(uart_parents), +}; + static const struct ccu_clk_gate a64_gates[] = { [CLK_BUS_OTG] = GATE(0x060, BIT(23)), [CLK_BUS_EHCI0] = GATE(0x060, BIT(24)), @@ -52,6 +91,7 @@ static const struct ccu_reset a64_resets[] = { }; static const struct ccu_desc a64_ccu_desc = { + .tree = a64_tree, .gates = a64_gates, .resets = a64_resets, }; diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c index 345d706c2a..2aebd257d1 100644 --- a/drivers/clk/sunxi/clk_sunxi.c +++ b/drivers/clk/sunxi/clk_sunxi.c @@ -18,6 +18,187 @@ static const struct ccu_clk_gate *priv_to_gate(struct ccu_priv *priv, return &priv->desc->gates[id]; } +static const struct ccu_clk_tree *priv_to_tree(struct ccu_priv *priv, + unsigned long id) +{ + return &priv->desc->tree[id]; +} + +static int sunxi_get_parent_idx(const struct ccu_clk_tree *tree, void *base) +{ + u32 reg, idx; + + reg = readl(base + tree->off); + idx = reg >> tree->mux.shift; + idx &= (1 << tree->mux.width) - 1; + + return idx; +} + +static ulong sunxi_fixed_get_rate(struct clk *clk, unsigned long id) +{ + struct ccu_priv *priv = dev_get_priv(clk->dev); + const struct ccu_clk_tree *tree = priv_to_tree(priv, id); + + if (!(tree->flags & CCU_CLK_F_INIT_DONE)) { + printf("%s: (CLK#%ld) unhandled\n", __func__, clk->id); + return 0; + } + + return tree->fixed_rate; +} + +static ulong sunxi_nk_get_parent_rate(struct clk *clk, unsigned long id) +{ + struct ccu_priv *priv = dev_get_priv(clk->dev); + const struct ccu_clk_tree *tree = priv_to_tree(priv, id); + ulong rate = 0; + + switch (tree->type) { + case CCU_CLK_TYPE_FIXED: + rate = sunxi_fixed_get_rate(clk, id); + break; + default: + printf("%s: Unknown (TYPE#%d)\n", __func__, tree->type); + break; + } + + return rate; +} + +static ulong sunxi_nk_get_rate(struct clk *clk, unsigned long id) +{ + struct ccu_priv *priv = dev_get_priv(clk->dev); + const struct ccu_clk_tree *tree = priv_to_tree(priv, id); + ulong rate, parent_rate; + unsigned long n, k; + u32 reg; + + parent_rate = sunxi_nk_get_parent_rate(clk, tree->parent[0]); + + reg = readl(priv->base + tree->off); + + n = reg >> tree->n.shift; + n &= (1 << tree->n.width) - 1; + n += tree->n.offset; + if (!n) + n++; + + k = reg >> tree->k.shift; + k &= (1 << tree->k.width) - 1; + k += tree->k.offset; + if (!k) + k++; + + rate = parent_rate * n * k; + if (tree->flags & CCU_CLK_F_POSTDIV) + rate /= tree->postdiv; + + return rate; +} + +static ulong sunxi_mp_get_parent_rate(struct clk *clk, unsigned long id) +{ + struct ccu_priv *priv = dev_get_priv(clk->dev); + const struct ccu_clk_tree *tree = priv_to_tree(priv, id); + ulong rate = 0; + + if (!(tree->flags & CCU_CLK_F_INIT_DONE)) { + printf("%s: (CLK#%ld) unhandled\n", __func__, clk->id); + return 0; + } + + switch (tree->type) { + case CCU_CLK_TYPE_FIXED: + rate = sunxi_fixed_get_rate(clk, id); + break; + case CCU_CLK_TYPE_NK: + rate = sunxi_nk_get_rate(clk, id); + break; + default: + printf("%s: (TYPE#%d) unhandled\n", __func__, tree->type); + break; + } + + return rate; +} + +static ulong sunxi_mp_get_rate(struct clk *clk, unsigned long id) +{ + struct ccu_priv *priv = dev_get_priv(clk->dev); + const struct ccu_clk_tree *tree = priv_to_tree(priv, id); + unsigned int m, p; + ulong parent_rate; + u32 reg, idx; + + idx = sunxi_get_parent_idx(tree, priv->base); + if (idx < 0) { + printf("%s: Wrong parent index %d\n", __func__, idx); + return 0; + } + + parent_rate = sunxi_mp_get_parent_rate(clk, tree->parent[idx]); + + reg = readl(priv->base + tree->off); + + m = reg >> tree->m.shift; + m &= (1 << tree->m.width) - 1; + m += tree->m.offset; + if (!m) + m++; + + p = reg >> tree->p.shift; + p &= (1 << tree->p.width) - 1; + + return (parent_rate >> p) / m; +} + +static ulong sunxi_misc_get_rate(struct clk *clk, unsigned long id) +{ + struct ccu_priv *priv = dev_get_priv(clk->dev); + const struct ccu_clk_tree *tree = priv_to_tree(priv, id); + ulong rate = 0; + + if (!(tree->flags & CCU_CLK_F_INIT_DONE)) { + printf("%s: (CLK#%ld) unhandled\n", __func__, clk->id); + return 0; + } + + switch (tree->type) { + case CCU_CLK_TYPE_MP: + rate = sunxi_mp_get_rate(clk, id); + break; + default: + printf("%s: (TYPE#%d) unhandled\n", __func__, tree->type); + break; + } + + return rate; +} + +static ulong sunxi_clk_get_rate(struct clk *clk) +{ + struct ccu_priv *priv = dev_get_priv(clk->dev); + const struct ccu_clk_tree *tree = priv_to_tree(priv, clk->id); + ulong rate = 0; + + if (!(tree->flags & CCU_CLK_F_INIT_DONE)) { + printf("%s: (CLK#%ld) unhandled\n", __func__, clk->id); + return 0; + } + + switch (tree->type) { + case CCU_CLK_TYPE_MISC: + rate = sunxi_misc_get_rate(clk, tree->parent[0]); + break; + default: + printf("%s: (TYPE#%d) unhandled\n", __func__, tree->type); + break; + } + + return rate; +} + static int sunxi_set_gate(struct clk *clk, bool on) { struct ccu_priv *priv = dev_get_priv(clk->dev); @@ -56,6 +237,7 @@ static int sunxi_clk_disable(struct clk *clk) struct clk_ops sunxi_clk_ops = { .enable = sunxi_clk_enable, .disable = sunxi_clk_disable, + .get_rate = sunxi_clk_get_rate, }; int sunxi_clk_probe(struct udevice *dev)