From patchwork Thu Nov 28 06:36:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shubhrajyoti Datta X-Patchwork-Id: 11265423 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4E14E17F0 for ; Thu, 28 Nov 2019 06:37:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 25D55217AB for ; Thu, 28 Nov 2019 06:37:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727209AbfK1Gg6 (ORCPT ); Thu, 28 Nov 2019 01:36:58 -0500 Received: from mail-eopbgr800081.outbound.protection.outlook.com ([40.107.80.81]:59969 "EHLO NAM03-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726565AbfK1Gg4 (ORCPT ); Thu, 28 Nov 2019 01:36:56 -0500 Received: from MWHPR02CA0012.namprd02.prod.outlook.com (2603:10b6:300:4b::22) by BN6PR02MB3363.namprd02.prod.outlook.com (2603:10b6:405:6b::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2474.17; Thu, 28 Nov 2019 06:36:53 +0000 Received: from BL2NAM02FT056.eop-nam02.prod.protection.outlook.com (2a01:111:f400:7e46::202) by MWHPR02CA0012.outlook.office365.com (2603:10b6:300:4b::22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2495.18 via Frontend Transport; Thu, 28 Nov 2019 06:36:53 +0000 Authentication-Results: spf=softfail (sender IP is 149.199.60.83) smtp.mailfrom=gmail.com; vger.kernel.org; dkim=none (message not signed) header.d=none;vger.kernel.org; dmarc=fail action=none header.from=gmail.com; Received-SPF: SoftFail (protection.outlook.com: domain of transitioning gmail.com discourages use of 149.199.60.83 as permitted sender) Received: from xsj-pvapsmtpgw01 (149.199.60.83) by BL2NAM02FT056.mail.protection.outlook.com (10.152.77.221) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.20.2474.17 via Frontend Transport; Thu, 28 Nov 2019 06:36:52 +0000 Received: from unknown-38-66.xilinx.com ([149.199.38.66] helo=xsj-pvapsmtp01) by xsj-pvapsmtpgw01 with esmtp (Exim 4.63) (envelope-from ) id 1iaDQ4-0007Y5-25; Wed, 27 Nov 2019 22:36:52 -0800 Received: from [127.0.0.1] (helo=xsj-smtp-dlp2.xlnx.xilinx.com) by xsj-pvapsmtp01 with esmtp (Exim 4.63) (envelope-from ) id 1iaDPy-0005D8-W5; Wed, 27 Nov 2019 22:36:46 -0800 Received: from xsj-pvapsmtp01 (xsj-pvapsmtp01.xilinx.com [149.199.38.66]) by xsj-smtp-dlp2.xlnx.xilinx.com (8.13.8/8.13.1) with ESMTP id xAS6ajKT007292; Wed, 27 Nov 2019 22:36:46 -0800 Received: from [10.140.6.59] (helo=xhdshubhraj40.xilinx.com) by xsj-pvapsmtp01 with esmtp (Exim 4.63) (envelope-from ) id 1iaDPx-00059i-EX; Wed, 27 Nov 2019 22:36:45 -0800 From: shubhrajyoti.datta@gmail.com To: linux-clk@vger.kernel.org, linux-kernel@vger.kernel.org, devel@driverdev.osuosl.org Cc: gregkh@linuxfoundation.org, mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com, shubhrajyoti.datta@gmail.com, devicetree@vger.kernel.org, soren.brinkmann@xilinx.com, Shubhrajyoti Datta Subject: [PATCH v3 05/10] clk: clock-wizard: Add support for fractional support Date: Thu, 28 Nov 2019 12:06:12 +0530 Message-Id: X-Mailer: git-send-email 2.1.1 In-Reply-To: References: X-RCIS-Action: ALLOW X-TM-AS-Product-Ver: IMSS-7.1.0.1224-8.2.0.1013-23620.005 X-TM-AS-Result: No--3.194-7.0-31-1 X-imss-scan-details: No--3.194-7.0-31-1;No--3.194-5.0-31-1 X-TM-AS-User-Approved-Sender: No;No X-TM-AS-Result-Xfilter: Match text exemption rules:No X-EOPAttributedMessage: 0 X-Matching-Connectors: 132193966130167885;(f9e945fa-a09a-4caa-7158-08d2eb1d8c44);() X-Forefront-Antispam-Report: CIP:149.199.60.83;IPV:NLI;CTRY:US;EFV:NLI;SFV:NSPM;SFS:(10009020)(376002)(396003)(346002)(39860400002)(136003)(199004)(189003)(2906002)(50226002)(8676002)(82202003)(8936002)(36756003)(16586007)(14444005)(81156014)(9786002)(76482006)(305945005)(316002)(118296001)(81166006)(5660300002)(47776003)(6666004)(356004)(26005)(9686003)(446003)(498600001)(336012)(73392003)(11346002)(48376002)(107886003)(51416003)(4326008)(70586007)(7696005)(426003)(50466002)(70206006)(86362001)(76176011)(450100002);DIR:OUT;SFP:1101;SCL:1;SRVR:BN6PR02MB3363;H:xsj-pvapsmtpgw01;FPR:;SPF:SoftFail;LANG:en;PTR:unknown-60-83.xilinx.com;A:1;MX:1; MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: c4b4ff29-03c6-4ae4-1014-08d773cd5b0d X-MS-TrafficTypeDiagnostic: BN6PR02MB3363: X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:3276; X-Forefront-PRVS: 0235CBE7D0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: NoC5p+PqkufYV2xQ1XA/4SKT5ePcbV08wExV6oTbclRzWuo6luWKrXOLkTbwdZopgUxpZH0hUFTxucT9gDealuY3BSDn8Eq7KniAUbYA74nF5FE8EIYPFt+WOTf3amnkbTBTSBZI8k6Rrst8MkNv/WazkZDGZt8KwZy08s4OpNhMjLGxoZ/ZAYDaFInNNPLZwtmhvVyKmgxx58veuOC1ZfYQ4C7QhnyYxmJmvZIdGmoFTMz7U6LF0JVTFaLRrMY4C2c8uhel1wKR2buFEsgze3chaOUl6tvJX+/c6H/MZcEPDdtxB7EwGDLNERq3WWqHNfquMmcXHrqituwkHpj9FHdGbyWV3rvH47sg3CO9kz3ewrvPWV+E1OxRaQ6UOXsZVQJyfuJqfOxALMdqBOmU4otW9pKSGPwAjUf21yTTjVdKasjhHoxFeGZInF9aI+ih X-MS-Exchange-CrossTenant-OriginalArrivalTime: 28 Nov 2019 06:36:52.6845 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: c4b4ff29-03c6-4ae4-1014-08d773cd5b0d X-MS-Exchange-CrossTenant-Id: 5afe0b00-7697-4969-b663-5eab37d5f47e X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=5afe0b00-7697-4969-b663-5eab37d5f47e;Ip=[149.199.60.83];Helo=[xsj-pvapsmtpgw01] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN6PR02MB3363 Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org From: Shubhrajyoti Datta Currently the set rate granularity is to integral divisors. Add support for the fractional divisors. Only the first output0 is fractional in the hardware. Signed-off-by: Shubhrajyoti Datta --- drivers/clk/clk-xlnx-clock-wizard.c | 192 +++++++++++++++++++++++++++++++++--- 1 file changed, 179 insertions(+), 13 deletions(-) diff --git a/drivers/clk/clk-xlnx-clock-wizard.c b/drivers/clk/clk-xlnx-clock-wizard.c index 870e7fb..bc0354a 100644 --- a/drivers/clk/clk-xlnx-clock-wizard.c +++ b/drivers/clk/clk-xlnx-clock-wizard.c @@ -26,11 +26,15 @@ #define WZRD_CLKFBOUT_MULT_SHIFT 8 #define WZRD_CLKFBOUT_MULT_MASK (0xff << WZRD_CLKFBOUT_MULT_SHIFT) +#define WZRD_CLKFBOUT_FRAC_SHIFT 16 +#define WZRD_CLKFBOUT_FRAC_MASK (0x3ff << WZRD_CLKFBOUT_FRAC_SHIFT) #define WZRD_DIVCLK_DIVIDE_SHIFT 0 #define WZRD_DIVCLK_DIVIDE_MASK (0xff << WZRD_DIVCLK_DIVIDE_SHIFT) #define WZRD_CLKOUT_DIVIDE_SHIFT 0 #define WZRD_CLKOUT_DIVIDE_WIDTH 8 #define WZRD_CLKOUT_DIVIDE_MASK (0xff << WZRD_DIVCLK_DIVIDE_SHIFT) +#define WZRD_CLKOUT_FRAC_SHIFT 8 +#define WZRD_CLKOUT_FRAC_MASK 0x3ff #define WZRD_DR_MAX_INT_DIV_VALUE 255 #define WZRD_DR_NUM_RETRIES 10000 @@ -49,6 +53,7 @@ enum clk_wzrd_int_clks { wzrd_clk_mul, wzrd_clk_mul_div, + wzrd_clk_mul_frac, wzrd_clk_int_max }; @@ -124,7 +129,7 @@ static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw, val &= div_mask(divider->width); return divider_recalc_rate(hw, parent_rate, val, divider->table, - divider->flags, divider->width); + divider->flags); } static int clk_wzrd_dynamic_reconfig(struct clk_hw *hw, unsigned long rate, @@ -210,6 +215,161 @@ static const struct clk_ops clk_wzrd_clk_divider_ops = { .recalc_rate = clk_wzrd_recalc_rate, }; +static unsigned long clk_wzrd_recalc_ratef(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int val; + u32 div, frac; + struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw); + void __iomem *div_addr = + (void __iomem *)((u64)divider->base + divider->offset); + + val = readl(div_addr); + div = val & div_mask(divider->width); + frac = (val >> WZRD_CLKOUT_FRAC_SHIFT) & WZRD_CLKOUT_FRAC_MASK; + + return ((parent_rate * 1000) / ((div * 1000) + frac)); +} + +static int clk_wzrd_dynamic_reconfig_f(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int err = 0; + u16 retries; + u32 value, pre; + unsigned long flags = 0; + unsigned long rate_div, f, clockout0_div; + struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw); + void __iomem *div_addr = + (void __iomem *)((u64)divider->base + divider->offset); + + if (divider->lock) + spin_lock_irqsave(divider->lock, flags); + else + __acquire(divider->lock); + + rate_div = ((parent_rate * 1000) / rate); + clockout0_div = rate_div / 1000; + + pre = DIV_ROUND_CLOSEST((parent_rate * 1000), rate); + f = (u32)(pre - (clockout0_div * 1000)); + f = f & WZRD_CLKOUT_FRAC_MASK; + + value = ((f << WZRD_CLKOUT_DIVIDE_WIDTH) | (clockout0_div & + WZRD_CLKOUT_DIVIDE_MASK)); + + /* Set divisor and clear phase offset */ + writel(value, div_addr); + writel(0x0, div_addr + WZRD_DR_DIV_TO_PHASE_OFFSET); + + /* Check status register */ + retries = WZRD_DR_NUM_RETRIES; + while (retries--) { + if (readl(divider->base + WZRD_DR_STATUS_REG_OFFSET) & + WZRD_DR_LOCK_BIT_MASK) + break; + } + + if (!retries) { + err = -ETIMEDOUT; + goto err_reconfig; + } + + /* Initiate reconfiguration */ + writel(WZRD_DR_BEGIN_DYNA_RECONF, + divider->base + WZRD_DR_INIT_REG_OFFSET); + + /* Check status register */ + retries = WZRD_DR_NUM_RETRIES; + while (retries--) { + if (readl(divider->base + WZRD_DR_STATUS_REG_OFFSET) & + WZRD_DR_LOCK_BIT_MASK) + break; + } + + if (!retries) + err = -ETIMEDOUT; + +err_reconfig: + if (divider->lock) + spin_unlock_irqrestore(divider->lock, flags); + else + __release(divider->lock); + + return err; +} + +static long clk_wzrd_round_rate_f(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return rate; +} + +static const struct clk_ops clk_wzrd_clk_divider_ops_f = { + .round_rate = clk_wzrd_round_rate_f, + .set_rate = clk_wzrd_dynamic_reconfig_f, + .recalc_rate = clk_wzrd_recalc_ratef, +}; + +static struct clk *clk_wzrd_register_divf(struct device *dev, + const char *name, + const char *parent_name, + unsigned long flags, + void __iomem *base, u16 offset, + u8 shift, u8 width, + u8 clk_divider_flags, + const struct clk_div_table *table, + spinlock_t *lock) +{ + struct clk_wzrd_divider *div; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { + if (width + shift > 16) { + pr_warn("divider value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + + /* allocate the divider */ + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + init.name = name; + + if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) + init.ops = &clk_divider_ro_ops; + else + init.ops = &clk_wzrd_clk_divider_ops_f; + + init.flags = flags | CLK_IS_BASIC; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_divider assignments */ + div->base = base; + div->offset = offset; + div->shift = shift; + div->width = width; + div->flags = clk_divider_flags; + div->lock = lock; + div->hw.init = &init; + div->table = table; + + /* register the clock */ + hw = &div->hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(div); + return ERR_PTR(ret); + } + + return hw->clk; +} + static struct clk *clk_wzrd_register_divider(struct device *dev, const char *name, const char *parent_name, @@ -328,7 +488,7 @@ static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend, static int clk_wzrd_probe(struct platform_device *pdev) { int i, ret; - u32 reg; + u32 reg, reg_f, mult; unsigned long rate; const char *clk_name; struct clk_wzrd *clk_wzrd; @@ -380,17 +540,13 @@ static int clk_wzrd_probe(struct platform_device *pdev) goto err_disable_clk; } - /* we don't support fractional div/mul yet */ - reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) & - WZRD_CLKFBOUT_FRAC_EN; - reg |= readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2)) & - WZRD_CLKOUT0_FRAC_EN; - if (reg) - dev_warn(&pdev->dev, "fractional div/mul not supported\n"); - /* register multiplier */ reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) & WZRD_CLKFBOUT_MULT_MASK) >> WZRD_CLKFBOUT_MULT_SHIFT; + reg_f = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) & + WZRD_CLKFBOUT_FRAC_MASK) >> WZRD_CLKFBOUT_FRAC_SHIFT; + + mult = ((reg * 1000) + reg_f); clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev)); if (!clk_name) { ret = -ENOMEM; @@ -399,7 +555,7 @@ static int clk_wzrd_probe(struct platform_device *pdev) clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor (&pdev->dev, clk_name, __clk_get_name(clk_wzrd->clk_in1), - 0, reg, 1); + 0, mult, 1000); kfree(clk_name); if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) { dev_err(&pdev->dev, "unable to register fixed-factor clock\n"); @@ -437,8 +593,18 @@ static int clk_wzrd_probe(struct platform_device *pdev) ret = -EINVAL; goto err_rm_int_clks; } - clk_wzrd->clkout[i] = clk_wzrd_register_divider(&pdev->dev, - clkout_name, + if (!i) + clk_wzrd->clkout[i] = clk_wzrd_register_divf + (&pdev->dev, clkout_name, + clk_name, 0, + clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12), + WZRD_CLKOUT_DIVIDE_SHIFT, + WZRD_CLKOUT_DIVIDE_WIDTH, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + NULL, &clkwzrd_lock); + else + clk_wzrd->clkout[i] = clk_wzrd_register_divider + (&pdev->dev, clkout_name, clk_name, 0, clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12), WZRD_CLKOUT_DIVIDE_SHIFT,