]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge 3.14-rc3 into char-misc-next
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Feb 2014 16:09:40 +0000 (08:09 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Feb 2014 16:09:40 +0000 (08:09 -0800)
We need the fixes here for future mei and other patches.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
111 files changed:
Documentation/DocBook/Makefile
Documentation/DocBook/w1.tmpl [new file with mode: 0644]
Documentation/connector/cn_test.c
Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spmi/spmi.txt [new file with mode: 0644]
Documentation/w1/masters/ds2490
Documentation/w1/w1.netlink
arch/arm/boot/dts/sun4i-a10.dtsi
arch/arm/boot/dts/sun5i-a10s.dtsi
arch/arm/boot/dts/sun5i-a13.dtsi
drivers/Kconfig
drivers/Makefile
drivers/base/regmap/regmap-spmi.c
drivers/char/agp/frontend.c
drivers/char/agp/generic.c
drivers/char/agp/intel-gtt.c
drivers/char/agp/sgi-agp.c
drivers/char/hw_random/bcm2835-rng.c
drivers/char/hw_random/core.c
drivers/char/hw_random/exynos-rng.c
drivers/char/hw_random/n2-drv.c
drivers/char/hw_random/nomadik-rng.c
drivers/char/hw_random/octeon-rng.c
drivers/char/ipmi/ipmi_si_intf.c
drivers/char/mem.c
drivers/char/mwave/3780i.c
drivers/char/tile-srom.c
drivers/char/tpm/tpm_i2c_infineon.c
drivers/char/tpm/tpm_i2c_stm_st33.c
drivers/connector/cn_proc.c
drivers/connector/connector.c
drivers/fmc/fmc-core.c
drivers/fmc/fmc-sdb.c
drivers/hv/channel.c
drivers/hv/hv_balloon.c
drivers/hv/hv_kvp.c
drivers/hv/hv_snapshot.c
drivers/hv/hyperv_vmbus.h
drivers/hv/ring_buffer.c
drivers/hv/vmbus_drv.c
drivers/md/dm-log-userspace-transfer.c
drivers/misc/ad525x_dpot.c
drivers/misc/apds9802als.c
drivers/misc/bmp085.c
drivers/misc/carma/carma-fpga.c
drivers/misc/ds1682.c
drivers/misc/eeprom/at25.c
drivers/misc/eeprom/eeprom.c
drivers/misc/eeprom/eeprom_93xx46.c
drivers/misc/eeprom/max6875.c
drivers/misc/eeprom/sunxi_sid.c
drivers/misc/genwqe/card_debugfs.c
drivers/misc/hmc6352.c
drivers/misc/isl29003.c
drivers/misc/isl29020.c
drivers/misc/lattice-ecp3-config.c
drivers/misc/lis3lv02d/lis3lv02d.c
drivers/misc/lis3lv02d/lis3lv02d_i2c.c
drivers/misc/lis3lv02d/lis3lv02d_spi.c
drivers/misc/lkdtm.c
drivers/misc/mei/Kconfig
drivers/misc/mei/Makefile
drivers/misc/mei/amthif.c
drivers/misc/mei/client.c
drivers/misc/mei/debugfs.c
drivers/misc/mei/hbm.c
drivers/misc/mei/hbm.h
drivers/misc/mei/hw-me.c
drivers/misc/mei/hw-txe-regs.h [new file with mode: 0644]
drivers/misc/mei/hw-txe.c [new file with mode: 0644]
drivers/misc/mei/hw-txe.h [new file with mode: 0644]
drivers/misc/mei/hw.h
drivers/misc/mei/init.c
drivers/misc/mei/interrupt.c
drivers/misc/mei/mei_dev.h
drivers/misc/mei/pci-me.c
drivers/misc/mei/pci-txe.c [new file with mode: 0644]
drivers/misc/sram.c
drivers/misc/ti-st/st_core.c
drivers/misc/ti_dac7512.c
drivers/misc/tsl2550.c
drivers/spmi/Kconfig [new file with mode: 0644]
drivers/spmi/Makefile [new file with mode: 0644]
drivers/spmi/spmi-pmic-arb.c [new file with mode: 0644]
drivers/spmi/spmi.c [new file with mode: 0644]
drivers/video/uvesafb.c
drivers/vme/bridges/vme_ca91cx42.c
drivers/vme/bridges/vme_tsi148.c
drivers/w1/masters/ds2490.c
drivers/w1/masters/w1-gpio.c
drivers/w1/slaves/w1_therm.c
drivers/w1/w1.c
drivers/w1/w1.h
drivers/w1/w1_family.c
drivers/w1/w1_family.h
drivers/w1/w1_int.c
drivers/w1/w1_io.c
drivers/w1/w1_netlink.c
drivers/w1/w1_netlink.h
include/dt-bindings/spmi/spmi.h [new file with mode: 0644]
include/linux/connector.h
include/linux/hyperv.h
include/linux/mod_devicetable.h
include/linux/regmap.h
include/linux/spmi.h [new file with mode: 0644]
include/uapi/linux/Kbuild
include/uapi/linux/hyperv.h [new file with mode: 0644]
tools/Makefile
tools/hv/Makefile [new file with mode: 0644]
tools/hv/hv_vss_daemon.c

index 0f9c6ff41aac5206eb470ef07d68553d067022f0..8d96ebf524e96e0f001dc999b1734b7b97feda4e 100644 (file)
@@ -14,7 +14,7 @@ DOCBOOKS := z8530book.xml device-drivers.xml \
            genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
            80211.xml debugobjects.xml sh.xml regulator.xml \
            alsa-driver-api.xml writing-an-alsa-driver.xml \
-           tracepoint.xml drm.xml media_api.xml
+           tracepoint.xml drm.xml media_api.xml w1.xml
 
 include $(srctree)/Documentation/DocBook/media/Makefile
 
diff --git a/Documentation/DocBook/w1.tmpl b/Documentation/DocBook/w1.tmpl
new file mode 100644 (file)
index 0000000..b0228d4
--- /dev/null
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+       "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="w1id">
+  <bookinfo>
+    <title>W1: Dallas' 1-wire bus</title>
+
+    <authorgroup>
+      <author>
+        <firstname>David</firstname>
+        <surname>Fries</surname>
+        <affiliation>
+          <address>
+            <email>David@Fries.net</email>
+          </address>
+        </affiliation>
+      </author>
+
+    </authorgroup>
+
+    <copyright>
+      <year>2013</year>
+      <!--
+      <holder></holder>
+      -->
+    </copyright>
+
+    <legalnotice>
+      <para>
+        This documentation is free software; you can redistribute
+        it and/or modify it under the terms of the GNU General Public
+        License version 2.
+      </para>
+
+      <para>
+        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.
+        For more details see the file COPYING in the source
+        distribution of Linux.
+      </para>
+    </legalnotice>
+  </bookinfo>
+
+  <toc></toc>
+
+  <chapter id="w1_internal">
+    <title>W1 API internal to the kernel</title>
+
+    <sect1 id="w1_internal_api">
+      <title>W1 API internal to the kernel</title>
+      <sect2 id="w1.h">
+        <title>drivers/w1/w1.h</title>
+        <para>W1 core functions.</para>
+!Idrivers/w1/w1.h
+      </sect2>
+
+      <sect2 id="w1.c">
+        <title>drivers/w1/w1.c</title>
+        <para>W1 core functions.</para>
+!Idrivers/w1/w1.c
+      </sect2>
+
+      <sect2 id="w1_family.h">
+        <title>drivers/w1/w1_family.h</title>
+        <para>Allows registering device family operations.</para>
+!Idrivers/w1/w1_family.h
+      </sect2>
+
+      <sect2 id="w1_family.c">
+        <title>drivers/w1/w1_family.c</title>
+        <para>Allows registering device family operations.</para>
+!Edrivers/w1/w1_family.c
+      </sect2>
+
+      <sect2 id="w1_int.c">
+        <title>drivers/w1/w1_int.c</title>
+        <para>W1 internal initialization for master devices.</para>
+!Edrivers/w1/w1_int.c
+      </sect2>
+
+      <sect2 id="w1_netlink.h">
+        <title>drivers/w1/w1_netlink.h</title>
+        <para>W1 external netlink API structures and commands.</para>
+!Idrivers/w1/w1_netlink.h
+      </sect2>
+
+      <sect2 id="w1_io.c">
+        <title>drivers/w1/w1_io.c</title>
+        <para>W1 input/output.</para>
+!Edrivers/w1/w1_io.c
+!Idrivers/w1/w1_io.c
+      </sect2>
+
+    </sect1>
+
+
+  </chapter>
+
+</book>
index adcca0368d602ff4cb54c10365aa4b3651a4e79b..d12cc944b69621e44b77a7a6f6abc8d3e6b76bb4 100644 (file)
@@ -145,7 +145,7 @@ static void cn_test_timer_func(unsigned long __data)
 
                memcpy(m + 1, data, m->len);
 
-               cn_netlink_send(m, 0, GFP_ATOMIC);
+               cn_netlink_send(m, 0, 0, GFP_ATOMIC);
                kfree(m);
        }
 
index 68ba37295565262d05807c1ba8ac0f7933395725..fabdf64a5737cc637b59f5612df2f82310619e3a 100644 (file)
@@ -1,12 +1,12 @@
 Allwinner sunxi-sid
 
 Required properties:
-- compatible: "allwinner,sun4i-sid" or "allwinner,sun7i-a20-sid".
+- compatible: "allwinner,sun4i-a10-sid" or "allwinner,sun7i-a20-sid"
 - reg: Should contain registers location and length
 
 Example for sun4i:
        sid@01c23800 {
-               compatible = "allwinner,sun4i-sid";
+               compatible = "allwinner,sun4i-a10-sid";
                reg = <0x01c23800 0x10>
        };
 
diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
new file mode 100644 (file)
index 0000000..715d099
--- /dev/null
@@ -0,0 +1,61 @@
+Qualcomm SPMI Controller (PMIC Arbiter)
+
+The SPMI PMIC Arbiter is found on the Snapdragon 800 Series.  It is an SPMI
+controller with wrapping arbitration logic to allow for multiple on-chip
+devices to control a single SPMI master.
+
+The PMIC Arbiter can also act as an interrupt controller, providing interrupts
+to slave devices.
+
+See spmi.txt for the generic SPMI controller binding requirements for child
+nodes.
+
+See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
+generic interrupt controller binding documentation.
+
+Required properties:
+- compatible : should be "qcom,spmi-pmic-arb".
+- reg-names  : must contain:
+     "core" - core registers
+     "intr" - interrupt controller registers
+     "cnfg" - configuration registers
+- reg : address + size pairs describing the PMIC arb register sets; order must
+        correspond with the order of entries in reg-names
+- #address-cells : must be set to 2
+- #size-cells : must be set to 0
+- qcom,ee : indicates the active Execution Environment identifier (0-5)
+- qcom,channel : which of the PMIC Arb provided channels to use for accesses (0-5)
+- interrupts : interrupt list for the PMIC Arb controller, must contain a
+               single interrupt entry for the peripheral interrupt
+- interrupt-names : corresponding interrupt names for the interrupts
+                    listed in the 'interrupts' property, must contain:
+     "periph_irq" - summary interrupt for PMIC peripherals
+- interrupt-controller : boolean indicator that the PMIC arbiter is an interrupt controller
+- #interrupt-cells :  must be set to 4. Interrupts are specified as a 4-tuple:
+    cell 1: slave ID for the requested interrupt (0-15)
+    cell 2: peripheral ID for requested interrupt (0-255)
+    cell 3: the requested peripheral interrupt (0-7)
+    cell 4: interrupt flags indicating level-sense information, as defined in
+            dt-bindings/interrupt-controller/irq.h
+
+Example:
+
+       spmi {
+               compatible = "qcom,spmi-pmic-arb";
+               reg-names = "core", "intr", "cnfg";
+               reg = <0xfc4cf000 0x1000>,
+                     <0xfc4cb000 0x1000>,
+                     <0xfc4ca000 0x1000>;
+
+               interrupt-names = "periph_irq";
+               interrupts = <0 190 0>;
+
+               qcom,ee = <0>;
+               qcom,channel = <0>;
+
+               #address-cells = <2>;
+               #size-cells = <0>;
+
+               interrupt-controller;
+               #interrupt-cells = <4>;
+       };
diff --git a/Documentation/devicetree/bindings/spmi/spmi.txt b/Documentation/devicetree/bindings/spmi/spmi.txt
new file mode 100644 (file)
index 0000000..462a42f
--- /dev/null
@@ -0,0 +1,41 @@
+System Power Management Interface (SPMI) Controller
+
+This document defines a generic set of bindings for use by SPMI controllers.  A
+controller is modelled in device tree as a node with zero or more child nodes,
+each representing a unique slave on the bus.
+
+Required properties:
+- #address-cells : must be set to 2
+- #size-cells : must be set to 0
+
+Child nodes:
+
+An SPMI controller node can contain zero or more child nodes representing slave
+devices on the bus.  Child 'reg' properties are specified as an address, type
+pair.  The address must be in the range 0-15 (4 bits).  The type must be one of
+SPMI_USID (0) or SPMI_GSID (1) for Unique Slave ID or Group Slave ID respectively.
+These are the identifiers "statically assigned by the system integrator", as
+per the SPMI spec.
+
+Each child node must have one and only one 'reg' entry of type SPMI_USID.
+
+#include <dt-bindings/spmi/spmi.h>
+
+       spmi@.. {
+               compatible = "...";
+               reg = <...>;
+
+               #address-cells = <2>;
+               #size-cells <0>;
+
+               child@0 {
+                       compatible = "...";
+                       reg = <0 SPMI_USID>;
+               };
+
+               child@7 {
+                       compatible = "...";
+                       reg = <7 SPMI_USID
+                              3 SPMI_GSID>;
+               };
+       };
index 28176def3d6f3430c0b2c71c6be9d6b180914cf9..3e091151dd80251363b99746be61c41362ee5e0a 100644 (file)
@@ -21,8 +21,6 @@ Notes and limitations.
 - The weak pullup current is a minimum of 0.9mA and maximum of 6.0mA.
 - The 5V strong pullup is supported with a minimum of 5.9mA and a
   maximum of 30.4 mA.  (From DS2490.pdf)
-- While the ds2490 supports a hardware search the code doesn't take
-  advantage of it (in tested case it only returned first device).
 - The hardware will detect when devices are attached to the bus on the
   next bus (reset?) operation, however only a message is printed as
   the core w1 code doesn't make use of the information.  Connecting
index f59a31965d50a38221ecd305e039d0f6319a03c7..927a52cc05197d68a37bf9b1d05e77c48b321a43 100644 (file)
@@ -5,8 +5,8 @@ Message types.
 =============
 
 There are three types of messages between w1 core and userspace:
-1. Events. They are generated each time new master or slave device
-       found either due to automatic or requested search.
+1. Events. They are generated each time new master or slave device
+       is found either due to automatic or requested search.
 2. Userspace commands.
 3. Replies to userspace commands.
 
@@ -131,7 +131,7 @@ of the w1_netlink_cmd structure and cn_msg.len will be equal to the sum
 of the sizeof(struct w1_netlink_msg) and sizeof(struct w1_netlink_cmd).
 If reply is generated for master or root command (which do not have
 w1_netlink_cmd attached), reply will contain only cn_msg and w1_netlink_msg
-structires.
+structures.
 
 w1_netlink_msg.status field will carry positive error value
 (EINVAL for example) or zero in case of success.
@@ -160,7 +160,7 @@ procedure is started to select given device.
 Then all requested in w1_netlink_msg operations are performed one by one.
 If command requires reply (like read command) it is sent on command completion.
 
-When all commands (w1_netlink_cmd) are processed muster device is unlocked
+When all commands (w1_netlink_cmd) are processed master device is unlocked
 and next w1_netlink_msg header processing started.
 
 
index 10666ca8aee1c49cd35ef7373a0298df27816ae3..9044c5373beede0a08d6e4b681a100e917fb665c 100644 (file)
                };
 
                sid: eeprom@01c23800 {
-                       compatible = "allwinner,sun4i-sid";
+                       compatible = "allwinner,sun4i-a10-sid";
                        reg = <0x01c23800 0x10>;
                };
 
index 64961595e8d63cafaaaf2373ce6e5a580a7e6879..327e87bbca0db0c7dad34c7116985e110663ac58 100644 (file)
                };
 
                sid: eeprom@01c23800 {
-                       compatible = "allwinner,sun4i-sid";
+                       compatible = "allwinner,sun4i-a10-sid";
                        reg = <0x01c23800 0x10>;
                };
 
index 320335abfccd763118408f3f836c0fba39ca6f69..f49eb13c33c32b8fbe819355d1a77b082b755b14 100644 (file)
                };
 
                sid: eeprom@01c23800 {
-                       compatible = "allwinner,sun4i-sid";
+                       compatible = "allwinner,sun4i-a10-sid";
                        reg = <0x01c23800 0x10>;
                };
 
index b3138fbb46a4bc4ede53190263d2d8b54e310b42..e0a4ae61a2c44565a7a9cd556b79a03860dfa171 100644 (file)
@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
 
 source "drivers/spi/Kconfig"
 
+source "drivers/spmi/Kconfig"
+
 source "drivers/hsi/Kconfig"
 
 source "drivers/pps/Kconfig"
index 8e3b8b06c0b2e26a90308b9f15457bfa9da06b13..3d6de8b489c55263cdd3816a89f52468ce665d16 100644 (file)
@@ -66,6 +66,7 @@ obj-$(CONFIG_ATA)             += ata/
 obj-$(CONFIG_TARGET_CORE)      += target/
 obj-$(CONFIG_MTD)              += mtd/
 obj-$(CONFIG_SPI)              += spi/
+obj-$(CONFIG_SPMI)             += spmi/
 obj-y                          += hsi/
 obj-y                          += net/
 obj-$(CONFIG_ATM)              += atm/
index ac2391013db16f96fd76497c8743f69401c712f0..d7026dc33388aada6003886d14d8d73f264b56a9 100644 (file)
 #include <linux/module.h>
 #include <linux/init.h>
 
-static int regmap_spmi_read(void *context,
-                           const void *reg, size_t reg_size,
-                           void *val, size_t val_size)
+static int regmap_spmi_base_read(void *context,
+                                const void *reg, size_t reg_size,
+                                void *val, size_t val_size)
 {
+       u8 addr = *(u8 *)reg;
+       int err = 0;
+
+       BUG_ON(reg_size != 1);
+
+       while (val_size-- && !err)
+               err = spmi_register_read(context, addr++, val++);
+
+       return err;
+}
+
+static int regmap_spmi_base_gather_write(void *context,
+                                        const void *reg, size_t reg_size,
+                                        const void *val, size_t val_size)
+{
+       const u8 *data = val;
+       u8 addr = *(u8 *)reg;
+       int err = 0;
+
+       BUG_ON(reg_size != 1);
+
+       /*
+        * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence,
+        * use it when possible.
+        */
+       if (addr == 0 && val_size) {
+               err = spmi_register_zero_write(context, *data);
+               if (err)
+                       goto err_out;
+
+               data++;
+               addr++;
+               val_size--;
+       }
+
+       while (val_size) {
+               err = spmi_register_write(context, addr, *data);
+               if (err)
+                       goto err_out;
+
+               data++;
+               addr++;
+               val_size--;
+       }
+
+err_out:
+       return err;
+}
+
+static int regmap_spmi_base_write(void *context, const void *data,
+                                 size_t count)
+{
+       BUG_ON(count < 1);
+       return regmap_spmi_base_gather_write(context, data, 1, data + 1,
+                                            count - 1);
+}
+
+static struct regmap_bus regmap_spmi_base = {
+       .read                           = regmap_spmi_base_read,
+       .write                          = regmap_spmi_base_write,
+       .gather_write                   = regmap_spmi_base_gather_write,
+       .reg_format_endian_default      = REGMAP_ENDIAN_NATIVE,
+       .val_format_endian_default      = REGMAP_ENDIAN_NATIVE,
+};
+
+/**
+ * regmap_init_spmi_base(): Create regmap for the Base register space
+ * @sdev:      SPMI device that will be interacted with
+ * @config:    Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_spmi_base(struct spmi_device *sdev,
+                                    const struct regmap_config *config)
+{
+       return regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_spmi_base);
+
+/**
+ * devm_regmap_init_spmi_base(): Create managed regmap for Base register space
+ * @sdev:      SPMI device that will be interacted with
+ * @config:    Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.  The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_spmi_base(struct spmi_device *sdev,
+                                         const struct regmap_config *config)
+{
+       return devm_regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_base);
+
+static int regmap_spmi_ext_read(void *context,
+                               const void *reg, size_t reg_size,
+                               void *val, size_t val_size)
+{
+       int err = 0;
+       size_t len;
+       u16 addr;
+
        BUG_ON(reg_size != 2);
-       return spmi_ext_register_readl(context, *(u16 *)reg,
-                                      val, val_size);
+
+       addr = *(u16 *)reg;
+
+       /*
+        * Split accesses into two to take advantage of the more
+        * bandwidth-efficient 'Extended Register Read' command when possible
+        */
+       while (addr <= 0xFF && val_size) {
+               len = min_t(size_t, val_size, 16);
+
+               err = spmi_ext_register_read(context, addr, val, len);
+               if (err)
+                       goto err_out;
+
+               addr += len;
+               val += len;
+               val_size -= len;
+       }
+
+       while (val_size) {
+               len = min_t(size_t, val_size, 8);
+
+               err = spmi_ext_register_readl(context, addr, val, val_size);
+               if (err)
+                       goto err_out;
+
+               addr += len;
+               val += len;
+               val_size -= len;
+       }
+
+err_out:
+       return err;
 }
 
-static int regmap_spmi_gather_write(void *context,
-                                   const void *reg, size_t reg_size,
-                                   const void *val, size_t val_size)
+static int regmap_spmi_ext_gather_write(void *context,
+                                       const void *reg, size_t reg_size,
+                                       const void *val, size_t val_size)
 {
+       int err = 0;
+       size_t len;
+       u16 addr;
+
        BUG_ON(reg_size != 2);
-       return spmi_ext_register_writel(context, *(u16 *)reg, val, val_size);
+
+       addr = *(u16 *)reg;
+
+       while (addr <= 0xFF && val_size) {
+               len = min_t(size_t, val_size, 16);
+
+               err = spmi_ext_register_write(context, addr, val, len);
+               if (err)
+                       goto err_out;
+
+               addr += len;
+               val += len;
+               val_size -= len;
+       }
+
+       while (val_size) {
+               len = min_t(size_t, val_size, 8);
+
+               err = spmi_ext_register_writel(context, addr, val, len);
+               if (err)
+                       goto err_out;
+
+               addr += len;
+               val += len;
+               val_size -= len;
+       }
+
+err_out:
+       return err;
 }
 
-static int regmap_spmi_write(void *context, const void *data,
-                            size_t count)
+static int regmap_spmi_ext_write(void *context, const void *data,
+                                size_t count)
 {
        BUG_ON(count < 2);
-       return regmap_spmi_gather_write(context, data, 2, data + 2, count - 2);
+       return regmap_spmi_ext_gather_write(context, data, 2, data + 2,
+                                           count - 2);
 }
 
-static struct regmap_bus regmap_spmi = {
-       .read                           = regmap_spmi_read,
-       .write                          = regmap_spmi_write,
-       .gather_write                   = regmap_spmi_gather_write,
+static struct regmap_bus regmap_spmi_ext = {
+       .read                           = regmap_spmi_ext_read,
+       .write                          = regmap_spmi_ext_write,
+       .gather_write                   = regmap_spmi_ext_gather_write,
        .reg_format_endian_default      = REGMAP_ENDIAN_NATIVE,
        .val_format_endian_default      = REGMAP_ENDIAN_NATIVE,
 };
 
 /**
- * regmap_init_spmi(): Initialize register map
- *
- * @sdev: Device that will be interacted with
- * @config: Configuration for register map
+ * regmap_init_spmi_ext(): Create regmap for Ext register space
+ * @sdev:      Device that will be interacted with
+ * @config:    Configuration for register map
  *
  * The return value will be an ERR_PTR() on error or a valid pointer to
  * a struct regmap.
  */
-struct regmap *regmap_init_spmi(struct spmi_device *sdev,
-                               const struct regmap_config *config)
+struct regmap *regmap_init_spmi_ext(struct spmi_device *sdev,
+                                   const struct regmap_config *config)
 {
-       return regmap_init(&sdev->dev, &regmap_spmi, sdev, config);
+       return regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config);
 }
-EXPORT_SYMBOL_GPL(regmap_init_spmi);
+EXPORT_SYMBOL_GPL(regmap_init_spmi_ext);
 
 /**
- * devm_regmap_init_spmi(): Initialise managed register map
- *
- * @sdev: Device that will be interacted with
- * @config: Configuration for register map
+ * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space
+ * @sdev:      SPMI device that will be interacted with
+ * @config:    Configuration for register map
  *
  * The return value will be an ERR_PTR() on error or a valid pointer
  * to a struct regmap.  The regmap will be automatically freed by the
  * device management code.
  */
-struct regmap *devm_regmap_init_spmi(struct spmi_device *sdev,
+struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *sdev,
                                     const struct regmap_config *config)
 {
-       return devm_regmap_init(&sdev->dev, &regmap_spmi, sdev, config);
+       return devm_regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config);
 }
-EXPORT_SYMBOL_GPL(devm_regmap_init_spmi);
+EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_ext);
 
 MODULE_LICENSE("GPL");
index 1b192395a90c840d48362fdf81a139a75243e08f..8121b4c70edec77114e20295e195fa1d5f2cc797 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/module.h>
 #include <linux/mman.h>
 #include <linux/pci.h>
-#include <linux/init.h>
 #include <linux/miscdevice.h>
 #include <linux/agp_backend.h>
 #include <linux/agpgart.h>
index f39437addb58ce2dde481f2ecdc0008024e35df4..0fbccce1cee92920106de28ff3dc2accd97d76b3 100644 (file)
@@ -29,7 +29,6 @@
  */
 #include <linux/module.h>
 #include <linux/pci.h>
-#include <linux/init.h>
 #include <linux/pagemap.h>
 #include <linux/miscdevice.h>
 #include <linux/pm.h>
index 5c85350f4c3d065e9c9bdf9ba762667604b7c0f3..9a024f899dd40a040ccede1571e1e2919e61bea9 100644 (file)
@@ -17,7 +17,6 @@
 
 #include <linux/module.h>
 #include <linux/pci.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/pagemap.h>
 #include <linux/agp_backend.h>
index 05b8d0241bdefe2df440bf7637f37efe7ee367df..3051c73bc3838a0ccdbc0784d7a5bac76d356cd2 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <linux/agp_backend.h>
 #include <asm/sn/addrs.h>
 #include <asm/sn/io.h>
index 43577ca780e304e7c6d25d919da70927bb8ffeb5..8c3b255e629a8253f040175c968905f10a064716 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include <linux/hw_random.h>
-#include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
index a0f7724852eb84d2ccb20eebc8e4309ea48a085c..b9495a8c05c6d7e9b192c8a2b403b7fb2ac90fdc 100644 (file)
@@ -37,7 +37,6 @@
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
-#include <linux/init.h>
 #include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
index 402ccfb625c516739b440f9a5fba76ff2c98e4d0..9f8277cc44b49639332f76b4f2c47da6db85fdd8 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/hw_random.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
index f9beed54d0c87fe2908b7c7d84bba111ee580f0b..432232eefe057da718751d9b6721ca9a0e657aaf 100644 (file)
@@ -7,7 +7,6 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/delay.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 #include <linux/preempt.h>
index 232b87fb5fc911d8697b4fbddd04063614dffd07..00e9d2d466343784a46abae1bbdbb5623d6d5037 100644 (file)
@@ -10,7 +10,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/device.h>
 #include <linux/amba/bus.h>
 #include <linux/hw_random.h>
index f2885dbe1849e03d0691a7cc51a6e85e646a7c0f..b5cc3420c659442a7ff77d748b23e5cbf0d814df 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/device.h>
 #include <linux/hw_random.h>
index 03f41896d09050ff391ad55ef263faa1430de19a..b7efd3c1a882525431da787f1679e33729243f82 100644 (file)
@@ -61,7 +61,6 @@
 #include <linux/ipmi_smi.h>
 #include <asm/io.h>
 #include "ipmi_si_sm.h"
-#include <linux/init.h>
 #include <linux/dmi.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
index 92c5937f80c33acac907dccb2377b6157721b33c..917403fe10da3eb62c0548bb29a2f0d898c8400a 100644 (file)
@@ -99,6 +99,9 @@ static ssize_t read_mem(struct file *file, char __user *buf,
        ssize_t read, sz;
        char *ptr;
 
+       if (p != *ppos)
+               return 0;
+
        if (!valid_phys_addr_range(p, count))
                return -EFAULT;
        read = 0;
@@ -157,6 +160,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
        unsigned long copied;
        void *ptr;
 
+       if (p != *ppos)
+               return -EFBIG;
+
        if (!valid_phys_addr_range(p, count))
                return -EFAULT;
 
index 881c9e59593912261bd68a39b0c08cfbcd4b793d..28740046bc83752ffbe3ba47b3ec1a540a8bb648 100644 (file)
@@ -50,7 +50,6 @@
 #include <linux/unistd.h>
 #include <linux/delay.h>
 #include <linux/ioport.h>
-#include <linux/init.h>
 #include <linux/bitops.h>
 #include <linux/sched.h>       /* cond_resched() */
 
index 0e506bad19866d78f1952bc26666eabc7725a046..bd377472dcfb33277f20fdc382a9a717dad92823 100644 (file)
@@ -20,7 +20,6 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/init.h>
 #include <linux/kernel.h>      /* printk() */
 #include <linux/slab.h>                /* kmalloc() */
 #include <linux/fs.h>          /* everything... */
index 52b9b2b2f30045d94be453785df66e54b0cc6e0b..472af4bb1b6128d3f7a0176fcb51720115f11aec 100644 (file)
@@ -21,7 +21,6 @@
  *
  *
  */
-#include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/wait.h>
index 5b0dd8ef74c0eb896cba51c275eff71d797ad888..3b7bf216289858ece2f8aff141b9d77ed023fde2 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/miscdevice.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
-#include <linux/init.h>
 #include <linux/wait.h>
 #include <linux/string.h>
 #include <linux/interrupt.h>
index 18c5b9b16645dfa49a218d4b12253240f97310b7..148d707a1d439375ef36bf2ac8e6655fe2a61042 100644 (file)
@@ -95,7 +95,7 @@ void proc_fork_connector(struct task_struct *task)
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
        /*  If cn_netlink_send() failed, the data is not sent */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 void proc_exec_connector(struct task_struct *task)
@@ -122,7 +122,7 @@ void proc_exec_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 void proc_id_connector(struct task_struct *task, int which_id)
@@ -163,7 +163,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 void proc_sid_connector(struct task_struct *task)
@@ -190,7 +190,7 @@ void proc_sid_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
@@ -225,7 +225,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 void proc_comm_connector(struct task_struct *task)
@@ -253,7 +253,7 @@ void proc_comm_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 void proc_coredump_connector(struct task_struct *task)
@@ -280,7 +280,7 @@ void proc_coredump_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 void proc_exit_connector(struct task_struct *task)
@@ -309,7 +309,7 @@ void proc_exit_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 /*
@@ -343,7 +343,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
        msg->ack = rcvd_ack + 1;
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
 }
 
 /**
index a36749f1e44a869418e1bcab6481334948618391..77afe7487d34e4f94b40dd7727571827069b2236 100644 (file)
@@ -50,7 +50,7 @@ static int cn_already_initialized;
  *
  * Sequence number is incremented with each message to be sent.
  *
- * If we expect reply to our message then the sequence number in
+ * If we expect reply to our message then the sequence number in
  * received message MUST be the same as in original message, and
  * acknowledge number MUST be the same + 1.
  *
@@ -62,8 +62,11 @@ static int cn_already_initialized;
  * the acknowledgement number in the original message + 1, then it is
  * a new message.
  *
+ * The message is sent to, the portid if given, the group if given, both if
+ * both, or if both are zero then the group is looked up and sent there.
  */
-int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
+int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
+       gfp_t gfp_mask)
 {
        struct cn_callback_entry *__cbq;
        unsigned int size;
@@ -74,7 +77,9 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
        u32 group = 0;
        int found = 0;
 
-       if (!__group) {
+       if (portid || __group) {
+               group = __group;
+       } else {
                spin_lock_bh(&dev->cbdev->queue_lock);
                list_for_each_entry(__cbq, &dev->cbdev->queue_list,
                                    callback_entry) {
@@ -88,11 +93,9 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
 
                if (!found)
                        return -ENODEV;
-       } else {
-               group = __group;
        }
 
-       if (!netlink_has_listeners(dev->nls, group))
+       if (!portid && !netlink_has_listeners(dev->nls, group))
                return -ESRCH;
 
        size = sizeof(*msg) + msg->len;
@@ -113,7 +116,10 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
 
        NETLINK_CB(skb).dst_group = group;
 
-       return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask);
+       if (group)
+               return netlink_broadcast(dev->nls, skb, portid, group,
+                                        gfp_mask);
+       return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT));
 }
 EXPORT_SYMBOL_GPL(cn_netlink_send);
 
index 24d52497524dce7b8935cb678c6084d39c6f2510..5a5e616c6324069bc79408abcfc60e33f0688963 100644 (file)
@@ -154,7 +154,7 @@ int fmc_device_register_n(struct fmc_device **devs, int n)
                        ret = -EINVAL;
                        break;
                }
-               if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) {
+               if (fmc->flags & FMC_DEVICE_NO_MEZZANINE) {
                        dev_info(fmc->hwdev, "absent mezzanine in slot %d\n",
                                 fmc->slot_id);
                        continue;
@@ -189,9 +189,6 @@ int fmc_device_register_n(struct fmc_device **devs, int n)
        for (i = 0; i < n; i++) {
                fmc = devarray[i];
 
-               if (fmc->flags == FMC_DEVICE_NO_MEZZANINE)
-                       continue; /* dev_info already done above */
-
                fmc->nr_slots = n; /* each slot must know how many are there */
                fmc->devarray = devarray;
 
@@ -263,8 +260,6 @@ void fmc_device_unregister_n(struct fmc_device **devs, int n)
        kfree(devs[0]->devarray);
 
        for (i = 0; i < n; i++) {
-               if (devs[i]->flags == FMC_DEVICE_NO_MEZZANINE)
-                       continue;
                sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
                device_del(&devs[i]->dev);
                fmc_free_id_info(devs[i]);
index 79adc39221ea745f1a403a9eb6b1434f9cb20618..69f42d70bc74f9ac29fedd74d3ad3ba62c8cc612 100644 (file)
@@ -153,20 +153,17 @@ EXPORT_SYMBOL(fmc_reprogram);
 static void __fmc_show_sdb_tree(const struct fmc_device *fmc,
                                const struct sdb_array *arr)
 {
+       unsigned long base = arr->baseaddr;
        int i, j, n = arr->len, level = arr->level;
-       const struct sdb_array *ap;
 
        for (i = 0; i < n; i++) {
-               unsigned long base;
                union  sdb_record *r;
                struct sdb_product *p;
                struct sdb_component *c;
                r = &arr->record[i];
                c = &r->dev.sdb_component;
                p = &c->product;
-               base = 0;
-               for (ap = arr; ap; ap = ap->parent)
-                       base += ap->baseaddr;
+
                dev_info(&fmc->dev, "SDB: ");
 
                for (j = 0; j < level; j++)
index 69ea36f07b4d6b47c2030fe9c82bb8bb3fe866d0..602ca86a6488d6d90e4927d79ed99ca6c5df42b8 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/hyperv.h>
+#include <linux/uio.h>
 
 #include "hyperv_vmbus.h"
 
@@ -554,14 +555,14 @@ EXPORT_SYMBOL_GPL(vmbus_close);
  *
  * Mainly used by Hyper-V drivers.
  */
-int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer,
+int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
                           u32 bufferlen, u64 requestid,
                           enum vmbus_packet_type type, u32 flags)
 {
        struct vmpacket_descriptor desc;
        u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen;
        u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64));
-       struct scatterlist bufferlist[3];
+       struct kvec bufferlist[3];
        u64 aligned_data = 0;
        int ret;
        bool signal = false;
@@ -575,11 +576,12 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer,
        desc.len8 = (u16)(packetlen_aligned >> 3);
        desc.trans_id = requestid;
 
-       sg_init_table(bufferlist, 3);
-       sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor));
-       sg_set_buf(&bufferlist[1], buffer, bufferlen);
-       sg_set_buf(&bufferlist[2], &aligned_data,
-                  packetlen_aligned - packetlen);
+       bufferlist[0].iov_base = &desc;
+       bufferlist[0].iov_len = sizeof(struct vmpacket_descriptor);
+       bufferlist[1].iov_base = buffer;
+       bufferlist[1].iov_len = bufferlen;
+       bufferlist[2].iov_base = &aligned_data;
+       bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
 
@@ -605,7 +607,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
        u32 descsize;
        u32 packetlen;
        u32 packetlen_aligned;
-       struct scatterlist bufferlist[3];
+       struct kvec bufferlist[3];
        u64 aligned_data = 0;
        bool signal = false;
 
@@ -637,11 +639,12 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
                desc.range[i].pfn        = pagebuffers[i].pfn;
        }
 
-       sg_init_table(bufferlist, 3);
-       sg_set_buf(&bufferlist[0], &desc, descsize);
-       sg_set_buf(&bufferlist[1], buffer, bufferlen);
-       sg_set_buf(&bufferlist[2], &aligned_data,
-               packetlen_aligned - packetlen);
+       bufferlist[0].iov_base = &desc;
+       bufferlist[0].iov_len = descsize;
+       bufferlist[1].iov_base = buffer;
+       bufferlist[1].iov_len = bufferlen;
+       bufferlist[2].iov_base = &aligned_data;
+       bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
 
@@ -665,7 +668,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
        u32 descsize;
        u32 packetlen;
        u32 packetlen_aligned;
-       struct scatterlist bufferlist[3];
+       struct kvec bufferlist[3];
        u64 aligned_data = 0;
        bool signal = false;
        u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
@@ -700,11 +703,12 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
        memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array,
               pfncount * sizeof(u64));
 
-       sg_init_table(bufferlist, 3);
-       sg_set_buf(&bufferlist[0], &desc, descsize);
-       sg_set_buf(&bufferlist[1], buffer, bufferlen);
-       sg_set_buf(&bufferlist[2], &aligned_data,
-               packetlen_aligned - packetlen);
+       bufferlist[0].iov_base = &desc;
+       bufferlist[0].iov_len = descsize;
+       bufferlist[1].iov_base = buffer;
+       bufferlist[1].iov_len = bufferlen;
+       bufferlist[2].iov_base = &aligned_data;
+       bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
 
index 7e17a5495e029cc02b127f4c0cb997b43aa09dfe..7e6d78dc9437def19b24ac588ed6171a469aed51 100644 (file)
@@ -1171,7 +1171,8 @@ static int dm_thread_func(void *dm_dev)
        int t;
 
        while (!kthread_should_stop()) {
-               t = wait_for_completion_timeout(&dm_device.config_event, 1*HZ);
+               t = wait_for_completion_interruptible_timeout(
+                                               &dm_device.config_event, 1*HZ);
                /*
                 * The host expects us to post information on the memory
                 * pressure every second.
index 09988b2896226552e4be5cd69d5f09ba3f842d11..ea852537307e8596f5a624ac5f5c44b11286360b 100644 (file)
@@ -113,7 +113,7 @@ kvp_register(int reg_value)
                kvp_msg->kvp_hdr.operation = reg_value;
                strcpy(version, HV_DRV_VERSION);
                msg->len = sizeof(struct hv_kvp_msg);
-               cn_netlink_send(msg, 0, GFP_ATOMIC);
+               cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
                kfree(msg);
        }
 }
@@ -435,7 +435,7 @@ kvp_send_key(struct work_struct *dummy)
        }
 
        msg->len = sizeof(struct hv_kvp_msg);
-       cn_netlink_send(msg, 0, GFP_ATOMIC);
+       cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
        kfree(msg);
 
        return;
index 0c354622437681d1b6dd157f718bb27cf8dbdbed..34f14fddb666bb753c92e3ea955264a862ef8976 100644 (file)
@@ -98,7 +98,7 @@ static void vss_send_op(struct work_struct *dummy)
        vss_msg->vss_hdr.operation = op;
        msg->len = sizeof(struct hv_vss_msg);
 
-       cn_netlink_send(msg, 0, GFP_ATOMIC);
+       cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
        kfree(msg);
 
        return;
index e05517616a06e549407f422a4713145b0f44e1de..1544609881feb30a403569c573259858d771b8da 100644 (file)
@@ -559,8 +559,8 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer,
 void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
 
 int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
-                   struct scatterlist *sglist,
-                   u32 sgcount, bool *signal);
+                   struct kvec *kv_list,
+                   u32 kv_count, bool *signal);
 
 int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer,
                   u32 buflen);
index 26c93cf9f6be4a083f47615318699e3110d39983..15db66b74141cb6c92a180197c4261c77a2eeaa3 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/hyperv.h>
+#include <linux/uio.h>
 
 #include "hyperv_vmbus.h"
 
@@ -387,23 +388,20 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
  *
  */
 int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
-                   struct scatterlist *sglist, u32 sgcount, bool *signal)
+                   struct kvec *kv_list, u32 kv_count, bool *signal)
 {
        int i = 0;
        u32 bytes_avail_towrite;
        u32 bytes_avail_toread;
        u32 totalbytes_towrite = 0;
 
-       struct scatterlist *sg;
        u32 next_write_location;
        u32 old_write;
        u64 prev_indices = 0;
        unsigned long flags;
 
-       for_each_sg(sglist, sg, sgcount, i)
-       {
-               totalbytes_towrite += sg->length;
-       }
+       for (i = 0; i < kv_count; i++)
+               totalbytes_towrite += kv_list[i].iov_len;
 
        totalbytes_towrite += sizeof(u64);
 
@@ -427,12 +425,11 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
 
        old_write = next_write_location;
 
-       for_each_sg(sglist, sg, sgcount, i)
-       {
+       for (i = 0; i < kv_count; i++) {
                next_write_location = hv_copyto_ringbuffer(outring_info,
                                                     next_write_location,
-                                                    sg_virt(sg),
-                                                    sg->length);
+                                                    kv_list[i].iov_base,
+                                                    kv_list[i].iov_len);
        }
 
        /* Set previous packet start */
index 077bb1bdac34ef4ed87c65c7bf0d204fd42001b4..b37c91b6ba8038f3d752b08da391ac3656a98c36 100644 (file)
@@ -43,6 +43,10 @@ static struct acpi_device  *hv_acpi_dev;
 static struct tasklet_struct msg_dpc;
 static struct completion probe_event;
 static int irq;
+u64 hyperv_mmio_start;
+EXPORT_SYMBOL_GPL(hyperv_mmio_start);
+u64 hyperv_mmio_size;
+EXPORT_SYMBOL_GPL(hyperv_mmio_size);
 
 static int vmbus_exists(void)
 {
@@ -886,18 +890,19 @@ void vmbus_device_unregister(struct hv_device *device_obj)
 
 
 /*
- * VMBUS is an acpi enumerated device. Get the the IRQ information
- * from DSDT.
+ * VMBUS is an acpi enumerated device. Get the the information we
+ * need from DSDT.
  */
 
-static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
+static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
 {
+       switch (res->type) {
+       case ACPI_RESOURCE_TYPE_IRQ:
+               irq = res->data.irq.interrupts[0];
 
-       if (res->type == ACPI_RESOURCE_TYPE_IRQ) {
-               struct acpi_resource_irq *irqp;
-               irqp = &res->data.irq;
-
-               *((unsigned int *)irq) = irqp->interrupts[0];
+       case ACPI_RESOURCE_TYPE_ADDRESS64:
+               hyperv_mmio_start = res->data.address64.minimum;
+               hyperv_mmio_size = res->data.address64.address_length;
        }
 
        return AE_OK;
@@ -906,18 +911,32 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
 static int vmbus_acpi_add(struct acpi_device *device)
 {
        acpi_status result;
+       int ret_val = -ENODEV;
 
        hv_acpi_dev = device;
 
        result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
-                                       vmbus_walk_resources, &irq);
+                                       vmbus_walk_resources, NULL);
 
-       if (ACPI_FAILURE(result)) {
-               complete(&probe_event);
-               return -ENODEV;
+       if (ACPI_FAILURE(result))
+               goto acpi_walk_err;
+       /*
+        * The parent of the vmbus acpi device (Gen2 firmware) is the VMOD that
+        * has the mmio ranges. Get that.
+        */
+       if (device->parent) {
+               result = acpi_walk_resources(device->parent->handle,
+                                       METHOD_NAME__CRS,
+                                       vmbus_walk_resources, NULL);
+
+               if (ACPI_FAILURE(result))
+                       goto acpi_walk_err;
        }
+       ret_val = 0;
+
+acpi_walk_err:
        complete(&probe_event);
-       return 0;
+       return ret_val;
 }
 
 static const struct acpi_device_id vmbus_acpi_device_ids[] = {
index 08d9a207259afdd6fc5ef92afeb403357c1e8a1c..b428c0ae63d5d1e3c173cd0afe9b04072f786415 100644 (file)
@@ -66,7 +66,7 @@ static int dm_ulog_sendto_server(struct dm_ulog_request *tfr)
        msg->seq = tfr->seq;
        msg->len = sizeof(struct dm_ulog_request) + tfr->data_size;
 
-       r = cn_netlink_send(msg, 0, gfp_any());
+       r = cn_netlink_send(msg, 0, 0, gfp_any());
 
        return r;
 }
index d3eee113baeba58a1bf2d9aae893c1e8ceed75b2..a43053daad0ec6698e39475f02499b955a12f66a 100644 (file)
@@ -72,7 +72,6 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 
index 0c6e037153d2231a3ff3859babcd5b8958e3ce7f..c6cc3dc8ae1fcfb55e3fc7f15873af1c635c5bbe 100644 (file)
@@ -22,7 +22,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/err.h>
index 820e53d0048fa16e248ee41ce5b89987935fa9ac..9b313f7810f5207d02ceb63fe864b7eba1b9637d 100644 (file)
@@ -47,7 +47,6 @@
 
 #include <linux/module.h>
 #include <linux/device.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/of.h>
 #include "bmp085.h"
index 9e2b985293fc08cf30bef9a9455fcade1eb095cb..14d90eae605bcea74c7d5f1d02b5700de93957dc 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/poll.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/kref.h>
 #include <linux/io.h>
index 154b02e5094f3202a528656045f6d1ac210fc883..6a672f9ef522c7fe2a477f3fcc7e1001abf5996d 100644 (file)
@@ -32,7 +32,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/string.h>
 #include <linux/list.h>
index 4f3bca1003a175d2eb0ea7fdf423cd8dfa70e14d..634f72929e123c342ba6ac812f259048df420e2f 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
index f0fa4e8ca124a905844f8f30d705be967d467965..33f8673d23a6d1307c8e4ae800f857eca4bfa7bf 100644 (file)
@@ -17,7 +17,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/jiffies.h>
index 78e55b501c94816a51b6ed7186c8473815b13689..9ebeacdb8ec4a06381974c8e3137cebe7d5fe41d 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
index e36157d5d3abf0c9d8ed388cad08a2dcbf6e74d8..580ff9df55296ccbb29e18561a98d7756e9c60b6 100644 (file)
@@ -27,7 +27,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
index 9c34e5704304f4e40dd33b4293347e6a2d6afa4f..3f2b625b203291c0724b85cfda06b1db9f804158 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/err.h>
 #include <linux/export.h>
 #include <linux/fs.h>
-#include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/kobject.h>
@@ -96,7 +95,7 @@ static int sunxi_sid_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id sunxi_sid_of_match[] = {
-       { .compatible = "allwinner,sun4i-sid", .data = (void *)16},
+       { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},
        { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
        {/* sentinel */},
 };
index 3bfdc07a7248b9654b8e1854aef2e81ff8e96640..50d2096ea1c74d0e86ce9e7fe637f046a2d01a59 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
index 170bd3daf33699598a794e4a58a9df19a917c279..90520d76633f4c2e92a07d3fdf9150e5b022fa6c 100644 (file)
@@ -22,7 +22,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/err.h>
index e3183f26216b876cee627c6a980bd1ed6ee35ec5..12c30b486b27c01ab8a741cbb743cbb8dbd58cbc 100644 (file)
@@ -26,7 +26,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/mutex.h>
index b7f84dacf822f40641858a329175235e2bfe9546..4a9c50a43afb864ebf76e2ea72e9809e783269a1 100644 (file)
@@ -23,7 +23,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/err.h>
index 61fbe6acabef87521a6e0fc02e07748dea49492f..0a1565e63c711eaf10f13c78ffa6c97a5ace9574 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/spi/spi.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
index 036effe9a795ab62338be47c306b7f98aa36e0d7..3ef4627f9cb1552a7585787f6da81cb880c4b784 100644 (file)
@@ -23,7 +23,6 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/dmi.h>
 #include <linux/module.h>
 #include <linux/types.h>
index 7c97550240f19db127c56b1eb1322f0fe8de803d..d324f8a97b88d7e981a7c5ea680457ec8f0d9ed0 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/pm_runtime.h>
index 9aa2bd2a71ae4fc268f4831d2c9622e890f48b54..bd06d0cfac45e15a2f1ee21f1e4ab856867e0e98 100644 (file)
@@ -10,7 +10,6 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/err.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
index 49c7a23f02fc554f3232295ed7a80b90b96bdc16..d66a2f24f6b3dceffbcc3335d502e858520558e2 100644 (file)
@@ -30,6 +30,7 @@
  *
  * See Documentation/fault-injection/provoke-crashes.txt for instructions
  */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
 #include <linux/fs.h>
@@ -45,6 +46,7 @@
 #include <linux/debugfs.h>
 #include <linux/vmalloc.h>
 #include <linux/mman.h>
+#include <asm/cacheflush.h>
 
 #ifdef CONFIG_IDE
 #include <linux/ide.h>
@@ -101,6 +103,7 @@ enum ctype {
        CT_EXEC_USERSPACE,
        CT_ACCESS_USERSPACE,
        CT_WRITE_RO,
+       CT_WRITE_KERN,
 };
 
 static char* cp_name[] = {
@@ -137,6 +140,7 @@ static char* cp_type[] = {
        "EXEC_USERSPACE",
        "ACCESS_USERSPACE",
        "WRITE_RO",
+       "WRITE_KERN",
 };
 
 static struct jprobe lkdtm;
@@ -316,6 +320,13 @@ static void do_nothing(void)
        return;
 }
 
+/* Must immediately follow do_nothing for size calculuations to work out. */
+static void do_overwritten(void)
+{
+       pr_info("do_overwritten wasn't overwritten!\n");
+       return;
+}
+
 static noinline void corrupt_stack(void)
 {
        /* Use default char array length that triggers stack protection. */
@@ -328,7 +339,12 @@ static void execute_location(void *dst)
 {
        void (*func)(void) = dst;
 
+       pr_info("attempting ok execution at %p\n", do_nothing);
+       do_nothing();
+
        memcpy(dst, do_nothing, EXEC_SIZE);
+       flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
+       pr_info("attempting bad execution at %p\n", func);
        func();
 }
 
@@ -337,8 +353,13 @@ static void execute_user_location(void *dst)
        /* Intentionally crossing kernel/user memory boundary. */
        void (*func)(void) = dst;
 
+       pr_info("attempting ok execution at %p\n", do_nothing);
+       do_nothing();
+
        if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE))
                return;
+       flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
+       pr_info("attempting bad execution at %p\n", func);
        func();
 }
 
@@ -463,8 +484,12 @@ static void lkdtm_do_action(enum ctype which)
                }
 
                ptr = (unsigned long *)user_addr;
+
+               pr_info("attempting bad read at %p\n", ptr);
                tmp = *ptr;
                tmp += 0xc0dec0de;
+
+               pr_info("attempting bad write at %p\n", ptr);
                *ptr = tmp;
 
                vm_munmap(user_addr, PAGE_SIZE);
@@ -475,10 +500,28 @@ static void lkdtm_do_action(enum ctype which)
                unsigned long *ptr;
 
                ptr = (unsigned long *)&rodata;
+
+               pr_info("attempting bad write at %p\n", ptr);
                *ptr ^= 0xabcd1234;
 
                break;
        }
+       case CT_WRITE_KERN: {
+               size_t size;
+               unsigned char *ptr;
+
+               size = (unsigned long)do_overwritten -
+                      (unsigned long)do_nothing;
+               ptr = (unsigned char *)do_overwritten;
+
+               pr_info("attempting bad %zu byte write at %p\n", size, ptr);
+               memcpy(ptr, (unsigned char *)do_nothing, size);
+               flush_icache_range((unsigned long)ptr,
+                                  (unsigned long)(ptr + size));
+
+               do_overwritten();
+               break;
+       }
        case CT_NONE:
        default:
                break;
@@ -493,8 +536,8 @@ static void lkdtm_handler(void)
 
        spin_lock_irqsave(&count_lock, flags);
        count--;
-       printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n",
-                       cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
+       pr_info("Crash point %s of type %s hit, trigger in %d rounds\n",
+               cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
 
        if (count == 0) {
                do_it = true;
@@ -551,18 +594,18 @@ static int lkdtm_register_cpoint(enum cname which)
                lkdtm.kp.symbol_name = "generic_ide_ioctl";
                lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
 #else
-               printk(KERN_INFO "lkdtm: Crash point not available\n");
+               pr_info("Crash point not available\n");
                return -EINVAL;
 #endif
                break;
        default:
-               printk(KERN_INFO "lkdtm: Invalid Crash Point\n");
+               pr_info("Invalid Crash Point\n");
                return -EINVAL;
        }
 
        cpoint = which;
        if ((ret = register_jprobe(&lkdtm)) < 0) {
-               printk(KERN_INFO "lkdtm: Couldn't register jprobe\n");
+               pr_info("Couldn't register jprobe\n");
                cpoint = CN_INVALID;
        }
 
@@ -709,8 +752,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
        if (type == CT_NONE)
                return -EINVAL;
 
-       printk(KERN_INFO "lkdtm: Performing direct entry %s\n",
-                       cp_type_to_str(type));
+       pr_info("Performing direct entry %s\n", cp_type_to_str(type));
        lkdtm_do_action(type);
        *off += count;
 
@@ -772,7 +814,7 @@ static int __init lkdtm_module_init(void)
        /* Register debugfs interface */
        lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
        if (!lkdtm_debugfs_root) {
-               printk(KERN_ERR "lkdtm: creating root dir failed\n");
+               pr_err("creating root dir failed\n");
                return -ENODEV;
        }
 
@@ -787,28 +829,26 @@ static int __init lkdtm_module_init(void)
                de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
                                NULL, &cur->fops);
                if (de == NULL) {
-                       printk(KERN_ERR "lkdtm: could not create %s\n",
-                                       cur->name);
+                       pr_err("could not create %s\n", cur->name);
                        goto out_err;
                }
        }
 
        if (lkdtm_parse_commandline() == -EINVAL) {
-               printk(KERN_INFO "lkdtm: Invalid command\n");
+               pr_info("Invalid command\n");
                goto out_err;
        }
 
        if (cpoint != CN_INVALID && cptype != CT_NONE) {
                ret = lkdtm_register_cpoint(cpoint);
                if (ret < 0) {
-                       printk(KERN_INFO "lkdtm: Invalid crash point %d\n",
-                                       cpoint);
+                       pr_info("Invalid crash point %d\n", cpoint);
                        goto out_err;
                }
-               printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n",
-                               cpoint_name, cpoint_type);
+               pr_info("Crash point %s of type %s registered\n",
+                       cpoint_name, cpoint_type);
        } else {
-               printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n");
+               pr_info("No crash points registered, enable through debugfs\n");
        }
 
        return 0;
@@ -823,7 +863,7 @@ static void __exit lkdtm_module_exit(void)
        debugfs_remove_recursive(lkdtm_debugfs_root);
 
        unregister_jprobe(&lkdtm);
-       printk(KERN_INFO "lkdtm: Crash point unregistered\n");
+       pr_info("Crash point unregistered\n");
 }
 
 module_init(lkdtm_module_init);
index c76fa31e9bf6926aac9cc181846d383f1325c8da..d23384dde73b97e3abf7ee166c1a13ef3fb83249 100644 (file)
@@ -34,3 +34,12 @@ config INTEL_MEI_ME
          82Q33 Express
          82X38/X48 Express
 
+config INTEL_MEI_TXE
+       tristate "Intel Trusted Execution Environment with ME Interface"
+       select INTEL_MEI
+       depends on X86 && PCI && WATCHDOG_CORE
+       help
+         MEI Support for Trusted Execution Environment device on Intel SoCs
+
+         Supported SoCs:
+         Intel Bay Trail
index 08698a466268a50aa55427aca15ad9ef2e9b2242..8ebc6cda1373d6c7e543d785064559720bbf0391 100644 (file)
@@ -1,6 +1,6 @@
 #
 # Makefile - Intel Management Engine Interface (Intel MEI) Linux driver
-# Copyright (c) 2010-2011, Intel Corporation.
+# Copyright (c) 2010-2014, Intel Corporation.
 #
 obj-$(CONFIG_INTEL_MEI) += mei.o
 mei-objs := init.o
@@ -17,3 +17,7 @@ mei-$(CONFIG_DEBUG_FS) += debugfs.o
 obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o
 mei-me-objs := pci-me.o
 mei-me-objs += hw-me.o
+
+obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o
+mei-txe-objs := pci-txe.o
+mei-txe-objs += hw-txe.o
index 2fad8443282913f65601c408bc37a2e9f990efdf..f88cb26364f59b681e756c0481c3be1ebad89ad1 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/fcntl.h>
 #include <linux/aio.h>
 #include <linux/pci.h>
-#include <linux/init.h>
 #include <linux/ioctl.h>
 #include <linux/cdev.h>
 #include <linux/list.h>
@@ -365,7 +364,7 @@ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb)
        if (ret)
                return ret;
 
-       cb->fop_type = MEI_FOP_IOCTL;
+       cb->fop_type = MEI_FOP_WRITE;
 
        if (!list_empty(&dev->amthif_cmd_list.list) ||
            dev->iamthif_state != MEI_IAMTHIF_IDLE) {
index 9b809cfc289924912b92a00d826831fbb46433ec..9ac72f1ea6b9d00f4bcfeedc56a4db388415c679 100644 (file)
@@ -505,7 +505,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
                goto out;
        }
 
-       cb->fop_type = MEI_FOP_IOCTL;
+       cb->fop_type = MEI_FOP_CONNECT;
 
        if (dev->hbuf_is_ready && !mei_cl_is_other_connecting(cl)) {
                dev->hbuf_is_ready = false;
index a3ae154444b2e81d60066ef4a1aefe70f7b51cc4..ced5b777c70fde3926df4c8e5ae2329d326e185c 100644 (file)
@@ -75,6 +75,54 @@ static const struct file_operations mei_dbgfs_fops_meclients = {
        .llseek = generic_file_llseek,
 };
 
+static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
+                                       size_t cnt, loff_t *ppos)
+{
+       struct mei_device *dev = fp->private_data;
+       struct mei_cl *cl;
+       const size_t bufsz = 1024;
+       char *buf;
+       int i = 0;
+       int pos = 0;
+       int ret;
+
+       if (!dev)
+               return -ENODEV;
+
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if  (!buf)
+               return -ENOMEM;
+
+       pos += scnprintf(buf + pos, bufsz - pos,
+                       "  |me|host|state|rd|wr|\n");
+
+       mutex_lock(&dev->device_lock);
+
+       /*  if the driver is not enabled the list won't b consitent */
+       if (dev->dev_state != MEI_DEV_ENABLED)
+               goto out;
+
+       list_for_each_entry(cl, &dev->file_list, link) {
+
+               pos += scnprintf(buf + pos, bufsz - pos,
+                       "%2d|%2d|%4d|%5d|%2d|%2d|\n",
+                       i, cl->me_client_id, cl->host_client_id, cl->state,
+                       cl->reading_state, cl->writing_state);
+               i++;
+       }
+out:
+       mutex_unlock(&dev->device_lock);
+       ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations mei_dbgfs_fops_active = {
+       .open = simple_open,
+       .read = mei_dbgfs_read_active,
+       .llseek = generic_file_llseek,
+};
+
 static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf,
                                        size_t cnt, loff_t *ppos)
 {
@@ -128,6 +176,12 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name)
                dev_err(&dev->pdev->dev, "meclients: registration failed\n");
                goto err;
        }
+       f = debugfs_create_file("active", S_IRUSR, dir,
+                               dev, &mei_dbgfs_fops_active);
+       if (!f) {
+               dev_err(&dev->pdev->dev, "meclients: registration failed\n");
+               goto err;
+       }
        f = debugfs_create_file("devstate", S_IRUSR, dir,
                                dev, &mei_dbgfs_fops_devstate);
        if (!f) {
index 28cd74c073b99e572161e668dba5082ac2c3cabf..d3fcb23bb0810179266b5f294b26c06fc7221a74 100644 (file)
@@ -147,7 +147,7 @@ int mei_hbm_start_wait(struct mei_device *dev)
        ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
                        dev->hbm_state == MEI_HBM_IDLE ||
                        dev->hbm_state >= MEI_HBM_STARTED,
-                       mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
+                       mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
        mutex_lock(&dev->device_lock);
 
        if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) {
@@ -283,17 +283,18 @@ static int mei_hbm_prop_req(struct mei_device *dev)
 }
 
 /**
- * mei_hbm_stop_req_prepare - prepare stop request message
+ * mei_hbm_stop_req - send stop request message
  *
  * @dev - mei device
- * @mei_hdr - mei message header
- * @data - hbm message body buffer
+ * @cl: client info
+ *
+ * This function returns -EIO on write failure
  */
-static void mei_hbm_stop_req_prepare(struct mei_device *dev,
-               struct mei_msg_hdr *mei_hdr, unsigned char *data)
+static int mei_hbm_stop_req(struct mei_device *dev)
 {
+       struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
        struct hbm_host_stop_request *req =
-                       (struct hbm_host_stop_request *)data;
+                       (struct hbm_host_stop_request *)dev->wr_msg.data;
        const size_t len = sizeof(struct hbm_host_stop_request);
 
        mei_hbm_hdr(mei_hdr, len);
@@ -301,6 +302,8 @@ static void mei_hbm_stop_req_prepare(struct mei_device *dev,
        memset(req, 0, len);
        req->hbm_cmd = HOST_STOP_REQ_CMD;
        req->reason = DRIVER_STOP_REQUEST;
+
+       return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
 }
 
 /**
@@ -404,6 +407,25 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
        return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
 }
 
+/**
+ * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
+ *
+ * @dev: the device structure
+ * @cl: a client to disconnect from
+ *
+ * This function returns -EIO on write failure
+ */
+int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
+{
+       struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
+       const size_t len = sizeof(struct hbm_client_connect_response);
+
+       mei_hbm_hdr(mei_hdr, len);
+       mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, dev->wr_msg.data, len);
+
+       return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
+}
+
 /**
  * mei_hbm_cl_disconnect_res - disconnect response from ME
  *
@@ -507,7 +529,7 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev,
                        list_del(&pos->list);
                        return;
                }
-               if (pos->fop_type == MEI_FOP_IOCTL) {
+               if (pos->fop_type == MEI_FOP_CONNECT) {
                        if (is_treat_specially_client(cl, rs)) {
                                list_del(&pos->list);
                                cl->status = 0;
@@ -525,12 +547,14 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev,
  *
  * @dev: the device structure.
  * @disconnect_req: disconnect request bus message from the me
+ *
+ * returns -ENOMEM on allocation failure
  */
-static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
+static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
                struct hbm_client_connect_request *disconnect_req)
 {
        struct mei_cl *cl, *next;
-       const size_t len = sizeof(struct hbm_client_connect_response);
+       struct mei_cl_cb *cb;
 
        list_for_each_entry_safe(cl, next, &dev->file_list, link) {
                if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
@@ -544,13 +568,17 @@ static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
                        else if (cl == &dev->iamthif_cl)
                                dev->iamthif_timer = 0;
 
-                       /* prepare disconnect response */
-                       mei_hbm_hdr(&dev->wr_ext_msg.hdr, len);
-                       mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD,
-                                        dev->wr_ext_msg.data, len);
+                       cb = mei_io_cb_init(cl, NULL);
+                       if (!cb)
+                               return -ENOMEM;
+                       cb->fop_type = MEI_FOP_DISCONNECT_RSP;
+                       cl_dbg(dev, cl, "add disconnect response as first\n");
+                       list_add(&cb->list, &dev->ctrl_wr_list.list);
+
                        break;
                }
        }
+       return 0;
 }
 
 
@@ -629,10 +657,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
                        dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
 
                        dev->hbm_state = MEI_HBM_STOPPED;
-                       mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
-                                               dev->wr_msg.data);
-                       if (mei_write_message(dev, &dev->wr_msg.hdr,
-                                       dev->wr_msg.data)) {
+                       if (mei_hbm_stop_req(dev)) {
                                dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
                                return -EIO;
                        }
@@ -778,10 +803,11 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
 
        case ME_STOP_REQ_CMD:
                dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
-
                dev->hbm_state = MEI_HBM_STOPPED;
-               mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
-                                       dev->wr_ext_msg.data);
+               if (mei_hbm_stop_req(dev)) {
+                       dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
+                       return -EIO;
+               }
                break;
        default:
                BUG();
index 5f92188a5cd7323653026dc5c4f57cdedb3c107e..20e8782711c0c5f53e8e43c97b0c32476b3d27c8 100644 (file)
@@ -54,6 +54,7 @@ int mei_hbm_start_req(struct mei_device *dev);
 int mei_hbm_start_wait(struct mei_device *dev);
 int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
 int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);
+int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl);
 int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
 bool mei_hbm_version_is_supported(struct mei_device *dev);
 
index 6f656c053b144a44b20b798e75e16f3efab49f7f..847c9e5746cb09b2a82d26c9bcd7780a888a4440 100644 (file)
@@ -240,7 +240,7 @@ static int mei_me_hw_ready_wait(struct mei_device *dev)
        mutex_unlock(&dev->device_lock);
        err = wait_event_interruptible_timeout(dev->wait_hw_ready,
                        dev->recvd_hw_ready,
-                       mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
+                       mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT));
        mutex_lock(&dev->device_lock);
        if (!err && !dev->recvd_hw_ready) {
                if (!err)
@@ -505,9 +505,6 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
        /* check slots available for reading */
        slots = mei_count_full_read_slots(dev);
        while (slots > 0) {
-               /* we have urgent data to send so break the read */
-               if (dev->wr_ext_msg.hdr.length)
-                       break;
                dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots);
                rets = mei_irq_read_handler(dev, &complete_list, &slots);
                if (rets && dev->dev_state != MEI_DEV_RESETTING) {
diff --git a/drivers/misc/mei/hw-txe-regs.h b/drivers/misc/mei/hw-txe-regs.h
new file mode 100644 (file)
index 0000000..7283c24
--- /dev/null
@@ -0,0 +1,294 @@
+/******************************************************************************
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Intel MEI Interface Header
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING
+ *
+ * Contact Information:
+ *     Intel Corporation.
+ *     linux-mei@linux.intel.com
+ *     http://www.intel.com
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef _MEI_HW_TXE_REGS_H_
+#define _MEI_HW_TXE_REGS_H_
+
+#include "hw.h"
+
+#define SEC_ALIVENESS_TIMER_TIMEOUT        (5 * MSEC_PER_SEC)
+#define SEC_ALIVENESS_WAIT_TIMEOUT         (1 * MSEC_PER_SEC)
+#define SEC_RESET_WAIT_TIMEOUT             (1 * MSEC_PER_SEC)
+#define SEC_READY_WAIT_TIMEOUT             (5 * MSEC_PER_SEC)
+#define START_MESSAGE_RESPONSE_WAIT_TIMEOUT (5 * MSEC_PER_SEC)
+#define RESET_CANCEL_WAIT_TIMEOUT          (1 * MSEC_PER_SEC)
+
+enum {
+       SEC_BAR,
+       BRIDGE_BAR,
+
+       NUM_OF_MEM_BARS
+};
+
+/* SeC FW Status Register
+ *
+ * FW uses this register in order to report its status to host.
+ * This register resides in PCI-E config space.
+ */
+#define PCI_CFG_TXE_FW_STS0   0x40
+#  define PCI_CFG_TXE_FW_STS0_WRK_ST_MSK    0x0000000F
+#  define PCI_CFG_TXE_FW_STS0_OP_ST_MSK     0x000001C0
+#  define PCI_CFG_TXE_FW_STS0_FW_INIT_CMPLT 0x00000200
+#  define PCI_CFG_TXE_FW_STS0_ERR_CODE_MSK  0x0000F000
+#  define PCI_CFG_TXE_FW_STS0_OP_MODE_MSK   0x000F0000
+#  define PCI_CFG_TXE_FW_STS0_RST_CNT_MSK   0x00F00000
+
+
+#define IPC_BASE_ADDR  0x80400 /* SeC IPC Base Address */
+
+/* IPC Input Doorbell Register */
+#define SEC_IPC_INPUT_DOORBELL_REG       (0x0000 + IPC_BASE_ADDR)
+
+/* IPC Input Status Register
+ * This register indicates whether or not processing of
+ * the most recent command has been completed by the SEC
+ * New commands and payloads should not be written by the Host
+ * until this indicates that the previous command has been processed.
+ */
+#define SEC_IPC_INPUT_STATUS_REG         (0x0008 + IPC_BASE_ADDR)
+#  define SEC_IPC_INPUT_STATUS_RDY    BIT(0)
+
+/* IPC Host Interrupt Status Register */
+#define SEC_IPC_HOST_INT_STATUS_REG      (0x0010 + IPC_BASE_ADDR)
+#define   SEC_IPC_HOST_INT_STATUS_OUT_DB             BIT(0)
+#define   SEC_IPC_HOST_INT_STATUS_IN_RDY             BIT(1)
+#define   SEC_IPC_HOST_INT_STATUS_HDCP_M0_RCVD       BIT(5)
+#define   SEC_IPC_HOST_INT_STATUS_ILL_MEM_ACCESS     BIT(17)
+#define   SEC_IPC_HOST_INT_STATUS_AES_HKEY_ERR       BIT(18)
+#define   SEC_IPC_HOST_INT_STATUS_DES_HKEY_ERR       BIT(19)
+#define   SEC_IPC_HOST_INT_STATUS_TMRMTB_OVERFLOW    BIT(21)
+
+/* Convenient mask for pending interrupts */
+#define   SEC_IPC_HOST_INT_STATUS_PENDING \
+               (SEC_IPC_HOST_INT_STATUS_OUT_DB| \
+               SEC_IPC_HOST_INT_STATUS_IN_RDY)
+
+/* IPC Host Interrupt Mask Register */
+#define SEC_IPC_HOST_INT_MASK_REG        (0x0014 + IPC_BASE_ADDR)
+
+#  define SEC_IPC_HOST_INT_MASK_OUT_DB BIT(0) /* Output Doorbell Int Mask */
+#  define SEC_IPC_HOST_INT_MASK_IN_RDY BIT(1) /* Input Ready Int Mask */
+
+/* IPC Input Payload RAM */
+#define SEC_IPC_INPUT_PAYLOAD_REG        (0x0100 + IPC_BASE_ADDR)
+/* IPC Shared Payload RAM */
+#define IPC_SHARED_PAYLOAD_REG           (0x0200 + IPC_BASE_ADDR)
+
+/* SeC Address Translation Table Entry 2 - Ctrl
+ *
+ * This register resides also in SeC's PCI-E Memory space.
+ */
+#define SATT2_CTRL_REG                   0x1040
+#  define SATT2_CTRL_VALID_MSK            BIT(0)
+#  define SATT2_CTRL_BR_BASE_ADDR_REG_SHIFT 8
+#  define SATT2_CTRL_BRIDGE_HOST_EN_MSK   BIT(12)
+
+/* SATT Table Entry 2 SAP Base Address Register */
+#define SATT2_SAP_BA_REG                 0x1044
+/* SATT Table Entry 2 SAP Size Register. */
+#define SATT2_SAP_SIZE_REG               0x1048
+ /* SATT Table Entry 2 SAP Bridge Address - LSB Register */
+#define SATT2_BRG_BA_LSB_REG             0x104C
+
+/* Host High-level Interrupt Status Register */
+#define HHISR_REG                        0x2020
+/* Host High-level Interrupt Enable Register
+ *
+ * Resides in PCI memory space. This is the top hierarchy for
+ * interrupts from SeC to host, aggregating both interrupts that
+ * arrive through HICR registers as well as interrupts
+ * that arrive via IPC.
+ */
+#define HHIER_REG                        0x2024
+#define   IPC_HHIER_SEC        BIT(0)
+#define   IPC_HHIER_BRIDGE     BIT(1)
+#define   IPC_HHIER_MSK        (IPC_HHIER_SEC | IPC_HHIER_BRIDGE)
+
+/* Host High-level Interrupt Mask Register.
+ *
+ * Resides in PCI memory space.
+ * This is the top hierarchy for masking interrupts from SeC to host.
+ */
+#define HHIMR_REG                        0x2028
+#define   IPC_HHIMR_SEC       BIT(0)
+#define   IPC_HHIMR_BRIDGE    BIT(1)
+
+/* Host High-level IRQ Status Register */
+#define HHIRQSR_REG                      0x202C
+
+/* Host Interrupt Cause Register 0 - SeC IPC Readiness
+ *
+ * This register is both an ICR to Host from PCI Memory Space
+ * and it is also exposed in the SeC memory space.
+ * This register is used by SeC's IPC driver in order
+ * to synchronize with host about IPC interface state.
+ */
+#define HICR_SEC_IPC_READINESS_REG       0x2040
+#define   HICR_SEC_IPC_READINESS_HOST_RDY  BIT(0)
+#define   HICR_SEC_IPC_READINESS_SEC_RDY   BIT(1)
+#define   HICR_SEC_IPC_READINESS_SYS_RDY     \
+         (HICR_SEC_IPC_READINESS_HOST_RDY | \
+          HICR_SEC_IPC_READINESS_SEC_RDY)
+#define   HICR_SEC_IPC_READINESS_RDY_CLR   BIT(2)
+
+/* Host Interrupt Cause Register 1 - Aliveness Response */
+/* This register is both an ICR to Host from PCI Memory Space
+ * and it is also exposed in the SeC memory space.
+ * The register may be used by SeC to ACK a host request for aliveness.
+ */
+#define HICR_HOST_ALIVENESS_RESP_REG     0x2044
+#define   HICR_HOST_ALIVENESS_RESP_ACK    BIT(0)
+
+/* Host Interrupt Cause Register 2 - SeC IPC Output Doorbell */
+#define HICR_SEC_IPC_OUTPUT_DOORBELL_REG 0x2048
+
+/* Host Interrupt Status Register.
+ *
+ * Resides in PCI memory space.
+ * This is the main register involved in generating interrupts
+ * from SeC to host via HICRs.
+ * The interrupt generation rules are as follows:
+ * An interrupt will be generated whenever for any i,
+ * there is a transition from a state where at least one of
+ * the following conditions did not hold, to a state where
+ * ALL the following conditions hold:
+ * A) HISR.INT[i]_STS == 1.
+ * B) HIER.INT[i]_EN == 1.
+ */
+#define HISR_REG                         0x2060
+#define   HISR_INT_0_STS      BIT(0)
+#define   HISR_INT_1_STS      BIT(1)
+#define   HISR_INT_2_STS      BIT(2)
+#define   HISR_INT_3_STS      BIT(3)
+#define   HISR_INT_4_STS      BIT(4)
+#define   HISR_INT_5_STS      BIT(5)
+#define   HISR_INT_6_STS      BIT(6)
+#define   HISR_INT_7_STS      BIT(7)
+#define   HISR_INT_STS_MSK \
+       (HISR_INT_0_STS | HISR_INT_1_STS | HISR_INT_2_STS)
+
+/* Host Interrupt Enable Register. Resides in PCI memory space. */
+#define HIER_REG                         0x2064
+#define   HIER_INT_0_EN      BIT(0)
+#define   HIER_INT_1_EN      BIT(1)
+#define   HIER_INT_2_EN      BIT(2)
+#define   HIER_INT_3_EN      BIT(3)
+#define   HIER_INT_4_EN      BIT(4)
+#define   HIER_INT_5_EN      BIT(5)
+#define   HIER_INT_6_EN      BIT(6)
+#define   HIER_INT_7_EN      BIT(7)
+
+#define   HIER_INT_EN_MSK \
+        (HIER_INT_0_EN | HIER_INT_1_EN | HIER_INT_2_EN)
+
+
+/* SEC Memory Space IPC output payload.
+ *
+ * This register is part of the output payload which SEC provides to host.
+ */
+#define BRIDGE_IPC_OUTPUT_PAYLOAD_REG    0x20C0
+
+/* SeC Interrupt Cause Register - Host Aliveness Request
+ * This register is both an ICR to SeC and it is also exposed
+ * in the host-visible PCI memory space.
+ * The register is used by host to request SeC aliveness.
+ */
+#define SICR_HOST_ALIVENESS_REQ_REG      0x214C
+#define   SICR_HOST_ALIVENESS_REQ_REQUESTED    BIT(0)
+
+
+/* SeC Interrupt Cause Register - Host IPC Readiness
+ *
+ * This register is both an ICR to SeC and it is also exposed
+ * in the host-visible PCI memory space.
+ * This register is used by the host's SeC driver uses in order
+ * to synchronize with SeC about IPC interface state.
+ */
+#define SICR_HOST_IPC_READINESS_REQ_REG  0x2150
+
+
+#define SICR_HOST_IPC_READINESS_HOST_RDY  BIT(0)
+#define SICR_HOST_IPC_READINESS_SEC_RDY   BIT(1)
+#define SICR_HOST_IPC_READINESS_SYS_RDY     \
+       (SICR_HOST_IPC_READINESS_HOST_RDY | \
+        SICR_HOST_IPC_READINESS_SEC_RDY)
+#define SICR_HOST_IPC_READINESS_RDY_CLR   BIT(2)
+
+/* SeC Interrupt Cause Register - SeC IPC Output Status
+ *
+ * This register indicates whether or not processing of the most recent
+ * command has been completed by the Host.
+ * New commands and payloads should not be written by SeC until this
+ * register indicates that the previous command has been processed.
+ */
+#define SICR_SEC_IPC_OUTPUT_STATUS_REG   0x2154
+#  define SEC_IPC_OUTPUT_STATUS_RDY BIT(0)
+
+
+
+/*  MEI IPC Message payload size 64 bytes */
+#define PAYLOAD_SIZE        64
+
+/* MAX size for SATT range 32MB */
+#define SATT_RANGE_MAX     (32 << 20)
+
+
+#endif /* _MEI_HW_TXE_REGS_H_ */
+
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
new file mode 100644 (file)
index 0000000..19579e5
--- /dev/null
@@ -0,0 +1,1106 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2013-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.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+
+#include <linux/mei.h>
+
+#include "mei_dev.h"
+#include "hw-txe.h"
+#include "client.h"
+#include "hbm.h"
+
+/**
+ * mei_txe_reg_read - Reads 32bit data from the device
+ *
+ * @base_addr: registers base address
+ * @offset: register offset
+ *
+ */
+static inline u32 mei_txe_reg_read(void __iomem *base_addr,
+                                       unsigned long offset)
+{
+       return ioread32(base_addr + offset);
+}
+
+/**
+ * mei_txe_reg_write - Writes 32bit data to the device
+ *
+ * @base_addr: registers base address
+ * @offset: register offset
+ * @value: the value to write
+ */
+static inline void mei_txe_reg_write(void __iomem *base_addr,
+                               unsigned long offset, u32 value)
+{
+       iowrite32(value, base_addr + offset);
+}
+
+/**
+ * mei_txe_sec_reg_read_silent - Reads 32bit data from the SeC BAR
+ *
+ * @dev: the device structure
+ * @offset: register offset
+ *
+ * Doesn't check for aliveness while Reads 32bit data from the SeC BAR
+ */
+static inline u32 mei_txe_sec_reg_read_silent(struct mei_txe_hw *hw,
+                               unsigned long offset)
+{
+       return mei_txe_reg_read(hw->mem_addr[SEC_BAR], offset);
+}
+
+/**
+ * mei_txe_sec_reg_read - Reads 32bit data from the SeC BAR
+ *
+ * @dev: the device structure
+ * @offset: register offset
+ *
+ * Reads 32bit data from the SeC BAR and shout loud if aliveness is not set
+ */
+static inline u32 mei_txe_sec_reg_read(struct mei_txe_hw *hw,
+                               unsigned long offset)
+{
+       WARN(!hw->aliveness, "sec read: aliveness not asserted\n");
+       return mei_txe_sec_reg_read_silent(hw, offset);
+}
+/**
+ * mei_txe_sec_reg_write_silent - Writes 32bit data to the SeC BAR
+ *   doesn't check for aliveness
+ *
+ * @dev: the device structure
+ * @offset: register offset
+ * @value: value to write
+ *
+ * Doesn't check for aliveness while writes 32bit data from to the SeC BAR
+ */
+static inline void mei_txe_sec_reg_write_silent(struct mei_txe_hw *hw,
+                               unsigned long offset, u32 value)
+{
+       mei_txe_reg_write(hw->mem_addr[SEC_BAR], offset, value);
+}
+
+/**
+ * mei_txe_sec_reg_write - Writes 32bit data to the SeC BAR
+ *
+ * @dev: the device structure
+ * @offset: register offset
+ * @value: value to write
+ *
+ * Writes 32bit data from the SeC BAR and shout loud if aliveness is not set
+ */
+static inline void mei_txe_sec_reg_write(struct mei_txe_hw *hw,
+                               unsigned long offset, u32 value)
+{
+       WARN(!hw->aliveness, "sec write: aliveness not asserted\n");
+       mei_txe_sec_reg_write_silent(hw, offset, value);
+}
+/**
+ * mei_txe_br_reg_read - Reads 32bit data from the Bridge BAR
+ *
+ * @hw: the device structure
+ * @offset: offset from which to read the data
+ *
+ */
+static inline u32 mei_txe_br_reg_read(struct mei_txe_hw *hw,
+                               unsigned long offset)
+{
+       return mei_txe_reg_read(hw->mem_addr[BRIDGE_BAR], offset);
+}
+
+/**
+ * mei_txe_br_reg_write - Writes 32bit data to the Bridge BAR
+ *
+ * @hw: the device structure
+ * @offset: offset from which to write the data
+ * @value: the byte to write
+ */
+static inline void mei_txe_br_reg_write(struct mei_txe_hw *hw,
+                               unsigned long offset, u32 value)
+{
+       mei_txe_reg_write(hw->mem_addr[BRIDGE_BAR], offset, value);
+}
+
+/**
+ * mei_txe_aliveness_set - request for aliveness change
+ *
+ * @dev: the device structure
+ * @req: requested aliveness value
+ *
+ * Request for aliveness change and returns true if the change is
+ *   really needed and false if aliveness is already
+ *   in the requested state
+ * Requires device lock to be held
+ */
+static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req)
+{
+
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       bool do_req = hw->aliveness != req;
+
+       dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n",
+                               hw->aliveness, req);
+       if (do_req) {
+               hw->recvd_aliveness = false;
+               mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req);
+       }
+       return do_req;
+}
+
+
+/**
+ * mei_txe_aliveness_req_get - get aliveness requested register value
+ *
+ * @dev: the device structure
+ *
+ * Extract HICR_HOST_ALIVENESS_RESP_ACK bit from
+ * from HICR_HOST_ALIVENESS_REQ register value
+ */
+static u32 mei_txe_aliveness_req_get(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       u32 reg;
+       reg = mei_txe_br_reg_read(hw, SICR_HOST_ALIVENESS_REQ_REG);
+       return reg & SICR_HOST_ALIVENESS_REQ_REQUESTED;
+}
+
+/**
+ * mei_txe_aliveness_get - get aliveness response register value
+ * @dev: the device structure
+ *
+ * Extract HICR_HOST_ALIVENESS_RESP_ACK bit
+ * from HICR_HOST_ALIVENESS_RESP register value
+ */
+static u32 mei_txe_aliveness_get(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       u32 reg;
+       reg = mei_txe_br_reg_read(hw, HICR_HOST_ALIVENESS_RESP_REG);
+       return reg & HICR_HOST_ALIVENESS_RESP_ACK;
+}
+
+/**
+ * mei_txe_aliveness_poll - waits for aliveness to settle
+ *
+ * @dev: the device structure
+ * @expected: expected aliveness value
+ *
+ * Polls for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set
+ * returns > 0 if the expected value was received, -ETIME otherwise
+ */
+static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       int t = 0;
+
+       do {
+               hw->aliveness = mei_txe_aliveness_get(dev);
+               if (hw->aliveness == expected) {
+                       dev_dbg(&dev->pdev->dev,
+                               "aliveness settled after %d msecs\n", t);
+                       return t;
+               }
+               mutex_unlock(&dev->device_lock);
+               msleep(MSEC_PER_SEC / 5);
+               mutex_lock(&dev->device_lock);
+               t += MSEC_PER_SEC / 5;
+       } while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
+
+       dev_err(&dev->pdev->dev, "aliveness timed out\n");
+       return -ETIME;
+}
+
+/**
+ * mei_txe_aliveness_wait - waits for aliveness to settle
+ *
+ * @dev: the device structure
+ * @expected: expected aliveness value
+ *
+ * Waits for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set
+ * returns returns 0 on success and < 0 otherwise
+ */
+static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       const unsigned long timeout =
+                       msecs_to_jiffies(SEC_ALIVENESS_WAIT_TIMEOUT);
+       long err;
+       int ret;
+
+       hw->aliveness = mei_txe_aliveness_get(dev);
+       if (hw->aliveness == expected)
+               return 0;
+
+       mutex_unlock(&dev->device_lock);
+       err = wait_event_timeout(hw->wait_aliveness,
+                       hw->recvd_aliveness, timeout);
+       mutex_lock(&dev->device_lock);
+
+       hw->aliveness = mei_txe_aliveness_get(dev);
+       ret = hw->aliveness == expected ? 0 : -ETIME;
+
+       if (ret)
+               dev_err(&dev->pdev->dev, "aliveness timed out");
+       else
+               dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n",
+                               jiffies_to_msecs(timeout - err));
+       hw->recvd_aliveness = false;
+       return ret;
+}
+
+/**
+ * mei_txe_aliveness_set_sync - sets an wait for aliveness to complete
+ *
+ * @dev: the device structure
+ *
+ * returns returns 0 on success and < 0 otherwise
+ */
+int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
+{
+       if (mei_txe_aliveness_set(dev, req))
+               return mei_txe_aliveness_wait(dev, req);
+       return 0;
+}
+
+/**
+ * mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt
+ *
+ * @dev: the device structure
+ */
+static void mei_txe_input_ready_interrupt_enable(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       u32 hintmsk;
+       /* Enable the SEC_IPC_HOST_INT_MASK_IN_RDY interrupt */
+       hintmsk = mei_txe_sec_reg_read(hw, SEC_IPC_HOST_INT_MASK_REG);
+       hintmsk |= SEC_IPC_HOST_INT_MASK_IN_RDY;
+       mei_txe_sec_reg_write(hw, SEC_IPC_HOST_INT_MASK_REG, hintmsk);
+}
+
+/**
+ * mei_txe_input_doorbell_set
+ *   - Sets bit 0 in SEC_IPC_INPUT_DOORBELL.IPC_INPUT_DOORBELL.
+ * @dev: the device structure
+ */
+static void mei_txe_input_doorbell_set(struct mei_txe_hw *hw)
+{
+       /* Clear the interrupt cause */
+       clear_bit(TXE_INTR_IN_READY_BIT, &hw->intr_cause);
+       mei_txe_sec_reg_write(hw, SEC_IPC_INPUT_DOORBELL_REG, 1);
+}
+
+/**
+ * mei_txe_output_ready_set - Sets the SICR_SEC_IPC_OUTPUT_STATUS bit to 1
+ *
+ * @dev: the device structure
+ */
+static void mei_txe_output_ready_set(struct mei_txe_hw *hw)
+{
+       mei_txe_br_reg_write(hw,
+                       SICR_SEC_IPC_OUTPUT_STATUS_REG,
+                       SEC_IPC_OUTPUT_STATUS_RDY);
+}
+
+/**
+ * mei_txe_is_input_ready - check if TXE is ready for receiving data
+ *
+ * @dev: the device structure
+ */
+static bool mei_txe_is_input_ready(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       u32 status;
+       status = mei_txe_sec_reg_read(hw, SEC_IPC_INPUT_STATUS_REG);
+       return !!(SEC_IPC_INPUT_STATUS_RDY & status);
+}
+
+/**
+ * mei_txe_intr_clear - clear all interrupts
+ *
+ * @dev: the device structure
+ */
+static inline void mei_txe_intr_clear(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       mei_txe_sec_reg_write_silent(hw, SEC_IPC_HOST_INT_STATUS_REG,
+               SEC_IPC_HOST_INT_STATUS_PENDING);
+       mei_txe_br_reg_write(hw, HISR_REG, HISR_INT_STS_MSK);
+       mei_txe_br_reg_write(hw, HHISR_REG, IPC_HHIER_MSK);
+}
+
+/**
+ * mei_txe_intr_disable - disable all interrupts
+ *
+ * @dev: the device structure
+ */
+static void mei_txe_intr_disable(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       mei_txe_br_reg_write(hw, HHIER_REG, 0);
+       mei_txe_br_reg_write(hw, HIER_REG, 0);
+}
+/**
+ * mei_txe_intr_disable - enable all interrupts
+ *
+ * @dev: the device structure
+ */
+static void mei_txe_intr_enable(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       mei_txe_br_reg_write(hw, HHIER_REG, IPC_HHIER_MSK);
+       mei_txe_br_reg_write(hw, HIER_REG, HIER_INT_EN_MSK);
+}
+
+/**
+ * mei_txe_pending_interrupts - check if there are pending interrupts
+ *     only Aliveness, Input ready, and output doorbell are of relevance
+ *
+ * @dev: the device structure
+ *
+ * Checks if there are pending interrupts
+ * only Aliveness, Readiness, Input ready, and Output doorbell are relevant
+ */
+static bool mei_txe_pending_interrupts(struct mei_device *dev)
+{
+
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       bool ret = (hw->intr_cause & (TXE_INTR_READINESS |
+                                     TXE_INTR_ALIVENESS |
+                                     TXE_INTR_IN_READY  |
+                                     TXE_INTR_OUT_DB));
+
+       if (ret) {
+               dev_dbg(&dev->pdev->dev,
+                       "Pending Interrupts InReady=%01d Readiness=%01d, Aliveness=%01d, OutDoor=%01d\n",
+                       !!(hw->intr_cause & TXE_INTR_IN_READY),
+                       !!(hw->intr_cause & TXE_INTR_READINESS),
+                       !!(hw->intr_cause & TXE_INTR_ALIVENESS),
+                       !!(hw->intr_cause & TXE_INTR_OUT_DB));
+       }
+       return ret;
+}
+
+/**
+ * mei_txe_input_payload_write - write a dword to the host buffer
+ *     at offset idx
+ *
+ * @dev: the device structure
+ * @idx: index in the host buffer
+ * @value: value
+ */
+static void mei_txe_input_payload_write(struct mei_device *dev,
+                       unsigned long idx, u32 value)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       mei_txe_sec_reg_write(hw, SEC_IPC_INPUT_PAYLOAD_REG +
+                       (idx * sizeof(u32)), value);
+}
+
+/**
+ * mei_txe_out_data_read - read dword from the device buffer
+ *     at offset idx
+ *
+ * @dev: the device structure
+ * @idx: index in the device buffer
+ *
+ * returns register value at index
+ */
+static u32 mei_txe_out_data_read(const struct mei_device *dev,
+                                       unsigned long idx)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       return mei_txe_br_reg_read(hw,
+               BRIDGE_IPC_OUTPUT_PAYLOAD_REG + (idx * sizeof(u32)));
+}
+
+/* Readiness */
+
+/**
+ * mei_txe_readiness_set_host_rdy
+ *
+ * @dev: the device structure
+ */
+static void mei_txe_readiness_set_host_rdy(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       mei_txe_br_reg_write(hw,
+               SICR_HOST_IPC_READINESS_REQ_REG,
+               SICR_HOST_IPC_READINESS_HOST_RDY);
+}
+
+/**
+ * mei_txe_readiness_clear
+ *
+ * @dev: the device structure
+ */
+static void mei_txe_readiness_clear(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       mei_txe_br_reg_write(hw, SICR_HOST_IPC_READINESS_REQ_REG,
+                               SICR_HOST_IPC_READINESS_RDY_CLR);
+}
+/**
+ * mei_txe_readiness_get - Reads and returns
+ *     the HICR_SEC_IPC_READINESS register value
+ *
+ * @dev: the device structure
+ */
+static u32 mei_txe_readiness_get(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       return mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG);
+}
+
+
+/**
+ * mei_txe_readiness_is_sec_rdy - check readiness
+ *  for HICR_SEC_IPC_READINESS_SEC_RDY
+ *
+ * @readiness - cached readiness state
+ */
+static inline bool mei_txe_readiness_is_sec_rdy(u32 readiness)
+{
+       return !!(readiness & HICR_SEC_IPC_READINESS_SEC_RDY);
+}
+
+/**
+ * mei_txe_hw_is_ready - check if the hw is ready
+ *
+ * @dev: the device structure
+ */
+static bool mei_txe_hw_is_ready(struct mei_device *dev)
+{
+       u32 readiness =  mei_txe_readiness_get(dev);
+       return mei_txe_readiness_is_sec_rdy(readiness);
+}
+
+/**
+ * mei_txe_host_is_ready - check if the host is ready
+ *
+ * @dev: the device structure
+ */
+static inline bool mei_txe_host_is_ready(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       u32 reg = mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG);
+       return !!(reg & HICR_SEC_IPC_READINESS_HOST_RDY);
+}
+
+/**
+ * mei_txe_readiness_wait - wait till readiness settles
+ *
+ * @dev: the device structure
+ *
+ * returns 0 on success and -ETIME on timeout
+ */
+static int mei_txe_readiness_wait(struct mei_device *dev)
+{
+       if (mei_txe_hw_is_ready(dev))
+               return 0;
+
+       mutex_unlock(&dev->device_lock);
+       wait_event_timeout(dev->wait_hw_ready, dev->recvd_hw_ready,
+                       msecs_to_jiffies(SEC_RESET_WAIT_TIMEOUT));
+       mutex_lock(&dev->device_lock);
+       if (!dev->recvd_hw_ready) {
+               dev_err(&dev->pdev->dev, "wait for readiness failed\n");
+               return -ETIME;
+       }
+
+       dev->recvd_hw_ready = false;
+       return 0;
+}
+
+/**
+ *  mei_txe_hw_config - configure hardware at the start of the devices
+ *
+ * @dev: the device structure
+ *
+ * Configure hardware at the start of the device should be done only
+ *   once at the device probe time
+ */
+static void mei_txe_hw_config(struct mei_device *dev)
+{
+
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       /* Doesn't change in runtime */
+       dev->hbuf_depth = PAYLOAD_SIZE / 4;
+
+       hw->aliveness = mei_txe_aliveness_get(dev);
+       hw->readiness = mei_txe_readiness_get(dev);
+
+       dev_dbg(&dev->pdev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n",
+               hw->aliveness, hw->readiness);
+}
+
+
+/**
+ * mei_txe_write - writes a message to device.
+ *
+ * @dev: the device structure
+ * @header: header of message
+ * @buf: message buffer will be written
+ * returns 1 if success, 0 - otherwise.
+ */
+
+static int mei_txe_write(struct mei_device *dev,
+               struct mei_msg_hdr *header, unsigned char *buf)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       unsigned long rem;
+       unsigned long length;
+       u32 *reg_buf = (u32 *)buf;
+       int i;
+
+       if (WARN_ON(!header || !buf))
+               return -EINVAL;
+
+       length = header->length;
+
+       dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header));
+
+       if ((length + sizeof(struct mei_msg_hdr)) > PAYLOAD_SIZE) {
+               dev_err(&dev->pdev->dev, "write length exceeded = %ld > %d\n",
+                       length + sizeof(struct mei_msg_hdr), PAYLOAD_SIZE);
+               return -ERANGE;
+       }
+
+       if (WARN(!hw->aliveness, "txe write: aliveness not asserted\n"))
+               return -EAGAIN;
+
+       /* Enable Input Ready Interrupt. */
+       mei_txe_input_ready_interrupt_enable(dev);
+
+       if (!mei_txe_is_input_ready(dev)) {
+               dev_err(&dev->pdev->dev, "Input is not ready");
+               return -EAGAIN;
+       }
+
+       mei_txe_input_payload_write(dev, 0, *((u32 *)header));
+
+       for (i = 0; i < length / 4; i++)
+               mei_txe_input_payload_write(dev, i + 1, reg_buf[i]);
+
+       rem = length & 0x3;
+       if (rem > 0) {
+               u32 reg = 0;
+               memcpy(&reg, &buf[length - rem], rem);
+               mei_txe_input_payload_write(dev, i + 1, reg);
+       }
+
+       dev->hbuf_is_ready = false;
+       /* Set Input-Doorbell */
+       mei_txe_input_doorbell_set(hw);
+
+       return 0;
+}
+
+/**
+ * mei_txe_hbuf_max_len - mimics the me hbuf circular buffer
+ *
+ * @dev: the device structure
+ *
+ * returns the PAYLOAD_SIZE - 4
+ */
+static size_t mei_txe_hbuf_max_len(const struct mei_device *dev)
+{
+       return PAYLOAD_SIZE - sizeof(struct mei_msg_hdr);
+}
+
+/**
+ * mei_txe_hbuf_empty_slots - mimics the me hbuf circular buffer
+ *
+ * @dev: the device structure
+ *
+ * returns always hbuf_depth
+ */
+static int mei_txe_hbuf_empty_slots(struct mei_device *dev)
+{
+       return dev->hbuf_depth;
+}
+
+/**
+ * mei_txe_count_full_read_slots - mimics the me device circular buffer
+ *
+ * @dev: the device structure
+ *
+ * returns always buffer size in dwords count
+ */
+static int mei_txe_count_full_read_slots(struct mei_device *dev)
+{
+       /* read buffers has static size */
+       return  PAYLOAD_SIZE / 4;
+}
+
+/**
+ * mei_txe_read_hdr - read message header which is always in 4 first bytes
+ *
+ * @dev: the device structure
+ *
+ * returns mei message header
+ */
+
+static u32 mei_txe_read_hdr(const struct mei_device *dev)
+{
+       return mei_txe_out_data_read(dev, 0);
+}
+/**
+ * mei_txe_read - reads a message from the txe device.
+ *
+ * @dev: the device structure
+ * @buf: message buffer will be written
+ * @len: message size will be read
+ *
+ * returns -EINVAL on error wrong argument and 0 on success
+ */
+static int mei_txe_read(struct mei_device *dev,
+               unsigned char *buf, unsigned long len)
+{
+
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       u32 i;
+       u32 *reg_buf = (u32 *)buf;
+       u32 rem = len & 0x3;
+
+       if (WARN_ON(!buf || !len))
+               return -EINVAL;
+
+       dev_dbg(&dev->pdev->dev,
+               "buffer-length = %lu buf[0]0x%08X\n",
+               len, mei_txe_out_data_read(dev, 0));
+
+       for (i = 0; i < len / 4; i++) {
+               /* skip header: index starts from 1 */
+               u32 reg = mei_txe_out_data_read(dev, i + 1);
+               dev_dbg(&dev->pdev->dev, "buf[%d] = 0x%08X\n", i, reg);
+               *reg_buf++ = reg;
+       }
+
+       if (rem) {
+               u32 reg = mei_txe_out_data_read(dev, i + 1);
+               memcpy(reg_buf, &reg, rem);
+       }
+
+       mei_txe_output_ready_set(hw);
+       return 0;
+}
+
+/**
+ * mei_txe_hw_reset - resets host and fw.
+ *
+ * @dev: the device structure
+ * @intr_enable: if interrupt should be enabled after reset.
+ *
+ * returns 0 on success and < 0 in case of error
+ */
+static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+
+       u32 aliveness_req;
+       /*
+        * read input doorbell to ensure consistency between  Bridge and SeC
+        * return value might be garbage return
+        */
+       (void)mei_txe_sec_reg_read_silent(hw, SEC_IPC_INPUT_DOORBELL_REG);
+
+       aliveness_req = mei_txe_aliveness_req_get(dev);
+       hw->aliveness = mei_txe_aliveness_get(dev);
+
+       /* Disable interrupts in this stage we will poll */
+       mei_txe_intr_disable(dev);
+
+       /*
+        * If Aliveness Request and Aliveness Response are not equal then
+        * wait for them to be equal
+        * Since we might have interrupts disabled - poll for it
+        */
+       if (aliveness_req != hw->aliveness)
+               if (mei_txe_aliveness_poll(dev, aliveness_req) < 0) {
+                       dev_err(&dev->pdev->dev,
+                               "wait for aliveness settle failed ... bailing out\n");
+                       return -EIO;
+               }
+
+       /*
+        * If Aliveness Request and Aliveness Response are set then clear them
+        */
+       if (aliveness_req) {
+               mei_txe_aliveness_set(dev, 0);
+               if (mei_txe_aliveness_poll(dev, 0) < 0) {
+                       dev_err(&dev->pdev->dev,
+                               "wait for aliveness failed ... bailing out\n");
+                       return -EIO;
+               }
+       }
+
+       /*
+        * Set rediness RDY_CLR bit
+        */
+       mei_txe_readiness_clear(dev);
+
+       return 0;
+}
+
+/**
+ * mei_txe_hw_start - start the hardware after reset
+ *
+ * @dev: the device structure
+ *
+ * returns 0 on success and < 0 in case of error
+ */
+static int mei_txe_hw_start(struct mei_device *dev)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       int ret;
+
+       u32 hisr;
+
+       /* bring back interrupts */
+       mei_txe_intr_enable(dev);
+
+       ret = mei_txe_readiness_wait(dev);
+       if (ret < 0) {
+               dev_err(&dev->pdev->dev, "wating for readiness failed\n");
+               return ret;
+       }
+
+       /*
+        * If HISR.INT2_STS interrupt status bit is set then clear it.
+        */
+       hisr = mei_txe_br_reg_read(hw, HISR_REG);
+       if (hisr & HISR_INT_2_STS)
+               mei_txe_br_reg_write(hw, HISR_REG, HISR_INT_2_STS);
+
+       /* Clear the interrupt cause of OutputDoorbell */
+       clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause);
+
+       ret = mei_txe_aliveness_set_sync(dev, 1);
+       if (ret < 0) {
+               dev_err(&dev->pdev->dev, "wait for aliveness failed ... bailing out\n");
+               return ret;
+       }
+
+       /* enable input ready interrupts:
+        * SEC_IPC_HOST_INT_MASK.IPC_INPUT_READY_INT_MASK
+        */
+       mei_txe_input_ready_interrupt_enable(dev);
+
+
+       /*  Set the SICR_SEC_IPC_OUTPUT_STATUS.IPC_OUTPUT_READY bit */
+       mei_txe_output_ready_set(hw);
+
+       /* Set bit SICR_HOST_IPC_READINESS.HOST_RDY
+        */
+       mei_txe_readiness_set_host_rdy(dev);
+
+       return 0;
+}
+
+/**
+ * mei_txe_check_and_ack_intrs - translate multi BAR interrupt into
+ *  single bit mask and acknowledge the interrupts
+ *
+ * @dev: the device structure
+ * @do_ack: acknowledge interrupts
+ */
+static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       u32 hisr;
+       u32 hhisr;
+       u32 ipc_isr;
+       u32 aliveness;
+       bool generated;
+
+       /* read interrupt registers */
+       hhisr = mei_txe_br_reg_read(hw, HHISR_REG);
+       generated = (hhisr & IPC_HHIER_MSK);
+       if (!generated)
+               goto out;
+
+       hisr = mei_txe_br_reg_read(hw, HISR_REG);
+
+       aliveness = mei_txe_aliveness_get(dev);
+       if (hhisr & IPC_HHIER_SEC && aliveness)
+               ipc_isr = mei_txe_sec_reg_read_silent(hw,
+                               SEC_IPC_HOST_INT_STATUS_REG);
+       else
+               ipc_isr = 0;
+
+       generated = generated ||
+               (hisr & HISR_INT_STS_MSK) ||
+               (ipc_isr & SEC_IPC_HOST_INT_STATUS_PENDING);
+
+       if (generated && do_ack) {
+               /* Save the interrupt causes */
+               hw->intr_cause |= hisr & HISR_INT_STS_MSK;
+               if (ipc_isr & SEC_IPC_HOST_INT_STATUS_IN_RDY)
+                       hw->intr_cause |= TXE_INTR_IN_READY;
+
+
+               mei_txe_intr_disable(dev);
+               /* Clear the interrupts in hierarchy:
+                * IPC and Bridge, than the High Level */
+               mei_txe_sec_reg_write_silent(hw,
+                       SEC_IPC_HOST_INT_STATUS_REG, ipc_isr);
+               mei_txe_br_reg_write(hw, HISR_REG, hisr);
+               mei_txe_br_reg_write(hw, HHISR_REG, hhisr);
+       }
+
+out:
+       return generated;
+}
+
+/**
+ * mei_txe_irq_quick_handler - The ISR of the MEI device
+ *
+ * @irq: The irq number
+ * @dev_id: pointer to the device structure
+ *
+ * returns irqreturn_t
+ */
+irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id)
+{
+       struct mei_device *dev = dev_id;
+
+       if (mei_txe_check_and_ack_intrs(dev, true))
+               return IRQ_WAKE_THREAD;
+       return IRQ_NONE;
+}
+
+
+/**
+ * mei_txe_irq_thread_handler - txe interrupt thread
+ *
+ * @irq: The irq number
+ * @dev_id: pointer to the device structure
+ *
+ * returns irqreturn_t
+ *
+ */
+irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
+{
+       struct mei_device *dev = (struct mei_device *) dev_id;
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+       struct mei_cl_cb complete_list;
+       s32 slots;
+       int rets = 0;
+
+       dev_dbg(&dev->pdev->dev, "irq thread: Interrupt Registers HHISR|HISR|SEC=%02X|%04X|%02X\n",
+               mei_txe_br_reg_read(hw, HHISR_REG),
+               mei_txe_br_reg_read(hw, HISR_REG),
+               mei_txe_sec_reg_read_silent(hw, SEC_IPC_HOST_INT_STATUS_REG));
+
+
+       /* initialize our complete list */
+       mutex_lock(&dev->device_lock);
+       mei_io_list_init(&complete_list);
+
+       if (pci_dev_msi_enabled(dev->pdev))
+               mei_txe_check_and_ack_intrs(dev, true);
+
+       /* show irq events */
+       mei_txe_pending_interrupts(dev);
+
+       hw->aliveness = mei_txe_aliveness_get(dev);
+       hw->readiness = mei_txe_readiness_get(dev);
+
+       /* Readiness:
+        * Detection of TXE driver going through reset
+        * or TXE driver resetting the HECI interface.
+        */
+       if (test_and_clear_bit(TXE_INTR_READINESS_BIT, &hw->intr_cause)) {
+               dev_dbg(&dev->pdev->dev, "Readiness Interrupt was received...\n");
+
+               /* Check if SeC is going through reset */
+               if (mei_txe_readiness_is_sec_rdy(hw->readiness)) {
+                       dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
+                       dev->recvd_hw_ready = true;
+               } else {
+                       dev->recvd_hw_ready = false;
+                       if (dev->dev_state != MEI_DEV_RESETTING) {
+
+                               dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n");
+                               schedule_work(&dev->reset_work);
+                               goto end;
+
+                       }
+               }
+               wake_up(&dev->wait_hw_ready);
+       }
+
+       /************************************************************/
+       /* Check interrupt cause:
+        * Aliveness: Detection of SeC acknowledge of host request that
+        * it remain alive or host cancellation of that request.
+        */
+
+       if (test_and_clear_bit(TXE_INTR_ALIVENESS_BIT, &hw->intr_cause)) {
+               /* Clear the interrupt cause */
+               dev_dbg(&dev->pdev->dev,
+                       "Aliveness Interrupt: Status: %d\n", hw->aliveness);
+               hw->recvd_aliveness = true;
+               if (waitqueue_active(&hw->wait_aliveness))
+                       wake_up(&hw->wait_aliveness);
+       }
+
+
+       /* Output Doorbell:
+        * Detection of SeC having sent output to host
+        */
+       slots = mei_count_full_read_slots(dev);
+       if (test_and_clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause)) {
+               /* Read from TXE */
+               rets = mei_irq_read_handler(dev, &complete_list, &slots);
+               if (rets && dev->dev_state != MEI_DEV_RESETTING) {
+                       dev_err(&dev->pdev->dev,
+                               "mei_irq_read_handler ret = %d.\n", rets);
+
+                       schedule_work(&dev->reset_work);
+                       goto end;
+               }
+       }
+       /* Input Ready: Detection if host can write to SeC */
+       if (test_and_clear_bit(TXE_INTR_IN_READY_BIT, &hw->intr_cause))
+               dev->hbuf_is_ready = true;
+
+       if (hw->aliveness && dev->hbuf_is_ready) {
+               /* if SeC did not complete reading the written data by host */
+               if (!mei_txe_is_input_ready(dev)) {
+                       dev_dbg(&dev->pdev->dev, "got Input Ready Int, but SEC_IPC_INPUT_STATUS_RDY is 0.\n");
+                       goto end;
+               }
+
+               rets = mei_irq_write_handler(dev, &complete_list);
+               if (rets)
+                       dev_err(&dev->pdev->dev,
+                               "mei_irq_write_handler ret = %d.\n", rets);
+       }
+
+
+
+       mei_irq_compl_handler(dev, &complete_list);
+
+end:
+       dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets);
+
+       mutex_unlock(&dev->device_lock);
+
+       mei_enable_interrupts(dev);
+       return IRQ_HANDLED;
+}
+
+static const struct mei_hw_ops mei_txe_hw_ops = {
+
+       .host_is_ready = mei_txe_host_is_ready,
+
+       .hw_is_ready = mei_txe_hw_is_ready,
+       .hw_reset = mei_txe_hw_reset,
+       .hw_config = mei_txe_hw_config,
+       .hw_start = mei_txe_hw_start,
+
+       .intr_clear = mei_txe_intr_clear,
+       .intr_enable = mei_txe_intr_enable,
+       .intr_disable = mei_txe_intr_disable,
+
+       .hbuf_free_slots = mei_txe_hbuf_empty_slots,
+       .hbuf_is_ready = mei_txe_is_input_ready,
+       .hbuf_max_len = mei_txe_hbuf_max_len,
+
+       .write = mei_txe_write,
+
+       .rdbuf_full_slots = mei_txe_count_full_read_slots,
+       .read_hdr = mei_txe_read_hdr,
+
+       .read = mei_txe_read,
+
+};
+
+/**
+ * mei_txe_dev_init - allocates and initializes txe hardware specific structure
+ *
+ * @pdev - pci device
+ * returns struct mei_device * on success or NULL;
+ *
+ */
+struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
+{
+       struct mei_device *dev;
+       struct mei_txe_hw *hw;
+
+       dev = kzalloc(sizeof(struct mei_device) +
+                        sizeof(struct mei_txe_hw), GFP_KERNEL);
+       if (!dev)
+               return NULL;
+
+       mei_device_init(dev);
+
+       hw = to_txe_hw(dev);
+
+       init_waitqueue_head(&hw->wait_aliveness);
+
+       dev->ops = &mei_txe_hw_ops;
+
+       dev->pdev = pdev;
+       return dev;
+}
+
+/**
+ * mei_txe_setup_satt2 - SATT2 configuration for DMA support.
+ *
+ * @dev:   the device structure
+ * @addr:  physical address start of the range
+ * @range: physical range size
+ */
+int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range)
+{
+       struct mei_txe_hw *hw = to_txe_hw(dev);
+
+       u32 lo32 = lower_32_bits(addr);
+       u32 hi32 = upper_32_bits(addr);
+       u32 ctrl;
+
+       /* SATT is limited to 36 Bits */
+       if (hi32 & ~0xF)
+               return -EINVAL;
+
+       /* SATT has to be 16Byte aligned */
+       if (lo32 & 0xF)
+               return -EINVAL;
+
+       /* SATT range has to be 4Bytes aligned */
+       if (range & 0x4)
+               return -EINVAL;
+
+       /* SATT is limited to 32 MB range*/
+       if (range > SATT_RANGE_MAX)
+               return -EINVAL;
+
+       ctrl = SATT2_CTRL_VALID_MSK;
+       ctrl |= hi32  << SATT2_CTRL_BR_BASE_ADDR_REG_SHIFT;
+
+       mei_txe_br_reg_write(hw, SATT2_SAP_SIZE_REG, range);
+       mei_txe_br_reg_write(hw, SATT2_BRG_BA_LSB_REG, lo32);
+       mei_txe_br_reg_write(hw, SATT2_CTRL_REG, ctrl);
+       dev_dbg(&dev->pdev->dev, "SATT2: SAP_SIZE_OFFSET=0x%08X, BRG_BA_LSB_OFFSET=0x%08X, CTRL_OFFSET=0x%08X\n",
+               range, lo32, ctrl);
+
+       return 0;
+}
diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h
new file mode 100644 (file)
index 0000000..857d88c
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2013-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 _MEI_HW_TXE_H_
+#define _MEI_HW_TXE_H_
+
+#include "hw.h"
+#include "hw-txe-regs.h"
+
+/* Flatten Hierarchy interrupt cause */
+#define TXE_INTR_READINESS_BIT  0 /* HISR_INT_0_STS */
+#define TXE_INTR_READINESS      HISR_INT_0_STS
+#define TXE_INTR_ALIVENESS_BIT  1 /* HISR_INT_1_STS */
+#define TXE_INTR_ALIVENESS      HISR_INT_1_STS
+#define TXE_INTR_OUT_DB_BIT     2 /* HISR_INT_2_STS */
+#define TXE_INTR_OUT_DB         HISR_INT_2_STS
+#define TXE_INTR_IN_READY_BIT   8 /* beyond HISR */
+#define TXE_INTR_IN_READY       BIT(8)
+
+/**
+ * struct mei_txe_hw - txe hardware specifics
+ *
+ * @mem_addr:        SeC and BRIDGE bars
+ * @aliveness:       aliveness (power gating) state of the hardware
+ * @readiness:       readiness state of the hardware
+ * @wait_aliveness:  aliveness wait queue
+ * @recvd_aliveness: aliveness interrupt was recived
+ * @intr_cause:      translated interrupt cause
+ */
+struct mei_txe_hw {
+       void __iomem *mem_addr[NUM_OF_MEM_BARS];
+       u32 aliveness;
+       u32 readiness;
+
+       wait_queue_head_t wait_aliveness;
+       bool recvd_aliveness;
+
+       unsigned long intr_cause;
+};
+
+#define to_txe_hw(dev) (struct mei_txe_hw *)((dev)->hw)
+
+static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw)
+{
+       return container_of((void *)hw, struct mei_device, hw);
+}
+
+struct mei_device *mei_txe_dev_init(struct pci_dev *pdev);
+
+irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id);
+irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id);
+
+int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req);
+
+int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range);
+
+
+#endif /* _MEI_HW_TXE_H_ */
index dd44e33ad2b6a39daa288a4f23132012629c6779..e06779d4413ec5d8196a8a8165169dde8c07db2c 100644 (file)
@@ -22,7 +22,7 @@
 /*
  * Timeouts in Seconds
  */
-#define MEI_INTEROP_TIMEOUT         7  /* Timeout on ready message */
+#define MEI_HW_READY_TIMEOUT        2  /* Timeout on ready message */
 #define MEI_CONNECT_TIMEOUT         3  /* HPS: at least 2 seconds */
 
 #define MEI_CL_CONNECT_TIMEOUT     15  /* HPS: Client Connect Timeout */
 #define MEI_IAMTHIF_STALL_TIMER    12  /* HPS */
 #define MEI_IAMTHIF_READ_TIMER     10  /* HPS */
 
+#define MEI_HBM_TIMEOUT            1   /* 1 second */
 
 /*
  * MEI Version
  */
 #define HBM_MINOR_VERSION                   0
 #define HBM_MAJOR_VERSION                   1
-#define HBM_TIMEOUT                         1  /* 1 second */
 
 /* Host bus message command opcode */
 #define MEI_HBM_CMD_OP_MSK                  0x7f
index cdd31c2a2a2bf095e827758e3d71567d80557a5f..214dcef9750a4853cc03fc4b0d904be5096cbace 100644 (file)
@@ -116,7 +116,6 @@ int mei_reset(struct mei_device *dev)
                mei_cl_unlink(&dev->wd_cl);
                mei_cl_unlink(&dev->iamthif_cl);
                mei_amthif_reset_params(dev);
-               memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
        }
 
 
@@ -126,7 +125,6 @@ int mei_reset(struct mei_device *dev)
 
        if (ret) {
                dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret);
-               dev->dev_state = MEI_DEV_DISABLED;
                return ret;
        }
 
@@ -139,7 +137,6 @@ int mei_reset(struct mei_device *dev)
        ret = mei_hw_start(dev);
        if (ret) {
                dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret);
-               dev->dev_state = MEI_DEV_DISABLED;
                return ret;
        }
 
@@ -149,7 +146,7 @@ int mei_reset(struct mei_device *dev)
        ret = mei_hbm_start_req(dev);
        if (ret) {
                dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret);
-               dev->dev_state = MEI_DEV_DISABLED;
+               dev->dev_state = MEI_DEV_RESETTING;
                return ret;
        }
 
@@ -166,6 +163,7 @@ EXPORT_SYMBOL_GPL(mei_reset);
  */
 int mei_start(struct mei_device *dev)
 {
+       int ret;
        mutex_lock(&dev->device_lock);
 
        /* acknowledge interrupt and stop interrupts */
@@ -175,10 +173,18 @@ int mei_start(struct mei_device *dev)
 
        dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
 
-       dev->dev_state = MEI_DEV_INITIALIZING;
        dev->reset_count = 0;
-       mei_reset(dev);
+       do {
+               dev->dev_state = MEI_DEV_INITIALIZING;
+               ret = mei_reset(dev);
+
+               if (ret == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) {
+                       dev_err(&dev->pdev->dev, "reset failed ret = %d", ret);
+                       goto err;
+               }
+       } while (ret);
 
+       /* we cannot start the device w/o hbm start message completed */
        if (dev->dev_state == MEI_DEV_DISABLED) {
                dev_err(&dev->pdev->dev, "reset failed");
                goto err;
@@ -238,27 +244,40 @@ int mei_restart(struct mei_device *dev)
 
        mutex_unlock(&dev->device_lock);
 
-       if (err || dev->dev_state == MEI_DEV_DISABLED)
+       if (err == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) {
+               dev_err(&dev->pdev->dev, "device disabled = %d\n", err);
                return -ENODEV;
+       }
+
+       /* try to start again */
+       if (err)
+               schedule_work(&dev->reset_work);
+
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(mei_restart);
 
-
 static void mei_reset_work(struct work_struct *work)
 {
        struct mei_device *dev =
                container_of(work, struct mei_device,  reset_work);
+       int ret;
 
        mutex_lock(&dev->device_lock);
 
-       mei_reset(dev);
+       ret = mei_reset(dev);
 
        mutex_unlock(&dev->device_lock);
 
-       if (dev->dev_state == MEI_DEV_DISABLED)
-               dev_err(&dev->pdev->dev, "reset failed");
+       if (dev->dev_state == MEI_DEV_DISABLED) {
+               dev_err(&dev->pdev->dev, "device disabled = %d\n", ret);
+               return;
+       }
+
+       /* retry reset in case of failure */
+       if (ret)
+               schedule_work(&dev->reset_work);
 }
 
 void mei_stop(struct mei_device *dev)
index f0fbb5179f80cf6960bed4771fd0b12fdadea786..75ff4092953e6f47a96605db18cc4c65482aa60c 100644 (file)
@@ -160,6 +160,41 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
        return 0;
 }
 
+/**
+ * mei_cl_irq_disconnect_rsp - send disconnection response message
+ *
+ * @cl: client
+ * @cb: callback block.
+ * @slots: free slots.
+ * @cmpl_list: complete list.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
+                       s32 *slots, struct mei_cl_cb *cmpl_list)
+{
+       struct mei_device *dev = cl->dev;
+       int ret;
+
+       u32 msg_slots =
+               mei_data2slots(sizeof(struct hbm_client_connect_response));
+
+       if (*slots < msg_slots)
+               return -EMSGSIZE;
+
+       *slots -= msg_slots;
+
+       ret = mei_hbm_cl_disconnect_rsp(dev, cl);
+
+       cl->state = MEI_FILE_DISCONNECTED;
+       cl->status = 0;
+       mei_io_cb_free(cb);
+
+       return ret;
+}
+
+
+
 /**
  * mei_cl_irq_close - processes close related operation from
  *     interrupt thread context - send disconnect request
@@ -244,8 +279,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
 
 
 /**
- * mei_cl_irq_ioctl - processes client ioctl related operation from the
- *     interrupt thread context -   send connection request
+ * mei_cl_irq_connect - send connect request in irq_thread context
  *
  * @cl: client
  * @cb: callback block.
@@ -254,7 +288,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
  *
  * returns 0, OK; otherwise, error.
  */
-static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb,
+static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
                           s32 *slots, struct mei_cl_cb *cmpl_list)
 {
        struct mei_device *dev = cl->dev;
@@ -263,6 +297,9 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb,
        u32 msg_slots =
                mei_data2slots(sizeof(struct hbm_client_connect_request));
 
+       if (mei_cl_is_other_connecting(cl))
+               return 0;
+
        if (*slots < msg_slots) {
                /* return the cancel routine */
                list_del(&cb->list);
@@ -450,12 +487,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
                wake_up_interruptible(&dev->wait_stop_wd);
        }
 
-       if (dev->wr_ext_msg.hdr.length) {
-               mei_write_message(dev, &dev->wr_ext_msg.hdr,
-                               dev->wr_ext_msg.data);
-               slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
-               dev->wr_ext_msg.hdr.length = 0;
-       }
        if (dev->dev_state == MEI_DEV_ENABLED) {
                if (dev->wd_pending &&
                    mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) {
@@ -496,16 +527,18 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
                                return ret;
 
                        break;
-               case MEI_FOP_IOCTL:
+               case MEI_FOP_CONNECT:
                        /* connect message */
-                       if (mei_cl_is_other_connecting(cl))
-                               continue;
-                       ret = mei_cl_irq_ioctl(cl, cb, &slots, cmpl_list);
+                       ret = mei_cl_irq_connect(cl, cb, &slots, cmpl_list);
                        if (ret)
                                return ret;
 
                        break;
-
+               case MEI_FOP_DISCONNECT_RSP:
+                       /* send disconnect resp */
+                       ret = mei_cl_irq_disconnect_rsp(cl, cb, &slots, cmpl_list);
+                       if (ret)
+                               return ret;
                default:
                        BUG();
                }
index f7de95b4cdd907ebfbf241b46f73d95cdc9cb3d7..21e52496bc6e68301ffcd0e33f6a275a676a5dd5 100644 (file)
@@ -130,16 +130,18 @@ enum mei_wd_states {
 
 /**
  * enum mei_cb_file_ops  - file operation associated with the callback
- * @MEI_FOP_READ   - read
- * @MEI_FOP_WRITE  - write
- * @MEI_FOP_IOCTL  - ioctl
- * @MEI_FOP_OPEN   - open
- * @MEI_FOP_CLOSE  - close
+ * @MEI_FOP_READ      - read
+ * @MEI_FOP_WRITE     - write
+ * @MEI_FOP_CONNECT   - connect
+ * @MEI_FOP_DISCONNECT_RSP - disconnect response
+ * @MEI_FOP_OPEN      - open
+ * @MEI_FOP_CLOSE     - close
  */
 enum mei_cb_file_ops {
        MEI_FOP_READ = 0,
        MEI_FOP_WRITE,
-       MEI_FOP_IOCTL,
+       MEI_FOP_CONNECT,
+       MEI_FOP_DISCONNECT_RSP,
        MEI_FOP_OPEN,
        MEI_FOP_CLOSE
 };
@@ -339,7 +341,6 @@ struct mei_cl_device {
  * @hbuf_depth - depth of hardware host/write buffer is slots
  * @hbuf_is_ready - query if the host host/write buffer is ready
  * @wr_msg - the buffer for hbm control messages
- * @wr_ext_msg - the buffer for hbm control responses (set in read cycle)
  */
 struct mei_device {
        struct pci_dev *pdev;   /* pointer to pci device struct */
@@ -394,11 +395,6 @@ struct mei_device {
                unsigned char data[128];
        } wr_msg;
 
-       struct {
-               struct mei_msg_hdr hdr;
-               unsigned char data[4];  /* All HBM messages are 4 bytes */
-       } wr_ext_msg;           /* for control responses */
-
        struct hbm_version version;
 
        struct mei_me_client *me_clients; /* Note: memory has to be allocated */
index ddadd08956f46bc3af94e93cc146c50b76cde196..5434354cf1d5d2a8dd16e7aa73248e33fec7541e 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/aio.h>
 #include <linux/pci.h>
 #include <linux/poll.h>
-#include <linux/init.h>
 #include <linux/ioctl.h>
 #include <linux/cdev.h>
 #include <linux/sched.h>
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
new file mode 100644 (file)
index 0000000..af4412c
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2013-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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/uuid.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+#include <linux/mei.h>
+
+
+#include "mei_dev.h"
+#include "hw-txe.h"
+
+static DEFINE_PCI_DEVICE_TABLE(mei_txe_pci_tbl) = {
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F18)}, /* Baytrail */
+       {0, }
+};
+MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
+
+
+static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
+{
+       int i;
+       for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) {
+               if (hw->mem_addr[i]) {
+                       pci_iounmap(pdev, hw->mem_addr[i]);
+                       hw->mem_addr[i] = NULL;
+               }
+       }
+}
+/**
+ * mei_probe - Device Initialization Routine
+ *
+ * @pdev: PCI device structure
+ * @ent: entry in mei_txe_pci_tbl
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct mei_device *dev;
+       struct mei_txe_hw *hw;
+       int err;
+       int i;
+
+       /* enable pci dev */
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "failed to enable pci device.\n");
+               goto end;
+       }
+       /* set PCI host mastering  */
+       pci_set_master(pdev);
+       /* pci request regions for mei driver */
+       err = pci_request_regions(pdev, KBUILD_MODNAME);
+       if (err) {
+               dev_err(&pdev->dev, "failed to get pci regions.\n");
+               goto disable_device;
+       }
+
+       err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
+       if (err) {
+               err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+               if (err) {
+                       dev_err(&pdev->dev, "No suitable DMA available.\n");
+                       goto release_regions;
+               }
+       }
+
+       /* allocates and initializes the mei dev structure */
+       dev = mei_txe_dev_init(pdev);
+       if (!dev) {
+               err = -ENOMEM;
+               goto release_regions;
+       }
+       hw = to_txe_hw(dev);
+
+       /* mapping  IO device memory */
+       for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) {
+               hw->mem_addr[i] = pci_iomap(pdev, i, 0);
+               if (!hw->mem_addr[i]) {
+                       dev_err(&pdev->dev, "mapping I/O device memory failure.\n");
+                       err = -ENOMEM;
+                       goto free_device;
+               }
+       }
+
+
+       pci_enable_msi(pdev);
+
+       /* clear spurious interrupts */
+       mei_clear_interrupts(dev);
+
+       /* request and enable interrupt  */
+       if (pci_dev_msi_enabled(pdev))
+               err = request_threaded_irq(pdev->irq,
+                       NULL,
+                       mei_txe_irq_thread_handler,
+                       IRQF_ONESHOT, KBUILD_MODNAME, dev);
+       else
+               err = request_threaded_irq(pdev->irq,
+                       mei_txe_irq_quick_handler,
+                       mei_txe_irq_thread_handler,
+                       IRQF_SHARED, KBUILD_MODNAME, dev);
+       if (err) {
+               dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n",
+                       pdev->irq);
+               goto free_device;
+       }
+
+       if (mei_start(dev)) {
+               dev_err(&pdev->dev, "init hw failure.\n");
+               err = -ENODEV;
+               goto release_irq;
+       }
+
+       err = mei_register(dev);
+       if (err)
+               goto release_irq;
+
+       pci_set_drvdata(pdev, dev);
+
+       return 0;
+
+release_irq:
+
+       mei_cancel_work(dev);
+
+       /* disable interrupts */
+       mei_disable_interrupts(dev);
+
+       free_irq(pdev->irq, dev);
+       pci_disable_msi(pdev);
+
+free_device:
+       mei_txe_pci_iounmap(pdev, hw);
+
+       kfree(dev);
+release_regions:
+       pci_release_regions(pdev);
+disable_device:
+       pci_disable_device(pdev);
+end:
+       dev_err(&pdev->dev, "initialization failed.\n");
+       return err;
+}
+
+/**
+ * mei_remove - Device Removal Routine
+ *
+ * @pdev: PCI device structure
+ *
+ * mei_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device.
+ */
+static void mei_txe_remove(struct pci_dev *pdev)
+{
+       struct mei_device *dev;
+       struct mei_txe_hw *hw;
+
+       dev = pci_get_drvdata(pdev);
+       if (!dev) {
+               dev_err(&pdev->dev, "mei: dev =NULL\n");
+               return;
+       }
+
+       hw = to_txe_hw(dev);
+
+       mei_stop(dev);
+
+       /* disable interrupts */
+       mei_disable_interrupts(dev);
+       free_irq(pdev->irq, dev);
+       pci_disable_msi(pdev);
+
+       pci_set_drvdata(pdev, NULL);
+
+       mei_txe_pci_iounmap(pdev, hw);
+
+       mei_deregister(dev);
+
+       kfree(dev);
+
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+}
+
+
+#ifdef CONFIG_PM
+static int mei_txe_pci_suspend(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct mei_device *dev = pci_get_drvdata(pdev);
+
+       if (!dev)
+               return -ENODEV;
+
+       dev_dbg(&pdev->dev, "suspend\n");
+
+       mei_stop(dev);
+
+       mei_disable_interrupts(dev);
+
+       free_irq(pdev->irq, dev);
+       pci_disable_msi(pdev);
+
+       return 0;
+}
+
+static int mei_txe_pci_resume(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct mei_device *dev;
+       int err;
+
+       dev = pci_get_drvdata(pdev);
+       if (!dev)
+               return -ENODEV;
+
+       pci_enable_msi(pdev);
+
+       mei_clear_interrupts(dev);
+
+       /* request and enable interrupt */
+       if (pci_dev_msi_enabled(pdev))
+               err = request_threaded_irq(pdev->irq,
+                       NULL,
+                       mei_txe_irq_thread_handler,
+                       IRQF_ONESHOT, KBUILD_MODNAME, dev);
+       else
+               err = request_threaded_irq(pdev->irq,
+                       mei_txe_irq_quick_handler,
+                       mei_txe_irq_thread_handler,
+                       IRQF_SHARED, KBUILD_MODNAME, dev);
+       if (err) {
+               dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n",
+                               pdev->irq);
+               return err;
+       }
+
+       err = mei_restart(dev);
+
+       return err;
+}
+
+static SIMPLE_DEV_PM_OPS(mei_txe_pm_ops,
+                        mei_txe_pci_suspend,
+                        mei_txe_pci_resume);
+
+#define MEI_TXE_PM_OPS (&mei_txe_pm_ops)
+#else
+#define MEI_TXE_PM_OPS NULL
+#endif /* CONFIG_PM */
+/*
+ *  PCI driver structure
+ */
+static struct pci_driver mei_txe_driver = {
+       .name = KBUILD_MODNAME,
+       .id_table = mei_txe_pci_tbl,
+       .probe = mei_txe_probe,
+       .remove = mei_txe_remove,
+       .shutdown = mei_txe_remove,
+       .driver.pm = MEI_TXE_PM_OPS,
+};
+
+module_pci_driver(mei_txe_driver);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel(R) Trusted Execution Environment Interface");
+MODULE_LICENSE("GPL v2");
index afe66571ce0b7bfde98f05a1a75175f250482fa5..e3e421d97ad4b7be5d9be9c2b8ad5ddd192f4f9a 100644 (file)
@@ -87,8 +87,6 @@ static int sram_remove(struct platform_device *pdev)
        if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool))
                dev_dbg(&pdev->dev, "removed while SRAM allocated\n");
 
-       gen_pool_destroy(sram->pool);
-
        if (sram->clk)
                clk_disable_unprepare(sram->clk);
 
index 3aed525e55b48bee7303715c55dea5f162f23752..1972d57aadb30b9aa69304a9513f251864dd589e 100644 (file)
@@ -22,7 +22,6 @@
 #define pr_fmt(fmt)    "(stc): " fmt
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/tty.h>
 
 #include <linux/seq_file.h>
index 83da711ce9f13688660192f55fac62571961ec93..cb0289b44a179150a4cc267fceb2b69fef379779 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/spi/spi.h>
 #include <linux/of.h>
 
index 5bc10fa193de16e682e5bcc540193bd157e37b50..b00335652e52ac45087a65510f0c82a799920879 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/mutex.h>
diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
new file mode 100644 (file)
index 0000000..075bd79
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# SPMI driver configuration
+#
+menuconfig SPMI
+       tristate "SPMI support"
+       help
+         SPMI (System Power Management Interface) is a two-wire
+         serial interface between baseband and application processors
+         and Power Management Integrated Circuits (PMIC).
+
+if SPMI
+
+config SPMI_MSM_PMIC_ARB
+       tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
+       depends on ARM
+       depends on IRQ_DOMAIN
+       depends on ARCH_MSM || COMPILE_TEST
+       default ARCH_MSM
+       help
+         If you say yes to this option, support will be included for the
+         built-in SPMI PMIC Arbiter interface on Qualcomm MSM family
+         processors.
+
+         This is required for communicating with Qualcomm PMICs and
+         other devices that have the SPMI interface.
+
+endif
diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
new file mode 100644 (file)
index 0000000..fc75104
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for kernel SPMI framework.
+#
+obj-$(CONFIG_SPMI)     += spmi.o
+
+obj-$(CONFIG_SPMI_MSM_PMIC_ARB)        += spmi-pmic-arb.o
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
new file mode 100644 (file)
index 0000000..246e03a
--- /dev/null
@@ -0,0 +1,778 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+
+/* PMIC Arbiter configuration registers */
+#define PMIC_ARB_VERSION               0x0000
+#define PMIC_ARB_INT_EN                        0x0004
+
+/* PMIC Arbiter channel registers */
+#define PMIC_ARB_CMD(N)                        (0x0800 + (0x80 * (N)))
+#define PMIC_ARB_CONFIG(N)             (0x0804 + (0x80 * (N)))
+#define PMIC_ARB_STATUS(N)             (0x0808 + (0x80 * (N)))
+#define PMIC_ARB_WDATA0(N)             (0x0810 + (0x80 * (N)))
+#define PMIC_ARB_WDATA1(N)             (0x0814 + (0x80 * (N)))
+#define PMIC_ARB_RDATA0(N)             (0x0818 + (0x80 * (N)))
+#define PMIC_ARB_RDATA1(N)             (0x081C + (0x80 * (N)))
+
+/* Interrupt Controller */
+#define SPMI_PIC_OWNER_ACC_STATUS(M, N)        (0x0000 + ((32 * (M)) + (4 * (N))))
+#define SPMI_PIC_ACC_ENABLE(N)         (0x0200 + (4 * (N)))
+#define SPMI_PIC_IRQ_STATUS(N)         (0x0600 + (4 * (N)))
+#define SPMI_PIC_IRQ_CLEAR(N)          (0x0A00 + (4 * (N)))
+
+/* Mapping Table */
+#define SPMI_MAPPING_TABLE_REG(N)      (0x0B00 + (4 * (N)))
+#define SPMI_MAPPING_BIT_INDEX(X)      (((X) >> 18) & 0xF)
+#define SPMI_MAPPING_BIT_IS_0_FLAG(X)  (((X) >> 17) & 0x1)
+#define SPMI_MAPPING_BIT_IS_0_RESULT(X)        (((X) >> 9) & 0xFF)
+#define SPMI_MAPPING_BIT_IS_1_FLAG(X)  (((X) >> 8) & 0x1)
+#define SPMI_MAPPING_BIT_IS_1_RESULT(X)        (((X) >> 0) & 0xFF)
+
+#define SPMI_MAPPING_TABLE_LEN         255
+#define SPMI_MAPPING_TABLE_TREE_DEPTH  16      /* Maximum of 16-bits */
+
+/* Ownership Table */
+#define SPMI_OWNERSHIP_TABLE_REG(N)    (0x0700 + (4 * (N)))
+#define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7)
+
+/* Channel Status fields */
+enum pmic_arb_chnl_status {
+       PMIC_ARB_STATUS_DONE    = (1 << 0),
+       PMIC_ARB_STATUS_FAILURE = (1 << 1),
+       PMIC_ARB_STATUS_DENIED  = (1 << 2),
+       PMIC_ARB_STATUS_DROPPED = (1 << 3),
+};
+
+/* Command register fields */
+#define PMIC_ARB_CMD_MAX_BYTE_COUNT    8
+
+/* Command Opcodes */
+enum pmic_arb_cmd_op_code {
+       PMIC_ARB_OP_EXT_WRITEL = 0,
+       PMIC_ARB_OP_EXT_READL = 1,
+       PMIC_ARB_OP_EXT_WRITE = 2,
+       PMIC_ARB_OP_RESET = 3,
+       PMIC_ARB_OP_SLEEP = 4,
+       PMIC_ARB_OP_SHUTDOWN = 5,
+       PMIC_ARB_OP_WAKEUP = 6,
+       PMIC_ARB_OP_AUTHENTICATE = 7,
+       PMIC_ARB_OP_MSTR_READ = 8,
+       PMIC_ARB_OP_MSTR_WRITE = 9,
+       PMIC_ARB_OP_EXT_READ = 13,
+       PMIC_ARB_OP_WRITE = 14,
+       PMIC_ARB_OP_READ = 15,
+       PMIC_ARB_OP_ZERO_WRITE = 16,
+};
+
+/* Maximum number of support PMIC peripherals */
+#define PMIC_ARB_MAX_PERIPHS           256
+#define PMIC_ARB_PERIPH_ID_VALID       (1 << 15)
+#define PMIC_ARB_TIMEOUT_US            100
+#define PMIC_ARB_MAX_TRANS_BYTES       (8)
+
+#define PMIC_ARB_APID_MASK             0xFF
+#define PMIC_ARB_PPID_MASK             0xFFF
+
+/* interrupt enable bit */
+#define SPMI_PIC_ACC_ENABLE_BIT                BIT(0)
+
+/**
+ * spmi_pmic_arb_dev - SPMI PMIC Arbiter object
+ *
+ * @base:              address of the PMIC Arbiter core registers.
+ * @intr:              address of the SPMI interrupt control registers.
+ * @cnfg:              address of the PMIC Arbiter configuration registers.
+ * @lock:              lock to synchronize accesses.
+ * @channel:           which channel to use for accesses.
+ * @irq:               PMIC ARB interrupt.
+ * @ee:                        the current Execution Environment
+ * @min_apid:          minimum APID (used for bounding IRQ search)
+ * @max_apid:          maximum APID
+ * @mapping_table:     in-memory copy of PPID -> APID mapping table.
+ * @domain:            irq domain object for PMIC IRQ domain
+ * @spmic:             SPMI controller object
+ * @apid_to_ppid:      cached mapping from APID to PPID
+ */
+struct spmi_pmic_arb_dev {
+       void __iomem            *base;
+       void __iomem            *intr;
+       void __iomem            *cnfg;
+       raw_spinlock_t          lock;
+       u8                      channel;
+       int                     irq;
+       u8                      ee;
+       u8                      min_apid;
+       u8                      max_apid;
+       u32                     mapping_table[SPMI_MAPPING_TABLE_LEN];
+       struct irq_domain       *domain;
+       struct spmi_controller  *spmic;
+       u16                     apid_to_ppid[256];
+};
+
+static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset)
+{
+       return readl_relaxed(dev->base + offset);
+}
+
+static inline void pmic_arb_base_write(struct spmi_pmic_arb_dev *dev,
+                                      u32 offset, u32 val)
+{
+       writel_relaxed(val, dev->base + offset);
+}
+
+/**
+ * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf
+ * @bc:                byte count -1. range: 0..3
+ * @reg:       register's address
+ * @buf:       output parameter, length must be bc + 1
+ */
+static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc)
+{
+       u32 data = pmic_arb_base_read(dev, reg);
+       memcpy(buf, &data, (bc & 3) + 1);
+}
+
+/**
+ * pa_write_data: write 1..4 bytes from buf to pmic-arb's register
+ * @bc:                byte-count -1. range: 0..3.
+ * @reg:       register's address.
+ * @buf:       buffer to write. length must be bc + 1.
+ */
+static void
+pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc)
+{
+       u32 data = 0;
+       memcpy(&data, buf, (bc & 3) + 1);
+       pmic_arb_base_write(dev, reg, data);
+}
+
+static int pmic_arb_wait_for_done(struct spmi_controller *ctrl)
+{
+       struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl);
+       u32 status = 0;
+       u32 timeout = PMIC_ARB_TIMEOUT_US;
+       u32 offset = PMIC_ARB_STATUS(dev->channel);
+
+       while (timeout--) {
+               status = pmic_arb_base_read(dev, offset);
+
+               if (status & PMIC_ARB_STATUS_DONE) {
+                       if (status & PMIC_ARB_STATUS_DENIED) {
+                               dev_err(&ctrl->dev,
+                                       "%s: transaction denied (0x%x)\n",
+                                       __func__, status);
+                               return -EPERM;
+                       }
+
+                       if (status & PMIC_ARB_STATUS_FAILURE) {
+                               dev_err(&ctrl->dev,
+                                       "%s: transaction failed (0x%x)\n",
+                                       __func__, status);
+                               return -EIO;
+                       }
+
+                       if (status & PMIC_ARB_STATUS_DROPPED) {
+                               dev_err(&ctrl->dev,
+                                       "%s: transaction dropped (0x%x)\n",
+                                       __func__, status);
+                               return -EIO;
+                       }
+
+                       return 0;
+               }
+               udelay(1);
+       }
+
+       dev_err(&ctrl->dev,
+               "%s: timeout, status 0x%x\n",
+               __func__, status);
+       return -ETIMEDOUT;
+}
+
+/* Non-data command */
+static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
+{
+       struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
+       unsigned long flags;
+       u32 cmd;
+       int rc;
+
+       /* Check for valid non-data command */
+       if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
+               return -EINVAL;
+
+       cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20);
+
+       raw_spin_lock_irqsave(&pmic_arb->lock, flags);
+       pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+       rc = pmic_arb_wait_for_done(ctrl);
+       raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
+
+       return rc;
+}
+
+static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
+                            u16 addr, u8 *buf, size_t len)
+{
+       struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
+       unsigned long flags;
+       u8 bc = len - 1;
+       u32 cmd;
+       int rc;
+
+       if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
+               dev_err(&ctrl->dev,
+                       "pmic-arb supports 1..%d bytes per trans, but %d requested",
+                       PMIC_ARB_MAX_TRANS_BYTES, len);
+               return  -EINVAL;
+       }
+
+       /* Check the opcode */
+       if (opc >= 0x60 && opc <= 0x7F)
+               opc = PMIC_ARB_OP_READ;
+       else if (opc >= 0x20 && opc <= 0x2F)
+               opc = PMIC_ARB_OP_EXT_READ;
+       else if (opc >= 0x38 && opc <= 0x3F)
+               opc = PMIC_ARB_OP_EXT_READL;
+       else
+               return -EINVAL;
+
+       cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+
+       raw_spin_lock_irqsave(&pmic_arb->lock, flags);
+       pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+       rc = pmic_arb_wait_for_done(ctrl);
+       if (rc)
+               goto done;
+
+       pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel),
+                    min_t(u8, bc, 3));
+
+       if (bc > 3)
+               pa_read_data(pmic_arb, buf + 4,
+                               PMIC_ARB_RDATA1(pmic_arb->channel), bc - 4);
+
+done:
+       raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
+       return rc;
+}
+
+static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
+                             u16 addr, const u8 *buf, size_t len)
+{
+       struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl);
+       unsigned long flags;
+       u8 bc = len - 1;
+       u32 cmd;
+       int rc;
+
+       if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
+               dev_err(&ctrl->dev,
+                       "pmic-arb supports 1..%d bytes per trans, but:%d requested",
+                       PMIC_ARB_MAX_TRANS_BYTES, len);
+               return  -EINVAL;
+       }
+
+       /* Check the opcode */
+       if (opc >= 0x40 && opc <= 0x5F)
+               opc = PMIC_ARB_OP_WRITE;
+       else if (opc >= 0x00 && opc <= 0x0F)
+               opc = PMIC_ARB_OP_EXT_WRITE;
+       else if (opc >= 0x30 && opc <= 0x37)
+               opc = PMIC_ARB_OP_EXT_WRITEL;
+       else if (opc >= 0x80 && opc <= 0xFF)
+               opc = PMIC_ARB_OP_ZERO_WRITE;
+       else
+               return -EINVAL;
+
+       cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+
+       /* Write data to FIFOs */
+       raw_spin_lock_irqsave(&pmic_arb->lock, flags);
+       pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel)
+                                                       , min_t(u8, bc, 3));
+       if (bc > 3)
+               pa_write_data(pmic_arb, buf + 4,
+                               PMIC_ARB_WDATA1(pmic_arb->channel), bc - 4);
+
+       /* Start the transaction */
+       pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd);
+       rc = pmic_arb_wait_for_done(ctrl);
+       raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
+
+       return rc;
+}
+
+enum qpnpint_regs {
+       QPNPINT_REG_RT_STS              = 0x10,
+       QPNPINT_REG_SET_TYPE            = 0x11,
+       QPNPINT_REG_POLARITY_HIGH       = 0x12,
+       QPNPINT_REG_POLARITY_LOW        = 0x13,
+       QPNPINT_REG_LATCHED_CLR         = 0x14,
+       QPNPINT_REG_EN_SET              = 0x15,
+       QPNPINT_REG_EN_CLR              = 0x16,
+       QPNPINT_REG_LATCHED_STS         = 0x18,
+};
+
+struct spmi_pmic_arb_qpnpint_type {
+       u8 type; /* 1 -> edge */
+       u8 polarity_high;
+       u8 polarity_low;
+} __packed;
+
+/* Simplified accessor functions for irqchip callbacks */
+static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
+                              size_t len)
+{
+       struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+       u8 sid = d->hwirq >> 24;
+       u8 per = d->hwirq >> 16;
+
+       if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid,
+                              (per << 8) + reg, buf, len))
+               dev_err_ratelimited(&pa->spmic->dev,
+                               "failed irqchip transaction on %x\n",
+                                   d->irq);
+}
+
+static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
+{
+       struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+       u8 sid = d->hwirq >> 24;
+       u8 per = d->hwirq >> 16;
+
+       if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid,
+                             (per << 8) + reg, buf, len))
+               dev_err_ratelimited(&pa->spmic->dev,
+                               "failed irqchip transaction on %x\n",
+                                   d->irq);
+}
+
+static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid)
+{
+       unsigned int irq;
+       u32 status;
+       int id;
+
+       status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid));
+       while (status) {
+               id = ffs(status) - 1;
+               status &= ~(1 << id);
+               irq = irq_find_mapping(pa->domain,
+                                      pa->apid_to_ppid[apid] << 16
+                                    | id << 8
+                                    | apid);
+               generic_handle_irq(irq);
+       }
+}
+
+static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc)
+{
+       struct spmi_pmic_arb_dev *pa = irq_get_handler_data(irq);
+       struct irq_chip *chip = irq_get_chip(irq);
+       void __iomem *intr = pa->intr;
+       int first = pa->min_apid >> 5;
+       int last = pa->max_apid >> 5;
+       u32 status;
+       int i, id;
+
+       chained_irq_enter(chip, desc);
+
+       for (i = first; i <= last; ++i) {
+               status = readl_relaxed(intr +
+                                      SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i));
+               while (status) {
+                       id = ffs(status) - 1;
+                       status &= ~(1 << id);
+                       periph_interrupt(pa, id + i * 32);
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static void qpnpint_irq_ack(struct irq_data *d)
+{
+       struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+       u8 irq  = d->hwirq >> 8;
+       u8 apid = d->hwirq;
+       unsigned long flags;
+       u8 data;
+
+       raw_spin_lock_irqsave(&pa->lock, flags);
+       writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid));
+       raw_spin_unlock_irqrestore(&pa->lock, flags);
+
+       data = 1 << irq;
+       qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
+}
+
+static void qpnpint_irq_mask(struct irq_data *d)
+{
+       struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+       u8 irq  = d->hwirq >> 8;
+       u8 apid = d->hwirq;
+       unsigned long flags;
+       u32 status;
+       u8 data;
+
+       raw_spin_lock_irqsave(&pa->lock, flags);
+       status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+       if (status & SPMI_PIC_ACC_ENABLE_BIT) {
+               status = status & ~SPMI_PIC_ACC_ENABLE_BIT;
+               writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+       }
+       raw_spin_unlock_irqrestore(&pa->lock, flags);
+
+       data = 1 << irq;
+       qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
+}
+
+static void qpnpint_irq_unmask(struct irq_data *d)
+{
+       struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+       u8 irq  = d->hwirq >> 8;
+       u8 apid = d->hwirq;
+       unsigned long flags;
+       u32 status;
+       u8 data;
+
+       raw_spin_lock_irqsave(&pa->lock, flags);
+       status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+       if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) {
+               writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT,
+                               pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+       }
+       raw_spin_unlock_irqrestore(&pa->lock, flags);
+
+       data = 1 << irq;
+       qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1);
+}
+
+static void qpnpint_irq_enable(struct irq_data *d)
+{
+       u8 irq  = d->hwirq >> 8;
+       u8 data;
+
+       qpnpint_irq_unmask(d);
+
+       data = 1 << irq;
+       qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
+}
+
+static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+       struct spmi_pmic_arb_qpnpint_type type;
+       u8 irq = d->hwirq >> 8;
+
+       qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
+
+       if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+               type.type |= 1 << irq;
+               if (flow_type & IRQF_TRIGGER_RISING)
+                       type.polarity_high |= 1 << irq;
+               if (flow_type & IRQF_TRIGGER_FALLING)
+                       type.polarity_low  |= 1 << irq;
+       } else {
+               if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
+                   (flow_type & (IRQF_TRIGGER_LOW)))
+                       return -EINVAL;
+
+               type.type &= ~(1 << irq); /* level trig */
+               if (flow_type & IRQF_TRIGGER_HIGH)
+                       type.polarity_high |= 1 << irq;
+               else
+                       type.polarity_low  |= 1 << irq;
+       }
+
+       qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
+       return 0;
+}
+
+static struct irq_chip pmic_arb_irqchip = {
+       .name           = "pmic_arb",
+       .irq_enable     = qpnpint_irq_enable,
+       .irq_ack        = qpnpint_irq_ack,
+       .irq_mask       = qpnpint_irq_mask,
+       .irq_unmask     = qpnpint_irq_unmask,
+       .irq_set_type   = qpnpint_irq_set_type,
+       .flags          = IRQCHIP_MASK_ON_SUSPEND
+                       | IRQCHIP_SKIP_SET_WAKE,
+};
+
+struct spmi_pmic_arb_irq_spec {
+       unsigned slave:4;
+       unsigned per:8;
+       unsigned irq:3;
+};
+
+static int search_mapping_table(struct spmi_pmic_arb_dev *pa,
+                               struct spmi_pmic_arb_irq_spec *spec,
+                               u8 *apid)
+{
+       u16 ppid = spec->slave << 8 | spec->per;
+       u32 *mapping_table = pa->mapping_table;
+       int index = 0, i;
+       u32 data;
+
+       for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
+               data = mapping_table[index];
+
+               if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) {
+                       if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
+                               index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
+                       } else {
+                               *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
+                               return 0;
+                       }
+               } else {
+                       if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
+                               index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
+                       } else {
+                               *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
+                               return 0;
+                       }
+               }
+       }
+
+       return -ENODEV;
+}
+
+static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
+                                          struct device_node *controller,
+                                          const u32 *intspec,
+                                          unsigned int intsize,
+                                          unsigned long *out_hwirq,
+                                          unsigned int *out_type)
+{
+       struct spmi_pmic_arb_dev *pa = d->host_data;
+       struct spmi_pmic_arb_irq_spec spec;
+       int err;
+       u8 apid;
+
+       dev_dbg(&pa->spmic->dev,
+               "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
+               intspec[0], intspec[1], intspec[2]);
+
+       if (d->of_node != controller)
+               return -EINVAL;
+       if (intsize != 4)
+               return -EINVAL;
+       if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
+               return -EINVAL;
+
+       spec.slave = intspec[0];
+       spec.per   = intspec[1];
+       spec.irq   = intspec[2];
+
+       err = search_mapping_table(pa, &spec, &apid);
+       if (err)
+               return err;
+
+       pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per;
+
+       /* Keep track of {max,min}_apid for bounding search during interrupt */
+       if (apid > pa->max_apid)
+               pa->max_apid = apid;
+       if (apid < pa->min_apid)
+               pa->min_apid = apid;
+
+       *out_hwirq = spec.slave << 24
+                  | spec.per   << 16
+                  | spec.irq   << 8
+                  | apid;
+       *out_type  = intspec[3] & IRQ_TYPE_SENSE_MASK;
+
+       dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
+
+       return 0;
+}
+
+static int qpnpint_irq_domain_map(struct irq_domain *d,
+                                 unsigned int virq,
+                                 irq_hw_number_t hwirq)
+{
+       struct spmi_pmic_arb_dev *pa = d->host_data;
+
+       dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq);
+
+       irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq);
+       irq_set_chip_data(virq, d->host_data);
+       irq_set_noprobe(virq);
+       return 0;
+}
+
+static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
+       .map    = qpnpint_irq_domain_map,
+       .xlate  = qpnpint_irq_domain_dt_translate,
+};
+
+static int spmi_pmic_arb_probe(struct platform_device *pdev)
+{
+       struct spmi_pmic_arb_dev *pa;
+       struct spmi_controller *ctrl;
+       struct resource *res;
+       u32 channel, ee;
+       int err, i;
+
+       ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
+       if (!ctrl)
+               return -ENOMEM;
+
+       pa = spmi_controller_get_drvdata(ctrl);
+       pa->spmic = ctrl;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+       pa->base = devm_ioremap_resource(&ctrl->dev, res);
+       if (IS_ERR(pa->base)) {
+               err = PTR_ERR(pa->base);
+               goto err_put_ctrl;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
+       pa->intr = devm_ioremap_resource(&ctrl->dev, res);
+       if (IS_ERR(pa->intr)) {
+               err = PTR_ERR(pa->intr);
+               goto err_put_ctrl;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg");
+       pa->cnfg = devm_ioremap_resource(&ctrl->dev, res);
+       if (IS_ERR(pa->cnfg)) {
+               err = PTR_ERR(pa->cnfg);
+               goto err_put_ctrl;
+       }
+
+       pa->irq = platform_get_irq_byname(pdev, "periph_irq");
+       if (pa->irq < 0) {
+               err = pa->irq;
+               goto err_put_ctrl;
+       }
+
+       err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
+       if (err) {
+               dev_err(&pdev->dev, "channel unspecified.\n");
+               goto err_put_ctrl;
+       }
+
+       if (channel > 5) {
+               dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
+                       channel);
+               goto err_put_ctrl;
+       }
+
+       pa->channel = channel;
+
+       err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
+       if (err) {
+               dev_err(&pdev->dev, "EE unspecified.\n");
+               goto err_put_ctrl;
+       }
+
+       if (ee > 5) {
+               dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
+               err = -EINVAL;
+               goto err_put_ctrl;
+       }
+
+       pa->ee = ee;
+
+       for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i)
+               pa->mapping_table[i] = readl_relaxed(
+                               pa->cnfg + SPMI_MAPPING_TABLE_REG(i));
+
+       /* Initialize max_apid/min_apid to the opposite bounds, during
+        * the irq domain translation, we are sure to update these */
+       pa->max_apid = 0;
+       pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
+
+       platform_set_drvdata(pdev, ctrl);
+       raw_spin_lock_init(&pa->lock);
+
+       ctrl->cmd = pmic_arb_cmd;
+       ctrl->read_cmd = pmic_arb_read_cmd;
+       ctrl->write_cmd = pmic_arb_write_cmd;
+
+       dev_dbg(&pdev->dev, "adding irq domain\n");
+       pa->domain = irq_domain_add_tree(pdev->dev.of_node,
+                                        &pmic_arb_irq_domain_ops, pa);
+       if (!pa->domain) {
+               dev_err(&pdev->dev, "unable to create irq_domain\n");
+               err = -ENOMEM;
+               goto err_put_ctrl;
+       }
+
+       irq_set_handler_data(pa->irq, pa);
+       irq_set_chained_handler(pa->irq, pmic_arb_chained_irq);
+
+       err = spmi_controller_add(ctrl);
+       if (err)
+               goto err_domain_remove;
+
+       dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n",
+               pmic_arb_base_read(pa, PMIC_ARB_VERSION));
+
+       return 0;
+
+err_domain_remove:
+       irq_set_chained_handler(pa->irq, NULL);
+       irq_set_handler_data(pa->irq, NULL);
+       irq_domain_remove(pa->domain);
+err_put_ctrl:
+       spmi_controller_put(ctrl);
+       return err;
+}
+
+static int spmi_pmic_arb_remove(struct platform_device *pdev)
+{
+       struct spmi_controller *ctrl = platform_get_drvdata(pdev);
+       struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl);
+       spmi_controller_remove(ctrl);
+       irq_set_chained_handler(pa->irq, NULL);
+       irq_set_handler_data(pa->irq, NULL);
+       irq_domain_remove(pa->domain);
+       spmi_controller_put(ctrl);
+       return 0;
+}
+
+static const struct of_device_id spmi_pmic_arb_match_table[] = {
+       { .compatible = "qcom,spmi-pmic-arb", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table);
+
+static struct platform_driver spmi_pmic_arb_driver = {
+       .probe          = spmi_pmic_arb_probe,
+       .remove         = spmi_pmic_arb_remove,
+       .driver         = {
+               .name   = "spmi_pmic_arb",
+               .owner  = THIS_MODULE,
+               .of_match_table = spmi_pmic_arb_match_table,
+       },
+};
+module_platform_driver(spmi_pmic_arb_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:spmi_pmic_arb");
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
new file mode 100644 (file)
index 0000000..6122c8f
--- /dev/null
@@ -0,0 +1,609 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spmi.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include <dt-bindings/spmi/spmi.h>
+
+static DEFINE_IDA(ctrl_ida);
+
+static void spmi_dev_release(struct device *dev)
+{
+       struct spmi_device *sdev = to_spmi_device(dev);
+       kfree(sdev);
+}
+
+static const struct device_type spmi_dev_type = {
+       .release        = spmi_dev_release,
+};
+
+static void spmi_ctrl_release(struct device *dev)
+{
+       struct spmi_controller *ctrl = to_spmi_controller(dev);
+       ida_simple_remove(&ctrl_ida, ctrl->nr);
+       kfree(ctrl);
+}
+
+static const struct device_type spmi_ctrl_type = {
+       .release        = spmi_ctrl_release,
+};
+
+#ifdef CONFIG_PM_RUNTIME
+static int spmi_runtime_suspend(struct device *dev)
+{
+       struct spmi_device *sdev = to_spmi_device(dev);
+       int err;
+
+       err = pm_generic_runtime_suspend(dev);
+       if (err)
+               return err;
+
+       return spmi_command_sleep(sdev);
+}
+
+static int spmi_runtime_resume(struct device *dev)
+{
+       struct spmi_device *sdev = to_spmi_device(dev);
+       int err;
+
+       err = spmi_command_wakeup(sdev);
+       if (err)
+               return err;
+
+       return pm_generic_runtime_resume(dev);
+}
+#endif
+
+static const struct dev_pm_ops spmi_pm_ops = {
+       SET_RUNTIME_PM_OPS(
+               spmi_runtime_suspend,
+               spmi_runtime_resume,
+               NULL
+       )
+};
+
+static int spmi_device_match(struct device *dev, struct device_driver *drv)
+{
+       if (of_driver_match_device(dev, drv))
+               return 1;
+
+       if (drv->name)
+               return strncmp(dev_name(dev), drv->name,
+                              SPMI_NAME_SIZE) == 0;
+
+       return 0;
+}
+
+/**
+ * spmi_device_add() - add a device previously constructed via spmi_device_alloc()
+ * @sdev:      spmi_device to be added
+ */
+int spmi_device_add(struct spmi_device *sdev)
+{
+       struct spmi_controller *ctrl = sdev->ctrl;
+       int err;
+
+       dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid);
+
+       err = device_add(&sdev->dev);
+       if (err < 0) {
+               dev_err(&sdev->dev, "Can't add %s, status %d\n",
+                       dev_name(&sdev->dev), err);
+               goto err_device_add;
+       }
+
+       dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev));
+
+err_device_add:
+       return err;
+}
+EXPORT_SYMBOL_GPL(spmi_device_add);
+
+/**
+ * spmi_device_remove(): remove an SPMI device
+ * @sdev:      spmi_device to be removed
+ */
+void spmi_device_remove(struct spmi_device *sdev)
+{
+       device_unregister(&sdev->dev);
+}
+EXPORT_SYMBOL_GPL(spmi_device_remove);
+
+static inline int
+spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid)
+{
+       if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type)
+               return -EINVAL;
+
+       return ctrl->cmd(ctrl, opcode, sid);
+}
+
+static inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode,
+                               u8 sid, u16 addr, u8 *buf, size_t len)
+{
+       if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type)
+               return -EINVAL;
+
+       return ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len);
+}
+
+static inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode,
+                                u8 sid, u16 addr, const u8 *buf, size_t len)
+{
+       if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type)
+               return -EINVAL;
+
+       return ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len);
+}
+
+/**
+ * spmi_register_read() - register read
+ * @sdev:      SPMI device.
+ * @addr:      slave register address (5-bit address).
+ * @buf:       buffer to be populated with data from the Slave.
+ *
+ * Reads 1 byte of data from a Slave device register.
+ */
+int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf)
+{
+       /* 5-bit register address */
+       if (addr > 0x1F)
+               return -EINVAL;
+
+       return spmi_read_cmd(sdev->ctrl, SPMI_CMD_READ, sdev->usid, addr,
+                            buf, 1);
+}
+EXPORT_SYMBOL_GPL(spmi_register_read);
+
+/**
+ * spmi_ext_register_read() - extended register read
+ * @sdev:      SPMI device.
+ * @addr:      slave register address (8-bit address).
+ * @buf:       buffer to be populated with data from the Slave.
+ * @len:       the request number of bytes to read (up to 16 bytes).
+ *
+ * Reads up to 16 bytes of data from the extended register space on a
+ * Slave device.
+ */
+int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf,
+                          size_t len)
+{
+       /* 8-bit register address, up to 16 bytes */
+       if (len == 0 || len > 16)
+               return -EINVAL;
+
+       return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READ, sdev->usid, addr,
+                            buf, len);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_read);
+
+/**
+ * spmi_ext_register_readl() - extended register read long
+ * @sdev:      SPMI device.
+ * @addr:      slave register address (16-bit address).
+ * @buf:       buffer to be populated with data from the Slave.
+ * @len:       the request number of bytes to read (up to 8 bytes).
+ *
+ * Reads up to 8 bytes of data from the extended register space on a
+ * Slave device using 16-bit address.
+ */
+int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf,
+                           size_t len)
+{
+       /* 16-bit register address, up to 8 bytes */
+       if (len == 0 || len > 8)
+               return -EINVAL;
+
+       return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READL, sdev->usid, addr,
+                            buf, len);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_readl);
+
+/**
+ * spmi_register_write() - register write
+ * @sdev:      SPMI device
+ * @addr:      slave register address (5-bit address).
+ * @data:      buffer containing the data to be transferred to the Slave.
+ *
+ * Writes 1 byte of data to a Slave device register.
+ */
+int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data)
+{
+       /* 5-bit register address */
+       if (addr > 0x1F)
+               return -EINVAL;
+
+       return spmi_write_cmd(sdev->ctrl, SPMI_CMD_WRITE, sdev->usid, addr,
+                             &data, 1);
+}
+EXPORT_SYMBOL_GPL(spmi_register_write);
+
+/**
+ * spmi_register_zero_write() - register zero write
+ * @sdev:      SPMI device.
+ * @data:      the data to be written to register 0 (7-bits).
+ *
+ * Writes data to register 0 of the Slave device.
+ */
+int spmi_register_zero_write(struct spmi_device *sdev, u8 data)
+{
+       return spmi_write_cmd(sdev->ctrl, SPMI_CMD_ZERO_WRITE, sdev->usid, 0,
+                             &data, 1);
+}
+EXPORT_SYMBOL_GPL(spmi_register_zero_write);
+
+/**
+ * spmi_ext_register_write() - extended register write
+ * @sdev:      SPMI device.
+ * @addr:      slave register address (8-bit address).
+ * @buf:       buffer containing the data to be transferred to the Slave.
+ * @len:       the request number of bytes to read (up to 16 bytes).
+ *
+ * Writes up to 16 bytes of data to the extended register space of a
+ * Slave device.
+ */
+int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, const u8 *buf,
+                           size_t len)
+{
+       /* 8-bit register address, up to 16 bytes */
+       if (len == 0 || len > 16)
+               return -EINVAL;
+
+       return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITE, sdev->usid, addr,
+                             buf, len);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_write);
+
+/**
+ * spmi_ext_register_writel() - extended register write long
+ * @sdev:      SPMI device.
+ * @addr:      slave register address (16-bit address).
+ * @buf:       buffer containing the data to be transferred to the Slave.
+ * @len:       the request number of bytes to read (up to 8 bytes).
+ *
+ * Writes up to 8 bytes of data to the extended register space of a
+ * Slave device using 16-bit address.
+ */
+int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, const u8 *buf,
+                            size_t len)
+{
+       /* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */
+       if (len == 0 || len > 8)
+               return -EINVAL;
+
+       return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITEL, sdev->usid,
+                             addr, buf, len);
+}
+EXPORT_SYMBOL_GPL(spmi_ext_register_writel);
+
+/**
+ * spmi_command_reset() - sends RESET command to the specified slave
+ * @sdev:      SPMI device.
+ *
+ * The Reset command initializes the Slave and forces all registers to
+ * their reset values. The Slave shall enter the STARTUP state after
+ * receiving a Reset command.
+ */
+int spmi_command_reset(struct spmi_device *sdev)
+{
+       return spmi_cmd(sdev->ctrl, SPMI_CMD_RESET, sdev->usid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_reset);
+
+/**
+ * spmi_command_sleep() - sends SLEEP command to the specified SPMI device
+ * @sdev:      SPMI device.
+ *
+ * The Sleep command causes the Slave to enter the user defined SLEEP state.
+ */
+int spmi_command_sleep(struct spmi_device *sdev)
+{
+       return spmi_cmd(sdev->ctrl, SPMI_CMD_SLEEP, sdev->usid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_sleep);
+
+/**
+ * spmi_command_wakeup() - sends WAKEUP command to the specified SPMI device
+ * @sdev:      SPMI device.
+ *
+ * The Wakeup command causes the Slave to move from the SLEEP state to
+ * the ACTIVE state.
+ */
+int spmi_command_wakeup(struct spmi_device *sdev)
+{
+       return spmi_cmd(sdev->ctrl, SPMI_CMD_WAKEUP, sdev->usid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_wakeup);
+
+/**
+ * spmi_command_shutdown() - sends SHUTDOWN command to the specified SPMI device
+ * @sdev:      SPMI device.
+ *
+ * The Shutdown command causes the Slave to enter the SHUTDOWN state.
+ */
+int spmi_command_shutdown(struct spmi_device *sdev)
+{
+       return spmi_cmd(sdev->ctrl, SPMI_CMD_SHUTDOWN, sdev->usid);
+}
+EXPORT_SYMBOL_GPL(spmi_command_shutdown);
+
+static int spmi_drv_probe(struct device *dev)
+{
+       const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
+       struct spmi_device *sdev = to_spmi_device(dev);
+       int err;
+
+       /* Ensure the slave is in ACTIVE state */
+       err = spmi_command_wakeup(sdev);
+       if (err)
+               goto fail_wakeup;
+
+       pm_runtime_get_noresume(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
+       err = sdrv->probe(sdev);
+       if (err)
+               goto fail_probe;
+
+       return 0;
+
+fail_probe:
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+       pm_runtime_put_noidle(dev);
+fail_wakeup:
+       return err;
+}
+
+static int spmi_drv_remove(struct device *dev)
+{
+       const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
+
+       pm_runtime_get_sync(dev);
+       sdrv->remove(to_spmi_device(dev));
+       pm_runtime_put_noidle(dev);
+
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+       pm_runtime_put_noidle(dev);
+       return 0;
+}
+
+static struct bus_type spmi_bus_type = {
+       .name           = "spmi",
+       .match          = spmi_device_match,
+       .pm             = &spmi_pm_ops,
+       .probe          = spmi_drv_probe,
+       .remove         = spmi_drv_remove,
+};
+
+/**
+ * spmi_controller_alloc() - Allocate a new SPMI device
+ * @ctrl:      associated controller
+ *
+ * Caller is responsible for either calling spmi_device_add() to add the
+ * newly allocated controller, or calling spmi_device_put() to discard it.
+ */
+struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl)
+{
+       struct spmi_device *sdev;
+
+       sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+       if (!sdev)
+               return NULL;
+
+       sdev->ctrl = ctrl;
+       device_initialize(&sdev->dev);
+       sdev->dev.parent = &ctrl->dev;
+       sdev->dev.bus = &spmi_bus_type;
+       sdev->dev.type = &spmi_dev_type;
+       return sdev;
+}
+EXPORT_SYMBOL_GPL(spmi_device_alloc);
+
+/**
+ * spmi_controller_alloc() - Allocate a new SPMI controller
+ * @parent:    parent device
+ * @size:      size of private data
+ *
+ * Caller is responsible for either calling spmi_controller_add() to add the
+ * newly allocated controller, or calling spmi_controller_put() to discard it.
+ * The allocated private data region may be accessed via
+ * spmi_controller_get_drvdata()
+ */
+struct spmi_controller *spmi_controller_alloc(struct device *parent,
+                                             size_t size)
+{
+       struct spmi_controller *ctrl;
+       int id;
+
+       if (WARN_ON(!parent))
+               return NULL;
+
+       ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL);
+       if (!ctrl)
+               return NULL;
+
+       device_initialize(&ctrl->dev);
+       ctrl->dev.type = &spmi_ctrl_type;
+       ctrl->dev.bus = &spmi_bus_type;
+       ctrl->dev.parent = parent;
+       ctrl->dev.of_node = parent->of_node;
+       spmi_controller_set_drvdata(ctrl, &ctrl[1]);
+
+       id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL);
+       if (id < 0) {
+               dev_err(parent,
+                       "unable to allocate SPMI controller identifier.\n");
+               spmi_controller_put(ctrl);
+               return NULL;
+       }
+
+       ctrl->nr = id;
+       dev_set_name(&ctrl->dev, "spmi-%d", id);
+
+       dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id);
+       return ctrl;
+}
+EXPORT_SYMBOL_GPL(spmi_controller_alloc);
+
+static void of_spmi_register_devices(struct spmi_controller *ctrl)
+{
+       struct device_node *node;
+       int err;
+
+       if (!ctrl->dev.of_node)
+               return;
+
+       for_each_available_child_of_node(ctrl->dev.of_node, node) {
+               struct spmi_device *sdev;
+               u32 reg[2];
+
+               dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name);
+
+               err = of_property_read_u32_array(node, "reg", reg, 2);
+               if (err) {
+                       dev_err(&ctrl->dev,
+                               "node %s err (%d) does not have 'reg' property\n",
+                               node->full_name, err);
+                       continue;
+               }
+
+               if (reg[1] != SPMI_USID) {
+                       dev_err(&ctrl->dev,
+                               "node %s contains unsupported 'reg' entry\n",
+                               node->full_name);
+                       continue;
+               }
+
+               if (reg[0] >= SPMI_MAX_SLAVE_ID) {
+                       dev_err(&ctrl->dev,
+                               "invalid usid on node %s\n",
+                               node->full_name);
+                       continue;
+               }
+
+               dev_dbg(&ctrl->dev, "read usid %02x\n", reg[0]);
+
+               sdev = spmi_device_alloc(ctrl);
+               if (!sdev)
+                       continue;
+
+               sdev->dev.of_node = node;
+               sdev->usid = (u8) reg[0];
+
+               err = spmi_device_add(sdev);
+               if (err) {
+                       dev_err(&sdev->dev,
+                               "failure adding device. status %d\n", err);
+                       spmi_device_put(sdev);
+               }
+       }
+}
+
+/**
+ * spmi_controller_add() - Add an SPMI controller
+ * @ctrl:      controller to be registered.
+ *
+ * Register a controller previously allocated via spmi_controller_alloc() with
+ * the SPMI core.
+ */
+int spmi_controller_add(struct spmi_controller *ctrl)
+{
+       int ret;
+
+       /* Can't register until after driver model init */
+       if (WARN_ON(!spmi_bus_type.p))
+               return -EAGAIN;
+
+       ret = device_add(&ctrl->dev);
+       if (ret)
+               return ret;
+
+       if (IS_ENABLED(CONFIG_OF))
+               of_spmi_register_devices(ctrl);
+
+       dev_dbg(&ctrl->dev, "spmi-%d registered: dev:%p\n",
+               ctrl->nr, &ctrl->dev);
+
+       return 0;
+};
+EXPORT_SYMBOL_GPL(spmi_controller_add);
+
+/* Remove a device associated with a controller */
+static int spmi_ctrl_remove_device(struct device *dev, void *data)
+{
+       struct spmi_device *spmidev = to_spmi_device(dev);
+       if (dev->type == &spmi_dev_type)
+               spmi_device_remove(spmidev);
+       return 0;
+}
+
+/**
+ * spmi_controller_remove(): remove an SPMI controller
+ * @ctrl:      controller to remove
+ *
+ * Remove a SPMI controller.  Caller is responsible for calling
+ * spmi_controller_put() to discard the allocated controller.
+ */
+void spmi_controller_remove(struct spmi_controller *ctrl)
+{
+       int dummy;
+
+       if (!ctrl)
+               return;
+
+       dummy = device_for_each_child(&ctrl->dev, NULL,
+                                     spmi_ctrl_remove_device);
+       device_del(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(spmi_controller_remove);
+
+/**
+ * spmi_driver_register() - Register client driver with SPMI core
+ * @sdrv:      client driver to be associated with client-device.
+ *
+ * This API will register the client driver with the SPMI framework.
+ * It is typically called from the driver's module-init function.
+ */
+int spmi_driver_register(struct spmi_driver *sdrv)
+{
+       sdrv->driver.bus = &spmi_bus_type;
+       return driver_register(&sdrv->driver);
+}
+EXPORT_SYMBOL_GPL(spmi_driver_register);
+
+static void __exit spmi_exit(void)
+{
+       bus_unregister(&spmi_bus_type);
+}
+module_exit(spmi_exit);
+
+static int __init spmi_init(void)
+{
+       return bus_register(&spmi_bus_type);
+}
+postcore_initcall(spmi_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SPMI module");
+MODULE_ALIAS("platform:spmi");
index 256fba7f46416fb0d5b293e2e0829872d6d344a4..1f38445014c17c85860d88bb0dac0d9086c08102 100644 (file)
@@ -190,7 +190,7 @@ static int uvesafb_exec(struct uvesafb_ktask *task)
        uvfb_tasks[seq] = task;
        mutex_unlock(&uvfb_lock);
 
-       err = cn_netlink_send(m, 0, GFP_KERNEL);
+       err = cn_netlink_send(m, 0, 0, GFP_KERNEL);
        if (err == -ESRCH) {
                /*
                 * Try to start the userspace helper if sending
@@ -204,7 +204,7 @@ static int uvesafb_exec(struct uvesafb_ktask *task)
                                        "helper is installed and executable\n");
                } else {
                        v86d_started = 1;
-                       err = cn_netlink_send(m, 0, gfp_any());
+                       err = cn_netlink_send(m, 0, 0, gfp_any());
                        if (err == -ENOBUFS)
                                err = 0;
                }
index 1b5d48c578e19208fbb2ef06f9e4788f93a0f5f5..bfb2d3f06738481d302e7edb3550addbbb6a4691 100644 (file)
@@ -869,14 +869,13 @@ static ssize_t ca91cx42_master_read(struct vme_master_resource *image,
 
        spin_lock(&image->lock);
 
-       /* The following code handles VME address alignment problem
-        * in order to assure the maximal data width cycle.
-        * We cannot use memcpy_xxx directly here because it
-        * may cut data transfer in 8-bits cycles, thus making
-        * D16 cycle impossible.
-        * From the other hand, the bridge itself assures that
-        * maximal configured data cycle is used and splits it
-        * automatically for non-aligned addresses.
+       /* The following code handles VME address alignment. We cannot use
+        * memcpy_xxx here because it may cut data transfers in to 8-bit
+        * cycles when D16 or D32 cycles are required on the VME bus.
+        * On the other hand, the bridge itself assures that the maximum data
+        * cycle configured for the transfer is used and splits it
+        * automatically for non-aligned addresses, so we don't want the
+        * overhead of needlessly forcing small transfers for the entire cycle.
         */
        if ((uintptr_t)addr & 0x1) {
                *(u8 *)buf = ioread8(addr);
@@ -896,9 +895,9 @@ static ssize_t ca91cx42_master_read(struct vme_master_resource *image,
        }
 
        count32 = (count - done) & ~0x3;
-       if (count32 > 0) {
-               memcpy_fromio(buf + done, addr + done, (unsigned int)count);
-               done += count32;
+       while (done < count32) {
+               *(u32 *)(buf + done) = ioread32(addr + done);
+               done += 4;
        }
 
        if ((count - done) & 0x2) {
@@ -930,7 +929,7 @@ static ssize_t ca91cx42_master_write(struct vme_master_resource *image,
        spin_lock(&image->lock);
 
        /* Here we apply for the same strategy we do in master_read
-        * function in order to assure D16 cycle when required.
+        * function in order to assure the correct cycles.
         */
        if ((uintptr_t)addr & 0x1) {
                iowrite8(*(u8 *)buf, addr);
@@ -950,9 +949,9 @@ static ssize_t ca91cx42_master_write(struct vme_master_resource *image,
        }
 
        count32 = (count - done) & ~0x3;
-       if (count32 > 0) {
-               memcpy_toio(addr + done, buf + done, count32);
-               done += count32;
+       while (done < count32) {
+               iowrite32(*(u32 *)(buf + done), addr + done);
+               done += 4;
        }
 
        if ((count - done) & 0x2) {
index 9911cd5fddb58415b56ad23efadef06504b822f3..06990c6a1a698c4d89c391848b185885b80767bb 100644 (file)
@@ -1276,8 +1276,8 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
        spin_lock(&image->lock);
 
        /* The following code handles VME address alignment. We cannot use
-        * memcpy_xxx directly here because it may cut small data transfers in
-        * to 8-bit cycles, thus making D16 cycle impossible.
+        * memcpy_xxx here because it may cut data transfers in to 8-bit
+        * cycles when D16 or D32 cycles are required on the VME bus.
         * On the other hand, the bridge itself assures that the maximum data
         * cycle configured for the transfer is used and splits it
         * automatically for non-aligned addresses, so we don't want the
@@ -1301,9 +1301,9 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
        }
 
        count32 = (count - done) & ~0x3;
-       if (count32 > 0) {
-               memcpy_fromio(buf + done, addr + done, count32);
-               done += count32;
+       while (done < count32) {
+               *(u32 *)(buf + done) = ioread32(addr + done);
+               done += 4;
        }
 
        if ((count - done) & 0x2) {
@@ -1363,7 +1363,7 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
        spin_lock(&image->lock);
 
        /* Here we apply for the same strategy we do in master_read
-        * function in order to assure D16 cycle when required.
+        * function in order to assure the correct cycles.
         */
        if ((uintptr_t)addr & 0x1) {
                iowrite8(*(u8 *)buf, addr);
@@ -1383,9 +1383,9 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
        }
 
        count32 = (count - done) & ~0x3;
-       if (count32 > 0) {
-               memcpy_toio(addr + done, buf + done, count32);
-               done += count32;
+       while (done < count32) {
+               iowrite32(*(u32 *)(buf + done), addr + done);
+               done += 4;
        }
 
        if ((count - done) & 0x2) {
index 4f7e1d770f81fcb6af19f665fff2ecebacca9baf..7404ad3062b7680e91a6b23f51fab7fcdbdf881d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *     dscore.c
+ *     ds2490.c  USB to one wire bridge
  *
  * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
  *
 #include "../w1_int.h"
 #include "../w1.h"
 
+/* USB Standard */
+/* USB Control request vendor type */
+#define VENDOR                         0x40
+
 /* COMMAND TYPE CODES */
 #define CONTROL_CMD                    0x00
 #define COMM_CMD                       0x01
 #define ST_HALT                                0x10  /* DS2490 is currently halted */
 #define ST_IDLE                                0x20  /* DS2490 is currently idle */
 #define ST_EPOF                                0x80
+/* Status transfer size, 16 bytes status, 16 byte result flags */
+#define ST_SIZE                                0x20
 
 /* Result Register flags */
 #define RR_DETECT                      0xA5 /* New device detected */
@@ -198,7 +204,7 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index)
        int err;
 
        err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
-                       CONTROL_CMD, 0x40, value, index, NULL, 0, 1000);
+                       CONTROL_CMD, VENDOR, value, index, NULL, 0, 1000);
        if (err < 0) {
                printk(KERN_ERR "Failed to send command control message %x.%x: err=%d.\n",
                                value, index, err);
@@ -213,7 +219,7 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index)
        int err;
 
        err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
-                       MODE_CMD, 0x40, value, index, NULL, 0, 1000);
+                       MODE_CMD, VENDOR, value, index, NULL, 0, 1000);
        if (err < 0) {
                printk(KERN_ERR "Failed to send mode control message %x.%x: err=%d.\n",
                                value, index, err);
@@ -228,7 +234,7 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index)
        int err;
 
        err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
-                       COMM_CMD, 0x40, value, index, NULL, 0, 1000);
+                       COMM_CMD, VENDOR, value, index, NULL, 0, 1000);
        if (err < 0) {
                printk(KERN_ERR "Failed to send control message %x.%x: err=%d.\n",
                                value, index, err);
@@ -246,7 +252,8 @@ static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st,
        memset(st, 0, sizeof(*st));
 
        count = 0;
-       err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_STATUS]), buf, size, &count, 100);
+       err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev,
+               dev->ep[EP_STATUS]), buf, size, &count, 100);
        if (err < 0) {
                printk(KERN_ERR "Failed to read 1-wire data from 0x%x: err=%d.\n", dev->ep[EP_STATUS], err);
                return err;
@@ -353,7 +360,7 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
        err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]),
                                buf, size, &count, 1000);
        if (err < 0) {
-               u8 buf[0x20];
+               u8 buf[ST_SIZE];
                int count;
 
                printk(KERN_INFO "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]);
@@ -398,7 +405,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit)
 {
        struct ds_status st;
        int count = 0, err = 0;
-       u8 buf[0x20];
+       u8 buf[ST_SIZE];
 
        do {
                err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0);
@@ -450,10 +457,11 @@ int ds_detect(struct ds_device *dev, struct ds_status *st)
 
 static int ds_wait_status(struct ds_device *dev, struct ds_status *st)
 {
-       u8 buf[0x20];
+       u8 buf[ST_SIZE];
        int err, count = 0;
 
        do {
+               st->status = 0;
                err = ds_recv_status_nodump(dev, st, buf, sizeof(buf));
 #if 0
                if (err >= 0) {
@@ -464,7 +472,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st)
                        printk("\n");
                }
 #endif
-       } while (!(buf[0x08] & ST_IDLE) && !(err < 0) && ++count < 100);
+       } while (!(st->status & ST_IDLE) && !(err < 0) && ++count < 100);
 
        if (err >= 16 && st->status & ST_EPOF) {
                printk(KERN_INFO "Resetting device after ST_EPOF.\n");
@@ -690,37 +698,106 @@ static int ds_write_block(struct ds_device *dev, u8 *buf, int len)
        return !(err == len);
 }
 
-#if 0
-
-static int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int conditional_search)
+static void ds9490r_search(void *data, struct w1_master *master,
+       u8 search_type, w1_slave_found_callback callback)
 {
+       /* When starting with an existing id, the first id returned will
+        * be that device (if it is still on the bus most likely).
+        *
+        * If the number of devices found is less than or equal to the
+        * search_limit, that number of IDs will be returned.  If there are
+        * more, search_limit IDs will be returned followed by a non-zero
+        * discrepency value.
+        */
+       struct ds_device *dev = data;
        int err;
        u16 value, index;
        struct ds_status st;
+       u8 st_buf[ST_SIZE];
+       int search_limit;
+       int found = 0;
+       int i;
 
-       memset(buf, 0, sizeof(buf));
+       /* DS18b20 spec, 13.16 ms per device, 75 per second, sleep for
+        * discovering 8 devices (1 bulk transfer and 1/2 FIFO size) at a time.
+        */
+       const unsigned long jtime = msecs_to_jiffies(1000*8/75);
+       /* FIFO 128 bytes, bulk packet size 64, read a multiple of the
+        * packet size.
+        */
+       u64 buf[2*64/8];
 
-       err = ds_send_data(ds_dev, (unsigned char *)&init, 8);
-       if (err)
-               return err;
+       mutex_lock(&master->bus_mutex);
 
-       ds_wait_status(ds_dev, &st);
+       /* address to start searching at */
+       if (ds_send_data(dev, (u8 *)&master->search_id, 8) < 0)
+               goto search_out;
+       master->search_id = 0;
 
-       value = COMM_SEARCH_ACCESS | COMM_IM | COMM_SM | COMM_F | COMM_RTS;
-       index = (conditional_search ? 0xEC : 0xF0) | (id_number << 8);
-       err = ds_send_control(ds_dev, value, index);
-       if (err)
-               return err;
+       value = COMM_SEARCH_ACCESS | COMM_IM | COMM_RST | COMM_SM | COMM_F |
+               COMM_RTS;
+       search_limit = master->max_slave_count;
+       if (search_limit > 255)
+               search_limit = 0;
+       index = search_type | (search_limit << 8);
+       if (ds_send_control(dev, value, index) < 0)
+               goto search_out;
 
-       ds_wait_status(ds_dev, &st);
+       do {
+               schedule_timeout(jtime);
 
-       err = ds_recv_data(ds_dev, (unsigned char *)buf, 8*id_number);
-       if (err < 0)
-               return err;
+               if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) <
+                       sizeof(st)) {
+                       break;
+               }
 
-       return err/8;
+               if (st.data_in_buffer_status) {
+                       /* Bulk in can receive partial ids, but when it does
+                        * they fail crc and will be discarded anyway.
+                        * That has only been seen when status in buffer
+                        * is 0 and bulk is read anyway, so don't read
+                        * bulk without first checking if status says there
+                        * is data to read.
+                        */
+                       err = ds_recv_data(dev, (u8 *)buf, sizeof(buf));
+                       if (err < 0)
+                               break;
+                       for (i = 0; i < err/8; ++i) {
+                               ++found;
+                               if (found <= search_limit)
+                                       callback(master, buf[i]);
+                               /* can't know if there will be a discrepancy
+                                * value after until the next id */
+                               if (found == search_limit)
+                                       master->search_id = buf[i];
+                       }
+               }
+
+               if (test_bit(W1_ABORT_SEARCH, &master->flags))
+                       break;
+       } while (!(st.status & (ST_IDLE | ST_HALT)));
+
+       /* only continue the search if some weren't found */
+       if (found <= search_limit) {
+               master->search_id = 0;
+       } else if (!test_bit(W1_WARN_MAX_COUNT, &master->flags)) {
+               /* Only max_slave_count will be scanned in a search,
+                * but it will start where it left off next search
+                * until all ids are identified and then it will start
+                * over.  A continued search will report the previous
+                * last id as the first id (provided it is still on the
+                * bus).
+                */
+               dev_info(&dev->udev->dev, "%s: max_slave_count %d reached, "
+                       "will continue next search.\n", __func__,
+                       master->max_slave_count);
+               set_bit(W1_WARN_MAX_COUNT, &master->flags);
+       }
+search_out:
+       mutex_unlock(&master->bus_mutex);
 }
 
+#if 0
 static int ds_match_access(struct ds_device *dev, u64 init)
 {
        int err;
@@ -894,6 +971,7 @@ static int ds_w1_init(struct ds_device *dev)
        dev->master.write_block = &ds9490r_write_block;
        dev->master.reset_bus   = &ds9490r_reset;
        dev->master.set_pullup  = &ds9490r_set_pullup;
+       dev->master.search      = &ds9490r_search;
 
        return w1_add_master_device(&dev->master);
 }
@@ -910,15 +988,13 @@ static int ds_probe(struct usb_interface *intf,
        struct usb_endpoint_descriptor *endpoint;
        struct usb_host_interface *iface_desc;
        struct ds_device *dev;
-       int i, err;
+       int i, err, alt;
 
-       dev = kmalloc(sizeof(struct ds_device), GFP_KERNEL);
+       dev = kzalloc(sizeof(struct ds_device), GFP_KERNEL);
        if (!dev) {
                printk(KERN_INFO "Failed to allocate new DS9490R structure.\n");
                return -ENOMEM;
        }
-       dev->spu_sleep = 0;
-       dev->spu_bit = 0;
        dev->udev = usb_get_dev(udev);
        if (!dev->udev) {
                err = -ENOMEM;
@@ -928,20 +1004,25 @@ static int ds_probe(struct usb_interface *intf,
 
        usb_set_intfdata(intf, dev);
 
-       err = usb_set_interface(dev->udev, intf->altsetting[0].desc.bInterfaceNumber, 3);
+       err = usb_reset_configuration(dev->udev);
        if (err) {
-               printk(KERN_ERR "Failed to set alternative setting 3 for %d interface: err=%d.\n",
-                               intf->altsetting[0].desc.bInterfaceNumber, err);
+               dev_err(&dev->udev->dev,
+                       "Failed to reset configuration: err=%d.\n", err);
                goto err_out_clear;
        }
 
-       err = usb_reset_configuration(dev->udev);
+       /* alternative 3, 1ms interrupt (greatly speeds search), 64 byte bulk */
+       alt = 3;
+       err = usb_set_interface(dev->udev,
+               intf->altsetting[alt].desc.bInterfaceNumber, alt);
        if (err) {
-               printk(KERN_ERR "Failed to reset configuration: err=%d.\n", err);
+               dev_err(&dev->udev->dev, "Failed to set alternative setting %d "
+                       "for %d interface: err=%d.\n", alt,
+                       intf->altsetting[alt].desc.bInterfaceNumber, err);
                goto err_out_clear;
        }
 
-       iface_desc = &intf->altsetting[0];
+       iface_desc = &intf->altsetting[alt];
        if (iface_desc->desc.bNumEndpoints != NUM_EP-1) {
                printk(KERN_INFO "Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints);
                err = -EINVAL;
index 9709b8b484bacc8fcd9ddb4c1a88e137e2afa961..1d111e56c8c8c6c4a49badcd5f92febcf5e666d3 100644 (file)
@@ -89,11 +89,22 @@ static int w1_gpio_probe_dt(struct platform_device *pdev)
                pdata->is_open_drain = 1;
 
        gpio = of_get_gpio(np, 0);
-       if (gpio < 0)
+       if (gpio < 0) {
+               if (gpio != -EPROBE_DEFER)
+                       dev_err(&pdev->dev,
+                                       "Failed to parse gpio property for data pin (%d)\n",
+                                       gpio);
+
                return gpio;
+       }
        pdata->pin = gpio;
 
-       pdata->ext_pullup_enable_pin = of_get_gpio(np, 1);
+       gpio = of_get_gpio(np, 1);
+       if (gpio == -EPROBE_DEFER)
+               return gpio;
+       /* ignore other errors as the pullup gpio is optional */
+       pdata->ext_pullup_enable_pin = gpio;
+
        pdev->dev.platform_data = pdata;
 
        return 0;
@@ -107,10 +118,8 @@ static int w1_gpio_probe(struct platform_device *pdev)
 
        if (of_have_populated_dt()) {
                err = w1_gpio_probe_dt(pdev);
-               if (err < 0) {
-                       dev_err(&pdev->dev, "Failed to parse DT\n");
+               if (err < 0)
                        return err;
-               }
        }
 
        pdata = dev_get_platdata(&pdev->dev);
index 8b5ff33f72cf461ce3e2f774883f87d0a1762731..1f11a20a8ab9dac08fd498da405f3068c48415ab 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/sched.h>
 #include <linux/device.h>
 #include <linux/types.h>
+#include <linux/slab.h>
 #include <linux/delay.h>
 
 #include "../w1.h"
@@ -58,6 +59,19 @@ MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00));
 static int w1_strong_pullup = 1;
 module_param_named(strong_pullup, w1_strong_pullup, int, 0);
 
+static int w1_therm_add_slave(struct w1_slave *sl)
+{
+       sl->family_data = kzalloc(9, GFP_KERNEL);
+       if (!sl->family_data)
+               return -ENOMEM;
+       return 0;
+}
+
+static void w1_therm_remove_slave(struct w1_slave *sl)
+{
+       kfree(sl->family_data);
+       sl->family_data = NULL;
+}
 
 static ssize_t w1_slave_show(struct device *device,
        struct device_attribute *attr, char *buf);
@@ -71,6 +85,8 @@ static struct attribute *w1_therm_attrs[] = {
 ATTRIBUTE_GROUPS(w1_therm);
 
 static struct w1_family_ops w1_therm_fops = {
+       .add_slave      = w1_therm_add_slave,
+       .remove_slave   = w1_therm_remove_slave,
        .groups         = w1_therm_groups,
 };
 
@@ -253,12 +269,13 @@ static ssize_t w1_slave_show(struct device *device,
        c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
                           crc, (verdict) ? "YES" : "NO");
        if (verdict)
-               memcpy(sl->rom, rom, sizeof(sl->rom));
+               memcpy(sl->family_data, rom, sizeof(rom));
        else
                dev_warn(device, "Read failed CRC check\n");
 
        for (i = 0; i < 9; ++i)
-               c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]);
+               c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ",
+                             ((u8 *)sl->family_data)[i]);
 
        c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
                w1_convert_temp(rom, sl->family->fid));
index 66efa96c460354b6fb8087634be49e3a25643fd0..b96f61b15dc65e866564a075686072eddc49f905 100644 (file)
@@ -46,18 +46,29 @@ MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
 MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol.");
 
 static int w1_timeout = 10;
-int w1_max_slave_count = 10;
+int w1_max_slave_count = 64;
 int w1_max_slave_ttl = 10;
 
 module_param_named(timeout, w1_timeout, int, 0);
+MODULE_PARM_DESC(timeout, "time in seconds between automatic slave searches");
+/* A search stops when w1_max_slave_count devices have been found in that
+ * search.  The next search will start over and detect the same set of devices
+ * on a static 1-wire bus.  Memory is not allocated based on this number, just
+ * on the number of devices known to the kernel.  Having a high number does not
+ * consume additional resources.  As a special case, if there is only one
+ * device on the network and w1_max_slave_count is set to 1, the device id can
+ * be read directly skipping the normal slower search process.
+ */
 module_param_named(max_slave_count, w1_max_slave_count, int, 0);
+MODULE_PARM_DESC(max_slave_count,
+       "maximum number of slaves detected in a search");
 module_param_named(slave_ttl, w1_max_slave_ttl, int, 0);
+MODULE_PARM_DESC(slave_ttl,
+       "Number of searches not seeing a slave before it will be removed");
 
 DEFINE_MUTEX(w1_mlock);
 LIST_HEAD(w1_masters);
 
-static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn);
-
 static int w1_master_match(struct device *dev, struct device_driver *drv)
 {
        return 1;
@@ -81,19 +92,10 @@ static void w1_slave_release(struct device *dev)
 {
        struct w1_slave *sl = dev_to_w1_slave(dev);
 
-       dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name);
-
-       while (atomic_read(&sl->refcnt)) {
-               dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n",
-                               sl->name, atomic_read(&sl->refcnt));
-               if (msleep_interruptible(1000))
-                       flush_signals(current);
-       }
+       dev_dbg(dev, "%s: Releasing %s [%p]\n", __func__, sl->name, sl);
 
        w1_family_put(sl->family);
        sl->master->slave_count--;
-
-       complete(&sl->released);
 }
 
 static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -243,7 +245,9 @@ static ssize_t w1_master_attribute_store_search(struct device * dev,
        mutex_lock(&md->mutex);
        md->search_count = tmp;
        mutex_unlock(&md->mutex);
-       wake_up_process(md->thread);
+       /* Only wake if it is going to be searching. */
+       if (tmp)
+               wake_up_process(md->thread);
 
        return count;
 }
@@ -277,7 +281,6 @@ static ssize_t w1_master_attribute_store_pullup(struct device *dev,
        mutex_lock(&md->mutex);
        md->enable_pullup = tmp;
        mutex_unlock(&md->mutex);
-       wake_up_process(md->thread);
 
        return count;
 }
@@ -314,6 +317,24 @@ static ssize_t w1_master_attribute_show_timeout(struct device *dev, struct devic
        return count;
 }
 
+static ssize_t w1_master_attribute_store_max_slave_count(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       int tmp;
+       struct w1_master *md = dev_to_w1_master(dev);
+
+       if (kstrtoint(buf, 0, &tmp) == -EINVAL || tmp < 1)
+               return -EINVAL;
+
+       mutex_lock(&md->mutex);
+       md->max_slave_count = tmp;
+       /* allow each time the max_slave_count is updated */
+       clear_bit(W1_WARN_MAX_COUNT, &md->flags);
+       mutex_unlock(&md->mutex);
+
+       return count;
+}
+
 static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct w1_master *md = dev_to_w1_master(dev);
@@ -352,23 +373,20 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev,
 {
        struct w1_master *md = dev_to_w1_master(dev);
        int c = PAGE_SIZE;
+       struct list_head *ent, *n;
+       struct w1_slave *sl = NULL;
 
-       mutex_lock(&md->mutex);
-
-       if (md->slave_count == 0)
-               c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n");
-       else {
-               struct list_head *ent, *n;
-               struct w1_slave *sl;
+       mutex_lock(&md->list_mutex);
 
-               list_for_each_safe(ent, n, &md->slist) {
-                       sl = list_entry(ent, struct w1_slave, w1_slave_entry);
+       list_for_each_safe(ent, n, &md->slist) {
+               sl = list_entry(ent, struct w1_slave, w1_slave_entry);
 
-                       c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name);
-               }
+               c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name);
        }
+       if (!sl)
+               c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n");
 
-       mutex_unlock(&md->mutex);
+       mutex_unlock(&md->list_mutex);
 
        return PAGE_SIZE - c;
 }
@@ -422,19 +440,22 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count,
 }
 
 /* Searches the slaves in the w1_master and returns a pointer or NULL.
- * Note: must hold the mutex
+ * Note: must not hold list_mutex
  */
-static struct w1_slave *w1_slave_search_device(struct w1_master *dev,
+struct w1_slave *w1_slave_search_device(struct w1_master *dev,
        struct w1_reg_num *rn)
 {
        struct w1_slave *sl;
+       mutex_lock(&dev->list_mutex);
        list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
                if (sl->reg_num.family == rn->family &&
                                sl->reg_num.id == rn->id &&
                                sl->reg_num.crc == rn->crc) {
+                       mutex_unlock(&dev->list_mutex);
                        return sl;
                }
        }
+       mutex_unlock(&dev->list_mutex);
        return NULL;
 }
 
@@ -491,7 +512,10 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev,
        mutex_lock(&md->mutex);
        sl = w1_slave_search_device(md, &rn);
        if (sl) {
-               w1_slave_detach(sl);
+               result = w1_slave_detach(sl);
+               /* refcnt 0 means it was detached in the call */
+               if (result == 0)
+                       result = count;
        } else {
                dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family,
                        (unsigned long long)rn.id);
@@ -516,7 +540,7 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev,
 static W1_MASTER_ATTR_RO(name, S_IRUGO);
 static W1_MASTER_ATTR_RO(slaves, S_IRUGO);
 static W1_MASTER_ATTR_RO(slave_count, S_IRUGO);
-static W1_MASTER_ATTR_RO(max_slave_count, S_IRUGO);
+static W1_MASTER_ATTR_RW(max_slave_count, S_IRUGO | S_IWUSR | S_IWGRP);
 static W1_MASTER_ATTR_RO(attempts, S_IRUGO);
 static W1_MASTER_ATTR_RO(timeout, S_IRUGO);
 static W1_MASTER_ATTR_RO(pointer, S_IRUGO);
@@ -686,12 +710,14 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
        dev_set_uevent_suppress(&sl->dev, false);
        kobject_uevent(&sl->dev.kobj, KOBJ_ADD);
 
+       mutex_lock(&sl->master->list_mutex);
        list_add_tail(&sl->w1_slave_entry, &sl->master->slist);
+       mutex_unlock(&sl->master->list_mutex);
 
        return 0;
 }
 
-static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
+int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
 {
        struct w1_slave *sl;
        struct w1_family *f;
@@ -713,8 +739,8 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
 
        memset(&msg, 0, sizeof(msg));
        memcpy(&sl->reg_num, rn, sizeof(sl->reg_num));
-       atomic_set(&sl->refcnt, 0);
-       init_completion(&sl->released);
+       atomic_set(&sl->refcnt, 1);
+       atomic_inc(&sl->master->refcnt);
 
        /* slave modules need to be loaded in a context with unlocked mutex */
        mutex_unlock(&dev->mutex);
@@ -754,23 +780,48 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
        return 0;
 }
 
-void w1_slave_detach(struct w1_slave *sl)
+int w1_unref_slave(struct w1_slave *sl)
 {
-       struct w1_netlink_msg msg;
-
-       dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, sl->name, sl);
-
-       list_del(&sl->w1_slave_entry);
-
-       memset(&msg, 0, sizeof(msg));
-       memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id));
-       msg.type = W1_SLAVE_REMOVE;
-       w1_netlink_send(sl->master, &msg);
-
-       device_unregister(&sl->dev);
+       struct w1_master *dev = sl->master;
+       int refcnt;
+       mutex_lock(&dev->list_mutex);
+       refcnt = atomic_sub_return(1, &sl->refcnt);
+       if (refcnt == 0) {
+               struct w1_netlink_msg msg;
+
+               dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__,
+                       sl->name, sl);
+
+               list_del(&sl->w1_slave_entry);
+
+               memset(&msg, 0, sizeof(msg));
+               memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id));
+               msg.type = W1_SLAVE_REMOVE;
+               w1_netlink_send(sl->master, &msg);
+
+               device_unregister(&sl->dev);
+               #ifdef DEBUG
+               memset(sl, 0, sizeof(*sl));
+               #endif
+               kfree(sl);
+       }
+       atomic_dec(&dev->refcnt);
+       mutex_unlock(&dev->list_mutex);
+       return refcnt;
+}
 
-       wait_for_completion(&sl->released);
-       kfree(sl);
+int w1_slave_detach(struct w1_slave *sl)
+{
+       /* Only detach a slave once as it decreases the refcnt each time. */
+       int destroy_now;
+       mutex_lock(&sl->master->list_mutex);
+       destroy_now = !test_bit(W1_SLAVE_DETACH, &sl->flags);
+       set_bit(W1_SLAVE_DETACH, &sl->flags);
+       mutex_unlock(&sl->master->list_mutex);
+
+       if (destroy_now)
+               destroy_now = !w1_unref_slave(sl);
+       return destroy_now ? 0 : -EBUSY;
 }
 
 struct w1_master *w1_search_master_id(u32 id)
@@ -799,7 +850,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id)
 
        mutex_lock(&w1_mlock);
        list_for_each_entry(dev, &w1_masters, w1_master_entry) {
-               mutex_lock(&dev->mutex);
+               mutex_lock(&dev->list_mutex);
                list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
                        if (sl->reg_num.family == id->family &&
                                        sl->reg_num.id == id->id &&
@@ -810,7 +861,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id)
                                break;
                        }
                }
-               mutex_unlock(&dev->mutex);
+               mutex_unlock(&dev->list_mutex);
 
                if (found)
                        break;
@@ -830,6 +881,7 @@ void w1_reconnect_slaves(struct w1_family *f, int attach)
                dev_dbg(&dev->dev, "Reconnecting slaves in device %s "
                        "for family %02x.\n", dev->name, f->fid);
                mutex_lock(&dev->mutex);
+               mutex_lock(&dev->list_mutex);
                list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
                        /* If it is a new family, slaves with the default
                         * family driver and are that family will be
@@ -841,14 +893,19 @@ void w1_reconnect_slaves(struct w1_family *f, int attach)
                                (!attach && sl->family->fid == f->fid)) {
                                struct w1_reg_num rn;
 
+                               mutex_unlock(&dev->list_mutex);
                                memcpy(&rn, &sl->reg_num, sizeof(rn));
-                               w1_slave_detach(sl);
-
-                               w1_attach_slave_device(dev, &rn);
+                               /* If it was already in use let the automatic
+                                * scan pick it up again later.
+                                */
+                               if (!w1_slave_detach(sl))
+                                       w1_attach_slave_device(dev, &rn);
+                               mutex_lock(&dev->list_mutex);
                        }
                }
                dev_dbg(&dev->dev, "Reconnecting slaves in device %s "
                        "has been finished.\n", dev->name);
+               mutex_unlock(&dev->list_mutex);
                mutex_unlock(&dev->mutex);
        }
        mutex_unlock(&w1_mlock);
@@ -876,7 +933,12 @@ void w1_slave_found(struct w1_master *dev, u64 rn)
 }
 
 /**
- * Performs a ROM Search & registers any devices found.
+ * w1_search() - Performs a ROM Search & registers any devices found.
+ * @dev: The master device to search
+ * @search_type: W1_SEARCH to search all devices, or W1_ALARM_SEARCH
+ * to return only devices in the alarmed state
+ * @cb: Function to call when a device is found
+ *
  * The 1-wire search is a simple binary tree search.
  * For each bit of the address, we read two bits and write one bit.
  * The bit written will put to sleep all devies that don't match that bit.
@@ -886,8 +948,6 @@ void w1_slave_found(struct w1_master *dev, u64 rn)
  *
  * See "Application note 187 1-wire search algorithm" at www.maxim-ic.com
  *
- * @dev        The master device to search
- * @cb         Function to call when a device is found
  */
 void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb)
 {
@@ -898,7 +958,8 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb
        u8  triplet_ret = 0;
 
        search_bit = 0;
-       rn = last_rn = 0;
+       rn = dev->search_id;
+       last_rn = 0;
        last_device = 0;
        last_zero = -1;
 
@@ -945,7 +1006,7 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb
                        else
                                search_bit = ((last_rn >> i) & 0x1);
 
-                       /** Read two bits and write one bit */
+                       /* Read two bits and write one bit */
                        triplet_ret = w1_triplet(dev, search_bit);
 
                        /* quit if no device responded */
@@ -960,8 +1021,7 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb
                        tmp64 = (triplet_ret >> 2);
                        rn |= (tmp64 << i);
 
-                       /* ensure we're called from kthread and not by netlink callback */
-                       if (!dev->priv && kthread_should_stop()) {
+                       if (test_bit(W1_ABORT_SEARCH, &dev->flags)) {
                                mutex_unlock(&dev->bus_mutex);
                                dev_dbg(&dev->dev, "Abort w1_search\n");
                                return;
@@ -970,11 +1030,30 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb
                mutex_unlock(&dev->bus_mutex);
 
                if ( (triplet_ret & 0x03) != 0x03 ) {
-                       if ( (desc_bit == last_zero) || (last_zero < 0))
+                       if ((desc_bit == last_zero) || (last_zero < 0)) {
                                last_device = 1;
+                               dev->search_id = 0;
+                       } else {
+                               dev->search_id = rn;
+                       }
                        desc_bit = last_zero;
                        cb(dev, rn);
                }
+
+               if (!last_device && slave_count == dev->max_slave_count &&
+                       !test_bit(W1_WARN_MAX_COUNT, &dev->flags)) {
+                       /* Only max_slave_count will be scanned in a search,
+                        * but it will start where it left off next search
+                        * until all ids are identified and then it will start
+                        * over.  A continued search will report the previous
+                        * last id as the first id (provided it is still on the
+                        * bus).
+                        */
+                       dev_info(&dev->dev, "%s: max_slave_count %d reached, "
+                               "will continue next search.\n", __func__,
+                               dev->max_slave_count);
+                       set_bit(W1_WARN_MAX_COUNT, &dev->flags);
+               }
        }
 }
 
@@ -983,17 +1062,24 @@ void w1_search_process_cb(struct w1_master *dev, u8 search_type,
 {
        struct w1_slave *sl, *sln;
 
+       mutex_lock(&dev->list_mutex);
        list_for_each_entry(sl, &dev->slist, w1_slave_entry)
                clear_bit(W1_SLAVE_ACTIVE, &sl->flags);
+       mutex_unlock(&dev->list_mutex);
 
        w1_search_devices(dev, search_type, cb);
 
+       mutex_lock(&dev->list_mutex);
        list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
-               if (!test_bit(W1_SLAVE_ACTIVE, &sl->flags) && !--sl->ttl)
+               if (!test_bit(W1_SLAVE_ACTIVE, &sl->flags) && !--sl->ttl) {
+                       mutex_unlock(&dev->list_mutex);
                        w1_slave_detach(sl);
+                       mutex_lock(&dev->list_mutex);
+               }
                else if (test_bit(W1_SLAVE_ACTIVE, &sl->flags))
                        sl->ttl = dev->slave_ttl;
        }
+       mutex_unlock(&dev->list_mutex);
 
        if (dev->search_count > 0)
                dev->search_count--;
@@ -1004,6 +1090,32 @@ static void w1_search_process(struct w1_master *dev, u8 search_type)
        w1_search_process_cb(dev, search_type, w1_slave_found);
 }
 
+/**
+ * w1_process_callbacks() - execute each dev->async_list callback entry
+ * @dev: w1_master device
+ *
+ * Return: 1 if there were commands to executed 0 otherwise
+ */
+int w1_process_callbacks(struct w1_master *dev)
+{
+       int ret = 0;
+       struct w1_async_cmd *async_cmd, *async_n;
+
+       /* The list can be added to in another thread, loop until it is empty */
+       while (!list_empty(&dev->async_list)) {
+               list_for_each_entry_safe(async_cmd, async_n, &dev->async_list,
+                       async_entry) {
+                       /* drop the lock, if it is a search it can take a long
+                        * time */
+                       mutex_unlock(&dev->list_mutex);
+                       async_cmd->cb(dev, async_cmd);
+                       ret = 1;
+                       mutex_lock(&dev->list_mutex);
+               }
+       }
+       return ret;
+}
+
 int w1_process(void *data)
 {
        struct w1_master *dev = (struct w1_master *) data;
@@ -1011,23 +1123,46 @@ int w1_process(void *data)
         * time can be calculated in jiffies once.
         */
        const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000);
+       /* remainder if it woke up early */
+       unsigned long jremain = 0;
 
-       while (!kthread_should_stop()) {
-               if (dev->search_count) {
+       for (;;) {
+
+               if (!jremain && dev->search_count) {
                        mutex_lock(&dev->mutex);
                        w1_search_process(dev, W1_SEARCH);
                        mutex_unlock(&dev->mutex);
                }
 
+               mutex_lock(&dev->list_mutex);
+               /* Note, w1_process_callback drops the lock while processing,
+                * but locks it again before returning.
+                */
+               if (!w1_process_callbacks(dev) && jremain) {
+                       /* a wake up is either to stop the thread, process
+                        * callbacks, or search, it isn't process callbacks, so
+                        * schedule a search.
+                        */
+                       jremain = 1;
+               }
+
                try_to_freeze();
                __set_current_state(TASK_INTERRUPTIBLE);
 
+               /* hold list_mutex until after interruptible to prevent loosing
+                * the wakeup signal when async_cmd is added.
+                */
+               mutex_unlock(&dev->list_mutex);
+
                if (kthread_should_stop())
                        break;
 
                /* Only sleep when the search is active. */
-               if (dev->search_count)
-                       schedule_timeout(jtime);
+               if (dev->search_count) {
+                       if (!jremain)
+                               jremain = jtime;
+                       jremain = schedule_timeout(jremain);
+               }
                else
                        schedule();
        }
index ca8081a101d64ac34fadbc872aeaf3e0898c4fc8..734dab7fc687a743e43e8f391b0922f55139b78e 100644 (file)
 #ifndef __W1_H
 #define __W1_H
 
+/**
+ * struct w1_reg_num - broken out slave device id
+ *
+ * @family: identifies the type of device
+ * @id: along with family is the unique device id
+ * @crc: checksum of the other bytes
+ */
 struct w1_reg_num
 {
 #if defined(__LITTLE_ENDIAN_BITFIELD)
@@ -58,7 +65,24 @@ struct w1_reg_num
 #define W1_RESUME_CMD          0xA5
 
 #define W1_SLAVE_ACTIVE                0
+#define W1_SLAVE_DETACH                1
 
+/**
+ * struct w1_slave - holds a single slave device on the bus
+ *
+ * @owner: Points to the one wire "wire" kernel module.
+ * @name: Device id is ascii.
+ * @w1_slave_entry: data for the linked list
+ * @reg_num: the slave id in binary
+ * @refcnt: reference count, delete when 0
+ * @flags: bit flags for W1_SLAVE_ACTIVE W1_SLAVE_DETACH
+ * @ttl: decrement per search this slave isn't found, deatch at 0
+ * @master: bus which this slave is on
+ * @family: module for device family type
+ * @family_data: pointer for use by the family module
+ * @dev: kernel device identifier
+ *
+ */
 struct w1_slave
 {
        struct module           *owner;
@@ -66,7 +90,6 @@ struct w1_slave
        struct list_head        w1_slave_entry;
        struct w1_reg_num       reg_num;
        atomic_t                refcnt;
-       u8                      rom[9];
        int                     ttl;
        unsigned long           flags;
 
@@ -74,99 +97,146 @@ struct w1_slave
        struct w1_family        *family;
        void                    *family_data;
        struct device           dev;
-       struct completion       released;
 };
 
 typedef void (*w1_slave_found_callback)(struct w1_master *, u64);
 
 
 /**
+ * struct w1_bus_master - operations available on a bus master
+ *
+ * @data: the first parameter in all the functions below
+ *
+ * @read_bit: Sample the line level @return the level read (0 or 1)
+ *
+ * @write_bit: Sets the line level
+ *
+ * @touch_bit: the lowest-level function for devices that really support the
+ * 1-wire protocol.
+ * touch_bit(0) = write-0 cycle
+ * touch_bit(1) = write-1 / read cycle
+ * @return the bit read (0 or 1)
+ *
+ * @read_byte: Reads a bytes. Same as 8 touch_bit(1) calls.
+ * @return the byte read
+ *
+ * @write_byte: Writes a byte. Same as 8 touch_bit(x) calls.
+ *
+ * @read_block: Same as a series of read_byte() calls
+ * @return the number of bytes read
+ *
+ * @write_block: Same as a series of write_byte() calls
+ *
+ * @triplet: Combines two reads and a smart write for ROM searches
+ * @return bit0=Id bit1=comp_id bit2=dir_taken
+ *
+ * @reset_bus: long write-0 with a read for the presence pulse detection
+ * @return -1=Error, 0=Device present, 1=No device present
+ *
+ * @set_pullup: Put out a strong pull-up pulse of the specified duration.
+ * @return -1=Error, 0=completed
+ *
+ * @search: Really nice hardware can handles the different types of ROM search
+ * w1_master* is passed to the slave found callback.
+ * u8 is search_type, W1_SEARCH or W1_ALARM_SEARCH
+ *
  * Note: read_bit and write_bit are very low level functions and should only
  * be used with hardware that doesn't really support 1-wire operations,
  * like a parallel/serial port.
  * Either define read_bit and write_bit OR define, at minimum, touch_bit and
  * reset_bus.
+ *
  */
 struct w1_bus_master
 {
-       /** the first parameter in all the functions below */
        void            *data;
 
-       /**
-        * Sample the line level
-        * @return the level read (0 or 1)
-        */
        u8              (*read_bit)(void *);
 
-       /** Sets the line level */
        void            (*write_bit)(void *, u8);
 
-       /**
-        * touch_bit is the lowest-level function for devices that really
-        * support the 1-wire protocol.
-        * touch_bit(0) = write-0 cycle
-        * touch_bit(1) = write-1 / read cycle
-        * @return the bit read (0 or 1)
-        */
        u8              (*touch_bit)(void *, u8);
 
-       /**
-        * Reads a bytes. Same as 8 touch_bit(1) calls.
-        * @return the byte read
-        */
        u8              (*read_byte)(void *);
 
-       /**
-        * Writes a byte. Same as 8 touch_bit(x) calls.
-        */
        void            (*write_byte)(void *, u8);
 
-       /**
-        * Same as a series of read_byte() calls
-        * @return the number of bytes read
-        */
        u8              (*read_block)(void *, u8 *, int);
 
-       /** Same as a series of write_byte() calls */
        void            (*write_block)(void *, const u8 *, int);
 
-       /**
-        * Combines two reads and a smart write for ROM searches
-        * @return bit0=Id bit1=comp_id bit2=dir_taken
-        */
        u8              (*triplet)(void *, u8);
 
-       /**
-        * long write-0 with a read for the presence pulse detection
-        * @return -1=Error, 0=Device present, 1=No device present
-        */
        u8              (*reset_bus)(void *);
 
-       /**
-        * Put out a strong pull-up pulse of the specified duration.
-        * @return -1=Error, 0=completed
-        */
        u8              (*set_pullup)(void *, int);
 
-       /** Really nice hardware can handles the different types of ROM search
-        *  w1_master* is passed to the slave found callback.
-        */
        void            (*search)(void *, struct w1_master *,
                u8, w1_slave_found_callback);
 };
 
+/**
+ * enum w1_master_flags - bitfields used in w1_master.flags
+ * @W1_ABORT_SEARCH: abort searching early on shutdown
+ * @W1_WARN_MAX_COUNT: limit warning when the maximum count is reached
+ */
+enum w1_master_flags {
+       W1_ABORT_SEARCH = 0,
+       W1_WARN_MAX_COUNT = 1,
+};
+
+/**
+ * struct w1_master - one per bus master
+ * @w1_master_entry:   master linked list
+ * @owner:             module owner
+ * @name:              dynamically allocate bus name
+ * @list_mutex:                protect slist and async_list
+ * @slist:             linked list of slaves
+ * @async_list:                linked list of netlink commands to execute
+ * @max_slave_count:   maximum number of slaves to search for at a time
+ * @slave_count:       current number of slaves known
+ * @attempts:          number of searches ran
+ * @slave_ttl:         number of searches before a slave is timed out
+ * @initialized:       prevent init/removal race conditions
+ * @id:                        w1 bus number
+ * @search_count:      number of automatic searches to run, -1 unlimited
+ * @search_id:         allows continuing a search
+ * @refcnt:            reference count
+ * @priv:              private data storage
+ * @priv_size:         size allocated
+ * @enable_pullup:     allows a strong pullup
+ * @pullup_duration:   time for the next strong pullup
+ * @flags:             one of w1_master_flags
+ * @thread:            thread for bus search and netlink commands
+ * @mutex:             protect most of w1_master
+ * @bus_mutex:         pretect concurrent bus access
+ * @driver:            sysfs driver
+ * @dev:               sysfs device
+ * @bus_master:                io operations available
+ * @seq:               sequence number used for netlink broadcasts
+ * @portid:            destination for the current netlink command
+ */
 struct w1_master
 {
        struct list_head        w1_master_entry;
        struct module           *owner;
        unsigned char           name[W1_MAXNAMELEN];
+       /* list_mutex protects just slist and async_list so slaves can be
+        * searched for and async commands added while the master has
+        * w1_master.mutex locked and is operating on the bus.
+        * lock order w1_mlock, w1_master.mutex, w1_master.list_mutex
+        */
+       struct mutex            list_mutex;
        struct list_head        slist;
+       struct list_head        async_list;
        int                     max_slave_count, slave_count;
        unsigned long           attempts;
        int                     slave_ttl;
        int                     initialized;
        u32                     id;
        int                     search_count;
+       /* id to start searching on, to continue a search or 0 to restart */
+       u64                     search_id;
 
        atomic_t                refcnt;
 
@@ -178,6 +248,8 @@ struct w1_master
        /** 5V strong pullup duration in milliseconds, zero disabled. */
        int                     pullup_duration;
 
+       long                    flags;
+
        struct task_struct      *thread;
        struct mutex            mutex;
        struct mutex            bus_mutex;
@@ -188,16 +260,41 @@ struct w1_master
        struct w1_bus_master    *bus_master;
 
        u32                     seq;
+       /* port id to send netlink responses to.  The value is temporarily
+        * stored here while processing a message, set after locking the
+        * mutex, zero before unlocking the mutex.
+        */
+       u32                     portid;
+};
+
+/**
+ * struct w1_async_cmd - execute callback from the w1_process kthread
+ * @async_entry: link entry
+ * @cb: callback function, must list_del and destroy this list before
+ * returning
+ *
+ * When inserted into the w1_master async_list, w1_process will execute
+ * the callback.  Embed this into the structure with the command details.
+ */
+struct w1_async_cmd {
+       struct list_head        async_entry;
+       void (*cb)(struct w1_master *dev, struct w1_async_cmd *async_cmd);
 };
 
 int w1_create_master_attributes(struct w1_master *);
 void w1_destroy_master_attributes(struct w1_master *master);
 void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb);
 void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb);
+/* call w1_unref_slave to release the reference counts w1_search_slave added */
 struct w1_slave *w1_search_slave(struct w1_reg_num *id);
+/* decrements the reference on sl->master and sl, and cleans up if zero
+ * returns the reference count after it has been decremented */
+int w1_unref_slave(struct w1_slave *sl);
 void w1_slave_found(struct w1_master *dev, u64 rn);
 void w1_search_process_cb(struct w1_master *dev, u8 search_type,
        w1_slave_found_callback cb);
+struct w1_slave *w1_slave_search_device(struct w1_master *dev,
+       struct w1_reg_num *rn);
 struct w1_master *w1_search_master_id(u32 id);
 
 /* Disconnect and reconnect devices in the given family.  Used for finding
@@ -206,7 +303,9 @@ struct w1_master *w1_search_master_id(u32 id);
  * has just been registered, to 0 when it has been unregistered.
  */
 void w1_reconnect_slaves(struct w1_family *f, int attach);
-void w1_slave_detach(struct w1_slave *sl);
+int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn);
+/* 0 success, otherwise EBUSY */
+int w1_slave_detach(struct w1_slave *sl);
 
 u8 w1_triplet(struct w1_master *dev, int bdir);
 void w1_write_8(struct w1_master *, u8);
@@ -242,6 +341,7 @@ extern int w1_max_slave_ttl;
 extern struct list_head w1_masters;
 extern struct mutex w1_mlock;
 
+extern int w1_process_callbacks(struct w1_master *dev);
 extern int w1_process(void *);
 
 #endif /* __KERNEL__ */
index e9309778ee72c84e72581b5e3ae54c88816cbb64..3bff6b37b4727f5ad692d31e6fcbdf62ab07b34a 100644 (file)
 DEFINE_SPINLOCK(w1_flock);
 static LIST_HEAD(w1_families);
 
+/**
+ * w1_register_family() - register a device family driver
+ * @newf:      family to register
+ */
 int w1_register_family(struct w1_family *newf)
 {
        struct list_head *ent, *n;
@@ -59,6 +63,10 @@ int w1_register_family(struct w1_family *newf)
        return ret;
 }
 
+/**
+ * w1_unregister_family() - unregister a device family driver
+ * @fent:      family to unregister
+ */
 void w1_unregister_family(struct w1_family *fent)
 {
        struct list_head *ent, *n;
index 4ad0e81b640460a14e25841a82b6b3eb2f9588bd..26ca1343055bc91a7068c54e01f347d42990ae92 100644 (file)
 
 struct w1_slave;
 
+/**
+ * struct w1_family_ops - operations for a family type
+ * @add_slave: add_slave
+ * @remove_slave: remove_slave
+ * @groups: sysfs group
+ */
 struct w1_family_ops
 {
        int  (* add_slave)(struct w1_slave *);
@@ -55,6 +61,13 @@ struct w1_family_ops
        const struct attribute_group **groups;
 };
 
+/**
+ * struct w1_family - reference counted family structure.
+ * @family_entry:      family linked list
+ * @fid:               8 bit family identifier
+ * @fops:              operations for this family
+ * @refcnt:            reference counter
+ */
 struct w1_family
 {
        struct list_head        family_entry;
index 590bd8a7cd1bf3f071b145a7a48c47c30cabe510..9b084db739c745b9940cd68ee268542f76820695 100644 (file)
@@ -75,8 +75,10 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
        atomic_set(&dev->refcnt, 2);
 
        INIT_LIST_HEAD(&dev->slist);
+       INIT_LIST_HEAD(&dev->async_list);
        mutex_init(&dev->mutex);
        mutex_init(&dev->bus_mutex);
+       mutex_init(&dev->list_mutex);
 
        memcpy(&dev->dev, device, sizeof(struct device));
        dev_set_name(&dev->dev, "w1_bus_master%u", dev->id);
@@ -103,6 +105,10 @@ static void w1_free_dev(struct w1_master *dev)
        device_unregister(&dev->dev);
 }
 
+/**
+ * w1_add_master_device() - registers a new master device
+ * @master:    master bus device to register
+ */
 int w1_add_master_device(struct w1_bus_master *master)
 {
        struct w1_master *dev, *entry;
@@ -172,6 +178,7 @@ int w1_add_master_device(struct w1_bus_master *master)
 
 #if 0 /* Thread cleanup code, not required currently. */
 err_out_kill_thread:
+       set_bit(W1_ABORT_SEARCH, &dev->flags);
        kthread_stop(dev->thread);
 #endif
 err_out_rm_attr:
@@ -187,16 +194,22 @@ void __w1_remove_master_device(struct w1_master *dev)
        struct w1_netlink_msg msg;
        struct w1_slave *sl, *sln;
 
-       kthread_stop(dev->thread);
-
        mutex_lock(&w1_mlock);
        list_del(&dev->w1_master_entry);
        mutex_unlock(&w1_mlock);
 
+       set_bit(W1_ABORT_SEARCH, &dev->flags);
+       kthread_stop(dev->thread);
+
        mutex_lock(&dev->mutex);
-       list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry)
+       mutex_lock(&dev->list_mutex);
+       list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
+               mutex_unlock(&dev->list_mutex);
                w1_slave_detach(sl);
+               mutex_lock(&dev->list_mutex);
+       }
        w1_destroy_master_attributes(dev);
+       mutex_unlock(&dev->list_mutex);
        mutex_unlock(&dev->mutex);
        atomic_dec(&dev->refcnt);
 
@@ -206,7 +219,9 @@ void __w1_remove_master_device(struct w1_master *dev)
 
                if (msleep_interruptible(1000))
                        flush_signals(current);
+               w1_process_callbacks(dev);
        }
+       w1_process_callbacks(dev);
 
        memset(&msg, 0, sizeof(msg));
        msg.id.mst.id = dev->id;
@@ -216,6 +231,10 @@ void __w1_remove_master_device(struct w1_master *dev)
        w1_free_dev(dev);
 }
 
+/**
+ * w1_remove_master_device() - unregister a master device
+ * @bm:        master bus device to remove
+ */
 void w1_remove_master_device(struct w1_bus_master *bm)
 {
        struct w1_master *dev, *found = NULL;
index e10acc23773395699d2bc8f19b37cb32f781fcc3..282092421cc9ee29d541e8722139387d0060c7db 100644 (file)
@@ -62,7 +62,9 @@ static void w1_write_bit(struct w1_master *dev, int bit);
 static u8 w1_read_bit(struct w1_master *dev);
 
 /**
- * Generates a write-0 or write-1 cycle and samples the level.
+ * w1_touch_bit() - Generates a write-0 or write-1 cycle and samples the level.
+ * @dev:       the master device
+ * @bit:       0 - write a 0, 1 - write a 0 read the level
  */
 static u8 w1_touch_bit(struct w1_master *dev, int bit)
 {
@@ -77,7 +79,10 @@ static u8 w1_touch_bit(struct w1_master *dev, int bit)
 }
 
 /**
- * Generates a write-0 or write-1 cycle.
+ * w1_write_bit() - Generates a write-0 or write-1 cycle.
+ * @dev:       the master device
+ * @bit:       bit to write
+ *
  * Only call if dev->bus_master->touch_bit is NULL
  */
 static void w1_write_bit(struct w1_master *dev, int bit)
@@ -102,11 +107,12 @@ static void w1_write_bit(struct w1_master *dev, int bit)
 }
 
 /**
+ * w1_pre_write() - pre-write operations
+ * @dev:       the master device
+ *
  * Pre-write operation, currently only supporting strong pullups.
  * Program the hardware for a strong pullup, if one has been requested and
  * the hardware supports it.
- *
- * @param dev     the master device
  */
 static void w1_pre_write(struct w1_master *dev)
 {
@@ -118,11 +124,12 @@ static void w1_pre_write(struct w1_master *dev)
 }
 
 /**
+ * w1_post_write() - post-write options
+ * @dev:       the master device
+ *
  * Post-write operation, currently only supporting strong pullups.
  * If a strong pullup was requested, clear it if the hardware supports
  * them, or execute the delay otherwise, in either case clear the request.
- *
- * @param dev     the master device
  */
 static void w1_post_write(struct w1_master *dev)
 {
@@ -136,10 +143,9 @@ static void w1_post_write(struct w1_master *dev)
 }
 
 /**
- * Writes 8 bits.
- *
- * @param dev     the master device
- * @param byte    the byte to write
+ * w1_write_8() - Writes 8 bits.
+ * @dev:       the master device
+ * @byte:      the byte to write
  */
 void w1_write_8(struct w1_master *dev, u8 byte)
 {
@@ -161,7 +167,9 @@ EXPORT_SYMBOL_GPL(w1_write_8);
 
 
 /**
- * Generates a write-1 cycle and samples the level.
+ * w1_read_bit() - Generates a write-1 cycle and samples the level.
+ * @dev:       the master device
+ *
  * Only call if dev->bus_master->touch_bit is NULL
  */
 static u8 w1_read_bit(struct w1_master *dev)
@@ -185,16 +193,17 @@ static u8 w1_read_bit(struct w1_master *dev)
 }
 
 /**
- * Does a triplet - used for searching ROM addresses.
+ * w1_triplet() - * Does a triplet - used for searching ROM addresses.
+ * @dev:       the master device
+ * @bdir:      the bit to write if both id_bit and comp_bit are 0
+ *
  * Return bits:
  *  bit 0 = id_bit
  *  bit 1 = comp_bit
  *  bit 2 = dir_taken
  * If both bits 0 & 1 are set, the search should be restarted.
  *
- * @param dev     the master device
- * @param bdir    the bit to write if both id_bit and comp_bit are 0
- * @return        bit fields - see above
+ * Return:        bit fields - see above
  */
 u8 w1_triplet(struct w1_master *dev, int bdir)
 {
@@ -226,10 +235,10 @@ u8 w1_triplet(struct w1_master *dev, int bdir)
 }
 
 /**
- * Reads 8 bits.
+ * w1_read_8() - Reads 8 bits.
+ * @dev:       the master device
  *
- * @param dev     the master device
- * @return        the byte read
+ * Return:        the byte read
  */
 u8 w1_read_8(struct w1_master *dev)
 {
@@ -247,11 +256,10 @@ u8 w1_read_8(struct w1_master *dev)
 EXPORT_SYMBOL_GPL(w1_read_8);
 
 /**
- * Writes a series of bytes.
- *
- * @param dev     the master device
- * @param buf     pointer to the data to write
- * @param len     the number of bytes to write
+ * w1_write_block() - Writes a series of bytes.
+ * @dev:       the master device
+ * @buf:       pointer to the data to write
+ * @len:       the number of bytes to write
  */
 void w1_write_block(struct w1_master *dev, const u8 *buf, int len)
 {
@@ -269,11 +277,10 @@ void w1_write_block(struct w1_master *dev, const u8 *buf, int len)
 EXPORT_SYMBOL_GPL(w1_write_block);
 
 /**
- * Touches a series of bytes.
- *
- * @param dev     the master device
- * @param buf     pointer to the data to write
- * @param len     the number of bytes to write
+ * w1_touch_block() - Touches a series of bytes.
+ * @dev:       the master device
+ * @buf:       pointer to the data to write
+ * @len:       the number of bytes to write
  */
 void w1_touch_block(struct w1_master *dev, u8 *buf, int len)
 {
@@ -294,12 +301,11 @@ void w1_touch_block(struct w1_master *dev, u8 *buf, int len)
 EXPORT_SYMBOL_GPL(w1_touch_block);
 
 /**
- * Reads a series of bytes.
- *
- * @param dev     the master device
- * @param buf     pointer to the buffer to fill
- * @param len     the number of bytes to read
- * @return        the number of bytes read
+ * w1_read_block() - Reads a series of bytes.
+ * @dev:       the master device
+ * @buf:       pointer to the buffer to fill
+ * @len:       the number of bytes to read
+ * Return:     the number of bytes read
  */
 u8 w1_read_block(struct w1_master *dev, u8 *buf, int len)
 {
@@ -319,10 +325,9 @@ u8 w1_read_block(struct w1_master *dev, u8 *buf, int len)
 EXPORT_SYMBOL_GPL(w1_read_block);
 
 /**
- * Issues a reset bus sequence.
- *
- * @param  dev The bus master pointer
- * @return     0=Device present, 1=No device present or error
+ * w1_reset_bus() - Issues a reset bus sequence.
+ * @dev:       the master device
+ * Return:     0=Device present, 1=No device present or error
  */
 int w1_reset_bus(struct w1_master *dev)
 {
@@ -383,12 +388,15 @@ void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_cal
 }
 
 /**
+ * w1_reset_select_slave() - reset and select a slave
+ * @sl:                the slave to select
+ *
  * Resets the bus and then selects the slave by sending either a skip rom
- * or a rom match.
+ * or a rom match.  A skip rom is issued if there is only one device
+ * registered on the bus.
  * The w1 master lock must be held.
  *
- * @param sl   the slave to select
- * @return     0=success, anything else=error
+ * Return:     0=success, anything else=error
  */
 int w1_reset_select_slave(struct w1_slave *sl)
 {
@@ -409,6 +417,9 @@ int w1_reset_select_slave(struct w1_slave *sl)
 EXPORT_SYMBOL_GPL(w1_reset_select_slave);
 
 /**
+ * w1_reset_resume_command() - resume instead of another match ROM
+ * @dev:       the master device
+ *
  * When the workflow with a slave amongst many requires several
  * successive commands a reset between each, this function is similar
  * to doing a reset then a match ROM for the last matched ROM. The
@@ -420,8 +431,6 @@ EXPORT_SYMBOL_GPL(w1_reset_select_slave);
  * doesn't work of course, but the resume command is the next best thing.
  *
  * The w1 master lock must be held.
- *
- * @param dev     the master device
  */
 int w1_reset_resume_command(struct w1_master *dev)
 {
@@ -435,6 +444,10 @@ int w1_reset_resume_command(struct w1_master *dev)
 EXPORT_SYMBOL_GPL(w1_reset_resume_command);
 
 /**
+ * w1_next_pullup() - register for a strong pullup
+ * @dev:       the master device
+ * @delay:     time in milliseconds
+ *
  * Put out a strong pull-up of the specified duration after the next write
  * operation.  Not all hardware supports strong pullups.  Hardware that
  * doesn't support strong pullups will sleep for the given time after the
@@ -442,8 +455,7 @@ EXPORT_SYMBOL_GPL(w1_reset_resume_command);
  * the next write, specifying zero will clear a previous request.
  * The w1 master lock must be held.
  *
- * @param delay        time in milliseconds
- * @return     0=success, anything else=error
+ * Return:     0=success, anything else=error
  */
 void w1_next_pullup(struct w1_master *dev, int delay)
 {
index 40788c925d1c9b4d847e6572f5a8510ac402ac71..5234964fe001e95935b8170d56d6eae7a8181a3b 100644 (file)
@@ -45,7 +45,7 @@ void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
 
        memcpy(w, msg, sizeof(struct w1_netlink_msg));
 
-       cn_netlink_send(m, 0, GFP_KERNEL);
+       cn_netlink_send(m, dev->portid, 0, GFP_KERNEL);
 }
 
 static void w1_send_slave(struct w1_master *dev, u64 rn)
@@ -54,53 +54,95 @@ static void w1_send_slave(struct w1_master *dev, u64 rn)
        struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1);
        struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1);
        int avail;
-
-       /* update kernel slave list */
-       w1_slave_found(dev, rn);
+       u64 *data;
 
        avail = dev->priv_size - cmd->len;
 
-       if (avail > 8) {
-               u64 *data = (void *)(cmd + 1) + cmd->len;
+       if (avail < 8) {
+               msg->ack++;
+               cn_netlink_send(msg, dev->portid, 0, GFP_KERNEL);
 
-               *data = rn;
-               cmd->len += 8;
-               hdr->len += 8;
-               msg->len += 8;
-               return;
+               msg->len = sizeof(struct w1_netlink_msg) +
+                       sizeof(struct w1_netlink_cmd);
+               hdr->len = sizeof(struct w1_netlink_cmd);
+               cmd->len = 0;
        }
 
-       msg->ack++;
-       cn_netlink_send(msg, 0, GFP_KERNEL);
+       data = (void *)(cmd + 1) + cmd->len;
 
-       msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd);
-       hdr->len = sizeof(struct w1_netlink_cmd);
-       cmd->len = 0;
+       *data = rn;
+       cmd->len += 8;
+       hdr->len += 8;
+       msg->len += 8;
 }
 
-static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg,
-               unsigned int avail)
+static void w1_found_send_slave(struct w1_master *dev, u64 rn)
 {
-       struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1);
-       struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1);
-       int search_type = (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH;
+       /* update kernel slave list */
+       w1_slave_found(dev, rn);
 
-       dev->priv = msg;
-       dev->priv_size = avail;
+       w1_send_slave(dev, rn);
+}
+
+/* Get the current slave list, or search (with or without alarm) */
+static int w1_get_slaves(struct w1_master *dev,
+               struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr,
+               struct w1_netlink_cmd *req_cmd)
+{
+       struct cn_msg *msg;
+       struct w1_netlink_msg *hdr;
+       struct w1_netlink_cmd *cmd;
+       struct w1_slave *sl;
+
+       msg = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       msg->id = req_msg->id;
+       msg->seq = req_msg->seq;
+       msg->ack = 0;
+       msg->len = sizeof(struct w1_netlink_msg) +
+               sizeof(struct w1_netlink_cmd);
+
+       hdr = (struct w1_netlink_msg *)(msg + 1);
+       cmd = (struct w1_netlink_cmd *)(hdr + 1);
+
+       hdr->type = W1_MASTER_CMD;
+       hdr->id = req_hdr->id;
+       hdr->len = sizeof(struct w1_netlink_cmd);
+
+       cmd->cmd = req_cmd->cmd;
+       cmd->len = 0;
 
-       w1_search_process_cb(dev, search_type, w1_send_slave);
+       dev->priv = msg;
+       dev->priv_size = PAGE_SIZE - msg->len - sizeof(struct cn_msg);
+
+       if (req_cmd->cmd == W1_CMD_LIST_SLAVES) {
+               __u64 rn;
+               mutex_lock(&dev->list_mutex);
+               list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
+                       memcpy(&rn, &sl->reg_num, sizeof(rn));
+                       w1_send_slave(dev, rn);
+               }
+               mutex_unlock(&dev->list_mutex);
+       } else {
+               w1_search_process_cb(dev, cmd->cmd == W1_CMD_ALARM_SEARCH ?
+                       W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave);
+       }
 
        msg->ack = 0;
-       cn_netlink_send(msg, 0, GFP_KERNEL);
+       cn_netlink_send(msg, dev->portid, 0, GFP_KERNEL);
 
        dev->priv = NULL;
        dev->priv_size = 0;
 
+       kfree(msg);
+
        return 0;
 }
 
 static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr,
-               struct w1_netlink_cmd *cmd)
+               struct w1_netlink_cmd *cmd, u32 portid)
 {
        void *data;
        struct w1_netlink_msg *h;
@@ -131,7 +173,7 @@ static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr,
 
        memcpy(c->data, cmd->data, c->len);
 
-       err = cn_netlink_send(cm, 0, GFP_KERNEL);
+       err = cn_netlink_send(cm, portid, 0, GFP_KERNEL);
 
        kfree(data);
 
@@ -146,11 +188,11 @@ static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg,
        switch (cmd->cmd) {
        case W1_CMD_TOUCH:
                w1_touch_block(dev, cmd->data, cmd->len);
-               w1_send_read_reply(msg, hdr, cmd);
+               w1_send_read_reply(msg, hdr, cmd, dev->portid);
                break;
        case W1_CMD_READ:
                w1_read_block(dev, cmd->data, cmd->len);
-               w1_send_read_reply(msg, hdr, cmd);
+               w1_send_read_reply(msg, hdr, cmd, dev->portid);
                break;
        case W1_CMD_WRITE:
                w1_write_block(dev, cmd->data, cmd->len);
@@ -163,38 +205,57 @@ static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg,
        return err;
 }
 
-static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_msg,
-               struct w1_netlink_msg *req_hdr, struct w1_netlink_cmd *req_cmd)
+static int w1_process_command_addremove(struct w1_master *dev,
+       struct cn_msg *msg, struct w1_netlink_msg *hdr,
+       struct w1_netlink_cmd *cmd)
 {
-       int err = -EINVAL;
-       struct cn_msg *msg;
-       struct w1_netlink_msg *hdr;
-       struct w1_netlink_cmd *cmd;
+       struct w1_slave *sl;
+       int err = 0;
+       struct w1_reg_num *id;
 
-       msg = kzalloc(PAGE_SIZE, GFP_KERNEL);
-       if (!msg)
-               return -ENOMEM;
+       if (cmd->len != 8)
+               return -EINVAL;
 
-       msg->id = req_msg->id;
-       msg->seq = req_msg->seq;
-       msg->ack = 0;
-       msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd);
+       id = (struct w1_reg_num *)cmd->data;
 
-       hdr = (struct w1_netlink_msg *)(msg + 1);
-       cmd = (struct w1_netlink_cmd *)(hdr + 1);
+       sl = w1_slave_search_device(dev, id);
+       switch (cmd->cmd) {
+       case W1_CMD_SLAVE_ADD:
+               if (sl)
+                       err = -EINVAL;
+               else
+                       err = w1_attach_slave_device(dev, id);
+               break;
+       case W1_CMD_SLAVE_REMOVE:
+               if (sl)
+                       w1_slave_detach(sl);
+               else
+                       err = -EINVAL;
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
 
-       hdr->type = W1_MASTER_CMD;
-       hdr->id = req_hdr->id;
-       hdr->len = sizeof(struct w1_netlink_cmd);
+       return err;
+}
 
-       cmd->cmd = req_cmd->cmd;
-       cmd->len = 0;
+static int w1_process_command_master(struct w1_master *dev,
+       struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr,
+       struct w1_netlink_cmd *req_cmd)
+{
+       int err = -EINVAL;
 
-       switch (cmd->cmd) {
+       /* drop bus_mutex for search (does it's own locking), and add/remove
+        * which doesn't use the bus
+        */
+       switch (req_cmd->cmd) {
        case W1_CMD_SEARCH:
        case W1_CMD_ALARM_SEARCH:
-               err = w1_process_search_command(dev, msg,
-                               PAGE_SIZE - msg->len - sizeof(struct cn_msg));
+       case W1_CMD_LIST_SLAVES:
+               mutex_unlock(&dev->bus_mutex);
+               err = w1_get_slaves(dev, req_msg, req_hdr, req_cmd);
+               mutex_lock(&dev->bus_mutex);
                break;
        case W1_CMD_READ:
        case W1_CMD_WRITE:
@@ -204,12 +265,20 @@ static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_m
        case W1_CMD_RESET:
                err = w1_reset_bus(dev);
                break;
+       case W1_CMD_SLAVE_ADD:
+       case W1_CMD_SLAVE_REMOVE:
+               mutex_unlock(&dev->bus_mutex);
+               mutex_lock(&dev->mutex);
+               err = w1_process_command_addremove(dev, req_msg, req_hdr,
+                       req_cmd);
+               mutex_unlock(&dev->mutex);
+               mutex_lock(&dev->bus_mutex);
+               break;
        default:
                err = -EINVAL;
                break;
        }
 
-       kfree(msg);
        return err;
 }
 
@@ -223,7 +292,8 @@ static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg,
        return w1_process_command_io(sl->master, msg, hdr, cmd);
 }
 
-static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mcmd)
+static int w1_process_command_root(struct cn_msg *msg,
+       struct w1_netlink_msg *mcmd, u32 portid)
 {
        struct w1_master *m;
        struct cn_msg *cn;
@@ -256,7 +326,7 @@ static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mc
        mutex_lock(&w1_mlock);
        list_for_each_entry(m, &w1_masters, w1_master_entry) {
                if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) {
-                       cn_netlink_send(cn, 0, GFP_KERNEL);
+                       cn_netlink_send(cn, portid, 0, GFP_KERNEL);
                        cn->ack++;
                        cn->len = sizeof(struct w1_netlink_msg);
                        w->len = 0;
@@ -269,7 +339,7 @@ static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mc
                id++;
        }
        cn->ack = 0;
-       cn_netlink_send(cn, 0, GFP_KERNEL);
+       cn_netlink_send(cn, portid, 0, GFP_KERNEL);
        mutex_unlock(&w1_mlock);
 
        kfree(cn);
@@ -277,7 +347,7 @@ static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mc
 }
 
 static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg,
-               struct w1_netlink_cmd *rcmd, int error)
+               struct w1_netlink_cmd *rcmd, int portid, int error)
 {
        struct cn_msg *cmsg;
        struct w1_netlink_msg *msg;
@@ -304,35 +374,147 @@ static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rm
                cmsg->len += sizeof(*cmd);
        }
 
-       error = cn_netlink_send(cmsg, 0, GFP_KERNEL);
+       error = cn_netlink_send(cmsg, portid, 0, GFP_KERNEL);
        kfree(cmsg);
 
        return error;
 }
 
+/* Bundle together a reference count, the full message, and broken out
+ * commands to be executed on each w1 master kthread in one memory allocation.
+ */
+struct w1_cb_block {
+       atomic_t refcnt;
+       u32 portid; /* Sending process port ID */
+       struct cn_msg msg;
+       /* cn_msg data */
+       /* one or more variable length struct w1_cb_node */
+};
+struct w1_cb_node {
+       struct w1_async_cmd async;
+       /* pointers within w1_cb_block and msg data */
+       struct w1_cb_block *block;
+       struct w1_netlink_msg *m;
+       struct w1_slave *sl;
+       struct w1_master *dev;
+};
+
+static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)
+{
+       struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node,
+               async);
+       u16 mlen = node->m->len;
+       u8 *cmd_data = node->m->data;
+       int err = 0;
+       struct w1_slave *sl = node->sl;
+       struct w1_netlink_cmd *cmd = NULL;
+
+       mutex_lock(&dev->bus_mutex);
+       dev->portid = node->block->portid;
+       if (sl && w1_reset_select_slave(sl))
+               err = -ENODEV;
+
+       while (mlen && !err) {
+               cmd = (struct w1_netlink_cmd *)cmd_data;
+
+               if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
+                       err = -E2BIG;
+                       break;
+               }
+
+               if (sl)
+                       err = w1_process_command_slave(sl, &node->block->msg,
+                               node->m, cmd);
+               else
+                       err = w1_process_command_master(dev, &node->block->msg,
+                               node->m, cmd);
+
+               w1_netlink_send_error(&node->block->msg, node->m, cmd,
+                       node->block->portid, err);
+               err = 0;
+
+               cmd_data += cmd->len + sizeof(struct w1_netlink_cmd);
+               mlen -= cmd->len + sizeof(struct w1_netlink_cmd);
+       }
+
+       if (!cmd || err)
+               w1_netlink_send_error(&node->block->msg, node->m, cmd,
+                       node->block->portid, err);
+
+       if (sl)
+               w1_unref_slave(sl);
+       else
+               atomic_dec(&dev->refcnt);
+       dev->portid = 0;
+       mutex_unlock(&dev->bus_mutex);
+
+       mutex_lock(&dev->list_mutex);
+       list_del(&async_cmd->async_entry);
+       mutex_unlock(&dev->list_mutex);
+
+       if (atomic_sub_return(1, &node->block->refcnt) == 0)
+               kfree(node->block);
+}
+
 static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
 {
        struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1);
-       struct w1_netlink_cmd *cmd;
        struct w1_slave *sl;
        struct w1_master *dev;
+       u16 msg_len;
        int err = 0;
+       struct w1_cb_block *block = NULL;
+       struct w1_cb_node *node = NULL;
+       int node_count = 0;
+
+       /* Count the number of master or slave commands there are to allocate
+        * space for one cb_node each.
+        */
+       msg_len = msg->len;
+       while (msg_len && !err) {
+               if (m->len + sizeof(struct w1_netlink_msg) > msg_len) {
+                       err = -E2BIG;
+                       break;
+               }
+
+               if (m->type == W1_MASTER_CMD || m->type == W1_SLAVE_CMD)
+                       ++node_count;
 
-       while (msg->len && !err) {
+               msg_len -= sizeof(struct w1_netlink_msg) + m->len;
+               m = (struct w1_netlink_msg *)(((u8 *)m) +
+                       sizeof(struct w1_netlink_msg) + m->len);
+       }
+       m = (struct w1_netlink_msg *)(msg + 1);
+       if (node_count) {
+               /* msg->len doesn't include itself */
+               long size = sizeof(struct w1_cb_block) + msg->len +
+                       node_count*sizeof(struct w1_cb_node);
+               block = kmalloc(size, GFP_KERNEL);
+               if (!block) {
+                       w1_netlink_send_error(msg, m, NULL, nsp->portid,
+                               -ENOMEM);
+                       return;
+               }
+               atomic_set(&block->refcnt, 1);
+               block->portid = nsp->portid;
+               memcpy(&block->msg, msg, sizeof(*msg) + msg->len);
+               node = (struct w1_cb_node *)((u8 *)block->msg.data + msg->len);
+       }
+
+       msg_len = msg->len;
+       while (msg_len && !err) {
                struct w1_reg_num id;
                u16 mlen = m->len;
-               u8 *cmd_data = m->data;
 
                dev = NULL;
                sl = NULL;
-               cmd = NULL;
 
                memcpy(&id, m->id.id, sizeof(id));
 #if 0
                printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n",
                                __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len);
 #endif
-               if (m->len + sizeof(struct w1_netlink_msg) > msg->len) {
+               if (m->len + sizeof(struct w1_netlink_msg) > msg_len) {
                        err = -E2BIG;
                        break;
                }
@@ -344,7 +526,7 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
                        if (sl)
                                dev = sl->master;
                } else {
-                       err = w1_process_command_root(msg, m);
+                       err = w1_process_command_root(msg, m, nsp->portid);
                        goto out_cont;
                }
 
@@ -357,41 +539,24 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
                if (!mlen)
                        goto out_cont;
 
-               mutex_lock(&dev->mutex);
-
-               if (sl && w1_reset_select_slave(sl)) {
-                       err = -ENODEV;
-                       goto out_up;
-               }
+               atomic_inc(&block->refcnt);
+               node->async.cb = w1_process_cb;
+               node->block = block;
+               node->m = (struct w1_netlink_msg *)((u8 *)&block->msg +
+                       (size_t)((u8 *)m - (u8 *)msg));
+               node->sl = sl;
+               node->dev = dev;
 
-               while (mlen) {
-                       cmd = (struct w1_netlink_cmd *)cmd_data;
+               mutex_lock(&dev->list_mutex);
+               list_add_tail(&node->async.async_entry, &dev->async_list);
+               wake_up_process(dev->thread);
+               mutex_unlock(&dev->list_mutex);
+               ++node;
 
-                       if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
-                               err = -E2BIG;
-                               break;
-                       }
-
-                       if (sl)
-                               err = w1_process_command_slave(sl, msg, m, cmd);
-                       else
-                               err = w1_process_command_master(dev, msg, m, cmd);
-
-                       w1_netlink_send_error(msg, m, cmd, err);
-                       err = 0;
-
-                       cmd_data += cmd->len + sizeof(struct w1_netlink_cmd);
-                       mlen -= cmd->len + sizeof(struct w1_netlink_cmd);
-               }
-out_up:
-               atomic_dec(&dev->refcnt);
-               if (sl)
-                       atomic_dec(&sl->refcnt);
-               mutex_unlock(&dev->mutex);
 out_cont:
-               if (!cmd || err)
-                       w1_netlink_send_error(msg, m, cmd, err);
-               msg->len -= sizeof(struct w1_netlink_msg) + m->len;
+               if (err)
+                       w1_netlink_send_error(msg, m, NULL, nsp->portid, err);
+               msg_len -= sizeof(struct w1_netlink_msg) + m->len;
                m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len);
 
                /*
@@ -400,6 +565,8 @@ out_cont:
                if (err == -ENODEV)
                        err = 0;
        }
+       if (block && atomic_sub_return(1, &block->refcnt) == 0)
+               kfree(block);
 }
 
 int w1_init_netlink(void)
index b0922dc29658a3fe1d19e8b17942e2521009beb0..1e9504e67650aca2966a4647ba54aea15850eeb6 100644 (file)
 
 #include "w1.h"
 
+/**
+ * enum w1_netlink_message_types - message type
+ *
+ * @W1_SLAVE_ADD: notification that a slave device was added
+ * @W1_SLAVE_REMOVE: notification that a slave device was removed
+ * @W1_MASTER_ADD: notification that a new bus master was added
+ * @W1_MASTER_REMOVE: notification that a bus masterwas removed
+ * @W1_MASTER_CMD: initiate operations on a specific master
+ * @W1_SLAVE_CMD: sends reset, selects the slave, then does a read/write/touch
+ * operation
+ * @W1_LIST_MASTERS: used to determine the bus master identifiers
+ */
 enum w1_netlink_message_types {
        W1_SLAVE_ADD = 0,
        W1_SLAVE_REMOVE,
@@ -52,6 +64,22 @@ struct w1_netlink_msg
        __u8                            data[0];
 };
 
+/**
+ * enum w1_commands - commands available for master or slave operations
+ * @W1_CMD_READ: read len bytes
+ * @W1_CMD_WRITE: write len bytes
+ * @W1_CMD_SEARCH: initiate a standard search, returns only the slave
+ * devices found during that search
+ * @W1_CMD_ALARM_SEARCH: search for devices that are currently alarming
+ * @W1_CMD_TOUCH: Touches a series of bytes.
+ * @W1_CMD_RESET: sends a bus reset on the given master
+ * @W1_CMD_SLAVE_ADD: adds a slave to the given master,
+ * 8 byte slave id at data[0]
+ * @W1_CMD_SLAVE_REMOVE: removes a slave to the given master,
+ * 8 byte slave id at data[0]
+ * @W1_CMD_LIST_SLAVES: list of slaves registered on this master
+ * @W1_CMD_MAX: number of available commands
+ */
 enum w1_commands {
        W1_CMD_READ = 0,
        W1_CMD_WRITE,
@@ -59,7 +87,10 @@ enum w1_commands {
        W1_CMD_ALARM_SEARCH,
        W1_CMD_TOUCH,
        W1_CMD_RESET,
-       W1_CMD_MAX,
+       W1_CMD_SLAVE_ADD,
+       W1_CMD_SLAVE_REMOVE,
+       W1_CMD_LIST_SLAVES,
+       W1_CMD_MAX
 };
 
 struct w1_netlink_cmd
diff --git a/include/dt-bindings/spmi/spmi.h b/include/dt-bindings/spmi/spmi.h
new file mode 100644 (file)
index 0000000..d11e1e5
--- /dev/null
@@ -0,0 +1,18 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __DT_BINDINGS_SPMI_H
+#define __DT_BINDINGS_SPMI_H
+
+#define SPMI_USID      0
+#define SPMI_GSID      1
+
+#endif
index b2b5a41b6a2400342ff5aec1abb4f2834caaf869..be9c4747d51190bcc92a1ae3c6992ae7c6d45018 100644 (file)
@@ -71,7 +71,7 @@ struct cn_dev {
 int cn_add_callback(struct cb_id *id, const char *name,
                    void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
 void cn_del_callback(struct cb_id *);
-int cn_netlink_send(struct cn_msg *, u32, gfp_t);
+int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 group, gfp_t gfp_mask);
 
 int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
                          struct cb_id *id,
index 344883dce5849b62e8f6c0e59e1daed09b5a0f80..fb66fba368d79346f8c6ea0e69e7edcc630b1f8a 100644 (file)
 #ifndef _HYPERV_H
 #define _HYPERV_H
 
-#include <linux/types.h>
-
-/*
- * Framework version for util services.
- */
-#define UTIL_FW_MINOR  0
-
-#define UTIL_WS2K8_FW_MAJOR  1
-#define UTIL_WS2K8_FW_VERSION     (UTIL_WS2K8_FW_MAJOR << 16 | UTIL_FW_MINOR)
-
-#define UTIL_FW_MAJOR  3
-#define UTIL_FW_VERSION     (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)
-
-
-/*
- * Implementation of host controlled snapshot of the guest.
- */
-
-#define VSS_OP_REGISTER 128
-
-enum hv_vss_op {
-       VSS_OP_CREATE = 0,
-       VSS_OP_DELETE,
-       VSS_OP_HOT_BACKUP,
-       VSS_OP_GET_DM_INFO,
-       VSS_OP_BU_COMPLETE,
-       /*
-        * Following operations are only supported with IC version >= 5.0
-        */
-       VSS_OP_FREEZE, /* Freeze the file systems in the VM */
-       VSS_OP_THAW, /* Unfreeze the file systems */
-       VSS_OP_AUTO_RECOVER,
-       VSS_OP_COUNT /* Number of operations, must be last */
-};
-
-
-/*
- * Header for all VSS messages.
- */
-struct hv_vss_hdr {
-       __u8 operation;
-       __u8 reserved[7];
-} __attribute__((packed));
-
-
-/*
- * Flag values for the hv_vss_check_feature. Linux supports only
- * one value.
- */
-#define VSS_HBU_NO_AUTO_RECOVERY       0x00000005
-
-struct hv_vss_check_feature {
-       __u32 flags;
-} __attribute__((packed));
-
-struct hv_vss_check_dm_info {
-       __u32 flags;
-} __attribute__((packed));
-
-struct hv_vss_msg {
-       union {
-               struct hv_vss_hdr vss_hdr;
-               int error;
-       };
-       union {
-               struct hv_vss_check_feature vss_cf;
-               struct hv_vss_check_dm_info dm_info;
-       };
-} __attribute__((packed));
-
-/*
- * An implementation of HyperV key value pair (KVP) functionality for Linux.
- *
- *
- * Copyright (C) 2010, Novell, Inc.
- * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
- *
- */
-
-/*
- * Maximum value size - used for both key names and value data, and includes
- * any applicable NULL terminators.
- *
- * Note:  This limit is somewhat arbitrary, but falls easily within what is
- * supported for all native guests (back to Win 2000) and what is reasonable
- * for the IC KVP exchange functionality.  Note that Windows Me/98/95 are
- * limited to 255 character key names.
- *
- * MSDN recommends not storing data values larger than 2048 bytes in the
- * registry.
- *
- * Note:  This value is used in defining the KVP exchange message - this value
- * cannot be modified without affecting the message size and compatibility.
- */
-
-/*
- * bytes, including any null terminators
- */
-#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE          (2048)
-
-
-/*
- * Maximum key size - the registry limit for the length of an entry name
- * is 256 characters, including the null terminator
- */
-
-#define HV_KVP_EXCHANGE_MAX_KEY_SIZE            (512)
-
-/*
- * In Linux, we implement the KVP functionality in two components:
- * 1) The kernel component which is packaged as part of the hv_utils driver
- * is responsible for communicating with the host and responsible for
- * implementing the host/guest protocol. 2) A user level daemon that is
- * responsible for data gathering.
- *
- * Host/Guest Protocol: The host iterates over an index and expects the guest
- * to assign a key name to the index and also return the value corresponding to
- * the key. The host will have atmost one KVP transaction outstanding at any
- * given point in time. The host side iteration stops when the guest returns
- * an error. Microsoft has specified the following mapping of key names to
- * host specified index:
- *
- *     Index           Key Name
- *     0               FullyQualifiedDomainName
- *     1               IntegrationServicesVersion
- *     2               NetworkAddressIPv4
- *     3               NetworkAddressIPv6
- *     4               OSBuildNumber
- *     5               OSName
- *     6               OSMajorVersion
- *     7               OSMinorVersion
- *     8               OSVersion
- *     9               ProcessorArchitecture
- *
- * The Windows host expects the Key Name and Key Value to be encoded in utf16.
- *
- * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the
- * data gathering functionality in a user mode daemon. The user level daemon
- * is also responsible for binding the key name to the index as well. The
- * kernel and user-level daemon communicate using a connector channel.
- *
- * The user mode component first registers with the
- * the kernel component. Subsequently, the kernel component requests, data
- * for the specified keys. In response to this message the user mode component
- * fills in the value corresponding to the specified key. We overload the
- * sequence field in the cn_msg header to define our KVP message types.
- *
- *
- * The kernel component simply acts as a conduit for communication between the
- * Windows host and the user-level daemon. The kernel component passes up the
- * index received from the Host to the user-level daemon. If the index is
- * valid (supported), the corresponding key as well as its
- * value (both are strings) is returned. If the index is invalid
- * (not supported), a NULL key string is returned.
- */
-
-
-/*
- * Registry value types.
- */
+#include <uapi/linux/hyperv.h>
 
-#define REG_SZ 1
-#define REG_U32 4
-#define REG_U64 8
-
-/*
- * As we look at expanding the KVP functionality to include
- * IP injection functionality, we need to maintain binary
- * compatibility with older daemons.
- *
- * The KVP opcodes are defined by the host and it was unfortunate
- * that I chose to treat the registration operation as part of the
- * KVP operations defined by the host.
- * Here is the level of compatibility
- * (between the user level daemon and the kernel KVP driver) that we
- * will implement:
- *
- * An older daemon will always be supported on a newer driver.
- * A given user level daemon will require a minimal version of the
- * kernel driver.
- * If we cannot handle the version differences, we will fail gracefully
- * (this can happen when we have a user level daemon that is more
- * advanced than the KVP driver.
- *
- * We will use values used in this handshake for determining if we have
- * workable user level daemon and the kernel driver. We begin by taking the
- * registration opcode out of the KVP opcode namespace. We will however,
- * maintain compatibility with the existing user-level daemon code.
- */
-
-/*
- * Daemon code not supporting IP injection (legacy daemon).
- */
-
-#define KVP_OP_REGISTER        4
-
-/*
- * Daemon code supporting IP injection.
- * The KVP opcode field is used to communicate the
- * registration information; so define a namespace that
- * will be distinct from the host defined KVP opcode.
- */
-
-#define KVP_OP_REGISTER1 100
-
-enum hv_kvp_exchg_op {
-       KVP_OP_GET = 0,
-       KVP_OP_SET,
-       KVP_OP_DELETE,
-       KVP_OP_ENUMERATE,
-       KVP_OP_GET_IP_INFO,
-       KVP_OP_SET_IP_INFO,
-       KVP_OP_COUNT /* Number of operations, must be last. */
-};
-
-enum hv_kvp_exchg_pool {
-       KVP_POOL_EXTERNAL = 0,
-       KVP_POOL_GUEST,
-       KVP_POOL_AUTO,
-       KVP_POOL_AUTO_EXTERNAL,
-       KVP_POOL_AUTO_INTERNAL,
-       KVP_POOL_COUNT /* Number of pools, must be last. */
-};
-
-/*
- * Some Hyper-V status codes.
- */
-
-#define HV_S_OK                                0x00000000
-#define HV_E_FAIL                      0x80004005
-#define HV_S_CONT                      0x80070103
-#define HV_ERROR_NOT_SUPPORTED         0x80070032
-#define HV_ERROR_MACHINE_LOCKED                0x800704F7
-#define HV_ERROR_DEVICE_NOT_CONNECTED  0x8007048F
-#define HV_INVALIDARG                  0x80070057
-#define HV_GUID_NOTFOUND               0x80041002
-
-#define ADDR_FAMILY_NONE       0x00
-#define ADDR_FAMILY_IPV4       0x01
-#define ADDR_FAMILY_IPV6       0x02
-
-#define MAX_ADAPTER_ID_SIZE    128
-#define MAX_IP_ADDR_SIZE       1024
-#define MAX_GATEWAY_SIZE       512
-
-
-struct hv_kvp_ipaddr_value {
-       __u16   adapter_id[MAX_ADAPTER_ID_SIZE];
-       __u8    addr_family;
-       __u8    dhcp_enabled;
-       __u16   ip_addr[MAX_IP_ADDR_SIZE];
-       __u16   sub_net[MAX_IP_ADDR_SIZE];
-       __u16   gate_way[MAX_GATEWAY_SIZE];
-       __u16   dns_addr[MAX_IP_ADDR_SIZE];
-} __attribute__((packed));
-
-
-struct hv_kvp_hdr {
-       __u8 operation;
-       __u8 pool;
-       __u16 pad;
-} __attribute__((packed));
-
-struct hv_kvp_exchg_msg_value {
-       __u32 value_type;
-       __u32 key_size;
-       __u32 value_size;
-       __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
-       union {
-               __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
-               __u32 value_u32;
-               __u64 value_u64;
-       };
-} __attribute__((packed));
-
-struct hv_kvp_msg_enumerate {
-       __u32 index;
-       struct hv_kvp_exchg_msg_value data;
-} __attribute__((packed));
-
-struct hv_kvp_msg_get {
-       struct hv_kvp_exchg_msg_value data;
-};
-
-struct hv_kvp_msg_set {
-       struct hv_kvp_exchg_msg_value data;
-};
-
-struct hv_kvp_msg_delete {
-       __u32 key_size;
-       __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
-};
-
-struct hv_kvp_register {
-       __u8 version[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
-};
-
-struct hv_kvp_msg {
-       union {
-               struct hv_kvp_hdr       kvp_hdr;
-               int error;
-       };
-       union {
-               struct hv_kvp_msg_get           kvp_get;
-               struct hv_kvp_msg_set           kvp_set;
-               struct hv_kvp_msg_delete        kvp_delete;
-               struct hv_kvp_msg_enumerate     kvp_enum_data;
-               struct hv_kvp_ipaddr_value      kvp_ip_val;
-               struct hv_kvp_register          kvp_register;
-       } body;
-} __attribute__((packed));
-
-struct hv_kvp_ip_msg {
-       __u8 operation;
-       __u8 pool;
-       struct hv_kvp_ipaddr_value      kvp_ip_val;
-} __attribute__((packed));
-
-#ifdef __KERNEL__
+#include <linux/types.h>
 #include <linux/scatterlist.h>
 #include <linux/list.h>
 #include <linux/uuid.h>
@@ -1043,6 +727,10 @@ struct vmbus_channel {
         * This will be NULL for the primary channel.
         */
        struct vmbus_channel *primary_channel;
+       /*
+        * Support per-channel state for use by vmbus drivers.
+        */
+       void *per_channel_state;
 };
 
 static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
@@ -1050,6 +738,16 @@ static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
        c->batched_reading = state;
 }
 
+static inline void set_per_channel_state(struct vmbus_channel *c, void *s)
+{
+       c->per_channel_state = s;
+}
+
+static inline void *get_per_channel_state(struct vmbus_channel *c)
+{
+       return c->per_channel_state;
+}
+
 void vmbus_onmessage(void *context);
 
 int vmbus_request_offers(void);
@@ -1118,7 +816,7 @@ extern int vmbus_open(struct vmbus_channel *channel,
 extern void vmbus_close(struct vmbus_channel *channel);
 
 extern int vmbus_sendpacket(struct vmbus_channel *channel,
-                                 const void *buffer,
+                                 void *buffer,
                                  u32 bufferLen,
                                  u64 requestid,
                                  enum vmbus_packet_type type,
@@ -1459,11 +1157,13 @@ int hv_vss_init(struct hv_util_service *);
 void hv_vss_deinit(void);
 void hv_vss_onchannelcallback(void *);
 
+extern u64 hyperv_mmio_start;
+extern u64 hyperv_mmio_size;
+
 /*
  * Negotiated version with the Host.
  */
 
 extern __u32 vmbus_proto_version;
 
-#endif /* __KERNEL__ */
 #endif /* _HYPERV_H */
index 45e921401b067b68c9912f82af1c19a1a8c6d048..677e474fc273fa247f49b012ab1992804568656e 100644 (file)
@@ -432,6 +432,14 @@ struct spi_device_id {
        kernel_ulong_t driver_data;     /* Data private to the driver */
 };
 
+#define SPMI_NAME_SIZE 32
+#define SPMI_MODULE_PREFIX "spmi:"
+
+struct spmi_device_id {
+       char name[SPMI_NAME_SIZE];
+       kernel_ulong_t driver_data;     /* Data private to the driver */
+};
+
 /* dmi */
 enum dmi_field {
        DMI_NONE,
index 4149f1a9b00320dfeea8768817b2adbd2d1a4da2..8cc73ac6f8884fbb8c877bb745e6e694c35155bb 100644 (file)
@@ -321,8 +321,10 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c,
                               const struct regmap_config *config);
 struct regmap *regmap_init_spi(struct spi_device *dev,
                               const struct regmap_config *config);
-struct regmap *regmap_init_spmi(struct spmi_device *dev,
-                              const struct regmap_config *config);
+struct regmap *regmap_init_spmi_base(struct spmi_device *dev,
+                                    const struct regmap_config *config);
+struct regmap *regmap_init_spmi_ext(struct spmi_device *dev,
+                                   const struct regmap_config *config);
 struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
                                    void __iomem *regs,
                                    const struct regmap_config *config);
@@ -335,8 +337,10 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
                                    const struct regmap_config *config);
 struct regmap *devm_regmap_init_spi(struct spi_device *dev,
                                    const struct regmap_config *config);
-struct regmap *devm_regmap_init_spmi(struct spmi_device *dev,
-                                    const struct regmap_config *config);
+struct regmap *devm_regmap_init_spmi_base(struct spmi_device *dev,
+                                         const struct regmap_config *config);
+struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev,
+                                        const struct regmap_config *config);
 struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
                                         void __iomem *regs,
                                         const struct regmap_config *config);
diff --git a/include/linux/spmi.h b/include/linux/spmi.h
new file mode 100644 (file)
index 0000000..91f5eab
--- /dev/null
@@ -0,0 +1,191 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _LINUX_SPMI_H
+#define _LINUX_SPMI_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+/* Maximum slave identifier */
+#define SPMI_MAX_SLAVE_ID              16
+
+/* SPMI Commands */
+#define SPMI_CMD_EXT_WRITE             0x00
+#define SPMI_CMD_RESET                 0x10
+#define SPMI_CMD_SLEEP                 0x11
+#define SPMI_CMD_SHUTDOWN              0x12
+#define SPMI_CMD_WAKEUP                        0x13
+#define SPMI_CMD_AUTHENTICATE          0x14
+#define SPMI_CMD_MSTR_READ             0x15
+#define SPMI_CMD_MSTR_WRITE            0x16
+#define SPMI_CMD_TRANSFER_BUS_OWNERSHIP        0x1A
+#define SPMI_CMD_DDB_MASTER_READ       0x1B
+#define SPMI_CMD_DDB_SLAVE_READ                0x1C
+#define SPMI_CMD_EXT_READ              0x20
+#define SPMI_CMD_EXT_WRITEL            0x30
+#define SPMI_CMD_EXT_READL             0x38
+#define SPMI_CMD_WRITE                 0x40
+#define SPMI_CMD_READ                  0x60
+#define SPMI_CMD_ZERO_WRITE            0x80
+
+/**
+ * struct spmi_device - Basic representation of an SPMI device
+ * @dev:       Driver model representation of the device.
+ * @ctrl:      SPMI controller managing the bus hosting this device.
+ * @usid:      This devices' Unique Slave IDentifier.
+ */
+struct spmi_device {
+       struct device           dev;
+       struct spmi_controller  *ctrl;
+       u8                      usid;
+};
+
+static inline struct spmi_device *to_spmi_device(struct device *d)
+{
+       return container_of(d, struct spmi_device, dev);
+}
+
+static inline void *spmi_device_get_drvdata(const struct spmi_device *sdev)
+{
+       return dev_get_drvdata(&sdev->dev);
+}
+
+static inline void spmi_device_set_drvdata(struct spmi_device *sdev, void *data)
+{
+       dev_set_drvdata(&sdev->dev, data);
+}
+
+struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl);
+
+static inline void spmi_device_put(struct spmi_device *sdev)
+{
+       if (sdev)
+               put_device(&sdev->dev);
+}
+
+int spmi_device_add(struct spmi_device *sdev);
+
+void spmi_device_remove(struct spmi_device *sdev);
+
+/**
+ * struct spmi_controller - interface to the SPMI master controller
+ * @dev:       Driver model representation of the device.
+ * @nr:                board-specific number identifier for this controller/bus
+ * @cmd:       sends a non-data command sequence on the SPMI bus.
+ * @read_cmd:  sends a register read command sequence on the SPMI bus.
+ * @write_cmd: sends a register write command sequence on the SPMI bus.
+ */
+struct spmi_controller {
+       struct device           dev;
+       unsigned int            nr;
+       int     (*cmd)(struct spmi_controller *ctrl, u8 opcode, u8 sid);
+       int     (*read_cmd)(struct spmi_controller *ctrl, u8 opcode,
+                           u8 sid, u16 addr, u8 *buf, size_t len);
+       int     (*write_cmd)(struct spmi_controller *ctrl, u8 opcode,
+                            u8 sid, u16 addr, const u8 *buf, size_t len);
+};
+
+static inline struct spmi_controller *to_spmi_controller(struct device *d)
+{
+       return container_of(d, struct spmi_controller, dev);
+}
+
+static inline
+void *spmi_controller_get_drvdata(const struct spmi_controller *ctrl)
+{
+       return dev_get_drvdata(&ctrl->dev);
+}
+
+static inline void spmi_controller_set_drvdata(struct spmi_controller *ctrl,
+                                              void *data)
+{
+       dev_set_drvdata(&ctrl->dev, data);
+}
+
+struct spmi_controller *spmi_controller_alloc(struct device *parent,
+                                             size_t size);
+
+/**
+ * spmi_controller_put() - decrement controller refcount
+ * @ctrl       SPMI controller.
+ */
+static inline void spmi_controller_put(struct spmi_controller *ctrl)
+{
+       if (ctrl)
+               put_device(&ctrl->dev);
+}
+
+int spmi_controller_add(struct spmi_controller *ctrl);
+void spmi_controller_remove(struct spmi_controller *ctrl);
+
+/**
+ * struct spmi_driver - SPMI slave device driver
+ * @driver:    SPMI device drivers should initialize name and owner field of
+ *             this structure.
+ * @probe:     binds this driver to a SPMI device.
+ * @remove:    unbinds this driver from the SPMI device.
+ * @shutdown:  standard shutdown callback used during powerdown/halt.
+ * @suspend:   standard suspend callback used during system suspend.
+ * @resume:    standard resume callback used during system resume.
+ *
+ * If PM runtime support is desired for a slave, a device driver can call
+ * pm_runtime_put() from their probe() routine (and a balancing
+ * pm_runtime_get() in remove()).  PM runtime support for a slave is
+ * implemented by issuing a SLEEP command to the slave on runtime_suspend(),
+ * transitioning the slave into the SLEEP state.  On runtime_resume(), a WAKEUP
+ * command is sent to the slave to bring it back to ACTIVE.
+ */
+struct spmi_driver {
+       struct device_driver driver;
+       int     (*probe)(struct spmi_device *sdev);
+       void    (*remove)(struct spmi_device *sdev);
+};
+
+static inline struct spmi_driver *to_spmi_driver(struct device_driver *d)
+{
+       return container_of(d, struct spmi_driver, driver);
+}
+
+int spmi_driver_register(struct spmi_driver *sdrv);
+
+/**
+ * spmi_driver_unregister() - unregister an SPMI client driver
+ * @sdrv:      the driver to unregister
+ */
+static inline void spmi_driver_unregister(struct spmi_driver *sdrv)
+{
+       if (sdrv)
+               driver_unregister(&sdrv->driver);
+}
+
+#define module_spmi_driver(__spmi_driver) \
+       module_driver(__spmi_driver, spmi_driver_register, \
+                       spmi_driver_unregister)
+
+int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf);
+int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf,
+                          size_t len);
+int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf,
+                           size_t len);
+int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data);
+int spmi_register_zero_write(struct spmi_device *sdev, u8 data);
+int spmi_ext_register_write(struct spmi_device *sdev, u8 addr,
+                           const u8 *buf, size_t len);
+int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr,
+                            const u8 *buf, size_t len);
+int spmi_command_reset(struct spmi_device *sdev);
+int spmi_command_sleep(struct spmi_device *sdev);
+int spmi_command_wakeup(struct spmi_device *sdev);
+int spmi_command_shutdown(struct spmi_device *sdev);
+
+#endif
index 3ce25b5d75a9facfd047a526d3c3af618169e488..6929571b79b0c396496991fc136a7ff811c9177d 100644 (file)
@@ -139,6 +139,7 @@ header-y += hid.h
 header-y += hiddev.h
 header-y += hidraw.h
 header-y += hpet.h
+header-y += hyperv.h
 header-y += hysdn_if.h
 header-y += i2c-dev.h
 header-y += i2c.h
diff --git a/include/uapi/linux/hyperv.h b/include/uapi/linux/hyperv.h
new file mode 100644 (file)
index 0000000..1861f8e
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ *
+ * Copyright (c) 2011, Microsoft 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Authors:
+ *   Haiyang Zhang <haiyangz@microsoft.com>
+ *   Hank Janssen  <hjanssen@microsoft.com>
+ *   K. Y. Srinivasan <kys@microsoft.com>
+ *
+ */
+
+#ifndef _UAPI_HYPERV_H
+#define _UAPI_HYPERV_H
+
+/*
+ * Framework version for util services.
+ */
+#define UTIL_FW_MINOR  0
+
+#define UTIL_WS2K8_FW_MAJOR  1
+#define UTIL_WS2K8_FW_VERSION     (UTIL_WS2K8_FW_MAJOR << 16 | UTIL_FW_MINOR)
+
+#define UTIL_FW_MAJOR  3
+#define UTIL_FW_VERSION     (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)
+
+
+/*
+ * Implementation of host controlled snapshot of the guest.
+ */
+
+#define VSS_OP_REGISTER 128
+
+enum hv_vss_op {
+       VSS_OP_CREATE = 0,
+       VSS_OP_DELETE,
+       VSS_OP_HOT_BACKUP,
+       VSS_OP_GET_DM_INFO,
+       VSS_OP_BU_COMPLETE,
+       /*
+        * Following operations are only supported with IC version >= 5.0
+        */
+       VSS_OP_FREEZE, /* Freeze the file systems in the VM */
+       VSS_OP_THAW, /* Unfreeze the file systems */
+       VSS_OP_AUTO_RECOVER,
+       VSS_OP_COUNT /* Number of operations, must be last */
+};
+
+
+/*
+ * Header for all VSS messages.
+ */
+struct hv_vss_hdr {
+       __u8 operation;
+       __u8 reserved[7];
+} __attribute__((packed));
+
+
+/*
+ * Flag values for the hv_vss_check_feature. Linux supports only
+ * one value.
+ */
+#define VSS_HBU_NO_AUTO_RECOVERY       0x00000005
+
+struct hv_vss_check_feature {
+       __u32 flags;
+} __attribute__((packed));
+
+struct hv_vss_check_dm_info {
+       __u32 flags;
+} __attribute__((packed));
+
+struct hv_vss_msg {
+       union {
+               struct hv_vss_hdr vss_hdr;
+               int error;
+       };
+       union {
+               struct hv_vss_check_feature vss_cf;
+               struct hv_vss_check_dm_info dm_info;
+       };
+} __attribute__((packed));
+
+/*
+ * An implementation of HyperV key value pair (KVP) functionality for Linux.
+ *
+ *
+ * Copyright (C) 2010, Novell, Inc.
+ * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
+ *
+ */
+
+/*
+ * Maximum value size - used for both key names and value data, and includes
+ * any applicable NULL terminators.
+ *
+ * Note:  This limit is somewhat arbitrary, but falls easily within what is
+ * supported for all native guests (back to Win 2000) and what is reasonable
+ * for the IC KVP exchange functionality.  Note that Windows Me/98/95 are
+ * limited to 255 character key names.
+ *
+ * MSDN recommends not storing data values larger than 2048 bytes in the
+ * registry.
+ *
+ * Note:  This value is used in defining the KVP exchange message - this value
+ * cannot be modified without affecting the message size and compatibility.
+ */
+
+/*
+ * bytes, including any null terminators
+ */
+#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE          (2048)
+
+
+/*
+ * Maximum key size - the registry limit for the length of an entry name
+ * is 256 characters, including the null terminator
+ */
+
+#define HV_KVP_EXCHANGE_MAX_KEY_SIZE            (512)
+
+/*
+ * In Linux, we implement the KVP functionality in two components:
+ * 1) The kernel component which is packaged as part of the hv_utils driver
+ * is responsible for communicating with the host and responsible for
+ * implementing the host/guest protocol. 2) A user level daemon that is
+ * responsible for data gathering.
+ *
+ * Host/Guest Protocol: The host iterates over an index and expects the guest
+ * to assign a key name to the index and also return the value corresponding to
+ * the key. The host will have atmost one KVP transaction outstanding at any
+ * given point in time. The host side iteration stops when the guest returns
+ * an error. Microsoft has specified the following mapping of key names to
+ * host specified index:
+ *
+ *     Index           Key Name
+ *     0               FullyQualifiedDomainName
+ *     1               IntegrationServicesVersion
+ *     2               NetworkAddressIPv4
+ *     3               NetworkAddressIPv6
+ *     4               OSBuildNumber
+ *     5               OSName
+ *     6               OSMajorVersion
+ *     7               OSMinorVersion
+ *     8               OSVersion
+ *     9               ProcessorArchitecture
+ *
+ * The Windows host expects the Key Name and Key Value to be encoded in utf16.
+ *
+ * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the
+ * data gathering functionality in a user mode daemon. The user level daemon
+ * is also responsible for binding the key name to the index as well. The
+ * kernel and user-level daemon communicate using a connector channel.
+ *
+ * The user mode component first registers with the
+ * the kernel component. Subsequently, the kernel component requests, data
+ * for the specified keys. In response to this message the user mode component
+ * fills in the value corresponding to the specified key. We overload the
+ * sequence field in the cn_msg header to define our KVP message types.
+ *
+ *
+ * The kernel component simply acts as a conduit for communication between the
+ * Windows host and the user-level daemon. The kernel component passes up the
+ * index received from the Host to the user-level daemon. If the index is
+ * valid (supported), the corresponding key as well as its
+ * value (both are strings) is returned. If the index is invalid
+ * (not supported), a NULL key string is returned.
+ */
+
+
+/*
+ * Registry value types.
+ */
+
+#define REG_SZ 1
+#define REG_U32 4
+#define REG_U64 8
+
+/*
+ * As we look at expanding the KVP functionality to include
+ * IP injection functionality, we need to maintain binary
+ * compatibility with older daemons.
+ *
+ * The KVP opcodes are defined by the host and it was unfortunate
+ * that I chose to treat the registration operation as part of the
+ * KVP operations defined by the host.
+ * Here is the level of compatibility
+ * (between the user level daemon and the kernel KVP driver) that we
+ * will implement:
+ *
+ * An older daemon will always be supported on a newer driver.
+ * A given user level daemon will require a minimal version of the
+ * kernel driver.
+ * If we cannot handle the version differences, we will fail gracefully
+ * (this can happen when we have a user level daemon that is more
+ * advanced than the KVP driver.
+ *
+ * We will use values used in this handshake for determining if we have
+ * workable user level daemon and the kernel driver. We begin by taking the
+ * registration opcode out of the KVP opcode namespace. We will however,
+ * maintain compatibility with the existing user-level daemon code.
+ */
+
+/*
+ * Daemon code not supporting IP injection (legacy daemon).
+ */
+
+#define KVP_OP_REGISTER        4
+
+/*
+ * Daemon code supporting IP injection.
+ * The KVP opcode field is used to communicate the
+ * registration information; so define a namespace that
+ * will be distinct from the host defined KVP opcode.
+ */
+
+#define KVP_OP_REGISTER1 100
+
+enum hv_kvp_exchg_op {
+       KVP_OP_GET = 0,
+       KVP_OP_SET,
+       KVP_OP_DELETE,
+       KVP_OP_ENUMERATE,
+       KVP_OP_GET_IP_INFO,
+       KVP_OP_SET_IP_INFO,
+       KVP_OP_COUNT /* Number of operations, must be last. */
+};
+
+enum hv_kvp_exchg_pool {
+       KVP_POOL_EXTERNAL = 0,
+       KVP_POOL_GUEST,
+       KVP_POOL_AUTO,
+       KVP_POOL_AUTO_EXTERNAL,
+       KVP_POOL_AUTO_INTERNAL,
+       KVP_POOL_COUNT /* Number of pools, must be last. */
+};
+
+/*
+ * Some Hyper-V status codes.
+ */
+
+#define HV_S_OK                                0x00000000
+#define HV_E_FAIL                      0x80004005
+#define HV_S_CONT                      0x80070103
+#define HV_ERROR_NOT_SUPPORTED         0x80070032
+#define HV_ERROR_MACHINE_LOCKED                0x800704F7
+#define HV_ERROR_DEVICE_NOT_CONNECTED  0x8007048F
+#define HV_INVALIDARG                  0x80070057
+#define HV_GUID_NOTFOUND               0x80041002
+
+#define ADDR_FAMILY_NONE       0x00
+#define ADDR_FAMILY_IPV4       0x01
+#define ADDR_FAMILY_IPV6       0x02
+
+#define MAX_ADAPTER_ID_SIZE    128
+#define MAX_IP_ADDR_SIZE       1024
+#define MAX_GATEWAY_SIZE       512
+
+
+struct hv_kvp_ipaddr_value {
+       __u16   adapter_id[MAX_ADAPTER_ID_SIZE];
+       __u8    addr_family;
+       __u8    dhcp_enabled;
+       __u16   ip_addr[MAX_IP_ADDR_SIZE];
+       __u16   sub_net[MAX_IP_ADDR_SIZE];
+       __u16   gate_way[MAX_GATEWAY_SIZE];
+       __u16   dns_addr[MAX_IP_ADDR_SIZE];
+} __attribute__((packed));
+
+
+struct hv_kvp_hdr {
+       __u8 operation;
+       __u8 pool;
+       __u16 pad;
+} __attribute__((packed));
+
+struct hv_kvp_exchg_msg_value {
+       __u32 value_type;
+       __u32 key_size;
+       __u32 value_size;
+       __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+       union {
+               __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
+               __u32 value_u32;
+               __u64 value_u64;
+       };
+} __attribute__((packed));
+
+struct hv_kvp_msg_enumerate {
+       __u32 index;
+       struct hv_kvp_exchg_msg_value data;
+} __attribute__((packed));
+
+struct hv_kvp_msg_get {
+       struct hv_kvp_exchg_msg_value data;
+};
+
+struct hv_kvp_msg_set {
+       struct hv_kvp_exchg_msg_value data;
+};
+
+struct hv_kvp_msg_delete {
+       __u32 key_size;
+       __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+};
+
+struct hv_kvp_register {
+       __u8 version[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+};
+
+struct hv_kvp_msg {
+       union {
+               struct hv_kvp_hdr       kvp_hdr;
+               int error;
+       };
+       union {
+               struct hv_kvp_msg_get           kvp_get;
+               struct hv_kvp_msg_set           kvp_set;
+               struct hv_kvp_msg_delete        kvp_delete;
+               struct hv_kvp_msg_enumerate     kvp_enum_data;
+               struct hv_kvp_ipaddr_value      kvp_ip_val;
+               struct hv_kvp_register          kvp_register;
+       } body;
+} __attribute__((packed));
+
+struct hv_kvp_ip_msg {
+       __u8 operation;
+       __u8 pool;
+       struct hv_kvp_ipaddr_value      kvp_ip_val;
+} __attribute__((packed));
+
+#endif /* _UAPI_HYPERV_H */
index feec3ad5fd09862a66ef64dc3fe647eb07f7a236..bcae806b0c398d5450975ab08001e7a808e376c4 100644 (file)
@@ -7,6 +7,7 @@ help:
        @echo '  cgroup     - cgroup tools'
        @echo '  cpupower   - a tool for all things x86 CPU power'
        @echo '  firewire   - the userspace part of nosy, an IEEE-1394 traffic sniffer'
+       @echo '  hv         - tools used when in Hyper-V clients'
        @echo '  lguest     - a minimal 32-bit x86 hypervisor'
        @echo '  perf       - Linux performance measurement and analysis tool'
        @echo '  selftests  - various kernel selftests'
@@ -40,7 +41,7 @@ acpi: FORCE
 cpupower: FORCE
        $(call descend,power/$@)
 
-cgroup firewire guest usb virtio vm net: FORCE
+cgroup firewire hv guest usb virtio vm net: FORCE
        $(call descend,$@)
 
 libapikfs: FORCE
@@ -64,7 +65,7 @@ acpi_install:
 cpupower_install:
        $(call descend,power/$(@:_install=),install)
 
-cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install net_install:
+cgroup_install firewire_install hv_install lguest_install perf_install usb_install virtio_install vm_install net_install:
        $(call descend,$(@:_install=),install)
 
 selftests_install:
@@ -76,7 +77,7 @@ turbostat_install x86_energy_perf_policy_install:
 tmon_install:
        $(call descend,thermal/$(@:_install=),install)
 
-install: acpi_install cgroup_install cpupower_install firewire_install lguest_install \
+install: acpi_install cgroup_install cpupower_install hv_install firewire_install lguest_install \
                perf_install selftests_install turbostat_install usb_install \
                virtio_install vm_install net_install x86_energy_perf_policy_install \
        tmon
@@ -87,7 +88,7 @@ acpi_clean:
 cpupower_clean:
        $(call descend,power/cpupower,clean)
 
-cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean:
+cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean:
        $(call descend,$(@:_clean=),clean)
 
 libapikfs_clean:
@@ -105,7 +106,7 @@ turbostat_clean x86_energy_perf_policy_clean:
 tmon_clean:
        $(call descend,thermal/tmon,clean)
 
-clean: acpi_clean cgroup_clean cpupower_clean firewire_clean lguest_clean \
+clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
                perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \
                vm_clean net_clean x86_energy_perf_policy_clean tmon_clean
 
diff --git a/tools/hv/Makefile b/tools/hv/Makefile
new file mode 100644 (file)
index 0000000..bd22f78
--- /dev/null
@@ -0,0 +1,13 @@
+# Makefile for Hyper-V tools
+
+CC = $(CROSS_COMPILE)gcc
+PTHREAD_LIBS = -lpthread
+WARNINGS = -Wall -Wextra
+CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS)
+
+all: hv_kvp_daemon hv_vss_daemon
+%: %.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+clean:
+       $(RM) hv_kvp_daemon hv_vss_daemon
index 520de330457106657ab747581f983d23d695e5df..6a213b8cd7b9034ad097d60b2ce5295e458d4054 100644 (file)
@@ -87,6 +87,8 @@ static int vss_operate(int operation)
                        continue;
                if (strcmp(ent->mnt_type, "iso9660") == 0)
                        continue;
+               if (strcmp(ent->mnt_type, "vfat") == 0)
+                       continue;
                if (strcmp(ent->mnt_dir, "/") == 0) {
                        root_seen = 1;
                        continue;