From patchwork Fri Feb 10 10:04:43 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Neil Armstrong X-Patchwork-Id: 9566389 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 430AF60216 for ; Fri, 10 Feb 2017 10:07:21 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2E1A5284F1 for ; Fri, 10 Feb 2017 10:07:21 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 22E0828535; Fri, 10 Feb 2017 10:07:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 37BC3284F1 for ; Fri, 10 Feb 2017 10:07:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751879AbdBJKGT (ORCPT ); Fri, 10 Feb 2017 05:06:19 -0500 Received: from mail-wm0-f41.google.com ([74.125.82.41]:36615 "EHLO mail-wm0-f41.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751793AbdBJKF4 (ORCPT ); Fri, 10 Feb 2017 05:05:56 -0500 Received: by mail-wm0-f41.google.com with SMTP id c85so255437892wmi.1 for ; Fri, 10 Feb 2017 02:05:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=BuMHdI4+Mxl/xLc5wnIpmpEGpCqjp9zykmeEb6jtVZU=; b=i3dMw+LuoKhsGQApFtfBlaVa2MdKsmZkFCZawu3WvOEjdWQgrGxqHbSofE9eW65vMI qvteOUbBJ475gA9Ye1dtZTgZGqfBo7+F63nPm0gIjut/ojgoOigh7lJlCma/cs8Mwzyd 793KSoftGoUp6n4tgRCZTrfgfQywVWzAMAffKijbn6OoA7s0teLmUBvVIXGzPx+oy1NP CDLZa5hv1Rne2Rb08Xo4JMjx1YV/V5KVOoqzRvV7iF3fqZzw8AFXBok4ewmQAYHNVab2 TtNKWAqZTQ46FQnhhOFYnjb58NtSmF82qX3gI2zZIqELP3IjjmRhOIMEkOGvzCpky5tP QV8A== 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; bh=BuMHdI4+Mxl/xLc5wnIpmpEGpCqjp9zykmeEb6jtVZU=; b=nI6fGQFBqYVuESlGGuI3Ze3oYnb0/0WUxgMcVZpOpFX+3QdS/tSbp95tnfo/Gffiqb QZWEE+Cl1p35Wt4xd9TMiHaR83AG0tC5ph5HlPrRzSg/EFdh0IndANudoOV1pdwLWZ8N HZDWpfn3Wb37FVuSjkZvtIYdIrqD+8C3TB3q+3wevhQdw8BWGQeuoIufLfSijxkMEUSg nGrA1LvOHkms8Y3Vx6lJVzGyBGM+IF3BSiA8ylz8wZQuv0X0UpjmzWBNiwG8hlLRRjNh O01RwnUmQa/zWORXa9UxkIjGtRSiaUzmoYohLrvssPH71KJCqQEB2fRB+K1/jJdjcdcv M8og== X-Gm-Message-State: AMke39n17TAljgalibYb6nOJvFvhIco2Kzsf82j5ozUX5/I5XhFwRywLk3p3E4aoOrq1673y X-Received: by 10.28.4.10 with SMTP id 10mr28164900wme.142.1486721106504; Fri, 10 Feb 2017 02:05:06 -0800 (PST) Received: from localhost.localdomain ([90.63.244.31]) by smtp.gmail.com with ESMTPSA id e72sm771344wma.16.2017.02.10.02.05.05 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 10 Feb 2017 02:05:06 -0800 (PST) From: Neil Armstrong To: sboyd@codeaurora.org, khilman@baylibre.com, carlo@caione.org Cc: Neil Armstrong , linux-amlogic@lists.infradead.org, linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/2] clk: meson-gxbb: Add MALI clocks and fix GP0 pll support Date: Fri, 10 Feb 2017 11:04:43 +0100 Message-Id: <1486721084-1383-2-git-send-email-narmstrong@baylibre.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1486721084-1383-1-git-send-email-narmstrong@baylibre.com> References: <1486721084-1383-1-git-send-email-narmstrong@baylibre.com> Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The Mali is clocked by two identical clock paths behind a glitch free mux to safely change frequency while running. The optimal clock frequency is provided by the GP0 PLL, but unlike Meson8 the GXBB GP0 PLL needs some tweaking and need some SoC specific values to be set in order to setup the PLL. Since these values will change for GXL and GXM, add an init table that will be changed for GXL or GXM. For the MALI clocks, introduce them as Composite clocks to simplify the management. To handle composite clocks, a gxbb_composite structure is added to dynamically create the composite clock at probe and fill accordingly the DT table. Finally the glitch free mux is added and other muxes and dividers are also grouped in initialization tables. Signed-off-by: Neil Armstrong --- drivers/clk/meson/clk-pll.c | 34 ++++-- drivers/clk/meson/clkc.h | 3 + drivers/clk/meson/gxbb.c | 211 +++++++++++++++++++++++++++++++++- drivers/clk/meson/gxbb.h | 7 +- include/dt-bindings/clock/gxbb-clkc.h | 4 + 5 files changed, 245 insertions(+), 14 deletions(-) diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 4adc1e8..b5ff81b 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -132,6 +132,24 @@ static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll, return -ETIMEDOUT; } +static void meson_clk_pll_init(struct meson_clk_pll *pll) +{ + if (pll->init_count && pll->init_regs && pll->init_data) { + unsigned int i; + + for (i = 0; i < pll->init_count; ++i) + writel(pll->init_data[i], pll->init_regs[i]); + } else { + struct parm *p; + u32 reg; + + /* PLL reset */ + p = &pll->n; + reg = readl(pll->base + p->reg_off); + writel(reg | MESON_PLL_RESET, pll->base + p->reg_off); + } +} + static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -151,19 +169,18 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, if (!rate_set) return -EINVAL; - /* PLL reset */ - p = &pll->n; - reg = readl(pll->base + p->reg_off); - writel(reg | MESON_PLL_RESET, pll->base + p->reg_off); - - reg = PARM_SET(p->width, p->shift, reg, rate_set->n); - writel(reg, pll->base + p->reg_off); + meson_clk_pll_init(pll); p = &pll->m; reg = readl(pll->base + p->reg_off); reg = PARM_SET(p->width, p->shift, reg, rate_set->m); writel(reg, pll->base + p->reg_off); + p = &pll->n; + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->n); + writel(reg, pll->base + p->reg_off); + p = &pll->od; reg = readl(pll->base + p->reg_off); reg = PARM_SET(p->width, p->shift, reg, rate_set->od); @@ -184,6 +201,9 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, } p = &pll->n; + reg = readl(pll->base + p->reg_off); + writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off); + ret = meson_clk_pll_wait_lock(pll, p); if (ret) { pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index 9bb70e7..22a6335 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -70,6 +70,9 @@ struct meson_clk_pll { struct parm frac; struct parm od; struct parm od2; + void __iomem **init_regs; + u32 *init_data; + unsigned int init_count; const struct pll_rate_table *rate_table; unsigned int rate_count; spinlock_t *lock; diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 9d9af44..d1dac0f 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -352,6 +352,20 @@ }, }; +static void __iomem *gxbb_gp0_init_regs[] = { + (void *)HHI_GP0_PLL_CNTL, + (void *)HHI_GP0_PLL_CNTL2, + (void *)HHI_GP0_PLL_CNTL3, + (void *)HHI_GP0_PLL_CNTL4, +}; + +static u32 gxbb_gp0_init_data[] = { + 0x6a000228, + 0x69c80000, + 0x0a5590c4, + 0x0000500d, +}; + static struct meson_clk_pll gxbb_gp0_pll = { .m = { .reg_off = HHI_GP0_PLL_CNTL, @@ -368,6 +382,9 @@ .shift = 16, .width = 2, }, + .init_regs = gxbb_gp0_init_regs, + .init_data = gxbb_gp0_init_data, + .init_count = 4, .rate_table = gp0_pll_rate_table, .rate_count = ARRAY_SIZE(gp0_pll_rate_table), .lock = &clk_lock, @@ -550,6 +567,75 @@ }, }; +/* Mali Clock components */ +static u32 mux_table_mali_0_1[] = {0, 1, 2, 3, 4, 5, 6, 7}; +const char *gxbb_mali_0_1_parent_names[] = { + "xtal", "gp0_pll", "mpll2", "mpll1", "fclk_div7", + "fclk_div4", "fclk_div3", "fclk_div5" +}; + +static struct clk_mux gxbb_mali_0_sel = { + .reg = (void *)HHI_MALI_CLK_CNTL, + .mask = 0x3, + .shift = 9, + .table = mux_table_mali_0_1, + .lock = &clk_lock, +}; + +static struct clk_divider gxbb_mali_0_div = { + .reg = (void *)HHI_MALI_CLK_CNTL, + .shift = 0, + .width = 7, + .lock = &clk_lock, +}; + +static struct clk_gate gxbb_mali_0_en = { + .reg = (void *)HHI_MALI_CLK_CNTL, + .bit_idx = 8, + .lock = &clk_lock, +}; + +static struct clk_mux gxbb_mali_1_sel = { + .reg = (void *)HHI_MALI_CLK_CNTL, + .mask = 0x3, + .shift = 25, + .table = mux_table_mali_0_1, + .lock = &clk_lock, +}; + +static struct clk_divider gxbb_mali_1_div = { + .reg = (void *)HHI_MALI_CLK_CNTL, + .shift = 16, + .width = 7, + .lock = &clk_lock, +}; + +static struct clk_gate gxbb_mali_1_en = { + .reg = (void *)HHI_MALI_CLK_CNTL, + .bit_idx = 24, + .lock = &clk_lock, +}; + +static u32 mux_table_mali[] = {0, 1}; +const char *gxbb_mali_parent_names[] = { + "mali_0", "mali_1" +}; + +static struct clk_mux gxbb_mali = { + .reg = (void *)HHI_MALI_CLK_CNTL, + .mask = 1, + .shift = 31, + .table = mux_table_mali, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "mali", + .ops = &clk_mux_ops, + .parent_names = gxbb_mali_parent_names, + .num_parents = 2, + .flags = (CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED), + }, +}; + /* the mother of dragons^W gates */ static struct clk_gate gxbb_clk81 = { .reg = (void *)HHI_MPEG_CLK_CNTL, @@ -754,6 +840,9 @@ [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw, [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw, [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw, + [CLKID_MALI] = &gxbb_mali.hw, + /* This sentinel entry makes sure the table is large enough */ + [NR_CLKS] = NULL, /* Sentinel */ }, .num = NR_CLKS, }; @@ -856,6 +945,66 @@ &gxbb_emmc_a, &gxbb_emmc_b, &gxbb_emmc_c, + &gxbb_mali_0_en, + &gxbb_mali_1_en, +}; + +static struct clk_mux *gxbb_clk_muxes[] = { + &gxbb_mpeg_clk_sel, + &gxbb_mali_0_sel, + &gxbb_mali_1_sel, + &gxbb_mali, +}; + +static struct clk_divider *gxbb_clk_dividers[] = { + &gxbb_mpeg_clk_div, + &gxbb_mali_0_div, + &gxbb_mali_1_div, +}; + +struct gxbb_composite_clk { + unsigned int id; + const char *name; + const char * const *parent_names; + int num_parents; + struct clk_hw *mux_hw; + const struct clk_ops *mux_ops; + struct clk_hw *rate_hw; + const struct clk_ops *rate_ops; + struct clk_hw *gate_hw; + const struct clk_ops *gate_ops; + unsigned long flags; +}; + +/* Convenient table to register the composite clocks */ + +static struct gxbb_composite_clk gxbb_composite_clks[] = { + { + .id = CLKID_MALI_0, + .name = "mali_0", + .parent_names = gxbb_mali_0_1_parent_names, + .num_parents = ARRAY_SIZE(gxbb_mali_0_1_parent_names), + .mux_hw = &gxbb_mali_0_sel.hw, + .mux_ops = &clk_mux_ops, + .rate_hw = &gxbb_mali_0_div.hw, + .rate_ops = &clk_divider_ops, + .gate_hw = &gxbb_mali_0_en.hw, + .gate_ops = &clk_gate_ops, + .flags = (CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED), + }, + { + .id = CLKID_MALI_1, + .name = "mali_1", + .parent_names = gxbb_mali_0_1_parent_names, + .num_parents = ARRAY_SIZE(gxbb_mali_0_1_parent_names), + .mux_hw = &gxbb_mali_1_sel.hw, + .mux_ops = &clk_mux_ops, + .rate_hw = &gxbb_mali_1_div.hw, + .rate_ops = &clk_divider_ops, + .gate_hw = &gxbb_mali_1_en.hw, + .gate_ops = &clk_gate_ops, + .flags = (CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED), + }, }; static int gxbb_clkc_probe(struct platform_device *pdev) @@ -884,24 +1033,61 @@ static int gxbb_clkc_probe(struct platform_device *pdev) /* Populate the base address for CPU clk */ gxbb_cpu_clk.base = clk_base; - /* Populate the base address for the MPEG clks */ - gxbb_mpeg_clk_sel.reg = clk_base + (u64)gxbb_mpeg_clk_sel.reg; - gxbb_mpeg_clk_div.reg = clk_base + (u64)gxbb_mpeg_clk_div.reg; + /* Populate base address for muxes */ + for (i = 0; i < ARRAY_SIZE(gxbb_clk_muxes); i++) + gxbb_clk_muxes[i]->reg = clk_base + + (u64)gxbb_clk_muxes[i]->reg; + + /* Populate base address for dividers */ + for (i = 0; i < ARRAY_SIZE(gxbb_clk_dividers); i++) + gxbb_clk_dividers[i]->reg = clk_base + + (u64)gxbb_clk_dividers[i]->reg; /* Populate base address for gates */ for (i = 0; i < ARRAY_SIZE(gxbb_clk_gates); i++) gxbb_clk_gates[i]->reg = clk_base + (u64)gxbb_clk_gates[i]->reg; + /* Populate base for GP0 init table */ + for (i = 0; i < ARRAY_SIZE(gxbb_gp0_init_regs); ++i) + gxbb_gp0_init_regs[i] = clk_base + + (u64)gxbb_gp0_init_regs[i]; + /* * register all clks */ for (clkid = 0; clkid < NR_CLKS; clkid++) { + if (!gxbb_hw_onecell_data.hws[clkid]) + continue; + ret = devm_clk_hw_register(dev, gxbb_hw_onecell_data.hws[clkid]); if (ret) goto iounmap; } + /* Register Composite Clocks */ + for (i = 0 ; i < ARRAY_SIZE(gxbb_composite_clks); ++i) { + struct gxbb_composite_clk *comp = &gxbb_composite_clks[i]; + struct clk_hw *hw; + + hw = clk_hw_register_composite(dev, comp->name, + comp->parent_names, + comp->num_parents, + comp->mux_hw, comp->mux_ops, + comp->rate_hw, comp->rate_ops, + comp->gate_hw, comp->gate_ops, + comp->flags); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + + pr_err("%s: Failed to register composite clock %s\n", + __func__, comp->name); + + goto unregister_composites; + } + gxbb_hw_onecell_data.hws[comp->id] = hw; + } + /* * Register CPU clk notifier * @@ -922,11 +1108,26 @@ static int gxbb_clkc_probe(struct platform_device *pdev) if (ret) { pr_err("%s: failed to register clock notifier for cpu_clk\n", __func__); - goto iounmap; + goto unregister_composites; } - return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, &gxbb_hw_onecell_data); + if (!ret) + return ret; + +unregister_composites: + for (i = 0 ; i < ARRAY_SIZE(gxbb_composite_clks); ++i) { + struct gxbb_composite_clk *comp = &gxbb_composite_clks[i]; + struct clk *clk; + + if (gxbb_hw_onecell_data.hws[comp->id]) { + clk = gxbb_hw_onecell_data.hws[comp->id]->clk; + clk_unregister_composite(clk); + } + + gxbb_hw_onecell_data.hws[comp->id] = NULL; + } iounmap: iounmap(clk_base); diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h index 0252939..f79ab57 100644 --- a/drivers/clk/meson/gxbb.h +++ b/drivers/clk/meson/gxbb.h @@ -177,7 +177,7 @@ /* CLKID_FCLK_DIV4 */ #define CLKID_FCLK_DIV5 7 #define CLKID_FCLK_DIV7 8 -#define CLKID_GP0_PLL 9 +/* CLKID_GP0_PLL */ #define CLKID_MPEG_SEL 10 #define CLKID_MPEG_DIV 11 /* CLKID_CLK81 */ @@ -265,8 +265,11 @@ /* CLKID_SD_EMMC_A */ /* CLKID_SD_EMMC_B */ /* CLKID_SD_EMMC_C */ +/* CLKID_MALI_0 */ +/* CLKID_MALI_1 */ +/* CLKID_MALI */ -#define NR_CLKS 97 +#define NR_CLKS 100 /* include the CLKIDs that have been made part of the stable DT binding */ #include diff --git a/include/dt-bindings/clock/gxbb-clkc.h b/include/dt-bindings/clock/gxbb-clkc.h index baade6f..7bbc1d0 100644 --- a/include/dt-bindings/clock/gxbb-clkc.h +++ b/include/dt-bindings/clock/gxbb-clkc.h @@ -10,6 +10,7 @@ #define CLKID_FCLK_DIV2 4 #define CLKID_FCLK_DIV3 5 #define CLKID_FCLK_DIV4 6 +#define CLKID_GP0_PLL 9 #define CLKID_CLK81 12 #define CLKID_MPLL2 15 #define CLKID_SPI 34 @@ -24,5 +25,8 @@ #define CLKID_SD_EMMC_A 94 #define CLKID_SD_EMMC_B 95 #define CLKID_SD_EMMC_C 96 +#define CLKID_MALI_0 97 +#define CLKID_MALI_1 98 +#define CLKID_MALI 99 #endif /* __GXBB_CLKC_H */