Merge tag 'v5.13-rc6' into char-misc-next
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 14 Jun 2021 06:59:06 +0000 (08:59 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 14 Jun 2021 06:59:06 +0000 (08:59 +0200)
We need the fixes in here as well.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
139 files changed:
Documentation/ABI/stable/sysfs-driver-w1_ds2438 [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-spi-eeprom [new file with mode: 0644]
Documentation/devicetree/bindings/eeprom/at25.yaml
Documentation/devicetree/bindings/fpga/fpga-region.txt
Documentation/devicetree/bindings/misc/eeprom-93xx46.txt
Documentation/fpga/dfl.rst
Documentation/userspace-api/accelerators/ocxl.rst
Documentation/w1/slaves/w1_ds2438.rst
arch/sparc/include/asm/vio.h
arch/sparc/kernel/ds.c
arch/sparc/kernel/vio.c
drivers/block/sunvdc.c
drivers/char/Kconfig
drivers/char/Makefile
drivers/char/hpet.c
drivers/char/hw_random/pseries-rng.c
drivers/char/mem.c
drivers/char/pcmcia/cm4000_cs.c
drivers/char/pcmcia/cm4040_cs.c
drivers/char/pcmcia/scr24x_cs.c
drivers/char/raw.c [deleted file]
drivers/char/xillybus/Kconfig
drivers/char/xillybus/Makefile
drivers/char/xillybus/xillybus.h
drivers/char/xillybus/xillybus_class.c [new file with mode: 0644]
drivers/char/xillybus/xillybus_class.h [new file with mode: 0644]
drivers/char/xillybus/xillybus_core.c
drivers/char/xillybus/xillybus_of.c
drivers/char/xillybus/xillybus_pcie.c
drivers/char/xillybus/xillyusb.c [new file with mode: 0644]
drivers/comedi/drivers/comedi_8254.c
drivers/comedi/drivers/comedi_isadma.c
drivers/comedi/drivers/ni_routes.c
drivers/comedi/drivers/ni_routes.h
drivers/comedi/drivers/ni_routing/ni_device_routes.c
drivers/comedi/drivers/ni_routing/ni_device_routes.h
drivers/comedi/drivers/ni_routing/ni_device_routes/all.h
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6220.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6221.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6229.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6251.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6254.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6259.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6534.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6602.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6713.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6723.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6733.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6030e.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6224.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6225.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6251.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6733.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6251.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6535.c
drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6738.c
drivers/comedi/drivers/ni_routing/ni_route_values.c
drivers/comedi/drivers/ni_routing/ni_route_values.h
drivers/comedi/drivers/ni_routing/ni_route_values/all.h
drivers/comedi/drivers/ni_routing/ni_route_values/ni_660x.c
drivers/comedi/drivers/ni_routing/ni_route_values/ni_eseries.c
drivers/comedi/drivers/ni_routing/ni_route_values/ni_mseries.c
drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c
drivers/comedi/drivers/ni_routing/tools/convert_csv_to_c.py
drivers/comedi/drivers/ni_routing/tools/convert_py_to_csv.py
drivers/comedi/drivers/ni_routing/tools/csv_collection.py
drivers/comedi/drivers/ni_routing/tools/make_blank_csv.py
drivers/comedi/drivers/ni_routing/tools/ni_names.py
drivers/comedi/drivers/ni_tio.c
drivers/comedi/drivers/tests/comedi_example_test.c
drivers/comedi/drivers/tests/ni_routes_test.c
drivers/comedi/drivers/tests/unittest.h
drivers/eisa/eisa-bus.c
drivers/firmware/stratix10-svc.c
drivers/fpga/Kconfig
drivers/fpga/fpga-bridge.c
drivers/fpga/fpga-mgr.c
drivers/fpga/fpga-region.c
drivers/fpga/of-fpga-region.c
drivers/fpga/stratix10-soc.c
drivers/fsi/fsi-core.c
drivers/fsi/fsi-master-aspeed.c
drivers/fsi/fsi-master-ast-cf.c
drivers/fsi/fsi-master-gpio.c
drivers/fsi/fsi-occ.c
drivers/fsi/fsi-sbefifo.c
drivers/fsi/fsi-scom.c
drivers/hwmon/occ/common.c
drivers/ipack/carriers/tpci200.c
drivers/ipack/carriers/tpci200.h
drivers/ipack/devices/ipoctal.c
drivers/ipack/devices/ipoctal.h
drivers/misc/bcm-vk/bcm_vk_msg.c
drivers/misc/bcm-vk/bcm_vk_msg.h
drivers/misc/cardreader/alcor_pci.c
drivers/misc/cxl/file.c
drivers/misc/eeprom/Kconfig
drivers/misc/eeprom/at25.c
drivers/misc/eeprom/ee1004.c
drivers/misc/eeprom/eeprom_93xx46.c
drivers/misc/eeprom/idt_89hpesx.c
drivers/misc/habanalabs/gaudi/gaudi.c
drivers/misc/hpilo.c
drivers/misc/hpilo.h
drivers/misc/ibmasm/module.c
drivers/misc/mei/main.c
drivers/misc/pvpanic/pvpanic-mmio.c
drivers/misc/pvpanic/pvpanic-pci.c
drivers/misc/pvpanic/pvpanic.c
drivers/misc/pvpanic/pvpanic.h
drivers/misc/uacce/uacce.c
drivers/misc/xilinx_sdfec.c
drivers/net/ethernet/sun/ldmvsw.c
drivers/net/ethernet/sun/sunvnet.c
drivers/nvmem/core.c
drivers/nvmem/qfprom.c
drivers/nvmem/sprd-efuse.c
drivers/nvmem/sunxi_sid.c
drivers/parport/probe.c
drivers/pnp/isapnp/proc.c
drivers/tty/vcc.c
drivers/uio/Kconfig
drivers/uio/uio_aec.c
drivers/uio/uio_pci_generic.c
drivers/video/fbdev/Kconfig
drivers/visorbus/visorchipset.c
drivers/w1/masters/ds2482.c
drivers/w1/slaves/w1_ds2438.c
drivers/w1/slaves/w1_therm.c
fs/block_dev.c
include/linux/eeprom_93xx46.h
include/linux/fpga/fpga-bridge.h
include/linux/fpga/fpga-mgr.h
include/linux/fs.h
include/linux/nvmem-provider.h
include/linux/sysfs.h
include/uapi/linux/raw.h [deleted file]
lib/dynamic_debug.c

diff --git a/Documentation/ABI/stable/sysfs-driver-w1_ds2438 b/Documentation/ABI/stable/sysfs-driver-w1_ds2438
new file mode 100644 (file)
index 0000000..d2e7681
--- /dev/null
@@ -0,0 +1,13 @@
+What:          /sys/bus/w1/devices/.../page1
+Date:          April 2021
+Contact:       Luiz Sampaio <sampaio.ime@gmail.com>
+Description:   read the contents of the page1 of the DS2438
+               see Documentation/w1/slaves/w1_ds2438.rst for detailed information
+Users:         any user space application which wants to communicate with DS2438
+
+What:          /sys/bus/w1/devices/.../offset
+Date:          April 2021
+Contact:       Luiz Sampaio <sampaio.ime@gmail.com>
+Description:   write the contents to the offset register of the DS2438
+               see Documentation/w1/slaves/w1_ds2438.rst for detailed information
+Users:         any user space application which wants to communicate with DS2438
diff --git a/Documentation/ABI/testing/sysfs-class-spi-eeprom b/Documentation/ABI/testing/sysfs-class-spi-eeprom
new file mode 100644 (file)
index 0000000..1ff7579
--- /dev/null
@@ -0,0 +1,19 @@
+What:          /sys/class/spi_master/spi<bus>/spi<bus>.<dev>/fram
+Date:          June 2021
+KernelVersion: 5.14
+Contact:       Jiri Prchal <jiri.prchal@aksignal.cz>
+Description:
+       Contains the FRAM binary data. Same as EEPROM, just another file
+       name to indicate that it employs ferroelectric process.
+       It performs write operations at bus speed - no write delays.
+
+What:          /sys/class/spi_master/spi<bus>/spi<bus>.<dev>/sernum
+Date:          May 2021
+KernelVersion: 5.14
+Contact:       Jiri Prchal <jiri.prchal@aksignal.cz>
+Description:
+       Contains the serial number of the Cypress FRAM (FM25VN) if it is
+       present.  It will be displayed as a 8 byte hex string, as read
+       from the device.
+
+       This is a read-only attribute.
index 6a2dc8b..fbf99e3 100644 (file)
@@ -4,14 +4,16 @@
 $id: "http://devicetree.org/schemas/eeprom/at25.yaml#"
 $schema: "http://devicetree.org/meta-schemas/core.yaml#"
 
-title: SPI EEPROMs compatible with Atmel's AT25
+title: SPI EEPROMs or FRAMs compatible with Atmel's AT25
 
 maintainers:
   - Christian Eggers <ceggers@arri.de>
 
 properties:
   $nodename:
-    pattern: "^eeprom@[0-9a-f]{1,2}$"
+    anyOf:
+      - pattern: "^eeprom@[0-9a-f]{1,2}$"
+      - pattern: "^fram@[0-9a-f]{1,2}$"
 
   # There are multiple known vendors who manufacture EEPROM chips compatible
   # with Atmel's AT25. The compatible string requires two items where the
@@ -31,6 +33,7 @@ properties:
               - microchip,25lc040
               - st,m95m02
               - st,m95256
+              - cypress,fm25
 
           - const: atmel,at25
 
@@ -47,7 +50,7 @@ properties:
     $ref: /schemas/types.yaml#/definitions/uint32
     enum: [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072]
     description:
-      Size of the eeprom page.
+      Size of the eeprom page. FRAMs don't have pages.
 
   size:
     $ref: /schemas/types.yaml#/definitions/uint32
@@ -100,9 +103,19 @@ required:
   - compatible
   - reg
   - spi-max-frequency
-  - pagesize
-  - size
-  - address-width
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          not:
+            contains:
+              const: cypress,fm25
+    then:
+      required:
+        - pagesize
+        - size
+        - address-width
 
 additionalProperties: false
 
@@ -125,4 +138,10 @@ examples:
             size = <32768>;
             address-width = <16>;
         };
+
+        fram@1 {
+            compatible = "cypress,fm25", "atmel,at25";
+            reg = <1>;
+            spi-max-frequency = <40000000>;
+        };
     };
index d787d57..7d35152 100644 (file)
@@ -38,7 +38,7 @@ Partial Reconfiguration (PR)
 
 Partial Reconfiguration Region (PRR)
  * Also called a "reconfigurable partition"
- * A PRR is a specific section of a FPGA reserved for reconfiguration.
+ * A PRR is a specific section of an FPGA reserved for reconfiguration.
  * A base (or static) FPGA image may create a set of PRR's that later may
    be independently reprogrammed many times.
  * The size and specific location of each PRR is fixed.
@@ -105,7 +105,7 @@ reprogrammed independently while the rest of the system continues to function.
 Sequence
 ========
 
-When a DT overlay that targets a FPGA Region is applied, the FPGA Region will
+When a DT overlay that targets an FPGA Region is applied, the FPGA Region will
 do the following:
 
  1. Disable appropriate FPGA bridges.
@@ -134,8 +134,8 @@ The intended use is that a Device Tree overlay (DTO) can be used to reprogram an
 FPGA while an operating system is running.
 
 An FPGA Region that exists in the live Device Tree reflects the current state.
-If the live tree shows a "firmware-name" property or child nodes under a FPGA
-Region, the FPGA already has been programmed.  A DTO that targets a FPGA Region
+If the live tree shows a "firmware-name" property or child nodes under an FPGA
+Region, the FPGA already has been programmed.  A DTO that targets an FPGA Region
 and adds the "firmware-name" property is taken as a request to reprogram the
 FPGA.  After reprogramming is successful, the overlay is accepted into the live
 tree.
@@ -152,9 +152,9 @@ These FPGA regions are children of FPGA bridges which are then children of the
 base FPGA region.  The "Full Reconfiguration to add PRR's" example below shows
 this.
 
-If an FPGA Region does not specify a FPGA Manager, it will inherit the FPGA
+If an FPGA Region does not specify an FPGA Manager, it will inherit the FPGA
 Manager specified by its ancestor FPGA Region.  This supports both the case
-where the same FPGA Manager is used for all of a FPGA as well the case where
+where the same FPGA Manager is used for all of an FPGA as well the case where
 a different FPGA Manager is used for each region.
 
 FPGA Regions do not inherit their ancestor FPGA regions' bridges.  This prevents
@@ -166,7 +166,7 @@ within the static image of the FPGA.
 Required properties:
 - compatible : should contain "fpga-region"
 - fpga-mgr : should contain a phandle to an FPGA Manager.  Child FPGA Regions
-       inherit this property from their ancestor regions.  A fpga-mgr property
+       inherit this property from their ancestor regions.  An fpga-mgr property
        in a region will override any inherited FPGA manager.
 - #address-cells, #size-cells, ranges : must be present to handle address space
        mapping for child nodes.
@@ -175,12 +175,12 @@ Optional properties:
 - firmware-name : should contain the name of an FPGA image file located on the
        firmware search path.  If this property shows up in a live device tree
        it indicates that the FPGA has already been programmed with this image.
-       If this property is in an overlay targeting a FPGA region, it is a
+       If this property is in an overlay targeting an FPGA region, it is a
        request to program the FPGA with that image.
 - fpga-bridges : should contain a list of phandles to FPGA Bridges that must be
        controlled during FPGA programming along with the parent FPGA bridge.
        This property is optional if the FPGA Manager handles the bridges.
-        If the fpga-region is  the child of a fpga-bridge, the list should not
+        If the fpga-region is  the child of an fpga-bridge, the list should not
         contain the parent bridge.
 - partial-fpga-config : boolean, set if partial reconfiguration is to be done,
        otherwise full reconfiguration is done.
@@ -279,7 +279,7 @@ Supported Use Models
 
 In all cases the live DT must have the FPGA Manager, FPGA Bridges (if any), and
 a FPGA Region.  The target of the Device Tree Overlay is the FPGA Region.  Some
-uses are specific to a FPGA device.
+uses are specific to an FPGA device.
 
  * No FPGA Bridges
    In this case, the FPGA Manager which programs the FPGA also handles the
@@ -300,7 +300,7 @@ uses are specific to a FPGA device.
    bridges need to exist in the FPGA that can gate the buses going to each FPGA
    region while the buses are enabled for other sections.  Before any partial
    reconfiguration can be done, a base FPGA image must be loaded which includes
-   PRR's with FPGA bridges.  The device tree should have a FPGA region for each
+   PRR's with FPGA bridges.  The device tree should have an FPGA region for each
    PRR.
 
 Device Tree Examples
index 7b636b7..72ea0af 100644 (file)
@@ -2,7 +2,10 @@ EEPROMs (SPI) compatible with Microchip Technology 93xx46 family.
 
 Required properties:
 - compatible : shall be one of:
+    "atmel,at93c46"
     "atmel,at93c46d"
+    "atmel,at93c56"
+    "atmel,at93c66"
     "eeprom-93xx46"
     "microchip,93lc46b"
 - data-size : number of data bits per word (either 8 or 16)
index f3a1223..75df90d 100644 (file)
@@ -57,7 +57,7 @@ FPGA Interface Unit (FIU) represents a standalone functional unit for the
 interface to FPGA, e.g. the FPGA Management Engine (FME) and Port (more
 descriptions on FME and Port in later sections).
 
-Accelerated Function Unit (AFU) represents a FPGA programmable region and
+Accelerated Function Unit (AFU) represents an FPGA programmable region and
 always connects to a FIU (e.g. a Port) as its child as illustrated above.
 
 Private Features represent sub features of the FIU and AFU. They could be
@@ -311,7 +311,7 @@ The driver organization in virtualization case is illustrated below:
              | PCI PF Device |            |          | PCI VF Device |
              +---------------+            |          +---------------+
 
-FPGA PCIe device driver is always loaded first once a FPGA PCIe PF or VF device
+FPGA PCIe device driver is always loaded first once an FPGA PCIe PF or VF device
 is detected. It:
 
 * Finishes enumeration on both FPGA PCIe PF and VF device using common
index 14cefc0..db7570d 100644 (file)
@@ -6,7 +6,7 @@ OpenCAPI is an interface between processors and accelerators. It aims
 at being low-latency and high-bandwidth. The specification is
 developed by the `OpenCAPI Consortium <http://opencapi.org/>`_.
 
-It allows an accelerator (which could be a FPGA, ASICs, ...) to access
+It allows an accelerator (which could be an FPGA, ASICs, ...) to access
 the host memory coherently, using virtual addresses. An OpenCAPI
 device can also host its own memory, that can be accessed from the
 host.
index a29309a..4fa671f 100644 (file)
@@ -22,7 +22,7 @@ is also often used in weather stations and applications such as: rain gauge,
 wind speed/direction measuring, humidity sensing, etc.
 
 Current support is provided through the following sysfs files (all files
-except "iad" are readonly):
+except "iad" and "offset" are readonly):
 
 "iad"
 -----
@@ -44,6 +44,23 @@ Internally when this file is read, the additional CRC byte is also obtained
 from the slave device. If it is correct, the 8 bytes page data are passed
 to userspace, otherwise an I/O error is returned.
 
+"page1"
+-------
+This file provides full 8 bytes of the chip Page 1 (01h).
+This page contains the ICA, elapsed time meter and current offset data of the DS2438.
+Internally when this file is read, the additional CRC byte is also obtained
+from the slave device. If it is correct, the 8 bytes page data are passed
+to userspace, otherwise an I/O error is returned.
+
+"offset"
+--------
+This file controls the 2-byte Offset Register of the chip.
+Writing a 2-byte value will change the Offset Register, which changes the
+current measurement done by the chip. Changing this register to the two's complement
+of the current register while forcing zero current through the load will calibrate
+the chip, canceling offset errors in the current ADC.
+
+
 "temperature"
 -------------
 Opening and reading this file initiates the CONVERT_T (temperature conversion)
index 059f0eb..8a1a83b 100644 (file)
@@ -362,7 +362,7 @@ struct vio_driver {
        struct list_head                node;
        const struct vio_device_id      *id_table;
        int (*probe)(struct vio_dev *dev, const struct vio_device_id *id);
-       int (*remove)(struct vio_dev *dev);
+       void (*remove)(struct vio_dev *dev);
        void (*shutdown)(struct vio_dev *dev);
        unsigned long                   driver_data;
        struct device_driver            driver;
index 522e5b5..4a5bdb0 100644 (file)
@@ -1236,11 +1236,6 @@ out_err:
        return err;
 }
 
-static int ds_remove(struct vio_dev *vdev)
-{
-       return 0;
-}
-
 static const struct vio_device_id ds_match[] = {
        {
                .type = "domain-services-port",
@@ -1251,7 +1246,6 @@ static const struct vio_device_id ds_match[] = {
 static struct vio_driver ds_driver = {
        .id_table       = ds_match,
        .probe          = ds_probe,
-       .remove         = ds_remove,
        .name           = "ds",
 };
 
index 4f57056..348a886 100644 (file)
@@ -105,10 +105,10 @@ static int vio_device_remove(struct device *dev)
                 * routines to do so at the moment. TBD
                 */
 
-               return drv->remove(vdev);
+               drv->remove(vdev);
        }
 
-       return 1;
+       return 0;
 }
 
 static ssize_t devspec_show(struct device *dev,
index 39aeebc..1547d43 100644 (file)
@@ -1071,7 +1071,7 @@ err_out_release_mdesc:
        return err;
 }
 
-static int vdc_port_remove(struct vio_dev *vdev)
+static void vdc_port_remove(struct vio_dev *vdev)
 {
        struct vdc_port *port = dev_get_drvdata(&vdev->dev);
 
@@ -1094,7 +1094,6 @@ static int vdc_port_remove(struct vio_dev *vdev)
 
                kfree(port);
        }
-       return 0;
 }
 
 static void vdc_requeue_inflight(struct vdc_port *port)
index b151e0f..8e516aa 100644 (file)
@@ -357,27 +357,6 @@ config NVRAM
          To compile this driver as a module, choose M here: the
          module will be called nvram.
 
-config RAW_DRIVER
-       tristate "RAW driver (/dev/raw/rawN)"
-       depends on BLOCK
-       help
-         The raw driver permits block devices to be bound to /dev/raw/rawN.
-         Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O.
-         See the raw(8) manpage for more details.
-
-         Applications should preferably open the device (eg /dev/hda1)
-         with the O_DIRECT flag.
-
-config MAX_RAW_DEVS
-       int "Maximum number of RAW devices to support (1-65536)"
-       depends on RAW_DRIVER
-       range 1 65536
-       default "256"
-       help
-         The maximum number of RAW devices that are supported.
-         Default is 256. Increase this number in case you need lots of
-         raw devices.
-
 config DEVPORT
        bool "/dev/port character device"
        depends on ISA || PCI
index ffce287..264eb39 100644 (file)
@@ -8,7 +8,6 @@ obj-$(CONFIG_TTY_PRINTK)        += ttyprintk.o
 obj-y                          += misc.o
 obj-$(CONFIG_ATARI_DSP56K)     += dsp56k.o
 obj-$(CONFIG_VIRTIO_CONSOLE)   += virtio_console.o
-obj-$(CONFIG_RAW_DRIVER)       += raw.o
 obj-$(CONFIG_MSPEC)            += mspec.o
 obj-$(CONFIG_UV_MMTIMER)       += uv_mmtimer.o
 obj-$(CONFIG_IBM_BSR)          += bsr.o
@@ -44,6 +43,6 @@ obj-$(CONFIG_TCG_TPM)         += tpm/
 
 obj-$(CONFIG_PS3_FLASH)                += ps3flash.o
 
-obj-$(CONFIG_XILLYBUS)         += xillybus/
+obj-$(CONFIG_XILLYBUS_CLASS)   += xillybus/
 obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
 obj-$(CONFIG_ADI)              += adi.o
index 8b55085..4e5431f 100644 (file)
@@ -156,12 +156,12 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
         * This has the effect of treating non-periodic like periodic.
         */
        if ((devp->hd_flags & (HPET_IE | HPET_PERIODIC)) == HPET_IE) {
-               unsigned long m, t, mc, base, k;
+               unsigned long t, mc, base, k;
                struct hpet __iomem *hpet = devp->hd_hpet;
                struct hpets *hpetp = devp->hd_hpets;
 
                t = devp->hd_ireqfreq;
-               m = read_counter(&devp->hd_timer->hpet_compare);
+               read_counter(&devp->hd_timer->hpet_compare);
                mc = read_counter(&hpet->hpet_mc);
                /* The time for the next interrupt would logically be t + m,
                 * however, if we are very unlucky and the interrupt is delayed
index f4949b6..62bdd5a 100644 (file)
@@ -29,7 +29,7 @@ static int pseries_rng_read(struct hwrng *rng, void *data, size_t max, bool wait
        return 8;
 }
 
-/**
+/*
  * pseries_rng_get_desired_dma - Return desired DMA allocate for CMO operations
  *
  * This is a required function for a driver to operate in a CMO environment
index 15dc54f..1c596b5 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/mman.h>
 #include <linux/random.h>
 #include <linux/init.h>
-#include <linux/raw.h>
 #include <linux/tty.h>
 #include <linux/capability.h>
 #include <linux/ptrace.h>
index 89681f0..8f1bce0 100644 (file)
@@ -544,6 +544,10 @@ static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq)
                io_read_num_rec_bytes(iobase, &num_bytes_read);
                if (num_bytes_read >= 4) {
                        DEBUGP(2, dev, "NumRecBytes = %i\n", num_bytes_read);
+                       if (num_bytes_read > 4) {
+                               rc = -EIO;
+                               goto exit_setprotocol;
+                       }
                        break;
                }
                usleep_range(10000, 11000);
@@ -1050,7 +1054,6 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf,
        struct cm4000_dev *dev = filp->private_data;
        unsigned int iobase = dev->p_dev->resource[0]->start;
        unsigned short s;
-       unsigned char tmp;
        unsigned char infolen;
        unsigned char sendT0;
        unsigned short nsend;
@@ -1148,7 +1151,7 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf,
        set_cardparameter(dev);
 
        /* dummy read, reset flag procedure received */
-       tmp = inb(REG_FLAGS1(iobase));
+       inb(REG_FLAGS1(iobase));
 
        dev->flags1 = 0x20      /* T_Active */
            | (sendT0)
index d5e4360..8277119 100644 (file)
@@ -221,7 +221,6 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf,
        unsigned long i;
        size_t min_bytes_to_read;
        int rc;
-       unsigned char uc;
 
        DEBUGP(2, dev, "-> cm4040_read(%s,%d)\n", current->comm, current->pid);
 
@@ -308,7 +307,7 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf,
                        return -EIO;
        }
 
-       uc = xinb(iobase + REG_OFFSET_BULK_IN);
+       xinb(iobase + REG_OFFSET_BULK_IN);
 
        DEBUGP(2, dev, "<- cm4040_read (successfully)\n");
        return min_bytes_to_read;
index 47feb39..1bdce08 100644 (file)
@@ -265,7 +265,6 @@ static int scr24x_probe(struct pcmcia_device *link)
 
        cdev_init(&dev->c_dev, &scr24x_fops);
        dev->c_dev.owner = THIS_MODULE;
-       dev->c_dev.ops = &scr24x_fops;
        ret = cdev_add(&dev->c_dev, MKDEV(MAJOR(scr24x_devt), dev->devno), 1);
        if (ret < 0)
                goto err;
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
deleted file mode 100644 (file)
index 5d52a1f..0000000
+++ /dev/null
@@ -1,362 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * linux/drivers/char/raw.c
- *
- * Front-end raw character devices.  These can be bound to any block
- * devices to provide genuine Unix raw character device semantics.
- *
- * We reserve minor number 0 for a control interface.  ioctl()s on this
- * device are used to bind the other minor numbers to block devices.
- */
-
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/major.h>
-#include <linux/blkdev.h>
-#include <linux/backing-dev.h>
-#include <linux/module.h>
-#include <linux/raw.h>
-#include <linux/capability.h>
-#include <linux/uio.h>
-#include <linux/cdev.h>
-#include <linux/device.h>
-#include <linux/mutex.h>
-#include <linux/gfp.h>
-#include <linux/compat.h>
-#include <linux/vmalloc.h>
-
-#include <linux/uaccess.h>
-
-struct raw_device_data {
-       dev_t binding;
-       struct block_device *bdev;
-       int inuse;
-};
-
-static struct class *raw_class;
-static struct raw_device_data *raw_devices;
-static DEFINE_MUTEX(raw_mutex);
-static const struct file_operations raw_ctl_fops; /* forward declaration */
-
-static int max_raw_minors = CONFIG_MAX_RAW_DEVS;
-
-module_param(max_raw_minors, int, 0);
-MODULE_PARM_DESC(max_raw_minors, "Maximum number of raw devices (1-65536)");
-
-/*
- * Open/close code for raw IO.
- *
- * We just rewrite the i_mapping for the /dev/raw/rawN file descriptor to
- * point at the blockdev's address_space and set the file handle to use
- * O_DIRECT.
- *
- * Set the device's soft blocksize to the minimum possible.  This gives the
- * finest possible alignment and has no adverse impact on performance.
- */
-static int raw_open(struct inode *inode, struct file *filp)
-{
-       const int minor = iminor(inode);
-       struct block_device *bdev;
-       int err;
-
-       if (minor == 0) {       /* It is the control device */
-               filp->f_op = &raw_ctl_fops;
-               return 0;
-       }
-
-       pr_warn_ratelimited(
-               "process %s (pid %d) is using the deprecated raw device\n"
-               "support will be removed in Linux 5.14.\n",
-               current->comm, current->pid);
-
-       mutex_lock(&raw_mutex);
-
-       /*
-        * All we need to do on open is check that the device is bound.
-        */
-       err = -ENODEV;
-       if (!raw_devices[minor].binding)
-               goto out;
-       bdev = blkdev_get_by_dev(raw_devices[minor].binding,
-                                filp->f_mode | FMODE_EXCL, raw_open);
-       if (IS_ERR(bdev)) {
-               err = PTR_ERR(bdev);
-               goto out;
-       }
-       err = set_blocksize(bdev, bdev_logical_block_size(bdev));
-       if (err)
-               goto out1;
-       filp->f_flags |= O_DIRECT;
-       filp->f_mapping = bdev->bd_inode->i_mapping;
-       if (++raw_devices[minor].inuse == 1)
-               file_inode(filp)->i_mapping =
-                       bdev->bd_inode->i_mapping;
-       filp->private_data = bdev;
-       raw_devices[minor].bdev = bdev;
-       mutex_unlock(&raw_mutex);
-       return 0;
-
-out1:
-       blkdev_put(bdev, filp->f_mode | FMODE_EXCL);
-out:
-       mutex_unlock(&raw_mutex);
-       return err;
-}
-
-/*
- * When the final fd which refers to this character-special node is closed, we
- * make its ->mapping point back at its own i_data.
- */
-static int raw_release(struct inode *inode, struct file *filp)
-{
-       const int minor= iminor(inode);
-       struct block_device *bdev;
-
-       mutex_lock(&raw_mutex);
-       bdev = raw_devices[minor].bdev;
-       if (--raw_devices[minor].inuse == 0)
-               /* Here  inode->i_mapping == bdev->bd_inode->i_mapping  */
-               inode->i_mapping = &inode->i_data;
-       mutex_unlock(&raw_mutex);
-
-       blkdev_put(bdev, filp->f_mode | FMODE_EXCL);
-       return 0;
-}
-
-/*
- * Forward ioctls to the underlying block device.
- */
-static long
-raw_ioctl(struct file *filp, unsigned int command, unsigned long arg)
-{
-       struct block_device *bdev = filp->private_data;
-       return blkdev_ioctl(bdev, 0, command, arg);
-}
-
-static int bind_set(int number, u64 major, u64 minor)
-{
-       dev_t dev = MKDEV(major, minor);
-       dev_t raw = MKDEV(RAW_MAJOR, number);
-       struct raw_device_data *rawdev;
-       int err = 0;
-
-       if (number <= 0 || number >= max_raw_minors)
-               return -EINVAL;
-
-       if (MAJOR(dev) != major || MINOR(dev) != minor)
-               return -EINVAL;
-
-       rawdev = &raw_devices[number];
-
-       /*
-        * This is like making block devices, so demand the
-        * same capability
-        */
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
-
-       /*
-        * For now, we don't need to check that the underlying
-        * block device is present or not: we can do that when
-        * the raw device is opened.  Just check that the
-        * major/minor numbers make sense.
-        */
-
-       if (MAJOR(dev) == 0 && dev != 0)
-               return -EINVAL;
-
-       mutex_lock(&raw_mutex);
-       if (rawdev->inuse) {
-               mutex_unlock(&raw_mutex);
-               return -EBUSY;
-       }
-       if (rawdev->binding)
-               module_put(THIS_MODULE);
-
-       rawdev->binding = dev;
-       if (!dev) {
-               /* unbind */
-               device_destroy(raw_class, raw);
-       } else {
-               __module_get(THIS_MODULE);
-               device_destroy(raw_class, raw);
-               device_create(raw_class, NULL, raw, NULL, "raw%d", number);
-       }
-       mutex_unlock(&raw_mutex);
-       return err;
-}
-
-static int bind_get(int number, dev_t *dev)
-{
-       if (number <= 0 || number >= max_raw_minors)
-               return -EINVAL;
-       *dev = raw_devices[number].binding;
-       return 0;
-}
-
-/*
- * Deal with ioctls against the raw-device control interface, to bind
- * and unbind other raw devices.
- */
-static long raw_ctl_ioctl(struct file *filp, unsigned int command,
-                         unsigned long arg)
-{
-       struct raw_config_request rq;
-       dev_t dev;
-       int err;
-
-       switch (command) {
-       case RAW_SETBIND:
-               if (copy_from_user(&rq, (void __user *) arg, sizeof(rq)))
-                       return -EFAULT;
-
-               return bind_set(rq.raw_minor, rq.block_major, rq.block_minor);
-
-       case RAW_GETBIND:
-               if (copy_from_user(&rq, (void __user *) arg, sizeof(rq)))
-                       return -EFAULT;
-
-               err = bind_get(rq.raw_minor, &dev);
-               if (err)
-                       return err;
-
-               rq.block_major = MAJOR(dev);
-               rq.block_minor = MINOR(dev);
-
-               if (copy_to_user((void __user *)arg, &rq, sizeof(rq)))
-                       return -EFAULT;
-
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
-#ifdef CONFIG_COMPAT
-struct raw32_config_request {
-       compat_int_t    raw_minor;
-       compat_u64      block_major;
-       compat_u64      block_minor;
-};
-
-static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd,
-                               unsigned long arg)
-{
-       struct raw32_config_request __user *user_req = compat_ptr(arg);
-       struct raw32_config_request rq;
-       dev_t dev;
-       int err = 0;
-
-       switch (cmd) {
-       case RAW_SETBIND:
-               if (copy_from_user(&rq, user_req, sizeof(rq)))
-                       return -EFAULT;
-
-               return bind_set(rq.raw_minor, rq.block_major, rq.block_minor);
-
-       case RAW_GETBIND:
-               if (copy_from_user(&rq, user_req, sizeof(rq)))
-                       return -EFAULT;
-
-               err = bind_get(rq.raw_minor, &dev);
-               if (err)
-                       return err;
-
-               rq.block_major = MAJOR(dev);
-               rq.block_minor = MINOR(dev);
-
-               if (copy_to_user(user_req, &rq, sizeof(rq)))
-                       return -EFAULT;
-
-               return 0;
-       }
-
-       return -EINVAL;
-}
-#endif
-
-static const struct file_operations raw_fops = {
-       .read_iter      = blkdev_read_iter,
-       .write_iter     = blkdev_write_iter,
-       .fsync          = blkdev_fsync,
-       .open           = raw_open,
-       .release        = raw_release,
-       .unlocked_ioctl = raw_ioctl,
-       .llseek         = default_llseek,
-       .owner          = THIS_MODULE,
-};
-
-static const struct file_operations raw_ctl_fops = {
-       .unlocked_ioctl = raw_ctl_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl   = raw_ctl_compat_ioctl,
-#endif
-       .open           = raw_open,
-       .owner          = THIS_MODULE,
-       .llseek         = noop_llseek,
-};
-
-static struct cdev raw_cdev;
-
-static char *raw_devnode(struct device *dev, umode_t *mode)
-{
-       return kasprintf(GFP_KERNEL, "raw/%s", dev_name(dev));
-}
-
-static int __init raw_init(void)
-{
-       dev_t dev = MKDEV(RAW_MAJOR, 0);
-       int ret;
-
-       if (max_raw_minors < 1 || max_raw_minors > 65536) {
-               pr_warn("raw: invalid max_raw_minors (must be between 1 and 65536), using %d\n",
-                       CONFIG_MAX_RAW_DEVS);
-               max_raw_minors = CONFIG_MAX_RAW_DEVS;
-       }
-
-       raw_devices = vzalloc(array_size(max_raw_minors,
-                                        sizeof(struct raw_device_data)));
-       if (!raw_devices) {
-               printk(KERN_ERR "Not enough memory for raw device structures\n");
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       ret = register_chrdev_region(dev, max_raw_minors, "raw");
-       if (ret)
-               goto error;
-
-       cdev_init(&raw_cdev, &raw_fops);
-       ret = cdev_add(&raw_cdev, dev, max_raw_minors);
-       if (ret)
-               goto error_region;
-       raw_class = class_create(THIS_MODULE, "raw");
-       if (IS_ERR(raw_class)) {
-               printk(KERN_ERR "Error creating raw class.\n");
-               cdev_del(&raw_cdev);
-               ret = PTR_ERR(raw_class);
-               goto error_region;
-       }
-       raw_class->devnode = raw_devnode;
-       device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl");
-
-       return 0;
-
-error_region:
-       unregister_chrdev_region(dev, max_raw_minors);
-error:
-       vfree(raw_devices);
-       return ret;
-}
-
-static void __exit raw_exit(void)
-{
-       device_destroy(raw_class, MKDEV(RAW_MAJOR, 0));
-       class_destroy(raw_class);
-       cdev_del(&raw_cdev);
-       unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), max_raw_minors);
-}
-
-module_init(raw_init);
-module_exit(raw_exit);
-MODULE_LICENSE("GPL");
index 130dbdc..a8036da 100644 (file)
@@ -3,10 +3,14 @@
 # Xillybus devices
 #
 
+config XILLYBUS_CLASS
+       tristate
+
 config XILLYBUS
        tristate "Xillybus generic FPGA interface"
        depends on PCI || OF
        select CRC32
+       select XILLYBUS_CLASS
        help
          Xillybus is a generic interface for peripherals designed on
          programmable logic (FPGA). The driver probes the hardware for
@@ -21,7 +25,7 @@ config XILLYBUS_PCIE
        depends on PCI_MSI
        help
          Set to M if you want Xillybus to use PCI Express for communicating
-         with the FPGA.
+         with the FPGA. The module will be called xillybus_pcie.
 
 config XILLYBUS_OF
        tristate "Xillybus over Device Tree"
@@ -29,6 +33,20 @@ config XILLYBUS_OF
        help
          Set to M if you want Xillybus to find its resources from the
          Open Firmware Flattened Device Tree. If the target is an embedded
-         system, say M.
+         system, say M. The module will be called xillybus_of.
 
 endif # if XILLYBUS
+
+# XILLYUSB doesn't depend on XILLYBUS
+
+config XILLYUSB
+       tristate "XillyUSB: Xillybus generic FPGA interface for USB"
+       depends on USB
+       select CRC32
+       select XILLYBUS_CLASS
+       help
+         XillyUSB is the Xillybus variant which uses USB for communicating
+         with the FPGA.
+
+         Set to M if you want Xillybus to use USB for communicating with
+         the FPGA. The module will be called xillyusb.
index 099e9a3..16f31d0 100644 (file)
@@ -3,6 +3,8 @@
 # Makefile for Xillybus driver
 #
 
+obj-$(CONFIG_XILLYBUS_CLASS)   += xillybus_class.o
 obj-$(CONFIG_XILLYBUS)         += xillybus_core.o
 obj-$(CONFIG_XILLYBUS_PCIE)    += xillybus_pcie.o
 obj-$(CONFIG_XILLYBUS_OF)      += xillybus_of.o
+obj-$(CONFIG_XILLYUSB)         += xillyusb.o
index 8e3ed4d..c63ffc5 100644 (file)
@@ -30,7 +30,8 @@ struct xilly_buffer {
 
 struct xilly_idt_handle {
        unsigned char *chandesc;
-       unsigned char *idt;
+       unsigned char *names;
+       int names_len;
        int entries;
 };
 
@@ -94,7 +95,6 @@ struct xilly_endpoint {
        struct device *dev;
        struct xilly_endpoint_hardware *ephw;
 
-       struct list_head ep_list;
        int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */
        __iomem void *registers;
        int fatal_error;
@@ -102,12 +102,6 @@ struct xilly_endpoint {
        struct mutex register_mutex;
        wait_queue_head_t ep_wait;
 
-       /* Channels and message handling */
-       struct cdev cdev;
-
-       int major;
-       int lowest_minor; /* Highest minor = lowest_minor + num_channels - 1 */
-
        int num_channels; /* EXCLUDING message buffer */
        struct xilly_channel **channels;
        int msg_counter;
diff --git a/drivers/char/xillybus/xillybus_class.c b/drivers/char/xillybus/xillybus_class.c
new file mode 100644 (file)
index 0000000..5046486
--- /dev/null
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2021 Xillybus Ltd, http://xillybus.com
+ *
+ * Driver for the Xillybus class
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+#include "xillybus_class.h"
+
+MODULE_DESCRIPTION("Driver for Xillybus class");
+MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
+MODULE_ALIAS("xillybus_class");
+MODULE_LICENSE("GPL v2");
+
+static DEFINE_MUTEX(unit_mutex);
+static LIST_HEAD(unit_list);
+static struct class *xillybus_class;
+
+#define UNITNAMELEN 16
+
+struct xilly_unit {
+       struct list_head list_entry;
+       void *private_data;
+
+       struct cdev *cdev;
+       char name[UNITNAMELEN];
+       int major;
+       int lowest_minor;
+       int num_nodes;
+};
+
+int xillybus_init_chrdev(struct device *dev,
+                        const struct file_operations *fops,
+                        struct module *owner,
+                        void *private_data,
+                        unsigned char *idt, unsigned int len,
+                        int num_nodes,
+                        const char *prefix, bool enumerate)
+{
+       int rc;
+       dev_t mdev;
+       int i;
+       char devname[48];
+
+       struct device *device;
+       size_t namelen;
+       struct xilly_unit *unit, *u;
+
+       unit = kzalloc(sizeof(*unit), GFP_KERNEL);
+
+       if (!unit)
+               return -ENOMEM;
+
+       mutex_lock(&unit_mutex);
+
+       if (!enumerate)
+               snprintf(unit->name, UNITNAMELEN, "%s", prefix);
+
+       for (i = 0; enumerate; i++) {
+               snprintf(unit->name, UNITNAMELEN, "%s_%02d",
+                        prefix, i);
+
+               enumerate = false;
+               list_for_each_entry(u, &unit_list, list_entry)
+                       if (!strcmp(unit->name, u->name)) {
+                               enumerate = true;
+                               break;
+                       }
+       }
+
+       rc = alloc_chrdev_region(&mdev, 0, num_nodes, unit->name);
+
+       if (rc) {
+               dev_warn(dev, "Failed to obtain major/minors");
+               goto fail_obtain;
+       }
+
+       unit->major = MAJOR(mdev);
+       unit->lowest_minor = MINOR(mdev);
+       unit->num_nodes = num_nodes;
+       unit->private_data = private_data;
+
+       unit->cdev = cdev_alloc();
+       if (!unit->cdev) {
+               rc = -ENOMEM;
+               goto unregister_chrdev;
+       }
+       unit->cdev->ops = fops;
+       unit->cdev->owner = owner;
+
+       rc = cdev_add(unit->cdev, MKDEV(unit->major, unit->lowest_minor),
+                     unit->num_nodes);
+       if (rc) {
+               dev_err(dev, "Failed to add cdev.\n");
+               /* kobject_put() is normally done by cdev_del() */
+               kobject_put(&unit->cdev->kobj);
+               goto unregister_chrdev;
+       }
+
+       for (i = 0; i < num_nodes; i++) {
+               namelen = strnlen(idt, len);
+
+               if (namelen == len) {
+                       dev_err(dev, "IDT's list of names is too short. This is exceptionally weird, because its CRC is OK\n");
+                       rc = -ENODEV;
+                       goto unroll_device_create;
+               }
+
+               snprintf(devname, sizeof(devname), "%s_%s",
+                        unit->name, idt);
+
+               len -= namelen + 1;
+               idt += namelen + 1;
+
+               device = device_create(xillybus_class,
+                                      NULL,
+                                      MKDEV(unit->major,
+                                            i + unit->lowest_minor),
+                                      NULL,
+                                      "%s", devname);
+
+               if (IS_ERR(device)) {
+                       dev_err(dev, "Failed to create %s device. Aborting.\n",
+                               devname);
+                       rc = -ENODEV;
+                       goto unroll_device_create;
+               }
+       }
+
+       if (len) {
+               dev_err(dev, "IDT's list of names is too long. This is exceptionally weird, because its CRC is OK\n");
+               rc = -ENODEV;
+               goto unroll_device_create;
+       }
+
+       list_add_tail(&unit->list_entry, &unit_list);
+
+       dev_info(dev, "Created %d device files.\n", num_nodes);
+
+       mutex_unlock(&unit_mutex);
+
+       return 0;
+
+unroll_device_create:
+       for (i--; i >= 0; i--)
+               device_destroy(xillybus_class, MKDEV(unit->major,
+                                                    i + unit->lowest_minor));
+
+       cdev_del(unit->cdev);
+
+unregister_chrdev:
+       unregister_chrdev_region(MKDEV(unit->major, unit->lowest_minor),
+                                unit->num_nodes);
+
+fail_obtain:
+       mutex_unlock(&unit_mutex);
+
+       kfree(unit);
+
+       return rc;
+}
+EXPORT_SYMBOL(xillybus_init_chrdev);
+
+void xillybus_cleanup_chrdev(void *private_data,
+                            struct device *dev)
+{
+       int minor;
+       struct xilly_unit *unit;
+       bool found = false;
+
+       mutex_lock(&unit_mutex);
+
+       list_for_each_entry(unit, &unit_list, list_entry)
+               if (unit->private_data == private_data) {
+                       found = true;
+                       break;
+               }
+
+       if (!found) {
+               dev_err(dev, "Weird bug: Failed to find unit\n");
+               mutex_unlock(&unit_mutex);
+               return;
+       }
+
+       for (minor = unit->lowest_minor;
+            minor < (unit->lowest_minor + unit->num_nodes);
+            minor++)
+               device_destroy(xillybus_class, MKDEV(unit->major, minor));
+
+       cdev_del(unit->cdev);
+
+       unregister_chrdev_region(MKDEV(unit->major, unit->lowest_minor),
+                                unit->num_nodes);
+
+       dev_info(dev, "Removed %d device files.\n",
+                unit->num_nodes);
+
+       list_del(&unit->list_entry);
+       kfree(unit);
+
+       mutex_unlock(&unit_mutex);
+}
+EXPORT_SYMBOL(xillybus_cleanup_chrdev);
+
+int xillybus_find_inode(struct inode *inode,
+                       void **private_data, int *index)
+{
+       int minor = iminor(inode);
+       int major = imajor(inode);
+       struct xilly_unit *unit;
+       bool found = false;
+
+       mutex_lock(&unit_mutex);
+
+       list_for_each_entry(unit, &unit_list, list_entry)
+               if (unit->major == major &&
+                   minor >= unit->lowest_minor &&
+                   minor < (unit->lowest_minor + unit->num_nodes)) {
+                       found = true;
+                       break;
+               }
+
+       mutex_unlock(&unit_mutex);
+
+       if (!found)
+               return -ENODEV;
+
+       *private_data = unit->private_data;
+       *index = minor - unit->lowest_minor;
+
+       return 0;
+}
+EXPORT_SYMBOL(xillybus_find_inode);
+
+static int __init xillybus_class_init(void)
+{
+       xillybus_class = class_create(THIS_MODULE, "xillybus");
+
+       if (IS_ERR(xillybus_class)) {
+               pr_warn("Failed to register xillybus class\n");
+
+               return PTR_ERR(xillybus_class);
+       }
+       return 0;
+}
+
+static void __exit xillybus_class_exit(void)
+{
+       class_destroy(xillybus_class);
+}
+
+module_init(xillybus_class_init);
+module_exit(xillybus_class_exit);
diff --git a/drivers/char/xillybus/xillybus_class.h b/drivers/char/xillybus/xillybus_class.h
new file mode 100644 (file)
index 0000000..5dbfdfc
--- /dev/null
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2021 Xillybus Ltd, http://www.xillybus.com
+ *
+ * Header file for the Xillybus class
+ */
+
+#ifndef __XILLYBUS_CLASS_H
+#define __XILLYBUS_CLASS_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+
+int xillybus_init_chrdev(struct device *dev,
+                        const struct file_operations *fops,
+                        struct module *owner,
+                        void *private_data,
+                        unsigned char *idt, unsigned int len,
+                        int num_nodes,
+                        const char *prefix, bool enumerate);
+
+void xillybus_cleanup_chrdev(void *private_data,
+                            struct device *dev);
+
+int xillybus_find_inode(struct inode *inode,
+                       void **private_data, int *index);
+
+#endif /* __XILLYBUS_CLASS_H */
index 57fa688..931d0bf 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
-#include <linux/cdev.h>
 #include <linux/spinlock.h>
 #include <linux/mutex.h>
 #include <linux/crc32.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 #include "xillybus.h"
+#include "xillybus_class.h"
 
 MODULE_DESCRIPTION("Xillybus core functions");
 MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
-MODULE_VERSION("1.07");
 MODULE_ALIAS("xillybus_core");
 MODULE_LICENSE("GPL v2");
 
@@ -58,16 +57,6 @@ MODULE_LICENSE("GPL v2");
 
 static const char xillyname[] = "xillybus";
 
-static struct class *xillybus_class;
-
-/*
- * ep_list_lock is the last lock to be taken; No other lock requests are
- * allowed while holding it. It merely protects list_of_endpoints, and not
- * the endpoints listed in it.
- */
-
-static LIST_HEAD(list_of_endpoints);
-static struct mutex ep_list_lock;
 static struct workqueue_struct *xillybus_wq;
 
 /*
@@ -570,10 +559,8 @@ static int xilly_scan_idt(struct xilly_endpoint *endpoint,
        unsigned char *scan;
        int len;
 
-       scan = idt;
-       idt_handle->idt = idt;
-
-       scan++; /* Skip version number */
+       scan = idt + 1;
+       idt_handle->names = scan;
 
        while ((scan <= end_of_idt) && *scan) {
                while ((scan <= end_of_idt) && *scan++)
@@ -581,6 +568,8 @@ static int xilly_scan_idt(struct xilly_endpoint *endpoint,
                count++;
        }
 
+       idt_handle->names_len = scan - idt_handle->names;
+
        scan++;
 
        if (scan > end_of_idt) {
@@ -1407,36 +1396,20 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf,
 
 static int xillybus_open(struct inode *inode, struct file *filp)
 {
-       int rc = 0;
+       int rc;
        unsigned long flags;
-       int minor = iminor(inode);
-       int major = imajor(inode);
-       struct xilly_endpoint *ep_iter, *endpoint = NULL;
+       struct xilly_endpoint *endpoint;
        struct xilly_channel *channel;
+       int index;
 
-       mutex_lock(&ep_list_lock);
-
-       list_for_each_entry(ep_iter, &list_of_endpoints, ep_list) {
-               if ((ep_iter->major == major) &&
-                   (minor >= ep_iter->lowest_minor) &&
-                   (minor < (ep_iter->lowest_minor +
-                             ep_iter->num_channels))) {
-                       endpoint = ep_iter;
-                       break;
-               }
-       }
-       mutex_unlock(&ep_list_lock);
-
-       if (!endpoint) {
-               pr_err("xillybus: open() failed to find a device for major=%d and minor=%d\n",
-                      major, minor);
-               return -ENODEV;
-       }
+       rc = xillybus_find_inode(inode, (void **)&endpoint, &index);
+       if (rc)
+               return rc;
 
        if (endpoint->fatal_error)
                return -EIO;
 
-       channel = endpoint->channels[1 + minor - endpoint->lowest_minor];
+       channel = endpoint->channels[1 + index];
        filp->private_data = channel;
 
        /*
@@ -1799,95 +1772,6 @@ static const struct file_operations xillybus_fops = {
        .poll       = xillybus_poll,
 };
 
-static int xillybus_init_chrdev(struct xilly_endpoint *endpoint,
-                               const unsigned char *idt)
-{
-       int rc;
-       dev_t dev;
-       int devnum, i, minor, major;
-       char devname[48];
-       struct device *device;
-
-       rc = alloc_chrdev_region(&dev, 0, /* minor start */
-                                endpoint->num_channels,
-                                xillyname);
-       if (rc) {
-               dev_warn(endpoint->dev, "Failed to obtain major/minors");
-               return rc;
-       }
-
-       endpoint->major = major = MAJOR(dev);
-       endpoint->lowest_minor = minor = MINOR(dev);
-
-       cdev_init(&endpoint->cdev, &xillybus_fops);
-       endpoint->cdev.owner = endpoint->ephw->owner;
-       rc = cdev_add(&endpoint->cdev, MKDEV(major, minor),
-                     endpoint->num_channels);
-       if (rc) {
-               dev_warn(endpoint->dev, "Failed to add cdev. Aborting.\n");
-               goto unregister_chrdev;
-       }
-
-       idt++;
-
-       for (i = minor, devnum = 0;
-            devnum < endpoint->num_channels;
-            devnum++, i++) {
-               snprintf(devname, sizeof(devname)-1, "xillybus_%s", idt);
-
-               devname[sizeof(devname)-1] = 0; /* Should never matter */
-
-               while (*idt++)
-                       /* Skip to next */;
-
-               device = device_create(xillybus_class,
-                                      NULL,
-                                      MKDEV(major, i),
-                                      NULL,
-                                      "%s", devname);
-
-               if (IS_ERR(device)) {
-                       dev_warn(endpoint->dev,
-                                "Failed to create %s device. Aborting.\n",
-                                devname);
-                       rc = -ENODEV;
-                       goto unroll_device_create;
-               }
-       }
-
-       dev_info(endpoint->dev, "Created %d device files.\n",
-                endpoint->num_channels);
-       return 0; /* succeed */
-
-unroll_device_create:
-       devnum--; i--;
-       for (; devnum >= 0; devnum--, i--)
-               device_destroy(xillybus_class, MKDEV(major, i));
-
-       cdev_del(&endpoint->cdev);
-unregister_chrdev:
-       unregister_chrdev_region(MKDEV(major, minor), endpoint->num_channels);
-
-       return rc;
-}
-
-static void xillybus_cleanup_chrdev(struct xilly_endpoint *endpoint)
-{
-       int minor;
-
-       for (minor = endpoint->lowest_minor;
-            minor < (endpoint->lowest_minor + endpoint->num_channels);
-            minor++)
-               device_destroy(xillybus_class, MKDEV(endpoint->major, minor));
-       cdev_del(&endpoint->cdev);
-       unregister_chrdev_region(MKDEV(endpoint->major,
-                                      endpoint->lowest_minor),
-                                endpoint->num_channels);
-
-       dev_info(endpoint->dev, "Removed %d device files.\n",
-                endpoint->num_channels);
-}
-
 struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev,
                                              struct device *dev,
                                              struct xilly_endpoint_hardware
@@ -2027,28 +1911,20 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
        if (rc)
                goto failed_idt;
 
-       /*
-        * endpoint is now completely configured. We put it on the list
-        * available to open() before registering the char device(s)
-        */
-
-       mutex_lock(&ep_list_lock);
-       list_add_tail(&endpoint->ep_list, &list_of_endpoints);
-       mutex_unlock(&ep_list_lock);
+       rc = xillybus_init_chrdev(dev, &xillybus_fops,
+                                 endpoint->ephw->owner, endpoint,
+                                 idt_handle.names,
+                                 idt_handle.names_len,
+                                 endpoint->num_channels,
+                                 xillyname, false);
 
-       rc = xillybus_init_chrdev(endpoint, idt_handle.idt);
        if (rc)
-               goto failed_chrdevs;
+               goto failed_idt;
 
        devres_release_group(dev, bootstrap_resources);
 
        return 0;
 
-failed_chrdevs:
-       mutex_lock(&ep_list_lock);
-       list_del(&endpoint->ep_list);
-       mutex_unlock(&ep_list_lock);
-
 failed_idt:
        xilly_quiesce(endpoint);
        flush_workqueue(xillybus_wq);
@@ -2059,11 +1935,7 @@ EXPORT_SYMBOL(xillybus_endpoint_discovery);
 
 void xillybus_endpoint_remove(struct xilly_endpoint *endpoint)
 {
-       xillybus_cleanup_chrdev(endpoint);
-
-       mutex_lock(&ep_list_lock);
-       list_del(&endpoint->ep_list);
-       mutex_unlock(&ep_list_lock);
+       xillybus_cleanup_chrdev(endpoint, endpoint->dev);
 
        xilly_quiesce(endpoint);
 
@@ -2077,17 +1949,9 @@ EXPORT_SYMBOL(xillybus_endpoint_remove);
 
 static int __init xillybus_init(void)
 {
-       mutex_init(&ep_list_lock);
-
-       xillybus_class = class_create(THIS_MODULE, xillyname);
-       if (IS_ERR(xillybus_class))
-               return PTR_ERR(xillybus_class);
-
        xillybus_wq = alloc_workqueue(xillyname, 0, 0);
-       if (!xillybus_wq) {
-               class_destroy(xillybus_class);
+       if (!xillybus_wq)
                return -ENOMEM;
-       }
 
        return 0;
 }
@@ -2096,8 +1960,6 @@ static void __exit xillybus_exit(void)
 {
        /* flush_workqueue() was called for each endpoint released */
        destroy_workqueue(xillybus_wq);
-
-       class_destroy(xillybus_class);
 }
 
 module_init(xillybus_init);
index 96b6de8..1a20b28 100644 (file)
@@ -17,7 +17,6 @@
 
 MODULE_DESCRIPTION("Xillybus driver for Open Firmware");
 MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
-MODULE_VERSION("1.06");
 MODULE_ALIAS("xillybus_of");
 MODULE_LICENSE("GPL v2");
 
index 18b0c39..bdf1c36 100644 (file)
@@ -14,7 +14,6 @@
 
 MODULE_DESCRIPTION("Xillybus driver for PCIe");
 MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
-MODULE_VERSION("1.06");
 MODULE_ALIAS("xillybus_pcie");
 MODULE_LICENSE("GPL v2");
 
diff --git a/drivers/char/xillybus/xillyusb.c b/drivers/char/xillybus/xillyusb.c
new file mode 100644 (file)
index 0000000..e7f88f3
--- /dev/null
@@ -0,0 +1,2259 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2020 Xillybus Ltd, http://xillybus.com
+ *
+ * Driver for the XillyUSB FPGA/host framework.
+ *
+ * This driver interfaces with a special IP core in an FPGA, setting up
+ * a pipe between a hardware FIFO in the programmable logic and a device
+ * file in the host. The number of such pipes and their attributes are
+ * set up on the logic. This driver detects these automatically and
+ * creates the device files accordingly.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/crc32.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+
+#include "xillybus_class.h"
+
+MODULE_DESCRIPTION("Driver for XillyUSB FPGA IP Core");
+MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
+MODULE_ALIAS("xillyusb");
+MODULE_LICENSE("GPL v2");
+
+#define XILLY_RX_TIMEOUT               (10 * HZ / 1000)
+#define XILLY_RESPONSE_TIMEOUT         (500 * HZ / 1000)
+
+#define BUF_SIZE_ORDER                 4
+#define BUFNUM                         8
+#define LOG2_IDT_FIFO_SIZE             16
+#define LOG2_INITIAL_FIFO_BUF_SIZE     16
+
+#define MSG_EP_NUM                     1
+#define IN_EP_NUM                      1
+
+static const char xillyname[] = "xillyusb";
+
+static unsigned int fifo_buf_order;
+
+#define USB_VENDOR_ID_XILINX           0x03fd
+#define USB_VENDOR_ID_ALTERA           0x09fb
+
+#define USB_PRODUCT_ID_XILLYUSB                0xebbe
+
+static const struct usb_device_id xillyusb_table[] = {
+       { USB_DEVICE(USB_VENDOR_ID_XILINX, USB_PRODUCT_ID_XILLYUSB) },
+       { USB_DEVICE(USB_VENDOR_ID_ALTERA, USB_PRODUCT_ID_XILLYUSB) },
+       { }
+};
+
+MODULE_DEVICE_TABLE(usb, xillyusb_table);
+
+struct xillyusb_dev;
+
+struct xillyfifo {
+       unsigned int bufsize; /* In bytes, always a power of 2 */
+       unsigned int bufnum;
+       unsigned int size; /* Lazy: Equals bufsize * bufnum */
+       unsigned int buf_order;
+
+       int fill; /* Number of bytes in the FIFO */
+       spinlock_t lock;
+       wait_queue_head_t waitq;
+
+       unsigned int readpos;
+       unsigned int readbuf;
+       unsigned int writepos;
+       unsigned int writebuf;
+       char **mem;
+};
+
+struct xillyusb_channel;
+
+struct xillyusb_endpoint {
+       struct xillyusb_dev *xdev;
+
+       struct mutex ep_mutex; /* serialize operations on endpoint */
+
+       struct list_head buffers;
+       struct list_head filled_buffers;
+       spinlock_t buffers_lock; /* protect these two lists */
+
+       unsigned int order;
+       unsigned int buffer_size;
+
+       unsigned int fill_mask;
+
+       int outstanding_urbs;
+
+       struct usb_anchor anchor;
+
+       struct xillyfifo fifo;
+
+       struct work_struct workitem;
+
+       bool shutting_down;
+       bool drained;
+       bool wake_on_drain;
+
+       u8 ep_num;
+};
+
+struct xillyusb_channel {
+       struct xillyusb_dev *xdev;
+
+       struct xillyfifo *in_fifo;
+       struct xillyusb_endpoint *out_ep;
+       struct mutex lock; /* protect @out_ep, @in_fifo, bit fields below */
+
+       struct mutex in_mutex; /* serialize fops on FPGA to host stream */
+       struct mutex out_mutex; /* serialize fops on host to FPGA stream */
+       wait_queue_head_t flushq;
+
+       int chan_idx;
+
+       u32 in_consumed_bytes;
+       u32 in_current_checkpoint;
+       u32 out_bytes;
+
+       unsigned int in_log2_element_size;
+       unsigned int out_log2_element_size;
+       unsigned int in_log2_fifo_size;
+       unsigned int out_log2_fifo_size;
+
+       unsigned int read_data_ok; /* EOF not arrived (yet) */
+       unsigned int poll_used;
+       unsigned int flushing;
+       unsigned int flushed;
+       unsigned int canceled;
+
+       /* Bit fields protected by @lock except for initialization */
+       unsigned readable:1;
+       unsigned writable:1;
+       unsigned open_for_read:1;
+       unsigned open_for_write:1;
+       unsigned in_synchronous:1;
+       unsigned out_synchronous:1;
+       unsigned in_seekable:1;
+       unsigned out_seekable:1;
+};
+
+struct xillybuffer {
+       struct list_head entry;
+       struct xillyusb_endpoint *ep;
+       void *buf;
+       unsigned int len;
+};
+
+struct xillyusb_dev {
+       struct xillyusb_channel *channels;
+
+       struct usb_device       *udev;
+       struct device           *dev; /* For dev_err() and such */
+       struct kref             kref;
+       struct workqueue_struct *workq;
+
+       int error;
+       spinlock_t error_lock; /* protect @error */
+       struct work_struct wakeup_workitem;
+
+       int num_channels;
+
+       struct xillyusb_endpoint *msg_ep;
+       struct xillyusb_endpoint *in_ep;
+
+       struct mutex msg_mutex; /* serialize opcode transmission */
+       int in_bytes_left;
+       int leftover_chan_num;
+       unsigned int in_counter;
+       struct mutex process_in_mutex; /* synchronize wakeup_all() */
+};
+
+/* FPGA to host opcodes */
+enum {
+       OPCODE_DATA = 0,
+       OPCODE_QUIESCE_ACK = 1,
+       OPCODE_EOF = 2,
+       OPCODE_REACHED_CHECKPOINT = 3,
+       OPCODE_CANCELED_CHECKPOINT = 4,
+};
+
+/* Host to FPGA opcodes */
+enum {
+       OPCODE_QUIESCE = 0,
+       OPCODE_REQ_IDT = 1,
+       OPCODE_SET_CHECKPOINT = 2,
+       OPCODE_CLOSE = 3,
+       OPCODE_SET_PUSH = 4,
+       OPCODE_UPDATE_PUSH = 5,
+       OPCODE_CANCEL_CHECKPOINT = 6,
+       OPCODE_SET_ADDR = 7,
+};
+
+/*
+ * fifo_write() and fifo_read() are NOT reentrant (i.e. concurrent multiple
+ * calls to each on the same FIFO is not allowed) however it's OK to have
+ * threads calling each of the two functions once on the same FIFO, and
+ * at the same time.
+ */
+
+static int fifo_write(struct xillyfifo *fifo,
+                     const void *data, unsigned int len,
+                     int (*copier)(void *, const void *, int))
+{
+       unsigned int done = 0;
+       unsigned int todo = len;
+       unsigned int nmax;
+       unsigned int writepos = fifo->writepos;
+       unsigned int writebuf = fifo->writebuf;
+       unsigned long flags;
+       int rc;
+
+       nmax = fifo->size - READ_ONCE(fifo->fill);
+
+       while (1) {
+               unsigned int nrail = fifo->bufsize - writepos;
+               unsigned int n = min(todo, nmax);
+
+               if (n == 0) {
+                       spin_lock_irqsave(&fifo->lock, flags);
+                       fifo->fill += done;
+                       spin_unlock_irqrestore(&fifo->lock, flags);
+
+                       fifo->writepos = writepos;
+                       fifo->writebuf = writebuf;
+
+                       return done;
+               }
+
+               if (n > nrail)
+                       n = nrail;
+
+               rc = (*copier)(fifo->mem[writebuf] + writepos, data + done, n);
+
+               if (rc)
+                       return rc;
+
+               done += n;
+               todo -= n;
+
+               writepos += n;
+               nmax -= n;
+
+               if (writepos == fifo->bufsize) {
+                       writepos = 0;
+                       writebuf++;
+
+                       if (writebuf == fifo->bufnum)
+                               writebuf = 0;
+               }
+       }
+}
+
+static int fifo_read(struct xillyfifo *fifo,
+                    void *data, unsigned int len,
+                    int (*copier)(void *, const void *, int))
+{
+       unsigned int done = 0;
+       unsigned int todo = len;
+       unsigned int fill;
+       unsigned int readpos = fifo->readpos;
+       unsigned int readbuf = fifo->readbuf;
+       unsigned long flags;
+       int rc;
+
+       /*
+        * The spinlock here is necessary, because otherwise fifo->fill
+        * could have been increased by fifo_write() after writing data
+        * to the buffer, but this data would potentially not have been
+        * visible on this thread at the time the updated fifo->fill was.
+        * That could lead to reading invalid data.
+        */
+
+       spin_lock_irqsave(&fifo->lock, flags);
+       fill = fifo->fill;
+       spin_unlock_irqrestore(&fifo->lock, flags);
+
+       while (1) {
+               unsigned int nrail = fifo->bufsize - readpos;
+               unsigned int n = min(todo, fill);
+
+               if (n == 0) {
+                       spin_lock_irqsave(&fifo->lock, flags);
+                       fifo->fill -= done;
+                       spin_unlock_irqrestore(&fifo->lock, flags);
+
+                       fifo->readpos = readpos;
+                       fifo->readbuf = readbuf;
+
+                       return done;
+               }
+
+               if (n > nrail)
+                       n = nrail;
+
+               rc = (*copier)(data + done, fifo->mem[readbuf] + readpos, n);
+
+               if (rc)
+                       return rc;
+
+               done += n;
+               todo -= n;
+
+               readpos += n;
+               fill -= n;
+
+               if (readpos == fifo->bufsize) {
+                       readpos = 0;
+                       readbuf++;
+
+                       if (readbuf == fifo->bufnum)
+                               readbuf = 0;
+               }
+       }
+}
+
+/*
+ * These three wrapper functions are used as the @copier argument to
+ * fifo_write() and fifo_read(), so that they can work directly with
+ * user memory as well.
+ */
+
+static int xilly_copy_from_user(void *dst, const void *src, int n)
+{
+       if (copy_from_user(dst, (const void __user *)src, n))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int xilly_copy_to_user(void *dst, const void *src, int n)
+{
+       if (copy_to_user((void __user *)dst, src, n))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int xilly_memcpy(void *dst, const void *src, int n)
+{
+       memcpy(dst, src, n);
+
+       return 0;
+}
+
+static int fifo_init(struct xillyfifo *fifo,
+                    unsigned int log2_size)
+{
+       unsigned int log2_bufnum;
+       unsigned int buf_order;
+       int i;
+
+       unsigned int log2_fifo_buf_size;
+
+retry:
+       log2_fifo_buf_size = fifo_buf_order + PAGE_SHIFT;
+
+       if (log2_size > log2_fifo_buf_size) {
+               log2_bufnum = log2_size - log2_fifo_buf_size;
+               buf_order = fifo_buf_order;
+               fifo->bufsize = 1 << log2_fifo_buf_size;
+       } else {
+               log2_bufnum = 0;
+               buf_order = (log2_size > PAGE_SHIFT) ?
+                       log2_size - PAGE_SHIFT : 0;
+               fifo->bufsize = 1 << log2_size;
+       }
+
+       fifo->bufnum = 1 << log2_bufnum;
+       fifo->size = fifo->bufnum * fifo->bufsize;
+       fifo->buf_order = buf_order;
+
+       fifo->mem = kmalloc_array(fifo->bufnum, sizeof(void *), GFP_KERNEL);
+
+       if (!fifo->mem)
+               return -ENOMEM;
+
+       for (i = 0; i < fifo->bufnum; i++) {
+               fifo->mem[i] = (void *)
+                       __get_free_pages(GFP_KERNEL, buf_order);
+
+               if (!fifo->mem[i])
+                       goto memfail;
+       }
+
+       fifo->fill = 0;
+       fifo->readpos = 0;
+       fifo->readbuf = 0;
+       fifo->writepos = 0;
+       fifo->writebuf = 0;
+       spin_lock_init(&fifo->lock);
+       init_waitqueue_head(&fifo->waitq);
+       return 0;
+
+memfail:
+       for (i--; i >= 0; i--)
+               free_pages((unsigned long)fifo->mem[i], buf_order);
+
+       kfree(fifo->mem);
+       fifo->mem = NULL;
+
+       if (fifo_buf_order) {
+               fifo_buf_order--;
+               goto retry;
+       } else {
+               return -ENOMEM;
+       }
+}
+
+static void fifo_mem_release(struct xillyfifo *fifo)
+{
+       int i;
+
+       if (!fifo->mem)
+               return;
+
+       for (i = 0; i < fifo->bufnum; i++)
+               free_pages((unsigned long)fifo->mem[i], fifo->buf_order);
+
+       kfree(fifo->mem);
+}
+
+/*
+ * When endpoint_quiesce() returns, the endpoint has no URBs submitted,
+ * won't accept any new URB submissions, and its related work item doesn't
+ * and won't run anymore.
+ */
+
+static void endpoint_quiesce(struct xillyusb_endpoint *ep)
+{
+       mutex_lock(&ep->ep_mutex);
+       ep->shutting_down = true;
+       mutex_unlock(&ep->ep_mutex);
+
+       usb_kill_anchored_urbs(&ep->anchor);
+       cancel_work_sync(&ep->workitem);
+}
+
+/*
+ * Note that endpoint_dealloc() also frees fifo memory (if allocated), even
+ * though endpoint_alloc doesn't allocate that memory.
+ */
+
+static void endpoint_dealloc(struct xillyusb_endpoint *ep)
+{
+       struct list_head *this, *next;
+
+       fifo_mem_release(&ep->fifo);
+
+       /* Join @filled_buffers with @buffers to free these entries too */
+       list_splice(&ep->filled_buffers, &ep->buffers);
+
+       list_for_each_safe(this, next, &ep->buffers) {
+               struct xillybuffer *xb =
+                       list_entry(this, struct xillybuffer, entry);
+
+               free_pages((unsigned long)xb->buf, ep->order);
+               kfree(xb);
+       }
+
+       kfree(ep);
+}
+
+static struct xillyusb_endpoint
+*endpoint_alloc(struct xillyusb_dev *xdev,
+               u8 ep_num,
+               void (*work)(struct work_struct *),
+               unsigned int order,
+               int bufnum)
+{
+       int i;
+
+       struct xillyusb_endpoint *ep;
+
+       ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+
+       if (!ep)
+               return NULL;
+
+       INIT_LIST_HEAD(&ep->buffers);
+       INIT_LIST_HEAD(&ep->filled_buffers);
+
+       spin_lock_init(&ep->buffers_lock);
+       mutex_init(&ep->ep_mutex);
+
+       init_usb_anchor(&ep->anchor);
+       INIT_WORK(&ep->workitem, work);
+
+       ep->order = order;
+       ep->buffer_size =  1 << (PAGE_SHIFT + order);
+       ep->outstanding_urbs = 0;
+       ep->drained = true;
+       ep->wake_on_drain = false;
+       ep->xdev = xdev;
+       ep->ep_num = ep_num;
+       ep->shutting_down = false;
+
+       for (i = 0; i < bufnum; i++) {
+               struct xillybuffer *xb;
+               unsigned long addr;
+
+               xb = kzalloc(sizeof(*xb), GFP_KERNEL);
+
+               if (!xb) {
+                       endpoint_dealloc(ep);
+                       return NULL;
+               }
+
+               addr = __get_free_pages(GFP_KERNEL, order);
+
+               if (!addr) {
+                       kfree(xb);
+                       endpoint_dealloc(ep);
+                       return NULL;
+               }
+
+               xb->buf = (void *)addr;
+               xb->ep = ep;
+               list_add_tail(&xb->entry, &ep->buffers);
+       }
+       return ep;
+}
+
+static void cleanup_dev(struct kref *kref)
+{
+       struct xillyusb_dev *xdev =
+               container_of(kref, struct xillyusb_dev, kref);
+
+       if (xdev->in_ep)
+               endpoint_dealloc(xdev->in_ep);
+
+       if (xdev->msg_ep)
+               endpoint_dealloc(xdev->msg_ep);
+
+       if (xdev->workq)
+               destroy_workqueue(xdev->workq);
+
+       kfree(xdev->channels); /* Argument may be NULL, and that's fine */
+       kfree(xdev);
+}
+
+/*
+ * @process_in_mutex is taken to ensure that bulk_in_work() won't call
+ * process_bulk_in() after wakeup_all()'s execution: The latter zeroes all
+ * @read_data_ok entries, which will make process_bulk_in() report false
+ * errors if executed. The mechanism relies on that xdev->error is assigned
+ * a non-zero value by report_io_error() prior to queueing wakeup_all(),
+ * which prevents bulk_in_work() from calling process_bulk_in().
+ *
+ * The fact that wakeup_all() and bulk_in_work() are queued on the same
+ * workqueue makes their concurrent execution very unlikely, however the
+ * kernel's API doesn't seem to ensure this strictly.
+ */
+
+static void wakeup_all(struct work_struct *work)
+{
+       int i;
+       struct xillyusb_dev *xdev = container_of(work, struct xillyusb_dev,
+                                                wakeup_workitem);
+
+       mutex_lock(&xdev->process_in_mutex);
+
+       for (i = 0; i < xdev->num_channels; i++) {
+               struct xillyusb_channel *chan = &xdev->channels[i];
+
+               mutex_lock(&chan->lock);
+
+               if (chan->in_fifo) {
+                       /*
+                        * Fake an EOF: Even if such arrives, it won't be
+                        * processed.
+                        */
+                       chan->read_data_ok = 0;
+                       wake_up_interruptible(&chan->in_fifo->waitq);
+               }
+
+               if (chan->out_ep)
+                       wake_up_interruptible(&chan->out_ep->fifo.waitq);
+
+               mutex_unlock(&chan->lock);
+
+               wake_up_interruptible(&chan->flushq);
+       }
+
+       mutex_unlock(&xdev->process_in_mutex);
+
+       wake_up_interruptible(&xdev->msg_ep->fifo.waitq);
+
+       kref_put(&xdev->kref, cleanup_dev);
+}
+
+static void report_io_error(struct xillyusb_dev *xdev,
+                           int errcode)
+{
+       unsigned long flags;
+       bool do_once = false;
+
+       spin_lock_irqsave(&xdev->error_lock, flags);
+       if (!xdev->error) {
+               xdev->error = errcode;
+               do_once = true;
+       }
+       spin_unlock_irqrestore(&xdev->error_lock, flags);
+
+       if (do_once) {
+               kref_get(&xdev->kref); /* xdev is used by work item */
+               queue_work(xdev->workq, &xdev->wakeup_workitem);
+       }
+}
+
+/*
+ * safely_assign_in_fifo() changes the value of chan->in_fifo and ensures
+ * the previous pointer is never used after its return.
+ */
+
+static void safely_assign_in_fifo(struct xillyusb_channel *chan,
+                                 struct xillyfifo *fifo)
+{
+       mutex_lock(&chan->lock);
+       chan->in_fifo = fifo;
+       mutex_unlock(&chan->lock);
+
+       flush_work(&chan->xdev->in_ep->workitem);
+}
+
+static void bulk_in_completer(struct urb *urb)
+{
+       struct xillybuffer *xb = urb->context;
+       struct xillyusb_endpoint *ep = xb->ep;
+       unsigned long flags;
+
+       if (urb->status) {
+               if (!(urb->status == -ENOENT ||
+                     urb->status == -ECONNRESET ||
+                     urb->status == -ESHUTDOWN))
+                       report_io_error(ep->xdev, -EIO);
+
+               spin_lock_irqsave(&ep->buffers_lock, flags);
+               list_add_tail(&xb->entry, &ep->buffers);
+               ep->outstanding_urbs--;
+               spin_unlock_irqrestore(&ep->buffers_lock, flags);
+
+               return;
+       }
+
+       xb->len = urb->actual_length;
+
+       spin_lock_irqsave(&ep->buffers_lock, flags);
+       list_add_tail(&xb->entry, &ep->filled_buffers);
+       spin_unlock_irqrestore(&ep->buffers_lock, flags);
+
+       if (!ep->shutting_down)
+               queue_work(ep->xdev->workq, &ep->workitem);
+}
+
+static void bulk_out_completer(struct urb *urb)
+{
+       struct xillybuffer *xb = urb->context;
+       struct xillyusb_endpoint *ep = xb->ep;
+       unsigned long flags;
+
+       if (urb->status &&
+           (!(urb->status == -ENOENT ||
+              urb->status == -ECONNRESET ||
+              urb->status == -ESHUTDOWN)))
+               report_io_error(ep->xdev, -EIO);
+
+       spin_lock_irqsave(&ep->buffers_lock, flags);
+       list_add_tail(&xb->entry, &ep->buffers);
+       ep->outstanding_urbs--;
+       spin_unlock_irqrestore(&ep->buffers_lock, flags);
+
+       if (!ep->shutting_down)
+               queue_work(ep->xdev->workq, &ep->workitem);
+}
+
+static void try_queue_bulk_in(struct xillyusb_endpoint *ep)
+{
+       struct xillyusb_dev *xdev = ep->xdev;
+       struct xillybuffer *xb;
+       struct urb *urb;
+
+       int rc;
+       unsigned long flags;
+       unsigned int bufsize = ep->buffer_size;
+
+       mutex_lock(&ep->ep_mutex);
+
+       if (ep->shutting_down || xdev->error)
+               goto done;
+
+       while (1) {
+               spin_lock_irqsave(&ep->buffers_lock, flags);
+
+               if (list_empty(&ep->buffers)) {
+                       spin_unlock_irqrestore(&ep->buffers_lock, flags);
+                       goto done;
+               }
+
+               xb = list_first_entry(&ep->buffers, struct xillybuffer, entry);
+               list_del(&xb->entry);
+               ep->outstanding_urbs++;
+
+               spin_unlock_irqrestore(&ep->buffers_lock, flags);
+
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!urb) {
+                       report_io_error(xdev, -ENOMEM);
+                       goto relist;
+               }
+
+               usb_fill_bulk_urb(urb, xdev->udev,
+                                 usb_rcvbulkpipe(xdev->udev, ep->ep_num),
+                                 xb->buf, bufsize, bulk_in_completer, xb);
+
+               usb_anchor_urb(urb, &ep->anchor);
+
+               rc = usb_submit_urb(urb, GFP_KERNEL);
+
+               if (rc) {
+                       report_io_error(xdev, (rc == -ENOMEM) ? -ENOMEM :
+                                       -EIO);
+                       goto unanchor;
+               }
+
+               usb_free_urb(urb); /* This just decrements reference count */
+       }
+
+unanchor:
+       usb_unanchor_urb(urb);
+       usb_free_urb(urb);
+
+relist:
+       spin_lock_irqsave(&ep->buffers_lock, flags);
+       list_add_tail(&xb->entry, &ep->buffers);
+       ep->outstanding_urbs--;
+       spin_unlock_irqrestore(&ep->buffers_lock, flags);
+
+done:
+       mutex_unlock(&ep->ep_mutex);
+}
+
+static void try_queue_bulk_out(struct xillyusb_endpoint *ep)
+{
+       struct xillyfifo *fifo = &ep->fifo;
+       struct xillyusb_dev *xdev = ep->xdev;
+       struct xillybuffer *xb;
+       struct urb *urb;
+
+       int rc;
+       unsigned int fill;
+       unsigned long flags;
+       bool do_wake = false;
+
+       mutex_lock(&ep->ep_mutex);
+
+       if (ep->shutting_down || xdev->error)
+               goto done;
+
+       fill = READ_ONCE(fifo->fill) & ep->fill_mask;
+
+       while (1) {
+               int count;
+               unsigned int max_read;
+
+               spin_lock_irqsave(&ep->buffers_lock, flags);
+
+               /*
+                * Race conditions might have the FIFO filled while the
+                * endpoint is marked as drained here. That doesn't matter,
+                * because the sole purpose of @drained is to ensure that
+                * certain data has been sent on the USB channel before
+                * shutting it down. Hence knowing that the FIFO appears
+                * to be empty with no outstanding URBs at some moment
+                * is good enough.
+                */
+
+               if (!fill) {
+                       ep->drained = !ep->outstanding_urbs;
+                       if (ep->drained && ep->wake_on_drain)
+                               do_wake = true;
+
+                       spin_unlock_irqrestore(&ep->buffers_lock, flags);
+                       goto done;
+               }
+
+               ep->drained = false;
+
+               if ((fill < ep->buffer_size && ep->outstanding_urbs) ||
+                   list_empty(&ep->buffers)) {
+                       spin_unlock_irqrestore(&ep->buffers_lock, flags);
+                       goto done;
+               }
+
+               xb = list_first_entry(&ep->buffers, struct xillybuffer, entry);
+               list_del(&xb->entry);
+               ep->outstanding_urbs++;
+
+               spin_unlock_irqrestore(&ep->buffers_lock, flags);
+
+               max_read = min(fill, ep->buffer_size);
+
+               count = fifo_read(&ep->fifo, xb->buf, max_read, xilly_memcpy);
+
+               /*
+                * xilly_memcpy always returns 0 => fifo_read can't fail =>
+                * count > 0
+                */
+
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!urb) {
+                       report_io_error(xdev, -ENOMEM);
+                       goto relist;
+               }
+
+               usb_fill_bulk_urb(urb, xdev->udev,
+                                 usb_sndbulkpipe(xdev->udev, ep->ep_num),
+                                 xb->buf, count, bulk_out_completer, xb);
+
+               usb_anchor_urb(urb, &ep->anchor);
+
+               rc = usb_submit_urb(urb, GFP_KERNEL);
+
+               if (rc) {
+                       report_io_error(xdev, (rc == -ENOMEM) ? -ENOMEM :
+                                       -EIO);
+                       goto unanchor;
+               }
+
+               usb_free_urb(urb); /* This just decrements reference count */
+
+               fill -= count;
+               do_wake = true;
+       }
+
+unanchor:
+       usb_unanchor_urb(urb);
+       usb_free_urb(urb);
+
+relist:
+       spin_lock_irqsave(&ep->buffers_lock, flags);
+       list_add_tail(&xb->entry, &ep->buffers);
+       ep->outstanding_urbs--;
+       spin_unlock_irqrestore(&ep->buffers_lock, flags);
+
+done:
+       mutex_unlock(&ep->ep_mutex);
+
+       if (do_wake)
+               wake_up_interruptible(&fifo->waitq);
+}
+
+static void bulk_out_work(struct work_struct *work)
+{
+       struct xillyusb_endpoint *ep = container_of(work,
+                                                   struct xillyusb_endpoint,
+                                                   workitem);
+       try_queue_bulk_out(ep);
+}
+
+static int process_in_opcode(struct xillyusb_dev *xdev,
+                            int opcode,
+                            int chan_num)
+{
+       struct xillyusb_channel *chan;
+       struct device *dev = xdev->dev;
+       int chan_idx = chan_num >> 1;
+
+       if (chan_idx >= xdev->num_channels) {
+               dev_err(dev, "Received illegal channel ID %d from FPGA\n",
+                       chan_num);
+               return -EIO;
+       }
+
+       chan = &xdev->channels[chan_idx];
+
+       switch (opcode) {
+       case OPCODE_EOF:
+               if (!chan->read_data_ok) {
+                       dev_err(dev, "Received unexpected EOF for channel %d\n",
+                               chan_num);
+                       return -EIO;
+               }
+
+               /*
+                * A write memory barrier ensures that the FIFO's fill level
+                * is visible before read_data_ok turns zero, so the data in
+                * the FIFO isn't missed by the consumer.
+                */
+               smp_wmb();
+               WRITE_ONCE(chan->read_data_ok, 0);
+               wake_up_interruptible(&chan->in_fifo->waitq);
+               break;
+
+       case OPCODE_REACHED_CHECKPOINT:
+               chan->flushing = 0;
+               wake_up_interruptible(&chan->flushq);
+               break;
+
+       case OPCODE_CANCELED_CHECKPOINT:
+               chan->canceled = 1;
+               wake_up_interruptible(&chan->flushq);
+               break;
+
+       default:
+               dev_err(dev, "Received illegal opcode %d from FPGA\n",
+                       opcode);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int process_bulk_in(struct xillybuffer *xb)
+{
+       struct xillyusb_endpoint *ep = xb->ep;
+       struct xillyusb_dev *xdev = ep->xdev;
+       struct device *dev = xdev->dev;
+       int dws = xb->len >> 2;
+       __le32 *p = xb->buf;
+       u32 ctrlword;
+       struct xillyusb_channel *chan;
+       struct xillyfifo *fifo;
+       int chan_num = 0, opcode;
+       int chan_idx;
+       int bytes, count, dwconsume;
+       int in_bytes_left = 0;
+       int rc;
+
+       if ((dws << 2) != xb->len) {
+               dev_err(dev, "Received BULK IN transfer with %d bytes, not a multiple of 4\n",
+                       xb->len);
+               return -EIO;
+       }
+
+       if (xdev->in_bytes_left) {
+               bytes = min(xdev->in_bytes_left, dws << 2);
+               in_bytes_left = xdev->in_bytes_left - bytes;
+               chan_num = xdev->leftover_chan_num;
+               goto resume_leftovers;
+       }
+
+       while (dws) {
+               ctrlword = le32_to_cpu(*p++);
+               dws--;
+
+               chan_num = ctrlword & 0xfff;
+               count = (ctrlword >> 12) & 0x3ff;
+               opcode = (ctrlword >> 24) & 0xf;
+
+               if (opcode != OPCODE_DATA) {
+                       unsigned int in_counter = xdev->in_counter++ & 0x3ff;
+
+                       if (count != in_counter) {
+                               dev_err(dev, "Expected opcode counter %d, got %d\n",
+                                       in_counter, count);
+                               return -EIO;
+                       }
+
+                       rc = process_in_opcode(xdev, opcode, chan_num);
+
+                       if (rc)
+                               return rc;
+
+                       continue;
+               }
+
+               bytes = min(count + 1, dws << 2);
+               in_bytes_left = count + 1 - bytes;
+
+resume_leftovers:
+               chan_idx = chan_num >> 1;
+
+               if (!(chan_num & 1) || chan_idx >= xdev->num_channels ||
+                   !xdev->channels[chan_idx].read_data_ok) {
+                       dev_err(dev, "Received illegal channel ID %d from FPGA\n",
+                               chan_num);
+                       return -EIO;
+               }
+               chan = &xdev->channels[chan_idx];
+
+               fifo = chan->in_fifo;
+
+               if (unlikely(!fifo))
+                       return -EIO; /* We got really unexpected data */
+
+               if (bytes != fifo_write(fifo, p, bytes, xilly_memcpy)) {
+                       dev_err(dev, "Misbehaving FPGA overflowed an upstream FIFO!\n");
+                       return -EIO;
+               }
+
+               wake_up_interruptible(&fifo->waitq);
+
+               dwconsume = (bytes + 3) >> 2;
+               dws -= dwconsume;
+               p += dwconsume;
+       }
+
+       xdev->in_bytes_left = in_bytes_left;
+       xdev->leftover_chan_num = chan_num;
+       return 0;
+}
+
+static void bulk_in_work(struct work_struct *work)
+{
+       struct xillyusb_endpoint *ep =
+               container_of(work, struct xillyusb_endpoint, workitem);
+       struct xillyusb_dev *xdev = ep->xdev;
+       unsigned long flags;
+       struct xillybuffer *xb;
+       bool consumed = false;
+       int rc = 0;
+
+       mutex_lock(&xdev->process_in_mutex);
+
+       spin_lock_irqsave(&ep->buffers_lock, flags);
+
+       while (1) {
+               if (rc || list_empty(&ep->filled_buffers)) {
+                       spin_unlock_irqrestore(&ep->buffers_lock, flags);
+                       mutex_unlock(&xdev->process_in_mutex);
+
+                       if (rc)
+                               report_io_error(xdev, rc);
+                       else if (consumed)
+                               try_queue_bulk_in(ep);
+
+                       return;
+               }
+
+               xb = list_first_entry(&ep->filled_buffers, struct xillybuffer,
+                                     entry);
+               list_del(&xb->entry);
+
+               spin_unlock_irqrestore(&ep->buffers_lock, flags);
+
+               consumed = true;
+
+               if (!xdev->error)
+                       rc = process_bulk_in(xb);
+
+               spin_lock_irqsave(&ep->buffers_lock, flags);
+               list_add_tail(&xb->entry, &ep->buffers);
+               ep->outstanding_urbs--;
+       }
+}
+
+static int xillyusb_send_opcode(struct xillyusb_dev *xdev,
+                               int chan_num, char opcode, u32 data)
+{
+       struct xillyusb_endpoint *ep = xdev->msg_ep;
+       struct xillyfifo *fifo = &ep->fifo;
+       __le32 msg[2];
+
+       int rc = 0;
+
+       msg[0] = cpu_to_le32((chan_num & 0xfff) |
+                            ((opcode & 0xf) << 24));
+       msg[1] = cpu_to_le32(data);
+
+       mutex_lock(&xdev->msg_mutex);
+
+       /*
+        * The wait queue is woken with the interruptible variant, so the
+        * wait function matches, however returning because of an interrupt
+        * will mess things up considerably, in particular when the caller is
+        * the release method. And the xdev->error part prevents being stuck
+        * forever in the event of a bizarre hardware bug: Pull the USB plug.
+        */
+
+       while (wait_event_interruptible(fifo->waitq,
+                                       fifo->fill <= (fifo->size - 8) ||
+                                       xdev->error))
+               ; /* Empty loop */
+
+       if (xdev->error) {
+               rc = xdev->error;
+               goto unlock_done;
+       }
+
+       fifo_write(fifo, (void *)msg, 8, xilly_memcpy);
+
+       try_queue_bulk_out(ep);
+
+unlock_done:
+       mutex_unlock(&xdev->msg_mutex);
+
+       return rc;
+}
+
+/*
+ * Note that flush_downstream() merely waits for the data to arrive to
+ * the application logic at the FPGA -- unlike PCIe Xillybus' counterpart,
+ * it does nothing to make it happen (and neither is it necessary).
+ *
+ * This function is not reentrant for the same @chan, but this is covered
+ * by the fact that for any given @chan, it's called either by the open,
+ * write, llseek and flush fops methods, which can't run in parallel (and the
+ * write + flush and llseek method handlers are protected with out_mutex).
+ *
+ * chan->flushed is there to avoid multiple flushes at the same position,
+ * in particular as a result of programs that close the file descriptor
+ * e.g. after a dup2() for redirection.
+ */
+
+static int flush_downstream(struct xillyusb_channel *chan,
+                           long timeout,
+                           bool interruptible)
+{
+       struct xillyusb_dev *xdev = chan->xdev;
+       int chan_num = chan->chan_idx << 1;
+       long deadline, left_to_sleep;
+       int rc;
+
+       if (chan->flushed)
+               return 0;
+
+       deadline = jiffies + 1 + timeout;
+
+       if (chan->flushing) {
+               long cancel_deadline = jiffies + 1 + XILLY_RESPONSE_TIMEOUT;
+
+               chan->canceled = 0;
+               rc = xillyusb_send_opcode(xdev, chan_num,
+                                         OPCODE_CANCEL_CHECKPOINT, 0);
+
+               if (rc)
+                       return rc; /* Only real error, never -EINTR */
+
+               /* Ignoring interrupts. Cancellation must be handled */
+               while (!chan->canceled) {
+                       left_to_sleep = cancel_deadline - ((long)jiffies);
+
+                       if (left_to_sleep <= 0) {
+                               report_io_error(xdev, -EIO);
+                               return -EIO;
+                       }
+
+                       rc = wait_event_interruptible_timeout(chan->flushq,
+                                                             chan->canceled ||
+                                                             xdev->error,
+                                                             left_to_sleep);
+
+                       if (xdev->error)
+                               return xdev->error;
+               }
+       }
+
+       chan->flushing = 1;
+
+       /*
+        * The checkpoint is given in terms of data elements, not bytes. As
+        * a result, if less than an element's worth of data is stored in the
+        * FIFO, it's not flushed, including the flush before closing, which
+        * means that such data is lost. This is consistent with PCIe Xillybus.
+        */
+
+       rc = xillyusb_send_opcode(xdev, chan_num,
+                                 OPCODE_SET_CHECKPOINT,
+                                 chan->out_bytes >>
+                                 chan->out_log2_element_size);
+
+       if (rc)
+               return rc; /* Only real error, never -EINTR */
+
+       if (!timeout) {
+               while (chan->flushing) {
+                       rc = wait_event_interruptible(chan->flushq,
+                                                     !chan->flushing ||
+                                                     xdev->error);
+                       if (xdev->error)
+                               return xdev->error;
+
+                       if (interruptible && rc)
+                               return -EINTR;
+               }
+
+               goto done;
+       }
+
+       while (chan->flushing) {
+               left_to_sleep = deadline - ((long)jiffies);
+
+               if (left_to_sleep <= 0)
+                       return -ETIMEDOUT;
+
+               rc = wait_event_interruptible_timeout(chan->flushq,
+                                                     !chan->flushing ||
+                                                     xdev->error,
+                                                     left_to_sleep);
+
+               if (xdev->error)
+                       return xdev->error;
+
+               if (interruptible && rc < 0)
+                       return -EINTR;
+       }
+
+done:
+       chan->flushed = 1;
+       return 0;
+}
+
+/* request_read_anything(): Ask the FPGA for any little amount of data */
+static int request_read_anything(struct xillyusb_channel *chan,
+                                char opcode)
+{
+       struct xillyusb_dev *xdev = chan->xdev;
+       unsigned int sh = chan->in_log2_element_size;
+       int chan_num = (chan->chan_idx << 1) | 1;
+       u32 mercy = chan->in_consumed_bytes + (2 << sh) - 1;
+
+       return xillyusb_send_opcode(xdev, chan_num, opcode, mercy >> sh);
+}
+
+static int xillyusb_open(struct inode *inode, struct file *filp)
+{
+       struct xillyusb_dev *xdev;
+       struct xillyusb_channel *chan;
+       struct xillyfifo *in_fifo = NULL;
+       struct xillyusb_endpoint *out_ep = NULL;
+       int rc;
+       int index;
+
+       rc = xillybus_find_inode(inode, (void **)&xdev, &index);
+       if (rc)
+               return rc;
+
+       chan = &xdev->channels[index];
+       filp->private_data = chan;
+
+       mutex_lock(&chan->lock);
+
+       rc = -ENODEV;
+
+       if (xdev->error)
+               goto unmutex_fail;
+
+       if (((filp->f_mode & FMODE_READ) && !chan->readable) ||
+           ((filp->f_mode & FMODE_WRITE) && !chan->writable))
+               goto unmutex_fail;
+
+       if ((filp->f_flags & O_NONBLOCK) && (filp->f_mode & FMODE_READ) &&
+           chan->in_synchronous) {
+               dev_err(xdev->dev,
+                       "open() failed: O_NONBLOCK not allowed for read on this device\n");
+               goto unmutex_fail;
+       }
+
+       if ((filp->f_flags & O_NONBLOCK) && (filp->f_mode & FMODE_WRITE) &&
+           chan->out_synchronous) {
+               dev_err(xdev->dev,
+                       "open() failed: O_NONBLOCK not allowed for write on this device\n");
+               goto unmutex_fail;
+       }
+
+       rc = -EBUSY;
+
+       if (((filp->f_mode & FMODE_READ) && chan->open_for_read) ||
+           ((filp->f_mode & FMODE_WRITE) && chan->open_for_write))
+               goto unmutex_fail;
+
+       kref_get(&xdev->kref);
+
+       if (filp->f_mode & FMODE_READ)
+               chan->open_for_read = 1;
+
+       if (filp->f_mode & FMODE_WRITE)
+               chan->open_for_write = 1;
+
+       mutex_unlock(&chan->lock);
+
+       if (filp->f_mode & FMODE_WRITE) {
+               out_ep = endpoint_alloc(xdev,
+                                       (chan->chan_idx + 2) | USB_DIR_OUT,
+                                       bulk_out_work, BUF_SIZE_ORDER, BUFNUM);
+
+               if (!out_ep) {
+                       rc = -ENOMEM;
+                       goto unopen;
+               }
+
+               rc = fifo_init(&out_ep->fifo, chan->out_log2_fifo_size);
+
+               if (rc)
+                       goto late_unopen;
+
+               out_ep->fill_mask = -(1 << chan->out_log2_element_size);
+               chan->out_bytes = 0;
+               chan->flushed = 0;
+
+               /*
+                * Sending a flush request to a previously closed stream
+                * effectively opens it, and also waits until the command is
+                * confirmed by the FPGA. The latter is necessary because the
+                * data is sent through a separate BULK OUT endpoint, and the
+                * xHCI controller is free to reorder transmissions.
+                *
+                * This can't go wrong unless there's a serious hardware error
+                * (or the computer is stuck for 500 ms?)
+                */
+               rc = flush_downstream(chan, XILLY_RESPONSE_TIMEOUT, false);
+
+               if (rc == -ETIMEDOUT) {
+                       rc = -EIO;
+                       report_io_error(xdev, rc);
+               }
+
+               if (rc)
+                       goto late_unopen;
+       }
+
+       if (filp->f_mode & FMODE_READ) {
+               in_fifo = kzalloc(sizeof(*in_fifo), GFP_KERNEL);
+
+               if (!in_fifo) {
+                       rc = -ENOMEM;
+                       goto late_unopen;
+               }
+
+               rc = fifo_init(in_fifo, chan->in_log2_fifo_size);
+
+               if (rc) {
+                       kfree(in_fifo);
+                       goto late_unopen;
+               }
+       }
+
+       mutex_lock(&chan->lock);
+       if (in_fifo) {
+               chan->in_fifo = in_fifo;
+               chan->read_data_ok = 1;
+       }
+       if (out_ep)
+               chan->out_ep = out_ep;
+       mutex_unlock(&chan->lock);
+
+       if (in_fifo) {
+               u32 in_checkpoint = 0;
+
+               if (!chan->in_synchronous)
+                       in_checkpoint = in_fifo->size >>
+                               chan->in_log2_element_size;
+
+               chan->in_consumed_bytes = 0;
+               chan->poll_used = 0;
+               chan->in_current_checkpoint = in_checkpoint;
+               rc = xillyusb_send_opcode(xdev, (chan->chan_idx << 1) | 1,
+                                         OPCODE_SET_CHECKPOINT,
+                                         in_checkpoint);
+
+               if (rc) /* Failure guarantees that opcode wasn't sent */
+                       goto unfifo;
+
+               /*
+                * In non-blocking mode, request the FPGA to send any data it
+                * has right away. Otherwise, the first read() will always
+                * return -EAGAIN, which is OK strictly speaking, but ugly.
+                * Checking and unrolling if this fails isn't worth the
+                * effort -- the error is propagated to the first read()
+                * anyhow.
+                */
+               if (filp->f_flags & O_NONBLOCK)
+                       request_read_anything(chan, OPCODE_SET_PUSH);
+       }
+
+       return 0;
+
+unfifo:
+       chan->read_data_ok = 0;
+       safely_assign_in_fifo(chan, NULL);
+       fifo_mem_release(in_fifo);
+       kfree(in_fifo);
+
+       if (out_ep) {
+               mutex_lock(&chan->lock);
+               chan->out_ep = NULL;
+               mutex_unlock(&chan->lock);
+       }
+
+late_unopen:
+       if (out_ep)
+               endpoint_dealloc(out_ep);
+
+unopen:
+       mutex_lock(&chan->lock);
+
+       if (filp->f_mode & FMODE_READ)
+               chan->open_for_read = 0;
+
+       if (filp->f_mode & FMODE_WRITE)
+               chan->open_for_write = 0;
+
+       mutex_unlock(&chan->lock);
+
+       kref_put(&xdev->kref, cleanup_dev);
+
+       return rc;
+
+unmutex_fail:
+       mutex_unlock(&chan->lock);
+       return rc;
+}
+
+static ssize_t xillyusb_read(struct file *filp, char __user *userbuf,
+                            size_t count, loff_t *f_pos)
+{
+       struct xillyusb_channel *chan = filp->private_data;
+       struct xillyusb_dev *xdev = chan->xdev;
+       struct xillyfifo *fifo = chan->in_fifo;
+       int chan_num = (chan->chan_idx << 1) | 1;
+
+       long deadline, left_to_sleep;
+       int bytes_done = 0;
+       bool sent_set_push = false;
+       int rc;
+
+       deadline = jiffies + 1 + XILLY_RX_TIMEOUT;
+
+       rc = mutex_lock_interruptible(&chan->in_mutex);
+
+       if (rc)
+               return rc;
+
+       while (1) {
+               u32 fifo_checkpoint_bytes, complete_checkpoint_bytes;
+               u32 complete_checkpoint, fifo_checkpoint;
+               u32 checkpoint;
+               s32 diff, leap;
+               unsigned int sh = chan->in_log2_element_size;
+               bool checkpoint_for_complete;
+
+               rc = fifo_read(fifo, (__force void *)userbuf + bytes_done,
+                              count - bytes_done, xilly_copy_to_user);
+
+               if (rc < 0)
+                       break;
+
+               bytes_done += rc;
+               chan->in_consumed_bytes += rc;
+
+               left_to_sleep = deadline - ((long)jiffies);
+
+               /*
+                * Some 32-bit arithmetic that may wrap. Note that
+                * complete_checkpoint is rounded up to the closest element
+                * boundary, because the read() can't be completed otherwise.
+                * fifo_checkpoint_bytes is rounded down, because it protects
+                * in_fifo from overflowing.
+                */
+
+               fifo_checkpoint_bytes = chan->in_consumed_bytes + fifo->size;
+               complete_checkpoint_bytes =
+                       chan->in_consumed_bytes + count - bytes_done;
+
+               fifo_checkpoint = fifo_checkpoint_bytes >> sh;
+               complete_checkpoint =
+                       (complete_checkpoint_bytes + (1 << sh) - 1) >> sh;
+
+               diff = (fifo_checkpoint - complete_checkpoint) << sh;
+
+               if (chan->in_synchronous && diff >= 0) {
+                       checkpoint = complete_checkpoint;
+                       checkpoint_for_complete = true;
+               } else {
+                       checkpoint = fifo_checkpoint;
+                       checkpoint_for_complete = false;
+               }
+
+               leap = (checkpoint - chan->in_current_checkpoint) << sh;
+
+               /*
+                * To prevent flooding of OPCODE_SET_CHECKPOINT commands as
+                * data is consumed, it's issued only if it moves the
+                * checkpoint by at least an 8th of the FIFO's size, or if
+                * it's necessary to complete the number of bytes requested by
+                * the read() call.
+                *
+                * chan->read_data_ok is checked to spare an unnecessary
+                * submission after receiving EOF, however it's harmless if
+                * such slips away.
+                */
+
+               if (chan->read_data_ok &&
+                   (leap > (fifo->size >> 3) ||
+                    (checkpoint_for_complete && leap > 0))) {
+                       chan->in_current_checkpoint = checkpoint;
+                       rc = xillyusb_send_opcode(xdev, chan_num,
+                                                 OPCODE_SET_CHECKPOINT,
+                                                 checkpoint);
+
+                       if (rc)
+                               break;
+               }
+
+               if (bytes_done == count ||
+                   (left_to_sleep <= 0 && bytes_done))
+                       break;
+
+               /*
+                * Reaching here means that the FIFO was empty when
+                * fifo_read() returned, but not necessarily right now. Error
+                * and EOF are checked and reported only now, so that no data
+                * that managed its way to the FIFO is lost.
+                */
+
+               if (!READ_ONCE(chan->read_data_ok)) { /* FPGA has sent EOF */
+                       /* Has data slipped into the FIFO since fifo_read()? */
+                       smp_rmb();
+                       if (READ_ONCE(fifo->fill))
+                               continue;
+
+                       rc = 0;
+                       break;
+               }
+
+               if (xdev->error) {
+                       rc = xdev->error;
+                       break;
+               }
+
+               if (filp->f_flags & O_NONBLOCK) {
+                       rc = -EAGAIN;
+                       break;
+               }
+
+               if (!sent_set_push) {
+                       rc = xillyusb_send_opcode(xdev, chan_num,
+                                                 OPCODE_SET_PUSH,
+                                                 complete_checkpoint);
+
+                       if (rc)
+                               break;
+
+                       sent_set_push = true;
+               }
+
+               if (left_to_sleep > 0) {
+                       /*
+                        * Note that when xdev->error is set (e.g. when the
+                        * device is unplugged), read_data_ok turns zero and
+                        * fifo->waitq is awaken.
+                        * Therefore no special attention to xdev->error.
+                        */
+
+                       rc = wait_event_interruptible_timeout
+                               (fifo->waitq,
+                                fifo->fill || !chan->read_data_ok,
+                                left_to_sleep);
+               } else { /* bytes_done == 0 */
+                       /* Tell FPGA to send anything it has */
+                       rc = request_read_anything(chan, OPCODE_UPDATE_PUSH);
+
+                       if (rc)
+                               break;
+
+                       rc = wait_event_interruptible
+                               (fifo->waitq,
+                                fifo->fill || !chan->read_data_ok);
+               }
+
+               if (rc < 0) {
+                       rc = -EINTR;
+                       break;
+               }
+       }
+
+       if (((filp->f_flags & O_NONBLOCK) || chan->poll_used) &&
+           !READ_ONCE(fifo->fill))
+               request_read_anything(chan, OPCODE_SET_PUSH);
+
+       mutex_unlock(&chan->in_mutex);
+
+       if (bytes_done)
+               return bytes_done;
+
+       return rc;
+}
+
+static int xillyusb_flush(struct file *filp, fl_owner_t id)
+{
+       struct xillyusb_channel *chan = filp->private_data;
+       int rc;
+
+       if (!(filp->f_mode & FMODE_WRITE))
+               return 0;
+
+       rc = mutex_lock_interruptible(&chan->out_mutex);
+
+       if (rc)
+               return rc;
+
+       /*
+        * One second's timeout on flushing. Interrupts are ignored, because if
+        * the user pressed CTRL-C, that interrupt will still be in flight by
+        * the time we reach here, and the opportunity to flush is lost.
+        */
+       rc = flush_downstream(chan, HZ, false);
+
+       mutex_unlock(&chan->out_mutex);
+
+       if (rc == -ETIMEDOUT) {
+               /* The things you do to use dev_warn() and not pr_warn() */
+               struct xillyusb_dev *xdev = chan->xdev;
+
+               mutex_lock(&chan->lock);
+               if (!xdev->error)
+                       dev_warn(xdev->dev,
+                                "Timed out while flushing. Output data may be lost.\n");
+               mutex_unlock(&chan->lock);
+       }
+
+       return rc;
+}
+
+static ssize_t xillyusb_write(struct file *filp, const char __user *userbuf,
+                             size_t count, loff_t *f_pos)
+{
+       struct xillyusb_channel *chan = filp->private_data;
+       struct xillyusb_dev *xdev = chan->xdev;
+       struct xillyfifo *fifo = &chan->out_ep->fifo;
+       int rc;
+
+       rc = mutex_lock_interruptible(&chan->out_mutex);
+
+       if (rc)
+               return rc;
+
+       while (1) {
+               if (xdev->error) {
+                       rc = xdev->error;
+                       break;
+               }
+
+               if (count == 0)
+                       break;
+
+               rc = fifo_write(fifo, (__force void *)userbuf, count,
+                               xilly_copy_from_user);
+
+               if (rc != 0)
+                       break;
+
+               if (filp->f_flags & O_NONBLOCK) {
+                       rc = -EAGAIN;
+                       break;
+               }
+
+               if (wait_event_interruptible
+                   (fifo->waitq,
+                    fifo->fill != fifo->size || xdev->error)) {
+                       rc = -EINTR;
+                       break;
+               }
+       }
+
+       if (rc < 0)
+               goto done;
+
+       chan->out_bytes += rc;
+
+       if (rc) {
+               try_queue_bulk_out(chan->out_ep);
+               chan->flushed = 0;
+       }
+
+       if (chan->out_synchronous) {
+               int flush_rc = flush_downstream(chan, 0, true);
+
+               if (flush_rc && !rc)
+                       rc = flush_rc;
+       }
+
+done:
+       mutex_unlock(&chan->out_mutex);
+
+       return rc;
+}
+
+static int xillyusb_release(struct inode *inode, struct file *filp)
+{
+       struct xillyusb_channel *chan = filp->private_data;
+       struct xillyusb_dev *xdev = chan->xdev;
+       int rc_read = 0, rc_write = 0;
+
+       if (filp->f_mode & FMODE_READ) {
+               struct xillyfifo *in_fifo = chan->in_fifo;
+
+               rc_read = xillyusb_send_opcode(xdev, (chan->chan_idx << 1) | 1,
+                                              OPCODE_CLOSE, 0);
+               /*
+                * If rc_read is nonzero, xdev->error indicates a global
+                * device error. The error is reported later, so that
+                * resources are freed.
+                *
+                * Looping on wait_event_interruptible() kinda breaks the idea
+                * of being interruptible, and this should have been
+                * wait_event(). Only it's being waken with
+                * wake_up_interruptible() for the sake of other uses. If
+                * there's a global device error, chan->read_data_ok is
+                * deasserted and the wait queue is awaken, so this is covered.
+                */
+
+               while (wait_event_interruptible(in_fifo->waitq,
+                                               !chan->read_data_ok))
+                       ; /* Empty loop */
+
+               safely_assign_in_fifo(chan, NULL);
+               fifo_mem_release(in_fifo);
+               kfree(in_fifo);
+
+               mutex_lock(&chan->lock);
+               chan->open_for_read = 0;
+               mutex_unlock(&chan->lock);
+       }
+
+       if (filp->f_mode & FMODE_WRITE) {
+               struct xillyusb_endpoint *ep = chan->out_ep;
+               /*
+                * chan->flushing isn't zeroed. If the pre-release flush timed
+                * out, a cancel request will be sent before the next
+                * OPCODE_SET_CHECKPOINT (i.e. when the file is opened again).
+                * This is despite that the FPGA forgets about the checkpoint
+                * request as the file closes. Still, in an exceptional race
+                * condition, the FPGA could send an OPCODE_REACHED_CHECKPOINT
+                * just before closing that would reach the host after the
+                * file has re-opened.
+                */
+
+               mutex_lock(&chan->lock);
+               chan->out_ep = NULL;
+               mutex_unlock(&chan->lock);
+
+               endpoint_quiesce(ep);
+               endpoint_dealloc(ep);
+
+               /* See comments on rc_read above */
+               rc_write = xillyusb_send_opcode(xdev, chan->chan_idx << 1,
+                                               OPCODE_CLOSE, 0);
+
+               mutex_lock(&chan->lock);
+               chan->open_for_write = 0;
+               mutex_unlock(&chan->lock);
+       }
+
+       kref_put(&xdev->kref, cleanup_dev);
+
+       return rc_read ? rc_read : rc_write;
+}
+
+/*
+ * Xillybus' API allows device nodes to be seekable, giving the user
+ * application access to a RAM array on the FPGA (or logic emulating it).
+ */
+
+static loff_t xillyusb_llseek(struct file *filp, loff_t offset, int whence)
+{
+       struct xillyusb_channel *chan = filp->private_data;
+       struct xillyusb_dev *xdev = chan->xdev;
+       loff_t pos = filp->f_pos;
+       int rc = 0;
+       unsigned int log2_element_size = chan->readable ?
+               chan->in_log2_element_size : chan->out_log2_element_size;
+
+       /*
+        * Take both mutexes not allowing interrupts, since it seems like
+        * common applications don't expect an -EINTR here. Besides, multiple
+        * access to a single file descriptor on seekable devices is a mess
+        * anyhow.
+        */
+
+       mutex_lock(&chan->out_mutex);
+       mutex_lock(&chan->in_mutex);
+
+       switch (whence) {
+       case SEEK_SET:
+               pos = offset;
+               break;
+       case SEEK_CUR:
+               pos += offset;
+               break;
+       case SEEK_END:
+               pos = offset; /* Going to the end => to the beginning */
+               break;
+       default:
+               rc = -EINVAL;
+               goto end;
+       }
+
+       /* In any case, we must finish on an element boundary */
+       if (pos & ((1 << log2_element_size) - 1)) {
+               rc = -EINVAL;
+               goto end;
+       }
+
+       rc = xillyusb_send_opcode(xdev, chan->chan_idx << 1,
+                                 OPCODE_SET_ADDR,
+                                 pos >> log2_element_size);
+
+       if (rc)
+               goto end;
+
+       if (chan->writable) {
+               chan->flushed = 0;
+               rc = flush_downstream(chan, HZ, false);
+       }
+
+end:
+       mutex_unlock(&chan->out_mutex);
+       mutex_unlock(&chan->in_mutex);
+
+       if (rc) /* Return error after releasing mutexes */
+               return rc;
+
+       filp->f_pos = pos;
+
+       return pos;
+}
+
+static __poll_t xillyusb_poll(struct file *filp, poll_table *wait)
+{
+       struct xillyusb_channel *chan = filp->private_data;
+       __poll_t mask = 0;
+
+       if (chan->in_fifo)
+               poll_wait(filp, &chan->in_fifo->waitq, wait);
+
+       if (chan->out_ep)
+               poll_wait(filp, &chan->out_ep->fifo.waitq, wait);
+
+       /*
+        * If this is the first time poll() is called, and the file is
+        * readable, set the relevant flag. Also tell the FPGA to send all it
+        * has, to kickstart the mechanism that ensures there's always some
+        * data in in_fifo unless the stream is dry end-to-end. Note that the
+        * first poll() may not return a EPOLLIN, even if there's data on the
+        * FPGA. Rather, the data will arrive soon, and trigger the relevant
+        * wait queue.
+        */
+
+       if (!chan->poll_used && chan->in_fifo) {
+               chan->poll_used = 1;
+               request_read_anything(chan, OPCODE_SET_PUSH);
+       }
+
+       /*
+        * poll() won't play ball regarding read() channels which
+        * are synchronous. Allowing that will create situations where data has
+        * been delivered at the FPGA, and users expecting select() to wake up,
+        * which it may not. So make it never work.
+        */
+
+       if (chan->in_fifo && !chan->in_synchronous &&
+           (READ_ONCE(chan->in_fifo->fill) || !chan->read_data_ok))
+               mask |= EPOLLIN | EPOLLRDNORM;
+
+       if (chan->out_ep &&
+           (READ_ONCE(chan->out_ep->fifo.fill) != chan->out_ep->fifo.size))
+               mask |= EPOLLOUT | EPOLLWRNORM;
+
+       if (chan->xdev->error)
+               mask |= EPOLLERR;
+
+       return mask;
+}
+
+static const struct file_operations xillyusb_fops = {
+       .owner      = THIS_MODULE,
+       .read       = xillyusb_read,
+       .write      = xillyusb_write,
+       .open       = xillyusb_open,
+       .flush      = xillyusb_flush,
+       .release    = xillyusb_release,
+       .llseek     = xillyusb_llseek,
+       .poll       = xillyusb_poll,
+};
+
+static int xillyusb_setup_base_eps(struct xillyusb_dev *xdev)
+{
+       xdev->msg_ep = endpoint_alloc(xdev, MSG_EP_NUM | USB_DIR_OUT,
+                                     bulk_out_work, 1, 2);
+       if (!xdev->msg_ep)
+               return -ENOMEM;
+
+       if (fifo_init(&xdev->msg_ep->fifo, 13)) /* 8 kiB */
+               goto dealloc;
+
+       xdev->msg_ep->fill_mask = -8; /* 8 bytes granularity */
+
+       xdev->in_ep = endpoint_alloc(xdev, IN_EP_NUM | USB_DIR_IN,
+                                    bulk_in_work, BUF_SIZE_ORDER, BUFNUM);
+       if (!xdev->in_ep)
+               goto dealloc;
+
+       try_queue_bulk_in(xdev->in_ep);
+
+       return 0;
+
+dealloc:
+       endpoint_dealloc(xdev->msg_ep); /* Also frees FIFO mem if allocated */
+       return -ENOMEM;
+}
+
+static int setup_channels(struct xillyusb_dev *xdev,
+                         __le16 *chandesc,
+                         int num_channels)
+{
+       struct xillyusb_channel *chan;
+       int i;
+
+       chan = kcalloc(num_channels, sizeof(*chan), GFP_KERNEL);
+       if (!chan)
+               return -ENOMEM;
+
+       xdev->channels = chan;
+
+       for (i = 0; i < num_channels; i++, chan++) {
+               unsigned int in_desc = le16_to_cpu(*chandesc++);
+               unsigned int out_desc = le16_to_cpu(*chandesc++);
+
+               chan->xdev = xdev;
+               mutex_init(&chan->in_mutex);
+               mutex_init(&chan->out_mutex);
+               mutex_init(&chan->lock);
+               init_waitqueue_head(&chan->flushq);
+
+               chan->chan_idx = i;
+
+               if (in_desc & 0x80) { /* Entry is valid */
+                       chan->readable = 1;
+                       chan->in_synchronous = !!(in_desc & 0x40);
+                       chan->in_seekable = !!(in_desc & 0x20);
+                       chan->in_log2_element_size = in_desc & 0x0f;
+                       chan->in_log2_fifo_size = ((in_desc >> 8) & 0x1f) + 16;
+               }
+
+               /*
+                * A downstream channel should never exist above index 13,
+                * as it would request a nonexistent BULK endpoint > 15.
+                * In the peculiar case that it does, it's ignored silently.
+                */
+
+               if ((out_desc & 0x80) && i < 14) { /* Entry is valid */
+                       chan->writable = 1;
+                       chan->out_synchronous = !!(out_desc & 0x40);
+                       chan->out_seekable = !!(out_desc & 0x20);
+                       chan->out_log2_element_size = out_desc & 0x0f;
+                       chan->out_log2_fifo_size =
+                               ((out_desc >> 8) & 0x1f) + 16;
+               }
+       }
+
+       return 0;
+}
+
+static int xillyusb_discovery(struct usb_interface *interface)
+{
+       int rc;
+       struct xillyusb_dev *xdev = usb_get_intfdata(interface);
+       __le16 bogus_chandesc[2];
+       struct xillyfifo idt_fifo;
+       struct xillyusb_channel *chan;
+       unsigned int idt_len, names_offset;
+       unsigned char *idt;
+       int num_channels;
+
+       rc = xillyusb_send_opcode(xdev, ~0, OPCODE_QUIESCE, 0);
+
+       if (rc) {
+               dev_err(&interface->dev, "Failed to send quiesce request. Aborting.\n");
+               return rc;
+       }
+
+       /* Phase I: Set up one fake upstream channel and obtain IDT */
+
+       /* Set up a fake IDT with one async IN stream */
+       bogus_chandesc[0] = cpu_to_le16(0x80);
+       bogus_chandesc[1] = cpu_to_le16(0);
+
+       rc = setup_channels(xdev, bogus_chandesc, 1);
+
+       if (rc)
+               return rc;
+
+       rc = fifo_init(&idt_fifo, LOG2_IDT_FIFO_SIZE);
+
+       if (rc)
+               return rc;
+
+       chan = xdev->channels;
+
+       chan->in_fifo = &idt_fifo;
+       chan->read_data_ok = 1;
+
+       xdev->num_channels = 1;
+
+       rc = xillyusb_send_opcode(xdev, ~0, OPCODE_REQ_IDT, 0);
+
+       if (rc) {
+               dev_err(&interface->dev, "Failed to send IDT request. Aborting.\n");
+               goto unfifo;
+       }
+
+       rc = wait_event_interruptible_timeout(idt_fifo.waitq,
+                                             !chan->read_data_ok,
+                                             XILLY_RESPONSE_TIMEOUT);
+
+       if (xdev->error) {
+               rc = xdev->error;
+               goto unfifo;
+       }
+
+       if (rc < 0) {
+               rc = -EINTR; /* Interrupt on probe method? Interesting. */
+               goto unfifo;
+       }
+
+       if (chan->read_data_ok) {
+               rc = -ETIMEDOUT;
+               dev_err(&interface->dev, "No response from FPGA. Aborting.\n");
+               goto unfifo;
+       }
+
+       idt_len = READ_ONCE(idt_fifo.fill);
+       idt = kmalloc(idt_len, GFP_KERNEL);
+
+       if (!idt) {
+               rc = -ENOMEM;
+               goto unfifo;
+       }
+
+       fifo_read(&idt_fifo, idt, idt_len, xilly_memcpy);
+
+       if (crc32_le(~0, idt, idt_len) != 0) {
+               dev_err(&interface->dev, "IDT failed CRC check. Aborting.\n");
+               rc = -ENODEV;
+               goto unidt;
+       }
+
+       if (*idt > 0x90) {
+               dev_err(&interface->dev, "No support for IDT version 0x%02x. Maybe the xillyusb driver needs an upgrade. Aborting.\n",
+                       (int)*idt);
+               rc = -ENODEV;
+               goto unidt;
+       }
+
+       /* Phase II: Set up the streams as defined in IDT */
+
+       num_channels = le16_to_cpu(*((__le16 *)(idt + 1)));
+       names_offset = 3 + num_channels * 4;
+       idt_len -= 4; /* Exclude CRC */
+
+       if (idt_len < names_offset) {
+               dev_err(&interface->dev, "IDT too short. This is exceptionally weird, because its CRC is OK\n");
+               rc = -ENODEV;
+               goto unidt;
+       }
+
+       rc = setup_channels(xdev, (void *)idt + 3, num_channels);
+
+       if (rc)
+               goto unidt;
+
+       /*
+        * Except for wildly misbehaving hardware, or if it was disconnected
+        * just after responding with the IDT, there is no reason for any
+        * work item to be running now. To be sure that xdev->channels
+        * is updated on anything that might run in parallel, flush the
+        * workqueue, which rarely does anything.
+        */
+       flush_workqueue(xdev->workq);
+
+       xdev->num_channels = num_channels;
+
+       fifo_mem_release(&idt_fifo);
+       kfree(chan);
+
+       rc = xillybus_init_chrdev(&interface->dev, &xillyusb_fops,
+                                 THIS_MODULE, xdev,
+                                 idt + names_offset,
+                                 idt_len - names_offset,
+                                 num_channels,
+                                 xillyname, true);
+
+       kfree(idt);
+
+       return rc;
+
+unidt:
+       kfree(idt);
+
+unfifo:
+       safely_assign_in_fifo(chan, NULL);
+       fifo_mem_release(&idt_fifo);
+
+       return rc;
+}
+
+static int xillyusb_probe(struct usb_interface *interface,
+                         const struct usb_device_id *id)
+{
+       struct xillyusb_dev *xdev;
+       int rc;
+
+       xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
+       if (!xdev)
+               return -ENOMEM;
+
+       kref_init(&xdev->kref);
+       mutex_init(&xdev->process_in_mutex);
+       mutex_init(&xdev->msg_mutex);
+
+       xdev->udev = usb_get_dev(interface_to_usbdev(interface));
+       xdev->dev = &interface->dev;
+       xdev->error = 0;
+       spin_lock_init(&xdev->error_lock);
+       xdev->in_counter = 0;
+       xdev->in_bytes_left = 0;
+       xdev->workq = alloc_workqueue(xillyname, WQ_HIGHPRI, 0);
+
+       if (!xdev->workq) {
+               dev_err(&interface->dev, "Failed to allocate work queue\n");
+               rc = -ENOMEM;
+               goto fail;
+       }
+
+       INIT_WORK(&xdev->wakeup_workitem, wakeup_all);
+
+       usb_set_intfdata(interface, xdev);
+
+       rc = xillyusb_setup_base_eps(xdev);
+       if (rc)
+               goto fail;
+
+       rc = xillyusb_discovery(interface);
+       if (rc)
+               goto latefail;
+
+       return 0;
+
+latefail:
+       endpoint_quiesce(xdev->in_ep);
+       endpoint_quiesce(xdev->msg_ep);
+
+fail:
+       usb_set_intfdata(interface, NULL);
+       kref_put(&xdev->kref, cleanup_dev);
+       return rc;
+}
+
+static void xillyusb_disconnect(struct usb_interface *interface)
+{
+       struct xillyusb_dev *xdev = usb_get_intfdata(interface);
+       struct xillyusb_endpoint *msg_ep = xdev->msg_ep;
+       struct xillyfifo *fifo = &msg_ep->fifo;
+       int rc;
+       int i;
+
+       xillybus_cleanup_chrdev(xdev, &interface->dev);
+
+       /*
+        * Try to send OPCODE_QUIESCE, which will fail silently if the device
+        * was disconnected, but makes sense on module unload.
+        */
+
+       msg_ep->wake_on_drain = true;
+       xillyusb_send_opcode(xdev, ~0, OPCODE_QUIESCE, 0);
+
+       /*
+        * If the device has been disconnected, sending the opcode causes
+        * a global device error with xdev->error, if such error didn't
+        * occur earlier. Hence timing out means that the USB link is fine,
+        * but somehow the message wasn't sent. Should never happen.
+        */
+
+       rc = wait_event_interruptible_timeout(fifo->waitq,
+                                             msg_ep->drained || xdev->error,
+                                             XILLY_RESPONSE_TIMEOUT);
+
+       if (!rc)
+               dev_err(&interface->dev,
+                       "Weird timeout condition on sending quiesce request.\n");
+
+       report_io_error(xdev, -ENODEV); /* Discourage further activity */
+
+       /*
+        * This device driver is declared with soft_unbind set, or else
+        * sending OPCODE_QUIESCE above would always fail. The price is
+        * that the USB framework didn't kill outstanding URBs, so it has
+        * to be done explicitly before returning from this call.
+        */
+
+       for (i = 0; i < xdev->num_channels; i++) {
+               struct xillyusb_channel *chan = &xdev->channels[i];
+
+               /*
+                * Lock taken to prevent chan->out_ep from changing. It also
+                * ensures xillyusb_open() and xillyusb_flush() don't access
+                * xdev->dev after being nullified below.
+                */
+               mutex_lock(&chan->lock);
+               if (chan->out_ep)
+                       endpoint_quiesce(chan->out_ep);
+               mutex_unlock(&chan->lock);
+       }
+
+       endpoint_quiesce(xdev->in_ep);
+       endpoint_quiesce(xdev->msg_ep);
+
+       usb_set_intfdata(interface, NULL);
+
+       xdev->dev = NULL;
+
+       kref_put(&xdev->kref, cleanup_dev);
+}
+
+static struct usb_driver xillyusb_driver = {
+       .name = xillyname,
+       .id_table = xillyusb_table,
+       .probe = xillyusb_probe,
+       .disconnect = xillyusb_disconnect,
+       .soft_unbind = 1,
+};
+
+static int __init xillyusb_init(void)
+{
+       int rc = 0;
+
+       if (LOG2_INITIAL_FIFO_BUF_SIZE > PAGE_SHIFT)
+               fifo_buf_order = LOG2_INITIAL_FIFO_BUF_SIZE - PAGE_SHIFT;
+       else
+               fifo_buf_order = 0;
+
+       rc = usb_register(&xillyusb_driver);
+
+       return rc;
+}
+
+static void __exit xillyusb_exit(void)
+{
+       usb_deregister(&xillyusb_driver);
+}
+
+module_init(xillyusb_init);
+module_exit(xillyusb_exit);
index d1d509e..4bf5daa 100644 (file)
@@ -555,6 +555,7 @@ static int comedi_8254_insn_config(struct comedi_device *dev,
 /**
  * comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer
  * @s:         comedi_subdevice struct
+ * @i8254:     comedi_8254 struct
  */
 void comedi_8254_subdevice_init(struct comedi_subdevice *s,
                                struct comedi_8254 *i8254)
@@ -607,7 +608,7 @@ static struct comedi_8254 *__i8254_init(unsigned long iobase,
 
 /**
  * comedi_8254_init - allocate and initialize the 8254 device for pio access
- * @mmio:      port I/O base address
+ * @iobase:    port I/O base address
  * @osc_base:  base time of the counter in ns
  *             OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
  * @iosize:    I/O register size
index c729094..479b58e 100644 (file)
@@ -143,7 +143,7 @@ EXPORT_SYMBOL_GPL(comedi_isadma_set_mode);
  * comedi_isadma_alloc - allocate and initialize the ISA DMA
  * @dev:       comedi_device struct
  * @n_desc:    the number of cookies to allocate
- * @dma_chan DMA channel for the first cookie
+ * @dma_chan1: DMA channel for the first cookie
  * @dma_chan2: DMA channel for the second cookie
  * @maxsize:   the size of the buffer to allocate for each cookie
  * @dma_dir:   the DMA direction
index c426a92..f0f8cd4 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routes.c
  *  Route information for NI boards.
@@ -246,7 +245,7 @@ unsigned int ni_get_valid_routes(const struct ni_route_tables *tables,
 }
 EXPORT_SYMBOL_GPL(ni_get_valid_routes);
 
-/**
+/*
  * List of NI global signal names that, as destinations, are only routeable
  * indirectly through the *_arg elements of the comedi_cmd structure.
  */
@@ -388,7 +387,7 @@ ni_find_route_set(const int destination,
 }
 EXPORT_SYMBOL_GPL(ni_find_route_set);
 
-/**
+/*
  * ni_route_set_has_source() - Determines whether the given source is in
  *                            included given route_set.
  *
@@ -507,7 +506,7 @@ s8 ni_route_to_register(const int src, const int dest,
 }
 EXPORT_SYMBOL_GPL(ni_route_to_register);
 
-/**
+/*
  * ni_find_route_source() - Finds the signal source corresponding to a signal
  *                         route (src-->dest) of the specified routing register
  *                         value and the specified route destination on the
index b7680fd..0369823 100644 (file)
@@ -1,5 +1,4 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routes.h
  *  Route information for NI boards.
index 7b6a74d..58654c2 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes.c
  *  List of valid routes for specific NI boards.
index b9f1c47..09e4e17 100644 (file)
@@ -1,5 +1,4 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes.c
  *  List of valid routes for specific NI boards.
index 78b2413..001dbb8 100644 (file)
@@ -1,5 +1,4 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/all.h
  *  List of valid routes for specific NI boards.
index f1126a0..7d3064c 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c
  *  List of valid routes for specific NI boards.
index 74a5922..e2c462e 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6220.c
  *  List of valid routes for specific NI boards.
index 44dcbab..9e02ec0 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6221.c
  *  List of valid routes for specific NI boards.
index fa5794e..33f7fff 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6229.c
  *  List of valid routes for specific NI boards.
index 645fd1c..dde676b 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6251.c
  *  List of valid routes for specific NI boards.
index 056a240..167a2da 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6254.c
  *  List of valid routes for specific NI boards.
index e0b5fa7..ba990f9 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6259.c
  *  List of valid routes for specific NI boards.
index a2472ed..f8d2a91 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6534.c
  *  List of valid routes for specific NI boards.
index 91de9da..2eee91f 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6602.c
  *  List of valid routes for specific NI boards.
index d378b36..c07ef35 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6713.c
  *  List of valid routes for specific NI boards.
index e0cc57a..c37373f 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6723.c
  *  List of valid routes for specific NI boards.
index f6e1e17..f252fbe 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pci-6733.c
  *  List of valid routes for specific NI boards.
index 9978d63..4ccba4f 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pxi-6030e.c
  *  List of valid routes for specific NI boards.
index 1b89e27..84fdfa2 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pxi-6224.c
  *  List of valid routes for specific NI boards.
index 10dfc34..2b99ce0 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pxi-6225.c
  *  List of valid routes for specific NI boards.
index 25db4b7..1c5164c 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pxi-6251.c
  *  List of valid routes for specific NI boards.
index 27da443..a3402b1 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pxi-6733.c
  *  List of valid routes for specific NI boards.
index 8354fe9..defcc4c 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pxie-6251.c
  *  List of valid routes for specific NI boards.
index 2ebb679..d2013b9 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pxie-6535.c
  *  List of valid routes for specific NI boards.
index d885043..89aff39 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_device_routes/pxie-6738.c
  *  List of valid routes for specific NI boards.
index 5901762..54a740b 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_route_values.c
  *  Route information for NI boards.
index 80e0145..6e358ef 100644 (file)
@@ -1,5 +1,4 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_route_values.h
  *  Route information for NI boards.
index 7227461..30761e5 100644 (file)
@@ -1,5 +1,4 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_route_values/all.h
  *  List of valid routes for specific NI boards.
index f1c7e66..aace60e 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_route_values/ni_660x.c
  *  Route information for NI_660X boards.
index d1ab3c9..7a52f02 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_route_values/ni_eseries.c
  *  Route information for NI_ESERIES boards.
index c59d8af..d1ddd13 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/ni_route_values/ni_mseries.c
  *  Route information for NI_MSERIES boards.
index dedb6f2..d55521b 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 
 #include <stdint.h>
 #include <stdbool.h>
index 532eb63..90378fb 100755 (executable)
@@ -1,6 +1,5 @@
 #!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0+
-# vim: ts=2:sw=2:et:tw=80:nowrap
 
 # This is simply to aide in creating the entries in the order of the value of
 # the device-global NI signal/terminal constants defined in comedi.h
@@ -123,7 +122,6 @@ class DeviceRoutes(CSVCollection):
 
   output_file_top = """\
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/{filename}
  *  List of valid routes for specific NI boards.
@@ -155,7 +153,6 @@ class DeviceRoutes(CSVCollection):
 
   extern_header = """\
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/{filename}
  *  List of valid routes for specific NI boards.
@@ -193,7 +190,6 @@ class DeviceRoutes(CSVCollection):
 
   single_output_file_top = """\
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/{filename}
  *  List of valid routes for specific NI boards.
@@ -299,7 +295,6 @@ class RouteValues(CSVCollection):
 
   output_file_top = """\
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/{filename}
  *  Route information for NI boards.
@@ -337,7 +332,6 @@ class RouteValues(CSVCollection):
 
   extern_header = """\
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/{filename}
  *  List of valid routes for specific NI boards.
@@ -375,7 +369,6 @@ class RouteValues(CSVCollection):
 
   single_output_file_top = """\
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/ni_routing/{filename}
  *  Route information for {sheet} boards.
index b3e6472..a273b33 100755 (executable)
@@ -1,6 +1,5 @@
 #!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0+
-# vim: ts=2:sw=2:et:tw=80:nowrap
 
 from os import path
 import os, csv
index 89c90a0..c00eaf8 100755 (executable)
@@ -1,6 +1,5 @@
 #!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0+
-# vim: ts=2:sw=2:et:tw=80:nowrap
 
 from os import path
 import os, csv
index 5f9b825..d4df5f2 100644 (file)
@@ -1,5 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0+
-# vim: ts=2:sw=2:et:tw=80:nowrap
 """
 This file helps to extract string names of NI signals as included in comedi.h
 between NI_NAMES_BASE and NI_NAMES_BASE+NI_NUM_NAMES.
index f6154ad..da6826d 100644 (file)
@@ -1501,7 +1501,7 @@ int ni_tio_insn_config(struct comedi_device *dev,
 }
 EXPORT_SYMBOL_GPL(ni_tio_insn_config);
 
-/**
+/*
  * Retrieves the register value of the current source of the output selector for
  * the given destination.
  *
@@ -1541,10 +1541,10 @@ int ni_tio_get_routing(struct ni_gpct_device *counter_dev, unsigned int dest)
 EXPORT_SYMBOL_GPL(ni_tio_get_routing);
 
 /**
- * Sets the register value of the selector MUX for the given destination.
- * @counter_dev:Pointer to general counter device.
- * @destination:Device-global identifier of route destination.
- * @register_value:
+ * ni_tio_set_routing() - Sets the register value of the selector MUX for the given destination.
+ * @counter_dev: Pointer to general counter device.
+ * @dest:        Device-global identifier of route destination.
+ * @reg:
  *             The first several bits of this value should store the desired
  *             value to write to the register.  All other bits are for
  *             transmitting information that modify the mode of the particular
@@ -1580,7 +1580,7 @@ int ni_tio_set_routing(struct ni_gpct_device *counter_dev, unsigned int dest,
 }
 EXPORT_SYMBOL_GPL(ni_tio_set_routing);
 
-/**
+/*
  * Sets the given destination MUX to its default value or disable it.
  *
  * Return: 0 if successful; -EINVAL if terminal is unknown.
index e5aaaea..81d074b 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/tests/comedi_example_test.c
  *  Example set of unit tests.
index 3207385..6523624 100644 (file)
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: GPL-2.0+
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/tests/ni_routes_test.c
  *  Unit tests for NI routes (ni_routes.c module).
index 2da3bee..b0b34e0 100644 (file)
@@ -1,5 +1,4 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* vim: set ts=8 sw=8 noet tw=80 nowrap: */
 /*
  *  comedi/drivers/tests/unittest.h
  *  Simple framework for unittests for comedi drivers.
index d9a16ba..65bffde 100644 (file)
@@ -155,34 +155,29 @@ void eisa_driver_unregister(struct eisa_driver *edrv)
 }
 EXPORT_SYMBOL(eisa_driver_unregister);
 
-static ssize_t eisa_show_sig(struct device *dev, struct device_attribute *attr,
-                            char *buf)
+static ssize_t signature_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
 {
        struct eisa_device *edev = to_eisa_device(dev);
        return sprintf(buf, "%s\n", edev->id.sig);
 }
+static DEVICE_ATTR_RO(signature);
 
-static DEVICE_ATTR(signature, S_IRUGO, eisa_show_sig, NULL);
-
-static ssize_t eisa_show_state(struct device *dev,
-                              struct device_attribute *attr,
-                              char *buf)
+static ssize_t enabled_show(struct device *dev,
+                           struct device_attribute *attr, char *buf)
 {
        struct eisa_device *edev = to_eisa_device(dev);
        return sprintf(buf, "%d\n", edev->state & EISA_CONFIG_ENABLED);
 }
+static DEVICE_ATTR_RO(enabled);
 
-static DEVICE_ATTR(enabled, S_IRUGO, eisa_show_state, NULL);
-
-static ssize_t eisa_show_modalias(struct device *dev,
-                                 struct device_attribute *attr,
-                                 char *buf)
+static ssize_t modalias_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
 {
        struct eisa_device *edev = to_eisa_device(dev);
        return sprintf(buf, EISA_DEVICE_MODALIAS_FMT "\n", edev->id.sig);
 }
-
-static DEVICE_ATTR(modalias, S_IRUGO, eisa_show_modalias, NULL);
+static DEVICE_ATTR_RO(modalias);
 
 static int __init eisa_init_device(struct eisa_root_device *root,
                                   struct eisa_device *edev,
index 3aa489d..2a76879 100644 (file)
@@ -1034,24 +1034,32 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
 
        /* add svc client device(s) */
        svc = devm_kzalloc(dev, sizeof(*svc), GFP_KERNEL);
-       if (!svc)
-               return -ENOMEM;
+       if (!svc) {
+               ret = -ENOMEM;
+               goto err_free_kfifo;
+       }
 
        svc->stratix10_svc_rsu = platform_device_alloc(STRATIX10_RSU, 0);
        if (!svc->stratix10_svc_rsu) {
                dev_err(dev, "failed to allocate %s device\n", STRATIX10_RSU);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_free_kfifo;
        }
 
        ret = platform_device_add(svc->stratix10_svc_rsu);
-       if (ret) {
-               platform_device_put(svc->stratix10_svc_rsu);
-               return ret;
-       }
+       if (ret)
+               goto err_put_device;
+
        dev_set_drvdata(dev, svc);
 
        pr_info("Intel Service Layer Driver Initialized\n");
 
+       return 0;
+
+err_put_device:
+       platform_device_put(svc->stratix10_svc_rsu);
+err_free_kfifo:
+       kfifo_free(&controller->svc_fifo);
        return ret;
 }
 
index 33e1505..8cd454e 100644 (file)
@@ -7,7 +7,7 @@ menuconfig FPGA
        tristate "FPGA Configuration Framework"
        help
          Say Y here if you want support for configuring FPGAs from the
-         kernel.  The FPGA framework adds a FPGA manager class and FPGA
+         kernel.  The FPGA framework adds an FPGA manager class and FPGA
          manager drivers.
 
 if FPGA
@@ -134,7 +134,7 @@ config FPGA_REGION
        tristate "FPGA Region"
        depends on FPGA_BRIDGE
        help
-         FPGA Region common code.  A FPGA Region controls a FPGA Manager
+         FPGA Region common code.  An FPGA Region controls an FPGA Manager
          and the FPGA Bridges associated with either a reconfigurable
          region of an FPGA or a whole FPGA.
 
index e9266b2..eeb71e3 100644 (file)
@@ -85,14 +85,14 @@ err_dev:
 }
 
 /**
- * of_fpga_bridge_get - get an exclusive reference to a fpga bridge
+ * of_fpga_bridge_get - get an exclusive reference to an fpga bridge
  *
- * @np: node pointer of a FPGA bridge
+ * @np: node pointer of an FPGA bridge
  * @info: fpga image specific information
  *
  * Return fpga_bridge struct if successful.
  * Return -EBUSY if someone already has a reference to the bridge.
- * Return -ENODEV if @np is not a FPGA Bridge.
+ * Return -ENODEV if @np is not an FPGA Bridge.
  */
 struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
                                       struct fpga_image_info *info)
@@ -113,11 +113,11 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data)
 }
 
 /**
- * fpga_bridge_get - get an exclusive reference to a fpga bridge
+ * fpga_bridge_get - get an exclusive reference to an fpga bridge
  * @dev:       parent device that fpga bridge was registered with
  * @info:      fpga manager info
  *
- * Given a device, get an exclusive reference to a fpga bridge.
+ * Given a device, get an exclusive reference to an fpga bridge.
  *
  * Return: fpga bridge struct or IS_ERR() condition containing error code.
  */
@@ -224,7 +224,7 @@ EXPORT_SYMBOL_GPL(fpga_bridges_put);
 /**
  * of_fpga_bridge_get_to_list - get a bridge, add it to a list
  *
- * @np: node pointer of a FPGA bridge
+ * @np: node pointer of an FPGA bridge
  * @info: fpga image specific information
  * @bridge_list: list of FPGA bridges
  *
@@ -373,7 +373,7 @@ error_kfree:
 EXPORT_SYMBOL_GPL(fpga_bridge_create);
 
 /**
- * fpga_bridge_free - free a fpga bridge created by fpga_bridge_create()
+ * fpga_bridge_free - free an fpga bridge created by fpga_bridge_create()
  * @bridge:    FPGA bridge struct
  */
 void fpga_bridge_free(struct fpga_bridge *bridge)
@@ -397,7 +397,7 @@ static void devm_fpga_bridge_release(struct device *dev, void *res)
  * @br_ops:    pointer to structure of fpga bridge ops
  * @priv:      FPGA bridge private data
  *
- * This function is intended for use in a FPGA bridge driver's probe function.
+ * This function is intended for use in an FPGA bridge driver's probe function.
  * After the bridge driver creates the struct with devm_fpga_bridge_create(), it
  * should register the bridge with fpga_bridge_register().  The bridge driver's
  * remove function should call fpga_bridge_unregister().  The bridge struct
@@ -430,7 +430,7 @@ struct fpga_bridge
 EXPORT_SYMBOL_GPL(devm_fpga_bridge_create);
 
 /**
- * fpga_bridge_register - register a FPGA bridge
+ * fpga_bridge_register - register an FPGA bridge
  *
  * @bridge: FPGA bridge struct
  *
@@ -454,11 +454,11 @@ int fpga_bridge_register(struct fpga_bridge *bridge)
 EXPORT_SYMBOL_GPL(fpga_bridge_register);
 
 /**
- * fpga_bridge_unregister - unregister a FPGA bridge
+ * fpga_bridge_unregister - unregister an FPGA bridge
  *
  * @bridge: FPGA bridge struct
  *
- * This function is intended for use in a FPGA bridge driver's remove function.
+ * This function is intended for use in an FPGA bridge driver's remove function.
  */
 void fpga_bridge_unregister(struct fpga_bridge *bridge)
 {
index b85bc47..ae21202 100644 (file)
@@ -26,7 +26,7 @@ struct fpga_mgr_devres {
 };
 
 /**
- * fpga_image_info_alloc - Allocate a FPGA image info struct
+ * fpga_image_info_alloc - Allocate an FPGA image info struct
  * @dev: owning device
  *
  * Return: struct fpga_image_info or NULL
@@ -50,7 +50,7 @@ struct fpga_image_info *fpga_image_info_alloc(struct device *dev)
 EXPORT_SYMBOL_GPL(fpga_image_info_alloc);
 
 /**
- * fpga_image_info_free - Free a FPGA image info struct
+ * fpga_image_info_free - Free an FPGA image info struct
  * @info: FPGA image info struct to free
  */
 void fpga_image_info_free(struct fpga_image_info *info)
@@ -470,7 +470,7 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data)
 }
 
 /**
- * fpga_mgr_get - Given a device, get a reference to a fpga mgr.
+ * fpga_mgr_get - Given a device, get a reference to an fpga mgr.
  * @dev:       parent device that fpga mgr was registered with
  *
  * Return: fpga manager struct or IS_ERR() condition containing error code.
@@ -487,7 +487,7 @@ struct fpga_manager *fpga_mgr_get(struct device *dev)
 EXPORT_SYMBOL_GPL(fpga_mgr_get);
 
 /**
- * of_fpga_mgr_get - Given a device node, get a reference to a fpga mgr.
+ * of_fpga_mgr_get - Given a device node, get a reference to an fpga mgr.
  *
  * @node:      device node
  *
@@ -506,7 +506,7 @@ struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
 EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
 
 /**
- * fpga_mgr_put - release a reference to a fpga manager
+ * fpga_mgr_put - release a reference to an fpga manager
  * @mgr:       fpga manager structure
  */
 void fpga_mgr_put(struct fpga_manager *mgr)
@@ -550,7 +550,7 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
 EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
 
 /**
- * fpga_mgr_create - create and initialize a FPGA manager struct
+ * fpga_mgr_create - create and initialize an FPGA manager struct
  * @dev:       fpga manager device from pdev
  * @name:      fpga manager name
  * @mops:      pointer to structure of fpga manager ops
@@ -617,7 +617,7 @@ error_kfree:
 EXPORT_SYMBOL_GPL(fpga_mgr_create);
 
 /**
- * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
+ * fpga_mgr_free - free an FPGA manager created with fpga_mgr_create()
  * @mgr:       fpga manager struct
  */
 void fpga_mgr_free(struct fpga_manager *mgr)
@@ -641,7 +641,7 @@ static void devm_fpga_mgr_release(struct device *dev, void *res)
  * @mops:      pointer to structure of fpga manager ops
  * @priv:      fpga manager private data
  *
- * This function is intended for use in a FPGA manager driver's probe function.
+ * This function is intended for use in an FPGA manager driver's probe function.
  * After the manager driver creates the manager struct with
  * devm_fpga_mgr_create(), it should register it with fpga_mgr_register().  The
  * manager driver's remove function should call fpga_mgr_unregister().  The
@@ -674,7 +674,7 @@ struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
 EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
 
 /**
- * fpga_mgr_register - register a FPGA manager
+ * fpga_mgr_register - register an FPGA manager
  * @mgr: fpga manager struct
  *
  * Return: 0 on success, negative error code otherwise.
@@ -706,10 +706,10 @@ error_device:
 EXPORT_SYMBOL_GPL(fpga_mgr_register);
 
 /**
- * fpga_mgr_unregister - unregister a FPGA manager
+ * fpga_mgr_unregister - unregister an FPGA manager
  * @mgr: fpga manager struct
  *
- * This function is intended for use in a FPGA manager driver's remove function.
+ * This function is intended for use in an FPGA manager driver's remove function.
  */
 void fpga_mgr_unregister(struct fpga_manager *mgr)
 {
index c3134b8..c5c55d2 100644 (file)
@@ -33,14 +33,14 @@ struct fpga_region *fpga_region_class_find(
 EXPORT_SYMBOL_GPL(fpga_region_class_find);
 
 /**
- * fpga_region_get - get an exclusive reference to a fpga region
+ * fpga_region_get - get an exclusive reference to an fpga region
  * @region: FPGA Region struct
  *
  * Caller should call fpga_region_put() when done with region.
  *
  * Return fpga_region struct if successful.
  * Return -EBUSY if someone already has a reference to the region.
- * Return -ENODEV if @np is not a FPGA Region.
+ * Return -ENODEV if @np is not an FPGA Region.
  */
 static struct fpga_region *fpga_region_get(struct fpga_region *region)
 {
@@ -234,7 +234,7 @@ err_free:
 EXPORT_SYMBOL_GPL(fpga_region_create);
 
 /**
- * fpga_region_free - free a FPGA region created by fpga_region_create()
+ * fpga_region_free - free an FPGA region created by fpga_region_create()
  * @region: FPGA region
  */
 void fpga_region_free(struct fpga_region *region)
@@ -257,7 +257,7 @@ static void devm_fpga_region_release(struct device *dev, void *res)
  * @mgr: manager that programs this region
  * @get_bridges: optional function to get bridges to a list
  *
- * This function is intended for use in a FPGA region driver's probe function.
+ * This function is intended for use in an FPGA region driver's probe function.
  * After the region driver creates the region struct with
  * devm_fpga_region_create(), it should register it with fpga_region_register().
  * The region driver's remove function should call fpga_region_unregister().
@@ -291,7 +291,7 @@ struct fpga_region
 EXPORT_SYMBOL_GPL(devm_fpga_region_create);
 
 /**
- * fpga_region_register - register a FPGA region
+ * fpga_region_register - register an FPGA region
  * @region: FPGA region
  *
  * Return: 0 or -errno
@@ -303,10 +303,10 @@ int fpga_region_register(struct fpga_region *region)
 EXPORT_SYMBOL_GPL(fpga_region_register);
 
 /**
- * fpga_region_unregister - unregister a FPGA region
+ * fpga_region_unregister - unregister an FPGA region
  * @region: FPGA region
  *
- * This function is intended for use in a FPGA region driver's remove function.
+ * This function is intended for use in an FPGA region driver's remove function.
  */
 void fpga_region_unregister(struct fpga_region *region)
 {
index e405309..e3c2557 100644 (file)
@@ -181,7 +181,7 @@ static int child_regions_with_firmware(struct device_node *overlay)
  * @region: FPGA region
  * @overlay: overlay applied to the FPGA region
  *
- * Given an overlay applied to a FPGA region, parse the FPGA image specific
+ * Given an overlay applied to an FPGA region, parse the FPGA image specific
  * info in the overlay and do some checking.
  *
  * Returns:
@@ -273,7 +273,7 @@ ret_no_info:
  * @region: FPGA region that the overlay was applied to
  * @nd: overlay notification data
  *
- * Called when an overlay targeted to a FPGA Region is about to be applied.
+ * Called when an overlay targeted to an FPGA Region is about to be applied.
  * Parses the overlay for properties that influence how the FPGA will be
  * programmed and does some checking. If the checks pass, programs the FPGA.
  * If the checks fail, overlay is rejected and does not get added to the
@@ -336,8 +336,8 @@ static void of_fpga_region_notify_post_remove(struct fpga_region *region,
  * @action:    notifier action
  * @arg:       reconfig data
  *
- * This notifier handles programming a FPGA when a "firmware-name" property is
- * added to a fpga-region.
+ * This notifier handles programming an FPGA when a "firmware-name" property is
+ * added to an fpga-region.
  *
  * Returns NOTIFY_OK or error if FPGA programming fails.
  */
index 657a70c..2aeb53f 100644 (file)
@@ -271,7 +271,7 @@ static int s10_send_buf(struct fpga_manager *mgr, const char *buf, size_t count)
 }
 
 /*
- * Send a FPGA image to privileged layers to write to the FPGA.  When done
+ * Send an FPGA image to privileged layers to write to the FPGA.  When done
  * sending, free all service layer buffers we allocated in write_init.
  */
 static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
index 4e60e84..59ddc9f 100644 (file)
@@ -724,7 +724,7 @@ static ssize_t cfam_read(struct file *filep, char __user *buf, size_t count,
        rc = count;
  fail:
        *offset = off;
-       return count;
+       return rc;
 }
 
 static ssize_t cfam_write(struct file *filep, const char __user *buf,
@@ -761,7 +761,7 @@ static ssize_t cfam_write(struct file *filep, const char __user *buf,
        rc = count;
  fail:
        *offset = off;
-       return count;
+       return rc;
 }
 
 static loff_t cfam_llseek(struct file *file, loff_t offset, int whence)
index 90dbe58..8606e55 100644 (file)
@@ -92,7 +92,7 @@ static const u32 fsi_base = 0xa0000000;
 static u16 aspeed_fsi_divisor = FSI_DIVISOR_DEFAULT;
 module_param_named(bus_div,aspeed_fsi_divisor, ushort, 0);
 
-#define OPB_POLL_TIMEOUT               10000
+#define OPB_POLL_TIMEOUT               500
 
 static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr,
                       u32 val, u32 transfer_size)
@@ -101,11 +101,15 @@ static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr,
        u32 reg, status;
        int ret;
 
-       writel(CMD_WRITE, base + OPB0_RW);
-       writel(transfer_size, base + OPB0_XFER_SIZE);
-       writel(addr, base + OPB0_FSI_ADDR);
-       writel(val, base + OPB0_FSI_DATA_W);
-       writel(0x1, base + OPB_IRQ_CLEAR);
+       /*
+        * The ordering of these writes up until the trigger
+        * write does not matter, so use writel_relaxed.
+        */
+       writel_relaxed(CMD_WRITE, base + OPB0_RW);
+       writel_relaxed(transfer_size, base + OPB0_XFER_SIZE);
+       writel_relaxed(addr, base + OPB0_FSI_ADDR);
+       writel_relaxed(val, base + OPB0_FSI_DATA_W);
+       writel_relaxed(0x1, base + OPB_IRQ_CLEAR);
        writel(0x1, base + OPB_TRIGGER);
 
        ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg,
@@ -149,10 +153,14 @@ static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr,
        u32 result, reg;
        int status, ret;
 
-       writel(CMD_READ, base + OPB0_RW);
-       writel(transfer_size, base + OPB0_XFER_SIZE);
-       writel(addr, base + OPB0_FSI_ADDR);
-       writel(0x1, base + OPB_IRQ_CLEAR);
+       /*
+        * The ordering of these writes up until the trigger
+        * write does not matter, so use writel_relaxed.
+        */
+       writel_relaxed(CMD_READ, base + OPB0_RW);
+       writel_relaxed(transfer_size, base + OPB0_XFER_SIZE);
+       writel_relaxed(addr, base + OPB0_FSI_ADDR);
+       writel_relaxed(0x1, base + OPB_IRQ_CLEAR);
        writel(0x1, base + OPB_TRIGGER);
 
        ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg,
@@ -525,7 +533,6 @@ static int tacoma_cabled_fsi_fixup(struct device *dev)
 static int fsi_master_aspeed_probe(struct platform_device *pdev)
 {
        struct fsi_master_aspeed *aspeed;
-       struct resource *res;
        int rc, links, reg;
        __be32 raw;
 
@@ -541,8 +548,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
 
        aspeed->dev = &pdev->dev;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       aspeed->base = devm_ioremap_resource(&pdev->dev, res);
+       aspeed->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(aspeed->base))
                return PTR_ERR(aspeed->base);
 
@@ -645,6 +651,7 @@ static const struct of_device_id fsi_master_aspeed_match[] = {
        { .compatible = "aspeed,ast2600-fsi-master" },
        { },
 };
+MODULE_DEVICE_TABLE(of, fsi_master_aspeed_match);
 
 static struct platform_driver fsi_master_aspeed_driver = {
        .driver = {
index 57a779a..24292ac 100644 (file)
@@ -1309,7 +1309,6 @@ static int fsi_master_acf_probe(struct platform_device *pdev)
        master->cf_mem = devm_ioremap_resource(&pdev->dev, &res);
        if (IS_ERR(master->cf_mem)) {
                rc = PTR_ERR(master->cf_mem);
-               dev_err(&pdev->dev, "Error %d mapping coldfire memory\n", rc);
                goto err_free;
        }
        dev_dbg(&pdev->dev, "DRAM allocation @%x\n", master->cf_mem_addr);
@@ -1427,6 +1426,7 @@ static const struct of_device_id fsi_master_acf_match[] = {
        { .compatible = "aspeed,ast2500-cf-fsi-master" },
        { },
 };
+MODULE_DEVICE_TABLE(of, fsi_master_acf_match);
 
 static struct platform_driver fsi_master_acf = {
        .driver = {
index aa97c4a..7d5f29b 100644 (file)
@@ -882,6 +882,7 @@ static const struct of_device_id fsi_master_gpio_match[] = {
        { .compatible = "fsi-master-gpio" },
        { },
 };
+MODULE_DEVICE_TABLE(of, fsi_master_gpio_match);
 
 static struct platform_driver fsi_master_gpio_driver = {
        .driver = {
index 10ca2e2..b223f0e 100644 (file)
@@ -223,7 +223,8 @@ static const struct file_operations occ_fops = {
        .release = occ_release,
 };
 
-static int occ_verify_checksum(struct occ_response *resp, u16 data_length)
+static int occ_verify_checksum(struct occ *occ, struct occ_response *resp,
+                              u16 data_length)
 {
        /* Fetch the two bytes after the data for the checksum. */
        u16 checksum_resp = get_unaligned_be16(&resp->data[data_length]);
@@ -238,8 +239,11 @@ static int occ_verify_checksum(struct occ_response *resp, u16 data_length)
        for (i = 0; i < data_length; ++i)
                checksum += resp->data[i];
 
-       if (checksum != checksum_resp)
+       if (checksum != checksum_resp) {
+               dev_err(occ->dev, "Bad checksum: %04x!=%04x\n", checksum,
+                       checksum_resp);
                return -EBADMSG;
+       }
 
        return 0;
 }
@@ -495,6 +499,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
                        goto done;
 
                if (resp->return_status == OCC_RESP_CMD_IN_PRG ||
+                   resp->return_status == OCC_RESP_CRIT_INIT ||
                    resp->seq_no != seq_no) {
                        rc = -ETIMEDOUT;
 
@@ -532,7 +537,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
        }
 
        *resp_len = resp_data_length + 7;
-       rc = occ_verify_checksum(resp, resp_data_length);
+       rc = occ_verify_checksum(occ, resp, resp_data_length);
 
  done:
        mutex_unlock(&occ->occ_lock);
@@ -635,6 +640,7 @@ static const struct of_device_id occ_match[] = {
        },
        { },
 };
+MODULE_DEVICE_TABLE(of, occ_match);
 
 static struct platform_driver occ_driver = {
        .driver = {
index bfd5e5d..84cb965 100644 (file)
@@ -325,7 +325,8 @@ static int sbefifo_up_write(struct sbefifo *sbefifo, __be32 word)
 static int sbefifo_request_reset(struct sbefifo *sbefifo)
 {
        struct device *dev = &sbefifo->fsi_dev->dev;
-       u32 status, timeout;
+       unsigned long end_time;
+       u32 status;
        int rc;
 
        dev_dbg(dev, "Requesting FIFO reset\n");
@@ -341,7 +342,8 @@ static int sbefifo_request_reset(struct sbefifo *sbefifo)
        }
 
        /* Wait for it to complete */
-       for (timeout = 0; timeout < SBEFIFO_RESET_TIMEOUT; timeout++) {
+       end_time = jiffies + msecs_to_jiffies(SBEFIFO_RESET_TIMEOUT);
+       while (!time_after(jiffies, end_time)) {
                rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &status);
                if (rc) {
                        dev_err(dev, "Failed to read UP fifo status during reset"
@@ -355,7 +357,7 @@ static int sbefifo_request_reset(struct sbefifo *sbefifo)
                        return 0;
                }
 
-               msleep(1);
+               cond_resched();
        }
        dev_err(dev, "FIFO reset timed out\n");
 
@@ -400,7 +402,7 @@ static int sbefifo_cleanup_hw(struct sbefifo *sbefifo)
        /* The FIFO already contains a reset request from the SBE ? */
        if (down_status & SBEFIFO_STS_RESET_REQ) {
                dev_info(dev, "Cleanup: FIFO reset request set, resetting\n");
-               rc = sbefifo_regw(sbefifo, SBEFIFO_UP, SBEFIFO_PERFORM_RESET);
+               rc = sbefifo_regw(sbefifo, SBEFIFO_DOWN, SBEFIFO_PERFORM_RESET);
                if (rc) {
                        sbefifo->broken = true;
                        dev_err(dev, "Cleanup: Reset reg write failed, rc=%d\n", rc);
index b45bfab..da1486b 100644 (file)
 #define SCOM_STATUS_PIB_RESP_MASK      0x00007000
 #define SCOM_STATUS_PIB_RESP_SHIFT     12
 
-#define SCOM_STATUS_ANY_ERR            (SCOM_STATUS_PROTECTION | \
-                                        SCOM_STATUS_PARITY |     \
-                                        SCOM_STATUS_PIB_ABORT | \
+#define SCOM_STATUS_FSI2PIB_ERROR      (SCOM_STATUS_PROTECTION |       \
+                                        SCOM_STATUS_PARITY |           \
+                                        SCOM_STATUS_PIB_ABORT)
+#define SCOM_STATUS_ANY_ERR            (SCOM_STATUS_FSI2PIB_ERROR |    \
                                         SCOM_STATUS_PIB_RESP_MASK)
 /* SCOM address encodings */
 #define XSCOM_ADDR_IND_FLAG            BIT_ULL(63)
@@ -60,7 +61,6 @@
 #define XSCOM_ADDR_FORM1_HI_SHIFT      20
 
 /* Retries */
-#define SCOM_MAX_RETRIES               100     /* Retries on busy */
 #define SCOM_MAX_IND_RETRIES           10      /* Retries indirect not ready */
 
 struct scom_device {
@@ -240,14 +240,15 @@ static int handle_fsi2pib_status(struct scom_device *scom, uint32_t status)
 {
        uint32_t dummy = -1;
 
-       if (status & SCOM_STATUS_PROTECTION)
-               return -EPERM;
-       if (status & SCOM_STATUS_PARITY) {
+       if (status & SCOM_STATUS_FSI2PIB_ERROR)
                fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
                                 sizeof(uint32_t));
+
+       if (status & SCOM_STATUS_PROTECTION)
+               return -EPERM;
+       if (status & SCOM_STATUS_PARITY)
                return -EIO;
-       }
-       /* Return -EBUSY on PIB abort to force a retry */
+
        if (status & SCOM_STATUS_PIB_ABORT)
                return -EBUSY;
        return 0;
@@ -284,69 +285,39 @@ static int handle_pib_status(struct scom_device *scom, uint8_t status)
 static int put_scom(struct scom_device *scom, uint64_t value,
                    uint64_t addr)
 {
-       uint32_t status, dummy = -1;
-       int rc, retries;
-
-       for (retries = 0; retries < SCOM_MAX_RETRIES; retries++) {
-               rc = raw_put_scom(scom, value, addr, &status);
-               if (rc) {
-                       /* Try resetting the bridge if FSI fails */
-                       if (rc != -ENODEV && retries == 0) {
-                               fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG,
-                                                &dummy, sizeof(uint32_t));
-                               rc = -EBUSY;
-                       } else
-                               return rc;
-               } else
-                       rc = handle_fsi2pib_status(scom, status);
-               if (rc && rc != -EBUSY)
-                       break;
-               if (rc == 0) {
-                       rc = handle_pib_status(scom,
-                                              (status & SCOM_STATUS_PIB_RESP_MASK)
-                                              >> SCOM_STATUS_PIB_RESP_SHIFT);
-                       if (rc && rc != -EBUSY)
-                               break;
-               }
-               if (rc == 0)
-                       break;
-               msleep(1);
-       }
-       return rc;
+       uint32_t status;
+       int rc;
+
+       rc = raw_put_scom(scom, value, addr, &status);
+       if (rc == -ENODEV)
+               return rc;
+
+       rc = handle_fsi2pib_status(scom, status);
+       if (rc)
+               return rc;
+
+       return handle_pib_status(scom,
+                                (status & SCOM_STATUS_PIB_RESP_MASK)
+                                >> SCOM_STATUS_PIB_RESP_SHIFT);
 }
 
 static int get_scom(struct scom_device *scom, uint64_t *value,
                    uint64_t addr)
 {
-       uint32_t status, dummy = -1;
-       int rc, retries;
-
-       for (retries = 0; retries < SCOM_MAX_RETRIES; retries++) {
-               rc = raw_get_scom(scom, value, addr, &status);
-               if (rc) {
-                       /* Try resetting the bridge if FSI fails */
-                       if (rc != -ENODEV && retries == 0) {
-                               fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG,
-                                                &dummy, sizeof(uint32_t));
-                               rc = -EBUSY;
-                       } else
-                               return rc;
-               } else
-                       rc = handle_fsi2pib_status(scom, status);
-               if (rc && rc != -EBUSY)
-                       break;
-               if (rc == 0) {
-                       rc = handle_pib_status(scom,
-                                              (status & SCOM_STATUS_PIB_RESP_MASK)
-                                              >> SCOM_STATUS_PIB_RESP_SHIFT);
-                       if (rc && rc != -EBUSY)
-                               break;
-               }
-               if (rc == 0)
-                       break;
-               msleep(1);
-       }
-       return rc;
+       uint32_t status;
+       int rc;
+
+       rc = raw_get_scom(scom, value, addr, &status);
+       if (rc == -ENODEV)
+               return rc;
+
+       rc = handle_fsi2pib_status(scom, status);
+       if (rc)
+               return rc;
+
+       return handle_pib_status(scom,
+                                (status & SCOM_STATUS_PIB_RESP_MASK)
+                                >> SCOM_STATUS_PIB_RESP_SHIFT);
 }
 
 static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
index 967532a..0d68a78 100644 (file)
@@ -1151,6 +1151,8 @@ int occ_setup(struct occ *occ, const char *name)
 {
        int rc;
 
+       /* start with 1 to avoid false match with zero-initialized SRAM buffer */
+       occ->seq_no = 1;
        mutex_init(&occ->lock);
        occ->groups[0] = &occ->group;
 
@@ -1160,8 +1162,9 @@ int occ_setup(struct occ *occ, const char *name)
                dev_info(occ->bus_dev, "host is not ready\n");
                return rc;
        } else if (rc < 0) {
-               dev_err(occ->bus_dev, "failed to get OCC poll response: %d\n",
-                       rc);
+               dev_err(occ->bus_dev,
+                       "failed to get OCC poll response=%02x: %d\n",
+                       occ->resp.return_status, rc);
                return rc;
        }
 
index ec71063..3461b0a 100644 (file)
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/**
- * tpci200.c
- *
+/*
  * driver for the TEWS TPCI-200 device
  *
  * Copyright (C) 2009-2012 CERN (www.cern.ch)
@@ -596,8 +594,11 @@ static int tpci200_pci_probe(struct pci_dev *pdev,
 
 out_err_bus_register:
        tpci200_uninstall(tpci200);
+       /* tpci200->info->cfg_regs is unmapped in tpci200_uninstall */
+       tpci200->info->cfg_regs = NULL;
 out_err_install:
-       iounmap(tpci200->info->cfg_regs);
+       if (tpci200->info->cfg_regs)
+               iounmap(tpci200->info->cfg_regs);
 out_err_ioremap:
        pci_release_region(pdev, TPCI200_CFG_MEM_BAR);
 out_err_pci_request:
index 2619f82..e79ac64 100644 (file)
@@ -1,7 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
-/**
- * tpci200.h
- *
+/*
  * driver for the carrier TEWS TPCI-200
  *
  * Copyright (C) 2009-2012 CERN (www.cern.ch)
index 3940714..2a3a94f 100644 (file)
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/**
- * ipoctal.c
- *
+/*
  * driver for the GE IP-OCTAL boards
  *
  * Copyright (C) 2009-2012 CERN (www.cern.ch)
index 75f83ba..773dc41 100644 (file)
@@ -1,9 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
-/**
- * ipoctal.h
- *
+/*
  * driver for the IPOCTAL boards
-
+ *
  * Copyright (C) 2009-2012 CERN (www.cern.ch)
  * Author: Nicolas Serafini, EIC2 SA
  * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
index f40cf08..066b9ef 100644 (file)
@@ -354,8 +354,7 @@ static void bcm_vk_drain_all_pend(struct device *dev,
        for (num = 0; num < chan->q_nr; num++) {
                list_for_each_entry_safe(entry, tmp, &chan->pendq[num], node) {
                        if ((!ctx) || (entry->ctx->idx == ctx->idx)) {
-                               list_del(&entry->node);
-                               list_add_tail(&entry->node, &del_q);
+                               list_move_tail(&entry->node, &del_q);
                        }
                }
        }
@@ -701,8 +700,7 @@ int bcm_vk_send_shutdown_msg(struct bcm_vk *vk, u32 shut_type,
                return -EINVAL;
        }
 
-       entry = kzalloc(sizeof(*entry) +
-                       sizeof(struct vk_msg_blk), GFP_KERNEL);
+       entry = kzalloc(struct_size(entry, to_v_msg, 1), GFP_KERNEL);
        if (!entry)
                return -ENOMEM;
 
index 4eaad84..56784c8 100644 (file)
@@ -116,7 +116,7 @@ struct bcm_vk_wkent {
        u32 usr_msg_id;
        u32 to_v_blks;
        u32 seq_num;
-       struct vk_msg_blk to_v_msg[0];
+       struct vk_msg_blk to_v_msg[];
 };
 
 /* queue stats counters */
index cd402c8..de6d44a 100644 (file)
@@ -139,7 +139,13 @@ static void alcor_pci_init_check_aspm(struct alcor_pci_priv *priv)
        u32 val32;
 
        priv->pdev_cap_off    = alcor_pci_find_cap_offset(priv, priv->pdev);
-       priv->parent_cap_off = alcor_pci_find_cap_offset(priv,
+       /*
+        * A device might be attached to root complex directly and
+        * priv->parent_pdev will be NULL. In this case we don't check its
+        * capability and disable ASPM completely.
+        */
+       if (priv->parent_pdev)
+               priv->parent_cap_off = alcor_pci_find_cap_offset(priv,
                                                         priv->parent_pdev);
 
        if ((priv->pdev_cap_off == 0) || (priv->parent_cap_off == 0)) {
index bd3bd32..3dbdce9 100644 (file)
@@ -569,7 +569,8 @@ static int cxl_add_chardev(struct cxl_afu *afu, dev_t devt, struct cdev *cdev,
        int rc;
 
        cdev_init(cdev, fops);
-       if ((rc = cdev_add(cdev, devt, 1))) {
+       rc = cdev_add(cdev, devt, 1);
+       if (rc) {
                dev_err(&afu->dev, "Unable to add %s chardev: %i\n", desc, rc);
                return rc;
        }
@@ -577,8 +578,8 @@ static int cxl_add_chardev(struct cxl_afu *afu, dev_t devt, struct cdev *cdev,
        dev = device_create(cxl_class, &afu->dev, devt, afu,
                        "afu%i.%i%s", afu->adapter->adapter_num, afu->slice, postfix);
        if (IS_ERR(dev)) {
-               dev_err(&afu->dev, "Unable to create %s chardev in sysfs: %i\n", desc, rc);
                rc = PTR_ERR(dev);
+               dev_err(&afu->dev, "Unable to create %s chardev in sysfs: %i\n", desc, rc);
                goto err;
        }
 
index 0f791bf..f0a7531 100644 (file)
@@ -32,12 +32,13 @@ config EEPROM_AT24
          will be called at24.
 
 config EEPROM_AT25
-       tristate "SPI EEPROMs from most vendors"
+       tristate "SPI EEPROMs (FRAMs) from most vendors"
        depends on SPI && SYSFS
        select NVMEM
        select NVMEM_SYSFS
        help
-         Enable this driver to get read/write support to most SPI EEPROMs,
+         Enable this driver to get read/write support to most SPI EEPROMs
+         and Cypress FRAMs,
          after you configure the board init code to know about each eeprom
          on your target board.
 
index b76e490..4d09b67 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models
+ *          and Cypress FRAMs FM25 models
  *
  * Copyright (C) 2006 David Brownell
  */
@@ -16,6 +17,9 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/eeprom.h>
 #include <linux/property.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/math.h>
 
 /*
  * NOTE: this is an *EEPROM* driver.  The vagaries of product naming
@@ -27,6 +31,7 @@
  *   AT25M02, AT25128B
  */
 
+#define        FM25_SN_LEN     8               /* serial number length */
 struct at25_data {
        struct spi_device       *spi;
        struct mutex            lock;
@@ -34,6 +39,7 @@ struct at25_data {
        unsigned                addrlen;
        struct nvmem_config     nvmem_config;
        struct nvmem_device     *nvmem;
+       u8 sernum[FM25_SN_LEN];
 };
 
 #define        AT25_WREN       0x06            /* latch the write enable */
@@ -42,6 +48,9 @@ struct at25_data {
 #define        AT25_WRSR       0x01            /* write status register */
 #define        AT25_READ       0x03            /* read byte(s) */
 #define        AT25_WRITE      0x02            /* write byte(s)/sector */
+#define        FM25_SLEEP      0xb9            /* enter sleep mode */
+#define        FM25_RDID       0x9f            /* read device ID */
+#define        FM25_RDSN       0xc3            /* read S/N */
 
 #define        AT25_SR_nRDY    0x01            /* nRDY = write-in-progress */
 #define        AT25_SR_WEN     0x02            /* write enable (latched) */
@@ -51,6 +60,8 @@ struct at25_data {
 
 #define        AT25_INSTR_BIT3 0x08            /* Additional address bit in instr */
 
+#define        FM25_ID_LEN     9               /* ID length */
+
 #define EE_MAXADDRLEN  3               /* 24 bit addresses, up to 2 MBytes */
 
 /* Specs often allow 5 msec for a page write, sometimes 20 msec;
@@ -129,6 +140,51 @@ static int at25_ee_read(void *priv, unsigned int offset,
        return status;
 }
 
+/*
+ * read extra registers as ID or serial number
+ */
+static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command,
+                        int len)
+{
+       int status;
+       struct spi_transfer t[2];
+       struct spi_message m;
+
+       spi_message_init(&m);
+       memset(t, 0, sizeof(t));
+
+       t[0].tx_buf = &command;
+       t[0].len = 1;
+       spi_message_add_tail(&t[0], &m);
+
+       t[1].rx_buf = buf;
+       t[1].len = len;
+       spi_message_add_tail(&t[1], &m);
+
+       mutex_lock(&at25->lock);
+
+       status = spi_sync(at25->spi, &m);
+       dev_dbg(&at25->spi->dev, "read %d aux bytes --> %d\n", len, status);
+
+       mutex_unlock(&at25->lock);
+       return status;
+}
+
+static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct at25_data *at25;
+
+       at25 = dev_get_drvdata(dev);
+       return sysfs_emit(buf, "%*ph\n", (int)sizeof(at25->sernum), at25->sernum);
+}
+static DEVICE_ATTR_RO(sernum);
+
+static struct attribute *sernum_attrs[] = {
+       &dev_attr_sernum.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(sernum);
+
 static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
 {
        struct at25_data *at25 = priv;
@@ -303,34 +359,39 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
        return 0;
 }
 
+static const struct of_device_id at25_of_match[] = {
+       { .compatible = "atmel,at25",},
+       { .compatible = "cypress,fm25",},
+       { }
+};
+MODULE_DEVICE_TABLE(of, at25_of_match);
+
 static int at25_probe(struct spi_device *spi)
 {
        struct at25_data        *at25 = NULL;
        struct spi_eeprom       chip;
        int                     err;
        int                     sr;
-       int                     addrlen;
+       u8 id[FM25_ID_LEN];
+       u8 sernum[FM25_SN_LEN];
+       int i;
+       const struct of_device_id *match;
+       bool is_fram = 0;
+
+       match = of_match_device(of_match_ptr(at25_of_match), &spi->dev);
+       if (match && !strcmp(match->compatible, "cypress,fm25"))
+               is_fram = 1;
 
        /* Chip description */
        if (!spi->dev.platform_data) {
-               err = at25_fw_to_chip(&spi->dev, &chip);
-               if (err)
-                       return err;
+               if (!is_fram) {
+                       err = at25_fw_to_chip(&spi->dev, &chip);
+                       if (err)
+                               return err;
+               }
        } else
                chip = *(struct spi_eeprom *)spi->dev.platform_data;
 
-       /* For now we only support 8/16/24 bit addressing */
-       if (chip.flags & EE_ADDR1)
-               addrlen = 1;
-       else if (chip.flags & EE_ADDR2)
-               addrlen = 2;
-       else if (chip.flags & EE_ADDR3)
-               addrlen = 3;
-       else {
-               dev_dbg(&spi->dev, "unsupported address type\n");
-               return -EINVAL;
-       }
-
        /* Ping the chip ... the status register is pretty portable,
         * unlike probing manufacturer IDs.  We do expect that system
         * firmware didn't write it in the past few milliseconds!
@@ -349,9 +410,51 @@ static int at25_probe(struct spi_device *spi)
        at25->chip = chip;
        at25->spi = spi;
        spi_set_drvdata(spi, at25);
-       at25->addrlen = addrlen;
 
-       at25->nvmem_config.type = NVMEM_TYPE_EEPROM;
+       if (is_fram) {
+               /* Get ID of chip */
+               fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN);
+               if (id[6] != 0xc2) {
+                       dev_err(&spi->dev,
+                               "Error: no Cypress FRAM (id %02x)\n", id[6]);
+                       return -ENODEV;
+               }
+               /* set size found in ID */
+               if (id[7] < 0x21 || id[7] > 0x26) {
+                       dev_err(&spi->dev, "Error: unsupported size (id %02x)\n", id[7]);
+                       return -ENODEV;
+               }
+               chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024;
+
+               if (at25->chip.byte_len > 64 * 1024)
+                       at25->chip.flags |= EE_ADDR3;
+               else
+                       at25->chip.flags |= EE_ADDR2;
+
+               if (id[8]) {
+                       fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN);
+                       /* swap byte order */
+                       for (i = 0; i < FM25_SN_LEN; i++)
+                               at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i];
+               }
+
+               at25->chip.page_size = PAGE_SIZE;
+               strncpy(at25->chip.name, "fm25", sizeof(at25->chip.name));
+       }
+
+       /* For now we only support 8/16/24 bit addressing */
+       if (at25->chip.flags & EE_ADDR1)
+               at25->addrlen = 1;
+       else if (at25->chip.flags & EE_ADDR2)
+               at25->addrlen = 2;
+       else if (at25->chip.flags & EE_ADDR3)
+               at25->addrlen = 3;
+       else {
+               dev_dbg(&spi->dev, "unsupported address type\n");
+               return -EINVAL;
+       }
+
+       at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM;
        at25->nvmem_config.name = dev_name(&spi->dev);
        at25->nvmem_config.dev = &spi->dev;
        at25->nvmem_config.read_only = chip.flags & EE_READONLY;
@@ -370,27 +473,22 @@ static int at25_probe(struct spi_device *spi)
        if (IS_ERR(at25->nvmem))
                return PTR_ERR(at25->nvmem);
 
-       dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n",
-               (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024),
-               (chip.byte_len < 1024) ? "Byte" : "KByte",
-               at25->chip.name,
-               (chip.flags & EE_READONLY) ? " (readonly)" : "",
-               at25->chip.page_size);
+       dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n",
+                (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024),
+                (chip.byte_len < 1024) ? "Byte" : "KByte",
+                at25->chip.name, is_fram ? "fram" : "eeprom",
+                (chip.flags & EE_READONLY) ? " (readonly)" : "",
+                at25->chip.page_size);
        return 0;
 }
 
 /*-------------------------------------------------------------------------*/
 
-static const struct of_device_id at25_of_match[] = {
-       { .compatible = "atmel,at25", },
-       { }
-};
-MODULE_DEVICE_TABLE(of, at25_of_match);
-
 static struct spi_driver at25_driver = {
        .driver = {
                .name           = "at25",
                .of_match_table = at25_of_match,
+               .dev_groups     = sernum_groups,
        },
        .probe          = at25_probe,
 };
index 252e15b..bb9c451 100644 (file)
  */
 
 #define EE1004_ADDR_SET_PAGE           0x36
-#define EE1004_EEPROM_SIZE             512
+#define EE1004_NUM_PAGES               2
 #define EE1004_PAGE_SIZE               256
 #define EE1004_PAGE_SHIFT              8
+#define EE1004_EEPROM_SIZE             (EE1004_PAGE_SIZE * EE1004_NUM_PAGES)
 
 /*
  * Mutex protects ee1004_set_page and ee1004_dev_count, and must be held
  * from page selection to end of read.
  */
 static DEFINE_MUTEX(ee1004_bus_lock);
-static struct i2c_client *ee1004_set_page[2];
+static struct i2c_client *ee1004_set_page[EE1004_NUM_PAGES];
 static unsigned int ee1004_dev_count;
 static int ee1004_current_page;
 
@@ -71,40 +72,58 @@ static int ee1004_get_current_page(void)
        return 0;
 }
 
+static int ee1004_set_current_page(struct device *dev, int page)
+{
+       int ret;
+
+       if (page == ee1004_current_page)
+               return 0;
+
+       /* Data is ignored */
+       ret = i2c_smbus_write_byte(ee1004_set_page[page], 0x00);
+       /*
+        * Don't give up just yet. Some memory modules will select the page
+        * but not ack the command. Check which page is selected now.
+        */
+       if (ret == -ENXIO && ee1004_get_current_page() == page)
+               ret = 0;
+       if (ret < 0) {
+               dev_err(dev, "Failed to select page %d (%d)\n", page, ret);
+               return ret;
+       }
+
+       dev_dbg(dev, "Selected page %d\n", page);
+       ee1004_current_page = page;
+
+       return 0;
+}
+
 static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf,
                                  unsigned int offset, size_t count)
 {
-       int status;
+       int status, page;
+
+       page = offset >> EE1004_PAGE_SHIFT;
+       offset &= (1 << EE1004_PAGE_SHIFT) - 1;
+
+       status = ee1004_set_current_page(&client->dev, page);
+       if (status)
+               return status;
 
-       if (count > I2C_SMBUS_BLOCK_MAX)
-               count = I2C_SMBUS_BLOCK_MAX;
        /* Can't cross page boundaries */
-       if (unlikely(offset + count > EE1004_PAGE_SIZE))
+       if (offset + count > EE1004_PAGE_SIZE)
                count = EE1004_PAGE_SIZE - offset;
 
-       status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset,
-                                                          count, buf);
-       dev_dbg(&client->dev, "read %zu@%d --> %d\n", count, offset, status);
-
-       return status;
+       return i2c_smbus_read_i2c_block_data_or_emulated(client, offset, count, buf);
 }
 
-static ssize_t ee1004_read(struct file *filp, struct kobject *kobj,
+static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
                           struct bin_attribute *bin_attr,
                           char *buf, loff_t off, size_t count)
 {
-       struct device *dev = kobj_to_dev(kobj);
-       struct i2c_client *client = to_i2c_client(dev);
+       struct i2c_client *client = kobj_to_i2c_client(kobj);
        size_t requested = count;
-       int page;
-
-       if (unlikely(!count))
-               return count;
-
-       page = off >> EE1004_PAGE_SHIFT;
-       if (unlikely(page > 1))
-               return 0;
-       off &= (1 << EE1004_PAGE_SHIFT) - 1;
+       int ret = 0;
 
        /*
         * Read data from chip, protecting against concurrent access to
@@ -113,139 +132,85 @@ static ssize_t ee1004_read(struct file *filp, struct kobject *kobj,
        mutex_lock(&ee1004_bus_lock);
 
        while (count) {
-               int status;
-
-               /* Select page */
-               if (page != ee1004_current_page) {
-                       /* Data is ignored */
-                       status = i2c_smbus_write_byte(ee1004_set_page[page],
-                                                     0x00);
-                       if (status == -ENXIO) {
-                               /*
-                                * Don't give up just yet. Some memory
-                                * modules will select the page but not
-                                * ack the command. Check which page is
-                                * selected now.
-                                */
-                               if (ee1004_get_current_page() == page)
-                                       status = 0;
-                       }
-                       if (status < 0) {
-                               dev_err(dev, "Failed to select page %d (%d)\n",
-                                       page, status);
-                               mutex_unlock(&ee1004_bus_lock);
-                               return status;
-                       }
-                       dev_dbg(dev, "Selected page %d\n", page);
-                       ee1004_current_page = page;
-               }
-
-               status = ee1004_eeprom_read(client, buf, off, count);
-               if (status < 0) {
-                       mutex_unlock(&ee1004_bus_lock);
-                       return status;
-               }
-               buf += status;
-               off += status;
-               count -= status;
+               ret = ee1004_eeprom_read(client, buf, off, count);
+               if (ret < 0)
+                       goto out;
 
-               if (off == EE1004_PAGE_SIZE) {
-                       page++;
-                       off = 0;
-               }
+               buf += ret;
+               off += ret;
+               count -= ret;
        }
-
+out:
        mutex_unlock(&ee1004_bus_lock);
 
-       return requested;
+       return ret < 0 ? ret : requested;
 }
 
-static const struct bin_attribute eeprom_attr = {
-       .attr = {
-               .name = "eeprom",
-               .mode = 0444,
-       },
-       .size = EE1004_EEPROM_SIZE,
-       .read = ee1004_read,
+static BIN_ATTR_RO(eeprom, EE1004_EEPROM_SIZE);
+
+static struct bin_attribute *ee1004_attrs[] = {
+       &bin_attr_eeprom,
+       NULL
 };
 
-static int ee1004_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
+BIN_ATTRIBUTE_GROUPS(ee1004);
+
+static void ee1004_cleanup(int idx)
+{
+       if (--ee1004_dev_count == 0)
+               while (--idx >= 0) {
+                       i2c_unregister_device(ee1004_set_page[idx]);
+                       ee1004_set_page[idx] = NULL;
+               }
+}
+
+static int ee1004_probe(struct i2c_client *client)
 {
        int err, cnr = 0;
-       const char *slow = NULL;
 
        /* Make sure we can operate on this adapter */
        if (!i2c_check_functionality(client->adapter,
-                                    I2C_FUNC_SMBUS_READ_BYTE |
-                                    I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
-               if (i2c_check_functionality(client->adapter,
-                                    I2C_FUNC_SMBUS_READ_BYTE |
-                                    I2C_FUNC_SMBUS_READ_WORD_DATA))
-                       slow = "word";
-               else if (i2c_check_functionality(client->adapter,
-                                    I2C_FUNC_SMBUS_READ_BYTE |
-                                    I2C_FUNC_SMBUS_READ_BYTE_DATA))
-                       slow = "byte";
-               else
-                       return -EPFNOSUPPORT;
-       }
+                                    I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_I2C_BLOCK) &&
+           !i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA))
+               return -EPFNOSUPPORT;
 
        /* Use 2 dummy devices for page select command */
        mutex_lock(&ee1004_bus_lock);
        if (++ee1004_dev_count == 1) {
-               for (cnr = 0; cnr < 2; cnr++) {
-                       ee1004_set_page[cnr] = i2c_new_dummy_device(client->adapter,
-                                               EE1004_ADDR_SET_PAGE + cnr);
-                       if (IS_ERR(ee1004_set_page[cnr])) {
-                               dev_err(&client->dev,
-                                       "address 0x%02x unavailable\n",
-                                       EE1004_ADDR_SET_PAGE + cnr);
-                               err = PTR_ERR(ee1004_set_page[cnr]);
+               for (cnr = 0; cnr < EE1004_NUM_PAGES; cnr++) {
+                       struct i2c_client *cl;
+
+                       cl = i2c_new_dummy_device(client->adapter, EE1004_ADDR_SET_PAGE + cnr);
+                       if (IS_ERR(cl)) {
+                               err = PTR_ERR(cl);
                                goto err_clients;
                        }
+                       ee1004_set_page[cnr] = cl;
                }
-       } else if (i2c_adapter_id(client->adapter) !=
-                  i2c_adapter_id(ee1004_set_page[0]->adapter)) {
+
+               /* Remember current page to avoid unneeded page select */
+               err = ee1004_get_current_page();
+               if (err < 0)
+                       goto err_clients;
+               dev_dbg(&client->dev, "Currently selected page: %d\n", err);
+               ee1004_current_page = err;
+       } else if (client->adapter != ee1004_set_page[0]->adapter) {
                dev_err(&client->dev,
                        "Driver only supports devices on a single I2C bus\n");
                err = -EOPNOTSUPP;
                goto err_clients;
        }
-
-       /* Remember current page to avoid unneeded page select */
-       err = ee1004_get_current_page();
-       if (err < 0)
-               goto err_clients;
-       ee1004_current_page = err;
-       dev_dbg(&client->dev, "Currently selected page: %d\n",
-               ee1004_current_page);
        mutex_unlock(&ee1004_bus_lock);
 
-       /* Create the sysfs eeprom file */
-       err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr);
-       if (err)
-               goto err_clients_lock;
-
        dev_info(&client->dev,
                 "%u byte EE1004-compliant SPD EEPROM, read-only\n",
                 EE1004_EEPROM_SIZE);
-       if (slow)
-               dev_notice(&client->dev,
-                          "Falling back to %s reads, performance will suffer\n",
-                          slow);
 
        return 0;
 
- err_clients_lock:
-       mutex_lock(&ee1004_bus_lock);
  err_clients:
-       if (--ee1004_dev_count == 0) {
-               for (cnr--; cnr >= 0; cnr--) {
-                       i2c_unregister_device(ee1004_set_page[cnr]);
-                       ee1004_set_page[cnr] = NULL;
-               }
-       }
+       ee1004_cleanup(cnr);
        mutex_unlock(&ee1004_bus_lock);
 
        return err;
@@ -253,18 +218,9 @@ static int ee1004_probe(struct i2c_client *client,
 
 static int ee1004_remove(struct i2c_client *client)
 {
-       int i;
-
-       sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
-
        /* Remove page select clients if this is the last device */
        mutex_lock(&ee1004_bus_lock);
-       if (--ee1004_dev_count == 0) {
-               for (i = 0; i < 2; i++) {
-                       i2c_unregister_device(ee1004_set_page[i]);
-                       ee1004_set_page[i] = NULL;
-               }
-       }
+       ee1004_cleanup(EE1004_NUM_PAGES);
        mutex_unlock(&ee1004_bus_lock);
 
        return 0;
@@ -275,8 +231,9 @@ static int ee1004_remove(struct i2c_client *client)
 static struct i2c_driver ee1004_driver = {
        .driver = {
                .name = "ee1004",
+               .dev_groups = ee1004_groups,
        },
-       .probe = ee1004_probe,
+       .probe_new = ee1004_probe,
        .remove = ee1004_remove,
        .id_table = ee1004_ids,
 };
index 80114f4..29d8971 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/device.h>
 #include <linux/gpio/consumer.h>
 #include <linux/kernel.h>
+#include <linux/log2.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
 
 struct eeprom_93xx46_devtype_data {
        unsigned int quirks;
+       unsigned char flags;
+};
+
+static const struct eeprom_93xx46_devtype_data at93c46_data = {
+       .flags = EE_SIZE1K,
+};
+
+static const struct eeprom_93xx46_devtype_data at93c56_data = {
+       .flags = EE_SIZE2K,
+};
+
+static const struct eeprom_93xx46_devtype_data at93c66_data = {
+       .flags = EE_SIZE4K,
 };
 
 static const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = {
+       .flags = EE_SIZE1K,
        .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ |
                  EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH,
 };
 
 static const struct eeprom_93xx46_devtype_data microchip_93lc46b_data = {
+       .flags = EE_SIZE1K,
        .quirks = EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE,
 };
 
@@ -70,6 +86,7 @@ static int eeprom_93xx46_read(void *priv, unsigned int off,
        struct eeprom_93xx46_dev *edev = priv;
        char *buf = val;
        int err = 0;
+       int bits;
 
        if (unlikely(off >= edev->size))
                return 0;
@@ -83,21 +100,21 @@ static int eeprom_93xx46_read(void *priv, unsigned int off,
        if (edev->pdata->prepare)
                edev->pdata->prepare(edev);
 
+       /* The opcode in front of the address is three bits. */
+       bits = edev->addrlen + 3;
+
        while (count) {
                struct spi_message m;
                struct spi_transfer t[2] = { { 0 } };
                u16 cmd_addr = OP_READ << edev->addrlen;
                size_t nbytes = count;
-               int bits;
 
-               if (edev->addrlen == 7) {
-                       cmd_addr |= off & 0x7f;
-                       bits = 10;
+               if (edev->pdata->flags & EE_ADDR8) {
+                       cmd_addr |= off;
                        if (has_quirk_single_word_read(edev))
                                nbytes = 1;
                } else {
-                       cmd_addr |= (off >> 1) & 0x3f;
-                       bits = 9;
+                       cmd_addr |= (off >> 1);
                        if (has_quirk_single_word_read(edev))
                                nbytes = 2;
                }
@@ -152,14 +169,14 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
        int bits, ret;
        u16 cmd_addr;
 
+       /* The opcode in front of the address is three bits. */
+       bits = edev->addrlen + 3;
+
        cmd_addr = OP_START << edev->addrlen;
-       if (edev->addrlen == 7) {
+       if (edev->pdata->flags & EE_ADDR8)
                cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1;
-               bits = 10;
-       } else {
+       else
                cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS);
-               bits = 9;
-       }
 
        if (has_quirk_instruction_length(edev)) {
                cmd_addr <<= 2;
@@ -205,15 +222,19 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
        int bits, data_len, ret;
        u16 cmd_addr;
 
+       if (unlikely(off >= edev->size))
+               return -EINVAL;
+
+       /* The opcode in front of the address is three bits. */
+       bits = edev->addrlen + 3;
+
        cmd_addr = OP_WRITE << edev->addrlen;
 
-       if (edev->addrlen == 7) {
-               cmd_addr |= off & 0x7f;
-               bits = 10;
+       if (edev->pdata->flags & EE_ADDR8) {
+               cmd_addr |= off;
                data_len = 1;
        } else {
-               cmd_addr |= (off >> 1) & 0x3f;
-               bits = 9;
+               cmd_addr |= (off >> 1);
                data_len = 2;
        }
 
@@ -253,7 +274,7 @@ static int eeprom_93xx46_write(void *priv, unsigned int off,
                return count;
 
        /* only write even number of bytes on 16-bit devices */
-       if (edev->addrlen == 6) {
+       if (edev->pdata->flags & EE_ADDR16) {
                step = 2;
                count &= ~1;
        }
@@ -295,14 +316,14 @@ static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
        int bits, ret;
        u16 cmd_addr;
 
+       /* The opcode in front of the address is three bits. */
+       bits = edev->addrlen + 3;
+
        cmd_addr = OP_START << edev->addrlen;
-       if (edev->addrlen == 7) {
+       if (edev->pdata->flags & EE_ADDR8)
                cmd_addr |= ADDR_ERAL << 1;
-               bits = 10;
-       } else {
+       else
                cmd_addr |= ADDR_ERAL;
-               bits = 9;
-       }
 
        if (has_quirk_instruction_length(edev)) {
                cmd_addr <<= 2;
@@ -375,8 +396,11 @@ static void select_deassert(void *context)
 }
 
 static const struct of_device_id eeprom_93xx46_of_table[] = {
-       { .compatible = "eeprom-93xx46", },
+       { .compatible = "eeprom-93xx46", .data = &at93c46_data, },
+       { .compatible = "atmel,at93c46", .data = &at93c46_data, },
        { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, },
+       { .compatible = "atmel,at93c56", .data = &at93c56_data, },
+       { .compatible = "atmel,at93c66", .data = &at93c66_data, },
        { .compatible = "microchip,93lc46b", .data = &microchip_93lc46b_data, },
        {}
 };
@@ -426,6 +450,7 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi)
                const struct eeprom_93xx46_devtype_data *data = of_id->data;
 
                pd->quirks = data->quirks;
+               pd->flags |= data->flags;
        }
 
        spi->dev.platform_data = pd;
@@ -455,10 +480,21 @@ static int eeprom_93xx46_probe(struct spi_device *spi)
        if (!edev)
                return -ENOMEM;
 
+       if (pd->flags & EE_SIZE1K)
+               edev->size = 128;
+       else if (pd->flags & EE_SIZE2K)
+               edev->size = 256;
+       else if (pd->flags & EE_SIZE4K)
+               edev->size = 512;
+       else {
+               dev_err(&spi->dev, "unspecified size\n");
+               return -EINVAL;
+       }
+
        if (pd->flags & EE_ADDR8)
-               edev->addrlen = 7;
+               edev->addrlen = ilog2(edev->size);
        else if (pd->flags & EE_ADDR16)
-               edev->addrlen = 6;
+               edev->addrlen = ilog2(edev->size) - 1;
        else {
                dev_err(&spi->dev, "unspecified address type\n");
                return -EINVAL;
@@ -469,7 +505,6 @@ static int eeprom_93xx46_probe(struct spi_device *spi)
        edev->spi = spi;
        edev->pdata = pd;
 
-       edev->size = 128;
        edev->nvmem_config.type = NVMEM_TYPE_EEPROM;
        edev->nvmem_config.name = dev_name(&spi->dev);
        edev->nvmem_config.dev = &spi->dev;
@@ -489,8 +524,9 @@ static int eeprom_93xx46_probe(struct spi_device *spi)
        if (IS_ERR(edev->nvmem))
                return PTR_ERR(edev->nvmem);
 
-       dev_info(&spi->dev, "%d-bit eeprom %s\n",
+       dev_info(&spi->dev, "%d-bit eeprom containing %d bytes %s\n",
                (pd->flags & EE_ADDR8) ? 8 : 16,
+               edev->size,
                (pd->flags & EE_READONLY) ? "(readonly)" : "");
 
        if (!(pd->flags & EE_READONLY)) {
index 81c70e5..b0cff4b 100644 (file)
@@ -1,38 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
- *   This file is provided under a GPLv2 license.  When using or
- *   redistributing this file, you may do so under that license.
- *
- *   GPL LICENSE SUMMARY
- *
  *   Copyright (C) 2016 T-Platforms. All Rights Reserved.
  *
- *   This program is free software; you can redistribute it and/or modify it
- *   under the terms and conditions of the GNU General Public License,
- *   version 2, as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful, but WITHOUT
- *   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- *   FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- *   more details.
- *
- *   You should have received a copy of the GNU General Public License along
- *   with this program; if not, it can be found <http://www.gnu.org/licenses/>.
- *
- *   The full GNU General Public License is included in this distribution in
- *   the file called "COPYING".
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
  * IDT PCIe-switch NTB Linux driver
  *
  * Contact Information:
@@ -1126,11 +1095,10 @@ static void idt_get_fw_data(struct idt_89hpesx_dev *pdev)
 
        device_for_each_child_node(dev, fwnode) {
                ee_id = idt_ee_match_id(fwnode);
-               if (!ee_id) {
-                       dev_warn(dev, "Skip unsupported EEPROM device");
-                       continue;
-               } else
+               if (ee_id)
                        break;
+
+               dev_warn(dev, "Skip unsupported EEPROM device %pfw\n", fwnode);
        }
 
        /* If there is no fwnode EEPROM device, then set zero size */
@@ -1161,6 +1129,7 @@ static void idt_get_fw_data(struct idt_89hpesx_dev *pdev)
        else /* if (!fwnode_property_read_bool(node, "read-only")) */
                pdev->eero = false;
 
+       fwnode_handle_put(fwnode);
        dev_info(dev, "EEPROM of %d bytes found by 0x%x",
                pdev->eesize, pdev->eeaddr);
 }
index 9e4a6bb..22f2208 100644 (file)
@@ -7379,9 +7379,6 @@ static int gaudi_hbm_read_interrupts(struct hl_device *hdev, int device,
                        device, ch, hbm_ecc_data->first_addr, type,
                        hbm_ecc_data->sec_cont_cnt, hbm_ecc_data->sec_cnt,
                        hbm_ecc_data->dec_cnt);
-
-               err = 1;
-
                return 0;
        }
 
index fea3ae9..8d00df9 100644 (file)
@@ -693,6 +693,8 @@ static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
 {
        int bar;
        unsigned long off;
+       u8 pci_rev_id;
+       int rc;
 
        /* map the memory mapped i/o registers */
        hw->mmio_vaddr = pci_iomap(pdev, 1, 0);
@@ -702,7 +704,13 @@ static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
        }
 
        /* map the adapter shared memory region */
-       if (pdev->subsystem_device == 0x00E4) {
+       rc = pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev_id);
+       if (rc != 0) {
+               dev_err(&pdev->dev, "Error reading PCI rev id: %d\n", rc);
+               goto out;
+       }
+
+       if (pci_rev_id >= PCI_REV_ID_NECHES) {
                bar = 5;
                /* Last 8k is reserved for CCBs */
                off = pci_resource_len(pdev, bar) - 0x2000;
index f69ff64..d57c346 100644 (file)
@@ -10,6 +10,9 @@
 
 #define ILO_NAME "hpilo"
 
+/* iLO ASIC PCI revision id */
+#define PCI_REV_ID_NECHES      7
+
 /* max number of open channel control blocks per device, hw limited to 32 */
 #define MAX_CCB               24
 /* min number of open channel control blocks per device, hw limited to 32 */
index 4edad6c..dc8a06c 100644 (file)
@@ -111,7 +111,7 @@ static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
        result = ibmasm_init_remote_input_dev(sp);
        if (result) {
                dev_err(sp->dev, "Failed to initialize remote queue\n");
-               goto error_send_message;
+               goto error_init_remote;
        }
 
        result = ibmasm_send_driver_vpd(sp);
@@ -131,8 +131,9 @@ static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
        return 0;
 
 error_send_message:
-       disable_sp_interrupts(sp->base_address);
        ibmasm_free_remote_input_dev(sp);
+error_init_remote:
+       disable_sp_interrupts(sp->base_address);
        free_irq(sp->irq, (void *)sp);
 error_request_irq:
        iounmap(sp->base_address);
index 28937b6..9001c45 100644 (file)
@@ -50,8 +50,6 @@ static int mei_open(struct inode *inode, struct file *file)
        int err;
 
        dev = container_of(inode->i_cdev, struct mei_device, cdev);
-       if (!dev)
-               return -ENODEV;
 
        mutex_lock(&dev->device_lock);
 
index 4c08417..be40160 100644 (file)
@@ -93,7 +93,7 @@ static int pvpanic_mmio_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       pi = kmalloc(sizeof(*pi), GFP_ATOMIC);
+       pi = devm_kmalloc(dev, sizeof(*pi), GFP_KERNEL);
        if (!pi)
                return -ENOMEM;
 
@@ -104,19 +104,7 @@ static int pvpanic_mmio_probe(struct platform_device *pdev)
        pi->capability &= ioread8(base);
        pi->events = pi->capability;
 
-       dev_set_drvdata(dev, pi);
-
-       return pvpanic_probe(pi);
-}
-
-static int pvpanic_mmio_remove(struct platform_device *pdev)
-{
-       struct pvpanic_instance *pi = dev_get_drvdata(&pdev->dev);
-
-       pvpanic_remove(pi);
-       kfree(pi);
-
-       return 0;
+       return devm_pvpanic_probe(dev, pi);
 }
 
 static const struct of_device_id pvpanic_mmio_match[] = {
@@ -139,6 +127,5 @@ static struct platform_driver pvpanic_mmio_driver = {
                .dev_groups = pvpanic_mmio_dev_groups,
        },
        .probe = pvpanic_mmio_probe,
-       .remove = pvpanic_mmio_remove,
 };
 module_platform_driver(pvpanic_mmio_driver);
index 9ecc4e8..a43c401 100644 (file)
@@ -73,20 +73,19 @@ ATTRIBUTE_GROUPS(pvpanic_pci_dev);
 static int pvpanic_pci_probe(struct pci_dev *pdev,
                             const struct pci_device_id *ent)
 {
-       struct device *dev = &pdev->dev;
        struct pvpanic_instance *pi;
        void __iomem *base;
        int ret;
 
-       ret = pci_enable_device(pdev);
+       ret = pcim_enable_device(pdev);
        if (ret < 0)
                return ret;
 
-       base = pci_iomap(pdev, 0, 0);
+       base = pcim_iomap(pdev, 0, 0);
        if (!base)
                return -ENOMEM;
 
-       pi = kmalloc(sizeof(*pi), GFP_ATOMIC);
+       pi = devm_kmalloc(&pdev->dev, sizeof(*pi), GFP_KERNEL);
        if (!pi)
                return -ENOMEM;
 
@@ -97,26 +96,13 @@ static int pvpanic_pci_probe(struct pci_dev *pdev,
        pi->capability &= ioread8(base);
        pi->events = pi->capability;
 
-       dev_set_drvdata(dev, pi);
-
-       return pvpanic_probe(pi);
-}
-
-static void pvpanic_pci_remove(struct pci_dev *pdev)
-{
-       struct pvpanic_instance *pi = dev_get_drvdata(&pdev->dev);
-
-       pvpanic_remove(pi);
-       iounmap(pi->base);
-       kfree(pi);
-       pci_disable_device(pdev);
+       return devm_pvpanic_probe(&pdev->dev, pi);
 }
 
 static struct pci_driver pvpanic_pci_driver = {
        .name =         "pvpanic-pci",
        .id_table =     pvpanic_pci_id_tbl,
        .probe =        pvpanic_pci_probe,
-       .remove =       pvpanic_pci_remove,
        .driver = {
                .dev_groups = pvpanic_pci_dev_groups,
        },
index 65f70a4..b6bd6ab 100644 (file)
@@ -60,25 +60,10 @@ static struct notifier_block pvpanic_panic_nb = {
        .priority = 1, /* let this called before broken drm_fb_helper */
 };
 
-int pvpanic_probe(struct pvpanic_instance *pi)
-{
-       if (!pi || !pi->base)
-               return -EINVAL;
-
-       spin_lock(&pvpanic_lock);
-       list_add(&pi->list, &pvpanic_list);
-       spin_unlock(&pvpanic_lock);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(pvpanic_probe);
-
-void pvpanic_remove(struct pvpanic_instance *pi)
+static void pvpanic_remove(void *param)
 {
        struct pvpanic_instance *pi_cur, *pi_next;
-
-       if (!pi)
-               return;
+       struct pvpanic_instance *pi = param;
 
        spin_lock(&pvpanic_lock);
        list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) {
@@ -89,7 +74,19 @@ void pvpanic_remove(struct pvpanic_instance *pi)
        }
        spin_unlock(&pvpanic_lock);
 }
-EXPORT_SYMBOL_GPL(pvpanic_remove);
+
+int devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi)
+{
+       if (!pi || !pi->base)
+               return -EINVAL;
+
+       spin_lock(&pvpanic_lock);
+       list_add(&pi->list, &pvpanic_list);
+       spin_unlock(&pvpanic_lock);
+
+       return devm_add_action_or_reset(dev, pvpanic_remove, pi);
+}
+EXPORT_SYMBOL_GPL(devm_pvpanic_probe);
 
 static int pvpanic_init(void)
 {
index 1afccc2..4935459 100644 (file)
@@ -15,7 +15,6 @@ struct pvpanic_instance {
        struct list_head list;
 };
 
-int pvpanic_probe(struct pvpanic_instance *pi);
-void pvpanic_remove(struct pvpanic_instance *pi);
+int devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi);
 
 #endif /* PVPANIC_H_ */
index bae18ef..488eeb2 100644 (file)
@@ -387,15 +387,22 @@ static void uacce_release(struct device *dev)
 
 static unsigned int uacce_enable_sva(struct device *parent, unsigned int flags)
 {
+       int ret;
+
        if (!(flags & UACCE_DEV_SVA))
                return flags;
 
        flags &= ~UACCE_DEV_SVA;
 
-       if (iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_IOPF))
+       ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_IOPF);
+       if (ret) {
+               dev_err(parent, "failed to enable IOPF feature! ret = %pe\n", ERR_PTR(ret));
                return flags;
+       }
 
-       if (iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA)) {
+       ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA);
+       if (ret) {
+               dev_err(parent, "failed to enable SVA feature! ret = %pe\n", ERR_PTR(ret));
                iommu_dev_disable_feature(parent, IOMMU_DEV_FEAT_IOPF);
                return flags;
        }
index 23c8448..d6e3c65 100644 (file)
@@ -1013,9 +1013,6 @@ static __poll_t xsdfec_poll(struct file *file, poll_table *wait)
 
        xsdfec = container_of(file->private_data, struct xsdfec_dev, miscdev);
 
-       if (!xsdfec)
-               return EPOLLNVAL | EPOLLHUP;
-
        poll_wait(file, &xsdfec->waitq, wait);
 
        /* XSDFEC ISR detected an error */
index 01ea0d6..50bd4e3 100644 (file)
@@ -404,7 +404,7 @@ err_out_free_dev:
        return err;
 }
 
-static int vsw_port_remove(struct vio_dev *vdev)
+static void vsw_port_remove(struct vio_dev *vdev)
 {
        struct vnet_port *port = dev_get_drvdata(&vdev->dev);
        unsigned long flags;
@@ -430,8 +430,6 @@ static int vsw_port_remove(struct vio_dev *vdev)
 
                free_netdev(port->dev);
        }
-
-       return 0;
 }
 
 static void vsw_cleanup(void)
index 96b883f..58ee892 100644 (file)
@@ -510,7 +510,7 @@ err_out_put_mdesc:
        return err;
 }
 
-static int vnet_port_remove(struct vio_dev *vdev)
+static void vnet_port_remove(struct vio_dev *vdev)
 {
        struct vnet_port *port = dev_get_drvdata(&vdev->dev);
 
@@ -533,7 +533,6 @@ static int vnet_port_remove(struct vio_dev *vdev)
 
                kfree(port);
        }
-       return 0;
 }
 
 static const struct vio_device_id vnet_port_match[] = {
index bca671f..20799e6 100644 (file)
@@ -180,6 +180,7 @@ static const char * const nvmem_type_str[] = {
        [NVMEM_TYPE_EEPROM] = "EEPROM",
        [NVMEM_TYPE_OTP] = "OTP",
        [NVMEM_TYPE_BATTERY_BACKED] = "Battery backed",
+       [NVMEM_TYPE_FRAM] = "FRAM",
 };
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
@@ -359,6 +360,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
        if (!config->base_dev)
                return -EINVAL;
 
+       if (config->type == NVMEM_TYPE_FRAM)
+               bin_attr_nvmem_eeprom_compat.attr.name = "fram";
+
        nvmem->eeprom = bin_attr_nvmem_eeprom_compat;
        nvmem->eeprom.attr.mode = nvmem_bin_attr_get_umode(nvmem);
        nvmem->eeprom.size = nvmem->size;
@@ -686,15 +690,17 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
                        continue;
                if (len < 2 * sizeof(u32)) {
                        dev_err(dev, "nvmem: invalid reg on %pOF\n", child);
+                       of_node_put(child);
                        return -EINVAL;
                }
 
                cell = kzalloc(sizeof(*cell), GFP_KERNEL);
-               if (!cell)
+               if (!cell) {
+                       of_node_put(child);
                        return -ENOMEM;
+               }
 
                cell->nvmem = nvmem;
-               cell->np = of_node_get(child);
                cell->offset = be32_to_cpup(addr++);
                cell->bytes = be32_to_cpup(addr);
                cell->name = kasprintf(GFP_KERNEL, "%pOFn", child);
@@ -715,11 +721,12 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
                                cell->name, nvmem->stride);
                        /* Cells already added will be freed later. */
                        kfree_const(cell->name);
-                       of_node_put(cell->np);
                        kfree(cell);
+                       of_node_put(child);
                        return -EINVAL;
                }
 
+               cell->np = of_node_get(child);
                nvmem_cell_add(cell);
        }
 
@@ -1606,9 +1613,9 @@ int nvmem_cell_read_u64(struct device *dev, const char *cell_id, u64 *val)
 }
 EXPORT_SYMBOL_GPL(nvmem_cell_read_u64);
 
-static void *nvmem_cell_read_variable_common(struct device *dev,
-                                            const char *cell_id,
-                                            size_t max_len, size_t *len)
+static const void *nvmem_cell_read_variable_common(struct device *dev,
+                                                  const char *cell_id,
+                                                  size_t max_len, size_t *len)
 {
        struct nvmem_cell *cell;
        int nbits;
@@ -1652,7 +1659,7 @@ int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id,
                                    u32 *val)
 {
        size_t len;
-       u8 *buf;
+       const u8 *buf;
        int i;
 
        buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len);
@@ -1683,7 +1690,7 @@ int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id,
                                    u64 *val)
 {
        size_t len;
-       u8 *buf;
+       const u8 *buf;
        int i;
 
        buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len);
index d6d3f24..81fbad5 100644 (file)
@@ -122,6 +122,7 @@ static const struct qfprom_soc_compatible_data sc7280_qfprom = {
        .keepout = sc7280_qfprom_keepout,
        .nkeepout = ARRAY_SIZE(sc7280_qfprom_keepout)
 };
+
 /**
  * qfprom_disable_fuse_blowing() - Undo enabling of fuse blowing.
  * @priv: Our driver data.
@@ -195,9 +196,9 @@ static int qfprom_enable_fuse_blowing(const struct qfprom_priv *priv,
        }
 
        /*
-        * Hardware requires 1.8V min for fuse blowing; this may be
-        * a rail shared do don't specify a max--regulator constraints
-        * will handle.
+        * Hardware requires a minimum voltage for fuse blowing.
+        * This may be a shared rail so don't specify a maximum.
+        * Regulator constraints will cap to the actual maximum.
         */
        ret = regulator_set_voltage(priv->vcc, qfprom_blow_uV, INT_MAX);
        if (ret) {
@@ -399,7 +400,7 @@ static int qfprom_probe(struct platform_device *pdev)
 
                if (major_version == 7 && minor_version == 8)
                        priv->soc_data = &qfprom_7_8_data;
-               if (major_version == 7 && minor_version == 15)
+               else if (major_version == 7 && minor_version == 15)
                        priv->soc_data = &qfprom_7_15_data;
 
                priv->vcc = devm_regulator_get(&pdev->dev, "vcc");
index 5952324..4f1fcbf 100644 (file)
@@ -234,7 +234,7 @@ static int sprd_efuse_raw_prog(struct sprd_efuse *efuse, u32 blk, bool doub,
        status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG);
        if (status) {
                dev_err(efuse->dev,
-                       "write error status %d of block %d\n", ret, blk);
+                       "write error status %u of block %d\n", status, blk);
 
                writel(SPRD_EFUSE_ERR_CLR_MASK,
                       efuse->base + SPRD_EFUSE_ERR_CLR);
index e26ef1b..275b915 100644 (file)
@@ -142,6 +142,7 @@ static int sunxi_sid_probe(struct platform_device *pdev)
 
        nvmem_cfg->dev = dev;
        nvmem_cfg->name = "sunxi-sid";
+       nvmem_cfg->type = NVMEM_TYPE_OTP;
        nvmem_cfg->read_only = true;
        nvmem_cfg->size = cfg->size;
        nvmem_cfg->word_size = 1;
index 7e6d713..5d1b9aa 100644 (file)
@@ -8,8 +8,8 @@
 
 #include <linux/module.h>
 #include <linux/parport.h>
-#include <linux/ctype.h>
 #include <linux/string.h>
+#include <linux/string_helpers.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 
@@ -74,11 +74,7 @@ static void parse_data(struct parport *port, int device, char *str)
                        u = sep + strlen (sep) - 1;
                        while (u >= p && *u == ' ')
                                *u-- = '\0';
-                       u = p;
-                       while (*u) {
-                               *u = toupper(*u);
-                               u++;
-                       }
+                       string_upper(p, p);
                        if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
                                kfree(info->mfr);
                                info->mfr = kstrdup(sep, GFP_KERNEL);
@@ -90,8 +86,7 @@ static void parse_data(struct parport *port, int device, char *str)
 
                                kfree(info->class_name);
                                info->class_name = kstrdup(sep, GFP_KERNEL);
-                               for (u = sep; *u; u++)
-                                       *u = toupper(*u);
+                               string_upper(sep, sep);
                                for (i = 0; classes[i].token; i++) {
                                        if (!strcmp(classes[i].token, sep)) {
                                                info->class = i;
index 785a796..1ae458c 100644 (file)
@@ -57,21 +57,20 @@ static const struct proc_ops isapnp_proc_bus_proc_ops = {
 static int isapnp_proc_attach_device(struct pnp_dev *dev)
 {
        struct pnp_card *bus = dev->card;
-       struct proc_dir_entry *de, *e;
        char name[16];
 
-       if (!(de = bus->procdir)) {
+       if (!bus->procdir) {
                sprintf(name, "%02x", bus->number);
-               de = bus->procdir = proc_mkdir(name, isapnp_proc_bus_dir);
-               if (!de)
+               bus->procdir = proc_mkdir(name, isapnp_proc_bus_dir);
+               if (!bus->procdir)
                        return -ENOMEM;
        }
        sprintf(name, "%02x", dev->number);
-       e = dev->procent = proc_create_data(name, S_IFREG | S_IRUGO, de,
+       dev->procent = proc_create_data(name, S_IFREG | S_IRUGO, bus->procdir,
                                            &isapnp_proc_bus_proc_ops, dev);
-       if (!e)
+       if (!dev->procent)
                return -ENOMEM;
-       proc_set_size(e, 256);
+       proc_set_size(dev->procent, 256);
        return 0;
 }
 
index 0a3a71e..0c9b291 100644 (file)
@@ -668,7 +668,7 @@ free_port:
  *
  * Return: status of removal
  */
-static int vcc_remove(struct vio_dev *vdev)
+static void vcc_remove(struct vio_dev *vdev)
 {
        struct vcc_port *port = dev_get_drvdata(&vdev->dev);
 
@@ -703,8 +703,6 @@ static int vcc_remove(struct vio_dev *vdev)
                kfree(port->domain);
                kfree(port);
        }
-
-       return 0;
 }
 
 static const struct vio_device_id vcc_match[] = {
index 5531f3a..2e16c53 100644 (file)
@@ -18,7 +18,7 @@ config UIO_CIF
        depends on PCI
        help
          Driver for Hilscher CIF DeviceNet and Profibus cards.  This
-         driver requires a userspace component called cif that handles
+         driver requires a userspace component called cif that handles
          all of the heavy lifting and can be found at:
                <http://www.osadl.org/projects/downloads/UIO/user/>
 
index 32357f8..64eafd5 100644 (file)
@@ -133,7 +133,7 @@ static void remove(struct pci_dev *pdev)
        uio_unregister_device(info);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       iounmap(info->priv);
+       pci_iounmap(pdev, info->priv);
 }
 
 static struct pci_driver pci_driver = {
index 3bb0b00..e03f9b5 100644 (file)
@@ -72,7 +72,9 @@ static int probe(struct pci_dev *pdev,
                           const struct pci_device_id *id)
 {
        struct uio_pci_generic_dev *gdev;
+       struct uio_mem *uiomem;
        int err;
+       int i;
 
        err = pcim_enable_device(pdev);
        if (err) {
@@ -101,6 +103,36 @@ static int probe(struct pci_dev *pdev,
                         "no support for interrupts?\n");
        }
 
+       uiomem = &gdev->info.mem[0];
+       for (i = 0; i < MAX_UIO_MAPS; ++i) {
+               struct resource *r = &pdev->resource[i];
+
+               if (r->flags != (IORESOURCE_SIZEALIGN | IORESOURCE_MEM))
+                       continue;
+
+               if (uiomem >= &gdev->info.mem[MAX_UIO_MAPS]) {
+                       dev_warn(
+                               &pdev->dev,
+                               "device has more than " __stringify(
+                                       MAX_UIO_MAPS) " I/O memory resources.\n");
+                       break;
+               }
+
+               uiomem->memtype = UIO_MEM_PHYS;
+               uiomem->addr = r->start & PAGE_MASK;
+               uiomem->offs = r->start & ~PAGE_MASK;
+               uiomem->size =
+                       (uiomem->offs + resource_size(r) + PAGE_SIZE - 1) &
+                       PAGE_MASK;
+               uiomem->name = r->name;
+               ++uiomem;
+       }
+
+       while (uiomem < &gdev->info.mem[MAX_UIO_MAPS]) {
+               uiomem->size = 0;
+               ++uiomem;
+       }
+
        return devm_uio_register_device(&pdev->dev, &gdev->info);
 }
 
index 4f02db6..7506b59 100644 (file)
@@ -2209,7 +2209,6 @@ config FB_SIMPLE
 config FB_SSD1307
        tristate "Solomon SSD1307 framebuffer support"
        depends on FB && I2C
-       depends on OF
        depends on GPIOLIB || COMPILE_TEST
        select FB_SYS_FOPS
        select FB_SYS_FILLRECT
index cb1eb7e..5668cad 100644 (file)
@@ -1561,7 +1561,7 @@ schedule_out:
 
 static int visorchipset_init(struct acpi_device *acpi_device)
 {
-       int err = -ENODEV;
+       int err = -ENOMEM;
        struct visorchannel *controlvm_channel;
 
        chipset_dev = kzalloc(sizeof(*chipset_dev), GFP_KERNEL);
@@ -1584,8 +1584,10 @@ static int visorchipset_init(struct acpi_device *acpi_device)
                                 "controlvm",
                                 sizeof(struct visor_controlvm_channel),
                                 VISOR_CONTROLVM_CHANNEL_VERSIONID,
-                                VISOR_CHANNEL_SIGNATURE))
+                                VISOR_CHANNEL_SIGNATURE)) {
+               err = -ENODEV;
                goto error_delete_groups;
+       }
        /* if booting in a crash kernel */
        if (is_kdump_kernel())
                INIT_DELAYED_WORK(&chipset_dev->periodic_controlvm_work,
index b471779..6c962e8 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/**
+/*
  * ds2482.c - provides i2c to w1-master bridge(s)
  * Copyright (C) 2005  Ben Gardner <bgardner@wabtec.com>
  *
@@ -19,7 +19,7 @@
 
 #include <linux/w1.h>
 
-/**
+/*
  * Allow the active pullup to be disabled, default is enabled.
  *
  * Note from the DS2482 datasheet:
@@ -39,7 +39,7 @@ static int extra_config;
 module_param(extra_config, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8=1WS");
 
-/**
+/*
  * The DS2482 registers - there are 3 registers that are addressed by a read
  * pointer. The read pointer is set by the last command executed.
  *
@@ -62,7 +62,7 @@ MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8
 #define DS2482_PTR_CODE_CHANNEL                0xD2    /* DS2482-800 only */
 #define DS2482_PTR_CODE_CONFIG         0xC3
 
-/**
+/*
  * Configure Register bit definitions
  * The top 4 bits always read 0.
  * To write, the top nibble must be the 1's compl. of the low nibble.
@@ -73,7 +73,7 @@ MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8
 #define DS2482_REG_CFG_APU             0x01    /* active pull-up */
 
 
-/**
+/*
  * Write and verify codes for the CHANNEL_SELECT command (DS2482-800 only).
  * To set the channel, write the value at the index of the channel.
  * Read and compare against the corresponding value to verify the change.
@@ -84,7 +84,7 @@ static const u8 ds2482_chan_rd[8] =
        { 0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87 };
 
 
-/**
+/*
  * Status Register bit definitions (read only)
  */
 #define DS2482_REG_STS_DIR             0x80
@@ -124,9 +124,9 @@ struct ds2482_data {
 
 
 /**
- * Helper to calculate values for configuration register
- * @param conf the raw config value
- * @return the value w/ complements that can be written to register
+ * ds2482_calculate_config - Helper to calculate values for configuration register
+ * @conf: the raw config value
+ * Return: the value w/ complements that can be written to register
  */
 static inline u8 ds2482_calculate_config(u8 conf)
 {
@@ -140,10 +140,10 @@ static inline u8 ds2482_calculate_config(u8 conf)
 
 
 /**
- * Sets the read pointer.
- * @param pdev         The ds2482 client pointer
- * @param read_ptr     see DS2482_PTR_CODE_xxx above
- * @return -1 on failure, 0 on success
+ * ds2482_select_register - Sets the read pointer.
+ * @pdev:              The ds2482 client pointer
+ * @read_ptr:  see DS2482_PTR_CODE_xxx above
+ * Return: -1 on failure, 0 on success
  */
 static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr)
 {
@@ -159,12 +159,12 @@ static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr)
 }
 
 /**
- * Sends a command without a parameter
- * @param pdev The ds2482 client pointer
- * @param cmd  DS2482_CMD_RESET,
+ * ds2482_send_cmd - Sends a command without a parameter
+ * @pdev:      The ds2482 client pointer
+ * @cmd:       DS2482_CMD_RESET,
  *             DS2482_CMD_1WIRE_RESET,
  *             DS2482_CMD_1WIRE_READ_BYTE
- * @return -1 on failure, 0 on success
+ * Return: -1 on failure, 0 on success
  */
 static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd)
 {
@@ -176,14 +176,14 @@ static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd)
 }
 
 /**
- * Sends a command with a parameter
- * @param pdev The ds2482 client pointer
- * @param cmd  DS2482_CMD_WRITE_CONFIG,
+ * ds2482_send_cmd_data - Sends a command with a parameter
+ * @pdev:      The ds2482 client pointer
+ * @cmd:       DS2482_CMD_WRITE_CONFIG,
  *             DS2482_CMD_1WIRE_SINGLE_BIT,
  *             DS2482_CMD_1WIRE_WRITE_BYTE,
  *             DS2482_CMD_1WIRE_TRIPLET
- * @param byte The data to send
- * @return -1 on failure, 0 on success
+ * @byte:      The data to send
+ * Return: -1 on failure, 0 on success
  */
 static inline int ds2482_send_cmd_data(struct ds2482_data *pdev,
                                       u8 cmd, u8 byte)
@@ -205,10 +205,10 @@ static inline int ds2482_send_cmd_data(struct ds2482_data *pdev,
 #define DS2482_WAIT_IDLE_TIMEOUT       100
 
 /**
- * Waits until the 1-wire interface is idle (not busy)
+ * ds2482_wait_1wire_idle - Waits until the 1-wire interface is idle (not busy)
  *
- * @param pdev Pointer to the device structure
- * @return the last value read from status or -1 (failure)
+ * @pdev: Pointer to the device structure
+ * Return: the last value read from status or -1 (failure)
  */
 static int ds2482_wait_1wire_idle(struct ds2482_data *pdev)
 {
@@ -230,12 +230,12 @@ static int ds2482_wait_1wire_idle(struct ds2482_data *pdev)
 }
 
 /**
- * Selects a w1 channel.
+ * ds2482_set_channel - Selects a w1 channel.
  * The 1-wire interface must be idle before calling this function.
  *
- * @param pdev         The ds2482 client pointer
- * @param channel      0-7
- * @return             -1 (failure) or 0 (success)
+ * @pdev:              The ds2482 client pointer
+ * @channel:           0-7
+ * Return:             -1 (failure) or 0 (success)
  */
 static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel)
 {
@@ -254,11 +254,11 @@ static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel)
 
 
 /**
- * Performs the touch-bit function, which writes a 0 or 1 and reads the level.
+ * ds2482_w1_touch_bit - Performs the touch-bit function, which writes a 0 or 1 and reads the level.
  *
- * @param data The ds2482 channel pointer
- * @param bit  The level to write: 0 or non-zero
- * @return     The level read: 0 or 1
+ * @data:      The ds2482 channel pointer
+ * @bit:       The level to write: 0 or non-zero
+ * Return:     The level read: 0 or 1
  */
 static u8 ds2482_w1_touch_bit(void *data, u8 bit)
 {
@@ -284,13 +284,13 @@ static u8 ds2482_w1_touch_bit(void *data, u8 bit)
 }
 
 /**
- * Performs the triplet function, which reads two bits and writes a bit.
+ * ds2482_w1_triplet - Performs the triplet function, which reads two bits and writes a bit.
  * The bit written is determined by the two reads:
  *   00 => dbit, 01 => 0, 10 => 1
  *
- * @param data The ds2482 channel pointer
- * @param dbit The direction to choose if both branches are valid
- * @return     b0=read1 b1=read2 b3=bit written
+ * @data:      The ds2482 channel pointer
+ * @dbit:      The direction to choose if both branches are valid
+ * Return:     b0=read1 b1=read2 b3=bit written
  */
 static u8 ds2482_w1_triplet(void *data, u8 dbit)
 {
@@ -317,10 +317,10 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit)
 }
 
 /**
- * Performs the write byte function.
+ * ds2482_w1_write_byte - Performs the write byte function.
  *
- * @param data The ds2482 channel pointer
- * @param byte The value to write
+ * @data:      The ds2482 channel pointer
+ * @byte:      The value to write
  */
 static void ds2482_w1_write_byte(void *data, u8 byte)
 {
@@ -341,10 +341,10 @@ static void ds2482_w1_write_byte(void *data, u8 byte)
 }
 
 /**
- * Performs the read byte function.
+ * ds2482_w1_read_byte - Performs the read byte function.
  *
- * @param data The ds2482 channel pointer
- * @return     The value read
+ * @data:      The ds2482 channel pointer
+ * Return:     The value read
  */
 static u8 ds2482_w1_read_byte(void *data)
 {
@@ -378,10 +378,10 @@ static u8 ds2482_w1_read_byte(void *data)
 
 
 /**
- * Sends a reset on the 1-wire interface
+ * ds2482_w1_reset_bus - Sends a reset on the 1-wire interface
  *
- * @param data The ds2482 channel pointer
- * @return     0=Device present, 1=No device present or error
+ * @data:      The ds2482 channel pointer
+ * Return:     0=Device present, 1=No device present or error
  */
 static u8 ds2482_w1_reset_bus(void *data)
 {
@@ -541,7 +541,7 @@ static int ds2482_remove(struct i2c_client *client)
        return 0;
 }
 
-/**
+/*
  * Driver data (common to all clients)
  */
 static const struct i2c_device_id ds2482_id[] = {
index 5cfb0ae..ca64f99 100644 (file)
 #define DS2438_CURRENT_MSB             0x06
 #define DS2438_THRESHOLD               0x07
 
+/* Page #1 definitions */
+#define DS2438_ETM_0                   0x00
+#define DS2438_ETM_1                   0x01
+#define DS2438_ETM_2                   0x02
+#define DS2438_ETM_3                   0x03
+#define DS2438_ICA                     0x04
+#define DS2438_OFFSET_LSB              0x05
+#define DS2438_OFFSET_MSB              0x06
+
 static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf)
 {
        unsigned int retries = W1_DS2438_RETRIES;
@@ -62,13 +71,13 @@ static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf)
                if (w1_reset_select_slave(sl))
                        continue;
                w1_buf[0] = W1_DS2438_RECALL_MEMORY;
-               w1_buf[1] = 0x00;
+               w1_buf[1] = (u8)pageno;
                w1_write_block(sl->master, w1_buf, 2);
 
                if (w1_reset_select_slave(sl))
                        continue;
                w1_buf[0] = W1_DS2438_READ_SCRATCH;
-               w1_buf[1] = 0x00;
+               w1_buf[1] = (u8)pageno;
                w1_write_block(sl->master, w1_buf, 2);
 
                count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1);
@@ -154,11 +163,11 @@ static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)
 
                if ((status & mask) == value)
                        return 0;       /* already set as requested */
-               else {
-                       /* changing bit */
-                       status ^= mask;
-                       perform_write = 1;
-               }
+
+               /* changing bit */
+               status ^= mask;
+               perform_write = 1;
+
                break;
        }
 
@@ -184,6 +193,34 @@ static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)
        return -1;
 }
 
+static int w1_ds2438_change_offset_register(struct w1_slave *sl, u8 *value)
+{
+       unsigned int retries = W1_DS2438_RETRIES;
+       u8 w1_buf[9];
+       u8 w1_page1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+
+       if (w1_ds2438_get_page(sl, 1, w1_page1_buf) == 0) {
+               memcpy(&w1_buf[2], w1_page1_buf, DS2438_PAGE_SIZE - 1); /* last register reserved */
+               w1_buf[7] = value[0]; /* change only offset register */
+               w1_buf[8] = value[1];
+               while (retries--) {
+                       if (w1_reset_select_slave(sl))
+                               continue;
+                       w1_buf[0] = W1_DS2438_WRITE_SCRATCH;
+                       w1_buf[1] = 0x01; /* write to page 1 */
+                       w1_write_block(sl->master, w1_buf, 9);
+
+                       if (w1_reset_select_slave(sl))
+                               continue;
+                       w1_buf[0] = W1_DS2438_COPY_SCRATCH;
+                       w1_buf[1] = 0x01;
+                       w1_write_block(sl->master, w1_buf, 2);
+                       return 0;
+               }
+       }
+       return -1;
+}
+
 static int w1_ds2438_get_voltage(struct w1_slave *sl,
                                 int adc_input, uint16_t *voltage)
 {
@@ -287,9 +324,9 @@ static ssize_t iad_read(struct file *filp, struct kobject *kobj,
        if (!buf)
                return -EINVAL;
 
-       if (w1_ds2438_get_current(sl, &voltage) == 0) {
+       if (w1_ds2438_get_current(sl, &voltage) == 0)
                ret = snprintf(buf, count, "%i\n", voltage);
-       else
+       else
                ret = -EIO;
 
        return ret;
@@ -325,6 +362,55 @@ static ssize_t page0_read(struct file *filp, struct kobject *kobj,
        return ret;
 }
 
+static ssize_t page1_read(struct file *filp, struct kobject *kobj,
+                         struct bin_attribute *bin_attr, char *buf,
+                         loff_t off, size_t count)
+{
+       struct w1_slave *sl = kobj_to_w1_slave(kobj);
+       int ret;
+       u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
+
+       if (off != 0)
+               return 0;
+       if (!buf)
+               return -EINVAL;
+
+       mutex_lock(&sl->master->bus_mutex);
+
+       /* Read no more than page1 size */
+       if (count > DS2438_PAGE_SIZE)
+               count = DS2438_PAGE_SIZE;
+
+       if (w1_ds2438_get_page(sl, 1, w1_buf) == 0) {
+               memcpy(buf, &w1_buf, count);
+               ret = count;
+       } else
+               ret = -EIO;
+
+       mutex_unlock(&sl->master->bus_mutex);
+
+       return ret;
+}
+
+static ssize_t offset_write(struct file *filp, struct kobject *kobj,
+                           struct bin_attribute *bin_attr, char *buf,
+                           loff_t off, size_t count)
+{
+       struct w1_slave *sl = kobj_to_w1_slave(kobj);
+       int ret;
+
+       mutex_lock(&sl->master->bus_mutex);
+
+       if (w1_ds2438_change_offset_register(sl, buf) == 0)
+               ret = count;
+       else
+               ret = -EIO;
+
+       mutex_unlock(&sl->master->bus_mutex);
+
+       return ret;
+}
+
 static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
                                struct bin_attribute *bin_attr, char *buf,
                                loff_t off, size_t count)
@@ -338,9 +424,9 @@ static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
        if (!buf)
                return -EINVAL;
 
-       if (w1_ds2438_get_temperature(sl, &temp) == 0) {
+       if (w1_ds2438_get_temperature(sl, &temp) == 0)
                ret = snprintf(buf, count, "%i\n", temp);
-       else
+       else
                ret = -EIO;
 
        return ret;
@@ -359,9 +445,9 @@ static ssize_t vad_read(struct file *filp, struct kobject *kobj,
        if (!buf)
                return -EINVAL;
 
-       if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) {
+       if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0)
                ret = snprintf(buf, count, "%u\n", voltage);
-       else
+       else
                ret = -EIO;
 
        return ret;
@@ -380,16 +466,18 @@ static ssize_t vdd_read(struct file *filp, struct kobject *kobj,
        if (!buf)
                return -EINVAL;
 
-       if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) {
+       if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0)
                ret = snprintf(buf, count, "%u\n", voltage);
-       else
+       else
                ret = -EIO;
 
        return ret;
 }
 
-static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, iad_read, iad_write, 0);
+static BIN_ATTR_RW(iad, 0);
 static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);
+static BIN_ATTR_RO(page1, DS2438_PAGE_SIZE);
+static BIN_ATTR_WO(offset, 2);
 static BIN_ATTR_RO(temperature, 0/* real length varies */);
 static BIN_ATTR_RO(vad, 0/* real length varies */);
 static BIN_ATTR_RO(vdd, 0/* real length varies */);
@@ -397,6 +485,8 @@ static BIN_ATTR_RO(vdd, 0/* real length varies */);
 static struct bin_attribute *w1_ds2438_bin_attrs[] = {
        &bin_attr_iad,
        &bin_attr_page0,
+       &bin_attr_page1,
+       &bin_attr_offset,
        &bin_attr_temperature,
        &bin_attr_vad,
        &bin_attr_vdd,
index 9d08a1c..ca70c5f 100644 (file)
@@ -834,7 +834,7 @@ static int check_family_data(struct w1_slave *sl)
 }
 
 /**
- * support_bulk_read() - check if slave support bulk read
+ * bulk_read_support() - check if slave support bulk read
  * @sl: device to check the ability
  *
  * Return: true if bulk read is supported, false if not or error
@@ -2056,7 +2056,6 @@ static ssize_t w1_seq_show(struct device *device,
 {
        struct w1_slave *sl = dev_to_w1_slave(device);
        ssize_t c = PAGE_SIZE;
-       int rv;
        int i;
        u8 ack;
        u64 rn;
@@ -2084,7 +2083,7 @@ static ssize_t w1_seq_show(struct device *device,
                        goto error;
 
                w1_write_8(sl->master, W1_42_COND_READ);
-               rv = w1_read_block(sl->master, (u8 *)&rn, 8);
+               w1_read_block(sl->master, (u8 *)&rn, 8);
                reg_num = (struct w1_reg_num *) &rn;
                if (reg_num->family == W1_42_FINISHED_BYTE)
                        break;
index 6cc4d4c..15c448e 100644 (file)
@@ -1669,7 +1669,7 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
  * Does not take i_mutex for the write and thus is not for general purpose
  * use.
  */
-ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
+static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct inode *bd_inode = bdev_file_inode(file);
@@ -1707,9 +1707,8 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
        blk_finish_plug(&plug);
        return ret;
 }
-EXPORT_SYMBOL_GPL(blkdev_write_iter);
 
-ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
+static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
        struct file *file = iocb->ki_filp;
        struct inode *bd_inode = bdev_file_inode(file);
@@ -1731,7 +1730,6 @@ ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
        iov_iter_reexpand(to, iov_iter_count(to) + shorted);
        return ret;
 }
-EXPORT_SYMBOL_GPL(blkdev_read_iter);
 
 /*
  * Try to release a page associated with block device when the system
index 99580c2..34c2175 100644 (file)
@@ -10,6 +10,9 @@ struct eeprom_93xx46_platform_data {
 #define EE_ADDR8       0x01            /*  8 bit addr. cfg */
 #define EE_ADDR16      0x02            /* 16 bit addr. cfg */
 #define EE_READONLY    0x08            /* forbid writing */
+#define EE_SIZE1K      0x10            /* 1 kb of data, that is a 93xx46 */
+#define EE_SIZE2K      0x20            /* 2 kb of data, that is a 93xx56 */
+#define EE_SIZE4K      0x40            /* 4 kb of data, that is a 93xx66 */
 
        unsigned int    quirks;
 /* Single word read transfers only; no sequential read. */
index 817600a..6c3c288 100644 (file)
@@ -11,7 +11,7 @@ struct fpga_bridge;
 /**
  * struct fpga_bridge_ops - ops for low level FPGA bridge drivers
  * @enable_show: returns the FPGA bridge's status
- * @enable_set: set a FPGA bridge as enabled or disabled
+ * @enable_set: set an FPGA bridge as enabled or disabled
  * @fpga_bridge_remove: set FPGA into a specific state during driver remove
  * @groups: optional attribute groups.
  */
index 2bc3030..ec2cd8b 100644 (file)
@@ -75,7 +75,7 @@ enum fpga_mgr_states {
 #define FPGA_MGR_COMPRESSED_BITSTREAM  BIT(4)
 
 /**
- * struct fpga_image_info - information specific to a FPGA image
+ * struct fpga_image_info - information specific to an FPGA image
  * @flags: boolean flags as defined above
  * @enable_timeout_us: maximum time to enable traffic through bridge (uSec)
  * @disable_timeout_us: maximum time to disable traffic through bridge (uSec)
index c3c88fd..8652ed7 100644 (file)
@@ -3242,11 +3242,8 @@ ssize_t vfs_iocb_iter_write(struct file *file, struct kiocb *iocb,
                            struct iov_iter *iter);
 
 /* fs/block_dev.c */
-extern ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to);
-extern ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from);
 extern int blkdev_fsync(struct file *filp, loff_t start, loff_t end,
                        int datasync);
-extern void block_sync_page(struct page *page);
 
 /* fs/splice.c */
 extern ssize_t generic_file_splice_read(struct file *, loff_t *,
index e162b75..8900035 100644 (file)
@@ -25,6 +25,7 @@ enum nvmem_type {
        NVMEM_TYPE_EEPROM,
        NVMEM_TYPE_OTP,
        NVMEM_TYPE_BATTERY_BACKED,
+       NVMEM_TYPE_FRAM,
 };
 
 #define NVMEM_DEVID_NONE       (-1)
index d76a1dd..a12556a 100644 (file)
@@ -162,6 +162,12 @@ static const struct attribute_group _name##_group = {              \
 };                                                             \
 __ATTRIBUTE_GROUPS(_name)
 
+#define BIN_ATTRIBUTE_GROUPS(_name)                            \
+static const struct attribute_group _name##_group = {          \
+       .bin_attrs = _name##_attrs,                             \
+};                                                             \
+__ATTRIBUTE_GROUPS(_name)
+
 struct file;
 struct vm_area_struct;
 struct address_space;
diff --git a/include/uapi/linux/raw.h b/include/uapi/linux/raw.h
deleted file mode 100644 (file)
index 4787491..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef __LINUX_RAW_H
-#define __LINUX_RAW_H
-
-#include <linux/types.h>
-
-#define RAW_SETBIND    _IO( 0xac, 0 )
-#define RAW_GETBIND    _IO( 0xac, 1 )
-
-struct raw_config_request 
-{
-       int     raw_minor;
-       __u64   block_major;
-       __u64   block_minor;
-};
-
-#endif /* __LINUX_RAW_H */
index 641767b..d3ce782 100644 (file)
@@ -1117,9 +1117,9 @@ static int __init dynamic_debug_init(void)
                goto out_err;
 
        ddebug_init_success = 1;
-       vpr_info("%d modules, %d entries and %d bytes in ddebug tables, %d bytes in __dyndbg section\n",
-                modct, entries, (int)(modct * sizeof(struct ddebug_table)),
-                (int)(entries * sizeof(struct _ddebug)));
+       vpr_info("%d prdebugs in %d modules, %d KiB in ddebug tables, %d kiB in __dyndbg section\n",
+                entries, modct, (int)((modct * sizeof(struct ddebug_table)) >> 10),
+                (int)((entries * sizeof(struct _ddebug)) >> 10));
 
        /* apply ddebug_query boot param, dont unload tables on err */
        if (ddebug_setup_string[0] != '\0') {