mbox series

[V4,XRT,Alveo,00/20] XRT Alveo driver overview

Message ID 20210324052947.27889-1-lizhi.hou@xilinx.com
Headers show
Series XRT Alveo driver overview | expand

Message

Lizhi Hou March 24, 2021, 5:29 a.m. UTC
Hello,

This is V4 of patch series which adds management physical function driver
for Xilinx Alveo PCIe accelerator cards.
    https://www.xilinx.com/products/boards-and-kits/alveo.html

This driver is part of Xilinx Runtime (XRT) open source stack.
The V4 patch series do not include bus_type change as suggested before.
The bus_type change will come with v5 patch series.

XILINX ALVEO PLATFORM ARCHITECTURE

Alveo PCIe FPGA based platforms have a static *shell* partition and a
partial re-configurable *user* partition. The shell partition is
automatically loaded from flash when host is booted and PCIe is enumerated
by BIOS. Shell cannot be changed till the next cold reboot. The shell
exposes two PCIe physical functions:

1. management physical function
2. user physical function

The patch series includes Documentation/xrt.rst which describes Alveo
platform, XRT driver architecture and deployment model in more detail.

Users compile their high level design in C/C++/OpenCL or RTL into FPGA
image using Vitis tools.
    https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html

The compiled image is packaged as xclbin which contains partial bitstream
for the user partition and necessary metadata. Users can dynamically swap
the image running on the user partition in order to switch between
different workloads by loading different xclbins.

XRT DRIVERS FOR XILINX ALVEO

XRT Linux kernel driver *xmgmt* binds to management physical function of
Alveo platform. The modular driver framework is organized into several
platform drivers which primarily handle the following functionality:

1.  Loading firmware container also called xsabin at driver attach time
2.  Loading of user compiled xclbin with FPGA Manager integration
3.  Clock scaling of image running on user partition
4.  In-band sensors: temp, voltage, power, etc.
5.  Device reset and rescan

The platform drivers are packaged into *xrt-lib* helper module with well
defined interfaces. The module provides a pseudo-bus implementation for the
platform drivers. More details on the driver model can be found in
Documentation/xrt.rst.

User physical function driver is not included in this patch series.

LIBFDT REQUIREMENT

XRT driver infrastructure uses Device Tree as a metadata format to discover
HW subsystems in the Alveo PCIe device. The Device Tree schema used by XRT
is documented in Documentation/xrt.rst.

TESTING AND VALIDATION

xmgmt driver can be tested with full XRT open source stack which includes
user space libraries, board utilities and (out of tree) first generation
user physical function driver xocl. XRT open source runtime stack is
available at https://github.com/Xilinx/XRT

Complete documentation for XRT open source stack including sections on
Alveo/XRT security and platform architecture can be found here:

https://xilinx.github.io/XRT/master/html/index.html
https://xilinx.github.io/XRT/master/html/security.html
https://xilinx.github.io/XRT/master/html/platforms_partitions.html

Changes since v3:
- Leaf drivers use regmap-mmio to access hardware registers.
- Renamed driver module: xmgmt.ko -> xrt-mgmt.ko
- Renamed files: calib.[c|h] -> ddr_calibration.[c|h],
                 lib/main.[c|h] -> lib/lib-drv.[c|h],
                 mgmt/main-impl.h - > mgmt/xmgnt.h
- Updated code base to include v3 code review comments.

Changes since v2:
- Streamlined the driver framework into *xleaf*, *group* and *xroot*
- Updated documentation to show the driver model with examples
- Addressed kernel test robot errors
- Added a selftest for basic driver framework
- Documented device tree schema
- Removed need to export libfdt symbols

Changes since v1:
- Updated the driver to use fpga_region and fpga_bridge for FPGA
  programming
- Dropped platform drivers not related to PR programming to focus on XRT
  core framework
- Updated Documentation/fpga/xrt.rst with information on XRT core framework
- Addressed checkpatch issues
- Dropped xrt- prefix from some header files

For reference V3 version of patch series can be found here:

https://lore.kernel.org/lkml/20210218064019.29189-1-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-2-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-3-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-4-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-5-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-6-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-7-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-8-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-9-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-10-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-11-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-12-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-13-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-14-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-15-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-16-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-17-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-18-lizhih@xilinx.com
https://lore.kernel.org/lkml/20210218064019.29189-19-lizhih@xilinx.com

Lizhi Hou (20):
  Documentation: fpga: Add a document describing XRT Alveo drivers
  fpga: xrt: driver metadata helper functions
  fpga: xrt: xclbin file helper functions
  fpga: xrt: xrt-lib platform driver manager
  fpga: xrt: group platform driver
  fpga: xrt: char dev node helper functions
  fpga: xrt: root driver infrastructure
  fpga: xrt: platform driver infrastructure
  fpga: xrt: management physical function driver (root)
  fpga: xrt: main platform driver for management function device
  fpga: xrt: fpga-mgr and region implementation for xclbin download
  fpga: xrt: VSEC platform driver
  fpga: xrt: User Clock Subsystem platform driver
  fpga: xrt: ICAP platform driver
  fpga: xrt: devctl platform driver
  fpga: xrt: clock platform driver
  fpga: xrt: clock frequency counter platform driver
  fpga: xrt: DDR calibration platform driver
  fpga: xrt: partition isolation platform driver
  fpga: xrt: Kconfig and Makefile updates for XRT drivers

 Documentation/fpga/index.rst                  |   1 +
 Documentation/fpga/xrt.rst                    | 844 +++++++++++++++++
 MAINTAINERS                                   |  11 +
 drivers/Makefile                              |   1 +
 drivers/fpga/Kconfig                          |   2 +
 drivers/fpga/Makefile                         |   5 +
 drivers/fpga/xrt/Kconfig                      |   8 +
 drivers/fpga/xrt/include/events.h             |  45 +
 drivers/fpga/xrt/include/group.h              |  25 +
 drivers/fpga/xrt/include/metadata.h           | 233 +++++
 drivers/fpga/xrt/include/subdev_id.h          |  38 +
 drivers/fpga/xrt/include/xclbin-helper.h      |  48 +
 drivers/fpga/xrt/include/xleaf.h              | 264 ++++++
 drivers/fpga/xrt/include/xleaf/axigate.h      |  23 +
 drivers/fpga/xrt/include/xleaf/clkfreq.h      |  21 +
 drivers/fpga/xrt/include/xleaf/clock.h        |  29 +
 .../fpga/xrt/include/xleaf/ddr_calibration.h  |  28 +
 drivers/fpga/xrt/include/xleaf/devctl.h       |  40 +
 drivers/fpga/xrt/include/xleaf/icap.h         |  27 +
 drivers/fpga/xrt/include/xmgmt-main.h         |  34 +
 drivers/fpga/xrt/include/xroot.h              | 117 +++
 drivers/fpga/xrt/lib/Kconfig                  |  17 +
 drivers/fpga/xrt/lib/Makefile                 |  30 +
 drivers/fpga/xrt/lib/cdev.c                   | 232 +++++
 drivers/fpga/xrt/lib/group.c                  | 286 ++++++
 drivers/fpga/xrt/lib/lib-drv.c                | 277 ++++++
 drivers/fpga/xrt/lib/lib-drv.h                |  17 +
 drivers/fpga/xrt/lib/subdev.c                 | 865 ++++++++++++++++++
 drivers/fpga/xrt/lib/subdev_pool.h            |  53 ++
 drivers/fpga/xrt/lib/xclbin.c                 | 369 ++++++++
 drivers/fpga/xrt/lib/xleaf/axigate.c          | 342 +++++++
 drivers/fpga/xrt/lib/xleaf/clkfreq.c          | 240 +++++
 drivers/fpga/xrt/lib/xleaf/clock.c            | 669 ++++++++++++++
 drivers/fpga/xrt/lib/xleaf/ddr_calibration.c  | 226 +++++
 drivers/fpga/xrt/lib/xleaf/devctl.c           | 183 ++++
 drivers/fpga/xrt/lib/xleaf/icap.c             | 344 +++++++
 drivers/fpga/xrt/lib/xleaf/ucs.c              | 167 ++++
 drivers/fpga/xrt/lib/xleaf/vsec.c             | 388 ++++++++
 drivers/fpga/xrt/lib/xroot.c                  | 589 ++++++++++++
 drivers/fpga/xrt/metadata/Kconfig             |  12 +
 drivers/fpga/xrt/metadata/Makefile            |  16 +
 drivers/fpga/xrt/metadata/metadata.c          | 545 +++++++++++
 drivers/fpga/xrt/mgmt/Kconfig                 |  15 +
 drivers/fpga/xrt/mgmt/Makefile                |  19 +
 drivers/fpga/xrt/mgmt/fmgr-drv.c              | 191 ++++
 drivers/fpga/xrt/mgmt/fmgr.h                  |  19 +
 drivers/fpga/xrt/mgmt/main-region.c           | 483 ++++++++++
 drivers/fpga/xrt/mgmt/main.c                  | 670 ++++++++++++++
 drivers/fpga/xrt/mgmt/root.c                  | 333 +++++++
 drivers/fpga/xrt/mgmt/xmgnt.h                 |  34 +
 include/uapi/linux/xrt/xclbin.h               | 409 +++++++++
 include/uapi/linux/xrt/xmgmt-ioctl.h          |  46 +
 52 files changed, 9930 insertions(+)
 create mode 100644 Documentation/fpga/xrt.rst
 create mode 100644 drivers/fpga/xrt/Kconfig
 create mode 100644 drivers/fpga/xrt/include/events.h
 create mode 100644 drivers/fpga/xrt/include/group.h
 create mode 100644 drivers/fpga/xrt/include/metadata.h
 create mode 100644 drivers/fpga/xrt/include/subdev_id.h
 create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
 create mode 100644 drivers/fpga/xrt/include/xleaf.h
 create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
 create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
 create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
 create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
 create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
 create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
 create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h
 create mode 100644 drivers/fpga/xrt/include/xroot.h
 create mode 100644 drivers/fpga/xrt/lib/Kconfig
 create mode 100644 drivers/fpga/xrt/lib/Makefile
 create mode 100644 drivers/fpga/xrt/lib/cdev.c
 create mode 100644 drivers/fpga/xrt/lib/group.c
 create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
 create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
 create mode 100644 drivers/fpga/xrt/lib/subdev.c
 create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h
 create mode 100644 drivers/fpga/xrt/lib/xclbin.c
 create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
 create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c
 create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
 create mode 100644 drivers/fpga/xrt/lib/xleaf/ddr_calibration.c
 create mode 100644 drivers/fpga/xrt/lib/xleaf/devctl.c
 create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
 create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c
 create mode 100644 drivers/fpga/xrt/lib/xleaf/vsec.c
 create mode 100644 drivers/fpga/xrt/lib/xroot.c
 create mode 100644 drivers/fpga/xrt/metadata/Kconfig
 create mode 100644 drivers/fpga/xrt/metadata/Makefile
 create mode 100644 drivers/fpga/xrt/metadata/metadata.c
 create mode 100644 drivers/fpga/xrt/mgmt/Kconfig
 create mode 100644 drivers/fpga/xrt/mgmt/Makefile
 create mode 100644 drivers/fpga/xrt/mgmt/fmgr-drv.c
 create mode 100644 drivers/fpga/xrt/mgmt/fmgr.h
 create mode 100644 drivers/fpga/xrt/mgmt/main-region.c
 create mode 100644 drivers/fpga/xrt/mgmt/main.c
 create mode 100644 drivers/fpga/xrt/mgmt/root.c
 create mode 100644 drivers/fpga/xrt/mgmt/xmgnt.h
 create mode 100644 include/uapi/linux/xrt/xclbin.h
 create mode 100644 include/uapi/linux/xrt/xmgmt-ioctl.h

Comments

Tom Rix March 27, 2021, 2:37 p.m. UTC | #1
general problem with xmgmt needing to be changed to xrt-mgmt


On 3/23/21 10:29 PM, Lizhi Hou wrote:
> Describe XRT driver architecture and provide basic overview of

> Xilinx Alveo platform.

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>  Documentation/fpga/index.rst |   1 +

>  Documentation/fpga/xrt.rst   | 844 +++++++++++++++++++++++++++++++++++

>  2 files changed, 845 insertions(+)

>  create mode 100644 Documentation/fpga/xrt.rst

>

> diff --git a/Documentation/fpga/index.rst b/Documentation/fpga/index.rst

> index f80f95667ca2..30134357b70d 100644

> --- a/Documentation/fpga/index.rst

> +++ b/Documentation/fpga/index.rst

> @@ -8,6 +8,7 @@ fpga

>      :maxdepth: 1

>  

>      dfl

> +    xrt

>  

>  .. only::  subproject and html

>  

> diff --git a/Documentation/fpga/xrt.rst b/Documentation/fpga/xrt.rst

> new file mode 100644

> index 000000000000..0f7977464270

> --- /dev/null

> +++ b/Documentation/fpga/xrt.rst

> @@ -0,0 +1,844 @@

> +.. SPDX-License-Identifier: GPL-2.0

> +

> +==================================

> +XRTV2 Linux Kernel Driver Overview

> +==================================

> +

> +Authors:

> +

> +* Sonal Santan <sonal.santan@xilinx.com>

> +* Max Zhen <max.zhen@xilinx.com>

> +* Lizhi Hou <lizhi.hou@xilinx.com>

> +

> +XRTV2 drivers are second generation `XRT <https://github.com/Xilinx/XRT>`_

> +drivers which support `Alveo <https://www.xilinx.com/products/boards-and-kits/alveo.html>`_

> +PCIe platforms from Xilinx.

> +

> +XRTV2 drivers support *subsystem* style data driven platforms where driver's

> +configuration and behavior is determined by meta data provided by the platform

> +(in *device tree* format). Primary management physical function (MPF) driver

> +is called **xmgmt**. Primary user physical function (UPF) driver is called

> +**xuser** and is under development. xrt driver framework and HW subsystem

> +drivers are packaged into a library module called **xrt-lib**, which is

> +shared by **xmgmt** and **xuser** (under development). The xrt driver framework

> +implements a pseudo-bus which is used to discover HW subsystems and facilitate

> +inter HW subsystem interaction.

> +

> +Driver Modules

> +==============

> +

> +xrt-lib.ko

> +----------

> +

> +Repository of all subsystem drivers and pure software modules that can potentially

> +be shared between xmgmt and xuser. All these drivers are structured as Linux

> +*platform driver* and are instantiated by xmgmt (or xuser under development) based

> +on meta data associated with the hardware. The metadata is in the form of a device

ok
> +tree as mentioned before. Each platform driver statically defines a subsystem node

> +array by using node name or a string in its ``compatible`` property. And this

> +array is eventually translated to IOMEM resources of the platform device.

> +

> +The xrt-lib core infrastructure provides hooks to platform drivers for device node

> +management, user file operations and ioctl callbacks. The core infrastructure also

ok
> +provides pseudo-bus functionality for platform driver registration, discovery and

> +inter platform driver ioctl calls.

if/where infrastructure moves is undecided.
> +

> +.. note::

> +   See code in ``include/xleaf.h``

> +

> +

> +xmgmt.ko

> +--------

> +

> +The xmgmt driver is a PCIe device driver driving MPF found on Xilinx's Alveo

> +PCIE device. It consists of one *root* driver, one or more *group* drivers

> +and one or more *xleaf* drivers. The root and MPF specific xleaf drivers are

> +in xmgmt.ko. The group driver and other xleaf drivers are in xrt-lib.ko.

> +

> +The instantiation of specific group driver or xleaf driver is completely data

> +driven based on meta data (mostly in device tree format) found through VSEC

> +capability and inside firmware files, such as platform xsabin or user xclbin file.

> +The root driver manages the life cycle of multiple group drivers, which, in turn,

> +manages multiple xleaf drivers. This allows a single set of drivers to support

ok
> +all kinds of subsystems exposed by different shells. The difference among all

> +these subsystems will be handled in xleaf drivers with root and group drivers

> +being part of the infrastructure and provide common services for all leaves

> +found on all platforms.

> +

> +The driver object model looks like the following::

> +

> +                    +-----------+

> +                    |   xroot   |

> +                    +-----+-----+

> +                          |

> +              +-----------+-----------+

> +              |                       |

> +              v                       v

> +        +-----------+          +-----------+

> +        |   group   |    ...   |   group   |

> +        +-----+-----+          +------+----+

> +              |                       |

> +              |                       |

> +        +-----+----+            +-----+----+

> +        |          |            |          |

> +        v          v            v          v

> +    +-------+  +-------+    +-------+  +-------+

> +    | xleaf |..| xleaf |    | xleaf |..| xleaf |

> +    +-------+  +-------+    +-------+  +-------+

> +

> +As an example for Xilinx Alveo U50 before user xclbin download, the tree

> +looks like the following::

> +

> +                                +-----------+

> +                                |   xmgmt   |

> +                                +-----+-----+

> +                                      |

> +            +-------------------------+--------------------+

> +            |                         |                    |

> +            v                         v                    v

> +       +--------+                +--------+            +--------+

> +       | group0 |                | group1 |            | group2 |

> +       +----+---+                +----+---+            +---+----+

> +            |                         |                    |

> +            |                         |                    |

> +      +-----+-----+        +----+-----+---+    +-----+-----+----+--------+

> +      |           |        |    |         |    |     |          |        |

> +      v           v        |    v         v    |     v          v        |

> + +------------+  +------+  | +------+ +------+ |  +------+ +-----------+ |

> + | xmgmt_main |  | VSEC |  | | GPIO | | QSPI | |  |  CMC | | AXI-GATE0 | |

> + +------------+  +------+  | +------+ +------+ |  +------+ +-----------+ |

> +                           | +---------+       |  +------+ +-----------+ |

> +                           +>| MAILBOX |       +->| ICAP | | AXI-GATE1 |<+

> +                             +---------+       |  +------+ +-----------+

> +                                               |  +-------+

> +                                               +->| CALIB |

> +                                                  +-------+

> +

> +After an xclbin is download, group3 will be added and the tree looks like the

> +following::

> +

> +                                +-----------+

> +                                |   xmgmt   |

> +                                +-----+-----+

> +                                      |

> +            +-------------------------+--------------------+-----------------+

> +            |                         |                    |                 |

> +            v                         v                    v                 |

> +       +--------+                +--------+            +--------+            |

> +       | group0 |                | group1 |            | group2 |            |

> +       +----+---+                +----+---+            +---+----+            |

> +            |                         |                    |                 |

> +            |                         |                    |                 |

> +      +-----+-----+       +-----+-----+---+    +-----+-----+----+--------+   |

> +      |           |       |     |         |    |     |          |        |   |

> +      v           v       |     v         v    |     v          v        |   |

> + +------------+  +------+ | +------+ +------+  |  +------+ +-----------+ |   |

> + | xmgmt_main |  | VSEC | | | GPIO | | QSPI |  |  |  CMC | | AXI-GATE0 | |   |

> + +------------+  +------+ | +------+ +------+  |  +------+ +-----------+ |   |

> +                          | +---------+        |  +------+ +-----------+ |   |

> +                          +>| MAILBOX |        +->| ICAP | | AXI-GATE1 |<+   |

> +                            +---------+        |  +------+ +-----------+     |

> +                                               |  +-------+                  |

> +                                               +->| CALIB |                  |

> +                                                  +-------+                  |

> +                      +---+----+                                             |

> +                      | group3 |<--------------------------------------------+

> +                      +--------+

> +                          |

> +                          |

> +     +-------+--------+---+--+--------+------+-------+

> +     |       |        |      |        |      |       |

> +     v       |        v      |        v      |       v

> + +--------+  |   +--------+  |   +--------+  |    +-----+

> + | CLOCK0 |  |   | CLOCK1 |  |   | CLOCK2 |  |    | UCS |

> + +--------+  v   +--------+  v   +--------+  v    +-----+

> + +-------------+ +-------------+ +-------------+

> + | CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |

> + +-------------+ +-------------+ +-------------+

> +

> +

> +xmgmt-root

> +^^^^^^^^^^

> +

> +The xmgmt-root driver is a PCIe device driver attached to MPF. It's part of the

> +infrastructure of the MPF driver and resides in xmgmt.ko. This driver

> +

> +* manages one or more group drivers

> +* provides access to functionalities that requires pci_dev, such as PCIE config

> +  space access, to other xleaf drivers through root calls

> +* facilities event callbacks for other xleaf drivers

> +* facilities inter-leaf driver calls for other xleaf drivers

> +

> +When root driver starts, it will explicitly create an initial group instance,

> +which contains xleaf drivers that will trigger the creation of other group

> +instances. The root driver will wait for all group and leaves to be created

> +before it returns from it's probe routine and claim success of the

> +initialization of the entire xmgmt driver. If any leaf fails to initialize the

> +xmgmt driver will still come online but with limited functionality.

thanks for adding this
> +

> +.. note::

> +   See code in ``lib/xroot.c`` and ``mgmt/root.c``

> +

> +

> +group

> +^^^^^

> +

> +The group driver represents a pseudo device whose life cycle is managed by

ok
> +root and does not have real IO mem or IRQ resources. It's part of the

> +infrastructure of the MPF driver and resides in xrt-lib.ko. This driver

> +

> +* manages one or more xleaf drivers

> +* provides access to root from leaves, so that root calls, event notifications

> +  and inter-leaf calls can happen

> +

> +In xmgmt, an initial group driver instance will be created by the root. This

> +instance contains leaves that will trigger group instances to be created to

> +manage groups of leaves found on different partitions on hardware, such as

> +VSEC, Shell, and User.

> +

> +Every *fpga_region* has a group object associated with it. The group is

> +created when xclbin image is loaded on the fpga_region. The existing group

> +is destroyed when a new xclbin image is loaded. The fpga_region persists

> +across xclbin downloads.

> +

> +.. note::

> +   See code in ``lib/group.c``

> +

> +

> +xleaf

> +^^^^^

> +

> +The xleaf driver is a platform device driver whose life cycle is managed by

> +a group driver and may or may not have real IO mem or IRQ resources. They

> +are the real meat of xmgmt and contains platform specific code to Shell and

> +User found on a MPF.

> +

> +A xleaf driver may not have real hardware resources when it merely acts as a

> +driver that manages certain in-memory states for xmgmt.


A xleaf driver without real hardware resources manages in-memory states for xrt-mgmt.

A more concise wording of above, change if you like.

I noticed use of xmgmt, this changed in v4 ot xrt-mgmt, check doc for others.

>  These in-memory states

> +could be shared by multiple other leaves.

> +

> +Leaf drivers assigned to specific hardware resources drive specific subsystem in

drive a specific
> +the device. To manipulate the subsystem or carry out a task, a xleaf driver may

> +ask help from root via root calls and/or from other leaves via inter-leaf calls.

ask for help
> +

> +A xleaf can also broadcast events through infrastructure code for other leaves

> +to process. It can also receive event notification from infrastructure about

> +certain events, such as post-creation or pre-exit of a particular xleaf.

> +

> +.. note::

> +   See code in ``lib/xleaf/*.c``

> +

> +

> +FPGA Manager Interaction

> +========================

> +

> +fpga_manager

> +------------

> +

> +An instance of fpga_manager is created by xmgmt_main and is used for xclbin

> +image download. fpga_manager requires the full xclbin image before it can

> +start programming the FPGA configuration engine via Internal Configuration

> +Access Port (ICAP) platform driver.

thanks for expanding icap
> +

> +fpga_region

> +-----------

> +

> +For every interface exposed by the currently loaded xclbin/xsabin in the

ok
> +*parent* fpga_region a new instance of fpga_region is created like a *child*

ok
> +fpga_region. The device tree of the *parent* fpga_region defines the

> +resources for a new instance of fpga_bridge which isolates the parent from

> +child fpga_region. This new instance of fpga_bridge will be used when a

ok
> +xclbin image is loaded on the child fpga_region. After the xclbin image is

> +downloaded to the fpga_region, an instance of group is created for the

> +fpga_region using the device tree obtained as part of the xclbin. If this

> +device tree defines any child interfaces then it can trigger the creation of

> +fpga_bridge and fpga_region for the next region in the chain.

a fpga_bridge and a fpga_region
> +

> +fpga_bridge

> +-----------

> +

> +Like the fpga_region, matching fpga_bridge is also created by walking the

ok
> +device tree of the parent group.

> +

> +Driver Interfaces

> +=================

> +

> +xmgmt Driver Ioctls

> +-------------------

> +

> +Ioctls exposed by xmgmt driver to user space are enumerated in the following

> +table:

> +

> +== ===================== ============================ ==========================

> +#  Functionality         ioctl request code            data format

> +== ===================== ============================ ==========================

> +1  FPGA image download   XMGMT_IOCICAPDOWNLOAD_AXLF    xmgmt_ioc_bitstream_axlf

> +== ===================== ============================ ==========================

> +

> +A user xclbin can be downloaded by using the xbmgmt tool from the XRT open source

> +suite. See example usage below::

ok
> +

> +  xbmgmt partition --program --path /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verify.xclbin --force

> +

> +xmgmt Driver Sysfs

> +------------------

> +

> +xmgmt driver exposes a rich set of sysfs interfaces. Subsystem platform

> +drivers export sysfs node for every platform instance.

> +

> +Every partition also exports its UUIDs. See below for examples::

> +

> +  /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids

> +  /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids

> +

> +

> +hwmon

> +-----

> +

> +xmgmt driver exposes standard hwmon interface to report voltage, current,

> +temperature, power, etc. These can easily be viewed using *sensors* command

> +line utility.

> +

> +Alveo Platform Overview

> +=======================

> +

> +Alveo platforms are architected as two physical FPGA partitions: *Shell* and

> +*User*. The Shell provides basic infrastructure for the Alveo platform like

> +PCIe connectivity, board management, Dynamic Function Exchange (DFX), sensors,

> +clocking, reset, and security. User partition contains user compiled FPGA


The User partition

contains the user

> +binary which is loaded by a process called DFX also known as partial

> +reconfiguration.

> +

> +For DFX to work properly physical partitions require strict HW compatibility

properly, physical
> +with each other. Every physical partition has two interface UUIDs: *parent* UUID

> +and *child* UUID. For simple single stage platforms, Shell → User forms parent

> +child relationship.

> +

> +.. note::

> +   Partition compatibility matching is key design component of Alveo platforms

is a key
> +   and XRT. Partitions have child and parent relationship. A loaded partition

> +   exposes child partition UUID to advertise its compatibility requirement.When

space needed after '.'
> +   loading a child partition the xmgmt management driver matches parent UUID of

> +   the child partition against child UUID exported by the parent. Parent and

> +   child partition UUIDs are stored in the *xclbin* (for user) or *xsabin* (for

> +   shell). Except for root UUID exported by VSEC, hardware itself does not know

> +   about UUIDs. UUIDs are stored in xsabin and xclbin. The image format has a

> +   special node called Partition UUIDs which define the compatibility UUIDs. See

> +   :ref:`partition_uuids`.

> +

This is worded better, thanks.
> +

> +The physical partitions and their loading is illustrated below::

> +

> +           SHELL                               USER

> +        +-----------+                  +-------------------+

> +        |           |                  |                   |

> +        | VSEC UUID | CHILD     PARENT |    LOGIC UUID     |

> +        |           o------->|<--------o                   |

> +        |           | UUID       UUID  |                   |

> +        +-----+-----+                  +--------+----------+

> +              |                                 |

> +              .                                 .

> +              |                                 |

> +          +---+---+                      +------+--------+

> +          |  POR  |                      | USER COMPILED |

> +          | FLASH |                      |    XCLBIN     |

> +          +-------+                      +---------------+

> +

> +

> +Loading Sequence

> +----------------

> +

> +The Shell partition is loaded from flash at system boot time. It establishes the

> +PCIe link and exposes two physical functions to the BIOS. After the OS boots, xmgmt


the xrt-mgmt

> +driver attaches to the PCIe physical function 0 exposed by the Shell and then looks

> +for VSEC in PCIe extended configuration space. Using VSEC it determines the logic


in the PCIe

Using VSEC, it

> +UUID of Shell and uses the UUID to load matching *xsabin* file from Linux firmware

> +directory. The xsabin file contains metadata to discover peripherals that are part


contains the metadata

the peripherals

> +of Shell and firmware(s) for any embedded soft processors in Shell. The xsabin file


of the Shell and the the firmware

can drop '(s)'

> +also contains Partition UUIDs as described here :ref:`partition_uuids`.

> +

> +The Shell exports a child interface UUID which is used for the compatibility check

ok
> +when loading user compiled xclbin over the User partition as part of DFX. When a user

> +requests loading of a specific xclbin the xmgmt management driver reads the parent


xclbin, the xrt-mgmt driver

can drop 'managment' since 'mgmt' is management

> +interface UUID specified in the xclbin and matches it with child interface UUID

> +exported by Shell to determine if xclbin is compatible with the Shell. If match

> +fails loading of xclbin is denied.

It the match fails, loading is denied.
> +

> +xclbin loading is requested using ICAP_DOWNLOAD_AXLF ioctl command. When loading

> +xclbin, xmgmt driver performs the following *logical* operations:

ok
> +

> +1. Copy xclbin from user to kernel memory

> +2. Sanity check the xclbin contents

> +3. Isolate the User partition

> +4. Download the bitstream using the FPGA config engine (ICAP)

> +5. De-isolate the User partition

> +6. Program the clocks (ClockWiz) driving the User partition

> +7. Wait for memory controller (MIG) calibration

for the
> +8. Return the loading status back to the caller

> +

> +`Platform Loading Overview <https://xilinx.github.io/XRT/master/html/platforms_partitions.html>`_

> +provides more detailed information on platform loading.

> +

> +

> +xsabin

> +------

> +

> +Each Alveo platform comes packaged with its own xsabin. The xsabin is a trusted

ok
> +component of the platform. For format details refer to :ref:`xsabin_xclbin_container_format`

> +below. xsabin contains basic information like UUIDs, platform name and metadata in the

> +form of device tree. See :ref:`device_tree_usage` below for details and example.

ok
> +

> +xclbin

> +------

> +

> +xclbin is compiled by end user using

> +`Vitis <https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html>`_

> +tool set from Xilinx. The xclbin contains sections describing user compiled

> +acceleration engines/kernels, memory subsystems, clocking information etc. It also

> +contains FPGA bitstream for the user partition, UUIDs, platform name, etc.


needs to be either

FPGA bitstreams

or

a FPGA bitstream

> +

> +

> +.. _xsabin_xclbin_container_format:

> +

> +xsabin/xclbin Container Format

> +------------------------------

> +

> +xclbin/xsabin is ELF-like binary container format. It is structured as series of

> +sections. There is a file header followed by several section headers which is

> +followed by sections. A section header points to an actual section. There is an

> +optional signature at the end. The format is defined by header file ``xclbin.h``.

ok
> +The following figure illustrates a typical xclbin::

> +

> +

> +           +---------------------+

> +           |                     |

> +           |       HEADER        |

> +           +---------------------+

> +           |   SECTION  HEADER   |

> +           |                     |

> +           +---------------------+

> +           |         ...         |

> +           |                     |

> +           +---------------------+

> +           |   SECTION  HEADER   |

> +           |                     |

> +           +---------------------+

> +           |       SECTION       |

> +           |                     |

> +           +---------------------+

> +           |         ...         |

> +           |                     |

> +           +---------------------+

> +           |       SECTION       |

> +           |                     |

> +           +---------------------+

> +           |      SIGNATURE      |

> +           |      (OPTIONAL)     |

> +           +---------------------+

> +

> +

> +xclbin/xsabin files can be packaged, un-packaged and inspected using XRT utility

using a XRT utility
> +called **xclbinutil**. xclbinutil is part of XRT open source software stack. The

of the XRT
> +source code for xclbinutil can be found at

> +https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/xclbinutil

> +

> +For example to enumerate the contents of a xclbin/xsabin use the *--info* switch

> +as shown below::

> +

> +

> +  xclbinutil --info --input /opt/xilinx/firmware/u50/gen3x16-xdma/blp/test/bandwidth.xclbin

> +  xclbinutil --info --input /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/partition.xsabin

> +

> +

> +.. _device_tree_usage:

> +

> +Device Tree Usage

> +-----------------

> +

> +As mentioned previously xsabin stores metadata which advertise HW subsystems present

previously, xsabin
> +in a partition. The metadata is stored in device tree format with a well defined schema.

ok
> +XRT management driver uses this information to bind *platform drivers* to the subsystem

> +instantiations. The platform drivers are found in **xrt-lib.ko** kernel module defined

> +later.

> +

> +Logic UUID

> +^^^^^^^^^^

> +A partition is identified uniquely through ``logic_uuid`` property::

> +

> +  /dts-v1/;

> +  / {

> +      logic_uuid = "0123456789abcdef0123456789abcdef";

> +      ...

> +    }

> +

> +Schema Version

> +^^^^^^^^^^^^^^

> +Schema version is defined through ``schema_version`` node. And it contains ``major``

> +and ``minor`` properties as below::

> +

> +  /dts-v1/;

> +  / {

> +       schema_version {

> +           major = <0x01>;

> +           minor = <0x00>;

> +       };

> +       ...

> +    }

> +

> +.. _partition_uuids:

> +

> +Partition UUIDs

> +^^^^^^^^^^^^^^^

> +As mentioned earlier, each partition may have parent and child UUIDs. These UUIDs are

> +defined by ``interfaces`` node and ``interface_uuid`` property::

> +

> +  /dts-v1/;

> +  / {

> +       interfaces {

> +           @0 {

> +                  interface_uuid = "0123456789abcdef0123456789abcdef";

> +           };

> +           @1 {

> +                  interface_uuid = "fedcba9876543210fedcba9876543210";

> +           };

> +           ...

> +        };

> +       ...

> +    }

> +

> +

> +Subsystem Instantiations

> +^^^^^^^^^^^^^^^^^^^^^^^^

> +Subsystem instantiations are captured as children of ``addressable_endpoints``

> +node::

> +

> +  /dts-v1/;

> +  / {

> +       addressable_endpoints {

> +           abc {

> +               ...

> +           };

> +           def {

> +               ...

> +           };

> +           ...

> +       }

> +  }

> +

> +Subnode 'abc' and 'def' are the name of subsystem nodes

> +

> +Subsystem Node

> +^^^^^^^^^^^^^^

> +Each subsystem node and its properties define a hardware instance::

> +

> +

> +  addressable_endpoints {

> +      abc {

> +          reg = <0xa 0xb>

> +          pcie_physical_function = <0x0>;

> +          pcie_bar_mapping = <0x2>;

> +          compatible = "abc def";

> +          firmware {

> +              firmware_product_name = "abc"

> +              firmware_branch_name = "def"

> +              firmware_version_major = <1>

> +              firmware_version_minor = <2>

> +          };

> +      }

> +      ...

> +  }

> +

> +:reg:

> + Property defines address range. '<0xa 0xb>' is BAR offset and length pair, both


defines an address

is the BAR

> + are 64-bit integer.

integers
> +:pcie_physical_function:

> + Property specifies which PCIe physical function the subsystem node resides.

> +:pcie_bar_mapping:

> + Property specifies which PCIe BAR the subsystem node resides. '<0x2>' is BAR

> + index and it is 0 if this property is not defined.

index. A value of 0 means the property is not defined.
> +:compatible:

> + Property is a list of strings. The first string in the list specifies the exact

> + subsystem node. The following strings represent other devices that the device

> + is compatible with.

> +:firmware:

> + Subnode defines the firmware required by this subsystem node.

> +

> +Alveo U50 Platform Example

> +^^^^^^^^^^^^^^^^^^^^^^^^^^

> +::

> +

> +  /dts-v1/;

> +

> +  /{

> +        logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";

> +

> +        schema_version {

> +                major = <0x01>;

> +                minor = <0x00>;

> +        };

> +

> +        interfaces {

> +

> +                @0 {

> +                        interface_uuid = "862c7020a250293e32036f19956669e5";

> +                };

> +        };

> +

> +        addressable_endpoints {

> +

> +                ep_blp_rom_00 {

> +                        reg = <0x00 0x1f04000 0x00 0x1000>;

this is 4 values, not 2
> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";

> +                };

> +

> +                ep_card_flash_program_00 {

> +                        reg = <0x00 0x1f06000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_quad_spi-1.0\0axi_quad_spi";

> +                        interrupts = <0x03 0x03>;

interrupts not covered above
> +                };

> +

> +                ep_cmc_firmware_mem_00 {

> +                        reg = <0x00 0x1e20000 0x00 0x20000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";

> +

> +                        firmware {

> +                                firmware_product_name = "cmc";

> +                                firmware_branch_name = "u50";

> +                                firmware_version_major = <0x01>;

> +                                firmware_version_minor = <0x00>;

> +                        };

> +                };

> +

> +                ep_cmc_intc_00 {

> +                        reg = <0x00 0x1e03000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";

> +                        interrupts = <0x04 0x04>;

> +                };

> +

> +                ep_cmc_mutex_00 {

> +                        reg = <0x00 0x1e02000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_cmc_regmap_00 {

> +                        reg = <0x00 0x1e08000 0x00 0x2000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";

> +

> +                        firmware {

> +                                firmware_product_name = "sc-fw";

> +                                firmware_branch_name = "u50";

> +                                firmware_version_major = <0x05>;

> +                        };

> +                };

> +

> +                ep_cmc_reset_00 {

> +                        reg = <0x00 0x1e01000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_ddr_mem_calib_00 {

> +                        reg = <0x00 0x63000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_debug_bscan_mgmt_00 {

> +                        reg = <0x00 0x1e90000 0x00 0x10000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-debug_bridge-1.0\0debug_bridge";

> +                };

> +

> +                ep_ert_base_address_00 {

> +                        reg = <0x00 0x21000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_ert_command_queue_mgmt_00 {

> +                        reg = <0x00 0x40000 0x00 0x10000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";

> +                };

> +

> +                ep_ert_command_queue_user_00 {

> +                        reg = <0x00 0x40000 0x00 0x10000>;

> +                        pcie_physical_function = <0x01>;

> +                        compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";

> +                };

> +

> +                ep_ert_firmware_mem_00 {

> +                        reg = <0x00 0x30000 0x00 0x8000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";

> +

> +                        firmware {

> +                                firmware_product_name = "ert";

> +                                firmware_branch_name = "v20";

> +                                firmware_version_major = <0x01>;

> +                        };

> +                };

> +

> +                ep_ert_intc_00 {

> +                        reg = <0x00 0x23000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";

> +                        interrupts = <0x05 0x05>;

> +                };

> +

> +                ep_ert_reset_00 {

> +                        reg = <0x00 0x22000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_ert_sched_00 {

> +                        reg = <0x00 0x50000 0x00 0x1000>;

> +                        pcie_physical_function = <0x01>;

> +                        compatible = "xilinx.com,reg_abs-ert_sched-1.0\0ert_sched";

> +                        interrupts = <0x09 0x0c>;

> +                };

> +

> +                ep_fpga_configuration_00 {

> +                        reg = <0x00 0x1e88000 0x00 0x8000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_hwicap-1.0\0axi_hwicap";

> +                        interrupts = <0x02 0x02>;

> +                };

> +

> +                ep_icap_reset_00 {

> +                        reg = <0x00 0x1f07000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_msix_00 {

> +                        reg = <0x00 0x00 0x00 0x20000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-msix-1.0\0msix";

> +                        pcie_bar_mapping = <0x02>;

> +                };

> +

> +                ep_pcie_link_mon_00 {

> +                        reg = <0x00 0x1f05000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_pr_isolate_plp_00 {

> +                        reg = <0x00 0x1f01000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_pr_isolate_ulp_00 {

> +                        reg = <0x00 0x1000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";

> +                };

> +

> +                ep_uuid_rom_00 {

> +                        reg = <0x00 0x64000 0x00 0x1000>;

> +                        pcie_physical_function = <0x00>;

> +                        compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";

> +                };

> +

> +                ep_xdma_00 {

> +                        reg = <0x00 0x00 0x00 0x10000>;

> +                        pcie_physical_function = <0x01>;

> +                        compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";

> +                        pcie_bar_mapping = <0x02>;

> +                };

> +        };

> +

> +  }

> +

> +

> +

> +Deployment Models

> +=================

> +

> +Baremetal

> +---------

> +

> +In bare-metal deployments, both MPF and UPF are visible and accessible. xmgmt


ok

xmgnt -> xrt-mgmt

> +driver binds to MPF. xmgmt driver operations are privileged and available to

> +system administrator. The full stack is illustrated below::

> +

> +                            HOST

> +

> +                 [XMGMT]            [XUSER]

> +                    |                  |

> +                    |                  |

> +                 +-----+            +-----+

> +                 | MPF |            | UPF |

> +                 |     |            |     |

> +                 | PF0 |            | PF1 |

> +                 +--+--+            +--+--+

> +          ......... ^................. ^..........

> +                    |                  |

> +                    |   PCIe DEVICE    |

> +                    |                  |

> +                 +--+------------------+--+

> +                 |         SHELL          |

> +                 |                        |

> +                 +------------------------+

> +                 |         USER           |

> +                 |                        |

> +                 |                        |

> +                 |                        |

> +                 |                        |

> +                 +------------------------+

> +

> +

> +

> +Virtualized

> +-----------

> +

> +In virtualized deployments, privileged MPF is assigned to host but unprivileged


an article is needed to precede 'MPF' and 'UPF'  pick either 'a' or 'the'

Thanks for all the changes.

Tom

> +UPF is assigned to guest VM via PCIe pass-through. xmgmt driver in host binds

> +to MPF. xmgmt driver operations are privileged and only accessible to the MPF.

> +The full stack is illustrated below::

> +

> +

> +                                 .............

> +                  HOST           .    VM     .

> +                                 .           .

> +                 [XMGMT]         .  [XUSER]  .

> +                    |            .     |     .

> +                    |            .     |     .

> +                 +-----+         .  +-----+  .

> +                 | MPF |         .  | UPF |  .

> +                 |     |         .  |     |  .

> +                 | PF0 |         .  | PF1 |  .

> +                 +--+--+         .  +--+--+  .

> +          ......... ^................. ^..........

> +                    |                  |

> +                    |   PCIe DEVICE    |

> +                    |                  |

> +                 +--+------------------+--+

> +                 |         SHELL          |

> +                 |                        |

> +                 +------------------------+

> +                 |         USER           |

> +                 |                        |

> +                 |                        |

> +                 |                        |

> +                 |                        |

> +                 +------------------------+

> +

> +

> +

> +

> +

> +Platform Security Considerations

> +================================

> +

> +`Security of Alveo Platform <https://xilinx.github.io/XRT/master/html/security.html>`_

> +discusses the deployment options and security implications in great detail.
Tom Rix March 29, 2021, 5:12 p.m. UTC | #2
On 3/23/21 10:29 PM, Lizhi Hou wrote:
> Alveo FPGA firmware and partial reconfigure file are in xclbin format. This

> code enumerates and extracts sections from xclbin files. xclbin.h is cross

> platform and used across all platforms and OS.

ok
>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>  drivers/fpga/xrt/include/xclbin-helper.h |  48 +++

>  drivers/fpga/xrt/lib/xclbin.c            | 369 ++++++++++++++++++++

>  include/uapi/linux/xrt/xclbin.h          | 409 +++++++++++++++++++++++

>  3 files changed, 826 insertions(+)

>  create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h

>  create mode 100644 drivers/fpga/xrt/lib/xclbin.c

>  create mode 100644 include/uapi/linux/xrt/xclbin.h

>

> diff --git a/drivers/fpga/xrt/include/xclbin-helper.h b/drivers/fpga/xrt/include/xclbin-helper.h

> new file mode 100644

> index 000000000000..382b1de97b0a

> --- /dev/null

> +++ b/drivers/fpga/xrt/include/xclbin-helper.h

> @@ -0,0 +1,48 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *    David Zhang <davidzha@xilinx.com>

> + *    Sonal Santan <sonal.santan@xilinx.com>

> + */

> +

> +#ifndef _XCLBIN_HELPER_H_

> +#define _XCLBIN_HELPER_H_

ok
> +

> +#include <linux/types.h>

> +#include <linux/device.h>

> +#include <linux/xrt/xclbin.h>

> +

> +#define XCLBIN_VERSION2	"xclbin2"

> +#define XCLBIN_HWICAP_BITFILE_BUF_SZ 1024

> +#define XCLBIN_MAX_SIZE (1024 * 1024 * 1024) /* Assuming xclbin <= 1G, always */

ok
> +

> +enum axlf_section_kind;

> +struct axlf;

> +

> +/**

> + * Bitstream header information as defined by Xilinx tools.

> + * Please note that this struct definition is not owned by the driver.

> + */

> +struct xclbin_bit_head_info {

> +	u32 header_length;		/* Length of header in 32 bit words */

> +	u32 bitstream_length;		/* Length of bitstream to read in bytes */

> +	const unchar *design_name;	/* Design name get from bitstream */

> +	const unchar *part_name;	/* Part name read from bitstream */

> +	const unchar *date;		/* Date read from bitstream header */

> +	const unchar *time;		/* Bitstream creation time */

> +	u32 magic_length;		/* Length of the magic numbers */

> +	const unchar *version;		/* Version string */

> +};

> +

ok, bit removed.
> +/* caller must free the allocated memory for **data. len could be NULL. */

> +int xrt_xclbin_get_section(struct device *dev,  const struct axlf *xclbin,

> +			   enum axlf_section_kind kind, void **data,

> +			   uint64_t *len);


need to add comment that user must free data

need to add comment that len is optional

> +int xrt_xclbin_get_metadata(struct device *dev, const struct axlf *xclbin, char **dtb);

> +int xrt_xclbin_parse_bitstream_header(struct device *dev, const unchar *data,

> +				      u32 size, struct xclbin_bit_head_info *head_info);

> +const char *xrt_clock_type2epname(enum XCLBIN_CLOCK_TYPE type);

ok
> +

> +#endif /* _XCLBIN_HELPER_H_ */

> diff --git a/drivers/fpga/xrt/lib/xclbin.c b/drivers/fpga/xrt/lib/xclbin.c

> new file mode 100644

> index 000000000000..31b363c014a3

> --- /dev/null

> +++ b/drivers/fpga/xrt/lib/xclbin.c

> @@ -0,0 +1,369 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * Xilinx Alveo FPGA Driver XCLBIN parser

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors: David Zhang <davidzha@xilinx.com>

> + */

> +

> +#include <asm/errno.h>

> +#include <linux/vmalloc.h>

> +#include <linux/device.h>

> +#include "xclbin-helper.h"

> +#include "metadata.h"

> +

> +/* Used for parsing bitstream header */

> +#define BITSTREAM_EVEN_MAGIC_BYTE	0x0f

> +#define BITSTREAM_ODD_MAGIC_BYTE	0xf0

ok
> +

> +static int xrt_xclbin_get_section_hdr(const struct axlf *xclbin,

> +				      enum axlf_section_kind kind,

> +				      const struct axlf_section_header **header)

> +{

> +	const struct axlf_section_header *phead = NULL;

> +	u64 xclbin_len;

> +	int i;

> +

> +	*header = NULL;

> +	for (i = 0; i < xclbin->header.num_sections; i++) {

> +		if (xclbin->sections[i].section_kind == kind) {

> +			phead = &xclbin->sections[i];

> +			break;

> +		}

> +	}

> +

> +	if (!phead)

> +		return -ENOENT;

> +

> +	xclbin_len = xclbin->header.length;

> +	if (xclbin_len > XCLBIN_MAX_SIZE ||

> +	    phead->section_offset + phead->section_size > xclbin_len)

> +		return -EINVAL;

> +

> +	*header = phead;

> +	return 0;

> +}

> +

> +static int xrt_xclbin_section_info(const struct axlf *xclbin,

> +				   enum axlf_section_kind kind,

> +				   u64 *offset, u64 *size)

> +{

> +	const struct axlf_section_header *mem_header = NULL;

> +	int rc;

> +

> +	rc = xrt_xclbin_get_section_hdr(xclbin, kind, &mem_header);

> +	if (rc)

> +		return rc;

> +

> +	*offset = mem_header->section_offset;

> +	*size = mem_header->section_size;

ok
> +

> +	return 0;

> +}

> +

> +/* caller must free the allocated memory for **data */

> +int xrt_xclbin_get_section(struct device *dev,

> +			   const struct axlf *buf,

> +			   enum axlf_section_kind kind,

> +			   void **data, u64 *len)

> +{

> +	const struct axlf *xclbin = (const struct axlf *)buf;

> +	void *section = NULL;

> +	u64 offset = 0;

> +	u64 size = 0;

> +	int err = 0;

> +

> +	if (!data) {

ok
> +		dev_err(dev, "invalid data pointer");

> +		return -EINVAL;

> +	}

> +

> +	err = xrt_xclbin_section_info(xclbin, kind, &offset, &size);

> +	if (err) {

> +		dev_dbg(dev, "parsing section failed. kind %d, err = %d", kind, err);

> +		return err;

> +	}

> +

> +	section = vzalloc(size);

> +	if (!section)

> +		return -ENOMEM;

> +

> +	memcpy(section, ((const char *)xclbin) + offset, size);

> +

> +	*data = section;

> +	if (len)

> +		*len = size;

> +

> +	return 0;

> +}

> +EXPORT_SYMBOL_GPL(xrt_xclbin_get_section);

> +

> +static inline int xclbin_bit_get_string(const unchar *data, u32 size,

> +					u32 offset, unchar prefix,

> +					const unchar **str)

> +{

> +	int len;

> +	u32 tmp;

> +

> +	/* prefix and length will be 3 bytes */

> +	if (offset + 3  > size)

> +		return -EINVAL;

> +

> +	/* Read prefix */

> +	tmp = data[offset++];

> +	if (tmp != prefix)

> +		return -EINVAL;

> +

> +	/* Get string length */

> +	len = data[offset++];

> +	len = (len << 8) | data[offset++];

> +

> +	if (offset + len > size)

> +		return -EINVAL;

> +

> +	if (data[offset + len - 1] != '\0')

> +		return -EINVAL;

> +

> +	*str = data + offset;

> +

> +	return len + 3;

> +}

> +

> +/* parse bitstream header */

> +int xrt_xclbin_parse_bitstream_header(struct device *dev, const unchar *data,

> +				      u32 size, struct xclbin_bit_head_info *head_info)

> +{

> +	u32 offset = 0;

> +	int len, i;

> +	u16 magic;

> +

> +	memset(head_info, 0, sizeof(*head_info));

> +

> +	/* Get "Magic" length */

> +	if (size < sizeof(u16)) {

> +		dev_err(dev, "invalid size");

> +		return -EINVAL;

> +	}

ok
> +

> +	len = data[offset++];

> +	len = (len << 8) | data[offset++];

> +

> +	if (offset + len > size) {

> +		dev_err(dev, "invalid magic len");

> +		return -EINVAL;

> +	}

> +	head_info->magic_length = len;

> +

> +	for (i = 0; i < head_info->magic_length - 1; i++) {

> +		magic = data[offset++];

> +		if (!(i % 2) && magic != BITSTREAM_EVEN_MAGIC_BYTE) {

> +			dev_err(dev, "invalid magic even byte at %d", offset);

> +			return -EINVAL;

> +		}

> +

> +		if ((i % 2) && magic != BITSTREAM_ODD_MAGIC_BYTE) {

> +			dev_err(dev, "invalid magic odd byte at %d", offset);

> +			return -EINVAL;

> +		}

> +	}

> +

> +	if (offset + 3 > size) {

> +		dev_err(dev, "invalid length of magic end");

> +		return -EINVAL;

> +	}

> +	/* Read null end of magic data. */

> +	if (data[offset++]) {

> +		dev_err(dev, "invalid magic end");

> +		return -EINVAL;

> +	}

> +

> +	/* Read 0x01 (short) */

> +	magic = data[offset++];

> +	magic = (magic << 8) | data[offset++];

> +

> +	/* Check the "0x01" half word */

> +	if (magic != 0x01) {

> +		dev_err(dev, "invalid magic end");

> +		return -EINVAL;

> +	}

> +

> +	len = xclbin_bit_get_string(data, size, offset, 'a', &head_info->design_name);

> +	if (len < 0) {

> +		dev_err(dev, "get design name failed");

> +		return -EINVAL;

> +	}

> +

> +	head_info->version = strstr(head_info->design_name, "Version=") + strlen("Version=");

> +	offset += len;

> +

> +	len = xclbin_bit_get_string(data, size, offset, 'b', &head_info->part_name);

> +	if (len < 0) {

> +		dev_err(dev, "get part name failed");

> +		return -EINVAL;

> +	}

> +	offset += len;

> +

> +	len = xclbin_bit_get_string(data, size, offset, 'c', &head_info->date);

> +	if (len < 0) {

> +		dev_err(dev, "get data failed");

> +		return -EINVAL;

> +	}

> +	offset += len;

> +

> +	len = xclbin_bit_get_string(data, size, offset, 'd', &head_info->time);

> +	if (len < 0) {

> +		dev_err(dev, "get time failed");

> +		return -EINVAL;

> +	}

> +	offset += len;

> +

> +	if (offset + 5 >= size) {

> +		dev_err(dev, "can not get bitstream length");

> +		return -EINVAL;

> +	}

> +

> +	/* Read 'e' */

> +	if (data[offset++] != 'e') {

> +		dev_err(dev, "invalid prefix of bitstream length");

> +		return -EINVAL;

> +	}

> +

> +	/* Get byte length of bitstream */

> +	head_info->bitstream_length = data[offset++];

> +	head_info->bitstream_length = (head_info->bitstream_length << 8) | data[offset++];

> +	head_info->bitstream_length = (head_info->bitstream_length << 8) | data[offset++];

> +	head_info->bitstream_length = (head_info->bitstream_length << 8) | data[offset++];

OK
> +

> +	head_info->header_length = offset;

ok
> +

> +	return 0;

> +}

> +EXPORT_SYMBOL_GPL(xrt_xclbin_parse_bitstream_header);

ok, removed xrt_xclbin_free_header
> +

> +struct xrt_clock_desc {

> +	char	*clock_ep_name;

> +	u32	clock_xclbin_type;

> +	char	*clkfreq_ep_name;

> +} clock_desc[] = {

> +	{

> +		.clock_ep_name = XRT_MD_NODE_CLK_KERNEL1,

> +		.clock_xclbin_type = CT_DATA,

> +		.clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_K1,

> +	},

> +	{

> +		.clock_ep_name = XRT_MD_NODE_CLK_KERNEL2,

> +		.clock_xclbin_type = CT_KERNEL,

> +		.clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_K2,

> +	},

> +	{

> +		.clock_ep_name = XRT_MD_NODE_CLK_KERNEL3,

> +		.clock_xclbin_type = CT_SYSTEM,

> +		.clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_HBM,

> +	},

> +};

> +

> +const char *xrt_clock_type2epname(enum XCLBIN_CLOCK_TYPE type)

> +{

> +	int i;

> +

> +	for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {

> +		if (clock_desc[i].clock_xclbin_type == type)

> +			return clock_desc[i].clock_ep_name;

> +	}

> +	return NULL;

> +}

> +EXPORT_SYMBOL_GPL(xrt_clock_type2epname);

> +

> +static const char *clock_type2clkfreq_name(enum XCLBIN_CLOCK_TYPE type)

> +{

> +	int i;

> +

> +	for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {

> +		if (clock_desc[i].clock_xclbin_type == type)

> +			return clock_desc[i].clkfreq_ep_name;

> +	}

> +	return NULL;

> +}

> +

> +static int xrt_xclbin_add_clock_metadata(struct device *dev,

> +					 const struct axlf *xclbin,

> +					 char *dtb)

> +{

> +	struct clock_freq_topology *clock_topo;

> +	u16 freq;

> +	int rc;

> +	int i;

> +

> +	/* if clock section does not exist, add nothing and return success */

ok
> +	rc = xrt_xclbin_get_section(dev, xclbin, CLOCK_FREQ_TOPOLOGY,

> +				    (void **)&clock_topo, NULL);

> +	if (rc == -ENOENT)

> +		return 0;

> +	else if (rc)

> +		return rc;

> +

> +	for (i = 0; i < clock_topo->count; i++) {

> +		u8 type = clock_topo->clock_freq[i].type;

> +		const char *ep_name = xrt_clock_type2epname(type);

> +		const char *counter_name = clock_type2clkfreq_name(type);

> +

> +		if (!ep_name || !counter_name)

> +			continue;

> +

> +		freq = cpu_to_be16(clock_topo->clock_freq[i].freq_MHZ);

> +		rc = xrt_md_set_prop(dev, dtb, ep_name, NULL, XRT_MD_PROP_CLK_FREQ,

> +				     &freq, sizeof(freq));

> +		if (rc)

> +			break;

> +

> +		rc = xrt_md_set_prop(dev, dtb, ep_name, NULL, XRT_MD_PROP_CLK_CNT,

> +				     counter_name, strlen(counter_name) + 1);

> +		if (rc)

> +			break;

> +	}

> +

> +	vfree(clock_topo);

> +

> +	return rc;

> +}

> +

> +int xrt_xclbin_get_metadata(struct device *dev, const struct axlf *xclbin, char **dtb)

> +{

> +	char *md = NULL, *newmd = NULL;

> +	u64 len, md_len;

> +	int rc;

> +

> +	*dtb = NULL;

ok
> +

> +	rc = xrt_xclbin_get_section(dev, xclbin, PARTITION_METADATA, (void **)&md, &len);

> +	if (rc)

> +		goto done;

> +

> +	md_len = xrt_md_size(dev, md);

> +

> +	/* Sanity check the dtb section. */

> +	if (md_len > len) {

> +		rc = -EINVAL;

> +		goto done;

> +	}

> +

> +	/* use dup function here to convert incoming metadata to writable */

> +	newmd = xrt_md_dup(dev, md);

> +	if (!newmd) {

> +		rc = -EFAULT;

> +		goto done;

> +	}

> +

> +	/* Convert various needed xclbin sections into dtb. */

> +	rc = xrt_xclbin_add_clock_metadata(dev, xclbin, newmd);

> +

> +	if (!rc)

> +		*dtb = newmd;

> +	else

> +		vfree(newmd);

ok
> +done:

> +	vfree(md);

> +	return rc;

> +}

> +EXPORT_SYMBOL_GPL(xrt_xclbin_get_metadata);

> diff --git a/include/uapi/linux/xrt/xclbin.h b/include/uapi/linux/xrt/xclbin.h

> new file mode 100644

> index 000000000000..baa14d6653ab

> --- /dev/null

> +++ b/include/uapi/linux/xrt/xclbin.h

> @@ -0,0 +1,409 @@

> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

> +/*

> + *  Xilinx FPGA compiled binary container format

> + *

> + *  Copyright (C) 2015-2021, Xilinx Inc

> + */

> +

> +#ifndef _XCLBIN_H_

> +#define _XCLBIN_H_

ok, removed _WIN32_
> +

> +#if defined(__KERNEL__)

> +

> +#include <linux/types.h>

ok, removed uuid.h and version.h
> +

> +#elif defined(__cplusplus)

> +

> +#include <cstdlib>

> +#include <cstdint>

> +#include <algorithm>

> +#include <uuid/uuid.h>

> +

> +#else

> +

> +#include <stdlib.h>

> +#include <stdint.h>

> +#include <uuid/uuid.h>

> +

> +#endif

> +

> +#ifdef __cplusplus

> +extern "C" {

> +#endif

> +

> +/**

> + * DOC: Container format for Xilinx FPGA images

> + * The container stores bitstreams, metadata and firmware images.

> + * xclbin/xsabin is an ELF-like binary container format. It is a structured

ok
> + * series of sections. There is a file header followed by several section

> + * headers which is followed by sections. A section header points to an

> + * actual section. There is an optional signature at the end. The

> + * following figure illustrates a typical xclbin:

> + *

> + *     +---------------------+

> + *     |                     |

> + *     |       HEADER        |

> + *     +---------------------+

> + *     |   SECTION  HEADER   |

> + *     |                     |

> + *     +---------------------+

> + *     |        ...          |

> + *     |                     |

> + *     +---------------------+

> + *     |   SECTION  HEADER   |

> + *     |                     |

> + *     +---------------------+

> + *     |       SECTION       |

> + *     |                     |

> + *     +---------------------+

> + *     |         ...         |

> + *     |                     |

> + *     +---------------------+

> + *     |       SECTION       |

> + *     |                     |

> + *     +---------------------+

> + *     |      SIGNATURE      |

> + *     |      (OPTIONAL)     |

> + *     +---------------------+

ok on the tabs to spaces
> + */

> +

> +enum XCLBIN_MODE {

> +	XCLBIN_FLAT = 0,

ok
> +	XCLBIN_PR,

> +	XCLBIN_TANDEM_STAGE2,

> +	XCLBIN_TANDEM_STAGE2_WITH_PR,

> +	XCLBIN_HW_EMU,

> +	XCLBIN_SW_EMU,

> +	XCLBIN_MODE_MAX

> +};

> +

> +enum axlf_section_kind {

> +	BITSTREAM = 0,

> +	CLEARING_BITSTREAM,

> +	EMBEDDED_METADATA,

> +	FIRMWARE,

> +	DEBUG_DATA,

> +	SCHED_FIRMWARE,

> +	MEM_TOPOLOGY,

> +	CONNECTIVITY,

> +	IP_LAYOUT,

> +	DEBUG_IP_LAYOUT,

> +	DESIGN_CHECK_POINT,

> +	CLOCK_FREQ_TOPOLOGY,

> +	MCS,

> +	BMC,

> +	BUILD_METADATA,

> +	KEYVALUE_METADATA,

> +	USER_METADATA,

> +	DNA_CERTIFICATE,

> +	PDI,

> +	BITSTREAM_PARTIAL_PDI,

> +	PARTITION_METADATA,

> +	EMULATION_DATA,

> +	SYSTEM_METADATA,

> +	SOFT_KERNEL,

> +	ASK_FLASH,

> +	AIE_METADATA,

> +	ASK_GROUP_TOPOLOGY,

> +	ASK_GROUP_CONNECTIVITY

> +};

> +

> +enum MEM_TYPE {

> +	MEM_DDR3 = 0,

> +	MEM_DDR4,

> +	MEM_DRAM,

> +	MEM_STREAMING,

> +	MEM_PREALLOCATED_GLOB,

> +	MEM_ARE,

> +	MEM_HBM,

> +	MEM_BRAM,

> +	MEM_URAM,

> +	MEM_STREAMING_CONNECTION

> +};

> +

> +enum IP_TYPE {

> +	IP_MB = 0,

> +	IP_KERNEL,

> +	IP_DNASC,

> +	IP_DDR4_CONTROLLER,

> +	IP_MEM_DDR4,

> +	IP_MEM_HBM

> +};

> +

> +struct axlf_section_header {

> +	uint32_t section_kind;	    /* Section type */

> +	char section_name[16];	    /* Examples: "stage2", "clear1", */

> +				    /* "clear2", "ocl1", "ocl2, */

> +				    /* "ublaze", "sched" */

> +	char rsvd[4];

> +	uint64_t section_offset;    /* File offset of section data */

> +	uint64_t section_size;	    /* Size of section data */

> +} __packed;

> +

> +struct axlf_header {

> +	uint64_t length;		    /* Total size of the xclbin file */

> +	uint64_t time_stamp;		    /* Number of seconds since epoch */

> +					    /* when xclbin was created */

> +	uint64_t feature_rom_timestamp;     /* TimeSinceEpoch of the featureRom */

> +	uint16_t version_patch;	    /* Patch Version */

> +	uint8_t version_major;	    /* Major Version - Version: 2.1.0*/


ok, version checked

whitepace, needs '2.1.0 */'

I see this is a general problem, look other places.

maybe it is a 'tab' and the diff is messing it up, convert tab to space.

> +	uint8_t version_minor;	    /* Minor Version */

> +	uint32_t mode;		    /* XCLBIN_MODE */

> +	union {

> +		struct {

> +			uint64_t platform_id;	/* 64 bit platform ID: */

> +					/* vendor-device-subvendor-subdev */

> +			uint64_t feature_id;	/* 64 bit feature id */

> +		} rom;

> +		unsigned char rom_uuid[16];	/* feature ROM UUID for which */

> +						/* this xclbin was generated */

> +	};

> +	unsigned char platform_vbnv[64];	/* e.g. */

> +		/* xilinx:xil-accel-rd-ku115:4ddr-xpr:3.4: null terminated */

> +	union {

> +		char next_axlf[16];		/* Name of next xclbin file */

> +						/* in the daisy chain */

> +		unsigned char uuid[16];		/* uuid of this xclbin*/


ok

whitespace comment need a ' ' before */

> +	};

> +	char debug_bin[16];			/* Name of binary with debug */

> +						/* information */

> +	uint32_t num_sections;		/* Number of section headers */

> +	char rsvd[4];

> +} __packed;

> +

> +struct axlf {

> +	char magic[8];			/* Should be "xclbin2\0"  */

> +	int32_t signature_length;		/* Length of the signature. */

> +						/* -1 indicates no signature */

> +	unsigned char reserved[28];		/* Note: Initialized to 0xFFs */

> +

> +	unsigned char key_block[256];		/* Signature for validation */

> +						/* of binary */

> +	uint64_t unique_id;			/* axlf's uniqueId, use it to */

> +						/* skip redownload etc */

> +	struct axlf_header header;		/* Inline header */

> +	struct axlf_section_header sections[1];   /* One or more section */

> +						    /* headers follow */

> +} __packed;

ok, thanks!
> +

> +/* bitstream information */

> +struct xlnx_bitstream {

> +	uint8_t freq[8];

> +	char bits[1];

> +} __packed;

> +

> +/****	MEMORY TOPOLOGY SECTION ****/

> +struct mem_data {

> +	uint8_t type; /* enum corresponding to mem_type. */

> +	uint8_t used; /* if 0 this bank is not present */

> +	uint8_t rsvd[6];

> +	union {

> +		uint64_t size; /* if mem_type DDR, then size in KB; */

> +		uint64_t route_id; /* if streaming then "route_id" */

> +	};

> +	union {

> +		uint64_t base_address;/* if DDR then the base address; */

> +		uint64_t flow_id; /* if streaming then "flow id" */

> +	};

> +	unsigned char tag[16]; /* DDR: BANK0,1,2,3, has to be null */

> +			/* terminated; if streaming then stream0, 1 etc */

> +} __packed;

> +

> +struct mem_topology {

> +	int32_t count; /* Number of mem_data */

> +	struct mem_data mem_data[1]; /* Should be sorted on mem_type */

> +} __packed;

> +

> +/****	CONNECTIVITY SECTION ****/

> +/* Connectivity of each argument of CU(Compute Unit). It will be in terms

ok
> + * of argument index associated. For associating CU instances with arguments

> + * and banks, start at the connectivity section. Using the ip_layout_index

> + * access the ip_data.name. Now we can associate this CU instance with its

> + * original CU name and get the connectivity as well. This enables us to form

> + * related groups of CU instances.

> + */

> +

> +struct connection {

> +	int32_t arg_index; /* From 0 to n, may not be contiguous as scalars */

> +			   /* skipped */

> +	int32_t ip_layout_index; /* index into the ip_layout section. */

> +			   /* ip_layout.ip_data[index].type == IP_KERNEL */

> +	int32_t mem_data_index; /* index of the mem_data . Flag error is */

> +				/* used false. */

> +} __packed;

> +

> +struct connectivity {

> +	int32_t count;

> +	struct connection connection[1];

> +} __packed;

> +

> +/****	IP_LAYOUT SECTION ****/

> +

> +/* IP Kernel */

> +#define IP_INT_ENABLE_MASK	  0x0001

> +#define IP_INTERRUPT_ID_MASK  0x00FE

> +#define IP_INTERRUPT_ID_SHIFT 0x1

> +

> +enum IP_CONTROL {

> +	AP_CTRL_HS = 0,


ok

Thanks for the changes!

Tom

> +	AP_CTRL_CHAIN,

> +	AP_CTRL_NONE,

> +	AP_CTRL_ME,

> +	ACCEL_ADAPTER

> +};

> +

> +#define IP_CONTROL_MASK	 0xFF00

> +#define IP_CONTROL_SHIFT 0x8

> +

> +/* IPs on AXI lite - their types, names, and base addresses.*/

> +struct ip_data {

> +	uint32_t type; /* map to IP_TYPE enum */

> +	union {

> +		uint32_t properties; /* Default: 32-bits to indicate ip */

> +				     /* specific property. */

> +		/* type: IP_KERNEL

> +		 *	    int_enable   : Bit  - 0x0000_0001;

> +		 *	    interrupt_id : Bits - 0x0000_00FE;

> +		 *	    ip_control   : Bits = 0x0000_FF00;

> +		 */

> +		struct {		 /* type: IP_MEM_* */

> +			uint16_t index;

> +			uint8_t pc_index;

> +			uint8_t unused;

> +		} indices;

> +	};

> +	uint64_t base_address;

> +	uint8_t name[64]; /* eg Kernel name corresponding to KERNEL */

> +			    /* instance, can embed CU name in future. */

> +} __packed;

> +

> +struct ip_layout {

> +	int32_t count;

> +	struct ip_data ip_data[1]; /* All the ip_data needs to be sorted */

> +				     /* by base_address. */

> +} __packed;

> +

> +/*** Debug IP section layout ****/

> +enum DEBUG_IP_TYPE {

> +	UNDEFINED = 0,

> +	LAPC,

> +	ILA,

> +	AXI_MM_MONITOR,

> +	AXI_TRACE_FUNNEL,

> +	AXI_MONITOR_FIFO_LITE,

> +	AXI_MONITOR_FIFO_FULL,

> +	ACCEL_MONITOR,

> +	AXI_STREAM_MONITOR,

> +	AXI_STREAM_PROTOCOL_CHECKER,

> +	TRACE_S2MM,

> +	AXI_DMA,

> +	TRACE_S2MM_FULL

> +};

> +

> +struct debug_ip_data {

> +	uint8_t type; /* type of enum DEBUG_IP_TYPE */

> +	uint8_t index_lowbyte;

> +	uint8_t properties;

> +	uint8_t major;

> +	uint8_t minor;

> +	uint8_t index_highbyte;

> +	uint8_t reserved[2];

> +	uint64_t base_address;

> +	char	name[128];

> +} __packed;

> +

> +struct debug_ip_layout {

> +	uint16_t count;

> +	struct debug_ip_data debug_ip_data[1];

> +} __packed;

> +

> +/* Supported clock frequency types */

> +enum XCLBIN_CLOCK_TYPE {

> +	CT_UNUSED = 0,			   /* Initialized value */

> +	CT_DATA	  = 1,			   /* Data clock */

> +	CT_KERNEL = 2,			   /* Kernel clock */

> +	CT_SYSTEM = 3			   /* System Clock */

> +};

> +

> +/* Clock Frequency Entry */

> +struct clock_freq {

> +	uint16_t freq_MHZ;		   /* Frequency in MHz */

> +	uint8_t type;			   /* Clock type (enum CLOCK_TYPE) */

> +	uint8_t unused[5];		   /* Not used - padding */

> +	char name[128];			   /* Clock Name */

> +} __packed;

> +

> +/* Clock frequency section */

> +struct clock_freq_topology {

> +	int16_t count;		   /* Number of entries */

> +	struct clock_freq clock_freq[1]; /* Clock array */

> +} __packed;

> +

> +/* Supported MCS file types */

> +enum MCS_TYPE {

> +	MCS_UNKNOWN = 0,		   /* Initialized value */

> +	MCS_PRIMARY = 1,		   /* The primary mcs file data */

> +	MCS_SECONDARY = 2,		   /* The secondary mcs file data */

> +};

> +

> +/* One chunk of MCS data */

> +struct mcs_chunk {

> +	uint8_t type;			   /* MCS data type */

> +	uint8_t unused[7];		   /* padding */

> +	uint64_t offset;		   /* data offset from the start of */

> +					   /* the section */

> +	uint64_t size;		   /* data size */

> +} __packed;

> +

> +/* MCS data section */

> +struct mcs {

> +	int8_t count;			   /* Number of chunks */

> +	int8_t unused[7];		   /* padding */

> +	struct mcs_chunk chunk[1];	   /* MCS chunks followed by data */

> +} __packed;

> +

> +/* bmc data section */

> +struct bmc {

> +	uint64_t offset;		   /* data offset from the start of */

> +					   /* the section */

> +	uint64_t size;		   /* data size (bytes) */

> +	char image_name[64];		   /* Name of the image */

> +					   /* (e.g., MSP432P401R) */

> +	char device_name[64];		   /* Device ID	(e.g., VCU1525)	 */

> +	char version[64];

> +	char md5value[33];		   /* MD5 Expected Value */

> +				/* (e.g., 56027182079c0bd621761b7dab5a27ca)*/

> +	char padding[7];		   /* Padding */

> +} __packed;

> +

> +/* soft kernel data section, used by classic driver */

> +struct soft_kernel {

> +	/** Prefix Syntax:

> +	 *  mpo - member, pointer, offset

> +	 *  This variable represents a zero terminated string

> +	 *  that is offseted from the beginning of the section.

> +	 *  The pointer to access the string is initialized as follows:

> +	 *  char * pCharString = (address_of_section) + (mpo value)

> +	 */

> +	uint32_t mpo_name;	   /* Name of the soft kernel */

> +	uint32_t image_offset;   /* Image offset */

> +	uint32_t image_size;	   /* Image size */

> +	uint32_t mpo_version;	   /* Version */

> +	uint32_t mpo_md5_value;	   /* MD5 checksum */

> +	uint32_t mpo_symbol_name;  /* Symbol name */

> +	uint32_t num_instances;  /* Number of instances */

> +	uint8_t padding[36];	   /* Reserved for future use */

> +	uint8_t reserved_ext[16];   /* Reserved for future extended data */

> +} __packed;

> +

> +enum CHECKSUM_TYPE {

> +	CST_UNKNOWN = 0,

> +	CST_SDBM = 1,

> +	CST_LAST

> +};

> +

> +#ifdef __cplusplus

> +}

> +#endif

> +

> +#endif
Tom Rix March 30, 2021, 12:52 p.m. UTC | #3
On 3/23/21 10:29 PM, Lizhi Hou wrote:
> group driver that manages life cycle of a bunch of leaf driver instances

> and bridges them with root.

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>  drivers/fpga/xrt/include/group.h |  25 +++

>  drivers/fpga/xrt/lib/group.c     | 286 +++++++++++++++++++++++++++++++

>  2 files changed, 311 insertions(+)

>  create mode 100644 drivers/fpga/xrt/include/group.h

>  create mode 100644 drivers/fpga/xrt/lib/group.c

>

> diff --git a/drivers/fpga/xrt/include/group.h b/drivers/fpga/xrt/include/group.h

> new file mode 100644

> index 000000000000..09e9d03f53fe

> --- /dev/null

> +++ b/drivers/fpga/xrt/include/group.h

> @@ -0,0 +1,25 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

ok, removed generic boilerplate
> + * Authors:

> + *	Cheng Zhen <maxz@xilinx.com>

> + */

> +

> +#ifndef _XRT_GROUP_H_

> +#define _XRT_GROUP_H_

> +

> +#include "xleaf.h"

move header to another patch
> +

> +/*

> + * Group driver leaf calls.

ok
> + */

> +enum xrt_group_leaf_cmd {

> +	XRT_GROUP_GET_LEAF = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */

ok
> +	XRT_GROUP_PUT_LEAF,

> +	XRT_GROUP_INIT_CHILDREN,

> +	XRT_GROUP_FINI_CHILDREN,

> +	XRT_GROUP_TRIGGER_EVENT,

> +};

> +

> +#endif	/* _XRT_GROUP_H_ */

> diff --git a/drivers/fpga/xrt/lib/group.c b/drivers/fpga/xrt/lib/group.c

> new file mode 100644

> index 000000000000..7b8716569641

> --- /dev/null

> +++ b/drivers/fpga/xrt/lib/group.c

> @@ -0,0 +1,286 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * Xilinx Alveo FPGA Group Driver

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Cheng Zhen <maxz@xilinx.com>

> + */

> +

> +#include <linux/mod_devicetable.h>

> +#include <linux/platform_device.h>

> +#include "xleaf.h"

> +#include "subdev_pool.h"

> +#include "group.h"

> +#include "metadata.h"

> +#include "lib-drv.h"

> +

> +#define XRT_GRP "xrt_group"

> +

> +struct xrt_group {

> +	struct platform_device *pdev;

> +	struct xrt_subdev_pool leaves;

> +	bool leaves_created;

> +	struct mutex lock; /* lock for group */

> +};

> +

> +static int xrt_grp_root_cb(struct device *dev, void *parg,

> +			   enum xrt_root_cmd cmd, void *arg)

ok
> +{

> +	int rc;

> +	struct platform_device *pdev =

> +		container_of(dev, struct platform_device, dev);

> +	struct xrt_group *xg = (struct xrt_group *)parg;

> +

> +	switch (cmd) {

> +	case XRT_ROOT_GET_LEAF_HOLDERS: {

> +		struct xrt_root_get_holders *holders =

> +			(struct xrt_root_get_holders *)arg;

> +		rc = xrt_subdev_pool_get_holders(&xg->leaves,

> +						 holders->xpigh_pdev,

> +						 holders->xpigh_holder_buf,

> +						 holders->xpigh_holder_buf_len);

> +		break;

> +	}

> +	default:

> +		/* Forward parent call to root. */

> +		rc = xrt_subdev_root_request(pdev, cmd, arg);

> +		break;

> +	}

> +

> +	return rc;

> +}

> +

> +/*

> + * Cut subdev's dtb from group's dtb based on passed-in endpoint descriptor.

> + * Return the subdev's dtb through dtbp, if found.

> + */

> +static int xrt_grp_cut_subdev_dtb(struct xrt_group *xg, struct xrt_subdev_endpoints *eps,

> +				  char *grp_dtb, char **dtbp)

> +{

> +	int ret, i, ep_count = 0;

> +	char *dtb = NULL;

> +

> +	ret = xrt_md_create(DEV(xg->pdev), &dtb);

> +	if (ret)

> +		return ret;

> +

> +	for (i = 0; eps->xse_names[i].ep_name || eps->xse_names[i].regmap_name; i++) {

> +		const char *ep_name = eps->xse_names[i].ep_name;

> +		const char *reg_name = eps->xse_names[i].regmap_name;

> +

> +		if (!ep_name)

> +			xrt_md_get_compatible_endpoint(DEV(xg->pdev), grp_dtb, reg_name, &ep_name);

> +		if (!ep_name)

> +			continue;

> +

> +		ret = xrt_md_copy_endpoint(DEV(xg->pdev), dtb, grp_dtb, ep_name, reg_name, NULL);

> +		if (ret)

> +			continue;

> +		xrt_md_del_endpoint(DEV(xg->pdev), grp_dtb, ep_name, reg_name);

> +		ep_count++;

> +	}

> +	/* Found enough endpoints, return the subdev's dtb. */

> +	if (ep_count >= eps->xse_min_ep) {

> +		*dtbp = dtb;

> +		return 0;

> +	}

> +

> +	/* Cleanup - Restore all endpoints that has been deleted, if any. */

> +	if (ep_count > 0) {

> +		xrt_md_copy_endpoint(DEV(xg->pdev), grp_dtb, dtb,

> +				     XRT_MD_NODE_ENDPOINTS, NULL, NULL);

> +	}

> +	vfree(dtb);

> +	*dtbp = NULL;

> +	return 0;

> +}

> +

> +static int xrt_grp_create_leaves(struct xrt_group *xg)

> +{

> +	struct xrt_subdev_platdata *pdata = DEV_PDATA(xg->pdev);

> +	struct xrt_subdev_endpoints *eps = NULL;

> +	int ret = 0, failed = 0;

> +	enum xrt_subdev_id did;

> +	char *grp_dtb = NULL;

> +	unsigned long mlen;

> +

> +	if (!pdata)

> +		return -EINVAL;

ok
> +

> +	mlen = xrt_md_size(DEV(xg->pdev), pdata->xsp_dtb);

> +	if (mlen == XRT_MD_INVALID_LENGTH) {

> +		xrt_err(xg->pdev, "invalid dtb, len %ld", mlen);

> +		return -EINVAL;

> +	}

> +

> +	mutex_lock(&xg->lock);

> +

> +	if (xg->leaves_created) {

> +		mutex_unlock(&xg->lock);

add a comment that this is not an error and/or error is handled elsewhere
> +		return -EEXIST;

> +	}

> +

> +	grp_dtb = vmalloc(mlen);

> +	if (!grp_dtb) {

> +		mutex_unlock(&xg->lock);

> +		return -ENOMEM;

ok
> +	}

> +

> +	/* Create all leaves based on dtb. */

> +	xrt_info(xg->pdev, "bringing up leaves...");

> +	memcpy(grp_dtb, pdata->xsp_dtb, mlen);

> +	for (did = 0; did < XRT_SUBDEV_NUM; did++) {

ok
> +		eps = xrt_drv_get_endpoints(did);

> +		while (eps && eps->xse_names) {

> +			char *dtb = NULL;

> +

> +			ret = xrt_grp_cut_subdev_dtb(xg, eps, grp_dtb, &dtb);

> +			if (ret) {

> +				failed++;

> +				xrt_err(xg->pdev, "failed to cut subdev dtb for drv %s: %d",

> +					xrt_drv_name(did), ret);

> +			}

> +			if (!dtb) {

> +				/*

> +				 * No more dtb to cut or bad things happened for this instance,

> +				 * switch to the next one.

> +				 */

> +				eps++;

> +				continue;

> +			}

> +

> +			/* Found a dtb for this instance, let's add it. */

> +			ret = xrt_subdev_pool_add(&xg->leaves, did, xrt_grp_root_cb, xg, dtb);

> +			if (ret < 0) {

> +				failed++;

> +				xrt_err(xg->pdev, "failed to add %s: %d", xrt_drv_name(did), ret);


add a comment that this is not a fatal error and cleanup happens elsewhere

Tom

> +			}

> +			vfree(dtb);

> +			/* Continue searching for the same instance from grp_dtb. */

> +		}

> +	}

> +

> +	xg->leaves_created = true;

> +	vfree(grp_dtb);

> +	mutex_unlock(&xg->lock);

> +	return failed == 0 ? 0 : -ECHILD;

> +}

> +

> +static void xrt_grp_remove_leaves(struct xrt_group *xg)

> +{

> +	mutex_lock(&xg->lock);

> +

> +	if (!xg->leaves_created) {

> +		mutex_unlock(&xg->lock);

> +		return;

> +	}

> +

> +	xrt_info(xg->pdev, "tearing down leaves...");

> +	xrt_subdev_pool_fini(&xg->leaves);

> +	xg->leaves_created = false;

> +

> +	mutex_unlock(&xg->lock);

> +}

> +

> +static int xrt_grp_probe(struct platform_device *pdev)

> +{

> +	struct xrt_group *xg;

> +

> +	xrt_info(pdev, "probing...");

> +

> +	xg = devm_kzalloc(&pdev->dev, sizeof(*xg), GFP_KERNEL);

> +	if (!xg)

> +		return -ENOMEM;

> +

> +	xg->pdev = pdev;

> +	mutex_init(&xg->lock);

> +	xrt_subdev_pool_init(DEV(pdev), &xg->leaves);

> +	platform_set_drvdata(pdev, xg);

> +

> +	return 0;

> +}

> +

> +static int xrt_grp_remove(struct platform_device *pdev)

> +{

> +	struct xrt_group *xg = platform_get_drvdata(pdev);

> +

> +	xrt_info(pdev, "leaving...");

> +	xrt_grp_remove_leaves(xg);

> +	return 0;

> +}

> +

> +static int xrt_grp_leaf_call(struct platform_device *pdev, u32 cmd, void *arg)

> +{

> +	int rc = 0;

> +	struct xrt_group *xg = platform_get_drvdata(pdev);

> +

> +	switch (cmd) {

> +	case XRT_XLEAF_EVENT:

> +		/* Simply forward to every child. */

> +		xrt_subdev_pool_handle_event(&xg->leaves,

> +					     (struct xrt_event *)arg);

> +		break;

> +	case XRT_GROUP_GET_LEAF: {

> +		struct xrt_root_get_leaf *get_leaf =

> +			(struct xrt_root_get_leaf *)arg;

> +

> +		rc = xrt_subdev_pool_get(&xg->leaves, get_leaf->xpigl_match_cb,

> +					 get_leaf->xpigl_match_arg,

> +					 DEV(get_leaf->xpigl_caller_pdev),

> +					 &get_leaf->xpigl_tgt_pdev);

> +		break;

> +	}

> +	case XRT_GROUP_PUT_LEAF: {

> +		struct xrt_root_put_leaf *put_leaf =

> +			(struct xrt_root_put_leaf *)arg;

> +

> +		rc = xrt_subdev_pool_put(&xg->leaves, put_leaf->xpipl_tgt_pdev,

> +					 DEV(put_leaf->xpipl_caller_pdev));

> +		break;

> +	}

> +	case XRT_GROUP_INIT_CHILDREN:

> +		rc = xrt_grp_create_leaves(xg);

> +		break;

> +	case XRT_GROUP_FINI_CHILDREN:

> +		xrt_grp_remove_leaves(xg);

> +		break;

> +	case XRT_GROUP_TRIGGER_EVENT:

> +		xrt_subdev_pool_trigger_event(&xg->leaves, (enum xrt_events)(uintptr_t)arg);

> +		break;

> +	default:

> +		xrt_err(pdev, "unknown IOCTL cmd %d", cmd);

> +		rc = -EINVAL;

> +		break;

> +	}

> +	return rc;

> +}

> +

> +static struct xrt_subdev_drvdata xrt_grp_data = {

> +	.xsd_dev_ops = {

> +		.xsd_leaf_call = xrt_grp_leaf_call,

> +	},

> +};

> +

> +static const struct platform_device_id xrt_grp_id_table[] = {

> +	{ XRT_GRP, (kernel_ulong_t)&xrt_grp_data },

> +	{ },

> +};

> +

> +static struct platform_driver xrt_group_driver = {

> +	.driver	= {

> +		.name    = XRT_GRP,

> +	},

> +	.probe   = xrt_grp_probe,

> +	.remove  = xrt_grp_remove,

> +	.id_table = xrt_grp_id_table,

> +};

> +

> +void group_leaf_init_fini(bool init)

> +{

> +	if (init)

> +		xleaf_register_driver(XRT_SUBDEV_GRP, &xrt_group_driver, NULL);

> +	else

> +		xleaf_unregister_driver(XRT_SUBDEV_GRP);

> +}
Tom Rix March 30, 2021, 3:11 p.m. UTC | #4
This was split from 'fpga: xrt: platform driver infrastructure'

and fpga: xrt: managment physical function driver (root)


On 3/23/21 10:29 PM, Lizhi Hou wrote:
> Contains common code for all root drivers and handles root calls from

> platform drivers. This is part of root driver infrastructure.

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>  drivers/fpga/xrt/include/events.h  |  45 +++

>  drivers/fpga/xrt/include/xroot.h   | 117 ++++++

>  drivers/fpga/xrt/lib/subdev_pool.h |  53 +++

>  drivers/fpga/xrt/lib/xroot.c       | 589 +++++++++++++++++++++++++++++

>  4 files changed, 804 insertions(+)

>  create mode 100644 drivers/fpga/xrt/include/events.h

>  create mode 100644 drivers/fpga/xrt/include/xroot.h

>  create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h

>  create mode 100644 drivers/fpga/xrt/lib/xroot.c

>

> diff --git a/drivers/fpga/xrt/include/events.h b/drivers/fpga/xrt/include/events.h

> new file mode 100644

> index 000000000000..775171a47c8e

> --- /dev/null

> +++ b/drivers/fpga/xrt/include/events.h

> @@ -0,0 +1,45 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

ok
> + *

> + * Authors:

> + *	Cheng Zhen <maxz@xilinx.com>

> + */

> +

> +#ifndef _XRT_EVENTS_H_

> +#define _XRT_EVENTS_H_

ok
> +

> +#include "subdev_id.h"

> +

> +/*

> + * Event notification.

> + */

> +enum xrt_events {

> +	XRT_EVENT_TEST = 0, /* for testing */

> +	/*

> +	 * Events related to specific subdev

> +	 * Callback arg: struct xrt_event_arg_subdev

> +	 */

> +	XRT_EVENT_POST_CREATION,

> +	XRT_EVENT_PRE_REMOVAL,

> +	/*

> +	 * Events related to change of the whole board

> +	 * Callback arg: <none>

> +	 */

> +	XRT_EVENT_PRE_HOT_RESET,

> +	XRT_EVENT_POST_HOT_RESET,

> +	XRT_EVENT_PRE_GATE_CLOSE,

> +	XRT_EVENT_POST_GATE_OPEN,

> +};

> +

> +struct xrt_event_arg_subdev {

> +	enum xrt_subdev_id xevt_subdev_id;

> +	int xevt_subdev_instance;

> +};

> +

> +struct xrt_event {

> +	enum xrt_events xe_evt;

> +	struct xrt_event_arg_subdev xe_subdev;

> +};

> +

> +#endif	/* _XRT_EVENTS_H_ */

> diff --git a/drivers/fpga/xrt/include/xroot.h b/drivers/fpga/xrt/include/xroot.h

> new file mode 100644

> index 000000000000..91c0aeb30bf8

> --- /dev/null

> +++ b/drivers/fpga/xrt/include/xroot.h

> @@ -0,0 +1,117 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Cheng Zhen <maxz@xilinx.com>

> + */

> +

> +#ifndef _XRT_ROOT_H_

> +#define _XRT_ROOT_H_

> +

> +#include <linux/platform_device.h>

> +#include <linux/pci.h>

> +#include "subdev_id.h"

> +#include "events.h"

> +

> +typedef bool (*xrt_subdev_match_t)(enum xrt_subdev_id,

> +	struct platform_device *, void *);

> +#define XRT_SUBDEV_MATCH_PREV	((xrt_subdev_match_t)-1)

> +#define XRT_SUBDEV_MATCH_NEXT	((xrt_subdev_match_t)-2)

> +

> +/*

> + * Root calls.

> + */

> +enum xrt_root_cmd {

> +	/* Leaf actions. */

> +	XRT_ROOT_GET_LEAF = 0,

> +	XRT_ROOT_PUT_LEAF,

> +	XRT_ROOT_GET_LEAF_HOLDERS,

> +

> +	/* Group actions. */

> +	XRT_ROOT_CREATE_GROUP,

> +	XRT_ROOT_REMOVE_GROUP,

> +	XRT_ROOT_LOOKUP_GROUP,

> +	XRT_ROOT_WAIT_GROUP_BRINGUP,

> +

> +	/* Event actions. */

> +	XRT_ROOT_EVENT_SYNC,

> +	XRT_ROOT_EVENT_ASYNC,

> +

> +	/* Device info. */

> +	XRT_ROOT_GET_RESOURCE,

> +	XRT_ROOT_GET_ID,

> +

> +	/* Misc. */

> +	XRT_ROOT_HOT_RESET,

> +	XRT_ROOT_HWMON,

> +};

> +

> +struct xrt_root_get_leaf {

> +	struct platform_device *xpigl_caller_pdev;

> +	xrt_subdev_match_t xpigl_match_cb;

> +	void *xpigl_match_arg;

> +	struct platform_device *xpigl_tgt_pdev;

> +};

> +

> +struct xrt_root_put_leaf {

> +	struct platform_device *xpipl_caller_pdev;

> +	struct platform_device *xpipl_tgt_pdev;

> +};

> +

> +struct xrt_root_lookup_group {

> +	struct platform_device *xpilp_pdev; /* caller's pdev */

> +	xrt_subdev_match_t xpilp_match_cb;

> +	void *xpilp_match_arg;

> +	int xpilp_grp_inst;

> +};

> +

> +struct xrt_root_get_holders {

> +	struct platform_device *xpigh_pdev; /* caller's pdev */

> +	char *xpigh_holder_buf;

> +	size_t xpigh_holder_buf_len;

> +};

> +

> +struct xrt_root_get_res {

> +	struct resource *xpigr_res;

> +};

> +

> +struct xrt_root_get_id {

> +	unsigned short  xpigi_vendor_id;

> +	unsigned short  xpigi_device_id;

> +	unsigned short  xpigi_sub_vendor_id;

> +	unsigned short  xpigi_sub_device_id;

> +};

> +

> +struct xrt_root_hwmon {

> +	bool xpih_register;

> +	const char *xpih_name;

> +	void *xpih_drvdata;

> +	const struct attribute_group **xpih_groups;

> +	struct device *xpih_hwmon_dev;

> +};

> +

> +/*

> + * Callback for leaf to make a root request. Arguments are: parent device, parent cookie, req,

> + * and arg.

> + */

> +typedef int (*xrt_subdev_root_cb_t)(struct device *, void *, u32, void *);

> +int xrt_subdev_root_request(struct platform_device *self, u32 cmd, void *arg);

> +

> +/*

> + * Defines physical function (MPF / UPF) specific operations

> + * needed in common root driver.

> + */

> +struct xroot_physical_function_callback {

> +	void (*xpc_hot_reset)(struct pci_dev *pdev);

> +};

> +

> +int xroot_probe(struct pci_dev *pdev, struct xroot_physical_function_callback *cb, void **root);

> +void xroot_remove(void *root);

> +bool xroot_wait_for_bringup(void *root);

> +int xroot_add_vsec_node(void *root, char *dtb);

> +int xroot_create_group(void *xr, char *dtb);

> +int xroot_add_simple_node(void *root, char *dtb, const char *endpoint);

> +void xroot_broadcast(void *root, enum xrt_events evt);

> +

> +#endif	/* _XRT_ROOT_H_ */

> diff --git a/drivers/fpga/xrt/lib/subdev_pool.h b/drivers/fpga/xrt/lib/subdev_pool.h

> new file mode 100644

> index 000000000000..09d148e4e7ea

> --- /dev/null

> +++ b/drivers/fpga/xrt/lib/subdev_pool.h

> @@ -0,0 +1,53 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Cheng Zhen <maxz@xilinx.com>

> + */

> +

> +#ifndef _XRT_SUBDEV_POOL_H_

> +#define _XRT_SUBDEV_POOL_H_

> +

> +#include <linux/device.h>

> +#include <linux/mutex.h>

> +#include "xroot.h"

> +

> +/*

> + * The struct xrt_subdev_pool manages a list of xrt_subdevs for root and group drivers.

> + */

> +struct xrt_subdev_pool {

> +	struct list_head xsp_dev_list;

> +	struct device *xsp_owner;

> +	struct mutex xsp_lock; /* pool lock */

> +	bool xsp_closing;

> +};

> +

> +/*

> + * Subdev pool helper functions for root and group drivers only.

> + */

> +void xrt_subdev_pool_init(struct device *dev,

> +			  struct xrt_subdev_pool *spool);

> +void xrt_subdev_pool_fini(struct xrt_subdev_pool *spool);

> +int xrt_subdev_pool_get(struct xrt_subdev_pool *spool,

> +			xrt_subdev_match_t match,

> +			void *arg, struct device *holder_dev,

> +			struct platform_device **pdevp);

> +int xrt_subdev_pool_put(struct xrt_subdev_pool *spool,

> +			struct platform_device *pdev,

> +			struct device *holder_dev);

> +int xrt_subdev_pool_add(struct xrt_subdev_pool *spool,

> +			enum xrt_subdev_id id, xrt_subdev_root_cb_t pcb,

> +			void *pcb_arg, char *dtb);

> +int xrt_subdev_pool_del(struct xrt_subdev_pool *spool,

> +			enum xrt_subdev_id id, int instance);

> +ssize_t xrt_subdev_pool_get_holders(struct xrt_subdev_pool *spool,

> +				    struct platform_device *pdev,

> +				    char *buf, size_t len);

> +

> +void xrt_subdev_pool_trigger_event(struct xrt_subdev_pool *spool,

> +				   enum xrt_events evt);

> +void xrt_subdev_pool_handle_event(struct xrt_subdev_pool *spool,

> +				  struct xrt_event *evt);

> +

> +#endif	/* _XRT_SUBDEV_POOL_H_ */

> diff --git a/drivers/fpga/xrt/lib/xroot.c b/drivers/fpga/xrt/lib/xroot.c

> new file mode 100644

> index 000000000000..03407272650f

> --- /dev/null

> +++ b/drivers/fpga/xrt/lib/xroot.c

> @@ -0,0 +1,589 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * Xilinx Alveo FPGA Root Functions

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Cheng Zhen <maxz@xilinx.com>

> + */

> +

> +#include <linux/module.h>

> +#include <linux/pci.h>

> +#include <linux/hwmon.h>

> +#include "xroot.h"

> +#include "subdev_pool.h"

> +#include "group.h"

> +#include "metadata.h"

> +

> +#define XROOT_PDEV(xr)		((xr)->pdev)

> +#define XROOT_DEV(xr)		(&(XROOT_PDEV(xr)->dev))

> +#define xroot_err(xr, fmt, args...)	\

> +	dev_err(XROOT_DEV(xr), "%s: " fmt, __func__, ##args)

> +#define xroot_warn(xr, fmt, args...)	\

> +	dev_warn(XROOT_DEV(xr), "%s: " fmt, __func__, ##args)

> +#define xroot_info(xr, fmt, args...)	\

> +	dev_info(XROOT_DEV(xr), "%s: " fmt, __func__, ##args)

> +#define xroot_dbg(xr, fmt, args...)	\

> +	dev_dbg(XROOT_DEV(xr), "%s: " fmt, __func__, ##args)

> +

> +#define XRT_VSEC_ID		0x20

> +


'root' is an abstraction, 'pci' is an implementation.

Consider splitting.

I think this will be part of the pseudo bus, so figure out how to do root there.


> +#define XROOT_GROUP_FIRST		(-1)

> +#define XROOT_GROUP_LAST		(-2)

> +

> +static int xroot_root_cb(struct device *, void *, u32, void *);

> +

> +struct xroot_evt {

> +	struct list_head list;

> +	struct xrt_event evt;

> +	struct completion comp;

> +	bool async;

> +};

> +

> +struct xroot_events {

> +	struct mutex evt_lock; /* event lock */

> +	struct list_head evt_list;

> +	struct work_struct evt_work;

> +};

> +

> +struct xroot_groups {

> +	struct xrt_subdev_pool pool;

> +	struct work_struct bringup_work;

add a comment that these two elements are counters or append '_cnt' or similar to name
> +	atomic_t bringup_pending;

> +	atomic_t bringup_failed;

> +	struct completion bringup_comp;

> +};

> +

> +struct xroot {

> +	struct pci_dev *pdev;

> +	struct xroot_events events;

> +	struct xroot_groups groups;

> +	struct xroot_physical_function_callback pf_cb;

ok
> +};

> +

> +struct xroot_group_match_arg {

> +	enum xrt_subdev_id id;

> +	int instance;

> +};

> +

> +static bool xroot_group_match(enum xrt_subdev_id id, struct platform_device *pdev, void *arg)

> +{

> +	struct xroot_group_match_arg *a = (struct xroot_group_match_arg *)arg;

> +

> +	/* pdev->id is the instance of the subdev. */

ok
> +	return id == a->id && pdev->id == a->instance;

> +}

> +

> +static int xroot_get_group(struct xroot *xr, int instance, struct platform_device **grpp)

> +{

> +	int rc = 0;

> +	struct xrt_subdev_pool *grps = &xr->groups.pool;

> +	struct device *dev = DEV(xr->pdev);

> +	struct xroot_group_match_arg arg = { XRT_SUBDEV_GRP, instance };

> +

> +	if (instance == XROOT_GROUP_LAST) {

> +		rc = xrt_subdev_pool_get(grps, XRT_SUBDEV_MATCH_NEXT,

> +					 *grpp, dev, grpp);

> +	} else if (instance == XROOT_GROUP_FIRST) {

> +		rc = xrt_subdev_pool_get(grps, XRT_SUBDEV_MATCH_PREV,

> +					 *grpp, dev, grpp);

> +	} else {

> +		rc = xrt_subdev_pool_get(grps, xroot_group_match,

> +					 &arg, dev, grpp);

> +	}

> +

> +	if (rc && rc != -ENOENT)

> +		xroot_err(xr, "failed to hold group %d: %d", instance, rc);

> +	return rc;

> +}

> +

> +static void xroot_put_group(struct xroot *xr, struct platform_device *grp)

> +{

> +	int inst = grp->id;

> +	int rc = xrt_subdev_pool_put(&xr->groups.pool, grp, DEV(xr->pdev));

> +

> +	if (rc)

> +		xroot_err(xr, "failed to release group %d: %d", inst, rc);

> +}

> +

> +static int xroot_trigger_event(struct xroot *xr, struct xrt_event *e, bool async)

> +{

> +	struct xroot_evt *enew = vzalloc(sizeof(*enew));

> +

> +	if (!enew)

> +		return -ENOMEM;

> +

> +	enew->evt = *e;

> +	enew->async = async;

> +	init_completion(&enew->comp);

> +

> +	mutex_lock(&xr->events.evt_lock);

> +	list_add(&enew->list, &xr->events.evt_list);

> +	mutex_unlock(&xr->events.evt_lock);

> +

> +	schedule_work(&xr->events.evt_work);

> +

> +	if (async)

> +		return 0;

> +

> +	wait_for_completion(&enew->comp);

> +	vfree(enew);

> +	return 0;

> +}

> +

> +static void

> +xroot_group_trigger_event(struct xroot *xr, int inst, enum xrt_events e)

> +{

> +	int ret;

> +	struct platform_device *pdev = NULL;

> +	struct xrt_event evt = { 0 };

> +

> +	WARN_ON(inst < 0);

> +	/* Only triggers subdev specific events. */

> +	if (e != XRT_EVENT_POST_CREATION && e != XRT_EVENT_PRE_REMOVAL) {

> +		xroot_err(xr, "invalid event %d", e);

> +		return;

> +	}

> +

> +	ret = xroot_get_group(xr, inst, &pdev);

> +	if (ret)

> +		return;

> +

> +	/* Triggers event for children, first. */

> +	xleaf_call(pdev, XRT_GROUP_TRIGGER_EVENT, (void *)(uintptr_t)e);

ok
> +

> +	/* Triggers event for itself. */

> +	evt.xe_evt = e;

> +	evt.xe_subdev.xevt_subdev_id = XRT_SUBDEV_GRP;

> +	evt.xe_subdev.xevt_subdev_instance = inst;

> +	xroot_trigger_event(xr, &evt, false);

> +

> +	xroot_put_group(xr, pdev);

> +}

> +

> +int xroot_create_group(void *root, char *dtb)

> +{

> +	struct xroot *xr = (struct xroot *)root;

> +	int ret;

> +

> +	atomic_inc(&xr->groups.bringup_pending);

> +	ret = xrt_subdev_pool_add(&xr->groups.pool, XRT_SUBDEV_GRP, xroot_root_cb, xr, dtb);

> +	if (ret >= 0) {

> +		schedule_work(&xr->groups.bringup_work);

> +	} else {

> +		atomic_dec(&xr->groups.bringup_pending);

> +		atomic_inc(&xr->groups.bringup_failed);

> +		xroot_err(xr, "failed to create group: %d", ret);

> +	}

> +	return ret;

> +}

> +EXPORT_SYMBOL_GPL(xroot_create_group);

> +

> +static int xroot_destroy_single_group(struct xroot *xr, int instance)

> +{

ok as-is
> +	struct platform_device *pdev = NULL;

> +	int ret;

> +

> +	WARN_ON(instance < 0);

> +	ret = xroot_get_group(xr, instance, &pdev);

> +	if (ret)

> +		return ret;

> +

> +	xroot_group_trigger_event(xr, instance, XRT_EVENT_PRE_REMOVAL);

> +

> +	/* Now tear down all children in this group. */

> +	ret = xleaf_call(pdev, XRT_GROUP_FINI_CHILDREN, NULL);

> +	xroot_put_group(xr, pdev);

> +	if (!ret)

> +		ret = xrt_subdev_pool_del(&xr->groups.pool, XRT_SUBDEV_GRP, instance);

> +

> +	return ret;

> +}

> +

> +static int xroot_destroy_group(struct xroot *xr, int instance)

> +{

> +	struct platform_device *target = NULL;

> +	struct platform_device *deps = NULL;

> +	int ret;

> +

> +	WARN_ON(instance < 0);

> +	/*

> +	 * Make sure target group exists and can't go away before

> +	 * we remove it's dependents

> +	 */

> +	ret = xroot_get_group(xr, instance, &target);

> +	if (ret)

> +		return ret;

> +

> +	/*

> +	 * Remove all groups depend on target one.

> +	 * Assuming subdevs in higher group ID can depend on ones in

> +	 * lower ID groups, we remove them in the reservse order.

> +	 */

> +	while (xroot_get_group(xr, XROOT_GROUP_LAST, &deps) != -ENOENT) {

> +		int inst = deps->id;

> +

> +		xroot_put_group(xr, deps);

> +		/* Reached the target group instance, stop here. */

ok
> +		if (instance == inst)

> +			break;

> +		xroot_destroy_single_group(xr, inst);

> +		deps = NULL;

> +	}

> +

> +	/* Now we can remove the target group. */

> +	xroot_put_group(xr, target);

> +	return xroot_destroy_single_group(xr, instance);

> +}

> +

> +static int xroot_lookup_group(struct xroot *xr,

> +			      struct xrt_root_lookup_group *arg)

> +{

> +	int rc = -ENOENT;

> +	struct platform_device *grp = NULL;

> +

> +	while (rc < 0 && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {

> +		if (arg->xpilp_match_cb(XRT_SUBDEV_GRP, grp, arg->xpilp_match_arg))

> +			rc = grp->id;

> +		xroot_put_group(xr, grp);

> +	}

> +	return rc;

> +}

> +

> +static void xroot_event_work(struct work_struct *work)

> +{

> +	struct xroot_evt *tmp;

> +	struct xroot *xr = container_of(work, struct xroot, events.evt_work);

> +

> +	mutex_lock(&xr->events.evt_lock);

> +	while (!list_empty(&xr->events.evt_list)) {

> +		tmp = list_first_entry(&xr->events.evt_list, struct xroot_evt, list);

> +		list_del(&tmp->list);

> +		mutex_unlock(&xr->events.evt_lock);

> +

> +		xrt_subdev_pool_handle_event(&xr->groups.pool, &tmp->evt);

> +

> +		if (tmp->async)

> +			vfree(tmp);

> +		else

> +			complete(&tmp->comp);

> +

> +		mutex_lock(&xr->events.evt_lock);

> +	}

> +	mutex_unlock(&xr->events.evt_lock);

> +}

> +

> +static void xroot_event_init(struct xroot *xr)

> +{

> +	INIT_LIST_HEAD(&xr->events.evt_list);

> +	mutex_init(&xr->events.evt_lock);

> +	INIT_WORK(&xr->events.evt_work, xroot_event_work);

> +}

> +

> +static void xroot_event_fini(struct xroot *xr)

> +{

> +	flush_scheduled_work();

> +	WARN_ON(!list_empty(&xr->events.evt_list));

> +}

> +

> +static int xroot_get_leaf(struct xroot *xr, struct xrt_root_get_leaf *arg)

> +{

> +	int rc = -ENOENT;

> +	struct platform_device *grp = NULL;

> +

> +	while (rc && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {

> +		rc = xleaf_call(grp, XRT_GROUP_GET_LEAF, arg);

> +		xroot_put_group(xr, grp);

> +	}

> +	return rc;

> +}

> +

> +static int xroot_put_leaf(struct xroot *xr, struct xrt_root_put_leaf *arg)

> +{

> +	int rc = -ENOENT;

> +	struct platform_device *grp = NULL;

> +

> +	while (rc && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {

> +		rc = xleaf_call(grp, XRT_GROUP_PUT_LEAF, arg);

> +		xroot_put_group(xr, grp);

> +	}

> +	return rc;

> +}

> +

> +static int xroot_root_cb(struct device *dev, void *parg, enum xrt_root_cmd cmd, void *arg)

> +{

> +	struct xroot *xr = (struct xroot *)parg;

> +	int rc = 0;

> +

> +	switch (cmd) {

> +	/* Leaf actions. */

> +	case XRT_ROOT_GET_LEAF: {

> +		struct xrt_root_get_leaf *getleaf = (struct xrt_root_get_leaf *)arg;

> +

> +		rc = xroot_get_leaf(xr, getleaf);

> +		break;

> +	}

> +	case XRT_ROOT_PUT_LEAF: {

> +		struct xrt_root_put_leaf *putleaf = (struct xrt_root_put_leaf *)arg;

> +

> +		rc = xroot_put_leaf(xr, putleaf);

> +		break;

> +	}

> +	case XRT_ROOT_GET_LEAF_HOLDERS: {

> +		struct xrt_root_get_holders *holders = (struct xrt_root_get_holders *)arg;

> +

> +		rc = xrt_subdev_pool_get_holders(&xr->groups.pool,

> +						 holders->xpigh_pdev,

> +						 holders->xpigh_holder_buf,

> +						 holders->xpigh_holder_buf_len);

> +		break;

> +	}

> +

> +	/* Group actions. */

> +	case XRT_ROOT_CREATE_GROUP:

> +		rc = xroot_create_group(xr, (char *)arg);

> +		break;

> +	case XRT_ROOT_REMOVE_GROUP:

> +		rc = xroot_destroy_group(xr, (int)(uintptr_t)arg);

> +		break;

> +	case XRT_ROOT_LOOKUP_GROUP: {

> +		struct xrt_root_lookup_group *getgrp = (struct xrt_root_lookup_group *)arg;

> +

> +		rc = xroot_lookup_group(xr, getgrp);

> +		break;

> +	}

> +	case XRT_ROOT_WAIT_GROUP_BRINGUP:

> +		rc = xroot_wait_for_bringup(xr) ? 0 : -EINVAL;

> +		break;

> +

> +	/* Event actions. */

> +	case XRT_ROOT_EVENT_SYNC:

> +	case XRT_ROOT_EVENT_ASYNC: {

> +		bool async = (cmd == XRT_ROOT_EVENT_ASYNC);

> +		struct xrt_event *evt = (struct xrt_event *)arg;

> +

> +		rc = xroot_trigger_event(xr, evt, async);

> +		break;

> +	}

> +

> +	/* Device info. */

> +	case XRT_ROOT_GET_RESOURCE: {

> +		struct xrt_root_get_res *res = (struct xrt_root_get_res *)arg;

> +

> +		res->xpigr_res = xr->pdev->resource;

> +		break;

> +	}

> +	case XRT_ROOT_GET_ID: {

> +		struct xrt_root_get_id *id = (struct xrt_root_get_id *)arg;

> +

> +		id->xpigi_vendor_id = xr->pdev->vendor;

> +		id->xpigi_device_id = xr->pdev->device;

> +		id->xpigi_sub_vendor_id = xr->pdev->subsystem_vendor;

> +		id->xpigi_sub_device_id = xr->pdev->subsystem_device;

> +		break;

> +	}

> +

> +	/* MISC generic PCIE driver functions. */

> +	case XRT_ROOT_HOT_RESET: {

> +		xr->pf_cb.xpc_hot_reset(xr->pdev);

> +		break;

> +	}

> +	case XRT_ROOT_HWMON: {

> +		struct xrt_root_hwmon *hwmon = (struct xrt_root_hwmon *)arg;

> +

> +		if (hwmon->xpih_register) {

> +			hwmon->xpih_hwmon_dev =

> +				hwmon_device_register_with_info(DEV(xr->pdev),

> +								hwmon->xpih_name,

> +								hwmon->xpih_drvdata,

> +								NULL,

> +								hwmon->xpih_groups);

> +		} else {

> +			hwmon_device_unregister(hwmon->xpih_hwmon_dev);

> +		}

> +		break;

> +	}

> +

> +	default:

> +		xroot_err(xr, "unknown IOCTL cmd %d", cmd);

> +		rc = -EINVAL;

> +		break;

> +	}

> +

> +	return rc;

> +}

> +

> +static void xroot_bringup_group_work(struct work_struct *work)

> +{

> +	struct platform_device *pdev = NULL;

> +	struct xroot *xr = container_of(work, struct xroot, groups.bringup_work);

> +

> +	while (xroot_get_group(xr, XROOT_GROUP_FIRST, &pdev) != -ENOENT) {

> +		int r, i;

> +

> +		i = pdev->id;

> +		r = xleaf_call(pdev, XRT_GROUP_INIT_CHILDREN, NULL);

> +		xroot_put_group(xr, pdev);

> +		if (r == -EEXIST)

> +			continue; /* Already brough up, nothing to do. */

> +		if (r)

> +			atomic_inc(&xr->groups.bringup_failed);

> +

> +		xroot_group_trigger_event(xr, i, XRT_EVENT_POST_CREATION);

> +

> +		if (atomic_dec_and_test(&xr->groups.bringup_pending))

> +			complete(&xr->groups.bringup_comp);

> +	}

> +}

> +

> +static void xroot_groups_init(struct xroot *xr)

ok
> +{

> +	xrt_subdev_pool_init(DEV(xr->pdev), &xr->groups.pool);

> +	INIT_WORK(&xr->groups.bringup_work, xroot_bringup_group_work);

> +	atomic_set(&xr->groups.bringup_pending, 0);

> +	atomic_set(&xr->groups.bringup_failed, 0);

> +	init_completion(&xr->groups.bringup_comp);

> +}

> +

> +static void xroot_groups_fini(struct xroot *xr)

> +{

> +	flush_scheduled_work();

> +	xrt_subdev_pool_fini(&xr->groups.pool);

> +}

> +

> +int xroot_add_vsec_node(void *root, char *dtb)

> +{

> +	struct xroot *xr = (struct xroot *)root;

> +	struct device *dev = DEV(xr->pdev);

> +	struct xrt_md_endpoint ep = { 0 };

> +	int cap = 0, ret = 0;

> +	u32 off_low, off_high, vsec_bar, header;

> +	u64 vsec_off;

> +

> +	while ((cap = pci_find_next_ext_capability(xr->pdev, cap, PCI_EXT_CAP_ID_VNDR))) {

> +		pci_read_config_dword(xr->pdev, cap + PCI_VNDR_HEADER, &header);

> +		if (PCI_VNDR_HEADER_ID(header) == XRT_VSEC_ID)

> +			break;

> +	}

> +	if (!cap) {

> +		xroot_info(xr, "No Vendor Specific Capability.");

> +		return -ENOENT;

> +	}

> +

> +	if (pci_read_config_dword(xr->pdev, cap + 8, &off_low) ||

> +	    pci_read_config_dword(xr->pdev, cap + 12, &off_high)) {

> +		xroot_err(xr, "pci_read vendor specific failed.");

> +		return -EINVAL;

> +	}

> +

> +	ep.ep_name = XRT_MD_NODE_VSEC;

> +	ret = xrt_md_add_endpoint(dev, dtb, &ep);

> +	if (ret) {

> +		xroot_err(xr, "add vsec metadata failed, ret %d", ret);

> +		goto failed;

> +	}

> +

> +	vsec_bar = cpu_to_be32(off_low & 0xf);

> +	ret = xrt_md_set_prop(dev, dtb, XRT_MD_NODE_VSEC, NULL,

> +			      XRT_MD_PROP_BAR_IDX, &vsec_bar, sizeof(vsec_bar));

> +	if (ret) {

> +		xroot_err(xr, "add vsec bar idx failed, ret %d", ret);

> +		goto failed;

> +	}

> +

> +	vsec_off = cpu_to_be64(((u64)off_high << 32) | (off_low & ~0xfU));

> +	ret = xrt_md_set_prop(dev, dtb, XRT_MD_NODE_VSEC, NULL,

> +			      XRT_MD_PROP_OFFSET, &vsec_off, sizeof(vsec_off));

> +	if (ret) {

> +		xroot_err(xr, "add vsec offset failed, ret %d", ret);

> +		goto failed;

> +	}

> +

> +failed:

> +	return ret;

> +}

> +EXPORT_SYMBOL_GPL(xroot_add_vsec_node);

> +

> +int xroot_add_simple_node(void *root, char *dtb, const char *endpoint)

> +{

> +	struct xroot *xr = (struct xroot *)root;

> +	struct device *dev = DEV(xr->pdev);

> +	struct xrt_md_endpoint ep = { 0 };

> +	int ret = 0;

> +

> +	ep.ep_name = endpoint;

> +	ret = xrt_md_add_endpoint(dev, dtb, &ep);

> +	if (ret)

> +		xroot_err(xr, "add %s failed, ret %d", endpoint, ret);

> +

> +	return ret;

> +}

> +EXPORT_SYMBOL_GPL(xroot_add_simple_node);

> +

> +bool xroot_wait_for_bringup(void *root)

> +{

> +	struct xroot *xr = (struct xroot *)root;

> +

> +	wait_for_completion(&xr->groups.bringup_comp);

> +	return atomic_read(&xr->groups.bringup_failed) == 0;


ok

Tom

> +}

> +EXPORT_SYMBOL_GPL(xroot_wait_for_bringup);

> +

> +int xroot_probe(struct pci_dev *pdev, struct xroot_physical_function_callback *cb, void **root)

> +{

> +	struct device *dev = DEV(pdev);

> +	struct xroot *xr = NULL;

> +

> +	dev_info(dev, "%s: probing...", __func__);

> +

> +	xr = devm_kzalloc(dev, sizeof(*xr), GFP_KERNEL);

> +	if (!xr)

> +		return -ENOMEM;

> +

> +	xr->pdev = pdev;

> +	xr->pf_cb = *cb;

> +	xroot_groups_init(xr);

> +	xroot_event_init(xr);

> +

> +	*root = xr;

> +	return 0;

> +}

> +EXPORT_SYMBOL_GPL(xroot_probe);

> +

> +void xroot_remove(void *root)

> +{

> +	struct xroot *xr = (struct xroot *)root;

> +	struct platform_device *grp = NULL;

> +

> +	xroot_info(xr, "leaving...");

> +

> +	if (xroot_get_group(xr, XROOT_GROUP_FIRST, &grp) == 0) {

> +		int instance = grp->id;

> +

> +		xroot_put_group(xr, grp);

> +		xroot_destroy_group(xr, instance);

> +	}

> +

> +	xroot_event_fini(xr);

> +	xroot_groups_fini(xr);

> +}

> +EXPORT_SYMBOL_GPL(xroot_remove);

> +

> +void xroot_broadcast(void *root, enum xrt_events evt)

> +{

> +	struct xroot *xr = (struct xroot *)root;

> +	struct xrt_event e = { 0 };

> +

> +	/* Root pf driver only broadcasts below two events. */

> +	if (evt != XRT_EVENT_POST_CREATION && evt != XRT_EVENT_PRE_REMOVAL) {

> +		xroot_info(xr, "invalid event %d", evt);

> +		return;

> +	}

> +

> +	e.xe_evt = evt;

> +	e.xe_subdev.xevt_subdev_id = XRT_ROOT;

> +	e.xe_subdev.xevt_subdev_instance = 0;

> +	xroot_trigger_event(xr, &e, false);

> +}

> +EXPORT_SYMBOL_GPL(xroot_broadcast);
Tom Rix April 1, 2021, 2:07 p.m. UTC | #5
On 3/23/21 10:29 PM, Lizhi Hou wrote:
> platform driver that handles IOCTLs, such as hot reset and xclbin download.

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>  drivers/fpga/xrt/include/xmgmt-main.h |  34 ++

>  drivers/fpga/xrt/mgmt/main.c          | 670 ++++++++++++++++++++++++++

>  drivers/fpga/xrt/mgmt/xmgnt.h         |  34 ++

>  include/uapi/linux/xrt/xmgmt-ioctl.h  |  46 ++

>  4 files changed, 784 insertions(+)

>  create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h

>  create mode 100644 drivers/fpga/xrt/mgmt/main.c

'main' is generic, how about xmgnt-main ?
>  create mode 100644 drivers/fpga/xrt/mgmt/xmgnt.h

>  create mode 100644 include/uapi/linux/xrt/xmgmt-ioctl.h

>

> diff --git a/drivers/fpga/xrt/include/xmgmt-main.h b/drivers/fpga/xrt/include/xmgmt-main.h

> new file mode 100644

> index 000000000000..dce9f0d1a0dc

> --- /dev/null

> +++ b/drivers/fpga/xrt/include/xmgmt-main.h

> @@ -0,0 +1,34 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Cheng Zhen <maxz@xilinx.com>

> + */

> +

> +#ifndef _XMGMT_MAIN_H_

> +#define _XMGMT_MAIN_H_

> +

> +#include <linux/xrt/xclbin.h>

> +#include "xleaf.h"

> +

> +enum xrt_mgmt_main_leaf_cmd {

> +	XRT_MGMT_MAIN_GET_AXLF_SECTION = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */

> +	XRT_MGMT_MAIN_GET_VBNV,

> +};

> +

> +/* There are three kind of partitions. Each of them is programmed independently. */

> +enum provider_kind {

> +	XMGMT_BLP, /* Base Logic Partition */

> +	XMGMT_PLP, /* Provider Logic Partition */

> +	XMGMT_ULP, /* User Logic Partition */

ok
> +};

> +

> +struct xrt_mgmt_main_get_axlf_section {

> +	enum provider_kind xmmigas_axlf_kind;

> +	enum axlf_section_kind xmmigas_section_kind;

> +	void *xmmigas_section;

> +	u64 xmmigas_section_size;

> +};

> +

> +#endif	/* _XMGMT_MAIN_H_ */

> diff --git a/drivers/fpga/xrt/mgmt/main.c b/drivers/fpga/xrt/mgmt/main.c

> new file mode 100644

> index 000000000000..f3b46e1fd78b

> --- /dev/null

> +++ b/drivers/fpga/xrt/mgmt/main.c

> @@ -0,0 +1,670 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * Xilinx Alveo FPGA MGMT PF entry point driver

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Sonal Santan <sonals@xilinx.com>

> + */

> +

> +#include <linux/firmware.h>

> +#include <linux/uaccess.h>

> +#include "xclbin-helper.h"

> +#include "metadata.h"

> +#include "xleaf.h"

> +#include <linux/xrt/xmgmt-ioctl.h>

> +#include "xleaf/devctl.h"

> +#include "xmgmt-main.h"

> +#include "fmgr.h"

> +#include "xleaf/icap.h"

> +#include "xleaf/axigate.h"

> +#include "xmgnt.h"

> +

> +#define XMGMT_MAIN "xmgmt_main"

> +#define XMGMT_SUPP_XCLBIN_MAJOR 2

> +

> +#define XMGMT_FLAG_FLASH_READY	1

> +#define XMGMT_FLAG_DEVCTL_READY	2

> +

> +#define XMGMT_UUID_STR_LEN	80

> +

> +struct xmgmt_main {

> +	struct platform_device *pdev;

> +	struct axlf *firmware_blp;

> +	struct axlf *firmware_plp;

> +	struct axlf *firmware_ulp;

> +	u32 flags;

ok
> +	struct fpga_manager *fmgr;

> +	struct mutex lock; /* busy lock */

ok
> +

do not need this nl
> +	uuid_t *blp_interface_uuids;

> +	u32 blp_interface_uuid_num;

ok
> +};

> +

> +/*

> + * VBNV stands for Vendor, BoardID, Name, Version. It is a string

> + * which describes board and shell.

> + *

> + * Caller is responsible for freeing the returned string.

ok
> + */

> +char *xmgmt_get_vbnv(struct platform_device *pdev)

> +{

> +	struct xmgmt_main *xmm = platform_get_drvdata(pdev);

> +	const char *vbnv;

> +	char *ret;

> +	int i;

> +

> +	if (xmm->firmware_plp)

> +		vbnv = xmm->firmware_plp->header.platform_vbnv;

> +	else if (xmm->firmware_blp)

> +		vbnv = xmm->firmware_blp->header.platform_vbnv;

> +	else

> +		return NULL;

> +

> +	ret = kstrdup(vbnv, GFP_KERNEL);

> +	if (!ret)

> +		return NULL;

> +

> +	for (i = 0; i < strlen(ret); i++) {

> +		if (ret[i] == ':' || ret[i] == '.')

> +			ret[i] = '_';

> +	}

> +	return ret;

> +}

> +

> +static int get_dev_uuid(struct platform_device *pdev, char *uuidstr, size_t len)

> +{

> +	struct xrt_devctl_rw devctl_arg = { 0 };

> +	struct platform_device *devctl_leaf;

> +	char uuid_buf[UUID_SIZE];

> +	uuid_t uuid;

> +	int err;

> +

> +	devctl_leaf = xleaf_get_leaf_by_epname(pdev, XRT_MD_NODE_BLP_ROM);

> +	if (!devctl_leaf) {

> +		xrt_err(pdev, "can not get %s", XRT_MD_NODE_BLP_ROM);

> +		return -EINVAL;

> +	}

> +

> +	devctl_arg.xdr_id = XRT_DEVCTL_ROM_UUID;

> +	devctl_arg.xdr_buf = uuid_buf;

> +	devctl_arg.xdr_len = sizeof(uuid_buf);

> +	devctl_arg.xdr_offset = 0;

> +	err = xleaf_call(devctl_leaf, XRT_DEVCTL_READ, &devctl_arg);

> +	xleaf_put_leaf(pdev, devctl_leaf);

> +	if (err) {

> +		xrt_err(pdev, "can not get uuid: %d", err);

> +		return err;

> +	}

> +	import_uuid(&uuid, uuid_buf);

ok
> +	xrt_md_trans_uuid2str(&uuid, uuidstr);

> +

> +	return 0;

> +}

> +

> +int xmgmt_hot_reset(struct platform_device *pdev)

> +{

> +	int ret = xleaf_broadcast_event(pdev, XRT_EVENT_PRE_HOT_RESET, false);

> +

> +	if (ret) {

> +		xrt_err(pdev, "offline failed, hot reset is canceled");

> +		return ret;

> +	}

> +

> +	xleaf_hot_reset(pdev);

> +	xleaf_broadcast_event(pdev, XRT_EVENT_POST_HOT_RESET, false);

> +	return 0;

> +}

> +

> +static ssize_t reset_store(struct device *dev, struct device_attribute *da,

> +			   const char *buf, size_t count)

> +{

> +	struct platform_device *pdev = to_platform_device(dev);

> +

> +	xmgmt_hot_reset(pdev);

> +	return count;

> +}

> +static DEVICE_ATTR_WO(reset);

> +

> +static ssize_t VBNV_show(struct device *dev, struct device_attribute *da, char *buf)

> +{

> +	struct platform_device *pdev = to_platform_device(dev);

> +	ssize_t ret;

> +	char *vbnv;

> +

> +	vbnv = xmgmt_get_vbnv(pdev);

> +	if (!vbnv)

> +		return -EINVAL;

ok
> +	ret = sprintf(buf, "%s\n", vbnv);

> +	kfree(vbnv);

> +	return ret;

> +}

> +static DEVICE_ATTR_RO(VBNV);

> +

> +/* logic uuid is the uuid uniquely identfy the partition */

> +static ssize_t logic_uuids_show(struct device *dev, struct device_attribute *da, char *buf)

> +{

> +	struct platform_device *pdev = to_platform_device(dev);

> +	char uuid[XMGMT_UUID_STR_LEN];

ok
> +	ssize_t ret;

> +

> +	/* Getting UUID pointed to by VSEC, should be the same as logic UUID of BLP. */

> +	ret = get_dev_uuid(pdev, uuid, sizeof(uuid));

> +	if (ret)

> +		return ret;

> +	ret = sprintf(buf, "%s\n", uuid);

> +	return ret;

> +}

> +static DEVICE_ATTR_RO(logic_uuids);

> +

> +static ssize_t interface_uuids_show(struct device *dev, struct device_attribute *da, char *buf)

> +{

> +	struct platform_device *pdev = to_platform_device(dev);

> +	struct xmgmt_main *xmm = platform_get_drvdata(pdev);

> +	ssize_t ret = 0;

> +	u32 i;

> +

> +	for (i = 0; i < xmm->blp_interface_uuid_num; i++) {

> +		char uuidstr[XMGMT_UUID_STR_LEN];

> +

> +		xrt_md_trans_uuid2str(&xmm->blp_interface_uuids[i], uuidstr);

> +		ret += sprintf(buf + ret, "%s\n", uuidstr);

> +	}

> +	return ret;

> +}

> +static DEVICE_ATTR_RO(interface_uuids);

> +

> +static struct attribute *xmgmt_main_attrs[] = {

> +	&dev_attr_reset.attr,

> +	&dev_attr_VBNV.attr,

> +	&dev_attr_logic_uuids.attr,

> +	&dev_attr_interface_uuids.attr,

> +	NULL,

> +};

> +

> +static const struct attribute_group xmgmt_main_attrgroup = {

> +	.attrs = xmgmt_main_attrs,

> +};

> +

ok,  removed ulp_image_write()
> +static int load_firmware_from_disk(struct platform_device *pdev, struct axlf **fw_buf, size_t *len)

> +{

> +	char uuid[XMGMT_UUID_STR_LEN];

> +	const struct firmware *fw;

> +	char fw_name[256];

> +	int err = 0;

> +

> +	*len = 0;

ok
> +	err = get_dev_uuid(pdev, uuid, sizeof(uuid));

> +	if (err)

> +		return err;

> +

> +	snprintf(fw_name, sizeof(fw_name), "xilinx/%s/partition.xsabin", uuid);

> +	xrt_info(pdev, "try loading fw: %s", fw_name);

> +

> +	err = request_firmware(&fw, fw_name, DEV(pdev));

> +	if (err)

> +		return err;

> +

> +	*fw_buf = vmalloc(fw->size);

> +	if (!*fw_buf) {

> +		release_firmware(fw);

> +		return -ENOMEM;

> +	}

> +

> +	*len = fw->size;

> +	memcpy(*fw_buf, fw->data, fw->size);

> +

> +	release_firmware(fw);

> +	return 0;

> +}

> +

> +static const struct axlf *xmgmt_get_axlf_firmware(struct xmgmt_main *xmm, enum provider_kind kind)

> +{

> +	switch (kind) {

> +	case XMGMT_BLP:

> +		return xmm->firmware_blp;

> +	case XMGMT_PLP:

> +		return xmm->firmware_plp;

> +	case XMGMT_ULP:

> +		return xmm->firmware_ulp;

> +	default:

> +		xrt_err(xmm->pdev, "unknown axlf kind: %d", kind);

> +		return NULL;

> +	}

> +}

> +

> +/* The caller needs to free the returned dtb buffer */

ok
> +char *xmgmt_get_dtb(struct platform_device *pdev, enum provider_kind kind)

> +{

> +	struct xmgmt_main *xmm = platform_get_drvdata(pdev);

> +	const struct axlf *provider;

> +	char *dtb = NULL;

> +	int rc;

> +

> +	provider = xmgmt_get_axlf_firmware(xmm, kind);

> +	if (!provider)

> +		return dtb;

> +

> +	rc = xrt_xclbin_get_metadata(DEV(pdev), provider, &dtb);

> +	if (rc)

> +		xrt_err(pdev, "failed to find dtb: %d", rc);

> +	return dtb;

> +}

> +

> +/* The caller needs to free the returned uuid buffer */

ok
> +static const char *get_uuid_from_firmware(struct platform_device *pdev, const struct axlf *xclbin)

> +{

> +	const void *uuiddup = NULL;

> +	const void *uuid = NULL;

> +	void *dtb = NULL;

> +	int rc;

> +

> +	rc = xrt_xclbin_get_section(DEV(pdev), xclbin, PARTITION_METADATA, &dtb, NULL);

> +	if (rc)

> +		return NULL;

> +

> +	rc = xrt_md_get_prop(DEV(pdev), dtb, NULL, NULL, XRT_MD_PROP_LOGIC_UUID, &uuid, NULL);

> +	if (!rc)

> +		uuiddup = kstrdup(uuid, GFP_KERNEL);

> +	vfree(dtb);

> +	return uuiddup;

> +}

> +

> +static bool is_valid_firmware(struct platform_device *pdev,

> +			      const struct axlf *xclbin, size_t fw_len)

> +{

> +	const char *fw_buf = (const char *)xclbin;

> +	size_t axlflen = xclbin->header.length;

> +	char dev_uuid[XMGMT_UUID_STR_LEN];

> +	const char *fw_uuid;

> +	int err;

> +

> +	err = get_dev_uuid(pdev, dev_uuid, sizeof(dev_uuid));

> +	if (err)

> +		return false;

> +

> +	if (memcmp(fw_buf, XCLBIN_VERSION2, sizeof(XCLBIN_VERSION2)) != 0) {

> +		xrt_err(pdev, "unknown fw format");

> +		return false;

> +	}

> +

> +	if (axlflen > fw_len) {

> +		xrt_err(pdev, "truncated fw, length: %zu, expect: %zu", fw_len, axlflen);

> +		return false;

> +	}

> +

> +	if (xclbin->header.version_major != XMGMT_SUPP_XCLBIN_MAJOR) {

> +		xrt_err(pdev, "firmware is not supported");

> +		return false;

> +	}

> +

> +	fw_uuid = get_uuid_from_firmware(pdev, xclbin);

> +	if (!fw_uuid || strncmp(fw_uuid, dev_uuid, sizeof(dev_uuid)) != 0) {

> +		xrt_err(pdev, "bad fw UUID: %s, expect: %s",

> +			fw_uuid ? fw_uuid : "<none>", dev_uuid);

> +		kfree(fw_uuid);

> +		return false;

> +	}

> +

> +	kfree(fw_uuid);

> +	return true;

> +}

> +

> +int xmgmt_get_provider_uuid(struct platform_device *pdev, enum provider_kind kind, uuid_t *uuid)

> +{

> +	struct xmgmt_main *xmm = platform_get_drvdata(pdev);

> +	const struct axlf *fwbuf;

> +	const char *fw_uuid;

> +	int rc = -ENOENT;

> +

> +	mutex_lock(&xmm->lock);

> +

> +	fwbuf = xmgmt_get_axlf_firmware(xmm, kind);

> +	if (!fwbuf)

> +		goto done;

> +

> +	fw_uuid = get_uuid_from_firmware(pdev, fwbuf);

> +	if (!fw_uuid)

> +		goto done;

> +

> +	rc = xrt_md_trans_str2uuid(DEV(pdev), fw_uuid, uuid);

> +	kfree(fw_uuid);

> +

> +done:

> +	mutex_unlock(&xmm->lock);

> +	return rc;

> +}

> +

> +static int xmgmt_create_blp(struct xmgmt_main *xmm)

> +{

> +	const struct axlf *provider = xmgmt_get_axlf_firmware(xmm, XMGMT_BLP);

> +	struct platform_device *pdev = xmm->pdev;

> +	int rc = 0;

> +	char *dtb = NULL;

> +

> +	dtb = xmgmt_get_dtb(pdev, XMGMT_BLP);

> +	if (!dtb) {

> +		xrt_err(pdev, "did not get BLP metadata");

> +		return -EINVAL;

ok
> +	}

> +

> +	rc = xmgmt_process_xclbin(xmm->pdev, xmm->fmgr, provider, XMGMT_BLP);

> +	if (rc) {

> +		xrt_err(pdev, "failed to process BLP: %d", rc);

> +		goto failed;

> +	}

> +

> +	rc = xleaf_create_group(pdev, dtb);

> +	if (rc < 0)

> +		xrt_err(pdev, "failed to create BLP group: %d", rc);

> +	else

> +		rc = 0;

> +

> +	WARN_ON(xmm->blp_interface_uuids);

> +	rc = xrt_md_get_interface_uuids(&pdev->dev, dtb, 0, NULL);

> +	if (rc > 0) {

> +		xmm->blp_interface_uuid_num = rc;

> +		xmm->blp_interface_uuids = vzalloc(sizeof(uuid_t) * xmm->blp_interface_uuid_num);

blp_interface_uuids should be small, so convert to kzalloc
> +		if (!xmm->blp_interface_uuids) {

ok
> +			rc = -ENOMEM;

> +			goto failed;

> +		}

> +		xrt_md_get_interface_uuids(&pdev->dev, dtb, xmm->blp_interface_uuid_num,

> +					   xmm->blp_interface_uuids);

> +	}

> +

> +failed:

> +	vfree(dtb);

> +	return rc;

> +}

> +

> +static int xmgmt_load_firmware(struct xmgmt_main *xmm)

> +{

> +	struct platform_device *pdev = xmm->pdev;

> +	size_t fwlen;

> +	int rc;

> +

> +	rc = load_firmware_from_disk(pdev, &xmm->firmware_blp, &fwlen);

ok
> +	if (!rc && is_valid_firmware(pdev, xmm->firmware_blp, fwlen))

> +		xmgmt_create_blp(xmm);

> +	else

> +		xrt_err(pdev, "failed to find firmware, giving up: %d", rc);

> +	return rc;

> +}

> +

> +static void xmgmt_main_event_cb(struct platform_device *pdev, void *arg)

> +{

> +	struct xmgmt_main *xmm = platform_get_drvdata(pdev);

> +	struct xrt_event *evt = (struct xrt_event *)arg;

> +	enum xrt_events e = evt->xe_evt;

> +	struct platform_device *leaf;

> +	enum xrt_subdev_id id;

> +

> +	id = evt->xe_subdev.xevt_subdev_id;

> +	switch (e) {

> +	case XRT_EVENT_POST_CREATION: {

> +		if (id == XRT_SUBDEV_DEVCTL && !(xmm->flags & XMGMT_FLAG_DEVCTL_READY)) {

> +			leaf = xleaf_get_leaf_by_epname(pdev, XRT_MD_NODE_BLP_ROM);

> +			if (leaf) {

> +				xmm->flags |= XMGMT_FLAG_DEVCTL_READY;

> +				xleaf_put_leaf(pdev, leaf);

> +			}

> +		} else if (id == XRT_SUBDEV_QSPI && !(xmm->flags & XMGMT_FLAG_FLASH_READY)) {

> +			xmm->flags |= XMGMT_FLAG_FLASH_READY;

> +		} else {

> +			break;

> +		}

> +

> +		if (xmm->flags & XMGMT_FLAG_DEVCTL_READY)

> +			xmgmt_load_firmware(xmm);

> +		break;

> +	}

> +	case XRT_EVENT_PRE_REMOVAL:

> +		break;

> +	default:

> +		xrt_dbg(pdev, "ignored event %d", e);

> +		break;

> +	}

> +}

> +

> +static int xmgmt_main_probe(struct platform_device *pdev)

> +{

> +	struct xmgmt_main *xmm;

> +

> +	xrt_info(pdev, "probing...");

> +

> +	xmm = devm_kzalloc(DEV(pdev), sizeof(*xmm), GFP_KERNEL);

> +	if (!xmm)

> +		return -ENOMEM;

> +

> +	xmm->pdev = pdev;

> +	xmm->fmgr = xmgmt_fmgr_probe(pdev);

> +	if (IS_ERR(xmm->fmgr))

> +		return PTR_ERR(xmm->fmgr);

> +

> +	platform_set_drvdata(pdev, xmm);

> +	mutex_init(&xmm->lock);

> +

> +	/* Ready to handle req thru sysfs nodes. */

> +	if (sysfs_create_group(&DEV(pdev)->kobj, &xmgmt_main_attrgroup))

> +		xrt_err(pdev, "failed to create sysfs group");

> +	return 0;

> +}

> +

> +static int xmgmt_main_remove(struct platform_device *pdev)

> +{

> +	struct xmgmt_main *xmm = platform_get_drvdata(pdev);

> +

> +	/* By now, group driver should prevent any inter-leaf call. */

> +

> +	xrt_info(pdev, "leaving...");

> +

> +	vfree(xmm->blp_interface_uuids);

> +	vfree(xmm->firmware_blp);

> +	vfree(xmm->firmware_plp);

> +	vfree(xmm->firmware_ulp);

> +	xmgmt_region_cleanup_all(pdev);

> +	xmgmt_fmgr_remove(xmm->fmgr);

> +	sysfs_remove_group(&DEV(pdev)->kobj, &xmgmt_main_attrgroup);

> +	return 0;

> +}

> +

> +static int

> +xmgmt_mainleaf_call(struct platform_device *pdev, u32 cmd, void *arg)

> +{

> +	struct xmgmt_main *xmm = platform_get_drvdata(pdev);

> +	int ret = 0;

> +

> +	switch (cmd) {

> +	case XRT_XLEAF_EVENT:

> +		xmgmt_main_event_cb(pdev, arg);

> +		break;

> +	case XRT_MGMT_MAIN_GET_AXLF_SECTION: {

> +		struct xrt_mgmt_main_get_axlf_section *get =

> +			(struct xrt_mgmt_main_get_axlf_section *)arg;

> +		const struct axlf *firmware = xmgmt_get_axlf_firmware(xmm, get->xmmigas_axlf_kind);

> +

> +		if (!firmware) {

> +			ret = -ENOENT;

> +		} else {

> +			ret = xrt_xclbin_get_section(DEV(pdev), firmware,

> +						     get->xmmigas_section_kind,

> +						     &get->xmmigas_section,

> +						     &get->xmmigas_section_size);

> +		}

> +		break;

> +	}

> +	case XRT_MGMT_MAIN_GET_VBNV: {

> +		char **vbnv_p = (char **)arg;

> +

> +		*vbnv_p = xmgmt_get_vbnv(pdev);

> +		if (!*vbnv_p)

> +			ret = -EINVAL;

ok
> +		break;

> +	}

> +	default:

> +		xrt_err(pdev, "unknown cmd: %d", cmd);

> +		ret = -EINVAL;

> +		break;

> +	}

> +	return ret;

> +}

> +

> +static int xmgmt_main_open(struct inode *inode, struct file *file)

> +{

> +	struct platform_device *pdev = xleaf_devnode_open(inode);

> +

> +	/* Device may have gone already when we get here. */

> +	if (!pdev)

> +		return -ENODEV;

> +

> +	xrt_info(pdev, "opened");

> +	file->private_data = platform_get_drvdata(pdev);

> +	return 0;

> +}

> +

> +static int xmgmt_main_close(struct inode *inode, struct file *file)

> +{

> +	struct xmgmt_main *xmm = file->private_data;

> +

> +	xleaf_devnode_close(inode);

> +

> +	xrt_info(xmm->pdev, "closed");

> +	return 0;

> +}

> +

> +/*

> + * Called for xclbin download xclbin load ioctl.

> + */

> +static int xmgmt_bitstream_axlf_fpga_mgr(struct xmgmt_main *xmm, void *axlf, size_t size)

> +{

> +	int ret;

> +

> +	WARN_ON(!mutex_is_locked(&xmm->lock));

> +

> +	/*

> +	 * Should any error happens during download, we can't trust

> +	 * the cached xclbin any more.

> +	 */

> +	vfree(xmm->firmware_ulp);

> +	xmm->firmware_ulp = NULL;

> +

> +	ret = xmgmt_process_xclbin(xmm->pdev, xmm->fmgr, axlf, XMGMT_ULP);

> +	if (ret == 0)

> +		xmm->firmware_ulp = axlf;

> +

> +	return ret;

> +}

> +

> +static int bitstream_axlf_ioctl(struct xmgmt_main *xmm, const void __user *arg)

> +{

> +	struct xmgmt_ioc_bitstream_axlf ioc_obj = { 0 };

> +	struct axlf xclbin_obj = { {0} };

> +	size_t copy_buffer_size = 0;

> +	void *copy_buffer = NULL;

> +	int ret = 0;

> +

> +	if (copy_from_user((void *)&ioc_obj, arg, sizeof(ioc_obj)))

> +		return -EFAULT;

> +	if (copy_from_user((void *)&xclbin_obj, ioc_obj.xclbin, sizeof(xclbin_obj)))

> +		return -EFAULT;

> +	if (memcmp(xclbin_obj.magic, XCLBIN_VERSION2, sizeof(XCLBIN_VERSION2)))

> +		return -EINVAL;

> +

> +	copy_buffer_size = xclbin_obj.header.length;

> +	if (copy_buffer_size > XCLBIN_MAX_SIZE || copy_buffer_size < sizeof(xclbin_obj))


ok

Tom

> +		return -EINVAL;

> +	if (xclbin_obj.header.version_major != XMGMT_SUPP_XCLBIN_MAJOR)

> +		return -EINVAL;

> +

> +	copy_buffer = vmalloc(copy_buffer_size);

> +	if (!copy_buffer)

> +		return -ENOMEM;

> +

> +	if (copy_from_user(copy_buffer, ioc_obj.xclbin, copy_buffer_size)) {

> +		vfree(copy_buffer);

> +		return -EFAULT;

> +	}

> +

> +	ret = xmgmt_bitstream_axlf_fpga_mgr(xmm, copy_buffer, copy_buffer_size);

> +	if (ret)

> +		vfree(copy_buffer);

> +

> +	return ret;

> +}

> +

> +static long xmgmt_main_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

> +{

> +	struct xmgmt_main *xmm = filp->private_data;

> +	long result = 0;

> +

> +	if (_IOC_TYPE(cmd) != XMGMT_IOC_MAGIC)

> +		return -ENOTTY;

> +

> +	mutex_lock(&xmm->lock);

> +

> +	xrt_info(xmm->pdev, "ioctl cmd %d, arg %ld", cmd, arg);

> +	switch (cmd) {

> +	case XMGMT_IOCICAPDOWNLOAD_AXLF:

> +		result = bitstream_axlf_ioctl(xmm, (const void __user *)arg);

> +		break;

> +	default:

> +		result = -ENOTTY;

> +		break;

> +	}

> +

> +	mutex_unlock(&xmm->lock);

> +	return result;

> +}

> +

> +static struct xrt_subdev_endpoints xrt_mgmt_main_endpoints[] = {

> +	{

> +		.xse_names = (struct xrt_subdev_ep_names []){

> +			{ .ep_name = XRT_MD_NODE_MGMT_MAIN },

> +			{ NULL },

> +		},

> +		.xse_min_ep = 1,

> +	},

> +	{ 0 },

> +};

> +

> +static struct xrt_subdev_drvdata xmgmt_main_data = {

> +	.xsd_dev_ops = {

> +		.xsd_leaf_call = xmgmt_mainleaf_call,

> +	},

> +	.xsd_file_ops = {

> +		.xsf_ops = {

> +			.owner = THIS_MODULE,

> +			.open = xmgmt_main_open,

> +			.release = xmgmt_main_close,

> +			.unlocked_ioctl = xmgmt_main_ioctl,

> +		},

> +		.xsf_dev_name = "xmgmt",

> +	},

> +};

> +

> +static const struct platform_device_id xmgmt_main_id_table[] = {

> +	{ XMGMT_MAIN, (kernel_ulong_t)&xmgmt_main_data },

> +	{ },

> +};

> +

> +static struct platform_driver xmgmt_main_driver = {

> +	.driver	= {

> +		.name    = XMGMT_MAIN,

> +	},

> +	.probe   = xmgmt_main_probe,

> +	.remove  = xmgmt_main_remove,

> +	.id_table = xmgmt_main_id_table,

> +};

> +

> +int xmgmt_register_leaf(void)

> +{

> +	return xleaf_register_driver(XRT_SUBDEV_MGMT_MAIN,

> +				     &xmgmt_main_driver, xrt_mgmt_main_endpoints);

> +}

> +

> +void xmgmt_unregister_leaf(void)

> +{

> +	xleaf_unregister_driver(XRT_SUBDEV_MGMT_MAIN);

> +}

> diff --git a/drivers/fpga/xrt/mgmt/xmgnt.h b/drivers/fpga/xrt/mgmt/xmgnt.h

> new file mode 100644

> index 000000000000..9d7c11194745

> --- /dev/null

> +++ b/drivers/fpga/xrt/mgmt/xmgnt.h

> @@ -0,0 +1,34 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Lizhi Hou <Lizhi.Hou@xilinx.com>

> + *	Cheng Zhen <maxz@xilinx.com>

> + */

> +

> +#ifndef _XMGMT_XMGNT_H_

> +#define _XMGMT_XMGNT_H_


For consistency, should be shortened to _XMGMNT_H_

> +

> +#include <linux/platform_device.h>

> +#include "xmgmt-main.h"

> +

> +struct fpga_manager;

> +int xmgmt_process_xclbin(struct platform_device *pdev,

> +			 struct fpga_manager *fmgr,

> +			 const struct axlf *xclbin,

> +			 enum provider_kind kind);

> +void xmgmt_region_cleanup_all(struct platform_device *pdev);

> +

> +int xmgmt_hot_reset(struct platform_device *pdev);

> +

> +/* Getting dtb for specified group. Caller should vfree returned dtb .*/

> +char *xmgmt_get_dtb(struct platform_device *pdev, enum provider_kind kind);

> +char *xmgmt_get_vbnv(struct platform_device *pdev);

> +int xmgmt_get_provider_uuid(struct platform_device *pdev,

> +			    enum provider_kind kind, uuid_t *uuid);

> +

> +int xmgmt_register_leaf(void);

ok
> +void xmgmt_unregister_leaf(void);

> +

> +#endif	/* _XMGMT_XMGNT_H_ */

> diff --git a/include/uapi/linux/xrt/xmgmt-ioctl.h b/include/uapi/linux/xrt/xmgmt-ioctl.h

> new file mode 100644

> index 000000000000..da992e581189

> --- /dev/null

> +++ b/include/uapi/linux/xrt/xmgmt-ioctl.h

> @@ -0,0 +1,46 @@

> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

> +/*

> + *  Copyright (C) 2015-2021, Xilinx Inc

> + *

> + */

> +

> +/**

> + * DOC: PCIe Kernel Driver for Management Physical Function

> + * Interfaces exposed by *xclmgmt* driver are defined in file, *mgmt-ioctl.h*.

> + * Core functionality provided by *xmgmt* driver is described in the following table:

> + *

> + * =========== ============================== ==================================

> + * Functionality           ioctl request code           data format

> + * =========== ============================== ==================================

> + * 1 FPGA image download   XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf

> + * =========== ============================== ==================================

> + */

> +

> +#ifndef _XMGMT_IOCTL_H_

> +#define _XMGMT_IOCTL_H_

> +

> +#include <linux/ioctl.h>

> +

> +#define XMGMT_IOC_MAGIC	'X'

> +#define XMGMT_IOC_ICAP_DOWNLOAD_AXLF 0x6

> +

> +/**

> + * struct xmgmt_ioc_bitstream_axlf - load xclbin (AXLF) device image

> + * used with XMGMT_IOCICAPDOWNLOAD_AXLF ioctl

> + *

> + * @xclbin:	Pointer to user's xclbin structure in memory

> + */

> +struct xmgmt_ioc_bitstream_axlf {

> +	struct axlf *xclbin;

> +};

> +

> +#define XMGMT_IOCICAPDOWNLOAD_AXLF				\

> +	_IOW(XMGMT_IOC_MAGIC, XMGMT_IOC_ICAP_DOWNLOAD_AXLF, struct xmgmt_ioc_bitstream_axlf)

> +

> +/*

> + * The following definitions are for binary compatibility with classic XRT management driver

> + */

> +#define XCLMGMT_IOCICAPDOWNLOAD_AXLF XMGMT_IOCICAPDOWNLOAD_AXLF

> +#define xclmgmt_ioc_bitstream_axlf xmgmt_ioc_bitstream_axlf

> +

> +#endif
Tom Rix April 1, 2021, 2:43 p.m. UTC | #6
small alloc's should use kzalloc.

On 3/23/21 10:29 PM, Lizhi Hou wrote:
> fpga-mgr and region implementation for xclbin download which will be

> called from main platform driver

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>  drivers/fpga/xrt/mgmt/fmgr-drv.c    | 191 +++++++++++

>  drivers/fpga/xrt/mgmt/fmgr.h        |  19 ++

>  drivers/fpga/xrt/mgmt/main-region.c | 483 ++++++++++++++++++++++++++++

>  3 files changed, 693 insertions(+)

>  create mode 100644 drivers/fpga/xrt/mgmt/fmgr-drv.c

>  create mode 100644 drivers/fpga/xrt/mgmt/fmgr.h

a better file name would be xrt-mgr.*
>  create mode 100644 drivers/fpga/xrt/mgmt/main-region.c

>

> diff --git a/drivers/fpga/xrt/mgmt/fmgr-drv.c b/drivers/fpga/xrt/mgmt/fmgr-drv.c

> new file mode 100644

> index 000000000000..12e1cc788ad9

> --- /dev/null

> +++ b/drivers/fpga/xrt/mgmt/fmgr-drv.c

> @@ -0,0 +1,191 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * FPGA Manager Support for Xilinx Alveo Management Function Driver


Since there is only one fpga mgr for xrt, this could be shortened to

* FPGA Manager Support for Xilinx Alevo

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors: Sonal.Santan@xilinx.com

> + */

> +

> +#include <linux/cred.h>

> +#include <linux/efi.h>

> +#include <linux/fpga/fpga-mgr.h>

> +#include <linux/platform_device.h>

> +#include <linux/module.h>

> +#include <linux/vmalloc.h>

> +

> +#include "xclbin-helper.h"

> +#include "xleaf.h"

> +#include "fmgr.h"

> +#include "xleaf/axigate.h"

> +#include "xleaf/icap.h"

> +#include "xmgnt.h"

> +

> +struct xfpga_class {

> +	const struct platform_device *pdev;

> +	char                          name[64];

> +};

> +

> +/*

> + * xclbin download plumbing -- find the download subsystem, ICAP and

> + * pass the xclbin for heavy lifting

> + */

> +static int xmgmt_download_bitstream(struct platform_device *pdev,

> +				    const struct axlf *xclbin)

> +

> +{

> +	struct xclbin_bit_head_info bit_header = { 0 };

> +	struct platform_device *icap_leaf = NULL;

> +	struct xrt_icap_wr arg;

> +	char *bitstream = NULL;

> +	u64 bit_len;

> +	int ret;

> +

> +	ret = xrt_xclbin_get_section(DEV(pdev), xclbin, BITSTREAM, (void **)&bitstream, &bit_len);

> +	if (ret) {

> +		xrt_err(pdev, "bitstream not found");

> +		return -ENOENT;

> +	}

> +	ret = xrt_xclbin_parse_bitstream_header(DEV(pdev), bitstream,

> +						XCLBIN_HWICAP_BITFILE_BUF_SZ,

> +						&bit_header);

> +	if (ret) {

> +		ret = -EINVAL;

> +		xrt_err(pdev, "invalid bitstream header");

> +		goto fail;

> +	}

> +	if (bit_header.header_length + bit_header.bitstream_length > bit_len) {

> +		ret = -EINVAL;

> +		xrt_err(pdev, "invalid bitstream length. header %d, bitstream %d, section len %lld",

> +			bit_header.header_length, bit_header.bitstream_length, bit_len);

> +		goto fail;

> +	}

> +

> +	icap_leaf = xleaf_get_leaf_by_id(pdev, XRT_SUBDEV_ICAP, PLATFORM_DEVID_NONE);

> +	if (!icap_leaf) {

> +		ret = -ENODEV;

> +		xrt_err(pdev, "icap does not exist");

> +		goto fail;

> +	}

> +	arg.xiiw_bit_data = bitstream + bit_header.header_length;

> +	arg.xiiw_data_len = bit_header.bitstream_length;

> +	ret = xleaf_call(icap_leaf, XRT_ICAP_WRITE, &arg);

> +	if (ret) {

> +		xrt_err(pdev, "write bitstream failed, ret = %d", ret);

> +		xleaf_put_leaf(pdev, icap_leaf);

> +		goto fail;

> +	}

ok, free_header removed
> +

> +	xleaf_put_leaf(pdev, icap_leaf);

> +	vfree(bitstream);

> +

> +	return 0;

> +

> +fail:

> +	vfree(bitstream);

> +

> +	return ret;

> +}

> +

> +/*

> + * There is no HW prep work we do here since we need the full

> + * xclbin for its sanity check.

> + */

> +static int xmgmt_pr_write_init(struct fpga_manager *mgr,

> +			       struct fpga_image_info *info,

> +			       const char *buf, size_t count)

> +{

> +	const struct axlf *bin = (const struct axlf *)buf;

> +	struct xfpga_class *obj = mgr->priv;

> +

> +	if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {

> +		xrt_info(obj->pdev, "%s only supports partial reconfiguration\n", obj->name);

> +		return -EINVAL;

> +	}

> +

> +	if (count < sizeof(struct axlf))

> +		return -EINVAL;

> +

> +	if (count > bin->header.length)

> +		return -EINVAL;

> +

> +	xrt_info(obj->pdev, "Prepare download of xclbin %pUb of length %lld B",

> +		 &bin->header.uuid, bin->header.length);

> +

> +	return 0;

> +}

> +

> +/*

> + * The implementation requries full xclbin image before we can start

> + * programming the hardware via ICAP subsystem. The full image is required

ok
> + * for checking the validity of xclbin and walking the sections to

> + * discover the bitstream.

> + */

> +static int xmgmt_pr_write(struct fpga_manager *mgr,

> +			  const char *buf, size_t count)

> +{

> +	const struct axlf *bin = (const struct axlf *)buf;

> +	struct xfpga_class *obj = mgr->priv;

> +

> +	if (bin->header.length != count)

> +		return -EINVAL;

> +

> +	return xmgmt_download_bitstream((void *)obj->pdev, bin);

> +}

> +

> +static int xmgmt_pr_write_complete(struct fpga_manager *mgr,

> +				   struct fpga_image_info *info)

> +{

> +	const struct axlf *bin = (const struct axlf *)info->buf;

> +	struct xfpga_class *obj = mgr->priv;

> +

> +	xrt_info(obj->pdev, "Finished download of xclbin %pUb",

> +		 &bin->header.uuid);

> +	return 0;

> +}

> +

> +static enum fpga_mgr_states xmgmt_pr_state(struct fpga_manager *mgr)

> +{

> +	return FPGA_MGR_STATE_UNKNOWN;

ok as-is
> +}

> +

> +static const struct fpga_manager_ops xmgmt_pr_ops = {

> +	.initial_header_size = sizeof(struct axlf),

> +	.write_init = xmgmt_pr_write_init,

> +	.write = xmgmt_pr_write,

> +	.write_complete = xmgmt_pr_write_complete,

> +	.state = xmgmt_pr_state,

> +};

> +

> +struct fpga_manager *xmgmt_fmgr_probe(struct platform_device *pdev)

> +{

> +	struct xfpga_class *obj = devm_kzalloc(DEV(pdev), sizeof(struct xfpga_class),

> +					       GFP_KERNEL);

> +	struct fpga_manager *fmgr = NULL;

> +	int ret = 0;

> +

> +	if (!obj)

> +		return ERR_PTR(-ENOMEM);

> +

> +	snprintf(obj->name, sizeof(obj->name), "Xilinx Alveo FPGA Manager");

> +	obj->pdev = pdev;

> +	fmgr = fpga_mgr_create(&pdev->dev,

> +			       obj->name,

> +			       &xmgmt_pr_ops,

> +			       obj);

> +	if (!fmgr)

> +		return ERR_PTR(-ENOMEM);

> +

> +	ret = fpga_mgr_register(fmgr);

> +	if (ret) {

> +		fpga_mgr_free(fmgr);

> +		return ERR_PTR(ret);

> +	}

> +	return fmgr;

> +}

> +

> +int xmgmt_fmgr_remove(struct fpga_manager *fmgr)

> +{

> +	fpga_mgr_unregister(fmgr);

> +	return 0;

> +}

> diff --git a/drivers/fpga/xrt/mgmt/fmgr.h b/drivers/fpga/xrt/mgmt/fmgr.h

> new file mode 100644

> index 000000000000..ff1fc5f870f8

> --- /dev/null

> +++ b/drivers/fpga/xrt/mgmt/fmgr.h

> @@ -0,0 +1,19 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors: Sonal.Santan@xilinx.com

> + */

> +

> +#ifndef _XMGMT_FMGR_H_

> +#define _XMGMT_FMGR_H_

> +

> +#include <linux/fpga/fpga-mgr.h>

> +#include <linux/mutex.h>


why do mutex.h and xclbin.h need to be included ?

consider removing them.

> +

> +#include <linux/xrt/xclbin.h>

ok enum removed.
> +

> +struct fpga_manager *xmgmt_fmgr_probe(struct platform_device *pdev);

> +int xmgmt_fmgr_remove(struct fpga_manager *fmgr);

> +

> +#endif

> diff --git a/drivers/fpga/xrt/mgmt/main-region.c b/drivers/fpga/xrt/mgmt/main-region.c

> new file mode 100644

> index 000000000000..96a674618e86

> --- /dev/null

> +++ b/drivers/fpga/xrt/mgmt/main-region.c

> @@ -0,0 +1,483 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * FPGA Region Support for Xilinx Alveo Management Function Driver

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + * Bulk of the code borrowed from XRT mgmt driver file, fmgr.c

review this line, there is not fmgr.c
> + *

> + * Authors: Lizhi.Hou@xilinx.com

> + */

> +

> +#include <linux/uuid.h>

> +#include <linux/fpga/fpga-bridge.h>

> +#include <linux/fpga/fpga-region.h>

> +#include "metadata.h"

> +#include "xleaf.h"

> +#include "xleaf/axigate.h"

> +#include "xclbin-helper.h"

> +#include "xmgnt.h"

> +

> +struct xmgmt_bridge {

> +	struct platform_device *pdev;

> +	const char *bridge_name;

ok
> +};

> +

> +struct xmgmt_region {

> +	struct platform_device *pdev;

> +	struct fpga_region *region;

> +	struct fpga_compat_id compat_id;

> +	uuid_t intf_uuid;

interface_uuid
> +	struct fpga_bridge *bridge;

> +	int group_instance;

> +	uuid_t dep_uuid;

dep ? expand.
> +	struct list_head list;

> +};

> +

> +struct xmgmt_region_match_arg {

> +	struct platform_device *pdev;

> +	uuid_t *uuids;

> +	u32 uuid_num;

> +};

> +

> +static int xmgmt_br_enable_set(struct fpga_bridge *bridge, bool enable)

> +{

> +	struct xmgmt_bridge *br_data = (struct xmgmt_bridge *)bridge->priv;

> +	struct platform_device *axigate_leaf;

> +	int rc;

> +

> +	axigate_leaf = xleaf_get_leaf_by_epname(br_data->pdev, br_data->bridge_name);

> +	if (!axigate_leaf) {

> +		xrt_err(br_data->pdev, "failed to get leaf %s",

> +			br_data->bridge_name);

> +		return -ENOENT;

> +	}

> +

> +	if (enable)

> +		rc = xleaf_call(axigate_leaf, XRT_AXIGATE_OPEN, NULL);

> +	else

> +		rc = xleaf_call(axigate_leaf, XRT_AXIGATE_CLOSE, NULL);

> +

> +	if (rc) {

> +		xrt_err(br_data->pdev, "failed to %s gate %s, rc %d",

> +			(enable ? "free" : "freeze"), br_data->bridge_name,

> +			rc);

> +	}

> +

> +	xleaf_put_leaf(br_data->pdev, axigate_leaf);

> +

> +	return rc;

> +}

> +

> +const struct fpga_bridge_ops xmgmt_bridge_ops = {

> +	.enable_set = xmgmt_br_enable_set

> +};

> +

> +static void xmgmt_destroy_bridge(struct fpga_bridge *br)

> +{

> +	struct xmgmt_bridge *br_data = br->priv;

> +

> +	if (!br_data)

> +		return;

> +

> +	xrt_info(br_data->pdev, "destroy fpga bridge %s", br_data->bridge_name);

> +	fpga_bridge_unregister(br);

> +

> +	devm_kfree(DEV(br_data->pdev), br_data);

> +

> +	fpga_bridge_free(br);

> +}

> +

> +static struct fpga_bridge *xmgmt_create_bridge(struct platform_device *pdev,

> +					       char *dtb)

> +{

> +	struct fpga_bridge *br = NULL;

> +	struct xmgmt_bridge *br_data;

> +	const char *gate;

> +	int rc;

> +

> +	br_data = devm_kzalloc(DEV(pdev), sizeof(*br_data), GFP_KERNEL);

> +	if (!br_data)

> +		return NULL;

> +	br_data->pdev = pdev;

> +

> +	br_data->bridge_name = XRT_MD_NODE_GATE_ULP;

> +	rc = xrt_md_find_endpoint(&pdev->dev, dtb, XRT_MD_NODE_GATE_ULP,

> +				  NULL, &gate);

> +	if (rc) {

> +		br_data->bridge_name = XRT_MD_NODE_GATE_PLP;

> +		rc = xrt_md_find_endpoint(&pdev->dev, dtb, XRT_MD_NODE_GATE_PLP,

> +					  NULL, &gate);

> +	}

> +	if (rc) {

> +		xrt_err(pdev, "failed to get axigate, rc %d", rc);

> +		goto failed;

> +	}

> +

> +	br = fpga_bridge_create(DEV(pdev), br_data->bridge_name,

> +				&xmgmt_bridge_ops, br_data);

> +	if (!br) {

> +		xrt_err(pdev, "failed to create bridge");

> +		goto failed;

> +	}

> +

> +	rc = fpga_bridge_register(br);

> +	if (rc) {

> +		xrt_err(pdev, "failed to register bridge, rc %d", rc);

> +		goto failed;

> +	}

> +

> +	xrt_info(pdev, "created fpga bridge %s", br_data->bridge_name);

> +

> +	return br;

> +

> +failed:

> +	if (br)

> +		fpga_bridge_free(br);

> +	if (br_data)

> +		devm_kfree(DEV(pdev), br_data);

> +

> +	return NULL;

> +}

> +

> +static void xmgmt_destroy_region(struct fpga_region *region)

ok
> +{

> +	struct xmgmt_region *r_data = region->priv;

> +

> +	xrt_info(r_data->pdev, "destroy fpga region %llx.%llx",

> +		 region->compat_id->id_l, region->compat_id->id_h);

are the args ordered correctly ? I expected id_h to be first.
> +

> +	fpga_region_unregister(region);

> +

> +	if (r_data->group_instance > 0)

> +		xleaf_destroy_group(r_data->pdev, r_data->group_instance);

> +

> +	if (r_data->bridge)

> +		xmgmt_destroy_bridge(r_data->bridge);

> +

> +	if (r_data->region->info) {

> +		fpga_image_info_free(r_data->region->info);

> +		r_data->region->info = NULL;

> +	}

> +

> +	fpga_region_free(region);

> +

> +	devm_kfree(DEV(r_data->pdev), r_data);

> +}

> +

> +static int xmgmt_region_match(struct device *dev, const void *data)

> +{

> +	const struct xmgmt_region_match_arg *arg = data;

> +	const struct fpga_region *match_region;

ok
> +	uuid_t compat_uuid;

> +	int i;

> +

> +	if (dev->parent != &arg->pdev->dev)

> +		return false;

> +

> +	match_region = to_fpga_region(dev);

> +	/*

> +	 * The device tree provides both parent and child uuids for an

> +	 * xclbin in one array. Here we try both uuids to see if it matches

> +	 * with target region's compat_id. Strictly speaking we should

> +	 * only match xclbin's parent uuid with target region's compat_id

> +	 * but given the uuids by design are unique comparing with both

> +	 * does not hurt.

> +	 */

> +	import_uuid(&compat_uuid, (const char *)match_region->compat_id);

> +	for (i = 0; i < arg->uuid_num; i++) {

> +		if (uuid_equal(&compat_uuid, &arg->uuids[i]))

> +			return true;

> +	}

> +

> +	return false;

> +}

> +

> +static int xmgmt_region_match_base(struct device *dev, const void *data)

> +{

> +	const struct xmgmt_region_match_arg *arg = data;

> +	const struct fpga_region *match_region;

> +	const struct xmgmt_region *r_data;

> +

> +	if (dev->parent != &arg->pdev->dev)

> +		return false;

> +

> +	match_region = to_fpga_region(dev);

> +	r_data = match_region->priv;

> +	if (uuid_is_null(&r_data->dep_uuid))

> +		return true;

> +

> +	return false;

> +}

> +

> +static int xmgmt_region_match_by_uuid(struct device *dev, const void *data)

ok
> +{

> +	const struct xmgmt_region_match_arg *arg = data;

> +	const struct fpga_region *match_region;

> +	const struct xmgmt_region *r_data;

> +

> +	if (dev->parent != &arg->pdev->dev)

> +		return false;

> +

> +	if (arg->uuid_num != 1)

> +		return false;

ok
> +

> +	match_region = to_fpga_region(dev);

> +	r_data = match_region->priv;

> +	if (uuid_equal(&r_data->dep_uuid, arg->uuids))

> +		return true;

> +

> +	return false;

> +}

> +

> +static void xmgmt_region_cleanup(struct fpga_region *region)

> +{

> +	struct xmgmt_region *r_data = region->priv, *pdata, *temp;

> +	struct platform_device *pdev = r_data->pdev;

> +	struct xmgmt_region_match_arg arg = { 0 };

> +	struct fpga_region *match_region = NULL;

> +	struct device *start_dev = NULL;

> +	LIST_HEAD(free_list);

> +	uuid_t compat_uuid;

> +

> +	list_add_tail(&r_data->list, &free_list);

> +	arg.pdev = pdev;

> +	arg.uuid_num = 1;

> +	arg.uuids = &compat_uuid;

> +

> +	/* find all regions depending on this region */

> +	list_for_each_entry_safe(pdata, temp, &free_list, list) {

ok
> +		import_uuid(arg.uuids, (const char *)pdata->region->compat_id);

> +		start_dev = NULL;

> +		while ((match_region = fpga_region_class_find(start_dev, &arg,

> +							      xmgmt_region_match_by_uuid))) {

> +			pdata = match_region->priv;

> +			list_add_tail(&pdata->list, &free_list);

> +			start_dev = &match_region->dev;

> +			put_device(&match_region->dev);

> +		}

> +	}

> +

> +	list_del(&r_data->list);

> +

> +	list_for_each_entry_safe_reverse(pdata, temp, &free_list, list)

> +		xmgmt_destroy_region(pdata->region);

> +

> +	if (r_data->group_instance > 0) {

> +		xleaf_destroy_group(pdev, r_data->group_instance);

> +		r_data->group_instance = -1;

> +	}

> +	if (r_data->region->info) {

> +		fpga_image_info_free(r_data->region->info);

> +		r_data->region->info = NULL;

> +	}

> +}

> +

> +void xmgmt_region_cleanup_all(struct platform_device *pdev)

> +{

> +	struct xmgmt_region_match_arg arg = { 0 };

> +	struct fpga_region *base_region;

> +

> +	arg.pdev = pdev;

> +

> +	while ((base_region = fpga_region_class_find(NULL, &arg, xmgmt_region_match_base))) {

ok
> +		put_device(&base_region->dev);

> +

> +		xmgmt_region_cleanup(base_region);

> +		xmgmt_destroy_region(base_region);

> +	}

> +}

> +

> +/*

> + * Program a region with a xclbin image. Bring up the subdevs and the

ok
> + * group object to contain the subdevs.

> + */

> +static int xmgmt_region_program(struct fpga_region *region, const void *xclbin, char *dtb)

> +{

> +	const struct axlf *xclbin_obj = xclbin;

> +	struct fpga_image_info *info;

> +	struct platform_device *pdev;

> +	struct xmgmt_region *r_data;

> +	int rc;

> +

> +	r_data = region->priv;

> +	pdev = r_data->pdev;

> +

> +	info = fpga_image_info_alloc(&pdev->dev);

> +	if (!info)

> +		return -ENOMEM;

> +

> +	info->buf = xclbin;

> +	info->count = xclbin_obj->header.length;

> +	info->flags |= FPGA_MGR_PARTIAL_RECONFIG;

> +	region->info = info;

> +	rc = fpga_region_program_fpga(region);

> +	if (rc) {

> +		xrt_err(pdev, "programming xclbin failed, rc %d", rc);

> +		return rc;

> +	}

> +

> +	/* free bridges to allow reprogram */

> +	if (region->get_bridges)

> +		fpga_bridges_put(&region->bridge_list);

> +

> +	/*

> +	 * Next bringup the subdevs for this region which will be managed by

> +	 * its own group object.

> +	 */

> +	r_data->group_instance = xleaf_create_group(pdev, dtb);

> +	if (r_data->group_instance < 0) {

> +		xrt_err(pdev, "failed to create group, rc %d",

> +			r_data->group_instance);

> +		rc = r_data->group_instance;

> +		return rc;

> +	}

> +

> +	rc = xleaf_wait_for_group_bringup(pdev);

> +	if (rc)

> +		xrt_err(pdev, "group bringup failed, rc %d", rc);

> +	return rc;

> +}

> +

> +static int xmgmt_get_bridges(struct fpga_region *region)

> +{

> +	struct xmgmt_region *r_data = region->priv;

> +	struct device *dev = &r_data->pdev->dev;

> +

> +	return fpga_bridge_get_to_list(dev, region->info, &region->bridge_list);

> +}

> +

> +/*

> + * Program/create FPGA regions based on input xclbin file.

ok, dropped sentence
> + * 1. Identify a matching existing region for this xclbin

> + * 2. Tear down any previous objects for the found region

> + * 3. Program this region with input xclbin

> + * 4. Iterate over this region's interface uuids to determine if it defines any

> + *    child region. Create fpga_region for the child region.

> + */

> +int xmgmt_process_xclbin(struct platform_device *pdev,

> +			 struct fpga_manager *fmgr,

> +			 const struct axlf *xclbin,

> +			 enum provider_kind kind)

> +{

> +	struct fpga_region *region, *compat_region = NULL;

> +	struct xmgmt_region_match_arg arg = { 0 };

ok
> +	struct xmgmt_region *r_data;

> +	uuid_t compat_uuid;

> +	char *dtb = NULL;

> +	int rc, i;

> +

> +	rc = xrt_xclbin_get_metadata(DEV(pdev), xclbin, &dtb);

> +	if (rc) {

> +		xrt_err(pdev, "failed to get dtb: %d", rc);

> +		goto failed;

> +	}

> +

> +	rc = xrt_md_get_interface_uuids(DEV(pdev), dtb, 0, NULL);

> +	if (rc < 0) {

> +		xrt_err(pdev, "failed to get intf uuid");

> +		rc = -EINVAL;

ok
> +		goto failed;

> +	}

> +	arg.uuid_num = rc;

> +	arg.uuids = vzalloc(sizeof(uuid_t) * arg.uuid_num);

uuids small, convert to bzalloc
> +	if (!arg.uuids) {

> +		rc = -ENOMEM;

> +		goto failed;

> +	}

> +	arg.pdev = pdev;

> +

> +	rc = xrt_md_get_interface_uuids(DEV(pdev), dtb, arg.uuid_num, arg.uuids);

> +	if (rc != arg.uuid_num) {

> +		xrt_err(pdev, "only get %d uuids, expect %d", rc, arg.uuid_num);

> +		rc = -EINVAL;

> +		goto failed;

> +	}

> +

> +	/* if this is not base firmware, search for a compatible region */

> +	if (kind != XMGMT_BLP) {

> +		compat_region = fpga_region_class_find(NULL, &arg, xmgmt_region_match);

> +		if (!compat_region) {

> +			xrt_err(pdev, "failed to get compatible region");

> +			rc = -ENOENT;

> +			goto failed;

> +		}

> +

> +		xmgmt_region_cleanup(compat_region);

> +

> +		rc = xmgmt_region_program(compat_region, xclbin, dtb);

> +		if (rc) {

> +			xrt_err(pdev, "failed to program region");

> +			goto failed;

> +		}

> +	}

> +

> +	if (compat_region)

> +		import_uuid(&compat_uuid, (const char *)compat_region->compat_id);

> +

> +	/* create all the new regions contained in this xclbin */

> +	for (i = 0; i < arg.uuid_num; i++) {

> +		if (compat_region && uuid_equal(&compat_uuid, &arg.uuids[i])) {

> +			/* region for this interface already exists */

> +			continue;

> +		}

> +

> +		region = fpga_region_create(DEV(pdev), fmgr, xmgmt_get_bridges);

> +		if (!region) {

> +			xrt_err(pdev, "failed to create fpga region");

> +			rc = -EFAULT;

> +			goto failed;

> +		}

> +		r_data = devm_kzalloc(DEV(pdev), sizeof(*r_data), GFP_KERNEL);

> +		if (!r_data) {

> +			rc = -ENOMEM;

> +			fpga_region_free(region);

> +			goto failed;

> +		}

> +		r_data->pdev = pdev;

> +		r_data->region = region;

> +		r_data->group_instance = -1;

> +		uuid_copy(&r_data->intf_uuid, &arg.uuids[i]);

> +		if (compat_region)

> +			import_uuid(&r_data->dep_uuid, (const char *)compat_region->compat_id);

> +		r_data->bridge = xmgmt_create_bridge(pdev, dtb);

> +		if (!r_data->bridge) {

> +			xrt_err(pdev, "failed to create fpga bridge");

> +			rc = -EFAULT;

> +			devm_kfree(DEV(pdev), r_data);

> +			fpga_region_free(region);

> +			goto failed;

> +		}

> +

> +		region->compat_id = &r_data->compat_id;

> +		export_uuid((char *)region->compat_id, &r_data->intf_uuid);

> +		region->priv = r_data;

> +

> +		rc = fpga_region_register(region);

> +		if (rc) {

> +			xrt_err(pdev, "failed to register fpga region");

> +			xmgmt_destroy_bridge(r_data->bridge);

> +			fpga_region_free(region);

> +			devm_kfree(DEV(pdev), r_data);

> +			goto failed;

> +		}

> +

> +		xrt_info(pdev, "created fpga region %llx%llx",

> +			 region->compat_id->id_l, region->compat_id->id_h);


see above comment on id_h

destroy's info used %llx.%llx, for consistency need to add or remove a '.'

Tom

> +	}

> +

> +	if (compat_region)

> +		put_device(&compat_region->dev);

> +	vfree(dtb);

> +	return 0;

> +

> +failed:

> +	if (compat_region) {

> +		put_device(&compat_region->dev);

> +		xmgmt_region_cleanup(compat_region);

> +	} else {

> +		xmgmt_region_cleanup_all(pdev);

> +	}

> +

> +	vfree(dtb);

> +	return rc;

> +}
Tom Rix April 2, 2021, 2:27 p.m. UTC | #7
On 3/23/21 10:29 PM, Lizhi Hou wrote:
> Add User Clock Subsystem (UCS) driver. UCS is a hardware function

ok
> discovered by walking xclbin metadata. A platform device node will be

> created for it.  UCS enables/disables the dynamic region clocks.

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>  drivers/fpga/xrt/lib/xleaf/ucs.c | 167 +++++++++++++++++++++++++++++++

ok on removing ucs.h
>  1 file changed, 167 insertions(+)

>  create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c

>

> diff --git a/drivers/fpga/xrt/lib/xleaf/ucs.c b/drivers/fpga/xrt/lib/xleaf/ucs.c

> new file mode 100644

> index 000000000000..d91ee229e7cb

> --- /dev/null

> +++ b/drivers/fpga/xrt/lib/xleaf/ucs.c

> @@ -0,0 +1,167 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * Xilinx Alveo FPGA UCS Driver

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *      Lizhi Hou<Lizhi.Hou@xilinx.com>

> + */

> +

> +#include <linux/mod_devicetable.h>

> +#include <linux/platform_device.h>

> +#include <linux/delay.h>

> +#include <linux/device.h>

> +#include <linux/regmap.h>

> +#include <linux/io.h>

> +#include "metadata.h"

> +#include "xleaf.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 XRT_UCS_CHANNEL1_REG			0

> +#define XRT_UCS_CHANNEL2_REG			8

> +

> +#define CLK_MAX_VALUE			6400

> +

> +static const struct regmap_config ucs_regmap_config = {

> +	.reg_bits = 32,

> +	.val_bits = 32,

> +	.reg_stride = 4,

> +	.max_register = 0x1000,

> +};

> +

> +struct xrt_ucs {

> +	struct platform_device	*pdev;

> +	struct regmap		*regmap;

ok
> +	struct mutex		ucs_lock; /* ucs dev lock */

> +};

> +

> +static void xrt_ucs_event_cb(struct platform_device *pdev, void *arg)

> +{

> +	struct xrt_event *evt = (struct xrt_event *)arg;

> +	enum xrt_events e = evt->xe_evt;

> +	struct platform_device *leaf;

> +	enum xrt_subdev_id id;

> +	int instance;

> +

> +	id = evt->xe_subdev.xevt_subdev_id;

> +	instance = evt->xe_subdev.xevt_subdev_instance;

> +

> +	if (e != XRT_EVENT_POST_CREATION) {

> +		xrt_dbg(pdev, "ignored event %d", e);

> +		return;

> +	}

> +

> +	if (id != XRT_SUBDEV_CLOCK)

> +		return;

ok
> +

> +	leaf = xleaf_get_leaf_by_id(pdev, XRT_SUBDEV_CLOCK, instance);

> +	if (!leaf) {

> +		xrt_err(pdev, "does not get clock subdev");

> +		return;

> +	}

> +

> +	xleaf_call(leaf, XRT_CLOCK_VERIFY, NULL);

> +	xleaf_put_leaf(pdev, leaf);

> +}

ok on removing ucs_check.
> +

> +static int ucs_enable(struct xrt_ucs *ucs)

> +{

> +	int ret;

> +

> +	mutex_lock(&ucs->ucs_lock);

ok
> +	ret = regmap_write(ucs->regmap, XRT_UCS_CHANNEL2_REG, 1);

> +	mutex_unlock(&ucs->ucs_lock);

> +

> +	return ret;

> +}

> +

> +static int

> +xrt_ucs_leaf_call(struct platform_device *pdev, u32 cmd, void *arg)


ok

Looks fine.

Reviewed-by: Tom Rix <trix@redhat.com>


> +{

> +	switch (cmd) {

> +	case XRT_XLEAF_EVENT:

> +		xrt_ucs_event_cb(pdev, arg);

> +		break;

> +	default:

> +		xrt_err(pdev, "unsupported cmd %d", cmd);

> +		return -EINVAL;

> +	}

> +

> +	return 0;

> +}

> +

> +static int ucs_probe(struct platform_device *pdev)

> +{

> +	struct xrt_ucs *ucs = NULL;

> +	void __iomem *base = NULL;

> +	struct resource *res;

> +

> +	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);

> +	if (!res)

> +		return -EINVAL;

> +

> +	base = devm_ioremap_resource(&pdev->dev, res);

> +	if (IS_ERR(base))

> +		return PTR_ERR(base);

> +

> +	ucs->regmap = devm_regmap_init_mmio(&pdev->dev, base, &ucs_regmap_config);

> +	if (IS_ERR(ucs->regmap)) {

> +		UCS_ERR(ucs, "map base %pR failed", res);

> +		return PTR_ERR(ucs->regmap);

> +	}

> +	ucs_enable(ucs);

> +

> +	return 0;

> +}

> +

> +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_leaf_call = xrt_ucs_leaf_call,

> +	},

> +};

> +

> +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,

> +	.id_table = xrt_ucs_table,

> +};

> +

> +XRT_LEAF_INIT_FINI_FUNC(XRT_SUBDEV_UCS, ucs);
Max Zhen April 5, 2021, 8:53 p.m. UTC | #8
Hi Tom,


On 3/30/21 8:11 AM, Tom Rix wrote:
> This was split from 'fpga: xrt: platform driver infrastructure'

>

> and fpga: xrt: managment physical function driver (root)



Yes, trying not to have huge patch for review.


>

>

> On 3/23/21 10:29 PM, Lizhi Hou wrote:

>> Contains common code for all root drivers and handles root calls from

>> platform drivers. This is part of root driver infrastructure.

>>

>> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

>> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

>> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

>> ---

>>   drivers/fpga/xrt/include/events.h  |  45 +++

>>   drivers/fpga/xrt/include/xroot.h   | 117 ++++++

>>   drivers/fpga/xrt/lib/subdev_pool.h |  53 +++

>>   drivers/fpga/xrt/lib/xroot.c       | 589 +++++++++++++++++++++++++++++

>>   4 files changed, 804 insertions(+)

>>   create mode 100644 drivers/fpga/xrt/include/events.h

>>   create mode 100644 drivers/fpga/xrt/include/xroot.h

>>   create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h

>>   create mode 100644 drivers/fpga/xrt/lib/xroot.c

>>

>> diff --git a/drivers/fpga/xrt/include/events.h b/drivers/fpga/xrt/include/events.h

>> new file mode 100644

>> index 000000000000..775171a47c8e

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/include/events.h

>> @@ -0,0 +1,45 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

> ok

>> + *

>> + * Authors:

>> + *   Cheng Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#ifndef _XRT_EVENTS_H_

>> +#define _XRT_EVENTS_H_

> ok

>> +

>> +#include "subdev_id.h"

>> +

>> +/*

>> + * Event notification.

>> + */

>> +enum xrt_events {

>> +     XRT_EVENT_TEST = 0, /* for testing */

>> +     /*

>> +      * Events related to specific subdev

>> +      * Callback arg: struct xrt_event_arg_subdev

>> +      */

>> +     XRT_EVENT_POST_CREATION,

>> +     XRT_EVENT_PRE_REMOVAL,

>> +     /*

>> +      * Events related to change of the whole board

>> +      * Callback arg: <none>

>> +      */

>> +     XRT_EVENT_PRE_HOT_RESET,

>> +     XRT_EVENT_POST_HOT_RESET,

>> +     XRT_EVENT_PRE_GATE_CLOSE,

>> +     XRT_EVENT_POST_GATE_OPEN,

>> +};

>> +

>> +struct xrt_event_arg_subdev {

>> +     enum xrt_subdev_id xevt_subdev_id;

>> +     int xevt_subdev_instance;

>> +};

>> +

>> +struct xrt_event {

>> +     enum xrt_events xe_evt;

>> +     struct xrt_event_arg_subdev xe_subdev;

>> +};

>> +

>> +#endif       /* _XRT_EVENTS_H_ */

>> diff --git a/drivers/fpga/xrt/include/xroot.h b/drivers/fpga/xrt/include/xroot.h

>> new file mode 100644

>> index 000000000000..91c0aeb30bf8

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/include/xroot.h

>> @@ -0,0 +1,117 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *   Cheng Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#ifndef _XRT_ROOT_H_

>> +#define _XRT_ROOT_H_

>> +

>> +#include <linux/platform_device.h>

>> +#include <linux/pci.h>

>> +#include "subdev_id.h"

>> +#include "events.h"

>> +

>> +typedef bool (*xrt_subdev_match_t)(enum xrt_subdev_id,

>> +     struct platform_device *, void *);

>> +#define XRT_SUBDEV_MATCH_PREV        ((xrt_subdev_match_t)-1)

>> +#define XRT_SUBDEV_MATCH_NEXT        ((xrt_subdev_match_t)-2)

>> +

>> +/*

>> + * Root calls.

>> + */

>> +enum xrt_root_cmd {

>> +     /* Leaf actions. */

>> +     XRT_ROOT_GET_LEAF = 0,

>> +     XRT_ROOT_PUT_LEAF,

>> +     XRT_ROOT_GET_LEAF_HOLDERS,

>> +

>> +     /* Group actions. */

>> +     XRT_ROOT_CREATE_GROUP,

>> +     XRT_ROOT_REMOVE_GROUP,

>> +     XRT_ROOT_LOOKUP_GROUP,

>> +     XRT_ROOT_WAIT_GROUP_BRINGUP,

>> +

>> +     /* Event actions. */

>> +     XRT_ROOT_EVENT_SYNC,

>> +     XRT_ROOT_EVENT_ASYNC,

>> +

>> +     /* Device info. */

>> +     XRT_ROOT_GET_RESOURCE,

>> +     XRT_ROOT_GET_ID,

>> +

>> +     /* Misc. */

>> +     XRT_ROOT_HOT_RESET,

>> +     XRT_ROOT_HWMON,

>> +};

>> +

>> +struct xrt_root_get_leaf {

>> +     struct platform_device *xpigl_caller_pdev;

>> +     xrt_subdev_match_t xpigl_match_cb;

>> +     void *xpigl_match_arg;

>> +     struct platform_device *xpigl_tgt_pdev;

>> +};

>> +

>> +struct xrt_root_put_leaf {

>> +     struct platform_device *xpipl_caller_pdev;

>> +     struct platform_device *xpipl_tgt_pdev;

>> +};

>> +

>> +struct xrt_root_lookup_group {

>> +     struct platform_device *xpilp_pdev; /* caller's pdev */

>> +     xrt_subdev_match_t xpilp_match_cb;

>> +     void *xpilp_match_arg;

>> +     int xpilp_grp_inst;

>> +};

>> +

>> +struct xrt_root_get_holders {

>> +     struct platform_device *xpigh_pdev; /* caller's pdev */

>> +     char *xpigh_holder_buf;

>> +     size_t xpigh_holder_buf_len;

>> +};

>> +

>> +struct xrt_root_get_res {

>> +     struct resource *xpigr_res;

>> +};

>> +

>> +struct xrt_root_get_id {

>> +     unsigned short  xpigi_vendor_id;

>> +     unsigned short  xpigi_device_id;

>> +     unsigned short  xpigi_sub_vendor_id;

>> +     unsigned short  xpigi_sub_device_id;

>> +};

>> +

>> +struct xrt_root_hwmon {

>> +     bool xpih_register;

>> +     const char *xpih_name;

>> +     void *xpih_drvdata;

>> +     const struct attribute_group **xpih_groups;

>> +     struct device *xpih_hwmon_dev;

>> +};

>> +

>> +/*

>> + * Callback for leaf to make a root request. Arguments are: parent device, parent cookie, req,

>> + * and arg.

>> + */

>> +typedef int (*xrt_subdev_root_cb_t)(struct device *, void *, u32, void *);

>> +int xrt_subdev_root_request(struct platform_device *self, u32 cmd, void *arg);

>> +

>> +/*

>> + * Defines physical function (MPF / UPF) specific operations

>> + * needed in common root driver.

>> + */

>> +struct xroot_physical_function_callback {

>> +     void (*xpc_hot_reset)(struct pci_dev *pdev);

>> +};

>> +

>> +int xroot_probe(struct pci_dev *pdev, struct xroot_physical_function_callback *cb, void **root);

>> +void xroot_remove(void *root);

>> +bool xroot_wait_for_bringup(void *root);

>> +int xroot_add_vsec_node(void *root, char *dtb);

>> +int xroot_create_group(void *xr, char *dtb);

>> +int xroot_add_simple_node(void *root, char *dtb, const char *endpoint);

>> +void xroot_broadcast(void *root, enum xrt_events evt);

>> +

>> +#endif       /* _XRT_ROOT_H_ */

>> diff --git a/drivers/fpga/xrt/lib/subdev_pool.h b/drivers/fpga/xrt/lib/subdev_pool.h

>> new file mode 100644

>> index 000000000000..09d148e4e7ea

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/lib/subdev_pool.h

>> @@ -0,0 +1,53 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *   Cheng Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#ifndef _XRT_SUBDEV_POOL_H_

>> +#define _XRT_SUBDEV_POOL_H_

>> +

>> +#include <linux/device.h>

>> +#include <linux/mutex.h>

>> +#include "xroot.h"

>> +

>> +/*

>> + * The struct xrt_subdev_pool manages a list of xrt_subdevs for root and group drivers.

>> + */

>> +struct xrt_subdev_pool {

>> +     struct list_head xsp_dev_list;

>> +     struct device *xsp_owner;

>> +     struct mutex xsp_lock; /* pool lock */

>> +     bool xsp_closing;

>> +};

>> +

>> +/*

>> + * Subdev pool helper functions for root and group drivers only.

>> + */

>> +void xrt_subdev_pool_init(struct device *dev,

>> +                       struct xrt_subdev_pool *spool);

>> +void xrt_subdev_pool_fini(struct xrt_subdev_pool *spool);

>> +int xrt_subdev_pool_get(struct xrt_subdev_pool *spool,

>> +                     xrt_subdev_match_t match,

>> +                     void *arg, struct device *holder_dev,

>> +                     struct platform_device **pdevp);

>> +int xrt_subdev_pool_put(struct xrt_subdev_pool *spool,

>> +                     struct platform_device *pdev,

>> +                     struct device *holder_dev);

>> +int xrt_subdev_pool_add(struct xrt_subdev_pool *spool,

>> +                     enum xrt_subdev_id id, xrt_subdev_root_cb_t pcb,

>> +                     void *pcb_arg, char *dtb);

>> +int xrt_subdev_pool_del(struct xrt_subdev_pool *spool,

>> +                     enum xrt_subdev_id id, int instance);

>> +ssize_t xrt_subdev_pool_get_holders(struct xrt_subdev_pool *spool,

>> +                                 struct platform_device *pdev,

>> +                                 char *buf, size_t len);

>> +

>> +void xrt_subdev_pool_trigger_event(struct xrt_subdev_pool *spool,

>> +                                enum xrt_events evt);

>> +void xrt_subdev_pool_handle_event(struct xrt_subdev_pool *spool,

>> +                               struct xrt_event *evt);

>> +

>> +#endif       /* _XRT_SUBDEV_POOL_H_ */

>> diff --git a/drivers/fpga/xrt/lib/xroot.c b/drivers/fpga/xrt/lib/xroot.c

>> new file mode 100644

>> index 000000000000..03407272650f

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/lib/xroot.c

>> @@ -0,0 +1,589 @@

>> +// SPDX-License-Identifier: GPL-2.0

>> +/*

>> + * Xilinx Alveo FPGA Root Functions

>> + *

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *   Cheng Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#include <linux/module.h>

>> +#include <linux/pci.h>

>> +#include <linux/hwmon.h>

>> +#include "xroot.h"

>> +#include "subdev_pool.h"

>> +#include "group.h"

>> +#include "metadata.h"

>> +

>> +#define XROOT_PDEV(xr)               ((xr)->pdev)

>> +#define XROOT_DEV(xr)                (&(XROOT_PDEV(xr)->dev))

>> +#define xroot_err(xr, fmt, args...)  \

>> +     dev_err(XROOT_DEV(xr), "%s: " fmt, __func__, ##args)

>> +#define xroot_warn(xr, fmt, args...) \

>> +     dev_warn(XROOT_DEV(xr), "%s: " fmt, __func__, ##args)

>> +#define xroot_info(xr, fmt, args...) \

>> +     dev_info(XROOT_DEV(xr), "%s: " fmt, __func__, ##args)

>> +#define xroot_dbg(xr, fmt, args...)  \

>> +     dev_dbg(XROOT_DEV(xr), "%s: " fmt, __func__, ##args)

>> +

>> +#define XRT_VSEC_ID          0x20

>> +

> 'root' is an abstraction, 'pci' is an implementation.

>

> Consider splitting.

>

> I think this will be part of the pseudo bus, so figure out how to do root there.



Yes, we will remove all PCI specific code from infra code (xroot.c, 
subdev.c and group.c). They will be moved to xrt-mgmt.ko, which is 
indeed a PCI device driver.


>

>

>> +#define XROOT_GROUP_FIRST            (-1)

>> +#define XROOT_GROUP_LAST             (-2)

>> +

>> +static int xroot_root_cb(struct device *, void *, u32, void *);

>> +

>> +struct xroot_evt {

>> +     struct list_head list;

>> +     struct xrt_event evt;

>> +     struct completion comp;

>> +     bool async;

>> +};

>> +

>> +struct xroot_events {

>> +     struct mutex evt_lock; /* event lock */

>> +     struct list_head evt_list;

>> +     struct work_struct evt_work;

>> +};

>> +

>> +struct xroot_groups {

>> +     struct xrt_subdev_pool pool;

>> +     struct work_struct bringup_work;

> add a comment that these two elements are counters or append '_cnt' or similar to name



Will add '_cnt' to the names.

Thanks,

Max

>> +     atomic_t bringup_pending;

>> +     atomic_t bringup_failed;

>> +     struct completion bringup_comp;

>> +};

>> +

>> +struct xroot {

>> +     struct pci_dev *pdev;

>> +     struct xroot_events events;

>> +     struct xroot_groups groups;

>> +     struct xroot_physical_function_callback pf_cb;

> ok

>> +};

>> +

>> +struct xroot_group_match_arg {

>> +     enum xrt_subdev_id id;

>> +     int instance;

>> +};

>> +

>> +static bool xroot_group_match(enum xrt_subdev_id id, struct platform_device *pdev, void *arg)

>> +{

>> +     struct xroot_group_match_arg *a = (struct xroot_group_match_arg *)arg;

>> +

>> +     /* pdev->id is the instance of the subdev. */

> ok

>> +     return id == a->id && pdev->id == a->instance;

>> +}

>> +

>> +static int xroot_get_group(struct xroot *xr, int instance, struct platform_device **grpp)

>> +{

>> +     int rc = 0;

>> +     struct xrt_subdev_pool *grps = &xr->groups.pool;

>> +     struct device *dev = DEV(xr->pdev);

>> +     struct xroot_group_match_arg arg = { XRT_SUBDEV_GRP, instance };

>> +

>> +     if (instance == XROOT_GROUP_LAST) {

>> +             rc = xrt_subdev_pool_get(grps, XRT_SUBDEV_MATCH_NEXT,

>> +                                      *grpp, dev, grpp);

>> +     } else if (instance == XROOT_GROUP_FIRST) {

>> +             rc = xrt_subdev_pool_get(grps, XRT_SUBDEV_MATCH_PREV,

>> +                                      *grpp, dev, grpp);

>> +     } else {

>> +             rc = xrt_subdev_pool_get(grps, xroot_group_match,

>> +                                      &arg, dev, grpp);

>> +     }

>> +

>> +     if (rc && rc != -ENOENT)

>> +             xroot_err(xr, "failed to hold group %d: %d", instance, rc);

>> +     return rc;

>> +}

>> +

>> +static void xroot_put_group(struct xroot *xr, struct platform_device *grp)

>> +{

>> +     int inst = grp->id;

>> +     int rc = xrt_subdev_pool_put(&xr->groups.pool, grp, DEV(xr->pdev));

>> +

>> +     if (rc)

>> +             xroot_err(xr, "failed to release group %d: %d", inst, rc);

>> +}

>> +

>> +static int xroot_trigger_event(struct xroot *xr, struct xrt_event *e, bool async)

>> +{

>> +     struct xroot_evt *enew = vzalloc(sizeof(*enew));

>> +

>> +     if (!enew)

>> +             return -ENOMEM;

>> +

>> +     enew->evt = *e;

>> +     enew->async = async;

>> +     init_completion(&enew->comp);

>> +

>> +     mutex_lock(&xr->events.evt_lock);

>> +     list_add(&enew->list, &xr->events.evt_list);

>> +     mutex_unlock(&xr->events.evt_lock);

>> +

>> +     schedule_work(&xr->events.evt_work);

>> +

>> +     if (async)

>> +             return 0;

>> +

>> +     wait_for_completion(&enew->comp);

>> +     vfree(enew);

>> +     return 0;

>> +}

>> +

>> +static void

>> +xroot_group_trigger_event(struct xroot *xr, int inst, enum xrt_events e)

>> +{

>> +     int ret;

>> +     struct platform_device *pdev = NULL;

>> +     struct xrt_event evt = { 0 };

>> +

>> +     WARN_ON(inst < 0);

>> +     /* Only triggers subdev specific events. */

>> +     if (e != XRT_EVENT_POST_CREATION && e != XRT_EVENT_PRE_REMOVAL) {

>> +             xroot_err(xr, "invalid event %d", e);

>> +             return;

>> +     }

>> +

>> +     ret = xroot_get_group(xr, inst, &pdev);

>> +     if (ret)

>> +             return;

>> +

>> +     /* Triggers event for children, first. */

>> +     xleaf_call(pdev, XRT_GROUP_TRIGGER_EVENT, (void *)(uintptr_t)e);

> ok

>> +

>> +     /* Triggers event for itself. */

>> +     evt.xe_evt = e;

>> +     evt.xe_subdev.xevt_subdev_id = XRT_SUBDEV_GRP;

>> +     evt.xe_subdev.xevt_subdev_instance = inst;

>> +     xroot_trigger_event(xr, &evt, false);

>> +

>> +     xroot_put_group(xr, pdev);

>> +}

>> +

>> +int xroot_create_group(void *root, char *dtb)

>> +{

>> +     struct xroot *xr = (struct xroot *)root;

>> +     int ret;

>> +

>> +     atomic_inc(&xr->groups.bringup_pending);

>> +     ret = xrt_subdev_pool_add(&xr->groups.pool, XRT_SUBDEV_GRP, xroot_root_cb, xr, dtb);

>> +     if (ret >= 0) {

>> +             schedule_work(&xr->groups.bringup_work);

>> +     } else {

>> +             atomic_dec(&xr->groups.bringup_pending);

>> +             atomic_inc(&xr->groups.bringup_failed);

>> +             xroot_err(xr, "failed to create group: %d", ret);

>> +     }

>> +     return ret;

>> +}

>> +EXPORT_SYMBOL_GPL(xroot_create_group);

>> +

>> +static int xroot_destroy_single_group(struct xroot *xr, int instance)

>> +{

> ok as-is

>> +     struct platform_device *pdev = NULL;

>> +     int ret;

>> +

>> +     WARN_ON(instance < 0);

>> +     ret = xroot_get_group(xr, instance, &pdev);

>> +     if (ret)

>> +             return ret;

>> +

>> +     xroot_group_trigger_event(xr, instance, XRT_EVENT_PRE_REMOVAL);

>> +

>> +     /* Now tear down all children in this group. */

>> +     ret = xleaf_call(pdev, XRT_GROUP_FINI_CHILDREN, NULL);

>> +     xroot_put_group(xr, pdev);

>> +     if (!ret)

>> +             ret = xrt_subdev_pool_del(&xr->groups.pool, XRT_SUBDEV_GRP, instance);

>> +

>> +     return ret;

>> +}

>> +

>> +static int xroot_destroy_group(struct xroot *xr, int instance)

>> +{

>> +     struct platform_device *target = NULL;

>> +     struct platform_device *deps = NULL;

>> +     int ret;

>> +

>> +     WARN_ON(instance < 0);

>> +     /*

>> +      * Make sure target group exists and can't go away before

>> +      * we remove it's dependents

>> +      */

>> +     ret = xroot_get_group(xr, instance, &target);

>> +     if (ret)

>> +             return ret;

>> +

>> +     /*

>> +      * Remove all groups depend on target one.

>> +      * Assuming subdevs in higher group ID can depend on ones in

>> +      * lower ID groups, we remove them in the reservse order.

>> +      */

>> +     while (xroot_get_group(xr, XROOT_GROUP_LAST, &deps) != -ENOENT) {

>> +             int inst = deps->id;

>> +

>> +             xroot_put_group(xr, deps);

>> +             /* Reached the target group instance, stop here. */

> ok

>> +             if (instance == inst)

>> +                     break;

>> +             xroot_destroy_single_group(xr, inst);

>> +             deps = NULL;

>> +     }

>> +

>> +     /* Now we can remove the target group. */

>> +     xroot_put_group(xr, target);

>> +     return xroot_destroy_single_group(xr, instance);

>> +}

>> +

>> +static int xroot_lookup_group(struct xroot *xr,

>> +                           struct xrt_root_lookup_group *arg)

>> +{

>> +     int rc = -ENOENT;

>> +     struct platform_device *grp = NULL;

>> +

>> +     while (rc < 0 && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {

>> +             if (arg->xpilp_match_cb(XRT_SUBDEV_GRP, grp, arg->xpilp_match_arg))

>> +                     rc = grp->id;

>> +             xroot_put_group(xr, grp);

>> +     }

>> +     return rc;

>> +}

>> +

>> +static void xroot_event_work(struct work_struct *work)

>> +{

>> +     struct xroot_evt *tmp;

>> +     struct xroot *xr = container_of(work, struct xroot, events.evt_work);

>> +

>> +     mutex_lock(&xr->events.evt_lock);

>> +     while (!list_empty(&xr->events.evt_list)) {

>> +             tmp = list_first_entry(&xr->events.evt_list, struct xroot_evt, list);

>> +             list_del(&tmp->list);

>> +             mutex_unlock(&xr->events.evt_lock);

>> +

>> +             xrt_subdev_pool_handle_event(&xr->groups.pool, &tmp->evt);

>> +

>> +             if (tmp->async)

>> +                     vfree(tmp);

>> +             else

>> +                     complete(&tmp->comp);

>> +

>> +             mutex_lock(&xr->events.evt_lock);

>> +     }

>> +     mutex_unlock(&xr->events.evt_lock);

>> +}

>> +

>> +static void xroot_event_init(struct xroot *xr)

>> +{

>> +     INIT_LIST_HEAD(&xr->events.evt_list);

>> +     mutex_init(&xr->events.evt_lock);

>> +     INIT_WORK(&xr->events.evt_work, xroot_event_work);

>> +}

>> +

>> +static void xroot_event_fini(struct xroot *xr)

>> +{

>> +     flush_scheduled_work();

>> +     WARN_ON(!list_empty(&xr->events.evt_list));

>> +}

>> +

>> +static int xroot_get_leaf(struct xroot *xr, struct xrt_root_get_leaf *arg)

>> +{

>> +     int rc = -ENOENT;

>> +     struct platform_device *grp = NULL;

>> +

>> +     while (rc && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {

>> +             rc = xleaf_call(grp, XRT_GROUP_GET_LEAF, arg);

>> +             xroot_put_group(xr, grp);

>> +     }

>> +     return rc;

>> +}

>> +

>> +static int xroot_put_leaf(struct xroot *xr, struct xrt_root_put_leaf *arg)

>> +{

>> +     int rc = -ENOENT;

>> +     struct platform_device *grp = NULL;

>> +

>> +     while (rc && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {

>> +             rc = xleaf_call(grp, XRT_GROUP_PUT_LEAF, arg);

>> +             xroot_put_group(xr, grp);

>> +     }

>> +     return rc;

>> +}

>> +

>> +static int xroot_root_cb(struct device *dev, void *parg, enum xrt_root_cmd cmd, void *arg)

>> +{

>> +     struct xroot *xr = (struct xroot *)parg;

>> +     int rc = 0;

>> +

>> +     switch (cmd) {

>> +     /* Leaf actions. */

>> +     case XRT_ROOT_GET_LEAF: {

>> +             struct xrt_root_get_leaf *getleaf = (struct xrt_root_get_leaf *)arg;

>> +

>> +             rc = xroot_get_leaf(xr, getleaf);

>> +             break;

>> +     }

>> +     case XRT_ROOT_PUT_LEAF: {

>> +             struct xrt_root_put_leaf *putleaf = (struct xrt_root_put_leaf *)arg;

>> +

>> +             rc = xroot_put_leaf(xr, putleaf);

>> +             break;

>> +     }

>> +     case XRT_ROOT_GET_LEAF_HOLDERS: {

>> +             struct xrt_root_get_holders *holders = (struct xrt_root_get_holders *)arg;

>> +

>> +             rc = xrt_subdev_pool_get_holders(&xr->groups.pool,

>> +                                              holders->xpigh_pdev,

>> +                                              holders->xpigh_holder_buf,

>> +                                              holders->xpigh_holder_buf_len);

>> +             break;

>> +     }

>> +

>> +     /* Group actions. */

>> +     case XRT_ROOT_CREATE_GROUP:

>> +             rc = xroot_create_group(xr, (char *)arg);

>> +             break;

>> +     case XRT_ROOT_REMOVE_GROUP:

>> +             rc = xroot_destroy_group(xr, (int)(uintptr_t)arg);

>> +             break;

>> +     case XRT_ROOT_LOOKUP_GROUP: {

>> +             struct xrt_root_lookup_group *getgrp = (struct xrt_root_lookup_group *)arg;

>> +

>> +             rc = xroot_lookup_group(xr, getgrp);

>> +             break;

>> +     }

>> +     case XRT_ROOT_WAIT_GROUP_BRINGUP:

>> +             rc = xroot_wait_for_bringup(xr) ? 0 : -EINVAL;

>> +             break;

>> +

>> +     /* Event actions. */

>> +     case XRT_ROOT_EVENT_SYNC:

>> +     case XRT_ROOT_EVENT_ASYNC: {

>> +             bool async = (cmd == XRT_ROOT_EVENT_ASYNC);

>> +             struct xrt_event *evt = (struct xrt_event *)arg;

>> +

>> +             rc = xroot_trigger_event(xr, evt, async);

>> +             break;

>> +     }

>> +

>> +     /* Device info. */

>> +     case XRT_ROOT_GET_RESOURCE: {

>> +             struct xrt_root_get_res *res = (struct xrt_root_get_res *)arg;

>> +

>> +             res->xpigr_res = xr->pdev->resource;

>> +             break;

>> +     }

>> +     case XRT_ROOT_GET_ID: {

>> +             struct xrt_root_get_id *id = (struct xrt_root_get_id *)arg;

>> +

>> +             id->xpigi_vendor_id = xr->pdev->vendor;

>> +             id->xpigi_device_id = xr->pdev->device;

>> +             id->xpigi_sub_vendor_id = xr->pdev->subsystem_vendor;

>> +             id->xpigi_sub_device_id = xr->pdev->subsystem_device;

>> +             break;

>> +     }

>> +

>> +     /* MISC generic PCIE driver functions. */

>> +     case XRT_ROOT_HOT_RESET: {

>> +             xr->pf_cb.xpc_hot_reset(xr->pdev);

>> +             break;

>> +     }

>> +     case XRT_ROOT_HWMON: {

>> +             struct xrt_root_hwmon *hwmon = (struct xrt_root_hwmon *)arg;

>> +

>> +             if (hwmon->xpih_register) {

>> +                     hwmon->xpih_hwmon_dev =

>> +                             hwmon_device_register_with_info(DEV(xr->pdev),

>> +                                                             hwmon->xpih_name,

>> +                                                             hwmon->xpih_drvdata,

>> +                                                             NULL,

>> +                                                             hwmon->xpih_groups);

>> +             } else {

>> +                     hwmon_device_unregister(hwmon->xpih_hwmon_dev);

>> +             }

>> +             break;

>> +     }

>> +

>> +     default:

>> +             xroot_err(xr, "unknown IOCTL cmd %d", cmd);

>> +             rc = -EINVAL;

>> +             break;

>> +     }

>> +

>> +     return rc;

>> +}

>> +

>> +static void xroot_bringup_group_work(struct work_struct *work)

>> +{

>> +     struct platform_device *pdev = NULL;

>> +     struct xroot *xr = container_of(work, struct xroot, groups.bringup_work);

>> +

>> +     while (xroot_get_group(xr, XROOT_GROUP_FIRST, &pdev) != -ENOENT) {

>> +             int r, i;

>> +

>> +             i = pdev->id;

>> +             r = xleaf_call(pdev, XRT_GROUP_INIT_CHILDREN, NULL);

>> +             xroot_put_group(xr, pdev);

>> +             if (r == -EEXIST)

>> +                     continue; /* Already brough up, nothing to do. */

>> +             if (r)

>> +                     atomic_inc(&xr->groups.bringup_failed);

>> +

>> +             xroot_group_trigger_event(xr, i, XRT_EVENT_POST_CREATION);

>> +

>> +             if (atomic_dec_and_test(&xr->groups.bringup_pending))

>> +                     complete(&xr->groups.bringup_comp);

>> +     }

>> +}

>> +

>> +static void xroot_groups_init(struct xroot *xr)

> ok

>> +{

>> +     xrt_subdev_pool_init(DEV(xr->pdev), &xr->groups.pool);

>> +     INIT_WORK(&xr->groups.bringup_work, xroot_bringup_group_work);

>> +     atomic_set(&xr->groups.bringup_pending, 0);

>> +     atomic_set(&xr->groups.bringup_failed, 0);

>> +     init_completion(&xr->groups.bringup_comp);

>> +}

>> +

>> +static void xroot_groups_fini(struct xroot *xr)

>> +{

>> +     flush_scheduled_work();

>> +     xrt_subdev_pool_fini(&xr->groups.pool);

>> +}

>> +

>> +int xroot_add_vsec_node(void *root, char *dtb)

>> +{

>> +     struct xroot *xr = (struct xroot *)root;

>> +     struct device *dev = DEV(xr->pdev);

>> +     struct xrt_md_endpoint ep = { 0 };

>> +     int cap = 0, ret = 0;

>> +     u32 off_low, off_high, vsec_bar, header;

>> +     u64 vsec_off;

>> +

>> +     while ((cap = pci_find_next_ext_capability(xr->pdev, cap, PCI_EXT_CAP_ID_VNDR))) {

>> +             pci_read_config_dword(xr->pdev, cap + PCI_VNDR_HEADER, &header);

>> +             if (PCI_VNDR_HEADER_ID(header) == XRT_VSEC_ID)

>> +                     break;

>> +     }

>> +     if (!cap) {

>> +             xroot_info(xr, "No Vendor Specific Capability.");

>> +             return -ENOENT;

>> +     }

>> +

>> +     if (pci_read_config_dword(xr->pdev, cap + 8, &off_low) ||

>> +         pci_read_config_dword(xr->pdev, cap + 12, &off_high)) {

>> +             xroot_err(xr, "pci_read vendor specific failed.");

>> +             return -EINVAL;

>> +     }

>> +

>> +     ep.ep_name = XRT_MD_NODE_VSEC;

>> +     ret = xrt_md_add_endpoint(dev, dtb, &ep);

>> +     if (ret) {

>> +             xroot_err(xr, "add vsec metadata failed, ret %d", ret);

>> +             goto failed;

>> +     }

>> +

>> +     vsec_bar = cpu_to_be32(off_low & 0xf);

>> +     ret = xrt_md_set_prop(dev, dtb, XRT_MD_NODE_VSEC, NULL,

>> +                           XRT_MD_PROP_BAR_IDX, &vsec_bar, sizeof(vsec_bar));

>> +     if (ret) {

>> +             xroot_err(xr, "add vsec bar idx failed, ret %d", ret);

>> +             goto failed;

>> +     }

>> +

>> +     vsec_off = cpu_to_be64(((u64)off_high << 32) | (off_low & ~0xfU));

>> +     ret = xrt_md_set_prop(dev, dtb, XRT_MD_NODE_VSEC, NULL,

>> +                           XRT_MD_PROP_OFFSET, &vsec_off, sizeof(vsec_off));

>> +     if (ret) {

>> +             xroot_err(xr, "add vsec offset failed, ret %d", ret);

>> +             goto failed;

>> +     }

>> +

>> +failed:

>> +     return ret;

>> +}

>> +EXPORT_SYMBOL_GPL(xroot_add_vsec_node);

>> +

>> +int xroot_add_simple_node(void *root, char *dtb, const char *endpoint)

>> +{

>> +     struct xroot *xr = (struct xroot *)root;

>> +     struct device *dev = DEV(xr->pdev);

>> +     struct xrt_md_endpoint ep = { 0 };

>> +     int ret = 0;

>> +

>> +     ep.ep_name = endpoint;

>> +     ret = xrt_md_add_endpoint(dev, dtb, &ep);

>> +     if (ret)

>> +             xroot_err(xr, "add %s failed, ret %d", endpoint, ret);

>> +

>> +     return ret;

>> +}

>> +EXPORT_SYMBOL_GPL(xroot_add_simple_node);

>> +

>> +bool xroot_wait_for_bringup(void *root)

>> +{

>> +     struct xroot *xr = (struct xroot *)root;

>> +

>> +     wait_for_completion(&xr->groups.bringup_comp);

>> +     return atomic_read(&xr->groups.bringup_failed) == 0;

> ok

>

> Tom

>

>> +}

>> +EXPORT_SYMBOL_GPL(xroot_wait_for_bringup);

>> +

>> +int xroot_probe(struct pci_dev *pdev, struct xroot_physical_function_callback *cb, void **root)

>> +{

>> +     struct device *dev = DEV(pdev);

>> +     struct xroot *xr = NULL;

>> +

>> +     dev_info(dev, "%s: probing...", __func__);

>> +

>> +     xr = devm_kzalloc(dev, sizeof(*xr), GFP_KERNEL);

>> +     if (!xr)

>> +             return -ENOMEM;

>> +

>> +     xr->pdev = pdev;

>> +     xr->pf_cb = *cb;

>> +     xroot_groups_init(xr);

>> +     xroot_event_init(xr);

>> +

>> +     *root = xr;

>> +     return 0;

>> +}

>> +EXPORT_SYMBOL_GPL(xroot_probe);

>> +

>> +void xroot_remove(void *root)

>> +{

>> +     struct xroot *xr = (struct xroot *)root;

>> +     struct platform_device *grp = NULL;

>> +

>> +     xroot_info(xr, "leaving...");

>> +

>> +     if (xroot_get_group(xr, XROOT_GROUP_FIRST, &grp) == 0) {

>> +             int instance = grp->id;

>> +

>> +             xroot_put_group(xr, grp);

>> +             xroot_destroy_group(xr, instance);

>> +     }

>> +

>> +     xroot_event_fini(xr);

>> +     xroot_groups_fini(xr);

>> +}

>> +EXPORT_SYMBOL_GPL(xroot_remove);

>> +

>> +void xroot_broadcast(void *root, enum xrt_events evt)

>> +{

>> +     struct xroot *xr = (struct xroot *)root;

>> +     struct xrt_event e = { 0 };

>> +

>> +     /* Root pf driver only broadcasts below two events. */

>> +     if (evt != XRT_EVENT_POST_CREATION && evt != XRT_EVENT_PRE_REMOVAL) {

>> +             xroot_info(xr, "invalid event %d", evt);

>> +             return;

>> +     }

>> +

>> +     e.xe_evt = evt;

>> +     e.xe_subdev.xevt_subdev_id = XRT_ROOT;

>> +     e.xe_subdev.xevt_subdev_instance = 0;

>> +     xroot_trigger_event(xr, &e, false);

>> +}

>> +EXPORT_SYMBOL_GPL(xroot_broadcast);
Tom Rix April 6, 2021, 1:50 p.m. UTC | #9
On 3/23/21 10:29 PM, Lizhi Hou wrote:
> ICAP stands for Hardware Internal Configuration Access Port. ICAP is

> discovered by walking firmware metadata. A platform device node will be

by walking the firmware
> created for it. FPGA bitstream is written to hardware through ICAP.

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>   drivers/fpga/xrt/include/xleaf/icap.h |  27 ++

>   drivers/fpga/xrt/lib/xleaf/icap.c     | 344 ++++++++++++++++++++++++++

>   2 files changed, 371 insertions(+)

>   create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h

>   create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c

>

> diff --git a/drivers/fpga/xrt/include/xleaf/icap.h b/drivers/fpga/xrt/include/xleaf/icap.h

> new file mode 100644

> index 000000000000..96d39a8934fa

> --- /dev/null

> +++ b/drivers/fpga/xrt/include/xleaf/icap.h

> @@ -0,0 +1,27 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Lizhi Hou <Lizhi.Hou@xilinx.com>

> + */

> +

> +#ifndef _XRT_ICAP_H_

> +#define _XRT_ICAP_H_

> +

> +#include "xleaf.h"

> +

> +/*

> + * ICAP driver leaf calls.

> + */

> +enum xrt_icap_leaf_cmd {

> +	XRT_ICAP_WRITE = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */

> +	XRT_ICAP_GET_IDCODE,

ok
> +};

> +

> +struct xrt_icap_wr {

> +	void	*xiiw_bit_data;

> +	u32	xiiw_data_len;

> +};

> +

> +#endif	/* _XRT_ICAP_H_ */

> diff --git a/drivers/fpga/xrt/lib/xleaf/icap.c b/drivers/fpga/xrt/lib/xleaf/icap.c

> new file mode 100644

> index 000000000000..13db2b759138

> --- /dev/null

> +++ b/drivers/fpga/xrt/lib/xleaf/icap.c

> @@ -0,0 +1,344 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * Xilinx Alveo FPGA ICAP Driver

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *      Lizhi Hou<Lizhi.Hou@xilinx.com>

> + *      Sonal Santan <sonals@xilinx.com>

> + *      Max Zhen <maxz@xilinx.com>

> + */

> +

> +#include <linux/mod_devicetable.h>

> +#include <linux/platform_device.h>

> +#include <linux/delay.h>

> +#include <linux/device.h>

> +#include <linux/regmap.h>

> +#include <linux/io.h>

> +#include "metadata.h"

> +#include "xleaf.h"

> +#include "xleaf/icap.h"

> +#include "xclbin-helper.h"

> +

> +#define XRT_ICAP "xrt_icap"

> +

> +#define ICAP_ERR(icap, fmt, arg...)	\

> +	xrt_err((icap)->pdev, fmt "\n", ##arg)

> +#define ICAP_WARN(icap, fmt, arg...)	\

> +	xrt_warn((icap)->pdev, fmt "\n", ##arg)

> +#define ICAP_INFO(icap, fmt, arg...)	\

> +	xrt_info((icap)->pdev, fmt "\n", ##arg)

> +#define ICAP_DBG(icap, fmt, arg...)	\

> +	xrt_dbg((icap)->pdev, fmt "\n", ##arg)

> +

> +/*

> + * AXI-HWICAP IP register layout. Please see

> + * https://www.xilinx.com/support/documentation/ip_documentation/axi_hwicap/v3_0/pg134-axi-hwicap.pdf

url works, looks good
> + */

> +#define ICAP_REG_GIER		0x1C

> +#define ICAP_REG_ISR		0x20

> +#define ICAP_REG_IER		0x28

> +#define ICAP_REG_WF		0x100

> +#define ICAP_REG_RF		0x104

> +#define ICAP_REG_SZ		0x108

> +#define ICAP_REG_CR		0x10C

> +#define ICAP_REG_SR		0x110

> +#define ICAP_REG_WFV		0x114

> +#define ICAP_REG_RFO		0x118

> +#define ICAP_REG_ASR		0x11C

> +

> +#define ICAP_STATUS_EOS		0x4

> +#define ICAP_STATUS_DONE	0x1

> +

> +/*

> + * Canned command sequence to obtain IDCODE of the FPGA

> + */

> +static const u32 idcode_stream[] = {

> +	/* dummy word */

> +	cpu_to_be32(0xffffffff),

> +	/* sync word */

> +	cpu_to_be32(0xaa995566),

> +	/* NOP word */

> +	cpu_to_be32(0x20000000),

> +	/* NOP word */

> +	cpu_to_be32(0x20000000),

> +	/* ID code */

> +	cpu_to_be32(0x28018001),

> +	/* NOP word */

> +	cpu_to_be32(0x20000000),

> +	/* NOP word */

> +	cpu_to_be32(0x20000000),

> +};

> +

> +static const struct regmap_config icap_regmap_config = {

ok
> +	.reg_bits = 32,

> +	.val_bits = 32,

> +	.reg_stride = 4,

> +	.max_register = 0x1000,

> +};

> +

> +struct icap {

> +	struct platform_device	*pdev;

> +	struct regmap		*regmap;

> +	struct mutex		icap_lock; /* icap dev lock */

> +

whitespace, remove extra nl
> +	u32			idcode;

> +};

> +

> +static int wait_for_done(const struct icap *icap)

> +{

> +	int i = 0;

> +	int ret;

> +	u32 w;

> +

> +	for (i = 0; i < 10; i++) {

> +		/*

> +		 * it requires few micro seconds for ICAP to process incoming data.

> +		 * Polling every 5us for 10 times would be good enough.

ok
> +		 */

> +		udelay(5);

> +		ret = regmap_read(icap->regmap, ICAP_REG_SR, &w);

> +		if (ret)

> +			return ret;

> +		ICAP_INFO(icap, "XHWICAP_SR: %x", w);

> +		if (w & (ICAP_STATUS_EOS | ICAP_STATUS_DONE))

ok
> +			return 0;

> +	}

> +

> +	ICAP_ERR(icap, "bitstream download timeout");

> +	return -ETIMEDOUT;

> +}

> +

> +static int icap_write(const struct icap *icap, const u32 *word_buf, int size)

> +{

> +	u32 value = 0;

> +	int ret;

> +	int i;

> +

> +	for (i = 0; i < size; i++) {

> +		value = be32_to_cpu(word_buf[i]);

> +		ret = regmap_write(icap->regmap, ICAP_REG_WF, value);

> +		if (ret)

> +			return ret;

> +	}

> +

> +	ret = regmap_write(icap->regmap, ICAP_REG_CR, 0x1);

> +	if (ret)

> +		return ret;

> +

> +	for (i = 0; i < 20; i++) {

> +		ret = regmap_read(icap->regmap, ICAP_REG_CR, &value);

> +		if (ret)

> +			return ret;

> +

> +		if ((value & 0x1) == 0)

> +			return 0;

> +		ndelay(50);

> +	}

> +

> +	ICAP_ERR(icap, "writing %d dwords timeout", size);

> +	return -EIO;

> +}

> +

> +static int bitstream_helper(struct icap *icap, const u32 *word_buffer,

> +			    u32 word_count)

> +{

> +	int wr_fifo_vacancy = 0;

> +	u32 word_written = 0;

> +	u32 remain_word;

> +	int err = 0;

> +

> +	WARN_ON(!mutex_is_locked(&icap->icap_lock));

> +	for (remain_word = word_count; remain_word > 0;

> +	     remain_word -= word_written, word_buffer += word_written) {

> +		err = regmap_read(icap->regmap, ICAP_REG_WFV, &wr_fifo_vacancy);

> +		if (err) {

> +			ICAP_ERR(icap, "read wr_fifo_vacancy failed %d", err);

> +			break;

> +		}

> +		if (wr_fifo_vacancy <= 0) {

> +			ICAP_ERR(icap, "no vacancy: %d", wr_fifo_vacancy);

> +			err = -EIO;

> +			break;

> +		}

> +		word_written = (wr_fifo_vacancy < remain_word) ?

> +			wr_fifo_vacancy : remain_word;

> +		if (icap_write(icap, word_buffer, word_written) != 0) {

> +			ICAP_ERR(icap, "write failed remain %d, written %d",

> +				 remain_word, word_written);

> +			err = -EIO;

> +			break;

> +		}

> +	}

> +

> +	return err;

> +}

> +

> +static int icap_download(struct icap *icap, const char *buffer,

> +			 unsigned long length)

> +{

> +	u32	num_chars_read = XCLBIN_HWICAP_BITFILE_BUF_SZ;

> +	u32	byte_read;

> +	int	err = 0;

> +

> +	if (length % sizeof(u32)) {

ok
> +		ICAP_ERR(icap, "invalid bitstream length %ld", length);

> +		return -EINVAL;

> +	}

> +

> +	mutex_lock(&icap->icap_lock);

> +	for (byte_read = 0; byte_read < length; byte_read += num_chars_read) {

> +		num_chars_read = length - byte_read;

> +		if (num_chars_read > XCLBIN_HWICAP_BITFILE_BUF_SZ)

> +			num_chars_read = XCLBIN_HWICAP_BITFILE_BUF_SZ;

> +

> +		err = bitstream_helper(icap, (u32 *)buffer, num_chars_read / sizeof(u32));

> +		if (err)

> +			goto failed;

> +		buffer += num_chars_read;

> +	}

> +

> +	/* there is not any cleanup needs to be done if writing ICAP timeout. */

> +	err = wait_for_done(icap);

> +

> +failed:

> +	mutex_unlock(&icap->icap_lock);

> +

> +	return err;

> +}

> +

> +/*

> + * Discover the FPGA IDCODE using special sequence of canned commands

> + */

> +static int icap_probe_chip(struct icap *icap)

> +{

> +	int err;

> +	u32 val = 0;


ok, thanks for demagic-ing this function.

Looks good overall, only a few minor things.

Reviewed-by: Tom Rix <trix@redhat.com>


> +

> +	regmap_read(icap->regmap, ICAP_REG_SR, &val);

> +	if (val != ICAP_STATUS_DONE)

> +		return -ENODEV;

> +	/* Read ICAP FIFO vacancy */

> +	regmap_read(icap->regmap, ICAP_REG_WFV, &val);

> +	if (val < 8)

> +		return -ENODEV;

> +	err = icap_write(icap, idcode_stream, ARRAY_SIZE(idcode_stream));

> +	if (err)

> +		return err;

> +	err = wait_for_done(icap);

> +	if (err)

> +		return err;

> +

> +	/* Tell config engine how many words to transfer to read FIFO */

> +	regmap_write(icap->regmap, ICAP_REG_SZ, 0x1);

> +	/* Switch the ICAP to read mode */

> +	regmap_write(icap->regmap, ICAP_REG_CR, 0x2);

> +	err = wait_for_done(icap);

> +	if (err)

> +		return err;

> +

> +	/* Read IDCODE from Read FIFO */

> +	regmap_read(icap->regmap, ICAP_REG_RF, &icap->idcode);

> +	return 0;

> +}

> +

> +static int

> +xrt_icap_leaf_call(struct platform_device *pdev, u32 cmd, void *arg)

> +{

> +	struct xrt_icap_wr *wr_arg = arg;

> +	struct icap *icap;

> +	int ret = 0;

> +

> +	icap = platform_get_drvdata(pdev);

> +

> +	switch (cmd) {

> +	case XRT_XLEAF_EVENT:

> +		/* Does not handle any event. */

> +		break;

> +	case XRT_ICAP_WRITE:

> +		ret = icap_download(icap, wr_arg->xiiw_bit_data,

> +				    wr_arg->xiiw_data_len);

> +		break;

> +	case XRT_ICAP_GET_IDCODE:

> +		*(u32 *)arg = icap->idcode;

> +		break;

> +	default:

> +		ICAP_ERR(icap, "unknown command %d", cmd);

> +		return -EINVAL;

> +	}

> +

> +	return ret;

> +}

> +

> +static int xrt_icap_probe(struct platform_device *pdev)

> +{

> +	void __iomem *base = NULL;

> +	struct resource *res;

> +	struct icap *icap;

> +	int result = 0;

> +

> +	icap = devm_kzalloc(&pdev->dev, sizeof(*icap), GFP_KERNEL);

> +	if (!icap)

> +		return -ENOMEM;

> +

> +	icap->pdev = pdev;

> +	platform_set_drvdata(pdev, icap);

> +	mutex_init(&icap->icap_lock);

> +

> +	xrt_info(pdev, "probing");

> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

> +	if (!res)

> +		return -EINVAL;

> +

> +	base = devm_ioremap_resource(&pdev->dev, res);

> +	if (IS_ERR(base))

> +		return PTR_ERR(base);

> +

> +	icap->regmap = devm_regmap_init_mmio(&pdev->dev, base, &icap_regmap_config);

> +	if (IS_ERR(icap->regmap)) {

> +		ICAP_ERR(icap, "init mmio failed");

> +		return PTR_ERR(icap->regmap);

> +	}

> +	/* Disable ICAP interrupts */

> +	regmap_write(icap->regmap, ICAP_REG_GIER, 0);

> +

> +	result = icap_probe_chip(icap);

> +	if (result)

> +		xrt_err(pdev, "Failed to probe FPGA");

> +	else

> +		xrt_info(pdev, "Discovered FPGA IDCODE %x", icap->idcode);

> +	return result;

> +}

> +

> +static struct xrt_subdev_endpoints xrt_icap_endpoints[] = {

> +	{

> +		.xse_names = (struct xrt_subdev_ep_names[]) {

> +			{ .ep_name = XRT_MD_NODE_FPGA_CONFIG },

> +			{ NULL },

> +		},

> +		.xse_min_ep = 1,

> +	},

> +	{ 0 },

> +};

> +

> +static struct xrt_subdev_drvdata xrt_icap_data = {

> +	.xsd_dev_ops = {

> +		.xsd_leaf_call = xrt_icap_leaf_call,

> +	},

> +};

> +

> +static const struct platform_device_id xrt_icap_table[] = {

> +	{ XRT_ICAP, (kernel_ulong_t)&xrt_icap_data },

> +	{ },

> +};

> +

> +static struct platform_driver xrt_icap_driver = {

> +	.driver = {

> +		.name = XRT_ICAP,

> +	},

> +	.probe = xrt_icap_probe,

> +	.id_table = xrt_icap_table,

> +};

> +

> +XRT_LEAF_INIT_FINI_FUNC(XRT_SUBDEV_ICAP, icap);
Lizhi Hou April 6, 2021, 5:52 p.m. UTC | #10
Hi Tom,


On 03/29/2021 10:12 AM, Tom Rix wrote:
> On 3/23/21 10:29 PM, Lizhi Hou wrote:

>> Alveo FPGA firmware and partial reconfigure file are in xclbin format. This

>> code enumerates and extracts sections from xclbin files. xclbin.h is cross

>> platform and used across all platforms and OS.

> ok

>> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

>> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

>> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

>> ---

>>   drivers/fpga/xrt/include/xclbin-helper.h |  48 +++

>>   drivers/fpga/xrt/lib/xclbin.c            | 369 ++++++++++++++++++++

>>   include/uapi/linux/xrt/xclbin.h          | 409 +++++++++++++++++++++++

>>   3 files changed, 826 insertions(+)

>>   create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h

>>   create mode 100644 drivers/fpga/xrt/lib/xclbin.c

>>   create mode 100644 include/uapi/linux/xrt/xclbin.h

>>

>> diff --git a/drivers/fpga/xrt/include/xclbin-helper.h b/drivers/fpga/xrt/include/xclbin-helper.h

>> new file mode 100644

>> index 000000000000..382b1de97b0a

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/include/xclbin-helper.h

>> @@ -0,0 +1,48 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *    David Zhang <davidzha@xilinx.com>

>> + *    Sonal Santan <sonal.santan@xilinx.com>

>> + */

>> +

>> +#ifndef _XCLBIN_HELPER_H_

>> +#define _XCLBIN_HELPER_H_

> ok

>> +

>> +#include <linux/types.h>

>> +#include <linux/device.h>

>> +#include <linux/xrt/xclbin.h>

>> +

>> +#define XCLBIN_VERSION2      "xclbin2"

>> +#define XCLBIN_HWICAP_BITFILE_BUF_SZ 1024

>> +#define XCLBIN_MAX_SIZE (1024 * 1024 * 1024) /* Assuming xclbin <= 1G, always */

> ok

>> +

>> +enum axlf_section_kind;

>> +struct axlf;

>> +

>> +/**

>> + * Bitstream header information as defined by Xilinx tools.

>> + * Please note that this struct definition is not owned by the driver.

>> + */

>> +struct xclbin_bit_head_info {

>> +     u32 header_length;              /* Length of header in 32 bit words */

>> +     u32 bitstream_length;           /* Length of bitstream to read in bytes */

>> +     const unchar *design_name;      /* Design name get from bitstream */

>> +     const unchar *part_name;        /* Part name read from bitstream */

>> +     const unchar *date;             /* Date read from bitstream header */

>> +     const unchar *time;             /* Bitstream creation time */

>> +     u32 magic_length;               /* Length of the magic numbers */

>> +     const unchar *version;          /* Version string */

>> +};

>> +

> ok, bit removed.

>> +/* caller must free the allocated memory for **data. len could be NULL. */

>> +int xrt_xclbin_get_section(struct device *dev,  const struct axlf *xclbin,

>> +                        enum axlf_section_kind kind, void **data,

>> +                        uint64_t *len);

> need to add comment that user must free data

>

> need to add comment that len is optional

It sounds the comment above the function.

	/* caller must free the allocated memory for **data. len could be NULL. */

Do you mean I need to add more detail or format the comment in different way?

>

>> +int xrt_xclbin_get_metadata(struct device *dev, const struct axlf *xclbin, char **dtb);

>> +int xrt_xclbin_parse_bitstream_header(struct device *dev, const unchar *data,

>> +                                   u32 size, struct xclbin_bit_head_info *head_info);

>> +const char *xrt_clock_type2epname(enum XCLBIN_CLOCK_TYPE type);

> ok

>> +

>> +#endif /* _XCLBIN_HELPER_H_ */

>> diff --git a/drivers/fpga/xrt/lib/xclbin.c b/drivers/fpga/xrt/lib/xclbin.c

>> new file mode 100644

>> index 000000000000..31b363c014a3

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/lib/xclbin.c

>> @@ -0,0 +1,369 @@

>> +// SPDX-License-Identifier: GPL-2.0

>> +/*

>> + * Xilinx Alveo FPGA Driver XCLBIN parser

>> + *

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors: David Zhang <davidzha@xilinx.com>

>> + */

>> +

>> +#include <asm/errno.h>

>> +#include <linux/vmalloc.h>

>> +#include <linux/device.h>

>> +#include "xclbin-helper.h"

>> +#include "metadata.h"

>> +

>> +/* Used for parsing bitstream header */

>> +#define BITSTREAM_EVEN_MAGIC_BYTE    0x0f

>> +#define BITSTREAM_ODD_MAGIC_BYTE     0xf0

> ok

>> +

>> +static int xrt_xclbin_get_section_hdr(const struct axlf *xclbin,

>> +                                   enum axlf_section_kind kind,

>> +                                   const struct axlf_section_header **header)

>> +{

>> +     const struct axlf_section_header *phead = NULL;

>> +     u64 xclbin_len;

>> +     int i;

>> +

>> +     *header = NULL;

>> +     for (i = 0; i < xclbin->header.num_sections; i++) {

>> +             if (xclbin->sections[i].section_kind == kind) {

>> +                     phead = &xclbin->sections[i];

>> +                     break;

>> +             }

>> +     }

>> +

>> +     if (!phead)

>> +             return -ENOENT;

>> +

>> +     xclbin_len = xclbin->header.length;

>> +     if (xclbin_len > XCLBIN_MAX_SIZE ||

>> +         phead->section_offset + phead->section_size > xclbin_len)

>> +             return -EINVAL;

>> +

>> +     *header = phead;

>> +     return 0;

>> +}

>> +

>> +static int xrt_xclbin_section_info(const struct axlf *xclbin,

>> +                                enum axlf_section_kind kind,

>> +                                u64 *offset, u64 *size)

>> +{

>> +     const struct axlf_section_header *mem_header = NULL;

>> +     int rc;

>> +

>> +     rc = xrt_xclbin_get_section_hdr(xclbin, kind, &mem_header);

>> +     if (rc)

>> +             return rc;

>> +

>> +     *offset = mem_header->section_offset;

>> +     *size = mem_header->section_size;

> ok

>> +

>> +     return 0;

>> +}

>> +

>> +/* caller must free the allocated memory for **data */

>> +int xrt_xclbin_get_section(struct device *dev,

>> +                        const struct axlf *buf,

>> +                        enum axlf_section_kind kind,

>> +                        void **data, u64 *len)

>> +{

>> +     const struct axlf *xclbin = (const struct axlf *)buf;

>> +     void *section = NULL;

>> +     u64 offset = 0;

>> +     u64 size = 0;

>> +     int err = 0;

>> +

>> +     if (!data) {

> ok

>> +             dev_err(dev, "invalid data pointer");

>> +             return -EINVAL;

>> +     }

>> +

>> +     err = xrt_xclbin_section_info(xclbin, kind, &offset, &size);

>> +     if (err) {

>> +             dev_dbg(dev, "parsing section failed. kind %d, err = %d", kind, err);

>> +             return err;

>> +     }

>> +

>> +     section = vzalloc(size);

>> +     if (!section)

>> +             return -ENOMEM;

>> +

>> +     memcpy(section, ((const char *)xclbin) + offset, size);

>> +

>> +     *data = section;

>> +     if (len)

>> +             *len = size;

>> +

>> +     return 0;

>> +}

>> +EXPORT_SYMBOL_GPL(xrt_xclbin_get_section);

>> +

>> +static inline int xclbin_bit_get_string(const unchar *data, u32 size,

>> +                                     u32 offset, unchar prefix,

>> +                                     const unchar **str)

>> +{

>> +     int len;

>> +     u32 tmp;

>> +

>> +     /* prefix and length will be 3 bytes */

>> +     if (offset + 3  > size)

>> +             return -EINVAL;

>> +

>> +     /* Read prefix */

>> +     tmp = data[offset++];

>> +     if (tmp != prefix)

>> +             return -EINVAL;

>> +

>> +     /* Get string length */

>> +     len = data[offset++];

>> +     len = (len << 8) | data[offset++];

>> +

>> +     if (offset + len > size)

>> +             return -EINVAL;

>> +

>> +     if (data[offset + len - 1] != '\0')

>> +             return -EINVAL;

>> +

>> +     *str = data + offset;

>> +

>> +     return len + 3;

>> +}

>> +

>> +/* parse bitstream header */

>> +int xrt_xclbin_parse_bitstream_header(struct device *dev, const unchar *data,

>> +                                   u32 size, struct xclbin_bit_head_info *head_info)

>> +{

>> +     u32 offset = 0;

>> +     int len, i;

>> +     u16 magic;

>> +

>> +     memset(head_info, 0, sizeof(*head_info));

>> +

>> +     /* Get "Magic" length */

>> +     if (size < sizeof(u16)) {

>> +             dev_err(dev, "invalid size");

>> +             return -EINVAL;

>> +     }

> ok

>> +

>> +     len = data[offset++];

>> +     len = (len << 8) | data[offset++];

>> +

>> +     if (offset + len > size) {

>> +             dev_err(dev, "invalid magic len");

>> +             return -EINVAL;

>> +     }

>> +     head_info->magic_length = len;

>> +

>> +     for (i = 0; i < head_info->magic_length - 1; i++) {

>> +             magic = data[offset++];

>> +             if (!(i % 2) && magic != BITSTREAM_EVEN_MAGIC_BYTE) {

>> +                     dev_err(dev, "invalid magic even byte at %d", offset);

>> +                     return -EINVAL;

>> +             }

>> +

>> +             if ((i % 2) && magic != BITSTREAM_ODD_MAGIC_BYTE) {

>> +                     dev_err(dev, "invalid magic odd byte at %d", offset);

>> +                     return -EINVAL;

>> +             }

>> +     }

>> +

>> +     if (offset + 3 > size) {

>> +             dev_err(dev, "invalid length of magic end");

>> +             return -EINVAL;

>> +     }

>> +     /* Read null end of magic data. */

>> +     if (data[offset++]) {

>> +             dev_err(dev, "invalid magic end");

>> +             return -EINVAL;

>> +     }

>> +

>> +     /* Read 0x01 (short) */

>> +     magic = data[offset++];

>> +     magic = (magic << 8) | data[offset++];

>> +

>> +     /* Check the "0x01" half word */

>> +     if (magic != 0x01) {

>> +             dev_err(dev, "invalid magic end");

>> +             return -EINVAL;

>> +     }

>> +

>> +     len = xclbin_bit_get_string(data, size, offset, 'a', &head_info->design_name);

>> +     if (len < 0) {

>> +             dev_err(dev, "get design name failed");

>> +             return -EINVAL;

>> +     }

>> +

>> +     head_info->version = strstr(head_info->design_name, "Version=") + strlen("Version=");

>> +     offset += len;

>> +

>> +     len = xclbin_bit_get_string(data, size, offset, 'b', &head_info->part_name);

>> +     if (len < 0) {

>> +             dev_err(dev, "get part name failed");

>> +             return -EINVAL;

>> +     }

>> +     offset += len;

>> +

>> +     len = xclbin_bit_get_string(data, size, offset, 'c', &head_info->date);

>> +     if (len < 0) {

>> +             dev_err(dev, "get data failed");

>> +             return -EINVAL;

>> +     }

>> +     offset += len;

>> +

>> +     len = xclbin_bit_get_string(data, size, offset, 'd', &head_info->time);

>> +     if (len < 0) {

>> +             dev_err(dev, "get time failed");

>> +             return -EINVAL;

>> +     }

>> +     offset += len;

>> +

>> +     if (offset + 5 >= size) {

>> +             dev_err(dev, "can not get bitstream length");

>> +             return -EINVAL;

>> +     }

>> +

>> +     /* Read 'e' */

>> +     if (data[offset++] != 'e') {

>> +             dev_err(dev, "invalid prefix of bitstream length");

>> +             return -EINVAL;

>> +     }

>> +

>> +     /* Get byte length of bitstream */

>> +     head_info->bitstream_length = data[offset++];

>> +     head_info->bitstream_length = (head_info->bitstream_length << 8) | data[offset++];

>> +     head_info->bitstream_length = (head_info->bitstream_length << 8) | data[offset++];

>> +     head_info->bitstream_length = (head_info->bitstream_length << 8) | data[offset++];

> OK

>> +

>> +     head_info->header_length = offset;

> ok

>> +

>> +     return 0;

>> +}

>> +EXPORT_SYMBOL_GPL(xrt_xclbin_parse_bitstream_header);

> ok, removed xrt_xclbin_free_header

>> +

>> +struct xrt_clock_desc {

>> +     char    *clock_ep_name;

>> +     u32     clock_xclbin_type;

>> +     char    *clkfreq_ep_name;

>> +} clock_desc[] = {

>> +     {

>> +             .clock_ep_name = XRT_MD_NODE_CLK_KERNEL1,

>> +             .clock_xclbin_type = CT_DATA,

>> +             .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_K1,

>> +     },

>> +     {

>> +             .clock_ep_name = XRT_MD_NODE_CLK_KERNEL2,

>> +             .clock_xclbin_type = CT_KERNEL,

>> +             .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_K2,

>> +     },

>> +     {

>> +             .clock_ep_name = XRT_MD_NODE_CLK_KERNEL3,

>> +             .clock_xclbin_type = CT_SYSTEM,

>> +             .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_HBM,

>> +     },

>> +};

>> +

>> +const char *xrt_clock_type2epname(enum XCLBIN_CLOCK_TYPE type)

>> +{

>> +     int i;

>> +

>> +     for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {

>> +             if (clock_desc[i].clock_xclbin_type == type)

>> +                     return clock_desc[i].clock_ep_name;

>> +     }

>> +     return NULL;

>> +}

>> +EXPORT_SYMBOL_GPL(xrt_clock_type2epname);

>> +

>> +static const char *clock_type2clkfreq_name(enum XCLBIN_CLOCK_TYPE type)

>> +{

>> +     int i;

>> +

>> +     for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {

>> +             if (clock_desc[i].clock_xclbin_type == type)

>> +                     return clock_desc[i].clkfreq_ep_name;

>> +     }

>> +     return NULL;

>> +}

>> +

>> +static int xrt_xclbin_add_clock_metadata(struct device *dev,

>> +                                      const struct axlf *xclbin,

>> +                                      char *dtb)

>> +{

>> +     struct clock_freq_topology *clock_topo;

>> +     u16 freq;

>> +     int rc;

>> +     int i;

>> +

>> +     /* if clock section does not exist, add nothing and return success */

> ok

>> +     rc = xrt_xclbin_get_section(dev, xclbin, CLOCK_FREQ_TOPOLOGY,

>> +                                 (void **)&clock_topo, NULL);

>> +     if (rc == -ENOENT)

>> +             return 0;

>> +     else if (rc)

>> +             return rc;

>> +

>> +     for (i = 0; i < clock_topo->count; i++) {

>> +             u8 type = clock_topo->clock_freq[i].type;

>> +             const char *ep_name = xrt_clock_type2epname(type);

>> +             const char *counter_name = clock_type2clkfreq_name(type);

>> +

>> +             if (!ep_name || !counter_name)

>> +                     continue;

>> +

>> +             freq = cpu_to_be16(clock_topo->clock_freq[i].freq_MHZ);

>> +             rc = xrt_md_set_prop(dev, dtb, ep_name, NULL, XRT_MD_PROP_CLK_FREQ,

>> +                                  &freq, sizeof(freq));

>> +             if (rc)

>> +                     break;

>> +

>> +             rc = xrt_md_set_prop(dev, dtb, ep_name, NULL, XRT_MD_PROP_CLK_CNT,

>> +                                  counter_name, strlen(counter_name) + 1);

>> +             if (rc)

>> +                     break;

>> +     }

>> +

>> +     vfree(clock_topo);

>> +

>> +     return rc;

>> +}

>> +

>> +int xrt_xclbin_get_metadata(struct device *dev, const struct axlf *xclbin, char **dtb)

>> +{

>> +     char *md = NULL, *newmd = NULL;

>> +     u64 len, md_len;

>> +     int rc;

>> +

>> +     *dtb = NULL;

> ok

>> +

>> +     rc = xrt_xclbin_get_section(dev, xclbin, PARTITION_METADATA, (void **)&md, &len);

>> +     if (rc)

>> +             goto done;

>> +

>> +     md_len = xrt_md_size(dev, md);

>> +

>> +     /* Sanity check the dtb section. */

>> +     if (md_len > len) {

>> +             rc = -EINVAL;

>> +             goto done;

>> +     }

>> +

>> +     /* use dup function here to convert incoming metadata to writable */

>> +     newmd = xrt_md_dup(dev, md);

>> +     if (!newmd) {

>> +             rc = -EFAULT;

>> +             goto done;

>> +     }

>> +

>> +     /* Convert various needed xclbin sections into dtb. */

>> +     rc = xrt_xclbin_add_clock_metadata(dev, xclbin, newmd);

>> +

>> +     if (!rc)

>> +             *dtb = newmd;

>> +     else

>> +             vfree(newmd);

> ok

>> +done:

>> +     vfree(md);

>> +     return rc;

>> +}

>> +EXPORT_SYMBOL_GPL(xrt_xclbin_get_metadata);

>> diff --git a/include/uapi/linux/xrt/xclbin.h b/include/uapi/linux/xrt/xclbin.h

>> new file mode 100644

>> index 000000000000..baa14d6653ab

>> --- /dev/null

>> +++ b/include/uapi/linux/xrt/xclbin.h

>> @@ -0,0 +1,409 @@

>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

>> +/*

>> + *  Xilinx FPGA compiled binary container format

>> + *

>> + *  Copyright (C) 2015-2021, Xilinx Inc

>> + */

>> +

>> +#ifndef _XCLBIN_H_

>> +#define _XCLBIN_H_

> ok, removed _WIN32_

>> +

>> +#if defined(__KERNEL__)

>> +

>> +#include <linux/types.h>

> ok, removed uuid.h and version.h

>> +

>> +#elif defined(__cplusplus)

>> +

>> +#include <cstdlib>

>> +#include <cstdint>

>> +#include <algorithm>

>> +#include <uuid/uuid.h>

>> +

>> +#else

>> +

>> +#include <stdlib.h>

>> +#include <stdint.h>

>> +#include <uuid/uuid.h>

>> +

>> +#endif

>> +

>> +#ifdef __cplusplus

>> +extern "C" {

>> +#endif

>> +

>> +/**

>> + * DOC: Container format for Xilinx FPGA images

>> + * The container stores bitstreams, metadata and firmware images.

>> + * xclbin/xsabin is an ELF-like binary container format. It is a structured

> ok

>> + * series of sections. There is a file header followed by several section

>> + * headers which is followed by sections. A section header points to an

>> + * actual section. There is an optional signature at the end. The

>> + * following figure illustrates a typical xclbin:

>> + *

>> + *     +---------------------+

>> + *     |                     |

>> + *     |       HEADER        |

>> + *     +---------------------+

>> + *     |   SECTION  HEADER   |

>> + *     |                     |

>> + *     +---------------------+

>> + *     |        ...          |

>> + *     |                     |

>> + *     +---------------------+

>> + *     |   SECTION  HEADER   |

>> + *     |                     |

>> + *     +---------------------+

>> + *     |       SECTION       |

>> + *     |                     |

>> + *     +---------------------+

>> + *     |         ...         |

>> + *     |                     |

>> + *     +---------------------+

>> + *     |       SECTION       |

>> + *     |                     |

>> + *     +---------------------+

>> + *     |      SIGNATURE      |

>> + *     |      (OPTIONAL)     |

>> + *     +---------------------+

> ok on the tabs to spaces

>> + */

>> +

>> +enum XCLBIN_MODE {

>> +     XCLBIN_FLAT = 0,

> ok

>> +     XCLBIN_PR,

>> +     XCLBIN_TANDEM_STAGE2,

>> +     XCLBIN_TANDEM_STAGE2_WITH_PR,

>> +     XCLBIN_HW_EMU,

>> +     XCLBIN_SW_EMU,

>> +     XCLBIN_MODE_MAX

>> +};

>> +

>> +enum axlf_section_kind {

>> +     BITSTREAM = 0,

>> +     CLEARING_BITSTREAM,

>> +     EMBEDDED_METADATA,

>> +     FIRMWARE,

>> +     DEBUG_DATA,

>> +     SCHED_FIRMWARE,

>> +     MEM_TOPOLOGY,

>> +     CONNECTIVITY,

>> +     IP_LAYOUT,

>> +     DEBUG_IP_LAYOUT,

>> +     DESIGN_CHECK_POINT,

>> +     CLOCK_FREQ_TOPOLOGY,

>> +     MCS,

>> +     BMC,

>> +     BUILD_METADATA,

>> +     KEYVALUE_METADATA,

>> +     USER_METADATA,

>> +     DNA_CERTIFICATE,

>> +     PDI,

>> +     BITSTREAM_PARTIAL_PDI,

>> +     PARTITION_METADATA,

>> +     EMULATION_DATA,

>> +     SYSTEM_METADATA,

>> +     SOFT_KERNEL,

>> +     ASK_FLASH,

>> +     AIE_METADATA,

>> +     ASK_GROUP_TOPOLOGY,

>> +     ASK_GROUP_CONNECTIVITY

>> +};

>> +

>> +enum MEM_TYPE {

>> +     MEM_DDR3 = 0,

>> +     MEM_DDR4,

>> +     MEM_DRAM,

>> +     MEM_STREAMING,

>> +     MEM_PREALLOCATED_GLOB,

>> +     MEM_ARE,

>> +     MEM_HBM,

>> +     MEM_BRAM,

>> +     MEM_URAM,

>> +     MEM_STREAMING_CONNECTION

>> +};

>> +

>> +enum IP_TYPE {

>> +     IP_MB = 0,

>> +     IP_KERNEL,

>> +     IP_DNASC,

>> +     IP_DDR4_CONTROLLER,

>> +     IP_MEM_DDR4,

>> +     IP_MEM_HBM

>> +};

>> +

>> +struct axlf_section_header {

>> +     uint32_t section_kind;      /* Section type */

>> +     char section_name[16];      /* Examples: "stage2", "clear1", */

>> +                                 /* "clear2", "ocl1", "ocl2, */

>> +                                 /* "ublaze", "sched" */

>> +     char rsvd[4];

>> +     uint64_t section_offset;    /* File offset of section data */

>> +     uint64_t section_size;      /* Size of section data */

>> +} __packed;

>> +

>> +struct axlf_header {

>> +     uint64_t length;                    /* Total size of the xclbin file */

>> +     uint64_t time_stamp;                /* Number of seconds since epoch */

>> +                                         /* when xclbin was created */

>> +     uint64_t feature_rom_timestamp;     /* TimeSinceEpoch of the featureRom */

>> +     uint16_t version_patch;     /* Patch Version */

>> +     uint8_t version_major;      /* Major Version - Version: 2.1.0*/

> ok, version checked

>

> whitepace, needs '2.1.0 */'

>

> I see this is a general problem, look other places.

>

> maybe it is a 'tab' and the diff is messing it up, convert tab to space.

Will fix it.
>

>> +     uint8_t version_minor;      /* Minor Version */

>> +     uint32_t mode;              /* XCLBIN_MODE */

>> +     union {

>> +             struct {

>> +                     uint64_t platform_id;   /* 64 bit platform ID: */

>> +                                     /* vendor-device-subvendor-subdev */

>> +                     uint64_t feature_id;    /* 64 bit feature id */

>> +             } rom;

>> +             unsigned char rom_uuid[16];     /* feature ROM UUID for which */

>> +                                             /* this xclbin was generated */

>> +     };

>> +     unsigned char platform_vbnv[64];        /* e.g. */

>> +             /* xilinx:xil-accel-rd-ku115:4ddr-xpr:3.4: null terminated */

>> +     union {

>> +             char next_axlf[16];             /* Name of next xclbin file */

>> +                                             /* in the daisy chain */

>> +             unsigned char uuid[16];         /* uuid of this xclbin*/

> ok

>

> whitespace comment need a ' ' before */

Will fix.

Thanks,
Lizhi
>

>> +     };

>> +     char debug_bin[16];                     /* Name of binary with debug */

>> +                                             /* information */

>> +     uint32_t num_sections;          /* Number of section headers */

>> +     char rsvd[4];

>> +} __packed;

>> +

>> +struct axlf {

>> +     char magic[8];                  /* Should be "xclbin2\0"  */

>> +     int32_t signature_length;               /* Length of the signature. */

>> +                                             /* -1 indicates no signature */

>> +     unsigned char reserved[28];             /* Note: Initialized to 0xFFs */

>> +

>> +     unsigned char key_block[256];           /* Signature for validation */

>> +                                             /* of binary */

>> +     uint64_t unique_id;                     /* axlf's uniqueId, use it to */

>> +                                             /* skip redownload etc */

>> +     struct axlf_header header;              /* Inline header */

>> +     struct axlf_section_header sections[1];   /* One or more section */

>> +                                                 /* headers follow */

>> +} __packed;

> ok, thanks!

>> +

>> +/* bitstream information */

>> +struct xlnx_bitstream {

>> +     uint8_t freq[8];

>> +     char bits[1];

>> +} __packed;

>> +

>> +/****        MEMORY TOPOLOGY SECTION ****/

>> +struct mem_data {

>> +     uint8_t type; /* enum corresponding to mem_type. */

>> +     uint8_t used; /* if 0 this bank is not present */

>> +     uint8_t rsvd[6];

>> +     union {

>> +             uint64_t size; /* if mem_type DDR, then size in KB; */

>> +             uint64_t route_id; /* if streaming then "route_id" */

>> +     };

>> +     union {

>> +             uint64_t base_address;/* if DDR then the base address; */

>> +             uint64_t flow_id; /* if streaming then "flow id" */

>> +     };

>> +     unsigned char tag[16]; /* DDR: BANK0,1,2,3, has to be null */

>> +                     /* terminated; if streaming then stream0, 1 etc */

>> +} __packed;

>> +

>> +struct mem_topology {

>> +     int32_t count; /* Number of mem_data */

>> +     struct mem_data mem_data[1]; /* Should be sorted on mem_type */

>> +} __packed;

>> +

>> +/****        CONNECTIVITY SECTION ****/

>> +/* Connectivity of each argument of CU(Compute Unit). It will be in terms

> ok

>> + * of argument index associated. For associating CU instances with arguments

>> + * and banks, start at the connectivity section. Using the ip_layout_index

>> + * access the ip_data.name. Now we can associate this CU instance with its

>> + * original CU name and get the connectivity as well. This enables us to form

>> + * related groups of CU instances.

>> + */

>> +

>> +struct connection {

>> +     int32_t arg_index; /* From 0 to n, may not be contiguous as scalars */

>> +                        /* skipped */

>> +     int32_t ip_layout_index; /* index into the ip_layout section. */

>> +                        /* ip_layout.ip_data[index].type == IP_KERNEL */

>> +     int32_t mem_data_index; /* index of the mem_data . Flag error is */

>> +                             /* used false. */

>> +} __packed;

>> +

>> +struct connectivity {

>> +     int32_t count;

>> +     struct connection connection[1];

>> +} __packed;

>> +

>> +/****        IP_LAYOUT SECTION ****/

>> +

>> +/* IP Kernel */

>> +#define IP_INT_ENABLE_MASK     0x0001

>> +#define IP_INTERRUPT_ID_MASK  0x00FE

>> +#define IP_INTERRUPT_ID_SHIFT 0x1

>> +

>> +enum IP_CONTROL {

>> +     AP_CTRL_HS = 0,

> ok

>

> Thanks for the changes!

>

> Tom

>

>> +     AP_CTRL_CHAIN,

>> +     AP_CTRL_NONE,

>> +     AP_CTRL_ME,

>> +     ACCEL_ADAPTER

>> +};

>> +

>> +#define IP_CONTROL_MASK       0xFF00

>> +#define IP_CONTROL_SHIFT 0x8

>> +

>> +/* IPs on AXI lite - their types, names, and base addresses.*/

>> +struct ip_data {

>> +     uint32_t type; /* map to IP_TYPE enum */

>> +     union {

>> +             uint32_t properties; /* Default: 32-bits to indicate ip */

>> +                                  /* specific property. */

>> +             /* type: IP_KERNEL

>> +              *          int_enable   : Bit  - 0x0000_0001;

>> +              *          interrupt_id : Bits - 0x0000_00FE;

>> +              *          ip_control   : Bits = 0x0000_FF00;

>> +              */

>> +             struct {                 /* type: IP_MEM_* */

>> +                     uint16_t index;

>> +                     uint8_t pc_index;

>> +                     uint8_t unused;

>> +             } indices;

>> +     };

>> +     uint64_t base_address;

>> +     uint8_t name[64]; /* eg Kernel name corresponding to KERNEL */

>> +                         /* instance, can embed CU name in future. */

>> +} __packed;

>> +

>> +struct ip_layout {

>> +     int32_t count;

>> +     struct ip_data ip_data[1]; /* All the ip_data needs to be sorted */

>> +                                  /* by base_address. */

>> +} __packed;

>> +

>> +/*** Debug IP section layout ****/

>> +enum DEBUG_IP_TYPE {

>> +     UNDEFINED = 0,

>> +     LAPC,

>> +     ILA,

>> +     AXI_MM_MONITOR,

>> +     AXI_TRACE_FUNNEL,

>> +     AXI_MONITOR_FIFO_LITE,

>> +     AXI_MONITOR_FIFO_FULL,

>> +     ACCEL_MONITOR,

>> +     AXI_STREAM_MONITOR,

>> +     AXI_STREAM_PROTOCOL_CHECKER,

>> +     TRACE_S2MM,

>> +     AXI_DMA,

>> +     TRACE_S2MM_FULL

>> +};

>> +

>> +struct debug_ip_data {

>> +     uint8_t type; /* type of enum DEBUG_IP_TYPE */

>> +     uint8_t index_lowbyte;

>> +     uint8_t properties;

>> +     uint8_t major;

>> +     uint8_t minor;

>> +     uint8_t index_highbyte;

>> +     uint8_t reserved[2];

>> +     uint64_t base_address;

>> +     char    name[128];

>> +} __packed;

>> +

>> +struct debug_ip_layout {

>> +     uint16_t count;

>> +     struct debug_ip_data debug_ip_data[1];

>> +} __packed;

>> +

>> +/* Supported clock frequency types */

>> +enum XCLBIN_CLOCK_TYPE {

>> +     CT_UNUSED = 0,                     /* Initialized value */

>> +     CT_DATA   = 1,                     /* Data clock */

>> +     CT_KERNEL = 2,                     /* Kernel clock */

>> +     CT_SYSTEM = 3                      /* System Clock */

>> +};

>> +

>> +/* Clock Frequency Entry */

>> +struct clock_freq {

>> +     uint16_t freq_MHZ;                 /* Frequency in MHz */

>> +     uint8_t type;                      /* Clock type (enum CLOCK_TYPE) */

>> +     uint8_t unused[5];                 /* Not used - padding */

>> +     char name[128];                    /* Clock Name */

>> +} __packed;

>> +

>> +/* Clock frequency section */

>> +struct clock_freq_topology {

>> +     int16_t count;             /* Number of entries */

>> +     struct clock_freq clock_freq[1]; /* Clock array */

>> +} __packed;

>> +

>> +/* Supported MCS file types */

>> +enum MCS_TYPE {

>> +     MCS_UNKNOWN = 0,                   /* Initialized value */

>> +     MCS_PRIMARY = 1,                   /* The primary mcs file data */

>> +     MCS_SECONDARY = 2,                 /* The secondary mcs file data */

>> +};

>> +

>> +/* One chunk of MCS data */

>> +struct mcs_chunk {

>> +     uint8_t type;                      /* MCS data type */

>> +     uint8_t unused[7];                 /* padding */

>> +     uint64_t offset;                   /* data offset from the start of */

>> +                                        /* the section */

>> +     uint64_t size;             /* data size */

>> +} __packed;

>> +

>> +/* MCS data section */

>> +struct mcs {

>> +     int8_t count;                      /* Number of chunks */

>> +     int8_t unused[7];                  /* padding */

>> +     struct mcs_chunk chunk[1];         /* MCS chunks followed by data */

>> +} __packed;

>> +

>> +/* bmc data section */

>> +struct bmc {

>> +     uint64_t offset;                   /* data offset from the start of */

>> +                                        /* the section */

>> +     uint64_t size;             /* data size (bytes) */

>> +     char image_name[64];               /* Name of the image */

>> +                                        /* (e.g., MSP432P401R) */

>> +     char device_name[64];              /* Device ID (e.g., VCU1525)  */

>> +     char version[64];

>> +     char md5value[33];                 /* MD5 Expected Value */

>> +                             /* (e.g., 56027182079c0bd621761b7dab5a27ca)*/

>> +     char padding[7];                   /* Padding */

>> +} __packed;

>> +

>> +/* soft kernel data section, used by classic driver */

>> +struct soft_kernel {

>> +     /** Prefix Syntax:

>> +      *  mpo - member, pointer, offset

>> +      *  This variable represents a zero terminated string

>> +      *  that is offseted from the beginning of the section.

>> +      *  The pointer to access the string is initialized as follows:

>> +      *  char * pCharString = (address_of_section) + (mpo value)

>> +      */

>> +     uint32_t mpo_name;         /* Name of the soft kernel */

>> +     uint32_t image_offset;   /* Image offset */

>> +     uint32_t image_size;       /* Image size */

>> +     uint32_t mpo_version;      /* Version */

>> +     uint32_t mpo_md5_value;    /* MD5 checksum */

>> +     uint32_t mpo_symbol_name;  /* Symbol name */

>> +     uint32_t num_instances;  /* Number of instances */

>> +     uint8_t padding[36];       /* Reserved for future use */

>> +     uint8_t reserved_ext[16];   /* Reserved for future extended data */

>> +} __packed;

>> +

>> +enum CHECKSUM_TYPE {

>> +     CST_UNKNOWN = 0,

>> +     CST_SDBM = 1,

>> +     CST_LAST

>> +};

>> +

>> +#ifdef __cplusplus

>> +}

>> +#endif

>> +

>> +#endif
Tom Rix April 6, 2021, 8:32 p.m. UTC | #11
On 3/23/21 10:29 PM, Lizhi Hou wrote:
> Add clock frequency counter driver. Clock frequency counter is

> a hardware function discovered by walking xclbin metadata. A platform

> device node will be created for it. Other part of driver can read the

> actual clock frequency through clock frequency counter driver.

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>   drivers/fpga/xrt/include/xleaf/clkfreq.h |  21 ++

>   drivers/fpga/xrt/lib/xleaf/clkfreq.c     | 240 +++++++++++++++++++++++

>   2 files changed, 261 insertions(+)

>   create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h

>   create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c

>

> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h b/drivers/fpga/xrt/include/xleaf/clkfreq.h

> new file mode 100644

> index 000000000000..005441d5df78

> --- /dev/null

> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h

> @@ -0,0 +1,21 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Lizhi Hou <Lizhi.Hou@xilinx.com>

> + */

> +

> +#ifndef _XRT_CLKFREQ_H_

> +#define _XRT_CLKFREQ_H_

> +

> +#include "xleaf.h"

> +

> +/*

> + * CLKFREQ driver leaf calls.

> + */

> +enum xrt_clkfreq_leaf_cmd {

> +	XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */

> +};

> +

> +#endif	/* _XRT_CLKFREQ_H_ */

> diff --git a/drivers/fpga/xrt/lib/xleaf/clkfreq.c b/drivers/fpga/xrt/lib/xleaf/clkfreq.c

> new file mode 100644

> index 000000000000..49473adde3fd

> --- /dev/null

> +++ b/drivers/fpga/xrt/lib/xleaf/clkfreq.c

> @@ -0,0 +1,240 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * Xilinx Alveo FPGA Clock Frequency Counter Driver

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *      Lizhi Hou<Lizhi.Hou@xilinx.com>

> + */

> +

> +#include <linux/mod_devicetable.h>

> +#include <linux/platform_device.h>

> +#include <linux/delay.h>

> +#include <linux/device.h>

> +#include <linux/regmap.h>

> +#include <linux/io.h>

> +#include "metadata.h"

> +#include "xleaf.h"

> +#include "xleaf/clkfreq.h"

> +

> +#define CLKFREQ_ERR(clkfreq, fmt, arg...)   \

> +	xrt_err((clkfreq)->pdev, fmt "\n", ##arg)

> +#define CLKFREQ_WARN(clkfreq, fmt, arg...)  \

> +	xrt_warn((clkfreq)->pdev, fmt "\n", ##arg)

> +#define CLKFREQ_INFO(clkfreq, fmt, arg...)  \

> +	xrt_info((clkfreq)->pdev, fmt "\n", ##arg)

> +#define CLKFREQ_DBG(clkfreq, fmt, arg...)   \

> +	xrt_dbg((clkfreq)->pdev, fmt "\n", ##arg)

> +

> +#define XRT_CLKFREQ		"xrt_clkfreq"

> +

> +#define XRT_CLKFREQ_CONTROL_STATUS_MASK		0xffff

> +

> +#define XRT_CLKFREQ_CONTROL_START	0x1

> +#define XRT_CLKFREQ_CONTROL_DONE	0x2

> +#define XRT_CLKFREQ_V5_CLK0_ENABLED	0x10000

> +

> +#define XRT_CLKFREQ_CONTROL_REG		0

> +#define XRT_CLKFREQ_COUNT_REG		0x8

> +#define XRT_CLKFREQ_V5_COUNT_REG	0x10

> +

> +#define XRT_CLKFREQ_READ_RETRIES	10

> +

> +static const struct regmap_config clkfreq_regmap_config = {

> +	.reg_bits = 32,

> +	.val_bits = 32,

> +	.reg_stride = 4,

> +	.max_register = 0x1000,

ok
> +};

> +

> +struct clkfreq {

> +	struct platform_device	*pdev;

> +	struct regmap		*regmap;

> +	const char		*clkfreq_ep_name;

> +	struct mutex		clkfreq_lock; /* clock counter dev lock */

> +};

> +

> +static int clkfreq_read(struct clkfreq *clkfreq, u32 *freq)

ok
> +{

> +	int times = XRT_CLKFREQ_READ_RETRIES;

ok
> +	u32 status;

> +	int ret;

> +

> +	*freq = 0;

> +	mutex_lock(&clkfreq->clkfreq_lock);

> +	ret = regmap_write(clkfreq->regmap, XRT_CLKFREQ_CONTROL_REG, XRT_CLKFREQ_CONTROL_START);

> +	if (ret) {

> +		CLKFREQ_INFO(clkfreq, "write start to control reg failed %d", ret);

> +		goto failed;

> +	}

> +	while (times != 0) {

> +		ret = regmap_read(clkfreq->regmap, XRT_CLKFREQ_CONTROL_REG, &status);

> +		if (ret) {

> +			CLKFREQ_INFO(clkfreq, "read control reg failed %d", ret);

> +			goto failed;

> +		}

> +		if ((status & XRT_CLKFREQ_CONTROL_STATUS_MASK) == XRT_CLKFREQ_CONTROL_DONE)

> +			break;

> +		mdelay(1);

> +		times--;

> +	};

> +

> +	if (!times) {

> +		ret = -ETIMEDOUT;

> +		goto failed;

> +	}

> +

> +	if (status & XRT_CLKFREQ_V5_CLK0_ENABLED)

> +		ret = regmap_read(clkfreq->regmap, XRT_CLKFREQ_V5_COUNT_REG, freq);

> +	else

> +		ret = regmap_read(clkfreq->regmap, XRT_CLKFREQ_COUNT_REG, freq);

> +	if (ret) {

> +		CLKFREQ_INFO(clkfreq, "read count failed %d", ret);

> +		goto failed;

> +	}

ok
> +

> +	mutex_unlock(&clkfreq->clkfreq_lock);

> +

> +	return 0;

> +

> +failed:

> +	mutex_unlock(&clkfreq->clkfreq_lock);

> +

> +	return ret;

> +}

> +

> +static ssize_t freq_show(struct device *dev, struct device_attribute *attr, char *buf)

> +{

> +	struct clkfreq *clkfreq = platform_get_drvdata(to_platform_device(dev));

> +	ssize_t count;

> +	u32 freq;

> +

> +	if (clkfreq_read(clkfreq, &freq))

> +		return -EINVAL;

ok
> +

> +	count = snprintf(buf, 64, "%u\n", freq);

ok
> +

> +	return count;

> +}

> +static DEVICE_ATTR_RO(freq);

> +

> +static struct attribute *clkfreq_attrs[] = {

> +	&dev_attr_freq.attr,

> +	NULL,

> +};

> +

> +static struct attribute_group clkfreq_attr_group = {

> +	.attrs = clkfreq_attrs,

> +};

> +

> +static int

> +xrt_clkfreq_leaf_call(struct platform_device *pdev, u32 cmd, void *arg)

> +{

> +	struct clkfreq *clkfreq;

> +	int ret = 0;

> +

> +	clkfreq = platform_get_drvdata(pdev);

> +

> +	switch (cmd) {

> +	case XRT_XLEAF_EVENT:

> +		/* Does not handle any event. */

> +		break;

> +	case XRT_CLKFREQ_READ:

ok
> +		ret = clkfreq_read(clkfreq, arg);

ok
> +		break;

> +	default:

> +		xrt_err(pdev, "unsupported cmd %d", cmd);

> +		return -EINVAL;

> +	}

> +

> +	return ret;

> +}

> +

> +static int clkfreq_remove(struct platform_device *pdev)

> +{

> +	sysfs_remove_group(&pdev->dev.kobj, &clkfreq_attr_group);

> +

> +	return 0;

> +}

> +

> +static int clkfreq_probe(struct platform_device *pdev)

> +{

> +	struct clkfreq *clkfreq = NULL;

> +	void __iomem *base = NULL;

> +	struct resource *res;

> +	int ret;

> +

> +	clkfreq = devm_kzalloc(&pdev->dev, sizeof(*clkfreq), GFP_KERNEL);

> +	if (!clkfreq)

> +		return -ENOMEM;

> +

> +	platform_set_drvdata(pdev, clkfreq);

> +	clkfreq->pdev = pdev;

> +	mutex_init(&clkfreq->clkfreq_lock);

> +

> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

> +	if (!res) {

> +		ret = -EINVAL;

> +		goto failed;

> +	}

> +	base = devm_ioremap_resource(&pdev->dev, res);

> +	if (IS_ERR(base)) {

> +		ret = PTR_ERR(base);

> +		goto failed;

> +	}

> +

> +	clkfreq->regmap = devm_regmap_init_mmio(&pdev->dev, base, &clkfreq_regmap_config);

> +	if (IS_ERR(clkfreq->regmap)) {

> +		CLKFREQ_ERR(clkfreq, "regmap %pR failed", res);

> +		ret = PTR_ERR(clkfreq->regmap);

> +		goto failed;

> +	}

> +	clkfreq->clkfreq_ep_name = res->name;

> +

> +	ret = sysfs_create_group(&pdev->dev.kobj, &clkfreq_attr_group);

> +	if (ret) {

> +		CLKFREQ_ERR(clkfreq, "create clkfreq attrs failed: %d", ret);

> +		goto failed;

> +	}

> +

> +	CLKFREQ_INFO(clkfreq, "successfully initialized clkfreq subdev");

> +

> +	return 0;

> +

> +failed:

> +	return ret;

> +}

> +

> +static struct xrt_subdev_endpoints xrt_clkfreq_endpoints[] = {

> +	{

> +		.xse_names = (struct xrt_subdev_ep_names[]) {

> +			{ .regmap_name = XRT_MD_REGMAP_CLKFREQ },


ok

Looks good to me

Reviewed-by: Tom Rix <trix@redhat.com>


> +			{ NULL },

> +		},

> +		.xse_min_ep = 1,

> +	},

> +	{ 0 },

> +};

> +

> +static struct xrt_subdev_drvdata xrt_clkfreq_data = {

> +	.xsd_dev_ops = {

> +		.xsd_leaf_call = xrt_clkfreq_leaf_call,

> +	},

> +};

> +

> +static const struct platform_device_id xrt_clkfreq_table[] = {

> +	{ XRT_CLKFREQ, (kernel_ulong_t)&xrt_clkfreq_data },

> +	{ },

> +};

> +

> +static struct platform_driver xrt_clkfreq_driver = {

> +	.driver = {

> +		.name = XRT_CLKFREQ,

> +	},

> +	.probe = clkfreq_probe,

> +	.remove = clkfreq_remove,

> +	.id_table = xrt_clkfreq_table,

> +};

> +

> +XRT_LEAF_INIT_FINI_FUNC(XRT_SUBDEV_CLKFREQ, clkfreq);
Tom Rix April 6, 2021, 8:46 p.m. UTC | #12
On 3/23/21 10:29 PM, Lizhi Hou wrote:
> Add partition isolation platform driver. partition isolation is

> a hardware function discovered by walking firmware metadata.

> A platform device node will be created for it. Partition isolation

> function isolate the different fpga regions

>

> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

> ---

>   drivers/fpga/xrt/include/xleaf/axigate.h |  23 ++

>   drivers/fpga/xrt/lib/xleaf/axigate.c     | 342 +++++++++++++++++++++++

>   2 files changed, 365 insertions(+)

>   create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h

>   create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c

>

> diff --git a/drivers/fpga/xrt/include/xleaf/axigate.h b/drivers/fpga/xrt/include/xleaf/axigate.h

> new file mode 100644

> index 000000000000..58f32c76dca1

> --- /dev/null

> +++ b/drivers/fpga/xrt/include/xleaf/axigate.h

> @@ -0,0 +1,23 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *	Lizhi Hou <Lizhi.Hou@xilinx.com>

> + */

> +

> +#ifndef _XRT_AXIGATE_H_

> +#define _XRT_AXIGATE_H_

> +

> +#include "xleaf.h"

> +#include "metadata.h"

> +

> +/*

> + * AXIGATE driver leaf calls.

> + */

> +enum xrt_axigate_leaf_cmd {

> +	XRT_AXIGATE_CLOSE = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */

> +	XRT_AXIGATE_OPEN,

ok
> +};

> +

> +#endif	/* _XRT_AXIGATE_H_ */

> diff --git a/drivers/fpga/xrt/lib/xleaf/axigate.c b/drivers/fpga/xrt/lib/xleaf/axigate.c

> new file mode 100644

> index 000000000000..231bb0335278

> --- /dev/null

> +++ b/drivers/fpga/xrt/lib/xleaf/axigate.c

> @@ -0,0 +1,342 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * Xilinx Alveo FPGA AXI Gate Driver

> + *

> + * Copyright (C) 2020-2021 Xilinx, Inc.

> + *

> + * Authors:

> + *      Lizhi Hou<Lizhi.Hou@xilinx.com>

> + */

> +

> +#include <linux/mod_devicetable.h>

> +#include <linux/platform_device.h>

> +#include <linux/delay.h>

> +#include <linux/device.h>

> +#include <linux/regmap.h>

> +#include <linux/io.h>

> +#include "metadata.h"

> +#include "xleaf.h"

> +#include "xleaf/axigate.h"

> +

> +#define XRT_AXIGATE "xrt_axigate"

> +

> +#define XRT_AXIGATE_WRITE_REG		0

> +#define XRT_AXIGATE_READ_REG		8

> +

> +#define XRT_AXIGATE_CTRL_CLOSE		0

> +#define XRT_AXIGATE_CTRL_OPEN_BIT0	1

> +#define XRT_AXIGATE_CTRL_OPEN_BIT1	2

> +

> +#define XRT_AXIGATE_INTERVAL		500 /* ns */

> +

> +struct xrt_axigate {

> +	struct platform_device	*pdev;

> +	struct regmap		*regmap;

> +	struct mutex		gate_lock; /* gate dev lock */

> +

> +	void			*evt_hdl;

> +	const char		*ep_name;

> +

> +	bool			gate_closed;

white space, extra nl's are not needed
> +};

> +

> +static const struct regmap_config axigate_regmap_config = {

> +	.reg_bits = 32,

> +	.val_bits = 32,

> +	.reg_stride = 4,

> +	.max_register = 0x1000,

ok
> +};

> +

> +/* the ep names are in the order of hardware layers */

> +static const char * const xrt_axigate_epnames[] = {

> +	XRT_MD_NODE_GATE_PLP, /* PLP: Provider Logic Partition */

> +	XRT_MD_NODE_GATE_ULP  /* ULP: User Logic Partition */

ok
> +};

> +

> +static inline int close_gate(struct xrt_axigate *gate)

> +{

> +	u32 val;

> +	int ret;

> +

> +	ret = regmap_write(gate->regmap, XRT_AXIGATE_WRITE_REG, XRT_AXIGATE_CTRL_CLOSE);

ok, regs defined
> +	if (ret) {

> +		xrt_err(gate->pdev, "write gate failed %d", ret);

> +		return ret;

> +	}

> +	ndelay(XRT_AXIGATE_INTERVAL);

> +	/*

> +	 * Legacy hardware requires extra read work properly.

> +	 * This is not on critical path, thus the extra read should not impact performance much.

> +	 */

> +	ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &val);

> +	if (ret) {

> +		xrt_err(gate->pdev, "read gate failed %d", ret);

> +		return ret;

> +	}

> +

> +	return 0;

> +}

> +

> +static inline int open_gate(struct xrt_axigate *gate)

> +{

> +	u32 val;

> +	int ret;

> +

> +	ret = regmap_write(gate->regmap, XRT_AXIGATE_WRITE_REG, XRT_AXIGATE_CTRL_OPEN_BIT1);

> +	if (ret) {

> +		xrt_err(gate->pdev, "write 2 failed %d", ret);

> +		return ret;

> +	}

> +	ndelay(XRT_AXIGATE_INTERVAL);

> +	/*

> +	 * Legacy hardware requires extra read work properly.

> +	 * This is not on critical path, thus the extra read should not impact performance much.

> +	 */

> +	ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &val);

> +	if (ret) {

> +		xrt_err(gate->pdev, "read 2 failed %d", ret);

> +		return ret;

> +	}

> +	ret = regmap_write(gate->regmap, XRT_AXIGATE_WRITE_REG,

> +			   XRT_AXIGATE_CTRL_OPEN_BIT0 | XRT_AXIGATE_CTRL_OPEN_BIT1);

> +	if (ret) {

> +		xrt_err(gate->pdev, "write 3 failed %d", ret);

> +		return ret;

> +	}

> +	ndelay(XRT_AXIGATE_INTERVAL);

> +	ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &val);

> +	if (ret) {

> +		xrt_err(gate->pdev, "read 3 failed %d", ret);

> +		return ret;

> +	}

> +

> +	return 0;

> +}

> +

> +static int xrt_axigate_epname_idx(struct platform_device *pdev)

> +{

> +	struct resource	*res;

> +	int ret, i;

ok
> +

> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

> +	if (!res) {

> +		xrt_err(pdev, "Empty Resource!");

> +		return -EINVAL;

> +	}

> +

> +	for (i = 0; i < ARRAY_SIZE(xrt_axigate_epnames); i++) {

ok
> +		ret = strncmp(xrt_axigate_epnames[i], res->name,

> +			      strlen(xrt_axigate_epnames[i]) + 1);

ok
> +		if (!ret)

> +			return i;

> +	}

> +

> +	return -EINVAL;

> +}

> +

> +static int xrt_axigate_close(struct platform_device *pdev)

> +{

> +	struct xrt_axigate *gate;

> +	u32 status = 0;

> +	int ret;

> +

> +	gate = platform_get_drvdata(pdev);

> +

> +	mutex_lock(&gate->gate_lock);

> +	ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &status);

> +	if (ret) {

> +		xrt_err(pdev, "read gate failed %d", ret);

> +		goto failed;

> +	}

> +	if (status) {		/* gate is opened */

> +		xleaf_broadcast_event(pdev, XRT_EVENT_PRE_GATE_CLOSE, false);

> +		ret = close_gate(gate);

ok
> +		if (ret)

> +			goto failed;

> +	}

> +

> +	gate->gate_closed = true;

ok
> +

> +failed:

> +	mutex_unlock(&gate->gate_lock);

> +

> +	xrt_info(pdev, "close gate %s", gate->ep_name);

> +	return ret;

> +}

> +

> +static int xrt_axigate_open(struct platform_device *pdev)

> +{

> +	struct xrt_axigate *gate;

> +	u32 status;

> +	int ret;

> +

> +	gate = platform_get_drvdata(pdev);

> +

> +	mutex_lock(&gate->gate_lock);

> +	ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &status);

> +	if (ret) {

> +		xrt_err(pdev, "read gate failed %d", ret);

> +		goto failed;

> +	}

> +	if (!status) {		/* gate is closed */

> +		ret = open_gate(gate);

> +		if (ret)

> +			goto failed;

> +		xleaf_broadcast_event(pdev, XRT_EVENT_POST_GATE_OPEN, true);

> +		/* xrt_axigate_open() could be called in event cb, thus

> +		 * we can not wait for the completes

> +		 */

> +	}

> +

> +	gate->gate_closed = false;

> +

> +failed:

> +	mutex_unlock(&gate->gate_lock);

> +

> +	xrt_info(pdev, "open gate %s", gate->ep_name);

> +	return ret;

> +}

> +

> +static void xrt_axigate_event_cb(struct platform_device *pdev, void *arg)

> +{

> +	struct xrt_axigate *gate = platform_get_drvdata(pdev);

> +	struct xrt_event *evt = (struct xrt_event *)arg;

> +	enum xrt_events e = evt->xe_evt;

> +	struct platform_device *leaf;

> +	enum xrt_subdev_id id;

> +	struct resource	*res;

> +	int instance;

> +

> +	if (e != XRT_EVENT_POST_CREATION)

> +		return;

> +

> +	instance = evt->xe_subdev.xevt_subdev_instance;

> +	id = evt->xe_subdev.xevt_subdev_id;

> +	if (id != XRT_SUBDEV_AXIGATE)

> +		return;

ok
> +

> +	leaf = xleaf_get_leaf_by_id(pdev, id, instance);

> +	if (!leaf)

> +		return;

> +

> +	res = platform_get_resource(leaf, IORESOURCE_MEM, 0);

> +	if (!res || !strncmp(res->name, gate->ep_name, strlen(res->name) + 1)) {

> +		xleaf_put_leaf(pdev, leaf);

> +		return;

> +	}

> +

> +	/* higher level axigate instance created, make sure the gate is opened. */


ok

only minor ws issue, otherwise good to go

Reviewed-by: Tom Rix <trix@redhat.com>


> +	if (xrt_axigate_epname_idx(leaf) > xrt_axigate_epname_idx(pdev))

> +		xrt_axigate_open(pdev);

> +	else

> +		xleaf_call(leaf, XRT_AXIGATE_OPEN, NULL);

> +

> +	xleaf_put_leaf(pdev, leaf);

> +}

> +

> +static int

> +xrt_axigate_leaf_call(struct platform_device *pdev, u32 cmd, void *arg)

> +{

> +	int ret = 0;

> +

> +	switch (cmd) {

> +	case XRT_XLEAF_EVENT:

> +		xrt_axigate_event_cb(pdev, arg);

> +		break;

> +	case XRT_AXIGATE_CLOSE:

> +		ret = xrt_axigate_close(pdev);

> +		break;

> +	case XRT_AXIGATE_OPEN:

> +		ret = xrt_axigate_open(pdev);

> +		break;

> +	default:

> +		xrt_err(pdev, "unsupported cmd %d", cmd);

> +		return -EINVAL;

> +	}

> +

> +	return ret;

> +}

> +

> +static int xrt_axigate_probe(struct platform_device *pdev)

> +{

> +	struct xrt_axigate *gate = NULL;

> +	void __iomem *base = NULL;

> +	struct resource *res;

> +	int ret;

> +

> +	gate = devm_kzalloc(&pdev->dev, sizeof(*gate), GFP_KERNEL);

> +	if (!gate)

> +		return -ENOMEM;

> +

> +	gate->pdev = pdev;

> +	platform_set_drvdata(pdev, gate);

> +

> +	xrt_info(pdev, "probing...");

> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

> +	if (!res) {

> +		xrt_err(pdev, "Empty resource 0");

> +		ret = -EINVAL;

> +		goto failed;

> +	}

> +

> +	base = devm_ioremap_resource(&pdev->dev, res);

> +	if (IS_ERR(base)) {

> +		xrt_err(pdev, "map base iomem failed");

> +		ret = PTR_ERR(base);

> +		goto failed;

> +	}

> +

> +	gate->regmap = devm_regmap_init_mmio(&pdev->dev, base, &axigate_regmap_config);

> +	if (IS_ERR(gate->regmap)) {

> +		xrt_err(pdev, "regmap %pR failed", res);

> +		ret = PTR_ERR(gate->regmap);

> +		goto failed;

> +	}

> +	gate->ep_name = res->name;

> +

> +	mutex_init(&gate->gate_lock);

> +

> +	return 0;

> +

> +failed:

> +	return ret;

> +}

> +

> +static struct xrt_subdev_endpoints xrt_axigate_endpoints[] = {

> +	{

> +		.xse_names = (struct xrt_subdev_ep_names[]) {

> +			{ .ep_name = XRT_MD_NODE_GATE_ULP },

> +			{ NULL },

> +		},

> +		.xse_min_ep = 1,

> +	},

> +	{

> +		.xse_names = (struct xrt_subdev_ep_names[]) {

> +			{ .ep_name = XRT_MD_NODE_GATE_PLP },

> +			{ NULL },

> +		},

> +		.xse_min_ep = 1,

> +	},

> +	{ 0 },

> +};

> +

> +static struct xrt_subdev_drvdata xrt_axigate_data = {

> +	.xsd_dev_ops = {

> +		.xsd_leaf_call = xrt_axigate_leaf_call,

> +	},

> +};

> +

> +static const struct platform_device_id xrt_axigate_table[] = {

> +	{ XRT_AXIGATE, (kernel_ulong_t)&xrt_axigate_data },

> +	{ },

> +};

> +

> +static struct platform_driver xrt_axigate_driver = {

> +	.driver = {

> +		.name = XRT_AXIGATE,

> +	},

> +	.probe = xrt_axigate_probe,

> +	.id_table = xrt_axigate_table,

> +};

> +

> +XRT_LEAF_INIT_FINI_FUNC(XRT_SUBDEV_AXIGATE, axigate);
Max Zhen April 6, 2021, 9:42 p.m. UTC | #13
Hi Tom,


On 3/30/21 5:52 AM, Tom Rix wrote:
> On 3/23/21 10:29 PM, Lizhi Hou wrote:

>> group driver that manages life cycle of a bunch of leaf driver instances

>> and bridges them with root.

>>

>> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

>> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

>> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

>> ---

>>   drivers/fpga/xrt/include/group.h |  25 +++

>>   drivers/fpga/xrt/lib/group.c     | 286 +++++++++++++++++++++++++++++++

>>   2 files changed, 311 insertions(+)

>>   create mode 100644 drivers/fpga/xrt/include/group.h

>>   create mode 100644 drivers/fpga/xrt/lib/group.c

>>

>> diff --git a/drivers/fpga/xrt/include/group.h b/drivers/fpga/xrt/include/group.h

>> new file mode 100644

>> index 000000000000..09e9d03f53fe

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/include/group.h

>> @@ -0,0 +1,25 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

> ok, removed generic boilerplate

>> + * Authors:

>> + *   Cheng Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#ifndef _XRT_GROUP_H_

>> +#define _XRT_GROUP_H_

>> +

>> +#include "xleaf.h"

> move header to another patch



Yes, the header is moved to 04/20 patch.


>> +

>> +/*

>> + * Group driver leaf calls.

> ok

>> + */

>> +enum xrt_group_leaf_cmd {

>> +     XRT_GROUP_GET_LEAF = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */

> ok

>> +     XRT_GROUP_PUT_LEAF,

>> +     XRT_GROUP_INIT_CHILDREN,

>> +     XRT_GROUP_FINI_CHILDREN,

>> +     XRT_GROUP_TRIGGER_EVENT,

>> +};

>> +

>> +#endif       /* _XRT_GROUP_H_ */

>> diff --git a/drivers/fpga/xrt/lib/group.c b/drivers/fpga/xrt/lib/group.c

>> new file mode 100644

>> index 000000000000..7b8716569641

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/lib/group.c

>> @@ -0,0 +1,286 @@

>> +// SPDX-License-Identifier: GPL-2.0

>> +/*

>> + * Xilinx Alveo FPGA Group Driver

>> + *

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *   Cheng Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#include <linux/mod_devicetable.h>

>> +#include <linux/platform_device.h>

>> +#include "xleaf.h"

>> +#include "subdev_pool.h"

>> +#include "group.h"

>> +#include "metadata.h"

>> +#include "lib-drv.h"

>> +

>> +#define XRT_GRP "xrt_group"

>> +

>> +struct xrt_group {

>> +     struct platform_device *pdev;

>> +     struct xrt_subdev_pool leaves;

>> +     bool leaves_created;

>> +     struct mutex lock; /* lock for group */

>> +};

>> +

>> +static int xrt_grp_root_cb(struct device *dev, void *parg,

>> +                        enum xrt_root_cmd cmd, void *arg)

> ok

>> +{

>> +     int rc;

>> +     struct platform_device *pdev =

>> +             container_of(dev, struct platform_device, dev);

>> +     struct xrt_group *xg = (struct xrt_group *)parg;

>> +

>> +     switch (cmd) {

>> +     case XRT_ROOT_GET_LEAF_HOLDERS: {

>> +             struct xrt_root_get_holders *holders =

>> +                     (struct xrt_root_get_holders *)arg;

>> +             rc = xrt_subdev_pool_get_holders(&xg->leaves,

>> +                                              holders->xpigh_pdev,

>> +                                              holders->xpigh_holder_buf,

>> +                                              holders->xpigh_holder_buf_len);

>> +             break;

>> +     }

>> +     default:

>> +             /* Forward parent call to root. */

>> +             rc = xrt_subdev_root_request(pdev, cmd, arg);

>> +             break;

>> +     }

>> +

>> +     return rc;

>> +}

>> +

>> +/*

>> + * Cut subdev's dtb from group's dtb based on passed-in endpoint descriptor.

>> + * Return the subdev's dtb through dtbp, if found.

>> + */

>> +static int xrt_grp_cut_subdev_dtb(struct xrt_group *xg, struct xrt_subdev_endpoints *eps,

>> +                               char *grp_dtb, char **dtbp)

>> +{

>> +     int ret, i, ep_count = 0;

>> +     char *dtb = NULL;

>> +

>> +     ret = xrt_md_create(DEV(xg->pdev), &dtb);

>> +     if (ret)

>> +             return ret;

>> +

>> +     for (i = 0; eps->xse_names[i].ep_name || eps->xse_names[i].regmap_name; i++) {

>> +             const char *ep_name = eps->xse_names[i].ep_name;

>> +             const char *reg_name = eps->xse_names[i].regmap_name;

>> +

>> +             if (!ep_name)

>> +                     xrt_md_get_compatible_endpoint(DEV(xg->pdev), grp_dtb, reg_name, &ep_name);

>> +             if (!ep_name)

>> +                     continue;

>> +

>> +             ret = xrt_md_copy_endpoint(DEV(xg->pdev), dtb, grp_dtb, ep_name, reg_name, NULL);

>> +             if (ret)

>> +                     continue;

>> +             xrt_md_del_endpoint(DEV(xg->pdev), grp_dtb, ep_name, reg_name);

>> +             ep_count++;

>> +     }

>> +     /* Found enough endpoints, return the subdev's dtb. */

>> +     if (ep_count >= eps->xse_min_ep) {

>> +             *dtbp = dtb;

>> +             return 0;

>> +     }

>> +

>> +     /* Cleanup - Restore all endpoints that has been deleted, if any. */

>> +     if (ep_count > 0) {

>> +             xrt_md_copy_endpoint(DEV(xg->pdev), grp_dtb, dtb,

>> +                                  XRT_MD_NODE_ENDPOINTS, NULL, NULL);

>> +     }

>> +     vfree(dtb);

>> +     *dtbp = NULL;

>> +     return 0;

>> +}

>> +

>> +static int xrt_grp_create_leaves(struct xrt_group *xg)

>> +{

>> +     struct xrt_subdev_platdata *pdata = DEV_PDATA(xg->pdev);

>> +     struct xrt_subdev_endpoints *eps = NULL;

>> +     int ret = 0, failed = 0;

>> +     enum xrt_subdev_id did;

>> +     char *grp_dtb = NULL;

>> +     unsigned long mlen;

>> +

>> +     if (!pdata)

>> +             return -EINVAL;

> ok

>> +

>> +     mlen = xrt_md_size(DEV(xg->pdev), pdata->xsp_dtb);

>> +     if (mlen == XRT_MD_INVALID_LENGTH) {

>> +             xrt_err(xg->pdev, "invalid dtb, len %ld", mlen);

>> +             return -EINVAL;

>> +     }

>> +

>> +     mutex_lock(&xg->lock);

>> +

>> +     if (xg->leaves_created) {

>> +             mutex_unlock(&xg->lock);

> add a comment that this is not an error and/or error is handled elsewhere



Will do.


>> +             return -EEXIST;

>> +     }

>> +

>> +     grp_dtb = vmalloc(mlen);

>> +     if (!grp_dtb) {

>> +             mutex_unlock(&xg->lock);

>> +             return -ENOMEM;

> ok

>> +     }

>> +

>> +     /* Create all leaves based on dtb. */

>> +     xrt_info(xg->pdev, "bringing up leaves...");

>> +     memcpy(grp_dtb, pdata->xsp_dtb, mlen);

>> +     for (did = 0; did < XRT_SUBDEV_NUM; did++) {

> ok

>> +             eps = xrt_drv_get_endpoints(did);

>> +             while (eps && eps->xse_names) {

>> +                     char *dtb = NULL;

>> +

>> +                     ret = xrt_grp_cut_subdev_dtb(xg, eps, grp_dtb, &dtb);

>> +                     if (ret) {

>> +                             failed++;

>> +                             xrt_err(xg->pdev, "failed to cut subdev dtb for drv %s: %d",

>> +                                     xrt_drv_name(did), ret);

>> +                     }

>> +                     if (!dtb) {

>> +                             /*

>> +                              * No more dtb to cut or bad things happened for this instance,

>> +                              * switch to the next one.

>> +                              */

>> +                             eps++;

>> +                             continue;

>> +                     }

>> +

>> +                     /* Found a dtb for this instance, let's add it. */

>> +                     ret = xrt_subdev_pool_add(&xg->leaves, did, xrt_grp_root_cb, xg, dtb);

>> +                     if (ret < 0) {

>> +                             failed++;

>> +                             xrt_err(xg->pdev, "failed to add %s: %d", xrt_drv_name(did), ret);

> add a comment that this is not a fatal error and cleanup happens elsewhere



Will do.


Thanks,

Max


>

> Tom

>

>> +                     }

>> +                     vfree(dtb);

>> +                     /* Continue searching for the same instance from grp_dtb. */

>> +             }

>> +     }

>> +

>> +     xg->leaves_created = true;

>> +     vfree(grp_dtb);

>> +     mutex_unlock(&xg->lock);

>> +     return failed == 0 ? 0 : -ECHILD;

>> +}

>> +

>> +static void xrt_grp_remove_leaves(struct xrt_group *xg)

>> +{

>> +     mutex_lock(&xg->lock);

>> +

>> +     if (!xg->leaves_created) {

>> +             mutex_unlock(&xg->lock);

>> +             return;

>> +     }

>> +

>> +     xrt_info(xg->pdev, "tearing down leaves...");

>> +     xrt_subdev_pool_fini(&xg->leaves);

>> +     xg->leaves_created = false;

>> +

>> +     mutex_unlock(&xg->lock);

>> +}

>> +

>> +static int xrt_grp_probe(struct platform_device *pdev)

>> +{

>> +     struct xrt_group *xg;

>> +

>> +     xrt_info(pdev, "probing...");

>> +

>> +     xg = devm_kzalloc(&pdev->dev, sizeof(*xg), GFP_KERNEL);

>> +     if (!xg)

>> +             return -ENOMEM;

>> +

>> +     xg->pdev = pdev;

>> +     mutex_init(&xg->lock);

>> +     xrt_subdev_pool_init(DEV(pdev), &xg->leaves);

>> +     platform_set_drvdata(pdev, xg);

>> +

>> +     return 0;

>> +}

>> +

>> +static int xrt_grp_remove(struct platform_device *pdev)

>> +{

>> +     struct xrt_group *xg = platform_get_drvdata(pdev);

>> +

>> +     xrt_info(pdev, "leaving...");

>> +     xrt_grp_remove_leaves(xg);

>> +     return 0;

>> +}

>> +

>> +static int xrt_grp_leaf_call(struct platform_device *pdev, u32 cmd, void *arg)

>> +{

>> +     int rc = 0;

>> +     struct xrt_group *xg = platform_get_drvdata(pdev);

>> +

>> +     switch (cmd) {

>> +     case XRT_XLEAF_EVENT:

>> +             /* Simply forward to every child. */

>> +             xrt_subdev_pool_handle_event(&xg->leaves,

>> +                                          (struct xrt_event *)arg);

>> +             break;

>> +     case XRT_GROUP_GET_LEAF: {

>> +             struct xrt_root_get_leaf *get_leaf =

>> +                     (struct xrt_root_get_leaf *)arg;

>> +

>> +             rc = xrt_subdev_pool_get(&xg->leaves, get_leaf->xpigl_match_cb,

>> +                                      get_leaf->xpigl_match_arg,

>> +                                      DEV(get_leaf->xpigl_caller_pdev),

>> +                                      &get_leaf->xpigl_tgt_pdev);

>> +             break;

>> +     }

>> +     case XRT_GROUP_PUT_LEAF: {

>> +             struct xrt_root_put_leaf *put_leaf =

>> +                     (struct xrt_root_put_leaf *)arg;

>> +

>> +             rc = xrt_subdev_pool_put(&xg->leaves, put_leaf->xpipl_tgt_pdev,

>> +                                      DEV(put_leaf->xpipl_caller_pdev));

>> +             break;

>> +     }

>> +     case XRT_GROUP_INIT_CHILDREN:

>> +             rc = xrt_grp_create_leaves(xg);

>> +             break;

>> +     case XRT_GROUP_FINI_CHILDREN:

>> +             xrt_grp_remove_leaves(xg);

>> +             break;

>> +     case XRT_GROUP_TRIGGER_EVENT:

>> +             xrt_subdev_pool_trigger_event(&xg->leaves, (enum xrt_events)(uintptr_t)arg);

>> +             break;

>> +     default:

>> +             xrt_err(pdev, "unknown IOCTL cmd %d", cmd);

>> +             rc = -EINVAL;

>> +             break;

>> +     }

>> +     return rc;

>> +}

>> +

>> +static struct xrt_subdev_drvdata xrt_grp_data = {

>> +     .xsd_dev_ops = {

>> +             .xsd_leaf_call = xrt_grp_leaf_call,

>> +     },

>> +};

>> +

>> +static const struct platform_device_id xrt_grp_id_table[] = {

>> +     { XRT_GRP, (kernel_ulong_t)&xrt_grp_data },

>> +     { },

>> +};

>> +

>> +static struct platform_driver xrt_group_driver = {

>> +     .driver = {

>> +             .name    = XRT_GRP,

>> +     },

>> +     .probe   = xrt_grp_probe,

>> +     .remove  = xrt_grp_remove,

>> +     .id_table = xrt_grp_id_table,

>> +};

>> +

>> +void group_leaf_init_fini(bool init)

>> +{

>> +     if (init)

>> +             xleaf_register_driver(XRT_SUBDEV_GRP, &xrt_group_driver, NULL);

>> +     else

>> +             xleaf_unregister_driver(XRT_SUBDEV_GRP);

>> +}
Lizhi Hou April 6, 2021, 11 p.m. UTC | #14
Hi Tom,


On 04/06/2021 06:50 AM, Tom Rix wrote:
>

>

> On 3/23/21 10:29 PM, Lizhi Hou wrote:

>> ICAP stands for Hardware Internal Configuration Access Port. ICAP is

>> discovered by walking firmware metadata. A platform device node will be

> by walking the firmware

Sure.
>> created for it. FPGA bitstream is written to hardware through ICAP.

>>

>> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

>> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

>> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

>> ---

>>   drivers/fpga/xrt/include/xleaf/icap.h |  27 ++

>>   drivers/fpga/xrt/lib/xleaf/icap.c     | 344 ++++++++++++++++++++++++++

>>   2 files changed, 371 insertions(+)

>>   create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h

>>   create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c

>>

>> diff --git a/drivers/fpga/xrt/include/xleaf/icap.h 

>> b/drivers/fpga/xrt/include/xleaf/icap.h

>> new file mode 100644

>> index 000000000000..96d39a8934fa

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/include/xleaf/icap.h

>> @@ -0,0 +1,27 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *   Lizhi Hou <Lizhi.Hou@xilinx.com>

>> + */

>> +

>> +#ifndef _XRT_ICAP_H_

>> +#define _XRT_ICAP_H_

>> +

>> +#include "xleaf.h"

>> +

>> +/*

>> + * ICAP driver leaf calls.

>> + */

>> +enum xrt_icap_leaf_cmd {

>> +     XRT_ICAP_WRITE = XRT_XLEAF_CUSTOM_BASE, /* See comments in 

>> xleaf.h */

>> +     XRT_ICAP_GET_IDCODE,

> ok

>> +};

>> +

>> +struct xrt_icap_wr {

>> +     void    *xiiw_bit_data;

>> +     u32     xiiw_data_len;

>> +};

>> +

>> +#endif       /* _XRT_ICAP_H_ */

>> diff --git a/drivers/fpga/xrt/lib/xleaf/icap.c 

>> b/drivers/fpga/xrt/lib/xleaf/icap.c

>> new file mode 100644

>> index 000000000000..13db2b759138

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/lib/xleaf/icap.c

>> @@ -0,0 +1,344 @@

>> +// SPDX-License-Identifier: GPL-2.0

>> +/*

>> + * Xilinx Alveo FPGA ICAP Driver

>> + *

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *      Lizhi Hou<Lizhi.Hou@xilinx.com>

>> + *      Sonal Santan <sonals@xilinx.com>

>> + *      Max Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#include <linux/mod_devicetable.h>

>> +#include <linux/platform_device.h>

>> +#include <linux/delay.h>

>> +#include <linux/device.h>

>> +#include <linux/regmap.h>

>> +#include <linux/io.h>

>> +#include "metadata.h"

>> +#include "xleaf.h"

>> +#include "xleaf/icap.h"

>> +#include "xclbin-helper.h"

>> +

>> +#define XRT_ICAP "xrt_icap"

>> +

>> +#define ICAP_ERR(icap, fmt, arg...)  \

>> +     xrt_err((icap)->pdev, fmt "\n", ##arg)

>> +#define ICAP_WARN(icap, fmt, arg...) \

>> +     xrt_warn((icap)->pdev, fmt "\n", ##arg)

>> +#define ICAP_INFO(icap, fmt, arg...) \

>> +     xrt_info((icap)->pdev, fmt "\n", ##arg)

>> +#define ICAP_DBG(icap, fmt, arg...)  \

>> +     xrt_dbg((icap)->pdev, fmt "\n", ##arg)

>> +

>> +/*

>> + * AXI-HWICAP IP register layout. Please see

>> + * 

>> https://www.xilinx.com/support/documentation/ip_documentation/axi_hwicap/v3_0/pg134-axi-hwicap.pdf

> url works, looks good

>> + */

>> +#define ICAP_REG_GIER                0x1C

>> +#define ICAP_REG_ISR         0x20

>> +#define ICAP_REG_IER         0x28

>> +#define ICAP_REG_WF          0x100

>> +#define ICAP_REG_RF          0x104

>> +#define ICAP_REG_SZ          0x108

>> +#define ICAP_REG_CR          0x10C

>> +#define ICAP_REG_SR          0x110

>> +#define ICAP_REG_WFV         0x114

>> +#define ICAP_REG_RFO         0x118

>> +#define ICAP_REG_ASR         0x11C

>> +

>> +#define ICAP_STATUS_EOS              0x4

>> +#define ICAP_STATUS_DONE     0x1

>> +

>> +/*

>> + * Canned command sequence to obtain IDCODE of the FPGA

>> + */

>> +static const u32 idcode_stream[] = {

>> +     /* dummy word */

>> +     cpu_to_be32(0xffffffff),

>> +     /* sync word */

>> +     cpu_to_be32(0xaa995566),

>> +     /* NOP word */

>> +     cpu_to_be32(0x20000000),

>> +     /* NOP word */

>> +     cpu_to_be32(0x20000000),

>> +     /* ID code */

>> +     cpu_to_be32(0x28018001),

>> +     /* NOP word */

>> +     cpu_to_be32(0x20000000),

>> +     /* NOP word */

>> +     cpu_to_be32(0x20000000),

>> +};

>> +

>> +static const struct regmap_config icap_regmap_config = {

> ok

>> +     .reg_bits = 32,

>> +     .val_bits = 32,

>> +     .reg_stride = 4,

>> +     .max_register = 0x1000,

>> +};

>> +

>> +struct icap {

>> +     struct platform_device  *pdev;

>> +     struct regmap           *regmap;

>> +     struct mutex            icap_lock; /* icap dev lock */

>> +

> whitespace, remove extra nl

Sure.

Thanks,
Lizhi
>> +     u32                     idcode;

>> +};

>> +

>> +static int wait_for_done(const struct icap *icap)

>> +{

>> +     int i = 0;

>> +     int ret;

>> +     u32 w;

>> +

>> +     for (i = 0; i < 10; i++) {

>> +             /*

>> +              * it requires few micro seconds for ICAP to process 

>> incoming data.

>> +              * Polling every 5us for 10 times would be good enough.

> ok

>> +              */

>> +             udelay(5);

>> +             ret = regmap_read(icap->regmap, ICAP_REG_SR, &w);

>> +             if (ret)

>> +                     return ret;

>> +             ICAP_INFO(icap, "XHWICAP_SR: %x", w);

>> +             if (w & (ICAP_STATUS_EOS | ICAP_STATUS_DONE))

> ok

>> +                     return 0;

>> +     }

>> +

>> +     ICAP_ERR(icap, "bitstream download timeout");

>> +     return -ETIMEDOUT;

>> +}

>> +

>> +static int icap_write(const struct icap *icap, const u32 *word_buf, 

>> int size)

>> +{

>> +     u32 value = 0;

>> +     int ret;

>> +     int i;

>> +

>> +     for (i = 0; i < size; i++) {

>> +             value = be32_to_cpu(word_buf[i]);

>> +             ret = regmap_write(icap->regmap, ICAP_REG_WF, value);

>> +             if (ret)

>> +                     return ret;

>> +     }

>> +

>> +     ret = regmap_write(icap->regmap, ICAP_REG_CR, 0x1);

>> +     if (ret)

>> +             return ret;

>> +

>> +     for (i = 0; i < 20; i++) {

>> +             ret = regmap_read(icap->regmap, ICAP_REG_CR, &value);

>> +             if (ret)

>> +                     return ret;

>> +

>> +             if ((value & 0x1) == 0)

>> +                     return 0;

>> +             ndelay(50);

>> +     }

>> +

>> +     ICAP_ERR(icap, "writing %d dwords timeout", size);

>> +     return -EIO;

>> +}

>> +

>> +static int bitstream_helper(struct icap *icap, const u32 *word_buffer,

>> +                         u32 word_count)

>> +{

>> +     int wr_fifo_vacancy = 0;

>> +     u32 word_written = 0;

>> +     u32 remain_word;

>> +     int err = 0;

>> +

>> +     WARN_ON(!mutex_is_locked(&icap->icap_lock));

>> +     for (remain_word = word_count; remain_word > 0;

>> +          remain_word -= word_written, word_buffer += word_written) {

>> +             err = regmap_read(icap->regmap, ICAP_REG_WFV, 

>> &wr_fifo_vacancy);

>> +             if (err) {

>> +                     ICAP_ERR(icap, "read wr_fifo_vacancy failed 

>> %d", err);

>> +                     break;

>> +             }

>> +             if (wr_fifo_vacancy <= 0) {

>> +                     ICAP_ERR(icap, "no vacancy: %d", wr_fifo_vacancy);

>> +                     err = -EIO;

>> +                     break;

>> +             }

>> +             word_written = (wr_fifo_vacancy < remain_word) ?

>> +                     wr_fifo_vacancy : remain_word;

>> +             if (icap_write(icap, word_buffer, word_written) != 0) {

>> +                     ICAP_ERR(icap, "write failed remain %d, written 

>> %d",

>> +                              remain_word, word_written);

>> +                     err = -EIO;

>> +                     break;

>> +             }

>> +     }

>> +

>> +     return err;

>> +}

>> +

>> +static int icap_download(struct icap *icap, const char *buffer,

>> +                      unsigned long length)

>> +{

>> +     u32     num_chars_read = XCLBIN_HWICAP_BITFILE_BUF_SZ;

>> +     u32     byte_read;

>> +     int     err = 0;

>> +

>> +     if (length % sizeof(u32)) {

> ok

>> +             ICAP_ERR(icap, "invalid bitstream length %ld", length);

>> +             return -EINVAL;

>> +     }

>> +

>> +     mutex_lock(&icap->icap_lock);

>> +     for (byte_read = 0; byte_read < length; byte_read += 

>> num_chars_read) {

>> +             num_chars_read = length - byte_read;

>> +             if (num_chars_read > XCLBIN_HWICAP_BITFILE_BUF_SZ)

>> +                     num_chars_read = XCLBIN_HWICAP_BITFILE_BUF_SZ;

>> +

>> +             err = bitstream_helper(icap, (u32 *)buffer, 

>> num_chars_read / sizeof(u32));

>> +             if (err)

>> +                     goto failed;

>> +             buffer += num_chars_read;

>> +     }

>> +

>> +     /* there is not any cleanup needs to be done if writing ICAP 

>> timeout. */

>> +     err = wait_for_done(icap);

>> +

>> +failed:

>> +     mutex_unlock(&icap->icap_lock);

>> +

>> +     return err;

>> +}

>> +

>> +/*

>> + * Discover the FPGA IDCODE using special sequence of canned commands

>> + */

>> +static int icap_probe_chip(struct icap *icap)

>> +{

>> +     int err;

>> +     u32 val = 0;

>

> ok, thanks for demagic-ing this function.

>

> Looks good overall, only a few minor things.

>

> Reviewed-by: Tom Rix <trix@redhat.com>

>

>> +

>> +     regmap_read(icap->regmap, ICAP_REG_SR, &val);

>> +     if (val != ICAP_STATUS_DONE)

>> +             return -ENODEV;

>> +     /* Read ICAP FIFO vacancy */

>> +     regmap_read(icap->regmap, ICAP_REG_WFV, &val);

>> +     if (val < 8)

>> +             return -ENODEV;

>> +     err = icap_write(icap, idcode_stream, ARRAY_SIZE(idcode_stream));

>> +     if (err)

>> +             return err;

>> +     err = wait_for_done(icap);

>> +     if (err)

>> +             return err;

>> +

>> +     /* Tell config engine how many words to transfer to read FIFO */

>> +     regmap_write(icap->regmap, ICAP_REG_SZ, 0x1);

>> +     /* Switch the ICAP to read mode */

>> +     regmap_write(icap->regmap, ICAP_REG_CR, 0x2);

>> +     err = wait_for_done(icap);

>> +     if (err)

>> +             return err;

>> +

>> +     /* Read IDCODE from Read FIFO */

>> +     regmap_read(icap->regmap, ICAP_REG_RF, &icap->idcode);

>> +     return 0;

>> +}

>> +

>> +static int

>> +xrt_icap_leaf_call(struct platform_device *pdev, u32 cmd, void *arg)

>> +{

>> +     struct xrt_icap_wr *wr_arg = arg;

>> +     struct icap *icap;

>> +     int ret = 0;

>> +

>> +     icap = platform_get_drvdata(pdev);

>> +

>> +     switch (cmd) {

>> +     case XRT_XLEAF_EVENT:

>> +             /* Does not handle any event. */

>> +             break;

>> +     case XRT_ICAP_WRITE:

>> +             ret = icap_download(icap, wr_arg->xiiw_bit_data,

>> +                                 wr_arg->xiiw_data_len);

>> +             break;

>> +     case XRT_ICAP_GET_IDCODE:

>> +             *(u32 *)arg = icap->idcode;

>> +             break;

>> +     default:

>> +             ICAP_ERR(icap, "unknown command %d", cmd);

>> +             return -EINVAL;

>> +     }

>> +

>> +     return ret;

>> +}

>> +

>> +static int xrt_icap_probe(struct platform_device *pdev)

>> +{

>> +     void __iomem *base = NULL;

>> +     struct resource *res;

>> +     struct icap *icap;

>> +     int result = 0;

>> +

>> +     icap = devm_kzalloc(&pdev->dev, sizeof(*icap), GFP_KERNEL);

>> +     if (!icap)

>> +             return -ENOMEM;

>> +

>> +     icap->pdev = pdev;

>> +     platform_set_drvdata(pdev, icap);

>> +     mutex_init(&icap->icap_lock);

>> +

>> +     xrt_info(pdev, "probing");

>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

>> +     if (!res)

>> +             return -EINVAL;

>> +

>> +     base = devm_ioremap_resource(&pdev->dev, res);

>> +     if (IS_ERR(base))

>> +             return PTR_ERR(base);

>> +

>> +     icap->regmap = devm_regmap_init_mmio(&pdev->dev, base, 

>> &icap_regmap_config);

>> +     if (IS_ERR(icap->regmap)) {

>> +             ICAP_ERR(icap, "init mmio failed");

>> +             return PTR_ERR(icap->regmap);

>> +     }

>> +     /* Disable ICAP interrupts */

>> +     regmap_write(icap->regmap, ICAP_REG_GIER, 0);

>> +

>> +     result = icap_probe_chip(icap);

>> +     if (result)

>> +             xrt_err(pdev, "Failed to probe FPGA");

>> +     else

>> +             xrt_info(pdev, "Discovered FPGA IDCODE %x", icap->idcode);

>> +     return result;

>> +}

>> +

>> +static struct xrt_subdev_endpoints xrt_icap_endpoints[] = {

>> +     {

>> +             .xse_names = (struct xrt_subdev_ep_names[]) {

>> +                     { .ep_name = XRT_MD_NODE_FPGA_CONFIG },

>> +                     { NULL },

>> +             },

>> +             .xse_min_ep = 1,

>> +     },

>> +     { 0 },

>> +};

>> +

>> +static struct xrt_subdev_drvdata xrt_icap_data = {

>> +     .xsd_dev_ops = {

>> +             .xsd_leaf_call = xrt_icap_leaf_call,

>> +     },

>> +};

>> +

>> +static const struct platform_device_id xrt_icap_table[] = {

>> +     { XRT_ICAP, (kernel_ulong_t)&xrt_icap_data },

>> +     { },

>> +};

>> +

>> +static struct platform_driver xrt_icap_driver = {

>> +     .driver = {

>> +             .name = XRT_ICAP,

>> +     },

>> +     .probe = xrt_icap_probe,

>> +     .id_table = xrt_icap_table,

>> +};

>> +

>> +XRT_LEAF_INIT_FINI_FUNC(XRT_SUBDEV_ICAP, icap);

>
Lizhi Hou April 7, 2021, 10:37 p.m. UTC | #15
Hi Tom,


On 04/01/2021 07:07 AM, Tom Rix wrote:
>

> On 3/23/21 10:29 PM, Lizhi Hou wrote:

>> platform driver that handles IOCTLs, such as hot reset and xclbin download.

>>

>> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

>> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

>> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

>> ---

>>   drivers/fpga/xrt/include/xmgmt-main.h |  34 ++

>>   drivers/fpga/xrt/mgmt/main.c          | 670 ++++++++++++++++++++++++++

>>   drivers/fpga/xrt/mgmt/xmgnt.h         |  34 ++

>>   include/uapi/linux/xrt/xmgmt-ioctl.h  |  46 ++

>>   4 files changed, 784 insertions(+)

>>   create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h

>>   create mode 100644 drivers/fpga/xrt/mgmt/main.c

> 'main' is generic, how about xmgnt-main ?

Sure. Will change to xmgnt-main
>>   create mode 100644 drivers/fpga/xrt/mgmt/xmgnt.h

>>   create mode 100644 include/uapi/linux/xrt/xmgmt-ioctl.h

>>

>> diff --git a/drivers/fpga/xrt/include/xmgmt-main.h b/drivers/fpga/xrt/include/xmgmt-main.h

>> new file mode 100644

>> index 000000000000..dce9f0d1a0dc

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/include/xmgmt-main.h

>> @@ -0,0 +1,34 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *   Cheng Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#ifndef _XMGMT_MAIN_H_

>> +#define _XMGMT_MAIN_H_

>> +

>> +#include <linux/xrt/xclbin.h>

>> +#include "xleaf.h"

>> +

>> +enum xrt_mgmt_main_leaf_cmd {

>> +     XRT_MGMT_MAIN_GET_AXLF_SECTION = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */

>> +     XRT_MGMT_MAIN_GET_VBNV,

>> +};

>> +

>> +/* There are three kind of partitions. Each of them is programmed independently. */

>> +enum provider_kind {

>> +     XMGMT_BLP, /* Base Logic Partition */

>> +     XMGMT_PLP, /* Provider Logic Partition */

>> +     XMGMT_ULP, /* User Logic Partition */

> ok

>> +};

>> +

>> +struct xrt_mgmt_main_get_axlf_section {

>> +     enum provider_kind xmmigas_axlf_kind;

>> +     enum axlf_section_kind xmmigas_section_kind;

>> +     void *xmmigas_section;

>> +     u64 xmmigas_section_size;

>> +};

>> +

>> +#endif       /* _XMGMT_MAIN_H_ */

>> diff --git a/drivers/fpga/xrt/mgmt/main.c b/drivers/fpga/xrt/mgmt/main.c

>> new file mode 100644

>> index 000000000000..f3b46e1fd78b

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/mgmt/main.c

>> @@ -0,0 +1,670 @@

>> +// SPDX-License-Identifier: GPL-2.0

>> +/*

>> + * Xilinx Alveo FPGA MGMT PF entry point driver

>> + *

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *   Sonal Santan <sonals@xilinx.com>

>> + */

>> +

>> +#include <linux/firmware.h>

>> +#include <linux/uaccess.h>

>> +#include "xclbin-helper.h"

>> +#include "metadata.h"

>> +#include "xleaf.h"

>> +#include <linux/xrt/xmgmt-ioctl.h>

>> +#include "xleaf/devctl.h"

>> +#include "xmgmt-main.h"

>> +#include "fmgr.h"

>> +#include "xleaf/icap.h"

>> +#include "xleaf/axigate.h"

>> +#include "xmgnt.h"

>> +

>> +#define XMGMT_MAIN "xmgmt_main"

>> +#define XMGMT_SUPP_XCLBIN_MAJOR 2

>> +

>> +#define XMGMT_FLAG_FLASH_READY       1

>> +#define XMGMT_FLAG_DEVCTL_READY      2

>> +

>> +#define XMGMT_UUID_STR_LEN   80

>> +

>> +struct xmgmt_main {

>> +     struct platform_device *pdev;

>> +     struct axlf *firmware_blp;

>> +     struct axlf *firmware_plp;

>> +     struct axlf *firmware_ulp;

>> +     u32 flags;

> ok

>> +     struct fpga_manager *fmgr;

>> +     struct mutex lock; /* busy lock */

> ok

>> +

> do not need this nl

Will remove.
>> +     uuid_t *blp_interface_uuids;

>> +     u32 blp_interface_uuid_num;

> ok

>> +};

>> +

>> +/*

>> + * VBNV stands for Vendor, BoardID, Name, Version. It is a string

>> + * which describes board and shell.

>> + *

>> + * Caller is responsible for freeing the returned string.

> ok

>> + */

>> +char *xmgmt_get_vbnv(struct platform_device *pdev)

>> +{

>> +     struct xmgmt_main *xmm = platform_get_drvdata(pdev);

>> +     const char *vbnv;

>> +     char *ret;

>> +     int i;

>> +

>> +     if (xmm->firmware_plp)

>> +             vbnv = xmm->firmware_plp->header.platform_vbnv;

>> +     else if (xmm->firmware_blp)

>> +             vbnv = xmm->firmware_blp->header.platform_vbnv;

>> +     else

>> +             return NULL;

>> +

>> +     ret = kstrdup(vbnv, GFP_KERNEL);

>> +     if (!ret)

>> +             return NULL;

>> +

>> +     for (i = 0; i < strlen(ret); i++) {

>> +             if (ret[i] == ':' || ret[i] == '.')

>> +                     ret[i] = '_';

>> +     }

>> +     return ret;

>> +}

>> +

>> +static int get_dev_uuid(struct platform_device *pdev, char *uuidstr, size_t len)

>> +{

>> +     struct xrt_devctl_rw devctl_arg = { 0 };

>> +     struct platform_device *devctl_leaf;

>> +     char uuid_buf[UUID_SIZE];

>> +     uuid_t uuid;

>> +     int err;

>> +

>> +     devctl_leaf = xleaf_get_leaf_by_epname(pdev, XRT_MD_NODE_BLP_ROM);

>> +     if (!devctl_leaf) {

>> +             xrt_err(pdev, "can not get %s", XRT_MD_NODE_BLP_ROM);

>> +             return -EINVAL;

>> +     }

>> +

>> +     devctl_arg.xdr_id = XRT_DEVCTL_ROM_UUID;

>> +     devctl_arg.xdr_buf = uuid_buf;

>> +     devctl_arg.xdr_len = sizeof(uuid_buf);

>> +     devctl_arg.xdr_offset = 0;

>> +     err = xleaf_call(devctl_leaf, XRT_DEVCTL_READ, &devctl_arg);

>> +     xleaf_put_leaf(pdev, devctl_leaf);

>> +     if (err) {

>> +             xrt_err(pdev, "can not get uuid: %d", err);

>> +             return err;

>> +     }

>> +     import_uuid(&uuid, uuid_buf);

> ok

>> +     xrt_md_trans_uuid2str(&uuid, uuidstr);

>> +

>> +     return 0;

>> +}

>> +

>> +int xmgmt_hot_reset(struct platform_device *pdev)

>> +{

>> +     int ret = xleaf_broadcast_event(pdev, XRT_EVENT_PRE_HOT_RESET, false);

>> +

>> +     if (ret) {

>> +             xrt_err(pdev, "offline failed, hot reset is canceled");

>> +             return ret;

>> +     }

>> +

>> +     xleaf_hot_reset(pdev);

>> +     xleaf_broadcast_event(pdev, XRT_EVENT_POST_HOT_RESET, false);

>> +     return 0;

>> +}

>> +

>> +static ssize_t reset_store(struct device *dev, struct device_attribute *da,

>> +                        const char *buf, size_t count)

>> +{

>> +     struct platform_device *pdev = to_platform_device(dev);

>> +

>> +     xmgmt_hot_reset(pdev);

>> +     return count;

>> +}

>> +static DEVICE_ATTR_WO(reset);

>> +

>> +static ssize_t VBNV_show(struct device *dev, struct device_attribute *da, char *buf)

>> +{

>> +     struct platform_device *pdev = to_platform_device(dev);

>> +     ssize_t ret;

>> +     char *vbnv;

>> +

>> +     vbnv = xmgmt_get_vbnv(pdev);

>> +     if (!vbnv)

>> +             return -EINVAL;

> ok

>> +     ret = sprintf(buf, "%s\n", vbnv);

>> +     kfree(vbnv);

>> +     return ret;

>> +}

>> +static DEVICE_ATTR_RO(VBNV);

>> +

>> +/* logic uuid is the uuid uniquely identfy the partition */

>> +static ssize_t logic_uuids_show(struct device *dev, struct device_attribute *da, char *buf)

>> +{

>> +     struct platform_device *pdev = to_platform_device(dev);

>> +     char uuid[XMGMT_UUID_STR_LEN];

> ok

>> +     ssize_t ret;

>> +

>> +     /* Getting UUID pointed to by VSEC, should be the same as logic UUID of BLP. */

>> +     ret = get_dev_uuid(pdev, uuid, sizeof(uuid));

>> +     if (ret)

>> +             return ret;

>> +     ret = sprintf(buf, "%s\n", uuid);

>> +     return ret;

>> +}

>> +static DEVICE_ATTR_RO(logic_uuids);

>> +

>> +static ssize_t interface_uuids_show(struct device *dev, struct device_attribute *da, char *buf)

>> +{

>> +     struct platform_device *pdev = to_platform_device(dev);

>> +     struct xmgmt_main *xmm = platform_get_drvdata(pdev);

>> +     ssize_t ret = 0;

>> +     u32 i;

>> +

>> +     for (i = 0; i < xmm->blp_interface_uuid_num; i++) {

>> +             char uuidstr[XMGMT_UUID_STR_LEN];

>> +

>> +             xrt_md_trans_uuid2str(&xmm->blp_interface_uuids[i], uuidstr);

>> +             ret += sprintf(buf + ret, "%s\n", uuidstr);

>> +     }

>> +     return ret;

>> +}

>> +static DEVICE_ATTR_RO(interface_uuids);

>> +

>> +static struct attribute *xmgmt_main_attrs[] = {

>> +     &dev_attr_reset.attr,

>> +     &dev_attr_VBNV.attr,

>> +     &dev_attr_logic_uuids.attr,

>> +     &dev_attr_interface_uuids.attr,

>> +     NULL,

>> +};

>> +

>> +static const struct attribute_group xmgmt_main_attrgroup = {

>> +     .attrs = xmgmt_main_attrs,

>> +};

>> +

> ok,  removed ulp_image_write()

>> +static int load_firmware_from_disk(struct platform_device *pdev, struct axlf **fw_buf, size_t *len)

>> +{

>> +     char uuid[XMGMT_UUID_STR_LEN];

>> +     const struct firmware *fw;

>> +     char fw_name[256];

>> +     int err = 0;

>> +

>> +     *len = 0;

> ok

>> +     err = get_dev_uuid(pdev, uuid, sizeof(uuid));

>> +     if (err)

>> +             return err;

>> +

>> +     snprintf(fw_name, sizeof(fw_name), "xilinx/%s/partition.xsabin", uuid);

>> +     xrt_info(pdev, "try loading fw: %s", fw_name);

>> +

>> +     err = request_firmware(&fw, fw_name, DEV(pdev));

>> +     if (err)

>> +             return err;

>> +

>> +     *fw_buf = vmalloc(fw->size);

>> +     if (!*fw_buf) {

>> +             release_firmware(fw);

>> +             return -ENOMEM;

>> +     }

>> +

>> +     *len = fw->size;

>> +     memcpy(*fw_buf, fw->data, fw->size);

>> +

>> +     release_firmware(fw);

>> +     return 0;

>> +}

>> +

>> +static const struct axlf *xmgmt_get_axlf_firmware(struct xmgmt_main *xmm, enum provider_kind kind)

>> +{

>> +     switch (kind) {

>> +     case XMGMT_BLP:

>> +             return xmm->firmware_blp;

>> +     case XMGMT_PLP:

>> +             return xmm->firmware_plp;

>> +     case XMGMT_ULP:

>> +             return xmm->firmware_ulp;

>> +     default:

>> +             xrt_err(xmm->pdev, "unknown axlf kind: %d", kind);

>> +             return NULL;

>> +     }

>> +}

>> +

>> +/* The caller needs to free the returned dtb buffer */

> ok

>> +char *xmgmt_get_dtb(struct platform_device *pdev, enum provider_kind kind)

>> +{

>> +     struct xmgmt_main *xmm = platform_get_drvdata(pdev);

>> +     const struct axlf *provider;

>> +     char *dtb = NULL;

>> +     int rc;

>> +

>> +     provider = xmgmt_get_axlf_firmware(xmm, kind);

>> +     if (!provider)

>> +             return dtb;

>> +

>> +     rc = xrt_xclbin_get_metadata(DEV(pdev), provider, &dtb);

>> +     if (rc)

>> +             xrt_err(pdev, "failed to find dtb: %d", rc);

>> +     return dtb;

>> +}

>> +

>> +/* The caller needs to free the returned uuid buffer */

> ok

>> +static const char *get_uuid_from_firmware(struct platform_device *pdev, const struct axlf *xclbin)

>> +{

>> +     const void *uuiddup = NULL;

>> +     const void *uuid = NULL;

>> +     void *dtb = NULL;

>> +     int rc;

>> +

>> +     rc = xrt_xclbin_get_section(DEV(pdev), xclbin, PARTITION_METADATA, &dtb, NULL);

>> +     if (rc)

>> +             return NULL;

>> +

>> +     rc = xrt_md_get_prop(DEV(pdev), dtb, NULL, NULL, XRT_MD_PROP_LOGIC_UUID, &uuid, NULL);

>> +     if (!rc)

>> +             uuiddup = kstrdup(uuid, GFP_KERNEL);

>> +     vfree(dtb);

>> +     return uuiddup;

>> +}

>> +

>> +static bool is_valid_firmware(struct platform_device *pdev,

>> +                           const struct axlf *xclbin, size_t fw_len)

>> +{

>> +     const char *fw_buf = (const char *)xclbin;

>> +     size_t axlflen = xclbin->header.length;

>> +     char dev_uuid[XMGMT_UUID_STR_LEN];

>> +     const char *fw_uuid;

>> +     int err;

>> +

>> +     err = get_dev_uuid(pdev, dev_uuid, sizeof(dev_uuid));

>> +     if (err)

>> +             return false;

>> +

>> +     if (memcmp(fw_buf, XCLBIN_VERSION2, sizeof(XCLBIN_VERSION2)) != 0) {

>> +             xrt_err(pdev, "unknown fw format");

>> +             return false;

>> +     }

>> +

>> +     if (axlflen > fw_len) {

>> +             xrt_err(pdev, "truncated fw, length: %zu, expect: %zu", fw_len, axlflen);

>> +             return false;

>> +     }

>> +

>> +     if (xclbin->header.version_major != XMGMT_SUPP_XCLBIN_MAJOR) {

>> +             xrt_err(pdev, "firmware is not supported");

>> +             return false;

>> +     }

>> +

>> +     fw_uuid = get_uuid_from_firmware(pdev, xclbin);

>> +     if (!fw_uuid || strncmp(fw_uuid, dev_uuid, sizeof(dev_uuid)) != 0) {

>> +             xrt_err(pdev, "bad fw UUID: %s, expect: %s",

>> +                     fw_uuid ? fw_uuid : "<none>", dev_uuid);

>> +             kfree(fw_uuid);

>> +             return false;

>> +     }

>> +

>> +     kfree(fw_uuid);

>> +     return true;

>> +}

>> +

>> +int xmgmt_get_provider_uuid(struct platform_device *pdev, enum provider_kind kind, uuid_t *uuid)

>> +{

>> +     struct xmgmt_main *xmm = platform_get_drvdata(pdev);

>> +     const struct axlf *fwbuf;

>> +     const char *fw_uuid;

>> +     int rc = -ENOENT;

>> +

>> +     mutex_lock(&xmm->lock);

>> +

>> +     fwbuf = xmgmt_get_axlf_firmware(xmm, kind);

>> +     if (!fwbuf)

>> +             goto done;

>> +

>> +     fw_uuid = get_uuid_from_firmware(pdev, fwbuf);

>> +     if (!fw_uuid)

>> +             goto done;

>> +

>> +     rc = xrt_md_trans_str2uuid(DEV(pdev), fw_uuid, uuid);

>> +     kfree(fw_uuid);

>> +

>> +done:

>> +     mutex_unlock(&xmm->lock);

>> +     return rc;

>> +}

>> +

>> +static int xmgmt_create_blp(struct xmgmt_main *xmm)

>> +{

>> +     const struct axlf *provider = xmgmt_get_axlf_firmware(xmm, XMGMT_BLP);

>> +     struct platform_device *pdev = xmm->pdev;

>> +     int rc = 0;

>> +     char *dtb = NULL;

>> +

>> +     dtb = xmgmt_get_dtb(pdev, XMGMT_BLP);

>> +     if (!dtb) {

>> +             xrt_err(pdev, "did not get BLP metadata");

>> +             return -EINVAL;

> ok

>> +     }

>> +

>> +     rc = xmgmt_process_xclbin(xmm->pdev, xmm->fmgr, provider, XMGMT_BLP);

>> +     if (rc) {

>> +             xrt_err(pdev, "failed to process BLP: %d", rc);

>> +             goto failed;

>> +     }

>> +

>> +     rc = xleaf_create_group(pdev, dtb);

>> +     if (rc < 0)

>> +             xrt_err(pdev, "failed to create BLP group: %d", rc);

>> +     else

>> +             rc = 0;

>> +

>> +     WARN_ON(xmm->blp_interface_uuids);

>> +     rc = xrt_md_get_interface_uuids(&pdev->dev, dtb, 0, NULL);

>> +     if (rc > 0) {

>> +             xmm->blp_interface_uuid_num = rc;

>> +             xmm->blp_interface_uuids = vzalloc(sizeof(uuid_t) * xmm->blp_interface_uuid_num);

> blp_interface_uuids should be small, so convert to kzalloc

Will convert to kcalloc.

Thanks,
Lizhi
>> +             if (!xmm->blp_interface_uuids) {

> ok

>> +                     rc = -ENOMEM;

>> +                     goto failed;

>> +             }

>> +             xrt_md_get_interface_uuids(&pdev->dev, dtb, xmm->blp_interface_uuid_num,

>> +                                        xmm->blp_interface_uuids);

>> +     }

>> +

>> +failed:

>> +     vfree(dtb);

>> +     return rc;

>> +}

>> +

>> +static int xmgmt_load_firmware(struct xmgmt_main *xmm)

>> +{

>> +     struct platform_device *pdev = xmm->pdev;

>> +     size_t fwlen;

>> +     int rc;

>> +

>> +     rc = load_firmware_from_disk(pdev, &xmm->firmware_blp, &fwlen);

> ok

>> +     if (!rc && is_valid_firmware(pdev, xmm->firmware_blp, fwlen))

>> +             xmgmt_create_blp(xmm);

>> +     else

>> +             xrt_err(pdev, "failed to find firmware, giving up: %d", rc);

>> +     return rc;

>> +}

>> +

>> +static void xmgmt_main_event_cb(struct platform_device *pdev, void *arg)

>> +{

>> +     struct xmgmt_main *xmm = platform_get_drvdata(pdev);

>> +     struct xrt_event *evt = (struct xrt_event *)arg;

>> +     enum xrt_events e = evt->xe_evt;

>> +     struct platform_device *leaf;

>> +     enum xrt_subdev_id id;

>> +

>> +     id = evt->xe_subdev.xevt_subdev_id;

>> +     switch (e) {

>> +     case XRT_EVENT_POST_CREATION: {

>> +             if (id == XRT_SUBDEV_DEVCTL && !(xmm->flags & XMGMT_FLAG_DEVCTL_READY)) {

>> +                     leaf = xleaf_get_leaf_by_epname(pdev, XRT_MD_NODE_BLP_ROM);

>> +                     if (leaf) {

>> +                             xmm->flags |= XMGMT_FLAG_DEVCTL_READY;

>> +                             xleaf_put_leaf(pdev, leaf);

>> +                     }

>> +             } else if (id == XRT_SUBDEV_QSPI && !(xmm->flags & XMGMT_FLAG_FLASH_READY)) {

>> +                     xmm->flags |= XMGMT_FLAG_FLASH_READY;

>> +             } else {

>> +                     break;

>> +             }

>> +

>> +             if (xmm->flags & XMGMT_FLAG_DEVCTL_READY)

>> +                     xmgmt_load_firmware(xmm);

>> +             break;

>> +     }

>> +     case XRT_EVENT_PRE_REMOVAL:

>> +             break;

>> +     default:

>> +             xrt_dbg(pdev, "ignored event %d", e);

>> +             break;

>> +     }

>> +}

>> +

>> +static int xmgmt_main_probe(struct platform_device *pdev)

>> +{

>> +     struct xmgmt_main *xmm;

>> +

>> +     xrt_info(pdev, "probing...");

>> +

>> +     xmm = devm_kzalloc(DEV(pdev), sizeof(*xmm), GFP_KERNEL);

>> +     if (!xmm)

>> +             return -ENOMEM;

>> +

>> +     xmm->pdev = pdev;

>> +     xmm->fmgr = xmgmt_fmgr_probe(pdev);

>> +     if (IS_ERR(xmm->fmgr))

>> +             return PTR_ERR(xmm->fmgr);

>> +

>> +     platform_set_drvdata(pdev, xmm);

>> +     mutex_init(&xmm->lock);

>> +

>> +     /* Ready to handle req thru sysfs nodes. */

>> +     if (sysfs_create_group(&DEV(pdev)->kobj, &xmgmt_main_attrgroup))

>> +             xrt_err(pdev, "failed to create sysfs group");

>> +     return 0;

>> +}

>> +

>> +static int xmgmt_main_remove(struct platform_device *pdev)

>> +{

>> +     struct xmgmt_main *xmm = platform_get_drvdata(pdev);

>> +

>> +     /* By now, group driver should prevent any inter-leaf call. */

>> +

>> +     xrt_info(pdev, "leaving...");

>> +

>> +     vfree(xmm->blp_interface_uuids);

>> +     vfree(xmm->firmware_blp);

>> +     vfree(xmm->firmware_plp);

>> +     vfree(xmm->firmware_ulp);

>> +     xmgmt_region_cleanup_all(pdev);

>> +     xmgmt_fmgr_remove(xmm->fmgr);

>> +     sysfs_remove_group(&DEV(pdev)->kobj, &xmgmt_main_attrgroup);

>> +     return 0;

>> +}

>> +

>> +static int

>> +xmgmt_mainleaf_call(struct platform_device *pdev, u32 cmd, void *arg)

>> +{

>> +     struct xmgmt_main *xmm = platform_get_drvdata(pdev);

>> +     int ret = 0;

>> +

>> +     switch (cmd) {

>> +     case XRT_XLEAF_EVENT:

>> +             xmgmt_main_event_cb(pdev, arg);

>> +             break;

>> +     case XRT_MGMT_MAIN_GET_AXLF_SECTION: {

>> +             struct xrt_mgmt_main_get_axlf_section *get =

>> +                     (struct xrt_mgmt_main_get_axlf_section *)arg;

>> +             const struct axlf *firmware = xmgmt_get_axlf_firmware(xmm, get->xmmigas_axlf_kind);

>> +

>> +             if (!firmware) {

>> +                     ret = -ENOENT;

>> +             } else {

>> +                     ret = xrt_xclbin_get_section(DEV(pdev), firmware,

>> +                                                  get->xmmigas_section_kind,

>> +                                                  &get->xmmigas_section,

>> +                                                  &get->xmmigas_section_size);

>> +             }

>> +             break;

>> +     }

>> +     case XRT_MGMT_MAIN_GET_VBNV: {

>> +             char **vbnv_p = (char **)arg;

>> +

>> +             *vbnv_p = xmgmt_get_vbnv(pdev);

>> +             if (!*vbnv_p)

>> +                     ret = -EINVAL;

> ok

>> +             break;

>> +     }

>> +     default:

>> +             xrt_err(pdev, "unknown cmd: %d", cmd);

>> +             ret = -EINVAL;

>> +             break;

>> +     }

>> +     return ret;

>> +}

>> +

>> +static int xmgmt_main_open(struct inode *inode, struct file *file)

>> +{

>> +     struct platform_device *pdev = xleaf_devnode_open(inode);

>> +

>> +     /* Device may have gone already when we get here. */

>> +     if (!pdev)

>> +             return -ENODEV;

>> +

>> +     xrt_info(pdev, "opened");

>> +     file->private_data = platform_get_drvdata(pdev);

>> +     return 0;

>> +}

>> +

>> +static int xmgmt_main_close(struct inode *inode, struct file *file)

>> +{

>> +     struct xmgmt_main *xmm = file->private_data;

>> +

>> +     xleaf_devnode_close(inode);

>> +

>> +     xrt_info(xmm->pdev, "closed");

>> +     return 0;

>> +}

>> +

>> +/*

>> + * Called for xclbin download xclbin load ioctl.

>> + */

>> +static int xmgmt_bitstream_axlf_fpga_mgr(struct xmgmt_main *xmm, void *axlf, size_t size)

>> +{

>> +     int ret;

>> +

>> +     WARN_ON(!mutex_is_locked(&xmm->lock));

>> +

>> +     /*

>> +      * Should any error happens during download, we can't trust

>> +      * the cached xclbin any more.

>> +      */

>> +     vfree(xmm->firmware_ulp);

>> +     xmm->firmware_ulp = NULL;

>> +

>> +     ret = xmgmt_process_xclbin(xmm->pdev, xmm->fmgr, axlf, XMGMT_ULP);

>> +     if (ret == 0)

>> +             xmm->firmware_ulp = axlf;

>> +

>> +     return ret;

>> +}

>> +

>> +static int bitstream_axlf_ioctl(struct xmgmt_main *xmm, const void __user *arg)

>> +{

>> +     struct xmgmt_ioc_bitstream_axlf ioc_obj = { 0 };

>> +     struct axlf xclbin_obj = { {0} };

>> +     size_t copy_buffer_size = 0;

>> +     void *copy_buffer = NULL;

>> +     int ret = 0;

>> +

>> +     if (copy_from_user((void *)&ioc_obj, arg, sizeof(ioc_obj)))

>> +             return -EFAULT;

>> +     if (copy_from_user((void *)&xclbin_obj, ioc_obj.xclbin, sizeof(xclbin_obj)))

>> +             return -EFAULT;

>> +     if (memcmp(xclbin_obj.magic, XCLBIN_VERSION2, sizeof(XCLBIN_VERSION2)))

>> +             return -EINVAL;

>> +

>> +     copy_buffer_size = xclbin_obj.header.length;

>> +     if (copy_buffer_size > XCLBIN_MAX_SIZE || copy_buffer_size < sizeof(xclbin_obj))

> ok

>

> Tom

>

>> +             return -EINVAL;

>> +     if (xclbin_obj.header.version_major != XMGMT_SUPP_XCLBIN_MAJOR)

>> +             return -EINVAL;

>> +

>> +     copy_buffer = vmalloc(copy_buffer_size);

>> +     if (!copy_buffer)

>> +             return -ENOMEM;

>> +

>> +     if (copy_from_user(copy_buffer, ioc_obj.xclbin, copy_buffer_size)) {

>> +             vfree(copy_buffer);

>> +             return -EFAULT;

>> +     }

>> +

>> +     ret = xmgmt_bitstream_axlf_fpga_mgr(xmm, copy_buffer, copy_buffer_size);

>> +     if (ret)

>> +             vfree(copy_buffer);

>> +

>> +     return ret;

>> +}

>> +

>> +static long xmgmt_main_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

>> +{

>> +     struct xmgmt_main *xmm = filp->private_data;

>> +     long result = 0;

>> +

>> +     if (_IOC_TYPE(cmd) != XMGMT_IOC_MAGIC)

>> +             return -ENOTTY;

>> +

>> +     mutex_lock(&xmm->lock);

>> +

>> +     xrt_info(xmm->pdev, "ioctl cmd %d, arg %ld", cmd, arg);

>> +     switch (cmd) {

>> +     case XMGMT_IOCICAPDOWNLOAD_AXLF:

>> +             result = bitstream_axlf_ioctl(xmm, (const void __user *)arg);

>> +             break;

>> +     default:

>> +             result = -ENOTTY;

>> +             break;

>> +     }

>> +

>> +     mutex_unlock(&xmm->lock);

>> +     return result;

>> +}

>> +

>> +static struct xrt_subdev_endpoints xrt_mgmt_main_endpoints[] = {

>> +     {

>> +             .xse_names = (struct xrt_subdev_ep_names []){

>> +                     { .ep_name = XRT_MD_NODE_MGMT_MAIN },

>> +                     { NULL },

>> +             },

>> +             .xse_min_ep = 1,

>> +     },

>> +     { 0 },

>> +};

>> +

>> +static struct xrt_subdev_drvdata xmgmt_main_data = {

>> +     .xsd_dev_ops = {

>> +             .xsd_leaf_call = xmgmt_mainleaf_call,

>> +     },

>> +     .xsd_file_ops = {

>> +             .xsf_ops = {

>> +                     .owner = THIS_MODULE,

>> +                     .open = xmgmt_main_open,

>> +                     .release = xmgmt_main_close,

>> +                     .unlocked_ioctl = xmgmt_main_ioctl,

>> +             },

>> +             .xsf_dev_name = "xmgmt",

>> +     },

>> +};

>> +

>> +static const struct platform_device_id xmgmt_main_id_table[] = {

>> +     { XMGMT_MAIN, (kernel_ulong_t)&xmgmt_main_data },

>> +     { },

>> +};

>> +

>> +static struct platform_driver xmgmt_main_driver = {

>> +     .driver = {

>> +             .name    = XMGMT_MAIN,

>> +     },

>> +     .probe   = xmgmt_main_probe,

>> +     .remove  = xmgmt_main_remove,

>> +     .id_table = xmgmt_main_id_table,

>> +};

>> +

>> +int xmgmt_register_leaf(void)

>> +{

>> +     return xleaf_register_driver(XRT_SUBDEV_MGMT_MAIN,

>> +                                  &xmgmt_main_driver, xrt_mgmt_main_endpoints);

>> +}

>> +

>> +void xmgmt_unregister_leaf(void)

>> +{

>> +     xleaf_unregister_driver(XRT_SUBDEV_MGMT_MAIN);

>> +}

>> diff --git a/drivers/fpga/xrt/mgmt/xmgnt.h b/drivers/fpga/xrt/mgmt/xmgnt.h

>> new file mode 100644

>> index 000000000000..9d7c11194745

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/mgmt/xmgnt.h

>> @@ -0,0 +1,34 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors:

>> + *   Lizhi Hou <Lizhi.Hou@xilinx.com>

>> + *   Cheng Zhen <maxz@xilinx.com>

>> + */

>> +

>> +#ifndef _XMGMT_XMGNT_H_

>> +#define _XMGMT_XMGNT_H_

> For consistency, should be shortened to _XMGMNT_H_

>

>> +

>> +#include <linux/platform_device.h>

>> +#include "xmgmt-main.h"

>> +

>> +struct fpga_manager;

>> +int xmgmt_process_xclbin(struct platform_device *pdev,

>> +                      struct fpga_manager *fmgr,

>> +                      const struct axlf *xclbin,

>> +                      enum provider_kind kind);

>> +void xmgmt_region_cleanup_all(struct platform_device *pdev);

>> +

>> +int xmgmt_hot_reset(struct platform_device *pdev);

>> +

>> +/* Getting dtb for specified group. Caller should vfree returned dtb .*/

>> +char *xmgmt_get_dtb(struct platform_device *pdev, enum provider_kind kind);

>> +char *xmgmt_get_vbnv(struct platform_device *pdev);

>> +int xmgmt_get_provider_uuid(struct platform_device *pdev,

>> +                         enum provider_kind kind, uuid_t *uuid);

>> +

>> +int xmgmt_register_leaf(void);

> ok

>> +void xmgmt_unregister_leaf(void);

>> +

>> +#endif       /* _XMGMT_XMGNT_H_ */

>> diff --git a/include/uapi/linux/xrt/xmgmt-ioctl.h b/include/uapi/linux/xrt/xmgmt-ioctl.h

>> new file mode 100644

>> index 000000000000..da992e581189

>> --- /dev/null

>> +++ b/include/uapi/linux/xrt/xmgmt-ioctl.h

>> @@ -0,0 +1,46 @@

>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */

>> +/*

>> + *  Copyright (C) 2015-2021, Xilinx Inc

>> + *

>> + */

>> +

>> +/**

>> + * DOC: PCIe Kernel Driver for Management Physical Function

>> + * Interfaces exposed by *xclmgmt* driver are defined in file, *mgmt-ioctl.h*.

>> + * Core functionality provided by *xmgmt* driver is described in the following table:

>> + *

>> + * =========== ============================== ==================================

>> + * Functionality           ioctl request code           data format

>> + * =========== ============================== ==================================

>> + * 1 FPGA image download   XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf

>> + * =========== ============================== ==================================

>> + */

>> +

>> +#ifndef _XMGMT_IOCTL_H_

>> +#define _XMGMT_IOCTL_H_

>> +

>> +#include <linux/ioctl.h>

>> +

>> +#define XMGMT_IOC_MAGIC      'X'

>> +#define XMGMT_IOC_ICAP_DOWNLOAD_AXLF 0x6

>> +

>> +/**

>> + * struct xmgmt_ioc_bitstream_axlf - load xclbin (AXLF) device image

>> + * used with XMGMT_IOCICAPDOWNLOAD_AXLF ioctl

>> + *

>> + * @xclbin:  Pointer to user's xclbin structure in memory

>> + */

>> +struct xmgmt_ioc_bitstream_axlf {

>> +     struct axlf *xclbin;

>> +};

>> +

>> +#define XMGMT_IOCICAPDOWNLOAD_AXLF                           \

>> +     _IOW(XMGMT_IOC_MAGIC, XMGMT_IOC_ICAP_DOWNLOAD_AXLF, struct xmgmt_ioc_bitstream_axlf)

>> +

>> +/*

>> + * The following definitions are for binary compatibility with classic XRT management driver

>> + */

>> +#define XCLMGMT_IOCICAPDOWNLOAD_AXLF XMGMT_IOCICAPDOWNLOAD_AXLF

>> +#define xclmgmt_ioc_bitstream_axlf xmgmt_ioc_bitstream_axlf

>> +

>> +#endif
Lizhi Hou April 7, 2021, 10:41 p.m. UTC | #16
Hi Tom,


On 04/01/2021 07:43 AM, Tom Rix wrote:
> small alloc's should use kzalloc.

>

> On 3/23/21 10:29 PM, Lizhi Hou wrote:

>> fpga-mgr and region implementation for xclbin download which will be

>> called from main platform driver

>>

>> Signed-off-by: Sonal Santan <sonal.santan@xilinx.com>

>> Signed-off-by: Max Zhen <max.zhen@xilinx.com>

>> Signed-off-by: Lizhi Hou <lizhi.hou@xilinx.com>

>> ---

>>   drivers/fpga/xrt/mgmt/fmgr-drv.c    | 191 +++++++++++

>>   drivers/fpga/xrt/mgmt/fmgr.h        |  19 ++

>>   drivers/fpga/xrt/mgmt/main-region.c | 483 ++++++++++++++++++++++++++++

>>   3 files changed, 693 insertions(+)

>>   create mode 100644 drivers/fpga/xrt/mgmt/fmgr-drv.c

>>   create mode 100644 drivers/fpga/xrt/mgmt/fmgr.h

> a better file name would be xrt-mgr.*

Will change file name to xrt-mgr.*
>>   create mode 100644 drivers/fpga/xrt/mgmt/main-region.c

>>

>> diff --git a/drivers/fpga/xrt/mgmt/fmgr-drv.c b/drivers/fpga/xrt/mgmt/fmgr-drv.c

>> new file mode 100644

>> index 000000000000..12e1cc788ad9

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/mgmt/fmgr-drv.c

>> @@ -0,0 +1,191 @@

>> +// SPDX-License-Identifier: GPL-2.0

>> +/*

>> + * FPGA Manager Support for Xilinx Alveo Management Function Driver

> Since there is only one fpga mgr for xrt, this could be shortened to

>

> * FPGA Manager Support for Xilinx Alevo

Sure.
>

>> + *

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors: Sonal.Santan@xilinx.com

>> + */

>> +

>> +#include <linux/cred.h>

>> +#include <linux/efi.h>

>> +#include <linux/fpga/fpga-mgr.h>

>> +#include <linux/platform_device.h>

>> +#include <linux/module.h>

>> +#include <linux/vmalloc.h>

>> +

>> +#include "xclbin-helper.h"

>> +#include "xleaf.h"

>> +#include "fmgr.h"

>> +#include "xleaf/axigate.h"

>> +#include "xleaf/icap.h"

>> +#include "xmgnt.h"

>> +

>> +struct xfpga_class {

>> +     const struct platform_device *pdev;

>> +     char                          name[64];

>> +};

>> +

>> +/*

>> + * xclbin download plumbing -- find the download subsystem, ICAP and

>> + * pass the xclbin for heavy lifting

>> + */

>> +static int xmgmt_download_bitstream(struct platform_device *pdev,

>> +                                 const struct axlf *xclbin)

>> +

>> +{

>> +     struct xclbin_bit_head_info bit_header = { 0 };

>> +     struct platform_device *icap_leaf = NULL;

>> +     struct xrt_icap_wr arg;

>> +     char *bitstream = NULL;

>> +     u64 bit_len;

>> +     int ret;

>> +

>> +     ret = xrt_xclbin_get_section(DEV(pdev), xclbin, BITSTREAM, (void **)&bitstream, &bit_len);

>> +     if (ret) {

>> +             xrt_err(pdev, "bitstream not found");

>> +             return -ENOENT;

>> +     }

>> +     ret = xrt_xclbin_parse_bitstream_header(DEV(pdev), bitstream,

>> +                                             XCLBIN_HWICAP_BITFILE_BUF_SZ,

>> +                                             &bit_header);

>> +     if (ret) {

>> +             ret = -EINVAL;

>> +             xrt_err(pdev, "invalid bitstream header");

>> +             goto fail;

>> +     }

>> +     if (bit_header.header_length + bit_header.bitstream_length > bit_len) {

>> +             ret = -EINVAL;

>> +             xrt_err(pdev, "invalid bitstream length. header %d, bitstream %d, section len %lld",

>> +                     bit_header.header_length, bit_header.bitstream_length, bit_len);

>> +             goto fail;

>> +     }

>> +

>> +     icap_leaf = xleaf_get_leaf_by_id(pdev, XRT_SUBDEV_ICAP, PLATFORM_DEVID_NONE);

>> +     if (!icap_leaf) {

>> +             ret = -ENODEV;

>> +             xrt_err(pdev, "icap does not exist");

>> +             goto fail;

>> +     }

>> +     arg.xiiw_bit_data = bitstream + bit_header.header_length;

>> +     arg.xiiw_data_len = bit_header.bitstream_length;

>> +     ret = xleaf_call(icap_leaf, XRT_ICAP_WRITE, &arg);

>> +     if (ret) {

>> +             xrt_err(pdev, "write bitstream failed, ret = %d", ret);

>> +             xleaf_put_leaf(pdev, icap_leaf);

>> +             goto fail;

>> +     }

> ok, free_header removed

>> +

>> +     xleaf_put_leaf(pdev, icap_leaf);

>> +     vfree(bitstream);

>> +

>> +     return 0;

>> +

>> +fail:

>> +     vfree(bitstream);

>> +

>> +     return ret;

>> +}

>> +

>> +/*

>> + * There is no HW prep work we do here since we need the full

>> + * xclbin for its sanity check.

>> + */

>> +static int xmgmt_pr_write_init(struct fpga_manager *mgr,

>> +                            struct fpga_image_info *info,

>> +                            const char *buf, size_t count)

>> +{

>> +     const struct axlf *bin = (const struct axlf *)buf;

>> +     struct xfpga_class *obj = mgr->priv;

>> +

>> +     if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {

>> +             xrt_info(obj->pdev, "%s only supports partial reconfiguration\n", obj->name);

>> +             return -EINVAL;

>> +     }

>> +

>> +     if (count < sizeof(struct axlf))

>> +             return -EINVAL;

>> +

>> +     if (count > bin->header.length)

>> +             return -EINVAL;

>> +

>> +     xrt_info(obj->pdev, "Prepare download of xclbin %pUb of length %lld B",

>> +              &bin->header.uuid, bin->header.length);

>> +

>> +     return 0;

>> +}

>> +

>> +/*

>> + * The implementation requries full xclbin image before we can start

>> + * programming the hardware via ICAP subsystem. The full image is required

> ok

>> + * for checking the validity of xclbin and walking the sections to

>> + * discover the bitstream.

>> + */

>> +static int xmgmt_pr_write(struct fpga_manager *mgr,

>> +                       const char *buf, size_t count)

>> +{

>> +     const struct axlf *bin = (const struct axlf *)buf;

>> +     struct xfpga_class *obj = mgr->priv;

>> +

>> +     if (bin->header.length != count)

>> +             return -EINVAL;

>> +

>> +     return xmgmt_download_bitstream((void *)obj->pdev, bin);

>> +}

>> +

>> +static int xmgmt_pr_write_complete(struct fpga_manager *mgr,

>> +                                struct fpga_image_info *info)

>> +{

>> +     const struct axlf *bin = (const struct axlf *)info->buf;

>> +     struct xfpga_class *obj = mgr->priv;

>> +

>> +     xrt_info(obj->pdev, "Finished download of xclbin %pUb",

>> +              &bin->header.uuid);

>> +     return 0;

>> +}

>> +

>> +static enum fpga_mgr_states xmgmt_pr_state(struct fpga_manager *mgr)

>> +{

>> +     return FPGA_MGR_STATE_UNKNOWN;

> ok as-is

>> +}

>> +

>> +static const struct fpga_manager_ops xmgmt_pr_ops = {

>> +     .initial_header_size = sizeof(struct axlf),

>> +     .write_init = xmgmt_pr_write_init,

>> +     .write = xmgmt_pr_write,

>> +     .write_complete = xmgmt_pr_write_complete,

>> +     .state = xmgmt_pr_state,

>> +};

>> +

>> +struct fpga_manager *xmgmt_fmgr_probe(struct platform_device *pdev)

>> +{

>> +     struct xfpga_class *obj = devm_kzalloc(DEV(pdev), sizeof(struct xfpga_class),

>> +                                            GFP_KERNEL);

>> +     struct fpga_manager *fmgr = NULL;

>> +     int ret = 0;

>> +

>> +     if (!obj)

>> +             return ERR_PTR(-ENOMEM);

>> +

>> +     snprintf(obj->name, sizeof(obj->name), "Xilinx Alveo FPGA Manager");

>> +     obj->pdev = pdev;

>> +     fmgr = fpga_mgr_create(&pdev->dev,

>> +                            obj->name,

>> +                            &xmgmt_pr_ops,

>> +                            obj);

>> +     if (!fmgr)

>> +             return ERR_PTR(-ENOMEM);

>> +

>> +     ret = fpga_mgr_register(fmgr);

>> +     if (ret) {

>> +             fpga_mgr_free(fmgr);

>> +             return ERR_PTR(ret);

>> +     }

>> +     return fmgr;

>> +}

>> +

>> +int xmgmt_fmgr_remove(struct fpga_manager *fmgr)

>> +{

>> +     fpga_mgr_unregister(fmgr);

>> +     return 0;

>> +}

>> diff --git a/drivers/fpga/xrt/mgmt/fmgr.h b/drivers/fpga/xrt/mgmt/fmgr.h

>> new file mode 100644

>> index 000000000000..ff1fc5f870f8

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/mgmt/fmgr.h

>> @@ -0,0 +1,19 @@

>> +/* SPDX-License-Identifier: GPL-2.0 */

>> +/*

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + *

>> + * Authors: Sonal.Santan@xilinx.com

>> + */

>> +

>> +#ifndef _XMGMT_FMGR_H_

>> +#define _XMGMT_FMGR_H_

>> +

>> +#include <linux/fpga/fpga-mgr.h>

>> +#include <linux/mutex.h>

> why do mutex.h and xclbin.h need to be included ?

>

> consider removing them.

Sure.
>

>> +

>> +#include <linux/xrt/xclbin.h>

> ok enum removed.

>> +

>> +struct fpga_manager *xmgmt_fmgr_probe(struct platform_device *pdev);

>> +int xmgmt_fmgr_remove(struct fpga_manager *fmgr);

>> +

>> +#endif

>> diff --git a/drivers/fpga/xrt/mgmt/main-region.c b/drivers/fpga/xrt/mgmt/main-region.c

>> new file mode 100644

>> index 000000000000..96a674618e86

>> --- /dev/null

>> +++ b/drivers/fpga/xrt/mgmt/main-region.c

>> @@ -0,0 +1,483 @@

>> +// SPDX-License-Identifier: GPL-2.0

>> +/*

>> + * FPGA Region Support for Xilinx Alveo Management Function Driver

>> + *

>> + * Copyright (C) 2020-2021 Xilinx, Inc.

>> + * Bulk of the code borrowed from XRT mgmt driver file, fmgr.c

> review this line, there is not fmgr.c

Will remove this line.
>> + *

>> + * Authors: Lizhi.Hou@xilinx.com

>> + */

>> +

>> +#include <linux/uuid.h>

>> +#include <linux/fpga/fpga-bridge.h>

>> +#include <linux/fpga/fpga-region.h>

>> +#include "metadata.h"

>> +#include "xleaf.h"

>> +#include "xleaf/axigate.h"

>> +#include "xclbin-helper.h"

>> +#include "xmgnt.h"

>> +

>> +struct xmgmt_bridge {

>> +     struct platform_device *pdev;

>> +     const char *bridge_name;

> ok

>> +};

>> +

>> +struct xmgmt_region {

>> +     struct platform_device *pdev;

>> +     struct fpga_region *region;

>> +     struct fpga_compat_id compat_id;

>> +     uuid_t intf_uuid;

> interface_uuid

Sure.
>> +     struct fpga_bridge *bridge;

>> +     int group_instance;

>> +     uuid_t dep_uuid;

> dep ? expand.

Will use 'depend_uuid'
>> +     struct list_head list;

>> +};

>> +

>> +struct xmgmt_region_match_arg {

>> +     struct platform_device *pdev;

>> +     uuid_t *uuids;

>> +     u32 uuid_num;

>> +};

>> +

>> +static int xmgmt_br_enable_set(struct fpga_bridge *bridge, bool enable)

>> +{

>> +     struct xmgmt_bridge *br_data = (struct xmgmt_bridge *)bridge->priv;

>> +     struct platform_device *axigate_leaf;

>> +     int rc;

>> +

>> +     axigate_leaf = xleaf_get_leaf_by_epname(br_data->pdev, br_data->bridge_name);

>> +     if (!axigate_leaf) {

>> +             xrt_err(br_data->pdev, "failed to get leaf %s",

>> +                     br_data->bridge_name);

>> +             return -ENOENT;

>> +     }

>> +

>> +     if (enable)

>> +             rc = xleaf_call(axigate_leaf, XRT_AXIGATE_OPEN, NULL);

>> +     else

>> +             rc = xleaf_call(axigate_leaf, XRT_AXIGATE_CLOSE, NULL);

>> +

>> +     if (rc) {

>> +             xrt_err(br_data->pdev, "failed to %s gate %s, rc %d",

>> +                     (enable ? "free" : "freeze"), br_data->bridge_name,

>> +                     rc);

>> +     }

>> +

>> +     xleaf_put_leaf(br_data->pdev, axigate_leaf);

>> +

>> +     return rc;

>> +}

>> +

>> +const struct fpga_bridge_ops xmgmt_bridge_ops = {

>> +     .enable_set = xmgmt_br_enable_set

>> +};

>> +

>> +static void xmgmt_destroy_bridge(struct fpga_bridge *br)

>> +{

>> +     struct xmgmt_bridge *br_data = br->priv;

>> +

>> +     if (!br_data)

>> +             return;

>> +

>> +     xrt_info(br_data->pdev, "destroy fpga bridge %s", br_data->bridge_name);

>> +     fpga_bridge_unregister(br);

>> +

>> +     devm_kfree(DEV(br_data->pdev), br_data);

>> +

>> +     fpga_bridge_free(br);

>> +}

>> +

>> +static struct fpga_bridge *xmgmt_create_bridge(struct platform_device *pdev,

>> +                                            char *dtb)

>> +{

>> +     struct fpga_bridge *br = NULL;

>> +     struct xmgmt_bridge *br_data;

>> +     const char *gate;

>> +     int rc;

>> +

>> +     br_data = devm_kzalloc(DEV(pdev), sizeof(*br_data), GFP_KERNEL);

>> +     if (!br_data)

>> +             return NULL;

>> +     br_data->pdev = pdev;

>> +

>> +     br_data->bridge_name = XRT_MD_NODE_GATE_ULP;

>> +     rc = xrt_md_find_endpoint(&pdev->dev, dtb, XRT_MD_NODE_GATE_ULP,

>> +                               NULL, &gate);

>> +     if (rc) {

>> +             br_data->bridge_name = XRT_MD_NODE_GATE_PLP;

>> +             rc = xrt_md_find_endpoint(&pdev->dev, dtb, XRT_MD_NODE_GATE_PLP,

>> +                                       NULL, &gate);

>> +     }

>> +     if (rc) {

>> +             xrt_err(pdev, "failed to get axigate, rc %d", rc);

>> +             goto failed;

>> +     }

>> +

>> +     br = fpga_bridge_create(DEV(pdev), br_data->bridge_name,

>> +                             &xmgmt_bridge_ops, br_data);

>> +     if (!br) {

>> +             xrt_err(pdev, "failed to create bridge");

>> +             goto failed;

>> +     }

>> +

>> +     rc = fpga_bridge_register(br);

>> +     if (rc) {

>> +             xrt_err(pdev, "failed to register bridge, rc %d", rc);

>> +             goto failed;

>> +     }

>> +

>> +     xrt_info(pdev, "created fpga bridge %s", br_data->bridge_name);

>> +

>> +     return br;

>> +

>> +failed:

>> +     if (br)

>> +             fpga_bridge_free(br);

>> +     if (br_data)

>> +             devm_kfree(DEV(pdev), br_data);

>> +

>> +     return NULL;

>> +}

>> +

>> +static void xmgmt_destroy_region(struct fpga_region *region)

> ok

>> +{

>> +     struct xmgmt_region *r_data = region->priv;

>> +

>> +     xrt_info(r_data->pdev, "destroy fpga region %llx.%llx",

>> +              region->compat_id->id_l, region->compat_id->id_h);

> are the args ordered correctly ? I expected id_h to be first.

Will switch the order.
>> +

>> +     fpga_region_unregister(region);

>> +

>> +     if (r_data->group_instance > 0)

>> +             xleaf_destroy_group(r_data->pdev, r_data->group_instance);

>> +

>> +     if (r_data->bridge)

>> +             xmgmt_destroy_bridge(r_data->bridge);

>> +

>> +     if (r_data->region->info) {

>> +             fpga_image_info_free(r_data->region->info);

>> +             r_data->region->info = NULL;

>> +     }

>> +

>> +     fpga_region_free(region);

>> +

>> +     devm_kfree(DEV(r_data->pdev), r_data);

>> +}

>> +

>> +static int xmgmt_region_match(struct device *dev, const void *data)

>> +{

>> +     const struct xmgmt_region_match_arg *arg = data;

>> +     const struct fpga_region *match_region;

> ok

>> +     uuid_t compat_uuid;

>> +     int i;

>> +

>> +     if (dev->parent != &arg->pdev->dev)

>> +             return false;

>> +

>> +     match_region = to_fpga_region(dev);

>> +     /*

>> +      * The device tree provides both parent and child uuids for an

>> +      * xclbin in one array. Here we try both uuids to see if it matches

>> +      * with target region's compat_id. Strictly speaking we should

>> +      * only match xclbin's parent uuid with target region's compat_id

>> +      * but given the uuids by design are unique comparing with both

>> +      * does not hurt.

>> +      */

>> +     import_uuid(&compat_uuid, (const char *)match_region->compat_id);

>> +     for (i = 0; i < arg->uuid_num; i++) {

>> +             if (uuid_equal(&compat_uuid, &arg->uuids[i]))

>> +                     return true;

>> +     }

>> +

>> +     return false;

>> +}

>> +

>> +static int xmgmt_region_match_base(struct device *dev, const void *data)

>> +{

>> +     const struct xmgmt_region_match_arg *arg = data;

>> +     const struct fpga_region *match_region;

>> +     const struct xmgmt_region *r_data;

>> +

>> +     if (dev->parent != &arg->pdev->dev)

>> +             return false;

>> +

>> +     match_region = to_fpga_region(dev);

>> +     r_data = match_region->priv;

>> +     if (uuid_is_null(&r_data->dep_uuid))

>> +             return true;

>> +

>> +     return false;

>> +}

>> +

>> +static int xmgmt_region_match_by_uuid(struct device *dev, const void *data)

> ok

>> +{

>> +     const struct xmgmt_region_match_arg *arg = data;

>> +     const struct fpga_region *match_region;

>> +     const struct xmgmt_region *r_data;

>> +

>> +     if (dev->parent != &arg->pdev->dev)

>> +             return false;

>> +

>> +     if (arg->uuid_num != 1)

>> +             return false;

> ok

>> +

>> +     match_region = to_fpga_region(dev);

>> +     r_data = match_region->priv;

>> +     if (uuid_equal(&r_data->dep_uuid, arg->uuids))

>> +             return true;

>> +

>> +     return false;

>> +}

>> +

>> +static void xmgmt_region_cleanup(struct fpga_region *region)

>> +{

>> +     struct xmgmt_region *r_data = region->priv, *pdata, *temp;

>> +     struct platform_device *pdev = r_data->pdev;

>> +     struct xmgmt_region_match_arg arg = { 0 };

>> +     struct fpga_region *match_region = NULL;

>> +     struct device *start_dev = NULL;

>> +     LIST_HEAD(free_list);

>> +     uuid_t compat_uuid;

>> +

>> +     list_add_tail(&r_data->list, &free_list);

>> +     arg.pdev = pdev;

>> +     arg.uuid_num = 1;

>> +     arg.uuids = &compat_uuid;

>> +

>> +     /* find all regions depending on this region */

>> +     list_for_each_entry_safe(pdata, temp, &free_list, list) {

> ok

>> +             import_uuid(arg.uuids, (const char *)pdata->region->compat_id);

>> +             start_dev = NULL;

>> +             while ((match_region = fpga_region_class_find(start_dev, &arg,

>> +                                                           xmgmt_region_match_by_uuid))) {

>> +                     pdata = match_region->priv;

>> +                     list_add_tail(&pdata->list, &free_list);

>> +                     start_dev = &match_region->dev;

>> +                     put_device(&match_region->dev);

>> +             }

>> +     }

>> +

>> +     list_del(&r_data->list);

>> +

>> +     list_for_each_entry_safe_reverse(pdata, temp, &free_list, list)

>> +             xmgmt_destroy_region(pdata->region);

>> +

>> +     if (r_data->group_instance > 0) {

>> +             xleaf_destroy_group(pdev, r_data->group_instance);

>> +             r_data->group_instance = -1;

>> +     }

>> +     if (r_data->region->info) {

>> +             fpga_image_info_free(r_data->region->info);

>> +             r_data->region->info = NULL;

>> +     }

>> +}

>> +

>> +void xmgmt_region_cleanup_all(struct platform_device *pdev)

>> +{

>> +     struct xmgmt_region_match_arg arg = { 0 };

>> +     struct fpga_region *base_region;

>> +

>> +     arg.pdev = pdev;

>> +

>> +     while ((base_region = fpga_region_class_find(NULL, &arg, xmgmt_region_match_base))) {

> ok

>> +             put_device(&base_region->dev);

>> +

>> +             xmgmt_region_cleanup(base_region);

>> +             xmgmt_destroy_region(base_region);

>> +     }

>> +}

>> +

>> +/*

>> + * Program a region with a xclbin image. Bring up the subdevs and the

> ok

>> + * group object to contain the subdevs.

>> + */

>> +static int xmgmt_region_program(struct fpga_region *region, const void *xclbin, char *dtb)

>> +{

>> +     const struct axlf *xclbin_obj = xclbin;

>> +     struct fpga_image_info *info;

>> +     struct platform_device *pdev;

>> +     struct xmgmt_region *r_data;

>> +     int rc;

>> +

>> +     r_data = region->priv;

>> +     pdev = r_data->pdev;

>> +

>> +     info = fpga_image_info_alloc(&pdev->dev);

>> +     if (!info)

>> +             return -ENOMEM;

>> +

>> +     info->buf = xclbin;

>> +     info->count = xclbin_obj->header.length;

>> +     info->flags |= FPGA_MGR_PARTIAL_RECONFIG;

>> +     region->info = info;

>> +     rc = fpga_region_program_fpga(region);

>> +     if (rc) {

>> +             xrt_err(pdev, "programming xclbin failed, rc %d", rc);

>> +             return rc;

>> +     }

>> +

>> +     /* free bridges to allow reprogram */

>> +     if (region->get_bridges)

>> +             fpga_bridges_put(&region->bridge_list);

>> +

>> +     /*

>> +      * Next bringup the subdevs for this region which will be managed by

>> +      * its own group object.

>> +      */

>> +     r_data->group_instance = xleaf_create_group(pdev, dtb);

>> +     if (r_data->group_instance < 0) {

>> +             xrt_err(pdev, "failed to create group, rc %d",

>> +                     r_data->group_instance);

>> +             rc = r_data->group_instance;

>> +             return rc;

>> +     }

>> +

>> +     rc = xleaf_wait_for_group_bringup(pdev);

>> +     if (rc)

>> +             xrt_err(pdev, "group bringup failed, rc %d", rc);

>> +     return rc;

>> +}

>> +

>> +static int xmgmt_get_bridges(struct fpga_region *region)

>> +{

>> +     struct xmgmt_region *r_data = region->priv;

>> +     struct device *dev = &r_data->pdev->dev;

>> +

>> +     return fpga_bridge_get_to_list(dev, region->info, &region->bridge_list);

>> +}

>> +

>> +/*

>> + * Program/create FPGA regions based on input xclbin file.

> ok, dropped sentence

>> + * 1. Identify a matching existing region for this xclbin

>> + * 2. Tear down any previous objects for the found region

>> + * 3. Program this region with input xclbin

>> + * 4. Iterate over this region's interface uuids to determine if it defines any

>> + *    child region. Create fpga_region for the child region.

>> + */

>> +int xmgmt_process_xclbin(struct platform_device *pdev,

>> +                      struct fpga_manager *fmgr,

>> +                      const struct axlf *xclbin,

>> +                      enum provider_kind kind)

>> +{

>> +     struct fpga_region *region, *compat_region = NULL;

>> +     struct xmgmt_region_match_arg arg = { 0 };

> ok

>> +     struct xmgmt_region *r_data;

>> +     uuid_t compat_uuid;

>> +     char *dtb = NULL;

>> +     int rc, i;

>> +

>> +     rc = xrt_xclbin_get_metadata(DEV(pdev), xclbin, &dtb);

>> +     if (rc) {

>> +             xrt_err(pdev, "failed to get dtb: %d", rc);

>> +             goto failed;

>> +     }

>> +

>> +     rc = xrt_md_get_interface_uuids(DEV(pdev), dtb, 0, NULL);

>> +     if (rc < 0) {

>> +             xrt_err(pdev, "failed to get intf uuid");

>> +             rc = -EINVAL;

> ok

>> +             goto failed;

>> +     }

>> +     arg.uuid_num = rc;

>> +     arg.uuids = vzalloc(sizeof(uuid_t) * arg.uuid_num);

> uuids small, convert to bzalloc

Will change to kcalloc.
>> +     if (!arg.uuids) {

>> +             rc = -ENOMEM;

>> +             goto failed;

>> +     }

>> +     arg.pdev = pdev;

>> +

>> +     rc = xrt_md_get_interface_uuids(DEV(pdev), dtb, arg.uuid_num, arg.uuids);

>> +     if (rc != arg.uuid_num) {

>> +             xrt_err(pdev, "only get %d uuids, expect %d", rc, arg.uuid_num);

>> +             rc = -EINVAL;

>> +             goto failed;

>> +     }

>> +

>> +     /* if this is not base firmware, search for a compatible region */

>> +     if (kind != XMGMT_BLP) {

>> +             compat_region = fpga_region_class_find(NULL, &arg, xmgmt_region_match);

>> +             if (!compat_region) {

>> +                     xrt_err(pdev, "failed to get compatible region");

>> +                     rc = -ENOENT;

>> +                     goto failed;

>> +             }

>> +

>> +             xmgmt_region_cleanup(compat_region);

>> +

>> +             rc = xmgmt_region_program(compat_region, xclbin, dtb);

>> +             if (rc) {

>> +                     xrt_err(pdev, "failed to program region");

>> +                     goto failed;

>> +             }

>> +     }

>> +

>> +     if (compat_region)

>> +             import_uuid(&compat_uuid, (const char *)compat_region->compat_id);

>> +

>> +     /* create all the new regions contained in this xclbin */

>> +     for (i = 0; i < arg.uuid_num; i++) {

>> +             if (compat_region && uuid_equal(&compat_uuid, &arg.uuids[i])) {

>> +                     /* region for this interface already exists */

>> +                     continue;

>> +             }

>> +

>> +             region = fpga_region_create(DEV(pdev), fmgr, xmgmt_get_bridges);

>> +             if (!region) {

>> +                     xrt_err(pdev, "failed to create fpga region");

>> +                     rc = -EFAULT;

>> +                     goto failed;

>> +             }

>> +             r_data = devm_kzalloc(DEV(pdev), sizeof(*r_data), GFP_KERNEL);

>> +             if (!r_data) {

>> +                     rc = -ENOMEM;

>> +                     fpga_region_free(region);

>> +                     goto failed;

>> +             }

>> +             r_data->pdev = pdev;

>> +             r_data->region = region;

>> +             r_data->group_instance = -1;

>> +             uuid_copy(&r_data->intf_uuid, &arg.uuids[i]);

>> +             if (compat_region)

>> +                     import_uuid(&r_data->dep_uuid, (const char *)compat_region->compat_id);

>> +             r_data->bridge = xmgmt_create_bridge(pdev, dtb);

>> +             if (!r_data->bridge) {

>> +                     xrt_err(pdev, "failed to create fpga bridge");

>> +                     rc = -EFAULT;

>> +                     devm_kfree(DEV(pdev), r_data);

>> +                     fpga_region_free(region);

>> +                     goto failed;

>> +             }

>> +

>> +             region->compat_id = &r_data->compat_id;

>> +             export_uuid((char *)region->compat_id, &r_data->intf_uuid);

>> +             region->priv = r_data;

>> +

>> +             rc = fpga_region_register(region);

>> +             if (rc) {

>> +                     xrt_err(pdev, "failed to register fpga region");

>> +                     xmgmt_destroy_bridge(r_data->bridge);

>> +                     fpga_region_free(region);

>> +                     devm_kfree(DEV(pdev), r_data);

>> +                     goto failed;

>> +             }

>> +

>> +             xrt_info(pdev, "created fpga region %llx%llx",

>> +                      region->compat_id->id_l, region->compat_id->id_h);

> see above comment on id_h

>

> destroy's info used %llx.%llx, for consistency need to add or remove a '.'

Sure.

Thanks,
Lizhi
>

> Tom

>

>> +     }

>> +

>> +     if (compat_region)

>> +             put_device(&compat_region->dev);

>> +     vfree(dtb);

>> +     return 0;

>> +

>> +failed:

>> +     if (compat_region) {

>> +             put_device(&compat_region->dev);

>> +             xmgmt_region_cleanup(compat_region);

>> +     } else {

>> +             xmgmt_region_cleanup_all(pdev);

>> +     }

>> +

>> +     vfree(dtb);

>> +     return rc;

>> +}