Hi Tom,

On 03/02/2021 08:09 AM, Tom Rix wrote:
On 2/17/21 10:40 PM, Lizhi Hou wrote:
Add UCS driver. UCS is a hardware function discovered by walking xclbin
What does UCS stand for ? add to commit log
UCS stands for User Clock Subsystem. I will add it to log.
metadata. A platform device node will be created for it.
UCS enables/disables the dynamic region clocks.

Signed-off-by: Sonal Santan <sonal.san...@xilinx.com>
Signed-off-by: Max Zhen <max.z...@xilinx.com>
Signed-off-by: Lizhi Hou <liz...@xilinx.com>
---
  drivers/fpga/xrt/include/xleaf/ucs.h |  24 +++
  drivers/fpga/xrt/lib/xleaf/ucs.c     | 235 +++++++++++++++++++++++++++
  2 files changed, 259 insertions(+)
  create mode 100644 drivers/fpga/xrt/include/xleaf/ucs.h
  create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c

diff --git a/drivers/fpga/xrt/include/xleaf/ucs.h 
b/drivers/fpga/xrt/include/xleaf/ucs.h
new file mode 100644
index 000000000000..a5ef0e100e12
--- /dev/null
+++ b/drivers/fpga/xrt/include/xleaf/ucs.h
This header is only used by ucs.c, so is it needed ?

could the enum be defined in ucs.c ?
It will be used in the future. I will remove it.

@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for XRT UCS Leaf Driver
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ *   Lizhi Hou <lizhi....@xilinx.com>
+ */
+
+#ifndef _XRT_UCS_H_
+#define _XRT_UCS_H_
+
+#include "xleaf.h"
+
+/*
+ * UCS driver IOCTL calls.
+ */
+enum xrt_ucs_ioctl_cmd {
+     XRT_UCS_CHECK = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+     XRT_UCS_ENABLE,
no disable ?
It does not need to disable because reset the fpga bridge will disable it. I will remove ucs.h because it is not used in this patchset.
+};
+
+#endif       /* _XRT_UCS_H_ */
diff --git a/drivers/fpga/xrt/lib/xleaf/ucs.c b/drivers/fpga/xrt/lib/xleaf/ucs.c
new file mode 100644
index 000000000000..ae762c8fddbb
--- /dev/null
+++ b/drivers/fpga/xrt/lib/xleaf/ucs.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA UCS Driver
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ *      Lizhi Hou<lizhi....@xilinx.com>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include "metadata.h"
+#include "xleaf.h"
+#include "xleaf/ucs.h"
+#include "xleaf/clock.h"
+
+#define UCS_ERR(ucs, fmt, arg...)   \
+     xrt_err((ucs)->pdev, fmt "\n", ##arg)
+#define UCS_WARN(ucs, fmt, arg...)  \
+     xrt_warn((ucs)->pdev, fmt "\n", ##arg)
+#define UCS_INFO(ucs, fmt, arg...)  \
+     xrt_info((ucs)->pdev, fmt "\n", ##arg)
+#define UCS_DBG(ucs, fmt, arg...)   \
+     xrt_dbg((ucs)->pdev, fmt "\n", ##arg)
+
+#define XRT_UCS              "xrt_ucs"
+
+#define CHANNEL1_OFFSET                      0
+#define CHANNEL2_OFFSET                      8
+
+#define CLK_MAX_VALUE                        6400
+
+struct ucs_control_status_ch1 {
+     unsigned int shutdown_clocks_latched:1;
+     unsigned int reserved1:15;
+     unsigned int clock_throttling_average:14;
+     unsigned int reserved2:2;
+};
Likely needs to be packed and/or the unsigned int changed to u32
Will remove this structure because it is not used in this patch set.
+
+struct xrt_ucs {
+     struct platform_device  *pdev;
+     void __iomem            *ucs_base;
+     struct mutex            ucs_lock; /* ucs dev lock */
+};
+
+static inline u32 reg_rd(struct xrt_ucs *ucs, u32 offset)
+{
+     return ioread32(ucs->ucs_base + offset);
+}
+
+static inline void reg_wr(struct xrt_ucs *ucs, u32 val, u32 offset)
+{
+     iowrite32(val, ucs->ucs_base + offset);
+}
+
+static void xrt_ucs_event_cb(struct platform_device *pdev, void *arg)
+{
+     struct platform_device  *leaf;
+     struct xrt_event *evt = (struct xrt_event *)arg;
+     enum xrt_events e = evt->xe_evt;
+     enum xrt_subdev_id id = evt->xe_subdev.xevt_subdev_id;
+     int instance = evt->xe_subdev.xevt_subdev_instance;
+
+     switch (e) {
+     case XRT_EVENT_POST_CREATION:
+             break;
+     default:
+             xrt_dbg(pdev, "ignored event %d", e);
+             return;
+     }
this switch is a noop, remove
Will change to if (e != XRT_EVENT_POST_CREATION) return -EINVAL
+
+     if (id != XRT_SUBDEV_CLOCK)
+             return;
+
+     leaf = xleaf_get_leaf_by_id(pdev, XRT_SUBDEV_CLOCK, instance);
+     if (!leaf) {
+             xrt_err(pdev, "does not get clock subdev");
+             return;
+     }
+
+     xleaf_ioctl(leaf, XRT_CLOCK_VERIFY, NULL);
+     xleaf_put_leaf(pdev, leaf);
+}
+
+static void ucs_check(struct xrt_ucs *ucs, bool *latched)
+{
checking but not returning status, change to returning int.

this function is called but xrt_ucs_leaf_ioctl which does return status.
Will remove ucs_check() because it is not used in this patch set.

+     struct ucs_control_status_ch1 *ucs_status_ch1;
+     u32 status;
+
+     mutex_lock(&ucs->ucs_lock);
+     status = reg_rd(ucs, CHANNEL1_OFFSET);
+     ucs_status_ch1 = (struct ucs_control_status_ch1 *)&status;
+     if (ucs_status_ch1->shutdown_clocks_latched) {
+             UCS_ERR(ucs,
+                     "Critical temperature or power event, kernel clocks have been 
stopped.");
+             UCS_ERR(ucs,
+                     "run 'xbutil valiate -q' to continue. See AR 73398 for more 
details.");
This error message does not seem like it would be useful, please review.
+             /* explicitly indicate reset should be latched */
+             *latched = true;
+     } else if (ucs_status_ch1->clock_throttling_average >
+         CLK_MAX_VALUE) {
+             UCS_ERR(ucs, "kernel clocks %d exceeds expected maximum value 
%d.",
+                     ucs_status_ch1->clock_throttling_average,
+                     CLK_MAX_VALUE);
+     } else if (ucs_status_ch1->clock_throttling_average) {
+             UCS_ERR(ucs, "kernel clocks throttled at %d%%.",
+                     (ucs_status_ch1->clock_throttling_average /
+                      (CLK_MAX_VALUE / 100)));
+     }
+     mutex_unlock(&ucs->ucs_lock);
+}
+
+static void ucs_enable(struct xrt_ucs *ucs)
+{
+     reg_wr(ucs, 1, CHANNEL2_OFFSET);
lock ?
Yes. will add it.
+}
+
+static int
+xrt_ucs_leaf_ioctl(struct platform_device *pdev, u32 cmd, void *arg)
+{
+     struct xrt_ucs          *ucs;
+     int                     ret = 0;
+
+     ucs = platform_get_drvdata(pdev);
+
+     switch (cmd) {
+     case XRT_XLEAF_EVENT:
+             xrt_ucs_event_cb(pdev, arg);
+             break;
+     case XRT_UCS_CHECK: {
brace not needed here
will remove.
+             ucs_check(ucs, (bool *)arg);
+             break;
+     }
+     case XRT_UCS_ENABLE:
+             ucs_enable(ucs);
+             break;
+     default:
+             xrt_err(pdev, "unsupported cmd %d", cmd);
+             return -EINVAL;
+     }
+
+     return ret;
+}
+
+static int ucs_remove(struct platform_device *pdev)
+{
+     struct xrt_ucs *ucs;
+
+     ucs = platform_get_drvdata(pdev);
+     if (!ucs) {
is this possible ?
Will remove.

Thanks,
Lizhi

Tom

+             xrt_err(pdev, "driver data is NULL");
+             return -EINVAL;
+     }
+
+     if (ucs->ucs_base)
+             iounmap(ucs->ucs_base);
+
+     platform_set_drvdata(pdev, NULL);
+     devm_kfree(&pdev->dev, ucs);
+
+     return 0;
+}
+
+static int ucs_probe(struct platform_device *pdev)
+{
+     struct xrt_ucs *ucs = NULL;
+     struct resource *res;
+     int ret;
+
+     ucs = devm_kzalloc(&pdev->dev, sizeof(*ucs), GFP_KERNEL);
+     if (!ucs)
+             return -ENOMEM;
+
+     platform_set_drvdata(pdev, ucs);
+     ucs->pdev = pdev;
+     mutex_init(&ucs->ucs_lock);
+
+     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     ucs->ucs_base = ioremap(res->start, res->end - res->start + 1);
+     if (!ucs->ucs_base) {
+             UCS_ERR(ucs, "map base %pR failed", res);
+             ret = -EFAULT;
+             goto failed;
+     }
+     ucs_enable(ucs);
+
+     return 0;
+
+failed:
+     ucs_remove(pdev);
+     return ret;
+}
+
+static struct xrt_subdev_endpoints xrt_ucs_endpoints[] = {
+     {
+             .xse_names = (struct xrt_subdev_ep_names[]) {
+                     { .ep_name = XRT_MD_NODE_UCS_CONTROL_STATUS },
+                     { NULL },
+             },
+             .xse_min_ep = 1,
+     },
+     { 0 },
+};
+
+static struct xrt_subdev_drvdata xrt_ucs_data = {
+     .xsd_dev_ops = {
+             .xsd_ioctl = xrt_ucs_leaf_ioctl,
+     },
+};
+
+static const struct platform_device_id xrt_ucs_table[] = {
+     { XRT_UCS, (kernel_ulong_t)&xrt_ucs_data },
+     { },
+};
+
+static struct platform_driver xrt_ucs_driver = {
+     .driver = {
+             .name = XRT_UCS,
+     },
+     .probe = ucs_probe,
+     .remove = ucs_remove,
+     .id_table = xrt_ucs_table,
+};
+
+void ucs_leaf_init_fini(bool init)
+{
+     if (init)
+             xleaf_register_driver(XRT_SUBDEV_UCS, &xrt_ucs_driver, 
xrt_ucs_endpoints);
+     else
+             xleaf_unregister_driver(XRT_SUBDEV_UCS);
+}

Reply via email to