Received: by 2002:a25:7ec1:0:0:0:0:0 with SMTP id z184csp6429321ybc; Wed, 27 Nov 2019 22:38:35 -0800 (PST) X-Google-Smtp-Source: APXvYqwfeI8JalkSq9VAKk9a2QFX42xM2q4H4QaouPi7fBqUpk3vi1CpG/ZgLjfiD73HK3XxkM4u X-Received: by 2002:aa7:db55:: with SMTP id n21mr36704591edt.113.1574923115235; Wed, 27 Nov 2019 22:38:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1574923115; cv=none; d=google.com; s=arc-20160816; b=afOO77vrzfwsJhJUPppTVdHw5IFyD6ei9rqXTC3UxqzVRFkqEoK9TKr26qJy28M0In vBYhDJMD5EnKUI+uou2NsWiCsuV2GlrB437iWPoJcv9pYntsAN/SP+/zNnIF9G035Uvo fH5mSYxMiaqQcTvXN52bUD4DxbHeQ/BF3002pvNi7ccnHIsQppEJrs8eKukv2G5idzt4 eapV2K+1+OIaBcsO7Yrv3fNhYUwnikGlPSmkbLCv/gtXnu+n9BfWADzBU+6lgIpsaYFr yj3qeC2GKsaKSmi5zPqzHRz+Ij6Bj0arxsHbpwpp5XMaLl2mCgFhjYlASB1sZRcohyJW M0hg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=ZNxXr/tpgukOTXNh81HOvMmP3S2z5xJqf2Ode4iUXWY=; b=HIIB1+N0VzTJIkLFBoQ0KKAay7bASPWDthJ1HyerxBve3v0TPhnByqOzPXOd5wGrKm 92Z683nxlKsvS3+i196L3YVdXYDctzX/y2Fedni1ASmfvs4J2I74lugP0nkjinc0SRU/ 5rb/98afS15q3X4za+krtt0/liG1GrN2baXxDJHhn96OqbpVMzKPN9bqUoVRahIbqfqw dgN+NYmTkT7ovl+16G7ocC7Tuqatt/xyYIxNPghtFLNz163EaaruOiKnK7CArQPzq2UT DrJby230oLxC8hLNIWVy97gIWbv1jDSNTSblt/DPly5q+Mj4VtBZNoDOTBTvPegCE7sq ikEw== ARC-Authentication-Results: i=1; mx.google.com; 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; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m7si11671184edv.377.2019.11.27.22.38.11; Wed, 27 Nov 2019 22:38:35 -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; 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; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727192AbfK1Gg4 (ORCPT + 99 others); Thu, 28 Nov 2019 01:36:56 -0500 Received: from mail-eopbgr690076.outbound.protection.outlook.com ([40.107.69.76]:58414 "EHLO NAM04-CO1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726438AbfK1Ggz (ORCPT ); Thu, 28 Nov 2019 01:36:55 -0500 Received: from SN6PR02CA0017.namprd02.prod.outlook.com (2603:10b6:805:a2::30) by BN7PR02MB4147.namprd02.prod.outlook.com (2603:10b6:406:f7::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2495.20; Thu, 28 Nov 2019 06:36:50 +0000 Received: from CY1NAM02FT064.eop-nam02.prod.protection.outlook.com (2a01:111:f400:7e45::205) by SN6PR02CA0017.outlook.office365.com (2603:10b6:805:a2::30) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2495.19 via Frontend Transport; Thu, 28 Nov 2019 06:36:50 +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 CY1NAM02FT064.mail.protection.outlook.com (10.152.74.64) 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:49 +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 1iaDQ0-0007Xt-7f; Wed, 27 Nov 2019 22:36:48 -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 1iaDPv-0005CU-4j; Wed, 27 Nov 2019 22:36:43 -0800 Received: from xsj-pvapsmtp01 (maildrop.xilinx.com [149.199.38.66]) by xsj-smtp-dlp2.xlnx.xilinx.com (8.13.8/8.13.1) with ESMTP id xAS6afgt007288; Wed, 27 Nov 2019 22:36:42 -0800 Received: from [10.140.6.59] (helo=xhdshubhraj40.xilinx.com) by xsj-pvapsmtp01 with esmtp (Exim 4.63) (envelope-from ) id 1iaDPt-00059i-Cp; Wed, 27 Nov 2019 22:36:41 -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 , Chirag Parekh , Michal Simek Subject: [PATCH v3 04/10] clk: clock-wizard: Add support for dynamic reconfiguration Date: Thu, 28 Nov 2019 12:06:11 +0530 Message-Id: <7a951e702f0c25217bacfcd4444d347d8bb2621e.1574922435.git.shubhrajyoti.datta@xilinx.com> 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--4.391-7.0-31-1 X-imss-scan-details: No--4.391-7.0-31-1;No--4.391-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: 132193966099372708;(f9e945fa-a09a-4caa-7158-08d2eb1d8c44);() X-Forefront-Antispam-Report: CIP:149.199.60.83;IPV:NLI;CTRY:US;EFV:NLI;SFV:NSPM;SFS:(10009020)(136003)(39860400002)(346002)(396003)(376002)(189003)(199004)(450100002)(14444005)(446003)(426003)(36756003)(336012)(11346002)(118296001)(47776003)(4326008)(73392003)(107886003)(26005)(76176011)(70206006)(70586007)(82202003)(356004)(6666004)(5660300002)(7696005)(51416003)(86362001)(498600001)(9686003)(54906003)(8936002)(50226002)(81166006)(16586007)(2906002)(316002)(81156014)(76482006)(8676002)(305945005)(9786002)(50466002)(48376002);DIR:OUT;SFP:1101;SCL:1;SRVR:BN7PR02MB4147;H:xsj-pvapsmtpgw01;FPR:;SPF:SoftFail;LANG:en;PTR:unknown-60-83.xilinx.com;MX:1;A:1; MIME-Version: 1.0 Content-Type: text/plain X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 71e61167-11ad-4aa4-9083-08d773cd593f X-MS-TrafficTypeDiagnostic: BN7PR02MB4147: X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:1201; X-Forefront-PRVS: 0235CBE7D0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: fDR5ywWfK2KUHEjJmMONSg8Jw5w19AJ07ZeT8IqCZw6+7kJh56SGnEr+eoLFD8/WRu6RFFoFOnVukactu9l6bYAnu0sLMk8Wqbq7xyi6S2ixyhFyKraxzoE6Wmwk+JEClugx9iAIqAH2oCcgHR6m1NmaX4odOF8AF0vfg7qlSkryRtLu8uJJIlTdQsC88kofu9BSSYddiuWmQMkPnc6hTookhkeMPPuK1mqlE0h9UGX6KSZ1iv62z+k8jSaUFkdTLU42d9B409EVE9gfuNzvj+8ODsSP1c/r6GWJBWG/nHrJpl6dQfI3v7xyGaiN2Utr8FqS6FeqJlZEgMvJkp5PAq06duLvCyuK9+9VO2O4uAFIP71PRXyojCmtdnc/pA6/EQ6bge6tmyYyCwP0yfllojyNLmaIPr6ylIa2FWEH19KInpujT7aNZAZ0iYK5oiHG X-MS-Exchange-CrossTenant-OriginalArrivalTime: 28 Nov 2019 06:36:49.7251 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 71e61167-11ad-4aa4-9083-08d773cd593f 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: BN7PR02MB4147 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Shubhrajyoti Datta The patch adds support for dynamic reconfiguration of clock output rate. Output clocks are registered as dividers and set rate callback function is used for dynamic reconfiguration. Based on the initial work from Chirag. Signed-off-by: Chirag Parekh Signed-off-by: Shubhrajyoti Datta Signed-off-by: Michal Simek --- drivers/clk/clk-xlnx-clock-wizard.c | 209 +++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 5 deletions(-) diff --git a/drivers/clk/clk-xlnx-clock-wizard.c b/drivers/clk/clk-xlnx-clock-wizard.c index ef9125d..870e7fb 100644 --- a/drivers/clk/clk-xlnx-clock-wizard.c +++ b/drivers/clk/clk-xlnx-clock-wizard.c @@ -29,8 +29,23 @@ #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_DR_MAX_INT_DIV_VALUE 255 +#define WZRD_DR_NUM_RETRIES 10000 +#define WZRD_DR_STATUS_REG_OFFSET 0x04 +#define WZRD_DR_LOCK_BIT_MASK 0x00000001 +#define WZRD_DR_INIT_REG_OFFSET 0x25C +#define WZRD_DR_DIV_TO_PHASE_OFFSET 4 +#define WZRD_DR_BEGIN_DYNA_RECONF 0x03 + +/* Get the mask from width */ +#define div_mask(width) ((1 << (width)) - 1) + +/* Extract divider instance from clock hardware instance */ +#define to_clk_wzrd_divider(_hw) container_of(_hw, struct clk_wzrd_divider, hw) + enum clk_wzrd_int_clks { wzrd_clk_mul, wzrd_clk_mul_div, @@ -62,6 +77,29 @@ struct clk_wzrd { bool suspended; }; +/** + * struct clk_wzrd_divider - clock divider specific to clk_wzrd + * + * @hw: handle between common and hardware-specific interfaces + * @base: base address of register containing the divider + * @offset: offset address of register containing the divider + * @shift: shift to the divider bit field + * @width: width of the divider bit field + * @flags: clk_wzrd divider flags + * @table: array of value/divider pairs, last entry should have div = 0 + * @lock: register lock + */ +struct clk_wzrd_divider { + struct clk_hw hw; + void __iomem *base; + u16 offset; + u8 shift; + u8 width; + u8 flags; + const struct clk_div_table *table; + spinlock_t *lock; /* divider lock */ +}; + #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) /* maximum frequencies for input/output clocks per speed grade */ @@ -71,6 +109,164 @@ static const unsigned long clk_wzrd_max_freq[] = { 1066000000UL }; +/* spin lock variable for clk_wzrd */ +static DEFINE_SPINLOCK(clkwzrd_lock); + +static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw); + void __iomem *div_addr = + (void __iomem *)((u64)divider->base + divider->offset); + unsigned int val; + + val = readl(div_addr) >> divider->shift; + val &= div_mask(divider->width); + + return divider_recalc_rate(hw, parent_rate, val, divider->table, + divider->flags, divider->width); +} + +static int clk_wzrd_dynamic_reconfig(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int err = 0; + u16 retries; + u32 value; + unsigned long flags = 0; + 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); + + value = DIV_ROUND_CLOSEST(parent_rate, rate); + + /* Cap the value to max */ + if (value > WZRD_DR_MAX_INT_DIV_VALUE) + value = WZRD_DR_MAX_INT_DIV_VALUE; + + /* Set divisor and clear phase offset */ + writel(value, div_addr); + writel(0x00, 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 == 0) { + 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 == 0) + 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(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + u8 div; + + /* + * since we donot change parent rate we just round rate to closest + * achievable + */ + div = DIV_ROUND_CLOSEST(*prate, rate); + + return (*prate / div); +} + +static const struct clk_ops clk_wzrd_clk_divider_ops = { + .round_rate = clk_wzrd_round_rate, + .set_rate = clk_wzrd_dynamic_reconfig, + .recalc_rate = clk_wzrd_recalc_rate, +}; + +static struct clk *clk_wzrd_register_divider(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; + 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); + hw = ERR_PTR(ret); + } + + return hw->clk; +} + static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event, void *data) { @@ -241,11 +437,14 @@ static int clk_wzrd_probe(struct platform_device *pdev) ret = -EINVAL; goto err_rm_int_clks; } - reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2) + i * 12); - reg &= WZRD_CLKOUT_DIVIDE_MASK; - reg >>= WZRD_CLKOUT_DIVIDE_SHIFT; - clk_wzrd->clkout[i] = clk_register_fixed_factor - (&pdev->dev, clkout_name, clk_name, 0, 1, reg); + 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, + WZRD_CLKOUT_DIVIDE_WIDTH, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + NULL, &clkwzrd_lock); if (IS_ERR(clk_wzrd->clkout[i])) { int j; -- 2.1.1