From patchwork Mon Sep 12 18:23:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: hotran X-Patchwork-Id: 9327461 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 C6C3D6048B for ; Mon, 12 Sep 2016 18:24:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C282F28E45 for ; Mon, 12 Sep 2016 18:24:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B722B28E73; Mon, 12 Sep 2016 18:24:15 +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 65ADD28E45 for ; Mon, 12 Sep 2016 18:24:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932109AbcILSXz (ORCPT ); Mon, 12 Sep 2016 14:23:55 -0400 Received: from mail-it0-f48.google.com ([209.85.214.48]:34021 "EHLO mail-it0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753837AbcILSXh (ORCPT ); Mon, 12 Sep 2016 14:23:37 -0400 Received: by mail-it0-f48.google.com with SMTP id 15so4096773ita.1 for ; Mon, 12 Sep 2016 11:23:36 -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=6+E0ztnHKgcRKKQZtE57mboEJ6qxcWyBP1SlRDx4LII=; b=GT3MKluM+QZM05nPIs/EN649BAxQ/dPwPFQneIyLMt6kjlNvRiFGkX1W0TFHy+YAwS pPWZdrMQxI9XX1ZVouAa8C0AhFE0KJN679aOqdmhJCVB0c0WdRQR+FYAGHngQfdbeRSR N6FMy8Qky5/5KsixuPZe30sdWPiDrFMIQbY60= 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=6+E0ztnHKgcRKKQZtE57mboEJ6qxcWyBP1SlRDx4LII=; b=fd31r1v2hfC+X/tJWUvgwzapTk/OOlazQhTPT1S+7NLZVPXb3kbu+ujeNIGB2RpT1Q q/+/ss9X7b/SVyxaF8pOEaqCMRUAaTDtxq/wWcZeNVStES5QdJVAAxf/+/8PVc8PVjF4 gthAIFir2t2AalHDkNL+uCq5IamjcfqXEuGRtpVK509zYlqHpUh9j31PkmP3C0UMGCzX opukWEyvmlnC4eKWaghNentoPeFh4TRz+s+eFQFKcO6tpZJOFl3aDvOEDVV1lECkN247 GBGRLUae19vAIWTLJt/zGhQYZaR5tpLRAjd2rPncJ+qYfvOPitWjDxLakM7pVAT6tVJB MmHw== X-Gm-Message-State: AE9vXwOolHi4R8gKAel3JhRjMQnERMTW1TF5cIrGgIoT8C2krJLRGLqQMcso0nPiT2MLySyu X-Received: by 10.157.17.133 with SMTP id v5mr23927584otf.101.1473704616032; Mon, 12 Sep 2016 11:23:36 -0700 (PDT) Received: from hotran_localhost.amcc.com ([206.80.4.98]) by smtp.gmail.com with ESMTPSA id h63sm505455ita.12.2016.09.12.11.23.34 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 12 Sep 2016 11:23:35 -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 v3 2/3] clk: xgene: Add PMD clock Date: Mon, 12 Sep 2016 11:23:24 -0700 Message-Id: <1473704605-28126-3-git-send-email-hotran@apm.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1473704605-28126-1-git-send-email-hotran@apm.com> References: <1473704605-28126-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 = values 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 | 221 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 3433132..5daddf5 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -217,6 +217,226 @@ 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) +#define XGENE_CLK_PMD_SHIFT 8 +#define XGENE_CLK_PMD_WIDTH 3 + +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; + 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; + } + of_property_read_string(np, "clock-output-names", &clk_name); + + denom = BIT(XGENE_CLK_PMD_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, XGENE_CLK_PMD_SHIFT, + XGENE_CLK_PMD_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 +763,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",