From patchwork Tue Aug 23 15:52:40 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: hotran X-Patchwork-Id: 9295901 X-Patchwork-Delegate: sboyd@codeaurora.org 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 EE29A60574 for ; Tue, 23 Aug 2016 15:54:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E089B2621E for ; Tue, 23 Aug 2016 15:54:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D531128CC6; Tue, 23 Aug 2016 15:54:35 +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.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,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 D21FC2621E for ; Tue, 23 Aug 2016 15:54:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753964AbcHWPyR (ORCPT ); Tue, 23 Aug 2016 11:54:17 -0400 Received: from mail-pf0-f180.google.com ([209.85.192.180]:34317 "EHLO mail-pf0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753528AbcHWPxn (ORCPT ); Tue, 23 Aug 2016 11:53:43 -0400 Received: by mail-pf0-f180.google.com with SMTP id p64so44990858pfb.1 for ; Tue, 23 Aug 2016 08:53:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=apm.com; s=apm; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=7zE3NWlr3MS4yyKdO0XHz9dW9kHsoBEVwD+1rxlpeVI=; b=VvXBdZuWjTVdrqRilINzvwJ/KL4wfAwQjRoqt4vDKN0kOHW3F/vYk4V/hKOAdTl9dr SI6o/iWDlhNh+TqZvzrY8GrSf2G28GY1mdl5Mli4DqO6SHvQJClNWsRNUUSL9OE4Vz5k yzUMO9bMcxCHC5w1KvHKuHemENwOUdZt6V8N8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=7zE3NWlr3MS4yyKdO0XHz9dW9kHsoBEVwD+1rxlpeVI=; b=Nrp9B6EXHf8VPzggEp1pS4FXeNt/1lXcYiFRmjXFjm2TcqKgHeDHROlH/5oS8K2U6T pjl1t55JPLN1mjGf+w4xFU8Awe/W7ZS0Im9iEvspKx9ox5942MrEZJ6Ww43KsnE0Qqo8 1q7kR4dsqf63kIazSXyYjbxzfGhcQdJpw/aZaRChaE3ipjHb8eIOd/8ncQz1hjKqfs8V zJg08fE1U1JsjI51nmsKgWe9v3fqdsJG+JUXLtwnDNTAgZnW89dmrTHcrSDZXxlmwbSh SkCEaTZCTd1nWfvy9Y7bLXVDfDM2y7/hkyUPLZ+Fe1GxuO/pV7FHyaO+CUa0zVNf3qDy h99A== X-Gm-Message-State: AEkoouvaKGH61jPYZcC1Pg0pOOzLBxaWIjNQHqGfw+4J3IbnxNoKW0/ungpoFolvJneJsEP8 X-Received: by 10.98.70.199 with SMTP id o68mr54513086pfi.17.1471967617823; Tue, 23 Aug 2016 08:53:37 -0700 (PDT) Received: from hotran_localhost.amcc.com ([206.80.4.98]) by smtp.gmail.com with ESMTPSA id s89sm6765178pfi.83.2016.08.23.08.53.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 23 Aug 2016 08:53:37 -0700 (PDT) From: Hoan Tran To: Michael Turquette , Rob Herring , Stephen Boyd , Mark Rutland Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, lho@apm.com, Duc Dang , Hoan Tran Subject: [PATCH 2/3] clk: xgene: Add PMD clock Date: Tue, 23 Aug 2016 08:52:40 -0700 Message-Id: <1471967561-23634-3-git-send-email-hotran@apm.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1471967561-23634-1-git-send-email-hotran@apm.com> References: <1471967561-23634-1-git-send-email-hotran@apm.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 Add X-Gene PMD clock support. PMD clock is implemented for a single register field. Output rate = parent_rate * (denominator - scale) / denominator with - denominator = bitmask of register field + 1 - scale = value of register field For example, for bitmask is 0x7, denominator will be 8 and scale will be computed and programmed accordingly. Signed-off-by: Hoan Tran --- drivers/clk/clk-xgene.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 3433132..66ce200 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -217,6 +217,228 @@ static void xgene_pcppllclk_init(struct device_node *np) xgene_pllclk_init(np, PLL_TYPE_PCP); } +/** + * struct xgene_clk_pmd - PMD clock + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing the fractional scale multiplier (scaler) + * @shift: shift to the unit bit field + * @denom: 1/denominator unit + * @lock: register lock + * Flags: + * XGENE_CLK_PMD_SCALE_INVERTED - By default the scaler is the value read + * from the register plus one. For example, + * 0 for (0 + 1) / denom, + * 1 for (1 + 1) / denom and etc. + * If this flag is set, it is + * 0 for (denom - 0) / denom, + * 1 for (denom - 1) / denom and etc. + * + */ +struct xgene_clk_pmd { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u32 mask; + u64 denom; + u32 flags; + spinlock_t *lock; +}; + +#define to_xgene_clk_pmd(_hw) container_of(_hw, struct xgene_clk_pmd, hw) + +#define XGENE_CLK_PMD_SCALE_INVERTED BIT(0) + +static unsigned long xgene_clk_pmd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + unsigned long flags = 0; + u64 ret, scale; + u32 val; + + if (fd->lock) + spin_lock_irqsave(fd->lock, flags); + else + __acquire(fd->lock); + + val = clk_readl(fd->reg); + + if (fd->lock) + spin_unlock_irqrestore(fd->lock, flags); + else + __release(fd->lock); + + ret = (u64)parent_rate; + + scale = (val & fd->mask) >> fd->shift; + if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED) + scale = fd->denom - scale; + else + scale++; + + /* freq = parent_rate * scaler / denom */ + do_div(ret, fd->denom); + ret *= scale; + if (ret == 0) + ret = (u64)parent_rate; + + return ret; +} + +static long xgene_clk_pmd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + u64 ret, scale; + + if (!rate || rate >= *parent_rate) + return *parent_rate; + + /* freq = parent_rate * scaler / denom */ + ret = rate * fd->denom; + scale = DIV_ROUND_UP_ULL(ret, *parent_rate); + + ret = (u64)*parent_rate * scale; + do_div(ret, fd->denom); + + return ret; +} + +static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw); + unsigned long flags = 0; + u64 scale, ret; + u32 val; + + /* + * Compute the scaler: + * + * freq = parent_rate * scaler / denom, or + * scaler = freq * denom / parent_rate + */ + ret = rate * fd->denom; + scale = DIV_ROUND_UP_ULL(ret, (u64)parent_rate); + + /* Check if inverted */ + if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED) + scale = fd->denom - scale; + else + scale--; + + if (fd->lock) + spin_lock_irqsave(fd->lock, flags); + else + __acquire(fd->lock); + + val = clk_readl(fd->reg); + val &= ~fd->mask; + val |= (scale << fd->shift); + clk_writel(val, fd->reg); + + if (fd->lock) + spin_unlock_irqrestore(fd->lock, flags); + else + __release(fd->lock); + + return 0; +} + +static const struct clk_ops xgene_clk_pmd_ops = { + .recalc_rate = xgene_clk_pmd_recalc_rate, + .round_rate = xgene_clk_pmd_round_rate, + .set_rate = xgene_clk_pmd_set_rate, +}; + +static struct clk * +xgene_register_clk_pmd(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u8 shift, + u8 width, u64 denom, u32 clk_flags, spinlock_t *lock) +{ + struct xgene_clk_pmd *fd; + struct clk_init_data init; + struct clk *clk; + + fd = kzalloc(sizeof(*fd), GFP_KERNEL); + if (!fd) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &xgene_clk_pmd_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + fd->reg = reg; + fd->shift = shift; + fd->mask = (BIT(width) - 1) << shift; + fd->denom = denom; + fd->flags = clk_flags; + fd->lock = lock; + fd->hw.init = &init; + + clk = clk_register(dev, &fd->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clk %s\n", __func__, name); + kfree(fd); + return NULL; + } + + return clk; +} + +static void xgene_pmdclk_init(struct device_node *np) +{ + const char *clk_name = np->full_name; + void __iomem *csr_reg; + struct resource res; + struct clk *clk; + u32 shift, width; + u64 denom; + u32 flags = 0; + int rc; + + /* Check if the entry is disabled */ + if (!of_device_is_available(np)) + return; + + /* Parse the DTS register for resource */ + rc = of_address_to_resource(np, 0, &res); + if (rc != 0) { + pr_err("no DTS register for %s\n", np->full_name); + return; + } + csr_reg = of_iomap(np, 0); + if (!csr_reg) { + pr_err("Unable to map resource for %s\n", np->full_name); + return; + } + if (of_property_read_u32(np, "clock-shift", &shift)) + shift = 0; + if (of_property_read_u32(np, "clock-width", &width)) + width = 32; + of_property_read_string(np, "clock-output-names", &clk_name); + + denom = BIT(width); + flags |= XGENE_CLK_PMD_SCALE_INVERTED; + + clk = xgene_register_clk_pmd(NULL, clk_name, + of_clk_get_parent_name(np, 0), 0, + csr_reg, shift, width, denom, + flags, &clk_lock); + if (!IS_ERR(clk)) { + of_clk_add_provider(np, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + pr_debug("Add %s clock\n", clk_name); + } else { + if (csr_reg) + iounmap(csr_reg); + } +} + /* IP Clock */ struct xgene_dev_parameters { void __iomem *csr_reg; /* CSR for IP clock */ @@ -543,6 +765,7 @@ err: CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init); CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init); +CLK_OF_DECLARE(xgene_pmd_clock, "apm,xgene-pmd-clock", xgene_pmdclk_init); CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock", xgene_socpllclk_init); CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock",