Merge 5.11-rc3 into char-misc-next
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 11 Jan 2021 07:09:25 +0000 (08:09 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 11 Jan 2021 07:09:25 +0000 (08:09 +0100)
We need the char/misc fixes in here as well.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
25 files changed:
Documentation/ABI/testing/sysfs-bus-dfl-devices-emif [new file with mode: 0644]
Documentation/ABI/testing/sysfs-bus-dfl-devices-n3000-nios [new file with mode: 0644]
Documentation/fpga/dfl.rst
MAINTAINERS
drivers/bus/fsl-mc/fsl-mc-bus.c
drivers/fpga/Kconfig
drivers/fpga/Makefile
drivers/fpga/dfl-n3000-nios.c [new file with mode: 0644]
drivers/fpga/dfl-pci.c
drivers/fpga/dfl.c
drivers/fpga/dfl.h
drivers/greybus/greybus_trace.h
drivers/memory/Kconfig
drivers/memory/Makefile
drivers/memory/dfl-emif.c [new file with mode: 0644]
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/atmel_tclib.c [deleted file]
drivers/misc/cardreader/rtsx_pcr.c
drivers/misc/eeprom/eeprom_93xx46.c
drivers/misc/ocxl/file.c
include/linux/dfl.h [new file with mode: 0644]
include/linux/mod_devicetable.h
scripts/mod/devicetable-offsets.c
scripts/mod/file2alias.c

diff --git a/Documentation/ABI/testing/sysfs-bus-dfl-devices-emif b/Documentation/ABI/testing/sysfs-bus-dfl-devices-emif
new file mode 100644 (file)
index 0000000..817d141
--- /dev/null
@@ -0,0 +1,25 @@
+What:          /sys/bus/dfl/devices/dfl_dev.X/infX_cal_fail
+Date:          Oct 2020
+KernelVersion: 5.12
+Contact:       Xu Yilun <yilun.xu@intel.com>
+Description:   Read-only. It indicates if the calibration failed on this
+               memory interface. "1" for calibration failure, "0" for OK.
+               Format: %u
+
+What:          /sys/bus/dfl/devices/dfl_dev.X/infX_init_done
+Date:          Oct 2020
+KernelVersion: 5.12
+Contact:       Xu Yilun <yilun.xu@intel.com>
+Description:   Read-only. It indicates if the initialization completed on
+               this memory interface. "1" for initialization complete, "0"
+               for not yet.
+               Format: %u
+
+What:          /sys/bus/dfl/devices/dfl_dev.X/infX_clear
+Date:          Oct 2020
+KernelVersion: 5.12
+Contact:       Xu Yilun <yilun.xu@intel.com>
+Description:   Write-only. Writing "1" to this file will zero out all memory
+               data in this memory interface. Writing of other values is
+               invalid.
+               Format: %u
diff --git a/Documentation/ABI/testing/sysfs-bus-dfl-devices-n3000-nios b/Documentation/ABI/testing/sysfs-bus-dfl-devices-n3000-nios
new file mode 100644 (file)
index 0000000..5335d74
--- /dev/null
@@ -0,0 +1,47 @@
+What:          /sys/bus/dfl/devices/dfl_dev.X/fec_mode
+Date:          Oct 2020
+KernelVersion: 5.12
+Contact:       Xu Yilun <yilun.xu@intel.com>
+Description:   Read-only. Returns the FEC mode of the 25G links of the
+               ethernet retimers configured by Nios firmware. "rs" for Reed
+               Solomon FEC, "kr" for Fire Code FEC, "no" for NO FEC.
+               "not supported" if the FEC mode setting is not supported, this
+               happens when the Nios firmware version major < 3, or no link is
+               configured to 25G.
+               Format: string
+
+What:          /sys/bus/dfl/devices/dfl_dev.X/retimer_A_mode
+Date:          Oct 2020
+KernelVersion: 5.12
+Contact:       Xu Yilun <yilun.xu@intel.com>
+Description:   Read-only. Returns the enumeration value of the working mode of
+               the retimer A configured by the Nios firmware. The value is
+               read out from shared registers filled by the Nios firmware. Now
+               the values could be:
+
+               - "0": Reset
+               - "1": 4x10G
+               - "2": 4x25G
+               - "3": 2x25G
+               - "4": 2x25G+2x10G
+               - "5": 1x25G
+
+               If the Nios firmware is updated in future to support more
+               retimer modes, more enumeration value is expected.
+               Format: 0x%x
+
+What:          /sys/bus/dfl/devices/dfl_dev.X/retimer_B_mode
+Date:          Oct 2020
+KernelVersion: 5.12
+Contact:       Xu Yilun <yilun.xu@intel.com>
+Description:   Read-only. Returns the enumeration value of the working mode of
+               the retimer B configured by the Nios firmware. The value format
+               is the same as retimer_A_mode.
+
+What:          /sys/bus/dfl/devices/dfl_dev.X/nios_fw_version
+Date:          Oct 2020
+KernelVersion: 5.12
+Contact:       Xu Yilun <yilun.xu@intel.com>
+Description:   Read-only. Returns the version of the Nios firmware in the
+               FPGA. Its format is "major.minor.patch".
+               Format: %x.%x.%x
index 0404fe6..ea8cefc 100644 (file)
@@ -501,6 +501,33 @@ Developer only needs to provide a sub feature driver with matched feature id.
 FME Partial Reconfiguration Sub Feature driver (see drivers/fpga/dfl-fme-pr.c)
 could be a reference.
 
+Location of DFLs on a PCI Device
+===========================
+The original method for finding a DFL on a PCI device assumed the start of the
+first DFL to offset 0 of bar 0.  If the first node of the DFL is an FME,
+then further DFLs in the port(s) are specified in FME header registers.
+Alternatively, a PCIe vendor specific capability structure can be used to
+specify the location of all the DFLs on the device, providing flexibility
+for the type of starting node in the DFL.  Intel has reserved the
+VSEC ID of 0x43 for this purpose.  The vendor specific
+data begins with a 4 byte vendor specific register for the number of DFLs followed 4 byte
+Offset/BIR vendor specific registers for each DFL. Bits 2:0 of Offset/BIR register
+indicates the BAR, and bits 31:3 form the 8 byte aligned offset where bits 2:0 are
+zero.
+
+        +----------------------------+
+        |31     Number of DFLS      0|
+        +----------------------------+
+        |31     Offset     3|2 BIR  0|
+        +----------------------------+
+                      . . .
+        +----------------------------+
+        |31     Offset     3|2 BIR  0|
+        +----------------------------+
+
+Being able to specify more than one DFL per BAR has been considered, but it
+was determined the use case did not provide value.  Specifying a single DFL
+per BAR simplifies the implementation and allows for extra error checking.
 
 Open discussion
 ===============
index cc1e6a5..f084d69 100644 (file)
@@ -6954,9 +6954,10 @@ M:       Wu Hao <hao.wu@intel.com>
 R:     Tom Rix <trix@redhat.com>
 L:     linux-fpga@vger.kernel.org
 S:     Maintained
-F:     Documentation/ABI/testing/sysfs-bus-dfl
+F:     Documentation/ABI/testing/sysfs-bus-dfl*
 F:     Documentation/fpga/dfl.rst
 F:     drivers/fpga/dfl*
+F:     include/linux/dfl.h
 F:     include/uapi/linux/fpga-dfl.h
 
 FPGA MANAGER FRAMEWORK
index b8e6acd..5a8fc68 100644 (file)
@@ -41,7 +41,7 @@ struct fsl_mc {
        struct fsl_mc_device *root_mc_bus_dev;
        u8 num_translation_ranges;
        struct fsl_mc_addr_translation_range *translation_ranges;
-       void *fsl_mc_regs;
+       void __iomem *fsl_mc_regs;
 };
 
 /**
index 5645226..5ff9438 100644 (file)
@@ -192,6 +192,17 @@ config FPGA_DFL_AFU
          to the FPGA infrastructure via a Port. There may be more than one
          Port/AFU per DFL based FPGA device.
 
+config FPGA_DFL_NIOS_INTEL_PAC_N3000
+       tristate "FPGA DFL NIOS Driver for Intel PAC N3000"
+       depends on FPGA_DFL
+       select REGMAP
+       help
+         This is the driver for the N3000 Nios private feature on Intel
+         PAC (Programmable Acceleration Card) N3000. It communicates
+         with the embedded Nios processor to configure the retimers on
+         the card. It also instantiates the SPI master (spi-altera) for
+         the card's BMC (Board Management Controller).
+
 config FPGA_DFL_PCI
        tristate "FPGA DFL PCIe Device Driver"
        depends on PCI && FPGA_DFL
index d8e21df..18dc988 100644 (file)
@@ -44,5 +44,7 @@ dfl-fme-objs += dfl-fme-perf.o
 dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o dfl-afu-dma-region.o
 dfl-afu-objs += dfl-afu-error.o
 
+obj-$(CONFIG_FPGA_DFL_NIOS_INTEL_PAC_N3000)    += dfl-n3000-nios.o
+
 # Drivers for FPGAs which implement DFL
 obj-$(CONFIG_FPGA_DFL_PCI)             += dfl-pci.o
diff --git a/drivers/fpga/dfl-n3000-nios.c b/drivers/fpga/dfl-n3000-nios.c
new file mode 100644 (file)
index 0000000..7a95366
--- /dev/null
@@ -0,0 +1,588 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DFL device driver for Nios private feature on Intel PAC (Programmable
+ * Acceleration Card) N3000
+ *
+ * Copyright (C) 2019-2020 Intel Corporation, Inc.
+ *
+ * Authors:
+ *   Wu Hao <hao.wu@intel.com>
+ *   Xu Yilun <yilun.xu@intel.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/dfl.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/stddef.h>
+#include <linux/spi/altera.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+/*
+ * N3000 Nios private feature registers, named as NIOS_SPI_XX on spec.
+ * NS is the abbreviation of NIOS_SPI.
+ */
+#define N3000_NS_PARAM                         0x8
+#define N3000_NS_PARAM_SHIFT_MODE_MSK          BIT_ULL(1)
+#define N3000_NS_PARAM_SHIFT_MODE_MSB          0
+#define N3000_NS_PARAM_SHIFT_MODE_LSB          1
+#define N3000_NS_PARAM_DATA_WIDTH              GENMASK_ULL(7, 2)
+#define N3000_NS_PARAM_NUM_CS                  GENMASK_ULL(13, 8)
+#define N3000_NS_PARAM_CLK_POL                 BIT_ULL(14)
+#define N3000_NS_PARAM_CLK_PHASE               BIT_ULL(15)
+#define N3000_NS_PARAM_PERIPHERAL_ID           GENMASK_ULL(47, 32)
+
+#define N3000_NS_CTRL                          0x10
+#define N3000_NS_CTRL_WR_DATA                  GENMASK_ULL(31, 0)
+#define N3000_NS_CTRL_ADDR                     GENMASK_ULL(44, 32)
+#define N3000_NS_CTRL_CMD_MSK                  GENMASK_ULL(63, 62)
+#define N3000_NS_CTRL_CMD_NOP                  0
+#define N3000_NS_CTRL_CMD_RD                   1
+#define N3000_NS_CTRL_CMD_WR                   2
+
+#define N3000_NS_STAT                          0x18
+#define N3000_NS_STAT_RD_DATA                  GENMASK_ULL(31, 0)
+#define N3000_NS_STAT_RW_VAL                   BIT_ULL(32)
+
+/* Nios handshake registers, indirect access */
+#define N3000_NIOS_INIT                                0x1000
+#define N3000_NIOS_INIT_DONE                   BIT(0)
+#define N3000_NIOS_INIT_START                  BIT(1)
+/* Mode for retimer A, link 0, the same below */
+#define N3000_NIOS_INIT_REQ_FEC_MODE_A0_MSK    GENMASK(9, 8)
+#define N3000_NIOS_INIT_REQ_FEC_MODE_A1_MSK    GENMASK(11, 10)
+#define N3000_NIOS_INIT_REQ_FEC_MODE_A2_MSK    GENMASK(13, 12)
+#define N3000_NIOS_INIT_REQ_FEC_MODE_A3_MSK    GENMASK(15, 14)
+#define N3000_NIOS_INIT_REQ_FEC_MODE_B0_MSK    GENMASK(17, 16)
+#define N3000_NIOS_INIT_REQ_FEC_MODE_B1_MSK    GENMASK(19, 18)
+#define N3000_NIOS_INIT_REQ_FEC_MODE_B2_MSK    GENMASK(21, 20)
+#define N3000_NIOS_INIT_REQ_FEC_MODE_B3_MSK    GENMASK(23, 22)
+#define N3000_NIOS_INIT_REQ_FEC_MODE_NO                0x0
+#define N3000_NIOS_INIT_REQ_FEC_MODE_KR                0x1
+#define N3000_NIOS_INIT_REQ_FEC_MODE_RS                0x2
+
+#define N3000_NIOS_FW_VERSION                  0x1004
+#define N3000_NIOS_FW_VERSION_PATCH            GENMASK(23, 20)
+#define N3000_NIOS_FW_VERSION_MINOR            GENMASK(27, 24)
+#define N3000_NIOS_FW_VERSION_MAJOR            GENMASK(31, 28)
+
+/* The retimers we use on Intel PAC N3000 is Parkvale, abbreviated to PKVL */
+#define N3000_NIOS_PKVL_A_MODE_STS             0x1020
+#define N3000_NIOS_PKVL_B_MODE_STS             0x1024
+#define N3000_NIOS_PKVL_MODE_STS_GROUP_MSK     GENMASK(15, 8)
+#define N3000_NIOS_PKVL_MODE_STS_GROUP_OK      0x0
+#define N3000_NIOS_PKVL_MODE_STS_ID_MSK                GENMASK(7, 0)
+/* When GROUP MASK field == GROUP_OK  */
+#define N3000_NIOS_PKVL_MODE_ID_RESET          0x0
+#define N3000_NIOS_PKVL_MODE_ID_4X10G          0x1
+#define N3000_NIOS_PKVL_MODE_ID_4X25G          0x2
+#define N3000_NIOS_PKVL_MODE_ID_2X25G          0x3
+#define N3000_NIOS_PKVL_MODE_ID_2X25G_2X10G    0x4
+#define N3000_NIOS_PKVL_MODE_ID_1X25G          0x5
+
+#define N3000_NIOS_REGBUS_RETRY_COUNT          10000   /* loop count */
+
+#define N3000_NIOS_INIT_TIMEOUT                        10000000        /* usec */
+#define N3000_NIOS_INIT_TIME_INTV              100000          /* usec */
+
+#define N3000_NIOS_INIT_REQ_FEC_MODE_MSK_ALL   \
+       (N3000_NIOS_INIT_REQ_FEC_MODE_A0_MSK |  \
+        N3000_NIOS_INIT_REQ_FEC_MODE_A1_MSK |  \
+        N3000_NIOS_INIT_REQ_FEC_MODE_A2_MSK |  \
+        N3000_NIOS_INIT_REQ_FEC_MODE_A3_MSK |  \
+        N3000_NIOS_INIT_REQ_FEC_MODE_B0_MSK |  \
+        N3000_NIOS_INIT_REQ_FEC_MODE_B1_MSK |  \
+        N3000_NIOS_INIT_REQ_FEC_MODE_B2_MSK |  \
+        N3000_NIOS_INIT_REQ_FEC_MODE_B3_MSK)
+
+#define N3000_NIOS_INIT_REQ_FEC_MODE_NO_ALL                    \
+       (FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A0_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_NO) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A1_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_NO) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A2_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_NO) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A3_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_NO) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B0_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_NO) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B1_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_NO) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B2_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_NO) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B3_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_NO))
+
+#define N3000_NIOS_INIT_REQ_FEC_MODE_KR_ALL                    \
+       (FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A0_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_KR) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A1_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_KR) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A2_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_KR) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A3_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_KR) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B0_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_KR) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B1_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_KR) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B2_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_KR) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B3_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_KR))
+
+#define N3000_NIOS_INIT_REQ_FEC_MODE_RS_ALL                    \
+       (FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A0_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_RS) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A1_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_RS) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A2_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_RS) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_A3_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_RS) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B0_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_RS) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B1_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_RS) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B2_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_RS) |          \
+        FIELD_PREP(N3000_NIOS_INIT_REQ_FEC_MODE_B3_MSK,        \
+                   N3000_NIOS_INIT_REQ_FEC_MODE_RS))
+
+struct n3000_nios {
+       void __iomem *base;
+       struct regmap *regmap;
+       struct device *dev;
+       struct platform_device *altera_spi;
+};
+
+static ssize_t nios_fw_version_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct n3000_nios *nn = dev_get_drvdata(dev);
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(nn->regmap, N3000_NIOS_FW_VERSION, &val);
+       if (ret)
+               return ret;
+
+       return sysfs_emit(buf, "%x.%x.%x\n",
+                         (u8)FIELD_GET(N3000_NIOS_FW_VERSION_MAJOR, val),
+                         (u8)FIELD_GET(N3000_NIOS_FW_VERSION_MINOR, val),
+                         (u8)FIELD_GET(N3000_NIOS_FW_VERSION_PATCH, val));
+}
+static DEVICE_ATTR_RO(nios_fw_version);
+
+#define IS_MODE_STATUS_OK(mode_stat)                                   \
+       (FIELD_GET(N3000_NIOS_PKVL_MODE_STS_GROUP_MSK, (mode_stat)) ==  \
+        N3000_NIOS_PKVL_MODE_STS_GROUP_OK)
+
+#define IS_RETIMER_FEC_SUPPORTED(retimer_mode)                 \
+       ((retimer_mode) != N3000_NIOS_PKVL_MODE_ID_RESET &&     \
+        (retimer_mode) != N3000_NIOS_PKVL_MODE_ID_4X10G)
+
+static int get_retimer_mode(struct n3000_nios *nn, unsigned int mode_stat_reg,
+                           unsigned int *retimer_mode)
+{
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(nn->regmap, mode_stat_reg, &val);
+       if (ret)
+               return ret;
+
+       if (!IS_MODE_STATUS_OK(val))
+               return -EFAULT;
+
+       *retimer_mode = FIELD_GET(N3000_NIOS_PKVL_MODE_STS_ID_MSK, val);
+
+       return 0;
+}
+
+static ssize_t retimer_A_mode_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct n3000_nios *nn = dev_get_drvdata(dev);
+       unsigned int mode;
+       int ret;
+
+       ret = get_retimer_mode(nn, N3000_NIOS_PKVL_A_MODE_STS, &mode);
+       if (ret)
+               return ret;
+
+       return sysfs_emit(buf, "0x%x\n", mode);
+}
+static DEVICE_ATTR_RO(retimer_A_mode);
+
+static ssize_t retimer_B_mode_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct n3000_nios *nn = dev_get_drvdata(dev);
+       unsigned int mode;
+       int ret;
+
+       ret = get_retimer_mode(nn, N3000_NIOS_PKVL_B_MODE_STS, &mode);
+       if (ret)
+               return ret;
+
+       return sysfs_emit(buf, "0x%x\n", mode);
+}
+static DEVICE_ATTR_RO(retimer_B_mode);
+
+static ssize_t fec_mode_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       unsigned int val, retimer_a_mode, retimer_b_mode, fec_modes;
+       struct n3000_nios *nn = dev_get_drvdata(dev);
+       int ret;
+
+       /* FEC mode setting is not supported in early FW versions */
+       ret = regmap_read(nn->regmap, N3000_NIOS_FW_VERSION, &val);
+       if (ret)
+               return ret;
+
+       if (FIELD_GET(N3000_NIOS_FW_VERSION_MAJOR, val) < 3)
+               return sysfs_emit(buf, "not supported\n");
+
+       /* If no 25G links, FEC mode setting is not supported either */
+       ret = get_retimer_mode(nn, N3000_NIOS_PKVL_A_MODE_STS, &retimer_a_mode);
+       if (ret)
+               return ret;
+
+       ret = get_retimer_mode(nn, N3000_NIOS_PKVL_B_MODE_STS, &retimer_b_mode);
+       if (ret)
+               return ret;
+
+       if (!IS_RETIMER_FEC_SUPPORTED(retimer_a_mode) &&
+           !IS_RETIMER_FEC_SUPPORTED(retimer_b_mode))
+               return sysfs_emit(buf, "not supported\n");
+
+       /* get the valid FEC mode for 25G links */
+       ret = regmap_read(nn->regmap, N3000_NIOS_INIT, &val);
+       if (ret)
+               return ret;
+
+       /*
+        * FEC mode should always be the same for all links, as we set them
+        * in this way.
+        */
+       fec_modes = (val & N3000_NIOS_INIT_REQ_FEC_MODE_MSK_ALL);
+       if (fec_modes == N3000_NIOS_INIT_REQ_FEC_MODE_NO_ALL)
+               return sysfs_emit(buf, "no\n");
+       else if (fec_modes == N3000_NIOS_INIT_REQ_FEC_MODE_KR_ALL)
+               return sysfs_emit(buf, "kr\n");
+       else if (fec_modes == N3000_NIOS_INIT_REQ_FEC_MODE_RS_ALL)
+               return sysfs_emit(buf, "rs\n");
+
+       return -EFAULT;
+}
+static DEVICE_ATTR_RO(fec_mode);
+
+static struct attribute *n3000_nios_attrs[] = {
+       &dev_attr_nios_fw_version.attr,
+       &dev_attr_retimer_A_mode.attr,
+       &dev_attr_retimer_B_mode.attr,
+       &dev_attr_fec_mode.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(n3000_nios);
+
+static int n3000_nios_init_done_check(struct n3000_nios *nn)
+{
+       unsigned int val, state_a, state_b;
+       struct device *dev = nn->dev;
+       int ret, ret2;
+
+       /*
+        * The SPI is shared by the Nios core inside the FPGA, Nios will use
+        * this SPI master to do some one time initialization after power up,
+        * and then release the control to OS. The driver needs to poll on
+        * INIT_DONE to see when driver could take the control.
+        *
+        * Please note that after Nios firmware version 3.0.0, INIT_START is
+        * introduced, so driver needs to trigger START firstly and then check
+        * INIT_DONE.
+        */
+
+       ret = regmap_read(nn->regmap, N3000_NIOS_FW_VERSION, &val);
+       if (ret)
+               return ret;
+
+       /*
+        * If Nios version register is totally uninitialized(== 0x0), then the
+        * Nios firmware is missing. So host could take control of SPI master
+        * safely, but initialization work for Nios is not done. To restore the
+        * card, we need to reprogram a new Nios firmware via the BMC chip on
+        * SPI bus. So the driver doesn't error out, it continues to create the
+        * spi controller device and spi_board_info for BMC.
+        */
+       if (val == 0) {
+               dev_err(dev, "Nios version reg = 0x%x, skip INIT_DONE check, but the retimer may be uninitialized\n",
+                       val);
+               return 0;
+       }
+
+       if (FIELD_GET(N3000_NIOS_FW_VERSION_MAJOR, val) >= 3) {
+               /* read NIOS_INIT to check if retimer initialization is done */
+               ret = regmap_read(nn->regmap, N3000_NIOS_INIT, &val);
+               if (ret)
+                       return ret;
+
+               /* check if retimers are initialized already */
+               if (val & (N3000_NIOS_INIT_DONE | N3000_NIOS_INIT_START))
+                       goto nios_init_done;
+
+               /* configure FEC mode per module param */
+               val = N3000_NIOS_INIT_START;
+
+               /*
+                * When the retimer is to be set to 10G mode, there is no FEC
+                * mode setting, so the REQ_FEC_MODE field will be ignored by
+                * Nios firmware in this case. But we should still fill the FEC
+                * mode field cause host could not get the retimer working mode
+                * until the Nios init is done.
+                *
+                * For now the driver doesn't support the retimer FEC mode
+                * switching per user's request. It is always set to Reed
+                * Solomon FEC.
+                *
+                * The driver will set the same FEC mode for all links.
+                */
+               val |= N3000_NIOS_INIT_REQ_FEC_MODE_RS_ALL;
+
+               ret = regmap_write(nn->regmap, N3000_NIOS_INIT, val);
+               if (ret)
+                       return ret;
+       }
+
+nios_init_done:
+       /* polls on NIOS_INIT_DONE */
+       ret = regmap_read_poll_timeout(nn->regmap, N3000_NIOS_INIT, val,
+                                      val & N3000_NIOS_INIT_DONE,
+                                      N3000_NIOS_INIT_TIME_INTV,
+                                      N3000_NIOS_INIT_TIMEOUT);
+       if (ret)
+               dev_err(dev, "NIOS_INIT_DONE %s\n",
+                       (ret == -ETIMEDOUT) ? "timed out" : "check error");
+
+       ret2 = regmap_read(nn->regmap, N3000_NIOS_PKVL_A_MODE_STS, &state_a);
+       if (ret2)
+               return ret2;
+
+       ret2 = regmap_read(nn->regmap, N3000_NIOS_PKVL_B_MODE_STS, &state_b);
+       if (ret2)
+               return ret2;
+
+       if (!ret) {
+               /*
+                * After INIT_DONE is detected, it still needs to check if the
+                * Nios firmware reports any error during the retimer
+                * configuration.
+                */
+               if (IS_MODE_STATUS_OK(state_a) && IS_MODE_STATUS_OK(state_b))
+                       return 0;
+
+               /*
+                * If the retimer configuration is failed, the Nios firmware
+                * will still release the spi controller for host to
+                * communicate with the BMC. It makes possible for people to
+                * reprogram a new Nios firmware and restore the card. So the
+                * driver doesn't error out, it continues to create the spi
+                * controller device and spi_board_info for BMC.
+                */
+               dev_err(dev, "NIOS_INIT_DONE OK, but err on retimer init\n");
+       }
+
+       dev_err(nn->dev, "PKVL_A_MODE_STS 0x%x\n", state_a);
+       dev_err(nn->dev, "PKVL_B_MODE_STS 0x%x\n", state_b);
+
+       return ret;
+}
+
+static struct spi_board_info m10_n3000_info = {
+       .modalias = "m10-n3000",
+       .max_speed_hz = 12500000,
+       .bus_num = 0,
+       .chip_select = 0,
+};
+
+static int create_altera_spi_controller(struct n3000_nios *nn)
+{
+       struct altera_spi_platform_data pdata = { 0 };
+       struct platform_device_info pdevinfo = { 0 };
+       void __iomem *base = nn->base;
+       u64 v;
+
+       v = readq(base + N3000_NS_PARAM);
+
+       pdata.mode_bits = SPI_CS_HIGH;
+       if (FIELD_GET(N3000_NS_PARAM_CLK_POL, v))
+               pdata.mode_bits |= SPI_CPOL;
+       if (FIELD_GET(N3000_NS_PARAM_CLK_PHASE, v))
+               pdata.mode_bits |= SPI_CPHA;
+
+       pdata.num_chipselect = FIELD_GET(N3000_NS_PARAM_NUM_CS, v);
+       pdata.bits_per_word_mask =
+               SPI_BPW_RANGE_MASK(1, FIELD_GET(N3000_NS_PARAM_DATA_WIDTH, v));
+
+       pdata.num_devices = 1;
+       pdata.devices = &m10_n3000_info;
+
+       dev_dbg(nn->dev, "%s cs %u bpm 0x%x mode 0x%x\n", __func__,
+               pdata.num_chipselect, pdata.bits_per_word_mask,
+               pdata.mode_bits);
+
+       pdevinfo.name = "subdev_spi_altera";
+       pdevinfo.id = PLATFORM_DEVID_AUTO;
+       pdevinfo.parent = nn->dev;
+       pdevinfo.data = &pdata;
+       pdevinfo.size_data = sizeof(pdata);
+
+       nn->altera_spi = platform_device_register_full(&pdevinfo);
+       return PTR_ERR_OR_ZERO(nn->altera_spi);
+}
+
+static void destroy_altera_spi_controller(struct n3000_nios *nn)
+{
+       platform_device_unregister(nn->altera_spi);
+}
+
+static int n3000_nios_poll_stat_timeout(void __iomem *base, u64 *v)
+{
+       int loops;
+
+       /*
+        * We don't use the time based timeout here for performance.
+        *
+        * The regbus read/write is on the critical path of Intel PAC N3000
+        * image programing. The time based timeout checking will add too much
+        * overhead on it. Usually the state changes in 1 or 2 loops on the
+        * test server, and we set 10000 times loop here for safety.
+        */
+       for (loops = N3000_NIOS_REGBUS_RETRY_COUNT; loops > 0 ; loops--) {
+               *v = readq(base + N3000_NS_STAT);
+               if (*v & N3000_NS_STAT_RW_VAL)
+                       break;
+               cpu_relax();
+       }
+
+       return (loops > 0) ? 0 : -ETIMEDOUT;
+}
+
+static int n3000_nios_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct n3000_nios *nn = context;
+       u64 v;
+       int ret;
+
+       v = FIELD_PREP(N3000_NS_CTRL_CMD_MSK, N3000_NS_CTRL_CMD_WR) |
+           FIELD_PREP(N3000_NS_CTRL_ADDR, reg) |
+           FIELD_PREP(N3000_NS_CTRL_WR_DATA, val);
+       writeq(v, nn->base + N3000_NS_CTRL);
+
+       ret = n3000_nios_poll_stat_timeout(nn->base, &v);
+       if (ret)
+               dev_err(nn->dev, "fail to write reg 0x%x val 0x%x: %d\n",
+                       reg, val, ret);
+
+       return ret;
+}
+
+static int n3000_nios_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct n3000_nios *nn = context;
+       u64 v;
+       int ret;
+
+       v = FIELD_PREP(N3000_NS_CTRL_CMD_MSK, N3000_NS_CTRL_CMD_RD) |
+           FIELD_PREP(N3000_NS_CTRL_ADDR, reg);
+       writeq(v, nn->base + N3000_NS_CTRL);
+
+       ret = n3000_nios_poll_stat_timeout(nn->base, &v);
+       if (ret)
+               dev_err(nn->dev, "fail to read reg 0x%x: %d\n", reg, ret);
+       else
+               *val = FIELD_GET(N3000_NS_STAT_RD_DATA, v);
+
+       return ret;
+}
+
+static const struct regmap_config n3000_nios_regbus_cfg = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .fast_io = true,
+
+       .reg_write = n3000_nios_reg_write,
+       .reg_read = n3000_nios_reg_read,
+};
+
+static int n3000_nios_probe(struct dfl_device *ddev)
+{
+       struct device *dev = &ddev->dev;
+       struct n3000_nios *nn;
+       int ret;
+
+       nn = devm_kzalloc(dev, sizeof(*nn), GFP_KERNEL);
+       if (!nn)
+               return -ENOMEM;
+
+       dev_set_drvdata(&ddev->dev, nn);
+
+       nn->dev = dev;
+
+       nn->base = devm_ioremap_resource(&ddev->dev, &ddev->mmio_res);
+       if (IS_ERR(nn->base))
+               return PTR_ERR(nn->base);
+
+       nn->regmap = devm_regmap_init(dev, NULL, nn, &n3000_nios_regbus_cfg);
+       if (IS_ERR(nn->regmap))
+               return PTR_ERR(nn->regmap);
+
+       ret = n3000_nios_init_done_check(nn);
+       if (ret)
+               return ret;
+
+       ret = create_altera_spi_controller(nn);
+       if (ret)
+               dev_err(dev, "altera spi controller create failed: %d\n", ret);
+
+       return ret;
+}
+
+static void n3000_nios_remove(struct dfl_device *ddev)
+{
+       struct n3000_nios *nn = dev_get_drvdata(&ddev->dev);
+
+       destroy_altera_spi_controller(nn);
+}
+
+#define FME_FEATURE_ID_N3000_NIOS      0xd
+
+static const struct dfl_device_id n3000_nios_ids[] = {
+       { FME_ID, FME_FEATURE_ID_N3000_NIOS },
+       { }
+};
+MODULE_DEVICE_TABLE(dfl, n3000_nios_ids);
+
+static struct dfl_driver n3000_nios_driver = {
+       .drv    = {
+               .name       = "dfl-n3000-nios",
+               .dev_groups = n3000_nios_groups,
+       },
+       .id_table = n3000_nios_ids,
+       .probe   = n3000_nios_probe,
+       .remove  = n3000_nios_remove,
+};
+
+module_dfl_driver(n3000_nios_driver);
+
+MODULE_DESCRIPTION("Driver for Nios private feature on Intel PAC N3000");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
index a2203d0..04e47e2 100644 (file)
 #define DRV_VERSION    "0.8"
 #define DRV_NAME       "dfl-pci"
 
+#define PCI_VSEC_ID_INTEL_DFLS 0x43
+
+#define PCI_VNDR_DFLS_CNT 0x8
+#define PCI_VNDR_DFLS_RES 0xc
+
+#define PCI_VNDR_DFLS_RES_BAR_MASK GENMASK(2, 0)
+#define PCI_VNDR_DFLS_RES_OFF_MASK GENMASK(31, 3)
+
 struct cci_drvdata {
        struct dfl_fpga_cdev *cdev;     /* container device */
 };
@@ -119,49 +127,94 @@ static int *cci_pci_create_irq_table(struct pci_dev *pcidev, unsigned int nvec)
        return table;
 }
 
-/* enumerate feature devices under pci device */
-static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
+static int find_dfls_by_vsec(struct pci_dev *pcidev, struct dfl_fpga_enum_info *info)
 {
-       struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
-       int port_num, bar, i, nvec, ret = 0;
-       struct dfl_fpga_enum_info *info;
-       struct dfl_fpga_cdev *cdev;
+       u32 bir, offset, vndr_hdr, dfl_cnt, dfl_res;
+       int dfl_res_off, i, bars, voff = 0;
        resource_size_t start, len;
-       void __iomem *base;
-       int *irq_table;
-       u32 offset;
-       u64 v;
 
-       /* allocate enumeration info via pci_dev */
-       info = dfl_fpga_enum_info_alloc(&pcidev->dev);
-       if (!info)
-               return -ENOMEM;
+       while ((voff = pci_find_next_ext_capability(pcidev, voff, PCI_EXT_CAP_ID_VNDR))) {
+               vndr_hdr = 0;
+               pci_read_config_dword(pcidev, voff + PCI_VNDR_HEADER, &vndr_hdr);
 
-       /* add irq info for enumeration if the device support irq */
-       nvec = cci_pci_alloc_irq(pcidev);
-       if (nvec < 0) {
-               dev_err(&pcidev->dev, "Fail to alloc irq %d.\n", nvec);
-               ret = nvec;
-               goto enum_info_free_exit;
-       } else if (nvec) {
-               irq_table = cci_pci_create_irq_table(pcidev, nvec);
-               if (!irq_table) {
-                       ret = -ENOMEM;
-                       goto irq_free_exit;
+               if (PCI_VNDR_HEADER_ID(vndr_hdr) == PCI_VSEC_ID_INTEL_DFLS &&
+                   pcidev->vendor == PCI_VENDOR_ID_INTEL)
+                       break;
+       }
+
+       if (!voff) {
+               dev_dbg(&pcidev->dev, "%s no DFL VSEC found\n", __func__);
+               return -ENODEV;
+       }
+
+       dfl_cnt = 0;
+       pci_read_config_dword(pcidev, voff + PCI_VNDR_DFLS_CNT, &dfl_cnt);
+       if (dfl_cnt > PCI_STD_NUM_BARS) {
+               dev_err(&pcidev->dev, "%s too many DFLs %d > %d\n",
+                       __func__, dfl_cnt, PCI_STD_NUM_BARS);
+               return -EINVAL;
+       }
+
+       dfl_res_off = voff + PCI_VNDR_DFLS_RES;
+       if (dfl_res_off + (dfl_cnt * sizeof(u32)) > PCI_CFG_SPACE_EXP_SIZE) {
+               dev_err(&pcidev->dev, "%s DFL VSEC too big for PCIe config space\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       for (i = 0, bars = 0; i < dfl_cnt; i++, dfl_res_off += sizeof(u32)) {
+               dfl_res = GENMASK(31, 0);
+               pci_read_config_dword(pcidev, dfl_res_off, &dfl_res);
+
+               bir = dfl_res & PCI_VNDR_DFLS_RES_BAR_MASK;
+               if (bir >= PCI_STD_NUM_BARS) {
+                       dev_err(&pcidev->dev, "%s bad bir number %d\n",
+                               __func__, bir);
+                       return -EINVAL;
                }
 
-               ret = dfl_fpga_enum_info_add_irq(info, nvec, irq_table);
-               kfree(irq_table);
-               if (ret)
-                       goto irq_free_exit;
+               if (bars & BIT(bir)) {
+                       dev_err(&pcidev->dev, "%s DFL for BAR %d already specified\n",
+                               __func__, bir);
+                       return -EINVAL;
+               }
+
+               bars |= BIT(bir);
+
+               len = pci_resource_len(pcidev, bir);
+               offset = dfl_res & PCI_VNDR_DFLS_RES_OFF_MASK;
+               if (offset >= len) {
+                       dev_err(&pcidev->dev, "%s bad offset %u >= %pa\n",
+                               __func__, offset, &len);
+                       return -EINVAL;
+               }
+
+               dev_dbg(&pcidev->dev, "%s BAR %d offset 0x%x\n", __func__, bir, offset);
+
+               len -= offset;
+
+               start = pci_resource_start(pcidev, bir) + offset;
+
+               dfl_fpga_enum_info_add_dfl(info, start, len);
        }
 
-       /* start to find Device Feature List in Bar 0 */
+       return 0;
+}
+
+/* default method of finding dfls starting at offset 0 of bar 0 */
+static int find_dfls_by_default(struct pci_dev *pcidev,
+                               struct dfl_fpga_enum_info *info)
+{
+       int port_num, bar, i, ret = 0;
+       resource_size_t start, len;
+       void __iomem *base;
+       u32 offset;
+       u64 v;
+
+       /* start to find Device Feature List from Bar 0 */
        base = cci_pci_ioremap_bar0(pcidev);
-       if (!base) {
-               ret = -ENOMEM;
-               goto irq_free_exit;
-       }
+       if (!base)
+               return -ENOMEM;
 
        /*
         * PF device has FME and Ports/AFUs, and VF device only has one
@@ -208,12 +261,54 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
                dfl_fpga_enum_info_add_dfl(info, start, len);
        } else {
                ret = -ENODEV;
-               goto irq_free_exit;
        }
 
        /* release I/O mappings for next step enumeration */
        pcim_iounmap_regions(pcidev, BIT(0));
 
+       return ret;
+}
+
+/* enumerate feature devices under pci device */
+static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
+{
+       struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
+       struct dfl_fpga_enum_info *info;
+       struct dfl_fpga_cdev *cdev;
+       int nvec, ret = 0;
+       int *irq_table;
+
+       /* allocate enumeration info via pci_dev */
+       info = dfl_fpga_enum_info_alloc(&pcidev->dev);
+       if (!info)
+               return -ENOMEM;
+
+       /* add irq info for enumeration if the device support irq */
+       nvec = cci_pci_alloc_irq(pcidev);
+       if (nvec < 0) {
+               dev_err(&pcidev->dev, "Fail to alloc irq %d.\n", nvec);
+               ret = nvec;
+               goto enum_info_free_exit;
+       } else if (nvec) {
+               irq_table = cci_pci_create_irq_table(pcidev, nvec);
+               if (!irq_table) {
+                       ret = -ENOMEM;
+                       goto irq_free_exit;
+               }
+
+               ret = dfl_fpga_enum_info_add_irq(info, nvec, irq_table);
+               kfree(irq_table);
+               if (ret)
+                       goto irq_free_exit;
+       }
+
+       ret = find_dfls_by_vsec(pcidev, info);
+       if (ret == -ENODEV)
+               ret = find_dfls_by_default(pcidev, info);
+
+       if (ret)
+               goto irq_free_exit;
+
        /* start enumeration with prepared enumeration information */
        cdev = dfl_fpga_feature_devs_enumerate(info);
        if (IS_ERR(cdev)) {
index b450870..511b20f 100644 (file)
@@ -10,6 +10,7 @@
  *   Wu Hao <hao.wu@intel.com>
  *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
  */
+#include <linux/dfl.h>
 #include <linux/fpga-dfl.h>
 #include <linux/module.h>
 #include <linux/uaccess.h>
@@ -298,8 +299,7 @@ static int dfl_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
        struct dfl_device *ddev = to_dfl_dev(dev);
 
-       /* The type has 4 valid bits and feature_id has 12 valid bits */
-       return add_uevent_var(env, "MODALIAS=dfl:t%01Xf%03X",
+       return add_uevent_var(env, "MODALIAS=dfl:t%04Xf%04X",
                              ddev->type, ddev->feature_id);
 }
 
index 5dc758f..2b82c96 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/iopoll.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/mod_devicetable.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/uuid.h>
@@ -516,88 +517,4 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev,
                               struct dfl_feature *feature,
                               unsigned long arg);
 
-/**
- * enum dfl_id_type - define the DFL FIU types
- */
-enum dfl_id_type {
-       FME_ID,
-       PORT_ID,
-       DFL_ID_MAX,
-};
-
-/**
- * struct dfl_device_id -  dfl device identifier
- * @type: contains 4 bits DFL FIU type of the device. See enum dfl_id_type.
- * @feature_id: contains 12 bits feature identifier local to its DFL FIU type.
- * @driver_data: driver specific data.
- */
-struct dfl_device_id {
-       u8 type;
-       u16 feature_id;
-       unsigned long driver_data;
-};
-
-/**
- * struct dfl_device - represent an dfl device on dfl bus
- *
- * @dev: generic device interface.
- * @id: id of the dfl device.
- * @type: type of DFL FIU of the device. See enum dfl_id_type.
- * @feature_id: 16 bits feature identifier local to its DFL FIU type.
- * @mmio_res: mmio resource of this dfl device.
- * @irqs: list of Linux IRQ numbers of this dfl device.
- * @num_irqs: number of IRQs supported by this dfl device.
- * @cdev: pointer to DFL FPGA container device this dfl device belongs to.
- * @id_entry: matched id entry in dfl driver's id table.
- */
-struct dfl_device {
-       struct device dev;
-       int id;
-       u8 type;
-       u16 feature_id;
-       struct resource mmio_res;
-       int *irqs;
-       unsigned int num_irqs;
-       struct dfl_fpga_cdev *cdev;
-       const struct dfl_device_id *id_entry;
-};
-
-/**
- * struct dfl_driver - represent an dfl device driver
- *
- * @drv: driver model structure.
- * @id_table: pointer to table of device IDs the driver is interested in.
- *           { } member terminated.
- * @probe: mandatory callback for device binding.
- * @remove: callback for device unbinding.
- */
-struct dfl_driver {
-       struct device_driver drv;
-       const struct dfl_device_id *id_table;
-
-       int (*probe)(struct dfl_device *dfl_dev);
-       void (*remove)(struct dfl_device *dfl_dev);
-};
-
-#define to_dfl_dev(d) container_of(d, struct dfl_device, dev)
-#define to_dfl_drv(d) container_of(d, struct dfl_driver, drv)
-
-/*
- * use a macro to avoid include chaining to get THIS_MODULE.
- */
-#define dfl_driver_register(drv) \
-       __dfl_driver_register(drv, THIS_MODULE)
-int __dfl_driver_register(struct dfl_driver *dfl_drv, struct module *owner);
-void dfl_driver_unregister(struct dfl_driver *dfl_drv);
-
-/*
- * module_dfl_driver() - Helper macro for drivers that don't do
- * anything special in module init/exit.  This eliminates a lot of
- * boilerplate.  Each module may only use this macro once, and
- * calling it replaces module_init() and module_exit().
- */
-#define module_dfl_driver(__dfl_driver) \
-       module_driver(__dfl_driver, dfl_driver_register, \
-                     dfl_driver_unregister)
-
 #endif /* __FPGA_DFL_H */
index 1bc9f12..616a3bd 100644 (file)
@@ -40,7 +40,7 @@ DECLARE_EVENT_CLASS(gb_message,
                __entry->result = message->header->result;
        ),
 
-       TP_printk("size=%hu operation_id=0x%04x type=0x%02x result=0x%02x",
+       TP_printk("size=%u operation_id=0x%04x type=0x%02x result=0x%02x",
                  __entry->size, __entry->operation_id,
                  __entry->type, __entry->result)
 );
@@ -317,7 +317,7 @@ DECLARE_EVENT_CLASS(gb_interface,
                __entry->mode_switch = intf->mode_switch;
        ),
 
-       TP_printk("intf_id=%hhu device_id=%hhu module_id=%hhu D=%d J=%d A=%d E=%d M=%d",
+       TP_printk("intf_id=%u device_id=%u module_id=%u D=%d J=%d A=%d E=%d M=%d",
                __entry->id, __entry->device_id, __entry->module_id,
                __entry->disconnected, __entry->ejected, __entry->active,
                __entry->enabled, __entry->mode_switch)
@@ -391,7 +391,7 @@ DECLARE_EVENT_CLASS(gb_module,
                __entry->disconnected = module->disconnected;
        ),
 
-       TP_printk("hd_bus_id=%d module_id=%hhu num_interfaces=%zu disconnected=%d",
+       TP_printk("hd_bus_id=%d module_id=%u num_interfaces=%zu disconnected=%d",
                __entry->hd_bus_id, __entry->module_id,
                __entry->num_interfaces, __entry->disconnected)
 );
index 3ea6913..3c9a988 100644 (file)
@@ -137,6 +137,15 @@ config TI_EMIF_SRAM
          sequence so this driver provides several relocatable PM functions
          for the SoC PM code to use.
 
+config FPGA_DFL_EMIF
+       tristate "FPGA DFL EMIF Driver"
+       depends on FPGA_DFL && HAS_IOMEM
+       help
+         This driver is for the EMIF private feature implemented under
+         FPGA Device Feature List (DFL) framework. It is used to expose
+         memory interface status information as well as memory clearing
+         control.
+
 config MVEBU_DEVBUS
        bool "Marvell EBU Device Bus Controller"
        default y if PLAT_ORION
index e71cf7b..bc7663e 100644 (file)
@@ -28,6 +28,8 @@ obj-$(CONFIG_STM32_FMC2_EBI)  += stm32-fmc2-ebi.o
 obj-$(CONFIG_SAMSUNG_MC)       += samsung/
 obj-$(CONFIG_TEGRA_MC)         += tegra/
 obj-$(CONFIG_TI_EMIF_SRAM)     += ti-emif-sram.o
+obj-$(CONFIG_FPGA_DFL_EMIF)    += dfl-emif.o
+
 ti-emif-sram-objs              := ti-emif-pm.o ti-emif-sram-pm.o
 
 AFLAGS_ti-emif-sram-pm.o       :=-Wa,-march=armv7-a
diff --git a/drivers/memory/dfl-emif.c b/drivers/memory/dfl-emif.c
new file mode 100644 (file)
index 0000000..3f71981
--- /dev/null
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DFL device driver for EMIF private feature
+ *
+ * Copyright (C) 2020 Intel Corporation, Inc.
+ *
+ */
+#include <linux/bitfield.h>
+#include <linux/dfl.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#define FME_FEATURE_ID_EMIF            0x9
+
+#define EMIF_STAT                      0x8
+#define EMIF_STAT_INIT_DONE_SFT                0
+#define EMIF_STAT_CALC_FAIL_SFT                8
+#define EMIF_STAT_CLEAR_BUSY_SFT       16
+#define EMIF_CTRL                      0x10
+#define EMIF_CTRL_CLEAR_EN_SFT         0
+#define EMIF_CTRL_CLEAR_EN_MSK         GENMASK_ULL(3, 0)
+
+#define EMIF_POLL_INVL                 10000 /* us */
+#define EMIF_POLL_TIMEOUT              5000000 /* us */
+
+struct dfl_emif {
+       struct device *dev;
+       void __iomem *base;
+       spinlock_t lock;        /* Serialises access to EMIF_CTRL reg */
+};
+
+struct emif_attr {
+       struct device_attribute attr;
+       u32 shift;
+       u32 index;
+};
+
+#define to_emif_attr(dev_attr) \
+       container_of(dev_attr, struct emif_attr, attr)
+
+static ssize_t emif_state_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct emif_attr *eattr = to_emif_attr(attr);
+       struct dfl_emif *de = dev_get_drvdata(dev);
+       u64 val;
+
+       val = readq(de->base + EMIF_STAT);
+
+       return sysfs_emit(buf, "%u\n",
+                         !!(val & BIT_ULL(eattr->shift + eattr->index)));
+}
+
+static ssize_t emif_clear_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct emif_attr *eattr = to_emif_attr(attr);
+       struct dfl_emif *de = dev_get_drvdata(dev);
+       u64 clear_busy_msk, clear_en_msk, val;
+       void __iomem *base = de->base;
+
+       if (!sysfs_streq(buf, "1"))
+               return -EINVAL;
+
+       clear_busy_msk = BIT_ULL(EMIF_STAT_CLEAR_BUSY_SFT + eattr->index);
+       clear_en_msk = BIT_ULL(EMIF_CTRL_CLEAR_EN_SFT + eattr->index);
+
+       spin_lock(&de->lock);
+       /* The CLEAR_EN field is WO, but other fields are RW */
+       val = readq(base + EMIF_CTRL);
+       val &= ~EMIF_CTRL_CLEAR_EN_MSK;
+       val |= clear_en_msk;
+       writeq(val, base + EMIF_CTRL);
+       spin_unlock(&de->lock);
+
+       if (readq_poll_timeout(base + EMIF_STAT, val,
+                              !(val & clear_busy_msk),
+                              EMIF_POLL_INVL, EMIF_POLL_TIMEOUT)) {
+               dev_err(de->dev, "timeout, fail to clear\n");
+               return -ETIMEDOUT;
+       }
+
+       return count;
+}
+
+#define emif_state_attr(_name, _shift, _index)                         \
+       static struct emif_attr emif_attr_##inf##_index##_##_name =     \
+               { .attr = __ATTR(inf##_index##_##_name, 0444,           \
+                                emif_state_show, NULL),                \
+                 .shift = (_shift), .index = (_index) }
+
+#define emif_clear_attr(_index)                                                \
+       static struct emif_attr emif_attr_##inf##_index##_clear =       \
+               { .attr = __ATTR(inf##_index##_clear, 0200,             \
+                                NULL, emif_clear_store),               \
+                 .index = (_index) }
+
+emif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 0);
+emif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 1);
+emif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 2);
+emif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 3);
+
+emif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 0);
+emif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 1);
+emif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 2);
+emif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 3);
+
+emif_clear_attr(0);
+emif_clear_attr(1);
+emif_clear_attr(2);
+emif_clear_attr(3);
+
+static struct attribute *dfl_emif_attrs[] = {
+       &emif_attr_inf0_init_done.attr.attr,
+       &emif_attr_inf0_cal_fail.attr.attr,
+       &emif_attr_inf0_clear.attr.attr,
+
+       &emif_attr_inf1_init_done.attr.attr,
+       &emif_attr_inf1_cal_fail.attr.attr,
+       &emif_attr_inf1_clear.attr.attr,
+
+       &emif_attr_inf2_init_done.attr.attr,
+       &emif_attr_inf2_cal_fail.attr.attr,
+       &emif_attr_inf2_clear.attr.attr,
+
+       &emif_attr_inf3_init_done.attr.attr,
+       &emif_attr_inf3_cal_fail.attr.attr,
+       &emif_attr_inf3_clear.attr.attr,
+
+       NULL,
+};
+
+static umode_t dfl_emif_visible(struct kobject *kobj,
+                               struct attribute *attr, int n)
+{
+       struct dfl_emif *de = dev_get_drvdata(kobj_to_dev(kobj));
+       struct emif_attr *eattr = container_of(attr, struct emif_attr,
+                                              attr.attr);
+       u64 val;
+
+       /*
+        * This device supports upto 4 memory interfaces, but not all
+        * interfaces are used on different platforms. The read out value of
+        * CLEAN_EN field (which is a bitmap) could tell how many interfaces
+        * are available.
+        */
+       val = FIELD_GET(EMIF_CTRL_CLEAR_EN_MSK, readq(de->base + EMIF_CTRL));
+
+       return (val & BIT_ULL(eattr->index)) ? attr->mode : 0;
+}
+
+static const struct attribute_group dfl_emif_group = {
+       .is_visible = dfl_emif_visible,
+       .attrs = dfl_emif_attrs,
+};
+
+static const struct attribute_group *dfl_emif_groups[] = {
+       &dfl_emif_group,
+       NULL,
+};
+
+static int dfl_emif_probe(struct dfl_device *ddev)
+{
+       struct device *dev = &ddev->dev;
+       struct dfl_emif *de;
+
+       de = devm_kzalloc(dev, sizeof(*de), GFP_KERNEL);
+       if (!de)
+               return -ENOMEM;
+
+       de->base = devm_ioremap_resource(dev, &ddev->mmio_res);
+       if (IS_ERR(de->base))
+               return PTR_ERR(de->base);
+
+       de->dev = dev;
+       spin_lock_init(&de->lock);
+       dev_set_drvdata(dev, de);
+
+       return 0;
+}
+
+static const struct dfl_device_id dfl_emif_ids[] = {
+       { FME_ID, FME_FEATURE_ID_EMIF },
+       { }
+};
+MODULE_DEVICE_TABLE(dfl, dfl_emif_ids);
+
+static struct dfl_driver dfl_emif_driver = {
+       .drv    = {
+               .name       = "dfl-emif",
+               .dev_groups = dfl_emif_groups,
+       },
+       .id_table = dfl_emif_ids,
+       .probe   = dfl_emif_probe,
+};
+module_dfl_driver(dfl_emif_driver);
+
+MODULE_DESCRIPTION("DFL EMIF driver");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
index fafa8b0..e90c252 100644 (file)
@@ -50,14 +50,6 @@ config AD525X_DPOT_SPI
          To compile this driver as a module, choose M here: the
          module will be called ad525x_dpot-spi.
 
-config ATMEL_TCLIB
-       bool "Atmel AT32/AT91 Timer/Counter Library"
-       depends on ARCH_AT91
-       help
-         Select this if you want a library to allocate the Timer/Counter
-         blocks found on many Atmel processors.  This facilitates using
-         these blocks by different drivers despite processor differences.
-
 config DUMMY_IRQ
        tristate "Dummy IRQ handler"
        help
index d23231e..f65e8b1 100644 (file)
@@ -10,7 +10,6 @@ obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
 obj-$(CONFIG_AD525X_DPOT_SPI)  += ad525x_dpot-spi.o
 obj-$(CONFIG_INTEL_MID_PTI)    += pti.o
 obj-$(CONFIG_ATMEL_SSC)                += atmel-ssc.o
-obj-$(CONFIG_ATMEL_TCLIB)      += atmel_tclib.o
 obj-$(CONFIG_DUMMY_IRQ)                += dummy-irq.o
 obj-$(CONFIG_ICS932S401)       += ics932s401.o
 obj-$(CONFIG_LKDTM)            += lkdtm/
diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c
deleted file mode 100644 (file)
index 7de7840..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <linux/of.h>
-#include <soc/at91/atmel_tcb.h>
-
-/*
- * This is a thin library to solve the problem of how to portably allocate
- * one of the TC blocks.  For simplicity, it doesn't currently expect to
- * share individual timers between different drivers.
- */
-
-#if defined(CONFIG_AVR32)
-/* AVR32 has these divide PBB */
-const u8 atmel_tc_divisors[5] = { 0, 4, 8, 16, 32, };
-EXPORT_SYMBOL(atmel_tc_divisors);
-
-#elif defined(CONFIG_ARCH_AT91)
-/* AT91 has these divide MCK */
-const u8 atmel_tc_divisors[5] = { 2, 8, 32, 128, 0, };
-EXPORT_SYMBOL(atmel_tc_divisors);
-
-#endif
-
-static DEFINE_SPINLOCK(tc_list_lock);
-static LIST_HEAD(tc_list);
-
-/**
- * atmel_tc_alloc - allocate a specified TC block
- * @block: which block to allocate
- *
- * Caller allocates a block.  If it is available, a pointer to a
- * pre-initialized struct atmel_tc is returned. The caller can access
- * the registers directly through the "regs" field.
- */
-struct atmel_tc *atmel_tc_alloc(unsigned block)
-{
-       struct atmel_tc         *tc;
-       struct platform_device  *pdev = NULL;
-
-       spin_lock(&tc_list_lock);
-       list_for_each_entry(tc, &tc_list, node) {
-               if (tc->allocated)
-                       continue;
-
-               if ((tc->pdev->dev.of_node && tc->id == block) ||
-                   (tc->pdev->id == block)) {
-                       pdev = tc->pdev;
-                       tc->allocated = true;
-                       break;
-               }
-       }
-       spin_unlock(&tc_list_lock);
-
-       return pdev ? tc : NULL;
-}
-EXPORT_SYMBOL_GPL(atmel_tc_alloc);
-
-/**
- * atmel_tc_free - release a specified TC block
- * @tc: Timer/counter block that was returned by atmel_tc_alloc()
- *
- * This reverses the effect of atmel_tc_alloc(), invalidating the resource
- * returned by that routine and making the TC available to other drivers.
- */
-void atmel_tc_free(struct atmel_tc *tc)
-{
-       spin_lock(&tc_list_lock);
-       if (tc->allocated)
-               tc->allocated = false;
-       spin_unlock(&tc_list_lock);
-}
-EXPORT_SYMBOL_GPL(atmel_tc_free);
-
-#if defined(CONFIG_OF)
-static struct atmel_tcb_config tcb_rm9200_config = {
-       .counter_width = 16,
-};
-
-static struct atmel_tcb_config tcb_sam9x5_config = {
-       .counter_width = 32,
-};
-
-static const struct of_device_id atmel_tcb_dt_ids[] = {
-       {
-               .compatible = "atmel,at91rm9200-tcb",
-               .data = &tcb_rm9200_config,
-       }, {
-               .compatible = "atmel,at91sam9x5-tcb",
-               .data = &tcb_sam9x5_config,
-       }, {
-               /* sentinel */
-       }
-};
-
-MODULE_DEVICE_TABLE(of, atmel_tcb_dt_ids);
-#endif
-
-static int __init tc_probe(struct platform_device *pdev)
-{
-       struct atmel_tc *tc;
-       struct clk      *clk;
-       int             irq;
-       unsigned int    i;
-
-       if (of_get_child_count(pdev->dev.of_node))
-               return -EBUSY;
-
-       irq = platform_get_irq(pdev, 0);
-       if (irq < 0)
-               return -EINVAL;
-
-       tc = devm_kzalloc(&pdev->dev, sizeof(struct atmel_tc), GFP_KERNEL);
-       if (!tc)
-               return -ENOMEM;
-
-       tc->pdev = pdev;
-
-       clk = devm_clk_get(&pdev->dev, "t0_clk");
-       if (IS_ERR(clk))
-               return PTR_ERR(clk);
-
-       tc->slow_clk = devm_clk_get(&pdev->dev, "slow_clk");
-       if (IS_ERR(tc->slow_clk))
-               return PTR_ERR(tc->slow_clk);
-
-       tc->regs = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(tc->regs))
-               return PTR_ERR(tc->regs);
-
-       /* Now take SoC information if available */
-       if (pdev->dev.of_node) {
-               const struct of_device_id *match;
-               match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node);
-               if (match)
-                       tc->tcb_config = match->data;
-
-               tc->id = of_alias_get_id(tc->pdev->dev.of_node, "tcb");
-       } else {
-               tc->id = pdev->id;
-       }
-
-       tc->clk[0] = clk;
-       tc->clk[1] = devm_clk_get(&pdev->dev, "t1_clk");
-       if (IS_ERR(tc->clk[1]))
-               tc->clk[1] = clk;
-       tc->clk[2] = devm_clk_get(&pdev->dev, "t2_clk");
-       if (IS_ERR(tc->clk[2]))
-               tc->clk[2] = clk;
-
-       tc->irq[0] = irq;
-       tc->irq[1] = platform_get_irq(pdev, 1);
-       if (tc->irq[1] < 0)
-               tc->irq[1] = irq;
-       tc->irq[2] = platform_get_irq(pdev, 2);
-       if (tc->irq[2] < 0)
-               tc->irq[2] = irq;
-
-       for (i = 0; i < 3; i++)
-               writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR));
-
-       spin_lock(&tc_list_lock);
-       list_add_tail(&tc->node, &tc_list);
-       spin_unlock(&tc_list_lock);
-
-       platform_set_drvdata(pdev, tc);
-
-       return 0;
-}
-
-static void tc_shutdown(struct platform_device *pdev)
-{
-       int i;
-       struct atmel_tc *tc = platform_get_drvdata(pdev);
-
-       for (i = 0; i < 3; i++)
-               writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR));
-}
-
-static struct platform_driver tc_driver = {
-       .driver = {
-               .name   = "atmel_tcb",
-               .of_match_table = of_match_ptr(atmel_tcb_dt_ids),
-       },
-       .shutdown = tc_shutdown,
-};
-
-static int __init tc_init(void)
-{
-       return platform_driver_probe(&tc_driver, tc_probe);
-}
-arch_initcall(tc_init);
index 2aa6648..d782754 100644 (file)
@@ -59,12 +59,6 @@ static const struct pci_device_id rtsx_pci_ids[] = {
 
 MODULE_DEVICE_TABLE(pci, rtsx_pci_ids);
 
-static inline void rtsx_pci_disable_aspm(struct rtsx_pcr *pcr)
-{
-       pcie_capability_clear_and_set_word(pcr->pci, PCI_EXP_LNKCTL,
-                                          PCI_EXP_LNKCTL_ASPMC, 0);
-}
-
 static int rtsx_comm_set_ltr_latency(struct rtsx_pcr *pcr, u32 latency)
 {
        rtsx_pci_write_register(pcr, MSGTXDATA0,
index 7c45f82..849cfea 100644 (file)
@@ -511,4 +511,5 @@ module_spi_driver(eeprom_93xx46_driver);
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs");
 MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
-MODULE_ALIAS("spi:93xx46");
+MODULE_ALIAS("spi:eeprom-93xx46");
+MODULE_ALIAS("spi:93lc46b");
index 4d1b44d..e70525e 100644 (file)
@@ -15,7 +15,7 @@
 
 static dev_t ocxl_dev;
 static struct class *ocxl_class;
-static struct mutex minors_idr_lock;
+static DEFINE_MUTEX(minors_idr_lock);
 static struct idr minors_idr;
 
 static struct ocxl_file_info *find_and_get_file_info(dev_t devno)
@@ -588,7 +588,6 @@ int ocxl_file_init(void)
 {
        int rc;
 
-       mutex_init(&minors_idr_lock);
        idr_init(&minors_idr);
 
        rc = alloc_chrdev_region(&ocxl_dev, 0, OCXL_NUM_MINORS, "ocxl");
diff --git a/include/linux/dfl.h b/include/linux/dfl.h
new file mode 100644 (file)
index 0000000..6cc1098
--- /dev/null
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for DFL driver and device API
+ *
+ * Copyright (C) 2020 Intel Corporation, Inc.
+ */
+
+#ifndef __LINUX_DFL_H
+#define __LINUX_DFL_H
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+/**
+ * enum dfl_id_type - define the DFL FIU types
+ */
+enum dfl_id_type {
+       FME_ID = 0,
+       PORT_ID = 1,
+       DFL_ID_MAX,
+};
+
+/**
+ * struct dfl_device - represent an dfl device on dfl bus
+ *
+ * @dev: generic device interface.
+ * @id: id of the dfl device.
+ * @type: type of DFL FIU of the device. See enum dfl_id_type.
+ * @feature_id: feature identifier local to its DFL FIU type.
+ * @mmio_res: mmio resource of this dfl device.
+ * @irqs: list of Linux IRQ numbers of this dfl device.
+ * @num_irqs: number of IRQs supported by this dfl device.
+ * @cdev: pointer to DFL FPGA container device this dfl device belongs to.
+ * @id_entry: matched id entry in dfl driver's id table.
+ */
+struct dfl_device {
+       struct device dev;
+       int id;
+       u16 type;
+       u16 feature_id;
+       struct resource mmio_res;
+       int *irqs;
+       unsigned int num_irqs;
+       struct dfl_fpga_cdev *cdev;
+       const struct dfl_device_id *id_entry;
+};
+
+/**
+ * struct dfl_driver - represent an dfl device driver
+ *
+ * @drv: driver model structure.
+ * @id_table: pointer to table of device IDs the driver is interested in.
+ *           { } member terminated.
+ * @probe: mandatory callback for device binding.
+ * @remove: callback for device unbinding.
+ */
+struct dfl_driver {
+       struct device_driver drv;
+       const struct dfl_device_id *id_table;
+
+       int (*probe)(struct dfl_device *dfl_dev);
+       void (*remove)(struct dfl_device *dfl_dev);
+};
+
+#define to_dfl_dev(d) container_of(d, struct dfl_device, dev)
+#define to_dfl_drv(d) container_of(d, struct dfl_driver, drv)
+
+/*
+ * use a macro to avoid include chaining to get THIS_MODULE.
+ */
+#define dfl_driver_register(drv) \
+       __dfl_driver_register(drv, THIS_MODULE)
+int __dfl_driver_register(struct dfl_driver *dfl_drv, struct module *owner);
+void dfl_driver_unregister(struct dfl_driver *dfl_drv);
+
+/*
+ * module_dfl_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit.  This eliminates a lot of
+ * boilerplate.  Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit().
+ */
+#define module_dfl_driver(__dfl_driver) \
+       module_driver(__dfl_driver, dfl_driver_register, \
+                     dfl_driver_unregister)
+
+#endif /* __LINUX_DFL_H */
index c425290..b8dae34 100644 (file)
@@ -846,4 +846,28 @@ struct auxiliary_device_id {
        kernel_ulong_t driver_data;
 };
 
+/*
+ * DFL (Device Feature List)
+ *
+ * DFL defines a linked list of feature headers within the device MMIO space to
+ * provide an extensible way of adding features. Software can walk through these
+ * predefined data structures to enumerate features. It is now used in the FPGA.
+ * See Documentation/fpga/dfl.rst for more information.
+ *
+ * The dfl bus type is introduced to match the individual feature devices (dfl
+ * devices) for specific dfl drivers.
+ */
+
+/**
+ * struct dfl_device_id -  dfl device identifier
+ * @type: DFL FIU type of the device. See enum dfl_id_type.
+ * @feature_id: feature identifier local to its DFL FIU type.
+ * @driver_data: driver specific data.
+ */
+struct dfl_device_id {
+       __u16 type;
+       __u16 feature_id;
+       kernel_ulong_t driver_data;
+};
+
 #endif /* LINUX_MOD_DEVICETABLE_H */
index e377f52..1b14f3c 100644 (file)
@@ -246,5 +246,9 @@ int main(void)
        DEVID(auxiliary_device_id);
        DEVID_FIELD(auxiliary_device_id, name);
 
+       DEVID(dfl_device_id);
+       DEVID_FIELD(dfl_device_id, type);
+       DEVID_FIELD(dfl_device_id, feature_id);
+
        return 0;
 }
index fb48270..7ebabeb 100644 (file)
@@ -1375,6 +1375,18 @@ static int do_auxiliary_entry(const char *filename, void *symval, char *alias)
        return 1;
 }
 
+/* Looks like: dfl:tNfN */
+static int do_dfl_entry(const char *filename, void *symval, char *alias)
+{
+       DEF_FIELD(symval, dfl_device_id, type);
+       DEF_FIELD(symval, dfl_device_id, feature_id);
+
+       sprintf(alias, "dfl:t%04Xf%04X", type, feature_id);
+
+       add_wildcard(alias);
+       return 1;
+}
+
 /* Does namelen bytes of name exactly match the symbol? */
 static bool sym_is(const char *name, unsigned namelen, const char *symbol)
 {
@@ -1450,6 +1462,7 @@ static const struct devtable devtable[] = {
        {"wmi", SIZE_wmi_device_id, do_wmi_entry},
        {"mhi", SIZE_mhi_device_id, do_mhi_entry},
        {"auxiliary", SIZE_auxiliary_device_id, do_auxiliary_entry},
+       {"dfl", SIZE_dfl_device_id, do_dfl_entry},
 };
 
 /* Create MODULE_ALIAS() statements.