Received: by 10.213.65.68 with SMTP id h4csp132039imn; Thu, 15 Mar 2018 20:13:54 -0700 (PDT) X-Google-Smtp-Source: AG47ELst3aVG+feEyuIEGwPTlTQYIcagnSGvyOmPjCZ9QBI2gg4DfU+4Vvn1KCl7wHiq7VcsMlKT X-Received: by 10.98.100.147 with SMTP id y141mr206622pfb.131.1521170034011; Thu, 15 Mar 2018 20:13:54 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1521170033; cv=none; d=google.com; s=arc-20160816; b=Wz2kCArv+u5V6mRhDw/3YqTTxUQAxiaYZw3U+WHq3osTSJUvfnlhEJVsOomTL/004x Ns93bnBTl9+136Er8SBKNCfOqB/4epA9LmLcSgSfeziYT/f1ha0vJ3kGS2vcqJYAmEbe MIFr0LnglWsk/8cnSMhECD3ig1r2groGnW7Up6B/m9rPVzOXj1BEw4dRSZ33HfCJ6cIc UaHWYh/5Xuqvj4LWK0YT/ogEKu1SfOTTdtiUCwT2C+el7YOd9ARn9m7HJhowD0Fa0LrH TwJ84G2jNHVCI2otmpUiiY6JlM0ZLKoEnognrIwiW+QdJm6ijRYYwmicjjysyPsd6V2L PVHA== 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=epHfru1YfOJMx1ueSfafzscTrAObxXr+c3Jp1ac1gB0=; b=K1cDaWeUQJVEl7IMHhsY6//gc3O8YfJqqdi6gG4pIO/YsLKXrMalnFN0r6yxEpip4v B7Tjo0APpdNrdAZ/8ptrDcVDFYWJRhhcSNmNWxVOyVXHjsWuLfBA2FUl1sder3Ug3lV8 jTeUEKUsRK+rXzIQxqn/bLFVquxLt3vc9qEcYW9gGXKcngh+lfYzRrqmHvnOOjDLm050 +xCd4o3XuengTulXq5iFFrb3dLEUols0aoe54pGDXI21W+54vJzCAJJDdbYaFaxlCgKz D4egzEHJFyB9lElAHi2dcUfjMxR+9TfcllF49oDwL6JUiHrqq14S9UZKwzIlUiKcZOMp tYUg== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@lechnology.com header.s=default header.b=my58PTO+; 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 m12-v6si5214593pls.74.2018.03.15.20.13.39; Thu, 15 Mar 2018 20:13:53 -0700 (PDT) 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=my58PTO+; 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 S933177AbeCPCx4 (ORCPT + 99 others); Thu, 15 Mar 2018 22:53:56 -0400 Received: from vern.gendns.com ([206.190.152.46]:55465 "EHLO vern.gendns.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933065AbeCPCxw (ORCPT ); Thu, 15 Mar 2018 22:53:52 -0400 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=epHfru1YfOJMx1ueSfafzscTrAObxXr+c3Jp1ac1gB0=; b=my58PTO+29N9/xDqN/YwhAmM6 SeaGCi1k8ixdzT/JjZmi0Ui1zyGUoK5HhFjhqiUS/IRGjsQYj6ZHASyF8qlhlDZEwo32FQL7ySniV khadAGw+8DCC4qx8QtuRKo3LkQvUDc9f7xzUSYp3QQKQFi46MaRwRXFHt/xbVoK64MpRlEmN3K8Me SIF2x/iRd6mPnTUFQ9q1y2FiYEFZKYZUaDSlfTy5V2td3/b0Ro5SgJgT6yKmtStUpRcAwp1eY+RJs oJ6lU4O69Wg9cBhGHHJPt1uUefgepQWYmg+xVnGA5wwWIGRlwQwskWaQHGu/qVkyzRn48/vvBQ0JB B3VSwyT8A==; Received: from 108-198-5-147.lightspeed.okcbok.sbcglobal.net ([108.198.5.147]:44986 helo=freyr.lechnology.com) by vern.gendns.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-SHA256:128) (Exim 4.89_1) (envelope-from ) id 1ewfTE-002UlD-04; Thu, 15 Mar 2018 22:51:52 -0400 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 v8 18/42] clk: davinci: New driver for TI DA8XX CFGCHIP clocks Date: Thu, 15 Mar 2018 21:52:34 -0500 Message-Id: <1521168778-27236-19-git-send-email-david@lechnology.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1521168778-27236-1-git-send-email-david@lechnology.com> References: <1521168778-27236-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 --- v8 changes: - register clkdev lookup for async1 and async3 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 | 439 ++++++++++++++++++++++++ include/linux/platform_data/clk-da8xx-cfgchip.h | 21 ++ 3 files changed, 462 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..f0bdfea --- /dev/null +++ b/drivers/clk/davinci/da8xx-cfgchip.c @@ -0,0 +1,439 @@ +// 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); + + clk_hw_register_clkdev(&mux->hw, "async1", "da850-psc0"); + + 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); + + clk_hw_register_clkdev(&mux->hw, "async3", "da850-psc1"); + + /* 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