From patchwork Thu Aug 17 11:20:24 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_F=C3=A4rber?= X-Patchwork-Id: 9905689 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 943BA6038C for ; Thu, 17 Aug 2017 11:20:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 72A1928A5D for ; Thu, 17 Aug 2017 11:20:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 671D028A62; Thu, 17 Aug 2017 11:20:50 +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.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham 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 5AA2C28A5E for ; Thu, 17 Aug 2017 11:20:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751167AbdHQLUs (ORCPT ); Thu, 17 Aug 2017 07:20:48 -0400 Received: from mx2.suse.de ([195.135.220.15]:49965 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751149AbdHQLUq (ORCPT ); Thu, 17 Aug 2017 07:20:46 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id A425CAB1E; Thu, 17 Aug 2017 11:20:44 +0000 (UTC) From: =?UTF-8?q?Andreas=20F=C3=A4rber?= To: Michael Turquette , Stephen Boyd , linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org, Roc He , =?UTF-8?q?=E8=92=8B=E4=B8=BD=E7=90=B4?= , =?UTF-8?q?Andreas=20F=C3=A4rber?= Subject: [RFC 3/4] clk: Add Realtek RTD1295 Date: Thu, 17 Aug 2017 13:20:24 +0200 Message-Id: <20170817112026.24062-4-afaerber@suse.de> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20170817112026.24062-1-afaerber@suse.de> References: <20170817112026.24062-1-afaerber@suse.de> MIME-Version: 1.0 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 Add two clock controller drivers for RTD1295. Clock names are taken from vendor DT and clk_summary where possible. Clock rate calculations are guesses derived from Android register values and resulting rates in clk_summary. Clock gate parents are mostly unknown - osc27M is chosen for UART baudrate. Signed-off-by: Andreas Färber --- drivers/clk/Kconfig | 7 + drivers/clk/Makefile | 1 + drivers/clk/clk-rtd1295.c | 385 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 393 insertions(+) create mode 100644 drivers/clk/clk-rtd1295.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a874b72612d0..8e4534912f51 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -209,6 +209,13 @@ config COMMON_CLK_OXNAS ---help--- Support for the OXNAS SoC Family clocks. +config CLK_RTD129X + bool "Clock driver for the Realtek RTD129x SoC family" + default ARCH_REALTEK && ARM64 + depends on (ARCH_REALTEK && ARM64) || COMPILE_TEST + ---help--- + Support for the Realtek RTD1295 SoC. + config COMMON_CLK_VC5 tristate "Clock driver for IDT VersaClock 5,6 devices" depends on I2C diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cd376b3fb47a..466965452d4f 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o +obj-$(CONFIG_CLK_RTD129X) += clk-rtd1295.o obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o diff --git a/drivers/clk/clk-rtd1295.c b/drivers/clk/clk-rtd1295.c new file mode 100644 index 000000000000..a20e71460dac --- /dev/null +++ b/drivers/clk/clk-rtd1295.c @@ -0,0 +1,385 @@ +/* + * Realtek RTD1295 + * + * Copyright (c) 2017 Andreas Färber + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +struct rtd_pll_clk { + struct clk_hw hw; + void __iomem *base; + bool gpu; +}; + +#define to_pll_clk(_hw) container_of(_hw, struct rtd_pll_clk, hw) + +static unsigned long rtd_scpu_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct rtd_pll_clk *pll = to_pll_clk(hw); + u32 reg1, reg2, reg3, reg4; + unsigned f1, f2, f3, f4, f5; + unsigned long rate, frac; + + reg1 = readl(pll->base + 0x4); + reg2 = readl(pll->base - (0x500 - 0x30)); + reg3 = readl(pll->base + 0x0); + reg4 = readl(pll->base + 0x1c); + f1 = (reg1 >> 11) & 0xff; + f2 = (reg1 >> 0) & 0x7ff; + f3 = (reg2 >> 7) & 0x3; + f4 = (reg3 >> 0) & 0x1; + f5 = (reg4 >> 20) & 0x1; + + rate = parent_rate * (f1 + 3) / f3; + frac = parent_rate / 2048 * f2 / BIT(f4); + rate += frac; + + pr_info("%s 0x%08x n=%u f=%u 0x%08x x=%u 0x%08x y=%u 0x%08x z=%u rate=%lu\n", + __clk_get_name(hw->clk), + reg1, f1, f2, + reg2, f3, + reg3, f4, + reg4, f5, rate); + return rate; +} + +static const struct clk_ops rtd_scpu_ops = { + .recalc_rate = rtd_scpu_recalc_rate, +}; + +static struct clk *rtd_scpu(void __iomem *base, const char *name, struct clk *parent) +{ + struct rtd_pll_clk *pll; + struct clk_init_data init; + struct clk *clk; + const char *parents[1]; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + if (parent) + parents[0] = __clk_get_name(parent); + init.name = name; + init.ops = &rtd_scpu_ops; + init.parent_names = parent ? parents : NULL; + init.num_parents = parent ? 1 : 0; + init.flags = CLK_IGNORE_UNUSED; + + pll->hw.init = &init; + pll->base = base; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: error registering clk", name); + kfree(pll); + } + return clk; +} + +static unsigned long rtd_nf_ssc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct rtd_pll_clk *pll = to_pll_clk(hw); + u32 reg1, reg2, reg3; + unsigned f1, f2, f3, f4; + unsigned long rate, frac; + + reg1 = readl(pll->base + 0x4); + reg2 = readl(pll->base + 0x0); + reg3 = readl(pll->base + 0x1c); + f1 = (reg1 >> 11) & 0xff; + f2 = (reg1 >> 0) & 0x7ff; + f3 = (reg2 >> 0) & 0xf; + f4 = (reg3 >> 20) & 0x1; + + rate = parent_rate * (f1 + 3); + if (pll->gpu) { + rate /= 2; + } + frac = parent_rate * 4 * f2 / BIT(f3); + if (pll->gpu) { + frac /= 2; + } + rate += frac; + + pr_info("%s 0x%08x n=%u f=%u 0x%08x d=%u 0x%08x x=%u rate=%lu\n", __clk_get_name(hw->clk), + reg1, f1, f2, + reg2, f3, + reg3, f4, rate); + return rate; +} + +static const struct clk_ops rtd_nf_ssc_ops = { + .recalc_rate = rtd_nf_ssc_recalc_rate, +}; + +static struct clk *rtd_nf_ssc(void __iomem *base, const char *name, struct clk *parent) +{ + struct rtd_pll_clk *pll; + struct clk_init_data init; + struct clk *clk; + const char *parents[1]; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + if (parent) + parents[0] = __clk_get_name(parent); + init.name = name; + init.ops = &rtd_nf_ssc_ops; + init.parent_names = parent ? parents : NULL; + init.num_parents = parent ? 1 : 0; + init.flags = CLK_IGNORE_UNUSED; + + pll->hw.init = &init; + pll->base = base; + pll->gpu = (strcmp(name, "pll_gpu") == 0); + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: error registering clk", name); + kfree(pll); + } + return clk; +} + +static unsigned long rtd_mno_ctrl_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct rtd_pll_clk *pll = to_pll_clk(hw); + u32 reg1, reg2; + unsigned f1, f2, f3; + unsigned long rate; + + reg1 = readl(pll->base + 0x0); + reg2 = readl(pll->base + 0x4); + f1 = (reg1 >> 4) & 0xff; + f2 = (reg1 >> 12) & 0x3; + f3 = (reg1 >> 17) & 0x3; + + rate = parent_rate * (f1 + 2) / (f2 +1) / (f3 + 1); + + pr_info("%s 0x%08x m=%u n=%u o=%u 0x%08x rate=%lu\n", __clk_get_name(hw->clk), + reg1, f1, f2, f3, + reg2, rate); + return rate; +} + +static const struct clk_ops rtd_mno_ctrl_ops = { + .recalc_rate = rtd_mno_ctrl_recalc_rate, +}; + +static struct clk *rtd_mno_ctrl(void __iomem *base, const char *name, struct clk *parent) +{ + struct rtd_pll_clk *pll; + struct clk_init_data init; + struct clk *clk; + const char *parents[1]; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + if (parent) + parents[0] = __clk_get_name(parent); + init.name = name; + init.ops = &rtd_mno_ctrl_ops; + init.parent_names = parent ? parents : NULL; + init.num_parents = parent ? 1 : 0; + init.flags = CLK_IGNORE_UNUSED; + + pll->hw.init = &init; + pll->base = base; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: error registering clk", name); + kfree(pll); + } + return clk; +} + +static const char * const rtd1295_gates1[32] = { + [ 0] = "clk_en_misc", + [ 1] = "clk_en_pcie0", + [ 2] = "clk_en_sata_0", + [ 3] = "clk_en_gspi", + [ 4] = "clk_en_usb", + [ 5] = "clk_en_pcr", + [ 6] = "clk_en_iso_misc", + [ 7] = "clk_en_sata_alive_0", + [ 8] = "clk_en_hdmi", + [ 9] = "clk_en_etn", + [10] = "clk_en_aio", + /* "*clk_en_gpu", */ + /* "*clk_en_ve1", */ + /* "*clk_en_ve2", */ + [14] = "clk_en_tve", + /* "*clk_en_vo", */ + [16] = "clk_en_lvds", + [17] = "clk_en_se", + [18] = "clk_en_dcu", + [19] = "clk_en_cp", + [20] = "clk_en_md", + [21] = "clk_en_tp", + [22] = "clk_en_rsa", + [23] = "clk_en_nf", + [24] = "clk_en_emmc", + [25] = "clk_en_cr", + [26] = "clk_en_sdio_ip", + [27] = "clk_en_mipi", + [28] = "clk_en_emmc_ip", + /* "*clk_en_ve3", */ + [30] = "clk_en_sdio", + [31] = "clk_en_sd_ip", +}; + +static const char * const rtd1295_gates2[32] = { + [ 0] = "clk_en_nat", + [ 1] = "clk_en_misc_i2c_5", + /* "*clk_en_scpu", */ + [ 3] = "clk_en_jpeg", + /* "*clk_en_apu", */ + [ 5] = "clk_en_pcie1", + [ 6] = "clk_en_misc_sc", + [ 7] = "clk_en_cbus_tx", + /* "*rvd", */ + /* "*rvd", */ + [10] = "clk_en_misc_rtc", + /* "*rvd", */ + /* "*rvd", */ + [13] = "clk_en_misc_i2c_4", + [14] = "clk_en_misc_i2c_3", + [15] = "clk_en_misc_i2c_2", + [16] = "clk_en_misc_i2c_1", + [17] = "clk_en_aio_au_codec", + [18] = "clk_en_aio_mod", + [19] = "clk_en_aio_da", + [20] = "clk_en_aio_hdmi", + [21] = "clk_en_aio_spdif", + [22] = "clk_en_aio_i2s", + [23] = "clk_en_aio_mclk", + [24] = "clk_en_hdmirx", + [25] = "clk_en_sata_1", + [26] = "clk_en_sata_alive_1", + [27] = "clk_en_ur2", + [28] = "clk_en_ur1", + [29] = "clk_en_fan", + [30] = "clk_en_dcphy_0", + [31] = "clk_en_dcphy_1", +}; + +static struct clk *clks[16 + 2 * 32] = {}; + +static struct clk_onecell_data rtd_clks = { + .clks = clks, + .clk_num = ARRAY_SIZE(clks), +}; + +static void __init rtd1295_clk_init(struct device_node *node) +{ + void __iomem *base; + struct clk *osc; + int i; + static const char *clk_sys_parents[2] = { "pll_bus", "pll_bus_div2" }; + static const char *clk_ve_parents[4] = { "clk_sysh", "pll_ve1", "pll_ve2", "pll_ve2" }; + + base = of_iomap(node, 0); + + osc = of_clk_get(node, 0); + + clks[RTD1295_CLK_PLL_SCPU] = rtd_scpu(base + 0x500, "pll_scpu", osc); + clks[RTD1295_CLK_PLL_BUS] = rtd_nf_ssc(base + 0x520, "pll_bus", osc); + clks[RTD1295_CLK_PLL_BUS_DIV2] = clk_register_fixed_factor(NULL, "pll_bus_div2", "pll_bus", 0, 1, 2); + clks[RTD1295_CLK_SYS] = clk_register_mux(NULL, "clk_sys", clk_sys_parents, 2, 0, base + 0x30, 0, 1, CLK_MUX_READ_ONLY, NULL); + clks[RTD1295_CLK_PLL_BUS_H] = rtd_nf_ssc(base + 0x540, "pll_bus_h", osc); + clks[RTD1295_CLK_SYSH] = clk_register_fixed_factor(NULL, "clk_sysh", "pll_bus_h", 0, 1, 1); + clks[RTD1295_CLK_PLL_DDSA] = rtd_nf_ssc(base + 0x560, "pll_ddsa", osc); + clks[RTD1295_CLK_PLL_DDSB] = rtd_nf_ssc(base + 0x580, "pll_ddsb", osc); + clks[RTD1295_CLK_PLL_VODMA] = rtd_mno_ctrl(base + 0x260, "pll_vodma", osc); + clk_register_fixed_factor(NULL, "clk_vodma", "pll_vodma", 0, 1, 1); + clks[RTD1295_CLK_EN_VO] = clk_register_gate(NULL, "clk_en_vo", "clk_vodma", CLK_IGNORE_UNUSED, base + 0xc, 15, 0, NULL); + clks[RTD1295_CLK_PLL_VE1] = rtd_mno_ctrl(base + 0x114, "pll_ve1", osc); + clks[RTD1295_CLK_PLL_VE2] = rtd_mno_ctrl(base + 0x1d0, "pll_ve2", osc); + clk_register_mux(NULL, "clk_ve1", clk_ve_parents, 4, 0, base + 0x4c, 0, 2, CLK_MUX_READ_ONLY, NULL); + clks[RTD1295_CLK_EN_VE1] = clk_register_gate(NULL, "clk_en_ve1", "clk_ve1", CLK_IGNORE_UNUSED, base + 0xc, 12, 0, NULL); + clk_register_mux(NULL, "clk_ve2", clk_ve_parents, 4, 0, base + 0x4c, 2, 2, CLK_MUX_READ_ONLY, NULL); + clks[RTD1295_CLK_EN_VE2] = clk_register_gate(NULL, "clk_en_ve2", "clk_ve2", CLK_IGNORE_UNUSED, base + 0xc, 13, 0, NULL); + clk_register_mux(NULL, "clk_ve3", clk_ve_parents, 4, 0, base + 0x4c, 4, 2, CLK_MUX_READ_ONLY, NULL); + clks[RTD1295_CLK_EN_VE3] = clk_register_gate(NULL, "clk_en_ve3", "clk_ve3", CLK_IGNORE_UNUSED, base + 0xc, 29, 0, NULL); + clks[RTD1295_CLK_PLL_GPU] = rtd_nf_ssc(base + 0x5a0, "pll_gpu", osc); + clk_register_fixed_factor(NULL, "clk_gpu", "pll_gpu", 0, 1, 1); + clks[RTD1295_CLK_EN_GPU] = clk_register_gate(NULL, "clk_en_gpu", "clk_gpu", CLK_IGNORE_UNUSED, base + 0xc, 11, 0, NULL); + clks[RTD1295_CLK_PLL_ACPU] = rtd_nf_ssc(base + 0x5c0, "pll_acpu", osc); + + for (i = 0; i < ARRAY_SIZE(rtd1295_gates1); i++) { + if (!rtd1295_gates1[i]) + continue; + clks[RTD1295_CLK_EN_BASE + i] = clk_register_gate(NULL, rtd1295_gates1[i], NULL, CLK_IGNORE_UNUSED, base + 0xc, i, 0, NULL); + } + + for (i = 0; i < ARRAY_SIZE(rtd1295_gates2); i++) { + if (!rtd1295_gates2[i]) + continue; + clks[RTD1295_CLK_EN_BASE2 + i] = clk_register_gate(NULL, rtd1295_gates2[i], __clk_get_name(osc), CLK_IGNORE_UNUSED, base + 0x10, i, 0, NULL); + } + + clk_put(osc); + + of_clk_add_provider(node, of_clk_src_onecell_get, &rtd_clks); +} +CLK_OF_DECLARE(rtd1295, "realtek,rtd1295-clk", rtd1295_clk_init); + +static const char * const rtd1295_iso_gates[13] = { + /* "*unused", */ + /* "*rvd", */ + [ 2] = "clk_en_misc_cec0", + [ 3] = "clk_en_cbusrx_sys", + [ 4] = "clk_en_cbustx_sys", + [ 5] = "clk_en_cbus_sys", + [ 6] = "clk_en_cbus_osc", + [ 7] = "clk_en_misc_ir", + [ 8] = "clk_en_misc_ur0", + [ 9] = "clk_en_i2c0", + [10] = "clk_en_i2c1", + [11] = "clk_en_etn_250m", + [12] = "clk_en_etn_sys", +}; + +static struct clk *iso_clks[13] = {}; + +static struct clk_onecell_data rtd_iso_clks = { + .clks = iso_clks, + .clk_num = ARRAY_SIZE(iso_clks), +}; + +static void __init rtd1295_iso_clk_init(struct device_node *node) +{ + void __iomem *base; + struct clk *osc; + int i; + + base = of_iomap(node, 0); + + osc = of_clk_get(node, 0); + + for (i = 0; i < ARRAY_SIZE(rtd1295_iso_gates); i++) { + if (!rtd1295_iso_gates[i]) + continue; + iso_clks[i] = clk_register_gate(NULL, rtd1295_iso_gates[i], __clk_get_name(osc), CLK_IGNORE_UNUSED, base + 0x8c, i, 0, NULL); + } + + clk_put(osc); + + of_clk_add_provider(node, of_clk_src_onecell_get, &rtd_iso_clks); +} +CLK_OF_DECLARE(rtd1295_iso, "realtek,rtd1295-iso-clk", rtd1295_iso_clk_init);