]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'char-misc-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregk...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 5 Apr 2018 03:07:20 +0000 (20:07 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 5 Apr 2018 03:07:20 +0000 (20:07 -0700)
Pull char/misc updates from Greg KH:
 "Here is the big set of char/misc driver patches for 4.17-rc1.

  There are a lot of little things in here, nothing huge, but all
  important to the different hardware types involved:

   -  thunderbolt driver updates

   -  parport updates (people still care...)

   -  nvmem driver updates

   -  mei updates (as always)

   -  hwtracing driver updates

   -  hyperv driver updates

   -  extcon driver updates

   -  ... and a handful of even smaller driver subsystem and individual
      driver updates

  All of these have been in linux-next with no reported issues"

* tag 'char-misc-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (149 commits)
  hwtracing: Add HW tracing support menu
  intel_th: Add ACPI glue layer
  intel_th: Allow forcing host mode through drvdata
  intel_th: Pick up irq number from resources
  intel_th: Don't touch switch routing in host mode
  intel_th: Use correct method of finding hub
  intel_th: Add SPDX GPL-2.0 header to replace GPLv2 boilerplate
  stm class: Make dummy's master/channel ranges configurable
  stm class: Add SPDX GPL-2.0 header to replace GPLv2 boilerplate
  MAINTAINERS: Bestow upon myself the care for drivers/hwtracing
  hv: add SPDX license id to Kconfig
  hv: add SPDX license to trace
  Drivers: hv: vmbus: do not mark HV_PCIE as perf_device
  Drivers: hv: vmbus: respect what we get from hv_get_synint_state()
  /dev/mem: Avoid overwriting "err" in read_mem()
  eeprom: at24: use SPDX identifier instead of GPL boiler-plate
  eeprom: at24: simplify the i2c functionality checking
  eeprom: at24: fix a line break
  eeprom: at24: tweak newlines
  eeprom: at24: refactor at24_probe()
  ...

141 files changed:
Documentation/ABI/stable/sysfs-bus-vmbus
Documentation/ABI/testing/sysfs-bus-thunderbolt
Documentation/ABI/testing/sysfs-class-mei
Documentation/ABI/testing/sysfs-driver-fsi-master-gpio [new file with mode: 0644]
Documentation/admin-guide/thunderbolt.rst
Documentation/devicetree/bindings/connector/samsung,usb-connector-11pin.txt [new file with mode: 0644]
Documentation/devicetree/bindings/connector/usb-connector.txt [new file with mode: 0644]
Documentation/devicetree/bindings/fsi/fsi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/aspeed-lpc.txt
Documentation/devicetree/bindings/nvmem/imx-ocotp.txt
Documentation/devicetree/bindings/nvmem/snvs-lpgpr.txt
Documentation/driver-api/uio-howto.rst
MAINTAINERS
arch/x86/entry/entry_32.S
arch/x86/entry/entry_64.S
arch/x86/include/asm/hardirq.h
arch/x86/include/asm/irq_vectors.h
arch/x86/include/asm/mshyperv.h
arch/x86/include/uapi/asm/hyperv.h
arch/x86/kernel/cpu/mshyperv.c
arch/x86/kernel/irq.c
block/genhd.c
drivers/Kconfig
drivers/char/Kconfig
drivers/char/mem.c
drivers/char/xillybus/xillybus_pcie.c
drivers/extcon/extcon-gpio.c
drivers/extcon/extcon-intel-cht-wc.c
drivers/extcon/extcon-intel-int3496.c
drivers/extcon/extcon.c
drivers/fpga/altera-cvp.c
drivers/fsi/Kconfig
drivers/fsi/fsi-core.c
drivers/fsi/fsi-master-gpio.c
drivers/fsi/fsi-master-hub.c
drivers/fsi/fsi-master.h
drivers/gpu/drm/bridge/sil-sii8620.c
drivers/hv/Kconfig
drivers/hv/Makefile
drivers/hv/channel_mgmt.c
drivers/hv/hv.c
drivers/hv/hv_balloon.c
drivers/hv/hv_trace.c
drivers/hv/hv_trace.h
drivers/hv/hv_trace_balloon.h [new file with mode: 0644]
drivers/hv/hyperv_vmbus.h
drivers/hwtracing/Kconfig [new file with mode: 0644]
drivers/hwtracing/coresight/coresight-cpu-debug.c
drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
drivers/hwtracing/intel_th/Kconfig
drivers/hwtracing/intel_th/Makefile
drivers/hwtracing/intel_th/acpi.c [new file with mode: 0644]
drivers/hwtracing/intel_th/core.c
drivers/hwtracing/intel_th/debug.c
drivers/hwtracing/intel_th/debug.h
drivers/hwtracing/intel_th/gth.c
drivers/hwtracing/intel_th/gth.h
drivers/hwtracing/intel_th/intel_th.h
drivers/hwtracing/intel_th/msu.c
drivers/hwtracing/intel_th/msu.h
drivers/hwtracing/intel_th/pci.c
drivers/hwtracing/intel_th/pti.c
drivers/hwtracing/intel_th/pti.h
drivers/hwtracing/intel_th/sth.c
drivers/hwtracing/intel_th/sth.h
drivers/hwtracing/stm/console.c
drivers/hwtracing/stm/core.c
drivers/hwtracing/stm/dummy_stm.c
drivers/hwtracing/stm/heartbeat.c
drivers/hwtracing/stm/policy.c
drivers/hwtracing/stm/stm.h
drivers/mcb/mcb-pci.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/aspeed-lpc-ctrl.c
drivers/misc/cardreader/rts5260.c
drivers/misc/eeprom/at24.c
drivers/misc/eeprom/at25.c
drivers/misc/lkdtm/Makefile [new file with mode: 0644]
drivers/misc/lkdtm/bugs.c [moved from drivers/misc/lkdtm_bugs.c with 100% similarity]
drivers/misc/lkdtm/core.c [moved from drivers/misc/lkdtm_core.c with 100% similarity]
drivers/misc/lkdtm/heap.c [moved from drivers/misc/lkdtm_heap.c with 100% similarity]
drivers/misc/lkdtm/lkdtm.h [moved from drivers/misc/lkdtm.h with 100% similarity]
drivers/misc/lkdtm/perms.c [moved from drivers/misc/lkdtm_perms.c with 100% similarity]
drivers/misc/lkdtm/refcount.c [moved from drivers/misc/lkdtm_refcount.c with 99% similarity]
drivers/misc/lkdtm/rodata.c [moved from drivers/misc/lkdtm_rodata.c with 100% similarity]
drivers/misc/lkdtm/usercopy.c [moved from drivers/misc/lkdtm_usercopy.c with 100% similarity]
drivers/misc/mei/bus.c
drivers/misc/mei/client.c
drivers/misc/mei/debugfs.c
drivers/misc/mei/init.c
drivers/misc/mei/main.c
drivers/misc/mei/mei_dev.h
drivers/misc/mic/bus/vop_bus.c
drivers/misc/ocxl/pci.c
drivers/nvmem/Kconfig
drivers/nvmem/bcm-ocotp.c
drivers/nvmem/core.c
drivers/nvmem/imx-iim.c
drivers/nvmem/imx-ocotp.c
drivers/nvmem/lpc18xx_otp.c
drivers/nvmem/meson-efuse.c
drivers/nvmem/meson-mx-efuse.c
drivers/nvmem/mtk-efuse.c
drivers/nvmem/qfprom.c
drivers/nvmem/rockchip-efuse.c
drivers/nvmem/snvs_lpgpr.c
drivers/nvmem/sunxi_sid.c
drivers/nvmem/uniphier-efuse.c
drivers/nvmem/vf610-ocotp.c
drivers/parport/parport_ax88796.c
drivers/parport/parport_pc.c
drivers/parport/parport_serial.c
drivers/pps/clients/pps_parport.c
drivers/pps/generators/pps_gen_parport.c
drivers/siox/siox-core.c
drivers/slimbus/core.c
drivers/thunderbolt/dma_port.c
drivers/thunderbolt/domain.c
drivers/thunderbolt/icm.c
drivers/thunderbolt/nhi.c
drivers/thunderbolt/nhi.h
drivers/thunderbolt/switch.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/tb_msgs.h
drivers/thunderbolt/xdomain.c
drivers/uio/uio_hv_generic.c
drivers/w1/w1.c
fs/char_dev.c
include/linux/extcon.h
include/linux/extcon/extcon-gpio.h [deleted file]
include/linux/hyperv.h
include/linux/nvmem-provider.h
include/linux/pci_ids.h
include/linux/stm.h
include/linux/thunderbolt.h
include/uapi/linux/stm.h
lib/devres.c
tools/hv/hv_fcopy_daemon.c
tools/hv/hv_kvp_daemon.c
tools/hv/hv_vss_daemon.c

index e46be65d0e1d2fd3525111f8670d79d37dc39e53..0c9d9dcd2151d183e46c2f37a4392061424ca76b 100644 (file)
@@ -132,3 +132,10 @@ KernelVersion:     4.16
 Contact:       Stephen Hemminger <sthemmin@microsoft.com>
 Description:   Monitor bit associated with channel
 Users:         Debugging tools and userspace drivers
+
+What:          /sys/bus/vmbus/devices/vmbus_*/channels/NN/ring
+Date:          January. 2018
+KernelVersion: 4.16
+Contact:       Stephen Hemminger <sthemmin@microsoft.com>
+Description:   Binary file created by uio_hv_generic for ring buffer
+Users:         Userspace drivers
index 93798c02e28b28c171c0d00bedd1444c3553ed64..151584a1f9504d5fb00dc8c6190a0aa479ad2287 100644 (file)
@@ -1,3 +1,26 @@
+What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl
+Date:          Jun 2018
+KernelVersion: 4.17
+Contact:       thunderbolt-software@lists.01.org
+Description:   Holds a comma separated list of device unique_ids that
+               are allowed to be connected automatically during system
+               startup (e.g boot devices). The list always contains
+               maximum supported number of unique_ids where unused
+               entries are empty. This allows the userspace software
+               to determine how many entries the controller supports.
+               If there are multiple controllers, each controller has
+               its own ACL list and size may be different between the
+               controllers.
+
+               System BIOS may have an option "Preboot ACL" or similar
+               that needs to be selected before this list is taken into
+               consideration.
+
+               Software always updates a full list in each write.
+
+               If a device is authorized automatically during boot its
+               boot attribute is set to 1.
+
 What: /sys/bus/thunderbolt/devices/.../domainX/security
 Date:          Sep 2017
 KernelVersion: 4.13
@@ -12,6 +35,9 @@ Description:  This attribute holds current Thunderbolt security level
                        minimum. User needs to authorize each device.
                dponly: Automatically tunnel Display port (and USB). No
                        PCIe tunnels are created.
+               usbonly: Automatically tunnel USB controller of the
+                        connected Thunderbolt dock (and Display Port). All
+                        PCIe links downstream of the dock are removed.
 
 What: /sys/bus/thunderbolt/devices/.../authorized
 Date:          Sep 2017
@@ -38,6 +64,13 @@ Description: This attribute is used to authorize Thunderbolt devices
                   the device did not contain a key at all, and
                   EKEYREJECTED if the challenge response did not match.
 
+What: /sys/bus/thunderbolt/devices/.../boot
+Date:          Jun 2018
+KernelVersion: 4.17
+Contact:       thunderbolt-software@lists.01.org
+Description:   This attribute contains 1 if Thunderbolt device was already
+               authorized on boot and 0 otherwise.
+
 What: /sys/bus/thunderbolt/devices/.../key
 Date:          Sep 2017
 KernelVersion: 4.13
index 5096a82f4cde6c0ceac7597fcdbdc1b979f5d8c8..81ff6abf967397c6034633ff4f31e51712c4bb20 100644 (file)
@@ -45,3 +45,12 @@ Contact:     Tomas Winkler <tomas.winkler@intel.com>
 Description:   Display the driver HBM protocol version.
 
                The HBM protocol version supported by the driver.
+
+What:          /sys/class/mei/meiN/tx_queue_limit
+Date:          Jan 2018
+KernelVersion: 4.16
+Contact:       Tomas Winkler <tomas.winkler@intel.com>
+Description:   Configure tx queue limit
+
+               Set maximal number of pending writes
+               per opened session.
diff --git a/Documentation/ABI/testing/sysfs-driver-fsi-master-gpio b/Documentation/ABI/testing/sysfs-driver-fsi-master-gpio
new file mode 100644 (file)
index 0000000..1f29c88
--- /dev/null
@@ -0,0 +1,10 @@
+What:           /sys/bus/platform/devices/[..]/fsi-master-gpio/external_mode
+Date:           Feb 2018
+KernelVersion:  4.17
+Contact:        jk@ozlabs.org
+Description:
+                Controls access arbitration for GPIO-based FSI master. A
+               value of 0 (the default) sets normal mode, where the
+               driver performs FSI bus transactions, 1 sets external mode,
+               where the FSI bus is driven externally (for example, by
+               a debug device).
index 9948ec36a204f9927821f529f57353b046efee38..35fccba6a9a6b4d247e590f8a9e535203015b2b4 100644 (file)
@@ -21,11 +21,11 @@ vulnerable to DMA attacks.
 Security levels and how to use them
 -----------------------------------
 Starting with Intel Falcon Ridge Thunderbolt controller there are 4
-security levels available. The reason for these is the fact that the
-connected devices can be DMA masters and thus read contents of the host
-memory without CPU and OS knowing about it. There are ways to prevent
-this by setting up an IOMMU but it is not always available for various
-reasons.
+security levels available. Intel Titan Ridge added one more security level
+(usbonly). The reason for these is the fact that the connected devices can
+be DMA masters and thus read contents of the host memory without CPU and OS
+knowing about it. There are ways to prevent this by setting up an IOMMU but
+it is not always available for various reasons.
 
 The security levels are as follows:
 
@@ -52,6 +52,11 @@ The security levels are as follows:
     USB. No PCIe tunneling is done. In BIOS settings this is
     typically called *Display Port Only*.
 
+  usbonly
+    The firmware automatically creates tunnels for the USB controller and
+    Display Port in a dock. All PCIe links downstream of the dock are
+    removed.
+
 The current security level can be read from
 ``/sys/bus/thunderbolt/devices/domainX/security`` where ``domainX`` is
 the Thunderbolt domain the host controller manages. There is typically
diff --git a/Documentation/devicetree/bindings/connector/samsung,usb-connector-11pin.txt b/Documentation/devicetree/bindings/connector/samsung,usb-connector-11pin.txt
new file mode 100644 (file)
index 0000000..22256e2
--- /dev/null
@@ -0,0 +1,49 @@
+Samsung micro-USB 11-pin connector
+==================================
+
+Samsung micro-USB 11-pin connector is an extension of micro-USB connector.
+It is present in multiple Samsung mobile devices.
+It has additional pins to route MHL traffic simultanously with USB.
+
+The bindings are superset of usb-connector bindings for micro-USB connector[1].
+
+Required properties:
+- compatible: must be: "samsung,usb-connector-11pin", "usb-b-connector",
+- type: must be "micro".
+
+Required nodes:
+- any data bus to the connector should be modeled using the OF graph bindings
+  specified in bindings/graph.txt, unless the bus is between parent node and
+  the connector. Since single connector can have multpile data buses every bus
+  has assigned OF graph port number as follows:
+    0: High Speed (HS),
+    3: Mobile High-Definition Link (MHL), specific to 11-pin Samsung micro-USB.
+
+[1]: bindings/connector/usb-connector.txt
+
+Example
+-------
+
+Micro-USB connector with HS lines routed via controller (MUIC) and MHL lines
+connected to HDMI-MHL bridge (sii8620):
+
+muic-max77843@66 {
+       ...
+       usb_con: connector {
+               compatible = "samsung,usb-connector-11pin", "usb-b-connector";
+               label = "micro-USB";
+               type = "micro";
+
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port@3 {
+                               reg = <3>;
+                               usb_con_mhl: endpoint {
+                                       remote-endpoint = <&sii8620_mhl>;
+                               };
+                       };
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt
new file mode 100644 (file)
index 0000000..e1463f1
--- /dev/null
@@ -0,0 +1,75 @@
+USB Connector
+=============
+
+USB connector node represents physical USB connector. It should be
+a child of USB interface controller.
+
+Required properties:
+- compatible: describes type of the connector, must be one of:
+    "usb-a-connector",
+    "usb-b-connector",
+    "usb-c-connector".
+
+Optional properties:
+- label: symbolic name for the connector,
+- type: size of the connector, should be specified in case of USB-A, USB-B
+  non-fullsize connectors: "mini", "micro".
+
+Required nodes:
+- any data bus to the connector should be modeled using the OF graph bindings
+  specified in bindings/graph.txt, unless the bus is between parent node and
+  the connector. Since single connector can have multpile data buses every bus
+  has assigned OF graph port number as follows:
+    0: High Speed (HS), present in all connectors,
+    1: Super Speed (SS), present in SS capable connectors,
+    2: Sideband use (SBU), present in USB-C.
+
+Examples
+--------
+
+1. Micro-USB connector with HS lines routed via controller (MUIC):
+
+muic-max77843@66 {
+       ...
+       usb_con: connector {
+               compatible = "usb-b-connector";
+               label = "micro-USB";
+               type = "micro";
+       };
+};
+
+2. USB-C connector attached to CC controller (s2mm005), HS lines routed
+to companion PMIC (max77865), SS lines to USB3 PHY and SBU to DisplayPort.
+DisplayPort video lines are routed to the connector via SS mux in USB3 PHY.
+
+ccic: s2mm005@33 {
+       ...
+       usb_con: connector {
+               compatible = "usb-c-connector";
+               label = "USB-C";
+
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port@0 {
+                               reg = <0>;
+                               usb_con_hs: endpoint {
+                                       remote-endpoint = <&max77865_usbc_hs>;
+                               };
+                       };
+                       port@1 {
+                               reg = <1>;
+                               usb_con_ss: endpoint {
+                                       remote-endpoint = <&usbdrd_phy_ss>;
+                               };
+                       };
+                       port@2 {
+                               reg = <2>;
+                               usb_con_sbu: endpoint {
+                                       remote-endpoint = <&dp_aux>;
+                               };
+                       };
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/fsi/fsi.txt b/Documentation/devicetree/bindings/fsi/fsi.txt
new file mode 100644 (file)
index 0000000..ab516c6
--- /dev/null
@@ -0,0 +1,151 @@
+FSI bus & engine generic device tree bindings
+=============================================
+
+The FSI bus is probe-able, so the OS is able to enumerate FSI slaves, and
+engines within those slaves. However, we have a facility to match devicetree
+nodes to probed engines. This allows for fsi engines to expose non-probeable
+busses, which are then exposed by the device tree. For example, an FSI engine
+that is an I2C master - the I2C bus can be described by the device tree under
+the engine's device tree node.
+
+FSI masters may require their own DT nodes (to describe the master HW itself);
+that requirement is defined by the master's implementation, and is described by
+the fsi-master-* binding specifications.
+
+Under the masters' nodes, we can describe the bus topology using nodes to
+represent the FSI slaves and their slave engines. As a basic outline:
+
+  fsi-master {
+      /* top-level of FSI bus topology, bound to an FSI master driver and
+       * exposes an FSI bus */
+
+      fsi-slave@<link,id> {
+          /* this node defines the FSI slave device, and is handled
+           * entirely with FSI core code */
+
+          fsi-slave-engine@<addr> {
+              /* this node defines the engine endpoint & address range, which
+               * is bound to the relevant fsi device driver */
+               ...
+          };
+
+          fsi-slave-engine@<addr> {
+              ...
+          };
+
+      };
+  };
+
+Note that since the bus is probe-able, some (or all) of the topology may
+not be described; this binding only provides an optional facility for
+adding subordinate device tree nodes as children of FSI engines.
+
+FSI masters
+-----------
+
+FSI master nodes declare themselves as such with the "fsi-master" compatible
+value. It's likely that an implementation-specific compatible value will
+be needed as well, for example:
+
+    compatible = "fsi-master-gpio", "fsi-master";
+
+Since the master nodes describe the top-level of the FSI topology, they also
+need to declare the FSI-standard addressing scheme. This requires two cells for
+addresses (link index and slave ID), and no size:
+
+    #address-cells = <2>;
+    #size-cells = <0>;
+
+An optional boolean property can be added to indicate that a particular master
+should not scan for connected devices at initialization time.  This is
+necessary in cases where a scan could cause arbitration issues with other
+masters that may be present on the bus.
+
+    no-scan-on-init;
+
+FSI slaves
+----------
+
+Slaves are identified by a (link-index, slave-id) pair, so require two cells
+for an address identifier. Since these are not a range, no size cells are
+required. For an example, a slave on link 1, with ID 2, could be represented
+as:
+
+    cfam@1,2 {
+        reg = <1 2>;
+       [...];
+    }
+
+Each slave provides an address-space, under which the engines are accessible.
+That address space has a maximum of 23 bits, so we use one cell to represent
+addresses and sizes in the slave address space:
+
+    #address-cells = <1>;
+    #size-cells = <1>;
+
+
+FSI engines (devices)
+---------------------
+
+Engines are identified by their address under the slaves' address spaces. We
+use a single cell for address and size. Engine nodes represent the endpoint
+FSI device, and are passed to those FSI device drivers' ->probe() functions.
+
+For example, for a slave using a single 0x400-byte page starting at address
+0xc00:
+
+    engine@c00 {
+        reg = <0xc00 0x400>;
+    };
+
+
+Full example
+------------
+
+Here's an example that illustrates:
+ - an FSI master
+   - connected to an FSI slave
+     - that contains an engine that is an I2C master
+       - connected to an I2C EEPROM
+
+The FSI master may be connected to additional slaves, and slaves may have
+additional engines, but they don't necessarily need to be describe in the
+device tree if no extra platform information is required.
+
+    /* The GPIO-based FSI master node, describing the top level of the
+     * FSI bus
+     */
+    gpio-fsi {
+        compatible = "fsi-master-gpio", "fsi-master";
+        #address-cells = <2>;
+        #size-cells = <0>;
+
+        /* A FSI slave (aka. CFAM) at link 0, ID 0. */
+        cfam@0,0 {
+            reg = <0 0>;
+            #address-cells = <1>;
+            #size-cells = <1>;
+
+            /* FSI engine at 0xc00, using a single page. In this example,
+             * it's an I2C master controller, so subnodes describe the
+             * I2C bus.
+             */
+            i2c-controller@c00 {
+                reg = <0xc00 0x400>;
+
+                /* Engine-specific data. In this case, we're describing an
+                 * I2C bus, so we're conforming to the generic I2C binding
+                 */
+                compatible = "some-vendor,fsi-i2c-controller";
+                #address-cells = <1>;
+                #size-cells = <1>;
+
+                /* I2C endpoint device: an Atmel EEPROM */
+                eeprom@50 {
+                    compatible = "atmel,24c256";
+                    reg = <0x50>;
+                    pagesize = <64>;
+                };
+            };
+        };
+    };
index 514d82ced95bbed3792ab642cafc67af0353cd8d..69aadee00d5f6594c92d499cb5d341837f67bd14 100644 (file)
@@ -109,9 +109,50 @@ lpc: lpc@1e789000 {
        };
 };
 
+BMC Node Children
+==================
+
+
 Host Node Children
 ==================
 
+LPC Host Interface Controller
+-------------------
+
+The LPC Host Interface Controller manages functions exposed to the host such as
+LPC firmware hub cycles, configuration of the LPC-to-AHB mapping, UART
+management and bus snoop configuration.
+
+Required properties:
+
+- compatible:  One of:
+               "aspeed,ast2400-lpc-ctrl";
+               "aspeed,ast2500-lpc-ctrl";
+
+- reg:         contains offset/length values of the host interface controller
+               memory regions
+
+- clocks:      contains a phandle to the syscon node describing the clocks.
+               There should then be one cell representing the clock to use
+
+- memory-region: A phandle to a reserved_memory region to be used for the LPC
+               to AHB mapping
+
+- flash:       A phandle to the SPI flash controller containing the flash to
+               be exposed over the LPC to AHB mapping
+
+Example:
+
+lpc-host@80 {
+       lpc_ctrl: lpc-ctrl@0 {
+               compatible = "aspeed,ast2500-lpc-ctrl";
+               reg = <0x0 0x80>;
+               clocks = <&syscon ASPEED_CLK_GATE_LCLK>;
+               memory-region = <&flash_memory>;
+               flash = <&spi>;
+       };
+};
+
 LPC Host Controller
 -------------------
 
index f162c72b4e3655b9625532c01781699c273480e7..729f6747813bbca58d7a9e72a0ffb42647323e41 100644 (file)
@@ -11,17 +11,32 @@ Required properties:
        "fsl,imx6ul-ocotp" (i.MX6UL),
        "fsl,imx7d-ocotp" (i.MX7D/S),
        followed by "syscon".
+- #address-cells : Should be 1
+- #size-cells : Should be 1
 - reg: Should contain the register base and length.
 - clocks: Should contain a phandle pointing to the gated peripheral clock.
 
 Optional properties:
 - read-only: disable write access
 
-Example:
+Optional Child nodes:
+
+- Data cells of ocotp:
+  Detailed bindings are described in bindings/nvmem/nvmem.txt
 
+Example:
        ocotp: ocotp@21bc000 {
-               compatible = "fsl,imx6q-ocotp", "syscon";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "fsl,imx6sx-ocotp", "syscon";
                reg = <0x021bc000 0x4000>;
-               clocks = <&clks IMX6QDL_CLK_IIM>;
-               read-only;
+               clocks = <&clks IMX6SX_CLK_OCOTP>;
+
+               tempmon_calib: calib@38 {
+                       reg = <0x38 4>;
+               };
+
+               tempmon_temp_grade: temp-grade@20 {
+                       reg = <0x20 4>;
+               };
        };
index 20bc49b49799dfa75b68ce910fd5f2d1b5700aad..3cb170896658699e8fd89deb4bca0bd5d0065297 100644 (file)
@@ -1,5 +1,5 @@
 Device tree bindings for Low Power General Purpose Register found in i.MX6Q/D
-Secure Non-Volatile Storage.
+and i.MX7 Secure Non-Volatile Storage.
 
 This DT node should be represented as a sub-node of a "syscon",
 "simple-mfd" node.
@@ -8,6 +8,7 @@ Required properties:
 - compatible: should be one of the fallowing variants:
        "fsl,imx6q-snvs-lpgpr" for Freescale i.MX6Q/D/DL/S
        "fsl,imx6ul-snvs-lpgpr" for Freescale i.MX6UL
+       "fsl,imx7d-snvs-lpgpr" for Freescale i.MX7D/S
 
 Example:
 snvs: snvs@020cc000 {
index 693e3bd84e79ae48df6519f03b7de330c488b038..92056c20e070649debad6b84b4442413c1611678 100644 (file)
@@ -709,6 +709,11 @@ The vmbus device regions are mapped into uio device resources:
     3) Network receive buffer region
     4) Network send buffer region
 
+If a subchannel is created by a request to host, then the uio_hv_generic
+device driver will create a sysfs binary file for the per-channel ring buffer.
+For example:
+       /sys/bus/vmbus/devices/3811fe4d-0fa0-4b62-981a-74fc1084c757/channels/21/ring
+
 Further information
 ===================
 
index 30bc6e417a035925e05546237f8219388b9fd4f7..89fd8040e29521f37487317c16650ed2c4cad172 100644 (file)
@@ -6212,6 +6212,11 @@ F:       Documentation/hw_random.txt
 F:     drivers/char/hw_random/
 F:     include/linux/hw_random.h
 
+HARDWARE TRACING FACILITIES
+M:     Alexander Shishkin <alexander.shishkin@linux.intel.com>
+S:     Maintained
+F:     drivers/hwtracing/
+
 HARDWARE SPINLOCK CORE
 M:     Ohad Ben-Cohen <ohad@wizery.com>
 M:     Bjorn Andersson <bjorn.andersson@linaro.org>
@@ -8140,7 +8145,7 @@ F:        drivers/*/*/*pasemi*
 LINUX KERNEL DUMP TEST MODULE (LKDTM)
 M:     Kees Cook <keescook@chromium.org>
 S:     Maintained
-F:     drivers/misc/lkdtm*
+F:     drivers/misc/lkdtm/*
 
 LINUX KERNEL MEMORY CONSISTENCY MODEL (LKMM)
 M:     Alan Stern <stern@rowland.harvard.edu>
index 6ad064c8cf35e6fdfc9c384212a6f76c780ffd69..bef8e2b202a8c0a608fb3dad7e38fb58889ee88e 100644 (file)
@@ -902,6 +902,9 @@ BUILD_INTERRUPT3(hyperv_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
 BUILD_INTERRUPT3(hyperv_reenlightenment_vector, HYPERV_REENLIGHTENMENT_VECTOR,
                 hyperv_reenlightenment_intr)
 
+BUILD_INTERRUPT3(hv_stimer0_callback_vector, HYPERV_STIMER0_VECTOR,
+                hv_stimer0_vector_handler)
+
 #endif /* CONFIG_HYPERV */
 
 ENTRY(page_fault)
index 936e19642eabba289a18be842814f31ffd490b11..b0a4649e55ce8185d4cd0f5c8b778750ff067f90 100644 (file)
@@ -1140,6 +1140,9 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
 
 apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \
        hyperv_reenlightenment_vector hyperv_reenlightenment_intr
+
+apicinterrupt3 HYPERV_STIMER0_VECTOR \
+       hv_stimer0_callback_vector hv_stimer0_vector_handler
 #endif /* CONFIG_HYPERV */
 
 idtentry debug                 do_debug                has_error_code=0        paranoid=1 shift_ist=DEBUG_STACK
index 7c341a74ec8c49c090257d9aa192bc8f6afa9517..5ea2afd4c8719cea73919db1241cdb093119d03e 100644 (file)
@@ -40,6 +40,7 @@ typedef struct {
 #endif
 #if IS_ENABLED(CONFIG_HYPERV)
        unsigned int irq_hv_reenlightenment_count;
+       unsigned int hyperv_stimer0_count;
 #endif
 } ____cacheline_aligned irq_cpustat_t;
 
index e71c1120426bbc9652539cae486762dd98098f19..404c5fdff859ded0e55325ac601ced0332910aac 100644 (file)
 
 #if IS_ENABLED(CONFIG_HYPERV)
 #define HYPERV_REENLIGHTENMENT_VECTOR  0xee
+#define HYPERV_STIMER0_VECTOR          0xed
 #endif
 
-#define LOCAL_TIMER_VECTOR             0xed
+#define LOCAL_TIMER_VECTOR             0xec
 
 #define NR_VECTORS                      256
 
index 25283f7eb299e06ab703c11f7a2f95c0425f6a95..e73c4d0c06adae0215e1799e93113405b31fcbf5 100644 (file)
@@ -173,6 +173,19 @@ void hv_remove_kexec_handler(void);
 void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
 void hv_remove_crash_handler(void);
 
+/*
+ * Routines for stimer0 Direct Mode handling.
+ * On x86/x64, there are no percpu actions to take.
+ */
+void hv_stimer0_vector_handler(struct pt_regs *regs);
+void hv_stimer0_callback_vector(void);
+int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void));
+void hv_remove_stimer0_irq(int irq);
+
+static inline void hv_enable_stimer0_percpu_irq(int irq) {}
+static inline void hv_disable_stimer0_percpu_irq(int irq) {}
+
+
 #if IS_ENABLED(CONFIG_HYPERV)
 extern struct clocksource *hyperv_cs;
 extern void *hv_hypercall_pg;
index 099414345865d588e6c55b63078f8b168fc8e907..6c0c3a3b631cdab34d6bf82bae79c34e64503b8f 100644 (file)
@@ -77,6 +77,9 @@
 /* Crash MSR available */
 #define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10)
 
+/* stimer Direct Mode is available */
+#define HV_X64_STIMER_DIRECT_MODE_AVAILABLE    (1 << 19)
+
 /*
  * Feature identification: EBX indicates which flags were specified at
  * partition creation. The format is the same as the partition creation
index 9340f41ce8d3d374ff4453638d88759ec5f72064..4488cf0dd499aa589f2ba250198c8f9b2f0e43e7 100644 (file)
@@ -37,6 +37,7 @@ EXPORT_SYMBOL_GPL(ms_hyperv);
 
 #if IS_ENABLED(CONFIG_HYPERV)
 static void (*vmbus_handler)(void);
+static void (*hv_stimer0_handler)(void);
 static void (*hv_kexec_handler)(void);
 static void (*hv_crash_handler)(struct pt_regs *regs);
 
@@ -69,6 +70,41 @@ void hv_remove_vmbus_irq(void)
 EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
 EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
 
+/*
+ * Routines to do per-architecture handling of stimer0
+ * interrupts when in Direct Mode
+ */
+
+__visible void __irq_entry hv_stimer0_vector_handler(struct pt_regs *regs)
+{
+       struct pt_regs *old_regs = set_irq_regs(regs);
+
+       entering_irq();
+       inc_irq_stat(hyperv_stimer0_count);
+       if (hv_stimer0_handler)
+               hv_stimer0_handler();
+       ack_APIC_irq();
+
+       exiting_irq();
+       set_irq_regs(old_regs);
+}
+
+int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void))
+{
+       *vector = HYPERV_STIMER0_VECTOR;
+       *irq = 0;   /* Unused on x86/x64 */
+       hv_stimer0_handler = handler;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq);
+
+void hv_remove_stimer0_irq(int irq)
+{
+       /* We have no way to deallocate the interrupt gate */
+       hv_stimer0_handler = NULL;
+}
+EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq);
+
 void hv_setup_kexec_handler(void (*handler)(void))
 {
        hv_kexec_handler = handler;
@@ -257,6 +293,10 @@ static void __init ms_hyperv_init_platform(void)
                alloc_intr_gate(HYPERV_REENLIGHTENMENT_VECTOR,
                                hyperv_reenlightenment_vector);
 
+       /* Setup the IDT for stimer0 */
+       if (ms_hyperv.misc_features & HV_X64_STIMER_DIRECT_MODE_AVAILABLE)
+               alloc_intr_gate(HYPERV_STIMER0_VECTOR,
+                               hv_stimer0_callback_vector);
 #endif
 }
 
index 45fb4d2565f80a451b04fbf9976f69a165efb2d3..328d027d829d215ef3f3732c9b47d55641746746 100644 (file)
@@ -150,6 +150,13 @@ int arch_show_interrupts(struct seq_file *p, int prec)
                                   irq_stats(j)->irq_hv_reenlightenment_count);
                seq_puts(p, "  Hyper-V reenlightenment interrupts\n");
        }
+       if (test_bit(HYPERV_STIMER0_VECTOR, system_vectors)) {
+               seq_printf(p, "%*s: ", prec, "HVS");
+               for_each_online_cpu(j)
+                       seq_printf(p, "%10u ",
+                                  irq_stats(j)->hyperv_stimer0_count);
+               seq_puts(p, "  Hyper-V stimer0 interrupts\n");
+       }
 #endif
        seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
 #if defined(CONFIG_X86_IO_APIC)
index 9656f9e9f99e20af13f1e175aba15f9447d9ef87..dc7e089373b9444a0b83e176b196e2539e6ff59b 100644 (file)
@@ -308,19 +308,22 @@ void blkdev_show(struct seq_file *seqf, off_t offset)
 /**
  * register_blkdev - register a new block device
  *
- * @major: the requested major device number [1..255]. If @major = 0, try to
- *         allocate any unused major number.
+ * @major: the requested major device number [1..BLKDEV_MAJOR_MAX-1]. If
+ *         @major = 0, try to allocate any unused major number.
  * @name: the name of the new block device as a zero terminated string
  *
  * The @name must be unique within the system.
  *
  * The return value depends on the @major input parameter:
  *
- *  - if a major device number was requested in range [1..255] then the
- *    function returns zero on success, or a negative error code
+ *  - if a major device number was requested in range [1..BLKDEV_MAJOR_MAX-1]
+ *    then the function returns zero on success, or a negative error code
  *  - if any unused major number was requested with @major = 0 parameter
  *    then the return value is the allocated major number in range
- *    [1..255] or a negative error code otherwise
+ *    [1..BLKDEV_MAJOR_MAX-1] or a negative error code otherwise
+ *
+ * See Documentation/admin-guide/devices.txt for the list of allocated
+ * major numbers.
  */
 int register_blkdev(unsigned int major, const char *name)
 {
@@ -347,8 +350,8 @@ int register_blkdev(unsigned int major, const char *name)
        }
 
        if (major >= BLKDEV_MAJOR_MAX) {
-               pr_err("register_blkdev: major requested (%d) is greater than the maximum (%d) for %s\n",
-                      major, BLKDEV_MAJOR_MAX, name);
+               pr_err("register_blkdev: major requested (%u) is greater than the maximum (%u) for %s\n",
+                      major, BLKDEV_MAJOR_MAX-1, name);
 
                ret = -EINVAL;
                goto out;
@@ -375,7 +378,7 @@ int register_blkdev(unsigned int major, const char *name)
                ret = -EBUSY;
 
        if (ret < 0) {
-               printk("register_blkdev: cannot get major %d for %s\n",
+               printk("register_blkdev: cannot get major %u for %s\n",
                       major, name);
                kfree(p);
        }
index 879dc0604cbacb65a23d948ac3aa3a51cda4cdf0..95b9ccc0816561b87e883053ca209f219ff2309d 100644 (file)
@@ -199,9 +199,7 @@ source "drivers/dax/Kconfig"
 
 source "drivers/nvmem/Kconfig"
 
-source "drivers/hwtracing/stm/Kconfig"
-
-source "drivers/hwtracing/intel_th/Kconfig"
+source "drivers/hwtracing/Kconfig"
 
 source "drivers/fpga/Kconfig"
 
index 40947a796666133705e8f82412cf7fa709190f9a..e538061eadcb2ab9b2955bb15ace762576535206 100644 (file)
@@ -236,7 +236,7 @@ source "drivers/char/hw_random/Kconfig"
 
 config NVRAM
        tristate "/dev/nvram support"
-       depends on ATARI || X86 || (ARM && RTC_DRV_CMOS) || GENERIC_NVRAM
+       depends on ATARI || X86 || GENERIC_NVRAM
        ---help---
          If you say Y here and create a character special file /dev/nvram
          with major number 10 and minor number 144 using mknod ("man mknod"),
index 052011bcf10014f086cdc1c54ae47154850eb974..ffeb60d3434c5150650d1ef4c9a3aec3d8ffd59e 100644 (file)
@@ -137,7 +137,7 @@ static ssize_t read_mem(struct file *file, char __user *buf,
 
        while (count > 0) {
                unsigned long remaining;
-               int allowed;
+               int allowed, probe;
 
                sz = size_inside_page(p, count);
 
@@ -160,9 +160,9 @@ static ssize_t read_mem(struct file *file, char __user *buf,
                        if (!ptr)
                                goto failed;
 
-                       err = probe_kernel_read(bounce, ptr, sz);
+                       probe = probe_kernel_read(bounce, ptr, sz);
                        unxlate_dev_mem_ptr(p, ptr);
-                       if (err)
+                       if (probe)
                                goto failed;
 
                        remaining = copy_to_user(buf, bounce, sz);
index dff2d153816471a6eef60b02dc4d1aa136314d7d..05e5324f60bd951f8182b0ce77213df6e6a1151d 100644 (file)
@@ -24,7 +24,6 @@ MODULE_LICENSE("GPL v2");
 
 #define PCI_DEVICE_ID_XILLYBUS         0xebeb
 
-#define PCI_VENDOR_ID_ALTERA           0x1172
 #define PCI_VENDOR_ID_ACTEL            0x11aa
 #define PCI_VENDOR_ID_LATTICE          0x1204
 
index ab770adcca7e32330368cc9d80f963312a226cc1..13ba3a6e81d565e74f65a7c9e2f4db360435134e 100644 (file)
@@ -18,8 +18,6 @@
  */
 
 #include <linux/extcon-provider.h>
-#include <linux/extcon/extcon-gpio.h>
-#include <linux/gpio.h>
 #include <linux/gpio/consumer.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 
+/**
+ * struct gpio_extcon_data - A simple GPIO-controlled extcon device state container.
+ * @edev:              Extcon device.
+ * @irq:               Interrupt line for the external connector.
+ * @work:              Work fired by the interrupt.
+ * @debounce_jiffies:  Number of jiffies to wait for the GPIO to stabilize, from the debounce
+ *                     value.
+ * @gpiod:             GPIO descriptor for this external connector.
+ * @extcon_id:         The unique id of specific external connector.
+ * @debounce:          Debounce time for GPIO IRQ in ms.
+ * @irq_flags:         IRQ Flags (e.g., IRQF_TRIGGER_LOW).
+ * @check_on_resume:   Boolean describing whether to check the state of gpio
+ *                     while resuming from sleep.
+ */
 struct gpio_extcon_data {
        struct extcon_dev *edev;
        int irq;
        struct delayed_work work;
        unsigned long debounce_jiffies;
-
-       struct gpio_desc *id_gpiod;
-       struct gpio_extcon_pdata *pdata;
+       struct gpio_desc *gpiod;
+       unsigned int extcon_id;
+       unsigned long debounce;
+       unsigned long irq_flags;
+       bool check_on_resume;
 };
 
 static void gpio_extcon_work(struct work_struct *work)
@@ -46,11 +60,8 @@ static void gpio_extcon_work(struct work_struct *work)
                container_of(to_delayed_work(work), struct gpio_extcon_data,
                             work);
 
-       state = gpiod_get_value_cansleep(data->id_gpiod);
-       if (data->pdata->gpio_active_low)
-               state = !state;
-
-       extcon_set_state_sync(data->edev, data->pdata->extcon_id, state);
+       state = gpiod_get_value_cansleep(data->gpiod);
+       extcon_set_state_sync(data->edev, data->extcon_id, state);
 }
 
 static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
@@ -62,65 +73,41 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data)
-{
-       struct gpio_extcon_pdata *pdata = data->pdata;
-       int ret;
-
-       ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_DIR_IN,
-                               dev_name(dev));
-       if (ret < 0)
-               return ret;
-
-       data->id_gpiod = gpio_to_desc(pdata->gpio);
-       if (!data->id_gpiod)
-               return -EINVAL;
-
-       if (pdata->debounce) {
-               ret = gpiod_set_debounce(data->id_gpiod,
-                                       pdata->debounce * 1000);
-               if (ret < 0)
-                       data->debounce_jiffies =
-                               msecs_to_jiffies(pdata->debounce);
-       }
-
-       data->irq = gpiod_to_irq(data->id_gpiod);
-       if (data->irq < 0)
-               return data->irq;
-
-       return 0;
-}
-
 static int gpio_extcon_probe(struct platform_device *pdev)
 {
-       struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev);
        struct gpio_extcon_data *data;
+       struct device *dev = &pdev->dev;
        int ret;
 
-       if (!pdata)
-               return -EBUSY;
-       if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE)
-               return -EINVAL;
-
-       data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
-                                  GFP_KERNEL);
+       data = devm_kzalloc(dev, sizeof(struct gpio_extcon_data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
-       data->pdata = pdata;
 
-       /* Initialize the gpio */
-       ret = gpio_extcon_init(&pdev->dev, data);
-       if (ret < 0)
-               return ret;
+       /*
+        * FIXME: extcon_id represents the unique identifier of external
+        * connectors such as EXTCON_USB, EXTCON_DISP_HDMI and so on. extcon_id
+        * is necessary to register the extcon device. But, it's not yet
+        * developed to get the extcon id from device-tree or others.
+        * On later, it have to be solved.
+        */
+       if (!data->irq_flags || data->extcon_id > EXTCON_NONE)
+               return -EINVAL;
+
+       data->gpiod = devm_gpiod_get(dev, "extcon", GPIOD_IN);
+       if (IS_ERR(data->gpiod))
+               return PTR_ERR(data->gpiod);
+       data->irq = gpiod_to_irq(data->gpiod);
+       if (data->irq <= 0)
+               return data->irq;
 
        /* Allocate the memory of extcon devie and register extcon device */
-       data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id);
+       data->edev = devm_extcon_dev_allocate(dev, &data->extcon_id);
        if (IS_ERR(data->edev)) {
-               dev_err(&pdev->dev, "failed to allocate extcon device\n");
+               dev_err(dev, "failed to allocate extcon device\n");
                return -ENOMEM;
        }
 
-       ret = devm_extcon_dev_register(&pdev->dev, data->edev);
+       ret = devm_extcon_dev_register(dev, data->edev);
        if (ret < 0)
                return ret;
 
@@ -130,8 +117,8 @@ static int gpio_extcon_probe(struct platform_device *pdev)
         * Request the interrupt of gpio to detect whether external connector
         * is attached or detached.
         */
-       ret = devm_request_any_context_irq(&pdev->dev, data->irq,
-                                       gpio_irq_handler, pdata->irq_flags,
+       ret = devm_request_any_context_irq(dev, data->irq,
+                                       gpio_irq_handler, data->irq_flags,
                                        pdev->name, data);
        if (ret < 0)
                return ret;
@@ -158,7 +145,7 @@ static int gpio_extcon_resume(struct device *dev)
        struct gpio_extcon_data *data;
 
        data = dev_get_drvdata(dev);
-       if (data->pdata->check_on_resume)
+       if (data->check_on_resume)
                queue_delayed_work(system_power_efficient_wq,
                        &data->work, data->debounce_jiffies);
 
index 7c4bc8c44c3f0cb5aa86c20f39cee1a8f067c0b2..b7e9ea377d70adaca5e5f576c0e870cd76b10f46 100644 (file)
@@ -66,6 +66,8 @@
 
 #define CHT_WC_VBUS_GPIO_CTLO          0x6e2d
 #define CHT_WC_VBUS_GPIO_CTLO_OUTPUT   BIT(0)
+#define CHT_WC_VBUS_GPIO_CTLO_DRV_OD   BIT(4)
+#define CHT_WC_VBUS_GPIO_CTLO_DIR_OUT  BIT(5)
 
 enum cht_wc_usb_id {
        USB_ID_OTG,
@@ -183,14 +185,15 @@ static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext,
 {
        int ret, val;
 
-       val = enable ? CHT_WC_VBUS_GPIO_CTLO_OUTPUT : 0;
-
        /*
         * The 5V boost converter is enabled through a gpio on the PMIC, since
         * there currently is no gpio driver we access the gpio reg directly.
         */
-       ret = regmap_update_bits(ext->regmap, CHT_WC_VBUS_GPIO_CTLO,
-                                CHT_WC_VBUS_GPIO_CTLO_OUTPUT, val);
+       val = CHT_WC_VBUS_GPIO_CTLO_DRV_OD | CHT_WC_VBUS_GPIO_CTLO_DIR_OUT;
+       if (enable)
+               val |= CHT_WC_VBUS_GPIO_CTLO_OUTPUT;
+
+       ret = regmap_write(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, val);
        if (ret)
                dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret);
 }
index 191e99f06a9a5d55a2433b3daa31978fa454e533..acaccb128fc41433cdca3f415b7e4f32195d5c78 100644 (file)
@@ -50,7 +50,11 @@ static const struct acpi_gpio_params vbus_gpios = { INT3496_GPIO_VBUS_EN, 0, fal
 static const struct acpi_gpio_params mux_gpios = { INT3496_GPIO_USB_MUX, 0, false };
 
 static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = {
-       { "id-gpios", &id_gpios, 1 },
+       /*
+        * Some platforms have a bug in ACPI GPIO description making IRQ
+        * GPIO to be output only. Ask the GPIO core to ignore this limit.
+        */
+       { "id-gpios", &id_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION },
        { "vbus-gpios", &vbus_gpios, 1 },
        { "mux-gpios", &mux_gpios, 1 },
        { },
@@ -112,9 +116,6 @@ static int int3496_probe(struct platform_device *pdev)
                ret = PTR_ERR(data->gpio_usb_id);
                dev_err(dev, "can't request USB ID GPIO: %d\n", ret);
                return ret;
-       } else if (gpiod_get_direction(data->gpio_usb_id) != GPIOF_DIR_IN) {
-               dev_warn(dev, FW_BUG "USB ID GPIO not in input mode, fixing\n");
-               gpiod_direction_input(data->gpio_usb_id);
        }
 
        data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id);
index cb38c27476847188aaa37051efee606052655b5a..8bff5fd1818516d6b4bc81a239136d926aa4ba47 100644 (file)
@@ -1336,6 +1336,28 @@ void extcon_dev_unregister(struct extcon_dev *edev)
 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
 
 #ifdef CONFIG_OF
+
+/*
+ * extcon_find_edev_by_node - Find the extcon device from devicetree.
+ * @node       : OF node identifying edev
+ *
+ * Return the pointer of extcon device if success or ERR_PTR(err) if fail.
+ */
+struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
+{
+       struct extcon_dev *edev;
+
+       mutex_lock(&extcon_dev_list_lock);
+       list_for_each_entry(edev, &extcon_dev_list, entry)
+               if (edev->dev.parent && edev->dev.parent->of_node == node)
+                       goto out;
+       edev = ERR_PTR(-EPROBE_DEFER);
+out:
+       mutex_unlock(&extcon_dev_list_lock);
+
+       return edev;
+}
+
 /*
  * extcon_get_edev_by_phandle - Get the extcon device from devicetree.
  * @dev                : the instance to the given device
@@ -1363,25 +1385,27 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
                return ERR_PTR(-ENODEV);
        }
 
-       mutex_lock(&extcon_dev_list_lock);
-       list_for_each_entry(edev, &extcon_dev_list, entry) {
-               if (edev->dev.parent && edev->dev.parent->of_node == node) {
-                       mutex_unlock(&extcon_dev_list_lock);
-                       of_node_put(node);
-                       return edev;
-               }
-       }
-       mutex_unlock(&extcon_dev_list_lock);
+       edev = extcon_find_edev_by_node(node);
        of_node_put(node);
 
-       return ERR_PTR(-EPROBE_DEFER);
+       return edev;
 }
+
 #else
+
+struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
 struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
 {
        return ERR_PTR(-ENOSYS);
 }
+
 #endif /* CONFIG_OF */
+
+EXPORT_SYMBOL_GPL(extcon_find_edev_by_node);
 EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle);
 
 /**
index 00e73d28077cee3732b61973222422f152ebb033..77b04e4b32544a3fd7359537c1e0acbe3ccf0f84 100644 (file)
@@ -384,8 +384,6 @@ static int altera_cvp_probe(struct pci_dev *pdev,
                            const struct pci_device_id *dev_id);
 static void altera_cvp_remove(struct pci_dev *pdev);
 
-#define PCI_VENDOR_ID_ALTERA   0x1172
-
 static struct pci_device_id altera_cvp_id_tbl[] = {
        { PCI_VDEVICE(ALTERA, PCI_ANY_ID) },
        { }
index 513e35173aaa195d2c1cdfe1195b415a626e7bcf..a326ed663d3c7e83fc3d908be2e611bd13eb0634 100644 (file)
@@ -4,6 +4,7 @@
 
 menuconfig FSI
        tristate "FSI support"
+       depends on OF
        select CRC4
        ---help---
          FSI - the FRU Support Interface - is a simple bus for low-level
index e318bf8c623c66e33201d1592dba5a9b86f762c7..4c03d6933646f03b8784a8de754b535fcbaba14d 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/fsi.h>
 #include <linux/idr.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/bitops.h>
 
@@ -142,6 +143,7 @@ static void fsi_device_release(struct device *_device)
 {
        struct fsi_device *device = to_fsi_dev(_device);
 
+       of_node_put(device->dev.of_node);
        kfree(device);
 }
 
@@ -205,7 +207,7 @@ static int fsi_slave_report_and_clear_errors(struct fsi_slave *slave)
        if (rc)
                return rc;
 
-       dev_info(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
+       dev_dbg(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
                        be32_to_cpu(stat), be32_to_cpu(irq));
 
        /* clear interrupts */
@@ -334,6 +336,57 @@ extern void fsi_slave_release_range(struct fsi_slave *slave,
 }
 EXPORT_SYMBOL_GPL(fsi_slave_release_range);
 
+static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
+               uint32_t addr, uint32_t size)
+{
+       unsigned int len, na, ns;
+       const __be32 *prop;
+       uint32_t psize;
+
+       na = of_n_addr_cells(np);
+       ns = of_n_size_cells(np);
+
+       if (na != 1 || ns != 1)
+               return false;
+
+       prop = of_get_property(np, "reg", &len);
+       if (!prop || len != 8)
+               return false;
+
+       if (of_read_number(prop, 1) != addr)
+               return false;
+
+       psize = of_read_number(prop + 1, 1);
+       if (psize != size) {
+               dev_warn(dev,
+                       "node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
+                       of_node_full_name(np), psize, size);
+       }
+
+       return true;
+}
+
+/* Find a matching node for the slave engine at @address, using @size bytes
+ * of space. Returns NULL if not found, or a matching node with refcount
+ * already incremented.
+ */
+static struct device_node *fsi_device_find_of_node(struct fsi_device *dev)
+{
+       struct device_node *parent, *np;
+
+       parent = dev_of_node(&dev->slave->dev);
+       if (!parent)
+               return NULL;
+
+       for_each_child_of_node(parent, np) {
+               if (fsi_device_node_matches(&dev->dev, np,
+                                       dev->addr, dev->size))
+                       return np;
+       }
+
+       return NULL;
+}
+
 static int fsi_slave_scan(struct fsi_slave *slave)
 {
        uint32_t engine_addr;
@@ -402,6 +455,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
                        dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
                                        slave->master->idx, slave->link,
                                        slave->id, i - 2);
+                       dev->dev.of_node = fsi_device_find_of_node(dev);
 
                        rc = device_register(&dev->dev);
                        if (rc) {
@@ -558,9 +612,53 @@ static void fsi_slave_release(struct device *dev)
 {
        struct fsi_slave *slave = to_fsi_slave(dev);
 
+       of_node_put(dev->of_node);
        kfree(slave);
 }
 
+static bool fsi_slave_node_matches(struct device_node *np,
+               int link, uint8_t id)
+{
+       unsigned int len, na, ns;
+       const __be32 *prop;
+
+       na = of_n_addr_cells(np);
+       ns = of_n_size_cells(np);
+
+       /* Ensure we have the correct format for addresses and sizes in
+        * reg properties
+        */
+       if (na != 2 || ns != 0)
+               return false;
+
+       prop = of_get_property(np, "reg", &len);
+       if (!prop || len != 8)
+               return false;
+
+       return (of_read_number(prop, 1) == link) &&
+               (of_read_number(prop + 1, 1) == id);
+}
+
+/* Find a matching node for the slave at (link, id). Returns NULL if none
+ * found, or a matching node with refcount already incremented.
+ */
+static struct device_node *fsi_slave_find_of_node(struct fsi_master *master,
+               int link, uint8_t id)
+{
+       struct device_node *parent, *np;
+
+       parent = dev_of_node(&master->dev);
+       if (!parent)
+               return NULL;
+
+       for_each_child_of_node(parent, np) {
+               if (fsi_slave_node_matches(np, link, id))
+                       return np;
+       }
+
+       return NULL;
+}
+
 static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 {
        uint32_t chip_id, llmode;
@@ -589,7 +687,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
                return -EIO;
        }
 
-       dev_info(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
+       dev_dbg(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
                        chip_id, master->idx, link, id);
 
        rc = fsi_slave_set_smode(master, link, id);
@@ -623,6 +721,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 
        slave->master = master;
        slave->dev.parent = &master->dev;
+       slave->dev.of_node = fsi_slave_find_of_node(master, link, id);
        slave->dev.release = fsi_slave_release;
        slave->link = link;
        slave->id = id;
@@ -656,10 +755,13 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 /* FSI master support */
 static int fsi_check_access(uint32_t addr, size_t size)
 {
-       if (size != 1 && size != 2 && size != 4)
-               return -EINVAL;
-
-       if ((addr & 0x3) != (size & 0x3))
+       if (size == 4) {
+               if (addr & 0x3)
+                       return -EINVAL;
+       } else if (size == 2) {
+               if (addr & 0x1)
+                       return -EINVAL;
+       } else if (size != 1)
                return -EINVAL;
 
        return 0;
@@ -762,14 +864,20 @@ static void fsi_master_unscan(struct fsi_master *master)
        device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
 }
 
+int fsi_master_rescan(struct fsi_master *master)
+{
+       fsi_master_unscan(master);
+       return fsi_master_scan(master);
+}
+EXPORT_SYMBOL_GPL(fsi_master_rescan);
+
 static ssize_t master_rescan_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct fsi_master *master = to_fsi_master(dev);
        int rc;
 
-       fsi_master_unscan(master);
-       rc = fsi_master_scan(master);
+       rc = fsi_master_rescan(master);
        if (rc < 0)
                return rc;
 
@@ -793,6 +901,7 @@ static DEVICE_ATTR(break, 0200, NULL, master_break_store);
 int fsi_master_register(struct fsi_master *master)
 {
        int rc;
+       struct device_node *np;
 
        if (!master)
                return -EINVAL;
@@ -820,7 +929,9 @@ int fsi_master_register(struct fsi_master *master)
                return rc;
        }
 
-       fsi_master_scan(master);
+       np = dev_of_node(&master->dev);
+       if (!of_property_read_bool(np, "no-scan-on-init"))
+               fsi_master_scan(master);
 
        return 0;
 }
index ae26187685080563b78f5a16b92baba587a5a89b..3f487449a277027390fe0b24da8ea5147a824098 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
@@ -59,6 +60,7 @@ struct fsi_master_gpio {
        struct gpio_desc        *gpio_trans;    /* Voltage translator */
        struct gpio_desc        *gpio_enable;   /* FSI enable */
        struct gpio_desc        *gpio_mux;      /* Mux control */
+       bool                    external_mode;
 };
 
 #define CREATE_TRACE_POINTS
@@ -411,6 +413,12 @@ static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave,
        int rc;
 
        spin_lock_irqsave(&master->cmd_lock, flags);
+
+       if (master->external_mode) {
+               spin_unlock_irqrestore(&master->cmd_lock, flags);
+               return -EBUSY;
+       }
+
        serial_out(master, cmd);
        echo_delay(master);
        rc = poll_for_response(master, slave, resp_len, resp);
@@ -461,12 +469,18 @@ static int fsi_master_gpio_term(struct fsi_master *_master,
 static int fsi_master_gpio_break(struct fsi_master *_master, int link)
 {
        struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+       unsigned long flags;
 
        if (link != 0)
                return -ENODEV;
 
        trace_fsi_master_gpio_break(master);
 
+       spin_lock_irqsave(&master->cmd_lock, flags);
+       if (master->external_mode) {
+               spin_unlock_irqrestore(&master->cmd_lock, flags);
+               return -EBUSY;
+       }
        set_sda_output(master, 1);
        sda_out(master, 1);
        clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
@@ -475,6 +489,7 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
        echo_delay(master);
        sda_out(master, 1);
        clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+       spin_unlock_irqrestore(&master->cmd_lock, flags);
 
        /* Wait for logic reset to take effect */
        udelay(200);
@@ -494,21 +509,84 @@ static void fsi_master_gpio_init(struct fsi_master_gpio *master)
        clock_zeros(master, FSI_INIT_CLOCKS);
 }
 
+static void fsi_master_gpio_init_external(struct fsi_master_gpio *master)
+{
+       gpiod_direction_output(master->gpio_mux, 0);
+       gpiod_direction_output(master->gpio_trans, 0);
+       gpiod_direction_output(master->gpio_enable, 1);
+       gpiod_direction_input(master->gpio_clk);
+       gpiod_direction_input(master->gpio_data);
+}
+
 static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
 {
        struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+       unsigned long flags;
+       int rc = -EBUSY;
 
        if (link != 0)
                return -ENODEV;
-       gpiod_set_value(master->gpio_enable, 1);
 
-       return 0;
+       spin_lock_irqsave(&master->cmd_lock, flags);
+       if (!master->external_mode) {
+               gpiod_set_value(master->gpio_enable, 1);
+               rc = 0;
+       }
+       spin_unlock_irqrestore(&master->cmd_lock, flags);
+
+       return rc;
+}
+
+static ssize_t external_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct fsi_master_gpio *master = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE - 1, "%u\n",
+                       master->external_mode ? 1 : 0);
 }
 
+static ssize_t external_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct fsi_master_gpio *master = dev_get_drvdata(dev);
+       unsigned long flags, val;
+       bool external_mode;
+       int err;
+
+       err = kstrtoul(buf, 0, &val);
+       if (err)
+               return err;
+
+       external_mode = !!val;
+
+       spin_lock_irqsave(&master->cmd_lock, flags);
+
+       if (external_mode == master->external_mode) {
+               spin_unlock_irqrestore(&master->cmd_lock, flags);
+               return count;
+       }
+
+       master->external_mode = external_mode;
+       if (master->external_mode)
+               fsi_master_gpio_init_external(master);
+       else
+               fsi_master_gpio_init(master);
+       spin_unlock_irqrestore(&master->cmd_lock, flags);
+
+       fsi_master_rescan(&master->master);
+
+       return count;
+}
+
+static DEVICE_ATTR(external_mode, 0664,
+               external_mode_show, external_mode_store);
+
 static int fsi_master_gpio_probe(struct platform_device *pdev)
 {
        struct fsi_master_gpio *master;
        struct gpio_desc *gpio;
+       int rc;
 
        master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
        if (!master)
@@ -516,6 +594,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
 
        master->dev = &pdev->dev;
        master->master.dev.parent = master->dev;
+       master->master.dev.of_node = of_node_get(dev_of_node(master->dev));
 
        gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
        if (IS_ERR(gpio)) {
@@ -565,6 +644,10 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
 
        fsi_master_gpio_init(master);
 
+       rc = device_create_file(&pdev->dev, &dev_attr_external_mode);
+       if (rc)
+               return rc;
+
        return fsi_master_register(&master->master);
 }
 
@@ -583,6 +666,8 @@ static int fsi_master_gpio_remove(struct platform_device *pdev)
                devm_gpiod_put(&pdev->dev, master->gpio_mux);
        fsi_master_unregister(&master->master);
 
+       of_node_put(master->master.dev.of_node);
+
        return 0;
 }
 
index 133b9bff1d650adc6007ca87f42bd83857875024..5885fc4a1ef0ab11523b2d6b0fe0fa9ff67d0e54 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/delay.h>
 #include <linux/fsi.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/slab.h>
 
 #include "fsi-master.h"
@@ -253,7 +254,7 @@ static int hub_master_probe(struct device *dev)
 
        reg = be32_to_cpu(__reg);
        links = (reg >> 8) & 0xff;
-       dev_info(dev, "hub version %08x (%d links)\n", reg, links);
+       dev_dbg(dev, "hub version %08x (%d links)\n", reg, links);
 
        rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
                        FSI_HUB_LINK_SIZE * links);
@@ -274,6 +275,7 @@ static int hub_master_probe(struct device *dev)
 
        hub->master.dev.parent = dev;
        hub->master.dev.release = hub_master_release;
+       hub->master.dev.of_node = of_node_get(dev_of_node(dev));
 
        hub->master.n_links = links;
        hub->master.read = hub_master_read;
@@ -286,10 +288,19 @@ static int hub_master_probe(struct device *dev)
        hub_master_init(hub);
 
        rc = fsi_master_register(&hub->master);
-       if (!rc)
-               return 0;
+       if (rc)
+               goto err_release;
+
+       /* At this point, fsi_master_register performs the device_initialize(),
+        * and holds the sole reference on master.dev. This means the device
+        * will be freed (via ->release) during any subsequent call to
+        * fsi_master_unregister.  We add our own reference to it here, so we
+        * can perform cleanup (in _remove()) without it being freed before
+        * we're ready.
+        */
+       get_device(&hub->master.dev);
+       return 0;
 
-       kfree(hub);
 err_release:
        fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
                        FSI_HUB_LINK_SIZE * links);
@@ -302,6 +313,14 @@ static int hub_master_remove(struct device *dev)
 
        fsi_master_unregister(&hub->master);
        fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
+       of_node_put(hub->master.dev.of_node);
+
+       /*
+        * master.dev will likely be ->release()ed after this, which free()s
+        * the hub
+        */
+       put_device(&hub->master.dev);
+
        return 0;
 }
 
index 12f7b119567da4d4a5fd988e45165b81cc8acefb..ee0b4608602603d739be44b906948a6c146f0861 100644 (file)
@@ -37,7 +37,24 @@ struct fsi_master {
 
 #define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
 
+/**
+ * fsi_master registration & lifetime: the fsi_master_register() and
+ * fsi_master_unregister() functions will take ownership of the master, and
+ * ->dev in particular. The registration path performs a get_device(), which
+ * takes the first reference on the device. Similarly, the unregistration path
+ * performs a put_device(), which may well drop the last reference.
+ *
+ * This means that master implementations *may* need to hold their own
+ * reference (via get_device()) on master->dev. In particular, if the device's
+ * ->release callback frees the fsi_master, then fsi_master_unregister will
+ * invoke this free if no other reference is held.
+ *
+ * The same applies for the error path of fsi_master_register; if the call
+ * fails, dev->release will have been invoked.
+ */
 extern int fsi_master_register(struct fsi_master *master);
 extern void fsi_master_unregister(struct fsi_master *master);
 
+extern int fsi_master_rescan(struct fsi_master *master);
+
 #endif /* DRIVERS_FSI_MASTER_H */
index 86789f8918a4958124f63493770523bc66cfa6a3..7ab36042a822cf6cfec2fc440ef3fbe6e018fa3d 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/extcon.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
@@ -25,6 +26,7 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/of_graph.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
@@ -81,6 +83,10 @@ struct sii8620 {
        struct edid *edid;
        unsigned int gen2_write_burst:1;
        enum sii8620_mt_state mt_state;
+       struct extcon_dev *extcon;
+       struct notifier_block extcon_nb;
+       struct work_struct extcon_wq;
+       int cable_state;
        struct list_head mt_queue;
        struct {
                int r_size;
@@ -2170,6 +2176,77 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
        ctx->rc_dev = rc_dev;
 }
 
+static void sii8620_cable_out(struct sii8620 *ctx)
+{
+       disable_irq(to_i2c_client(ctx->dev)->irq);
+       sii8620_hw_off(ctx);
+}
+
+static void sii8620_extcon_work(struct work_struct *work)
+{
+       struct sii8620 *ctx =
+               container_of(work, struct sii8620, extcon_wq);
+       int state = extcon_get_state(ctx->extcon, EXTCON_DISP_MHL);
+
+       if (state == ctx->cable_state)
+               return;
+
+       ctx->cable_state = state;
+
+       if (state > 0)
+               sii8620_cable_in(ctx);
+       else
+               sii8620_cable_out(ctx);
+}
+
+static int sii8620_extcon_notifier(struct notifier_block *self,
+                       unsigned long event, void *ptr)
+{
+       struct sii8620 *ctx =
+               container_of(self, struct sii8620, extcon_nb);
+
+       schedule_work(&ctx->extcon_wq);
+
+       return NOTIFY_DONE;
+}
+
+static int sii8620_extcon_init(struct sii8620 *ctx)
+{
+       struct extcon_dev *edev;
+       struct device_node *musb, *muic;
+       int ret;
+
+       /* get micro-USB connector node */
+       musb = of_graph_get_remote_node(ctx->dev->of_node, 1, -1);
+       /* next get micro-USB Interface Controller node */
+       muic = of_get_next_parent(musb);
+
+       if (!muic) {
+               dev_info(ctx->dev, "no extcon found, switching to 'always on' mode\n");
+               return 0;
+       }
+
+       edev = extcon_find_edev_by_node(muic);
+       of_node_put(muic);
+       if (IS_ERR(edev)) {
+               if (PTR_ERR(edev) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               dev_err(ctx->dev, "Invalid or missing extcon\n");
+               return PTR_ERR(edev);
+       }
+
+       ctx->extcon = edev;
+       ctx->extcon_nb.notifier_call = sii8620_extcon_notifier;
+       INIT_WORK(&ctx->extcon_wq, sii8620_extcon_work);
+       ret = extcon_register_notifier(edev, EXTCON_DISP_MHL, &ctx->extcon_nb);
+       if (ret) {
+               dev_err(ctx->dev, "failed to register notifier for MHL\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
 {
        return container_of(bridge, struct sii8620, bridge);
@@ -2302,13 +2379,20 @@ static int sii8620_probe(struct i2c_client *client,
        if (ret)
                return ret;
 
+       ret = sii8620_extcon_init(ctx);
+       if (ret < 0) {
+               dev_err(ctx->dev, "failed to initialize EXTCON\n");
+               return ret;
+       }
+
        i2c_set_clientdata(client, ctx);
 
        ctx->bridge.funcs = &sii8620_bridge_funcs;
        ctx->bridge.of_node = dev->of_node;
        drm_bridge_add(&ctx->bridge);
 
-       sii8620_cable_in(ctx);
+       if (!ctx->extcon)
+               sii8620_cable_in(ctx);
 
        return 0;
 }
@@ -2317,8 +2401,15 @@ static int sii8620_remove(struct i2c_client *client)
 {
        struct sii8620 *ctx = i2c_get_clientdata(client);
 
-       disable_irq(to_i2c_client(ctx->dev)->irq);
-       sii8620_hw_off(ctx);
+       if (ctx->extcon) {
+               extcon_unregister_notifier(ctx->extcon, EXTCON_DISP_MHL,
+                                          &ctx->extcon_nb);
+               flush_work(&ctx->extcon_wq);
+               if (ctx->cable_state > 0)
+                       sii8620_cable_out(ctx);
+       } else {
+               sii8620_cable_out(ctx);
+       }
        drm_bridge_remove(&ctx->bridge);
 
        return 0;
index 50b89ea0e60f85934b1ca97c7c179deda9f307da..97954f575c3f691df222aa7102f01b5d296ff893 100644 (file)
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
 menu "Microsoft Hyper-V guest support"
 
 config HYPERV
index 14c22786b519c1e8279d5909085476f566be20f2..a1eec7177c2d7377d31e47a7474cfd9b814eb223 100644 (file)
@@ -4,6 +4,7 @@ obj-$(CONFIG_HYPERV_UTILS)      += hv_utils.o
 obj-$(CONFIG_HYPERV_BALLOON)   += hv_balloon.o
 
 CFLAGS_hv_trace.o = -I$(src)
+CFLAGS_hv_balloon.o = -I$(src)
 
 hv_vmbus-y := vmbus_drv.o \
                 hv.o connection.o channel.o \
index c21020b69114b18648ff83562aa97ab95b3b65b3..ecc2bd275a73e614972065e1693b8a3f92a705b9 100644 (file)
@@ -71,7 +71,7 @@ static const struct vmbus_device vmbus_devs[] = {
        /* PCIE */
        { .dev_type = HV_PCIE,
          HV_PCIE_GUID,
-         .perf_device = true,
+         .perf_device = false,
        },
 
        /* Synthetic Frame Buffer */
@@ -596,10 +596,8 @@ static int next_numa_node_id;
 /*
  * Starting with Win8, we can statically distribute the incoming
  * channel interrupt load by binding a channel to VCPU.
- * We do this in a hierarchical fashion:
- * First distribute the primary channels across available NUMA nodes
- * and then distribute the subchannels amongst the CPUs in the NUMA
- * node assigned to the primary channel.
+ * We distribute the interrupt loads to one or more NUMA nodes based on
+ * the channel's affinity_policy.
  *
  * For pre-win8 hosts or non-performance critical channels we assign the
  * first CPU in the first NUMA node.
index fe96aab9e794fac85b919757c57751bed6cab9c5..8137b3885b99c7933862641dd4ab3911e275a923 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/vmalloc.h>
 #include <linux/hyperv.h>
 #include <linux/version.h>
-#include <linux/interrupt.h>
+#include <linux/random.h>
 #include <linux/clockchips.h>
 #include <asm/hyperv.h>
 #include <asm/mshyperv.h>
@@ -38,6 +38,17 @@ struct hv_context hv_context = {
        .synic_initialized      = false,
 };
 
+/*
+ * If false, we're using the old mechanism for stimer0 interrupts
+ * where it sends a VMbus message when it expires. The old
+ * mechanism is used when running on older versions of Hyper-V
+ * that don't support Direct Mode. While Hyper-V provides
+ * four stimer's per CPU, Linux uses only stimer0.
+ */
+static bool direct_mode_enabled;
+static int stimer0_irq;
+static int stimer0_vector;
+
 #define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */
 #define HV_MAX_MAX_DELTA_TICKS 0xffffffff
 #define HV_MIN_DELTA_TICKS 1
@@ -53,6 +64,8 @@ int hv_init(void)
        if (!hv_context.cpu_context)
                return -ENOMEM;
 
+       direct_mode_enabled = ms_hyperv.misc_features &
+                       HV_X64_STIMER_DIRECT_MODE_AVAILABLE;
        return 0;
 }
 
@@ -91,6 +104,21 @@ int hv_post_message(union hv_connection_id connection_id,
        return status & 0xFFFF;
 }
 
+/*
+ * ISR for when stimer0 is operating in Direct Mode.  Direct Mode
+ * does not use VMbus or any VMbus messages, so process here and not
+ * in the VMbus driver code.
+ */
+
+static void hv_stimer0_isr(void)
+{
+       struct hv_per_cpu_context *hv_cpu;
+
+       hv_cpu = this_cpu_ptr(hv_context.cpu_context);
+       hv_cpu->clk_evt->event_handler(hv_cpu->clk_evt);
+       add_interrupt_randomness(stimer0_vector, 0);
+}
+
 static int hv_ce_set_next_event(unsigned long delta,
                                struct clock_event_device *evt)
 {
@@ -108,6 +136,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt)
 {
        hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0);
        hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0);
+       if (direct_mode_enabled)
+               hv_disable_stimer0_percpu_irq(stimer0_irq);
 
        return 0;
 }
@@ -116,11 +146,26 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
 {
        union hv_timer_config timer_cfg;
 
+       timer_cfg.as_uint64 = 0;
        timer_cfg.enable = 1;
        timer_cfg.auto_enable = 1;
-       timer_cfg.sintx = VMBUS_MESSAGE_SINT;
+       if (direct_mode_enabled) {
+               /*
+                * When it expires, the timer will directly interrupt
+                * on the specified hardware vector/IRQ.
+                */
+               timer_cfg.direct_mode = 1;
+               timer_cfg.apic_vector = stimer0_vector;
+               hv_enable_stimer0_percpu_irq(stimer0_irq);
+       } else {
+               /*
+                * When it expires, the timer will generate a VMbus message,
+                * to be handled by the normal VMbus interrupt handler.
+                */
+               timer_cfg.direct_mode = 0;
+               timer_cfg.sintx = VMBUS_MESSAGE_SINT;
+       }
        hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
-
        return 0;
 }
 
@@ -147,7 +192,7 @@ int hv_synic_alloc(void)
        int cpu;
 
        hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids,
-                                        GFP_ATOMIC);
+                                        GFP_KERNEL);
        if (hv_context.hv_numa_map == NULL) {
                pr_err("Unable to allocate NUMA map\n");
                goto err;
@@ -191,6 +236,11 @@ int hv_synic_alloc(void)
                INIT_LIST_HEAD(&hv_cpu->chan_list);
        }
 
+       if (direct_mode_enabled &&
+           hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector,
+                               hv_stimer0_isr))
+               goto err;
+
        return 0;
 err:
        return -ENOMEM;
@@ -217,7 +267,7 @@ void hv_synic_free(void)
 }
 
 /*
- * hv_synic_init - Initialize the Synthethic Interrupt Controller.
+ * hv_synic_init - Initialize the Synthetic Interrupt Controller.
  *
  * If it is already initialized by another entity (ie x2v shim), we need to
  * retrieve the initialized message and event pages.  Otherwise, we create and
@@ -252,7 +302,6 @@ int hv_synic_init(unsigned int cpu)
        hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
                            shared_sint.as_uint64);
 
-       shared_sint.as_uint64 = 0;
        shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
        shared_sint.masked = false;
        if (ms_hyperv.hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED)
@@ -292,6 +341,9 @@ void hv_synic_clockevents_cleanup(void)
        if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
                return;
 
+       if (direct_mode_enabled)
+               hv_remove_stimer0_irq(stimer0_irq);
+
        for_each_present_cpu(cpu) {
                struct hv_per_cpu_context *hv_cpu
                        = per_cpu_ptr(hv_context.cpu_context, cpu);
index db0e6652d7efc16b26829253668563057a5c3cf1..b3e9f13f8bc339895117925aba730e7346c04f9b 100644 (file)
@@ -34,6 +34,9 @@
 
 #include <linux/hyperv.h>
 
+#define CREATE_TRACE_POINTS
+#include "hv_trace_balloon.h"
+
 /*
  * We begin with definitions supporting the Dynamic Memory protocol
  * with the host.
@@ -576,11 +579,65 @@ static struct hv_dynmem_device dm_device;
 static void post_status(struct hv_dynmem_device *dm);
 
 #ifdef CONFIG_MEMORY_HOTPLUG
+static inline bool has_pfn_is_backed(struct hv_hotadd_state *has,
+                                    unsigned long pfn)
+{
+       struct hv_hotadd_gap *gap;
+
+       /* The page is not backed. */
+       if ((pfn < has->covered_start_pfn) || (pfn >= has->covered_end_pfn))
+               return false;
+
+       /* Check for gaps. */
+       list_for_each_entry(gap, &has->gap_list, list) {
+               if ((pfn >= gap->start_pfn) && (pfn < gap->end_pfn))
+                       return false;
+       }
+
+       return true;
+}
+
+static unsigned long hv_page_offline_check(unsigned long start_pfn,
+                                          unsigned long nr_pages)
+{
+       unsigned long pfn = start_pfn, count = 0;
+       struct hv_hotadd_state *has;
+       bool found;
+
+       while (pfn < start_pfn + nr_pages) {
+               /*
+                * Search for HAS which covers the pfn and when we find one
+                * count how many consequitive PFNs are covered.
+                */
+               found = false;
+               list_for_each_entry(has, &dm_device.ha_region_list, list) {
+                       while ((pfn >= has->start_pfn) &&
+                              (pfn < has->end_pfn) &&
+                              (pfn < start_pfn + nr_pages)) {
+                               found = true;
+                               if (has_pfn_is_backed(has, pfn))
+                                       count++;
+                               pfn++;
+                       }
+               }
+
+               /*
+                * This PFN is not in any HAS (e.g. we're offlining a region
+                * which was present at boot), no need to account for it. Go
+                * to the next one.
+                */
+               if (!found)
+                       pfn++;
+       }
+
+       return count;
+}
+
 static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
                              void *v)
 {
        struct memory_notify *mem = (struct memory_notify *)v;
-       unsigned long flags;
+       unsigned long flags, pfn_count;
 
        switch (val) {
        case MEM_ONLINE:
@@ -593,7 +650,19 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
 
        case MEM_OFFLINE:
                spin_lock_irqsave(&dm_device.ha_lock, flags);
-               dm_device.num_pages_onlined -= mem->nr_pages;
+               pfn_count = hv_page_offline_check(mem->start_pfn,
+                                                 mem->nr_pages);
+               if (pfn_count <= dm_device.num_pages_onlined) {
+                       dm_device.num_pages_onlined -= pfn_count;
+               } else {
+                       /*
+                        * We're offlining more pages than we managed to online.
+                        * This is unexpected. In any case don't let
+                        * num_pages_onlined wrap around zero.
+                        */
+                       WARN_ON_ONCE(1);
+                       dm_device.num_pages_onlined = 0;
+               }
                spin_unlock_irqrestore(&dm_device.ha_lock, flags);
                break;
        case MEM_GOING_ONLINE:
@@ -612,30 +681,9 @@ static struct notifier_block hv_memory_nb = {
 /* Check if the particular page is backed and can be onlined and online it. */
 static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
 {
-       unsigned long cur_start_pgp;
-       unsigned long cur_end_pgp;
-       struct hv_hotadd_gap *gap;
-
-       cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn);
-       cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
-
-       /* The page is not backed. */
-       if (((unsigned long)pg < cur_start_pgp) ||
-           ((unsigned long)pg >= cur_end_pgp))
+       if (!has_pfn_is_backed(has, page_to_pfn(pg)))
                return;
 
-       /* Check for gaps. */
-       list_for_each_entry(gap, &has->gap_list, list) {
-               cur_start_pgp = (unsigned long)
-                       pfn_to_page(gap->start_pfn);
-               cur_end_pgp = (unsigned long)
-                       pfn_to_page(gap->end_pfn);
-               if (((unsigned long)pg >= cur_start_pgp) &&
-                   ((unsigned long)pg < cur_end_pgp)) {
-                       return;
-               }
-       }
-
        /* This frame is currently backed; online the page. */
        __online_page_set_limits(pg);
        __online_page_increment_counters(pg);
@@ -691,7 +739,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
                                (HA_CHUNK << PAGE_SHIFT));
 
                if (ret) {
-                       pr_warn("hot_add memory failed error is %d\n", ret);
+                       pr_err("hot_add memory failed error is %d\n", ret);
                        if (ret == -EEXIST) {
                                /*
                                 * This error indicates that the error
@@ -726,19 +774,13 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
 static void hv_online_page(struct page *pg)
 {
        struct hv_hotadd_state *has;
-       unsigned long cur_start_pgp;
-       unsigned long cur_end_pgp;
        unsigned long flags;
+       unsigned long pfn = page_to_pfn(pg);
 
        spin_lock_irqsave(&dm_device.ha_lock, flags);
        list_for_each_entry(has, &dm_device.ha_region_list, list) {
-               cur_start_pgp = (unsigned long)
-                       pfn_to_page(has->start_pfn);
-               cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
-
                /* The page belongs to a different HAS. */
-               if (((unsigned long)pg < cur_start_pgp) ||
-                   ((unsigned long)pg >= cur_end_pgp))
+               if ((pfn < has->start_pfn) || (pfn >= has->end_pfn))
                        continue;
 
                hv_page_online_one(has, pg);
@@ -1014,7 +1056,7 @@ static void hot_add_req(struct work_struct *dummy)
                resp.result = 0;
 
        if (!do_hot_add || (resp.page_count == 0))
-               pr_info("Memory hot add failed\n");
+               pr_err("Memory hot add failed\n");
 
        dm->state = DM_INITIALIZED;
        resp.hdr.trans_id = atomic_inc_return(&trans_id);
@@ -1041,7 +1083,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
 
                break;
        default:
-               pr_info("Received Unknown type: %d\n", info_hdr->type);
+               pr_warn("Received Unknown type: %d\n", info_hdr->type);
        }
 }
 
@@ -1120,6 +1162,9 @@ static void post_status(struct hv_dynmem_device *dm)
                 dm->num_pages_added - dm->num_pages_onlined : 0) +
                compute_balloon_floor();
 
+       trace_balloon_status(status.num_avail, status.num_committed,
+                            vm_memory_committed(), dm->num_pages_ballooned,
+                            dm->num_pages_added, dm->num_pages_onlined);
        /*
         * If our transaction ID is no longer current, just don't
         * send the status. This can happen if we were interrupted
@@ -1290,7 +1335,7 @@ static void balloon_up(struct work_struct *dummy)
                        /*
                         * Free up the memory we allocatted.
                         */
-                       pr_info("Balloon response failed\n");
+                       pr_err("Balloon response failed\n");
 
                        for (i = 0; i < bl_resp->range_count; i++)
                                free_balloon_pages(&dm_device,
@@ -1421,7 +1466,7 @@ static void cap_resp(struct hv_dynmem_device *dm,
                        struct dm_capabilities_resp_msg *cap_resp)
 {
        if (!cap_resp->is_accepted) {
-               pr_info("Capabilities not accepted by host\n");
+               pr_err("Capabilities not accepted by host\n");
                dm->state = DM_INIT_ERROR;
        }
        complete(&dm->host_event);
@@ -1508,7 +1553,7 @@ static void balloon_onchannelcallback(void *context)
                        break;
 
                default:
-                       pr_err("Unhandled message: type: %d\n", dm_hdr->type);
+                       pr_warn("Unhandled message: type: %d\n", dm_hdr->type);
 
                }
        }
index df47acd01a81cfaebabe7ee62b21c56e7741feb9..38d359cf1e700a4086db8d50cb1f9c263cd3bf86 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
 #include "hyperv_vmbus.h"
 
 #define CREATE_TRACE_POINTS
index d635ee95b20d9141e407f129300632c04b80f3d1..999f80a63bffab43abf57c5daf5bd1a2b952d797 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM hyperv
 
diff --git a/drivers/hv/hv_trace_balloon.h b/drivers/hv/hv_trace_balloon.h
new file mode 100644 (file)
index 0000000..9308288
--- /dev/null
@@ -0,0 +1,48 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hyperv
+
+#if !defined(_HV_TRACE_BALLOON_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _HV_TRACE_BALLOON_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(balloon_status,
+           TP_PROTO(u64 available, u64 committed,
+                    unsigned long vm_memory_committed,
+                    unsigned long pages_ballooned,
+                    unsigned long pages_added,
+                    unsigned long pages_onlined),
+           TP_ARGS(available, committed, vm_memory_committed,
+                   pages_ballooned, pages_added, pages_onlined),
+           TP_STRUCT__entry(
+                   __field(u64, available)
+                   __field(u64, committed)
+                   __field(unsigned long, vm_memory_committed)
+                   __field(unsigned long, pages_ballooned)
+                   __field(unsigned long, pages_added)
+                   __field(unsigned long, pages_onlined)
+                   ),
+           TP_fast_assign(
+                   __entry->available = available;
+                   __entry->committed = committed;
+                   __entry->vm_memory_committed = vm_memory_committed;
+                   __entry->pages_ballooned = pages_ballooned;
+                   __entry->pages_added = pages_added;
+                   __entry->pages_onlined = pages_onlined;
+                   ),
+           TP_printk("available %lld, committed %lld; vm_memory_committed %ld;"
+                     " pages_ballooned %ld, pages_added %ld, pages_onlined %ld",
+                     __entry->available, __entry->committed,
+                     __entry->vm_memory_committed, __entry->pages_ballooned,
+                     __entry->pages_added, __entry->pages_onlined
+                   )
+       );
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE hv_trace_balloon
+#endif /* _HV_TRACE_BALLOON_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 22300ec7b5567478bbeb607b84a0c6f6cd94c785..36d34fe3ccb32b1ebf8bc5229a1adb13e1e2d12b 100644 (file)
@@ -57,7 +57,9 @@ union hv_timer_config {
                u64 periodic:1;
                u64 lazy:1;
                u64 auto_enable:1;
-               u64 reserved_z0:12;
+               u64 apic_vector:8;
+               u64 direct_mode:1;
+               u64 reserved_z0:3;
                u64 sintx:4;
                u64 reserved_z1:44;
        };
diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig
new file mode 100644 (file)
index 0000000..f68e025
--- /dev/null
@@ -0,0 +1,7 @@
+menu "HW tracing support"
+
+source "drivers/hwtracing/stm/Kconfig"
+
+source "drivers/hwtracing/intel_th/Kconfig"
+
+endmenu
index 6ea62c62ff27123ed21e5119deceab6d0afa0e7d..9cdb3fbc8c1f345c27ab5c6afe2e4f021757a865 100644 (file)
@@ -315,7 +315,7 @@ static void debug_dump_regs(struct debug_drvdata *drvdata)
        }
 
        pc = debug_adjust_pc(drvdata);
-       dev_emerg(dev, " EDPCSR:  [<%p>] %pS\n", (void *)pc, (void *)pc);
+       dev_emerg(dev, " EDPCSR:  [<%px>] %pS\n", (void *)pc, (void *)pc);
 
        if (drvdata->edcidsr_present)
                dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr);
index 4e6eab53e34e5ef2aa1b931ecfeca8ab39a20b9b..d21961710713b1b77f16ef2f30e6004e6cf9af6d 100644 (file)
@@ -1780,7 +1780,7 @@ static ssize_t ctxid_masks_store(struct device *dev,
                 */
                for (j = 0; j < 8; j++) {
                        if (maskbyte & 1)
-                               config->ctxid_pid[i] &= ~(0xFF << (j * 8));
+                               config->ctxid_pid[i] &= ~(0xFFUL << (j * 8));
                        maskbyte >>= 1;
                }
                /* Select the next ctxid comparator mask value */
@@ -1963,7 +1963,7 @@ static ssize_t vmid_masks_store(struct device *dev,
                 */
                for (j = 0; j < 8; j++) {
                        if (maskbyte & 1)
-                               config->vmid_val[i] &= ~(0xFF << (j * 8));
+                               config->vmid_val[i] &= ~(0xFFUL << (j * 8));
                        maskbyte >>= 1;
                }
                /* Select the next vmid comparator mask value */
index 1b412f8a56b5ef85546b6e9700388f652c3c0c53..ca0527d588e958391f1f3e4a2f2b61031ee45782 100644 (file)
@@ -25,6 +25,18 @@ config INTEL_TH_PCI
 
          Say Y here to enable PCI Intel TH support.
 
+config INTEL_TH_ACPI
+       tristate "Intel(R) Trace Hub ACPI controller"
+       depends on ACPI
+       help
+         Intel(R) Trace Hub may exist as an ACPI device. This option enables
+         support glue layer for ACPI-based Intel TH. This typically implies
+         'host debugger' mode, that is, the trace configuration and capture
+         is handled by an external debug host and corresponding controls will
+         not be available on the target.
+
+         Say Y here to enable ACPI Intel TH support.
+
 config INTEL_TH_GTH
        tristate "Intel(R) Trace Hub Global Trace Hub"
        help
index 880c9b5e8566b71958d138e8d14edca5abda9bfe..d9252fa8d9ca19142bb72e808636de10006457fd 100644 (file)
@@ -6,6 +6,9 @@ intel_th-$(CONFIG_INTEL_TH_DEBUG) += debug.o
 obj-$(CONFIG_INTEL_TH_PCI)     += intel_th_pci.o
 intel_th_pci-y                 := pci.o
 
+obj-$(CONFIG_INTEL_TH_ACPI)    += intel_th_acpi.o
+intel_th_acpi-y                        := acpi.o
+
 obj-$(CONFIG_INTEL_TH_GTH)     += intel_th_gth.o
 intel_th_gth-y                 := gth.o
 
diff --git a/drivers/hwtracing/intel_th/acpi.c b/drivers/hwtracing/intel_th/acpi.c
new file mode 100644 (file)
index 0000000..87bc374
--- /dev/null
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel(R) Trace Hub ACPI driver
+ *
+ * Copyright (C) 2017 Intel Corporation.
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+#include "intel_th.h"
+
+#define DRIVER_NAME "intel_th_acpi"
+
+static const struct intel_th_drvdata intel_th_acpi_pch = {
+       .host_mode_only = 1,
+};
+
+static const struct intel_th_drvdata intel_th_acpi_uncore = {
+       .host_mode_only = 1,
+};
+
+static const struct acpi_device_id intel_th_acpi_ids[] = {
+       { "INTC1000",   (kernel_ulong_t)&intel_th_acpi_uncore },
+       { "INTC1001",   (kernel_ulong_t)&intel_th_acpi_pch },
+       { "",           0 },
+};
+
+MODULE_DEVICE_TABLE(acpi, intel_th_acpi_ids);
+
+static int intel_th_acpi_probe(struct platform_device *pdev)
+{
+       struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+       const struct acpi_device_id *id;
+       struct intel_th *th;
+
+       id = acpi_match_device(intel_th_acpi_ids, &pdev->dev);
+       if (!id)
+               return -ENODEV;
+
+       th = intel_th_alloc(&pdev->dev, (void *)id->driver_data,
+                           pdev->resource, pdev->num_resources, -1);
+       if (IS_ERR(th))
+               return PTR_ERR(th);
+
+       adev->driver_data = th;
+
+       return 0;
+}
+
+static int intel_th_acpi_remove(struct platform_device *pdev)
+{
+       struct intel_th *th = platform_get_drvdata(pdev);
+
+       intel_th_free(th);
+
+       return 0;
+}
+
+static struct platform_driver intel_th_acpi_driver = {
+       .probe          = intel_th_acpi_probe,
+       .remove         = intel_th_acpi_remove,
+       .driver         = {
+               .name                   = DRIVER_NAME,
+               .acpi_match_table       = intel_th_acpi_ids,
+       },
+};
+
+module_platform_driver(intel_th_acpi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel(R) Trace Hub ACPI controller driver");
+MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>");
index 1a023e30488c66d4e7e58367a58a02252e6e63b0..da962aa2cef5297ba7a5f78c4f499c12ac5a464a 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub driver core
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
@@ -638,7 +630,8 @@ intel_th_subdevice_alloc(struct intel_th *th,
                thdev->output.port = -1;
                thdev->output.scratchpad = subdev->scrpd;
        } else if (subdev->type == INTEL_TH_SWITCH) {
-               thdev->host_mode = host_mode;
+               thdev->host_mode =
+                       INTEL_TH_CAP(th, host_mode_only) ? true : host_mode;
                th->hub = thdev;
        }
 
@@ -737,7 +730,8 @@ static int intel_th_populate(struct intel_th *th)
                struct intel_th_device *thdev;
 
                /* only allow SOURCE and SWITCH devices in host mode */
-               if (host_mode && subdev->type == INTEL_TH_OUTPUT)
+               if ((INTEL_TH_CAP(th, host_mode_only) || host_mode) &&
+                   subdev->type == INTEL_TH_OUTPUT)
                        continue;
 
                /*
@@ -813,7 +807,14 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
               struct resource *devres, unsigned int ndevres, int irq)
 {
        struct intel_th *th;
-       int err;
+       int err, r;
+
+       if (irq == -1)
+               for (r = 0; r < ndevres; r++)
+                       if (devres[r].flags & IORESOURCE_IRQ) {
+                               irq = devres[r].start;
+                               break;
+                       }
 
        th = kzalloc(sizeof(*th), GFP_KERNEL);
        if (!th)
@@ -935,9 +936,13 @@ EXPORT_SYMBOL_GPL(intel_th_trace_disable);
 int intel_th_set_output(struct intel_th_device *thdev,
                        unsigned int master)
 {
-       struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
+       struct intel_th_device *hub = to_intel_th_hub(thdev);
        struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
 
+       /* In host mode, this is up to the external debugger, do nothing. */
+       if (hub->host_mode)
+               return 0;
+
        if (!hubdrv->set_output)
                return -ENOTSUPP;
 
index 788a1f0a97adaf1a7162bb40dc2a588979c675e7..ff79063118a0e79705caa20f441e9245d9cbfa3e 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub driver debugging
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #include <linux/types.h>
index 88311bad3ba493e7a3c697ed701f0bfd8f6edaa2..78bd7e4bf9ce323c8bc774724d1be049fc154035 100644 (file)
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Intel(R) Trace Hub driver debugging
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #ifndef __INTEL_TH_DEBUG_H__
index 018678ec3c13559989174c6e25d021e851dadee3..8426b7970c148a6104fa6986050db4ea0dbf9cbf 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub Global Trace Hub
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
index f3d234251a12102792bfeee16817637df3ffc757..6f2b0b9308755ad2bfba17cf085418293ddd1e24 100644 (file)
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Intel(R) Trace Hub Global Trace Hub (GTH) data structures
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #ifndef __INTEL_TH_GTH_H__
index 99ad563fc40d483027420694ad3feb470cb909c2..780206dc9012c26c534df934b593eaa455bec780 100644 (file)
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Intel(R) Trace Hub data structures
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #ifndef __INTEL_TH_H__
@@ -50,9 +42,11 @@ struct intel_th_output {
 /**
  * struct intel_th_drvdata - describes hardware capabilities and quirks
  * @tscu_enable:       device needs SW to enable time stamping unit
+ * @host_mode_only:    device can only operate in 'host debugger' mode
  */
 struct intel_th_drvdata {
-       unsigned int    tscu_enable        : 1;
+       unsigned int    tscu_enable        : 1,
+                       host_mode_only     : 1;
 };
 
 #define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0)
index dfb57eaa9f22a94d5e4a3454649f33f64f6a3f1c..ede388309376f5d5680298787bcc541b9f8c86e4 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub Memory Storage Unit
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
index 9b710e4aa98affe6b1dab3ab3b8cc5b9b3158148..9cc8aced6116ac863bfdbd5beb4e2634829a54be 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub Memory Storage Unit (MSU) data structures
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #ifndef __INTEL_TH_MSU_H__
index c2a2ce8ee5410743c750bdbcad8c0170992af1e0..c2e55e5d97f654cf3a392fdf1449ca9ddd47250d 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub pci driver
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
index e96a1fcb57b201f52ed74344d8e0b0439da4cf9e..56694339cb068591d255666244a7875a1c43be6e 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub PTI output driver
  *
  * Copyright (C) 2014-2016 Intel Corporation.
- *
- * 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 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.
  */
 
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
index 30827be67b4cb6f325e605f78799576a6d0bb1c0..e9381babc84c37f662ee8e822806a99a1dd9bab3 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub PTI output data structures
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #ifndef __INTEL_TH_STH_H__
index b0344462464873abdeb9795f0ac813dde779748e..4b7ae47789d215d77ec834c243d2d1c4994e16c6 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Intel(R) Trace Hub Software Trace Hub support
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
index f1390cd4f2ed49cc6e2c14b61cf466f2ab3dc496..f97fc0c5173919d7024dabc7499b58f817ffcc7d 100644 (file)
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Intel(R) Trace Hub Software Trace Hub (STH) data structures
  *
  * Copyright (C) 2014-2015 Intel Corporation.
- *
- * 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 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.
  */
 
 #ifndef __INTEL_TH_STH_H__
index c9d9a8d2ff52ef8ef97b0d7b71df93ae04b1e29b..a00f65e217477e9eaccfbf18acbef1256fd12220 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Simple kernel console driver for STM devices
  * Copyright (c) 2014, Intel Corporation.
  *
- * 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 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.
- *
  * STM console will send kernel messages over STM devices to a trace host.
  */
 
index f129869e05a9b0dc6430b63ce8ff1693c25c4726..05386b76465ed56105436b4d3b15da98d8051bf1 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * System Trace Module (STM) infrastructure
  * Copyright (c) 2014, Intel Corporation.
  *
- * 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 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.
- *
  * STM class implements generic infrastructure for  System Trace Module devices
  * as defined in MIPI STPv2 specification.
  */
index c5f94ca31c4d79bfa7be20ef8c672b0bc96f4c0b..38528ffdc0b3f7869b73e0a59677a9e0399ca3d7 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * A dummy STM device for stm/stm_source class testing.
  * Copyright (c) 2014, Intel Corporation.
  *
- * 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 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.
- *
  * STM class implements generic infrastructure for  System Trace Module devices
  * as defined in MIPI STPv2 specification.
  */
@@ -20,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/stm.h>
+#include <uapi/linux/stm.h>
 
 static ssize_t notrace
 dummy_stm_packet(struct stm_data *stm_data, unsigned int master,
@@ -52,6 +45,18 @@ static unsigned int fail_mode;
 
 module_param(fail_mode, int, 0600);
 
+static unsigned int master_min;
+
+module_param(master_min, int, 0400);
+
+static unsigned int master_max = STP_MASTER_MAX;
+
+module_param(master_max, int, 0400);
+
+static unsigned int nr_channels = STP_CHANNEL_MAX;
+
+module_param(nr_channels, int, 0400);
+
 static int dummy_stm_link(struct stm_data *data, unsigned int master,
                          unsigned int channel)
 {
@@ -68,14 +73,19 @@ static int dummy_stm_init(void)
        if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX)
                return -EINVAL;
 
+       if (master_min > master_max ||
+           master_max > STP_MASTER_MAX ||
+           nr_channels > STP_CHANNEL_MAX)
+               return -EINVAL;
+
        for (i = 0; i < nr_dummies; i++) {
                dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i);
                if (!dummy_stm[i].name)
                        goto fail_unregister;
 
-               dummy_stm[i].sw_start           = 0x0000;
-               dummy_stm[i].sw_end             = 0xffff;
-               dummy_stm[i].sw_nchannels       = 0xffff;
+               dummy_stm[i].sw_start           = master_min;
+               dummy_stm[i].sw_end             = master_max;
+               dummy_stm[i].sw_nchannels       = nr_channels;
                dummy_stm[i].packet             = dummy_stm_packet;
                dummy_stm[i].link               = dummy_stm_link;
 
index 3da7b673aab252a245991c901eb5a07696c5fb38..7db42395e131637aa7eeba40e014577dc8b7e447 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Simple heartbeat STM source driver
  * Copyright (c) 2016, Intel Corporation.
  *
- * 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 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.
- *
  * Heartbeat STM source will send repetitive messages over STM devices to a
  * trace host.
  */
index 33e9a1b6ea7c2b9571574dc7a971085b043813a8..3fd07e275b34e9a8470e8203445cfc3c7a4cc19d 100644 (file)
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * System Trace Module (STM) master/channel allocation policy management
  * Copyright (c) 2014, Intel Corporation.
  *
- * 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 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.
- *
  * A master/channel allocation policy allows mapping string identifiers to
  * master and channel ranges, where allocation can be done.
  */
index 4e8c6926260f3e8eec0ec8da70de8f13f86f0cc6..923571adc6f408f44da82cb06d631d46498a4032 100644 (file)
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * System Trace Module (STM) infrastructure
  * Copyright (c) 2014, Intel Corporation.
  *
- * 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 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.
- *
  * STM class implements generic infrastructure for  System Trace Module devices
  * as defined in MIPI STPv2 specification.
  */
index af4d2f26f1c620a9cd0cd600739fd22e2aaee16a..c2d69e33bf2bfc1d16780f90802bf4695f699013 100644 (file)
@@ -117,6 +117,7 @@ static void mcb_pci_remove(struct pci_dev *pdev)
 
 static const struct pci_device_id mcb_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) },
+       { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) },
        { 0 },
 };
 MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);
index 03605f8fc0dc94490f9ee76fb69ec68c22528ec7..5d713008749b78c7fdfdecc31e4fba7b33d1b9bb 100644 (file)
@@ -75,7 +75,6 @@ config ATMEL_TCB_CLKSRC
 config ATMEL_TCB_CLKSRC_BLOCK
        int
        depends on ATMEL_TCB_CLKSRC
-       prompt "TC Block" if CPU_AT32AP700X
        default 0
        range 0 1
        help
index c3c8624f4d9506efe0f21168a3ebf7c0ae4536ee..20be70c3f118d943c626ff530875f18ab97382f9 100644 (file)
@@ -12,7 +12,7 @@ obj-$(CONFIG_ATMEL_SSC)               += atmel-ssc.o
 obj-$(CONFIG_ATMEL_TCLIB)      += atmel_tclib.o
 obj-$(CONFIG_DUMMY_IRQ)                += dummy-irq.o
 obj-$(CONFIG_ICS932S401)       += ics932s401.o
-obj-$(CONFIG_LKDTM)            += lkdtm.o
+obj-$(CONFIG_LKDTM)            += lkdtm/
 obj-$(CONFIG_TIFM_CORE)        += tifm_core.o
 obj-$(CONFIG_TIFM_7XX1)        += tifm_7xx1.o
 obj-$(CONFIG_PHANTOM)          += phantom.o
@@ -57,21 +57,3 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)        += pci_endpoint_test.o
 obj-$(CONFIG_OCXL)             += ocxl/
 obj-$(CONFIG_MISC_RTSX)                += cardreader/
-
-lkdtm-$(CONFIG_LKDTM)          += lkdtm_core.o
-lkdtm-$(CONFIG_LKDTM)          += lkdtm_bugs.o
-lkdtm-$(CONFIG_LKDTM)          += lkdtm_heap.o
-lkdtm-$(CONFIG_LKDTM)          += lkdtm_perms.o
-lkdtm-$(CONFIG_LKDTM)          += lkdtm_refcount.o
-lkdtm-$(CONFIG_LKDTM)          += lkdtm_rodata_objcopy.o
-lkdtm-$(CONFIG_LKDTM)          += lkdtm_usercopy.o
-
-KCOV_INSTRUMENT_lkdtm_rodata.o := n
-
-OBJCOPYFLAGS :=
-OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
-                       --set-section-flags .text=alloc,readonly \
-                       --rename-section .text=.rodata
-targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o
-$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE
-       $(call if_changed,objcopy)
index b5439643f54b23c67dda8831e674344616f97e68..a024f8042259ae8a778788f9b88ee0fa0bcb66d6 100644 (file)
@@ -7,6 +7,7 @@
  * 2 of the License, or (at your option) any later version.
  */
 
+#include <linux/clk.h>
 #include <linux/mfd/syscon.h>
 #include <linux/miscdevice.h>
 #include <linux/mm.h>
 
 #define DEVICE_NAME    "aspeed-lpc-ctrl"
 
+#define HICR5 0x0
+#define HICR5_ENL2H    BIT(8)
+#define HICR5_ENFWH    BIT(10)
+
 #define HICR7 0x8
 #define HICR8 0xc
 
 struct aspeed_lpc_ctrl {
        struct miscdevice       miscdev;
        struct regmap           *regmap;
+       struct clk              *clk;
        phys_addr_t             mem_base;
        resource_size_t         mem_size;
        u32             pnor_size;
@@ -153,8 +159,18 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd,
                if (rc)
                        return rc;
 
-               return regmap_write(lpc_ctrl->regmap, HICR8,
-                       (~(map.size - 1)) | ((map.size >> 16) - 1));
+               rc = regmap_write(lpc_ctrl->regmap, HICR8,
+                               (~(map.size - 1)) | ((map.size >> 16) - 1));
+               if (rc)
+                       return rc;
+
+               /*
+                * Enable LPC FHW cycles. This is required for the host to
+                * access the regions specified.
+                */
+               return regmap_update_bits(lpc_ctrl->regmap, HICR5,
+                               HICR5_ENFWH | HICR5_ENL2H,
+                               HICR5_ENFWH | HICR5_ENL2H);
        }
 
        return -EINVAL;
@@ -221,16 +237,33 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
+       lpc_ctrl->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(lpc_ctrl->clk)) {
+               dev_err(dev, "couldn't get clock\n");
+               return PTR_ERR(lpc_ctrl->clk);
+       }
+       rc = clk_prepare_enable(lpc_ctrl->clk);
+       if (rc) {
+               dev_err(dev, "couldn't enable clock\n");
+               return rc;
+       }
+
        lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR;
        lpc_ctrl->miscdev.name = DEVICE_NAME;
        lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops;
        lpc_ctrl->miscdev.parent = dev;
        rc = misc_register(&lpc_ctrl->miscdev);
-       if (rc)
+       if (rc) {
                dev_err(dev, "Unable to register device\n");
-       else
-               dev_info(dev, "Loaded at %pr\n", &resm);
+               goto err;
+       }
+
+       dev_info(dev, "Loaded at %pr\n", &resm);
+
+       return 0;
 
+err:
+       clk_disable_unprepare(lpc_ctrl->clk);
        return rc;
 }
 
@@ -239,6 +272,7 @@ static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
        struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
 
        misc_deregister(&lpc_ctrl->miscdev);
+       clk_disable_unprepare(lpc_ctrl->clk);
 
        return 0;
 }
index 07cb93abf685346459a57c32744135329874609c..a493b01c5bc65933b90a6e8ae50e32dd7e174550 100644 (file)
@@ -388,17 +388,17 @@ static void rts5260_disable_ocp(struct rtsx_pcr *pcr)
                                OC_POWER_DOWN);
 }
 
-int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val)
+static int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val)
 {
        return rtsx_pci_read_register(pcr, REG_OCPSTAT, val);
 }
 
-int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val)
+static int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val)
 {
        return rtsx_pci_read_register(pcr, REG_DV3318_OCPSTAT, val);
 }
 
-void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
+static void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
 {
        u8 mask = 0;
        u8 val = 0;
@@ -418,7 +418,7 @@ void rts5260_clear_ocpstat(struct rtsx_pcr *pcr)
                                DV3318_OCP_INT_CLR | DV3318_OCP_CLR, 0);
 }
 
-void rts5260_process_ocp(struct rtsx_pcr *pcr)
+static void rts5260_process_ocp(struct rtsx_pcr *pcr)
 {
        if (!pcr->option.ocp_en)
                return;
@@ -449,7 +449,7 @@ void rts5260_process_ocp(struct rtsx_pcr *pcr)
        }
 }
 
-int rts5260_init_hw(struct rtsx_pcr *pcr)
+static int rts5260_init_hw(struct rtsx_pcr *pcr)
 {
        int err;
 
@@ -620,7 +620,7 @@ static int rts5260_extra_init_hw(struct rtsx_pcr *pcr)
        return 0;
 }
 
-void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable)
+static void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable)
 {
        struct rtsx_cr_option *option = &pcr->option;
        u8 val = 0;
index 01f9c4921c5087c6bcc0e9dc482eaaed5495a0fb..0c125f207aea82c394f989f5033fccda9cd7e0b1 100644 (file)
@@ -1,14 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * at24.c - handle most I2C EEPROMs
  *
  * Copyright (C) 2005-2007 David Brownell
  * Copyright (C) 2008 Wolfram Sang, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
  */
+
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -63,8 +60,6 @@ struct at24_client {
 };
 
 struct at24_data {
-       struct at24_platform_data chip;
-
        /*
         * Lock protects against activities from other Linux tasks,
         * but not from changes by other I2C masters.
@@ -75,7 +70,10 @@ struct at24_data {
        unsigned int num_addresses;
        unsigned int offset_adj;
 
-       struct nvmem_config nvmem_config;
+       u32 byte_len;
+       u16 page_size;
+       u8 flags;
+
        struct nvmem_device *nvmem;
 
        struct gpio_desc *wp_gpio;
@@ -239,8 +237,6 @@ static const struct acpi_device_id at24_acpi_ids[] = {
 };
 MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
 
-/*-------------------------------------------------------------------------*/
-
 /*
  * This routine supports chips which consume multiple I2C addresses. It
  * computes the addressing information to be used for a given r/w request.
@@ -255,7 +251,7 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24,
 {
        unsigned int i;
 
-       if (at24->chip.flags & AT24_FLAG_ADDR16) {
+       if (at24->flags & AT24_FLAG_ADDR16) {
                i = *offset >> 16;
                *offset &= 0xffff;
        } else {
@@ -266,6 +262,11 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24,
        return &at24->client[i];
 }
 
+static struct device *at24_base_client_dev(struct at24_data *at24)
+{
+       return &at24->client[0].client->dev;
+}
+
 static size_t at24_adjust_read_count(struct at24_data *at24,
                                      unsigned int offset, size_t count)
 {
@@ -277,8 +278,8 @@ static size_t at24_adjust_read_count(struct at24_data *at24,
         * the next slave address: truncate the count to the slave boundary,
         * so that the read never straddles slaves.
         */
-       if (at24->chip.flags & AT24_FLAG_NO_RDROL) {
-               bits = (at24->chip.flags & AT24_FLAG_ADDR16) ? 16 : 8;
+       if (at24->flags & AT24_FLAG_NO_RDROL) {
+               bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8;
                remainder = BIT(bits) - offset;
                if (count > remainder)
                        count = remainder;
@@ -337,7 +338,7 @@ static size_t at24_adjust_write_count(struct at24_data *at24,
                count = at24->write_max;
 
        /* Never roll over backwards, to the start of this page */
-       next_page = roundup(offset + 1, at24->chip.page_size);
+       next_page = roundup(offset + 1, at24->page_size);
        if (offset + count > next_page)
                count = next_page - offset;
 
@@ -371,15 +372,18 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf,
 
 static int at24_read(void *priv, unsigned int off, void *val, size_t count)
 {
-       struct at24_data *at24 = priv;
-       struct device *dev = &at24->client[0].client->dev;
+       struct at24_data *at24;
+       struct device *dev;
        char *buf = val;
        int ret;
 
+       at24 = priv;
+       dev = at24_base_client_dev(at24);
+
        if (unlikely(!count))
                return count;
 
-       if (off + count > at24->chip.byte_len)
+       if (off + count > at24->byte_len)
                return -EINVAL;
 
        ret = pm_runtime_get_sync(dev);
@@ -395,17 +399,15 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
        mutex_lock(&at24->lock);
 
        while (count) {
-               int     status;
-
-               status = at24_regmap_read(at24, buf, off, count);
-               if (status < 0) {
+               ret = at24_regmap_read(at24, buf, off, count);
+               if (ret < 0) {
                        mutex_unlock(&at24->lock);
                        pm_runtime_put(dev);
-                       return status;
+                       return ret;
                }
-               buf += status;
-               off += status;
-               count -= status;
+               buf += ret;
+               off += ret;
+               count -= ret;
        }
 
        mutex_unlock(&at24->lock);
@@ -417,15 +419,18 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
 
 static int at24_write(void *priv, unsigned int off, void *val, size_t count)
 {
-       struct at24_data *at24 = priv;
-       struct device *dev = &at24->client[0].client->dev;
+       struct at24_data *at24;
+       struct device *dev;
        char *buf = val;
        int ret;
 
+       at24 = priv;
+       dev = at24_base_client_dev(at24);
+
        if (unlikely(!count))
                return -EINVAL;
 
-       if (off + count > at24->chip.byte_len)
+       if (off + count > at24->byte_len)
                return -EINVAL;
 
        ret = pm_runtime_get_sync(dev);
@@ -442,18 +447,16 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
        gpiod_set_value_cansleep(at24->wp_gpio, 0);
 
        while (count) {
-               int status;
-
-               status = at24_regmap_write(at24, buf, off, count);
-               if (status < 0) {
+               ret = at24_regmap_write(at24, buf, off, count);
+               if (ret < 0) {
                        gpiod_set_value_cansleep(at24->wp_gpio, 1);
                        mutex_unlock(&at24->lock);
                        pm_runtime_put(dev);
-                       return status;
+                       return ret;
                }
-               buf += status;
-               off += status;
-               count -= status;
+               buf += ret;
+               off += ret;
+               count -= ret;
        }
 
        gpiod_set_value_cansleep(at24->wp_gpio, 1);
@@ -464,7 +467,8 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
        return 0;
 }
 
-static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip)
+static void at24_properties_to_pdata(struct device *dev,
+                                    struct at24_platform_data *chip)
 {
        int err;
        u32 val;
@@ -491,6 +495,43 @@ static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip)
        }
 }
 
+static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
+{
+       struct device_node *of_node = dev->of_node;
+       const struct at24_chip_data *cdata;
+       const struct i2c_device_id *id;
+       struct at24_platform_data *pd;
+
+       pd = dev_get_platdata(dev);
+       if (pd) {
+               memcpy(pdata, pd, sizeof(*pdata));
+               return 0;
+       }
+
+       id = i2c_match_id(at24_ids, to_i2c_client(dev));
+
+       /*
+        * The I2C core allows OF nodes compatibles to match against the
+        * I2C device ID table as a fallback, so check not only if an OF
+        * node is present but also if it matches an OF device ID entry.
+        */
+       if (of_node && of_match_device(at24_of_match, dev))
+               cdata = of_device_get_match_data(dev);
+       else if (id)
+               cdata = (void *)&id->driver_data;
+       else
+               cdata = acpi_device_get_match_data(dev);
+
+       if (!cdata)
+               return -ENODEV;
+
+       pdata->byte_len = cdata->byte_len;
+       pdata->flags = cdata->flags;
+       at24_properties_to_pdata(dev, pdata);
+
+       return 0;
+}
+
 static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len)
 {
        if (flags & AT24_FLAG_MAC) {
@@ -514,102 +555,83 @@ static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len)
        }
 }
 
-static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int at24_probe(struct i2c_client *client)
 {
-       struct at24_platform_data chip = { 0 };
-       const struct at24_chip_data *cd = NULL;
-       bool writable;
-       struct at24_data *at24;
-       int err;
-       unsigned int i, num_addresses;
        struct regmap_config regmap_config = { };
+       struct nvmem_config nvmem_config = { };
+       struct at24_platform_data pdata = { };
+       struct device *dev = &client->dev;
+       bool i2c_fn_i2c, i2c_fn_block;
+       unsigned int i, num_addresses;
+       struct at24_data *at24;
+       struct regmap *regmap;
+       size_t at24_size;
+       bool writable;
        u8 test_byte;
+       int err;
 
-       if (client->dev.platform_data) {
-               chip = *(struct at24_platform_data *)client->dev.platform_data;
-       } else {
-               /*
-                * The I2C core allows OF nodes compatibles to match against the
-                * I2C device ID table as a fallback, so check not only if an OF
-                * node is present but also if it matches an OF device ID entry.
-                */
-               if (client->dev.of_node &&
-                   of_match_device(at24_of_match, &client->dev)) {
-                       cd = of_device_get_match_data(&client->dev);
-               } else if (id) {
-                       cd = (void *)id->driver_data;
-               } else {
-                       const struct acpi_device_id *aid;
-
-                       aid = acpi_match_device(at24_acpi_ids, &client->dev);
-                       if (aid)
-                               cd = (void *)aid->driver_data;
-               }
-               if (!cd)
-                       return -ENODEV;
+       i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+       i2c_fn_block = i2c_check_functionality(client->adapter,
+                                              I2C_FUNC_SMBUS_WRITE_I2C_BLOCK);
 
-               chip.byte_len = cd->byte_len;
-               chip.flags = cd->flags;
-               at24_get_pdata(&client->dev, &chip);
-       }
+       err = at24_get_pdata(dev, &pdata);
+       if (err)
+               return err;
+
+       if (!i2c_fn_i2c && !i2c_fn_block)
+               pdata.page_size = 1;
 
-       if (!is_power_of_2(chip.byte_len))
-               dev_warn(&client->dev,
-                       "byte_len looks suspicious (no power of 2)!\n");
-       if (!chip.page_size) {
-               dev_err(&client->dev, "page_size must not be 0!\n");
+       if (!pdata.page_size) {
+               dev_err(dev, "page_size must not be 0!\n");
                return -EINVAL;
        }
-       if (!is_power_of_2(chip.page_size))
-               dev_warn(&client->dev,
-                       "page_size looks suspicious (no power of 2)!\n");
 
-       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
-           !i2c_check_functionality(client->adapter,
-                                    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
-               chip.page_size = 1;
+       if (!is_power_of_2(pdata.page_size))
+               dev_warn(dev, "page_size looks suspicious (no power of 2)!\n");
 
-       if (chip.flags & AT24_FLAG_TAKE8ADDR)
+       if (pdata.flags & AT24_FLAG_TAKE8ADDR)
                num_addresses = 8;
        else
-               num_addresses = DIV_ROUND_UP(chip.byte_len,
-                       (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+               num_addresses = DIV_ROUND_UP(pdata.byte_len,
+                       (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+
+       if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) {
+               dev_err(dev,
+                       "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
+               return -EINVAL;
+       }
 
        regmap_config.val_bits = 8;
-       regmap_config.reg_bits = (chip.flags & AT24_FLAG_ADDR16) ? 16 : 8;
+       regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8;
+       regmap_config.disable_locking = true;
 
-       at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
-               num_addresses * sizeof(struct at24_client), GFP_KERNEL);
+       regmap = devm_regmap_init_i2c(client, &regmap_config);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       at24_size = sizeof(*at24) + num_addresses * sizeof(struct at24_client);
+       at24 = devm_kzalloc(dev, at24_size, GFP_KERNEL);
        if (!at24)
                return -ENOMEM;
 
        mutex_init(&at24->lock);
-       at24->chip = chip;
+       at24->byte_len = pdata.byte_len;
+       at24->page_size = pdata.page_size;
+       at24->flags = pdata.flags;
        at24->num_addresses = num_addresses;
-       at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len);
+       at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len);
+       at24->client[0].client = client;
+       at24->client[0].regmap = regmap;
 
-       at24->wp_gpio = devm_gpiod_get_optional(&client->dev,
-                                               "wp", GPIOD_OUT_HIGH);
+       at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH);
        if (IS_ERR(at24->wp_gpio))
                return PTR_ERR(at24->wp_gpio);
 
-       at24->client[0].client = client;
-       at24->client[0].regmap = devm_regmap_init_i2c(client, &regmap_config);
-       if (IS_ERR(at24->client[0].regmap))
-               return PTR_ERR(at24->client[0].regmap);
-
-       if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) {
-               dev_err(&client->dev,
-                       "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
-               return -EINVAL;
-       }
-
-       writable = !(chip.flags & AT24_FLAG_READONLY);
+       writable = !(pdata.flags & AT24_FLAG_READONLY);
        if (writable) {
                at24->write_max = min_t(unsigned int,
-                                       chip.page_size, at24_io_limit);
-               if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
-                   at24->write_max > I2C_SMBUS_BLOCK_MAX)
+                                       pdata.page_size, at24_io_limit);
+               if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX)
                        at24->write_max = I2C_SMBUS_BLOCK_MAX;
        }
 
@@ -618,8 +640,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
                at24->client[i].client = i2c_new_dummy(client->adapter,
                                                       client->addr + i);
                if (!at24->client[i].client) {
-                       dev_err(&client->dev, "address 0x%02x unavailable\n",
-                                       client->addr + i);
+                       dev_err(dev, "address 0x%02x unavailable\n",
+                               client->addr + i);
                        err = -EADDRINUSE;
                        goto err_clients;
                }
@@ -635,48 +657,47 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
        i2c_set_clientdata(client, at24);
 
        /* enable runtime pm */
-       pm_runtime_set_active(&client->dev);
-       pm_runtime_enable(&client->dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
 
        /*
         * Perform a one-byte test read to verify that the
         * chip is functional.
         */
        err = at24_read(at24, 0, &test_byte, 1);
-       pm_runtime_idle(&client->dev);
+       pm_runtime_idle(dev);
        if (err) {
                err = -ENODEV;
                goto err_clients;
        }
 
-       at24->nvmem_config.name = dev_name(&client->dev);
-       at24->nvmem_config.dev = &client->dev;
-       at24->nvmem_config.read_only = !writable;
-       at24->nvmem_config.root_only = true;
-       at24->nvmem_config.owner = THIS_MODULE;
-       at24->nvmem_config.compat = true;
-       at24->nvmem_config.base_dev = &client->dev;
-       at24->nvmem_config.reg_read = at24_read;
-       at24->nvmem_config.reg_write = at24_write;
-       at24->nvmem_config.priv = at24;
-       at24->nvmem_config.stride = 1;
-       at24->nvmem_config.word_size = 1;
-       at24->nvmem_config.size = chip.byte_len;
-
-       at24->nvmem = nvmem_register(&at24->nvmem_config);
-
+       nvmem_config.name = dev_name(dev);
+       nvmem_config.dev = dev;
+       nvmem_config.read_only = !writable;
+       nvmem_config.root_only = true;
+       nvmem_config.owner = THIS_MODULE;
+       nvmem_config.compat = true;
+       nvmem_config.base_dev = dev;
+       nvmem_config.reg_read = at24_read;
+       nvmem_config.reg_write = at24_write;
+       nvmem_config.priv = at24;
+       nvmem_config.stride = 1;
+       nvmem_config.word_size = 1;
+       nvmem_config.size = pdata.byte_len;
+
+       at24->nvmem = nvmem_register(&nvmem_config);
        if (IS_ERR(at24->nvmem)) {
                err = PTR_ERR(at24->nvmem);
                goto err_clients;
        }
 
-       dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
-               chip.byte_len, client->name,
-               writable ? "writable" : "read-only", at24->write_max);
+       dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
+                pdata.byte_len, client->name,
+                writable ? "writable" : "read-only", at24->write_max);
 
        /* export data to kernel code */
-       if (chip.setup)
-               chip.setup(at24->nvmem, chip.context);
+       if (pdata.setup)
+               pdata.setup(at24->nvmem, pdata.context);
 
        return 0;
 
@@ -685,7 +706,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
                if (at24->client[i].client)
                        i2c_unregister_device(at24->client[i].client);
 
-       pm_runtime_disable(&client->dev);
+       pm_runtime_disable(dev);
 
        return err;
 }
@@ -708,15 +729,13 @@ static int at24_remove(struct i2c_client *client)
        return 0;
 }
 
-/*-------------------------------------------------------------------------*/
-
 static struct i2c_driver at24_driver = {
        .driver = {
                .name = "at24",
                .of_match_table = at24_of_match,
                .acpi_match_table = ACPI_PTR(at24_acpi_ids),
        },
-       .probe = at24_probe,
+       .probe_new = at24_probe,
        .remove = at24_remove,
        .id_table = at24_ids,
 };
index 9282ffd607ff2799e08f4c96b9e493ec64c454a1..6a7d4a2ad51479624fae229023d698c58549f405 100644 (file)
@@ -102,7 +102,7 @@ static int at25_ee_read(void *priv, unsigned int offset,
        }
 
        spi_message_init(&m);
-       memset(t, 0, sizeof t);
+       memset(t, 0, sizeof(t));
 
        t[0].tx_buf = command;
        t[0].len = at25->addrlen + 1;
diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile
new file mode 100644 (file)
index 0000000..3370a41
--- /dev/null
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_LKDTM)            += lkdtm.o
+
+lkdtm-$(CONFIG_LKDTM)          += core.o
+lkdtm-$(CONFIG_LKDTM)          += bugs.o
+lkdtm-$(CONFIG_LKDTM)          += heap.o
+lkdtm-$(CONFIG_LKDTM)          += perms.o
+lkdtm-$(CONFIG_LKDTM)          += refcount.o
+lkdtm-$(CONFIG_LKDTM)          += rodata_objcopy.o
+lkdtm-$(CONFIG_LKDTM)          += usercopy.o
+
+KCOV_INSTRUMENT_rodata.o       := n
+
+OBJCOPYFLAGS :=
+OBJCOPYFLAGS_rodata_objcopy.o  := \
+                       --set-section-flags .text=alloc,readonly \
+                       --rename-section .text=.rodata
+targets += rodata.o rodata_objcopy.o
+$(obj)/rodata_objcopy.o: $(obj)/rodata.o FORCE
+       $(call if_changed,objcopy)
similarity index 99%
rename from drivers/misc/lkdtm_refcount.c
rename to drivers/misc/lkdtm/refcount.c
index 2b99d448e7fd00921924da4a466ffa8fcb443a11..0a146b32da132f9f38af747372214604b08836f9 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * This is for all the tests related to refcount bugs (e.g. overflow,
  * underflow, reaching zero untested, etc).
index 772d02922529e2722184d23df7e65891f1b2b596..b1133739fb4b2a21743cb313a93516744990f4af 100644 (file)
@@ -74,6 +74,23 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
                goto out;
        }
 
+       while (cl->tx_cb_queued >= bus->tx_queue_limit) {
+               mutex_unlock(&bus->device_lock);
+               rets = wait_event_interruptible(cl->tx_wait,
+                               cl->writing_state == MEI_WRITE_COMPLETE ||
+                               (!mei_cl_is_connected(cl)));
+               mutex_lock(&bus->device_lock);
+               if (rets) {
+                       if (signal_pending(current))
+                               rets = -EINTR;
+                       goto out;
+               }
+               if (!mei_cl_is_connected(cl)) {
+                       rets = -ENODEV;
+                       goto out;
+               }
+       }
+
        cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
        if (!cb) {
                rets = -ENOMEM;
@@ -449,6 +466,29 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev)
 }
 EXPORT_SYMBOL_GPL(mei_cldev_enabled);
 
+/**
+ * mei_cl_bus_module_get - acquire module of the underlying
+ *    hw driver.
+ *
+ * @cldev: mei client device
+ *
+ * Return: true on success; false if the module was removed.
+ */
+static bool mei_cl_bus_module_get(struct mei_cl_device *cldev)
+{
+       return try_module_get(cldev->bus->dev->driver->owner);
+}
+
+/**
+ * mei_cl_bus_module_put -  release the underlying hw module.
+ *
+ * @cldev: mei client device
+ */
+static void mei_cl_bus_module_put(struct mei_cl_device *cldev)
+{
+       module_put(cldev->bus->dev->driver->owner);
+}
+
 /**
  * mei_cldev_enable - enable me client device
  *     create connection with me client
@@ -487,9 +527,17 @@ int mei_cldev_enable(struct mei_cl_device *cldev)
                goto out;
        }
 
+       if (!mei_cl_bus_module_get(cldev)) {
+               dev_err(&cldev->dev, "get hw module failed");
+               ret = -ENODEV;
+               goto out;
+       }
+
        ret = mei_cl_connect(cl, cldev->me_cl, NULL);
-       if (ret < 0)
+       if (ret < 0) {
                dev_err(&cldev->dev, "cannot connect\n");
+               mei_cl_bus_module_put(cldev);
+       }
 
 out:
        mutex_unlock(&bus->device_lock);
@@ -553,6 +601,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
                dev_err(bus->dev, "Could not disconnect from the ME client\n");
 
 out:
+       mei_cl_bus_module_put(cldev);
+
        /* Flush queues and remove any pending read */
        mei_cl_flush_queues(cl, NULL);
        mei_cl_unlink(cl);
@@ -562,37 +612,6 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
 }
 EXPORT_SYMBOL_GPL(mei_cldev_disable);
 
-/**
- * mei_cl_bus_module_get - acquire module of the underlying
- *    hw module.
- *
- * @cl: host client
- *
- * Return: true on success; false if the module was removed.
- */
-bool mei_cl_bus_module_get(struct mei_cl *cl)
-{
-       struct mei_cl_device *cldev = cl->cldev;
-
-       if (!cldev)
-               return true;
-
-       return try_module_get(cldev->bus->dev->driver->owner);
-}
-
-/**
- * mei_cl_bus_module_put -  release the underlying hw module.
- *
- * @cl: host client
- */
-void mei_cl_bus_module_put(struct mei_cl *cl)
-{
-       struct mei_cl_device *cldev = cl->cldev;
-
-       if (cldev)
-               module_put(cldev->bus->dev->driver->owner);
-}
-
 /**
  * mei_cl_device_find - find matching entry in the driver id table
  *
index 7e60c1817c311e0b54a0711c116adb8eea89cf00..8d6197a88b54551d07de68d2c4387add2a7fbc5a 100644 (file)
@@ -349,6 +349,36 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
        kfree(cb);
 }
 
+/**
+ * mei_tx_cb_queue - queue tx callback
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @cb: mei callback struct
+ * @head: an instance of list to queue on
+ */
+static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb,
+                                    struct list_head *head)
+{
+       list_add_tail(&cb->list, head);
+       cb->cl->tx_cb_queued++;
+}
+
+/**
+ * mei_tx_cb_dequeue - dequeue tx callback
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @cb: mei callback struct to dequeue and free
+ */
+static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
+{
+       if (!WARN_ON(cb->cl->tx_cb_queued == 0))
+               cb->cl->tx_cb_queued--;
+
+       mei_io_cb_free(cb);
+}
+
 /**
  * mei_io_cb_init - allocate and initialize io callback
  *
@@ -377,49 +407,37 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
 }
 
 /**
- * __mei_io_list_flush_cl - removes and frees cbs belonging to cl.
+ * mei_io_list_flush_cl - removes cbs belonging to the cl.
  *
  * @head:  an instance of our list structure
- * @cl:    host client, can be NULL for flushing the whole list
- * @free:  whether to free the cbs
+ * @cl:    host client
  */
-static void __mei_io_list_flush_cl(struct list_head *head,
-                                  const struct mei_cl *cl, bool free)
+static void mei_io_list_flush_cl(struct list_head *head,
+                                const struct mei_cl *cl)
 {
        struct mei_cl_cb *cb, *next;
 
-       /* enable removing everything if no cl is specified */
        list_for_each_entry_safe(cb, next, head, list) {
-               if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
+               if (mei_cl_cmp_id(cl, cb->cl))
                        list_del_init(&cb->list);
-                       if (free)
-                               mei_io_cb_free(cb);
-               }
        }
 }
 
 /**
- * mei_io_list_flush_cl - removes list entry belonging to cl.
+ * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them
  *
  * @head: An instance of our list structure
  * @cl: host client
  */
-static inline void mei_io_list_flush_cl(struct list_head *head,
-                                       const struct mei_cl *cl)
+static void mei_io_tx_list_free_cl(struct list_head *head,
+                                  const struct mei_cl *cl)
 {
-       __mei_io_list_flush_cl(head, cl, false);
-}
+       struct mei_cl_cb *cb, *next;
 
-/**
- * mei_io_list_free_cl - removes cb belonging to cl and free them
- *
- * @head: An instance of our list structure
- * @cl: host client
- */
-static inline void mei_io_list_free_cl(struct list_head *head,
-                                      const struct mei_cl *cl)
-{
-       __mei_io_list_flush_cl(head, cl, true);
+       list_for_each_entry_safe(cb, next, head, list) {
+               if (mei_cl_cmp_id(cl, cb->cl))
+                       mei_tx_cb_dequeue(cb);
+       }
 }
 
 /**
@@ -538,8 +556,8 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
        dev = cl->dev;
 
        cl_dbg(dev, cl, "remove list entry belonging to cl\n");
-       mei_io_list_free_cl(&cl->dev->write_list, cl);
-       mei_io_list_free_cl(&cl->dev->write_waiting_list, cl);
+       mei_io_tx_list_free_cl(&cl->dev->write_list, cl);
+       mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl);
        mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl);
        mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
        mei_io_list_free_fp(&cl->rd_pending, fp);
@@ -756,8 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
                return;
 
        cl->state = MEI_FILE_DISCONNECTED;
-       mei_io_list_free_cl(&dev->write_list, cl);
-       mei_io_list_free_cl(&dev->write_waiting_list, cl);
+       mei_io_tx_list_free_cl(&dev->write_list, cl);
+       mei_io_tx_list_free_cl(&dev->write_waiting_list, cl);
        mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
        mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
        mei_cl_wake_all(cl);
@@ -765,8 +783,6 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
        cl->tx_flow_ctrl_creds = 0;
        cl->timer_count = 0;
 
-       mei_cl_bus_module_put(cl);
-
        if (!cl->me_cl)
                return;
 
@@ -1076,9 +1092,6 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
 
        dev = cl->dev;
 
-       if (!mei_cl_bus_module_get(cl))
-               return -ENODEV;
-
        rets = mei_cl_set_connecting(cl, me_cl);
        if (rets)
                goto nortpm;
@@ -1698,9 +1711,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
 
 out:
        if (mei_hdr.msg_complete)
-               list_add_tail(&cb->list, &dev->write_waiting_list);
+               mei_tx_cb_enqueue(cb, &dev->write_waiting_list);
        else
-               list_add_tail(&cb->list, &dev->write_list);
+               mei_tx_cb_enqueue(cb, &dev->write_list);
 
        cb = NULL;
        if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
@@ -1746,7 +1759,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
 
        switch (cb->fop_type) {
        case MEI_FOP_WRITE:
-               mei_io_cb_free(cb);
+               mei_tx_cb_dequeue(cb);
                cl->writing_state = MEI_WRITE_COMPLETE;
                if (waitqueue_active(&cl->tx_wait)) {
                        wake_up_interruptible(&cl->tx_wait);
index a617aa5a3ad8434119bf7cb69a19d62f4a66a320..c815da91089c79a98f3313cb7440a904dcf87b91 100644 (file)
@@ -97,7 +97,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
        int pos = 0;
        int ret;
 
-#define HDR "   |me|host|state|rd|wr|\n"
+#define HDR "   |me|host|state|rd|wr|wrq\n"
 
        if (!dev)
                return -ENODEV;
@@ -130,9 +130,10 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
        list_for_each_entry(cl, &dev->file_list, link) {
 
                pos += scnprintf(buf + pos, bufsz - pos,
-                       "%3d|%2d|%4d|%5d|%2d|%2d|\n",
+                       "%3d|%2d|%4d|%5d|%2d|%2d|%3u\n",
                        i, mei_cl_me_id(cl), cl->host_client_id, cl->state,
-                       !list_empty(&cl->rd_completed), cl->writing_state);
+                       !list_empty(&cl->rd_completed), cl->writing_state,
+                       cl->tx_cb_queued);
                i++;
        }
 out:
index c46f6e99a55efb19fa3477481183d6c3f6f87c2e..4888ebc076b74b5f8ee9cdf3d956e73dd2929fdb 100644 (file)
@@ -383,6 +383,7 @@ void mei_device_init(struct mei_device *dev,
        INIT_LIST_HEAD(&dev->write_waiting_list);
        INIT_LIST_HEAD(&dev->ctrl_wr_list);
        INIT_LIST_HEAD(&dev->ctrl_rd_list);
+       dev->tx_queue_limit = MEI_TX_QUEUE_LIMIT_DEFAULT;
 
        INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
        INIT_WORK(&dev->reset_work, mei_reset_work);
index 758dc73602d5ed4618ccf2347daaad0c526f1161..7465f17e1559fd3bb3b1f807b150d36dd59f3b51 100644 (file)
@@ -291,6 +291,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                goto out;
        }
 
+       while (cl->tx_cb_queued >= dev->tx_queue_limit) {
+               if (file->f_flags & O_NONBLOCK) {
+                       rets = -EAGAIN;
+                       goto out;
+               }
+               mutex_unlock(&dev->device_lock);
+               rets = wait_event_interruptible(cl->tx_wait,
+                               cl->writing_state == MEI_WRITE_COMPLETE ||
+                               (!mei_cl_is_connected(cl)));
+               mutex_lock(&dev->device_lock);
+               if (rets) {
+                       if (signal_pending(current))
+                               rets = -EINTR;
+                       goto out;
+               }
+               if (!mei_cl_is_connected(cl)) {
+                       rets = -ENODEV;
+                       goto out;
+               }
+       }
+
        *offset = 0;
        cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
        if (!cb) {
@@ -507,7 +528,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
                break;
 
        default:
-               dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd);
                rets = -ENOIOCTLCMD;
        }
 
@@ -580,6 +600,12 @@ static __poll_t mei_poll(struct file *file, poll_table *wait)
                        mei_cl_read_start(cl, mei_cl_mtu(cl), file);
        }
 
+       if (req_events & (POLLOUT | POLLWRNORM)) {
+               poll_wait(file, &cl->tx_wait, wait);
+               if (cl->tx_cb_queued < dev->tx_queue_limit)
+                       mask |= POLLOUT | POLLWRNORM;
+       }
+
 out:
        mutex_unlock(&dev->device_lock);
        return mask;
@@ -749,10 +775,48 @@ static ssize_t hbm_ver_drv_show(struct device *device,
 }
 static DEVICE_ATTR_RO(hbm_ver_drv);
 
+static ssize_t tx_queue_limit_show(struct device *device,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct mei_device *dev = dev_get_drvdata(device);
+       u8 size = 0;
+
+       mutex_lock(&dev->device_lock);
+       size = dev->tx_queue_limit;
+       mutex_unlock(&dev->device_lock);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", size);
+}
+
+static ssize_t tx_queue_limit_store(struct device *device,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct mei_device *dev = dev_get_drvdata(device);
+       u8 limit;
+       unsigned int inp;
+       int err;
+
+       err = kstrtouint(buf, 10, &inp);
+       if (err)
+               return err;
+       if (inp > MEI_TX_QUEUE_LIMIT_MAX || inp < MEI_TX_QUEUE_LIMIT_MIN)
+               return -EINVAL;
+       limit = inp;
+
+       mutex_lock(&dev->device_lock);
+       dev->tx_queue_limit = limit;
+       mutex_unlock(&dev->device_lock);
+
+       return count;
+}
+static DEVICE_ATTR_RW(tx_queue_limit);
+
 static struct attribute *mei_attrs[] = {
        &dev_attr_fw_status.attr,
        &dev_attr_hbm_ver.attr,
        &dev_attr_hbm_ver_drv.attr,
+       &dev_attr_tx_queue_limit.attr,
        NULL
 };
 ATTRIBUTE_GROUPS(mei);
index ebcd5132e447e0290dbefa563ea2356fa6705ed3..be9c48415da947c0672b6b73f756333a028a035e 100644 (file)
@@ -210,6 +210,7 @@ struct mei_cl_cb {
  * @timer_count:  watchdog timer for operation completion
  * @notify_en: notification - enabled/disabled
  * @notify_ev: pending notification event
+ * @tx_cb_queued: number of tx callbacks in queue
  * @writing_state: state of the tx
  * @rd_pending: pending read credits
  * @rd_completed: completed read
@@ -234,6 +235,7 @@ struct mei_cl {
        u8 timer_count;
        u8 notify_en;
        u8 notify_ev;
+       u8 tx_cb_queued;
        enum mei_file_transaction_states writing_state;
        struct list_head rd_pending;
        struct list_head rd_completed;
@@ -241,6 +243,10 @@ struct mei_cl {
        struct mei_cl_device *cldev;
 };
 
+#define MEI_TX_QUEUE_LIMIT_DEFAULT 50
+#define MEI_TX_QUEUE_LIMIT_MAX 255
+#define MEI_TX_QUEUE_LIMIT_MIN 30
+
 /**
  * struct mei_hw_ops - hw specific ops
  *
@@ -315,8 +321,6 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
 bool mei_cl_bus_rx_event(struct mei_cl *cl);
 bool mei_cl_bus_notify_event(struct mei_cl *cl);
 void mei_cl_bus_remove_devices(struct mei_device *bus);
-bool mei_cl_bus_module_get(struct mei_cl *cl);
-void mei_cl_bus_module_put(struct mei_cl *cl);
 int mei_cl_bus_init(void);
 void mei_cl_bus_exit(void);
 
@@ -361,6 +365,7 @@ const char *mei_pg_state_str(enum mei_pg_state state);
  * @write_waiting_list : write completion list
  * @ctrl_wr_list : pending control write list
  * @ctrl_rd_list : pending control read list
+ * @tx_queue_limit: tx queues per client linit
  *
  * @file_list   : list of opened handles
  * @open_handle_count: number of opened handles
@@ -425,6 +430,7 @@ struct mei_device {
        struct list_head write_waiting_list;
        struct list_head ctrl_wr_list;
        struct list_head ctrl_rd_list;
+       u8 tx_queue_limit;
 
        struct list_head file_list;
        long open_handle_count;
index fd7f2a6049f86218ded7d3033773e03514ae8424..e5bb9c749b5d113eab7de0284af943aa06725e16 100644 (file)
@@ -135,7 +135,9 @@ EXPORT_SYMBOL_GPL(vop_unregister_driver);
 
 static void vop_release_dev(struct device *d)
 {
-       put_device(d);
+       struct vop_device *dev = dev_to_vop(d);
+
+       kfree(dev);
 }
 
 struct vop_device *
@@ -174,7 +176,7 @@ vop_register_device(struct device *pdev, int id,
                goto free_vdev;
        return vdev;
 free_vdev:
-       kfree(vdev);
+       put_device(&vdev->dev);
        return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(vop_register_device);
index 0051d9ec76ccd2ecbd29601c48ade5f94eed83e1..21f425472a82fe773d916324dd59f8649ea60c4f 100644 (file)
@@ -519,7 +519,7 @@ static struct ocxl_fn *init_function(struct pci_dev *dev)
        rc = device_register(&fn->dev);
        if (rc) {
                deconfigure_function(fn);
-               device_unregister(&fn->dev);
+               put_device(&fn->dev);
                return ERR_PTR(rc);
        }
        return fn;
index 55cf64ee7f7b0d15a8a183b036ac2ebd6839c4dd..1090924efdb1231a2ad31dddf6658870abdfa5f9 100644 (file)
@@ -167,10 +167,10 @@ config MESON_MX_EFUSE
 
 config NVMEM_SNVS_LPGPR
        tristate "Support for Low Power General Purpose Register"
-       depends on SOC_IMX6 || COMPILE_TEST
+       depends on SOC_IMX6 || SOC_IMX7D || COMPILE_TEST
        help
          This is a driver for Low Power General Purpose Register (LPGPR) available on
-         i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
+         i.MX6 and i.MX7 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
 
          This driver can also be built as a module. If so, the module
          will be called nvmem-snvs-lpgpr.
index 5e9e324427f9186ceea6d16e33116bb9d919cf21..4159b3f41d7933a72514add98dd0e1eb935a5748 100644 (file)
@@ -262,8 +262,7 @@ static int bcm_otpc_probe(struct platform_device *pdev)
        else if (of_device_is_compatible(dev->of_node, "brcm,ocotp-v2"))
                priv->map = &otp_map_v2;
        else {
-               dev_err(&pdev->dev,
-                       "%s otpc config map not defined\n", __func__);
+               dev_err(dev, "%s otpc config map not defined\n", __func__);
                return -EINVAL;
        }
 
@@ -302,27 +301,17 @@ static int bcm_otpc_probe(struct platform_device *pdev)
 
        priv->config = &bcm_otpc_nvmem_config;
 
-       nvmem = nvmem_register(&bcm_otpc_nvmem_config);
+       nvmem = devm_nvmem_register(dev, &bcm_otpc_nvmem_config);
        if (IS_ERR(nvmem)) {
                dev_err(dev, "error registering nvmem config\n");
                return PTR_ERR(nvmem);
        }
 
-       platform_set_drvdata(pdev, nvmem);
-
        return 0;
 }
 
-static int bcm_otpc_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
-}
-
 static struct platform_driver bcm_otpc_driver = {
        .probe  = bcm_otpc_probe,
-       .remove = bcm_otpc_remove,
        .driver = {
                .name   = "brcm-otpc",
                .of_match_table = bcm_otpc_dt_ids,
index 35a3dbeea3245589107f680ae1044748a7027ff8..b05aa8e813031dd9d97511aab8a8a09046aaa4ae 100644 (file)
@@ -473,9 +473,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
        nvmem->reg_read = config->reg_read;
        nvmem->reg_write = config->reg_write;
        nvmem->dev.of_node = config->dev->of_node;
-       dev_set_name(&nvmem->dev, "%s%d",
-                    config->name ? : "nvmem",
-                    config->name ? config->id : nvmem->id);
+
+       if (config->id == -1 && config->name) {
+               dev_set_name(&nvmem->dev, "%s", config->name);
+       } else {
+               dev_set_name(&nvmem->dev, "%s%d",
+                            config->name ? : "nvmem",
+                            config->name ? config->id : nvmem->id);
+       }
 
        nvmem->read_only = device_property_present(config->dev, "read-only") |
                           config->read_only;
@@ -544,6 +549,65 @@ int nvmem_unregister(struct nvmem_device *nvmem)
 }
 EXPORT_SYMBOL_GPL(nvmem_unregister);
 
+static void devm_nvmem_release(struct device *dev, void *res)
+{
+       WARN_ON(nvmem_unregister(*(struct nvmem_device **)res));
+}
+
+/**
+ * devm_nvmem_register() - Register a managed nvmem device for given
+ * nvmem_config.
+ * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
+ *
+ * @config: nvmem device configuration with which nvmem device is created.
+ *
+ * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
+ * on success.
+ */
+struct nvmem_device *devm_nvmem_register(struct device *dev,
+                                        const struct nvmem_config *config)
+{
+       struct nvmem_device **ptr, *nvmem;
+
+       ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       nvmem = nvmem_register(config);
+
+       if (!IS_ERR(nvmem)) {
+               *ptr = nvmem;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return nvmem;
+}
+EXPORT_SYMBOL_GPL(devm_nvmem_register);
+
+static int devm_nvmem_match(struct device *dev, void *res, void *data)
+{
+       struct nvmem_device **r = res;
+
+       return *r == data;
+}
+
+/**
+ * devm_nvmem_unregister() - Unregister previously registered managed nvmem
+ * device.
+ *
+ * @nvmem: Pointer to previously registered nvmem device.
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
+{
+       return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem);
+}
+EXPORT_SYMBOL(devm_nvmem_unregister);
+
+
 static struct nvmem_device *__nvmem_device_get(struct device_node *np,
                                               struct nvmem_cell **cellp,
                                               const char *cell_id)
index 52cfe91d976292c465c464e394d5b4231f257443..6651e4cdc002888c78c1b378d8634b35f70d1479 100644 (file)
@@ -125,7 +125,7 @@ static int imx_iim_probe(struct platform_device *pdev)
 
        drvdata = of_id->data;
 
-       iim->clk = devm_clk_get(&pdev->dev, NULL);
+       iim->clk = devm_clk_get(dev, NULL);
        if (IS_ERR(iim->clk))
                return PTR_ERR(iim->clk);
 
@@ -138,25 +138,13 @@ static int imx_iim_probe(struct platform_device *pdev)
        cfg.size = drvdata->nregs;
        cfg.priv = iim;
 
-       nvmem = nvmem_register(&cfg);
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
+       nvmem = devm_nvmem_register(dev, &cfg);
 
-       platform_set_drvdata(pdev, nvmem);
-
-       return 0;
-}
-
-static int imx_iim_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static struct platform_driver imx_iim_driver = {
        .probe  = imx_iim_probe,
-       .remove = imx_iim_remove,
        .driver = {
                .name   = "imx-iim",
                .of_match_table = imx_iim_dt_ids,
index d7ba351a70c9f142c2b3cea9f910e5543da3487e..60816c856dd62cdb532e1ffb0310a879539349dd 100644 (file)
@@ -439,7 +439,6 @@ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
 
 static int imx_ocotp_probe(struct platform_device *pdev)
 {
-       const struct of_device_id *of_id;
        struct device *dev = &pdev->dev;
        struct resource *res;
        struct ocotp_priv *priv;
@@ -460,32 +459,19 @@ static int imx_ocotp_probe(struct platform_device *pdev)
        if (IS_ERR(priv->clk))
                return PTR_ERR(priv->clk);
 
-       of_id = of_match_device(imx_ocotp_dt_ids, dev);
        priv->params = of_device_get_match_data(&pdev->dev);
        imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
        imx_ocotp_nvmem_config.dev = dev;
        imx_ocotp_nvmem_config.priv = priv;
        priv->config = &imx_ocotp_nvmem_config;
-       nvmem = nvmem_register(&imx_ocotp_nvmem_config);
+       nvmem = devm_nvmem_register(dev, &imx_ocotp_nvmem_config);
 
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
 
-       platform_set_drvdata(pdev, nvmem);
-
-       return 0;
-}
-
-static int imx_ocotp_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static struct platform_driver imx_ocotp_driver = {
        .probe  = imx_ocotp_probe,
-       .remove = imx_ocotp_remove,
        .driver = {
                .name   = "imx_ocotp",
                .of_match_table = imx_ocotp_dt_ids,
index 95268db155e9c9333bda35ab6416594c0892223a..549b5298ac4c10fe0c646021538b72d622419b98 100644 (file)
@@ -86,20 +86,9 @@ static int lpc18xx_otp_probe(struct platform_device *pdev)
        lpc18xx_otp_nvmem_config.dev = &pdev->dev;
        lpc18xx_otp_nvmem_config.priv = otp;
 
-       nvmem = nvmem_register(&lpc18xx_otp_nvmem_config);
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
+       nvmem = devm_nvmem_register(&pdev->dev, &lpc18xx_otp_nvmem_config);
 
-       platform_set_drvdata(pdev, nvmem);
-
-       return 0;
-}
-
-static int lpc18xx_otp_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static const struct of_device_id lpc18xx_otp_dt_ids[] = {
@@ -110,7 +99,6 @@ MODULE_DEVICE_TABLE(of, lpc18xx_otp_dt_ids);
 
 static struct platform_driver lpc18xx_otp_driver = {
        .probe  = lpc18xx_otp_probe,
-       .remove = lpc18xx_otp_remove,
        .driver = {
                .name   = "lpc18xx_otp",
                .of_match_table = lpc18xx_otp_dt_ids,
index a43c68f909373a03c173ed1f52c79ec55e713aeb..71823d1403c5201539b36d682135258ddf655455 100644 (file)
@@ -60,25 +60,13 @@ static int meson_efuse_probe(struct platform_device *pdev)
        econfig.reg_read = meson_efuse_read;
        econfig.size = size;
 
-       nvmem = nvmem_register(&econfig);
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
+       nvmem = devm_nvmem_register(&pdev->dev, &econfig);
 
-       platform_set_drvdata(pdev, nvmem);
-
-       return 0;
-}
-
-static int meson_efuse_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static struct platform_driver meson_efuse_driver = {
        .probe = meson_efuse_probe,
-       .remove = meson_efuse_remove,
        .driver = {
                .name = "meson-efuse",
                .of_match_table = meson_efuse_match,
index 41d3a3c1104ec7f810c7f9c33a2cab414c475299..a085563e39e3bfc9240c4fb25e0e6db89a2e6273 100644 (file)
@@ -233,25 +233,13 @@ static int meson_mx_efuse_probe(struct platform_device *pdev)
                return PTR_ERR(efuse->core_clk);
        }
 
-       efuse->nvmem = nvmem_register(&efuse->config);
-       if (IS_ERR(efuse->nvmem))
-               return PTR_ERR(efuse->nvmem);
+       efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
 
-       platform_set_drvdata(pdev, efuse);
-
-       return 0;
-}
-
-static int meson_mx_efuse_remove(struct platform_device *pdev)
-{
-       struct meson_mx_efuse *efuse = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(efuse->nvmem);
+       return PTR_ERR_OR_ZERO(efuse->nvmem);
 }
 
 static struct platform_driver meson_mx_efuse_driver = {
        .probe = meson_mx_efuse_probe,
-       .remove = meson_mx_efuse_remove,
        .driver = {
                .name = "meson-mx-efuse",
                .of_match_table = meson_mx_efuse_match,
index 9ee3479cfc7b2cedcbffe5950f3e3d7051c4e172..e66adf17a7477e25cde8f5a7b55283fc358ab8b3 100644 (file)
@@ -72,20 +72,9 @@ static int mtk_efuse_probe(struct platform_device *pdev)
        econfig.size = resource_size(res);
        econfig.priv = priv;
        econfig.dev = dev;
-       nvmem = nvmem_register(&econfig);
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
+       nvmem = devm_nvmem_register(dev, &econfig);
 
-       platform_set_drvdata(pdev, nvmem);
-
-       return 0;
-}
-
-static int mtk_efuse_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static const struct of_device_id mtk_efuse_of_match[] = {
@@ -97,7 +86,6 @@ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
 
 static struct platform_driver mtk_efuse_driver = {
        .probe = mtk_efuse_probe,
-       .remove = mtk_efuse_remove,
        .driver = {
                .name = "mediatek,efuse",
                .of_match_table = mtk_efuse_of_match,
index cb3b48b47d649ac986f4c9597fc2d1a633f3db5a..4f650baad9835beec7fbc9f926b661b815b71a07 100644 (file)
@@ -47,13 +47,6 @@ static int qfprom_reg_write(void *context,
        return 0;
 }
 
-static int qfprom_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
-}
-
 static struct nvmem_config econfig = {
        .name = "qfprom",
        .stride = 1,
@@ -82,13 +75,9 @@ static int qfprom_probe(struct platform_device *pdev)
        econfig.dev = dev;
        econfig.priv = priv;
 
-       nvmem = nvmem_register(&econfig);
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
-
-       platform_set_drvdata(pdev, nvmem);
+       nvmem = devm_nvmem_register(dev, &econfig);
 
-       return 0;
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static const struct of_device_id qfprom_of_match[] = {
@@ -99,7 +88,6 @@ MODULE_DEVICE_TABLE(of, qfprom_of_match);
 
 static struct platform_driver qfprom_driver = {
        .probe = qfprom_probe,
-       .remove = qfprom_remove,
        .driver = {
                .name = "qcom,qfprom",
                .of_match_table = qfprom_of_match,
index f13a8335f3642d576a09cd73c7add3934f29cd8f..b3b0b648be62b929e99d2554aaaef1eeab0b4bf5 100644 (file)
@@ -259,55 +259,43 @@ static int rockchip_efuse_probe(struct platform_device *pdev)
        struct resource *res;
        struct nvmem_device *nvmem;
        struct rockchip_efuse_chip *efuse;
-       const struct of_device_id *match;
+       const void *data;
        struct device *dev = &pdev->dev;
 
-       match = of_match_device(dev->driver->of_match_table, dev);
-       if (!match || !match->data) {
+       data = of_device_get_match_data(dev);
+       if (!data) {
                dev_err(dev, "failed to get match data\n");
                return -EINVAL;
        }
 
-       efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip),
+       efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip),
                             GFP_KERNEL);
        if (!efuse)
                return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       efuse->base = devm_ioremap_resource(&pdev->dev, res);
+       efuse->base = devm_ioremap_resource(dev, res);
        if (IS_ERR(efuse->base))
                return PTR_ERR(efuse->base);
 
-       efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse");
+       efuse->clk = devm_clk_get(dev, "pclk_efuse");
        if (IS_ERR(efuse->clk))
                return PTR_ERR(efuse->clk);
 
-       efuse->dev = &pdev->dev;
+       efuse->dev = dev;
        if (of_property_read_u32(dev->of_node, "rockchip,efuse-size",
                                 &econfig.size))
                econfig.size = resource_size(res);
-       econfig.reg_read = match->data;
+       econfig.reg_read = data;
        econfig.priv = efuse;
        econfig.dev = efuse->dev;
-       nvmem = nvmem_register(&econfig);
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
+       nvmem = devm_nvmem_register(dev, &econfig);
 
-       platform_set_drvdata(pdev, nvmem);
-
-       return 0;
-}
-
-static int rockchip_efuse_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static struct platform_driver rockchip_efuse_driver = {
        .probe = rockchip_efuse_probe,
-       .remove = rockchip_efuse_remove,
        .driver = {
                .name = "rockchip-efuse",
                .of_match_table = rockchip_efuse_match,
index e5c2a4a17f03db668fdfa65677949c428beae539..c050a23a9f2b59760a1d9198160661cf316db3c4 100644 (file)
 #include <linux/regmap.h>
 
 #define IMX6Q_SNVS_HPLR                0x00
-#define IMX6Q_GPR_SL           BIT(5)
 #define IMX6Q_SNVS_LPLR                0x34
-#define IMX6Q_GPR_HL           BIT(5)
 #define IMX6Q_SNVS_LPGPR       0x68
 
+#define IMX7D_SNVS_HPLR                0x00
+#define IMX7D_SNVS_LPLR                0x34
+#define IMX7D_SNVS_LPGPR       0x90
+
+#define IMX_GPR_SL             BIT(5)
+#define IMX_GPR_HL             BIT(5)
+
 struct snvs_lpgpr_cfg {
        int offset;
        int offset_hplr;
        int offset_lplr;
+       int size;
 };
 
 struct snvs_lpgpr_priv {
@@ -36,6 +42,14 @@ static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = {
        .offset         = IMX6Q_SNVS_LPGPR,
        .offset_hplr    = IMX6Q_SNVS_HPLR,
        .offset_lplr    = IMX6Q_SNVS_LPLR,
+       .size           = 4,
+};
+
+static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d = {
+       .offset         = IMX7D_SNVS_LPGPR,
+       .offset_hplr    = IMX7D_SNVS_HPLR,
+       .offset_lplr    = IMX7D_SNVS_LPLR,
+       .size           = 16,
 };
 
 static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
@@ -50,14 +64,14 @@ static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
        if (ret < 0)
                return ret;
 
-       if (lock_reg & IMX6Q_GPR_SL)
+       if (lock_reg & IMX_GPR_SL)
                return -EPERM;
 
        ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg);
        if (ret < 0)
                return ret;
 
-       if (lock_reg & IMX6Q_GPR_HL)
+       if (lock_reg & IMX_GPR_HL)
                return -EPERM;
 
        return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val,
@@ -110,40 +124,29 @@ static int snvs_lpgpr_probe(struct platform_device *pdev)
        cfg->priv = priv;
        cfg->name = dev_name(dev);
        cfg->dev = dev;
-       cfg->stride = 4,
-       cfg->word_size = 4,
-       cfg->size = 4,
-       cfg->owner = THIS_MODULE,
-       cfg->reg_read  = snvs_lpgpr_read,
-       cfg->reg_write = snvs_lpgpr_write,
-
-       nvmem = nvmem_register(cfg);
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
+       cfg->stride = 4;
+       cfg->word_size = 4;
+       cfg->size = dcfg->size,
+       cfg->owner = THIS_MODULE;
+       cfg->reg_read  = snvs_lpgpr_read;
+       cfg->reg_write = snvs_lpgpr_write;
 
-       platform_set_drvdata(pdev, nvmem);
-
-       return 0;
-}
-
-static int snvs_lpgpr_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
+       nvmem = devm_nvmem_register(dev, cfg);
 
-       return nvmem_unregister(nvmem);
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static const struct of_device_id snvs_lpgpr_dt_ids[] = {
        { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q },
        { .compatible = "fsl,imx6ul-snvs-lpgpr",
          .data = &snvs_lpgpr_cfg_imx6q },
+       { .compatible = "fsl,imx7d-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx7d },
        { },
 };
 MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids);
 
 static struct platform_driver snvs_lpgpr_driver = {
        .probe  = snvs_lpgpr_probe,
-       .remove = snvs_lpgpr_remove,
        .driver = {
                .name   = "snvs_lpgpr",
                .of_match_table = snvs_lpgpr_dt_ids,
@@ -152,5 +155,5 @@ static struct platform_driver snvs_lpgpr_driver = {
 module_platform_driver(snvs_lpgpr_driver);
 
 MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
-MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage");
+MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage");
 MODULE_LICENSE("GPL v2");
index 99bd54d85fcbab9a30a4a5f952a8e82a0388dca1..26bb637afe924b314a25ec1a2ca6f5e669a14f24 100644 (file)
@@ -85,13 +85,14 @@ static int sunxi_sid_read(void *context, unsigned int offset,
 }
 
 static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
-                                     const unsigned int word)
+                                     const unsigned int offset,
+                                     u32 *out)
 {
        u32 reg_val;
        int ret;
 
        /* Set word, lock access, and set read command */
-       reg_val = (word & SUN8I_SID_OFFSET_MASK)
+       reg_val = (offset & SUN8I_SID_OFFSET_MASK)
                  << SUN8I_SID_OFFSET_SHIFT;
        reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ;
        writel(reg_val, sid->base + SUN8I_SID_PRCTL);
@@ -101,7 +102,49 @@ static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
        if (ret)
                return ret;
 
+       if (out)
+               *out = readl(sid->base + SUN8I_SID_RDKEY);
+
        writel(0, sid->base + SUN8I_SID_PRCTL);
+
+       return 0;
+}
+
+/*
+ * On Allwinner H3, the value on the 0x200 offset of the SID controller seems
+ * to be not reliable at all.
+ * Read by the registers instead.
+ */
+static int sun8i_sid_read_byte_by_reg(const struct sunxi_sid *sid,
+                                     const unsigned int offset,
+                                     u8 *out)
+{
+       u32 word;
+       int ret;
+
+       ret = sun8i_sid_register_readout(sid, offset & ~0x03, &word);
+
+       if (ret)
+               return ret;
+
+       *out = (word >> ((offset & 0x3) * 8)) & 0xff;
+
+       return 0;
+}
+
+static int sun8i_sid_read_by_reg(void *context, unsigned int offset,
+                                void *val, size_t bytes)
+{
+       struct sunxi_sid *sid = context;
+       u8 *buf = val;
+       int ret;
+
+       while (bytes--) {
+               ret = sun8i_sid_read_byte_by_reg(sid, offset++, buf++);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -131,26 +174,12 @@ static int sunxi_sid_probe(struct platform_device *pdev)
 
        size = cfg->size;
 
-       if (cfg->need_register_readout) {
-               /*
-                * H3's SID controller have a bug that the value at 0x200
-                * offset is not the correct value when the hardware is reseted.
-                * However, after doing a register-based read operation, the
-                * value become right.
-                * Do a full read operation here, but ignore its value
-                * (as it's more fast to read by direct MMIO value than
-                * with registers)
-                */
-               for (i = 0; i < (size >> 2); i++) {
-                       ret = sun8i_sid_register_readout(sid, i);
-                       if (ret)
-                               return ret;
-               }
-       }
-
        econfig.size = size;
        econfig.dev = dev;
-       econfig.reg_read = sunxi_sid_read;
+       if (cfg->need_register_readout)
+               econfig.reg_read = sun8i_sid_read_by_reg;
+       else
+               econfig.reg_read = sunxi_sid_read;
        econfig.priv = sid;
        nvmem = nvmem_register(&econfig);
        if (IS_ERR(nvmem))
@@ -163,7 +192,7 @@ static int sunxi_sid_probe(struct platform_device *pdev)
        }
 
        for (i = 0; i < size; i++)
-               randomness[i] = sunxi_sid_read_byte(sid, i);
+               econfig.reg_read(sid, i, &randomness[i], 1);
 
        add_device_randomness(randomness, size);
        kfree(randomness);
index be11880a1358d95dfec7f4955fa83de4c28b9a65..271f0b2ff86a7d681106941d6ec56486bd8947ec 100644 (file)
@@ -60,20 +60,9 @@ static int uniphier_efuse_probe(struct platform_device *pdev)
        econfig.size = resource_size(res);
        econfig.priv = priv;
        econfig.dev = dev;
-       nvmem = nvmem_register(&econfig);
-       if (IS_ERR(nvmem))
-               return PTR_ERR(nvmem);
+       nvmem = devm_nvmem_register(dev, &econfig);
 
-       platform_set_drvdata(pdev, nvmem);
-
-       return 0;
-}
-
-static int uniphier_efuse_remove(struct platform_device *pdev)
-{
-       struct nvmem_device *nvmem = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(nvmem);
+       return PTR_ERR_OR_ZERO(nvmem);
 }
 
 static const struct of_device_id uniphier_efuse_of_match[] = {
@@ -84,7 +73,6 @@ MODULE_DEVICE_TABLE(of, uniphier_efuse_of_match);
 
 static struct platform_driver uniphier_efuse_driver = {
        .probe = uniphier_efuse_probe,
-       .remove = uniphier_efuse_remove,
        .driver = {
                .name = "uniphier-efuse",
                .of_match_table = uniphier_efuse_of_match,
index 5ae9e002f1959972c52a257e49761546a2e039d3..4662309489dbbe72ce54a01fffc9f4a3dc5eafd9 100644 (file)
@@ -217,21 +217,13 @@ static const struct of_device_id ocotp_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, ocotp_of_match);
 
-static int vf610_ocotp_remove(struct platform_device *pdev)
-{
-       struct vf610_ocotp *ocotp_dev = platform_get_drvdata(pdev);
-
-       return nvmem_unregister(ocotp_dev->nvmem);
-}
-
 static int vf610_ocotp_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct resource *res;
        struct vf610_ocotp *ocotp_dev;
 
-       ocotp_dev = devm_kzalloc(&pdev->dev,
-                       sizeof(struct vf610_ocotp), GFP_KERNEL);
+       ocotp_dev = devm_kzalloc(dev, sizeof(struct vf610_ocotp), GFP_KERNEL);
        if (!ocotp_dev)
                return -ENOMEM;
 
@@ -246,26 +238,20 @@ static int vf610_ocotp_probe(struct platform_device *pdev)
                        PTR_ERR(ocotp_dev->clk));
                return PTR_ERR(ocotp_dev->clk);
        }
+       ocotp_dev->dev = dev;
+       ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
 
        ocotp_config.size = resource_size(res);
        ocotp_config.priv = ocotp_dev;
        ocotp_config.dev = dev;
 
-       ocotp_dev->nvmem = nvmem_register(&ocotp_config);
-       if (IS_ERR(ocotp_dev->nvmem))
-               return PTR_ERR(ocotp_dev->nvmem);
+       ocotp_dev->nvmem = devm_nvmem_register(dev, &ocotp_config);
 
-       ocotp_dev->dev = dev;
-       platform_set_drvdata(pdev, ocotp_dev);
-
-       ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(ocotp_dev->nvmem);
 }
 
 static struct platform_driver vf610_ocotp_driver = {
        .probe = vf610_ocotp_probe,
-       .remove = vf610_ocotp_remove,
        .driver = {
                .name = "vf610-ocotp",
                .of_match_table = ocotp_of_match,
index 2fc91edb058df94996df58ff2ad1ac4211e7586e..bfe97c2a8d4c21793f67b9a0b4a14fcc13941ac1 100644 (file)
@@ -273,18 +273,16 @@ static int parport_ax88796_probe(struct platform_device *pdev)
 {
        struct device *_dev = &pdev->dev;
        struct ax_drvdata *dd;
-       struct parport *pp = NULL;
+       struct parport *pp;
        struct resource *res;
        unsigned long size;
        int spacing;
        int irq;
        int ret;
 
-       dd = kzalloc(sizeof(struct ax_drvdata), GFP_KERNEL);
-       if (dd == NULL) {
-               dev_err(_dev, "no memory for private data\n");
+       dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+       if (!dd)
                return -ENOMEM;
-       }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
index 489492b608cf325ee5e1b662b2902f5465c5eecf..380916bff9e05ce4a1cd12fbdbb6a4b787759dec 100644 (file)
@@ -2646,6 +2646,7 @@ enum parport_pc_pci_cards {
        netmos_9901,
        netmos_9865,
        quatech_sppxp100,
+       wch_ch382l,
 };
 
 
@@ -2708,6 +2709,7 @@ static struct parport_pc_pci {
        /* netmos_9901 */               { 1, { { 0, -1 }, } },
        /* netmos_9865 */               { 1, { { 0, -1 }, } },
        /* quatech_sppxp100 */          { 1, { { 0, 1 }, } },
+       /* wch_ch382l */                { 1, { { 2, -1 }, } },
 };
 
 static const struct pci_device_id parport_pc_pci_tbl[] = {
@@ -2797,6 +2799,8 @@ static const struct pci_device_id parport_pc_pci_tbl[] = {
        /* Quatech SPPXP-100 Parallel port PCI ExpressCard */
        { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 },
+       /* WCH CH382L PCI-E single parallel port card */
+       { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l },
        { 0, } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl);
index 087e847b1da2291ffb6f3297e3ddae95ed54ea06..ae9e01ef7599a5e9a87ef3e30d724d7c18d6c2cc 100644 (file)
@@ -1,30 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Support for common PCI multi-I/O cards (which is most of them)
  *
  * Copyright (C) 2001  Tim Waugh <twaugh@redhat.com>
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- *
  * Multi-function PCI cards are supposed to present separate logical
  * devices on the bus.  A common thing to do seems to be to just use
  * one logical device with lots of base address registers for both
  * parallel ports and serial ports.  This driver is for dealing with
  * that.
- *
  */
 
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
 #include <linux/interrupt.h>
+#include <linux/module.h>
 #include <linux/parport.h>
 #include <linux/parport_pc.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
 #include <linux/8250_pci.h>
 
 enum parport_pc_pci_cards {
@@ -65,6 +59,7 @@ enum parport_pc_pci_cards {
        wch_ch353_1s1p,
        wch_ch353_2s1p,
        wch_ch382_2s1p,
+       brainboxes_5s1p,
        sunix_2s1p,
 };
 
@@ -153,6 +148,7 @@ static struct parport_pc_pci cards[] = {
        /* wch_ch353_1s1p*/             { 1, { { 1, -1}, } },
        /* wch_ch353_2s1p*/             { 1, { { 2, -1}, } },
        /* wch_ch382_2s1p*/             { 1, { { 2, -1}, } },
+       /* brainboxes_5s1p */           { 1, { { 3, -1 }, } },
        /* sunix_2s1p */                { 1, { { 3, -1 }, } },
 };
 
@@ -258,6 +254,10 @@ static struct pci_device_id parport_serial_pci_tbl[] = {
        { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
        { 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p},
 
+       /* BrainBoxes PX272/PX306 MIO card */
+       { PCI_VENDOR_ID_INTASHIELD, 0x4100,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p },
+
        /*
         * More SUNIX variations. At least one of these has part number
         * '5079A but subdevice 0x102. That board reports 0x0708 as
@@ -501,6 +501,12 @@ static struct pciserial_board pci_parport_serial_boards[] = {
                .uart_offset    = 8,
                .first_offset   = 0xC0,
        },
+       [brainboxes_5s1p] = {
+               .flags          = FL_BASE2,
+               .num_ports      = 5,
+               .base_baud      = 921600,
+               .uart_offset    = 8,
+       },
        [sunix_2s1p] = {
                .flags          = FL_BASE0|FL_BASE_BARS,
                .num_ports      = 2,
@@ -524,12 +530,10 @@ static int serial_register(struct pci_dev *dev, const struct pci_device_id *id)
        struct serial_private *serial;
 
        board = &pci_parport_serial_boards[id->driver_data];
-
        if (board->num_ports == 0)
                return 0;
 
        serial = pciserial_init_ports(dev, board);
-
        if (IS_ERR(serial))
                return PTR_ERR(serial);
 
@@ -558,10 +562,9 @@ static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
                int irq;
 
                if (priv->num_par == ARRAY_SIZE (priv->port)) {
-                       printk (KERN_WARNING
-                               "parport_serial: %s: only %zu parallel ports "
-                               "supported (%d reported)\n", pci_name (dev),
-                               ARRAY_SIZE(priv->port), card->numports);
+                       dev_warn(&dev->dev,
+                                "only %zu parallel ports supported (%d reported)\n",
+                                ARRAY_SIZE(priv->port), card->numports);
                        break;
                }
 
@@ -577,12 +580,12 @@ static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
                irq = dev->irq;
                if (irq == IRQ_NONE) {
                        dev_dbg(&dev->dev,
-                       "PCI parallel port detected: I/O at %#lx(%#lx)\n",
+                               "PCI parallel port detected: I/O at %#lx(%#lx)\n",
                                io_lo, io_hi);
                        irq = PARPORT_IRQ_NONE;
                } else {
                        dev_dbg(&dev->dev,
-               "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
+                               "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
                                io_lo, io_hi, irq);
                }
                port = parport_pc_probe_port (io_lo, io_hi, irq,
@@ -605,28 +608,26 @@ static int parport_serial_pci_probe(struct pci_dev *dev,
        struct parport_serial_private *priv;
        int err;
 
-       priv = kzalloc (sizeof *priv, GFP_KERNEL);
+       priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
+
        pci_set_drvdata (dev, priv);
 
-       err = pci_enable_device (dev);
-       if (err) {
-               kfree (priv);
+       err = pcim_enable_device(dev);
+       if (err)
                return err;
-       }
 
-       if (parport_register (dev, id)) {
-               kfree (priv);
-               return -ENODEV;
-       }
+       err = parport_register(dev, id);
+       if (err)
+               return err;
 
-       if (serial_register (dev, id)) {
+       err = serial_register(dev, id);
+       if (err) {
                int i;
                for (i = 0; i < priv->num_par; i++)
                        parport_pc_unregister_port (priv->port[i]);
-               kfree (priv);
-               return -ENODEV;
+               return err;
        }
 
        return 0;
@@ -645,78 +646,47 @@ static void parport_serial_pci_remove(struct pci_dev *dev)
        for (i = 0; i < priv->num_par; i++)
                parport_pc_unregister_port (priv->port[i]);
 
-       kfree (priv);
        return;
 }
 
-#ifdef CONFIG_PM
-static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
+static int __maybe_unused parport_serial_pci_suspend(struct device *dev)
 {
-       struct parport_serial_private *priv = pci_get_drvdata(dev);
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct parport_serial_private *priv = pci_get_drvdata(pdev);
 
        if (priv->serial)
                pciserial_suspend_ports(priv->serial);
 
        /* FIXME: What about parport? */
-
-       pci_save_state(dev);
-       pci_set_power_state(dev, pci_choose_state(dev, state));
        return 0;
 }
 
-static int parport_serial_pci_resume(struct pci_dev *dev)
+static int __maybe_unused parport_serial_pci_resume(struct device *dev)
 {
-       struct parport_serial_private *priv = pci_get_drvdata(dev);
-       int err;
-
-       pci_set_power_state(dev, PCI_D0);
-       pci_restore_state(dev);
-
-       /*
-        * The device may have been disabled.  Re-enable it.
-        */
-       err = pci_enable_device(dev);
-       if (err) {
-               printk(KERN_ERR "parport_serial: %s: error enabling "
-                       "device for resume (%d)\n", pci_name(dev), err);
-               return err;
-       }
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct parport_serial_private *priv = pci_get_drvdata(pdev);
 
        if (priv->serial)
                pciserial_resume_ports(priv->serial);
 
        /* FIXME: What about parport? */
-
        return 0;
 }
-#endif
+
+static SIMPLE_DEV_PM_OPS(parport_serial_pm_ops,
+                        parport_serial_pci_suspend, parport_serial_pci_resume);
 
 static struct pci_driver parport_serial_pci_driver = {
        .name           = "parport_serial",
        .id_table       = parport_serial_pci_tbl,
        .probe          = parport_serial_pci_probe,
        .remove         = parport_serial_pci_remove,
-#ifdef CONFIG_PM
-       .suspend        = parport_serial_pci_suspend,
-       .resume         = parport_serial_pci_resume,
-#endif
+       .driver         = {
+               .pm     = &parport_serial_pm_ops,
+       },
 };
-
-
-static int __init parport_serial_init (void)
-{
-       return pci_register_driver (&parport_serial_pci_driver);
-}
-
-static void __exit parport_serial_exit (void)
-{
-       pci_unregister_driver (&parport_serial_pci_driver);
-       return;
-}
+module_pci_driver(parport_serial_pci_driver);
 
 MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>");
 MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards");
 MODULE_LICENSE("GPL");
-
-module_init(parport_serial_init);
-module_exit(parport_serial_exit);
index 83797d89c30fe88665c5d7214c391a0a571cfe88..4db824f88d0095363d387cdfae7905784e4ffd28 100644 (file)
@@ -49,6 +49,7 @@ MODULE_PARM_DESC(clear_wait,
        " zero turns clear edge capture off entirely");
 module_param(clear_wait, uint, 0);
 
+static DEFINE_IDA(pps_client_index);
 
 /* internal per port structure */
 struct pps_client_pp {
@@ -56,6 +57,7 @@ struct pps_client_pp {
        struct pps_device *pps;         /* PPS device */
        unsigned int cw;                /* port clear timeout */
        unsigned int cw_err;            /* number of timeouts */
+       int index;                      /* device number */
 };
 
 static inline int signal_is_set(struct parport *port)
@@ -136,6 +138,8 @@ static void parport_irq(void *handle)
 
 static void parport_attach(struct parport *port)
 {
+       struct pardev_cb pps_client_cb;
+       int index;
        struct pps_client_pp *device;
        struct pps_source_info info = {
                .name           = KBUILD_MODNAME,
@@ -154,8 +158,15 @@ static void parport_attach(struct parport *port)
                return;
        }
 
-       device->pardev = parport_register_device(port, KBUILD_MODNAME,
-                       NULL, NULL, parport_irq, PARPORT_FLAG_EXCL, device);
+       index = ida_simple_get(&pps_client_index, 0, 0, GFP_KERNEL);
+       memset(&pps_client_cb, 0, sizeof(pps_client_cb));
+       pps_client_cb.private = device;
+       pps_client_cb.irq_func = parport_irq;
+       pps_client_cb.flags = PARPORT_FLAG_EXCL;
+       device->pardev = parport_register_dev_model(port,
+                                                   KBUILD_MODNAME,
+                                                   &pps_client_cb,
+                                                   index);
        if (!device->pardev) {
                pr_err("couldn't register with %s\n", port->name);
                goto err_free;
@@ -176,6 +187,7 @@ static void parport_attach(struct parport *port)
        device->cw = clear_wait;
 
        port->ops->enable_irq(port);
+       device->index = index;
 
        pr_info("attached to %s\n", port->name);
 
@@ -186,6 +198,7 @@ static void parport_attach(struct parport *port)
 err_unregister_dev:
        parport_unregister_device(device->pardev);
 err_free:
+       ida_simple_remove(&pps_client_index, index);
        kfree(device);
 }
 
@@ -205,13 +218,15 @@ static void parport_detach(struct parport *port)
        pps_unregister_source(device->pps);
        parport_release(pardev);
        parport_unregister_device(pardev);
+       ida_simple_remove(&pps_client_index, device->index);
        kfree(device);
 }
 
 static struct parport_driver pps_parport_driver = {
        .name = KBUILD_MODNAME,
-       .attach = parport_attach,
+       .match_port = parport_attach,
        .detach = parport_detach,
+       .devmodel = true,
 };
 
 /* module staff */
index 51cfde6afffd398b022719178f5f2b4960e4e70f..7fd36cac063b79936020b24494aacecf26056fb9 100644 (file)
@@ -192,13 +192,18 @@ static inline ktime_t next_intr_time(struct pps_generator_pp *dev)
 
 static void parport_attach(struct parport *port)
 {
+       struct pardev_cb pps_cb;
+
        if (attached) {
                /* we already have a port */
                return;
        }
 
-       device.pardev = parport_register_device(port, KBUILD_MODNAME,
-                       NULL, NULL, NULL, PARPORT_FLAG_EXCL, &device);
+       memset(&pps_cb, 0, sizeof(pps_cb));
+       pps_cb.private = &device;
+       pps_cb.flags = PARPORT_FLAG_EXCL;
+       device.pardev = parport_register_dev_model(port, KBUILD_MODNAME,
+                                                  &pps_cb, 0);
        if (!device.pardev) {
                pr_err("couldn't register with %s\n", port->name);
                return;
@@ -236,8 +241,9 @@ static void parport_detach(struct parport *port)
 
 static struct parport_driver pps_gen_parport_driver = {
        .name = KBUILD_MODNAME,
-       .attach = parport_attach,
+       .match_port = parport_attach,
        .detach = parport_detach,
+       .devmodel = true,
 };
 
 /* module staff */
index fdfcdea258678950d0c0cf502f7964a9087bf6cb..16590dfaafa448d43f00cbced0dbb457782fff93 100644 (file)
@@ -594,7 +594,7 @@ static ssize_t device_add_store(struct device *dev,
        size_t inbytes = 0, outbytes = 0;
        u8 statustype = 0;
 
-       ret = sscanf(buf, "%20s %zu %zu %hhu", type, &inbytes,
+       ret = sscanf(buf, "%19s %zu %zu %hhu", type, &inbytes,
                     &outbytes, &statustype);
        if (ret != 3 && ret != 4)
                return -EINVAL;
index 4988a8f4d90552a51501791ee3abe2737f428305..7ddfc675b131fba29bb9419541d58169cba5aa22 100644 (file)
@@ -141,7 +141,7 @@ static struct slim_device *slim_alloc_device(struct slim_controller *ctrl,
        sbdev->e_addr = *eaddr;
        ret = slim_add_device(ctrl, sbdev, node);
        if (ret) {
-               kfree(sbdev);
+               put_device(&sbdev->dev);
                return NULL;
        }
 
index af6dde347bee9e030402c683da64ae87f6c06c03..f2701194f81000da67926347064eabf12eca8be7 100644 (file)
@@ -170,24 +170,22 @@ static int dma_port_write(struct tb_ctl *ctl, const void *buffer, u64 route,
 
 static int dma_find_port(struct tb_switch *sw)
 {
-       int port, ret;
-       u32 type;
+       static const int ports[] = { 3, 5, 7 };
+       int i;
 
        /*
-        * The DMA (NHI) port is either 3 or 5 depending on the
-        * controller. Try both starting from 5 which is more common.
+        * The DMA (NHI) port is either 3, 5 or 7 depending on the
+        * controller. Try all of them.
         */
-       port = 5;
-       ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), port, 2, 1,
-                           DMA_PORT_TIMEOUT);
-       if (!ret && (type & 0xffffff) == TB_TYPE_NHI)
-               return port;
-
-       port = 3;
-       ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), port, 2, 1,
-                           DMA_PORT_TIMEOUT);
-       if (!ret && (type & 0xffffff) == TB_TYPE_NHI)
-               return port;
+       for (i = 0; i < ARRAY_SIZE(ports); i++) {
+               u32 type;
+               int ret;
+
+               ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), ports[i],
+                                   2, 1, DMA_PORT_TIMEOUT);
+               if (!ret && (type & 0xffffff) == TB_TYPE_NHI)
+                       return ports[i];
+       }
 
        return -ENODEV;
 }
index 9b90115319ce8ef5065bd3e418b76a34fab1051e..6281266b8ec0a15721da5196b641b707b8a5c973 100644 (file)
@@ -117,23 +117,151 @@ static const char * const tb_security_names[] = {
        [TB_SECURITY_USER] = "user",
        [TB_SECURITY_SECURE] = "secure",
        [TB_SECURITY_DPONLY] = "dponly",
+       [TB_SECURITY_USBONLY] = "usbonly",
 };
 
+static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct tb *tb = container_of(dev, struct tb, dev);
+       uuid_t *uuids;
+       ssize_t ret;
+       int i;
+
+       uuids = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL);
+       if (!uuids)
+               return -ENOMEM;
+
+       if (mutex_lock_interruptible(&tb->lock)) {
+               ret = -ERESTARTSYS;
+               goto out;
+       }
+       ret = tb->cm_ops->get_boot_acl(tb, uuids, tb->nboot_acl);
+       if (ret) {
+               mutex_unlock(&tb->lock);
+               goto out;
+       }
+       mutex_unlock(&tb->lock);
+
+       for (ret = 0, i = 0; i < tb->nboot_acl; i++) {
+               if (!uuid_is_null(&uuids[i]))
+                       ret += snprintf(buf + ret, PAGE_SIZE - ret, "%pUb",
+                                       &uuids[i]);
+
+               ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s",
+                              i < tb->nboot_acl - 1 ? "," : "\n");
+       }
+
+out:
+       kfree(uuids);
+       return ret;
+}
+
+static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct tb *tb = container_of(dev, struct tb, dev);
+       char *str, *s, *uuid_str;
+       ssize_t ret = 0;
+       uuid_t *acl;
+       int i = 0;
+
+       /*
+        * Make sure the value is not bigger than tb->nboot_acl * UUID
+        * length + commas and optional "\n". Also the smallest allowable
+        * string is tb->nboot_acl * ",".
+        */
+       if (count > (UUID_STRING_LEN + 1) * tb->nboot_acl + 1)
+               return -EINVAL;
+       if (count < tb->nboot_acl - 1)
+               return -EINVAL;
+
+       str = kstrdup(buf, GFP_KERNEL);
+       if (!str)
+               return -ENOMEM;
+
+       acl = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL);
+       if (!acl) {
+               ret = -ENOMEM;
+               goto err_free_str;
+       }
+
+       uuid_str = strim(str);
+       while ((s = strsep(&uuid_str, ",")) != NULL && i < tb->nboot_acl) {
+               size_t len = strlen(s);
+
+               if (len) {
+                       if (len != UUID_STRING_LEN) {
+                               ret = -EINVAL;
+                               goto err_free_acl;
+                       }
+                       ret = uuid_parse(s, &acl[i]);
+                       if (ret)
+                               goto err_free_acl;
+               }
+
+               i++;
+       }
+
+       if (s || i < tb->nboot_acl) {
+               ret = -EINVAL;
+               goto err_free_acl;
+       }
+
+       if (mutex_lock_interruptible(&tb->lock)) {
+               ret = -ERESTARTSYS;
+               goto err_free_acl;
+       }
+       ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl);
+       mutex_unlock(&tb->lock);
+
+err_free_acl:
+       kfree(acl);
+err_free_str:
+       kfree(str);
+
+       return ret ?: count;
+}
+static DEVICE_ATTR_RW(boot_acl);
+
 static ssize_t security_show(struct device *dev, struct device_attribute *attr,
                             char *buf)
 {
        struct tb *tb = container_of(dev, struct tb, dev);
+       const char *name = "unknown";
 
-       return sprintf(buf, "%s\n", tb_security_names[tb->security_level]);
+       if (tb->security_level < ARRAY_SIZE(tb_security_names))
+               name = tb_security_names[tb->security_level];
+
+       return sprintf(buf, "%s\n", name);
 }
 static DEVICE_ATTR_RO(security);
 
 static struct attribute *domain_attrs[] = {
+       &dev_attr_boot_acl.attr,
        &dev_attr_security.attr,
        NULL,
 };
 
+static umode_t domain_attr_is_visible(struct kobject *kobj,
+                                     struct attribute *attr, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct tb *tb = container_of(dev, struct tb, dev);
+
+       if (attr == &dev_attr_boot_acl.attr) {
+               if (tb->nboot_acl &&
+                   tb->cm_ops->get_boot_acl &&
+                   tb->cm_ops->set_boot_acl)
+                       return attr->mode;
+               return 0;
+       }
+
+       return attr->mode;
+}
+
 static struct attribute_group domain_attr_group = {
+       .is_visible = domain_attr_is_visible,
        .attrs = domain_attrs,
 };
 
index ab02d13f40b705fd67955ec0c26517f904319282..2d2ceda9aa2691ed3b7d38752a74bcaf06955547 100644 (file)
@@ -41,7 +41,8 @@
 #define PHY_PORT_CS1_LINK_STATE_MASK   GENMASK(29, 26)
 #define PHY_PORT_CS1_LINK_STATE_SHIFT  26
 
-#define ICM_TIMEOUT                    5000 /* ms */
+#define ICM_TIMEOUT                    5000    /* ms */
+#define ICM_APPROVE_TIMEOUT            10000   /* ms */
 #define ICM_MAX_LINK                   4
 #define ICM_MAX_DEPTH                  6
 
  * @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides
  *          (only set when @upstream_port is not %NULL)
  * @safe_mode: ICM is in safe mode
+ * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported)
  * @is_supported: Checks if we can support ICM on this controller
  * @get_mode: Read and return the ICM firmware mode (optional)
  * @get_route: Find a route string for given switch
+ * @driver_ready: Send driver ready message to ICM
  * @device_connected: Handle device connected ICM message
  * @device_disconnected: Handle device disconnected ICM message
  * @xdomain_connected - Handle XDomain connected ICM message
@@ -67,11 +70,15 @@ struct icm {
        struct mutex request_lock;
        struct delayed_work rescan_work;
        struct pci_dev *upstream_port;
+       size_t max_boot_acl;
        int vnd_cap;
        bool safe_mode;
        bool (*is_supported)(struct tb *tb);
        int (*get_mode)(struct tb *tb);
        int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route);
+       int (*driver_ready)(struct tb *tb,
+                           enum tb_security_level *security_level,
+                           size_t *nboot_acl);
        void (*device_connected)(struct tb *tb,
                                 const struct icm_pkg_header *hdr);
        void (*device_disconnected)(struct tb *tb,
@@ -111,6 +118,12 @@ static inline u64 get_route(u32 route_hi, u32 route_lo)
        return (u64)route_hi << 32 | route_lo;
 }
 
+static inline u64 get_parent_route(u64 route)
+{
+       int depth = tb_route_length(route);
+       return depth ? route & ~(0xffULL << (depth - 1) * TB_ROUTE_SHIFT) : 0;
+}
+
 static bool icm_match(const struct tb_cfg_request *req,
                      const struct ctl_pkg *pkg)
 {
@@ -245,6 +258,28 @@ static int icm_fr_get_route(struct tb *tb, u8 link, u8 depth, u64 *route)
        return ret;
 }
 
+static int
+icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+                   size_t *nboot_acl)
+{
+       struct icm_fr_pkg_driver_ready_response reply;
+       struct icm_pkg_driver_ready request = {
+               .hdr.code = ICM_DRIVER_READY,
+       };
+       int ret;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (security_level)
+               *security_level = reply.security_level & ICM_FR_SLEVEL_MASK;
+
+       return 0;
+}
+
 static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw)
 {
        struct icm_fr_pkg_approve_device request;
@@ -260,7 +295,7 @@ static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw)
        memset(&reply, 0, sizeof(reply));
        /* Use larger timeout as establishing tunnels can take some time */
        ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
-                         1, 10000);
+                         1, ICM_APPROVE_TIMEOUT);
        if (ret)
                return ret;
 
@@ -374,6 +409,59 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
        return 0;
 }
 
+static void add_switch(struct tb_switch *parent_sw, u64 route,
+                      const uuid_t *uuid, u8 connection_id, u8 connection_key,
+                      u8 link, u8 depth, enum tb_security_level security_level,
+                      bool authorized, bool boot)
+{
+       struct tb_switch *sw;
+
+       sw = tb_switch_alloc(parent_sw->tb, &parent_sw->dev, route);
+       if (!sw)
+               return;
+
+       sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL);
+       sw->connection_id = connection_id;
+       sw->connection_key = connection_key;
+       sw->link = link;
+       sw->depth = depth;
+       sw->authorized = authorized;
+       sw->security_level = security_level;
+       sw->boot = boot;
+
+       /* Link the two switches now */
+       tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
+       tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw);
+
+       if (tb_switch_add(sw)) {
+               tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
+               tb_switch_put(sw);
+               return;
+       }
+}
+
+static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
+                         u64 route, u8 connection_id, u8 connection_key,
+                         u8 link, u8 depth, bool boot)
+{
+       /* Disconnect from parent */
+       tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
+       /* Re-connect via updated port*/
+       tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
+
+       /* Update with the new addressing information */
+       sw->config.route_hi = upper_32_bits(route);
+       sw->config.route_lo = lower_32_bits(route);
+       sw->connection_id = connection_id;
+       sw->connection_key = connection_key;
+       sw->link = link;
+       sw->depth = depth;
+       sw->boot = boot;
+
+       /* This switch still exists */
+       sw->is_unplugged = false;
+}
+
 static void remove_switch(struct tb_switch *sw)
 {
        struct tb_switch *parent_sw;
@@ -383,15 +471,52 @@ static void remove_switch(struct tb_switch *sw)
        tb_switch_remove(sw);
 }
 
+static void add_xdomain(struct tb_switch *sw, u64 route,
+                       const uuid_t *local_uuid, const uuid_t *remote_uuid,
+                       u8 link, u8 depth)
+{
+       struct tb_xdomain *xd;
+
+       xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid);
+       if (!xd)
+               return;
+
+       xd->link = link;
+       xd->depth = depth;
+
+       tb_port_at(route, sw)->xdomain = xd;
+
+       tb_xdomain_add(xd);
+}
+
+static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link)
+{
+       xd->link = link;
+       xd->route = route;
+       xd->is_unplugged = false;
+}
+
+static void remove_xdomain(struct tb_xdomain *xd)
+{
+       struct tb_switch *sw;
+
+       sw = tb_to_switch(xd->dev.parent);
+       tb_port_at(xd->route, sw)->xdomain = NULL;
+       tb_xdomain_remove(xd);
+}
+
 static void
 icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
 {
        const struct icm_fr_event_device_connected *pkg =
                (const struct icm_fr_event_device_connected *)hdr;
+       enum tb_security_level security_level;
        struct tb_switch *sw, *parent_sw;
        struct icm *icm = tb_priv(tb);
        bool authorized = false;
+       struct tb_xdomain *xd;
        u8 link, depth;
+       bool boot;
        u64 route;
        int ret;
 
@@ -399,6 +524,15 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
        depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >>
                ICM_LINK_INFO_DEPTH_SHIFT;
        authorized = pkg->link_info & ICM_LINK_INFO_APPROVED;
+       security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
+                        ICM_FLAGS_SLEVEL_SHIFT;
+       boot = pkg->link_info & ICM_LINK_INFO_BOOT;
+
+       if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
+               tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n",
+                       link, depth);
+               return;
+       }
 
        ret = icm->get_route(tb, link, depth, &route);
        if (ret) {
@@ -425,16 +559,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
                 */
                if (sw->depth == depth && sw_phy_port == phy_port &&
                    !!sw->authorized == authorized) {
-                       tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
-                       tb_port_at(route, parent_sw)->remote =
-                                  tb_upstream_port(sw);
-                       sw->config.route_hi = upper_32_bits(route);
-                       sw->config.route_lo = lower_32_bits(route);
-                       sw->connection_id = pkg->connection_id;
-                       sw->connection_key = pkg->connection_key;
-                       sw->link = link;
-                       sw->depth = depth;
-                       sw->is_unplugged = false;
+                       update_switch(parent_sw, sw, route, pkg->connection_id,
+                                     pkg->connection_key, link, depth, boot);
                        tb_switch_put(sw);
                        return;
                }
@@ -467,6 +593,13 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
                tb_switch_put(sw);
        }
 
+       /* Remove existing XDomain connection if found */
+       xd = tb_xdomain_find_by_link_depth(tb, link, depth);
+       if (xd) {
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+
        parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1);
        if (!parent_sw) {
                tb_err(tb, "failed to find parent switch for %u.%u\n",
@@ -474,30 +607,10 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
                return;
        }
 
-       sw = tb_switch_alloc(tb, &parent_sw->dev, route);
-       if (!sw) {
-               tb_switch_put(parent_sw);
-               return;
-       }
-
-       sw->uuid = kmemdup(&pkg->ep_uuid, sizeof(pkg->ep_uuid), GFP_KERNEL);
-       sw->connection_id = pkg->connection_id;
-       sw->connection_key = pkg->connection_key;
-       sw->link = link;
-       sw->depth = depth;
-       sw->authorized = authorized;
-       sw->security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
-                               ICM_FLAGS_SLEVEL_SHIFT;
-
-       /* Link the two switches now */
-       tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
-       tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw);
+       add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id,
+                  pkg->connection_key, link, depth, security_level,
+                  authorized, boot);
 
-       ret = tb_switch_add(sw);
-       if (ret) {
-               tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
-               tb_switch_put(sw);
-       }
        tb_switch_put(parent_sw);
 }
 
@@ -529,15 +642,6 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
        tb_switch_put(sw);
 }
 
-static void remove_xdomain(struct tb_xdomain *xd)
-{
-       struct tb_switch *sw;
-
-       sw = tb_to_switch(xd->dev.parent);
-       tb_port_at(xd->route, sw)->xdomain = NULL;
-       tb_xdomain_remove(xd);
-}
-
 static void
 icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
 {
@@ -577,9 +681,7 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
                phy_port = phy_port_from_route(route, depth);
 
                if (xd->depth == depth && xd_phy_port == phy_port) {
-                       xd->link = link;
-                       xd->route = route;
-                       xd->is_unplugged = false;
+                       update_xdomain(xd, route, link);
                        tb_xdomain_put(xd);
                        return;
                }
@@ -629,19 +731,8 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
                return;
        }
 
-       xd = tb_xdomain_alloc(sw->tb, &sw->dev, route,
-                             &pkg->local_uuid, &pkg->remote_uuid);
-       if (!xd) {
-               tb_switch_put(sw);
-               return;
-       }
-
-       xd->link = link;
-       xd->depth = depth;
-
-       tb_port_at(route, sw)->xdomain = xd;
-
-       tb_xdomain_add(xd);
+       add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, link,
+                   depth);
        tb_switch_put(sw);
 }
 
@@ -664,6 +755,351 @@ icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
        }
 }
 
+static int
+icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+                   size_t *nboot_acl)
+{
+       struct icm_tr_pkg_driver_ready_response reply;
+       struct icm_pkg_driver_ready request = {
+               .hdr.code = ICM_DRIVER_READY,
+       };
+       int ret;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, 20000);
+       if (ret)
+               return ret;
+
+       if (security_level)
+               *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK;
+       if (nboot_acl)
+               *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >>
+                               ICM_TR_INFO_BOOT_ACL_SHIFT;
+       return 0;
+}
+
+static int icm_tr_approve_switch(struct tb *tb, struct tb_switch *sw)
+{
+       struct icm_tr_pkg_approve_device request;
+       struct icm_tr_pkg_approve_device reply;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+       request.hdr.code = ICM_APPROVE_DEVICE;
+       request.route_lo = sw->config.route_lo;
+       request.route_hi = sw->config.route_hi;
+       request.connection_id = sw->connection_id;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_APPROVE_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR) {
+               tb_warn(tb, "PCIe tunnel creation failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int icm_tr_add_switch_key(struct tb *tb, struct tb_switch *sw)
+{
+       struct icm_tr_pkg_add_device_key_response reply;
+       struct icm_tr_pkg_add_device_key request;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+       request.hdr.code = ICM_ADD_DEVICE_KEY;
+       request.route_lo = sw->config.route_lo;
+       request.route_hi = sw->config.route_hi;
+       request.connection_id = sw->connection_id;
+       memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE);
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR) {
+               tb_warn(tb, "Adding key to switch failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw,
+                                      const u8 *challenge, u8 *response)
+{
+       struct icm_tr_pkg_challenge_device_response reply;
+       struct icm_tr_pkg_challenge_device request;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+       request.hdr.code = ICM_CHALLENGE_DEVICE;
+       request.route_lo = sw->config.route_lo;
+       request.route_hi = sw->config.route_hi;
+       request.connection_id = sw->connection_id;
+       memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE);
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EKEYREJECTED;
+       if (reply.hdr.flags & ICM_FLAGS_NO_KEY)
+               return -ENOKEY;
+
+       memcpy(response, reply.response, TB_SWITCH_KEY_SIZE);
+
+       return 0;
+}
+
+static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
+{
+       struct icm_tr_pkg_approve_xdomain_response reply;
+       struct icm_tr_pkg_approve_xdomain request;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       request.hdr.code = ICM_APPROVE_XDOMAIN;
+       request.route_hi = upper_32_bits(xd->route);
+       request.route_lo = lower_32_bits(xd->route);
+       request.transmit_path = xd->transmit_path;
+       request.transmit_ring = xd->transmit_ring;
+       request.receive_path = xd->receive_path;
+       request.receive_ring = xd->receive_ring;
+       memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid));
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EIO;
+
+       return 0;
+}
+
+static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd,
+                                   int stage)
+{
+       struct icm_tr_pkg_disconnect_xdomain_response reply;
+       struct icm_tr_pkg_disconnect_xdomain request;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       request.hdr.code = ICM_DISCONNECT_XDOMAIN;
+       request.stage = stage;
+       request.route_hi = upper_32_bits(xd->route);
+       request.route_lo = lower_32_bits(xd->route);
+       memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid));
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EIO;
+
+       return 0;
+}
+
+static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
+{
+       int ret;
+
+       ret = icm_tr_xdomain_tear_down(tb, xd, 1);
+       if (ret)
+               return ret;
+
+       usleep_range(10, 50);
+       return icm_tr_xdomain_tear_down(tb, xd, 2);
+}
+
+static void
+icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+       const struct icm_tr_event_device_connected *pkg =
+               (const struct icm_tr_event_device_connected *)hdr;
+       enum tb_security_level security_level;
+       struct tb_switch *sw, *parent_sw;
+       struct tb_xdomain *xd;
+       bool authorized, boot;
+       u64 route;
+
+       /*
+        * Currently we don't use the QoS information coming with the
+        * device connected message so simply just ignore that extra
+        * packet for now.
+        */
+       if (pkg->hdr.packet_id)
+               return;
+
+       /*
+        * After NVM upgrade adding root switch device fails because we
+        * initiated reset. During that time ICM might still send device
+        * connected message which we ignore here.
+        */
+       if (!tb->root_switch)
+               return;
+
+       route = get_route(pkg->route_hi, pkg->route_lo);
+       authorized = pkg->link_info & ICM_LINK_INFO_APPROVED;
+       security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
+                        ICM_FLAGS_SLEVEL_SHIFT;
+       boot = pkg->link_info & ICM_LINK_INFO_BOOT;
+
+       if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
+               tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n",
+                       route);
+               return;
+       }
+
+       sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid);
+       if (sw) {
+               /* Update the switch if it is still in the same place */
+               if (tb_route(sw) == route && !!sw->authorized == authorized) {
+                       parent_sw = tb_to_switch(sw->dev.parent);
+                       update_switch(parent_sw, sw, route, pkg->connection_id,
+                                     0, 0, 0, boot);
+                       tb_switch_put(sw);
+                       return;
+               }
+
+               remove_switch(sw);
+               tb_switch_put(sw);
+       }
+
+       /* Another switch with the same address */
+       sw = tb_switch_find_by_route(tb, route);
+       if (sw) {
+               remove_switch(sw);
+               tb_switch_put(sw);
+       }
+
+       /* XDomain connection with the same address */
+       xd = tb_xdomain_find_by_route(tb, route);
+       if (xd) {
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+
+       parent_sw = tb_switch_find_by_route(tb, get_parent_route(route));
+       if (!parent_sw) {
+               tb_err(tb, "failed to find parent switch for %llx\n", route);
+               return;
+       }
+
+       add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id,
+                  0, 0, 0, security_level, authorized, boot);
+
+       tb_switch_put(parent_sw);
+}
+
+static void
+icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+       const struct icm_tr_event_device_disconnected *pkg =
+               (const struct icm_tr_event_device_disconnected *)hdr;
+       struct tb_switch *sw;
+       u64 route;
+
+       route = get_route(pkg->route_hi, pkg->route_lo);
+
+       sw = tb_switch_find_by_route(tb, route);
+       if (!sw) {
+               tb_warn(tb, "no switch exists at %llx, ignoring\n", route);
+               return;
+       }
+
+       remove_switch(sw);
+       tb_switch_put(sw);
+}
+
+static void
+icm_tr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+       const struct icm_tr_event_xdomain_connected *pkg =
+               (const struct icm_tr_event_xdomain_connected *)hdr;
+       struct tb_xdomain *xd;
+       struct tb_switch *sw;
+       u64 route;
+
+       if (!tb->root_switch)
+               return;
+
+       route = get_route(pkg->local_route_hi, pkg->local_route_lo);
+
+       xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid);
+       if (xd) {
+               if (xd->route == route) {
+                       update_xdomain(xd, route, 0);
+                       tb_xdomain_put(xd);
+                       return;
+               }
+
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+
+       /* An existing xdomain with the same address */
+       xd = tb_xdomain_find_by_route(tb, route);
+       if (xd) {
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+
+       /*
+        * If the user disconnected a switch during suspend and
+        * connected another host to the same port, remove the switch
+        * first.
+        */
+       sw = get_switch_at_route(tb->root_switch, route);
+       if (sw)
+               remove_switch(sw);
+
+       sw = tb_switch_find_by_route(tb, get_parent_route(route));
+       if (!sw) {
+               tb_warn(tb, "no switch exists at %llx, ignoring\n", route);
+               return;
+       }
+
+       add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, 0, 0);
+       tb_switch_put(sw);
+}
+
+static void
+icm_tr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+       const struct icm_tr_event_xdomain_disconnected *pkg =
+               (const struct icm_tr_event_xdomain_disconnected *)hdr;
+       struct tb_xdomain *xd;
+       u64 route;
+
+       route = get_route(pkg->route_hi, pkg->route_lo);
+
+       xd = tb_xdomain_find_by_route(tb, route);
+       if (xd) {
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+}
+
 static struct pci_dev *get_upstream_port(struct pci_dev *pdev)
 {
        struct pci_dev *parent;
@@ -728,14 +1164,14 @@ static bool icm_ar_is_supported(struct tb *tb)
 static int icm_ar_get_mode(struct tb *tb)
 {
        struct tb_nhi *nhi = tb->nhi;
-       int retries = 5;
+       int retries = 60;
        u32 val;
 
        do {
                val = ioread32(nhi->iobase + REG_FW_STS);
                if (val & REG_FW_STS_NVM_AUTH_DONE)
                        break;
-               msleep(30);
+               msleep(50);
        } while (--retries);
 
        if (!retries) {
@@ -746,6 +1182,30 @@ static int icm_ar_get_mode(struct tb *tb)
        return nhi_mailbox_mode(nhi);
 }
 
+static int
+icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+                   size_t *nboot_acl)
+{
+       struct icm_ar_pkg_driver_ready_response reply;
+       struct icm_pkg_driver_ready request = {
+               .hdr.code = ICM_DRIVER_READY,
+       };
+       int ret;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (security_level)
+               *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK;
+       if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED))
+               *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >>
+                               ICM_AR_INFO_BOOT_ACL_SHIFT;
+       return 0;
+}
+
 static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route)
 {
        struct icm_ar_pkg_get_route_response reply;
@@ -768,6 +1228,87 @@ static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route)
        return 0;
 }
 
+static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids)
+{
+       struct icm_ar_pkg_preboot_acl_response reply;
+       struct icm_ar_pkg_preboot_acl request = {
+               .hdr = { .code = ICM_PREBOOT_ACL },
+       };
+       int ret, i;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EIO;
+
+       for (i = 0; i < nuuids; i++) {
+               u32 *uuid = (u32 *)&uuids[i];
+
+               uuid[0] = reply.acl[i].uuid_lo;
+               uuid[1] = reply.acl[i].uuid_hi;
+
+               if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) {
+                       /* Map empty entries to null UUID */
+                       uuid[0] = 0;
+                       uuid[1] = 0;
+               } else {
+                       /* Upper two DWs are always one's */
+                       uuid[2] = 0xffffffff;
+                       uuid[3] = 0xffffffff;
+               }
+       }
+
+       return ret;
+}
+
+static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids,
+                              size_t nuuids)
+{
+       struct icm_ar_pkg_preboot_acl_response reply;
+       struct icm_ar_pkg_preboot_acl request = {
+               .hdr = {
+                       .code = ICM_PREBOOT_ACL,
+                       .flags = ICM_FLAGS_WRITE,
+               },
+       };
+       int ret, i;
+
+       for (i = 0; i < nuuids; i++) {
+               const u32 *uuid = (const u32 *)&uuids[i];
+
+               if (uuid_is_null(&uuids[i])) {
+                       /*
+                        * Map null UUID to the empty (all one) entries
+                        * for ICM.
+                        */
+                       request.acl[i].uuid_lo = 0xffffffff;
+                       request.acl[i].uuid_hi = 0xffffffff;
+               } else {
+                       /* Two high DWs need to be set to all one */
+                       if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff)
+                               return -EINVAL;
+
+                       request.acl[i].uuid_lo = uuid[0];
+                       request.acl[i].uuid_hi = uuid[1];
+               }
+       }
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EIO;
+
+       return 0;
+}
+
 static void icm_handle_notification(struct work_struct *work)
 {
        struct icm_notification *n = container_of(work, typeof(*n), work);
@@ -814,23 +1355,18 @@ static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
 }
 
 static int
-__icm_driver_ready(struct tb *tb, enum tb_security_level *security_level)
+__icm_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+                  size_t *nboot_acl)
 {
-       struct icm_pkg_driver_ready_response reply;
-       struct icm_pkg_driver_ready request = {
-               .hdr.code = ICM_DRIVER_READY,
-       };
-       unsigned int retries = 10;
+       struct icm *icm = tb_priv(tb);
+       unsigned int retries = 50;
        int ret;
 
-       memset(&reply, 0, sizeof(reply));
-       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
-                         1, ICM_TIMEOUT);
-       if (ret)
+       ret = icm->driver_ready(tb, security_level, nboot_acl);
+       if (ret) {
+               tb_err(tb, "failed to send driver ready to ICM\n");
                return ret;
-
-       if (security_level)
-               *security_level = reply.security_level & 0xf;
+       }
 
        /*
         * Hold on here until the switch config space is accessible so
@@ -848,6 +1384,7 @@ __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level)
                msleep(50);
        } while (--retries);
 
+       tb_err(tb, "failed to read root switch config space, giving up\n");
        return -ETIMEDOUT;
 }
 
@@ -915,6 +1452,9 @@ static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi)
        struct icm *icm = tb_priv(tb);
        u32 val;
 
+       if (!icm->upstream_port)
+               return -ENODEV;
+
        /* Put ARC to wait for CIO reset event to happen */
        val = ioread32(nhi->iobase + REG_FW_STS);
        val |= REG_FW_STS_CIO_RESET_REQ;
@@ -1054,6 +1594,9 @@ static int icm_firmware_init(struct tb *tb)
                        break;
 
                default:
+                       if (ret < 0)
+                               return ret;
+
                        tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret);
                        return -ENODEV;
                }
@@ -1089,7 +1632,18 @@ static int icm_driver_ready(struct tb *tb)
                return 0;
        }
 
-       return __icm_driver_ready(tb, &tb->security_level);
+       ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl);
+       if (ret)
+               return ret;
+
+       /*
+        * Make sure the number of supported preboot ACL matches what we
+        * expect or disable the whole feature.
+        */
+       if (tb->nboot_acl > icm->max_boot_acl)
+               tb->nboot_acl = 0;
+
+       return 0;
 }
 
 static int icm_suspend(struct tb *tb)
@@ -1185,7 +1739,7 @@ static void icm_complete(struct tb *tb)
         * Now all existing children should be resumed, start events
         * from ICM to get updated status.
         */
-       __icm_driver_ready(tb, NULL);
+       __icm_driver_ready(tb, NULL, NULL);
 
        /*
         * We do not get notifications of devices that have been
@@ -1238,7 +1792,7 @@ static int icm_disconnect_pcie_paths(struct tb *tb)
        return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0);
 }
 
-/* Falcon Ridge and Alpine Ridge */
+/* Falcon Ridge */
 static const struct tb_cm_ops icm_fr_ops = {
        .driver_ready = icm_driver_ready,
        .start = icm_start,
@@ -1254,6 +1808,42 @@ static const struct tb_cm_ops icm_fr_ops = {
        .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths,
 };
 
+/* Alpine Ridge */
+static const struct tb_cm_ops icm_ar_ops = {
+       .driver_ready = icm_driver_ready,
+       .start = icm_start,
+       .stop = icm_stop,
+       .suspend = icm_suspend,
+       .complete = icm_complete,
+       .handle_event = icm_handle_event,
+       .get_boot_acl = icm_ar_get_boot_acl,
+       .set_boot_acl = icm_ar_set_boot_acl,
+       .approve_switch = icm_fr_approve_switch,
+       .add_switch_key = icm_fr_add_switch_key,
+       .challenge_switch_key = icm_fr_challenge_switch_key,
+       .disconnect_pcie_paths = icm_disconnect_pcie_paths,
+       .approve_xdomain_paths = icm_fr_approve_xdomain_paths,
+       .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths,
+};
+
+/* Titan Ridge */
+static const struct tb_cm_ops icm_tr_ops = {
+       .driver_ready = icm_driver_ready,
+       .start = icm_start,
+       .stop = icm_stop,
+       .suspend = icm_suspend,
+       .complete = icm_complete,
+       .handle_event = icm_handle_event,
+       .get_boot_acl = icm_ar_get_boot_acl,
+       .set_boot_acl = icm_ar_set_boot_acl,
+       .approve_switch = icm_tr_approve_switch,
+       .add_switch_key = icm_tr_add_switch_key,
+       .challenge_switch_key = icm_tr_challenge_switch_key,
+       .disconnect_pcie_paths = icm_disconnect_pcie_paths,
+       .approve_xdomain_paths = icm_tr_approve_xdomain_paths,
+       .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths,
+};
+
 struct tb *icm_probe(struct tb_nhi *nhi)
 {
        struct icm *icm;
@@ -1272,6 +1862,7 @@ struct tb *icm_probe(struct tb_nhi *nhi)
        case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
                icm->is_supported = icm_fr_is_supported;
                icm->get_route = icm_fr_get_route;
+               icm->driver_ready = icm_fr_driver_ready;
                icm->device_connected = icm_fr_device_connected;
                icm->device_disconnected = icm_fr_device_disconnected;
                icm->xdomain_connected = icm_fr_xdomain_connected;
@@ -1284,14 +1875,29 @@ struct tb *icm_probe(struct tb_nhi *nhi)
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI:
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI:
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI:
+               icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
                icm->is_supported = icm_ar_is_supported;
                icm->get_mode = icm_ar_get_mode;
                icm->get_route = icm_ar_get_route;
+               icm->driver_ready = icm_ar_driver_ready;
                icm->device_connected = icm_fr_device_connected;
                icm->device_disconnected = icm_fr_device_disconnected;
                icm->xdomain_connected = icm_fr_xdomain_connected;
                icm->xdomain_disconnected = icm_fr_xdomain_disconnected;
-               tb->cm_ops = &icm_fr_ops;
+               tb->cm_ops = &icm_ar_ops;
+               break;
+
+       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI:
+       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI:
+               icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
+               icm->is_supported = icm_ar_is_supported;
+               icm->get_mode = icm_ar_get_mode;
+               icm->driver_ready = icm_tr_driver_ready;
+               icm->device_connected = icm_tr_device_connected;
+               icm->device_disconnected = icm_tr_device_disconnected;
+               icm->xdomain_connected = icm_tr_xdomain_connected;
+               icm->xdomain_disconnected = icm_tr_xdomain_disconnected;
+               tb->cm_ops = &icm_tr_ops;
                break;
        }
 
index f45bcbc63738ffb3598e958e1af529c443b98e1d..f5a33e88e676160c4d4c1ca091ce3680a470207a 100644 (file)
@@ -1036,7 +1036,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                 */
                tb_domain_put(tb);
                nhi_shutdown(nhi);
-               return -EIO;
+               return res;
        }
        pci_set_drvdata(pdev, tb);
 
@@ -1064,6 +1064,7 @@ static const struct dev_pm_ops nhi_pm_ops = {
                                            * we just disable hotplug, the
                                            * pci-tunnels stay alive.
                                            */
+       .thaw_noirq = nhi_resume_noirq,
        .restore_noirq = nhi_resume_noirq,
        .suspend = nhi_suspend,
        .freeze = nhi_suspend,
@@ -1110,6 +1111,8 @@ static struct pci_device_id nhi_ids[] = {
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI) },
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI) },
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI) },
 
        { 0,}
 };
index 4476ab4cfd0c4acb90453e60bb4525eb77fc07e0..1696a45609484c1045ac747a24380781ae452c85 100644 (file)
@@ -45,5 +45,10 @@ enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi);
 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_USBONLY_NHI        0x15dc
 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_USBONLY_NHI   0x15dd
 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI 0x15de
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE      0x15e7
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI         0x15e8
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE      0x15ea
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI         0x15eb
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE      0x15ef
 
 #endif
index da54ace4dd2f2fe35030dcfab1092a8f26fced59..25758671ddf4fb1cf9d42b3e7d58568d97e2f5cd 100644 (file)
@@ -716,6 +716,13 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
        if (sw->authorized)
                goto unlock;
 
+       /*
+        * Make sure there is no PCIe rescan ongoing when a new PCIe
+        * tunnel is created. Otherwise the PCIe rescan code might find
+        * the new tunnel too early.
+        */
+       pci_lock_rescan_remove();
+
        switch (val) {
        /* Approve switch */
        case 1:
@@ -735,6 +742,8 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
                break;
        }
 
+       pci_unlock_rescan_remove();
+
        if (!ret) {
                sw->authorized = val;
                /* Notify status change to the userspace */
@@ -766,6 +775,15 @@ static ssize_t authorized_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(authorized);
 
+static ssize_t boot_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct tb_switch *sw = tb_to_switch(dev);
+
+       return sprintf(buf, "%u\n", sw->boot);
+}
+static DEVICE_ATTR_RO(boot);
+
 static ssize_t device_show(struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
@@ -942,6 +960,7 @@ static DEVICE_ATTR_RO(unique_id);
 
 static struct attribute *switch_attrs[] = {
        &dev_attr_authorized.attr,
+       &dev_attr_boot.attr,
        &dev_attr_device.attr,
        &dev_attr_device_name.attr,
        &dev_attr_key.attr,
@@ -970,6 +989,10 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,
                if (sw->dma_port)
                        return attr->mode;
                return 0;
+       } else if (attr == &dev_attr_boot.attr) {
+               if (tb_route(sw))
+                       return attr->mode;
+               return 0;
        }
 
        return sw->safe_mode ? 0 : attr->mode;
@@ -1028,6 +1051,9 @@ static int tb_switch_get_generation(struct tb_switch *sw)
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE:
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE:
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE:
+       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE:
+       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE:
+       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE:
                return 3;
 
        default:
@@ -1470,6 +1496,7 @@ struct tb_sw_lookup {
        u8 link;
        u8 depth;
        const uuid_t *uuid;
+       u64 route;
 };
 
 static int tb_switch_match(struct device *dev, void *data)
@@ -1485,6 +1512,11 @@ static int tb_switch_match(struct device *dev, void *data)
        if (lookup->uuid)
                return !memcmp(sw->uuid, lookup->uuid, sizeof(*lookup->uuid));
 
+       if (lookup->route) {
+               return sw->config.route_lo == lower_32_bits(lookup->route) &&
+                      sw->config.route_hi == upper_32_bits(lookup->route);
+       }
+
        /* Root switch is matched only by depth */
        if (!lookup->depth)
                return !sw->depth;
@@ -1519,7 +1551,7 @@ struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, u8 depth)
 }
 
 /**
- * tb_switch_find_by_link_depth() - Find switch by UUID
+ * tb_switch_find_by_uuid() - Find switch by UUID
  * @tb: Domain the switch belongs
  * @uuid: UUID to look for
  *
@@ -1542,6 +1574,33 @@ struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid)
        return NULL;
 }
 
+/**
+ * tb_switch_find_by_route() - Find switch by route string
+ * @tb: Domain the switch belongs
+ * @route: Route string to look for
+ *
+ * Returned switch has reference count increased so the caller needs to
+ * call tb_switch_put() when done with the switch.
+ */
+struct tb_switch *tb_switch_find_by_route(struct tb *tb, u64 route)
+{
+       struct tb_sw_lookup lookup;
+       struct device *dev;
+
+       if (!route)
+               return tb_switch_get(tb->root_switch);
+
+       memset(&lookup, 0, sizeof(lookup));
+       lookup.tb = tb;
+       lookup.route = route;
+
+       dev = bus_find_device(&tb_bus_type, NULL, &lookup, tb_switch_match);
+       if (dev)
+               return tb_to_switch(dev);
+
+       return NULL;
+}
+
 void tb_switch_exit(void)
 {
        ida_destroy(&nvm_ida);
index 895c57a0a090b4f1ab958bf8caf1f46f3e78ad63..9d9f0ca16bfbea9bd18695ee712757df0354730f 100644 (file)
@@ -66,6 +66,7 @@ struct tb_switch_nvm {
  * @nvm: Pointer to the NVM if the switch has one (%NULL otherwise)
  * @no_nvm_upgrade: Prevent NVM upgrade of this switch
  * @safe_mode: The switch is in safe-mode
+ * @boot: Whether the switch was already authorized on boot or not
  * @authorized: Whether the switch is authorized by user or policy
  * @work: Work used to automatically authorize a switch
  * @security_level: Switch supported security level
@@ -99,6 +100,7 @@ struct tb_switch {
        struct tb_switch_nvm *nvm;
        bool no_nvm_upgrade;
        bool safe_mode;
+       bool boot;
        unsigned int authorized;
        struct work_struct work;
        enum tb_security_level security_level;
@@ -198,6 +200,8 @@ struct tb_path {
  * @suspend: Connection manager specific suspend
  * @complete: Connection manager specific complete
  * @handle_event: Handle thunderbolt event
+ * @get_boot_acl: Get boot ACL list
+ * @set_boot_acl: Set boot ACL list
  * @approve_switch: Approve switch
  * @add_switch_key: Add key to switch
  * @challenge_switch_key: Challenge switch using key
@@ -215,6 +219,8 @@ struct tb_cm_ops {
        void (*complete)(struct tb *tb);
        void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type,
                             const void *buf, size_t size);
+       int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids);
+       int (*set_boot_acl)(struct tb *tb, const uuid_t *uuids, size_t nuuids);
        int (*approve_switch)(struct tb *tb, struct tb_switch *sw);
        int (*add_switch_key)(struct tb *tb, struct tb_switch *sw);
        int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw,
@@ -386,6 +392,14 @@ struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
 struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link,
                                               u8 depth);
 struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid);
+struct tb_switch *tb_switch_find_by_route(struct tb *tb, u64 route);
+
+static inline struct tb_switch *tb_switch_get(struct tb_switch *sw)
+{
+       if (sw)
+               get_device(&sw->dev);
+       return sw;
+}
 
 static inline void tb_switch_put(struct tb_switch *sw)
 {
index b0a092baa6054f154b614904407bccfff1df53c7..bc13f8d6b80450fb87ee1be980bfd765cb6017a5 100644 (file)
@@ -102,6 +102,8 @@ enum icm_pkg_code {
        ICM_ADD_DEVICE_KEY = 0x6,
        ICM_GET_ROUTE = 0xa,
        ICM_APPROVE_XDOMAIN = 0x10,
+       ICM_DISCONNECT_XDOMAIN = 0x11,
+       ICM_PREBOOT_ACL = 0x18,
 };
 
 enum icm_event_code {
@@ -122,18 +124,23 @@ struct icm_pkg_header {
 #define ICM_FLAGS_NO_KEY               BIT(1)
 #define ICM_FLAGS_SLEVEL_SHIFT         3
 #define ICM_FLAGS_SLEVEL_MASK          GENMASK(4, 3)
+#define ICM_FLAGS_WRITE                        BIT(7)
 
 struct icm_pkg_driver_ready {
        struct icm_pkg_header hdr;
 };
 
-struct icm_pkg_driver_ready_response {
+/* Falcon Ridge only messages */
+
+struct icm_fr_pkg_driver_ready_response {
        struct icm_pkg_header hdr;
        u8 romver;
        u8 ramver;
        u16 security_level;
 };
 
+#define ICM_FR_SLEVEL_MASK             0xf
+
 /* Falcon Ridge & Alpine Ridge common messages */
 
 struct icm_fr_pkg_get_topology {
@@ -176,6 +183,8 @@ struct icm_fr_event_device_connected {
 #define ICM_LINK_INFO_DEPTH_SHIFT      4
 #define ICM_LINK_INFO_DEPTH_MASK       GENMASK(7, 4)
 #define ICM_LINK_INFO_APPROVED         BIT(8)
+#define ICM_LINK_INFO_REJECTED         BIT(9)
+#define ICM_LINK_INFO_BOOT             BIT(10)
 
 struct icm_fr_pkg_approve_device {
        struct icm_pkg_header hdr;
@@ -270,6 +279,18 @@ struct icm_fr_pkg_approve_xdomain_response {
 
 /* Alpine Ridge only messages */
 
+struct icm_ar_pkg_driver_ready_response {
+       struct icm_pkg_header hdr;
+       u8 romver;
+       u8 ramver;
+       u16 info;
+};
+
+#define ICM_AR_INFO_SLEVEL_MASK                GENMASK(3, 0)
+#define ICM_AR_INFO_BOOT_ACL_SHIFT     7
+#define ICM_AR_INFO_BOOT_ACL_MASK      GENMASK(11, 7)
+#define ICM_AR_INFO_BOOT_ACL_SUPPORTED BIT(13)
+
 struct icm_ar_pkg_get_route {
        struct icm_pkg_header hdr;
        u16 reserved;
@@ -284,6 +305,163 @@ struct icm_ar_pkg_get_route_response {
        u32 route_lo;
 };
 
+struct icm_ar_boot_acl_entry {
+       u32 uuid_lo;
+       u32 uuid_hi;
+};
+
+#define ICM_AR_PREBOOT_ACL_ENTRIES     16
+
+struct icm_ar_pkg_preboot_acl {
+       struct icm_pkg_header hdr;
+       struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES];
+};
+
+struct icm_ar_pkg_preboot_acl_response {
+       struct icm_pkg_header hdr;
+       struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES];
+};
+
+/* Titan Ridge messages */
+
+struct icm_tr_pkg_driver_ready_response {
+       struct icm_pkg_header hdr;
+       u16 reserved1;
+       u16 info;
+       u32 nvm_version;
+       u16 device_id;
+       u16 reserved2;
+};
+
+#define ICM_TR_INFO_SLEVEL_MASK                GENMASK(2, 0)
+#define ICM_TR_INFO_BOOT_ACL_SHIFT     7
+#define ICM_TR_INFO_BOOT_ACL_MASK      GENMASK(12, 7)
+
+struct icm_tr_event_device_connected {
+       struct icm_pkg_header hdr;
+       uuid_t ep_uuid;
+       u32 route_hi;
+       u32 route_lo;
+       u8 connection_id;
+       u8 reserved;
+       u16 link_info;
+       u32 ep_name[55];
+};
+
+struct icm_tr_event_device_disconnected {
+       struct icm_pkg_header hdr;
+       u32 route_hi;
+       u32 route_lo;
+};
+
+struct icm_tr_event_xdomain_connected {
+       struct icm_pkg_header hdr;
+       u16 reserved;
+       u16 link_info;
+       uuid_t remote_uuid;
+       uuid_t local_uuid;
+       u32 local_route_hi;
+       u32 local_route_lo;
+       u32 remote_route_hi;
+       u32 remote_route_lo;
+};
+
+struct icm_tr_event_xdomain_disconnected {
+       struct icm_pkg_header hdr;
+       u32 route_hi;
+       u32 route_lo;
+       uuid_t remote_uuid;
+};
+
+struct icm_tr_pkg_approve_device {
+       struct icm_pkg_header hdr;
+       uuid_t ep_uuid;
+       u32 route_hi;
+       u32 route_lo;
+       u8 connection_id;
+       u8 reserved1[3];
+};
+
+struct icm_tr_pkg_add_device_key {
+       struct icm_pkg_header hdr;
+       uuid_t ep_uuid;
+       u32 route_hi;
+       u32 route_lo;
+       u8 connection_id;
+       u8 reserved[3];
+       u32 key[8];
+};
+
+struct icm_tr_pkg_challenge_device {
+       struct icm_pkg_header hdr;
+       uuid_t ep_uuid;
+       u32 route_hi;
+       u32 route_lo;
+       u8 connection_id;
+       u8 reserved[3];
+       u32 challenge[8];
+};
+
+struct icm_tr_pkg_approve_xdomain {
+       struct icm_pkg_header hdr;
+       u32 route_hi;
+       u32 route_lo;
+       uuid_t remote_uuid;
+       u16 transmit_path;
+       u16 transmit_ring;
+       u16 receive_path;
+       u16 receive_ring;
+};
+
+struct icm_tr_pkg_disconnect_xdomain {
+       struct icm_pkg_header hdr;
+       u8 stage;
+       u8 reserved[3];
+       u32 route_hi;
+       u32 route_lo;
+       uuid_t remote_uuid;
+};
+
+struct icm_tr_pkg_challenge_device_response {
+       struct icm_pkg_header hdr;
+       uuid_t ep_uuid;
+       u32 route_hi;
+       u32 route_lo;
+       u8 connection_id;
+       u8 reserved[3];
+       u32 challenge[8];
+       u32 response[8];
+};
+
+struct icm_tr_pkg_add_device_key_response {
+       struct icm_pkg_header hdr;
+       uuid_t ep_uuid;
+       u32 route_hi;
+       u32 route_lo;
+       u8 connection_id;
+       u8 reserved[3];
+};
+
+struct icm_tr_pkg_approve_xdomain_response {
+       struct icm_pkg_header hdr;
+       u32 route_hi;
+       u32 route_lo;
+       uuid_t remote_uuid;
+       u16 transmit_path;
+       u16 transmit_ring;
+       u16 receive_path;
+       u16 receive_ring;
+};
+
+struct icm_tr_pkg_disconnect_xdomain_response {
+       struct icm_pkg_header hdr;
+       u8 stage;
+       u8 reserved[3];
+       u32 route_hi;
+       u32 route_lo;
+       uuid_t remote_uuid;
+};
+
 /* XDomain messages */
 
 struct tb_xdomain_header {
index f25d88d4552be3b3f1c5ed8597896a2f4ca38530..8abb4e8430857c0d79b12f3b60dd1234715649fa 100644 (file)
@@ -1255,6 +1255,7 @@ struct tb_xdomain_lookup {
        const uuid_t *uuid;
        u8 link;
        u8 depth;
+       u64 route;
 };
 
 static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw,
@@ -1275,9 +1276,13 @@ static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw,
                        if (lookup->uuid) {
                                if (uuid_equal(xd->remote_uuid, lookup->uuid))
                                        return xd;
-                       } else if (lookup->link == xd->link &&
+                       } else if (lookup->link &&
+                                  lookup->link == xd->link &&
                                   lookup->depth == xd->depth) {
                                return xd;
+                       } else if (lookup->route &&
+                                  lookup->route == xd->route) {
+                               return xd;
                        }
                } else if (port->remote) {
                        xd = switch_find_xdomain(port->remote->sw, lookup);
@@ -1313,12 +1318,7 @@ struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid)
        lookup.uuid = uuid;
 
        xd = switch_find_xdomain(tb->root_switch, &lookup);
-       if (xd) {
-               get_device(&xd->dev);
-               return xd;
-       }
-
-       return NULL;
+       return tb_xdomain_get(xd);
 }
 EXPORT_SYMBOL_GPL(tb_xdomain_find_by_uuid);
 
@@ -1349,13 +1349,36 @@ struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link,
        lookup.depth = depth;
 
        xd = switch_find_xdomain(tb->root_switch, &lookup);
-       if (xd) {
-               get_device(&xd->dev);
-               return xd;
-       }
+       return tb_xdomain_get(xd);
+}
 
-       return NULL;
+/**
+ * tb_xdomain_find_by_route() - Find an XDomain by route string
+ * @tb: Domain where the XDomain belongs to
+ * @route: XDomain route string
+ *
+ * Finds XDomain by walking through the Thunderbolt topology below @tb.
+ * The returned XDomain will have its reference count increased so the
+ * caller needs to call tb_xdomain_put() when it is done with the
+ * object.
+ *
+ * This will find all XDomains including the ones that are not yet added
+ * to the bus (handshake is still in progress).
+ *
+ * The caller needs to hold @tb->lock.
+ */
+struct tb_xdomain *tb_xdomain_find_by_route(struct tb *tb, u64 route)
+{
+       struct tb_xdomain_lookup lookup;
+       struct tb_xdomain *xd;
+
+       memset(&lookup, 0, sizeof(lookup));
+       lookup.route = route;
+
+       xd = switch_find_xdomain(tb->root_switch, &lookup);
+       return tb_xdomain_get(xd);
 }
+EXPORT_SYMBOL_GPL(tb_xdomain_find_by_route);
 
 bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type,
                               const void *buf, size_t size)
index 8ca549032c27342ff01c219ee298d1d1d0c613fb..f695a7e8c314585c8b0df8ab88f44d897986839b 100644 (file)
@@ -121,6 +121,94 @@ static void hv_uio_rescind(struct vmbus_channel *channel)
        uio_event_notify(&pdata->info);
 }
 
+/*
+ * Handle fault when looking for sub channel ring buffer
+ * Subchannel ring buffer is same as resource 0 which is main ring buffer
+ * This is derived from uio_vma_fault
+ */
+static int hv_uio_vma_fault(struct vm_fault *vmf)
+{
+       struct vm_area_struct *vma = vmf->vma;
+       void *ring_buffer = vma->vm_private_data;
+       struct page *page;
+       void *addr;
+
+       addr = ring_buffer + (vmf->pgoff << PAGE_SHIFT);
+       page = virt_to_page(addr);
+       get_page(page);
+       vmf->page = page;
+       return 0;
+}
+
+static const struct vm_operations_struct hv_uio_vm_ops = {
+       .fault = hv_uio_vma_fault,
+};
+
+/* Sysfs API to allow mmap of the ring buffers */
+static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj,
+                           struct bin_attribute *attr,
+                           struct vm_area_struct *vma)
+{
+       struct vmbus_channel *channel
+               = container_of(kobj, struct vmbus_channel, kobj);
+       unsigned long requested_pages, actual_pages;
+
+       if (vma->vm_end < vma->vm_start)
+               return -EINVAL;
+
+       /* only allow 0 for now */
+       if (vma->vm_pgoff > 0)
+               return -EINVAL;
+
+       requested_pages = vma_pages(vma);
+       actual_pages = 2 * HV_RING_SIZE;
+       if (requested_pages > actual_pages)
+               return -EINVAL;
+
+       vma->vm_private_data = channel->ringbuffer_pages;
+       vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+       vma->vm_ops = &hv_uio_vm_ops;
+       return 0;
+}
+
+static struct bin_attribute ring_buffer_bin_attr __ro_after_init = {
+       .attr = {
+               .name = "ring",
+               .mode = 0600,
+               /* size is set at init time */
+       },
+       .mmap = hv_uio_ring_mmap,
+};
+
+/* Callback from VMBUS subystem when new channel created. */
+static void
+hv_uio_new_channel(struct vmbus_channel *new_sc)
+{
+       struct hv_device *hv_dev = new_sc->primary_channel->device_obj;
+       struct device *device = &hv_dev->device;
+       struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev);
+       const size_t ring_bytes = HV_RING_SIZE * PAGE_SIZE;
+       int ret;
+
+       /* Create host communication ring */
+       ret = vmbus_open(new_sc, ring_bytes, ring_bytes, NULL, 0,
+                        hv_uio_channel_cb, pdata);
+       if (ret) {
+               dev_err(device, "vmbus_open subchannel failed: %d\n", ret);
+               return;
+       }
+
+       /* Disable interrupts on sub channel */
+       new_sc->inbound.ring_buffer->interrupt_mask = 1;
+       set_channel_read_mode(new_sc, HV_CALL_ISR);
+
+       ret = sysfs_create_bin_file(&new_sc->kobj, &ring_buffer_bin_attr);
+       if (ret) {
+               dev_err(device, "sysfs create ring bin file failed; %d\n", ret);
+               vmbus_close(new_sc);
+       }
+}
+
 static void
 hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata)
 {
@@ -236,6 +324,7 @@ hv_uio_probe(struct hv_device *dev,
        }
 
        vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind);
+       vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel);
 
        hv_set_drvdata(dev, pdata);
 
index 0c2a5a8327bd0c38b514c000ed9827656359f193..80a778b02f284d09d7744907de465d8f2b721420 100644 (file)
@@ -706,6 +706,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
                dev_err(&sl->dev,
                        "Device registration [%s] failed. err=%d\n",
                        dev_name(&sl->dev), err);
+               put_device(&sl->dev);
                return err;
        }
        w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl);
index a65e4a56318ca79d17c91e36c69c6aa3371728e4..a279c58fe3606224df4c7c441090ed07f98d15b9 100644 (file)
@@ -67,18 +67,18 @@ static int find_dynamic_major(void)
        int i;
        struct char_device_struct *cd;
 
-       for (i = ARRAY_SIZE(chrdevs)-1; i > CHRDEV_MAJOR_DYN_END; i--) {
+       for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) {
                if (chrdevs[i] == NULL)
                        return i;
        }
 
        for (i = CHRDEV_MAJOR_DYN_EXT_START;
-            i > CHRDEV_MAJOR_DYN_EXT_END; i--) {
+            i >= CHRDEV_MAJOR_DYN_EXT_END; i--) {
                for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next)
                        if (cd->major == i)
                                break;
 
-               if (cd == NULL || cd->major != i)
+               if (cd == NULL)
                        return i;
        }
 
@@ -121,8 +121,8 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor,
        }
 
        if (major >= CHRDEV_MAJOR_MAX) {
-               pr_err("CHRDEV \"%s\" major requested (%d) is greater than the maximum (%d)\n",
-                      name, major, CHRDEV_MAJOR_MAX);
+               pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",
+                      name, major, CHRDEV_MAJOR_MAX-1);
                ret = -EINVAL;
                goto out;
        }
index 6d94e82c8ad9e339de564f54bba91bcdc0ebddfe..7f033b1ea5680e392efd082fbc8cdf0f91b926ac 100644 (file)
@@ -230,6 +230,7 @@ extern void devm_extcon_unregister_notifier_all(struct device *dev,
  * Following APIs get the extcon_dev from devicetree or by through extcon name.
  */
 extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
+extern struct extcon_dev *extcon_find_edev_by_node(struct device_node *node);
 extern struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev,
                                                     int index);
 
@@ -283,6 +284,11 @@ static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
        return ERR_PTR(-ENODEV);
 }
 
+static inline struct extcon_dev *extcon_find_edev_by_node(struct device_node *node)
+{
+       return ERR_PTR(-ENODEV);
+}
+
 static inline struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev,
                                int index)
 {
diff --git a/include/linux/extcon/extcon-gpio.h b/include/linux/extcon/extcon-gpio.h
deleted file mode 100644 (file)
index 7cacafb..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Single-state GPIO extcon driver based on extcon class
- *
- * Copyright (C) 2012 Samsung Electronics
- * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
- *
- * based on switch class driver
- * Copyright (C) 2008 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- */
-#ifndef __EXTCON_GPIO_H__
-#define __EXTCON_GPIO_H__ __FILE__
-
-#include <linux/extcon.h>
-
-/**
- * struct gpio_extcon_pdata - A simple GPIO-controlled extcon device.
- * @extcon_id:         The unique id of specific external connector.
- * @gpio:              Corresponding GPIO.
- * @gpio_active_low:   Boolean describing whether gpio active state is 1 or 0
- *                     If true, low state of gpio means active.
- *                     If false, high state of gpio means active.
- * @debounce:          Debounce time for GPIO IRQ in ms.
- * @irq_flags:         IRQ Flags (e.g., IRQF_TRIGGER_LOW).
- * @check_on_resume:   Boolean describing whether to check the state of gpio
- *                     while resuming from sleep.
- */
-struct gpio_extcon_pdata {
-       unsigned int extcon_id;
-       unsigned gpio;
-       bool gpio_active_low;
-       unsigned long debounce;
-       unsigned long irq_flags;
-
-       bool check_on_resume;
-};
-
-#endif /* __EXTCON_GPIO_H__ */
index 93bd6fcd6e6217144496feef423b491151865707..2048f3c3b68a8120937dcc03c25dcded160e74ac 100644 (file)
@@ -844,7 +844,7 @@ struct vmbus_channel {
 
        /*
         * NUMA distribution policy:
-        * We support teo policies:
+        * We support two policies:
         * 1) Balanced: Here all performance critical channels are
         *    distributed evenly amongst all the NUMA nodes.
         *    This policy will be the default policy.
index 497706f5adcacc5db666a3099931bb45d0fbfc28..f89598bc4e1cb3d5036c48d50fe357f260de6d50 100644 (file)
@@ -22,6 +22,31 @@ typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
 typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
                                 void *val, size_t bytes);
 
+/**
+ * struct nvmem_config - NVMEM device configuration
+ *
+ * @dev:       Parent device.
+ * @name:      Optional name.
+ * @id:                Optional device ID used in full name. Ignored if name is NULL.
+ * @owner:     Pointer to exporter module. Used for refcounting.
+ * @cells:     Optional array of pre-defined NVMEM cells.
+ * @ncells:    Number of elements in cells.
+ * @read_only: Device is read-only.
+ * @root_only: Device is accessibly to root only.
+ * @reg_read:  Callback to read data.
+ * @reg_write: Callback to write data.
+ * @size:      Device size.
+ * @word_size: Minimum read/write access granularity.
+ * @stride:    Minimum read/write access stride.
+ * @priv:      User context passed to read/write callbacks.
+ *
+ * Note: A default "nvmem<id>" name will be assigned to the device if
+ * no name is specified in its configuration. In such case "<id>" is
+ * generated with ida_simple_get() and provided id field is ignored.
+ *
+ * Note: Specifying name and setting id to -1 implies a unique device
+ * whose name is provided as-is (kept unaltered).
+ */
 struct nvmem_config {
        struct device           *dev;
        const char              *name;
@@ -47,6 +72,11 @@ struct nvmem_config {
 struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
 int nvmem_unregister(struct nvmem_device *nvmem);
 
+struct nvmem_device *devm_nvmem_register(struct device *dev,
+                                        const struct nvmem_config *cfg);
+
+int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem);
+
 #else
 
 static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
@@ -59,5 +89,17 @@ static inline int nvmem_unregister(struct nvmem_device *nvmem)
        return -ENOSYS;
 }
 
+static inline struct nvmem_device *
+devm_nvmem_register(struct device *dev, const struct nvmem_config *c)
+{
+       return nvmem_register(c);
+}
+
+static inline int
+devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
+{
+       return nvmem_unregister(nvmem);
+}
+
 #endif /* CONFIG_NVMEM */
 #endif  /* ifndef _LINUX_NVMEM_PROVIDER_H */
index 44f74b54d48302ae34d691a8bf564df0c6c29c0f..2d61d9bde83d60258261d98368d0d889401cc422 100644 (file)
 #define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227
 #define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408
 
+#define PCI_VENDOR_ID_ALTERA           0x1172
+
 #define PCI_VENDOR_ID_SBE              0x1176
 #define PCI_DEVICE_ID_SBE_WANXL100     0x0301
 #define PCI_DEVICE_ID_SBE_WANXL200     0x0302
index 210ff2292361a9daecdf0ac027dc3f28a9d1fbff..c6f577ab6f21aa005c83e56ddcc80701f984b9fd 100644 (file)
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * System Trace Module (STM) infrastructure apis
  * Copyright (C) 2014 Intel Corporation.
- *
- * 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 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.
  */
 
 #ifndef _STM_H_
index 7b69853188b1afbed9387f5cfddbf4e547602217..a3ed26082bc1d8ba316074b4b1c5475866948084 100644 (file)
@@ -45,12 +45,16 @@ enum tb_cfg_pkg_type {
  * @TB_SECURITY_USER: User approval required at minimum
  * @TB_SECURITY_SECURE: One time saved key required at minimum
  * @TB_SECURITY_DPONLY: Only tunnel Display port (and USB)
+ * @TB_SECURITY_USBONLY: Only tunnel USB controller of the connected
+ *                      Thunderbolt dock (and Display Port). All PCIe
+ *                      links downstream of the dock are removed.
  */
 enum tb_security_level {
        TB_SECURITY_NONE,
        TB_SECURITY_USER,
        TB_SECURITY_SECURE,
        TB_SECURITY_DPONLY,
+       TB_SECURITY_USBONLY,
 };
 
 /**
@@ -65,6 +69,7 @@ enum tb_security_level {
  * @cm_ops: Connection manager specific operations vector
  * @index: Linux assigned domain number
  * @security_level: Current security level
+ * @nboot_acl: Number of boot ACLs the domain supports
  * @privdata: Private connection manager specific data
  */
 struct tb {
@@ -77,6 +82,7 @@ struct tb {
        const struct tb_cm_ops *cm_ops;
        int index;
        enum tb_security_level security_level;
+       size_t nboot_acl;
        unsigned long privdata[0];
 };
 
@@ -237,6 +243,7 @@ int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path,
                            u16 receive_ring);
 int tb_xdomain_disable_paths(struct tb_xdomain *xd);
 struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid);
+struct tb_xdomain *tb_xdomain_find_by_route(struct tb *tb, u64 route);
 
 static inline struct tb_xdomain *
 tb_xdomain_find_by_uuid_locked(struct tb *tb, const uuid_t *uuid)
@@ -250,6 +257,18 @@ tb_xdomain_find_by_uuid_locked(struct tb *tb, const uuid_t *uuid)
        return xd;
 }
 
+static inline struct tb_xdomain *
+tb_xdomain_find_by_route_locked(struct tb *tb, u64 route)
+{
+       struct tb_xdomain *xd;
+
+       mutex_lock(&tb->lock);
+       xd = tb_xdomain_find_by_route(tb, route);
+       mutex_unlock(&tb->lock);
+
+       return xd;
+}
+
 static inline struct tb_xdomain *tb_xdomain_get(struct tb_xdomain *xd)
 {
        if (xd)
index dbffdc23d80480aa1e0202fc8a146498ec6b556a..7bac318b44400e1d54485db8be67bc9bc9cbe42d 100644 (file)
@@ -3,15 +3,6 @@
  * System Trace Module (STM) userspace interfaces
  * Copyright (c) 2014, Intel Corporation.
  *
- * 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 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.
- *
  * STM class implements generic infrastructure for  System Trace Module devices
  * as defined in MIPI STPv2 specification.
  */
 
 #include <linux/types.h>
 
+/* Maximum allowed master and channel values */
+#define STP_MASTER_MAX 0xffff
+#define STP_CHANNEL_MAX        0xffff
+
 /**
  * struct stp_policy_id - identification for the STP policy
  * @size:      size of the structure including real id[] length
index 5f2aedd58bc5032eb18e882fbb2e539f0feed550..5bec1120b392e8449b94b556f2c70a40292b65b3 100644 (file)
@@ -5,6 +5,12 @@
 #include <linux/gfp.h>
 #include <linux/export.h>
 
+enum devm_ioremap_type {
+       DEVM_IOREMAP = 0,
+       DEVM_IOREMAP_NC,
+       DEVM_IOREMAP_WC,
+};
+
 void devm_ioremap_release(struct device *dev, void *res)
 {
        iounmap(*(void __iomem **)res);
@@ -15,24 +21,28 @@ static int devm_ioremap_match(struct device *dev, void *res, void *match_data)
        return *(void **)res == match_data;
 }
 
-/**
- * devm_ioremap - Managed ioremap()
- * @dev: Generic device to remap IO address for
- * @offset: Resource address to map
- * @size: Size of map
- *
- * Managed ioremap().  Map is automatically unmapped on driver detach.
- */
-void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
-                          resource_size_t size)
+static void __iomem *__devm_ioremap(struct device *dev, resource_size_t offset,
+                                   resource_size_t size,
+                                   enum devm_ioremap_type type)
 {
-       void __iomem **ptr, *addr;
+       void __iomem **ptr, *addr = NULL;
 
        ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
        if (!ptr)
                return NULL;
 
-       addr = ioremap(offset, size);
+       switch (type) {
+       case DEVM_IOREMAP:
+               addr = ioremap(offset, size);
+               break;
+       case DEVM_IOREMAP_NC:
+               addr = ioremap_nocache(offset, size);
+               break;
+       case DEVM_IOREMAP_WC:
+               addr = ioremap_wc(offset, size);
+               break;
+       }
+
        if (addr) {
                *ptr = addr;
                devres_add(dev, ptr);
@@ -41,6 +51,20 @@ void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
 
        return addr;
 }
+
+/**
+ * devm_ioremap - Managed ioremap()
+ * @dev: Generic device to remap IO address for
+ * @offset: Resource address to map
+ * @size: Size of map
+ *
+ * Managed ioremap().  Map is automatically unmapped on driver detach.
+ */
+void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
+                          resource_size_t size)
+{
+       return __devm_ioremap(dev, offset, size, DEVM_IOREMAP);
+}
 EXPORT_SYMBOL(devm_ioremap);
 
 /**
@@ -55,20 +79,7 @@ EXPORT_SYMBOL(devm_ioremap);
 void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
                                   resource_size_t size)
 {
-       void __iomem **ptr, *addr;
-
-       ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
-       if (!ptr)
-               return NULL;
-
-       addr = ioremap_nocache(offset, size);
-       if (addr) {
-               *ptr = addr;
-               devres_add(dev, ptr);
-       } else
-               devres_free(ptr);
-
-       return addr;
+       return __devm_ioremap(dev, offset, size, DEVM_IOREMAP_NC);
 }
 EXPORT_SYMBOL(devm_ioremap_nocache);
 
@@ -83,20 +94,7 @@ EXPORT_SYMBOL(devm_ioremap_nocache);
 void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset,
                              resource_size_t size)
 {
-       void __iomem **ptr, *addr;
-
-       ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
-       if (!ptr)
-               return NULL;
-
-       addr = ioremap_wc(offset, size);
-       if (addr) {
-               *ptr = addr;
-               devres_add(dev, ptr);
-       } else
-               devres_free(ptr);
-
-       return addr;
+       return __devm_ioremap(dev, offset, size, DEVM_IOREMAP_WC);
 }
 EXPORT_SYMBOL(devm_ioremap_wc);
 
index 457a1521f32fe6cc6cb3493cdde8105c187c0d44..d78aed86af09b61ab27d476f850df1da52669ac4 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <string.h>
 #include <errno.h>
 #include <linux/hyperv.h>
+#include <linux/limits.h>
 #include <syslog.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <getopt.h>
 
 static int target_fd;
-static char target_fname[W_MAX_PATH];
+static char target_fname[PATH_MAX];
 static unsigned long long filesize;
 
 static int hv_start_fcopy(struct hv_start_fcopy *smsg)
index 4c99c57736cefd155326293be053a7c4ffef9c6a..dbf6e8bd98ba59be0519d284fdc7bf59b19f46d3 100644 (file)
@@ -634,64 +634,6 @@ static char *kvp_if_name_to_mac(char *if_name)
        return mac_addr;
 }
 
-
-/*
- * Retrieve the interface name given tha MAC address.
- */
-
-static char *kvp_mac_to_if_name(char *mac)
-{
-       DIR *dir;
-       struct dirent *entry;
-       FILE    *file;
-       char    *p, *x;
-       char    *if_name = NULL;
-       char    buf[256];
-       char dev_id[PATH_MAX];
-       unsigned int i;
-
-       dir = opendir(KVP_NET_DIR);
-       if (dir == NULL)
-               return NULL;
-
-       while ((entry = readdir(dir)) != NULL) {
-               /*
-                * Set the state for the next pass.
-                */
-               snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR,
-                        entry->d_name);
-
-               file = fopen(dev_id, "r");
-               if (file == NULL)
-                       continue;
-
-               p = fgets(buf, sizeof(buf), file);
-               if (p) {
-                       x = strchr(p, '\n');
-                       if (x)
-                               *x = '\0';
-
-                       for (i = 0; i < strlen(p); i++)
-                               p[i] = toupper(p[i]);
-
-                       if (!strcmp(p, mac)) {
-                               /*
-                                * Found the MAC match; return the interface
-                                * name. The caller will free the memory.
-                                */
-                               if_name = strdup(entry->d_name);
-                               fclose(file);
-                               break;
-                       }
-               }
-               fclose(file);
-       }
-
-       closedir(dir);
-       return if_name;
-}
-
-
 static void kvp_process_ipconfig_file(char *cmd,
                                        char *config_buf, unsigned int len,
                                        int element_size, int offset)
@@ -997,6 +939,70 @@ kvp_get_ip_info(int family, char *if_name, int op,
        return error;
 }
 
+/*
+ * Retrieve the IP given the MAC address.
+ */
+static int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val)
+{
+       char *mac = (char *)kvp_ip_val->adapter_id;
+       DIR *dir;
+       struct dirent *entry;
+       FILE    *file;
+       char    *p, *x;
+       char    *if_name = NULL;
+       char    buf[256];
+       char dev_id[PATH_MAX];
+       unsigned int i;
+       int error = HV_E_FAIL;
+
+       dir = opendir(KVP_NET_DIR);
+       if (dir == NULL)
+               return HV_E_FAIL;
+
+       while ((entry = readdir(dir)) != NULL) {
+               /*
+                * Set the state for the next pass.
+                */
+               snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR,
+                        entry->d_name);
+
+               file = fopen(dev_id, "r");
+               if (file == NULL)
+                       continue;
+
+               p = fgets(buf, sizeof(buf), file);
+               fclose(file);
+               if (!p)
+                       continue;
+
+               x = strchr(p, '\n');
+               if (x)
+                       *x = '\0';
+
+               for (i = 0; i < strlen(p); i++)
+                       p[i] = toupper(p[i]);
+
+               if (strcmp(p, mac))
+                       continue;
+
+               /*
+                * Found the MAC match.
+                * A NIC (e.g. VF) matching the MAC, but without IP, is skipped.
+                */
+               if_name = entry->d_name;
+               if (!if_name)
+                       continue;
+
+               error = kvp_get_ip_info(0, if_name, KVP_OP_GET_IP_INFO,
+                                       kvp_ip_val, MAX_IP_ADDR_SIZE * 2);
+
+               if (!error && strlen((char *)kvp_ip_val->ip_addr))
+                       break;
+       }
+
+       closedir(dir);
+       return error;
+}
 
 static int expand_ipv6(char *addr, int type)
 {
@@ -1472,26 +1478,12 @@ int main(int argc, char *argv[])
                switch (op) {
                case KVP_OP_GET_IP_INFO:
                        kvp_ip_val = &hv_msg->body.kvp_ip_val;
-                       if_name =
-                       kvp_mac_to_if_name((char *)kvp_ip_val->adapter_id);
 
-                       if (if_name == NULL) {
-                               /*
-                                * We could not map the mac address to an
-                                * interface name; return error.
-                                */
-                               hv_msg->error = HV_E_FAIL;
-                               break;
-                       }
-                       error = kvp_get_ip_info(
-                                               0, if_name, KVP_OP_GET_IP_INFO,
-                                               kvp_ip_val,
-                                               (MAX_IP_ADDR_SIZE * 2));
+                       error =  kvp_mac_to_ip(kvp_ip_val);
 
                        if (error)
                                hv_msg->error = error;
 
-                       free(if_name);
                        break;
 
                case KVP_OP_SET_IP_INFO:
index b2b4ebffab8ca491cb31f47c52c2e8406a546328..34031a297f0246116d7cff92e6226a8900e4c4a8 100644 (file)
@@ -22,6 +22,7 @@
 #include <sys/poll.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <mntent.h>