Received: by 10.223.185.116 with SMTP id b49csp4169630wrg; Mon, 19 Feb 2018 12:27:31 -0800 (PST) X-Google-Smtp-Source: AH8x225XeWQhAUKEWfvezf0kWrbdTrck5zbcuS292zIza6Bs06LRtLO0+h8PpqaPyglfUaOPsbhv X-Received: by 2002:a17:902:aa81:: with SMTP id d1-v6mr949834plr.251.1519072051762; Mon, 19 Feb 2018 12:27:31 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519072051; cv=none; d=google.com; s=arc-20160816; b=yGeE+JPzEvbrVN2gRNLQ/Y646I8Zi/NdCZlTsajwJO02R5ENJ4d+LKVBj0eIdcHXj9 9rCBC1nLXllm7ZQqxLB845XyhHywFF26HCBCekwfNEY0h9AsZ6DDrNyKLTM2XPhce63A lD+FG6CUrwhKHz7bYI56Nr+zscR5EURUUVvkyb58ZMIACFirdwRK53zZidQuFKai9jfH tWL0/t+wpd81R1ZMMZX/lLkPoHRGvF+zEajwTGYhWW6Ug3w9hrr2mhxteF7QfetMf+HT Bn9z+mRl4uUKhFpy4lWT35CRwGB+3uik0V34ATbSspKnlIkQ5IKWtFtkNfqgITbFKSLK aQqw== 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=Y9xyEo40mp0t1Y/6BVXjIHOQ1jSbV2vFyS5busbY6Gg=; b=DNLsJSBJ2MY7uwcFIAJFxYM8w8lF1BX1u1H/GaeeXh1cMVP86Y2MFs5bjL3IsSK+t2 8FE93G6D6Vg7PnsvdVsRK25D1xlc86WQRhZAYF65tKNoUph27k7ae/BGBy1nAM8qhYBw i0hMwThxDVnjNeU5Kksh30bcK0I1xAFNTNfcuaJsEvxx6hK4fmpI2wVTF9rTiMYtjCL2 SQ30OmQlLEINk9hblPXkNWepCN49aJ0F/CSyZ6o/fs1vOMQZbpFTMtN46M62zkL3nzjA bMoKkNlRlo1JYb+WWyfz78x+TRfug/BdeO5SFb2M2lUlnn0vH7XLpIuKWT5wfR9ds9E9 Oblw== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@lechnology.com header.s=default header.b=XhxZSXB7; 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 y19-v6si1322292plr.630.2018.02.19.12.27.17; Mon, 19 Feb 2018 12:27:31 -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=XhxZSXB7; 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 S932364AbeBSUZp (ORCPT + 99 others); Mon, 19 Feb 2018 15:25:45 -0500 Received: from vern.gendns.com ([206.190.152.46]:58564 "EHLO vern.gendns.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932184AbeBSUXH (ORCPT ); Mon, 19 Feb 2018 15:23:07 -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=Y9xyEo40mp0t1Y/6BVXjIHOQ1jSbV2vFyS5busbY6Gg=; b=XhxZSXB7nYusguxRlprVilPCZ /f1xMjWr7cpW5XA8pOSAdsahdXnHw+orNAW/bvFTR4qXxORXzaIvg0edtEvKMFaWvmTZ0NIYdmN+M MUYVEX8aLLAUgpBEVY3I7yiuAJRqxcs9CP2tqYqmnwgB8vHfCE/b+MCudQt/X53vpxcn8IjJQDwFM XjxClQJkADyBmfRAG4A+nD5ECUUGDjIuhuBO0/okv5ehQpEvt6wbEAbBtNbe532UjhzowRCidoP2b VNPsAyZd34XVm8H0/BSIiLu0a5sy+r52dyr33IgzbozODTeXNBdKlc9F9wL5nnK2fbCzvjTND6RkI 5OLYjkYVA==; 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 1enrwY-003xBV-CQ; Mon, 19 Feb 2018 15:21:46 -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 19/42] clk: davinci: cfgchip: Add TI DA8XX USB PHY clocks Date: Mon, 19 Feb 2018 14:21:40 -0600 Message-Id: <1519071723-31790-20-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 USB PHY clocks in the CFGCHIP2 syscon register on TI DA8XX-type SoCs. The USB0 (USB 2.0) PHY clock is an interesting case because it calls clk_enable() in a reentrant way. The USB 2.0 PSC only has to be enabled temporarily while we are locking the PLL, which takes place during the clk_enable() callback. Signed-off-by: David Lechner --- v7 changes: - convert to platform device - rename clk local variable to usb0/usb1 - put code in da8xx-cfgchip.c instead of creating a new file v6 changes: - rename clocks to usb{0,1}_clk48 - rename USB 2.0 PSC clock to "fck" - simplify {s,g}et_parent implementations - use pr_fmt macro drivers/clk/davinci/da8xx-cfgchip.c | 351 ++++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) diff --git a/drivers/clk/davinci/da8xx-cfgchip.c b/drivers/clk/davinci/da8xx-cfgchip.c index 386da99..7d5cf16 100644 --- a/drivers/clk/davinci/da8xx-cfgchip.c +++ b/drivers/clk/davinci/da8xx-cfgchip.c @@ -339,6 +339,349 @@ 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); } +/* --- USB 2.0 PHY clock --- */ + +struct da8xx_usb0_clk48 { + struct clk_hw hw; + struct clk *fck; + struct regmap *regmap; +}; + +#define to_da8xx_usb0_clk48(_hw) \ + container_of((_hw), struct da8xx_usb0_clk48, hw) + +static int da8xx_usb0_clk48_prepare(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + + /* The USB 2.0 PSC clock is only needed temporarily during the USB 2.0 + * PHY clock enable, but since clk_prepare() can't be called in an + * atomic context (i.e. in clk_enable()), we have to prepare it here. + */ + return clk_prepare(usb0->fck); +} + +static void da8xx_usb0_clk48_unprepare(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + + clk_unprepare(usb0->fck); +} + +static int da8xx_usb0_clk48_enable(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int mask, val; + int ret; + + /* Locking the USB 2.O PLL requires that the USB 2.O PSC is enabled + * temporaily. It can be turned back off once the PLL is locked. + */ + clk_enable(usb0->fck); + + /* Turn on the USB 2.0 PHY, but just the PLL, and not OTG. The USB 1.1 + * PHY may use the USB 2.0 PLL clock without USB 2.0 OTG being used. + */ + mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_PHY_PLLON; + val = CFGCHIP2_PHY_PLLON; + + regmap_write_bits(usb0->regmap, CFGCHIP(2), mask, val); + ret = regmap_read_poll_timeout(usb0->regmap, CFGCHIP(2), val, + val & CFGCHIP2_PHYCLKGD, 0, 500000); + + clk_disable(usb0->fck); + + return ret; +} + +static void da8xx_usb0_clk48_disable(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int val; + + val = CFGCHIP2_PHYPWRDN; + regmap_write_bits(usb0->regmap, CFGCHIP(2), val, val); +} + +static int da8xx_usb0_clk48_is_enabled(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int val; + + regmap_read(usb0->regmap, CFGCHIP(2), &val); + + return !!(val & CFGCHIP2_PHYCLKGD); +} + +static unsigned long da8xx_usb0_clk48_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int mask, val; + + /* The parent clock rate must be one of the following */ + mask = CFGCHIP2_REFFREQ_MASK; + switch (parent_rate) { + case 12000000: + val = CFGCHIP2_REFFREQ_12MHZ; + break; + case 13000000: + val = CFGCHIP2_REFFREQ_13MHZ; + break; + case 19200000: + val = CFGCHIP2_REFFREQ_19_2MHZ; + break; + case 20000000: + val = CFGCHIP2_REFFREQ_20MHZ; + break; + case 24000000: + val = CFGCHIP2_REFFREQ_24MHZ; + break; + case 26000000: + val = CFGCHIP2_REFFREQ_26MHZ; + break; + case 38400000: + val = CFGCHIP2_REFFREQ_38_4MHZ; + break; + case 40000000: + val = CFGCHIP2_REFFREQ_40MHZ; + break; + case 48000000: + val = CFGCHIP2_REFFREQ_48MHZ; + break; + default: + return 0; + } + + regmap_write_bits(usb0->regmap, CFGCHIP(2), mask, val); + + /* USB 2.0 PLL always supplies 48MHz */ + return 48000000; +} + +static long da8xx_usb0_clk48_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return 48000000; +} + +static int da8xx_usb0_clk48_set_parent(struct clk_hw *hw, u8 index) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + + return regmap_write_bits(usb0->regmap, CFGCHIP(2), + CFGCHIP2_USB2PHYCLKMUX, + index ? CFGCHIP2_USB2PHYCLKMUX : 0); +} + +static u8 da8xx_usb0_clk48_get_parent(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int val; + + regmap_read(usb0->regmap, CFGCHIP(2), &val); + + return (val & CFGCHIP2_USB2PHYCLKMUX) ? 1 : 0; +} + +static const struct clk_ops da8xx_usb0_clk48_ops = { + .prepare = da8xx_usb0_clk48_prepare, + .unprepare = da8xx_usb0_clk48_unprepare, + .enable = da8xx_usb0_clk48_enable, + .disable = da8xx_usb0_clk48_disable, + .is_enabled = da8xx_usb0_clk48_is_enabled, + .recalc_rate = da8xx_usb0_clk48_recalc_rate, + .round_rate = da8xx_usb0_clk48_round_rate, + .set_parent = da8xx_usb0_clk48_set_parent, + .get_parent = da8xx_usb0_clk48_get_parent, +}; + +static struct da8xx_usb0_clk48 * +da8xx_cfgchip_register_usb0_clk48(struct device *dev, + struct regmap *regmap) +{ + const char * const parent_names[] = { "usb_refclkin", "pll0_auxclk" }; + struct clk *fck_clk; + struct da8xx_usb0_clk48 *usb0; + struct clk_init_data init; + int ret; + + fck_clk = devm_clk_get(dev, "fck"); + if (IS_ERR(fck_clk)) { + if (PTR_ERR(fck_clk) != -EPROBE_DEFER) + dev_err(dev, "Missing fck clock\n"); + return ERR_CAST(fck_clk); + } + + usb0 = devm_kzalloc(dev, sizeof(*usb0), GFP_KERNEL); + if (!usb0) + return ERR_PTR(-ENOMEM); + + init.name = "usb0_clk48"; + init.ops = &da8xx_usb0_clk48_ops; + init.parent_names = parent_names; + init.num_parents = 2; + + usb0->hw.init = &init; + usb0->fck = fck_clk; + usb0->regmap = regmap; + + ret = devm_clk_hw_register(dev, &usb0->hw); + if (ret < 0) + return ERR_PTR(ret); + + return usb0; +} + +/* --- USB 1.1 PHY clock --- */ + +struct da8xx_usb1_clk48 { + struct clk_hw hw; + struct regmap *regmap; +}; + +#define to_da8xx_usb1_clk48(_hw) \ + container_of((_hw), struct da8xx_usb1_clk48, hw) + +static int da8xx_usb1_clk48_set_parent(struct clk_hw *hw, u8 index) +{ + struct da8xx_usb1_clk48 *usb1 = to_da8xx_usb1_clk48(hw); + + return regmap_write_bits(usb1->regmap, CFGCHIP(2), + CFGCHIP2_USB1PHYCLKMUX, + index ? CFGCHIP2_USB1PHYCLKMUX : 0); +} + +static u8 da8xx_usb1_clk48_get_parent(struct clk_hw *hw) +{ + struct da8xx_usb1_clk48 *usb1 = to_da8xx_usb1_clk48(hw); + unsigned int val; + + regmap_read(usb1->regmap, CFGCHIP(2), &val); + + return (val & CFGCHIP2_USB1PHYCLKMUX) ? 1 : 0; +} + +static const struct clk_ops da8xx_usb1_clk48_ops = { + .set_parent = da8xx_usb1_clk48_set_parent, + .get_parent = da8xx_usb1_clk48_get_parent, +}; + +/** + * da8xx_cfgchip_register_usb1_clk48 - Register a new USB 1.1 PHY clock + * @regmap: The CFGCHIP regmap + */ +static struct da8xx_usb1_clk48 * +da8xx_cfgchip_register_usb1_clk48(struct device *dev, + struct regmap *regmap) +{ + const char * const parent_names[] = { "usb0_clk48", "usb_refclkin" }; + struct da8xx_usb1_clk48 *usb1; + struct clk_init_data init; + int ret; + + usb1 = devm_kzalloc(dev, sizeof(*usb1), GFP_KERNEL); + if (!usb1) + return ERR_PTR(-ENOMEM); + + init.name = "usb1_clk48"; + init.ops = &da8xx_usb1_clk48_ops; + init.parent_names = parent_names; + init.num_parents = 2; + + usb1->hw.init = &init; + usb1->regmap = regmap; + + ret = devm_clk_hw_register(dev, &usb1->hw); + if (ret < 0) + return ERR_PTR(ret); + + return usb1; +} + +static int da8xx_cfgchip_register_usb_phy_clk(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_usb0_clk48 *usb0; + struct da8xx_usb1_clk48 *usb1; + struct clk_hw *parent; + + usb0 = da8xx_cfgchip_register_usb0_clk48(dev, regmap); + if (IS_ERR(usb0)) + return PTR_ERR(usb0); + + /* + * All existing boards use pll0_auxclk as the parent and new boards + * should use device tree, so hard-coding the value (1) here. + */ + parent = clk_hw_get_parent_by_index(&usb0->hw, 1); + if (parent) + clk_set_parent(usb0->hw.clk, parent->clk); + else + dev_warn(dev, "Failed to find usb0 parent clock\n"); + + usb1 = da8xx_cfgchip_register_usb1_clk48(dev, regmap); + if (IS_ERR(usb1)) + return PTR_ERR(usb1); + + /* + * All existing boards use usb0_clk48 as the parent and new boards + * should use device tree, so hard-coding the value (0) here. + */ + parent = clk_hw_get_parent_by_index(&usb1->hw, 0); + if (parent) + clk_set_parent(usb1->hw.clk, parent->clk); + else + dev_warn(dev, "Failed to find usb1 parent clock\n"); + + clk_hw_register_clkdev(&usb0->hw, "usb0_clk48", "da8xx-usb-phy"); + clk_hw_register_clkdev(&usb1->hw, "usb1_clk48", "da8xx-usb-phy"); + + return 0; +} + +static int of_da8xx_usb_phy_clk_init(struct device *dev, struct regmap *regmap) +{ + struct clk_hw_onecell_data *clk_data; + struct da8xx_usb0_clk48 *usb0; + struct da8xx_usb1_clk48 *usb1; + + clk_data = devm_kzalloc(dev, sizeof(*clk_data) + 2 * + sizeof(*clk_data->hws), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = 2; + + usb0 = da8xx_cfgchip_register_usb0_clk48(dev, regmap); + if (IS_ERR(usb0)) { + if (PTR_ERR(usb0) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(dev, "Failed to register usb0_clk48 (%ld)\n", + PTR_ERR(usb0)); + + clk_data->hws[0] = ERR_PTR(-ENOENT); + } else { + clk_data->hws[0] = &usb0->hw; + } + + usb1 = da8xx_cfgchip_register_usb1_clk48(dev, regmap); + if (IS_ERR(usb1)) { + if (PTR_ERR(usb0) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(dev, "Failed to register usb1_clk48 (%ld)\n", + PTR_ERR(usb1)); + + clk_data->hws[1] = ERR_PTR(-ENOENT); + } else { + clk_data->hws[1] = &usb1->hw; + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + /* --- platform device --- */ static const struct of_device_id da8xx_cfgchip_of_match[] = { @@ -358,6 +701,10 @@ static const struct of_device_id da8xx_cfgchip_of_match[] = { .compatible = "ti,da850-async3-clksrc", .data = of_da850_async3_init, }, + { + .compatible = "ti,da830-usb-phy-clocks", + .data = of_da8xx_usb_phy_clk_init, + }, { } }; @@ -378,6 +725,10 @@ static const struct platform_device_id da8xx_cfgchip_id_table[] = { .name = "da850-async3-clksrc", .driver_data = (kernel_ulong_t)da850_cfgchip_register_async3, }, + { + .name = "da830-usb-phy-clks", + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_usb_phy_clk, + }, { } }; -- 2.7.4