Received: by 10.223.185.116 with SMTP id b49csp4169791wrg; Mon, 19 Feb 2018 12:27:45 -0800 (PST) X-Google-Smtp-Source: AH8x227c+VuwEi4XJMJrikIbxyXDWHlNCCJ/nsO+JPgCvJQPzoDWKGIZg2MY3LfFpUcUAyjSUaXO X-Received: by 10.98.107.130 with SMTP id g124mr15552078pfc.225.1519072064969; Mon, 19 Feb 2018 12:27:44 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519072064; cv=none; d=google.com; s=arc-20160816; b=MmjBn62tup4kaIO1Ti8wm96GaU7BgceHrgdXh1Dk2EctiqGkUSrgK6W54gTbgMS/E2 gACAwb1NN5BU6XW8zQJJ5RAR9ZaybBX6tMZbEGwE94M5UrbWVIo1/CYbqyhnYqMtdZuy 0Z4ihEgny6NVG5BYHOqJ5AmU/2UBbYFx15wY2WHiv4KQcF4u53OVeRpaR3U40x8rKI2m DZrW9cFySIkFwcDd26CiH+ekmEAq2ujsIN7VU15b7f2/y7Isv8g1JaacyVBThSNwNtAY dBTRNZfgZk05EbRovMul8cTMLih9HBrY8owF3V1FH96n0bFMGEeopB/b6qLvN0FRUkxv qY2g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=yY48HJKR5IgcM0zu7RKclVs4C786efRZxO1oBYQ7Clo=; b=IRHN21tav769BCXdlayl3Mdyahgg2nl78aT4VD8cg4bVIdiTX/HjRijMkvkCdYyMJB dwzR90uHphLSR8zPZQDVkFJuQVfhOG7NWs4s7brxFYfk/UeMaN6ylfNokJX8utqFqTqN EOjfGpnlgTpi3jw9e0VuumNkl7/7MRfThC4Q7bJq7o+UQItqQtUIW3PSTasiB8Fk3DU9 316roLlEfgGbPXuoph5Qrzn9AAz86ZSbgOFO93S7+fLp7ol9Nfflsiz31k6VM+LNYmbC dGrParGw/Ccm23Mn7r1xNQJv0wHkNlpeCaeOyw9HktMPgOFePsv+HlvaJXwtKKMCTdoO utpA== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@lechnology.com header.s=default header.b=juQXxixX; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id y6-v6si9502285plt.467.2018.02.19.12.27.30; Mon, 19 Feb 2018 12:27:44 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=fail header.i=@lechnology.com header.s=default header.b=juQXxixX; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932287AbeBSU0O (ORCPT + 99 others); Mon, 19 Feb 2018 15:26:14 -0500 Received: from vern.gendns.com ([206.190.152.46]:58555 "EHLO vern.gendns.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932176AbeBSUXF (ORCPT ); Mon, 19 Feb 2018 15:23:05 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lechnology.com; s=default; h=References:In-Reply-To:Message-Id:Date:Subject :Cc:To:From:Sender:Reply-To:MIME-Version:Content-Type: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=yY48HJKR5IgcM0zu7RKclVs4C786efRZxO1oBYQ7Clo=; b=juQXxixXOuCPlBwyr4gfFp1x7 9U3HJi/FXS0v3Q/x7w+pyQlGrXZtWZYly62SJXCiMoWinW+S405m57/kl6fwMP3yM4CeXUmiCV/Be N+MqSzyIehAIXF7GI7oo86MHuABpBNHA3OeFKqyEF1uFMKaTfJygc7b6ZOhmSL3JmHjBEAT+ymrMC ogKGjXWv/pgumw/T0/MSr7L4jixbqsmS78zhHyTEo6m+hvismRykW7/wtabYswo0jPQiIGCbH5fHM Rph5t8bhdmTIbE41HUYvSC+YEoxHpUMUdTehbLZ0/zV0lVIHcYMotKcBs6i+OGdKnt5md1MZCn+cU 71mz3tcMA==; Received: from 108-198-5-147.lightspeed.okcbok.sbcglobal.net ([108.198.5.147]:48842 helo=freyr.lechnology.com) by vern.gendns.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-SHA256:128) (Exim 4.89_1) (envelope-from ) id 1enrwW-003xBV-Mc; Mon, 19 Feb 2018 15:21:45 -0500 From: David Lechner To: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: Michael Turquette , Stephen Boyd , Rob Herring , Mark Rutland , Sekhar Nori , Kevin Hilman , Bartosz Golaszewski , Adam Ford , linux-kernel@vger.kernel.org, David Lechner Subject: [PATCH v7 18/42] clk: davinci: New driver for TI DA8XX CFGCHIP clocks Date: Mon, 19 Feb 2018 14:21:39 -0600 Message-Id: <1519071723-31790-19-git-send-email-david@lechnology.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1519071723-31790-1-git-send-email-david@lechnology.com> References: <1519071723-31790-1-git-send-email-david@lechnology.com> X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - vern.gendns.com X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - lechnology.com X-Get-Message-Sender-Via: vern.gendns.com: authenticated_id: davidmain+lechnology.com/only user confirmed/virtual account not confirmed X-Authenticated-Sender: vern.gendns.com: davidmain@lechnology.com X-Source: X-Source-Args: X-Source-Dir: Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This adds a new driver for the gate and multiplexer clocks in the CFGCHIPn syscon registers on TI DA8XX-type SoCs. Signed-off-by: David Lechner --- v7 changes: - convert to platform device v6 changes: - added DIV4.5 and ASYNC1 clocks - refactored code to be more generic - added functions for registering clocks from board files (no longer device tree only) - use pr_fmt macro drivers/clk/davinci/Makefile | 2 + drivers/clk/davinci/da8xx-cfgchip.c | 435 ++++++++++++++++++++++++ include/linux/platform_data/clk-da8xx-cfgchip.h | 21 ++ 3 files changed, 458 insertions(+) create mode 100644 drivers/clk/davinci/da8xx-cfgchip.c create mode 100644 include/linux/platform_data/clk-da8xx-cfgchip.h diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile index 6c388d4..11178b7 100644 --- a/drivers/clk/davinci/Makefile +++ b/drivers/clk/davinci/Makefile @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 ifeq ($(CONFIG_COMMON_CLK), y) +obj-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx-cfgchip.o + obj-y += pll.o obj-$(CONFIG_ARCH_DAVINCI_DA830) += pll-da830.o obj-$(CONFIG_ARCH_DAVINCI_DA850) += pll-da850.o diff --git a/drivers/clk/davinci/da8xx-cfgchip.c b/drivers/clk/davinci/da8xx-cfgchip.c new file mode 100644 index 0000000..386da99 --- /dev/null +++ b/drivers/clk/davinci/da8xx-cfgchip.c @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock driver for DA8xx/AM17xx/AM18xx/OMAP-L13x CFGCHIP + * + * Copyright (C) 2018 David Lechner + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --- Gate clocks --- */ + +#define DA8XX_GATE_CLOCK_IS_DIV4P5 BIT(1) + +struct da8xx_cfgchip_gate_clk_info { + const char *name; + u32 cfgchip; + u32 bit; + u32 flags; +}; + +struct da8xx_cfgchip_gate_clk { + struct clk_hw hw; + struct regmap *regmap; + u32 reg; + u32 mask; +}; + +#define to_da8xx_cfgchip_gate_clk(_hw) \ + container_of((_hw), struct da8xx_cfgchip_gate_clk, hw) + +static int da8xx_cfgchip_gate_clk_enable(struct clk_hw *hw) +{ + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); + + return regmap_write_bits(clk->regmap, clk->reg, clk->mask, clk->mask); +} + +static void da8xx_cfgchip_gate_clk_disable(struct clk_hw *hw) +{ + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); + + regmap_write_bits(clk->regmap, clk->reg, clk->mask, 0); +} + +static int da8xx_cfgchip_gate_clk_is_enabled(struct clk_hw *hw) +{ + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); + unsigned int val; + + regmap_read(clk->regmap, clk->reg, &val); + + return !!(val & clk->mask); +} + +static unsigned long da8xx_cfgchip_div4p5_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* this clock divides by 4.5 */ + return parent_rate * 2 / 9; +} + +static const struct clk_ops da8xx_cfgchip_gate_clk_ops = { + .enable = da8xx_cfgchip_gate_clk_enable, + .disable = da8xx_cfgchip_gate_clk_disable, + .is_enabled = da8xx_cfgchip_gate_clk_is_enabled, +}; + +static const struct clk_ops da8xx_cfgchip_div4p5_clk_ops = { + .enable = da8xx_cfgchip_gate_clk_enable, + .disable = da8xx_cfgchip_gate_clk_disable, + .is_enabled = da8xx_cfgchip_gate_clk_is_enabled, + .recalc_rate = da8xx_cfgchip_div4p5_recalc_rate, +}; + +static struct da8xx_cfgchip_gate_clk * __init +da8xx_cfgchip_gate_clk_register(struct device *dev, + const struct da8xx_cfgchip_gate_clk_info *info, + struct regmap *regmap) +{ + struct clk *parent; + const char *parent_name; + struct da8xx_cfgchip_gate_clk *gate; + struct clk_init_data init; + int ret; + + parent = devm_clk_get(dev, NULL); + if (IS_ERR(parent)) + return ERR_CAST(parent); + + parent_name = __clk_get_name(parent); + + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + init.name = info->name; + if (info->flags & DA8XX_GATE_CLOCK_IS_DIV4P5) + init.ops = &da8xx_cfgchip_div4p5_clk_ops; + else + init.ops = &da8xx_cfgchip_gate_clk_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + + gate->hw.init = &init; + gate->regmap = regmap; + gate->reg = info->cfgchip; + gate->mask = info->bit; + + ret = devm_clk_hw_register(dev, &gate->hw); + if (ret < 0) + return ERR_PTR(ret); + + return gate; +} + +static const struct da8xx_cfgchip_gate_clk_info da8xx_tbclksync_info __initconst = { + .name = "ehrpwm_tbclk", + .cfgchip = CFGCHIP(1), + .bit = CFGCHIP1_TBCLKSYNC, +}; + +static int __init da8xx_cfgchip_register_tbclk(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_cfgchip_gate_clk *gate; + + gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_tbclksync_info, + regmap); + if (IS_ERR(gate)) + return PTR_ERR(gate); + + clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.0"); + clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.1"); + + return 0; +} + +static const struct da8xx_cfgchip_gate_clk_info da8xx_div4p5ena_info __initconst = { + .name = "div4.5", + .cfgchip = CFGCHIP(3), + .bit = CFGCHIP3_DIV45PENA, + .flags = DA8XX_GATE_CLOCK_IS_DIV4P5, +}; + +static int __init da8xx_cfgchip_register_div4p5(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_cfgchip_gate_clk *gate; + + gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_div4p5ena_info, regmap); + if (IS_ERR(gate)) + return PTR_ERR(gate); + + return 0; +} + +static int __init +of_da8xx_cfgchip_gate_clk_init(struct device *dev, + const struct da8xx_cfgchip_gate_clk_info *info, + struct regmap *regmap) +{ + struct da8xx_cfgchip_gate_clk *gate; + + gate = da8xx_cfgchip_gate_clk_register(dev, info, regmap); + if (IS_ERR(gate)) + return PTR_ERR(gate); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, gate); +} + +static int __init of_da8xx_tbclksync_init(struct device *dev, + struct regmap *regmap) +{ + return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_tbclksync_info, regmap); +} + +static int __init of_da8xx_div4p5ena_init(struct device *dev, + struct regmap *regmap) +{ + return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_div4p5ena_info, regmap); +} + +/* --- MUX clocks --- */ + +struct da8xx_cfgchip_mux_clk_info { + const char *name; + const char *parent0; + const char *parent1; + u32 cfgchip; + u32 bit; +}; + +struct da8xx_cfgchip_mux_clk { + struct clk_hw hw; + struct regmap *regmap; + u32 reg; + u32 mask; +}; + +#define to_da8xx_cfgchip_mux_clk(_hw) \ + container_of((_hw), struct da8xx_cfgchip_mux_clk, hw) + +static int da8xx_cfgchip_mux_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw); + unsigned int val = index ? clk->mask : 0; + + return regmap_write_bits(clk->regmap, clk->reg, clk->mask, val); +} + +static u8 da8xx_cfgchip_mux_clk_get_parent(struct clk_hw *hw) +{ + struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw); + unsigned int val; + + regmap_read(clk->regmap, clk->reg, &val); + + return (val & clk->mask) ? 1 : 0; +} + +static const struct clk_ops da8xx_cfgchip_mux_clk_ops = { + .set_parent = da8xx_cfgchip_mux_clk_set_parent, + .get_parent = da8xx_cfgchip_mux_clk_get_parent, +}; + +static struct da8xx_cfgchip_mux_clk * __init +da8xx_cfgchip_mux_clk_register(struct device *dev, + const struct da8xx_cfgchip_mux_clk_info *info, + struct regmap *regmap) +{ + const char * const parent_names[] = { info->parent0, info->parent1 }; + struct da8xx_cfgchip_mux_clk *mux; + struct clk_init_data init; + int ret; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + init.name = info->name; + init.ops = &da8xx_cfgchip_mux_clk_ops; + init.parent_names = parent_names; + init.num_parents = 2; + init.flags = 0; + + mux->hw.init = &init; + mux->regmap = regmap; + mux->reg = info->cfgchip; + mux->mask = info->bit; + + ret = devm_clk_hw_register(dev, &mux->hw); + if (ret < 0) + return ERR_PTR(ret); + + return mux; +} + +static const struct da8xx_cfgchip_mux_clk_info da850_async1_info __initconst = { + .name = "async1", + .parent0 = "pll0_sysclk3", + .parent1 = "div4.5", + .cfgchip = CFGCHIP(3), + .bit = CFGCHIP3_EMA_CLKSRC, +}; + +static int __init da8xx_cfgchip_register_async1(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_cfgchip_mux_clk *mux; + + mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async1_info, regmap); + if (IS_ERR(mux)) + return PTR_ERR(mux); + + return 0; +} + +static const struct da8xx_cfgchip_mux_clk_info da850_async3_info __initconst = { + .name = "async3", + .parent0 = "pll0_sysclk2", + .parent1 = "pll1_sysclk2", + .cfgchip = CFGCHIP(3), + .bit = CFGCHIP3_ASYNC3_CLKSRC, +}; + +static int __init da850_cfgchip_register_async3(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_cfgchip_mux_clk *mux; + struct clk_hw *parent; + + mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async3_info, regmap); + if (IS_ERR(mux)) + return PTR_ERR(mux); + + /* pll1_sysclk2 is not affected by CPU scaling, so use it for async3 */ + parent = clk_hw_get_parent_by_index(&mux->hw, 1); + if (parent) + clk_set_parent(mux->hw.clk, parent->clk); + else + dev_warn(dev, "Failed to find async3 parent clock\n"); + + return 0; +} + +static int __init +of_da8xx_cfgchip_init_mux_clock(struct device *dev, + const struct da8xx_cfgchip_mux_clk_info *info, + struct regmap *regmap) +{ + struct da8xx_cfgchip_mux_clk *mux; + + mux = da8xx_cfgchip_mux_clk_register(dev, info, regmap); + if (IS_ERR(mux)) + return PTR_ERR(mux); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &mux->hw); +} + +static int __init of_da850_async1_init(struct device *dev, struct regmap *regmap) +{ + return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async1_info, regmap); +} + +static int __init of_da850_async3_init(struct device *dev, struct regmap *regmap) +{ + return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async3_info, regmap); +} + +/* --- platform device --- */ + +static const struct of_device_id da8xx_cfgchip_of_match[] = { + { + .compatible = "ti,da830-tbclksync", + .data = of_da8xx_tbclksync_init, + }, + { + .compatible = "ti,da830-div4p5ena", + .data = of_da8xx_div4p5ena_init, + }, + { + .compatible = "ti,da850-async1-clksrc", + .data = of_da850_async1_init, + }, + { + .compatible = "ti,da850-async3-clksrc", + .data = of_da850_async3_init, + }, + { } +}; + +static const struct platform_device_id da8xx_cfgchip_id_table[] = { + { + .name = "da830-tbclksync", + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_tbclk, + }, + { + .name = "da830-div4p5ena", + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_div4p5, + }, + { + .name = "da850-async1-clksrc", + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_async1, + }, + { + .name = "da850-async3-clksrc", + .driver_data = (kernel_ulong_t)da850_cfgchip_register_async3, + }, + { } +}; + +typedef int (*da8xx_cfgchip_init)(struct device *dev, void __iomem *base); + +static int da8xx_cfgchip_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct da8xx_cfgchip_clk_platform_data *pdata = dev->platform_data; + const struct of_device_id *of_id; + da8xx_cfgchip_init clk_init = NULL; + struct regmap *regmap = NULL; + + of_id = of_match_device(da8xx_cfgchip_of_match, dev); + if (of_id) { + struct device_node *parent; + + clk_init = of_id->data; + parent = of_get_parent(dev->of_node); + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + } else if (pdev->id_entry && pdata) { + clk_init = (void *)pdev->id_entry->driver_data; + regmap = pdata->cfgchip; + } + + if (!clk_init) { + dev_err(dev, "unable to find driver data\n"); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(regmap)) { + dev_err(dev, "no regmap for CFGCHIP syscon\n"); + return regmap ? PTR_ERR(regmap) : -ENOENT; + } + + return clk_init(dev, regmap); +} + +static struct platform_driver da8xx_cfgchip_driver = { + .probe = da8xx_cfgchip_probe, + .driver = { + .name = "da8xx-cfgchip-clk", + .of_match_table = da8xx_cfgchip_of_match, + }, + .id_table = da8xx_cfgchip_id_table, +}; + +static int __init da8xx_cfgchip_driver_init(void) +{ + return platform_driver_register(&da8xx_cfgchip_driver); +} + +/* has to be postcore_initcall because PSC devices depend on the async3 clock */ +postcore_initcall(da8xx_cfgchip_driver_init); diff --git a/include/linux/platform_data/clk-da8xx-cfgchip.h b/include/linux/platform_data/clk-da8xx-cfgchip.h new file mode 100644 index 0000000..de0f77d --- /dev/null +++ b/include/linux/platform_data/clk-da8xx-cfgchip.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * clk-da8xx-cfgchip - TI DaVinci DA8xx CFGCHIP clock driver + * + * Copyright (C) 2018 David Lechner + */ + +#ifndef __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__ +#define __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__ + +#include + +/** + * da8xx_cfgchip_clk_platform_data + * @cfgchip: CFGCHIP syscon regmap + */ +struct da8xx_cfgchip_clk_platform_data { + struct regmap *cfgchip; +}; + +#endif /* __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__ */ -- 2.7.4