Received: by 2002:a05:6a10:a852:0:0:0:0 with SMTP id d18csp3677251pxy; Tue, 4 May 2021 07:38:06 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzR4r99dPNHea2rRVccRRbO4V45UehuGGwTAJUyQ8Apkh4nFQMm3TolNe28RJLzTV7kjRCb X-Received: by 2002:a17:907:110f:: with SMTP id qu15mr21315772ejb.378.1620139086687; Tue, 04 May 2021 07:38:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1620139086; cv=none; d=google.com; s=arc-20160816; b=eIQazr9+bBkXvhdWxsCmBWuAbDTY2lMEmXhcxXMtZRuwip8VlzaehSfR6IOc6yjGrH Ko9ayNrtE6cv1JCv5zVOejnr3In1/4Eq8FAy1NDAnePDiWLeJNy5/Ha6IQRJVSHq5S2h b8ogsceeu3kR9pKwyVT9bf8H4U6i8lQ9fiYqj7oJRBNMJafU4PsILm+hOJQd3BM6i/L3 VwjSTJZTlxYsUdRVePkfrizB1ynZ26JC7aouE88kJdFwYuHigzhFkq0Fn69VsWwoV8bK g3j7NE0iZWdRRsIBja7fmFsiK6FzC2XgzvR1kl2LsXATwn8U6V5f2rVeShOxqnUqNz42 Lp+w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-language:content-transfer-encoding :in-reply-to:mime-version:user-agent:date:message-id:from:references :cc:to:subject:dkim-signature; bh=CvyaK28oBja1Ac+Q+oiWhkbzV8wINd/q8xnsxOyaXi8=; b=ES/eCBMFdxb6nBHtuhJlg3fhlx/Uw4ldriA1tRsEEPcjS0UmVHWlRDe9U2IBWl+js3 EvibaFA3bFOZBZTtUTAJB+CNAl7t7c4olsU/8v3MVJtRAkm4MdOYvRSEnKqa8xZu2ZeY hU2hd2+HlWBeumHnuOcVR2tiKBhCosSFYNU30UHYK+zBiFUoglJ/wyybMKSLzp1sEfpv /rT7yjUL16o419OAYb3YtWldhY86mZMYN81NypkEpjPAeQtm6xm/TNUKhHZ3MJM4ZjeP WELb42uD219V78YIRWB22cHoZYxH+wBZd9Jre3KYsSpmxbceld5fDYf1dspM6tIf+nVf HgnQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=PZAAKoek; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id t26si5985393edf.425.2021.05.04.07.37.43; Tue, 04 May 2021 07:38:06 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=PZAAKoek; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231405AbhEDOJ7 (ORCPT + 99 others); Tue, 4 May 2021 10:09:59 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:23585 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231393AbhEDOJ6 (ORCPT ); Tue, 4 May 2021 10:09:58 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1620137343; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=CvyaK28oBja1Ac+Q+oiWhkbzV8wINd/q8xnsxOyaXi8=; b=PZAAKoekrX6JS/1xeukXZpnfvS6G/lfYd+RNJVRBR9b9BYx+CJSQwVuhPPlqvmKVJCvoM7 UIK4NYLfwZi15A2nfpPmw4V6TQ01WccdHpG4KmcdqjXx1UO5KqpBGuzVH9FMf83Ao/QzZr a3OMY4sQFZf/TgeXhzWmt3VAC4RpkOc= Received: from mail-qk1-f197.google.com (mail-qk1-f197.google.com [209.85.222.197]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-304-HvG4t7t5O1yXqbBUST4yMQ-1; Tue, 04 May 2021 10:08:57 -0400 X-MC-Unique: HvG4t7t5O1yXqbBUST4yMQ-1 Received: by mail-qk1-f197.google.com with SMTP id u126-20020a3792840000b02902e769005fe1so7601706qkd.2 for ; Tue, 04 May 2021 07:08:56 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-transfer-encoding :content-language; bh=CvyaK28oBja1Ac+Q+oiWhkbzV8wINd/q8xnsxOyaXi8=; b=C93C9U9+L6D8VAdMgibejs55o68L9ek9NBk9l3adApK5FWdmb2EVXGQwcSC87kh9+y +G4jOJOivM9MbqNLWosKHjswcsK7vqZ//W2Y3PpADu8ssTEOhACnMZOWYh9Z/z6Ijg3e qMzfk4N/ggnmsJ/dyiJP5YV3UnwIGt3T66W89T0OJqeYJp2NvnUsgAQOCGP+lyFxfaEx yplzXFHKIX/4G8VdS+/V58k6vqvzQvgXjkmjkFhvl+4c7jJn0A3maFGjv/Jq9mYU0qQR 1VfOzZ4XshDdPz6weqnaHGXrFB3fDokAkqR544AE0gsTE9J5nPFOCXDNjyd4o/RQfMPp ZyNg== X-Gm-Message-State: AOAM531uEtfIKPeltapiBseNBq3bCPDro+R+zc7R1MTKRwRqFbBJYlEk WWSmCJIz//XGVYRo+3MJPtimRvDdWwSq4Ng4YlSlRzUyvh5of0F9zqYK3RvMIZfnj530mCSvasz GE6G8qowy/ule2Gj7BEq4R43t X-Received: by 2002:a05:6214:18d:: with SMTP id q13mr26047741qvr.60.1620137335839; Tue, 04 May 2021 07:08:55 -0700 (PDT) X-Received: by 2002:a05:6214:18d:: with SMTP id q13mr26047709qvr.60.1620137335560; Tue, 04 May 2021 07:08:55 -0700 (PDT) Received: from localhost.localdomain (075-142-250-213.res.spectrum.com. [75.142.250.213]) by smtp.gmail.com with ESMTPSA id a23sm5301447qkl.6.2021.05.04.07.08.53 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 04 May 2021 07:08:55 -0700 (PDT) Subject: Re: [PATCH V5 XRT Alveo 16/20] fpga: xrt: clock driver To: Lizhi Hou , linux-kernel@vger.kernel.org Cc: linux-fpga@vger.kernel.org, maxz@xilinx.com, sonal.santan@xilinx.com, yliu@xilinx.com, michal.simek@xilinx.com, stefanos@xilinx.com, devicetree@vger.kernel.org, mdf@kernel.org, robh@kernel.org, Max Zhen References: <20210427205431.23896-1-lizhi.hou@xilinx.com> <20210427205431.23896-17-lizhi.hou@xilinx.com> From: Tom Rix Message-ID: Date: Tue, 4 May 2021 07:08:52 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.1 MIME-Version: 1.0 In-Reply-To: <20210427205431.23896-17-lizhi.hou@xilinx.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-Language: en-US Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 4/27/21 1:54 PM, Lizhi Hou wrote: > Add clock driver. Clock is a hardware function discovered by walking > xclbin metadata. A xrt device node will be created for it. Other part of > driver configures clock through clock driver. > > Signed-off-by: Sonal Santan > Signed-off-by: Max Zhen > Signed-off-by: Lizhi Hou v4 was also ok, please add my Reviewed-by line Reviewed-by: Tom Rix > --- > drivers/fpga/xrt/include/xleaf/clock.h | 29 ++ > drivers/fpga/xrt/lib/xleaf/clock.c | 652 +++++++++++++++++++++++++ > 2 files changed, 681 insertions(+) > create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h > create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c > > diff --git a/drivers/fpga/xrt/include/xleaf/clock.h b/drivers/fpga/xrt/include/xleaf/clock.h > new file mode 100644 > index 000000000000..6858473fd096 > --- /dev/null > +++ b/drivers/fpga/xrt/include/xleaf/clock.h > @@ -0,0 +1,29 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2021 Xilinx, Inc. > + * > + * Authors: > + * Lizhi Hou > + */ > + > +#ifndef _XRT_CLOCK_H_ > +#define _XRT_CLOCK_H_ > + > +#include "xleaf.h" > +#include > + > +/* > + * CLOCK driver leaf calls. > + */ > +enum xrt_clock_leaf_cmd { > + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */ > + XRT_CLOCK_GET, > + XRT_CLOCK_VERIFY, > +}; > + > +struct xrt_clock_get { > + u16 freq; > + u32 freq_cnter; > +}; > + > +#endif /* _XRT_CLOCK_H_ */ > diff --git a/drivers/fpga/xrt/lib/xleaf/clock.c b/drivers/fpga/xrt/lib/xleaf/clock.c > new file mode 100644 > index 000000000000..7303be55c07a > --- /dev/null > +++ b/drivers/fpga/xrt/lib/xleaf/clock.c > @@ -0,0 +1,652 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Xilinx Alveo FPGA Clock Wizard Driver > + * > + * Copyright (C) 2020-2021 Xilinx, Inc. > + * > + * Authors: > + * Lizhi Hou > + * Sonal Santan > + * David Zhang > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include "metadata.h" > +#include "xleaf.h" > +#include "xleaf/clock.h" > +#include "xleaf/clkfreq.h" > + > +/* XRT_CLOCK_MAX_NUM_CLOCKS should be a concept from XCLBIN_ in the future */ > +#define XRT_CLOCK_MAX_NUM_CLOCKS 4 > +#define XRT_CLOCK_STATUS_MASK 0xffff > +#define XRT_CLOCK_STATUS_MEASURE_START 0x1 > +#define XRT_CLOCK_STATUS_MEASURE_DONE 0x2 > + > +#define XRT_CLOCK_STATUS_REG 0x4 > +#define XRT_CLOCK_CLKFBOUT_REG 0x200 > +#define XRT_CLOCK_CLKOUT0_REG 0x208 > +#define XRT_CLOCK_LOAD_SADDR_SEN_REG 0x25C > +#define XRT_CLOCK_DEFAULT_EXPIRE_SECS 1 > + > +#define CLOCK_ERR(clock, fmt, arg...) \ > + xrt_err((clock)->xdev, fmt "\n", ##arg) > +#define CLOCK_WARN(clock, fmt, arg...) \ > + xrt_warn((clock)->xdev, fmt "\n", ##arg) > +#define CLOCK_INFO(clock, fmt, arg...) \ > + xrt_info((clock)->xdev, fmt "\n", ##arg) > +#define CLOCK_DBG(clock, fmt, arg...) \ > + xrt_dbg((clock)->xdev, fmt "\n", ##arg) > + > +#define XRT_CLOCK "xrt_clock" > + > +XRT_DEFINE_REGMAP_CONFIG(clock_regmap_config); > + > +struct clock { > + struct xrt_device *xdev; > + struct regmap *regmap; > + struct mutex clock_lock; /* clock dev lock */ > + > + const char *clock_ep_name; > +}; > + > +/* > + * Precomputed table with config0 and config2 register values together with > + * target frequency. The steps are approximately 5 MHz apart. Table is > + * generated by platform creation tool. > + */ > +static const struct xmgnt_ocl_clockwiz { > + /* target frequency */ > + u16 ocl; > + /* config0 register */ > + u32 config0; > + /* config2 register */ > + u32 config2; > +} frequency_table[] = { > + /*1275.000*/ { 10, 0x02EE0C01, 0x0001F47F }, > + /*1575.000*/ { 15, 0x02EE0F01, 0x00000069}, > + /*1600.000*/ { 20, 0x00001001, 0x00000050}, > + /*1600.000*/ { 25, 0x00001001, 0x00000040}, > + /*1575.000*/ { 30, 0x02EE0F01, 0x0001F434}, > + /*1575.000*/ { 35, 0x02EE0F01, 0x0000002D}, > + /*1600.000*/ { 40, 0x00001001, 0x00000028}, > + /*1575.000*/ { 45, 0x02EE0F01, 0x00000023}, > + /*1600.000*/ { 50, 0x00001001, 0x00000020}, > + /*1512.500*/ { 55, 0x007D0F01, 0x0001F41B}, > + /*1575.000*/ { 60, 0x02EE0F01, 0x0000FA1A}, > + /*1462.500*/ { 65, 0x02710E01, 0x0001F416}, > + /*1575.000*/ { 70, 0x02EE0F01, 0x0001F416}, > + /*1575.000*/ { 75, 0x02EE0F01, 0x00000015}, > + /*1600.000*/ { 80, 0x00001001, 0x00000014}, > + /*1487.500*/ { 85, 0x036B0E01, 0x0001F411}, > + /*1575.000*/ { 90, 0x02EE0F01, 0x0001F411}, > + /*1425.000*/ { 95, 0x00FA0E01, 0x0000000F}, > + /*1600.000*/ { 100, 0x00001001, 0x00000010}, > + /*1575.000*/ { 105, 0x02EE0F01, 0x0000000F}, > + /*1512.500*/ { 110, 0x007D0F01, 0x0002EE0D}, > + /*1437.500*/ { 115, 0x01770E01, 0x0001F40C}, > + /*1575.000*/ { 120, 0x02EE0F01, 0x00007D0D}, > + /*1562.500*/ { 125, 0x02710F01, 0x0001F40C}, > + /*1462.500*/ { 130, 0x02710E01, 0x0000FA0B}, > + /*1350.000*/ { 135, 0x01F40D01, 0x0000000A}, > + /*1575.000*/ { 140, 0x02EE0F01, 0x0000FA0B}, > + /*1450.000*/ { 145, 0x01F40E01, 0x0000000A}, > + /*1575.000*/ { 150, 0x02EE0F01, 0x0001F40A}, > + /*1550.000*/ { 155, 0x01F40F01, 0x0000000A}, > + /*1600.000*/ { 160, 0x00001001, 0x0000000A}, > + /*1237.500*/ { 165, 0x01770C01, 0x0001F407}, > + /*1487.500*/ { 170, 0x036B0E01, 0x0002EE08}, > + /*1575.000*/ { 175, 0x02EE0F01, 0x00000009}, > + /*1575.000*/ { 180, 0x02EE0F01, 0x0002EE08}, > + /*1387.500*/ { 185, 0x036B0D01, 0x0001F407}, > + /*1425.000*/ { 190, 0x00FA0E01, 0x0001F407}, > + /*1462.500*/ { 195, 0x02710E01, 0x0001F407}, > + /*1600.000*/ { 200, 0x00001001, 0x00000008}, > + /*1537.500*/ { 205, 0x01770F01, 0x0001F407}, > + /*1575.000*/ { 210, 0x02EE0F01, 0x0001F407}, > + /*1075.000*/ { 215, 0x02EE0A01, 0x00000005}, > + /*1512.500*/ { 220, 0x007D0F01, 0x00036B06}, > + /*1575.000*/ { 225, 0x02EE0F01, 0x00000007}, > + /*1437.500*/ { 230, 0x01770E01, 0x0000FA06}, > + /*1175.000*/ { 235, 0x02EE0B01, 0x00000005}, > + /*1500.000*/ { 240, 0x00000F01, 0x0000FA06}, > + /*1225.000*/ { 245, 0x00FA0C01, 0x00000005}, > + /*1562.500*/ { 250, 0x02710F01, 0x0000FA06}, > + /*1275.000*/ { 255, 0x02EE0C01, 0x00000005}, > + /*1462.500*/ { 260, 0x02710E01, 0x00027105}, > + /*1325.000*/ { 265, 0x00FA0D01, 0x00000005}, > + /*1350.000*/ { 270, 0x01F40D01, 0x00000005}, > + /*1512.500*/ { 275, 0x007D0F01, 0x0001F405}, > + /*1575.000*/ { 280, 0x02EE0F01, 0x00027105}, > + /*1425.000*/ { 285, 0x00FA0E01, 0x00000005}, > + /*1450.000*/ { 290, 0x01F40E01, 0x00000005}, > + /*1475.000*/ { 295, 0x02EE0E01, 0x00000005}, > + /*1575.000*/ { 300, 0x02EE0F01, 0x0000FA05}, > + /*1525.000*/ { 305, 0x00FA0F01, 0x00000005}, > + /*1550.000*/ { 310, 0x01F40F01, 0x00000005}, > + /*1575.000*/ { 315, 0x02EE0F01, 0x00000005}, > + /*1600.000*/ { 320, 0x00001001, 0x00000005}, > + /*1462.500*/ { 325, 0x02710E01, 0x0001F404}, > + /*1237.500*/ { 330, 0x01770C01, 0x0002EE03}, > + /* 837.500*/ { 335, 0x01770801, 0x0001F402}, > + /*1487.500*/ { 340, 0x036B0E01, 0x00017704}, > + /* 862.500*/ { 345, 0x02710801, 0x0001F402}, > + /*1575.000*/ { 350, 0x02EE0F01, 0x0001F404}, > + /* 887.500*/ { 355, 0x036B0801, 0x0001F402}, > + /*1575.000*/ { 360, 0x02EE0F01, 0x00017704}, > + /* 912.500*/ { 365, 0x007D0901, 0x0001F402}, > + /*1387.500*/ { 370, 0x036B0D01, 0x0002EE03}, > + /*1500.000*/ { 375, 0x00000F01, 0x00000004}, > + /*1425.000*/ { 380, 0x00FA0E01, 0x0002EE03}, > + /* 962.500*/ { 385, 0x02710901, 0x0001F402}, > + /*1462.500*/ { 390, 0x02710E01, 0x0002EE03}, > + /* 987.500*/ { 395, 0x036B0901, 0x0001F402}, > + /*1600.000*/ { 400, 0x00001001, 0x00000004}, > + /*1012.500*/ { 405, 0x007D0A01, 0x0001F402}, > + /*1537.500*/ { 410, 0x01770F01, 0x0002EE03}, > + /*1037.500*/ { 415, 0x01770A01, 0x0001F402}, > + /*1575.000*/ { 420, 0x02EE0F01, 0x0002EE03}, > + /*1487.500*/ { 425, 0x036B0E01, 0x0001F403}, > + /*1075.000*/ { 430, 0x02EE0A01, 0x0001F402}, > + /*1087.500*/ { 435, 0x036B0A01, 0x0001F402}, > + /*1375.000*/ { 440, 0x02EE0D01, 0x00007D03}, > + /*1112.500*/ { 445, 0x007D0B01, 0x0001F402}, > + /*1575.000*/ { 450, 0x02EE0F01, 0x0001F403}, > + /*1137.500*/ { 455, 0x01770B01, 0x0001F402}, > + /*1437.500*/ { 460, 0x01770E01, 0x00007D03}, > + /*1162.500*/ { 465, 0x02710B01, 0x0001F402}, > + /*1175.000*/ { 470, 0x02EE0B01, 0x0001F402}, > + /*1425.000*/ { 475, 0x00FA0E01, 0x00000003}, > + /*1500.000*/ { 480, 0x00000F01, 0x00007D03}, > + /*1212.500*/ { 485, 0x007D0C01, 0x0001F402}, > + /*1225.000*/ { 490, 0x00FA0C01, 0x0001F402}, > + /*1237.500*/ { 495, 0x01770C01, 0x0001F402}, > + /*1562.500*/ { 500, 0x02710F01, 0x00007D03}, > + /*1262.500*/ { 505, 0x02710C01, 0x0001F402}, > + /*1275.000*/ { 510, 0x02EE0C01, 0x0001F402}, > + /*1287.500*/ { 515, 0x036B0C01, 0x0001F402}, > + /*1300.000*/ { 520, 0x00000D01, 0x0001F402}, > + /*1575.000*/ { 525, 0x02EE0F01, 0x00000003}, > + /*1325.000*/ { 530, 0x00FA0D01, 0x0001F402}, > + /*1337.500*/ { 535, 0x01770D01, 0x0001F402}, > + /*1350.000*/ { 540, 0x01F40D01, 0x0001F402}, > + /*1362.500*/ { 545, 0x02710D01, 0x0001F402}, > + /*1512.500*/ { 550, 0x007D0F01, 0x0002EE02}, > + /*1387.500*/ { 555, 0x036B0D01, 0x0001F402}, > + /*1400.000*/ { 560, 0x00000E01, 0x0001F402}, > + /*1412.500*/ { 565, 0x007D0E01, 0x0001F402}, > + /*1425.000*/ { 570, 0x00FA0E01, 0x0001F402}, > + /*1437.500*/ { 575, 0x01770E01, 0x0001F402}, > + /*1450.000*/ { 580, 0x01F40E01, 0x0001F402}, > + /*1462.500*/ { 585, 0x02710E01, 0x0001F402}, > + /*1475.000*/ { 590, 0x02EE0E01, 0x0001F402}, > + /*1487.500*/ { 595, 0x036B0E01, 0x0001F402}, > + /*1575.000*/ { 600, 0x02EE0F01, 0x00027102}, > + /*1512.500*/ { 605, 0x007D0F01, 0x0001F402}, > + /*1525.000*/ { 610, 0x00FA0F01, 0x0001F402}, > + /*1537.500*/ { 615, 0x01770F01, 0x0001F402}, > + /*1550.000*/ { 620, 0x01F40F01, 0x0001F402}, > + /*1562.500*/ { 625, 0x02710F01, 0x0001F402}, > + /*1575.000*/ { 630, 0x02EE0F01, 0x0001F402}, > + /*1587.500*/ { 635, 0x036B0F01, 0x0001F402}, > + /*1600.000*/ { 640, 0x00001001, 0x0001F402}, > + /*1290.000*/ { 645, 0x01F44005, 0x00000002}, > + /*1462.500*/ { 650, 0x02710E01, 0x0000FA02} > +}; > + > +static u32 find_matching_freq_config(unsigned short freq, > + const struct xmgnt_ocl_clockwiz *table, > + int size) > +{ > + u32 end = size - 1; > + u32 start = 0; > + u32 idx; > + > + if (freq < table[0].ocl) > + return 0; > + > + if (freq > table[size - 1].ocl) > + return size - 1; > + > + while (start < end) { > + idx = (start + end) / 2; > + if (freq == table[idx].ocl) > + break; > + if (freq < table[idx].ocl) > + end = idx; > + else > + start = idx + 1; > + } > + if (freq < table[idx].ocl) > + idx--; > + > + return idx; > +} > + > +static u32 find_matching_freq(u32 freq, > + const struct xmgnt_ocl_clockwiz *freq_table, > + int freq_table_size) > +{ > + int idx = find_matching_freq_config(freq, freq_table, freq_table_size); > + > + return freq_table[idx].ocl; > +} > + > +static inline int clock_wiz_busy(struct clock *clock, int cycle, int interval) > +{ > + u32 val = 0; > + int count; > + int ret; > + > + for (count = 0; count < cycle; count++) { > + ret = regmap_read(clock->regmap, XRT_CLOCK_STATUS_REG, &val); > + if (ret) { > + CLOCK_ERR(clock, "read status failed %d", ret); > + return ret; > + } > + if (val == 1) > + break; > + > + mdelay(interval); > + } > + if (val != 1) { > + CLOCK_ERR(clock, "clockwiz is (%u) busy after %d ms", > + val, cycle * interval); > + return -EBUSY; > + } > + > + return 0; > +} > + > +static int get_freq(struct clock *clock, u16 *freq) > +{ > + u32 mul_frac0 = 0; > + u32 div_frac1 = 0; > + u32 mul0, div0; > + u64 input; > + u32 div1; > + u32 val; > + int ret; > + > + WARN_ON(!mutex_is_locked(&clock->clock_lock)); > + > + ret = regmap_read(clock->regmap, XRT_CLOCK_STATUS_REG, &val); > + if (ret) { > + CLOCK_ERR(clock, "read status failed %d", ret); > + return ret; > + } > + > + if ((val & 0x1) == 0) { > + CLOCK_ERR(clock, "clockwiz is busy %x", val); > + *freq = 0; > + return -EBUSY; > + } > + > + ret = regmap_read(clock->regmap, XRT_CLOCK_CLKFBOUT_REG, &val); > + if (ret) { > + CLOCK_ERR(clock, "read clkfbout failed %d", ret); > + return ret; > + } > + > + div0 = val & 0xff; > + mul0 = (val & 0xff00) >> 8; > + if (val & BIT(26)) { > + mul_frac0 = val >> 16; > + mul_frac0 &= 0x3ff; > + } > + > + /* > + * Multiply both numerator (mul0) and the denominator (div0) with 1000 > + * to account for fractional portion of multiplier > + */ > + mul0 *= 1000; > + mul0 += mul_frac0; > + div0 *= 1000; > + > + ret = regmap_read(clock->regmap, XRT_CLOCK_CLKOUT0_REG, &val); > + if (ret) { > + CLOCK_ERR(clock, "read clkout0 failed %d", ret); > + return ret; > + } > + > + div1 = val & 0xff; > + if (val & BIT(18)) { > + div_frac1 = val >> 8; > + div_frac1 &= 0x3ff; > + } > + > + /* > + * Multiply both numerator (mul0) and the denominator (div1) with > + * 1000 to account for fractional portion of divider > + */ > + > + div1 *= 1000; > + div1 += div_frac1; > + div0 *= div1; > + mul0 *= 1000; > + if (div0 == 0) { > + CLOCK_ERR(clock, "clockwiz 0 divider"); > + return 0; > + } > + > + input = mul0 * 100; > + do_div(input, div0); > + *freq = (u16)input; > + > + return 0; > +} > + > +static int set_freq(struct clock *clock, u16 freq) > +{ > + int err = 0; > + u32 idx = 0; > + u32 val = 0; > + u32 config; > + > + mutex_lock(&clock->clock_lock); > + idx = find_matching_freq_config(freq, frequency_table, > + ARRAY_SIZE(frequency_table)); > + > + CLOCK_INFO(clock, "New: %d Mhz", freq); > + err = clock_wiz_busy(clock, 20, 50); > + if (err) > + goto fail; > + > + config = frequency_table[idx].config0; > + err = regmap_write(clock->regmap, XRT_CLOCK_CLKFBOUT_REG, config); > + if (err) { > + CLOCK_ERR(clock, "write clkfbout failed %d", err); > + goto fail; > + } > + > + config = frequency_table[idx].config2; > + err = regmap_write(clock->regmap, XRT_CLOCK_CLKOUT0_REG, config); > + if (err) { > + CLOCK_ERR(clock, "write clkout0 failed %d", err); > + goto fail; > + } > + > + mdelay(10); > + err = regmap_write(clock->regmap, XRT_CLOCK_LOAD_SADDR_SEN_REG, 7); > + if (err) { > + CLOCK_ERR(clock, "write load_saddr_sen failed %d", err); > + goto fail; > + } > + > + mdelay(1); > + err = regmap_write(clock->regmap, XRT_CLOCK_LOAD_SADDR_SEN_REG, 2); > + if (err) { > + CLOCK_ERR(clock, "write saddr failed %d", err); > + goto fail; > + } > + > + CLOCK_INFO(clock, "clockwiz waiting for locked signal"); > + > + err = clock_wiz_busy(clock, 100, 100); > + if (err) { > + CLOCK_ERR(clock, "clockwiz MMCM/PLL did not lock"); > + /* restore */ > + regmap_write(clock->regmap, XRT_CLOCK_LOAD_SADDR_SEN_REG, 4); > + mdelay(10); > + regmap_write(clock->regmap, XRT_CLOCK_LOAD_SADDR_SEN_REG, 0); > + goto fail; > + } > + regmap_read(clock->regmap, XRT_CLOCK_CLKFBOUT_REG, &val); > + CLOCK_INFO(clock, "clockwiz CONFIG(0) 0x%x", val); > + regmap_read(clock->regmap, XRT_CLOCK_CLKOUT0_REG, &val); > + CLOCK_INFO(clock, "clockwiz CONFIG(2) 0x%x", val); > + > +fail: > + mutex_unlock(&clock->clock_lock); > + return err; > +} > + > +static int get_freq_counter(struct clock *clock, u32 *freq) > +{ > + struct xrt_subdev_platdata *pdata = DEV_PDATA(clock->xdev); > + struct xrt_device *xdev = clock->xdev; > + struct xrt_device *counter_leaf; > + const void *counter; > + int err; > + > + WARN_ON(!mutex_is_locked(&clock->clock_lock)); > + > + err = xrt_md_get_prop(DEV(xdev), pdata->xsp_dtb, clock->clock_ep_name, > + NULL, XRT_MD_PROP_CLK_CNT, &counter, NULL); > + if (err) { > + xrt_err(xdev, "no counter specified"); > + return err; > + } > + > + counter_leaf = xleaf_get_leaf_by_epname(xdev, counter); > + if (!counter_leaf) { > + xrt_err(xdev, "can't find counter"); > + return -ENOENT; > + } > + > + err = xleaf_call(counter_leaf, XRT_CLKFREQ_READ, freq); > + if (err) > + xrt_err(xdev, "can't read counter"); > + xleaf_put_leaf(clock->xdev, counter_leaf); > + > + return err; > +} > + > +static int clock_get_freq(struct clock *clock, u16 *freq, u32 *freq_cnter) > +{ > + int err = 0; > + > + mutex_lock(&clock->clock_lock); > + > + if (err == 0 && freq) > + err = get_freq(clock, freq); > + > + if (err == 0 && freq_cnter) > + err = get_freq_counter(clock, freq_cnter); > + > + mutex_unlock(&clock->clock_lock); > + return err; > +} > + > +static int clock_verify_freq(struct clock *clock) > +{ > + u32 lookup_freq, clock_freq_counter, request_in_khz, tolerance; > + int err = 0; > + u16 freq; > + > + mutex_lock(&clock->clock_lock); > + > + err = get_freq(clock, &freq); > + if (err) { > + xrt_err(clock->xdev, "get freq failed, %d", err); > + goto end; > + } > + > + err = get_freq_counter(clock, &clock_freq_counter); > + if (err) { > + xrt_err(clock->xdev, "get freq counter failed, %d", err); > + goto end; > + } > + > + lookup_freq = find_matching_freq(freq, frequency_table, > + ARRAY_SIZE(frequency_table)); > + request_in_khz = lookup_freq * 1000; > + tolerance = lookup_freq * 50; > + if (tolerance < abs(clock_freq_counter - request_in_khz)) { > + CLOCK_ERR(clock, > + "set clock(%s) failed, request %ukhz, actual %dkhz", > + clock->clock_ep_name, request_in_khz, clock_freq_counter); > + err = -EDOM; > + } else { > + CLOCK_INFO(clock, "verified clock (%s)", clock->clock_ep_name); > + } > + > +end: > + mutex_unlock(&clock->clock_lock); > + return err; > +} > + > +static int clock_init(struct clock *clock) > +{ > + struct xrt_subdev_platdata *pdata = DEV_PDATA(clock->xdev); > + const u16 *freq; > + int err = 0; > + > + err = xrt_md_get_prop(DEV(clock->xdev), pdata->xsp_dtb, > + clock->clock_ep_name, NULL, XRT_MD_PROP_CLK_FREQ, > + (const void **)&freq, NULL); > + if (err) { > + xrt_info(clock->xdev, "no default freq"); > + return 0; > + } > + > + err = set_freq(clock, be16_to_cpu(*freq)); > + > + return err; > +} > + > +static ssize_t freq_show(struct device *dev, struct device_attribute *attr, char *buf) > +{ > + struct clock *clock = xrt_get_drvdata(to_xrt_dev(dev)); > + ssize_t count; > + u16 freq = 0; > + > + count = clock_get_freq(clock, &freq, NULL); > + if (count < 0) > + return count; > + > + count = snprintf(buf, 64, "%u\n", freq); > + > + return count; > +} > +static DEVICE_ATTR_RO(freq); > + > +static struct attribute *clock_attrs[] = { > + &dev_attr_freq.attr, > + NULL, > +}; > + > +static struct attribute_group clock_attr_group = { > + .attrs = clock_attrs, > +}; > + > +static int > +xrt_clock_leaf_call(struct xrt_device *xdev, u32 cmd, void *arg) > +{ > + struct clock *clock; > + int ret = 0; > + > + clock = xrt_get_drvdata(xdev); > + > + switch (cmd) { > + case XRT_XLEAF_EVENT: > + /* Does not handle any event. */ > + break; > + case XRT_CLOCK_SET: { > + u16 freq = (u16)(uintptr_t)arg; > + > + ret = set_freq(clock, freq); > + break; > + } > + case XRT_CLOCK_VERIFY: > + ret = clock_verify_freq(clock); > + break; > + case XRT_CLOCK_GET: { > + struct xrt_clock_get *get = > + (struct xrt_clock_get *)arg; > + > + ret = clock_get_freq(clock, &get->freq, &get->freq_cnter); > + break; > + } > + default: > + xrt_err(xdev, "unsupported cmd %d", cmd); > + return -EINVAL; > + } > + > + return ret; > +} > + > +static void clock_remove(struct xrt_device *xdev) > +{ > + sysfs_remove_group(&xdev->dev.kobj, &clock_attr_group); > +} > + > +static int clock_probe(struct xrt_device *xdev) > +{ > + struct clock *clock = NULL; > + void __iomem *base = NULL; > + struct resource *res; > + int ret; > + > + clock = devm_kzalloc(&xdev->dev, sizeof(*clock), GFP_KERNEL); > + if (!clock) > + return -ENOMEM; > + > + xrt_set_drvdata(xdev, clock); > + clock->xdev = xdev; > + mutex_init(&clock->clock_lock); > + > + res = xrt_get_resource(xdev, IORESOURCE_MEM, 0); > + if (!res) { > + ret = -EINVAL; > + goto failed; > + } > + > + base = devm_ioremap_resource(&xdev->dev, res); > + if (IS_ERR(base)) { > + ret = PTR_ERR(base); > + goto failed; > + } > + > + clock->regmap = devm_regmap_init_mmio(&xdev->dev, base, &clock_regmap_config); > + if (IS_ERR(clock->regmap)) { > + CLOCK_ERR(clock, "regmap %pR failed", res); > + ret = PTR_ERR(clock->regmap); > + goto failed; > + } > + clock->clock_ep_name = res->name; > + > + ret = clock_init(clock); > + if (ret) > + goto failed; > + > + ret = sysfs_create_group(&xdev->dev.kobj, &clock_attr_group); > + if (ret) { > + CLOCK_ERR(clock, "create clock attrs failed: %d", ret); > + goto failed; > + } > + > + CLOCK_INFO(clock, "successfully initialized Clock subdev"); > + > + return 0; > + > +failed: > + return ret; > +} > + > +static struct xrt_dev_endpoints xrt_clock_endpoints[] = { > + { > + .xse_names = (struct xrt_dev_ep_names[]) { > + { .compat = "clkwiz" }, > + { NULL }, > + }, > + .xse_min_ep = 1, > + }, > + { 0 }, > +}; > + > +static struct xrt_driver xrt_clock_driver = { > + .driver = { > + .name = XRT_CLOCK, > + }, > + .subdev_id = XRT_SUBDEV_CLOCK, > + .endpoints = xrt_clock_endpoints, > + .probe = clock_probe, > + .remove = clock_remove, > + .leaf_call = xrt_clock_leaf_call, > +}; > + > +XRT_LEAF_INIT_FINI_FUNC(clock);